学生公寓电费管理系统实战:微信小程序+Java+MySQL全解析
每年新生入学那阵儿宿舍楼下的充值柜台都是重灾区。排队半小时就为了插一下电卡看看还剩几度电宿管阿姨一边给学生操作一边还得解释为什么这个月电费比上个月高了二十块。后来我帮朋友折腾过一套宿舍管理系统顺手把电费这一块完全翻新了做成了基于微信小程序的学生公寓电费管理系统——学生手机点点就能查余额、充电费、报修管理员在后台录入读数、看欠费名单、导出账单。这套项目前后花了一个多月包含了完整的前端小程序、后端Java接口、MySQL数据库、设计文档和答辩PPT代码都能跑通。今天我把整个项目的拆解思路、技术选型、核心代码逻辑和调试过程中踩过的坑一次性讲清楚特别适合正在做课程设计、毕业设计或者想给学校后勤做一套轻量级缴费工具的同学参考。整个系统说到底就是“一屏看全楼一键管全楼”。学生端解决“不知道自己宿舍还剩多少电、欠费了被断电才知道”的信息差管理端解决“抄表全靠腿、对账全靠笔、催费全靠吼”的粗放式运营。我见过太多类似的项目要么只做了前端界面没有后端逻辑要么把业务规则写得过于理想化一上线就对不上账。这套系统在设计时我始终把握一个原则所有的钱和度数的变动都必须有记录、可追溯、能平账这是公寓电费系统和普通demo之间最本质的区别。1. 项目定位与需求拆解这套系统到底在解决什么1.1 三种角色的真实痛点和系统边界开发这类系统之前最先要搞清楚的不是用什么框架而是“谁在用、他们现在怎么干活、哪里最痛”。我梳理了学生公寓电费管理链路里最典型的三个角色并且把痛点和功能点逐一对应了起来。学生最关心“还剩多少钱、能用几天、这个月为什么用了这么多”。痛点是得到答案的成本极高——要么跑去楼下充电卡、要么等月底公告。系统对应的功能是余额查询、充值缴费、用电明细、在线报修。宿管/后勤管理员最关心“哪些宿舍快欠费了、电表读数怎么录、月底账能不能对上”。痛点是逐间抄表、手工记账、催费全靠贴条。系统对应的功能是宿舍电表管理、读数录入、欠费预警、账单导出。系统运维/超级管理员最关心“单价谁定的、补助怎么发、出问题找谁”。痛点是权限边界模糊谁都改数据最后对不上账。系统对应的功能是角色权限控制、基础参数配置、日志审计。这三类角色对应的界面差异很大所以我在设计时把小程序端拆成了“普通用户端”和“管理员端”两套视角同一套后端接口通过角色字段控制权限。很多课程设计做到最后变成“人人能登录、人人能改单价”这在真实场景里是灾难答辩的时候也容易被问住。所以你设计项目时第一优先级就是把角色边界定清楚。1.2 核心业务流程和状态流转理解系统最好的方式是把一次完整的“电费生命周期”讲清楚管理员在系统里录入本月初每个房间的电表读数系统根据“本次读数 - 上次读数”算出用电量再乘上单价从房间余额里扣钱并生成一条用电明细学生打开小程序看到余额被扣了于是发起充值充值的金额进入房间余额同时产生一条充值订单。如果余额低于某个阈值系统标记该房间为“欠费预警”再低就触发“欠费断电”状态学生充值到账后状态自动恢复。这个流程里有几个容易忽略的关键点。比如“预付费模式”和“后付费模式”在很多学校是混用的有的学校是先充值后用电余额不足直接断电有的学校是先用后扣月底出账单再催缴。我做的这套系统默认支持预付费模式同时在后端留下一个模式开关方便不同学校切换。另外电费是跟随“房间”而不是“学生个人”的学生搬宿舍剩余电费要跟着房间走这套系统的余额字段就放在宿舍房间表里而不是用户表里。这一点你答辩的时候主动讲出来会显得你对业务流程理解得很透。1.3 功能清单反推项目工作量在动手写代码前一定要先列功能清单并给每个功能估算开发量不然很容易后期失控。我整理这套系统的完整功能清单如下学生端微信登录与学号绑定、首页电费卡片、用电明细列表、在线充值模拟支付、余额不足提醒、房间报修、个人缴费记录。管理端宿舍楼栋和房间管理、学生信息导入、电表读数录入手工/批量、欠费宿舍列表、自助充电补助发放、电费单价配置、报修工单处理、账单明细导出。公共模块手机号绑定校验、订单幂等校验、数据库自动备份、操作日志记录、定时任务执行每日扣费扫描、欠费预警、月度结算。这套清单做下来如果是单人开发后端大约需要三周小程序端大约需要两周剩下时间全部用来联调和改bug。功能看似不多但每个都要考虑边界情况比如“订单已经支付成功但网络断了前端该显示什么状态”“管理员录错读数导致电费是负数怎么办”。宁可先砍掉花哨的图表和大屏展示也要把账务流做扎实。2. 技术选型与数据库设计为什么是“小程序 Java MySQL”2.1 技术栈选择的底层逻辑微信小程序是当下校园类工具最合适的载体没有之一。原因很直接学生不用下载App扫码或搜索就能用微信自带身份体系学生点一下授权后端就能拿到OpenID作为账号标识省去了注册、找回密码这一大堆交互。相比纯H5页面小程序能调用微信原生的登录、支付、订阅消息能力体验更顺滑相比原生App开发和分发成本低得多后勤老师也更容易接受。后端我用的是Java Spring Boot因为校园里最普及的课程设计技术栈就是Java加上Spring Boot生态成熟接口开发效率高一个问题搜出来社区资料也特别多。数据库选了MySQL对于这种千万行级别都不到的数据量来说绰绰有余。可能有人会问为什么不直接用Python Flask或者Node.js我的回答是能跑但如果你要交文档、要答辩、要讲清楚设计模式Spring Boot那套分层结构Controller / Service / Mapper是最标准、最容易被老师认可的思路。前端小程序原生开发没有引入第三方UI框架。这样做的好处是包体小、编译快、版本兼容问题少。页面上展示电费趋势图时用了简单的Canvas手绘折线也不依赖图表库。说实话课程设计里引入一个大型图表库徒增体积万一版本升级接口变了调试成本非常高。2.2 数据库表结构每张表为什么这么设计数据库是这套系统的地基。很多新手一上来就建一张“用户表”加一张“电费表”结果做到后面发现充值记录对不上账、不知道某度电是哪个时段用的。我的表结构设计遵循一个原则谁的钱、谁的度数、什么时间、什么原因、操作人是谁——五个维度必须全部能回答。用户表负责存学生的微信OpenID、学号、姓名、手机号、所属房间、角色标识。重点说一下表里不要直接做学号主键因为学生转专业、换号码的情况很常见自增ID主键加唯一索引学号更稳妥。房间表就是“电费钱包”。字段包括楼栋、房间号、当前余额、房间状态正常/欠费预警/已断电、补助阀值。房间状态和余额都冗余存一份虽然严格来说可以实时计算但冗余能大幅简化查询和断电判断。电表读数表每间房每次读数的记录字段包括房间ID、抄表日期、当前读数、上期读数、抄表方式、抄表人。这张表保留了原始的抄表痕迹月末对不上的时候能回溯是哪次录入出了问题。充值订单表包括订单号、用户ID、房间ID、充值金额、支付状态、创建时间、支付时间。订单号是全局唯一的业务主键所有对账都以这个号为准。用电消费明细表包括扣费周期、起止读数、用电量、单价、金额、扣费时间。这张表回答“我这个月为什么花了这么多钱”。报修工单表包括报修人、宿舍号、故障描述、图片URL、状态字段、处理结果、完成时间。核心建表SQL片段大致长这样CREATE TABLE room ( id BIGINT AUTO_INCREMENT PRIMARY KEY, building_no VARCHAR(20) NOT NULL, room_number VARCHAR(20) NOT NULL, current_balance DECIMAL(10,2) DEFAULT 0.00, status TINYINT DEFAULT 0 COMMENT 0-正常 1-预警 2-已断电, threshold DECIMAL(10,2) DEFAULT 5.00, UNIQUE KEY uk_building_room (building_no, room_number) ); CREATE TABLE charge_order ( id BIGINT AUTO_INCREMENT PRIMARY KEY, order_no VARCHAR(32) NOT NULL UNIQUE, user_id BIGINT NOT NULL, room_id BIGINT NOT NULL, amount DECIMAL(10,2) NOT NULL, status TINYINT DEFAULT 0 COMMENT 0-待支付 1-已支付 2-已取消, create_time DATETIME NOT NULL, pay_time DATETIME );这里有个跟直觉相反的细节余额放在房间表而不是用户表这是我踩过坑之后总结出来的。一开始我把余额放在用户表结果A同学退宿、B同学入住搞了半天还要做余额迁移后来改成余额跟房间走谁住在里面谁就享受这个房间的余额逻辑瞬间就顺了。数据库的每条分表决策本质上都是对业务规则的提前定义。2.3 接口统一返回和鉴权约定给小程序用的后端接口统一返回格式非常重要。小程序端的回调写法极其依赖返回结构的一致性如果一会儿返回JSON、一会儿直接返回字符串前端解析代码就会变得乱七八糟。我用的统一返回结构是{ code: 200, message: 操作成功, data: {} }code为200表示成功非200表示各种业务错误前端只要判断code是否等于200即可不需要每个接口单独写异常解析。涉及分页的接口data里统一放total和list两个字段这个约定写进接口文档后前端和后端联调时几乎不用来回问。鉴权部分学生端采用微信登录后返回Token后续请求在Header里带Authorization: Bearer token管理员端则使用账号密码登录后同样拿Token。后端用拦截器统一校验Token白名单接口比如获取OpenID的登录接口放行其余一律拦截。这个方案实现简单对课程设计级别的系统完全够用。3. 核心业务逻辑与关键代码实现3.1 微信登录与学号绑定前后端各自该做什么微信小程序的登录流程看似简单但有很多实现得不对的项目。我讲一个标准且安全的做法前端调用wx.login()拿到临时凭证code把code发给后端后端用这个code加上AppID和AppSecret去微信接口换openid和session_key。关键点openid的换取必须放在后端完成不能在小程序端直接请求微信接口否则AppSecret会暴露给前端。拿到openid后后端去用户表查询有没有这个openid。如果存在直接生成Token返回前端跳首页如果不存在说明这个微信号还没绑定学号后端先返回一个“未绑定”标识前端跳转到一个绑定学号的页面让用户填学号和姓名调bindStudent接口完成绑定。后端绑定接口里有个细节——不能无条件相信前端传来的学号姓名否则任何人都能绑定别人的宿舍。我在项目里加了一步校验从学生名单表里查这个学号是否存在、姓名是否匹配校验通过才绑定。这相当于把“身份可信”的责任从代码层转移到了初始数据层。核心代码如下PostMapping(/wx/login) public Result login(RequestBody WxLoginDTO dto) { String openid wxService.code2Session(dto.getCode()); User user userMapper.selectByOpenid(openid); if (user null) { return Result.failed(1001, 未绑定学生身份); } String token tokenService.createToken(user.getId()); return Result.success(token); } PostMapping(/user/bind) public Result bind(RequestBody BindDTO dto, RequestHeader(Authorization) String token) { StudentInfo info studentInfoMapper.checkByNoAndName(dto.getStudentNo(), dto.getRealName()); if (info null) { return Result.failed(1002, 学号与姓名不匹配); } User user new User(); user.setOpenid(openidService.getOpenidByToken(token)); user.setStudentNo(dto.getStudentNo()); user.setRealName(dto.getRealName()); user.setRoomId(info.getRoomId()); userMapper.insert(user); return Result.success(); }3.2 电费计算的核心逻辑单价、阶梯电价与公摊损耗电费计算看着简单——读数差乘单价但真实场景里藏着两个很容易被答辩老师追问的细节阶梯电价和公摊电费。阶梯电价的意思是用电量在某段区间内是一个价超过区间后单价上涨。比如每月0到50度按0.54元/度50到100度按0.62元/度超过100度按0.82元/度。启用电阶梯电价功能后计算逻辑就变成分段累加public BigDecimal calculateFee(BigDecimal usage) { BigDecimal fee BigDecimal.ZERO; BigDecimal remaining usage; for (PriceTier tier : priceTierList) { if (remaining.compareTo(BigDecimal.ZERO) 0) break; BigDecimal tierRange tier.getEndValue().subtract(tier.getStartValue()); BigDecimal usedInTier remaining.min(tierRange); fee fee.add(usedInTier.multiply(tier.getUnitPrice())); remaining remaining.subtract(usedInTier); } // 再加上公摊损耗费按房间建筑面积分摊 fee fee.add(publicAreaFee); return fee.setScale(2, RoundingMode.HALF_UP); }公摊电费也是不少学校真实存在的逻辑走廊灯、楼道应急灯、公共洗衣房的电费不能算在某一个房间头上通常按宿舍人数或面积均摊。我在设计时把这部分费用单独做成一个“公共分摊任务”每月月底自动跑一次生成所有房间的公共电费清单并统一扣费。这样学生看到的账单里会明确区分“宿舍用电”和“公共分摊”两项解释起来也清楚。扣费时机有两种策略管理员手工按“抄表周期扣费”和系统定时自动扣费。我推荐定时自动扣费每天凌晨两点系统扫描所有房间有最新读数的就用最新读数计算没有新读数的沿用上期读数。定时任务实现很简单Spring Boot里加一个Scheduled(cron 0 0 2 * * ?)注解方法就行。3.3 充值与余额变更的并发安全不要让一分钱悄悄消失这块是整套系统实现成败的胜负手。电费充值和扣款都涉及余额增减如果直接写成“读出余额 - 修改余额 - 写回余额”并发下一定丢数据。比如学生同时在App上发起两笔充值两笔请求同时读到余额100分别增加50和30最终写回的可能只是130而不是180。解决并发有两个层次。第一个层次用数据库能力兜底SQL直接原子更新UPDATE room SET current_balance current_balance #{amount} WHERE id #{roomId};扣款时加一个条件防止扣成负数UPDATE room SET current_balance current_balance - #{amount} WHERE id #{roomId} AND current_balance #{amount};这样即便并发来了数据库的行锁也会让操作串行执行第二条SQL影响行数为0时业务层就知道“余额不足不能扣费”自动触发欠费预警。第二个层次是接口层的幂等校验同一个充值订单无论用户手滑点了几次确定后端只允许处理一次。做法是支付回调里先查订单状态只有当订单还是“待支付”时才更新余额否则直接返回成功避免重复入账。微信支付的小程序支付流程在真实上线时需要商户号和支付资质学生项目里通常申请不下来。我在这套系统里做了一个“模拟支付”开关支付页面走完整下单流程但点击确认后走支付沙箱接口或者直接模拟支付成功后端回到订单支付成功逻辑。这样业务闭环完全跑通又不卡在资质申请上。答辩时你可以明确讲真实环境只需替换支付接口其余逻辑完全复用。3.4 欠费预警与远程断电的实现链路欠费断电是整个系统最“硬核”的功能也最容易做得华而不实。真实公寓里要远程断电需要电表硬件支持远程阀控也就是智能电表里有个继电器管理平台远程下发“拉闸”或“合闸”指令。这套系统里我做了三档状态余额大于预警阈值比如20元正常状态余额小于预警阈值但大于断电阈值比如5元预警状态前端首页出现黄色提醒条后端给对应用户发微信订阅消息余额小于断电阈值断电状态后端置字段status2同时如果对接了智能电表平台就调用对应接口下发电表拉闸指令。断电前的订阅消息提醒是个很人性化的设计。微信的订阅消息需要用户主动授权一次获得授权后后端可以在“用户同意、有实际触发场景”的情况下给他发送一条“您的宿舍余额已不足X元请及时充值”。注意订阅消息是一次性授权用户点了一次授权只能接收一条消息下次提醒需要再次请求授权这是微信平台自身的规则限制。后端每日扫描代码片段Scheduled(cron 0 0 2 * * ?) public void dailyCheck() { ListRoom rooms roomMapper.selectAll(); for (Room room : rooms) { if (room.getCurrentBalance().compareTo(room.getThreshold()) 0) { // 给关联用户发送订阅消息 messageService.sendLowBalanceNotify(room); if (room.getCurrentBalance().compareTo(BigDecimal.ZERO) 0) { room.setStatus(2); // 调用电表平台断电接口模拟 ammeterClient.powerOff(room.getId()); } else { room.setStatus(1); } } else { room.setStatus(0); } roomMapper.updateById(room); } }3.5 报修工单的状态机设计宿舍里除了电费最常见的诉求是报修。电费系统里集成报修功能不算复杂但状态流转要设计清楚不然就会出现“学生报修了管理员也说处理了但学生没看到结论”的问题。我用的状态机很简单0-待受理、1-处理中、2-已完成、3-已关闭。学生提交报修创建工单管理员在管理端看到待受理列表后点击受理状态变处理中同时学生端显示“师傅处理中”管理员填写处理结果后状态变已完成学生端同步显示结果。这里有个容易被忽略的体验点状态变化时要给学生发一条订阅消息不然学生不知道工单的推进进度。报修模块虽然代码量不大但它是整个系统“有人情味”的加分项建议一定要保留。4. 小程序端页面设计与交互让数据在手机屏幕上“活”起来4.1 首页视觉与信息密度一眼看到最重要的三个数字首页的UI设计我用的是“一个核心卡片 三条明细列表 一个操作按钮”的结构。顶部卡片展示当前宿舍余额大字显示、房间号、所属楼栋和当前用电状态正常/预警/已断电。卡片正下方放两个高亮度操作按钮充值缴费、查看明细。中间部分是“本月用电”的简版柱状图按最近7天的用电量画出趋势方便学生快速感知自己宿舍用电是否异常。页面底部是最近三条用电扣费记录和充值记录。小程序的首页是所有功能的价值浓缩我不建议把菜单堆得密密麻麻。视觉上采用白底加蓝色渐变主按钮状态用红黄绿三色区分余额不足时红色脉冲提醒会一起出现。这里说个原生的交互技巧小程序onShow生命周期非常关键学生每次从前台切回小程序首页都要拉取最新余额因为极端情况下学生在另外一个设备上头已经充值了当前设备还显示旧数据会引发误解。关键页面结构示意view classbalance-card text classroom-name{{roomInfo.buildingNo}}栋{{roomInfo.roomNumber}}/text text classbalance-num{{roomInfo.currentBalance}}/text text classstatus-tag wx:if{{roomInfo.status 2}}已断电/text /view4.2 充值缴费的业务闭环订单状态必须先立起来充值缴费的交互流程看上去简单但代码里隐藏着各种状态问题。我在订单表里设计了三状态待支付、已支付、已取消。前端点击“充值”后先请求后端接口创建订单拿到订单号后进入支付确认页学生点击“确认支付”后端在模拟支付模式下直接把订单置为已支付给房间余额加钱然后返回结果。这里有一个非常值得注意的交互细节充值成功后不要立即跳回首页刷新余额而要等接口返回成功的回调再刷新。否则在极端情况下余额还没来得及入账用户看到余额没变会下意识再点一次充值于是重复扣费。另外充值需要支持输入自定义金额和快速金额10元、20元、50元两种方式快速金额的按钮要足够大减少手机端输入的麻烦。支付成功页展示新增余额、新的总余额和大字“充值成功”这个简单页面能极大降低学生的焦虑感。4.3 管理员端业务页面抄表录入与欠费名单的表格体验管理端的重点不在花哨而在高效。我用的是“顶部Tab切换 列表 弹窗表单”的交互模式。进入管理端首页默认展示“欠费宿舍名单”按余额从低到高排序列表项显示楼栋、房间、余额、上次扣费时间点击可直接给该宿舍发放补助或远程合闸。这个页面是宿管每天打开最多的页面所以加载速度一定要快如果宿舍数量大后台接口要支持分页和搜索。抄表录入是另一个核心功能。管理员可以逐间录入也可以批量粘贴数据。为了减少手工录入出错我在录入表单里面做了“上期读数自动带出”的功能管理员输入本次读数后系统自动计算用电量并给出参考电费确认后提交。每次提交都会往meter_reading表插入一条记录同时生成扣费流水。录入错误的场景人人都会遇到系统需要支持“回退上一条记录”的操作把余额和状态恢复原状否则月底对账时非常痛苦。4.4 前端公共方法封装请求拦截、Token注入与统一错误提示小程序端的公共代码我单独封装了util/request.js所有页面请求都走这一个入口。这样能统一处理四件事自动拼上baseUrl、自动在请求头里带上Token、code非200时弹出统一错误提示、请求过程中自动展示加载动画。很多新手在每个页面写重复的wx.request导致项目越写越乱到联调时改一个baseUrl要改几十处。封装后从开发环境切生产环境只需要改一个变量。另外要特别注意小程序的wx.setStorageSync缓存策略Token和用户基本信息可以存到本地存储但余额、电费明细这些动态数据不应该长时间缓存每次进入页面都应该拉取新数据缓存只用于减少冷启动空白感。我在首页加了一个“骨架屏”占位——数据没回来之前页面先显示灰色布局避免用户以为自己打开了个空页面。这个细节很多源码里都没有做完之后整体的用户口碑会明显不一样。封装请求的核心代码const request (url, method, data) { return new Promise((resolve, reject) { wx.request({ url: baseUrl url, method: method || GET, data: data || {}, header: { Authorization: Bearer wx.getStorageSync(token) }, success: (res) { if (res.data.code 200) { resolve(res.data.data); } else { wx.showToast({ title: res.data.message, icon: none }); reject(res.data); } }, fail: (err) { wx.showToast({ title: 网络异常, icon: none }); reject(err); } }); }); };5. 本地部署、联调排错与项目演示实录5.1 从源码到跑起来的完整步骤项目拿回来之后第一步不是急着写代码而是先把环境搭好。我把环境和启动顺序完整列一下按顺序操作基本不会卡壳安装JDK 1.8或更高版本配置环境变量JAVA_HOME。安装MySQL 5.7及以上版本执行项目里的init.sql建库建表。安装Maven配置阿里云镜像国内网络下载依赖会快很多。用IDEA导入后端代码等待Maven依赖下载完毕。修改application.yml里的数据库账号密码、小程序AppID和AppSecret。运行DormElectricApplication主类后端默认端口8080。打开微信开发者工具导入小程序前端目录修改util/config.js里的baseUrl为http://localhost:8080。开发工具里勾选“不校验合法域名、web-view业务域名、TLS版本以及HTTPS证书”后就可以正常请求本地后端了。如果使用现成的源码注意先检查数据库有没有初始数据例如学生名单、房间数据和一条最新的电表读数否则登录绑定会提示“查无此人”。有一个经常被新手忽略的坑微信开发者工具默认模拟器的用户代理和真机不同本地调试时一切正常一到真机预览就请求失败。这是因为小程序真机要求后端接口必须是HTTPS且域名备案。课程设计阶段通常在局域网内演示这时候需要让手机和电脑连同一个Wi-Fi后端地址从localhost改成电脑的局域网IP比如http://192.168.1.100:8080同时在微信开发者工具里点“真机调试”用手机扫码预览。这一步很多人卡一整天其实就改一个IP的事。5.2 联调过程中我踩过的几个坑第一个坑是MySQL中文乱码。建库的时候如果没指定字符集存进去的中文全变成问号。解决办法是建库时显式指定utf8mb4数据库连接的URL里也必须加上characterEncodingutf8。第二个坑是端口被占用。本机恰好有个旧服务占了8080后端一启动就报Port already in use。我直接把后端端口改成了8080同时把小程序端baseUrl同步改为8080但这个改动在联调时很容易被忽略导致前端报404。建议给所有配置项统一做一个常量管理端口号集中写在一个地方。第三个坑是跨域问题。小程序端请求后端接口本质上是跨域请求如果后端没配跨域过滤器浏览器请求会报CORS错误。我一开始只在Controller上加了CrossOrigin但拦截器拦截的请求仍然会先被CORS挡住。最后解决方案是采用一个全局CORS配置拦截器把所有接口的跨域问题统一解决。第四个坑是定时任务的冲突——本地调试时数据库时间凌晨两点已经过了定时任务不执行这时候我写了一个测试接口手动触发任务调试完再删除。5.3 高频问题速查表联调阶段把大量高频问题整理成了一张速查表这里分享给大家按图索骥能省很多排查时间。症状可能原因解决方案小程序请求后端超时基础库版本过低或后端未启动检查后端Console是否有启动成功日志真机调试时确认IP可达登录后一直显示未绑定学生名单中没有该学号或前端传的学号姓名不匹配先在后端数据库直接查学生名单表确认数据存在充值成功但余额没增加订单状态判断有误或更新SQL未生效看后端日志确认订单状态是否从0置为1再检查房间ID是否传对中文乱码数据库字符集不对或URL缺少编码参数字符集统一用utf8mb4连接URL加characterEncodingutf8定时任务不跑没有加EnableScheduling注解在启动类上补充该注解页面白屏或数据不显示前端data字段名与后端返回字段不一致用Console打印接口返回逐个字段对照并发充值金额变少没有用到原子更新SQL参照上文SQL改为current_balance current_balance amount形式5.4 演示与答辩的加分细节项目做出来了最后一步是怎么把成果讲好。很多同学演示时喜欢面面俱到结果五分钟演讲什么都讲了老师什么都没记住。我的建议是演示只讲三个场景学生从登录到看到自己宿舍余额的完整过程管理员录入一次读数后宿舍余额变化的完整过程余额不足触发预警和断电提醒的完整过程。这三个场景刚好覆盖了登录鉴权、核心业务计算、定时任务与消息推送四条主线也最能体现系统设计的完整性。答辩时如果被问到“这个系统有什么难点”千万不要只说“做了很多功能”。要主动抛出设计取舍比如余额为什么放在房间表而不是用户表为什么用SQL原子更新而不是Java代码里先查后改为什么模拟支付而不是直接跳过支付。这些是能体现真实工程思考的点比“我用了Spring Boot和微信小程序”这种回答有分量得多。另外事前准备好一份带演示数据的Excel账单打印出来给评委看这个小动作通常会让答辩老师眼前一亮。最后再分享一个经验项目做完不是终点而是起点。我在这套系统里留了一个抽象出来的“计量计费引擎”换掉数据源就是一套通用的预付费账户系统不光能管电费以后管水费、燃气费、洗衣费都能复用。你在做类似项目时如果时间允许也建议把自己的代码往抽象层推一步用合理的接口隔离开核心计费逻辑和外部依赖这样等你回头维护或者扩展时会发现当初多花的一天特别值。