ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

SpringBoot+Vue社区陪诊系统全栈开发实战指南

SpringBoot+Vue社区陪诊系统全栈开发实战指南 社区陪诊系统这个题目在近两年的毕业设计和简历项目里出现频率非常高。很多人一看到“基于SpringBootVue”就开始发怵觉得前端后端都得碰工作量太大。实际上如果你把需求拆开会发现它只是一个典型的管理系统加一个小型交易闭环用户下单、陪诊员接单、服务完成、评价结算。这个复杂度刚好适合用SpringBootVue这套前后端分离方案来承载既不会简单到没东西可讲也不会复杂到做不完。这篇内容我按自己实际开发这类项目的经验来写覆盖从需求分析、数据库设计、后端接口实现到前端页面联调、打包部署的完整链路。后端的SpringBoot项目怎么初始化、JWT登录怎么做、Redis缓存加在哪些位置前端的Vue路由怎么传参、Axios怎么封装、打包后布局为什么乱都会提到。适合准备做毕设、想练全栈项目或者刚从前端转后端第一次接触Java项目的人参考。1. 项目定位与整体设计思路1.1 陪诊系统到底在解决什么问题先把业务想清楚再写代码这是我从一开始就坚持的顺序。陪诊服务的核心场景是老人或者不熟悉医院流程的患者看病时需要有人帮忙挂号、取号、排队、取药、记录医嘱。家属通常要上班没法全程陪同于是就有了“按小时或者按半天购买陪诊服务”的需求。这里的关键点在于“社区”两个字。它意味着用户不是面向所有陌生人而是基于社区服务站点来组织陪诊员像网格员一样被分配到某个社区附近。对应的系统设计就必须包含几个基本角色患者或家属下单方、陪诊员服务提供方、社区管理员审核和监管、系统管理员平台维护。角色身份不同能看到的数据和操作的功能也不同这直接决定了后端需要做RBAC权限控制前端需要做动态路由和菜单权限。另一个容易忽略的点是“服务过程透明”。家属在上班时也想随时知道老人现在到哪个环节了是排队中、看诊中还是取药中。所以系统除了基础的下单接单流程最好加入订单状态流转记录让用户能实时看到进度。这个需求落到表结构设计上就是订单主表加订单状态变更记录表每次状态变化都落一条记录。1.2 为什么选SpringBootVue这套组合很多人在技术选型时会纠结觉得用若依这种开源脚手架改改更快或者后端也想用Python。从我实际做项目的角度说SpringBootVue仍然是这类社区服务系统最稳的选择。SpringBoot的优势在于生态成熟。做接口开发依赖引入简单Spring MVC的注解一套就能把Controller写得很干净做数据访问MyBatis-Plus一引入单表CRUD基本不用写SQL做权限Spring Security加JWT虽然配置麻烦点但是网上能找到大量现成方案。这些对于一个需要兼顾“设计实现”和“满足老师/面试官提问”的项目来说非常重要。Vue这边组件化开发很顺手Element Plus或Element UI组件库能快速把后台管理界面搭出来Vue Router负责前端路由跳转Pinia或者Vuex管理用户登录信息和全局状态。前后端通过JSON格式交互职责清晰接口调试也方便。如果是第一次做前后端分离Vue的学习曲线也算最友好的那一档。当然Vue和React的区别面试会问但在实际项目中选Vue就是看中它中文文档全、社区问答多、组件库成熟遇到问题基本都能搜到答案。1.3 功能模块与角色权限设计这个系统的功能模块我习惯拆成五块用户管理、陪诊服务管理、订单管理、评价管理、消息通知管理。用户管理包含用户注册登录、个人信息维护、陪诊员资质审核。陪诊员不是注册后就能接单的需要管理员审核身份证、健康证明、技能证书审核通过后才会出现在可预约列表里。服务管理包含陪诊服务项目定义比如半天陪诊、全天陪诊、代办取药、预约检查陪同不同项目对应不同价格和说明。订单管理是整个系统的核心从用户创建订单开始到陪诊员接单、开始服务、结束服务、用户确认、评价结算一整个生命周期都要覆盖。评价管理比较简单用户对陪诊员的服务打分和文字评价。消息通知管理订单状态变化后通过站内消息通知相关方也可以预留短信或微信通知的扩展点。角色权限我用一张表就能梳理清楚角色核心权限典型操作患者/家属下单、支付、查看订单进度、评价创建订单、取消订单、提交评价陪诊员接单、更新服务进度、查看我的订单接单、开始服务、点击结束服务社区管理员审核陪诊员、管理本社区订单和用户审核资质、查看服务记录、处理投诉系统管理员全局配置、数据统计、系统监控管理服务项目、用户禁用、报表查看权限设计上不需要做到Spring Security的细粒度方法级权限做到角色级别的拦截就可以。前端根据登录角色动态生成菜单后端在接口上加角色校验注解或者拦截器判断双保险防止有人绕过前端直接调接口。2. 技术选型、数据库设计与关键参数2.1 后端依赖怎么选版本坑有哪些我创建SpringBoot项目时踩过最大的坑就是版本。SpringBoot 3.x推出后很多人新建项目直接选最新版结果发现JDK版本要求17以上有些旧教程里的javax.包全变成了jakarta.MyBatis-Plus的适配也有问题。如果你的JDK环境是8老老实实用SpringBoot 2.7.x别追新。推荐一套组合SpringBoot 2.7.18JDK 8MyBatis-Plus 3.5.xMySQL 5.7或8.0Redisjjwt 0.9.xLombok。这套组合我跑了多个项目都很稳。如果你非要用SpringBoot 3就要接受JDK 17和jakarta命名空间迁移同时确保MyBatis-Plus用的版本是3.5.3之后的兼容版本。Maven依赖用starter方式引入核心pom配置大概是这样parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-security/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.5/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt/artifactId version0.9.1/version /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies这里有个小技巧Maven仓库如果下载慢可以把仓库地址换成国内镜像在settings.xml里配置。这个问题卡住过很多新手其实不是依赖写错是没下载下来。2.2 前端技术栈与环境准备前端我建议直接用Vue CLI或者Vite创建项目。Vue 3配Vite是当前主流依赖安装快启动也快。如果你之前只学过Vue 2第一次上手Vue 3需要适应下组合式API的写法但其实核心概念还是那些组件、路由、状态管理、生命周期。环境准备阶段最容易翻车的是Node.js版本。Vite需要Node.js 16以上老项目可能要求低版本新项目用太老的Node会直接报错。建议装Node.js 18 LTS版本一把梭不纠结。创建命令很简单npm init vuelatest或者走传统方式npm install -g vue/cli vue create community-escort-web依赖安装用npm install项目启动用npm run serveVite下是npm run dev。如果安装时卡在某个包先删掉node_modules和package-lock.json重新装多半是锁文件损坏。前端技术栈清单用途推荐方案UI组件Element Plus路由Vue Router 4状态管理PiniaHTTP请求Axios代码规范ESLint Prettier如果你对Element Plus的样式印象深刻应该知道它的表单和表格组件直接能覆盖管理系统大部分场景。下单页的表单校验、订单列表的分页表格、陪诊员审核页的详情弹窗靠组件库都能解决不用自己手搓。2.3 核心表结构设计数据库设计是整个项目的地基。很多同学表建得不合理后面接口写起来各种别扭。我建议按业务域拆表下面这几张是必须要有的。用户表sys_user统一存所有角色账号用role_type字段区分患者、陪诊员、社区管理员、系统管理员。陪诊员额外的资质信息放单独一张表冗余姓名、身份证信息到用户表避免每次联表查询。订单表escort_order是核心表字段至少包括订单编号、下单用户ID、陪诊员ID、服务项目ID、服务日期、开始时间、结束时间、患者姓名、患者联系电话、医院名称、医院地址、病情描述、订单状态、支付金额、创建时间、更新时间。金额建议用decimal(10,2)千万不要用float精度会出问题。订单编号我习惯用时间戳加随机数生成或者用“日期自增ID”格式方便人眼识别和排查问题。服务项目表service_item项目名称、服务内容描述、原价、折扣价、服务时长、状态。这个表可以直接做成数据字典风格的配置表管理员后台能增删改。评价表evaluation订单ID、陪诊员ID、评分1-5分、评价内容、回复内容、评价时间。评分字段用tinyint范围限制0到5。一个订单只允许评价一次所以订单ID要加唯一索引。订单状态记录表order_status_log订单ID、旧状态、新状态、操作人ID、备注、创建时间。每次订单状态变更都插一条记录前端时间线组件直接查这张表渲染用户就能看到完整流程。陪诊员资质表escort_certification用户ID、身份证号、身份证照片、健康证照片、审核状态、审核意见、提交时间、审核时间。管理员审核状态可以做成0待审核、1通过、2拒绝。这些表建好之后用物理外键还是逻辑外键我一直推荐逻辑外键。数据库只存关联ID真正的外键约束在应用层控制这样后续分库分表或者归档数据时不容易被数据库的约束卡住。2.4 订单状态机所有业务流转的骨架状态机是整个订单模块最容易让新手混乱的地方但也是面试官最爱问的点。订单状态我建议这样定义状态值状态名称含义可执行操作0待支付用户已提交订单但未支付取消订单、支付1待接单支付成功等待陪诊员接单系统派单、陪诊员抢单2已接单陪诊员已接单准备服务开始服务3服务中陪诊员已开始陪诊结束服务4待确认陪诊员已结束服务用户确认完成5已完成用户确认完成评价、查看详情6已取消用户或管理员取消无7退款中用户申请退款管理员处理退款8已退款退款完成无状态流转不能乱跳比如用户不能从“待接单”直接跳到“已完成”。实现上最简单的方式是在Service层写一个状态变更的公共方法进入方法先判断当前状态和目标状态是否满足流转条件满足才更新不满足直接抛业务异常。代码大概长这样public void changeStatus(Long orderId, Integer targetStatus) { EscortOrder order orderMapper.selectById(orderId); if (order null) { throw new BusinessException(订单不存在); } Integer current order.getOrderStatus(); boolean allowed statusTransitionMap.getOrDefault(current, Collections.emptyList()) .contains(targetStatus); if (!allowed) { throw new BusinessException(订单状态不允许从 current 变更为 targetStatus); } order.setOrderStatus(targetStatus); orderMapper.updateById(order); OrderStatusLog log new OrderStatusLog(); log.setOrderId(orderId); log.setOldStatus(current); log.setNewStatus(targetStatus); log.setOperatorId(LoginUserUtil.getUserId()); orderStatusLogMapper.insert(log); }状态流转Map可以在项目启动时初始化成static常量把所有允许的路径写死越界操作直接拒绝。这是个小细节但能挡住很多逻辑漏洞。3. 后端核心实现与踩坑记录3.1 项目初始化与统一返回结构在IDEA里新建SpringBoot项目很简单选择Spring Initializr改好Group和Artifact勾选Web、Security、MySQL、Redis这些依赖生成后把多余的文件清理掉按controller/service/mapper/entity/common配置包分层。这里有个容易被忽略的点是统一返回结构。如果每个接口返回的JSON格式都不统一前端Axios拦截器就没法统一处理。我的做法是封装一个Result对象固定包含code、message、data三个字段code为200表示成功401表示未登录403表示无权限500表示服务异常。Data public class ResultT { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(success); result.setData(data); return result; } public static T ResultT error(Integer code, String message) { ResultT result new Result(); result.setCode(code); result.setMessage(message); return result; } }全局异常处理也需要提前写好不然到处都是try-catch代码丑不说前端拿到一堆看不懂的默认异常信息。我自定义了BusinessException业务上能预判的错误比如金额不对、状态不能流转都主动抛这个异常。Controller里的业务代码就不会被异常处理淹没看起来清爽很多。全局异常处理器用RestControllerAdvice注解标注ExceptionHandler分别处理BusinessException和Exception。BusinessException返回业务提示信息Exception记录日志并返回“系统繁忙请稍后重试”。3.2 JWT认证与拦截器实现登录认证是SpringBoot项目逃不掉的一环。社区陪诊系统不需要复杂OAuth2JWT加拦截器足够。登录接口验证用户名密码后生成JWT返回前端前端每次请求在请求头里带Authorization字段后端拦截器从请求头取出token验证身份。JWT工具类主要做三件事生成token、解析token、判断token是否过期。生成时把用户ID、用户名、角色放进去过期时间我根据项目需要设置为24小时。public class JwtUtils { private static final String SECRET_KEY your-secret-key-please-change; private static final long EXPIRE_TIME 24 * 60 * 60 * 1000L; public static String generateToken(Integer userId, String username, String role) { return Jwts.builder() .claim(userId, userId) .claim(username, username) .claim(role, role) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() EXPIRE_TIME)) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); } public static Claims parseToken(String token) { return Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(token) .getBody(); } }拦截器里做的事也简单判断请求路径是否是登录路径或静态资源白名单不是就取token解析解析失败返回401解析成功把用户信息放进ThreadLocal方便后续Service获取当前操作用户。SecurityConfig那里我做了简化没有用Spring Security的完整表单登录流程而是放行所有请求把真正的安全校验交给自定义拦截器。很多教程不推荐这种做法但从这个项目体量来看自己接管JWT拦截比折腾Spring Security的过滤器链更可控也更好理解。如果你想突出Spring Security技能也可以在SecurityConfig里配置SessionCreationPolicy.STATELESS加上JWT过滤器两者结合。3.3 陪诊下单和接单的核心接口下单流程是整个系统的核心链路我完整走一遍。用户在前端选好服务项目、日期填写患者信息和医院地址提交订单。后端收到请求后先校验用户是否登录再校验服务项目是否在有效期内然后是计算金额。我这里的价格计算规则是优先看陪诊员的个人定价没有个人定价就使用服务项目的默认价格优惠券功能为了控制复杂度我没有做这个扩展后续可以加。金额确认后订单状态为待支付这里因为是毕设演示支付模块我用的是模拟支付不会真接微信支付宝。接单流程是陪诊员端的主场景。陪诊员登录后看到待接单订单列表列表是按服务日期和地址排序的。点击接单时后端不能简单看一眼订单状态就改为已接单要考虑并发情况。如果两个陪诊员同时点了同一个订单就可能出现两位陪诊员都显示“接单成功”但订单只属于其中一人的尴尬。解决方法是更新时带条件用数据库乐观锁Integer rows orderMapper.acceptOrder(orderId, userId, oldStatus); if (rows 0) { throw new BusinessException(订单已被其他陪诊员接走); }对应的SQL是UPDATE escort_order SET order_status 2, companion_user_id #{userId}, accept_time NOW() WHERE id #{orderId} AND order_status 1这个写法的核心在WHERE条件带order_status1更新条数为0就说明状态已经被别人抢先改掉了立即抛出异常。不需要分布式锁不需要redis锁就能把这个并发问题解决掉效率还高。服务结束流程类似。陪诊员点击结束服务把状态改为待确认系统给用户的站内消息表插一条“陪诊员已完成服务请确认”。用户确认完成之后订单变成已完成状态此时才允许评价。这里要注意一点用户确认不能省掉它起到“服务结果把关”的作用不然陪诊员自己点开始点结束就完成订单家属完全不知情体验会很差。3.4 Redis缓存用在哪几个位置Redis在这个项目里不是必须的但加上可以让面试多一个聊点。我实际用在了三个位置。第一是验证码缓存。注册时发送验证码把验证码存Redis并设置5分钟过期。用Redis而不是数据库是因为这类临时数据不需要持久化还能自动过期。第二是陪诊员热门列表缓存。首页显示推荐的陪诊员这个列表数据变化不频繁查询频率却很高。我可以把陪诊员ID列表缓存到Redis缓存里没有数据时再查数据库查完重新设置缓存。缓存更新策略采用先删缓存再更新数据库的简单方式对一致性要求不是极高完全够用。第三是陪诊员接单量的计数。每次接单成功后使用Redis的incr命令把当天的接单量加1前端展示“今日已接N单”的实时统计。这个数据如果每次都查数据库既慢又没必要Redis做计数器再合适不过。public void incrementDailyAcceptCount(Integer userId) { String key companion:daily:accept: userId : DateUtil.today(); redisTemplate.opsForValue().increment(key); redisTemplate.expire(key, Duration.ofDays(2)); }使用Redis前的序列化配置也要注意。默认的JdkSerializationRedisSerializer会把数据存成一堆二进制内容不好看也不好排查。建议改成使用Jackson的GenericJackson2JsonRedisSerializerkey用StringRedisSerializer。这个细节经常被忽略但配置好之后用Redis Desktop Manager看数据会舒服很多。3.5 注解使用心得SpringBoot常用注解一页纸很多面试题会问SpringBoot常用注解我在项目里用到的核心注解就这么几个。Controller层用RestController标注返回JSONRequestMapping定义路径GetMapping、PostMapping分别处理GET和POST请求PathVariable获取URL路径参数RequestBody接收JSON对象RequestParam接收查询参数。Service层用Service标记业务类Transactional声明事务Autowired注入依赖或者直接用构造器注入更推荐。配置类用Configuration标记Bean注入自定义组件ConfigurationProperties绑定配置对象。MyBatis-Plus里常用的是TableName指定表名、TableId指定主键、TableField指定字段名和自动填充标记。创建时间和更新时间字段用TableField(fill FieldFill.INSERT)自动填充创建时间用TableField(fill FieldFill.INSERT_UPDATE)自动填充更新时间配合MetaObjectHandler实现类插入和更新时自动设置值省去一遍遍手动set。这个做法代码量减少不多但绝对能避免“忘了set创建时间”的低级错误。4. 前端核心页面与联调经验4.1 创建Vue项目和路由配置前端项目骨架我用的是Vue 3加Vite。创建好之后第一件事是安装依赖装Element Plus、Vue Router、Pinia、Axios。安装Element Plus时要注意Vue 3必须用Element Plus而不是Element UI后者只支持Vue 2这个混淆坑了很多刚接触的人。安装命令npm install element-plus npm install vue-router4 npm install pinia npm install axios路由配置分两块公开路由和需要登录的路由。公开路由包含首页、登录页、注册页其余页面全部走登录守卫。为了不让业务代码里到处都是v-if判断角色我把动态路由和静态路由分开用户登录后根据角色信息动态添加路由这个做法在“若依vue开源项目在idea中部署”那类项目里很常见。const routes [ { path: /login, component: Login, meta: { public: true } }, { path: /register, component: Register, meta: { public: true } }, { path: /, component: Layout, redirect: /home, children: [] } ]路由守卫放在main.js或单独router文件里router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.public) { next() } else if (!token) { next(/login) } else { next() } })路由参数有两种最常用场景。一种是从订单列表点击进入订单详情通过路径参数传递订单ID跳转时把订单ID带上。另一种是列表页往表单页传对象这种数据量稍大放到路由参数不好维护我会用Pinia的临时状态或者直接在跳转后用订单ID去查详情接口。我的习惯是能用ID查后端就绝不传整个对象这样刷新页面后数据也还在。4.2 Axios封装与登录态保持Axios封装是前端项目的标配。我的封装逻辑是创建一个axios实例设置baseURL为后端接口地址设置请求超时时间比如10秒请求拦截器里从localStorage取出token拼到请求头的Authorization字段响应拦截器里判断响应状态码200直接返回data401跳转登录页其他错误用Element Plus的Message提示错误信息。const service axios.create({ baseURL: /api, timeout: 10000 }) service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] token } return config }) service.interceptors.response.use( response { const res response.data if (res.code 200) { return res.data } if (res.code 401) { localStorage.removeItem(token) router.push(/login) return Promise.reject(new Error(未登录或登录已过期)) } ElMessage.error(res.message) return Promise.reject(new Error(res.message)) }, error { ElMessage.error(网络异常请稍后重试) return Promise.reject(error) } )登录态保持我直接用localStorage存token和用户信息。每次刷新页面时App.vue或路由守卫里会调用一次“获取当前用户信息”接口把最新的用户数据重新放到Pinia。这样即使手动改了localStorage里的角色信息刷新后也会被后端返回的真实身份覆盖安全性更有保障。这里有个小坑后端JWT过期后前端拿到的所有接口都会返回401如果Axios拦截器里没有做跳转页面会停留在“看起来正常但所有接口都请求失败”的状态。把401统一处理成跳转登录页是最省事的方案但要注意登录页本身不能走401逻辑否则会跳转死循环。所以在响应拦截器里先判断当前路由不是/login再跳转。4.3 下单页面和服务评价页怎么做下单页面是这个系统里交互最复杂的页面包含三块内容服务项目选择、服务时间选择、患者信息填写。服务项目选择我使用Element Plus的卡片列表展示点击某个服务项目卡片后页面下方显示对应的价格同时调用后端接口获取按日期条件筛选后的可用陪诊员列表。日期选择器限制只能选今天之后的时间禁选过去的日期。患者信息部分做了表单校验姓名、联系电话、医院名称必填病情描述为选填但这些信息最终在订单详情页要展示给陪诊员所以不建议太简略。el-form refformRef :modelorderForm :rulesrules label-width100px el-form-item label患者姓名 proppatientName el-input v-modelorderForm.patientName placeholder请输入患者姓名 / /el-form-item el-form-item label联系电话 proppatientPhone el-input v-modelorderForm.patientPhone placeholder请输入联系电话 / /el-form-item el-form-item label医院名称 prophospitalName el-input v-modelorderForm.hospitalName placeholder请输入医院名称 / /el-form-item el-form-item label服务日期 propserviceDate el-date-picker v-modelorderForm.serviceDate typedate value-formatYYYY-MM-DD / /el-form-item /el-form提交订单时调用创建订单接口成功后把订单ID存起来跳转到支付页面。支付页面我放了一个模拟支付按钮和一个后台二维码图点击“模拟支付成功”后调用后端支付回调接口把状态从待支付改成待接单然后跳转到订单详情页。这个流程虽然模拟但和真实支付流程的接口设计很接近后续接微信支付时只需要替换掉支付调用部分。服务评价页用评分组件加文本域评分默认5分用户可改。提交评价前需要判断当前订单是否已完成已完成才能调评价接口。评价成功后订单详情页出现已评价样式评价内容回显不可修改。4.4 打包部署后布局异常的排查这是我从热搜词里看到很多人遇到的高频问题。开发环境布局好好的npm run build打包后扔到服务器上页面布局就乱了。常见原因有三个。第一个是静态资源路径问题。打包后index.html里引用的CSS和JS路径默认是根路径如果你的项目部署在子路径下比如nginx的location /admin/那这些资源就找不到。解决办法是在vite.config.js里配置base字段Vue CLI项目则配置publicPath。部署在子路径时设置base为相对路径或者完整子路径这一点极易踩坑。第二个是字体和图标文件404。element-plus等组件库内部会引用一些图片和字体资源打包时路径处理不好就会404导致图标变成方块。检查打包后的static目录里有没有对应资源再看网络请求的路径是否正确。第三个是CSS作用域问题。有些人和我一样图方便在好几个组件里都写了全局样式开发时没问题打包后样式被压缩优先级发生变化布局就乱了。解决办法是重新检查style标签是否没写scoped。全局样式统一放到App.vue或单独的css文件里管理。部署上线我比较习惯的方式是后端打包成jar前端打包成dist目录再用Nginx做反向代理把/api请求转发到后端端口server { listen 80; server_name your-domain.com; location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }try_files这行让前端路由在刷新时不报404少了它你从订单列表点进详情页刷新一下就会白屏。这也是高频问题之一。5. 系统测试、常见问题与经验复盘5.1 接口自测与联调遇到的问题接口写完后不能直接丢给前端联调我习惯先用Postman或者Apifox把核心接口完整走一遍从注册登录到下单接单再到服务完成评价一条完整业务链路跑通后再喊前端联调。这里记录几个我实际遇到过的通用问题。第一个是跨域问题。前端开发时访问http://localhost:5173后端接口在http://localhost:8080端口不同就触发跨域。解决方法有三后端加CorsFilter、前端配置Vite代理、或者上线后用Nginx同源访问。开发环境我推荐前端配置Vite代理生产环境走Nginx。调试跨域问题时先看浏览器请求有没有被CORS拦截再看后端响应头里有没有Access-Control-Allow-Origin排查起来很快。第二个是LocalDateTime序列化问题。Java 8的LocalDateTime默认序列化成数组格式前端拿到的是不知所云的结构。解决方法是统一配置JacksonBean public Jackson2ObjectMapperBuilderCustomizer customizer() { return builder - builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss))); }如果图省事也可以直接在实体类字段上加JsonFormat(pattern yyyy-MM-dd HH:mm:ss)注解。两种写法都可以但我更推荐全局配置不然每个字段都要加注解太烦。第三个是分页参数问题。MyBatis-Plus的分页返回结果是IPage对象默认字段是records、total、size、current前端分页组件刚好能对上但需要确保配置了分页插件否则分页不生效直接查全表。Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }5.2 并发和状态冲突怎么办社区陪诊系统的并发量不会很高但订单状态流转、接单抢单是并发问题的重点区域。抢单场景我前面已经介绍过使用带状态条件的更新语句解决。另一个常见冲突是用户下单后超时未支付订单一直占着位置。我设置了订单创建时间和支付时间页面展示支付倒计时后端不一定要做定时任务强制关闭可以在用户查询订单时如果发现待支付订单已超过30分钟就自动修改为已取消。这种懒计算方案对小型项目完全够用省去引入定时任务框架的复杂度。还有一个问题是陪诊员接单后如果临时有事取消订单需要管理员介入。这里我把取消权限严格控制订单在待接单状态用户可以取消订单在已接单状态用户不能直接取消需要申请管理员取消或者陪诊员同意。这样设计是为了防止恶意取消干扰陪诊员排班逻辑上更接近真实社区服务场景。5.3 提醒通知模块SpringBoot整合ActiveMQ的取舍关于消息通知我在这个项目里使用的是SpringBoot整合ActiveMQ的异步消息方案。为什么用它而不是RocketMQ或RabbitMQ原因很简单ActiveMQ对单机小项目更加轻量安装配置也简单SpringBoot有官方starter支持跑在默认的虚拟机模式下不需要额外安装服务非常适合毕设演示和中小型项目。我用它的场景是订单状态变更后的站内消息通知。比如用户下单成功后系统发出一个“订单创建成功等待陪诊员接单”的消息陪诊员接单后系统发消息通知用户“您约的陪诊员已经接单请注意查收服务信息”。使用ActiveMQ时pom引入dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-activemq/artifactId /dependency配置连接工厂和队列名称后消息生产者就是调用JmsTemplate.convertAndSend消息消费者是写一个JmsListener注解的方法整体链路不复杂。如果你不想引入消息队列也可以用Spring的ApplicationEvent发布事件变相实现异步通知但面试时能主动聊消息队列的使用场景是一个明显的加分项。5.4 从毕设到生产环境可扩展点清单这个项目做完之后如果你还想继续完善有几个方向可以做。第一个是真实支付对接。模拟支付改成微信支付或支付宝支付后端增加支付回调接口加入签名验证逻辑。这个改动可以单独做一个支付模块面试时提到会显得有真实落地意识。第二个是接入地图服务做成陪诊员位置追踪。可以用腾讯地图或者高德地图的API把陪诊员的实时位置展示给用户这样家属能直观看到陪诊员和患者的距离解决等待焦虑。第三个是消息通知渠道扩展。站内消息之外增加短信通知或者微信公众号模板消息通知这些都有现成云服务接入不算难。第四个是数据报表。管理员后台增加订单量趋势图、陪诊员服务排行、用户满意度统计等图表。前端用ECharts后端增加一些统计接口整体工作量不大但整个项目会显得更完整。如果想把项目做成一个能讲的“有深度”的毕设我建议多在这些非CRUD模块上花时间。只做增删改查很容易被问穿但能聊消息队列解耦、乐观锁防并发、Redis缓存热点数据、前后端分离权限控制面试官会觉得你真的思考过生产问题。6. 关于这个项目我再多说几句这个系统我做下来最大的体会是技术难度其实都被框架消化了真正费心思的是把业务状态和用户预期对齐。比如我把“用户下单”和“陪诊员接单”之间拆成两个明确状态用户能看到等待过程而不是下单后就觉得自己预约成功了。前端加一个订单时间线组件用户能一眼看到进度这种体验上的细节比多写十个接口更能打动使用者和评审老师。最后再分享一个小经验。做这类SpringBootVue的前后端分离项目时前后端联调是最容易拖慢进度的环节建议提前把接口文档写清楚字段名、类型、状态码含义都列出来。我用Apifox离线文档分享给前端同学开发效率高很多。技术本身不难难的是把流程安排得井井有条。希望这篇内容能帮到正在做社区陪诊系统的你。
返回列表