完成专项核查家庭资产负债实现
This commit is contained in:
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 <= total_asset * 1.5 then 'NORMAL'
|
||||||
|
when comparison_amount > total_asset * 1.5 and comparison_amount <= total_asset * 3 then 'RISK'
|
||||||
|
when comparison_amount > 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 <= total_asset * 1.5 then '正常'
|
||||||
|
when comparison_amount > total_asset * 1.5 and comparison_amount <= total_asset * 3 then '存在风险'
|
||||||
|
when comparison_amount > 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)
|
||||||
|
) <= 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)
|
||||||
|
) <= 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)
|
||||||
|
) > 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 <= total_asset * 1.5 then 'NORMAL'
|
||||||
|
when comparison_amount > total_asset * 1.5 and comparison_amount <= total_asset * 3 then 'RISK'
|
||||||
|
when comparison_amount > 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 <= total_asset * 1.5 then '正常'
|
||||||
|
when comparison_amount > total_asset * 1.5 and comparison_amount <= total_asset * 3 then '存在风险'
|
||||||
|
when comparison_amount > 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>
|
||||||
@@ -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"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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("<= total_asset * 1.5"));
|
||||||
|
assertTrue(listSql.contains("> total_asset * 1.5"));
|
||||||
|
assertTrue(listSql.contains("<= total_asset * 3"));
|
||||||
|
assertTrue(listSql.contains("> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
# 2026-03-24 专项核查页员工家庭资产负债专项核查后端实施记录
|
||||||
|
|
||||||
|
## 本次新增文件
|
||||||
|
|
||||||
|
- 控制器
|
||||||
|
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectSpecialCheckController.java`
|
||||||
|
- 服务
|
||||||
|
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiProjectSpecialCheckService.java`
|
||||||
|
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectSpecialCheckServiceImpl.java`
|
||||||
|
- Mapper
|
||||||
|
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiProjectSpecialCheckMapper.java`
|
||||||
|
- `ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectSpecialCheckMapper.xml`
|
||||||
|
- DTO
|
||||||
|
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiProjectFamilyAssetLiabilityListQueryDTO.java`
|
||||||
|
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiProjectFamilyAssetLiabilityDetailQueryDTO.java`
|
||||||
|
- VO
|
||||||
|
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectFamilyAssetLiabilityListItemVO.java`
|
||||||
|
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectFamilyAssetLiabilityListVO.java`
|
||||||
|
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectFamilyIncomeDetailVO.java`
|
||||||
|
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectFamilyAssetItemVO.java`
|
||||||
|
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectFamilyAssetDetailVO.java`
|
||||||
|
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectFamilyDebtItemVO.java`
|
||||||
|
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectFamilyDebtDetailVO.java`
|
||||||
|
- `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiProjectFamilyAssetLiabilityDetailVO.java`
|
||||||
|
- 测试
|
||||||
|
- `ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectSpecialCheckControllerContractTest.java`
|
||||||
|
- `ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectSpecialCheckControllerTest.java`
|
||||||
|
- `ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectSpecialCheckMapperListSqlTest.java`
|
||||||
|
- `ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectSpecialCheckMapperDetailSqlTest.java`
|
||||||
|
- `ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectSpecialCheckServiceImplTest.java`
|
||||||
|
|
||||||
|
## 接口与查询口径
|
||||||
|
|
||||||
|
### 查询范围
|
||||||
|
|
||||||
|
- 员工集合沿用项目内已入库流水命中且能匹配员工主数据的口径
|
||||||
|
- 员工范围由 `ccdi_bank_statement_tag_result` 结合 `ccdi_base_staff`、`ccdi_bank_statement`、`ccdi_staff_fmy_relation` 反查得到
|
||||||
|
- 仅对匹配成功的员工输出专项核查结果
|
||||||
|
|
||||||
|
### 收入口径
|
||||||
|
|
||||||
|
- 本人收入取自 `ccdi_base_staff.annual_income`
|
||||||
|
- 配偶收入取自 `ccdi_staff_fmy_relation` 中 `relation_type = '配偶'` 的 `annual_income`
|
||||||
|
- 家庭总年收入 = 本人收入 + 配偶收入,空值按 `0` 处理
|
||||||
|
|
||||||
|
### 资产口径
|
||||||
|
|
||||||
|
- 资产来源表为 `ccdi_asset_info`
|
||||||
|
- 以员工身份证号为 `family_id`
|
||||||
|
- 持有人范围仅统计员工本人身份证号与配偶身份证号
|
||||||
|
- 家庭总资产按 `current_value` 汇总
|
||||||
|
|
||||||
|
### 负债口径
|
||||||
|
|
||||||
|
- 负债来源表为 `ccdi_debts_info`
|
||||||
|
- 归属人范围仅统计员工本人身份证号与配偶身份证号
|
||||||
|
- 家庭总负债按 `principal_balance` 汇总
|
||||||
|
|
||||||
|
### 风险等级口径
|
||||||
|
|
||||||
|
- `comparisonAmount = totalIncome + totalDebt`
|
||||||
|
- `comparisonAmount <= totalAsset * 1.5` 判定为 `NORMAL / 正常`
|
||||||
|
- `comparisonAmount > totalAsset * 1.5 and <= totalAsset * 3` 判定为 `RISK / 存在风险`
|
||||||
|
- `comparisonAmount > totalAsset * 3` 判定为 `HIGH / 高风险`
|
||||||
|
|
||||||
|
## 详情返回结构
|
||||||
|
|
||||||
|
- `incomeDetail` 返回本人收入、配偶收入、家庭总收入
|
||||||
|
- `assetDetail` 返回本人资产小计、配偶资产小计、家庭总资产、资产明细列表
|
||||||
|
- `debtDetail` 返回本人负债小计、配偶负债小计、家庭总负债、负债明细列表
|
||||||
|
- `summary` 返回与列表同口径的汇总金额与风险等级
|
||||||
|
|
||||||
|
## 范围说明
|
||||||
|
|
||||||
|
- 本次实现仅覆盖员工本人及配偶
|
||||||
|
- 未扩展父母、子女、兄弟姐妹等其他家庭成员收入、资产、负债口径
|
||||||
|
- 未复用结果总览员工结果表,也未改动流水打标链路
|
||||||
|
|
||||||
|
## 增量调整
|
||||||
|
|
||||||
|
### 本人信息缺失口径
|
||||||
|
|
||||||
|
- 若员工本人名下不存在资产记录,则标记为“缺少资产信息”
|
||||||
|
- 若员工本人名下不存在负债记录,则标记为“缺少负债信息”
|
||||||
|
- 列表与详情 `summary` 统一处理为:
|
||||||
|
- 只要本人缺少资产信息或负债信息任一项
|
||||||
|
- 风险等级直接输出 `MISSING_INFO / 缺少信息`
|
||||||
|
- 不再进入正常、存在风险、高风险三档计算
|
||||||
|
|
||||||
|
### 详情结构补充
|
||||||
|
|
||||||
|
- `assetDetail` 新增 `missingSelfAssetInfo`
|
||||||
|
- `debtDetail` 新增 `missingSelfDebtInfo`
|
||||||
|
- 供前端在详情卡片中控制资产/负债小计显隐
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
# 2026-03-24 专项核查页员工家庭资产负债专项核查前端实施记录
|
||||||
|
|
||||||
|
## 本次新增与修改文件
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
- 新增 `ruoyi-ui/src/api/ccdi/projectSpecialCheck.js`
|
||||||
|
|
||||||
|
### 页面与组件
|
||||||
|
|
||||||
|
- 修改 `ruoyi-ui/src/views/ccdiProject/components/detail/SpecialCheck.vue`
|
||||||
|
- 新增 `ruoyi-ui/src/views/ccdiProject/components/detail/specialCheck.mock.js`
|
||||||
|
- 新增 `ruoyi-ui/src/views/ccdiProject/components/detail/FamilyAssetLiabilitySection.vue`
|
||||||
|
- 新增 `ruoyi-ui/src/views/ccdiProject/components/detail/FamilyAssetLiabilityDetail.vue`
|
||||||
|
|
||||||
|
### 测试
|
||||||
|
|
||||||
|
- 新增 `ruoyi-ui/tests/unit/special-check-family-asset-liability-api.test.js`
|
||||||
|
- 新增 `ruoyi-ui/tests/unit/special-check-layout.test.js`
|
||||||
|
- 新增 `ruoyi-ui/tests/unit/special-check-states.test.js`
|
||||||
|
- 新增 `ruoyi-ui/tests/unit/special-check-family-table.test.js`
|
||||||
|
- 新增 `ruoyi-ui/tests/unit/special-check-risk-tag.test.js`
|
||||||
|
- 新增 `ruoyi-ui/tests/unit/special-check-detail-expand.test.js`
|
||||||
|
- 新增 `ruoyi-ui/tests/unit/special-check-detail-layout.test.js`
|
||||||
|
- 新增 `ruoyi-ui/tests/unit/special-check-visual-alignment.test.js`
|
||||||
|
|
||||||
|
## 实现内容
|
||||||
|
|
||||||
|
### 1. 专项核查 API 封装
|
||||||
|
|
||||||
|
- 新增独立 API 文件 `projectSpecialCheck.js`
|
||||||
|
- 提供 `getFamilyAssetLiabilityList(projectId)` 与 `getFamilyAssetLiabilityDetail(projectId, staffIdCard)`
|
||||||
|
- 请求统一走 `@/utils/request`
|
||||||
|
|
||||||
|
### 2. 专项排查页主容器升级
|
||||||
|
|
||||||
|
- `SpecialCheck.vue` 从占位页改为真实三态容器
|
||||||
|
- 页面保留 `loading / empty / loaded` 三态
|
||||||
|
- 页面初始化与项目切换时都会重新触发专项核查列表加载
|
||||||
|
- `specialCheck.mock.js` 仅保留页面状态切换所需的最小结构
|
||||||
|
|
||||||
|
### 3. 员工家庭核查列表区块
|
||||||
|
|
||||||
|
- 新增 `FamilyAssetLiabilitySection.vue`
|
||||||
|
- 使用 `el-table` 展示姓名、身份证号、所属部门、家庭总年收入、家庭总资产、家庭总负债、风险情况、操作
|
||||||
|
- 风险标签统一使用 `el-tag`
|
||||||
|
- 风险码与标签类型映射:
|
||||||
|
- `NORMAL -> success`
|
||||||
|
- `RISK -> warning`
|
||||||
|
- `HIGH -> danger`
|
||||||
|
- 金额统一格式化为元并保留两位小数
|
||||||
|
|
||||||
|
### 4. 行内展开详情
|
||||||
|
|
||||||
|
- 新增 `FamilyAssetLiabilityDetail.vue`
|
||||||
|
- 详情固定拆成收入、资产、负债三组
|
||||||
|
- 详情使用当前表格行内展开,不引入弹窗、抽屉和路由跳转
|
||||||
|
- 首次点击“查看详情”时按需请求详情接口
|
||||||
|
- 详情结果按员工身份证号缓存
|
||||||
|
- 项目切换时清空展开状态与详情缓存,避免串项目数据
|
||||||
|
|
||||||
|
### 5. 视觉对齐收口
|
||||||
|
|
||||||
|
- 列表区块沿用结果总览已有的 `section-card / block-header / block-title / block-subtitle` 结构
|
||||||
|
- 表格头背景与标题区留白延续结果总览节奏
|
||||||
|
- 详情区块的标题层级统一为 `block-title / block-subtitle`
|
||||||
|
- 页面彻底移除“功能开发中”占位文案与占位图标
|
||||||
|
|
||||||
|
## 增量调整
|
||||||
|
|
||||||
|
### 缺少信息展示
|
||||||
|
|
||||||
|
- 风险标签新增 `MISSING_INFO` 映射,展示为“缺少信息”
|
||||||
|
- 当后端返回本人缺少资产信息时:
|
||||||
|
- 资产卡片隐藏小计区
|
||||||
|
- 当后端返回本人缺少负债信息时:
|
||||||
|
- 负债卡片隐藏小计区
|
||||||
|
|
||||||
|
### 详情卡片布局
|
||||||
|
|
||||||
|
- 详情区三个卡片改为桌面端横向三列均分
|
||||||
|
- 中小屏保留响应式回落为单列,避免表格挤压
|
||||||
@@ -4,6 +4,10 @@
|
|||||||
|
|
||||||
- 新增后端实施计划 `docs/plans/backend/2026-03-24-special-check-family-asset-liability-backend-implementation.md`
|
- 新增后端实施计划 `docs/plans/backend/2026-03-24-special-check-family-asset-liability-backend-implementation.md`
|
||||||
- 新增前端实施计划 `docs/plans/frontend/2026-03-24-special-check-family-asset-liability-frontend-implementation.md`
|
- 新增前端实施计划 `docs/plans/frontend/2026-03-24-special-check-family-asset-liability-frontend-implementation.md`
|
||||||
|
- 新增后端实施记录 `docs/reports/implementation/2026-03-24-special-check-family-asset-liability-backend-record.md`
|
||||||
|
- 新增后端验证记录 `docs/tests/records/2026-03-24-special-check-family-asset-liability-backend-verification.md`
|
||||||
|
- 新增前端实施记录 `docs/reports/implementation/2026-03-24-special-check-family-asset-liability-frontend-record.md`
|
||||||
|
- 新增前端验证记录 `docs/tests/records/2026-03-24-special-check-family-asset-liability-frontend-verification.md`
|
||||||
|
|
||||||
## 计划拆分结论
|
## 计划拆分结论
|
||||||
|
|
||||||
@@ -21,5 +25,7 @@
|
|||||||
|
|
||||||
## 后续动作
|
## 后续动作
|
||||||
|
|
||||||
- 按实施计划顺序执行后端与前端任务
|
- 已按实施计划顺序完成后端与前端任务
|
||||||
- 每端实施完成后分别补实施记录与验证记录
|
- 后端已完成接口、Mapper SQL、服务组装与计划内目标验证
|
||||||
|
- 前端已完成 API、主容器、列表区块、行内展开详情与计划内目标验证
|
||||||
|
- 后续可按需要执行联调验证或整理提交
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# 2026-03-24 专项核查页员工家庭资产负债专项核查后端验证记录
|
||||||
|
|
||||||
|
## 验证命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn test -pl ccdi-project -Dtest=CcdiProjectSpecialCheckControllerContractTest,CcdiProjectSpecialCheckMapperListSqlTest,CcdiProjectSpecialCheckMapperDetailSqlTest,CcdiProjectSpecialCheckServiceImplTest,CcdiProjectSpecialCheckControllerTest
|
||||||
|
```
|
||||||
|
|
||||||
|
## 验证结果
|
||||||
|
|
||||||
|
- 执行时间:2026-03-24 17:38:26 +08:00
|
||||||
|
- 执行结果:`PASS`
|
||||||
|
- 测试统计:`Tests run: 13, Failures: 0, Errors: 0, Skipped: 0`
|
||||||
|
|
||||||
|
## 输出摘要
|
||||||
|
|
||||||
|
```text
|
||||||
|
[INFO] Running com.ruoyi.ccdi.project.mapper.CcdiProjectSpecialCheckMapperListSqlTest
|
||||||
|
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
[INFO] Running com.ruoyi.ccdi.project.mapper.CcdiProjectSpecialCheckMapperDetailSqlTest
|
||||||
|
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
[INFO] Running com.ruoyi.ccdi.project.controller.CcdiProjectSpecialCheckControllerContractTest
|
||||||
|
[INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
[INFO] Running com.ruoyi.ccdi.project.controller.CcdiProjectSpecialCheckControllerTest
|
||||||
|
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
[INFO] Running com.ruoyi.ccdi.project.service.impl.CcdiProjectSpecialCheckServiceImplTest
|
||||||
|
[INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0
|
||||||
|
[INFO] BUILD SUCCESS
|
||||||
|
```
|
||||||
|
|
||||||
|
## 结论
|
||||||
|
|
||||||
|
- 后端专项核查接口契约、列表 SQL、详情 SQL、服务组装与控制器返回均已通过计划内目标验证
|
||||||
|
- 验证过程中出现 Mockito 动态加载 agent 的 JDK 警告,但不影响本次测试通过结论
|
||||||
|
|
||||||
|
## 增量验证
|
||||||
|
|
||||||
|
### 验证命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn test -pl ccdi-project -Dtest=CcdiProjectSpecialCheckControllerContractTest,CcdiProjectSpecialCheckMapperListSqlTest,CcdiProjectSpecialCheckMapperDetailSqlTest,CcdiProjectSpecialCheckServiceImplTest,CcdiProjectSpecialCheckControllerTest
|
||||||
|
```
|
||||||
|
|
||||||
|
### 验证结果
|
||||||
|
|
||||||
|
- 执行时间:2026-03-24 17:58:10 +08:00
|
||||||
|
- 执行结果:`PASS`
|
||||||
|
- 测试统计:`Tests run: 14, Failures: 0, Errors: 0, Skipped: 0`
|
||||||
|
|
||||||
|
### 变更关注点
|
||||||
|
|
||||||
|
- 新增“本人缺少资产信息 / 本人缺少负债信息”口径
|
||||||
|
- 列表与详情 `summary` 风险等级可返回 `MISSING_INFO / 缺少信息`
|
||||||
|
- 详情结构新增缺失标记,供前端控制资产/负债小计显隐
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
# 2026-03-24 专项核查页员工家庭资产负债专项核查前端验证记录
|
||||||
|
|
||||||
|
## 验证命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ruoyi-ui
|
||||||
|
node tests/unit/special-check-family-asset-liability-api.test.js
|
||||||
|
node tests/unit/special-check-layout.test.js
|
||||||
|
node tests/unit/special-check-states.test.js
|
||||||
|
node tests/unit/special-check-family-table.test.js
|
||||||
|
node tests/unit/special-check-risk-tag.test.js
|
||||||
|
node tests/unit/special-check-detail-expand.test.js
|
||||||
|
node tests/unit/special-check-detail-layout.test.js
|
||||||
|
node tests/unit/special-check-visual-alignment.test.js
|
||||||
|
```
|
||||||
|
|
||||||
|
## 验证结果
|
||||||
|
|
||||||
|
- 执行时间:2026-03-24 17:46:34 +0800
|
||||||
|
- 执行结果:`PASS`
|
||||||
|
- 测试统计:8 个前端静态验证脚本全部通过
|
||||||
|
|
||||||
|
## 输出摘要
|
||||||
|
|
||||||
|
```text
|
||||||
|
node tests/unit/special-check-family-asset-liability-api.test.js
|
||||||
|
node tests/unit/special-check-layout.test.js
|
||||||
|
node tests/unit/special-check-states.test.js
|
||||||
|
node tests/unit/special-check-family-table.test.js
|
||||||
|
node tests/unit/special-check-risk-tag.test.js
|
||||||
|
node tests/unit/special-check-detail-expand.test.js
|
||||||
|
node tests/unit/special-check-detail-layout.test.js
|
||||||
|
node tests/unit/special-check-visual-alignment.test.js
|
||||||
|
```
|
||||||
|
|
||||||
|
## 结论
|
||||||
|
|
||||||
|
- 前端 API、主容器三态、列表区块、风险标签、行内展开详情与视觉对齐约束均通过计划内验证
|
||||||
|
- 专项排查页已不再保留“功能开发中”占位实现
|
||||||
|
|
||||||
|
## 增量验证
|
||||||
|
|
||||||
|
### 验证命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ruoyi-ui
|
||||||
|
node tests/unit/special-check-family-asset-liability-api.test.js
|
||||||
|
node tests/unit/special-check-layout.test.js
|
||||||
|
node tests/unit/special-check-states.test.js
|
||||||
|
node tests/unit/special-check-family-table.test.js
|
||||||
|
node tests/unit/special-check-risk-tag.test.js
|
||||||
|
node tests/unit/special-check-detail-expand.test.js
|
||||||
|
node tests/unit/special-check-detail-layout.test.js
|
||||||
|
node tests/unit/special-check-visual-alignment.test.js
|
||||||
|
```
|
||||||
|
|
||||||
|
### 验证结果
|
||||||
|
|
||||||
|
- 执行时间:2026-03-24 17:58:10 +08:00
|
||||||
|
- 执行结果:`PASS`
|
||||||
|
- 测试统计:8 个前端静态验证脚本全部通过
|
||||||
|
|
||||||
|
### 变更关注点
|
||||||
|
|
||||||
|
- 风险标签新增 `MISSING_INFO`
|
||||||
|
- 详情资产/负债卡片支持按缺失标记隐藏小计
|
||||||
|
- 三张详情卡片保持横向均分布局
|
||||||
20
ruoyi-ui/src/api/ccdi/projectSpecialCheck.js
Normal file
20
ruoyi-ui/src/api/ccdi/projectSpecialCheck.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import request from "@/utils/request";
|
||||||
|
|
||||||
|
export function getFamilyAssetLiabilityList(projectId) {
|
||||||
|
return request({
|
||||||
|
url: "/ccdi/project/special-check/family-asset-liability/list",
|
||||||
|
method: "get",
|
||||||
|
params: { projectId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFamilyAssetLiabilityDetail(projectId, staffIdCard) {
|
||||||
|
return request({
|
||||||
|
url: "/ccdi/project/special-check/family-asset-liability/detail",
|
||||||
|
method: "get",
|
||||||
|
params: {
|
||||||
|
projectId,
|
||||||
|
staffIdCard,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,225 @@
|
|||||||
|
<template>
|
||||||
|
<div class="family-asset-liability-detail" v-loading="loading">
|
||||||
|
<div class="detail-grid">
|
||||||
|
<section class="detail-block">
|
||||||
|
<div class="block-header">
|
||||||
|
<div>
|
||||||
|
<div class="block-title">收入明细</div>
|
||||||
|
<div class="block-subtitle">展示本人、配偶与家庭收入汇总</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="detail-metric-list">
|
||||||
|
<div class="detail-metric-item">
|
||||||
|
<span class="metric-label">本人收入</span>
|
||||||
|
<span class="metric-value">{{ formatAmount(incomeDetail.selfIncome) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-metric-item">
|
||||||
|
<span class="metric-label">配偶收入</span>
|
||||||
|
<span class="metric-value">{{ formatAmount(incomeDetail.spouseIncome) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-metric-item">
|
||||||
|
<span class="metric-label">家庭总收入</span>
|
||||||
|
<span class="metric-value">{{ formatAmount(incomeDetail.totalIncome) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="detail-block">
|
||||||
|
<div class="block-header">
|
||||||
|
<div>
|
||||||
|
<div class="block-title">资产明细</div>
|
||||||
|
<div class="block-subtitle">展示本人及配偶资产小计与明细列表</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="!assetDetail.missingSelfAssetInfo" class="detail-summary">
|
||||||
|
<span>本人资产小计:{{ formatAmount(assetDetail.selfTotalAsset) }}</span>
|
||||||
|
<span>配偶资产小计:{{ formatAmount(assetDetail.spouseTotalAsset) }}</span>
|
||||||
|
</div>
|
||||||
|
<el-table v-if="assetItems.length" :data="assetItems" size="mini" class="detail-table">
|
||||||
|
<el-table-column prop="assetName" label="资产名称" min-width="140" />
|
||||||
|
<el-table-column prop="assetMainType" label="资产大类" min-width="100" />
|
||||||
|
<el-table-column prop="assetSubType" label="资产小类" min-width="120" />
|
||||||
|
<el-table-column prop="holderName" label="持有人" min-width="100" />
|
||||||
|
<el-table-column label="当前估值" min-width="120">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ formatAmount(scope.row.currentValue) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="valuationDate" label="估值日期" min-width="120" />
|
||||||
|
</el-table>
|
||||||
|
<el-empty v-else :image-size="64" description="暂无资产明细" />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="detail-block">
|
||||||
|
<div class="block-header">
|
||||||
|
<div>
|
||||||
|
<div class="block-title">负债明细</div>
|
||||||
|
<div class="block-subtitle">展示本人及配偶负债小计与明细列表</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="!debtDetail.missingSelfDebtInfo" class="detail-summary">
|
||||||
|
<span>本人负债小计:{{ formatAmount(debtDetail.selfTotalDebt) }}</span>
|
||||||
|
<span>配偶负债小计:{{ formatAmount(debtDetail.spouseTotalDebt) }}</span>
|
||||||
|
</div>
|
||||||
|
<el-table v-if="debtItems.length" :data="debtItems" size="mini" class="detail-table">
|
||||||
|
<el-table-column prop="debtName" label="负债名称" min-width="140" />
|
||||||
|
<el-table-column prop="debtMainType" label="负债大类" min-width="100" />
|
||||||
|
<el-table-column prop="debtSubType" label="负债小类" min-width="120" />
|
||||||
|
<el-table-column prop="creditorType" label="债权人类型" min-width="120" />
|
||||||
|
<el-table-column prop="ownerName" label="归属人" min-width="100" />
|
||||||
|
<el-table-column label="本金余额" min-width="120">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ formatAmount(scope.row.principalBalance) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="queryDate" label="查询日期" min-width="120" />
|
||||||
|
</el-table>
|
||||||
|
<el-empty v-else :image-size="64" description="暂无负债明细" />
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "FamilyAssetLiabilityDetail",
|
||||||
|
props: {
|
||||||
|
detail: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
incomeDetail() {
|
||||||
|
return this.detail && this.detail.incomeDetail
|
||||||
|
? this.detail.incomeDetail
|
||||||
|
: {
|
||||||
|
selfIncome: 0,
|
||||||
|
spouseIncome: 0,
|
||||||
|
totalIncome: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
assetDetail() {
|
||||||
|
return this.detail && this.detail.assetDetail
|
||||||
|
? this.detail.assetDetail
|
||||||
|
: {
|
||||||
|
missingSelfAssetInfo: false,
|
||||||
|
selfTotalAsset: 0,
|
||||||
|
spouseTotalAsset: 0,
|
||||||
|
totalAsset: 0,
|
||||||
|
items: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
debtDetail() {
|
||||||
|
return this.detail && this.detail.debtDetail
|
||||||
|
? this.detail.debtDetail
|
||||||
|
: {
|
||||||
|
missingSelfDebtInfo: false,
|
||||||
|
selfTotalDebt: 0,
|
||||||
|
spouseTotalDebt: 0,
|
||||||
|
totalDebt: 0,
|
||||||
|
items: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
assetItems() {
|
||||||
|
return Array.isArray(this.assetDetail.items) ? this.assetDetail.items : [];
|
||||||
|
},
|
||||||
|
debtItems() {
|
||||||
|
return Array.isArray(this.debtDetail.items) ? this.debtDetail.items : [];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
formatAmount(value) {
|
||||||
|
const amount = Number(value || 0);
|
||||||
|
return `${amount.toLocaleString("zh-CN", {
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
})} 元`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.family-asset-liability-detail {
|
||||||
|
padding: 8px 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 12px;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-block {
|
||||||
|
padding: 16px;
|
||||||
|
background: #f8fafc;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2937;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-subtitle {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #94a3b8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-metric-list {
|
||||||
|
display: grid;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-metric-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 16px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #475569;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-value {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #111827;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-summary {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #475569;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-table {
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.detail-table .el-table__body-wrapper) {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1200px) {
|
||||||
|
.detail-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,211 @@
|
|||||||
|
<template>
|
||||||
|
<section class="family-asset-liability-section">
|
||||||
|
<div class="section-card">
|
||||||
|
<div class="block-header">
|
||||||
|
<div>
|
||||||
|
<div class="block-title">{{ title }}</div>
|
||||||
|
<div class="block-subtitle">{{ subtitle }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-table
|
||||||
|
ref="familyTable"
|
||||||
|
v-loading="loading"
|
||||||
|
:data="rows"
|
||||||
|
class="family-table"
|
||||||
|
row-key="staffIdCard"
|
||||||
|
:expand-row-keys="expandedRowKeys"
|
||||||
|
>
|
||||||
|
<template slot="empty">
|
||||||
|
<el-empty :image-size="80" description="暂无员工家庭资产负债核查数据" />
|
||||||
|
</template>
|
||||||
|
<el-table-column type="expand" width="1">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<family-asset-liability-detail
|
||||||
|
:detail="detailCache[scope.row.staffIdCard]"
|
||||||
|
:loading="Boolean(detailLoadingMap[scope.row.staffIdCard])"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column type="index" label="序号" width="60" />
|
||||||
|
<el-table-column prop="staffName" label="姓名" min-width="100" />
|
||||||
|
<el-table-column prop="staffIdCard" label="身份证号" min-width="180" />
|
||||||
|
<el-table-column prop="deptName" label="所属部门" min-width="140" />
|
||||||
|
<el-table-column label="家庭总年收入" min-width="140">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ formatAmount(scope.row.totalIncome) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="家庭总资产" min-width="140">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ formatAmount(scope.row.totalAsset) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="家庭总负债" min-width="140">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ formatAmount(scope.row.totalDebt) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="风险情况" min-width="120">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-tag size="mini" effect="plain" :type="resolveRiskTagType(scope.row.riskLevelCode)">
|
||||||
|
{{ scope.row.riskLevelName || "-" }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="100" align="right">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button type="text" size="mini" @click="handleToggleDetail(scope.row)">
|
||||||
|
查看详情
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getFamilyAssetLiabilityDetail } from "@/api/ccdi/projectSpecialCheck";
|
||||||
|
import FamilyAssetLiabilityDetail from "./FamilyAssetLiabilityDetail";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "FamilyAssetLiabilitySection",
|
||||||
|
components: {
|
||||||
|
FamilyAssetLiabilityDetail,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
rows: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
projectId: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: "员工家庭资产负债专项核查",
|
||||||
|
},
|
||||||
|
subtitle: {
|
||||||
|
type: String,
|
||||||
|
default: "展示项目内员工家庭收入、资产、负债与风险情况",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
expandedStaffIdCard: "",
|
||||||
|
detailCache: {},
|
||||||
|
detailLoadingMap: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
expandedRowKeys() {
|
||||||
|
return this.expandedStaffIdCard ? [this.expandedStaffIdCard] : [];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
projectId() {
|
||||||
|
this.resetDetailState();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
resolveRiskTagType(riskLevelCode) {
|
||||||
|
const riskTagTypeMap = {
|
||||||
|
NORMAL: "success",
|
||||||
|
RISK: "warning",
|
||||||
|
HIGH: "danger",
|
||||||
|
MISSING_INFO: "info",
|
||||||
|
};
|
||||||
|
return riskTagTypeMap[riskLevelCode] || "info";
|
||||||
|
},
|
||||||
|
formatAmount(value) {
|
||||||
|
const amount = Number(value || 0);
|
||||||
|
return `${amount.toLocaleString("zh-CN", {
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
})} 元`;
|
||||||
|
},
|
||||||
|
async handleToggleDetail(row) {
|
||||||
|
if (!row || !row.staffIdCard) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.expandedStaffIdCard === row.staffIdCard) {
|
||||||
|
this.expandedStaffIdCard = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.expandedStaffIdCard = row.staffIdCard;
|
||||||
|
if (!this.detailCache[row.staffIdCard]) {
|
||||||
|
await this.loadFamilyDetail(row);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async loadFamilyDetail(row) {
|
||||||
|
const staffIdCard = row.staffIdCard;
|
||||||
|
this.$set(this.detailLoadingMap, staffIdCard, true);
|
||||||
|
try {
|
||||||
|
const response = await getFamilyAssetLiabilityDetail(this.projectId, staffIdCard);
|
||||||
|
const detail = (response && response.data) || {};
|
||||||
|
this.$set(this.detailCache, staffIdCard, detail);
|
||||||
|
} catch (error) {
|
||||||
|
this.$set(this.detailCache, staffIdCard, null);
|
||||||
|
console.error("加载员工家庭资产负债详情失败", error);
|
||||||
|
} finally {
|
||||||
|
this.$set(this.detailLoadingMap, staffIdCard, false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resetDetailState() {
|
||||||
|
this.expandedStaffIdCard = "";
|
||||||
|
this.detailCache = {};
|
||||||
|
this.detailLoadingMap = {};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.family-asset-liability-section {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-card {
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 0;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2937;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-subtitle {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #94a3b8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.family-table {
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.family-table th) {
|
||||||
|
background: #f8fafc;
|
||||||
|
color: #64748b;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,15 +1,39 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="special-check-container">
|
<div class="special-check-container">
|
||||||
<div class="placeholder-content">
|
<div v-if="pageState === 'loading'" class="special-check-state">
|
||||||
<i class="el-icon-search"></i>
|
<div class="state-card">
|
||||||
<p>专项排查功能开发中...</p>
|
<el-skeleton animated :rows="6" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="pageState === 'empty'" class="special-check-state">
|
||||||
|
<div class="state-card">
|
||||||
|
<el-empty description="暂无员工家庭资产负债核查数据" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="special-check-page">
|
||||||
|
<family-asset-liability-section
|
||||||
|
:rows="currentData.rows"
|
||||||
|
:loading="false"
|
||||||
|
:project-id="projectId"
|
||||||
|
:title="sectionTitle"
|
||||||
|
:subtitle="sectionSubtitle"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { createSpecialCheckLoadedData, specialCheckStateData } from "./specialCheck.mock";
|
||||||
|
import { getFamilyAssetLiabilityList } from "@/api/ccdi/projectSpecialCheck";
|
||||||
|
import FamilyAssetLiabilitySection from "./FamilyAssetLiabilitySection";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "SpecialCheck",
|
name: "SpecialCheck",
|
||||||
|
components: {
|
||||||
|
FamilyAssetLiabilitySection,
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
projectId: {
|
projectId: {
|
||||||
type: [String, Number],
|
type: [String, Number],
|
||||||
@@ -24,28 +48,85 @@ export default {
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
pageState: "loading",
|
||||||
|
realData: specialCheckStateData.loading,
|
||||||
|
sectionTitle: "员工家庭资产负债专项核查",
|
||||||
|
sectionSubtitle: "按项目员工范围聚合本人及配偶的收入、资产与负债情况",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
currentData() {
|
||||||
|
if (this.pageState === "loaded") {
|
||||||
|
return this.realData;
|
||||||
|
}
|
||||||
|
return specialCheckStateData[this.pageState] || this.realData;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
projectId(newVal) {
|
||||||
|
if (newVal) {
|
||||||
|
this.loadSpecialCheckData();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.realData = specialCheckStateData.empty;
|
||||||
|
this.pageState = "empty";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
if (this.projectId) {
|
||||||
|
this.loadSpecialCheckData();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.realData = specialCheckStateData.empty;
|
||||||
|
this.pageState = "empty";
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async loadSpecialCheckData() {
|
||||||
|
if (!this.projectId) {
|
||||||
|
this.realData = specialCheckStateData.empty;
|
||||||
|
this.pageState = "empty";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pageState = "loading";
|
||||||
|
try {
|
||||||
|
const response = await getFamilyAssetLiabilityList(this.projectId);
|
||||||
|
const listData = (response && response.data) || {};
|
||||||
|
this.realData = createSpecialCheckLoadedData({
|
||||||
|
projectId: this.projectId,
|
||||||
|
listData,
|
||||||
|
});
|
||||||
|
this.pageState = this.realData.rows.length ? "loaded" : "empty";
|
||||||
|
} catch (error) {
|
||||||
|
this.realData = specialCheckStateData.empty;
|
||||||
|
this.pageState = "empty";
|
||||||
|
console.error("加载专项核查数据失败", error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.special-check-container {
|
.special-check-container {
|
||||||
padding: 40px 20px;
|
min-height: 400px;
|
||||||
background: #fff;
|
padding: 0 0 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.special-check-state {
|
||||||
min-height: 400px;
|
min-height: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.placeholder-content {
|
.state-card {
|
||||||
text-align: center;
|
padding: 32px 24px;
|
||||||
color: #909399;
|
border-radius: 0;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
i {
|
.special-check-page {
|
||||||
font-size: 48px;
|
min-height: 400px;
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-size: 14px;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
const baseLoadedData = {
|
||||||
|
projectId: null,
|
||||||
|
rows: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
export function createSpecialCheckLoadedData({ projectId, listData } = {}) {
|
||||||
|
return {
|
||||||
|
...baseLoadedData,
|
||||||
|
projectId,
|
||||||
|
rows: Array.isArray(listData && listData.rows) ? listData.rows : [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const specialCheckStateData = {
|
||||||
|
loaded: baseLoadedData,
|
||||||
|
empty: {
|
||||||
|
...baseLoadedData,
|
||||||
|
rows: [],
|
||||||
|
},
|
||||||
|
loading: {
|
||||||
|
...baseLoadedData,
|
||||||
|
rows: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
16
ruoyi-ui/tests/unit/special-check-detail-expand.test.js
Normal file
16
ruoyi-ui/tests/unit/special-check-detail-expand.test.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
const assert = require("assert");
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const sectionSource = fs.readFileSync(
|
||||||
|
path.resolve(__dirname, "../../src/views/ccdiProject/components/detail/FamilyAssetLiabilitySection.vue"),
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(sectionSource.includes("expandedStaffIdCard"), "缺少当前展开员工状态");
|
||||||
|
assert(sectionSource.includes("detailCache"), "缺少详情缓存");
|
||||||
|
assert(sectionSource.includes("getFamilyAssetLiabilityDetail"), "首次展开应请求详情接口");
|
||||||
|
assert(sectionSource.includes("查看详情"), "缺少详情操作入口");
|
||||||
|
assert(!sectionSource.includes("el-dialog"), "不应使用弹窗详情");
|
||||||
|
assert(!sectionSource.includes("el-drawer"), "不应使用抽屉详情");
|
||||||
|
assert(!sectionSource.includes("$router"), "不应通过路由跳转查看详情");
|
||||||
33
ruoyi-ui/tests/unit/special-check-detail-layout.test.js
Normal file
33
ruoyi-ui/tests/unit/special-check-detail-layout.test.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
const assert = require("assert");
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const source = fs.readFileSync(
|
||||||
|
path.resolve(__dirname, "../../src/views/ccdiProject/components/detail/FamilyAssetLiabilityDetail.vue"),
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
|
||||||
|
[
|
||||||
|
"收入明细",
|
||||||
|
"资产明细",
|
||||||
|
"负债明细",
|
||||||
|
"本人收入",
|
||||||
|
"配偶收入",
|
||||||
|
"本人资产小计",
|
||||||
|
"配偶资产小计",
|
||||||
|
"本人负债小计",
|
||||||
|
"配偶负债小计",
|
||||||
|
"el-table",
|
||||||
|
"el-empty",
|
||||||
|
].forEach((token) => assert(source.includes(token), token));
|
||||||
|
|
||||||
|
assert(source.includes("missingSelfAssetInfo"), "资产卡片应支持缺少信息判断");
|
||||||
|
assert(source.includes("missingSelfDebtInfo"), "负债卡片应支持缺少信息判断");
|
||||||
|
assert(source.includes('v-if="!assetDetail.missingSelfAssetInfo"'), "资产小计应可隐藏");
|
||||||
|
assert(source.includes('v-if="!debtDetail.missingSelfDebtInfo"'), "负债小计应可隐藏");
|
||||||
|
|
||||||
|
assert(
|
||||||
|
source.includes("grid-template-columns: repeat(3, minmax(0, 1fr));"),
|
||||||
|
"三个详情卡片应横向均分"
|
||||||
|
);
|
||||||
|
assert(source.includes("@media (max-width: 1200px)"), "中小屏应保留响应式回落");
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
const assert = require("assert");
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const source = fs.readFileSync(
|
||||||
|
path.resolve(__dirname, "../../src/api/ccdi/projectSpecialCheck.js"),
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
|
||||||
|
[
|
||||||
|
"getFamilyAssetLiabilityList",
|
||||||
|
"getFamilyAssetLiabilityDetail",
|
||||||
|
"/ccdi/project/special-check/family-asset-liability/list",
|
||||||
|
"/ccdi/project/special-check/family-asset-liability/detail",
|
||||||
|
].forEach((token) => assert(source.includes(token), token));
|
||||||
|
|
||||||
|
const detailStart = source.indexOf("export function getFamilyAssetLiabilityDetail(projectId, staffIdCard)");
|
||||||
|
assert(detailStart >= 0, "缺少详情接口函数定义");
|
||||||
|
|
||||||
|
const detailBlock = source.slice(detailStart, detailStart + 260);
|
||||||
|
assert(detailBlock.includes("projectId"), "详情接口缺少 projectId");
|
||||||
|
assert(detailBlock.includes("staffIdCard"), "详情接口缺少 staffIdCard");
|
||||||
25
ruoyi-ui/tests/unit/special-check-family-table.test.js
Normal file
25
ruoyi-ui/tests/unit/special-check-family-table.test.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
const assert = require("assert");
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const source = fs.readFileSync(
|
||||||
|
path.resolve(__dirname, "../../src/views/ccdiProject/components/detail/FamilyAssetLiabilitySection.vue"),
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
|
||||||
|
[
|
||||||
|
"姓名",
|
||||||
|
"身份证号",
|
||||||
|
"所属部门",
|
||||||
|
"家庭总年收入",
|
||||||
|
"家庭总资产",
|
||||||
|
"家庭总负债",
|
||||||
|
"风险情况",
|
||||||
|
"操作",
|
||||||
|
"查看详情",
|
||||||
|
"el-table",
|
||||||
|
"el-table-column",
|
||||||
|
].forEach((token) => assert(source.includes(token), token));
|
||||||
|
|
||||||
|
assert(source.includes("loading"), "列表区块应接收加载状态");
|
||||||
|
assert(source.includes("rows"), "列表区块应消费列表数据");
|
||||||
13
ruoyi-ui/tests/unit/special-check-layout.test.js
Normal file
13
ruoyi-ui/tests/unit/special-check-layout.test.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
const assert = require("assert");
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const source = fs.readFileSync(
|
||||||
|
path.resolve(__dirname, "../../src/views/ccdiProject/components/detail/SpecialCheck.vue"),
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(!source.includes("专项排查功能开发中"), "不应继续保留占位文案");
|
||||||
|
assert(!source.includes("功能开发中..."), "不应继续保留功能开发中提示");
|
||||||
|
assert(source.includes("员工家庭资产负债专项核查"), "缺少专项核查卡片标题");
|
||||||
|
assert(source.includes("getFamilyAssetLiabilityList"), "主容器应接入真实列表接口");
|
||||||
15
ruoyi-ui/tests/unit/special-check-risk-tag.test.js
Normal file
15
ruoyi-ui/tests/unit/special-check-risk-tag.test.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
const assert = require("assert");
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const source = fs.readFileSync(
|
||||||
|
path.resolve(__dirname, "../../src/views/ccdiProject/components/detail/FamilyAssetLiabilitySection.vue"),
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(source.includes("el-tag"), "风险情况应使用标签展示");
|
||||||
|
assert(source.includes("resolveRiskTagType"), "缺少风险标签类型映射");
|
||||||
|
assert(source.includes("NORMAL"), "缺少正常风险码映射");
|
||||||
|
assert(source.includes("RISK"), "缺少存在风险码映射");
|
||||||
|
assert(source.includes("HIGH"), "缺少高风险码映射");
|
||||||
|
assert(source.includes("MISSING_INFO"), "缺少缺少信息风险码映射");
|
||||||
20
ruoyi-ui/tests/unit/special-check-states.test.js
Normal file
20
ruoyi-ui/tests/unit/special-check-states.test.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
const assert = require("assert");
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const vueSource = fs.readFileSync(
|
||||||
|
path.resolve(__dirname, "../../src/views/ccdiProject/components/detail/SpecialCheck.vue"),
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
const mockSource = fs.readFileSync(
|
||||||
|
path.resolve(__dirname, "../../src/views/ccdiProject/components/detail/specialCheck.mock.js"),
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
|
||||||
|
["loading", "empty", "loaded"].forEach((state) => {
|
||||||
|
assert(vueSource.includes(`pageState === "${state}"`) || vueSource.includes(`pageState === '${state}'`), state);
|
||||||
|
assert(mockSource.includes(`${state}:`), `mock 中缺少 ${state} 状态`);
|
||||||
|
});
|
||||||
|
|
||||||
|
assert(vueSource.includes("el-skeleton"), "加载态应保留骨架屏");
|
||||||
|
assert(vueSource.includes("el-empty"), "空态应保留空态组件");
|
||||||
25
ruoyi-ui/tests/unit/special-check-visual-alignment.test.js
Normal file
25
ruoyi-ui/tests/unit/special-check-visual-alignment.test.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
const assert = require("assert");
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const specialCheckSource = fs.readFileSync(
|
||||||
|
path.resolve(__dirname, "../../src/views/ccdiProject/components/detail/SpecialCheck.vue"),
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
const sectionSource = fs.readFileSync(
|
||||||
|
path.resolve(__dirname, "../../src/views/ccdiProject/components/detail/FamilyAssetLiabilitySection.vue"),
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
const detailSource = fs.readFileSync(
|
||||||
|
path.resolve(__dirname, "../../src/views/ccdiProject/components/detail/FamilyAssetLiabilityDetail.vue"),
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(!specialCheckSource.includes("功能开发中"), "不应再出现占位文案");
|
||||||
|
assert(sectionSource.includes("section-card"), "列表区块应沿用白卡容器结构");
|
||||||
|
assert(sectionSource.includes("block-header"), "列表区块应沿用标题头结构");
|
||||||
|
assert(sectionSource.includes("block-title"), "列表区块缺少标题样式");
|
||||||
|
assert(sectionSource.includes("block-subtitle"), "列表区块缺少副标题样式");
|
||||||
|
assert(sectionSource.includes(":deep(.family-table th)"), "表格头样式应与结果总览统一");
|
||||||
|
assert(sectionSource.includes("el-tag"), "风险标签应保留标签形态");
|
||||||
|
assert(detailSource.includes("block-title"), "详情区块标题应与结果总览标题层级统一");
|
||||||
Reference in New Issue
Block a user