完成专项核查家庭资产负债实现

This commit is contained in:
wkc
2026-03-24 20:42:56 +08:00
parent 979b905682
commit c1b4514806
38 changed files with 2514 additions and 19 deletions

View File

@@ -0,0 +1,51 @@
package com.ruoyi.ccdi.project.controller;
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectFamilyAssetLiabilityDetailQueryDTO;
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectFamilyAssetLiabilityListQueryDTO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetLiabilityDetailVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetLiabilityListVO;
import com.ruoyi.ccdi.project.service.ICcdiProjectSpecialCheckService;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 项目专项核查控制器
*/
@RestController
@RequestMapping("/ccdi/project/special-check")
@Tag(name = "项目专项核查")
public class CcdiProjectSpecialCheckController extends BaseController {
@Resource
private ICcdiProjectSpecialCheckService specialCheckService;
/**
* 查询员工家庭资产负债列表
*/
@GetMapping("/family-asset-liability/list")
@Operation(summary = "查询员工家庭资产负债列表")
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
public AjaxResult getFamilyAssetLiabilityList(@Validated CcdiProjectFamilyAssetLiabilityListQueryDTO queryDTO) {
CcdiProjectFamilyAssetLiabilityListVO result = specialCheckService.getFamilyAssetLiabilityList(queryDTO);
return AjaxResult.success(result);
}
/**
* 查询员工家庭资产负债详情
*/
@GetMapping("/family-asset-liability/detail")
@Operation(summary = "查询员工家庭资产负债详情")
@PreAuthorize("@ss.hasPermi('ccdi:project:query')")
public AjaxResult getFamilyAssetLiabilityDetail(@Validated CcdiProjectFamilyAssetLiabilityDetailQueryDTO queryDTO) {
CcdiProjectFamilyAssetLiabilityDetailVO result = specialCheckService.getFamilyAssetLiabilityDetail(queryDTO);
return AjaxResult.success(result);
}
}

View File

@@ -0,0 +1,20 @@
package com.ruoyi.ccdi.project.domain.dto;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* 员工家庭资产负债详情查询DTO
*/
@Data
public class CcdiProjectFamilyAssetLiabilityDetailQueryDTO {
/** 项目ID */
@NotNull(message = "项目ID不能为空")
private Long projectId;
/** 员工身份证号 */
@NotBlank(message = "员工身份证号不能为空")
private String staffIdCard;
}

View File

@@ -0,0 +1,15 @@
package com.ruoyi.ccdi.project.domain.dto;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* 员工家庭资产负债列表查询DTO
*/
@Data
public class CcdiProjectFamilyAssetLiabilityListQueryDTO {
/** 项目ID */
@NotNull(message = "项目ID不能为空")
private Long projectId;
}

View File

@@ -0,0 +1,27 @@
package com.ruoyi.ccdi.project.domain.vo;
import java.math.BigDecimal;
import java.util.List;
import lombok.Data;
/**
* 员工家庭资产明细VO
*/
@Data
public class CcdiProjectFamilyAssetDetailVO {
/** 本人是否缺少资产信息 */
private Boolean missingSelfAssetInfo;
/** 本人资产小计 */
private BigDecimal selfTotalAsset;
/** 配偶资产小计 */
private BigDecimal spouseTotalAsset;
/** 家庭总资产 */
private BigDecimal totalAsset;
/** 资产明细 */
private List<CcdiProjectFamilyAssetItemVO> items;
}

View File

@@ -0,0 +1,33 @@
package com.ruoyi.ccdi.project.domain.vo;
import java.math.BigDecimal;
import java.util.Date;
import lombok.Data;
/**
* 员工家庭资产明细项VO
*/
@Data
public class CcdiProjectFamilyAssetItemVO {
/** 资产名称 */
private String assetName;
/** 资产大类 */
private String assetMainType;
/** 资产小类 */
private String assetSubType;
/** 持有人姓名 */
private String holderName;
/** 持有人证件号 */
private String holderIdCard;
/** 当前估值 */
private BigDecimal currentValue;
/** 估值日期 */
private Date valuationDate;
}

View File

@@ -0,0 +1,22 @@
package com.ruoyi.ccdi.project.domain.vo;
import lombok.Data;
/**
* 员工家庭资产负债详情VO
*/
@Data
public class CcdiProjectFamilyAssetLiabilityDetailVO {
/** 收入明细 */
private CcdiProjectFamilyIncomeDetailVO incomeDetail;
/** 资产明细 */
private CcdiProjectFamilyAssetDetailVO assetDetail;
/** 负债明细 */
private CcdiProjectFamilyDebtDetailVO debtDetail;
/** 汇总信息 */
private CcdiProjectFamilyAssetLiabilityListItemVO summary;
}

View File

@@ -0,0 +1,41 @@
package com.ruoyi.ccdi.project.domain.vo;
import java.math.BigDecimal;
import lombok.Data;
/**
* 员工家庭资产负债列表项VO
*/
@Data
public class CcdiProjectFamilyAssetLiabilityListItemVO {
/** 员工身份证号 */
private String staffIdCard;
/** 员工工号 */
private String staffCode;
/** 员工姓名 */
private String staffName;
/** 所属部门 */
private String deptName;
/** 家庭总年收入 */
private BigDecimal totalIncome;
/** 家庭总资产 */
private BigDecimal totalAsset;
/** 家庭总负债 */
private BigDecimal totalDebt;
/** 收入负债对比金额 */
private BigDecimal comparisonAmount;
/** 风险等级编码 */
private String riskLevelCode;
/** 风险等级名称 */
private String riskLevelName;
}

View File

@@ -0,0 +1,14 @@
package com.ruoyi.ccdi.project.domain.vo;
import java.util.List;
import lombok.Data;
/**
* 员工家庭资产负债列表VO
*/
@Data
public class CcdiProjectFamilyAssetLiabilityListVO {
/** 列表数据 */
private List<CcdiProjectFamilyAssetLiabilityListItemVO> rows;
}

View File

@@ -0,0 +1,27 @@
package com.ruoyi.ccdi.project.domain.vo;
import java.math.BigDecimal;
import java.util.List;
import lombok.Data;
/**
* 员工家庭负债明细VO
*/
@Data
public class CcdiProjectFamilyDebtDetailVO {
/** 本人是否缺少负债信息 */
private Boolean missingSelfDebtInfo;
/** 本人负债小计 */
private BigDecimal selfTotalDebt;
/** 配偶负债小计 */
private BigDecimal spouseTotalDebt;
/** 家庭总负债 */
private BigDecimal totalDebt;
/** 负债明细 */
private List<CcdiProjectFamilyDebtItemVO> items;
}

View File

