新增员工资产信息后端实施计划

This commit is contained in:
wkc
2026-03-12 16:33:07 +08:00
parent 606aab6bb4
commit bac3cf094e
22 changed files with 1825 additions and 2 deletions

View File

@@ -0,0 +1,102 @@
package com.ruoyi.info.collection.controller;
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 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 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-12
*/
@Tag(name = "员工资产信息管理")
@RestController
@RequestMapping("/ccdi/assetInfo")
public class CcdiAssetInfoController extends BaseController {
@Resource
private ICcdiAssetInfoImportService assetInfoImportService;
/**
* 下载导入模板
*/
@Operation(summary = "下载资产导入模板")
@PostMapping("/importTemplate")
public void importTemplate(HttpServletResponse response) {
EasyExcelUtil.importTemplateWithDictDropdown(response, CcdiAssetInfoExcel.class, "员工资产信息");
}
/**
* 导入员工资产信息
*/
@Operation(summary = "导入员工资产信息")
@PreAuthorize("@ss.hasPermi('ccdi:baseStaff:import')")
@Log(title = "员工资产信息", businessType = BusinessType.IMPORT)
@PostMapping("/importData")
public AjaxResult importData(MultipartFile file) throws Exception {
List<CcdiAssetInfoExcel> list = EasyExcelUtil.importExcel(file.getInputStream(), CcdiAssetInfoExcel.class);
if (list == null || list.isEmpty()) {
return error("至少需要一条数据");
}
String taskId = assetInfoImportService.importAssetInfo(list);
ImportResultVO result = new ImportResultVO();
result.setTaskId(taskId);
result.setStatus("PROCESSING");
result.setMessage("导入任务已提交,正在后台处理");
return AjaxResult.success("导入任务已提交,正在后台处理", result);
}
/**
* 查询导入状态
*/
@Operation(summary = "查询员工资产导入状态")
@PreAuthorize("@ss.hasPermi('ccdi:baseStaff:import')")
@GetMapping("/importStatus/{taskId}")
public AjaxResult getImportStatus(@PathVariable String taskId) {
return success(assetInfoImportService.getImportStatus(taskId));
}
/**
* 查询导入失败记录
*/
@Operation(summary = "查询员工资产导入失败记录")
@PreAuthorize("@ss.hasPermi('ccdi:baseStaff:import')")
@GetMapping("/importFailures/{taskId}")
public TableDataInfo getImportFailures(
@PathVariable String taskId,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
List<AssetImportFailureVO> failures = assetInfoImportService.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());
}
}

View File

@@ -0,0 +1,83 @@
package com.ruoyi.info.collection.domain;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 员工资产信息对象 ccdi_asset_info
*
* @author ruoyi
* @date 2026-03-12
*/
@Data
@TableName("ccdi_asset_info")
public class CcdiAssetInfo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/** 资产ID */
@TableId(type = IdType.AUTO)
private Long assetId;
/** 归属员工身份证号 */
private String familyId;
/** 资产实际持有人身份证号 */
private String personId;
/** 资产大类 */
private String assetMainType;
/** 资产小类 */
private String assetSubType;
/** 资产名称 */
private String assetName;
/** 产权占比 */
private BigDecimal ownershipRatio;
/** 购买/评估日期 */
private Date purchaseEvalDate;
/** 资产原值 */
private BigDecimal originalValue;
/** 当前估值 */
private BigDecimal currentValue;
/** 估值截止日期 */
private Date valuationDate;
/** 资产状态 */
private String assetStatus;
/** 备注 */
private String remarks;
/** 创建者 */
@TableField(fill = FieldFill.INSERT)
private String createBy;
/** 创建时间 */
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/** 更新者 */
@TableField(fill = FieldFill.INSERT_UPDATE)
private String updateBy;
/** 更新时间 */
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
}

View File

