
每年课设季和毕设季后台总有一批人问同一个问题想做一个基于 Spring Boot 的管理系统业务别太抽象功能别太简单CRUD 里能带一点权限、状态流转和统计报表最后还要有源码、数据库脚本和文档方便直接上手改。社区康养管理系统正好卡在这个需求点上。它不是一个纯理论的课程作业而是把“社区养老服务”这个真实场景搬到线上老人有健康档案服务人员能上门或预约服务管理员能看全局数据。业务边界清楚表结构不算复杂但又足够撑起一份毕业设计的设计说明书。这篇文章我会把整个系统的需求拆解、技术选型、数据库设计和核心代码实现全部过一遍最后把源码导入和常见踩坑也一并说清楚希望能帮你们少走弯路。1. 为什么这个系统值得做背景、定位与需求拆解1.1 社区康养的业务场景到底需要什么社区康养管理系统面向的是社区服务中心、养老服务站这类场景。传统方式下老人的健康档案是纸质表预约上门服务靠电话测量数据记在Excel里。问题很明显档案容易丢、服务过程没有记录、管理人员统计起来非常痛苦。系统要解决的就是三个核心问题档案电子化、服务流程线上化、运营数据可视化。老人或家属注册登录后可以维护个人健康档案提交血压、血糖等测量记录在线预约康养服务服务人员能查看预约、跟进服务状态管理员负责审核、发布活动公告以及查看各类统计报表。这个业务模型非常适合作课程设计或毕业设计因为它的功能边界清晰不会像“电商系统”那样牵扯支付、库存、物流一堆复杂模块同时又比单纯的学生管理系统更有业务感。你答辩的时候能讲清楚“这个系统给谁用、解决什么问题”比堆砌再多的功能都更有说服力。1.2 用户角色与功能模块梳理我在设计这个系统时把用户分成三个角色角色核心职责典型功能老人/家属前台用户维护自身信息、预约服务、记录健康数据注册登录、健康档案维护、测量记录添加、服务预约、查看公告服务人员/医护人员处理预约、录入服务结果查看预约列表、确认服务、填写服务反馈、维护服务项目系统管理员整体运营管理与数据查看用户管理、角色权限配置、活动公告发布、服务项目管理、统计报表围绕这三个角色功能模块可以划分为登录注册与权限管理、用户管理、健康档案管理、健康测量记录管理、服务项目管理、服务预约管理、活动公告管理、数据统计报表。1.3 我为什么推荐你们拿它做课设或毕设直接说理由。第一Spring Boot 的常规考点它基本全覆盖分层架构、MyBatis-Plus 数据操作、统一异常处理、JWT 或 Session 鉴权、文件上传、定时任务、统计查询。第二业务上有话可讲论文和答辩 PPT 不愁没内容。第三扩展性很强后续想加体检报告导入、家属多端查看、消息推送都是在现有架构上增加模块不会推倒重来。如果一个项目做出来只能演示“登录 增删改查”答辩老师问“你这个系统解决什么问题”就很容易卡壳。但康养管理系统天然具备社会价值背景你能从人口老龄化、社区养老资源匹配这些角度去解释系统设计动机整体完成度会高很多。2. 技术选型与架构设计不要为了炫技而复杂2.1 选型清单与背后理由先放一张我自己实践下来的选型清单下面逐个说原因技术项推荐选型选型理由后端框架Spring Boot 2.7.x成熟稳定教程多兼容 JDK8部署资料丰富ORM 框架MyBatis-Plus 3.5.x单表 CRUD 不需要写 SQL复杂统计可自定义 XML权限校验Spring Security JWT满足角色权限控制答辩时有深度可讲数据库MySQL 8.0主流且免费脚本通用性好连接池Druid自带监控页面课设里加 Druid 监控也是加分项前端方案Thymeleaf Bootstrap Layui 或 AdminLTE服务端渲染部署简单不需要单独起前端服务工具库Hutool、Lombok简化编码减少冗余代码图表展示ECharts统计报表直接引用 CDN效果直观很多人会纠结要不要用前后端分离比如 Vue Spring Boot。我的建议是如果这是课程设计优先用 Thymeleaf 服务端渲染。理由很现实答辩环境不一定稳定前后端分离意味着你需要同时跑后端服务和前端 node 服务一旦端口、跨域、代理出问题现场演示很容易翻车。而服务端渲染的应用打包后一个 jar 跑起来浏览器直接访问演示成本最低。2.2 项目分层结构与代码组织方式项目采用经典的四层结构Controller 接收请求、Service 处理业务、Mapper 操作数据库、Entity 映射表结构。中间再加一层 DTO 和 VO避免前端参数直接绑定实体类。Controller 层的职责是参数校验和结果封装业务逻辑不要写在 Controller 里。举个最简单的例子处理一个新增健康档案请求Controller 只做参数接收然后调用healthProfileService.addProfile(dto)返回统一 Result 对象。Service 层要有接口和实现类方便后续写单元测试。虽然课设里很少真测但这种习惯在真实项目里非常重要而且答辩时老师问到“为什么 Service 要拆接口”你能答出“解耦和便于测试”印象分会完全不同。统一返回结果我建议定义成public class ResultT { private Integer code; private String msg; private T data; }成功时code 200失败时code 500未登录或权限不足时分别返回401和403。这样前端只需要根据 code 值决定提示还是跳转不需要关心后端抛出的异常类型。2.3 项目结构说明与源码组织方式拿到源码后建议先看整体的包结构不要急着跑起来。一个清晰的 Spring Boot 项目包结构是这样的com.community.health ├── config // 配置类WebMvc、Security、Cors、MybatisPlus分页插件 ├── controller // 接口入口层 ├── service // 业务接口 │ └── impl // 业务实现 ├── mapper // MyBatis-Plus Mapper接口 ├── entity // 数据库实体 ├── dto // 接收参数的类 ├── vo // 返回给前端的类 ├── common // 工具类、全局常量、Result封装 ├── exception // 自定义异常与全局异常处理器 └── utils // JWT、日期处理等工具类resources 目录下templates放 Thymeleaf 页面static放 CSS、JS、图片mapper放自定义 SQL 的 XML 文件sql目录放初始化数据库脚本。文档的话一般会有一份万字设计文档内容包括需求分析、功能设计、数据库设计、系统实现和测试。3. 数据库设计先把地基打牢3.1 核心表清单与设计思路数据库表的设计决定了系统的上限。我当时设计这个系统时没有一股脑堆很多表而是紧扣业务需求。核心表大概 8 张名字你拿源码时对照一下基本上不会差太多表名中文含义关键字段说明sys_user系统用户表登录账号、密码、姓名、电话、角色ID、状态sys_role角色表角色名称、角色标识health_profile健康档案表身高、体重、血型、过敏史、慢性病史、家族病史health_record健康测量记录表用户ID、类型、数值、单位、测量时间service_item服务项目表服务名称、描述、时长、适用对象service_appointment服务预约表用户ID、项目ID、预约时间、时段、状态、备注activity_info活动公告表标题、内容、活动时间、地点、状态sys_announcement系统公告表标题、内容、发布时间、发布人设计思路遵循一个原则能用一张表表达清楚的就不要拆两张表。比如用户和角色我用角色ID字段关联没有做成标准的三表 RBAC因为课设场景不需要那么重的权限模型。但如果你论文里想体现更完整的权限设计也可以改成用户表、角色表、菜单表、用户角色关联表这种模式工作量会增加一些但对论文深度有帮助。3.2 关键表结构与字段说明以健康档案表为例这是康养系统的核心表。我给出关键建表 SQL你们导入源码后可以对照看CREATE TABLE health_profile ( id bigint(20) NOT NULL AUTO_INCREMENT, user_id bigint(20) NOT NULL COMMENT 关联的用户ID, height decimal(5,1) DEFAULT NULL COMMENT 身高cm, weight decimal(5,1) DEFAULT NULL COMMENT 体重kg, blood_type varchar(8) DEFAULT NULL COMMENT 血型, allergy_history varchar(255) DEFAULT NULL COMMENT 过敏史, chronic_disease varchar(255) DEFAULT NULL COMMENT 慢性病史, family_history varchar(255) DEFAULT NULL COMMENT 家族病史, remark varchar(500) DEFAULT NULL COMMENT 备注, create_time datetime DEFAULT NULL COMMENT 创建时间, update_time datetime DEFAULT NULL COMMENT 更新时间, deleted tinyint(1) DEFAULT 0 COMMENT 逻辑删除标识, PRIMARY KEY (id), KEY idx_user_id (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里有几个设计细节值得注意。第一user_id一定要建索引因为所有针对某个人的档案查询都要走这个字段。第二deleted字段用逻辑删除避免用户删除档案后相关历史记录彻底丢失。这在医疗健康类系统中是合理的设计思路。第三身高体重用decimal而不是int因为身高可能有 168.5 这种数据。需要一张关联查询示例图的话我口头描述一下逻辑关系。health_profile.user_id指向sys_user.idhealth_record.user_id也指向sys_user.idservice_appointment.user_id指向用户、service_appointment.item_id指向service_item.id。这就是典型的一对多和一对一关系。3.3 需要注意的数据库设计细节有几个坑我在实际开发中踩过提前写出来。第一所有表都要有create_time和update_time这是非常基础但重要的规范。用 MyBatis-Plus 的话可以直接配置字段自动填充不需要每次手动塞时间。第二varchar 长度要按实际业务设置。手机号码 11 位姓名 50 个字符足够但身份证号不要直接用 varchar(18)因为最后一位可能带 X你要预留长度同时加上校验。不过身份证明文存储不太合规课设为了方便可以存但论文里最好提一句生产环境应加密存储。第三也是很容易踩的坑如果开了逻辑删除同时又给某个字段建了唯一索引逻辑删除的数据会占用这个唯一键。比如账号字段如果加了唯一索引用户被逻辑删除后账号还在表里再注册同名账号就会被拦截。解决方法是唯一索引改成联合索引加上deleted字段或者删除时给账号字段拼一个时间戳后缀。4. 核心功能模块剖析关键代码是怎么写的4.1 登录鉴权与权限控制登录鉴权这块我用的方案是 Spring Security JWT。整体流程是用户提交用户名密码后端校验成功后生成 JWT token 返回前端前端后续请求在 Header 里带上Authorization: Bearer token后端通过过滤器解析 token 并设置登录用户上下文。关键配置类Configuration EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers(/api/auth/login, /api/auth/register, /static/**).permitAll() .antMatchers(/api/admin/**).hasRole(ADMIN) .antMatchers(/api/staff/**).hasAnyRole(ADMIN, STAFF) .anyRequest().authenticated() .and() .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); } }使用 Spring Security 的关键点是理解过滤器的执行顺序。我自己的JwtAuthenticationFilter必须在UsernamePasswordAuthenticationFilter之前执行因为要先解析 token 并把Authentication对象放到 SecurityContext 中后面的权限判断才能拿到用户角色。很多课设项目用简单的拦截器加 Session 也能实现登录我为什么推荐 Spring Security因为论文里可以写“基于 Spring Security 的认证与授权机制有效防止越权访问”这句话的含金量比“用拦截器判断是否登录”高不少。同时你也在真实提升自己的框架理解能力。4.2 健康档案与测量记录模块健康档案模块就是典型的单表 CRUD但有一点值得讲档案和测量记录之间的展示关系。前端页面展示档案时我不会简单地只查档案表而是把最近一次的健康测量记录一起带出来让老人或服务人员一眼看到当前的健康趋势。代码实现上可以用 MyBatis-Plus 的 ServiceImpl 减少重复代码。比如健康档案保存逻辑Override public Boolean saveProfile(HealthProfileDTO dto) { LambdaQueryWrapperHealthProfile wrapper new LambdaQueryWrapper(); wrapper.eq(HealthProfile::getUserId, dto.getUserId()); HealthProfile existing this.getOne(wrapper); if (existing ! null) { BeanUtils.copyProperties(dto, existing); return this.updateById(existing); } HealthProfile profile new HealthProfile(); BeanUtils.copyProperties(dto, profile); return this.save(profile); }这里用到一个小技巧同一个用户只能有一条健康档案所以新增的时候先查是否存在。存在就更新不存在就新增。这样前端不需要区分“新增档案”和“编辑档案”两个页面逻辑更统一。健康测量记录模块的核心是分页查询和趋势展示。分页用 MyBatis-Plus 自带的分页插件前端传页码和每页条数后端返回总条数和列表数据。展示趋势的时候把最近 7 天的血压数据从数据库查出来转换成年月日格式的列表交给前端 ECharts 画折线图。4.3 服务预约模块的状态流转服务预约是整个系统里最有业务感的模块因为它涉及状态的流转。我用一个 int 类型的 status 字段来表示状态值含义前端显示0待确认待确认1已确认服务人员已接单2已完成已完成3已取消已取消状态流转规则要写清楚用户提交预约后为待确认服务人员确认后变为已确认服务执行完成后变为已完成用户在待确认或已确认状态下可以取消。已完成的不能取消。这个规则直接在 Service 层判断public ResultString cancelAppointment(Long appointmentId, Long userId) { ServiceAppointment appointment this.getById(appointmentId); if (appointment null) { return Result.error(预约不存在); } if (appointment.getStatus() 2) { return Result.error(已完成的服务不能取消); } if (appointment.getStatus() 0 || appointment.getStatus() 1) { appointment.setStatus(3); this.updateById(appointment); return Result.success(取消成功); } return Result.error(当前状态不允许取消); }这就是状态机在业务里的简化应用。答辩时你可以说用状态字段控制整个预约生命周期通过在 Service 层校验当前状态来避免非法操作。这个设计点很小但能体现你的业务逻辑思考能力。4.4 统计报表怎么从数据库出数据统计报表听着高大上其实核心就是几个 SQL 聚合查询。最常见的是按日期统计预约数量SQL 长这样SELECT DATE_FORMAT(create_time, %Y-%m-%d) AS date, COUNT(*) AS total FROM service_appointment WHERE create_time #{startDate} AND create_time #{endDate} GROUP BY DATE_FORMAT(create_time, %Y-%m-%d) ORDER BY date这里有一个重点不能直接在 MySQL 里对 create_time 用%Y-%m-%d格式化后再 group by性能会差。但课设数据量不到一万条这种写法完全够用反而简单直观。更好的写法是用create_time ? AND create_time DATE_ADD(?, INTERVAL 1 DAY)这样的范围查询旁边再按字段分组不过复杂度会上去。统计模块我建议做一个“首页数据看板”展示四块内容总用户数、今日预约数、本月新增档案数、近7天预约趋势。数据看板一眼就能让答辩老师看出系统的完整性也是截图放进论文里的好素材。5. 源码导入与数据库初始化从零跑通项目5.1 环境准备拿到源码后别急着双击打开先把环境准备好。我推荐的环境版本如下软件推荐版本JDK1.8Maven3.6.3 或 3.8.xMySQL5.7 或 8.0IDEA2021 以上即可Lombok 插件IDEA 自带或手动安装JDK 版本为什么推荐 1.8因为这个项目的 Spring Boot 版本是 2.7.x对 JDK8 支持最好。你要是装了 JDK17 甚至 JDK21反而可能出现兼容问题。不少同学第一次跑不起来就是 IDEA 里 Project SDK 选错了版本。MySQL 建议用 8.0因为 8.0 的驱动类和 URL 写法跟 5.7 有区别。你拿到的源码如果用的是com.mysql.cj.jdbc.Driver就说明是针对 MySQL 8 写的不要拿着去跑 5.6 的旧库。5.2 导入项目与初始化数据库导入项目有两条路我用 IDEA 演示最常规的一条。第一步IDEA 中选File - New - Project from Existing Sources找到源码根目录的pom.xml以 Maven 项目方式导入。导入后等右下角的 Maven 依赖下载完成。如果下载慢参考后面的常见问题部分换镜像源。第二步在 MySQL 中建库。打开 Navicat 或命令行执行CREATE DATABASE community_health DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;注意数据库名不要随便换成别的除非你同步修改application.yml里的连接配置。建库后选择这个库运行源码中sql目录下的初始化脚本一般在sql/community_health.sql。运行完后你应该能在表列表里看到 sys_user、health_profile 这些表。第三步检查启动配置。打开application.yml核对三件事数据库名、用户名、密码。这三个是最容易漏改的也是启动报错最常见的原因。5.3 关键配置与启动验证启动项目前application.yml 里最核心的数据源配置是这个样子server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/community_health?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse username: root password: 123456 type: com.alibaba.druid.pool.DruidDataSource mybatis-plus: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.community.health.entity global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0启动主类后控制台出现 Spring Boot 的 Banner接着看到一行Tomcat started on port(s): 8080就说明启动成功了。然后浏览器访问http://localhost:8080/如果跳转到登录页说明前端资源和页面加载正常。默认账号密码在文档里一般会写清楚通常是admin / admin123之类。如果登录后页面报 500优先检查数据库密码和账号是否写对。另外一个小细节Thymeleaf 页面改动后需要刷新浏览器但如果你改了 Java 代码却不想重启项目可以在 IDEA 里配 Spring Boot DevTools 热部署。不需要专门重启终端不停页面也会自动刷新调试体验好很多。6. 常见问题与排障技巧实录6.1 应用启动类问题启动时最容易碰到两类报错一类是“端口被占用”另一类是“找不到主类或依赖冲突”。端口被占用时控制台会提示Port 8080 was already in use。解决办法改配置文件里的server.port改为 8081 或 9090。也可以直接把占用进程结束。Windows 上用netstat -ano | findstr 8080找到 PID然后taskkill /pid PID /fMac 或 Linux 上用lsof -i:8080。依赖冲突的问题通常表现为某个类找不到或者方法报 NoSuchMethodError。这种情况多半是引入了多个版本的同一依赖。我的经验是检查pom.xml去掉重复依赖或者用mvn dependency:tree查看依赖树。6.2 数据库连接问题数据库连接报错是重灾区最常见的几种错误信息我整理成了表格方便你们直接对照排查报错信息原因解决办法Access denied for user rootlocalhost用户名或密码错误修改 application.yml 中的账号密码Unknown database community_health数据库不存在或名字写错检查 SQL 脚本是否执行成功Server returns invalid timezone时区配置问题URL 加serverTimezoneAsia/ShanghaiPublic Key Retrieval is not allowedMySQL 8 驱动认证问题URL 加allowPublicKeyRetrievaltrueConnection refused数据库没启动打开 MySQL 服务确认端口为 3306最坑的是Public Key Retrieval is not allowed。MySQL 8 默认用 caching_sha2_password 认证JDBC 驱动第一次连接时无法直接获取公钥就会报这个错。解决方式就是在连接串上补一个参数allowPublicKeyRetrievaltrue。6.3 Maven 依赖下载与编译问题Maven 如果下载特别慢根本原因是没有配置国内镜像。IDEA 中打开settings.xml在 mirrors 节点里加阿里云仓库mirror idaliyunmaven/id mirrorOfcentral/mirrorOf name阿里云公共仓库/name urlhttps://maven.aliyun.com/repository/public/url /mirror改完镜像源后IDEA 里CtrlShiftF10或点刷新 Maven 项目重新下载即可。Compilation 报错如果指向 Lombok 的注解比如getter/setter 找不到多半是 IDEA 没有启用注解处理器。在 IDEA 的Settings - Build, Execution, Deployment - Compiler - Annotation Processors里勾选Enable annotation processing重启项目就好。6.4 功能层面的奇怪 Bug页面能打开但登录后跳到 404多半是因为前端访问的路径跟 Controller 里的映射不一致。比如控制器映射是/admin/index但页面里的链接写成了/admin/index.html。Thymeleaf 是模板引擎不是静态资源不需要后缀路径要对齐。还有一类问题比较隐蔽用了逻辑删除后一查数据就查不到。如果你自己写 SQL 时忘记加where deleted 0MyBatis-Plus 自动生成的查询都会带上但手写的 XML 不会。这就导致有些页面正常有些页面却看不到同一条记录。解决办法是自定义 XML 里的查询统一拼接deleted 0条件。还有如果你改过表结构比如新增了字段但实体类里没加对应属性MyBatis-Plus 查询时结果集映射会忽略这个字段页面取数据时自然就是 null。新增字段后注意同步实体类中的属性。结尾的个人收尾这套系统完整跑下来我个人最大的感受是课设和毕设项目功能多不如功能精。很多人一上来想做强杀、秒杀、高并发结果数据库表建了二十多张功能却都是半成品论文写得云里雾里。社区康养管理系统最大的优点就是业务链路完整每个模块能讲出设计理由代码量适中框架技术也拿得出手。如果你时间充裕可以在此基础上加一个体检报告导入导出、或者家属端分离的页面再补几张数据权限控制的图论文的实用性和完整度会上一个台阶。答辩前建议自己完整跑两遍演示流程把管理员新增用户、用户预约服务、服务人员处理预约、管理员查看统计报表这四个主流程连起来走一遍记得准备几条不同状态的预约数据免得现场临时造数据手忙脚乱。祝你们顺利。