1. 项目概述
作为一名有着10年Java开发经验的程序员,我想分享一个基于Java的养老院管理系统的完整开发过程。这个项目采用了当前主流的Spring Boot+Vue前后端分离架构,涵盖了从需求分析到系统测试的全流程。对于计算机专业的学生来说,这是一个非常典型的毕业设计选题,既包含了完整的技术栈应用,又具有实际的社会应用价值。
养老院管理系统主要解决养老机构在日常运营中的信息化管理需求,包括老人信息管理、员工管理、床位管理、健康监测、费用结算等功能模块。系统采用B/S架构,管理员和工作人员可以通过浏览器直接访问,操作简便,无需安装额外客户端。
2. 技术选型与架构设计
2.1 后端技术栈
后端采用Spring Boot框架作为基础,这是目前Java领域最流行的微服务框架。选择Spring Boot主要基于以下几个考虑:
- 快速开发:Spring Boot的自动配置和起步依赖大大减少了配置工作量,可以快速搭建项目骨架
- 生态丰富:Spring生态拥有大量成熟的解决方案,如安全认证、数据访问等
- 易于扩展:当系统需要扩展功能时,可以方便地集成其他Spring组件
数据库访问层使用MyBatis-Plus,相比原生MyBatis,它提供了更多便捷功能:
- 通用CRUD操作,减少重复代码
- 强大的条件构造器,简化复杂查询
- 分页插件,支持多种数据库分页
- 性能分析插件,帮助优化SQL
2.2 前端技术栈
前端采用Vue.js框架,主要优势包括:
- 组件化开发:将UI拆分为独立可复用的组件,提高开发效率
- 响应式设计:数据驱动视图,自动更新DOM
- 丰富的生态系统:Vue Router、Vuex等配套工具完善
- 渐进式框架:可以根据项目需求灵活选择功能
前端工程使用Vue CLI搭建,集成Webpack打包工具,支持热重载等开发便利功能。
2.3 系统架构设计
系统采用典型的三层架构:
- 表现层:Vue前端负责用户界面展示和交互
- 业务逻辑层:Spring Boot后端处理业务逻辑
- 数据访问层:MyBatis-Plus操作MySQL数据库
这种分层架构的优点是职责分离,便于维护和扩展。前后端通过RESTful API进行通信,接口设计遵循以下原则:
- 使用HTTP动词表达操作类型(GET/POST/PUT/DELETE)
- 资源使用名词复数形式(如/api/users)
- 状态码准确反映操作结果
- 返回统一格式的JSON数据
3. 数据库设计
3.1 数据库表结构
系统主要包含以下几张核心表:
-
用户表(sys_user):存储系统用户信息
sql复制CREATE TABLE `sys_user` ( `user_id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID', `username` varchar(50) NOT NULL COMMENT '用户名', `password` varchar(100) NOT NULL COMMENT '密码', `real_name` varchar(50) DEFAULT NULL COMMENT '真实姓名', `phone` varchar(20) DEFAULT NULL COMMENT '手机号', `email` varchar(100) DEFAULT NULL COMMENT '邮箱', `avatar` varchar(255) DEFAULT NULL COMMENT '头像', `status` tinyint DEFAULT '1' COMMENT '状态 0:禁用 1:正常', `create_time` datetime DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (`user_id`), UNIQUE KEY `username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统用户'; -
老人信息表(elderly):存储养老院老人基本信息
sql复制CREATE TABLE `elderly` ( `elderly_id` bigint NOT NULL AUTO_INCREMENT COMMENT '老人ID', `name` varchar(50) NOT NULL COMMENT '姓名', `gender` tinyint DEFAULT NULL COMMENT '性别 0:女 1:男', `birth_date` date DEFAULT NULL COMMENT '出生日期', `id_card` varchar(20) DEFAULT NULL COMMENT '身份证号', `phone` varchar(20) DEFAULT NULL COMMENT '联系电话', `address` varchar(255) DEFAULT NULL COMMENT '家庭住址', `check_in_date` date DEFAULT NULL COMMENT '入住日期', `health_status` varchar(50) DEFAULT NULL COMMENT '健康状况', `bed_id` bigint DEFAULT NULL COMMENT '床位ID', `guardian_name` varchar(50) DEFAULT NULL COMMENT '监护人姓名', `guardian_phone` varchar(20) DEFAULT NULL COMMENT '监护人电话', `create_time` datetime DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (`elderly_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='老人信息'; -
床位表(bed):管理养老院床位资源
sql复制CREATE TABLE `bed` ( `bed_id` bigint NOT NULL AUTO_INCREMENT COMMENT '床位ID', `bed_no` varchar(20) NOT NULL COMMENT '床位编号', `room_no` varchar(20) DEFAULT NULL COMMENT '房间号', `floor` int DEFAULT NULL COMMENT '楼层', `status` tinyint DEFAULT '0' COMMENT '状态 0:空闲 1:已占用', `price` decimal(10,2) DEFAULT NULL COMMENT '床位价格', `description` varchar(255) DEFAULT NULL COMMENT '描述', PRIMARY KEY (`bed_id`), UNIQUE KEY `bed_no` (`bed_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='床位信息';
3.2 数据库关系设计
系统使用外键建立表间关联关系:
- 老人表(elderly)通过bed_id关联床位表(bed)
- 护理记录表(care_record)通过elderly_id关联老人表
- 费用记录表(payment)通过elderly_id关联老人表
这种关系设计保证了数据的一致性和完整性。例如,当删除一个老人记录时,相关的护理记录和费用记录也会被级联删除。
4. 核心功能实现
4.1 用户认证与授权
系统采用基于Token的认证机制,流程如下:
- 用户登录时,后端验证用户名密码
- 验证通过后生成JWT Token返回给前端
- 前端将Token存储在localStorage中
- 后续请求在Authorization头中携带Token
- 后端拦截器验证Token有效性
Spring Security配置示例:
java复制@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/**").authenticated()
.anyRequest().permitAll();
http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
4.2 老人信息管理
老人信息管理模块提供CRUD操作,核心代码如下:
- Controller层
java复制@RestController
@RequestMapping("/api/elderly")
public class ElderlyController {
@Autowired
private ElderlyService elderlyService;
@GetMapping("/list")
public Result list(@RequestParam Map<String, Object> params) {
PageUtils page = elderlyService.queryPage(params);
return Result.ok().put("page", page);
}
@GetMapping("/info/{elderlyId}")
public Result info(@PathVariable("elderlyId") Long elderlyId) {
ElderlyEntity elderly = elderlyService.getById(elderlyId);
return Result.ok().put("elderly", elderly);
}
@PostMapping("/save")
public Result save(@RequestBody ElderlyEntity elderly) {
elderlyService.saveElderly(elderly);
return Result.ok();
}
@PostMapping("/update")
public Result update(@RequestBody ElderlyEntity elderly) {
elderlyService.updateById(elderly);
return Result.ok();
}
@PostMapping("/delete")
public Result delete(@RequestBody Long[] elderlyIds) {
elderlyService.removeByIds(Arrays.asList(elderlyIds));
return Result.ok();
}
}
- Service层实现分页查询
java复制@Service
public class ElderlyServiceImpl extends ServiceImpl<ElderlyDao, ElderlyEntity> implements ElderlyService {
@Override
public PageUtils queryPage(Map<String, Object> params) {
String key = (String) params.get("key");
IPage<ElderlyEntity> page = this.page(
new Query<ElderlyEntity>().getPage(params),
new QueryWrapper<ElderlyEntity>()
.like(StringUtils.isNotBlank(key), "name", key)
.or()
.like(StringUtils.isNotBlank(key), "id_card", key)
.or()
.like(StringUtils.isNotBlank(key), "phone", key)
);
return new PageUtils(page);
}
}
4.3 床位管理
床位管理模块实现了床位分配、状态变更等功能:
- 床位分配逻辑
java复制public Result assignBed(Long elderlyId, Long bedId) {
// 检查床位是否可用
BedEntity bed = bedService.getById(bedId);
if (bed == null || bed.getStatus() == 1) {
return Result.error("床位不可用");
}
// 更新老人床位信息
ElderlyEntity elderly = elderlyService.getById(elderlyId);
if (elderly == null) {
return Result.error("老人不存在");
}
// 如果老人已有床位,先释放原床位
if (elderly.getBedId() != null) {
BedEntity oldBed = bedService.getById(elderly.getBedId());
oldBed.setStatus(0);
bedService.updateById(oldBed);
}
// 分配新床位
elderly.setBedId(bedId);
elderlyService.updateById(elderly);
bed.setStatus(1);
bedService.updateById(bed);
return Result.ok();
}
- 床位状态变更
java复制public Result changeBedStatus(Long bedId, Integer status) {
BedEntity bed = bedService.getById(bedId);
if (bed == null) {
return Result.error("床位不存在");
}
// 如果要设置为已占用,检查是否已有老人使用
if (status == 1) {
Long count = elderlyService.count(
new QueryWrapper<ElderlyEntity>().eq("bed_id", bedId)
);
if (count > 0) {
return Result.error("该床位已被占用");
}
}
bed.setStatus(status);
bedService.updateById(bed);
return Result.ok();
}
5. 系统测试与部署
5.1 单元测试
使用JUnit进行单元测试,确保各模块功能正常:
java复制@SpringBootTest
public class ElderlyServiceTest {
@Autowired
private ElderlyService elderlyService;
@Test
public void testSaveElderly() {
ElderlyEntity elderly = new ElderlyEntity();
elderly.setName("测试老人");
elderly.setGender(1);
elderly.setBirthDate(new Date());
elderly.setPhone("13800138000");
boolean result = elderlyService.save(elderly);
assertTrue(result);
ElderlyEntity saved = elderlyService.getById(elderly.getElderlyId());
assertNotNull(saved);
assertEquals("测试老人", saved.getName());
}
@Test
public void testUpdateElderly() {
ElderlyEntity elderly = elderlyService.getById(1L);
assertNotNull(elderly);
String newPhone = "13900139000";
elderly.setPhone(newPhone);
boolean result = elderlyService.updateById(elderly);
assertTrue(result);
ElderlyEntity updated = elderlyService.getById(1L);
assertEquals(newPhone, updated.getPhone());
}
}
5.2 接口测试
使用Postman进行接口测试,验证API功能:
- 登录接口测试
code复制POST /api/auth/login
Content-Type: application/json
{
"username": "admin",
"password": "admin123"
}
- 获取老人列表
code复制GET /api/elderly/list?page=1&limit=10
Authorization: Bearer {token}
- 添加老人信息
code复制POST /api/elderly/save
Content-Type: application/json
Authorization: Bearer {token}
{
"name": "张三",
"gender": 1,
"birthDate": "1940-05-15",
"phone": "13800138000",
"address": "北京市朝阳区",
"checkInDate": "2023-01-10",
"healthStatus": "良好"
}
5.3 系统部署
系统采用Docker容器化部署,部署流程如下:
- 构建后端镜像
dockerfile复制FROM openjdk:8-jdk-alpine
VOLUME /tmp
ADD target/nursing-home-0.0.1-SNAPSHOT.jar app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
- 构建前端镜像
dockerfile复制FROM nginx:alpine
COPY dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
- 使用docker-compose编排服务
yaml复制version: '3'
services:
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: nursing_home
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
backend:
build: ./backend
ports:
- "8080:8080"
depends_on:
- mysql
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/nursing_home?useSSL=false
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: root
frontend:
build: ./frontend
ports:
- "80:80"
depends_on:
- backend
volumes:
mysql_data:
6. 开发经验与注意事项
6.1 开发中的常见问题
-
跨域问题:前后端分离开发时,浏览器会阻止跨域请求。解决方案:
- 后端配置CORS
- 开发环境使用代理
- Nginx反向代理
-
日期时间处理:前后端日期格式不一致会导致问题。建议:
- 统一使用ISO8601格式(yyyy-MM-dd'T'HH:mm:ss.SSS'Z')
- 使用Jackson配置全局日期格式
java复制@Configuration public class JacksonConfig { @Bean public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() { return builder -> { builder.simpleDateFormat("yyyy-MM-dd HH:mm:ss"); builder.serializers(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); builder.serializers(new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); }; } } -
密码安全:绝对不能明文存储密码。必须:
- 使用BCrypt等强哈希算法
- 加盐处理
- 定期更换加密方式
6.2 性能优化建议
-
数据库优化:
- 为常用查询字段添加索引
- 避免SELECT *,只查询需要的字段
- 合理使用JOIN,避免多表关联查询
-
缓存应用:
- 使用Redis缓存热点数据
- 实现二级缓存(MyBatis缓存+Redis)
- 合理设置缓存过期时间
-
前端优化:
- 组件懒加载
- 路由懒加载
- 使用CDN加速静态资源
6.3 项目扩展方向
- 移动端适配:开发微信小程序或APP版本
- 智能设备对接:接入智能手环等设备,实时监测老人健康数据
- 数据分析:基于老人健康数据进行分析预测
- 家属端:开发家属专用入口,方便查看老人情况
在实际开发过程中,我发现有几个关键点需要特别注意:
-
需求明确:养老院管理系统的需求一定要与客户充分沟通,特别是各种业务流程和特殊情况的处理。
-
数据安全:老人健康信息属于敏感数据,必须做好数据加密和访问控制。
-
用户体验:系统使用者可能包括不熟悉电脑的老人院工作人员,界面设计要简洁明了,操作流程要尽可能简单。
-
系统稳定性:系统需要7×24小时运行,必须考虑各种异常情况的处理,如网络中断、数据库连接失败等。
这个项目从技术角度来说涵盖了Java开发的完整技术栈,包括Spring Boot、MyBatis、Vue.js等主流框架,非常适合作为毕业设计选题。通过这个项目,学生可以掌握企业级应用开发的完整流程,积累实战经验。