管理微信小程序畢設(shè)源碼:從數(shù)據(jù)庫到支付回調(diào)的完整閉環(huán))
簡介校園水電費(fèi)管理微信小程序畢業(yè)設(shè)計(jì)源碼基于Java技術(shù)棧開發(fā)適合計(jì)算機(jī)、電子信息工程、數(shù)學(xué)等專業(yè)正在準(zhǔn)備畢業(yè)設(shè)計(jì)或課程作業(yè)的學(xué)生。資源包共包含1072個(gè)文件既有Java后端服務(wù)、Vue管理端頁面、微信小程序wxml/wxss頁面、JavaScript交互邏輯也有JSON/XML配置文件、SQL數(shù)據(jù)庫腳本以及png、svg、jpg等前端圖標(biāo)與素材壓縮包大小16.56MB整體結(jié)構(gòu)緊湊便于分類學(xué)習(xí)。項(xiàng)目代碼經(jīng)導(dǎo)師指導(dǎo)并調(diào)試通過自帶安裝運(yùn)行批處理與工程配置可快速啟動(dòng)項(xiàng)目便于理解水電費(fèi)賬單管理、用戶繳費(fèi)、用能統(tǒng)計(jì)等核心模塊的實(shí)現(xiàn)思路也能覆蓋課程設(shè)計(jì)和期末大作業(yè)的實(shí)戰(zhàn)場景。目前已有230人學(xué)習(xí)下載對(duì)于需要參考高分畢設(shè)項(xiàng)目、二次開發(fā)或項(xiàng)目實(shí)戰(zhàn)練手的學(xué)生都是一份實(shí)用價(jià)值較高的完整源碼包可輔助快速完成畢業(yè)設(shè)計(jì)。1. 校園水電費(fèi)管理微信小程序源碼為什么我建議你把它當(dāng)作第一個(gè)「完整閉環(huán)」畢設(shè)項(xiàng)目每年畢設(shè)季總有人拿著「校園水電費(fèi)管理微信小程序源碼」這個(gè)標(biāo)題來找我問的第一句通常是「這東西到底能不能跑、老師會(huì)不會(huì)一眼看出是網(wǎng)上扒的」。我的回答一般是這個(gè)題目比大多數(shù)圖書管理系統(tǒng)、商城系統(tǒng)都更能體現(xiàn)一個(gè)完整業(yè)務(wù)閉環(huán)——它同時(shí)涉及微信登錄、狀態(tài)機(jī)賬單從待繳到已繳、費(fèi)用計(jì)算規(guī)則、角色權(quán)限學(xué)生和管理員、甚至支付回調(diào)。我做過的模擬項(xiàng)目X功能就是抄表、計(jì)費(fèi)、催繳、繳費(fèi)、臺(tái)賬查詢五件事前后端加起來三千多行代碼卻把畢設(shè)答辯里「你講講這個(gè)復(fù)雜業(yè)務(wù)怎么設(shè)計(jì)的」這類問題全堵住了。如果你正在找能落地、邏輯不單薄、工作量看得見的項(xiàng)目這確實(shí)是個(gè)值得投入的方向。2. 把這個(gè)源碼跑起來之前技術(shù)選型和項(xiàng)目骨架2.1 為什么小程序端用原生、后端用 Spring Boot 是校園場景最不容易翻車的組合常見做法是前端用微信小程序原生框架后端用 Spring Boot 加 MySQL。有人會(huì)覺得原生小程序?qū)懫饋聿蝗?uni-app 順手但校園水電費(fèi)這種場景頁面主要是列表、表單、賬單詳情和圖表原生小程序完全夠用而且少了 H5 轉(zhuǎn)小程序那一層兼容問題。后端選 Spring Boot 的原因也很直接你隨便找一個(gè)室友給他看application.yml里的數(shù)據(jù)庫賬號(hào)和端口配置他都能幫你把環(huán)境搭起來而如果選了 Node.js 或 Go很多同學(xué)對(duì)依賴管理不熟春節(jié)期間環(huán)境裝不上就卡住了。我一般會(huì)建議后端用 MVC 分層controller 只做參數(shù)接收和返回service 寫計(jì)費(fèi)邏輯mapper 操作數(shù)據(jù)庫。這樣做的好處是如果你從網(wǎng)上找到的這份校園水電費(fèi)管理微信小程序源碼里 service 層寫得特別薄、所有 SQL 都堆在 controller 里你一眼就能看出來這項(xiàng)目質(zhì)量不高不值得當(dāng)?shù)鬃痈摹?.2 拿到一份源碼后先看哪幾個(gè)目錄和文件我拿到一份校園水電費(fèi)管理微信小程序源碼第一件事不是急著放進(jìn)微信開發(fā)者工具而是先掃目錄結(jié)構(gòu)。正常情況下應(yīng)該是這樣wechat-mini-program/ ├── pages/ │ ├── login/ # 登錄頁 │ ├── index/ # 首頁我的房間、本月用量 │ ├── bill/ # 賬單列表與詳情 │ ├── payment/ # 收銀臺(tái) │ └── admin/ # 管理員抄表錄入、價(jià)格設(shè)置 ├── utils/ │ ├── request.js # 封裝 wx.request統(tǒng)一帶 token │ └── format.js # 金額、日期格式化 ├── app.js ├── app.json └── project.config.json同時(shí)后端源碼應(yīng)該是這種姿勢backend/ ├── src/main/java/com/xxx/ │ ├── controller/ │ ├── service/ │ ├── mapper/ │ ├── entity/ │ └── config/ ├── src/main/resources/ │ ├── mapper/*.xml # SQL │ └── application.yml └── pom.xml先看application.yml里的數(shù)據(jù)庫連接是不是寫死的、有沒有用jdbc:mysql://localhost:3306/dormitory這種可改的寫法——去平臺(tái)化的穩(wěn)定配置是你能不能本地跑起來的前提。再看app.js里的globalData.apiBaseUrl小程序端所有請(qǐng)求都走這個(gè)公共地址本地調(diào)試時(shí)改成http://localhost:8080。如果一份源碼這兩個(gè)文件都寫死了某個(gè)公網(wǎng)地址我勸你立刻關(guān)掉因?yàn)槟悴恢浪B的是誰的數(shù)據(jù)庫。2.3 最小啟動(dòng)步驟兩個(gè)小時(shí)內(nèi)在本地把它跑通我按自己跑通某水電費(fèi)項(xiàng)目源碼的順序給你一套可復(fù)現(xiàn)的步驟# 1. 創(chuàng)建數(shù)據(jù)庫并導(dǎo)入 mysql -u root -p -e CREATE DATABASE dormitory DEFAULT CHARSET utf8mb4; mysql -u root -p dormitory sql/init.sql # 2. 修改后端配置 cd backend vim src/main/resources/application.yml # 改 spring.datasource.username / password / url # 3. 啟動(dòng)后端 mvn spring-boot:run # 4. 確認(rèn)后端起來 curl http://localhost:8080/api/health關(guān)鍵參數(shù)就三個(gè)數(shù)據(jù)庫名、端口默認(rèn) 8080微信開發(fā)者工具不校驗(yàn)這個(gè)、以及小程序project.config.json里的appid測試號(hào)可以填touristappid。跑起來后用微信開發(fā)者工具打開小程序根目錄在utils/request.js里把 baseURL 指到http://localhost:8080然后在模擬器里點(diǎn)登錄——能拿到 openid 且首頁不白屏說明這套源碼的基礎(chǔ)鏈路是通的。# 如果登錄報(bào)錯(cuò)先看后端日志有沒有這句話 # code2session err → 說明 appid 或 secret 不對(duì)注意真機(jī)預(yù)覽時(shí)localhost指向的是你手機(jī)自己不是電腦。要做真機(jī)聯(lián)調(diào)把 baseURL 改成電腦的局域網(wǎng) IP并且微信公眾平臺(tái)里要把該 IP 加入 request 合法域名調(diào)試階段可勾選「不校驗(yàn)合法域名」。3. 別急著寫界面計(jì)費(fèi)模型和數(shù)據(jù)庫表設(shè)計(jì)決定你答辯時(shí)有沒有底氣3.1 三張核心表房間表、抄表記錄表、賬單表我在做某跨平臺(tái)系統(tǒng)的水電費(fèi)模塊時(shí)第一版表結(jié)構(gòu)設(shè)計(jì)很簡單——一張user表、一張record表結(jié)果項(xiàng)目走到一半就卡住了損耗分?jǐn)倹]法算、階梯計(jì)費(fèi)對(duì)不上、同一個(gè)房間一個(gè)月產(chǎn)生了兩條賬單。后來重構(gòu)成三張表加一張日志表才順過來。CREATE TABLE room ( id int NOT NULL AUTO_INCREMENT, building varchar(32) NOT NULL COMMENT 樓棟號(hào), room_number varchar(32) NOT NULL COMMENT 房間號(hào), capacity int DEFAULT 4 COMMENT 標(biāo)準(zhǔn)入住人數(shù), area decimal(6,2) DEFAULT 0 COMMENT 面積用于分?jǐn)? PRIMARY KEY (id), UNIQUE KEY uk_building_room (building,room_number) ) ENGINEInnoDB COMMENT房間表; CREATE TABLE meter_record ( id bigint NOT NULL AUTO_INCREMENT, room_id int NOT NULL, period char(7) NOT NULL COMMENT 賬期如 2025-06, type tinyint NOT NULL COMMENT 1電 2水, last_value decimal(10,2) NOT NULL COMMENT 上次表底, current_value decimal(10,2) NOT NULL COMMENT 本次表底, read_time datetime NOT NULL, operator_id bigint DEFAULT NULL COMMENT 抄表管理員, PRIMARY KEY (id), UNIQUE KEY uk_room_period_type (room_id,period,type) ) ENGINEInnoDB COMMENT抄表記錄表; CREATE TABLE bill ( id bigint NOT NULL AUTO_INCREMENT, room_id int NOT NULL, period char(7) NOT NULL, bill_no varchar(40) NOT NULL, total_amount int NOT NULL COMMENT 總金額單位分, status tinyint NOT NULL DEFAULT 0 COMMENT 0待繳 1已繳 2作廢, created_at datetime DEFAULT CURRENT_TIMESTAMP, paid_at datetime DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY uk_bill_no (bill_no), KEY idx_room_period (room_id,period) ) ENGINEInnoDB COMMENT賬單表;這里最值得你注意的就是UNIQUE KEY uk_room_period_type。沒有這個(gè)唯一約束兩個(gè)管理員同一天對(duì)同一房間重復(fù)錄入抄表數(shù)系統(tǒng)就會(huì)生成兩張賬單學(xué)生對(duì)賬時(shí)必然翻車。我在模擬項(xiàng)目X里就被這個(gè)坑絆過一次后來加了唯一索引重復(fù)錄入直接報(bào)錯(cuò)才把數(shù)據(jù)可靠性救回來。3.2 階梯計(jì)費(fèi)和損耗分?jǐn)們蓚€(gè)必調(diào)的參數(shù)水電費(fèi)看著簡單真去算時(shí)發(fā)現(xiàn)全是細(xì)節(jié)。以電費(fèi)為例校園宿舍常見的規(guī)則是每月每房間基礎(chǔ)額度 20 度超過部分按階梯計(jì)價(jià)。我把計(jì)費(fèi)邏輯寫成一個(gè)獨(dú)立 service 方法public int calcElectricFee(BigDecimal usedAmount, FeeRate rate) { // 單位分 int total 0; BigDecimal remain usedAmount; for (LadderItem item : rate.getLadderList()) { if (remain.compareTo(item.getThreshold()) 0) { total item.getThreshold().multiply(item.getPrice()) .setScale(0, RoundingMode.HALF_UP).intValue(); remain remain.subtract(item.getThreshold()); } else { total remain.multiply(item.getPrice()) .setScale(0, RoundingMode.HALF_UP).intValue(); break; } } return total; }計(jì)費(fèi)參數(shù)我建議做成數(shù)據(jù)庫表而不是寫死在代碼里字段包括ladder_type電/水、threshold檔位閾值、price單價(jià)、effective_date生效日期。理由很實(shí)際你答辯時(shí)老師大概率會(huì)問「如果明年電費(fèi)調(diào)整了怎么辦」你直接演示改數(shù)據(jù)庫不改代碼比解釋「我硬編碼了」有說服力得多。損耗分?jǐn)偸橇硪粋€(gè)容易做錯(cuò)的地方。我的方案是公攤電 總表數(shù) - 各分表數(shù)之和然后按每個(gè)房間用電量占比分?jǐn)?。注意這里要用「用電量占比」而不是「按人頭均攤」因?yàn)橛秒姸嗟姆块g多攤、用電少的少攤學(xué)生才不會(huì)有意見。公式是room_share public_loss * room_usage / total_usage這個(gè)除法會(huì)產(chǎn)生除不盡的小數(shù)所以最后要保證sum(room_share) public_loss做法是最后一個(gè)房間的份額用總損耗減去前面所有房間的份額而不是自己再算一遍。3.3 金額字段用「分」還是「元」這個(gè)選擇影響整個(gè)支付模塊新手拿到源碼后最喜歡把total_amount設(shè)計(jì)成decimal(10,2)存元因?yàn)?SQL 里看起來直觀。但只要你接支付哪怕只是模擬支付我強(qiáng)烈建議你存「分」理由有兩個(gè)一是金額計(jì)算過程全是乘法除法用元會(huì)出現(xiàn)0.1 * 3 0.30000000000000004這種浮點(diǎn)誤差用整型分永遠(yuǎn)不會(huì)二是支付回調(diào)里平臺(tái)返回的金額單位就是分你后端直接比對(duì)少做一次單位轉(zhuǎn)換就少一個(gè)出錯(cuò)點(diǎn)。上面的bill表里total_amount int NOT NULL COMMENT 總金額單位分就是這個(gè)用意。展示給用戶看時(shí)再做格式化(total_amount / 100).toFixed(2)。這條習(xí)慣我不只在畢設(shè)項(xiàng)目里用后來在公司做計(jì)費(fèi)系統(tǒng)也是這個(gè)規(guī)矩。4. 跑通一條完整鏈路登錄、綁定房間、抄表出賬、模擬支付4.1 從 wx.login 到 openid這條身份識(shí)別鏈路只有一種正確寫法校園水電費(fèi)小程序的登錄本質(zhì)上不是「用戶名密碼登錄」而是通過微信的wx.login拿到臨時(shí) code再發(fā)給后端換取 openid。前端關(guān)鍵代碼// utils/request.js 中的登錄封裝 login() { return new Promise((resolve, reject) { wx.login({ success: (res) { wx.request({ url: ${this.baseUrl}/api/login, method: POST, data: { code: res.code }, success: (res2) { const { token } res2.data.data wx.setStorageSync(token, token) resolve(token) }, fail: reject }) } }) }) }后端對(duì)應(yīng)處理邏輯PostMapping(/login) public Result login(RequestBody LoginReq req) { // 1. 調(diào)用微信 jscode2session 接口用 code 換 openid String openid wechatClient.code2Session(req.getCode()); // 2. 查 user 表不存在則創(chuàng)建新用戶 User user userMapper.selectByOpenid(openid); if (user null) { user new User(); user.setOpenid(openid); userMapper.insert(user); } // 3. 生成自己的 tokenUUID 即可并返回 String token UUID.randomUUID().toString().replace(-, ); userMapper.saveToken(user.getId(), token); return Result.ok(token); }這里有一個(gè)代碼邏輯要注意jscode2session是后端去調(diào)微信接口不是你前端直接調(diào)。原因是這個(gè)接口需要appSecret而appSecret放在小程序前端就等于公開了任何人都能冒充你的后端。很多畢設(shè)源碼圖省事讓前端直接拿 code 換 openid這種代碼答辯時(shí)被老師追問「密鑰安全怎么保證」就會(huì)卡殼。你自己改的時(shí)候務(wù)必把a(bǔ)ppSecret留在后端配置文件里。4.2 綁定房間與身份校驗(yàn)一個(gè)「入住碼」就能解決的取舍登錄只解決了「你是誰微信號(hào)」還沒解決「你住哪個(gè)房間」。校園場景里學(xué)生和房間的綁定不能亂來——如果隨便填個(gè)房間號(hào)就綁成功那任何人都能看別人的賬單。常見做法有兩種預(yù)置名單校驗(yàn)或者入住碼綁定。我更喜歡入住碼方案因?yàn)檫壿嫼唵吻已菔拘Ч霉芾韱T在后臺(tái)給某房間生成一個(gè) 6 位入住碼學(xué)生在小程序里輸入房間號(hào)和入住碼完成綁定。后端代碼PostMapping(/bindRoom) public Result bindRoom(RequestParam String roomNo, RequestParam String joinCode, RequestParam Long userId) { Room room roomMapper.selectByRoomNo(roomNo); if (room null || !room.getJoinCode().equals(joinCode)) { return Result.fail(房間號(hào)或入住碼錯(cuò)誤); } // 解綁舊綁定再綁新房 userMapper.updateRoom(userId, room.getId()); return Result.ok(); }這個(gè)方案比「上傳名單核對(duì)學(xué)號(hào)姓名」少一步導(dǎo)入操作也比「不校驗(yàn)直接綁」顯得有設(shè)計(jì)感。你在答辯時(shí)可以說入住碼由管理員生成離校后解綁即作廢能防止學(xué)生串房。4.3 沒有商戶號(hào)也能演示支付模擬支付回調(diào)的正確姿勢校園開發(fā)者最現(xiàn)實(shí)的問題個(gè)人小程序沒有微信支付權(quán)限拿不到商戶號(hào)。很多人的處理辦法是把支付按鈕做成「假裝支付成功」賬單狀態(tài)直接改成已繳。這個(gè)做法在答辯時(shí)只要老師細(xì)看就會(huì)覺得項(xiàng)目「黑匣子」——你解釋不清錢去哪了。我的建議是把支付流程做成兩層模擬支付一層真實(shí)微信支付留好接口。后端收銀臺(tái)代碼這樣寫// 發(fā)起支付創(chuàng)建支付單狀態(tài)為待支付 public PayOrder createPayOrder(Long billId) { PayOrder order new PayOrder(); order.setBillId(billId); order.setOrderNo(generateOrderNo()); order.setStatus(WAIT_PAY); payOrderMapper.insert(order); return order; } // 模擬支付回調(diào)不調(diào)微信直接標(biāo)記支付成功 PostMapping(/mockPayNotify) public Result mockPayNotify(RequestParam String orderNo) { PayOrder order payOrderMapper.selectByOrderNo(orderNo); if (order null || !WAIT_PAY.equals(order.getStatus())) { return Result.fail(訂單不存在或已處理); } // 冪等重復(fù)回調(diào)不重復(fù)修改 order.setStatus(PAID); order.setPaidAt(new Date()); payOrderMapper.update(order); // 賬單同步更新 billMapper.markPaid(order.getBillId()); return Result.ok(); }真實(shí)微信支付時(shí)你只需要把mockPayNotify替換成微信的支付回調(diào)地址并把payOrderMapper里的狀態(tài)更新邏輯換成微信支付結(jié)果驗(yàn)簽其他部分完全不用動(dòng)。這個(gè)「接口留白」的做法比把代碼寫死強(qiáng)得多。5. 校園水電費(fèi)系統(tǒng)最常見的 5 個(gè)坑現(xiàn)象、原因、解決5.1 換設(shè)備登錄后看到的是別人的賬單現(xiàn)象學(xué)生換手機(jī)或者重新授權(quán)登錄后首頁顯示的不是自己房間的賬單而是另一個(gè)房間的數(shù)據(jù)。原因user表里沒有把openid和room_id的對(duì)應(yīng)關(guān)系存好。很多源碼是「登錄時(shí)創(chuàng)建一個(gè)新用戶綁定房間時(shí)再創(chuàng)建一個(gè)新用戶」導(dǎo)致兩次登錄生成兩條記錄綁定房間只寫到了第二條記錄上。解決user表設(shè)計(jì)為openid唯一綁定房間只做UPDATE user SET room_idxx WHERE openidyy不要走「新用戶插入」邏輯。5.2 階梯電費(fèi)算出一個(gè)無限循環(huán)小數(shù)現(xiàn)象賬單金額莫名其妙多出一分錢或者階梯計(jì)算后金額對(duì)不上。原因用double或float存儲(chǔ)并計(jì)算單價(jià)二進(jìn)制無法精確表示 0.1。解決金額全部用分int存儲(chǔ)Java 里用BigDecimal計(jì)算最后setScale(0, HALF_UP)。水電表底數(shù)可以保留兩位小數(shù)但只作為表底存儲(chǔ)單價(jià)乘完的結(jié)果立刻轉(zhuǎn)成整數(shù)分。5.3 賬期和費(fèi)率生效日期錯(cuò)位現(xiàn)象6 月的賬單用了 7 月 1 日才生效的新電價(jià)。原因生成賬單時(shí)代碼里直接查SELECT * FROM fee_rate取第一條而這條記錄恰好是后來修改過的。解決fee_rate表增加effective_date字段生成賬單時(shí)按WHERE effective_date 賬期最后一天 ORDER BY effective_date DESC LIMIT 1取費(fèi)率。賬期是 6 月就用 6 月底之前最后生效的費(fèi)率而不是當(dāng)前最新費(fèi)率。5.4 學(xué)生雙擊支付按鈕生成了兩張重復(fù)賬單現(xiàn)象學(xué)生點(diǎn)了一次繳費(fèi)沒反應(yīng)又點(diǎn)了一次結(jié)果支付列表里出現(xiàn)兩條一模一樣的賬單。原因支付創(chuàng)建接口沒有做冪等控制兩次請(qǐng)求各生成一個(gè)訂單。解決兩個(gè)層面兜底。數(shù)據(jù)庫層面pay_order表的bill_id加唯一索引同一個(gè)賬單只能有一條未支付訂單。代碼層面創(chuàng)建訂單前先查SELECT * FROM pay_order WHERE bill_id ? AND status WAIT_PAY存在就直接返回舊訂單號(hào)。5.5 本地能跑真機(jī)一調(diào)用就連接失敗現(xiàn)象在微信開發(fā)者工具里一切正常換成手機(jī)預(yù)覽就請(qǐng)求失敗。原因開發(fā)者工具里localhost能通是因?yàn)楣ぞ吆碗娔X在同一臺(tái)機(jī)器上真機(jī)上localhost指向手機(jī)自身請(qǐng)求自然到達(dá)不了電腦。解決調(diào)試階段在request.js里把 baseURL 換成電腦的局域網(wǎng) IP同時(shí)微信開發(fā)者工具勾選「不校驗(yàn)合法域名」。如果換了 IP 仍不行檢查 Windows 防火墻有沒有放行 8080 端口。這個(gè)坑我?guī)缀趺孔鲆粋€(gè)項(xiàng)目都要栽一次所以現(xiàn)在已經(jīng)養(yǎng)成習(xí)慣項(xiàng)目根目錄放一個(gè)config.js第一行就是環(huán)境注釋。6. 讓這個(gè)項(xiàng)目從「能跑」變成「能打」批量抄表和欠費(fèi)提醒6.1 管理端抄表脫離手工錄入Excel 批量導(dǎo)入怎么做畢設(shè)里演示抄表逐條手輸 6 個(gè)房間還能忍輸 60 個(gè)房間時(shí)演示就有點(diǎn)尷尬。給管理端加一個(gè)批量導(dǎo)入導(dǎo)出的功能屬于性價(jià)比很高的進(jìn)階點(diǎn)。常見做法是后端用 Apache POI 讀取 Excel核心邏輯如下public ImportResult importExcel(MultipartFile file) { Workbook wb WorkbookFactory.create(file.getInputStream()); Sheet sheet wb.getSheetAt(0); ListMeterRecord list new ArrayList(); for (Row row : sheet) { if (row.getRowNum() 0) continue; // 跳過表頭 String building row.getCell(0).getStringCellValue(); String roomNo row.getCell(1).getStringCellValue(); BigDecimal currentValue BigDecimal.valueOf(row.getCell(2).getNumericCellValue()); // 查找房間 id沒有則報(bào)錯(cuò) Room room roomMapper.selectByBuildingAndNo(building, roomNo); if (room null) { importResult.addError(row.getRowNum(), 房間不存在); continue; } list.add(buildRecord(room.getId(), currentValue)); } // 批量插入依賴唯一索引跳過重復(fù)賬期 meterRecordMapper.batchInsertIgnore(list); return importResult; }參數(shù)說明batchInsertIgnore對(duì)應(yīng) SQL 里的INSERT IGNORE INTO配合表結(jié)構(gòu)里uk_room_period_type唯一索引重復(fù)賬期會(huì)靜默跳過而不是報(bào)錯(cuò)中斷。在 Excel 表里增加「上次表底」列并在導(dǎo)入時(shí)和庫里比對(duì)如果本次表底小于上次表底就判定為錄入異?!@是實(shí)際使用中很容易被忽略的校驗(yàn)。6.2 欠費(fèi)主動(dòng)推送把消息訂閱加進(jìn)來賬單生成后沒人看是常態(tài)要做成一套能用的欠費(fèi)提醒系統(tǒng)可以在用戶端加一個(gè)「訂閱消息」入口。微信小程序的訂閱消息需要用戶主動(dòng)點(diǎn)擊授權(quán)所以常規(guī)做法是用戶繳費(fèi)成功后彈出「允許發(fā)送賬單提醒」然后后端在每月出賬后調(diào)用subscribeMessage.send推送模板消息。這里有個(gè)反直覺點(diǎn)微信的訂閱消息是「一次性」的用戶授權(quán)一次只能收到一條推送不是永久的。所以你的授權(quán)入口要設(shè)計(jì)在「繳費(fèi)成功」這個(gè)用戶粘性最高的環(huán)節(jié)讓他每次繳費(fèi)都順便訂閱下個(gè)月的賬單提醒而不是在登錄時(shí)誘導(dǎo)授權(quán)。6.3 月度賬單統(tǒng)計(jì)一條 SQL 看出整個(gè)系統(tǒng)有沒有做對(duì)你寫完計(jì)費(fèi)邏輯后可以用一條 SQL 自檢SELECT COUNT(*) AS total_bills, SUM(CASE WHEN status 1 THEN 1 ELSE 0 END) AS paid_bills, ROUND(SUM(CASE WHEN status 1 THEN 1 ELSE 0 END) / COUNT(*), 4) AS paid_rate, SUM(total_amount) AS should_receive, SUM(CASE WHEN status 1 THEN total_amount ELSE 0 END) AS real_received FROM bill WHERE period 2025-06;這條 SQL 能查出當(dāng)月應(yīng)收、實(shí)收、繳費(fèi)率三個(gè)指標(biāo)是你在答辯時(shí)展示「我做的是完整系統(tǒng)」最直接的證據(jù)。如果實(shí)收金額對(duì)不上那就是前面 5.2 的精度問題沒處理干凈。我大四那年做某圖像處理 Demo完全跳過了計(jì)費(fèi)規(guī)則直接寫界面結(jié)果導(dǎo)師一句「你這費(fèi)用用元算出現(xiàn)浮點(diǎn)誤差怎么辦」就把我問住了。后來重做了這套水電費(fèi)系統(tǒng)把費(fèi)率表、抄表記錄、賬單關(guān)系想清楚才真正弄明白業(yè)務(wù)系統(tǒng)應(yīng)該怎么搭。這個(gè)方向值得你投入不是因?yàn)槟苁∈露亲鐾暌槐槟銓?duì)「登錄—業(yè)務(wù)—支付—對(duì)賬」整條鏈路的理解會(huì)徹底不一樣希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取