diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiAssetInfoController.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiAssetInfoController.java index 9e01fdd..de6f572 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiAssetInfoController.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiAssetInfoController.java @@ -28,12 +28,12 @@ import java.util.ArrayList; import java.util.List; /** - * 员工资产信息Controller + * 亲属资产信息Controller * * @author ruoyi * @date 2026-03-12 */ -@Tag(name = "员工资产信息管理") +@Tag(name = "亲属资产信息管理") @RestController @RequestMapping("/ccdi/assetInfo") public class CcdiAssetInfoController extends BaseController { @@ -44,18 +44,18 @@ public class CcdiAssetInfoController extends BaseController { /** * 下载导入模板 */ - @Operation(summary = "下载资产导入模板") + @Operation(summary = "下载亲属资产导入模板") @PostMapping("/importTemplate") public void importTemplate(HttpServletResponse response) { - EasyExcelUtil.importTemplateWithDictDropdown(response, CcdiAssetInfoExcel.class, "员工资产信息"); + EasyExcelUtil.importTemplateWithDictDropdown(response, CcdiAssetInfoExcel.class, "亲属资产信息"); } /** - * 导入员工资产信息 + * 导入亲属资产信息 */ - @Operation(summary = "导入员工资产信息") - @PreAuthorize("@ss.hasPermi('ccdi:baseStaff:import')") - @Log(title = "员工资产信息", businessType = BusinessType.IMPORT) + @Operation(summary = "导入亲属资产信息") + @PreAuthorize("@ss.hasPermi('ccdi:staffFmyRelation:import')") + @Log(title = "亲属资产信息", businessType = BusinessType.IMPORT) @PostMapping("/importData") public AjaxResult importData(MultipartFile file) throws Exception { List list = EasyExcelUtil.importExcel(file.getInputStream(), CcdiAssetInfoExcel.class); @@ -74,8 +74,8 @@ public class CcdiAssetInfoController extends BaseController { /** * 查询导入状态 */ - @Operation(summary = "查询员工资产导入状态") - @PreAuthorize("@ss.hasPermi('ccdi:baseStaff:import')") + @Operation(summary = "查询亲属资产导入状态") + @PreAuthorize("@ss.hasPermi('ccdi:staffFmyRelation:import')") @GetMapping("/importStatus/{taskId}") public AjaxResult getImportStatus(@PathVariable String taskId) { return success(assetInfoImportService.getImportStatus(taskId)); @@ -84,8 +84,8 @@ public class CcdiAssetInfoController extends BaseController { /** * 查询导入失败记录 */ - @Operation(summary = "查询员工资产导入失败记录") - @PreAuthorize("@ss.hasPermi('ccdi:baseStaff:import')") + @Operation(summary = "查询亲属资产导入失败记录") + @PreAuthorize("@ss.hasPermi('ccdi:staffFmyRelation:import')") @GetMapping("/importFailures/{taskId}") public TableDataInfo getImportFailures( @PathVariable String taskId, diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/CcdiAssetInfo.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/CcdiAssetInfo.java index 4370429..768e80e 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/CcdiAssetInfo.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/CcdiAssetInfo.java @@ -13,7 +13,7 @@ import java.math.BigDecimal; import java.util.Date; /** - * 员工资产信息对象 ccdi_asset_info + * 资产信息对象 ccdi_asset_info * * @author ruoyi * @date 2026-03-12 @@ -29,10 +29,10 @@ public class CcdiAssetInfo implements Serializable { @TableId(type = IdType.AUTO) private Long assetId; - /** 归属员工身份证号 */ + /** 归属员工证件号 */ private String familyId; - /** 资产实际持有人身份证号 */ + /** 资产实际持有人证件号 */ private String personId; /** 资产大类 */ diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/CcdiAssetInfoDTO.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/CcdiAssetInfoDTO.java index 03b2fa5..ec9e62c 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/CcdiAssetInfoDTO.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/CcdiAssetInfoDTO.java @@ -4,7 +4,6 @@ import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Size; import lombok.Data; @@ -14,24 +13,18 @@ import java.math.BigDecimal; import java.util.Date; /** - * 员工资产信息DTO + * 亲属资产信息DTO * * @author ruoyi * @date 2026-03-12 */ @Data -@Schema(description = "员工资产信息") +@Schema(description = "亲属资产信息") public class CcdiAssetInfoDTO implements Serializable { @Serial private static final long serialVersionUID = 1L; - /** 资产实际持有人身份证号 */ - @NotBlank(message = "资产实际持有人身份证号不能为空") - @Pattern(regexp = "^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[0-9Xx]$", message = "资产实际持有人身份证号格式不正确") - @Schema(description = "资产实际持有人身份证号") - private String personId; - /** 资产大类 */ @NotBlank(message = "资产大类不能为空") @Size(max = 20, message = "资产大类长度不能超过20个字符") diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/CcdiStaffFmyRelationAddDTO.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/CcdiStaffFmyRelationAddDTO.java index 8ca8ee7..d53c105 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/CcdiStaffFmyRelationAddDTO.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/CcdiStaffFmyRelationAddDTO.java @@ -10,6 +10,7 @@ import lombok.Data; import java.io.Serial; import java.io.Serializable; import java.util.Date; +import java.util.List; /** * 员工亲属关系新增DTO @@ -116,4 +117,8 @@ public class CcdiStaffFmyRelationAddDTO implements Serializable { /** 备注 */ @Schema(description = "备注") private String remark; + + /** 亲属资产列表 */ + @Schema(description = "亲属资产列表") + private List assetInfoList; } diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/CcdiStaffFmyRelationEditDTO.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/CcdiStaffFmyRelationEditDTO.java index d9373b8..db97fab 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/CcdiStaffFmyRelationEditDTO.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/CcdiStaffFmyRelationEditDTO.java @@ -11,6 +11,7 @@ import lombok.Data; import java.io.Serial; import java.io.Serializable; import java.util.Date; +import java.util.List; /** * 员工亲属关系编辑DTO @@ -122,4 +123,8 @@ public class CcdiStaffFmyRelationEditDTO implements Serializable { /** 备注 */ @Schema(description = "备注") private String remark; + + /** 亲属资产列表 */ + @Schema(description = "亲属资产列表") + private List assetInfoList; } diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/excel/CcdiAssetInfoExcel.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/excel/CcdiAssetInfoExcel.java index 1091983..32995c7 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/excel/CcdiAssetInfoExcel.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/excel/CcdiAssetInfoExcel.java @@ -11,7 +11,7 @@ import java.math.BigDecimal; import java.util.Date; /** - * 员工资产信息Excel导入导出对象 + * 亲属资产信息Excel导入导出对象 * * @author ruoyi * @date 2026-03-12 @@ -22,8 +22,8 @@ public class CcdiAssetInfoExcel implements Serializable { @Serial private static final long serialVersionUID = 1L; - /** 资产实际持有人身份证号 */ - @ExcelProperty(value = "资产实际持有人身份证号*", index = 0) + /** 关系人证件号 */ + @ExcelProperty(value = "关系人证件号*", index = 0) @ColumnWidth(22) @Required private String personId; diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/AssetImportFailureVO.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/AssetImportFailureVO.java index 6347c6b..aad259e 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/AssetImportFailureVO.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/AssetImportFailureVO.java @@ -6,17 +6,17 @@ import lombok.Data; import java.math.BigDecimal; /** - * 员工资产信息导入失败记录VO + * 亲属资产信息导入失败记录VO * * @author ruoyi * @date 2026-03-12 */ @Data -@Schema(description = "员工资产信息导入失败记录") +@Schema(description = "亲属资产信息导入失败记录") public class AssetImportFailureVO { - /** 资产实际持有人身份证号 */ - @Schema(description = "资产实际持有人身份证号") + /** 关系人证件号 */ + @Schema(description = "关系人证件号") private String personId; /** 资产大类 */ diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CcdiAssetInfoVO.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CcdiAssetInfoVO.java index 5ca36f5..e0d86f3 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CcdiAssetInfoVO.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CcdiAssetInfoVO.java @@ -10,20 +10,20 @@ import java.math.BigDecimal; import java.util.Date; /** - * 员工资产信息VO + * 资产信息VO * * @author ruoyi * @date 2026-03-12 */ @Data -@Schema(description = "员工资产信息") +@Schema(description = "资产信息") public class CcdiAssetInfoVO implements Serializable { @Serial private static final long serialVersionUID = 1L; - /** 资产实际持有人身份证号 */ - @Schema(description = "资产实际持有人身份证号") + /** 资产实际持有人证件号 */ + @Schema(description = "资产实际持有人证件号") private String personId; /** 资产大类 */ diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CcdiStaffFmyRelationVO.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CcdiStaffFmyRelationVO.java index 4042056..a35b981 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CcdiStaffFmyRelationVO.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CcdiStaffFmyRelationVO.java @@ -7,6 +7,7 @@ import lombok.Data; import java.io.Serial; import java.io.Serializable; import java.util.Date; +import java.util.List; /** * 员工亲属关系VO @@ -141,4 +142,8 @@ public class CcdiStaffFmyRelationVO implements Serializable { /** 更新人 */ @Schema(description = "更新人") private String updatedBy; + + /** 亲属资产列表 */ + @Schema(description = "亲属资产列表") + private List assetInfoList; } diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/CcdiAssetInfoMapper.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/CcdiAssetInfoMapper.java index ccd4098..7f2231d 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/CcdiAssetInfoMapper.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/CcdiAssetInfoMapper.java @@ -8,7 +8,7 @@ import java.util.List; import java.util.Map; /** - * 员工资产信息 数据层 + * 资产信息 数据层 * * @author ruoyi * @date 2026-03-12 @@ -23,6 +23,16 @@ public interface CcdiAssetInfoMapper extends BaseMapper { */ List selectByFamilyId(@Param("familyId") String familyId); + /** + * 按归属键查询资产列表 + * + * @param familyId 归属员工证件号 + * @param personId 资产实际持有人证件号 + * @return 资产列表 + */ + List selectByFamilyIdAndPersonId(@Param("familyId") String familyId, + @Param("personId") String personId); + /** * 按归属员工身份证号删除资产 * @@ -31,6 +41,16 @@ public interface CcdiAssetInfoMapper extends BaseMapper { */ int deleteByFamilyId(@Param("familyId") String familyId); + /** + * 按归属键删除资产 + * + * @param familyId 归属员工证件号 + * @param personId 资产实际持有人证件号 + * @return 影响行数 + */ + int deleteByFamilyIdAndPersonId(@Param("familyId") String familyId, + @Param("personId") String personId); + /** * 批量删除归属员工资产 * @@ -56,18 +76,10 @@ public interface CcdiAssetInfoMapper extends BaseMapper { int insertBatch(@Param("list") List list); /** - * 按员工身份证号查询归属信息 + * 按关系人证件号查询归属员工候选 * - * @param personIds 资产实际持有人身份证号列表 + * @param relationCertNos 关系人证件号列表 * @return 归属映射 */ - List> selectOwnerByEmployeeIdCards(@Param("personIds") List personIds); - - /** - * 按员工家庭关系身份证号查询归属信息 - * - * @param personIds 资产实际持有人身份证号列表 - * @return 归属映射 - */ - List> selectOwnerByFamilyRelationIdCards(@Param("personIds") List personIds); + List> selectOwnerCandidatesByRelationCertNos(@Param("relationCertNos") List relationCertNos); } diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiAssetInfoService.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiAssetInfoService.java index bdd79b0..1de9f8e 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiAssetInfoService.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiAssetInfoService.java @@ -21,6 +21,15 @@ public interface ICcdiAssetInfoService { */ List selectByFamilyId(String familyId); + /** + * 按归属键查询资产列表 + * + * @param familyId 归属员工证件号 + * @param personId 资产实际持有人证件号 + * @return 资产列表 + */ + List selectByFamilyIdAndPersonId(String familyId, String personId); + /** * 按归属员工身份证号覆盖资产列表 * @@ -29,6 +38,15 @@ public interface ICcdiAssetInfoService { */ void replaceByFamilyId(String familyId, List assetInfoList); + /** + * 按归属键覆盖资产列表 + * + * @param familyId 归属员工证件号 + * @param personId 资产实际持有人证件号 + * @param assetInfoList 资产列表 + */ + void replaceByFamilyIdAndPersonId(String familyId, String personId, List assetInfoList); + /** * 删除单个员工资产 * @@ -37,6 +55,15 @@ public interface ICcdiAssetInfoService { */ int deleteByFamilyId(String familyId); + /** + * 按归属键删除资产 + * + * @param familyId 归属员工证件号 + * @param personId 资产实际持有人证件号 + * @return 影响行数 + */ + int deleteByFamilyIdAndPersonId(String familyId, String personId); + /** * 批量删除员工资产 * diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiAssetInfoImportServiceImpl.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiAssetInfoImportServiceImpl.java index 7ae25a8..9dc4a37 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiAssetInfoImportServiceImpl.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiAssetInfoImportServiceImpl.java @@ -30,7 +30,7 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** - * 员工资产信息异步导入服务层处理 + * 亲属资产信息异步导入服务层处理 * * @author ruoyi * @date 2026-03-12 @@ -97,10 +97,10 @@ public class CcdiAssetInfoImportServiceImpl implements ICcdiAssetInfoImportServi validateExcel(excel); Set familyIds = ownerMap.get(excel.getPersonId()); if (familyIds == null || familyIds.isEmpty()) { - throw new RuntimeException("未找到资产归属员工"); + throw new RuntimeException("未找到亲属资产归属员工"); } if (familyIds.size() > 1) { - throw new RuntimeException("资产归属员工不唯一"); + throw new RuntimeException("亲属资产归属员工不唯一"); } CcdiAssetInfo assetInfo = new CcdiAssetInfo(); @@ -164,8 +164,7 @@ public class CcdiAssetInfoImportServiceImpl implements ICcdiAssetInfoImportServi private Map> buildOwnerMap(List personIds) { Map> result = new LinkedHashMap<>(); - mergeOwnerMappings(result, assetInfoMapper.selectOwnerByEmployeeIdCards(personIds)); - mergeOwnerMappings(result, assetInfoMapper.selectOwnerByFamilyRelationIdCards(personIds)); + mergeOwnerMappings(result, assetInfoMapper.selectOwnerCandidatesByRelationCertNos(personIds)); return result; } @@ -185,7 +184,7 @@ public class CcdiAssetInfoImportServiceImpl implements ICcdiAssetInfoImportServi private void validateExcel(CcdiAssetInfoExcel excel) { if (StringUtils.isEmpty(excel.getPersonId())) { - throw new RuntimeException("资产实际持有人身份证号不能为空"); + throw new RuntimeException("关系人证件号不能为空"); } if (StringUtils.isEmpty(excel.getAssetMainType())) { throw new RuntimeException("资产大类不能为空"); diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiAssetInfoServiceImpl.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiAssetInfoServiceImpl.java index f28a4b1..d012dd0 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiAssetInfoServiceImpl.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiAssetInfoServiceImpl.java @@ -30,22 +30,21 @@ public class CcdiAssetInfoServiceImpl implements ICcdiAssetInfoService { return assetInfoMapper.selectByFamilyId(familyId); } + @Override + public List selectByFamilyIdAndPersonId(String familyId, String personId) { + return assetInfoMapper.selectByFamilyIdAndPersonId(familyId, personId); + } + @Override @Transactional public void replaceByFamilyId(String familyId, List assetInfoList) { - assetInfoMapper.deleteByFamilyId(familyId); - if (assetInfoList == null || assetInfoList.isEmpty()) { - return; - } + replaceAssets(familyId, familyId, assetInfoList, true); + } - List saveList = assetInfoList.stream() - .filter(item -> !isEmptyRow(item)) - .map(item -> toEntity(familyId, item)) - .toList(); - - if (!saveList.isEmpty()) { - assetInfoMapper.insertBatch(saveList); - } + @Override + @Transactional + public void replaceByFamilyIdAndPersonId(String familyId, String personId, List assetInfoList) { + replaceAssets(familyId, personId, assetInfoList, false); } @Override @@ -53,6 +52,11 @@ public class CcdiAssetInfoServiceImpl implements ICcdiAssetInfoService { return assetInfoMapper.deleteByFamilyId(familyId); } + @Override + public int deleteByFamilyIdAndPersonId(String familyId, String personId) { + return assetInfoMapper.deleteByFamilyIdAndPersonId(familyId, personId); + } + @Override public int deleteByFamilyIds(List familyIds) { if (familyIds == null || familyIds.isEmpty()) { @@ -61,16 +65,39 @@ public class CcdiAssetInfoServiceImpl implements ICcdiAssetInfoService { return assetInfoMapper.deleteByFamilyIds(familyIds); } - private CcdiAssetInfo toEntity(String familyId, CcdiAssetInfoDTO dto) { + private void replaceAssets(String familyId, String personId, List assetInfoList, boolean deleteByFamilyOnly) { + if (deleteByFamilyOnly) { + assetInfoMapper.deleteByFamilyId(familyId); + } else { + assetInfoMapper.deleteByFamilyIdAndPersonId(familyId, personId); + } + if (assetInfoList == null || assetInfoList.isEmpty()) { + return; + } + + List saveList = assetInfoList.stream() + .filter(item -> !isEmptyRow(item)) + .map(item -> { + validateAsset(item); + return toEntity(familyId, personId, item); + }) + .toList(); + + if (!saveList.isEmpty()) { + assetInfoMapper.insertBatch(saveList); + } + } + + private CcdiAssetInfo toEntity(String familyId, String personId, CcdiAssetInfoDTO dto) { CcdiAssetInfo assetInfo = new CcdiAssetInfo(); BeanUtils.copyProperties(dto, assetInfo); assetInfo.setFamilyId(familyId); + assetInfo.setPersonId(personId); return assetInfo; } private boolean isEmptyRow(CcdiAssetInfoDTO dto) { - return StringUtils.isEmpty(dto.getPersonId()) - && StringUtils.isEmpty(dto.getAssetMainType()) + return StringUtils.isEmpty(dto.getAssetMainType()) && StringUtils.isEmpty(dto.getAssetSubType()) && StringUtils.isEmpty(dto.getAssetName()) && dto.getCurrentValue() == null @@ -81,4 +108,22 @@ public class CcdiAssetInfoServiceImpl implements ICcdiAssetInfoService { && dto.getValuationDate() == null && StringUtils.isEmpty(dto.getRemarks()); } + + private void validateAsset(CcdiAssetInfoDTO dto) { + if (StringUtils.isEmpty(dto.getAssetMainType())) { + throw new RuntimeException("资产大类不能为空"); + } + if (StringUtils.isEmpty(dto.getAssetSubType())) { + throw new RuntimeException("资产小类不能为空"); + } + if (StringUtils.isEmpty(dto.getAssetName())) { + throw new RuntimeException("资产名称不能为空"); + } + if (dto.getCurrentValue() == null) { + throw new RuntimeException("当前估值不能为空"); + } + if (StringUtils.isEmpty(dto.getAssetStatus())) { + throw new RuntimeException("资产状态不能为空"); + } + } } diff --git a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffFmyRelationServiceImpl.java b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffFmyRelationServiceImpl.java index 4af2af9..8998be2 100644 --- a/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffFmyRelationServiceImpl.java +++ b/ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffFmyRelationServiceImpl.java @@ -1,13 +1,16 @@ package com.ruoyi.info.collection.service.impl; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.info.collection.domain.CcdiAssetInfo; import com.ruoyi.info.collection.domain.CcdiStaffFmyRelation; +import com.ruoyi.info.collection.domain.vo.CcdiAssetInfoVO; import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationAddDTO; import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationEditDTO; import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationQueryDTO; import com.ruoyi.info.collection.domain.excel.CcdiStaffFmyRelationExcel; import com.ruoyi.info.collection.domain.vo.CcdiStaffFmyRelationVO; import com.ruoyi.info.collection.mapper.CcdiStaffFmyRelationMapper; +import com.ruoyi.info.collection.service.ICcdiAssetInfoService; import com.ruoyi.info.collection.service.ICcdiStaffFmyRelationImportService; import com.ruoyi.info.collection.service.ICcdiStaffFmyRelationService; import com.ruoyi.common.utils.SecurityUtils; @@ -19,6 +22,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -42,6 +46,9 @@ public class CcdiStaffFmyRelationServiceImpl implements ICcdiStaffFmyRelationSer @Resource private RedisTemplate redisTemplate; + @Resource + private ICcdiAssetInfoService assetInfoService; + /** * 查询员工亲属关系列表 * @@ -90,7 +97,19 @@ public class CcdiStaffFmyRelationServiceImpl implements ICcdiStaffFmyRelationSer */ @Override public CcdiStaffFmyRelationVO selectRelationById(Long id) { - return relationMapper.selectRelationById(id); + CcdiStaffFmyRelationVO relationVO = relationMapper.selectRelationById(id); + if (relationVO == null) { + return null; + } + if (StringUtils.isNotEmpty(relationVO.getPersonId()) && StringUtils.isNotEmpty(relationVO.getRelationCertNo())) { + List assetInfoList = assetInfoService + .selectByFamilyIdAndPersonId(relationVO.getPersonId(), relationVO.getRelationCertNo()) + .stream() + .map(this::toAssetInfoVO) + .toList(); + relationVO.setAssetInfoList(assetInfoList); + } + return relationVO; } /** @@ -114,6 +133,7 @@ public class CcdiStaffFmyRelationServiceImpl implements ICcdiStaffFmyRelationSer } int result = relationMapper.insert(relation); + assetInfoService.replaceByFamilyIdAndPersonId(addDTO.getPersonId(), addDTO.getRelationCertNo(), addDTO.getAssetInfoList()); return result; } @@ -126,9 +146,19 @@ public class CcdiStaffFmyRelationServiceImpl implements ICcdiStaffFmyRelationSer @Override @Transactional public int updateRelation(CcdiStaffFmyRelationEditDTO editDTO) { + CcdiStaffFmyRelation existing = relationMapper.selectById(editDTO.getId()); + if (existing == null) { + throw new RuntimeException("员工亲属关系不存在"); + } + if (!StringUtils.equals(existing.getRelationCertType(), editDTO.getRelationCertType()) + || !StringUtils.equals(existing.getRelationCertNo(), editDTO.getRelationCertNo())) { + throw new RuntimeException("关系人证件类型/证件号码不允许修改"); + } + CcdiStaffFmyRelation relation = new CcdiStaffFmyRelation(); BeanUtils.copyProperties(editDTO, relation); int result = relationMapper.updateById(relation); + assetInfoService.replaceByFamilyIdAndPersonId(editDTO.getPersonId(), editDTO.getRelationCertNo(), editDTO.getAssetInfoList()); return result; } @@ -141,7 +171,15 @@ public class CcdiStaffFmyRelationServiceImpl implements ICcdiStaffFmyRelationSer @Override @Transactional public int deleteRelationByIds(Long[] ids) { - return relationMapper.deleteBatchIds(java.util.List.of(ids)); + List idList = java.util.List.of(ids); + List relationList = relationMapper.selectBatchIds(idList); + for (CcdiStaffFmyRelation relation : relationList) { + if (StringUtils.isEmpty(relation.getPersonId()) || StringUtils.isEmpty(relation.getRelationCertNo())) { + continue; + } + assetInfoService.deleteByFamilyIdAndPersonId(relation.getPersonId(), relation.getRelationCertNo()); + } + return relationMapper.deleteBatchIds(idList); } /** @@ -184,4 +222,10 @@ public class CcdiStaffFmyRelationServiceImpl implements ICcdiStaffFmyRelationSer return taskId; } + + private CcdiAssetInfoVO toAssetInfoVO(CcdiAssetInfo assetInfo) { + CcdiAssetInfoVO assetInfoVO = new CcdiAssetInfoVO(); + BeanUtils.copyProperties(assetInfo, assetInfoVO); + return assetInfoVO; + } } diff --git a/ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiAssetInfoMapper.xml b/ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiAssetInfoMapper.xml index 59c8e3b..da3dd28 100644 --- a/ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiAssetInfoMapper.xml +++ b/ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiAssetInfoMapper.xml @@ -34,13 +34,14 @@ ORDER BY create_time DESC, asset_id DESC - SELECT asset_id, family_id, person_id, asset_main_type, asset_sub_type, asset_name, ownership_ratio, purchase_eval_date, original_value, current_value, valuation_date, asset_status, remarks, create_by, create_time, update_by, update_time FROM ccdi_asset_info - WHERE person_id = #{personId} + WHERE family_id = #{familyId} + AND person_id = #{personId} ORDER BY create_time DESC, asset_id DESC @@ -49,6 +50,12 @@ WHERE family_id = #{familyId} + + DELETE FROM ccdi_asset_info + WHERE family_id = #{familyId} + AND person_id = #{personId} + + DELETE FROM ccdi_asset_info WHERE family_id IN @@ -70,26 +77,15 @@ - - - SELECT relation_cert_no AS personId, person_id AS familyId FROM ccdi_staff_fmy_relation WHERE is_emp_family = 1 AND relation_cert_no IN - - #{personId} + + #{relationCertNo} diff --git a/ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiStaffFmyRelationMapper.xml b/ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiStaffFmyRelationMapper.xml index 119907a..024caf4 100644 --- a/ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiStaffFmyRelationMapper.xml +++ b/ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiStaffFmyRelationMapper.xml @@ -46,33 +46,32 @@ r.created_by, r.create_time, r.updated_by, r.update_time FROM ccdi_staff_fmy_relation r LEFT JOIN ccdi_base_staff s ON r.person_id = s.id_card - - r.is_emp_family = 1 - - AND r.person_id = #{query.personId} - - - AND s.name LIKE CONCAT('%', #{query.personName}, '%') - - - AND r.relation_type = #{query.relationType} - - - AND r.relation_name LIKE CONCAT('%', #{query.relationName}, '%') - - - AND r.status = #{query.status} - - - AND r.data_source = #{query.dataSource} - - - AND r.effective_date >= #{query.effectiveDateStart} - - - AND r.effective_date <= #{query.effectiveDateEnd} - - + WHERE 1 = 1 + AND r.is_emp_family = 1 + + AND r.person_id = #{query.personId} + + + AND s.name LIKE CONCAT('%', #{query.personName}, '%') + + + AND r.relation_type = #{query.relationType} + + + AND r.relation_name LIKE CONCAT('%', #{query.relationName}, '%') + + + AND r.status = #{query.status} + + + AND r.data_source = #{query.dataSource} + + + AND r.effective_date >= #{query.effectiveDateStart} + + + AND r.effective_date <= #{query.effectiveDateEnd} + ORDER BY r.create_time DESC @@ -101,33 +100,32 @@ r.created_by, r.create_time, r.updated_by, r.update_time FROM ccdi_staff_fmy_relation r LEFT JOIN ccdi_base_staff s ON r.person_id = s.id_card - - r.is_emp_family = 1 - - AND r.person_id = #{query.personId} - - - AND s.name LIKE CONCAT('%', #{query.personName}, '%') - - - AND r.relation_type = #{query.relationType} - - - AND r.relation_name LIKE CONCAT('%', #{query.relationName}, '%') - - - AND r.status = #{query.status} - - - AND r.data_source = #{query.dataSource} - - - AND r.effective_date >= #{query.effectiveDateStart} - - - AND r.effective_date <= #{query.effectiveDateEnd} - - + WHERE 1 = 1 + AND r.is_emp_family = 1 + + AND r.person_id = #{query.personId} + + + AND s.name LIKE CONCAT('%', #{query.personName}, '%') + + + AND r.relation_type = #{query.relationType} + + + AND r.relation_name LIKE CONCAT('%', #{query.relationName}, '%') + + + AND r.status = #{query.status} + + + AND r.data_source = #{query.dataSource} + + + AND r.effective_date >= #{query.effectiveDateStart} + + + AND r.effective_date <= #{query.effectiveDateEnd} + ORDER BY r.create_time DESC diff --git a/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/controller/CcdiAssetInfoControllerTest.java b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/controller/CcdiAssetInfoControllerTest.java index dd090ab..fb070a3 100644 --- a/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/controller/CcdiAssetInfoControllerTest.java +++ b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/controller/CcdiAssetInfoControllerTest.java @@ -2,7 +2,11 @@ package com.ruoyi.info.collection.controller; import com.ruoyi.common.constant.HttpStatus; import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.info.collection.domain.excel.CcdiAssetInfoExcel; +import com.ruoyi.info.collection.domain.vo.AssetImportFailureVO; +import com.ruoyi.info.collection.domain.vo.ImportResultVO; +import com.ruoyi.info.collection.domain.vo.ImportStatusVO; import com.ruoyi.info.collection.service.ICcdiAssetInfoImportService; import com.ruoyi.info.collection.utils.EasyExcelUtil; import org.junit.jupiter.api.Test; @@ -13,8 +17,8 @@ import org.mockito.MockedStatic; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.mock.web.MockMultipartFile; -import java.nio.charset.StandardCharsets; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -72,6 +76,44 @@ class CcdiAssetInfoControllerTest { assertEquals(HttpStatus.SUCCESS, result.get(AjaxResult.CODE_TAG)); assertEquals("导入任务已提交,正在后台处理", result.get(AjaxResult.MSG_TAG)); + ImportResultVO data = (ImportResultVO) result.get(AjaxResult.DATA_TAG); + assertEquals("task-1", data.getTaskId()); + } + } + + @Test + void getImportStatus_shouldDelegateToImportService() { + ImportStatusVO statusVO = new ImportStatusVO(); + statusVO.setTaskId("task-2"); + when(assetInfoImportService.getImportStatus("task-2")).thenReturn(statusVO); + + AjaxResult result = controller.getImportStatus("task-2"); + + assertEquals(HttpStatus.SUCCESS, result.get(AjaxResult.CODE_TAG)); + assertEquals(statusVO, result.get(AjaxResult.DATA_TAG)); + } + + @Test + void getImportFailures_shouldReturnPagedRows() { + AssetImportFailureVO failure1 = new AssetImportFailureVO(); + failure1.setPersonId("A1"); + AssetImportFailureVO failure2 = new AssetImportFailureVO(); + failure2.setPersonId("A2"); + when(assetInfoImportService.getImportFailures("task-3")).thenReturn(List.of(failure1, failure2)); + + TableDataInfo result = controller.getImportFailures("task-3", 2, 1); + + assertEquals(2, result.getTotal()); + assertEquals(1, result.getRows().size()); + assertEquals("A2", ((AssetImportFailureVO) result.getRows().get(0)).getPersonId()); + } + + @Test + void importTemplate_shouldUseRelativeAssetTemplateName() { + try (MockedStatic mocked = mockStatic(EasyExcelUtil.class)) { + controller.importTemplate(null); + + mocked.verify(() -> EasyExcelUtil.importTemplateWithDictDropdown(null, CcdiAssetInfoExcel.class, "亲属资产信息")); } } } diff --git a/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/mapper/CcdiAssetInfoMapperTest.java b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/mapper/CcdiAssetInfoMapperTest.java index b68db32..4e63b37 100644 --- a/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/mapper/CcdiAssetInfoMapperTest.java +++ b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/mapper/CcdiAssetInfoMapperTest.java @@ -26,37 +26,33 @@ class CcdiAssetInfoMapperTest { private static final String RESOURCE = "mapper/info/collection/CcdiAssetInfoMapper.xml"; @Test - void selectByFamilyId_shouldFilterByFamilyId() throws Exception { + void selectByFamilyIdAndPersonId_shouldFilterByOwnershipKey() throws Exception { MappedStatement mappedStatement = loadMappedStatement( - "com.ruoyi.info.collection.mapper.CcdiAssetInfoMapper.selectByFamilyId"); + "com.ruoyi.info.collection.mapper.CcdiAssetInfoMapper.selectByFamilyIdAndPersonId"); - String sql = renderSql(mappedStatement, Map.of("familyId", "320101199001010011")); + String sql = renderSql(mappedStatement, Map.of( + "familyId", "320101199001010011", + "personId", "320101199201010022" + )); assertTrue(sql.contains("FROM ccdi_asset_info"), sql); assertTrue(sql.contains("WHERE family_id = ?"), sql); + assertTrue(sql.contains("AND person_id = ?"), sql); } @Test - void selectByPersonId_shouldFilterByPersonId() throws Exception { + void deleteByFamilyIdAndPersonId_shouldFilterByOwnershipKey() throws Exception { MappedStatement mappedStatement = loadMappedStatement( - "com.ruoyi.info.collection.mapper.CcdiAssetInfoMapper.selectByPersonId"); + "com.ruoyi.info.collection.mapper.CcdiAssetInfoMapper.deleteByFamilyIdAndPersonId"); - String sql = renderSql(mappedStatement, Map.of("personId", "320101199201010022")); - - assertTrue(sql.contains("FROM ccdi_asset_info"), sql); - assertTrue(sql.contains("WHERE person_id = ?"), sql); - } - - @Test - void deleteByFamilyIds_shouldRenderInClause() throws Exception { - MappedStatement mappedStatement = loadMappedStatement( - "com.ruoyi.info.collection.mapper.CcdiAssetInfoMapper.deleteByFamilyIds"); - - String sql = renderSql(mappedStatement, Map.of("familyIds", List.of("A", "B"))); + String sql = renderSql(mappedStatement, Map.of( + "familyId", "320101199001010011", + "personId", "320101199201010022" + )); assertTrue(sql.contains("DELETE FROM ccdi_asset_info"), sql); - assertTrue(sql.contains("family_id IN"), sql); - assertFalse(sql.contains("IN ()"), sql); + assertTrue(sql.contains("WHERE family_id = ?"), sql); + assertTrue(sql.contains("AND person_id = ?"), sql); } @Test @@ -84,23 +80,18 @@ class CcdiAssetInfoMapperTest { } @Test - void ownerLookupQueries_shouldResolveFromEmployeeAndFamilyRelation() throws Exception { - MappedStatement employeeStatement = loadMappedStatement( - "com.ruoyi.info.collection.mapper.CcdiAssetInfoMapper.selectOwnerByEmployeeIdCards"); + void ownerLookupQuery_shouldResolveFromFamilyRelationOnly() throws Exception { MappedStatement familyStatement = loadMappedStatement( - "com.ruoyi.info.collection.mapper.CcdiAssetInfoMapper.selectOwnerByFamilyRelationIdCards"); + "com.ruoyi.info.collection.mapper.CcdiAssetInfoMapper.selectOwnerCandidatesByRelationCertNos"); - String employeeSql = renderSql(employeeStatement, Map.of("personIds", List.of("A"))); - String familySql = renderSql(familyStatement, Map.of("personIds", List.of("B"))); - - assertTrue(employeeSql.contains("FROM ccdi_base_staff"), employeeSql); - assertTrue(employeeSql.contains("id_card AS personId"), employeeSql); - assertTrue(employeeSql.contains("id_card AS familyId"), employeeSql); + String familySql = renderSql(familyStatement, Map.of("relationCertNos", List.of("B"))); assertTrue(familySql.contains("FROM ccdi_staff_fmy_relation"), familySql); assertTrue(familySql.contains("relation_cert_no AS personId"), familySql); assertTrue(familySql.contains("person_id AS familyId"), familySql); + assertTrue(familySql.contains("relation_cert_no IN"), familySql); assertTrue(familySql.contains("is_emp_family = 1"), familySql); + assertFalse(familySql.contains("FROM ccdi_base_staff"), familySql); } private MappedStatement loadMappedStatement(String statementId) throws Exception { diff --git a/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/mapper/CcdiStaffFmyRelationMapperTest.java b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/mapper/CcdiStaffFmyRelationMapperTest.java new file mode 100644 index 0000000..e3d7ea6 --- /dev/null +++ b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/mapper/CcdiStaffFmyRelationMapperTest.java @@ -0,0 +1,119 @@ +package com.ruoyi.info.collection.mapper; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.github.pagehelper.parser.defaults.DefaultCountSqlParser; +import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationQueryDTO; +import org.apache.ibatis.builder.xml.XMLMapperBuilder; +import org.apache.ibatis.mapping.BoundSql; +import org.apache.ibatis.mapping.Environment; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver; +import org.apache.ibatis.session.Configuration; +import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; +import org.apache.ibatis.type.TypeAliasRegistry; +import org.junit.jupiter.api.Test; + +import javax.sql.DataSource; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class CcdiStaffFmyRelationMapperTest { + + private static final String RESOURCE = "mapper/info/collection/CcdiStaffFmyRelationMapper.xml"; + + @Test + void selectRelationPage_shouldRenderWhitespaceBeforeDynamicAndClause() throws Exception { + MappedStatement mappedStatement = loadMappedStatement( + "com.ruoyi.info.collection.mapper.CcdiStaffFmyRelationMapper.selectRelationPage"); + + CcdiStaffFmyRelationQueryDTO queryDTO = new CcdiStaffFmyRelationQueryDTO(); + queryDTO.setPersonId("320101199001010011"); + String sql = renderSql(mappedStatement, Map.of( + "page", new Page<>(1, 10), + "query", queryDTO + )); + String countSql = normalizeSql(new DefaultCountSqlParser().getSmartCountSql(sql, "0")); + + assertTrue(sql.contains("WHERE 1 = 1 AND r.is_emp_family = 1 AND r.person_id = ?"), sql); + assertFalse(sql.contains("1AND"), sql); + assertFalse(countSql.contains("1AND"), countSql); + } + + private MappedStatement loadMappedStatement(String statementId) throws Exception { + Configuration configuration = new Configuration(); + configuration.setEnvironment(new Environment("test", new JdbcTransactionFactory(), new NoOpDataSource())); + registerTypeAliases(configuration.getTypeAliasRegistry()); + configuration.getLanguageRegistry().register(XMLLanguageDriver.class); + configuration.addMapper(CcdiStaffFmyRelationMapper.class); + + try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(RESOURCE)) { + XMLMapperBuilder xmlMapperBuilder = + new XMLMapperBuilder(inputStream, configuration, RESOURCE, configuration.getSqlFragments()); + xmlMapperBuilder.parse(); + } + return configuration.getMappedStatement(statementId); + } + + private String renderSql(MappedStatement mappedStatement, Map params) { + BoundSql boundSql = mappedStatement.getBoundSql(new HashMap<>(params)); + return normalizeSql(boundSql.getSql()); + } + + private String normalizeSql(String sql) { + return sql.replaceAll("\\s+", " ").trim(); + } + + private void registerTypeAliases(TypeAliasRegistry typeAliasRegistry) { + typeAliasRegistry.registerAlias("map", Map.class); + } + + private static class NoOpDataSource implements DataSource { + + @Override + public java.sql.Connection getConnection() { + throw new UnsupportedOperationException("Not required for SQL rendering tests"); + } + + @Override + public java.sql.Connection getConnection(String username, String password) { + throw new UnsupportedOperationException("Not required for SQL rendering tests"); + } + + @Override + public java.io.PrintWriter getLogWriter() { + return null; + } + + @Override + public void setLogWriter(java.io.PrintWriter out) { + } + + @Override + public void setLoginTimeout(int seconds) { + } + + @Override + public int getLoginTimeout() { + return 0; + } + + @Override + public java.util.logging.Logger getParentLogger() { + return java.util.logging.Logger.getGlobal(); + } + + @Override + public T unwrap(Class iface) { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public boolean isWrapperFor(Class iface) { + return false; + } + } +} diff --git a/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiAssetInfoDesignContractTest.java b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiAssetInfoDesignContractTest.java new file mode 100644 index 0000000..66997ac --- /dev/null +++ b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiAssetInfoDesignContractTest.java @@ -0,0 +1,33 @@ +package com.ruoyi.info.collection.service; + +import com.ruoyi.info.collection.domain.dto.CcdiAssetInfoDTO; +import com.ruoyi.info.collection.domain.excel.CcdiAssetInfoExcel; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class CcdiAssetInfoDesignContractTest { + + @Test + void aggregateSaveDto_shouldNotExposePersonId() { + Set fieldNames = Arrays.stream(CcdiAssetInfoDTO.class.getDeclaredFields()) + .map(field -> field.getName()) + .collect(Collectors.toSet()); + + assertFalse(fieldNames.contains("personId")); + } + + @Test + void importExcel_shouldStillExposePersonIdForRelationCertNoMatching() { + Set fieldNames = Arrays.stream(CcdiAssetInfoExcel.class.getDeclaredFields()) + .map(field -> field.getName()) + .collect(Collectors.toSet()); + + assertTrue(fieldNames.contains("personId")); + } +} diff --git a/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiAssetInfoImportServiceImplTest.java b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiAssetInfoImportServiceImplTest.java index 55cc199..7a65451 100644 --- a/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiAssetInfoImportServiceImplTest.java +++ b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiAssetInfoImportServiceImplTest.java @@ -82,19 +82,17 @@ class CcdiAssetInfoImportServiceImplTest { } @Test - void importAssetInfoAsync_shouldResolveFamilyIdFromEmployeeIdCard() { + void importAssetInfoAsync_shouldResolveFamilyIdFromFamilyRelationForRelativeAsset() { CcdiAssetInfoExcel excel = buildExcel("320101199001010011", "房产"); when(redisTemplate.opsForHash()).thenReturn(hashOperations); - when(assetInfoMapper.selectOwnerByEmployeeIdCards(List.of("320101199001010011"))) - .thenReturn(List.of(owner("320101199001010011", "320101199001010011"))); - when(assetInfoMapper.selectOwnerByFamilyRelationIdCards(List.of("320101199001010011"))) - .thenReturn(List.of()); + when(assetInfoMapper.selectOwnerCandidatesByRelationCertNos(List.of("320101199001010011"))) + .thenReturn(List.of(owner("320101199001010011", "320101199009090099"))); service.importAssetInfoAsync(List.of(excel), "task-1", "tester"); ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); verify(assetInfoMapper).insertBatch(captor.capture()); - assertEquals("320101199001010011", captor.getValue().get(0).getFamilyId()); + assertEquals("320101199009090099", captor.getValue().get(0).getFamilyId()); assertEquals("320101199001010011", captor.getValue().get(0).getPersonId()); } @@ -102,9 +100,7 @@ class CcdiAssetInfoImportServiceImplTest { void importAssetInfoAsync_shouldResolveFamilyIdFromFamilyRelationIdCard() { CcdiAssetInfoExcel excel = buildExcel("320101199201010022", "车辆"); when(redisTemplate.opsForHash()).thenReturn(hashOperations); - when(assetInfoMapper.selectOwnerByEmployeeIdCards(List.of("320101199201010022"))) - .thenReturn(List.of()); - when(assetInfoMapper.selectOwnerByFamilyRelationIdCards(List.of("320101199201010022"))) + when(assetInfoMapper.selectOwnerCandidatesByRelationCertNos(List.of("320101199201010022"))) .thenReturn(List.of(owner("320101199201010022", "320101199001010011"))); service.importAssetInfoAsync(List.of(excel), "task-2", "tester"); @@ -122,17 +118,15 @@ class CcdiAssetInfoImportServiceImplTest { when(redisTemplate.opsForHash()).thenReturn(hashOperations); when(redisTemplate.opsForValue()).thenReturn(valueOperations); - when(assetInfoMapper.selectOwnerByEmployeeIdCards(List.of("320101199001010011", "320101199001010099"))) - .thenReturn(List.of(owner("320101199001010011", "320101199001010011"))); - when(assetInfoMapper.selectOwnerByFamilyRelationIdCards(List.of("320101199001010011", "320101199001010099"))) - .thenReturn(List.of()); + when(assetInfoMapper.selectOwnerCandidatesByRelationCertNos(List.of("320101199001010011", "320101199001010099"))) + .thenReturn(List.of(owner("320101199001010011", "320101199009090099"))); service.importAssetInfoAsync(List.of(good, bad), "task-3", "tester"); ArgumentCaptor> insertCaptor = ArgumentCaptor.forClass(List.class); verify(assetInfoMapper).insertBatch(insertCaptor.capture()); assertEquals(1, insertCaptor.getValue().size()); - assertEquals("320101199001010011", insertCaptor.getValue().get(0).getFamilyId()); + assertEquals("320101199009090099", insertCaptor.getValue().get(0).getFamilyId()); ArgumentCaptor failureCaptor = ArgumentCaptor.forClass(Object.class); verify(valueOperations).set(eq("import:assetInfo:task-3:failures"), failureCaptor.capture(), eq(7L), eq(TimeUnit.DAYS)); @@ -140,7 +134,7 @@ class CcdiAssetInfoImportServiceImplTest { assertEquals(1, failures.size()); AssetImportFailureVO failure = (AssetImportFailureVO) failures.get(0); assertEquals("320101199001010099", failure.getPersonId()); - assertTrue(failure.getErrorMessage().contains("未找到资产归属员工")); + assertTrue(failure.getErrorMessage().contains("未找到亲属资产归属员工")); } @Test @@ -148,13 +142,11 @@ class CcdiAssetInfoImportServiceImplTest { CcdiAssetInfoExcel excel = buildExcel("320101199201010022", "车辆"); when(redisTemplate.opsForHash()).thenReturn(hashOperations); when(redisTemplate.opsForValue()).thenReturn(valueOperations); - when(assetInfoMapper.selectOwnerByEmployeeIdCards(List.of("320101199201010022"))) + when(assetInfoMapper.selectOwnerCandidatesByRelationCertNos(List.of("320101199201010022"))) .thenReturn(List.of( owner("320101199201010022", "320101199001010011"), owner("320101199201010022", "320101199001010033") )); - when(assetInfoMapper.selectOwnerByFamilyRelationIdCards(List.of("320101199201010022"))) - .thenReturn(List.of()); service.importAssetInfoAsync(List.of(excel), "task-4", "tester"); @@ -162,7 +154,7 @@ class CcdiAssetInfoImportServiceImplTest { ArgumentCaptor failureCaptor = ArgumentCaptor.forClass(Object.class); verify(valueOperations).set(eq("import:assetInfo:task-4:failures"), failureCaptor.capture(), eq(7L), eq(TimeUnit.DAYS)); AssetImportFailureVO failure = (AssetImportFailureVO) ((List) failureCaptor.getValue()).get(0); - assertTrue(failure.getErrorMessage().contains("资产归属员工不唯一")); + assertTrue(failure.getErrorMessage().contains("亲属资产归属员工不唯一")); } @Test @@ -183,7 +175,7 @@ class CcdiAssetInfoImportServiceImplTest { )); AssetImportFailureVO failureVO = new AssetImportFailureVO(); failureVO.setPersonId("320101199001010099"); - failureVO.setErrorMessage("未找到资产归属员工"); + failureVO.setErrorMessage("未找到亲属资产归属员工"); when(valueOperations.get("import:assetInfo:task-5:failures")).thenReturn(List.of(failureVO)); ImportStatusVO statusVO = service.getImportStatus("task-5"); diff --git a/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiAssetInfoServiceImplTest.java b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiAssetInfoServiceImplTest.java index bdd049d..d736d06 100644 --- a/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiAssetInfoServiceImplTest.java +++ b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiAssetInfoServiceImplTest.java @@ -6,18 +6,23 @@ import com.ruoyi.info.collection.mapper.CcdiAssetInfoMapper; import com.ruoyi.info.collection.service.impl.CcdiAssetInfoServiceImpl; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.invocation.Invocation; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.math.BigDecimal; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mockingDetails; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -42,43 +47,64 @@ class CcdiAssetInfoServiceImplTest { } @Test - void replaceByFamilyId_shouldDeleteThenInsertNormalizedRows() { - CcdiAssetInfoDTO selfOwnedAsset = buildDto("320101199001010011", "房产"); - CcdiAssetInfoDTO familyOwnedAsset = buildDto("320101199201010022", "车辆"); + void replaceByFamilyIdAndPersonId_shouldDeleteThenInsertNormalizedRows() throws Exception { + CcdiAssetInfoDTO familyOwnedAsset = buildDto("房产"); + CcdiAssetInfoDTO anotherAsset = buildDto("车辆"); - service.replaceByFamilyId("320101199001010011", List.of(selfOwnedAsset, familyOwnedAsset)); + Method method = CcdiAssetInfoServiceImpl.class.getMethod( + "replaceByFamilyIdAndPersonId", String.class, String.class, List.class); + method.invoke(service, "320101199001010011", "A123456789", List.of(familyOwnedAsset, anotherAsset)); - verify(assetInfoMapper).deleteByFamilyId("320101199001010011"); + Invocation deleteInvocation = mockingDetails(assetInfoMapper).getInvocations().stream() + .filter(invocation -> "deleteByFamilyIdAndPersonId".equals(invocation.getMethod().getName())) + .findFirst() + .orElseThrow(); + assertEquals("320101199001010011", deleteInvocation.getArguments()[0]); + assertEquals("A123456789", deleteInvocation.getArguments()[1]); ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); verify(assetInfoMapper).insertBatch(captor.capture()); List savedList = captor.getValue(); assertEquals(2, savedList.size()); assertEquals("320101199001010011", savedList.get(0).getFamilyId()); - assertEquals("320101199001010011", savedList.get(0).getPersonId()); + assertEquals("A123456789", savedList.get(0).getPersonId()); assertEquals("320101199001010011", savedList.get(1).getFamilyId()); - assertEquals("320101199201010022", savedList.get(1).getPersonId()); + assertEquals("A123456789", savedList.get(1).getPersonId()); assertEquals("房产", savedList.get(0).getAssetMainType()); assertEquals("车辆", savedList.get(1).getAssetMainType()); } @Test - void replaceByFamilyId_shouldIgnoreEmptyRows() { + void replaceByFamilyIdAndPersonId_shouldIgnoreEmptyRows() throws Exception { CcdiAssetInfoDTO emptyRow = new CcdiAssetInfoDTO(); - service.replaceByFamilyId("320101199001010011", List.of(emptyRow)); + Method method = CcdiAssetInfoServiceImpl.class.getMethod( + "replaceByFamilyIdAndPersonId", String.class, String.class, List.class); + method.invoke(service, "320101199001010011", "A123456789", List.of(emptyRow)); - verify(assetInfoMapper).deleteByFamilyId("320101199001010011"); + Invocation deleteInvocation = mockingDetails(assetInfoMapper).getInvocations().stream() + .filter(invocation -> "deleteByFamilyIdAndPersonId".equals(invocation.getMethod().getName())) + .findFirst() + .orElseThrow(); + assertEquals("320101199001010011", deleteInvocation.getArguments()[0]); + assertEquals("A123456789", deleteInvocation.getArguments()[1]); verify(assetInfoMapper, never()).insertBatch(anyList()); } @Test - void deleteByFamilyId_shouldDelegateToMapper() { - when(assetInfoMapper.deleteByFamilyId("320101199001010011")).thenReturn(1); + void replaceByFamilyIdAndPersonId_shouldValidateRequiredFields() throws Exception { + CcdiAssetInfoDTO invalid = new CcdiAssetInfoDTO(); + invalid.setAssetMainType("房产"); + invalid.setAssetSubType("商品房"); + invalid.setAssetName("测试房产"); + invalid.setAssetStatus("正常"); - int result = service.deleteByFamilyId("320101199001010011"); + Method method = CcdiAssetInfoServiceImpl.class.getMethod( + "replaceByFamilyIdAndPersonId", String.class, String.class, List.class); + InvocationTargetException exception = assertThrows(InvocationTargetException.class, + () -> method.invoke(service, "320101199001010011", "A123456789", List.of(invalid))); - assertEquals(1, result); + assertEquals("当前估值不能为空", exception.getCause().getMessage()); } @Test @@ -92,9 +118,8 @@ class CcdiAssetInfoServiceImplTest { verify(assetInfoMapper).deleteByFamilyIds(eq(familyIds)); } - private CcdiAssetInfoDTO buildDto(String personId, String assetMainType) { + private CcdiAssetInfoDTO buildDto(String assetMainType) { CcdiAssetInfoDTO dto = new CcdiAssetInfoDTO(); - dto.setPersonId(personId); dto.setAssetMainType(assetMainType); dto.setAssetSubType(assetMainType + "小类"); dto.setAssetName(assetMainType + "名称"); diff --git a/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiBaseStaffServiceImplTest.java b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiBaseStaffServiceImplTest.java index b199f23..f4a3566 100644 --- a/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiBaseStaffServiceImplTest.java +++ b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiBaseStaffServiceImplTest.java @@ -56,8 +56,8 @@ class CcdiBaseStaffServiceImplTest { addDTO.setPhone("13812345678"); addDTO.setStatus("0"); addDTO.setAssetInfoList(List.of( - buildAssetDto("320101199001010011", "房产"), - buildAssetDto("320101199201010022", "车辆") + buildAssetDto("房产"), + buildAssetDto("车辆") )); when(baseStaffMapper.selectById(1001L)).thenReturn(null); @@ -71,8 +71,8 @@ class CcdiBaseStaffServiceImplTest { verify(assetInfoService).replaceByFamilyId(eq("320101199001010011"), captor.capture()); List savedAssets = captor.getValue(); assertEquals(2, savedAssets.size()); - assertEquals("320101199001010011", savedAssets.get(0).getPersonId()); - assertEquals("320101199201010022", savedAssets.get(1).getPersonId()); + assertEquals("房产", savedAssets.get(0).getAssetMainType()); + assertEquals("车辆", savedAssets.get(1).getAssetMainType()); } @Test @@ -88,7 +88,7 @@ class CcdiBaseStaffServiceImplTest { editDTO.setIdCard("320101199001010011"); editDTO.setPhone("13812345678"); editDTO.setStatus("0"); - editDTO.setAssetInfoList(List.of(buildAssetDto("320101199201010022", "车辆"))); + editDTO.setAssetInfoList(List.of(buildAssetDto("车辆"))); when(baseStaffMapper.selectById(1001L)).thenReturn(existing); when(baseStaffMapper.selectCount(any())).thenReturn(0L); @@ -114,7 +114,7 @@ class CcdiBaseStaffServiceImplTest { editDTO.setIdCard("320101199001010011"); editDTO.setPhone("13812345678"); editDTO.setStatus("0"); - editDTO.setAssetInfoList(List.of(buildAssetDto("320101199201010022", "车辆"))); + editDTO.setAssetInfoList(List.of(buildAssetDto("车辆"))); when(baseStaffMapper.selectById(1001L)).thenReturn(existing); when(baseStaffMapper.selectCount(any())).thenReturn(0L); @@ -172,9 +172,8 @@ class CcdiBaseStaffServiceImplTest { verify(assetInfoService).deleteByFamilyIds(List.of("320101199001010011", "320101199001010022")); } - private CcdiAssetInfoDTO buildAssetDto(String personId, String assetMainType) { + private CcdiAssetInfoDTO buildAssetDto(String assetMainType) { CcdiAssetInfoDTO dto = new CcdiAssetInfoDTO(); - dto.setPersonId(personId); dto.setAssetMainType(assetMainType); dto.setAssetSubType(assetMainType + "小类"); dto.setAssetName(assetMainType + "名称"); diff --git a/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiStaffFmyRelationAssetAggregationTest.java b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiStaffFmyRelationAssetAggregationTest.java new file mode 100644 index 0000000..309e618 --- /dev/null +++ b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiStaffFmyRelationAssetAggregationTest.java @@ -0,0 +1,45 @@ +package com.ruoyi.info.collection.service; + +import com.ruoyi.info.collection.domain.dto.CcdiAssetInfoDTO; +import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationAddDTO; +import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationEditDTO; +import com.ruoyi.info.collection.domain.vo.CcdiAssetInfoVO; +import com.ruoyi.info.collection.domain.vo.CcdiStaffFmyRelationVO; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertSame; + +class CcdiStaffFmyRelationAssetAggregationTest { + + @Test + void addDto_shouldExposeAssetInfoList() { + CcdiStaffFmyRelationAddDTO addDTO = new CcdiStaffFmyRelationAddDTO(); + List assetInfoList = List.of(new CcdiAssetInfoDTO()); + + addDTO.setAssetInfoList(assetInfoList); + + assertSame(assetInfoList, addDTO.getAssetInfoList()); + } + + @Test + void editDto_shouldExposeAssetInfoList() { + CcdiStaffFmyRelationEditDTO editDTO = new CcdiStaffFmyRelationEditDTO(); + List assetInfoList = List.of(new CcdiAssetInfoDTO()); + + editDTO.setAssetInfoList(assetInfoList); + + assertSame(assetInfoList, editDTO.getAssetInfoList()); + } + + @Test + void relationVo_shouldExposeAssetInfoList() { + CcdiStaffFmyRelationVO relationVO = new CcdiStaffFmyRelationVO(); + List assetInfoList = List.of(new CcdiAssetInfoVO()); + + relationVO.setAssetInfoList(assetInfoList); + + assertSame(assetInfoList, relationVO.getAssetInfoList()); + } +} diff --git a/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiStaffFmyRelationServiceImplTest.java b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiStaffFmyRelationServiceImplTest.java new file mode 100644 index 0000000..f470fb3 --- /dev/null +++ b/ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiStaffFmyRelationServiceImplTest.java @@ -0,0 +1,200 @@ +package com.ruoyi.info.collection.service; + +import com.ruoyi.info.collection.domain.CcdiAssetInfo; +import com.ruoyi.info.collection.domain.CcdiStaffFmyRelation; +import com.ruoyi.info.collection.domain.dto.CcdiAssetInfoDTO; +import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationAddDTO; +import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationEditDTO; +import com.ruoyi.info.collection.domain.vo.CcdiStaffFmyRelationVO; +import com.ruoyi.info.collection.mapper.CcdiStaffFmyRelationMapper; +import com.ruoyi.info.collection.service.impl.CcdiStaffFmyRelationServiceImpl; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.redis.core.RedisTemplate; + +import java.math.BigDecimal; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class CcdiStaffFmyRelationServiceImplTest { + + @InjectMocks + private CcdiStaffFmyRelationServiceImpl service; + + @Mock + private CcdiStaffFmyRelationMapper relationMapper; + + @Mock + private ICcdiStaffFmyRelationImportService relationImportService; + + @Mock + private RedisTemplate redisTemplate; + + @Mock + private ICcdiAssetInfoService assetInfoService; + + @Test + void selectRelationById_shouldAggregateAssetInfoList() { + CcdiStaffFmyRelationVO relationVO = new CcdiStaffFmyRelationVO(); + relationVO.setId(10L); + relationVO.setPersonId("320101199001010011"); + relationVO.setRelationCertNo("A123456789"); + CcdiAssetInfo assetInfo = new CcdiAssetInfo(); + assetInfo.setFamilyId("320101199001010011"); + assetInfo.setPersonId("A123456789"); + assetInfo.setAssetMainType("房产"); + + when(relationMapper.selectRelationById(10L)).thenReturn(relationVO); + when(assetInfoService.selectByFamilyIdAndPersonId("320101199001010011", "A123456789")) + .thenReturn(List.of(assetInfo)); + + CcdiStaffFmyRelationVO result = service.selectRelationById(10L); + + assertNotNull(result.getAssetInfoList()); + assertEquals(1, result.getAssetInfoList().size()); + assertEquals("A123456789", result.getAssetInfoList().get(0).getPersonId()); + assertEquals("房产", result.getAssetInfoList().get(0).getAssetMainType()); + } + + @Test + void insertRelation_shouldSaveRelationThenReplaceRelativeAssets() { + CcdiStaffFmyRelationAddDTO addDTO = new CcdiStaffFmyRelationAddDTO(); + addDTO.setPersonId("320101199001010011"); + addDTO.setRelationType("配偶"); + addDTO.setRelationName("李四"); + addDTO.setRelationCertType("护照"); + addDTO.setRelationCertNo("A123456789"); + addDTO.setAssetInfoList(List.of(buildAssetDto("房产"))); + + when(relationMapper.insert(any(CcdiStaffFmyRelation.class))).thenReturn(1); + + int result = service.insertRelation(addDTO); + + assertEquals(1, result); + ArgumentCaptor relationCaptor = ArgumentCaptor.forClass(CcdiStaffFmyRelation.class); + verify(relationMapper).insert(relationCaptor.capture()); + assertEquals("MANUAL", relationCaptor.getValue().getDataSource()); + assertEquals(Boolean.TRUE, relationCaptor.getValue().getIsEmpFamily()); + assertEquals(Boolean.FALSE, relationCaptor.getValue().getIsCustFamily()); + verify(assetInfoService).replaceByFamilyIdAndPersonId("320101199001010011", "A123456789", addDTO.getAssetInfoList()); + } + + @Test + void updateRelation_shouldRejectRelationCertChange() { + CcdiStaffFmyRelation existing = new CcdiStaffFmyRelation(); + existing.setId(10L); + existing.setRelationCertType("护照"); + existing.setRelationCertNo("A123456789"); + + CcdiStaffFmyRelationEditDTO editDTO = new CcdiStaffFmyRelationEditDTO(); + editDTO.setId(10L); + editDTO.setPersonId("320101199001010011"); + editDTO.setRelationType("配偶"); + editDTO.setRelationName("李四"); + editDTO.setRelationCertType("身份证"); + editDTO.setRelationCertNo("A123456789"); + + when(relationMapper.selectById(10L)).thenReturn(existing); + + RuntimeException exception = assertThrows(RuntimeException.class, () -> service.updateRelation(editDTO)); + + assertEquals("关系人证件类型/证件号码不允许修改", exception.getMessage()); + verify(relationMapper, never()).updateById(any(CcdiStaffFmyRelation.class)); + verify(assetInfoService, never()).replaceByFamilyIdAndPersonId(any(), any(), any()); + } + + @Test + void updateRelation_shouldRejectRelationCertNoChange() { + CcdiStaffFmyRelation existing = new CcdiStaffFmyRelation(); + existing.setId(10L); + existing.setRelationCertType("护照"); + existing.setRelationCertNo("A123456789"); + + CcdiStaffFmyRelationEditDTO editDTO = new CcdiStaffFmyRelationEditDTO(); + editDTO.setId(10L); + editDTO.setPersonId("320101199001010011"); + editDTO.setRelationType("配偶"); + editDTO.setRelationName("李四"); + editDTO.setRelationCertType("护照"); + editDTO.setRelationCertNo("B987654321"); + + when(relationMapper.selectById(10L)).thenReturn(existing); + + RuntimeException exception = assertThrows(RuntimeException.class, () -> service.updateRelation(editDTO)); + + assertEquals("关系人证件类型/证件号码不允许修改", exception.getMessage()); + verify(relationMapper, never()).updateById(any(CcdiStaffFmyRelation.class)); + verify(assetInfoService, never()).replaceByFamilyIdAndPersonId(any(), any(), any()); + } + + @Test + void updateRelation_shouldReplaceAssetsByOwnershipKey() { + CcdiStaffFmyRelation existing = new CcdiStaffFmyRelation(); + existing.setId(10L); + existing.setRelationCertType("护照"); + existing.setRelationCertNo("A123456789"); + + CcdiStaffFmyRelationEditDTO editDTO = new CcdiStaffFmyRelationEditDTO(); + editDTO.setId(10L); + editDTO.setPersonId("320101199001010011"); + editDTO.setRelationType("配偶"); + editDTO.setRelationName("李四"); + editDTO.setRelationCertType("护照"); + editDTO.setRelationCertNo("A123456789"); + editDTO.setAssetInfoList(List.of(buildAssetDto("车辆"))); + + when(relationMapper.selectById(10L)).thenReturn(existing); + when(relationMapper.updateById(any(CcdiStaffFmyRelation.class))).thenReturn(1); + + int result = service.updateRelation(editDTO); + + assertEquals(1, result); + verify(assetInfoService).replaceByFamilyIdAndPersonId("320101199001010011", "A123456789", editDTO.getAssetInfoList()); + } + + @Test + void deleteRelationByIds_shouldDeleteRelativeAssetsBeforeDeletingRelations() { + CcdiStaffFmyRelation relation1 = new CcdiStaffFmyRelation(); + relation1.setId(10L); + relation1.setPersonId("320101199001010011"); + relation1.setRelationCertNo("A123456789"); + CcdiStaffFmyRelation relation2 = new CcdiStaffFmyRelation(); + relation2.setId(11L); + relation2.setPersonId("320101199001010022"); + relation2.setRelationCertNo("B987654321"); + + when(relationMapper.selectBatchIds(List.of(10L, 11L))).thenReturn(List.of(relation1, relation2)); + when(relationMapper.deleteBatchIds(List.of(10L, 11L))).thenReturn(2); + + int result = service.deleteRelationByIds(new Long[]{10L, 11L}); + + assertEquals(2, result); + var order = inOrder(assetInfoService, relationMapper); + order.verify(assetInfoService).deleteByFamilyIdAndPersonId("320101199001010011", "A123456789"); + order.verify(assetInfoService).deleteByFamilyIdAndPersonId("320101199001010022", "B987654321"); + order.verify(relationMapper).deleteBatchIds(List.of(10L, 11L)); + } + + private CcdiAssetInfoDTO buildAssetDto(String assetMainType) { + CcdiAssetInfoDTO dto = new CcdiAssetInfoDTO(); + dto.setAssetMainType(assetMainType); + dto.setAssetSubType(assetMainType + "小类"); + dto.setAssetName(assetMainType + "名称"); + dto.setCurrentValue(new BigDecimal("100.00")); + dto.setAssetStatus("正常"); + return dto; + } +} diff --git a/sql/2026-03-12_ccdi_asset_info.sql b/sql/ccdi_asset_info.sql similarity index 54% rename from sql/2026-03-12_ccdi_asset_info.sql rename to sql/ccdi_asset_info.sql index d4badda..46a3e32 100644 --- a/sql/2026-03-12_ccdi_asset_info.sql +++ b/sql/ccdi_asset_info.sql @@ -1,9 +1,10 @@ USE ccdi; +-- 新建表结构(适用于首次落库) CREATE TABLE IF NOT EXISTS `ccdi_asset_info` ( `asset_id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '资产ID', - `family_id` VARCHAR(18) NOT NULL COMMENT '归属员工身份证号', - `person_id` VARCHAR(18) NOT NULL COMMENT '资产实际持有人身份证号', + `family_id` VARCHAR(100) NOT NULL COMMENT '归属员工证件号', + `person_id` VARCHAR(100) NOT NULL COMMENT '资产实际持有人证件号', `asset_main_type` VARCHAR(20) NOT NULL COMMENT '资产大类', `asset_sub_type` VARCHAR(50) NOT NULL COMMENT '资产小类', `asset_name` VARCHAR(200) NOT NULL COMMENT '资产名称', @@ -21,5 +22,18 @@ CREATE TABLE IF NOT EXISTS `ccdi_asset_info` ( PRIMARY KEY (`asset_id`), KEY `idx_family_id` (`family_id`), KEY `idx_person_id` (`person_id`), - KEY `idx_asset_main_type` (`asset_main_type`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工资产信息表'; + KEY `idx_family_person` (`family_id`, `person_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='亲属资产信息表'; + +-- 存量环境迁移说明 +-- 如果当前库中的 ccdi_asset_info 仍是旧“员工资产信息表”结构,需要至少执行以下迁移: +-- 1. 将 family_id / person_id 扩容到 VARCHAR(100),以兼容非 18 位证件号 +-- 2. 补齐 idx_family_person 联合索引 +-- 3. 旧索引 idx_asset_main_type 可按需保留或删除,不影响本次功能 +-- +-- 示例迁移语句: +-- ALTER TABLE `ccdi_asset_info` +-- MODIFY `family_id` VARCHAR(100) NOT NULL COMMENT '归属员工证件号', +-- MODIFY `person_id` VARCHAR(100) NOT NULL COMMENT '资产实际持有人证件号'; +-- +-- CREATE INDEX `idx_family_person` ON `ccdi_asset_info` (`family_id`, `person_id`);