diff --git a/docs/plans/backend/2026-04-26-enterprise-auto-fill-backend-implementation.md b/docs/plans/backend/2026-04-26-enterprise-auto-fill-backend-implementation.md new file mode 100644 index 00000000..a80bdd48 --- /dev/null +++ b/docs/plans/backend/2026-04-26-enterprise-auto-fill-backend-implementation.md @@ -0,0 +1,493 @@ +# 关联业务自动补入实体库 Backend Implementation Plan + +> **执行约束:** 按当前项目 `AGENTS.md` 执行;未获得用户明确要求时不启用 subagent。Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 新建和导入员工亲属实体关联、中介实体关联、信贷客户实体关联、招投标供应商时,实体库缺失的企业自动写入 `ccdi_enterprise_base_info`。 + +**Architecture:** 新增一个后端内部实体库自动补全服务,统一处理“已存在不覆盖、缺失则最小插入、同批去重、来源和风险等级映射”。各业务 Service 在业务校验通过、业务数据落库前调用该能力;`EnterpriseSource` 枚举新增 `SUPPLIER` 并继续由现有 `/ccdi/enum/enterpriseSource` 接口驱动前端。 + +**Tech Stack:** Java 21, Spring Boot 3, MyBatis Plus, JUnit 5, Mockito, Maven. + +--- + +## File Structure + +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/enums/EnterpriseSource.java` + - 新增 `SUPPLIER("SUPPLIER", "供应商")`。 +- Create: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/support/EnterpriseAutoFillService.java` + - 内部补全服务,封装单条和批量实体补入。 +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffEnterpriseRelationServiceImpl.java` + - 新建员工亲属实体关联前补实体库。 +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffEnterpriseRelationImportServiceImpl.java` + - 导入成功行批量补实体库。 +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiCustEnterpriseRelationServiceImpl.java` + - 新建信贷客户实体关联前补实体库。 +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiCustEnterpriseRelationImportServiceImpl.java` + - 导入成功行批量补实体库。 +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiIntermediaryServiceImpl.java` + - 中介实体关联新建时取消实体库必须已存在校验,改为补实体库。 + - 中介库管理新增实体时风险等级默认高风险。 +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiIntermediaryEnterpriseRelationImportServiceImpl.java` + - 取消“机构表不存在”失败条件,改为成功行批量补实体库。 +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiEnterpriseBaseInfoImportServiceImpl.java` + - 中介库管理导入实体风险等级默认高风险。 +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiPurchaseTransactionServiceImpl.java` + - 招投标新建时供应商补实体库。 +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiPurchaseTransactionImportServiceImpl.java` + - 招投标导入成功采购事项的供应商批量补实体库。 +- Test: existing unit tests under `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/` + - 扩展或新增对应 Service/Import/Controller 测试。 +- Create: `docs/reports/implementation/2026-04-26-enterprise-auto-fill-implementation.md` + - 记录修改内容、影响范围、验证情况。 + +## Task 1: EnterpriseSource 枚举与接口契约 + +**Files:** +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/enums/EnterpriseSource.java` +- Modify: `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/controller/CcdiEnumControllerTest.java` + +- [ ] **Step 1: 写失败测试** + +在 `CcdiEnumControllerTest#getEnterpriseSourceOptions_shouldReturnConfiguredOptions` 中断言返回值包含 `SUPPLIER/供应商`。 + +```java +assertTrue(data.stream() + .map(EnumOptionVO.class::cast) + .anyMatch(option -> + "SUPPLIER".equals(option.getValue()) && "供应商".equals(option.getLabel()))); +``` + +- [ ] **Step 2: 运行测试确认失败** + +Run: + +```bash +mvn -pl ccdi-info-collection -Dtest=CcdiEnumControllerTest test +``` + +Expected: FAIL,提示未找到 `SUPPLIER`。 + +- [ ] **Step 3: 实现枚举** + +在 `EnterpriseSource` 中新增: + +```java +SUPPLIER("SUPPLIER", "供应商"), +``` + +保持 `contains`、`resolveCode`、`getDescByCode` 通过 `values()` 自动生效。 + +- [ ] **Step 4: 运行测试确认通过** + +Run: + +```bash +mvn -pl ccdi-info-collection -Dtest=CcdiEnumControllerTest test +``` + +Expected: PASS。 + +## Task 2: 实体库自动补全服务 + +**Files:** +- Create: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/support/EnterpriseAutoFillService.java` +- Test: `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/support/EnterpriseAutoFillServiceTest.java` + +- [ ] **Step 1: 写服务测试** + +覆盖以下行为: + +- 已存在实体不插入、不覆盖。 +- 缺失实体插入最小记录。 +- 中介来源写 `riskLevel=1`。 +- 员工亲属、信贷客户、供应商来源写 `riskLevel=null`。 +- 批量同一信用代码只插一次,并使用首次有效名称。 +- 插入时遇到主键重复按已存在处理。 + +核心断言示例: + +```java +assertEquals("SUPPLIER", captured.getEntSource()); +assertNull(captured.getRiskLevel()); +assertEquals("IMPORT", captured.getDataSource()); +``` + +- [ ] **Step 2: 运行测试确认失败** + +Run: + +```bash +mvn -pl ccdi-info-collection -Dtest=EnterpriseAutoFillServiceTest test +``` + +Expected: FAIL,类不存在。 + +- [ ] **Step 3: 实现服务接口** + +创建内部记录类型和方法: + +```java +@Service +public class EnterpriseAutoFillService { + @Resource + private CcdiEnterpriseBaseInfoMapper enterpriseBaseInfoMapper; + + public record EnterpriseFillItem( + String socialCreditCode, + String enterpriseName, + String entSource, + String dataSource, + String userName + ) {} + + @Transactional + public void ensureExists(EnterpriseFillItem item) { + ensureExistsBatch(List.of(item)); + } + + @Transactional + public void ensureExistsBatch(List items) { + // trim、过滤空信用代码、按 socialCreditCode 首次出现去重 + // selectBatchIds 查询已存在记录 + // 组装 CcdiEnterpriseBaseInfo 最小实体 + // riskLevel: INTERMEDIARY -> "1",其他 -> null + // dataSource: MANUAL 或 IMPORT + // 分批调用 enterpriseBaseInfoMapper.insertBatch + // 捕获 DuplicateKeyException 后继续逐条 selectById/insert,重复则忽略 + } +} +``` + +实现注意: + +- 不调用 `CcdiEnterpriseBaseInfoServiceImpl#insertEnterpriseBaseInfo`,避免复用手工新增风险等级校验。 +- 对非中介来源显式 `setRiskLevel(null)`。 +- 不更新已存在实体。 +- `enterpriseName` 使用来源业务已通过校验的名称,不增加额外兜底。 + +- [ ] **Step 4: 运行服务测试** + +Run: + +```bash +mvn -pl ccdi-info-collection -Dtest=EnterpriseAutoFillServiceTest test +``` + +Expected: PASS。 + +## Task 3: 员工亲属实体关联接入 + +**Files:** +- Modify: `CcdiStaffEnterpriseRelationServiceImpl.java` +- Modify: `CcdiStaffEnterpriseRelationImportServiceImpl.java` +- Modify: `CcdiStaffEnterpriseRelationServiceImplTest.java` +- Modify: `CcdiStaffEnterpriseRelationImportServiceImplTest.java` + +- [ ] **Step 1: 写新建测试** + +在 `insertRelation_shouldAllowValidFamily` 中注入 `EnterpriseAutoFillService` mock,并验证: + +```java +verify(enterpriseAutoFillService).ensureExists(argThat(item -> + "91310000123456789A".equals(item.socialCreditCode()) + && "测试企业".equals(item.enterpriseName()) + && "EMP_RELATION".equals(item.entSource()) + && "MANUAL".equals(item.dataSource()))); +``` + +- [ ] **Step 2: 写导入测试** + +扩展导入测试,验证成功行调用批量补入,失败行不进入补入集合。 + +- [ ] **Step 3: 运行员工亲属测试确认失败** + +Run: + +```bash +mvn -pl ccdi-info-collection -Dtest=CcdiStaffEnterpriseRelationServiceImplTest,CcdiStaffEnterpriseRelationImportServiceImplTest test +``` + +Expected: FAIL,尚未调用自动补全服务。 + +- [ ] **Step 4: 实现新建接入** + +在 `insertRelation` 中,`existsByPersonIdAndSocialCreditCode` 通过后、`relationMapper.insert` 前调用: + +```java +enterpriseAutoFillService.ensureExists(new EnterpriseAutoFillService.EnterpriseFillItem( + addDTO.getSocialCreditCode(), + addDTO.getEnterpriseName(), + EnterpriseSource.EMP_RELATION.getCode(), + DataSource.MANUAL.getCode(), + SecurityUtils.getUsername() +)); +``` + +- [ ] **Step 5: 实现导入接入** + +在 `importRelationAsync` 成功构建 `newRecords` 后、`saveBatch(newRecords, 500)` 前,按成功记录组装补入集合: + +```java +enterpriseAutoFillService.ensureExistsBatch(newRecords.stream() + .map(item -> new EnterpriseFillItem(item.getSocialCreditCode(), item.getEnterpriseName(), + EnterpriseSource.EMP_RELATION.getCode(), DataSource.IMPORT.getCode(), userName)) + .toList()); +``` + +- [ ] **Step 6: 运行员工亲属测试** + +Run: + +```bash +mvn -pl ccdi-info-collection -Dtest=CcdiStaffEnterpriseRelationServiceImplTest,CcdiStaffEnterpriseRelationImportServiceImplTest test +``` + +Expected: PASS。 + +## Task 4: 信贷客户实体关联接入 + +**Files:** +- Modify: `CcdiCustEnterpriseRelationServiceImpl.java` +- Modify: `CcdiCustEnterpriseRelationImportServiceImpl.java` +- Test: add `CcdiCustEnterpriseRelationServiceImplTest.java` if missing +- Test: add or extend `CcdiCustEnterpriseRelationImportServiceImplTest.java` + +- [ ] **Step 1: 写新建测试** + +验证 `insertRelation` 成功时调用自动补全: + +```java +assertEquals("CREDIT_CUSTOMER", item.entSource()); +assertEquals("MANUAL", item.dataSource()); +``` + +- [ ] **Step 2: 写导入测试** + +准备一条成功、一条重复组合失败,验证只有成功行传入 `ensureExistsBatch`。 + +- [ ] **Step 3: 运行测试确认失败** + +Run: + +```bash +mvn -pl ccdi-info-collection -Dtest=CcdiCustEnterpriseRelationServiceImplTest,CcdiCustEnterpriseRelationImportServiceImplTest test +``` + +Expected: FAIL,尚未调用自动补全服务。 + +- [ ] **Step 4: 实现新建接入** + +在 `insertRelation` 唯一性校验后、插入前调用自动补全,来源 `CREDIT_CUSTOMER`,数据来源 `MANUAL`。 + +- [ ] **Step 5: 实现导入接入** + +在 `importRelationAsync` 成功记录批量插入前调用自动补全,来源 `CREDIT_CUSTOMER`,数据来源 `IMPORT`。 + +- [ ] **Step 6: 运行测试** + +Run: + +```bash +mvn -pl ccdi-info-collection -Dtest=CcdiCustEnterpriseRelationServiceImplTest,CcdiCustEnterpriseRelationImportServiceImplTest test +``` + +Expected: PASS。 + +## Task 5: 中介实体关联和中介实体管理规则 + +**Files:** +- Modify: `CcdiIntermediaryServiceImpl.java` +- Modify: `CcdiIntermediaryEnterpriseRelationImportServiceImpl.java` +- Modify: `CcdiEnterpriseBaseInfoImportServiceImpl.java` +- Modify: `CcdiIntermediaryServiceImplTest.java` +- Modify: `CcdiIntermediaryEnterpriseRelationImportServiceImplTest.java` +- Modify: `CcdiEnterpriseBaseInfoImportServiceImplTest.java` + +- [ ] **Step 1: 写中介实体关联新建测试** + +验证实体库缺失不再抛“关联机构不存在”,而是调用自动补全并插入关联: + +```java +when(enterpriseRelationMapper.existsByIntermediaryBizIdAndSocialCreditCode("owner-biz", uscc)).thenReturn(false); +verify(enterpriseAutoFillService).ensureExists(argThat(item -> + "INTERMEDIARY".equals(item.entSource()) && "MANUAL".equals(item.dataSource()))); +verify(enterpriseRelationMapper).insert(any(CcdiIntermediaryEnterpriseRelation.class)); +``` + +- [ ] **Step 2: 写中介实体关联导入测试** + +将现有 `importEnterpriseRelationAsync_shouldFailWhenEnterpriseDoesNotExist` 改成成功场景,断言: + +- 不再产生失败记录。 +- 调用 `ensureExistsBatch`。 +- 插入关联记录。 + +- [ ] **Step 3: 写中介库管理默认高风险测试** + +在 `CcdiEnterpriseBaseInfoImportServiceImplTest` 增加: + +```java +excel.setRiskLevel(null); +excel.setEntSource("中介"); +CcdiEnterpriseBaseInfo entity = service.validateAndBuildEntity(excel, Set.of(), new HashSet<>(), "admin"); +assertEquals("1", entity.getRiskLevel()); +assertEquals("INTERMEDIARY", entity.getEntSource()); +``` + +在 `CcdiIntermediaryServiceImplTest` 验证 `insertIntermediaryEntity` 未传风险等级时写入 `1`。 + +- [ ] **Step 4: 运行测试确认失败** + +Run: + +```bash +mvn -pl ccdi-info-collection -Dtest=CcdiIntermediaryServiceImplTest,CcdiIntermediaryEnterpriseRelationImportServiceImplTest,CcdiEnterpriseBaseInfoImportServiceImplTest test +``` + +Expected: FAIL。 + +- [ ] **Step 5: 实现新建接入** + +修改 `validateEnterpriseRelation`:保留中介本人和重复组合校验,删除 `enterpriseBaseInfoMapper.selectById(socialCreditCode) == null` 抛错。 + +在 `insertIntermediaryEnterpriseRelation` 插入前调用自动补全,来源 `INTERMEDIARY`,数据来源 `MANUAL`。 + +- [ ] **Step 6: 实现导入接入** + +在导入服务中删除 `getExistingEnterpriseCodes` 的失败判断。成功记录插入前按 Excel 行组装实体补入,来源 `INTERMEDIARY`,数据来源 `IMPORT`。 + +- [ ] **Step 7: 实现中介实体默认高风险** + +在 `insertIntermediaryEntity` 中,如果 `riskLevel` 为空,设置为 `"1"`。 + +在 `CcdiEnterpriseBaseInfoImportServiceImpl#validateAndBuildEntity` 中,当解析出的 `entSource` 为 `INTERMEDIARY` 且 `riskLevel` 为空时,设置 `"1"`。 + +- [ ] **Step 8: 运行测试** + +Run: + +```bash +mvn -pl ccdi-info-collection -Dtest=CcdiIntermediaryServiceImplTest,CcdiIntermediaryEnterpriseRelationImportServiceImplTest,CcdiEnterpriseBaseInfoImportServiceImplTest test +``` + +Expected: PASS。 + +## Task 6: 招投标供应商接入 + +**Files:** +- Modify: `CcdiPurchaseTransactionServiceImpl.java` +- Modify: `CcdiPurchaseTransactionImportServiceImpl.java` +- Test: add `CcdiPurchaseTransactionServiceImplTest.java` if missing +- Modify: `CcdiPurchaseTransactionFeatureContractTest.java` or add import service unit test + +- [ ] **Step 1: 写新建测试** + +验证 `insertTransaction` 成功时,仅对 `supplierUscc` 不为空的供应商调用自动补全: + +```java +assertEquals("SUPPLIER", item.entSource()); +assertEquals("MANUAL", item.dataSource()); +assertEquals("供应商A", item.enterpriseName()); +``` + +- [ ] **Step 2: 写导入测试** + +准备一个成功采购事项和一个失败采购事项,断言只有成功事项的供应商进入 `ensureExistsBatch`,且来源为 `SUPPLIER`、数据来源为 `IMPORT`。 + +- [ ] **Step 3: 运行测试确认失败** + +Run: + +```bash +mvn -pl ccdi-info-collection -Dtest=CcdiPurchaseTransactionServiceImplTest,CcdiPurchaseTransactionImportServiceImplTest,CcdiPurchaseTransactionFeatureContractTest test +``` + +Expected: FAIL。 + +- [ ] **Step 4: 实现新建接入** + +在 `insertTransaction` 中,`buildSupplierEntities` 和校验完成后、写主从表前,收集供应商: + +```java +enterpriseAutoFillService.ensureExistsBatch(supplierList.stream() + .filter(item -> StringUtils.isNotEmpty(item.getSupplierUscc())) + .map(item -> new EnterpriseFillItem(item.getSupplierUscc(), item.getSupplierName(), + EnterpriseSource.SUPPLIER.getCode(), DataSource.MANUAL.getCode(), SecurityUtils.getUsername())) + .toList()); +``` + +- [ ] **Step 5: 实现导入接入** + +在 `importTransactionAsync` 中,按成功构建的 `newSuppliers` 收集供应商实体,在 `saveBatch(newTransactions, 500)` 之前调用自动补全。失败事项的供应商不进入 `newSuppliers`,天然不补。 + +- [ ] **Step 6: 运行测试** + +Run: + +```bash +mvn -pl ccdi-info-collection -Dtest=CcdiPurchaseTransactionServiceImplTest,CcdiPurchaseTransactionImportServiceImplTest,CcdiPurchaseTransactionFeatureContractTest test +``` + +Expected: PASS。 + +## Task 7: 集成验证与实施记录 + +**Files:** +- Create: `docs/reports/implementation/2026-04-26-enterprise-auto-fill-implementation.md` + +- [ ] **Step 1: 运行后端相关测试集合** + +Run: + +```bash +mvn -pl ccdi-info-collection -Dtest=CcdiEnumControllerTest,EnterpriseAutoFillServiceTest,CcdiStaffEnterpriseRelationServiceImplTest,CcdiStaffEnterpriseRelationImportServiceImplTest,CcdiCustEnterpriseRelationServiceImplTest,CcdiCustEnterpriseRelationImportServiceImplTest,CcdiIntermediaryServiceImplTest,CcdiIntermediaryEnterpriseRelationImportServiceImplTest,CcdiEnterpriseBaseInfoImportServiceImplTest,CcdiPurchaseTransactionServiceImplTest,CcdiPurchaseTransactionImportServiceImplTest,CcdiPurchaseTransactionFeatureContractTest test +``` + +Expected: BUILD SUCCESS。 + +- [ ] **Step 2: 如涉及数据库实测,确认 `risk_level` 落库值** + +验证样本: + +- 员工亲属自动补入:`risk_level IS NULL` +- 信贷客户自动补入:`risk_level IS NULL` +- 招投标供应商自动补入:`risk_level IS NULL` +- 中介自动补入:`risk_level = '1'` + +- [ ] **Step 3: 写实施记录** + +实施记录至少包含: + +```markdown +# 关联业务自动补入实体库实施记录 + +## 修改内容 +- 新增实体库自动补全服务 +- 接入员工亲属、中介、信贷客户、招投标链路 +- 新增 SUPPLIER 企业来源 + +## 影响范围 +- ccdi-info-collection 后端服务 +- 实体库管理企业来源枚举接口 + +## 验证情况 +- 列出 Maven 测试命令与结果 +- 列出页面或数据库验证结果 +``` + +- [ ] **Step 4: 检查工作区** + +Run: + +```bash +git status --short +``` + +Expected: 仅包含本次功能相关源码、测试和实施记录,不包含 `.DS_Store` 或生成测试文件。 + +- [ ] **Step 5: 提交后端改动** + +```bash +git add ccdi-info-collection/src/main/java/com/ruoyi/info/collection ccdi-info-collection/src/test/java/com/ruoyi/info/collection docs/reports/implementation/2026-04-26-enterprise-auto-fill-implementation.md +git commit -m "实现关联业务自动补入实体库" +``` diff --git a/docs/plans/frontend/2026-04-26-enterprise-auto-fill-frontend-implementation.md b/docs/plans/frontend/2026-04-26-enterprise-auto-fill-frontend-implementation.md new file mode 100644 index 00000000..7c5b2f39 --- /dev/null +++ b/docs/plans/frontend/2026-04-26-enterprise-auto-fill-frontend-implementation.md @@ -0,0 +1,244 @@ +# 关联业务自动补入实体库 Frontend Implementation Plan + +> **执行约束:** 按当前项目 `AGENTS.md` 执行;未获得用户明确要求时不启用 subagent。Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 验证前端通过现有企业来源枚举接口展示新增 `SUPPLIER=供应商`,并在真实页面确认自动补入实体库后的展示链路可用。 + +**Architecture:** 本次不新增前端交互,不修改前端源码。企业来源选项由后端 `/ccdi/enum/enterpriseSource` 返回,实体库管理页与招投标详情页沿用 `getEnterpriseSourceOptions()` 展示新增来源;前端工作重点是运行真实页面验证并记录结果。 + +**Tech Stack:** Vue 2, Element UI, npm, nvm, Playwright. + +--- + +## File Structure + +- No source changes expected: `ruoyi-ui/src/views/ccdiEnterpriseBaseInfo/index.vue` + - 已通过 `getEnterpriseSourceOptions()` 获取企业来源。 +- No source changes expected: `ruoyi-ui/src/views/ccdiPurchaseTransaction/index.vue` + - 企业详情弹窗已通过 `getEnterpriseSourceOptions()` 格式化企业来源。 +- Create or update: `docs/reports/implementation/2026-04-26-enterprise-auto-fill-implementation.md` + - 补充前端真实页面验证结果。 +- Generated test files: + - 如需生成导入样本,放在 `output/playwright/` 或 `output/spreadsheet/`,不提交到 git。 + +## Task 1: 前端源码确认 + +**Files:** +- Read: `ruoyi-ui/src/views/ccdiEnterpriseBaseInfo/index.vue` +- Read: `ruoyi-ui/src/views/ccdiPurchaseTransaction/index.vue` +- Read: `ruoyi-ui/src/api/ccdiEnum.js` + +- [ ] **Step 1: 确认企业来源接口使用点** + +Run: + +```bash +rg -n "getEnterpriseSourceOptions|formatEnterpriseSource|enterpriseSourceOptions" ruoyi-ui/src/views/ccdiEnterpriseBaseInfo/index.vue ruoyi-ui/src/views/ccdiPurchaseTransaction/index.vue ruoyi-ui/src/api/ccdiEnum.js +``` + +Expected: + +- 实体库管理页调用 `getEnterpriseSourceOptions()` +- 招投标详情企业弹窗调用 `getEnterpriseSourceOptions()` +- API 路径为 `/ccdi/enum/enterpriseSource` + +- [ ] **Step 2: 确认不存在本地硬编码映射** + +Expected: 未发现页面本地写死企业来源映射;如发现硬编码映射,停止实施并先修订本计划。 + +## Task 2: 前端启动准备 + +**Files:** +- Read: `ruoyi-ui/package.json` +- Use: `ruoyi-ui/.nvmrc` if present + +- [ ] **Step 1: 使用 nvm 确认 Node 版本** + +Run: + +```bash +cd ruoyi-ui +source ~/.nvm/nvm.sh +nvm use +node -v +``` + +Expected: 切换到项目要求的 Node 版本。 + +- [ ] **Step 2: 启动前端开发服务** + +Run: + +```bash +cd ruoyi-ui +source ~/.nvm/nvm.sh +nvm use +npm run dev +``` + +Expected: 前端服务启动成功,记录实际 URL。若端口占用,按 Vite/Vue CLI 输出使用实际端口。 + +- [ ] **Step 3: 启动后端** + +Run: + +```bash +sh bin/restart_java_backend.sh +``` + +Expected: 后端 `62318` 可访问。 + +测试结束后必须关闭本次启动的前后端进程。 + +## Task 3: 实体库管理页面验证 + +**Files:** +- Verify: real page `ruoyi-ui/src/views/ccdiEnterpriseBaseInfo/index.vue` +- Do not use: prototype pages + +- [ ] **Step 1: Playwright 打开真实实体库管理页面** + +进入实际路由: + +```text +http://localhost:8080/maintain/enterpriseBaseInfo +``` + +Expected: 页面正常加载。 + +- [ ] **Step 2: 验证企业来源下拉包含供应商** + +操作: + +- 打开查询区“企业来源”下拉。 +- 检查存在“供应商”选项。 + +Expected: 下拉出现“供应商”。 + +- [ ] **Step 3: 验证列表/详情展示** + +准备后端自动补入的一条 `ent_source=SUPPLIER` 测试企业后: + +- 在实体库管理页面搜索该统一社会信用代码。 +- 检查列表企业来源显示“供应商”。 +- 打开详情,检查企业来源显示“供应商”,风险等级为空时显示为空值占位。 + +Expected: 枚举中文展示正确。 + +## Task 4: 招投标真实页面验证 + +**Files:** +- Verify: `ruoyi-ui/src/views/ccdiPurchaseTransaction/index.vue` + +- [ ] **Step 1: 打开真实招投标信息维护页面** + +进入实际路由,例如: + +```text +http://localhost:8080/maintain/purchaseTransaction +``` + +Expected: 页面正常加载。 + +- [ ] **Step 2: 新建含供应商统一信用代码的招投标记录** + +使用真实页面新增测试数据: + +- 采购事项 ID 使用本轮唯一测试值。 +- 供应商明细中至少一条填写供应商名称和统一信用代码。 + +Expected: 保存成功。 + +- [ ] **Step 3: 回到实体库管理验证供应商自动补入** + +用供应商统一信用代码查询实体库。 + +Expected: + +- 能查到实体库记录。 +- 企业名称为供应商名称。 +- 企业来源显示“供应商”。 +- 风险等级为空。 + +- [ ] **Step 4: 清理测试数据** + +删除本轮新建的招投标测试数据和自动补入的实体库测试数据。若实体库记录已有关联限制,先删除业务数据再删除实体库记录。 + +Expected: 页面列表回到测试前状态。 + +## Task 5: 导入页面验证 + +**Files:** +- Generated samples: `output/playwright/` or `output/spreadsheet/` + +- [ ] **Step 1: 在真实页面下载导入模板** + +必须从当前业务页面点击下载模板,不手工凭记忆构造表头。 + +Expected: 获取当前模板。 + +- [ ] **Step 2: 基于模板生成测试文件** + +至少覆盖: + +- 员工亲属实体关联:页面 `/maintain/staffEnterpriseRelation`,点击“导入”后在弹窗中点击“下载模板”,接口 `ccdi/staffEnterpriseRelation/importTemplate`,上传接口 `/ccdi/staffEnterpriseRelation/importData`,验证成功行实体自动补入 `EMP_RELATION`。 +- 信贷客户实体关联:页面 `/maintain/custEnterpriseRelation`,点击“导入”后在弹窗中点击“下载模板”,接口 `ccdi/custEnterpriseRelation/importTemplate`,上传接口 `/ccdi/custEnterpriseRelation/importData`,验证成功行实体自动补入 `CREDIT_CUSTOMER`。 +- 中介实体关联:页面 `/maintain/intermediary`,点击“导入中介实体关联关系”,在导入弹窗下载模板,接口 `ccdi/intermediary/importEnterpriseRelationTemplate`,上传接口 `/ccdi/intermediary/importEnterpriseRelationData`,验证成功行实体自动补入 `INTERMEDIARY` 且风险等级高风险。 +- 招投标信息维护:页面 `/maintain/purchaseTransaction`,点击“导入”后在弹窗中点击“下载模板”,接口 `ccdi/purchaseTransaction/importTemplate`,上传接口 `/ccdi/purchaseTransaction/importData`,验证供应商统一信用代码自动补入 `SUPPLIER`。 +- 每个页面至少包含一个混合成功失败样本,验证失败行不补实体。 + +Expected: 测试文件保存在 `output/playwright/` 或 `output/spreadsheet/`,不提交 git。 + +- [ ] **Step 3: 上传并核对导入状态** + +在真实页面上传文件,核对: + +- 页面提示 +- 导入状态 +- 失败记录弹窗 +- 列表总数变化 +- 实体库是否新增对应实体 + +Expected: 成功行补实体,失败行不补实体。 + +- [ ] **Step 4: 清理测试数据和任务缓存** + +删除本轮成功写入的业务数据和实体库数据,清理页面本地导入任务缓存。 + +Expected: 页面和数据库不残留本轮测试数据。 + +## Task 6: 记录验证结果 + +**Files:** +- Modify: `docs/reports/implementation/2026-04-26-enterprise-auto-fill-implementation.md` + +- [ ] **Step 1: 补充前端验证记录** + +记录: + +```markdown +## 前端验证 +- Node 版本: +- 前端 URL: +- 后端 URL: +- 实体库企业来源“供应商”展示: +- 招投标供应商自动补入页面验证: +- 导入页面验证: +- 测试数据清理: +``` + +- [ ] **Step 2: 停止测试进程** + +停止本次启动的前端和后端进程。 + +Expected: 无测试进程残留。 + +- [ ] **Step 3: 检查生成文件未进入 git** + +Run: + +```bash +git status --short +``` + +Expected: `output/playwright/`、`output/spreadsheet/` 下生成测试文件不在待提交范围。