实现结果总览详情弹窗后端接口

This commit is contained in:
wkc
2026-03-25 15:15:07 +08:00
parent 717f836190
commit a52fb35bd3
8 changed files with 285 additions and 2 deletions

View File

@@ -0,0 +1,14 @@
package com.ruoyi.ccdi.project.domain.vo;
import lombok.Data;
/**
* 项目分析对象型异常补充字段
*/
@Data
public class CcdiProjectPersonAnalysisObjectFieldVO {
private String label;
private String value;
}

View File

@@ -1,7 +1,7 @@
package com.ruoyi.ccdi.project.domain.vo;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import lombok.Data;
/**
@@ -18,5 +18,5 @@ public class CcdiProjectPersonAnalysisObjectRecordVO {
private String summary;
private List<Map<String, String>> extraFields;
private List<CcdiProjectPersonAnalysisObjectFieldVO> extraFields = new ArrayList<>();
}

View File

@@ -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.entity.CcdiProjectOverviewEmployeeResult;
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.CcdiProjectPersonAnalysisAbnormalDetailVO;
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.CcdiProjectTopRiskPeopleVO;
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.ICcdiProjectOverviewService;
import com.ruoyi.common.exception.ServiceException;
import jakarta.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -51,6 +56,9 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi
@Resource
private CcdiProjectOverviewEmployeeResultMapper overviewEmployeeResultMapper;
@Resource
private CcdiBankTagResultMapper bankTagResultMapper;
@Resource
private CcdiProjectOverviewEmployeeResultBuilder overviewEmployeeResultBuilder;
@@ -124,6 +132,8 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi
queryDTO.getProjectId(),
queryDTO.getStaffIdCard()
));
attachStatementHitTags(statementRows, queryDTO.getProjectId());
normalizeObjectRows(objectRows);
CcdiProjectPersonAnalysisDetailVO detail = new CcdiProjectPersonAnalysisDetailVO();
detail.setBasicInfo(basicInfo == null ? new CcdiProjectPersonAnalysisBasicInfoVO() : basicInfo);
@@ -314,6 +324,42 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi
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) {
CcdiProject project = projectMapper.selectById(projectId);
if (project == null) {

View File

@@ -377,6 +377,93 @@
json_unquote(json_extract(result.hit_rules_json, concat('$[', idx.idx, '].ruleCode'))) asc
</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
coalesce(sum(case when agg.rule_count >= 5 then 1 else 0 end), 0) as highRiskCount,

View File

@@ -45,10 +45,32 @@ class CcdiProjectOverviewMapperSqlTest {
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) {
String start = "<select id=\"" + selectId + "\"";
int startIndex = xml.indexOf(start);
assertTrue(startIndex >= 0, "missing select: " + selectId);
int endIndex = xml.indexOf("</select>", startIndex);
assertTrue(endIndex >= 0, "missing closing select tag: " + selectId);
return xml.substring(startIndex, endIndex);
}
}

View File

@@ -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.CcdiProjectTopRiskPeopleVO;
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.common.exception.ServiceException;
@@ -56,6 +57,9 @@ class CcdiProjectOverviewServiceImplTest {
@Mock
private CcdiProjectOverviewEmployeeResultMapper overviewEmployeeResultMapper;
@Mock
private CcdiBankTagResultMapper bankTagResultMapper;
@Mock
private CcdiProjectOverviewEmployeeResultBuilder overviewEmployeeResultBuilder;
@@ -173,6 +177,15 @@ class CcdiProjectOverviewServiceImplTest {
when(overviewMapper.selectPersonAnalysisStatementRows(40L, "330000000000000001"))
.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();
objectRow.setTitle("张三");
objectRow.setSubtitle("关联人员");
@@ -191,6 +204,10 @@ class CcdiProjectOverviewServiceImplTest {
assertEquals(2, result.getAbnormalDetail().getGroups().size());
assertEquals("BANK_STATEMENT", result.getAbnormalDetail().getGroups().get(0).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