资讯详情 智慧能耗管理系统后端源码解析:九大模块架构与核心链路实现
📅 2026/10/26 7:54:56
简介面向毕业设计或课程作业场景的智慧能耗管理系统后端源码包聚焦能耗数据采集、分析与智能优化适合具备一定Java/Spring基础并希望实践物联网与人工智能应用的计算机专业学生。包体共481个文件、约53.56MB以Java源码与XML配置为主另有JAR依赖、属性配置、YAML及部署相关文件整体覆盖业务逻辑、接口定义、数据库脚本、系统配置与容器化部署等完整后端工程结构。压缩包内按client、common、dao、interface、report等模块组织便于按分层架构理解项目与二次开发。目前已有67人学习下载适合作为毕业设计参考或课程项目实战素材通过研读源码可掌握API设计、数据持久化、服务分层及能耗预测等AI辅助功能的落地思路同时积累从需求分析到部署上线的完整项目经验。1. 智慧能耗管理系统后端这份 zip 里的九个模块到底答不答得完毕设拿到这份智慧能耗管理系统后端的 zip我第一反应不是解压而是先看文件列表。九个 .iml 后缀的模块文件整整齐齐躺在里面说明这是一个标准的 IDEA 多模块 Java 后端工程不是随手丢进来的散代码。对做毕设或者课程作业的人来说这种结构比单模块项目更接近真实的前后端分离项目实战common、dao、service、interface、web 分层明确还带着 report 和 work 这类业务模块。整套系统要解决的事听名字就能猜个大概——把能耗数据接进来、存下来、算清楚再给前端展示出去顺带用一点人工智能手段做趋势预测。无论你是想拆着学架构还是想把后端直接跑起来做演示这份源码都值得先花十分钟读懂它的模块地图。2. 从 .iml 反推项目架构九个模块各管哪一段依赖怎么理拿到陌生工程先读代码我习惯先扫一遍 .iml 文件。后缀 .iml 是 IntelliJ IDEA 的模块描述文件全称 IntelliJ Module。它不是业务源码但记录了模块名、模块类型、源目录、依赖关系这些关键信息。一个多模块 Java 工程长什么样不用跑代码看这几个文件就能还原出七八成。2.1 为什么 .iml 文件能暴露项目全貌每个 .iml 文件本质是 XML里面存着模块的元数据。我贴一段典型的片段你对照自己解压出来的文件看结构基本一致?xml version1.0 encodingUTF-8? module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModuletrue typeJAVA_MODULE version4 component nameNewModuleRootManager LANGUAGE_LEVELJAVA_1_8 content urlfile://$MODULE_DIR$ / orderEntry typesourceFolder forTestsfalse / orderEntry typemodule module-nameservice / /component /module这段是示意实际文件以你解压后为准。两个字段最关键一是LANGUAGE_LEVELJAVA_1_8直接告诉你编译语言级别是 Java 8后面配置 JDK 时不能乱用 17 或 21二是MavenProjectsManager.isMavenModuletrue说明这个工程是 Maven 管理的多模块项目而不是 Gradle——Gradle 多模块一般只保留 .gradle 文件不会给每个模块单独生成 .iml。想不打开 IDEA 就快速摸清模块依赖我一般直接跑一条命令grep -h orderEntry typemodule *.iml | sed s/.*module-name//;s/.*// | sort -u逻辑说明这条命令扫描当前目录下所有 .iml把引用其它模块的 orderEntry 行抓出来再用 sed 剥掉前后缀只留下module-name的值最后排序去重。跑完你就能得到一张「谁依赖谁」的简表比在 IDEA 里逐个点开模块设置快得多。参数上注意grep -h是去掉文件名前缀sort -u是去重如果你的文件不在同一目录先把它们拷到临时目录再跑。拿到这份依赖简表后再配合 IDEA 的导入步骤项目就跑起来了File → New → Project from Existing Sources选中工程根目录的 pom.xmlIDEA 会自动识别全部九个模块。如果你只选中某个 .iml 文件导入模块间的依赖引不到后面全是飘红报错。2.2 common / dao / service三层架构的模块化拆分先把三门基础课讲清楚这三个模块是整套系统的骨架。common 放的是工具类、常量、统一返回体 Result、自定义异常、分页参数dao 层放 MyBatis 的 Mapper 接口和 SQL XML或者 JPA 的 Repositoryservice 层放业务规则、事务边界、统计逻辑和预测逻辑。调用方向是单向的web/interface → service → daocommon 可以被任意层引用但 dao 绝不能反过来调用 service这是分层的底线。实际工程里代码会长这样// web 层接收前端请求不写业务逻辑 RestController RequestMapping(/api/energy) public class EnergyController { private final EnergyService energyService; public EnergyController(EnergyService energyService) { this.energyService energyService; } GetMapping(/daily) public ResultListDailyEnergyVO daily(RequestParam String date) { return Result.ok(energyService.getDailyReport(date)); } }逻辑说明Controller 只做两件事——接收参数、包装返回结果真正的统计逻辑在EnergyService里。参数用RequestParam String date表示按天查询格式建议yyyy-MM-ddController 不做格式校验格式合法性在 service 层统一校验这样多个入口调用 service 时校验逻辑不会重复。为什么强调构造器注入而不是在 Controller 里new一个 Service因为构造器注入能让你在单元测试时把 Service 替换成 mock 对象这是答辩时聊「可测试性」的加分点。dao 层对应的是 Mapper 接口加 XML你会在 dao 模块里看到类似EnergyRecordMapper.java和EnergyRecordMapper.xml成对出现的文件。XML 里只写 SQL不写业务判断连表查询和聚合都在这一层解决。2.3 web / interface / report接入层、接口层和报表层怎么分工新手最容易懵的是 web 和 interface 的区别。同一个工程里同时出现这两个模块说明设计者把「HTTP 接入」和「接口契约」分开了。web 模块放 Controller、拦截器、跨域配置、全局异常处理是真正对外提供 HTTP 服务的地方interface 模块放请求参数对象、响应对象、以及可能存在的 RPC 接口定义——比如 Dubbo 的接口或者模块间调用的 Feign 接口契约。怎么快速判断 interface 在工程里的真实角色看 web 模块里 Controller 的 import 列表如果 import 的全是 interface 包下的 Request/Response 对象说明 interface 是契约层前后端约定和模块间约定都沉淀在这如果 import 的是 service 的接口那 interface 只是业务接口定义层。两种设计都常见能在答辩时说清你选的是哪一种本身就是对架构意识的证明。report 模块专门处理报表。能耗系统里报表是重头戏日报、月报、区域汇总、分项占比全是给人看图表的。report 模块通常不直接暴露 HTTP 接口而是被 web 层调用或者被定时任务触发——注意这个「定时任务」大概率就在 work 模块里。2.4 ql / qlzh / work核心业务模块的命名规律ql 和 qlzh 这两个名字一看就是「擎联」「擎联智慧」的拼音缩写。q 系列模块承载的是业务域代码设备档案、计量点管理、能耗读数、分项统计。qlzh 大概率是核心业务域ql 是基础数据域比如组织架构和字典配置。work 模块对应工作台或定时任务每天凌晨跑统计、跑预测都归它管。命名规律说明开发者在拆模块时用的是「业务域」思维而不是单纯的技术分层——既有 common/dao/service 这种技术层模块又有 qlzh/report/work 这种业务域模块混合分层在真实项目里很常见这也是这份资源值得学的地方。按文件名反推职责地图大概是这样的模块文件职责推测典型内容common.iml公共基础设施工具类、常量、统一返回体、全局异常dao.iml数据访问层Mapper 接口、SQL XML、实体类service.iml业务逻辑层事务逻辑、统计逻辑、预测逻辑web.imlHTTP 入口Controller、拦截器、跨域配置interface.iml接口契约层DTO/VO、RPC 接口定义report.iml报表域聚合 SQL、报表模板ql.iml / qlzh.iml核心业务域设备、能耗读数、分项计量work.iml任务/工作台定时任务、调度、待办这个表只是依据文件名做的推测不要当成文档用但按这个地图去读代码能少走不少弯路。我拆这类工程有个习惯把表格贴在 IDE 旁边每读完一个包的代码就回去核对更新一次职责描述读到一半发现职责地图与实际不符那往往就是代码里有彩蛋或坏味道的地方。读代码的顺序建议先读 common 的 Result 和异常定义知道接口返回格式长什么样再读 interface 的 DTO知道前端传什么、后端回什么接着读 dao 的表结构知道数据从哪来最后读 service 和 web把调用链串起来。按这个顺序走三十分钟能把这套工程的骨架摸清楚。3. 能耗数据从采集到预测一套数据模型与两条核心链路模块结构看完了接下来要回答的是「这套系统到底在做什么」。能耗管理系统的后端核心就是两条链路采集链路——设备把读数上报进来分析链路——把明细数据聚合成报表再喂给预测模块。下面从数据模型讲到具体实现。3.1 能耗数据模型设备、计量点、读数、账单的关系数据模型是最先要从抽象里想清楚的事。能耗场景里设备是物理载体一台空调、一块电表计量点是采集单位一块表可以有几个计量点读数是最小记录某个时刻的用量报表是聚合结果。最小化设计四张表就能撑起整个系统核心两张如下-- 设备表物理设备注册信息 CREATE TABLE device_info ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 主键, device_no VARCHAR(32) NOT NULL COMMENT 设备编号通常由厂商下发, device_name VARCHAR(128) NOT NULL COMMENT 设备名称, location VARCHAR(128) COMMENT 安装位置如 A 栋 3 层, status TINYINT NOT NULL DEFAULT 1 COMMENT 1 启用0 停用, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_device_no (device_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT设备档案表; -- 能耗读数表每次采集产生的明细数据 CREATE TABLE energy_record ( id BIGINT NOT NULL AUTO_INCREMENT, device_id BIGINT NOT NULL COMMENT 关联 device_info.id, collect_time DATETIME NOT NULL COMMENT 采集时间业务时间而非入库时间, energy_type TINYINT NOT NULL COMMENT 1 电2 水3 气, energy_value DECIMAL(12,4) NOT NULL COMMENT 本次读数累计值或增量值由采集协议约定, unit VARCHAR(8) NOT NULL DEFAULT kWh, PRIMARY KEY (id), KEY idx_device_time (device_id, collect_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT能耗采集明细表;参数说明energy_record是整个系统的数据地基collect_time必须用业务采集时间而不是数据库入库时间——否则设备断网后补报数据你会按错误时间去聚合报表直接错位。energy_value的单位和口径要在采集协议里写死累计值还是增量值必须统一混用了后面所有统计全部翻车。索引idx_device_time支撑的是「按设备按时间段」查询这是报表模块最高频的查询模式。报表统计结果一般再落一张energy_stat表字段就是device_id stat_date energy_type total_value主键加唯一键(device_id, stat_date, energy_type)。有没有这张表决定了报表查询是秒开还是几十秒。3.2 数据接入采集接口的设计与校验逻辑数据怎么进来常见做法是设备侧通过上报接口推数据后端暴露一个接收接口。这个接口只做接收和结构校验业务加工全扔给 service。用 Spring Boot 写一个采集接收接口骨架长这样RestController RequestMapping(/api/collect) public class CollectController { private final EnergyRecordService recordService; public CollectController(EnergyRecordService recordService) { this.recordService recordService; } /** * 设备上报单个读数 * body 示例{deviceNo:MTR-001,collectTime:2025-06-01 12:00:00, * energyType:1,value:10086.5} */ PostMapping(/record) public ResultVoid record(RequestBody CollectRequest req) { recordService.saveRecord(req); return Result.ok(); } }参数说明CollectRequest里四个核心字段——deviceNo设备编号、collectTime业务采集时间、energyType能源类型、value读数。接收端只做字段非空、时间格式、value 范围这类结构校验设备是否存在、时间是否合理、是否重复上报这些业务校验放 service。这样设计的理由采集接口会被设备高频调用校验逻辑必须快业务校验放在 service 层可以复用给其它入口。真正要小心的是幂等。设备断网恢复后会把积压的数据重新上报一遍同一个deviceNo collectTime energyType可能被推送多次。我一般会在energy_record表上直接加唯一键(device_no, collect_time, energy_type)然后在saveRecord里捕获DuplicateKeyException后直接返回成功。这样设备重试不会污染统计数据也不用引入额外缓存。这个坑在毕设答辩时主动讲出来比背十页文档都有说服力。3.3 统计报表按日/月/区域聚合的 SQL 思路能耗报表的核心是聚合第一反应就是GROUP BY但有两个口径问题要先想清楚如果读数是增量值直接SUM没问题如果是累计值得用相邻记录差分或者「区间最大值减最小值」这个口径必须和采集协议对齐。日报表典型写法select idsumDailyByDevice resultTypecom.qlzh.report.dto.DailyStatDTO SELECT t.device_id AS deviceId, t.stat_date AS statDate, t.energy_type AS energyType, SUM(t.energy_value) AS totalValue FROM ( SELECT device_id, DATE_FORMAT(collect_time, %Y-%m-%d) AS stat_date, energy_type, energy_value FROM energy_record WHERE collect_time BETWEEN #{beginTime} AND #{endTime} ) t GROUP BY t.device_id, t.stat_date, t.energy_type ORDER BY t.device_id, t.stat_date /select逻辑说明内层子查询先按时间范围把数据卡住再用DATE_FORMAT把时间泛化成天外层GROUP BY按设备按天按能源类型聚合。三个分组字段支撑「哪个设备、哪天、什么能源用了多少」这个最小报表粒度。参数beginTime和endTime是查询区间我一般会在 service 层把时间边界按自然日算好再传进来而不是在 SQL 里对collect_time做函数处理——否则字段上的索引就失效了。提示数据量上来之后别每次报表都扫明细表。常见做法是每天凌晨用定时任务——就是你工程里的 work 模块——把前一天的聚合结果写进energy_stat统计表报表查询只查统计表明细表只保留近 90 天。答辩时主动说出这个设计比堆 CRUD 有说服力得多。3.4 人工智能预测模块时序数据怎么落到 service摘要里挂着「人工智能」标签落到后端代码里大概率是两件事一是规则引擎做节能策略二是时序模型做能耗趋势预测。毕设场景我不会建议上深度学习数据量不够、训练时间也不允许。常见做法是用移动平均或线性回归做未来几小时、几天的预测效果够用也好解释。public class EnergyPredictor { /** * 基于最近 N 个历史点做移动平均预测 * param history 历史值列表按时间升序 * param window 取最近几个点建议按业务周期设置 * return 下一个时间点的预测值 */ public double predictNext(ListDouble history, int window) { if (history null || history.isEmpty()) { return 0d; } int size history.size(); int start Math.max(0, size - window); double sum 0d; for (int i start; i size; i) { sum history.get(i); } return sum / (size - start); } }参数说明history是某设备某能源类型最近一段时间的序列window是窗口大小取值 7 表示取最近 7 个点平均。如果数据是按小时采集想预测工作日的用电高峰窗口可以改成「过去 7 天同一时刻的值」——把history预先过滤成同一时刻的序列再传入预测含义就变成了周期叠加。移动平均是最稳的兜底方案换线性回归也简单用最小二乘拟合斜率再外推趋势型曲线会更贴合。预测结果往哪用两个地方一是给报表模块的「未来 24 小时能耗趋势」曲线图用二是给规则引擎做阈值预判——预测值超过设定阈值就触发告警。这就是「人工智能」在后端最常见的落地姿态不炫模型把预测结果接进业务链路让告警和报表都活起来。我习惯把预测结果缓存起来设 30 分钟过期预测计算本身轻但前端每次刷新图表都重新拉历史数据算一遍就没必要了。4. 本地复现一个能跑的后端JDK/Maven/MySQL 版本选择与三步启动代码看懂了接下来是把它跑起来。这类毕设后端工程环境版本踩的坑往往比代码本身多。老工程不是越新的环境越好先按稳妥组合跑通再考虑升级。4.1 环境清单JDK、Maven、MySQL 的版本选择先给一张我实测过比较稳的组合表照着装能省掉一半玄学问题组件推荐版本选择理由JDK1.88u202 及以上老毕设工程最常见的编译级别高版本 JDK 容易触发 Lombok、cglib 兼容问题Maven3.6.3 ~ 3.8.8太老的 Maven 拉不动较新依赖太新的对老 pom 有兼容告警MySQL5.7 或 8.0.365.7 兼容性最好8.0 要注意时区和认证插件问题IDEA2021.3 及以上对 Maven 多模块工程支持稳定Redis2.6 以上可选代码里有缓存注解才需要没有就跳过这张表的逻辑是「向老版本靠」。课程作业和毕设工程大多是两到三年前写的依赖版本普遍不高直接用 JDK 17 编译经常会遇到javax.*包名迁移和 Lombok 版本不兼容这类问题。先按 1.8 跑通再升级顺序不要反。装完环境先做三连确认java -version # 确认输出是 1.8.0_xxx而不是 17 或 21 mvn -v # 确认 Maven 版本在 3.6 以上 mysql --version # 确认 MySQL 可执行再确认服务已启动参数说明java -version重点看 major version如果显示 17 或 21要么换 JDK要么进 IDEA 的 Project Structure 把 Language Level 调回 8。mvn -v还要看它读取的settings.xml路径确认本地仓库位置和你 IDEA 里配置的一致否则命令行能编译、IDEA 里却飘红就是两边仓库没对上。4.2 client.conf 与配置文件从配置到数据库初始化压缩包里有个client.conf文件按名字判断它管的是设备接入侧或采集客户端的参数——服务器地址、上报周期、连接超时、密钥。这类配置文件在毕设工程里两种用法都有一是给后端集成测试用二是给模拟采集端用。我更推荐第二种演示时让「模拟设备」按这个配置去上报数据没有真硬件也能让系统完整流转起来。典型内容如下具体以你解压后的实际文件为准# 采集客户端连接后端的配置 server.host127.0.0.1 server.port8080 # 上报间隔单位秒 collect.interval30 # 模拟设备编号与能源类型 device.noMTR-001 energy.type1参数说明server.host和server.port指向后端服务的地址collect.interval是模拟采集端的上报间隔。演示时我会把 interval 调成 10 秒前端曲线刷新快、观感好正式验证时调回 300 秒减轻数据库写入压力。device.no和energy.type控制在演示里造哪一类数据。数据库初始化靠自己找 SQL 脚本。工程里数据库脚本一般放在db/或doc/目录下找到后执行mysql -uroot -p db/init.sql # 导入表结构 mysql -uroot -p db/data.sql # 导入基础数据设备、区域、用户说明两条命令分别导入表结构和基础数据。data.sql里通常是设备档案、区域字典、用户账号这类「没有业务也能跑起来」的初始化数据。如果工程里建库语句用了utf8mb4而连接串用的是utf8导入后中文可能乱码检查点放在字符集。4.3 启动顺序与接口自测多模块工程的启动顺序一条铁律先基础设施再后端服务。MySQL 先起来Redis 有也先起来然后才是后端 jar。首次构建建议跳过测试毕设工程的测试用例经常依赖测试库或特定环境直接跑会被环境卡住不是代码有问题。# 工程根目录执行跳过测试打包 mvn clean package -DskipTests # 查看各模块打出的 jar ls */target/*.jar # 启动 web 模块jar 名以 target 下实际文件为准 java -jar web/target/web-1.0.0.jar --spring.profiles.activedev参数说明-DskipTests表示只跳过测试执行但会编译测试代码如果想连测试代码编译都跳过用-Dmaven.test.skiptrue。启动参数--spring.profiles.activedev会加载application-dev.yml里的配置数据源、端口、日志级别都从那里读。如果启动日志报「Failed to configure a DataSource」八成是数据库连接串或账号密码不对先别查代码先查配置。启动成功后不要急着开前端先用 curl 做接口自测# 健康检查接口路径以工程实际为准 curl -X GET http://127.0.0.1:8080/api/health # 登录接口确认数据库连接和用户表正常 curl -X POST http://127.0.0.1:8080/api/auth/login \ -H Content-Type: application/json \ -d {username:admin,password:123456}逻辑说明健康检查确认进程起来了、能响应 HTTP登录接口确认数据库连接正常、用户表有数据。健康检查通但登录 500优先查数据库连接串和账号权限这是两个最常见的翻车点。登录返回的 token 记下来后续接口在 header 里带Authorization: Bearer token具体鉴权方式看工程里的拦截器实现。前端联调时记得确认后端跨域配置允许本地开发地址否则前后端分离的项目浏览器里会被 CORS 拦死。这个问题频率太高下章专门展开。5. 复现路上的避坑记录四条血泪经验从导入报错到接口不通以下四个问题是我在复现同类型毕设后端时踩过、也帮别人排过的每条按「现象 → 原因 → 解决」写希望能帮你绕开。5.1 IDEA 导入后模块飘红Maven 依赖没进来现象IDEA 导入根 pom.xml 后项目树里每个模块都标红波浪线提示「Cannot resolve symbol」或找不到SpringBootApplication。原因最常见两种——本地 Maven 仓库没有 pom 里声明的依赖版本IDEA 不会自动帮你全量拉取或者 Maven 配置的镜像仓库失效依赖下到一半就断了。IDEA 默认的 Maven 配置还容易和你命令行装的 Maven 指向不同的settings.xml依赖就「一会儿有一会儿没有」。解决先在命令行把依赖拉下来mvn dependency:resolve -DskipTests然后回到 IDEA右键根 pom → Maven → Reload Project旧版本叫 Reimport。如果还是飘红检查 IDEA 的 Maven 设置里 User settings file 是否指向你实际使用的settings.xml跟命令行保持一致。这条处理完百分之八十的飘红能消掉。5.2 MySQL 8 时区导致时间错乱日期差 8 小时现象接口返回的统计日期比实际晚 8 小时或者按天查询查不到当天数据只有下午以后的数据能对上。原因MySQL 8 默认时区跟 Java 进程时区不一致JDBC 连接串里又没指定serverTimezone。Java 侧按本地时区 UTC8 传参数据库侧按 UTC 存储或返回两头一对就偏了 8 小时。这个偏移跟DATETIME类型无关纯粹是连接串设置问题。解决在数据源连接串里显式指定时区jdbc:mysql://127.0.0.1:3306/energy?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse说明serverTimezoneAsia/Shanghai是关键参数必须显式写明。顺带一个隐藏坑如果 MySQL 是 5.7 且系统时区是 UTC同样偏移也会出现解法一致。改完连接串记得重启后端进程只改配置文件不重启是没用的。5.3 接口 404 或跨域拦截前后端分离联调的老翻车现象后端起了curl 能通但前端页面的请求全部失败。控制台要么报跨域 CORS要么前端请求/api/xxx后端路由是/xxx全部落到 404。原因两个独立问题。跨域是因为前端地址比如localhost:5173与后端地址localhost:8080不同源后端没配 CORS 过滤器浏览器直接拦截响应404 是因为前端没把/api前缀代理到后端或者后端设置了 context-path 和前端约定不一致。解决跨域在 web 模块加一个全局配置类Configuration public class CorsConfig { Bean public CorsFilter corsFilter() { CorsConfiguration config new CorsConfiguration(); config.addAllowedOriginPattern(*); // 开发环境放开生产必须改成具体域名 config.addAllowedMethod(*); config.addAllowedHeader(*); UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration(/**, config); return new CorsFilter(source); } }参数说明addAllowedOriginPattern比addAllowedOrigin宽容支持带端口的具体域名生产环境把*改成https://your-domain不要图省事。类里的 import 按你工程包名补全。404 那个问题去前端工程里看vite.config.ts或vue.config.js的 proxy 配置把/api前缀代理到 8080 端口如果后端设置了 context-path比如/energyproxy 里的路径要不就带全、要不就统一去掉前后必须一致。5.4 报表数据为空SQL 聚合分组字段不一致现象分项统计接口返回成功但totalValue全是 0或者某些设备在报表里查不到数据前端折线图一条线也没有。原因最常见是两个——energy_type字段在表里是数字1、2、3查询条件里传的是字符串1MySQL 隐式转换有时能通、有时对不上另一种更隐蔽采集端上报的是设备编号device_no报表查询用的却是主键device_id两套标识对不上数据自然查不出来。解决先把一条明细捞出来核对SELECT id, device_id, energy_type, energy_value, collect_time FROM energy_record ORDER BY id DESC LIMIT 5;说明先看采集数据有没有进库。有数据再看统计查询条件重点核对device_id传的是主键还是编号。我的习惯是查询接口只接收device_no在 service 层先转成device_id再查报表层和采集层永远用一套标识体系。这个习惯从那以后一直保留着能避免大量「数据明明有却查不到」的尴尬。6. 毕设演示的加分技巧给后端造一批像样的能耗数据后端能跑只是及格演示有说服力才是加分。我见过太多毕设演示翻车不是系统崩了而是数据太假——一条水平直线看不出任何波动预测模块拿什么预测正确的做法是先造一批「看着像真的」能耗数据工作日白天高、凌晨低、周末低、整体有缓慢趋势、带随机噪声。这套数据既能让报表曲线有峰有谷又让预测模块有规律可循。我习惯用独立脚本造数不污染业务代码直接写进 MySQLimport random import datetime import pymysql conn pymysql.connect(host127.0.0.1, userroot, password123456, databaseenergy, charsetutf8mb4) cur conn.cursor() device_id 1 # 假设设备主键是 1 start datetime.datetime(2025, 5, 1) for i in range(24 * 90): # 90 天每小时一条 ts start datetime.timedelta(hoursi) hour ts.hour # 工作日白天高、夜间低周末整体偏低 base 50 if (ts.weekday() 5 and 8 hour 20) else 20 # 叠加随机噪声和缓慢上升趋势 value base random.uniform(-5, 5) (i / 1000) cur.execute( INSERT INTO energy_record(device_id, collect_time, energy_type, energy_value, unit) VALUES(%s, %s, %s, %s, %s), (device_id, ts, 1, round(value, 2), kWh), ) conn.commit() cur.close() conn.close()参数说明脚本造 90 天、每小时一条的用电数据。工作日 8 点到 20 点基线 50夜间基线 20random.uniform(-5, 5)模拟测量噪声(i / 1000)模拟缓慢上升的能耗趋势。三个参数按需调24 * 90改数据量基线值改负载形态噪声幅度改曲线毛刺程度。如果设备不止一台外层再加个device_id循环。验证方法造完数先查统计接口确认聚合值有波动再看告警阈值会被触发——把阈值设成 60没有噪声数据告警永远不会触发演示就没有互动性加了噪声后偶尔触发的告警反而让答辩节奏更自然。从那以后我每次拿到别人的毕设后端都先不急着跑业务功能而是先把造数脚本写好让演示数据能讲故事再对着故事去演示模块。这份 zip 里的后端如果跑通了也建议你照这个顺序走一遍——先造数再跑接口最后录演示。希望帮到你。本文还有配套的精品资源点击获取