☰
Spring Boot + 微信小程序:社区居民传染病防治系统开发实战
2026/9/26 11:53:05 网站建设 项目流程

1. 这套系统到底要解决什么问题

先说个我亲身经历的场景。去年秋天我接了个社区卫生服务中心的数字化改造项目,当时对方提的需求很简单——“我们要一个能报传染病的系统”。等坐到会议室里聊完才发现,他们要的不止是一张电子报表,而是一整套从居民自查、社区上报、疾控审核到跟踪随访的业务闭环。而技术团队最初的方案是做个纯管理后台,让社区医生手动录入数据,结果试点一周就废了——居民不愿意为了一个“疑似症状”专门跑一趟社区,医生也扛不住天天电话回访的工作量。

这个项目标题里的关键词拆开来看就很有意思:“springboot”决定了后端的技术骨架,“微信小程序”承包了居民侧的使用入口,“社区居民传染病防治”则是整个业务的核心场景。做这类系统最忌讳的就是只把它当成一个CRUD应用开发。它真正解决的是三件事:第一,降低居民报告异常健康状况的门槛,让人人都有随手报告的能力;第二,让社区医生从“挨个打电话问”变成“看系统推送跟进”,把有限的公共卫生人力花在刀刃上;第三,让数据在疾控和社区之间自动流转,而不是靠微信群里的Excel传来传去。

这套系统面向的人群也很清楚:居民端是普通老百姓,包括不太会用手机的老年人,所以小程序端的操作必须极致简化,界面字号要大、路径要短、反馈要即时;管理端是社区卫生服务中心的医生、防保科人员和上级疾控机构的管理员,他们要的是待办清单清晰、统计报表直观、审核流程顺畅。至于开发者,这个项目也是一个比较典型的Spring Boot + 微信小程序全栈实战案例,涉及用户登录鉴权、健康数据上报、任务流流转、报表统计这些常见的业务模块,非常适合用来练手或者改造成其他行业的信息采集系统。

我在设计这套系统时,始终记着一句话:公共卫生系统的价值不在于功能多,而在于链路通。居民上传一条症状记录,后面能不能自动触发社区医生的关注任务?社区医生做了初步判断后,信息能不能带着历史轨迹同步给疾控审核?这些流程如果不打通,系统做得再花哨也是摆设。

2. 核心功能拆解与数据模型设计

2.1 居民侧:四个入口撑起日常使用

居民端的小程序不需要复杂的导航结构,我做的是底部四个Tab:首页、上报、记录、我的。首页展示社区公告和当前传染病的防控提醒,比如流感季节的疫苗接种通知;上报是核心入口,居民选择症状类型、填写体温、勾选接触史、上传图片,十几秒就能完成一次健康自报;记录页面按时间倒序展示历史上报记录及处理状态;我的就是个人信息维护和家庭成员管理。

这里有一个很关键的产品决策:家庭成员管理。社区里大量老人不会用智能手机,往往是子女帮父母上报,所以系统必须支持“一人注册、绑定多人”的模式。我在实际开发时给家庭成员表单独设计了relation_type字段,用来区分本人、子女、父母等关系,后端校验时会限制一个人最多绑定5个成员,防止数据滥用。

居民上报的表单设计也需要拿捏分寸。字段少了,疾控侧拿不到足够的流行病学信息;字段多了,居民填到一半就放弃。我最终保留的字段是:症状类型(多选)、体温数值、发病日期、接触史(是否接触过确诊/疑似人员)、近期出行记录、备注说明。图片上传单独作为一个可选附件,不放进必填项。

2.2 管理端:业务闭环的核心阵地

管理端没有做独立App,而是直接用Spring Boot + Thymeleaf渲染后台页面,这样一个仓库就能同时搞定居民端和管理端,部署成本低。管理端有三个核心工作台:待审核上报列表、居民健康档案查询、统计数据看板。

