feat(alarm): 设备报警监控报警提示升级与警灯交互优化 - 报警提示升级:后端新增报警派单完成接口 POST /api/worker-app/alarm-dispatches/:id/complete(供共享电工APP确认完成,归属校验403/不存在404/已完成幂等,status置1并写completed_at);GET /api/device-alarms 的 summary 由 severeIds 改为 activeSevereIds(grade=1 且无已完成派单的报警id,倒序100条,派单完成自动移出即报警取消)并新增轻量接口 GET /api/device-alarms/active-severe;前端新建全局悬浮立体警灯组件 AlarmSirenLight(红蓝双色3D警灯,挂载主布局所有页面可见,点击跳转报警监控页,30秒轮询activeSevereIds新id即时响+每3分钟重复响,Web Audio合成警笛700/900Hz交替,浏览器自动播放静默降级);index.vue 移除原10秒指示灯与880Hz beep、瘦身为纯列表页;警灯交互优化:警灯可鼠标拖动(位移>5px判为拖动不触发跳转)、位置经localStorage跨刷新记忆(默认右上角)、取消「重置报警提示」按钮;后端tsc与前端vue-tsc编译通过,接口实测派单完成排除逻辑生效,浏览器端到端验证通过(全局显示、点击跳转、拖动、刷新记忆、重置按钮无残留)

This commit is contained in:
wk authored and trae-solo-agent committed 2026-10-24 18:40:05 +08:00
1 parent 7ff2405634
commit 2d55947762
13 files changed
+676 -115

No files matched your search

