docs/:系统梳理、架构优化方案、CR-02 三端功能设计修订(含 §15 三端一致性同步与平台统计分析)、公寓端/应用端/平台端分册、公寓统计分析与排名报表、公寓端小步迭代开发计划(IT-0~IT-8) dev/:RuoYi-Vue-master(后端)与 RuoYi-Vue2-master(前端)官方上游基线,未改动
128 KiB
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 |
| v1.2 | 2026-11-07 | 回填公寓端设计的差异确认结果:表 2-2(不引入代码生成模块)、表 2-3(前端静态路由实现落点)、2.5 末段(操作域表名)、表 5-3(补归档表)、表 5-4(排序规则例外与隔离级别口径)、表 5-5(备份恢复行不再写非法枚举值)、表 5-6(隔离级别改部署待办、排序规则指向例外)、表 5-9(第 1、7 项口径)、表 7-4(增「落库载体」列) |
| v1.3 | 2026-11-09 | 同步 docs/backend/10 v1.3(CR-01「本地建单与本地调整」)口径:本地建单、apt_local_task、要素 / 时刻受控命令、D8' 时刻归属;按附录 D 核对第 3、4、6 章 |
| v1.4 | 2026-11-09 | 落实 docs/公寓端/00 对公寓端功能的重新规划:三端覆盖范围、交互协议与交互数据、模块与数据表设计、关键流程变更以 docs/设计变更/03-三端功能设计修订(CR-02).md 为权威增量(§3 端职责、§4 模块划分、§6 接口契约同步改判,详见 CR-02 §8 施工单);跨端接口仍为 20 个编号、不新增不重排,附录 B 图表索引不变;公寓本地库业务表口径统一为 58 张 |
| v1.5 | 2026-11-09 | 落实 docs/公寓端/00 v1.1 的 Asterisk 配置数据管理模块(分机表 / 呼叫记录表 / 录音文件表 / 注册在线状态表的字段、CRUD、权限)与 SIP 分机离线告警规则;补遗见 docs/设计变更/03 §10(CR-C15apt_sip_reg_status,公寓本地库业务表口径 58 → 59 张;§5.4 表 5-3 的 APT_SIP_ENDPOINT.register_status 枚举补充为 ONLINE/OFFLINE/UNREACHABLE/UNKNOWN,表 7-4 的 sync_state 四值状态机与附录 B 图表索引不变 |
| v1.6 | 2026-11-09 | 明确叫乘功能的两类实现方式及其业务流程:SIP 语音叫乘(Asterisk 主动呼叫、房间 SIP 终端接听入会后播放提示语音、状态依 AMI 事件经状态机上报)与平板叫乘(平板拉取任务、按 jcsj 本地定时播放、刷脸签到结束叫乘、结果上报公寓服务),含触发条件、交互流程、状态上报机制与结果反馈路径;流程权威表述为 docs/公寓端/05 §9,裁决与施工单见 docs/设计变更/03 §11(CR-C20 |
| v1.7 | 2026-11-09 | 新增 docs/公寓端/10「人脸数据下发管理」模块(AF-01AF-06:注册与平台同步、平台下载入库、签到设备下发、消费 / 叫乘按设备范围隔离、离寓自动擦除、长期驻留保留);注册链路按 CR-C25 改判为「签到终端发起注册申请并上传人脸 → 值班员在值班台完成人员注册 → 注册成功后人脸进入公寓服务系统 → 分别上传平台与下发公寓内设备」,两条路径解耦并行、互不阻塞,平台 personRegVerdicts[] 降级为主档同步回执;人员主数据照片与特征值按 CR-C24 允许平台 ↔ 公寓双向同步,业务抓拍、录音与签到现场图仍不出公寓。裁决 / 流程 / 施工单见 docs/设计变更/03 §12(CR-C24v2-draft、平台零回调;§7.7 / §7.9 表 7-5、表 7-6 与附录 B 图表索引不变 |
| v1.8 | 2026-11-10 | 新增 docs/公寓端/11「人员入住作业流程」(CI-01CI-05):注册与签到、入住申请(房间 / 交路)、值班员审核与房间调整、#9 CHECKIN 提报与平台运安匹配(#6 / #8 / #7)、#7 任务查询进入叫乘;含三条状态链、apt_local_task 补列结构、端内接口 N-38gy_jcxx 状态字段、不新增业务表(公寓本地库仍 59 张);jcsj 仍恒由公寓按规则快照复算、apt.duty.state 仍是状态字段单写入口,match_state 沿用正式五值。裁决 / 流程 / 施工单见 docs/设计变更/03 §13(CR-C26v2-draft、平台零回调;§7.7 / §7.9 表 7-5、表 7-6 与附录 B 图表索引不变 |
| v1.9 | 2026-11-10 | 新增 docs/公寓端/12「消费、充值与菜品管理」(CF-01CF-03):人员消费(选餐加菜显示金额 → 查补贴 / 现金额度 → 补贴优先扣款、不足现金补足 → 上报公寓端 → 同步平台并落入人员账户)、人员线下充值(仅线下现金、扫码只识别公寓、系统只记账,管理员核对后计入现金额度并同步平台)、公寓菜品管理(阶段性食谱发布 → 消费终端查看菜品并加入选择菜单 → 远程点菜仅预留入口);含四条状态链、双额度 CAS 扣减与"一笔消费只一条流水"、gy_ykt_* / apt_ledger_offline / YKT_MEALS 补列、端内接口 N-48docs/设计变更/03 §14(CR-C31bizType,公寓本地库仍 59 张;apt.ledger.local / platform-ledger 各自单写入口不变,平台零回调、契约 v2-draft;§7.7 / §7.9 表 7-5、表 7-6 与附录 B 图表索引不变 |
| v2.0 | 2026-11-10 | 三端一致性同步 + 平台统计分析(CR-02 §15):把公寓端已完成的平板叫乘(§11)、人脸签到(§12)、消费与菜品管理(§14)在应用端 / 平台端同步落地,统一业务规则、交互流程、权限控制与异常处理;三端状态映射与数据同步机制以 CR-02 §15 表 3-66 为唯一权威。新增 docs/平台端/10「公寓统计分析与排名报表」:以 gyid 为单位分类统计接待能力(入住人数 / 叫乘人数 / 评价情况 / 报警情况)、补贴发放、补贴消费、现金充值(计为收入)、房间利用周转,按月 / 季 / 年生成报表并对各公寓做分类 + 综合排名。新增裁决 CR-C36v2-draft、平台零回调、公寓本地库仍 59 张、不新增 bizType / 公寓业务表 / 对象存储桶;平台库新增 pl_apt_stat_period / pl_apt_stat_rank 2 张(累计 5 张,另计),统计结果不回写任何业务状态,apt.duty.state / platform-ledger / platform-report 三个单写入口不变;§7.7 / §7.9 表 7-5、表 7-6 与附录 B 图表索引不变 |
表 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-1、4-3 | 能力归属表补入“语音资源”与“语音事件/录音”两条;公寓端模块清单追加 4 个语音模块 |
| 新增 | 4.4 / 图 4-1 / 表 4-4 | 语音相关模块与依赖方向 |
| 新增/修订 | 5.4 / 表 5-1、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 | 术语、图表索引、代码证据、维护约定同步更新 |
v1.2 回填说明(2026-11-07)
本次回填的唯一依据是 docs/公寓端/08-待决策与风险.md §4 表 8-8 与 §5 的确认结果,评审口径为:表 8-8 的 8 处差异全部采纳(其中差-1 保留库默认 utf8mb4_general_ci、差-3 不引入代码生成模块、差-7 采用前端静态路由);docs/公寓端 表 3-16 的 G-01(房间楼栋列)、G-02(人员本地启停列)、G-03(交路停用原因列)三项字段缺口,本期一律走“不改库兜底”、暂不新增字段,因此本文不新增任何字段级口径。
本次只改以下 9 处表述,其余内容(尤其第 3、4、6 章结论与表 10-2 中 D1–D16 的待决状态)不动:
- 表 2-2「后端框架」行(差-3):模块清单去掉
ruoyi-generator,明确不引入代码生成模块。 - 表 2-2「定时任务」行(差-3):Quartz 依托公寓端执行引擎模块落地,不单独建框架子模块;JobStore 仍持久化到
sys_job。 - 表 2-3「前端路由」行(差-7):补“实现落点”——
entry-duty用sys_menu驱动的动态路由,entry-apt用前端静态路由表、sys_menu仅作权限点载体。 - 2.5 末段(差-4):操作域表名
DUTY_OPERATION更正为duty_operation_log(同族duty_manual_action、duty_shift_handover)。 - 表 5-3(差-5):补归档表
APT_CALL_EVENT_HIS一行,语音域由 10 张补为 11 张。 - 表 5-4「字符集与排序」行 + 表 5-6「大小写」「字符集」两行(差-1):统一追加排序规则例外说明(库默认
utf8mb4_general_ci),覆盖表 5-9 第 7 项的逐列要求。 - 表 5-6「事务与锁」行 + 表 5-4「事务隔离」行(差-2):
READ-COMMITTED由“实例级固定/显式设”改为“部署待办”,应用侧不以隔离级别为正确性前提。 - 表 5-9 第 1、7 项 + 表 7-4(差-6 / 差-1 / 差-8):第 1 项口径改为 44 张业务表 + 30 张若依表;第 7 项改为库默认统一 + 例外清单;表 7-4 增加“落库载体”列,7 态映射为
sync_state4 值 +enabled+deleted。 - 表 5-5「备份恢复/版本升级」行(差-8 连带):
sync_state全量置UNKNOWN改为置PENDING并清applied_version,避免写入 CHECK 之外的值被拒。
以上改动均不新增图/表编号,附录 B 图表索引无需变更;逐条差异的原始依据与影响面仍以 docs/公寓端/08 表 8-8 为准。
目录
- 1. 现状评估
- 2. 目标架构
- 3. 端职责边界
- 4. 领域与模块划分
- 5. 数据架构
- 6. 接口与集成
- 7. 设备接入方案
- 8. 部署与运行
- 9. 迁移与实施路径
- 10. 风险与决策点
- 11. 结论与优先级
- 附录 A 术语表
- 附录 B 图表清单
- 附录 C 代码证据索引
- 附录 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<sessionId, endpoint>,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 与目标架构不一致的核心点
- “平台—公寓”未分层,只靠
centerServerUrl字符串互调。注册、交路两条链路都是“同一套 Controller 既当设备接口又当中心接口”,无版本、无契约、无重试与幂等。 - 公寓身份靠 IP 推断且多公寓共享库。这是“一实例多公寓”的妥协设计,直接阻碍“公寓本地数据本地存”。
- 叫乘执行、设备接入、平台接入三者同进程。平台不可达会拖累叫乘主链路,叫乘高峰也会挤占同步线程(
AsyncConfig核心 10 / 最大 200 / 队列 10,队列极小)。 - 账务散落。余额在公寓库被直接更新,餐饮、售货机、充值三条链路各自改余额,缺少统一流水与对账口径。
- 设备通道不可靠。一次性投递 + 全局口令 + 无 ACK,是弱网现场最典型故障源。
- 状态机与视图耦合。
ryzt由设备上报、Quartz、AMI 事件、人工操作共同写入,缺少单一写入口;应用端与公寓端同库后风险被放大。
2. 目标架构
2.1 逻辑架构
图 2-1 目标逻辑架构
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 部署拓扑
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
拓扑要点
- 平台端不进公寓内网:所有跨端流量由公寓端主动出站发起,规避现场 NAT/防火墙,并让平台与设备链路彻底解耦。
- 公寓端 = 部署单元 = 公寓隔离单元:一公寓一实例一库(或独立 schema),
gyid由部署参数固定。 - 应用端不新增进程与库:值班员访问
http://<公寓服务器>/duty,与公寓端同 JVM、同库、同 Nginx。 - 平台端可多实例;公寓端的 WebSocket 为进程内实现,因此公寓端须保持单实例(或先完成广播改造再扩容)。
2.3 三端调用关系与数据流
表 2-1 三端调用关系与数据流
| 调用方向 | 协议 | 触发方式 | 典型数据 |
|---|---|---|---|
| 应用端 → 公寓端 | 进程内 Service 调用 | 同步 | 人工干预、房态修改、审核动作 |
| 公寓端 → 平台端 | HTTPS 主动拉取 | 定时(如 5 分钟)+ 变更通知 | 主数据增量、乘务计划、规则版本 |
| 公寓端 → 平台端 | HTTPS 主动推送 | 近实时批量 + 重试 | 执行结果、账务流水、告警摘要、设备摘要、水位 |
| 设备 → 公寓端 | 内网 HTTP(后续可评估 MQTT) | 心跳 + 事件 | 指令取走、业务上报 |
| 平台端 → 运安 | SOAP | 定时 | 乘务计划 |
| 平台端 → 中心/财务 | HTTPS | 定时/按需 | 财务汇总、跨公寓人员查询 |
| 平台端 → 公寓端 | 不允许直连 | — | 仅落“待下发配置版本”,由公寓端拉取 |
2.4 技术基线(确定前提)
本节把公寓端与应用端的技术选型固定为约束条件,后续章节不再讨论替代方案;与基线冲突的现有实现按第 9 章处理。
表 2-2 公寓端与应用端技术基线
| 项 | 基线选择 | 约束说明 | 与现状的关系 |
|---|---|---|---|
| 后端框架 | RuoYi-Vue 前后端分离版(Spring Boot 多模块:ruoyi-admin / ruoyi-framework / ruoyi-system / ruoyi-common / ruoyi-quartz) |
只在 ruoyi-* 之上增加业务模块,不修改框架自身的认证、权限机制;不引入代码生成模块 ruoyi-generator(表结构已定稿,生成代码风格与手写不一致,维护成本高于收益) |
现有 yktgl 为单模块 Spring Boot 2.6.13;阶段零先修 pom.xml 版本错配,使两侧依赖可对齐 |
| 前端框架 | Vue 2 + Element UI(RuoYi-Vue 默认栈),沿用 Vuex + Vue Router | 一个前端工程两个入口,不引入第二套 UI 库 | YktVue 的 src/utils/{request,permission,ruoyi}.js、src/plugins/* 已是 RuoYi 前端骨架,请求与权限层可直接复用 |
| 数据库 | MySQL 8.0,字符集 utf8mb4,引擎 InnoDB |
一公寓一库;不允许与其他公寓共库 | 现网为 KingbaseES(Oracle 兼容模式),切换影响见 5.6 |
| 持久层 | RuoYi 默认 MyBatis + mapper/*.xml;存量 MyBatis-Plus 代码允许暂留 |
新模块统一 MyBatis XML;MyBatis-Plus 仅作过渡,不新增用法 | 现有 yktgl 用 MyBatis-Plus 3.1.0,需在阶段零把 DbType 与分页方言切到 MySQL |
| 定时任务 | Quartz + sys_job(ruoyi-quartz 的任务定义与调度能力) |
任务定义入库、支持暂停/恢复/立即执行;公寓端单实例运行;Quartz 依赖落在公寓端执行引擎模块,不单独建框架子模块,JobStore 仍持久化到 sys_job |
现有 quartz/WakeUpJob + SchedulerManager 为内存 JobStore,重启只恢复未来任务,须一并改造 |
| 权限 | RuoYi RBAC(sys_user / sys_role / sys_menu / sys_dept) |
公寓端本地账号体系;平台账号不进公寓端 | 替代现有 display + gl_* 自研 RBAC;两套体系通过“人员—账号映射”打通 |
| 缓存与会话 | 本地 Redis | 会话、令牌、设备 nonce、指令去重 | 现有 spring-session-data-redis;与 Boot 3.x starter 的版本错配需在阶段零修复 |
| 语音 | asterisk-java(现有定制版 3.38.0-zfjia)+ AMI + Conf-01 会议桥 |
只在公寓端使用,平台端不依赖 | 现有 AsteriskAmiConfig / AsteriskCallUtil 直接演进,不重写协议层 |
| 媒体 | Asterisk 本地录音 + 本地 HTTP 目录;TTS 走 Playback(tts) |
不引入独立媒体服务器或流媒体服务 | 现有 callcontrol.recording.base-url 已指向 http://<ami.host>/conf/ |
| 部署 | 单 jar + Nginx + 本地库 + Asterisk 同机 | 不引入注册中心、消息中间件、独立网关 | 与现有“一公寓一套部署”一致 |
基线带来的三条硬约束:
- 框架不魔改:
ruoyi-framework的鉴权链、异常处理、分页插件不改,业务差异集中在apt-*模块,升级 RuoYi 时不必逐处合并。 - 一套代码两个入口:应用端不是独立工程,任何“只给值班员用”的能力必须落在同一 jar 内的
apt-duty模块与前端duty入口,不允许起第二个服务。 - 语音只在公寓内闭环:平台端不得成为媒体或 AMI 的中转节点,跨端只有“配置版本下行”与“质量摘要上行”两类流量。
2.5 应用端与公寓端的复用实现
复用不是“两个入口指向同一个地址”,需要在工程结构、路由与权限、部署形态三层各有明确落点。
图 2-3 复用形态
flowchart TB
subgraph REPO["单仓库 / 单 Maven 多模块"]
ADM["apt-admin(唯一可执行 jar,启动类与配置)"]
DUTY["apt-duty(值班操作 API、人工干预命令)"]
DISP["apt-dispatch(叫乘执行引擎、Quartz、状态机)"]
VOICE["apt-voice(AMI、分机、事件、录音媒体)"]
DEV["apt-device(设备网关、指令队列)"]
STAY["apt-stay(入住/离寓/房态)"]
SYS["apt-system(RuoYi RBAC、账号、菜单、日志)"]
FND["apt-framework / apt-common(拦截器、gyid 注入、错误码)"]
REP["apt-report(outbox 上报)"]
MC["apt-master-client(平台下行)"]
OPS["apt-ops(自检、健康检查)"]
end
subgraph FE["单前端工程,双入口"]
EA["entry-apt:设备 / 终端 / 运维页"]
ED["entry-duty:值班员盯控与操作页"]
SH["shared:api / store / components / dict"]
end
ADM --> SYS
ADM --> DUTY
ADM --> DEV
ADM --> OPS
DUTY --> DISP
DUTY --> STAY
DISP --> VOICE
DISP --> STAY
DEV --> DISP
DISP --> REP
REP --> MC
EA --> SH
ED --> SH
表 2-3 复用维度与实现落点
| 维度 | 复用方式 | 具体落点 | 负面清单(不允许) |
|---|---|---|---|
| 代码库 | 单 Git 仓库、单 Maven 多模块、单可执行 jar | apt-admin 是唯一打包入口;应用端 = apt-duty 模块 + 前端 duty 入口 |
不允许为应用端再建一个启动模块或第二套 jar |
| 数据库 | 同一公寓库、同一 DataSource、同一事务管理器 |
执行域表(GY_*)与操作域表(DUTY_*)同库不同前缀,可处于同一本地事务 |
不允许为应用端建第二数据源、第二库或读写分离副本 |
| 模块依赖 | 单向依赖:apt-duty → apt-dispatch / apt-stay → apt-voice |
应用端只发“命令”与“查询”,状态由 apt.duty.state 唯一写入 |
不允许 apt-dispatch / apt-voice 反向依赖 apt-duty;不允许应用端绕过状态机直写 ryzt 等字段 |
| 后端路由 | 按前缀分区:/apt/**(管理/终端/运维)、/duty/**(值班员)、/system/**(RuoYi 框架)、/open/**(跨端与设备,独立鉴权) |
各前缀独立拦截器链:/duty/** 走会话 + 权限点 + gyid 数据域;/open/** 走设备签名或 mTLS |
不允许 /duty/** 出现在设备可达的接口清单里;不允许 /open/** 复用会话鉴权 |
| 前端路由 | 同一工程、同一次构建,两个 entry,路由按角色过滤 |
实现落点:① entry-duty 的路由由 sys_menu 驱动的动态路由生成(菜单即路由);② entry-apt 使用前端静态路由表(在 entries/apt/router 内按功能域声明),sys_menu 对 apt 入口仅作权限点载体,不参与路由生成;③ 两个入口共用 shared 的路由守卫与异常页,路由表各自维护;④ 按钮级一律 v-hasPermi 校验权限点,前端隐藏不等于后端放行 |
不允许两个 entry 互相 import 页面;共用只走 shared;不允许为了让 apt 入口“能显示菜单”而伪造 sys_menu 记录 |
| 菜单与权限点 | RuoYi 菜单树 + 权限点(如 duty:call:stop、duty:room:swap) |
值班菜单挂 duty 目录,运维菜单挂 apt 目录;按钮级复用 v-hasPermi |
不允许以“前端隐藏”代替后端校验;不允许只有前端判断角色 |
| 鉴权与身份 | 值班员用本地 RuoYi 账号;gyid 作为登录用户扩展属性写入令牌 |
RuoYi LoginUser 扩展 gyId;数据范围扩展为“按 gyid 过滤”;服务层拦截器强制注入 |
不允许再出现“从请求 IP 推断公寓”;getGyIdByIp 只作旧设备兜底并打告警 |
| 部署形态 | 单进程、单 Nginx、单端口对外 | Nginx location /duty 与 /apt 指向同一 SPA,/prod-api 反代同一后端;/ws/duty 与 /ws/device 两个 WebSocket 端点 |
不允许为应用端单独扩容(推送为进程内状态、Quartz 单实例);扩容前先完成推送广播与任务抢占改造 |
| 发布节奏 | 一次发布同时生效 | 应用端与公寓端同版本号、同发布窗口;接口变更至少向前兼容一个版本 | 不允许跨越破坏性变更只发前端不发后端 |
同库边界如何避免互相踩:apt-duty 只能通过领域服务写执行域,事务边界由 apt-dispatch 提供;值班员的每次操作落操作域 duty_operation_log(同族表:duty_manual_action 人工干预、duty_shift_handover 交接班),记录“谁在什么时间对哪个对象下了什么命令”,并通过 commandId 与执行域的状态变更关联。这样即使两侧同库,也能从审计上区分“人下的命令”与“系统产生的状态”。
3. 端职责边界
CR-02 指针(v1.4,2026-11-09):依据
docs/公寓端/00的公寓端功能重新规划,第 3 章(端职责边界)、第 4 章(领域与模块划分)、第 6 章(接口与集成)的增量与口径改判以docs/设计变更/03-三端功能设计修订(CR-02).md为权威,逐处施工单见 CR-02 §8。要点:① 入住 / 换房 / 离寓与账务本地流水由"后续 / P2"调整为本期;② 平板叫乘、换乘、保洁、排班考核、上报历史续报纳入三端覆盖范围;③ 跨端仍为 20 个接口编号、不新增不重排,新数据走payload+bizType扩展(契约v2-draft);④jcsj恒由公寓复算(R-C9 / R-C10);⑤ 公寓本地库业务表口径 v1.4 为 58 张、v1.5 补遗后为 59 张(见docs/公寓端/00§3、CR-02 §10)。本章及以下原有结论中与上述要点冲突处,以 CR-02 表 3-4 为准;⑥ v1.7 人脸数据下发模块(CR-02 §12)不新增业务表与跨端编号,人员主数据人脸按 CR-C24 允许跨端、注册链路按 CR-C25 改判为「签到终端申请 → 值班员注册 → 入公寓服务系统 → 分别上传平台 / 下发公寓内设备」,59 张表口径与契约v2-draft不变;⑦ v1.8 人员入住作业流程(CR-02 §13)不新增业务表、不新增gy_jcxx状态字段与跨端编号,"已入住"为流程态 + 业务事实双重表达,jcsj公寓复算与apt.duty.state单写入口不变;⑧ v1.9 消费 / 充值 / 菜品管理(CR-02 §14)三项流程不新增业务表、不新增状态字段、不新增跨端编号与bizType,复用gy_ykt_*/apt_ledger_offline与平台库YKT_MEALS补列,apt.ledger.local/platform-ledger各自单写入口不变,仅线下充值不接支付渠道、远程点菜仅预留入口,权威表述见docs/公寓端/12;⑨ v2.0 三端一致性同步与平台统计分析(CR-02 §15)不改变公寓端四项能力的权威表述,只在应用端 / 平台端同步业务规则、交互流程、权限控制与异常处理,三端状态映射与数据同步机制以 CR-02 §15 表 3-66 为唯一权威;#13 摘要按payload扩展评价 / 房间周转分组计数,不新增bizType与上报接口;新增docs/平台端/10以gyid为单位的五类分类统计、月 / 季 / 年报表与公寓排名,平台库新增 2 张另计(累计 5 张)、公寓本地库仍 59 张,统计结果不回写任何业务状态,apt.duty.state/platform-ledger/platform-report三个单写入口不变,变更点与影响范围见 CR-02 §15 表 3-63;附录 B 图表索引不变。
3.1 平台端
负责
- 公寓档案、房间基线(
gyid + room_no + 床位数)、人员主数据与照片、交路模板与叫乘时刻、时间规则、节假日、餐补/补助标准。 - 运安计划的唯一入口(替代现状各公寓分别抓取),并将计划转换为叫乘任务/计划快照下发对应公寓。
- 账务主账:补助发放规则、余额主账、消费/充值/补助流水归集、跨端对账。
- 餐饮业务(菜品、订单、后厨、结算规则),但结算扣款必须经账务接口,不直接改公寓库余额。
- 账号、角色、权限、操作审计、跨公寓报表、设备在线率与执行质量监控、告警汇总。
- 接收各公寓执行结果与异常补偿请求,维护每公寓水位与对账结果。
不负责
- 不接入现场设备、不下发设备指令、不持有设备心跳流水。
- 不执行叫乘(不连 AMI、不控串口、不播 TTS)。
- 不直连公寓本地库,不做跨库 JOIN。
- 不承担房间实时房态(仅保留基线,实时占用以公寓端为准)。
3.2 公寓端
负责
- 本公寓设备接入:注册、认证、心跳、指令队列、事件受理、离线缓存。
- 本公寓叫乘执行全链路:任务创建、Quartz 调度、AMI 会议桥、TTS、串口广播、复叫/报警/停止、重启恢复与漏叫补偿。
- Asterisk 资源全生命周期:SIP 分机与中继的新增、修改、停用、删除;分机注册状态与在线判定;分机与房间、值班台、广播终端的绑定关系;分机台账与 Asterisk 实际配置的收敛与对账。
- Asterisk 事件闭环:AMI 连接建立与断线自愈、事件订阅与解析、通话与会议状态跟踪(呼入/接通/入会/离会/挂断/异常)、呼叫失败与超时识别、录音与 TTS 的触发时机与结果回写。
- 会议桥与媒体:会议桥粒度与占用判定、TTS 文本排队与播放结果、录音落盘与索引、录音留存与清理。
- 在寓事实与房态(
GY_JCXX语义)、换房、退房、保洁、超时离寓报警。 - 本地主数据缓存与规则快照(人员照片、交路、时间规则、节假日),保证断网可叫乘。
- 本地账务流水与离线消费受理(缓存余额),向平台归集。
- 结果上报:执行结果、流水、告警摘要、设备摘要、归档摘要、水位。
不负责
- 不直连运安(运安仅由平台接入),不维护跨公寓人员全集。
- 不决定补助标准,不做跨公寓统计,不维护平台账号体系。
- 不制定园区级设备策略(策略由平台配置,执行在公寓)。
- 不制定跨公寓的号段规划、中继路由策略与录音合规策略(平台给规则,公寓落地执行)。
- 不经平台中转运维语音媒体流;外线呼入业务规则由本地 Asterisk 拨号计划定义,公寓端只做事件观测与关联。
- 不评估录音内容合规性,只执行留存期与访问控制策略。
3.3 应用端(与公寓端同代码同库)
负责
- 叫乘盯控:任务列表、报警、应答确认、临时叫乘、房间/多房间喊话、停止叫乘、录音回放入口。
- 房间与房态:选房、换房、停用/恢复、保洁开始/结束、在寓人员查看。
- 人工干预与审核:充值确认、人员资料修正、交路人工指定与重置、备注与操作留痕。
不负责
- 不直接改设备指令表、不直接改叫乘任务表(必须经公寓端领域服务)。
- 不持有独立数据库,不引入第三套服务,不做独立鉴权中心。
- 不承担跨公寓查询(跨公寓看平台端)。
表 3-1 应用端与公寓端的逻辑分工
| 维度 | 公寓端(执行域) | 应用端(操作域) |
|---|---|---|
| 表归属 | 现有 GY_JCXX、GY_SB*、GY_SYN_JCXX 等执行与设备表(可加 apt_* 前缀新表) |
新增 DUTY_*:操作审计、人工干预记录、审核动作、备注、班次交接 |
| 写权限 | 仅执行引擎/设备网关写状态字段(ryzt、jcsj_s、finish_jc 等) |
仅通过状态机服务写,禁止直连 Mapper 修改状态字段 |
| 部署形态 | 同一 JVM 内的 apt-dispatch(叫乘引擎)+ apt-voice(语音域) |
同一 JVM 内的 apt-duty,依赖公寓端领域接口,禁止反向依赖 |
| 前端形态 | 同一前端工程的 apt 入口(设备/终端页) |
同一前端工程的 duty 入口(值班员页),按角色裁剪路由与菜单 |
| 鉴权 | 设备用设备凭据;值班员用本地 RuoYi 账号 | 复用公寓端 RuoYi 账号 + 数据范围锁定 gyid |
公寓隔离方式
- 部署期固定
apartment.gyId,服务启动即校验;gyid不再从请求 IP 推断,getGyIdByIp降级为旧设备兼容兜底并打告警。 - 库级隔离:一公寓一 schema/库 + 独立连接账号,账号仅对本库有 DML 权限,对平台库无任何权限。
- 请求级隔离:值班员登录后
gyid写入会话与令牌声明,服务层用拦截器强制注入,业务 SQL 一律带gyid条件。 - 设备级隔离:设备注册时绑定
gyid + deviceCode,跨公寓deviceCode直接拒绝。
3.4 语音链路上的三端分工
表 3-2 语音/分机/事件的三端分工(● 主责 ○ 参与 — 不参与)
| 环节 | 平台端 | 公寓端 | 应用端 | 说明 |
|---|---|---|---|---|
| 号段与编号规则 | ● | ○ 取规则快照 | — | 平台定“房间号 ↔ 分机号”映射规则,公寓端按规则生成并校验 |
| 中继与路由策略 | ● 定策略 | ● 落地配置 | — | 外线呼入/呼出的业务规则由公寓本地拨号计划承载 |
| 分机资源增删改停 | — | ● | ○ 界面发起 | 应用端只发命令,apt.voice.endpoint 落库并收敛到 Asterisk |
| 分机与房间/值班台/广播绑定 | — | ● | ○ 界面发起 | 换房、改值班台、调整广播区改绑定,不改分机号 |
| 注册状态与在线判定 | ○ 汇总 | ● 采集 | ● 展示 | 平台只收“注册率/离线清单”摘要 |
| 叫乘发起与会议桥 | — | ● | ○ 人工触发 | 语音链路不经平台中转 |
| TTS 文本生成 | — | ● | ○ 单次改写 | 模板可由平台配置,生成与播放在公寓端 |
| 录音落盘与索引 | — | ● | ○ 回放 | 平台只收索引摘要与缺失数 |
| 录音留存与访问控制 | ● 定策略 | ● 执行 | ○ 申请导出 | 合规口径见待决策 D15 |
| 语音质量与故障 | ○ 汇总 | ● 判定 | ● 展示 | 注册率、呼叫成功率、录音缺失、AMI 断连次数 |
| 外线呼入业务 | — | ● 观测/关联 | ○ 查看 | 呼入由 Asterisk 拨号计划处理,公寓端只跟踪会话与关联房间 |
一句话边界:平台管“号码规则与合规策略”,公寓管“资源、事件与媒体”,应用端管“人看与人工干预”。
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 |
自检、时钟偏差、磁盘(抓拍/录音)、日志、版本 | 新增 |
apt.voice.ami |
AMI 连接建立与守护、重连退避、命令发送与超时、事件分类分发 | AsteriskAmiConfig + AsteriskCallUtil 连接部分 |
apt.voice.endpoint |
分机与中继台账、绑定关系、期望态→实际态收敛、对账与漂移 | GY_ROOMS.sip_account 单列、sip_status 从未写入 |
apt.voice.event |
会话与会议状态机、失败/超时识别、录音与 TTS 触发、结果回写 | AsteriskCallUtil 事件回调 + 内存 recordlist |
apt.voice.media |
录音索引与清理、TTS 文本队列与模板、播放结果回写 | GY_JCXX.recordfile 逗号拼接 + conftts 单槽位 |
4.4 语音相关模块与依赖方向
图 4-1 语音模块依赖方向
flowchart LR
subgraph CALL["叫乘执行"]
M1["apt.duty.engine<br/>任务/调度/复叫"]
M2["apt.duty.state<br/>状态机唯一写入口"]
end
subgraph VOICE["语音域"]
V1["apt.voice.event<br/>会话状态机/失败识别"]
V2["apt.voice.ami<br/>连接/事件分发"]
V3["apt.voice.endpoint<br/>分机中继台账/收敛对账"]
V4["apt.voice.media<br/>录音索引/TTS 队列"]
end
subgraph SIDE["相邻域"]
D1["apt.device.gateway<br/>设备网关"]
D2["apt.master.cache<br/>平台配置快照"]
D3["apt.report.outbox<br/>上报"]
D4["apt.duty.view<br/>值班 API"]
end
AST["Asterisk(AMI + 会议桥 + 录音目录)"]
SPK["串口广播"]
M1 --> V1
M1 --> D1
M1 --> SPK
M1 --> M2
V1 --> V2
V1 --> V4
V1 --> M2
V3 --> V2
V2 --> AST
V4 --> AST
D2 --> V3
D4 --> V1
D4 --> V3
V1 --> D3
表 4-4 语音模块职责与依赖约束
| 模块 | 核心职责 | 允许依赖 | 禁止依赖 | 现状来源 |
|---|---|---|---|---|
apt.voice.ami |
连接建立与守护、重连退避、命令发送与超时、事件按类型分发 | apt.common、配置 |
任何业务模块(不得反向调用叫乘/设备/值班) | AsteriskAmiConfig(Bean 内单次 login(),失败仅打印) |
apt.voice.endpoint |
分机/中继台账、绑定、期望态→实际态收敛、对账与漂移 | apt.voice.ami、apt.master.cache |
apt.voice.event、apt.duty.* |
GY_ROOMS.sip_account 可写但无台账、无状态 |
apt.voice.event |
会话与会议状态机、失败/超时识别、录音与 TTS 触发、结果回写状态机 | apt.voice.ami、apt.voice.media、apt.duty.state |
apt.device.*、apt.report.*、apt.duty.view |
AsteriskCallUtil 的 recordlist 内存跟踪 |
apt.voice.media |
录音索引与清理、TTS 文本队列与模板、播放结果回写 | 存储、apt.common |
apt.duty.* |
GY_JCXX.recordfile 逗号拼接、conftts 单槽位 |
apt.duty.engine |
任务领取、Quartz 调度、复叫/报警/停止 | apt.voice.event、apt.device.command、串口 |
直连 AMI(必须经 apt.voice.*) |
WakeUpJob、SchedulerManager |
apt.duty.state |
状态机唯一写入口 | apt.common |
全部语音/设备模块(只能被调用) | JcglService.updateJcxxRyzt 被多处直接调用 |
apt.duty.view |
值班 API、WebSocket、人工干预命令 | apt.voice.endpoint、apt.voice.event、apt.duty.state |
直接写状态字段 | JcglController |
依赖方向的三条硬规则:
- 事件单向下行:
apt.duty.engine → apt.voice.event,语音层不得回调叫乘层;需要通知执行层时,以“状态机命令”形式交给apt.duty.state,避免双向依赖。 - AMI 只在最底层:只有
apt.voice.ami持有ManagerConnection,其他模块不得自行建连(现状AsteriskCallUtil既是连接持有者又是业务逻辑,必须先拆)。 - 设备与语音互不感知:广播终端、房间屏走设备网关,话机走语音域;两者只在“同房间、同任务”这一业务层通过
task_id关联。
5. 数据架构
5.1 数据分层与归属
表 5-1 数据分层、归属与同步方向
| 数据层 | 典型数据 | 归属 | 存储位置 | 同步方向 |
|---|---|---|---|---|
| 平台主数据 | 公寓、房间基线、人员、照片、交路、时间规则、节假日、餐补标准 | 平台 | 平台主库 + 对象存储 | 下行:平台 → 公寓(版本化增量) |
| 平台计划数据 | 运安计划快照、生成的叫乘任务/时刻、变更与取消 | 平台 | 平台主库 | 下行:平台 → 公寓 |
| 公寓执行数据 | GY_JCXX(在寓/叫乘事实)、GYGL_LY_EVENT、房态、保洁、报警 |
公寓 | 公寓本地库 | 本地强一致;摘要上行 |
| 设备数据 | GY_SB、GY_SB_XT、指令队列、心跳 |
公寓 | 公寓本地库 | 仅上行摘要(在线率/版本/故障) |
| 账务数据 | 补助明细、消费/充值流水、余额 | 主账平台 / 流水双记 | 平台主库 + 公寓本地缓存 | 流水上行;对账后余额快照下行 |
| 归档数据 | GY_JCXX_HIS、离寓事件历史 |
公寓 | 公寓本地库 | 上行归档摘要 |
| 账号与审计 | 账号、角色、权限、操作审计 | 平台(平台侧)/ 公寓(本地侧) | 各自库 | 人员—账号映射下行 |
| 语音与媒体数据 | 分机与中继台账、会话与事件、录音索引、TTS 单元、配置版本与漂移 | 公寓 | 公寓本地库 + 本地磁盘 | 本地强一致;仅上行质量与应用摘要 |
| 汇总与报表 | 跨公寓统计、财务汇总 | 平台 | 平台主库/数仓 | 由上行数据加工 |
5.2 一致性策略
表 5-2 各场景一致性策略
| 场景 | 策略 |
|---|---|
| 公寓内业务(入住、离寓、房态、叫乘状态) | 强一致:单库本地事务 + 状态机唯一入口 |
| 主数据/规则下发 | 最终一致 + 版本号:公寓端保存 masterVersion,版本不一致时拒绝接收任务并触发全量重拉 |
| 执行结果上报 | 最终一致 + 至少一次 + 幂等:outbox 表 + idempotencyKey,平台按 (gyid, bizType, bizId, bizVersion) 去重 |
| 账务 | 本地记账为准的最终一致:本地先落流水(离线可用),平台归集后按日对账,差异进入人工处理队列 |
| 余额 | 平台主账权威;公寓端缓存带“版本 + 最后对账日期”,超期未对账时限制高额操作(如大额充值与退款) |
| 任务下发与执行 | 平台下发“计划 + 规则版本”,公寓端复算并回执 taskAck;不一致以平台为准并告警 |
5.3 同库边界防混乱
- 表归属清单(Ownership Matrix):每张表标注
owner=platform|apartment|duty、允许的写入模块、是否为上报源;越权写入纳入 Code Review 检查项。 - 命名分区:执行域沿用
GY_*(避免大规模迁移),应用域新增DUTY_*(操作审计、人工干预、审核、交接班);报表/只读视图统一V_*,禁止跨域直写。 - 模块依赖单向:
module-duty → module-apartment → module-platform-client,禁止反向依赖;用 Maven 模块或架构约束测试固定。 - 禁止跨端 SQL 关联:不在公寓库写“需要平台数据才能算”的统计 SQL,一律先落到本地快照。
gyid强制注入:拦截器统一注入并校验,任何 SQL 缺gyid视为缺陷;新代码统一走上下文,不再逐处解析 IP。- 状态字段单写入口:
ryzt、finish_jc、finish_ly、stoped仅允许apt.duty.state写入,设备上报、Quartz、AMI 事件、人工操作统一转为状态机命令。
5.4 语音域数据设计
语音域数据全部落在公寓本地库,与执行域同库;归属标注为 owner=apartment,写入口在 apt.voice.*。表名沿用公寓端新增前缀 APT_,与操作域 DUTY_ 区分。
表 5-3 语音域表清单、主键与关键约束
| 表 | 用途 | 主键 | 关键唯一约束与索引 | 关键字段 | 写入口 |
|---|---|---|---|---|---|
APT_SIP_ENDPOINT |
分机台账(房间分机、值班台、广播终端话机) | endpoint_id(bigint) |
UK (gyid, extension);IDX (gyid, endpoint_type, enabled)、(room_no) |
extension、endpoint_type(ROOM/CONSOLE/BROADCAST/TRUNK_SIDE)、room_no、enabled、register_status、last_reg_at、last_seen_ip、sync_state(PENDING/SYNCED/FAILED/DRIFT)、applied_version、config_version、remark、deleted |
apt.voice.endpoint |
APT_SIP_BINDING |
分机与房间/值班台/广播区/设备的绑定 | bind_id |
UK (endpoint_id, bind_type, bind_target, eff_from);IDX (bind_type, bind_target, eff_to) |
bind_type(ROOM/CONSOLE/BROADCAST_ZONE/DEVICE)、bind_target(房间号/台号/区号/device_code)、is_primary、eff_from、eff_to |
apt.voice.endpoint |
APT_TRUNK |
中继台账 | trunk_id |
UK (gyid, trunk_name) |
trunk_name、trunk_type(REGISTER/PEER)、peer_host、peer_port、auth_user、register_status、last_reg_at、enabled、sync_state |
apt.voice.endpoint |
APT_CALL_SESSION |
一次呼叫/入会会话(业务视角) | session_id |
UK (gyid, linked_id);IDX (task_id)、(ryid, originate_at)、(state, originate_at) |
linked_id(Asterisk linkedid)、unique_id、conf_no、task_id、ryid、room_no、peer_extension、console_extension、attempt_no、state(CALLING/RINGING/ANSWERED/JOINED_CONF/LEFT/HANGUP/FAILED/TIMEOUT/UNKNOWN)、originate_at/ring_at/answer_at/join_at/leave_at/hangup_at、hangup_cause、duration_ms、recording_id、source(AUTO/MANUAL/TEST) |
apt.voice.event |
APT_CALL_EVENT |
原始 AMI 事件流水(只追加) | event_seq(bigint 自增) |
UK (gyid, session_id, event_type, channel, event_at_ms);IDX (event_at_ms) |
session_id、event_type、channel、conference、caller_id、cause、event_at_ms、received_at、payload(JSON,原始事件快照) |
apt.voice.ami |
APT_CALL_EVENT_HIS |
AMI 事件归档(按月归档,结构与在线表同口径) | event_seq(bigint 自增) |
PK (event_seq);IDX (event_at_ms)、(session_id, event_at_ms) |
session_id、event_type、channel、conference、caller_id、cause、event_at_ms、received_at、payload(JSON)、create_time(原记录创建时间)、archived_at(归档时间) |
apt.voice.ami(归档任务,只写) |
APT_RECORDING |
录音索引(与会话一对多) | recording_id |
UK (gyid, file_key);IDX (task_id)、(ryid, started_at)、(state, started_at) |
session_id、task_id、ryid、file_key(Asterisk recordfile)、storage_kind(LOCAL/NFS/HTTP)、storage_path、url、bytes、duration_ms、codec、started_at、state(RECORDING/AVAILABLE/MISSING/PURGED)、retention_days、purged_at、checksum |
apt.voice.media |
APT_TTS_UTTERANCE |
TTS 播报单元(会话内排队) | utterance_id |
IDX (session_id, seq)、(state, created_at) |
session_id、task_id、conf_no、seq、template_code、text、state(PENDING/PLAYING/DONE/FAILED)、started_at、finished_at、fail_reason |
apt.voice.media |
APT_AST_CONFIG_VERSION |
配置版本(本地意图版本 + 平台下发版本) | version_id |
UK (gyid, config_type, source, version_no) |
config_type(SIP/TRUNK/CONF/DIALPLAN/TTS/RETENTION)、source(LOCAL/PLATFORM)、version_no、checksum、applied_at、applied_by、apply_result |
apt.voice.endpoint |
APT_AST_DRIFT |
对账差异 | drift_id |
UK (gyid, config_type, object_key, detected_at);IDX (resolved_at) |
config_type、object_key(分机号/中继名/会议桥)、drift_type(MISSING_IN_AST/EXTRA_IN_AST/ATTR_MISMATCH)、expected_value、actual_value、detected_at、resolved_at、resolve_action |
apt.voice.endpoint |
APT_AST_AMI_SESSION |
AMI 连接健康与断连记录 | ami_session_id |
IDX (connected_at)、(node, connected_at) |
node、connected_at、disconnected_at、disconnect_reason、reconnect_count、last_event_at、event_lag_ms |
apt.voice.ami |
与既有表的处理关系:
GY_ROOMS.sip_account:迁入APT_SIP_ENDPOINT后降级为只读冗余(一期内保留,供旧报表与旧接口兼容),不再作为写入口;从未被写入的sip_status直接废弃。GY_JCXX.recordfile:现状是逗号拼接的多条录音文件名(updateJcxxRyzt内部合并),改为APT_RECORDING一对多;旧列保留只读一期,新会话只写APT_RECORDING。GY_JCXX.ryzt:语音事件不再直接 UPDATE,统一提交apt.duty.state的状态机命令,由状态机条件更新(保留“已结束叫乘不回写”的现有保护语义)。- 设备档案
GY_SB与分机台账是两套对象:前者是“终端”,后者是“号码/账号”,用APT_SIP_BINDING的DEVICE类型关联,不合表。 - 归档:
APT_CALL_EVENT按天分区或按月归档到APT_CALL_EVENT_HIS,在线保留 90 天;APT_RECORDING索引长期保留(仅状态变PURGED)。
表 5-4 语音域字段级 MySQL 适配要点
| 关注点 | 现状(KingbaseES) | MySQL 8.0 落法 | 说明 |
|---|---|---|---|
| 主键 | 现库 0 主键,业务靠 id 列(numeric/varchar 混用) |
每表单列 bigint 主键;业务键另建 UK |
InnoDB 按主键聚簇,无主键会退化为隐藏 rowid,写入与复制代价上升 |
| 分机号/房间号 | varcharbyte 无长度 |
varchar(32);extension 建议 char(8) 定长 + CHECK 数字 |
定长便于索引与比对;utf8mb4 下宽度按字符计,长度要克制 |
| 枚举状态 | varchar 存 0/1/2 数字 |
varchar(16) + CHECK 约束(8.0.16 起真实生效) |
不用 MySQL ENUM:新增值需 DDL,跨端契约变更成本高 |
| 时间 | timestamp/timestamp(6)/sys.date 混用 |
统一 datetime(3) |
不用 timestamp:受会话 time_zone 影响;毫秒精度对 30s 级叫乘判定足够 |
| 三时间戳 | 现状只有单列时间 | event_at_ms、received_at、created_at 分列 |
与 6.2 三时间戳约定对齐;event_at_ms 用 bigint 存毫秒,避开解析歧义 |
| 原始事件载荷 | 无(事件只在内存) | payload json |
JSON 列不能有默认值、不能直接建索引;需检索的字段用生成列 + 索引 |
| 录音路径 | varchar 存文件名 |
storage_path varchar(512)、url varchar(1024)(或 text) |
长 URL 用 text,只在 file_key 上建 UK |
| 空串语义 | ora_input_emptystr_isnull=on,空串入库即 NULL |
需在应用层把 "" 归一为 NULL(或反之,但必须全端统一) |
迁移后 = '' 与 IS NULL 结果会变,是“假差异”的主要来源 |
| 唯一约束长度 | 现库无唯一约束 | 组合 UK 总长 < 3072 字节(utf8mb4 约 768 字符) |
(gyid, extension) 这类短键无风险;带长路径的组合键改用哈希列 |
| 字符集与排序 | 库级 UTF8,Oracle 模式比较区分大小写 | 库/表/连接统一 utf8mb4;排序规则以库默认 utf8mb4_general_ci 统一,不逐列改 utf8mb4_0900_ai_ci / _as_cs |
例外说明(覆盖表 5-9 第 7 项的逐列要求,也约束表 5-6「大小写」「字符集」两行):与若依 sys_* 表 JOIN 时混排规则会报 Illegal mix of collations,故全库统一 general_ci;大小写归一改在应用层(分机号、房间号入库统一大写),不依赖数据库排序规则折叠 |
| 自增与序列 | sys_guid();64 个序列仅 4 个被引用 |
bigint AUTO_INCREMENT(单库内)+ 业务 biz_id 用 ULID/UUID |
跨端幂等键不能用自增;biz_id 必须独立且全局唯一 |
| 长文本 | text / nclob 混用 |
text(64 KB)/ mediumtext |
录音只存索引不存二进制;照片仍走对象存储或本地磁盘 |
| 事务隔离 | Oracle 兼容模式(读已提交) | 实例级 transaction_isolation=READ-COMMITTED(部署待办,口径见 表 5-6) |
MySQL 默认 REPEATABLE-READ 有间隙锁与幻读差异,会改变状态机条件更新语义 |
本节只给字段与约束口径,建表语句在详细设计阶段产出,本文不提供 DDL。
5.5 分机配置在“本地库”与“Asterisk 实际配置”之间的一致性
一致性模型采用 期望态(desired)在库、实际态(actual)在 Asterisk、协调器单向收敛,不做双向双写。
表 5-5 分机配置一致性机制
| 场景 | 机制 | 失败处理 | 可见性 |
|---|---|---|---|
| 新增分机 | 本地库事务写 APT_SIP_ENDPOINT(sync_state=PENDING,config_version+1)→ 提交后投递收敛任务 → 通过 AMI/配置文件下发 → 回写 SYNCED 与 applied_version |
下发失败置 FAILED + 错误码 + 退避重试;连续失败 N 次告警并禁止该分机参与叫乘 |
列表显示“同步中/同步失败”,值班员可见但不可选 |
| 修改属性(值班台号、房间绑定) | 只改绑定与属性,不换号码;绑定表按生效区间新增一条,旧记录写 eff_to |
绑定写入失败不产生半状态;Asterisk 侧属性变更失败保留旧值并告警 | 换房历史可追溯 |
| 修改号码 | 不允许原地改号:停用旧分机 + 新建新分机,旧号保留 N 天(默认 7 天) | 旧号停用即不再参与叫乘;物理删除延迟执行 | 审计留痕“哪个任务改的号” |
| 停用 | 本地先置 enabled=0,叫乘前过滤;再下发 Asterisk 停用(不注册) |
Asterisk 不可达时本地已停用,功能上已生效(安全侧优先) | 状态“已停用/待同步” |
| 删除 | 逻辑删除 + 解绑 → 延迟物理删除;Asterisk 侧先停用后删对象 | 物理删除前二次对账,确认无活动会话 | 仅管理员可见 |
| 启动自检 | 见 7.11:AMI 连通、会议桥、拨号计划、分机数量与属性比对 | 关键项失败进入 DEGRADED,叫乘前二次校验并走值班台兜底 |
首页横幅 + 健康探针 |
| 运行期对账 | 每 30 分钟(可配)拉取 pjsip show endpoints / pjsip show registrations,与台账比对 |
差异写 APT_AST_DRIFT;默认“只报告不自动修”,自动修复需显式开关 |
漂移清单页 + 差异数量指标 |
| 现场手工改 Asterisk | 视为漂移来源之一;对账发现后由运维选择“库为准”或“Asterisk 为准” | 选择后回写台账并记 resolve_action |
漂移处理记录可审计 |
| 备份恢复/版本升级 | 升级窗口内把 sync_state 全量置 PENDING 并清 applied_version(不写 CHECK 之外的值),升级后全量比对一次 |
比对期间叫乘使用“最后已知可用”快照 | 升级后跑完全量对账才算成功 |
三条原则:
- 写顺序固定:先库后 Asterisk。库是意图,Asterisk 是派生物;库成功、Asterisk 失败可以重试,反过来会出现“设备在响、系统不知道”。
- 失败不回滚库:Asterisk 失败不回滚本地库,而是保留失败态并阻断该分机的叫乘参与,避免“回滚后库里没、Asterisk 里多一个孤儿号”这种更难修的状态。
- 叫乘前二次校验:
sync_state=SYNCED且注册在线才允许发起;否则直接走“值班台人工兜底 + 告警”,而不是像现状makeWakeup失败只打印日志那样静默失败。
5.6 数据库选型:KingbaseES → MySQL 的影响与迁移
现行生产库为 KingbaseES(实测 V009R001C010,server_version 报 12.1,database_mode=oracle,ora_input_emptystr_isnull=on);公寓端基线库改为 MySQL 8.0。这不是换一个 JDBC 驱动,而是类型、语义、约束、排序规则四类同时改变。
5.6.1 影响面
表 5-6 数据库差异与影响
| 差异类别 | 现状事实(实测,见 docs/11) | MySQL 后的变化 | 应对 |
|---|---|---|---|
| 字符类型 | 524 列中 varcharbyte 276 列,全部未指定长度;varchar 78 列 |
varchar 必须给长度;utf8mb4 行上限 65535 字节、单索引键 3072 字节 |
逐列定长:编码类 32、名称类 64~128、备注 512、长文本 text |
| 空串语义 | ora_input_emptystr_isnull=on:写 '' 实际存 NULL(实测 bz='' 命中 0 行、bz IS NULL 命中 3,541 行) |
MySQL 原样保留空串 | 全端统一“空串归一为 NULL”并在拦截器/实体层实现;迁移必须做空串专项比对,否则会出现大量假差异 |
| 时间类型 | timestamp 68 列、sys.date(Oracle DATE 语义)、gy_jcxx_his.yappsj 漂移为 timestamp(6) |
datetime 不随时区变,timestamp 随会话时区变;毫秒精度需显式 (3) |
统一 datetime(3);禁止用 timestamp;纯日期列用 date |
| 数值与金额 | numeric 82 列,金额未统一精度(实测三表合计 552,295.00 / 265.10 / 1,228.00 无法对账) |
decimal(p,s) 需显式精度 |
金额统一 decimal(12,2),比率 decimal(6,4),计数 bigint |
| 自增/序列 | 64 个序列,仅 4 个被引用(gen_table*、gy_device、seq_gy_allowance);大量主键是应用层 UUID |
MySQL 无序列;自增列只能一个且与业务语义无关 | 主键 bigint AUTO_INCREMENT,业务标识另设 biz_id + UK;跨端幂等键必须用 biz_id |
| JSON | 现库无 JSON 列,事件类数据靠 text 拼接 |
MySQL 原生 json,不能有默认值、不能直接建索引 |
只在 payload 类字段用 json;需检索字段用“生成列 + 索引” |
| 大小写 | Oracle 模式下标识符折叠为大写(表名 GY_JCXX、列名 JCID);比较区分大小写 |
Linux 下 lower_case_table_names=0 表名区分大小写;默认排序规则 _ci 不区分大小写 |
表名列名统一小写;业务键入库统一大写;排序规则以库默认统一,不逐列指定(例外见 表 5-4) |
| 字符集 | 库级 UTF8 | utf8mb4 + 库默认 utf8mb4_general_ci(与若依 sys_* 一致,例外见 表 5-4) |
连接串固定 characterEncoding=UTF-8 与 connectionCollation;禁止 utf8mb3 |
| 索引与约束 | 索引 0、主键 0、唯一 0、外键 0(40 表全量) | InnoDB 必须主键;二级索引带来写放大 | 按表 5-3 口径补 PK/UK/IDX;外键默认不建(跨端与分库场景),用应用层约束 + 对账兜底 |
| 注释 | 表注释 3/40,列注释 60/524(业务表仅 5 条) | 表/列注释均支持,上限 2048 字符 | 迁移时补齐中文注释,作为 DDL 评审项 |
| 事务与锁 | Oracle 兼容模式(读已提交) | 默认 REPEATABLE-READ,有间隙锁与幻读差异 |
部署待办(实例级参数,不入应用配置):my.cnf 设 transaction_isolation=READ-COMMITTED,随切换一并整改并纳入上线检查;应用侧不以隔离级别为正确性前提,状态机一律条件更新(WHERE state=?)而非先查后改 |
| SQL 方言 | sysdate()、dual、sys_guid()、Oracle 风格分页 |
NOW(3)、LIMIT、UUID() |
阶段一产出方言点清单;MyBatis-Plus DbType 与分页插件同步切换 |
5.6.2 迁移与双跑验证
表 5-7 迁移步骤与退出条件
| 步骤 | 内容 | 退出条件(可验收) |
|---|---|---|
| S0 元数据盘点 | 以实测 information_schema 为唯一来源(不依赖 dump),产出列级清单:类型、长度、可空、注释、实际最大值 |
524 列全部有目标类型与长度,无“待定”列 |
| S1 目标结构 | 按 5.4 与 docs/11 第 6 章口径产出 MySQL 结构草案(PK/UK/IDX/NOT NULL/默认值/注释) | 结构评审通过;每表有主键;金额精度统一 |
| S2 全量迁移 | 按表迁移,字符集转换显式指定;大表分批(gy_logs 285,914 行、gy_sb_zl 129,665 行) |
行数一致;sum() 类指标一致;抽样 1,000 行字段级 diff 为零 |
| S3 空串/NULL 专项 | 对现状 '' 与 NULL 语义差异做专项比对 |
差异清单可解释,每条都归属到一条归一规则 |
| S4 只读双跑 | 新库只读,页面与报表并行对照 2 周 | 差异条目收敛到 0,或全部判定为非缺陷 |
| S5 影子上报 | 新链路只写不生效(见 6.3),验证上报与对账 | 对账零差异连续 3 天 |
| S6 灰度切换 | 按公寓逐个切换,旧库保留只读一个周期 | 单公寓连续 7 天无 P0/P1 缺陷 |
| S7 回滚演练 | 保留旧实例与旧库只读副本,演练回退 | 回退时间 < 2 小时且数据可追平 |
双跑必须固定的三条口径:
- 校验维度:
count、关键金额sum、max(updated_at)、max(watermark)、抽样字段级 diff。少一维都可能“看起来对”。 - 差异分类:真实缺陷 / 空串与 NULL 语义 / 排序与大小写 / 时间精度截断 / 历史脏数据。后四类必须在双跑手册里预置判定规则,否则争议无法收敛。
- 双跑时长:至少覆盖 2 个完整结算周期(含一次月度补助结算),账务类表尤其不能缩短。
5.6.3 平台端是否一并改为 MySQL
表 5-8 平台端数据库选项对比
| 选项 | 做法 | 优点 | 风险 | 适用判断 |
|---|---|---|---|---|
| A 平台保持现库 | 公寓端 MySQL,平台端继续 KingbaseES(Oracle 存量公寓继续 Oracle) | 平台零迁移风险;公寓端可先行;跨端只有 HTTP,无跨库 JOIN 需求 | 两套方言、两套 SQL 审查与测试矩阵;团队要同时维护 | 推荐作为第一阶段:先把公寓端跑通 |
| B 平台一并改 MySQL | 平台主库迁 MySQL,Oracle 存量公寓按期退役 | 全栈统一,工具链与运维口径一致,人力最省 | 平台库更大、耦合更多;现库 0 主键 0 索引,迁移等于“结构重构 + 数据迁移”叠加;运维与 DBA 熟悉度需确认 | 中期评估,不建议与公寓端试点同期进行 |
| C 平台 MySQL + 存量公寓 Oracle | 平台迁 MySQL,未退役公寓保持 Oracle 只读过渡 | 兼顾统一与存量 | 三套方言并存,过渡期最长 | 仅在 Oracle 存量公寓有明确退役时间表时采用 |
不替用户拍板的三个前置条件(满足再议 B/C):
- Oracle 存量公寓(
byq/cc/dl/pj等 profile)的退役时间表是否已定(对应 D9)。 - 平台端是否已有跨 schema 的强耦合 SQL(如
gy_*与gl_*混查、gen_table*依赖),改造量能否估清。 - 运维团队对 MySQL 高可用(主从/组复制)与备份恢复的能力是否覆盖平台端 SLA。
表 5-9 切换前必查清单(评审用)
| # | 检查项 | 判定标准 |
|---|---|---|
| 1 | 每表有主键 | 44 张业务表(APT_* 23 + GY_*/gygl_* 18 + duty_* 3)+ 30 张若依 sys_* 表,全部 100% 有单列主键 |
| 2 | 字符列有长度 | 无“无长度 varchar”;长文本用 text |
| 3 | 非空与默认值 | 业务必填列 NOT NULL;状态列有默认值 |
| 4 | 时间列统一 | 全部 datetime(3),无 timestamp |
| 5 | 金额精度 | decimal(12,2),无 float/double |
| 6 | 空串归一 | 有统一实现点(拦截器/实体转换),有单测覆盖 |
| 7 | 排序规则 | 库/表/连接统一库默认 utf8mb4_general_ci,例外清单见 表 5-4「字符集与排序」行;不逐列指定 _ai_ci / _as_cs |
| 8 | 唯一键长度 | 组合 UK 总字节数 < 3072 |
| 9 | 保留字冲突 | 列名不与 MySQL 保留字冲突(如 key、rank、groups) |
| 10 | 分页与方言 | 全量 SQL 清点完成,DbType 已切换并有回归用例 |
| 11 | 标识符大小写 | 全小写;部署机 lower_case_table_names 已确认 |
| 12 | 权限模型 | 一公寓一 database + 独立账号;账号无平台库权限 |
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 上报协议
信封结构(所有上行接口统一)
{
"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": {}
}
]
}
协议要点:
occurredAt(现场发生时间)、receivedAt(公寓端入库时间)、serverReceivedAt(平台接收时间)三时间戳并存,用于处理时钟差异与迟到数据。idempotencyKey = hash(gyId + bizType + bizId + bizVersion):同一业务对象同一版本只生效一次;版本递增用于“更正上报”。watermark:公寓端本地自增序号,平台据此判断漏报/乱序并作为对账水位;现状GY_SYN_JCXX_STATUS(0/1)可作为该思路的历史参照。- 批量上限建议单批 ≤ 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 落本地库(非内存),断网期间持续累积,恢复后按水位续传 |
| 平台不可用 | 不影响叫乘:下行使用本地快照(带版本与有效期),过期后仅降级“新计划不生效”并告警 |
6.4 语音域接口清单
语音域接口分三组:管理/值班侧(/apt/voice/**,会话鉴权 + 权限点)、健康与运维(/apt/voice/ast/**)、跨端(/open/**,mTLS 或双向令牌)。下表只给清单级描述,字段级契约在阶段二冻结。
表 6-3 公寓端语音域接口清单
| # | 方向/分组 | 接口 | 用途 | 关键字段 |
|---|---|---|---|---|
| 1 | 管理 | GET /apt/voice/endpoints |
分机列表(分页) | extension、endpointType、roomNo、enabled、registerStatus、syncState |
| 2 | 管理 | POST /apt/voice/endpoints |
新增分机(只落库,异步下发) | extension、endpointType、roomNo、bindings[] |
| 3 | 管理 | PUT /apt/voice/endpoints/{id} |
修改分机属性(不含号码) | roomNo、remark、enabled |
| 4 | 管理 | POST /apt/voice/endpoints/{id}/enable、/disable |
启用/停用 | id、reason |
| 5 | 管理 | DELETE /apt/voice/endpoints/{id} |
逻辑删除(延迟物理删除) | id、reason |
| 6 | 管理 | POST /apt/voice/endpoints/{id}/bindings |
绑定房间/值班台/广播区/设备 | bindType、bindTarget、effFrom |
| 7 | 管理 | DELETE /apt/voice/endpoints/{id}/bindings/{bindId} |
解绑(写 eff_to) |
bindId、effTo |
| 8 | 管理 | POST /apt/voice/endpoints/{id}/resync |
手工触发收敛 | id、force |
| 9 | 管理 | GET /apt/voice/trunks、POST/PUT/DELETE /apt/voice/trunks |
中继台账与维护 | trunkName、trunkType、peerHost、registerStatus |
| 10 | 管理 | GET /apt/voice/registrations |
注册与在线状态 | extension、registered、userAgent、lastRegAt |
| 11 | 查询 | GET /apt/voice/calls |
会话列表(按任务/人员/时间/状态) | taskId、ryid、state、from、to |
| 12 | 查询 | GET /apt/voice/calls/{sessionId} |
单次会话详情 | 全量时间戳、hangupCause、attemptNo |
| 13 | 查询 | GET /apt/voice/calls/{sessionId}/events |
原始事件时间线 | eventType、eventAt、channel |
| 14 | 干预 | POST /apt/voice/calls/{sessionId}/hangup |
人工挂断 | reason、operator |
| 15 | 录音 | GET /apt/voice/recordings |
录音索引查询 | taskId、ryid、state、from、to |
| 16 | 录音 | GET /apt/voice/recordings/{id}/url |
取带签名的临时播放地址 | url、expireAt |
| 17 | 录音 | POST /apt/voice/recordings/{id}/purge |
提前清理(需审计) | reason、operator |
| 18 | TTS | POST /apt/voice/tts/test |
试听(不代表叫乘,标 TEST) |
confNo、text |
| 19 | 健康 | GET /apt/voice/ast/health |
Asterisk 与 AMI 健康度 | amiState、astVersion、confReady、onlineCount、recDiskFree |
| 20 | 运维 | POST /apt/voice/ast/reconcile |
触发对账 | configType、dryRun |
| 21 | 运维 | GET /apt/voice/ast/drifts |
漂移清单 | driftType、objectKey、detectedAt |
| 22 | 运维 | GET /apt/voice/config/versions |
配置版本与生效情况 | configType、source、versionNo、appliedAt |
| 23 | 跨端下行 | GET /open/v1/voice/config?gyId&configType |
平台下发语音配置(号段规则、TTS 模板、留存策略) | versionNo、checksum、payload |
| 24 | 跨端上行 | POST /open/v1/voice/summary |
上报语音质量摘要 | regRate、callSuccessRate、recordingMissing、amiReconnects |
| 25 | 跨端查询 | GET /platform/v1/voice/endpoints?gyId |
平台侧汇总查看(只读) | gyId、total、online、driftCount |
接口设计要点:
- 增删改停一律异步收敛:接口返回“已受理 + 当前
syncState”,不阻塞等待 Asterisk;前端轮询或订阅 WebSocket 获取最终结果。 - 号码不可改:接口层直接拒绝改号请求(返回专用错误码),引导“停旧建新”,避免历史会话与录音索引失去归属。
- 录音不外直链:播放必须经
recordings/{id}/url换取短期签名地址,不暴露物理路径与目录结构。 - 健康探针分两个:
/apt/voice/ast/health面向人看(字段全),另设/apt/ops/health/call-ready面向监控(只返回“可叫乘/不可叫乘”),避免内部状态导致误判。 - 跨端不带媒体:
/open/**只有配置与摘要,不上传录音、不代理 AMI。
不对外暴露的“回调”:AMI 是公寓端主动连接 Asterisk 的客户端,不存在 Asterisk 反向回调 HTTP 的场景;会议与通话状态通过本地领域事件 + /ws/duty 推给前端,不设计公网回调接口。
与表 6-1 的关系:#23~#25 属于新增的跨端语音接口,冻结 v1 契约时并入同一版本号管理(跨端接口总数由 17 变 20),不另立 v2。
7. 设备接入方案
7.1 接入形态
- 统一网关入口
/apt/dev/{deviceType}/register|heartbeat|event|command/ack,把现有 6 个*Device包的重复协议收敛为“网关 + 类型插件”(叫乘屏、签到机、房间屏、消费机、注册机、售货机)。 - 设备只连本公寓服务器,内网优先;公寓端不暴露公网入口。
- 协议保持现有 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 告警处理
- 判定在公寓端:叫乘未应答、复叫未应答、叫乘结束未应答、超时未离寓、设备离线、AMI 不可用、串口异常、上报积压。
- 两级输出:本地即时告警(应用端弹窗/声响/TTS,走本地 WebSocket)与平台摘要上报(数量、类型、最近发生时间,不逐条上报)。
- 现有
GYGL_JGXX与is_jcbj/is_lybj语义保留,作为本地告警事实表。
7.5 本地数据缓存
- 主数据缓存:人员(含照片)、交路、时间规则、节假日、房间属性,带
version + validUntil,过期只提示不阻断。 - 指令缓存:可靠队列(本地库表 + 状态机)。
- 事件缓存:设备事件先落本地(
deviceCode + deviceEventId幂等,沿用GYGL_LY_EVENT设计),再异步上报。 - 媒体缓存:离寓抓拍、录音本地保存 + 生命周期清理(沿用
leaveSnapshot.retentionDays/cleanupCron),平台只存索引并按需回捞。
7.6 上报边界
表 7-3 设备域数据上报与不上报
| 上报平台 | 不上报平台 |
|---|---|
| 设备清单与在线率摘要、版本分布、故障事件 | 原始心跳流水、每次指令明细 |
| 叫乘/复叫/应答/离寓执行结果(业务事件) | AMI 通道事件、TTS 文本、串口字节流 |
| 报警摘要(类型、数量、时间) | 每条报警的完整现场上下文 |
| 账务流水 | 余额中间态、实时读余额请求 |
| 归档摘要与对账水位 | 全量历史明细(按需回捞) |
7.7 语音链路拓扑
图 7-1 公寓内语音链路
flowchart TB
subgraph APTHOST["公寓服务器(单机)"]
UI["值班员浏览器(duty 入口)"]
APP["公寓端进程<br/>apt-duty.view / apt.duty.engine<br/>apt.voice.*"]
AST["Asterisk<br/>PJSIP + ConfBridge(Conf-01) + Playback(tts)"]
REC["录音目录<br/>/data/asterisk/monitor/..."]
WEB["Nginx<br/>静态页 + 录音 HTTP"]
DB[("本地 MySQL")]
SER["串口广播<br/>COM1 9600 8N1"]
end
PHONE["房间话机<br/>PJSIP 分机"]
CONSOLE["值班台话机<br/>固定分机 1009/1010"]
SPK["广播终端"]
PLAT["平台端"]
UI --> WEB
WEB --> APP
APP -->|AMI 7058 命令| AST
AST -->|AMI 事件| APP
PHONE <-->|SIP 5060 / RTP| AST
CONSOLE <-->|SIP / RTP| AST
AST -->|写录音| REC
REC -->|HTTP 目录| WEB
APP --> SER --> SPK
APP --> DB
APP -->|HTTPS 443 出站| PLAT
链路要点(对应现状实现,作为改造基线):
- 叫乘发起固定为两条 Originate:一条打值班台(
PJSIP/<console>,扩展1701@from-internal),一条打房间(PJSIP/<sip_account>,扩展1901@from-internal);房间侧带Call-Info: answer-after=5自动接听。 - 接通判定依赖
ConfbridgeJoinEvent(会议号Conf-01、通道含PJSIP/且非值班台),不是Answer事件。 - TTS 由
Local/1701@from-internal发起Playback(<文本>,tts),文本现经进程内conftts单槽位传递。 - 录音由 Asterisk 侧产出,公寓端只从事件取文件名并索引;播放走
callcontrol.recording.base-url。 - 串口广播与语音是两条独立通道,同一任务可同时触发,互不阻塞。
7.8 分机与中继资源的生命周期
表 7-4 分机资源状态机
| 状态 | 含义 | 进入条件 | 允许动作 | 出错处理 | 落库载体(APT_SIP_ENDPOINT) |
|---|---|---|---|---|---|
DRAFT |
台账已建,未下发 | 新增保存 | 编辑、下发、删除 | — | sync_state='PENDING' + enabled=0(与 PENDING 合并,落库即待下发) |
PENDING |
已提交下发,等待 Asterisk 结果 | 提交收敛任务 | 重试、撤回(删除) | 超时转 FAILED |
sync_state='PENDING' |
SYNCED |
库与 Asterisk 一致 | 下发成功或对账确认 | 停用、改绑定、改号(停旧建新) | 对账发现差异转 DRIFT |
sync_state='SYNCED' |
FAILED |
下发失败 | 下发报错/超时 | 重试、修正后重发 | 连续失败 N 次告警并禁止叫乘 | sync_state='FAILED' |
DISABLED |
停用,不参与叫乘 | 停用操作 | 启用(回 PENDING)、删除 |
— | enabled=0(sync_state 不改值) |
DELETED |
逻辑删除,等待物理清理 | 删除操作 | 恢复(仅未物理删除前) | 物理删除前二次对账 | deleted=1(sync_state 不改值) |
DRIFT |
库与 Asterisk 不一致 | 对账发现 | 按库修复 / 按 Asterisk 回写 | 未解决前禁止叫乘 | sync_state='DRIFT' + APT_AST_DRIFT 一行 |
落库载体说明:上表 7 个状态不落成 7 个枚举值,而以“sync_state(CHECK 限定 PENDING / SYNCED / FAILED / DRIFT 四值)+ enabled + deleted”三个载体表达,DRAFT 与 PENDING 合并。不这么拆会出现“把 DISABLED 写进 sync_state 直接被 CHECK 拒绝”的故障;语义等价、可查询性更好,迁移到多会议桥或多中继时也不需要改状态机。
配套规则:
- 分机号是业务键,不允许原地改号:改号 = 停旧 + 建新,旧号保留 N 天(默认 7 天)以防话机仍注册。
- 中继(
APT_TRUNK)只有启用/停用两态,注册状态由pjsip show registrations观测,不写入资源状态。 - 分机与房间是“绑定”而非“字段”:一个房间可临时绑定广播区、一个广播区可含多个分机,绑定按生效区间版本化。
- 所有资源变更必须留
APT_AST_CONFIG_VERSION版本记录与操作审计,支持“回到某一版本”。
7.9 事件跟踪:从 AMI 事件到业务状态
表 7-5 关键事件与处理动作
| AMI 事件 | 触发时机 | 处理动作 | 业务状态迁移 | 落表 |
|---|---|---|---|---|
OriginateResponse |
发起后立即 | 判 Success/Failure,失败记录原因 |
CALLING → FAILED |
APT_CALL_SESSION |
Newstate(Ringing/Dialing) |
振铃 | 记录振铃时间 | CALLING → RINGING |
APT_CALL_SESSION |
ConfbridgeJoin |
入会 | 匹配任务与人员,回写应答 | RINGING → ANSWERED/JOINED_CONF |
APT_CALL_SESSION + 状态机命令 |
ConfbridgeLeave |
离会 | 记录离会,启动挂断计时 | JOINED_CONF → LEFT |
APT_CALL_SESSION |
Hangup |
挂断 | 按 cause 分类,计算时长 |
任意 → HANGUP |
APT_CALL_SESSION |
AMI Disconnect |
连接断开 | 在途会话标 UNKNOWN,启动补偿巡检 |
不变(待补偿判定) | APT_AST_AMI_SESSION |
| 本地超时(无事件) | 发起后 30s 无应答 | 判超时,触发复叫策略 | CALLING/RINGING → TIMEOUT |
APT_CALL_SESSION |
无 recordfile 字段 |
入会但事件无录音字段 | 按文件名约定反查录音目录 | 不变 | APT_RECORDING(缺失态) |
失败与超时的识别口径(对齐现状 CALL_TIMEOUT_MS=30000、复叫判定 35s、接听后自动挂断 40s/喊话 30s):
- 发起失败:
OriginateResponse.Failure,或 AMI 调用抛异常且重连后仍失败。 - 无应答超时:30s 内无
ConfbridgeJoin且通道不存在。 - 接通即断:
ConfbridgeJoin后 < 3sHangup,判“异常中断”,触发复叫并告警。 - 会议桥满/不可用:
ConfBridge应用不存在或入会失败,判“语音链路不可用”,直接转值班台兜底。 - 挂断原因映射:
cause=16正常、17忙、18无应答、19不可达、21拒绝;其余归“未知”并保留原始值。
录音与 TTS 的触发时机与回写:
| 能力 | 触发时机 | 成功判定 | 失败回写 |
|---|---|---|---|
| 录音 | 会议桥内房间侧通道入会即开始(Asterisk 侧策略) | 事件带 recordfile 或目录存在对应文件 |
索引置 MISSING + 告警,不影响叫乘结论 |
| TTS | 房间侧入会后、播放前生成 APT_TTS_UTTERANCE |
Playback 返回成功且状态 PLAYING→DONE |
置 FAILED + 原因,可人工重播(重播记 source=TEST) |
| 复叫 TTS | 复叫任务入会后 | 同上,seq 递增 |
同上 |
7.10 本地闭环与平台依赖边界
表 7-6 能力闭环矩阵
| 能力 | 断网是否可用 | 是否依赖平台 | 说明 |
|---|---|---|---|
| 叫乘发起、会议桥、TTS、录音 | 可用 | 不依赖 | 全部本地,平台不可达不影响 |
| 分机/中继增删改停 | 可用 | 不依赖(号段规则取本地快照) | 平台只定规则,落地在本地 |
| 注册状态与在线判定 | 可用 | 不依赖 | AMI/注册事件本地可得 |
| 换房后重新绑定分机 | 可用 | 不依赖 | 绑定在本地版本化 |
| 语音配置版本(TTS 模板、会议桥参数、录音留存) | 可用(用最后版本) | 依赖平台下发 | 断网使用最后已知版本 + 有效期,过期仅告警不阻断 |
| 号段规划与中继路由策略 | — | 依赖平台 | 变更频率低,随主数据版本下发 |
| 语音质量摘要上报 | 可用(本地累积) | 依赖平台接收 | 进 outbox,恢复后续传 |
| 录音合规策略 | 可用(按最后策略执行) | 依赖平台定策略 | 策略过期时按“更保守”的本地默认执行 |
| 跨公寓号码冲突检查 | — | 依赖平台 | 新增分机时本地校验规则快照,平台侧做最终冲突检测 |
7.11 启动自检与运行期巡检
表 7-7 自检项与失败处理
| # | 检查项 | 判定方式 | 失败级别 | 恢复动作 |
|---|---|---|---|---|
| 1 | AMI 可达与登录 | TCP 连接 + login() 成功 |
BLOCKED | 指数退避重连,告警 |
| 2 | Asterisk 版本与能力 | core show version,比对 asterisk-java 兼容矩阵 |
DEGRADED | 记录并禁止高级能力(如录音) |
| 3 | 会议桥存在 | Conf-01 在 confbridge.conf 且应用可用 |
BLOCKED | 告警;叫乘转值班台 |
| 4 | 拨号计划上下文 | from-internal 存在且含 1901/1701 |
BLOCKED | 告警,禁止叫乘 |
| 5 | 值班台分机在册且注册 | 台账 CONSOLE 类型 + 注册状态 |
DEGRADED | 告警;房间侧仍可叫乘 |
| 6 | 分机台账与 Asterisk 一致 | pjsip show endpoints 与台账数量/属性比对 |
DEGRADED | 生成漂移清单 |
| 7 | 录音目录可写、空间充足 | 试写文件 + 剩余空间 > 阈值 | DEGRADED | 停止录音、保留叫乘 |
| 8 | 录音播放地址可达 | 请求 recording.base-url 返回 200 |
DEGRADED | 告警,回放降级为下载 |
| 9 | TTS 通道可用 | 试播一条 TEST 文本 |
DEGRADED | 关闭 TTS,仅播放提示音 |
| 10 | 串口可打开 | 按 console.port 打开(COM1 9600 8N1) |
DEGRADED | 广播改用语音通道 |
| 11 | 时钟偏差 | NTP 偏差 < 1s | DEGRADED | 告警,三时间戳仍可判序 |
| 12 | AMI 事件滞后 | last_event_at 与当前时间差 |
DEGRADED | 触发补偿巡检 |
运行期巡检(不依赖事件,用于兜住“事件丢了”):每 5 分钟执行一次 core show channels concise 与在途会话比对,发现“库里有会话、Asterisk 无通道”则判 HANGUP(UNKNOWN) 并记补偿;发现“Asterisk 有通道、库里无会话”则记孤儿通道并告警。
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 思路 |
— |
隔离与网络:
- 一公寓一实例一库,Nginx 按公寓内网域名/端口区分;平台端不配置到公寓的路由。
- 跨端仅允许公寓 → 平台出站 HTTPS;平台侧以
gyId+ 客户端证书/令牌双向认证。 - 反向代理必须透传真实 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 事件 |
8.3 Asterisk 与公寓端同机部署
表 8-3 单机资源与容量
| 项 | 建议值 | 依据 |
|---|---|---|
| CPU | 2 核(4 核更稳) | 会议桥混音 + 转码 + JVM + MySQL 同机 |
| 内存 | 8 GB(JVM 3 GB / Asterisk 1 GB / MySQL 2 GB / 系统 2 GB) | 并发叫乘时会议桥与 TTS 是内存与 CPU 双峰 |
| 磁盘 | 200 GB SSD:系统 60、库 30、录音 80、余量 30 | 录音按 197 房间量级估算,见下 |
| 网卡 | 千兆内网 | RTP 与终端同网段 |
| 操作系统 | Linux(推荐统信/麒麟或 Rocky);若保留 Windows 需验证串口与录音路径 | 现状配置含 serverOS: windows、串口 COM1 |
| 数据库 | 本地 MySQL 8.0,独立数据目录 | 与 Asterisk 抢 IO 时优先保证库 |
| 时钟 | NTP 客户端指向公寓内网源 | 叫乘精确到秒,录音与事件时间戳需对齐 |
录音容量估算(用于定保留天数):8 kHz/16 bit 单声道 WAV 约 0.94 MB/分钟;单次叫乘(含 TTS 与应答)按 2 分钟计约 1.9 MB;197 房间、日均 60 通 ≈ 114 MB/日;保留 90 天 ≈ 10 GB。抓拍与照片另计,不与录音共用卷。
表 8-4 端口与防火墙
| 端口/协议 | 用途 | 监听地址 | 放通范围 |
|---|---|---|---|
7058/tcp |
AMI(现状配置值,非默认 5038) | 127.0.0.1 |
仅本机;不建议对公寓内网开放 |
5060/udp、5060/tcp |
SIP 信令(PJSIP) | 公寓服务器内网地址 | 公寓内网话机与广播终端 |
10000-20000/udp |
RTP 媒体 | 同上 | 同上;端口段固定便于策略收敛 |
80/443/tcp |
Nginx:值班台、终端页、录音 HTTP | 内网地址 | 公寓内网 |
22/tcp |
运维 SSH | 内网地址 | 仅运维网段 |
出站 443/tcp |
公寓 → 平台 HTTPS | — | 单向出站,平台侧不回调 |
防火墙三条硬约束:
- AMI 不出本机:现状 AMI 口令(
manager01)属于弱边界,同机部署没有理由对外开放 7058。 - SIP/RTP 不出公寓内网:话机与广播终端必须与 Asterisk 同网段或经内网 VPN,避免 NAT 导致单向语音。
- 录音 HTTP 只对内网:录音含个人信息,不通过公网直链;外发须走申请与审计(见 D15)。
表 8-5 录音存储与清理
| 项 | 设计 |
|---|---|
| 目录结构 | /data/asterisk/monitor/<gyid>/<yyyy>/<MM>/<dd>/,文件名为 Asterisk 回传的 recordfile 名 |
| 索引与文件的关系 | 先落盘、后写 APT_RECORDING;索引缺失时置 MISSING,由巡检补齐或标记丢失 |
| 保留期 | 叫乘录音默认 90 天;TEST 试听 7 天;可按平台策略调为 180/365 |
| 清理 | 每日低峰执行(沿用 cleanupCron 思路 0 30 2 * * ?),先置状态 PURGED 再删文件,失败重试并告警 |
| 容量保护 | 磁盘 > 85% 触发“最旧优先”清理;< 5 GB 停止录音并告警,叫乘不受影响 |
| 一致性校验 | 每日抽样 10 条录音做“索引↔文件↔可播放”三向校验 |
| 备份 | 默认不集中备份;如需归档,按公寓打包上传对象存储,不作为叫乘前置条件 |
单点故障与高可用取舍:
- 不做双活:会议桥状态、SIP 注册与 RTP 无法无状态化,双活会带来注册漂移与串音,收益低于复杂度。
- 推荐冷备:同型号备机 + 配置与录音目录定时同步,恢复目标 15 分钟(含 Asterisk 起服与自检)。
- 降级路径:Asterisk 不可用时,叫乘退化为“串口广播 + 终端提示 + 值班台人工”,不允许静默失败。
- 必须写出 SLA:叫乘可用性目标、恢复时间、降级期间责任人,作为验收条款(对应 D16)。
9. 迁移与实施路径
图 9-1 实施路径
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 章固化为可评审契约 |
| 主要产出 | 跨端接口契约(表 6-1 的 17 个 + 表 6-3 的语音域 3 个,含错误码、幂等语义、限流);上报信封与 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 库存边界 |
独立处置,不进入公寓端 |
| D11 | 会议桥粒度 | 现状单 Conf-01 是串音与容量的硬约束;建议“每任务一个会议桥 + 保留 Conf-01 作为兼容与广播”,需先评估 Asterisk 资源与拨号计划改造量;未定则分机表与事件表的 conf_no 只能先做“多值预留” |
| D12 | 号段与分机编号规则归属 | 建议平台定规则、公寓端落地;需明确跨公寓是否要求分机号唯一(影响平台侧冲突检测与历史录音检索) |
| D13 | 平台端是否一并改 MySQL | 短期选 A(平台不动),中期满足表 5-8 三个前置条件后再议 B/C;不建议与公寓端试点同期切换 |
| D14 | 公寓端操作系统 | 建议统一 Linux(串口、录音、监控、系统服务均更可控);若必须兼容 Windows,需为串口库、路径、服务化各留一套验证用例 |
| D15 | 录音合规 | 需明确:留存期、告知方式、可访问角色、是否允许导出与对外提供;未定前按“90 天 + 仅内网 + 导出必审计”执行 |
| D16 | Asterisk 高可用方式 | 建议单机 + 冷备(RTO 15 分钟)+ 降级广播;双机主备需解决 SIP 注册漂移、录音共享与会议桥状态,不建议首期引入 |
10.3 语音与数据库专项风险
表 10-3 语音域与数据库迁移专项风险
| 风险 | 表现 | 应对 |
|---|---|---|
| Asterisk 单点 | 整个公寓叫乘停摆 | 冷备 + 自检 + 降级广播 + 明确 RTO;不承诺双活 |
| AMI 事件丢失 | 连接瞬断丢事件,会话永久停在“呼叫中” | 事件 + 超时 + 周期巡检三路判定;巡检发现无通道即判 HANGUP(UNKNOWN) 并补偿 |
| 分机配置漂移 | 现场手工改 Asterisk,库与实际不一致 | 定时对账 + 漂移表 + 默认只报告;叫乘前二次校验 |
| 会议桥串音/串台 | 多房间同时叫乘互相听到 | 会议粒度定稿(D11);未定前限制“同瞬间仅一个房间会话”并告警 |
| TTS 文本槽位覆盖 | conftts 按会议号单值,并发时后写覆盖前写 |
消息化 TTS(APT_TTS_UTTERANCE 队列 + 任务绑定),不依赖内存槽位 |
| 录音合规 | 录音含个人信息,留存与导出无约束 | 定稿 D15;索引层记录访问与导出操作;默认仅内网可访问 |
| 录音缺失 | recordfile 字段依赖 asterisk-java 版本,现状靠反射容错 |
文件名约定兜底 + 每日三向校验 + 缺失告警,不阻断叫乘 |
| 注册状态不可靠 | 无 qualify 时分机“假在线” |
开启 qualify + 注册事件 + 定时探测;在线判定以“事件 + 探测”双源为准 |
| MySQL 迁移语义变化 | 空串变 NULL、大小写、无长度 varchar、无主键被放大 | 表 5-9 清单前置检查 + 只读双跑 + 差异分类手册 |
| 双库方言 | 平台 Kingbase + 公寓 MySQL,同一段 SQL 两边跑 | 方言测试矩阵;禁止跨库 SQL;SQL 审查纳入 CI |
| 号码规划冲突 | 多公寓号段重叠,历史录音无法定位 | 号段规则平台化(D12);分机号全局唯一键含 gyid |
| 时钟偏差 | 录音、事件、上报时间错位 | NTP + 三时间戳 + 偏差告警;账务日切以平台时间为准 |
11. 结论与优先级
11.1 总体结论
- 拆分方向可行,且现状已有雏形。
centerServerUrl的双向调用、getApartmentPersonData*/synFinanceData、GY_SYN_JCXX_STATUS状态位,本质是“平台—公寓”关系未完成的形态;本次重构是把隐式关系显式化,而非推倒重建。 - 最大障碍不是技术选型,而是三件事:公寓身份(IP 推断 → 部署绑定)、数据归属(共享库 → 库隔离)、状态写入口分散(多写 → 单一状态机)。
- 公寓端用 RuoYi 新建是合理的。设备网关、执行引擎、上报器、值班 API 天然属于“管理后台 + 本地接入”形态,可复用 RBAC、定时任务、代码生成;但必须约定“RuoYi 管本地账号、平台管全局账号”,否则权限体系会打架。
- 应用端不独立是正确的。值班员的全部操作都必须落在公寓本地事实库上,独立服务只会带来跨服务事务与延迟;同代码同库 + 模块边界 + 状态机单入口足以支撑。
- 必须先做阶段零。
pom.xml依赖错配会让任何新工程无法建立可信基线,明文口令入库是安全红线;这两项不解决,拆分过程无法验收。
11.2 推荐落地顺序
表 11-1 优先级与落地顺序
| 优先级 | 事项 | 理由 |
|---|---|---|
| P0-1 | 修复 pom.xml 版本错配;建立编译/单测/CI 基线 |
没有基线就没有“拆分正确性” |
| P0-2 | 口令与内网地址外置、轮换;关闭测试接口 | 安全红线,改造成本低 |
| P0-3 | 产出表归属矩阵 + 接口归属清单 + JcglService 职责地图 |
后续所有设计的输入 |
| P0-4 | 语音域基线修复:AMI 连接自愈、会议桥粒度定稿(D11)、分机台账建立 | 语音是叫乘主链路,现状无台账、无重连、会议桥硬编码 |
| P1-1 | 跨端接口契约(17 + 3 个)+ 上报信封 + outbox 模型 | 决定拆分能否落地 |
| P1-2 | 公寓端设备网关(一机一密 + 指令 ACK + 离线队列) | 现场稳定性收益最直接 |
| P1-3 | 叫乘状态机单一入口 + 应用端盯控台(基于 JcXx.vue 抽取) |
降低同库后脏写风险 |
| P1-4 | 语音域模型与接口(5.4 / 6.4)+ 配置收敛与对账(5.5) | 决定分机能否日常运维,也是录音可追溯的前提 |
| P2-1 | 账务:平台主账 + 公寓流水 + 日对账;餐饮改走账务接口 | 数据一致性风险最高的一环 |
| P2-2 | 运安接入上收平台,公寓端仅消费计划 | 解除多公寓匹配限制 |
| P2-3 | 单公寓试点 → 多公寓推广 → 旧逻辑下线与回滚演练 | 控制现场风险 |
| P2-4 | 公寓端 KingbaseES → MySQL 切换与双跑(5.6) | 影响全库,必须与试点同步,不能拖到推广后 |
11.3 后续动作
- 以第 1、4、5 章为模板,产出《表归属矩阵》与《接口归属清单》两份工作底稿。
- 以第 6 章为蓝本,先冻结 v1 契约(含错误码与幂等语义),再进入任何编码。
- 以第 3.3 节为基础,明确“值班员可写字段白名单”,作为 Code Review 检查项。
- 本文落盘后,在
docs/README.md与docs/09-迭代指引与风险清单.md中建立索引。 - 语音域专项(会议桥粒度 D11、号段规则 D12、录音合规 D15)需在阶段二冻结契约前定稿,否则分机表与语音接口会返工;这三项不依赖开发进度,宜先推。
附录 A 术语表
表 A-1 术语表
| 术语 | 说明 |
|---|---|
| 平台端 | 提供主数据、运安计划、账务主账、餐饮、跨公寓报表与审计的中心服务 |
| 公寓端 | 部署在某公寓现场、负责设备接入与叫乘执行的本地服务(基于 RuoYi 新建) |
| 应用端 | 与公寓端同代码同库、面向值班员的盯控与操作界面,不是独立服务或独立库 |
| 执行域 / 操作域 | 同一库内的两类数据与模块:执行域由执行引擎与设备网关写;操作域记录人工干预与审核 |
| 水位(watermark) | 公寓端上报序号,用于判断漏报、乱序与对账进度 |
| 幂等键 | 同一业务对象同一版本仅生效一次的去重标识 |
| 快照版本 | 下行主数据/规则的版本号,公寓端据此判断是否过期 |
| 影子上报 | 迁移期只上报不生效的写入通道,用于验证新链路而不改变业务结果 |
| 分机(endpoint) | Asterisk 侧的一个 SIP/PJSIP 终端对象,对应一个号码;与“房间”是多对多绑定关系 |
| 中继(trunk) | Asterisk 与外部语音系统/运营商对接的通道,不直接绑定房间 |
| AMI | Asterisk Manager Interface,公寓端向 Asterisk 发命令并接收事件的通道 |
| 会议桥(conference bridge) | Asterisk ConfBridge 应用中的一个会议实例;现状固定为 Conf-01 |
| 期望态 / 实际态 | 期望态在本地库(分机台账),实际态在 Asterisk 配置;两者以对账收敛 |
| 收敛(reconcile) | 将本地台账变更应用到 Asterisk 并回写结果的过程 |
| 漂移(drift) | 本地台账与 Asterisk 实际配置的差异,记入 APT_AST_DRIFT |
附录 B 图表清单
表 B-1 图表索引
| 编号 | 名称 | 位置 |
|---|---|---|
| 表 0-1 | 文档信息 | 0 章 |
| 表 0-2 | 修订记录 | 0 章 |
| 表 0-3 | 本次修订涉及的小节与表号 | 0 章 |
| 表 1-1 | 现状事实与拆分影响 | 1.1 |
| 表 1-2 | 主要模块定位与目标归属 | 1.2 |
| 图 2-1 | 目标逻辑架构 | 2.1 |
| 图 2-2 | 部署拓扑 | 2.2 |
| 表 2-1 | 三端调用关系与数据流 | 2.3 |
| 表 2-2 | 公寓端与应用端技术基线 | 2.4 |
| 图 2-3 | 应用端与公寓端复用形态 | 2.5 |
| 表 2-3 | 复用维度与实现落点 | 2.5 |
| 表 3-1 | 应用端与公寓端的逻辑分工 | 3.3 |
| 表 3-2 | 语音/分机/事件的三端分工 | 3.4 |
| 表 4-1 | 能力归属决策表 | 4.1 |
| 表 4-2 | 平台端模块清单 | 4.2 |
| 表 4-3 | 公寓端模块清单 | 4.3 |
| 图 4-1 | 语音模块依赖方向 | 4.4 |
| 表 4-4 | 语音模块职责与依赖约束 | 4.4 |
| 表 5-1 | 数据分层、归属与同步方向 | 5.1 |
| 表 5-2 | 各场景一致性策略 | 5.2 |
| 表 5-3 | 语音域表清单、主键与关键约束 | 5.4 |
| 表 5-4 | 语音域字段级 MySQL 适配要点 | 5.4 |
| 表 5-5 | 分机配置一致性机制 | 5.5 |
| 表 5-6 | 数据库差异与影响 | 5.6.1 |
| 表 5-7 | 迁移步骤与退出条件 | 5.6.2 |
| 表 5-8 | 平台端数据库选项对比 | 5.6.3 |
| 表 5-9 | 切换前必查清单 | 5.6.3 |
| 表 6-1 | 平台端与公寓端关键接口 | 6.1 |
| 表 6-2 | 跨端可靠性机制 | 6.3 |
| 表 6-3 | 公寓端语音域接口清单 | 6.4 |
| 表 7-1 | 设备认证阶段设计 | 7.2 |
| 表 7-2 | 心跳、指令与状态采集 | 7.3 |
| 表 7-3 | 设备域数据上报与不上报 | 7.6 |
| 图 7-1 | 公寓内语音链路 | 7.7 |
| 表 7-4 | 分机资源状态机 | 7.8 |
| 表 7-5 | 关键事件与处理动作 | 7.9 |
| 表 7-6 | 能力闭环矩阵 | 7.10 |
| 表 7-7 | 自检项与失败处理 | 7.11 |
| 表 8-1 | 三端部署形态 | 8.1 |
| 表 8-2 | 非功能要求与实现要点 | 8.2 |
| 表 8-3 | 单机资源与容量 | 8.3 |
| 表 8-4 | 端口与防火墙 | 8.3 |
| 表 8-5 | 录音存储与清理 | 8.3 |
| 图 9-1 | 实施路径 | 9 章 |
| 表 9-1 ~ 9-6 | 各阶段目标/产出/验收/风险 | 9 章 |
| 表 10-1 | 风险与应对 | 10.1 |
| 表 10-2 | 待决策点与建议 | 10.2 |
| 表 10-3 | 语音域与数据库迁移专项风险 | 10.3 |
| 表 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 |
阶段零修复项 |
| AMI 连接单例 | jcgl/service/AsteriskAmiConfig.java(@Bean 内 login(),失败仅打印) |
语音域连接自愈改造依据 |
| 会议桥与事件跟踪 | jcgl/service/AsteriskCallUtil.java(Conf-01 硬编码、recordlist 内存跟踪、conftts 单槽位、CALL_TIMEOUT_MS=30000、接听后自动挂断 40s/喊话 30s) |
7.7–7.11 与 5.4 的设计输入 |
| 叫乘发起拨号计划 | 同上:PJSIP/<console> → 1701@from-internal、PJSIP/<sip_account> → 1901@from-internal、Call-Info: answer-after=5 |
拨号计划自检项与分机参数基线 |
| 录音结果回写 | jcgl/service/JcglService.java(updateJcxxRyzt → appendRecordFile 逗号拼接)、mapper/jcgl/JcglMapper.xml(recordfile 列) |
录音索引拆表依据(5.4) |
| 分机无台账 | mapper/jcgl/JcglRoomMapper.xml、RoomEntity.sip_account |
APT_SIP_ENDPOINT 拆分依据 |
| 语音配置与串口 | application.yml / application-{local,prod}.yml(callcontrol.ami.*:host/port 7058/user/pwd/console、callcontrol.console.*:COM1 9600 8N1、callcontrol.recording.base-url) |
8.3 部署与 8.4 端口依据 |
| MySQL 驱动已就位 | yktgl/pom.xml(mysql-connector-j、kingbase8、ojdbc8 同时存在) |
5.6 数据库切换的可行性依据 |
| 多公寓部署差异 | yktgl/src/main/resources/application-*.yml |
库隔离与 Profile 收敛依据 |
附录 D 文档维护约定
- 本文为设计方案,实施过程中若与代码现状出现偏差,以代码为准并同步更新本文,不得反向以文档为借口修改既有约定。
- 新增/调整端职责、模块划分、跨端接口时,同步更新第 3、4、6 章与附录 B 的图表清单。
- 每个迁移阶段结束,在第 9 章对应阶段表下追加“实际完成情况”与偏差说明。
- 决策点在 10.2 定稿后,从“待决策”移入正文对应章节,并保留决策时间与决策人。
- 语音域任何变更(新增事件类型、改变会议桥粒度、调整录音路径或留存期)必须同步更新 5.4、5.5、7.7–7.11 与表 8-5,并在表 C-1 补充对应代码位置;不得只改代码不改本文。