待审核列表是这个系统的神经中枢。社区医生登录后第一眼看到的就是所有待处理的上报记录,每条记录会显示风险等级(由后端根据体温、症状组合自动计算),医生可以执行的操作包括:核实无误后上报疾控、标记为疑似需要采样、登记为已排除并填写排除理由。每一次操作都会写审计日志,这个日志表很重要,公共卫生系统免不了后续的追溯检查。

统计看板则是给管理层看的,按周、月统计上报量、各类症状分布、社区覆盖率等指标。我最初用ECharts画折线图和饼图,后来发现定期跑批生成统计报表让前端直接读会更稳,就用Spring Task写了个定时任务,每天凌晨汇总前一天的数据。

2.3 数据库设计:别看表少,关系要对

整套系统核心表不超过十几张,但表间关系一定要捋清楚。我列出几张关键表的结构:

居民用户表(resident):openid、nickname、phone、community_id、household_id、create_time。这里要注意openid是微信登录的唯一凭证,必须建唯一索引。我把家庭成员都挂在household_id下,查询家庭成员时一次索引就能带出来,比递归查parent_id高效得多。

上报记录表(report_record):id、resident_id、patient_name、symptom_type、temperature、contact_history、risk_level、status、audit_user_id、audit_time、audit_remark。status字段用tinyint存枚举值:0待审核、1已上报疾控、2已排除、3疑似待采样、4已确诊。这个状态流转是整个业务流程的骨架。

审核日志表(audit_log):report_id、operator_id、action、remark、create_time。这里我来回改过几次才定下用独立表记录而不是在上报表里加字段,因为一次上报可能被多次审核(社区初审、疾控复核),独立表才能保留完整的操作轨迹。

任务跟进表(follow_task):report_id、assignee_id、task_type、due_time、status、finish_time。当一条上报被标记为“疑似待采样”时,系统会自动生成一条跟进任务分配给对应的社区医生,并在临近截止时间时推送提醒。

组合索引的设计上,我给report_record表建了(community_id, status, create_time)的联合索引,因为管理端最频繁的查询就是“某社区所有待审核记录,按时间倒序”,这个索引可以覆盖90%的列表查询场景。建完之后千万记得用EXPLAIN看执行计划,我遇到过索引建了但查询没走上的情况,多半是排序字段和索引顺序不匹配导致的。

3. 关键流程的工程化实现

3.1 微信登录与Spring Boot的认证链路

微信小程序登录是这套系统的第一个技术关卡。整体流程我相信不少开发者都熟:小程序端调用wx.login()拿code,后端拿code加appid和secret去微信接口换openid和session_key,然后签发自己的登录凭证。这里我踩过的最大的坑是——千万不要把session_key存到数据库里,它是微信会话密钥,应该只留在内存里用于解密敏感数据,用完就丢。

我在Spring Boot端用JWT做登录态管理,生成的token有效期设为2小时,小程序端把token存在storage里,每次请求通过拦截器校验。这块我单独写了一个AuthInterceptor,实现HandlerInterceptor接口,在preHandle里从Header取token然后解析用户信息放入ThreadLocal,方便后续业务代码直接取当前操作人。

