完成员工与亲属资产导入后端拆分
This commit is contained in:
@@ -28,12 +28,12 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 资产信息导入Controller
|
* 亲属资产信息导入Controller
|
||||||
*
|
*
|
||||||
* @author ruoyi
|
* @author ruoyi
|
||||||
* @date 2026-03-12
|
* @date 2026-03-12
|
||||||
*/
|
*/
|
||||||
@Tag(name = "资产信息导入管理")
|
@Tag(name = "亲属资产信息导入管理")
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/ccdi/assetInfo")
|
@RequestMapping("/ccdi/assetInfo")
|
||||||
public class CcdiAssetInfoController extends BaseController {
|
public class CcdiAssetInfoController extends BaseController {
|
||||||
@@ -44,18 +44,18 @@ public class CcdiAssetInfoController extends BaseController {
|
|||||||
/**
|
/**
|
||||||
* 下载导入模板
|
* 下载导入模板
|
||||||
*/
|
*/
|
||||||
@Operation(summary = "下载资产导入模板")
|
@Operation(summary = "下载亲属资产导入模板")
|
||||||
@PostMapping("/importTemplate")
|
@PostMapping("/importTemplate")
|
||||||
public void importTemplate(HttpServletResponse response) {
|
public void importTemplate(HttpServletResponse response) {
|
||||||
EasyExcelUtil.importTemplateWithDictDropdown(response, CcdiAssetInfoExcel.class, "资产信息");
|
EasyExcelUtil.importTemplateWithDictDropdown(response, CcdiAssetInfoExcel.class, "亲属资产信息");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 导入资产信息
|
* 导入亲属资产信息
|
||||||
*/
|
*/
|
||||||
@Operation(summary = "导入资产信息")
|
@Operation(summary = "导入亲属资产信息")
|
||||||
@PreAuthorize("@ss.hasAnyPermi('ccdi:employee:import,ccdi:staffFmyRelation:import')")
|
@PreAuthorize("@ss.hasPermi('ccdi:staffFmyRelation:import')")
|
||||||
@Log(title = "资产信息", businessType = BusinessType.IMPORT)
|
@Log(title = "亲属资产信息", businessType = BusinessType.IMPORT)
|
||||||
@PostMapping("/importData")
|
@PostMapping("/importData")
|
||||||
public AjaxResult importData(MultipartFile file) throws Exception {
|
public AjaxResult importData(MultipartFile file) throws Exception {
|
||||||
List<CcdiAssetInfoExcel> list = EasyExcelUtil.importExcel(file.getInputStream(), CcdiAssetInfoExcel.class);
|
List<CcdiAssetInfoExcel> list = EasyExcelUtil.importExcel(file.getInputStream(), CcdiAssetInfoExcel.class);
|
||||||
@@ -74,8 +74,8 @@ public class CcdiAssetInfoController extends BaseController {
|
|||||||
/**
|
/**
|
||||||
* 查询导入状态
|
* 查询导入状态
|
||||||
*/
|
*/
|
||||||
@Operation(summary = "查询资产导入状态")
|
@Operation(summary = "查询亲属资产导入状态")
|
||||||
@PreAuthorize("@ss.hasAnyPermi('ccdi:employee:import,ccdi:staffFmyRelation:import')")
|
@PreAuthorize("@ss.hasPermi('ccdi:staffFmyRelation:import')")
|
||||||
@GetMapping("/importStatus/{taskId}")
|
@GetMapping("/importStatus/{taskId}")
|
||||||
public AjaxResult getImportStatus(@PathVariable String taskId) {
|
public AjaxResult getImportStatus(@PathVariable String taskId) {
|
||||||
return success(assetInfoImportService.getImportStatus(taskId));
|
return success(assetInfoImportService.getImportStatus(taskId));
|
||||||
@@ -84,8 +84,8 @@ public class CcdiAssetInfoController extends BaseController {
|
|||||||
/**
|
/**
|
||||||
* 查询导入失败记录
|
* 查询导入失败记录
|
||||||
*/
|
*/
|
||||||
@Operation(summary = "查询资产导入失败记录")
|
@Operation(summary = "查询亲属资产导入失败记录")
|
||||||
@PreAuthorize("@ss.hasAnyPermi('ccdi:employee:import,ccdi:staffFmyRelation:import')")
|
@PreAuthorize("@ss.hasPermi('ccdi:staffFmyRelation:import')")
|
||||||
@GetMapping("/importFailures/{taskId}")
|
@GetMapping("/importFailures/{taskId}")
|
||||||
public TableDataInfo getImportFailures(
|
public TableDataInfo getImportFailures(
|
||||||
@PathVariable String taskId,
|
@PathVariable String taskId,
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
package com.ruoyi.info.collection.controller;
|
||||||
|
|
||||||
|
import com.ruoyi.common.annotation.Log;
|
||||||
|
import com.ruoyi.common.core.controller.BaseController;
|
||||||
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
|
import com.ruoyi.common.core.page.TableDataInfo;
|
||||||
|
import com.ruoyi.common.enums.BusinessType;
|
||||||
|
import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffAssetInfoExcel;
|
||||||
|
import com.ruoyi.info.collection.domain.vo.BaseStaffAssetImportFailureVO;
|
||||||
|
import com.ruoyi.info.collection.domain.vo.ImportResultVO;
|
||||||
|
import com.ruoyi.info.collection.service.ICcdiBaseStaffAssetImportService;
|
||||||
|
import com.ruoyi.info.collection.utils.EasyExcelUtil;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 员工资产信息导入Controller
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
* @date 2026-03-13
|
||||||
|
*/
|
||||||
|
@Tag(name = "员工资产信息导入管理")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/ccdi/baseStaff/asset")
|
||||||
|
public class CcdiBaseStaffAssetImportController extends BaseController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ICcdiBaseStaffAssetImportService baseStaffAssetImportService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载导入模板
|
||||||
|
*/
|
||||||
|
@Operation(summary = "下载员工资产导入模板")
|
||||||
|
@PostMapping("/importTemplate")
|
||||||
|
public void importTemplate(HttpServletResponse response) {
|
||||||
|
EasyExcelUtil.importTemplateWithDictDropdown(response, CcdiBaseStaffAssetInfoExcel.class, "员工资产信息");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入员工资产信息
|
||||||
|
*/
|
||||||
|
@Operation(summary = "导入员工资产信息")
|
||||||
|
@PreAuthorize("@ss.hasPermi('ccdi:employee:import')")
|
||||||
|
@Log(title = "员工资产信息", businessType = BusinessType.IMPORT)
|
||||||
|
@PostMapping("/importData")
|
||||||
|
public AjaxResult importData(MultipartFile file) throws Exception {
|
||||||
|
List<CcdiBaseStaffAssetInfoExcel> list = EasyExcelUtil.importExcel(file.getInputStream(), CcdiBaseStaffAssetInfoExcel.class);
|
||||||
|
if (list == null || list.isEmpty()) {
|
||||||
|
return warn("至少需要一条数据");
|
||||||
|
}
|
||||||
|
|
||||||
|
String taskId = baseStaffAssetImportService.importAssetInfo(list);
|
||||||
|
ImportResultVO result = new ImportResultVO();
|
||||||
|
result.setTaskId(taskId);
|
||||||
|
result.setStatus("PROCESSING");
|
||||||
|
result.setMessage("导入任务已提交,正在后台处理");
|
||||||
|
return AjaxResult.success("导入任务已提交,正在后台处理", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询导入状态
|
||||||
|
*/
|
||||||
|
@Operation(summary = "查询员工资产导入状态")
|
||||||
|
@PreAuthorize("@ss.hasPermi('ccdi:employee:import')")
|
||||||
|
@GetMapping("/importStatus/{taskId}")
|
||||||
|
public AjaxResult getImportStatus(@PathVariable String taskId) {
|
||||||
|
return success(baseStaffAssetImportService.getImportStatus(taskId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询导入失败记录
|
||||||
|
*/
|
||||||
|
@Operation(summary = "查询员工资产导入失败记录")
|
||||||
|
@PreAuthorize("@ss.hasPermi('ccdi:employee:import')")
|
||||||
|
@GetMapping("/importFailures/{taskId}")
|
||||||
|
public TableDataInfo getImportFailures(
|
||||||
|
@PathVariable String taskId,
|
||||||
|
@RequestParam(defaultValue = "1") Integer pageNum,
|
||||||
|
@RequestParam(defaultValue = "10") Integer pageSize) {
|
||||||
|
List<BaseStaffAssetImportFailureVO> failures = baseStaffAssetImportService.getImportFailures(taskId);
|
||||||
|
int fromIndex = (pageNum - 1) * pageSize;
|
||||||
|
int toIndex = Math.min(fromIndex + pageSize, failures.size());
|
||||||
|
if (fromIndex >= failures.size()) {
|
||||||
|
return getDataTable(new ArrayList<>(), failures.size());
|
||||||
|
}
|
||||||
|
return getDataTable(failures.subList(fromIndex, toIndex), failures.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,8 +24,8 @@ public class CcdiAssetInfoExcel implements Serializable {
|
|||||||
@Serial
|
@Serial
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
/** 资产实际持有人证件号 */
|
/** 亲属证件号 */
|
||||||
@ExcelProperty(value = "资产实际持有人证件号*", index = 0)
|
@ExcelProperty(value = "亲属证件号*", index = 0)
|
||||||
@ColumnWidth(22)
|
@ColumnWidth(22)
|
||||||
@Required
|
@Required
|
||||||
@TextFormat
|
@TextFormat
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package com.ruoyi.info.collection.domain.excel;
|
||||||
|
|
||||||
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
|
import com.alibaba.excel.annotation.write.style.ColumnWidth;
|
||||||
|
import com.ruoyi.common.annotation.DictDropdown;
|
||||||
|
import com.ruoyi.common.annotation.Required;
|
||||||
|
import com.ruoyi.common.annotation.TextFormat;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 员工资产信息Excel导入导出对象
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
* @date 2026-03-13
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CcdiBaseStaffAssetInfoExcel implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** 员工身份证号 */
|
||||||
|
@ExcelProperty(value = "员工身份证号*", index = 0)
|
||||||
|
@ColumnWidth(22)
|
||||||
|
@Required
|
||||||
|
@TextFormat
|
||||||
|
private String personId;
|
||||||
|
|
||||||
|
/** 资产大类 */
|
||||||
|
@ExcelProperty(value = "资产大类*", index = 1)
|
||||||
|
@ColumnWidth(16)
|
||||||
|
@Required
|
||||||
|
private String assetMainType;
|
||||||
|
|
||||||
|
/** 资产小类 */
|
||||||
|
@ExcelProperty(value = "资产小类*", index = 2)
|
||||||
|
@ColumnWidth(18)
|
||||||
|
@Required
|
||||||
|
private String assetSubType;
|
||||||
|
|
||||||
|
/** 资产名称 */
|
||||||
|
@ExcelProperty(value = "资产名称*", index = 3)
|
||||||
|
@ColumnWidth(24)
|
||||||
|
@Required
|
||||||
|
private String assetName;
|
||||||
|
|
||||||
|
/** 产权占比 */
|
||||||
|
@ExcelProperty(value = "产权占比", index = 4)
|
||||||
|
@ColumnWidth(12)
|
||||||
|
private BigDecimal ownershipRatio;
|
||||||
|
|
||||||
|
/** 购买/评估日期 */
|
||||||
|
@ExcelProperty(value = "购买/评估日期", index = 5)
|
||||||
|
@ColumnWidth(16)
|
||||||
|
private Date purchaseEvalDate;
|
||||||
|
|
||||||
|
/** 资产原值 */
|
||||||
|
@ExcelProperty(value = "资产原值", index = 6)
|
||||||
|
@ColumnWidth(16)
|
||||||
|
private BigDecimal originalValue;
|
||||||
|
|
||||||
|
/** 当前估值 */
|
||||||
|
@ExcelProperty(value = "当前估值*", index = 7)
|
||||||
|
@ColumnWidth(16)
|
||||||
|
@Required
|
||||||
|
private BigDecimal currentValue;
|
||||||
|
|
||||||
|
/** 估值截止日期 */
|
||||||
|
@ExcelProperty(value = "估值截止日期", index = 8)
|
||||||
|
@ColumnWidth(16)
|
||||||
|
private Date valuationDate;
|
||||||
|
|
||||||
|
/** 资产状态 */
|
||||||
|
@ExcelProperty(value = "资产状态*", index = 9)
|
||||||
|
@ColumnWidth(14)
|
||||||
|
@DictDropdown(dictType = "ccdi_asset_status")
|
||||||
|
@Required
|
||||||
|
private String assetStatus;
|
||||||
|
|
||||||
|
/** 备注 */
|
||||||
|
@ExcelProperty(value = "备注", index = 10)
|
||||||
|
@ColumnWidth(28)
|
||||||
|
private String remarks;
|
||||||
|
}
|
||||||
@@ -15,8 +15,8 @@ import java.math.BigDecimal;
|
|||||||
@Schema(description = "亲属资产信息导入失败记录")
|
@Schema(description = "亲属资产信息导入失败记录")
|
||||||
public class AssetImportFailureVO {
|
public class AssetImportFailureVO {
|
||||||
|
|
||||||
/** 关系人证件号 */
|
/** 亲属证件号 */
|
||||||
@Schema(description = "关系人证件号")
|
@Schema(description = "亲属证件号")
|
||||||
private String personId;
|
private String personId;
|
||||||
|
|
||||||
/** 资产大类 */
|
/** 资产大类 */
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package com.ruoyi.info.collection.domain.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 员工资产信息导入失败记录VO
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
* @date 2026-03-13
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "员工资产信息导入失败记录")
|
||||||
|
public class BaseStaffAssetImportFailureVO {
|
||||||
|
|
||||||
|
/** 员工身份证号 */
|
||||||
|
@Schema(description = "员工身份证号")
|
||||||
|
private String personId;
|
||||||
|
|
||||||
|
/** 资产大类 */
|
||||||
|
@Schema(description = "资产大类")
|
||||||
|
private String assetMainType;
|
||||||
|
|
||||||
|
/** 资产小类 */
|
||||||
|
@Schema(description = "资产小类")
|
||||||
|
private String assetSubType;
|
||||||
|
|
||||||
|
/** 资产名称 */
|
||||||
|
@Schema(description = "资产名称")
|
||||||
|
private String assetName;
|
||||||
|
|
||||||
|
/** 产权占比 */
|
||||||
|
@Schema(description = "产权占比")
|
||||||
|
private BigDecimal ownershipRatio;
|
||||||
|
|
||||||
|
/** 当前估值 */
|
||||||
|
@Schema(description = "当前估值")
|
||||||
|
private BigDecimal currentValue;
|
||||||
|
|
||||||
|
/** 资产状态 */
|
||||||
|
@Schema(description = "资产状态")
|
||||||
|
private String assetStatus;
|
||||||
|
|
||||||
|
/** 错误信息 */
|
||||||
|
@Schema(description = "错误信息")
|
||||||
|
private String errorMessage;
|
||||||
|
}
|
||||||
@@ -75,14 +75,6 @@ public interface CcdiAssetInfoMapper extends BaseMapper<CcdiAssetInfo> {
|
|||||||
*/
|
*/
|
||||||
int insertBatch(@Param("list") List<CcdiAssetInfo> list);
|
int insertBatch(@Param("list") List<CcdiAssetInfo> list);
|
||||||
|
|
||||||
/**
|
|
||||||
* 按资产实际持有人证件号查询员工本人归属候选
|
|
||||||
*
|
|
||||||
* @param personIds 资产实际持有人证件号列表
|
|
||||||
* @return 归属映射
|
|
||||||
*/
|
|
||||||
List<Map<String, String>> selectOwnerCandidatesByPersonIds(@Param("personIds") List<String> personIds);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按关系人证件号查询归属员工候选
|
* 按关系人证件号查询归属员工候选
|
||||||
*
|
*
|
||||||
@@ -90,4 +82,12 @@ public interface CcdiAssetInfoMapper extends BaseMapper<CcdiAssetInfo> {
|
|||||||
* @return 归属映射
|
* @return 归属映射
|
||||||
*/
|
*/
|
||||||
List<Map<String, String>> selectOwnerCandidatesByRelationCertNos(@Param("relationCertNos") List<String> relationCertNos);
|
List<Map<String, String>> selectOwnerCandidatesByRelationCertNos(@Param("relationCertNos") List<String> relationCertNos);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按员工身份证号查询员工本人归属候选
|
||||||
|
*
|
||||||
|
* @param idCards 员工身份证号列表
|
||||||
|
* @return 归属映射
|
||||||
|
*/
|
||||||
|
List<Map<String, String>> selectOwnerCandidatesByBaseStaffIdCards(@Param("idCards") List<String> idCards);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import com.ruoyi.info.collection.domain.vo.ImportStatusVO;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 员工资产信息异步导入 服务层
|
* 亲属资产信息异步导入 服务层
|
||||||
*
|
*
|
||||||
* @author ruoyi
|
* @author ruoyi
|
||||||
* @date 2026-03-12
|
* @date 2026-03-12
|
||||||
@@ -23,7 +23,7 @@ public interface ICcdiAssetInfoImportService {
|
|||||||
String importAssetInfo(List<CcdiAssetInfoExcel> excelList);
|
String importAssetInfo(List<CcdiAssetInfoExcel> excelList);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 异步导入员工资产数据
|
* 异步导入亲属资产数据
|
||||||
*
|
*
|
||||||
* @param excelList Excel实体列表
|
* @param excelList Excel实体列表
|
||||||
* @param taskId 任务ID
|
* @param taskId 任务ID
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package com.ruoyi.info.collection.service;
|
||||||
|
|
||||||
|
import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffAssetInfoExcel;
|
||||||
|
import com.ruoyi.info.collection.domain.vo.BaseStaffAssetImportFailureVO;
|
||||||
|
import com.ruoyi.info.collection.domain.vo.ImportStatusVO;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 员工资产信息异步导入 服务层
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
* @date 2026-03-13
|
||||||
|
*/
|
||||||
|
public interface ICcdiBaseStaffAssetImportService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动异步导入任务
|
||||||
|
*
|
||||||
|
* @param excelList Excel实体列表
|
||||||
|
* @return 任务ID
|
||||||
|
*/
|
||||||
|
String importAssetInfo(List<CcdiBaseStaffAssetInfoExcel> excelList);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步导入员工资产数据
|
||||||
|
*
|
||||||
|
* @param excelList Excel实体列表
|
||||||
|
* @param taskId 任务ID
|
||||||
|
* @param userName 用户名
|
||||||
|
*/
|
||||||
|
void importAssetInfoAsync(List<CcdiBaseStaffAssetInfoExcel> excelList, String taskId, String userName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询导入状态
|
||||||
|
*
|
||||||
|
* @param taskId 任务ID
|
||||||
|
* @return 导入状态
|
||||||
|
*/
|
||||||
|
ImportStatusVO getImportStatus(String taskId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询导入失败记录
|
||||||
|
*
|
||||||
|
* @param taskId 任务ID
|
||||||
|
* @return 失败记录列表
|
||||||
|
*/
|
||||||
|
List<BaseStaffAssetImportFailureVO> getImportFailures(String taskId);
|
||||||
|
}
|
||||||
@@ -27,7 +27,6 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 亲属资产信息异步导入服务层处理
|
* 亲属资产信息异步导入服务层处理
|
||||||
@@ -97,10 +96,10 @@ public class CcdiAssetInfoImportServiceImpl implements ICcdiAssetInfoImportServi
|
|||||||
validateExcel(excel);
|
validateExcel(excel);
|
||||||
Set<String> familyIds = ownerMap.get(excel.getPersonId());
|
Set<String> familyIds = ownerMap.get(excel.getPersonId());
|
||||||
if (familyIds == null || familyIds.isEmpty()) {
|
if (familyIds == null || familyIds.isEmpty()) {
|
||||||
throw new RuntimeException("未找到资产归属员工");
|
throw new RuntimeException("未找到亲属资产归属员工");
|
||||||
}
|
}
|
||||||
if (familyIds.size() > 1) {
|
if (familyIds.size() > 1) {
|
||||||
throw new RuntimeException("资产归属员工不唯一");
|
throw new RuntimeException("亲属资产归属员工不唯一");
|
||||||
}
|
}
|
||||||
|
|
||||||
CcdiAssetInfo assetInfo = new CcdiAssetInfo();
|
CcdiAssetInfo assetInfo = new CcdiAssetInfo();
|
||||||
@@ -167,16 +166,7 @@ public class CcdiAssetInfoImportServiceImpl implements ICcdiAssetInfoImportServi
|
|||||||
if (personIds == null || personIds.isEmpty()) {
|
if (personIds == null || personIds.isEmpty()) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
List<Map<String, String>> selfMappings = assetInfoMapper.selectOwnerCandidatesByPersonIds(personIds);
|
mergeOwnerMappings(result, assetInfoMapper.selectOwnerCandidatesByRelationCertNos(personIds));
|
||||||
mergeOwnerMappings(result, selfMappings);
|
|
||||||
|
|
||||||
Set<String> selfMatchedIds = result.keySet();
|
|
||||||
List<String> relationPersonIds = personIds.stream()
|
|
||||||
.filter(personId -> !selfMatchedIds.contains(personId))
|
|
||||||
.toList();
|
|
||||||
if (!relationPersonIds.isEmpty()) {
|
|
||||||
mergeOwnerMappings(result, assetInfoMapper.selectOwnerCandidatesByRelationCertNos(relationPersonIds));
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,7 +186,7 @@ public class CcdiAssetInfoImportServiceImpl implements ICcdiAssetInfoImportServi
|
|||||||
|
|
||||||
private void validateExcel(CcdiAssetInfoExcel excel) {
|
private void validateExcel(CcdiAssetInfoExcel excel) {
|
||||||
if (StringUtils.isEmpty(excel.getPersonId())) {
|
if (StringUtils.isEmpty(excel.getPersonId())) {
|
||||||
throw new RuntimeException("资产实际持有人证件号不能为空");
|
throw new RuntimeException("亲属证件号不能为空");
|
||||||
}
|
}
|
||||||
if (StringUtils.isEmpty(excel.getAssetMainType())) {
|
if (StringUtils.isEmpty(excel.getAssetMainType())) {
|
||||||
throw new RuntimeException("资产大类不能为空");
|
throw new RuntimeException("资产大类不能为空");
|
||||||
|
|||||||
@@ -0,0 +1,226 @@
|
|||||||
|
package com.ruoyi.info.collection.service.impl;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.ruoyi.common.utils.SecurityUtils;
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import com.ruoyi.info.collection.domain.CcdiAssetInfo;
|
||||||
|
import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffAssetInfoExcel;
|
||||||
|
import com.ruoyi.info.collection.domain.vo.BaseStaffAssetImportFailureVO;
|
||||||
|
import com.ruoyi.info.collection.domain.vo.ImportResult;
|
||||||
|
import com.ruoyi.info.collection.domain.vo.ImportStatusVO;
|
||||||
|
import com.ruoyi.info.collection.mapper.CcdiAssetInfoMapper;
|
||||||
|
import com.ruoyi.info.collection.service.ICcdiBaseStaffAssetImportService;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
|
import org.springframework.scheduling.annotation.EnableAsync;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 员工资产信息异步导入服务层处理
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
* @date 2026-03-13
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@EnableAsync
|
||||||
|
public class CcdiBaseStaffAssetImportServiceImpl implements ICcdiBaseStaffAssetImportService {
|
||||||
|
|
||||||
|
private static final String STATUS_KEY_PREFIX = "import:baseStaffAsset:";
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private CcdiAssetInfoMapper assetInfoMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private RedisTemplate<String, Object> redisTemplate;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Resource
|
||||||
|
private ICcdiBaseStaffAssetImportService baseStaffAssetImportService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public String importAssetInfo(List<CcdiBaseStaffAssetInfoExcel> excelList) {
|
||||||
|
if (excelList == null || excelList.isEmpty()) {
|
||||||
|
throw new RuntimeException("至少需要一条数据");
|
||||||
|
}
|
||||||
|
|
||||||
|
String taskId = UUID.randomUUID().toString();
|
||||||
|
Map<String, Object> statusData = new HashMap<>();
|
||||||
|
statusData.put("taskId", taskId);
|
||||||
|
statusData.put("status", "PROCESSING");
|
||||||
|
statusData.put("totalCount", excelList.size());
|
||||||
|
statusData.put("successCount", 0);
|
||||||
|
statusData.put("failureCount", 0);
|
||||||
|
statusData.put("progress", 0);
|
||||||
|
statusData.put("startTime", System.currentTimeMillis());
|
||||||
|
statusData.put("message", "正在处理...");
|
||||||
|
|
||||||
|
String statusKey = STATUS_KEY_PREFIX + taskId;
|
||||||
|
redisTemplate.opsForHash().putAll(statusKey, statusData);
|
||||||
|
redisTemplate.expire(statusKey, 7, TimeUnit.DAYS);
|
||||||
|
|
||||||
|
baseStaffAssetImportService.importAssetInfoAsync(excelList, taskId, currentUserName());
|
||||||
|
return taskId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Async
|
||||||
|
@Transactional
|
||||||
|
public void importAssetInfoAsync(List<CcdiBaseStaffAssetInfoExcel> excelList, String taskId, String userName) {
|
||||||
|
List<CcdiAssetInfo> successList = new ArrayList<>();
|
||||||
|
List<BaseStaffAssetImportFailureVO> failures = new ArrayList<>();
|
||||||
|
|
||||||
|
List<String> personIds = excelList.stream()
|
||||||
|
.map(CcdiBaseStaffAssetInfoExcel::getPersonId)
|
||||||
|
.filter(StringUtils::isNotEmpty)
|
||||||
|
.distinct()
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
Map<String, Set<String>> ownerMap = buildOwnerMap(personIds);
|
||||||
|
|
||||||
|
for (CcdiBaseStaffAssetInfoExcel excel : excelList) {
|
||||||
|
try {
|
||||||
|
validateExcel(excel);
|
||||||
|
Set<String> familyIds = ownerMap.get(excel.getPersonId());
|
||||||
|
if (familyIds == null || familyIds.isEmpty()) {
|
||||||
|
throw new RuntimeException("员工资产导入仅支持员工本人证件号");
|
||||||
|
}
|
||||||
|
|
||||||
|
CcdiAssetInfo assetInfo = new CcdiAssetInfo();
|
||||||
|
BeanUtils.copyProperties(excel, assetInfo);
|
||||||
|
assetInfo.setFamilyId(excel.getPersonId());
|
||||||
|
assetInfo.setPersonId(excel.getPersonId());
|
||||||
|
assetInfo.setCreateBy(userName);
|
||||||
|
assetInfo.setUpdateBy(userName);
|
||||||
|
successList.add(assetInfo);
|
||||||
|
} catch (Exception e) {
|
||||||
|
BaseStaffAssetImportFailureVO failureVO = new BaseStaffAssetImportFailureVO();
|
||||||
|
BeanUtils.copyProperties(excel, failureVO);
|
||||||
|
failureVO.setErrorMessage(e.getMessage());
|
||||||
|
failures.add(failureVO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!successList.isEmpty()) {
|
||||||
|
assetInfoMapper.insertBatch(successList);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!failures.isEmpty()) {
|
||||||
|
redisTemplate.opsForValue().set(STATUS_KEY_PREFIX + taskId + ":failures", failures, 7, TimeUnit.DAYS);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImportResult result = new ImportResult();
|
||||||
|
result.setTotalCount(excelList.size());
|
||||||
|
result.setSuccessCount(successList.size());
|
||||||
|
result.setFailureCount(failures.size());
|
||||||
|
updateImportStatus(taskId, failures.isEmpty() ? "SUCCESS" : "PARTIAL_SUCCESS", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImportStatusVO getImportStatus(String taskId) {
|
||||||
|
String key = STATUS_KEY_PREFIX + taskId;
|
||||||
|
if (Boolean.FALSE.equals(redisTemplate.hasKey(key))) {
|
||||||
|
throw new RuntimeException("任务不存在或已过期");
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Object, Object> statusMap = redisTemplate.opsForHash().entries(key);
|
||||||
|
ImportStatusVO statusVO = new ImportStatusVO();
|
||||||
|
statusVO.setTaskId((String) statusMap.get("taskId"));
|
||||||
|
statusVO.setStatus((String) statusMap.get("status"));
|
||||||
|
statusVO.setTotalCount((Integer) statusMap.get("totalCount"));
|
||||||
|
statusVO.setSuccessCount((Integer) statusMap.get("successCount"));
|
||||||
|
statusVO.setFailureCount((Integer) statusMap.get("failureCount"));
|
||||||
|
statusVO.setProgress((Integer) statusMap.get("progress"));
|
||||||
|
statusVO.setStartTime((Long) statusMap.get("startTime"));
|
||||||
|
statusVO.setEndTime((Long) statusMap.get("endTime"));
|
||||||
|
statusVO.setMessage((String) statusMap.get("message"));
|
||||||
|
return statusVO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<BaseStaffAssetImportFailureVO> getImportFailures(String taskId) {
|
||||||
|
Object failuresObj = redisTemplate.opsForValue().get(STATUS_KEY_PREFIX + taskId + ":failures");
|
||||||
|
if (failuresObj == null) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
return JSON.parseArray(JSON.toJSONString(failuresObj), BaseStaffAssetImportFailureVO.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Set<String>> buildOwnerMap(List<String> personIds) {
|
||||||
|
Map<String, Set<String>> result = new LinkedHashMap<>();
|
||||||
|
if (personIds == null || personIds.isEmpty()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
mergeOwnerMappings(result, assetInfoMapper.selectOwnerCandidatesByBaseStaffIdCards(personIds));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mergeOwnerMappings(Map<String, Set<String>> result, List<Map<String, String>> mappings) {
|
||||||
|
if (mappings == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (Map<String, String> mapping : mappings) {
|
||||||
|
String personId = mapping.get("personId");
|
||||||
|
String familyId = mapping.get("familyId");
|
||||||
|
if (StringUtils.isEmpty(personId) || StringUtils.isEmpty(familyId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
result.computeIfAbsent(personId, key -> new java.util.LinkedHashSet<>()).add(familyId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateExcel(CcdiBaseStaffAssetInfoExcel excel) {
|
||||||
|
if (StringUtils.isEmpty(excel.getPersonId())) {
|
||||||
|
throw new RuntimeException("员工身份证号不能为空");
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(excel.getAssetMainType())) {
|
||||||
|
throw new RuntimeException("资产大类不能为空");
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(excel.getAssetSubType())) {
|
||||||
|
throw new RuntimeException("资产小类不能为空");
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(excel.getAssetName())) {
|
||||||
|
throw new RuntimeException("资产名称不能为空");
|
||||||
|
}
|
||||||
|
if (excel.getCurrentValue() == null) {
|
||||||
|
throw new RuntimeException("当前估值不能为空");
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(excel.getAssetStatus())) {
|
||||||
|
throw new RuntimeException("资产状态不能为空");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateImportStatus(String taskId, String status, ImportResult result) {
|
||||||
|
Map<String, Object> statusData = new HashMap<>();
|
||||||
|
statusData.put("status", status);
|
||||||
|
statusData.put("successCount", result.getSuccessCount());
|
||||||
|
statusData.put("failureCount", result.getFailureCount());
|
||||||
|
statusData.put("progress", 100);
|
||||||
|
statusData.put("endTime", System.currentTimeMillis());
|
||||||
|
statusData.put("message", "SUCCESS".equals(status)
|
||||||
|
? "全部成功!共导入" + result.getTotalCount() + "条数据"
|
||||||
|
: "成功" + result.getSuccessCount() + "条,失败" + result.getFailureCount() + "条");
|
||||||
|
redisTemplate.opsForHash().putAll(STATUS_KEY_PREFIX + taskId, statusData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String currentUserName() {
|
||||||
|
try {
|
||||||
|
return SecurityUtils.getUsername();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return "system";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -77,17 +77,6 @@
|
|||||||
</foreach>
|
</foreach>
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
<select id="selectOwnerCandidatesByPersonIds" resultType="map">
|
|
||||||
SELECT
|
|
||||||
id_card AS personId,
|
|
||||||
id_card AS familyId
|
|
||||||
FROM ccdi_base_staff
|
|
||||||
WHERE id_card IN
|
|
||||||
<foreach collection="personIds" item="personId" open="(" separator="," close=")">
|
|
||||||
#{personId}
|
|
||||||
</foreach>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="selectOwnerCandidatesByRelationCertNos" resultType="map">
|
<select id="selectOwnerCandidatesByRelationCertNos" resultType="map">
|
||||||
SELECT
|
SELECT
|
||||||
relation_cert_no AS personId,
|
relation_cert_no AS personId,
|
||||||
@@ -100,4 +89,15 @@
|
|||||||
</foreach>
|
</foreach>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="selectOwnerCandidatesByBaseStaffIdCards" resultType="map">
|
||||||
|
SELECT
|
||||||
|
id_card AS personId,
|
||||||
|
id_card AS familyId
|
||||||
|
FROM ccdi_base_staff
|
||||||
|
WHERE id_card IN
|
||||||
|
<foreach collection="idCards" item="idCard" open="(" separator="," close=")">
|
||||||
|
#{idCard}
|
||||||
|
</foreach>
|
||||||
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -109,11 +109,11 @@ class CcdiAssetInfoControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void importTemplate_shouldUseGenericAssetTemplateName() {
|
void importTemplate_shouldUseFamilyAssetTemplateName() {
|
||||||
try (MockedStatic<EasyExcelUtil> mocked = mockStatic(EasyExcelUtil.class)) {
|
try (MockedStatic<EasyExcelUtil> mocked = mockStatic(EasyExcelUtil.class)) {
|
||||||
controller.importTemplate(null);
|
controller.importTemplate(null);
|
||||||
|
|
||||||
mocked.verify(() -> EasyExcelUtil.importTemplateWithDictDropdown(null, CcdiAssetInfoExcel.class, "资产信息"));
|
mocked.verify(() -> EasyExcelUtil.importTemplateWithDictDropdown(null, CcdiAssetInfoExcel.class, "亲属资产信息"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,119 @@
|
|||||||
|
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.CcdiBaseStaffAssetInfoExcel;
|
||||||
|
import com.ruoyi.info.collection.domain.vo.BaseStaffAssetImportFailureVO;
|
||||||
|
import com.ruoyi.info.collection.domain.vo.ImportResultVO;
|
||||||
|
import com.ruoyi.info.collection.domain.vo.ImportStatusVO;
|
||||||
|
import com.ruoyi.info.collection.service.ICcdiBaseStaffAssetImportService;
|
||||||
|
import com.ruoyi.info.collection.utils.EasyExcelUtil;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockedStatic;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.springframework.mock.web.MockMultipartFile;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.mockStatic;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class CcdiBaseStaffAssetImportControllerTest {
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private CcdiBaseStaffAssetImportController controller;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ICcdiBaseStaffAssetImportService baseStaffAssetImportService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void importData_shouldReturnWarnWhenExcelHasNoRows() throws Exception {
|
||||||
|
MockMultipartFile file = new MockMultipartFile(
|
||||||
|
"file",
|
||||||
|
"base-staff-asset-empty.xlsx",
|
||||||
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||||
|
"empty".getBytes(StandardCharsets.UTF_8)
|
||||||
|
);
|
||||||
|
|
||||||
|
try (MockedStatic<EasyExcelUtil> mocked = mockStatic(EasyExcelUtil.class)) {
|
||||||
|
mocked.when(() -> EasyExcelUtil.importExcel(any(InputStream.class), eq(CcdiBaseStaffAssetInfoExcel.class)))
|
||||||
|
.thenReturn(List.of());
|
||||||
|
|
||||||
|
AjaxResult result = controller.importData(file);
|
||||||
|
|
||||||
|
assertEquals(HttpStatus.WARN, result.get(AjaxResult.CODE_TAG));
|
||||||
|
assertEquals("至少需要一条数据", result.get(AjaxResult.MSG_TAG));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void importData_shouldReturnSuccessWhenTaskCreated() throws Exception {
|
||||||
|
MockMultipartFile file = new MockMultipartFile(
|
||||||
|
"file",
|
||||||
|
"base-staff-asset.xlsx",
|
||||||
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||||
|
"asset".getBytes(StandardCharsets.UTF_8)
|
||||||
|
);
|
||||||
|
CcdiBaseStaffAssetInfoExcel excel = new CcdiBaseStaffAssetInfoExcel();
|
||||||
|
excel.setPersonId("320101199001010011");
|
||||||
|
when(baseStaffAssetImportService.importAssetInfo(List.of(excel))).thenReturn("task-1");
|
||||||
|
|
||||||
|
try (MockedStatic<EasyExcelUtil> mocked = mockStatic(EasyExcelUtil.class)) {
|
||||||
|
mocked.when(() -> EasyExcelUtil.importExcel(any(InputStream.class), eq(CcdiBaseStaffAssetInfoExcel.class)))
|
||||||
|
.thenReturn(List.of(excel));
|
||||||
|
|
||||||
|
AjaxResult result = controller.importData(file);
|
||||||
|
|
||||||
|
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(baseStaffAssetImportService.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() {
|
||||||
|
BaseStaffAssetImportFailureVO failure1 = new BaseStaffAssetImportFailureVO();
|
||||||
|
failure1.setPersonId("A1");
|
||||||
|
BaseStaffAssetImportFailureVO failure2 = new BaseStaffAssetImportFailureVO();
|
||||||
|
failure2.setPersonId("A2");
|
||||||
|
when(baseStaffAssetImportService.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", ((BaseStaffAssetImportFailureVO) result.getRows().get(0)).getPersonId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void importTemplate_shouldUseBaseStaffAssetTemplateName() {
|
||||||
|
try (MockedStatic<EasyExcelUtil> mocked = mockStatic(EasyExcelUtil.class)) {
|
||||||
|
controller.importTemplate(null);
|
||||||
|
|
||||||
|
mocked.verify(() -> EasyExcelUtil.importTemplateWithDictDropdown(null, CcdiBaseStaffAssetInfoExcel.class, "员工资产信息"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -97,19 +97,21 @@ class CcdiAssetInfoImportServiceImplTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void importAssetInfoAsync_shouldResolveFamilyIdFromEmployeeIdCardBeforeFamilyRelation() {
|
void importAssetInfoAsync_shouldFailWhenEmployeeIdCardIsUsedForFamilyAssetImport() {
|
||||||
CcdiAssetInfoExcel excel = buildExcel("320101199001010011", "房产");
|
CcdiAssetInfoExcel excel = buildExcel("320101199001010011", "房产");
|
||||||
when(redisTemplate.opsForHash()).thenReturn(hashOperations);
|
when(redisTemplate.opsForHash()).thenReturn(hashOperations);
|
||||||
when(assetInfoMapper.selectOwnerCandidatesByPersonIds(List.of("320101199001010011")))
|
when(redisTemplate.opsForValue()).thenReturn(valueOperations);
|
||||||
.thenReturn(List.of(owner("320101199001010011", "320101199001010011")));
|
when(assetInfoMapper.selectOwnerCandidatesByRelationCertNos(List.of("320101199001010011")))
|
||||||
|
.thenReturn(List.of());
|
||||||
|
|
||||||
service.importAssetInfoAsync(List.of(excel), "task-self", "tester");
|
service.importAssetInfoAsync(List.of(excel), "task-self", "tester");
|
||||||
|
|
||||||
ArgumentCaptor<List<CcdiAssetInfo>> captor = ArgumentCaptor.forClass(List.class);
|
verify(assetInfoMapper, never()).insertBatch(any());
|
||||||
verify(assetInfoMapper).insertBatch(captor.capture());
|
ArgumentCaptor<Object> failureCaptor = ArgumentCaptor.forClass(Object.class);
|
||||||
assertEquals("320101199001010011", captor.getValue().get(0).getFamilyId());
|
verify(valueOperations).set(eq("import:assetInfo:task-self:failures"), failureCaptor.capture(), eq(7L), eq(TimeUnit.DAYS));
|
||||||
assertEquals("320101199001010011", captor.getValue().get(0).getPersonId());
|
AssetImportFailureVO failure = (AssetImportFailureVO) ((List<?>) failureCaptor.getValue()).get(0);
|
||||||
verify(assetInfoMapper, never()).selectOwnerCandidatesByRelationCertNos(any());
|
assertEquals("320101199001010011", failure.getPersonId());
|
||||||
|
assertTrue(failure.getErrorMessage().contains("未找到亲属资产归属员工"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -150,7 +152,7 @@ class CcdiAssetInfoImportServiceImplTest {
|
|||||||
assertEquals(1, failures.size());
|
assertEquals(1, failures.size());
|
||||||
AssetImportFailureVO failure = (AssetImportFailureVO) failures.get(0);
|
AssetImportFailureVO failure = (AssetImportFailureVO) failures.get(0);
|
||||||
assertEquals("320101199001010099", failure.getPersonId());
|
assertEquals("320101199001010099", failure.getPersonId());
|
||||||
assertTrue(failure.getErrorMessage().contains("未找到资产归属员工"));
|
assertTrue(failure.getErrorMessage().contains("未找到亲属资产归属员工"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -170,7 +172,7 @@ class CcdiAssetInfoImportServiceImplTest {
|
|||||||
ArgumentCaptor<Object> failureCaptor = ArgumentCaptor.forClass(Object.class);
|
ArgumentCaptor<Object> failureCaptor = ArgumentCaptor.forClass(Object.class);
|
||||||
verify(valueOperations).set(eq("import:assetInfo:task-4:failures"), failureCaptor.capture(), eq(7L), eq(TimeUnit.DAYS));
|
verify(valueOperations).set(eq("import:assetInfo:task-4:failures"), failureCaptor.capture(), eq(7L), eq(TimeUnit.DAYS));
|
||||||
AssetImportFailureVO failure = (AssetImportFailureVO) ((List<?>) failureCaptor.getValue()).get(0);
|
AssetImportFailureVO failure = (AssetImportFailureVO) ((List<?>) failureCaptor.getValue()).get(0);
|
||||||
assertTrue(failure.getErrorMessage().contains("资产归属员工不唯一"));
|
assertTrue(failure.getErrorMessage().contains("亲属资产归属员工不唯一"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -191,7 +193,7 @@ class CcdiAssetInfoImportServiceImplTest {
|
|||||||
));
|
));
|
||||||
AssetImportFailureVO failureVO = new AssetImportFailureVO();
|
AssetImportFailureVO failureVO = new AssetImportFailureVO();
|
||||||
failureVO.setPersonId("320101199001010099");
|
failureVO.setPersonId("320101199001010099");
|
||||||
failureVO.setErrorMessage("未找到资产归属员工");
|
failureVO.setErrorMessage("未找到亲属资产归属员工");
|
||||||
when(valueOperations.get("import:assetInfo:task-5:failures")).thenReturn(List.of(failureVO));
|
when(valueOperations.get("import:assetInfo:task-5:failures")).thenReturn(List.of(failureVO));
|
||||||
|
|
||||||
ImportStatusVO statusVO = service.getImportStatus("task-5");
|
ImportStatusVO statusVO = service.getImportStatus("task-5");
|
||||||
|
|||||||
@@ -0,0 +1,145 @@
|
|||||||
|
package com.ruoyi.info.collection.service;
|
||||||
|
|
||||||
|
import com.ruoyi.info.collection.domain.CcdiAssetInfo;
|
||||||
|
import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffAssetInfoExcel;
|
||||||
|
import com.ruoyi.info.collection.domain.vo.BaseStaffAssetImportFailureVO;
|
||||||
|
import com.ruoyi.info.collection.domain.vo.ImportStatusVO;
|
||||||
|
import com.ruoyi.info.collection.mapper.CcdiAssetInfoMapper;
|
||||||
|
import com.ruoyi.info.collection.service.impl.CcdiBaseStaffAssetImportServiceImpl;
|
||||||
|
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.HashOperations;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.data.redis.core.ValueOperations;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
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.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyMap;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class CcdiBaseStaffAssetImportServiceImplTest {
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private CcdiBaseStaffAssetImportServiceImpl service;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private CcdiAssetInfoMapper assetInfoMapper;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private RedisTemplate<String, Object> redisTemplate;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ICcdiBaseStaffAssetImportService baseStaffAssetImportService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private HashOperations<String, Object, Object> hashOperations;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ValueOperations<String, Object> valueOperations;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void importAssetInfo_shouldUseDedicatedBaseStaffAssetTaskKeys() {
|
||||||
|
List<CcdiBaseStaffAssetInfoExcel> excelList = List.of(buildExcel("320101199001010011", "房产"));
|
||||||
|
when(redisTemplate.opsForHash()).thenReturn(hashOperations);
|
||||||
|
|
||||||
|
String taskId = service.importAssetInfo(excelList);
|
||||||
|
|
||||||
|
verify(hashOperations).putAll(eq("import:baseStaffAsset:" + taskId), anyMap());
|
||||||
|
verify(redisTemplate).expire("import:baseStaffAsset:" + taskId, 7, TimeUnit.DAYS);
|
||||||
|
verify(baseStaffAssetImportService).importAssetInfoAsync(eq(excelList), eq(taskId), any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void importAssetInfoAsync_shouldImportWhenEmployeeIdCardExists() {
|
||||||
|
CcdiBaseStaffAssetInfoExcel excel = buildExcel("320101199001010011", "房产");
|
||||||
|
when(redisTemplate.opsForHash()).thenReturn(hashOperations);
|
||||||
|
when(assetInfoMapper.selectOwnerCandidatesByBaseStaffIdCards(List.of("320101199001010011")))
|
||||||
|
.thenReturn(List.of(owner("320101199001010011", "320101199001010011")));
|
||||||
|
|
||||||
|
service.importAssetInfoAsync(List.of(excel), "task-1", "tester");
|
||||||
|
|
||||||
|
ArgumentCaptor<List<CcdiAssetInfo>> captor = ArgumentCaptor.forClass(List.class);
|
||||||
|
verify(assetInfoMapper).insertBatch(captor.capture());
|
||||||
|
assertEquals("320101199001010011", captor.getValue().get(0).getFamilyId());
|
||||||
|
assertEquals("320101199001010011", captor.getValue().get(0).getPersonId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void importAssetInfoAsync_shouldFailWhenFamilyCertificateIsUsed() {
|
||||||
|
CcdiBaseStaffAssetInfoExcel excel = buildExcel("320101199201010022", "车辆");
|
||||||
|
when(redisTemplate.opsForHash()).thenReturn(hashOperations);
|
||||||
|
when(redisTemplate.opsForValue()).thenReturn(valueOperations);
|
||||||
|
when(assetInfoMapper.selectOwnerCandidatesByBaseStaffIdCards(List.of("320101199201010022")))
|
||||||
|
.thenReturn(List.of());
|
||||||
|
|
||||||
|
service.importAssetInfoAsync(List.of(excel), "task-2", "tester");
|
||||||
|
|
||||||
|
verify(assetInfoMapper, never()).insertBatch(any());
|
||||||
|
ArgumentCaptor<Object> failureCaptor = ArgumentCaptor.forClass(Object.class);
|
||||||
|
verify(valueOperations).set(eq("import:baseStaffAsset:task-2:failures"), failureCaptor.capture(), eq(7L), eq(TimeUnit.DAYS));
|
||||||
|
BaseStaffAssetImportFailureVO failure = (BaseStaffAssetImportFailureVO) ((List<?>) failureCaptor.getValue()).get(0);
|
||||||
|
assertEquals("320101199201010022", failure.getPersonId());
|
||||||
|
assertTrue(failure.getErrorMessage().contains("员工资产导入仅支持员工本人证件号"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getImportStatusAndFailures_shouldUseBaseStaffAssetPrefixes() {
|
||||||
|
when(redisTemplate.opsForHash()).thenReturn(hashOperations);
|
||||||
|
when(redisTemplate.opsForValue()).thenReturn(valueOperations);
|
||||||
|
when(redisTemplate.hasKey("import:baseStaffAsset:task-3")).thenReturn(true);
|
||||||
|
when(hashOperations.entries("import:baseStaffAsset:task-3")).thenReturn(Map.of(
|
||||||
|
"taskId", "task-3",
|
||||||
|
"status", "SUCCESS",
|
||||||
|
"totalCount", 1,
|
||||||
|
"successCount", 1,
|
||||||
|
"failureCount", 0,
|
||||||
|
"progress", 100,
|
||||||
|
"startTime", 1L,
|
||||||
|
"endTime", 2L,
|
||||||
|
"message", "全部成功"
|
||||||
|
));
|
||||||
|
BaseStaffAssetImportFailureVO failureVO = new BaseStaffAssetImportFailureVO();
|
||||||
|
failureVO.setPersonId("320101199001010099");
|
||||||
|
failureVO.setErrorMessage("员工资产导入仅支持员工本人证件号");
|
||||||
|
when(valueOperations.get("import:baseStaffAsset:task-3:failures")).thenReturn(List.of(failureVO));
|
||||||
|
|
||||||
|
ImportStatusVO statusVO = service.getImportStatus("task-3");
|
||||||
|
List<BaseStaffAssetImportFailureVO> failures = service.getImportFailures("task-3");
|
||||||
|
|
||||||
|
assertEquals("task-3", statusVO.getTaskId());
|
||||||
|
assertEquals("SUCCESS", statusVO.getStatus());
|
||||||
|
assertNotNull(failures);
|
||||||
|
assertEquals(1, failures.size());
|
||||||
|
assertEquals("320101199001010099", failures.get(0).getPersonId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private CcdiBaseStaffAssetInfoExcel buildExcel(String personId, String assetMainType) {
|
||||||
|
CcdiBaseStaffAssetInfoExcel excel = new CcdiBaseStaffAssetInfoExcel();
|
||||||
|
excel.setPersonId(personId);
|
||||||
|
excel.setAssetMainType(assetMainType);
|
||||||
|
excel.setAssetSubType(assetMainType + "小类");
|
||||||
|
excel.setAssetName(assetMainType + "名称");
|
||||||
|
excel.setCurrentValue(new BigDecimal("100.00"));
|
||||||
|
excel.setAssetStatus("正常");
|
||||||
|
return excel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> owner(String personId, String familyId) {
|
||||||
|
return Map.of("personId", personId, "familyId", familyId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -239,3 +239,10 @@
|
|||||||
- 独立提交员工资产导入新增改动
|
- 独立提交员工资产导入新增改动
|
||||||
- 独立提交亲属资产导入收敛改动
|
- 独立提交亲属资产导入收敛改动
|
||||||
- 任一阶段出现回归,可按提交粒度回退
|
- 任一阶段出现回归,可按提交粒度回退
|
||||||
|
|
||||||
|
## 实现状态
|
||||||
|
|
||||||
|
- 2026-03-13 已完成后端拆分实现
|
||||||
|
- 已新增员工资产独立导入接口 `/ccdi/baseStaff/asset/*`
|
||||||
|
- 已将 `/ccdi/assetInfo/*` 收敛为亲属资产专用接口
|
||||||
|
- 已通过后端定向测试验证员工与亲属两套导入链路、模板名称和失败文案拆分生效
|
||||||
|
|||||||
Reference in New Issue
Block a user