当前位置:首页 > 技术 > 正文内容

SpringBoot与Vue驱动的养老服务平台架构实践

访客 技术 2026年6月4日 1

项目背景与行业价值

当前社会结构呈现显著老龄化特征,传统养老机构在资源调配、服务响应等方面存在明显短板。借助信息化手段重构养老服务流程,已成为提升机构运营效能、改善长者生活质量的关键路径。本平台通过整合物联网感知、实时数据分析和移动端交互技术,构建覆盖健康监测、护理调度、家属联动的全场景数字化管理体系。

整体技术架构

平台采用经典的分层架构设计,服务端以SpringBoot为核心承载业务逻辑,前端基于Vue3实现响应式界面,数据层通过关系型数据库与缓存中间件协同保障访问性能。

服务端技术选型

  • 核心框架:SpringBoot 3.x(JDK17+),内置Tomcat容器
  • 安全机制:Spring Security 6.x配合OAuth2.1协议,支持多因素认证
  • 持久化方案:MyBatis-Plus 3.5+,支持动态SQL与分页插件
  • 数据存储:MySQL 8.0主库 + Redis 7.x缓存集群
  • 消息中间件:RabbitMQ实现异步解耦,如告警通知、数据同步

前端技术选型

  • 框架版本:Vue 3.3+(Composition API + <script setup>语法)
  • 语言支持:TypeScript 5.x严格模式
  • UI组件库:Element Plus 2.x,自定义主题适配无障碍设计
  • 状态管理:Pinia 2.x,模块化管理用户、设备、告警状态
  • 构建工具:Vite 4.x,支持按需加载与热更新

核心模块实现

长者信息域模型

采用DDD思想设计实体关系,区分值对象与聚合根:

@Data
@TableName("senior_resident")
public class SeniorResident {
    @TableId(type = IdType.AUTO)
    private Long residentId;
    
    private String fullName;
    private LocalDate birthDate;
    private Integer gender;
    private String idCardNo;
    private String contactPhone;
    private String emergencyContact;
    private String emergencyPhone;
    private Integer careLevel;
    private LocalDateTime checkInTime;
    private Integer accountStatus;
    
    @TableField(exist = false)
    private List<HealthMetric> recentMetrics;
}

健康数据实时采集

通过MQTT协议接入智能穿戴设备,采用WebSocket向前端推送关键指标:

@Component
public class VitalSignsHandler {
    
    @Autowired
    private SimpMessagingTemplate messagingTemplate;
    
    @Autowired
    private AlertRuleEngine ruleEngine;
    
    @KafkaListener(topics = "device-telemetry", groupId = "health-monitor")
    public void processTelemetry(DevicePayload payload) {
        VitalSigns signs = parsePayload(payload);
        
        // 持久化原始数据
        vitalSignsRepository.save(signs);
        
        // 触发规则引擎检测异常
        List<AlertEvent> alerts = ruleEngine.evaluate(signs);
        alerts.forEach(alert -> {
            alertService.createAlert(alert);
            messagingTemplate.convertAndSend("/topic/alerts/" + alert.getResidentId(), alert);
        });
    }
}

前端实时告警组件

使用Vue3组合式API封装WebSocket连接,实现告警消息的自动订阅与展示:

<template>
  <div class="alert-panel">
    <transition-group name="alert-fade">
      <div v-for="alert in activeAlerts" :key="alert.alertId" 
           :class="['alert-card', severityClass(alert.level)]">
        <div class="alert-header">
          <span class="resident-name">{{ alert.residentName }}</span>
          <span class="alert-time">{{ formatTime(alert.triggerTime) }}</span>
        </div>
        <p class="alert-desc">{{ alert.description }}</p>
        <div class="alert-actions">
          <el-button size="small" @click="acknowledge(alert.alertId)">确认</el-button>
          <el-button size="small" type="primary" @click="dispatchCare(alert)">派单</el-button>
        </div>
      </div>
    </transition-group>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted, computed } from 'vue'
import { useAlertStore } from '@/stores/alert'
import type { AlertEvent } from '@/types/alert'

