补充项目详情风险人员导出能力
This commit is contained in:
@@ -5,6 +5,7 @@ import com.ruoyi.ccdi.project.domain.dto.CcdiProjectPersonAnalysisDetailQueryDTO
|
||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskModelPeopleQueryDTO;
|
||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskPeopleQueryDTO;
|
||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSuspiciousTransactionQueryDTO;
|
||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectRiskPeopleOverviewExcel;
|
||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectSuspiciousTransactionExcel;
|
||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeCreditNegativePageVO;
|
||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisDetailVO;
|
||||
@@ -145,6 +146,19 @@ public class CcdiProjectOverviewController extends BaseController {
|
||||
util.exportExcel(response, rows, "涉疑交易明细");
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出风险人员总览
|
||||
*/
|
||||
@PostMapping("/risk-people/export")
|
||||
@Operation(summary = "导出风险人员总览")
|
||||
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||
public void exportRiskPeople(HttpServletResponse response, Long projectId) {
|
||||
List<CcdiProjectRiskPeopleOverviewExcel> rows = overviewService.exportRiskPeopleOverview(projectId);
|
||||
ExcelUtil<CcdiProjectRiskPeopleOverviewExcel> util =
|
||||
new ExcelUtil<>(CcdiProjectRiskPeopleOverviewExcel.class);
|
||||
util.exportExcel(response, rows, "风险人员总览");
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出风险明细
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.ruoyi.ccdi.project.domain.excel;
|
||||
|
||||
import com.ruoyi.common.annotation.Excel;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 风险人员总览导出对象
|
||||
*/
|
||||
@Data
|
||||
public class CcdiProjectRiskPeopleOverviewExcel {
|
||||
|
||||
@Excel(name = "姓名")
|
||||
private String name;
|
||||
|
||||
@Excel(name = "身份证号")
|
||||
private String idNo;
|
||||
|
||||
@Excel(name = "所属部门")
|
||||
private String department;
|
||||
|
||||
@Excel(name = "疑似违规数")
|
||||
private Integer riskCount;
|
||||
|
||||
@Excel(name = "风险等级")
|
||||
private String riskLevel;
|
||||
|
||||
@Excel(name = "命中模型数")
|
||||
private Integer modelCount;
|
||||
|
||||
@Excel(name = "核心异常点")
|
||||
private String riskPoint;
|
||||
}
|
||||
@@ -46,6 +46,14 @@ public interface CcdiProjectOverviewMapper {
|
||||
@Param("query") CcdiProjectRiskPeopleQueryDTO query
|
||||
);
|
||||
|
||||
/**
|
||||
* 查询风险人员总览导出列表
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 风险人员聚合列表
|
||||
*/
|
||||
List<CcdiProjectEmployeeRiskAggregateVO> selectRiskPeopleOverviewList(@Param("projectId") Long projectId);
|
||||
|
||||
/**
|
||||
* 查询中高风险TOP10
|
||||
*
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskModelPeopleQueryDTO;
|
||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskPeopleQueryDTO;
|
||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSuspiciousTransactionQueryDTO;
|
||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectEmployeeCreditNegativeExcel;
|
||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectRiskPeopleOverviewExcel;
|
||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectSuspiciousTransactionExcel;
|
||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeCreditNegativePageVO;
|
||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisDetailVO;
|
||||
@@ -102,6 +103,16 @@ public interface ICcdiProjectOverviewService {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出风险人员总览
|
||||
*
|
||||
* @param projectId 项目ID
|
||||
* @return 导出列表
|
||||
*/
|
||||
default List<CcdiProjectRiskPeopleOverviewExcel> exportRiskPeopleOverview(Long projectId) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一导出风险明细
|
||||
*
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskModelPeopleQueryDTO;
|
||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskPeopleQueryDTO;
|
||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSuspiciousTransactionQueryDTO;
|
||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectEmployeeCreditNegativeExcel;
|
||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectRiskPeopleOverviewExcel;
|
||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectSuspiciousTransactionExcel;
|
||||
import com.ruoyi.ccdi.project.domain.entity.CcdiProjectOverviewEmployeeResult;
|
||||
import com.ruoyi.ccdi.project.domain.vo.CcdiBankStatementListVO;
|
||||
@@ -228,6 +229,16 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CcdiProjectRiskPeopleOverviewExcel> exportRiskPeopleOverview(Long projectId) {
|
||||
ensureProjectExists(projectId);
|
||||
|
||||
return defaultList(overviewMapper.selectRiskPeopleOverviewList(projectId)).stream()
|
||||
.map(aggregate -> buildRiskPeopleItem(projectId, aggregate))
|
||||
.map(this::buildRiskPeopleExcelRow)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CcdiProjectEmployeeCreditNegativePageVO getEmployeeCreditNegative(
|
||||
CcdiProjectEmployeeCreditNegativeQueryDTO queryDTO
|
||||
@@ -337,6 +348,18 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi
|
||||
return item;
|
||||
}
|
||||
|
||||
private CcdiProjectRiskPeopleOverviewExcel buildRiskPeopleExcelRow(CcdiProjectRiskPeopleOverviewItemVO item) {
|
||||
CcdiProjectRiskPeopleOverviewExcel row = new CcdiProjectRiskPeopleOverviewExcel();
|
||||
row.setName(item.getName());
|
||||
row.setIdNo(item.getIdNo());
|
||||
row.setDepartment(item.getDepartment());
|
||||
row.setRiskCount(item.getRiskCount());
|
||||
row.setRiskLevel(item.getRiskLevel());
|
||||
row.setModelCount(item.getModelCount());
|
||||
row.setRiskPoint(item.getRiskPoint());
|
||||
return row;
|
||||
}
|
||||
|
||||
private void ensureProjectExists(Long projectId) {
|
||||
getRequiredProject(projectId);
|
||||
}
|
||||
|
||||
@@ -223,32 +223,48 @@
|
||||
and del_flag = '0'
|
||||
</select>
|
||||
|
||||
<sql id="riskPeopleOverviewSelectColumns">
|
||||
result.staff_id_card,
|
||||
result.staff_name,
|
||||
result.dept_id,
|
||||
result.dept_name,
|
||||
result.rule_count,
|
||||
result.model_count,
|
||||
result.hit_count,
|
||||
null as top_rule_code,
|
||||
null as top_rule_name,
|
||||
result.risk_point,
|
||||
result.risk_level_code,
|
||||
case
|
||||
when result.risk_level_code = 'HIGH' then '高风险'
|
||||
when result.risk_level_code = 'MEDIUM' then '中风险'
|
||||
else '低风险'
|
||||
end as risk_level_name,
|
||||
case
|
||||
when result.risk_level_code = 'HIGH' then 1
|
||||
when result.risk_level_code = 'MEDIUM' then 2
|
||||
else 3
|
||||
end as risk_level_sort
|
||||
</sql>
|
||||
|
||||
<sql id="riskPeopleOverviewOrderBy">
|
||||
order by risk_level_sort asc, result.model_count desc, result.rule_count desc, result.staff_id_card asc
|
||||
</sql>
|
||||
|
||||
<select id="selectRiskPeopleOverviewPage" resultMap="EmployeeRiskAggregateResultMap">
|
||||
select
|
||||
result.staff_id_card,
|
||||
result.staff_name,
|
||||
result.dept_id,
|
||||
result.dept_name,
|
||||
result.rule_count,
|
||||
result.model_count,
|
||||
result.hit_count,
|
||||
null as top_rule_code,
|
||||
null as top_rule_name,
|
||||
result.risk_point,
|
||||
result.risk_level_code,
|
||||
case
|
||||
when result.risk_level_code = 'HIGH' then '高风险'
|
||||
when result.risk_level_code = 'MEDIUM' then '中风险'
|
||||
else '低风险'
|
||||
end as risk_level_name,
|
||||
case
|
||||
when result.risk_level_code = 'HIGH' then 1
|
||||
when result.risk_level_code = 'MEDIUM' then 2
|
||||
else 3
|
||||
end as risk_level_sort
|
||||
<include refid="riskPeopleOverviewSelectColumns"/>
|
||||
from ccdi_project_overview_employee_result result
|
||||
where result.project_id = #{query.projectId}
|
||||
order by risk_level_sort asc, result.model_count desc, result.rule_count desc, result.staff_id_card asc
|
||||
<include refid="riskPeopleOverviewOrderBy"/>
|
||||
</select>
|
||||
|
||||
<select id="selectRiskPeopleOverviewList" resultMap="EmployeeRiskAggregateResultMap">
|
||||
select
|
||||
<include refid="riskPeopleOverviewSelectColumns"/>
|
||||
from ccdi_project_overview_employee_result result
|
||||
where result.project_id = #{projectId}
|
||||
<include refid="riskPeopleOverviewOrderBy"/>
|
||||
</select>
|
||||
|
||||
<select id="selectTopRiskPeopleByProjectId" resultMap="EmployeeRiskAggregateResultMap">
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.ruoyi.ccdi.project.controller;
|
||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectEmployeeCreditNegativeQueryDTO;
|
||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectPersonAnalysisDetailQueryDTO;
|
||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSuspiciousTransactionQueryDTO;
|
||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectRiskPeopleOverviewExcel;
|
||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeCreditNegativeItemVO;
|
||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeCreditNegativePageVO;
|
||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectSuspiciousTransactionExcel;
|
||||
@@ -15,8 +16,10 @@ import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskPeopleOverviewVO;
|
||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectSuspiciousTransactionPageVO;
|
||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectTopRiskPeopleVO;
|
||||
import com.ruoyi.ccdi.project.service.ICcdiProjectOverviewService;
|
||||
import com.ruoyi.common.annotation.Excel;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
@@ -289,4 +292,53 @@ class CcdiProjectOverviewControllerTest {
|
||||
assertEquals("/risk-details/export", postMapping.value()[0]);
|
||||
assertNotNull(operation);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldExposeRiskPeopleExportContract() throws Exception {
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
CcdiProjectRiskPeopleOverviewExcel row = new CcdiProjectRiskPeopleOverviewExcel();
|
||||
row.setName("张三");
|
||||
row.setRiskPoint("薪酬异常");
|
||||
when(overviewService.exportRiskPeopleOverview(40L)).thenReturn(List.of(row));
|
||||
|
||||
controller.exportRiskPeople(response, 40L);
|
||||
|
||||
verify(overviewService).exportRiskPeopleOverview(40L);
|
||||
assertTrue(response.getContentType().startsWith("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"));
|
||||
assertTrue(response.getContentAsByteArray().length > 0);
|
||||
|
||||
Method controllerMethod = CcdiProjectOverviewController.class.getMethod(
|
||||
"exportRiskPeople",
|
||||
jakarta.servlet.http.HttpServletResponse.class,
|
||||
Long.class
|
||||
);
|
||||
PostMapping postMapping = controllerMethod.getAnnotation(PostMapping.class);
|
||||
Operation operation = controllerMethod.getAnnotation(Operation.class);
|
||||
PreAuthorize preAuthorize = controllerMethod.getAnnotation(PreAuthorize.class);
|
||||
|
||||
assertNotNull(postMapping);
|
||||
assertEquals("/risk-people/export", postMapping.value()[0]);
|
||||
assertNotNull(operation);
|
||||
assertEquals("导出风险人员总览", operation.summary());
|
||||
assertNotNull(preAuthorize);
|
||||
assertEquals("@ss.hasPermi('ccdi:project:query')", preAuthorize.value());
|
||||
|
||||
Method serviceMethod = ICcdiProjectOverviewService.class.getMethod("exportRiskPeopleOverview", Long.class);
|
||||
assertEquals(List.class, serviceMethod.getReturnType());
|
||||
|
||||
assertExcelColumnName("name", "姓名");
|
||||
assertExcelColumnName("idNo", "身份证号");
|
||||
assertExcelColumnName("department", "所属部门");
|
||||
assertExcelColumnName("riskCount", "疑似违规数");
|
||||
assertExcelColumnName("riskLevel", "风险等级");
|
||||
assertExcelColumnName("modelCount", "命中模型数");
|
||||
assertExcelColumnName("riskPoint", "核心异常点");
|
||||
}
|
||||
|
||||
private void assertExcelColumnName(String fieldName, String expectedColumnName) throws Exception {
|
||||
Field field = CcdiProjectRiskPeopleOverviewExcel.class.getDeclaredField(fieldName);
|
||||
Excel excel = field.getAnnotation(Excel.class);
|
||||
assertNotNull(excel);
|
||||
assertEquals(expectedColumnName, excel.name());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,21 +14,31 @@ class CcdiProjectOverviewMapperSqlTest {
|
||||
void shouldReadOverviewQueriesFromEmployeeResultTable() throws Exception {
|
||||
String xml = Files.readString(Path.of("src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml"));
|
||||
String riskPeopleSql = extractSelect(xml, "selectRiskPeopleOverviewPage");
|
||||
String riskPeopleExportSql = extractSelect(xml, "selectRiskPeopleOverviewList");
|
||||
String topRiskPeopleSql = extractSelect(xml, "selectTopRiskPeopleByProjectId");
|
||||
String riskModelCardsSql = extractSelect(xml, "selectRiskModelCardsByProjectId");
|
||||
String riskModelPeopleSql = extractSelect(xml, "selectRiskModelPeoplePage");
|
||||
|
||||
assertTrue(xml.contains("<sql id=\"riskPeopleOverviewSelectColumns\">"), xml);
|
||||
assertTrue(xml.contains("<sql id=\"riskPeopleOverviewOrderBy\">"), xml);
|
||||
assertTrue(riskPeopleSql.contains("from ccdi_project_overview_employee_result"));
|
||||
assertTrue(riskPeopleSql.contains("result.project_id = #{query.projectId}"), riskPeopleSql);
|
||||
assertTrue(riskPeopleSql.contains("<include refid=\"riskPeopleOverviewSelectColumns\"/>"), riskPeopleSql);
|
||||
assertTrue(riskPeopleSql.contains("<include refid=\"riskPeopleOverviewOrderBy\"/>"), riskPeopleSql);
|
||||
assertTrue(
|
||||
riskPeopleSql.contains(
|
||||
xml.contains(
|
||||
"order by risk_level_sort asc, result.model_count desc, result.rule_count desc, result.staff_id_card asc"
|
||||
),
|
||||
riskPeopleSql
|
||||
xml
|
||||
);
|
||||
assertFalse(riskPeopleSql.contains("limit 10"), riskPeopleSql);
|
||||
assertFalse(riskPeopleSql.contains("resolvedEmployeeRiskBaseSql"));
|
||||
|
||||
assertTrue(riskPeopleExportSql.contains("from ccdi_project_overview_employee_result"), riskPeopleExportSql);
|
||||
assertTrue(riskPeopleExportSql.contains("result.project_id = #{projectId}"), riskPeopleExportSql);
|
||||
assertTrue(riskPeopleExportSql.contains("<include refid=\"riskPeopleOverviewSelectColumns\"/>"), riskPeopleExportSql);
|
||||
assertTrue(riskPeopleExportSql.contains("<include refid=\"riskPeopleOverviewOrderBy\"/>"), riskPeopleExportSql);
|
||||
|
||||
assertTrue(topRiskPeopleSql.contains("from ccdi_project_overview_employee_result"));
|
||||
assertTrue(topRiskPeopleSql.contains("risk_level_code in ('HIGH', 'MEDIUM')"));
|
||||
assertFalse(topRiskPeopleSql.contains("resolvedEmployeeRiskBaseSql"));
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.ruoyi.ccdi.project.domain.dto.CcdiProjectPersonAnalysisDetailQueryDTO
|
||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskPeopleQueryDTO;
|
||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskModelPeopleQueryDTO;
|
||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectEmployeeCreditNegativeExcel;
|
||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectRiskPeopleOverviewExcel;
|
||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectSuspiciousTransactionExcel;
|
||||
import com.ruoyi.ccdi.project.domain.entity.CcdiProjectOverviewEmployeeResult;
|
||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeCreditNegativeItemVO;
|
||||
@@ -195,11 +196,46 @@ class CcdiProjectOverviewServiceImplTest {
|
||||
assertEquals("查看详情", topRiskPeople.getTopRiskList().getFirst().getActionLabel());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldExportRiskPeopleOverviewRowsWithSameFieldsAsPage() {
|
||||
CcdiProject project = new CcdiProject();
|
||||
project.setProjectId(40L);
|
||||
when(projectMapper.selectById(40L)).thenReturn(project);
|
||||
|
||||
CcdiProjectEmployeeRiskAggregateVO aggregate = new CcdiProjectEmployeeRiskAggregateVO();
|
||||
aggregate.setStaffName("李四");
|
||||
aggregate.setStaffIdCard("330000000000000001");
|
||||
aggregate.setDeptName("信息二部");
|
||||
aggregate.setRuleCount(5);
|
||||
aggregate.setHitCount(8);
|
||||
aggregate.setRiskLevelCode("HIGH");
|
||||
aggregate.setModelCount(3);
|
||||
aggregate.setRiskPoint("大额单笔收入、疑似兼职");
|
||||
when(overviewMapper.selectRiskPeopleOverviewList(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")
|
||||
));
|
||||
|
||||
List<CcdiProjectRiskPeopleOverviewExcel> rows = service.exportRiskPeopleOverview(40L);
|
||||
|
||||
assertEquals(1, rows.size());
|
||||
assertEquals("李四", rows.getFirst().getName());
|
||||
assertEquals("330000000000000001", rows.getFirst().getIdNo());
|
||||
assertEquals("信息二部", rows.getFirst().getDepartment());
|
||||
assertEquals(8, rows.getFirst().getRiskCount());
|
||||
assertEquals("高风险", rows.getFirst().getRiskLevel());
|
||||
assertEquals(3, rows.getFirst().getModelCount());
|
||||
assertEquals("大额单笔收入、疑似兼职", rows.getFirst().getRiskPoint());
|
||||
verify(overviewMapper).selectRiskPeopleOverviewList(40L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldThrowWhenProjectDoesNotExist() {
|
||||
when(projectMapper.selectById(99L)).thenReturn(null);
|
||||
|
||||
assertThrows(ServiceException.class, () -> service.getRiskPeopleOverview(buildRiskPeopleQuery(99L)));
|
||||
assertThrows(ServiceException.class, () -> service.exportRiskPeopleOverview(99L));
|
||||
assertThrows(ServiceException.class, () -> service.getTopRiskPeople(99L));
|
||||
assertThrows(ServiceException.class, () -> service.getRiskModelCards(99L));
|
||||
assertThrows(ServiceException.class, () -> service.getRiskModelPeople(buildRiskModelPeopleQuery(99L)));
|
||||
|
||||
Reference in New Issue
Block a user