實(shí)戰(zhàn):研究生之路設(shè)計(jì)與云開發(fā)實(shí)現(xiàn))
每次接到小程序類的畢業(yè)設(shè)計(jì)題我最怕的不是寫代碼而是把題目里的幾個(gè)字翻譯成一套能演示、能答辯、能落地的產(chǎn)品。這次聊的“基于微信小程序的研究生之路設(shè)計(jì)與實(shí)現(xiàn)”就是很典型的一個(gè)題目看著像校園應(yīng)用實(shí)際要做的是把“目標(biāo)管理、信息查詢、學(xué)習(xí)打卡、社區(qū)互動(dòng)”串成一條完整業(yè)務(wù)線。很多同學(xué)拿到這種題習(xí)慣性先去翻模板結(jié)果做出來的東西要么是幾個(gè)頁(yè)面堆在一起要么是一個(gè)沒有數(shù)據(jù)流轉(zhuǎn)的靜態(tài)圖答辯時(shí)老師一問核心邏輯就卡殼。這個(gè)項(xiàng)目如果認(rèn)真拆能做出很多亮點(diǎn)備考階段的院校信息查詢、讀研階段的計(jì)劃打卡、經(jīng)驗(yàn)分享的帖子社區(qū)、個(gè)人中心的數(shù)據(jù)統(tǒng)計(jì)每塊都能對(duì)應(yīng)到具體功能點(diǎn)。既能體現(xiàn)前端頁(yè)面設(shè)計(jì)能力又能展示后端數(shù)據(jù)交互能力還方便講清楚需求分析、數(shù)據(jù)庫(kù)設(shè)計(jì)這些計(jì)算機(jī)關(guān)鍵內(nèi)容。對(duì)想拿高分的同學(xué)來說它的上限很高對(duì)只想順利過審的同學(xué)來說它也能做得規(guī)規(guī)矩矩不出大錯(cuò)。所以這篇文章我不會(huì)只貼一段代碼而是把這套項(xiàng)目從需求到架構(gòu)、從核心代碼到聯(lián)調(diào)排錯(cuò)、再到文檔答辯完整走一遍讓你不僅能復(fù)現(xiàn)還能在復(fù)現(xiàn)時(shí)講出為什么這么做。1. 先拆題研究生這條路到底要設(shè)計(jì)成什么樣1.1 需求方最想看到什么題目里的“研究生之路”是一個(gè)很寬泛的表達(dá)寬泛到容易被做飄。我第一次拿到這類題目時(shí)差點(diǎn)把它做成一個(gè)“考研科普門戶”堆了一堆院校排行榜和考試攻略。后來帶我的導(dǎo)師點(diǎn)了一句你要做的是路不是手冊(cè)?!奥贰币馕吨衅瘘c(diǎn)、有終點(diǎn)、有進(jìn)度、有記錄用戶是在這條路上持續(xù)行動(dòng)的。所以這個(gè)產(chǎn)品的核心應(yīng)該是“過程管理”備考時(shí)能看到目標(biāo)院校和專業(yè)信息知道自己下一步該做什么入學(xué)后能制定學(xué)習(xí)計(jì)劃每天打卡記錄進(jìn)度壓力大的時(shí)候能在社區(qū)里看看別人怎么走、發(fā)一條自己的狀態(tài)。這樣理解下來它就不再是一個(gè)信息展示類小程序而是一個(gè)帶個(gè)人屬性的成長(zhǎng)工具。答辯時(shí)如果被問到“這個(gè)系統(tǒng)解決了什么問題”最穩(wěn)妥的回答就是用它串起一個(gè)研究生的完整成長(zhǎng)周期把分散的備考資料、學(xué)習(xí)計(jì)劃、日常記錄和經(jīng)驗(yàn)交流集中到一個(gè)小程序里讓用戶在移動(dòng)端方便地管理自己的研究生之路。1.2 功能范圍的合理收斂畢設(shè)項(xiàng)目最忌諱功能清單寫得像景點(diǎn)打卡每個(gè)模塊都淺嘗輒止。我建議把主干收斂成五個(gè)模塊既能覆蓋業(yè)務(wù)又不會(huì)把自己拖進(jìn)開發(fā)泥潭。首頁(yè)與公告作為信息入口展示近期通知、考研節(jié)點(diǎn)、系統(tǒng)公告起到引導(dǎo)作用。院校信息查詢按地區(qū)、專業(yè)、類型篩選院校查看招生簡(jiǎn)章、專業(yè)目錄、往年分?jǐn)?shù)等參考信息。學(xué)習(xí)計(jì)劃與打卡用戶創(chuàng)建復(fù)習(xí)計(jì)劃或?qū)W期計(jì)劃按天打卡用日歷形式回看完成情況。經(jīng)驗(yàn)分享社區(qū)支持發(fā)布圖文帖子、點(diǎn)贊、評(píng)論、收藏讓用戶之間產(chǎn)生互動(dòng)。個(gè)人中心展示我的計(jì)劃、我的打卡、我的帖子、我的收藏并進(jìn)行基礎(chǔ)的數(shù)據(jù)統(tǒng)計(jì)。這五個(gè)模塊里最核心的是“學(xué)習(xí)計(jì)劃與打卡”。它最能體現(xiàn)數(shù)據(jù)交互也最容易在答辯時(shí)展開講。院校查詢和社區(qū)帖子的數(shù)據(jù)模型可以復(fù)用寫代碼時(shí)邊際成本很低但演示效果卻很好。1.3 業(yè)務(wù)角色和用戶故事怎么講為了讓文檔里“需求分析”那一章有內(nèi)容可寫建議用用戶故事來定義角色。系統(tǒng)里至少要分兩類角色普通用戶和管理者。普通用戶的故事可以是“作為一名準(zhǔn)備考研的學(xué)生我希望在小程序里查詢目標(biāo)院校和專業(yè)信息以便快速評(píng)估報(bào)考難度作為一名在讀研究生我希望創(chuàng)建學(xué)習(xí)計(jì)劃并每天打卡以便保持科研和學(xué)習(xí)節(jié)奏作為一名用戶我希望在社區(qū)分享自己的經(jīng)驗(yàn)并看到其他人的回復(fù)以便獲得交流反饋。”管理者的故事則圍繞內(nèi)容維護(hù)展開“作為管理員我希望發(fā)布公告和更新院校信息以便用戶獲取最新資料作為管理員我希望刪除違規(guī)帖子以便保持社區(qū)環(huán)境健康。”不用真的做一套復(fù)雜的管理后臺(tái)在微信云開發(fā)的控制臺(tái)里維護(hù)集合數(shù)據(jù)就足夠演示“管理員操作”了。文檔里把角色用例圖畫清楚同樣能得高分。2. 技術(shù)選型與技術(shù)棧為什么這么定2.1 原生小程序還是框架現(xiàn)在做小程序常用三個(gè)方向原生微信小程序、uni-app、Taro。畢設(shè)場(chǎng)景里我的建議是除非你很清楚自己要什么否則優(yōu)先用原生。原生最大的好處是沒有編譯鏈問題。uni-app 和 Taro 都很成熟但它們會(huì)多一層抽象一旦遇到第三方組件不兼容或編譯報(bào)錯(cuò)排查成本會(huì)翻倍。畢設(shè)周期有限你要把時(shí)間花在核心邏輯上而不是環(huán)境配置上。界面方面可以用自定義樣式也可以引入主流的 UI 組件庫(kù)。不過為了減少依賴我會(huì)建議底部的 TabBar、列表、卡片全部自己寫樣式控制在自己手里答辯時(shí)被問到“這個(gè)頁(yè)面怎么實(shí)現(xiàn)的”時(shí)你可以理直氣壯地說是自己寫的而不是我引了什么黑盒庫(kù)。真要是時(shí)間緊那就保守地用一個(gè)輕量組件庫(kù)處理彈窗和表單但盡量別讓 UI 層的黑盒影響功能演示。2.2 后端到底選哪條路云開發(fā)還是自建服務(wù)這個(gè)問題很多人糾結(jié)我直接給結(jié)論畢設(shè)階段優(yōu)先走微信云開發(fā)。自建后端意味著你要自己設(shè)計(jì) REST 接口、處理鑒權(quán)、部署服務(wù)器、配域名和 HTTPS 證書。每一個(gè)環(huán)節(jié)都是坑尤其域名備案和 HTTPS 證書在小程序后臺(tái)有嚴(yán)格限制一旦弄錯(cuò)整個(gè)項(xiàng)目都無法真機(jī)預(yù)覽非常打擊士氣。微信云開發(fā)可以完美繞開大多數(shù)痛處數(shù)據(jù)庫(kù)是 JSON 文檔型云函數(shù)對(duì)應(yīng)后端接口存儲(chǔ)用來放圖片身份認(rèn)證直接拿 openid 當(dāng)用戶標(biāo)識(shí)。它還有配套的安全規(guī)則可以控制每個(gè)集合的讀寫權(quán)限。你只需要把精力放在業(yè)務(wù)邏輯上不用運(yùn)維服務(wù)器這對(duì)一個(gè)兩周要出完整 Demo 的畢設(shè)來說是最優(yōu)解。如果你非要展現(xiàn)自己的后端能力也可以讓云函數(shù)去請(qǐng)求一個(gè)外部接口比如在云函數(shù)里寫匯聚邏輯。答辯時(shí)既能講云開發(fā)又能講傳統(tǒng)后端的交互兩頭的分都能拿。2.3 數(shù)據(jù)庫(kù)集合與字段設(shè)計(jì)數(shù)據(jù)庫(kù)我按業(yè)務(wù)拆成了這幾個(gè)集合每個(gè)集合的字段都盡量精簡(jiǎn)避免后面改結(jié)構(gòu)。users_id、openid、nickName、avatarUrl、role、createTime。openid 用來唯一標(biāo)識(shí)用戶role 可以用來區(qū)分管理員。colleges_id、name、province、category、major、scoreLine、introduction。院校信息字段可以再按需補(bǔ)充。plans_id、openid、title、category、startDate、endDate、status。學(xué)習(xí)計(jì)劃status 用來區(qū)分進(jìn)行中、已完成、已放棄。planRecords_id、planId、openid、date、completed。打卡記錄每天一條。posts_id、openid、title、content、images、likeCount、commentCount、createTime。comments_id、postId、openid、content、createTime。favorites_id、openid、postId、createTime。notices_id、title、content、createTime。這里有兩個(gè)容易忽略的設(shè)計(jì)點(diǎn)。第一帖子里的 likeCount 和 commentCount 是冗余字段每次點(diǎn)贊或評(píng)論時(shí)直接更新它列表就不用反復(fù)聚合統(tǒng)計(jì)。第二planRecords 用 planId 關(guān)聯(lián) plans而不是塞成數(shù)組這樣后續(xù)做統(tǒng)計(jì)、做日歷回看都更順暢。數(shù)據(jù)庫(kù)設(shè)計(jì)這一節(jié)在文檔里寫清楚基本就能撐起“概要設(shè)計(jì)”到“詳細(xì)設(shè)計(jì)”的過渡。3. 核心功能拆解與代碼落地3.1 登錄與身份綁定小程序的登錄流程看起來簡(jiǎn)單實(shí)際寫的時(shí)候要注意時(shí)機(jī)。我的方案是在首頁(yè) onLoad 時(shí)先檢查本地緩存是否有 openid如果沒有就調(diào)用云函數(shù)拿 openid再查詢或創(chuàng)建用戶記錄。// 云函數(shù) login/index.js const cloud require(wx-server-sdk) cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) const db cloud.database() exports.main async (event, context) { const { OPENID } cloud.getWXContext() const userCol db.collection(users) const exist await userCol.where({ openid: OPENID }).get() if (exist.data.length 0) { return { code: 0, openid: OPENID, isNew: false } } await userCol.add({ data: { openid: OPENID, nickName: , avatarUrl: , role: user, createTime: db.serverDate() } }) return { code: 0, openid: OPENID, isNew: true } }寫完之后App 啟動(dòng)時(shí)調(diào)一次把返回的 openid 存進(jìn) globalData 和 storage。后續(xù)所有集合操作都通過 openid 來過濾當(dāng)前用戶。注意不要在客戶端直接用 wx.getUserInfo 拿昵稱頭像微信對(duì)新版接口限制很嚴(yán)真機(jī)調(diào)試時(shí)很容易拿不到數(shù)據(jù)比較穩(wěn)的方案是讓用戶在小程序里手動(dòng)填寫昵稱或者使用新版的頭像昵稱填寫能力演示時(shí)不會(huì)翻車。3.2 學(xué)習(xí)計(jì)劃打卡的實(shí)現(xiàn)打卡功能是整個(gè)項(xiàng)目的邏輯心臟。我的做法是兩張表配合plans 存計(jì)劃元信息planRecords 存某一天某計(jì)劃是否完成。創(chuàng)建計(jì)劃時(shí)const db wx.cloud.database() const plans db.collection(plans) async function createPlan(title, category, startDate, endDate) { const app getApp() await plans.add({ data: { openid: app.globalData.openid, title, category, startDate, endDate, status: active, createTime: db.serverDate() } }) }打卡邏輯用關(guān)鍵字“今日打卡”實(shí)現(xiàn)。我先查出用戶所有 active 狀態(tài)計(jì)劃然后按計(jì)劃生成今天的記錄。為了避免重復(fù)打卡要在計(jì)劃 id 加日期上做唯一性判斷。這里給出一個(gè)“無腦但穩(wěn)”的寫法先查 planRecords如果存在今天的記錄就直接更新 completed 為 true否則新增。async function checkIn(planId) { const db wx.cloud.database() const today formatDate(new Date()) // 格式 YYYY-MM-DD const record await db.collection(planRecords) .where({ planId, date: today }) .get() if (record.data.length 0) { await db.collection(planRecords).doc(record.data[0]._id).update({ data: { completed: true } }) } else { await db.collection(planRecords).add({ data: { planId, openid: getApp().globalData.openid, date: today, completed: true } }) } }這里有一個(gè)經(jīng)驗(yàn)不要在客戶端用當(dāng)前時(shí)間直接拼日期字符串一定要做時(shí)區(qū)處理。用new Date()拿到的是本地時(shí)區(qū)用戶手機(jī)如果時(shí)區(qū)不對(duì)打卡日期就會(huì)錯(cuò)亂。穩(wěn)一點(diǎn)的做法是獲取服務(wù)端時(shí)間或者寫一個(gè) formatDate 函數(shù)把年月日分別取出來手動(dòng)補(bǔ)零不要用 toISOString()。日歷展示是本模塊的加分項(xiàng)。頁(yè)面里用一個(gè)網(wǎng)格渲染當(dāng)月日期然后對(duì)每個(gè)格子去數(shù)據(jù)庫(kù)查該天的打卡記錄已打卡的就顯示特殊標(biāo)記。為了減少查詢次數(shù)可以一次拉取本月所有記錄用一個(gè)對(duì)象按日期索引前端渲染時(shí)查對(duì)象就行不要每個(gè)格子單獨(dú)發(fā)請(qǐng)求。3.3 經(jīng)驗(yàn)分享帖的發(fā)布與列表帖子發(fā)布的核心不是文本而是圖片處理。使用 wx.chooseMedia 選擇圖片后逐張上傳到云存儲(chǔ)把返回的 fileID 存進(jìn) post 的 images 數(shù)組。后續(xù)頁(yè)面顯示圖片時(shí)不能用 fileID 直接作為圖片 src要調(diào)用 wx.cloud.getTempFileURL 換取臨時(shí)鏈接。async function uploadImages(filePaths) { const tasks filePaths.map((filePath, index) { const ext filePath.match(/\.[a-zA-Z0-9]$/)[0] return wx.cloud.uploadFile({ cloudPath: posts/${Date.now()}-${index}${ext}, filePath }) }) const results await Promise.all(tasks) return results.map(res res.fileID) }列表頁(yè)的關(guān)鍵是分頁(yè)。小程序端不適合一次性拉全部數(shù)據(jù)我用 onReachBottom 觸發(fā)加載更多每頁(yè)取 10 條按 createTime 倒序。云開發(fā)的 get() 默認(rèn)最多返回 20 條所以必須用 limit(10) 配合 skip 做翻頁(yè)。不要忘了記錄最后一次文檔 id 或用頁(yè)碼做偏移不然演示時(shí)下拉加載會(huì)出重復(fù)數(shù)據(jù)。帖子詳情也有一段要注意進(jìn)入詳情時(shí)不能只靠列表傳入的數(shù)據(jù)應(yīng)該重新從數(shù)據(jù)庫(kù)讀取詳情順便把該帖子的評(píng)論列表一起查出來。點(diǎn)進(jìn)詳情再刷新可以保證評(píng)論數(shù)、點(diǎn)贊數(shù)是最新的。3.4 個(gè)人中心的數(shù)據(jù)聚合個(gè)人中心如果只是顯示頭像和昵稱分值上不去。我建議在個(gè)人中心里做三個(gè)統(tǒng)計(jì)卡片計(jì)劃總數(shù)、今日完成數(shù)、累計(jì)打卡天數(shù)。這三個(gè)值看起來簡(jiǎn)單但把“狀態(tài)管理”和“數(shù)據(jù)庫(kù)聚合”都體現(xiàn)出來了。async function loadStats() { const db wx.cloud.database() const openid getApp().globalData.openid const today formatDate(new Date()) const [planRes, todayRes, totalRes] await Promise.all([ db.collection(plans).where({ openid }).count(), db.collection(planRecords).where({ openid, date: today, completed: true }).count(), db.collection(planRecords).where({ openid, completed: true }).count() ]) this.setData({ planCount: planRes.total, todayCount: todayRes.total, totalDays: totalRes.total }) }三個(gè)請(qǐng)求用 Promise.all 并發(fā)執(zhí)行比串行快很多。注意云開發(fā)的 count 方法返回的是 total 字段不是 length第一次寫的時(shí)候很容易用錯(cuò)。個(gè)人中心里還要展示“我的帖子”和“我的收藏”可以直接用 openid 過濾集合列表兩列卡片展示即可。4. 調(diào)試和遠(yuǎn)程協(xié)作畢設(shè)里最容易翻車的環(huán)節(jié)4.1 調(diào)試環(huán)境的配置要點(diǎn)很多同學(xué)代碼寫完頁(yè)面一編譯卻白屏第一反應(yīng)是找代碼問題其實(shí)多數(shù)情況是環(huán)境沒配好。小程序項(xiàng)目根目錄要有一個(gè) app.js并且所有提前引用了 wx.cloud 的文件必須保證 cloud 初始化已執(zhí)行。一個(gè)安全的做法是把初始化放在 app.js 的 onLaunch 里App({ onLaunch() { if (!wx.cloud) { console.error(當(dāng)前基礎(chǔ)庫(kù)過低) return } wx.cloud.init({ env: 你的云環(huán)境ID, traceUser: true }) } })初始化之后再進(jìn)入各頁(yè)面就能通過wx.cloud.database()訪問數(shù)據(jù)庫(kù)。如果你在頁(yè)面 onLoad 里直接用 db 而 app.js 還沒執(zhí)行完偶爾會(huì)遇到 getApp().globalData 為空。所以不要把關(guān)鍵邏輯放在 app.js 后面緊跟著的頁(yè)面里要在頁(yè)面 onReady 或者回調(diào)里再獲取。4.2 真機(jī)調(diào)試與遠(yuǎn)程聯(lián)調(diào)小程序開發(fā)最爽的一點(diǎn)是微信開發(fā)者工具自帶真機(jī)調(diào)試。點(diǎn)擊工具欄的“真機(jī)調(diào)試”會(huì)生成一個(gè)二維碼手機(jī)掃碼后可以在真機(jī)上實(shí)時(shí)看控制臺(tái)日志和網(wǎng)絡(luò)請(qǐng)求。畢設(shè)場(chǎng)景中通常需要給導(dǎo)師遠(yuǎn)程演示這時(shí)可以用云開發(fā)的“云函數(shù)日志”來排查線上數(shù)據(jù)也可以讓共同開發(fā)的隊(duì)友用同一個(gè) AppID 和項(xiàng)目目錄在各自電腦上跑同一套代碼。遠(yuǎn)程聯(lián)調(diào)的經(jīng)驗(yàn)有三條。第一所有環(huán)境變量集中放比如云環(huán)境 ID 寫在一個(gè) config.js 里不要散落在各個(gè)頁(yè)面。第二本地改代碼后重新上傳云函數(shù)必須在微信開發(fā)者工具里右鍵云函數(shù)目錄選擇“上傳并部署云端安裝依賴”否則你在本地改的邏輯在真機(jī)上不生效。第三真機(jī)預(yù)覽時(shí)如果提示“開發(fā)版不可用”需要在微信公眾平臺(tái)把當(dāng)前微信號(hào)加入體驗(yàn)成員名單或者切換成體驗(yàn)版。4.3 網(wǎng)絡(luò)與權(quán)限問題的排查小程序控制臺(tái)里的 Network 面板能幫你定位絕大多數(shù)問題。云開發(fā)的數(shù)據(jù)庫(kù)請(qǐng)求在面板里會(huì)顯示成具體集合名如果顯示權(quán)限不足就要檢查集合權(quán)限設(shè)置。默認(rèn)情況下我統(tǒng)一用“僅創(chuàng)建者可讀寫”但像 colleges、notices 這類公開數(shù)據(jù)就需要改成“所有用戶可讀僅管理端可寫”這樣普通用戶能看但不會(huì)誤刪。另外一個(gè)隱藏坑是云函數(shù)部署到云端后如果調(diào)用了其他云函數(shù)或外部接口需要檢查云函數(shù)里的環(huán)境變量和依賴。上傳云函數(shù)時(shí)選擇“云端安裝依賴”否則安裝云開發(fā) SDK 之后本地能跑云端卻會(huì)報(bào)模塊找不到這種錯(cuò)容易讓人懷疑人生。5. 常見問題速查我在這個(gè)項(xiàng)目里踩過的坑問題現(xiàn)象可能原因解決辦法啟動(dòng)后所有頁(yè)面空白wx.cloud 未初始化或環(huán)境 ID 錯(cuò)誤確認(rèn) app.js 中 init 的 env 與當(dāng)前云環(huán)境一致真機(jī)掃碼后提示不能體驗(yàn)當(dāng)前微信號(hào)未添加為體驗(yàn)成員在公眾平臺(tái)添加體驗(yàn)成員或用預(yù)覽二維碼打卡記錄日期錯(cuò)一天使用了 toISOString() 導(dǎo)致 UTC 時(shí)區(qū)偏差自定義 formatDate按本地年/月/日補(bǔ)零拼接圖片顯示不出來數(shù)據(jù)庫(kù)存的是 fileID頁(yè)面直接用了 fileID用 wx.cloud.getTempFileURL 轉(zhuǎn)換為臨時(shí)鏈接下拉加載總是重復(fù)數(shù)據(jù)skip 參數(shù)沒有累計(jì)每次都從 0 開始在 data 中保存 pageonReachBottom 時(shí) page列表頁(yè)偶發(fā)卡頓每個(gè)格子單獨(dú)查數(shù)據(jù)庫(kù)一次拉取當(dāng)月記錄用對(duì)象做日期索引云函數(shù)改了不生效沒有重新上傳部署右鍵云函數(shù)目錄選擇上傳并部署這些坑里面最值得展開說的是時(shí)區(qū)問題。小程序前端運(yùn)行在手機(jī)本地上如果你用new Date().toISOString().split(T)[0]這種寫法得到的是 UTC 日期。國(guó)內(nèi)在東八區(qū)凌晨零到八點(diǎn)之間打卡日期會(huì)比北京時(shí)間少一天這在做連續(xù)打卡統(tǒng)計(jì)時(shí)會(huì)直接漏一天。我寫了一個(gè)專門的 tools 文件放一個(gè) formatDate 函數(shù)所有涉及日期的模塊統(tǒng)一調(diào)它。還有一個(gè)容易忽略的是集合權(quán)限里的“自定義安全規(guī)則”。如果想讓數(shù)據(jù)在某些條件下可寫可以在安全規(guī)則里寫 JSON 條件。我遇到過多次用戶想刪除自己帖子卻報(bào)權(quán)限不足的情況解決方案是安全規(guī)則中把doc._openid auth.openid的寫權(quán)限打開。這個(gè)字段是云開發(fā)自動(dòng)加在每條記錄上的你新增記錄時(shí)不需要手動(dòng)寫但規(guī)則里要用到它。6. 從源碼到文檔再到達(dá)辯現(xiàn)場(chǎng)6.1 項(xiàng)目文檔怎么寫才不被挑毛病畢設(shè)文檔的質(zhì)量很多時(shí)候比代碼更重要。評(píng)審老師看你有沒有完整走完“提出問題、分析問題、設(shè)計(jì)系統(tǒng)、實(shí)現(xiàn)系統(tǒng)、測(cè)試系統(tǒng)”這個(gè)流程。一份合格的文檔至少包含五章需求分析、概要設(shè)計(jì)、詳細(xì)設(shè)計(jì)、系統(tǒng)實(shí)現(xiàn)、系統(tǒng)測(cè)試。需求分析里寫清楚背景和用例圖別只寫一句“本系統(tǒng)方便研究生管理學(xué)習(xí)”。概要設(shè)計(jì)畫系統(tǒng)架構(gòu)圖把前端頁(yè)面和后端云函數(shù)的關(guān)系標(biāo)出來。詳細(xì)設(shè)計(jì)放數(shù)據(jù)庫(kù)表結(jié)構(gòu)和核心流程圖。系統(tǒng)實(shí)現(xiàn)里不用貼全部代碼貼兩三個(gè)核心模塊的代碼片段并解釋執(zhí)行過程。系統(tǒng)測(cè)試寫測(cè)試用例比如“創(chuàng)建計(jì)劃后能否成功新增記錄”“重復(fù)打卡是否會(huì)覆蓋”“非本人能否刪除帖子”。這些測(cè)試用例你開發(fā)時(shí)順手就能執(zhí)行記錄下來就成了文檔的一部分。6.2 演示時(shí)的演示路徑和講解詞設(shè)計(jì)答辯演示千萬不要按功能列表一個(gè)一個(gè)點(diǎn)過去很散評(píng)委記不住。我建議設(shè)計(jì)一條業(yè)務(wù)主線從進(jìn)入小程序開始先看到首頁(yè)公告和院校信息接著創(chuàng)建一條“沖刺復(fù)習(xí)計(jì)劃”然后完成今天的一次打卡打卡后去社區(qū)發(fā)一條經(jīng)驗(yàn)帖再回到個(gè)人中心看統(tǒng)計(jì)數(shù)據(jù)。這條路徑走完每個(gè)模塊都自然串聯(lián)起來了評(píng)委看到的是一個(gè)完整用戶旅程。講解詞也要提前設(shè)計(jì)。每打開一個(gè)頁(yè)面先講這個(gè)頁(yè)面解決什么問題再講實(shí)現(xiàn)用的什么技術(shù)最后講一個(gè)難點(diǎn)。比如進(jìn)入打卡頁(yè)可以說“這是全系統(tǒng)最核心的地方用戶創(chuàng)建計(jì)劃后打卡記錄會(huì)按日期寫入 planRecords 集合個(gè)人中心的日歷統(tǒng)計(jì)直接查這個(gè)集合不需要額外冗余計(jì)算?!边@樣每個(gè)頁(yè)面都能把業(yè)務(wù)邏輯和技術(shù)實(shí)現(xiàn)綁定在一起說。6.3 擴(kuò)展成更完善系統(tǒng)的幾種做法時(shí)間充裕的話可以在當(dāng)前版本上做三個(gè)方向的擴(kuò)展。第一個(gè)方向是加管理員端在云函數(shù)里判斷角色為 admin 后提供文章審核、公告錄入、用戶禁言接口。第二個(gè)方向是加數(shù)據(jù)可視化把打卡數(shù)據(jù)按周、按月統(tǒng)計(jì)用 canvas 畫柱狀圖或者折線圖這是不少老師喜歡看的加分項(xiàng)。第三個(gè)方向是接入訂閱消息計(jì)劃當(dāng)天沒打卡時(shí)用戶開啟訂閱消息提醒第二天能收到提醒通知。這個(gè)方向涉及微信消息模板需要預(yù)約模板 ID做的時(shí)候提前去公眾平臺(tái)配置好。每個(gè)擴(kuò)展方向都能單獨(dú)寫成答辯亮點(diǎn)。哪怕最后只做一個(gè)方向也足以讓項(xiàng)目看起來比普通模板層級(jí)的畢設(shè)完整很多。我個(gè)人在實(shí)際操作中的體會(huì)是這類小程序畢設(shè)的核心不是堆頁(yè)面而是把一條業(yè)務(wù)主線的數(shù)據(jù)打通。只要你讓用戶的每一次動(dòng)作都真實(shí)地寫入數(shù)據(jù)庫(kù)并能在另一個(gè)頁(yè)面讀到它就已經(jīng)超過市面上大量靜態(tài)模板項(xiàng)目。最后再分享一個(gè)經(jīng)驗(yàn)答辯前的那個(gè)晚上不要繼續(xù)加功能把主流程完整地走三遍把開發(fā)版的代碼重新上傳一次確保云函數(shù)是最新版本。我見過太多同學(xué)功能寫得很好但因?yàn)橥洸渴鹪坪瘮?shù)演示時(shí)數(shù)據(jù)全都不見了整場(chǎng)心態(tài)崩掉。代碼強(qiáng)不強(qiáng)是加分項(xiàng)流程穩(wěn)不穩(wěn)才是保命項(xiàng)。