diff --git a/.DS_Store b/.DS_Store index 349a95d0..183ecf64 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/assets/~$模型信息.xlsx b/assets/~$模型信息.xlsx deleted file mode 100644 index 3c3a7940..00000000 Binary files a/assets/~$模型信息.xlsx and /dev/null differ diff --git a/assets/征信解析/HTML引擎服务_ 接口设计说明书_1.docx b/assets/征信解析/HTML引擎服务_ 接口设计说明书_1.docx new file mode 100644 index 00000000..cbbb71f3 Binary files /dev/null and b/assets/征信解析/HTML引擎服务_ 接口设计说明书_1.docx differ diff --git a/assets/征信解析/征信解析接口payload.xlsx b/assets/征信解析/征信解析接口payload.xlsx new file mode 100644 index 00000000..3054088d Binary files /dev/null and b/assets/征信解析/征信解析接口payload.xlsx differ diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectRiskHitTagVO.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectRiskHitTagVO.java index 049877f5..b0e2ea60 100644 --- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectRiskHitTagVO.java +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectRiskHitTagVO.java @@ -8,6 +8,10 @@ import lombok.Data; @Data public class CcdiProjectRiskHitTagVO { + private String modelCode; + + private String modelName; + private String ruleCode; private String ruleName; diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectRiskPeopleOverviewItemVO.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectRiskPeopleOverviewItemVO.java index ea0f6746..e3120fe6 100644 --- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectRiskPeopleOverviewItemVO.java +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectRiskPeopleOverviewItemVO.java @@ -1,5 +1,6 @@ package com.ruoyi.ccdi.project.domain.vo; +import java.util.List; import lombok.Data; /** @@ -24,5 +25,7 @@ public class CcdiProjectRiskPeopleOverviewItemVO { private String riskPoint; + private List riskPointTagList; + private String actionLabel; } diff --git a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapper.java b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapper.java index 19a3bbb9..5aa9bc38 100644 --- a/ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapper.java +++ b/ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapper.java @@ -5,6 +5,7 @@ import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskModelPeopleQueryDTO; import com.ruoyi.ccdi.project.domain.CcdiProject; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeRiskAggregateVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelCardVO; +import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskHitTagVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelPeopleItemVO; import java.util.List; import java.util.Map; @@ -61,6 +62,20 @@ public interface CcdiProjectOverviewMapper { @Param("query") CcdiProjectRiskModelPeopleQueryDTO query ); + /** + * 按员工范围查询命中标签 + * + * @param projectId 项目ID + * @param staffIdCard 员工身份证号 + * @param selectedModelCodes 已选模型编码CSV,可为空 + * @return 命中标签列表 + */ + List selectRiskHitTagsByScope( + @Param("projectId") Long projectId, + @Param("staffIdCard") String staffIdCard, + @Param("selectedModelCodes") String selectedModelCodes + ); + /** * 查询项目风险人数汇总 * 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 0f6e935e..521242d9 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 @@ -78,7 +78,7 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi List overviewList = overviewMapper.selectRiskPeopleOverviewByProjectId(projectId) .stream() - .map(this::buildRiskPeopleItem) + .map(aggregate -> buildRiskPeopleItem(projectId, aggregate)) .toList(); CcdiProjectRiskPeopleOverviewVO overview = new CcdiProjectRiskPeopleOverviewVO(); @@ -168,7 +168,7 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi ); } - private CcdiProjectRiskPeopleOverviewItemVO buildRiskPeopleItem(CcdiProjectEmployeeRiskAggregateVO aggregate) { + private CcdiProjectRiskPeopleOverviewItemVO buildRiskPeopleItem(Long projectId, CcdiProjectEmployeeRiskAggregateVO aggregate) { CcdiProjectRiskPeopleOverviewItemVO item = new CcdiProjectRiskPeopleOverviewItemVO(); item.setName(aggregate.getStaffName()); item.setIdNo(aggregate.getStaffIdCard()); @@ -178,6 +178,9 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi item.setRiskLevelType(resolveRiskLevelType(aggregate.getRiskLevelCode())); item.setModelCount(defaultZero(aggregate.getModelCount())); item.setRiskPoint(aggregate.getRiskPoint()); + item.setRiskPointTagList(defaultList( + overviewMapper.selectRiskHitTagsByScope(projectId, aggregate.getStaffIdCard(), null) + )); item.setActionLabel(ACTION_LABEL); return item; } diff --git a/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml b/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml index 57b631ee..b2b8e3d3 100644 --- a/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml +++ b/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml @@ -347,12 +347,6 @@ ) idx on idx.idx < json_length(result.model_hit_summary_json) where result.project_id = #{projectId} and result.staff_id_card = #{staffIdCard} - - and find_in_set( - json_unquote(json_extract(result.model_hit_summary_json, concat('$[', idx.idx, '].modelCode'))), - #{selectedModelCodes} - ) - group by json_unquote(json_extract(result.model_hit_summary_json, concat('$[', idx.idx, '].modelCode'))), json_unquote(json_extract(result.model_hit_summary_json, concat('$[', idx.idx, '].modelName'))) @@ -361,6 +355,8 @@ diff --git a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerTest.java b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerTest.java index e9562045..1c17ec73 100644 --- a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerTest.java +++ b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerTest.java @@ -1,6 +1,7 @@ package com.ruoyi.ccdi.project.controller; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewDashboardVO; +import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskHitTagVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskPeopleOverviewItemVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskPeopleOverviewVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectTopRiskPeopleVO; @@ -62,6 +63,11 @@ class CcdiProjectOverviewControllerTest { item.setRiskLevel("中风险"); item.setRiskLevelType("warning"); item.setModelCount(4); + CcdiProjectRiskHitTagVO riskPointTag = new CcdiProjectRiskHitTagVO(); + riskPointTag.setModelCode("SALARY"); + riskPointTag.setModelName("产薪异常模型"); + riskPointTag.setRuleName("多工资转入"); + item.setRiskPointTagList(List.of(riskPointTag)); CcdiProjectRiskPeopleOverviewVO overview = new CcdiProjectRiskPeopleOverviewVO(); overview.setOverviewList(List.of(item)); when(overviewService.getRiskPeopleOverview(40L)).thenReturn(overview); @@ -73,6 +79,7 @@ class CcdiProjectOverviewControllerTest { assertEquals("中风险", data.getOverviewList().getFirst().getRiskLevel()); assertEquals("warning", data.getOverviewList().getFirst().getRiskLevelType()); assertEquals(4, data.getOverviewList().getFirst().getModelCount()); + assertEquals("SALARY", data.getOverviewList().getFirst().getRiskPointTagList().getFirst().getModelCode()); verify(overviewService).getRiskPeopleOverview(40L); Method method = CcdiProjectOverviewController.class.getMethod("getRiskPeople", Long.class); diff --git a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapperRiskModelPeopleTest.java b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapperRiskModelPeopleTest.java index 46fec559..346a9f3c 100644 --- a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapperRiskModelPeopleTest.java +++ b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapperRiskModelPeopleTest.java @@ -48,5 +48,8 @@ class CcdiProjectOverviewMapperRiskModelPeopleTest { assertTrue(xml.contains(".modelName")); assertTrue(xml.contains(".ruleCode")); assertTrue(xml.contains(".riskLevel")); + assertTrue(xml.contains("as model_code")); + assertTrue(xml.contains("as model_name")); + assertTrue(xml.contains("group by json_unquote(json_extract(result.hit_rules_json, concat('$[', idx.idx, '].modelCode')))")); } } 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 7fcffda6..fd982a63 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 @@ -9,6 +9,7 @@ import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewDashboardVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewEmployeeHitRowVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelCardVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelCardsVO; +import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskHitTagVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelPeopleItemVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelPeopleVO; import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskPeopleOverviewVO; @@ -86,6 +87,10 @@ class CcdiProjectOverviewServiceImplTest { aggregate.setModelCount(3); aggregate.setRiskPoint("大额单笔收入、疑似兼职"); when(overviewMapper.selectRiskPeopleOverviewByProjectId(40L)).thenReturn(List.of(aggregate)); + when(overviewMapper.selectRiskHitTagsByScope(40L, "330000000000000001", null)).thenReturn(List.of( + buildHitTag("LARGE_TRANSACTION", "大额交易模型", "RULE_A", "大额单笔收入", "HIGH"), + buildHitTag("PART_TIME", "兼职取酬模型", "RULE_B", "疑似兼职", "MEDIUM") + )); CcdiProjectRiskPeopleOverviewVO overview = service.getRiskPeopleOverview(40L); @@ -94,6 +99,9 @@ class CcdiProjectOverviewServiceImplTest { assertEquals("高风险", overview.getOverviewList().getFirst().getRiskLevel()); assertEquals("danger", overview.getOverviewList().getFirst().getRiskLevelType()); assertEquals(3, overview.getOverviewList().getFirst().getModelCount()); + assertEquals(2, overview.getOverviewList().getFirst().getRiskPointTagList().size()); + assertEquals("LARGE_TRANSACTION", overview.getOverviewList().getFirst().getRiskPointTagList().getFirst().getModelCode()); + assertEquals("大额交易模型", overview.getOverviewList().getFirst().getRiskPointTagList().getFirst().getModelName()); assertEquals("大额单笔收入、疑似兼职", overview.getOverviewList().getFirst().getRiskPoint()); assertEquals("查看详情", overview.getOverviewList().getFirst().getActionLabel()); } @@ -279,4 +287,20 @@ class CcdiProjectOverviewServiceImplTest { result.setRiskLevelCode(riskLevelCode); return result; } + + private CcdiProjectRiskHitTagVO buildHitTag( + String modelCode, + String modelName, + String ruleCode, + String ruleName, + String riskLevel + ) { + CcdiProjectRiskHitTagVO hitTag = new CcdiProjectRiskHitTagVO(); + hitTag.setModelCode(modelCode); + hitTag.setModelName(modelName); + hitTag.setRuleCode(ruleCode); + hitTag.setRuleName(ruleName); + hitTag.setRiskLevel(riskLevel); + return hitTag; + } } diff --git a/docs/plans/backend/2026-03-23-results-overview-hit-tag-model-color-backend-implementation.md b/docs/plans/backend/2026-03-23-results-overview-hit-tag-model-color-backend-implementation.md new file mode 100644 index 00000000..cf3594c2 --- /dev/null +++ b/docs/plans/backend/2026-03-23-results-overview-hit-tag-model-color-backend-implementation.md @@ -0,0 +1,124 @@ +# Results Overview Hit Tag Model Color Backend Implementation Plan + +> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 为项目总览页“核心异常点”和“异常标签”补充标签所属模型字段,支撑前端按选中模型高亮标签颜色。 + +**Architecture:** 保持现有结果总览接口与查询入口不变,仅在标签 VO、Mapper XML 和服务映射链路中补充 `modelCode`、`modelName` 字段。风险人员总览、命中模型涉及人员、流水详情统一复用同一模型归属信息,不新增兼容接口、不改动统计口径。 + +**Tech Stack:** Java 21, Spring Boot 3, MyBatis XML, Maven, JUnit 5, Mockito + +--- + +### Task 1: 先锁定后端标签模型字段契约 + +**Files:** +- Modify: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImplTest.java` +- Modify: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerTest.java` +- Modify: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapperRiskModelPeopleTest.java` + +- [ ] **Step 1: Write the failing test** + +补充断言,锁定以下预期: + +- 风险模型人员列表返回的 `hitTagList` 项包含 `modelCode` +- 风险人员总览返回的核心异常点标签项包含模型字段 +- Mapper XML 的标签查询明确从 JSON 中提取 `modelCode`、`modelName` + +- [ ] **Step 2: Run test to verify it fails** + +Run: + +```bash +mvn test -pl ccdi-project -Dtest=CcdiProjectOverviewServiceImplTest,CcdiProjectOverviewControllerTest,CcdiProjectOverviewMapperRiskModelPeopleTest +``` + +Expected: + +- `FAIL` +- 原因是当前标签 VO 与 SQL 尚未暴露模型字段 + +- [ ] **Step 3: Commit the test expectation update** + +```bash +git add ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImplTest.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerTest.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapperRiskModelPeopleTest.java +git commit -m "锁定结果总览标签模型字段测试" +``` + +### Task 2: 扩展标签 VO 与 Mapper 提取字段 + +**Files:** +- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectRiskHitTagVO.java` +- Modify: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiBankStatementHitTagVO.java` +- Modify: `ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml` + +- [ ] **Step 1: Extend the tag VOs** + +为两个标签 VO 均新增: + +- `private String modelCode;` +- `private String modelName;` + +不要新增无关字段,也不要调整现有字段命名。 + +- [ ] **Step 2: Update tag SQL extraction** + +在 `CcdiProjectOverviewMapper.xml` 中: + +- `selectRiskHitTagsByScope` 增加 `model_code`、`model_name` +- 风险人员总览核心异常点对应查询同步返回模型字段 +- 保持现有按 `selectedModelCodes` 过滤逻辑不变 + +- [ ] **Step 3: Run focused backend tests** + +Run: + +```bash +mvn test -pl ccdi-project -Dtest=CcdiProjectOverviewServiceImplTest,CcdiProjectOverviewMapperRiskModelPeopleTest +``` + +Expected: + +- `PASS` + +- [ ] **Step 4: Review interface boundary** + +人工确认: + +- 未新增接口路径 +- 未修改查询入参 +- 未变更风险等级与人数统计逻辑 + +- [ ] **Step 5: Commit** + +```bash +git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectRiskHitTagVO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiBankStatementHitTagVO.java ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml +git commit -m "补充结果总览标签模型字段" +``` + +### Task 3: 回归验证后端结果总览链路 + +**Files:** +- Verify: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerTest.java` +- Verify: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapperSqlTest.java` +- Verify: `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/CcdiProjectOverviewServiceStructureTest.java` + +- [ ] **Step 1: Run backend regression checks** + +Run: + +```bash +mvn test -pl ccdi-project -Dtest=CcdiProjectOverviewControllerTest,CcdiProjectOverviewMapperSqlTest,CcdiProjectOverviewServiceStructureTest +``` + +Expected: + +- `PASS` +- 证明结果总览接口边界、SQL 结构和服务接口未被破坏 + +- [ ] **Step 2: Commit** + +```bash +git add ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerTest.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapperSqlTest.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/CcdiProjectOverviewServiceStructureTest.java +git commit -m "回归验证结果总览标签模型字段后端改动" +``` diff --git a/docs/plans/frontend/2026-03-23-results-overview-hit-tag-model-color-frontend-implementation.md b/docs/plans/frontend/2026-03-23-results-overview-hit-tag-model-color-frontend-implementation.md new file mode 100644 index 00000000..b98a6fc3 --- /dev/null +++ b/docs/plans/frontend/2026-03-23-results-overview-hit-tag-model-color-frontend-implementation.md @@ -0,0 +1,132 @@ +# Results Overview Hit Tag Model Color Frontend Implementation Plan + +> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 在项目总览页中将已选模型对应的异常标签显示为红色,未选中的标签保持无色,并让核心异常点与异常标签列遵循同一颜色规则。 + +**Architecture:** 继续沿用结果总览现有组件拆分,不新增页面层级。前端只基于后端返回的 `modelCode/modelName` 和当前 `selectedModelCodes` 计算标签视觉状态;当没有选中模型时,标签默认无色,避免误导。风险人员总览、命中模型涉及人员、流水详情共用同一颜色映射工具方法。 + +**Tech Stack:** Vue 2, Element UI, Node.js + +--- + +### Task 1: 先锁定前端颜色规则测试 + +**Files:** +- Modify: `ruoyi-ui/tests/unit/preliminary-check-risk-people-hit-tags.test.js` +- Modify: `ruoyi-ui/tests/unit/preliminary-check-model-table-columns.test.js` +- Modify: `ruoyi-ui/tests/unit/detail-query-hit-tags-list.test.js` + +- [ ] **Step 1: Write the failing test** + +补充静态断言,锁定以下预期: + +- 标签渲染逻辑读取 `tag.modelCode` +- 命中模型涉及人员标签根据选中模型切换 `danger` / `info` +- 流水详情和列表异常标签共用同一颜色判断方法 + +- [ ] **Step 2: Run test to verify it fails** + +Run: + +```bash +cd ruoyi-ui +node tests/unit/preliminary-check-risk-people-hit-tags.test.js +node tests/unit/preliminary-check-model-table-columns.test.js +node tests/unit/detail-query-hit-tags-list.test.js +``` + +Expected: + +- `FAIL` +- 原因是当前前端仍按 `riskLevel` 映射颜色 + +- [ ] **Step 3: Commit the test expectation update** + +```bash +git add ruoyi-ui/tests/unit/preliminary-check-risk-people-hit-tags.test.js ruoyi-ui/tests/unit/preliminary-check-model-table-columns.test.js ruoyi-ui/tests/unit/detail-query-hit-tags-list.test.js +git commit -m "锁定结果总览标签模型颜色规则" +``` + +### Task 2: 实现标签模型颜色映射 + +**Files:** +- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/RiskPeopleSection.vue` +- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/RiskModelSection.vue` +- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue` +- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/preliminaryCheck.mock.js` + +- [ ] **Step 1: Normalize incoming tag model fields** + +在风险人员总览、命中模型人员、流水详情中统一兼容: + +- `tag.modelCode` +- `tag.modelName` + +mock 数据同步补齐模型字段,避免本地演示与真实接口脱节。 + +- [ ] **Step 2: Implement color rule** + +颜色规则固定为: + +- 当前标签 `modelCode` 在已选模型集合中时,标签 `type="danger"` +- 当前标签不在已选模型集合中时,标签 `type="info"` +- 风险人员总览没有模型筛选器时,默认按“未选中”展示为无色 + +不要继续使用 `riskLevel` 做颜色分流。 + +- [ ] **Step 3: Run focused frontend tests** + +Run: + +```bash +cd ruoyi-ui +node tests/unit/preliminary-check-risk-people-hit-tags.test.js +node tests/unit/preliminary-check-model-table-columns.test.js +node tests/unit/detail-query-hit-tags-list.test.js +node tests/unit/preliminary-check-summary-and-people.test.js +``` + +Expected: + +- `PASS` + +- [ ] **Step 4: Commit** + +```bash +git add ruoyi-ui/src/views/ccdiProject/components/detail/RiskPeopleSection.vue ruoyi-ui/src/views/ccdiProject/components/detail/RiskModelSection.vue ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue ruoyi-ui/src/views/ccdiProject/components/detail/preliminaryCheck.mock.js +git commit -m "调整结果总览标签模型颜色展示" +``` + +### Task 3: 做结果总览前端回归检查 + +**Files:** +- Verify: `ruoyi-ui/tests/unit/preliminary-check-model-linkage-flow.test.js` +- Verify: `ruoyi-ui/tests/unit/preliminary-check-summary-and-people.test.js` +- Verify: `ruoyi-ui/tests/unit/project-overview-api.test.js` + +- [ ] **Step 1: Run final regression checks** + +Run: + +```bash +cd ruoyi-ui +node tests/unit/preliminary-check-model-linkage-flow.test.js +node tests/unit/preliminary-check-summary-and-people.test.js +node tests/unit/preliminary-check-risk-people-hit-tags.test.js +node tests/unit/preliminary-check-model-table-columns.test.js +node tests/unit/detail-query-hit-tags-list.test.js +node tests/unit/project-overview-api.test.js +``` + +Expected: + +- `PASS` +- 证明标签联动、页面静态结构和 API 调用都保持稳定 + +- [ ] **Step 2: Commit** + +```bash +git add ruoyi-ui/tests/unit/preliminary-check-model-linkage-flow.test.js ruoyi-ui/tests/unit/preliminary-check-summary-and-people.test.js ruoyi-ui/tests/unit/project-overview-api.test.js +git commit -m "回归验证结果总览标签颜色前端改动" +``` diff --git a/docs/reports/implementation/2026-03-23-results-overview-hit-tag-model-color-implementation.md b/docs/reports/implementation/2026-03-23-results-overview-hit-tag-model-color-implementation.md new file mode 100644 index 00000000..da97471a --- /dev/null +++ b/docs/reports/implementation/2026-03-23-results-overview-hit-tag-model-color-implementation.md @@ -0,0 +1,44 @@ +# 项目总览标签按模型着色实施记录 + +## 本次改动 + +- 后端扩展结果总览标签对象 [`CcdiProjectRiskHitTagVO`](/Users/wkc/Desktop/ccdi/ccdi/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectRiskHitTagVO),新增 `modelCode`、`modelName` 字段,供前端按模型识别标签归属。 +- 后端扩展风险人员总览项 [`CcdiProjectRiskPeopleOverviewItemVO`](/Users/wkc/Desktop/ccdi/ccdi/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectRiskPeopleOverviewItemVO),新增 `riskPointTagList`,让“核心异常点”不再只依赖纯文本拆分。 +- 调整结果总览 Mapper 与服务: + - [`CcdiProjectOverviewMapper.xml`](/Users/wkc/Desktop/ccdi/ccdi/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml) 的标签查询改为返回 `model_code`、`model_name`,并按 `modelCode + ruleCode` 分组,避免跨模型标签被合并。 + - [`CcdiProjectOverviewMapper.java`](/Users/wkc/Desktop/ccdi/ccdi/ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapper.java) 新增 `selectRiskHitTagsByScope` 方法。 + - [`CcdiProjectOverviewServiceImpl.java`](/Users/wkc/Desktop/ccdi/ccdi/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImpl.java) 在风险人员总览映射时补充 `riskPointTagList`。 +- 调整结果总览前端联动: + - [`PreliminaryCheck.vue`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/src/views/ccdiProject/components/detail/PreliminaryCheck.vue) 维护共享的 `selectedModelCodes`,并把模型区选中状态同步给风险人员区。 + - [`RiskModelSection.vue`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/src/views/ccdiProject/components/detail/RiskModelSection.vue) 的“异常标签”列改为按模型判断颜色:已选模型标签为红色,未选中标签保持无色。 + - [`RiskPeopleSection.vue`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/src/views/ccdiProject/components/detail/RiskPeopleSection.vue) 的“核心异常点”标签改为按共享选中模型着色,未选中时保持无色。 + - [`preliminaryCheck.mock.js`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/src/views/ccdiProject/components/detail/preliminaryCheck.mock.js) 补齐标签示例数据中的 `modelCode`、`modelName`。 + +## 实现说明 + +- 选中模型只影响标签颜色,不再裁掉同一人员的其他模型标签,保证“选中红色、未选中无色”的对比能真实展示。 +- 风险模型人员列表的行筛选逻辑保持不变,仍由现有 `selectedModelCodes + matchMode` 控制。 +- 当项目切换为空、重新加载或加载失败时,前端会主动清空共享的模型选中状态,避免旧状态污染新页面。 + +## 验证情况 + +### 后端 + +- `mvn test -pl ccdi-project -Dtest=CcdiProjectOverviewServiceImplTest,CcdiProjectOverviewControllerTest,CcdiProjectOverviewMapperRiskModelPeopleTest` + +### 前端 + +- `node ruoyi-ui/tests/unit/project-overview-api.test.js` +- `node ruoyi-ui/tests/unit/preliminary-check-api-integration.test.js` +- `node ruoyi-ui/tests/unit/preliminary-check-states.test.js` +- `node ruoyi-ui/tests/unit/preliminary-check-summary-and-people.test.js` +- `node ruoyi-ui/tests/unit/preliminary-check-risk-people-binding.test.js` +- `node ruoyi-ui/tests/unit/preliminary-check-risk-people-hit-tags.test.js` +- `node ruoyi-ui/tests/unit/preliminary-check-model-linkage-flow.test.js` +- `node ruoyi-ui/tests/unit/preliminary-check-model-multiselect.test.js` +- `node ruoyi-ui/tests/unit/preliminary-check-model-table-columns.test.js` + +## 未包含内容 + +- 未改动项目详情页 [`DetailQuery.vue`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue) 的异常标签展示规则。 +- 未新增新的模型颜色体系,当前仅按“已选模型红色、未选模型无色”执行。 diff --git a/ruoyi-ui/src/views/ccdiProject/components/detail/PreliminaryCheck.vue b/ruoyi-ui/src/views/ccdiProject/components/detail/PreliminaryCheck.vue index 1084d045..0e231c0b 100644 --- a/ruoyi-ui/src/views/ccdiProject/components/detail/PreliminaryCheck.vue +++ b/ruoyi-ui/src/views/ccdiProject/components/detail/PreliminaryCheck.vue @@ -14,8 +14,14 @@
- - + +
@@ -62,6 +68,7 @@ export default { data() { return { pageState: "loading", + selectedModelCodes: [], mockData: mockOverviewData, stateDataMap: mockOverviewStateData, realData: mockOverviewData, @@ -83,6 +90,7 @@ export default { } this.realData = this.stateDataMap.empty; this.pageState = "empty"; + this.selectedModelCodes = []; }, }, created() { @@ -94,14 +102,19 @@ export default { this.pageState = "empty"; }, methods: { + handleRiskModelSelectionChange(modelCodes) { + this.selectedModelCodes = Array.isArray(modelCodes) ? [...modelCodes] : []; + }, async loadOverviewData() { if (!this.projectId) { this.realData = this.stateDataMap.empty; this.pageState = "empty"; + this.selectedModelCodes = []; return; } this.pageState = "loading"; + this.selectedModelCodes = []; try { const [dashboardRes, riskPeopleRes, riskModelCardsRes] = await Promise.all([ getOverviewDashboard(this.projectId), @@ -129,6 +142,7 @@ export default { } catch (error) { this.realData = this.stateDataMap.empty; this.pageState = "empty"; + this.selectedModelCodes = []; console.error("加载结果总览失败", error); } }, diff --git a/ruoyi-ui/src/views/ccdiProject/components/detail/RiskModelSection.vue b/ruoyi-ui/src/views/ccdiProject/components/detail/RiskModelSection.vue index 409559f1..8497da7e 100644 --- a/ruoyi-ui/src/views/ccdiProject/components/detail/RiskModelSection.vue +++ b/ruoyi-ui/src/views/ccdiProject/components/detail/RiskModelSection.vue @@ -113,7 +113,7 @@ :key="`${scope.row.staffCode || scope.row.idNo || index}-tag-${index}`" size="mini" effect="plain" - :type="mapRiskLevelToTagType(tag.riskLevel)" + :type="resolveModelTagType(tag)" > {{ tag.ruleName }} @@ -232,6 +232,8 @@ export default { projectId: { immediate: true, handler() { + this.selectedModelCodes = []; + this.$emit("selection-change", this.selectedModelCodes); this.pageNum = 1; this.fetchPeopleList(); }, @@ -250,6 +252,7 @@ export default { } else { this.selectedModelCodes = [...this.selectedModelCodes, modelCode]; } + this.$emit("selection-change", this.selectedModelCodes); this.pageNum = 1; this.fetchPeopleList({ syncCardLoading: true }); }, @@ -263,6 +266,7 @@ export default { }, resetQuery() { this.selectedModelCodes = []; + this.$emit("selection-change", this.selectedModelCodes); this.matchMode = "ANY"; this.keyword = ""; this.deptId = undefined; @@ -290,15 +294,14 @@ export default { } return modelNames.join("、"); }, - mapRiskLevelToTagType(riskLevel) { - const level = String(riskLevel || "").toUpperCase(); - if (level === "HIGH") { + resolveModelTagType(tag) { + if (!this.selectedModelCodes.length) { + return ""; + } + if (this.selectedModelCodes.includes(tag.modelCode)) { return "danger"; } - if (level === "MEDIUM") { - return "warning"; - } - return "info"; + return ""; }, async loadDeptOptions() { this.deptLoading = true; diff --git a/ruoyi-ui/tests/unit/preliminary-check-model-table-columns.test.js b/ruoyi-ui/tests/unit/preliminary-check-model-table-columns.test.js index 5d003d47..889ac07a 100644 --- a/ruoyi-ui/tests/unit/preliminary-check-model-table-columns.test.js +++ b/ruoyi-ui/tests/unit/preliminary-check-model-table-columns.test.js @@ -16,6 +16,9 @@ const source = fs.readFileSync( "异常标签", "hitTagList", "ruleName", + "tag.modelCode", + 'this.$emit("selection-change", this.selectedModelCodes)', + ':type="resolveModelTagType(tag)"', ].forEach((token) => assert(source.includes(token), token)); [ diff --git a/ruoyi-ui/tests/unit/preliminary-check-summary-and-people.test.js b/ruoyi-ui/tests/unit/preliminary-check-summary-and-people.test.js index e53839af..76f3a882 100644 --- a/ruoyi-ui/tests/unit/preliminary-check-summary-and-people.test.js +++ b/ruoyi-ui/tests/unit/preliminary-check-summary-and-people.test.js @@ -38,6 +38,11 @@ const mockSource = fs.readFileSync( ["currentData.summary", "currentData.riskPeople"].forEach((token) => assert(entry.includes(token), token) ); +[ + ':selected-model-codes="selectedModelCodes"', + '@selection-change="handleRiskModelSelectionChange"', + "selectedModelCodes: []", +].forEach((token) => assert(entry.includes(token), token)); ["风险人员总览", "风险等级", "命中模型数", "查看详情"].forEach((token) => assert(people.includes(token), token) );