@@ -0,0 +1,36 @@
package com.ruoyi.ccdi.project.domain.vo;
import java.math.BigDecimal;
import java.util.Date;
import lombok.Data;
/**
* 员工家庭负债明细项VO
*/
@Data
public class CcdiProjectFamilyDebtItemVO {
/** 负债名称 */
private String debtName;
/** 负债大类 */
private String debtMainType;
/** 负债小类 */
private String debtSubType;
/** 债权人类型 */
private String creditorType;
/** 归属人姓名 */
private String ownerName;
/** 归属人证件号 */
private String ownerIdCard;
/** 本金余额 */
private BigDecimal principalBalance;
/** 查询日期 */
private Date queryDate;
}

View File

@@ -0,0 +1,20 @@
package com.ruoyi.ccdi.project.domain.vo;
import java.math.BigDecimal;
import lombok.Data;
/**
* 员工家庭收入明细VO
*/
@Data
public class CcdiProjectFamilyIncomeDetailVO {
/** 本人年收入 */
private BigDecimal selfIncome;
/** 配偶年收入 */
private BigDecimal spouseIncome;
/** 家庭总年收入 */
private BigDecimal totalIncome;
}

View File

@@ -0,0 +1,64 @@
package com.ruoyi.ccdi.project.mapper;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetItemVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetLiabilityDetailVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetLiabilityListItemVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyDebtItemVO;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* 项目专项核查Mapper
*/
@Mapper
public interface CcdiProjectSpecialCheckMapper {
/**
* 查询员工家庭资产负债列表
*
* @param projectId 项目ID
* @return 列表数据
*/
List<CcdiProjectFamilyAssetLiabilityListItemVO> selectFamilyAssetLiabilityList(@Param("projectId") Long projectId);
/**
* 查询员工家庭资产负债详情
*
* @param projectId 项目ID
* @param staffIdCard 员工身份证号
* @return 详情结果
*/
CcdiProjectFamilyAssetLiabilityDetailVO selectFamilyAssetLiabilityDetail(
@Param("projectId") Long projectId,
@Param("staffIdCard") String staffIdCard
);
/**
* 查询员工家庭资产明细
*
* @param projectId 项目ID
* @param staffIdCard 员工身份证号
* @param spouseIdCard 配偶身份证号
* @return 资产明细
*/
List<CcdiProjectFamilyAssetItemVO> selectFamilyAssetItemsByScope(
@Param("projectId") Long projectId,
@Param("staffIdCard") String staffIdCard,
@Param("spouseIdCard") String spouseIdCard
);
/**
* 查询员工家庭负债明细
*
* @param projectId 项目ID
* @param staffIdCard 员工身份证号
* @param spouseIdCard 配偶身份证号
* @return 负债明细
*/
List<CcdiProjectFamilyDebtItemVO> selectFamilyDebtItemsByScope(
@Param("projectId") Long projectId,
@Param("staffIdCard") String staffIdCard,
@Param("spouseIdCard") String spouseIdCard
);
}

View File

@@ -0,0 +1,32 @@
package com.ruoyi.ccdi.project.service;
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectFamilyAssetLiabilityDetailQueryDTO;
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectFamilyAssetLiabilityListQueryDTO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetLiabilityDetailVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetLiabilityListVO;
/**
* 项目专项核查服务接口
*/
public interface ICcdiProjectSpecialCheckService {
/**
* 查询员工家庭资产负债列表
*
* @param queryDTO 查询条件
* @return 列表结果
*/
CcdiProjectFamilyAssetLiabilityListVO getFamilyAssetLiabilityList(
CcdiProjectFamilyAssetLiabilityListQueryDTO queryDTO
);
/**
* 查询员工家庭资产负债详情
*
* @param queryDTO 查询条件
* @return 详情结果
*/
CcdiProjectFamilyAssetLiabilityDetailVO getFamilyAssetLiabilityDetail(
CcdiProjectFamilyAssetLiabilityDetailQueryDTO queryDTO
);
}

View File

@@ -0,0 +1,115 @@
package com.ruoyi.ccdi.project.service.impl;
import com.ruoyi.ccdi.project.domain.CcdiProject;
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectFamilyAssetLiabilityDetailQueryDTO;
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectFamilyAssetLiabilityListQueryDTO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetDetailVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetLiabilityDetailVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetLiabilityListItemVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetLiabilityListVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyDebtDetailVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyIncomeDetailVO;
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
import com.ruoyi.ccdi.project.mapper.CcdiProjectSpecialCheckMapper;
import com.ruoyi.ccdi.project.service.ICcdiProjectSpecialCheckService;
import com.ruoyi.common.exception.ServiceException;
import jakarta.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;
import org.springframework.stereotype.Service;
/**
* 项目专项核查服务实现
*/
@Service
public class CcdiProjectSpecialCheckServiceImpl implements ICcdiProjectSpecialCheckService {
@Resource
private CcdiProjectSpecialCheckMapper specialCheckMapper;
@Resource
private CcdiProjectMapper projectMapper;
@Override
public CcdiProjectFamilyAssetLiabilityListVO getFamilyAssetLiabilityList(
CcdiProjectFamilyAssetLiabilityListQueryDTO queryDTO
) {
ensureProjectExists(queryDTO.getProjectId());
CcdiProjectFamilyAssetLiabilityListVO result = new CcdiProjectFamilyAssetLiabilityListVO();
result.setRows(defaultList(specialCheckMapper.selectFamilyAssetLiabilityList(queryDTO.getProjectId())));
return result;
}
@Override
public CcdiProjectFamilyAssetLiabilityDetailVO getFamilyAssetLiabilityDetail(
CcdiProjectFamilyAssetLiabilityDetailQueryDTO queryDTO
) {
ensureProjectExists(queryDTO.getProjectId());
CcdiProjectFamilyAssetLiabilityDetailVO detail = specialCheckMapper.selectFamilyAssetLiabilityDetail(
queryDTO.getProjectId(),
queryDTO.getStaffIdCard()
);
if (detail == null) {
throw new ServiceException("当前员工不属于该项目专项核查范围");
}
normalizeDetail(detail);
return detail;
}
private void ensureProjectExists(Long projectId) {
CcdiProject project = projectMapper.selectById(projectId);
if (project == null) {
throw new ServiceException("项目不存在");
}
}
private void normalizeDetail(CcdiProjectFamilyAssetLiabilityDetailVO detail) {
if (detail.getIncomeDetail() == null) {
CcdiProjectFamilyIncomeDetailVO incomeDetail = new CcdiProjectFamilyIncomeDetailVO();
incomeDetail.setSelfIncome(BigDecimal.ZERO);
incomeDetail.setSpouseIncome(BigDecimal.ZERO);
incomeDetail.setTotalIncome(BigDecimal.ZERO);
detail.setIncomeDetail(incomeDetail);
}
if (detail.getAssetDetail() == null) {
CcdiProjectFamilyAssetDetailVO assetDetail = new CcdiProjectFamilyAssetDetailVO();
assetDetail.setMissingSelfAssetInfo(false);
assetDetail.setSelfTotalAsset(BigDecimal.ZERO);
assetDetail.setSpouseTotalAsset(BigDecimal.ZERO);
assetDetail.setTotalAsset(BigDecimal.ZERO);
assetDetail.setItems(List.of());
detail.setAssetDetail(assetDetail);
} else if (detail.getAssetDetail().getItems() == null) {
detail.getAssetDetail().setItems(List.of());
}
if (detail.getAssetDetail().getMissingSelfAssetInfo() == null) {
detail.getAssetDetail().setMissingSelfAssetInfo(false);
}
if (detail.getDebtDetail() == null) {
CcdiProjectFamilyDebtDetailVO debtDetail = new CcdiProjectFamilyDebtDetailVO();
debtDetail.setMissingSelfDebtInfo(false);
debtDetail.setSelfTotalDebt(BigDecimal.ZERO);
debtDetail.setSpouseTotalDebt(BigDecimal.ZERO);
debtDetail.setTotalDebt(BigDecimal.ZERO);
debtDetail.setItems(List.of());
detail.setDebtDetail(debtDetail);
} else if (detail.getDebtDetail().getItems() == null) {
detail.getDebtDetail().setItems(List.of());
}
if (detail.getDebtDetail().getMissingSelfDebtInfo() == null) {
detail.getDebtDetail().setMissingSelfDebtInfo(false);
}
if (detail.getSummary() == null) {
detail.setSummary(new CcdiProjectFamilyAssetLiabilityListItemVO());
}
}
private <T> List<T> defaultList(List<T> list) {
return list == null ? List.of() : list;
}
}

