# 访客与电梯业务:注册/登记与派梯授权完整说明 > 本文档针对 **本仓库 `cw-elevator-application`(以 service 层为逻辑落点)** 中与「访客」相关的 **完整可追踪路径** 做说明,并区分行业语义下常被混用的 **「访客登记/注册」** 与 **「在电梯域为访客授派梯权」**。 ## 1. 概念与边界 | 概念 | 通常含义 | 在本项目中的落点 | |------|----------|------------------| | **访客主数据登记/注册** | 在**标准访客/一卡通**中录入:姓名、证号、访期、人脸、被访人、访客单等 | **不在**电梯应用内实现完整登记 UI;主数据以 **人员 ID(`personId`)** 或 **标准访客服务** 的档案形式存在。电梯服务通过 `PersonService`、图库、`ninca-crk-std` 的 HTTP/ Feign 与**既有档案**对接。 | | **电梯侧「访客派梯授权」** | 在已存在**访客人员 ID** 的前提下,为指定**楼层**写入通行规则、绑定图库、设访期,使闸机/电梯能识别其通行 | **本应用显式能力**:`PersonRuleService#addVisitor` → HTTP `POST /elevator/person/add/visitor`(见下文)。 | | **通行中「是否访客」打标** | 识别到人脸后,写通行记录时判断是否访客、回填被访人 | `AcsElevatorRecordServiceImpl#add` 中调 **`intelligent/three/visitor/record/query`**,不创建新访客档案。 | **结论**:若将「访客注册」理解为**在平台完整新建访客档案**,其主流程在 **外部标准服务**;本仓文档的「电梯侧部分」是 **(1) 授权** 与 **(2) 业务记录打标** 的完整、可追溯描述。 --- ## 2. 总览图:访客相关双主线 ```mermaid flowchart TB subgraph 标准域["标准访客/人员/图库(多在外部或 CWOS 组件)"] VReg[访客档案登记/维护] P[平台人员 personId] G[图库 imageStoreId] end subgraph 电梯应用 A["POST /elevator/person/add/visitor\naddVisitor 派梯授权"] B["AcsElevatorRecordService#add\nthree 线反查访客+被访人"] end VReg -.->|产生 visitor 对应人员ID| P P --> A A --> G B -->|只读反查| VReg ``` --- ## 3. 主线 A:访客派梯授权(核心业务逻辑) ### 3.1 对外入口 | 项 | 值 | |----|-----| | HTTP 方法/路径 | `POST` **`/elevator/person/add/visitor`** | | Web | `AcsPersonController#addVisitor`(`person/controller/AcsPersonController.java`) | | 入参体 | `AcsPersonAddVisitorForm` → 复制为 `AcsPersonAddVisitorParam` | | 服务 | `PersonRuleService#addVisitor` | | 实现 | `PersonRuleServiceImpl#addVisitor`(`person/impl/PersonRuleServiceImpl.java`) | **入参字段(`AcsPersonAddVisitorParam`)**: | 字段 | 含义 | |------|------| | `visitorId` | 访客在**平台人员体系**中的人员主键(非电梯单独造号) | | `personId` | **被访人**人员主键,用于在**未传 floorIds 时**拉取被访人可通行楼层 | | `begVisitorTime` / `endVisitorTime` | 图库绑定**有效期**(见 `ImageStorePersonBindParam`) | | `floorIds` | 可选。若**非空**,则跳过被访人楼层与租户策略的自动推算,**直接使用**该列表作为要授权的楼层 id 集合 | ### 3.2 处理步骤(与源码顺序一致) 1. **若调用方未传 `floorIds`** - 用 `PersonService#detail` 取 **被访人** `PersonResult`。 - 失败或无人 → 返回 `CloudwalkResult.fail`(`76260531` 等,以返回码为准)。 - 取被访人 **`floorList`** 作为候选楼层;**为空** → 失败 `76260531`。 2. **租户访客楼层策略(可选收窄)** - 从 `TenantVisitorFloorPolicyDao#selectEnabledTenantDefault(businessId)` 读**启用**的默认策略行(`TenantVisitorFloorPolicyDto`)。 - 若 `enabled==1` 且 `allowZoneIds`(JSON 字符串数组)解析出非空列表: 将 **被访人 `floorList`** 与 **策略允许 zone id 集合** 做**有序交集**(`intersectPreserveHostOrder`:保留被访人原顺序、只留落在 allow 中的楼层)。 - 交完若**为空** → 失败 **`76260532`**(可配置文案,表示与租户策略无交集)。 3. **若调用方已传 `floorIds`** - 以上「被访人楼层 + 策略求交」**整段不执行**;**直接使用**传入的 `floorIds` 作为 `effectiveFloors` 后续逻辑已合并进 `param.setFloorIds` 分支,最终以 `param.getFloorIds()` 非空为继续条件;若仍为空则 `76260531`。 4. **解析首楼层所属楼栋 → 取图库 ID** - 用 `zoneService.page` 以 **第一个 `floorId`** 查 `ZoneResult`,再 `deviceImageStoreDao.getByBuildingId(首楼层 parentId)` 得 **`imageStoreId`**(与楼栋绑定的图库)。 5. **每层写入「人员挂默认规则」的规则引用行** - 对每个 `floorId`:查 `imageRuleRefDao.getDefaultByZoneId(floorId)` 得 **该楼层默认规则**。 - 拼 `ImageRuleRefAddDto` 列表:新建 UUID、`businessId`、`personId=visitorId`、挂 `parentRule`、`name`/`zoneId`/`zoneName` 自默认规则、`personDelete=0` 等。 - `imageRuleRefDao.insertList(insertList)` 批量落库(电梯侧**规则与人员**关系,供通行策略使用)。 6. **图库人员绑定与分组同步** - `ImageStorePersonBindParam`:`imageStoreId`、`personIds=[visitorId]`、长期 null 作长期、起止时间用 `begVisitorTime`/`endVisitorTime`。 - `imageStorePersonService.batchBind` —— 将访客**绑定**到本楼栋图库并带有效期。 - 再 `updateGroupPersonRef`:对同一 `visitorId` + `imageStoreId` 做**组人员引用**更新(与通行业务的图库组一致)。 7. 任一步远程失败:返回 Feign/平台返回的 code/message 或 `76260530` 等包装异常(见 `catch`)。 ### 3.3 活动图(无 floorIds,有租户策略时) ```mermaid flowchart TD Start([收到 add/visitor]) --> F{floorIds 已传?} F -- 是 --> G[直接使用 floorIds] F -- 否 --> D[被访人 detail] D --> H{有 floorList?} H -- 否 --> E1[失败 76260531] H -- 是 --> T{租户策略 enabled=1 且 allow 非空?} T -- 否 --> G T -- 是 --> I[与 allowZoneIds 求交] I --> J{交后非空?} J -- 否 --> E2[失败 76260532] J -- 是 --> G G --> K[首 floor → 楼栋 → imageStoreId] K --> L[每楼层: 写 ImageRuleRef + 默认父规则] L --> M[图库 batchBind 访客+访期] M --> N[updateGroupPersonRef] N --> Ok([成功]) ``` ### 3.4 时序图 ```mermaid sequenceDiagram autonumber participant C as 前端/调用方 participant API as AcsPersonController participant PR as PersonRuleServiceImpl participant PS as PersonService(CWOS) participant DB as ImageRuleRefDao/租户策略表 participant Z as ZoneService participant D as DeviceImageStoreDao participant IS as ImageStorePersonService C->>API: POST /elevator/person/add/visitor API->>PR: addVisitor(param, context) opt 未带 floorIds PR->>PS: detail(被访人) PS-->>PR: floorList PR->>DB: 读租户策略 + 求交 end PR->>Z: page(查首层 zone) Z-->>PR: 楼栋信息 PR->>D: getByBuildingId(楼栋) D-->>PR: imageStoreId loop 每层 PR->>DB: getDefaultByZoneId 规则 PR->>DB: insertList 人员规则引用 end PR->>IS: batchBind(访期+访客+图库) IS-->>PR: 成功 PR->>IS: updateGroupPersonRef PR-->>API: CloudwalkResult API-->>C: Boolean ``` ### 3.5 策略与数据表(只读理解) - **`tenant_visitor_floor_policy`**:租户级默认策略;`allow_zone_ids` 为 **JSON 数组字符串**,表示允许作为访客派梯的 **区域/楼层 id**;`enabled=1` 时参与**交集收紧**(见 DTO 注释与 DAO)。 --- ## 4. 主线 B:通行记录落库时「访客身份」的认定(非注册) **场景**:设备侧上报识别结果 **`add` 一条电梯通行/开门记录**。 **实现**:`AcsElevatorRecordServiceImpl#add` 中,在**写本库**之前: 1. 组装 `VisitorRecordQueryParam`:`visitorId = addDTO.getRecognitionFaceId()`(**识别脸/访客侧 id**,与产品约定有关)、`businessId` 为租户;请求头 `businessId` 同。 2. `RestTemplateUtil.post` → **`http://{ninca-crk-std}/intelligent/three/visitor/record/query`** 3. 若返回列表**非空**:`isVisitor=1`,并从首条 `VisitorResult` 取 `personId` 写入 **被访人** `interviewee`。 4. 此接口**不**在电梯服务内**新建**访客主档,仅**查询**与打标;与 `VisitorFeignClient` 的 ` /intelligent/visitor/record/query` **路径不同**(见 [04-mqtt-visitor-event.md](04-mqtt-visitor-event.md))。 ```mermaid sequenceDiagram participant R as AcsElevatorRecordServiceImpl participant HTTP as ninca-crk-std three/query participant DAO as 电梯记录 DAO R->>HTTP: visitorId+tenant HTTP-->>R: List 访客档案或空 R->>DAO: add(含 isVisitor, interviewee) ``` --- ## 5. 与图库/通行「访客标签」的其它关系 - **MQTT 侧**:`MqttServiceImpl` 在识别流水中若 `personLabelIds` **含 `"1"`**,在 MQTT JSON 中置 `isVisitor=true`(**标签**语义,与上一节**档案访客**是不同判断维度)。见 `MqttServiceImpl` 中 `VISITOR_LABEL_CODE`。 - **域事件**:`VisitorRecordPushEvent` 在记录 `add` 成功后发布,**主题**为 `VISITOR_RECORD_TOPIC`;不替代访客主数据登记。 --- ## 6. 错误与日志索引(addVisitor 相关,便于排障) | 场景 | 码/信息方向 | |------|-------------| | 被访人查不到/无 floorList/最终 floor 为空 | `76260531`(及 message 中 `getMessage` 文案) | | 与租户 `allowZoneIds` 交后无楼层 | `76260532` | | 其它未预期异常 | `76260530` 包装为 `ServiceException` | | 图库 `batchBind` 失败 | 透传 `bindResult` 的 code/message | **日志关键词**:`根据被访人添加访客派梯权限`;`租户访客楼层策略求交`;`访客添加派梯权限`;`远程调用绑定人员图库`。 --- ## 7. 本文档在仓库中的位置 - 与 [INDEX.md](INDEX.md) 其它分册互补:本文件专门 **深挖访客在电梯服务中的业务闭环**;部署与接口清单仍以运行环境及标准访客系统文档为准。 --- ## 8. 关键源码索引 | 说明 | 路径(相对于 `maven-cw-elevator-application/…` 下模块) | |------|--------------------------------------------------------| | 入口 Controller | `cw-elevator-application-web/.../person/controller/AcsPersonController.java` | | 派梯实现 | `cw-elevator-application-service/.../person/impl/PersonRuleServiceImpl.java` `addVisitor` | | 入参 | `.../person/param/AcsPersonAddVisitorParam.java` | | 策略 DTO/DAO | `cw-elevator-application-data/.../person/dto/TenantVisitorFloorPolicyDto.java`、`TenantVisitorFloorPolicyDao` | | 记录打标 | `.../record/impl/AcsElevatorRecordServiceImpl.java` `add` | 最后更新以当前工作区代码为准;若你方将「访客注册」**唯一定义**为 **HTTP `/elevator/person/add/visitor`** 这一条,可忽略第 4 节,仅将第 3 节作为上线评审主材料。