@@ -0,0 +1,86 @@
package com.ruoyi.info.collection.domain.dto;
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;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 员工资产信息DTO
*
* @author ruoyi
* @date 2026-03-12
*/
@Data
@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个字符")
@Schema(description = "资产大类")
private String assetMainType;
/** 资产小类 */
@NotBlank(message = "资产小类不能为空")
@Size(max = 50, message = "资产小类长度不能超过50个字符")
@Schema(description = "资产小类")
private String assetSubType;
/** 资产名称 */
@NotBlank(message = "资产名称不能为空")
@Size(max = 200, message = "资产名称长度不能超过200个字符")
@Schema(description = "资产名称")
private String assetName;
/** 产权占比 */
@Schema(description = "产权占比")
private BigDecimal ownershipRatio;
/** 购买/评估日期 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Schema(description = "购买/评估日期")
private Date purchaseEvalDate;
/** 资产原值 */
@Schema(description = "资产原值")
private BigDecimal originalValue;
/** 当前估值 */
@NotNull(message = "当前估值不能为空")
@Schema(description = "当前估值")
private BigDecimal currentValue;
/** 估值截止日期 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Schema(description = "估值截止日期")
private Date valuationDate;
/** 资产状态 */
@NotBlank(message = "资产状态不能为空")
@Size(max = 10, message = "资产状态长度不能超过10个字符")
@Schema(description = "资产状态")
private String assetStatus;
/** 备注 */
@Size(max = 500, message = "备注长度不能超过500个字符")
@Schema(description = "备注")
private String remarks;
}

View File

@@ -9,6 +9,7 @@ import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* 员工信息新增 DTO
@@ -51,4 +52,7 @@ public class CcdiBaseStaffAddDTO implements Serializable {
/** 状态 */
@NotBlank(message = "状态不能为空")
private String status;
/** 资产信息列表 */
private List<CcdiAssetInfoDTO> assetInfoList;
}

View File

@@ -9,6 +9,7 @@ import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* 员工信息编辑 DTO
@@ -49,4 +50,7 @@ public class CcdiBaseStaffEditDTO implements Serializable {
/** 状态 */
private String status;
/** 资产信息列表 */
private List<CcdiAssetInfoDTO> assetInfoList;
}

View File

@@ -0,0 +1,85 @@
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.Required;
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-12
*/
@Data
public class CcdiAssetInfoExcel implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/** 资产实际持有人身份证号 */
@ExcelProperty(value = "资产实际持有人身份证号*", index = 0)
@ColumnWidth(22)
@Required
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)
@Required
private String assetStatus;
/** 备注 */
@ExcelProperty(value = "备注", index = 10)
@ColumnWidth(28)
private String remarks;
}

View File

@@ -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-12
*/
@Data
@Schema(description = "员工资产信息导入失败记录")
public class AssetImportFailureVO {
/** 资产实际持有人身份证号 */
@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;
}

View File

@@ -0,0 +1,70 @@
package com.ruoyi.info.collection.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 员工资产信息VO
*
* @author ruoyi
* @date 2026-03-12
*/
@Data
@Schema(description = "员工资产信息")
public class CcdiAssetInfoVO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/** 资产实际持有人身份证号 */
@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;
/** 购买/评估日期 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Schema(description = "购买/评估日期")
private Date purchaseEvalDate;
/** 资产原值 */
@Schema(description = "资产原值")
private BigDecimal originalValue;
/** 当前估值 */
@Schema(description = "当前估值")
private BigDecimal currentValue;
/** 估值截止日期 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Schema(description = "估值截止日期")
private Date valuationDate;
/** 资产状态 */
@Schema(description = "资产状态")
private String assetStatus;
/** 备注 */
@Schema(description = "备注")
private String remarks;
}

View File

@@ -5,6 +5,7 @@ import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* 员工信息 VO
@@ -56,4 +57,7 @@ public class CcdiBaseStaffVO implements Serializable {
/** 更新者 */
private String updateBy;
/** 资产信息列表 */
private List<CcdiAssetInfoVO> assetInfoList;
}

View File

