Files
smartClean/docs/architecture-flow.md
xqzp2026 8373460096 feat: 添加自动化部署方案(Docker + 远程服务器两套方案)
- 新增 deploy/docker/:Docker 本机模拟部署,含 Dockerfile、docker-compose、deploy.sh 一键脚本
- 新增 deploy/remote/:远程服务器部署,含 SSH 自动上传、重启、回滚脚本
- 新增 deploy/README.md:完整使用手册,含现状分析、落地调整工作清单、命令速查
- 新增 build.sh/start.sh:本地构建和启动脚本(含飞书通知)
- 新增前端 .env.docker 环境配置,API 指向测试服务器
- 前端 package.json 新增 build-docker 命令
- 更新 .gitignore:排除 IDE 配置、SQL 数据、Docker 敏感文件
- 前端 UI 样式优化(多个页面组件)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 18:41:15 +09:30

706 lines
58 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 智慧清洁 SaaS 平台 - 系统流程架构图
## 一、系统整体架构
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 客户端层 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Web 管理后台 │ │ 手机 APP │ │ 智能手表 │ │
│ │ (Vue 3 SPA) │ │ (Android/iOS)│ │ (IoT 设备) │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ HTTP/REST │ HTTP/REST │ MQTT │
└─────────┼───────────────────┼───────────────────┼──────────────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 服务层 │
│ ┌─────────────────────────────────┐ ┌─────────────────────────────────┐ │
│ │ Web 服务 (端口 8095) │ │ Task 服务 (端口 8097) │ │
│ │ ┌───────────┐ ┌─────────────┐ │ │ ┌───────────┐ ┌────────────┐ │ │
│ │ │ Controller│ │ Interceptor │ │ │ │ XXL-Job │ │ @Scheduled │ │ │
│ │ │ (API) │ │ (认证签名) │ │ │ │ (定时任务) │ │ (消息队列) │ │ │
│ │ └─────┬─────┘ └──────┬──────┘ │ │ └─────┬─────┘ └─────┬──────┘ │ │
│ │ │ │ │ │ │ │ │ │
│ │ ┌─────▼──────────────▼──────┐ │ │ ┌─────▼─────────────▼───────┐ │ │
│ │ │ Service 层 │ │ │ │ Service 层 │ │ │
│ │ └─────────────┬─────────────┘ │ │ └────────────┬──────────────┘ │ │
│ │ │ │ │ │ │ │
│ │ ┌─────────────▼─────────────┐ │ │ ┌────────────▼──────────────┐ │ │
│ │ │ Mapper (MyBatis-Plus) │ │ │ │ Mapper (MyBatis-Plus) │ │ │
│ │ └─────────────┬─────────────┘ │ │ └────────────┬──────────────┘ │ │
│ └────────────────┼────────────────┘ └───────────────┼─────────────────┘ │
│ │ │ │
└───────────────────┼─────────────────────────────────────┼──────────────────────┘
│ │
┌─────────▼─────────────────────────────────────▼──────────┐
│ 数据层 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ MySQL DB1 │ │ MySQL DB2 │ │ Redis │ │
│ │ (基础信息) │ │ (业务数据) │ │ (DB4+DB5) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────┘
```
---
## 二、Web 请求完整链路
### 2.1 前端 HTTP 请求 → 后端响应
```
┌──────────────────────────────────────────────────────────────────────────────┐
│ 前端 (Vue 3 SPA) │
│ │
│ 用户操作 (点击按钮/提交表单) │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ ApiService (如 taskCenterService.js) │ │
│ │ 调用封装的 API 方法 │ │
│ └──────────────────────────┬──────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ requestUtils.js - Axios 请求拦截器 │ │
│ │ │ │
│ │ ① 从 sessionStorage 读取登录信息 (UUID, phone) │ │
│ │ ② 生成签名: MD5(MD5(uuid) + MD5(timestamp + phone)) │ │
│ │ ③ 设置请求头: │ │
│ │ - signature: 签名值 │ │
│ │ - uuid: 用户UUID │ │
│ │ - timestamp: 当前时间戳 │ │
│ │ - webId: 当前页面ID │ │
│ │ ④ 清除空值参数 │ │
│ │ ⑤ 设置 withCredentials: true │ │
│ └──────────────────────────┬──────────────────────────────────────┘ │
│ │ │
│ │ POST /api/xxx │
│ │ (通过 Vite proxy 代理到 localhost:8095) │
└─────────────────────────────┼────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────┐
│ 后端 Web 服务 (Spring Boot, 端口 8095) │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ ① SystemInterceptor (请求拦截器) │ │
│ │ │ │
│ │ 排除路径: /login, /sms/sendCode, /doc.html, /swagger │ │
│ │ │ │
│ │ a. 提取请求头: uuid, signature, timestamp │ │
│ │ b. 验证时间戳 (10秒有效窗口) │ │
│ │ c. 从 Redis 查询登录态: │ │
│ │ Redis GET uuid → 获取用户信息(mobile等) │ │
│ │ d. 验证签名: │ │
│ │ 服务端重算 MD5 签名 vs 请求头签名 │ │
│ │ e. 验证通过 → 将用户信息存入 Request Attribute │ │
│ │ f. 验证失败 → 返回 code 104 (需重新登录) │ │
│ │ │ │
│ └──────────────────────────┬──────────────────────────────────────┘ │
│ │ 认证通过 │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ ② RepeatSubmitAspect (AOP 防重复提交) │ │
│ │ │ │
│ │ a. 生成锁 Key: "redis_lock_" + 请求路径 │ │
│ │ b. Redis SETNX 尝试加锁 (30秒过期) │ │
│ │ c. 获取锁 → 继续执行 │ │
│ │ d. 未获取锁 → 返回 "请勿重复提交" │ │
│ │ │ │
│ └──────────────────────────┬──────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ ③ Controller (如 TaskController) │ │
│ │ │ │
│ │ a. 接收请求参数 (@RequestBody / @RequestParam) │ │
│ │ b. 从 Request Attribute 获取当前用户信息 │ │
│ │ c. 调用 Service 层处理业务逻辑 │ │
│ │ │ │
│ └──────────────────────────┬──────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ ④ Service 层 │ │
│ │ │ │
│ │ a. 业务逻辑处理 │ │
│ │ b. 组合多个 Mapper 查询/更新 │ │
│ │ c. 操作 Redis 缓存 (读/写) │ │
│ │ d. 写入变更日志 (ChangeLog) │ │
│ │ │ │
│ └──────────────────────────┬──────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ ⑤ Mapper 层 (MyBatis-Plus) │ │
│ │ │ │
│ │ a. DB1 Mapper → MySQL xiaoqu_comples_d (基础数据) │ │
│ │ b. DB2 Mapper → MySQL xiaoqu_intellectual_d (业务数据) │ │
│ │ c. 支持分页查询 (IPage) │ │
│ │ d. 支持动态条件构造 (QueryWrapper) │ │
│ │ │ │
│ └──────────────────────────┬──────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ ⑥ SetUserAspect (AOP 审计日志) │ │
│ │ │ │
│ │ a. 记录操作人 (create_id / modify_id) │ │
│ │ b. 记录操作时间 (create_time / modify_time) │ │
│ │ c. 记录变更日志 (ChangeLog) │ │
│ │ │ │
│ └──────────────────────────┬──────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ ⑦ 构造响应 (统一返回格式) │ │
│ │ │ │
│ │ 成功: { code: 100, data: {...}, msg: "操作成功" } │ │
│ │ 失败: { code: 101, data: null, msg: "错误信息" } │ │
│ │ 未登录: { code: 104, data: null, msg: "请重新登录" } │ │
│ │ 指纹无效: { code: 113, data: null, msg: "指纹无效" } │ │
│ │ │ │
│ └──────────────────────────┬──────────────────────────────────────┘ │
│ │ │
└─────────────────────────────┼────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────┐
│ 前端 (Vue 3 SPA) │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ requestUtils.js - Axios 响应拦截器 │ │
│ │ │ │
│ │ ① code == 100 → 返回 data 给业务组件 │ │
│ │ ② code == 104 → 清除 sessionStorage → 跳转登录页 │ │
│ │ ③ code == 113 → 指纹无效 → 跳转登录页 │ │
│ │ ④ 其他 code → 显示错误提示 (ElMessage) │ │
│ │ │ │
│ └──────────────────────────┬──────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 页面组件渲染 │ │
│ │ │ │
│ │ a. qu-table 根据权限组动态渲染列 (fetchFields) │ │
│ │ b. qu-button-group 根据权限组动态渲染按钮 (fetchButtons) │ │
│ │ c. 数据绑定到 Vuex Store / 组件 data │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
```
### 2.2 登录认证流程
```
用户输入手机号
┌──────────────┐ GET /sms/sendCode?mobile=xxx
│ 获取验证码 │ ──────────────────────────────────► SendSmsController
└──────┬───────┘ │
│ ▼
│ 生成验证码 → Redis SET
│ 调用短信平台发送
┌──────────────┐ POST /login {mobile, code}
│ 提交登录 │ ──────────────────────────────────► LoginController
└──────┬───────┘ │
│ ▼
│ ① 验证码校验 (Redis GET)
│ ② 查询用户 (MySQL user表)
│ ③ 生成 UUID
│ ④ Redis SET uuid → 用户信息 (30天)
│ ⑤ 记录登录日志 (login_log)
│ ⑥ 返回: uuid + 用户信息 + 菜单权限
┌──────────────┐
│ sessionStorage│ ← 存储 loginInfo(base64), uuid, phone
│ 后续请求携带 │ → signature, uuid, timestamp 请求头
└──────────────┘
```
---
## 三、任务调度完整链路
### 3.1 计划 → 任务生成 → 派单 → 推送
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ 阶段一:计划创建与启用 (Web 服务) │
│ │
│ 管理员操作 (Web后台) │
│ │ │
│ ▼ │
│ POST /plan/addOrUpdatePlan │
│ │ │
│ ▼ │
│ PlanController → PlanService │
│ │ 保存计划: Plan + PlanObject + PlanOperation + PlanScenes │
│ │ + PlanUser + PlanWorkGroup │
│ ▼ │
│ POST /plan/enablePlan │
│ │ │
│ ▼ │
│ 创建 XXL-Job 定时任务 (planCreateTask) │
│ 设置 cron 表达式 (根据周期配置) │
│ Redis SET Plan:<type>:<planId> → xxlJobId │
│ │
└───────────────────────────────┬─────────────────────────────────────────────┘
│ XXL-Job 定时触发
┌─────────────────────────────────────────────────────────────────────────────┐
│ 阶段二:任务生成 (Task 服务 - PlanTimeTask) │
│ │
│ planCreateTask 触发 │
│ │ │
│ ▼ │
│ ① 检查节假日 (Holiday表) │
│ │ eliminate_type=1 且为节假日 → 跳过 │
│ ▼ │
│ ② 检查禁用标志 │
│ │ Redis GET Disable:d:<districtId>:<taskTypeId> │
│ │ 如果 "1" → 存入 RebuildTask 等待恢复 │
│ ▼ │
│ ③ 生成任务 │
│ │ Redis LPUSH PlanTaskQueue ← planId │
│ ▼ │
│ ④ 计算下一次执行时间 │
│ │ 根据 circle_type (天/周/月) + circle_no 计算 │
│ │ 动态更新 XXL-Job cron 表达式 │
│ ▼ │
│ ⑤ 创建 TaskInfo 记录 (MySQL) │
│ │ 状态: status=0 (已生成) │
│ │ 关联: plan_id, district_id, task_type │
│ │ 创建: TaskScenes, TaskUser │
│ │
└───────────────────────────────┬─────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ 阶段三:任务派单 (Task 服务 - TaskCorrelationSetTask, 每秒执行) │
│ │
│ Redis RPOP taskUserQueue │
│ │ │
│ ▼ │
│ ① 读取派单配置 │
│ │ Redis HMGET Dictionary: │
│ │ UserAppointFlag (是否指定人员) │
│ │ UserAreaFlag (是否限定区域) │
│ │ UserDutyFlag (是否按职责) │
│ ▼ │
│ ② 匹配可用人员 │
│ │ 查询: 用户状态=空闲, 所属站点, 技能匹配, 服务区域 │
│ │ 排序: 按优先级/距离 │
│ ▼ │
│ ③ 分配任务 │
│ │ 更新 TaskInfo.status = 1 (已匹配) / 2 (待抢单) / 3 (已接单) │
│ │ 创建/更新 TaskUser 记录 │
│ ▼ │
│ ④ 触发推送 │
│ │ Redis LPUSH taskPushQueue ← TaskPushInfo │
│ │ Redis LPUSH WatchTaskUserList ← taskUserId │
│ │ Redis LPUSH IntellectualTaskIdList ← taskId │
│ │
└──────────┬──────────────────────────────┬───────────────────────────────────┘
│ │
▼ ▼
┌────────────────────────┐ ┌──────────────────────────────────────────────┐
│ 阶段四AAPP推送 │ │ 阶段四B手表推送 │
│ (TaskPushMessage, 每秒) │ │ (ScheduledTask, 每2秒) │
│ │ │ │
│ Redis RPOP │ │ Redis RPOP WatchTaskUserList │
│ taskPushQueue │ │ │ │
│ │ │ │ ▼ │
│ ▼ │ │ 构建 WatchTaskMessage │
│ 判断推送类型: │ │ (任务名/类型/位置/时间) │
│ status=1 新任务 │ │ │ │
│ status=2 抢单池 │ │ ▼ │
│ status=3 已接单 │ │ Redis LPUSH MqttWatchTaskMessage │
│ status=7/8 已取消 │ │ │ │
│ │ │ │ ▼ │
│ ▼ │ │ Redis RPOP MqttWatchTaskMessage │
│ 调用个推 SDK: │ │ │ │
│ 在线→NotificationTpl │ │ ▼ │
│ 离线→TransmissionTpl │ │ MQTT publish → 手表 (topic=imei) │
│ │ │ │ │
│ ▼ │ │ │
│ 保存 PushLog │ │ │
│ │ │ │ │
│ ▼ │ │ │
│ ┌──────────┐ │ │ ┌──────────┐ │
│ │ 手机 APP │ │ │ │ 智能手表 │ │
│ │ 收到通知 │ │ │ │ 收到任务 │ │
│ └──────────┘ │ │ └──────────┘ │
└────────────────────────┘ └──────────────────────────────────────────────┘
```
### 3.2 任务状态变更推送流程
```
员工操作 (APP: 接单/开始/完成)
Web API → 更新 TaskInfo.status
Redis LPUSH IntellectualTaskIdList ← taskId
▼ (Task 服务, 每2秒)
┌─────────────────────────────────────────────┐
│ TodayDynamicMessage │
│ │
│ ① Redis LPOP IntellectualTaskIdList │
│ ② 查询 TaskInfo 详情 │
│ ③ 确定推送对象: │
│ - 代理人员 (Agent用户) │
│ - 物业管理员 │
│ - 任务分配人员 │
│ - 指派管理员 │
│ ④ 生成 TodayDynamic 记录 (MySQL) │
│ ⑤ Redis LPUSH MqttTaskStateMessage │
└──────────────────┬──────────────────────────┘
▼ (每2秒)
┌─────────────────────────────────────────────┐
│ MqttTaskStateMessage │
│ │
│ ① Redis RPOP MqttTaskStateMessage │
│ ② 构建推送消息 │
│ ③ MQTT publish → mobile_<districtId> │
│ │
│ ┌──────────┐ │
│ │ 手机 APP │ ← 实时接收任务动态 │
│ └──────────┘ │
└─────────────────────────────────────────────┘
```
---
## 四、设备告警 → 维护任务流程
```
┌──────────────┐
│ IoT 设备 │ (纸巾机/空气净化器/传感器)
│ 检测到异常 │
└──────┬───────┘
│ 告警数据
┌──────────────────────────────────────────────────────────────┐
│ 设备告警 → Redis │
│ │
│ Redis LPUSH taskCommand ← TaskCommandInfo JSON │
│ { │
│ snCode: "设备SN", │
│ taskTypeId: 23, // 空气质量/纸巾不足等 │
│ districtId: 1, │
│ scenesId: 5 │
│ } │
└──────────────────────────┬───────────────────────────────────┘
▼ (Task 服务 - TaskCreateQueue, 每秒)
┌──────────────────────────────────────────────────────────────┐
│ TaskCreateQueue.taskCommandHandle() │
│ │
│ ① Redis RPOP taskCommand (DB5) │
│ ② 检查任务开关: │
│ Redis GET DevTaskFlag:<districtId> (DB4) │
│ 如果 "0" → 跳过 │
│ ③ 检查禁用标志: │
│ Redis GET Disable:d/t/p:<id>:<taskTypeId> (DB4) │
│ 如果 "1" → 存入 RebuildTask 等待恢复 │
│ ④ 去重检查: │
│ 查询是否存在同设备未完成任务 │
│ 如果存在 → 跳过 │
│ ⑤ 创建 TaskInfo (MySQL): │
│ status=0, task_type=设备维护 │
│ 关联: sn_code, district_id, scenes_id │
│ ⑥ 创建 TaskScenes, TaskUser │
│ ⑦ Redis LPUSH taskPushQueue ← 推送消息 │
│ ⑧ Redis LPUSH WatchTaskUserList ← 手表任务 │
│ │
└──────────────────────────┬───────────────────────────────────┘
(走标准推送流程,同 3.1 阶段四)
┌────────────┼────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ APP 通知 │ │ 手表提醒 │ │ Web 后台 │
│ 维护员收到│ │ 震动+语音 │ │ 任务列表 │
└──────────┘ └──────────┘ └──────────┘
```
---
## 五、MQTT 设备通信流程
### 5.1 手表上下线监控
```
智能手表
│ MQTT connect/disconnect
┌──────────────────────────────────────────────┐
│ MQTT Broker (tcp://mqtt:1883) │
│ │
│ Topic: $SYS/brokers/+/clients/# │
│ 消息: {clientid:"watch_IMEI_xxx", │
│ connected_at: 1700000000} │
└──────────────────┬───────────────────────────┘
┌──────────────────────────────────────────────┐
│ PushCallback (MQTT 消息回调) │
│ │
│ ① 过滤: clientid 包含 "watch" │
│ ② 解析: connected_at / disconnected_at │
│ ③ Redis LPUSH WatchConnectList ← 连接事件 │
└──────────────────┬───────────────────────────┘
▼ (每秒)
┌──────────────────────────────────────────────┐
│ ScheduledTask.WatchConnectList() │
│ │
│ ① Redis RPOP WatchConnectList │
│ ② 解析连接/断开: │
│ connected_at ≠ null → 上线 (status=1) │
│ disconnected_at ≠ null → 离线 (status=0) │
│ ③ 创建 WatchOffLog (MySQL) │
│ ④ 更新 Watch 表状态 (MySQL) │
└──────────────────────────────────────────────┘
```
### 5.2 蓝牙定位流程
```
┌──────────────┐
│ 智能手表 │ 持续扫描周围蓝牙信标
│ BLE 扫描 │
└──────┬───────┘
│ 信标数据: {mac, rssi, imei}
┌──────────────────────────────────────────────┐
│ 数据上报 → Redis │
│ Redis LPUSH BleutoothLocationQueue ← JSON │
└──────────────────┬───────────────────────────┘
▼ (每秒)
┌──────────────────────────────────────────────────────────────┐
│ LocationTask.BleutoothLocationTask() │
│ │
│ ① Redis RPOP BleutoothLocationQueue │
│ ② 根据信标 MAC 查询 Beacon 表 → 获取位置信息 │
│ ③ RSSI 阈值判断: │
│ ┌────────────────────────────────────────────────┐ │
│ │ RSSI ≥ 阈值 (进入范围) │ │
│ │ → 保存 UserLocation (MySQL) │ │
│ │ → 创建 UserTrajectory (status=0, 进行中) │ │
│ │ → Redis SET Location:<userId> ← 位置信息 │ │
│ ├────────────────────────────────────────────────┤ │
│ │ 同一位置停留 < 5秒 │ │
│ │ → 更新 UserTrajectory.stay_time │ │
│ ├────────────────────────────────────────────────┤ │
│ │ 同一位置停留 ≥ 5秒 │ │
│ │ → 保存当前轨迹 (status=1, 完成) │ │
│ │ → 创建新的 UserTrajectory │ │
│ ├────────────────────────────────────────────────┤ │
│ │ RSSI < 阈值 (离开范围) │ │
│ │ → 记录离开时间 │ │
│ │ → 标记轨迹完成 (status=1) │ │
│ └────────────────────────────────────────────────┘ │
│ │
│ ④ 定位数据支撑: │
│ → Web 后台实时位置显示 │
│ → 人效分析报表 │
│ → 考勤打卡佐证 │
└──────────────────────────────────────────────────────────────┘
```
---
## 六、员工休息管理流程
```
员工点击 "休息" (APP)
Web API → 创建休息请求
▼ (Task 服务 - XXL-Job)
┌──────────────────────────────────────────────────────────────┐
│ userRestStartTask │
│ │
│ ① 创建 RestRecord (punchType=0, MySQL) │
│ ② 查询站点配置 → 获取休息时长 (默认60分钟) │
│ ③ Redis ZADD CleaningRestLastTime │
│ score=截止时间戳 value=userId │
│ ④ 更新 UserDistrictAttendance.workAtStatus=3 (休息中) │
│ ⑤ Redis SET CleaningUpWorkAtStatus:<mobile> ← 原状态 │
│ ⑥ Redis LPUSH AttendanceStatusMessage ← 考勤消息 │
│ │ │
│ ▼ (ScheduledTask, 每秒) │
│ MQTT publish → 手表 (messageType=5, 关闭蓝牙) │
│ │
└──────────────────────────────┬───────────────────────────────┘
┌──────────┴──────────┐
▼ ▼
正常结束休息 超时自动恢复
┌─────────────────────────┐ ┌─────────────────────────┐
│ userRestEndTask │ │ restCountDown (每秒检查) │
│ (XXL-Job 定时触发) │ │ │
│ │ │ Redis ZRANGEBYSCORE │
│ ① RestRecord │ │ CleaningRestLastTime │
│ (punchType=1) │ │ 0 ~ 当前时间戳 │
│ ② 计算实际休息时长 │ │ │ │
│ ③ 恢复 workAtStatus │ │ ▼ │
│ (从 Redis 读取原状态) │ │ 发现超时员工 │
│ ④ 清除 Redis 标记 │ │ → 自动创建恢复记录 │
│ │ │ → 恢复 workAtStatus=2 │
└─────────────────────────┘ └─────────────────────────┘
```
---
## 七、数据导出流程
```
管理员点击 "导出" (Web 后台)
POST /xxx/export
┌──────────────────────────────────────────────────────────────┐
│ Controller.export() │
│ │
│ ① 接收查询条件 (同列表查询) │
│ ② 查询权限组字段 (FieldWebpageAuthority) │
│ → 确定可导出的列 │
│ ③ Service 查询数据 (不分页, 全量) │
│ ④ 构造 Excel (Apache POI / EasyExcel) │
│ → 根据权限动态设置列 │
│ ⑤ 设置响应头: │
│ Content-Type: application/octet-stream │
│ Content-Disposition: attachment; filename=xxx.xlsx │
│ ⑥ 输出流写入 Response │
└──────────────────────────┬───────────────────────────────────┘
┌──────────────┐
│ 浏览器下载 │
│ Excel 文件 │
└──────────────┘
```
---
## 八、权限动态加载流程
```
页面加载 / 路由切换
┌──────────────────────────────────────────────────────────────┐
│ router-guard.js (路由守卫) │
│ │
│ ① 检查 sessionStorage 登录态 │
│ ② 读取路由 query: wId (页面ID), pageType │
│ ③ 触发 Vuex actions: │
│ store.dispatch('fetchFields', {wId, pageType}) │
│ store.dispatch('fetchButtons', {wId, pageType}) │
└──────────────────────────┬───────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ Vuex Store │
│ │
│ fetchFields: │
│ POST /webPage/getFieldsByAuthority │
│ → 返回当前用户权限组下该页面可见字段列表 │
│ → 存入 state.fields │
│ │
│ fetchButtons: │
│ POST /webPage/getButtonsByAuthority │
│ → 返回当前用户权限组下该页面可见按钮列表 │
│ → 存入 state.buttons │
└──────────────────────────┬───────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ 页面组件渲染 │
│ │
│ <qu-table> │
│ 遍历 state.fields → 动态生成 <el-table-column> │
│ 字段不在权限列表 → 不渲染该列 │
│ │
│ <qu-button-group> │
│ 遍历 state.buttons → 动态生成 <el-button> │
│ 按钮不在权限列表 → 不渲染该按钮 │
└──────────────────────────────────────────────────────────────┘
```
---
## 九、Redis 消息队列数据流全景
```
┌─────────────┐
│ DB 4 │
└──────┬──────┘
┌──────────────────────────────┬┼┬──────────────────────────────┐
│ │ ││ │ │
▼ ▼ ▼▼ ▼ ▼
┌────────┐ ┌──────────┐ ┌─────────────┐ ┌──────────┐ ┌────────────┐
│taskUser│ │taskPush │ │WatchTask │ │Mqtt │ │Bleutooth │
│Queue │ │Queue │ │UserList │ │TaskState │ │Location │
│ │ │ │ │ │ │Message │ │Queue │
└───┬────┘ └────┬─────┘ └──────┬──────┘ └────┬─────┘ └─────┬──────┘
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌────────┐ ┌──────────┐ ┌─────────────┐ ┌──────────┐ ┌────────────┐
│派单 │ │个推推送 │ │构建手表消息 │ │MQTT推送 │ │位置计算 │
│匹配 │ │APP通知 │ │ │ │到手机端 │ │轨迹记录 │
└────────┘ └──────────┘ └──────┬──────┘ └──────────┘ └────────────┘
┌─────────────┐
│MqttWatch │
│TaskMessage │
└──────┬──────┘
┌─────────────┐
│MQTT推送 │
│到手表 │
└─────────────┘
┌─────────────┐
│ DB 5 │
└──────┬──────┘
┌──────────────────────┬───────┼───────┬──────────────────────┐
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌────────┐ ┌──────────┐ ┌────┐ ┌──────────┐ ┌──────────┐
│task │ │Finish │ │实体│ │Dictionary│ │Location │
│Command │ │TaskQueue │ │缓存│ │ (Hash) │ │:<userId> │
│(设备告警)│ │(完成队列)│ │ │ │(系统配置)│ │(实时位置) │
└───┬────┘ └────┬─────┘ └────┘ └──────────┘ └──────────┘
│ │
▼ ▼
┌────────┐ ┌──────────┐
│生成 │ │完成后 │
│维护任务│ │处理逻辑 │
└────────┘ └──────────┘
```