- 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.
18 KiB
访客与电梯业务:注册/登记与派梯授权完整说明
本文档覆盖 组织组件(
maven-ninca-common-component-organization)、智能组件 Feign 路由(maven-intelligent-cwoscomponent)与 电梯应用(maven-cw-elevator-application)中与「访客派梯」相关的可追踪路径;区分 访客主数据登记 与POST /elevator/person/add/visitor派梯授权。文中源码路径均相对于各 Maven 模块仓库根目录。
1. 概念与边界
| 概念 | 通常含义 | 在本项目中的落点 |
|---|---|---|
| 访客主数据登记/注册 | 在标准访客/一卡通中录入档案 | 不在电梯应用内完成完整登记 UI;人员以 visitorId(平台 personId) 等形式存在于外部服务。 |
| 电梯侧「访客派梯授权」 | 在已有访客人员 ID 前提下写入通行规则、绑定图库、访期 | PersonRuleServiceImpl#addVisitor → POST /elevator/person/add/visitor(AcsPersonController)。 |
| 通行记录「是否访客」打标 | 识别流水写库时标记访客/被访人 | AcsElevatorRecordServiceImpl#add → CRK /intelligent/three/visitor/record/query(见第 8 节)。 |
2. 组件关系总览
访客邀约页、电梯 addVisitor 在 floorList 语义上应对齐:二者都应依赖 PersonService.detail → PersonResult.floorList(经 Intelligent 转发到组织 POST /component/person/detail)。组织组装 floorList 时会 Feign 回调电梯(见 §3.3):电梯 image_rule_ref 里已有的人员—楼层规则,经 /elevator/passRule/image 转成 zoneId 列表,再写回组织的 floorList。
flowchart LR
subgraph fe["前端 / BFF / 第三方"]
UI[访客邀约 / 派梯调用方]
end
subgraph intelligent["maven-intelligent-cwoscomponent"]
PS[PersonService / RestPersonServiceImpl]
PFC[PersonFeignClient → /component/person]
end
subgraph org["maven-ninca-common-component-organization"]
PC[PersonController /component/person]
IMG[ImgPersonServiceImpl]
EF[ElevatorFeignClient → /elevator/passRule/image]
end
subgraph elevator["maven-cw-elevator-application"]
AC[AcsPersonController /elevator/person/add/visitor]
PR[PersonRuleServiceImpl addVisitor]
IRR[ImageRuleRefDao / ZoneService / DeviceImageStoreDao]
IS[ImageStorePersonService Feign 绑定图库]
end
UI --> PS
PS --> PFC --> PC --> IMG
IMG --> EF
UI --> AC --> PR
PR --> PS
PR --> IRR
PR --> IS
3. 组织侧:被访人 detail 与 floorList(邀约页 / UC-01 的数据源)
3.1 HTTP 入口与实现类
| 项 | 说明 |
|---|---|
| 路径 | POST /component/person/detail |
| Controller | cwos-component-organization-web · PersonController#detail |
| 服务 | ImgStorePersonService#detail → ImgPersonServiceImpl#detail |
对外契约返回 CloudwalkResult<ImgStorePersonGetResult>;经 Intelligent PersonFeignClient 解码为 PersonResult(字段名一致部分落入 floorList,电梯 addVisitor 仅消费 PersonResult.getFloorList())。
3.2 ImgPersonServiceImpl#detail 处理顺序(与源码一致)
| 步骤 | 做什么 | 与 floorList 的关系 |
|---|---|---|
| 1 | selectByPrimaryKey 查组织库人员 |
无此人则 data 可为 null(见下) |
| 2 | 可选 defaultFloor → zoneFeignClient.findZonelist |
仅 floorName 展示,不等于下面得到的 floorList |
| 3 | 可选 vehicleFeignClient.getVehicleIds |
与楼层列表无关 |
| 4 | getImgStorePersonResults |
得到 organizationIds、labelIds,供 §3.3 调用电梯 |
| 5 | elevatorFeignClient.listByImageId(...)(Feign,语义见 §3.3) |
唯一写入 floorList / floorNames(在返回码成功且进入分支时) |
| 6 | (规范)租户 allow_zone_ids 替代 |
当前未实现,运行态 floorList = 步骤 5 结果 |
| 7 | portalUserService.query 填创建/更新人姓名 |
与楼层无关 |
- 查无此人:
result仍为 null,接口CloudwalkResult.success(null);电梯addVisitor取personResult == null→76260531。 - 步骤 5 失败或未进入分支:
floorList不会被赋值(可能为 null / 未覆盖);UC-01addVisitor→76260531。
3.3 组织 Feign listByImageId ↔ 电梯 HTTP ↔ 真实落库(避免混淆)
这一块名字容易混:组织侧 Java 方法叫 listByImageId,并不等于电梯里另一个 DAO 方法 listByImageId。
3.3.1 组织侧调用(入口)
| 项 | 说明 |
|---|---|
| 组织代码 | ElevatorFeignClient#listByImageId(cwos-component-organization-service/.../feign/ElevatorFeignClient.java) |
| HTTP | POST {elevator-base}/elevator/passRule/image(Feign name 一般为 feign.elevator.name,如 elevator-app) |
| 请求体 | AcsPassRuleImageForm:personId、businessId、includeOrganizations、includeLabels(对应组织 detail 里 getImgStorePersonResults 填好的档案字段) |
3.3.2 电梯侧实际执行(与「另一个 listByImageId」区分)
| 易混点 | 说明 |
|---|---|
| 本链路 | AcsPassRuleController(类上 @RequestMapping("/elevator/passRule"))方法 @RequestMapping("/image") → ImageRuleRefServiceImpl#listByPersonInfo → ImageRuleRefDao#listByPersonInfo → ImageRuleRefMapper.xml#listByPersonInfo |
| 勿混 | AcsPassRuleServiceImpl#listByImageId → acsPassRuleDao.listByImageId 查的是 it_acs_pass_rule,按 imageStoreIds 过滤——不是组织 Feign 这条 HTTP 路径当前走的实现。 |
3.3.3 listByPersonInfo 在查什么(业务语义)
- 表:电梯库
image_rule_ref(人员与楼层通行规则引用:访客addVisitor写入的也是这张表的语义同类数据)。 - 返回字段:
DISTINCT zone_id, zone_name(映射AcsPassRuleImageResultDto)。 - 查询逻辑(摘要):
- 至少包含:
person_id = 被访人且person_delete = 0的规则所对应的楼层; - 若
includeOrganizations/includeLabels非空,SQL 中另有OR分支,按机构/标签关联规则补充楼层,并排除person_delete = 1等条件下已标记删除的zone_id(详见ImageRuleRefMapper.xml全文)。 - 排序:
order by CAST(zone_name as signed)(按楼层名称可解析的数字排序)。
- 至少包含:
因此:组织 detail 里的 floorList,本质上是「电梯侧已为该被访人(及档案上的机构/标签)开通过的楼层 zoneId 列表」的只读投影,不是组织库自己存的楼层字段。
3.3.4 组织如何把返回值写进 detail
仅当 images.getCode() 等于 00000000(成功)时,ImgPersonServiceImpl#detail 才遍历 images.getData(),把每条 zoneId 依次 floorList.add,并把 zoneName 拼成逗号分隔的 floorNames。
若 Feign 失败、超时、或业务码非 00000000:不会进入该分支,floorList 保持未赋值 → 下游 UC-01 常 76260531。
3.3.5 与「访客派梯 addVisitor」的数据关系(闭环)
| 环节 | 数据 |
|---|---|
| 被访人已在电梯侧挂规则 | image_rule_ref 中有 person_id=被访人 等记录 |
组织 detail |
listByImageId(Feign) → /passRule/image → listByPersonInfo 读出 zone 列表 → PersonResult.floorList |
电梯 addVisitor UC-01 |
再次 PersonService.detail,用同一 floorList 作为 effective,再 写入访客的 image_rule_ref 等 |
所以:floorList 不是「组织凭空算的」,而是「电梯规则表里已有被访人楼层」经 /passRule/image 汇总后的结果;租户策略若要做「替代」,应在步骤 §3.2 第 6 步改 floorList,而不是再发明一套与 image_rule_ref 无关的算法(除非产品另行约定)。
3.4 时序图 — 组织侧「单人 detail」(含回调电梯)
sequenceDiagram
autonumber
participant Caller as 调用方 / Intelligent
participant PC as PersonController
participant IMG as ImgPersonServiceImpl
participant Zone as ZoneFeignClient
participant Veh as VehicleFeignClient
participant Elev as ElevatorFeignClient<br/>方法名 listByImageId
participant EA as 电梯 AcsPassRuleController<br/>/elevator/passRule/image<br/>→ ImageRuleRefService listByPersonInfo
Caller->>PC: POST /component/person/detail
PC->>IMG: detail(param, context)
IMG->>IMG: selectByPrimaryKey(personId)
opt defaultFloor 非空
IMG->>Zone: findZonelist
Zone-->>IMG: ZoneResult
end
IMG->>Veh: getVehicleIds
Veh-->>IMG: vehicle ids
IMG->>IMG: getImgStorePersonResults → organizationIds, labelIds
IMG->>Elev: listByImageId(AcsPassRuleImageForm)
Elev->>EA: POST /elevator/passRule/image
EA->>EA: listByPersonInfo → SQL image_rule_ref
EA-->>Elev: List zoneId/zoneName
Elev-->>IMG: CloudwalkResult code=00000000 + list
IMG->>IMG: setFloorList / setFloorNames(策略替代:待实现)
IMG-->>PC: ImgStorePersonGetResult
PC-->>Caller: CloudwalkResult
4. 电梯侧:addVisitor 业务步骤
4.1 对外入口
| 项 | 值 |
|---|---|
| HTTP | POST /elevator/person/add/visitor |
| Controller | cw-elevator-application-web · AcsPersonController#addVisitor |
| 实现 | PersonRuleServiceImpl#addVisitor |
4.2 与源码一致的执行顺序
阶段 1 — 被访人详情(必经)
- 组装
PersonDetailParam:id = param.getPersonId()(被访人),businessId = context.getCompany().getCompanyId()。 personService.detail(detailParam, context)→ IntelligentPersonFeignClient→ 组织POST /component/person/detail。- 失败或
personResult == null→ 返回失败码(常见76260531)。
阶段 2 — 生效楼层 effective(UC 分流)
callerProvidedFloors = !CollectionUtils.isEmpty(param.getFloorIds())true(UC-02):effective = param.getFloorIds()(不使用personResult.getFloorList()作为列表来源;仍已执行阶段 1,用于保证被访人可查)。false(UC-01):effective = personResult.getFloorList();空 →76260531。
阶段 3 — 再次空集校验 — 防御 effective 仍为空。
阶段 4 — 楼栋与图库
zoneService.page:查询effective.get(0)对应ZoneResult,取parentId作为楼栋。deviceImageStoreDao.getByBuildingId(parentId)→imageStoreId(后续batchBind/updateGroupPersonRef均绑定该图库)。
阶段 5 — 每层通行规则引用
- 对每个
floorId:imageRuleRefDao.getDefaultByZoneId(floorId)取默认父规则,拼装ImageRuleRefAddDto(personId = visitorId),imageRuleRefDao.insertList。
阶段 6 — 图库与分组
ImageStorePersonBindParam:imageStoreId、personIds=[visitorId]、访期begVisitorTime/endVisitorTime。imageStorePersonService.batchBind(Feign 至 Intelligent/组织图库能力)。updateGroupPersonRef同步组人员引用。
异常 — 未捕获的运行异常包装 76260530;batchBind 失败透传下游码。
4.3 租户策略语义(与仓库规范一致)
电梯 不注入 TenantVisitorFloorPolicyDao,不在 addVisitor 内做 floorList ∩ allow。租户 allow_zone_ids 替代须落在 组织 detail(见第 3.2 节「待实现」说明)。
5. 时序图 — 电梯 addVisitor(UC-01:不传 floorIds)
sequenceDiagram
autonumber
participant C as 调用方
participant API as AcsPersonController
participant PR as PersonRuleServiceImpl
participant PS as PersonService Intelligent
participant Org as 组织 /component/person/detail
participant Z as ZoneService
participant DIS as DeviceImageStoreDao
participant IRR as ImageRuleRefDao
participant IS as ImageStorePersonService
C->>API: POST /elevator/person/add/visitor(floorIds 空)
API->>PR: addVisitor(param, context)
PR->>PS: detail(personId, businessId)
PS->>Org: Feign POST detail
Org-->>PS: PersonResult.floorList
PS-->>PR: PersonResult
Note over PR: effective = floorList;空则 76260531
PR->>Z: page(首 floorId)
Z-->>PR: ZoneResult.parentId
PR->>DIS: getByBuildingId(parentId)
DIS-->>PR: imageStoreId
loop 每个 floorId
PR->>IRR: getDefaultByZoneId + insertList
end
PR->>IS: batchBind(visitorId, 访期, imageStoreId)
IS-->>PR: 成功
PR->>IS: updateGroupPersonRef
PR-->>API: CloudwalkResult Boolean
API-->>C: 结果
6. 时序图 — 电梯 addVisitor(UC-02:显式 floorIds)
sequenceDiagram
autonumber
participant C as 调用方
participant API as AcsPersonController
participant PR as PersonRuleServiceImpl
participant PS as PersonService Intelligent
participant Z as ZoneService
participant DIS as DeviceImageStoreDao
participant IRR as ImageRuleRefDao
participant IS as ImageStorePersonService
C->>API: POST /elevator/person/add/visitor(floorIds 非空)
API->>PR: addVisitor
PR->>PS: detail(被访人)(校验被访人存在)
PS-->>PR: PersonResult
Note over PR: effective = param.floorIds(不用 detail.floorList)
PR->>Z: page(首 floorId)
Z-->>PR: parentId
PR->>DIS: getByBuildingId
DIS-->>PR: imageStoreId
loop 每层
PR->>IRR: 默认规则 + insertList visitorId
end
PR->>IS: batchBind + updateGroupPersonRef
PR-->>API: success
7. 活动图(addVisitor 分支汇总)
flowchart TD
Start([POST /elevator/person/add/visitor]) --> D[PersonService.detail 被访人]
D --> E{success 且 PersonResult 非空?}
E -- 否 --> E1[76260531 等]
E -- 是 --> F{param.floorIds 非空?}
F -- 是 UC-02 --> G[effective = floorIds]
F -- 否 UC-01 --> H{personResult.floorList 非空?}
H -- 否 --> E1
H -- 是 --> G2[effective = floorList]
G --> K[zone.page 首层 → imageStoreId]
G2 --> K
K --> L[逐层 ImageRuleRef 挂 visitorId]
L --> M[batchBind + updateGroupPersonRef]
M --> Ok([true])
8. 主线 B:通行记录落库时「访客身份」认定(非派梯)
场景:设备上报识别结果写入电梯通行记录。
实现:AcsElevatorRecordServiceImpl#add 在写库前 RestTemplateUtil.post → http://{ninca-crk-std}/intelligent/three/visitor/record/query;返回非空则 isVisitor=1 并回填被访人。该路径 不创建 访客主档,与第 3~7 节派梯链路独立。
sequenceDiagram
participant R as AcsElevatorRecordServiceImpl
participant HTTP as CRK three/query
participant DAO as 电梯记录 DAO
R->>HTTP: visitorId + tenant
HTTP-->>R: 访客档案或空
R->>DAO: add(含 isVisitor, interviewee)
9. 其它:MQTT 访客标签
MqttServiceImpl 若识别流水 personLabelIds 含 "1",MQTT JSON 置 isVisitor=true(标签维度,与档案访客不同)。
10. 错误与日志索引(addVisitor)
| 场景 | 码 |
|---|---|
detail 失败 / 被访人无数据 / UC-01 floorList 为空 / effective 仍为空 |
76260531 |
| 其它未预期异常 | 76260530 |
batchBind 失败 |
透传下游 code/message |
日志关键字:根据被访人添加访客派梯权限、UC-01 / UC-02、最终生效楼层、访客添加派梯权限、远程调用绑定人员图库。
11. 关键源码索引
| 层级 | 路径 |
|---|---|
| 电梯 Controller | cw-elevator-application-web/.../person/controller/AcsPersonController.java |
| 电梯派梯 | cw-elevator-application-service/.../person/impl/PersonRuleServiceImpl.java addVisitor |
| Intelligent Feign | intelligent-cwoscomponent-rest/.../person/feign/PersonFeignClient.java(/component/person/detail) |
| 组织 Controller | cwos-component-organization-web/.../controller/PersonController.java /detail |
| 组织 detail 实现 | cwos-component-organization-service/.../ImgPersonServiceImpl.java detail |
| 组织→电梯 Feign | cwos-component-organization-service/.../feign/ElevatorFeignClient.java(方法 listByImageId → POST /elevator/passRule/image) |
| 电梯「按人员信息列楼层」 | cw-elevator-application-web/.../passrule/controller/AcsPassRuleController.java /image |
| 电梯实现 | cw-elevator-application-service/.../passrule/impl/ImageRuleRefServiceImpl.java listByPersonInfo |
| 电梯 SQL | cw-elevator-application-data/.../mapper/ImageRuleRefMapper.xml listByPersonInfo(表 image_rule_ref) |
| (勿与本链路混淆) | AcsPassRuleServiceImpl#listByImageId / it_acs_pass_rule — 不同入口 |
| 通行访客打标 | cw-elevator-application-service/.../record/impl/AcsElevatorRecordServiceImpl.java add |
12. 规范交叉引用
- 租户楼层策略 替代 语义与迁移边界:
docs/superpowers/specs/2026-05-06-tenant-visitor-policy-organization-implementation.md
说明:组织 detail 内租户策略替代若未落地,UC-01 的 floorList 完全等于 /elevator/passRule/image → listByPersonInfo 返回的 zone 列表(经 Feign listByImageId 写入 ImgStorePersonGetResult)。与产品「仅开放接待层」不一致时,应排查 image_rule_ref 数据、该接口 成功与否,以及 组织侧策略替代是否已实现。