拦截器配置要注意放行路径:/api/auth/**(登录、获取验证码)、/api/public/**(公告查询)、静态资源路径必须放行,其他/api/**全部走鉴权。我见过同事把鉴权拦截器配成拦截所有路径,结果小程序端首屏加载公告页直接401,排查了半天才发现是静态资源也被拦了。

@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (!(handler instanceof HandlerMethod)) { return true; } String token = request.getHeader("Authorization"); if (StringUtils.hasText(token) && jwtUtil.validateToken(token)) { Long userId = jwtUtil.getUserId(token); UserContext.set(userId); return true; } response.setStatus(HttpStatus.UNAUTHORIZED.value()); return false; }

登录接口返回给前端的数据结构我建议用统一响应体,比如{code: 200, data: {token: "xxx", userInfo: {...}}, message: "success"}。小程序端封装请求时统一处理code非200的情况,弹Toast提示错误信息。这样后续加新接口时前端不用在每个页面单独写错误处理,省一大块重复劳动。

3.2 上报流程的实现与风险等级判定

居民上报的核心接口是POST /api/report/submit,这个接口串联了数据校验、风险计算、任务生成三个环节。数据校验除了常规的字段非空、体温范围检查,还要做简单的频控——同一居民5分钟内不能重复提交相同症状的上报,这个用Redis的SET NX EX就能实现,key设计成report:frequency:{residentId},过期时间300秒。

风险等级判定我用了很朴素的规则引擎:体温≥38℃且症状包含咳嗽/乏力 → 高风险;体温≥37.3℃或接触过确诊/疑似人员 → 中风险;其余 → 低风险。规则写在配置表里而不是写死在代码中,因为疾控的评判标准可能随时调整,让运营人员通过管理后台改配置比重发版本靠谱得多。

风险判定之后,高风险和中风险的上报会自动生成跟进任务。这个逻辑我觉得有必要提到:不是所有上报都需要医生立即处理,低风险记录进入常规队列即可。我用@Async注解把任务生成的逻辑放到独立线程池里执行,避免居民端提交接口因为要写多张表而变慢。线程池在Spring Boot里的配置不复杂:

@Bean("taskExecutor") public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(200); executor.setThreadNamePrefix("report-task-"); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; }

这个CallerRunsPolicy值得说一下——当任务队列满了的时候,不会丢弃任务,而是退回调用线程执行,保证数据不丢。公共卫生数据丢一条都可能导致漏报,宁可接口慢几毫秒也不能丢数据。

3.3 小程序端的请求封装与本地调试

小程序端的请求封装看似简单,但直接影响开发效率和线上稳定性。我在utils/request.js里统一封装了request方法,内部处理三步:从storage取token拼到Header、请求超时控制(我设为10秒)、响应码统一处理。同时我把接口地址按环境区分,通过wx.getAccountInfoSync().miniProgram.envVersion判断当前是开发版还是正式版,自动切换baseUrl。

有一个非常实用的调试技巧:在小程序开发者工具里勾选“不校验合法域名”,本地开发时后端跑在localhost:8080也能直接联调。但要注意,手机上预览时必须使用真机调试配合HTTPS域名,否则请求会被拦截。这个坑我帮不少同事踩平过。

关于热词里提到的“微信小程序中的视频下载”,在健康宣教模块我用到了。小程序端播放宣传视频没问题,但下载视频到本地受限于小程序沙箱机制,官方并没有提供直接的视频下载API。我的方案是后端生成视频的临时链接返回给小程序端,用户长按识别二维码跳转到浏览器播放页,再由浏览器完成真正下载。这不是妥协,而是平台安全策略下的合理绕行。

3.4 文件上传与XSS过滤的坑

居民上报时可能会上传症状图片,我用的是Spring Boot的MultipartFile上传接口,文件存本地磁盘,数据库只存相对路径。这里有几个生产环境必须处理的问题:文件类型校验不能只看扩展名,要检测文件的真实MIME类型,防止上传恶意脚本;单个文件大小限制在5MB以内,用spring.servlet.multipart.max-file-size配置;上传目录要按日期分文件夹,避免单目录文件过多。

热词里有一条关于“全局过滤器处理上传PDF文件时XSS攻击”的讨论,我在图片上传场景里同样处理了安全问题。做法是写一个OncePerRequestFilter,重写请求流,将上传内容中的危险字符转义。对于普通表单提交,继承HttpServletRequestWrapper重写getParameter方法,统一过滤<script>等标签;对于JSON请求体,在Controller层通过@RequestBody的前置处理或AOP做转义。双管齐下之后,Web应用防火墙扫描基本能过。

4. 开发中常见的坑与排查实战

4.1 微信小程序安全域名与本地联调

这个是新入行的朋友最容易卡住的地方。小程序线上环境要求所有请求域名必须HTTPS且在后台配置白名单,本地联调阶段会遇到两种情况:在开发者工具里可以勾选“不校验合法域名”绕过限制,但只要需要在真机上预览调试,就必须有HTTPS域名。

我的建议是开发初期直接申请一个测试域名,配置Nginx反向代理到本地后端,同时申请免费的SSL证书。这样开发全程走HTTPS链路,开发的最后阶段就不会突然发现所有接口都请求不通。在Nginx配置里需要注意,微信要求TLS版本不低于1.2,SSL证书要完整配置证书链,有些免费的证书只给叶子证书,不配上中间证书会导致部分Android手机握手失败。

4.2 小程序端的缓存与数据一致性

健康宣教页面有大量图文内容,每次都从服务器拉取不仅慢还费流量,我在小程序端用wx.setStorageSync做了简单的缓存策略。核心文章的列表缓存30分钟,详情页缓存24小时,缓存的key带上文章ID,用户下拉刷新时强制更新。

这就涉及一个容易忽略的问题:缓存时间到底怎么设。热门新闻类的宣教内容时效性要求低,可以缓存到当天结束;而公告通知类的内容必须实时更新,就不能走缓存。我在后端接口里加了一个cacheControl字段标注建议缓存时长,前端拿到后按接口的建议来缓存,比前端写死时间灵活得多。

这背后其实是个“数据新鲜度vs性能”的取舍问题。我个人的习惯是用“脏读窗口”来换取性能,让页面能以极快的速度渲染旧数据,后台静默拉新数据后再更新视图。微信小程序的wx.request没有内置这个能力,我在请求封装里自己实现了一个简单的响应拦截,数据到达后在页面onShow时检查缓存是否过期,过期就自动刷新。

4.3 Spring Boot版本与配置的兼容性

热词里提到“springboot版本太高”,这个我深有体会。有次接手旧项目升级Spring Boot,从2.3升到2.7,表面上看代码没动几行,结果启动直接报错。排查下来是spring-boot-starter-validation的包名从javax.validation迁移到了jakarta.validation,所有@Valid注解的import全部失效。类似的坑还有Redis客户端从Jedis切换到Lettuce导致的配置项变化、Spring Security的配置类方法弃用等。

我现在的原则是:新项目直接用当前稳定版本,旧项目不要轻易升大版本。如果确实要升级,先看官方的spring-boot-migrator工具能不能处理,再逐个模块验证兼容性。配置项的变化用官方文档对照表逐条排查,别信网上零散的博客,过时的配置项会让应用在某些环境下静默失效,非常难排查。

还有个小技巧分享:Spring Boot的配置明明改了却不生效,先查application.yml和application.properties同时存在的优先级问题。Spring Boot默认properties优先于yml,如果你两个文件都创建了,而且配置内容有冲突,实际生效的可能是你删掉的properties文件里那份旧配置。我在项目里强制规定只能使用一种配置文件格式,从根本上避免这个问题。

4.4 分页插件与数据量增长的博弈

管理端的上报列表、健康档案都需要分页,我用的是MyBatis-Plus自带的分页插件,配置一个PaginationInnerInterceptor即可。但用着用着会发现一个问题:深度分页场景下,比如查询第1000页,MySQL会扫描前几万行再丢弃,性能急剧下降。上报记录表过了几个月后数据量轻松突破几十万条,这个问题就显形了。

我现在的替代方案是:列表页的跳页功能限制在500页以内,超过这个范围的查询强制走“上一页/下一页”模式,用create_time < 上一页最后一条记录的时间这种键值分页代替LIMIT offset。接口入参从pageNum/pageSize改成cursor/limit,前端的改动也不大,但查询性能稳如老狗。如果数据量再上一个量级,还能配合ES做查询,但对于社区卫生场景,这套优化完全够用。

5. 部署落地与日常维护心得

5.1 服务器端部署的完整链路

整套系统的部署我用的还是经典的Docker + Nginx方案。后端服务的Dockerfile实现很常规:

FROM openjdk:8-jre-alpine COPY target/*.jar app.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "/app.jar", "--spring.profiles.active=prod"]

构建镜像时有个细节:JVM参数要显式配置在启动命令中,特别是-Xms和-Xmx。Docker容器内的应用如果不限制堆内存,JVM会默认按宿主机内存的1/4来分配,如果宿主机部署了多个容器就容易OOM。我一般给后端服务分配512MB堆内存,配置-Xms256m -Xmx512m,配合容器本身的memory limit一起限制。

Nginx层除了常规的静态资源服务和反向代理,还有一个值得说的配置项——client_max_body_size,默认1MB的上限会让图片上传直接413。我在上传场景里把它调到10MB,同时后端也做了对应的文件大小限制,双层配置一致才能保证链路通畅。

部署完成后,我一般会用一条命令做快速的健康检查,看后端是否启动成功:

docker logs --tail 50 springboot-app | grep "Started Application"

看到Started Application in x.xxx seconds就说明进程正常拉起,这时候再检查Nginx的转发配置、HTTPS证书是否有效、数据库连接池是否就绪,整套服务的发布就算完成了。

5.2 日志与监控的配置经验

上线之后最怕的不是功能出bug,而是bug出现时什么线索都没有。我在项目里做了三件事来保障线上可观测性:日志分级输出、关键接口耗时统计、定时任务执行记录。

日志配置用Logback,结合Spring Boot的logging.level配置,生产环境把com.example.mapper的日志级别设为INFO而不是DEBUG,避免打印大量SQL日志拖慢性能。同时给关键的业务节点加了log.info输出,比如“居民上报成功-用户ID-报告ID-风险等级”,这条日志是后续排查问题的关键锚点。

接口耗时统计我用的最简单的方式,在拦截器里记录每个请求的开始时间和结束时间,超过2秒的请求单独打一条WARN日志。上线那么久,这条日志帮我发现过几次慢SQL问题——某天突然大量请求超过2秒,一查是某张表的全表扫描,加了索引瞬间解决。

定时任务执行记录则要单独建一张task_log表,每次定时跑批开始时插入一条记录,结束时更新状态和耗时。否则哪天凌晨的汇总报表数据不对,你都没法确定是任务没跑还是数据源头有问题。

5.3 我个人的几个避坑心得

整个项目做下来,我积累了几条可能对你有用的经验:

第一,涉及微信小程序登录的接口,务必先做code2Session的真实调用测试,别在本地Mock数据自嗨。微信的appid和secret配置错误率特别高,尤其是复制粘贴时偷偷多带了一个空格,这种错误调试半天都发现不了。我后来写了个启动检查——后端启动时自动拉取微信接口验证密钥,无效就直接启动失败,倒逼配置必须正确。

第二,小程序端的openid是敏感信息,不能直接暴露给前端JSON。我给居民表设计时故意不返回openid,而是返回一个自增的userId,前端所有操作都用userId传参。这样即使某天用户数据被爬,别人也拿不到原始的openid去冒充身份。

第三,所有状态流转的操作必须幂等。居民端可能存在用户双击提交、管理端医生重复点击审核按钮的情况。我在审核接口上加了状态校验,只有status为待审核的记录才允许审核,一旦变更就返回“该记录已处理”的提示。这个防重复操作的设计在公共卫生场景太重要了——错位审核会直接导致真实患者的处理被覆盖。

第四,给所有接口补上参数校验,别为了一时省事只校验非空就完事。我遇到过居民端传了一个temperature=-1的体温值,后端没做范围校验直接入库,导致统计图表上出现一个离谱的负值点。从那以后所有数值字段都加了@Min和@Max校验,用Bean Validation统一处理。

这套系统上线至今稳定运行,社区医生反馈最多的一个点是“跟进任务列表救了大命”——以前靠本子记着哪个疑似病例明天该随访了,现在系统会自动弹出来。也许这就是这类系统的价值所在:技术和业务的边界融合得很好,一个用Spring Boot写出来的程序,真的参与到了公共卫生的日常防护中。如果让我给这个项目再加一个扩展方向,我会选择对接上级疾控平台的自动上报接口,让数据流从社区一路通到区级、市级,那才是传染病防治信息化的完整链条。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询