1. 这个题目为什么值得做:业务场景、角色设定与模块边界
又到毕业设计季,后台私信里问Java选题的人比平时多了不少。翻了一圈,问得最多的还是那几类:图书管理、商城系统、校园助手……不是说这些题目不行,而是同质化太严重了,答辩时评委一年看几十个图书管理系统,很难有记忆点。相比之下,SpringBoot+Vue做的人口老龄化社区服务与管理平台,算是今年看到比较扎实的一个方向——业务域足够真实,功能上有发挥空间,又不会复杂到一个人做不完。
我拿到这个项目的源码资料时,第一反应是看它的需求边界划分。一套完整的毕设项目通常配备源码、SQL脚本和接口文档三大件,但源码本身只是结果,真正值钱的是设计思路。老龄化社区服务这个方向,核心价值在于它摸到了一个真实的社会痛点:独居老人越来越多,社区层面的助餐、助行、助医、陪护等服务需求在快速上涨,但大多数社区的管理方式还停留在电话、纸质登记和微信群接龙。这个平台本质上就是把线下的服务供需匹配搬到线上,让老人或家属能发起服务请求,让社区工作人员能派单调度,让志愿者能接单服务并记录工时。
这类项目最大的优势是"讲得清楚"。图书管理系统的业务逻辑三句话就说完了,而这个平台天然带着多角色协同、多流程流转、多状态管理,无论写进毕业设计论文还是面试时讲项目,都有足够的深度可以挖掘。它适合计算机软件工程方向的学生做毕设,也适合刚入职场的初级Java开发拿来做全栈练手,甚至社区信息化相关的小型实际项目也可以照这个骨架去裁剪。
1.1 老龄化社区服务的真实场景:谁在用、在解决什么问题
先别急着写代码,把业务场景想明白,后面所有设计都不会跑偏。我习惯把这类系统拆成"服务对象"和"服务流程"来理解。
服务对象很明确:社区里的老年居民,尤其是独居、行动不便、慢性病较多的群体。他们需要的不是一套冷冰冰的软件,而是能解决实际生活问题的入口。比如午饭没人做、需要有人陪着去医院复查、家里水管坏了需要人帮忙联系维修、白天一个人在家想找人聊聊天。这些需求落到系统里,就是一个一个"服务工单"。
但这个平台的服务对象还不只是老人本身。老人的子女常年在外地,他们需要一个能查看父母健康状况、了解社区活动的窗口;社区工作人员需要一套工具来受理需求、协调资源、记录服务过程;志愿者需要看到自己可以接哪些任务、累计服务了多少时长。所以这个系统的用户画像是多元的,这就要求数据库设计、权限设计、界面设计都要围绕"角色不同,看到的东西不同"来做。
理解了这一点,再看业务闭环就清晰多了。整个系统至少有两条主干流程:一条是服务流,老人或家属发起服务预约,工作人员审核派单,志愿者接单执行,完成后老人评价;另一条是管理流,社区工作人员维护老人档案、录入健康记录、发布活动公告、审核志愿者资料。这两条流程交织在一起,就是平台的核心业务骨架。
1.2 四类角色与三条核心业务闭环
角色权限设计我建议直接按四类来划分:系统管理员、社区工作人员、志愿者、老年用户(家属可代操作)。为什么要分四类而不是简单搞一个"用户"和"管理员"?因为你不分清楚,后面的权限校验、菜单渲染、接口鉴权全都会变成一锅粥。
- 系统管理员:管账号、管角色、看全局数据统计,偶尔处理异常工单。
- 社区工作人员:业务的实际运营者,负责审核档案、派单调度、发布活动、处理求助。
- 志愿者:服务资源提供方,接单、报名活动、填写服务记录。
- 老年用户:服务的发起方和接收方,可以预约服务、查看健康档案、发起紧急求助。
三条核心业务闭环分别是:服务工单闭环(预约-派单-接单-完成-评价)、健康档案闭环(登记-更新-查看-预警)、社区活动闭环(发布-报名-签到-统计)。这三个闭环几乎覆盖了系统里80%的功能模块,也是答辩时评委最感兴趣的部分。
1.3 模块清单:一个完整项目该有哪些功能
结合源码里实际包含的页面和接口,我把功能模块梳理成了一张表。这张表既是开发时的任务清单,也是论文里系统功能结构图的原型:
| 模块 | 功能点 | 服务对象 |
|---|---|---|
| 登录注册 | 账号注册、登录、退出、密码修改 | 全部角色 |
| 老人档案 | 信息录入、健康档案、紧急联系人、家庭地址 | 工作人员/老人 |
| 服务管理 | 服务项目维护、预约下单、派单、接单、完成、评价 | 全部角色 |
| 志愿者管理 | 志愿者注册、技能标签、服务时长统计 | 志愿者/工作人员 |
| 社区活动 | 活动发布、报名、取消报名、签到 | 工作人员/志愿者 |
| 紧急求助 | SOS一键求助、消息通知、处理记录 | 老人/工作人员 |
| 公告通知 | 公告发布、列表展示、详情查看 | 全部角色 |
| 数据统计 | 年龄段分布、服务类型占比、工单完成率 | 管理员 |
模块清单不是拍脑袋定的,每个模块背后都对应着一个真实场景。比如紧急求助模块,不是简单的"加一个按钮",而是要考虑求助时老人可能说不清具体情况,所以求助信息要自动带上老人档案、住址、紧急联系人电话,工作人员接到求助直接能看到完整信息,这就比微信群求助靠谱得多。
2. 技术栈的稳妥搭配:SpringBoot与Vue的选型和分层设计
技术选型这部分,很多同学喜欢堆热门词,Redis、RabbitMQ、ElasticSearch恨不得全写上去。我的建议是:毕设项目讲究的是"技术栈与业务复杂度匹配",你拿一个每秒几十次请求的小平台硬上一套微服务,答辩时第一个问题就露馅了。SpringBoot+Vue这个组合之所以成为Java Web毕设的标配,就是因为它在"够用、好讲、有深度"三个维度上做到了平衡。
2.1 后端:SpringBoot + MyBatis Plus的组合理由
后端用SpringBoot 2.7.x,搭配MyBatis Plus 3.5.x,是这套项目最稳妥的选择。SpringBoot不用多说,自动配置、内嵌Tomcat、生态成熟,省去了大量XML配置的繁琐。MyBatis Plus则是在MyBatis之上封装了通用CRUD、分页插件、逻辑删除、代码生成器,特别适合这种业务逻辑清晰但表数量不少的管理系统——你不需要为每一张表写一套重复的增删改查Mapper方法。
有人会问,为什么不用Spring Data JPA?我的看法是:JPA的学习曲线和复杂关联查询的处理方式,对多数做毕设的同学来说不如MyBatis Plus直观。MyBatis Plus生成的SQL你是能预期到的,出了问题也好排查;而JPA一旦懒加载、级联、N+1问题同时出现,排查成本会高很多。另外,MyBatis Plus在面试中被问到的频率也高,简历上写"熟悉MyBatis Plus"至少不会扣分。
# application.yml 里的核心配置 spring: datasource: url: jdbc:mysql://localhost:3306/community_service?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0注意数据库连接串里的serverTimezone=Asia/Shanghai,这个不写,高版本MySQL驱动会报时区错误。logic-delete-field这些配置也很关键,后面章节会展开讲。
2.2 前端:Vue版本与UI组件库怎么选
前端这块,源码里用的是Vue 2 + Element UI。我知道Vue 3 + Element Plus已经出了很久,但做毕设我还是建议先确认一下你手头的参考资料和组件库示例更偏向哪个版本。Vue 2的资源存量依然巨大,遇到问题随便一搜就有答案,这对时间紧张的同学来说是实打实的优势;如果学校课程用的是Vue 3,那你用Vue 3 + Element Plus也完全没问题,核心思路是一样的。
前端工程化的关键是vue.config.js里的代理配置。开发环境下前端跑在8080端口,后端跑在8081端口,浏览器直接请求跨域,所以需要把接口请求代理到后端地址:
// vue.config.js module.exports = { devServer: { port: 8080, proxy: { '/api': { target: 'http://localhost:8081', changeOrigin: true } } } }这里有个细节:changeOrigin: true一定要开,否则后端收到的请求的Host还是前端地址,有些反向代理环境下会出问题。另外,代理路径建议统一带/api前缀,后端Controller的RequestMapping也统一以/api开头,这样前后端约定清晰,后期部署也能灵活配置Nginx转发。
2.3 认证与权限:JWT方案为什么够用
登录认证方案里,我推荐JWT + 拦截器的方式,而不是Spring Security + OAuth2那一整套。不是说Spring Security不好,而是那套东西对新手来说太重了——配置类写了一大堆,过滤链调了半天,最后可能连它默认帮你做了什么都没吃透。
JWT的本质,是服务器在登录成功后签发一个包含用户信息和过期时间的加密令牌,客户端每次请求带上这个令牌,服务器校验签名和有效期即可。它天然适合前后端分离的场景,因为服务器不需要存Session,天然支持水平扩展,这在答辩时也是一个很好的加分论据。
令牌结构:Header.Payload.Signature Payload部分包含:userId、roleType、exp过期时间后端用一个拦截器统一处理:放行登录接口和静态资源路径,其余接口从请求头的Authorization: Bearer {token}里解析令牌。解析失败或过期直接返回401。拿到用户ID后放入ThreadLocal,供业务代码随时取用,避免每个接口都手动解析一遍。
前端配合路由守卫:没有token访问需要登录的页面时,跳转到登录页;登录成功后把token存到localStorage里,axios请求拦截器统一加上请求头。
2.4 项目分层:源码拿到手先看哪几个包
拿到这套源码,别急着跑起来,先看目录结构。一个清晰的SpringBoot项目应该有这几层:controller(接收请求)、service(业务逻辑)、mapper(数据访问)、entity(实体类)、common(通用返回结果、异常处理、工具类)、config(配置类)。前端则是views(页面组件)、router(路由)、api(接口封装)、utils(工具)。
我特别想强调common包里的统一返回结果类Result。所有的接口都返回{ code, message, data }这种结构,前端axios响应拦截器统一判断code,成功才走success逻辑,失败统一弹错误提示。这个习惯看着简单,但能省掉一大半前后端联调的扯皮时间。很多项目的接口返回格式五花八门,有的直接返回数据,有的返回Map,联调时前端代码里全是各种res.data.data.xxx,谁维护谁头疼。
3. 数据库表如何一步步设计出来:从ER推演到SQL落地
数据库是这类项目里最见功底的部分。我拿到一份源码时,习惯先看SQL脚本——几张表、什么字段、什么关系,立刻就能判断这个项目是认真设计的还是东拼西凑的。这套平台的核心表大约十一张,接下来按业务域分组推演一遍。
3.1 用户、角色与老人档案表:身份侧的基座
先看用户表。我见过很多项目直接在用户表里塞一堆字段,角色用字符串"admin/user"区分,老人专属字段也塞在用户表里,结果就是一张表四五十个字段,改需求时痛不欲生。合理的做法是用户表只存登录认证和通用信息,角色用role_type字段区分,老人专属信息单独一张扩展表。
-- 用户表 CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, nickname VARCHAR(50), phone VARCHAR(20), avatar VARCHAR(255), role_type TINYINT NOT NULL COMMENT '1管理员 2工作人员 3志愿者 4老人', status TINYINT DEFAULT 1 COMMENT '1正常 0禁用', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, deleted TINYINT DEFAULT 0 COMMENT '逻辑删除' );老人档案表为什么要单独建?因为老人专属字段太多了:出生日期、身份证号、住址、紧急联系人、紧急联系电话、健康状况描述、是否独居、是否有慢性病……这些字段如果全塞进用户表,志愿者和工作人员登录时也得带上这些无意义的数据,性能和结构都不合理。
-- 老人档案表 CREATE TABLE elder_info ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL COMMENT '关联sys_user.id', name VARCHAR(50), gender TINYINT, birthday DATE, id_card VARCHAR(18), address VARCHAR(255), emergency_contact VARCHAR(50), emergency_phone VARCHAR(20), health_status VARCHAR(500) COMMENT '基础健康情况描述', live_alone TINYINT DEFAULT 0 COMMENT '是否独居', create_time DATETIME DEFAULT CURRENT_TIMESTAMP );3.2 健康记录与服务工单:核心业务表的字段推演
健康记录表的设计,关键是想清楚"一条记录记录的是什么"。社区场景下,健康数据往往是工作人员上门巡访时手动填的,也可能是对接设备自动上报的。无论哪种,每条记录都对应一个老人、一个时间点、一组健康指标。血压、血糖、心率、体温这些常量字段用单独的列,方便做统计分析;额外指标放到备注字段里。
服务工单表是整个系统里最核心的一张表,我建议你花时间把它想透。一条工单要回答这些问题:谁发起的(老人)、要什么服务(服务项目)、谁来做(志愿者)、做到哪一步了(状态)、约定什么时候(预约时间)、在什么地方(服务地址)、有什么需要特别交代的(备注)、做得怎么样(评价和评分)。所以字段设计如下:
CREATE TABLE service_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(30) NOT NULL COMMENT '工单编号', elder_id BIGINT NOT NULL COMMENT '老人档案id', item_id BIGINT NOT NULL COMMENT '服务项目id', volunteer_id BIGINT COMMENT '接单志愿者id', status TINYINT DEFAULT 0 COMMENT '0待派单 1已接单 2服务中 3已完成 4已取消 5已评价', appoint_time DATETIME COMMENT '预约服务时间', address VARCHAR(255) COMMENT '服务地址', remark VARCHAR(500) COMMENT '需求描述', score TINYINT COMMENT '评价分数 1-5', comment VARCHAR(500) COMMENT '评价内容', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP );status字段单独拿出来讲。状态流转不要用魔法数字裸奔在代码里,一定要建枚举类,写清楚每个数字的含义。这个字段的设计直接决定了工单业务流程能不能顺畅跑起来,后面第5章会详细讲状态机的实现。
3.3 活动、志愿者、公告、求助:辅助模块的表设计
辅助模块的表相对简单,但有几个关联关系要留意。志愿者表是用户表的扩展,存技能标签和服务时长;活动表与报名表是一对多关系,活动删除时报名记录不能直接物理删除,要做判断;紧急求助表要记录处理人、处理时间、处理结果,形成完整的"发现-响应-处置"链路。
CREATE TABLE sos_alert ( id BIGINT PRIMARY KEY AUTO_INCREMENT, elder_id BIGINT NOT NULL, status TINYINT DEFAULT 0 COMMENT '0待处理 1已处理', handler_id BIGINT COMMENT '处理人id', handle_time DATETIME, handle_note VARCHAR(500) COMMENT '处理结果', create_time DATETIME DEFAULT CURRENT_TIMESTAMP );这里有个细节:紧急求助不求接,而是要求响应。所以这张表的状态只有待处理和已处理两个,工作人员在首页要能看到醒目的待处理列表,最好还能有声音或WebSocket提醒。如果只有一张普通的CRUD表,这个模块就失去了它的业务意义。
3.4 SQL脚本里的隐藏细节:保留字、字符集与初始化数据
SQL脚本这部分,我直接说几个常见的坑,都是实际帮同学改项目时踩过的。
第一,表名和字段名避开MySQL保留字。user、order、desc、rank这些词,建表时要么加反引号,要么换个名字。这套脚本里用的是sys_user和service_order,就是专门避开保留字的写法,你二次开发时新增表也要注意这个习惯。
第二,字符集和排序规则统一。建库语句必须是CREATE DATABASE community_service DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;。为什么用utf8mb4而不是utf8?因为utf8在MySQL里最多存3字节,emoji表情和一些特殊字符存不进去,一旦前端输入了这类内容,后端直接报错。排序规则统一用utf8mb4_general_ci,避免联表查询时因排序规则不一致报错。
第三,初始化数据要精心准备。这个平台的SQL脚本里通常预置了管理员账号、若干测试老人档案、服务项目字典数据。跑系统之前先执行脚本,然后用管理员账号登录,先去服务管理页看看数据长什么样,再去注册一个志愿者账号测试接单流程。初始化数据里不要出现真实身份信息,用"张三""李四"这类测试数据即可,答辩演示时也方便讲解。
4. 前后端联调中的四个高发坑:配置、格式、上传与分页
把一个前后端分离项目从"后端能跑"带到"前后端能顺畅联调",过程总是伴随着几个固定位置的坑。我把这些年在毕设项目里高频出现的问题集中说一下,每一条都对应真实场景,不要等到报错了再回来翻。
4.1 跨域:前后端分离项目的第一道坎
前端跑在8080,后端跑在8081,浏览器里一调接口就报CORS error,这是几乎所有前后端分离项目的必修课。前面提到前端用devServer代理可以规避,但如果前端没有配置代理,就需要后端开启跨域支持。
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } }注意这里用的是allowedOriginPatterns("*")而不是allowedOrigins("*")。因为当allowCredentials(true)时,Spring Boot 2.4以上版本不允许allowedOrigins("*"),必须用allowedOriginPatterns,否则启动不报错但请求会被拦。这个细节我见过不少同学纠结半天。
4.2 日期时间格式:LocalDateTime带来的显示问题
后端用LocalDateTime类型,前端拿到的时间字符串默认是2024-05-15T10:30:00这种带T的格式,直接显示在页面上非常丑。解决办法是在application.yml里统一配置:
spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8只配date-format对Date类型有效,对Java 8的LocalDateTime不生效,还要配合time-zone把时区指定到东八区,否则数据库里的时间可能差8个小时。如果配置完还是不对,就在LocalDateTime字段上单独加@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")注解,这种兜底方案要记牢。
4.3 文件上传:头像图片的存储与访问路径
用户头像、活动图片这类上传功能,开发环境跑得好好的,部署到服务器就失效,通常是文件存储路径写死了本地地址。正确的做法是配置文件里用相对路径或可配置路径,比如:
file: upload-dir: ./upload/前端上传接口返回的图片URL,也要考虑是返回完整路径还是相对路径。建议返回相对路径/upload/xxx.jpg,前端访问时拼上后端域名。开发环境用代理可以将/upload也代理到后端,部署阶段再用Nginx把上传目录映射出来。
这个模块比较隐蔽的坑是:上传文件格式没有做白名单限制,前端只做了一次文件类型校验,后端没有校验,导致绕过前端直接传恶意文件。虽然毕设不会真的有人攻击,但答辩时老师问"上传接口的安全性你怎么考虑",能答出"后端校验MIME类型和扩展名白名单"是一个很明显的加分项。
4.4 分页:MyBatis Plus分页插件的正确配置
用MyBatis Plus做分页,如果忘记了配置分页插件,你会发现selectPage方法返回的total是0,翻页永远只能查到第一页数据。这个坑的复现率极高,因为MyBatis Plus的分页插件是独立于核心包之外需要自己注入的:
@Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }前端的分页参数也要约定好。我建议统一用page表示页码从1开始,size表示每页条数,后端返回records、total、current、size四个字段。Element UI的el-pagination组件直接对应这套数据结构,前端不需要做二次转换。
5. 核心业务怎么实现:服务工单状态机与紧急求助链路
业务代码里最值得展开讲的是服务工单的状态设计,以及紧急求助的处理链路。这两个模块做扎实了,整个项目的技术含量立刻上一个档次。我见过很多同学的毕设,接口都是简单的增删改查,唯独缺了"业务规则"——而业务规则恰恰是评委区分"实现功能"和"理解了系统"的关键。
5.1 服务预约到评价的完整接口链路
我们先梳理完整链路:老人发起预约(或家属代操作)→ 生成待派单工单 → 工作人员查看待派单列表并指派志愿者 → 志愿者接单 → 按约定时间上门服务,标记服务中 → 服务完成,标记完成 → 老人或家属评价打分。
对应的接口大概是这些:
| 接口 | 路径 | 角色 |
|---|---|---|
| 创建工单 | POST /api/order/create | 老人/家属 |
| 工单分页查询 | GET /api/order/list | 全部角色 |
| 工作人员派单 | PUT /api/order/assign | 工作人员 |
| 志愿者接单 | PUT /api/order/accept | 志愿者 |
| 开始服务 | PUT /api/order/start | 志愿者 |
| 完成服务 | PUT /api/order/finish | 志愿者 |
| 取消工单 | PUT /api/order/cancel | 老人/工作人员 |
| 评价工单 | PUT /api/order/evaluate | 老人 |
接口的路径设计尽量放动词,比如create、assign、accept,而不是/order/updateStatus这种只站在数据角度设计的接口。RESTful风格的核心不是"用对了HTTP动词",而是"接口表达的是业务动作而不是数据操作"。
5.2 状态机的设计:拒绝非法流转
工单状态从0到5共六个状态,不能允许随便跳。比如一张已取消的工单不能突然变成已完成,一张还没派单的工单不能直接跳到服务中。如果没有状态机约束,前端调错接口或者有人拿Postman乱敲,数据就脏了。
public boolean validTransition(Integer current, Integer target) { // 0待派单 1已接单 2服务中 3已完成 4已取消 5已评价 Map<Integer, List<Integer>> rules = new HashMap<>(); rules.put(0, Arrays.asList(1, 4)); // 待派单 -> 已接单/取消 rules.put(1, Arrays.asList(2, 4)); // 已接单 -> 服务中/取消 rules.put(2, Arrays.asList(3)); // 服务中 -> 已完成 rules.put(3, Arrays.asList(5)); // 已完成 -> 已评价 return rules.getOrDefault(current, Collections.emptyList()).contains(target); }这个校验逻辑写在Service层,Controller先调validTransition,不通过直接抛业务异常。状态更新语句也要带上旧状态作为更新条件,比如UPDATE service_order SET status = 2 WHERE id = ? AND status = 1,这样即使并发情况下两个人同时操作,也只有一个人能成功。
5.3 紧急求助的优先级处理
紧急求助这个模块,我看到的源码里通常是一个独立的SOS按钮,点击后向后端发一条求助记录。但真正支撑这个功能的不只是一个接口,而是一套"信息完整性优先"的设计思路。
当老人点击求助按钮,前端把当前登录用户的ID传过去,后端自动关联出老人档案里的住址、紧急联系人、基础健康信息,生成一条完整的求助记录。工作人员端的首页接口里,要有专门的"待处理求助"查询,按时间倒序排列,未处理数量用红色角标在导航栏上提示。处理完成后,工作人员填写处理备注,状态更新为已处理。
这套链路虽然不复杂,但想清楚了一个关键点——紧急场景下,减少老人或家属的输入负担。你不需要让老人去填"我现在哪里不舒服、我在什么位置",系统自动带上这些信息,才是这个模块该有的样子。
5.4 通知消息的轻量实现
很多同学一看到"通知"就想到WebSocket,其实毕设项目里用更轻量、更易讲清楚的方式就够了。工单状态变化后,向相关用户插入一条通知记录,前端进入系统时轮询或进入页面时查询未读数量即可。
CREATE TABLE notification ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL COMMENT '接收人', title VARCHAR(100), content VARCHAR(500), is_read TINYINT DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP );如果想让项目更有亮点,可以再做一层轮询,前端每30秒调一次未读数量接口,有变化就弹出提醒。再深入一点,引入WebSocket做实时推送,在答辩时就能多聊几句"为什么选择WebSocket而不是轮询"——比如服务端主动推送、减少无效请求开销等。但注意,这块是加分项不是必需项,先把基础功能做稳,有余力再上。
6. 从能跑到能讲:部署交付、答辩准备与项目亮点提炼
代码写完只是完成了第一层,把项目完整地部署起来,能流畅地演示,并且能把设计思路讲清楚,才算真正做好了这套毕设。在这个阶段我遇到过太多"代码能跑但演示翻车"的例子,所以把最后的经验也一次性写透。
6.1 部署前必须检查的三件事
第一,检查数据库配置。很多同学在自己电脑上数据库密码是root/123456,到了演示环境忘了改,一启动就报连接拒绝。强烈建议把数据库账号密码提取到配置文件顶部,注释写清楚,或者直接在命令行启动时用--spring.profiles.active=prod切换配置。
第二,检查前端构建。开发环境跑得好好的是因为走了devServer代理,生产环境必须执行npm run build生成dist目录,由Nginx或后端静态资源路径托管。很多同学第一次打包,发现图片路径404、接口请求地址没写全,本质上都是没有理解"开发代理"和"生产部署"是两码事。接口地址建议用相对路径/api/xxx,部署到Nginx时统一配置反向代理。
第三,检查时间字段。如果SQL脚本里的时间字段用的是CURRENT_TIMESTAMP,而服务器时区不是东八区,你插入的数据时间就会和本地时间对不上。启动参数里加-Duser.timezone=GMT+8,或者在JVM参数里配置,演示时就省去这个麻烦。
6.2 接口文档怎么用:从一个创建工单的接口说起
接口文档不是摆设,它承担着"前后端契约"的作用。这个项目配套的接口文档里,每个接口都写清楚了请求地址、请求方式、请求参数和返回示例。我建议你在开发联调阶段就养成对照文档的习惯,而不是后端写完了扔给前端一句"你自己抓包看"。
拿创建工单接口来说,文档里应该明确:
| 项目 | 内容 |
|---|---|
| 请求地址 | POST /api/order/create |
| 请求头 | Authorization: Bearer {token} |
| Content-Type | application/json |
| 请求参数 | elderId(必填,Long)、itemId(必填,Long)、appointTime(必填,String,格式yyyy-MM-dd HH:mm)、address(必填,String)、remark(选填,String) |
| 返回示例 | {"code": 200, "message": "success", "data": {"orderId": 12, "orderNo": "20240415001"}} |
返回示例里只放关键字段,不要把所有实体字段都甩出去。前端只需要知道工单号生成了、记录主键是多少,数据处理就完成了。多余的字段只会让前端困惑"我到底该用哪个"。错误码部分也要约定好:401表示未登录或token过期,403表示无权访问,400表示参数错误,500表示服务器异常。错误信息在后端用message字段返回,前端统一弹提示。
6.3 答辩现场最常被追问的五类问题
答辩环节,评委问的问题其实很容易预判。我总结了几类高频问题,建议你在答辩前逐条准备:
第一,为什么选这个题目,社会意义是什么。这个问题不是让你背大话,而是讲清楚平台解决了"社区养老服务供需匹配效率低"这个实际问题。回答时聚焦业务场景,比如独居老人助餐需求怎么被响应,比大谈"老龄化社会挑战"要扎实得多。
第二,系统有哪些角色,权限是怎么控制的。准备这个问题时,把角色权限表背下来:四类角色、菜单不同、接口鉴权用拦截器校验JWT中的角色字段、前端用路由守卫控制页面访问。能流利答出"前端控制和后端校验双层配合"的,基本就是高分答案。
第三,核心业务流程怎么流转。把服务工单的状态机讲一遍,重点说清楚为什么要限制状态流转,以及怎么用数据库更新条件防止并发问题。
第四,遇到的最大技术难点是什么。不要编造,就把联调时踩过的日期格式、跨域、分页插件这些真实问题说一遍,然后重点讲你的排查思路——怎么定位是前端问题还是后端问题,怎么通过看日志一步步找到根因。这道题的得分点不在"解决了什么",而在"你怎么分析问题"。
第五,项目还有哪些可以改进的地方。这个问题千万别答"没有了"。准备2-3个真实可行的扩展方向:接入智能设备自动上报健康数据、用WebSocket优化紧急求助的实时推送、开发移动端小程序方便老人和家属使用。言之有物、逻辑自洽,反而给评委留下好印象。
6.4 项目亮点怎么提炼,以及还能往哪扩展
最后说说简历上怎么写这个项目。不要写"熟练使用SpringBoot+Vue开发了XX平台",这个描述等于没说。要写清楚三个层次:你解决了什么问题、你用了什么技术方案、方案里有什么值得说的设计。
以这个项目为例,可以这样提炼:
- 设计了服务工单状态机,覆盖预约到评价的完整流程,通过合法状态转移校验和条件更新,保证并发场景下工单状态的一致性。
- 设计了基于JWT的无状态认证方案,结合前端路由守卫与后端拦截器实现四类角色的接口级访问控制。
- 基于MyBatis Plus实现核心业务模块的快速开发,统一返回结构与全局异常处理,接口文档规范覆盖全部业务接口。
- 紧急求助模块通过自动关联老人档案、住址与紧急联系人,减少求助场景下的信息输入成本,缩短响应链路。
如果还有余力,我会建议你在这个基础上加一个数据统计模块。用ECharts画三张图:老人年龄段分布饼图、服务类型占比柱状图、月度工单完成率折线图。这不仅仅是视觉加分,更重要的是图背后关联的聚合查询,MySQL的GROUP BY、DATE_FORMAT这些SQL技能在面试时经常被问到。更进阶一点,可以谈一谈大数据量下如何用索引优化统计查询,比如在service_order表的create_time字段上建索引,统计时用覆盖索引避免回表。
部署层面,如果条件允许,试着把前端dist目录部署到Nginx,后端用mvn package打出来的jar包用nohup启动,数据库单独一台服务。这套标准的"前后端分离部署"流程走一遍,你在简历上写"熟悉Linux环境下的项目部署"就有底气了,不会一问细节就露怯。
开发这套系统时,我个人的习惯是先跑通一条最核心的链路——登录、建档案、发工单、派单、接单、完成、评价,然后再填充其他模块。这条链路一旦跑通,你会对整个系统的数据流转有完整的感知,后面每加一个模块,心里都有"它插在哪个环节"的清晰认知。这比从边缘模块做起、最后才拼核心链路要高效得多,也推荐你复现项目时采用同样的顺序。