設(shè)計(jì)與實(shí)現(xiàn)詳解)
每年到了畢業(yè)季就有不少同學(xué)私信問我畢設(shè)選題的事情。說實(shí)話與其跟風(fēng)做那些重復(fù)度極高的商城系統(tǒng)、圖書管理系統(tǒng)不如做點(diǎn)既有社會(huì)話題性、又能把主流技術(shù)棧完整串起來的項(xiàng)目。我最近幫人review過一套“基于SpringBoot的城市垃圾分類管理系統(tǒng)”的源碼整體質(zhì)量不錯(cuò)而且挺適合做畢設(shè)的。今天就把這套系統(tǒng)的設(shè)計(jì)思路、數(shù)據(jù)庫模型、核心實(shí)現(xiàn)和部署排錯(cuò)心得掰開揉碎了講一講。不管你最后是直接用的開源源碼還是準(zhǔn)備自己從零敲一個(gè)同款這篇文章都能幫你避開不少彎路。1. 項(xiàng)目概述與選題邏輯1.1 為什么垃圾分類管理適合當(dāng)畢設(shè)先聊聊選題。很多同學(xué)在畢設(shè)選題的時(shí)候都卡在“看起來簡單”和“技術(shù)含量夠”之間。垃圾分類管理系統(tǒng)這個(gè)題目看起來是給學(xué)校答辯看的但打開來看它其實(shí)覆蓋了一套完整業(yè)務(wù)系統(tǒng)該有的所有環(huán)節(jié)用戶端、管理端、數(shù)據(jù)存儲、狀態(tài)流轉(zhuǎn)、甚至還有積分激勵(lì)體系。從答辯表現(xiàn)的角度來說這個(gè)選題有個(gè)天然優(yōu)勢——業(yè)務(wù)模型貼近生活評委老師一看就明白你在做什么不用花大量口舌解釋業(yè)務(wù)背景。更重要的是它的功能擴(kuò)展空間很大你可以在標(biāo)準(zhǔn)CRUD之外加上垃圾分類查詢、預(yù)約上門回收、積分兌換、統(tǒng)計(jì)報(bào)表等功能每一塊都能拿出來單獨(dú)講技術(shù)實(shí)現(xiàn)。這套系統(tǒng)的核心價(jià)值在于它完成了從“居民不知道垃圾怎么分”到“分完能拿積分換獎(jiǎng)品”的完整閉環(huán)。用戶端解決“怎么分”的疑惑后臺解決“分類結(jié)果怎么管理”的訴求積分體系則解決了“讓用戶愿意主動(dòng)分類”的激勵(lì)問題。技術(shù)棧上則涵蓋了SpringBoot、MyBatis Plus、MySQL、Redis、Vue等畢設(shè)高頻技術(shù)屬于那種“看起來不炫技但每塊都踩在點(diǎn)子上”的項(xiàng)目。1.2 系統(tǒng)功能模塊全貌我拿到這套源碼第一件事就是畫了一張功能腦圖。主要角色分三種普通居民用戶、小區(qū)管理員、平臺超級管理員。用戶端有注冊登錄、垃圾分類查詢、預(yù)約回收、積分明細(xì)、個(gè)人中心管理端有垃圾類別管理、回收訂單管理、積分規(guī)則配置、公告發(fā)布、數(shù)據(jù)統(tǒng)計(jì)看板。這里需要特別提一下“垃圾類別管理”和“垃圾分類查詢”的區(qū)別。很多同學(xué)一開始把這兩個(gè)功能混在一起做結(jié)果后臺配置改了前端查詢結(jié)果卻不更新。這套源碼的處理方式是垃圾類別條目獨(dú)立成一張表用戶查詢時(shí)實(shí)時(shí)關(guān)聯(lián)后臺每改一條數(shù)據(jù)用戶端下一次搜索立刻就能看到最新結(jié)果不需要重新構(gòu)建緩存。這個(gè)細(xì)節(jié)我后面在代碼解析部分會(huì)展開講。2. 數(shù)據(jù)庫設(shè)計(jì)與核心表結(jié)構(gòu)2.1 四分類業(yè)務(wù)模型怎么落地到表垃圾分類本身有明確的行業(yè)標(biāo)準(zhǔn)可回收物、有害垃圾、廚余垃圾、其他垃圾。不過到了系統(tǒng)設(shè)計(jì)里這個(gè)“四分類”不能簡單地寫成四個(gè)硬編碼字符串否則以后街道想細(xì)化分類比如增加“大件垃圾”“裝修垃圾”你就得改代碼。這套源碼采用了一張字典表 多張業(yè)務(wù)表的策略。字典表存分類的基本信息比如分類名稱、圖標(biāo)、描述、投放要求業(yè)務(wù)表存具體的垃圾條目比如“塑料瓶”“廢電池”“剩菜剩飯”每個(gè)條目通過category_id關(guān)聯(lián)到字典表。這樣做的好處是數(shù)據(jù)結(jié)構(gòu)上有主外鍵約束統(tǒng)計(jì)報(bào)表按分類聚合時(shí)SQL寫起來非常順手。值得留意的是預(yù)約回收模塊單獨(dú)建了一張訂單表訂單里既有用戶信息和預(yù)約時(shí)段也有垃圾的重量估算和回收狀態(tài)。這個(gè)設(shè)計(jì)把“知識庫查詢”和“業(yè)務(wù)流程”完全隔離開不會(huì)因?yàn)槔鴹l目刪改影響到訂單歷史數(shù)據(jù)。2.2 核心表結(jié)構(gòu)設(shè)計(jì)與建表參考看源碼的時(shí)候我習(xí)慣先把建表SQL過一遍因?yàn)楸斫Y(jié)構(gòu)能直接反映開發(fā)者的數(shù)據(jù)建模水平。下面這張表就是核心的用戶表表結(jié)構(gòu)設(shè)計(jì)得比較規(guī)范CREATE TABLE user ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主鍵, phone varchar(11) DEFAULT NULL COMMENT 手機(jī)號, password varchar(128) DEFAULT NULL COMMENT 密碼BCrypt加密, nickname varchar(32) DEFAULT NULL COMMENT 昵稱, avatar_url varchar(255) DEFAULT NULL COMMENT 頭像地址, role tinyint(4) DEFAULT 0 COMMENT 角色0普通用戶 1管理員, status tinyint(4) DEFAULT 1 COMMENT 狀態(tài)1正常 0禁用, create_time datetime DEFAULT NULL COMMENT 創(chuàng)建時(shí)間, update_time datetime DEFAULT NULL COMMENT 更新時(shí)間, PRIMARY KEY (id), UNIQUE KEY uk_phone (phone) ) ENGINEInnoDB AUTO_INCREMENT1 DEFAULT CHARSETutf8mb4 COMMENT用戶表;這里有兩個(gè)細(xì)節(jié)值得抄作業(yè)。一是role字段用數(shù)字而不是直接用字符串user/admin這樣擴(kuò)展角色的時(shí)候不用動(dòng)表結(jié)構(gòu)。二是密碼字段長度給了128因?yàn)橛玫氖荁Crypt加密加密后的字符串比MD5長不少如果你還是用char(32)后面存密文會(huì)直接報(bào)錯(cuò)。垃圾條目表的設(shè)計(jì)也很典型CREATE TABLE garbage_item ( id bigint(20) NOT NULL AUTO_INCREMENT, name varchar(64) NOT NULL COMMENT 垃圾名稱, category_id bigint(20) NOT NULL COMMENT 分類ID, description varchar(255) DEFAULT NULL COMMENT 詳細(xì)說明, recycle_method varchar(255) DEFAULT NULL COMMENT 投放/回收方式, status tinyint(4) DEFAULT 1 COMMENT 狀態(tài)1上架 0下架, create_time datetime DEFAULT NULL, PRIMARY KEY (id), KEY idx_category (category_id), CONSTRAINT fk_category FOREIGN KEY (category_id) REFERENCES garbage_category (id) ON DELETE RESTRICT ON UPDATE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT垃圾條目表;外鍵約束很多人會(huì)忽略但我們自己建表最好還是加上。雖然網(wǎng)上有說法說互聯(lián)網(wǎng)公司為了性能不用外鍵可畢設(shè)場景里外鍵的存在反而是加分項(xiàng)——它能證明你懂?dāng)?shù)據(jù)完整性而且答辯時(shí)老師很可能問“垃圾類目刪了訂單數(shù)據(jù)怎么辦”這時(shí)你可以說使用ON DELETE RESTRICT限制了直接刪除必須先把關(guān)聯(lián)數(shù)據(jù)處理掉這就是一個(gè)很好的答辯論據(jù)。預(yù)約回收訂單表我單獨(dú)拿出來說因?yàn)樗钦麖垬I(yè)務(wù)鏈路的主軸CREATE TABLE recycle_order ( id bigint(20) NOT NULL AUTO_INCREMENT, order_no varchar(32) NOT NULL COMMENT 訂單編號, user_id bigint(20) NOT NULL COMMENT 下單用戶, category_id bigint(20) DEFAULT NULL COMMENT 回收大類, appointment_time datetime DEFAULT NULL COMMENT 預(yù)約上門時(shí)間, address varchar(255) DEFAULT NULL COMMENT 回收地址, weight_estimate decimal(10,2) DEFAULT NULL COMMENT 預(yù)估重量(kg), status tinyint(4) DEFAULT 0 COMMENT 狀態(tài)0待接單 1已接單 2已完成 3已取消, points_rewarded int(11) DEFAULT 0 COMMENT 獎(jiǎng)勵(lì)積分, remark varchar(255) DEFAULT NULL COMMENT 備注, PRIMARY KEY (id), KEY idx_user (user_id), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT回收訂單表;訂單號的生成方式在源碼里是直接用yyyyMMddHHmmss 隨機(jī)數(shù)這種方案的優(yōu)點(diǎn)是簡單直觀不會(huì)有并發(fā)主鍵沖突。如果想更嚴(yán)謹(jǐn)一點(diǎn)可以用數(shù)據(jù)庫自增 Redis自增拼接不過對體量不大的系統(tǒng)來說已經(jīng)夠用了。3. 技術(shù)棧與工程結(jié)構(gòu)解析3.1 后端技術(shù)棧為什么這么選SpringBoot 2.x MyBatis Plus MySQL Redis JWT這套組合在畢設(shè)圈里算是“標(biāo)配中的頂配”。選SpringBoot不奇怪它省掉了Spring MVC時(shí)代那一大堆XML配置起步快、社區(qū)活躍遇到問題一搜一大把解決方案。MyBatis Plus作為MyBatis的增強(qiáng)工具最大價(jià)值就是單表操作不用寫SQL。比如刪除一個(gè)條目只需要調(diào)用garbageItemMapper.deleteById(id)。自由組合條件查詢可以用它的LambdaQueryWrapper既安全又能避免字符串字段名寫錯(cuò)導(dǎo)致運(yùn)行時(shí)才報(bào)錯(cuò)的問題。對于畢設(shè)交作業(yè)這個(gè)場景來說MyBatis Plus能讓你把時(shí)間花在業(yè)務(wù)邏輯上而不是毫無技術(shù)含量的CRUD模板代碼里。Redis在這個(gè)項(xiàng)目里當(dāng)了兩類角色一類是緩存比如首頁的垃圾分類熱榜另一類是JWT Token的存儲。講個(gè)實(shí)際例子后臺修改了用戶狀態(tài)為禁用后已經(jīng)登錄的用戶理論上還能繼續(xù)訪問接口。這種場景靠前端判斷不夠可靠后端在攔截器里每次校驗(yàn)Token的同時(shí)順帶查一下Redis里的用戶狀態(tài)如果已被禁用就直接拒絕。這個(gè)小技巧答辯時(shí)講出來老師是會(huì)點(diǎn)頭的。前端用的是Vue Element UI典型的后臺管理系統(tǒng)組合。因?yàn)檫@套源碼把前后端完全分離了所以開發(fā)時(shí)兩邊獨(dú)立跑端口聯(lián)調(diào)階段靠CORS配置放行不同源的請求。后面我在部署環(huán)節(jié)會(huì)專門說明這個(gè)配置細(xì)節(jié)。3.2 工程目錄結(jié)構(gòu)與啟動(dòng)流程SpringBoot項(xiàng)目看結(jié)構(gòu)基本就能猜出作者的代碼風(fēng)格。這套源碼的工程結(jié)構(gòu)是標(biāo)準(zhǔn)的單模塊分層src/main/java/com/example/garbage ├── config # 配置類跨域、MyBatis Plus分頁等 ├── controller # 接口層 ├── service # 業(yè)務(wù)邏輯層 │ ├── impl # 業(yè)務(wù)實(shí)現(xiàn) ├── mapper # 數(shù)據(jù)訪問層 ├── entity # 實(shí)體類 ├── dto # 請求/響應(yīng)封裝對象 ├── common # 公共類統(tǒng)一返回結(jié)果、異常處理、常量 ├── utils # 工具類JWT工具、日期工具 └── GarbageApplication.java啟動(dòng)流程也不復(fù)雜三步走先創(chuàng)建數(shù)據(jù)庫并導(dǎo)入garbage.sql然后修改application.yml里的數(shù)據(jù)庫賬號密碼最后運(yùn)行GarbageApplication.java的main方法。整個(gè)過程沒有任何分布式中間件的強(qiáng)依賴單機(jī)就能跑起來這也符合畢設(shè)演示的實(shí)際情況。有點(diǎn)需要提醒的源碼里的application.yml有多個(gè)配置項(xiàng)比如redis.host、file.upload-path、jwt.secret等別看漏了。有些同學(xué)啟動(dòng)報(bào)錯(cuò)其實(shí)是Redis連不上但日志只給了“連接超時(shí)”讓人誤以為是SpringBoot本身的問題白白排查老半天。4. 核心功能實(shí)現(xiàn)與代碼細(xì)節(jié)4.1 登錄鑒權(quán)JWT方案登錄模塊看起來簡單但真正動(dòng)手做過的人都知道坑不少。這套源碼用的是JWT Redis的解決方案用戶登錄成功后后端生成一個(gè)Token返回給前端前端在請求頭里帶上Authorization: Bearer token后端寫了一個(gè)攔截器統(tǒng)一校驗(yàn)。生成Token的代碼大致是這樣public String generateToken(Long userId, String role) { return Jwts.builder() .setSubject(String.valueOf(userId)) .claim(role, role) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() 24 * 60 * 60 * 1000)) .signWith(SignatureAlgorithm.HS256, secretKey) .compact(); }這里有幾個(gè)點(diǎn)值得細(xì)看。一是簽發(fā)時(shí)間setIssuedAt和過期時(shí)間setExpiration一定要都設(shè)置有些教程只設(shè)置過期時(shí)間導(dǎo)致生成的Token沒有簽發(fā)時(shí)間后面排查問題會(huì)很被動(dòng)。二是密鑰secretKey不要硬編碼在代碼里雖然畢設(shè)項(xiàng)目一般不會(huì)有人真的攻擊你的服務(wù)但在課上被老師看到你把密鑰寫在常量類里印象分還是會(huì)打折的我傾向于放到配置文件里引用。攔截器校驗(yàn)的邏輯也不復(fù)雜先從請求頭里取Token解析成功后再查Redis是否還在有效期內(nèi)兩步都通過才放行。有些同學(xué)問為什么Token本身已經(jīng)包含了過期時(shí)間還要查Redis答案其實(shí)我前面提過——為了支持“強(qiáng)制下線”這種主動(dòng)注銷場景你不用Redis的話后端根本沒有辦法提前作廢一個(gè)還沒過期的Token。4.2 垃圾分類查詢與知識庫用戶在前端搜索框輸入“塑料瓶”系統(tǒng)返回該垃圾條目以及對應(yīng)的分類名稱、投放建議和回收方式這個(gè)功能就是知識庫的核心。源碼里用的是LambdaQueryWrapper做模糊搜索public ListGarbageItemVO searchGarbage(String keyword) { LambdaQueryWrapperGarbageItem wrapper new LambdaQueryWrapper(); wrapper.like(item - item.getName(), keyword) .and(item - item.getCategoryName() ! null item.getStatus() 1); ListGarbageItem items garbageItemMapper.selectList(wrapper); return convertToVO(items); }like用的是SQL里的%keyword%寫法這個(gè)做法對中小型數(shù)據(jù)量完全沒壓力。但如果垃圾條目表到了幾十萬條規(guī)模%keyword%是走不了索引的那時(shí)候就該考慮全文檢索方案比如MySQL全文索引或者ES。不過畢設(shè)項(xiàng)目把所有條目全量插進(jìn)去也就幾千條用like完全OK答辯時(shí)如果老師問性能你把這個(gè)擴(kuò)展思路講出來比單純說“能用”要有力得多。搜索接口的響應(yīng)設(shè)計(jì)成統(tǒng)一返回結(jié)構(gòu)我用過一次之后發(fā)現(xiàn)這個(gè)習(xí)慣還挺重要{ code: 200, message: success, data: { list: [...], total: 5 } }前端的Vue里對response.code做了判斷非200統(tǒng)一彈ElMessage提示錯(cuò)誤。這種統(tǒng)一返回結(jié)構(gòu)減少了前后端聯(lián)調(diào)的溝通成本也讓接口風(fēng)格看起來更專業(yè)我在自己日常的項(xiàng)目里也一直沿用這個(gè)模式。4.3 預(yù)約回收與積分閉環(huán)預(yù)約回收這個(gè)環(huán)節(jié)是區(qū)分“課設(shè)CRUD”和“更像真實(shí)業(yè)務(wù)系統(tǒng)”的分水嶺。用戶選擇垃圾類別、填寫上門時(shí)間地址、提交訂單后訂單狀態(tài)為“待接單”管理員在后臺把訂單改成“已接單”然后等實(shí)際上門完成后再改成“已完成”。這里面的狀態(tài)流轉(zhuǎn)源碼里不是簡單地用if else硬寫而是提取了一個(gè)訂單狀態(tài)枚舉類各個(gè)狀態(tài)之間還做了合法流轉(zhuǎn)校驗(yàn)。這個(gè)設(shè)計(jì)值得學(xué)習(xí)的地方在于它把訂單狀態(tài)從“字符串魔改”變成了“類型安全”的對象。比如你要從“待接單”直接跳到“已完成”系統(tǒng)會(huì)直接拒絕因?yàn)槊杜e里定義的狀態(tài)機(jī)根本不允許這個(gè)跳躍。畢設(shè)項(xiàng)目能做出這種細(xì)節(jié)說明你對業(yè)務(wù)有認(rèn)真思考。積分發(fā)放的邏輯放在訂單被標(biāo)記為“已完成”的時(shí)候觸發(fā)而不是在“已接單”階段就發(fā)放。這個(gè)細(xì)節(jié)很關(guān)鍵因?yàn)樗WC了用戶真正把垃圾交出去之后才能拿到獎(jiǎng)勵(lì)。積分的計(jì)算規(guī)則是估算重量大于0就按重量 * 權(quán)重?fù)Q算默認(rèn)情況下每公斤計(jì)10分。源碼里把權(quán)重直接做成了一張配置表方便管理員在后臺隨時(shí)調(diào)整。這套配置化的思路也可以套用在簽到積分、評論積分等場景里。4.4 管理后臺與統(tǒng)計(jì)報(bào)表管理后臺是整個(gè)系統(tǒng)的“面子工程”也是答辯老師最喜歡演示的部分。源碼里集成了ECharts用幾個(gè)簡單的折線圖、柱狀圖和餅圖來展示各小區(qū)回收單量的趨勢、垃圾類別的占比、用戶積分排行等數(shù)據(jù)。統(tǒng)計(jì)接口的實(shí)現(xiàn)很有意思不是到業(yè)務(wù)庫里做復(fù)雜的多表聯(lián)查而是單獨(dú)建了一張daily_statistics表每天定時(shí)任務(wù)跑一次把昨天的訂單數(shù)、總重量、分類統(tǒng)計(jì)都預(yù)處理好。前端查詢的時(shí)候直接讀這張表性能自然快。這個(gè)設(shè)計(jì)在真實(shí)企業(yè)項(xiàng)目里就是“日報(bào)表預(yù)處理”在畢設(shè)里用上它等于告訴老師你理解“讀多寫少的數(shù)據(jù)可以提前聚合”這個(gè)優(yōu)化原則。ECharts前端配置也不復(fù)雜Vue里在mounted鉤子里調(diào)用后端接口拿到數(shù)據(jù)后直接setOption渲染圖表。唯一提醒的是圖表容器的寬度高度一定要通過style指定我曾經(jīng)見過學(xué)生把div寬度寫成100%結(jié)果圖表渲染出來只有一條線怎么查都查不出來原因后來發(fā)現(xiàn)是父容器沒給高度導(dǎo)致ECharts初始化時(shí)拿到的尺寸是0。5. 本地部署與踩坑實(shí)錄5.1 環(huán)境準(zhǔn)備與配置清單部署這套系統(tǒng)的硬件門檻很低一臺8G內(nèi)存的筆記本電腦就夠。軟件環(huán)境方面建議統(tǒng)一用這些版本能最大程度避免版本兼容問題依賴項(xiàng)推薦版本說明JDK8 或 11Spring Boot 2.x 必須用 8別用21除非你改了全套依賴Maven3.6.x3.8以上也可以但鏡像源需要配好MySQL5.7 或 8.0建庫用utf8mb4否則中文可能亂碼Redis6.x默認(rèn)端口6379密碼可留空Node.js12~16前端Vue項(xiàng)目npm install用的application.yml里最重要的幾個(gè)配置項(xiàng)如下server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/garbage_db?useUnicodetruecharacterEncodingutf8mb4useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 redis: host: localhost port: 6379 jwt: secret: your-secret-key expire-hours: 24有同學(xué)反映MySQL8.0連接時(shí)提示Public Key Retrieval is not allowed這是因?yàn)檫B接串里少了allowPublicKeyRetrievaltrue。JDBC寫法如下url: jdbc:mysql://localhost:3306/garbage_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue原來是MySQL8默認(rèn)使用caching_sha2_password認(rèn)證客戶端第一次連接時(shí)想拿服務(wù)端公鑰結(jié)果被安全策略擋住。加上這個(gè)參數(shù)就解決了。5.2 常見部署報(bào)錯(cuò)對照表這套系統(tǒng)我在不同電腦上跑過多次也幫人遠(yuǎn)程看過不少錯(cuò)誤日志大部分問題集中在下面這幾類報(bào)錯(cuò)現(xiàn)象根本原因解決辦法Failed to configure a DataSource數(shù)據(jù)庫連接配置錯(cuò)誤或驅(qū)動(dòng)缺失檢查application.yml的url/賬號密碼Table garbage_db.xxx doesnt exist建庫導(dǎo)表不完整用Navicat或命令行重新執(zhí)行g(shù)arbage.sqlERR Client sent AUTH, but no password is setRedis有密碼但沒配置在配置里加上redis.password或清掉本地Redis密碼端口被占用: 8080其他程序占了端口改server.port或查進(jìn)程殺掉npm run serve報(bào)Node Sass/ Sass相關(guān)錯(cuò)誤Node版本和sass-loader不兼容換成Node14或者重裝node-sass端口占用這個(gè)問題我多說一句。Windows下可以先netstat -ano | findstr :8080找到PID后到任務(wù)管理器結(jié)束進(jìn)程。macOS/Linux用lsof -i:8080。這種情況在實(shí)驗(yàn)室電腦上特別常見因?yàn)樯弦粚脤W(xué)長可能跑過別的SpringBoot項(xiàng)目沒關(guān)干凈。5.3 前后端聯(lián)調(diào)時(shí)的跨域配置前后端分離后前端跑在http://localhost:5173后端跑在http://localhost:8080兩個(gè)端口不同瀏覽器默認(rèn)會(huì)攔截非同源的Ajax請求。這時(shí)候需要在后端配置CORS。源碼里的做法是寫一個(gè)WebMvcConfigurer配置類Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }要注意的是allowedOriginPatterns(*)和allowCredentials(true)搭配是SpringBoot 2.4以后允許的寫法早期的allowedOrigins(*)在這種組合下會(huì)報(bào)錯(cuò)。前后端跑在一個(gè)服務(wù)器上部署時(shí)這個(gè)跨域配置可以去掉改成Nginx統(tǒng)一轉(zhuǎn)發(fā)效果更好。6. 從畢設(shè)源碼到答辯加分拿到一套能跑的源碼只是起點(diǎn)真正拉開分差的是你對代碼的理解深度。我建議你在答辯前把這幾條鏈路親手走一遍注冊用戶 → 搜索垃圾條目 → 提交預(yù)約回收 → 管理員后臺接單 → 標(biāo)記完成 → 用戶積分變化 → 積分明細(xì)展示。這條鏈路從頭到尾能串起來說明你理解了系統(tǒng)的數(shù)據(jù)流任何一環(huán)答不上來都容易露怯。一個(gè)比較實(shí)用的小技巧把源碼里的一些關(guān)鍵表比如recycle_order的ER關(guān)系、狀態(tài)機(jī)流轉(zhuǎn)圖、系統(tǒng)架構(gòu)圖提前畫好打印成紙質(zhì)版帶去答辯現(xiàn)場。老師提問時(shí)你直接指著圖講既省時(shí)間又顯得專業(yè)。很多同學(xué)只顧著把代碼跑起來卻忽略“可視化表達(dá)”這件事等于白丟了一部分印象分。最后一個(gè)建議如果你打算在源碼基礎(chǔ)上改功能優(yōu)先改“積分規(guī)則配置”和“統(tǒng)計(jì)報(bào)表”這兩個(gè)模塊。因?yàn)檫@兩個(gè)地方最容易被老師追問“為什么這樣設(shè)計(jì)”而你也最容易結(jié)合自己的改動(dòng)說出真實(shí)思考。比如給積分規(guī)則增加一個(gè)“按投放次數(shù)加權(quán)”的參數(shù)前端再配合改一個(gè)表單工作量不大但講出來的效果完全不一樣。跑通代碼只是開始把每條數(shù)據(jù)流、每個(gè)決策的前因后果講明白才是畢設(shè)真正想考察的東西。希望這篇拆解能幫你少走幾步彎路。