多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

SpringBoot+Vue+MyBatis全栈实战:疫情防控管理系统源码解析与部署指南

SpringBoot+Vue+MyBatis全栈实战:疫情防控管理系统源码解析与部署指南 最近后台收到不少刚入行的同学留言都在问能不能找一套“能直接跑起来、代码结构又比较规范”的管理系统源码作为练手项目。刚好我手上有一套基于 SpringBoot Vue 的疫情防控管理系统用到了 MyBatis 和 MySQL是 2025 年重新整理过依赖版本和前端构建流程的版本。这套东西虽然名字带着“疫情”但本质上就是一个标准的“健康信息上报 后台管理”全栈项目把用户端、管理端、数据统计、权限控制都串了起来。无论你是想拿来应付毕业设计还是想学 SpringBoot 和 Vue 的整合方式甚至是想参考一下前后端分离项目的真实目录结构和接口设计都有很直接的参考价值。这篇文章我会从技术选型到数据库设计把核心前后端接口的实现思路和踩过的坑全部摊开来讲尽量做到让一个刚学完 SSM 或 Vue 基础的人也能跟着做出来。文章里的代码都是我整理过的核心片段可以直接抄进自己的项目里改一改再用。1. 项目整体设计与技术选型思路1.1 为什么是 SpringBoot Vue MyBatis MySQL 这套组合先说结论这套组合在 2025 年依然是中小型管理系统里性价比最高的选择没有之一。SpringBoot 解决的是后端“配置地狱”的问题。如果你以前写过 SSM 整合一定记得那一堆 XML 配置、扫描包配置、事务配置。SpringBoot 用自动配置把这些全压下去了一个SpringBootApplication启动类就能跑起整个项目。而且它内置了 Tomcat部署的时候不用再单独装容器一条java -jar就能上线。Vue 这边我选的是 Vue 3 Vite 的组合。2025 年 Vue 2 已经进入维护末期新项目再写 Vue 2 属于给自己挖坑。Vite 的冷启动速度和热更新体验比 Webpack 好太多了尤其在你前后端分离开发的时候改一行前端代码页面马上刷新这种效率提升是实打实的。当然如果你手里拿到的老版本源码还是 Vue 2 Webpack也能跑但建议尽早迁移后面我会提几个迁移的关键点。MyBatis 和 MySQL 这一层主要是为了灵活和轻量。项目里有大量动态查询条件比如按日期、按姓名、按状态、按地区组合查询健康上报记录MyBatis 的动态 SQL 写起来比 JPA 里的 Specification 直接得多。而 MySQL 本身就是最常见的生产级数据库配合 Navicat 或 DataGrip 调试非常方便。1.2 系统需要解决的核心业务问题这套系统的业务背景不复杂但流程比较典型很适合作为全栈练手。围绕“健康信息上报与管控”主要解决三个问题用户端每天提交自己的体温、行程、健康码状态、异常症状等信息。管理端各级管理员查看所辖范围的上报数据处理异常人员发布通知。数据端对上报数据进行汇总统计支持按日期、区域、状态多维度筛选导出。这三个问题对应到系统里就是三个核心模块用户上报模块、后台管理模块、数据统计模块。再加上支撑这些模块的登录认证和权限管理整个系统的骨架就出来了。我画过一张简单的功能脑图这里用文字描述用户端是首页填报 历史记录 个人信息管理端是人员管理 上报审核 异常处理 通知公告统计端是图表展示 报表导出。后端所有接口都按/api前缀统一暴露权限用 Spring Security JWT 控制。1.3 项目目录结构的规划一个好的目录结构能让你在写完第一版之后少掉一半的头发。我的后端包结构大概是这样的com.example.epidemic ├── config // 配置类Security、CORS、MyBatis分页等 ├── controller // 接口层接收参数返回Result ├── service // 业务层写业务判断和事务 ├── mapper // Dao层MyBatis接口 ├── entity // 实体类和数据库表对应 ├── dto // 数据传输对象封装请求参数和响应结果 ├── vo // 视图对象给前端展示用的封装 ├── utils // 工具类JWT工具、Excel导出工具 └── common // 统一返回结果、异常处理、常量前端 Vue 项目目录用标准 Vite 结构src ├── api // 所有请求接口封装 ├── assets // 静态资源 ├── components // 通用组件 ├── router // 路由配置 ├── store // Pinia 状态管理 ├── views // 页面组件 ├── utils // token 存取、axios 封装 └── App.vue这个结构的好处是前后端各层职责清晰controller 里只负责参数校验和调用 service不写任何业务逻辑。mapper 里只放 SQL 相关操作。这样就算以后想换掉 MyBatis 改 MyBatis-Plus 或者 JPA只需改 mapper 层上层完全不用动。2. 数据库设计与核心数据表解析2.1 数据表结构需要覆盖哪些维度数据库设计是这个系统里最不能糊弄的部分。我第一版设计吃了不少亏比如用户表里直接存角色名称导致后来加权限功能的时候还得拆表。所以这次重新整理我严格表了职责一共设计了 6 张核心表sys_user系统用户表包含登录账号、密码、姓名、手机号、所属部门、角色ID。sys_role角色表存角色编码和角色名称。user_health_report健康上报记录表存体温、症状、行程卡颜色、健康码颜色、所在地点、上报日期。user_contact_log行程轨迹记录表如果要扩功能可以用基础版可以合并到上报表里。notice_info通知公告表存标题、内容、发布人、发布时间。exception_log异常处理记录表记录异常人员的处理状态和跟进人。有些表之间通过外键逻辑关联但我不建议在数据库物理层建太多外键约束。原因很简单高并发场景下外键会额外消耗性能而且 MyBatis 本身并不依赖物理外键。只要在业务层保证数据一致性就够。如果你只是做毕设建物理外键也无所谓但作为有经验的开发我更倾向于用逻辑外键。2.2 健康上报表的设计要点健康上报表是整张系统的核心直接上建表语句CREATE TABLE user_health_report ( id bigint NOT NULL AUTO_INCREMENT, user_id bigint NOT NULL COMMENT 上报人ID, report_date date NOT NULL COMMENT 上报日期, temperature decimal(4,1) DEFAULT NULL COMMENT 体温, symptom_type varchar(255) DEFAULT NULL COMMENT 症状描述, health_code varchar(20) DEFAULT NULL COMMENT 健康码状态green/yellow/red, travel_code varchar(20) DEFAULT NULL COMMENT 行程卡状态green/yellow/red, location varchar(255) DEFAULT NULL COMMENT 当前位置, is_risk tinyint(1) DEFAULT 0 COMMENT 是否异常0正常 1异常, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_date (user_id,report_date), KEY idx_date (report_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT健康上报记录表;这里几个关键点我单独说明一下report_date用了date类型而不是datetime因为业务上一个人每天只能上报一次用日期做唯一性判断更精准。在业务层插入前会先查询当天是否已存在记录避免重复上报。health_code和travel_code用varchar存枚举字符串而不是用数字因为前端展示直接映射颜色和文字省了一层转换。虽然稍微浪费一点存储但胜在直观。联合索引idx_user_date非常重要。因为最频繁的查询就是“查某人某天的上报记录”和“查某天所有上报记录”这个联合索引能直接命中避免全表扫描。表名我用了user_health_report而不是health_report因为业务上每个用户都有多条记录前缀user_能更清楚地表达这层归属关系。2.3 用户表与角色表的权限设计用户表和角色表我做成了一对一关系一个用户只有一个角色因为这套系统的角色固定就三类普通用户、部门管理员、系统管理员。如果你以后扩展出多个角色改成一对多也就是拆一张中间表的事。CREATE TABLE sys_user ( id bigint NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL COMMENT 登录账号, password varchar(100) NOT NULL COMMENT BCrypt加密后的密码, real_name varchar(50) DEFAULT NULL, phone varchar(20) DEFAULT NULL, department varchar(100) DEFAULT NULL COMMENT 所属部门或区域, role_id bigint DEFAULT NULL, status tinyint(1) DEFAULT 1 COMMENT 账号状态1启用 0禁用, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT系统用户表;注意密码字段我预留了 100 长度因为 BCrypt 加密后的串是 60 位虽然 60 够用但留点余地更保险。登录时用 Spring Security 的BCryptPasswordEncoder校验密码明文存储这种事情千万别干一旦数据库泄露所有账号直接裸露。角色表更简单CREATE TABLE sys_role ( id bigint NOT NULL AUTO_INCREMENT, role_code varchar(50) NOT NULL COMMENT 角色编码ROLE_USER/ROLE_ADMIN, role_name varchar(50) NOT NULL, PRIMARY KEY (id), UNIQUE KEY uk_role_code (role_code) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;在controller层我会用PreAuthorize(hasRole(ADMIN))这类注解直接做接口级权限控制比在 Service 里手动 if 判断优雅得多。Spring Security 的过滤器链会拦截所有/api/**请求先解析 JWT再把用户 ID 和角色信息塞进SecurityContext业务代码里随时可以取。3. 核心功能模块的实现与踩坑记录3.1 登录认证JWT SpringSecurity 的整合细节先提一个最容易踩的坑Security 配置类必须放行 Swagger、放行登录接口、放行静态资源否则会白屏 401 半小时。我的安全配置核心思路是/api/auth/login完全匿名访问其他/api/**都必须带上Authorization: Bearer token。自定义 JWT 过滤器继承OncePerRequestFilter在过滤器里解析 token如果合法就把UsernamePasswordAuthenticationToken塞进上下文。JWT 工具类主要做三件事生成 token、解析 token、校验过期时间。这里我踩过一个特别经典的坑JWT 的过期时间不能设置太长否则用户被盗过一次 token 就能持续作恶。我最终把过期时间设成 24 小时前端在 axios 拦截器里检测到 401 就跳回登录页重新登录。前端 axios 封装的核心代码长这样// request.js import axios from axios import { getToken, removeToken } from /utils/auth const request axios.create({ baseURL: /api, // 通过 Vite 代理转发避免跨域 timeout: 10000 }) request.interceptors.request.use(config { const token getToken() if (token) { config.headers.Authorization Bearer ${token} } return config }) request.interceptors.response.use( response response.data, error { if (error.response.status 401) { removeToken() window.location.href /login } return Promise.reject(error) } )为什么用/api作为 baseURL 而不是写完整的后端地址因为我在vite.config.js里配了代理server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true, rewrite: path path.replace(/^\/api/, ) } } }这样前端代码里所有请求都是相对路径本地开发和生产部署都不需要改前端代码。生产部署时直接把前端 build 出来的文件扔进 SpringBoot 的src/main/resources/static/目录用同一个端口访问跨域问题直接不存在。3.2 用户健康上报的完整业务链路健康上报是用户端最核心的操作。业务上需要做到用户登录后进入填报页页面显示当前日期和上次上报信息。用户提交体温、身体状况、健康码颜色、行程卡颜色、当前位置。后端先校验当天是否已经上报如果上报过则返回“今日已上报请勿重复提交”。校验通过后插入记录并自动判断是否需要标记异常。判断异常的逻辑代码如下public void checkAndSetRisk(UserHealthReport report) { if (report.getTemperature() ! null report.getTemperature().compareTo(new BigDecimal(37.3)) 0) { report.setIsRisk(1); } if (red.equals(report.getHealthCode()) || red.equals(report.getTravelCode())) { report.setIsRisk(1); } if (StringUtils.hasText(report.getSymptomType())) { report.setIsRisk(1); } }这里我把“是否风险”的判断放到了 service 层而不是数据库层。好处是以后规则变了比如体温阈值从 37.3 改成 37.2只需要改这一处不用动 SQL。前端填报页我用了el-form做表单校验提交按钮加 loading 和防重复提交。用户狂点提交按钮时后端即使做了唯一性校验也可能因为并发同时插入两条。解决方法是先在数据库表上加UNIQUE KEY uk_user_date (user_id, report_date)然后业务层捕获DuplicateKeyException并转换为友好提示。这样即使并发也能保证一条数据。3.3 后台管理端的列表查询MyBatis 动态 SQL管理端最常用的就是列表查询上面我再加一个按日期范围筛选。MyBatis 的动态 SQL 写多了以后我觉得最需要注意的是每次查询必须带上 limit不能让用户一次查几十万条数据。分页插件用 PageHelper配置简单用法也直接但它和多数据源时不兼容一个项目里如果没有多数据源需求放心用。一个典型的管理端查询 Mapper XML 片段select idselectReportPage resultTypecom.example.epidemic.vo.ReportVO SELECT r.id, u.real_name, u.department, r.report_date, r.temperature, r.health_code, r.travel_code, r.is_risk FROM user_health_report r LEFT JOIN sys_user u ON r.user_id u.id where if testkeyword ! null and keyword ! AND (u.real_name LIKE CONCAT(%, #{keyword}, %) OR u.username LIKE CONCAT(%, #{keyword}, %)) /if if teststartDate ! null AND r.report_date gt; #{startDate} /if if testendDate ! null AND r.report_date lt; #{endDate} /if if testisRisk ! null AND r.is_risk #{isRisk} /if /where ORDER BY r.report_date DESC, r.id DESC /select这里有两个小细节CONCAT(%, #{keyword}, %)而不是直接在 SQL 里写%${keyword}%。前者是预编译不会产生注入风险后者是文本拼接一定要避免。MyBatis 的${}是字符串替换#{}是占位符凡是用户输入的参数一律用#{}。日期比较用gt;和lt;因为 XML 里和是保留字符不转义会解析报错。有些人会写![CDATA[ ]]能用但看起来丑我习惯用实体转义。3.4 数据统计与 Excel 导出统计模块我用的是 ECharts 图表。管理员进入首页的统计面板能看到今日上报人数、异常人数、各区域上报率趋势图。这些数据的获取逻辑可以统一走一个统计 SQL按日期分组SELECT report_date, COUNT(*) AS total_count, SUM(CASE WHEN is_risk 1 THEN 1 ELSE 0 END) AS risk_count FROM user_health_report WHERE report_date BETWEEN #{startDate} AND #{endDate} GROUP BY report_date ORDER BY report_date这张统计表每天都会增长如果查询范围太长必须加索引。我建了(report_date, is_risk)联合索引这条 SQL 走索引效率很高。关于 Excel 导出我踩过一个特别无语的坑用 EasyExcel 导出的时候如果实体字段中有日期类型默认导出的格式是一串数字必须加DateTimeFormat注解才能显示成年月日。后来我直接在 VO 里把日期转成字符串反而更稳。EasyExcel 的核心导出代码String fileName URLEncoder.encode(健康上报统计_ LocalDate.now(), UTF-8); response.setContentType(application/vnd.openxmlformats-officedocument.spreadsheetml.sheet); response.setCharacterEncoding(utf-8); String header attachment; filename*UTF-8 fileName .xlsx; response.setHeader(Content-Disposition, header); EasyExcel.write(response.getOutputStream(), ReportExportVO.class).sheet(上报数据).doWrite(dataList);前端请求导出时要注意必须设置responseType: blob否则下载下来的 Excel 打开会显示乱码知识。这个不加基本必踩。4. 常见问题与排查技巧实录4.1 前端请求后端跨域接口通了吗前端和后端分离开发时最频繁的问题就是跨域。我这里推荐最稳妥的方案后端配一个 CORS 配置类前端同时配 Vite 代理两边都做了生产环境下就不会因为跨域出幺蛾子。SpringBoot 的 CorsConfig 核心代码Configuration public class CorsConfig { Bean public CorsFilter corsFilter() { CorsConfiguration config new CorsConfiguration(); config.addAllowedOriginPattern(*); config.addAllowedHeader(*); config.addAllowedMethod(*); config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration(/**, config); return new CorsFilter(source); } }注意allowedOrigins(*)和allowCredentials(true)不能同时使用否则启动直接报错。因为浏览器规定这里不能带通配符必须用allowedOriginPatterns(*)。这是我之前从 Spring Boot 2.2 升级到 2.7 后踩过的雷升级到 2025 最新版同样适用。另外如果你后端用了 Spring Security跨域配置必须在 Security 过滤链之前生效。CorsFilter 需要注册在http.cors()之前否则 Security 的过滤器先返回 401CORS 配置根本没机会生效前端照样报跨域。4.2 MyBatis 控制台不打印 SQL调试很难受怎么办很多新手拿到源码后把项目跑起来结果后台日志看不到 SQL查不到 mapper 执行了什么语句。其实只需要在application.yml里加一行配置mybatis: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl加完这句话控制台就会打印完整的 SQL 和参数。但有一点要注意这个配置只建议在开发环境开启。生产环境会打印所有 SQL一方面有信息泄漏风险另一方面日志文件膨胀得非常快。所以我会把配置拆成application-dev.yml和application-prod.yml只在 dev 里开。另外如果你用的是 MyBatis-Plus可以分mybatis-plus.configuration.log-impl配置两者不一样别搞混了。4.3 SpringBoot 版本太高导致 javamail 或其它依赖冲突2025 年最新的 SpringBoot 可能已经到 3.5 了但如果你从网上下载的这套项目源码还是基于 SpringBoot 2.x拿最新 JDK 去跑会报一堆ClassNotFoundException或者UnsupportedClassVersionError。这里我建议不要盲目追求最新版先看你的 JDK 版本。SpringBoot 3.x 强制要求 JDK 17如果本地装了 JDK8老老实实用 SpringBoot 2.7.x 版本。我整理过一个版本兼容表照着配基本没有坑组件推荐版本备注JDK1.8 或 11SpringBoot 2.x17SpringBoot 3.x官方要求SpringBoot2.7.18 或 3.2.x长期维护版更稳MyBatis Starter2.3.x 或 3.0.x注意 SpringBoot 3 需要 jakarta 命名空间MySQL Connector8.0.33兼容 MySQL 5.7 和 8.0Vue3.4不用 Vue 2Vite5.x构建工具这里有个特别生动的类比项目的依赖关系就像装修房子水电、地板、家具必须匹配。你拿 4 分管的水管去接 6 分接口怎么都拧不上。JDK 版本就是水管的直径标准SpringBoot 版本就是接口尺寸两者不匹配后面不管写多少代码都跑不起来。4.4 MySQL 建库编码导致中文乱码我收到过好几个同学的求助页面显示正常但往数据库里插入中文全变成???。百万种情况里九成是建库时字符集没指定。必须在建库时显式声明CREATE DATABASE epidemic_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;在你已经建好库又不想删除重建的情况下可以执行下面这句补救ALTER DATABASE epidemic_db CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;同时数据库连接的 URL 里也要加上参数jdbc:mysql://localhost:3306/epidemic_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai缺了characterEncodingutf8的话即使数据库是 utf8mb4插入中文也有概率出乱码。4.5 前端打包后放入 SpringBoot刷新页面 404 的解决办法开发完成部署时如果你把 Vue 打包后的文件放到static目录下直接访问首页没问题但一刷新子路由比如/admin/user/list就会因为 SpringBoot 默认找不到这个路径而返回 404。解决办法是加一个路由转发配置让所有非 API 请求都转发到index.htmlController public class WebController { RequestMapping(value {/, /{path:^(?!api$).*$}/**}) public String forward() { return forward:/index.html; } }注意其中的正则排除掉了/api开头的路径避免把接口请求也转发到前端页面。这是前后端分离部署最经典的一个坑网上很多文章没讲清楚我这里单独写出来。5. 从源码到部署一套完整的操作记录5.1 环境准备与初始化先说跑起来需要哪些东西。我这里按最干净的 Windows 开发环境来举例Mac 或 Linux 差别不大JDK 1.8 或 17我建议直接装 17因为 SpringBoot 3 是以后主流虽然这套源码可以兼容 2.x但 17 能覆盖更多情况。Maven 3.8用来管理后端依赖和打包。Node.js 16 以上用来跑 Vite 前端工程。MySQL 8.0记得用 root 创建数据库。IDE后端用 IntelliJ IDEA前端用 VSCode 或者继续用 IDEA 都行。这些工具安装很简单但版本坑多。Node 版本太低 Vite 5 会报错Maven 版本太低拉不下来某些依赖。建议都装最新的稳定版。把源码拿到手之后第一步不要急着跑。先看一下项目的 README或者直接在application.yml里把数据库账号密码改成你自己的。这个步骤漏了99% 的报错都集中在连不上数据库上。5.2 本地启动的详细步骤与验证方法我的启动顺序从来都是先后端、再前端因为前端登录时要调后端接口后端没起来前端页面全是接口错误。后端启动在 IDEA 里打开后端项目根目录等待 Maven 下载依赖。第一次下载会比较慢建议在 Maven 的settings.xml里配置阿里云镜像能省一大半时间。修改application.yml里的数据库 URL、账号、密码。右键运行Application.java看到Started Application in x.x seconds就成功了。打开浏览器访问http://localhost:8080/api/auth/loginGET如果返回 405 而不是 404说明接口已经注册成功。前端启动在 VSCode 或 IDEA 的终端里进入前端项目目录。执行npm install装完依赖后执行npm run dev。看到VITE vx.x.x ready in xxx ms且打印出本地地址http://localhost:5173后用浏览器打开。如果页面能正常加载出登录界面但点击登录提示“获取用户信息失败”多数是代理没生效检查vite.config.js里的 proxy 配置。完整启动之后用管理员账号登录能看到仪表盘。随便点几个菜单看看接口返回数据和表格渲染是否正常。到这里一套源码就算是真正跑通了。5.3 生产环境部署的两种常见方式部署这种东西很多人觉得要买服务器很麻烦但其实两种方式都还行我就挑最省事的两条总结下。第一种是把前端 build 到后端用 SpringBoot 内嵌 Tomcat 直接启动。流程很简单# 前端构建 npm run build # 把 dist 目录下的文件复制到后端 src/main/resources/static/ # 然后打包后端 mvn clean package -DskipTests # 得到 jar 包后启动 java -jar epidemic-system.jar --spring.profiles.activeprod这种方式适合单机小项目或者毕业设计部署。好处是只需要一个端口、一个进程不用配置 Nginx。坏处是如果前端资源和后端接口形成同一个 Jar 包以后改前端得重新打包整个后端稍显笨拙因此更讲究一些的项目还是建议用 Nginx。第二种是前后端分开部署后端打 jar 单独跑在 8080 端口前端 build 后的静态文件放到 Nginx 的 html 目录Nginx 配置里把/api反向代理到http://localhost:8080。这样重构前端时不用碰后端生产环境应用的也只是少数几个文件。2025 年了我强烈建议学会 Nginx 这个部署方式——不管是实习还是正式工作这个都属于基础中的基础。Nginx 配置核心部分就这一段server { listen 80; server_name your.domain.com; root /var/www/epidemic-web; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }这里最重要的是第一条try_files $uri $uri/ /index.html;它解决了前文提到的刷新 404 问题。Nginx 在找不到路径时会把请求回退到index.html交给 Vue Router 处理从而保证子路由刷新正常。5.4 给新手的实操建议与拓展方向最后说几句掏心窝子的话。如果你是完全没写过全栈项目的新手我建议从这个项目入手时不要一开始就追求把所有功能都跑通而是先“死磕”一条链路登录、上报健康信息、管理端审核。把这一条链路里的前后端请求、数据流、权限校验全部搞懂比模糊地浏览所有页面有价值得多。如果已经有基础想让这个系统从“毕设水平”提升到“简历可写项目”可以从这几个方向扩展接入消息推送每天到点提醒用户上报可以用 Spring 的定时任务 微信模板消息或钉钉机器人。这个扩展能让系统看起来更完整因为“提醒”这是管理类系统的刚需。增加多级审批流异常人员处理改成多级审核比如班级管理员审核后还要学院管理员确认。这就涉及到工作流设计非常有含金量。用 Redis 做热点缓存比如把今日上报人数缓存到 Redis设置 5 分钟过期降低数据库压力。在简历里写上“缓存优化”这个关键词面试时就有话题可以聊。我个人实际折腾过一遍的感受是这类管理系统的难点从来不在技术选型而在于把“健康上报”这件事理解透谁能上报、谁必须上报、上报什么字段、谁可以看、谁可以改、异常怎么流转。把这些业务流程理顺了代码只是实现你想法的工具而已。最后再分享一个小技巧做任何全栈项目时一定要把前端每次请求的参数和后端接口返回的数据打印到控制台对比着看。很多时候前端调不通不是因为后端代码错了而是接口需要的字段名和你从表单里取出来的字段名对不上比如前端传了userId后端接收的却是user_id查不到数据就很自然了。我遇到的头号后端调试痛点基本都在这。
返回列表