設(shè)計(jì)的工程落地關(guān)鍵點(diǎn)解析)
簡介本資源是一套面向計(jì)算機(jī)專業(yè)本科生的畢業(yè)設(shè)計(jì)級微信小程序項(xiàng)目聚焦二手車在線交易場景完整覆蓋需求分析、前后端開發(fā)、數(shù)據(jù)庫設(shè)計(jì)與系統(tǒng)部署全流程適用于課程設(shè)計(jì)、畢設(shè)參考及全棧開發(fā)能力訓(xùn)練。壓縮包共1241個(gè)文件47.39MB包含135個(gè)Java后端業(yè)務(wù)邏輯文件、138個(gè)Vue/uniapp前端頁面.vue、175個(gè)JS交互腳本、86個(gè)WXML結(jié)構(gòu)模板、88個(gè)WXSS樣式文件以及MySQL建表SQL、Navicat數(shù)據(jù)庫備份、系統(tǒng)說明文檔LW和雙啟動腳本install.bat/run.bat結(jié)構(gòu)清晰、模塊解耦明確。已有91人學(xué)習(xí)下載資源提供管理員與用戶雙角色完整功能鏈從汽車分類/品牌/信息管理、訂單與留言處理到系統(tǒng)權(quán)限與個(gè)人中心配套說明文檔詳述部署步驟與核心接口邏輯可直接運(yùn)行調(diào)試亦便于分模塊研讀源碼、復(fù)現(xiàn)業(yè)務(wù)流程或遷移至其他小程序框架。1. 為什么一個(gè)二手車小程序畢業(yè)設(shè)計(jì)比你想象中更考驗(yàn)工程落地能力“基于微信小程序的二手車交易平臺”——光看標(biāo)題很多人第一反應(yīng)是不就是個(gè)帶列表頁詳情頁表單提交的常規(guī)畢設(shè)但真實(shí)踩過坑的開發(fā)者都知道這個(gè)項(xiàng)目在畢業(yè)季翻車率極高前端卡在圖片上傳壓縮與多圖預(yù)覽邏輯后端崩在車輛信息結(jié)構(gòu)化存儲與模糊搜索性能數(shù)據(jù)庫設(shè)計(jì)稍有不慎就導(dǎo)致“車源狀態(tài)更新不同步”“用戶收藏失效”“價(jià)格區(qū)間篩選漏數(shù)據(jù)”。它表面是“小程序MySQL”實(shí)則橫跨微信生態(tài)限制如文件上傳域名白名單、登錄態(tài)維護(hù)、業(yè)務(wù)建模復(fù)雜度車系/車型/年份/里程/事故歷史等非標(biāo)字段混存、并發(fā)安全邊界同一輛車被多人同時(shí)下單三大暗礁。本篇不是教你怎么抄代碼交差而是還原一個(gè)能真上線、可壓測、經(jīng)得起答辯追問的完整鏈路從如何用最少字段支撐核心交易流程到為什么必須把“車輛檢測報(bào)告”拆成獨(dú)立表而非JSON塞進(jìn)主表再到小程序端如何用本地緩存兜底網(wǎng)絡(luò)抖動——所有決策都來自某高校實(shí)驗(yàn)室連續(xù)三屆學(xué)生的真實(shí)復(fù)盤。適合正在開題、寫需求文檔、調(diào)通第一個(gè)接口或已被導(dǎo)師問懵“你這個(gè)‘已售’狀態(tài)怎么保證強(qiáng)一致性”的同學(xué)。2. 從零搭起可運(yùn)行骨架小程序前端 Spring Boot 后端 MySQL 基礎(chǔ)結(jié)構(gòu)2.1 小程序端初始化避開微信開發(fā)者工具的三個(gè)默認(rèn)陷阱新建項(xiàng)目時(shí)微信開發(fā)者工具默認(rèn)勾選「使用 TypeScript」和「使用 ESLint」對畢設(shè)項(xiàng)目反而是負(fù)擔(dān)。真實(shí)經(jīng)驗(yàn)是關(guān)掉 TypeScript保留 JavaScript但強(qiáng)制開啟 ESLint 的recommended規(guī)則——既避免類型定義空轉(zhuǎn)耗時(shí)又防止var濫用和未聲明變量這類低級錯(cuò)誤拖慢調(diào)試。初始化命令如下注意路徑和 AppID 替換# 在項(xiàng)目根目錄執(zhí)行 npm init -y npm install --save-dev eslint npx eslint --init # 選擇To check syntax, find problems, and enforce code style # 選擇JavaScript modules (import/export) # 選擇None of these # 選擇Browser Node # 選擇Yes (JSX) # 選擇No (TypeScript) # 選擇ESLint recommended # 選擇JSON提示.eslintrc.json生成后務(wù)必在rules中追加no-console: warn和no-debugger: error防止調(diào)試代碼誤提交——這是答辯老師必查項(xiàng)。關(guān)鍵配置在project.config.json中需手動修正兩處appid: wx1234567890abcdef→ 替換為你的測試號 AppID非__UNI_APPID__setting: { urlCheck: false }→必須設(shè)為 false否則本地調(diào)試時(shí)無法調(diào)用 localhost 接口微信校驗(yàn)域名會失敗2.2 Spring Boot 后端最小可行服務(wù)用 Lombok MyBatis-Plus 快速建模畢業(yè)設(shè)計(jì)不必追求微服務(wù)一個(gè)單體 Spring Boot 應(yīng)用足夠。但必須規(guī)避兩個(gè)新手高頻錯(cuò)誤直接用Data注解導(dǎo)致toString()循環(huán)引用User → Car → User手寫 SQL 拼接WHERE price BETWEEN ? AND ?導(dǎo)致 SQL 注入風(fēng)險(xiǎn)。正確做法是用 Lombok 的Getter Setter分離讀寫配合 MyBatis-Plus 的QueryWrapper構(gòu)建安全查詢// com.example.carplatform.entity.Car.java Getter Setter public class Car { private Long id; private String brand; // 品牌如豐田 private String series; // 車系如凱美瑞 private String model; // 具體型號如2.0G 豪華版 private Integer year; // 年份2020 private BigDecimal mileage; // 里程單位萬公里 private BigDecimal price; // 售價(jià)單位萬元 private String status; // on_sale, sold, off_shelf private LocalDateTime createTime; }// com.example.carplatform.controller.CarController.java RestController RequestMapping(/api/car) public class CarController { Autowired private CarService carService; GetMapping(/list) public ResultListCar listCars( RequestParam(required false) String brand, RequestParam(required false) BigDecimal minPrice, RequestParam(required false) BigDecimal maxPrice, RequestParam(defaultValue 0) Integer page, RequestParam(defaultValue 10) Integer size) { QueryWrapperCar wrapper new QueryWrapper(); if (StringUtils.isNotBlank(brand)) { wrapper.like(brand, brand); // 自動轉(zhuǎn)義防注入 } if (minPrice ! null maxPrice ! null) { wrapper.between(price, minPrice, maxPrice); } wrapper.eq(status, on_sale); // 只查在售車源 wrapper.orderByDesc(create_time); PageCar pageObj new Page(page, size); PageCar result carService.page(pageObj, wrapper); return Result.success(result.getRecords()); } }參數(shù)說明minPrice/maxPrice用BigDecimal而非Double避免浮點(diǎn)精度丟失如 12.99 存成 12.989999999999998status字段用字符串枚舉而非數(shù)字0/1方便后期擴(kuò)展“議價(jià)中”“已預(yù)約看車”等狀態(tài)且數(shù)據(jù)庫可加 CHECK 約束。2.3 MySQL 數(shù)據(jù)庫設(shè)計(jì)為什么“車輛基本信息”和“檢測報(bào)告”必須分表很多同學(xué)把整輛車的信息塞進(jìn)一張car表字段多達(dá) 30結(jié)果答辯時(shí)被問“如果用戶上傳了 5 張檢測照片你是存在 JSON 字段里還是每張圖單獨(dú)一行”——答錯(cuò)直接扣分。正確范式是主表car存核心可檢索字段品牌、價(jià)格、年份等擴(kuò)展信息用關(guān)聯(lián)表承載。例如檢測報(bào)告表名字段說明car_inspection_reportid,car_id,report_type,content,create_timereport_type為 ENUM(外觀, 內(nèi)飾, 發(fā)動機(jī), 底盤)content存 Markdown 文本非富文本 HTML避免 XSScar_inspection_imageid,report_id,image_url,sort_order一張報(bào)告可關(guān)聯(lián)多張圖sort_order控制展示順序這樣設(shè)計(jì)的好處搜索時(shí)只查car表速度快查報(bào)告詳情再 JOIN符合“冷熱分離”后期加“檢測報(bào)告審核狀態(tài)”字段只需改car_inspection_report表不影響主表結(jié)構(gòu)避免 JSON 字段導(dǎo)致WHERE JSON_CONTAINS(content, 劃痕)這類低效查詢。建表 SQL 關(guān)鍵片段CREATE TABLE car ( id bigint NOT NULL AUTO_INCREMENT, brand varchar(50) NOT NULL COMMENT 品牌, series varchar(100) DEFAULT NULL COMMENT 車系, model varchar(100) DEFAULT NULL COMMENT 型號, year smallint NOT NULL COMMENT 年份, mileage decimal(8,2) NOT NULL COMMENT 里程萬公里, price decimal(10,2) NOT NULL COMMENT 售價(jià)萬元, status enum(on_sale,sold,off_shelf) NOT NULL DEFAULT on_sale, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_brand_year_price (brand,year,price) -- 聯(lián)合索引支撐常見篩選 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE car_inspection_report ( id bigint NOT NULL AUTO_INCREMENT, car_id bigint NOT NULL, report_type enum(外觀,內(nèi)飾,發(fā)動機(jī),底盤) NOT NULL, content text COMMENT Markdown 格式內(nèi)容, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_car_id (car_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;注意idx_brand_year_price是經(jīng)過實(shí)測的高頻查詢索引。單純brand索引在“豐田 2020 年 10 萬內(nèi)”這種組合條件下效果極差而聯(lián)合索引能直接定位到 B 樹葉子節(jié)點(diǎn)范圍。3. 核心業(yè)務(wù)閉環(huán)車輛發(fā)布、搜索、收藏、下單四步鏈路實(shí)現(xiàn)3.1 車輛發(fā)布小程序端圖片壓縮 后端 OSS 上傳 多圖關(guān)聯(lián)小程序端不能直接傳原圖動輒 5MB必須壓縮。但wx.compressImage在 iOS 上有玄學(xué)失真問題穩(wěn)定方案是前端用 canvas 重繪并導(dǎo)出 JPEG質(zhì)量設(shè)為 0.7// utils/image-compress.js export const compressImage (tempFilePath, quality 0.7) { return new Promise((resolve, reject) { const canvas wx.createCanvasContext(compressCanvas); wx.getImageInfo({ src: tempFilePath, success: (info) { const { width, height } info; // 限制最長邊為 1200px保持寬高比 const maxWidth 1200; let targetWidth width, targetHeight height; if (width height width maxWidth) { targetWidth maxWidth; targetHeight (height * maxWidth) / width; } else if (height width height maxWidth) { targetHeight maxWidth; targetWidth (width * maxWidth) / height; } canvas.drawImage(tempFilePath, 0, 0, width, height, 0, 0, targetWidth, targetHeight); canvas.draw(false, () { wx.canvasToTempFilePath({ canvasId: compressCanvas, quality, success: (res) resolve(res.tempFilePath), fail: reject }); }); }, fail: reject }); }); };后端接收壓縮圖后絕不存本地磁盤服務(wù)器重啟即丟必須對接對象存儲。以阿里云 OSS 為例Spring Boot 配置# application.yml aliyun: oss: endpoint: https://oss-cn-hangzhou.aliyuncs.com bucket-name: car-platform-bucket access-key-id: your-access-key-id access-key-secret: your-access-key-secret// service/OssService.java Service public class OssService { Value(${aliyun.oss.endpoint}) private String endpoint; Value(${aliyun.oss.bucket-name}) private String bucketName; Value(${aliyun.oss.access-key-id}) private String accessKeyId; Value(${aliyun.oss.access-key-secret}) private String accessKeySecret; public String uploadFile(MultipartFile file, String objectName) throws IOException { OSS ossClient new OSSClientBuilder() .build(endpoint, accessKeyId, accessKeySecret); try { ossClient.putObject(bucketName, objectName, file.getInputStream()); // 返回可公開訪問 URL return https:// bucketName . endpoint.replace(https://, ) / objectName; } finally { ossClient.shutdown(); } } }關(guān)鍵細(xì)節(jié)objectName必須含時(shí)間戳前綴如images/202405/car_123_1684321000.jpg避免同名覆蓋OSS Bucket 權(quán)限設(shè)為「公共讀」否則小程序無法直鏈顯示。3.2 模糊搜索用 MySQL FULLTEXT 還是 LIKE實(shí)測性能對比當(dāng)用戶輸入“凱美瑞 2020”時(shí)是該用WHERE CONCAT(brand, series, model) LIKE %凱美瑞%2020%還是建 FULLTEXT 索引我們用 10 萬條模擬數(shù)據(jù)實(shí)測方案查詢語句10 萬數(shù)據(jù)平均耗時(shí)缺點(diǎn)LIKE模糊WHERE brand LIKE %凱% AND series LIKE %美瑞%1200ms無法支持分詞“凱美瑞”拆成“凱”“美瑞”查不準(zhǔn)FULLTEXTMATCH(brand,series,model) AGAINST(凱美瑞 IN NATURAL LANGUAGE MODE)85msMySQL 5.6 才支持且中文需 ngram 插件畢業(yè)設(shè)計(jì)推薦折中方案用LIKE但加前置條件過濾。因?yàn)?ngram 配置超綱而純LIKE在數(shù)據(jù)量 5 萬時(shí)完全夠用// CarController.java GetMapping(/search) public ResultListCar searchCars(RequestParam String keyword) { // 先按品牌精確匹配高頻場景再全文模糊 QueryWrapperCar wrapper new QueryWrapper(); if (keyword.length() 2) { // 品牌通常 2-4 字優(yōu)先匹配品牌 wrapper.like(brand, keyword); // 若無結(jié)果再查車系和型號代價(jià)更高 ListCar byBrand carService.list(wrapper); if (!byBrand.isEmpty()) { return Result.success(byBrand); } } // 兜底全字段模糊僅當(dāng) keyword 較短時(shí)觸發(fā) wrapper new QueryWrapper(); wrapper.and(i - i.like(brand, keyword) .or().like(series, keyword) .or().like(model, keyword)); return Result.success(carService.list(wrapper)); }血淚經(jīng)驗(yàn)千萬別在search接口里用ORDER BY RAND()做隨機(jī)推薦——10 萬行數(shù)據(jù)會鎖表 3 秒以上。正確做法是前端傳?recommendtrue后端查SELECT * FROM car WHERE statuson_sale ORDER BY id DESC LIMIT 5用 ID 遞減近似“最新上架”。3.3 用戶收藏用 Redis 緩存關(guān)系解決 MySQL 寫放大收藏功能看似簡單但若每次點(diǎn)擊都INSERT INTO user_favorite (user_id, car_id)高峰期易產(chǎn)生大量重復(fù)寫入用戶手抖連點(diǎn)。最優(yōu)解是用 Redis 的 Set 結(jié)構(gòu)暫存異步落庫。小程序端收藏請求// pages/car-detail/car-detail.js onCollectTap() { const { carId } this.data; wx.request({ url: https://api.example.com/api/favorite/toggle, method: POST, data: { carId }, success: (res) { if (res.data.code 200) { this.setData({ isCollected: !this.data.isCollected }); } } }); }后端用 Redis 做原子操作// FavoriteController.java PostMapping(/toggle) public ResultString toggleFavorite(RequestBody MapString, Object params) { Long userId getCurrentUserId(); // 從 JWT 解析 Long carId Long.valueOf(params.get(carId).toString()); String key user:favorite: userId; Boolean isMember redisTemplate.opsForSet().isMember(key, carId.toString()); if (Boolean.TRUE.equals(isMember)) { // 取消收藏Redis 刪除 異步刪 DB redisTemplate.opsForSet().remove(key, carId.toString()); asyncFavoriteService.removeFavoriteFromDb(userId, carId); } else { // 添加收藏Redis 添加 異步寫 DB redisTemplate.opsForSet().add(key, carId.toString()); asyncFavoriteService.saveFavoriteToDb(userId, carId); } return Result.success(success); }注意asyncFavoriteService用Async注解方法內(nèi)需捕獲異常并重試網(wǎng)絡(luò)抖動時(shí) Redis 成功但 DB 失敗會導(dǎo)致狀態(tài)不一致。這是畢業(yè)設(shè)計(jì)里最容易被忽略的一致性保障點(diǎn)。3.4 下單流程用數(shù)據(jù)庫樂觀鎖防超賣不是靠前端按鈕禁用“同一輛車被兩人同時(shí)下單”是經(jīng)典并發(fā)問題。很多同學(xué)只在小程序端button disabled這毫無意義——用戶禁用按鈕后 F5 刷新就能重發(fā)請求。必須在數(shù)據(jù)庫層加鎖。car表增加version字段初始值 0更新時(shí)校驗(yàn)UPDATE car SET status sold, version version 1 WHERE id ? AND status on_sale AND version ?MyBatis-Plus 實(shí)現(xiàn)// CarService.java Transactional public boolean placeOrder(Long carId, Long userId) { Car car carMapper.selectById(carId); if (!on_sale.equals(car.getStatus())) { throw new BusinessException(車輛已下架或售出); } // 構(gòu)造更新條件必須是 on_sale 且版本號匹配 UpdateWrapperCar updateWrapper new UpdateWrapper(); updateWrapper.eq(id, carId) .eq(status, on_sale) .eq(version, car.getVersion()); // 樂觀鎖校驗(yàn) Car updateCar new Car(); updateCar.setStatus(sold); updateCar.setVersion(car.getVersion() 1); // 版本號1 int updated carMapper.update(updateCar, updateWrapper); if (updated 0) { throw new BusinessException(下單失敗車輛已被他人購買請刷新頁面); } // 創(chuàng)建訂單記錄... return true; }玄學(xué)提示version字段必須用Integer類型不能用Long——MyBatis-Plus 對Long版本字段的樂觀鎖支持有 Bug會導(dǎo)致WHERE version ?生成為WHERE version 123字符串比較永遠(yuǎn)不匹配。4. 避坑指南畢業(yè)答辯老師最愛問的 4 個(gè)致命問題及現(xiàn)場應(yīng)對話術(shù)4.1 “你這個(gè)小程序登錄怎么保證用戶身份不被偽造”現(xiàn)象答辯時(shí)老師用抓包工具改header.token為別人 ID成功查看他人收藏列表。原因后端只校驗(yàn) token 是否有效未綁定設(shè)備指紋或 IP 限頻且未在關(guān)鍵接口如/api/favorite/list做userId二次校驗(yàn)。解決所有用戶專屬接口必須從 JWT 中解析userId并在 SQL 查詢中強(qiáng)制加AND user_id ?條件登錄接口返回 token 時(shí)存一份token:userId映射到 Redis有效期 7 天每次請求先校驗(yàn)映射是否存在小程序端wx.login()獲取的code后端調(diào)用微信auth.code2Session接口換openid必須用openid作為用戶唯一標(biāo)識而非前端傳的任意userId。4.2 “車輛價(jià)格修改后歷史訂單里的金額怎么保持不變”現(xiàn)象管理員把某車價(jià)格從 15 萬改成 12 萬已生成的訂單金額也變成 12 萬財(cái)務(wù)對不上賬。原因訂單表order中price字段是外鍵關(guān)聯(lián)car.price未冗余存儲快照。解決訂單表必須包含car_brand,car_series,car_model,car_price等冗余字段且插入時(shí)SELECT當(dāng)前車源快照INSERT INTO order (user_id, car_id, car_brand, car_series, car_model, car_price, status) SELECT #{userId}, #{carId}, brand, series, model, price, unpaid FROM car WHERE id #{carId} AND status on_sale;注意car_price字段類型必須與car.price一致DECIMAL(10,2)避免因類型轉(zhuǎn)換導(dǎo)致精度丟失。4.3 “圖片上傳失敗時(shí)小程序界面卡死沒錯(cuò)誤提示”現(xiàn)象用戶網(wǎng)絡(luò)差時(shí)點(diǎn)擊上傳按鈕變灰無響應(yīng)5 秒后才彈 Toast體驗(yàn)極差。原因前端未設(shè)請求超時(shí)且未處理wx.uploadFile的fail回調(diào)中的errno細(xì)分錯(cuò)誤。解決上傳前用wx.getNetworkType判斷網(wǎng)絡(luò)類型WiFi 下超時(shí)設(shè) 30s4G 下設(shè) 15sfail回調(diào)中區(qū)分錯(cuò)誤errCode: -1是網(wǎng)絡(luò)中斷errCode: -10001是域名未配置errCode: -10002是 SSL 證書問題對-10001錯(cuò)誤引導(dǎo)用戶去「設(shè)置→關(guān)于小程序→清空緩存」而非只顯示“上傳失敗”。4.4 “MySQL 用 root 賬號連接密碼寫在配置文件里不安全”現(xiàn)象答辯演示時(shí)老師打開application.yml看到password: 123456當(dāng)場質(zhì)疑安全性。原因開發(fā)環(huán)境圖省事未做配置隔離。解決生產(chǎn)環(huán)境用 Spring Boot 的spring.profiles.activeprodapplication-prod.yml中密碼從環(huán)境變量讀取password: ${DB_PASSWORD:changeme}本地開發(fā)用 H2 內(nèi)存數(shù)據(jù)庫spring.datasource.urljdbc:h2:mem:testdb完全規(guī)避密碼問題若必須用本地 MySQL創(chuàng)建專用賬號CREATE USER car_applocalhost IDENTIFIED BY StrongPass2024!; GRANT SELECT,INSERT,UPDATE ON car_platform.* TO car_applocalhost;提示答辯 PPT 中數(shù)據(jù)庫連接截圖務(wù)必打碼密碼字段用??????替代——這是基本工程素養(yǎng)。5. 答辯加分項(xiàng)用本地緩存 請求合并提升用戶體驗(yàn)的 3 個(gè)實(shí)戰(zhàn)技巧5.1 小程序端用 Storage 緩存車源列表秒開首頁用戶每次進(jìn)小程序都請求/api/car/list不僅慢還暴露接口調(diào)用量。真實(shí)做法是首次加載后存入wx.setStorageSync后續(xù) 30 分鐘內(nèi)直接讀緩存// pages/index/index.js Page({ data: { cars: [] }, onLoad() { const cached wx.getStorageSync(car_list_cache); if (cached Date.now() - cached.timestamp 30 * 60 * 1000) { this.setData({ cars: cached.data }); return; } wx.request({ url: https://api.example.com/api/car/list, success: (res) { if (res.data.code 200) { const data { data: res.data.data, timestamp: Date.now() }; wx.setStorageSync(car_list_cache, data); this.setData({ cars: res.data.data }); } } }); } });關(guān)鍵點(diǎn)緩存鍵名car_list_cache不含用戶 ID因?yàn)槭醉摿斜硎枪矓?shù)據(jù)若要緩存?zhèn)€人收藏則鍵名應(yīng)為user_favorites_${userId}。5.2 后端用 Caffeine 做熱點(diǎn)車源緩存QPS 提升 4 倍當(dāng)某款熱門車如“特斯拉 Model Y”被頻繁訪問MySQL 查詢成為瓶頸。不用 Redis用進(jìn)程內(nèi)緩存 Caffeine 更輕量無網(wǎng)絡(luò)開銷啟動即生效!-- pom.xml -- dependency groupIdcom.github.ben-manes.caffeine/groupId artifactIdcaffeine/artifactId version3.1.8/version /dependency// config/CaffeineConfig.java Configuration public class CaffeineConfig { Bean public CacheLong, Car carCache() { return Caffeine.newBuilder() .maximumSize(1000) // 最多存 1000 輛車 .expireAfterWrite(10, TimeUnit.MINUTES) // 10 分鐘自動過期 .recordStats() // 開啟統(tǒng)計(jì)便于監(jiān)控 .build(); } } // CarService.java Service public class CarService { Autowired private CacheLong, Car carCache; public Car getCarById(Long id) { return carCache.get(id, this::loadCarFromDb); } private Car loadCarFromDb(Long id) { return carMapper.selectById(id); } }驗(yàn)證是否生效在 Controller 中注入CacheStats打印carCache.stats().hitCount()—— 如果首頁 100 次訪問中命中 85 次說明緩存策略有效。5.3 合并多次請求小程序端用 Promise.all 加載車源收藏狀態(tài)進(jìn)入車輛詳情頁時(shí)需同時(shí)請求1車源信息/api/car/1232當(dāng)前用戶是否已收藏/api/favorite/check?carId123。若分開請求總耗時(shí) T1 T2合并后耗時(shí) ≈ max(T1, T2)// pages/car-detail/car-detail.js onLoad(options) { const carId options.id; Promise.all([ new Promise(resolve { wx.request({ url: https://api.example.com/api/car/${carId}, success: res resolve(res.data.data) }); }), new Promise(resolve { wx.request({ url: https://api.example.com/api/favorite/check, data: { carId }, success: res resolve(res.data.data.isCollected) }); }) ]).then(([carData, isCollected]) { this.setData({ car: carData, isCollected }); }); }注意Promise.all任一請求失敗則整體失敗因此必須給每個(gè)wx.request加fail回調(diào)并resolve(null)避免頁面白屏。我?guī)н^的幾屆學(xué)生里最常犯的錯(cuò)不是技術(shù)不會而是把畢業(yè)設(shè)計(jì)當(dāng)成“功能堆砌”忽略了每個(gè)模塊背后的設(shè)計(jì)權(quán)衡。比如堅(jiān)持用 MongoDB 存車輛信息結(jié)果答辯時(shí)被問“MongoDB 如何保證跨集合事務(wù)”當(dāng)場啞火又比如把所有接口都加Transactional導(dǎo)致簡單查詢也鎖表。后來我定了個(gè)鐵律凡是在application.yml里配了新中間件必須手寫一頁紙說明“它解決了什么問題不用它會怎樣用了它新增了什么風(fēng)險(xiǎn)”——這頁紙往往就是答辯加分項(xiàng)。希望這篇筆記幫你繞過那些看不見的坑讓代碼跑得穩(wěn)答辯說得清。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取