尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

SpringBoot+Vue+MySQL疫苗预约系统全栈项目实战解析

SpringBoot+Vue+MySQL疫苗预约系统全栈项目实战解析 做信息管理系统这些年我接过不少类似的需求也帮周围同学和同行排查过无数个“明明按教程走就是跑不起来”的鬼故事。今天拿一套疫苗发布和接种预约系统出来彻底拆一遍这是非常典型的 SpringBoot Vue MySQL 全栈项目后端 SpringBoot前端 Vue数据库 MySQL刚好覆盖一条完整的中小型管理平台链路。整套源码本地实测过环境配好就能跑特别适合拿来做毕业设计、课程设计或者作为全栈练手项目参考。这类系统的现实意义不用多说——疫苗从入库、发布到预约接种整个流程如果靠手工登记和微信接龙管理很容易出现信息不同步、名额超卖、现场排长队的问题。而一套信息管理系统要解决的核心问题就两个一是让管理员能高效发布疫苗信息、管理接种点库存二是让普通用户能随时查看可预约的疫苗、在线完成预约、按时去现场接种。技术上的难点反而不是功能多复杂而是如何保证“名额不超卖”“状态不混乱”“操作可追溯”这是所有疫苗预约类系统都会踩的坑也正是这套源码里最值得研究和复用的一部分。下面我按项目梳理、技术选型、数据库设计、后端实现、前端实现、本地运行、问题排查这条线事无巨细地聊一遍尽量把源码里看不到的设计思路也补出来。1. 项目整体设计与需求拆解1.1 拆开标题看本质需求标题里写着“疫苗发布和接种预约系统”但源代码打开后你会发现它其实是一个标准的双端管理平台一个普通用户使用的预约前端一个管理员使用的管理后台。把业务拆开核心的数据流转大概是这样的管理员录入疫苗种类、批次、生产企业、有效期并维护接种点的可预约库存。管理员发布公告用户登录后可以看到最近到货的疫苗和接种点开放信息。普通用户选择疫苗、选择接种点、选择时间段系统校验是否还有剩余名额。校验通过后生成一条预约记录同时扣减接种点的剩余名额。用户按时到场管理员在后台将预约状态更新为“已接种”流程结束。用户也可以取消未接种的预约取消后名额自动释放回接种点。这套流程表面上不复杂但每一步都牵扯到状态变化和数据一致性。做这类系统最忌讳的就是把功能想得太简单直接在 Controller 里堆 SQL结果预约并发一上来库存就变负数或者同一个用户重复预约成功。源码里比较可取的一点是预约处理时对名额扣减做了条件更新后面我会详细讲。1.2 功能模块梳理从源代码的包结构和前端路由可以反推出完整的模块清单我整理成表格方便对照模块功能点对应角色用户认证注册、登录、个人信息查看与修改普通用户疫苗信息疫苗列表、疫苗详情、批次与有效期展示普通用户 / 管理员预约接种选择接种点、时间、提交预约、取消预约普通用户我的预约查看历史预约记录、当前状态普通用户公告管理发布疫苗到货信息、系统通知管理员接种点管理维护接种点名称、地址、开放时间、库存管理员疫苗管理新增、编辑、上下架疫苗管理员预约记录管理查看全部预约、确认接种、过期处理管理员用户管理用户列表、禁用/启用账号管理员这个模块划分基本覆盖了实际业务里能想到的全部主体操作。对做毕设的同学来说照着这个功能清单去写说明文档也能把“需求分析”这一章节写得非常丰满。这里提一句模块设计时把“公告管理”放在管理端而不是直接放在用户端是更合理的做法因为后台内容需要有人审核和发布权限边界清楚系统才不会乱。1.3 这套源码“可直接运行”意味着什么“可直接运行”这四个字在源码圈里的含金量差别很大。碰到过不少号称可直接运行的代码下载下来要么缺数据库脚本要么 node_modules 没带要么端口和配置跟本机环境完全对不上光折腾环境就能劝退一批人。这套源码比较省心的地方在于数据库脚本齐全、前后端分离结构完整、配置文件位置集中只要把 MySQL 的连接参数改掉基本三步就能跑起来——建库导数据、启动后端、启动前端。不过也别高兴得太早能不能跑起来是一回事跑起来之后能不能理解每一行代码在干什么就是另一回事了。所以我后面所有章节都会结合源码目录结构和关键类一点一点拆开讲。2. 技术选型逻辑解析2.1 后端框架为什么是 SpringBoot 而不是 SSM 或别的SpringBoot 在后端开发中的地位已经不需要再论证了。它最大的价值不是技术本身有多新奇而是把 Spring 生态里那一大堆 XML 配置、繁琐的 Bean 装配给自动处理了开发人员只需要通过注解和少量配置就能把一个 Web 应用跑起来。对疫苗预约系统这种 CRUD 为主、外加少量业务规则的场景SpringBoot Spring Data JPA 或者 MyBatis 都是非常成熟的选择。源码里用的是 SpringBoot 的标准分层结构Controller 层负责接收请求、Service 层处理业务逻辑、Repository/Mapper 层做数据访问、entity 包放实体类、dto 包放前后端交互的数据对象。这种分层看起来有点死板但恰恰是最适合中小型管理系统长期维护的。不夸张地说我刚带过的几个实习生能把这套分层结构吃透出去面全栈开发初级岗基本问题不大。SpringBoot 内嵌 Tomcat 这一点也要提一下。传统 SSM 项目需要额外配置 Tomcat而 SpringBoot 打出来的 jar 包可以直接通过java -jar启动部署成本极低。做毕设答辩演示的时候直接运行一个 jar 包比在 IDE 里折腾一堆配置要体面得多。2.2 前端框架Vue 的核心优势Vue 在当前前端圈的使用量非常大核心原因就是上手曲线平缓、组件化设计合理、中文生态资料丰富。这套系统的前端用的是 Vue 单页应用方案配合 Vue Router 做路由管理axios 发 HTTP 请求UI 部分直接引入 Element UI或类似组件库所以页面看起来很规整表单验证、弹窗、表格分页这些都不用自己从零写。Vue 的响应式数据绑定在提交预约表单时非常舒服。用户在页面上选择疫苗后系统可以自动联动加载对应接种点的剩余名额选择时间片段后提交按钮的动态禁用、剩余名额不足的红色提示都能用v-model和computed轻松实现。这些在 jQuery 时代都得写一堆 DOM 操作而在 Vue 里是数据驱动视图代码可读性会高出一个档次。有一点值得提醒如果下载到的 Vue 代码是 2.x 版本建议保持原版本不变别急着升级到 Vue 3。因为 Element UI 和大量现成组件库在 Vue 3 下需要切换到 Element PlusAPI 和写法都有不少变化升级可能引入新的兼容问题。做项目以稳为主能跑能改比追新重要得多。2.3 数据库MySQL 的适用场景MySQL 是这个系统的数据基石。它是一个关系型数据库数据严格结构化支持事务和外键正好匹配预约记录、用户信息、疫苗库存这类强关系数据。数据库版本方面5.7 和 8.0 都可用。如果本机安装的是 8.0远程连接时需要注意认证插件问题——MySQL 8.0 默认的caching_sha2_password认证方式某些旧版本的数据库连接驱动不一定支持容易出现Public Key Retrieval is not allowed这类报错。解决办法有两个要么把数据库用户密码改回mysql_native_password方式要么在数据库连接 URL 上增加allowPublicKeyRetrievaltrue参数。这是新手最容易卡一天的坑这里先提个醒后面问题排查章节还会再展开。为什么这套系统不需要 Redis很多同类预约系统会引入 Redis 做库存预扣减但代价是引入了分布式一致性的复杂度。如果接种点规模不大、单机并发也不过几十上百MySQL 的事务和行级锁完全能扛住没必要为了技术炫技增加系统负担。这套源码用 MySQL 自带的条件更新来解决并发扣减方案更轻量也更适合作为教学和毕设项目。3. 数据库设计与表结构拆解3.1 核心数据表有哪些打开源码里的数据库脚本文件大概会看到六到七张核心表我把它们梳理一下表名核心字段作用userid, username, password, real_name, phone, id_card, role, status存储用户和管理员账号vaccineid, name, manufacturer, batch_no, category, description, status存储疫苗基础信息vaccination_siteid, name, address, open_time, total_capacity, available_count, version存储接种点信息和剩余名额appointmentid, user_id, vaccine_id, site_id, appointment_date, time_slot, status, create_time存储预约记录announcementid, title, content, create_time存储公告vaccine_stock可能有id, vaccine_id, site_id, stock_count疫苗批次在接种点的分布库存这里拿vaccination_site表特别注意一下可用名额字段。预约系统最怕的就是库存不对所以这位作者设计了一个available_count字段同时很可能还带了一个version字段用于乐观锁或者至少会在更新时加条件判断这是整个系统正确性的关键所在。3.2 名额扣减的并发设计预约场景下两个用户同一秒预约最后一个名额如果代码是“先查剩余数量再在内存里减一再写回数据库”最后很可能出现负数库存或者超卖。这种问题在写代码时不容易被发现往往要等到上线后被并发请求打爆才会暴露。这套系统采用的是一个比较经典的方案更新语句里加条件判断让数据库在事务层面保证原子性。核心 SQL 长这样UPDATE vaccination_site SET available_count available_count - 1 WHERE id ? AND available_count 0;注意最后那个available_count 0条件。只有库存还有剩余时这条 UPDATE 才会更新到行返回的影响行数为 1如果已经没有名额影响行数就是 0服务端据此判断预约失败并返回“名额不足”。这种方式在低并发场景下非常简单可靠不需要引入分布式锁也不容易出现死锁。在 Java 代码里对应的逻辑要放在一个事务方法中通常用Transactional注解确保“检查用户是否已预约”“扣减名额”“插入预约记录”三个操作要么全部成功、要么全部失败。事务一旦回滚名额和预约记录都会恢复到操作前状态。3.3 预约状态机设计一个完整的预约状态流转是有规矩的不能随便从“已预约”直接跳到“已接种”。一般会定义这几个状态待接种预约成功等用户到场。已取消用户主动取消名额已经释放。已接种管理员确认用户已完成接种。已过期预约时间段已经过去用户没有到场系统定时任务自动处理。状态流转的核心规则是从待接种可以到已取消、已接种、已过期从已取消就不能再改从已过期也不能被确认接种。代码里用 switch 或者简单的状态判断就能实现。这个小设计看起来不起眼但是能有效防止管理员误操作把已取消的预约改成已接种或者把过期的预约又恢复成待接种。取消预约时别忘了同步恢复名额。这个操作要放在同一个事务里否则会出现用户取消成功但名额没回来的问题。我见过不少二手代码在这块偷懒预约记录状态改了库存却纹丝不动最后管理员只能手动去数据库里改字段非常痛苦。4. 后端核心实现与关键代码解析4.1 工程结构与启动入口后端项目的包结构大概是这样的src/main/java ├── com.example.vaccine │ ├── VaccineApplication.java // 启动类 │ ├── config/ // 配置类CORS、拦截器、定时任务 │ ├── controller/ // 控制层AuthController、VaccineController、AppointmentController、AdminController │ ├── service/ // 业务层 │ │ ├── impl/ │ ├── mapper/ 或 repository/ // 数据访问层 │ ├── entity/ // 实体类 │ ├── dto/ // 请求和响应对象 │ └── util/ // 工具类JwtUtil、Result封装启动类是一个标准的 SpringBoot 入口内容非常简单SpringBootApplication MapperScan(com.example.vaccine.mapper) public class VaccineApplication { public static void main(String[] args) { SpringApplication.run(VaccineApplication.class, args); } }如果你用的是源码压缩包第一件事就是确认pom.xml里的 SpringBoot 版本和你本机的 JDK 是否匹配。比如 SpringBoot 2.7 支持 JDK 8 和 11SpringBoot 3.x 则强制要求 JDK 17。这点不匹配编译必报错而且是那种一眼看不懂的抽象错误。4.2 认证与权限设计系统的登录认证用的是 JWT 方案这是一个相对轻量的有状态替代。用户登录成功后服务端生成一个带有效期的 token 返回给前端前端存在 localStorage 里之后每次请求在请求头带上Authorization: Bearer token。后端通过拦截器统一校验 token解析出用户 ID 和角色然后放进 ThreadLocal 或请求上下文里供业务方法使用。拦截器的核心逻辑大致如下public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { if (OPTIONS.equals(request.getMethod())) { return true; // 放行跨域预检请求 } String token request.getHeader(Authorization); if (token ! null token.startsWith(Bearer )) { Claims claims JwtUtil.parseToken(token.replace(Bearer , )); if (claims ! null) { request.setAttribute(userId, claims.get(userId)); request.setAttribute(role, claims.get(role)); return true; } } response.setStatus(401); return false; } }角色权限的判断一般在 Controller 层用注解或方法内判断比如if (!ADMIN.equals(role)) { return Result.error(无权限); }。如果项目规模再大一点可以引入 Spring Security 或 Shiro但当前场景下一个拦截器加角色判断完全够用还省掉了一堆安全框架的配置成本。这里特别提醒一下JWT 的密钥千万不要硬编码在代码里还提交到公开仓库至少要放到application.yml配置里。虽然这是个教学项目但养成习惯很重要。另外 token 过期时间别设太长7 天以内比较稳妥。4.3 预约接口的业务编排预约功能是整个系统最核心的接口。代码层面处理流程如下从 token 里拿到用户 ID查询用户状态是否正常。检查该用户当前是否已有待接种的预约防止重复预约。根据前端提交的 vaccineId、siteId、预约日期和时间段校验疫苗和接种点是否存在且可用。执行 UPDATE 扣减名额判断影响行数。插入预约记录初始状态为“待接种”。所有步骤在Transactional中完成任何一步失败都整体回滚。这段逻辑对应的 Service 方法大致长这样Transactional(rollbackFor Exception.class) public Result createAppointment(AppointmentRequest req, Long userId) { // 1. 校验用户 User user userMapper.selectById(userId); if (user null || user.getStatus() 0) { return Result.error(用户不存在或已被禁用); } // 2. 防止重复预约 int count appointmentMapper.countPendingByUserId(userId); if (count 0) { return Result.error(您已有待接种的预约); } // 3. 校验接种点与库存条件更新 int rows siteMapper.decreaseAvailableCount(req.getSiteId()); if (rows 0) { return Result.error(该接种点暂无剩余名额); } // 4. 插入预约记录 Appointment appointment new Appointment(); appointment.setUserId(userId); appointment.setVaccineId(req.getVaccineId()); appointment.setSiteId(req.getSiteId()); appointment.setAppointmentDate(req.getAppointmentDate()); appointment.setTimeSlot(req.getTimeSlot()); appointment.setStatus(PENDING); appointmentMapper.insert(appointment); return Result.success(预约成功); }注意decreaseAvailableCount这个方法的 SQL 就是前面说的条件更新。这一段代码也是最容易被面试官追问的地方能讲清楚“为什么这里要用条件更新而不是先查后减”说明你是真的理解并发场景而不是只会写 CRUD。4.4 定时任务自动过期处理预约系统还有一个绕不开的场景约了不来。如果系统不处理这种“占着名额不过期”的记录一段时间后库存会被大量无效预约耗尽。解决办法是用 Spring 自带的Scheduled定时任务每天凌晨或者每小时扫描一次超过预约日期但状态还是“待接种”的记录改成“已过期”同时恢复对应名额。Component public class ExpireTask { Scheduled(cron 0 0 * * * ?) // 每小时执行一次 public void expireAppointments() { ListAppointment pendingList appointmentMapper.selectExpiredPending(); for (Appointment appointment : pendingList) { appointment.setStatus(EXPIRED); appointmentMapper.updateById(appointment); siteMapper.increaseAvailableCount(appointment.getSiteId()); } } }这里的坑在于判断“过期”的时间标准。预约日期是当天那么只要当前时间晚于预约时间段结束时间就视为过期。写成 SQL 时要注意时区问题now()和 Java 的LocalDateTime.now()必须保证在同一时区否则会出现刚预约就被过期的诡异 bug。上线前可以用一条模拟数据手动测试一下这个任务。5. 前端核心实现与交互细节5.1 前端项目结构与路由前端采用 Vue CLI 创建的单页应用目录结构一般长这样src ├── api/ // axios 接口封装 │ ├── auth.js │ ├── vaccine.js │ ├── appointment.js ├── assets/ ├── components/ // 公共组件 ├── router/index.js // 路由配置 ├── views/ // 页面组件 │ ├── Login.vue │ ├── Register.vue │ ├── VaccineList.vue │ ├── Appointment.vue │ ├── MyAppointment.vue │ ├── admin/ │ │ ├── AdminHome.vue │ │ ├── VaccineManage.vue │ │ ├── SiteManage.vue │ │ ├── AppointmentManage.vue ├── App.vue └── main.js路由配置里需要做权限控制。简单做法是在路由元信息里加requiresAuth和requiresAdmin然后在全局前置守卫里判断 token 和角色router.beforeEach((to, from, next) { const token localStorage.getItem(token); if (to.meta.requiresAuth !token) { next(/login); } else if (to.meta.requiresAdmin localStorage.getItem(role) ! ADMIN) { next(/); } else { next(); } });这个守卫写得好不好直接决定系统是不是“裸奔”状态。很多管理系统后端接口保护得不错但前端路由却不做拦截结果普通用户直接改 URL 就能访问管理后台页面虽然接口会拒绝但体验和安全性都很糟糕。5.2 axios 请求封装与统一错误处理axios 封装是前端项目里最值得复用的代码。源码里通常有一个request.js做几件事设置 baseURL、请求拦截器里加 token、响应拦截器里统一处理错误码。import axios from axios; import { Message } from element-ui; import router from /router; const request axios.create({ baseURL: /api, timeout: 10000 }); request.interceptors.request.use(config { const token localStorage.getItem(token); if (token) { config.headers[Authorization] Bearer token; } return config; }); request.interceptors.response.use( response response.data, error { if (error.response error.response.status 401) { localStorage.clear(); router.push(/login); Message.error(登录已过期请重新登录); } else { Message.error(error.response?.data?.message || 网络异常); } return Promise.reject(error); } ); export default request;做了这个统一处理业务代码里就不需要到处写try catch和弹窗提示了。比如预约接口返回Result.error(名额不足)前端只需要判断res.code ! 200时提示一下即可。这种风格的项目代码看起来干净许多也更容易维护。5.3 预约表单的联动与防重复提交预约页面可能是整个前端交互最复杂的一页逻辑链路是选择疫苗 → 选择接种点 → 选择日期和时间段 → 看到剩余名额 → 提交预约。前端要做几件关键事情当用户选择疫苗和接种点后前端根据siteId调接口查询剩余名额并展示。提交按钮在名额为 0 或用户未完整选择时保持禁用。提交期间按钮进入 loading 状态防止用户连点产生重复请求。一个比较实用的提交逻辑片段async function submitAppointment() { if (submitting.value) return; submitting.value true; try { const res await createAppointment(form.value); if (res.code 200) { Message.success(预约成功); router.push(/my-appointment); } else { Message.error(res.message); } } finally { submitting.value false; } }这里的submitting变量是防重复提交的关键。就算用户双击按钮第二次进入函数直接 return不会发出重复请求。这个细节在预约场景里非常重要否则一次点击发出两个请求就会出现一个用户两条预约记录的情况。5.4 前端跨域与开发环境代理前后端分离项目本地联调最容易遇到的就是跨域问题。前端跑在http://localhost:8080后端跑在http://localhost:8081端口不同即跨域。解决办法有两个方向后端加 CORS 配置前端配置开发代理。后端加 CORS 配置是常见做法。一个简单的写法Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true); } }前端也可以在vue.config.js里配代理这样前端代码里写的请求地址都是/api/xxx由开发服务器转发到后端module.exports { devServer: { port: 8080, proxy: { /api: { target: http://localhost:8081, changeOrigin: true } } } };两种方案一起用其实也可以但要注意allowCredentials(true)和allowedOriginPatterns(*)的搭配SpringBoot 2.4 之后对allowedOrigins(*)加了不少限制用allowedOriginPatterns更省心。6. 本地运行全流程实录6.1 环境准备清单这部分说多了都是泪。真正做到“就像跑起来”的用户基本都是环境预先配好了的。我整理一份建议的环境版本避免版本冲突软件推荐版本检查命令JDK1.8 或 11对应 SpringBoot 2.xjava -versionMaven3.6.x 或 3.8.xmvn -vNode.js14.x 或 16.xnode -vnpm随 Node 版本npm -vMySQL5.7 或 8.0mysql --versionIDEIDEA 或 VSCode—如果你下载的源码是较新的 SpringBoot 3.x那 JDK 必须 17 以上Node 版本建议 16 以上。版本不匹配是最常见的启动失败原因没有之一。6.2 后端启动步骤先建数据库。打开 MySQL 命令行或 Navicat执行数据库脚本通常是一个vaccine.sql或db.sql文件里面建了库和表还插入了初始数据。执行完成后确认一下表是否都已创建。然后打开src/main/resources/application.yml修改数据库连接server: port: 8081 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/vaccine_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue username: root password: 你的数据库密码启动方式有两种在 IDEA 里直接运行VaccineApplication.java或在项目根目录执行mvn spring-boot:run看到日志输出Started VaccineApplication就说明后端起来了。为了验证接口是否可用可以在浏览器访问http://localhost:8081/api/vaccine/list如果返回 JSON 数据就说明数据库连接正常。6.3 前端启动步骤前端启动最怕的就是 npm 依赖安装失败。进到前端项目目录先安装依赖npm install国内用户如果下载慢或者卡住建议先切换 npm 镜像源npm config set registry https://registry.npmmirror.com安装完成后启动开发服务器npm run serve看到App running at: http://localhost:8080就成功了。浏览器访问这个地址用初始化账号登录。初始账号一般会在数据库脚本或 README 里注明比如admin / 123456和user / 123456。6.4 直接运行常见的小坑源码“可直接运行”不代表完全没有坑总结几个高频问题MySQL 连不上90% 是因为application.yml里的密码没改成自己本机的密码或者没有启动 MySQL 服务。数据库驱动版本不匹配MySQL 5.7 用com.mysql.jdbc.Driver或com.mysql.cj.jdbc.Driver都行但 MySQL 8.0 必须用后者否则会报 ClassNotFound。Node 版本过高Vue 2 项目在老版本依赖下Node 17 以上可能会报opensslErrorStack。解决办法是降 Node 版本或者在启动命令里加NODE_OPTIONS--openssl-legacy-provider。前端请求接口 404确认后端端口是否真的在8081以及前端代理或 axiosbaseURL是否匹配。端口被占用后端端口被占用时改application.yml里的server.port前端端口被占用时可以执行npm run serve -- --port 8082临时换端口。7. 常见问题与排查技巧实录7.1 启动期的报错速查表现象常见原因处理方法Port 8081 was already in use端口被占用找到占用进程杀掉或改后端端口Access denied for user rootlocalhost数据库密码错误或权限不足核对application.yml的密码Unknown database vaccine_db没有执行 SQL 脚本建库重新执行建库脚本Failed to bind properties配置项名称写错对照官方配置检查缩进和单词拼写npm install 卡住不动网络原因切换镜像源重试Cannot find module node-sass依赖安装不完整删除 node_modules重新 install前端页面能开但接口全 404代理配置错误或后端没启动分别检查后端能否访问、代理转发是否有效排查这类问题有个笨办法但很有效先把后端接口通过浏览器或 Postman 直接调用确认后端没问题之后再去查前端代理这样能把上下两层问题隔离开不至于瞎猜。7.2 业务逻辑层面的隐蔽 Bug相比启动问题业务逻辑的坑更隐蔽编译期不会报错运行期也不会崩溃但数据会慢慢乱掉。重点提醒几个库存为负如果扣减名额没有加available_count 0条件高并发下必然出现负数。检查siteMapper里的 SQL。重复预约如果创建预约时没有检查用户已有待接种记录同一个用户可以“开挂”约好几条。需要确认createAppointment方法里是否有查询校验。取消预约名额不恢复cancelAppointment方法必须同时做更新预约状态和恢复库存。如果用了事务要确认两个操作在同一事务里。日期比较时区 bug数据库的datetime和 Java 的LocalDateTime如果时区不一致会出现“当天预约被判定为过期”之类的问题。连接 URL 里加serverTimezoneAsia/Shanghai可以缓解。调试这类问题我的习惯是在 Service 里加关键日志比如扣减影响行数、预约记录 ID、状态变化前后的值。日志是定位业务 bug 的第一利器别老想着单步调试分布式环境下日志比断点好用得多。7.3 代码可优化与扩展的方向作为毕设如果想让项目看起来更“有水平”在现有源码基础上做这几个扩展会很加分验证码登录接入图形验证码或短信验证码提升系统真实感。疫苗批次追溯在疫苗表基础上增加批次入库记录和出库记录做到来可溯、去可查。预约放量策略不同时间段设置不同放量名额而不是所有时段一个值。统计报表用 ECharts 画出每日预约人数、疫苗种类预约占比、接种点负荷分析充分展示前后端能力。微信小程序端如果有余力复用后端接口写一个小程序预约端直接让项目的完整度上一个大台阶。个人体会是这套项目的价值不在于代码量而在于它覆盖了“预约资源”这种通用业务的核心难点。把库存、状态机、事务、权限这些点学透后面无论换什么业务会议室预约、门诊挂号、图书馆座位都能平移过去。最后再分享一个小技巧如果你拿到源码后第一时间不是去启动而是先读数据库脚本把每个表的字段和注释过一遍再对着启动后的页面操作一遍你会对一个陌生项目的理解速度快很多。我每次接手别人的项目都是这个习惯十次有九次都比直接看代码更高效。
返回列表