去年年初,我接了一个连锁养老机构的系统需求。对方说得直白:老人档案还是纸质加Excel,护工排班靠微信群喊,家属想知道老人当天血压怎么样,得打电话问前台。他们要上一个养老智慧服务平台,技术栈要主流、代码要能交付、后面还得自己改。我最后定下来的方案,就是标题里这套:Java SpringBoot 做后端,Vue3 做前端,MyBatis 操作业务数据,MySQL 做存储,前后端彻底分离。这套系统从需求确认到上线跑了三个多月,目前接入两个社区站点,几百位老人的档案、健康记录、服务工单都在上面正常流转。这篇文章我就把这套系统的业务拆解、技术选型背后的逻辑、表结构设计、前端开发实践,以及联调部署阶段踩过的坑,一次性写完。
1. 养老智慧服务平台的项目全貌:从业务需求到系统边界
1.1 甲方真正的诉求和管理痛点
如果只看标题,你可能会以为这只是一个普通的CRUD后台。但养老平台比较特殊:它既要有传统管理系统的档案、审批、统计,又要处理健康这种半实时数据。我从需求调研里总结出三类核心诉求。
第一,档案数字化。老人基本信息、入住时间、护理等级、家属联系方式、病史与过敏史,这些必须能在系统里快速查到,同时要有权限控制——不是所有护工都能看老人的完整病历。这和普通员工管理系统有很大区别,健康档案的隐私等级明显更高。
第二,服务流程线上化。从家属发起助餐、助浴、陪诊申请,到管理员生成工单、护工接单、服务完成回执,再到运营者月底统计服务量。这条链路如果断了,系统就是个摆设。所以从第一天起,我就把"订单—工单—回执"这条链路当成平台的主干功能来设计,所有页面都围绕它展开。
第三,健康数据的可感知。机构内日常会测血压、心率、体温,如果这些数据能自动入库,家属端就能远程看到趋势,这也是"智慧养老"最加分的地方。甲方甚至提出过"能不能对接智能手环",当时条件不成熟,但我把健康数据的采集接口设计成独立模块,后面接设备时不用改表结构。
1.2 四类角色如何构成业务闭环
我按使用角色把系统划分成四块,避免一开始就掉进功能堆砌的坑。这个项目的角色关系比常规后台复杂,因为有老人、家属、护工、运营者四方参与,每一方的诉求都不一样。
| 角色 | 核心操作 | 前端形态 |
|---|---|---|
| 平台管理员 | 老人入住登记、工单调度、权限分配、服务项目管理 | 管理后台PC端 |
| 护工/服务人员 | 接收工单、上报服务状态、录入健康测量值 | H5移动页面 |
| 家属/老人本人 | 查看健康档案、发起服务预约、查看账单 | 微信H5/小程序 |
| 运营者/机构管理层 | 查看入住率、服务量、人员负荷、健康异常统计 | 管理后台只读报表 |
这张角色表直接决定了权限模型和前端页面数量。平台管理员是管理后台的重度用户,护工端必须极简,因为护工群体普遍对电脑操作不熟悉。家属端则是展示为主,操作路径要短。搞明白这四类人各自的习惯,比一开始就想着把页面做漂亮重要得多。
1.3 前后端分离不是赶时髦,而是这个项目确实需要
很多人问我:为什么不用传统的服务端渲染模板?这个项目如果放在十年前,用Thymeleaf或JSP也一样能做。但这里有几个现实原因,让前后端分离成了必然选择。
第一,前端不止一个。管理后台是Vue3的SPA,家属端和护工端以后大概率要套壳成小程序或原生App。如果后端页面模板和业务逻辑揉在一起,每加一个客户端就要动一遍服务端代码,代价太高。
第二,部署环境多变。这类项目交付到客户机房,有的拿一台Windows Server,有的用阿里云Linux,甚至遇到过客户只有一台普通PC的情况。前后端分离之后,后端只要打一个jar包,前端构建成静态文件丢到Nginx里就行,迁移成本极低。
第三,前后端分工明确。后端只暴露JSON接口,前端专注交互。这个项目迭代快,甲方经常今天提一个字段、明天加一个按钮,前后端分离时改动局部页面的成本明显更低。这也是我后来做所有中小型管理系统的默认姿势。
2. 技术选型背后的取舍:SpringBoot、Vue3、MyBatis、MySQL如何组合才不浪费
2.1 SpringBoot版本怎么定才是真稳妥
标题写的是SpringBoot,但没写明版本。这里其实藏着一个最常见的坑:Spring Boot 3.x 需要 JDK 17,而且包名从 javax 迁到了 jakarta,很多老依赖的 starter 还没跟上。养老平台面对的往往是客户已有的服务器,上面可能装的是 JDK 8、Windows Server 2012,你不可能让客户给你升级全套环境。
我当时选的是 Spring Boot 2.7.x + JDK 8/11。这个决策考虑是:Spring Boot 2.7 还在社区维护期,HikariCP、Druid、MyBatis Starter、PageHelper 这些依赖的兼容性都已经被大量生产项目验证过。如果你确实要上3.x,也要先确认团队里没有老代码依赖 javax 命名空间,否则上线前光是改 import 就够你喝一壶的。
| 维度 | Spring Boot 2.7.x | Spring Boot 3.x |
|---|---|---|
| JDK要求 | 8/11 | 17+ |
| 包名规范 | javax | jakarta |
| 生态兼容 | 老依赖基本全适配 | 部分中间件starter需要升级 |
| 适合场景 | 服务器环境不可控的交付项目 | 全新云原生、可控JDK环境 |
另外,Maven工程里务必锁版本,用spring-boot-parent统一管理版本即可。团队里如果有多个成员,依赖版本不一致导致的"我本地是好的,服务器却跑不起来"绝对是你联调阶段的头号敌人。
2.2 Vue3 + Vite + Element Plus:这套前端组合省钱又省心
Vue3这边我采用的组合是 Vue 3 + Vite + Pinia + Vue Router + Element Plus。相比Vue2时代,Vue3最大的变化是组合式API和响应式代理。新项目直接上<script setup>语法糖,组件逻辑复用抽到 composables 里,比选项式写得清爽太多。
有一点务必提醒:Vite 对 Node.js 版本有要求,官方建议至少 Node 16+,最好用 18/20 LTS。如果你在Windows上用老版本Node跑npm run dev,大概率会报一个ERR_OSSL_EVP_UNSUPPORTED,这时候不用折腾,直接换Node版本就好。
Element Plus是Vue3配套的组件库,管理后台的表单、表格、弹窗、分页都能直接复用,开发效率高。如果你不想被Element Plus绑死,可以用Naive UI或者Ant Design Vue,但对养老系统的后台管理场景,Element Plus的文档和社区活跃度是三者里最省心的。
2.3 MyBatis vs MyBatis-Plus:手写SQL的边界在哪里
标题里写的是MyBatis,实际项目中我也保留了不少手写SQL。理由很直接:养老平台的查询大多是组合条件、多表关联、报表统计。比如"查询本月每个站点各服务类型的完成工单数并按护工排序",这种SQL你用MyBatis-Plus的 LambdaQueryWrapper 写能累死,而且可读性极差。
我的建议是混用:纯单表CRUD可以交给MyBatis-Plus(如果你决定引入),涉及复杂关联和统计的查询全部写在XML的<select>里,并且把SQL命名为selectServiceStatsForReport这种看一眼就知道干什么的名字。MyBatis的动态SQL是它真正的核心资产,<if>、<where>、<foreach>配合起来做多条件筛选十分顺手。
必须强调一个安全习惯:写动态SQL时拼接条件一律用#{}占位,不要用${}。${}是直接字符串替换,虽然能实现动态表名、动态排序列名的场景,但一旦用户输入被拼进去就是SQL注入漏洞。我之前排查过的很多安全问题,八成都出在有人图省事用了${}。
2.4 MySQL版本和表设计的三个前置决定
MySQL这一层,标题没有写版本,但我在建库前就要定死三件事。
一是存储引擎。全部使用 InnoDB,支持事务、行级锁、外键约束,对接业务系统这是底线。MyISAM那套只适合读多写少的内部日志,业务表千万别用。
二是字符集。统一用utf8mb4而不是utf8。utf8在MySQL里最多存3字节,存不了emoji和某些生僻字,老人姓名里的生僻字可不是小概率事件。排序规则建议utf8mb4_general_ci或utf8mb4_unicode_ci,前者性能略好,后者排序更规范,看团队习惯。
三是连接方式。如果客户环境是MySQL 5.7,JDBC连接串里要带上useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai,否则第一次连接就可能被caching_sha2_password认证插件搞到抓狂。8.0默认用caching_sha2_password,JDBC驱动版本也得匹配,8.0.33 以上的MySQL Connector/J比较稳。
3. 数据库与服务端核心设计:老人档案、健康数据、订单流转的表关系
3.1 老人、家属、护工:先把人的关系建模想清楚
养老系统最核心的实体是"老人档案",但它不是一张孤立的表。我的设计思路是:elder_info作为老人主表,字段包括姓名、身份证号、性别、出生日期、入住日期、护理等级、紧急联系人、病史说明、状态(在住/出院/已故)。身份证号要做加密存储或脱敏展示,接口返回时默认隐藏中间8位。
elder_family是老人与家属的关联表。一个老人可以关联多个家属,一个家属也可能关联多个老人(比如儿子同时是父母两位老人的联系人),所以中间表是必要的,而不是给老人表加几个家属字段了事。staff_info是护工和服务人员表,包含工种、技能标签、排班状态。排班逻辑第一版不需要做复杂的班次轮转,用一张简单的staff_schedule记录每日在岗人员即可。这样既满足业务,又不被排班算法拖死。
实际上,人表的设计一定要考虑查询场景。例:家属端登录后要立刻看到他关联的所有老人的健康概览,SQL需要从elder_family反查elder_info,再关联health_metric_daily。所以我在elder_family的(family_mobile, elder_id)上建了联合索引,这个查询在几百人规模下毫无压力。
3.2 健康监测数据表:明细流水加定时聚合的折中方案
健康数据是养老平台相对特殊的一块。护工每天早中晚会给老人测量血压、心率、血氧、体温,如果全部用明细流水表存,数据量增长很快,查询趋势图又必须做聚合。
我采用的折中方式是:一张health_record_detail存原始测量流水,每条记录带老人ID、测量类型、测量值、测量时间、录入人;另建一张health_metric_daily存每天的聚合结果(当日最高/最低/平均血压等),定时任务每天凌晨跑一次聚合。
这个设计有几个实际好处。明细表保证数据的可追溯性和审计要求,聚合表让趋势图查询秒开,家属端看血压曲线时不用去扫百万级流水。如果以后接入IoT设备,设备上报的数据可以直接落明细表,聚合逻辑不变。索引方面,明细表在(elder_id, measure_time)建联合索引,聚合表在(elder_id, metric_date)建唯一索引,实际接口响应速度很理想。
3.3 服务订单和工单的状态机设计:这是平台的主干
服务预约和派单是整个平台的主干业务,我建议把订单和工单分开建模。service_order是家属/老人发起的服务订单,包含服务类型(助餐、助浴、陪诊、保洁等)、预约时间、备注、状态,状态枚举为待审核、已确认、已完成、已取消。service_work_order是管理员把订单拆解或直接派给护工的工单,包含接单人、计划执行时间、实际完成时间、结果说明、状态,状态枚举为待接单、执行中、已完成、已驳回。service_type是服务项目分类表,方便运营者调整服务项目和定价,不用改代码。
最值得留意的是状态流转的校验。我在Service层写了一个专门的状态机校验类,比如"已取消的订单不能再次进入待审核""完成服务必须有回执描述"这类规则都在Service层校验。别只看着前端按钮能不能点就完事——前端只是交互层,后端必须兜底。实际开发中,很多项目死在后端不校验、前端随便调,最后状态全乱。
3.4 RBAC权限模型落地:不要让每个接口都自己写鉴权
权限模型选用了经典RBAC:sys_user、sys_role、sys_menu、sys_user_role、sys_role_menu。SpringBoot里用拦截器加自定义注解来控制接口权限,而不是每个Controller方法里复制粘贴判断代码。
在实现上,我维护了两个缓存点。一个把登录用户的角色和权限码列表放在Redis里,key是user:perms:{userId},过期时间跟着登录态走;另一个是菜单表按角色生成的路由信息,前端登录成功后通过/user/info接口一次性拿到动态路由,再注册到Vue Router里。
这个权限模型对养老平台很够用,甚至有点过剩。但多花一个礼拜做权限设计,能帮你省下后续半年的售后。管理员三天两头提"这个菜单只有站长角色能看""这个按钮只有超级管理员能点",如果没有RBAC,你的代码会被各种if (user.getRole() == 2)塞满,改权限时改到想骂人。
4. Vue3管理后台开发实践:路由权限、接口封装、组件化
4.1 目录结构和管理后台的项目骨架
先提醒一个Vue3新手必踩的坑:reactive包裹的数组,如果你直接整体替换,响应式会丢失。必须用 ref 或者把替换动作改成 splice、push。我在做老人列表的时候,每次从接口拿新数据回来,如果用reactive({ list: [] })然后state.list = res.list,页面是不会刷新的。统一用const tableData = ref([])然后tableData.value = res.list才稳。管理后台开发这种整体赋值太常见了,这个坑提前踩掉能省不少时间。
管理后台的目录结构重点看几个关键目录。src/api下按模块拆分接口定义文件,比如elder.js、order.js、health.js,每个文件导出一个对象,对象里是接口方法。src/views按业务模块分页面文件夹,管理后台的页面可以粗分成列表页、表单页、详情页三种模板。src/stores放Pinia状态,登录态、用户信息、权限列表放在这里,页面组件里通过useUserStore()访问。src/router放路由定义,静态路由部分写登录页、404等,动态路由部分在登录后按权限注册。
4.2 动态路由与按钮级权限的实践
动态路由的核心是后端返回菜单和权限码,前端根据权限码决定菜单项是否渲染、按钮是否可点击。我的做法是:登录成功后,后端返回用户拥有的menuList和perms数组。前端在全局守卫router.beforeEach里判断:如果还没拉过用户信息就先拉,再根据menuList用addRoute动态挂载业务路由;页面里的按钮,比如"删除老人"、“派单”、“导出Excel”,用自定义指令v-perm包一层,没有权限码就直接移除DOM元素。
这里有一个反复踩的坑:动态路由如果注册时机不对,刷新页面路由表会先被清空,出现"白屏一秒"的现象。解决思路是在Pinia里存一个isRoutesLoaded标记,刷新时判断如果已加载就不重复注册。整体顺序建议先在main.js里创建router实例但不挂载业务路由,等登录接口返回后再addRoute,这套流程多调试几遍,形成自己项目的稳定模式就好。
4.3 axios实例与拦截器:统一处理Token和错误提示
管理后台的HTTP层,我封了一个request.js。创建axios实例,baseURL指向后端的/api前缀;请求拦截器里从Pinia拿到token,加到Authorization头,注意用Bearer拼接;响应拦截器里统一解包后端返回体,后端固定返回{ code, message, data }结构。code === 200直接返回data;code === 401时清空登录态、跳转登录页;其他错误码统一弹ElMessage提示。
这个封装的收益是:业务代码里不用每个请求都写.catch处理错误,接口层只写const list = await getElderPage(params),错误提示和登录态失效交给拦截器统一处理。看起来是小事,但几十个页面写下来少掉的重复代码非常可观。
还有一个细节:文件下载接口不能用普通JSON请求处理,要单独用blob方式来接收响应,不然导出的Excel会变成一串乱码。我在导出服务工单报表时专门踩过,后来前端在拦截器里判断responseType === 'blob'就直接返回原始响应,这才算把坑填平。
4.4 页面快速开发的套路:表格、表单、弹窗三件套
后台管理页面的开发节奏,90%都是"搜索区+表格区+弹窗表单"。我总结了一套自己的最快套路:搜索区由几个el-input、el-select加一个"查询"按钮组成,查询参数统一放在一个queryParams响应式对象里;表格区用el-table绑定列表数据,列用el-table-column,操作列里放"编辑""详情""删除"按钮;弹窗表单用el-dialog里嵌el-form,编辑和新增共用同一个弹窗,打开时根据是否有id来决定是 create 还是 update。
这套模式配合Element Plus的el-form校验规则,基本可以半天出一个页面。但页面多了以后要注意抽象公共组件,比如老人下拉选择器、服务类型选择器、分页组件,都抽到src/components下,避免五个页面里复制六份差不多的代码。等到后面接家属端小程序时,这些组件虽然用不上,但后端接口和权限模型可以直接复用,前端写起来会快很多。
5. 联调与部署阶段的实战排坑:跨域、时区、缓存、事务
5.1 跨域问题:最烦但最好解决的坑
前后端分离项目,本地联调时前端跑http://localhost:5173,后端跑http://localhost:8081,第一件事就是跨域。我见过团队里有人图省事直接在每一个Controller上加@CrossOrigin,这是非常糟糕的做法:第一代码重复,第二一旦出现跨域以外的复杂情况,比如网关转发,这种注解方式根本无法复用。
正确的做法是在后端写一个全局CorsFilter,配置allowedOrigins、allowedMethods、allowedHeaders,明确放开哪些域名。注意allowedOrigins不要写*,因为涉及携带Cookie的认证请求时allowCredentials(true)不能和*共存。规范的做法是把允许的来源写在配置文件里,部署时按环境修改。
如果用了Nginx部署,也可以直接在Nginx层做反向代理,把/api转发到后端,前端请求同源,压根不需要CORS。生产环境推荐用Nginx方案,开发环境用CorsFilter更方便。我在交付文档里专门把这个点写清楚了,后来接手的人按这个配基本零报错。
5.2 时区问题:数据库时间比北京慢了8小时
凡是做前后端分离,时间问题一定逃不掉。我项目里出现过几次"时间对不上"的怪现象:前端显示的创建时间是凌晨而不是下午,投诉接踵而至。根因是serverTimezone没配Asia/Shanghai,MySQL驱动按服务器默认时区解析时间,导致相差8小时。
排查路径分享一下:先看MySQL的time_zone和system_time_zone,确认数据库所在时区;再看JDBC连接串的serverTimezone参数是否明确指定;最后看后端返回前端的JSON序列化格式,比如LocalDateTime默认序列化可能输出yyyy-MM-dd'T'HH:mm:ss,前端如果不按这个格式解析会显示错。
我的统一约定是:后端用一个统一JSON序列化配置,把所有LocalDateTime格式化为yyyy-MM-dd HH:mm:ss,前端直接当字符串展示,别在前端再用new Date(字符串)转一遍。转多了时区偏移问题必然出现,尤其是部署在不同云平台时,服务器时区不统一的概率非常高。
5.3 MyBatis缓存:一级缓存带来的偶发性脏数据
热词里有"mybatis缓存"和"mybatis面试题",但实际项目里缓存坑也很常见。MyBatis默认开着一级缓存(SqlSession级别),同一个会话里执行同一条SQL会复用之前的结果。当你在一个业务方法里先查了老人列表,随后修改了某条数据,再查同一条列表,如果没有主动清缓存,查到的可能是旧数据。
碰到这种问题的标准做法是理解一级缓存的生命周期:它在SqlSession关闭后就失效。SpringBoot集成MyBatis时每个方法默认一个SqlSession,通常方法结束就关闭,所以多数情况下不会踩到。但一旦你用了@Transactional把多个查询包在同一个事务里,SqlSession会被复用,一级缓存才会真的发挥作用。我遇到的实际案例就是在事务里调了一个自动生成编号的方法,编号生成了两次,第二次查出来和第一次一样,排查半天才定位到是一级缓存。
二级缓存默认不开启,开了以后要特别注意缓存刷新策略,涉及增删改的表配置flushCache="true"。项目简单的话,建议干脆不启动二级缓存,Redis做分布式缓存完全够用,别给自己找额外的维护负担。
5.4 事务不生效的几个典型场景:绕开这些才能保证数据一致
养老平台的订单和工单都涉及金额和状态,事务用不好会出大问题。我复盘过的典型失效场景有四个。
第一,@Transactional加在private方法上。Spring基于代理实现事务,private方法不走代理,注定不生效,必须放在public方法上。第二,同类内部方法调用。this.saveOrder()调到同类里的@Transactional方法,代理不参与,事务不生效。解决办法是注入自身代理,或者把事务方法放到另一个Bean里。第三,异常被捕获。方法里try-catch把RuntimeException吞了,事务回滚无从谈起,要么让异常抛出去,要么手动setRollbackOnly()。第四,没指定回滚异常。默认只对RuntimeException和Error回滚,如果业务方法抛了一个自定义CheckedException,直接不会回滚,规范做法是@Transactional(rollbackFor = Exception.class)。
我在这个项目里把涉及订单创建、工单派发、健康记录入库的方法都加了@Transactional(rollbackFor = Exception.class),并且要求Service层内部不能越权调用自己的方法,就是为了一次性避开上面四个坑。
注意:
@Transactional即便加了rollbackFor = Exception.class,也只在当前线程内生效。如果你在事务方法里异步调用了另一个线程去更新数据,那部分是管不到的。
5.5 部署到服务器:宝塔Docker部署SpringBoot的注意点
部署路径大致是:本地mvn clean package打出jar包;写一个Dockerfile,基础镜像用eclipse-temurin:8-jre对应JDK8,把jar拷进去,ENV TZ=Asia/Shanghai设置时区,VOLUME /logs挂载日志目录;用宝塔面板的Docker管理器拉取镜像、映射端口。注意宿主机和容器端口不要冲突。
另外,服务器的内存和连接数经常被忽视。SpringBoot默认内嵌Tomcat最大线程数200,养老平台平时并发不高,不需要调,但MySQL的连接池最大连接数建议在配置里显式写清楚,防止高峰期连接耗尽。我用的是HikariCP,maximum-pool-size设的20,minimum-idle设的5,实测够用。字符集和时区在Docker容器里跑起来后再验证一遍,不然数据"调来调去出差错"的问题还会复发。
6. 源码交付后的二次开发指引:从跑通到真正落地
6.1 接手这套源码的正确顺序
不管你是买源码的还是团队里接手的,第一周建议按这个顺序走:先看数据库脚本,把表结构理清楚,重点看老人档案、服务订单、工单、健康数据这四组核心表的关系;再把后端启动起来,用Postman调通登录接口,拿到token后挨个把核心模块的列表接口跑一遍;接着启动Vue3前端,登录管理后台,按"老人档案→服务项目→服务预约→工单派发→健康记录→报表统计"的顺序点一遍页面,这个顺序就是整个平台的业务主线。
最后,确定好第一轮要改的需求,从列表页开始改,再到表单,再到权限。列表页最简单的字段增减能让团队快速建立信心,不要一上来就动订单状态机。我见过太多人拿到源码第一天就想改核心状态流转逻辑,结果把自己绕进去,连原有逻辑都没吃透。
6.2 那些"看起来高大上"的扩展到底该不该上
最近总有同学问SpringBoot整合Flink值不值得学、能不能往这个平台里加,或者用ActiveMQ做异步消息行不行。我的看法是:养老平台如果只有几千个老人、每天几千条工单,Flink和Kafka这类重组件完全没必要。先把定时任务(Spring @Scheduled)和MySQL处理好,数据量真上来再迁,系统设计的时候把采集层做成可替换的接口就行。
真正值得优先扩展的是这三样:家属端小程序或App,管理后台只是内网工具,家属端的远程查看和预约才是感知最强的入口,前端单独开发即可;消息通知,工单派发、健康异常告警用MQ或WebSocket推送提醒,比让护工隔一小时刷一次页面靠谱;报表可视化,管理后台的统计报表接入ECharts,把服务量、健康趋势、站点负荷做成图表,运营者很吃这一套。
至于OFD查看、分词搜索这类需求,如果有电子健康档案和搜索功能,可以按需集成,但不要为了"技术丰富"而盲目加模块。任何多余的技术选型都会变成后续的维护负担,这是我在多个交付项目里反复验证过的教训。
6.3 给接手开发的人最后几点忠告
第一,不要绕过权限体系。见过太多人为了快速加功能,直接写一个Controller不校验权限,等于给系统开了后门。第二,接口文档要维护好。前后端分离后,接口是第二份"合同",用Apifox或OpenAPI自动生成文档,每次改接口同步更新,否则三个月后没人敢动这个接口。第三,备份策略一定要交代给客户。MySQL的定时备份用crontab加mysqldump即可,数据是养老机构最核心的资产,没有之一。
这套源码我跑了一年多,看着它在两个社区站点完成几百位老人的日常流转,最大的体会有两点:一是技术栈本身没有高低之分,SpringBoot、Vue3、MyBatis、MySQL这套组合在中小型交付项目里极其顺手,难的是把业务逻辑做扎实、把状态流转做严谨;二是前后端分离之后,真正决定项目未来的是接口的稳定性和权限模型的清晰度,而不是某个新框架。如果你正准备拿这套源码做二次开发,先把主线功能跑通、把权限配明白、把备份落地,再谈花哨的扩展,方向就不会走偏。