简介:这份毕业设计资料包围绕微信小程序端的消防隐患在线举报流程,提供前后端完整工程、项目说明文档与答辩PPT,适合计算机、软件、电子信息等专业用作课程设计、期末大作业或毕设参考。压缩包共823个文件,大小约18.54MB,以png、svg图片素材,vue、js、wxml、wxss小程序与前端代码,以及java、xml后台接口和SQL数据库脚本为主,目录结构清晰,便于按模块查看与二次开发。目前已有106人学习下载。除源码外,还附带项目说明、数据库文件、PPT演示文档和运行相关批处理脚本,有助于快速理解系统设计思路、部署方式及功能模块划分。若需在此基础上扩展功能,需要具备一定代码阅读与调试能力,适合作为进一步学习微信小程序开发、后端联调和系统设计的参考资料。
1. 微信小程序如何撑起消防隐患举报:一套毕业设计包的完整画像
基于微信小程序的消防隐患在线举报系统,一句话概括就是:居民在楼道电动车充电、消防通道被杂物堵住、灭火器过期这类场景下,掏出手机拍照上传,物业和管理部门在后台接单处理,举报人能实时看到进度。这类选题在毕业设计里出现频率很高,因为它链路短、角色清晰,能同时检验数据库设计、接口开发和移动端实现。压缩包里附带源码、项目说明、数据库和答辩 PPT,文件看起来齐全,但真正决定成绩的是你能把系统跑通、讲清楚、经得起追问——这恰恰是大部分同学拿到包之后卡住的地方。下文我会按数据设计、接口实现、部署排错的顺序,把这条路上能让你少熬夜的细节都过一遍。
2. 系统架构与数据流转:举报单从上报到闭环的核心链路
拿到这套系统的源码,先别急着跑起来,第一件事是把它的角色边界和数据流向理清楚。消防隐患举报不是简单的「用户发一条记录」,它天然带着审批和处理流:居民提交、管理人员受理、处理人员办结,每一步都会改变举报单的状态,也会触发不同的权限控制。如果不先把这个链路想明白,后面写接口和调页面时会反复返工。
2.1 三种角色与页面结构:用户、管理员与处理人员的边界
我见过不少同学把系统设计成只有用户端一个入口,管理员硬塞进同一个小程序里,结果角色判断写得乱七八糟。常见做法是拆成三个角色,每类角色只看到自己该看的东西:
普通用户(举报人)走小程序端,页面包括授权登录、举报填写页、我的举报列表、举报详情页。举报填写页要有隐患分类选择、位置描述、最多三张现场照片、提交按钮。用户不需要看到处理人员的内部备注,所以详情页只展示状态和最终处理结果。
管理员走管理后台,一般是独立 Web 端页面,功能是查看全部举报单、按状态筛选、受理或驳回、把任务指派给处理人员、查看基础统计。管理员的权限范围是「分配和裁决」,不负责具体处置。
处理人员是执行层,看到的是分配给自己的待处理清单,能把自己的任务从「处理中」推进到「已办结」,并填写处理说明。三者的关系后端用 user 表里的 role 字段区分,0 普通用户、1 管理员、2 处理人员,接口层按角色拦截,前端隐藏按钮只是锦上添花,不能作为唯一防线。
页面数量不多,小程序端控制在 6 到 8 个页面,管理端控制在 4 到 5 个页面,这套规模对毕业设计来说刚好能展示完整度,又不至于让自己陷在页面堆里出不来。
2.2 举报单状态机:从待受理到已办结的流转规则
状态设计是整个系统的骨架。我在给别的同学排查代码时发现,最常见的毛病是状态字段随便填,比如直接用 String 存「未处理」「处理中」「完成」,一旦拼写不一致,列表页就查不出数据。常见做法是用 TINYINT 存数字状态:0 待受理、1 已受理、2 处理中、3 已办结、4 已驳回。
流转规则要定清楚:用户提交后进入 0;管理员受理后变为 1;处理人员开始处置变为 2;处置完成写结论后变为 3。驳回是管理员在 0 或 1 状态下给出的特殊出口,驳回时必须要填理由,理由写入处理记录表而不是只改状态,否则用户看到状态却不知道为什么被驳。
这里有一个容易被忽略的边界:用户提交之后能不能自己撤销?我的建议是不做。举报属于留痕行为,一旦提交就进入管理视野,允许随意撤销会带来审计问题,也容易让答辩老师追问「如何防止恶意举报」时你答不上来。页面和应用层不做撤销入口,后端也不提供对应接口,逻辑上最干净。
状态字段直接放在举报主表里,不单独建状态历史表。毕业设计的数据量级几百条到几千条,单独建一张状态流转表属于过度设计,反而把简单逻辑搞复杂。只需要记录当前状态和最近一次变更时间,配合处理记录表就能还原完整过程。
2.3 技术栈选型与数据流向:小程序端、后端服务与数据库的分工
小程序端选型上,常见方案是原生 WXML + WXSS + JavaScript 与 uni-app 二选一。我的建议是:如果只做微信小程序,用原生;如果老师要求顺便支持支付宝或 App 端,再用 uni-app。原因是这套系统页面不超过十个,原生写法直白,加载快,调试工具对原生支持也最稳。为「以后可能多端」这种假想需求引入框架,会增加很多没必要的编译问题,答辩时还容易被问「你解决了什么 uni-app 解决不了的问题」。
后端常见做法是 Spring Boot 或 Node.js 二选一。有 Java 基础选 Spring Boot + MyBatis,理由是和 MySQL 配合成熟,答辩时聊依赖注入、拦截器、事务都有话说;非 Java 方向的选 Node.js + Express,开发速度快,代码量更小。数据库统一用 MySQL,别用 SQLite 或 H2 做本地测试然后提交 MySQL 脚本,两套数据库的建表语法和函数差异会让你的 SQL 在老师那边跑不起来。
数据流向是整个系统最核心的一条线:小程序端 wx.request 调后端接口,后端 Controller 接收并校验参数,Service 层处理业务逻辑,Mapper 层操作 MySQL,处理完把结果包装成统一结构返回给小程序端。图片是例外,它是先通过 wx.uploadFile 上传到服务器拿到 URL,再跟表单数据一起提交——先传图后提交,这个顺序是硬性的,否则会出现表单写进数据库了图片还没传完的错乱状态。
3. 数据库设计与核心表结构:从建表 SQL 看懂六张表的职责
数据库是这套系统最值钱的交付物,因为接口和小程序页面都能重写,唯独数据模型反映你有没有完整理解业务。这个系统的数据库设计其实不难,难的是别漏表、别用错字段类型、别把关联关系写得让人看不懂。我按常见的六张表来拆:用户表、隐患分类表、举报主表、举报图片表、处理记录表、通知消息表。
3.1 六张核心表的职责划分:从举报主表到处理记录
user 表管登录账号和角色,字段不需要多,openid 是微信侧唯一标识,nickname 和 avatar 用于展示,role 决定权限。隐患分类表是字典表,装「电动车违规充电」「堵塞消防通道」「消防设施损坏」「其他」这些固定的几类,用表存而不是前端写死,是为了后台以后加分类不用发版。举报主表 report 是核心,所有业务围绕它转,字段覆盖标题、描述、位置、经纬度、状态、提交时间、办结时间。举报图片表是独立的从表,因为一个举报对应一到三张图片,拆开存便于按图管理,也避免主表字段过长。处理记录表记录每一次状态变更,谁在什么时间做了什么操作、批注了什么,这是答辩时展示系统严谨性的利器。通知消息表是加分项,用来存系统消息或订阅消息的发送记录。
3.2 举报主表与图片表的建表语句:字段类型与索引的选择
直接给建表语句,我一般会用下面这套方案,覆盖了最常见的业务字段和索引设计,你拿来改改就能用:
CREATE TABLE `report` ( `report_id` INT NOT NULL AUTO_INCREMENT COMMENT '举报单主键', `report_no` VARCHAR(32) NOT NULL COMMENT '举报单编号,格式 R+年月日+三位序号', `user_id` INT NOT NULL COMMENT '举报人用户ID,关联 user 表', `category_id` INT NOT NULL COMMENT '隐患分类ID,关联 category 表', `title` VARCHAR(100) NOT NULL COMMENT '隐患标题,如:楼道电动车违规充电', `description` TEXT COMMENT '隐患描述,限制 500 字以内', `location` VARCHAR(255) NOT NULL COMMENT '隐患位置描述,如:XX 小区 3 栋 2 单元 5 楼', `longitude` DECIMAL(10, 7) COMMENT '经度,GCJ-02 坐标系', `latitude` DECIMAL(9, 7) COMMENT '纬度,GCJ-02 坐标系', `status` TINYINT NOT NULL DEFAULT 0 COMMENT '状态:0待受理 1已受理 2处理中 3已办结 4已驳回', `reject_reason` VARCHAR(255) COMMENT '驳回理由,状态为4时必填', `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '提交时间', `handle_time` DATETIME COMMENT '办结时间', PRIMARY KEY (`report_id`), UNIQUE KEY `uk_report_no` (`report_no`), KEY `idx_status_create` (`status`, `create_time`), KEY `idx_user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='消防隐患举报单主表';report_no 用「R + 年月日 + 三位序号」而不是自增 ID,比如 R20250601001,这样自带可读性,老师一看就知道你考虑过编号规范。联合索引 idx_status_create 支撑管理后台最常见的「按状态筛选并按时间排序」查询,这是高频查询路径,不建索引的话数据量上来后会慢。经纬度用 DECIMAL(10,7) 和 DECIMAL(9,7) 而不是 DOUBLE,因为 DOUBLE 的精度在小数点后对地图坐标来说超配且计算不稳,DECIMAL 在这个场景够用且可控。
图片表的设计要避免一个典型错误:有人把图片地址直接拼在 report 表的一个字段里用逗号分隔,看起来省事,实际上对「统计某类举报平均上传了几张图」这类问题完全无能为力。独立建表才能按报告联查图片,也能单独管理图片的删除和访问状态,下面给建表语句:
CREATE TABLE `report_image` ( `image_id` INT NOT NULL AUTO_INCREMENT COMMENT '图片主键', `report_id` INT NOT NULL COMMENT '举报单ID,关联 report 表', `image_url` VARCHAR(255) NOT NULL COMMENT '图片访问路径,存相对路径不存完整域名', `sort_no` TINYINT NOT NULL DEFAULT 1 COMMENT '排序号,1代表第一张', `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '上传时间', PRIMARY KEY (`image_id`), KEY `idx_report_id` (`report_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='举报图片表';image_url 存相对路径而不是完整 URL,是因为部署环境可能换域名或换端口,存了完整域名会导致迁移后图片全部裂掉。sort_no 字段用来维持图片顺序,前端展示时按它排序,保证用户传的三张图不会因为数据库返回顺序不稳定而乱掉。
3.3 逻辑外键与角色权限的落法:少用物理外键的三个理由
这套系统里我建议用逻辑外键,也就是表结构里不带 FOREIGN KEY 约束,只保留关联字段如 report_id、user_id,在 Service 层保证关联正确性。理由有三个:一是物理外键会让数据初始化顺序变得很僵,导入数据或写测试数据时要按依赖顺序插入,非常烦;二是物理外键在高并发写入时有额外开销,虽然这套系统的量级远达不到,但这个习惯对你以后工作有帮助;三是答辩时导师拿 SQL 语句查数据,逻辑外键更灵活,造测试场景更快。
角色权限字段直接放 user 表,设计如下:
CREATE TABLE `user` ( `user_id` INT NOT NULL AUTO_INCREMENT COMMENT '用户主键', `openid` VARCHAR(64) NOT NULL COMMENT '微信openid,唯一标识', `nickname` VARCHAR(64) COMMENT '昵称', `avatar_url` VARCHAR(255) COMMENT '头像地址', `phone` VARCHAR(20) COMMENT '手机号,选填', `role` TINYINT NOT NULL DEFAULT 0 COMMENT '角色:0普通用户 1管理员 2处理人员', `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间', PRIMARY KEY (`user_id`), UNIQUE KEY `uk_openid` (`openid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';openid 设置 UNIQUE 索引是必须的,微信登录时用户可能反复进入系统,openid 重复会导致一个用户裂成好几条记录。role 默认值是 0 而不是 1,这是安全习惯——宁可先把所有人当普通用户,再手动把账号升级成管理员,也不要默认给管理员权限。
4. 后端接口与小程序端联动:把举报功能跑通的最小实现
数据库建好后,下一步就是把举报这条主链路打通。这章我先给接口清单和统一返回结构,再给后端举报接口的 Java 实现,最后给小程序端的表单提交与图片上传代码。代码是最小可用版本,重点是把参数校验、状态初始化和图片先传后提交这三件事做对。
4.1 接口清单与数据格式约定:先定 code 再写接口
整套系统最少需要七个接口,我列成表:
| 接口 | 方法 | 路径 | 调用方 | 说明 |
|---|---|---|---|---|
| 登录 | POST | /api/user/login | 小程序端 | 用 code 换 openid,首次登录自动创建用户 |
| 提交举报 | POST | /api/report/submit | 小程序端 | 核心接口,入参含标题、分类、位置、经纬度、图片URL列表 |
| 我的举报列表 | GET | /api/report/list | 小程序端 | 分页查询当前用户的举报单 |
| 举报详情 | GET | /api/report/detail | 小程序端 | 返回举报单和图片列表、处理记录 |
| 举报处理 | POST | /api/report/handle | 管理后台 | 受理、驳回、办结,按角色限制操作 |
| 图片上传 | POST | /api/upload | 小程序端 | 单文件上传,返回相对路径 |
| 隐患分类列表 | GET | /api/category/list | 小程序端 | 返回分类字典数据 |
接口返回结构这步一定要统一,不然前端判断是否成功会分成好几种写法。我一般用一个 Result 对象包三层:code 表示业务状态码,0 成功、非 0 具体错误;msg 是给用户看的提示文案;data 是业务数据。前端只判断 code 是否等于 0,msg 直接用于 toast 弹窗,data 按接口约定解析。
4.2 后端举报接口:参数校验与状态初始化的实现
提交举报是用户侧最核心的接口,我用 Spring Boot 风格给出控制器层实现。真实项目里 Service 层要多做几步,但控制器层是排查参数问题最快的地方:
@RestController @RequestMapping("/api/report") public class ReportController { @PostMapping("/submit") public Result submit(@RequestBody ReportSubmitDTO dto, @RequestHeader("X-User-Id") Long userId) { // 第一层:基础参数校验,标题、位置、分类是硬性要求 if (dto.getTitle() == null || dto.getTitle().trim().isEmpty()) { return Result.error(400, "隐患标题不能为空"); } if (dto.getLocation() == null || dto.getLocation().trim().isEmpty()) { return Result.error(400, "隐患位置不能为空"); } if (dto.getCategoryId() == null) { return Result.error(400, "请选择隐患分类"); } // 第二层:业务组装,状态强制置为 0,编号由 Service 生成 Report report = reportService.submit(dto, userId); // 返回编号方便用户后续查询 return Result.success(report.getReportNo()); } }这段逻辑的要点有四个。第一,标题和位置用「先判空再 trim 后再判空」的写法,能挡住纯空格这种脏数据。第二,userId 从请求头取而不是从参数里传入,防止普通用户伪造他人身份提交,这是权限设计的第一道闸门。第三,状态字段不在前端传,后端强制置为 0,前端传了也不认,避免恶意用户把任意状态推给后端。第四,接口返回 reportNo 而不是自增 ID,因为用户后续查询跟踪的是「举报编号」这个业务概念,不是数据库主键。
4.3 小程序端提交举报:先传图片再提交表单
小程序端的工作量主要是把表单数据和图片上传编排好。很多人第一次写会对 wx.uploadFile 和 wx.request 的配合感到混乱,容易把图片上传和表单提交写成并行,导致后端拿到表单时图片 URL 还没生成。正确做法是串行:先并发传图,全部拿到 URL 后再提交表单。代码如下:
/** * 提交举报信息 * @param {Object} data - 包含表单字段和图片临时路径的集合 */ submitReport(data) { const userId = wx.getStorageSync('userId'); // 第一步:先把所有图片传上去,拿到后端返回的 URL 数组 return this.uploadImages(data.imagePaths) .then((imageUrls) => { // 第二步:图片 URL 全部就绪后再提交表单数据 return new Promise((resolve, reject) => { wx.request({ url: `${BASE_URL}/api/report/submit`, method: 'POST', header: { 'X-User-Id': userId }, data: { title: data.title, description: data.description, location: data.location, categoryId: data.categoryId, latitude: data.latitude, longitude: data.longitude, imageUrls: imageUrls // 后端映射为 ReportSubmitDTO 的 List<String> }, success: (res) => { // 约定后端始终返回 { code, msg, data } if (res.data.code === 0) { wx.showToast({ title: '举报成功,等待受理' }); resolve(res.data.data); } else { wx.showToast({ title: res.data.msg, icon: 'none' }); reject(new Error(res.data.msg)); } }, fail: (err) => { wx.showToast({ title: '网络异常,请重试', icon: 'none' }); reject(err); } }); }); }); }, /** * 并发上传多张图片,全部成功后统一返回 URL 数组 */ uploadImages(imagePaths) { const tasks = imagePaths.map((path) => { return new Promise((resolve, reject) => { wx.uploadFile({ url: `${BASE_URL}/api/upload`, filePath: path, // wx.chooseMedia 返回的临时路径 name: 'file', success: (res) => { // wx.uploadFile 的 res.data 是字符串,必须手动 JSON.parse const result = JSON.parse(res.data); if (result.code === 0) { resolve(result.data.url); } else { reject(new Error(result.msg)); } }, fail: reject }); }); }); // 并发上传所有图片,任一失败则整体失败 return Promise.all(tasks); }这段代码值得注意的细节有三个。第一,Promise.all 并发传图,而不是 for 循环里 await 串行传,用户体验更好,三张图几乎同时到达服务器。第二,wx.uploadFile 的 success 回调里 res.data 是字符串,必须先 JSON.parse 再判断 code,这一步漏掉会导致你永远拿不到图片 URL,这是高频翻车点。第三,imageUrls 作为数组直接放进 data 对象交给 wx.request,后端用 List 接收,前端不要自己把它 join 成字符串,那是给后端添麻烦。
4.4 图片路径的本地存储与静态映射:不要把完整域名存进数据库
图片传上去了,服务器得有地方放,也得能访问。常见做法是在服务器上建一个 upload 目录,按日期分目录存储,比如 upload/2025/06/01/xxx.jpg。后端上传接口收到文件后,计算出相对路径存数据库,访问时通过静态资源映射暴露出去。千万不要把「http://localhost:8080/upload/xxx.jpg」这种完整地址存库,部署到服务器后域名一变就全裂。
Spring Boot 里的静态映射很简单,把本地目录映射成 /upload/** 访问路径。如果采用的是 Node.js,express.static 指向 upload 目录也行。关键点是数据库里存的是相对路径,前端拿到相对路径后拼上自己的 BASE_URL 做展示。这样切环境时只改一处前端配置,数据库不用动。
图片大小要限制,常见做法是单张不超过 5MB,总图数三张以内。接口层对文件类型做白名单校验,只接受 jpg、jpeg、png 三种格式,别用黑名单方式屏蔽 exe,因为绕过黑名单太容易了。这类限制在答辩时也是加分点,说明你考虑了存储成本和越权上传风险。
5. 部署与答辩避坑:五个直接决定项目成败的高频翻车点
这一章全是我见过别人踩过、自己也踩过的真实问题。每一条都按「现象 → 原因 → 解决」来写,覆盖了从开发调试到最终演示的完整阶段。把这些坑提前避开,你的系统稳定性能上一个台阶。
5.1 真机预览时所有请求全部失败:合法域名与调试开关
现象:开发者工具里接口一切正常,一换成真机预览,所有 wx.request 全部报「url not in domain list」。
原因:微信小程序对网络请求有严格限制,小程序端只能请求已配置到微信公众平台的合法域名,而且必须 HTTPS。localhost 调试时工具默认放行,真机上这套规则就生效了。
解决:开发调试阶段,在微信开发者工具的「详情 → 本地设置」里勾选「不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书」。注意这是开发阶段的权利,正式发布的小程序必须配置合法域名并启用 HTTPS,这条路绕不开。上架前记得把服务器域名填到微信公众平台后台的 request 合法域名和 uploadFile 合法域名里,两个配置对应两个不同的接口类别,漏一个就会有一部分功能失灵。
5.2 图片上传成功但刷新页面后图片裂了:临时目录与相对路径
现象:用户刚提交举报时详情页图片显示正常,过一段时间或者服务器重启后图片变成裂图。
原因:图片被保存到了服务器的临时目录,比如直接用项目下的临时文件夹,甚至存进了内存文件系统。服务器一重启,临时文件被清理,数据库里的 URL 全部指向不存在的文件。另一个常见原因是存了完整域名,换了端口或域名后全部失效。
解决:不要把 upload 目录放在项目构建输出目录下,单独建在服务器固定位置,例如 /data/upload,用配置文件指定路径而不是硬编码。数据库只存相对路径,前端展示时按当前环境的 BASE_URL 拼接。更进一步,图片应该按日期分目录存储,避免单目录文件过多,后续做清理也方便。
5.3 中文乱码与编号撞车:字符集和编号生成策略
现象:举报单里的小区名称、楼道描述在后台显示成问号;或者两张举报单生成了相同的 report_no,后插入的直接报唯一键冲突。
原因:乱码是因为建表用了默认字符集,MySQL 5.7 之前的默认是 latin1,存中文就裂。编号撞车是因为表数据被清空过,自增主键没有按业务规则生成,或者有人直接把当前时间戳当编号,同一毫秒内提交两条就冲突。
解决:建表语句统一加 CHARSET=utf8mb4,连接串里加 characterEncoding=utf8 参数,两处都配齐才能根治乱码。编号不用时间戳,用「R + yyyyMMdd + 当日自增序号」的规则,在 Service 层先查当天最大值再加一,产生冲突的概率极低,同时具有业务可读性。清表数据后要重置自增起点,否则编号序列会乱。
5.4 权限被绕过:普通用户直接调接口改状态
现象:小程序端只对管理员显示了「受理」「驳回」按钮,但普通用户抓包或者用工具直接请求后端接口,一样能把举报单状态改成已办结。
原因:权限控制只做在了前端页面可见性上,后端接口没有任何角色校验。前端隐藏按钮对正常用户友好,但对懂技术的人形同虚设。
解决:后端拦截器统一处理,对所有 /api/report/handle 这类写接口做角色校验,只允许管理员和处理人员调用。用户信息从请求头里拿,不要从请求体里读。代码层面的常用做法是加一个拦截器或 AOP 切面,标注 @RequireRole 这样的注解,接口上声明需要的角色,一处配置处处生效。这一条在答辩时几乎是必问的,提前做好就是送分题。
5.5 答辩现场白屏:演示环境的三个准备
现象:答辩演示时投影仪连上,小程序加载白屏,接口请求卡死,台下老师开始皱眉。
原因:演示现场网络不稳定,或者你前一天在宿舍能连的服务器今天已经关停。最常见的是后端服务还跑在本机 8080 端口,而小程序只配置了远程服务器的域名,现场网络访问不到。
解决:做三手准备。第一,演示前把后端服务打包部署在演示电脑本机,小程序开发者工具里把 BASE_URL 指到本地,并勾选不校验合法域名,这样断外网也能跑。第二,准备一份功能演示录屏,防止现场环境彻底罢工,录屏时长控制在 5 分钟内。第三,预置一份模拟数据,包含各个状态的举报单,演示时直接从相对丰富的列表页开始讲,不要在登录和空白列表上浪费时间。
6. 把毕业设计做出区分度:三个投入产出比最高的进阶方向
基础功能跑通只能保证及格,要让答辩老师觉得「这个学生真的思考过」,还得在一两个点上往前多走一步。我建议从以下三个方向里挑一个做深,不要三个都加,堆功能反而暴露实现粗糙。
第一个方向是用订阅消息打通处理结果通知。现在系统的痛点在于用户提交举报后要自己回来看状态,体验上的闭环是断的。接入微信订阅消息,管理员办结举报时后端向用户推送一条「您举报的隐患已处理完毕」的通知。代码逻辑不难:用户提交时调 wx.requestSubscribeMessage 申请授权,后端存下用户的 openid 和订阅凭证,状态变更时调订阅消息发送接口。注意一次性订阅消息的规则,用户授权一次只能发一条,你要在合适的时机申请授权,通常是提交成功那一刻。这一点能讲清楚,说明你真的理解小程序平台的限制。
第二个方向是做坐标距离排序,在管理端或者用户端加一个「附近隐患」列表。用户在举报详情页提交了经纬度,数据已经在那里了,只差一个距离计算。具体做法是后端拿到当前用户经纬度,用 Haversine 公式逐一计算与各举报单的距离,按距离排序返回。数据量不大时不需要引入地理位置索引这类重型方案,一条 SQL 或者内存计算都能扛住。这个功能的好处是让举报数据不再是孤立的记录,而是带空间维度的信息,答辩时演示效果很直观。
第三个方向是做管理端月度趋势统计。用 GROUP BY 按分类统计、按周或按月聚合举报数量,再用 ECharts 画柱状图和饼图。我建议重点展示「高发隐患分类 Top5」和「最近六个月的举报趋势」两张图,前者说明分类治理有重点,后者说明趋势是可跟踪的。统计查询单独走一个只读接口,和业务写接口分离,避免统计 SQL 拖慢主流程。老师看到图表时通常会接着问数据口径,你答得出「按 create_time 聚合,排除已驳回记录」就已经赢了。
最后说一个我印象很深的教训。有一位同学把系统功能做得非常完整,但是答辩时老师问了一句「如果同一栋楼五个人举报同一个隐患,系统怎么处理」,他当场答不上来。这个问题的本质是数据一致性和重复举报处理策略,不需要做复杂合并算法,只要在设计里加一个「同位置同分类且未办结的举报单不被重复受理」的判断,就可以正面回答。我想说的是,毕业设计做到最后,拼的不只是代码量,而是你对业务边界的思考。把自己代入真实使用场景,把边界问题提前过一遍,你的项目会完全不一样。希望帮到你。
本文还有配套的精品资源,点击获取