From 2bc30f18d7e01bb779130e638c88bce7ddb47e36 Mon Sep 17 00:00:00 2001 From: jzf Date: Sat, 7 Nov 2026 15:08:50 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=B9=E6=97=A2=E6=9C=89=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E8=BF=9B=E8=A1=8C=E6=9E=B6=E6=9E=84=E5=88=86=E6=9E=90=EF=BC=8C?= =?UTF-8?q?=E6=95=B4=E7=90=86=E6=9E=B6=E6=9E=84=E8=B0=83=E6=95=B4=E6=96=B9?= =?UTF-8?q?=E6=A1=88=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/01-系统总览.md | 110 ++++ docs/02-工程结构与分层.md | 167 +++++ docs/03-业务模块地图.md | 97 +++ docs/04-核心业务流程.md | 294 +++++++++ docs/05-数据模型.md | 241 ++++++++ docs/06-接口清单.md | 227 +++++++ docs/07-外部集成与定时任务.md | 210 +++++++ docs/08-部署与配置.md | 191 ++++++ docs/09-迭代指引与风险清单.md | 94 +++ docs/10-架构优化方案-三端拆分.md | 818 +++++++++++++++++++++++++ docs/11-既有数据库结构分析.md | 451 ++++++++++++++ docs/README.md | 52 ++ docs/frontend/01-前端总览与技术栈.md | 143 +++++ docs/frontend/02-工程结构与请求链路.md | 277 +++++++++ docs/frontend/03-路由与页面职责.md | 143 +++++ docs/frontend/04-核心页面拆解-JcXx.md | 267 ++++++++ docs/frontend/05-实时通信与语音告警.md | 284 +++++++++ docs/frontend/06-食堂与自助终端模块.md | 221 +++++++ docs/frontend/07-构建部署与风险清单.md | 193 ++++++ docs/frontend/README.md | 45 ++ yktgl/yktgl.iml | 183 ++++++ 21 files changed, 4708 insertions(+) create mode 100644 docs/01-系统总览.md create mode 100644 docs/02-工程结构与分层.md create mode 100644 docs/03-业务模块地图.md create mode 100644 docs/04-核心业务流程.md create mode 100644 docs/05-数据模型.md create mode 100644 docs/06-接口清单.md create mode 100644 docs/07-外部集成与定时任务.md create mode 100644 docs/08-部署与配置.md create mode 100644 docs/09-迭代指引与风险清单.md create mode 100644 docs/10-架构优化方案-三端拆分.md create mode 100644 docs/11-既有数据库结构分析.md create mode 100644 docs/README.md create mode 100644 docs/frontend/01-前端总览与技术栈.md create mode 100644 docs/frontend/02-工程结构与请求链路.md create mode 100644 docs/frontend/03-路由与页面职责.md create mode 100644 docs/frontend/04-核心页面拆解-JcXx.md create mode 100644 docs/frontend/05-实时通信与语音告警.md create mode 100644 docs/frontend/06-食堂与自助终端模块.md create mode 100644 docs/frontend/07-构建部署与风险清单.md create mode 100644 docs/frontend/README.md create mode 100644 yktgl/yktgl.iml diff --git a/docs/01-系统总览.md b/docs/01-系统总览.md new file mode 100644 index 0000000..895f668 --- /dev/null +++ b/docs/01-系统总览.md @@ -0,0 +1,110 @@ +# 01 系统总览 + +## 1. 业务定位 + +`yktgl` 是铁路行车公寓的**叫乘(叫班)管理系统**。公寓为值乘机车的乘务员提供住宿,系统负责把“入住—叫乘—出乘—离寓”串起来,并对接机务运安系统的乘务计划,确保乘务员按时出乘。 + +核心业务闭环: + +1. 乘务员到寓,在设备或值班端完成**入寓登记**,分配房间与床位,读取/生成叫乘计划(开车时间、叫乘时间、复叫时间、叫乘结束时间)。 +2. 到点自动**叫乘**:通过电话/广播(Asterisk 会议桥 + TTS)呼叫房间和值班台;未应答触发**复叫**,仍未应答触发**报警**。 +3. 乘务员**应答**后记录应答时间,结束本次叫乘。 +4. 乘务员**离寓**(刷卡/人脸/值班端操作),释放房间床位,写离寓时间与归档标记。 +5. 卡务侧完成**补助发放、余额、消费、现金充值**;管理端提供房间、人员、交路、统计报表、设备心跳与指令。 + +## 2. 使用角色 + +| 角色 | 使用入口 | 主要动作 | +| --- | --- | --- | +| 公寓值班员 | Web 管理端(`YktVue`) | 入寓/离寓登记、叫乘监控与人工干预、换房、房间状态、充值审核 | +| 公寓管理员 | Web 管理端 | 人员、交路、房间、设备、报表、用户权限 | +| 乘务员 | 公寓设备(叫乘屏/签到机/房间屏/消费机/售货机) | 自助登记、查询、应答、消费、充值 | +| 保洁人员 | 房间设备/管理端 | 开始/结束保洁 | +| 运安系统 | SOAP / HTTP | 下发乘务计划、人员资料 | +| 中心服务器 | HTTP | 汇总公寓人员、财务数据 | + +## 3. 运行形态 + +系统同时支持两种部署语义,但代码是同一套: + +- **公寓端**:每套公寓一套服务(或同一服务按 IP 区分公寓),连接本地 Asterisk、串口控制台、各类设备。 +- **中心/汇总端**:通过 `/synData/*`、`/excel/*` 提供人员与财务数据汇总;部分环境由中心下发交路。 + +```mermaid +flowchart LR + subgraph 公寓现场 + DEV[叫乘屏/签到机/房间屏/消费机/售货机] + WEB[值班员浏览器 YktVue] + PHONE[电话/广播终端] + CONSOLE[串口控制台] + end + + subgraph 应用服务器 + APP[Spring Boot yktgl
context /yktgl :8081] + WS[WebSocket 服务] + QZ[Quartz / @Scheduled] + REDIS[(Redis Session)] + DB[(KingbaseES gygl)] + end + + subgraph 外部系统 + AST[Asterisk AMI / Conf-01] + BYXX[机务运安 SOAP] + CENTER[中心服务器 HTTP] + end + + DEV -->|HTTP 心跳/业务| APP + WEB -->|HTTP + WebSocket| APP + APP --- WS + APP --- QZ + APP --- REDIS + APP --- DB + QZ -->|呼叫| AST + AST --> PHONE + AST --> CONSOLE + APP -->|WSDL PbResource| BYXX + APP <-->|人员/财务| CENTER +``` + +## 4. 技术栈 + +| 层次 | 选型 | 说明 | +| --- | --- | --- | +| 运行时 | Java 8、Spring Boot 2.6.13 | 继承 `spring-boot-starter-parent`;`pom.xml` 未声明 packaging(默认 jar),入口继承 `SpringBootServletInitializer`,也可改为 WAR 部署到外部 Tomcat | +| Web | Spring MVC、Spring WebSocket | `@ServerEndpoint` + `ServerEndpointExporter` | +| 持久化 | MyBatis-Plus 3.1.0、PageHelper 1.4.1 | Mapper XML 位于 `classpath:mapper/**/*.xml` | +| 数据库 | KingbaseES 8(`kingbase8` 8.6.0) | URL 形如 `jdbc:kingbase8://host:54321/gygl?currentSchema=gygl`;SQL 大量使用 Oracle 兼容写法(`TO_DATE`、`NVL`、`ROWNUM`、`MERGE`、`LISTAGG`、`sysdate`) | +| 缓存/会话 | Redis + Spring Session | `@EnableRedisHttpSession`,默认 30 分钟 | +| 调度 | Quartz(内存模式)+ Spring `@Scheduled` | 叫乘任务用 Quartz,补偿/清理用 `@Scheduled` | +| 电话 | asterisk-java `3.38.0-zfjia` + jSerialComm | AMI 控制会议桥,串口控制广播/话机 | +| 文档 | Springfox Swagger 2.9.2 | 仅扫描 `jlgl.controller` | +| 报表 | Apache POI 4.1.2 | `/excel/*` 导出 | +| 其他 | fastjson2、Guava、Reflections、JNA | JSON、工具、类扫描、本地库调用 | + +> 注意:`pom.xml` 中 `spring-boot-starter-web` 显式写了 `3.5.4`、`spring-boot-starter-data-redis` 写了 `3.3.1`,与 Boot 2.6.13 / Java 8 不匹配。见 [09-迭代指引与风险清单.md](./09-迭代指引与风险清单.md)。 + +## 5. 多公寓识别(重要约束) + +同一套后端服务多个公寓时,公寓身份由请求来源 IP 决定: + +- 入口:`ZdwhService.getUniqueGyByIp(request)` / `getGyIdByIp(request)`。 +- 解析顺序:设备表(`GY_SB`)中的 IP → 公寓字典 `GY_GYZD.ip`。 +- 若同一 IP 命中多个公寓,直接拒绝,避免写错公寓数据。 +- 业务代码中 `gyid` 不能信任前端传入;入住、房间、充值等写操作均由服务端重新解析或校验。 + +相关配置:`baseConfig.gyid`、`baseConfig.gymc` 作为单公寓/默认值兜底;各 Profile 不同。 + +## 6. 关键设计约束(迭代时不要破坏) + +1. **房间占用以有效在寓明细为准**:`live_person`、`person_name` 由 `GY_JCXX` 中 `finish_ly='0' AND sjlysj IS NULL` 的记录实时重算,不能仅做 ±1 计数。 +2. **交路快照与匹配分离**:运安计划先整表落到 `GY_SYN_JCXX`(快照),再由匹配流程写入 `GY_JCXX`;匹配有全局锁和状态位,避免抓取/推送并发写坏数据。 +3. **叫乘任务可恢复**:Quartz 任务按 `ryid + 类型` 建键,应用重启后由 `WakeupJobRecoveryRunner` 从库中恢复未来任务。 +4. **设备指令一次性投递**:指令写入设备指令表,心跳时下发,下发后置 `deleted=1`。 +5. **事务内不做外部副作用**:WebSocket 推送、创建 Quartz 任务等放在事务提交后(`afterCurrentTransactionCommits`)。 +6. **离寓有证据链**:人脸离寓会保存抓拍快照、写 `GYGL_LY_EVENT`,并以 `deviceEventId` 做幂等。 + +## 7. 与前端/设备的关系 + +- Web 管理端 `YktVue` 通过 `/yktgl/*` 接口与 WebSocket 交互;后端也会把旧构建产物放在 `src/main/resources/static` 下(历史遗留,见风险清单)。 +- 设备通过各自的心跳接口注册,通过业务接口上报事件;服务端通过 WebSocket 向值班端推送刷新与人员信息。 +- 叫乘执行不经过设备,而是直接走 Asterisk 电话/广播链路。 diff --git a/docs/02-工程结构与分层.md b/docs/02-工程结构与分层.md new file mode 100644 index 0000000..fe3c2b5 --- /dev/null +++ b/docs/02-工程结构与分层.md @@ -0,0 +1,167 @@ +# 02 工程结构与分层 + +## 1. 目录结构 + +仓库根目录: + +``` +jcgl/ +├── yktgl/ # 后端(Spring Boot / Maven) +│ ├── pom.xml +│ └── src/ +│ ├── main/ +│ │ ├── java/org/example/yktgl/... +│ │ └── resources/ +│ │ ├── application.yml / application-*.yml +│ │ ├── mapper/**/*.xml +│ │ ├── wsdl/PbResource.wsdl +│ │ ├── db/ # 手工升级脚本 +│ │ └── static/ # 历史前端构建产物 +│ └── test/java/... +├── YktVue/ # Vue 前端 +└── docs/ # 本文档目录 +``` + +后端 Java 包(`org.example.yktgl`): + +| 包 | 职责 | +| --- | --- | +| `jcgl` | 叫乘核心:入寓/离寓、叫乘执行、房间、交路匹配、补助、卡余额、统计联动 | +| `jlgl` | 交路维护:交路 CRUD、叫乘时刻、上传、跨公寓同步、车次查询 | +| `regist` | 人员资料与注册:注册查询、照片、人员增删改 | +| `recharge` | 现金充值:设备发起、值班端处理、余额变动、补助入账 | +| `pay` | 消费:设备余额查询与扣款、消费明细 | +| `MealsOrdering` | 食堂/餐食订单与消费 | +| `kcgl` | 库存/出入库/物料/成品/文件(扩展业务) | +| `display` | 登录、用户、角色、权限、菜单、部门、日志 | +| `excel` | 各类报表导出(POI) | +| `xxtj` | 信息统计:日报、进出情况、月报、补助统计 | +| `zdwh` | 字典/基础资料:公寓、房间、工种、节假日、餐补标准;IP→公寓解析 | +| `syndata` | 运安/中心数据同步、同步日志、人员同步服务 | +| `heartBeat` | 设备心跳、设备指令、设备信息 | +| `callingDevice` / `checkInDevice` / `roomDevice` / `payDevice` / `registDevice` / `vendPayDevice` | 各类设备的接入层(心跳 + 业务) | +| `clean` | 房间保洁 | +| `jgxxgl` | 叫乘报警/警示信息查询 | +| `quartz` | Quartz 工具、任务管理、叫乘任务 `WakeUpJob` | +| `schedule` | `@Scheduled` 补偿任务与启动恢复 | +| `websockets` | 各类 WebSocket 端点 | +| `common` | 通用返回体、验证码、工具、Session/WebSocket 配置 | +| `config` | 异步线程池、RestTemplate、Swagger | +| `util` | 日期、字符串工具 | +| `test` | 测试/演示接口(生产应限制) | + +## 2. 分层约定与调用链 + +典型调用链: + +``` +Controller(参数/鉴权/协议编解码) + → Service(业务编排、事务、状态机) + → Mapper 接口(MyBatis) + → mapper/**/*.xml(SQL) + → KingbaseES +``` + +实际代码中的偏差: + +- 部分 Controller 直接调用 Mapper(如设备心跳、字典查询)。 +- 设备接入层通常只有一个 Service,同时承担协议解析与业务调用。 +- `JcglService` 体量过大,承担了叫乘、房间、人员、补助、同步、归档等多类职责;迭代时应优先“新增小服务 + 复用其中方法”,避免继续膨胀。 + +### 请求与返回 + +- 统一返回 `common/entity/Result` 或 `ResultData`,包含成功标志、消息、数据。 +- 设备接口多返回 `ResultData`;Web 接口多返回 `Result`。 +- 部分统计接口直接返回 `JSONObject`(如 `/synData/synPersonData`)。 + +### 事务与异常 + +- 写操作使用 `@Transactional`;捕获异常后调用 `TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()` 再返回失败。 +- 叫乘任务创建、WebSocket 推送等副作用通过 `afterCurrentTransactionCommits(...)` 延迟到提交后,避免“事务回滚但消息已发/任务已建”。 +- 大量 `try/catch` 将异常转成 `Result.failure(message)`,同时写业务日志;排查时需同时看 `GY_LOGS` / 同步日志 / `System.out`。 + +## 3. 启动流程 + +入口:`YktglApplication` + +```java +@EnableTransactionManagement +@SpringBootApplication +@EnableScheduling +public class YktglApplication extends SpringBootServletInitializer +``` + +启动关键动作: + +1. Spring 容器初始化,激活 `spring.profiles.active`(默认 `local`)。 +2. `SessionConfig` 开启 Redis HttpSession。 +3. `WebSocketConfig` 注册 `ServerEndpointExporter`,扫描 `@ServerEndpoint`(`websockets/*`)。 +4. `ServerStartupListener` 在启动时做初始化(端口/环境相关)。 +5. `WakeupJobRecoveryRunner` 监听 `ApplicationReadyEvent`,从 `GY_JCXX` 恢复未来叫乘任务。 +6. `@Scheduled` 任务开始运行(见 07)。 + +## 4. 配置与 Profile + +- 主配置:`application.yml`,只保留通用项与 `active: local`。 +- 环境配置:`application-{byq,cc,dev,dl,local,pj,prod,sjt,tl}.yml`,各自定义端口、数据源、Redis、公寓、AMI、串口等。 +- 自定义前缀:`baseConfig.*`(公寓、中心地址、同步白名单、设备密码、服务器 OS)、`callcontrol.*`(AMI、录音、串口)。 + +详细字段见 [08-部署与配置.md](./08-部署与配置.md)。 + +## 5. 公共组件速查 + +| 组件 | 位置 | 用途 | +| --- | --- | --- | +| `Result` / `ResultData` | `common/entity` | 统一返回体 | +| `CaptchaUtil` / `CaptchaResult` | `common` | 登录验证码 | +| `IpUtil` | `common/utils` | 取真实客户端 IP | +| `HttpRequest` | `common/utils` | 内部 HTTP 调用(运安人员接口等) | +| `MD5Util` / `PasswordEncoder` | `common/utils` | 口令处理 | +| `MyBatisUtils` | `common/utils` | MyBatis 辅助 | +| `SpringUtils` | `common/utils` | 静态获取 Bean | +| `AsyncConfig` | `config` | 异步线程池(核心 10、最大 200、队列 10) | +| `RestTemplateConfig` | `config` | HTTP 客户端 | +| `SwaggerConfig` | `config` | 仅扫描 `jlgl.controller` | +| `QuartzUtils` / `SchedulerManager` | `quartz` | 任务创建/取消/重建 | +| `CallDutyMatchLock` | `jcgl/service` | 交路抓取与匹配的互斥锁(DB 行锁 + 租约) | +| `AsteriskCallUtil` | `jcgl/service` | AMI 呼叫、会议桥、录音、挂断 | +| `JcWebsocket` 等 | `websockets` | 按公寓/房间推送 | + +## 6. 测试现状 + +`src/test/java` 下已有部分单元/切片测试(录音控制器、当前在寓交路 SQL、最近同步时间、人员同步等),但工程 `target/` 为空,未见完整构建/测试产物。迭代前建议先补齐可重复的构建与测试基线。 + +## 7. 关键文件索引 + +> 行号为当前版本近似位置,改动后请以方法名检索。 + +| 文件 | 关注内容 | +| --- | --- | +| `jcgl/service/JcglService.java` | 叫乘/入住/离寓/房间/补助/同步/归档主逻辑(约 4192 行) | +| ├─ 559–1010 | 房间列表、推荐、房态修改、入住提交、离寓提交 | +| ├─ 1320–1450 | `processCallDuty` / `processTempCallDuty` 状态上报 | +| ├─ 1700–2150 | 远程同步、交路匹配、提醒补偿 | +| ├─ 2400–2510 | `ryzt` 状态更新与校验 | +| ├─ 2640–2800 | 按开车时间与时间规则计算叫乘时刻 | +| ├─ 3065–3175 | 刷脸离寓 | +| ├─ 3600–3700 | 房间校验、时间线校验 | +| └─ 3880–4192 | 停止叫乘、录音、归档 | +| `jcgl/controller/JcglController.java` | `/jcgl` 全部 Web/设备接口入口 | +| `jcgl/service/AsteriskCallUtil.java` | AMI 连接、呼叫、会议桥、TTS、录音、挂断 | +| `jcgl/service/CallDutyMatchLock.java` | 同步/匹配互斥锁 | +| `jcgl/service/ByxxResourceService.java` | 运安 WSDL 调用与快照写入 | +| `quartz/SchedulerManager.java` | 叫乘任务创建/取消/重建 | +| `quartz/WakeUpJob.java` | 单个叫乘任务执行 | +| `quartz/QuartzUtils.java` | Quartz 基础设施 | +| `schedule/JcSchedule.java` | 漏叫提醒、超时离寓、归档 | +| `schedule/SynDataSchedule.java` | 运安抓取/人员同步 | +| `schedule/WakeupJobRecoveryRunner.java` | 启动恢复任务 | +| `mapper/jcgl/JcglMapper.xml` | 叫乘核心 SQL(表结构事实来源之一) | +| `mapper/jcgl/JcglRoomMapper.xml` | 房间 SQL 与推荐逻辑 | +| `mapper/jcgl/LeaveEventMapper.xml` | 离寓事件 | +| `mapper/heartBeat/HeartBeatMapper.xml` | 设备、心跳、指令 | +| `mapper/recharge/RechargeMapper.xml` | 充值申请/余额/补助 | +| `heartBeat/service/HeartBeatService.java` | 指令写入与设备维护 | +| `zdwh/service/ZdwhService.java` | IP→公寓、字典 | +| `websockets/JcWebsocket.java` | 叫乘实时推送 | +| `application.yml` / `application-*.yml` | 运行配置 | diff --git a/docs/03-业务模块地图.md b/docs/03-业务模块地图.md new file mode 100644 index 0000000..2a12954 --- /dev/null +++ b/docs/03-业务模块地图.md @@ -0,0 +1,97 @@ +# 03 业务模块地图 + +## 1. 模块总表 + +| 模块(包) | 入口 | 核心服务 | 一句话职责 | 主要数据表 | +| --- | --- | --- | --- | --- | +| `jcgl` | `JcglController` `/jcgl`、`RecordingController` | `JcglService`(4192 行)、`AsteriskCallUtil`、`ByxxResourceService`、`CallDutyMatchLock` | 叫乘主流程、入寓/离寓、房间、补助、交路匹配、归档 | `GY_JCXX`、`GY_JCXX_HIS`、`GY_ROOMS`、`GY_SYN_JCXX`、`GY_YKT_*`、`GYGL_LY_EVENT` | +| `jlgl` | `JlGlController` `/jlgl` | `JlGlService` | 交路模板维护、叫乘时刻、上传/同步、车次查询 | `GYGL_JLXX`、`GYGL_JLXX_TY`、`GYGL_JLXX_LOGS`、`GY_GYZD` | +| `regist` | `RegistController` `/regist` | `RegistService` | 人员注册信息、照片、人员资料增删改 | `GY_YKT_RY`、人员照片表 | +| `recharge` | `RechargeController` `/recharge` | `RechargeService` | 现金充值申请/处理、余额与补助入账 | `GY_YKT_CASH_REQUEST`、`GY_YKT_REMAINDER`、`GY_YKT_XJCZJL`、`GY_YKT_ALLOWANCE` | +| `pay` | `PayController` `/pay` | `PayService` | 消费扣款、消费明细与统计 | `GY_YKT_CONSUME`、`GY_YKT_REMAINDER` | +| `MealsOrdering` | `MealsController` `/Meals` | `MealsService` | 餐食/订单、房间送餐、消费 | 餐食/订单相关表 | +| `kcgl` | `kcglController` `/kcgl` | `kcglService` | 库存、出入库、物料、成品、PDF 文件 | 库存/出入库相关表 | +| `display` | `/auth`、`/user`、`/role`、`/permission`、`/menu`、`/dept` | `SysUserService` 等 | 登录、会话、RBAC、菜单、部门、日志 | `GL_YH`、`GL_JS`、`GL_QX`、`GL_CD`、`GL_YH_JS`、`GL_JS_QX`、`GY_LOGS` | +| `excel` | `ExcelExportController` `/excel` | 直接查询/POI | 叫乘、日报、补助、充值、消费等 Excel 导出 | 只读多表 | +| `xxtj` | `xxtjController` `/xxtj` | `XxTjService` | 报警次数、进出情况、日报、月报、补助统计 | 只读多表 | +| `zdwh` | `ZdwhController` `/Zdwh` | `ZdwhService` | 公寓/房间/工种/节假日/餐补字典;IP→公寓 | `GY_GYZD`、`GY_ROOMS`、`GY_GZZD`、`GY_JRB`、`GY_JBXX` | +| `syndata` | `/synData`、`/jwDataLog` | `SynDataService`、`DriverPersonSyncService`、`JwDataLogService` | 运安人员、公寓人员、财务数据;同步日志 | `GY_YKT_RY`、`GY_JCXX`、`GY_YKT_REMAINDER`、同步日志表 | +| `heartBeat` | `HeartBeatController` `/heartBeat` | `HeartBeatService` | 设备注册、心跳、指令下发、版本/配置 | `GY_SB`、`GY_SB_XT`、设备指令表 | +| `callingDevice` | `/callingDevice` | `CallingDeviceService` | 叫乘屏心跳/业务 | `GY_SB` | +| `checkInDevice` | `/checkInDevice` | `CheckInDeviceService` | 签到机心跳/业务 | `GY_SB` | +| `roomDevice` | `/roomDevice` | `RoomDeviceService` | 房间屏心跳/应答/保洁 | `GY_SB` | +| `payDevice` | `/payDevice` | `PayDeviceService` | 消费机心跳/消费 | `GY_SB` | +| `registDevice` | `/registDevice` | `RegistDeviceHeartBeatService` | 注册机心跳 | `GY_SB` | +| `vendPayDevice` | `/vendPayDevice` | `VendPayDeviceService` | 售货机支付/退款 | 订单、余额 | +| `clean` | `CleanController` `/clean` | `CleanService` | 开始/结束保洁、保洁记录 | `GY_ROOMS`、保洁记录 | +| `jgxxgl` | `JgXxGlController` `/jgxxgl` | `JgXxGlService` | 报警/警示信息查询 | `GYGL_JGXX` | +| `quartz` | — | `SchedulerManager`、`WakeUpJob` | 叫乘/复叫/叫乘结束任务 | Quartz 内存 JobStore | +| `schedule` | — | 各 `@Scheduled` | 漏叫补偿、超时离寓、归档、运安同步、快照清理 | 多表 | +| `websockets` | `/websocket/*` | — | 值班端/房间端实时推送 | — | + +## 2. 模块依赖关系 + +```mermaid +flowchart TD + DEV[设备接入层
calling/checkIn/room/pay/regist/vend] --> CORE[jcgl 核心服务] + WEB[Web 控制器
jcgl/jlgl/regist/recharge/pay/xxtj/excel] --> CORE + CORE --> JL[jlgl 交路] + CORE --> ZD[zdwh 字典/IP→公寓] + CORE --> HB[heartBeat 设备指令] + CORE --> WS[websockets 推送] + CORE --> QZ[quartz 叫乘任务] + QZ --> AST[AsteriskCallUtil] + SYNC[syndata / ByxxResourceService] --> CORE + CORE --> CARD[卡务: recharge/pay/Meals] + ALL[全部模块] --> DISP[display 登录/RBAC/日志] +``` + +关键耦合点: + +- **`jcgl` 是事实上的“领域服务层”**,其他模块大量直接调用 `JcglService`(如 `pay` 调 `checkRemainder`/`isHoliday`,`regist` 调 `processPersonDeviceInstructions`,`recharge` 调余额与补助)。 +- **`zdwh` 提供公寓上下文**,几乎所有写操作都依赖 `getGyIdByIp` / `getUniqueGyByIp`。 +- **`heartBeat` 提供设备指令通道**,叫乘、入住、充值、人员变更都会写入指令。 +- **`websockets` 只负责推送**,业务状态不以其为准。 + +## 3. 按业务视角分组 + +### 3.1 叫乘与住宿(核心) + +- 入寓登记:`submit_rydj` / `processCheckInRegistration` +- 叫乘执行:`processCallDuty`、`processTempCallDuty`、`WakeUpJob`、`AsteriskCallUtil` +- 房间:`getRoomList`、`getRoomRecommendations`、`modifyRoomStatus`、`exchangeRoom` +- 离寓:`processCheckOutRegistration`、`handleCheckOut`、`submit_ccly` +- 交路匹配:`synCallDutyInfo`、`synUnMatchJcXx`、`matchCallDutyUnderLock` +- 归档:`syncJcXxData` + +### 3.2 卡务与消费 + +- 补助:`getAllowanceInfo`、`getTrainAllowanceInfo`、`changeRemainder`、`insert_allowance` +- 余额:`checkRemainder`、`remakeRemainder` +- 消费:`PayService`、`VendPayDeviceService`、`MealsService.updateMoney` +- 充值:`RechargeService`(设备申请 + 值班端确认) + +### 3.3 基础资料与管理 + +- 人员:`regist` + `syndata`(运安/中心) +- 交路:`jlgl` +- 公寓/房间/工种/节假日:`zdwh` +- 用户/角色/权限/菜单:`display` + +### 3.4 统计与导出 + +- 页面统计:`xxtj`(日报、月报、补助统计) +- Excel:`excel`(叫乘明细、日报、补助、充值、消费及汇总) + +## 4. 模块边界与迭代建议 + +| 场景 | 建议落点 | 说明 | +| --- | --- | --- | +| 叫乘规则(时间/次数/优先级) | `JcglService` + `quartz` | 先改状态机与任务生成,再改执行器 | +| 新的设备类型 | 新建 `xxxDevice` 包 | 复用 `heartBeat` 指令与 `GY_SB` | +| 新报表 | `xxtj` 或 `excel` | 统计 SQL 放对应 Mapper XML | +| 新的卡务规则 | `recharge` / `pay` | 余额变动必须与明细同事务 | +| 交路来源新增 | `syndata` + `jlgl` | 统一落到快照表再匹配 | +| 管理端新功能 | `display` + 对应业务模块 | 权限走 RBAC 表 | + +> `kcgl`(库存)与叫乘主线关系较弱,更像同工程内的扩展业务;迭代叫乘时通常无需改动。 diff --git a/docs/04-核心业务流程.md b/docs/04-核心业务流程.md new file mode 100644 index 0000000..fd00c03 --- /dev/null +++ b/docs/04-核心业务流程.md @@ -0,0 +1,294 @@ +# 04 核心业务流程 + +本文按“人”和“房间”两条主线梳理:人从入寓到离寓,房间从占用到释放;同时说明叫乘任务如何被创建、执行和补偿。 + +## 1. 入寓登记 + +入口有两类: + +- 设备端:`POST /jcgl/processCheckInRegistration`(签到机) +- 值班端:`POST /jcgl/submitAll` → `JcglService.submit_rydj` + +### 1.1 设备端查询/建档(`processCheckInRegistration`) + +| code | 行为 | +| --- | --- | +| 1 | 按 `ryid` 查询当前在寓记录、离寓快照、人员资料,返回给设备;用于设备判断“是否已入住/是否可入住” | +| 2 | 由人员资料 + 选择的交路(`jlid`)或班次构造入住信息,通过 WebSocket `{"lx":"ry","content":...}` 推送值班端确认;房间号仅作为协议字段,不在此步写库 | +| 3 | 按 `id` 查询指定记录 | + +> 设备端只负责“采集 + 推送”,真正的写入统一走值班端确认后的 `submit_rydj`。 + +### 1.2 值班端提交(`submit_rydj`,`@Transactional`) + +按顺序执行: + +1. **重复入住校验**:`isExistCallDutyInfo(ryid, gyid)`,已有在寓记录直接失败。 +2. **房间校验**:按 `gyid + room_no` 精确查房,房间不存在/状态不可入住/床位已满则失败。 +3. **交路处理**: + - `jllx='0'` 表示按交路入住。 + - 若传了 `jlid`(固定交路),从交路表取车次、机车号、起点、到达车次/时间,回填到入住记录。 + - 若 `cc` 包含“便乘/驻在”,`jllx='1'`,不生成标准叫乘;否则调用 `populateJcXxByJlXx` 生成叫乘时间。 +4. **开车日期校验**:有车次时必须传 `kcrq`,且格式为 `yyyy-MM-dd`。 +5. **补助计算**: + - 固定交路:`getTrainAllowanceInfo(cc, sfffcb, gyid, jlid)`。 + - 非固定交路:`getAllowanceInfo(gz, sfffcb, sjrysj, gyid)`(按工种、是否发补助、入住时间规则)。 + - `changeRemainder` 生成/更新余额与补助明细;节假日可能跳过余额与补助写入。 +6. **写入住记录**:`insertJcxx`,`ryzt='0'`、`finish_ly='0'`、`sjrysj=入住时间`,并记录房间号。 +7. **设备指令**: + - 向签到机下发指令 `5`; + - 向房间设备下发指令 `1`(刷新房间屏)。 +8. **占用重算**:`room_rydj` → `refreshRoomOccupancy`,从有效在寓明细重算 `live_person`、`person_name`,再按床位计算房态。 +9. **余额/补助落账**:非节假日执行 `updateRemainder` + `insert_allowance`。 +10. **叫乘任务**:固定交路且 `sfjc='1'` 且非便乘/驻在时,事务提交后创建 `WakeUpJob`,并置 `is_yaxx='1'`。 +11. **推送**:WebSocket `{"lx":"refresh"}`。 + +失败时统一回滚并写业务日志。 + +### 1.3 房间推荐(`getRoomRecommendations`) + +- 仅对“可入住”的房间给出推荐顺序; +- 排序依据:**同行房间**(同车次/同批人)→ **历史房间**(该人员曾住过); +- 推荐结果不作为最终校验,提交时仍由房间状态与床位兜底。 + +## 2. 叫乘状态机 + +### 2.1 人员状态 `ryzt` + +| 值 | 含义 | 进入方式 | +| --- | --- | --- | +| `0` | 待叫乘/未叫乘 | 入住初始化、重置交路、任务取消 | +| `1` | 叫乘中(已叫/复叫) | 任务执行、设备上报 status=1/2 | +| `2` | 叫乘报警(未应答) | 报警任务、设备上报 status=3 | +| `3` | 已应答,待离寓 | 应答(status=5)或通话结束 | +| `4` | 离寓报警(超时未离寓) | 超时检查、设备上报 status=4 | +| `5` | 正在通话 | Asterisk 接通后临时状态 | +| `-1` | 查询用合成状态(未匹配/特殊行) | SQL 构造,不落库 | + +```mermaid +stateDiagram-v2 + state "0 待叫乘" as S0 + state "1 叫乘中" as S1 + state "2 叫乘报警" as S2 + state "3 已应答待离寓" as S3 + state "4 离寓报警" as S4 + state "5 通话中" as S5 + + [*] --> S0 + S0 --> S1: JC 任务 / 设备 status=1 + S1 --> S1: FJ 复叫 / status=2 + S1 --> S5: 电话接通 + S5 --> S3: 挂断 / 应答 + S1 --> S2: 未应答 / status=3 + S2 --> S3: 人工应答 / status=5 + S3 --> S4: 超时未离寓 + S3 --> [*]: 办理离寓 + S4 --> [*]: 办理离寓 +``` + +配套字段: + +- `jcsj_s` / `fjsj_s` / `jcbjsj_s`:实际叫乘/复叫/报警时间。 +- `hjxysj`:呼叫应答时间。 +- `finish_jc`:叫乘是否结束(`1` 结束)。 +- `finish_ly`:是否离寓(`1` 离寓)。 +- `stoped`:是否人工停止叫乘。 +- `is_jcbj` / `is_lybj`:叫乘报警/离寓报警标记。 + +### 2.2 设备上报(`processCallDuty`) + +设备 POST `/jcgl/processCallDuty`,`status` 语义: + +| status | 动作 | +| --- | --- | +| 1 | 开始叫乘:`ryzt=1`、记录 `jcsj_s` | +| 2 | 开始复叫:记录 `fjsj_s`(状态保持叫乘中) | +| 3 | 叫乘报警:若原状态 < 2 则 `ryzt=2`,`is_jcbj=1`,记录 `jcbjsj_s`,写报警记录 | +| 4 | 离寓报警:`triggerLeaveAlarm`(条件更新,防重复),写报警记录 | +| 5 | 呼叫应答:`ryzt=3`、`hjxysj`、`finish_jc=1`、`stoped=0` | + +临时叫乘走 `/jcgl/processTempCallDuty`,语义相同,但会额外刷新对应房间设备。 + +### 2.3 自动叫乘(Quartz) + +入住/修改交路时为一个人创建 **3 个一次性任务**: + +| 任务 | 触发时间 | 行为 | +| --- | --- | --- | +| `JC` | `jcsj` | 开始叫乘:状态置 1,TTS 呼叫值班台 + 房间;未接通则按规则转报警 | +| `FJ` | `fjsj`(叫乘 +10 分钟) | 复叫:再次呼叫;仍未接通转报警 | +| `JCJS` | `jcbjsj`(叫乘 +15 分钟) | 叫乘结束:挂断会议桥、状态置 2、写报警记录 | + +任务键:`ryid + "JC"/"FJ"/"JCJS"`,组:`gyid + fjh`;同一房间/车次会合并为一个执行组,组长优先呼叫。任务执行前会校验 `jcid/fjh/时间` 是否仍与库中一致,避免过期任务误叫。 + +补偿: + +- `SchedulerManager` 统一创建/取消/重建; +- `WakeupJobRecoveryRunner` 在应用启动后恢复未来任务; +- 会议桥忙时延迟 60 秒再试; +- 人工“停止叫乘”会取消任务、挂断通话、置 `stoped=1`。 + +### 2.4 叫乘时间规则 + +叫乘时间统一由**开车时间 + 本地时间规则**计算(运安下发的 `jcsj` 仅保留读取,不作为最终依据): + +- 规则来自 `GY_SJGZ`(按 `gyid + 单位`): + - `zjjcsjgz`:站接叫乘提前分钟数; + - `kjjcsjgz`:库出叫乘提前分钟数; + - `lysjgz`:离寓时间规则。 +- `站接`:`jcsj = kcsj - zjjcsjgz`;`库出`:`jcsj = kcsj - kjjcsjgz`。 +- `fjsj = jcsj + 10 分钟`;`jcbjsj = jcsj + 15 分钟`;`ylysj = jcsj + lysjgz`。 +- 若计算出的叫乘时间已过期:只发 TTS 提醒,不生成报警记录(`adjustPastCallDutyTimes`)。 + +## 3. 运安交路同步与匹配 + +### 3.1 推送式同步(`POST /jcgl/synCallDutyInfo`) + +1. 校验来源 IP 是否在 `baseConfig.synJcXxIp` 白名单。 +2. 解析 JSON 快照,校验 `id`、`workno`、`deptime` 必填,`id` 唯一。 +3. 获取 `CallDutyMatchLock.tryAcquireSync()`(数据库协调行 + 租约),避免与抓取/匹配并发。 +4. `TRUNCATE GY_SYN_JCXX` → 批量插入 → `GY_SYN_JCXX_STATUS='1'`。 +5. 事务提交后释放锁;返回 `{code,message,result}`。 + +### 3.2 主动抓取(`ByxxResourceService.fetchJlXX`) + +1. 获取同步锁。 +2. 加载 `classpath:/wsdl/PbResource.wsdl`,创建 SOAP 客户端。 +3. 清空 `GY_SYN_JCXX`。 +4. 调用运安 `getDriverPlanByTimeStr(start, end)`,把司机 1–4 的计划分别写入快照。 +5. 更新同步状态为 `1`,记录日志。 + +> `mergeSynJcXx` 已停用:历史数据存在同一 `id` 对应多名司机,按 `id` MERGE 会重复匹配。 + +### 3.3 匹配(`/jcgl/synUnMatchJcXx` → `matchCallDutyUnderLock`) + +前置条件:`GY_SYN_JCXX_STATUS='1'`(同步完成),且配置了 `baseConfig.gyid`(匹配只处理该公寓)。 + +流程: + +1. 获取数据库行锁,串行匹配。 +2. 查询 `jllx='0'` 的有效在寓记录,跳过人工锁定交路的记录;若配置不允许“叫乘完成后变更交路”,则跳过已完成叫乘且未离寓的人员。 +3. 以人员 `card_id`(工号)匹配 `GY_SYN_JCXX.workno`。 +4. 命中且交路有变化:按开车时间 + 本地规则重算叫乘/复叫/报警/离寓时间,更新当前入住记录,取消并重建 3 个 Quartz 任务;事务提交后写“交路变更/首次自动匹配/重新匹配”事件日志并 TTS 提醒。 +5. 命中但交路未变:检查任务是否齐全,缺失则补建。 +6. 未命中且该人有开车时间:清空交路与时间、取消任务、TTS 通知本人。 +7. 汇总写同步日志,WebSocket `"all"` 刷新。 + +### 3.4 归档 + +`POST /jcgl/syncJcXxData`: + +- 条件:`finish_ly='1' AND sjlysj <= sysdate - 7`,且属于当前公寓。 +- 分批 500 条:复制到 `GY_JCXX_HIS` → 从 `GY_JCXX` 删除。 +- 统计冲突/无效条数并写日志。 +- 每日 00:00 也有自动归档任务。 + +## 4. 离寓 + +### 4.1 刷脸离寓(`processCheckOutRegistration`) + +| code | 行为 | +| --- | --- | +| 1 | 按 `ryid + gyid` 查询在寓记录与人员资料,返回离寓页面数据 | +| 2 | 刷脸离寓:校验事件幂等 → 保存抓拍 → 更新离寓字段 → 写离寓事件 → 更新房间 → 推送刷新 | + +code=2 的关键点: + +1. **幂等**:`deviceCode + deviceEventId` 已存在则直接返回“已经处理”;缺失事件号时只记录日志、不阻断(兼容旧设备)。 +2. **抓拍**:Base64 只接受 JPEG;按 `leaveSnapshot` 配置保存到本地目录,生成 `/yktgl/photo/leave-snapshots/...` URL;未开启抓拍时按 `requiredForDevice` 决定是否报错。 +3. **服务评分**:请求可带 `kffwpf/jcfwpf/stfwpf`,写入离寓记录。 +4. **事件证据**:`recordLeaveEvent` 写 `GYGL_LY_EVENT`(类型 `FACE`,含 `deviceEventId`、`snapshotUrl`、操作人、备注)。 +5. **提交离寓**:`submit_ccly` → 更新 `GY_JCXX`(`finish_ly=1`、`sjlysj`)→ `room_ccly` 释放房间 → 房间设备刷新 → `remakeRemainder` 处理余额(如退补助)。 +6. **失败回滚**:异常时删除已保存快照并回滚事务。 + +### 4.2 值班端离寓 + +`POST /jcgl/handleCheckOut` 走同一套 `submit_ccly`,离寓类型记为 `MANUAL`,无设备事件号与抓拍。 + +### 4.3 超时未离寓 + +`JcSchedule` 每 15 秒检查:`finish_jc='1'`、`ryzt='3'`、`ylysj <= now`、未触发过的记录 → `triggerLeaveAlarm`(条件更新 `ryzt=4`、`lybjsj`、`is_lybj=1`)→ 写报警记录。 + +## 5. 房间与保洁 + +### 5.1 房态 + +| `room_status` | 含义 | 计算/设置 | +| --- | --- | --- | +| `1` | 空闲 | 结束保洁、恢复可入住 | +| `2` | 住满 | `live_person >= bed_numbers` | +| `3` | 待清扫 | 空房(`live_person=0`),退房后的默认状态 | +| `4` | 不能入住 | 人工设置(要求房间无人) | +| `5` | 未满 | `0 < live_person < bed_numbers` | +| `6` | 保洁中 | 保洁开始;结束后回到 `1` | + +约束: + +- 房间占用始终由 `GY_JCXX` 有效在寓明细重算,不直接 ±1。 +- 入住校验会重新计算房态,拒绝 `4`(不能入住)、`6`(保洁中)、满员房间。 +- 保洁中不允许直接改房态;需先结束保洁。 + +### 5.2 换房(`/jcgl/exchangeRoom`) + +1. 校验新房间可入住; +2. 校验人员当前房间未变化(防并发); +3. 更新入住记录 `fjh`; +4. 重算旧房、新房占用; +5. 向两个房间设备下发刷新指令。 + +## 6. 卡务:补助、充值、消费 + +### 6.1 补助 + +- 入住时按规则计算补助,写 `GY_YKT_ALLOWANCE` 并更新 `GY_YKT_REMAINDER` 的补助余额。 +- 固定交路按车次查固定补助;非固定交路按工种/时间规则。 +- 节假日策略:`isHoliday()` 命中时不发补助、不入账。 + +### 6.2 现金充值(`RechargeService`) + +设备协议 `code`: + +| code | 行为 | +| --- | --- | +| 1 | 查询余额 | +| 2 | 创建充值申请(`GY_YKT_CASH_REQUEST`,状态 `PENDING`),WebSocket `"cz"` 通知值班端 | +| 3 | 查询申请状态 | +| 4 | 取消申请(`CANCELLED`) | + +值班端处理 `/recharge/handleRecharge`: + +- 金额必须 `> 0` 且 `< 100`,最多 2 位小数; +- 通过条件更新把申请从 `PENDING` 置为 `COMPLETED`(与取消互斥); +- 更新余额、写充值记录、向设备下发指令 `6`。 + +### 6.3 消费 + +- 消费机:`PayService`,code 1 查余额,code 2 扣款;节假日金额按 0 处理;“内部人员”可透支。 +- 售货机:`VendPayDeviceService`,code 1 查余额,code 2 下单扣款,code 3 查询结果/退款;退款时恢复余额并标记 `refunded=1`。 +- 所有扣款都写 `GY_YKT_CONSUME`,并与余额更新同事务。 + +## 7. 设备心跳与指令 + +1. 设备按类型调用 `/callingDevice/heartbeat`、`/checkInDevice/heartbeat`、`/roomDevice/heartbeat`、`/payDevice/heartbeat`、`/registDevice/heartbeat`、`/vendPayDevice/heartbeat`。 +2. 心跳上报:`deviceCode`、`versionName`、`deviceType`、`ip`、`deviceUpdateState`、`lastUserUpdateTime`、`lastDeviceConfigUpdateTime`、`roomNumber` 等。 +3. 服务端返回:`devicePassword`、`apartmentName`、`serverTime`、`updateState`、`apkUrl`、指令列表。 +4. 指令写入设备指令表,心跳时取走并置 `deleted=1`(一次性投递)。 +5. 叫乘/入住/离寓/充值/人员变更都会产生指令。 + +## 8. 流程与代码位置对照 + +| 流程 | 主要方法 | +| --- | --- | +| 入住提交 | `JcglService.submit_rydj` | +| 设备入住 | `JcglService.processCheckInRegistration` | +| 叫乘上报 | `JcglService.processCallDuty` / `processTempCallDuty` | +| 叫乘执行 | `quartz/WakeUpJob`、`quartz/SchedulerManager` | +| 电话/会议桥 | `jcgl/service/AsteriskCallUtil` | +| 离寓 | `JcglService.processCheckOutRegistration` / `submit_ccly` | +| 交路推送 | `JcglService.synCallDutyInfo` | +| 交路匹配 | `JcglService.matchCallDutyUnderLock` | +| 运安抓取 | `ByxxResourceService.fetchJlXX` | +| 归档 | `JcglService.syncJcXxData` | +| 房间 | `JcglService.getRoomRecommendations` / `modifyRoomStatus` / `exchangeRoom` | +| 充值 | `RechargeService` | +| 消费 | `PayService` / `VendPayDeviceService` | diff --git a/docs/05-数据模型.md b/docs/05-数据模型.md new file mode 100644 index 0000000..72feb96 --- /dev/null +++ b/docs/05-数据模型.md @@ -0,0 +1,241 @@ +# 05 数据模型 + +> 数据库为 KingbaseES 8,默认 schema `gygl`。表名大小写混用(`GY_JCXX`、`gy_rooms`、`GYGL_JLXX`),SQL 中需按现有写法保持一致。 +> 本文只列核心表与关键字段;完整字段以 `src/main/resources/mapper/**/*.xml` 为准。 + +## 1. 核心关系图 + +```mermaid +erDiagram + GY_YKT_RY ||--o{ GY_JCXX : "ryid/card_id" + GY_JCXX }o--|| GY_ROOMS : "gyid + fjh" + GY_JCXX ||--o{ GYGL_LY_EVENT : "jcid" + GY_JCXX }o--o| GYGL_JLXX : "jlid" + GY_JCXX ||--o| GY_JCXX_HIS : "归档" + GY_YKT_RY ||--o| GY_YKT_REMAINDER : "ryid" + GY_YKT_REMAINDER ||--o{ GY_YKT_ALLOWANCE : "补助" + GY_YKT_REMAINDER ||--o{ GY_YKT_CONSUME : "消费" + GY_YKT_REMAINDER ||--o{ GY_YKT_XJCZJL : "现金充值" + GY_YKT_CASH_REQUEST ||--o| GY_YKT_XJCZJL : "完成充值" + GY_SYN_JCXX }o--|| GY_SYN_JCXX_STATUS : "同步状态" + GY_GYZD ||--o{ GY_ROOMS : "gyid" + GY_GYZD ||--o{ GY_SB : "gyid" + GY_SB ||--o| GY_SB_XT : "devicecode" +``` + +## 2. 住宿与叫乘 + +### 2.1 `GY_JCXX`(当前在寓/叫乘记录,核心表) + +| 分组 | 字段 | 说明 | +| --- | --- | --- | +| 主键 | `jcid` | 入住记录 ID | +| 人员 | `ryid`、`card_id`、`rymc`、`rydw`、`gzzh`、`depotid` | 人员工号/卡号/姓名/单位/工资账号 | +| 公寓 | `gyid`、`gymc` | 公寓 | +| 房间 | `fjh` | 房间号(与 `GY_ROOMS.room_no` 对应) | +| 交路 | `jlid`、`cc`、`jch`、`cclx`、`qd`、`ddcc`、`ddsj` | 交路 ID、车次、机车号、接车方式、起点、到达车次/时间 | +| 时间 | `kcsj`、`jcsj`、`fjsj`、`jcbjsj` | 开车、叫乘、复叫、叫乘结束(计划) | +| 实际时间 | `jcsj_s`、`fjsj_s`、`jcbjsj_s`、`hjxysj`、`lybjsj` | 实际叫乘/复叫/报警/应答/离寓报警 | +| 入住离寓 | `yrysj`、`sjrysj`、`ylysj`、`sjlysj` | 预定/实际入住、预定/实际离寓 | +| 状态 | `ryzt`、`finish_jc`、`finish_ly`、`stoped`、`jllx` | 见状态字典 | +| 报警 | `is_jcbj`、`is_lybj`、`alarm_event_key` | 叫乘/离寓报警标记、报警事件键 | +| 通知去重 | `route_notice_key`、`call_timeout_notice_key`、`missed_call_notice_key` | 防止重复提醒 | +| 离寓证据 | `leave_type`、`leave_event_id`、`leave_remark`、`leave_snapshot_url` | 冗余自 `GYGL_LY_EVENT`,便于查询 | +| 其他 | `is_yaxx`、`yappsj`、`is_tempCall`、`recordfile`、`cblx`、`cbje`、`sfffcb` | 运安匹配标记、临时叫乘、录音文件、补助标识 | + +有效在寓判定:`finish_ly='0' AND sjlysj IS NULL`。 + +### 2.2 `GY_JCXX_HIS` + +与 `GY_JCXX` 同构的历史归档表;另有 `ly_event_id` 等证据字段。归档条件:已离寓且 `sjlysj <= sysdate - 7`。 + +### 2.3 `GY_ROOMS` + +| 字段 | 说明 | +| --- | --- | +| `gyid`、`room_no` | 公寓 + 房间号(联合定位) | +| `room_floor`、`room_dir`、`room_face`、`is_edge`、`is_next_floor`、`next_floor` | 楼层/朝向/相邻关系(用于推荐与排序) | +| `room_status` | 房态,见下表 | +| `bed_numbers` | 床位数 | +| `room_type`、`room_dept`、`room_order` | 房型/所属部门/排序 | +| `live_person`、`person_name` | 当前人数/姓名串(实时重算) | +| `sip_account` | 房间 SIP 账号(叫乘呼叫用) | +| `bjkssj` | 保洁开始时间 | + +| `room_status` | 含义 | +| --- | --- | +| `1` | 空闲 | +| `2` | 住满 | +| `3` | 待清扫 | +| `4` | 不能入住 | +| `5` | 未满 | +| `6` | 保洁中 | + +### 2.4 `GYGL_LY_EVENT`(离寓事件证据) + +| 字段 | 说明 | +| --- | --- | +| `id` | 事件 ID | +| `jcid`、`gyid`、`ryid`、`fjh` | 关联入住记录 | +| `leave_type` | `FACE` / `MANUAL` 等 | +| `device_code`、`device_event_id` | 设备与事件号(`device_code + device_event_id` 幂等) | +| `snapshot_url`、`remark`、`operator_id`、`operator_name` | 抓拍、备注、操作人 | +| `occurred_at`、`received_at` | 发生/接收时间 | + +### 2.5 `GYGL_JLXX`(交路模板) + +`id`、`jch`、`cc`、`qd`、`kcsj`、`jcsj`、`cclx`、`ddcc`、`ddsj`、`rysj`、`lysj`、`sfpc`、`bz`、`qr_fcd`、`qr_gy`、`lrsj`、`gyid`、`gymc`、`flag_send`、`bzje`。 + +关联: + +- `GYGL_JLXX_TY`:交路停运/调整时间段(`gyid`、`cc`、`kssj`、`jssj`)。 +- `GYGL_JLXX_LOGS`:交路操作日志。 +- 交路叫乘时刻不单独建表:`getJlXxJcsjList` 由 `GYGL_JLXX` 的 `kcsj`(开车时刻)、`jcsj`(叫乘时刻)、`lysj`(离寓时刻)结合传入的开车日期 `kcrq` 计算得出,`fjsj = jcsj + 10 分钟`、`jcbjsj = jcsj + 15 分钟`。 + +## 3. 运安同步 + +### 3.1 `GY_SYN_JCXX`(运安计划快照) + +| 字段 | 说明 | +| --- | --- | +| `id` | 计划 ID(`jobid|taskdicid`) | +| `ryid`、`ryxm`、`sfzh`、`sjh` | 司机工号/姓名/身份证/手机 | +| `jch`、`cx`、`cc` | 机车号、车型、车次 | +| `kcsj` | 开车时间(匹配后据此本地计算叫乘时间) | +| `jhxl`、`jhcqdd`、`jcfs` | 计划线路、机车缺勤地点、接车方式 | +| `h1` | 派班室/公寓辅助字段 | +| `write_time` | 写入时间 | + +### 3.2 `GY_SYN_JCXX_STATUS` + +单行/少量行状态表,表示快照是否可用于匹配:`0` 未同步/同步中,`1` 同步完成。 + +### 3.3 `GY_SYN_JCXX_HIS` + +同步快照历史(按需保留/排查用)。 + +### 3.4 `GY_CALL_DUTY_MATCH_LOCK` + +交路抓取/推送/匹配的协调锁表。使用固定协调行(如 `__SYNC_JCXX__`)持有租约,避免多实例/多线程同时 TRUNCATE 与匹配。 + +### 3.5 `JWDATA_SYNC_LOG` / 同步日志 + +记录运安抓取、推送、匹配结果与异常,页面 `/jwDataLog/*` 查询。 + +## 4. 人员与卡务 + +### 4.1 `GY_YKT_RY`(人员) + +`ryid`、`card_id`、`gzzh`、`xm`、`dw`、`depotid`、`gwmc`、`gzmcnew`、`sjh`、`sfzh`、`xb`、`mz`、`zm`、`deleted`、`gxsj`、`photoUrl`。 + +- `deleted='1'` 为逻辑删除,查询普遍带 `nvl(deleted,'0') != '1'`。 +- `gzmcnew` 是岗位归类,用于补助规则与“内部人员”判断。 + +### 4.2 `GY_YKT_RY_ZP` + +人员照片(`ryid` + `photoBase64` BLOB)。 + +### 4.3 `GY_YKT_REMAINDER`(卡余额) + +`ryid`、`xm`、`xjye`(现金余额)、`knye`(卡内余额/可用余额)、`xfze`(消费总额)、`bzze`(补助总额)、`xfxjje`(现金消费额)、`xfbzje`(补助消费额)。 + +### 4.4 `GY_YKT_ALLOWANCE`(补助明细) + +`ryid`、`gyid`、`cblx`(补助类型)、`cbje`(金额)、`sfffcb`、`ryzt`、`fjh`、`jlid`、`yrysj`、`sjrysj`、`ylysj` 等。 + +### 4.5 `GY_YKT_CONSUME`(消费明细) + +`tid`(交易号)、`ryid`、`xm`、`xfje`、`xfsj`、`gyid`、`knye`、`xjye`、`xfze`、`bzze`、`xfxjje`、`xfbzje`、`deviceCode`、`xfly`(消费来源)、`is_jrxf`(节假日消费)。 + +### 4.6 `GY_YKT_XJCZJL`(现金充值记录) + +充值流水,关联现金充值申请。 + +### 4.7 `GY_YKT_CASH_REQUEST`(现金充值申请) + +| 字段 | 说明 | +| --- | --- | +| `id` | 申请 ID | +| `ryid`、`gyid`、`device_code` | 人员/公寓/设备 | +| `request_state` | `PENDING` / `CANCELLED` / `COMPLETED` | +| `created_at`、`updated_at` | 创建/更新时间 | + +> 申请表本身不存金额;金额由设备/值班端在处理时传入,校验后写入充值流水与余额。 + +状态流转:`PENDING → COMPLETED`(值班端处理)或 `PENDING → CANCELLED`(设备取消),两者通过条件更新互斥。 + +### 4.8 `GY_YKT_BTJS` / `GY_YKT_BTBJ` + +补助结算/补助报警相关表,配合补助统计与异常检查。 + +## 5. 设备 + +### 5.1 `GY_SB`(设备) + +`DEVICECODE`、`GYID`、`LX`(类型)、`jrsj`、`gxsj`、`ip`、`fjh`(房间号)。 + +### 5.2 `GY_SB_XT`(设备心跳/系统信息) + +`devicecode`、版本、`deviceType`、`ip`、`deviceUpdateState`、`lastUserUpdateTime`、`lastDeviceConfigUpdateTime`、心跳时间等。 + +### 5.3 `GY_SB_ZL`(设备指令/版本) + +`id`、`devicecode`、`updateState`、`generatedTime`、`apkurl`、`deleted`。 + +- 业务代码写入指令后,设备心跳时按 `devicecode + deleted='0'` 取**最新一条**返回,并置 `deleted='1'`。 +- 因此一次心跳只消费一条指令;多条指令会随多次心跳依次下发。 + +## 6. 基础资料与统计 + +| 表 | 说明 | +| --- | --- | +| `GY_GYZD` | 公寓字典(`gyid`、`gymc`、`ip` 等) | +| `GY_GZZD` | 工种字典 | +| `GY_JRB` | 节假日日历 | +| `GY_JBXX` | 基本/班次信息 | +| `GY_SJGZ` | 时间规则:`lysjgz`、`zjjcsjgz`、`kjjcsjgz`(按公寓 + 单位) | +| `GY_BJCSTJ`、`GY_BJXX` | 报警次数/报警信息 | +| `GYGL_JGXX` | 叫乘/离寓警示记录(`gyid`、`fssj`、`xx_cc`、`xx_sj`、`xx_ryid`、`xx_ry`、`xxlx`、`xxms`) | +| `GY_JDQKHZ` | 进出情况汇总 | +| `GY_RTJB`、`GY_YTJB` | 日报/月报 | +| `GY_LOGS`、`GL_LOGS` | 业务/登录日志 | +| `GY_ZBY` | 值班员信息 | + +## 7. 权限与用户 + +`GL_YH`(用户)、`GL_JS`(角色)、`GL_QX`(权限)、`GL_CD`(菜单)、`GL_YH_JS`(用户-角色)、`GL_JS_QX`(角色-权限);另有 `SYS_USER`、`SYS_USER_ROLE`(历史/兼容)。 + +## 8. 库存扩展(kcgl) + +`GYGL_KCGL_*`:仓库、供应商、物料、成品、入库单/出库单、盘点单、领料单、价格系数等;与叫乘主线弱相关。 + +## 9. 状态字典汇总 + +| 字段 | 取值 | +| --- | --- | +| `GY_JCXX.ryzt` | 0 待叫乘;1 叫乘中;2 叫乘报警;3 已应答待离寓;4 离寓报警;5 通话中;-1 查询合成 | +| `GY_JCXX.finish_jc` | 0 未结束;1 已结束 | +| `GY_JCXX.finish_ly` | 0 在寓;1 已离寓 | +| `GY_JCXX.jllx` | 0 按交路;1 非标准/便乘/驻在/人工修改 | +| `GY_ROOMS.room_status` | 1 空闲;2 住满;3 待清扫;4 不能入住;5 未满;6 保洁中 | +| `GY_YKT_CASH_REQUEST.request_state` | PENDING / CANCELLED / COMPLETED | +| `GY_JCXX.leave_type` | FACE / MANUAL 等 | +| 同步状态 | 0 未完成;1 完成 | + +## 10. 升级脚本 + +`yktgl/src/main/resources/db/manual/` 下为手工执行的 Kingbase 脚本,按文件名日期顺序执行: + +| 脚本 | 作用 | +| --- | --- | +| `V20260921_01__leave_snapshot_evidence_kingbase.sql` | 离寓抓拍证据字段/表 | +| `V20260922_01__call_duty_notice_dedup_kingbase.sql` | 叫乘提醒去重键 | +| `V20261008_01__syn_jcxx_write_time_kingbase.sql` | 快照写入时间 | +| `V20261008_02__call_duty_match_lock_kingbase.sql` | 匹配锁表与协调行 | +| `V20261009_01__jcxx_history_archive_kingbase.sql` | 历史归档字段/索引 | +| `V20261023_01__cash_request_state_kingbase.sql` | 现金充值申请状态 | +| `V20261029_01__reconcile_tlxc_room_occupancy.sql` | 通辽公寓房间占用校对(含 preview 版本) | + +另有 `db/GY_SYN_JCXX_STATUS.sql` 初始化同步状态表。 + +> 这些脚本不在应用启动时自动执行,升级需人工按序执行并校验。执行前务必备份。 diff --git a/docs/06-接口清单.md b/docs/06-接口清单.md new file mode 100644 index 0000000..d19b7c4 --- /dev/null +++ b/docs/06-接口清单.md @@ -0,0 +1,227 @@ +# 06 接口清单 + +## 1. 通用约定 + +- 基础路径:`http://host:8081/yktgl`(`server.servlet.context-path`)。 +- 返回体: + - `Result`:`{code/flag, message, data}`(Web 接口常用) + - `ResultData`:设备接口常用 + - 少数接口直接返回 `JSONObject` 或字符串。 +- 公寓上下文:绝大多数写接口通过客户端 IP 解析 `gyid`;少数接口显式接收 `gyid`,但服务端仍会校验。 +- **方法约束宽松**:大量接口用 `@RequestMapping`,GET/POST 都可访问,部分写操作也能用 GET 触发。新增接口建议明确 `@GetMapping`/`@PostMapping`。 +- 字符集:JSON 为 UTF-8;设备请求体常为单行 JSON。 + +## 2. 叫乘核心 `/jcgl` + +| 方法 | 路径 | 用途 | +| --- | --- | --- | +| ALL | `/getJcxxByRyid` | 按人员/记录查叫乘信息 | +| ALL | `/getEmployeeArriveInfo` | 按单位查人员到达信息 | +| POST | `/getJcXx` | 条件查询在寓/叫乘列表 | +| POST | `/resetJllx` | 重置交路类型 | +| GET | `/getLatestSyncTime` | 当前公寓最近同步时间 | +| GET | `/getSjgz` | 时间规则(叫乘/离寓提前量) | +| ALL | `/getJlxxList` | 按车次/机车号查交路 | +| ALL | `/getRoomList` | 房间列表 | +| GET | `/getRoomRecommendations` | 入住房间推荐 | +| ALL | `/modifyRoomStatus` | 设置不能入住/恢复可入住 | +| ALL | `/submitAll` | 提交入寓登记(`submit_rydj`) | +| ALL | `/updateMeal3` | 更新第三餐补助 | +| ALL | `/getBtje_info` | 按入住时间查补助金额 | +| ALL | `/getBtje_infoMeal3` | 第三餐补助信息 | +| ALL | `/getRyxx` | 按人员查资料 | +| ALL | `/getRyxxByRyId` | 按人员查资料(含公寓上下文) | +| ALL | `/remakeRemainder` | 重算余额/补助 | +| ALL | `/modifyJcXx` | 修改叫乘信息并重建任务 | +| POST | `/getJcXxFromGy` | 从公寓端获取叫乘信息 | +| ALL | `/getZbyList` | 值班员列表 | +| ALL | `/getGzList` | 工种列表 | +| POST | `/modifyRyXx` | 修改人员资料 | +| POST | `/deleteRyXx` | 删除人员 | +| ALL | `/isHoliday` | 是否节假日 | +| ALL | `/getGyOptions` | 按单位取公寓选项 | +| POST | `/synCallDutyInfo` | 运安计划推送(整表快照) | +| POST | `/processCheckInRegistration` | 设备入寓登记(code 1/2/3) | +| POST | `/processCheckOutRegistration` | 设备离寓(code 1/2) | +| POST | `/processCallDuty` | 叫乘设备状态上报(status 1–5) | +| POST | `/processTempCallDuty` | 临时叫乘状态上报 | +| ALL | `/getCcJcXx` | 按车次聚合叫乘信息 | +| ALL | `/getCcJcXx_Ls` | 历史车次叫乘信息 | +| ALL | `/getCallDutyInfoByDeviceCode` | 叫乘屏按设备码取人员 | +| ALL | `/getTempCallDutyInfoByDeviceCode` | 临时叫乘屏取人员 | +| ALL | `/getSynJcXx` | 分页查运安快照 | +| ALL | `/getRzXx` | 入住信息 | +| ALL | `/getRemoteSynJcXx` | 远程同步(当前为桩实现,返回空成功) | +| ALL | `/synUnMatchJcXx` | 触发交路匹配 | +| POST | `/syncJcXxData` | 手工归档 7 天前已离寓记录 | +| ALL | `/handleTempCalling` | 临时叫乘(人工) | +| ALL | `/handleRoomCall2` / `/handleRoomCallEnd2` | 房间呼叫/结束(新版) | +| ALL | `/handleRoomCall` / `/handleRoomCallEnd` | 房间呼叫/结束(旧版) | +| ALL | `/handleMultiRoomCall` / `/handleMultiRoomCallEnd` | 多房间呼叫/结束 | +| ALL | `/handleCheckOut` | 值班端办理离寓 | +| ALL | `/cancelTrainAssignment` | 取消交路并清理任务 | +| ALL | `/getWslRyXx` | 未匹配人员信息 | +| ALL | `/getRyFjXx` | 人员房间信息 | +| ALL | `/exchangeRoom` | 换房 | +| ALL | `/handleStopCalling` | 停止叫乘(需报警事件键) | +| ALL | `/yatest` | 运安 SOAP 连通性测试 | +| ALL | `/ya/list` | 触发运安计划抓取 | +| ALL | `/driver/{driverId}` | 查询运安司机信息 | +| GET | `/getRecordingConfig` | 录音回放配置(RecordingController) | + +## 3. 交路 `/jlgl` + +| 路径 | 用途 | +| --- | --- | +| `/addJlXx` | 新增交路 | +| `/modifyJlXx` | 修改交路 | +| `/getJlXxList` | 交路列表 | +| `/getJlXxJcsjList` | 交路及叫乘时刻 | +| `/delJlXx`、`/deleteJlXx` | 删除交路 | +| `/uploadJlXx` | Excel 上传交路 | +| `/getMessageFromGy`、`/getMessageFromFcd` | 从公寓/发车点获取信息 | +| `/getGyXxList`、`/getTyXxList` | 公寓/停运信息 | +| `/addTySjList` | 新增停运时间 | +| `/confirmJlXx` | 交路确认 | +| `/getTrainNumberInfo` | 车次信息 | + +## 4. 人员与注册 `/regist` + +| 方法 | 路径 | 用途 | +| --- | --- | --- | +| POST | `/registPersonInfo` | 设备注册协议(code 1 拍照、2 查/更新照片、3 按 ID 查、4 批量增量) | +| POST | `/addPersonInfo` | 新增人员(写真 + 照片表) | +| ALL | `/getPersonelInfo` | 按手机号/姓名查人员 | +| POST | `/updatePersonelInfo` | 修改人员并下发设备指令 | +| ALL | `/deletePersonelInfo` | 逻辑删除人员并清理照片 | + +## 5. 充值 `/recharge` + +| 路径 | 用途 | +| --- | --- | +| `/processCashRecharge` | 设备充值协议(code 1–4) | +| `/handleRecharge` | 值班端处理充值 | +| `/autoRecharge` | 自动补助入账(对应定时器已停用) | +| `/getAllowance` | 补助查询 | +| `/getRechargeInfo` | 充值记录 | +| `/getRechargeStatisInfo` | 充值统计 | +| `/getAllowanceStatis` | 补助统计 | + +## 6. 消费 `/pay` + +| 路径 | 用途 | +| --- | --- | +| `/processPayment` | 设备消费协议(code 1 查余额、2 扣款) | +| `/getConsumDetailInfo` | 消费明细 | +| `/getCashConsumeStatisInfo` | 现金消费统计 | +| `/getAllowanceConsumeStatisInfo` | 补助消费统计 | +| `/getConsumeStatis` | 消费汇总 | + +## 7. 设备接入 + +| 基础路径 | 接口 | 用途 | +| --- | --- | --- | +| `/callingDevice` | `POST /heartbeat` | 叫乘屏心跳 | +| `/checkInDevice` | `POST /heartbeat` | 签到机心跳 | +| `/roomDevice` | `POST /heartbeat` | 房间屏心跳 | +| `/payDevice` | `POST /heartbeat` | 消费机心跳 | +| `/registDevice` | `POST /heartbeat` | 注册机心跳 | +| `/vendPayDevice` | `POST /heartbeat`、`POST /processPayment` | 售货机心跳/支付 | +| `/heartBeat` | `/getDeviceInfo`、`/getDeviceStatusInfo`、`/modifyDeviceInfo`、`/modifyApkVersion` | 管理端设备查询/维护 | + +### 7.1 心跳请求字段(常见) + +`deviceCode`、`versionName`、`deviceType`、`ip`、`deviceUpdateState`、`lastUserUpdateTime`、`lastDeviceConfigUpdateTime`、`roomNumber`。 + +### 7.2 心跳响应字段 + +`devicePassword`、`apartmentName`、`serverTime`、`updateState`、`apkUrl`、指令列表。 + +### 7.3 设备业务协议 + +| 场景 | code/status | 说明 | +| --- | --- | --- | +| 入寓登记 | code 1/2/3 | 查询/提交/按 ID 查询 | +| 离寓 | code 1/2 | 查询/刷脸离寓 | +| 叫乘 | status 1–5 | 开始/复叫/叫乘报警/离寓报警/应答 | +| 临时叫乘 | status 1–5 | 同上,另刷新房间设备 | +| 充值 | code 1–4 | 查余额/申请/查状态/取消 | +| 消费 | code 1/2 | 查余额/扣款 | +| 售货机 | code 1/2/3 | 查余额/下单/结果或退款 | +| 注册 | code 1–4 | 拍照/查询/按 ID/批量增量 | +| 保洁 | — | 开始/结束保洁(`/clean`) | + +## 8. 管理后台 + +### 8.1 登录与权限 + +| 路径 | 用途 | +| --- | --- | +| `/auth/getCaptcha`、`/auth/captchaImage` | 验证码 | +| `/auth/login` | 登录 | +| `/auth/logon` | 注册/开通 | +| `/auth/logout` | 退出 | +| `/auth/getInfo` | 当前用户信息 | +| `/auth/getMenuInfo` | 用户菜单 | +| `/user/*` | 用户增删改查、改密、启停 | +| `/role/*` | 角色与角色权限 | +| `/permission/*` | 权限树与权限维护 | +| `/menu/*` | 菜单维护 | +| `/dept/*` | 部门树、公寓部门树 | + +### 8.2 基础资料 `/Zdwh` + +时间规则、公寓、房间、节假日、餐补标准的新增/修改/删除/查询;公寓与房间资料是入住与推荐的基础。 + +### 8.3 统计与导出 + +| 路径 | 用途 | +| --- | --- | +| `/xxtj/getBjcs` | 报警次数 | +| `/xxtj/getJdqk` | 进出情况 | +| `/xxtj/getRtjb` | 日报 | +| `/xxtj/getYtjb` | 月报 | +| `/xxtj/getDailyApartmentReport`、`/getDailyApartmentReportFull` | 公寓日报 | +| `/xxtj/getApartmentAllowanceStatis` | 补助统计 | +| `/excel/exportJcXxExcell` | 叫乘明细 | +| `/excel/exportDailyApartmentReport` | 日报 | +| `/excel/exportJcXxExcellOld` | 旧版叫乘明细 | +| `/excel/exportAllowanceExcell` | 补助明细 | +| `/excel/exportRechargeExcell` | 充值明细 | +| `/excel/exportConsumeExcell` | 消费明细 | +| `/excel/exportRechargeStatisExcell` | 充值统计 | +| `/excel/exportAllowanceStatisExcell` | 补助统计 | +| `/excel/exportCashConsumeStatisExcell` | 现金消费统计 | +| `/excel/exportAllowanceConsumeStatisExcell` | 补助消费统计 | + +### 8.4 其他 + +| 路径 | 用途 | +| --- | --- | +| `/clean/startCleaning`、`/endCleaning`、`/getRoomCleanInfo` | 保洁 | +| `/jgxxgl/getJgXx` | 报警/警示查询 | +| `/Meals/*` | 餐食、订单、房间、餐次列表 | +| `/kcgl/*` | 库存/出入库/物料/成品/文件 | +| `/synData/*` | 运安/公寓人员、财务数据 | +| `/jwDataLog/getSynJcXxIsOffline` | 同步状态/是否离线 | +| `/websocket/{username}`、`/jcwebsocket/{username}`、`/jlwebsocket/{username}`、`/rlywebsocket/{username}`、`/MealsWebsocket/{username}` | WebSocket 端点 | + +## 9. WebSocket 消息 + +| 消息 | 含义 | +| --- | --- | +| `{"lx":"refresh"}` | 叫乘/房间/人员数据刷新 | +| `{"lx":"ry","content":...}` | 入寓人员信息推送值班端 | +| `{"lx":"zcxx","photoBase64":...,"deviceCode":...,"zcid":...}` | 注册拍照 | +| `{"lx":"xgxx","content":...,"newPhotoBase64":...}` | 人员资料/照片更新 | +| `{"lx":"cz"}` | 充值申请提醒 | + +`JcWebsocket.sendMessage(message, gyid)` 会按公寓路由;`gyid="all"` 表示广播。 + +## 10. 接口扩展建议 + +1. 新接口明确 HTTP 方法,避免用 `@RequestMapping` 触发写操作。 +2. 统一返回体,不在 Controller 里拼 `JSONObject`。 +3. 前端传入的 `gyid` 只能作为提示,服务端必须重新解析/校验。 +4. 设备协议新增字段时保持向后兼容:旧字段可缺省,新增字段给默认值。 +5. 接口文档以本清单为基线,最终以 `JcglController` 等源码为准;Swagger 目前只扫描 `jlgl.controller`,覆盖不全。 diff --git a/docs/07-外部集成与定时任务.md b/docs/07-外部集成与定时任务.md new file mode 100644 index 0000000..4363997 --- /dev/null +++ b/docs/07-外部集成与定时任务.md @@ -0,0 +1,210 @@ +# 07 外部集成与定时任务 + +## 1. Asterisk 电话/广播链路 + +### 1.1 角色 + +- 叫乘执行者:`AsteriskCallUtil`。 +- 连接方式:Asterisk Manager Interface(AMI),使用定制版 `asterisk-java 3.38.0-zfjia`。 +- 会议桥:`Conf-01`。 +- 呼叫对象: + - 值班台:`Local/1701@from-internal`(`callcontrol.ami.console`,默认 1009/1010)。 + - 房间:房间 `sip_account`(`GY_ROOMS.sip_account`)经 `Local/1901@from-internal` 呼入。 +- 串口:`callcontrol.console.*`(默认 `COM1`、9600),用于广播/话机控制。 + +### 1.2 配置项 + +```yaml +callcontrol: + ami: + host: 127.0.0.1 + port: 7058 + user: ${AMI_USER} + pwd: ${AMI_PASSWORD} + console: 1010 + callduration: 180 + console: + port: COM1 + baudrate: 9600 + recording: + base-url: ... # 录音回放地址 +``` + +### 1.3 一次叫乘的时序 + +```mermaid +sequenceDiagram + participant Q as Quartz WakeUpJob + participant S as JcglService + participant A as AsteriskCallUtil + participant AMI as Asterisk AMI + participant C as 值班台 + participant R as 房间话机 + + Q->>S: 触发 JC/FJ/JCJS + S->>A: makeWakeup(人员, 房间SIP) + A->>A: 组装 TTS 文本 + A->>AMI: 呼叫 1701(值班台) + A->>AMI: 呼叫 1901 + 房间 SIP + AMI->>C: 接通 + AMI->>R: 接通并加入 Conf-01 + A->>AMI: 播放 TTS + R-->>A: ConfbridgeJoin / Answered + A->>S: ryzt=5(通话中)→ 挂断后 ryzt=3(待离寓) +``` + +### 1.4 TTS 文案 + +> 尊敬的{姓名},{公寓名称}提醒您,您值乘的{车次}次,接车方式是{接车方式},开车时间{开车时间},现在时间{当前时间},请您携带好随身物品,准备出乘。 + +车次、接车方式、时间会做口语化格式化(`formatTrainCodeForSpeech` / `formatPickupModeForSpeech` / `formatTrainTimeForSpeech`)。 + +### 1.5 超时与挂断 + +| 常量 | 值 | 说明 | +| --- | --- | --- | +| `CALL_TIMEOUT_MS` | 30 秒 | 呼叫无应答超时 | +| `WAKEUP_ANSWERED_AUTO_HANGUP_SECONDS` | 40 秒 | 叫乘接通后自动挂断 | +| `SHOUT_CALL_ANSWERED_AUTO_HANGUP_SECONDS` | 30 秒 | 广播/喊话接通后自动挂断 | + +事件监听: + +- `ConfbridgeJoinEvent`:入会,记录通道与接通。 +- `ConfbridgeLeaveEvent`:离会,清理跟踪。 +- `NewCallerIdEvent`:回填通道/主叫。 +- `HangupEvent`:挂断,若已接通且需跟踪则置 `ryzt=3`;未接通则进入未应答处理(转报警/复叫)。 + +录音:接通后记录录音文件名,追加到 `GY_JCXX.recordfile`;`/jcgl/getRecordingConfig` 返回回放地址。 + +### 1.6 停止/挂断 + +- `stopWakeupCall` / `stopWakeupCallAsync`:挂断该人员所有跟踪通道、值班台/房间通道、`Conf-01`。 +- `hangupTrackedCallsByRyid`:只挂断跟踪到的通道。 +- `isConferenceBusy`:会议桥占用时任务延迟 60 秒。 + +## 2. 机务运安(BYXX)SOAP + +### 2.1 接入方式 + +- WSDL:`classpath:/wsdl/PbResource.wsdl`。 +- 生成客户端:`byxx.resource.PbResource_Service` / `PbResource`。 +- 服务实现:`ByxxResourceService`。 + +### 2.2 主要操作 + +| 操作 | 用途 | +| --- | --- | +| `getLocoHomeList` | 机务段列表 | +| `getDriverInfoBySectionCode` | 按段获取司机资料 | +| `getDriverInfoByWorkNo` | 按工号获取司机 | +| `getDriverPlanByTimeStr` | 按时间段获取乘务计划(叫乘快照来源) | + +### 2.3 计划字段映射 + +| 运安字段 | 快照字段 | +| --- | --- | +| `jobid + taskdicid` | `id` | +| `driver1..driver4` | `ryid`(一条计划拆成最多 4 条人员记录) | +| `plantrainnum` | `cc` | +| `model + loco` | `jch` | +| `dispmancode` | `h1` | +| `plandeptime` | `kcsj` | +| `plancalltime` | `jcsj`(仅保留,最终叫乘时间按本地规则重算) | +| `plandepstation` | `jhcqdd` | +| `jobType`(0 库出,其他站接) | `jcfs` | + +### 2.4 调度 + +- 每 5 分钟抓取一次(`SynDataSchedule.getSynDataJob`),窗口约 4 小时。 +- 每 5 天同步一次人员(`SyncPersonData`,仅 `T` 开头机务段)。 +- 抓取/匹配共用 `CallDutyMatchLock`,防止并发清表。 +- **匹配只处理 `baseConfig.gyid` 配置的公寓**;一实例多公寓部署时,其他公寓不会自动匹配,需改造或拆实例。 + +### 2.5 已知限制 + +- `mergeSynJcXx` 已停用(同 `id` 多司机导致 MERGE 重复)。 +- `getRemoteSynJcXx` 为桩实现,未恢复远程 HTTP 同步。 +- WSDL 地址与派班室/公寓编码硬编码(“通辽南公寓”“通辽南派班室”),多公寓部署需参数化。 + +## 3. 中心服务器 HTTP + +| 场景 | 地址/配置 | 说明 | +| --- | --- | --- | +| 人员资料拉取 | `http://10.32.112.153:52411/api/users?pagenum=N` | `SynDataService.personData`,分页拉取机务人员 | +| 人员变更推送 | `/synData/modifyPersonData` | 运安推送人员增量 | +| 公寓人员查询 | `/synData/getApartmentPersonData*` | 按公寓/工号/时间查询在寓人员 | +| 财务数据 | `/synData/synFinanceData` | 按 IP 解析公寓后返回余额数据 | +| 照片/静态资源 | `baseConfig.centerServerUrl` / `serverUrl` | 人员照片 URL 前缀 | + +中心同步使用 `HttpRequest`(内部封装),未见超时/重试统一配置,排障时需关注网络与超时。 + +## 4. WebSocket + +| 端点 | 用途 | +| --- | --- | +| `/websocket/{username}` | 通用推送(历史) | +| `/jcwebsocket/{username}` | 叫乘主界面刷新/人员信息 | +| `/jlwebsocket/{username}` | 交路界面 | +| `/rlywebsocket/{username}` | 人员/入住相关 | +| `/MealsWebsocket/{username}` | 餐食订单 | + +关键消息见 [06-接口清单.md](./06-接口清单.md) 第 9 节。服务端推送按 `gyid` 路由,`"all"` 广播。 + +## 5. Quartz 叫乘任务 + +| 项 | 说明 | +| --- | --- | +| Job 类 | `quartz/WakeUpJob` | +| 调度管理 | `quartz/SchedulerManager` | +| JobKey | `ryid + "JC"` / `"FJ"` / `"JCJS"` | +| TriggerGroup | `gyid + fjh` | +| 触发方式 | 一次性 cron,精确到秒 | +| 优先级 | JC=1、FJ=2、JCJS=3 | +| 并发控制 | `@DisallowConcurrentExecution` | +| 参数 | `JobParas`(人员、房间、车次、时间、jcid 等) | +| 执行前校验 | `jcid/fjh/叫乘时间` 与库中一致才执行,否则跳过 | +| 合并执行 | 同房间/同车次合并为一个执行组,组长先叫 | +| 会议桥忙 | 延迟 60 秒 | +| 恢复 | `WakeupJobRecoveryRunner` 在 `ApplicationReadyEvent` 后恢复未来任务 | + +失败处理: + +- 任务内部异常写业务日志,不抛到 Quartz。 +- `JCJS` 挂断会议桥、置 `ryzt=2`、写报警记录。 +- 人工停止叫乘会取消该人员全部任务。 + +## 6. Spring `@Scheduled` 清单 + +| 类.方法 | Cron | 作用 | 状态 | +| --- | --- | --- | --- | +| `JcSchedule.notifyMissedCallWarnings` | `5 * * * * ?`(每分钟第 5 秒) | 超时未叫乘分级提醒(第 1/10/15 分钟) | 启用 | +| `JcSchedule.triggerOverdueLeaveAlarms` | `15 * * * * ?`(每分钟第 15 秒) | 已应答但超时未离寓报警 | 启用 | +| `JcSchedule.syncJcXxData` | `0 0 0 * * ?`(Asia/Shanghai) | 归档已离寓满 7 天记录 | 启用 | +| `SynDataSchedule.getSynDataJob` | `0 */5 * * * ?` | 运安抓取 + 记录同步时间 + 触发匹配 | 启用 | +| `SynDataSchedule.synPersonDataJob` | `30 0 0 */5 * ?` | 每 5 天同步运安人员 | 启用 | +| `LeaveSnapshotCleanupSchedule.cleanupExpiredLeaveSnapshots` | `${leaveSnapshot.cleanupCron:0 30 2 * * ?}` | 清理过期离寓抓拍 | 启用 | +| `RechargeSchedule.*` | — | 自动补助/充值 | 全部注释停用 | +| `JlSchedule.*` | — | 交路定时同步 | 注释停用 | +| `LogSchedule.*` | — | 日志清理 | 注释停用 | + +## 7. 并发控制与幂等 + +| 机制 | 位置 | 说明 | +| --- | --- | --- | +| 交路同步锁 | `CallDutyMatchLock` + `GY_CALL_DUTY_MATCH_LOCK` | 抓取/推送/匹配互斥,事务提交后释放 | +| 同步状态位 | `GY_SYN_JCXX_STATUS` | 只有 `1` 才能匹配 | +| 设备事件幂等 | `GYGL_LY_EVENT(device_code, device_event_id)` | 刷脸离寓重复请求直接返回已处理 | +| 报警去重 | `triggerLeaveAlarmIfPending` 条件更新 | 同一次入住只触发一次离寓报警 | +| 提醒去重 | `route_notice_key` / `call_timeout_notice_key` / `missed_call_notice_key` | 同一节点只提醒一次 | +| 充值互斥 | `GY_YKT_CASH_REQUEST` 条件更新 | 完成与取消只能成功一个 | +| 房间占用 | `refreshRoomOccupancy` + 有效在寓明细 | 防止计数漂移 | +| 任务过期校验 | `WakeUpJob` 执行前比对库中数据 | 防止旧任务误叫 | + +## 8. 排障入口 + +1. **叫乘没响**:看 Quartz 任务是否存在(`SchedulerManager` 日志)→ AMI 连接 → 会议桥是否忙 → 房间 `sip_account` 是否为空 → 任务时间是否已过期被跳过。 +2. **叫乘响了但状态不变**:看 AMI 事件(Join/Hangup)是否被正确匹配通道 → `GY_JCXX.ryzt` / `recordfile`。 +3. **运安计划没进来**:看 5 分钟任务日志、`GY_SYN_JCXX_STATUS`、`JWDATA_SYNC_LOG`、WSDL 可达性。 +4. **匹配不上**:核对 `GY_YKT_RY.card_id` 与 `GY_SYN_JCXX.workno`,以及人员是否在有效在寓状态、`jllx` 是否被人工锁定。 +5. **离寓报错**:看抓拍配置 `leaveSnapshot.*`、事件幂等记录、`GYGL_LY_EVENT`。 +6. **设备指令不下发**:看 `GY_SB` 是否有该设备、心跳是否带 `deviceCode`、指令是否已被置 `deleted=1`。 diff --git a/docs/08-部署与配置.md b/docs/08-部署与配置.md new file mode 100644 index 0000000..d689ee1 --- /dev/null +++ b/docs/08-部署与配置.md @@ -0,0 +1,191 @@ +# 08 部署与配置 + +## 1. 构建与运行 + +### 1.1 环境要求 + +| 组件 | 要求 | +| --- | --- | +| JDK | 8 | +| Maven | 3.6+ | +| 数据库 | KingbaseES 8(部分 Profile 仍接 Oracle) | +| 缓存 | Redis(Session) | +| 电话 | Asterisk + AMI 账号 + 会议桥 `Conf-01` | +| 串口 | 广播/话机控制串口(默认 `COM1`,Linux 下为 `/dev/ttyS0` 等) | +| 文件 | 照片、离寓抓拍、录音的可读写目录 | + +### 1.2 构建 + +```bash +cd yktgl +mvn clean package -DskipTests +``` + +- `pom.xml` 未声明 ``,默认生成 jar。 +- 入口继承 `SpringBootServletInitializer`,如需部署到外部 Tomcat,可改为 `war` 并排除内嵌容器。 +- 构建前注意 `pom.xml` 中 Boot 3.x 依赖版本与 Boot 2.6.13 的兼容问题(见 09)。 + +### 1.3 启动 + +```bash +java -jar yktgl-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod +``` + +或: + +```bash +java -Dspring.profiles.active=prod -jar yktgl-0.0.1-SNAPSHOT.jar +``` + +默认端口 `8081`,上下文路径 `/yktgl`。 + +## 2. Profile 对照 + +| Profile | 公寓 `gyid` / `gymc` | 数据源 | 备注 | +| --- | --- | --- | --- | +| `local` | `TMXC` 图们行车公寓 | Kingbase `127.0.0.1:54321/gygl` | 本机调试,Redis/AMI/串口均本机 | +| `dev` | 未启用(注释为 `TLXC`) | Kingbase `10.32.209.76:54321/gygl` | 开发环境 | +| `prod` | `TLNXC` 通辽南行车公寓 | Kingbase `10.33.165.41:54321/gygl` | 生产 | +| `tl` | `TLXC` 通辽行车公寓 | Kingbase `10.32.209.76:54321/gygl` | 通辽 | +| `sjt` | `SJTXC` 苏家屯行车公寓 | Kingbase `10.32.209.76:54321/gygl` | 苏家屯 | +| `byq` | `BYQXC` 鲅鱼圈行车公寓 | Oracle `10.32.116.45/db3` | 仍在 Oracle | +| `cc` | `SJTXC` 苏家屯行车公寓 | Oracle `10.32.5.175/rlxt` | 仍在 Oracle,注意与 `sjt` 同名不同库 | +| `dl` | `DLXC` 大连行车公寓 | Oracle `10.32.116.45/db3` | 仍在 Oracle | +| `pj` | `PJXC` 盘锦行车公寓 | Oracle `10.32.116.45/db3` | 仍在 Oracle | + +> 所有 Profile 中的数据库口令、AMI 口令、`devicePassword` 目前均为明文,且已提交到仓库。生产使用前应改为环境变量/配置中心,并轮换口令。 + +## 3. 关键配置项 + +### 3.1 服务 + +| 配置 | 说明 | +| --- | --- | +| `server.port` | 默认 8081 | +| `server.servlet.context-path` | `/yktgl` | +| `spring.profiles.active` | 默认 `local` | +| `spring.mvc.pathmatch.matching-strategy` | `ant_path_matcher`(兼容 Springfox) | + +### 3.2 数据源与连接池 + +| 配置 | 说明 | +| --- | --- | +| `spring.datasource.url` | Kingbase 需带 `currentSchema=gygl` | +| `spring.datasource.driver-class-name` | `com.kingbase8.Driver` | +| `spring.datasource.hikari.*` | 最小空闲 5、最大 15、连接超时 60s、最大生命周期 600s | + +### 3.3 Redis / Session + +| 配置 | 说明 | +| --- | --- | +| `spring.redis.host/port` | Redis 地址 | +| `spring.session.store-type` | `redis` | +| `spring.session.timeout` | 默认 30m | + +### 3.4 公寓与业务开关 `baseConfig` + +| 配置 | 说明 | +| --- | --- | +| `gyid` / `gymc` | 当前公寓(单公寓/默认兜底) | +| `centerServerUrl` | 中心服务器地址(照片前缀等) | +| `serverUrl` | 服务自身地址 | +| `synJcXxIp` | 运安推送来源 IP 白名单(逗号分隔) | +| `devicePassword` | 设备心跳返回的设备密码 | +| `centralPhoneIp` | 中心电话 IP | +| `serverOS` | `windows` / `linux`,影响照片与抓拍路径 | +| `modifyyasj` | 是否允许调整运安叫乘时间 | +| `allowRouteChangeAfterCompletedCall` | 默认 `true`;叫乘完成后是否允许运安匹配继续变更交路 | +| `syncMatchWaitSeconds` | 默认 900;抓取/匹配等待同步锁的最长秒数 | +| `fjgydbs` | 分公寓/终端所属公寓段的交互标识(按需配置) | + +### 3.5 电话与录音 `callcontrol` + +| 配置 | 说明 | +| --- | --- | +| `callcontrol.ami.host/port` | AMI 地址 | +| `callcontrol.ami.user/pwd` | AMI 账号 | +| `callcontrol.ami.console` | 值班台分机/控制台号 | +| `callcontrol.ami.callduration` | 呼叫时长上限(秒) | +| `callcontrol.console.port/baudrate/...` | 串口参数 | +| `callcontrol.recording.base-url` | 录音回放根地址;为空时默认 `http:///conf/` | +| `RECORDING_BASE_URL` | 环境变量覆盖录音地址 | + +### 3.6 离寓抓拍 `leaveSnapshot` + +| 配置 | 默认 | 说明 | +| --- | --- | --- | +| `enabled` | `true` | 是否保存抓拍 | +| `requiredForDevice` | `false` | 设备未传抓拍是否失败(兼容旧设备) | +| `retentionDays` | `7` | 保留天数 | +| `cleanupCron` | `0 30 2 * * ?` | 每日清理 | +| `rootDirectory` | 见 Profile | 抓拍保存目录 | +| `urlPrefix` | `/yktgl/photo/leave-snapshots` | 访问前缀 | + +### 3.7 静态资源与文件 + +| 配置 | 说明 | +| --- | --- | +| `spring.web.resources.static-locations` | `classpath:/static/` + 文件目录(Linux 常见 `/home/gygl/resouce/`) | +| 人员照片 | `.../resouce/photo/{ryid}.png`(代码中写死,Windows 为 `C:\project_doc\resouce\photo\`) | +| 离寓抓拍 | `.../resouce/photo/leave-snapshots/{gyid}/{eventId}.jpg` | + +> 照片路径在 `RegistService` 中按 `serverOS` 硬编码,迁移环境时需同步修改代码或改为配置项。 + +## 4. 数据库升级 + +手工脚本位于 `yktgl/src/main/resources/db/`,按日期顺序执行: + +1. `GY_SYN_JCXX_STATUS.sql`(首次部署) +2. `manual/V20260921_01__leave_snapshot_evidence_kingbase.sql` +3. `manual/V20260922_01__call_duty_notice_dedup_kingbase.sql` +4. `manual/V20261008_01__syn_jcxx_write_time_kingbase.sql` +5. `manual/V20261008_02__call_duty_match_lock_kingbase.sql` +6. `manual/V20261009_01__jcxx_history_archive_kingbase.sql` +7. `manual/V20261023_01__cash_request_state_kingbase.sql` +8. `manual/V20261029_01__reconcile_tlxc_room_occupancy_preview.sql`(先预览) +9. `manual/V20261029_01__reconcile_tlxc_room_occupancy.sql`(确认后执行) + +注意: + +- 脚本不会随应用启动自动执行。 +- 执行前备份;先跑 preview 核对影响行数。 +- 若数据库仍为 Oracle,需确认脚本语法是否兼容(现有 manual 脚本以 Kingbase 为主)。 + +## 5. 多公寓部署 + +两种方式: + +1. **一套服务一个公寓**:通过 `baseConfig.gyid/gymc` 指定;设备 IP 仍参与解析。 +2. **一套服务多个公寓**:依赖 `GY_SB.ip` / `GY_GYZD.ip` 做 IP→公寓映射;同一 IP 命中多个公寓会直接拒绝。 + +部署检查: + +- 每个公寓的设备 IP 必须唯一归属。 +- 反向代理必须透传真实客户端 IP(`X-Forwarded-For`),否则所有请求会被识别为代理 IP。 +- WebSocket 需允许 `Upgrade`/`Connection` 头,并适当延长超时。 + +## 6. 上线检查清单 + +- [ ] JDK 8、时区 `Asia/Shanghai`、服务器时间与 NTP 同步(叫乘精确到秒)。 +- [ ] Kingbase 可连,schema `gygl` 正确,升级脚本已执行。 +- [ ] Redis 可连,Session 能写入。 +- [ ] `baseConfig.gyid/gymc` 与当前公寓一致。 +- [ ] `synJcXxIp` 包含运安推送源 IP。 +- [ ] `devicePassword` 与设备端一致。 +- [ ] Asterisk AMI 可连,`Conf-01` 存在,值班台/房间分机可呼通。 +- [ ] 串口设备可打开(Linux 检查权限与设备名)。 +- [ ] 照片/抓拍目录存在且可写,静态资源映射正确。 +- [ ] 反向代理透传真实 IP,WebSocket 可升级。 +- [ ] 启动日志无 Mapper/Bean 初始化错误;`WakeupJobRecoveryRunner` 已恢复任务。 +- [ ] 用一个测试人员走通:入住 → 叫乘 → 应答 → 离寓 → 归档。 + +## 7. 运行期维护 + +| 事项 | 建议 | +| --- | --- | +| 日志 | 现有 `System.out` 与业务日志混杂,建议接入统一日志文件与轮转 | +| 归档 | 每日 00:00 自动归档 7 天前离寓记录;数据量大时关注执行耗时 | +| 抓拍清理 | 每日 02:30 清理过期抓拍;确认磁盘余量 | +| 同步 | 运安每 5 分钟抓取;观察 `JWDATA_SYNC_LOG` 与同步状态 | +| 备份 | 数据库 + 照片/抓拍目录 + 录音目录 | +| 口令 | 尽快外置并轮换数据库/AMI/设备口令 | diff --git a/docs/09-迭代指引与风险清单.md b/docs/09-迭代指引与风险清单.md new file mode 100644 index 0000000..ea7e75c --- /dev/null +++ b/docs/09-迭代指引与风险清单.md @@ -0,0 +1,94 @@ +# 09 迭代指引与风险清单 + +## 1. 技术债与风险 + +### P0:会直接影响构建/上线 + +| 项 | 现状 | 影响 | 建议 | +| --- | --- | --- | --- | +| Spring Boot 依赖版本错配 | `pom.xml` 在 Boot 2.6.13 父工程下显式声明 `spring-boot-starter-web:3.5.4`、`spring-boot-starter-data-redis:3.3.1` | 可能拉入 Spring 6 / Jakarta 依赖,与 `javax.servlet` 代码不兼容,导致编译/运行失败 | 删除显式版本,交给父工程管理;或统一升到 Boot 3 + Jakarta(大改造)。先跑一次 `mvn clean test` 确认基线 | +| 明文口令与内部地址入库 | 各 `application-*.yml` 含数据库口令、AMI 口令、`devicePassword`、内网 IP | 已推送到远端,存在泄露风险 | 轮换口令;改用环境变量/配置中心;生产库最小权限 | +| 无根级 `.gitignore` | 仓库根目录没有 `.gitignore`,仅有 `YktVue/.gitignore` | 易误提交构建产物、日志、数据转储 | 在仓库根补充 `.gitignore`(target、logs、*.log、node_modules、dist、本地配置) | +| 构建/测试基线不明 | `target/` 为空,测试未运行,无 CI 配置 | 无法判断改动是否破坏现有功能 | 建立 CI:编译 + 单测 + 关键 SQL 校验 | + +### P1:影响正确性与可维护性 + +| 项 | 现状 | 影响 | 建议 | +| --- | --- | --- | --- | +| `JcglService` 过大 | 约 4192 行,混合叫乘、房间、补助、交路、归档、设备指令 | 改动风险高、冲突多、难测试 | 按“叫乘执行 / 房间 / 卡务 / 交路匹配 / 归档”拆服务,先抽只读逻辑与纯函数 | +| 交路 MERGE 停用 | `mergeSynJcXx` 因同 `id` 多司机被注释 | 快照与历史数据可能长期不一致 | 改为 `id + ryid` 唯一键后恢复,并加数据清理脚本 | +| 远程同步为桩 | `/jcgl/getRemoteSynJcXx` 直接返回成功 | 调用方误以为已同步 | 恢复实现或明确返回“未启用” | +| 部分定时任务停用 | `RechargeSchedule`、`JlSchedule`、`LogSchedule` 全部注释 | 自动补助、交路同步、日志清理不生效 | 明确替代方案;若不再需要则删除代码 | +| 写接口用 `@RequestMapping` | 大量写操作接受 GET | 易被误触发、难做权限/审计 | 新接口强制 POST;存量逐步收敛 | +| 异常被吞 | 大量 `catch` 返回 `Result.failure`,仅写日志 | 故障定位困难、事务边界不清 | 统一异常处理与错误码;关键路径保留堆栈 | +| 日志不规范 | `System.out.println` 与业务日志混用 | 无法集中检索/轮转 | 统一 SLF4J + 文件轮转;敏感信息脱敏 | +| 硬编码 | 公寓名“通辽南公寓/通辽南派班室”、照片路径、内网 IP、中心地址 | 多公寓/迁移环境需改代码 | 抽到配置或字典表 | +| 交路匹配只覆盖单公寓 | `matchCallDutyUnderLock` 只处理 `baseConfig.gyid` 配置的公寓 | 一实例多公寓时,其他公寓不会自动匹配交路 | 明确部署形态;如需一实例多公寓,把 `gyid` 改为循环/列表并逐公寓加锁 | +| 房态语义不一致 | `selectRoomRecommendations` 与前端允许 `3`(待清扫)、`6`(保洁中);`validateRoomForCheckIn` 也允许 `6` | 保洁中的房间可能被入住 | 确认业务规则;若不允许,收紧 SQL 与校验 | +| 备份文件/旧构建 | `RoomLook copy.vue`、`JcXx.vue.bak_mojibake`、`resources/static` 下旧前端与 `.gz` | 干扰阅读、增大仓库 | 清理或移入 `legacy/` | + +### P2:体验与稳健性 + +| 项 | 说明 | +| --- | --- | +| `processCallDuty` 空值判断顺序 | `ryid.equals("") || ryid == null` 在 `ryid=null` 时会先 NPE;应改为 `StringUtils.isBlank` | +| `h1.concat(...)` 结果未赋值 | `ByxxResourceService` 中拼接公寓编码无效,`h1` 实际未追加 `housecode` | +| `syncJlXX` 返回 `null` | `/jcgl/ya/list` 永远返回空数据,容易误判 | +| `personData` 空数组 | `jsonArray.getJSONObject(0)` 未判空,上游无数据时异常 | +| 时间字符串 | 全链路用字符串 + `TO_DATE`,格式/时区问题只能在运行期暴露 | +| IP→公寓 | 依赖真实客户端 IP;反向代理未透传时会识别错误 | +| 设备照片/抓拍 | Base64 体积大,需关注请求体大小、内存与磁盘 | +| 测试接口暴露 | `/test/*`、`/jcgl/yatest`、`/jcgl/driver/{id}` 可访问外部系统,生产应限制 | + +## 2. 改动落点速查 + +| 需求 | 先看 | 可能要改 | +| --- | --- | --- | +| 调整叫乘/复叫/报警时间 | `GY_SJGZ`、`modifyJcXxBySynJcXx`、`populateJcXxByJlXx` | `JcglService`、`WakeUpJob`、`SchedulerManager` | +| 改叫乘提醒节点 | `JcglService.notifyMissedCallWarnings`、`selectMissedCallWarnings` | `JcSchedule`、SQL | +| 改叫乘语音内容 | `AsteriskCallUtil.makeWakeup` | TTS 文案、格式化函数 | +| 改房间状态规则 | `getRoomStatus`、`validateRoomForCheckIn`、`modifyRoomStatus` | `JcglRoomMapper.xml`、前端 `Room.vue` | +| 改房间推荐 | `selectRoomRecommendations` | 推荐 SQL、`getRoomRecommendations` | +| 新增设备类型 | `heartBeat`、`GY_SB`、`GY_SB_XT` | 新建 `xxxDevice` 包、心跳控制器 | +| 改设备指令 | `HeartBeatService.addDeviceInstructionInfo*` | 各业务调用点 | +| 改入住补助 | `getAllowanceInfo`、`getTrainAllowanceInfo`、`changeRemainder` | `GY_YKT_*` 表、节假日规则 | +| 改充值/消费 | `RechargeService`、`PayService`、`VendPayDeviceService` | 余额与明细同事务 | +| 改交路维护 | `JlGlService`、`GYGL_JLXX` | 上传模板、同步接口 | +| 改运安同步 | `ByxxResourceService`、`SynDataSchedule` | WSDL、字段映射、锁 | +| 改匹配规则 | `matchCallDutyUnderLock` | 工号匹配、时间重算、任务重建 | +| 改离寓/抓拍 | `processCheckOutRegistration`、`leaveSnapshot.*` | `GYGL_LY_EVENT`、文件目录 | +| 新增报表 | `xxtj` / `excel` | 对应 Mapper XML | +| 改权限/菜单 | `display`、`GL_*` | 登录、RBAC、菜单 | +| 改公寓识别 | `ZdwhService.getUniqueGyByIp` | `GY_SB`、`GY_GYZD`、代理配置 | + +## 3. 建议的迭代顺序 + +1. **建立基线**:修复 pom 版本错配,确保能编译;跑通现有测试;加 CI。 +2. **安全整改**:口令外置与轮换;生产关闭测试接口;补根 `.gitignore`;清理备份文件。 +3. **稳定性**:统一日志与异常;关键路径加监控(叫乘任务创建数、AMI 连接、同步状态、快照积压)。 +4. **结构治理**:拆分 `JcglService`;把硬编码公寓/路径/地址配置化;收敛写接口 HTTP 方法。 +5. **业务增强**:恢复并测试 `mergeSynJcXx`、远程同步;完善多公寓参数化;补叫乘/入住/离寓的集成测试。 +6. **数据治理**:归档与索引优化;房间占用对账;历史数据清理。 + +## 4. 待确认问题 + +建议与业务/现场逐条确认,确认后回写本文档: + +1. **保洁中(`room_status=6`)房间是否允许入住?** 当前推荐 SQL、入住校验、前端都允许,但 `modifyRoomStatus` 又禁止修改保洁中房间。 +2. **待清扫(`3`)房间是否应出现在推荐列表?** 当前会出现,可能影响值班员选房效率。 +3. **便乘/驻在是否完全不叫乘?** 当前 `jllx=1` 且跳过任务;是否符合现场? +4. **运安下发的 `jcsj` 是否彻底弃用?** 当前仅保留读取,最终按本地规则重算。 +5. **远程同步是否还需要?** `getRemoteSynJcXx` 目前是桩。 +6. **多公寓部署形态**:一公寓一实例,还是一实例多公寓?影响 IP 映射、同步任务与锁。 +7. **刷脸抓拍是否强制?** `requiredForDevice=false` 目前兼容旧设备,何时切为 `true`? +8. **Oracle Profile(byq/cc/dl/pj)是否继续维护?** 若迁移 Kingbase,需要同步 SQL 兼容性。 +9. **叫乘任务恢复范围**:应用重启后恢复未来任务,是否也要补偿“重启期间已错过”的任务?当前只恢复未来任务,错过由提醒任务兜底。 +10. **归档保留策略**:`GY_JCXX_HIS` 保留多久?是否需要冷备/清理? + +## 5. 文档维护约定 + +- 本文档以代码为准;接口、表、状态码变更时同步更新对应章节。 +- 新增模块时,在 [03-业务模块地图.md](./03-业务模块地图.md) 增加一行,并在 [06-接口清单.md](./06-接口清单.md) 补接口。 +- 新增表/字段时,在 [05-数据模型.md](./05-数据模型.md) 补字段与状态字典。 +- 新增外部依赖/定时任务时,在 [07-外部集成与定时任务.md](./07-外部集成与定时任务.md) 补时序与排障点。 +- 每次迭代结束,把已解决的风险从本清单移除,并在提交说明中引用相关文档章节。 diff --git a/docs/10-架构优化方案-三端拆分.md b/docs/10-架构优化方案-三端拆分.md new file mode 100644 index 0000000..ef34d82 --- /dev/null +++ b/docs/10-架构优化方案-三端拆分.md @@ -0,0 +1,818 @@ +# 10 架构优化方案:平台端 / 公寓端 / 应用端三端拆分 + +> 文档性质:架构设计方案(Design Only)。本文不含代码补丁、不含数据库变更脚本、不含配置修改,仅作为后续重构设计、评审与实施的输入。 + +## 0. 文档说明 + +表 0-1 文档信息 + +| 项 | 内容 | +| --- | --- | +| 文档编号 | docs/10 | +| 文档名称 | 架构优化方案:平台端 / 公寓端 / 应用端三端拆分 | +| 适用范围 | 铁路行车公寓叫乘系统(`YktVue` 前端、`yktgl` 后端、`docs` 文档) | +| 关联文档 | `01-系统总览`、`02-工程结构与分层`、`03-业务模块地图`、`04-核心业务流程`、`05-数据模型`、`06-接口清单`、`07-外部集成与定时任务`、`08-部署与配置`、`09-迭代指引与风险清单`、`frontend/*` | +| 约束条件 | 1)不改变“平台做核心业务与基础数据服务、公寓端做本地设备与任务执行、应用端做值班员操作界面”的总体定位;2)应用端与公寓端复用同一套代码与同一个数据库,不构成第三套服务或独立库;3)平台端不承担现场设备实时接入与控制;4)公寓端与应用端技术基线锁定为 RuoYi-Vue + Vue + MySQL(见 2.4),本基线不再讨论替代方案;5)公寓端直连本地 Asterisk,语音链路不经平台端中转 | +| 交付对象 | 架构评审、重构详细设计、试点实施与验收 | + +表 0-2 修订记录 + +| 版本 | 日期 | 说明 | +| --- | --- | --- | +| v1.0 | 2026-11-07 | 首次形成:现状评估、目标架构、职责边界、模块划分、数据架构、接口契约方向、设备接入、部署运行、迁移路径、风险与优先级 | +| v1.1 | 2026-11-07 | 细化公寓端:锁定 RuoYi-Vue + Vue + MySQL 技术基线;补齐应用端与公寓端的复用实现(2.5);新增语音模块清单与依赖方向(4.4);新增语音域数据设计(5.4)与分机配置一致性(5.5);新增数据库选型迁移分析(5.6);新增语音域接口清单(6.4);新增 Asterisk 链路设计(7.7–7.11);新增 Asterisk 部署运行(8.3);新增专项风险与决策点 D11–D16 | + +表 0-3 本次修订(v1.1)涉及的小节与表号 + +| 类型 | 编号 | 内容 | +| --- | --- | --- | +| 修订 | 表 0-1 | 约束条件补入技术基线与语音链路前提(第 4、5 条) | +| 修订 | 表 1-1 | 追加 3 条与 AMI、会议桥、分机台账相关的现状事实 | +| 新增 | 2.4 / 表 2-2 | 三端技术基线(确定前提) | +| 新增 | 2.5 / 图 2-3 / 表 2-3 | 应用端与公寓端的复用实现(工程结构、路由、权限、部署) | +| 修订 | 3.2 | 公寓端“负责/不负责”补入语音、分机、事件三条职责 | +| 新增 | 3.4 / 表 3-2 | 语音链路上的三端分工 | +| 修订 | 表 4-3 | 公寓端模块清单追加 4 个语音模块 | +| 新增 | 4.4 / 图 4-1 / 表 4-4 | 语音相关模块与依赖方向 | +| 新增 | 5.4 / 表 5-3、5-4 | 语音域数据设计(含 MySQL 适配) | +| 新增 | 5.5 / 表 5-5 | 分机配置在本地库与 Asterisk 之间的一致性机制 | +| 新增 | 5.6 / 表 5-6 ~ 5-9 | 数据库选型:KingbaseES → MySQL 的影响、迁移、双跑与平台端取舍 | +| 新增 | 6.4 / 表 6-3 | 公寓端语音域接口清单 | +| 新增 | 7.7 ~ 7.11 / 图 7-1 / 表 7-4 ~ 7-7 | Asterisk 语音链路:拓扑、资源生命周期、事件跟踪、闭环边界、自检巡检 | +| 新增 | 8.3 / 表 8-3 ~ 8-5 | Asterisk 与公寓端同机部署、端口防火墙、录音存储与清理 | +| 新增 | 10.3 / 表 10-3 | 语音与数据库专项风险 | +| 修订 | 表 10-2 | 追加待决策点 D11–D16 | +| 修订 | 表 11-1、11.3 | 落地顺序补入语音域治理与数据库切换两项 | +| 修订 | 附录 A/B/C/D | 术语、图表索引、代码证据、维护约定同步更新 | + +## 目录 + +- [1. 现状评估](#1-现状评估) +- [2. 目标架构](#2-目标架构) +- [3. 端职责边界](#3-端职责边界) +- [4. 领域与模块划分](#4-领域与模块划分) +- [5. 数据架构](#5-数据架构) +- [6. 接口与集成](#6-接口与集成) +- [7. 设备接入方案](#7-设备接入方案) +- [8. 部署与运行](#8-部署与运行) +- [9. 迁移与实施路径](#9-迁移与实施路径) +- [10. 风险与决策点](#10-风险与决策点) +- [11. 结论与优先级](#11-结论与优先级) +- [附录 A 术语表](#附录-a-术语表) +- [附录 B 图表清单](#附录-b-图表清单) +- [附录 C 代码证据索引](#附录-c-代码证据索引) +- [附录 D 文档维护约定](#附录-d-文档维护约定) + +--- + +## 1. 现状评估 + +本章只做定位与改造建议,不修改代码。表 1-1 是拆分设计所依据的代码与配置事实,表 1-2 是模块级归属判断。 + +### 1.1 现状事实基线 + +表 1-1 现状事实与拆分影响 + +| 事实 | 代码/配置证据 | 对拆分的影响 | +| --- | --- | --- | +| 一套 jar 同时扮演“公寓端”和“中心端” | `JcglService` 注册人员时转发 `centerServerUrl + /yktgl/regist/addPersonInfo`;`JlGlService` 从 `centerServerUrl + /yktgl/jlgl/getMessageFromGy` 拉交路 | 平台与公寓的边界已存在,但靠同一套接口“自调自”,必须先切出接口契约 | +| 公寓身份靠请求 IP 推断 | `ZdwhService.getGyIdByIp` / `getUniqueGyByIp`,在 `JcglController`、6 个设备包、`ExcelExportController`、`LoginController` 等 25 处以上调用 | 目标架构下公寓身份应来自部署配置与认证上下文,IP 识别只作为兼容层 | +| 多公寓共用一套库 | `tl` / `sjt` / `dev` 共用 `10.32.209.76:54321/gygl`,仅 `prod` 独立 | 与“公寓本地数据本地存”直接冲突,是数据归属改造重点 | +| 交路匹配只处理一个公寓 | `matchCallDutyUnderLock` 仅处理 `baseConfig.gyid` | 一实例多公寓时其他公寓永不自动匹配,本身说明“计划与匹配应上收平台” | +| 叫乘执行与运安强绑定在同一进程 | `SynDataSchedule`(每 5 分钟抓取 → 写 `GY_SYN_JCXX` → 触发匹配)与 `WakeUpJob` 同进程 | 目标是平台下发计划、公寓只执行、结果上报 | +| 设备认证是全局共享口令 | `baseConfig.devicePassword` 在 6 个设备服务中被注入并原样下发心跳响应 | 公寓本地化后必须升级为一机一密/签名 | +| 设备指令一次性投递、无 ACK | `GY_SB_ZL` 取最新一条后置 `deleted=1`(`HeartBeatService`) | 弱网必丢指令,属公寓端必须解决的核心问题 | +| 餐饮直接改卡务余额 | `MealsService.updateMoney` → `JcglService.changeRemainder` + `JcglMapper.updateRemainder` | 餐饮归平台、账务在主账,二者必须走“记账接口”而非直接改余额 | +| WebSocket 是进程内实现 | `JcWebsocket` 用静态 `Map`,`sendMessage(message, gyid)` 按 `username` 当 `gyid` 匹配 | 应用端与公寓端同进程可复用;但不能横向扩容,除非补广播 | +| 前端按部署硬编码公寓 | `public/config.js`:`apiRoot = window.location.host`、`gyid = localStorage.getItem('gyid') || 'TLXC'`,并注释各公寓 IP/端口 | 正好对应“一公寓一部署”,可演进为公寓端/应用端同源前端 | +| 值班能力集中在单页 | `views/JcXx.vue`(约 5294 行)承载盯控、入寓、换房、充值、人员、交路、设备状态、告警音、TTS | 应用端应以此页为骨架做角色化拆分,而非另起一套 | +| 构建基线不稳 | `pom.xml` 在 Boot 2.6.13 父工程下显式声明 `spring-boot-starter-web:3.5.4`、`spring-boot-starter-data-redis:3.3.1` | 拆分前必须先修复,否则新工程无法验证 | +| AMI 连接是单例且不重连 | `AsteriskAmiConfig` 在 `@Bean` 内 `login()`,失败仅打印;`AsteriskCallUtil.makeCall` 仅在 `IllegalStateException` 时补登一次 | 语音链路必须在公寓端闭环并具备连接自愈,不能靠重启容器恢复 | +| 会议桥硬编码单实例 | `"Conf-01"` 在 `AsteriskCallUtil`、`JcglController`、`WakeUpJob` 中硬编码 20 处以上;`isConferenceBusy` 以内存 `recordlist` + `core show channels concise` 判断 | 单会议桥是容量与串音约束,必须在设计层明确会议粒度 | +| 分机没有独立台账 | 分机只以 `GY_ROOMS.sip_account` 单列存在(`RoomEntity.sip_account`、`JcglRoomMapper` 可写),无注册状态、无中继、无绑定实体,`sip_status` 从未被写入 | 分机资源需独立建模,才具备增删改停用与状态跟踪能力 | + +### 1.2 模块定位与目标归属 + +表 1-2 主要模块定位与目标归属 + +| 现状模块(包) | 实际职责 | 目标归属 | 主要问题 | +| --- | --- | --- | --- | +| `jcgl`(`JcglService`,约 4193 行) | 入寓/离寓、叫乘状态机、房间推荐与房态、补助计算、交路匹配、归档、设备指令、WebSocket 推送 | 拆三份:任务执行与状态机 → 公寓端;补助规则与账务 → 平台端;盯控与人工干预 → 应用端 | 上帝类,跨 6 个领域,任一改动影响全链路 | +| `jlgl` | 交路模板 CRUD、叫乘时刻、Excel 上传、从中心拉取 | 平台端(交路主数据)+ 公寓端只读快照 | 与 `centerServerUrl` 双向调用语义不清,真源不明确 | +| `regist` | 人员资料、照片、增删改 | 平台端(人员主数据)+ 公寓端缓存 | 注册链路绕过本地直写“中心”,失败会整体回滚用户操作 | +| `recharge` / `pay` / `vendPayDevice` | 现金充值、消费扣款、余额变动 | 账务主账 → 平台端;公寓端保留本地流水与离线可用缓存 | 余额以“改字段”方式落在公寓库,无流水约束、无对账 | +| `MealsOrdering` | 菜品、订单、后厨、送餐、结算 | 平台端(餐饮业务) | 直接调 `JcglService.changeRemainder` 改余额,跨端耦合最重 | +| `zdwh` | 公寓、房间、工种、节假日、餐补、IP→公寓 | 平台端(主数据)+ 公寓端本地缓存 | 房间是公寓资产却与平台主数据同库;IP 解析扩散全工程 | +| `syndata` / `ByxxResourceService` | 运安 SOAP 抓取、人员同步、财务汇总(`getApartmentPersonDataByGyCode/ByWorkNo/ByTime`、`synFinanceData`) | 平台端(运安接入 + 分发 + 汇总接收) | 部署在公寓实例内,每个公寓各自连运安并各自清空快照表 | +| `heartBeat` + 6 个 `*Device` 包 | 设备心跳、指令下发、设备档案 | 公寓端(设备网关) | 全局共享口令、指令一次性投递、无 ACK、无离线队列 | +| `quartz`(`WakeUpJob` / `SchedulerManager`)+ `AsteriskCallUtil` | 叫乘调度、AMI 会议桥、TTS、串口广播 | 公寓端 | 未配置持久化 JobStore(默认内存模式),重启只恢复未来任务;AMI/串口为本地资源,不能上收 | +| `schedule` | `JcSchedule`(漏叫提醒、超时离寓、归档)、`SynDataSchedule`(运安)、`LeaveSnapshotCleanupSchedule` | 混合:提醒/归档/清理 → 公寓端;运安抓取 → 平台端 | 一个包内混着“本地执行”与“平台接入”两类任务 | +| `websockets` | 5 个端点,按 `gyid` 推送刷新/人员/充值/订单 | 公寓端 + 应用端共用 | 进程内状态,无订阅者身份校验 | +| `display` | 登录、RBAC、菜单、日志 | 平台端(全局账号与审计基线)+ 公寓端用 RuoYi 自有 RBAC | 两套账号体系若不约定会出现“平台账号进不了公寓端、公寓账号看不到平台菜单” | +| `xxtj` / `excel` | 日报、月报、补助/充值/消费统计与导出 | 平台端(跨公寓报表);公寓端保留本公寓运行报表 | 当前跨表统计在公寓库内执行,数据分散后无法汇总 | +| `kcgl` | 库存、出入库、物料 | 独立边界(可留平台或独立系统) | 与叫乘主线弱相关,不应进入公寓端 | +| `test`、`/jcgl/yatest`、`/jcgl/driver/{id}` | 测试/演示入口 | 全部下线 | 生产暴露外部系统调用入口 | + +### 1.3 与目标架构不一致的核心点 + +1. **“平台—公寓”未分层,只靠 `centerServerUrl` 字符串互调**。注册、交路两条链路都是“同一套 Controller 既当设备接口又当中心接口”,无版本、无契约、无重试与幂等。 +2. **公寓身份靠 IP 推断且多公寓共享库**。这是“一实例多公寓”的妥协设计,直接阻碍“公寓本地数据本地存”。 +3. **叫乘执行、设备接入、平台接入三者同进程**。平台不可达会拖累叫乘主链路,叫乘高峰也会挤占同步线程(`AsyncConfig` 核心 10 / 最大 200 / 队列 10,队列极小)。 +4. **账务散落**。余额在公寓库被直接更新,餐饮、售货机、充值三条链路各自改余额,缺少统一流水与对账口径。 +5. **设备通道不可靠**。一次性投递 + 全局口令 + 无 ACK,是弱网现场最典型故障源。 +6. **状态机与视图耦合**。`ryzt` 由设备上报、Quartz、AMI 事件、人工操作共同写入,缺少单一写入口;应用端与公寓端同库后风险被放大。 + +--- + +## 2. 目标架构 + +### 2.1 逻辑架构 + +图 2-1 目标逻辑架构 + +```mermaid +flowchart TB + subgraph PLAT["平台端(现有 yktgl 收敛演进)"] + P1["主数据服务:公寓/房间基线/人员/交路/时间规则/节假日/餐补"] + P2["计划服务:运安 SOAP 接入 + 乘务计划 + 任务生成"] + P3["账务服务:补助规则/余额主账/流水归集/对账"] + P4["餐饮服务:菜品/订单/后厨/结算"] + P5["权限与审计:账号/RBAC/操作审计/接口审计"] + P6["报表与监控:跨公寓统计/设备在线率/执行质量"] + P7["上报接收与对账中心:结果/流水/告警/水位"] + end + + subgraph APT["公寓端(RuoYi 新建,一公寓一实例)"] + A1["设备网关:注册/心跳/指令/事件/离线队列"] + A2["叫乘执行引擎:Quartz + AMI + 串口广播 + TTS"] + A3["本地主数据缓存:人员/交路/房间/规则快照"] + A4["在寓与房态事实:入住/离寓/换房/保洁"] + A5["本地账务缓存 + 消费受理"] + A6["上报器:outbox + 幂等键 + 重试"] + A7["值班 API + WebSocket 推送"] + A8["本地运维:自检/日志/版本"] + end + + subgraph DUTY["应用端(与公寓端同代码同库,同进程部署)"] + D1["叫乘盯控台:任务/报警/应答/临叫/停止"] + D2["房态与房间管理:选房/换房/停用/保洁"] + D3["人工干预与审核:充值确认/资料修正/备注"] + end + + subgraph EXT["外部"] + E1["运安 SOAP"] + E2["现场设备"] + E3["Asterisk / 串口 / 话机"] + E4["中心汇总/财务"] + end + + DUTY -->|同进程领域调用| APT + A2 --> E3 + A1 --> E2 + APT -->|下行拉取:主数据/计划/规则| PLAT + APT -->|上行推送:结果/流水/告警/水位| PLAT + PLAT --> E1 + PLAT --> E4 +``` + +### 2.2 部署拓扑 + +图 2-2 部署拓扑 + +```mermaid +flowchart LR + subgraph Center["中心机房"] + LB["Nginx / 网关(TLS)"] + PF1["平台端实例 A"] + PF2["平台端实例 B"] + PDB[("平台主库")] + PRD[("Redis")] + POS[("对象存储:照片/抓拍/录音")] + end + + subgraph Apt1["公寓 A 现场"] + N1["Nginx(公寓内网)"] + S1["公寓端+应用端同进程实例"] + DB1[("公寓本地库")] + RD1[("本地 Redis")] + AST1["Asterisk + 串口"] + DEV1["叫乘屏/签到机/房间屏/消费机/售货机"] + DUTY1["值班员浏览器"] + end + + subgraph Apt2["公寓 B 现场(同构)"] + S2["公寓端+应用端同进程实例"] + DB2[("公寓本地库")] + end + + LB --- PF1 + LB --- PF2 + PF1 --- PDB + PF2 --- PDB + PF1 --- PRD + PF2 --- PRD + PF1 --- POS + + N1 --- S1 + S1 --- DB1 + S1 --- RD1 + S1 --- AST1 + DEV1 --> N1 + DUTY1 --> N1 + S2 --- DB2 + + S1 -->|出站 HTTPS 443| LB + S2 -->|出站 HTTPS 443| LB +``` + +**拓扑要点** + +1. 平台端不进公寓内网:所有跨端流量由公寓端主动出站发起,规避现场 NAT/防火墙,并让平台与设备链路彻底解耦。 +2. 公寓端 = 部署单元 = 公寓隔离单元:一公寓一实例一库(或独立 schema),`gyid` 由部署参数固定。 +3. 应用端不新增进程与库:值班员访问 `http://<公寓服务器>/duty`,与公寓端同 JVM、同库、同 Nginx。 +4. 平台端可多实例;公寓端的 WebSocket 为进程内实现,因此公寓端须保持单实例(或先完成广播改造再扩容)。 + +### 2.3 三端调用关系与数据流 + +表 2-1 三端调用关系与数据流 + +| 调用方向 | 协议 | 触发方式 | 典型数据 | +| --- | --- | --- |-----------------------------| +| 应用端 → 公寓端 | 进程内 Service 调用 | 同步 | 人工干预、房态修改、审核动作 | +| 公寓端 → 平台端 | HTTPS 主动拉取 | 定时(如 5 分钟)+ 变更通知 | 主数据增量、乘务计划、规则版本 | +| 公寓端 → 平台端 | HTTPS 主动推送 | 近实时批量 + 重试 | 执行结果、账务流水、告警摘要、设备摘要、g'y'g水位 | +| 设备 → 公寓端 | 内网 HTTP(后续可评估 MQTT) | 心跳 + 事件 | 指令取走、业务上报 | +| 平台端 → 运安 | SOAP | 定时 | 乘务计划 | +| 平台端 → 中心/财务 | HTTPS | 定时/按需 | 财务汇总、跨公寓人员查询 | +| 平台端 → 公寓端 | 不允许直连 | — | 仅落“待下发配置版本”,由公寓端拉取 | + +--- + +## 3. 端职责边界 + +### 3.1 平台端 + +**负责** + +- 公寓档案、房间基线(`gyid + room_no + 床位数`)、人员主数据与照片、交路模板与叫乘时刻、时间规则、节假日、餐补/补助标准。 +- 运安计划的唯一入口(替代现状各公寓分别抓取),并将计划转换为叫乘任务/计划快照下发对应公寓。 +- 账务主账:补助发放规则、余额主账、消费/充值/补助流水归集、跨端对账。 +- 餐饮业务(菜品、订单、后厨、结算规则),但结算扣款必须经账务接口,不直接改公寓库余额。 +- 账号、角色、权限、操作审计、跨公寓报表、设备在线率与执行质量监控、告警汇总。 +- 接收各公寓执行结果与异常补偿请求,维护每公寓水位与对账结果。 + +**不负责** + +- 不接入现场设备、不下发设备指令、不持有设备心跳流水。 +- 不执行叫乘(不连 AMI、不控串口、不播 TTS)。 +- 不直连公寓本地库,不做跨库 JOIN。 +- 不承担房间实时房态(仅保留基线,实时占用以公寓端为准)。 + +### 3.2 公寓端 + +**负责** + +- 本公寓设备接入:注册、认证、心跳、指令队列、事件受理、离线缓存。 +- 本公寓叫乘执行全链路:任务创建、Quartz 调度、AMI 会议桥、TTS、串口广播、复叫/报警/停止、重启恢复与漏叫补偿。 +- 在寓事实与房态(`GY_JCXX` 语义)、换房、退房、保洁、超时离寓报警。 +- 本地主数据缓存与规则快照(人员照片、交路、时间规则、节假日),保证断网可叫乘。 +- 本地账务流水与离线消费受理(缓存余额),向平台归集。 +- 结果上报:执行结果、流水、告警摘要、设备摘要、归档摘要、水位。 + +**不负责** + +- 不直连运安(运安仅由平台接入),不维护跨公寓人员全集。 +- 不决定补助标准,不做跨公寓统计,不维护平台账号体系。 +- 不制定园区级设备策略(策略由平台配置,执行在公寓)。 + +### 3.3 应用端(与公寓端同代码同库) + +**负责** + +- 叫乘盯控:任务列表、报警、应答确认、临时叫乘、房间/多房间喊话、停止叫乘、录音回放入口。 +- 房间与房态:选房、换房、停用/恢复、保洁开始/结束、在寓人员查看。 +- 人工干预与审核:充值确认、人员资料修正、交路人工指定与重置、备注与操作留痕。 + +**不负责** + +- 不直接改设备指令表、不直接改叫乘任务表(必须经公寓端领域服务)。 +- 不持有独立数据库,不引入第三套服务,不做独立鉴权中心。 +- 不承担跨公寓查询(跨公寓看平台端)。 + +表 3-1 应用端与公寓端的逻辑分工 + +| 维度 | 公寓端(执行域) | 应用端(操作域) | +| --- | --- | --- | +| 表归属 | 现有 `GY_JCXX`、`GY_SB*`、`GY_SYN_JCXX` 等执行与设备表(可加 `apt_*` 前缀新表) | 新增 `DUTY_*`:操作审计、人工干预记录、审核动作、备注、班次交接 | +| 写权限 | 仅执行引擎/设备网关写状态字段(`ryzt`、`jcsj_s`、`finish_jc` 等) | 仅通过状态机服务写,禁止直连 Mapper 修改状态字段 | +| 部署形态 | 同一 JVM 内的 `module-apartment` | 同一 JVM 内的 `module-duty`,依赖公寓端领域接口,禁止反向依赖 | +| 前端形态 | 同一前端工程的 `apt` 入口(设备/终端页) | 同一前端工程的 `duty` 入口(值班员页),按角色裁剪路由与菜单 | +| 鉴权 | 设备用设备凭据;值班员用本地 RuoYi 账号 | 复用公寓端 RuoYi 账号 + 数据范围锁定 `gyid` | + +**公寓隔离方式** + +1. 部署期固定 `apartment.gyId`,服务启动即校验;`gyid` 不再从请求 IP 推断,`getGyIdByIp` 降级为旧设备兼容兜底并打告警。 +2. 库级隔离:一公寓一 schema/库 + 独立连接账号,账号仅对本库有 DML 权限,对平台库无任何权限。 +3. 请求级隔离:值班员登录后 `gyid` 写入会话与令牌声明,服务层用拦截器强制注入,业务 SQL 一律带 `gyid` 条件。 +4. 设备级隔离:设备注册时绑定 `gyid + deviceCode`,跨公寓 `deviceCode` 直接拒绝。 + +--- + +## 4. 领域与模块划分 + +### 4.1 能力归属 + +表 4-1 能力归属决策表(● 主责 ○ 参与/缓存 — 不参与) + +| 能力 | 平台端 | 公寓端 | 应用端 | 协同说明 | +| --- | --- | --- | --- | --- | +| 公寓/房间基线 | ● | ○ 缓存 | — | 房间物理属性(楼层/朝向/SIP/床位数)由公寓维护后上报,平台存基线 | +| 人员主数据/照片 | ● | ○ 缓存 | — | 平台是全量真源,公寓增量缓存 + 照片版本 | +| 交路模板/叫乘时刻 | ● | ○ 快照 | — | 公寓可上报“现场临时交路”,平台确认后版本化 | +| 时间规则/节假日/餐补 | ● | ○ 快照 | — | 按 `gyid` 差异化,必须版本化下发 | +| 运安计划接入 | ● | — | — | 公寓端不再持有 WSDL | +| 任务生成(计划→叫乘时刻) | ● | ○ 复算校验 | — | 平台给“计划 + 规则版本”,公寓端本地复算并回执 | +| 叫乘任务执行 | — | ● | — | Quartz/AMI/串口/TTS 全在公寓端 | +| 叫乘状态机 | — | ● | ○ 触发 | 唯一写入口在公寓端,应用端只发命令 | +| 入寓/离寓/换房/房态 | — | ● | ○ 操作 | 应用端发起,公寓端校验与落库 | +| 设备接入/指令/心跳 | — | ● | ○ 查看 | 应用端仅查询与触发“刷新”类指令 | +| 补助规则与发放 | ● | ○ 试用缓存 | — | 规则在平台,入住时由公寓端触发发放并上报 | +| 余额主账 | ● | ○ 缓存 | — | 公寓端本地记账 + 平台归集,日对账 | +| 消费受理(消费机/售货机) | — | ● | — | 现场必须离线可用 | +| 餐饮订单/后厨 | ● | ○ 代理 | ○ 视图 | 公寓端只做终端接入与转发,业务在平台 | +| 报警与告警 | ○ 汇总 | ● 判定 | ● 展示 | 判定阈值在公寓端,摘要上报平台 | +| 归档 | — | ● | — | 公寓本地归档 + 归档摘要上报 | +| 统计报表 | ○ 跨公寓 | ● 本公寓 | ● 查看 | 公寓端出运行报表,平台出跨公寓报表 | +| 账号/RBAC/审计 | ● 平台侧 | ● 本地侧 | ● 复用公寓侧 | 两套 RBAC 通过“人员—账号映射”打通,不合并表 | +| 库存 `kcgl` | 独立 | — | — | 不进入公寓端,避免污染现场部署 | + +### 4.2 平台端模块清单 + +表 4-2 平台端模块清单 + +| 模块 | 职责 | 现状来源 | +| --- | --- | --- | +| `platform.master` | 公寓、房间基线、工种、时间规则、节假日、餐补标准 | `zdwh` | +| `platform.person` | 人员主数据、照片、增量分发 | `regist` + `syndata` 人员部分 | +| `platform.route` | 交路模板、叫乘时刻、停运调整、Excel 上传 | `jlgl` | +| `platform.plan` | 运安 SOAP 接入、计划快照、任务生成与下发 | `ByxxResourceService` + `SynDataSchedule` | +| `platform.ledger` | 补助规则、余额主账、流水归集、对账 | `recharge` / `pay` 账务部分 | +| `platform.catering` | 菜品、订单、后厨、结算 | `MealsOrdering` | +| `platform.report` | 跨公寓报表、导出、执行质量 | `xxtj` + `excel` | +| `platform.ingest` | 上报接收、幂等、水位、重放、对账(新增) | 新增 | +| `platform.iam` | 账号、角色、权限、审计 | `display` | +| `platform.ops` | 公寓注册、版本、健康度、配置下发版本(新增) | 新增 | + +### 4.3 公寓端模块清单 + +表 4-3 公寓端模块清单 + +| 模块 | 职责 | 现状来源 | +| --- | --- | --- | +| `apt.device.gateway` | 统一设备入口:注册/认证/心跳/指令/事件 | 收敛 6 个 `*Device` 包 | +| `apt.device.command` | 指令队列:下发—ACK—重投—过期 | 替代 `GY_SB_ZL` 一次性投递 | +| `apt.device.catalog` | 设备档案、在线率、版本 | `heartBeat` | +| `apt.duty.engine` | 叫乘执行:Quartz、AMI、TTS、串口、复叫/报警/停止 | `quartz` + `AsteriskCallUtil` | +| `apt.duty.state` | 状态机唯一写入口(`ryzt`/`finish_jc`/`finish_ly`/`stoped`) | 从 `JcglService` 抽取 | +| `apt.stay` | 入住、离寓、换房、在寓事实、房态重算 | 从 `JcglService` 抽取 | +| `apt.room` | 房间本地属性、推荐、停用、保洁 | `zdwh` 房间部分 + `clean` | +| `apt.ledger.local` | 本地余额缓存、离线消费、流水生成 | `pay` / `recharge` 本地部分 | +| `apt.report.outbox` | 上报 outbox、幂等键、重试、水位(新增) | 新增 | +| `apt.master.cache` | 主数据快照与版本校验 | `zdwh` 缓存部分 | +| `apt.duty.view` | 应用端后端 API、WebSocket 推送、人工干预命令 | 从 `JcglController` 拆出 | +| `apt.ops` | 自检、时钟偏差、磁盘(抓拍/录音)、日志、版本 | 新增 | + +--- + +## 5. 数据架构 + +### 5.1 数据分层与归属 + +表 5-1 数据分层、归属与同步方向 + +| 数据层 | 典型数据 | 归属 | 存储位置 | 同步方向 | +| --- | --- | --- | --- | --- | +| 平台主数据 | 公寓、房间基线、人员、照片、交路、时间规则、节假日、餐补标准 | 平台 | 平台主库 + 对象存储 | 下行:平台 → 公寓(版本化增量) | +| 平台计划数据 | 运安计划快照、生成的叫乘任务/时刻、变更与取消 | 平台 | 平台主库 | 下行:平台 → 公寓 | +| 公寓执行数据 | `GY_JCXX`(在寓/叫乘事实)、`GYGL_LY_EVENT`、房态、保洁、报警 | 公寓 | 公寓本地库 | 本地强一致;摘要上行 | +| 设备数据 | `GY_SB`、`GY_SB_XT`、指令队列、心跳 | 公寓 | 公寓本地库 | 仅上行摘要(在线率/版本/故障) | +| 账务数据 | 补助明细、消费/充值流水、余额 | 主账平台 / 流水双记 | 平台主库 + 公寓本地缓存 | 流水上行;对账后余额快照下行 | +| 归档数据 | `GY_JCXX_HIS`、离寓事件历史 | 公寓 | 公寓本地库 | 上行归档摘要 | +| 账号与审计 | 账号、角色、权限、操作审计 | 平台(平台侧)/ 公寓(本地侧) | 各自库 | 人员—账号映射下行 | +| 汇总与报表 | 跨公寓统计、财务汇总 | 平台 | 平台主库/数仓 | 由上行数据加工 | + +### 5.2 一致性策略 + +表 5-2 各场景一致性策略 + +| 场景 | 策略 | +| --- | --- | +| 公寓内业务(入住、离寓、房态、叫乘状态) | 强一致:单库本地事务 + 状态机唯一入口 | +| 主数据/规则下发 | 最终一致 + 版本号:公寓端保存 `masterVersion`,版本不一致时拒绝接收任务并触发全量重拉 | +| 执行结果上报 | 最终一致 + 至少一次 + 幂等:outbox 表 + `idempotencyKey`,平台按 `(gyid, bizType, bizId, bizVersion)` 去重 | +| 账务 | 本地记账为准的最终一致:本地先落流水(离线可用),平台归集后按日对账,差异进入人工处理队列 | +| 余额 | 平台主账权威;公寓端缓存带“版本 + 最后对账日期”,超期未对账时限制高额操作(如大额充值与退款) | +| 任务下发与执行 | 平台下发“计划 + 规则版本”,公寓端复算并回执 `taskAck`;不一致以平台为准并告警 | + +### 5.3 同库边界防混乱 + +1. **表归属清单(Ownership Matrix)**:每张表标注 `owner=platform|apartment|duty`、允许的写入模块、是否为上报源;越权写入纳入 Code Review 检查项。 +2. **命名分区**:执行域沿用 `GY_*`(避免大规模迁移),应用域新增 `DUTY_*`(操作审计、人工干预、审核、交接班);报表/只读视图统一 `V_*`,禁止跨域直写。 +3. **模块依赖单向**:`module-duty → module-apartment → module-platform-client`,禁止反向依赖;用 Maven 模块或架构约束测试固定。 +4. **禁止跨端 SQL 关联**:不在公寓库写“需要平台数据才能算”的统计 SQL,一律先落到本地快照。 +5. **`gyid` 强制注入**:拦截器统一注入并校验,任何 SQL 缺 `gyid` 视为缺陷;新代码统一走上下文,不再逐处解析 IP。 +6. **状态字段单写入口**:`ryzt`、`finish_jc`、`finish_ly`、`stoped` 仅允许 `apt.duty.state` 写入,设备上报、Quartz、AMI 事件、人工操作统一转为状态机命令。 + +--- + +## 6. 接口与集成 + +### 6.1 跨端接口清单 + +表 6-1 平台端与公寓端关键接口 + +| # | 方向 | 接口 | 用途 | 关键字段 | +| --- | --- | --- | --- | --- | +| 1 | 下行 | `GET /platform/v1/master/apartments/{gyId}` | 公寓档案与能力开关 | `gyId`、`name`、`features[]`、`version` | +| 2 | 下行 | `GET /platform/v1/master/persons?gyId&cursor&version` | 人员增量(含照片版本) | `ryid`、`cardId`、`deptId`、`photoVersion`、`deleted` | +| 3 | 下行 | `GET /platform/v1/master/routes?gyId&from&to` | 交路模板与叫乘时刻 | `jlid`、`cc`、`kcsj`、`jcsj`、`lysj` | +| 4 | 下行 | `GET /platform/v1/master/rules?gyId` | 时间规则/节假日/餐补标准 | `zjjcsjgz`、`kjjcsjgz`、`lysjgz`、`holidays[]` | +| 5 | 下行 | `GET /platform/v1/master/rooms?gyId&version` | 房间基线(取决于决策点 D3) | `roomNo`、`bedNumbers` | +| 6 | 下行 | `GET /platform/v1/plans/duty?gyId&from&to` | 乘务计划(替代本地抓运安) | `planId`、`workno`、`trainNo`、`depTime`、`pickupMode` | +| 7 | 下行 | `GET /platform/v1/tasks/changes?gyId&cursor` | 任务变更/取消增量 | `taskId`、`op=UPSERT/CANCEL`、`version` | +| 8 | 上行 | `POST /platform/v1/ack/tasks` | 任务接收回执(含复算结果) | `taskId`、`accepted`、`computedCallTime`、`ruleVersion` | +| 9 | 上行 | `POST /platform/v1/report/duty-results` | 叫乘/入寓/离寓执行结果(批量) | `events[]`:`eventId`、`type`、`jcid`、`ryid`、`occurredAt`、`status` | +| 10 | 上行 | `POST /platform/v1/report/ledger` | 账务流水(消费/充值/补助) | `flowId`、`type`、`ryid`、`amount`、`balanceAfter`、`deviceCode` | +| 11 | 上行 | `POST /platform/v1/report/device-status` | 设备在线率/版本/故障摘要 | `onlineCount`、`offlineList[]`、`versions{}` | +| 12 | 上行 | `POST /platform/v1/report/alarms` | 叫乘/离寓报警摘要 | `alarmId`、`type`、`jcid`、`level` | +| 13 | 上行 | `POST /platform/v1/report/archive` | 归档摘要与数据量 | `period`、`archivedCount`、`from`、`to` | +| 14 | 上行 | `POST /platform/v1/heartbeat` | 公寓端存活、版本、水位、时钟偏差 | `gyId`、`appVersion`、`outboxWatermark`、`clockSkewMs` | +| 15 | 查询 | `GET /platform/v1/status/{gyId}` | 平台侧查看公寓健康度与积压 | `lastReportAt`、`pending`、`failed` | +| 16 | 补偿 | `POST /platform/v1/reconcile/run?gyId&date` | 触发日对账 | 返回差异清单 | +| 17 | 补偿 | `POST /platform/v1/replay` | 指定幂等键/区间重放 | `gyId`、`bizType`、`from`、`to` | + +现状对应关系:`getApartmentPersonDataByGyCode` / `ByWorkNo` / `ByTime`、`synFinanceData` 是“中心被查询”的雏形;`/yktgl/regist/addPersonInfo`、`/yktgl/jlgl/getMessageFromGy` 是“中心被调用”的雏形。新接口应保留旧路径的兼容适配层,不直接改旧签名。 + +### 6.2 上报协议 + +**信封结构(所有上行接口统一)** + +```json +{ + "gyId": "TLXC", + "appVersion": "1.0.0", + "batchId": "uuid", + "sentAt": "2026-11-07T10:00:00+08:00", + "watermark": 102345, + "items": [ + { + "idempotencyKey": "hash(gyId+bizType+bizId+bizVersion)", + "bizType": "DUTY_RESULT", + "bizId": "JC20261107001", + "bizVersion": 3, + "occurredAt": "2026-11-07T09:58:12+08:00", + "receivedAt": "2026-11-07T09:58:13+08:00", + "payload": {} + } + ] +} +``` + +协议要点: + +1. `occurredAt`(现场发生时间)、`receivedAt`(公寓端入库时间)、`serverReceivedAt`(平台接收时间)三时间戳并存,用于处理时钟差异与迟到数据。 +2. `idempotencyKey = hash(gyId + bizType + bizId + bizVersion)`:同一业务对象同一版本只生效一次;版本递增用于“更正上报”。 +3. `watermark`:公寓端本地自增序号,平台据此判断漏报/乱序并作为对账水位;现状 `GY_SYN_JCXX_STATUS`(0/1)可作为该思路的历史参照。 +4. 批量上限建议单批 ≤ 500 条、≤ 1MB,失败按条回报,避免整批重传。 + +### 6.3 幂等、重试、对账与失败处理 + +表 6-2 跨端可靠性机制 + +| 机制 | 设计 | +| --- | --- | +| 幂等 | 平台侧 `(gyid, biz_type, biz_id, biz_version)` 唯一约束;重复请求返回“已处理 + 原结果” | +| 重试 | 指数退避(1s / 5s / 30s / 2min / 10min,上限 1 小时)+ 抖动;连续失败进入死信队列并触发公寓端告警 | +| 顺序 | 同一 `bizId` 按 `bizVersion` 单调;乱序到达丢弃旧版本并记录 | +| 对账 | 按日比对 `count`、`sum(amount)`、`max(watermark)` 三维;差异输出至“待人工处理”并可触发重放 | +| 补偿 | 平台支持“按区间重放”,公寓端支持“按 outbox 序号重发”,双侧均可主动发起 | +| 断网 | outbox 落本地库(非内存),断网期间持续累积,恢复后按水位续传 | +| 平台不可用 | 不影响叫乘:下行使用本地快照(带版本与有效期),过期后仅降级“新计划不生效”并告警 | + +--- + +## 7. 设备接入方案 + +### 7.1 接入形态 + +1. 统一网关入口 `/apt/dev/{deviceType}/register|heartbeat|event|command/ack`,把现有 6 个 `*Device` 包的重复协议收敛为“网关 + 类型插件”(叫乘屏、签到机、房间屏、消费机、注册机、售货机)。 +2. 设备只连本公寓服务器,内网优先;公寓端不暴露公网入口。 +3. 协议保持现有 JSON 字段以兼容存量固件;新增字段一律可选并给默认值。 + +### 7.2 设备认证 + +表 7-1 设备认证阶段设计 + +| 阶段 | 机制 | +| --- | --- | +| 注册 | `deviceCode + 公寓激活码` 首次绑定 `gyid`,生成一机一密(替换现状全局 `baseConfig.devicePassword`) | +| 心跳 | `deviceCode + 时间戳 + nonce + HMAC(secret, body)`;校验时间窗(±5 分钟)与 nonce 去重,防重放 | +| 越权 | `deviceCode` 与 `gyid`/房间不匹配直接拒绝并告警(现状仅校验存在性) | +| 轮换 | 密钥支持在线轮换,旧密钥保留一个过渡期 | +| 过渡 | 存量设备保留“口令模式”开关,按公寓逐步切换为签名模式 | + +### 7.3 心跳、指令与状态采集 + +表 7-2 心跳、指令与状态采集 + +| 能力 | 设计 | +| --- | --- | +| 心跳 | 设备上报版本、IP、房间号、配置版本;服务端返回 `serverTime`(校时)、`apartmentName`、`configVersion`、待执行指令列表 | +| 指令 | 下发—确认—重投:指令带 `commandId + seq + expireAt`,取走后置 `DELIVERED`,设备 ACK 后置 `DONE`;超时未 ACK 自动重投(上限 N 次),过期作废并告警(替代 `deleted=1` 一次性投递) | +| 状态采集 | 设备上报在线/离线、软件版本、故障码、当前展示状态;公寓端按小时聚合,仅上报摘要 | +| 优先级 | `P0` 叫乘相关(重投 5 次、不过期)、`P1` 房态刷新、`P2` 版本升级(可延后) | +| 离线 | 设备离线超阈值产生“设备离线”告警,进入应用端盯控列表 | + +### 7.4 告警处理 + +1. 判定在公寓端:叫乘未应答、复叫未应答、叫乘结束未应答、超时未离寓、设备离线、AMI 不可用、串口异常、上报积压。 +2. 两级输出:本地即时告警(应用端弹窗/声响/TTS,走本地 WebSocket)与平台摘要上报(数量、类型、最近发生时间,不逐条上报)。 +3. 现有 `GYGL_JGXX` 与 `is_jcbj` / `is_lybj` 语义保留,作为本地告警事实表。 + +### 7.5 本地数据缓存 + +1. 主数据缓存:人员(含照片)、交路、时间规则、节假日、房间属性,带 `version + validUntil`,过期只提示不阻断。 +2. 指令缓存:可靠队列(本地库表 + 状态机)。 +3. 事件缓存:设备事件先落本地(`deviceCode + deviceEventId` 幂等,沿用 `GYGL_LY_EVENT` 设计),再异步上报。 +4. 媒体缓存:离寓抓拍、录音本地保存 + 生命周期清理(沿用 `leaveSnapshot.retentionDays` / `cleanupCron`),平台只存索引并按需回捞。 + +### 7.6 上报边界 + +表 7-3 设备域数据上报与不上报 + +| 上报平台 | 不上报平台 | +| --- | --- | +| 设备清单与在线率摘要、版本分布、故障事件 | 原始心跳流水、每次指令明细 | +| 叫乘/复叫/应答/离寓执行结果(业务事件) | AMI 通道事件、TTS 文本、串口字节流 | +| 报警摘要(类型、数量、时间) | 每条报警的完整现场上下文 | +| 账务流水 | 余额中间态、实时读余额请求 | +| 归档摘要与对账水位 | 全量历史明细(按需回捞) | + +--- + +## 8. 部署与运行 + +### 8.1 部署建议 + +表 8-1 三端部署形态 + +| 端 | 形态 | 规模建议 | 关键依赖 | +| --- | --- | --- | --- | +| 平台端 | 无状态双实例 + Nginx + 平台主库(Kingbase/Oracle)+ Redis + 对象存储 | 2 实例起步;对外仅暴露 443 | 运安 WSDL、中心/财务系统 | +| 公寓端 + 应用端 | 单实例同进程,Nginx 前置,本地库 + 本地 Redis + Asterisk + 串口 | 1 公寓 1 实例;不允许水平扩容(WebSocket 进程内) | 本地设备、AMI、串口、磁盘 | +| 前端 | 同一工程双入口:`/apt`(终端)、`/duty`(值班) | 与公寓端同域部署,沿用 `config.js` 的 `apiRoot = host` 思路 | — | + +隔离与网络: + +1. 一公寓一实例一库,Nginx 按公寓内网域名/端口区分;平台端不配置到公寓的路由。 +2. 跨端仅允许公寓 → 平台出站 HTTPS;平台侧以 `gyId` + 客户端证书/令牌双向认证。 +3. 反向代理必须透传真实 IP(兼容层仍需),并提前把 `gyid` 落到会话,逐步降低对 IP 的依赖。 + +### 8.2 非功能要求 + +表 8-2 非功能要求与实现要点 + +| 项 | 要求与做法 | +| --- | --- | +| 多公寓隔离 | 部署单元隔离 + 库隔离 + 设备绑定隔离 + 会话 `gyid` 强制注入;平台侧查询强制 `gyId` 参数并做权限校验 | +| 网络中断/弱网 | 叫乘主链路 100% 本地闭环(任务、AMI、设备、房态);下行快照带有效期;上行走 outbox 断点续传;补齐统一超时与熔断(现状 `HttpRequest` 无统一超时) | +| 重复上报 | 幂等键 + 版本号 + 平台唯一约束;重复请求返回原结果,不产生二次账务 | +| 时钟差异 | 全端 NTP;心跳下发 `serverTime`;三时间戳并存;账务日切以平台时间为准;偏差 > 5s 在应用端显式告警 | +| 安全认证 | 设备一机一密 + HMAC;值班员走本地 RuoYi 会话 + 权限点;平台—公寓 mTLS 或双向令牌;数据库/AMI/设备口令外置到环境变量或配置中心并轮换 | +| 审计日志 | 三类审计:值班员操作审计(`DUTY_*`)、设备指令审计(下发/确认/重投/过期)、跨端接口审计(请求/响应摘要 + 幂等键 + 结果);保留期与查询入口在应用端可查 | +| 可观测性 | 指标:叫乘任务创建数与执行成功率、AMI 连接状态、会议桥占用、设备在线率、指令积压、outbox 积压、上报失败率、对账差异数、平台接口 P99;日志统一 SLF4J 并以 `traceId` / `gyId` 贯穿;健康检查区分“可叫乘”与“可上报”两个探针 | +| 容量与降级 | 归档与抓拍清理置于低峰;平台不可用时公寓端进入“离线模式”并横幅提示;上报积压超阈值时降级为仅上报 `P0` 事件 | + +--- + +## 9. 迁移与实施路径 + +图 9-1 实施路径 + +```mermaid +flowchart LR + S0["阶段零 基线修复"] --> S1["阶段一 现状梳理与边界确认"] + S1 --> S2["阶段二 接口契约与数据模型设计"] + S2 --> S3["阶段三 公寓端试点"] + S3 --> S4["阶段四 多公寓推广"] + S4 --> S5["阶段五 平台端收敛与旧逻辑下线"] + S3 -.并行.-> S0 +``` + +表 9-1 阶段零:基线修复(前置) + +| 项 | 内容 | +| --- | --- | +| 目标 | 建立可信的编译、测试、安全基线,使后续拆分可验证 | +| 主要产出 | 修复 `pom.xml` 依赖版本错配;CI(编译 + 单测 + 关键 SQL 校验);口令与内网地址外置并轮换;关闭测试接口;补仓库根 `.gitignore` | +| 验收标准 | 主干可稳定编译;测试可重复执行;仓库无明文口令 | +| 风险 | 版本修复可能暴露存量编译问题 → 单独分支验证,不与业务改动混提 | + +表 9-2 阶段一:现状梳理与边界确认 + +| 项 | 内容 | +| --- | --- | +| 目标 | 锁定边界,不改业务逻辑 | +| 主要产出 | 表归属清单(每张 `GY_*` 表标注 owner 与写入模块);接口现状清单(`jcgl`/`jlgl`/`regist`/`syndata`/`heartBeat` 全量,标注设备用/值班用/中心用);`JcglService` 方法级职责地图(叫乘、房间、卡务、交路、归档五组);`gyid` 依赖点清单;待决策问题清单(见 10.2) | +| 验收标准 | 任一表能回答“谁写、谁读、跨不跨端”;任一接口能回答目标归属;无悬空模块 | +| 风险 | 业务方对“房间属谁、余额属谁”认知不一致 → 安排两轮评审并签字定稿 | + +表 9-3 阶段二:接口契约与数据模型设计 + +| 项 | 内容 | +| --- | --- | +| 目标 | 把第 5、6 章固化为可评审契约 | +| 主要产出 | 17 个跨端接口契约(含错误码、幂等语义、限流);上报信封与 outbox 表设计;主数据版本与快照表设计;任务下发/回执模型;平台主账与公寓流水模型;旧接口兼容适配层方案 | +| 验收标准 | 契约评审通过;Mock 服务完成“下行 → 复算 → 上报 → 对账”全链路联调;幂等与重试用例通过(重复投递、乱序、断网续传三类) | +| 风险 | 契约反复变更 → 冻结版本号,变更走 v2 并保留 v1 至少两个公寓周期 | + +表 9-4 阶段三:公寓端试点(1 个公寓) + +| 项 | 内容 | +| --- | --- | +| 目标 | 单公寓端到端跑通,平台端仅提供主数据与计划 | +| 主要产出 | RuoYi 公寓端骨架 + 设备网关 + 叫乘执行引擎 + 状态机 + outbox;应用端盯控台(从 `JcXx.vue` 抽取核心能力,先做“看 + 干预”);本地库 schema 与数据迁移脚本(机场只读双跑) | +| 验收标准 | 断网 2 小时仍完成“入住 → 叫乘 → 复叫 → 报警 → 应答 → 离寓 → 归档”;恢复后 10 分钟内上报追平且对账零差异;叫乘准点率不低于现网;值班员操作步骤增加不超过 10% 且有记录 | +| 风险 | RuoYi 与现有前端体验差异大 → 试点期保留旧值班台只读并行对照;固件不支持 ACK → 网关做“尽力确认”降级 | + +表 9-5 阶段四:多公寓推广 + +| 项 | 内容 | +| --- | --- | +| 目标 | 复制到 3–5 个公寓,验证隔离能力与运维成本 | +| 主要产出 | 标准化部署包与一键初始化脚本(库/schema/配置/证书);公寓注册与版本管理;监控大盘(在线率、上报积压、对账差异);运维手册与故障预案 | +| 验收标准 | 单公寓部署 ≤ 4 小时;公寓间零串数据(跨 `gyid` 越权用例 100% 拦截);平台端可查看各公寓健康度与差异清单 | +| 风险 | Oracle 存量公寓(`byq`/`cc`/`dl`/`pj`)SQL 兼容 → 试点避开或先做兼容层;网络质量差异 → 按公寓配置批量与重试参数 | + +表 9-6 阶段五:平台端收敛与旧逻辑下线 + +| 项 | 内容 | +| --- | --- | +| 目标 | 平台端成为唯一业务与主数据入口,公寓端剥离平台职责 | +| 主要产出 | `syndata` / `ByxxResourceService` / `SynDataSchedule` 从公寓端下线;`regist` 转发中心改为“本地写 + 上报”;`jlgl` 从中心拉取改为标准下行接口;`xxtj` / `excel` 跨公寓统计迁平台;`getGyIdByIp` 兼容层按公寓逐个关闭;`kcgl`、`test`、`yatest`、`driver/{id}` 明确处置 | +| 验收标准 | 公寓端不再连接运安与中心;平台端可独立出具跨公寓报表;旧接口调用量归零(按网关日志统计);回滚演练通过 | +| 风险 | 旧报表/旧终端仍依赖旧接口 → 提前一个周期盘点调用量,保留灰度通道 | + +--- + +## 10. 风险与决策点 + +### 10.1 风险清单 + +表 10-1 风险与应对 + +| 风险 | 表现 | 应对建议 | +| --- | --- | --- | +| 职责边界不清 | 房间归属、余额归属、餐饮是否碰余额反复讨论 | 阶段一产出归属矩阵并签字;争议项默认“业务在平台、事实在公寓” | +| 数据不一致 | 平台余额与公寓缓存长期漂移;计划与任务时间不一致 | 日对账 + 水位 + 差异工单;规则版本化 + 公寓端复算回执 | +| 设备兼容 | 存量固件不支持签名/ACK;不同厂家协议差异 | 网关做协议适配层 + 能力协商(`capabilities`);保留口令模式开关;固件升级与网关改造解耦 | +| 双写/重复上报 | 迁移期新旧通道同时写;重试导致重复账务 | 迁移期用“影子上报”(只写不生效);幂等键 + 唯一约束兜底;账务类接口强制幂等 | +| 旧系统兼容 | 旧值班台、旧报表、旧终端依赖旧接口 | 网关级兼容层 + 调用量盘点 + 按公寓灰度下线 | +| 迁移回滚 | 新端上线后叫乘异常,需快速回退 | 数据只读双跑 → 按公寓粒度的切换开关 → 保留旧实例一个周期;回滚脚本与演练纳入验收 | +| 状态机多写 | 应用端与设备同时修改 `ryzt` 引发脏写 | 状态机唯一入口 + 条件更新(沿用 `triggerLeaveAlarmIfPending` 思路并推广) | +| 时钟/时区 | 叫乘精确到秒,偏差导致误叫/漏叫 | NTP 强约束 + `serverTime` 下发 + 偏差告警 + 三时间戳 | +| 单实例约束被忽视 | 公寓端为高可用随手扩容,导致 WebSocket/Quartz 重复执行 | 部署规范明确“公寓端单实例”;若必须扩容,先做推送广播 + Quartz 持久化 + 任务抢占 | +| 权限体系冲突 | 平台 `GL_*` 与 RuoYi `sys_*` 并存导致账号混乱 | 两套 RBAC 各自管理,用“人员—账号映射表”打通;值班员登录只走公寓端 | +| 构建基线不稳 | `pom.xml` 版本错配导致新工程无法编译验证 | 阶段零修复依赖并建立 CI,先于一切拆分动作 | +| 安全暴露 | 测试接口、明文口令、全局设备口令 | 生产关闭 `/test`、`/jcgl/yatest`、`/jcgl/driver/*`;口令外置轮换;设备改一机一密 | + +### 10.2 待决策点 + +表 10-2 待决策点与建议 + +| # | 决策点 | 建议 | +| --- | --- | --- | +| D1 | 餐饮归属 | 业务平台化;扣款走账务接口,禁止公寓端直接改余额(当前 `MealsService` 直改余额必须改) | +| D2 | 余额主账位置 | 平台主账 + 公寓缓存;离线消费允许本地记账并上报 | +| D3 | 房间主数据归属 | 平台存基线(`gyid + roomNo + 床位数`),物理属性(楼层/朝向/SIP)公寓维护并上报 | +| D4 | 运安接入位置 | 只在平台端接入;公寓端不再持有 WSDL(同时解除“通辽南”硬编码的多公寓障碍) | +| D5 | 是否保留“一实例多公寓” | 目标架构下取消;存量多公寓共享库(`tl`/`sjt` 共用库)在阶段四拆分 | +| D6 | 应用端是否独立前端工程 | 与公寓端同一工程双入口,按角色裁剪路由与菜单 | +| D7 | 交路人工维护落点 | 平台维护模板,公寓可“应急新增”并上报确认 | +| D8 | 叫乘时间以谁为准 | 平台下发“计划 + 规则版本”,公寓端复算执行,不一致告警 | +| D9 | Oracle 存量公寓 | 明确保留期与迁移批次,避免新端并行维护两套 SQL | +| D10 | `kcgl` 库存边界 | 独立处置,不进入公寓端 | + +--- + +## 11. 结论与优先级 + +### 11.1 总体结论 + +1. 拆分方向可行,且现状已有雏形。`centerServerUrl` 的双向调用、`getApartmentPersonData*` / `synFinanceData`、`GY_SYN_JCXX_STATUS` 状态位,本质是“平台—公寓”关系未完成的形态;本次重构是把隐式关系显式化,而非推倒重建。 +2. 最大障碍不是技术选型,而是三件事:公寓身份(IP 推断 → 部署绑定)、数据归属(共享库 → 库隔离)、状态写入口分散(多写 → 单一状态机)。 +3. 公寓端用 RuoYi 新建是合理的。设备网关、执行引擎、上报器、值班 API 天然属于“管理后台 + 本地接入”形态,可复用 RBAC、定时任务、代码生成;但必须约定“RuoYi 管本地账号、平台管全局账号”,否则权限体系会打架。 +4. 应用端不独立是正确的。值班员的全部操作都必须落在公寓本地事实库上,独立服务只会带来跨服务事务与延迟;同代码同库 + 模块边界 + 状态机单入口足以支撑。 +5. 必须先做阶段零。`pom.xml` 依赖错配会让任何新工程无法建立可信基线,明文口令入库是安全红线;这两项不解决,拆分过程无法验收。 + +### 11.2 推荐落地顺序 + +表 11-1 优先级与落地顺序 + +| 优先级 | 事项 | 理由 | +| --- | --- | --- | +| P0-1 | 修复 `pom.xml` 版本错配;建立编译/单测/CI 基线 | 没有基线就没有“拆分正确性” | +| P0-2 | 口令与内网地址外置、轮换;关闭测试接口 | 安全红线,改造成本低 | +| P0-3 | 产出表归属矩阵 + 接口归属清单 + `JcglService` 职责地图 | 后续所有设计的输入 | +| P1-1 | 17 个跨端接口契约 + 上报信封 + outbox 模型 | 决定拆分能否落地 | +| P1-2 | 公寓端设备网关(一机一密 + 指令 ACK + 离线队列) | 现场稳定性收益最直接 | +| P1-3 | 叫乘状态机单一入口 + 应用端盯控台(基于 `JcXx.vue` 抽取) | 降低同库后脏写风险 | +| P2-1 | 账务:平台主账 + 公寓流水 + 日对账;餐饮改走账务接口 | 数据一致性风险最高的一环 | +| P2-2 | 运安接入上收平台,公寓端仅消费计划 | 解除多公寓匹配限制 | +| P2-3 | 单公寓试点 → 多公寓推广 → 旧逻辑下线与回滚演练 | 控制现场风险 | + +### 11.3 后续动作 + +1. 以第 1、4、5 章为模板,产出《表归属矩阵》与《接口归属清单》两份工作底稿。 +2. 以第 6 章为蓝本,先冻结 v1 契约(含错误码与幂等语义),再进入任何编码。 +3. 以第 3.3 节为基础,明确“值班员可写字段白名单”,作为 Code Review 检查项。 +4. 本文落盘后,在 `docs/README.md` 与 `docs/09-迭代指引与风险清单.md` 中建立索引。 + +--- + +## 附录 A 术语表 + +表 A-1 术语表 + +| 术语 | 说明 | +| --- | --- | +| 平台端 | 提供主数据、运安计划、账务主账、餐饮、跨公寓报表与审计的中心服务 | +| 公寓端 | 部署在某公寓现场、负责设备接入与叫乘执行的本地服务(基于 RuoYi 新建) | +| 应用端 | 与公寓端同代码同库、面向值班员的盯控与操作界面,不是独立服务或独立库 | +| 执行域 / 操作域 | 同一库内的两类数据与模块:执行域由执行引擎与设备网关写;操作域记录人工干预与审核 | +| 水位(watermark) | 公寓端上报序号,用于判断漏报、乱序与对账进度 | +| 幂等键 | 同一业务对象同一版本仅生效一次的去重标识 | +| 快照版本 | 下行主数据/规则的版本号,公寓端据此判断是否过期 | +| 影子上报 | 迁移期只上报不生效的写入通道,用于验证新链路而不改变业务结果 | + +## 附录 B 图表清单 + +表 B-1 图表索引 + +| 编号 | 名称 | 位置 | +| --- | --- | --- | +| 表 0-1 | 文档信息 | 0 章 | +| 表 0-2 | 修订记录 | 0 章 | +| 表 1-1 | 现状事实与拆分影响 | 1.1 | +| 表 1-2 | 主要模块定位与目标归属 | 1.2 | +| 图 2-1 | 目标逻辑架构 | 2.1 | +| 图 2-2 | 部署拓扑 | 2.2 | +| 表 2-1 | 三端调用关系与数据流 | 2.3 | +| 表 3-1 | 应用端与公寓端的逻辑分工 | 3.3 | +| 表 4-1 | 能力归属决策表 | 4.1 | +| 表 4-2 | 平台端模块清单 | 4.2 | +| 表 4-3 | 公寓端模块清单 | 4.3 | +| 表 5-1 | 数据分层、归属与同步方向 | 5.1 | +| 表 5-2 | 各场景一致性策略 | 5.2 | +| 表 6-1 | 平台端与公寓端关键接口 | 6.1 | +| 表 6-2 | 跨端可靠性机制 | 6.3 | +| 表 7-1 | 设备认证阶段设计 | 7.2 | +| 表 7-2 | 心跳、指令与状态采集 | 7.3 | +| 表 7-3 | 设备域数据上报与不上报 | 7.6 | +| 表 8-1 | 三端部署形态 | 8.1 | +| 表 8-2 | 非功能要求与实现要点 | 8.2 | +| 图 9-1 | 实施路径 | 9 章 | +| 表 9-1 ~ 9-6 | 各阶段目标/产出/验收/风险 | 9 章 | +| 表 10-1 | 风险与应对 | 10.1 | +| 表 10-2 | 待决策点与建议 | 10.2 | +| 表 11-1 | 优先级与落地顺序 | 11.2 | + +## 附录 C 代码证据索引 + +表 C-1 本文引用证据的代码位置 + +| 证据 | 位置 | 用途 | +| --- | --- | --- | +| 注册转发中心 | `jcgl/service/JcglService.java`(`centerServerUrl + /yktgl/regist/addPersonInfo`) | 证明平台/公寓同源、需切接口契约 | +| 交路从中心拉取 | `jlgl/service/JlGlService.java`(`getMessageFromGy`) | 证明已有下行雏形 | +| IP→公寓解析 | `zdwh/service/ZdwhService.java`(`getGyIdByIp` / `getUniqueGyByIp`) | 公寓身份改造对象 | +| 设备指令一次性投递 | `heartBeat/service/HeartBeatService.java` | 指令可靠性与 ACK 改造依据 | +| 设备全局口令 | 6 个 `*Device` 服务中的 `baseConfig.devicePassword` | 一机一密改造依据 | +| 叫乘调度 | `quartz/SchedulerManager.java`、`quartz/WakeUpJob.java`、`schedule/WakeupJobRecoveryRunner.java` | 公寓端执行引擎范围 | +| 定时任务混合 | `schedule/JcSchedule.java`、`schedule/SynDataSchedule.java` | 任务上收/下沉划分依据 | +| WebSocket 进程内实现 | `websockets/JcWebsocket.java` | 单实例约束与推送改造依据 | +| 餐饮改余额 | `MealsOrdering/service/MealsService.java`(`updateMoney`) | 餐饮与账务解耦依据 | +| 中心侧汇总接口 | `syndata/controller/SynPersonDataController.java`(`getApartmentPersonDataByGyCode` / `ByWorkNo` / `ByTime`、`synFinanceData`) | 平台化接口的现状参照 | +| 设备业务协议 | `callingDevice` / `checkInDevice` / `roomDevice` / `payDevice` / `registDevice` / `vendPayDevice` | 网关收敛范围 | +| 前端部署硬编码 | `YktVue/public/config.js`、`YktVue/vue.config.js` | 双入口前端与公寓绑定的依据 | +| 值班主页面 | `YktVue/src/views/JcXx.vue` | 应用端拆分骨架 | +| 构建依赖错配 | `yktgl/pom.xml` | 阶段零修复项 | +| 多公寓部署差异 | `yktgl/src/main/resources/application-*.yml` | 库隔离与 Profile 收敛依据 | + +## 附录 D 文档维护约定 + +1. 本文为设计方案,实施过程中若与代码现状出现偏差,以代码为准并同步更新本文,不得反向以文档为借口修改既有约定。 +2. 新增/调整端职责、模块划分、跨端接口时,同步更新第 3、4、6 章与附录 B 的图表清单。 +3. 每个迁移阶段结束,在第 9 章对应阶段表下追加“实际完成情况”与偏差说明。 +4. 决策点在 10.2 定稿后,从“待决策”移入正文对应章节,并保留决策时间与决策人。 + diff --git a/docs/11-既有数据库结构分析.md b/docs/11-既有数据库结构分析.md new file mode 100644 index 0000000..7dab163 --- /dev/null +++ b/docs/11-既有数据库结构分析.md @@ -0,0 +1,451 @@ +# 11 既有数据库结构分析(KingbaseES) + +> 文档性质:现状实证分析(Analysis Only)。本文所有结论来自对 `db/gygl.sql` 的结构解析与对已导入实例的只读查询(全部为 `SELECT`),**不含**数据库变更、DDL 执行、配置修改。改造建议仅为设计输入,需评审后另行排期。 + +## 0. 文档说明 + +表 0-1 采集信息 + +| 项 | 内容 | +| --- | --- | +| 数据来源 | `db/gygl.sql`(Navicat 导出,源库 `10.33.165.41:54321/gygl`,导出时间 2026-11-07 14:34) | +| 校验实例 | 本机 KingbaseES 导入库,`jdbc:kingbase8://127.0.0.1:54321/gygl` | +| 采集方式 | JDBC 只读查询(information_schema / pg_catalog / pg_stat_user_tables)+ dump 文本解析 | +| 库与 schema | 数据库 `gygl`,业务 schema `gygl`,表 owner 均为 `system` | +| 规模 | 40 张表、524 个字段、schema 总容量 117 MB、495,772 行数据 | +| 关联文档 | `05-数据模型.md`(表语义与状态码)、`09-迭代指引与风险清单.md`(技术债)、`10-架构优化方案-三端拆分.md`(目标归属) | + +> 样本口径提醒:本机实例的数据是导出快照,业务数据只覆盖 2 个公寓(见 3.9)。行数、分布用于判断**结构缺陷**,不作为生产容量基线;容量测算需在生产库重跑附录 A 的 SQL。 + +## 1. 实例与库级特征 + +表 1-1 库级参数(实测) + +| 参数 | 实测值 | 结构影响 | +| --- | --- | --- | +| 版本 | `KingbaseES V009R001C010`(`server_version` 报 12.1) | 兼容 PG 12 协议;JDBC 9.0.0 驱动可直连 | +| 兼容模式 | `database_mode = oracle` | 出现 `sys.date`、`sys_guid()`、`sysdate()`、`dual` 表等 Oracle 兼容对象 | +| 空串语义 | `ora_input_emptystr_isnull = on` | **写入 `""` 存成 `NULL`**,读取恒为 `NULL`;见 3.4 | +| 字符集 | `UTF8` | 中文数据无乱码风险 | +| MAC 扩展 | schema `sysmac`(8 张表,248 kB) | 金仓安全版强制访问控制扩展;上线前需确认是否启用策略 | +| 对象种类 | 视图 0、触发器 0、函数 0、分区表 0 | **业务逻辑全部在 Java 侧**,库内无任何约束性逻辑 | + +表 1-2 schema 分布 + +| schema | 表数 | 容量 | 说明 | +| --- | --- | --- | --- | +| `gygl` | 40 | 117 MB | 全部业务表 | +| `sysmac` | 8 | 248 kB | 金仓安全版 MAC 元数据(空表) | +| `sys_hm` | 3 | 128 kB | 金仓自带 | +| `sys` | 1 | 40 kB | 金仓自带 | + +结论:**这是一个"纯存储库"**——没有主键、没有索引、没有外键、没有触发器、没有存储过程。数据完整性、唯一性、顺序性、并发控制全部由应用代码承担,这也是下一章几乎所有缺陷的共同根因。 + +## 2. 表清单与规模 + +图 2-1 数据域与表簇 + +```mermaid +graph LR + A[公寓主数据
gy_jbxx / gy_gyzd / gy_sjgz / gy_jcsj / gy_jrb] --> B[房间
gy_rooms] + B --> C[叫乘执行
gy_jcxx / gy_jcxx_his
gy_bjxx / gy_bjcstj] + D[交路同步
gy_syn_jcxx / _his / _status] --> C + E[人员与卡
gy_ykt_ry / gy_ykt_ry_zp] --> C + F[账务
gy_ykt_remainder / _allowance / _consume / _btbj / _btjs] --> E + G[设备
gy_sb / gy_device / gy_sb_xt / gy_sb_zl / _his] --> C + H[权限
gl_yh / gl_js / gl_qx / gl_cd / gl_yh_js / gl_js_qx] + I[日志
gy_logs / gy_operation_log] +``` + +表 2-1 业务表清单(按数据域) + +| 表 | 行数 | 大小 | 列数 | 含 gyid | 现职责 | +| --- | --- | --- | --- | --- | --- | +| `gy_jbxx` | 9 | 16 kB | 26 | ✔ | 公寓基础信息(面积、床位、负责人、热水/燃气方式) | +| `gy_gyzd` | 37 | 16 kB | 8 | ✔ | 公寓字典(网关地址、机器号、IP) | +| `gy_gzzd` | 17 | 16 kB | 3 | — | 工种字典 + 客货类型 | +| `gy_sjgz` | 17 | 16 kB | 10 | ✔ | 时间规则(入住/离寓/叫乘基准) | +| `gy_jcsj` | 7 | 16 kB | 3 | — | 叫乘时刻表(`cb/kssj/jssj`,均为字符) | +| `gy_jrb` | 2 | 16 kB | 6 | — | 节假日 | +| `gy_rooms` | 197 | 64 kB | 22 | ✔ | 房间基线 + 房态 + 分机 + 报警起点(三类语义混装) | +| `gy_jcxx` | 3,541 | 1.5 MB | **70** | ✔ | 叫乘执行主表(上帝表,见 3.5) | +| `gy_jcxx_his` | 32,505 | 12 MB | **70** | ✔ | 叫乘归档表 | +| `gy_bjxx` | 2,490 | 304 kB | 7 | ✔ | 报警记录(开始/结束时间) | +| `gy_bjcstj` | 0 | 8 kB | 12 | — | 补叫统计(**未使用**) | +| `gy_jdqkhz` | 1 | 16 kB | 9 | ✔ | 房间年度/月度汇总(近似废表) | +| `gy_rtjb` | 0 | 8 kB | 7 | ✔ | 日报(**未使用**) | +| `gy_call_duty_match_lock` | 38 | 8 kB | 1 | ✔ | 单列行锁表,用 `gyid` 做悲观锁 | +| `gy_syn_jcxx` | 1,139 | 384 kB | 15 | — | 运安交路快照 | +| `gy_syn_jcxx_his` | 2,291 | 376 kB | 12 | — | 交路归档 | +| `gy_syn_jcxx_status` | 1 | 8 kB | 2 | — | 同步状态/水位(单行开关) | +| `gy_ykt_ry` | 1,349 | 336 kB | 27 | — | 人员与卡主数据 | +| `gy_ykt_ry_zp` | 100 | **27 MB** | 2 | — | 照片(BLOB 存库) | +| `gy_ykt_remainder` | 1,253 | 224 kB | 12 | — | 余额(补助/现金/消费/补助总额) | +| `gy_ykt_allowance` | 34,182 | 4.2 MB | 11 | ✔ | 补助发放流水 | +| `gy_ykt_consume` | 35 | 16 kB | 19 | ✔ | 消费流水 | +| `gy_ykt_btbj` | 5 | 16 kB | 8 | ✔ | 补助标准(早/正/节假日) | +| `gy_ykt_btjs` | 5 | 16 kB | 7 | — | 补助计算规则 | +| `gy_sb` | 148 | 48 kB | 8 | ✔ | 设备台账 v1(`devicecode/lx`) | +| `gy_device` | 92 | 48 kB | 18 | ✔ | 设备台账 v2(`device_code/device_type`,RuoYi 风格) | +| `gy_sb_xt` | 246 | 64 kB | 9 | ✔ | 设备心跳与版本状态 | +| `gy_sb_zl` | **129,665** | 11 MB | 6 | — | 设备指令队列(**与历史同表**) | +| `gy_sb_zl_his` | 0 | 8 kB | 6 | — | 指令历史(**从未使用**) | +| `gy_logs` | **285,914** | **56 MB** | 7 | ✔ | 运行日志(无清理策略) | +| `gy_operation_log` | 0 | 8 kB | 6 | — | 操作审计(**从未写入**) | +| `gy_file` | 1 | 16 kB | 6 | — | 文件登记 | + +表 2-2 支撑表清单 + +| 表 | 行数 | 列数 | 说明 | +| --- | --- | --- | --- | +| `gl_yh` / `gl_js` / `gl_qx` / `gl_cd` / `gl_yh_js` / `gl_js_qx` | 17 / 4 / 48 / 45 / 16 / 58 | 18 / 6 / 9 / 9 / 2 / 2 | 自研 RBAC(用户/角色/权限/菜单及关联) | +| `gen_table` / `gen_table_column` | 18 / 279 | 21 / 22 | **RuoYi 代码生成器元数据残留** | + +容量结构:`gy_logs` 占 48%、`gy_ykt_ry_zp` 占 23%、`gy_jcxx_his` 占 10%、`gy_sb_zl` 占 9%——四张表吃掉全库 90%。其中两张(日志、指令)是纯追加、无清理的表。 + +## 3. 结构缺陷诊断 + +表 3-1 约束与索引现状(全库实测) + +| 指标 | 数量 | 说明 | +| --- | --- | --- | +| 主键 | **0** | 40 张表无一有主键 | +| 唯一约束 | **0** | 唯一性完全依赖应用层判断 | +| 外键 | **0** | 关联完整性靠代码,无 DB 兜底 | +| 索引 | **0** | 含隐式主键索引在内,`pg_indexes` 计数为 0 | +| 序列 | 64 | 仅 4 个被列默认值引用,其余 60 个为**孤儿序列** | +| 表注释 | 3/40 | 仅 `gen_table`、`gen_table_column`、`gy_device` 有 | +| 列注释 | 60/524 | 其中仅 5 条属于业务表(`gy_jcxx` 3 条、`gy_syn_jcxx` 1 条、`gy_gzzd` 1 条) | +| 非空约束 | 5 处 | 仅 `gy_ykt_ry.ryid`、`gy_call_duty_match_lock.gyid`、`gy_syn_jcxx_status.id/sync_status`、`gy_device.device_code` | + +### 3.1 无主键、无索引的连锁后果 + +1. **每次按 `devicecode` / `ryid` / `jcid` 查询都是全表扫描**。`gy_sb_zl` 12.9 万行、`gy_logs` 28.6 万行,心跳与指令链路是高频路径,随运行时间线性劣化。 +2. **重复数据无法阻断**。实测 `gy_jcxx` 3,541 行中 `jcid` 只有 3,540 个不同值——已存在 1 组重复 `jcid`,且没有任何机制能阻止再次发生。 +3. **更新语义不可靠**。`update ... where jcid=?` 命中多行时,MyBatis 返回的影响行数与业务预期不符,容易被误判为"成功"。 +4. **分页与排序无稳定基准**。无主键导致同一秒内多行顺序不确定,翻页可能重复或漏行。 +5. **归档与对账无法定位单行**。`gy_jcxx` → `gy_jcxx_his` 的搬迁只能按业务键或不带条件批量搬,出错后难以精确回滚。 + +### 3.2 类型体系混乱 + +表 3-2 字段类型分布(524 列) + +| 类型 | 列数 | 占比 | 风险 | +| --- | --- | --- | --- | +| `varcharbyte`(金仓特有) | **276** | 53% | **全部未指定长度**(`character_maximum_length` 为 NULL),任何超长/脏数据都能写入 | +| `numeric` | 82 | 16% | 未指定精度标度,金额列与计数列同型,无精度约束 | +| `varchar` | 78 | 15% | 有长度,但风格与 `varcharbyte` 混用 | +| `timestamp` | 68 | 13% | 含 `Oracle DATE`(`sys.date`)映射,秒级精度 | +| `bpchar` | 11 | 2% | 定长字符(`'0'/'1'` 标记) | +| `text` / `int8` / `int4` / `bytea` | 9 | 2% | LOB(`nclob`/`blob`)、自增主键 | + +**同一字段名在不同表里类型不一致,共 12 组**: + +| 字段 | 类型变体 | 涉及表数 | 直接后果 | +| --- | --- | --- | --- | +| `id` | `numeric` / `varchar` / `varcharbyte` | 14 | 跨表 `join`、`union` 需隐式转换;索引即便补上也难生效 | +| `deleted` | `numeric` / `varcharbyte` | 4 | 逻辑删除写法不统一(`=0` 与 `='0'` 混用) | +| `gyid` | `varchar(64)` / `varcharbyte` | 17 | 长度与比较语义不一致,`gyid='TLXC'` 在不同表可能命中/不命中 | +| `kssj` / `jssj` | `timestamp` / `varcharbyte` | 3 / 3 | 同一含义的时间一半能比较、一半是字符串 | +| `sort` | `int4` / `numeric` | 4 | — | +| `lx` / `cb` / `name` / `dwmc` / `room_no` / `table_id` | 各 2 种 | — | — | + +### 3.3 时间字段三种存法并存 + +| 存法 | 示例 | 问题 | +| --- | --- | --- | +| `timestamp`(含 `sys.date`) | `kcsj`、`jcsj`、`fjsj`、`sjrysj`、`scbzsj` | 正常,但秒级精度、无时区字段 | +| **字符串** `varcharbyte` | `gy_jcxx.yrysj`(预期入住时间)、`gy_jcxx.ddsj`、`gy_jcsj.kssj/jssj`、`gy_jbxx.zrsj/sjsj` | 无法做范围比较、无法排序、格式靠约定;共 13 处 | +| 冗余列并存 | `jcsj` 与 `jcsj_s`、`fjsj` 与 `fjsj_s`、`jcbjsj` 与 `jcbjsj_s` | 同一时刻两列,靠代码保证同步,无约束 | + +### 3.4 Oracle 空串语义(高危) + +实例参数 `ora_input_emptystr_isnull = on`,实测验证: + +- `select count(*) from gy_jcxx where bz = ''` → **0** +- `select count(*) from gy_jcxx where bz is null` → **3541**(全表) + +含义:业务代码写 `""` 入库后变成 `NULL`;反过来读出的 `NULL` 在 Java 侧可能是 `null` 也可能是 `""`,取决于取值方式。表现为:前端显示`undefined`、`StringUtils.isBlank` 与 `!= null` 判断在不同层得到不同结论、`where col <> ''` 恒不命中。三端拆分后跨库/跨端传输时这个语义差异会被放大。 + +### 3.5 `gy_jcxx` 上帝表 + +单表 **70 列**,同时承载 7 类语义: + +| 语义组 | 代表列 | +| --- | --- | +| 车次与交路 | `jch`、`cc`、`cdw`、`kcsj`、`jlid`、`cclx`、`qd`、`ddcc`、`depotid` | +| 叫乘状态机 | `ryzt`、`jcsj`、`fjsj`、`jcbjsj`、`sjrysj`、`lybjsj`、`scbzsj` | +| 入住/离寓 | `yrysj`、`sjrysj`、`ylysj`、`sjlysj`、`flag_send`、`tbsj_gytofcd`、`tbsj_fcdtogy` | +| 补助与餐饮 | `cblx`、`cbje`、`sfffcb`、`dayallowance`、`is_meal3`、`isoverday` | +| 服务评分 | `kffwpf`、`stfwpf`、`jcfwpf`、`zm` | +| 报警与提醒去重 | `is_yaxx`、`is_jcbj`、`is_lybj`、`route_notice_key`、`call_timeout_notice_key`、`missed_call_notice_key` | +| 设备与录音 | `finish_sb`、`recordfile`、`hjxysj`、`fjhold` | + +后 8 列(`business_id`、`ly_type`、`ly_event_id`、`ly_remark`、`ly_snapshot_url`、`route_notice_key`、`call_timeout_notice_key`、`missed_call_notice_key`)明显是后期追加,且 `ly_*` 属于"离寓事件"子域、`*_notice_key` 属于"通知去重"子域——表已演化为多子域的物理合并点。`gy_jcxx_his` 同步复制了这 70 列(但 `yappsj` 类型从 `sys.date` 漂移为 `timestamp`),归档表与主表已出现结构漂移。 + +### 3.6 设备台账双轨 + +表 3-4 两套设备表 + +| 项 | `gy_sb`(v1) | `gy_device`(v2) | +| --- | --- | --- | +| 行数 | 148 | 92 | +| 命名风格 | 全小写无下划线:`devicecode`/`lx`/`fjh` | 下划线 + RuoYi 审计列:`device_code`/`device_type`/`create_by` | +| 类型编码 | `lx`:3=134、1=4、5=3、6=3、2=2、空=2 | `device_type`:3=80、1=3、5=3、6=3、99=2、2=1 | +| 附加字段 | `gxsj`、`gymc` | `online_status`、`last_heartbeat`、`firmware_version`、`del_flag` | +| 关系 | — | `device_code` 与 `gy_sb.devicecode` **交集 92 行 = 完全子集** | + +即:v2 的 92 个 `device_code` **全部能在 v1 中找到**(交集恰为 92),而 v1 仍有 148 行未被 v2 覆盖。两张表**同时在线、同时被读**,无外键、无同步机制。设备在线率、版本、故障统计存在两个数据源,"当前是否在线"取决于读哪张表。 + +### 3.7 指令队列与历史同表,且积压 + +表 3-5 `gy_sb_zl` 实测分布 + +| `deleted` | 行数 | 涉及设备数 | 时间范围 | +| --- | --- | --- | --- | +| `0`(未消费/未删除) | 72,088 | 96 | 2025-08-26 ~ 2026-11-07 | +| `1`(已消费) | 57,577 | 172 | 2025-08-26 ~ 2026-11-07 | + +其中 **`deleted='0'` 且生成时间超过 1 天的记录有 71,723 条**——占未消费量的 99.5%。这说明:"取最新一条 → 置 `deleted=1`"的消费方式(见 `HeartBeatService`/设备服务)在多数情况下没有闭环:要么设备从未取走,要么取了没回写成功。同时该表 12.9 万行、零索引,每次取指令都是全表扫描 + 排序。 + +### 3.8 日志表无清理 + +`gy_logs` 28.6 万行 / 56 MB,`eventtime` 跨度 2025-05 ~ 2026-11。按 `lx` 分布: + +| `lx` | 行数 | +| --- | --- | +| 运安叫乘同步汇总 | 238,386 | +| 刷脸离寓事件号缺失 / 请求 / 完成 / 抓拍缺失 | 7,655 / 7,655 / 7,649 / 7,649 | +| 运安叫乘同步跳过 | 4,472 | +| 运安定时抓取完成 | 3,470 | +| 自动叫乘任务未创建 | 2,369 | +| 叫乘时间延后 | 2,258 | +| 注册机心跳异常 | 1,797 | +| 超时未叫乘提醒 | 857 | +| 其余(匹配异常、登录、启动恢复等) | ~1,400 | + +按月看,"运安叫乘同步汇总"稳定在 **每月约 4.4 万条**(2026-05 ~ 09 均在 40,980 ~ 44,640 之间)。这是"每 5 分钟 × 每公寓写一条汇总日志"的直接结果——**一条业务无异常的流水日志,占了全库 87% 的日志行数**。该表无归档、无分区、无索引,年增约 53 万行。 + +### 3.9 主数据与租户键(`gyid`)不一致 + +17 张表带 `gyid`,但各表的取值域互不匹配: + +| 观测点 | 实测值 | +| --- | --- | +| `gy_jbxx`(公寓基础信息) | **9** 个:SYXC、SYNDC、SJTXC、SYXXC、DDXC、DSQXC、BYQXC、DLXC、DLBDC | +| `gy_call_duty_match_lock`(行锁表) | **37** 个业务 gyid + 1 个伪 gyid `__SYNC_JCXX__` | +| `gy_rooms`(房间) | **6** 个:TLXC 102、THXC 56、PJXC 35、TLNXC 2、CCXDC 1、**空 1** | +| `gy_jcxx`(叫乘) | **2** 个:THXC 2,110、TLXC 1,431 | +| `gy_sb.lx` 为空 / `gy_rooms.gyid` 为空 | 各存在 1~2 行 | + +三个结论: +1. **主数据严重缺失**:系统实际服务的 gyid 至少 37 个,但只有 9 个在 `gy_jbxx` 有基础信息,31 个公寓缺面积/床位/负责人/时间规则来源。 +2. **租户键可空**:`gy_rooms` 有 1 行 `gyid` 为 `NULL`(很可能是代码写入 `""` 后被 Oracle 空串语义转成 `NULL`),这类行在按租户过滤时会被全局漏掉或全局命中。 +3. **行锁表用 `gyid` 当锁键**并塞入 `__SYNC_JCXX__` 这种伪租户,说明"公寓"与"同步任务"两个概念共用了一张锁表——三端拆分后必须把锁的粒度从"字符串键"改为"业务对象 + 版本"。 + +> 样本库只覆盖 THXC / TLXC 两个公寓的叫乘数据,属于导出时的数据裁剪(或该公寓数据被迁移过),不代表生产分布;但 `gyid` 的**取值域不一致**是结构性问题,与数据量无关。 + +### 3.10 RuoYi 残留与孤儿序列 + +- `gen_table`(18 行)、`gen_table_column`(279 行)是 RuoYi 代码生成器的元数据表,业务上完全不用,却占据 40 张表中的 2 张。 +- 64 个序列中只有 4 个被列默认值引用:`gen_table_table_id_seq`、`gen_table_column_column_id_seq`、`gy_device_device_id_seq`、`seq_gy_allowance`。其余 60 个指向**已不存在的表**:`sys_user_*`、`sys_role_*`、`sys_menu_*`、`sys_dept_*`、`sys_job_*`、`t_oa_*`、`t_role_*`、`ykt_account_id_seq`、`ykt_consume_flow_id_seq`、`gygl_consumer_terminal_device_id_seq` 等。 + +这组序列是"库曾经跑过一版 RuoYi/其他业务系统、后来表被删但序列没删"的直接证据。对三端拆分的意义:**新公寓端选 RuoYi 并不是引入新范式,而是回到这条被中断过的路线上**,因此必须明确"沿用还是重建"权限表(现状 `gl_*` 是自研 RBAC,与 RuoYi `sys_*` 完全不同构)。 + +### 3.11 照片以 BLOB 入库 + +`gy_ykt_ry_zp`:100 行 / 27 MB / 平均 281.3 KB / 最大 547,778 字节,列类型 `blob`(`bytea`)。同时 `gy_ykt_ry.photourl` 又存了一份路径,代码里还有按 `serverOS` 硬编码的文件目录(`C:\project_doc\resouce\photo\` 与 `/home/gygl/resouce/photo/`)。**同一张照片有"库内 BLOB + 本地磁盘路径"两条真相**,且备份策略完全不同(逻辑备份含 BLOB,磁盘目录不在备份范围)。 + +### 3.12 审计与统计表空置 + +`gy_operation_log`(操作审计)0 行、`gy_bjcstj`(补叫统计)0 行、`gy_rtjb`(日报)0 行、`gy_sb_zl_his`(指令历史)0 行。四张表有结构无数据——**设计了但没有接线**。其中操作审计的缺失,与"值班员可改叫乘状态但无留痕"的风险直接相关。 + +## 4. 数据质量实证 + +表 4-1 抽样核对结果 + +| 检查项 | 结果 | 判读 | +| --- | --- | --- | +| `gy_jcxx` 主键候选 `jcid` | 3,541 行 / 3,540 个不同值 | 存在 1 组重复,无约束可阻断 | +| `gy_jcxx.kcsj` 为空 | 367 / 3,541(10.4%) | 开车时间缺失仍可入库,下游按时间排序/提醒会漏 | +| `gy_jcxx.ryzt` 分布 | 3=2,373、0=582、5=225、4=171、2=134、1=56 | `-1` 未出现;状态分布偏向"已完成/在寓",缺失败态样本 | +| `gy_jcxx.jllx` 分布 | 1=2,224、0=1,317 | 两值区分,但字段无注释、无字典表 | +| `gy_ykt_remainder` | 1,253 行 / 1,253 个 `ryid`,无重复 | 当前无重复,但无唯一约束保障 | +| `gy_rooms` 房态分布 | 3=68、1=58、4=32、2=32、5=7 | `room_status=6` 从未出现(字典有 6 值,实际只用 5 值) | +| `gy_sb_xt` 心跳 | 246 行,`updatestate` 全为 0 | 更新状态位从未被置位,与 3.7 的指令积压互相印证 | +| `gy_sb_zl.id` / `gy_logs.id` | 12.9 万 / 28.6 万,全部唯一 | 应用层 UUID 生成正确,纯靠代码保证 | + +账务三表口径无法自洽(实测合计): + +| 表 | 口径 | 合计 | +| --- | --- | --- | +| `gy_ykt_allowance` | 补助发放流水 | 552,295.00 元 | +| `gy_ykt_consume` | 消费流水 | 265.10 元 | +| `gy_ykt_remainder` | 当前余额(补助/现金/消费/补助总额) | 补助余额 1,228.00 元,现金余额 0.00 元 | + +补助累计 55.2 万元的规模,与仅 265 元的消费流水、1,228 元的当前余额之间,**缺少任何可对账的中间口径**(无期初、无结转、无冲正、无对账批次)。这不能简单判定为"数据错误",但可以确定:**当前结构下无法证明账是对的**。 + +## 5. 表归属矩阵(对接文档 10 第 5 章) + +表 5-1 40 张表的目标归属与改造动作 + +| 表 | 行数 | 目标端 | 改造动作 | +| --- | --- | --- | --- | +| `gy_jbxx` | 9 | 平台 | 升为公寓主数据,补全 37 个 gyid,版本化下发 | +| `gy_gyzd` | 37 | 平台 | 主数据(网关/机器号/IP),下发只读 | +| `gy_gzzd` | 17 | 平台 | 主数据(工种/客货类型) | +| `gy_sjgz` | 17 | 平台 | 时间规则收敛为平台唯一口径,下发 | +| `gy_jcsj` | 7 | 平台 | 计划数据 | +| `gy_jrb` | 2 | 平台 | 主数据 | +| `gy_rooms` | 197 | 平台 + 公寓 + 值班 | **拆三份**:房间基线(平台)/ 房态(公寓)/ 分机与报警起点(公寓) | +| `gy_jcxx` | 3,541 | 公寓 | **拆表**:叫乘任务 / 入住离寓 / 通知去重键,见 6.4 | +| `gy_jcxx_his` | 32,505 | 公寓 + 归档 | 按月分区;修正与主表的类型漂移 | +| `gy_bjxx` | 2,490 | 公寓 | 保留,补主键与 `gyid+时间` 索引 | +| `gy_bjcstj` | 0 | — | 退役(无数据无代码写入) | +| `gy_jdqkhz` | 1 | 平台 | 转报表视图或退役 | +| `gy_rtjb` | 0 | — | 退役 | +| `gy_call_duty_match_lock` | 38 | 公寓 | 换为行级锁表(含 `locked_at`/`owner`)或 DB advisory lock | +| `gy_syn_jcxx` | 1,139 | 平台 | 运安抓取上收平台,公寓只收下行计划 | +| `gy_syn_jcxx_his` | 2,291 | 平台 | 归档分区 | +| `gy_syn_jcxx_status` | 1 | 平台 | 改为 watermark 表(含 `last_success_at`/`cursor`) | +| `gy_ykt_ry` | 1,349 | 平台 | 人员/卡主数据,公寓本地缓存 | +| `gy_ykt_ry_zp` | 100 | 平台 | 迁对象存储,表只留 URL 与校验和 | +| `gy_ykt_remainder` | 1,253 | 平台主账 + 公寓缓存 | 双记:本地流水 + 平台主账,日对账 | +| `gy_ykt_allowance` | 34,182 | 平台 | 补助流水归平台,公寓不上报原始流水 | +| `gy_ykt_consume` | 35 | 平台(餐饮)/ 公寓(其他) | 按消费来源拆分归属 | +| `gy_ykt_btbj` | 5 | 平台 | 补助标准主数据 | +| `gy_ykt_btjs` | 5 | 平台 | 补助规则主数据 | +| `gy_sb` | 148 | 公寓 | **合并到 `gy_device`**,命名归一 | +| `gy_device` | 92 | 公寓 | 保留为唯一设备台账,补 `(gyid, device_code)` 唯一 | +| `gy_sb_xt` | 246 | 公寓 | 保留;心跳改 upsert,加 `last_heartbeat` 索引 | +| `gy_sb_zl` | 129,665 | 公寓 | **拆队列 / 历史**,见 6.4 | +| `gy_sb_zl_his` | 0 | 公寓 | 改分区表承接指令历史 | +| `gy_logs` | 285,914 | 公寓 | 按月分区 + 保留期;"同步汇总"降为采样 | +| `gy_operation_log` | 0 | 公寓 + 值班 | 启用,按端打标 | +| `gy_file` | 1 | 平台 | 对象存储索引 | +| `gl_*`(6 张) | 17/4/48/45/16/58 | 平台 | 统一账号与权限;公寓端沿用 RuoYi `sys_*` 或适配 `gl_*`,二者只能留一套 | +| `gen_table` / `gen_table_column` | 18 / 279 | — | 退役,目标库不携带 | + +## 6. 目标结构建议(设计输入,未执行) + +### 6.1 命名与边界 + +1. 保留 schema `gygl`,用**表名前缀**表达归属:`PLT_`(平台主数据/计划)、`APT_`(公寓执行事实)、`DUTY_`(值班操作)、`ARC_`(归档)。 +2. 现有 `gy_*` 表在迁移期保留原名,新增列/新表用新前缀,避免一次性重命名带来的全量改代码。 +3. 权限表二选一:若公寓端采用 RuoYi,则 `gl_*` 只作为平台端兼容视图存在,不再有第二份可写副本。 + +### 6.2 主键与索引基线 + +表 6-1 必补约束与索引(按优先级) + +| 表 | 建议主键 / 唯一 | 建议索引 | +| --- | --- | --- | +| `gy_jcxx` / `gy_jcxx_his` | `jcid` | `(gyid, kcsj)`、`(gyid, ryzt)` | +| `gy_rooms` | `(gyid, room_no)` | `(gyid, room_status)` | +| `gy_ykt_ry` | `ryid` | `(card_id)`、`(gzzh)` | +| `gy_ykt_remainder` | `ryid` | `(cardid)` | +| `gy_ykt_allowance` | `tid` | `(gyid, czsj)`、`(ryid, czsj)` | +| `gy_ykt_consume` | `tid` | `(gyid, xfsj)`、`(orderid)` | +| `gy_device` | `(gyid, device_code)` | `(ip_addr)`、`(last_heartbeat)` | +| `gy_sb_xt` | `devicecode` | `(gyid, heartbeattime)` | +| `gy_sb_zl` | `id` | `(devicecode, deleted, generatedtime)` —— 取指令的主路径 | +| `gy_logs` | `id` | `(eventtime)` + 按月分区 | +| `gy_bjxx` | `id` | `(gyid, kssj)` | +| `gy_syn_jcxx` | `id` | `(kcsj)`、`(jch, cc)` | +| `gy_jbxx` / `gy_gyzd` / `gy_sjgz` | `gyid` | — | +| `gl_yh` / `gl_js` / `gl_qx` / `gl_cd` | 各自首列 | 关联表 `(user_id)/(role_id)` | +| `gy_call_duty_match_lock` | `gyid` | — | + +### 6.3 类型归一规则 + +表 6-2 类型目标 + +| 现类型 | 目标 | 规则 | +| --- | --- | --- | +| `varcharbyte`(无长度) | `varchar(n)` | 按实测最大长度 + 30% 余量定 n;标识类统一 `varchar(32/64)` | +| 业务时间(`kssj`/`jssj`/`ddsj`/`yrysj`…) | `timestamp(3)` | 禁止再新增字符串时间;迁移时按 `YYYY-MM-DD HH24:MI:SS` 解析,解析失败行单独落错误表 | +| `sys.date` | `timestamp(0)` | 保持秒精度,避免与 `timestamp(6)` 混比 | +| 金额(`bzje`/`xfje`/`knye`/`xjye`/`xfze`/`bzze`) | `numeric(12,2)` | 统一两位小数,禁止 `numeric` 裸用 | +| 布尔标记(`deleted`/`sfjc`/`finish_*`/`is_*`/`stoped`) | `char(1)` + `CHECK IN ('0','1')` | 或 `boolean`;**同一字段名全库只允许一种表示** | +| `id`(`numeric` vs `varchar`) | `varchar(32)` 或 `bigint` | 每张表内自洽即可,但跨表引用必须同型 | +| `blob` 照片 | 移出库 | 表内只留 `photo_url` + `sha256` + `size` | + +### 6.4 拆表与归档 + +1. **`gy_jcxx` 三分**: + - `APT_JC_TASK`:任务与状态机(`jcid/ryid/jch/cc/kcsj/jcsj/fjsj/jcbjsj/ryzt`) + - `APT_JC_STAY`:入住离寓(`yrysj/sjrysj/ylysj/sjlysj/ly_*`/`flag_send`) + - `APT_JC_NOTICE`:通知去重键(`route_notice_key/call_timeout_notice_key/missed_call_notice_key`)→ 或并入通用 `NOTICE_KEY` 表 + - 物理上不再是 70 列宽表,写入口从"多处 update 同一行"变为"三个子域各自写自己的表",`JcglService` 的 4193 行才有可能拆开。 +2. **`gy_sb_zl` 拆队列/历史**:`APT_DEV_CMD`(只保留未消费,带 `status/attempt/expire_at`)+ `ARC_DEV_CMD`(消费即搬走),队列表行数应稳定在"在线设备数 × 个位数"量级,当前 7.1 万条积压即为异常基线。 +3. **`gy_logs` 分区**:按月 `RANGE` 分区,保留 3 个月热数据;"运安叫乘同步汇总"改为只记异常与抽样,预计日志量下降 90%。 +4. **`gy_jcxx_his` / `gy_syn_jcxx_his` / `gy_sb_zl_his`** 统一进 `ARC_` 分区族,保留期与合规要求对齐。 + +### 6.5 迁移时的兼容陷阱 + +1. `varcharbyte` 无长度 → 转 `varchar(n)` 前必须先跑 `select max(length(col))` 全量核对,否则截断报错会中断迁移。 +2. `ora_input_emptystr_isnull=on` → 迁移前后同一份数据 `''` 与 `NULL` 的判断结果不同;迁移脚本与校验 SQL 都要用 `coalesce(col,'')` 口径。 +3. `gen_table_column` 里记录的 `is_pk` 全为 RuoYi 生成器推断值,**不能当作主键依据**——实测全库主键为 0,该字段不代表真实约束。 +4. 60 个孤儿序列不要随库迁移,目标库从 0 重建,否则序列起始值会带来 ID 跳号与冲突排查成本。 +5. Oracle 兼容模式与金仓安全版 MAC 策略需在目标环境确认(`database_mode`、`sysmac` 是否启用),二者会改变 SQL 行为与权限模型。 + +## 7. 与三端拆分方案的衔接 + +表 7-1 结构缺陷 → 方案措施 + +| 本次实测缺陷 | 对应章节 | 方案措施 | +| --- | --- | --- | +| 零主键零索引零外键(3.1) | 阶段零 基线修复 | 先补 `(gyid, 业务键)` 唯一约束与查询索引,再谈拆分 | +| 同名不同型 12 组(3.2) | 5 数据架构 / 6 接口契约 | 契约层强制类型规范;`gyid` 统一 `varchar(64)` | +| 空串即 NULL(3.4) | 6 接口契约 | 上报信封中 `null` 与 `""` 语义显式化,服务端统一 `coalesce` | +| `gy_jcxx` 70 列(3.5) | 4 领域与模块划分 | 拆三子域,状态机唯一写入口 | +| 双设备台账(3.6) | 7 设备接入 | 设备主数据唯一化,`gy_device` 为准,`gy_sb` 合并 | +| 指令积压 7.1 万(3.7) | 7.4 指令下发-ACK-重投 | 队列化 + ACK + 过期作废 + 优先级 | +| 日志月增 4.4 万(3.8) | 8 部署与运行 | 分区 + 保留期 + 异常化 | +| `gyid` 取值域不一致(3.9) | 3 端职责边界 | 公寓主数据上收,租户键非空 + 外键校验 | +| RuoYi 残留与孤儿序列(3.10) | 阶段一 新公寓端脚手架 | 明确权限表归属,目标库不携带 `gen_*` 与孤儿序列 | +| 照片双真相(3.11) | 5.3 数据归属 | 统一对象存储 + 校验和 | +| 审计表空置(3.12) | 10 风险 | 值班操作留痕纳入验收项 | +| 账务不可对账(4 节) | 5.2 一致性策略 | 主账在平台 + 本地流水 + 日对账 | + +## 8. 待现场确认 + +1. **生产库是否真的没有索引**:本机导入库与 dump 均为 0,但需要在生产 DBA 侧执行 `\d+ gygl.gy_jcxx` 复核(导出工具理论上会带索引,需排除导出裁剪)。 +2. **37 个 gyid 与 9 个 `gy_jbxx` 的差集**:是"未登记"还是"已停用"?决定主数据补录范围。 +3. **样本库只有 2 个公寓的叫乘数据**:确认是导出裁剪、数据迁移,还是这两个公寓曾单独部署。 +4. **`gy_sb_zl` 积压 7.1 万条的成因**:设备端未取、网关未转发,还是消费后回写失败?需查 2026-10 之后的设备侧日志。 +5. **Oracle 存量公寓**:`ojdbc8` 依赖存在,确认是否存在仍直连 Oracle 的部署环境。 +6. **金仓安全版 MAC 是否启用**:`sysmac` schema 为空表,若启用策略会影响后续新增表的授权模型。 + +## 附录 A 采集 SQL(可复现) + +```sql +-- 库级 +select version(), current_setting('server_encoding'); +select name, setting from pg_settings where name in ('database_mode','ora_input_emptystr_isnull'); +select nspname, count(*) from pg_class c join pg_namespace n on n.oid=c.relnamespace + where c.relkind='r' group by 1; + +-- 约束与索引 +select count(*) from pg_indexes where schemaname='gygl'; +select constraint_type, count(*) from information_schema.table_constraints + where table_schema='gygl' group by 1; +select count(*) from pg_class c join pg_namespace n on n.oid=c.relnamespace + where c.relkind='S' and n.nspname='gygl'; + +-- 类型与注释 +select udt_name, count(*), sum(case when character_maximum_length is null then 1 else 0 end) + from information_schema.columns where table_schema='gygl' group by 1 order by 2 desc; +select column_name, count(distinct udt_name) from information_schema.columns + where table_schema='gygl' group by 1 having count(distinct udt_name)>1; + +-- 规模与分布 +select relname, n_live_tup, pg_size_pretty(pg_total_relation_size(relid)) + from pg_stat_user_tables order by pg_total_relation_size(relid) desc; +select count(*), count(distinct jcid), count(distinct gyid), min(kcsj), max(kcsj) from gygl.gy_jcxx; +select deleted, count(*), min(generatedtime), max(generatedtime) from gygl.gy_sb_zl group by 1; +select count(*) from gygl.gy_sb_zl where deleted='0' and generatedtime < now() - interval '1 day'; +select to_char(eventtime,'YYYY-MM'), count(*) from gygl.gy_logs group by 1 order by 1; +``` + +> 本轮对实例的全部操作均为 `SELECT`,未执行任何 DDL/DML;分析用临时脚本仅存在于本机临时目录。 diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..b39db11 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,52 @@ +# 叫乘管理系统 梳理文档 + +本目录是对既有代码的结构化梳理,目的是为后续迭代提供统一的“地图”:知道每个业务落在哪个包/页面、数据怎么流转、接口和外部系统怎么对接、改动时应该看哪些文件。 + +> 后端:`yktgl/`;前端:`YktVue/`,见 [frontend/](./frontend/README.md)。 +> 梳理基准:`main` 分支提交 `135a192`(2026-11-07)。 + +## 文档索引 + +| 文档 | 内容 | 适用场景 | +| --- | --- | --- | +| [01-系统总览.md](./01-系统总览.md) | 业务定位、角色、运行形态、技术栈、关键设计约束 | 新人入门、方案评审 | +| [02-工程结构与分层.md](./02-工程结构与分层.md) | 目录/包结构、分层约定、启动流程、公共组件 | 找代码、定落点 | +| [03-业务模块地图.md](./03-业务模块地图.md) | 各业务模块职责、控制器、服务、依赖关系 | 评估改动影响面 | +| [04-核心业务流程.md](./04-核心业务流程.md) | 入住、叫乘、复叫、报警、应答、离寓、房间、交路同步、卡务 | 改业务逻辑前必读 | +| [05-数据模型.md](./05-数据模型.md) | 核心表、字段语义、状态码、归档与锁 | 写 SQL / 改表 / 排查数据 | +| [06-接口清单.md](./06-接口清单.md) | Web 接口与设备接口清单、协议枚举 | 联调、接口扩展 | +| [07-外部集成与定时任务.md](./07-外部集成与定时任务.md) | Asterisk AMI、运安 SOAP、中心 HTTP、WebSocket、Quartz/@Scheduled | 排障、集成改造 | +| [08-部署与配置.md](./08-部署与配置.md) | Profile、数据库/Redis、资源目录、升级脚本 | 部署、环境切换 | +| [09-迭代指引与风险清单.md](./09-迭代指引与风险清单.md) | 技术债、改动落点速查、待确认问题、迭代建议 | 排期、评审 | +| [10-架构优化方案-三端拆分.md](./10-架构优化方案-三端拆分.md) | 平台端/公寓端/应用端拆分:现状评估、目标架构、职责边界、模块与数据归属、接口契约、设备接入、部署与迁移路径 | 架构评审、重构设计 | +| [11-既有数据库结构分析.md](./11-既有数据库结构分析.md) | 实测库级参数、40 表清单与规模、零主键零索引等结构缺陷、数据质量实证、表归属矩阵、类型归一与拆表建议 | 库设计评审、迁移改造 | + +## 前端文档 + +前端梳理独立成册,入口见 [frontend/README.md](./frontend/README.md): + +| 文档 | 内容 | +| --- | --- | +| [frontend/01-前端总览与技术栈.md](./frontend/01-前端总览与技术栈.md) | 四类界面、运行拓扑、技术栈、全局变量 | +| [frontend/02-工程结构与请求链路.md](./frontend/02-工程结构与请求链路.md) | 启动装配、登录守卫、axios、API 与后端映射 | +| [frontend/03-路由与页面职责.md](./frontend/03-路由与页面职责.md) | 13 条路由、入口关系、遗留页面 | +| [frontend/04-核心页面拆解-JcXx.md](./frontend/04-核心页面拆解-JcXx.md) | 值班台布局、状态、操作、时间规则、轮询 | +| [frontend/05-实时通信与语音告警.md](./frontend/05-实时通信与语音告警.md) | WebSocket/SIP、消息协议、告警与 TTS 队列 | +| [frontend/06-食堂与自助终端模块.md](./frontend/06-食堂与自助终端模块.md) | 自助登记、点餐/结算/后厨/保洁、选房与房间维护 | +| [frontend/07-构建部署与风险清单.md](./frontend/07-构建部署与风险清单.md) | 构建、环境、部署要点、测试基线、P0/P1/P2 风险 | + +## 建议阅读路径 + +- **第一次接触项目**:01 → 02 → 03 → 04;再看前端 01 → 02 → 04。 +- **要接一个需求**:03 定位模块 → 04 看流程 → 09 查落点与风险 → 06/05 查接口和表;涉及页面时读前端 04/05。 +- **线上排障**:07(外部依赖/定时任务)→ 04(状态机)→ 05(数据与状态码)→ 08(配置);前端问题读前端 05/07。 +- **准备重构**:02(分层)→ 09(技术债与建议)→ 10(三端拆分方案)→ 11(既有库结构分析);前端读前端 02/04/07。 + +## 快速事实 + +- 后端:Spring Boot 2.6.13 + Java 8,默认 jar 打包(可改 WAR),上下文路径 `/yktgl`,默认端口 `8081`。 +- 数据库:KingbaseES 8(兼容 Oracle 语法),Redis 存 Session。 +- 多公寓:同一套代码通过“客户端 IP → 公寓”解析 `gyid`,不依赖前端传参。 +- 核心链路:设备/网页入寓登记 → 生成叫乘计划(Quartz 三个任务)→ 叫乘/复叫/报警/应答 → 离寓归档。 +- 最大单体:后端 `jcgl/service/JcglService.java`(约 4192 行);前端 `src/views/JcXx.vue`(5294 行)。 +- 前端:Vue 2.6 + Element UI,生产静态与接口同域 `/yktgl`;登录无真实 token,公寓 `gyid` 由后端按客户端 IP 解析。 diff --git a/docs/frontend/01-前端总览与技术栈.md b/docs/frontend/01-前端总览与技术栈.md new file mode 100644 index 0000000..26412f9 --- /dev/null +++ b/docs/frontend/01-前端总览与技术栈.md @@ -0,0 +1,143 @@ +# 01 前端总览与技术栈 + +## 1. 系统定位 + +前端是“叫乘 / 公寓管理”系统的操作层,服务四类现场角色,共用一个 Vue 工程、一套构建产物: + +| 界面 | 主要使用者 | 典型设备 | 入口路由 | +| --- | --- | --- | --- | +| 叫乘值班控制台 | 公寓值班员 | 值班室大屏 / 双屏 PC | `/jcxx`(登录后默认页) | +| 入寓自助终端 | 乘务员本人 | 触摸屏一体机(带摄像头、读卡器) | `/ykt_main` | +| 食堂终端 | 乘务员、厨师、保洁 | 点餐屏 / 后厨屏 / 保洁屏 | `/MealsOrdering`、`/paymentPlatform`、`/chefOrders`、`/roomService` | +| 房间维护与同步查询 | 值班员、管理员 | PC | 值班台弹窗 + `/synJcXx` | + +前端不承担鉴权与公寓归属判断:**公寓 `gyid` 最终以后端按客户端 IP 解析的结果为准**。前端登录时把用户名写入 `localStorage.gyid`,主要用于 WebSocket 的 `{username}` 路径段和部分页面查询参数;两者不一致是本系统最常见的联调问题来源,详见 [02](./02-工程结构与请求链路.md) 与 [07](./07-构建部署与风险清单.md)。 + +## 2. 运行拓扑 + +```mermaid +flowchart LR + subgraph Browser["浏览器(现场终端)"] + Duty["值班控制台 JcXx.vue"] + Self["自助终端 shijinpeng/main.vue"] + Meal["食堂终端 zhengchuang/*"] + RoomUI["房间选择/维护 Room.vue / RoomLook.vue"] + end + + subgraph SameOrigin["同域静态资源"] + Static["/yktgl/ 前端 dist"] + end + + subgraph Backend["后端 yktgl(8081,context /yktgl)"] + API["REST /jcgl /jlgl /Meals /clean /recharge /heartBeat /excel /jwDataLog"] + WS["WebSocket /jcwebsocket /jlwebsocket /rlywebsocket /MealsWebsocket"] + SIPGW["SIP 客户端网关 :9004 /sipclient"] + end + + PBX["Asterisk / AMI / 广播设备"] + + Duty --> Static + Self --> Static + Meal --> Static + RoomUI --> Static + Duty -->|"axios baseURL /yktgl(生产)或 /dev-api→/yktgl(开发)"| API + Self -->|"axios 同源 /yktgl"| API + Meal -->|"axios 同源 /yktgl"| API + RoomUI -->|"axios 同源 /yktgl"| API + Duty -->|"ws://host/yktgl/jcwebsocket/{gyid}"| WS + Duty -->|"ws://host/yktgl/jlwebsocket/{gyid}"| WS + Self -->|"ws://host/yktgl/rlywebsocket/test33"| WS + Meal -->|"ws://host/yktgl/MealsWebsocket/test33"| WS + Duty -->|"ws://127.0.0.1:9004/sipclient/{gyid}"| SIPGW + SIPGW --> PBX + Backend --> PBX +``` + +要点: + +- **静态资源与接口同域**。生产 `vue.config.js` 的 `publicPath` 是 `/yktgl/`([vue.config.js:21](../../YktVue/vue.config.js#L21)),axios 生产 `baseURL` 也是 `/yktgl`([request.js:17](../../YktVue/src/utils/request.js#L17)),依赖反向代理把 `/yktgl/*` 转到后端。 +- **WebSocket 地址是硬编码相对当前主机**,没有走 `public/config.js` 里的 `getWebSocketUrl()`;协议固定 `ws://`,HTTPS 部署会失败。 +- **SIP 地址固定本机 `127.0.0.1:9004`**,意味着值班台必须与 SIP 网关同机部署。 +- 浏览器不直连 Asterisk,语音呼叫由后端/SIP 网关完成;前端只发“呼叫/停止”指令。 + +## 3. 技术栈 + +| 层次 | 选型 | 版本 | 说明 | +| --- | --- | --- | --- | +| 框架 | Vue | 2.6.12 | Options API,无 Composition API | +| 路由 | vue-router | 3.4.9 | hash 默认模式,守卫只判断本地登录痕迹 | +| 状态 | Vuex | 3.6.0 | 依赖已声明,但工程没有 `src/store`,实际未使用 | +| UI | Element UI | 2.15.13 | 大量内联样式覆盖主题 | +| 请求 | axios | 0.24.0 | 统一拦截器 + 两套实例(`request.js` / `fileRequest.js`) | +| 图表 | ECharts | 5.4.0 | 依赖已声明,业务页面基本未使用 | +| 构建 | Vue CLI / webpack | 4.4.6 / 4 | `--openssl-legacy-provider` 兼容新 Node | +| 样式 | Sass | 1.32.13 | `lang="scss"`,含全局大屏样式 | +| 工具 | js-cookie、file-saver、jsencrypt、quill、vuedraggable、vue-cropper | 见 package.json | 多数为 RuoYi 模板遗留 | + +工程来源是 RuoYi-Vue 模板(`package.json` 的 repository 仍指向 RuoYi),但业务页面已大幅改造,模板的权限、字典、布局、Vuex 体系基本被剥离或未接线。 + +## 4. 四类界面与页面清单 + +```mermaid +flowchart TB + Login["/login 登录"] --> Duty["/jcxx 值班控制台"] + Duty -->|弹窗| Room["Room.vue 选房"] + Duty -->|弹窗| RoomLook["RoomLook.vue 房间维护"] + Duty -->|弹窗| History["历史叫乘 / 交路 / 人员 / 充值"] + Login --> Self["/ykt_main 自助终端"] + Login --> MealOrder["/MealsOrdering 点餐"] + Login --> Pay["/paymentPlatform 结算"] + Login --> Chef["/chefOrders 后厨"] + Login --> Clean["/roomService 保洁"] + Login --> Syn["/synJcXx 运安同步查询"] +``` + +页面规模与职责: + +| 文件 | 行数 | 职责 | 详细说明 | +| --- | --- | --- | --- | +| [`src/views/JcXx.vue`](../../YktVue/src/views/JcXx.vue) | 5294 | 值班主界面:叫乘列表、全部操作、实时告警、SIP、历史、导出 | [04](./04-核心页面拆解-JcXx.md) | +| [`src/shijinpeng/main.vue`](../../YktVue/src/shijinpeng/main.vue) | 943 | 自助入寓/离寓登记、刷脸、餐补查询、录像 | [06](./06-食堂与自助终端模块.md) | +| [`src/zhengchuang/views/MealsOrdering.vue`](../../YktVue/src/zhengchuang/views/MealsOrdering.vue) | 988 | 乘务员自助点餐、刷脸支付、购物车 | [06](./06-食堂与自助终端模块.md) | +| [`src/zhengchuang/views/paymentPlatform.vue`](../../YktVue/src/zhengchuang/views/paymentPlatform.vue) | 570 | 自助结算金额录入 | [06](./06-食堂与自助终端模块.md) | +| [`src/zhengchuang/views/chefOrders.vue`](../../YktVue/src/zhengchuang/views/chefOrders.vue) | 532 | 后厨订单队列、完成订单、新单提示 | [06](./06-食堂与自助终端模块.md) | +| [`src/zhengchuang/views/roomService.vue`](../../YktVue/src/zhengchuang/views/roomService.vue) | 339 | 保洁房间卡片、清扫计时 | [06](./06-食堂与自助终端模块.md) | +| [`src/components/Room.vue`](../../YktVue/src/components/Room.vue) | 332 | 房间选择器,按楼层分组、推荐房、状态过滤 | [06](./06-食堂与自助终端模块.md) | +| [`src/components/RoomLook.vue`](../../YktVue/src/components/RoomLook.vue) | 618 | 房间维护:多选同状态房间、开始/结束保洁、停用/恢复 | [06](./06-食堂与自助终端模块.md) | +| [`src/views/SynJcXx.vue`](../../YktVue/src/views/SynJcXx.vue) | 101 | 运安同步叫乘数据只读查询 | [03](./03-路由与页面职责.md) | +| [`src/views/Login.vue`](../../YktVue/src/views/Login.vue) | 206 | 公寓账号登录 | [02](./02-工程结构与请求链路.md) | + +## 5. 全局变量与页面差异 + +`public/config.js` 在 `index.html` 中以脚本方式先于应用加载,向 `window` 注入: + +| 变量 | 值 | 用途 | +| --- | --- | --- | +| `apiRoot` | `window.location.host` | 拼接 WebSocket 地址;注意不含协议 | +| `gyid` | `localStorage.getItem('gyid') \|\| 'TLXC'` | 全局公寓标识兜底值 | + +不同页面对这两个变量的使用并不一致: + +| 页面 | WebSocket 地址 | 公寓标识 | +| --- | --- | --- | +| `JcXx.vue` | `ws://{apiRoot}/yktgl/{jc,jl}websocket/{this.gyid}` | `this.gyid = localStorage.gyid \|\| 全局 gyid`([JcXx.vue:1728](../../YktVue/src/views/JcXx.vue#L1728)) | +| `shijinpeng/main.vue` | `ws://{apiRoot}/yktgl/rlywebsocket/test33` | 页面内硬编码 `gyid: "SYBXC"`、`gymc: "沈阳站公寓"`([main.vue:291](../../YktVue/src/shijinpeng/main.vue#L291)) | +| `MealsOrdering.vue` / `paymentPlatform.vue` | `ws://{apiRoot}/yktgl/MealsWebsocket/test33` | 无 | +| `roomService.vue` | 无 | 硬编码 `gyid: "SYBXC"`([roomService.vue:190](../../YktVue/src/zhengchuang/views/roomService.vue#L190)) | +| `public/config.js` 注释 | — | 保留 PJXC / SJTXC / BYQXC 等多公寓 IP/gyid 手工切换示例 | + +结论:**这个工程原本按“一套代码、多个公寓分别部署”的方式维护**,多公寓配置靠改 `config.js` 注释或改页面常量,而不是运行时配置中心。 + +## 6. 关键设计约束 + +1. **同域部署**:静态资源 `/yktgl/`、接口 `/yktgl/*`、WebSocket `/yktgl/*websocket` 必须由同一反向代理暴露。 +2. **后端按 IP 识别公寓**:`/jcgl/*` 的大量接口不接收前端 `gyid`,后端用 `zdwhService.getGyIdByIp(request)` 解析(见 [后端 03](../03-业务模块地图.md))。前端传的 `gyid` 主要用于推送寻址。 +3. **WebSocket username 必须等于公寓 gyid**:`JcWebsocket` 只向 `username == gyid` 或 `all` 的会话发送(见 [后端 07](../07-外部集成与定时任务.md))。因此值班员登录名通常就是公寓代码。 +4. **无全局状态**:所有页面状态各自维护,跨页面协作靠 `localStorage`、`BroadcastChannel` 和 `location.reload()`。 +5. **单机终端假设**:SIP、自助终端、食堂终端都存在本机地址或固定 `test33`/`SYBXC` 的硬编码,跨机部署需要先改代码。 + +## 7. 下一步 + +- 想看一次请求从点击到后端:读 [02-工程结构与请求链路](./02-工程结构与请求链路.md)。 +- 想改叫乘主界面:直接读 [04-核心页面拆解-JcXx](./04-核心页面拆解-JcXx.md)。 +- 想改实时告警:读 [05-实时通信与语音告警](./05-实时通信与语音告警.md)。 diff --git a/docs/frontend/02-工程结构与请求链路.md b/docs/frontend/02-工程结构与请求链路.md new file mode 100644 index 0000000..a4dcad9 --- /dev/null +++ b/docs/frontend/02-工程结构与请求链路.md @@ -0,0 +1,277 @@ +# 02 工程结构与请求链路 + +## 1. 目录结构 + +```text +YktVue/ +├── public/ +│ ├── index.html # 先加载 config.js,再加载应用 +│ ├── config.js # 全局 apiRoot / gyid / getWebSocketUrl +│ └── favicon.ico +├── src/ +│ ├── main.js # 仅装配 ElementUI + router +│ ├── App.vue # 仅 +│ ├── router/index.js # 13 条路由 + 登录守卫 +│ ├── api/ +│ │ ├── login.js # /auth/captchaImage、/auth/logon +│ │ ├── jcgl/jcgl.js # 叫乘主业务接口(35 个) +│ │ ├── jlgl/jlgl.js # 交路管理接口(7 个) +│ │ └── zhengchuang/mealsOrdering.js # 食堂接口(9 个) +│ ├── utils/ +│ │ ├── request.js # axios 主实例 + 拦截器 + download +│ │ ├── fileRequest.js # 导出专用 axios 实例 +│ │ ├── auth.js # Cookie token + 清理登录信息 +│ │ ├── ruoyi.js # tansParams / blobValidate 等模板工具 +│ │ └── permission.js # 依赖不存在的 @/store,死代码 +│ ├── plugins/ # RuoYi 插件(cache/download/modal/...),未安装 +│ ├── components/ +│ │ ├── Room.vue # 选房器 +│ │ ├── RoomLook.vue # 房间维护 +│ │ └── RoomLook copy.vue, media.vue # 遗留 +│ ├── views/ +│ │ ├── Login.vue +│ │ ├── JcXx.vue # 值班主界面(5294 行) +│ │ ├── SynJcXx.vue +│ │ ├── Home.vue / About.vue # 模板遗留 +│ │ └── JcXx.vue.bak_mojibake # 遗留备份 +│ ├── shijinpeng/main.vue # 自助登记终端 +│ └── zhengchuang/ +│ ├── views/ # 点餐、结算、后厨、保洁 +│ ├── components/ # audioPlay / textSpeak +│ └── css/ # MealCss.css 等 +├── tests/ # 两个纯 Node 测试脚本 +├── vue.config.js +├── package.json +└── .env.development / .env.production / .env.staging +``` + +## 2. 启动装配 + +[`src/main.js`](../../YktVue/src/main.js) 只有 14 行: + +```js +Vue.use(ElementUI) +Vue.prototype.$axios = axios +new Vue({ router, render: h => h(App) }).$mount('#app') +``` + +因此: + +- **没有安装 Vuex**,也没有 `store` 目录;`package.json` 中的 `vuex` 只是模板残留。 +- **没有安装 `src/plugins/index.js`**,所以 `this.$modal`、`this.$download`、`this.$tab` 等 RuoYi 原型方法不存在;`JcXx.vue` 的 `handleDelete` 仍调用了 `this.$modal.confirm`,该分支是死路径。 +- **没有全局路由守卫之外的权限体系**;`utils/permission.js` 因为 import `@/store` 会失败,且没有被引用。 +- `public/index.html:9` 先加载 `config.js`,所以页面里可以直接使用 `apiRoot`、`gyid` 这两个隐式全局变量。 + +## 3. 登录与路由守卫 + +### 3.1 登录时序 + +```mermaid +sequenceDiagram + participant U as 用户 + participant L as Login.vue + participant R as request.js + participant B as 后端 /auth/logon + participant LS as localStorage/Cookie + + U->>L: 输入用户名/密码 + L->>R: GET /auth/logon?username&password + R->>B: baseURL + /auth/logon + B->>B: 按用户名查 DictData_gy,校验请求 IP 是否在该公寓 ip 列表 + alt 校验通过 + B-->>R: Result.success(LogEntity) + R-->>L: { code:'00000', data: LogEntity } + L->>LS: setToken('fake-token-')(响应无 token) + L->>LS: gyid=username, loginAccount=username + L->>L: $router.push('/jcxx') + else 用户/IP 不匹配 + B-->>R: Result.failure(msg) + R-->>L: Promise.reject + Message.error + end +``` + +关键代码: + +- 登录请求:`api/login.js` 的 `login()` 使用 GET `/auth/logon`([login.js:16](../../YktVue/src/api/login.js#L16))。 +- 成功处理:`Login.vue` 判断 `response.code === '00000'`,没有 `response.token` 就写入 `fake-token-时间戳`,并把 **用户名当成 gyid**([Login.vue:83](../../YktVue/src/views/Login.vue#L83))。 +- 后端契约:`/auth/logon` 只校验 `DictData_gy` 与 IP,写入 Session 的 `gyid`/`ip`,返回 `LogEntity`,不签发 token(见 [后端 06](../06-接口清单.md))。 + +### 3.2 守卫逻辑 + +[`src/router/index.js:80`](../../YktVue/src/router/index.js#L80): + +```js +const isLoggedIn = token || loginAccount +if (to.path === '/login') { + isLoggedIn ? next('/jcxx') : next() +} else { + isLoggedIn ? next() : next('/login') +} +``` + +含义与风险: + +- 登录态 = Cookie `Admin-Token` 或 `localStorage.loginAccount` 任一存在,**不校验 token 有效性**,也不校验后端 Session 是否过期。 +- 退出登录只清本地信息(`clearLoginInfo()`),没有调用后端 `/auth/logout`。 +- 只要用户手工写入 `loginAccount`,就能进入所有页面;真正的权限控制完全依赖后端接口和 IP 校验。 +- 登录页在已登录时会强制跳 `/jcxx`,无法回到登录页切换账号,除非先清 localStorage/Cookie。 + +## 4. axios 请求链路 + +### 4.1 主实例 + +[`src/utils/request.js:14`](../../YktVue/src/utils/request.js#L14) 创建实例: + +| 配置 | 值 | 说明 | +| --- | --- | --- | +| `baseURL` | 开发 `/dev-api`,其他 `/yktgl` | 硬编码,**不读取 `VUE_APP_BASE_API`**([request.js:17](../../YktVue/src/utils/request.js#L17)) | +| `timeout` | 10000 ms | 导出走 `fileRequest` 的 50 s | +| `Content-Type` | `application/json;charset=utf-8` | GET 参数由拦截器拼到 URL | + +请求拦截器([request.js:24](../../YktVue/src/utils/request.js#L24)): + +1. 若存在 Cookie token 且未显式 `isToken: false`,加 `Authorization: Bearer `;由于 token 是假的,后端并不校验。 +2. GET 请求用 `tansParams` 把 `params` 序列化到 URL,然后清空 `params`。 +3. POST/PUT 的防重复提交逻辑只写不校验:`sessionObj` 写入后,真正比较的 `else` 分支被整段注释([request.js:54](../../YktVue/src/utils/request.js#L54))。 + +响应拦截器([request.js:75](../../YktVue/src/utils/request.js#L75)): + +```js +const { code, msg } = response.data +if (code === '00000' || code === '11111' || code === 1) return response.data +if (response.data instanceof ArrayBuffer) return response +Message({ message: msg || '系统出错', type: 'error' }) +return Promise.reject(new Error(msg || 'Error')) +``` + +注意: + +- **`11111` 被当成成功返回**。业务层需要自己判断 `res.code === '11111'` 并提示错误(例如 `handleRechargeConfirm`、`handleUpdateJcXxConfirm`)。这与后端“`00000` 成功、`11111` 业务失败”的约定不一致,容易漏判。 +- 二进制判断在成功码判断之后,且 axios 默认不会把 blob 转成 `ArrayBuffer`,所以 blob 分支实际很少命中;导出另走 `fileRequest`。 +- 网络错误分支整体被块注释([request.js:94](../../YktVue/src/utils/request.js#L94)),**请求失败时不会弹全局错误提示**,多数页面 `.catch()` 为空或只 `console.log`,表现为“点了没反应”。 + +### 4.2 导出实例 + +[`src/utils/fileRequest.js:6`](../../YktVue/src/utils/fileRequest.js#L6) 固定 `baseURL: '/yktgl'`、`timeout: 50000`。开发环境没有 `/yktgl` 代理(只有 `/dev-api`),**本地导出会 404**;生产同域部署才可用。 + +### 4.3 环境变量现状 + +| 文件 | `VUE_APP_BASE_API` | 是否生效 | +| --- | --- | --- | +| `.env.development` | `/yktgl` | 否,`request.js` 写死 `/dev-api` | +| `.env.production` | `/prod-api` | 否,写死 `/yktgl` | +| `.env.staging` | `/stage-api` | 否,`build:stage` 也走写死值 | + +改环境时不要只改 `.env`,必须同步改 `request.js`(以及需要时的 `fileRequest.js` 和 `vue.config.js` 代理)。 + +## 5. API 封装与后端映射 + +### 5.1 模块划分 + +| 模块 | 文件 | 接口数 | 用途 | +| --- | --- | --- | --- | +| 登录 | `src/api/login.js` | 2 | 验证码(未使用)、登录 | +| 叫乘主业务 | `src/api/jcgl/jcgl.js` | 35 | 列表、叫乘、房间、人员、导出、同步、时间规则 | +| 交路 | `src/api/jlgl/jlgl.js` | 7 | 交路增删改查、导入、确认 | +| 食堂 | `src/api/zhengchuang/mealsOrdering.js` | 9 | 菜品、订单、房间、消费、餐次 | + +### 5.2 叫乘主业务接口映射 + +`src/api/jcgl/jcgl.js` 的函数与后端控制器对应关系(后端细节见 [06-接口清单](../06-接口清单.md)): + +| 前端函数 | 方法 | URL | 典型调用方 | +| --- | --- | --- | --- | +| `getJcXxList` | POST | `/jcgl/getCcJcXx` | 值班主列表、轮询 | +| `getJcXxList_Ls` | POST | `/jcgl/getCcJcXx_Ls` | 历史叫乘(分页/滚动加载) | +| `modifyJcXx` | POST | `/jcgl/modifyJcXx` | 修改叫乘信息 | +| `cancelTrainAssignment` | POST | `/jcgl/cancelTrainAssignment` | 取消叫乘 | +| `handleCheckOut` | POST | `/jcgl/handleCheckOut` | 出乘 | +| `handleStopCalling` | POST | `/jcgl/handleStopCalling` | 停止报警/暂停报警 | +| `handleTempCalling` | POST | `/jcgl/handleTempCalling` | 语音临叫(广播式) | +| `handleRoomCall` | POST | `/jcgl/handleRoomCall`(method=1 语音,=2 喊话) | 单房间临叫 | +| `handleRoomCallEnd` | POST | `/jcgl/handleRoomCallEnd` / `...End2` | 停止单房间呼叫 | +| `handleMultiRoomCall` | POST | `/jcgl/handleMultiRoomCall` | 多房间喊话 | +| `handleMultiRoomCallEnd` | POST | `/jcgl/handleMultiRoomCallEnd` | 停止多房间喊话 | +| `getRoomList` | GET | `/jcgl/getRoomList` | 选房、房间维护、终端 | +| `getRoomRecommendations` | GET | `/jcgl/getRoomRecommendations` | 选房推荐 | +| `modifyRoomStatus` | GET | `/jcgl/modifyRoomStatus` | 停用/恢复房间 | +| `startRoomCleaning` | POST | `/clean/startCleaning` | 开始保洁(`deviceCode` 固定 `"123123"`) | +| `endRoomCleaning` | POST | `/clean/endCleaning` | 结束保洁 | +| `getRyXxByRyId` | POST | `/jcgl/getRyxxByRyId` | 人员查询/维护 | +| `modifyRyXx` | POST | `/jcgl/modifyRyXx` | 人员维护保存 | +| `deleteRyXx` | POST | `/jcgl/deleteRyXx` | 删除人员 | +| `getWslRyXx` | POST | `/jcgl/getWslRyXx` | 未刷脸人员 | +| `getRyFjXx` | POST | `/jcgl/getRyFjXx` | 换房前查询同房间人员 | +| `exchangeRoom` | POST | `/jcgl/exchangeRoom` | 确认换房 | +| `getJlxxList` | GET | `/jcgl/getJlxxList` | 交路列表 | +| `getJlXxJcsjList` | POST | `/jlgl/getJlXxJcsjList` | 按开车日期取车次 | +| `addJlXx` / `modifyJlXx` / `delJlXx` / `deleteJlXx` / `confirmJlXx` | POST | `/jlgl/*` | 交路维护 | +| `getZbyList` / `getGzList` | GET | `/jcgl/getZbyList`、`/jcgl/getGzList` | 值班员/工种字典 | +| `getSjgz` | GET | `/jcgl/getSjgz` | 时间规则(150/120/30 分钟) | +| `isHoliday` | GET | `/jcgl/isHoliday` | 食堂节假日判断 | +| `getSynJcXx` | GET | `/jcgl/getSynJcXx` | 运安同步数据查询 | +| `getSynJcXxIsOffline` | POST | `/jwDataLog/getSynJcXxIsOffline` | 运安离线检测 | +| `getLatestSyncTime` | GET | `/jcgl/getLatestSyncTime` | 最近同步时间 | +| `getDeviceStatusInfo` | POST | `/heartBeat/getDeviceStatusInfo` | 设备心跳/在线状态 | +| `getRecordingConfig` | GET | `/jcgl/getRecordingConfig` | 录音回放配置 | +| `handleRecharge` | GET | `/recharge/handleRecharge` | 现金充值办理 | +| `exportJcXxExcell` | GET(blob) | `/excel/exportJcXxExcell` | 定时/手工导出 | +| `getBtje_info` / `getBtje_infoMeal3` / `updateMeal3` | GET/POST | `/jcgl/getBtje_info*`、`/jcgl/updateMeal3` | 餐补余额 | +| `submitAll` | GET | `/jcgl/submitAll` | 自助终端/值班台提交入寓登记 | +| `getJcxxByRyid` | POST | `/jcgl/getJcxxByRyid` | 按人员查叫乘 | +| `jcAlarm` | POST | `/jcgl/jcAlarm` | 设备报警 | +| `login` | GET | `/auth/login` | **未被页面使用**,登录走 `api/login.js` | + +### 5.3 食堂接口映射 + +| 前端函数 | 方法 | URL | +| --- | --- | --- | +| `getMeals` | GET | `/Meals/getMeals` | +| `createOrder` | POST | `/Meals/createOrder` | +| `addOrders_MX` | POST | `/Meals/addOrders_MX` | +| `OrdersSearch` | GET | `/Meals/OrdersSearch` | +| `OrderMX_search` | GET | `/Meals/OrderMX_search` | +| `updateStatus` | POST | `/Meals/updateStatus` | +| `getRooms` | GET | `/Meals/getRooms` | +| `consume` | POST | `/Meals/consume` | +| `getCsLb` | GET | `/Meals/getCsLb` | + +## 6. 响应契约与业务判断 + +后端统一返回 `Result`: + +```json +{ "code": "00000", "msg": "...", "data": ..., "count": 123 } +``` + +前端使用约定: + +- `code === '00000'`:成功,`data` 为业务数据,`count` 为分页总数。 +- `code === '11111'`:业务失败;拦截器仍返回该对象,调用方必须自己处理。 +- 其他:拦截器弹 `Message.error` 并 reject。 +- 导出接口返回 blob,不经过统一响应判断。 + +## 7. 工程遗留与陷阱 + +| 现象 | 位置 | 影响 | +| --- | --- | --- | +| `JcXx.vue` 导入不存在的 `auditJlXx` | [JcXx.vue:1392](../../YktVue/src/views/JcXx.vue#L1392) | 构建告警;若将来使用会运行时报错 | +| `shijinpeng/main.vue` 导入不存在的 `getRyxx` | [main.vue:280](../../YktVue/src/shijinpeng/main.vue#L280) | 刷脸登记调用时 `TypeError: getRyxx is not a function` | +| `utils/permission.js` 依赖不存在的 `@/store` | `src/utils/permission.js` | 死代码,若被引入立即失败 | +| `App.vue` 结尾多一个字面量 `3` | [App.vue:32](../../YktVue/src/App.vue#L32) | 无效语句,构建噪音 | +| `Home.vue` 使用未注册的 `` | [Home.vue:6](../../YktVue/src/views/Home.vue#L6) | 访问 `/home` 时渲染告警 | +| `README.md` 命令写成 `npm run serve/build` | `YktVue/README.md` | 与 `package.json` 的 `dev/build:prod` 不符 | +| 防重复提交只写不判 | [request.js:51](../../YktVue/src/utils/request.js#L51) | 双击可能重复提交 | +| 请求失败无全局提示 | [request.js:94](../../YktVue/src/utils/request.js#L94) | 用户无感知,排障困难 | +| `build:stage` 没有对应接口前缀 | `.env.staging` | 构建产物仍请求 `/yktgl` | + +## 8. 修改落点速查 + +| 需求 | 先看 | +| --- | --- | +| 新增一个后端接口 | `src/api/jcgl/jcgl.js` 增加封装 → 页面调用 | +| 改登录/切换账号 | `Login.vue`、`utils/auth.js`、`router/index.js` | +| 改请求超时/前缀/错误提示 | `utils/request.js`、`fileRequest.js`、`vue.config.js` | +| 改开发代理指向 | `vue.config.js` 的 `devServer.proxy` | +| 新增页面 | `router/index.js` + `src/views/` | +| 清理模板遗留 | `plugins/`、`utils/permission.js`、`views/Home.vue`、`About.vue`、`RoomLook copy.vue`、`JcXx.vue.bak_mojibake` | diff --git a/docs/frontend/03-路由与页面职责.md b/docs/frontend/03-路由与页面职责.md new file mode 100644 index 0000000..1813d9e --- /dev/null +++ b/docs/frontend/03-路由与页面职责.md @@ -0,0 +1,143 @@ +# 03 路由与页面职责 + +## 1. 路由总表 + +路由定义在 [`src/router/index.js`](../../YktVue/src/router/index.js),全部为静态路由,无动态权限路由、无嵌套路由。 + +| # | 路径 | 名称 | 组件 | 状态 | 职责 | +| --- | --- | --- | --- | --- | --- | +| 1 | `/` | — | 重定向 `/login` | 可用 | 默认入口 | +| 2 | `/login` | `Login` | `views/Login.vue` | 可用 | 公寓账号 + 密码登录 | +| 3 | `/home` | `Home` | `views/Home.vue` | **模板遗留** | 展示 HelloWorld,含未注册组件 | +| 4 | `/MealsOrdering` | `MealsOrdering` | `zhengchuang/views/MealsOrdering.vue` | 可用(有缺陷) | 乘务员自助点餐 | +| 5 | `/paymentPlatform` | `paymentPlatform` | `zhengchuang/views/paymentPlatform.vue` | **半可用** | 自助结算金额录入,刷脸链路被注释 | +| 6 | `/chefOrders` | `chefOrders` | `zhengchuang/views/chefOrders.vue` | 可用 | 后厨订单队列 | +| 7 | `/roomService` | `roomService` | `zhengchuang/views/roomService.vue` | **半可用** | 保洁房间卡片与计时,完成不回写后端 | +| 8 | `/jcxx` | `JcXx` | `views/JcXx.vue` | 可用 | 值班控制台(主业务) | +| 9 | `/JcXx` | — | 重定向 `/jcxx` | 可用 | 大小写兼容 | +| 10 | `/about` | `About` | `views/About.vue` | 模板遗留 | 空壳页 | +| 11 | `/ykt_main` | `ykt_main` | `shijinpeng/main.vue` | 可用(有缺陷) | 自助入寓/离寓登记终端 | +| 12 | `/room` | `room` | `components/Room.vue` | 独立可访问 | 选房组件;正常由值班台弹窗内嵌 | +| 13 | `/synJcXx` | `synJcXx` | `views/SynJcXx.vue` | 可用 | 运安同步叫乘数据查询 | + +## 2. 页面分组 + +```mermaid +flowchart TB + subgraph DutySide["值班侧"] + J["/jcxx 主控制台"] + S["/synJcXx 同步查询"] + end + subgraph CrewSide["乘务员自助"] + Y["/ykt_main 入寓登记"] + M["/MealsOrdering 点餐"] + P["/paymentPlatform 结算"] + end + subgraph CanteenSide["食堂/保洁"] + C["/chefOrders 后厨"] + R["/roomService 保洁"] + end + subgraph LegacySide["模板遗留"] + H["/home"] + A["/about"] + end +``` + +四组页面之间**没有前端共享状态**,协作方式只有三种: + +1. 后端轮询 / WebSocket 推送; +2. `localStorage`(`gyid`、`loginAccount`、`Admin-Token`); +3. `BroadcastChannel`(同浏览器多标签页,见 [06](./06-食堂与自助终端模块.md))。 + +## 3. 页面职责与关键行为 + +### 3.1 `/login` 登录页 + +- 表单只有用户名、密码,没有验证码;`getCodeImg()` 虽已封装但未使用。 +- 成功后写假 token、`gyid`、`loginAccount`,跳 `/jcxx`。 +- 已登录用户访问 `/login` 会被守卫重定向回 `/jcxx`,无法直接切换账号。 +- 详见 [02-登录与路由守卫](./02-工程结构与请求链路.md#3-登录与路由守卫)。 + +### 3.2 `/jcxx` 值班控制台 + +系统的核心页面,承担: + +- 叫乘任务列表(机务/客运、其他人员两个 Tab,但 Tab 仅切换选中态,不改变查询); +- 入寓登记、修改叫乘、取消叫乘、出乘、换房、暂停报警、临叫、停止呼叫; +- 交路信息查看/确认/维护; +- 人员维护、现金充值、历史叫乘、设备状态; +- 实时告警音、运安离线报警、同步提醒弹窗与 TTS; +- 定时刷新列表、定时导出 Excel。 + +完整拆解见 [04-核心页面拆解-JcXx](./04-核心页面拆解-JcXx.md)。 + +### 3.3 `/ykt_main` 自助登记终端 + +- 无登录态输入,靠 `rlywebsocket/test33` 接收“有人靠近/刷脸”事件后弹出登记窗口。 +- 页面内固定 `gyid: "SYBXC"`、`gymc: "沈阳站公寓"`,部署到其他公寓必须改代码。 +- 依赖的 `getRyxx` 在 `api/jcgl/jcgl.js` 中不存在(实际函数名是 `getRyXxByRyId`),刷脸确认时会抛异常;即使修复,入寓分支也只广播、不调用 `/jcgl/submitAll`。 +- 详见 [06](./06-食堂与自助终端模块.md)。 + +### 3.4 食堂四页 + +| 页面 | 正常业务闭环 | 当前状态 | +| --- | --- | --- | +| 点餐 `MealsOrdering` | 选餐 → 确认 → 刷脸 → `consume` 扣款 → `createOrder` + 明细 → 广播后厨 | 刷卡回调存在;餐次判断与定时器有缺陷 | +| 结算 `paymentPlatform` | 录入金额 → 刷脸 → 扣款 → 生成订单 | `initWebSocket()` 被注释,刷脸回调不会触发 | +| 后厨 `chefOrders` | 拉取待处理订单 → 查看明细 → 完成订单;收到 `2603` 广播刷新并响铃 | 可用 | +| 保洁 `roomService` | 房间卡片 → 开始保洁计时 → 完成 | “完成”只弹提示,不调用后端 | + +详见 [06](./06-食堂与自助终端模块.md)。 + +### 3.5 `/synJcXx` 同步查询 + +- 只读列表:车次、机车号、开车时间、计划出勤地点、司机姓名、接车方式。 +- 输入即查询(`@input`),带分页 `[5,10,15,20,30,40,50]`。 +- 数据来源 `/jcgl/getSynJcXx`,是运安计划同步结果的查询视图,不参与叫乘状态流转。 + +### 3.6 `/room` 与房间维护 + +- `/room` 可直接访问 `Room.vue`,但正常使用方式是值班台弹窗:`JcXx.vue` 通过 `` 内嵌,选房后 `$emit('childByValue', room)` 回传。 +- `RoomLook.vue` 没有独立路由,只作为值班台“房间信息”弹窗使用。 +- 两者都属于“房间状态”操作面,详见 [06](./06-食堂与自助终端模块.md)。 + +## 4. 入口关系 + +```mermaid +flowchart LR + Login["/login"] -->|成功| Jcxx["/jcxx"] + Jcxx -->|房间信息| RoomLook["RoomLook 弹窗"] + Jcxx -->|入寓登记选房| Room["Room 弹窗"] + Jcxx -->|历史数据| History["历史叫乘弹窗"] + Jcxx -->|交路管理| Jl["交路弹窗"] + Jcxx -->|人员维护| Ry["人员维护弹窗"] + Jcxx -->|退出| Login + Login -.手工访问.-> Terminal["/ykt_main · /MealsOrdering · /paymentPlatform · /chefOrders · /roomService · /synJcXx"] +``` + +除 `/jcxx` 外,其余业务页面**没有导航菜单**,需要现场终端在浏览器书签/快捷方式中直接配置 URL。这是“终端型系统”的常见做法,但也意味着改路由会直接影响现场快捷方式。 + +## 5. 路由层面的注意事项 + +| 问题 | 影响 | 位置 | +| --- | --- | --- | +| 守卫只判断本地痕迹 | 可绕过;后端 Session 过期后页面仍可进入,接口才报错 | [router/index.js:85](../../YktVue/src/router/index.js#L85) | +| 没有 404 路由 | 未知路径会渲染空白 | `router/index.js` | +| 没有路由级 code-splitting 之外的懒加载策略 | 首屏仍会加载 Element UI 全量 | `main.js` | +| `/home`、`/about` 仍在路由表 | 可被访问,产生无意义页面/告警 | `router/index.js:19`、`:53` | +| 终端页面无登录页跳转保护 | 未登录访问会被送到 `/login`,终端无法自动恢复 | 守卫全局生效 | + +## 6. 遗留文件清单 + +以下文件不被路由或组件引用,属于历史备份/模板残留,重构时可直接评估删除: + +| 文件 | 说明 | +| --- | --- | +| `src/views/JcXx.vue.bak_mojibake` | 主页面乱码备份 | +| `src/components/RoomLook copy.vue` | 房间维护旧版 | +| `src/components/media.vue` | 媒体示例组件 | +| `src/components/HelloWorld.vue` | Vue CLI 模板组件(仅 `/home` 使用) | +| `src/views/Home.vue`、`src/views/About.vue` | Vue CLI 模板页面 | +| `src/plugins/*` | RuoYi 插件,未安装 | +| `src/utils/permission.js` | 依赖不存在的 `@/store` | +| `src/utils/jsencrypt.js`、`src/utils/validate.js` | 模板工具,当前业务未使用 | diff --git a/docs/frontend/04-核心页面拆解-JcXx.md b/docs/frontend/04-核心页面拆解-JcXx.md new file mode 100644 index 0000000..f5c4303 --- /dev/null +++ b/docs/frontend/04-核心页面拆解-JcXx.md @@ -0,0 +1,267 @@ +# 04 核心页面拆解:JcXx 值班控制台 + +`src/views/JcXx.vue` 5294 行,模板 1–1389,脚本 1390–4532,样式 4533 之后。它是整个系统的操作中枢,下面按“布局 → 状态 → 列表 → 操作 → 时间规则 → 轮询 → 历史 → 陷阱”拆解。 + +## 1. 页面布局 + +```text +┌─────────────────────────────────────────────────────────────────────┐ +│ 跑马灯(当前告警/呼叫提示) │ 大时钟 │ 房间信息 设备信息 人员维护 │ +│ 机车号 车次 房间号 姓名 [查询][重置] │ │ 交路管理 历史数据 退出 │ +│ │ │ 语音临叫 喊话临叫 停止呼叫 停止报警 │ +├─────────────────────────────────────────────────────────────────────┤ +│ Tab:机务人员和客运人员 / 其他人员 │ +│ 叫乘列表(状态、到寓时间、单位、机车号、姓名、房间号、车次、开车/叫乘/ │ +│ 复叫/应离寓时间、接车方式、暂停报警) │ +├─────────────────────────────────────────────────────────────────────┤ +│ 底部:在寓汇总 / 值班信息 │ +└─────────────────────────────────────────────────────────────────────┘ +弹窗:历史叫乘、离寓抓拍、设备信息、未刷脸、换房、交路、待确认交路、 + 新增/修改交路、修改叫乘、入寓登记、人员维护、现金充值、交接班、 + 房间选择、房间维护、多房间喊话、出乘确认、广播确认、临叫确认、 + 停止呼叫确认、运安离线告警、交路及叫乘提醒 +``` + +| 区域 | 行号 | 说明 | +| --- | --- | --- | +| 顶部卡片 | 4–176 | 左:跑马灯 + 4 个查询框;中:每秒刷新的大时钟;右:功能按钮 | +| 列表卡片 | 178–241 | 两个 Tab(仅视觉)、主表格 | +| 主表格 | 183–240 | `height=600`、`row-style=setRowStyle`、单选高亮 | +| 底部栏 | 1178–1191 | 在寓人数汇总、值班信息 | +| 业务弹窗 | 244–1378 | 见第 7 节 | + +主表格列:序号、状态、到寓时间、单位、机车号、姓名、房间号、出发车次、开车时间、叫乘时间、复叫时间、叫乘日期、应离寓时间、接车方式、操作(暂停报警)。 + +## 2. 页面状态 + +`data()` 从 1406 行开始,核心字段: + +| 字段 | 行号 | 用途 | +| --- | --- | --- | +| `gyid` | 1453 | 当前公寓;`created()` 中取 `localStorage.gyid` 或全局 `gyid` | +| `apiRoot` | 1459 | WebSocket 主机名 | +| `jcxxList` | 1542 | 主列表数据 | +| `selectedRow` | 1470 | 当前选中行,所有行操作的入参 | +| `JcXxQueryParams` | 1546 | 查询条件:`gyid/jch/cc/fjh/rymc/begindt/enddt` | +| `LsJcXxQueryParams` | 1555 | 历史查询:增加 `historyTimeType/pageNum/pageSize` | +| `sjgzConfig` | 1631 | 时间规则 `{kjjcsjgz,zjjcsjgz,lysjgz}` | +| `dismissedAlarmEvents` | 1430 | 已处理告警事件 key 集合,防止重复响铃 | +| `alarmVoiceActive` | 1428 | 报警音是否正在播放 | +| `syncSpeechQueue` / `syncSpeechCurrent` | 1627 起 | TTS 播报队列 | +| `activeTempCalls` | 1644 | 本页面发起的临叫房间及类型(voice/speech) | +| `callListRequestVersion` | 1429 | 列表请求版本号,防止旧响应覆盖新数据 | + +`created()`([JcXx.vue:1726](../../YktVue/src/views/JcXx.vue#L1726))设置 `gyid` 与查询参数,加载交路列表与当前时间;`mounted()`([:1784](../../YktVue/src/views/JcXx.vue#L1784))启动 WebSocket、SIP、字典、列表、时间规则和 4 个定时器。 + +## 3. 列表与状态 + +### 3.1 状态码映射 + +表格“状态”列([JcXx.vue:194](../../YktVue/src/views/JcXx.vue#L194)): + +| `ryzt` | 显示 | 含义 | 行颜色 | +| --- | --- | --- | --- | +| `0` | 待叫乘 | 尚未到叫乘时间 | 黄色 `#FFF200` | +| `1` | 正在呼叫 | 正在叫乘/通话建立中 | 绿色 `#00FF00` | +| `2` | 叫乘报警 | 叫乘未应答 | 红色 `#F56C6C` | +| `3` | 待离寓 | 已叫乘完成,等待离寓 | 浅绿 `#B3E19D` | +| `4` | 离寓报警 | 超时应离寓 | 红色 `#F56C6C` | +| `5` | 正在通话 | 语音通道已建立 | 绿色 `#00FF00` | +| 其他 | 已入寓 | 正常在寓 | 默认 | + +### 3.2 行样式与跑马灯 + +`setRowStyle(row)`([JcXx.vue:3723](../../YktVue/src/views/JcXx.vue#L3723))按以下优先级返回样式,并**在渲染函数里直接修改 `this.message`**: + +1. `ryzt=0` 且 `missedCallTaskWarning=1`:黄色 + 闪烁动画,提示“叫乘时间已过”; +2. `ryzt=1/5`:绿色; +3. `ryzt=2/4`:红色; +4. 本页发起的临叫:绿色,提示“正在喊话/语音临叫”; +5. `jllx=1`:浅蓝 `#d9ecff`; +6. `ryzt=3`:浅绿; +7. `ryzt=0`:黄色; +8. 其他:默认。 + +问题:`setRowStyle` 是纯函数位,却在渲染期间写 `this.message`,会触发额外的渲染与响应式更新;表格行数多时可能造成卡顿或“跑马灯内容错行”。修改行样式逻辑时建议把消息计算移出渲染函数。 + +### 3.3 接车方式显示 + +`cclx` 为“站接”显示站接;“库接/库出”统一显示“库出”;其他为空。 + +## 4. 操作矩阵 + +所有行级操作都要求先选中行(`selectedRow`),点击行时还会向 SIP 发送一次注册状态([JcXx.vue:3231](../../YktVue/src/views/JcXx.vue#L3231))。 + +| 操作 | 入口 | 处理方法 | 后端接口 | 说明 | +| --- | --- | --- | --- | --- | +| 查询/重置 | 顶部 | `getJcXxList` / `resetForm` | `/jcgl/getCcJcXx` | 输入即查询;重置只清 4 个文本条件 | +| 取消叫乘 | 顶部 | `openCancelTrainAssignment` | `/jcgl/cancelTrainAssignment` | 二次确认;成功后停报警、刷新列表 | +| 修改叫乘 | 行/弹窗 | `handleModifyJcXx` → `handleUpdateJcXxConfirm` | `/jcgl/modifyJcXx` | 编辑表单与表格行分离,避免后端拒绝时已改表格 | +| 出乘 | 弹窗 | `handleCheckOut` → `handleCheckOutConfirm` | `/jcgl/handleCheckOut` | 确认后不可修改 | +| 暂停报警 | 行内按钮 | `handleStopCalling` | `/jcgl/handleStopCalling` | 仅 `ryzt=2/4` 有效;成功后把事件 key 加入已忽略集合 | +| 停止报警 | 顶部 | `stopCurrentAlarm` | 同上 | 先校验选中行必须是 `2/4` | +| 语音临叫 | 顶部 | `handleCalling` → `handleRoomCallConfirm` | SIP + `/jcgl/handleRoomCall` | 需 SIP 已连接;`callmethod=1` | +| 喊话临叫(单房间) | 顶部 | `handleCallingOld` → `handleRoomCallConfirm` | SIP + `/jcgl/handleRoomCall2` | 选中行时 `callmethod=2` | +| 喊话临叫(多房间) | 顶部 | `confirmMultiCall` | `/jcgl/handleMultiRoomCall` | 未选中行时弹出多选房间;记录 `lastCalledRooms` | +| 停止呼叫 | 顶部 | `handleRoomCallEnd` | `/jcgl/handleMultiRoomCallEnd` 或 `/jcgl/handleRoomCallEnd(2)` | 优先停止多房间,否则停止当前行 | +| 换房间 | 弹窗 | `handleChangeRoom` → `exchangeRoomConfirm` | `/jcgl/getRyFjXx`、`/jcgl/exchangeRoom` | 先列出同房间人员,再逐人设置新房间 | +| 入寓登记 | 弹窗 | `finalCheck` | `/jcgl/submitAll` | 校验房间/开车日期后提交;成功后关弹窗刷新 | +| 人员维护 | 顶部 | `handleUpdateRyXx` | `/jcgl/getRyxxByRyId`、`/jcgl/modifyRyXx`、`/jcgl/deleteRyXx` | 支持刷脸照片上传/替换 | +| 现金充值 | WebSocket 触发 | `handleRechargeConfirm` | `/recharge/handleRecharge` | 收到 `cz` 消息弹窗;`cz_cancel` 可按 id 关闭 | +| 交路管理 | 顶部 | `handleJlGlClick` | `/jcgl/getJlxxList` | 打开交路列表,可新增/修改/删除/导入 | +| 待确认交路 | 交路弹窗 | `handdleDqrButton` → `handleConfirmJlXxClick` | `/jlgl/confirmJlXx` | 按勾选 id 批量确认 | +| 历史数据 | 顶部 | `handleLsSjClick` → `loadLsJcXx` | `/jcgl/getCcJcXx_Ls` | 分页 + 滚动加载;可回放录音 | +| 设备信息 | 顶部 | `handleDeviceStatus` | `/heartBeat/getDeviceStatusInfo` | 离线设备整行标红 | +| 导出 Excel | 定时 | `exportJcXxExcell` | `/excel/exportJcXxExcell` | 每 10 分钟自动下载一次 | +| 退出 | 顶部 | `handleLogout` | — | 关 WS、清定时器、清本地登录信息 | + +### 4.1 临叫的 SIP 前置动作 + +`handleRoomCallConfirm`([JcXx.vue:4263](../../YktVue/src/views/JcXx.vue#L4263))在调用后端前先向 SIP 发送: + +```json +{ + "reqAct": { + "type": "call_make", + "actId": "actid_12121212", + "params": ["sip:1001@10.32.100.166"] + } +} +``` + +- 目标 SIP 地址、`actId` 都是硬编码; +- `语音临叫` 按钮在 `sipclient.connected == 0` 时禁用,但喊话临叫没有同样保护; +- SIP 发送失败不会阻断后端调用,前后端状态可能不一致。 + +## 5. 时间规则与校验 + +### 5.1 规则来源 + +`getSjgzConfig()`([JcXx.vue:3782](../../YktVue/src/views/JcXx.vue#L3782))调用 `/jcgl/getSjgz`,取第一条: + +| 字段 | 默认 | 含义 | +| --- | --- | --- | +| `kjjcsjgz` | 150 | 库出/库接提前叫乘分钟数 | +| `zjjcsjgz` | 120 | 站接提前叫乘分钟数 | +| `lysjgz` | 30 | 叫乘后到应离寓的分钟数 | + +接口失败时页面回退到同一组默认值并提示。 + +### 5.2 计算关系 + +`test()`(修改叫乘,[JcXx.vue:3901](../../YktVue/src/views/JcXx.vue#L3901))与 `calculateJlxxTime()`(交路新增,[:3998](../../YktVue/src/views/JcXx.vue#L3998)): + +```text +叫乘时间 jcsj = 开车时间 kcsj − 规则分钟(站接 zjjcsjgz,库出/库接/未选 kjjcsjgz) +复叫时间 fjsj = jcsj + 10 分钟 +报警时间 jcbjsj = fjsj + 5 分钟 +应离寓 ylysj = jcsj + lysjgz(默认 30 分钟) +``` + +注意:交路新增表单里的字段叫 `lysj`,修改叫乘表单里叫 `ylysj`,两者复用同一套计算,但字段名不同。 + +### 5.3 合法性校验 + +`validateCallDutyTimeline`([JcXx.vue:3852](../../YktVue/src/views/JcXx.vue#L3852))要求: + +1. 五个时间全部存在且格式正确(`yyyy-MM-dd HH:mm[:ss]`); +2. 严格满足 `kcsj > ylysj > jcbjsj > fjsj > jcsj`; +3. `kcsj − jcsj < 24 小时`。 + +`handleJcsjChange`([:3870](../../YktVue/src/views/JcXx.vue#L3870))在手工修改叫乘时间时重算后三个时间,并要求当前开车时间仍然合法。 + +### 5.4 修改叫乘的提交 + +`handleUpdateJcXxConfirm`([JcXx.vue:3335](../../YktVue/src/views/JcXx.vue#L3335))先规范化 5 个时间字段、再校验时间线,最后提交;`00000` 关闭弹窗刷新列表,`11111` 只弹错误不关闭。 + +## 6. 轮询与自动行为 + +`mounted()` 注册的定时器: + +| 定时器 | 周期 | 动作 | 副作用 | +| --- | --- | --- | --- | +| `latestSyncTimeTimer` | 30 s | `loadLatestSyncTime` → `/jcgl/getLatestSyncTime` | 更新底部“最近同步时间” | +| `minuteTimer` | 10 min | `getJcXxList` | 刷新列表并触发告警音/TTS 调度 | +| `fiveMinuteTimer` | 10 min | `exportJcXxExcell` | **自动下载 Excel 到浏览器** | +| `threeMinuteTimer` | 10 min | `getSynJcXxIsOffline` | 运安离线时弹窗并播放离线报警音 | +| `currentDateTimer` | 1 s | 更新 `currentDate` | 大时钟 | + +`getJcXxList`([JcXx.vue:2262](../../YktVue/src/views/JcXx.vue#L2262))用 `callListRequestVersion` 防止旧响应覆盖: + +```js +const requestVersion = ++this.callListRequestVersion +getJcXxList(params).then(res => { + if (requestVersion !== this.callListRequestVersion) return + this.jcxxList = res.data + this.syncAlarmVoice(this.jcxxList) + ... +}) +``` + +`handleStopCalling` 成功后主动 `++callListRequestVersion`,使所有在途响应失效,避免“刚停掉的报警又被旧数据拉响”。 + +退出/离开页面时 `destroyed`([JcXx.vue:1740](../../YktVue/src/views/JcXx.vue#L1740))清理全部定时器、队列和语音;但 `jlwebsocketclose()` / `jcwebsocketclose()` 被直接调用,而这两个方法内部会执行 `location.reload()`,因此**路由离开本页会触发整页刷新**(详见 05、07)。 + +## 7. 弹窗清单 + +| 弹窗 | 行号 | 绑定 | 说明 | +| --- | --- | --- | --- | +| 历史叫乘信息 | 244–413 | `visible_jcxx_ls` | 查询、分页、录音回放、离寓抓拍 | +| 刷脸离寓抓拍 | 415–426 | `visible_leaveSnapshot` | 图片预览 | +| 设备信息 | 428–484 | `visible_sbxx` | 离线设备标红 | +| 未刷脸人员 | 486–496 | `visible_wslRy` | 入口按钮当前被注释 | +| 换房间 | 498–518 | `visible_hfj` | 逐人选择新房间 | +| 交路信息 | 521–584 | `visible_jlxx` | 列表、增删改、导入、确认 | +| 待确认交路信息 | 595–642 | `visible_jlqrxx` | 批量确认 | +| 新增/修改交路 | 644–779 | `visible_jlxx_add` | 时间自动计算 | +| 修改叫乘信息 | 781–898 | `visible_jcxx` | 5 个时间 + 车次选择 | +| 入寓登记信息 | 900–988 | `visible_rydj` | 刷脸照片、房间选择 | +| 人员维护 | 990–1118 | `visible_ryxxwh` | 查询、编辑、照片 | +| 现金充值 | 1120–1176 | `visible_xjcz` | 金额 0–100,WebSocket 触发 | +| 交接班 | 1192–1205 | `visible_login` | 登录表单(当前值班台不使用) | +| 房间选择 | 1207–1210 | `visible_room` | 内嵌 `Room.vue` | +| 房间维护 | 1212–1215 | `visible_roomLook` | 内嵌 `RoomLook.vue` | +| 多房间喊话 | 1222–1258 | `dialogMultiCallVisible` | 房间多选 | +| 出乘确认 | 1260–1282 | `dialog1Visible` | 不可撤销提示 | +| 广播确认 | 1284–1301 | `dialog2Visible` | 所有公共区域呼叫 | +| 临叫确认 | 1303–1317 | `dialogCallVisible` | 语音/喊话文案动态 | +| 停止呼叫确认 | 1319–1333 | `dialogCallEndVisible` | — | +| 运安离线报警 | 1335–1344 | `visible_synJcXxIsOffline` | 同步异常提示 | +| 交路及叫乘提醒 | 1347–1378 | `syncDialogVisible` | 分组提醒、10 秒自动关闭、TTS | + +## 8. 历史数据与录音 + +- `handleLsSjClick`([JcXx.vue:4312](../../YktVue/src/views/JcXx.vue#L4312))打开弹窗后 `getLsJcXx`。 +- `loadLsJcXx(reset)`([:4326](../../YktVue/src/views/JcXx.vue#L4326))每次取 100 条,`lsJcxxList.length < count` 时允许继续加载;滚动到底部触发下一页。 +- 历史查询支持按“入寓时间/离寓时间”切换,日期输入兼容 `8`、`815`、`0815`、`081530` 等简写(2300 行附近)。 +- 录音:`getRecordingConfig()` 获取基地址,`splitRecordFiles` / `getRecordAudioUrl` / `playRecordVoice` 处理 `recordfile` 逗号分隔的多段录音。 +- 离寓抓拍:`leaveSnapshotPreviewUrl` 显示照片,加载失败时显示占位。 + +## 9. 代码级陷阱 + +| 陷阱 | 位置 | 影响 | +| --- | --- | --- | +| `setRowStyle` 内改 `this.message` | :3723 | 渲染期副作用,列表大时卡顿/提示错乱 | +| `formatTime` 定义两次 | :2276、:3153 | 后者覆盖前者,易误用 | +| `destroyed` 调用 `*websocketclose()` | :1743–1744 | 方法内 `location.reload()`,离开页面即刷新 | +| WebSocket `onclose` 无条件 `location.reload()` | :2086、:2198 | 后端重启/网络抖动会导致页面反复刷新 | +| 自动导出每 10 分钟 | :1792 | 值班机持续产生下载文件,可能占满磁盘 | +| 临叫 SIP 目标硬编码 | :4272 | 换 SIP 账号/网段必须改代码 | +| 语音临叫依赖 SIP 连接,喊话不依赖 | :153、:157 | 两条路径的前置条件不一致 | +| `handleUpdateJcXxConfirm` 只处理 `00000/11111` | :3353 | 其他错误码无提示 | +| `handleTabClick` 空实现 | :3779 | 两个 Tab 不改变数据,仅是视觉分组 | +| `handleDelete` 使用未安装的 `$modal` | :3034 | 该删除入口不可用 | + +## 10. 修改落点速查 + +| 需求 | 修改位置 | +| --- | --- | +| 增加列表列/状态 | 模板 183–240;状态映射 :194 | +| 改行颜色/闪烁规则 | `setRowStyle` :3723 | +| 增加行操作按钮 | 模板 234–239 + 新方法 | +| 改查询条件 | `JcXxQueryParams` :1546、顶部模板 21–46 | +| 改时间计算 | `test` :3901、`calculateJlxxTime` :3998、`buildFollowUpTimes` :3808 | +| 改临叫/停止逻辑 | `handleCalling*`、`handleRoomCall*` :4194–4310 | +| 改告警/TTS | `syncAlarmVoice` :2742、`enqueueSyncNotice` :2765、`playNextSyncSpeech` :2917 | +| 改轮询周期 | `mounted` :1790–1793 | +| 改历史查询 | `loadLsJcXx` :4326、历史弹窗 244–413 | diff --git a/docs/frontend/05-实时通信与语音告警.md b/docs/frontend/05-实时通信与语音告警.md new file mode 100644 index 0000000..810778d --- /dev/null +++ b/docs/frontend/05-实时通信与语音告警.md @@ -0,0 +1,284 @@ +# 05 实时通信与语音告警 + +前端有 5 条长连接:4 条业务 WebSocket + 1 条本机 SIP WebSocket。它们都不走 axios,地址在代码里硬编码,连接生命周期与页面绑定。 + +## 1. 连接清单 + +| 连接 | 地址 | 页面 | 用途 | 断线行为 | +| --- | --- | --- | --- | --- | +| 叫乘推送 | `ws://{apiRoot}/yktgl/jcwebsocket/{gyid}` | `JcXx.vue` | 叫乘状态、入寓人员、充值、同步提醒 | `location.reload()` | +| 交路推送 | `ws://{apiRoot}/yktgl/jlwebsocket/{gyid}` | `JcXx.vue` | 交路更新提醒 | `location.reload()` | +| 人员/入住推送 | `ws://{apiRoot}/yktgl/rlywebsocket/test33` | `shijinpeng/main.vue` | 触发自助登记 | `location.reload()` | +| 食堂推送 | `ws://{apiRoot}/yktgl/MealsWebsocket/test33` | `MealsOrdering.vue`、`paymentPlatform.vue` | 刷脸/刷卡回调 | `location.reload()` | +| SIP 客户端 | `ws://127.0.0.1:9004/sipclient/{gyid}` | `JcXx.vue` | 注册、呼叫指令 | 仅日志,不重连 | + +说明: + +- `apiRoot = window.location.host`,由 `public/config.js` 注入;地址里再拼 `/yktgl` 上下文。 +- **协议固定 `ws://`**,没有使用 `config.js` 的 `getWebSocketUrl()`(该函数支持 wss 但无人调用)。 +- 业务 WebSocket 没有心跳、没有指数退避重连,关闭即整页刷新。 +- `shijinpeng` 与食堂页面把 username 固定为 `test33`,多终端同时连接时后端广播会给所有 `test33` 会话,无法区分终端。 +- 后端路由规则见 [后端 07](../07-外部集成与定时任务.md#4-websocket):`jcwebsocket` 只投递给 `username == gyid` 或 `all` 的会话;`jlwebsocket`、`rlywebsocket`、`MealsWebsocket` 为广播。 + +## 2. 叫乘推送协议(jcwebsocket) + +消息体是 JSON 字符串,至少包含 `lx`(类型);`JcXx.vue:2097` 的 `switch` 分发: + +| `lx` | 附带的字段 | 前端行为 | 当前状态 | +| --- | --- | --- | --- | +| `ry` | `content` | 打开“入寓登记信息”弹窗并填充 | 启用 | +| `cz` | `content`、`id`、`deviceCode` | 打开“现金充值”弹窗,记录申请 id | 启用 | +| `cz_cancel` | `id` | 仅当 id 与当前弹窗一致时关闭,并提示“乘务员已取消” | 启用 | +| `zcxx` | `photoBase64`、`deviceCode`、`zcid` | 打开“注册人员信息”,进入新增人员流程 | 启用 | +| `xgxx` | `content`、`newPhotoBase64` | 打开“修改人员信息”,带原照片/新照片 | 启用 | +| `refresh` | — | 重新拉取叫乘列表 | 启用 | +| `autoCallFailed` | `content`、`roomNo` | 入同步提醒队列(标题“超时未叫乘提醒”)+ 刷新列表 | 启用 | +| `missedCallWarning` | `content`、`roomNo` | 入同步提醒队列 | 启用 | +| `routeChangeWarning` | `content`、`routeStatus` | 原为交路变更提醒 | **已注释禁用** | +| `routeExpiredWarning` | `content`、`roomNo` | 原为运安交路超时提醒 | **已注释禁用** | +| `callTimeResetWarning` | `content`、`roomNo` | 原为叫乘时间重置提醒 | **已注释禁用** | +| 其他 | — | 忽略 | — | + +后端文档 [06-接口清单](../06-接口清单.md#9-websocket-消息) 目前只列了前 5 类,`cz_cancel`、`autoCallFailed`、`missedCallWarning` 是前端实际已在处理、文档需补齐的消息。 + +### 2.1 人员登记链路 + +```mermaid +sequenceDiagram + participant D as 自助终端/设备 + participant B as 后端 + participant W as JcXx 值班台 + D->>B: 刷脸/读卡事件 + B->>W: {"lx":"zcxx", photoBase64, zcid, deviceCode} + W->>W: 打开注册人员弹窗,设置 ryxx_code='0' + W->>B: GET /jcgl/getRyxxByRyId?ryid=... + B-->>W: 人员信息(可能不存在) + W->>B: POST /jcgl/modifyRyXx?code=0 + B-->>W: 保存结果 +``` + +`xgxx` 走同一弹窗但 `ryxx_code='1'`,提交时 `code=1`。 + +## 3. 交路推送(jlwebsocket) + +`jlwebsocketonmessage`([JcXx.vue:2081](../../YktVue/src/views/JcXx.vue#L2081))不解析消息内容,只做两件事: + +```js +this.visible_jlxxtx = true +this.playVoice() // 播放 orderCompleteTip.mp3 +``` + +页面右下角出现“交路信息已更新”的浮动提示(`bottom-right-div`),点击后打开交路弹窗。消息内容、变更类型完全依赖用户再查询。 + +## 4. 告警链路 + +### 4.1 报警音状态机 + +相关字段: + +- `alarmVoiceActive`:是否正在播放报警音; +- `dismissedAlarmEvents`:已人工处理的事件 key 集合; +- `audioPlayerAlarm`:`