Files
starRiverProperty/docs/superpowers/walkthroughs/2026-05-09-guangfa-28f-hardcoded-design.md
T
hpd840321 42c4a9fd6b docs: mark elevator-side tenant policy SQL as deprecated, add guangfa visitor floor design
- Deprecate elevator-side tenant_visitor_floor_policy SQL files
  (V2 queries only component-organization library)
- Add guangfa 28F visitor floor design spec (table-driven approach A)
- Add complete database ER diagram (14 DBs, 537 tables)
- Add implementation plan for guangfa visitor floor policy
- Code walkthrough docs for visitor floor policy analysis
2026-05-09 23:56:12 +08:00

297 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 广发基金 28F 硬编码方案 — 组件/模块/位置细化设计
**日期**: 2026-05-09
**分支**: `feature/guangfa-28f-hardcoded`
**状态**: 方案细化完成,待实施
---
## 1. 涉及组件与模块总览
```
source/backend/
├── ninca-common-component-organization/ ★ 唯一变更组件
│ ├── cwos-component-organization-service/ ★ 代码变更
│ │ └── src/main/java/cn/cloudwalk/service/organization/service/
│ │ └── ImgPersonServiceImpl.java ★ 唯一变更 Java 文件
│ │
│ ├── cwos-component-organization-starter/ ★ 配置变更
│ │ └── deploy/run-verify/
│ │ └── application.properties ★ 唯一变更配置文件
│ │
│ └── (其余模块不变: data, interface, web)
├── cw-elevator-application/ ☆ 仅增强日志
│ └── cw-elevator-application-service/
│ └── .../impl/PersonRuleServiceImpl.java ☆ +2 行日志
└── intelligent-cwoscomponent/ 不变 (仅 Feign 接口定义)
```
---
## 2. 变更点详细定位
### 2.1 模块: `cwos-component-organization-service`
**文件**: `backend/ninca-common-component-organization/cwos-component-organization-service/src/main/java/cn/cloudwalk/service/organization/service/ImgPersonServiceImpl.java`
**包**: `cn.cloudwalk.service.organization.service`
**类**: `ImgPersonServiceImpl` (1414 行)
#### 位置 A — 依赖注入区 (第 157-158 行后,新增 2 行)
**原代码**:
```java
157: @Value("${xhwSixFloorId}")
158: private String xhwSixFloorId;
159:
160: private static final String imageBase64 =
```
**改为**:
```java
157: @Value("${xhwSixFloorId}")
158: private String xhwSixFloorId;
159:
160: @Value("${gfOrgId}")
161: private String gfOrgId;
162:
163: @Value("${gfDefaultFloorId}")
164: private String gfDefaultFloorId;
165:
166: private static final String imageBase64 =
```
#### 位置 B — `listByPage()` 访客列表分支 (第 354-370 行,插入 else-if)
**当前代码** (精确):
```java
354: if (imgStorePersonResult.getOrganizationIds().contains(this.xhwId)) {
355: imgStorePersonResult.setDefaultChooseFloor(this.xhwDefaultFloorId);
356: List<AcsPassRuleImageResultDto> floorInfoList = new ArrayList<>();
357: AcsPassRuleImageResultDto resultDto = new AcsPassRuleImageResultDto();
358: resultDto.setZoneId(this.xhwDefaultFloorId);
359: resultDto.setZoneName("40F");
360: floorInfoList.add(resultDto);
361: imgStorePersonResult.setFloorInfoList(floorInfoList);
362: } else {
363: imgStorePersonResult.setDefaultChooseFloor(this.xhwSixFloorId);
364: List<AcsPassRuleImageResultDto> floorInfoList = new ArrayList<>();
365: AcsPassRuleImageResultDto resultDto = new AcsPassRuleImageResultDto();
366: resultDto.setZoneId(this.xhwSixFloorId);
367: resultDto.setZoneName("6F");
368: floorInfoList.add(resultDto);
369: imgStorePersonResult.setFloorInfoList(floorInfoList);
370: }
```
**改为**:
```java
354: if (imgStorePersonResult.getOrganizationIds().contains(this.xhwId)) {
355: imgStorePersonResult.setDefaultChooseFloor(this.xhwDefaultFloorId);
356: List<AcsPassRuleImageResultDto> floorInfoList = new ArrayList<>();
357: AcsPassRuleImageResultDto resultDto = new AcsPassRuleImageResultDto();
358: resultDto.setZoneId(this.xhwDefaultFloorId);
359: resultDto.setZoneName("40F");
360: floorInfoList.add(resultDto);
361: imgStorePersonResult.setFloorInfoList(floorInfoList);
362: } else if (imgStorePersonResult.getOrganizationIds().contains(this.gfOrgId)) {
363: imgStorePersonResult.setDefaultChooseFloor(this.gfDefaultFloorId);
364: List<AcsPassRuleImageResultDto> floorInfoList = new ArrayList<>();
365: AcsPassRuleImageResultDto resultDto = new AcsPassRuleImageResultDto();
366: resultDto.setZoneId(this.gfDefaultFloorId);
367: resultDto.setZoneName("28F");
368: floorInfoList.add(resultDto);
369: imgStorePersonResult.setFloorInfoList(floorInfoList);
370: imgStorePersonResult.setIsAcrossDay(Integer.valueOf(0));
371: this.logger.info("[GF-28F] listByPage MATCH orgId={} in orgIds={} → default 28F",
372: this.gfOrgId, imgStorePersonResult.getOrganizationIds());
373: } else {
374: imgStorePersonResult.setDefaultChooseFloor(this.xhwSixFloorId);
375: List<AcsPassRuleImageResultDto> floorInfoList = new ArrayList<>();
376: AcsPassRuleImageResultDto resultDto = new AcsPassRuleImageResultDto();
377: resultDto.setZoneId(this.xhwSixFloorId);
378: resultDto.setZoneName("6F");
379: floorInfoList.add(resultDto);
380: imgStorePersonResult.setFloorInfoList(floorInfoList);
381: }
```
**注意**: 行号偏移 (+5 行,因上方注入区新增)。实际编辑以内容匹配为准。
#### 位置 C — `detail()` 邀约+派梯核心路径 (第 643-651 行,在策略替代块后插入)
**当前代码** (精确):
```java
643: Optional<List<String>> replacementFloors =
644: this.tenantVisitorFloorPolicyService.replacementZoneIdsIfPolicyActive(
645: result.getOrganizationIds());
646: if (replacementFloors.isPresent()) {
647: floorList = new ArrayList<>(replacementFloors.get());
648: zoneNames = buildCommaSeparatedFloorNames(businessId, floorList);
649: }
650: result.setFloorNames(zoneNames);
651: result.setFloorList(floorList);
```
**改为**:
```java
643: Optional<List<String>> replacementFloors =
644: this.tenantVisitorFloorPolicyService.replacementZoneIdsIfPolicyActive(
645: result.getOrganizationIds());
646: if (replacementFloors.isPresent()) {
647: floorList = new ArrayList<>(replacementFloors.get());
648: zoneNames = buildCommaSeparatedFloorNames(businessId, floorList);
649: }
650: // 广发基金: 邀约 + UC-01 派梯 floorList 限制为 28F
651: if (!CollectionUtils.isEmpty(result.getOrganizationIds())
652: && result.getOrganizationIds().contains(this.gfOrgId)) {
653: List<String> originalFloors = new ArrayList<>(floorList);
654: floorList = Collections.singletonList(this.gfDefaultFloorId);
655: zoneNames = "28F";
656: this.logger.info("[GF-28F] detail MATCH orgId={} in orgIds={} → floor restricted: {}→[28F]",
657: this.gfOrgId, result.getOrganizationIds(), originalFloors);
658: } else {
659: this.logger.debug("[GF-28F] detail NO-MATCH orgId={} not in orgIds={}",
660: this.gfOrgId, result.getOrganizationIds());
661: }
662: result.setFloorNames(zoneNames);
663: result.setFloorList(floorList);
```
**注意**: `Collections.singletonList` 需要 `import java.util.Collections;` — 检查是否已存在(当前代码 `listByPage` 中使用了 `Collections.singletonList`import 应已存在)。
#### 位置 D — `detail()` 入口日志 (在第 600 行附近,`detail()` 方法体内)
`detail()` 方法体的早期(如第 570-600 行区间,根据实际方法体定位),新增:
```java
this.logger.info("[GF-DETAIL] entry personId={} businessId={} orgIds={}",
param.getId(), businessId, result.getOrganizationIds());
```
#### 位置 E — `detail()` listByImageId 返回日志 (在第 630 行附近)
`listByImageId` 调用成功后、循环组装 `floorList` 前:
```java
this.logger.info("[GF-DETAIL] listByImageId returned {} zones: {}",
acsPassRuleImageResultDtoList.size(),
acsPassRuleImageResultDtoList.stream()
.map(AcsPassRuleImageResultDto::getZoneId).collect(Collectors.toList()));
```
---
### 2.2 模块: `cw-elevator-application-service`
**文件**: `backend/cw-elevator-application/cw-elevator-application-service/src/main/java/cn/cloudwalk/elevator/person/impl/PersonRuleServiceImpl.java`
**包**: `cn.cloudwalk.elevator.person.impl`
**类**: `PersonRuleServiceImpl`
#### 位置 F — addVisitor() 日志增强 (第 187-194 行附近)
**当前代码** (精确):
```java
187: boolean callerProvidedFloors = !CollectionUtils.isEmpty(param.getFloorIds());
188: if (callerProvidedFloors) {
189: effective = param.getFloorIds();
190: this.logger.info("UC-02:调用方显式楼层 effective={}", effective);
191: } else {
192: effective = personResult.getFloorList();
193: if (CollectionUtils.isEmpty(effective)) {
194: this.logger.warn("UC-01:被访人 detail.floorList 为空 personId={}", param.getPersonId());
```
**改为**:
```java
187: boolean callerProvidedFloors = !CollectionUtils.isEmpty(param.getFloorIds());
188: if (callerProvidedFloors) {
189: effective = param.getFloorIds();
190: this.logger.info("[GF-ADDV] UC-02 effective={}", effective);
191: } else {
192: effective = personResult.getFloorList();
193: if (CollectionUtils.isEmpty(effective)) {
194: this.logger.warn("[GF-ADDV] UC-01 floorList empty personId={}", param.getPersonId());
```
---
### 2.3 模块: `cwos-component-organization-starter` (配置)
**文件**: `backend/ninca-common-component-organization/cwos-component-organization-starter/deploy/run-verify/application.properties`
**位置**: 第 172 行后(`xhwSixFloorId` 之后),新增 2 行:
```properties
# 第 170-172 行 (现有):
xhwId=21474e012cd14e26bc62771873b22562
xhwDefaultFloorId=605560547135455232
xhwSixFloorId=605560541473144832
# 第 173-174 行 (新增):
gfOrgId=488b8ad049bb43408a6fbcc50bcb89ac
gfDefaultFloorId=605560545117995008
```
---
### 2.4 数据层: 禁用原表驱动策略
**数据库**: `component-organization` (组织库)
**表**: `tenant_visitor_floor_policy`
```sql
UPDATE tenant_visitor_floor_policy
SET enabled = 0,
remark = CONCAT(remark, ' [DISABLED 2026-05-09: migrated to hardcoded 28F in ImgPersonServiceImpl]'),
updated_at = UNIX_TIMESTAMP(NOW()) * 1000
WHERE id = 'gf_vstr_policy_guangfa_fund_001x'
AND enabled = 1;
```
**数据库**: `cw-elevator-application` (电梯库,如果存在同步表)
```sql
-- 仅当电梯库也有 tenant_visitor_floor_policy 表时执行
UPDATE tenant_visitor_floor_policy
SET enabled = 0
WHERE id = 'gf_vstr_policy_guangfa_fund_001x'
AND enabled = 1;
```
---
## 3. 不变更清单
| 组件 | 模块 | 文件 | 原因 |
|------|------|------|------|
| intelligent-cwoscomponent | interface | PersonService.java | Feign 接口定义,不变 |
| intelligent-cwoscomponent | rest | PersonFeignClient.java | HTTP 路径映射,不变 |
| ninca-common-component-organization | data | TenantVisitorFloorPolicyMapper.java | 仍服务物业策略,不删 |
| ninca-common-component-organization | service | TenantVisitorFloorPolicyService.java | 仍服务物业策略,不删 |
| ninca-common-component-organization | web | PersonController.java | REST 入口,不变 |
| ninca-common-component-organization | starter | bootstrap.properties | 启动配置,不变 |
| cw-elevator-application | web | AcsPersonController.java | REST 入口,不变 |
| cw-elevator-application | data | ImageRuleRefDao.java | DAO 层,不变 |
| scripts/test-env | config | component-org.properties | 测试模板(最小配置),不变 |
| ninca-common-component-organization | releases/ | 所有 `releases/` 下文件 | 历史发布快照,不变 |
---
## 4. 汇总
| # | 组件 | Maven 模块 | 文件 | 变更类型 | 行数 |
|---|------|-----------|------|---------|------|
| A | component-org | cwos-component-organization-**service** | `ImgPersonServiceImpl.java` | 注入 `@Value` ×2 | +4 |
| B | component-org | cwos-component-organization-**service** | `ImgPersonServiceImpl.java` | `listByPage` else-if 分支 | +11 |
| C | component-org | cwos-component-organization-**service** | `ImgPersonServiceImpl.java` | `detail()` floorList 截断 | +13 |
| D | component-org | cwos-component-organization-**service** | `ImgPersonServiceImpl.java` | `detail()` 入口日志 | +2 |
| E | component-org | cwos-component-organization-**service** | `ImgPersonServiceImpl.java` | listByImageId 返回日志 | +3 |
| F | elevator-app | cw-elevator-application-**service** | `PersonRuleServiceImpl.java` | addVisitor 日志前缀 | ~0 (替换) |
| — | component-org | cwos-component-organization-**starter** | `application.properties` | 配置 ×2 | +2 |
| — | (数据库) | — | `tenant_visitor_floor_policy` | UPDATE enabled=0 | 1 行 |
**总计: 2 个组件, 3 个模块, 3 个文件, ~35 行新增**