@@ -0,0 +1,73 @@
package com.ruoyi.info.collection.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.info.collection.domain.CcdiAssetInfo;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
* 员工资产信息 数据层
*
* @author ruoyi
* @date 2026-03-12
*/
public interface CcdiAssetInfoMapper extends BaseMapper<CcdiAssetInfo> {
/**
* 按归属员工身份证号查询资产列表
*
* @param familyId 归属员工身份证号
* @return 资产列表
*/
List<CcdiAssetInfo> selectByFamilyId(@Param("familyId") String familyId);
/**
* 按归属员工身份证号删除资产
*
* @param familyId 归属员工身份证号
* @return 影响行数
*/
int deleteByFamilyId(@Param("familyId") String familyId);
/**
* 批量删除归属员工资产
*
* @param familyIds 归属员工身份证号列表
* @return 影响行数
*/
int deleteByFamilyIds(@Param("familyIds") List<String> familyIds);
/**
* 按资产实际持有人身份证号查询资产列表
*
* @param personId 资产实际持有人身份证号
* @return 资产列表
*/
List<CcdiAssetInfo> selectByPersonId(@Param("personId") String personId);
/**
* 批量插入资产数据
*
* @param list 资产列表
* @return 影响行数
*/
int insertBatch(@Param("list") List<CcdiAssetInfo> list);
/**
* 按员工身份证号查询归属信息
*
* @param personIds 资产实际持有人身份证号列表
* @return 归属映射
*/
List<Map<String, String>> selectOwnerByEmployeeIdCards(@Param("personIds") List<String> personIds);
/**
* 按员工家庭关系身份证号查询归属信息
*
* @param personIds 资产实际持有人身份证号列表
* @return 归属映射
*/
List<Map<String, String>> selectOwnerByFamilyRelationIdCards(@Param("personIds") List<String> personIds);
}

View File

@@ -0,0 +1,49 @@
package com.ruoyi.info.collection.service;
import com.ruoyi.info.collection.domain.excel.CcdiAssetInfoExcel;
import com.ruoyi.info.collection.domain.vo.AssetImportFailureVO;
import com.ruoyi.info.collection.domain.vo.ImportStatusVO;
import java.util.List;
/**
* 员工资产信息异步导入 服务层
*
* @author ruoyi
* @date 2026-03-12
*/
public interface ICcdiAssetInfoImportService {
/**
* 启动异步导入任务
*
* @param excelList Excel实体列表
* @return 任务ID
*/
String importAssetInfo(List<CcdiAssetInfoExcel> excelList);
/**
* 异步导入员工资产数据
*
* @param excelList Excel实体列表
* @param taskId 任务ID
* @param userName 用户名
*/
void importAssetInfoAsync(List<CcdiAssetInfoExcel> excelList, String taskId, String userName);
/**
* 查询导入状态
*
* @param taskId 任务ID
* @return 导入状态
*/
ImportStatusVO getImportStatus(String taskId);
/**
* 查询导入失败记录
*
* @param taskId 任务ID
* @return 失败记录列表
*/
List<AssetImportFailureVO> getImportFailures(String taskId);
}

View File

@@ -0,0 +1,47 @@
package com.ruoyi.info.collection.service;
import com.ruoyi.info.collection.domain.CcdiAssetInfo;
import com.ruoyi.info.collection.domain.dto.CcdiAssetInfoDTO;
import java.util.List;
/**
* 员工资产信息 服务层
*
* @author ruoyi
* @date 2026-03-12
*/
public interface ICcdiAssetInfoService {
/**
* 按归属员工身份证号查询资产列表
*
* @param familyId 归属员工身份证号
* @return 资产列表
*/
List<CcdiAssetInfo> selectByFamilyId(String familyId);
/**
* 按归属员工身份证号覆盖资产列表
*
* @param familyId 归属员工身份证号
* @param assetInfoList 资产列表
*/
void replaceByFamilyId(String familyId, List<CcdiAssetInfoDTO> assetInfoList);
/**
* 删除单个员工资产
*
* @param familyId 归属员工身份证号
* @return 影响行数
*/
int deleteByFamilyId(String familyId);
/**
* 批量删除员工资产
*
* @param familyIds 归属员工身份证号列表
* @return 影响行数
*/
int deleteByFamilyIds(List<String> familyIds);
}

View File