@@ -0,0 +1,17 @@
# Checklist
- [x] 后端报警派单完成接口 `POST /api/worker-app/alarm-dispatches/:id/complete`:归属校验(403)、不存在(404)、status 0→1 + completedAt 写入、已完成幂等
- [x] 后端 summary 返回 activeSevereIds(grade=1 且无 status=1 派单的报警 id 数组),接口实测为数组且不含已完成派单报警
- [x] 原 severeIds 字段已替换为 activeSevereIds(后端 + 前端类型同步,无残留引用)
- [x] 前端顶部旧「严重报警」指示灯已移除,替换为右上角固定悬浮立体警灯(红蓝双色旋转闪烁、CSS 3D 效果)
- [x] activeSevereIds 非空时悬浮警灯出现并旋转闪烁
- [x] 不在 `/device/alarm` 页面时点击警灯,跳转到设备报警监控页面
- [x] 严重报警派单完成后(status=1)报警移出 activeSevereIds,不再触发警灯/警笛,列表记录保留
- [x] 全部严重报警派单完成后警灯停止旋转、3 分钟重复响停止
- [x] 派单完成时该线路存在遗留未派单严重报警:继续/重新触发灯与声音(新入库遗留报警即时触发)
- [x] 「重置报警提示」按钮清空已通知集合,当前未派单完成的严重报警重新触发一次
- [x] 新未派单完成的严重报警即时播放合成警笛声(700Hz↔900Hz 高低音交替)
- [x] 存在未派单完成的严重报警时每 3 分钟重复响一次警笛
- [x] 3 分钟警笛定时器与 30 秒列表轮询在页面卸载时正确清理(无泄漏)
- [x] 后端 tsc 编译通过、前端 vue-tsc 编译通过
- [x] 浏览器端到端验证通过(警灯旋转、点击跳转、派单完成取消、遗留重新触发、重置、警笛触发)
@@ -0,0 +1,95 @@
# 设备报警监控报警提示升级 Spec
## Why
现有报警提示仅「严重报警亮 10 秒指示灯 + 880Hz 三声 beep」,与派单状态无关联,提示不够醒目;需升级为悬浮立体警灯样式、警车警报声,并与派单联动:同线路报警派单完成后取消提示,未派单完成则每 3 分钟重复报警声。
## What Changes
- **后端**:新增**报警派单完成接口** `POST /api/worker-app/alarm-dispatches/:id/complete`(供共享电工 APP 确认完成报警派单):校验派单归属当前电工、状态为未完成(status=0),更新 status→1 并写入 completed_at;已完成的重复调用幂等提示。
- **后端**:`GET /api/device-alarms` 的 summary 由 `severeIds`(全量 grade=1)改为 **`activeSevereIds`**:grade=1 且**无已完成派单(status=1)**的报警 id 集合(倒序取最近 100 条)。派单完成后报警自动移出集合即取消提示;未派单完成的严重报警始终在集合内持续触发。
- **前端**:替换现有顶部「严重报警」指示灯为**悬浮立体警灯**(红蓝双色旋转闪烁、CSS 3D 效果),固定悬浮页面右上角;存在未派单完成的严重报警时警灯旋转闪烁。
- **前端**:点击悬浮警灯时,若当前路由不是设备报警监控页(`/device/alarm`),直接 `router.push('/device/alarm')`。
- **前端**:报警声由 880Hz beep 改为 **Web Audio 合成警笛声**(高低音交替,如 700Hz↔900Hz,循环 2~3 轮)。
- **前端**:报警声触发策略改为「**即时响一次 + 每 3 分钟重复响一次**」:新严重报警即时响;之后只要存在未派单完成的严重报警,每 3 分钟重复响一次警笛,直到全部派单完成。新增独立 3 分钟定时器(现有 30 秒列表轮询保持不变)。
- **前端**:新增「重置报警提示」小按钮(悬浮警灯旁):清空已通知集合,使当前所有未派单完成的严重报警重新触发一次灯/声音。
## 与派单联动的报警取消/重新触发语义(用户确认)
- **派单完成** = 管理端发起报警派单(status=0)→ 共享电工到现场处理完成派单任务 → 电工在共享电工 APP 点击确认完成(调用本系统新增的完成接口,status→1 + completed_at)。
- **派单完成后报警取消**:严重报警派单完成后移出 activeSevereIds → 不再触发灯/声音,3 分钟重复响(若无其他活跃严重报警)停止。
- **有遗留严重报警才重新触发**:派单完成时若该线路仍存在未派单完成的严重报警(遗留),该遗留报警持续(重新)触发灯/声音;其后同线路新严重报警正常触发。
- **手动重置**:保留「重置报警提示」按钮,清空已通知集合后当前所有未派单完成的严重报警重新触发一次。
## Impact
- Affected specs: 设备报警监控(ICC 报警事件订阅)、共享电工 APP
- Affected code:
- [server/src/controller/device_alarm.controller.ts](file:///F:/NewProject/鑫潼Git/smart_grid_iot/server/src/controller/device_alarm.controller.ts)(summary `severeIds` → `activeSevereIds`)
- [server/src/controller/worker_app.controller.ts](file:///F:/NewProject/鑫潼Git/smart_grid_iot/server/src/controller/worker_app.controller.ts) + [server/src/service/worker_app.service.ts](file:///F:/NewProject/鑫潼Git/smart_grid_iot/server/src/service/worker_app.service.ts)(报警派单完成接口)
- [client/src/views/device/alarm/index.vue](file:///F:/NewProject/鑫潼Git/smart_grid_iot/client/src/views/device/alarm/index.vue)(警灯替换、警笛、activeSevereIds 驱动、3 分钟定时器、点击跳转、重置按钮)
- [client/src/api/alarm.ts](file:///F:/NewProject/鑫潼Git/smart_grid_iot/client/src/api/alarm.ts)(`DeviceAlarmSummary` 的 `severeIds` → `activeSevereIds`)
## 明确假设(需用户知悉)
1. **「派单完成」业务链路与判定**:确认动作在共享电工 APP 发起,本系统后端新增完成接口承接(status→1 + completed_at);`alarm_dispatch.status = 1` 即「派单完成」判定。共享电工 APP 端 UI 的确认按钮不在本次范围(由 APP 端对接本接口)。
2. **悬浮警灯位置** = 设备报警监控页面右上角固定悬浮(position: fixed),z-index 高于页面内容。
3. **警笛声**为合成警笛(高频交替),不受浏览器自动播放策略影响时正常发声;首次加载无用户交互仍遵循浏览器自动播放限制(静默降级只亮灯/旋转)。
4. **手动重置**作用于本地已通知集合(无后端持久化),刷新页面后自动重建。
## ADDED Requirements
### Requirement: 报警派单完成接口
系统 SHALL 提供共享电工 APP 确认完成报警派单的接口:校验派单归属当前电工且未完成,更新状态为已完成(status=1)并写入完成时间(completed_at)。
#### Scenario: 电工确认完成报警派单
- **WHEN** 共享电工 APP 调用 `POST /api/worker-app/alarm-dispatches/:id/complete`,且该派单归属当前电工、状态为未完成(status=0)
- **THEN** 派单 status 更新为 1、completed_at 写入当前时间,返回完成成功;管理端报警监控页轮询后该报警移出 activeSevereIds,不再触发灯/声音
#### Scenario: 归属校验与幂等
- **WHEN** 派单不归属当前电工,或派单不存在
- **THEN** 返回 403/404;派单已完成(status=1)时重复调用返回幂等提示(不重复计数、不报错)
### Requirement: 悬浮立体警灯
系统 SHALL 在设备报警监控页面右上角以固定悬浮方式展示立体警灯(红蓝双色旋转闪烁、CSS 3D 效果),替换现有「严重报警」指示灯;当存在未派单完成的严重报警时警灯旋转闪烁。
#### Scenario: 严重报警触发警灯
- **WHEN** activeSevereIds 非空(存在未派单完成的严重报警)
- **THEN** 悬浮警灯出现并旋转闪烁
#### Scenario: 点击警灯跳转页面
- **WHEN** 页面不在设备报警监控页时用户点击悬浮警灯
- **THEN** 跳转到 `/device/alarm` 并打开设备报警监控页面
### Requirement: 派单完成后报警取消与遗留重新触发
系统 SHALL 将「已完成派单(status=1)关联的报警」从触发集合中移除,实现报警取消;同线路未派单完成的严重报警持续触发,派单完成时若有遗留未派单严重报警则重新触发。
#### Scenario: 派单完成后报警取消
- **WHEN** 某严重报警的派单完成后(status=1)
- **THEN** 该报警移出 activeSevereIds,不再触发警灯旋转与警笛声;若无其他活跃严重报警,3 分钟重复响停止;KPI 统计不受影响(仅提示取消,列表记录保留)
#### Scenario: 遗留严重报警重新触发
- **WHEN** 派单完成时该线路仍存在未派单完成的严重报警
- **THEN** 遗留报警继续/重新触发灯与声音(新入库的遗留报警即时触发一次;已触发过的按 3 分钟重复响)
#### Scenario: 手动重置
- **WHEN** 用户点击「重置报警提示」按钮
- **THEN** 已通知集合清空,当前所有未派单完成的严重报警重新触发一次灯/声音
### Requirement: 警笛声与轮询策略
系统 SHALL 在出现未派单完成的严重报警时即时播放一次合成警笛声,并每 3 分钟重复播放一次(只要仍存在未派单完成的严重报警);报警声为 Web Audio 合成警笛(高低音交替)。
#### Scenario: 即时响
- **WHEN** 新未派单完成的严重报警出现
- **THEN** 立即播放一次警笛声,同时警灯旋转闪烁
#### Scenario: 3 分钟重复响
- **WHEN** 存在未派单完成的严重报警,且距上次响声满 3 分钟
- **THEN** 重复播放一次警笛声
#### Scenario: 全部派单完成后停止
- **WHEN** 所有严重报警均派单完成(activeSevereIds 为空)
- **THEN** 不再触发警笛声,警灯停止旋转
## MODIFIED Requirements
### Requirement: 严重报警提示(原「严重报警指示灯 + 880Hz beep」)
原实现为顶部 `alarm-light` 指示灯点亮 10 秒 + 880Hz×3 次 beep,触发扫描基于后端 summary.severeIds 全量 grade=1 报警。现修改为:悬浮立体警灯(替换指示灯,存在未派单完成的严重报警时持续旋转闪烁)+ 合成警笛声(即时 + 每 3 分钟重复);触发扫描改为 summary.activeSevereIds(过滤掉已派单完成的报警)。
## REMOVED Requirements
### Requirement: 880Hz beep、10 秒报警灯计时、全量 severeIds
**Reason**: 报警提示样式与声音升级为悬浮警灯与警笛声,10 秒指示灯逻辑不再适用;触发集合需排除已派单完成的报警,severeIds(全量 grade=1)语义不再满足。
**Migration**: 删除 `alarmLight` 10 秒 setTimeout 逻辑与 doBeep;保留 Web Audio 自动播放策略静默降级(resume 失败仅亮灯不发声);summary 字段 `severeIds` 改为 `activeSevereIds`(前端唯一使用方为 device/alarm/index.vue,同步迁移)。
@@ -0,0 +1,54 @@
# Tasks
- [x] Task 1: 后端报警派单完成接口(供共享电工 APP 确认完成)
- [worker_app.controller.ts](file:///F:/NewProject/鑫潼Git/smart_grid_iot/server/src/controller/worker_app.controller.ts) 新增 `@Post("/alarm-dispatches/:id/complete")`,注入 AlarmDispatch 仓储
- [worker_app.service.ts](file:///F:/NewProject/鑫潼Git/smart_grid_iot/server/src/service/worker_app.service.ts) 新增 `completeAlarmDispatch(workerId, id)`:
- 查派单:不存在 → 404;`workerId` 不归属当前电工 → 403
- status 已是 1 → 幂等提示「已完成」(不重复更新)
- 事务内更新 status→1、completedAt=new Date()
- 验证:curl 实测归属校验 / 完成更新 / 幂等(编译 + 接口)
- [x] Task 2: 后端 summary 改为 activeSevereIds(未派单完成的严重报警集合)
- [device_alarm.controller.ts](file:///F:/NewProject/鑫潼Git/smart_grid_iot/server/src/controller/device_alarm.controller.ts) `list()`:
- 查 `dispatchRepo.findAll({ where: { status: 1 }, attributes: ["alarmId"], raw: true })` 得已完成派单的 alarmId 集合
- `activeSevereIds` = grade=1、lineId 非空、id 不在已完成集合中的报警 id(alarmTime DESC limit 100)
- summary 返回 `activeSevereIds`(替换并删除原 `severeIds` 查询)
- 验证:接口返回 summary.activeSevereIds 为数组,且不含已完成派单的报警 id
- [x] Task 3: 前端悬浮立体警灯替换现有指示灯(样式独立可先行)
- [index.vue](file:///F:/NewProject/鑫潼Git/smart_grid_iot/client/src/views/device/alarm/index.vue) 删除 header-right 中现有 `.alarm-light` 指示灯(保留「订阅状态」按钮)
- 新增悬浮警灯:position: fixed 右上角 + z-index;CSS 3D 造型(警灯灯罩 + 红蓝双色灯芯 + 旋转/闪烁动画)
- 警灯可见/旋转条件:`activeSevereIds` 非空(由 Task 4 触发模型提供)
- 点击警灯:若 `route.path !== '/device/alarm'` 则 `router.push('/device/alarm')`
- 警灯旁加「重置报警提示」小按钮(tooltip:清空已通知集合,重新触发当前严重报警)
- [x] Task 4: 前端触发模型(依赖 Task 2 的 activeSevereIds)
- `scanSevereAlarms` 语义改为:新 id(不在已通知集合 `notifiedIds`)→ 即时触发一次;触发过的 id 追加进集合
- 警灯旋转条件:activeSevereIds 非空(而非「新触发」)
- 「重置报警提示」:清空 notifiedIds 后重新扫描当前 activeSevereIds 触发
- 30 秒列表轮询保持,每次同步 activeSevereIds;派单完成后报警移出集合即取消
- [x] Task 5: 警笛合成 + 即时响 + 3 分钟重复响(依赖 Task 4)
- 移除 doBeep(880Hz×3),新增 `playSiren(ctx)`:700Hz↔900Hz 高低音交替 2~3 轮(每轮 0.4s),gain 包络防爆音
- playBeep 逻辑保留(AudioContext 懒创建 + suspended resume + 静默降级),仅替换发声函数
- 新增 3 分钟定时器 `sirenTimer`(`setInterval` 180000ms):activeSevereIds 非空则播放警笛;onUnmounted 清理
- [x] Task 6: 编译验证
- 后端 `npx tsc --noEmit`(server/)
- 前端 `npx vue-tsc --noEmit`(client/)
- 接口实测:登录后 `GET /api/device-alarms` 返回 summary.activeSevereIds;完成接口归属/幂等实测
- [x] Task 7: 浏览器端到端验证
- 注入 grade=1 报警(无已完成派单)后:警灯出现并旋转(class/动画状态捕获)
- 点击警灯在非报警监控页跳转 `/device/alarm`
- 调用完成接口将该报警派单置 status=1 后:报警移出 activeSevereIds,警灯停止、重复响停止
- 该线路注入新严重报警:重新触发警灯/警笛
- 点击「重置报警提示」:当前未派单完成严重报警重新触发
- 3 分钟重复响逻辑(通过缩短定时器或代码检查确认)
# Task Dependencies
- Task 1、Task 2 相互独立可并行
- Task 3 样式部分不依赖 Task 2,可与 Task 1/2 并行;触发条件与 Task 4 联动
- Task 4 依赖 Task 2(activeSevereIds 数据源)
- Task 5 依赖 Task 4
- Task 6 / Task 7 依赖 Task 1-5