生托管管理系統(tǒng)實(shí)戰(zhàn)全流程)
又到了一年兩度的畢業(yè)設(shè)計(jì)選題季托管管理系統(tǒng)這個(gè)題目每年都有一大批人做但真正能把前后端串起來、把業(yè)務(wù)流程講清楚的并不多。很多同學(xué)卡在最基礎(chǔ)的地方微信小程序怎么和后端通信登錄狀態(tài)怎么維持接送記錄和簽到記錄怎么設(shè)計(jì)才不會(huì)亂這篇文章就圍繞一個(gè)基于SpringBoot和微信小程序的小學(xué)生托管管理系統(tǒng)把從業(yè)務(wù)拆解、數(shù)據(jù)庫(kù)設(shè)計(jì)、核心接口實(shí)現(xiàn)到小程序端聯(lián)調(diào)、部署運(yùn)行的完整鏈路捋一遍。內(nèi)容偏實(shí)戰(zhàn)適合正在做類似課題、或者想快速入門SpringBoot全棧開發(fā)的同學(xué)參考。我會(huì)把當(dāng)時(shí)設(shè)計(jì)時(shí)踩過的坑和后來復(fù)盤覺得最該注意的點(diǎn)也都寫出來。1. 托管業(yè)務(wù)拆解先理清需求再談技術(shù)選型1.1 托管班的日常運(yùn)轉(zhuǎn)到底有哪些事做系統(tǒng)之前我專門跑了兩家托管機(jī)構(gòu)把他們的日常流程捋了一遍。所謂托管其實(shí)就是放學(xué)后把孩子接到機(jī)構(gòu)里看著寫作業(yè)、管頓飯、等家長(zhǎng)來接。這里面有四個(gè)核心角色家長(zhǎng)、學(xué)生、老師、機(jī)構(gòu)管理員。日常動(dòng)作看起來簡(jiǎn)單但拆成系統(tǒng)需求后其實(shí)不少早晨或放學(xué)后學(xué)生到達(dá)托管班需要記錄入托時(shí)間學(xué)生離開時(shí)需要記錄離托時(shí)間并且要確認(rèn)是哪個(gè)家長(zhǎng)接走的家長(zhǎng)需要實(shí)時(shí)看到孩子到?jīng)]到、走沒走、作業(yè)完成狀態(tài)每月收費(fèi)、繳費(fèi)記錄、欠費(fèi)提醒孩子生病或有事不能來家長(zhǎng)需要請(qǐng)假機(jī)構(gòu)發(fā)通知比如放假安排、活動(dòng)通知管理員要能按班級(jí)、按時(shí)間查看考勤統(tǒng)計(jì)。這些需求單獨(dú)拎出來每一個(gè)都不難但要在同一個(gè)系統(tǒng)里串起來并且保證數(shù)據(jù)準(zhǔn)確、流程可追溯就需要在設(shè)計(jì)和實(shí)現(xiàn)上下一番功夫。1.2 技術(shù)選型SpringBoot與微信小程序?yàn)槭裁词墙^配后端選SpringBoot理由很實(shí)在生態(tài)成熟、資料多、答辯好講。SpringBoot的自動(dòng)配置機(jī)制讓項(xiàng)目初期搭建成本極低配合MyBatis-Plus單表CRUD基本不用寫SQL開發(fā)效率很高。Java語言的強(qiáng)類型特性也讓多人協(xié)作時(shí)更容易保持代碼穩(wěn)定。前端選微信小程序核心原因是家長(zhǎng)端的使用門檻為零。托管班的家長(zhǎng)群體里很多是爺爺奶奶輩讓他們下載一個(gè)獨(dú)立App根本不現(xiàn)實(shí)但在微信里打開小程序、點(diǎn)一下就能看到孩子狀態(tài)幾乎不需要學(xué)習(xí)成本。從技術(shù)角度說小程序提供了完整的登錄體系、模板消息訂閱能力而且開發(fā)調(diào)試工具很成熟比H5在微信里的體驗(yàn)更可控。這里我也對(duì)比過另一條路線只做一個(gè)web管理后臺(tái)家長(zhǎng)端用微信群人工溝通。結(jié)果是老師每天要拍幾十張照片發(fā)群家長(zhǎng)信息淹沒在聊天記錄里出了糾紛都找不到依據(jù)。所以最終方案還是管理后臺(tái)家長(zhǎng)小程序雙端結(jié)構(gòu)。1.3 功能模塊劃分雙端各司其職整個(gè)系統(tǒng)的功能模塊劃分大致如下這個(gè)劃分也直接決定了后面的表結(jié)構(gòu)和接口設(shè)計(jì)。模塊核心功能面向角色實(shí)現(xiàn)端學(xué)生管理學(xué)生信息增刪改查、分班、狀態(tài)管理老師/管理員管理后臺(tái)小程序班級(jí)管理班級(jí)信息、師生綁定、容量設(shè)置管理員管理后臺(tái)小程序考勤簽到入托簽到、離托簽退、記錄查詢老師/家長(zhǎng)雙端接送管理接送人白名單、接送記錄確認(rèn)老師/家長(zhǎng)雙端費(fèi)用管理月費(fèi)生成、繳費(fèi)登記、欠費(fèi)統(tǒng)計(jì)管理員/家長(zhǎng)雙端請(qǐng)假管理請(qǐng)假申請(qǐng)、審批、缺勤同步家長(zhǎng)/老師雙端公告通知發(fā)布公告、消息觸達(dá)管理員/家長(zhǎng)雙端每個(gè)模塊的功能都不復(fù)雜難點(diǎn)在于模塊之間的數(shù)據(jù)聯(lián)動(dòng)。比如請(qǐng)假審批通過后當(dāng)天的考勤狀態(tài)要自動(dòng)標(biāo)記為請(qǐng)假不能算缺勤繳費(fèi)完成后家長(zhǎng)端的費(fèi)用狀態(tài)要實(shí)時(shí)刷新。2. 核心表結(jié)構(gòu)設(shè)計(jì)從業(yè)務(wù)反推數(shù)據(jù)庫(kù)而不是先畫ER圖2.1 主表設(shè)計(jì)學(xué)生、班級(jí)、老師、家長(zhǎng)四張核心表很多新手做表結(jié)構(gòu)時(shí)喜歡一上來就畫ER圖結(jié)果畫出來一堆關(guān)系落庫(kù)的時(shí)候卻不知道怎么處理。我的做法正好反過來把業(yè)務(wù)里的每一個(gè)功能點(diǎn)列出來反推需要哪些表、哪些字段。這個(gè)系統(tǒng)的核心主表有六張student學(xué)生信息表字段包括學(xué)生姓名、性別、出生日期、班級(jí)ID、入學(xué)日期、狀態(tài)。特別要注意的字段是status我用了tinyint類型1代表在讀0代表退托這樣退托學(xué)生不會(huì)影響班級(jí)考勤統(tǒng)計(jì)。class_info托管班級(jí)表包括班級(jí)名稱、所屬年級(jí)、負(fù)責(zé)老師ID、容量上限。這里有個(gè)業(yè)務(wù)細(xì)節(jié)一個(gè)托管班通常對(duì)應(yīng)一個(gè)主帶老師所以直接冗余了teacher_id查詢班級(jí)列表時(shí)少一次join。teacher教師表包括登錄賬號(hào)、密碼BCrypt加密、姓名、手機(jī)號(hào)、角色。角色字段我用的是普通字符串雖然不夠優(yōu)雅但勝在直觀項(xiàng)目里判斷權(quán)限時(shí)直接用ADMIN、TEACHER比較。parent_user家長(zhǎng)表核心字段是openid這是微信小程序登錄后拿到的唯一標(biāo)識(shí)。第一次通過微信授權(quán)登錄時(shí)系統(tǒng)會(huì)自動(dòng)創(chuàng)建這條記錄。student_parent_rel學(xué)生家長(zhǎng)關(guān)聯(lián)表這個(gè)表單獨(dú)說。sign_record簽到記錄表后面詳細(xì)說。2.2 學(xué)生與家長(zhǎng)的多對(duì)多關(guān)系為什么必須建關(guān)聯(lián)表學(xué)生和家長(zhǎng)之間不是簡(jiǎn)單的一對(duì)一。一個(gè)家庭可能有兩個(gè)孩子都在同一個(gè)托管班一個(gè)孩子也可能同時(shí)綁定父親、母親、爺爺奶奶四個(gè)監(jiān)護(hù)人。如果只在student表里加一個(gè)parent_id字段遇到多孩子或多家長(zhǎng)的情況就直接崩了。所以這里必須建關(guān)聯(lián)表student_parent_rel字段包括自增ID、student_id、parent_id、關(guān)系類型爸爸/媽媽/爺爺/奶奶/其他、是否接送授權(quán)人。其中是否接送授權(quán)人這個(gè)字段是我后來加上的因?yàn)樵诮铀蛨?chǎng)景里不是所有綁定家長(zhǎng)都有權(quán)接走孩子得有一個(gè)明確的標(biāo)記。查詢?cè)O(shè)計(jì)上一個(gè)典型的接口邏輯是家長(zhǎng)登錄后先查關(guān)聯(lián)表拿到所有student_id再查student表列表。這里我用了一條SQL解決SELECT s.* FROM student s INNER JOIN student_parent_rel r ON s.id r.student_id WHERE r.parent_id #{parentId} AND s.status 12.3 簽到記錄與接送記錄兩個(gè)動(dòng)作不能混在一張表里入托簽到和離托接送是兩個(gè)完全不同的業(yè)務(wù)動(dòng)作雖然它們都發(fā)生在學(xué)生進(jìn)出托管班這個(gè)時(shí)間點(diǎn)上但記錄的內(nèi)容和用途差別很大。簽到記錄側(cè)重的是考勤誰、幾點(diǎn)到、幾點(diǎn)走、當(dāng)天狀態(tài)正常/遲到/請(qǐng)假/缺勤。它面向的是老師和管理員用于統(tǒng)計(jì)出勤率。接送記錄側(cè)重的是安全與責(zé)任歸屬誰來接的、和學(xué)生的關(guān)系是什么、哪個(gè)老師確認(rèn)放行的。它面向的更多是安全事故追溯出了問題能查清楚哪一環(huán)有漏洞。我把這兩張表分開設(shè)計(jì)字段對(duì)比如下字段sign_record簽到記錄pickup_record接送記錄學(xué)生IDstudent_idstudent_id動(dòng)作類型sign_type1入托/2離托無本身就是接送行為時(shí)間sign_timepickup_time操作人operator_id老師IDconfirm_teacher_id確認(rèn)老師ID關(guān)聯(lián)人員無picker_name、picker_relation接送人姓名與關(guān)系備注remarkremark之所以要拆還有一個(gè)現(xiàn)實(shí)原因簽到記錄一天只有兩條入托離托但接送記錄可能一天有多條比如中午家長(zhǎng)臨時(shí)接走一小時(shí)內(nèi)送回這種特殊場(chǎng)景如果是單表design記錄會(huì)非常混亂。3. 后端接口與小程序端的交互設(shè)計(jì)登錄、鑒權(quán)、請(qǐng)求封裝3.1 微信登錄完整流程code換openid再換業(yè)務(wù)token微信小程序登錄和傳統(tǒng)賬密登錄最大的區(qū)別是它沒有密碼這個(gè)概念。整個(gè)流程基于微信的開放能力小程序端調(diào)用wx.login()拿到一個(gè)臨時(shí)憑證code把code傳給自己的后端接口/api/auth/login后端用code調(diào)用微信的jscode2session接口換回用戶的openid和session_key后端拿著openid查parent_user表存在則直接生成業(yè)務(wù)token返回不存在則自動(dòng)創(chuàng)建新用戶前端把token存到wx.setStorageSync里后續(xù)所有請(qǐng)求都帶這個(gè)token。這里有個(gè)安全誤區(qū)必須提醒openid的換取必須放在后端完成。有些同學(xué)圖方便在小程序端直接請(qǐng)求微信接口換openid這意味著你不得不在小程序代碼里寫死AppSecret而小程序代碼是半公開的AppSecret一旦泄露任何人都能偽造身份。后端核心代碼大致是這樣RestController RequestMapping(/api/auth) public class AuthController { Resource private ParentUserService parentUserService; GetMapping(/login) public Result login(RequestParam String code) { // 1. 調(diào)用微信接口換取openid WxLoginResult wxResult wxService.code2Session(code); if (wxResult.getErrcode() ! 0) { return Result.error(微信登錄失敗 wxResult.getErrmsg()); } // 2. 查詢或創(chuàng)建家長(zhǎng)用戶 String openid wxResult.getOpenid(); ParentUser parent parentUserService.findByOpenid(openid); if (parent null) { parent parentUserService.createByOpenid(openid); } // 3. 簽發(fā)業(yè)務(wù)token String token JwtUtil.createToken(parent.getId(), parent.getRole()); return Result.ok(token); } }3.2 攔截器鑒權(quán)與三種角色的權(quán)限控制系統(tǒng)里一共三類使用者管理員、老師、家長(zhǎng)。接口不能誰都能調(diào)比如家長(zhǎng)不能調(diào)用簽到接口老師不能修改收費(fèi)標(biāo)準(zhǔn)。我的做法是后端統(tǒng)一加一個(gè)AuthInterceptor攔截器所有/api/開頭的請(qǐng)求都會(huì)先經(jīng)過它。攔截器從請(qǐng)求頭里拿token校驗(yàn)簽名和有效期解析出用戶ID和角色放ThreadLocal里供后續(xù)Controller直接取用。Component public class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); if (StringUtils.isBlank(token)) { throw new BizException(401, 未登錄); } // 解析token若過期或非法則拋異常 LoginUser user JwtUtil.parseToken(token); UserContext.set(user); return true; } Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { UserContext.clear(); // 防止線程池復(fù)用導(dǎo)致用戶串號(hào) } }角色控制我用了Spring攔截器的路徑匹配來實(shí)現(xiàn)簡(jiǎn)潔夠用不需要引入Spring Security那么重的框架。比如/api/teacher/**的請(qǐng)求必須角色為TEACHER或ADMIN/api/admin/**必須為ADMIN。這種配置對(duì)一個(gè)畢設(shè)級(jí)別、訪客量不大的系統(tǒng)是合適的真要做大型生產(chǎn)系統(tǒng)再換Security做細(xì)粒度權(quán)限。3.3 小程序端請(qǐng)求封裝統(tǒng)一域名、token注入與錯(cuò)誤處理小程序端的請(qǐng)求不能每次都寫一遍wx.request必須封裝。我習(xí)慣在utils/request.js里統(tǒng)一管理。const BASE_URL http://192.168.1.100:8080/api const request (url, method, data) { return new Promise((resolve, reject) { wx.request({ url: BASE_URL url, method: method || GET, data: data || {}, header: { Authorization: wx.getStorageSync(token) }, success: (res) { if (res.statusCode 200 res.data.code 200) { resolve(res.data.data) } else if (res.data.code 401) { wx.navigateTo({ url: /pages/login/login }) reject(res.data) } else { wx.showToast({ title: res.data.msg, icon: none }) reject(res.data) } }, fail: (err) { wx.showToast({ title: 網(wǎng)絡(luò)異常, icon: none }) reject(err) } }) }) } module.exports { request, BASE_URL }有個(gè)細(xì)節(jié)真機(jī)預(yù)覽時(shí)BASE_URL絕對(duì)不能寫localhost。手機(jī)訪問localhost指向的是手機(jī)自身不是你開發(fā)用的電腦。要連電腦上起的后端必須把BASE_URL改成電腦在局域網(wǎng)里的IP地址比如http://192.168.1.100:8080/api。開發(fā)模式下微信開發(fā)者工具可以勾選不校驗(yàn)合法域名但真機(jī)預(yù)覽需要在詳情設(shè)置里打開調(diào)試模式否則請(qǐng)求會(huì)被攔掉。4. 幾個(gè)容易寫歪的業(yè)務(wù)功能接送授權(quán)、繳費(fèi)流水、請(qǐng)假狀態(tài)機(jī)4.1 接送人白名單安全紅線怎么用代碼表達(dá)接送場(chǎng)景的業(yè)務(wù)痛點(diǎn)在于托管機(jī)構(gòu)必須保證孩子不會(huì)被陌生人接走。所以設(shè)計(jì)上我做了接送人白名單機(jī)制。家長(zhǎng)在小程序端維護(hù)接送白名單也就是student_parent_rel表里is_authorized_pickup1的那些記錄。每新增一個(gè)接送授權(quán)人系統(tǒng)會(huì)要求填寫姓名、關(guān)系、手機(jī)號(hào)。老師在小程序端執(zhí)行離托操作時(shí)需要在接送人列表里選擇此刻來接的是誰并可以調(diào)起攝像頭拍一張接送人的照片作為留存證據(jù)。這個(gè)流程用后端接口表達(dá)如下POST /api/pickup/confirm 參數(shù)studentId, pickerId, photoUrl, remark 邏輯 1. 校驗(yàn)pickerId是否在studentId的白名單里 2. 校驗(yàn)學(xué)生當(dāng)前狀態(tài)是否為在托 3. 寫入pickup_record表 4. 將學(xué)生狀態(tài)置為已離托。第2步的校驗(yàn)特別重要。如果學(xué)生已經(jīng)離托是不能再次執(zhí)行離托操作的否則會(huì)出現(xiàn)一個(gè)學(xué)生被接走兩次的數(shù)據(jù)臟賬。我當(dāng)時(shí)用一個(gè)整型狀態(tài)字段current_status來標(biāo)記學(xué)生當(dāng)下是在托還是離托1表示在托0表示離托簽到時(shí)置為1離托時(shí)置為0并且通過事務(wù)保證這兩步同時(shí)成功或同時(shí)失敗。4.2 費(fèi)用流水表不做余額字段只做變動(dòng)記錄費(fèi)用模塊一開始我犯過一個(gè)設(shè)計(jì)錯(cuò)誤想了直接在student表里加一個(gè)balance余額字段每次繳費(fèi)就加每次計(jì)費(fèi)就減。后來發(fā)現(xiàn)這會(huì)產(chǎn)生嚴(yán)重的數(shù)據(jù)一致性問題——退費(fèi)、調(diào)價(jià)、優(yōu)惠這些操作很難追溯而且并發(fā)時(shí)容易算錯(cuò)。后來我改成了純粹的流水賬設(shè)計(jì)一張fee_record表只記錄每一筆費(fèi)用的變動(dòng)不冗余任何當(dāng)前余額。需要看某個(gè)學(xué)生的繳費(fèi)情況時(shí)就查這張表按時(shí)間排序。費(fèi)用記錄表字段如下字段說明id自增主鍵student_id學(xué)生IDfee_type費(fèi)用類型1月托費(fèi)/2延時(shí)費(fèi)/3餐費(fèi)/4退費(fèi)amount變動(dòng)金額正數(shù)表示應(yīng)收負(fù)數(shù)表示退費(fèi)status狀態(tài)0待支付/1已支付/2已取消create_time生成時(shí)間pay_time支付時(shí)間remark備注比如2024年3月托費(fèi)為什么不做余額字段因?yàn)橥泄馨嗟氖召M(fèi)場(chǎng)景相對(duì)簡(jiǎn)單一個(gè)月就一筆托費(fèi)不像商城那樣有充值、優(yōu)惠、積分等復(fù)雜場(chǎng)景流水賬完全能支持所有查詢需求而且沒有任何一致性問題。如果你發(fā)現(xiàn)項(xiàng)目要求里出現(xiàn)了賬戶余額這種概念那才需要單獨(dú)考慮錢包表在此之前流水賬夠了。4.3 請(qǐng)假流程一個(gè)簡(jiǎn)單的狀態(tài)機(jī)卻有明確的業(yè)務(wù)閉環(huán)請(qǐng)假功能看似只是家長(zhǎng)填個(gè)表單但它牽涉到考勤統(tǒng)計(jì)的聯(lián)動(dòng)。我把它設(shè)計(jì)成三態(tài)狀態(tài)機(jī)申請(qǐng)中家長(zhǎng)提交后狀態(tài)為PENDING已通過老師審核同意后狀態(tài)為APPROVED同時(shí)自動(dòng)生成一條當(dāng)天的考勤標(biāo)記狀態(tài)為請(qǐng)假不記入缺勤已駁回老師審核拒絕后狀態(tài)為REJECTED此時(shí)家長(zhǎng)可以修改日期后重新提交。這個(gè)狀態(tài)機(jī)的核心在于已通過時(shí)對(duì)考勤記錄的操作。如果請(qǐng)假審核通過后考勤表里沒有同步標(biāo)記月底統(tǒng)計(jì)出勤率時(shí)就會(huì)把請(qǐng)假當(dāng)缺勤家長(zhǎng)一投訴就是事故。所以這個(gè)聯(lián)動(dòng)必須放在請(qǐng)假審批通過的同一個(gè)事務(wù)里不能靠定時(shí)任務(wù)去補(bǔ)。請(qǐng)假表的表結(jié)構(gòu)里還有一個(gè)隱藏字段leave_date記錄的是具體請(qǐng)假日期。因?yàn)橥泄馨嗟目记诎刺焖阋粋€(gè)請(qǐng)假申請(qǐng)只對(duì)應(yīng)某一天不做多日連請(qǐng)。如果家長(zhǎng)想請(qǐng)三天假就分別提交三條申請(qǐng)這樣后端邏輯簡(jiǎn)單老師審批也清楚。5. 項(xiàng)目跑通的全流程與典型報(bào)錯(cuò)從零環(huán)境到雙端聯(lián)調(diào)5.1 環(huán)境準(zhǔn)備清單這個(gè)項(xiàng)目后端依賴的核心技術(shù)棧是JDK 8或17看SpringBoot版本、Maven 3.6、MySQL 5.7或8.0前端需要微信開發(fā)者工具。JDK版本這塊特別提醒一下SpringBoot 2.x建議用JDK 8SpringBoot 3.x必須用JDK 17。買的畢設(shè)代碼如果用的是SpringBoot 2.7.x但本機(jī)裝了JDK 17大概率啟動(dòng)時(shí)沒問題但一旦項(xiàng)目里用了javax開頭的包就會(huì)報(bào)ClassNotFoundException。先把項(xiàng)目里的pom.xml打開看一下spring-boot-starter-parent版本再?zèng)Q定裝哪個(gè)JDK別拿到就急著去改環(huán)境變量。MySQL方面建議直接用8.0字符集建庫(kù)時(shí)指定utf8mb4避免學(xué)生姓名里有生僻字時(shí)出現(xiàn)亂碼。建庫(kù)語句CREATE DATABASE tuition_manager DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;5.2 后端啟動(dòng)三步走第一步定基礎(chǔ)環(huán)境。JDK配置好JAVA_HOMEMaven配置好settings.xml里的阿里云鏡像不然拉依賴能把人急死。Maven鏡像配置如下這是每個(gè)Java后端開發(fā)者必會(huì)的操作mirror idaliyunmaven/id mirrorOfcentral/mirrorOf namealiyun maven mirror/name urlhttps://maven.aliyun.com/repository/public/url /mirror第二步建庫(kù)和執(zhí)行SQL。項(xiàng)目源碼里一般會(huì)帶一個(gè)sql目錄里面是完整的建表腳本和初始數(shù)據(jù)。用Navicat直接執(zhí)行就好。如果源碼沒帶SQL文件那要么是作者忘了打包要么是用了MyBatis-Plus的自動(dòng)建表插件啟動(dòng)時(shí)自動(dòng)生成但這種情況反而是隱患。第三步改配置文件。打開src/main/resources/application.yml把數(shù)據(jù)庫(kù)賬號(hào)密碼、微信小程序AppID和AppSecret改掉。這里微信配置如果沒填登錄接口就會(huì)報(bào)所以你得先去微信公眾平臺(tái)注冊(cè)一個(gè)小程序賬號(hào)拿到AppID。個(gè)人主體的注冊(cè)就能拿到AppID不需要認(rèn)證。啟動(dòng)成功后控制臺(tái)會(huì)出現(xiàn)SpringBoot的啟動(dòng)日志訪問http://localhost:8080/一般會(huì)返回404這其實(shí)說明啟動(dòng)成功了只是沒有配根路徑的Controller而已。5.3 小程序前端配置與真機(jī)調(diào)試打開微信開發(fā)者工具導(dǎo)入小程序前端目錄時(shí)需要填寫自己的AppID這一點(diǎn)不要用測(cè)試號(hào)。測(cè)試號(hào)雖然能編譯但很多接口權(quán)限受限。小程序端要改的位置通常只有一個(gè)請(qǐng)求封裝文件里的BASE_URL。調(diào)試階段建議使用局域網(wǎng)IP加后端端口。比如電腦IP是192.168.1.100那就把BASE_URL設(shè)為http://192.168.1.100:8080/api。在開發(fā)者工具的詳情-本地設(shè)置里勾選不校驗(yàn)合法域名、web-view業(yè)務(wù)域名、TLS版本以及HTTPS證書。這個(gè)選項(xiàng)是開發(fā)調(diào)試的生命線不勾選的話請(qǐng)求直接返回fail而且錯(cuò)誤信息藏在控制臺(tái)里不夠顯眼很容易讓人誤判成后端問題。真機(jī)預(yù)覽時(shí)手機(jī)和電腦必須連同一個(gè)局域網(wǎng)Wi-Fi。如果手機(jī)能打開網(wǎng)頁(yè)但小程序請(qǐng)求還是失敗檢查一下電腦防火墻是不是攔了8080端口臨時(shí)關(guān)掉Windows防火墻再試一次成功率會(huì)大幅提升。5.4 我踩過的三個(gè)坑以及對(duì)應(yīng)的排查思路第一個(gè)坑LocalDateTime序列化格式問題前端顯示簽到時(shí)間時(shí)突然變成了平平整整的數(shù)字字符串比如2024-03-01T10:30:00和預(yù)期格式差很遠(yuǎn)。這本質(zhì)上是SpringBoot默認(rèn)的Jackson序列化器對(duì)LocalDateTime的輸出格式不符合我們的展示需求。處理方式有兩種一是字段上加JsonFormat(pattern yyyy-MM-dd HH:mm:ss)二是全局配置。我推薦在application.yml里統(tǒng)一配置spring: jackson: date-format: yyyy-MM-dd HH:mm:ss但這只對(duì)java.util.Date生效對(duì)LocalDateTime不生效。LocalDateTime還是要加上JsonFormat注解或者引入jackson-datatype-jsr310模塊并自定義序列化配置。最省事的做法是實(shí)體類字段上直接標(biāo)注。第二個(gè)坑MyBatis-Plus邏輯刪除與唯一索引的沖突MyBatis-Plus的邏輯刪除是加了一個(gè)deleted字段查詢時(shí)自動(dòng)拼上WHERE deleted 0。但如果某張表的某個(gè)字段設(shè)置了唯一索引當(dāng)過一年的學(xué)生退托被邏輯刪除后第二年重新錄入一個(gè)同名同號(hào)的轉(zhuǎn)學(xué)生就會(huì)觸發(fā)唯一索引沖突因?yàn)檫壿嬌蟿h掉的記錄其實(shí)還在表里。解決思路是在業(yè)務(wù)上繞開問題比如對(duì)學(xué)生的唯一索引設(shè)計(jì)為student_no加class_id組合或使用唯一索引加版本字段。如果畢設(shè)代碼已經(jīng)踩到這個(gè)坑最簡(jiǎn)單的處理方式是把邏輯刪除改成物理刪除畢竟學(xué)生數(shù)據(jù)恢復(fù)場(chǎng)景在托管系統(tǒng)里很少出現(xiàn)。第三個(gè)坑微信code2Session的errcode尤其40029微信登錄第一次聯(lián)調(diào)時(shí)一直報(bào)錯(cuò)code換取openid失敗錯(cuò)誤碼是40029也就是code無效。這個(gè)坑的原因是后端拿到的code已經(jīng)被用過了微信的code是一次性的。常見的觸發(fā)場(chǎng)景有兩個(gè)前端登錄邏輯寫了重試或者后端接口被重復(fù)調(diào)用。比如頁(yè)面onLoad時(shí)執(zhí)行wx.loginonShow時(shí)又執(zhí)行了一次wx.login后一次拿到的code就會(huì)讓前一次的失效。排查時(shí)可以直接在微信開發(fā)者工具的Network面板里看請(qǐng)求次數(shù)一般立即就能發(fā)現(xiàn)問題。最后再說幾句大實(shí)話做這類全棧管理系統(tǒng)最耗時(shí)間的永遠(yuǎn)不是某個(gè)單一功能而是數(shù)據(jù)流轉(zhuǎn)與功能聯(lián)動(dòng)的設(shè)計(jì)。表結(jié)構(gòu)沒想清楚就動(dòng)手寫接口后期一定會(huì)在聯(lián)調(diào)返工上付出成倍的代價(jià)。先把業(yè)務(wù)場(chǎng)景走一遍把所有需要記錄的狀態(tài)和關(guān)系列出來再落數(shù)據(jù)庫(kù)這步做得越扎實(shí)后面寫代碼越快。如果你拿到的就是一套像這樣帶前后端的畢設(shè)源碼我建議不要急著直接跑起來看效果先用Navicat把每個(gè)表都點(diǎn)開看一眼字段再用微信開發(fā)者工具的調(diào)試器逐行追一遍登錄流程。把這套系統(tǒng)真正弄懂比單純跑通拿到一個(gè)結(jié)果有用得多答辯時(shí)老師隨便提個(gè)問題你也能接住。有個(gè)小技巧送給你給老師演示系統(tǒng)前把一些典型數(shù)據(jù)做齊——比如一個(gè)學(xué)生完整的簽到離托記錄、一筆繳費(fèi)流水、一條請(qǐng)假審批記錄——截好圖放在PPT里。老師不一定有時(shí)間看你現(xiàn)場(chǎng)操作但截圖能直觀展示系統(tǒng)的業(yè)務(wù)完整性這是你在答辯時(shí)最能拿分的部分。