Made-with: Cursor
18 KiB
对外接口不变:走查任务与状态
依据:对外接口不变-远程调用与性能优化约定(§2 总原则、§3 场景、§4 优先级)。
走查代码根:maven-cw-elevator-application/cw-elevator-application-service(2026-04-24 静态走查)。
说明:下表「子任务数」指与约定相关的 RPC/可优化循环次数上界(随运行时数据规模变化);状态表示在不扩展 Feign/HTTP 契约前提下是否建议动代码。
1. 总览表(子任务量 + 可修正 / 不可修正)
| 约定 § | 代码锚点 | 子任务数(上界) | 下一可修正动作(建议顺序) | 不可修正或须前置确认 |
|---|---|---|---|---|
| §3.1 | ImageRuleRefServiceImpl#delete(约 575~598 行) |
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(约 46~119 行) |
增:A = addFloors.size() 次 personRuleService.add 或 imageRuleRefService.addOnlyRule;删:D = delFloorIds.size() 次 personRuleService.delete / imageRuleRefService.delete / DAO |
P1:删层在 §3.1 落地后可减少「内层 refresh」放大;可对楼层维度做有界并行(与限流/异步线程池策略一致) | @Async("updateFloorsExecutor") 下线程池与背压须单独评估;错误现为 throw new ServiceException(e.getMessage()) 信息较粗,是否属「接口不变」范畴由产品/运维定义 |
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 文末回填对接人、日期;再按 §1 表拆分 PR(合并与返回值校验可分步)。 |
迭代 3(§3.5 updateFloors)
| 字段 | 内容 |
|---|---|
| 状态 | 走查与首轮修正已完成(见 §7):getById 空防护、步骤级 CloudwalkResult 校验、keepAliveSeconds 绑定线程池。 |
| 约定锚点 | §3.5 AcsDeviceTaskServiceImpl#updateFloors |
| 暂缓项 | 楼层有界并行、AbortPolicy 与 catch 语义、删楼 ruleMap 缺键等见 §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内楼层循环有界并行(仍受 §3.1 内层放大约束)。 - 调参 / 观测:
ninca.elevator.remote-io.pool按环境压测调整;必要时为并行批增加指标日志。
已完成回顾:迭代 1 — §5;迭代 2 — §6(§3.1 冻结);迭代 3 — §7;迭代 4 — §8。
4. 文档维护
| 项目 | 内容 |
|---|---|
| 更新触发 | ImageRuleRefServiceImpl#delete、AcsPersonServiceImpl#delete、AcsPassRuleServiceImpl#listFloor / #addImageStore、AcsDeviceTaskServiceImpl#updateFloors 任一处重构或签契约变更 |
| 结论回填 | 图库对 §3.1 的确认结论请写回 约定文档 §3.1 文末建议行(对接人 + 日期) |
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(约 571~605 行) |
按 param.getIds() 循环:listByParentRule → 收集子规则 includeLabels / includeOrganizations → deleteById → imageStorePersonService.updateGroupPersonRef(每删一条父规则 1 次 RPC) |
同文件其它 updateGroupPersonRef |
addOnlyRule(约 434~439)、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 文末(见约定 §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缺键防护。
修正实施后:提交 0ddeedc(分支 v0.11)。
8. 迭代 4:P1 有界并行(§3.2 / §3.3 / §3.4)
实施日期:2026-04-25
8.1 行为与约定对齐说明
| 项 | 说明 |
|---|---|
| 并发度 | 代码常量与默认池 corePoolSize=maxPoolSize=6(约定 4~8 区间内),可通过 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,避免池化线程串请求头。 |
实施后提交:7eb3785(分支 v0.11)。