1. 项目背景与需求分析
在当代社会结构转型的背景下,养老服务行业正面临前所未有的数字化升级需求。传统养老机构普遍存在信息孤岛、服务响应滞后等问题,而这款基于SpringBoot+Vue的大健康养老公寓管理系统,正是针对这些痛点设计的全栈解决方案。
系统核心要解决三个层面的问题:
- 管理效率层面:通过信息化手段替代纸质档案,实现健康数据自动采集、服务需求智能分配
- 服务质量层面:建立标准化服务流程,支持服务评价反馈机制
- 家属参与层面:提供实时健康数据可视化和消息推送功能
典型用户场景包括:
- 护工通过移动端接收服务工单,完成服务后扫码确认
- 家属通过微信小程序查看老人当日血压波动曲线
- 管理员在后台生成月度服务满意度统计报表
2. 技术架构设计
2.1 前后端分离架构
采用经典的B/S架构模式,前端使用Vue3+Element Plus实现响应式界面,后端基于SpringBoot2.7构建RESTful API。这种架构选择主要基于以下考量:
- 开发效率:Vue的组件化开发与SpringBoot的自动配置特性完美契合
- 性能优化:Axios拦截器实现Token自动刷新,减少重复登录
- 维护成本:Swagger UI文档与Postman集合双重保障接口可维护性
技术栈全景图:
code复制前端层:Vue3 + Pinia + Vite + Element Plus
网关层:Spring Cloud Gateway
服务层:SpringBoot + MyBatis-Plus + Redis
数据层:MySQL8.0 + Elasticsearch(日志)
2.2 数据库设计要点
系统共设计28张核心表,这里重点解析三个典型表的设计思路:
2.2.1 住户健康档案表(health_record)
sql复制CREATE TABLE `health_record` (
`health_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
`resident_code` VARCHAR(32) NOT NULL COMMENT '关联住户',
`blood_pressure` VARCHAR(20) DEFAULT NULL COMMENT '格式:收缩压/舒张压',
`blood_sugar` DECIMAL(5,2) DEFAULT NULL COMMENT '单位mmol/L',
`heart_rate` INT DEFAULT NULL COMMENT '次/分钟',
`temperature` DECIMAL(3,1) DEFAULT NULL COMMENT '摄氏度',
`spo2` TINYINT DEFAULT NULL COMMENT '血氧饱和度%',
`fall_detection` TINYINT(1) DEFAULT 0 COMMENT '跌倒检测',
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`health_id`),
INDEX `idx_resident` (`resident_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
设计特点:
- 采用复合健康指标存储,适应不同智能设备数据格式
- 建立居民维度索引,支持按人查询历史数据
- 时间戳自动更新,确保数据时效性
2.2.2 服务订单表(service_order)
java复制@Entity
@Table(name = "service_order")
public class ServiceOrder {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long orderId;
@Enumerated(EnumType.STRING)
private ServiceType serviceType; // 枚举定义服务类型
@ManyToOne
@JoinColumn(name = "resident_id")
private Resident resident;
@ManyToOne
@JoinColumn(name = "staff_id")
private Staff staff;
private LocalDateTime createTime;
private LocalDateTime finishTime;
@Column(length = 500)
private String remark;
// 状态机设计
public enum Status {
PENDING, ACCEPTED, PROCESSING, COMPLETED, CANCELLED
}
}
JPA实体类设计体现了:
- 使用枚举规范服务类型
- 多对一关联映射
- 状态机模式管理订单生命周期
3. 核心功能实现
3.1 健康数据可视化
前端采用ECharts实现动态图表,关键实现步骤:
- 数据聚合接口:
java复制@GetMapping("/health/stats/{residentId}")
public HealthStatsDTO getHealthStats(
@PathVariable String residentId,
@RequestParam(required = false) @DateTimeFormat(iso = ISO.DATE) LocalDate startDate,
@RequestParam(required = false) @DateTimeFormat(iso = ISO.DATE) LocalDate endDate) {
// 日期默认处理
if(startDate == null) startDate = LocalDate.now().minusDays(7);
if(endDate == null) endDate = LocalDate.now();
return healthService.getHealthStats(residentId, startDate, endDate);
}
- 前端图表配置:
javascript复制const initChart = () => {
chartInstance = echarts.init(domRef.value);
const option = {
tooltip: { trigger: 'axis' },
legend: { data: ['血压', '心率', '血糖'] },
xAxis: { type: 'category', data: timeData },
yAxis: [
{ name: '血压(mmHg)', type: 'value' },
{ name: '血糖(mmol/L)', type: 'value', offset: 40 }
],
series: [
{
name: '血压',
type: 'line',
smooth: true,
data: bloodPressureData,
lineStyle: { width: 3 }
},
// 其他系列配置...
]
};
chartInstance.setOption(option);
// 响应式处理
window.addEventListener('resize', () => chartInstance.resize());
};
注意事项:血压数据需要特殊处理,建议前端将"120/80"格式拆分为收缩压和舒张压两个数据系列展示
3.2 服务订单状态机
采用状态模式实现订单流转,核心状态转换逻辑:
java复制public class OrderStateMachine extends StateMachine<Status, Event> {
public OrderStateMachine() {
super(Status.PENDING);
// 定义状态转换规则
addTransition(Status.PENDING, Event.ACCEPT, Status.ACCEPTED)
.withAction(this::notifyResident);
addTransition(Status.ACCEPTED, Event.START, Status.PROCESSING)
.withGuard(this::checkStaffAvailability);
addTransition(Status.PROCESSING, Event.COMPLETE, Status.COMPLETED)
.withAction(this::generateEvaluation);
}
private boolean checkStaffAvailability(OrderContext context) {
return staffService.isAvailable(context.getStaffId());
}
// 其他业务方法...
}
状态转换触发方式:
java复制@PostMapping("/orders/{id}/accept")
public ResponseEntity<Void> acceptOrder(@PathVariable Long id) {
Order order = orderService.findById(id);
orderStateMachine.fire(Event.ACCEPT, new OrderContext(order));
return ResponseEntity.ok().build();
}
4. 权限控制方案
4.1 RBAC模型实现
系统设计五类角色:
- SUPER_ADMIN:系统超级管理员
- APARTMENT_ADMIN:公寓管理员
- STAFF:护工/服务人员
- FAMILY:家属
- RESIDENT:老人
权限拦截器核心逻辑:
java复制public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
String token = request.getHeader("Authorization");
if(StringUtils.isEmpty(token)) {
throw new UnauthorizedException("缺少访问令牌");
}
Claims claims = JwtUtil.parseToken(token);
String[] requiredRoles = getAnnotationRoles((HandlerMethod)handler);
if(!checkRoles(claims.get("roles", List.class), requiredRoles)) {
throw new ForbiddenException("权限不足");
}
request.setAttribute("USER_ID", claims.getSubject());
return true;
}
private String[] getAnnotationRoles(HandlerMethod method) {
RequireRoles annotation = method.getMethodAnnotation(RequireRoles.class);
return annotation != null ? annotation.value() : new String[0];
}
}
4.2 前端权限控制
Vue路由守卫实现:
javascript复制router.beforeEach((to, from, next) => {
const requiredRoles = to.meta?.roles || [];
const userRoles = store.state.user.roles;
if(requiredRoles.length > 0) {
const hasRole = requiredRoles.some(role => userRoles.includes(role));
if(!hasRole) return next('/403');
}
next();
});
组件级权限控制:
vue复制<template>
<el-button v-permission="['APARTMENT_ADMIN']">管理按钮</el-button>
</template>
// 权限指令实现
app.directive('permission', {
mounted(el, binding) {
const { value } = binding;
const roles = store.state.user.roles;
if(value && !roles.some(role => value.includes(role))) {
el.parentNode?.removeChild(el);
}
}
});
5. 项目部署实践
5.1 容器化部署方案
Docker Compose编排文件示例:
yaml复制version: '3.8'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASS}
MYSQL_DATABASE: elderly_care
volumes:
- mysql_data:/var/lib/mysql
ports:
- "3306:3306"
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
redis:
image: redis:6
ports:
- "6379:6379"
volumes:
- redis_data:/data
backend:
build: ./backend
ports:
- "8080:8080"
depends_on:
mysql:
condition: service_healthy
environment:
SPRING_PROFILES_ACTIVE: prod
frontend:
build: ./frontend
ports:
- "80:80"
depends_on:
- backend
volumes:
mysql_data:
redis_data:
5.2 性能优化实践
- 接口缓存策略:
java复制@Cacheable(value = "apartmentInfo", key = "#apartmentId")
public ApartmentDTO getApartmentDetail(String apartmentId) {
// 数据库查询逻辑
}
@CacheEvict(value = "apartmentInfo", key = "#dto.apartmentId")
public void updateApartment(ApartmentUpdateDTO dto) {
// 更新逻辑
}
- 前端懒加载方案:
javascript复制// 路由配置
const routes = [
{
path: '/health',
component: () => import('../views/HealthChart.vue'),
meta: { preload: true }
}
];
// 预加载策略
router.beforeResolve((to, from, next) => {
if(to.meta.preload) {
const components = router.resolve(to).route.matched.map(m => m.components.default);
Promise.all(components.map(c => c()))
.then(next)
.catch(next);
} else {
next();
}
});
6. 开发经验总结
在实现这个养老系统过程中,有几个关键经验值得分享:
- 时间处理陷阱:
- 前后端时间传递必须明确时区,建议统一使用UTC时间
- MySQL的DATETIME与TIMESTAMP选择:需要时区转换用TIMESTAMP,固定时间记录用DATETIME
- 状态管理技巧:
- 复杂表单建议使用Vuex/Pinia管理状态
- 对于跨组件共享数据,优先考虑provide/inject而非全局状态
- 异常处理规范:
java复制@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex) {
return ResponseEntity.status(ex.getStatusCode())
.body(new ErrorResponse(ex.getCode(), ex.getMessage()));
}
// 统一包装成功响应
@Override
protected ResponseEntity<Object> handleMethodArgumentValid(
MethodArgumentNotValidException ex,
HttpHeaders headers,
HttpStatus status,
WebRequest request) {
List<String> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(x -> x.getField() + ": " + x.getDefaultMessage())
.collect(Collectors.toList());
return ResponseEntity.badRequest()
.body(new ErrorResponse("VALIDATION_FAILED", errors));
}
}
- 前端性能监控:
javascript复制// 使用web-vitals库监控关键指标
import {getCLS, getFID, getLCP} from 'web-vitals';
function sendToAnalytics(metric) {
const body = JSON.stringify(metric);
navigator.sendBeacon('/analytics', body);
}
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getLCP(sendToAnalytics);
这个项目从技术架构到业务实现都体现了现代Web开发的典型模式,特别适合作为毕业设计项目来学习全栈开发技术栈。在实际开发中,建议先从数据库设计入手,再逐步实现服务层和前端界面,最后完善权限和监控等非功能需求。