ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SpringBoot+Vue老年一站式服务毕设:权限控制到部署实战

SpringBoot+Vue老年一站式服务毕设:权限控制到部署实战 简介面向Java毕业设计、课程设计及期末大作业场景这份基于SpringBootVue的老年一站式服务平台完整源码包提供可运行的前后端一体化解决方案。系统面向老年服务管理包含前台展示与后台管理端涵盖信息发布、业务处理、数据统计等典型模块功能完整界面简洁操作方便适合作为毕设或课设的高分参考。压缩包内共911个文件以java、vue、js、html、css为主配合xml配置文件、sql数据库脚本、bat启动脚本及maven相关文件覆盖后端业务逻辑、前端页面组件、数据表结构与一键部署配置整体仅16.4MB包体紧凑。已有70人学习下载项目经过严格调试可部署于Tomcat和MySQL环境建议5.7。除源码外还附带数据库工具、构建脚本及作者部署咨询支持能显著降低环境搭建门槛帮助快速交付一个完整可演示的毕业设计项目。1. 为什么是老年一站式服务平台一个能答辩、能部署、能讲深的毕设闭环很多同学拿到“老年一站式服务平台”这个题目第一反应是做个用户列表、服务列表、预约列表跑起来截几张图就交差。但真正让答辩老师挑不出毛病、也让简历上有话可说的是另一个维度服务预约的状态怎么流转、健康档案怎么导入导出、子女端怎么越权查看老人信息、部署到服务器之后前端路由为什么刷新就404。这篇文章就按springbootVue这条主线把老年一站式服务平台从源码到部署的完整路径拆开讲适合正在做毕设、或者想把这套代码改成自己项目的开发者。下面几章我会从选型和落地开始一步步把代码、参数和部署细节讲透。2. 技术选型要落地这套SpringBootVue组合的边界在哪里2.1 后端骨架SpringBoot整合Sa-Token与MyBatis Plus的取舍老年一站式服务平台虽然名字听起来简单但它实际包含用户端、服务端管理员/服务人员、以及子女代管视角。这样的系统如果每个接口都手写权限判断代码很快会变成一坨if else。所以后端选型我一般会用 SpringBoot MyBatis Plus Sa-Token 这三样。SpringBoot负责提供统一配置和自动装配MyBatis Plus把单表CRUD的样板代码省掉Sa-Token用来处理登录态和接口权限。相比之下Shiro要写大量XML配置Spring Security虽然功能强但学习曲线对毕设去讲清楚并不划算。Sa-Token翻开文档就能用自带注解校验答辩时也比较好讲。说回springboot配置有一个容易忽略的细节SpringBoot 2.x和3.x的依赖坐标写法有差异。如果照抄网上2.x的教程把javax.servlet改成jakarta.servlet这步漏掉项目连启动都过不了。所以我会在pom.xml里锁定版本然后所有依赖统一走spring-boot-starter-parent的版本管理避免自己手动指定一堆版本号。拿到源码之后第一个要看的文件也是pom.xml版本对齐了再谈跑通。parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent dependencies !-- Web MVC 与内嵌Tomcat -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- MyBatis Plus 的 SpringBoot 自动配置 -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.2/version /dependency !-- Sa-Token 权限认证 -- dependency groupIdcn.dev33/groupId artifactIdsa-token-spring-boot-starter/artifactId version1.37.0/version /dependency !-- MySQL 驱动 -- dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency /dependencies这里有个参数要特别说明如果你本机装的是JDK17SpringBoot 2.7.18可以正常跑如果你用的是JDK21我更推荐直接上SpringBoot 3.x因为2.x对JDK21的兼容没有官方保证。上面Sa-Token用的是spring-boot-starter对应SpringBoot 2.x如果换成3.x坐标要改成sa-token-spring-boot3-starter。我的习惯是不追求最新版本选一个我踩过坑、社区里能搜到解决方案的稳定组合。很多毕设项目会额外集成Redis但老年服务平台如果只是登录态和基础数据单机MySQL加本地缓存完全够用。把Redis加进来会让部署多一个容器也会让答辩时多一个“为什么用Redis”的问题。我的建议是题目没强制要求就别加把精力放在业务正确性上。真正需要缓存的时候Spring Cache加Cacheable注解也能应付一阵。2.2 前端选型Vue 3 Element Plus Vite谁在负责什么Vue部分我一般会选Vue 3 Vite Element Plus。Vue 2已经停止维护新写的项目再用Vue 2等于给自己埋坑。Vite作为构建工具开发启动速度比Webpack快很多这对毕设调试特别友好。Element Plus则是组件库用户列表、服务预约表单、分页表格这些模块直接用现成组件拼装视觉上也比手写CSS更接近真实产品。注意Element Plus只支持Vue 3如果你在package.json里看到element-ui那说明是从Vue 2老项目改过来的混用会报错。前端目录结构我一般这样安排src/api放所有请求src/router放vue路由src/views按用户端、管理端、子女端三个角色分文件夹src/components放复用组件。这样规划的好处是答辩讲项目结构时可以直接说“按角色划分了模块”而不是“所有页面都堆在views下面”。vue路由是这里的一个关键点。老年服务平台里用户端和服务管理端是两个完全不同的界面我用路由守卫做登录拦截同时根据角色跳转到不同的首页。下面是一段在实际项目里跑过的路由配置逻辑import { createRouter, createWebHistory } from vue-router const routes [ { path: /, redirect: /home }, { path: /login, component: () import(/views/Login.vue), meta: { public: true } }, { path: /admin, component: () import(/layout/AdminLayout.vue), meta: { roles: [ADMIN] }, children: [ { path: service-order, component: () import(/views/admin/ServiceOrder.vue) }, { path: health-record, component: () import(/views/admin/HealthRecord.vue) } ] } ] const router createRouter({ history: createWebHistory(), routes }) // 登录成功后把 token 和角色写进 localStorage再在这里统一判断 router.beforeEach((to, from, next) { const token localStorage.getItem(sa-token) if (to.meta.public) { next() return } if (!token) { next(/login) return } const role localStorage.getItem(role) if (to.meta.roles !to.meta.roles.includes(role)) { next(/403) return } next() }) export default router这段逻辑里有三个参数值得展开。第一createWebHistory是HTML5历史模式URL里没有#号更干净但后面部署时必须让nginx把所有请求回退到index.html否则一刷新就404这点第5章会专门踩一次。第二meta.public标记登录页这类不需要鉴权的路由比在守卫里逐个判断路径更省事。第三router.beforeEach里先读token再读role顺序不能反因为角色信息依赖登录态。很多前端新手会写成在登录组件里判断“有没有token”来决定跳转那只是表面路由守卫用户直接在地址栏输入/admin路径依然能进页面。用beforeEach做全局前置守卫才是把后端接口和前端页面两层的权限都关上了。Element Plus里像表格操作列的自定义按钮会用到vue插槽比如在服务工单表格里用template #operation插入“接单”“完成”按钮这就是slots的基础用法别在答辩时说是“自定义组件”。2.3 前后端接口约定统一返回结构才能让联调不吵架后端返回格式不统一前端写起来就是一场灾难。有的接口返回{code:200, data:{...}}有的接口只返回一个对象前端分页组件拿到不同结构还要适配纯属浪费时间。我一般会在后端写一个全局返回类Result规定所有接口走同一个结构code为0表示成功非0表示业务失败msg放提示信息data塞业务数据。Data public class ResultT { private int code; private String msg; private T data; public static T ResultT ok(T data) { ResultT r new Result(); r.code 0; r.msg ok; r.data data; return r; } public static T ResultT fail(int code, String msg) { ResultT r new Result(); r.code code; r.msg msg; return r; } }联调阶段前端最怕的是接口报错后直接返回一段堆栈跟踪页面里什么也渲染不出来。有了统一返回类前端Axios拦截器只需要判断code字段非0就弹ElMessage.error(msg)开发效率高很多。要注意的是Result里三个字段一旦定下来就别中途改名字否则前端拦截器、后端子类、甚至数据库里的操作日志模块全都要跟着动。我见过一次改成dataList导致前端十几个页面同时报undefined的翻车现场代价是花了一个通宵替换。到这里选型就选完了。这套SpringBoot Vue 3的方案本质上是把后端变成一个“接口提供方”前端变成一个“页面消费方”两者用统一的JSON结构沟通各改各的谁先完成不影响对方联调。3. 核心功能怎么落地从登录鉴权到服务预约的源码写法3.1 登录不是查个表那么简单Token怎么发、密码怎么存老年服务平台里登录角色有老人、家属子女、服务人员、管理员不能只做一张user表不然权限没法分。常见做法是一张sys_user表加一个role字段配合Sa-Token的注解做接口级校验。密码不能明文存注册时用BCrypt加密登录时用BCrypt校验——这是写进简历都能加分的基本功。加密工具我习惯用spring-security-crypto里的BCrypt单独引一个依赖不把Spring Security全家桶带进来。Service public class AuthService { Autowired private SysUserMapper userMapper; public String login(String username, String password) { SysUser user userMapper.selectOne( new LambdaQueryWrapperSysUser() .eq(SysUser::getUsername, username)); if (user null) { throw new BusinessException(用户不存在); } if (!BCrypt.checkpw(password, user.getPassword())) { throw new BusinessException(密码错误); } StpUtil.login(user.getId()); return StpUtil.getTokenValue(); } }这里有两个参数解释。第一LambdaQueryWrapper的eq条件拼查询比手写字符串SQL安全不会因为用户输入单引号把SQL语句截断。第二StpUtil.login把用户id写进登录态返回的token由Sa-Token管理前端后续请求在header里带token后端用SaCheckLogin注解就能拦截未登录请求。不要自己用UUID造token再存到数据库那只是把session换了个名字和真正无状态鉴权不是一回事。还有个容易被忽略的点注册接口。很多毕设直接在前端把密码明文POST到后端稍微有点经验的人一眼就能看出问题。注册时用BCrypt.hashpw(password, BCrypt.gensalt())加密每个用户生成的密文都不同即使数据库泄露也没法反推原始密码。BCrypt自己带随机盐不需要单独存盐字段。3.2 服务预约的数据库设计与接口实现老年一站式服务平台最核心的业务就是“预约服务”的流转。老人预约保洁、陪诊、康复理疗服务人员接单完成后归档整个过程不能一上来就UPDATE一行状态字段否则后面想查“这个月完成了多少单”会非常吃力。我一般会直接设计一张service_order表把关键业务字段一次建全宁可多花十分钟设计表也不愿在系统跑起来之后反复加列。字段类型说明idbigint主键自增order_novarchar(32)业务单号前端展示用elderly_idbigint老人用户IDservice_typetinyint1保洁 2陪诊 3康复appoint_timedatetime预约上门时间addressvarchar(200)服务地址statustinyint0待接单 1已接单 2已完成 3已取消remarkvarchar(500)备注create_timedatetime创建时间设计这张表时order_no不要用自增ID直接展示给用户否则别人注册一个账号看几条数据就能猜出你的单量。我用时间戳加随机数生成yyyyMMddHHmmss加四位随机数。appoint_time单独拆出来不塞进create_time里因为预约时间是未来的业务时间创建时间是系统时间两个语义完全不同。接口实现上创建预约只需要插入一条status等于0的记录但要让管理端“待接单”列表刷新出来。这里给出创建预约的核心Service代码Service public class ServiceOrderService { Autowired private ServiceOrderMapper orderMapper; Transactional(rollbackFor Exception.class) public void createOrder(ServiceOrder order) { order.setStatus(0); order.setOrderNo(generateOrderNo()); order.setCreateTime(LocalDateTime.now()); orderMapper.insert(order); // 这里预留消息通知入口新订单先进队列 } private String generateOrderNo() { String time LocalDateTime.now().format(DateTimeFormatter.ofPattern(yyyyMMddHHmmss)); int random (int) (Math.random() * 9000) 1000; return time random; } }Transactional需要注意如果方法里后面还要做扣库存、发消息这些事这个事务必须回滚处理rollbackForException.class的意思是所有异常都回滚。别只写一个Transactional不加rollbackForSpring默认只在运行时异常回滚业务异常如果被catch掉数据就会留在库里。这也是答辩时老师很喜欢追问的位置。3.3 健康档案导入导出用EasyExcel少写80行代码健康档案是老年平台里最能体现“一站式”的模块。老人每年体检的报告如果靠手工录入几百条数据录入员能录到崩溃所以需要一个Excel导入功能。传统方式用POI写读一行、解析一行、还要自己处理单元格类型光一个导入模块就要几百行。用EasyExcel阿里的开源库能把这部分代码压缩到一半以下。调用时只需要一行EasyExcel.read(file.getInputStream(), HealthRecordRow.class, listener).sheet().doRead()。public class HealthRecordListener extends AnalysisEventListenerHealthRecordRow { private final ListHealthRecordRow rows new ArrayList(); Override public void invoke(HealthRecordRow row, AnalysisContext context) { rows.add(row); } Override public void doAfterAllAnalysed(AnalysisContext context) { // 批量校验前端Excel里有一行格式错误整批不能入库 } }注意listener里不要直接调用Mapper逐行insert那会触发N次数据库连接几百行数据会明显卡顿。正确做法是invoke方法里把行对象收集到listdoAfterAllAnalysed里一次性批量insert到数据库。这个优化的效果在我实测的2万条导入里耗时从十几秒降到两秒以内。导出方向同理给前端一个按钮后端从查询结果里把List填进EasyExcel.write的模板类一行一行写入即可。模板类的字段顺序决定了Excel列的顺序加了ExcelProperty注解就能控制列名。健康档案的导出最好加一个日期范围参数不然一次把全量数据导出Excel文件太大打开也卡。4. 老年服务业务的四个细节从工单状态机到数据权限4.1 工单状态不硬编码状态机比if else更不容易漏在服务管理里待接单、已接单、已完成、已取消这几个状态看起来简单但每个角色的操作权限是不同的。老人只能取消“待接单”的订单服务人员只能“接单”和“完成”管理员可以全流程干预。如果全用if (status 1 role xxx)这种写法状态一多代码就变成一团乱麻。我的做法是给状态定义一套迁移规则把“哪些状态下可以做什么操作”写成一个对象或枚举类。后端提供一个统一的状态流转接口每次操作先校验当前状态是否允许这次动作。这样就自然消除了“已完成的单子被取消了”“待接单的单子被标记完成了”这类逻辑漏洞。当前状态可执行操作目标状态可操作角色0 待接单接单1 已接单服务人员0 待接单取消3 已取消老人/家属1 已接单完成2 已完成服务人员1 已接单取消3 已取消管理员这张表在答辩时可以作为“我的业务设计”展示比单纯说“做了增删改查”有说服力得多。状态机规则用前端代码写一次、后端校验一次双重保险。我见过一个项目因为漏了“已接单才能完成”这条规则结果服务人员对着待接单的订单狂点完成数据直接乱掉。4.2 定时提醒取药和复诊提醒用Scheduled还是XXL-Job老年平台里复诊提醒、取药提醒、生日问候这类推送是常见功能。SpringBoot自带的Scheduled注解就能胜任大部分场景。在启动类加EnableScheduling方法上加Scheduled(cron 0 0 9 * * ?)每天9点扫一遍当天的预约提前一天给老人或子女发短信/微信通知。cron表达式顺序是秒、分、时、日、月、周这里“0 0 9 * * ?”表示每天9点整触发?表示不指定星期几。但Scheduled有两个问题。第一它是进程内的如果后期把项目部署成多实例定时任务会重复执行。第二它没有失败重试机制任务中途抛异常日志不完整就找不到了。毕设阶段用Scheduled完全够答辩时如果能说出这两个边界反而比无脑说“用了XXL-Job”更显得你真正思考过。如果未来真要上XXL-Job注意它本身带一个调度中心部署时要多开一个服务配置也比单机定时任务复杂。4.3 子女端数据权限一个老人能被多个子女查看很多老年平台都设计了“子女代管”功能子女绑定老人账号后能看到老人的健康档案、服务订单。但这里有一个权限陷阱如果一个儿子和一个女儿同时绑定了同一个老人两个子女都能看那A子女能不能看B子女给老人预约的订单业务上一般是可以看的——因为订单属于老人不属于某个子女。所以子女端查数据时查询条件应该拼elderly_id老人ID而不是拼当前登录用户的ID。正确的关联表设计是elderly_bindings表字段包括bind_id、elderly_user_id、family_user_id。子女查服务记录时先查bindings表拿到自己有权限的老人ID集合再查service_order时用IN条件。核心查询长这样SELECT * FROM service_order WHERE elderly_id IN ( SELECT elderly_user_id FROM elderly_bindings WHERE family_user_id #{currentUserId} )这个SQL写清楚之后前端也一样不可见的数据就不渲染按钮也不给。接口层和页面层两层都做才能算真正处理了越权问题。我在评审别人代码时看到过好几次只用前端判断角色、后端接口直接把老人数据全量返回的情况那等于把老年用户的健康数据裸露在接口里没有任何安全性可言。4.4 vue-router动态菜单和按钮级权限管理端的菜单根据角色动态生成——管理员能看用户管理、服务分类管理服务人员只能看自己的工单。做法是登录时后端一次性返回菜单权限列表前端用v-for渲染侧边栏路由表里全部页面都注册但用meta.roles来控制渲染。按钮级权限同理Element Plus里封一个v-permission指令如果当前用户没有对应权限码就把按钮从DOM上移除。不要小看这层处理许多毕设只做了“页面能打开”但没做“不该操作的人看不到按钮”老师点开看了一下就能看出差别。写这个指令时要注意它不是简单的v-if。因为v-if在组件内部判断权限指令最好挂在元素上用mounted钩子里做remove操作这样按钮从DOM上彻底消失而不是被CSS隐藏。隐藏的按钮还能被爬虫或者开发者工具改样式点出来移除才是最干净的。5. 避坑部署与开发的常见问题、原因与解决5.1 Vue打包放进SpringBoot后刷新404现象前端开发时一切正常npm run build之后把dist目录拷进SpringBoot的static目录访问首页正常点侧边栏跳到子页面也没问题但一按F5刷新就出现404或者SpringBoot的Whitelabel Error Page。原因vue-router用了history模式URL是真实路径而SpringBoot默认把/xxx当成后端接口路径去匹配找不到对应的Controller就404了。解决有两种做法。一是把vue-router改成createWebHashHistory带#号最简单最无脑二是保留history模式在SpringBoot里写一个Controller转发所有不在白名单里的路径到index.html。我建议优先用第二种因为带#号的URL确实显得不够专业。Controller public class ViewController { GetMapping(value {/admin/**, /home/**, /order/**}) public String forward() { return forward:/index.html; } }注意这段代码只适合把前端打进jar包的方式如果前后端分离部署前端用nginx、后端用Tomcat转发逻辑应该写在nginx配置里见第6章。5.2 SpringBoot版本太高javax到jakarta的大坑现象照着网上教程引入依赖启动直接报ClassNotFoundException: javax.servlet.Filter或者Sa-Token的过滤器全都不生效。原因SpringBoot 3.x从Java EE的javax迁移到了Jakarta EE的jakarta命名空间。老教程里的javax.servlet、javax.annotation全部失效。解决如果你需要网上老方案的代码老老实实用SpringBoot 2.7.x如果你非要用3.x把所有javax换成jakartaSa-Token也要引入sa-token-spring-boot3-starter而不是普通的starter。我自己的项目里同时维护过两套结论是别跟风上高版本。毕设首要任务是稳定跑通不是展示你用了多新的框架版本。5.3 跨域问题前端请求接口拿到了CORS错误现象前端在http://localhost:5173后端在http://localhost:8081前端登录接口请求发出去控制台报“Access to XMLHttpRequest at ... has been blocked by CORS policy”。有人会去后端加CrossOrigin注解加了还是不行因为过滤器抛出异常的时候拦截器还没生效。原因开发环境下前端和后端端口不同浏览器禁止跨域读取响应。更隐蔽的是如果用了Sa-Token它注册的过滤器在Spring MVC的CORS处理之前执行导致带token的请求直接在过滤器层就被拦下来了。解决最省心的方案是后端写一个CorsConfig注册CorsFilter并且让它在过滤器链最前面执行。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); } }注意allowCredentials和allowedOrigin不能同时用“*”要改用allowedOriginPatterns。这个细节让很多人折腾半宿。5.4 MySQL连接时区服务启动成功一查询就报错现象SpringBoot启动日志没有异常但调用列表接口时报java.sql.SQLException: The server time zone value Öйú±ê׼ʱ¼ä is unrecognized。原因MySQL 8.x之前的连接驱动对时区不敏感8.x后如果URL里不带serverTimezone参数驱动会尝试读取系统时区中文系统输出的时区名乱码导致解析失败。解决在spring.datasource.url的末尾拼serverTimezoneAsia/ShanghaiuseUnicodetruecharacterEncodingutf8useSSLfalse。我已经数不清这行救过多少次场。spring: datasource: url: jdbc:mysql://localhost:3306/elder_care?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse5.5 MyBatis Plus分页失效列表永远只有第一页现象前端分页组件页码切换正常但返回的数据永远是前10条。原因MyBatis Plus 3.5.9开始将jsqlparser依赖从starter里拆分出去了只引入mybatis-plus-boot-starter不会自动带分页SQL解析能力导致分页插件拦截器没有解析器可用分页逻辑失效。解决如果你用的还是3.5.3.2分页插件依赖还在核心包里直接注册PaginationInnerInterceptor就行如果版本比较新在pom里加一个mybatis-plus-jsqlparser依赖然后注册分页插件拦截器。这个坑相当常见因为网上旧的教程代码里没有这一步。6. 部署到一台云服务器用Docker Compose一键跑起来的细节与验证到了这一步代码在本地能跑、流程能走通剩下就是部署到服务器让答辩现场不用开本地环境也让评委觉得项目“真的做完了”。我一般会准备一个最简单的部署方案MySQL容器 后端jar包容器 前端nginx容器三个容器用Docker Compose跑在一起。先看compose文件的核心部分services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: elder_care ports: - 3306:3306 volumes: - ./mysql-data:/var/lib/mysql - ./sql/init.sql:/docker-entrypoint-initdb.d/init.sql backend: build: ./backend depends_on: - mysql environment: SPRING_PROFILES_ACTIVE: prod DB_HOST: mysql ports: - 8081:8081 frontend: image: nginx:1.24 depends_on: - backend volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf ports: - 80:80DB_HOST这个参数是后端容器访问MySQL容器时用的主机名和本地项目里的localhost完全不一样。如果你把配置硬编码在application.yml里容器一启动就会连不上数据库。我习惯把这类环境相关的配置放到application-prod.yml里面用占位符DB_HOST: ${DB_HOST:localhost}本地启动时默认localhost容器部署时从环境变量注入。nginx.conf有两处要改。第一处是前端history路由的fallbacklocation / { try_files $uri $uri/ /index.html; }。没有这行刷新子页面就404——和5.1节是同一个问题只是从SpringBoot转发换成了nginx转发。第二处是把/api前缀的请求反向代理到后端容器的8081端口注意proxy_pass里写的是http://backend:8081backend是compose服务名在docker网络里可以解析。部署完之后的验证顺序也很重要。我会先登录服务器docker compose ps看三个容器是不是都Up状态然后看后端日志docker compose logs backend | tail -50确认没有数据库连接错误最后再本地浏览器打开http://服务器IP登录一次跑一遍“预约服务→接单→完成”全流程。这套验证做完再回本地把代码仓库整理提交一遍就可以安心准备答辩了。提示后端jar包镜像构建前先删掉本地target目录重新打包。用旧jar包上线是最难排查的“本地能跑线上不行”我遇到过两次都是忘了重新打包。这套方案的可贵之处在于MySQL、后端、前端三个组件各自独立任何一环出问题都可以单独重启或看日志不会互相污染。我自己的习惯是部署前先删掉本地target目录重新打包一次确保不是用旧jar包上线。希望你做完这套老年一站式服务平台后也能顺手把部署脚本沉淀成自己的工具下次做别的项目能直接复用。希望帮到你。本文还有配套的精品资源点击获取
返回列表