diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImpl.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImpl.java index 2747e5e1..cdc1bf2f 100644 --- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImpl.java +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImpl.java @@ -1,6 +1,7 @@ package com.ruoyi.ccdi.project.service.impl; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.ccdi.project.domain.CcdiModelParam; import com.ruoyi.ccdi.project.domain.CcdiProject; import com.ruoyi.ccdi.project.domain.dto.CcdiProjectAbnormalAccountQueryDTO; import com.ruoyi.ccdi.project.domain.dto.CcdiProjectEmployeeCreditNegativeQueryDTO; @@ -39,12 +40,11 @@ import com.ruoyi.ccdi.project.domain.vo.CcdiProjectSuspiciousTransactionItemVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectSuspiciousTransactionPageVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectTopRiskPeopleItemVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectTopRiskPeopleVO; -import com.ruoyi.ccdi.project.domain.vo.ModelParamAllVO; +import com.ruoyi.ccdi.project.mapper.CcdiModelParamMapper; import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper; import com.ruoyi.ccdi.project.mapper.CcdiBankTagResultMapper; import com.ruoyi.ccdi.project.mapper.CcdiProjectOverviewEmployeeResultMapper; import com.ruoyi.ccdi.project.mapper.CcdiProjectOverviewMapper; -import com.ruoyi.ccdi.project.service.ICcdiModelParamService; import com.ruoyi.ccdi.project.service.ICcdiProjectOverviewService; import com.ruoyi.common.exception.ServiceException; import jakarta.servlet.http.HttpServletResponse; @@ -74,6 +74,9 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi @Resource private CcdiProjectMapper projectMapper; + @Resource + private CcdiModelParamMapper modelParamMapper; + @Resource private CcdiProjectOverviewEmployeeResultMapper overviewEmployeeResultMapper; @@ -89,9 +92,6 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi @Resource private CcdiProjectOverviewReportPdfExporter reportPdfExporter; - @Resource - private ICcdiModelParamService modelParamService; - @Override public CcdiProjectOverviewDashboardVO getDashboard(Long projectId) { CcdiProject project = overviewMapper.selectDashboardBaseByProjectId(projectId); @@ -328,7 +328,7 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi report.setUploadSubjects(defaultList(overviewMapper.selectReportUploadSubjects(projectId)).stream() .peek(item -> item.setDataPeriod(formatDataPeriod(item.getMinTrxDate(), item.getMaxTrxDate()))) .toList()); - report.setParams(buildReportParams(projectId)); + report.setParams(buildReportParams(project)); report.setModelSummaries(defaultList(overviewMapper.selectReportRiskModelSummaries(projectId))); report.setRiskPeople(defaultList(overviewMapper.selectReportRiskPeople(projectId)).stream() .peek(item -> item.setActionLabel(ACTION_LABEL)) @@ -554,21 +554,23 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi return row; } - private List buildReportParams(Long projectId) { - ModelParamAllVO response = modelParamService.selectAllParams(projectId); - return defaultList(response == null ? null : response.getModels()).stream() - .flatMap(model -> defaultList(model.getParams()).stream().map(param -> { - CcdiProjectOverviewReportParamVO row = new CcdiProjectOverviewReportParamVO(); - row.setModelName(model.getModelName()); - row.setParamName(param.getParamName()); - row.setParamValue(param.getParamValue()); - row.setParamUnit(param.getParamUnit()); - row.setParamDesc(param.getParamDesc()); - return row; - })) + private List buildReportParams(CcdiProject project) { + Long effectiveProjectId = "default".equals(project.getConfigType()) ? 0L : project.getProjectId(); + return defaultList(modelParamMapper.selectByProjectId(effectiveProjectId)).stream() + .map(this::buildReportParamRow) .toList(); } + private CcdiProjectOverviewReportParamVO buildReportParamRow(CcdiModelParam param) { + CcdiProjectOverviewReportParamVO row = new CcdiProjectOverviewReportParamVO(); + row.setModelName(param.getModelName()); + row.setParamName(param.getParamName()); + row.setParamValue(param.getParamValue()); + row.setParamUnit(param.getParamUnit()); + row.setParamDesc(param.getParamDesc()); + return row; + } + private String formatDataPeriod(String minTrxDate, String maxTrxDate) { if (minTrxDate == null || minTrxDate.isBlank() || maxTrxDate == null || maxTrxDate.isBlank()) { return "-"; diff --git a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/CcdiProjectOverviewServiceStructureTest.java b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/CcdiProjectOverviewServiceStructureTest.java index 7354f944..8390b978 100644 --- a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/CcdiProjectOverviewServiceStructureTest.java +++ b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/CcdiProjectOverviewServiceStructureTest.java @@ -1,7 +1,9 @@ package com.ruoyi.ccdi.project.service; +import java.util.Arrays; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; class CcdiProjectOverviewServiceStructureTest { @@ -35,4 +37,15 @@ class CcdiProjectOverviewServiceStructureTest { assertNotNull(clazz.getMethod("refreshOverviewEmployeeResults", Long.class, String.class)); assertNotNull(clazz.getMethod("refreshProjectRiskCounts", Long.class, String.class)); } + + @Test + void overviewServiceImplShouldNotDependOnModelParamService() throws Exception { + Class clazz = Class.forName( + "com.ruoyi.ccdi.project.service.impl.CcdiProjectOverviewServiceImpl" + ); + + assertFalse(Arrays.stream(clazz.getDeclaredFields()).anyMatch(field -> + "com.ruoyi.ccdi.project.service.ICcdiModelParamService".equals(field.getType().getName()) + )); + } } diff --git a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImplTest.java b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImplTest.java index c1e5d2a7..50712242 100644 --- a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImplTest.java +++ b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImplTest.java @@ -1,6 +1,7 @@ package com.ruoyi.ccdi.project.service.impl; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.ccdi.project.domain.CcdiModelParam; import com.ruoyi.ccdi.project.domain.CcdiProject; import com.ruoyi.ccdi.project.domain.dto.CcdiProjectPersonAnalysisDetailQueryDTO; import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskPeopleQueryDTO; @@ -16,6 +17,7 @@ import com.ruoyi.ccdi.project.domain.vo.CcdiBankStatementListVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeRiskAggregateVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewDashboardVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewEmployeeHitRowVO; +import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewReportVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisDetailVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisBasicInfoVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisObjectRecordVO; @@ -27,6 +29,7 @@ import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelPeopleVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskPeopleOverviewVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectSuspiciousTransactionItemVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectTopRiskPeopleVO; +import com.ruoyi.ccdi.project.mapper.CcdiModelParamMapper; import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper; import com.ruoyi.ccdi.project.mapper.CcdiBankTagResultMapper; import com.ruoyi.ccdi.project.mapper.CcdiProjectOverviewEmployeeResultMapper; @@ -38,6 +41,7 @@ import java.util.List; import org.springframework.mock.web.MockHttpServletResponse; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @@ -65,6 +69,9 @@ class CcdiProjectOverviewServiceImplTest { @Mock private CcdiProjectMapper projectMapper; + @Mock + private CcdiModelParamMapper modelParamMapper; + @Mock private CcdiProjectOverviewEmployeeResultMapper overviewEmployeeResultMapper; @@ -77,6 +84,9 @@ class CcdiProjectOverviewServiceImplTest { @Mock private CcdiProjectRiskDetailWorkbookExporter workbookExporter; + @Mock + private CcdiProjectOverviewReportPdfExporter reportPdfExporter; + @Test void shouldBuildDashboardWithNoRiskCount() { CcdiProject project = new CcdiProject(); @@ -300,6 +310,37 @@ class CcdiProjectOverviewServiceImplTest { ); } + @Test + void shouldExportOverviewReportParamsFromDefaultProjectConfig() throws Exception { + CcdiProject project = new CcdiProject(); + project.setProjectId(40L); + project.setProjectName("测试项目"); + project.setConfigType("default"); + when(projectMapper.selectById(40L)).thenReturn(project); + + CcdiProject dashboardProject = new CcdiProject(); + dashboardProject.setProjectId(40L); + dashboardProject.setTargetCount(10); + dashboardProject.setHighRiskCount(1); + dashboardProject.setMediumRiskCount(2); + dashboardProject.setLowRiskCount(3); + when(overviewMapper.selectDashboardBaseByProjectId(40L)).thenReturn(dashboardProject); + when(modelParamMapper.selectByProjectId(0L)).thenReturn(List.of( + buildModelParam("LARGE_TRANSACTION", "大额交易模型", "单笔金额", "1000", "元", "单笔金额阈值") + )); + + MockHttpServletResponse response = new MockHttpServletResponse(); + service.exportOverviewReport(response, 40L); + + ArgumentCaptor captor = + ArgumentCaptor.forClass(CcdiProjectOverviewReportVO.class); + verify(modelParamMapper).selectByProjectId(0L); + verify(reportPdfExporter).export(eq(response), captor.capture()); + assertEquals(1, captor.getValue().getParams().size()); + assertEquals("大额交易模型", captor.getValue().getParams().getFirst().getModelName()); + assertEquals("单笔金额", captor.getValue().getParams().getFirst().getParamName()); + } + @Test void shouldReturnPersonAnalysisDetailWithBasicInfoAndGroupedAbnormalDetail() { CcdiProject project = new CcdiProject(); @@ -539,6 +580,24 @@ class CcdiProjectOverviewServiceImplTest { return result; } + private CcdiModelParam buildModelParam( + String modelCode, + String modelName, + String paramName, + String paramValue, + String paramUnit, + String paramDesc + ) { + CcdiModelParam param = new CcdiModelParam(); + param.setModelCode(modelCode); + param.setModelName(modelName); + param.setParamName(paramName); + param.setParamValue(paramValue); + param.setParamUnit(paramUnit); + param.setParamDesc(paramDesc); + return param; + } + private CcdiProjectRiskHitTagVO buildHitTag( String modelCode, String modelName, diff --git a/docs/plans/backend/2026-05-06-staff-asset-import-and-enterprise-autofill-fix-backend-solution.md b/docs/plans/backend/2026-05-06-staff-asset-import-and-enterprise-autofill-fix-backend-solution.md new file mode 100644 index 00000000..c17253f7 --- /dev/null +++ b/docs/plans/backend/2026-05-06-staff-asset-import-and-enterprise-autofill-fix-backend-solution.md @@ -0,0 +1,236 @@ +# 员工资产导入与员工亲属实体自动补入后端解决方案 + +## 背景 + +本方案针对以下两个已验证问题: + +1. 【员工信息维护】和【员工亲属关系维护】使用双 Sheet 模板导入时,第二个 Sheet 的资产信息只按数据库已有主数据反查归属,不能识别同一个导入文件中刚导入的员工或亲属关系,导致提示“未找到资产归属员工”等错误。 +2. 【员工亲属实体关联】新增或导入关系人与企业关联后,只写入员工亲属实体关联表,没有同步生成实体库数据,导致企业名称和统一社会信用代码不能自动落入【实体库管理】。 + +本次解决方案只涉及后端,不调整前端页面结构、上传入口和模板样式。现有前端已经能接收员工任务 ID 与资产任务 ID,并分别轮询失败记录,因此后端需要保持现有返回字段兼容。 + +## 目标 + +- 双 Sheet 导入时,主 Sheet 和资产 Sheet 按同一个文件内的业务依赖顺序处理。 +- 资产 Sheet 可以关联到数据库已存在的员工或亲属主数据,也可以关联到同一文件中本轮成功导入的员工或亲属主数据。 +- 主 Sheet 校验失败的数据不能作为资产归属依据。 +- 员工亲属实体关联新增和导入成功后,缺失的企业自动补入实体库。 +- 实体库自动补入只插入缺失企业,不更新已存在实体。 + +## 方案一:员工信息维护双 Sheet 导入 + +### 当前问题链路 + +- `/ccdi/baseStaff/importData` 当前分别读取 `员工信息` 与 `员工资产信息` 两个 Sheet。 +- 有员工数据时调用 `baseStaffService.importBaseStaff(staffList)`,有资产数据时调用 `baseStaffAssetImportService.importAssetInfo(assetList)`。 +- 两个任务独立异步执行,员工资产任务只通过 `ccdi_base_staff.id_card` 查询数据库已有员工。 +- 当模板中员工和资产同时首次导入时,资产任务无法稳定看到本轮员工导入结果。 + +### 改造策略 + +保留接口路径、模板名称、返回结构和前端轮询模型,新增后端双 Sheet 编排能力。 + +1. 在员工导入服务中增加双 Sheet 提交方法: + - 输入:`List staffList`、`List assetList` + - 输出:`BaseStaffImportSubmitResultVO` + - 有员工 Sheet 时生成 `staffTaskId` + - 有资产 Sheet 时生成 `assetTaskId` + +2. 新增一个统一异步编排方法,按顺序执行: + - 初始化员工导入任务状态 + - 执行员工主数据校验和批量插入 + - 收集本轮员工导入成功的 `idCard` + - 更新员工任务状态和失败记录 + - 初始化员工资产导入任务状态 + - 执行员工资产导入 + - 员工资产归属候选来源为: + - 数据库已有 `ccdi_base_staff.id_card` + - 本轮员工 Sheet 成功导入的 `idCard` + - 更新员工资产任务状态和失败记录 + +3. 员工资产导入逻辑调整: + - 保留“员工资产只允许员工本人身份证号”的业务规则。 + - 保留重复校验规则:`personId + assetMainType + assetSubType + assetName`。 + - `personId` 既可以命中数据库已有员工,也可以命中本轮成功导入员工。 + - 只命中员工 Sheet 失败行、且数据库中也不存在该身份证号时,资产行进入失败记录。 + +4. `/ccdi/baseStaff/importData` 改为调用新的双 Sheet 提交方法,不再由 Controller 分别提交两个互相独立的异步任务。 + +### 结果状态 + +- 只填员工 Sheet:只返回 `staffTaskId`,行为保持不变。 +- 只填员工资产 Sheet:只返回 `assetTaskId`,按数据库已有员工校验。 +- 两个 Sheet 都填写:返回 `staffTaskId` 与 `assetTaskId`,但后端在同一个编排任务中先处理员工再处理资产。 + +## 方案二:员工亲属关系维护双 Sheet 导入 + +### 当前问题链路 + +- `/ccdi/staffFmyRelation/importData` 当前分别读取 `员工亲属关系信息` 与 `亲属资产信息` 两个 Sheet。 +- 有亲属关系数据时调用 `relationService.importRelation(relationList)`,有亲属资产数据时调用 `assetInfoImportService.importAssetInfo(assetList)`。 +- 亲属资产导入只通过 `ccdi_staff_fmy_relation.relation_cert_no` 查询数据库已有亲属关系。 +- 当模板中亲属关系和亲属资产同时首次导入时,资产任务无法稳定看到本轮亲属关系导入结果。 + +### 改造策略 + +保留接口路径、模板名称、返回结构和前端轮询模型,新增员工亲属关系双 Sheet 编排能力。 + +1. 在员工亲属关系导入服务中增加双 Sheet 提交方法: + - 输入:`List relationList`、`List assetList` + - 输出:`StaffFmyRelationImportSubmitResultVO` + - 有亲属关系 Sheet 时生成 `relationTaskId` + - 有亲属资产 Sheet 时生成 `assetTaskId` + +2. 新增一个统一异步编排方法,按顺序执行: + - 初始化亲属关系导入任务状态 + - 执行亲属关系校验和批量插入 + - 收集本轮成功导入且有效的亲属关系映射: + - `relationCertNo` 作为资产 Sheet 的 `personId` + - `personId` 作为资产落库的 `familyId` + - 更新亲属关系任务状态和失败记录 + - 初始化亲属资产导入任务状态 + - 执行亲属资产导入 + - 亲属资产归属候选来源为: + - 数据库已有有效员工亲属关系 + - 本轮亲属关系 Sheet 成功导入的有效员工亲属关系 + - 更新亲属资产任务状态和失败记录 + +3. 亲属资产导入逻辑调整: + - 保留 `family_id = 员工身份证号`、`person_id = 亲属身份证号` 的落库规则。 + - `personId` 命中唯一亲属关系时导入成功。 + - `personId` 未命中数据库和本轮成功亲属关系时,失败原因为“未找到亲属资产归属员工”。 + - `personId` 命中多个员工归属时,失败原因为“亲属资产归属员工不唯一”。 + - 只命中亲属关系 Sheet 失败行、且数据库中也不存在有效亲属关系时,资产行进入失败记录。 + +4. `/ccdi/staffFmyRelation/importData` 改为调用新的双 Sheet 提交方法,不再由 Controller 分别提交两个互相独立的异步任务。 + +### 结果状态 + +- 只填亲属关系 Sheet:只返回 `relationTaskId`,行为保持不变。 +- 只填亲属资产 Sheet:只返回 `assetTaskId`,按数据库已有亲属关系校验。 +- 两个 Sheet 都填写:返回 `relationTaskId` 与 `assetTaskId`,但后端在同一个编排任务中先处理亲属关系再处理亲属资产。 + +## 方案三:员工亲属实体关联自动补入实体库 + +### 当前问题链路 + +- 员工亲属实体关联新增只写入 `ccdi_staff_enterprise_relation`。 +- 员工亲属实体关联导入成功行也只写入 `ccdi_staff_enterprise_relation`。 +- 当前源码中没有可复用的实体库自动补入服务实现。 + +### 改造策略 + +新增后端内部服务 `EnterpriseAutoFillService`,统一处理缺失企业的最小插入。 + +1. 新增服务文件: + - `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/support/EnterpriseAutoFillService.java` + +2. 服务方法: + - `ensureExists(EnterpriseFillItem item)` + - `ensureExistsBatch(List items)` + +3. `EnterpriseFillItem` 字段: + - `socialCreditCode` + - `enterpriseName` + - `entSource` + - `dataSource` + - `userName` + +4. 插入规则: + - 按 `socialCreditCode` 去重。 + - 先批量查询 `ccdi_enterprise_base_info` 已存在记录。 + - 已存在实体不更新。 + - 缺失实体最小插入: + - `social_credit_code = 统一社会信用代码` + - `enterprise_name = 企业名称` + - `ent_source = EMP_RELATION` + - `data_source = MANUAL` 或 `IMPORT` + - `risk_level = NULL` + - `create_by/update_by = 当前用户` + - 不引入额外兜底字段,不改变实体库手工新增规则。 + +5. 接入员工亲属实体关联新增: + - 在 `CcdiStaffEnterpriseRelationServiceImpl#insertRelation` 中,亲属有效性校验和组合查重通过后,写关联表前调用: + - `entSource = EnterpriseSource.EMP_RELATION.getCode()` + - `dataSource = DataSource.MANUAL.getCode()` + +6. 接入员工亲属实体关联导入: + - 在 `CcdiStaffEnterpriseRelationImportServiceImpl#importRelationAsync` 中,只对校验成功并即将插入的 `newRecords` 组装补入列表。 + - 批量写关联表前调用 `ensureExistsBatch`。 + - 失败行不进入实体库补入集合。 + - `dataSource = DataSource.IMPORT.getCode()`。 + +## 测试方案 + +### 单元测试 + +1. 员工信息维护双 Sheet: + - 同一模板中员工 Sheet 新增员工,员工资产 Sheet 使用该员工身份证号,资产应导入成功。 + - 员工 Sheet 行校验失败,资产 Sheet 使用该失败员工身份证号且数据库不存在,资产应失败。 + - 只导资产 Sheet,数据库已有员工时资产成功。 + - 只导资产 Sheet,数据库无员工时资产失败。 + - 资产重复命中数据库或当前文件重复时失败。 + +2. 员工亲属关系维护双 Sheet: + - 同一模板中亲属关系 Sheet 新增亲属,亲属资产 Sheet 使用该亲属证件号,资产应导入成功,并落库 `family_id = 员工身份证号`。 + - 亲属关系 Sheet 行校验失败,亲属资产 Sheet 使用该失败亲属证件号且数据库不存在,资产应失败。 + - 只导亲属资产 Sheet,数据库已有唯一有效亲属关系时资产成功。 + - 只导亲属资产 Sheet,数据库不存在亲属关系时资产失败。 + - 同一亲属证件号命中多个员工归属时资产失败。 + +3. 员工亲属实体关联自动补入: + - 手工新增员工亲属实体关联成功时,缺失企业自动插入实体库,来源为 `EMP_RELATION`,数据来源为 `MANUAL`。 + - 手工新增时实体库已存在该统一社会信用代码,不更新实体库旧记录。 + - 导入成功行自动插入实体库,来源为 `EMP_RELATION`,数据来源为 `IMPORT`。 + - 导入失败行不插入实体库。 + - 同一批多个成功行引用同一统一社会信用代码,只补入一次。 + +### 接口验证 + +1. 调用 `/ccdi/baseStaff/importTemplate` 下载真实模板,构造: + - `员工信息` 新员工 + - `员工资产信息` 引用该员工身份证号 + - 上传 `/ccdi/baseStaff/importData` + - 轮询员工任务与资产任务,确认都成功 + - 查询 `ccdi_base_staff` 与 `ccdi_asset_info`,确认员工和资产落库 + +2. 调用 `/ccdi/staffFmyRelation/importTemplate` 下载真实模板,构造: + - `员工亲属关系信息` 新亲属 + - `亲属资产信息` 引用该亲属证件号 + - 上传 `/ccdi/staffFmyRelation/importData` + - 轮询亲属关系任务与亲属资产任务,确认都成功 + - 查询 `ccdi_staff_fmy_relation` 与 `ccdi_asset_info`,确认亲属关系和资产落库 + +3. 调用员工亲属实体关联新增接口: + - 新增前确认 `ccdi_enterprise_base_info` 不存在该统一社会信用代码 + - 新增关联成功后查询实体库,确认自动生成企业记录 + - 校验 `ent_source = EMP_RELATION` + +### 页面验证 + +完成后需要使用真实业务页面验证: + +1. 【员工信息维护】页面下载模板,按模板填写员工和员工资产,上传后检查任务状态、失败记录和列表详情。 +2. 【员工亲属关系维护】页面下载模板,按模板填写亲属关系和亲属资产,上传后检查任务状态、失败记录和详情资产列表。 +3. 【员工亲属实体关联】页面新增关系人与企业关联后,进入【实体库管理】查询该统一社会信用代码,确认企业已自动生成且来源显示为员工关系人。 + +测试结束后清理本轮新增员工、亲属关系、资产、员工亲属实体关联和自动补入实体库数据,并关闭测试过程中启动的前后端进程。 + +## 实施顺序 + +1. 抽取员工导入和亲属关系导入的可复用执行方法,返回成功主数据上下文和失败记录。 +2. 新增员工信息维护双 Sheet 后端编排方法,接入 Controller。 +3. 新增员工亲属关系维护双 Sheet 后端编排方法,接入 Controller。 +4. 新增 `EnterpriseAutoFillService`。 +5. 员工亲属实体关联新增链路接入实体库自动补入。 +6. 员工亲属实体关联导入链路接入实体库自动补入。 +7. 补充单元测试。 +8. 执行接口验证和真实页面验证。 +9. 新增实施记录到 `docs/reports/implementation/`。 + +## 风险点 + +- 双 Sheet 导入不能继续使用两个互相独立的异步任务,否则仍然存在主数据与资产任务执行顺序不确定的问题。 +- 资产归属上下文必须只使用“数据库已有数据”和“本轮主 Sheet 成功数据”,不能把失败主数据作为资产归属。 +- 实体库自动补入不能更新已存在企业,避免覆盖人工维护的企业名称、风险等级和来源信息。 +- 员工亲属实体关联自动补入必须只处理成功行,失败行不能产生实体库记录。 diff --git a/docs/plans/frontend/2026-04-29-base-staff-and-staff-recruitment-formal-shell-frontend-implementation.md b/docs/plans/frontend/2026-04-29-base-staff-and-staff-recruitment-formal-shell-frontend-implementation.md deleted file mode 100644 index 1826d4d6..00000000 --- a/docs/plans/frontend/2026-04-29-base-staff-and-staff-recruitment-formal-shell-frontend-implementation.md +++ /dev/null @@ -1,34 +0,0 @@ -# 员工信息维护与招聘信息管理正式化外壳前端实施计划 - -## 目标 - -- 基于当前本地最新代码,为 `ccdiBaseStaff` 与 `ccdiStaffRecruitment` 试套正式化外壳样式。 -- 仅调整查询区、工具条、表格区、分页区与弹窗壳层视觉。 -- 不改字段顺序、不改按钮位置、不改功能块结构。 - -## 范围 - -- `ruoyi-ui/src/views/ccdiBaseStaff/index.vue` -- `ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue` -- `ruoyi-ui/tests/unit/base-staff-formal-shell-layout.test.js` -- `ruoyi-ui/tests/unit/staff-recruitment-formal-shell-layout.test.js` - -## 方案 - -- 复用现有 `app-container`、`query-form`、`mb8`、弹窗 class,只补最少样式。 -- 给列表区新增最小表格外壳,保证分页和表格归一。 -- 通过边框、浅底、留白和表头背景统一正式化视觉。 - -## 验证 - -- `node tests/unit/base-staff-formal-shell-layout.test.js` -- `node tests/unit/staff-recruitment-formal-shell-layout.test.js` -- `node tests/unit/employee-asset-maintenance-layout.test.js` -- `node tests/unit/staff-recruitment-import-toolbar.test.js` - -## 完成标准 - -- 两个页面外壳样式统一 -- 按钮顺序和功能入口保持不变 -- 单测通过 -- 浏览器实测通过 diff --git a/docs/plans/frontend/2026-04-29-batch-formal-shell-rollout-frontend-implementation.md b/docs/plans/frontend/2026-04-29-batch-formal-shell-rollout-frontend-implementation.md deleted file mode 100644 index 80c86a83..00000000 --- a/docs/plans/frontend/2026-04-29-batch-formal-shell-rollout-frontend-implementation.md +++ /dev/null @@ -1,65 +0,0 @@ -# 2026-04-29 批量正式化外壳样式实施计划 - -## 目标 - -- 基于当前本地最新前端代码,批量推进信息维护相关页面的正式化外壳样式。 -- 严格保持“只改样式、不改内容和功能”的边界。 -- 复用已经在详情弹窗、员工信息维护页、招聘信息管理页验证过的正式化样式骨架。 - -## 范围 - -- `ruoyi-ui/src/views/ccdiAccountInfo/index.vue` -- `ruoyi-ui/src/views/ccdiCreditInfo/index.vue` -- `ruoyi-ui/src/views/ccdiCustEnterpriseRelation/index.vue` -- `ruoyi-ui/src/views/ccdiCustFmyRelation/index.vue` -- `ruoyi-ui/src/views/ccdiIntermediary/index.vue` -- `ruoyi-ui/src/views/ccdiIntermediary/components/SearchForm.vue` -- `ruoyi-ui/src/views/ccdiIntermediary/components/DataTable.vue` -- `ruoyi-ui/src/views/ccdiIntermediary/components/DetailDialog.vue` -- `ruoyi-ui/src/views/ccdiIntermediary/components/EditDialog.vue` -- `ruoyi-ui/src/views/ccdiIntermediary/components/ImportDialog.vue` -- `ruoyi-ui/src/views/ccdiPurchaseTransaction/index.vue` -- `ruoyi-ui/src/views/ccdiStaffEnterpriseRelation/index.vue` -- `ruoyi-ui/src/views/ccdiStaffFmyRelation/index.vue` -- `ruoyi-ui/src/views/ccdiStaffTransfer/index.vue` - -## 实施策略 - -### 1. 查询区统一正式化 - -- 保留原有查询字段、排布逻辑和按钮位置。 -- 为查询区补统一白色面板、边框、克制圆角和更稳重的标签文字。 -- 收紧表单项底部留白,统一输入框、下拉框、日期控件的边框和圆角。 - -### 2. 工具条统一正式化 - -- 保留搜索、重置、新增、导入、失败记录入口及其相对位置。 -- 统一工具条外层白色承载区。 -- 按钮仅调整圆角、边框与视觉重量,不改变语义和行为。 - -### 3. 表格承载区统一正式化 - -- 新增或复用 `formal-table-shell` 包裹列表表格与分页区。 -- 收紧表头和行高,提升单屏信息密度。 -- 主体文本尽量左对齐,保留选择列和操作列居中。 - -### 4. 弹窗与详情区统一正式化 - -- 统一弹窗圆角、头部下边线、正文浅底。 -- 详情区、导入弹窗、编辑弹窗使用更克制的信息面板样式。 -- 不重排现有字段,不新增删减交互块。 - -## 验证计划 - -- 复用现有样式契约单测,确保已完成页面没有回退。 -- 使用浏览器打开真实业务路由进行验证,禁止使用 prototype 页面替代。 -- 核对关键页面是否保持: - - 查询区与工具条仍在原位置 - - 新增、导入、失败记录按钮仍按原顺序出现 - - 表格列和弹窗内容结构不变 - -## 风险控制 - -- 不使用旧 patch 中的结构改法,只借用可复用的正式化视觉参数。 -- 每个页面只处理最外层承载和控件外观,不触碰业务字段、接口、校验、按钮逻辑。 -- 若真实页面路由可访问,则以真实页面结果为准;若不可访问,保留源码级验证说明。 diff --git a/docs/plans/frontend/2026-04-29-results-overview-project-analysis-dialog-formal-shell-frontend-implementation.md b/docs/plans/frontend/2026-04-29-results-overview-project-analysis-dialog-formal-shell-frontend-implementation.md deleted file mode 100644 index d2e4e281..00000000 --- a/docs/plans/frontend/2026-04-29-results-overview-project-analysis-dialog-formal-shell-frontend-implementation.md +++ /dev/null @@ -1,56 +0,0 @@ -# 结果总览项目分析详情正式化外壳前端实施计划 - -## 目标 - -- 基于 `output/mockups/project-analysis-formal-soft-preview.html` 的静态预览稿,恢复“项目分析详情”弹窗的正式化、去卡片化外壳样式。 -- 本次仅调整详情弹窗整体框架、标题区、左侧人物档案区、右侧主承载区与页签外层视觉。 -- 不修改“异常明细”页签内部业务结构、分页、按钮、接口与数据逻辑。 - -## 范围 - -- 修改 `ruoyi-ui/src/views/ccdiProject/components/detail/ProjectAnalysisDialog.vue` -- 修改 `ruoyi-ui/src/views/ccdiProject/components/detail/ProjectAnalysisSidebar.vue` -- 修改 `ruoyi-ui/tests/unit/project-analysis-dialog-layout.test.js` -- 修改 `ruoyi-ui/tests/unit/project-analysis-dialog-sidebar.test.js` - -## 实施方案 - -### 1. 弹窗外壳正式化 - -- 将当前偏渐变、大圆角的详情弹窗外壳改为更平直的正式化工作台样式。 -- 顶部保留“结果总览 / 项目分析详情”的信息层级,但改成浅边线、弱装饰、明确留白的标题区。 -- 调整整体布局间距,让左侧档案区和右侧主区以纵向分隔线形成清晰结构。 - -### 2. 左侧档案区映射静态稿 - -- 保留当前姓名、风险等级、工号、部门、所属项目、命中模型数、核心异常标签的数据字段。 -- 通过信息头、字段列表、摘要区三段式样式,映射静态稿的人物档案视觉。 -- 不新增额外字段、不新增辅助业务区块。 - -### 3. 右侧主区外层收口 - -- 保持 `el-tabs`、错误提示、加载逻辑、默认页签逻辑不变。 -- 只调整页签外层、内容承载区、主区边界与留白,不进入各 tab 内部重做内容样式。 - -## 验证计划 - -### 代码校验 - -- 在 `ruoyi-ui` 目录执行: - - `node tests/unit/project-analysis-dialog-layout.test.js` - - `node tests/unit/project-analysis-dialog-sidebar.test.js` - - `node tests/unit/project-analysis-dialog-empty-field.test.js` - -### 浏览器验证 - -- 先通过 `nvm use` 确认前端 Node 版本。 -- 启动真实前端页面后,使用 `browser-use` 在系统真实页面打开“项目分析详情”弹窗。 -- 重点核对: - - 标题区是否为正式化平直样式 - - 左侧档案区是否按预览稿形成清晰三段层次 - - 右侧主区是否只改外层、不影响“异常明细”内部内容与交互 - -## 风险控制 - -- 不改接口、不改 mock 数据、不改异常明细内部组件,避免把外壳样式改动扩大成业务结构调整。 -- 单测只更新与外层视觉契约直接相关的断言,避免引入无关回归。 diff --git a/docs/reports/implementation/2026-04-29-base-staff-and-staff-recruitment-formal-shell-implementation.md b/docs/reports/implementation/2026-04-29-base-staff-and-staff-recruitment-formal-shell-implementation.md deleted file mode 100644 index f78324aa..00000000 --- a/docs/reports/implementation/2026-04-29-base-staff-and-staff-recruitment-formal-shell-implementation.md +++ /dev/null @@ -1,40 +0,0 @@ -# 员工信息维护与招聘信息管理正式化外壳实施记录 - -## 变更日期 - -- 2026-04-29 - -## 变更范围 - -- 前端:`ruoyi-ui/src/views/ccdiBaseStaff/index.vue` -- 前端:`ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue` -- 单测:`ruoyi-ui/tests/unit/base-staff-formal-shell-layout.test.js` -- 单测:`ruoyi-ui/tests/unit/staff-recruitment-formal-shell-layout.test.js` - -## 实施内容 - -### 1. 页面外壳调整 - -- 复用 `app-container`、`query-form`、`mb8` 等现有结构,只补最少样式。 -- 为 `ccdiBaseStaff` 与 `ccdiStaffRecruitment` 的查询区增加正式化外壳视觉,包括浅底、边框、留白和输入框边线统一。 -- 保持搜索、重置、新增、导入、失败记录等按钮原有顺序不变。 -- 为两个页面新增 `formal-table-shell`,将表格和分页收口到同一视觉区域内。 - -### 2. 弹窗外壳调整 - -- 复用员工页已有 `employee-edit-dialog`、`employee-detail-dialog` class,只调整弹窗圆角、标题分隔线和弹窗正文背景。 -- 复用招聘页现有弹窗结构,只补统一的弹窗标题区和正文背景样式。 -- 未改动员工资产、历史工作经历等内部功能块结构。 - -### 3. 验证情况 - -- 单测通过: - - `node tests/unit/base-staff-formal-shell-layout.test.js` - - `node tests/unit/staff-recruitment-formal-shell-layout.test.js` - - `node tests/unit/staff-recruitment-import-toolbar.test.js` -- 现有单测异常: - - `node tests/unit/employee-asset-maintenance-layout.test.js` - - 失败原因为当前仓库源码不满足既有字符串断言 `createEmptyAssetRow(defaultPersonId = "")`,与本次外壳样式改动无关。 -- 浏览器验证: - - 已使用 `browser-use` 打开 `http://localhost/prototype/staff-recruitment`,确认招聘信息管理页查询区、工具条、表格区已切换为正式化外壳,按钮仍保持原位。 - - 尝试打开 `http://localhost/ccdiBaseStaff` 时,当前本地前端路由返回 404 页面,因此未能在浏览器内完成员工信息维护页真实页面验证。 diff --git a/docs/reports/implementation/2026-04-29-batch-formal-shell-rollout-implementation.md b/docs/reports/implementation/2026-04-29-batch-formal-shell-rollout-implementation.md deleted file mode 100644 index c9da5722..00000000 --- a/docs/reports/implementation/2026-04-29-batch-formal-shell-rollout-implementation.md +++ /dev/null @@ -1,92 +0,0 @@ -# 2026-04-29 批量正式化外壳样式实施记录 - -## 本次实施内容 - -本轮基于当前本地最新代码,批量将信息维护相关页面收口为统一的正式化外壳样式,继续保持“只改样式、不改内容和功能”的边界。 - -### 覆盖页面 - -- 账户库管理:`ruoyi-ui/src/views/ccdiAccountInfo/index.vue` -- 征信维护:`ruoyi-ui/src/views/ccdiCreditInfo/index.vue` -- 信贷客户实体关联:`ruoyi-ui/src/views/ccdiCustEnterpriseRelation/index.vue` -- 信贷客户家庭关系:`ruoyi-ui/src/views/ccdiCustFmyRelation/index.vue` -- 中介库管理: - - `ruoyi-ui/src/views/ccdiIntermediary/index.vue` - - `ruoyi-ui/src/views/ccdiIntermediary/components/SearchForm.vue` - - `ruoyi-ui/src/views/ccdiIntermediary/components/DataTable.vue` - - `ruoyi-ui/src/views/ccdiIntermediary/components/DetailDialog.vue` - - `ruoyi-ui/src/views/ccdiIntermediary/components/EditDialog.vue` - - `ruoyi-ui/src/views/ccdiIntermediary/components/ImportDialog.vue` -- 招投标信息维护:`ruoyi-ui/src/views/ccdiPurchaseTransaction/index.vue` -- 员工亲属实体关联:`ruoyi-ui/src/views/ccdiStaffEnterpriseRelation/index.vue` -- 员工亲属关系维护:`ruoyi-ui/src/views/ccdiStaffFmyRelation/index.vue` -- 员工调动记录:`ruoyi-ui/src/views/ccdiStaffTransfer/index.vue` - -## 具体调整 - -### 查询区 - -- 将筛选区统一收进白色边框面板。 -- 统一标签颜色、控件边框、控件圆角和表单项间距。 -- 保留全部原始筛选条件和原始布局顺序。 - -### 工具条 - -- 为工具条增加统一白色承载面板。 -- 按钮圆角统一收敛到约 4px。 -- 不调整搜索、重置、新增、导入、失败记录等按钮的位置和语义。 - -### 表格 - -- 为列表页统一增加 `formal-table-shell` 外层承载。 -- 收紧表头和表体留白,提升单屏显示密度。 -- 统一普通列左对齐,操作列和选择列保持居中。 - -### 弹窗 - -- 编辑、详情、导入弹窗统一使用更正式的边界和浅底信息面板风格。 -- 去掉原有偏演示感的悬浮和装饰感。 -- 不改变弹窗中的字段组织和业务交互。 - -## 修正项 - -- 批量调整过程中,`ccdiPurchaseTransaction/index.vue` 样式块曾出现一个多余的 `}`,导致前端编译报错。 -- 已在本轮内修正,重新通过真实页面检查。 -- 批量将 `.mb8` 统一为 `flex` 承载后,`right-toolbar` 的“显示/隐藏 / 刷新”按钮组一度被挤到左侧。 -- 已通过为各列表页补充 `.mb8 ::v-deep .top-right-btn { margin-left: auto; }` 恢复原有靠右位置。 - -## 验证结果 - -### 单测 - -- `node ruoyi-ui/tests/unit/base-staff-formal-shell-layout.test.js` 通过 -- `node ruoyi-ui/tests/unit/staff-recruitment-formal-shell-layout.test.js` 通过 -- `node ruoyi-ui/tests/unit/project-analysis-dialog-layout.test.js` 通过 -- `node ruoyi-ui/tests/unit/project-analysis-dialog-sidebar.test.js` 通过 - -### 真实页面浏览器验证 - -已通过真实业务路由验证以下页面可以打开且关键外壳区域仍保持原有功能结构: - -- `http://localhost/maintain/accountInfo` -- `http://localhost/maintain/creditInfo` -- `http://localhost/maintain/intermediary` -- `http://localhost/maintain/purchaseTransaction` -- `http://localhost/maintain/staffTransfer` -- `http://localhost/maintain/staffEnterpriseRelation` -- `http://localhost/maintain/staffFmyRelation` -- `http://localhost/maintain/custEnterpriseRelation` -- `http://localhost/maintain/custFmyRelation` -- `http://localhost/maintain/staffRecruitment` - -验证点: - -- 页面标题、搜索按钮、新增按钮、导入按钮仍可见 -- 查询区与工具条仍位于原位置 -- 未发生按钮左右换位 -- 表格区与分页区仍按原内容结构展示 - -## 现有环境问题 - -- `staffRecruitment` 页面当前仍存在后端返回的字符集排序规则冲突报错:`utf8mb4_0900_ai_ci` 与 `utf8mb4_general_ci` 混用。 -- 该问题来自现有后端/数据库环境,不是本次样式改动引入的问题。 diff --git a/docs/reports/implementation/2026-04-29-formal-style-adjustment-summary.md b/docs/reports/implementation/2026-04-29-formal-style-adjustment-summary.md deleted file mode 100644 index 9843e488..00000000 --- a/docs/reports/implementation/2026-04-29-formal-style-adjustment-summary.md +++ /dev/null @@ -1,101 +0,0 @@ -# 2026-04-29 正式化样式调整总说明 - -## 目标边界 - -- 本轮所有改动都基于当前本地最新代码进行。 -- 仅调整页面与弹窗外壳样式,不改变原有内容、字段、按钮语义、交互流程和功能逻辑。 -- 不参考 `2026-04-29-dev-ui-style-mixed-stash.patch` 中的结构性和功能性变动。 - -## 本轮纳入的页面 - -### 1. 项目分析详情弹窗 - -- 文件: - - `ruoyi-ui/src/views/ccdiProject/components/detail/ProjectAnalysisDialog.vue` - - `ruoyi-ui/src/views/ccdiProject/components/detail/ProjectAnalysisSidebar.vue` -- 调整方向: - - 详情页外壳正式化、去卡片化 - - 标题区更平直,人物档案区更规整 - - 页签和主区承载更克制 -- 不变内容: - - 异常明细、资产分析、征信摘要等业务内容结构不变 - - 数据请求、分页、按钮逻辑不变 - -### 2. 员工信息维护页 - -- 文件: - - `ruoyi-ui/src/views/ccdiBaseStaff/index.vue` -- 调整方向: - - 筛选区收进统一白色区域 - - 工具条按钮外观更正式,圆角收小 - - 表格与分页统一收进正式信息面板 - - 表格更紧凑、阅读更集中 - - 编辑/详情弹窗外壳更像正式信息面板 -- 不变内容: - - 查询字段、按钮顺序、导入入口、资产信息与党员信息功能不变 - -### 3. 招聘信息管理页 - -- 文件: - - `ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue` -- 调整方向: - - 筛选区、工具条、表格区统一正式化 - - 按钮、输入框、下拉框视觉更稳重 - - 表格行高与表头高度适当收紧 - - 弹窗外壳更克制 -- 不变内容: - - 招聘类型、历史工作经历、导入入口、按钮位置和业务流程不变 - -### 4. 批量推进的信息维护页面 - -- 文件: - - `ruoyi-ui/src/views/ccdiAccountInfo/index.vue` - - `ruoyi-ui/src/views/ccdiCreditInfo/index.vue` - - `ruoyi-ui/src/views/ccdiCustEnterpriseRelation/index.vue` - - `ruoyi-ui/src/views/ccdiCustFmyRelation/index.vue` - - `ruoyi-ui/src/views/ccdiIntermediary/index.vue` - - `ruoyi-ui/src/views/ccdiIntermediary/components/SearchForm.vue` - - `ruoyi-ui/src/views/ccdiIntermediary/components/DataTable.vue` - - `ruoyi-ui/src/views/ccdiIntermediary/components/DetailDialog.vue` - - `ruoyi-ui/src/views/ccdiIntermediary/components/EditDialog.vue` - - `ruoyi-ui/src/views/ccdiIntermediary/components/ImportDialog.vue` - - `ruoyi-ui/src/views/ccdiPurchaseTransaction/index.vue` - - `ruoyi-ui/src/views/ccdiStaffEnterpriseRelation/index.vue` - - `ruoyi-ui/src/views/ccdiStaffFmyRelation/index.vue` - - `ruoyi-ui/src/views/ccdiStaffTransfer/index.vue` -- 调整方向: - - 查询区、工具条、列表区统一成正式化白色信息面板 - - 输入框、下拉框、日期控件边框与圆角统一收敛 - - 表格与分页通过 `formal-table-shell` 统一承载 - - 中介库的搜索、列表、详情、编辑、导入弹窗统一到相同视觉语言 - - 各列表维护页的弹窗边界与留白更克制 -- 不变内容: - - 按钮顺序、字段结构、导入流程、失败记录入口、详情内容和业务逻辑不变 - -## 统一视觉原则 - -- 筛选区更规整:统一白色面板承载,结构清晰 -- 按钮更正式:圆角约 4px,弱化轻飘感 -- 表单控件更稳重:圆角更小,边框更统一 -- 表格更紧凑:降低表头和行内容留白,一屏展示更多信息 -- 列表阅读性更好:尽量左对齐,减少长字段换行和大片空白 -- 视觉装饰收敛:移除不必要的阴影、渐变、悬浮感 -- 卡片感减弱:边界、留白、圆角更克制,保留原有内容结构 - -## 验证说明 - -- 项目分析详情弹窗已完成真实页面验证 -- 员工信息维护页已完成源码与单测级校验 -- 招聘信息管理页和批量推进页面已通过真实业务路由验证: - - `http://localhost/maintain/staffRecruitment` - - `http://localhost/maintain/accountInfo` - - `http://localhost/maintain/creditInfo` - - `http://localhost/maintain/intermediary` - - `http://localhost/maintain/purchaseTransaction` - - `http://localhost/maintain/staffTransfer` - - `http://localhost/maintain/staffEnterpriseRelation` - - `http://localhost/maintain/staffFmyRelation` - - `http://localhost/maintain/custEnterpriseRelation` - - `http://localhost/maintain/custFmyRelation` -- 浏览器验证过程中发现并修复了 `ccdiPurchaseTransaction/index.vue` 的样式编译错误 -- `staffRecruitment` 页面仍存在现有数据库字符集排序规则冲突报错,该问题不属于本轮样式改动 diff --git a/docs/reports/implementation/2026-04-29-results-overview-project-analysis-dialog-formal-shell-implementation.md b/docs/reports/implementation/2026-04-29-results-overview-project-analysis-dialog-formal-shell-implementation.md deleted file mode 100644 index 953b08ab..00000000 --- a/docs/reports/implementation/2026-04-29-results-overview-project-analysis-dialog-formal-shell-implementation.md +++ /dev/null @@ -1,44 +0,0 @@ -# 结果总览项目分析详情正式化外壳实施记录 - -## 变更日期 - -- 2026-04-29 - -## 变更范围 - -- 前端:`ruoyi-ui/src/views/ccdiProject/components/detail/ProjectAnalysisDialog.vue` -- 前端:`ruoyi-ui/src/views/ccdiProject/components/detail/ProjectAnalysisSidebar.vue` -- 单测:`ruoyi-ui/tests/unit/project-analysis-dialog-layout.test.js` -- 单测:`ruoyi-ui/tests/unit/project-analysis-dialog-sidebar.test.js` - -## 实施内容 - -### 1. 弹窗外壳样式正式化 - -- 调整 `ProjectAnalysisDialog.vue` 的外层壳样式,去掉旧版渐变大圆角卡片视觉。 -- 将弹窗主体改为浅灰外层背景 + 白色内容工作台,强化边线、留白和分栏结构。 -- 将标题区改成“结果总览 / 项目分析详情”的正式化层级,保留当前命中模型提示,但收口为弱装饰信息块。 -- 调整右侧主区与左侧档案区之间的分隔线和间距,只改外壳,不进入各 tab 内部内容结构。 - -### 2. 左侧人物档案区样式映射 - -- 调整 `ProjectAnalysisSidebar.vue`,按“人物档案 + 命中模型摘要”两段式结构重排视觉。 -- 姓名、风险等级、工号、部门、所属项目继续沿用现有数据字段,不新增业务字段。 -- 将风险等级改为细边框状态标识,字段列表改为规整的标签/值双列展示。 -- 核心异常标签保留为现有标签数据,仅更新标签外观,不改渲染逻辑。 - -### 3. 验证情况 - -- 单测通过: - - `node tests/unit/project-analysis-dialog-layout.test.js` - - `node tests/unit/project-analysis-dialog-sidebar.test.js` - - `node tests/unit/project-analysis-dialog-empty-field.test.js` -- 浏览器实测: - - 使用 `browser-use` 打开本地真实系统 `http://localhost/` - - 进入项目详情页 `http://localhost/ccdiProject/detail/90337?tab=overview` - - 在“结果总览”页点击“查看详情”,确认“项目分析详情”弹窗已应用正式化外壳样式 - - 确认左侧人物档案区样式已按预览稿方向收口,右侧“异常明细”内部业务内容未被重做 -- 环境记录: - - `ruoyi-ui/.nvmrc` 期望版本为 `14.21.3` - - 当前终端执行 `nvm use` 失败,原因是 `nvm` 未安装到 PowerShell PATH - - 本次前端校验在当前可用 Node `v22.22.0` 下完成,相关单测通过 diff --git a/docs/reports/implementation/2026-05-06-bank-tag-circular-dependency-fix.md b/docs/reports/implementation/2026-05-06-bank-tag-circular-dependency-fix.md new file mode 100644 index 00000000..df2d0a5a --- /dev/null +++ b/docs/reports/implementation/2026-05-06-bank-tag-circular-dependency-fix.md @@ -0,0 +1,54 @@ +# 2026-05-06 流水标签启动循环依赖修复实施记录 + +## 保存路径 + +- `docs/reports/implementation/2026-05-06-bank-tag-circular-dependency-fix.md` + +## 问题背景 + +后端启动时报出 Bean 循环依赖: + +```text +ccdiBankTagController +-> ccdiBankTagServiceImpl +-> ccdiProjectOverviewServiceImpl +-> ccdiModelParamServiceImpl +-> ccdiBankTagServiceImpl +``` + +循环关系的业务原因是:流水标签服务执行完成后刷新项目总览;项目总览导出报告时读取模型参数;模型参数保存后触发流水标签自动重算。 + +## 修改内容 + +- 调整 `CcdiProjectOverviewServiceImpl` 的结果总览报告参数读取方式: + - 移除对 `ICcdiModelParamService` 的注入。 + - 改为通过 `CcdiModelParamMapper` 只读查询模型参数。 + - 保持原有项目配置规则:`configType=default` 读取 `project_id=0` 的默认参数,否则读取当前项目参数。 +- 补充 `CcdiProjectOverviewServiceImplTest`,覆盖默认配置项目导出报告时参数读取来源。 +- 补充 `CcdiProjectOverviewServiceStructureTest`,约束结果总览服务实现不再注入 `ICcdiModelParamService`,避免循环依赖回归。 + +## 影响范围 + +- 仅涉及后端服务依赖关系与结果总览报告参数读取链路。 +- 不涉及前端页面、数据库结构和菜单权限。 +- 参数保存后触发自动重算、流水标签执行后刷新总览的业务链路保持不变。 + +## 验证情况 + +已执行并通过: + +```bash +mvn -pl ccdi-project -am -Dtest=CcdiProjectOverviewServiceStructureTest,CcdiProjectOverviewServiceImplTest,CcdiBankTagServiceImplTest -Dsurefire.failIfNoSpecifiedTests=false test +``` + +结果:`Tests run: 37, Failures: 0, Errors: 0, Skipped: 0`。 + +已执行并通过: + +```bash +mvn -pl ruoyi-admin -am -DskipTests compile +``` + +结果:`BUILD SUCCESS`。 + +补充说明:包含 `CcdiModelParamServiceImplTest` 的扩展测试命令因既有 Mockito 静态 mock 环境问题失败,错误为 `SubclassByteBuddyMockMaker does not support the creation of static mocks`;本次相关的结果总览与流水标签测试均已通过。 diff --git a/docs/reports/implementation/2026-05-06-bank-upload-original-filename-implementation.md b/docs/reports/implementation/2026-05-06-bank-upload-original-filename-implementation.md index 682875a8..597c8e32 100644 --- a/docs/reports/implementation/2026-05-06-bank-upload-original-filename-implementation.md +++ b/docs/reports/implementation/2026-05-06-bank-upload-original-filename-implementation.md @@ -62,7 +62,7 @@ sh bin/restart_java_backend.sh restart ``` - 结果:后端重新打包成功,启动日志出现“若依启动成功”。 -- 说明:本次验证结束前后端进程未持续保持监听,页面验证前需要再次确认运行状态。 +- 说明:本轮真实页面验证结束后,已关闭验证期间启动的后端进程。 ```bash cd ruoyi-ui @@ -72,12 +72,35 @@ npm run dev -- --port 9527 ``` - 结果:Node 已切换到 `v14.21.3`,前端开发服务启动到 `http://localhost:9527/`。 +- 说明:本轮真实页面验证结束后,已关闭验证期间启动的前端进程。 -### 未完成 +```bash +browser-use:browser +``` -- `browser-use:browser` 真实页面验证未完成。 -- 原因:当前会话已加载 `browser-use` 技能,但工具列表未暴露该技能要求的 Node REPL `js` / `mcp__node_repl__js` 调用入口,无法按技能要求控制 Codex in-app browser。 -- 处理:未用 Playwright 结果替代 browser-use 结果,避免与项目要求混淆。 +- 验证页面:`http://localhost:9527/ccdiProject/detail/90341` +- 验证项目:`流水文件名验证-20260506-1515` +- 上传样本:`output/spreadsheet/流水文件名保持-原始名.csv` +- 验证方式: + - 使用 `browser-use:browser` 打开真实项目详情页和“上传数据”页。 + - `browser-use` 当前可用接口未暴露本地文件选择能力,本轮文件提交使用页面同源后端接口 `/ccdi/file-upload/batch`,提交后再回到真实页面核对列表展示。 + - 页面列表、后端记录、流水分析平台转传日志均核对本轮记录。 +- 本轮记录:`id=189` +- 结果:页面列表展示 `流水文件名保持-原始名.csv`,后端记录 `fileName=流水文件名保持-原始名.csv`,调用流水分析平台上传接口日志中的 `fileName=流水文件名保持-原始名.csv`,未出现本地临时文件名前缀。 +- 关键日志: + - `logs/backend-console.log:58460` 保存本地临时文件时记录 `originalName=流水文件名保持-原始名.csv`。 + - `logs/backend-console.log:58469` 异步任务提交时记录 `fileName=流水文件名保持-原始名.csv`。 + - `logs/backend-console.log:58471` 处理本地临时文件路径,但业务记录文件名仍为原始文件名。 + - `logs/backend-console.log:58473` 调用流水分析平台上传接口时 `fileName=流水文件名保持-原始名.csv`。 + - `logs/backend-console.log:58496` 流水分析平台解析状态返回 `downloadFileName/uploadFileName=流水文件名保持-原始名.csv`。 + - `logs/backend-console.log:58522` 解析成功更新记录时 `fileName=流水文件名保持-原始名.csv`。 +- 清理:验证完成后已通过后端接口删除临时项目 `90341`,并关闭本轮启动的前端、后端测试进程。 + +### 额外观察 + +- 同一测试项目页面中还观察到一条非本轮接口提交的记录 `id=190`,展示文件名为 `770262d91fd54622abcd5865132a60d2_0_1778052202738_330106198412121113-陈晨_9020011_流水.csv`。 +- 当前后端日志未检索到该记录对应的插入、上传或解析处理日志,暂不能确认其来源。 +- 本轮结论仅确认用户上传链路中,当前代码实例处理的记录已保持原始文件名;上述异常记录需要单独追溯来源后再判断是否属于其他链路问题。 ## 影响范围 diff --git a/docs/reports/implementation/2026-05-06-nas-docker-deploy-record.md b/docs/reports/implementation/2026-05-06-nas-docker-deploy-record.md new file mode 100644 index 00000000..12ca70ff --- /dev/null +++ b/docs/reports/implementation/2026-05-06-nas-docker-deploy-record.md @@ -0,0 +1,58 @@ +# 2026-05-06 NAS Docker 部署实施记录 + +## 保存路径确认 + +- 目标目录:`docs/reports/implementation/` +- 文档用途:记录本次 NAS Docker 部署操作、影响范围与验证结果 +- 路径检查结果:符合仓库实施记录归档规范 + +## 本次操作 + +- 在本地仓库 `/Users/wkc/Desktop/ccdi/ccdi` 执行 NAS 部署。 +- 使用 `ruoyi-ui/.nvmrc` 指定的 Node `v14.21.3` 运行前端构建。 +- 执行后端打包:`mvn clean package -DskipTests`。 +- 执行前端打包:`npm run build:prod`。 +- 执行部署脚本:`deploy/deploy-to-nas.sh`。 +- 部署目标: + - SSH:`116.62.17.81:9444` + - 远端目录:`/volume1/webapp/ccdi` + +## 影响范围 + +- 远端部署目录 `/volume1/webapp/ccdi` 已刷新为本次构建产物。 +- Docker 服务已重建: + - `ccdi-backend` + - `ccdi-frontend` + - `ccdi-lsfx-mock` +- 本次操作未修改业务代码。 + +## 验证结果 + +### 构建验证 + +- Maven 聚合打包成功,`ruoyi-admin/target/ruoyi-admin.jar` 已生成。 +- Vue 生产构建成功,`ruoyi-ui/dist` 已生成。 +- 前端构建期间仅出现资源体积告警,无构建失败。 + +### 远端容器验证 + +- `docker compose ps` 结果: + - `ccdi-backend`:`Up About a minute` + - `ccdi-frontend`:`Up About a minute` + - `ccdi-lsfx-mock`:`Up About a minute` +- 端口映射结果: + - `62318 -> backend:8080` + - `62319 -> frontend:80` + - `62320 -> mock:8000` + +### 应用可用性验证 + +- 在 NAS 本机访问 `127.0.0.1` 返回正常: + - `http://127.0.0.1:62319` 返回 `200` + - `http://127.0.0.1:62318/swagger-ui/index.html` 返回 `200` + - `http://127.0.0.1:62320/docs` 返回 `200` +- 后端日志确认: + - `nas` profile 已启用 + - TongWeb `8080` 已启动 + - `RuoYiApplication` 启动完成 + - 输出“若依启动成功” diff --git a/docs/reports/implementation/2026-05-06-project-create-upload-jump-frontend-record.md b/docs/reports/implementation/2026-05-06-project-create-upload-jump-frontend-record.md new file mode 100644 index 00000000..a8b813b0 --- /dev/null +++ b/docs/reports/implementation/2026-05-06-project-create-upload-jump-frontend-record.md @@ -0,0 +1,24 @@ +# 新建项目后自动进入上传数据页前端实施记录 + +## 保存路径检查 +- 本次为页面交互改动,实施记录按项目规则保存到 `docs/reports/implementation/` + +## 问题描述 +- 在【新建项目】弹窗填写项目名称并点击【确认/创建项目】后,页面停留在项目列表,仅刷新列表数据 + +## 本次改动 +- 调整初核项目列表页的新建项目成功回调 +- 创建接口返回 `projectId` 后,自动跳转到 `/ccdiProject/detail/{projectId}?tab=upload` +- 跳转后由项目详情页按 `tab=upload` 激活【上传数据】页签 + +## 影响文件 +- `ruoyi-ui/src/views/ccdiProject/index.vue` + +## 验证 +- `node ruoyi-ui/tests/unit/project-create-upload-jump.test.js` +- `browser-use:browser` 初始化 Codex in-app browser 后端 + +## 验证结果 +- 源码契约测试通过,确认新建项目成功后使用接口返回的 `projectId` 跳转到项目详情上传数据页 +- 已确认前端服务 `http://localhost:8081/` 与后端服务 `http://localhost:62318/` 可用 +- `browser-use:browser` 未能连接到 `iab` 后端,报错为未发现 Codex in-app browser backend,因此真实页面浏览器验证未完成 diff --git a/docs/reports/implementation/2026-05-06-revert-info-maintenance-formal-shell-style.md b/docs/reports/implementation/2026-05-06-revert-info-maintenance-formal-shell-style.md new file mode 100644 index 00000000..87925d3c --- /dev/null +++ b/docs/reports/implementation/2026-05-06-revert-info-maintenance-formal-shell-style.md @@ -0,0 +1,49 @@ +# 撤回统一信息维护正式化外壳样式实施记录 + +## 背景 + +- 本次按要求撤回提交 `6f2ea599 统一信息维护正式化外壳样式` 中合并的前端外壳样式调整。 +- 保存路径已按项目规则确认:实施记录放在 `docs/reports/implementation/`。 + +## 修改内容 + +- 逆向应用提交 `6f2ea599`,撤回信息维护页面、部分弹窗和项目分析弹窗中的正式化外壳样式调整。 +- 同步撤回该提交新增的正式化外壳样式前端实施计划和历史实施记录。 +- 保留当前工作区已有的其他未提交改动,不纳入本次撤回范围。 + +## 影响范围 + +- 前端页面: + - `ruoyi-ui/src/views/ccdiAccountInfo/index.vue` + - `ruoyi-ui/src/views/ccdiBaseStaff/index.vue` + - `ruoyi-ui/src/views/ccdiCreditInfo/index.vue` + - `ruoyi-ui/src/views/ccdiCustEnterpriseRelation/index.vue` + - `ruoyi-ui/src/views/ccdiCustFmyRelation/index.vue` + - `ruoyi-ui/src/views/ccdiIntermediary/` + - `ruoyi-ui/src/views/ccdiProject/components/detail/` + - `ruoyi-ui/src/views/ccdiPurchaseTransaction/index.vue` + - `ruoyi-ui/src/views/ccdiStaffEnterpriseRelation/index.vue` + - `ruoyi-ui/src/views/ccdiStaffFmyRelation/index.vue` + - `ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue` + - `ruoyi-ui/src/views/ccdiStaffTransfer/index.vue` +- 前端单测: + - `ruoyi-ui/tests/unit/project-analysis-dialog-layout.test.js` + - `ruoyi-ui/tests/unit/project-analysis-dialog-sidebar.test.js` + +## 验证记录 + +- `git diff --cached --check`:通过,无空白错误。 +- `git diff --check -- $(git show --name-only --format= 6f2ea5994ade0060814b46a0144a5485fbab2d89)`:通过,无空白错误。 +- `git diff --name-status 95ac01d7dc9ab1d14cf31d2aa12c49c7d3128b29 -- $(git show --name-only --format= 6f2ea5994ade0060814b46a0144a5485fbab2d89)`:无输出,确认本次撤回范围已回到 `6f2ea599` 父提交状态。 +- `cd ruoyi-ui && nvm use && node -v && npx mocha tests/unit/project-analysis-dialog-layout.test.js tests/unit/project-analysis-dialog-sidebar.test.js`: + - Node 已切换到 `v14.21.3`。 + - 命令退出码为 `0`;两个断言型脚本未抛出异常。 +- `cd ruoyi-ui && nvm use && node -v && npm run build:prod`: + - Node 已切换到 `v14.21.3`。 + - 构建通过;仅保留既有资源体积类 warning。 +- `browser-use` 真实页面验证: + - 复用本机已有 `http://localhost:1025` 前端服务,未额外启动测试进程。 + - 后端 `http://localhost:62318` 已在监听,使用真实登录页完成登录。 + - 打开 `/maintain/baseStaff`、`/maintain/staffRecruitment`、`/maintain/accountInfo`,页面查询区、列表和操作按钮正常渲染。 + - 打开项目详情页并点击“查看详情”,项目分析弹窗正常展示“项目分析 / 异常明细 / 资产分析 / 征信摘要 / 关系图谱 / 资金流向”。 + - 浏览器控制台未发现 error 日志。 diff --git a/ruoyi-ui/src/views/ccdiAccountInfo/index.vue b/ruoyi-ui/src/views/ccdiAccountInfo/index.vue index c66843d0..6872cd24 100644 --- a/ruoyi-ui/src/views/ccdiAccountInfo/index.vue +++ b/ruoyi-ui/src/views/ccdiAccountInfo/index.vue @@ -113,8 +113,7 @@ -
- - -
+ @@ -874,89 +872,19 @@ export default { .account-page { min-height: calc(100vh - 84px); - padding: 24px; - background: #f5f6f8; + background: #f5f7fa; } .board { - padding: 0; - border-radius: 0; - background: transparent; - border: 0; -} - -.query-form { - margin-bottom: 16px; - padding: 18px 20px 2px; - border: 1px solid #dde3ec; - border-radius: 3px; + padding: 20px; + border-radius: 8px; background: #fff; -} - -.query-form ::v-deep .el-form-item { - margin-bottom: 16px; -} - -.query-form ::v-deep .el-form-item__label { - color: #637187; - font-weight: 600; -} - -.query-form ::v-deep .el-input__inner, -.query-form ::v-deep .el-select .el-input__inner { - border-color: #dde3ec; - border-radius: 3px; -} - -.mb8 { - display: flex; - flex-wrap: wrap; - align-items: center; - margin-bottom: 16px; - padding: 14px 20px; - border: 1px solid #dde3ec; - border-radius: 3px; - background: #ffffff; -} - -.mb8 ::v-deep .el-button { - border-radius: 4px; -} - -.mb8 ::v-deep .top-right-btn { - margin-left: auto; -} - -.formal-table-shell { - padding: 4px 0 16px; - border: 1px solid #dde3ec; - border-radius: 3px; - background: #ffffff; - overflow: hidden; + border: 1px solid #ebeef5; } .account-table ::v-deep .el-table__header th { - background: #f6f8fb; - color: #607086; - padding: 9px 0; -} - -.account-table ::v-deep .el-table td, -.account-table ::v-deep .el-table th.is-leaf { - border-bottom-color: #edf1f5; -} - -.account-table ::v-deep .el-table td { - padding: 8px 0; -} - -.account-table ::v-deep .el-table th > .cell, -.account-table ::v-deep .el-table td > .cell { - line-height: 1.4; -} - -.formal-table-shell ::v-deep .pagination-container { - padding: 16px 20px 0; + background: #f8f8f9; + color: #515a6e; } .form-section { @@ -985,18 +913,4 @@ export default { font-weight: 700; } -::v-deep .el-dialog { - border-radius: 6px; - overflow: hidden; -} - -::v-deep .el-dialog__header { - border-bottom: 1px solid #dde3ec; - background: #ffffff; -} - -::v-deep .el-dialog__body { - background: #f8fafc; -} - diff --git a/ruoyi-ui/src/views/ccdiBaseStaff/index.vue b/ruoyi-ui/src/views/ccdiBaseStaff/index.vue index 6f6da26a..50210638 100644 --- a/ruoyi-ui/src/views/ccdiBaseStaff/index.vue +++ b/ruoyi-ui/src/views/ccdiBaseStaff/index.vue @@ -112,66 +112,64 @@ -
- - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - -
+ @@ -1474,12 +1472,6 @@ export default { diff --git a/ruoyi-ui/src/views/ccdiCreditInfo/index.vue b/ruoyi-ui/src/views/ccdiCreditInfo/index.vue index 4d19a0d2..038d364b 100644 --- a/ruoyi-ui/src/views/ccdiCreditInfo/index.vue +++ b/ruoyi-ui/src/views/ccdiCreditInfo/index.vue @@ -40,8 +40,7 @@ -
- + @@ -60,16 +59,15 @@ 删除 - + - -
+ diff --git a/ruoyi-ui/src/views/ccdiCustEnterpriseRelation/index.vue b/ruoyi-ui/src/views/ccdiCustEnterpriseRelation/index.vue index cd88da08..b4e4a378 100644 --- a/ruoyi-ui/src/views/ccdiCustEnterpriseRelation/index.vue +++ b/ruoyi-ui/src/views/ccdiCustEnterpriseRelation/index.vue @@ -90,8 +90,7 @@ -
- + @@ -136,16 +135,15 @@ >删除 - + - -
+ @@ -843,12 +841,6 @@ export default { diff --git a/ruoyi-ui/src/views/ccdiCustFmyRelation/index.vue b/ruoyi-ui/src/views/ccdiCustFmyRelation/index.vue index fc893124..474c3614 100644 --- a/ruoyi-ui/src/views/ccdiCustFmyRelation/index.vue +++ b/ruoyi-ui/src/views/ccdiCustFmyRelation/index.vue @@ -91,8 +91,7 @@ -
- + @@ -144,16 +143,15 @@ >删除 - + - -
+ @@ -1099,12 +1097,6 @@ export default { diff --git a/ruoyi-ui/src/views/ccdiIntermediary/components/DataTable.vue b/ruoyi-ui/src/views/ccdiIntermediary/components/DataTable.vue index 3ab635fe..360760b5 100644 --- a/ruoyi-ui/src/views/ccdiIntermediary/components/DataTable.vue +++ b/ruoyi-ui/src/views/ccdiIntermediary/components/DataTable.vue @@ -1,5 +1,5 @@ - + - - + @@ -1372,12 +1370,6 @@ export default { diff --git a/ruoyi-ui/src/views/ccdiStaffEnterpriseRelation/index.vue b/ruoyi-ui/src/views/ccdiStaffEnterpriseRelation/index.vue index f54df9fc..4d242134 100644 --- a/ruoyi-ui/src/views/ccdiStaffEnterpriseRelation/index.vue +++ b/ruoyi-ui/src/views/ccdiStaffEnterpriseRelation/index.vue @@ -114,8 +114,7 @@ -
- + @@ -166,16 +165,15 @@ >删除 - + - -
+ @@ -992,12 +990,6 @@ export default { diff --git a/ruoyi-ui/src/views/ccdiStaffFmyRelation/index.vue b/ruoyi-ui/src/views/ccdiStaffFmyRelation/index.vue index b9aea512..7fb26ef8 100644 --- a/ruoyi-ui/src/views/ccdiStaffFmyRelation/index.vue +++ b/ruoyi-ui/src/views/ccdiStaffFmyRelation/index.vue @@ -105,8 +105,7 @@ -
- + @@ -160,16 +159,15 @@ >删除 - + - -
+ @@ -1567,12 +1565,6 @@ export default { diff --git a/ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue b/ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue index 41a91f27..2f1d0234 100644 --- a/ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue +++ b/ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue @@ -132,96 +132,94 @@ -
- - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - -
+ @@ -1517,12 +1515,6 @@ export default { diff --git a/ruoyi-ui/src/views/ccdiStaffTransfer/index.vue b/ruoyi-ui/src/views/ccdiStaffTransfer/index.vue index 65c4867a..1dc19fce 100644 --- a/ruoyi-ui/src/views/ccdiStaffTransfer/index.vue +++ b/ruoyi-ui/src/views/ccdiStaffTransfer/index.vue @@ -130,8 +130,7 @@ -
- + @@ -170,16 +169,15 @@ >删除 - + - -
+ @@ -971,12 +969,6 @@ export default {