简介:这是一份面向高校计算机专业毕业设计场景的Java电费管理系统完整源码包,适合正在准备毕设或需要Java全栈实战练习的学生参考。系统围绕居民小区与企业电费管理展开,涵盖用户管理、电费计算、在线缴费、数据统计与缴费提醒等核心模块,采用Spring Boot结合MyBatis构建后端,MySQL存储数据,前端使用HTML5、CSS3与JavaScript,并集成Spring Security与JWT保障安全,整体遵循MVC分层设计。压缩包共158个文件,约1.78MB,其中52个java文件承载业务逻辑与接口实现,24个js、19个html与14个css构成前端页面与交互样式,另有xml配置、字体图标及少量properties、json等资源文件,结构完整便于二次开发。目前已有115人学习下载,可作为毕设选题参考、课程设计模板或Java Web综合练习素材,帮助读者理解电费计费算法、权限控制与报表生成等实现思路。
1. 电费管理系统毕业设计:从能跑起来到能讲清楚
很多同学拿到「毕业设计电费管理系统(java).zip」这类压缩包时,第一反应是解压、找 main 方法、点运行,然后发现数据库连不上、依赖下载失败、页面 404,最后只能硬着头皮改到能跑,答辩时被问「你这个系统怎么保证多用户同时抄表不重复计费」就卡住了。这个标题背后其实是一套典型的 Java Web 业务系统:用户管理、电表台账、抄表记录、阶梯电价计算、缴费流水、统计报表。它适合计算机、软件工程、电子信息工程等专业的本科毕业生,也适合刚学完 Java 基础、想找一个完整项目练手的初级开发者。真正要解决的不是「怎么把压缩包跑起来」,而是「怎么在有限时间里把业务逻辑讲清楚、把数据一致性守住、把答辩问题接住」。下面按落地顺序拆开讲。
2. 先定技术栈再动手:Spring Boot + MyBatis-Plus 的最小可运行骨架
2.1 为什么这套组合是毕业设计的稳妥选择
毕业设计的时间通常只有 8 到 12 周,其中还要写论文、做答辩 PPT。技术选型的第一原则不是「先进」,而是「资料多、报错能搜到、导师看得懂」。Spring Boot 把 Tomcat 内嵌、自动配置、起步依赖都封装好了,MyBatis-Plus 在 MyBatis 基础上提供了单表 CRUD 的默认实现,省掉大量 XML 映射文件。常见做法是:Spring Boot 2.7.x 或 3.x 搭配 MyBatis-Plus 3.5.x,MySQL 8.0,前端用 Thymeleaf 或 Vue 加 Element UI。如果导师明确要求用 JSP,那就用 JSP,不要为了「技术新」去赌答辩风险。
这里有一个容易被忽略的点:热词里出现「mybatisplus根据java实体类生成创建表的sql语句」,说明很多人希望减少手写建表语句的工作量。MyBatis-Plus 本身不直接生成 DDL,但可以通过读取实体类的@TableName、@TableField注解,配合自定义代码生成器或简单的反射工具输出建表 SQL。毕业设计里可以写一个TableSqlGenerator工具类,在测试阶段跑一次,把生成的 SQL 贴进schema.sql,既省时间又能在论文里写「基于元数据的建表脚本自动生成」。
2.2 从零搭建可运行骨架的四个步骤
第一步,创建 Spring Boot 项目。用 IDEA 的 Spring Initializr,勾选 Spring Web、MyBatis-Plus、MySQL Driver、Lombok。如果网络环境导致依赖下载慢,在settings.xml里配置国内镜像,这是环境配置阶段最常见的卡点。
第二步,配置数据源。在application.yml里写清楚 JDBC URL、用户名、密码,并开启 MyBatis-Plus 的 SQL 日志,方便调试。
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/electric_bill?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 123456 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: id-type: auto logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0这段配置里,serverTimezone必须写,否则 MySQL 8 会报时区错误;logic-delete-field是逻辑删除,电费系统里用户和电表都不建议物理删除,保留历史数据对统计报表很重要。
第三步,写实体类和 Mapper。以电表为例:
@Data @TableName("t_meter") public class Meter { @TableId(type = IdType.AUTO) private Long id; private String meterNo; private Long userId; private BigDecimal initialReading; private Integer status; @TableLogic private Integer deleted; }@TableLogic配合全局配置,查询时自动追加deleted = 0,删除时变成update set deleted = 1。这个细节在答辩时如果被问到「删除用户后历史缴费记录怎么办」,可以回答「逻辑删除保留关联数据,报表统计不受影响」。
第四步,写一个 Controller 验证链路:
@RestController @RequestMapping("/meter") public class MeterController { @Autowired private MeterMapper meterMapper; @GetMapping("/list") public List<Meter> list() { return meterMapper.selectList(null); } }启动后访问http://localhost:8080/meter/list,能看到 JSON 数据,说明 Spring Boot、MyBatis-Plus、MySQL 三层已经打通。这一步跑通之前,不要急着写业务逻辑,否则报错时很难定位是配置问题还是代码问题。
3. 电费计算与抄表业务:阶梯电价、并发抄表和账单生成
3.1 阶梯电价的计算模型怎么落到代码里
电费管理系统的核心是计费。居民阶梯电价通常按月分档:第一档 0 到 180 度,单价 0.5 元;第二档 181 到 280 度,单价 0.55 元;第三档 280 度以上,单价 0.8 元。注意第二档和第三档的单价通常是对超出部分加价,不是全量加价。常见做法是把计费规则配置在数据库表t_price_rule里,而不是硬编码在 Java 中,这样答辩时可以说「规则可配置,适应不同地区政策调整」。
public BigDecimal calculateFee(BigDecimal usage, List<PriceRule> rules) { BigDecimal total = BigDecimal.ZERO; BigDecimal remaining = usage; for (PriceRule rule : rules) { if (remaining.compareTo(BigDecimal.ZERO) <= 0) break; BigDecimal upper = rule.getUpperLimit(); BigDecimal inThisTier = remaining.min(upper.subtract(rule.getLowerLimit())); total = total.add(inThisTier.multiply(rule.getUnitPrice())); remaining = remaining.subtract(inThisTier); } return total.setScale(2, RoundingMode.HALF_UP); }参数说明:usage是本月用电量,由本次抄表读数减去上次抄表读数得到;rules按档位顺序排列;setScale(2)保留两位小数,金额计算必须用BigDecimal,用double会出现 0.1 + 0.2 不等于 0.3 的经典问题,答辩时被问到就是送分题。
3.2 抄表记录的并发安全与账单生成
抄表员在移动端提交读数时,可能同时有多个人操作同一块电表,或者网络重试导致重复提交。如果直接insert抄表记录再更新电表读数,会出现重复计费。血泪经验是:在t_meter表上加一个version字段做乐观锁,或者用update t_meter set last_reading = #{newReading}, version = version + 1 where id = #{id} and version = #{oldVersion},根据影响行数判断是否成功。
@Transactional(rollbackFor = Exception.class) public void submitReading(Long meterId, BigDecimal newReading, Integer oldVersion) { Meter meter = meterMapper.selectById(meterId); if (meter == null) throw new BizException("电表不存在"); if (newReading.compareTo(meter.getLastReading()) < 0) { throw new BizException("本次读数不能小于上次读数"); } int rows = meterMapper.updateReadingWithVersion(meterId, newReading, oldVersion); if (rows == 0) throw new BizException("数据已被他人修改,请刷新后重试"); Reading reading = new Reading(); reading.setMeterId(meterId); reading.setReadingValue(newReading); reading.setReadingTime(LocalDateTime.now()); readingMapper.insert(reading); billService.generateBill(meterId, newReading.subtract(meter.getLastReading())); }逻辑说明:先校验读数不能回退,再用乐观锁更新电表当前读数,更新成功后插入抄表记录,最后生成账单。@Transactional保证三步要么全成功要么全回滚。如果并发冲突,前端提示用户刷新,而不是静默失败。这个设计在答辩时能直接回应「怎么保证数据一致性」的问题。
账单生成时要注意:同一个抄表周期内不能重复生成账单。可以在t_bill表上加唯一索引(meter_id, period),插入冲突时捕获异常并提示「本期账单已生成」。这比先查后插更可靠,因为查和插之间仍有并发窗口。
4. 避坑与排查:毕业设计电费系统最常见的五个翻车点
4.1 数据库连不上,报 Public Key Retrieval is not allowed
现象:启动时抛com.mysql.cj.jdbc.exceptions.CommunicationsException,提示Public Key Retrieval is not allowed。原因:MySQL 8 默认使用caching_sha2_password认证插件,JDBC 连接串没有允许公钥检索。解决:在 URL 后加allowPublicKeyRetrieval=true&useSSL=false,仅限本地开发环境,生产环境应配置 SSL 证书。
4.2 前端页面 404,静态资源加载失败
现象:Controller 能返回 JSON,但访问index.html报 404,或者 CSS、JS 加载不出来。原因:Spring Boot 默认静态资源放在src/main/resources/static下,如果用了 Thymeleaf,模板要放在templates下;如果用了 Vue 打包,dist目录要放到static下并配置好路由。解决:先确认文件位置,再检查application.yml里有没有配spring.mvc.static-path-pattern和spring.web.resources.static-locations。常见误用是把 JSP 放在templates下,JSP 需要额外配prefix和suffix。
4.3 金额计算出现 0.30000000000000004
现象:电费合计和手工算的对不上,差几分钱。原因:用了double或float做金额运算。解决:所有金额字段用BigDecimal,数据库用decimal(10,2),Java 里用new BigDecimal("0.5")而不是new BigDecimal(0.5)。除法必须指定精度和舍入模式,否则BigDecimal也会抛ArithmeticException。
4.4 逻辑删除后唯一索引冲突
现象:删除了一个电表,再新增相同表号的电表,报唯一索引冲突。原因:逻辑删除只是把deleted置为 1,数据库里那条记录还在,唯一索引仍然生效。解决:把唯一索引改成(meter_no, deleted)联合唯一,或者删除时把meter_no改写成meter_no + '_del_' + id。前者更干净,但要注意deleted只有 0 和 1,同一表号最多只能有一条删除记录,如果反复删除同一表号仍会冲突,所以更稳妥的是后者。
4.5 答辩时被问「你的系统并发能力怎么样」
现象:演示时一切正常,答辩老师问「1000 个用户同时缴费会不会超卖」。原因:没有准备并发相关的回答。解决:不要吹牛说支持高并发,而是诚实回答「当前是单机部署,缴费接口用了数据库行锁和唯一索引防止重复扣款,如果要做高并发需要引入消息队列削峰和分布式锁」。然后可以补充「我在抄表模块用了乐观锁,这是我在这个项目里实际处理的并发场景」。诚实且有细节,比背八股文得分高。
5. 让系统经得起追问:用接口测试和 SQL 审计验证核心链路
5.1 用 Postman 或 curl 跑通缴费全流程
答辩前一定要自己把「新增用户 → 新增电表 → 抄表 → 生成账单 → 缴费 → 查询余额」这条链路完整跑一遍,并且用接口测试工具留下记录。下面是一组 curl 命令,可以直接复制到终端执行:
# 新增用户 curl -X POST http://localhost:8080/user/add \ -H "Content-Type: application/json" \ -d '{"name":"张三","phone":"13800000000","address":"1号楼101"}' # 新增电表,假设用户 id 返回为 1 curl -X POST http://localhost:8080/meter/add \ -H "Content-Type: application/json" \ -d '{"meterNo":"M2026001","userId":1,"initialReading":1000}' # 抄表,假设电表 id 为 1,上次读数 1000,本次 1200 curl -X POST http://localhost:8080/reading/submit \ -H "Content-Type: application/json" \ -d '{"meterId":1,"newReading":1200,"oldVersion":0}' # 查询账单 curl http://localhost:8080/bill/list?meterId=1 # 缴费 curl -X POST http://localhost:8080/payment/pay \ -H "Content-Type: application/json" \ -d '{"billId":1,"amount":110.00}'跑完后去数据库里核对:t_reading有没有多出一条,t_bill的金额是不是 200 度电按阶梯算出来的 110 元(180×0.5 + 20×0.55 = 90 + 11 = 101,这里只是示例,实际按你的规则算),t_meter的last_reading有没有更新成 1200。如果对不上,优先查事务有没有生效、@Transactional是不是加在 public 方法上、异常是不是被 catch 后没抛出。
5.2 用 SQL 审计确认没有全表扫描和重复数据
在 MySQL 里开慢查询日志,或者直接对核心表跑explain:
EXPLAIN SELECT * FROM t_bill WHERE meter_id = 1 AND period = '2026-03';如果type是ALL,说明没走索引,数据量大了会慢。给t_bill加(meter_id, period)联合索引。再查重复账单:
SELECT meter_id, period, COUNT(*) FROM t_bill GROUP BY meter_id, period HAVING COUNT(*) > 1;这条语句返回空,说明唯一约束或业务逻辑生效了。答辩时如果老师问「你怎么保证不重复计费」,可以直接把这个查询结果展示出来,比口头解释有说服力。
5.3 一个我踩过的坑:别在答辩前一天换技术栈
我见过也经历过,有人觉得 Thymeleaf 太老,答辩前三天换成 Vue 前后端分离,结果跨域、登录态、打包路径全出问题,最后演示时页面白屏。毕业设计的首要目标是顺利通过,不是技术炫技。如果时间充裕,可以在论文的「未来改进」里写「前端可升级为 Vue3 + Element Plus,后端提供 RESTful 接口」,但不要在交付前动主干。另一个习惯是:每完成一个模块就git commit一次,压缩包里的代码和数据库脚本分开存放,schema.sql和data.sql放在resources下,换一台电脑也能快速恢复环境。希望帮到你。
本文还有配套的精品资源,点击获取