实现结果总览详情弹窗后端接口
This commit is contained in:
@@ -0,0 +1,14 @@
|
|||||||
|
package com.ruoyi.ccdi.project.domain.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目分析对象型异常补充字段
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CcdiProjectPersonAnalysisObjectFieldVO {
|
||||||
|
|
||||||
|
private String label;
|
||||||
|
|
||||||
|
private String value;
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.ruoyi.ccdi.project.domain.vo;
|
package com.ruoyi.ccdi.project.domain.vo;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,5 +18,5 @@ public class CcdiProjectPersonAnalysisObjectRecordVO {
|
|||||||
|
|
||||||
private String summary;
|
private String summary;
|
||||||
|
|
||||||
private List<Map<String, String>> extraFields;
|
private List<CcdiProjectPersonAnalysisObjectFieldVO> extraFields = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.ruoyi.ccdi.project.domain.dto.CcdiProjectPersonAnalysisDetailQueryDTO
|
|||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskModelPeopleQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskModelPeopleQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.entity.CcdiProjectOverviewEmployeeResult;
|
import com.ruoyi.ccdi.project.domain.entity.CcdiProjectOverviewEmployeeResult;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiBankStatementListVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiBankStatementListVO;
|
||||||
|
import com.ruoyi.ccdi.project.domain.vo.CcdiBankStatementHitTagVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisDetailVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisDetailVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisAbnormalDetailVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisAbnormalDetailVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisAbnormalGroupVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisAbnormalGroupVO;
|
||||||
@@ -23,14 +24,18 @@ import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskPeopleOverviewVO;
|
|||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectTopRiskPeopleItemVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectTopRiskPeopleItemVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectTopRiskPeopleVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectTopRiskPeopleVO;
|
||||||
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
|
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.CcdiProjectOverviewEmployeeResultMapper;
|
||||||
import com.ruoyi.ccdi.project.mapper.CcdiProjectOverviewMapper;
|
import com.ruoyi.ccdi.project.mapper.CcdiProjectOverviewMapper;
|
||||||
import com.ruoyi.ccdi.project.service.ICcdiProjectOverviewService;
|
import com.ruoyi.ccdi.project.service.ICcdiProjectOverviewService;
|
||||||
import com.ruoyi.common.exception.ServiceException;
|
import com.ruoyi.common.exception.ServiceException;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
@@ -51,6 +56,9 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi
|
|||||||
@Resource
|
@Resource
|
||||||
private CcdiProjectOverviewEmployeeResultMapper overviewEmployeeResultMapper;
|
private CcdiProjectOverviewEmployeeResultMapper overviewEmployeeResultMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private CcdiBankTagResultMapper bankTagResultMapper;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private CcdiProjectOverviewEmployeeResultBuilder overviewEmployeeResultBuilder;
|
private CcdiProjectOverviewEmployeeResultBuilder overviewEmployeeResultBuilder;
|
||||||
|
|
||||||
@@ -124,6 +132,8 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi
|
|||||||
queryDTO.getProjectId(),
|
queryDTO.getProjectId(),
|
||||||
queryDTO.getStaffIdCard()
|
queryDTO.getStaffIdCard()
|
||||||
));
|
));
|
||||||
|
attachStatementHitTags(statementRows, queryDTO.getProjectId());
|
||||||
|
normalizeObjectRows(objectRows);
|
||||||
|
|
||||||
CcdiProjectPersonAnalysisDetailVO detail = new CcdiProjectPersonAnalysisDetailVO();
|
CcdiProjectPersonAnalysisDetailVO detail = new CcdiProjectPersonAnalysisDetailVO();
|
||||||
detail.setBasicInfo(basicInfo == null ? new CcdiProjectPersonAnalysisBasicInfoVO() : basicInfo);
|
detail.setBasicInfo(basicInfo == null ? new CcdiProjectPersonAnalysisBasicInfoVO() : basicInfo);
|
||||||
@@ -314,6 +324,42 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi
|
|||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void attachStatementHitTags(List<CcdiBankStatementListVO> statementRows, Long projectId) {
|
||||||
|
if (statementRows.isEmpty() || projectId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<Long> bankStatementIds = statementRows.stream()
|
||||||
|
.map(CcdiBankStatementListVO::getBankStatementId)
|
||||||
|
.filter(item -> item != null)
|
||||||
|
.distinct()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (bankStatementIds.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Map<Long, List<CcdiBankStatementHitTagVO>> hitTagMap = defaultList(
|
||||||
|
bankTagResultMapper.selectStatementTagsByProjectAndStatementIds(projectId, bankStatementIds)
|
||||||
|
).stream().filter(item -> item.getBankStatementId() != null)
|
||||||
|
.collect(Collectors.groupingBy(
|
||||||
|
CcdiBankStatementHitTagVO::getBankStatementId,
|
||||||
|
LinkedHashMap::new,
|
||||||
|
Collectors.toList()
|
||||||
|
));
|
||||||
|
statementRows.forEach(row -> row.setHitTags(new ArrayList<>(
|
||||||
|
hitTagMap.getOrDefault(row.getBankStatementId(), Collections.emptyList())
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void normalizeObjectRows(List<CcdiProjectPersonAnalysisObjectRecordVO> objectRows) {
|
||||||
|
objectRows.forEach(row -> {
|
||||||
|
if (row.getRiskTags() == null) {
|
||||||
|
row.setRiskTags(new ArrayList<>());
|
||||||
|
}
|
||||||
|
if (row.getExtraFields() == null) {
|
||||||
|
row.setExtraFields(new ArrayList<>());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private CcdiProject getRequiredProject(Long projectId) {
|
private CcdiProject getRequiredProject(Long projectId) {
|
||||||
CcdiProject project = projectMapper.selectById(projectId);
|
CcdiProject project = projectMapper.selectById(projectId);
|
||||||
if (project == null) {
|
if (project == null) {
|
||||||
|
|||||||
@@ -377,6 +377,93 @@
|
|||||||
json_unquote(json_extract(result.hit_rules_json, concat('$[', idx.idx, '].ruleCode'))) asc
|
json_unquote(json_extract(result.hit_rules_json, concat('$[', idx.idx, '].ruleCode'))) asc
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="selectPersonAnalysisBasicInfo" resultType="com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisBasicInfoVO">
|
||||||
|
select
|
||||||
|
coalesce(staff.name, result.staff_name) as name,
|
||||||
|
result.staff_id_card as idNo,
|
||||||
|
result.staff_code as staffCode,
|
||||||
|
dept.dept_name as department,
|
||||||
|
staff.phone as phone,
|
||||||
|
case
|
||||||
|
when result.risk_level_code = 'HIGH' then '高风险'
|
||||||
|
when result.risk_level_code = 'MEDIUM' then '中风险'
|
||||||
|
else '低风险'
|
||||||
|
end as riskLevel,
|
||||||
|
project.project_name as projectName
|
||||||
|
from ccdi_project_overview_employee_result result
|
||||||
|
left join ccdi_base_staff staff
|
||||||
|
on staff.id_card = result.staff_id_card
|
||||||
|
left join sys_dept dept
|
||||||
|
on dept.dept_id = coalesce(staff.dept_id, result.dept_id)
|
||||||
|
left join ccdi_project project
|
||||||
|
on project.project_id = result.project_id
|
||||||
|
where result.project_id = #{projectId}
|
||||||
|
and result.staff_id_card = #{staffIdCard}
|
||||||
|
limit 1
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectPersonAnalysisStatementRows" resultType="com.ruoyi.ccdi.project.domain.vo.CcdiBankStatementListVO">
|
||||||
|
select distinct
|
||||||
|
bs.bank_statement_id as bankStatementId,
|
||||||
|
bs.TRX_DATE as trxDate,
|
||||||
|
bs.LE_ACCOUNT_NO as leAccountNo,
|
||||||
|
bs.LE_ACCOUNT_NAME as leAccountName,
|
||||||
|
bs.CUSTOMER_ACCOUNT_NAME as customerAccountName,
|
||||||
|
bs.CUSTOMER_ACCOUNT_NO as customerAccountNo,
|
||||||
|
bs.USER_MEMO as userMemo,
|
||||||
|
bs.CASH_TYPE as cashType,
|
||||||
|
case
|
||||||
|
when ifnull(bs.AMOUNT_CR, 0) > 0 then bs.AMOUNT_CR
|
||||||
|
when ifnull(bs.AMOUNT_DR, 0) > 0 then -bs.AMOUNT_DR
|
||||||
|
else 0
|
||||||
|
end as displayAmount
|
||||||
|
from ccdi_bank_statement bs
|
||||||
|
inner join ccdi_bank_statement_tag_result tr
|
||||||
|
on tr.project_id = bs.project_id
|
||||||
|
and tr.bank_statement_id = bs.bank_statement_id
|
||||||
|
left join ccdi_staff_fmy_relation relation
|
||||||
|
on relation.status = 1
|
||||||
|
and relation.relation_cert_no = bs.cret_no
|
||||||
|
where bs.project_id = #{projectId}
|
||||||
|
and (
|
||||||
|
bs.cret_no = #{staffIdCard}
|
||||||
|
or relation.person_id = #{staffIdCard}
|
||||||
|
or tr.object_key = #{staffIdCard}
|
||||||
|
)
|
||||||
|
order by bs.bank_statement_id desc
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectPersonAnalysisObjectRows" resultType="com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisObjectRecordVO">
|
||||||
|
select
|
||||||
|
coalesce(max(staff.name), max(relation.relation_name), max(tr.object_key), max(tr.object_type)) as title,
|
||||||
|
max(case
|
||||||
|
when tr.object_type = 'STAFF_ID_CARD' then '员工对象'
|
||||||
|
else tr.object_type
|
||||||
|
end) as subtitle,
|
||||||
|
group_concat(distinct tr.rule_name order by tr.rule_code asc separator '、') as summary
|
||||||
|
from ccdi_bank_statement_tag_result tr
|
||||||
|
left join ccdi_base_staff staff
|
||||||
|
on tr.object_type = 'STAFF_ID_CARD'
|
||||||
|
and tr.object_key = staff.id_card
|
||||||
|
left join ccdi_staff_fmy_relation relation
|
||||||
|
on relation.status = 1
|
||||||
|
and tr.object_key = relation.relation_cert_no
|
||||||
|
where tr.project_id = #{projectId}
|
||||||
|
and tr.bank_statement_id is null
|
||||||
|
and (
|
||||||
|
tr.object_key = #{staffIdCard}
|
||||||
|
or exists (
|
||||||
|
select 1
|
||||||
|
from ccdi_staff_fmy_relation relation_scope
|
||||||
|
where relation_scope.status = 1
|
||||||
|
and relation_scope.person_id = #{staffIdCard}
|
||||||
|
and relation_scope.relation_cert_no = tr.object_key
|
||||||
|
)
|
||||||
|
)
|
||||||
|
group by coalesce(tr.object_key, tr.object_type)
|
||||||
|
order by title asc
|
||||||
|
</select>
|
||||||
|
|
||||||
<select id="selectRiskCountSummaryByProjectId" resultType="map">
|
<select id="selectRiskCountSummaryByProjectId" resultType="map">
|
||||||
select
|
select
|
||||||
coalesce(sum(case when agg.rule_count >= 5 then 1 else 0 end), 0) as highRiskCount,
|
coalesce(sum(case when agg.rule_count >= 5 then 1 else 0 end), 0) as highRiskCount,
|
||||||
|
|||||||
@@ -45,10 +45,32 @@ class CcdiProjectOverviewMapperSqlTest {
|
|||||||
assertFalse(xml.contains("json_table("), xml);
|
assertFalse(xml.contains("json_table("), xml);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldExposePersonAnalysisDetailQueries() throws Exception {
|
||||||
|
String xml = Files.readString(Path.of("src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml"));
|
||||||
|
String basicInfoSql = extractSelect(xml, "selectPersonAnalysisBasicInfo");
|
||||||
|
String statementRowsSql = extractSelect(xml, "selectPersonAnalysisStatementRows");
|
||||||
|
String objectRowsSql = extractSelect(xml, "selectPersonAnalysisObjectRows");
|
||||||
|
|
||||||
|
assertTrue(basicInfoSql.contains("ccdi_base_staff"), basicInfoSql);
|
||||||
|
assertTrue(basicInfoSql.contains("left join sys_dept"), basicInfoSql);
|
||||||
|
assertTrue(basicInfoSql.contains("ccdi_project_overview_employee_result"), basicInfoSql);
|
||||||
|
|
||||||
|
assertTrue(statementRowsSql.contains("from ccdi_bank_statement"), statementRowsSql);
|
||||||
|
assertTrue(statementRowsSql.contains("ccdi_bank_statement_tag_result"), statementRowsSql);
|
||||||
|
assertTrue(statementRowsSql.contains("bs.project_id = #{projectId}"), statementRowsSql);
|
||||||
|
|
||||||
|
assertTrue(objectRowsSql.contains("from ccdi_bank_statement_tag_result"), objectRowsSql);
|
||||||
|
assertTrue(objectRowsSql.contains("tr.object_type"), objectRowsSql);
|
||||||
|
assertTrue(objectRowsSql.contains("tr.staff_id_card = #{staffIdCard}") || objectRowsSql.contains("#{staffIdCard}"), objectRowsSql);
|
||||||
|
}
|
||||||
|
|
||||||
private String extractSelect(String xml, String selectId) {
|
private String extractSelect(String xml, String selectId) {
|
||||||
String start = "<select id=\"" + selectId + "\"";
|
String start = "<select id=\"" + selectId + "\"";
|
||||||
int startIndex = xml.indexOf(start);
|
int startIndex = xml.indexOf(start);
|
||||||
|
assertTrue(startIndex >= 0, "missing select: " + selectId);
|
||||||
int endIndex = xml.indexOf("</select>", startIndex);
|
int endIndex = xml.indexOf("</select>", startIndex);
|
||||||
|
assertTrue(endIndex >= 0, "missing closing select tag: " + selectId);
|
||||||
return xml.substring(startIndex, endIndex);
|
return xml.substring(startIndex, endIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelPeopleVO;
|
|||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskPeopleOverviewVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskPeopleOverviewVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectTopRiskPeopleVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectTopRiskPeopleVO;
|
||||||
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
|
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.CcdiProjectOverviewEmployeeResultMapper;
|
||||||
import com.ruoyi.ccdi.project.mapper.CcdiProjectOverviewMapper;
|
import com.ruoyi.ccdi.project.mapper.CcdiProjectOverviewMapper;
|
||||||
import com.ruoyi.common.exception.ServiceException;
|
import com.ruoyi.common.exception.ServiceException;
|
||||||
@@ -56,6 +57,9 @@ class CcdiProjectOverviewServiceImplTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private CcdiProjectOverviewEmployeeResultMapper overviewEmployeeResultMapper;
|
private CcdiProjectOverviewEmployeeResultMapper overviewEmployeeResultMapper;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private CcdiBankTagResultMapper bankTagResultMapper;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private CcdiProjectOverviewEmployeeResultBuilder overviewEmployeeResultBuilder;
|
private CcdiProjectOverviewEmployeeResultBuilder overviewEmployeeResultBuilder;
|
||||||
|
|
||||||
@@ -173,6 +177,15 @@ class CcdiProjectOverviewServiceImplTest {
|
|||||||
when(overviewMapper.selectPersonAnalysisStatementRows(40L, "330000000000000001"))
|
when(overviewMapper.selectPersonAnalysisStatementRows(40L, "330000000000000001"))
|
||||||
.thenReturn(List.of(statementRow));
|
.thenReturn(List.of(statementRow));
|
||||||
|
|
||||||
|
com.ruoyi.ccdi.project.domain.vo.CcdiBankStatementHitTagVO hitTag =
|
||||||
|
new com.ruoyi.ccdi.project.domain.vo.CcdiBankStatementHitTagVO();
|
||||||
|
hitTag.setBankStatementId(1L);
|
||||||
|
hitTag.setRuleCode("RULE_A");
|
||||||
|
hitTag.setRuleName("大额转账");
|
||||||
|
hitTag.setRiskLevel("HIGH");
|
||||||
|
when(bankTagResultMapper.selectStatementTagsByProjectAndStatementIds(40L, List.of(1L)))
|
||||||
|
.thenReturn(List.of(hitTag));
|
||||||
|
|
||||||
CcdiProjectPersonAnalysisObjectRecordVO objectRow = new CcdiProjectPersonAnalysisObjectRecordVO();
|
CcdiProjectPersonAnalysisObjectRecordVO objectRow = new CcdiProjectPersonAnalysisObjectRecordVO();
|
||||||
objectRow.setTitle("张三");
|
objectRow.setTitle("张三");
|
||||||
objectRow.setSubtitle("关联人员");
|
objectRow.setSubtitle("关联人员");
|
||||||
@@ -191,6 +204,10 @@ class CcdiProjectOverviewServiceImplTest {
|
|||||||
assertEquals(2, result.getAbnormalDetail().getGroups().size());
|
assertEquals(2, result.getAbnormalDetail().getGroups().size());
|
||||||
assertEquals("BANK_STATEMENT", result.getAbnormalDetail().getGroups().get(0).getGroupType());
|
assertEquals("BANK_STATEMENT", result.getAbnormalDetail().getGroups().get(0).getGroupType());
|
||||||
assertEquals("OBJECT", result.getAbnormalDetail().getGroups().get(1).getGroupType());
|
assertEquals("OBJECT", result.getAbnormalDetail().getGroups().get(1).getGroupType());
|
||||||
|
List<?> statementRecords = result.getAbnormalDetail().getGroups().get(0).getRecords();
|
||||||
|
assertEquals(1, ((CcdiBankStatementListVO) statementRecords.getFirst()).getHitTags().size());
|
||||||
|
List<?> objectRecords = result.getAbnormalDetail().getGroups().get(1).getRecords();
|
||||||
|
assertNotNull(((CcdiProjectPersonAnalysisObjectRecordVO) objectRecords.getFirst()).getExtraFields());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
# 结果总览项目分析弹窗真实详情后端实施记录
|
||||||
|
|
||||||
|
**日期**: 2026-03-25
|
||||||
|
**模块**: 初核项目详情 - 结果总览
|
||||||
|
|
||||||
|
## 本次实现
|
||||||
|
|
||||||
|
- 在结果总览控制器下新增详情接口 `GET /ccdi/project/overview/person-analysis/detail`
|
||||||
|
- 新增详情查询 DTO 与 VO 结构,统一承载:
|
||||||
|
- 人员基础信息
|
||||||
|
- 异常明细分组
|
||||||
|
- 在结果总览 Mapper 中新增 3 个详情查询入口:
|
||||||
|
- `selectPersonAnalysisBasicInfo`
|
||||||
|
- `selectPersonAnalysisStatementRows`
|
||||||
|
- `selectPersonAnalysisObjectRows`
|
||||||
|
- 在服务层新增详情组装逻辑:
|
||||||
|
- 基础信息查询
|
||||||
|
- `BANK_STATEMENT` 分组组装
|
||||||
|
- `OBJECT` 分组组装
|
||||||
|
- 流水命中标签回填
|
||||||
|
|
||||||
|
## 主要文件
|
||||||
|
|
||||||
|
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewController.java`
|
||||||
|
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiProjectOverviewService.java`
|
||||||
|
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImpl.java`
|
||||||
|
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapper.java`
|
||||||
|
- `ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml`
|
||||||
|
|
||||||
|
## 实现说明
|
||||||
|
|
||||||
|
### 1. 接口边界
|
||||||
|
|
||||||
|
- 详情能力继续收敛在结果总览域内,没有新建平行控制器
|
||||||
|
- 控制器返回格式保持 `AjaxResult.success(data)` 一致
|
||||||
|
|
||||||
|
### 2. 基础信息来源
|
||||||
|
|
||||||
|
- 风险等级、工号、项目范围仍以 `ccdi_project_overview_employee_result` 为结果总览口径
|
||||||
|
- 姓名、手机号、部门信息通过员工表与部门表补齐
|
||||||
|
|
||||||
|
### 3. 异常明细组装
|
||||||
|
|
||||||
|
- `BANK_STATEMENT` 分组直接复用流水详情字段口径
|
||||||
|
- `OBJECT` 分组统一输出对象摘要记录
|
||||||
|
- 服务层在返回前补齐:
|
||||||
|
- 流水命中标签
|
||||||
|
- 对象记录默认空列表字段,避免前端拿到 `null`
|
||||||
|
|
||||||
|
## 验证情况
|
||||||
|
|
||||||
|
- 已执行结果总览相关后端聚焦回归
|
||||||
|
- 详情接口契约、服务层、Mapper SQL 和既有模型区回归全部通过
|
||||||
|
- 详见:
|
||||||
|
- `docs/tests/records/2026-03-25-results-overview-project-analysis-dialog-real-detail-backend-verification.md`
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# 结果总览项目分析弹窗真实详情后端验证记录
|
||||||
|
|
||||||
|
**日期**: 2026-03-25
|
||||||
|
**模块**: 初核项目详情 - 结果总览
|
||||||
|
|
||||||
|
## 执行命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn test -pl ccdi-project -Dtest=CcdiProjectOverviewControllerContractTest,CcdiProjectOverviewServiceStructureTest
|
||||||
|
mvn test -pl ccdi-project -Dtest=CcdiProjectOverviewServiceImplTest,CcdiProjectOverviewControllerTest
|
||||||
|
mvn test -pl ccdi-project -Dtest=CcdiProjectOverviewMapperSqlTest,CcdiBankStatementMapperXmlTest
|
||||||
|
mvn test -pl ccdi-project -Dtest=CcdiProjectOverviewControllerContractTest,CcdiProjectOverviewControllerTest,CcdiProjectOverviewServiceImplTest,CcdiProjectOverviewMapperSqlTest,CcdiProjectOverviewMapperRiskModelPeopleTest,CcdiProjectOverviewMapperRiskModelCardsTest
|
||||||
|
```
|
||||||
|
|
||||||
|
## 执行结果
|
||||||
|
|
||||||
|
- 上述命令全部执行成功
|
||||||
|
- 新增详情接口契约测试通过
|
||||||
|
- 服务层详情组装测试通过
|
||||||
|
- 结果总览 Mapper 新增 3 个详情查询入口测试通过
|
||||||
|
- 既有模型卡片与模型命中人员查询回归通过
|
||||||
|
|
||||||
|
## 关键验证点
|
||||||
|
|
||||||
|
1. 已新增 `GET /ccdi/project/overview/person-analysis/detail`
|
||||||
|
2. 详情接口入参固定为 `projectId + staffIdCard`
|
||||||
|
3. 返回结构包含:
|
||||||
|
- `basicInfo`
|
||||||
|
- `abnormalDetail.groups`
|
||||||
|
4. `basicInfo` 查询链路已关联:
|
||||||
|
- `ccdi_project_overview_employee_result`
|
||||||
|
- `ccdi_base_staff`
|
||||||
|
- `sys_dept`
|
||||||
|
- `ccdi_project`
|
||||||
|
5. `abnormalDetail.groups` 已支持:
|
||||||
|
- `BANK_STATEMENT`
|
||||||
|
- `OBJECT`
|
||||||
|
6. 流水型异常会附加真实命中标签列表
|
||||||
|
|
||||||
|
## 结论
|
||||||
|
|
||||||
|
结果总览项目分析弹窗后端真实详情链路已打通,且未影响既有结果总览模型区和人员区查询能力。
|
||||||
Reference in New Issue
Block a user