發(fā)江西美食推薦畢設(shè)全攻略)
每年畢業(yè)設(shè)計(jì)季總有學(xué)弟學(xué)妹抱著選題列表來(lái)問(wèn)我“老師這個(gè)江西美食推薦小程序能不能做會(huì)不會(huì)太難”我的回答通常很直接這個(gè)題不僅能做而且是很舒服的畢設(shè)方向。贛菜品類豐富、地市特征明顯數(shù)據(jù)隨便一整理就是幾十條優(yōu)質(zhì)內(nèi)容微信小程序加后端接口技術(shù)棧完整又不超綱再配上一份能跑的源碼起步難度直接降一半。這篇文章我就把這個(gè)項(xiàng)目的整體設(shè)計(jì)、核心實(shí)現(xiàn)、數(shù)據(jù)庫(kù)細(xì)節(jié)、以及我親手做一遍踩過(guò)的坑全部攤開(kāi)講清楚供準(zhǔn)備做同類題目的同學(xué)參考。1. 畢設(shè)選題解讀為什么是江西美食加微信小程序1.1 選題背后的三層邏輯畢業(yè)設(shè)計(jì)這件事本質(zhì)上是向評(píng)委證明三件事系統(tǒng)能跑、邏輯能講、論文能寫(xiě)。圍繞這個(gè)目標(biāo)來(lái)看“江西美食推薦微信小程序”它的優(yōu)勢(shì)非常明顯。第一業(yè)務(wù)場(chǎng)景自帶“數(shù)據(jù)源”。江西是魚(yú)米之鄉(xiāng)贛菜有南昌菜、贛南菜、九江菜等不同流派南昌拌粉、瓦罐湯、藜蒿炒臘肉、贛南小炒魚(yú)、蓮花血鴨、堿水粑……隨口就能報(bào)出幾十道有代表性的美食。這就意味著你的數(shù)據(jù)庫(kù)天然有填充內(nèi)容不需要編造“測(cè)試數(shù)據(jù)1、測(cè)試數(shù)據(jù)2”頁(yè)面一渲染出來(lái)就有真實(shí)感和生活氣息截圖放進(jìn)論文里也好看。第二技術(shù)棧覆蓋廣但難度可控。小程序端涉及頁(yè)面開(kāi)發(fā)、組件通信、生命周期后端涉及登錄鑒權(quán)、分頁(yè)查詢、條件搜索、文件上傳推薦邏輯可以做得簡(jiǎn)單也可以做得復(fù)雜數(shù)據(jù)庫(kù)涉及表結(jié)構(gòu)設(shè)計(jì)和關(guān)聯(lián)查詢。一套流程走下來(lái)大學(xué)四年學(xué)的課程基本都串起來(lái)了又不會(huì)像“分布式秒殺系統(tǒng)”那樣失控到做不完。第三評(píng)委眼里的“真實(shí)感”和“差異化”。同樣是管理系統(tǒng)圖書(shū)管理、學(xué)生信息管理已經(jīng)爛大街評(píng)委看一眼題目就失去興趣。美食推薦則自帶文化故事你能講“整理了江西11個(gè)地市的特色美食數(shù)據(jù)”能講“幫助外地游客快速了解贛菜”這種有場(chǎng)景價(jià)值的選題在答辯環(huán)節(jié)天然占優(yōu)勢(shì)。1.2 項(xiàng)目目標(biāo)和功能邊界怎么劃做畢設(shè)最忌諱的就是“什么都想做”最后什么都做不完整。我當(dāng)時(shí)給這個(gè)項(xiàng)目劃定的目標(biāo)是做一個(gè)支持瀏覽、搜索、推薦、收藏、評(píng)分、評(píng)論的完整小程序數(shù)據(jù)聚焦江西本地美食按地市、菜系、辣度、場(chǎng)景標(biāo)簽分類后端接口規(guī)范、代碼結(jié)構(gòu)清晰。具體功能模塊規(guī)劃如下首頁(yè)推薦綜合用戶口味偏好、美食熱度、評(píng)分加權(quán)排序分類瀏覽按地市、菜系、辣度、一日三餐場(chǎng)景篩選搜索關(guān)鍵詞模糊匹配美食名稱、介紹、標(biāo)簽美食詳情圖文介紹、推薦理由、價(jià)格區(qū)間、辣度等級(jí)用戶系統(tǒng)微信一鍵登錄、收藏、1-5星評(píng)分、文字評(píng)論個(gè)人中心我的收藏、我的評(píng)論、偏好設(shè)置同時(shí)要主動(dòng)放棄一些功能不做在線點(diǎn)餐、不做外賣下單、不做商家入駐。這些功能涉及支付、定位、物流、商家后臺(tái)對(duì)畢設(shè)來(lái)說(shuō)嚴(yán)重超綱而且答辯時(shí)容易被追問(wèn)到答不上來(lái)。把“美食推薦”這個(gè)垂直場(chǎng)景做透比畫(huà)一張大餅強(qiáng)得多。2. 技術(shù)方案選型原生小程序加Spring Boot的組合怎么定2.1 前端方案原生小程序還是uni-app這是很多人在技術(shù)選型階段糾結(jié)最久的問(wèn)題。我的建議非常明確如果交付物鎖死是“微信小程序”直接用原生小程序框架。原生小程序的好處是調(diào)試鏈路最短。微信開(kāi)發(fā)者工具里報(bào)錯(cuò)直接定位到具體頁(yè)面文件和代碼行所見(jiàn)即所得不需要經(jīng)過(guò)任何中間層轉(zhuǎn)換。原生框架對(duì)登錄、地圖、上傳等官方API的支持也是最新的不用等框架適配。從答辯的角度講評(píng)委問(wèn)“你這個(gè)自定義組件怎么實(shí)現(xiàn)”你直接說(shuō)用Component構(gòu)造器比說(shuō)“我用uni-app封裝的”更有技術(shù)說(shuō)服力。這里把兩個(gè)方案的差異列成一張表方便你直接參考對(duì)比項(xiàng)原生微信小程序uni-app語(yǔ)法體系WXML/WXSS/JS貼近小程序底層Vue語(yǔ)法跨端復(fù)用調(diào)試體驗(yàn)開(kāi)發(fā)者工具直接定位報(bào)錯(cuò)清晰多一層編譯定位偶爾繞彎多端發(fā)布僅微信小程序可打包App、H5、抖音等包體積風(fēng)險(xiǎn)相對(duì)可控資源依賴分析直觀有source size exceed 2MB的經(jīng)典坑答辯敘事“原生開(kāi)發(fā)”更直接“跨端框架”是加分還是減分看老師學(xué)習(xí)成本小程序API為主需要Vue基礎(chǔ)不熟反而更痛苦順帶說(shuō)一句如果選了uni-app打包時(shí)遇到“source size exceed max limit 2mb”是高頻問(wèn)題。小程序主包限制2MB處理辦法是壓縮圖片、移除冗余依賴、配置分包subPackages。這個(gè)問(wèn)題不是不能解決但會(huì)占用寶貴的開(kāi)發(fā)時(shí)間。畢設(shè)有這個(gè)精力不如多打磨推薦邏輯。2.2 后端方案為什么推薦Spring Boot后端方案在Java和Node.js之間選。如果你對(duì)Java更熟悉或者課程里主要學(xué)的是Java那Spring Boot是首選。這套組合在網(wǎng)上資料極多遇到問(wèn)題搜索一下就有解決方案對(duì)畢設(shè)來(lái)說(shuō)“可查性”是最重要的生產(chǎn)力。我推薦的具體版本組合是Spring Boot 2.x MyBatis Plus MySQL 5.7或8.0。注意Spring Boot別追新到3.x3.x要求Java 17很多學(xué)生本機(jī)的JDK版本還停留在8或11環(huán)境不一致會(huì)引出莫名其妙的兼容問(wèn)題。而且網(wǎng)上絕大多數(shù)教程和代碼示例都是基于2.x寫(xiě)的遇到問(wèn)題更好排查。前后端交互統(tǒng)一走RESTful接口數(shù)據(jù)格式用JSON。Controller只做參數(shù)接收和結(jié)果包裝Service層放業(yè)務(wù)邏輯Mapper層做數(shù)據(jù)庫(kù)操作。這個(gè)分層結(jié)構(gòu)不復(fù)雜但足夠清晰論文里的架構(gòu)圖就按這個(gè)畫(huà)順理成章。2.3 數(shù)據(jù)存儲(chǔ)與資源方案數(shù)據(jù)存儲(chǔ)用MySQL單庫(kù)即可建庫(kù)字符集選utf8mb4因?yàn)槊朗辰榻B里可能出現(xiàn)特殊字符和emojiutf8mb4才能完整存儲(chǔ)。緩存方面畢設(shè)階段不用硬上Redis幾百條美食數(shù)據(jù)MySQL查詢毫秒級(jí)返回加一層緩存反而增加部署復(fù)雜度。圖片資源是另一個(gè)需要注意的點(diǎn)。美食類小程序圖片必不可少但小程序包有2MB限制圖片不能全部塞進(jìn)代碼包。策略是圖片上傳到后端服務(wù)器或云存儲(chǔ)數(shù)據(jù)庫(kù)存URL地址小程序端用image組件的src屬性加載外鏈。開(kāi)發(fā)階段可以先放一批壓縮過(guò)的本地圖片但后續(xù)要遷移到外鏈否則包體積很容易超限。3. 核心功能實(shí)現(xiàn)推薦、搜索、收藏一個(gè)都不能少3.1 微信登錄從wx.login到自定義token的完整鏈路微信小程序登錄的完整鏈路是這樣的前端調(diào)用wx.login拿到臨時(shí)code把code發(fā)給后端后端拿著code請(qǐng)求微信接口jscode2session換取openid和session_keyopenid是用戶在當(dāng)前小程序下的唯一標(biāo)識(shí)但不能直接暴露給前端所以后端要自己生成一個(gè)token返回給前端小程序端把token存到storage里之后每次請(qǐng)求都在請(qǐng)求頭帶上后端攔截器校驗(yàn)token有效性。這里有幾個(gè)非常容易踩的坑。第一個(gè)appid和secret必須和開(kāi)發(fā)者工具里的一致很多同學(xué)開(kāi)發(fā)時(shí)用測(cè)試號(hào)后面又換正式號(hào)后端配置忘了同步登錄就一直失敗。第二個(gè)現(xiàn)在微信對(duì)獲取手機(jī)號(hào)的限制很嚴(yán)個(gè)人主體小程序無(wú)法調(diào)用getPhoneNumber接口畢設(shè)里如果不需要手機(jī)號(hào)就別硬做用戶表留一個(gè)phone字段讓用戶自己填寫(xiě)功能上也算實(shí)現(xiàn)了。關(guān)于token用UUID或者JWT都可以。畢設(shè)里用UUID存數(shù)據(jù)庫(kù)邏輯更簡(jiǎn)單用JWT則免去查庫(kù)步驟各有優(yōu)劣。我建議用UUID理由是小程序用戶量不大查一次庫(kù)的開(kāi)銷可以忽略而且代碼更好理解答辯時(shí)解釋起來(lái)也輕松。3.2 江西美食數(shù)據(jù)模型讓地方味道變成結(jié)構(gòu)化字段美食數(shù)據(jù)的建模直接決定整個(gè)項(xiàng)目的觀感。我設(shè)計(jì)了這幾張核心表food美食、city地市、category分類、user用戶、favorite收藏、comment評(píng)論。food表是關(guān)鍵字段設(shè)計(jì)如下name美食名稱city_id所屬地市關(guān)聯(lián)city表category_id所屬分類如早餐小吃、贛菜熱菜、湯羹燉品image圖片URLintroduction詳細(xì)介紹taste_tags口味標(biāo)簽逗號(hào)分隔如“辣,咸,鮮”spicy_level辣度等級(jí)1-5price_range人均價(jià)格區(qū)間recommend_reason推薦理由這是美食類項(xiàng)目獨(dú)有的亮點(diǎn)字段heat熱度值rating_num和rating_sum評(píng)分人數(shù)與總分實(shí)時(shí)算平均分江西美食的數(shù)據(jù)整理我花了不少功夫。南昌拌粉、瓦罐湯、藜蒿炒臘肉屬于南昌九江茶餅、修水哨子在九江贛南小炒魚(yú)、寧都三杯雞在贛州萍鄉(xiāng)有蓮花血鴨上饒有廣豐炒粉景德鎮(zhèn)有堿水粑和冷粉鷹潭有上清豆腐……每個(gè)地市至少錄入3到5道代表美食整個(gè)數(shù)據(jù)集一下就豐滿了。值得多說(shuō)一句的是recommend_reason字段。比如瓦罐湯的推薦理由我寫(xiě)的是“南昌人的早餐從一罐湯開(kāi)始肉餅湯配拌粉才是最地道的打開(kāi)方式”這種有生活氣息的文案不僅讓頁(yè)面更有溫度答辯時(shí)截圖也更有說(shuō)服力。3.3 首頁(yè)推薦邏輯可解釋的標(biāo)簽加權(quán)排序既然是“推薦”小程序推薦邏輯就得有說(shuō)法。我不建議上機(jī)器學(xué)習(xí)一方面數(shù)據(jù)量不夠訓(xùn)練另一方面答辯時(shí)容易說(shuō)不清楚。更聰明的做法是做一套“標(biāo)簽加權(quán)熱度修正”的推薦策略規(guī)則透明可解釋性強(qiáng)。具體思路是用戶首次進(jìn)入可以設(shè)置口味偏好比如能接受的辣度、喜歡的菜品類型。后端計(jì)算推薦分時(shí)依次疊加規(guī)則美食辣度在用戶可接受范圍內(nèi)加50分美食標(biāo)簽命中用戶偏好標(biāo)簽每個(gè)加20分熱度值乘以0.1計(jì)入總分平均評(píng)分乘以10計(jì)入總分這個(gè)策略的好處是每一分都有業(yè)務(wù)依據(jù)。辣度匹配保證用戶不會(huì)踩雷標(biāo)簽匹配體現(xiàn)個(gè)性化熱度代表市場(chǎng)認(rèn)可評(píng)分代表用戶口碑。答辯時(shí)評(píng)委問(wèn)“為什么這么設(shè)計(jì)”你可以從這四個(gè)維度正面回答邏輯無(wú)懈可擊。代碼層面核心就是用Java的Comparator對(duì)美食列表按推薦分排序取前10條返回。如果想在論文里體現(xiàn)“進(jìn)階”可以再寫(xiě)一段基于物品協(xié)同過(guò)濾的SQL查詢收藏了同一道美食的用戶還收藏了哪些其他美食作為補(bǔ)充推薦列表。這個(gè)擴(kuò)展邏輯簡(jiǎn)單但概念高級(jí)寫(xiě)進(jìn)論文是加分項(xiàng)。3.4 收藏、評(píng)分與評(píng)論形成交互閉環(huán)收藏、評(píng)分、評(píng)論是讓用戶“留下來(lái)”的關(guān)鍵設(shè)計(jì)也是個(gè)人中心模塊的數(shù)據(jù)來(lái)源。收藏表的核心邏輯是防重復(fù)用戶點(diǎn)擊收藏時(shí)先查詢是否已收藏已存在則取消否則新增前端用愛(ài)心圖標(biāo)的實(shí)心/空心切換狀態(tài)。評(píng)分設(shè)計(jì)成1到5星用戶提交后更新food表的rating_num和rating_sum平均分實(shí)時(shí)計(jì)算。這里有一個(gè)細(xì)節(jié)評(píng)分更新必須保證數(shù)據(jù)一致性最好放在數(shù)據(jù)庫(kù)事務(wù)里否則并發(fā)情況下評(píng)分人數(shù)和總和可能對(duì)不上。評(píng)論文表設(shè)計(jì)比較簡(jiǎn)單id、food_id、user_id、content、create_time列表按時(shí)間倒序。交互層面有一條經(jīng)驗(yàn)詳情頁(yè)把評(píng)分和評(píng)論放在同一個(gè)模塊用戶看完推薦理由順手就能打分留言不要額外跳轉(zhuǎn)頁(yè)面。每跳轉(zhuǎn)一次交互轉(zhuǎn)化就流失一大半。這個(gè)小細(xì)節(jié)我實(shí)測(cè)下來(lái)非常有用頁(yè)面停留時(shí)間和評(píng)論數(shù)量都有明顯提升。4. 實(shí)操記錄關(guān)鍵代碼與接口開(kāi)發(fā)全程解析4.1 前后端工程目錄怎么搭工程結(jié)構(gòu)直接影響論文截圖的美觀度和代碼可讀性建議一開(kāi)始就按規(guī)范搭好。后端目錄我用的標(biāo)準(zhǔn)Spring Boot分層結(jié)構(gòu)server/src/main/java/com/example/jxfood ├── controller # 接口層 │ ├── FoodController.java │ ├── UserController.java │ ├── CommentController.java │ └── FavoriteController.java ├── service # 業(yè)務(wù)邏輯層 ├── mapper # 數(shù)據(jù)庫(kù)操作層 ├── entity # 實(shí)體類 ├── common # 統(tǒng)一返回結(jié)果、異常處理 └── config # 配置類前端目錄按頁(yè)面維度組織miniprogram/ ├── pages │ ├── index # 首頁(yè)推薦 │ ├── category # 分類瀏覽 │ ├── search # 搜索 │ ├── detail # 美食詳情 │ ├── profile # 個(gè)人中心 │ └── login # 登錄 ├── components # 自定義組件 ├── utils/request.js # 請(qǐng)求封裝 └── app.js這樣劃分的好處是模塊職責(zé)單一找代碼非???。評(píng)委打開(kāi)工程目錄第一眼印象就是“規(guī)范”。4.2 小程序請(qǐng)求封裝統(tǒng)一處理token和錯(cuò)誤小程序里不能每個(gè)頁(yè)面都直接寫(xiě)wx.request那會(huì)非常冗余。我封裝了一個(gè)request工具統(tǒng)一管理baseUrl、token注入、請(qǐng)求攔截和錯(cuò)誤提示。核心實(shí)現(xiàn)如下// utils/request.js const BASE_URL http://localhost:8080/api; function request(url, method GET, data {}) { return new Promise((resolve, reject) { wx.request({ url: BASE_URL url, method: method, data: data, header: { Content-Type: application/json, token: wx.getStorageSync(token) || }, success(res) { if (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ùn)本地開(kāi)發(fā)用localhost沒(méi)問(wèn)題但真機(jī)調(diào)試時(shí)手機(jī)訪問(wèn)不到電腦的localhost必須改成電腦的局域網(wǎng)IP比如192.168.x.x。我當(dāng)時(shí)調(diào)試接口一直報(bào)“網(wǎng)絡(luò)異?!迸挪榱税胩觳虐l(fā)現(xiàn)是這個(gè)問(wèn)題。另外開(kāi)發(fā)階段一定要在微信開(kāi)發(fā)者工具里勾選“不校驗(yàn)合法域名”否則本地訪問(wèn)http接口會(huì)被攔截。4.3 后端接口實(shí)現(xiàn)示例后端以美食列表接口為例Controller層代碼量非常少參數(shù)接收、分頁(yè)、條件查詢都交給Service和MyBatis Plus處理RestController RequestMapping(/api/food) public class FoodController { Autowired private FoodService foodService; GetMapping(/list) public Result list(RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 10) Integer size, RequestParam(required false) Integer cityId, RequestParam(required false) Integer categoryId, RequestParam(required false) String keyword) { return Result.success(foodService.pageQuery(page, size, cityId, categoryId, keyword)); } }Service層用LambdaQueryWrapper做條件拼接cityId和categoryId用eq方法精確匹配keyword用like方法模糊搜索參數(shù)為空就不拼條件。這段邏輯讓列表接口同時(shí)具備了分類篩選和關(guān)鍵詞搜索能力一份代碼兩處使用非常劃算。4.4 首頁(yè)推薦接口的代碼實(shí)現(xiàn)首頁(yè)推薦的核心是推薦分的計(jì)算。我直接貼一段核心邏輯注釋已經(jīng)寫(xiě)清楚了public ListFoodVO getRecommendList(Integer userId) { User user userMapper.selectById(userId); ListFoodVO foods foodMapper.selectAllWithTagAndRating(); if (user null) { foods.sort(Comparator.comparing(FoodVO::getHeat).reversed()); return foods.subList(0, 10); } for (FoodVO food : foods) { double score 0.0; if (food.getSpicyLevel() user.getMaxSpicyLevel()) { score 50; } if (user.getPreferredTags() ! null food.getTagList() ! null) { for (String tag : user.getPreferredTags()) { if (food.getTagList().contains(tag)) { score 20; } } } score food.getHeat() * 0.1; score food.getAvgRating() * 10; food.setRecommendScore(score); } foods.sort(Comparator.comparing(FoodVO::getRecommendScore).reversed()); return foods.subList(0, 10); }這段代碼注釋好之后幾乎可以直接寫(xiě)進(jìn)論文的“核心算法實(shí)現(xiàn)”小節(jié)。每一個(gè)加分項(xiàng)對(duì)應(yīng)一段業(yè)務(wù)解釋評(píng)委看完會(huì)覺(jué)得你的推薦系統(tǒng)有理有據(jù)。5. 數(shù)據(jù)庫(kù)設(shè)計(jì)表結(jié)構(gòu)、初始數(shù)據(jù)與圖片資源處理5.1 核心表結(jié)構(gòu)參考數(shù)據(jù)庫(kù)是畢設(shè)的地基表結(jié)構(gòu)拿到手就能看出設(shè)計(jì)水平。food表我貼一下建表SQL你可以直接參考CREATE TABLE food ( id bigint(20) NOT NULL AUTO_INCREMENT, name varchar(100) NOT NULL COMMENT 美食名稱, city_id bigint(20) DEFAULT NULL COMMENT 所屬地市, category_id bigint(20) DEFAULT NULL COMMENT 分類ID, image varchar(255) DEFAULT NULL COMMENT 圖片URL, introduction text COMMENT 詳細(xì)介紹, taste_tags varchar(255) DEFAULT NULL COMMENT 口味標(biāo)簽,逗號(hào)分隔, spicy_level int(11) DEFAULT 0 COMMENT 辣度1-5, price_range varchar(50) DEFAULT NULL COMMENT 人均價(jià)格區(qū)間, recommend_reason varchar(500) DEFAULT NULL COMMENT 推薦理由, heat int(11) DEFAULT 0 COMMENT 熱度值, rating_num int(11) DEFAULT 0 COMMENT 評(píng)分人數(shù), rating_sum int(11) DEFAULT 0 COMMENT 評(píng)分總和, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;幾個(gè)設(shè)計(jì)細(xì)節(jié)值得注意。taste_tags用逗號(hào)分隔字符串查詢方便代碼里split一下就能得到標(biāo)簽數(shù)組。評(píng)分不直接存平均分而是存人數(shù)和總分兩個(gè)字段每次更新時(shí)累加查詢時(shí)相除這樣避免了反復(fù)聚合計(jì)算性能更好。每個(gè)字段的COMMENT注釋寫(xiě)清楚這點(diǎn)是論文里“數(shù)據(jù)庫(kù)設(shè)計(jì)”章節(jié)的現(xiàn)成素材。5.2 美食初始數(shù)據(jù)腳本怎么寫(xiě)初始數(shù)據(jù)是項(xiàng)目的門(mén)面我建議一次性寫(xiě)一套完整的SQL插入腳本。示例INSERT INTO food (name, city_id, category_id, image, introduction, taste_tags, spicy_level, price_range, recommend_reason, heat) VALUES (南昌拌粉, 1, 1, /images/food/nanchangbanfen.jpg, 南昌人的早餐靈魂。米粉爽滑配上蘿卜干、花生米、香菜淋上一勺秘制辣椒油拌開(kāi)就是滿嘴香氣。, 辣,咸,米粉,早餐, 3, 5-15元, 來(lái)南昌不吃拌粉等于白來(lái)便宜大碗又管飽街頭巷尾都是它的身影。, 999);這種帶溫度的數(shù)據(jù)比隨便填充的假數(shù)據(jù)強(qiáng)太多。頁(yè)面渲染出來(lái)好看答辯截圖時(shí)也是實(shí)實(shí)在在的展示內(nèi)容。我整理數(shù)據(jù)時(shí)按地市為單位批量編寫(xiě)讓同城的菜品在分類篩選下有明顯的聚集效果演示“按地市篩選”功能時(shí)特別直觀。5.3 圖片資源的選圖與版權(quán)避坑美食圖片是小程序美觀度的關(guān)鍵但版權(quán)問(wèn)題要提前規(guī)避。不建議直接爬取網(wǎng)絡(luò)圖片尤其不要用有明確水印的商家圖。推薦兩個(gè)思路一個(gè)是用免費(fèi)圖庫(kù)Unsplash、Pixabay這些站點(diǎn)有大量美食攝影圖下載后按菜品改名存入項(xiàng)目另一個(gè)是如果條件允許去本地小吃店拍幾張實(shí)拍圖更加真實(shí)。無(wú)論用哪種方式圖片都要統(tǒng)一壓縮處理目標(biāo)控制在200KB以內(nèi)。微信小程序包體積有嚴(yán)格限制圖片原圖直接塞進(jìn)去代碼包很快就會(huì)報(bào)警。壓縮可以用在線工具批量處理一分鐘搞定包體能小一半以上。6. 開(kāi)發(fā)踩坑實(shí)錄這些問(wèn)題我排查了一整晚6.1 接口請(qǐng)求失敗的排查四步法小程序真機(jī)調(diào)試時(shí)接口不通我總結(jié)出一套四步排查法每次都能解決問(wèn)題。第一步確認(rèn)后端服務(wù)正常啟動(dòng)電腦瀏覽器直接訪問(wèn)接口地址能返回JSON說(shuō)明后端沒(méi)問(wèn)題。第二步確認(rèn)小程序請(qǐng)求地址用的是局域網(wǎng)IP而不是localhost手機(jī)和電腦要連同一個(gè)WiFi。第三步確認(rèn)開(kāi)發(fā)者工具勾選了“不校驗(yàn)合法域名”。第四步看后端控制臺(tái)日志有沒(méi)有數(shù)據(jù)庫(kù)連接異常或SQL報(bào)錯(cuò)。這套排查法解決了我在開(kāi)發(fā)過(guò)程中遇到的90%的“網(wǎng)絡(luò)異?!眴?wèn)題。局域網(wǎng)IP會(huì)變建議在request.js里留一個(gè)常量配置換網(wǎng)絡(luò)環(huán)境時(shí)改一處就能重新調(diào)試非常方便。6.2 頂部導(dǎo)航欄高度和膠囊對(duì)齊問(wèn)題使用自定義導(dǎo)航欄時(shí)最煩人的問(wèn)題就是不同機(jī)型的頂部對(duì)齊。iPhone和安卓全面屏手機(jī)的狀態(tài)欄高度不一樣寫(xiě)死一個(gè)高度必然會(huì)在某些機(jī)型上錯(cuò)位。解決方法是動(dòng)態(tài)獲取系統(tǒng)信息const systemInfo wx.getSystemInfoSync(); const statusBarHeight systemInfo.statusBarHeight;拿到statusBarHeight后導(dǎo)航欄總高度用這個(gè)值加44px計(jì)算并通過(guò)CSS變量注入到頁(yè)面樣式中。這樣無(wú)論什么機(jī)型頂部按鈕都能和膠囊按鈕對(duì)齊。答辯演示時(shí)如果用全面屏手機(jī)這個(gè)細(xì)節(jié)做不好非常掉價(jià)。6.3 小程序包體積超限的處理“source size exceed max limit 2mb”是很多同學(xué)的噩夢(mèng)。處理思路分三步先在開(kāi)發(fā)者工具的“詳情-代碼依賴分析”里看資源占用大頭通常圖片排第一然后把圖片能外鏈的全部外鏈能壓縮的批量壓縮最后如果還不夠配置分包subPackages把詳情頁(yè)、個(gè)人中心這類非啟動(dòng)必需頁(yè)面放進(jìn)分包主包只保留首頁(yè)、分類、登錄等核心頁(yè)面。我實(shí)測(cè)過(guò)分包配置好之后主包體積能降到1.5MB以內(nèi)完全符合限制。而且微信小程序分包加載是官方推薦做法寫(xiě)進(jìn)論文里也是一個(gè)優(yōu)化亮點(diǎn)。6.4 中文亂碼和JSON序列化的坑后端返回中文亂碼九成是數(shù)據(jù)庫(kù)連接串沒(méi)配編碼。jdbcUrl后面必須加上useUnicodetruecharacterEncodingutf8改完重啟服務(wù)就能解決。JSON序列化出現(xiàn)奇怪字段檢查實(shí)體類有沒(méi)有手寫(xiě)getter/setter不規(guī)范建議統(tǒng)一用Lombok的Data注解省心又不會(huì)出錯(cuò)。另一個(gè)高頻問(wèn)題是LocalDateTime字段返回的是時(shí)間戳數(shù)組或奇怪格式需要在application.yml里配置Jackson日期格式或者直接在時(shí)間字段上加JsonFormat注解指定yyyy-MM-dd HH:mm:ss格式前端拿到字符串直接展示省去格式化代碼。6.5 小程序認(rèn)證和發(fā)布策略畢設(shè)階段通常不需要真的發(fā)布上線用開(kāi)發(fā)者工具的“預(yù)覽”功能生成二維碼手機(jī)掃碼就能真機(jī)運(yùn)行答辯完全夠用。如果要上線體驗(yàn)注意個(gè)人主體小程序無(wú)法開(kāi)通支付也無(wú)法獲取用戶手機(jī)號(hào)只能做內(nèi)容展示類項(xiàng)目。個(gè)人主體認(rèn)證費(fèi)用是30元一年企業(yè)主體是300元具體以微信公眾平臺(tái)的最新規(guī)則為準(zhǔn)。建議答辯前用“真機(jī)調(diào)試開(kāi)發(fā)者工具”準(zhǔn)備兩條演示路徑防止現(xiàn)場(chǎng)網(wǎng)絡(luò)出問(wèn)題導(dǎo)致演示卡殼。7. 論文與答辯讓評(píng)委打高分的實(shí)操建議7.1 論文框架怎么搭更自然論文的核心邏輯是把“從零到一實(shí)現(xiàn)這個(gè)系統(tǒng)”的過(guò)程講清楚。推薦結(jié)構(gòu)是第一章緒論交代選題背景重點(diǎn)突出“江西飲食文化數(shù)字化展示不足”這個(gè)切入點(diǎn)第二章相關(guān)技術(shù)介紹寫(xiě)微信小程序、Spring Boot、MySQL每項(xiàng)技術(shù)寫(xiě)清楚“在項(xiàng)目中用在哪兒”第三章需求分析列功能用例和角色分析第四章系統(tǒng)設(shè)計(jì)畫(huà)架構(gòu)圖、模塊圖、數(shù)據(jù)庫(kù)ER圖第五章系統(tǒng)實(shí)現(xiàn)逐模塊配截圖和代碼片段第六章系統(tǒng)測(cè)試寫(xiě)功能測(cè)試用例表和測(cè)試結(jié)果截圖。寫(xiě)論文最大的秘訣是多截圖、少空話。每個(gè)功能模塊配兩三張頁(yè)面截圖再配一段核心代碼文字圍繞“這個(gè)頁(yè)面怎么實(shí)現(xiàn)”展開(kāi)。數(shù)據(jù)庫(kù)表結(jié)構(gòu)直接把建表語(yǔ)句貼進(jìn)去比干巴巴的表格描述有力得多。7.2 答辯演示順序和常見(jiàn)提問(wèn)答辯演示的前5分鐘非常關(guān)鍵順序建議是先展示首頁(yè)推薦把推薦邏輯講清楚再搜索一個(gè)關(guān)鍵詞展示搜索功能點(diǎn)進(jìn)一個(gè)美食詳情演示收藏、評(píng)分、評(píng)論最后切到個(gè)人中心展示收藏記錄。這個(gè)流程下來(lái)系統(tǒng)亮點(diǎn)全部覆蓋時(shí)間也正好。高頻問(wèn)題我也整理了應(yīng)答思路。評(píng)委問(wèn)“數(shù)據(jù)庫(kù)為什么這么設(shè)計(jì)”你就答結(jié)合查詢場(chǎng)景避免一次聯(lián)表過(guò)多評(píng)分字段冗余存儲(chǔ)提升查詢性能。問(wèn)“推薦邏輯是機(jī)器學(xué)習(xí)嗎”答用的是標(biāo)簽加權(quán)和熱度修正的可解釋推薦策略后續(xù)可以引入?yún)f(xié)同過(guò)濾做擴(kuò)展。問(wèn)“項(xiàng)目有什么不足”虛心承認(rèn)數(shù)據(jù)量有限、推薦精度一般并說(shuō)后續(xù)可以接用戶行為日志做更精細(xì)的推薦。這幾個(gè)問(wèn)題提前背熟答辯時(shí)基本不會(huì)冷場(chǎng)。拿到這套附源碼的項(xiàng)目之后我建議你先別急著跑起來(lái)花半天時(shí)間把表結(jié)構(gòu)和接口捋一遍把推薦邏輯的權(quán)重參數(shù)改一改讓它更像“你自己的作品”。答辯答辯關(guān)鍵在“答”你能把這個(gè)系統(tǒng)的每個(gè)模塊講明白比什么都重要。如果時(shí)間來(lái)得及再把初始數(shù)據(jù)擴(kuò)充一批自己家鄉(xiāng)的特色美食項(xiàng)目特色一下就出來(lái)了。最后再分享一個(gè)我自己做這個(gè)項(xiàng)目時(shí)發(fā)現(xiàn)的細(xì)節(jié)江西11個(gè)地市每個(gè)地市的口味傾向其實(shí)不太一樣贛南偏辣偏咸南昌偏鮮香九江靠鄱陽(yáng)湖偏河鮮。把這些地方飲食特點(diǎn)寫(xiě)進(jìn)地市介紹里再關(guān)聯(lián)到對(duì)應(yīng)美食數(shù)據(jù)上整個(gè)項(xiàng)目的文化厚度立刻不一樣。這種“額外用心”不會(huì)寫(xiě)進(jìn)代碼但會(huì)讓評(píng)委和用戶都感覺(jué)到這個(gè)畢設(shè)不是交差是真的花了心思。