很多准备毕业设计的同学拿到“springboot社区疫情报备微信小程序的设计与实现”这个题目时,第一反应往往是“到底要做什么”“模块怎么拆”“代码从哪写起”。这个题目在计算机毕业设计里非常典型,它表面上是两个技术栈的组合——Spring Boot做后端接口,微信小程序做前端交互,但本质上考的是一套完整的信息管理流程:居民端提交健康信息、社区端审核与统计、数据落库与接口联调。这篇文章我会按毕设的实际推进顺序,把需求拆解、表结构设计、核心接口实现、小程序端调用方式、联调部署的坑全部过一遍,给准备做类似课题的同学一条可以直接照着走的路。
这个系统适合两类人参考:一是选题相近、准备从零搭建后端加小程序项目的毕业生,二是想快速搞清楚“一个带有身份角色、数据审核、统计导出功能的信息系统”应该如何落地的开发者。我会把每一步背后的为什么也讲清楚,不只是贴代码。
1. 选题定位与核心思路拆解
1.1 这个题目实际要完成什么
从功能上看,这是一个典型的“C端上报 + B端管理”双向系统。居民通过微信小程序填报个人健康信息、居住地址、体温和异常接触情况;社区管理员在小程序或后台查看本社区的上报记录,对异常信息进行核实、标记和处理,同时能看到上报人数、异常数量等统计数据。
很多同学容易把题目想得过大,一上来就弄几十张表、十几个页面,最后根本写不完。这里给你一个建议:毕业设计的核心是“完整闭环”,不是“功能大而全”。一个用户能登录、能上报、能查看自己的历史记录,管理员能审核、能统计,数据能落库、接口能跑通,这套闭环已经足够支撑一篇不错的论文和答辩了。在此基础上再考虑公告管理、消息通知、数据导出这些加分项,才是正确的节奏。
技术上,Spring Boot负责提供RESTful接口,小程序端通过wx.request调用这些接口,数据统一存到MySQL。前后端分离,部署时可以后端跑在云服务器,小程序用开发版或体验版联调。这个模式也是目前企业里最常见的小程序开发方式,写在简历里是加分项。
1.2 为什么是Spring Boot加微信小程序这套组合
这不是拍脑袋选的组合,而是有内在原因。Spring Boot的最大价值是“约定大于配置”,内嵌Tomcat,一个jar包就能跑起来,天生适合做接口服务。配合MyBatis-Plus,连基本的增删改查SQL都不用手写,这对毕设周期来说非常友好。而微信小程序选原生框架即可,不需要额外引入Vue或uni-app,因为毕设的页面量不大,原生开发的调试成本最低、参考资料最多。
在这个项目里,后端承担的不是简单的CRUD,它还要处理小程序登录时拿到的code、调微信接口换取openid、再签发一个自定义的登录态给小程序后续请求使用。同时,健康报备数据涉及用户隐私,接口不能让人随便遍历,所以后端必须做基于身份的鉴权。这些用Spring Boot实现起来很顺手,拦截器加一个自定义注解就能搞定。
另外,Spring Boot社区生态非常成熟,MyBatis-Plus、EasyExcel、Redis等中间件都有开箱即用的starter。毕设如果有“创新点”需求,直接在项目里引入Redis做缓存热门数据,或者用EasyExcel做报表导出,都能作为论文的亮点章节,不会额外增加太多工作量。
1.3 需求边界:哪些功能必须做,哪些可以不做
据我接触过的毕设经验,这类题目的“标配功能”主要集中在三个角色和一个核心流程上。三个角色分别是居民、社区管理员、系统管理员;核心流程就是“报备数据产生—入库—审核—汇总统计”。
居民端必须做:微信授权登录、个人信息维护(主要是住址楼栋房号)、每日健康信息填报、历史报备记录查询。管理员端必须做:查看本社区报备列表、按日期或状态筛选、审核处理异常报备、查看统计概览(今日上报量、异常人数、趋势图)。系统管理员可以简化处理,主要负责社区信息维护,甚至可以合并到管理员端里,用角色字段区分即可。
不需要做的:千万不要在毕设里做在线问诊、核酸预约这类跟题目离得太远的功能。评委老师一眼就能看出来功能不是围绕核心流程展开的,反而会问“你的核心业务逻辑是什么”这种更难回答的问题。优先保证主流程扎实,再把公告通知、数据导出、消息订阅作为扩展点,这才是拿高分的策略。
2. 系统架构与数据设计
2.1 后端项目结构与分层设计
我建议按下面这个包结构来组织代码,它既符合规范,也方便答辩时讲清楚每个层的职责。
com.example.communityreport ├── config // 拦截器、跨域、Web配置 ├── controller // 接口层,接收请求参数 ├── service // 业务逻辑层,处理核心规则 ├── mapper // MyBatis-Plus的Mapper接口 ├── entity // 数据库实体类 ├── dto // 数据传输对象,避免实体直接暴露 ├── vo // 视图对象,封装返回给前端的数据 ├── utils // 工具类,例如JwtUtil、Result封装 └── common // 统一返回结果、异常处理、常量controller层只负责接收参数和返回结果,不写业务逻辑。业务逻辑放在service层,比如“提交报备时校验用户是否存在”“管理员审核时只能处理本社区的数据”这些规则全部在service里完成。mapper层用MyBatis-Plus提供的BaseMapper,复杂统计用注解SQL或QueryWrapper。这样分层带来的实际好处是,答辩时老师问“这个接口流程是什么”,你能很清晰地讲出请求从哪里进、经过了哪些校验、最后落到了哪张表。
统一返回结果类也很重要,建议所有接口返回一个固定格式:
public class Result<T> { private Integer code; private String message; private T data; }小程序端拿到返回值后,只需要判断code是否为200就能决定后续操作,错误信息直接弹toast。这个设计虽然简单,但能避免前后端因字段命名不统一产生大量联调问题。
2.2 数据库表设计
毕设系统不需要很复杂,四张核心表加两张辅助表足够。我先说核心表,每一张都会直接影响主流程。
用户表的字段设计如下:
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| openid | varchar(64) | 微信openid,唯一 |
| nick_name | varchar(64) | 昵称 |
| real_name | varchar(32) | 真实姓名,用来核实身份 |
| phone | varchar(20) | 手机号 |
| community_id | bigint | 所属社区 |
| building | varchar(32) | 楼栋号 |
| unit | varchar(32) | 单元号 |
| room | varchar(32) | 房号 |
| role | tinyint | 角色:0居民,1管理员 |
| create_time | datetime | 创建时间 |
openid是微信用户在小程序内的唯一标识,它不能反查用户信息,但可以作为用户表的主业务键。用户首次进入小程序时用wx.login拿code,后端请求微信接口换取openid,查不到就自动创建用户。这解决了“用户不需要注册登录”的体验问题。
报备表是整个系统的核心表,记录每一次健康上报:
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| user_id | bigint | 上报人ID |
| report_date | date | 上报日期 |
| temperature | decimal(4,1) | 体温 |
| has_fever | tinyint | 是否发热 |
| has_cough | tinyint | 是否咳嗽 |
| has_abnormal_contact | tinyint | 是否有异常接触情况 |
| health_code_status | tinyint | 健康状态颜色标记,0绿色,1黄色,2红色 |
| address_detail | varchar(255) | 具体位置描述 |
| status | tinyint | 0待审核,1已通过,2已驳回 |
| audit_user_id | bigint | 审核人ID |
| audit_time | datetime | 审核时间 |
| audit_remark | varchar(255) | 审核备注 |
| create_time | datetime | 提交时间 |
设计这张表时有几个细节值得注意:report_date要单独存,而不是直接用create_time的日期,因为后续需要做“当天是否已上报”的判断,分开存可以避免时区、时分秒带来的麻烦。体温字段用decimal(4,1)意味着最多3位整数加1位小数,正常人用不到4位整数,但保留位数是为了防止脏数据。
社区表和公告表作为辅助,社区表很简单,只有id、community_name、region_code这几个字段;公告表记录管理员发布的通知,包含title、content、publish_time。如果有导出功能,再加一张导出日志表也不是不行,但非必需。
2.3 小程序端页面与接口对应关系
小程序端的页面不要贪多,建议控制在9个以内,对应关系如下:
| 页面 | 功能 | 后端接口 |
|---|---|---|
| pages/login/index | 登录与授权 | POST /api/user/login |
| pages/home/index | 首页,显示今日状态与公告 | GET /api/report/current |
| pages/report/index | 填写健康信息并提交 | POST /api/report/submit |
| pages/list/index | 历史报备记录 | GET /api/report/history |
| pages/admin/index | 管理员审核列表 | GET /api/admin/report/list |
| pages/admin/detail | 报备详情与审核操作 | GET /api/admin/report/detail, PUT /api/admin/report/audit |
| pages/admin/stats | 数据统计概览 | GET /api/admin/stats |
页面跳转用tabBar加普通页面混合即可。首页和填写页作为tabBar一级页面,管理员审核相关页面用普通跳转。最主要的是不要让居民端页面和管理员端页面混在一个tabBar里,否则用户切tab就能看到审核列表,角色隔离形同虚设。
3. 核心功能实现与实操细节
3.1 小程序登录与后端鉴权
登录流程是“微信登录—后端换openid—签发自定义登录态”的三步走。小程序端调wx.login拿到短期有效的code,再把code发给你的后端接口。后端拿到code后,使用appid和secret请求微信官方接口,换取openid和session_key。
@PostMapping("/login") public Result login(@RequestBody LoginDTO dto) { String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appid + "&secret=" + secret + "&js_code=" + dto.getCode() + "&grant_type=authorization_code"; // 使用Java的HttpClient发起GET请求,解析返回openid String openid = parseOpenid(httpClientGet(url)); User user = userService.findByOpenid(openid); if (user == null) { user = userService.register(openid); } String token = JwtUtil.createToken(user.getId(), user.getRole()); return Result.success(new LoginVO(token, user)); }这里要特别强调一个常见错误:openid只能在登录流程中由后端去微信服务器换取,小程序前端是拿不到openid的。有些同学会尝试在开发者工具里通过某些“捷径”直接解析code,这是不可能的,因为code本来就是给后端换取会话信息用的。
后端签发token后,小程序端把它存在本地storage,之后每次请求都加到Header里。Spring Boot侧用一个拦截器统一拦截除login外的所有接口,解析token里的userId,塞到ThreadLocal或请求上下文里供service使用。
public class AuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String token = request.getHeader("Authorization"); // 校验token有效性 Long userId = jwtUtil.parseToken(token); if (userId == null) { throw new BizException(401, "登录已过期"); } UserContext.setUserId(userId); return true; } }我建议token使用JWT而不是“随机字符串存在Redis里”的唯一考虑是,毕设项目不需要额外部署Redis,JWT自带信息且无状态,写起来也简单。但项目里如果已经有了Redis,那用Redis保存token、设置过期时间也完全可以,答辩时反而能说“我通过Redis控制会话失效”,这也是个加分点。
3.2 健康报备提交与表单校验
报备提交接口是核心中的核心。用户打开填写页,填入体温、勾选症状、选择健康状态,然后提交。后端要做的第一件事不是直接入库,而是先查“今天是否已经提交过”。一个用户一天只允许上报一次,否则统计数据会出现严重重复。
“是否已提交”的判断不能只靠前端提示,因为前端关了页面再进,后端还是得兜底。在service里做这个判断比较合理:
@Override @Transactional public void submit(ReportSubmitDTO dto) { LocalDate today = LocalDate.now(); Long userId = UserContext.getUserId(); long count = healthReportMapper.selectCount( new QueryWrapper<HealthReport>() .eq("user_id", userId) .eq("report_date", today)); if (count > 0) { throw new BizException("今日已提交,不能重复上报"); } HealthReport report = new HealthReport(); report.setUserId(userId); report.setReportDate(today); report.setTemperature(dto.getTemperature()); report.setHasFever(dto.getHasFever()); report.setHasCough(dto.getHasCough()); report.setHasAbnormalContact(dto.getHasAbnormalContact()); report.setStatus(0); healthReportMapper.insert(report); }注意方法上加@Transactional,这保证插入和后续操作要么全成功要么全回滚。确认提交成功后,小程序端应该弹一个成功的反馈,并跳回首页显示今天已完成上报。
对于体温这类数字字段,前端和后端都要做校验。前端在表单提交前用正则限制只能输入一位小数;后端用@DecimalMin和@DecimalMax注解做二次校验。比如体温允许范围35.0到42.0,超出直接拒绝。这里有个很典型的教训:有些同学只在后端写了校验,前端没限制,用户随手填个999,后台统计平均值时整个图表就崩了。两端都要防,别偷懒。
3.3 管理端审核与统计
管理员端最核心的权限规则是“只能看到自己管辖社区的报备数据”。这句话听起来简单,但实际很容易写成“看所有数据”,这是毕设评审时一个典型的扣分点。拦截器只解决了“你是不是登录用户”的问题,没有解决“你能不能看这些数据”的问题,所以业务层必须再做一次数据权限校验。
以审核列表为例,查询条件里必须拼上community_id,并由UserId反查当前用户的communityId,然后加进QueryWrapper中。审核按钮也同理:操作前先查这条报备是不是本社区的,不是就直接拒绝,而不是只在前端隐藏按钮。
审核动作本身是个简单的状态流转:把status从0改成1或2。但在更新数据时需要同时写入audit_user_id和audit_time,为后续追责和论文里的时序分析留数据基础。
统计接口建议用SQL聚合而不是在Java里for循环数数。比如查“最近7天每天的提交量”,用一条groupBy就完成:
SELECT report_date, COUNT(*) AS count FROM health_report WHERE community_id = ${communityId} AND report_date >= #{startDate} GROUP BY report_date ORDER BY report_date统计结果返回给小程序端时,前端拿echarts-for-weixin或简单的canvas绘制柱状图即可。如果不想引图表库,直接用view画柱子也可以,但视觉上稍微逊色。我建议引入一个轻量级图表库,代码量不大,图表效果会让答辩现场印象分明显提升。
3.4 数据导出与通知扩展
数据导出是很多老师关心的亮点功能,实现起来却并不复杂。后端用EasyExcel,定义好导出类,把报备列表一次性查出来,写成Excel返回流。小程序端不能直接保存文件,一般做法是后端生成文件后返回一个下载链接,或返回临时URL,小程序用web-view打开或提示“请到管理端后台下载”。这里我建议做成一个独立的管理后台页面用浏览器打开下载,而不是纠结在手机小程序里导出。
通知功能可以使用微信订阅消息。用户提交报备后,后端引导用户订阅“审核结果通知”;管理员审核之后,后端调用微信的subscribeMessage.send接口,把结果推送给用户。但这个功能需要申请模板ID,且订阅消息有“一次性订阅”限制,首次申报时必须先让用户主动点击订阅按钮。作为毕设,这部分实现到“发送接口调用成功并可看到发送记录”即可,不必追求真实送达率。
4. 联调部署与常见问题排查
4.1 本地联调必备配置
联调阶段最容易卡住的地方是“小程序真机预览”和“本地后端接口”的网络联通问题。小程序开发者工具有一个“不校验合法域名”的选项,调试阶段可以勾选,但真机预览时就失效了,手机会因为域名校验失败而请求失败。所以联调时最稳妥的方案是让后端跑在同一局域网内,手机访问后端机器的局域网IP,同时在小程序后台把request合法域名临时加好。
后端的application.yml里有几处配置是必须改的:
server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/community_report?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: yourpassword wx: appid: your-appid secret: your-secret数据库连接串里的serverTimezone最好显式指定为Asia/Shanghai,否则MySQL 8和系统时区不一致时,查询出来的时间会差8小时,排查起来非常隐蔽。appid和secret在毕设里写进配置文件中没问题,但注意不要提交到公开仓库,这是基本功。
4.2 小程序合法域名与上线审核
如果你打算把这个小程序真正发布上线,而不是只停留在开发版,那么request域名必须是HTTPS且在小程序后台配置了合法域名。这个域名需要ICP备案,且HTTPS证书有效。大多数毕设项目没有现成的备案域名,如果你真的想发布,可以考虑用一个云服务商的轻量服务器搭好Spring Boot,再挂上域名和证书。
小程序审核方面需要准备的资质通常包括:涉及用户信息收集时需要隐私保护指引声明,避免审核以“没有隐私政策”为由驳回。如果你只是线上答辩展示,用“体验版”就够了,不需要走完整发布流程;体验版也可以给评委扫码访问,功能和正式版完全一致。
4.3 五个高频踩坑点
我把带过的学生和自己在开发中真实遇到过的问题整理成速查表,按出现频率排序:
| 问题 | 现象 | 原因 | 解决方案 |
|---|---|---|---|
| 登录后接口全部401 | 首页数据加载不出来 | token没正确放到请求头,或后端Header名不一致 | 统一封装request方法,在拦截器里打印Header名做核对 |
| 时间相差8小时 | 统计图日期错位 | 数据库连接串未指定serverTimezone | 连接串加serverTimezone=Asia/Shanghai |
| 用户重复提交两条记录 | 统计数字翻倍 | 未做“当天是否已上报”的幂等校验 | 在submit逻辑里先查count再insert |
| 小程序图片/图标无法显示 | 控制台提示url无合法域名 | 开发者工具中图片域名未加白 | 需要在小程序后台配置downloadFile合法域名 |
| Excel导出中文乱码 | 文件打开乱码 | 导出时输出流未设置UTF-8或Excel字符集问题 | EasyExcel中用write方式并自定义样式即可,乱码多数是文件流未刷新 |
除了表格里这五个,还有一个非常容易踩但不易察觉的逻辑坑:管理员审核时,前端传了reportId和status,后端没有校验该reportId是否属于本社区,导致管理员可以审核其他社区的数据。这类越权问题在答辩时被问到的概率极高,建议在写代码时就把它当作必查项。
如果你在联调时发现接口能通但数据不对,我建议先抓包看一眼实际请求参数。开发者工具console里打印出请求和响应,90%的问题一眼就能看出来。真正花时间多的是“前端以为是后端问题,后端以为是前端问题”,所以接口统一返回Result结构能大大减少这种扯皮。
5. 答辩准备与项目成长空间
最后一个章节我想聊几句从毕设延伸到就业的体会。这类系统的答辩,老师最常问的几个方向,我整理下来基本逃不过这些:
- 为什么用户表不存密码,而是靠openid登录?
- 接口怎么防止别人的小程序调用?答:校验了appid和签名,其实更准确的说法是靠token鉴权。
- 如果一天有上万人同时提交,你会怎么优化?答:接口限流、加Redis缓存、上报数据走消息队列异步落库。
- 统计报表数据量大时怎么优化?答:定时任务预聚合,日报表单独建表。
其中“接口鉴权”这个问题,很多同学在写代码时没想明白。你要能说清楚:微信小程序id是公开的,所以任何人都能用你的appid调微信接口,但用户没有你的code就无法换取openid,而每个请求又带着你的token,因此系统安全的本质是“token校验”,而不是“appid保密”。能把这个逻辑讲清楚,老师一般就不会再往死里追问了。
扩展方向上,这个系统有两条非常自然的成长路径。一条是数据维度:把报备记录从单日的“表单数据”变成“连续多日的个人健康档案”,并加入画像分析;另一条是交互维度:引入订阅消息、位置定位自动填充社区,甚至用低代码平台管理公告内容。这些不需要在毕设阶段全做,但作为论文里的“未来展望”,本身就是一个完整的故事。
我个人在实际操作中的体会是,这类课程设计最大的价值不是让人学会堆框架,而是让人理解“一个真实的信息系统到底是怎么把身份、数据、流程串起来的”。当你把openid登录、角色校验、一日一报的幂等逻辑、按社区隔离的数据权限这些点一个个想清楚,毕业设计就不再是“调通代码”这么简单,而是真正拥有了从需求到实现的完整认知。希望这篇文章能帮你少走几步弯路,如果你在开发中遇到具体的技术报错,也欢迎按我前面给的排查思路先试一遍,大部分问题其实都出在那几个固定的位置上。