@@ -0,0 +1,227 @@
package com.ruoyi.info.collection.service.impl;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.info.collection.domain.CcdiAssetInfo;
import com.ruoyi.info.collection.domain.excel.CcdiAssetInfoExcel;
import com.ruoyi.info.collection.domain.vo.AssetImportFailureVO;
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.ICcdiAssetInfoImportService;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
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;
import java.util.stream.Collectors;
/**
* 员工资产信息异步导入服务层处理
*
* @author ruoyi
* @date 2026-03-12
*/
@Service
@EnableAsync
public class CcdiAssetInfoImportServiceImpl implements ICcdiAssetInfoImportService {
private static final String STATUS_KEY_PREFIX = "import:assetInfo:";
@Resource
private CcdiAssetInfoMapper assetInfoMapper;
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Lazy
@Resource
private ICcdiAssetInfoImportService assetInfoImportService;
@Override
@Transactional
public String importAssetInfo(List<CcdiAssetInfoExcel> 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);
assetInfoImportService.importAssetInfoAsync(excelList, taskId, currentUserName());
return taskId;
}
@Override
@Async
@Transactional
public void importAssetInfoAsync(List<CcdiAssetInfoExcel> excelList, String taskId, String userName) {
List<CcdiAssetInfo> successList = new ArrayList<>();
List<AssetImportFailureVO> failures = new ArrayList<>();
List<String> personIds = excelList.stream()
.map(CcdiAssetInfoExcel::getPersonId)
.filter(StringUtils::isNotEmpty)
.distinct()
.toList();
Map<String, Set<String>> ownerMap = buildOwnerMap(personIds);
for (CcdiAssetInfoExcel excel : excelList) {
try {
validateExcel(excel);
Set<String> familyIds = ownerMap.get(excel.getPersonId());
if (familyIds == null || familyIds.isEmpty()) {
throw new RuntimeException("未找到资产归属员工");
}
if (familyIds.size() > 1) {
throw new RuntimeException("资产归属员工不唯一");
}
CcdiAssetInfo assetInfo = new CcdiAssetInfo();
BeanUtils.copyProperties(excel, assetInfo);
assetInfo.setFamilyId(familyIds.iterator().next());
assetInfo.setCreateBy(userName);
assetInfo.setUpdateBy(userName);
successList.add(assetInfo);
} catch (Exception e) {
AssetImportFailureVO failureVO = new AssetImportFailureVO();
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<AssetImportFailureVO> 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), AssetImportFailureVO.class);
}
private Map<String, Set<String>> buildOwnerMap(List<String> personIds) {
Map<String, Set<String>> result = new LinkedHashMap<>();
mergeOwnerMappings(result, assetInfoMapper.selectOwnerByEmployeeIdCards(personIds));
mergeOwnerMappings(result, assetInfoMapper.selectOwnerByFamilyRelationIdCards(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(CcdiAssetInfoExcel 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";
}
}
}

View File

@@ -0,0 +1,84 @@
package com.ruoyi.info.collection.service.impl;
import com.ruoyi.info.collection.domain.CcdiAssetInfo;
import com.ruoyi.info.collection.domain.dto.CcdiAssetInfoDTO;
import com.ruoyi.info.collection.mapper.CcdiAssetInfoMapper;
import com.ruoyi.info.collection.service.ICcdiAssetInfoService;
import com.ruoyi.common.utils.StringUtils;
import jakarta.annotation.Resource;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collections;
import java.util.List;
/**
* 员工资产信息 服务层处理
*
* @author ruoyi
* @date 2026-03-12
*/
@Service
public class CcdiAssetInfoServiceImpl implements ICcdiAssetInfoService {
@Resource
private CcdiAssetInfoMapper assetInfoMapper;
@Override
public List<CcdiAssetInfo> selectByFamilyId(String familyId) {
return assetInfoMapper.selectByFamilyId(familyId);
}
@Override
@Transactional
public void replaceByFamilyId(String familyId, List<CcdiAssetInfoDTO> assetInfoList) {
assetInfoMapper.deleteByFamilyId(familyId);
if (assetInfoList == null || assetInfoList.isEmpty()) {
return;
}
List<CcdiAssetInfo> saveList = assetInfoList.stream()
.filter(item -> !isEmptyRow(item))
.map(item -> toEntity(familyId, item))
.toList();
if (!saveList.isEmpty()) {
assetInfoMapper.insertBatch(saveList);
}
}
@Override
public int deleteByFamilyId(String familyId) {
return assetInfoMapper.deleteByFamilyId(familyId);
}
@Override
public int deleteByFamilyIds(List<String> familyIds) {
if (familyIds == null || familyIds.isEmpty()) {
return 0;
}
return assetInfoMapper.deleteByFamilyIds(familyIds);
}
private CcdiAssetInfo toEntity(String familyId, CcdiAssetInfoDTO dto) {
CcdiAssetInfo assetInfo = new CcdiAssetInfo();
BeanUtils.copyProperties(dto, assetInfo);
assetInfo.setFamilyId(familyId);
return assetInfo;
}
private boolean isEmptyRow(CcdiAssetInfoDTO dto) {
return StringUtils.isEmpty(dto.getPersonId())
&& StringUtils.isEmpty(dto.getAssetMainType())
&& StringUtils.isEmpty(dto.getAssetSubType())
&& StringUtils.isEmpty(dto.getAssetName())
&& dto.getCurrentValue() == null
&& StringUtils.isEmpty(dto.getAssetStatus())
&& dto.getOwnershipRatio() == null
&& dto.getPurchaseEvalDate() == null
&& dto.getOriginalValue() == null
&& dto.getValuationDate() == null
&& StringUtils.isEmpty(dto.getRemarks());
}
}

View File

@@ -7,10 +7,12 @@ import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffAddDTO;
import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffEditDTO;
import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffQueryDTO;
import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffExcel;
import com.ruoyi.info.collection.domain.vo.CcdiAssetInfoVO;
import com.ruoyi.info.collection.domain.vo.CcdiBaseStaffOptionVO;
import com.ruoyi.info.collection.domain.vo.CcdiBaseStaffVO;
import com.ruoyi.info.collection.enums.EmployeeStatus;
import com.ruoyi.info.collection.mapper.CcdiBaseStaffMapper;
import com.ruoyi.info.collection.service.ICcdiAssetInfoService;
import com.ruoyi.info.collection.service.ICcdiBaseStaffImportService;
import com.ruoyi.info.collection.service.ICcdiBaseStaffService;
import com.ruoyi.common.utils.StringUtils;
@@ -40,6 +42,9 @@ public class CcdiBaseStaffServiceImpl implements ICcdiBaseStaffService {
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Resource
private ICcdiAssetInfoService assetInfoService;
/**
* 查询员工列表
*
@@ -104,7 +109,15 @@ public class CcdiBaseStaffServiceImpl implements ICcdiBaseStaffService {
@Override
public CcdiBaseStaffVO selectBaseStaffById(Long staffId) {
CcdiBaseStaff staff = baseStaffMapper.selectById(staffId);
return convertToVO(staff);
CcdiBaseStaffVO vo = convertToVO(staff);
if (staff != null) {
vo.setAssetInfoList(assetInfoService.selectByFamilyId(staff.getIdCard()).stream().map(asset -> {
CcdiAssetInfoVO assetInfoVO = new CcdiAssetInfoVO();
BeanUtils.copyProperties(asset, assetInfoVO);
return assetInfoVO;
}).toList());
}
return vo;
}
/**
@@ -131,6 +144,7 @@ public class CcdiBaseStaffServiceImpl implements ICcdiBaseStaffService {
CcdiBaseStaff staff = new CcdiBaseStaff();
BeanUtils.copyProperties(addDTO, staff);
int result = baseStaffMapper.insert(staff);
assetInfoService.replaceByFamilyId(addDTO.getIdCard(), addDTO.getAssetInfoList());
return result;
}
@@ -144,6 +158,11 @@ public class CcdiBaseStaffServiceImpl implements ICcdiBaseStaffService {
@Override
@Transactional
public int updateBaseStaff(CcdiBaseStaffEditDTO editDTO) {
CcdiBaseStaff existing = baseStaffMapper.selectById(editDTO.getStaffId());
if (existing == null) {
throw new RuntimeException("员工不存在");
}
// 检查身份证号唯一性(排除自己)
if (StringUtils.isNotEmpty(editDTO.getIdCard())) {
LambdaQueryWrapper<CcdiBaseStaff> wrapper = new LambdaQueryWrapper<>();
@@ -158,6 +177,11 @@ public class CcdiBaseStaffServiceImpl implements ICcdiBaseStaffService {
BeanUtils.copyProperties(editDTO, staff);
int result = baseStaffMapper.updateById(staff);
if (!StringUtils.equals(existing.getIdCard(), editDTO.getIdCard())) {
assetInfoService.deleteByFamilyId(existing.getIdCard());
}
assetInfoService.replaceByFamilyId(editDTO.getIdCard(), editDTO.getAssetInfoList());
return result;
}
@@ -170,7 +194,13 @@ public class CcdiBaseStaffServiceImpl implements ICcdiBaseStaffService {
@Override
@Transactional
public int deleteBaseStaffByIds(Long[] staffIds) {
return baseStaffMapper.deleteBatchIds(List.of(staffIds));
List<Long> idList = List.of(staffIds);
List<String> familyIds = baseStaffMapper.selectBatchIds(idList).stream()
.map(CcdiBaseStaff::getIdCard)
.filter(StringUtils::isNotEmpty)
.toList();
assetInfoService.deleteByFamilyIds(familyIds);
return baseStaffMapper.deleteBatchIds(idList);
}
/**

View File

@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.info.collection.mapper.CcdiAssetInfoMapper">
<resultMap id="CcdiAssetInfoResultMap" type="com.ruoyi.info.collection.domain.CcdiAssetInfo">
<id property="assetId" column="asset_id"/>
<result property="familyId" column="family_id"/>
<result property="personId" column="person_id"/>
<result property="assetMainType" column="asset_main_type"/>
<result property="assetSubType" column="asset_sub_type"/>
<result property="assetName" column="asset_name"/>
<result property="ownershipRatio" column="ownership_ratio"/>
<result property="purchaseEvalDate" column="purchase_eval_date"/>
<result property="originalValue" column="original_value"/>
<result property="currentValue" column="current_value"/>
<result property="valuationDate" column="valuation_date"/>
<result property="assetStatus" column="asset_status"/>
<result property="remarks" column="remarks"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
</resultMap>
<select id="selectByFamilyId" resultMap="CcdiAssetInfoResultMap">
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 family_id = #{familyId}
ORDER BY create_time DESC, asset_id DESC
</select>
<select id="selectByPersonId" resultMap="CcdiAssetInfoResultMap">
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}
ORDER BY create_time DESC, asset_id DESC
</select>
<delete id="deleteByFamilyId">
DELETE FROM ccdi_asset_info
WHERE family_id = #{familyId}
</delete>
<delete id="deleteByFamilyIds">
DELETE FROM ccdi_asset_info
WHERE family_id IN
<foreach collection="familyIds" item="familyId" open="(" separator="," close=")">
#{familyId}
</foreach>
</delete>
<insert id="insertBatch">
INSERT INTO ccdi_asset_info
(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)
VALUES
<foreach collection="list" item="item" separator=",">
(#{item.familyId}, #{item.personId}, #{item.assetMainType}, #{item.assetSubType}, #{item.assetName},
#{item.ownershipRatio}, #{item.purchaseEvalDate}, #{item.originalValue}, #{item.currentValue},
#{item.valuationDate}, #{item.assetStatus}, #{item.remarks}, #{item.createBy}, NOW(), #{item.updateBy}, NOW())
</foreach>
</insert>
<select id="selectOwnerByEmployeeIdCards" 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="selectOwnerByFamilyRelationIdCards" resultType="map">
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
<foreach collection="personIds" item="personId" open="(" separator="," close=")">
#{personId}
</foreach>
</select>
</mapper>