View File

@@ -0,0 +1,465 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.ccdi.project.mapper.CcdiProjectSpecialCheckMapper">
<resultMap id="FamilyAssetLiabilityListItemResultMap"
type="com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetLiabilityListItemVO">
<id property="staffIdCard" column="staff_id_card"/>
<result property="staffCode" column="staff_code"/>
<result property="staffName" column="staff_name"/>
<result property="deptName" column="dept_name"/>
<result property="totalIncome" column="total_income"/>
<result property="totalAsset" column="total_asset"/>
<result property="totalDebt" column="total_debt"/>
<result property="comparisonAmount" column="comparison_amount"/>
<result property="riskLevelCode" column="risk_level_code"/>
<result property="riskLevelName" column="risk_level_name"/>
</resultMap>
<resultMap id="FamilyAssetItemResultMap"
type="com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetItemVO">
<result property="assetName" column="asset_name"/>
<result property="assetMainType" column="asset_main_type"/>
<result property="assetSubType" column="asset_sub_type"/>
<result property="holderName" column="holder_name"/>
<result property="holderIdCard" column="holder_id_card"/>
<result property="currentValue" column="current_value"/>
<result property="valuationDate" column="valuation_date"/>
</resultMap>
<resultMap id="FamilyDebtItemResultMap"
type="com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyDebtItemVO">
<result property="debtName" column="debt_name"/>
<result property="debtMainType" column="debt_main_type"/>
<result property="debtSubType" column="debt_sub_type"/>
<result property="creditorType" column="creditor_type"/>
<result property="ownerName" column="owner_name"/>
<result property="ownerIdCard" column="owner_id_card"/>
<result property="principalBalance" column="principal_balance"/>
<result property="queryDate" column="query_date"/>
</resultMap>
<resultMap id="FamilyAssetLiabilityDetailResultMap"
type="com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetLiabilityDetailVO">
<association property="incomeDetail"
javaType="com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyIncomeDetailVO">
<result property="selfIncome" column="income_self_income"/>
<result property="spouseIncome" column="income_spouse_income"/>
<result property="totalIncome" column="income_total_income"/>
</association>
<association property="assetDetail"
javaType="com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetDetailVO">
<result property="missingSelfAssetInfo" column="asset_missing_self_asset_info"/>
<result property="selfTotalAsset" column="asset_self_total_asset"/>
<result property="spouseTotalAsset" column="asset_spouse_total_asset"/>
<result property="totalAsset" column="asset_total_asset"/>
<collection property="items"
column="{projectId=project_id,staffIdCard=staff_id_card,spouseIdCard=spouse_id_card}"
ofType="com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetItemVO"
select="selectFamilyAssetItemsByScope"/>
</association>
<association property="debtDetail"
javaType="com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyDebtDetailVO">
<result property="missingSelfDebtInfo" column="debt_missing_self_debt_info"/>
<result property="selfTotalDebt" column="debt_self_total_debt"/>
<result property="spouseTotalDebt" column="debt_spouse_total_debt"/>
<result property="totalDebt" column="debt_total_debt"/>
<collection property="items"
column="{projectId=project_id,staffIdCard=staff_id_card,spouseIdCard=spouse_id_card}"
ofType="com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyDebtItemVO"
select="selectFamilyDebtItemsByScope"/>
</association>
<association property="summary"
javaType="com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetLiabilityListItemVO">
<id property="staffIdCard" column="summary_staff_id_card"/>
<result property="staffCode" column="summary_staff_code"/>
<result property="staffName" column="summary_staff_name"/>
<result property="deptName" column="summary_dept_name"/>
<result property="totalIncome" column="summary_total_income"/>
<result property="totalAsset" column="summary_total_asset"/>
<result property="totalDebt" column="summary_total_debt"/>
<result property="comparisonAmount" column="summary_comparison_amount"/>
<result property="riskLevelCode" column="summary_risk_level_code"/>
<result property="riskLevelName" column="summary_risk_level_name"/>
</association>
</resultMap>
<sql id="projectEmployeeScopeSql">
select distinct
coalesce(direct_staff.id_card, statement_staff.id_card, family_staff.id_card) as staff_id_card,
cast(coalesce(direct_staff.staff_id, statement_staff.staff_id, family_staff.staff_id) as char) as staff_code,
coalesce(direct_staff.name, statement_staff.name, family_staff.name) as staff_name,
dept.dept_name
from ccdi_bank_statement_tag_result tr
left join ccdi_base_staff direct_staff
on tr.object_type = 'STAFF_ID_CARD'
and tr.object_key = direct_staff.id_card
left join ccdi_bank_statement bs
on tr.bank_statement_id = bs.bank_statement_id
left join ccdi_base_staff statement_staff
on (tr.object_key is null or tr.object_key = '')
and bs.cret_no = statement_staff.id_card
left join ccdi_staff_fmy_relation relation
on relation.status = 1
and (
((tr.object_key is null or tr.object_key = '') and bs.cret_no = relation.relation_cert_no)
or ((tr.object_key is not null and tr.object_key != '') and tr.object_type != 'STAFF_ID_CARD'
and tr.object_key = relation.relation_cert_no)
)
left join ccdi_base_staff family_staff
on relation.person_id = family_staff.id_card
left join sys_dept dept
on dept.dept_id = coalesce(direct_staff.dept_id, statement_staff.dept_id, family_staff.dept_id)
where tr.project_id = #{projectId}
and coalesce(direct_staff.id_card, statement_staff.id_card, family_staff.id_card) is not null
</sql>
<sql id="spouseRelationSql">
select
person_id,
max(relation_name) as spouse_name,
min(relation_cert_no) as spouse_id_card,
max(annual_income) as spouse_income
from ccdi_staff_fmy_relation
where status = 1
and is_emp_family = 1
and relation_type = '配偶'
group by person_id
</sql>
<select id="selectFamilyAssetLiabilityList" resultMap="FamilyAssetLiabilityListItemResultMap">
select
aggregated.staff_id_card,
aggregated.staff_code,
aggregated.staff_name,
aggregated.dept_name,
aggregated.total_income,
aggregated.total_asset,
aggregated.total_debt,
aggregated.comparison_amount,
case
when aggregated.self_asset_record_count = 0 or aggregated.self_debt_record_count = 0 then 'MISSING_INFO'
when comparison_amount &lt;= total_asset * 1.5 then 'NORMAL'
when comparison_amount &gt; total_asset * 1.5 and comparison_amount &lt;= total_asset * 3 then 'RISK'
when comparison_amount &gt; total_asset * 3 then 'HIGH'
else 'HIGH'
end as risk_level_code,
case
when aggregated.self_asset_record_count = 0 or aggregated.self_debt_record_count = 0 then '缺少信息'
when comparison_amount &lt;= total_asset * 1.5 then '正常'
when comparison_amount &gt; total_asset * 1.5 and comparison_amount &lt;= total_asset * 3 then '存在风险'
when comparison_amount &gt; total_asset * 3 then '高风险'
else '高风险'
end as risk_level_name
from (
select
scope.staff_id_card,
scope.staff_code,
scope.staff_name,
scope.dept_name,
coalesce(base_staff.annual_income, 0) + coalesce(spouse.spouse_income, 0) as total_income,
coalesce((
select count(1)
from ccdi_asset_info asset
where asset.family_id = scope.staff_id_card
and asset.person_id = scope.staff_id_card
), 0) as self_asset_record_count,
coalesce((
select count(1)
from ccdi_debts_info debt
where debt.person_id = scope.staff_id_card
), 0) as self_debt_record_count,
coalesce((
select sum(coalesce(asset.current_value, 0))
from ccdi_asset_info asset
where asset.family_id = scope.staff_id_card
and (
asset.person_id = scope.staff_id_card
or (spouse.spouse_id_card is not null and asset.person_id = spouse.spouse_id_card)
)
), 0) as total_asset,
coalesce((
select sum(coalesce(debt.principal_balance, 0))
from ccdi_debts_info debt
where debt.person_id = scope.staff_id_card
or (spouse.spouse_id_card is not null and debt.person_id = spouse.spouse_id_card)
), 0) as total_debt,
coalesce(base_staff.annual_income, 0)
+ coalesce(spouse.spouse_income, 0)
+ coalesce((
select sum(coalesce(debt.principal_balance, 0))
from ccdi_debts_info debt
where debt.person_id = scope.staff_id_card
or (spouse.spouse_id_card is not null and debt.person_id = spouse.spouse_id_card)
), 0) as comparison_amount,
case
when coalesce((
select count(1)
from ccdi_asset_info asset
where asset.family_id = scope.staff_id_card
and asset.person_id = scope.staff_id_card
), 0) = 0
or coalesce((
select count(1)
from ccdi_debts_info debt
where debt.person_id = scope.staff_id_card
), 0) = 0 then 4
when (
coalesce(base_staff.annual_income, 0)
+ coalesce(spouse.spouse_income, 0)
+ coalesce((
select sum(coalesce(debt.principal_balance, 0))
from ccdi_debts_info debt
where debt.person_id = scope.staff_id_card
or (spouse.spouse_id_card is not null and debt.person_id = spouse.spouse_id_card)
), 0)
) &lt;= coalesce((
select sum(coalesce(asset.current_value, 0))
from ccdi_asset_info asset
where asset.family_id = scope.staff_id_card
and (
asset.person_id = scope.staff_id_card
or (spouse.spouse_id_card is not null and asset.person_id = spouse.spouse_id_card)
)
), 0) * 1.5 then 1
when (
coalesce(base_staff.annual_income, 0)
+ coalesce(spouse.spouse_income, 0)
+ coalesce((
select sum(coalesce(debt.principal_balance, 0))
from ccdi_debts_info debt
where debt.person_id = scope.staff_id_card
or (spouse.spouse_id_card is not null and debt.person_id = spouse.spouse_id_card)
), 0)
) &lt;= coalesce((
select sum(coalesce(asset.current_value, 0))
from ccdi_asset_info asset
where asset.family_id = scope.staff_id_card
and (
asset.person_id = scope.staff_id_card
or (spouse.spouse_id_card is not null and asset.person_id = spouse.spouse_id_card)
)
), 0) * 3 then 2
when (
coalesce(base_staff.annual_income, 0)
+ coalesce(spouse.spouse_income, 0)
+ coalesce((
select sum(coalesce(debt.principal_balance, 0))
from ccdi_debts_info debt
where debt.person_id = scope.staff_id_card
or (spouse.spouse_id_card is not null and debt.person_id = spouse.spouse_id_card)
), 0)
) &gt; coalesce((
select sum(coalesce(asset.current_value, 0))
from ccdi_asset_info asset
where asset.family_id = scope.staff_id_card
and (
asset.person_id = scope.staff_id_card
or (spouse.spouse_id_card is not null and asset.person_id = spouse.spouse_id_card)
)
), 0) * 3 then 3
else 3
end as risk_level_sort
from (
<include refid="projectEmployeeScopeSql"/>
) scope
left join ccdi_base_staff base_staff
on base_staff.id_card = scope.staff_id_card
left join (
<include refid="spouseRelationSql"/>
) spouse
on spouse.person_id = scope.staff_id_card
) aggregated
order by risk_level_sort desc, comparison_amount desc, staff_name asc
</select>
<select id="selectFamilyAssetLiabilityDetail" resultMap="FamilyAssetLiabilityDetailResultMap">
select
aggregated.project_id,
aggregated.staff_id_card,
aggregated.spouse_id_card,
aggregated.staff_code,
aggregated.staff_name,
aggregated.dept_name,
aggregated.self_income as income_self_income,
aggregated.spouse_income as income_spouse_income,
aggregated.total_income as income_total_income,
aggregated.missing_self_asset_info as asset_missing_self_asset_info,
aggregated.self_total_asset as asset_self_total_asset,
aggregated.spouse_total_asset as asset_spouse_total_asset,
aggregated.total_asset as asset_total_asset,
aggregated.missing_self_debt_info as debt_missing_self_debt_info,
aggregated.self_total_debt as debt_self_total_debt,
aggregated.spouse_total_debt as debt_spouse_total_debt,
aggregated.total_debt as debt_total_debt,
aggregated.staff_id_card as summary_staff_id_card,
aggregated.staff_code as summary_staff_code,
aggregated.staff_name as summary_staff_name,
aggregated.dept_name as summary_dept_name,
aggregated.total_income as summary_total_income,
aggregated.total_asset as summary_total_asset,
aggregated.total_debt as summary_total_debt,
aggregated.comparison_amount as summary_comparison_amount,
case
when aggregated.missing_self_asset_info = 1 or aggregated.missing_self_debt_info = 1 then 'MISSING_INFO'
when comparison_amount &lt;= total_asset * 1.5 then 'NORMAL'
when comparison_amount &gt; total_asset * 1.5 and comparison_amount &lt;= total_asset * 3 then 'RISK'
when comparison_amount &gt; total_asset * 3 then 'HIGH'
else 'HIGH'
end as summary_risk_level_code,
case
when aggregated.missing_self_asset_info = 1 or aggregated.missing_self_debt_info = 1 then '缺少信息'
when comparison_amount &lt;= total_asset * 1.5 then '正常'
when comparison_amount &gt; total_asset * 1.5 and comparison_amount &lt;= total_asset * 3 then '存在风险'
when comparison_amount &gt; total_asset * 3 then '高风险'
else '高风险'
end as summary_risk_level_name
from (
select
#{projectId} as project_id,
scope.staff_id_card,
scope.staff_code,
scope.staff_name,
scope.dept_name,
spouse.spouse_id_card,
coalesce(base_staff.annual_income, 0) as self_income,
coalesce(spouse.spouse_income, 0) as spouse_income,
coalesce(base_staff.annual_income, 0) + coalesce(spouse.spouse_income, 0) as total_income,
case
when coalesce((
select count(1)
from ccdi_asset_info asset
where asset.family_id = scope.staff_id_card
and asset.person_id = scope.staff_id_card
), 0) = 0 then 1
else 0
end as missing_self_asset_info,
coalesce((
select sum(coalesce(asset.current_value, 0))
from ccdi_asset_info asset
where asset.family_id = scope.staff_id_card
and asset.person_id = scope.staff_id_card
), 0) as self_total_asset,
coalesce((
select sum(coalesce(asset.current_value, 0))
from ccdi_asset_info asset
where asset.family_id = scope.staff_id_card
and spouse.spouse_id_card is not null
and asset.person_id = spouse.spouse_id_card
), 0) as spouse_total_asset,
coalesce((
select sum(coalesce(asset.current_value, 0))
from ccdi_asset_info asset
where asset.family_id = scope.staff_id_card
and (
asset.person_id = scope.staff_id_card
or (spouse.spouse_id_card is not null and asset.person_id = spouse.spouse_id_card)
)
), 0) as total_asset,
case
when coalesce((
select count(1)
from ccdi_debts_info debt
where debt.person_id = scope.staff_id_card
), 0) = 0 then 1
else 0
end as missing_self_debt_info,
coalesce((
select sum(coalesce(debt.principal_balance, 0))
from ccdi_debts_info debt
where debt.person_id = scope.staff_id_card
), 0) as self_total_debt,
coalesce((
select sum(coalesce(debt.principal_balance, 0))
from ccdi_debts_info debt
where spouse.spouse_id_card is not null
and debt.person_id = spouse.spouse_id_card
), 0) as spouse_total_debt,
coalesce((
select sum(coalesce(debt.principal_balance, 0))
from ccdi_debts_info debt
where debt.person_id = scope.staff_id_card
or (spouse.spouse_id_card is not null and debt.person_id = spouse.spouse_id_card)
), 0) as total_debt,
coalesce(base_staff.annual_income, 0)
+ coalesce(spouse.spouse_income, 0)
+ coalesce((
select sum(coalesce(debt.principal_balance, 0))
from ccdi_debts_info debt
where debt.person_id = scope.staff_id_card
or (spouse.spouse_id_card is not null and debt.person_id = spouse.spouse_id_card)
), 0) as comparison_amount
from (
<include refid="projectEmployeeScopeSql"/>
) scope
left join ccdi_base_staff base_staff
on base_staff.id_card = scope.staff_id_card
left join (
<include refid="spouseRelationSql"/>
) spouse
on spouse.person_id = scope.staff_id_card
where scope.staff_id_card = #{staffIdCard}
) aggregated
</select>
<select id="selectFamilyAssetItemsByScope" resultMap="FamilyAssetItemResultMap">
select
asset.asset_name,
asset.asset_main_type,
asset.asset_sub_type,
case
when asset.person_id = #{staffIdCard} then base_staff.name
else spouse.relation_name
end as holder_name,
asset.person_id as holder_id_card,
asset.current_value,
asset.valuation_date
from ccdi_asset_info asset
left join ccdi_base_staff base_staff
on base_staff.id_card = #{staffIdCard}
left join ccdi_staff_fmy_relation spouse
on spouse.person_id = #{staffIdCard}
and spouse.status = 1
and spouse.relation_type = '配偶'
and spouse.relation_cert_no = asset.person_id
where asset.family_id = #{staffIdCard}
and (
asset.person_id = #{staffIdCard}
or (#{spouseIdCard} is not null and asset.person_id = #{spouseIdCard})
)
order by
case when asset.person_id = #{staffIdCard} then 1 else 2 end,
asset.valuation_date desc,
asset.asset_name asc
</select>
<select id="selectFamilyDebtItemsByScope" resultMap="FamilyDebtItemResultMap">
select
debt.debt_name,
debt.debt_main_type,
debt.debt_sub_type,
debt.creditor_type,
case
when debt.person_id = #{staffIdCard} then base_staff.name
else spouse.relation_name
end as owner_name,
debt.person_id as owner_id_card,
debt.principal_balance,
debt.query_date
from ccdi_debts_info debt
left join ccdi_base_staff base_staff
on base_staff.id_card = #{staffIdCard}
left join ccdi_staff_fmy_relation spouse
on spouse.person_id = #{staffIdCard}
and spouse.status = 1
and spouse.relation_type = '配偶'
and spouse.relation_cert_no = debt.person_id
where debt.person_id = #{staffIdCard}
or (#{spouseIdCard} is not null and debt.person_id = #{spouseIdCard})
order by
case when debt.person_id = #{staffIdCard} then 1 else 2 end,
debt.query_date desc,
debt.debt_name asc
</select>
</mapper>

View File

@@ -0,0 +1,143 @@
package com.ruoyi.ccdi.project.controller;
import io.swagger.v3.oas.annotations.Operation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
class CcdiProjectSpecialCheckControllerContractTest {
@Test
void shouldExposeFamilyAssetLiabilityListEndpointContract() throws Exception {
Class<?> controllerClass = Class.forName("com.ruoyi.ccdi.project.controller.CcdiProjectSpecialCheckController");
Class<?> queryDtoClass = Class.forName(
"com.ruoyi.ccdi.project.domain.dto.CcdiProjectFamilyAssetLiabilityListQueryDTO"
);
RequestMapping requestMapping = controllerClass.getAnnotation(RequestMapping.class);
Method method = controllerClass.getMethod("getFamilyAssetLiabilityList", queryDtoClass);
GetMapping getMapping = method.getAnnotation(GetMapping.class);
Operation operation = method.getAnnotation(Operation.class);
assertNotNull(requestMapping);
assertEquals("/ccdi/project/special-check", requestMapping.value()[0]);
assertNotNull(getMapping);
assertEquals("/family-asset-liability/list", getMapping.value()[0]);
assertNotNull(operation);
assertEquals(queryDtoClass, method.getParameterTypes()[0]);
}
@Test
void shouldExposeFamilyAssetLiabilityDetailEndpointContract() throws Exception {
Class<?> controllerClass = Class.forName("com.ruoyi.ccdi.project.controller.CcdiProjectSpecialCheckController");
Class<?> queryDtoClass = Class.forName(
"com.ruoyi.ccdi.project.domain.dto.CcdiProjectFamilyAssetLiabilityDetailQueryDTO"
);
Method method = controllerClass.getMethod("getFamilyAssetLiabilityDetail", queryDtoClass);
GetMapping getMapping = method.getAnnotation(GetMapping.class);
Operation operation = method.getAnnotation(Operation.class);
assertNotNull(getMapping);
assertEquals("/family-asset-liability/detail", getMapping.value()[0]);
assertNotNull(operation);
assertEquals(queryDtoClass, method.getParameterTypes()[0]);
}
@Test
void shouldExposeFamilyAssetLiabilityDtoFields() throws Exception {
Class<?> listDtoClass = Class.forName(
"com.ruoyi.ccdi.project.domain.dto.CcdiProjectFamilyAssetLiabilityListQueryDTO"
);
Class<?> detailDtoClass = Class.forName(
"com.ruoyi.ccdi.project.domain.dto.CcdiProjectFamilyAssetLiabilityDetailQueryDTO"
);
List<String> listFields = Arrays.stream(listDtoClass.getDeclaredFields())
.map(Field::getName)
.collect(Collectors.toList());
List<String> detailFields = Arrays.stream(detailDtoClass.getDeclaredFields())
.map(Field::getName)
.collect(Collectors.toList());
assertTrue(listFields.contains("projectId"));
assertTrue(detailFields.contains("projectId"));
assertTrue(detailFields.contains("staffIdCard"));
}
@Test
void shouldExposeFamilyAssetLiabilityListVoFields() throws Exception {
Class<?> itemVoClass = Class.forName(
"com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetLiabilityListItemVO"
);
Class<?> listVoClass = Class.forName(
"com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetLiabilityListVO"
);
List<String> itemFields = Arrays.stream(itemVoClass.getDeclaredFields())
.map(Field::getName)
.collect(Collectors.toList());
List<String> listFields = Arrays.stream(listVoClass.getDeclaredFields())
.map(Field::getName)
.collect(Collectors.toList());
assertTrue(itemFields.contains("staffIdCard"));
assertTrue(itemFields.contains("staffCode"));
assertTrue(itemFields.contains("staffName"));
assertTrue(itemFields.contains("deptName"));
assertTrue(itemFields.contains("totalIncome"));
assertTrue(itemFields.contains("totalAsset"));
assertTrue(itemFields.contains("totalDebt"));
assertTrue(itemFields.contains("comparisonAmount"));
assertTrue(itemFields.contains("riskLevelCode"));
assertTrue(itemFields.contains("riskLevelName"));
assertTrue(listFields.contains("rows"));
}
@Test
void shouldExposeFamilyAssetLiabilityDetailVoFields() throws Exception {
Class<?> detailVoClass = Class.forName(
"com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetLiabilityDetailVO"
);
Class<?> incomeDetailClass = Class.forName(
"com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyIncomeDetailVO"
);
Class<?> assetDetailClass = Class.forName(
"com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetDetailVO"
);
Class<?> debtDetailClass = Class.forName(
"com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyDebtDetailVO"
);
List<String> detailFields = Arrays.stream(detailVoClass.getDeclaredFields())
.map(Field::getName)
.collect(Collectors.toList());
List<String> incomeFields = Arrays.stream(incomeDetailClass.getDeclaredFields())
.map(Field::getName)
.collect(Collectors.toList());
List<String> assetFields = Arrays.stream(assetDetailClass.getDeclaredFields())
.map(Field::getName)
.collect(Collectors.toList());
List<String> debtFields = Arrays.stream(debtDetailClass.getDeclaredFields())
.map(Field::getName)
.collect(Collectors.toList());
assertTrue(detailFields.contains("incomeDetail"));
assertTrue(detailFields.contains("assetDetail"));
assertTrue(detailFields.contains("debtDetail"));
assertTrue(detailFields.contains("summary"));
assertTrue(incomeFields.contains("selfIncome"));
assertTrue(incomeFields.contains("spouseIncome"));
assertTrue(assetFields.contains("items"));
assertTrue(debtFields.contains("items"));
}
}

View File

@@ -0,0 +1,90 @@
package com.ruoyi.ccdi.project.controller;
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectFamilyAssetLiabilityDetailQueryDTO;
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectFamilyAssetLiabilityListQueryDTO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetLiabilityDetailVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetLiabilityListVO;
import com.ruoyi.ccdi.project.service.ICcdiProjectSpecialCheckService;
import com.ruoyi.common.core.domain.AjaxResult;
import io.swagger.v3.oas.annotations.Operation;
import java.lang.reflect.Method;
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 org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class CcdiProjectSpecialCheckControllerTest {
@InjectMocks
private CcdiProjectSpecialCheckController controller;
@Mock
private ICcdiProjectSpecialCheckService specialCheckService;
@Test
void shouldExposeFamilyAssetLiabilityListEndpoint() throws Exception {
CcdiProjectFamilyAssetLiabilityListQueryDTO queryDTO = new CcdiProjectFamilyAssetLiabilityListQueryDTO();
queryDTO.setProjectId(40L);
when(specialCheckService.getFamilyAssetLiabilityList(queryDTO)).thenReturn(new CcdiProjectFamilyAssetLiabilityListVO());
AjaxResult result = controller.getFamilyAssetLiabilityList(queryDTO);
assertEquals(200, result.get("code"));
verify(specialCheckService).getFamilyAssetLiabilityList(queryDTO);
RequestMapping mapping = CcdiProjectSpecialCheckController.class.getAnnotation(RequestMapping.class);
Method method = CcdiProjectSpecialCheckController.class.getMethod(
"getFamilyAssetLiabilityList",
CcdiProjectFamilyAssetLiabilityListQueryDTO.class
);
GetMapping getMapping = method.getAnnotation(GetMapping.class);
PreAuthorize preAuthorize = method.getAnnotation(PreAuthorize.class);
Operation operation = method.getAnnotation(Operation.class);
assertNotNull(mapping);
assertEquals("/ccdi/project/special-check", mapping.value()[0]);
assertNotNull(getMapping);
assertEquals("/family-asset-liability/list", getMapping.value()[0]);
assertNotNull(preAuthorize);
assertEquals("@ss.hasPermi('ccdi:project:query')", preAuthorize.value());
assertNotNull(operation);
}
@Test
void shouldExposeFamilyAssetLiabilityDetailEndpoint() throws Exception {
CcdiProjectFamilyAssetLiabilityDetailQueryDTO queryDTO = new CcdiProjectFamilyAssetLiabilityDetailQueryDTO();
queryDTO.setProjectId(40L);
queryDTO.setStaffIdCard("330102199001011234");
when(specialCheckService.getFamilyAssetLiabilityDetail(queryDTO))
.thenReturn(new CcdiProjectFamilyAssetLiabilityDetailVO());
AjaxResult result = controller.getFamilyAssetLiabilityDetail(queryDTO);
assertEquals(200, result.get("code"));
verify(specialCheckService).getFamilyAssetLiabilityDetail(queryDTO);
Method method = CcdiProjectSpecialCheckController.class.getMethod(
"getFamilyAssetLiabilityDetail",
CcdiProjectFamilyAssetLiabilityDetailQueryDTO.class
);
GetMapping getMapping = method.getAnnotation(GetMapping.class);
PreAuthorize preAuthorize = method.getAnnotation(PreAuthorize.class);
Operation operation = method.getAnnotation(Operation.class);
assertNotNull(getMapping);
assertEquals("/family-asset-liability/detail", getMapping.value()[0]);
assertNotNull(preAuthorize);
assertEquals("@ss.hasPermi('ccdi:project:query')", preAuthorize.value());
assertNotNull(operation);
}
}

View File

@@ -0,0 +1,45 @@
package com.ruoyi.ccdi.project.mapper;
import java.nio.file.Files;
import java.nio.file.Path;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
class CcdiProjectSpecialCheckMapperDetailSqlTest {
@Test
void shouldBuildFamilyAssetLiabilityDetailStructureFromProjectScope() throws Exception {
String xml = Files.readString(Path.of("src/main/resources/mapper/ccdi/project/CcdiProjectSpecialCheckMapper.xml"));
assertTrue(xml.contains("select id=\"selectFamilyAssetLiabilityDetail\""));
assertTrue(xml.contains("select id=\"selectFamilyAssetItemsByScope\""));
assertTrue(xml.contains("select id=\"selectFamilyDebtItemsByScope\""));
assertTrue(xml.contains("scope.staff_id_card = #{staffIdCard}"));
assertTrue(xml.contains("incomeDetail"));
assertTrue(xml.contains("assetDetail"));
assertTrue(xml.contains("debtDetail"));
assertTrue(xml.contains("summary"));
assertTrue(xml.contains("self_total_asset"));
assertTrue(xml.contains("spouse_total_asset"));
assertTrue(xml.contains("self_total_debt"));
assertTrue(xml.contains("spouse_total_debt"));
assertTrue(xml.contains("asset_missing_self_asset_info"));
assertTrue(xml.contains("debt_missing_self_debt_info"));
assertTrue(xml.contains("asset_name"));
assertTrue(xml.contains("asset_main_type"));
assertTrue(xml.contains("asset_sub_type"));
assertTrue(xml.contains("holder_name"));
assertTrue(xml.contains("current_value"));
assertTrue(xml.contains("valuation_date"));
assertTrue(xml.contains("debt_name"));
assertTrue(xml.contains("debt_main_type"));
assertTrue(xml.contains("debt_sub_type"));
assertTrue(xml.contains("creditor_type"));
assertTrue(xml.contains("owner_name"));
assertTrue(xml.contains("principal_balance"));
assertTrue(xml.contains("query_date"));
assertFalse(xml.contains("ccdi_project_overview_employee_result"));
}
}

View File

@@ -0,0 +1,43 @@
package com.ruoyi.ccdi.project.mapper;
import java.nio.file.Files;
import java.nio.file.Path;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
class CcdiProjectSpecialCheckMapperListSqlTest {
@Test
void shouldAggregateFamilyAssetLiabilityListByProjectEmployeeScope() throws Exception {
String xml = Files.readString(Path.of("src/main/resources/mapper/ccdi/project/CcdiProjectSpecialCheckMapper.xml"));
String listSql = extractSelect(xml, "selectFamilyAssetLiabilityList");
assertTrue(listSql.contains("order by risk_level_sort desc, comparison_amount desc, staff_name asc"));
assertTrue(xml.contains("from ccdi_bank_statement_tag_result"));
assertTrue(xml.contains("ccdi_base_staff"));
assertTrue(xml.contains("ccdi_staff_fmy_relation"));
assertTrue(xml.contains("relation_type = '配偶'"));
assertTrue(xml.contains("annual_income"));
assertTrue(xml.contains("current_value"));
assertTrue(xml.contains("principal_balance"));
assertTrue(listSql.contains("self_asset_record_count"));
assertTrue(listSql.contains("self_debt_record_count"));
assertTrue(listSql.contains("then 'MISSING_INFO'"));
assertTrue(listSql.contains("then '缺少信息'"));
assertTrue(listSql.contains("comparison_amount"));
assertTrue(listSql.contains("&lt;= total_asset * 1.5"));
assertTrue(listSql.contains("&gt; total_asset * 1.5"));
assertTrue(listSql.contains("&lt;= total_asset * 3"));
assertTrue(listSql.contains("&gt; total_asset * 3"));
assertFalse(xml.contains("ccdi_project_overview_employee_result"));
}
private String extractSelect(String xml, String selectId) {
String start = "<select id=\"" + selectId + "\"";
int startIndex = xml.indexOf(start);
int endIndex = xml.indexOf("</select>", startIndex);
return xml.substring(startIndex, endIndex);
}
}

View File

@@ -0,0 +1,159 @@
package com.ruoyi.ccdi.project.service.impl;
import com.ruoyi.ccdi.project.domain.CcdiProject;
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectFamilyAssetLiabilityDetailQueryDTO;
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectFamilyAssetLiabilityListQueryDTO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetLiabilityDetailVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetLiabilityListItemVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetLiabilityListVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyAssetDetailVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyDebtDetailVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectFamilyIncomeDetailVO;
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
import com.ruoyi.ccdi.project.mapper.CcdiProjectSpecialCheckMapper;
import com.ruoyi.common.exception.ServiceException;
import java.math.BigDecimal;
import java.util.List;
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 static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class CcdiProjectSpecialCheckServiceImplTest {
@InjectMocks
private CcdiProjectSpecialCheckServiceImpl service;
@Mock
private CcdiProjectSpecialCheckMapper specialCheckMapper;
@Mock
private CcdiProjectMapper projectMapper;
@Test
void shouldThrowWhenProjectDoesNotExist() {
when(projectMapper.selectById(99L)).thenReturn(null);
assertThrows(ServiceException.class, () -> service.getFamilyAssetLiabilityList(buildListQuery(99L)));
assertThrows(ServiceException.class, () -> service.getFamilyAssetLiabilityDetail(buildDetailQuery(99L, "3301")));
}
@Test
void shouldReturnEmptyRowsWhenListMapperReturnsNull() {
when(projectMapper.selectById(40L)).thenReturn(buildProject(40L));
when(specialCheckMapper.selectFamilyAssetLiabilityList(40L)).thenReturn(null);
CcdiProjectFamilyAssetLiabilityListVO result = service.getFamilyAssetLiabilityList(buildListQuery(40L));
assertNotNull(result.getRows());
assertEquals(0, result.getRows().size());
}
@Test
void shouldThrowClearErrorWhenStaffDoesNotBelongToProjectScope() {
when(projectMapper.selectById(40L)).thenReturn(buildProject(40L));
when(specialCheckMapper.selectFamilyAssetLiabilityDetail(40L, "330102199001011234")).thenReturn(null);
ServiceException exception = assertThrows(
ServiceException.class,
() -> service.getFamilyAssetLiabilityDetail(buildDetailQuery(40L, "330102199001011234"))
);
assertEquals("当前员工不属于该项目专项核查范围", exception.getMessage());
}
@Test
void shouldKeepDetailSummaryAmountsAlignedWithListCaliber() {
when(projectMapper.selectById(40L)).thenReturn(buildProject(40L));
CcdiProjectFamilyAssetLiabilityListItemVO listItem = new CcdiProjectFamilyAssetLiabilityListItemVO();
listItem.setStaffIdCard("330102199001011234");
listItem.setTotalIncome(new BigDecimal("180000.00"));
listItem.setTotalAsset(new BigDecimal("520000.00"));
listItem.setTotalDebt(new BigDecimal("260000.00"));
listItem.setComparisonAmount(new BigDecimal("440000.00"));
listItem.setRiskLevelCode("RISK");
listItem.setRiskLevelName("存在风险");
when(specialCheckMapper.selectFamilyAssetLiabilityList(40L)).thenReturn(List.of(listItem));
CcdiProjectFamilyAssetLiabilityDetailVO detailVO = new CcdiProjectFamilyAssetLiabilityDetailVO();
detailVO.setIncomeDetail(new CcdiProjectFamilyIncomeDetailVO());
detailVO.setAssetDetail(new CcdiProjectFamilyAssetDetailVO());
detailVO.setDebtDetail(new CcdiProjectFamilyDebtDetailVO());
detailVO.setSummary(listItem);
when(specialCheckMapper.selectFamilyAssetLiabilityDetail(40L, "330102199001011234")).thenReturn(detailVO);
CcdiProjectFamilyAssetLiabilityListVO listResult = service.getFamilyAssetLiabilityList(buildListQuery(40L));
CcdiProjectFamilyAssetLiabilityDetailVO detailResult =
service.getFamilyAssetLiabilityDetail(buildDetailQuery(40L, "330102199001011234"));
assertEquals(listResult.getRows().getFirst().getTotalIncome(), detailResult.getSummary().getTotalIncome());
assertEquals(listResult.getRows().getFirst().getTotalAsset(), detailResult.getSummary().getTotalAsset());
assertEquals(listResult.getRows().getFirst().getTotalDebt(), detailResult.getSummary().getTotalDebt());
assertEquals(
listResult.getRows().getFirst().getComparisonAmount(),
detailResult.getSummary().getComparisonAmount()
);
assertNotNull(detailResult.getAssetDetail().getItems());
assertNotNull(detailResult.getDebtDetail().getItems());
verify(specialCheckMapper).selectFamilyAssetLiabilityList(40L);
verify(specialCheckMapper).selectFamilyAssetLiabilityDetail(40L, "330102199001011234");
}
@Test
void shouldPreserveMissingInfoFlagsForDetailCards() {
when(projectMapper.selectById(40L)).thenReturn(buildProject(40L));
CcdiProjectFamilyAssetLiabilityListItemVO summary = new CcdiProjectFamilyAssetLiabilityListItemVO();
summary.setRiskLevelCode("MISSING_INFO");
summary.setRiskLevelName("缺少信息");
CcdiProjectFamilyAssetDetailVO assetDetail = new CcdiProjectFamilyAssetDetailVO();
assetDetail.setMissingSelfAssetInfo(true);
CcdiProjectFamilyDebtDetailVO debtDetail = new CcdiProjectFamilyDebtDetailVO();
debtDetail.setMissingSelfDebtInfo(true);
CcdiProjectFamilyAssetLiabilityDetailVO detailVO = new CcdiProjectFamilyAssetLiabilityDetailVO();
detailVO.setIncomeDetail(new CcdiProjectFamilyIncomeDetailVO());
detailVO.setAssetDetail(assetDetail);
detailVO.setDebtDetail(debtDetail);
detailVO.setSummary(summary);
when(specialCheckMapper.selectFamilyAssetLiabilityDetail(40L, "330102199001011234")).thenReturn(detailVO);
CcdiProjectFamilyAssetLiabilityDetailVO result =
service.getFamilyAssetLiabilityDetail(buildDetailQuery(40L, "330102199001011234"));
assertEquals("MISSING_INFO", result.getSummary().getRiskLevelCode());
assertEquals("缺少信息", result.getSummary().getRiskLevelName());
assertTrue(result.getAssetDetail().getMissingSelfAssetInfo());
assertTrue(result.getDebtDetail().getMissingSelfDebtInfo());
}
private CcdiProject buildProject(Long projectId) {
CcdiProject project = new CcdiProject();
project.setProjectId(projectId);
return project;
}
private CcdiProjectFamilyAssetLiabilityListQueryDTO buildListQuery(Long projectId) {
CcdiProjectFamilyAssetLiabilityListQueryDTO queryDTO = new CcdiProjectFamilyAssetLiabilityListQueryDTO();
queryDTO.setProjectId(projectId);
return queryDTO;
}
private CcdiProjectFamilyAssetLiabilityDetailQueryDTO buildDetailQuery(Long projectId, String staffIdCard) {
CcdiProjectFamilyAssetLiabilityDetailQueryDTO queryDTO = new CcdiProjectFamilyAssetLiabilityDetailQueryDTO();
queryDTO.setProjectId(projectId);
queryDTO.setStaffIdCard(staffIdCard);
return queryDTO;
}
}