feat(elevator): 对齐 V1 lib 的 Davinci/扫描/事件与部署配置

- davinci-manager-storage:FilePart 路径与基址按 V1 JAR(/portal/file、/part/*、GET /download)
- 启动类:扫描 cn.cloudwalk.serial 与 cn.cloudwalk.cwos.client.resource,补 UUIDSerial 与 ApplicationService
- deploy:v1/v2 application 中 cloudwalk.serial.enabled、Kafka 指向 192.168.3.12:9092;deploy/.gitignore 忽略日志
- cloudwalk-common-serial:补充 META-INF/spring.factories(Boot 自动配置)
- 电梯:Session 配置、Davinci Bean、Feign 包、MQTT/Visitor/Zone Feign;部署脚本与 API parity 工具更新
- 文档与根脚本若干;未纳入大体积 jar/zip 与 v1 CFR 对比目录

Made-with: Cursor

Former-commit-id: b76d142d13ebb5c0898de2d9d11bc583876829c2
This commit is contained in:
反编译工作区
2026-04-28 01:02:31 +08:00
parent be7a8e9d89
commit 418c7db202
61 changed files with 2967 additions and 461 deletions
@@ -3,7 +3,7 @@
> **范围**`/media/zebra/9e8fa357-7db6-4d70-88ed-d5de5a059a663/星河湾星中星/反编译` 下 `maven-*` 目录内全部 `pom.xml`(**主聚合工程 5 个 + 补充反应堆 7 个**,合计 **44** 个 `pom.xml` 文件)。
> **说明**:子模块未单独声明 `<version>` 时,与**反应堆(reactor)父 POM** 的 `<version>` 一致。
> **生成方式**:走查各 `pom.xml` 的 `parent`、`groupId`、`artifactId`、`version`、`properties` 关键项。
> **版本演进(主版本升级)**:相对历史反编译/私服线,本工作区工程坐标已整体抬升主版本号以便区分 —— 电梯 **2.0-SNAPSHOT**、intelligent **3.0.0-xinghewan**、cloudwalk-cloud **4.0.0-Brussels-SRX**、ninca-crk **2.0.0**、ninca-qk-alarm **1.0.0-SNAPSHOT**`cloudwalk.internal.version` 与 intelligent 依赖属性已同步
> **版本演进(主版本升级)**:相对历史反编译/私服线,本工作区工程坐标抬升主版本号以便区分。**电梯运行口径**:依赖 **`intelligent-cwoscomponent` 2.9.2-xinghewan**(与 `cw_lib` 一致,**不使用** 3.0.0);**`maven-intelligent-cwoscomponent` 源码反应堆** 仍为 **3.0.0-xinghewan**(与其它工程/历史 3.x 线兼容)。另:cloudwalk-cloud **4.0.0-Brussels-SRX**、ninca-crk **2.0.0**、ninca-qk-alarm **1.0.0-SNAPSHOT**。
---
@@ -45,7 +45,7 @@
| `cw-elevator-application-service/` | `cw-elevator-application-service` | **2.0-SNAPSHOT**(继承) | 同上 |
| `cw-elevator-application-web/` | `cw-elevator-application-web` | **2.0-SNAPSHOT**(继承) | 同上 |
**反应堆内常用属性(节选)**`cloudwalk.internal.version` **4.0.0-Brussels-SRX**`intelligent.cwoscomponent.version` **3.0.0-xinghewan**`fastjson.version` **1.2.83**`guava.version` **28.2-jre**`poi.version` **4.1.2**`java.version` **1.8**
**反应堆内常用属性(节选)**`cloudwalk.internal.version` / `cloudwalk.legacy.public.version` **3.7.2-Brussels-SRX**`intelligent.cwoscomponent.version` **2.9.2-xinghewan**(与 **`cw_lib`** / 上线口径一致)`fastjson.version` **1.2.73**`guava.version` **28.2-jre**`poi.version` **4.1.2**`java.version` **1.8**
---
@@ -187,8 +187,8 @@
| 概念 | 常见取值 | 出现在 |
|------|----------|--------|
| 云从内部线版本 | **4.0.0-Brussels-SRX** | 电梯、intelligent、cloudwalk-cloud、ninca-crk、ninca-qk-alarm `cloudwalk.internal.version` |
| intelligent 组件线 | **3.0.0-xinghewan** | intelligent 反应堆;电梯 `intelligent.cwoscomponent.version`ninca-crk `intelligent.cwoscomponent.rest.version` |
| 云从内部线版本 | **3.7.2**(电梯常用) / **4.0.0**(部分工程) | 电梯、ninca-qk-alarm `cloudwalk.internal.version` 多为 **3.7.2****cloudwalk-cloud / ninca-crk** 等仍可能 **4.0.0** |
| intelligent 组件线 | **2.9.2-xinghewan**(电梯) / **3.0.0-xinghewan**(反应堆源码、ninca-crk | **电梯** `intelligent.cwoscomponent.version` **必须为 2.9.2**(与 `cw_lib` 一致);**`maven-intelligent-cwoscomponent`** 反应堆仍为 **3.0.0****ninca-crk** `intelligent.cwoscomponent.rest.version` 多为 **3.0.0** |
| 设备 SDK 协议实体 | **2.2.0** | intelligent `cloudwalk.device.sdk.version`**`maven-cloudwalk-device-sdk`** 反应堆版本 |
| 设备管理 common/interface | **2.0.2** | **`maven-cloudwalk-device-manager`**;依赖 **`cloudwalk-common-result` 3.7.2****`maven-cloudwalk-legacy-public`** |
| AKS / 设备认证 interface | **1.0.0-SNAPSHOT** | **`maven-cwos-common-aks`****`cwos-common-aks-interface`**)、**`maven-cwos-device-authentication`****`cwos-device-authentication-interface`**);与 **`cw_lib`** 同名 JAR 对齐;**device-authentication** 另依赖 **`cloudwalk-common-service`****`maven-cloudwalk-cloud`**)、**device-manager-interface**、**protocol-entity** |
@@ -272,6 +272,7 @@ maven-ninca-qk-alarm/ninca-qk-alarm-app-starter/pom.xml
| 2026-04-24 | 补充反应堆 **`maven-cloudwalk-device-manager`****`cloudwalk-device-manager` 2.0.2**、**common**、**interface****`反1`** zip);`pom` 总数 **37 → 40**;须在 **`maven-cloudwalk-legacy-public`** 之后 install;见 [本地编译说明.md](../build/本地编译说明.md) §3 |
| 2026-04-24 | 补充反应堆 **`maven-cwos-common-aks`**、**`maven-cwos-device-authentication`****`反1`**`cwos-common-aks-interface``cwos-device-authentication-interface`);`pom` 总数 **40 → 44****device-authentication** 须在 **`maven-cloudwalk-cloud`** 与 **`maven-cwos-common-aks`** 之后 install;见 [本地编译说明.md](../build/本地编译说明.md) §3 |
| 2026-04-24 | **`intelligent-cwoscomponent-rest`**`FeignClient`**`org.springframework.cloud.netflix.feign`** 改为 **`org.springframework.cloud.openfeign`**,与 **Greenwich + `spring-cloud-starter-openfeign`** 一致 |
| 2026-04-27 | **电梯** `intelligent.cwoscomponent.version` 定为 **2.9.2-xinghewan**(与 **`cw_lib`**、上线口径一致,**禁止 3.0.0**);`build_nexus_only.sh` 改为从 **`cw_lib` + 父 POM 桩** install-file,不再默认编译 **`maven-intelligent-cwoscomponent` 3.0.0** |
---
@@ -1,227 +1,227 @@
# 对外接口不变:走查任务与状态
> **依据**:[对外接口不变-远程调用与性能优化约定](对外接口不变-远程调用与性能优化约定.md)(§2 总原则、§3 场景、§4 优先级)。
> **走查代码根**`maven-cw-elevator-application/cw-elevator-application-service`2026-04-24 静态走查)。
> **说明**:下表「子任务数」指**与约定相关的 RPC/可优化循环次数上界**(随运行时数据规模变化);**状态**表示在**不扩展 Feign/HTTP 契约**前提下是否建议动代码。
> **排期(2026-04**`frontend/` 暂无可用前端工程;**凡需改前端、登记页、物业管理端 UI 的迭代,暂跳过**(详见 [docs/README 当前排期与范围](../README.md#当前排期与范围2026-04))。本文档仅跟踪**电梯等服务端**在约定下的走查与修正。
---
## 1. 总览表(子任务量 + 可修正 / 不可修正)
| 约定 § | 代码锚点 | 子任务数(上界) | 下一可修正动作(建议顺序) | 不可修正或须前置确认 |
|--------|----------|------------------|----------------------------|------------------------|
| **§3.1** | `ImageRuleRefServiceImpl#delete`(约 575598 行) | **`N = param.getIds().size()`** 次 `updateGroupPersonRef`(每规则删后各 1 次) | **P0**:全部 `deleteById` 完成后,对本轮涉及的 `labelIds``organizationIds` **去重并集**,调用 **1 次** `updateGroupPersonRef`;为每次 RPC 增加 **`isSuccess` 校验**(与 §2.2 一致) | **须图库/通行确认**:合并调用是否为「刷新引用」语义、是否等价于当前 N 次效果;若不能确认则**不得合并**,仅可补返回值校验与日志(**工作区走查与方案审核见 §6**) |
| **§3.2** | `AcsPersonServiceImpl#delete` | **`P = param.getPersonIds().size()`** 次 `imageStorePersonService.delete` | **P1 首轮已实施****§8**):`elevatorRemoteBoundedExecutor` 按批 `invokeAll`(默认并发 **6**),子线程 **`FeignThreadLocalUtil.callWithContext`**;遇失败**整批后**即返回 `CloudwalkResult.fail`(与原先顺序循环遇错即停一致,**非**单条失败即取消同批其它在途 RPC) | **无批量 delete**;同批内并行语义见 **§8.1** |
| **§3.3** | `AcsPassRuleServiceImpl#listFloor` | **`F = passRuleResults.size()`** 次 `acsPersonService.page` | **P1 首轮已实施****§8**):楼层 `page` 有界并行(默认 **6**),`personTotals[]` 按下标写回,**列表顺序不变** | **禁止**本地 count 替代 `totalRows`RPC 上界仍为 **F** |
| **§3.4** | `AcsPassRuleServiceImpl#addImageStore` 内设备绑图库 | **`D = deviceList.size()`** 次 `bindDeviceAndImageStore` | **P1 首轮已实施****§8**):`bind` 有界并行;失败仍 **`rollbackImageStoreAfterBindFailure`** 后抛 `ServiceException`(与同批已绑设备竞态与顺序循环**同类**) | **无批量 bind** |
| **§3.5** | `AcsDeviceTaskServiceImpl#updateFloors` | 增删楼层远程调用**墙钟**上界仍分别为 **A**、**D** 次;**有界并行**可缩短多楼层场景耗时(**迭代 5**,见 **§9** | 内层 `imageRuleRefService.delete` 仍受 **§3.1 冻结** 时的 RPC 上界与语义约束;`@Async("updateFloorsExecutor")` 下背压与 `catch` 细粒度见 **§7.3** / **§9** |
---
## 2. 数量小结(便于排期)
| 类型 | 计数符号 | 含义 |
|------|----------|------|
| **§3.1 可合并 RPC** | 由 N 降为 **1**(在语义确认后) | 规则批量删除场景收益最大 |
| **§3.2 并行度** | P | 人员多 ID 删除 |
| **§3.3 并行度** | F | 楼层列表人数统计 |
| **§3.4 并行度** | D | 设备绑图库 |
| **§3.5** | A + D | 异步任务按层调用 |
---
## 3. 迭代规划与下一迭代范围(不改 HTTP/Feign 签名)
### 冻结范围(2026-04-25
| 项 | 说明 |
|----|------|
| **约定 §3.1 全范围** | 未取得图库/通行对 `updateGroupPersonRef` 语义确认前,**不进行**与该约定相关的**任何**代码修正与优化(含 **合并 N→1** 及 §6.4 所述**仅 `isSuccess` 校验**小步),避免在无确认期分散实现与回滚成本。 |
| **恢复条件** | 图库书面或接口说明确认 + 在 [约定文档 §3.1](对外接口不变-远程调用与性能优化约定.md) 文末回填对接人、日期;再按 §1 表拆分 PR(合并与返回值校验可分步)。 |
| **前端相关** | 仓库**暂无可用前端**;所有需改**前端 / 登记页 / 物业端页面**的迭代**暂不排期、不执行**(与本文服务端走查无冲突;产品方案中 UI 类阶段见 [docs/README](../README.md#当前排期与范围2026-04))。 |
### 迭代 3**§3.5 `updateFloors`**
| 字段 | 内容 |
|------|------|
| **状态** | **走查与首轮修正已完成**(见 **§7**):`getById` 空防护、步骤级 `CloudwalkResult` 校验、`keepAliveSeconds` 绑定线程池。 |
| **约定锚点** | **§3.5** `AcsDeviceTaskServiceImpl#updateFloors` |
| **暂缓项** | 楼层有界并行**已**见 **§9**`AbortPolicy``catch` 语义、其它见 **§7.3**。 |
### 迭代 4**P1:§3.2 / §3.3 / §3.4 + 统一有界池**
| 字段 | 内容 |
|------|------|
| **状态** | **已实施**(实现说明与语义边界见 **§8**)。 |
| **线程池 Bean** | `elevatorRemoteBoundedExecutor``ElevatorRemoteIoExecutorConfig`),配置前缀 **`ninca.elevator.remote-io.pool`**(默认 core=max=**6**queue=512`CallerRunsPolicy`)。**未**与 `updateFloorsExecutor` 合并,避免异步任务与同步 RPC 抢同池。 |
| **公共能力** | `FeignThreadLocalUtil.callWithContext``cw-elevator-application-common`):子线程执行 Feign 前绑定/恢复 ThreadLocal 请求头。 |
### 迭代 5**§3.5 `updateFloors` 楼层有界并行**
| 字段 | 内容 |
|------|------|
| **状态** | **已实施**(见 **§9**):增楼列表、删楼列表在各自阶段内以最多 **6** 路**并发**执行 `add` / `addOnlyRule` / `delete` 等;**`BIND_DEVICES` 仍按原列表顺序**、每层成功**仍 +1** 重读任务行,与纯串行「进度语义」一致;子线程 Feign 经 `FeignThreadLocalUtil``ruleMap``zoneId` 时用 **`getOrDefault(…, "")`** 防 NPE。 |
| **不变更** | **§3.1 全冻结** 期间未改 `ImageRuleRefServiceImpl#delete``updateGroupPersonRef` 次数。 |
### 迭代 6 及以后(可选)
- **调参 / 观测**`ninca.elevator.remote-io.pool` 按环境压测;必要时为 `updateFloors` 批处理增加**指标/耗时日志**(不扩展 HTTP 响应)。
- **§3.1**:图库确认后再评估合并 N→1 与(若允许)`updateGroupPersonRef``isSuccess` 小步。
**已完成回顾**:迭代 1 — **§5**;迭代 2 — **§6**(§3.1 冻结);迭代 3 — **§7**;迭代 4 — **§8**;迭代 5 — **§9**。
---
## 4. 文档维护
| 项目 | 内容 |
|------|------|
| 更新触发 | `ImageRuleRefServiceImpl#delete``AcsPersonServiceImpl#delete``AcsPassRuleServiceImpl#listFloor` / `#addImageStore``AcsDeviceTaskServiceImpl#updateFloors` 任一处重构或签契约变更 |
| 结论回填 | 图库对 §3.1 的确认结论请写回 [约定文档 §3.1](对外接口不变-远程调用与性能优化约定.md) 文末建议行(对接人 + 日期) |
---
## 5. 迭代 1`AcsPassRuleServiceImpl#listFloor` 走查结论(§3.3 / §2.2
**走查日期**2026-04-25
**代码位置**`maven-cw-elevator-application/.../passrule/impl/AcsPassRuleServiceImpl.java` 方法 `listFloor`
| 检查项 | 现状 | 结论 |
|--------|------|------|
| `zoneService.tree` 返回值 | 已校验 `zoneTree.isSuccess()`,失败抛 `ServiceException` | **通过** |
| `acsPersonService.page` 返回值 | 循环内**未**校验 `page.isSuccess()`,直接 `page.getData()``totalRows` | **不通过**:违反约定 **§2.2**Feign 失败时 `getData()` 可能为 null,存在 **NPE** 风险,且可能把失败误当「0 人」 |
| `page.getData()` 空指针 | 未防护 | **不通过**:与上项合并修正 |
| `rowsOfPage` | 当前为 `10`,仅使用 `totalRows` | **建议**:改为 **`1`**(约定 §3.3:仅取总数,略减负载),**不改变** HTTP 响应字段 |
**评估结论(是否允许进入代码修正)****通过进入修正** — 仅补充与 `zoneTree` 分支一致的失败处理及空数据防护,**不**改变对外 JSON 字段语义;`rowsOfPage=1` 与现逻辑(只读 `totalRows`)等价。
**代码修正(已应用,2026-04-25**`AcsPassRuleServiceImpl#listFloor` — 增加 `page.isSuccess()` 失败抛错、`page.getData()` 为空时 `personNumber=0``CloudwalkPageInfo(1,1)` 仅取总数。
**修正实施后**:提交 **`e652eb3`**(分支 `v0.11`)。
---
## 6. 迭代 2`ImageRuleRefServiceImpl#delete` 与 §3.1 方案走查(仅评估,未改代码)
**走查日期**2026-04-25
**目标**:在全工作区定位**图库(intelligent 图库人员服务)**与**通行规则(电梯应用本地)**相关代码,审核「合并 `updateGroupPersonRef`」方案是否需图库侧语义确认后方可实施。
### 6.1 图库 / 通行相关代码位置(工作区)
| 层级 | 路径/符号 | 职责 |
|------|-----------|------|
| **Feign 契约** | `maven-intelligent-cwoscomponent/intelligent-cwoscomponent-rest/.../feign/ImageStorePersonFeignClient.java` | `POST .../updateGroupPersonRef`,请求体 `UpdateGroupPersonRefParam` |
| **DTO** | `maven-intelligent-cwoscomponent/intelligent-cwoscomponent-interface/.../param/UpdateGroupPersonRefParam.java` | `businessId``imageStoreId``personIds``labelIds``organizationIds`(可同时只填部分字段) |
| **客户端封装** | `.../service/RestImageStorePersonServiceImpl.java` | 透传 Feign |
| **电梯侧 §3.1 锚点** | `maven-cw-elevator-application/.../passrule/impl/ImageRuleRefServiceImpl.java` 方法 **`delete`**(约 571605 行) | 按 `param.getIds()` 循环:`listByParentRule` → 收集子规则 `includeLabels` / `includeOrganizations`**`deleteById`** → **`imageStorePersonService.updateGroupPersonRef`**(每删一条父规则 1 次 RPC) |
| **同文件其它 `updateGroupPersonRef`** | `addOnlyRule`(约 434439)、`update` 内分支(约 556~562) | 新增/编辑规则后刷新;**同样未校验** `CloudwalkResult.isSuccess()` |
| **人员通行规则** | `maven-cw-elevator-application/.../person/impl/PersonRuleServiceImpl.java` | `add` / `addVisitor` / `delete` 末尾各 1 次 `updateGroupPersonRef`,入参以 **`personIds`** 为主(与 `delete`**label/org** 为主不同) |
| **HTTP 入口** | `cw-elevator-application-web/.../AcsPassRuleController.java` | `imageRuleRefService.delete` |
| **异步任务调用** | `AcsDeviceTaskServiceImpl#updateFloors` | 删楼层时多构造 `deleteParam.setIds(Collections.singletonList(ruleId))` **逐层**调 `imageRuleRefService.delete`;单次 `delete` 内仍可能 1 次或多次 `updateGroupPersonRef`(视该层 `ids` 数量) |
**说明**:本仓库**无** intelligent 图库服务端的 `updateGroupPersonRef` 业务实现源码,仅能依据 DTO 与调用方推断语义;**与图库/通行团队确认**仍属 §3.1 前置条件。
### 6.2 当前 `delete` 行为摘要(与合并相关)
- `imageStoreId` 来自 **`deviceImageStoreDao.getByBuildingId(param.getParentId())`**,整次批量删除共用同一图库。
- 对每个待删父规则 `id`:先读**该父规则下子规则**的 label/org(子行若带 label 则 `continue`,**同一子行不会同时写入 org**,与数据模型一致),再删父规则,再带着**本轮** `includeLabels` / `includeOrganizations` 调图库刷新。
- **`updateGroupPersonRef` 的返回值未做 `isSuccess` 校验**(违反约定 §2.2;与 `getImageStorePerson` 等分支不一致)。
### 6.3 约定中的「合并」方案审核
| 维度 | 结论 |
|------|------|
| **与现网 N 次调用的等价性** | 若图库侧语义为:在**给定 `imageStoreId`** 下,对传入的 **labelIds / organizationIds 集合**做**增量刷新或按维度重算引用**(各维度独立、与顺序无关),则「删库前汇总所有待删父规则的子维度 → **去重并集** → 删库完成后 **1 次** `updateGroupPersonRef`」与「每删一条父规则刷新其维度子集」在**最终一致**上通常等价。 |
| **必须向图库确认的风险** | 若远端实现为「以本次入参**覆盖/裁剪**图库可见范围」或依赖**调用顺序**产生副作用,则合并后的**单次并集**与 N 次**子集递进**可能不等价。约定文档 §3.1 所述「非破坏性刷新」即针对此。 |
| **`personIds``labelIds`/`organizationIds` 混用** | `PersonRuleServiceImpl``personIds` 路径;`ImageRuleRefServiceImpl#delete` 走 label/org。合并方案**不改变** `delete` 仅设 label/org 的现状;但若图库服务在**未传 `personIds`** 时对空列表有特殊含义,仍须一并确认。 |
| **空列表仍 RPC** | 当前循环在子规则为空时仍调用 `updateGroupPersonRef`(两列表皆空)。合并后是否**跳过空并集**可减少无效 RPC,但属于**行为微调**,若图库依赖「空刷」触发全量重算,须图库确认后再定。 |
| **不合并时的安全增量** | 在未获图库书面确认前,**仅**可为每次 `updateGroupPersonRef` 增加 **`isSuccess` 校验 + 失败抛 `ServiceException`**(及可选日志),**不改变** RPC 次数;与约定「退化为循环调用 + 返回值校验」一致。 |
### 6.4 评估结论(是否允许进入 §3.1「合并」类代码修正)
- **合并 N→1**:**不允许在图库/通行确认前实施** — 与 §1 表及约定 §3.1 前置条件一致。
- **仅返回值校验(及可选空并集跳过,若产品同意)**:**允许作为独立小步** — 不依赖远端语义新假设,符合 §2.2。
**图库确认建议提问(可复制)**:「对同一 `imageStoreId``updateGroupPersonRef` 在仅设置 `labelIds``organizationIds``personIds` 为空)时,是否为**按这些维度刷新人员引用**且**不会**将图库维度裁剪为仅等于本次入参?多次调用子集与单次调用**并集**是否在业务上等价?」
**实施后回填**:确认结论、对接人、日期写入 [约定文档 §3.1 文末](对外接口不变-远程调用与性能优化约定.md)(见约定 §5)。
**排期决策(2026-04-25**:在取得图库确认前,**冻结**约定 **§3.1** 相关全部代码变更;下一迭代转 **§3.5**(见上文 **「迭代 3」**)。
---
## 7. 迭代 3`AcsDeviceTaskServiceImpl#updateFloors` 走查结论(§3.5
**走查日期**2026-04-25
**代码位置**`cw-elevator-application-service/.../device/impl/AcsDeviceTaskServiceImpl.java` 方法 `updateFloors`;线程池 `.../common/UpdateFloorsTaskExecutor.java`;配置 `UpdateFloorsPoolProperties``ninca.update.floor.pool.*`,默认 core=3、max=5、queue=100、`AbortPolicy`)。
### 7.1 调用链与 RPC 上界(与 §1 表对齐)
| 分支 | 行为 | 上界 |
|------|------|------|
| 增楼层 | `personRuleService.add` **或** `imageRuleRefService.addOnlyRule`,成功后 `updateBingDevices` | `addFloors.size()` |
| 删楼层 | `personRuleService.delete` **或** `imageRuleRefService.delete`(单 id**或** 仅 DAO `deleteByOrgAndLabel`,成功后 `updateBingDevices` | `delFloorIds.size()` |
| 内层放大 | `imageRuleRefService.delete` 仍受约定 **§3.1** 冻结影响(`updateGroupPersonRef` 多次);本迭代**未**改该内层。 | 不变 |
**入口**`AcsElevatorDeviceServiceImpl#bindingFloors` / `#bindingPerson` 在插入任务行后**同步**调用 `updateFloors`;方法带 `@Async`,实际在 **`updateFloorsExecutor`** 线程执行;HTTP 已返回 `taskId` 后,**异步内失败不会回写该 HTTP 响应**(现网行为保持;运维依赖任务进度与日志)。
### 7.2 检查项与结论
| 检查项 | 现状(走查时) | 结论 |
|--------|----------------|------|
| `acsDeviceTaskDao.getById` | 未判空即 `task.getIsStop()`,存在 **NPE** 风险(数据异常或竞态) | **不通过** → 已修正:空则记录并 `ServiceException` |
| `personRuleService.add/delete``imageRuleRefService.addOnlyRule/delete` 返回值 | 未校验 `CloudwalkResult.isSuccess()`,失败时仍 **`updateBingDevices`**,进度与真实绑定不一致 | **不通过**(违反 §2.2)→ 已修正:统一 `requireTaskStepSuccess`,失败抛错且**不**递增 |
| `catch``ServiceException(e.getMessage())` | 丢失根因类型与栈信息到调用方;异步场景仅日志含 `{}` 与异常 | **记录**:是否改为 `ServiceException(code, msg)``initCause` 属产品/运维范围,**本轮不改** |
| 线程池 `keepAliveSeconds` | `UpdateFloorsPoolProperties` 有字段,**Bean 未 `setKeepAliveSeconds`**,配置项无效 | **缺陷** → 已在 `UpdateFloorsTaskExecutor` 绑定 |
| `RejectedExecutionHandler` | `AbortPolicy`,队列满时拒绝提交 | **记录**:与背压策略相关,**本轮不改**(须与运维对齐) |
| 删楼 `ruleMap.get(delFloorId)` | 若 `listZoneInfoByIds` 未覆盖某 `delFloorId` 可能 **null** 拼接 `ruleName` | **记录**:数据正常时风险低;**本轮未改**(可后续与 DAO 对齐) |
### 7.3 评估结论(是否允许进入代码修正)
- **允许并已实施(本轮)**`task` 空指针防护;对 **`personRuleService.add` / `delete`**、**`imageRuleRefService.addOnlyRule` / `delete`** 的 **`CloudwalkResult` 成功校验**(约定 §2.2);`updateFloorsExecutor` 绑定 **`keepAliveSeconds`**。
- **暂缓(须单独评审)**:拒绝策略、`catch` 异常语义增强;**楼层有界并行**与 `ruleMap` 缺键防护**已**见 **§9**。
**修正实施后**:提交 **`0ddeedc`**(分支 `v0.11`)。
---
## 8. 迭代 4P1 有界并行(§3.2 / §3.3 / §3.4
**实施日期**2026-04-25
### 8.1 行为与约定对齐说明
| 项 | 说明 |
|----|------|
| **并发度** | 代码常量与默认池 **`corePoolSize=maxPoolSize=6`**(约定 48 区间内),可通过 **`ninca.elevator.remote-io.pool.core-pool-size` / `max-pool-size`** 覆盖。 |
| **§3.2 `delete`** | 多 `personId` 时按批 `ThreadPoolExecutor.invokeAll`;单 ID 仍走主线程(无 Feign 子线程问题)。失败时返回 **`76260407`** 风格 `CloudwalkResult.fail`,与改造前**一致**;**同批内**若某 RPC 失败,`invokeAll` 仍会等本批其它任务结束后再统一 `get()` 抛出/返回,与**严格单线程「第一条失败即不再发起后续」**在「已发起请求数」上略有差异,属典型有界并行取舍。 |
| **§3.3 `listFloor`** | 设备数仍顺序 DAO`acsPersonService.page` 按批并行,结果写入 `personTotals[idx]` 后顺序 `setPersonNumber`**响应楼层顺序不变**。 |
| **§3.4 `addImageStore`** | `bindAppImageStoreDevice` 仍顺序执行;仅 **`bindDeviceAndImageStore`** 按批并行;任一批次中失败则 **`rollbackImageStoreAfterBindFailure`**(抽方法)后抛 `ServiceException`,与原先 try/catch 回滚路径一致。 |
| **Feign ThreadLocal** | 所有子线程 RPC 经 **`FeignThreadLocalUtil.callWithContext`**,避免池化线程串请求头。 |
**实施后提交****`fe571aa`**(分支 `v0.11`)。
---
## 9. 迭代 5`updateFloors` 楼层有界并行(§3.5
**实施日期**2026-04-24(工作区)
### 9.1 设计要点
| 项 | 说明 |
|----|------|
| **并发度** | 与 §8 一致使用 **`elevatorRemoteBoundedExecutor`** 的 **`ThreadPoolExecutor#invokeAll`**,批大小 **`UPDATE_FLOORS_FLOOR_PARALLEL = 6`**(与 `AcsPassRuleServiceImpl` 等处一致)。 |
| **bind 推进** | 同批内各楼层 RPC **可并发**`Future#get()` **按列表下标顺序** 等待;每遇返回值 **1** 则主线程**再** `getById`**`BIND_DEVICES`+1**,与旧实现「每层成功后立即 +1」的**终态**一致。 |
| **停任务** | 每层子步骤开头仍 `getById``isStop != 0` 时该层贡献 **0**、不推进 bind。 |
| **DAO 异常** | 删楼分支中 `getByRuleName` / `deleteByOrgAndLabel`**`DataAccessException`** 在子步内转 **`ServiceException`**,以配合 `Callable``invokeAll` 的异常链。 |
| **§3.1** | 未改 `imageRuleRefService.delete` 实现,内层 `updateGroupPersonRef` 行为与冻结前一致。 |
### 9.2 同批多楼层与「遇错即停」
**§8.1** 类似:同批中若一 Floor 的 RPC 失败,同批**其它在途** Floor 的 RPC 可能已执行完毕;`get()` 按顺序在**首败**时抛出。与严格串行「前一层失败则后层不再发起」在**已发出请求**上可存在差异,属有界并行常见取舍。
### 9.3 代码位置
- `AcsDeviceTaskServiceImpl#updateFloors``#runAddFloorsInBoundedParallel` / `#runDelFloorsInBoundedParallel``#addOneFloorStep` / `#delOneFloorStep``#advanceBindProgressOne`
# 对外接口不变:走查任务与状态
> **依据**:[对外接口不变-远程调用与性能优化约定](对外接口不变-远程调用与性能优化约定.md)(§2 总原则、§3 场景、§4 优先级)。
> **走查代码根**`maven-cw-elevator-application/cw-elevator-application-service`2026-04-24 静态走查)。
> **说明**:下表「子任务数」指**与约定相关的 RPC/可优化循环次数上界**(随运行时数据规模变化);**状态**表示在**不扩展 Feign/HTTP 契约**前提下是否建议动代码。
> **排期(2026-04**`frontend/` 暂无可用前端工程;**凡需改前端、登记页、物业管理端 UI 的迭代,暂跳过**(详见 [docs/README 当前排期与范围](../README.md#当前排期与范围2026-04))。本文档仅跟踪**电梯等服务端**在约定下的走查与修正。
---
## 1. 总览表(子任务量 + 可修正 / 不可修正)
| 约定 § | 代码锚点 | 子任务数(上界) | 下一可修正动作(建议顺序) | 不可修正或须前置确认 |
|--------|----------|------------------|----------------------------|------------------------|
| **§3.1** | `ImageRuleRefServiceImpl#delete`(约 575598 行) | **`N = param.getIds().size()`** 次 `updateGroupPersonRef`(每规则删后各 1 次) | **P0**:全部 `deleteById` 完成后,对本轮涉及的 `labelIds``organizationIds` **去重并集**,调用 **1 次** `updateGroupPersonRef`;为每次 RPC 增加 **`isSuccess` 校验**(与 §2.2 一致) | **须图库/通行确认**:合并调用是否为「刷新引用」语义、是否等价于当前 N 次效果;若不能确认则**不得合并**,仅可补返回值校验与日志(**工作区走查与方案审核见 §6**) |
| **§3.2** | `AcsPersonServiceImpl#delete` | **`P = param.getPersonIds().size()`** 次 `imageStorePersonService.delete` | **P1 首轮已实施****§8**):`elevatorRemoteBoundedExecutor` 按批 `invokeAll`(默认并发 **6**),子线程 **`FeignThreadLocalUtil.callWithContext`**;遇失败**整批后**即返回 `CloudwalkResult.fail`(与原先顺序循环遇错即停一致,**非**单条失败即取消同批其它在途 RPC) | **无批量 delete**;同批内并行语义见 **§8.1** |
| **§3.3** | `AcsPassRuleServiceImpl#listFloor` | **`F = passRuleResults.size()`** 次 `acsPersonService.page` | **P1 首轮已实施****§8**):楼层 `page` 有界并行(默认 **6**),`personTotals[]` 按下标写回,**列表顺序不变** | **禁止**本地 count 替代 `totalRows`RPC 上界仍为 **F** |
| **§3.4** | `AcsPassRuleServiceImpl#addImageStore` 内设备绑图库 | **`D = deviceList.size()`** 次 `bindDeviceAndImageStore` | **P1 首轮已实施****§8**):`bind` 有界并行;失败仍 **`rollbackImageStoreAfterBindFailure`** 后抛 `ServiceException`(与同批已绑设备竞态与顺序循环**同类**) | **无批量 bind** |
| **§3.5** | `AcsDeviceTaskServiceImpl#updateFloors` | 增删楼层远程调用**墙钟**上界仍分别为 **A**、**D** 次;**有界并行**可缩短多楼层场景耗时(**迭代 5**,见 **§9** | 内层 `imageRuleRefService.delete` 仍受 **§3.1 冻结** 时的 RPC 上界与语义约束;`@Async("updateFloorsExecutor")` 下背压与 `catch` 细粒度见 **§7.3** / **§9** |
---
## 2. 数量小结(便于排期)
| 类型 | 计数符号 | 含义 |
|------|----------|------|
| **§3.1 可合并 RPC** | 由 N 降为 **1**(在语义确认后) | 规则批量删除场景收益最大 |
| **§3.2 并行度** | P | 人员多 ID 删除 |
| **§3.3 并行度** | F | 楼层列表人数统计 |
| **§3.4 并行度** | D | 设备绑图库 |
| **§3.5** | A + D | 异步任务按层调用 |
---
## 3. 迭代规划与下一迭代范围(不改 HTTP/Feign 签名)
### 冻结范围(2026-04-25
| 项 | 说明 |
|----|------|
| **约定 §3.1 全范围** | 未取得图库/通行对 `updateGroupPersonRef` 语义确认前,**不进行**与该约定相关的**任何**代码修正与优化(含 **合并 N→1** 及 §6.4 所述**仅 `isSuccess` 校验**小步),避免在无确认期分散实现与回滚成本。 |
| **恢复条件** | 图库书面或接口说明确认 + 在 [约定文档 §3.1](对外接口不变-远程调用与性能优化约定.md) 文末回填对接人、日期;再按 §1 表拆分 PR(合并与返回值校验可分步)。 |
| **前端相关** | 仓库**暂无可用前端**;所有需改**前端 / 登记页 / 物业端页面**的迭代**暂不排期、不执行**(与本文服务端走查无冲突;产品方案中 UI 类阶段见 [docs/README](../README.md#当前排期与范围2026-04))。 |
### 迭代 3**§3.5 `updateFloors`**
| 字段 | 内容 |
|------|------|
| **状态** | **走查与首轮修正已完成**(见 **§7**):`getById` 空防护、步骤级 `CloudwalkResult` 校验、`keepAliveSeconds` 绑定线程池。 |
| **约定锚点** | **§3.5** `AcsDeviceTaskServiceImpl#updateFloors` |
| **暂缓项** | 楼层有界并行**已**见 **§9**`AbortPolicy``catch` 语义、其它见 **§7.3**。 |
### 迭代 4**P1:§3.2 / §3.3 / §3.4 + 统一有界池**
| 字段 | 内容 |
|------|------|
| **状态** | **已实施**(实现说明与语义边界见 **§8**)。 |
| **线程池 Bean** | `elevatorRemoteBoundedExecutor``ElevatorRemoteIoExecutorConfig`),配置前缀 **`ninca.elevator.remote-io.pool`**(默认 core=max=**6**queue=512`CallerRunsPolicy`)。**未**与 `updateFloorsExecutor` 合并,避免异步任务与同步 RPC 抢同池。 |
| **公共能力** | `FeignThreadLocalUtil.callWithContext``cw-elevator-application-common`):子线程执行 Feign 前绑定/恢复 ThreadLocal 请求头。 |
### 迭代 5**§3.5 `updateFloors` 楼层有界并行**
| 字段 | 内容 |
|------|------|
| **状态** | **已实施**(见 **§9**):增楼列表、删楼列表在各自阶段内以最多 **6** 路**并发**执行 `add` / `addOnlyRule` / `delete` 等;**`BIND_DEVICES` 仍按原列表顺序**、每层成功**仍 +1** 重读任务行,与纯串行「进度语义」一致;子线程 Feign 经 `FeignThreadLocalUtil``ruleMap``zoneId` 时用 **`getOrDefault(…, "")`** 防 NPE。 |
| **不变更** | **§3.1 全冻结** 期间未改 `ImageRuleRefServiceImpl#delete``updateGroupPersonRef` 次数。 |
### 迭代 6 及以后(可选)
- **调参 / 观测**`ninca.elevator.remote-io.pool` 按环境压测;必要时为 `updateFloors` 批处理增加**指标/耗时日志**(不扩展 HTTP 响应)。
- **§3.1**:图库确认后再评估合并 N→1 与(若允许)`updateGroupPersonRef``isSuccess` 小步。
**已完成回顾**:迭代 1 — **§5**;迭代 2 — **§6**(§3.1 冻结);迭代 3 — **§7**;迭代 4 — **§8**;迭代 5 — **§9**。
---
## 4. 文档维护
| 项目 | 内容 |
|------|------|
| 更新触发 | `ImageRuleRefServiceImpl#delete``AcsPersonServiceImpl#delete``AcsPassRuleServiceImpl#listFloor` / `#addImageStore``AcsDeviceTaskServiceImpl#updateFloors` 任一处重构或签契约变更 |
| 结论回填 | 图库对 §3.1 的确认结论请写回 [约定文档 §3.1](对外接口不变-远程调用与性能优化约定.md) 文末建议行(对接人 + 日期) |
---
## 5. 迭代 1`AcsPassRuleServiceImpl#listFloor` 走查结论(§3.3 / §2.2
**走查日期**2026-04-25
**代码位置**`maven-cw-elevator-application/.../passrule/impl/AcsPassRuleServiceImpl.java` 方法 `listFloor`
| 检查项 | 现状 | 结论 |
|--------|------|------|
| `zoneService.tree` 返回值 | 已校验 `zoneTree.isSuccess()`,失败抛 `ServiceException` | **通过** |
| `acsPersonService.page` 返回值 | 循环内**未**校验 `page.isSuccess()`,直接 `page.getData()``totalRows` | **不通过**:违反约定 **§2.2**Feign 失败时 `getData()` 可能为 null,存在 **NPE** 风险,且可能把失败误当「0 人」 |
| `page.getData()` 空指针 | 未防护 | **不通过**:与上项合并修正 |
| `rowsOfPage` | 当前为 `10`,仅使用 `totalRows` | **建议**:改为 **`1`**(约定 §3.3:仅取总数,略减负载),**不改变** HTTP 响应字段 |
**评估结论(是否允许进入代码修正)****通过进入修正** — 仅补充与 `zoneTree` 分支一致的失败处理及空数据防护,**不**改变对外 JSON 字段语义;`rowsOfPage=1` 与现逻辑(只读 `totalRows`)等价。
**代码修正(已应用,2026-04-25**`AcsPassRuleServiceImpl#listFloor` — 增加 `page.isSuccess()` 失败抛错、`page.getData()` 为空时 `personNumber=0``CloudwalkPageInfo(1,1)` 仅取总数。
**修正实施后**:提交 **`e652eb3`**(分支 `v0.11`)。
---
## 6. 迭代 2`ImageRuleRefServiceImpl#delete` 与 §3.1 方案走查(仅评估,未改代码)
**走查日期**2026-04-25
**目标**:在全工作区定位**图库(intelligent 图库人员服务)**与**通行规则(电梯应用本地)**相关代码,审核「合并 `updateGroupPersonRef`」方案是否需图库侧语义确认后方可实施。
### 6.1 图库 / 通行相关代码位置(工作区)
| 层级 | 路径/符号 | 职责 |
|------|-----------|------|
| **Feign 契约** | `maven-intelligent-cwoscomponent/intelligent-cwoscomponent-rest/.../feign/ImageStorePersonFeignClient.java` | `POST .../updateGroupPersonRef`,请求体 `UpdateGroupPersonRefParam` |
| **DTO** | `maven-intelligent-cwoscomponent/intelligent-cwoscomponent-interface/.../param/UpdateGroupPersonRefParam.java` | `businessId``imageStoreId``personIds``labelIds``organizationIds`(可同时只填部分字段) |
| **客户端封装** | `.../service/RestImageStorePersonServiceImpl.java` | 透传 Feign |
| **电梯侧 §3.1 锚点** | `maven-cw-elevator-application/.../passrule/impl/ImageRuleRefServiceImpl.java` 方法 **`delete`**(约 571605 行) | 按 `param.getIds()` 循环:`listByParentRule` → 收集子规则 `includeLabels` / `includeOrganizations`**`deleteById`** → **`imageStorePersonService.updateGroupPersonRef`**(每删一条父规则 1 次 RPC) |
| **同文件其它 `updateGroupPersonRef`** | `addOnlyRule`(约 434439)、`update` 内分支(约 556~562) | 新增/编辑规则后刷新;**同样未校验** `CloudwalkResult.isSuccess()` |
| **人员通行规则** | `maven-cw-elevator-application/.../person/impl/PersonRuleServiceImpl.java` | `add` / `addVisitor` / `delete` 末尾各 1 次 `updateGroupPersonRef`,入参以 **`personIds`** 为主(与 `delete`**label/org** 为主不同) |
| **HTTP 入口** | `cw-elevator-application-web/.../AcsPassRuleController.java` | `imageRuleRefService.delete` |
| **异步任务调用** | `AcsDeviceTaskServiceImpl#updateFloors` | 删楼层时多构造 `deleteParam.setIds(Collections.singletonList(ruleId))` **逐层**调 `imageRuleRefService.delete`;单次 `delete` 内仍可能 1 次或多次 `updateGroupPersonRef`(视该层 `ids` 数量) |
**说明**:本仓库**无** intelligent 图库服务端的 `updateGroupPersonRef` 业务实现源码,仅能依据 DTO 与调用方推断语义;**与图库/通行团队确认**仍属 §3.1 前置条件。
### 6.2 当前 `delete` 行为摘要(与合并相关)
- `imageStoreId` 来自 **`deviceImageStoreDao.getByBuildingId(param.getParentId())`**,整次批量删除共用同一图库。
- 对每个待删父规则 `id`:先读**该父规则下子规则**的 label/org(子行若带 label 则 `continue`,**同一子行不会同时写入 org**,与数据模型一致),再删父规则,再带着**本轮** `includeLabels` / `includeOrganizations` 调图库刷新。
- **`updateGroupPersonRef` 的返回值未做 `isSuccess` 校验**(违反约定 §2.2;与 `getImageStorePerson` 等分支不一致)。
### 6.3 约定中的「合并」方案审核
| 维度 | 结论 |
|------|------|
| **与现网 N 次调用的等价性** | 若图库侧语义为:在**给定 `imageStoreId`** 下,对传入的 **labelIds / organizationIds 集合**做**增量刷新或按维度重算引用**(各维度独立、与顺序无关),则「删库前汇总所有待删父规则的子维度 → **去重并集** → 删库完成后 **1 次** `updateGroupPersonRef`」与「每删一条父规则刷新其维度子集」在**最终一致**上通常等价。 |
| **必须向图库确认的风险** | 若远端实现为「以本次入参**覆盖/裁剪**图库可见范围」或依赖**调用顺序**产生副作用,则合并后的**单次并集**与 N 次**子集递进**可能不等价。约定文档 §3.1 所述「非破坏性刷新」即针对此。 |
| **`personIds``labelIds`/`organizationIds` 混用** | `PersonRuleServiceImpl``personIds` 路径;`ImageRuleRefServiceImpl#delete` 走 label/org。合并方案**不改变** `delete` 仅设 label/org 的现状;但若图库服务在**未传 `personIds`** 时对空列表有特殊含义,仍须一并确认。 |
| **空列表仍 RPC** | 当前循环在子规则为空时仍调用 `updateGroupPersonRef`(两列表皆空)。合并后是否**跳过空并集**可减少无效 RPC,但属于**行为微调**,若图库依赖「空刷」触发全量重算,须图库确认后再定。 |
| **不合并时的安全增量** | 在未获图库书面确认前,**仅**可为每次 `updateGroupPersonRef` 增加 **`isSuccess` 校验 + 失败抛 `ServiceException`**(及可选日志),**不改变** RPC 次数;与约定「退化为循环调用 + 返回值校验」一致。 |
### 6.4 评估结论(是否允许进入 §3.1「合并」类代码修正)
- **合并 N→1**:**不允许在图库/通行确认前实施** — 与 §1 表及约定 §3.1 前置条件一致。
- **仅返回值校验(及可选空并集跳过,若产品同意)**:**允许作为独立小步** — 不依赖远端语义新假设,符合 §2.2。
**图库确认建议提问(可复制)**:「对同一 `imageStoreId``updateGroupPersonRef` 在仅设置 `labelIds``organizationIds``personIds` 为空)时,是否为**按这些维度刷新人员引用**且**不会**将图库维度裁剪为仅等于本次入参?多次调用子集与单次调用**并集**是否在业务上等价?」
**实施后回填**:确认结论、对接人、日期写入 [约定文档 §3.1 文末](对外接口不变-远程调用与性能优化约定.md)(见约定 §5)。
**排期决策(2026-04-25**:在取得图库确认前,**冻结**约定 **§3.1** 相关全部代码变更;下一迭代转 **§3.5**(见上文 **「迭代 3」**)。
---
## 7. 迭代 3`AcsDeviceTaskServiceImpl#updateFloors` 走查结论(§3.5
**走查日期**2026-04-25
**代码位置**`cw-elevator-application-service/.../device/impl/AcsDeviceTaskServiceImpl.java` 方法 `updateFloors`;线程池 `.../common/UpdateFloorsTaskExecutor.java`;配置 `UpdateFloorsPoolProperties``ninca.update.floor.pool.*`,默认 core=3、max=5、queue=100、`AbortPolicy`)。
### 7.1 调用链与 RPC 上界(与 §1 表对齐)
| 分支 | 行为 | 上界 |
|------|------|------|
| 增楼层 | `personRuleService.add` **或** `imageRuleRefService.addOnlyRule`,成功后 `updateBingDevices` | `addFloors.size()` |
| 删楼层 | `personRuleService.delete` **或** `imageRuleRefService.delete`(单 id**或** 仅 DAO `deleteByOrgAndLabel`,成功后 `updateBingDevices` | `delFloorIds.size()` |
| 内层放大 | `imageRuleRefService.delete` 仍受约定 **§3.1** 冻结影响(`updateGroupPersonRef` 多次);本迭代**未**改该内层。 | 不变 |
**入口**`AcsElevatorDeviceServiceImpl#bindingFloors` / `#bindingPerson` 在插入任务行后**同步**调用 `updateFloors`;方法带 `@Async`,实际在 **`updateFloorsExecutor`** 线程执行;HTTP 已返回 `taskId` 后,**异步内失败不会回写该 HTTP 响应**(现网行为保持;运维依赖任务进度与日志)。
### 7.2 检查项与结论
| 检查项 | 现状(走查时) | 结论 |
|--------|----------------|------|
| `acsDeviceTaskDao.getById` | 未判空即 `task.getIsStop()`,存在 **NPE** 风险(数据异常或竞态) | **不通过** → 已修正:空则记录并 `ServiceException` |
| `personRuleService.add/delete``imageRuleRefService.addOnlyRule/delete` 返回值 | 未校验 `CloudwalkResult.isSuccess()`,失败时仍 **`updateBingDevices`**,进度与真实绑定不一致 | **不通过**(违反 §2.2)→ 已修正:统一 `requireTaskStepSuccess`,失败抛错且**不**递增 |
| `catch``ServiceException(e.getMessage())` | 丢失根因类型与栈信息到调用方;异步场景仅日志含 `{}` 与异常 | **记录**:是否改为 `ServiceException(code, msg)``initCause` 属产品/运维范围,**本轮不改** |
| 线程池 `keepAliveSeconds` | `UpdateFloorsPoolProperties` 有字段,**Bean 未 `setKeepAliveSeconds`**,配置项无效 | **缺陷** → 已在 `UpdateFloorsTaskExecutor` 绑定 |
| `RejectedExecutionHandler` | `AbortPolicy`,队列满时拒绝提交 | **记录**:与背压策略相关,**本轮不改**(须与运维对齐) |
| 删楼 `ruleMap.get(delFloorId)` | 若 `listZoneInfoByIds` 未覆盖某 `delFloorId` 可能 **null** 拼接 `ruleName` | **记录**:数据正常时风险低;**本轮未改**(可后续与 DAO 对齐) |
### 7.3 评估结论(是否允许进入代码修正)
- **允许并已实施(本轮)**`task` 空指针防护;对 **`personRuleService.add` / `delete`**、**`imageRuleRefService.addOnlyRule` / `delete`** 的 **`CloudwalkResult` 成功校验**(约定 §2.2);`updateFloorsExecutor` 绑定 **`keepAliveSeconds`**。
- **暂缓(须单独评审)**:拒绝策略、`catch` 异常语义增强;**楼层有界并行**与 `ruleMap` 缺键防护**已**见 **§9**。
**修正实施后**:提交 **`0ddeedc`**(分支 `v0.11`)。
---
## 8. 迭代 4P1 有界并行(§3.2 / §3.3 / §3.4
**实施日期**2026-04-25
### 8.1 行为与约定对齐说明
| 项 | 说明 |
|----|------|
| **并发度** | 代码常量与默认池 **`corePoolSize=maxPoolSize=6`**(约定 48 区间内),可通过 **`ninca.elevator.remote-io.pool.core-pool-size` / `max-pool-size`** 覆盖。 |
| **§3.2 `delete`** | 多 `personId` 时按批 `ThreadPoolExecutor.invokeAll`;单 ID 仍走主线程(无 Feign 子线程问题)。失败时返回 **`76260407`** 风格 `CloudwalkResult.fail`,与改造前**一致**;**同批内**若某 RPC 失败,`invokeAll` 仍会等本批其它任务结束后再统一 `get()` 抛出/返回,与**严格单线程「第一条失败即不再发起后续」**在「已发起请求数」上略有差异,属典型有界并行取舍。 |
| **§3.3 `listFloor`** | 设备数仍顺序 DAO`acsPersonService.page` 按批并行,结果写入 `personTotals[idx]` 后顺序 `setPersonNumber`**响应楼层顺序不变**。 |
| **§3.4 `addImageStore`** | `bindAppImageStoreDevice` 仍顺序执行;仅 **`bindDeviceAndImageStore`** 按批并行;任一批次中失败则 **`rollbackImageStoreAfterBindFailure`**(抽方法)后抛 `ServiceException`,与原先 try/catch 回滚路径一致。 |
| **Feign ThreadLocal** | 所有子线程 RPC 经 **`FeignThreadLocalUtil.callWithContext`**,避免池化线程串请求头。 |
**实施后提交****`fe571aa`**(分支 `v0.11`)。
---
## 9. 迭代 5`updateFloors` 楼层有界并行(§3.5
**实施日期**2026-04-24(工作区)
### 9.1 设计要点
| 项 | 说明 |
|----|------|
| **并发度** | 与 §8 一致使用 **`elevatorRemoteBoundedExecutor`** 的 **`ThreadPoolExecutor#invokeAll`**,批大小 **`UPDATE_FLOORS_FLOOR_PARALLEL = 6`**(与 `AcsPassRuleServiceImpl` 等处一致)。 |
| **bind 推进** | 同批内各楼层 RPC **可并发**`Future#get()` **按列表下标顺序** 等待;每遇返回值 **1** 则主线程**再** `getById`**`BIND_DEVICES`+1**,与旧实现「每层成功后立即 +1」的**终态**一致。 |
| **停任务** | 每层子步骤开头仍 `getById``isStop != 0` 时该层贡献 **0**、不推进 bind。 |
| **DAO 异常** | 删楼分支中 `getByRuleName` / `deleteByOrgAndLabel`**`DataAccessException`** 在子步内转 **`ServiceException`**,以配合 `Callable``invokeAll` 的异常链。 |
| **§3.1** | 未改 `imageRuleRefService.delete` 实现,内层 `updateGroupPersonRef` 行为与冻结前一致。 |
### 9.2 同批多楼层与「遇错即停」
**§8.1** 类似:同批中若一 Floor 的 RPC 失败,同批**其它在途** Floor 的 RPC 可能已执行完毕;`get()` 按顺序在**首败**时抛出。与严格串行「前一层失败则后层不再发起」在**已发出请求**上可存在差异,属有界并行常见取舍。
### 9.3 代码位置
- `AcsDeviceTaskServiceImpl#updateFloors``#runAddFloorsInBoundedParallel` / `#runDelFloorsInBoundedParallel``#addOneFloorStep` / `#delOneFloorStep``#advanceBindProgressOne`
@@ -1,96 +1,96 @@
# 对外接口不变前提下的远程调用与性能优化约定
**适用范围**:本仓库电梯应用(`maven-cw-elevator-application`)及与之集成的 intelligent / 图库等 Feign 调用路径。
**订立日期**2026-04-24
**状态**:团队约定(实施前对「远端语义」类条目需与图库/通行服务二次确认)。
**走查落地**[对外接口不变-走查任务与状态](对外接口不变-走查任务与状态.md)(子任务计数、可修正 / 不可修正与下一步)。
---
## 1. 「对外接口不变」的可操作定义
| 层级 | 是否允许变更 | 说明 |
|------|----------------|------|
| **对客户端 HTTP** | **否** | URL、Method、请求/响应 JSON 字段与语义、成功/失败码含义与现网一致。 |
| **本模块内部实现** | **是** | `ServiceImpl`、DAO、私有方法、远程调用次数、并行策略等,只要最终 HTTP 行为与业务语义一致。 |
| **intelligent / 图库等 Feign 契约** | **默认否** | 不新增 `batchDelete`、多 zone 一次统计等接口时,仅允许在**现有方法**上组合、批处理逻辑或**有界并行**。若单独立项扩展 Feign,再按新版本评审。 |
下文默认 **HTTP 与 Feign 契约均不扩展**
---
## 2. 总原则
1. **等价优先**:任何减少 RPC 或改并行的地方,须保证与改造前**同一业务语义**(尤其 `totalRows`、删除范围、刷新范围);不得用「近似」本地统计替代远端分页结果,除非完成**对账验收**并文档留痕。
2. **返回值必检**:对 `CloudwalkResult` / Feign 封装结果须校验 `isSuccess`(或项目统一规范);禁止依赖「失败静默」作为常态路径。
3. **合并优于 N 次**:若远端操作对同一维度**幂等重算**(如按 label/org 刷新引用),优先在本地删库结束后**去重并集单次调用**,而非循环内重复调用。
4. **无法合并时的默认手段**:在契约无批量 API 时,采用**有界并行**(固定线程池或 `Semaphore`,建议并发度 4~8,可按环境调参),并明确**失败聚合策略**与现网「遇错即停」等行为一致。
5. **事务边界**:涉及多 RPC + 本地 DB 时,在约定中明确是否 `@Transactional`、失败是否需补偿;禁止在未定义产品语义时擅自「部分成功」。
---
## 3. 按场景的具体约定
### 3.1 `ImageRuleRefServiceImpl.delete`(循环内 `updateGroupPersonRef`
- **目标**:降低重复刷新带来的 RPC 与负载。
- **约定**:在**所有** `deleteById`(或等价删库步骤)完成后,对本轮涉及的 `labelIds``organizationIds` **去重并集**,再调用**一次** `updateGroupPersonRef`
- **前置条件**:与图库/通行侧确认「仅传 label/org 列表」为**刷新引用**语义,而非会把图库裁剪为仅含这些维度的破坏性语义。若不能确认,**退化为**保持循环调用 + **仍须做返回值校验**
- **HTTP**:不变。
### 3.2 `AcsPersonServiceImpl.delete`(循环内 `imageStorePersonService.delete`
- **约束**`ImageStorePersonDelParam` 仅支持单 `personId`,无批量 delete 时不扩展契约。
- **约定**:采用**有界并行** `delete`;失败策略与现实现**一致**(例如任一失败则整体失败);若需与现网「严格顺序失败」完全一致,须在实现中定义完成顺序与异常聚合方式。
- **HTTP**:不变。
### 3.3 `AcsPassRuleServiceImpl.listFloor`(每层 `acsPersonService.page` 仅取 `totalRows`
- **约束**`AcsPersonQueryParam` 单 zone;图库侧无「多楼层一次返回人数」API 时,不能靠单次 RPC 消除 N。
- **禁止**:用本地 `countPersonIdByZoneId` 等 SQL 人数**直接替代** `imageStorePersonService.page``totalRows`(与 `PersonRuleServiceImpl.page` 路径下的 label/org/del 等过滤**不等价**),除非完成**逐层对账**并书面验收。
- **约定**:在契约不扩展时,**最优为按楼层有界并行** `page`(如 `rowsOfPage=1` 仅取总数),按树顺序**合并结果**,保证响应列表顺序与字段不变。
- **HTTP**:不变。
### 3.4 `AcsPassRuleServiceImpl.addImageStore`(循环 `bindDeviceAndImageStore`
- **约定**:无批量 bind 时,**有界并行 bind**;异常时的回滚(如删除已建图库)须与现逻辑一致,并注意并行下与顺序相关的竞态。
- **HTTP**:不变。
### 3.5 `AcsDeviceTaskServiceImpl` 等按楼层调用 `personRuleService.delete` / `imageRuleRefService.delete`
- **约定**:优先受益于 **3.1** 的合并刷新;若仍为每层 `delete`,可叠加**楼层级有界并行**,前提是错误语义与资源侧限流可接受。
---
## 4. 实施优先级(契约不扩展时的 ROI)
| 优先级 | 项 | 说明 |
|--------|-----|------|
| P0 | 3.1 合并 `updateGroupPersonRef` + 返回值校验 | 收益大、变更面相对集中;依赖远端语义确认。 |
| P1 | 3.3 / 3.2 / 3.4 / 3.5 有界并行 | 不改 DTO/URL,主要降低墙钟时间;注意线程池生命周期与超时。 |
| 远期 | Feign 批量/聚合接口 | 契约可扩展时,再评估批量 delete、多 zone 统计等结构性优化。 |
---
## 5. 变更与评审
- 任何偏离本约定(例如采用本地 count 替代 `totalRows`)须在 PR/变更说明中**单列风险**并附对账或测试证据。
- 与 intelligent 团队对齐「`updateGroupPersonRef` 语义」后,建议在本文 **3.1** 节追加**结论日期与对接人**一行,便于审计。
---
## 6. 相关代码锚点(便于检索)
| 场景 | 典型类/方法 |
|------|-------------|
| 人员分页与规则 | `AcsPersonServiceImpl#page``getRuleListByZoneId` |
| 楼层人数 | `AcsPassRuleServiceImpl#listFloor` |
| 规则与图库口径对照 | `PersonRuleServiceImpl#page` |
| 规则引用删除 | `ImageRuleRefServiceImpl#delete` |
| 人员删除与图库 | `AcsPersonServiceImpl#delete` |
---
## 7. 走查任务索引(子任务与状态)
**[对外接口不变-走查任务与状态.md](对外接口不变-走查任务与状态.md)**:按 §3.1~§3.5 与当前代码对齐,列出 **RPC 次数上界**、**下一可实施修正**、**在契约不扩展前提下不可做或须先确认** 的项。
# 对外接口不变前提下的远程调用与性能优化约定
**适用范围**:本仓库电梯应用(`maven-cw-elevator-application`)及与之集成的 intelligent / 图库等 Feign 调用路径。
**订立日期**2026-04-24
**状态**:团队约定(实施前对「远端语义」类条目需与图库/通行服务二次确认)。
**走查落地**[对外接口不变-走查任务与状态](对外接口不变-走查任务与状态.md)(子任务计数、可修正 / 不可修正与下一步)。
---
## 1. 「对外接口不变」的可操作定义
| 层级 | 是否允许变更 | 说明 |
|------|----------------|------|
| **对客户端 HTTP** | **否** | URL、Method、请求/响应 JSON 字段与语义、成功/失败码含义与现网一致。 |
| **本模块内部实现** | **是** | `ServiceImpl`、DAO、私有方法、远程调用次数、并行策略等,只要最终 HTTP 行为与业务语义一致。 |
| **intelligent / 图库等 Feign 契约** | **默认否** | 不新增 `batchDelete`、多 zone 一次统计等接口时,仅允许在**现有方法**上组合、批处理逻辑或**有界并行**。若单独立项扩展 Feign,再按新版本评审。 |
下文默认 **HTTP 与 Feign 契约均不扩展**
---
## 2. 总原则
1. **等价优先**:任何减少 RPC 或改并行的地方,须保证与改造前**同一业务语义**(尤其 `totalRows`、删除范围、刷新范围);不得用「近似」本地统计替代远端分页结果,除非完成**对账验收**并文档留痕。
2. **返回值必检**:对 `CloudwalkResult` / Feign 封装结果须校验 `isSuccess`(或项目统一规范);禁止依赖「失败静默」作为常态路径。
3. **合并优于 N 次**:若远端操作对同一维度**幂等重算**(如按 label/org 刷新引用),优先在本地删库结束后**去重并集单次调用**,而非循环内重复调用。
4. **无法合并时的默认手段**:在契约无批量 API 时,采用**有界并行**(固定线程池或 `Semaphore`,建议并发度 4~8,可按环境调参),并明确**失败聚合策略**与现网「遇错即停」等行为一致。
5. **事务边界**:涉及多 RPC + 本地 DB 时,在约定中明确是否 `@Transactional`、失败是否需补偿;禁止在未定义产品语义时擅自「部分成功」。
---
## 3. 按场景的具体约定
### 3.1 `ImageRuleRefServiceImpl.delete`(循环内 `updateGroupPersonRef`
- **目标**:降低重复刷新带来的 RPC 与负载。
- **约定**:在**所有** `deleteById`(或等价删库步骤)完成后,对本轮涉及的 `labelIds``organizationIds` **去重并集**,再调用**一次** `updateGroupPersonRef`
- **前置条件**:与图库/通行侧确认「仅传 label/org 列表」为**刷新引用**语义,而非会把图库裁剪为仅含这些维度的破坏性语义。若不能确认,**退化为**保持循环调用 + **仍须做返回值校验**
- **HTTP**:不变。
### 3.2 `AcsPersonServiceImpl.delete`(循环内 `imageStorePersonService.delete`
- **约束**`ImageStorePersonDelParam` 仅支持单 `personId`,无批量 delete 时不扩展契约。
- **约定**:采用**有界并行** `delete`;失败策略与现实现**一致**(例如任一失败则整体失败);若需与现网「严格顺序失败」完全一致,须在实现中定义完成顺序与异常聚合方式。
- **HTTP**:不变。
### 3.3 `AcsPassRuleServiceImpl.listFloor`(每层 `acsPersonService.page` 仅取 `totalRows`
- **约束**`AcsPersonQueryParam` 单 zone;图库侧无「多楼层一次返回人数」API 时,不能靠单次 RPC 消除 N。
- **禁止**:用本地 `countPersonIdByZoneId` 等 SQL 人数**直接替代** `imageStorePersonService.page``totalRows`(与 `PersonRuleServiceImpl.page` 路径下的 label/org/del 等过滤**不等价**),除非完成**逐层对账**并书面验收。
- **约定**:在契约不扩展时,**最优为按楼层有界并行** `page`(如 `rowsOfPage=1` 仅取总数),按树顺序**合并结果**,保证响应列表顺序与字段不变。
- **HTTP**:不变。
### 3.4 `AcsPassRuleServiceImpl.addImageStore`(循环 `bindDeviceAndImageStore`
- **约定**:无批量 bind 时,**有界并行 bind**;异常时的回滚(如删除已建图库)须与现逻辑一致,并注意并行下与顺序相关的竞态。
- **HTTP**:不变。
### 3.5 `AcsDeviceTaskServiceImpl` 等按楼层调用 `personRuleService.delete` / `imageRuleRefService.delete`
- **约定**:优先受益于 **3.1** 的合并刷新;若仍为每层 `delete`,可叠加**楼层级有界并行**,前提是错误语义与资源侧限流可接受。
---
## 4. 实施优先级(契约不扩展时的 ROI)
| 优先级 | 项 | 说明 |
|--------|-----|------|
| P0 | 3.1 合并 `updateGroupPersonRef` + 返回值校验 | 收益大、变更面相对集中;依赖远端语义确认。 |
| P1 | 3.3 / 3.2 / 3.4 / 3.5 有界并行 | 不改 DTO/URL,主要降低墙钟时间;注意线程池生命周期与超时。 |
| 远期 | Feign 批量/聚合接口 | 契约可扩展时,再评估批量 delete、多 zone 统计等结构性优化。 |
---
## 5. 变更与评审
- 任何偏离本约定(例如采用本地 count 替代 `totalRows`)须在 PR/变更说明中**单列风险**并附对账或测试证据。
- 与 intelligent 团队对齐「`updateGroupPersonRef` 语义」后,建议在本文 **3.1** 节追加**结论日期与对接人**一行,便于审计。
---
## 6. 相关代码锚点(便于检索)
| 场景 | 典型类/方法 |
|------|-------------|
| 人员分页与规则 | `AcsPersonServiceImpl#page``getRuleListByZoneId` |
| 楼层人数 | `AcsPassRuleServiceImpl#listFloor` |
| 规则与图库口径对照 | `PersonRuleServiceImpl#page` |
| 规则引用删除 | `ImageRuleRefServiceImpl#delete` |
| 人员删除与图库 | `AcsPersonServiceImpl#delete` |
---
## 7. 走查任务索引(子任务与状态)
**[对外接口不变-走查任务与状态.md](对外接口不变-走查任务与状态.md)**:按 §3.1~§3.5 与当前代码对齐,列出 **RPC 次数上界**、**下一可实施修正**、**在契约不扩展前提下不可做或须先确认** 的项。