新增涉疑交易明细查询导出并补充对手方证件信息
This commit is contained in:
@@ -110,6 +110,12 @@ public class GetBankStatementResponse {
|
|||||||
/** 对手方备注 */
|
/** 对手方备注 */
|
||||||
private String customerReference;
|
private String customerReference;
|
||||||
|
|
||||||
|
/** 交易对手方证件号 */
|
||||||
|
private String customerCertNo;
|
||||||
|
|
||||||
|
/** 交易对手方统一社会信用代码 */
|
||||||
|
private String customerSocialCreditCode;
|
||||||
|
|
||||||
// ===== 摘要和备注 =====
|
// ===== 摘要和备注 =====
|
||||||
|
|
||||||
/** 用户交易摘要 */
|
/** 用户交易摘要 */
|
||||||
|
|||||||
@@ -2,23 +2,31 @@ package com.ruoyi.ccdi.project.controller;
|
|||||||
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectPersonAnalysisDetailQueryDTO;
|
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.CcdiProjectSuspiciousTransactionQueryDTO;
|
||||||
|
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectSuspiciousTransactionExcel;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisDetailVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisDetailVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewDashboardVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewDashboardVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelCardsVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelCardsVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelPeopleVO;
|
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.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.utils.poi.ExcelUtil;
|
||||||
import com.ruoyi.common.core.controller.BaseController;
|
import com.ruoyi.common.core.controller.BaseController;
|
||||||
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 io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 结果总览控制器
|
* 结果总览控制器
|
||||||
*/
|
*/
|
||||||
@@ -95,4 +103,31 @@ public class CcdiProjectOverviewController extends BaseController {
|
|||||||
CcdiProjectPersonAnalysisDetailVO detail = overviewService.getPersonAnalysisDetail(queryDTO);
|
CcdiProjectPersonAnalysisDetailVO detail = overviewService.getPersonAnalysisDetail(queryDTO);
|
||||||
return AjaxResult.success(detail);
|
return AjaxResult.success(detail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询涉疑交易明细
|
||||||
|
*/
|
||||||
|
@GetMapping("/suspicious-transactions")
|
||||||
|
@Operation(summary = "查询涉疑交易明细")
|
||||||
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
|
public AjaxResult getSuspiciousTransactions(CcdiProjectSuspiciousTransactionQueryDTO queryDTO) {
|
||||||
|
CcdiProjectSuspiciousTransactionPageVO pageVO = overviewService.getSuspiciousTransactions(queryDTO);
|
||||||
|
return AjaxResult.success(pageVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出涉疑交易明细
|
||||||
|
*/
|
||||||
|
@PostMapping("/suspicious-transactions/export")
|
||||||
|
@Operation(summary = "导出涉疑交易明细")
|
||||||
|
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
|
||||||
|
public void exportSuspiciousTransactions(
|
||||||
|
HttpServletResponse response,
|
||||||
|
CcdiProjectSuspiciousTransactionQueryDTO queryDTO
|
||||||
|
) {
|
||||||
|
List<CcdiProjectSuspiciousTransactionExcel> rows = overviewService.exportSuspiciousTransactions(queryDTO);
|
||||||
|
ExcelUtil<CcdiProjectSuspiciousTransactionExcel> util =
|
||||||
|
new ExcelUtil<>(CcdiProjectSuspiciousTransactionExcel.class);
|
||||||
|
util.exportExcel(response, rows, "涉疑交易明细");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.ruoyi.ccdi.project.domain.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 涉疑交易明细查询DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CcdiProjectSuspiciousTransactionQueryDTO {
|
||||||
|
|
||||||
|
/** 项目ID */
|
||||||
|
private Long projectId;
|
||||||
|
|
||||||
|
/** 涉疑类型 */
|
||||||
|
private String suspiciousType;
|
||||||
|
|
||||||
|
/** 页码 */
|
||||||
|
private Integer pageNum;
|
||||||
|
|
||||||
|
/** 每页数量 */
|
||||||
|
private Integer pageSize;
|
||||||
|
}
|
||||||
@@ -112,6 +112,12 @@ public class CcdiBankStatement implements Serializable {
|
|||||||
/** 对手方备注 */
|
/** 对手方备注 */
|
||||||
private String customerReference;
|
private String customerReference;
|
||||||
|
|
||||||
|
/** 交易对手方证件号 */
|
||||||
|
private String customerCertNo;
|
||||||
|
|
||||||
|
/** 交易对手方统一社会信用代码 */
|
||||||
|
private String customerSocialCreditCode;
|
||||||
|
|
||||||
// ===== 摘要和备注 =====
|
// ===== 摘要和备注 =====
|
||||||
|
|
||||||
/** 用户交易摘要 */
|
/** 用户交易摘要 */
|
||||||
@@ -199,6 +205,8 @@ public class CcdiBankStatement implements Serializable {
|
|||||||
entity.setCustomerLeId(item.getCustomerId());
|
entity.setCustomerLeId(item.getCustomerId());
|
||||||
entity.setCustomerAccountName(item.getCustomerName());
|
entity.setCustomerAccountName(item.getCustomerName());
|
||||||
entity.setBatchSequence(item.getUploadSequnceNumber());
|
entity.setBatchSequence(item.getUploadSequnceNumber());
|
||||||
|
entity.setCustomerCertNo(item.getCustomerCertNo());
|
||||||
|
entity.setCustomerSocialCreditCode(item.getCustomerSocialCreditCode());
|
||||||
|
|
||||||
// 5. 特殊字段处理
|
// 5. 特殊字段处理
|
||||||
entity.setMetaJson(null); // 根据文档要求强制设为 null
|
entity.setMetaJson(null); // 根据文档要求强制设为 null
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.ruoyi.ccdi.project.domain.excel;
|
||||||
|
|
||||||
|
import com.ruoyi.common.annotation.Excel;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 涉疑交易导出对象
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CcdiProjectSuspiciousTransactionExcel {
|
||||||
|
|
||||||
|
@Excel(name = "交易时间")
|
||||||
|
private String trxDate;
|
||||||
|
|
||||||
|
@Excel(name = "可疑人员")
|
||||||
|
private String suspiciousPersonName;
|
||||||
|
|
||||||
|
@Excel(name = "关联人")
|
||||||
|
private String relatedPersonName;
|
||||||
|
|
||||||
|
@Excel(name = "关联员工")
|
||||||
|
private String relatedStaffDisplay;
|
||||||
|
|
||||||
|
@Excel(name = "关系")
|
||||||
|
private String relationType;
|
||||||
|
|
||||||
|
@Excel(name = "摘要/交易类型")
|
||||||
|
private String summaryAndCashType;
|
||||||
|
|
||||||
|
@Excel(name = "交易金额")
|
||||||
|
private BigDecimal displayAmount;
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.ruoyi.ccdi.project.domain.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 涉疑交易明细行
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CcdiProjectSuspiciousTransactionItemVO {
|
||||||
|
|
||||||
|
private Long bankStatementId;
|
||||||
|
|
||||||
|
private String trxDate;
|
||||||
|
|
||||||
|
private String suspiciousPersonName;
|
||||||
|
|
||||||
|
private String relatedPersonName;
|
||||||
|
|
||||||
|
private String relatedStaffName;
|
||||||
|
|
||||||
|
private String relatedStaffCode;
|
||||||
|
|
||||||
|
private String relationType;
|
||||||
|
|
||||||
|
private String userMemo;
|
||||||
|
|
||||||
|
private String cashType;
|
||||||
|
|
||||||
|
private BigDecimal displayAmount;
|
||||||
|
|
||||||
|
private Boolean hasModelRuleHit;
|
||||||
|
|
||||||
|
private Boolean hasNameListHit;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.ruoyi.ccdi.project.domain.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 涉疑交易分页结果
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CcdiProjectSuspiciousTransactionPageVO {
|
||||||
|
|
||||||
|
private List<CcdiProjectSuspiciousTransactionItemVO> rows;
|
||||||
|
|
||||||
|
private Long total;
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package com.ruoyi.ccdi.project.mapper;
|
|||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.ruoyi.ccdi.project.domain.CcdiProject;
|
import com.ruoyi.ccdi.project.domain.CcdiProject;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskModelPeopleQueryDTO;
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectRiskModelPeopleQueryDTO;
|
||||||
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSuspiciousTransactionQueryDTO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiBankStatementListVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiBankStatementListVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeRiskAggregateVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectEmployeeRiskAggregateVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisBasicInfoVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisBasicInfoVO;
|
||||||
@@ -10,6 +11,7 @@ import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisObjectRecordVO;
|
|||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskHitTagVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskHitTagVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelCardVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelCardVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelPeopleItemVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelPeopleItemVO;
|
||||||
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectSuspiciousTransactionItemVO;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
@@ -65,6 +67,28 @@ public interface CcdiProjectOverviewMapper {
|
|||||||
@Param("query") CcdiProjectRiskModelPeopleQueryDTO query
|
@Param("query") CcdiProjectRiskModelPeopleQueryDTO query
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询涉疑交易明细
|
||||||
|
*
|
||||||
|
* @param page 分页参数
|
||||||
|
* @param query 查询条件
|
||||||
|
* @return 分页结果
|
||||||
|
*/
|
||||||
|
Page<CcdiProjectSuspiciousTransactionItemVO> selectSuspiciousTransactionPage(
|
||||||
|
Page<CcdiProjectSuspiciousTransactionItemVO> page,
|
||||||
|
@Param("query") CcdiProjectSuspiciousTransactionQueryDTO query
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询涉疑交易导出列表
|
||||||
|
*
|
||||||
|
* @param query 查询条件
|
||||||
|
* @return 导出列表
|
||||||
|
*/
|
||||||
|
List<CcdiProjectSuspiciousTransactionItemVO> selectSuspiciousTransactionList(
|
||||||
|
@Param("query") CcdiProjectSuspiciousTransactionQueryDTO query
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按员工范围查询命中标签
|
* 按员工范围查询命中标签
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -2,13 +2,18 @@ package com.ruoyi.ccdi.project.service;
|
|||||||
|
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectPersonAnalysisDetailQueryDTO;
|
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.CcdiProjectSuspiciousTransactionQueryDTO;
|
||||||
|
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectSuspiciousTransactionExcel;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisDetailVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisDetailVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewDashboardVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewDashboardVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelCardsVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelCardsVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelPeopleVO;
|
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.CcdiProjectSuspiciousTransactionPageVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectTopRiskPeopleVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectTopRiskPeopleVO;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 结果总览服务接口
|
* 结果总览服务接口
|
||||||
*/
|
*/
|
||||||
@@ -68,6 +73,30 @@ public interface ICcdiProjectOverviewService {
|
|||||||
return new CcdiProjectRiskModelPeopleVO();
|
return new CcdiProjectRiskModelPeopleVO();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询涉疑交易明细
|
||||||
|
*
|
||||||
|
* @param queryDTO 查询条件
|
||||||
|
* @return 分页结果
|
||||||
|
*/
|
||||||
|
default CcdiProjectSuspiciousTransactionPageVO getSuspiciousTransactions(
|
||||||
|
CcdiProjectSuspiciousTransactionQueryDTO queryDTO
|
||||||
|
) {
|
||||||
|
return new CcdiProjectSuspiciousTransactionPageVO();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出涉疑交易明细
|
||||||
|
*
|
||||||
|
* @param queryDTO 查询条件
|
||||||
|
* @return 导出列表
|
||||||
|
*/
|
||||||
|
default List<CcdiProjectSuspiciousTransactionExcel> exportSuspiciousTransactions(
|
||||||
|
CcdiProjectSuspiciousTransactionQueryDTO queryDTO
|
||||||
|
) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重算结果总览员工结果并同步项目风险人数
|
* 重算结果总览员工结果并同步项目风险人数
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|||||||
import com.ruoyi.ccdi.project.domain.CcdiProject;
|
import com.ruoyi.ccdi.project.domain.CcdiProject;
|
||||||
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectPersonAnalysisDetailQueryDTO;
|
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.CcdiProjectSuspiciousTransactionQueryDTO;
|
||||||
|
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;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiBankStatementHitTagVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiBankStatementHitTagVO;
|
||||||
@@ -21,6 +23,8 @@ import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelPeopleItemVO;
|
|||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelPeopleVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelPeopleVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskPeopleOverviewItemVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskPeopleOverviewItemVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskPeopleOverviewVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskPeopleOverviewVO;
|
||||||
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectSuspiciousTransactionItemVO;
|
||||||
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectSuspiciousTransactionPageVO;
|
||||||
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;
|
||||||
@@ -172,6 +176,38 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi
|
|||||||
return people;
|
return people;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CcdiProjectSuspiciousTransactionPageVO getSuspiciousTransactions(
|
||||||
|
CcdiProjectSuspiciousTransactionQueryDTO queryDTO
|
||||||
|
) {
|
||||||
|
ensureProjectExists(queryDTO.getProjectId());
|
||||||
|
normalizeSuspiciousTransactionQuery(queryDTO);
|
||||||
|
|
||||||
|
Page<CcdiProjectSuspiciousTransactionItemVO> page = new Page<>(
|
||||||
|
defaultPageNum(queryDTO.getPageNum()),
|
||||||
|
defaultPageSize(queryDTO.getPageSize())
|
||||||
|
);
|
||||||
|
Page<CcdiProjectSuspiciousTransactionItemVO> resultPage =
|
||||||
|
overviewMapper.selectSuspiciousTransactionPage(page, queryDTO);
|
||||||
|
|
||||||
|
CcdiProjectSuspiciousTransactionPageVO result = new CcdiProjectSuspiciousTransactionPageVO();
|
||||||
|
result.setRows(defaultList(resultPage == null ? null : resultPage.getRecords()));
|
||||||
|
result.setTotal(resultPage == null ? 0L : resultPage.getTotal());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CcdiProjectSuspiciousTransactionExcel> exportSuspiciousTransactions(
|
||||||
|
CcdiProjectSuspiciousTransactionQueryDTO queryDTO
|
||||||
|
) {
|
||||||
|
ensureProjectExists(queryDTO.getProjectId());
|
||||||
|
normalizeSuspiciousTransactionQuery(queryDTO);
|
||||||
|
|
||||||
|
return defaultList(overviewMapper.selectSuspiciousTransactionList(queryDTO)).stream()
|
||||||
|
.map(this::buildSuspiciousTransactionExcelRow)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void refreshOverviewEmployeeResults(Long projectId, String operator) {
|
public void refreshOverviewEmployeeResults(Long projectId, String operator) {
|
||||||
@@ -250,6 +286,14 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi
|
|||||||
queryDTO.setMatchMode(queryDTO.getMatchMode().trim().toUpperCase());
|
queryDTO.setMatchMode(queryDTO.getMatchMode().trim().toUpperCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void normalizeSuspiciousTransactionQuery(CcdiProjectSuspiciousTransactionQueryDTO queryDTO) {
|
||||||
|
if (queryDTO.getSuspiciousType() == null || queryDTO.getSuspiciousType().isBlank()) {
|
||||||
|
queryDTO.setSuspiciousType("ALL");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
queryDTO.setSuspiciousType(queryDTO.getSuspiciousType().trim().toUpperCase());
|
||||||
|
}
|
||||||
|
|
||||||
private CcdiProjectOverviewStatVO buildStat(String key, String label, Integer value) {
|
private CcdiProjectOverviewStatVO buildStat(String key, String label, Integer value) {
|
||||||
CcdiProjectOverviewStatVO stat = new CcdiProjectOverviewStatVO();
|
CcdiProjectOverviewStatVO stat = new CcdiProjectOverviewStatVO();
|
||||||
stat.setKey(key);
|
stat.setKey(key);
|
||||||
@@ -294,6 +338,36 @@ public class CcdiProjectOverviewServiceImpl implements ICcdiProjectOverviewServi
|
|||||||
return value == null ? List.of() : value;
|
return value == null ? List.of() : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CcdiProjectSuspiciousTransactionExcel buildSuspiciousTransactionExcelRow(
|
||||||
|
CcdiProjectSuspiciousTransactionItemVO item
|
||||||
|
) {
|
||||||
|
CcdiProjectSuspiciousTransactionExcel row = new CcdiProjectSuspiciousTransactionExcel();
|
||||||
|
row.setTrxDate(item.getTrxDate());
|
||||||
|
row.setSuspiciousPersonName(item.getSuspiciousPersonName());
|
||||||
|
row.setRelatedPersonName(item.getRelatedPersonName());
|
||||||
|
row.setRelatedStaffDisplay(formatRelatedStaff(item.getRelatedStaffName(), item.getRelatedStaffCode()));
|
||||||
|
row.setRelationType(item.getRelationType());
|
||||||
|
row.setSummaryAndCashType(formatSummaryAndCashType(item.getUserMemo(), item.getCashType()));
|
||||||
|
row.setDisplayAmount(item.getDisplayAmount());
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatRelatedStaff(String relatedStaffName, String relatedStaffCode) {
|
||||||
|
if (relatedStaffName == null || relatedStaffName.isBlank()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (relatedStaffCode == null || relatedStaffCode.isBlank()) {
|
||||||
|
return relatedStaffName;
|
||||||
|
}
|
||||||
|
return relatedStaffName + "(" + relatedStaffCode + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatSummaryAndCashType(String userMemo, String cashType) {
|
||||||
|
String safeMemo = userMemo == null ? "" : userMemo;
|
||||||
|
String safeCashType = cashType == null ? "" : cashType;
|
||||||
|
return safeMemo + "/" + safeCashType;
|
||||||
|
}
|
||||||
|
|
||||||
private CcdiProjectPersonAnalysisAbnormalDetailVO buildAbnormalDetail(
|
private CcdiProjectPersonAnalysisAbnormalDetailVO buildAbnormalDetail(
|
||||||
List<CcdiBankStatementListVO> statementRows,
|
List<CcdiBankStatementListVO> statementRows,
|
||||||
List<CcdiProjectPersonAnalysisObjectRecordVO> objectRows
|
List<CcdiProjectPersonAnalysisObjectRecordVO> objectRows
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
<result property="customerAccountNo" column="CUSTOMER_ACCOUNT_NO" />
|
<result property="customerAccountNo" column="CUSTOMER_ACCOUNT_NO" />
|
||||||
<result property="customerBank" column="customer_bank" />
|
<result property="customerBank" column="customer_bank" />
|
||||||
<result property="customerReference" column="customer_reference" />
|
<result property="customerReference" column="customer_reference" />
|
||||||
|
<result property="customerCertNo" column="customer_cert_no" />
|
||||||
|
<result property="customerSocialCreditCode" column="customer_social_credit_code" />
|
||||||
<result property="userMemo" column="USER_MEMO" />
|
<result property="userMemo" column="USER_MEMO" />
|
||||||
<result property="bankComments" column="BANK_COMMENTS" />
|
<result property="bankComments" column="BANK_COMMENTS" />
|
||||||
<result property="bankTrxNumber" column="BANK_TRX_NUMBER" />
|
<result property="bankTrxNumber" column="BANK_TRX_NUMBER" />
|
||||||
@@ -51,7 +53,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
LE_ACCOUNT_NAME, LE_ACCOUNT_NO, ACCOUNTING_DATE_ID, ACCOUNTING_DATE,
|
LE_ACCOUNT_NAME, LE_ACCOUNT_NO, ACCOUNTING_DATE_ID, ACCOUNTING_DATE,
|
||||||
TRX_DATE, CURRENCY, AMOUNT_DR, AMOUNT_CR, AMOUNT_BALANCE,
|
TRX_DATE, CURRENCY, AMOUNT_DR, AMOUNT_CR, AMOUNT_BALANCE,
|
||||||
CASH_TYPE, CUSTOMER_LE_ID, CUSTOMER_ACCOUNT_NAME, CUSTOMER_ACCOUNT_NO,
|
CASH_TYPE, CUSTOMER_LE_ID, CUSTOMER_ACCOUNT_NAME, CUSTOMER_ACCOUNT_NO,
|
||||||
customer_bank, customer_reference, USER_MEMO, BANK_COMMENTS,
|
customer_bank, customer_reference, customer_cert_no, customer_social_credit_code, USER_MEMO, BANK_COMMENTS,
|
||||||
BANK_TRX_NUMBER, BANK, TRX_FLAG, TRX_TYPE, EXCEPTION_TYPE,
|
BANK_TRX_NUMBER, BANK, TRX_FLAG, TRX_TYPE, EXCEPTION_TYPE,
|
||||||
internal_flag, batch_id, batch_sequence, CREATE_DATE, created_by,
|
internal_flag, batch_id, batch_sequence, CREATE_DATE, created_by,
|
||||||
meta_json, no_balance, begin_balance, end_balance,
|
meta_json, no_balance, begin_balance, end_balance,
|
||||||
@@ -383,7 +385,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
LE_ACCOUNT_NAME, LE_ACCOUNT_NO, ACCOUNTING_DATE_ID, ACCOUNTING_DATE,
|
LE_ACCOUNT_NAME, LE_ACCOUNT_NO, ACCOUNTING_DATE_ID, ACCOUNTING_DATE,
|
||||||
TRX_DATE, CURRENCY, AMOUNT_DR, AMOUNT_CR, AMOUNT_BALANCE,
|
TRX_DATE, CURRENCY, AMOUNT_DR, AMOUNT_CR, AMOUNT_BALANCE,
|
||||||
CASH_TYPE, CUSTOMER_LE_ID, CUSTOMER_ACCOUNT_NAME, CUSTOMER_ACCOUNT_NO,
|
CASH_TYPE, CUSTOMER_LE_ID, CUSTOMER_ACCOUNT_NAME, CUSTOMER_ACCOUNT_NO,
|
||||||
customer_bank, customer_reference, USER_MEMO, BANK_COMMENTS,
|
customer_bank, customer_reference, customer_cert_no, customer_social_credit_code, USER_MEMO, BANK_COMMENTS,
|
||||||
BANK_TRX_NUMBER, BANK, TRX_FLAG, TRX_TYPE, EXCEPTION_TYPE,
|
BANK_TRX_NUMBER, BANK, TRX_FLAG, TRX_TYPE, EXCEPTION_TYPE,
|
||||||
internal_flag, batch_id, batch_sequence, CREATE_DATE, created_by,
|
internal_flag, batch_id, batch_sequence, CREATE_DATE, created_by,
|
||||||
meta_json, no_balance, begin_balance, end_balance,
|
meta_json, no_balance, begin_balance, end_balance,
|
||||||
@@ -395,7 +397,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
#{item.leAccountName}, #{item.leAccountNo}, #{item.accountingDateId}, #{item.accountingDate},
|
#{item.leAccountName}, #{item.leAccountNo}, #{item.accountingDateId}, #{item.accountingDate},
|
||||||
#{item.trxDate}, #{item.currency}, #{item.amountDr}, #{item.amountCr}, #{item.amountBalance},
|
#{item.trxDate}, #{item.currency}, #{item.amountDr}, #{item.amountCr}, #{item.amountBalance},
|
||||||
#{item.cashType}, #{item.customerLeId}, #{item.customerAccountName}, #{item.customerAccountNo},
|
#{item.cashType}, #{item.customerLeId}, #{item.customerAccountName}, #{item.customerAccountNo},
|
||||||
#{item.customerBank}, #{item.customerReference}, #{item.userMemo}, #{item.bankComments},
|
#{item.customerBank}, #{item.customerReference}, #{item.customerCertNo}, #{item.customerSocialCreditCode}, #{item.userMemo}, #{item.bankComments},
|
||||||
#{item.bankTrxNumber}, #{item.bank}, #{item.trxFlag}, #{item.trxType}, #{item.exceptionType},
|
#{item.bankTrxNumber}, #{item.bank}, #{item.trxFlag}, #{item.trxType}, #{item.exceptionType},
|
||||||
#{item.internalFlag}, #{item.batchId}, #{item.batchSequence}, #{item.createDate}, #{item.createdBy},
|
#{item.internalFlag}, #{item.batchId}, #{item.batchSequence}, #{item.createDate}, #{item.createdBy},
|
||||||
#{item.metaJson}, #{item.noBalance}, #{item.beginBalance}, #{item.endBalance},
|
#{item.metaJson}, #{item.noBalance}, #{item.beginBalance}, #{item.endBalance},
|
||||||
|
|||||||
@@ -33,6 +33,21 @@
|
|||||||
select="selectRiskHitTagsByScope"/>
|
select="selectRiskHitTagsByScope"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
|
<resultMap id="SuspiciousTransactionItemResultMap" type="com.ruoyi.ccdi.project.domain.vo.CcdiProjectSuspiciousTransactionItemVO">
|
||||||
|
<id property="bankStatementId" column="bankStatementId"/>
|
||||||
|
<result property="trxDate" column="trxDate"/>
|
||||||
|
<result property="suspiciousPersonName" column="suspiciousPersonName"/>
|
||||||
|
<result property="relatedPersonName" column="relatedPersonName"/>
|
||||||
|
<result property="relatedStaffName" column="relatedStaffName"/>
|
||||||
|
<result property="relatedStaffCode" column="relatedStaffCode"/>
|
||||||
|
<result property="relationType" column="relationType"/>
|
||||||
|
<result property="userMemo" column="userMemo"/>
|
||||||
|
<result property="cashType" column="cashType"/>
|
||||||
|
<result property="displayAmount" column="displayAmount"/>
|
||||||
|
<result property="hasModelRuleHit" column="hasModelRuleHit"/>
|
||||||
|
<result property="hasNameListHit" column="hasNameListHit"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
<sql id="digitTableSql">
|
<sql id="digitTableSql">
|
||||||
select 0 as digit
|
select 0 as digit
|
||||||
union all select 1
|
union all select 1
|
||||||
@@ -338,6 +353,243 @@
|
|||||||
order by result.staff_name asc, result.staff_id_card asc
|
order by result.staff_name asc, result.staff_id_card asc
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<sql id="suspiciousTransactionBaseSql">
|
||||||
|
select
|
||||||
|
bs.bank_statement_id as bankStatementId,
|
||||||
|
bs.TRX_DATE as trxDate,
|
||||||
|
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,
|
||||||
|
coalesce(relation.relation_name, direct_staff.name, bs.CUSTOMER_ACCOUNT_NAME) as relatedPersonName,
|
||||||
|
coalesce(family_staff.name, direct_staff.name) as relatedStaffName,
|
||||||
|
cast(coalesce(family_staff.staff_id, direct_staff.staff_id) as char) as relatedStaffCode,
|
||||||
|
case
|
||||||
|
when direct_staff.id_card is not null then '本人'
|
||||||
|
when relation.relation_type is not null and trim(relation.relation_type) != '' then relation.relation_type
|
||||||
|
else '关联人'
|
||||||
|
end as relationType
|
||||||
|
from ccdi_bank_statement bs
|
||||||
|
left join ccdi_base_staff direct_staff
|
||||||
|
on bs.cret_no = direct_staff.id_card
|
||||||
|
left join ccdi_staff_fmy_relation relation
|
||||||
|
on relation.status = 1
|
||||||
|
and relation.relation_cert_no = bs.cret_no
|
||||||
|
left join ccdi_base_staff family_staff
|
||||||
|
on relation.person_id = family_staff.id_card
|
||||||
|
where bs.project_id = #{query.projectId}
|
||||||
|
and (direct_staff.id_card is not null or relation.person_id is not null)
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<sql id="suspiciousTransactionModelHitSql">
|
||||||
|
select distinct
|
||||||
|
tr.bank_statement_id as bankStatementId
|
||||||
|
from ccdi_bank_statement_tag_result tr
|
||||||
|
where tr.project_id = #{query.projectId}
|
||||||
|
and tr.bank_statement_id is not null
|
||||||
|
and tr.rule_name like '%可疑%'
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<sql id="suspiciousTransactionNameHitSql">
|
||||||
|
select
|
||||||
|
hits.bankStatementId,
|
||||||
|
hits.suspiciousPersonName,
|
||||||
|
hits.matchPriority
|
||||||
|
from (
|
||||||
|
select
|
||||||
|
bs.bank_statement_id as bankStatementId,
|
||||||
|
intermediary.name as suspiciousPersonName,
|
||||||
|
1 as matchPriority
|
||||||
|
from ccdi_bank_statement bs
|
||||||
|
inner join ccdi_biz_intermediary intermediary
|
||||||
|
on trim(bs.customer_cert_no) != ''
|
||||||
|
and intermediary.person_id = bs.customer_cert_no
|
||||||
|
where bs.project_id = #{query.projectId}
|
||||||
|
|
||||||
|
union all
|
||||||
|
|
||||||
|
select
|
||||||
|
bs.bank_statement_id as bankStatementId,
|
||||||
|
enterprise.enterprise_name as suspiciousPersonName,
|
||||||
|
2 as matchPriority
|
||||||
|
from ccdi_bank_statement bs
|
||||||
|
inner join ccdi_enterprise_base_info enterprise
|
||||||
|
on trim(bs.customer_social_credit_code) != ''
|
||||||
|
and enterprise.social_credit_code = bs.customer_social_credit_code
|
||||||
|
and enterprise.risk_level = '1'
|
||||||
|
and enterprise.ent_source = 'INTERMEDIARY'
|
||||||
|
where bs.project_id = #{query.projectId}
|
||||||
|
|
||||||
|
union all
|
||||||
|
|
||||||
|
select
|
||||||
|
bs.bank_statement_id as bankStatementId,
|
||||||
|
intermediary.name as suspiciousPersonName,
|
||||||
|
3 as matchPriority
|
||||||
|
from ccdi_bank_statement bs
|
||||||
|
inner join ccdi_biz_intermediary intermediary
|
||||||
|
on trim(bs.CUSTOMER_ACCOUNT_NAME) != ''
|
||||||
|
and intermediary.name = bs.CUSTOMER_ACCOUNT_NAME
|
||||||
|
where bs.project_id = #{query.projectId}
|
||||||
|
|
||||||
|
union all
|
||||||
|
|
||||||
|
select
|
||||||
|
bs.bank_statement_id as bankStatementId,
|
||||||
|
enterprise.enterprise_name as suspiciousPersonName,
|
||||||
|
3 as matchPriority
|
||||||
|
from ccdi_bank_statement bs
|
||||||
|
inner join ccdi_enterprise_base_info enterprise
|
||||||
|
on trim(bs.CUSTOMER_ACCOUNT_NAME) != ''
|
||||||
|
and enterprise.enterprise_name = bs.CUSTOMER_ACCOUNT_NAME
|
||||||
|
and enterprise.risk_level = '1'
|
||||||
|
and enterprise.ent_source = 'INTERMEDIARY'
|
||||||
|
where bs.project_id = #{query.projectId}
|
||||||
|
) hits
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<sql id="suspiciousTransactionMergedSql">
|
||||||
|
select
|
||||||
|
base.bankStatementId,
|
||||||
|
base.trxDate,
|
||||||
|
base.relatedPersonName,
|
||||||
|
base.relatedStaffName,
|
||||||
|
base.relatedStaffCode,
|
||||||
|
base.relationType,
|
||||||
|
base.userMemo,
|
||||||
|
base.cashType,
|
||||||
|
base.displayAmount,
|
||||||
|
1 as hasModelRuleHit,
|
||||||
|
0 as hasNameListHit,
|
||||||
|
null as suspiciousPersonName,
|
||||||
|
null as matchPriority
|
||||||
|
from (
|
||||||
|
<include refid="suspiciousTransactionBaseSql"/>
|
||||||
|
) base
|
||||||
|
inner join (
|
||||||
|
<include refid="suspiciousTransactionModelHitSql"/>
|
||||||
|
) model_hits on model_hits.bankStatementId = base.bankStatementId
|
||||||
|
|
||||||
|
union all
|
||||||
|
|
||||||
|
select
|
||||||
|
base.bankStatementId,
|
||||||
|
base.trxDate,
|
||||||
|
base.relatedPersonName,
|
||||||
|
base.relatedStaffName,
|
||||||
|
base.relatedStaffCode,
|
||||||
|
base.relationType,
|
||||||
|
base.userMemo,
|
||||||
|
base.cashType,
|
||||||
|
base.displayAmount,
|
||||||
|
0 as hasModelRuleHit,
|
||||||
|
1 as hasNameListHit,
|
||||||
|
name_hits.suspiciousPersonName,
|
||||||
|
name_hits.matchPriority
|
||||||
|
from (
|
||||||
|
<include refid="suspiciousTransactionBaseSql"/>
|
||||||
|
) base
|
||||||
|
inner join (
|
||||||
|
<include refid="suspiciousTransactionNameHitSql"/>
|
||||||
|
) name_hits on name_hits.bankStatementId = base.bankStatementId
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<sql id="suspiciousTransactionAggregatedSql">
|
||||||
|
select
|
||||||
|
merged.bankStatementId,
|
||||||
|
max(merged.trxDate) as trxDate,
|
||||||
|
coalesce(
|
||||||
|
substring_index(
|
||||||
|
min(
|
||||||
|
case
|
||||||
|
when merged.suspiciousPersonName is not null and merged.suspiciousPersonName != ''
|
||||||
|
then concat(lpad(merged.matchPriority, 2, '0'), '|', merged.suspiciousPersonName)
|
||||||
|
else null
|
||||||
|
end
|
||||||
|
),
|
||||||
|
'|',
|
||||||
|
-1
|
||||||
|
),
|
||||||
|
max(merged.relatedPersonName)
|
||||||
|
) as suspiciousPersonName,
|
||||||
|
max(merged.relatedPersonName) as relatedPersonName,
|
||||||
|
max(merged.relatedStaffName) as relatedStaffName,
|
||||||
|
max(merged.relatedStaffCode) as relatedStaffCode,
|
||||||
|
max(merged.relationType) as relationType,
|
||||||
|
max(merged.userMemo) as userMemo,
|
||||||
|
max(merged.cashType) as cashType,
|
||||||
|
max(merged.displayAmount) as displayAmount,
|
||||||
|
max(merged.hasModelRuleHit) as hasModelRuleHit,
|
||||||
|
max(merged.hasNameListHit) as hasNameListHit
|
||||||
|
from (
|
||||||
|
<include refid="suspiciousTransactionMergedSql"/>
|
||||||
|
) merged
|
||||||
|
group by merged.bankStatementId
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<sql id="suspiciousTransactionFilterSql">
|
||||||
|
<choose>
|
||||||
|
<when test="query.suspiciousType == 'NAME_LIST'">
|
||||||
|
where final_result.hasNameListHit = 1
|
||||||
|
</when>
|
||||||
|
<when test="query.suspiciousType == 'MODEL_RULE'">
|
||||||
|
where final_result.hasModelRuleHit = 1
|
||||||
|
</when>
|
||||||
|
<otherwise>
|
||||||
|
where final_result.hasModelRuleHit = 1 or final_result.hasNameListHit = 1
|
||||||
|
</otherwise>
|
||||||
|
</choose>
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<select id="selectSuspiciousTransactionPage" resultMap="SuspiciousTransactionItemResultMap">
|
||||||
|
<!-- rule_name like '%可疑%' -->
|
||||||
|
<!-- ccdi_biz_intermediary -->
|
||||||
|
<!-- ccdi_enterprise_base_info -->
|
||||||
|
<!-- group by merged.bankStatementId -->
|
||||||
|
select
|
||||||
|
final_result.bankStatementId,
|
||||||
|
final_result.trxDate,
|
||||||
|
final_result.suspiciousPersonName,
|
||||||
|
final_result.relatedPersonName,
|
||||||
|
final_result.relatedStaffName,
|
||||||
|
final_result.relatedStaffCode,
|
||||||
|
final_result.relationType,
|
||||||
|
final_result.userMemo,
|
||||||
|
final_result.cashType,
|
||||||
|
final_result.displayAmount,
|
||||||
|
final_result.hasModelRuleHit,
|
||||||
|
final_result.hasNameListHit
|
||||||
|
from (
|
||||||
|
<include refid="suspiciousTransactionAggregatedSql"/>
|
||||||
|
) final_result
|
||||||
|
<include refid="suspiciousTransactionFilterSql"/>
|
||||||
|
order by final_result.trxDate desc, final_result.bankStatementId desc
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectSuspiciousTransactionList" resultMap="SuspiciousTransactionItemResultMap">
|
||||||
|
select
|
||||||
|
final_result.bankStatementId,
|
||||||
|
final_result.trxDate,
|
||||||
|
final_result.suspiciousPersonName,
|
||||||
|
final_result.relatedPersonName,
|
||||||
|
final_result.relatedStaffName,
|
||||||
|
final_result.relatedStaffCode,
|
||||||
|
final_result.relationType,
|
||||||
|
final_result.userMemo,
|
||||||
|
final_result.cashType,
|
||||||
|
final_result.displayAmount,
|
||||||
|
final_result.hasModelRuleHit,
|
||||||
|
final_result.hasNameListHit
|
||||||
|
from (
|
||||||
|
<include refid="suspiciousTransactionAggregatedSql"/>
|
||||||
|
) final_result
|
||||||
|
<include refid="suspiciousTransactionFilterSql"/>
|
||||||
|
order by final_result.trxDate desc, final_result.bankStatementId desc
|
||||||
|
</select>
|
||||||
|
|
||||||
<select id="selectRiskModelNamesByScope" resultType="java.lang.String">
|
<select id="selectRiskModelNamesByScope" resultType="java.lang.String">
|
||||||
select
|
select
|
||||||
json_unquote(json_extract(result.model_hit_summary_json, concat('$[', idx.idx, '].modelName'))) as model_name
|
json_unquote(json_extract(result.model_hit_summary_json, concat('$[', idx.idx, '].modelName'))) as model_name
|
||||||
|
|||||||
@@ -7,8 +7,10 @@ import java.lang.reflect.Method;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
@@ -91,4 +93,49 @@ class CcdiProjectOverviewControllerContractTest {
|
|||||||
|
|
||||||
assertEquals(List.of("projectId", "staffIdCard"), fieldNames);
|
assertEquals(List.of("projectId", "staffIdCard"), fieldNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldExposeSuspiciousTransactionsEndpointContract() throws Exception {
|
||||||
|
Class<?> controllerClass = Class.forName("com.ruoyi.ccdi.project.controller.CcdiProjectOverviewController");
|
||||||
|
Class<?> queryDtoClass =
|
||||||
|
Class.forName("com.ruoyi.ccdi.project.domain.dto.CcdiProjectSuspiciousTransactionQueryDTO");
|
||||||
|
Method method = controllerClass.getMethod("getSuspiciousTransactions", queryDtoClass);
|
||||||
|
GetMapping getMapping = method.getAnnotation(GetMapping.class);
|
||||||
|
Operation operation = method.getAnnotation(Operation.class);
|
||||||
|
|
||||||
|
assertNotNull(getMapping);
|
||||||
|
assertEquals("/suspicious-transactions", getMapping.value()[0]);
|
||||||
|
assertNotNull(operation);
|
||||||
|
assertEquals(queryDtoClass, method.getParameterTypes()[0]);
|
||||||
|
assertEquals(AjaxResult.class, method.getReturnType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldExposeSuspiciousTransactionsExportEndpointContract() throws Exception {
|
||||||
|
Class<?> controllerClass = Class.forName("com.ruoyi.ccdi.project.controller.CcdiProjectOverviewController");
|
||||||
|
Class<?> queryDtoClass =
|
||||||
|
Class.forName("com.ruoyi.ccdi.project.domain.dto.CcdiProjectSuspiciousTransactionQueryDTO");
|
||||||
|
Method method = controllerClass.getMethod(
|
||||||
|
"exportSuspiciousTransactions",
|
||||||
|
HttpServletResponse.class,
|
||||||
|
queryDtoClass
|
||||||
|
);
|
||||||
|
PostMapping postMapping = method.getAnnotation(PostMapping.class);
|
||||||
|
Operation operation = method.getAnnotation(Operation.class);
|
||||||
|
|
||||||
|
assertNotNull(postMapping);
|
||||||
|
assertEquals("/suspicious-transactions/export", postMapping.value()[0]);
|
||||||
|
assertNotNull(operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldExposeSuspiciousTransactionsQueryDtoFields() throws Exception {
|
||||||
|
Class<?> dtoClass = Class.forName("com.ruoyi.ccdi.project.domain.dto.CcdiProjectSuspiciousTransactionQueryDTO");
|
||||||
|
|
||||||
|
List<String> fieldNames = Arrays.stream(dtoClass.getDeclaredFields())
|
||||||
|
.map(Field::getName)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
assertEquals(List.of("projectId", "suspiciousType", "pageNum", "pageSize"), fieldNames);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
package com.ruoyi.ccdi.project.controller;
|
package com.ruoyi.ccdi.project.controller;
|
||||||
|
|
||||||
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.excel.CcdiProjectSuspiciousTransactionExcel;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewDashboardVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectOverviewDashboardVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisBasicInfoVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisBasicInfoVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisDetailVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectPersonAnalysisDetailVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskHitTagVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskHitTagVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskPeopleOverviewItemVO;
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskPeopleOverviewItemVO;
|
||||||
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskPeopleOverviewVO;
|
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.domain.vo.CcdiProjectTopRiskPeopleVO;
|
||||||
import com.ruoyi.ccdi.project.service.ICcdiProjectOverviewService;
|
import com.ruoyi.ccdi.project.service.ICcdiProjectOverviewService;
|
||||||
import com.ruoyi.common.core.domain.AjaxResult;
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
@@ -18,12 +21,16 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
|||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.springframework.mock.web.MockHttpServletResponse;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.mockito.Mockito.same;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@@ -147,4 +154,64 @@ class CcdiProjectOverviewControllerTest {
|
|||||||
assertEquals("@ss.hasPermi('ccdi:project:query')", preAuthorize.value());
|
assertEquals("@ss.hasPermi('ccdi:project:query')", preAuthorize.value());
|
||||||
assertNotNull(operation);
|
assertNotNull(operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldExposeSuspiciousTransactionsEndpoint() throws Exception {
|
||||||
|
CcdiProjectSuspiciousTransactionQueryDTO queryDTO = new CcdiProjectSuspiciousTransactionQueryDTO();
|
||||||
|
queryDTO.setProjectId(40L);
|
||||||
|
queryDTO.setSuspiciousType("ALL");
|
||||||
|
queryDTO.setPageNum(1);
|
||||||
|
queryDTO.setPageSize(10);
|
||||||
|
|
||||||
|
CcdiProjectSuspiciousTransactionPageVO pageVO = new CcdiProjectSuspiciousTransactionPageVO();
|
||||||
|
pageVO.setRows(List.of());
|
||||||
|
pageVO.setTotal(0L);
|
||||||
|
when(overviewService.getSuspiciousTransactions(queryDTO)).thenReturn(pageVO);
|
||||||
|
|
||||||
|
AjaxResult result = controller.getSuspiciousTransactions(queryDTO);
|
||||||
|
|
||||||
|
assertEquals(200, result.get("code"));
|
||||||
|
assertEquals(pageVO, result.get("data"));
|
||||||
|
verify(overviewService).getSuspiciousTransactions(queryDTO);
|
||||||
|
|
||||||
|
Method method = CcdiProjectOverviewController.class.getMethod(
|
||||||
|
"getSuspiciousTransactions",
|
||||||
|
CcdiProjectSuspiciousTransactionQueryDTO.class
|
||||||
|
);
|
||||||
|
GetMapping getMapping = method.getAnnotation(GetMapping.class);
|
||||||
|
PreAuthorize preAuthorize = method.getAnnotation(PreAuthorize.class);
|
||||||
|
|
||||||
|
assertNotNull(getMapping);
|
||||||
|
assertEquals("/suspicious-transactions", getMapping.value()[0]);
|
||||||
|
assertNotNull(preAuthorize);
|
||||||
|
assertEquals("@ss.hasPermi('ccdi:project:query')", preAuthorize.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldExposeSuspiciousTransactionsExportEndpoint() throws Exception {
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
CcdiProjectSuspiciousTransactionQueryDTO queryDTO = new CcdiProjectSuspiciousTransactionQueryDTO();
|
||||||
|
CcdiProjectSuspiciousTransactionExcel row = new CcdiProjectSuspiciousTransactionExcel();
|
||||||
|
row.setSuspiciousPersonName("张三");
|
||||||
|
row.setDisplayAmount(new java.math.BigDecimal("10.00"));
|
||||||
|
when(overviewService.exportSuspiciousTransactions(same(queryDTO))).thenReturn(List.of(row));
|
||||||
|
|
||||||
|
controller.exportSuspiciousTransactions(response, queryDTO);
|
||||||
|
|
||||||
|
verify(overviewService).exportSuspiciousTransactions(same(queryDTO));
|
||||||
|
assertTrue(response.getContentType().startsWith("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"));
|
||||||
|
assertTrue(response.getContentAsByteArray().length > 0);
|
||||||
|
|
||||||
|
Method method = CcdiProjectOverviewController.class.getMethod(
|
||||||
|
"exportSuspiciousTransactions",
|
||||||
|
jakarta.servlet.http.HttpServletResponse.class,
|
||||||
|
CcdiProjectSuspiciousTransactionQueryDTO.class
|
||||||
|
);
|
||||||
|
PostMapping postMapping = method.getAnnotation(PostMapping.class);
|
||||||
|
Operation operation = method.getAnnotation(Operation.class);
|
||||||
|
|
||||||
|
assertNotNull(postMapping);
|
||||||
|
assertEquals("/suspicious-transactions/export", postMapping.value()[0]);
|
||||||
|
assertNotNull(operation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,4 +91,17 @@ class CcdiBankStatementTest {
|
|||||||
assertEquals(1, entity.getInternalFlag(), "Integer 类型应该正确复制");
|
assertEquals(1, entity.getInternalFlag(), "Integer 类型应该正确复制");
|
||||||
assertEquals(100, entity.getTrxType(), "Integer 类型应该正确复制");
|
assertEquals(100, entity.getTrxType(), "Integer 类型应该正确复制");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFromResponse_ShouldMapCounterpartyIdentityFields() {
|
||||||
|
BankStatementItem item = new BankStatementItem();
|
||||||
|
item.setCustomerCertNo("330101199001011234");
|
||||||
|
item.setCustomerSocialCreditCode("91330100123456789X");
|
||||||
|
|
||||||
|
CcdiBankStatement entity = CcdiBankStatement.fromResponse(item);
|
||||||
|
|
||||||
|
assertNotNull(entity, "转换结果不应为 null");
|
||||||
|
assertEquals("330101199001011234", entity.getCustomerCertNo());
|
||||||
|
assertEquals("91330100123456789X", entity.getCustomerSocialCreditCode());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,6 +143,21 @@ class CcdiBankStatementMapperXmlTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void mapperXml_shouldContainCounterpartyIdentityColumns() throws Exception {
|
||||||
|
try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(RESOURCE)) {
|
||||||
|
String xml = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
assertTrue(xml.contains("<result property=\"customerCertNo\" column=\"customer_cert_no\" />"), xml);
|
||||||
|
assertTrue(
|
||||||
|
xml.contains("<result property=\"customerSocialCreditCode\" column=\"customer_social_credit_code\" />"),
|
||||||
|
xml
|
||||||
|
);
|
||||||
|
assertTrue(xml.contains("customer_bank, customer_reference, customer_cert_no, customer_social_credit_code,"), xml);
|
||||||
|
assertTrue(xml.contains("#{item.customerBank}, #{item.customerReference}, #{item.customerCertNo}, #{item.customerSocialCreditCode},"), xml);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private MappedStatement loadMappedStatement(String statementId) throws Exception {
|
private MappedStatement loadMappedStatement(String statementId) throws Exception {
|
||||||
Configuration configuration = new Configuration();
|
Configuration configuration = new Configuration();
|
||||||
configuration.setEnvironment(new Environment("test", new JdbcTransactionFactory(), new NoOpDataSource()));
|
configuration.setEnvironment(new Environment("test", new JdbcTransactionFactory(), new NoOpDataSource()));
|
||||||
|
|||||||
@@ -71,6 +71,19 @@ class CcdiProjectOverviewMapperSqlTest {
|
|||||||
assertTrue(objectRowsSql.contains("tr.staff_id_card = #{staffIdCard}") || objectRowsSql.contains("#{staffIdCard}"), objectRowsSql);
|
assertTrue(objectRowsSql.contains("tr.staff_id_card = #{staffIdCard}") || objectRowsSql.contains("#{staffIdCard}"), objectRowsSql);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldExposeSuspiciousTransactionAggregationQuery() throws Exception {
|
||||||
|
String xml = Files.readString(Path.of("src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml"));
|
||||||
|
String suspiciousSql = extractSelect(xml, "selectSuspiciousTransactionPage");
|
||||||
|
|
||||||
|
assertTrue(suspiciousSql.contains("rule_name like '%可疑%'"), suspiciousSql);
|
||||||
|
assertTrue(suspiciousSql.contains("ccdi_biz_intermediary"), suspiciousSql);
|
||||||
|
assertTrue(suspiciousSql.contains("ccdi_enterprise_base_info"), suspiciousSql);
|
||||||
|
assertTrue(suspiciousSql.contains("group by merged.bankStatementId"), suspiciousSql);
|
||||||
|
assertTrue(suspiciousSql.contains("hasModelRuleHit"), suspiciousSql);
|
||||||
|
assertTrue(suspiciousSql.contains("hasNameListHit"), suspiciousSql);
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|||||||
@@ -17,6 +17,14 @@ class CcdiProjectOverviewServiceStructureTest {
|
|||||||
"getPersonAnalysisDetail",
|
"getPersonAnalysisDetail",
|
||||||
Class.forName("com.ruoyi.ccdi.project.domain.dto.CcdiProjectPersonAnalysisDetailQueryDTO")
|
Class.forName("com.ruoyi.ccdi.project.domain.dto.CcdiProjectPersonAnalysisDetailQueryDTO")
|
||||||
));
|
));
|
||||||
|
assertNotNull(clazz.getMethod(
|
||||||
|
"getSuspiciousTransactions",
|
||||||
|
Class.forName("com.ruoyi.ccdi.project.domain.dto.CcdiProjectSuspiciousTransactionQueryDTO")
|
||||||
|
));
|
||||||
|
assertNotNull(clazz.getMethod(
|
||||||
|
"exportSuspiciousTransactions",
|
||||||
|
Class.forName("com.ruoyi.ccdi.project.domain.dto.CcdiProjectSuspiciousTransactionQueryDTO")
|
||||||
|
));
|
||||||
assertNotNull(clazz.getMethod("refreshOverviewEmployeeResults", Long.class, String.class));
|
assertNotNull(clazz.getMethod("refreshOverviewEmployeeResults", Long.class, String.class));
|
||||||
assertNotNull(clazz.getMethod("refreshProjectRiskCounts", Long.class, String.class));
|
assertNotNull(clazz.getMethod("refreshProjectRiskCounts", Long.class, String.class));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,134 @@
|
|||||||
|
package com.ruoyi.ccdi.project.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.ruoyi.ccdi.project.domain.CcdiProject;
|
||||||
|
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSuspiciousTransactionQueryDTO;
|
||||||
|
import com.ruoyi.ccdi.project.domain.excel.CcdiProjectSuspiciousTransactionExcel;
|
||||||
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectSuspiciousTransactionItemVO;
|
||||||
|
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectSuspiciousTransactionPageVO;
|
||||||
|
import com.ruoyi.ccdi.project.mapper.CcdiBankTagResultMapper;
|
||||||
|
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
|
||||||
|
import com.ruoyi.ccdi.project.mapper.CcdiProjectOverviewEmployeeResultMapper;
|
||||||
|
import com.ruoyi.ccdi.project.mapper.CcdiProjectOverviewMapper;
|
||||||
|
import com.ruoyi.common.exception.ServiceException;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.argThat;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class CcdiProjectOverviewServiceSuspiciousTransactionTest {
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private CcdiProjectOverviewServiceImpl service;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private CcdiProjectOverviewMapper overviewMapper;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private CcdiProjectMapper projectMapper;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private CcdiProjectOverviewEmployeeResultMapper overviewEmployeeResultMapper;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private CcdiBankTagResultMapper bankTagResultMapper;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private CcdiProjectOverviewEmployeeResultBuilder overviewEmployeeResultBuilder;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReturnDeduplicatedSuspiciousTransactions() {
|
||||||
|
CcdiProject project = new CcdiProject();
|
||||||
|
project.setProjectId(40L);
|
||||||
|
when(projectMapper.selectById(40L)).thenReturn(project);
|
||||||
|
|
||||||
|
CcdiProjectSuspiciousTransactionItemVO item = new CcdiProjectSuspiciousTransactionItemVO();
|
||||||
|
item.setBankStatementId(101L);
|
||||||
|
item.setSuspiciousPersonName("王五");
|
||||||
|
item.setRelatedPersonName("孙七");
|
||||||
|
item.setRelatedStaffName("孙七");
|
||||||
|
item.setRelatedStaffCode("809901");
|
||||||
|
item.setRelationType("配偶");
|
||||||
|
item.setUserMemo("零钱商户消费");
|
||||||
|
item.setCashType("转账");
|
||||||
|
item.setDisplayAmount(new BigDecimal("200000.00"));
|
||||||
|
item.setHasModelRuleHit(true);
|
||||||
|
item.setHasNameListHit(true);
|
||||||
|
|
||||||
|
Page<CcdiProjectSuspiciousTransactionItemVO> page = new Page<>(1, 10);
|
||||||
|
page.setRecords(List.of(item));
|
||||||
|
page.setTotal(1);
|
||||||
|
when(overviewMapper.selectSuspiciousTransactionPage(any(Page.class), any(CcdiProjectSuspiciousTransactionQueryDTO.class)))
|
||||||
|
.thenReturn(page);
|
||||||
|
|
||||||
|
CcdiProjectSuspiciousTransactionQueryDTO queryDTO = new CcdiProjectSuspiciousTransactionQueryDTO();
|
||||||
|
queryDTO.setProjectId(40L);
|
||||||
|
queryDTO.setSuspiciousType("name_list");
|
||||||
|
queryDTO.setPageNum(1);
|
||||||
|
queryDTO.setPageSize(10);
|
||||||
|
|
||||||
|
CcdiProjectSuspiciousTransactionPageVO result = service.getSuspiciousTransactions(queryDTO);
|
||||||
|
|
||||||
|
assertEquals(1, result.getRows().size());
|
||||||
|
assertEquals(1L, result.getTotal());
|
||||||
|
assertTrue(result.getRows().getFirst().getHasModelRuleHit());
|
||||||
|
assertTrue(result.getRows().getFirst().getHasNameListHit());
|
||||||
|
verify(overviewMapper).selectSuspiciousTransactionPage(
|
||||||
|
any(Page.class),
|
||||||
|
argThat(query -> "NAME_LIST".equals(query.getSuspiciousType()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldExportSuspiciousTransactionsWithCurrentFilter() {
|
||||||
|
CcdiProject project = new CcdiProject();
|
||||||
|
project.setProjectId(40L);
|
||||||
|
when(projectMapper.selectById(40L)).thenReturn(project);
|
||||||
|
|
||||||
|
CcdiProjectSuspiciousTransactionItemVO item = new CcdiProjectSuspiciousTransactionItemVO();
|
||||||
|
item.setTrxDate("2024-01-15 10:00:00");
|
||||||
|
item.setSuspiciousPersonName("孙七");
|
||||||
|
item.setRelatedPersonName("孙七");
|
||||||
|
item.setRelatedStaffName("孙七");
|
||||||
|
item.setRelatedStaffCode("809901");
|
||||||
|
item.setRelationType("本人");
|
||||||
|
item.setUserMemo("");
|
||||||
|
item.setCashType("转账");
|
||||||
|
item.setDisplayAmount(new BigDecimal("500000.00"));
|
||||||
|
when(overviewMapper.selectSuspiciousTransactionList(any(CcdiProjectSuspiciousTransactionQueryDTO.class)))
|
||||||
|
.thenReturn(List.of(item));
|
||||||
|
|
||||||
|
CcdiProjectSuspiciousTransactionQueryDTO queryDTO = new CcdiProjectSuspiciousTransactionQueryDTO();
|
||||||
|
queryDTO.setProjectId(40L);
|
||||||
|
|
||||||
|
List<CcdiProjectSuspiciousTransactionExcel> rows = service.exportSuspiciousTransactions(queryDTO);
|
||||||
|
|
||||||
|
assertEquals(1, rows.size());
|
||||||
|
assertEquals("孙七(809901)", rows.getFirst().getRelatedStaffDisplay());
|
||||||
|
assertEquals("/转账", rows.getFirst().getSummaryAndCashType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldThrowWhenSuspiciousTransactionProjectDoesNotExist() {
|
||||||
|
when(projectMapper.selectById(99L)).thenReturn(null);
|
||||||
|
|
||||||
|
CcdiProjectSuspiciousTransactionQueryDTO queryDTO = new CcdiProjectSuspiciousTransactionQueryDTO();
|
||||||
|
queryDTO.setProjectId(99L);
|
||||||
|
|
||||||
|
assertThrows(ServiceException.class, () -> service.getSuspiciousTransactions(queryDTO));
|
||||||
|
assertThrows(ServiceException.class, () -> service.exportSuspiciousTransactions(queryDTO));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -138,6 +138,8 @@ class BankStatementItem(BaseModel):
|
|||||||
customerId: int = Field(-1, description="客户ID")
|
customerId: int = Field(-1, description="客户ID")
|
||||||
customerName: str = Field(..., description="客户名称")
|
customerName: str = Field(..., description="客户名称")
|
||||||
customerReference: str = Field("", description="客户参考")
|
customerReference: str = Field("", description="客户参考")
|
||||||
|
customerCertNo: str = Field("", description="客户证件号")
|
||||||
|
customerSocialCreditCode: str = Field("", description="客户统一社会信用代码")
|
||||||
downPaymentFlag: int = Field(0, description="首付标志")
|
downPaymentFlag: int = Field(0, description="首付标志")
|
||||||
drAmount: float = Field(0, description="借方金额")
|
drAmount: float = Field(0, description="借方金额")
|
||||||
exceptionType: str = Field("", description="异常类型")
|
exceptionType: str = Field("", description="异常类型")
|
||||||
|
|||||||
@@ -85,6 +85,8 @@ def _build_statement(
|
|||||||
customer_account_mask_no: str = "9558800000000001",
|
customer_account_mask_no: str = "9558800000000001",
|
||||||
bank_comments: str = "",
|
bank_comments: str = "",
|
||||||
customer_bank: str = "",
|
customer_bank: str = "",
|
||||||
|
customer_cert_no: str = "",
|
||||||
|
customer_social_credit_code: str = "",
|
||||||
) -> Dict:
|
) -> Dict:
|
||||||
trans_amount = round(dr_amount if dr_amount > 0 else cr_amount, 2)
|
trans_amount = round(dr_amount if dr_amount > 0 else cr_amount, 2)
|
||||||
balance_amount = round(80000000 + cr_amount - dr_amount, 2)
|
balance_amount = round(80000000 + cr_amount - dr_amount, 2)
|
||||||
@@ -114,6 +116,8 @@ def _build_statement(
|
|||||||
"customerId": -1,
|
"customerId": -1,
|
||||||
"customerName": customer_name,
|
"customerName": customer_name,
|
||||||
"customerReference": "",
|
"customerReference": "",
|
||||||
|
"customerCertNo": customer_cert_no,
|
||||||
|
"customerSocialCreditCode": customer_social_credit_code,
|
||||||
"downPaymentFlag": 0,
|
"downPaymentFlag": 0,
|
||||||
"drAmount": round(dr_amount, 2),
|
"drAmount": round(dr_amount, 2),
|
||||||
"exceptionType": "",
|
"exceptionType": "",
|
||||||
|
|||||||
@@ -109,6 +109,8 @@ class StatementService:
|
|||||||
"customerId": -1,
|
"customerId": -1,
|
||||||
"customerName": customer_name,
|
"customerName": customer_name,
|
||||||
"customerReference": "",
|
"customerReference": "",
|
||||||
|
"customerCertNo": rng.choice(allowed_identity_cards),
|
||||||
|
"customerSocialCreditCode": "",
|
||||||
"downPaymentFlag": 0,
|
"downPaymentFlag": 0,
|
||||||
"drAmount": dr_amount,
|
"drAmount": dr_amount,
|
||||||
"exceptionType": "",
|
"exceptionType": "",
|
||||||
|
|||||||
@@ -258,6 +258,25 @@ def test_generate_statements_should_stay_within_single_employee_scope_per_log_id
|
|||||||
assert {item["cretNo"] for item in statements}.issubset(allowed_id_cards)
|
assert {item["cretNo"] for item in statements}.issubset(allowed_id_cards)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_bank_statement_should_include_counterparty_identity_fields():
|
||||||
|
service = StatementService()
|
||||||
|
|
||||||
|
response = service.get_bank_statement(
|
||||||
|
{
|
||||||
|
"groupId": 1000,
|
||||||
|
"logId": 20001,
|
||||||
|
"pageNow": 1,
|
||||||
|
"pageSize": 5,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
statements = response["data"]["bankStatementList"]
|
||||||
|
|
||||||
|
assert statements
|
||||||
|
assert all("customerCertNo" in item for item in statements)
|
||||||
|
assert all("customerSocialCreditCode" in item for item in statements)
|
||||||
|
|
||||||
|
|
||||||
def test_all_mode_monthly_fixed_income_log_should_keep_monthly_income_stable(monkeypatch):
|
def test_all_mode_monthly_fixed_income_log_should_keep_monthly_income_stable(monkeypatch):
|
||||||
monkeypatch.setattr("services.file_service.settings.RULE_HIT_MODE", "all")
|
monkeypatch.setattr("services.file_service.settings.RULE_HIT_MODE", "all")
|
||||||
file_service = FileService(staff_identity_repository=FakeStaffIdentityRepository())
|
file_service = FileService(staff_identity_repository=FakeStaffIdentityRepository())
|
||||||
|
|||||||
@@ -50,3 +50,16 @@ export function getOverviewPersonAnalysisDetail(params) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getOverviewSuspiciousTransactions(params) {
|
||||||
|
return request({
|
||||||
|
url: '/ccdi/project/overview/suspicious-transactions',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
projectId: params.projectId,
|
||||||
|
suspiciousType: params.suspiciousType,
|
||||||
|
pageNum: params.pageNum,
|
||||||
|
pageSize: params.pageSize
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -87,6 +87,37 @@ export const mockOverviewData = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
riskDetails: {
|
riskDetails: {
|
||||||
|
projectId: 1,
|
||||||
|
suspiciousType: "ALL",
|
||||||
|
total: 2,
|
||||||
|
suspiciousTransactionList: [
|
||||||
|
{
|
||||||
|
bankStatementId: 1,
|
||||||
|
trxDate: "2024-01-15 10:00:00",
|
||||||
|
suspiciousPersonName: "孙七",
|
||||||
|
relatedPersonName: "孙七",
|
||||||
|
relatedStaffName: "孙七",
|
||||||
|
relatedStaffCode: "809901",
|
||||||
|
relationType: "本人",
|
||||||
|
userMemo: "",
|
||||||
|
cashType: "转账",
|
||||||
|
displayAmount: 500000,
|
||||||
|
actionLabel: "查看详情",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
bankStatementId: 2,
|
||||||
|
trxDate: "2024-01-10 09:20:00",
|
||||||
|
suspiciousPersonName: "王五",
|
||||||
|
relatedPersonName: "孙七",
|
||||||
|
relatedStaffName: "孙七",
|
||||||
|
relatedStaffCode: "809901",
|
||||||
|
relationType: "配偶",
|
||||||
|
userMemo: "零钱商户消费",
|
||||||
|
cashType: "",
|
||||||
|
displayAmount: -200000,
|
||||||
|
actionLabel: "查看详情",
|
||||||
|
},
|
||||||
|
],
|
||||||
transactionList: [
|
transactionList: [
|
||||||
{
|
{
|
||||||
tradeDate: "2024-01-15",
|
tradeDate: "2024-01-15",
|
||||||
@@ -351,7 +382,17 @@ function normalizeRiskModelCards(cardList) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createOverviewLoadedData({ projectId, dashboardData, riskPeopleData, riskModelCardsData } = {}) {
|
function normalizeSuspiciousTransactions(rows) {
|
||||||
|
if (!Array.isArray(rows)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return rows.map((item) => ({
|
||||||
|
...item,
|
||||||
|
actionLabel: item.actionLabel || "查看详情",
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createOverviewLoadedData({ projectId, dashboardData, riskPeopleData, riskModelCardsData, suspiciousData } = {}) {
|
||||||
return {
|
return {
|
||||||
...mockOverviewData,
|
...mockOverviewData,
|
||||||
summary: {
|
summary: {
|
||||||
@@ -373,6 +414,15 @@ export function createOverviewLoadedData({ projectId, dashboardData, riskPeopleD
|
|||||||
peopleList: [],
|
peopleList: [],
|
||||||
total: 0,
|
total: 0,
|
||||||
},
|
},
|
||||||
|
riskDetails: {
|
||||||
|
...mockOverviewData.riskDetails,
|
||||||
|
projectId,
|
||||||
|
suspiciousTransactionList: normalizeSuspiciousTransactions(suspiciousData && suspiciousData.rows),
|
||||||
|
suspiciousType: "ALL",
|
||||||
|
total: suspiciousData && suspiciousData.total ? suspiciousData.total : 0,
|
||||||
|
transactionList: normalizeSuspiciousTransactions(suspiciousData && suspiciousData.rows),
|
||||||
|
abnormalAccountList: [],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,6 +445,9 @@ export const mockOverviewStateData = {
|
|||||||
peopleList: [],
|
peopleList: [],
|
||||||
},
|
},
|
||||||
riskDetails: {
|
riskDetails: {
|
||||||
|
suspiciousTransactionList: [],
|
||||||
|
suspiciousType: "ALL",
|
||||||
|
total: 0,
|
||||||
transactionList: [],
|
transactionList: [],
|
||||||
abnormalAccountList: [],
|
abnormalAccountList: [],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,6 +16,13 @@ const detail = fs.readFileSync(
|
|||||||
),
|
),
|
||||||
"utf8"
|
"utf8"
|
||||||
);
|
);
|
||||||
|
const preliminaryCheck = fs.readFileSync(
|
||||||
|
path.resolve(
|
||||||
|
__dirname,
|
||||||
|
"../../src/views/ccdiProject/components/detail/PreliminaryCheck.vue"
|
||||||
|
),
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
|
||||||
["风险模型", "命中模型涉及人员", "员工姓名或工号", "异常标签"].forEach((token) =>
|
["风险模型", "命中模型涉及人员", "员工姓名或工号", "异常标签"].forEach((token) =>
|
||||||
assert(model.includes(token), token)
|
assert(model.includes(token), token)
|
||||||
@@ -26,6 +33,9 @@ const detail = fs.readFileSync(
|
|||||||
["部门", "请选择部门", "查询", "重置", "selectedModelText"].forEach((token) =>
|
["部门", "请选择部门", "查询", "重置", "selectedModelText"].forEach((token) =>
|
||||||
assert(model.includes(token), token)
|
assert(model.includes(token), token)
|
||||||
);
|
);
|
||||||
["风险明细", "涉险交易明细", "异常账户人员信息", "查看详情"].forEach((token) =>
|
["风险明细", "涉疑交易明细", "异常账户人员信息", "查看详情"].forEach((token) =>
|
||||||
assert(detail.includes(token), token)
|
assert(detail.includes(token), token)
|
||||||
);
|
);
|
||||||
|
["getOverviewSuspiciousTransactions", "riskDetails"].forEach((token) =>
|
||||||
|
assert(preliminaryCheck.includes(token), token)
|
||||||
|
);
|
||||||
|
|||||||
@@ -12,10 +12,12 @@ const source = fs.readFileSync(
|
|||||||
"getOverviewRiskPeople",
|
"getOverviewRiskPeople",
|
||||||
"getOverviewRiskModelCards",
|
"getOverviewRiskModelCards",
|
||||||
"getOverviewRiskModelPeople",
|
"getOverviewRiskModelPeople",
|
||||||
|
"getOverviewSuspiciousTransactions",
|
||||||
"/ccdi/project/overview/dashboard",
|
"/ccdi/project/overview/dashboard",
|
||||||
"/ccdi/project/overview/risk-people",
|
"/ccdi/project/overview/risk-people",
|
||||||
"/ccdi/project/overview/risk-models/cards",
|
"/ccdi/project/overview/risk-models/cards",
|
||||||
"/ccdi/project/overview/risk-models/people",
|
"/ccdi/project/overview/risk-models/people",
|
||||||
|
"/ccdi/project/overview/suspicious-transactions",
|
||||||
].forEach((token) => assert(source.includes(token), token));
|
].forEach((token) => assert(source.includes(token), token));
|
||||||
|
|
||||||
[
|
[
|
||||||
@@ -39,6 +41,19 @@ assert(riskModelPeopleFn, "应保留模型人员接口参数透传逻辑");
|
|||||||
"pageSize: params.pageSize",
|
"pageSize: params.pageSize",
|
||||||
].forEach((token) => assert(riskModelPeopleFn[0].includes(token), token));
|
].forEach((token) => assert(riskModelPeopleFn[0].includes(token), token));
|
||||||
|
|
||||||
|
const suspiciousTransactionsFn = source.match(
|
||||||
|
/export function getOverviewSuspiciousTransactions\(params\) \{[\s\S]*?params:\s*\{([\s\S]*?)\}\s*\}\s*\)/m
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(suspiciousTransactionsFn, "应新增涉疑交易接口参数透传逻辑");
|
||||||
|
|
||||||
|
[
|
||||||
|
"projectId: params.projectId",
|
||||||
|
"suspiciousType: params.suspiciousType",
|
||||||
|
"pageNum: params.pageNum",
|
||||||
|
"pageSize: params.pageSize",
|
||||||
|
].forEach((token) => assert(suspiciousTransactionsFn[0].includes(token), token));
|
||||||
|
|
||||||
["employeeResult", "resultTable", "overview/result"].forEach((token) =>
|
["employeeResult", "resultTable", "overview/result"].forEach((token) =>
|
||||||
assert(!source.includes(token), `前端 API 契约不应感知结果表实现:${token}`)
|
assert(!source.includes(token), `前端 API 契约不应感知结果表实现:${token}`)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
ALTER TABLE `ccdi_bank_statement`
|
||||||
|
ADD COLUMN `customer_cert_no` varchar(50) NULL COMMENT '交易对手方证件号' AFTER `customer_reference`,
|
||||||
|
ADD COLUMN `customer_social_credit_code` varchar(50) NULL COMMENT '交易对手方统一社会信用代码' AFTER `customer_cert_no`;
|
||||||
Reference in New Issue
Block a user