- backend/: 13 Maven modules (cw-elevator-application, cloudwalk-cloud, intelligent-cwoscomponent, ninca-crk, etc.) - frontend/: 4 Vue projects (elevator-front, cwos-portal, alarm-front, front_acs) + decompiled + scripts - scripts/: build, test-env, tools (Docker Compose, service templates, API parity) - docs/: AGENTS.md, superpowers specs, architecture docs - .gitignore: standard Java/Maven exclusions Moved from legacy maven-*/ root layout to backend/ organized structure.
21 KiB
访客邀约与派梯楼层一致性梳理
文档用途:把「邀约页展示的楼层」与「电梯开通权限实际生效的楼层」如何在现有代码中对齐,用大白话 + 源码位置一并说明,便于产品、集成与排障。
关联文档:访客与电梯业务完整说明、租户访客默认楼层技术产品方案、租户访客策略迁入组织规格、租户访客楼层策略 — 代码重构实施指南(落地步骤与阶段划分)。
1. 一句话原则
所有环节只信同一串楼层 ID:能去哪几层,应在拉被访人详情时由组织侧算清(含租户默认楼层策略时写入 floorList);邀约登记把用户选定写进自己的业务单;派梯时把这份楼层列表放进请求里的 floorIds,电梯就按这一串执行。不要用「人员列表分页」等接口顶替详情里的楼层主数据。
2. 大白话说明
| 诉求 | 做法 |
|---|---|
| 邀约和派梯不要两套楼层 | 选人后楼层候选以 PersonService.detail(组织 /component/person/detail)→ floorList 为准;租户「默认只开放某几层」应在组织 **detail 链路内完成策略替代后再返回(规范见 §5,落地状态见 §6)。 |
| 邀约单已经定了访问楼层 | 后续 POST /elevator/person/add/visitor 必须把邀约单里保存的那串楼层放进 floorIds;电梯不会根据邀约单号去数据库查记录——只认本次 HTTP 参数。 |
| 派梯请求不传楼层 | 电梯会用 被访人详情里的 floorList 作为生效楼层,不会自动对齐你们库里存的邀约楼层——若要与邀约一致,集成侧应始终传 floorIds。 |
3. 时序图(实现归属:组织侧 / 电梯侧 / 集成侧)
图例
| 标注 | 含义 |
|---|---|
| 组织侧 | maven-ninca-common-component-organization:PersonController、ImgPersonServiceImpl#detail、组织库、(规范中的)租户访客策略服务 |
| 电梯侧 | maven-cw-elevator-application:AcsPersonController、PersonRuleServiceImpl#addVisitor、电梯库 image_rule_ref / zone / 楼栋图库映射等 |
| Intelligent | maven-intelligent-cwoscomponent:PersonService.detail → Feign 调组织 /component/person/detail |
| 集成侧 | 第三方 / BFF / 访客业务库:邀约单持久化、派梯前组装 floorIds(电梯代码中无邀约表) |
3.0 端到端:访客建档在前、派梯在后(推荐集成顺序)
典型闭环:先完成 访客人员建档 并得到 visitorId(平台人员主键),再调用 POST /elevator/person/add/visitor。邀约页拉 detail、保存 floorIds 可与建档分步编排;但 addVisitor 必须在建档成功之后调用(电梯侧不写访客主数据,见 §4.4)。
sequenceDiagram
autonumber
participant U as 用户 / 前台
participant BFF as BFF / 集成侧
participant PS as Intelligent<br/>PersonService.detail
participant OC as 组织侧<br/>ImgPersonServiceImpl#detail
participant ORG as 组织侧<br/>访客建档(示意接口)
participant AC as 电梯侧<br/>AcsPersonController addVisitor
rect rgb(245,248,250)
Note over U,OC: ① 邀约:选被访人 + 楼层候选(与 §3.1 同源)
U->>BFF: 选被访人 hostPersonId
BFF->>PS: detail(hostPersonId)
PS->>OC: POST /component/person/detail
OC-->>BFF: PersonResult.floorList 等
U->>BFF: 勾选楼层并提交邀约单
BFF->>BFF: 持久化邀约记录(hostPersonId、floorIds、访期等)
end
rect rgb(248,252,255)
Note over BFF,ORG: ② 访客建档(须先于派梯;具体路径以现场组织/访客模块为准)
BFF->>ORG: 创建访客人员 / 人像入库(示意)
Note over ORG: 组织侧或其它访客服务:人员落库,得到 visitorId
ORG-->>BFF: visitorId(平台 personId)
end
rect rgb(255,248,245)
Note over BFF,AC: ③ 派梯开通(visitorId 已存在)
BFF->>AC: POST /elevator/person/add/visitor<br/>personId=hostPersonId, visitorId, floorIds=邀约单楼层…
Note over AC: 电梯侧:§3.3 UC-02;不传 floorIds 则 §3.2 UC-01
AC-->>BFF: CloudwalkResult
end
| 区块 | 实现归属 | 说明 |
|---|---|---|
| ① 邀约与楼层候选 | 集成侧 + Intelligent → 组织 detail |
楼层权威路径与 §3.1 一致;邀约单仅存 集成侧 业务库 |
| ② 访客建档 | 组织侧为主(示意) | 本仓库 addVisitor 不包含建档;返回的 visitorId 即后续规则与图库绑定的主体 |
| ③ 派梯 | 电梯侧 addVisitor + 组织 Feign 绑图库 |
详见 §4.5;请求体无邀约单号,floorIds 须由集成侧按单据填入(对齐 §3.3) |
3.1 邀约页初始化 — 拉被访人「可访问楼层」主路径(与 UC-01 同源)
组织侧组装 floorList;其中 listByImageId 通过 Feign 回调电梯 HTTP 读取通行规则。
sequenceDiagram
autonumber
participant FE as 前端 / BFF<br/>集成侧
participant PS as Intelligent<br/>PersonService.detail
participant OC as 组织侧<br/>PersonController / ImgPersonServiceImpl#detail
participant EF as 电梯 HTTP<br/>passRule/image(listByImageId)
participant TP as 租户策略(规范)<br/>组织侧 DB / Service
FE->>PS: detail(personId, businessId)
Note over PS: Intelligent:路由实现
PS->>OC: POST /component/person/detail
Note over OC: 组织侧:查人员、组装机构标签等
OC->>EF: Feign listByImageId(通行楼层原始列表)
Note over EF: 电梯侧:凭规则返回 zoneId 列表
EF-->>OC: AcsPassRuleImageResultDto 列表
OC->>TP: (可选)命中租户访客策略则替代 floorList
Note over TP: 组织侧:策略语义见 §5<br/>未落地时仍为 listByImageId 结果(§6)
TP-->>OC: allow_zone_ids / 未启用
OC-->>PS: PersonResult(含 floorList)
PS-->>FE: 展示可选楼层 / 默认勾选依据
| 步骤 | 实现位置 |
|---|---|
| 对外 detail 聚合入口 | Intelligent → 组织 PersonController |
floorList 原始数据来源(通行规则) |
电梯侧 HTTP,由组织 ElevatorFeignClient.listByImageId 调用 |
租户默认楼层「替代」写入 floorList |
组织侧(规范:ImgPersonServiceImpl#detail 内;见 §5~§6) |
| 邀约单保存用户勾选 | 集成侧业务库,不在本仓库电梯模块 |
3.2 派梯 UC-01 — 请求 未带 floorIds(电梯用组织 detail 的 floorList)
电梯侧编排;被访人详情仍在 组织侧 计算;写规则、绑图库在 电梯侧 + 组织 Feign。
sequenceDiagram
autonumber
participant BFF as BFF / 调用方<br/>集成侧
participant AC as 电梯侧<br/>AcsPersonController
participant PR as 电梯侧<br/>PersonRuleServiceImpl#addVisitor
participant PS as Intelligent<br/>PersonService.detail
participant OC as 组织侧<br/>ImgPersonServiceImpl#detail
participant DB as 电梯侧 DB<br/>image_rule_ref 等
participant IS as 组织侧<br/>ImageStorePersonService.batchBind(Feign)
BFF->>AC: POST /elevator/person/add/visitor<br/>floorIds 为空
AC->>PR: addVisitor(param)
PR->>PS: detail(personId)
PS->>OC: /component/person/detail
Note over OC: 组织侧:返回 floorList(§3.1 同源)
OC-->>PS: PersonResult
PS-->>PR: floorList
PR->>PR: effective = personResult.floorList(UC-01)
PR->>DB: 按楼层写规则、取楼栋 imageStoreId
Note over DB: 电梯侧:PersonRuleServiceImpl 本地 Dao
PR->>IS: batchBind(visitorId, 访期…)
Note over IS: 组织侧:图库绑定
IS-->>PR: 成功 / 失败
PR-->>AC: CloudwalkResult
AC-->>BFF: 结果
| 步骤 | 实现位置 |
|---|---|
| HTTP 入口 | 电梯侧 AcsPersonController |
| UC-01 取楼层 | 电梯侧 PersonRuleServiceImpl 使用 组织 detail 返回的 floorList |
PersonService.detail 实现 |
组织侧 ImgPersonServiceImpl#detail |
写 image_rule_ref、选首层换楼栋 |
电梯侧 PersonRuleServiceImpl |
| 访客绑图库 | 组织侧 服务,电梯 Feign 调用 |
3.3 派梯 UC-02 — 请求 携带 floorIds(与邀约单保存的楼层一致)
与 UC-01 共用同一入口;生效楼层取请求体,不再用 detail.floorList 替换,但仍会调 detail 做被访人存在性与前置校验。
sequenceDiagram
autonumber
participant BFF as BFF / 调用方<br/>集成侧
participant AC as 电梯侧<br/>AcsPersonController
participant PR as 电梯侧<br/>PersonRuleServiceImpl#addVisitor
participant PS as Intelligent<br/>PersonService.detail
participant OC as 组织侧<br/>detail(仅校验被访人)
participant DB as 电梯侧 DB
participant IS as 组织侧<br/>batchBind(Feign)
BFF->>AC: POST /elevator/person/add/visitor<br/>floorIds = 邀约单持久化的列表
Note over BFF: 集成侧:从邀约记录读出楼层写入 body
AC->>PR: addVisitor(param)
PR->>PS: detail(personId)
PS->>OC: /component/person/detail
OC-->>PR: PersonResult(floorList 本轮可不用于生效集)
PR->>PR: effective = param.getFloorIds()(UC-02)
Note over PR: 电梯侧:不做 floorIds ⊆ detail.floorList 校验(信任调用方)
PR->>DB: 按 effective 写规则…
PR->>IS: batchBind…
IS-->>PR: ok
PR-->>BFF: 结果
| 步骤 | 实现位置 |
|---|---|
从邀约单带出 floorIds |
集成侧 |
UC-02 分支(effective = param.floorIds) |
电梯侧 PersonRuleServiceImpl |
| 子集校验(可选) | 集成侧 BFF 或后续扩展;当前电梯侧未实现 |
4. 派梯接口代码走查(电梯应用)
4.1 HTTP 入口
- 路径:
POST /elevator/person/add/visitor - 类:
maven-cw-elevator-application/cw-elevator-application-web/.../AcsPersonController.java - 请求体:
AcsPersonAddVisitorForm→ 拷贝为AcsPersonAddVisitorParam→PersonRuleService.addVisitor
4.2 请求体字段(与邀约记录的关系)
| 字段 | 含义 |
|---|---|
personId |
被访人在组织侧的人员 ID |
visitorId |
访客人员 ID(平台 personId) |
begVisitorTime / endVisitorTime |
访期(绑图库 et al.) |
floorIds |
本次开通涉及的楼层 zoneId 列表;由调用方填入。无邀约单号字段——电梯侧不读访客邀约业务表 |
定义见:cw-elevator-application-web/.../form/AcsPersonAddVisitorForm.java
4.3 生效楼层如何决定(PersonRuleServiceImpl#addVisitor)
实现类:cw-elevator-application-service/.../PersonRuleServiceImpl.java
- 始终先调
personService.detail(被访人存在性 / 组织侧详情)。失败则直接返回(如76260531)。 - 决定
effective(最终开通的楼层列表):floorIds非空(实现里称 UC-02):effective = 请求里的floorIds`,原样使用。floorIds为空(UC-01):effective = personResult.getFloorList()(来自组织detail)。
- 空列表:返回失败(
76260531)。 - 后续:按
effective每层写image_rule_ref、取楼栋图库imageStoreId、对visitorId调组织侧batchBind、更新组人员引用等。
重要:显式传入 floorIds 时,当前实现不会再与 personResult.getFloorList() 做「子集校验」——楼层是否合规依赖调用方 / BFF;电梯信任请求体。
4.4 业务目标与范围(访客派梯做什么、不做什么)
| 维度 | 说明 |
|---|---|
| 目标 | 在已有 访客人员 ID(visitorId)前提下,为本租户本次访问开通 电梯通行规则(电梯库 image_rule_ref),并把访客绑定到 对应楼栋的人脸图库(组织侧 batchBind),使闸机/电梯侧能按楼层放行。 |
| 不做 | 不在此接口创建访客档案;访客姓名、证件、人像建档应在组织/访客业务前置完成并得到 visitorId。 |
| 租户上下文 | businessId 来自调用上下文(与 CloudwalkCallContext.company 一致),与被访人 personId、访客 visitorId 同属该租户数据范围。 |
4.5 分阶段业务逻辑(与 PersonRuleServiceImpl#addVisitor 对齐)
flowchart LR
S1[1 被访人 detail] --> S2[2 确定 effective]
S2 --> S3[3 首层 zone → 楼栋 → imageStoreId]
S3 --> S4[4 每层写 image_rule_ref<br/>personId=visitorId]
S4 --> S5[5 batchBind 访期 + updateGroupPersonRef]
下列阶段均在 maven-cw-elevator-application · PersonRuleServiceImpl.addVisitor 中顺序执行。
阶段 1 — 校验被访人可查(组织侧)
- 构造
PersonDetailParam(id = personId,businessId = context 租户),调用personService.detail(经 Intelligent → 组织ImgPersonServiceImpl#detail)。 - 成功:得到
PersonResult,后续 UC-01 需要其中的floorList。 - 失败:透传组织返回的 code / message;若结果为 null 或不成功且无语义,使用
76260531(无可用被访人信息或详情不可用)。 - 数据为空:
personResult == null→76260531。
阶段 2 — 确定本次生效楼层列表 effective
- 见 §4.3:UC-02 用请求
floorIds;UC-01 用personResult.getFloorList()。 effective为空(含 UC-01 时floorList为空、或 UC-02 传空列表):76260531。
阶段 3 — 解析「楼栋」并确定组织侧图库 imageStoreId(电梯侧 + 空间服务)
- 取
effective的第一个元素作为zoneQueryParam.id,调用zoneService.page(空间服务)解析 楼层节点。 - 用返回的
ZoneResult.get(0).getParentId()作为 楼栋 ID,再deviceImageStoreDao.getByBuildingId(parentId)得到imageStoreId。 - 业务含义:多楼层同一次开通时,以列表首层所在楼栋作为本次绑定的图库归属;各 **
floorId仍分别落在对应分区规则上,但 人脸图库绑定指向同一imageStoreId(由首层楼栋决定)。
阶段 4 — 按楼层写入访客通行规则(电梯侧库)
- 对
effective中每个floorId:
imageRuleRefDao.getDefaultByZoneId(floorId)取该分区默认规则模板 → 组装ImageRuleRefAddDto,personId填的是visitorId(访客),写入image_rule_ref(批量insertList)。 - 语义:访客在每个选定楼层上各有一条「挂默认父规则」的通行引用,与被访人自有规则分离。
阶段 5 — 访期内人脸图库绑定与组同步(组织侧 Feign)
ImageStorePersonBindParam:imageStoreId(阶段 3)、personIds = [visitorId]、nullDateIsLongTerm = true、expiryBeginDate/expiryEndDate取自请求的begVisitorTime/endVisitorTime。- 调用
imageStorePersonService.batchBind(失败则返回 组织侧错误码,电梯透传)。 - 成功后
updateGroupPersonRef:同一visitorId+imageStoreId,用于组人员引用同步(具体语义见组织组件实现)。
阶段 6 — 异常兜底
- 未捕获的
ServiceException:向上抛出。 - 其他
Exception:包装为76260530(通用失败)。
4.6 访期参数(begVisitorTime / endVisitorTime)
| 项 | 说明 |
|---|---|
| 用途 | 传入组织 batchBind,作为访客在该图库上的 有效期起止(与通行规则配合使用,以现场组织/图库策略为准)。 |
| 与楼层关系 | 访期 不改变 effective 楼层集合;仅影响绑定是否在时间上有效。 |
nullDateIsLongTerm=true |
代码固定传入;具体与空起止如何组合以组织 batchBind 实现为准,集成时建议在测试环境验证边界。 |
4.7 错误码与典型原因(电梯侧可见)
| 错误码 | 典型场景 |
|---|---|
76260531 |
detail 失败;被访人 PersonResult 为空;UC-01 时 floorList 为空;或 effective 最终仍为空。 |
76260530 |
addVisitor 内部未预期的 Exception(如 DB、空指针、空间分页无数据等未单独映射时)。 |
| 组织 / Feign 返回码 | batchBind 失败时 透传对方 code / message,成功后再执行 updateGroupPersonRef;若 bind 失败则 不会继续组引用更新。 |
76260521 |
Controller 层捕获 ServiceException 时映射(多见于其它接口;addVisitor 内多为 fail 码直接返回)。 |
排障时建议按日志顺序核对:detail → effective → zone 首层 → imageStoreId → 每层 getDefaultByZoneId → batchBind。
4.8 业务边界与集成注意
- 首层决定楼栋图库:若
effective中楼层分属不同楼栋,当前实现仅以第一项定imageStoreId;产品若要求「多楼栋多图库」,需拆分多次调用或扩展实现(现状未支持单次多楼栋)。 - 每层必须有默认规则:某
floorId下getDefaultByZoneId若为空,可能在后续组装/插入时异常并落入76260530,需在数据配置层保证各访客可达层已配置默认规则。 - 幂等与重复开通:同一访客重复调用可能产生多条规则引用或组织侧重复绑定行为,以组织与电梯 DAO 约束为准;重要场景建议业务层幂等(例如按邀约单号去重)。
- 部分失败:规则已
insertList后若batchBind失败,当前流程 不会自动回滚已插入的规则行(需运维或补偿策略关注)。
5. 与「租户访客默认楼层」策略的关系
- 规范方向(详见
docs/superpowers/specs/2026-05-06-tenant-visitor-policy-organization-implementation.md):租户允许楼层在组织ImgPersonServiceImpl#detail内以allow_zone_ids替代写入返回的floorList;电梯 UC-01 仅透传该floorList,不在电梯库再与策略表求交。 - 邀约初始化:应通过
detail→floorList展示可选楼层,与 UC-01 同源。 - 邀约单已保存楼层后的派梯:把单据里的楼层写入
floorIds(UC-02),与单据一致;若需防止超范围,应在 BFF 侧校验floorIds ⊆ detail.floorList(策略替代后)。
6. 实现状态提示(避免误判)
组织侧已实现:TenantVisitorFloorPolicyService 读取组织库 tenant_visitor_floor_policy,在 ImgPersonServiceImpl#detail(及 page(isVisitor))对 floorList / 楼层展示 做 allow_zone_ids 替代。部署前须在组织库执行 docs/sql/organization_tenant_visitor_floor_policy.sql;未建表时策略查询失败会自动回退为 listByImageId 原始结果。详见 租户访客楼层策略-代码重构实施指南。
7. 集成 checklist
- 邀约页初始化楼层:走
PersonService.detail,不要用人员分页/导出接口当作floorList主来源。 - 邀约保存:持久化用户选定或允许的 楼层 zoneId 列表(与展示同源)。
- 派梯:从邀约单读出楼层 → 填入
floorIds调用/elevator/person/add/visitor;若业务允许「不传楼层」,需知悉此时等价 UC-01,与邀约单无自动对齐。 - 安全(可选):BFF 校验
floorIds为detail.floorList的子集(或业务规则允许的超集策略)。 - 日志:
AcsPersonController已打requestFloorSize,便于核对是否传了楼层。
8. 源码索引(电梯)
| 环节 | 路径 |
|---|---|
| Controller | maven-cw-elevator-application/cw-elevator-application-web/.../AcsPersonController.java → addVisitor |
| 表单 | .../form/AcsPersonAddVisitorForm.java |
| 参数 | cw-elevator-application-service/.../param/AcsPersonAddVisitorParam.java |
| 核心逻辑(§4.4~§4.8) | cw-elevator-application-service/.../PersonRuleServiceImpl.java → addVisitor |
| 空间分页(首层 → 楼栋) | 同模块内 ZoneService(PersonRuleServiceImpl 注入调用) |
| 图库绑定 / 组引用 | Feign → 组织 ImageStorePersonService:batchBind、updateGroupPersonRef |
文档版本:与仓库梳理同步;若 addVisitor 行为变更,请同步更新 §4(含 §4.4~§4.8 业务逻辑与错误码)。