const alertStore = useAlertStore()
const socket = ref<WebSocket | null>(null)
const activeAlerts = computed(() => alertStore.pendingAlerts)

const severityClass = (level: number) => {
  const map: Record<number, string> = { 1: 'critical', 2: 'warning', 3: 'notice' }
  return map[level] || 'default'
}

const connectWebSocket = () => {
  const wsUrl = import.meta.env.VITE_WS_BASE + '/ws/alerts'
  socket.value = new WebSocket(wsUrl)
  
  socket.value.onmessage = (event) => {
    const alert: AlertEvent = JSON.parse(event.data)
    alertStore.pushAlert(alert)
    notifySound(alert.level)
  }
}

const acknowledge = async (id: string) => {
  await alertStore.resolveAlert(id)
}

const dispatchCare = (alert: AlertEvent) => {
  // 调用护理调度服务
}

onMounted(connectWebSocket)
onUnmounted(() => socket.value?.close())
</script>

护理工单智能调度

基于贪心算法实现护理人员的动态任务分配,考虑位置距离、技能匹配、工作负荷等多维度因素:

@Service
public class CareDispatchService {
    
    public DispatchResult assignTask(CareTask task) {
        List<StaffProfile> candidates = staffRepository
            .findAvailableStaff(task.getRequiredSkills(), task.getLocationZone());
        
        Optional<StaffProfile> optimal = candidates.stream()
            .map(staff -> Map.entry(staff, calculateScore(staff, task)))
            .max(Comparator.comparingDouble(Map.Entry::getValue))
            .map(Map.Entry::getKey);
        
        return optimal.map(staff -> {
            task.assignTo(staff.getStaffId());
            taskRepository.updateStatus(task.getTaskId(), TaskStatus.ASSIGNED);
            notificationService.pushToStaff(staff.getDeviceToken(), task);
            return DispatchResult.success(staff);
        }).orElse(DispatchResult.failed("无可用人员"));
    }
    
    private double calculateScore(StaffProfile staff, CareTask task) {
        double distanceScore = 1.0 / (haversine(staff.getCurrentLocation(), task.getLocation()) + 0.1);
        double loadScore = 1.0 / (staff.getPendingTasks() + 1);
        double skillScore = staff.getSkillMatchRate(task.getRequiredSkills());
        return 0.4 * distanceScore + 0.3 * loadScore + 0.3 * skillScore;
    }
}

数据库关键设计

表名核心字段设计要点
senior_residentresident_id, care_level, room_idcare_level建立B-tree索引,支持分级查询
vital_signsrecord_id, resident_id, metric_time按metric_time分区,保留90天热数据
care_tasktask_id, assignee_id, status, deadline复合索引(status, deadline)优化待办查询
iot_devicedevice_sn, resident_id, online_statusdevice_sn唯一约束,关联Redis在线状态

性能优化策略

针对高频查询场景实施专项优化:

  • 健康数据查询:采用时间分区表 + 预聚合视图,将7日趋势查询从秒级降至毫秒级
  • 实时告警推送:Redis Stream作为消息缓冲,削峰填谷保障WebSocket稳定性
  • 移动端接口:启用Gzip压缩 + 字段裁剪,减少弱网环境下的传输耗时
  • 报表生成:异步任务队列处理,结果落库后通过SSE通知前端下载

部署架构

生产环境采用Docker Compose编排,核心服务定义如下:

version: '3.8'
services:
  api-gateway:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      
  app-server:
    build: ./backend
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - DB_HOST=mysql-cluster
      - REDIS_HOST=redis-sentinel
    depends_on:
      - mysql-cluster
      - redis-sentinel
      
  vue-admin:
    build: ./frontend/admin
    depends_on:
      - app-server
      
  ws-server:
    build: ./websocket-service
    ports:
      - "8088:8088"
    environment:
      - REDIS_HOST=redis-sentinel

该架构通过Nginx实现反向代理与负载均衡,WebSocket服务独立部署避免长连接阻塞HTTP请求,MySQL采用主从复制保障读性能,Redis哨兵模式确保缓存高可用。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。