补充项目详情风险人员导出能力
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.CcdiProjectRiskModelPeopleQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskPeopleQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskPeopleQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSuspiciousTransactionQueryDTO;
|
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.excel.CcdiProjectSuspiciousTransactionExcel;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeCreditNegativePageVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeCreditNegativePageVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisDetailVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisDetailVO;
|
||||||
@@ -145,6 +146,19 @@ public class CcdiProjectOverviewController extends BaseController {
|
|||||||
util.exportExcel(response, rows, "涉疑交易明细");
|
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("query") CcdiProjectRiskPeopleQueryDTO query
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询风险人员总览导出列表
|
||||||
|
*
|
||||||
|
* @param projectId 项目ID
|
||||||
|
* @return 风险人员聚合列表
|
||||||
|
*/
|
||||||
|
List<CcdiProjectEmployeeRiskAggregateVO> selectRiskPeopleOverviewList(@Param("projectId") Long projectId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询中高风险TOP10
|
* 查询中高风险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.CcdiProjectRiskPeopleQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSuspiciousTransactionQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSuspiciousTransactionQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectEmployeeCreditNegativeExcel;
|
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.excel.CcdiProjectSuspiciousTransactionExcel;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeCreditNegativePageVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeCreditNegativePageVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisDetailVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisDetailVO;
|
||||||
@@ -102,6 +103,16 @@ public interface ICcdiProjectOverviewService {
|
|||||||
return List.of();
|
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.CcdiProjectRiskPeopleQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSuspiciousTransactionQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSuspiciousTransactionQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectEmployeeCreditNegativeExcel;
|
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.excel.CcdiProjectSuspiciousTransactionExcel;
|
||||||
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;
|
||||||
@@ -228,6 +229,16 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi
|
|||||||
.toList();
|
.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
|
@Override
|
||||||
public CcdiProjectEmployeeCreditNegativePageVO getEmployeeCreditNegative(
|
public CcdiProjectEmployeeCreditNegativePageVO getEmployeeCreditNegative(
|
||||||
CcdiProjectEmployeeCreditNegativeQueryDTO queryDTO
|
CcdiProjectEmployeeCreditNegativeQueryDTO queryDTO
|
||||||
@@ -337,6 +348,18 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi
|
|||||||
return item;
|
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) {
|
private void ensureProjectExists(Long projectId) {
|
||||||
getRequiredProject(projectId);
|
getRequiredProject(projectId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -223,8 +223,7 @@
|
|||||||
and del_flag = '0'
|
and del_flag = '0'
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectRiskPeopleOverviewPage" resultMap="EmployeeRiskAggregateResultMap">
|
<sql id="riskPeopleOverviewSelectColumns">
|
||||||
select
|
|
||||||
result.staff_id_card,
|
result.staff_id_card,
|
||||||
result.staff_name,
|
result.staff_name,
|
||||||
result.dept_id,
|
result.dept_id,
|
||||||
@@ -246,9 +245,26 @@
|
|||||||
when result.risk_level_code = 'MEDIUM' then 2
|
when result.risk_level_code = 'MEDIUM' then 2
|
||||||
else 3
|
else 3
|
||||||
end as risk_level_sort
|
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
|
||||||
|
<include refid="riskPeopleOverviewSelectColumns"/>
|
||||||
from ccdi_project_overview_employee_result result
|
from ccdi_project_overview_employee_result result
|
||||||
where result.project_id = #{query.projectId}
|
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>
|
||||||
|
|
||||||
<select id="selectTopRiskPeopleByProjectId" resultMap="EmployeeRiskAggregateResultMap">
|
<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.CcdiProjectEmployeeCreditNegativeQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectPersonAnalysisDetailQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectPersonAnalysisDetailQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSuspiciousTransactionQueryDTO;
|
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.CcdiProjectEmployeeCreditNegativeItemVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeCreditNegativePageVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeCreditNegativePageVO;
|
||||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectSuspiciousTransactionExcel;
|
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.CcdiProjectSuspiciousTransactionPageVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectTopRiskPeopleVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectTopRiskPeopleVO;
|
||||||
import com.ruoyi.ccdi.project.service.ICcdiProjectOverviewService;
|
import com.ruoyi.ccdi.project.service.ICcdiProjectOverviewService;
|
||||||
|
import com.ruoyi.common.annotation.Excel;
|
||||||
import com.ruoyi.common.core.domain.AjaxResult;
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -289,4 +292,53 @@ class CcdiProjectOverviewControllerTest {
|
|||||||
assertEquals("/risk-details/export", postMapping.value()[0]);
|
assertEquals("/risk-details/export", postMapping.value()[0]);
|
||||||
assertNotNull(operation);
|
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 {
|
void shouldReadOverviewQueriesFromEmployeeResultTable() throws Exception {
|
||||||
String xml = Files.readString(Path.of("src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml"));
|
String xml = Files.readString(Path.of("src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml"));
|
||||||
String riskPeopleSql = extractSelect(xml, "selectRiskPeopleOverviewPage");
|
String riskPeopleSql = extractSelect(xml, "selectRiskPeopleOverviewPage");
|
||||||
|
String riskPeopleExportSql = extractSelect(xml, "selectRiskPeopleOverviewList");
|
||||||
String topRiskPeopleSql = extractSelect(xml, "selectTopRiskPeopleByProjectId");
|
String topRiskPeopleSql = extractSelect(xml, "selectTopRiskPeopleByProjectId");
|
||||||
String riskModelCardsSql = extractSelect(xml, "selectRiskModelCardsByProjectId");
|
String riskModelCardsSql = extractSelect(xml, "selectRiskModelCardsByProjectId");
|
||||||
String riskModelPeopleSql = extractSelect(xml, "selectRiskModelPeoplePage");
|
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("from ccdi_project_overview_employee_result"));
|
||||||
assertTrue(riskPeopleSql.contains("result.project_id = #{query.projectId}"), riskPeopleSql);
|
assertTrue(riskPeopleSql.contains("result.project_id = #{query.projectId}"), riskPeopleSql);
|
||||||
|
assertTrue(riskPeopleSql.contains("<include refid=\"riskPeopleOverviewSelectColumns\"/>"), riskPeopleSql);
|
||||||
|
assertTrue(riskPeopleSql.contains("<include refid=\"riskPeopleOverviewOrderBy\"/>"), riskPeopleSql);
|
||||||
assertTrue(
|
assertTrue(
|
||||||
riskPeopleSql.contains(
|
xml.contains(
|
||||||
"order by risk_level_sort asc, result.model_count desc, result.rule_count desc, result.staff_id_card asc"
|
"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("limit 10"), riskPeopleSql);
|
||||||
assertFalse(riskPeopleSql.contains("resolvedEmployeeRiskBaseSql"));
|
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("from ccdi_project_overview_employee_result"));
|
||||||
assertTrue(topRiskPeopleSql.contains("risk_level_code in ('HIGH', 'MEDIUM')"));
|
assertTrue(topRiskPeopleSql.contains("risk_level_code in ('HIGH', 'MEDIUM')"));
|
||||||
assertFalse(topRiskPeopleSql.contains("resolvedEmployeeRiskBaseSql"));
|
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.CcdiProjectRiskPeopleQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskModelPeopleQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskModelPeopleQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectEmployeeCreditNegativeExcel;
|
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.excel.CcdiProjectSuspiciousTransactionExcel;
|
||||||
import com.ruoyi.ccdi.project.domain.entity.CcdiProjectOverviewEmployeeResult;
|
import com.ruoyi.ccdi.project.domain.entity.CcdiProjectOverviewEmployeeResult;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeCreditNegativeItemVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeCreditNegativeItemVO;
|
||||||
@@ -195,11 +196,46 @@ class CcdiProjectOverviewServiceImplTest {
|
|||||||
assertEquals("查看详情", topRiskPeople.getTopRiskList().getFirst().getActionLabel());
|
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
|
@Test
|
||||||
void shouldThrowWhenProjectDoesNotExist() {
|
void shouldThrowWhenProjectDoesNotExist() {
|
||||||
when(projectMapper.selectById(99L)).thenReturn(null);
|
when(projectMapper.selectById(99L)).thenReturn(null);
|
||||||
|
|
||||||
assertThrows(ServiceException.class, () -> service.getRiskPeopleOverview(buildRiskPeopleQuery(99L)));
|
assertThrows(ServiceException.class, () -> service.getRiskPeopleOverview(buildRiskPeopleQuery(99L)));
|
||||||
|
assertThrows(ServiceException.class, () -> service.exportRiskPeopleOverview(99L));
|
||||||
assertThrows(ServiceException.class, () -> service.getTopRiskPeople(99L));
|
assertThrows(ServiceException.class, () -> service.getTopRiskPeople(99L));
|
||||||
assertThrows(ServiceException.class, () -> service.getRiskModelCards(99L));
|
assertThrows(ServiceException.class, () -> service.getRiskModelCards(99L));
|
||||||
assertThrows(ServiceException.class, () -> service.getRiskModelPeople(buildRiskModelPeopleQuery(99L)));
|
assertThrows(ServiceException.class, () -> service.getRiskModelPeople(buildRiskModelPeopleQuery(99L)));
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# 2026-03-30 项目详情风险人员导出后端实施记录
|
||||||
|
|
||||||
|
## 设计文档
|
||||||
|
|
||||||
|
- 已核对设计文档路径:`docs/design/2026-03-30-project-detail-risk-people-export-design.md`
|
||||||
|
|
||||||
|
## 本次实施内容
|
||||||
|
|
||||||
|
- 在 `CcdiProjectOverviewController` 新增 `POST /ccdi/project/overview/risk-people/export` 导出入口,沿用现有 `ccdi:project:query` 权限。
|
||||||
|
- 新增 `CcdiProjectRiskPeopleOverviewExcel` 导出对象,导出列仅覆盖页面展示字段:姓名、身份证号、所属部门、疑似违规数、风险等级、命中模型数、核心异常点。
|
||||||
|
- 在 `ICcdiProjectOverviewService` / `CcdiProjectOverviewServiceImpl` 补齐 `exportRiskPeopleOverview(Long projectId)`,服务层先复用 `buildRiskPeopleItem(...)` 统一页面口径,再映射为 Excel 行对象,避免页面与导出出现两套字段拼装逻辑。
|
||||||
|
- 在 `CcdiProjectOverviewMapper` / `CcdiProjectOverviewMapper.xml` 增加 `selectRiskPeopleOverviewList`,并抽取 `riskPeopleOverviewSelectColumns`、`riskPeopleOverviewOrderBy` 两个 SQL 片段,让分页查询与导出查询共用同一套列映射与排序规则。
|
||||||
|
|
||||||
|
## 约束说明
|
||||||
|
|
||||||
|
- 未新增筛选条件。
|
||||||
|
- 未新增菜单、路由或权限标识。
|
||||||
|
- 未引入平行接口或补丁式实现,仍走 `Controller -> Service -> Mapper` 既有结果总览链路。
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# 2026-03-30 项目详情风险人员导出前端实施记录
|
||||||
|
|
||||||
|
## 设计文档
|
||||||
|
|
||||||
|
- 已核对设计文档路径:`docs/design/2026-03-30-project-detail-risk-people-export-design.md`
|
||||||
|
|
||||||
|
## 本次实施内容
|
||||||
|
|
||||||
|
- 在 `ruoyi-ui/src/views/ccdiProject/components/detail/RiskPeopleSection.vue` 直接给“导出”按钮绑定 `handleRiskPeopleExport`。
|
||||||
|
- 组件内直接调用仓库现成的 `this.download(...)` 下载能力,请求路径为 `ccdi/project/overview/risk-people/export`,参数仅传 `projectId`。
|
||||||
|
- 下载文件名口径为 `风险人员总览_${projectId}_${timestamp}.xlsx`,与若依现有导出写法保持一致。
|
||||||
|
|
||||||
|
## 为什么采用当前实现
|
||||||
|
|
||||||
|
- 直接使用 `this.download(...)` 是仓库内现成模式,改动最小,能够与现有鉴权、二进制下载流程保持一致。
|
||||||
|
- 未额外新增 `projectOverview.js` 导出封装,是因为本次只需单一导出动作,继续放在 `RiskPeopleSection.vue` 内即可满足需求,避免平行抽象。
|
||||||
|
- 未新增导出弹窗、加载状态或分页导出参数,保持实现最短路径,确保“导出当前项目全部风险人员”这一业务口径不被分页状态干扰。
|
||||||
|
|
||||||
|
## 影响范围
|
||||||
|
|
||||||
|
- 本次前端仅修改 `RiskPeopleSection.vue` 与对应静态结构测试。
|
||||||
|
- 未改动 `loadRiskPeoplePage`、`handlePageChange`、`handleViewProject` 和 `sectionData -> localRows` 的既有逻辑。
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
# 2026-03-30 项目详情风险人员导出后端验证记录
|
||||||
|
|
||||||
|
## 设计文档
|
||||||
|
|
||||||
|
- 已核对设计文档路径:`docs/design/2026-03-30-project-detail-risk-people-export-design.md`
|
||||||
|
|
||||||
|
## 执行命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn -pl ccdi-project -Dtest=CcdiProjectOverviewControllerTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
## 验证结果
|
||||||
|
|
||||||
|
- 本次 Maven 验证未能进入本需求测试断言阶段,编译阶段被仓库内既有无关问题阻塞。
|
||||||
|
- 阻塞信息为:
|
||||||
|
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/excel/CcdiIdCardExcelRow.java`
|
||||||
|
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImpl.java`
|
||||||
|
- 缺少 `com.alibaba.excel` / `com.alibaba.excel.annotation.ExcelProperty`
|
||||||
|
- 因此当前无法在本地完成计划中的 `CcdiProjectOverviewControllerTest`、`CcdiProjectOverviewMapperSqlTest`、`CcdiProjectOverviewServiceImplTest` Maven 回归。
|
||||||
|
|
||||||
|
## 已完成的静态核对
|
||||||
|
|
||||||
|
- 导出接口路径已接通为 `POST /ccdi/project/overview/risk-people/export`。
|
||||||
|
- Service 已新增 `exportRiskPeopleOverview(Long projectId)`。
|
||||||
|
- 导出查询 `selectRiskPeopleOverviewList` 已与分页查询共用字段片段 `riskPeopleOverviewSelectColumns` 和排序片段 `riskPeopleOverviewOrderBy`。
|
||||||
|
- 导出字段顺序与页面表格字段保持一致:姓名、身份证号、所属部门、疑似违规数、风险等级、命中模型数、核心异常点。
|
||||||
|
|
||||||
|
## 结论
|
||||||
|
|
||||||
|
- 需求代码已补齐,但后端自动化回归仍受仓库现有 EasyExcel 编译问题影响,待该阻塞解除后需重新执行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn -pl ccdi-project -Dtest=CcdiProjectOverviewControllerTest,CcdiProjectOverviewMapperSqlTest,CcdiProjectOverviewServiceImplTest test
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2026-03-30 真机接口补充验证
|
||||||
|
|
||||||
|
### 执行命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./bin/restart_java_backend.sh restart
|
||||||
|
curl -sS -H 'Content-Type: application/json' \
|
||||||
|
-d '{"username":"admin","password":"admin123"}' \
|
||||||
|
http://127.0.0.1:62318/login/test
|
||||||
|
curl -sS -H 'Authorization: Bearer <token>' \
|
||||||
|
'http://127.0.0.1:62318/ccdi/project/overview/risk-people?projectId=62&pageNum=1&pageSize=5'
|
||||||
|
curl -sS -D /tmp/risk_people_export_headers.txt \
|
||||||
|
-o /tmp/risk_people_export.xlsx \
|
||||||
|
-H 'Authorization: Bearer <token>' \
|
||||||
|
-X POST 'http://127.0.0.1:62318/ccdi/project/overview/risk-people/export?projectId=62'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 结果
|
||||||
|
|
||||||
|
- 后端已成功重启并监听 `62318`。
|
||||||
|
- `POST /login/test` 使用 JSON 请求体可成功返回 token。
|
||||||
|
- `GET /ccdi/project/overview/risk-people?projectId=62&pageNum=1&pageSize=5` 返回 `200`,分页结果为 `total = 17`、`pageNum = 1`、`pageSize = 5`,首行数据为 `郑强`。
|
||||||
|
- `POST /ccdi/project/overview/risk-people/export?projectId=62` 返回 `200`,响应头 `Content-Type` 为 `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8`。
|
||||||
|
- 导出文件 `/tmp/risk_people_export.xlsx` 成功生成,sheet 维度为 `A1:G18`,包含 1 行表头 + 17 行数据。
|
||||||
|
- 抽样核对导出表头为:`姓名`、`身份证号`、`所属部门`、`疑似违规数`、`风险等级`、`命中模型数`、`核心异常点`。
|
||||||
|
- 抽样核对导出首行数据为 `郑强 / 180516198412041355 / 若依科技 / 23 / 高风险 / 8`,与页面列表口径一致。
|
||||||
|
- 后端日志确认导出请求命中 `selectRiskPeopleOverviewList`,并返回 `17` 条记录。
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# 2026-03-30 项目详情风险人员导出前端验证记录
|
||||||
|
|
||||||
|
## 设计文档
|
||||||
|
|
||||||
|
- 已核对设计文档路径:`docs/design/2026-03-30-project-detail-risk-people-export-design.md`
|
||||||
|
|
||||||
|
## 执行命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node ruoyi-ui/tests/unit/preliminary-check-risk-people-binding.test.js
|
||||||
|
node ruoyi-ui/tests/unit/preliminary-check-risk-people-export.test.js
|
||||||
|
node ruoyi-ui/tests/unit/preliminary-check-risk-people-pagination.test.js
|
||||||
|
node ruoyi-ui/tests/unit/preliminary-check-project-analysis-entry.test.js
|
||||||
|
```
|
||||||
|
|
||||||
|
## 通过情况
|
||||||
|
|
||||||
|
- 以上 4 条静态结构回归均执行通过。
|
||||||
|
|
||||||
|
## 核对结论
|
||||||
|
|
||||||
|
- “导出”按钮已绑定 `handleRiskPeopleExport`。
|
||||||
|
- 下载路径为 `ccdi/project/overview/risk-people/export`。
|
||||||
|
- 下载参数仅包含 `projectId`,不会把当前分页参数带入导出。
|
||||||
|
- `loadRiskPeoplePage`、分页切换和“查看项目”事件发射逻辑未受本次改动影响。
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="risk-people-section">
|
<section class="risk-people-section">
|
||||||
<div class="section-toolbar">
|
<div class="section-toolbar">
|
||||||
<el-button size="mini" type="text">导出</el-button>
|
<el-button size="mini" type="text" @click="handleRiskPeopleExport">导出</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-table v-loading="tableLoading" :data="overviewList" class="people-table">
|
<el-table v-loading="tableLoading" :data="overviewList" class="people-table">
|
||||||
@@ -205,6 +205,18 @@ export default {
|
|||||||
resolveModelTagStyle(tag) {
|
resolveModelTagStyle(tag) {
|
||||||
return CORE_TAG_PALETTE[tag.modelCode] || {};
|
return CORE_TAG_PALETTE[tag.modelCode] || {};
|
||||||
},
|
},
|
||||||
|
handleRiskPeopleExport() {
|
||||||
|
if (!this.projectId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.download(
|
||||||
|
"ccdi/project/overview/risk-people/export",
|
||||||
|
{
|
||||||
|
projectId: this.projectId,
|
||||||
|
},
|
||||||
|
`风险人员总览_${this.projectId}_${new Date().getTime()}.xlsx`
|
||||||
|
);
|
||||||
|
},
|
||||||
handleViewProject(row) {
|
handleViewProject(row) {
|
||||||
this.$emit("view-project-analysis", row);
|
this.$emit("view-project-analysis", row);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ const source = fs.readFileSync(
|
|||||||
"pageSize",
|
"pageSize",
|
||||||
"total",
|
"total",
|
||||||
"riskLevelType",
|
"riskLevelType",
|
||||||
|
'@click="handleRiskPeopleExport"',
|
||||||
].forEach((token) => assert(source.includes(token), token));
|
].forEach((token) => assert(source.includes(token), token));
|
||||||
|
|
||||||
assert(!source.includes("sectionData.topRiskList"), "不应再绑定TOP10列表");
|
assert(!source.includes("sectionData.topRiskList"), "不应再绑定TOP10列表");
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
const assert = require("assert");
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const source = fs.readFileSync(
|
||||||
|
path.resolve(__dirname, "../../src/views/ccdiProject/components/detail/RiskPeopleSection.vue"),
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
|
||||||
|
[
|
||||||
|
"handleRiskPeopleExport",
|
||||||
|
"this.download(",
|
||||||
|
"ccdi/project/overview/risk-people/export",
|
||||||
|
"projectId: this.projectId",
|
||||||
|
"风险人员总览_",
|
||||||
|
].forEach((token) => assert(source.includes(token), token));
|
||||||
|
|
||||||
|
assert(source.includes('@click="handleRiskPeopleExport"'), "导出按钮必须绑定导出事件");
|
||||||
Reference in New Issue
Block a user