Commit 3ffe3bb0 authored by huyuchen's avatar huyuchen

huych-电子档案批量导入初次提交

parent eb755797
......@@ -161,6 +161,19 @@ public class TEmployeeInfoController {
return tEmployeeInfoService.getById(empId);
}
/**
* @param vo
* @Description: 内部接口获取档案基本信息
* @Author: huych
* @Date: 2025/12/17 14:47
* @return: com.yifu.cloud.plus.v1.yifu.common.core.util.R<com.yifu.cloud.plus.v1.yifu.archives.entity.TEmployeeInfo>
**/
@Inner
@PostMapping("/inner/getTEmployeeInfoByIdcardAndName")
public TEmployeeInfo getTEmployeeInfoByIdcardAndName(@RequestBody IdNameNoVo vo) {
return tEmployeeInfoService.getTEmployeeInfoByIdcardAndName(vo);
}
/**
* @param idcard
* @Description: 内部接口获取档案基本信息
......
......@@ -22,7 +22,6 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pig4cloud.plugin.excel.vo.ErrorMessage;
import com.yifu.cloud.plus.v1.yifu.archives.entity.TEmployeeInfo;
import com.yifu.cloud.plus.v1.yifu.archives.entity.TEmployeeProject;
import com.yifu.cloud.plus.v1.yifu.archives.vo.*;
import com.yifu.cloud.plus.v1.yifu.common.core.util.R;
import com.yifu.cloud.plus.v1.yifu.common.core.vo.BaseSearchListVO;
......@@ -213,6 +212,8 @@ public interface TEmployeeInfoService extends IService<TEmployeeInfo> {
// 未删除的档案
TEmployeeInfo getInfoByIdcard(String idcard);
TEmployeeInfo getTEmployeeInfoByIdcardAndName(IdNameNoVo vo);
// 已删除的档案
TEmployeeInfo getInfoByDelete(String idcard);
......
......@@ -2828,6 +2828,14 @@ public class TEmployeeInfoServiceImpl extends ServiceImpl<TEmployeeInfoMapper, T
.last(CommonConstants.LAST_ONE_SQL));
}
@Override
public TEmployeeInfo getTEmployeeInfoByIdcardAndName(IdNameNoVo vo) {
return baseMapper.selectOne(Wrappers.<TEmployeeInfo>query().lambda()
.eq(TEmployeeInfo::getEmpIdcard, vo.getNo())
.eq(TEmployeeInfo::getEmpName, vo.getName())
.last(CommonConstants.LAST_ONE_SQL));
}
@Override
public TEmployeeInfo getInfoByDelete(String idcard) {
return baseMapper.selectOne(Wrappers.<TEmployeeInfo>query().lambda()
......
package com.yifu.cloud.plus.v1.batch.constants;
/**
* 电子档案相关常量
*
* @author huych
* @date 2025-12-17
*/
public interface ArchiveConstants {
/**
* 任务编号前缀
*/
String TASK_CODE_PREFIX = "DZDAPL";
/**
* 任务状态-未上传
*/
String TASK_STATUS_NOT_UPLOADED = "0";
/**
* 任务状态-生成电子档案中
*/
String TASK_STATUS_GENERATING = "1";
/**
* 任务状态-已生成电子档案
*/
String TASK_STATUS_GENERATED = "2";
/**
* 明细状态-不可用
*/
String DETAIL_STATUS_DISABLED = "0";
/**
* 明细状态-可用
*/
String DETAIL_STATUS_ENABLED = "1";
/**
* 附件关系类型-任务表附件
*/
String ATTA_RELATION_TYPE_TASK = "1";
/**
* 附件关系类型-ZIP附件
*/
String ATTA_RELATION_TYPE_ZIP = "2";
/**
* 附件关系类型-反馈表附件
*/
String ATTA_RELATION_TYPE_FEEDBACK = "3";
/**
* 附件状态-可用
*/
String ATTA_STATUS_ENABLED = "0";
/**
* 附件状态-不可用
*/
String ATTA_STATUS_DISABLED = "1";
/**
* 资料类型-存量
*/
String MATERIAL_TYPE_STOCK = "存量";
/**
* 资料类型-新签
*/
String MATERIAL_TYPE_NEW = "新签";
/**
* 资料类型-续签
*/
String MATERIAL_TYPE_RENEW = "续签";
/**
* 资料类型-离职
*/
String MATERIAL_TYPE_LEAVE = "离职";
/**
* 资料类型编码-存量
*/
String MATERIAL_TYPE_CODE_STOCK = "0";
/**
* 资料类型编码-新签
*/
String MATERIAL_TYPE_CODE_NEW = "1";
/**
* 资料类型编码-续签
*/
String MATERIAL_TYPE_CODE_RENEW = "2";
/**
* 资料类型编码-离职
*/
String MATERIAL_TYPE_CODE_LEAVE = "3";
/**
* 处理结果-成功
*/
String PROCESS_RESULT_SUCCESS = "成功";
/**
* 处理结果-失败
*/
String PROCESS_RESULT_FAILED = "失败";
/**
* 处理结果-跳过
*/
String PROCESS_RESULT_SKIP = "跳过";
/**
* ZIP文件最大大小(500M)
*/
long ZIP_MAX_SIZE = 500 * 1024 * 1024L;
/**
* 单个附件最大大小(15M)
*/
long SINGLE_FILE_MAX_SIZE = 15 * 1024 * 1024L;
/**
* 同时上传任务数限制
*/
int MAX_CONCURRENT_TASKS = 5;
/**
* 支持的文件格式
*/
String[] SUPPORTED_FILE_FORMATS = {"png", "jpg", "jpeg", "xlsx", "xls", "doc", "docx", "pdf", "ppt", "pptx", "txt"};
}
package com.yifu.cloud.plus.v1.batch.entity;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.ExcelAttribute;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
/**
* 系统限制配置表
*
* @author hgw
* @date 2024-09-24 17:39:57
*/
@Data
@TableName("sys_config_limit")
@Schema(description = "系统限制配置表")
public class SysConfigLimit {
/**
* id
*/
@TableId(type = IdType.ASSIGN_ID)
@ExcelProperty("id")
@Schema(description = "id")
private String id;
/**
* 限制键
*/
@ExcelAttribute(name = "限制键", isNotEmpty = true, errorInfo = "限制键不能为空", maxLength = 32)
@NotBlank(message = "限制键不能为空")
@Length(max = 32, message = "限制键不能超过32个字符")
@ExcelProperty("限制键")
@Schema(description = "限制键")
private String configKey;
/**
* 限制值
*/
@ExcelAttribute(name = "限制值", isNotEmpty = true, errorInfo = "限制值不能为空")
@NotBlank(message = "限制值不能为空")
@ExcelProperty("限制值")
@Schema(description = "限制值")
private Integer configValue;
/**
* 备注
*/
@ExcelAttribute(name = "备注", maxLength = 255)
@Length(max = 255, message = "备注不能超过255个字符")
@ExcelProperty("备注")
@Schema(description = "备注")
private String remark;
}
package com.yifu.cloud.plus.v1.batch.entity;
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 com.yifu.cloud.plus.v1.yifu.common.mybatis.base.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
/**
* @Author fxj
* @Description 附件信息表
* @Date 11:07 2022/6/22
**/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("t_atta_info")
@Tag(name = "附件信息表")
public class TAttaInfo extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private String id;
/**
* 资源名称
*/
@NotBlank(message = "资源名称不能为空")
@Length(max = 50, message = "资源名称不能超过50个字符")
@Schema(description = "资源名称")
private String attaName;
/**
* 资源地址
*/
@NotBlank(message = "资源地址不能为空")
@Length(max = 100, message = "资源地址不能超过100个字符")
@Schema(description = "资源地址")
private String attaSrc;
/**
* 资源大小
*/
@Schema(description = "资源大小")
private Long attaSize;
/**
* 资源类型
*/
@Length(max = 10, message = "资源类型不能超过10个字符")
@Schema(description = "资源类型")
private String attaType;
/**
* 关系类型 1 任务表附件 2 zip附件 3反馈表附件
*/
@NotBlank(message = "关系类型不能为空")
@Length(max = 20, message = "关系类型不能超过20个字符")
@Schema(description = "关系类型 1 任务表附件 2 zip附件 3反馈表附件")
private String relationType;
/**
* 实体id和关系类型共同确定附件所属
*/
@NotBlank(message = "实体id和关系类型共同确定附件所属不能为空")
@Length(max = 32, message = "实体id和关系类型共同确定附件所属不能超过32个字符")
@Schema(description = "实体id和关系类型共同确定附件所属")
private String domainId;
/**
* 状态 0可用 1不可用
*/
@Schema(description = "状态 0可用 1不可用")
private String status;
/**
* 地址URL
*/
@Length(max = 100, message = "地址URL不能超过100个字符")
@Schema(description = "地址URL")
@TableField(exist = false)
private String attaUrl;
/**
* 附件变更类型:0 新增 1 更新 2 删除 3 不变 档案编辑附件时需要告知后端是增加还是减少
*/
@TableField(exist = false)
private String handleType;
/**
* 其他分租名称
*/
@TableField(exist = false)
private String groupName;
}
package com.yifu.cloud.plus.v1.batch.entity;
import com.alibaba.excel.annotation.ExcelProperty;
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 com.yifu.cloud.plus.v1.batch.vo.TaskAttachmentGroupVO;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.ExcelAttribute;
import com.yifu.cloud.plus.v1.yifu.common.mybatis.base.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.hibernate.validator.constraints.Length;
import java.util.List;
/**
* 电子档案明细表
*
* @author huych
* @date 2025-12-17 11:03:37
*/
@Data
@TableName("t_electronic_archive_detail")
@EqualsAndHashCode(callSuper = true)
@Schema(description = "电子档案明细表")
public class TElectronicArchiveDetail extends BaseEntity {
/**
* 主键
*/
@TableId(type = IdType.ASSIGN_ID)
@ExcelProperty("主键")
@Schema(description = "主键")
private String id;
/**
* 姓名
*/
@ExcelAttribute(name = "姓名", maxLength = 32)
@ExcelProperty("姓名")
@Schema(description = "姓名")
private String empName;
/**
* 身份证号码
*/
@ExcelAttribute(name = "身份证号码")
@ExcelProperty("身份证号码")
@Schema(description = "身份证号码")
private String empIdcard;
/**
* 项目编码
*/
@ExcelAttribute(name = "项目编码")
@ExcelProperty("项目编码")
@Schema(description = "项目编码")
private String deptNo;
/**
* 项目名称
*/
@ExcelAttribute(name = "项目名称")
@ExcelProperty("项目名称")
@Schema(description = "项目名称")
private String deptName;
/**
* 客户名称
*/
@ExcelAttribute(name = "客户名称")
@ExcelProperty("客户名称")
@Schema(description = "客户名称")
private String customerName;
/**
* 客户编码
*/
@ExcelAttribute(name = "客户编码")
@ExcelProperty("客户编码")
@Schema(description = "客户编码")
private String customerCode;
/**
* 资料类型(存量、新签、续签、离职,多个用逗号分隔)
*/
@ExcelAttribute(name = "资料类型")
@ExcelProperty("资料类型")
@Schema(description = "资料类型 存量、新签、续签、离职,多个用逗号分隔)")
private String materialType;
/**
* 档案所在地-省
*/
@ExcelAttribute(name = "档案所在地-省")
@ExcelProperty("档案所在地-省")
@Schema(description = "档案所在地-省")
private Integer fileProvince;
/**
* 档案所在地-市
*/
@ExcelAttribute(name = "档案所在地-市")
@ExcelProperty("档案所在地-市")
@Schema(description = "档案所在地-市")
private Integer fileCity;
/**
* 档案所在地-县
*/
@ExcelAttribute(name = "档案所在地-县")
@ExcelProperty("档案所在地-县")
@Schema(description = "档案所在地-县")
private Integer fileTown;
/**
* 状态(0不可用,1可用)
*/
@ExcelAttribute(name = "状态")
@ExcelProperty("状态")
@Schema(description = "状态(0不可用,1可用)")
private String status;
/**
* 是否删除 0否/1是
*/
@ExcelAttribute(name = "是否删除 0否/1是", maxLength = 1)
@Length(max = 1, message = "是否删除 0否/1是不能超过1个字符")
@ExcelProperty("是否删除 0否/1是")
@Schema(description = "是否删除 0否/1是")
private String deleteFlag;
/**
* 附件Id
*/
@TableField(exist = false)
private List<String> attaIdList;
/**
* 印章附件
*/
@TableField(exist = false)
private List<TAttaInfo> attaInfoList;
/**
* 按任务分组的附件列表
*/
@TableField(exist = false)
private List<TaskAttachmentGroupVO> taskAttachmentGroups;
}
package com.yifu.cloud.plus.v1.batch.entity;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.ExcelAttribute;
import com.yifu.cloud.plus.v1.yifu.common.mybatis.base.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
/**
* 电子档案任务表
*
* @author huych
* @date 2025-12-17 11:03:07
*/
@Data
@TableName("t_electronic_archive_task")
@EqualsAndHashCode(callSuper = true)
@Schema(description = "电子档案任务表")
public class TElectronicArchiveTask extends BaseEntity {
/**
* 主键
*/
@TableId(type = IdType.ASSIGN_ID)
@ExcelProperty("主键")
@Schema(description = "主键")
private String id;
/**
* 任务编号
*/
@ExcelAttribute(name = "任务编号")
@NotBlank(message = "任务编号不能为空")
@ExcelProperty("任务编号")
@Schema(description = "任务编号")
private String taskCode;
/**
* 上传表格名称
*/
@ExcelAttribute(name = "上传表格名称")
@ExcelProperty("上传表格名称")
@Schema(description = "上传表格名称")
private String uploadFileName;
/**
* 上传压缩包名称
*/
@ExcelAttribute(name = "上传压缩包名称")
@ExcelProperty("上传压缩包名称")
@Schema(description = "上传压缩包名称")
private String uploadZipName;
@Schema(description = "状态(未上传,生成电子档案中,已生成电子档案)")
private String status;
@Schema(description = "是否删除 0否/1是")
private String deleteFlag;
}
package com.yifu.cloud.plus.v1.batch.entity;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.ExcelAttribute;
import com.yifu.cloud.plus.v1.yifu.common.mybatis.base.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 电子档案上传任务和档案明细关系表
*
* @author huych
* @date 2025-12-17 11:02:01
*/
@Data
@TableName("t_task_archive_relation")
@EqualsAndHashCode(callSuper = true)
@Schema(description = "电子档案上传任务和档案明细关系表")
public class TTaskArchiveRelation extends BaseEntity {
/**
* 主键
*/
@TableId(type = IdType.ASSIGN_ID)
@ExcelProperty("主键")
@Schema(description = "主键")
private String id;
/**
* 任务表ID
*/
@ExcelAttribute(name = "任务表ID")
@ExcelProperty("任务表ID")
@Schema(description = "任务表ID")
private String taskId;
/**
* 电子档案明细表ID
*/
@ExcelAttribute(name = "电子档案明细表ID")
@ExcelProperty("电子档案明细表ID")
@Schema(description = "电子档案明细表ID")
private String archiveDetailId;
}
package com.yifu.cloud.plus.v1.batch.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
/**
* 电子档案批量下载请求VO
*
* @author huych
* @date 2025-12-17
*/
@Data
@Schema(description = "电子档案批量下载请求")
public class ArchiveBatchDownloadRequestVO implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 姓名
*/
@NotBlank(message = "姓名不能为空")
@Schema(description = "姓名")
private String empName;
/**
* 身份证号码
*/
@NotBlank(message = "身份证号码不能为空")
@Schema(description = "身份证号码")
private String empIdcard;
/**
* 项目编码
*/
@NotBlank(message = "项目编码不能为空")
@Schema(description = "项目编码")
private String deptNo;
/**
* 项目名称
*/
@Schema(description = "项目名称")
private String deptName;
/**
* 资料类型(为空则下载该人员该项目下所有资料类型)
*/
@Schema(description = "资料类型(为空则下载该人员该项目下所有资料类型)")
private String materialType;
/**
* Excel行号(用于反馈)
*/
@Schema(description = "Excel行号")
private Integer rowIndex;
}
package com.yifu.cloud.plus.v1.batch.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 电子档案下载结果反馈VO
*
* @author huych
* @date 2025-12-17
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "电子档案下载结果反馈")
public class ArchiveDownloadFeedbackVO implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 姓名
*/
@ExcelProperty(value = "姓名", index = 0)
@Schema(description = "姓名")
private String empName;
/**
* 身份证号码
*/
@ExcelProperty(value = "身份证号码", index = 1)
@Schema(description = "身份证号码")
private String empIdcard;
/**
* 项目编码
*/
@ExcelProperty(value = "项目编码", index = 2)
@Schema(description = "项目编码")
private String deptNo;
/**
* 项目名称
*/
@ExcelProperty(value = "项目名称", index = 3)
@Schema(description = "项目名称")
private String deptName;
/**
* 资料类型
*/
@ExcelProperty(value = "资料类型", index = 4)
@Schema(description = "资料类型")
private String materialType;
/**
* 下载状态(成功/失败)
*/
@ExcelProperty(value = "下载状态", index = 5)
@Schema(description = "下载状态")
private String downloadStatus;
/**
* 失败原因
*/
@ExcelProperty(value = "失败原因", index = 6)
@Schema(description = "失败原因")
private String failReason;
}
package com.yifu.cloud.plus.v1.batch.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.ExcelAttribute;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* 电子档案导入反馈表VO
*
* @author huych
* @date 2025-12-17
*/
@Data
@Schema(description = "电子档案导入反馈表VO")
public class ArchiveFeedbackVO implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 姓名
*/
@ExcelAttribute(name = "姓名")
@ExcelProperty(value = "姓名", index = 0)
@Schema(description = "姓名")
private String empName;
/**
* 身份证号码
*/
@ExcelAttribute(name = "身份证号码")
@ExcelProperty(value = "身份证号码", index = 1)
@Schema(description = "身份证号码")
private String empIdcard;
/**
* 项目名称
*/
@ExcelAttribute(name = "项目名称")
@ExcelProperty(value = "项目名称", index = 2)
@Schema(description = "项目名称")
private String deptName;
/**
* 项目编码
*/
@ExcelAttribute(name = "项目编码")
@ExcelProperty(value = "项目编码", index = 3)
@Schema(description = "项目编码")
private String deptNo;
/**
* 资料类型
*/
@ExcelAttribute(name = "资料类型")
@ExcelProperty(value = "资料类型", index = 4)
@Schema(description = "资料类型(存量、新签、续签、离职)")
private String materialType;
/**
* 处理结果
*/
@ExcelAttribute(name = "处理结果")
@ExcelProperty(value = "处理结果", index = 5)
@Schema(description = "处理结果(成功、失败、跳过)")
private String processResult;
/**
* 失败原因
*/
@ExcelAttribute(name = "失败原因")
@ExcelProperty(value = "失败原因", index = 6)
@Schema(description = "失败原因")
private String failReason;
}
package com.yifu.cloud.plus.v1.batch.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.ExcelAttribute;
import com.yifu.cloud.plus.v1.yifu.common.core.vo.RowIndex;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
/**
* 电子档案导入Excel模板VO
*
* @author huych
* @date 2025-12-17
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "电子档案导入Excel模板VO")
public class ArchiveImportVO extends RowIndex implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 身份证号码
*/
@ExcelAttribute(name = "身份证号码", isNotEmpty = true, errorInfo = "身份证号码不能为空", maxLength = 18)
@ExcelProperty(value = "身份证号码", index = 0)
@NotBlank(message = "身份证号码不能为空")
@Schema(description = "身份证号码")
private String empIdcard;
/**
* 姓名
*/
@ExcelAttribute(name = "姓名", isNotEmpty = true, errorInfo = "姓名不能为空", maxLength = 32)
@ExcelProperty(value = "姓名", index = 1)
@NotBlank(message = "姓名不能为空")
@Schema(description = "姓名")
private String empName;
/**
* 项目编码
*/
@ExcelAttribute(name = "项目编码", isNotEmpty = true, errorInfo = "项目编码不能为空", maxLength = 50)
@ExcelProperty(value = "项目编码", index = 2)
@NotBlank(message = "项目编码不能为空")
@Schema(description = "项目编码")
private String deptNo;
/**
* 项目名称
*/
@ExcelAttribute(name = "项目名称", isNotEmpty = true, errorInfo = "项目名称不能为空", maxLength = 50)
@ExcelProperty(value = "项目名称", index = 3)
@NotBlank(message = "项目名称不能为空")
@Schema(description = "项目名称")
private String deptName;
}
package com.yifu.cloud.plus.v1.batch.vo;
import com.yifu.cloud.plus.v1.batch.entity.TElectronicArchiveDetail;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
/**
* 电子档案明细表
*
* @author huych
* @date 2025-12-17 11:03:37
*/
@Data
public class TElectronicArchiveDetailSearchVo extends TElectronicArchiveDetail {
/**
* 多选导出或删除等操作
*/
@Schema(description = "选中ID,多个逗号分割")
private String ids;
/**
* 创建时间区间 [开始时间,结束时间]
*/
@Schema(description = "创建时间区间")
private LocalDateTime[] createTimes;
/**
* @Author fxj
* 查询数据起
**/
@Schema(description = "查询limit 开始")
private int limitStart;
/**
* @Author fxj
* 查询数据止
**/
@Schema(description = "查询limit 数据条数")
private int limitEnd;
@Schema(description = "资料类型数组")
private List<String> typeList;
}
package com.yifu.cloud.plus.v1.batch.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.ExcelAttribute;
import com.yifu.cloud.plus.v1.yifu.common.core.vo.RowIndex;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import java.io.Serializable;
/**
* 电子档案明细表
*
* @author huych
* @date 2025-12-17 11:03:37
*/
@Data
public class TElectronicArchiveDetailVo extends RowIndex implements Serializable {
@ExcelAttribute(name = "主键")
@Schema(description = "主键")
@ExcelProperty("主键")
private String id;
@ExcelAttribute(name = "姓名")
@Schema(description = "姓名")
@ExcelProperty("姓名")
private String empName;
@ExcelAttribute(name = "身份证号码")
@Schema(description = "身份证号码")
@ExcelProperty("身份证号码")
private String empIdcard;
@ExcelAttribute(name = "项目编码")
@Schema(description = "项目编码")
@ExcelProperty("项目编码")
private String deptNo;
/**
* 项目名称
*/
@ExcelAttribute(name = "项目名称")
@Schema(description = "项目名称")
@ExcelProperty("项目名称")
private String deptName;
/**
* 客户名称
*/
@ExcelAttribute(name = "客户名称")
@Schema(description = "客户名称")
@ExcelProperty("客户名称")
private String customerName;
/**
* 客户编码
*/
@ExcelAttribute(name = "客户编码")
@Schema(description = "客户编码")
@ExcelProperty("客户编码")
private String customerCode;
/**
* 资料类型(存量、新签、续签、离职,多个用逗号分隔)
*/
@ExcelAttribute(name = "资料类型")
@Schema(description = "资料类型(存量、新签、续签、离职,多个用逗号分隔)")
@ExcelProperty("资料类型")
private String materialType;
/**
* 档案所在地-省
*/
@ExcelAttribute(name = "档案所在地-省")
@Schema(description = "档案所在地-省")
@ExcelProperty("档案所在地-省")
private Integer fileProvince;
/**
* 档案所在地-市
*/
@ExcelAttribute(name = "档案所在地-市")
@Schema(description = "档案所在地-市")
@ExcelProperty("档案所在地-市")
private Integer fileCity;
/**
* 档案所在地-县
*/
@ExcelAttribute(name = "档案所在地-县")
@Schema(description = "档案所在地-县")
@ExcelProperty("档案所在地-县")
private Integer fileTown;
/**
* 状态(0不可用,1可用)
*/
@Length(max = 10, message = "状态(0不可用,1可用) 不能超过10 个字符")
@ExcelAttribute(name = "状态(0不可用,1可用)", maxLength = 10)
@Schema(description = "状态(0不可用,1可用)")
@ExcelProperty("状态(0不可用,1可用)")
private String status;
}
package com.yifu.cloud.plus.v1.batch.vo;
import com.yifu.cloud.plus.v1.batch.entity.TElectronicArchiveTask;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
/**
* 电子档案任务表
*
* @author huych
* @date 2025-12-17 11:03:07
*/
@Data
public class TElectronicArchiveTaskSearchVo extends TElectronicArchiveTask {
/**
* 多选导出或删除等操作
*/
@Schema(description = "选中ID,多个逗号分割")
private String ids;
/**
* 创建时间区间 [开始时间,结束时间]
*/
@Schema(description = "创建时间起")
private LocalDateTime createTimeStart;
/**
* 创建时间区间 [开始时间,结束时间]
*/
@Schema(description = "创建时间止")
private LocalDateTime createTimeEnd;
/**
* @Author fxj
* 查询数据起
**/
@Schema(description = "查询limit 开始")
private int limitStart;
/**
* @Author fxj
* 查询数据止
**/
@Schema(description = "查询limit 数据条数")
private int limitEnd;
@Schema(description = "资料类型数组")
private List<String> typeList;
}
package com.yifu.cloud.plus.v1.batch.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.ExcelAttribute;
import com.yifu.cloud.plus.v1.yifu.common.core.vo.RowIndex;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* 电子档案任务表
*
* @author huych
* @date 2025-12-17 11:03:07
*/
@Data
public class TElectronicArchiveTaskVo extends RowIndex implements Serializable {
@ExcelAttribute(name = "主键")
@Schema(description = "主键")
@ExcelProperty("主键")
private String id;
@ExcelAttribute(name = "任务编号")
@Schema(description = "任务编号")
@ExcelProperty("任务编号")
private String taskCode;
@ExcelAttribute(name = "上传表格名称")
@Schema(description = "上传表格名称")
@ExcelProperty("上传表格名称")
private String uploadFileName;
@ExcelAttribute(name = "上传压缩包名称")
@Schema(description = "上传压缩包名称")
@ExcelProperty("上传压缩包名称")
private String uploadZipName;
@ExcelAttribute(name = "状态")
@Schema(description = "状态(未上传,生成电子档案中,已生成电子档案)")
@ExcelProperty("状态")
private String status;
}
package com.yifu.cloud.plus.v1.batch.vo;
import com.yifu.cloud.plus.v1.batch.entity.TAttaInfo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
/**
* 任务附件分组VO
* 用于按任务分组展示电子档案的附件信息
*
* @author huych
* @date 2025-12-17
*/
@Data
@Schema(description = "任务附件分组VO")
public class TaskAttachmentGroupVO {
/**
* 任务ID
*/
@Schema(description = "任务ID")
private String taskId;
/**
* 任务编号
*/
@Schema(description = "任务编号")
private String taskCode;
/**
* 上传人
*/
@Schema(description = "上传人")
private String createUser;
/**
* 上传时间
*/
@Schema(description = "上传时间")
private LocalDateTime createTime;
/**
* 资料类型及对应的附件列表
* key: 资料类型(存量、新签、续签、离职)
* value: 该资料类型下的附件列表
*/
@Schema(description = "资料类型及对应的附件列表")
private Map<String, List<TAttaInfo>> materialTypeAttachments;
}
......@@ -85,30 +85,19 @@
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.apache.shardingsphere</groupId>-->
<!-- <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>-->
<!-- <version>5.1.2</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.apache.shardingsphere</groupId>-->
<!-- <artifactId>sharding-transaction-base-seata-at</artifactId>-->
<!-- <version>4.1.1</version>-->
<!-- </dependency>-->
<dependency>
<groupId>com.yifu.cloud.plus.v1</groupId>
<artifactId>yifu-common-core</artifactId>
</dependency>
<dependency>
<groupId>com.yifu.cloud.plus.v1</groupId>
<artifactId>yifu-common-batch</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-cp</artifactId>
<version>4.7.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
</dependencies>
<build>
......
package com.yifu.cloud.plus.v1.batch.controller;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.yifu.cloud.plus.v1.batch.service.FileUploadService;
import com.yifu.cloud.plus.v1.batch.service.TAttaInfoService;
import com.yifu.cloud.plus.v1.batch.entity.TAttaInfo;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.CommonConstants;
import com.yifu.cloud.plus.v1.yifu.common.core.util.Common;
import com.yifu.cloud.plus.v1.yifu.common.core.util.OSSUtil;
import com.yifu.cloud.plus.v1.yifu.common.core.util.R;
import com.yifu.cloud.plus.v1.yifu.common.log.annotation.SysLog;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.net.URL;
import java.util.List;
/**
* @author fang
* 文件上传接口
*/
@RestController
@RequestMapping("/fileUpload")
@Tag(name = "文件上传接口")
@Slf4j
public class FileUploadController {
@Autowired
private OSSUtil ossUtil;
@Autowired
private TAttaInfoService tAttaInfoService;
@Autowired
private FileUploadService fileUploadService;
/**
* @Author fxj
* @Description
* @Date 13:49 2022/6/17
* @Param
* @return
**/
@Operation(description = "OSS文件上传接口" +
"@ApiImplicitParam(name = \"file\", value = \"Form文件上传\", required = true, dataType = \"__file\", paramType = \"form\"),\n" +
"\t\t\t@ApiImplicitParam(name = \"filePath\", value = \"文件上传路径\", required = false, dataType = \"String\", paramType = \"form\"),\n" +
"\t\t\t@ApiImplicitParam(name = \"type\", value = \"关系类型 1 任务表附件 2 zip附件 3反馈表附件\", required = true, dataType = \"int\", paramType = \"form\"),\n" +
"\t\t\t@ApiImplicitParam(name = \"domain\", value = \"实体id(传入就插入对应关系,用于编辑)\", required = false, paramType = \"form\")\n" +
"\t")
@PostMapping(value = "/ossUploadFile")
public R<T> uploadImg(@RequestBody MultipartFile file, String filePath, String type, String domain) throws IOException {
return fileUploadService.uploadAsso(file,filePath,type,domain, CommonConstants.ZERO_STRING);
}
/**
* @Author hgw
* @Description 改编自ossUploadFile接口,改变了回传信息——专为预入职微信端使用——忽略了token,其余人慎用
* @Date 13:49 2022/6/17
* @Param relationTypeSub 预入职其他附件的子类型
* @return
**/
@Operation(description = "OSS文件上传接口" +
"@ApiImplicitParam(name = \"file\", value = \"Form文件上传\", required = true, dataType = \"__file\", paramType = \"form\"),\n" +
"\t\t\t@ApiImplicitParam(name = \"filePath\", value = \"文件上传路径\", required = false, dataType = \"String\", paramType = \"form\"),\n" +
"\t\t\t@ApiImplicitParam(name = \"type\", value = \"关系类型 1 任务表附件 2 zip附件 3反馈表附件\", required = true, dataType = \"int\", paramType = \"form\"),\n" +
"\t\t\t@ApiImplicitParam(name = \"domain\", value = \"实体id(传入就插入对应关系,用于编辑)\", required = false, paramType = \"form\")\n" +
"\t")
@PostMapping(value = "/uploadFileReturnAtta")
public R<T> uploadFileReturnAtta(@RequestBody MultipartFile file, String filePath, String type, String domainId) throws IOException {
return fileUploadService.uploadAsso(file,filePath,type,domainId,CommonConstants.ONE_STRING);
}
/**
* @Author fxj
* @Description
* @Date 13:51 2022/6/17
* @Param
* @return
**/
@Operation(description = "附件删除")
@SysLog("附件删除")
@PostMapping("/ossFileDelete/deleteByIds")
public R<Boolean> ossFileDelete(@RequestBody List<String> Ids) {
if (Common.isNotNull(Ids)) {
try {
for (String id : Ids) {
TAttaInfo attaInfo = tAttaInfoService.getById(id);
if (null == attaInfo) {
return R.failed("无对应附件信息");
}
if (tAttaInfoService.removeById(id)) {
ossUtil.deleteObject(null, attaInfo.getAttaSrc());
return R.ok(null, "删除成功!");
}
}
} catch (Exception e) {
log.error("附件删除异常:" + e.getMessage());
return R.failed("附件删除异常!");
}
}
return R.ok(null,"删除失败!");
}
/**
* 获取附件下载地址
*
* @param id
* @return
* @Author fxj
* @Date 2019-08-16
* @param id domainId
**/
@Operation(description = "附件预览下载地址 资源类型 0 存量 1 新签 2续签 3离职")
@GetMapping("/ossFileUrl/{id}")
public R<List<TAttaInfo>> ossFileUrl(@PathVariable String id) {
List<TAttaInfo> attas = tAttaInfoService.list(Wrappers.<TAttaInfo>query().lambda().eq(TAttaInfo::getDomainId,id));
if (Common.isEmpty(attas)) {
return new R<>(attas);
}
for (TAttaInfo atta:attas){
URL url = ossUtil.getObjectUrl(null, atta.getAttaSrc());
atta.setAttaUrl(url.toString().replace("http","https"));
}
return new R<>(attas);
}
}
package com.yifu.cloud.plus.v1.batch.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yifu.cloud.plus.v1.batch.entity.TElectronicArchiveDetail;
import com.yifu.cloud.plus.v1.batch.entity.TElectronicArchiveTask;
import com.yifu.cloud.plus.v1.batch.service.ElectronicArchiveImportService;
import com.yifu.cloud.plus.v1.batch.service.TElectronicArchiveTaskService;
import com.yifu.cloud.plus.v1.batch.vo.TElectronicArchiveTaskSearchVo;
import com.yifu.cloud.plus.v1.yifu.common.core.util.R;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
/**
* 电子档案任务表
*
* @author huych
* @date 2025-12-17 11:03:07
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/telectronicarchivetask")
@Tag(name = "电子档案任务表管理")
public class TElectronicArchiveTaskController {
private final TElectronicArchiveTaskService tElectronicArchiveTaskService;
private final ElectronicArchiveImportService importService;
/**
* 简单分页查询
*
* @param page 分页对象
* @param tElectronicArchiveTask 电子档案任务表
* @return
*/
@Operation(description = "简单分页查询")
@GetMapping("/page")
public R<IPage<TElectronicArchiveTask>> getTElectronicArchiveTaskPage(Page<TElectronicArchiveTask> page, TElectronicArchiveTaskSearchVo tElectronicArchiveTask) {
return new R<>(tElectronicArchiveTaskService.getTElectronicArchiveTaskPage(page, tElectronicArchiveTask));
}
/**
* 通过id查询电子档案任务表
*
* @param id id
* @return R
*/
@Operation(summary = "通过id查询", description = "通过id查询")
@GetMapping("/{id}")
public R<TElectronicArchiveTask> getById(@PathVariable("id") String id) {
return R.ok(tElectronicArchiveTaskService.getById(id));
}
/**
* 通过id删除电子档案任务表
*
* @param id id
* @return R
*/
@Operation(summary = "通过id删除", description = "通过id删除")
@PostMapping("/deleteById")
public R<?> deleteById(@RequestParam String id) {
return tElectronicArchiveTaskService.deleteTaskById(id);
}
/**
* 第一步:导入Excel模板文件
*
* @param file Excel文件
* @return R
*/
@Operation(summary = "导入Excel模板文件", description = "第一步:导入Excel模板文件,包含身份证、姓名、项目编码、项目名称")
@PostMapping("/importExcel")
public R<?> importExcel(@Parameter(description = "Excel文件") @RequestParam("file") MultipartFile file) {
if (file == null || file.isEmpty()) {
return R.failed("文件不能为空");
}
String fileName = file.getOriginalFilename();
if (fileName == null || !fileName.toLowerCase().endsWith(".xlsx") && !fileName.toLowerCase().endsWith(".xls")) {
return R.failed("文件格式不正确,请上传Excel文件");
}
try {
return importService.importExcelTemplate(file.getInputStream(), fileName);
} catch (IOException e) {
return R.failed("文件读取失败:" + e.getMessage());
}
}
/**
* 第二步:导入ZIP文件
*
* @param file ZIP文件
* @param taskId 任务ID
* @return R
*/
@Operation(summary = "导入ZIP文件", description = "第二步:导入ZIP文件,包含员工档案资料")
@PostMapping("/importZip")
public R<?> importZip(
@Parameter(description = "ZIP文件") @RequestParam("file") MultipartFile file,
@Parameter(description = "任务ID") @RequestParam("taskId") String taskId) {
if (file == null || file.isEmpty()) {
return R.failed("文件不能为空");
}
String fileName = file.getOriginalFilename();
if (fileName == null || !fileName.toLowerCase().endsWith(".zip")) {
return R.failed("文件格式不正确,请上传ZIP文件");
}
return importService.importZipFile(file, taskId);
}
/**
* 查询任务状态
*
* @param taskId 任务ID
* @return R
*/
@Operation(summary = "查询任务状态", description = "查询电子档案导入任务的处理状态")
@GetMapping("/status/{taskId}")
public R<TElectronicArchiveTask> getTaskStatus(@PathVariable("taskId") String taskId) {
TElectronicArchiveTask task = tElectronicArchiveTaskService.getById(taskId);
if (task == null) {
return R.failed("任务不存在");
}
return R.ok(task);
}
/**
* 手动触发异步处理(用于重试)
*
* @param taskId 任务ID
* @return R
*/
@Operation(summary = "手动触发异步处理", description = "手动触发异步处理任务(用于重试失败的任务)")
@PostMapping("/retryProcess/{taskId}")
public R<?> retryProcess(@PathVariable("taskId") String taskId) {
TElectronicArchiveTask task = tElectronicArchiveTaskService.getById(taskId);
if (task == null) {
return R.failed("任务不存在");
}
// 只有生成中或失败的任务才能重试
// 这里可以根据需要增加失败状态
return importService.asyncProcessUpload(taskId);
}
/**
* 单个新增人员附件
*
* @param detail 人员信息
* @param files 附件文件列表
* @return R
*/
@Operation(summary = "单个新增人员附件", description = "单个新增人员档案附件,包含身份证号、姓名、项目编码、项目名称、资料类型和多个附件")
@PostMapping("/uploadSingleArchive")
public R<?> uploadSingleArchive(@RequestBody TElectronicArchiveDetail detail,
@RequestParam("files") MultipartFile[] files) {
// 参数校验
if (files == null || files.length == 0) {
return R.failed("附件不能为空");
}
return importService.uploadSinglePersonArchive(detail.getEmpIdcard(), detail.getEmpName()
, detail.getDeptNo(), detail.getDeptName(), detail.getMaterialType(),files);
}
}
package com.yifu.cloud.plus.v1.batch.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yifu.cloud.plus.v1.batch.entity.TTaskArchiveRelation;
import com.yifu.cloud.plus.v1.batch.service.TTaskArchiveRelationService;
import com.yifu.cloud.plus.v1.yifu.common.core.util.R;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 电子档案上传任务和档案明细关系表
*
* @author huych
* @date 2025-12-17 11:02:01
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/ttaskarchiverelation" )
@Tag(name = "电子档案上传任务和档案明细关系表管理")
public class TTaskArchiveRelationController {
private final TTaskArchiveRelationService tTaskArchiveRelationService;
/**
* 简单分页查询
* @param page 分页对象
* @param tTaskArchiveRelation 电子档案上传任务和档案明细关系表
* @return
*/
@Operation(description = "简单分页查询")
@GetMapping("/page")
public R<IPage<TTaskArchiveRelation>> getTTaskArchiveRelationPage(Page<TTaskArchiveRelation> page, TTaskArchiveRelation tTaskArchiveRelation) {
return new R<>(tTaskArchiveRelationService.getTTaskArchiveRelationPage(page,tTaskArchiveRelation));
}
/**
* 通过id查询电子档案上传任务和档案明细关系表
* @param id id
* @return R
*/
@Operation(summary = "通过id查询", description = "通过id查询")
@GetMapping("/{id}" )
public R<TTaskArchiveRelation> getById(@PathVariable("id" ) String id) {
return R.ok(tTaskArchiveRelationService.getById(id));
}
}
package com.yifu.cloud.plus.v1.batch.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yifu.cloud.plus.v1.batch.entity.SysConfigLimit;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* 系统限制配置表
*
* @author hgw
* @date 2024-09-24 17:39:57
*/
@Mapper
public interface SysConfigLimitMapper extends BaseMapper<SysConfigLimit> {
/**
* 系统限制配置表简单分页查询
*
* @param configKey 键
* @return
*/
Integer getSysConfigLimitByKey(@Param("configKey") String configKey);
}
package com.yifu.cloud.plus.v1.batch.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yifu.cloud.plus.v1.batch.entity.TAttaInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
/**
* 附件信息表
*
* @author pwang
* @date 2021-02-19 16:16:07
*/
@Mapper
public interface TAttaInfoMapper extends BaseMapper<TAttaInfo> {
/**
* 附件信息表简单分页查询
*
* @param tAttaInfo 附件信息表
* @return
*/
IPage<TAttaInfo> getTAttaInfoPage(Page<TAttaInfo> page, @Param("tAttaInfo") TAttaInfo tAttaInfo);
/**
* @param tAttaInfo
* @Description: 获取list
* @Author: hgw
* @Date: 2021/5/28 16:02
* @return: java.util.List<com.yifu.cloud.v1.crm.api.entity.TAttaInfo>
**/
List<TAttaInfo> getTAttaInfoList(@Param("tAttaInfo") TAttaInfo tAttaInfo);
List<TAttaInfo> getTAttaInfoListByDoMainId(@Param("domainId") String domainId);
int updateDomainId(@Param("domainId")String domainId, @Param("ids")List<String> ids);
List<TAttaInfo> getAttaByApplyId(@Param("applyId")String applyId);
void deleteByDomainId(@Param("domainId") String domainId);
void deleteByDomainIdAndType(@Param("domainId") String domainId, @Param("relationType") String relationType);
}
package com.yifu.cloud.plus.v1.batch.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yifu.cloud.plus.v1.batch.entity.TElectronicArchiveDetail;
import com.yifu.cloud.plus.v1.batch.vo.TElectronicArchiveDetailSearchVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* 电子档案明细表
*
* @author huych
* @date 2025-12-17 11:03:37
*/
@Mapper
public interface TElectronicArchiveDetailMapper extends BaseMapper<TElectronicArchiveDetail> {
/**
* 电子档案明细表简单分页查询
* @param tElectronicArchiveDetail 电子档案明细表
* @return
*/
IPage<TElectronicArchiveDetail> getTElectronicArchiveDetailPage(Page<TElectronicArchiveDetail> page, @Param("tElectronicArchiveDetail") TElectronicArchiveDetailSearchVo tElectronicArchiveDetail);
}
package com.yifu.cloud.plus.v1.batch.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yifu.cloud.plus.v1.batch.entity.TElectronicArchiveTask;
import com.yifu.cloud.plus.v1.batch.vo.TElectronicArchiveTaskSearchVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* 电子档案任务表
*
* @author huych
* @date 2025-12-17 11:03:07
*/
@Mapper
public interface TElectronicArchiveTaskMapper extends BaseMapper<TElectronicArchiveTask> {
/**
* 电子档案任务表简单分页查询
* @param tElectronicArchiveTask 电子档案任务表
* @return
*/
IPage<TElectronicArchiveTask> getTElectronicArchiveTaskPage(Page<TElectronicArchiveTask> page
, @Param("tElectronicArchiveTask") TElectronicArchiveTaskSearchVo tElectronicArchiveTask);
}
package com.yifu.cloud.plus.v1.batch.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yifu.cloud.plus.v1.batch.entity.TTaskArchiveRelation;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* 电子档案上传任务和档案明细关系表
*
* @author huych
* @date 2025-12-17 11:02:01
*/
@Mapper
public interface TTaskArchiveRelationMapper extends BaseMapper<TTaskArchiveRelation> {
/**
* 电子档案上传任务和档案明细关系表简单分页查询
* @param tTaskArchiveRelation 电子档案上传任务和档案明细关系表
* @return
*/
IPage<TTaskArchiveRelation> getTTaskArchiveRelationPage(Page<TTaskArchiveRelation> page, @Param("tTaskArchiveRelation") TTaskArchiveRelation tTaskArchiveRelation);
}
package com.yifu.cloud.plus.v1.batch.service;
import com.yifu.cloud.plus.v1.yifu.common.core.util.R;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
/**
* 电子档案导入服务接口
*
* @author huych
* @date 2025-12-17
*/
public interface ElectronicArchiveImportService {
/**
* 第一步:导入Excel模板文件
*
* @param inputStream Excel文件流
* @param fileName 文件名
* @return 导入结果
*/
R<?> importExcelTemplate(InputStream inputStream, String fileName);
/**
* 第二步:导入ZIP文件
*
* @param file ZIP文件
* @param taskId 任务ID
* @return 导入结果
*/
R<?> importZipFile(MultipartFile file, String taskId);
/**
* 第三步:异步处理上传
*
* @param taskId 任务ID
* @return 处理结果
*/
R<?> asyncProcessUpload(String taskId);
/**
* 生成任务编号
*
* @return 任务编号
*/
String generateTaskCode();
/**
* 单个新增人员附件
*
* @param empIdcard 身份证号
* @param empName 姓名
* @param deptNo 项目编码
* @param deptName 项目名称
* @param materialType 资料类型
* @param files 附件文件列表
* @return 上传结果
*/
R<?> uploadSinglePersonArchive(String empIdcard, String empName, String deptNo,
String deptName, String materialType, MultipartFile[] files);
}
package com.yifu.cloud.plus.v1.batch.service;
import com.yifu.cloud.plus.v1.batch.entity.TAttaInfo;
import com.yifu.cloud.plus.v1.yifu.common.core.util.R;
import com.yifu.cloud.plus.v1.yifu.common.core.vo.FileVo;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
/**
* 文件上传
*
* @author pwang
* @date 2022-07-27 16:16:07
*/
public interface FileUploadService {
R<FileVo> uploadImg(MultipartFile file, String filePath, String type, String domain) throws IOException;
R<TAttaInfo> uploadFileReturnAtta(MultipartFile file, String filePath, String type, String domainId) throws IOException;
R<T> uploadAsso(MultipartFile file, String filePath, String type, String domain, String uploadType);
/**
* 检查文件是否存在
*
* @param filePath 文件路径
* @return 是否存在
*/
boolean checkFileExists(String filePath);
}
package com.yifu.cloud.plus.v1.batch.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yifu.cloud.plus.v1.batch.entity.TAttaInfo;
import com.yifu.cloud.plus.v1.yifu.common.core.util.R;
import java.util.List;
/**
* 附件信息表
*
* @author pwang
* @date 2021-02-19 16:16:07
*/
public interface TAttaInfoService extends IService<TAttaInfo> {
/**
* @param domainId 关联id
* @Description: 根据domainId获取附件
* @Author: huych
* @Date: 2025/12/17 18:23
* @return: java.util.List<com.yifu.cloud.plus.v1.yifu.archives.entity.TAttaInfo>
**/
List<TAttaInfo> getTAttaInfoListByDoMainId(String domainId);
Boolean updateDomainId(String domainId, List<String> ids);
/**
* @param applyId
* @Description: 收入证明使用
* @Author: hgw
* @Date: 2023/2/22 10:50
* @return: com.yifu.cloud.plus.v1.yifu.common.core.util.R<java.util.List < com.yifu.cloud.plus.v1.yifu.archives.entity.TAttaInfo>>
**/
R<List<TAttaInfo>> getAttaByApplyId(String applyId);
/**
* @param domainId 关联id
* @Description: 删除附件
* @Author: hgw
* @Date: 2024/6/20 14:50
* @return: void
**/
void deleteByDomainId(String domainId);
TAttaInfo add(TAttaInfo attaInfo);
}
package com.yifu.cloud.plus.v1.batch.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yifu.cloud.plus.v1.batch.entity.TElectronicArchiveDetail;
import com.yifu.cloud.plus.v1.batch.vo.ArchiveAttachmentDeleteRequestVO;
import com.yifu.cloud.plus.v1.batch.vo.ArchiveBatchDownloadRequestVO;
import com.yifu.cloud.plus.v1.batch.vo.ArchiveSelectedExportRequestVO;
import com.yifu.cloud.plus.v1.batch.vo.TElectronicArchiveDetailSearchVo;
import com.yifu.cloud.plus.v1.yifu.common.core.util.R;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* 电子档案明细表
*
* @author huych
* @date 2025-12-17 11:03:37
*/
public interface TElectronicArchiveDetailService extends IService<TElectronicArchiveDetail> {
/**
* 电子档案明细表简单分页查询
* @param tElectronicArchiveDetail 电子档案明细表
* @return
*/
IPage<TElectronicArchiveDetail> getTElectronicArchiveDetailPage(Page<TElectronicArchiveDetail> page, TElectronicArchiveDetailSearchVo tElectronicArchiveDetail);
/**
* 通过id查询电子档案明细表
* @param id id
* @return
*/
R<TElectronicArchiveDetail> getDetailInfoById(String id);
/**
* 批量下载电子档案
* @param requestList 下载请求列表
* @param response HTTP响应
*/
void batchDownloadArchives(List<ArchiveBatchDownloadRequestVO> requestList, HttpServletResponse response);
/**
* 删除电子档案附件
* @param request 删除请求
* @return 删除结果
*/
R<String> deleteArchiveAttachments(ArchiveAttachmentDeleteRequestVO request);
/**
* 勾选导出电子档案
* @param requestList 导出请求列表
* @param response HTTP响应
*/
void selectedExportArchives(List<ArchiveSelectedExportRequestVO> requestList, HttpServletResponse response);
}
package com.yifu.cloud.plus.v1.batch.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yifu.cloud.plus.v1.batch.entity.TElectronicArchiveTask;
import com.yifu.cloud.plus.v1.batch.vo.TElectronicArchiveTaskSearchVo;
import com.yifu.cloud.plus.v1.yifu.common.core.util.R;
/**
* 电子档案任务表
*
* @author huych
* @date 2025-12-17 11:03:07
*/
public interface TElectronicArchiveTaskService extends IService<TElectronicArchiveTask> {
/**
* 电子档案任务表简单分页查询
* @param tElectronicArchiveTask 电子档案任务表
* @return
*/
IPage<TElectronicArchiveTask> getTElectronicArchiveTaskPage(Page<TElectronicArchiveTask> page, TElectronicArchiveTaskSearchVo tElectronicArchiveTask);
R deleteTaskById(String id);
}
package com.yifu.cloud.plus.v1.batch.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yifu.cloud.plus.v1.batch.entity.TTaskArchiveRelation;
/**
* 电子档案上传任务和档案明细关系表
*
* @author huych
* @date 2025-12-17 11:02:01
*/
public interface TTaskArchiveRelationService extends IService<TTaskArchiveRelation> {
/**
* 电子档案上传任务和档案明细关系表简单分页查询
* @param tTaskArchiveRelation 电子档案上传任务和档案明细关系表
* @return
*/
IPage<TTaskArchiveRelation> getTTaskArchiveRelationPage(Page<TTaskArchiveRelation> page, TTaskArchiveRelation tTaskArchiveRelation);
}
package com.yifu.cloud.plus.v1.batch.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.read.metadata.holder.ReadRowHolder;
import com.alibaba.excel.util.ListUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yifu.cloud.plus.v1.batch.constants.ArchiveConstants;
import com.yifu.cloud.plus.v1.batch.entity.TAttaInfo;
import com.yifu.cloud.plus.v1.batch.entity.TElectronicArchiveDetail;
import com.yifu.cloud.plus.v1.batch.entity.TElectronicArchiveTask;
import com.yifu.cloud.plus.v1.batch.entity.TTaskArchiveRelation;
import com.yifu.cloud.plus.v1.batch.service.*;
import com.yifu.cloud.plus.v1.batch.vo.ArchiveFeedbackVO;
import com.yifu.cloud.plus.v1.batch.vo.ArchiveImportVO;
import com.yifu.cloud.plus.v1.yifu.archives.entity.TEmployeeInfo;
import com.yifu.cloud.plus.v1.yifu.archives.vo.IdNameNoVo;
import com.yifu.cloud.plus.v1.yifu.archives.vo.TSettleDomainSelectVo;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.CacheConstants;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.CommonConstants;
import com.yifu.cloud.plus.v1.yifu.common.core.util.*;
import com.yifu.cloud.plus.v1.yifu.common.core.vo.YifuUser;
import com.yifu.cloud.plus.v1.yifu.common.dapr.util.ArchivesDaprUtil;
import com.yifu.cloud.plus.v1.yifu.common.security.util.SecurityUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;
/**
* 电子档案导入服务实现
*
* @author huych
* @date 2025-12-17
*/
@Log4j2
@Service
@RequiredArgsConstructor
public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImportService {
private final TElectronicArchiveTaskService taskService;
private final TElectronicArchiveDetailService detailService;
private final TTaskArchiveRelationService relationService;
private final TAttaInfoService attaInfoService;
private final FileUploadService fileUploadService;
private final ArchivesDaprUtil archivesDaprUtil;
private final CacheManager cacheManager;
/**
* 第一步:导入Excel模板文件
*/
@Override
public R<?> importExcelTemplate(InputStream inputStream, String fileName) {
YifuUser user = SecurityUtils.getUser();
if (null == user) {
return R.failed("用户未登录");
}
// 成功导入的数据
List<ArchiveImportVO> successList = new ArrayList<>();
ExcelUtil<ArchiveImportVO> util = new ExcelUtil<>(ArchiveImportVO.class);
List<ErrorMessage<ArchiveImportVO>> errorMessageList = new ArrayList<>();
try {
EasyExcel.read(inputStream, ArchiveImportVO.class, new ReadListener<ArchiveImportVO>() {
public static final int BATCH_COUNT = CommonConstants.BATCH_COUNT;
private List<ArchiveImportVO> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
@Override
public void invoke(ArchiveImportVO data, AnalysisContext context) {
ReadRowHolder readRowHolder = context.readRowHolder();
Integer rowIndex = readRowHolder.getRowIndex();
data.setRowIndex(rowIndex + 1);
ErrorMessage<ArchiveImportVO> errorMessage = util.checkEntity(data, data.getRowIndex());
if (Common.isNotNull(errorMessage)) {
errorMessageList.add(errorMessage);
} else {
cachedDataList.add(data);
}
if (cachedDataList.size() >= BATCH_COUNT) {
saveData();
cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
saveData();
}
private void saveData() {
if (CollUtil.isNotEmpty(cachedDataList)) {
processImportData(cachedDataList, errorMessageList, successList, user);
}
}
}).sheet().doRead();
// 如果校验失败,返回错误信息
if (CollUtil.isNotEmpty(errorMessageList)) {
return R.failed(errorMessageList);
}
// 校验通过,生成任务表数据
if (CollUtil.isEmpty(successList)) {
return R.failed("没有有效的数据可以导入");
}
// 生成任务编号
String taskCode = generateTaskCode();
// 创建任务
TElectronicArchiveTask task = new TElectronicArchiveTask();
task.setTaskCode(taskCode);
task.setUploadFileName(fileName);
task.setStatus(ArchiveConstants.TASK_STATUS_NOT_UPLOADED);
task.setDeleteFlag(CommonConstants.ZERO_STRING);
taskService.save(task);
// 保存任务表附件
saveTaskAttachment(task.getId(), fileName, inputStream, user);
// 建立任务和档案详情的关联关系
createTaskArchiveRelations(task.getId(), successList);
return R.ok("导入成功,任务编号:" + taskCode);
} catch (Exception e) {
log.error("导入Excel文件失败", e);
return R.failed("导入失败:" + e.getMessage());
}
}
/**
* 处理导入数据
*/
private void processImportData(List<ArchiveImportVO> dataList, List<ErrorMessage<ArchiveImportVO>> errorMessageList,
List<ArchiveImportVO> successList, YifuUser user) {
TEmployeeInfo empInfo;
TSettleDomainSelectVo settleResult;
R<TEmployeeInfo> empResult;
Map<String,String> map = new HashMap<>();
for (ArchiveImportVO data : dataList) {
try {
// 1. 校验表格中一个姓名、身份证只能有一条
String uniqueKey = data.getEmpIdcard() + "_" + data.getEmpName() + "_" + data.getDeptNo();
if (null != map.get(uniqueKey)) {
ErrorMessage<ArchiveImportVO> errorMessage = new ErrorMessage<>(data.getRowIndex()
,"第" + data.getRowIndex() + "行:姓名和身份证信息存在重复数据",data);
errorMessageList.add(errorMessage);
continue;
} else {
map.put(uniqueKey, "1");
}
// 查询项目信息获取客户信息
settleResult = archivesDaprUtil.selectDeptByNo(data.getDeptNo());
if (settleResult == null) {
ErrorMessage<ArchiveImportVO> errorMessage = new ErrorMessage<>(data.getRowIndex()
,"第" + data.getRowIndex() + "行:项目编码[" + data.getDeptNo() + "]信息不存在",data);
errorMessageList.add(errorMessage);
continue;
}
// 查询人员档案信息获取档案所在地
IdNameNoVo vo = new IdNameNoVo();
vo.setName(data.getEmpName());
vo.setNo(data.getEmpIdcard());
empResult = archivesDaprUtil.getTEmployeeInfoByIdcardAndName(vo);
if (empResult == null || empResult.getData() == null) {
ErrorMessage<ArchiveImportVO> errorMessage = new ErrorMessage<>(data.getRowIndex()
,"第" + data.getRowIndex() + "行:人员档案信息不存在",data);
errorMessageList.add(errorMessage);
continue;
}
empInfo = empResult.getData();
// 查找是否存在相同的电子档案明细
LambdaQueryWrapper<TElectronicArchiveDetail> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(TElectronicArchiveDetail::getEmpIdcard, data.getEmpIdcard())
.eq(TElectronicArchiveDetail::getEmpName, data.getEmpName())
.eq(TElectronicArchiveDetail::getDeptNo, data.getDeptNo())
.eq(TElectronicArchiveDetail::getDeleteFlag, CommonConstants.ZERO_STRING)
.last(CommonConstants.LAST_ONE_SQL);
TElectronicArchiveDetail existDetail = detailService.getOne(wrapper);
if (existDetail == null) {
// 不存在,生成新的明细数据
TElectronicArchiveDetail detail = new TElectronicArchiveDetail();
detail.setEmpName(data.getEmpName());
detail.setEmpIdcard(data.getEmpIdcard());
detail.setDeptNo(data.getDeptNo());
detail.setDeptName(data.getDeptName());
detail.setCustomerName(settleResult.getCustomerName());
detail.setCustomerCode(settleResult.getCustomerCode());
detail.setMaterialType(""); // 初始为空,第三步处理时填充
detail.setStatus(ArchiveConstants.DETAIL_STATUS_DISABLED);
detail.setDeleteFlag(CommonConstants.ZERO_STRING);
// 设置档案所在地(需要从empInfo中获取)
detail.setFileProvince(empInfo.getFileProvince());
detail.setFileCity(empInfo.getFileCity());
detail.setFileTown(empInfo.getFileTown());
detailService.save(detail);
}
successList.add(data);
} catch (Exception e) {
log.error("处理第" + data.getRowIndex() + "行数据失败", e);
}
}
}
/**
* 保存任务表附件
*/
private void saveTaskAttachment(String taskId, String fileName, InputStream inputStream, YifuUser user) {
try {
TAttaInfo attaInfo = new TAttaInfo();
attaInfo.setAttaName(fileName);
attaInfo.setRelationType(ArchiveConstants.ATTA_RELATION_TYPE_TASK);
attaInfo.setDomainId(taskId);
attaInfo.setStatus(ArchiveConstants.ATTA_STATUS_ENABLED);
attaInfoService.save(attaInfo);
} catch (Exception e) {
log.error("保存任务表附件失败", e);
}
}
/**
* 建立任务和档案详情的关联关系
*/
private void createTaskArchiveRelations(String taskId, List<ArchiveImportVO> successList) {
try {
// 根据姓名、身份证、项目编码去重
Map<String, ArchiveImportVO> uniqueMap = new HashMap<>();
for (ArchiveImportVO data : successList) {
String uniqueKey = data.getEmpIdcard() + "_" + data.getEmpName() + "_" + data.getDeptNo();
uniqueMap.put(uniqueKey, data);
}
// 为每个唯一的人员创建关联关系
for (ArchiveImportVO data : uniqueMap.values()) {
// 查找对应的电子档案明细
LambdaQueryWrapper<TElectronicArchiveDetail> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(TElectronicArchiveDetail::getEmpIdcard, data.getEmpIdcard())
.eq(TElectronicArchiveDetail::getEmpName, data.getEmpName())
.eq(TElectronicArchiveDetail::getDeptNo, data.getDeptNo())
.eq(TElectronicArchiveDetail::getDeleteFlag, CommonConstants.ZERO_STRING)
.last(CommonConstants.LAST_ONE_SQL);
TElectronicArchiveDetail detail = detailService.getOne(wrapper);
if (detail != null) {
// 创建关联记录
TTaskArchiveRelation relation = new TTaskArchiveRelation();
relation.setTaskId(taskId);
relation.setArchiveDetailId(detail.getId());
relationService.save(relation);
}
}
log.info("任务关联关系创建成功,任务ID:{},关联数量:{}", taskId, uniqueMap.size());
} catch (Exception e) {
log.error("创建任务关联关系失败,任务ID:{}", taskId, e);
}
}
/**
* 第二步:导入ZIP文件
*/
@Override
@Transactional(rollbackFor = Exception.class)
public R<?> importZipFile(MultipartFile file, String taskId) {
YifuUser user = SecurityUtils.getUser();
if (null == user) {
return R.failed("用户未登录");
}
// 查询任务
TElectronicArchiveTask task = taskService.getById(taskId);
if (task == null) {
return R.failed("任务不存在");
}
if (!ArchiveConstants.TASK_STATUS_NOT_UPLOADED.equals(task.getStatus())) {
return R.failed("任务状态不正确,无法上传ZIP文件");
}
File tempZipFile = null;
try {
// 1. 校验ZIP大小
if (file.getSize() > ArchiveConstants.ZIP_MAX_SIZE) {
return R.failed("ZIP文件大小不能超过500M");
}
// 2. 校验ZIP不为空
if (file.isEmpty()) {
return R.failed("ZIP文件不能为空");
}
// 3. 校验同时上传任务数
long runningTaskCount = taskService.count(new LambdaQueryWrapper<TElectronicArchiveTask>()
.eq(TElectronicArchiveTask::getStatus, ArchiveConstants.TASK_STATUS_GENERATING));
if (runningTaskCount >= ArchiveConstants.MAX_CONCURRENT_TASKS) {
return R.failed("有其他客服正在上传中,请5分钟后重试");
}
// 更新任务信息
task.setUploadZipName(file.getOriginalFilename());
task.setStatus(ArchiveConstants.TASK_STATUS_GENERATING);
taskService.updateById(task);
// 将ZIP文件保存到临时目录
String tempDirPath = System.getProperty("java.io.tmpdir") + File.separator + "zip_upload_" + taskId;
File tempDir = new File(tempDirPath);
if (!tempDir.exists()) {
tempDir.mkdirs();
}
tempZipFile = new File(tempDir, file.getOriginalFilename());
file.transferTo(tempZipFile);
// 保存ZIP附件信息(记录临时文件路径)
TAttaInfo zipAtta = new TAttaInfo();
zipAtta.setAttaName(file.getOriginalFilename());
zipAtta.setAttaSize(file.getSize());
zipAtta.setRelationType(ArchiveConstants.ATTA_RELATION_TYPE_ZIP);
zipAtta.setDomainId(taskId);
zipAtta.setStatus(ArchiveConstants.ATTA_STATUS_DISABLED);
zipAtta.setAttaSrc(tempZipFile.getAbsolutePath()); // 临时存储文件路径
attaInfoService.save(zipAtta);
// 调用异步处理
asyncProcessUpload(taskId);
return R.ok("ZIP文件提交成功,正在处理中");
} catch (Exception e) {
log.error("导入ZIP文件失败", e);
// 清理临时文件
if (tempZipFile != null && tempZipFile.exists()) {
FileUtil.del(tempZipFile);
}
// 回滚任务状态
task.setStatus(ArchiveConstants.TASK_STATUS_NOT_UPLOADED);
taskService.updateById(task);
return R.failed("导入ZIP文件失败:" + e.getMessage());
}
}
/**
* 第三步:异步处理上传
*/
@Override
@Async
public R<?> asyncProcessUpload(String taskId) {
log.info("开始异步处理任务:{}", taskId);
List<ArchiveFeedbackVO> feedbackList = new ArrayList<>();
File tempDir = null;
File zipSourceFile = null;
try {
// 查询任务及关联的明细
TElectronicArchiveTask task = taskService.getById(taskId);
if (task == null) {
return R.failed("任务不存在");
}
// 查询ZIP附件(从附件导入中获取)
List<TAttaInfo> zipAttas = attaInfoService.list(new LambdaQueryWrapper<TAttaInfo>()
.eq(TAttaInfo::getDomainId, taskId)
.eq(TAttaInfo::getRelationType, ArchiveConstants.ATTA_RELATION_TYPE_ZIP));
if (CollUtil.isEmpty(zipAttas)) {
log.error("ZIP附件不存在,任务ID:{}", taskId);
updateTaskStatus(task, ArchiveConstants.TASK_STATUS_NOT_UPLOADED);
return R.failed("ZIP附件不存在");
}
TAttaInfo zipAtta = zipAttas.get(0);
// 从附件记录中获取ZIP文件路径
if (StrUtil.isBlank(zipAtta.getAttaSrc())) {
log.error("ZIP文件路径为空,任务ID:{}", taskId);
updateTaskStatus(task, ArchiveConstants.TASK_STATUS_NOT_UPLOADED);
return R.failed("ZIP文件路径为空");
}
zipSourceFile = new File(zipAtta.getAttaSrc());
if (!zipSourceFile.exists()) {
log.error("ZIP文件不存在:{},任务ID:{}", zipAtta.getAttaSrc(), taskId);
updateTaskStatus(task, ArchiveConstants.TASK_STATUS_NOT_UPLOADED);
return R.failed("ZIP文件不存在");
}
// 获取任务关联的明细
List<TTaskArchiveRelation> relations = relationService.list(new LambdaQueryWrapper<TTaskArchiveRelation>()
.eq(TTaskArchiveRelation::getTaskId, taskId));
if (CollUtil.isEmpty(relations)) {
log.error("任务没有关联的明细数据,任务ID:{}", taskId);
updateTaskStatus(task, ArchiveConstants.TASK_STATUS_NOT_UPLOADED);
return R.failed("任务没有关联的明细数据");
}
List<String> detailIds = relations.stream()
.map(TTaskArchiveRelation::getArchiveDetailId)
.collect(Collectors.toList());
List<TElectronicArchiveDetail> details = detailService.listByIds(detailIds);
Map<String, TElectronicArchiveDetail> detailMap = details.stream()
.collect(Collectors.toMap(
d -> d.getEmpIdcard() + "_" + d.getEmpName(),
d -> d
));
// 创建临时目录用于解压ZIP文件
String tempDirPath = System.getProperty("java.io.tmpdir") + File.separator + "archive_" + taskId + "_" + System.currentTimeMillis();
tempDir = new File(tempDirPath);
if (!tempDir.mkdirs()) {
log.error("创建临时目录失败:{}", tempDirPath);
updateTaskStatus(task, ArchiveConstants.TASK_STATUS_NOT_UPLOADED);
return R.failed("创建临时目录失败");
}
// 解压ZIP文件
try {
log.info("开始解压ZIP文件:{},任务ID:{}", zipSourceFile.getAbsolutePath(), taskId);
cn.hutool.core.util.ZipUtil.unzip(zipSourceFile, tempDir);
log.info("ZIP文件解压成功,任务ID:{}", taskId);
} catch (Exception e) {
log.error("解压ZIP文件失败,任务ID:{}", taskId, e);
updateTaskStatus(task, ArchiveConstants.TASK_STATUS_NOT_UPLOADED);
return R.failed("解压ZIP文件失败:" + e.getMessage());
}
// 获取ZIP包的根目录(第一级目录)
File[] zipRootDirs = tempDir.listFiles(File::isDirectory);
if (zipRootDirs == null || zipRootDirs.length == 0) {
log.error("ZIP文件中没有找到目录,任务ID:{}", taskId);
updateTaskStatus(task, ArchiveConstants.TASK_STATUS_NOT_UPLOADED);
return R.failed("ZIP文件格式不正确");
}
File zipRootDir = zipRootDirs[0]; // 第一级目录
// 处理第二级目录(身份证+姓名目录)
File[] personDirs = zipRootDir.listFiles(File::isDirectory);
if (personDirs == null || personDirs.length == 0) {
log.error("ZIP文件中没有找到人员目录,任务ID:{}", taskId);
updateTaskStatus(task, ArchiveConstants.TASK_STATUS_NOT_UPLOADED);
return R.failed("ZIP文件格式不正确");
}
// 遍历每个人员目录,递归处理所有附件
for (File personDir : personDirs) {
processPersonDirectory(personDir, detailMap, feedbackList, taskId);
}
// 处理完成,更新任务状态
task.setStatus(ArchiveConstants.TASK_STATUS_GENERATED);
taskService.updateById(task);
// 生成反馈表
generateFeedbackExcel(taskId, feedbackList);
log.info("任务处理完成,任务ID:{}", taskId);
return R.ok("处理完成");
} catch (Exception e) {
log.error("异步处理任务失败,任务ID:{}", taskId, e);
// 更新任务状态为失败
TElectronicArchiveTask task = taskService.getById(taskId);
if (task != null) {
updateTaskStatus(task, ArchiveConstants.TASK_STATUS_NOT_UPLOADED);
}
return R.failed("处理失败:" + e.getMessage());
} finally {
// 清理临时解压目录
if (tempDir != null && tempDir.exists()) {
try {
FileUtil.del(tempDir);
log.info("清理临时解压目录成功:{}", tempDir.getAbsolutePath());
} catch (Exception e) {
log.error("清理临时解压目录失败:{}", tempDir.getAbsolutePath(), e);
}
}
// 清理上传的ZIP文件
if (zipSourceFile != null && zipSourceFile.exists()) {
try {
FileUtil.del(zipSourceFile.getParentFile());
log.info("清理ZIP源文件成功:{}", zipSourceFile.getAbsolutePath());
} catch (Exception e) {
log.error("清理ZIP源文件失败:{}", zipSourceFile.getAbsolutePath(), e);
}
}
}
}
/**
* 更新任务状态
*/
private void updateTaskStatus(TElectronicArchiveTask task, String status) {
task.setStatus(status);
taskService.updateById(task);
}
/**
* 生成任务编号
*/
@Override
public synchronized String generateTaskCode() {
String nowDay = DateUtil.getThisDay();
Cache cache = cacheManager.getCache(CacheConstants.ARCHIVE_TASK_CODE);
int nowNums = 1;
if (cache != null) {
Object nowNumObj = cache.get(nowDay);
if (Common.isNotNull(nowNumObj)) {
SimpleValueWrapper value = (SimpleValueWrapper) nowNumObj;
nowNums = (Integer) value.get() + 1;
} else {
// 从数据库查询当天最大编号
LambdaQueryWrapper<TElectronicArchiveTask> wrapper = new LambdaQueryWrapper<>();
wrapper.likeRight(TElectronicArchiveTask::getTaskCode, ArchiveConstants.TASK_CODE_PREFIX + nowDay)
.orderByDesc(TElectronicArchiveTask::getTaskCode)
.last("LIMIT 1");
TElectronicArchiveTask lastTask = taskService.getOne(wrapper);
if (lastTask != null && StrUtil.isNotBlank(lastTask.getTaskCode())) {
try {
String codeNum = lastTask.getTaskCode().substring(lastTask.getTaskCode().length() - 3);
nowNums = Integer.parseInt(codeNum) + 1;
} catch (Exception e) {
log.error("解析任务编号失败", e);
nowNums = 1;
}
}
}
cache.put(nowDay, nowNums);
}
return ArchiveConstants.TASK_CODE_PREFIX + nowDay + String.format("%03d", nowNums);
}
/**
* 处理单个人员目录
*/
private void processPersonDirectory(File personDir, Map<String, TElectronicArchiveDetail> detailMap,
List<ArchiveFeedbackVO> feedbackList, String taskId) {
String dirName = personDir.getName();
log.info("处理人员目录:{}", dirName);
// 解析身份证和姓名
Map<String, String> idNameMap = parseIdCardAndName(dirName);
if (idNameMap.isEmpty() || !idNameMap.containsKey("idCard") || !idNameMap.containsKey("name")) {
// 目录格式不正确
ArchiveFeedbackVO feedback = new ArchiveFeedbackVO();
feedback.setEmpName(dirName);
feedback.setEmpIdcard("");
feedback.setProcessResult(ArchiveConstants.PROCESS_RESULT_SKIP);
feedback.setFailReason("目录名称格式不正确,无法解析身份证和姓名");
feedbackList.add(feedback);
return;
}
String idCard = idNameMap.get("idCard");
String name = idNameMap.get("name");
String uniqueKey = idCard + "_" + name;
// 检查是否在第一步导入的数据中
TElectronicArchiveDetail detail = detailMap.get(uniqueKey);
if (detail == null) {
// 不在第一步的文档中
ArchiveFeedbackVO feedback = new ArchiveFeedbackVO();
feedback.setEmpName(name);
feedback.setEmpIdcard(idCard);
feedback.setDeptNo("");
feedback.setDeptName("");
feedback.setProcessResult(ArchiveConstants.PROCESS_RESULT_SKIP);
feedback.setFailReason("压缩包中未找到对应文件夹,跳过处理");
feedbackList.add(feedback);
return;
}
// 处理第三级目录(资料类型或直接是文件)
File[] subFiles = personDir.listFiles();
if (subFiles == null || subFiles.length == 0) {
// 文件夹为空
ArchiveFeedbackVO feedback = createFeedbackVO(detail, "", ArchiveConstants.PROCESS_RESULT_FAILED, "文件夹为空");
feedbackList.add(feedback);
return;
}
// 用于记录该人员的所有资料类型
Set<String> materialTypes = new HashSet<>();
// 遍历第三级,递归处理所有文件
for (File subFile : subFiles) {
if (subFile.isDirectory()) {
// 是资料类型目录
String materialType = subFile.getName();
if (!isValidMaterialType(materialType)) {
// 不是有效的资料类型,跳过
continue;
}
// 递归处理资料类型目录下的所有文件(包括子目录)
processDirectoryRecursively(subFile, detail, materialType, feedbackList, materialTypes);
} else {
// 直接是文件,默认为存量类型
processFile(subFile, detail, ArchiveConstants.MATERIAL_TYPE_STOCK, feedbackList, materialTypes);
}
}
// 更新明细表的资料类型和状态
if (!materialTypes.isEmpty()) {
updateDetailMaterialType(detail, materialTypes);
}
}
/**
* 递归处理目录下的所有文件(包括子目录)
*/
private void processDirectoryRecursively(File directory, TElectronicArchiveDetail detail,
String materialType, List<ArchiveFeedbackVO> feedbackList,
Set<String> materialTypes) {
File[] files = directory.listFiles();
if (files == null || files.length == 0) {
// 文件夹为空
ArchiveFeedbackVO feedback = createFeedbackVO(detail, materialType,
ArchiveConstants.PROCESS_RESULT_FAILED, "文件夹为空");
feedbackList.add(feedback);
return;
}
// 递归处理该目录下的所有文件和子目录
for (File file : files) {
if (file.isDirectory()) {
// 如果是子目录,递归处理
processDirectoryRecursively(file, detail, materialType, feedbackList, materialTypes);
} else if (file.isFile()) {
// 如果是文件,上传到阿里云并生成TAttaInfo记录
processFile(file, detail, materialType, feedbackList, materialTypes);
}
}
}
/**
* 处理单个文件(上传到阿里云OSS,每个文件生成一条TAttaInfo记录)
*/
private void processFile(File file, TElectronicArchiveDetail detail, String materialType,
List<ArchiveFeedbackVO> feedbackList, Set<String> materialTypes) {
String fileName = file.getName();
long fileSize = file.length();
log.info("处理文件:{},大小:{},电子档案详情ID:{}", fileName, fileSize, detail.getId());
// 1. 校验文件大小
if (fileSize > ArchiveConstants.SINGLE_FILE_MAX_SIZE) {
ArchiveFeedbackVO feedback = createFeedbackVO(detail, materialType,
ArchiveConstants.PROCESS_RESULT_FAILED, "单个文件大小超过15M:" + fileName);
feedbackList.add(feedback);
return;
}
// 2. 校验文件格式
if (!checkFileFormat(fileName)) {
ArchiveFeedbackVO feedback = createFeedbackVO(detail, materialType,
ArchiveConstants.PROCESS_RESULT_FAILED, "文件格式不符,文件格式需为png\\jpg\\jpeg\\xlsx\\xls\\doc\\docx\\pdf\\ppt\\pptx\\txt:" + fileName);
feedbackList.add(feedback);
return;
}
// 3. 上传文件到阿里云OSS
try {
// 构建OSS目录路径
String ossDirectory = buildOssDirectory(detail, materialType);
// 转换为MultipartFile
MultipartFile multipartFile = fileToMultipartFile(file);
if (multipartFile == null) {
ArchiveFeedbackVO feedback = createFeedbackVO(detail, materialType,
ArchiveConstants.PROCESS_RESULT_FAILED, "文件转换失败:" + fileName);
feedbackList.add(feedback);
return;
}
// 上传文件,uploadFileReturnAtta会自动生成TAttaInfo记录
// domainId为电子档案详情表的id(detail.getId())
R<TAttaInfo> uploadResult = fileUploadService.uploadFileReturnAtta(
multipartFile,
ossDirectory,
getMaterialTypeCode(materialType),
detail.getId()
);
if (uploadResult == null || !R.isSuccess(uploadResult)) {
ArchiveFeedbackVO feedback = createFeedbackVO(detail, materialType,
ArchiveConstants.PROCESS_RESULT_FAILED,
"文件上传失败:" + fileName + ",原因:" + (uploadResult != null ? uploadResult.getMsg() : "未知错误"));
feedbackList.add(feedback);
return;
}
// 上传成功,记录附件信息
TAttaInfo attaInfo = uploadResult.getData();
log.info("文件上传成功:{},附件ID:{},domainId:{}", fileName, attaInfo.getId(), attaInfo.getDomainId());
// 记录该人员的资料类型
materialTypes.add(materialType);
// 生成成功反馈
ArchiveFeedbackVO feedback = createFeedbackVO(detail, materialType,
ArchiveConstants.PROCESS_RESULT_SUCCESS, "");
feedbackList.add(feedback);
} catch (Exception e) {
log.error("上传文件失败:{},电子档案详情ID:{}", fileName, detail.getId(), e);
ArchiveFeedbackVO feedback = createFeedbackVO(detail, materialType,
ArchiveConstants.PROCESS_RESULT_FAILED, "文件上传异常:" + fileName + ",原因:" + e.getMessage());
feedbackList.add(feedback);
}
}
/**
* 构建OSS目录路径
*/
private String buildOssDirectory(TElectronicArchiveDetail detail, String materialType) {
// 目录格式:项目编码-姓名-身份证号/资料类型
return detail.getDeptNo() + "-" + detail.getEmpName() + "-" + detail.getEmpIdcard() +
"/" + materialType;
}
/**
* 创建反馈VO
*/
private ArchiveFeedbackVO createFeedbackVO(TElectronicArchiveDetail detail, String materialType,
String processResult, String failReason) {
ArchiveFeedbackVO feedback = new ArchiveFeedbackVO();
feedback.setEmpName(detail.getEmpName());
feedback.setEmpIdcard(detail.getEmpIdcard());
feedback.setDeptNo(detail.getDeptNo());
feedback.setDeptName(detail.getDeptName());
feedback.setMaterialType(materialType);
feedback.setProcessResult(processResult);
feedback.setFailReason(failReason);
return feedback;
}
/**
* 判断是否是有效的资料类型
*/
private boolean isValidMaterialType(String typeName) {
return ArchiveConstants.MATERIAL_TYPE_STOCK.equals(typeName) ||
ArchiveConstants.MATERIAL_TYPE_NEW.equals(typeName) ||
ArchiveConstants.MATERIAL_TYPE_RENEW.equals(typeName) ||
ArchiveConstants.MATERIAL_TYPE_LEAVE.equals(typeName);
}
/**
* 更新明细表的资料类型和状态
*/
private void updateDetailMaterialType(TElectronicArchiveDetail detail, Set<String> newMaterialTypes) {
// 查找是否存在相同身份证姓名和项目编码的可用明细
LambdaQueryWrapper<TElectronicArchiveDetail> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(TElectronicArchiveDetail::getEmpIdcard, detail.getEmpIdcard())
.eq(TElectronicArchiveDetail::getEmpName, detail.getEmpName())
.eq(TElectronicArchiveDetail::getDeptNo, detail.getDeptNo())
.eq(TElectronicArchiveDetail::getStatus, ArchiveConstants.DETAIL_STATUS_ENABLED)
.eq(TElectronicArchiveDetail::getDeleteFlag, CommonConstants.ZERO_STRING)
.last(CommonConstants.LAST_ONE_SQL);
TElectronicArchiveDetail existDetail = detailService.getOne(wrapper);
if (existDetail != null) {
// 存在可用明细,合并资料类型
Set<String> existTypes = new HashSet<>();
if (StrUtil.isNotBlank(existDetail.getMaterialType())) {
existTypes.addAll(Arrays.asList(existDetail.getMaterialType().split(",")));
}
existTypes.addAll(newMaterialTypes);
// 更新资料类型
existDetail.setMaterialType(String.join(",", existTypes));
detailService.updateById(existDetail);
} else {
// 不存在可用明细,更新当前明细为可用
detail.setMaterialType(String.join(",", newMaterialTypes));
detail.setStatus(ArchiveConstants.DETAIL_STATUS_ENABLED);
detailService.updateById(detail);
}
}
/**
* File转MultipartFile
*/
private MultipartFile fileToMultipartFile(File file) {
try {
FileInputStream input = new FileInputStream(file);
return new MockMultipartFile(
"file",
file.getName(),
null,
input
);
} catch (Exception e) {
log.error("File转MultipartFile失败", e);
return null;
}
}
/**
* 生成反馈表Excel
*/
private void generateFeedbackExcel(String taskId, List<ArchiveFeedbackVO> feedbackList) {
if (CollUtil.isEmpty(feedbackList)) {
log.info("没有反馈数据,跳过生成反馈表");
return;
}
try {
// 创建临时文件
String tempFilePath = System.getProperty("java.io.tmpdir") + File.separator +
"feedback_" + taskId + "_" + System.currentTimeMillis() + ".xlsx";
File feedbackFile = new File(tempFilePath);
// 使用EasyExcel生成反馈表
EasyExcel.write(feedbackFile, ArchiveFeedbackVO.class)
.sheet("处理结果反馈")
.doWrite(feedbackList);
// 上传反馈表到OSS
YifuUser user = SecurityUtils.getUser();
if (user != null) {
R<TAttaInfo> uploadResult = fileUploadService.uploadFileReturnAtta(
fileToMultipartFile(feedbackFile),
"archive/feedback",
ArchiveConstants.ATTA_RELATION_TYPE_FEEDBACK,
taskId
);
if (uploadResult != null && R.isSuccess(uploadResult)) {
log.info("反馈表生成成功,任务ID:{}", taskId);
} else {
log.error("反馈表上传失败,任务ID:{}", taskId);
}
}
// 删除临时文件
FileUtil.del(feedbackFile);
} catch (Exception e) {
log.error("生成反馈表失败,任务ID:{}", taskId, e);
}
}
/**
* 解析身份证和姓名信息
* 支持格式:姓名-身份证号、身份证号-姓名、姓名身份证号、身份证号姓名
*/
private Map<String, String> parseIdCardAndName(String folderName) {
Map<String, String> result = new HashMap<>();
// 尝试用-分隔
if (folderName.contains("-")) {
String[] parts = folderName.split("-");
if (parts.length == 2) {
// 判断哪个是身份证号(18位数字)
if (parts[0].matches("\\d{18}")) {
result.put("idCard", parts[0]);
result.put("name", parts[1]);
} else if (parts[1].matches("\\d{18}")) {
result.put("name", parts[0]);
result.put("idCard", parts[1]);
}
}
} else {
// 尝试提取18位身份证号
int idCardStart = -1;
for (int i = 0; i <= folderName.length() - 18; i++) {
String sub = folderName.substring(i, i + 18);
if (sub.matches("\\d{18}")) {
idCardStart = i;
break;
}
}
if (idCardStart >= 0) {
String idCard = folderName.substring(idCardStart, idCardStart + 18);
String name = folderName.substring(0, idCardStart) +
folderName.substring(idCardStart + 18);
result.put("idCard", idCard);
result.put("name", name);
}
}
return result;
}
/**
* 校验文件格式
*/
private boolean checkFileFormat(String fileName) {
if (StrUtil.isBlank(fileName)) {
return false;
}
String ext = FileUtil.extName(fileName).toLowerCase();
for (String format : ArchiveConstants.SUPPORTED_FILE_FORMATS) {
if (format.equalsIgnoreCase(ext)) {
return true;
}
}
return false;
}
/**
* 资料类型名称转换为编码
*/
private String getMaterialTypeCode(String typeName) {
if (ArchiveConstants.MATERIAL_TYPE_STOCK.equals(typeName)) {
return ArchiveConstants.MATERIAL_TYPE_CODE_STOCK;
} else if (ArchiveConstants.MATERIAL_TYPE_NEW.equals(typeName)) {
return ArchiveConstants.MATERIAL_TYPE_CODE_NEW;
} else if (ArchiveConstants.MATERIAL_TYPE_RENEW.equals(typeName)) {
return ArchiveConstants.MATERIAL_TYPE_CODE_RENEW;
} else if (ArchiveConstants.MATERIAL_TYPE_LEAVE.equals(typeName)) {
return ArchiveConstants.MATERIAL_TYPE_CODE_LEAVE;
}
return ArchiveConstants.MATERIAL_TYPE_CODE_STOCK; // 默认存量
}
/**
* 单个新增人员附件
*/
@Override
@Transactional(rollbackFor = Exception.class)
public R<?> uploadSinglePersonArchive(String empIdcard, String empName, String deptNo,
String deptName, String materialType, MultipartFile[] files) {
YifuUser user = SecurityUtils.getUser();
if (null == user) {
return R.failed("用户未登录");
}
// 参数校验
if (StrUtil.isBlank(empIdcard) || StrUtil.isBlank(empName) || StrUtil.isBlank(deptNo) ||
StrUtil.isBlank(deptName) || StrUtil.isBlank(materialType)) {
return R.failed("参数不能为空");
}
// 校验附件
if (files == null || files.length == 0) {
return R.failed("附件不能为空");
}
// 校验资料类型是否有效
if (!isValidMaterialType(materialType)) {
return R.failed("资料类型不正确,必须为:存量、新签、续签、离职");
}
// 校验身份证格式
if (!empIdcard.matches("\\d{18}")) {
return R.failed("身份证号格式不正确");
}
try {
// 1. 查询项目信息获取客户信息
TSettleDomainSelectVo settleResult = archivesDaprUtil.selectDeptByNo(deptNo);
if (settleResult == null) {
return R.failed("项目编码[" + deptNo + "]信息不存在");
}
// 2. 查询人员档案信息获取档案所在地
IdNameNoVo vo = new IdNameNoVo();
vo.setName(empName);
vo.setNo(empIdcard);
R<TEmployeeInfo> empResult = archivesDaprUtil.getTEmployeeInfoByIdcardAndName(vo);
if (empResult == null || empResult.getData() == null) {
return R.failed("人员档案信息不存在");
}
TEmployeeInfo empInfo = empResult.getData();
// 3. 查找是否存在相同的电子档案明细
LambdaQueryWrapper<TElectronicArchiveDetail> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(TElectronicArchiveDetail::getEmpIdcard, empIdcard)
.eq(TElectronicArchiveDetail::getEmpName, empName)
.eq(TElectronicArchiveDetail::getDeptNo, deptNo)
.eq(TElectronicArchiveDetail::getStatus, ArchiveConstants.DETAIL_STATUS_ENABLED)
.eq(TElectronicArchiveDetail::getDeleteFlag, CommonConstants.ZERO_STRING)
.last(CommonConstants.LAST_ONE_SQL);
TElectronicArchiveDetail detail = detailService.getOne(wrapper);
// 4. 如果不存在,创建新的明细
if (detail == null) {
detail = new TElectronicArchiveDetail();
detail.setEmpName(empName);
detail.setEmpIdcard(empIdcard);
detail.setDeptNo(deptNo);
detail.setDeptName(deptName);
detail.setCustomerName(settleResult.getCustomerName());
detail.setCustomerCode(settleResult.getCustomerCode());
detail.setMaterialType(materialType);
detail.setStatus(ArchiveConstants.DETAIL_STATUS_ENABLED);
detail.setDeleteFlag(CommonConstants.ZERO_STRING);
detail.setFileProvince(empInfo.getFileProvince());
detail.setFileCity(empInfo.getFileCity());
detail.setFileTown(empInfo.getFileTown());
detailService.save(detail);
} else {
// 存在则更新资料类型(追加)
Set<String> existTypes = new HashSet<>();
if (StrUtil.isNotBlank(detail.getMaterialType())) {
existTypes.addAll(Arrays.asList(detail.getMaterialType().split(",")));
}
existTypes.add(materialType);
detail.setMaterialType(String.join(",", existTypes));
detailService.updateById(detail);
}
// 5. 上传附件到阿里云OSS
int successCount = 0;
int failCount = 0;
StringBuilder failReasons = new StringBuilder();
for (MultipartFile file : files) {
if (file == null || file.isEmpty()) {
continue;
}
// 校验文件大小
if (file.getSize() > ArchiveConstants.SINGLE_FILE_MAX_SIZE) {
failCount++;
failReasons.append(file.getOriginalFilename()).append(":文件大小超过15M;");
continue;
}
// 校验文件格式
String fileName = file.getOriginalFilename();
if (!checkFileFormat(fileName)) {
failCount++;
failReasons.append(fileName).append(":文件格式不符;");
continue;
}
try {
// 构建OSS目录路径
String ossDirectory = buildOssDirectory(detail, materialType);
// 上传文件到OSS
R<TAttaInfo> uploadResult = fileUploadService.uploadFileReturnAtta(
file,
ossDirectory,
getMaterialTypeCode(materialType),
detail.getId()
);
if (uploadResult != null && R.isSuccess(uploadResult)) {
successCount++;
} else {
failCount++;
failReasons.append(fileName).append(":上传失败;");
}
} catch (Exception e) {
log.error("上传文件失败:{}", fileName, e);
failCount++;
failReasons.append(fileName).append(":上传异常;");
}
}
// 6. 返回处理结果
String resultMsg = String.format("处理完成,成功:%d个,失败:%d个", successCount, failCount);
if (failCount > 0) {
resultMsg += "。失败原因:" + failReasons.toString();
}
return R.ok(null, resultMsg);
} catch (Exception e) {
log.error("单个新增人员附件失败", e);
return R.failed("处理失败:" + e.getMessage());
}
}
}
package com.yifu.cloud.plus.v1.batch.service.impl;
import com.yifu.cloud.plus.v1.batch.entity.TAttaInfo;
import com.yifu.cloud.plus.v1.batch.mapper.SysConfigLimitMapper;
import com.yifu.cloud.plus.v1.batch.service.FileUploadService;
import com.yifu.cloud.plus.v1.batch.service.TAttaInfoService;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.CommonConstants;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.ResultConstants;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.SecurityConstants;
import com.yifu.cloud.plus.v1.yifu.common.core.util.Common;
import com.yifu.cloud.plus.v1.yifu.common.core.util.OSSUtil;
import com.yifu.cloud.plus.v1.yifu.common.core.util.R;
import com.yifu.cloud.plus.v1.yifu.common.core.vo.FileVo;
import com.yifu.cloud.plus.v1.yifu.common.core.vo.YifuUser;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.net.URL;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
@Service
@RequiredArgsConstructor
@Slf4j
public class FileUploadServiceImpl implements FileUploadService {
private final OSSUtil ossUtil;
private final TAttaInfoService tAttaInfoService;
private AtomicInteger atomicInteger = new AtomicInteger(0);
//初始化附件上传队列上限值
private int maxLimit = 1;
private final SysConfigLimitMapper configLimitMapper;
@Override
public R<TAttaInfo> uploadFileReturnAtta(MultipartFile file, String filePath, String type, String domainId) throws IOException {
if (null == file) {
return R.failed("文件删除异常,请重新上传!");
}
String fileName = file.getOriginalFilename();
if (fileName == null || Common.isEmpty(fileName)) {
return R.failed("文件名不能为空!!");
}
if (fileName.length() > 100) {
return R.failed("文件名不能超过100个字符!");
}
//filePath不传默认存储空间的根目录
//jpg,jpeg,png,bmp
String key = "";
if (Common.isNotNull(filePath)) {
key = filePath + "/" + System.currentTimeMillis() + fileName;
} else {
key = System.currentTimeMillis() + fileName;
}
if (!Common.checkFile(key)) {
return R.failed("非法上传类型:"+fileName);
}
boolean flag = ossUtil.uploadFileByStream(file.getInputStream(), key, null);
TAttaInfo attaInfo;
URL url;
if (flag) {
log.info("文件:" + fileName + "上传到存储空间" + ossUtil.getBucketName() + "成功!");
attaInfo = initUnitAttaForInsert(fileName, key, file.getSize());
attaInfo.setRelationType(type);
try {
attaInfo.setDomainId(domainId);
Set<String> dbAuthsSet = new HashSet<>();
Collection<? extends GrantedAuthority> authorities = AuthorityUtils
.createAuthorityList(dbAuthsSet.toArray(new String[0]));
YifuUser user = new YifuUser("8", 1L, "", "电子档案上传",
"电子档案上传", "0", SecurityConstants.BCRYPT + "123456",
"12345678911", true, true, true,
true,
"1", authorities, "1",
null, null,
null,null);
attaInfo.setCreateBy(user.getId());
attaInfo.setCreateName(user.getNickname());
attaInfo = tAttaInfoService.add(attaInfo);
} catch (Exception e) {
log.error("OSS文件上传异常:" + e.getMessage());
ossUtil.deleteObject(null, key);
return R.failed("上传异常:" + e.getMessage());
}
url = ossUtil.getObjectUrl(null, attaInfo.getAttaSrc());
attaInfo.setAttaUrl(String.valueOf(url));
return R.ok(attaInfo);
} else {
return R.failed("fail:上传异常");
}
}
@Override
public R<FileVo> uploadImg(MultipartFile file, String filePath, String type, String domain) throws IOException {
if (null == file) {
return R.failed("文件删除异常,请重新上传!");
}
String fileName = file.getOriginalFilename();
if (Common.isEmpty(fileName)) {
return R.failed("文件名不能为空!");
}
if (fileName.length() > 100) {
return R.failed("文件名不能超过100个字符!");
}
//filePath不传默认存储空间的根目录
//jpg,jpeg,png,bmp
String key = "";
if (Common.isNotNull(filePath)) {
key = filePath + "/" + System.currentTimeMillis() + fileName;
} else {
key = System.currentTimeMillis() + fileName;
}
if (!Common.checkFile(key)) {
return R.failed("非法上传类型!");
}
boolean flag = ossUtil.uploadFileByStream(file.getInputStream(), key, null);
FileVo fileVo;
TAttaInfo attaInfo;
URL url;
if (flag) {
log.info("文件:" + fileName + "上传至存储空间" + ossUtil.getBucketName() + "成功!");
attaInfo = initUnitAttaForInsert(fileName, key, file.getSize());
attaInfo.setRelationType(type);
try {
attaInfo.setDomainId(domain);
attaInfo = tAttaInfoService.add(attaInfo);
} catch (Exception e) {
log.error("OSS文件上传接口异常:" + e.getMessage());
ossUtil.deleteObject(null, key);
return R.failed("fail:" + e.getMessage());
}
//有实体id则插入关系,用于编辑
if (Common.isNotNull(domain)) {
url = ossUtil.getObjectUrl(null, attaInfo.getAttaSrc());
fileVo = new FileVo(attaInfo.getId(), attaInfo.getAttaName(), url.toString());
return R.ok(fileVo, "success");
}
url = ossUtil.getObjectUrl(null, attaInfo.getAttaSrc());
fileVo = new FileVo(attaInfo.getId(), attaInfo.getAttaName(), url.toString());
return R.ok(fileVo, "success");
} else {
return R.failed("fail:上传失败");
}
}
/**
* 对象初始化
* @param fileName
* @param key
* @param fileSize
* @return
*/
private TAttaInfo initUnitAttaForInsert(String fileName, String key, long fileSize) {
TAttaInfo unitAtta = new TAttaInfo();
char slash = '/';
char point = '.';
if (fileName.lastIndexOf(slash) >= 0) {
unitAtta.setAttaName(fileName.substring(fileName.lastIndexOf(slash)));
} else {
unitAtta.setAttaName(fileName);
}
unitAtta.setAttaSize(fileSize);
unitAtta.setAttaSrc(key);
if (key.lastIndexOf(point) >= 0) {
unitAtta.setAttaType(key.substring(key.lastIndexOf(point)));
}
unitAtta.setCreateTime(LocalDateTime.now());
return unitAtta;
}
/**
* @Author fxj
* @Description 整合接口
* @Date 15:24 2024/9/24
* @Param
* @return
**/
@Override
public R uploadAsso(MultipartFile file, String filePath, String type, String domain, String uploadType){
return getR(file, filePath, type, domain, uploadType);
}
public R getR(MultipartFile file, String filePath, String type, String domain, String uploadType) {
maxLimit = configLimitMapper.getSysConfigLimitByKey("UPLOAD_MAX_LIMIT");
if (atomicInteger.incrementAndGet() <= maxLimit){
try {
if (CommonConstants.ZERO_STRING.equals(uploadType)){
return uploadImg(file, filePath, type, domain);
}else if (CommonConstants.ONE_STRING.equals(uploadType)){
return uploadFileReturnAtta(file, filePath, type, domain);
}
}catch (Exception e){
log.error("附件上传异常:",e);
}finally {
atomicInteger.decrementAndGet();
}
}else {
atomicInteger.decrementAndGet();
log.error("超出阈值:"+ResultConstants.FILE_UPLOADING_DATA);
return R.failed(ResultConstants.FILE_UPLOADING_DATA);
}
return null;
}
@Override
public boolean checkFileExists(String filePath) {
try {
return ossUtil.doesObjectExist(null, filePath);
} catch (Exception e) {
log.error("检查文件是否存在失败:{}", filePath, e);
return false;
}
}
}
\ No newline at end of file
package com.yifu.cloud.plus.v1.batch.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yifu.cloud.plus.v1.batch.entity.TAttaInfo;
import com.yifu.cloud.plus.v1.batch.mapper.TAttaInfoMapper;
import com.yifu.cloud.plus.v1.batch.service.TAttaInfoService;
import com.yifu.cloud.plus.v1.yifu.common.core.util.Common;
import com.yifu.cloud.plus.v1.yifu.common.core.util.OSSUtil;
import com.yifu.cloud.plus.v1.yifu.common.core.util.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.net.URL;
import java.util.List;
/**
* 附件信息表
*
* @author pwang
* @date 2021-02-19 16:16:07
*/
@Service
public class TAttaInfoServiceImpl extends ServiceImpl<TAttaInfoMapper, TAttaInfo> implements TAttaInfoService {
@Autowired
private OSSUtil ossUtil;
@Override
public List<TAttaInfo> getTAttaInfoListByDoMainId(String domainId) {
return baseMapper.getTAttaInfoListByDoMainId(domainId);
}
@Override
public TAttaInfo add(TAttaInfo attaInfo) {
this.save(attaInfo);
return attaInfo;
}
@Override
public Boolean updateDomainId(String domainId, List<String> ids){
if (Common.isEmpty(domainId) || !Common.isNotEmpty(ids)){
return false;
}
return baseMapper.updateDomainId(domainId,ids) > 0;
}
@Override
public R<List<TAttaInfo>> getAttaByApplyId(String applyId) {
List<TAttaInfo> list = baseMapper.getAttaByApplyId(applyId);
if (Common.isNotNull(list)) {
URL url;
for (TAttaInfo atta:list) {
url = ossUtil.getObjectUrl(null, atta.getAttaSrc());
atta.setAttaUrl(url.toString());
}
return R.ok(list);
}else{
return R.failed("没有附件");
}
}
@Override
public void deleteByDomainId(String domainId) {
baseMapper.deleteByDomainId(domainId);
}
}
package com.yifu.cloud.plus.v1.batch.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yifu.cloud.plus.v1.batch.constants.ArchiveConstants;
import com.yifu.cloud.plus.v1.batch.entity.TAttaInfo;
import com.yifu.cloud.plus.v1.batch.entity.TElectronicArchiveDetail;
import com.yifu.cloud.plus.v1.batch.entity.TElectronicArchiveTask;
import com.yifu.cloud.plus.v1.batch.entity.TTaskArchiveRelation;
import com.yifu.cloud.plus.v1.batch.mapper.TElectronicArchiveDetailMapper;
import com.yifu.cloud.plus.v1.batch.service.TAttaInfoService;
import com.yifu.cloud.plus.v1.batch.service.TElectronicArchiveDetailService;
import com.yifu.cloud.plus.v1.batch.service.TElectronicArchiveTaskService;
import com.yifu.cloud.plus.v1.batch.service.TTaskArchiveRelationService;
import com.yifu.cloud.plus.v1.batch.vo.*;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.CommonConstants;
import com.yifu.cloud.plus.v1.yifu.common.core.util.Common;
import com.yifu.cloud.plus.v1.yifu.common.core.util.OSSUtil;
import com.yifu.cloud.plus.v1.yifu.common.core.util.R;
import com.yifu.cloud.plus.v1.yifu.common.core.util.bean.OSSObjectBean;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* 电子档案明细表
*
* @author huych
* @date 2025-12-17 11:03:37
*/
@Log4j2
@Service
@RequiredArgsConstructor
public class TElectronicArchiveDetailServiceImpl extends ServiceImpl<TElectronicArchiveDetailMapper, TElectronicArchiveDetail> implements TElectronicArchiveDetailService {
private final TAttaInfoService attaInfoService;
private final TTaskArchiveRelationService relationService;
private final TElectronicArchiveTaskService taskService;
@Autowired
private OSSUtil ossUtil;
/**
* 电子档案明细表简单分页查询
* @param tElectronicArchiveDetail 电子档案明细表
* @return
*/
@Override
public IPage<TElectronicArchiveDetail> getTElectronicArchiveDetailPage(Page<TElectronicArchiveDetail> page, TElectronicArchiveDetailSearchVo tElectronicArchiveDetail){
return baseMapper.getTElectronicArchiveDetailPage(page,tElectronicArchiveDetail);
}
/**
* 通过id查询电子档案明细表详情
* 附件信息按任务分组展示,每个任务的附件按资料类型分类
*
* @param id 明细ID
* @return 明细详情
*/
@Override
public R<TElectronicArchiveDetail> getDetailInfoById(String id) {
TElectronicArchiveDetail detail = baseMapper.selectById(id);
if (Common.isEmpty(detail)) {
return R.failed(CommonConstants.NO_DATA_TO_HANDLE);
}
// 查询该明细关联的所有任务
List<TTaskArchiveRelation> relations = relationService.list(Wrappers.<TTaskArchiveRelation>lambdaQuery()
.eq(TTaskArchiveRelation::getArchiveDetailId, id));
if (CollUtil.isEmpty(relations)) {
log.info("电子档案明细没有关联的任务,明细ID:{}", id);
return R.ok(detail);
}
// 按任务分组组织附件信息
List<TaskAttachmentGroupVO> taskGroups = new ArrayList<>();
for (TTaskArchiveRelation relation : relations) {
// 查询任务信息
TElectronicArchiveTask task = taskService.getById(relation.getTaskId());
if (task == null || !ArchiveConstants.TASK_STATUS_GENERATED.equals(task.getStatus())) {
// 任务不存在或未完成,跳过
continue;
}
// 查询该任务上传的附件(domainId为明细ID,附件是在处理ZIP时关联到明细的)
List<TAttaInfo> taskAttachments = attaInfoService.list(Wrappers.<TAttaInfo>lambdaQuery()
.eq(TAttaInfo::getDomainId, id)
.eq(TAttaInfo::getRelationType, ArchiveConstants.ATTA_RELATION_TYPE_ZIP)
.eq(TAttaInfo::getStatus, ArchiveConstants.ATTA_STATUS_ENABLED));
if (CollUtil.isEmpty(taskAttachments)) {
continue;
}
// 按资料类型分组附件
Map<String, List<TAttaInfo>> materialTypeMap = new LinkedHashMap<>();
for (TAttaInfo atta : taskAttachments) {
// 从附件路径中提取资料类型
// 路径格式:项目编码-姓名-身份证号/资料类型/文件名
String materialType = extractMaterialTypeFromPath(atta.getAttaSrc());
if (StrUtil.isBlank(materialType)) {
materialType = "其他";
}
// 生成附件访问URL
URL url = ossUtil.getObjectUrl("yf-hr-efile-source", atta.getAttaSrc());
atta.setAttaUrl(url.toString());
// 按资料类型分组
materialTypeMap.computeIfAbsent(materialType, k -> new ArrayList<>()).add(atta);
}
// 创建任务附件分组VO
TaskAttachmentGroupVO groupVO = new TaskAttachmentGroupVO();
groupVO.setTaskId(task.getId());
groupVO.setTaskCode(task.getTaskCode());
// 设置创建人和创建时间
String createUser = task.getCreateName();
if (StrUtil.isBlank(createUser)) {
createUser = task.getCreateBy();
}
groupVO.setCreateUser(createUser);
groupVO.setCreateTime(task.getCreateTime());
groupVO.setMaterialTypeAttachments(materialTypeMap);
taskGroups.add(groupVO);
}
// 按上传时间倒序排序(最新的在前面)
taskGroups.sort((g1, g2) -> g2.getCreateTime().compareTo(g1.getCreateTime()));
detail.setTaskAttachmentGroups(taskGroups);
return R.ok(detail);
}
/**
* 从附件路径中提取资料类型
* 路径格式:项目编码-姓名-身份证号/资料类型/文件名
*
* @param attaSrc 附件路径
* @return 资料类型
*/
private String extractMaterialTypeFromPath(String attaSrc) {
if (StrUtil.isBlank(attaSrc)) {
return "";
}
try {
// 按/分割路径
String[] parts = attaSrc.split("/");
if (parts.length >= 2) {
// 第二部分应该是资料类型
String materialType = parts[parts.length - 2];
// 验证是否是有效的资料类型
if (ArchiveConstants.MATERIAL_TYPE_STOCK.equals(materialType) ||
ArchiveConstants.MATERIAL_TYPE_NEW.equals(materialType) ||
ArchiveConstants.MATERIAL_TYPE_RENEW.equals(materialType) ||
ArchiveConstants.MATERIAL_TYPE_LEAVE.equals(materialType)) {
return materialType;
}
}
} catch (Exception e) {
log.error("从附件路径提取资料类型失败,路径:{}", attaSrc, e);
}
return "";
}
/**
* 批量下载电子档案
*
* @param requestList 下载请求列表
* @param response HTTP响应
* @author huych
* @date 2025-12-17
*/
@Override
public void batchDownloadArchives(List<ArchiveBatchDownloadRequestVO> requestList, HttpServletResponse response) {
log.info("开始批量下载电子档案,请求数量:{}", requestList.size());
// 反馈列表
List<ArchiveDownloadFeedbackVO> feedbackList = new ArrayList<>();
// 用于存储需要下载的档案及其附件
Map<String, ArchiveDownloadInfo> downloadMap = new LinkedHashMap<>();
try {
// 1. 校验请求并查询档案信息
for (ArchiveBatchDownloadRequestVO request : requestList) {
try {
validateAndPrepareDownload(request, downloadMap, feedbackList);
} catch (Exception e) {
log.error("处理下载请求失败,姓名:{},身份证:{},项目编码:{}",
request.getEmpName(), request.getEmpIdcard(), request.getDeptNo(), e);
feedbackList.add(createFeedbackVO(request, "系统异常:" + e.getMessage()));
}
}
// 2. 设置响应头
String fileName = "电子档案批量下载_" +
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + ".zip";
response.setContentType("application/zip");
response.setCharacterEncoding("UTF-8");
response.setHeader(CommonConstants.CONTENT_DISPOSITION,
"attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
// 3. 创建ZIP输出流
try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) {
// 4. 下载并打包档案文件
for (Map.Entry<String, ArchiveDownloadInfo> entry : downloadMap.entrySet()) {
String folderName = entry.getKey();
ArchiveDownloadInfo downloadInfo = entry.getValue();
try {
downloadArchiveFiles(zipOut, folderName, downloadInfo);
// 更新反馈状态为成功
for (ArchiveDownloadFeedbackVO feedback : feedbackList) {
if (feedback.getEmpName().equals(downloadInfo.getDetail().getEmpName()) &&
feedback.getEmpIdcard().equals(downloadInfo.getDetail().getEmpIdcard()) &&
feedback.getDeptNo().equals(downloadInfo.getDetail().getDeptNo()) &&
feedback.getMaterialType().equals(downloadInfo.getMaterialType())) {
feedback.setDownloadStatus("成功");
feedback.setFailReason("");
}
}
} catch (Exception e) {
log.error("下载档案文件失败,文件夹:{}", folderName, e);
// 更新反馈状态为失败
for (ArchiveDownloadFeedbackVO feedback : feedbackList) {
if (feedback.getEmpName().equals(downloadInfo.getDetail().getEmpName()) &&
feedback.getEmpIdcard().equals(downloadInfo.getDetail().getEmpIdcard()) &&
feedback.getDeptNo().equals(downloadInfo.getDetail().getDeptNo()) &&
feedback.getMaterialType().equals(downloadInfo.getMaterialType())) {
feedback.setDownloadStatus("失败");
feedback.setFailReason("下载文件失败:" + e.getMessage());
}
}
}
}
// 5. 生成并添加反馈Excel表
addFeedbackExcel(zipOut, feedbackList);
zipOut.finish();
log.info("电子档案批量下载完成");
}
} catch (Exception e) {
log.error("批量下载电子档案失败", e);
throw new RuntimeException("批量下载失败:" + e.getMessage(), e);
}
}
/**
* 校验请求并准备下载信息
*
* @param request 下载请求
* @param downloadMap 下载信息Map
* @param feedbackList 反馈列表
*/
private void validateAndPrepareDownload(ArchiveBatchDownloadRequestVO request,
Map<String, ArchiveDownloadInfo> downloadMap,
List<ArchiveDownloadFeedbackVO> feedbackList) {
// 1. 根据姓名、身份证、项目编码、项目名称查询电子档案明细
LambdaQueryWrapper<TElectronicArchiveDetail> wrapper = Wrappers.<TElectronicArchiveDetail>lambdaQuery()
.eq(TElectronicArchiveDetail::getEmpName, request.getEmpName())
.eq(TElectronicArchiveDetail::getEmpIdcard, request.getEmpIdcard())
.eq(TElectronicArchiveDetail::getDeptNo, request.getDeptNo())
.eq(ArchiveConstants.DETAIL_STATUS_ENABLED.equals(ArchiveConstants.DETAIL_STATUS_ENABLED),
TElectronicArchiveDetail::getStatus, ArchiveConstants.DETAIL_STATUS_ENABLED);
// 如果项目名称不为空,添加项目名称条件
if (StrUtil.isNotBlank(request.getDeptName())) {
wrapper.eq(TElectronicArchiveDetail::getDeptName, request.getDeptName());
}
// 查询档案明细
TElectronicArchiveDetail detail = baseMapper.selectOne(wrapper);
// 2. 如果未找到档案,添加失败反馈
if (Common.isEmpty(detail)) {
feedbackList.add(createFeedbackVO(request, "没找到这个人和项目对应的信息"));
return;
}
// 3. 判断是否下载所有资料类型
boolean downloadAllTypes = StrUtil.isBlank(request.getMaterialType());
if (downloadAllTypes) {
// 资料类型为空,下载该人员该项目下所有资料类型
log.info("资料类型为空,将下载该人员该项目下所有资料类型,姓名:{},身份证:{},项目编码:{}",
request.getEmpName(), request.getEmpIdcard(), request.getDeptNo());
// 查询该明细下所有可用附件
List<TAttaInfo> allAttaList = attaInfoService.list(Wrappers.<TAttaInfo>lambdaQuery()
.eq(TAttaInfo::getDomainId, detail.getId())
.eq(TAttaInfo::getRelationType, ArchiveConstants.ATTA_RELATION_TYPE_ZIP)
.eq(TAttaInfo::getStatus, ArchiveConstants.ATTA_STATUS_ENABLED));
if (CollUtil.isEmpty(allAttaList)) {
feedbackList.add(createFeedbackVO(request, "该人员在此项目下没有可用的附件"));
return;
}
// 构建文件夹名称:项目编码-姓名-身份证
String folderName = detail.getDeptNo() + "-" + detail.getEmpName() + "-" + detail.getEmpIdcard();
// 按资料类型分组附件
Map<String, List<TAttaInfo>> materialTypeAttaMap = new LinkedHashMap<>();
for (TAttaInfo atta : allAttaList) {
String materialType = extractMaterialTypeFromPath(atta.getAttaSrc());
if (StrUtil.isBlank(materialType)) {
materialType = "其他";
}
materialTypeAttaMap.computeIfAbsent(materialType, k -> new ArrayList<>()).add(atta);
}
// 添加到下载Map
ArchiveDownloadInfo downloadInfo = downloadMap.get(folderName);
if (downloadInfo == null) {
downloadInfo = new ArchiveDownloadInfo();
downloadInfo.setDetail(detail);
downloadInfo.setMaterialTypeAttaMap(materialTypeAttaMap);
downloadMap.put(folderName, downloadInfo);
} else {
// 合并资料类型附件
downloadInfo.getMaterialTypeAttaMap().putAll(materialTypeAttaMap);
}
downloadInfo.setMaterialType("全部资料类型");
// 添加成功的反馈
ArchiveBatchDownloadRequestVO feedbackRequest = new ArchiveBatchDownloadRequestVO();
feedbackRequest.setEmpName(request.getEmpName());
feedbackRequest.setEmpIdcard(request.getEmpIdcard());
feedbackRequest.setDeptNo(request.getDeptNo());
feedbackRequest.setDeptName(request.getDeptName());
feedbackRequest.setMaterialType("全部资料类型");
feedbackList.add(createSuccessFeedbackVO(feedbackRequest));
} else {
// 下载指定资料类型
// 检查资料类型是否匹配(支持多个资料类型,用逗号分隔)
if (StrUtil.isBlank(detail.getMaterialType()) ||
!detail.getMaterialType().contains(request.getMaterialType())) {
feedbackList.add(createFeedbackVO(request, "该人员在此项目下没有【" + request.getMaterialType() + "】类型的资料"));
return;
}
// 查询该明细下指定资料类型的附件(状态为可用)
List<TAttaInfo> attaList = attaInfoService.list(Wrappers.<TAttaInfo>lambdaQuery()
.eq(TAttaInfo::getDomainId, detail.getId())
.eq(TAttaInfo::getRelationType, ArchiveConstants.ATTA_RELATION_TYPE_ZIP)
.eq(TAttaInfo::getStatus, ArchiveConstants.ATTA_STATUS_ENABLED)
.like(TAttaInfo::getAttaSrc, request.getMaterialType())); // 附件路径中包含资料类型
// 如果没有附件,添加失败反馈
if (CollUtil.isEmpty(attaList)) {
feedbackList.add(createFeedbackVO(request, "该资料类型下没有可用的附件"));
return;
}
// 构建文件夹名称:项目编码-姓名-身份证
String folderName = detail.getDeptNo() + "-" + detail.getEmpName() + "-" + detail.getEmpIdcard();
// 添加到下载Map
ArchiveDownloadInfo downloadInfo = downloadMap.get(folderName);
if (downloadInfo == null) {
downloadInfo = new ArchiveDownloadInfo();
downloadInfo.setDetail(detail);
downloadInfo.setMaterialTypeAttaMap(new HashMap<>());
downloadMap.put(folderName, downloadInfo);
}
// 按资料类型分组附件
if (!downloadInfo.getMaterialTypeAttaMap().containsKey(request.getMaterialType())) {
downloadInfo.getMaterialTypeAttaMap().put(request.getMaterialType(), attaList);
}
downloadInfo.setMaterialType(request.getMaterialType());
// 添加成功的反馈(状态暂时为"处理中",实际下载完成后更新)
feedbackList.add(createSuccessFeedbackVO(request));
}
}
/**
* 下载档案文件到ZIP
*
* @param zipOut ZIP输出流
* @param folderName 文件夹名称
* @param downloadInfo 下载信息
*/
private void downloadArchiveFiles(ZipOutputStream zipOut, String folderName,
ArchiveDownloadInfo downloadInfo) throws Exception {
Map<String, List<TAttaInfo>> materialTypeAttaMap = downloadInfo.getMaterialTypeAttaMap();
// 遍历每个资料类型
for (Map.Entry<String, List<TAttaInfo>> entry : materialTypeAttaMap.entrySet()) {
String materialType = entry.getKey();
List<TAttaInfo> attaList = entry.getValue();
// 创建二级文件夹:资料类型
String subFolderPath = folderName + "/" + materialType + "/";
// 准备OSS对象Bean列表
List<OSSObjectBean> ossObjectList = new ArrayList<>();
for (TAttaInfo atta : attaList) {
OSSObjectBean bean = new OSSObjectBean();
bean.setKey(atta.getAttaSrc()); // OSS文件路径
bean.setName(subFolderPath + atta.getAttaName()); // ZIP中的完整路径
ossObjectList.add(bean);
}
// 批量下载文件到ZIP
if (CollUtil.isNotEmpty(ossObjectList)) {
ossUtil.getObjectZip("yf-hr-efile-source", ossObjectList, zipOut);
}
}
}
/**
* 添加反馈Excel到ZIP
*
* @param zipOut ZIP输出流
* @param feedbackList 反馈列表
*/
private void addFeedbackExcel(ZipOutputStream zipOut, List<ArchiveDownloadFeedbackVO> feedbackList)
throws IOException {
try {
// 创建Excel内存流
ByteArrayOutputStream excelOut = new ByteArrayOutputStream();
// 使用EasyExcel写入Excel
EasyExcel.write(excelOut, ArchiveDownloadFeedbackVO.class)
.sheet("下载结果反馈")
.doWrite(feedbackList);
// 添加Excel到ZIP
ZipEntry excelEntry = new ZipEntry("下载结果反馈表.xlsx");
zipOut.putNextEntry(excelEntry);
zipOut.write(excelOut.toByteArray());
zipOut.closeEntry();
excelOut.close();
log.info("反馈Excel添加成功,共{}条记录", feedbackList.size());
} catch (Exception e) {
log.error("生成反馈Excel失败", e);
throw new IOException("生成反馈Excel失败", e);
}
}
/**
* 创建失败反馈VO
*
* @param request 请求VO
* @param failReason 失败原因
* @return 反馈VO
*/
private ArchiveDownloadFeedbackVO createFeedbackVO(ArchiveBatchDownloadRequestVO request, String failReason) {
ArchiveDownloadFeedbackVO feedback = new ArchiveDownloadFeedbackVO();
feedback.setEmpName(request.getEmpName());
feedback.setEmpIdcard(request.getEmpIdcard());
feedback.setDeptNo(request.getDeptNo());
feedback.setDeptName(request.getDeptName());
feedback.setMaterialType(request.getMaterialType());
feedback.setDownloadStatus("失败");
feedback.setFailReason(failReason);
return feedback;
}
/**
* 创建成功反馈VO
*
* @param request 请求VO
* @return 反馈VO
*/
private ArchiveDownloadFeedbackVO createSuccessFeedbackVO(ArchiveBatchDownloadRequestVO request) {
ArchiveDownloadFeedbackVO feedback = new ArchiveDownloadFeedbackVO();
feedback.setEmpName(request.getEmpName());
feedback.setEmpIdcard(request.getEmpIdcard());
feedback.setDeptNo(request.getDeptNo());
feedback.setDeptName(request.getDeptName());
feedback.setMaterialType(request.getMaterialType());
feedback.setDownloadStatus("处理中");
feedback.setFailReason("");
return feedback;
}
/**
* 下载信息内部类
*/
private static class ArchiveDownloadInfo {
private TElectronicArchiveDetail detail;
private Map<String, List<TAttaInfo>> materialTypeAttaMap;
private String materialType;
public TElectronicArchiveDetail getDetail() {
return detail;
}
public void setDetail(TElectronicArchiveDetail detail) {
this.detail = detail;
}
public Map<String, List<TAttaInfo>> getMaterialTypeAttaMap() {
return materialTypeAttaMap;
}
public void setMaterialTypeAttaMap(Map<String, List<TAttaInfo>> materialTypeAttaMap) {
this.materialTypeAttaMap = materialTypeAttaMap;
}
public String getMaterialType() {
return materialType;
}
public void setMaterialType(String materialType) {
this.materialType = materialType;
}
}
/**
* 删除电子档案附件
* <p>
* 功能说明:
* 1. 校验请求参数(明细ID、任务ID、资料类型、附件ID列表)
* 2. 校验附件是否属于指定的明细、任务和资料类型
* 3. 将附件状态更新为不可用(逻辑删除)
* 4. 同时删除OSS上的文件
* 5. 如果该资料类型下的所有附件都被删除,则从明细的资料类型字段中移除该类型
* </p>
*
* @param request 删除请求
* @return 删除结果
* @author huych
* @date 2025-12-17
*/
@Override
public R<String> deleteArchiveAttachments(ArchiveAttachmentDeleteRequestVO request) {
log.info("开始删除电子档案附件,明细ID:{},任务ID:{},资料类型:{},附件数量:{}",
request.getDetailId(), request.getTaskId(), request.getMaterialType(),
request.getAttachmentIds().size());
try {
// 1. 校验明细是否存在
TElectronicArchiveDetail detail = baseMapper.selectById(request.getDetailId());
if (Common.isEmpty(detail)) {
return R.failed("电子档案明细不存在");
}
// 2. 校验任务是否存在
TElectronicArchiveTask task = taskService.getById(request.getTaskId());
if (task == null) {
return R.failed("任务不存在");
}
// 3. 校验任务和明细的关联关系
LambdaQueryWrapper<TTaskArchiveRelation> relationWrapper = Wrappers.<TTaskArchiveRelation>lambdaQuery()
.eq(TTaskArchiveRelation::getTaskId, request.getTaskId())
.eq(TTaskArchiveRelation::getArchiveDetailId, request.getDetailId());
TTaskArchiveRelation relation = relationService.getOne(relationWrapper);
if (relation == null) {
return R.failed("任务和档案明细没有关联关系");
}
// 4. 校验资料类型是否有效
if (!isValidMaterialType(request.getMaterialType())) {
return R.failed("资料类型不正确,必须为:存量、新签、续签、离职");
}
// 5. 查询要删除的附件
List<TAttaInfo> attachments = attaInfoService.listByIds(request.getAttachmentIds());
if (CollUtil.isEmpty(attachments)) {
return R.failed("附件不存在");
}
// 6. 校验附件是否都属于该明细
for (TAttaInfo atta : attachments) {
if (!request.getDetailId().equals(atta.getDomainId())) {
return R.failed("附件【" + atta.getAttaName() + "】不属于该档案明细");
}
// 校验附件关系类型是否为ZIP附件
if (!ArchiveConstants.ATTA_RELATION_TYPE_ZIP.equals(atta.getRelationType())) {
return R.failed("附件【" + atta.getAttaName() + "】不是电子档案附件");
}
// 校验附件是否属于指定的资料类型(从路径中提取)
String materialType = extractMaterialTypeFromPath(atta.getAttaSrc());
if (!request.getMaterialType().equals(materialType)) {
return R.failed("附件【" + atta.getAttaName() + "】不属于资料类型【" + request.getMaterialType() + "】");
}
}
// 7. 逻辑删除附件(将状态更新为不可用)
int successCount = 0;
int failCount = 0;
StringBuilder failReasons = new StringBuilder();
for (TAttaInfo atta : attachments) {
try {
// 更新附件状态为不可用
atta.setStatus(ArchiveConstants.ATTA_STATUS_DISABLED);
boolean updated = attaInfoService.updateById(atta);
if (updated) {
// 删除OSS文件
try {
ossUtil.deleteObject("yf-hr-efile-source", atta.getAttaSrc());
successCount++;
log.info("删除附件成功,附件ID:{},文件名:{}", atta.getId(), atta.getAttaName());
} catch (Exception e) {
log.error("删除OSS文件失败,附件ID:{},路径:{}", atta.getId(), atta.getAttaSrc(), e);
// OSS删除失败不影响整体流程,已经逻辑删除了
successCount++;
}
} else {
failCount++;
failReasons.append(atta.getAttaName()).append(";");
}
} catch (Exception e) {
log.error("删除附件失败,附件ID:{}", atta.getId(), e);
failCount++;
failReasons.append(atta.getAttaName()).append(";");
}
}
// 8. 检查该资料类型下是否还有其他可用附件
LambdaQueryWrapper<TAttaInfo> attaWrapper = Wrappers.<TAttaInfo>lambdaQuery()
.eq(TAttaInfo::getDomainId, request.getDetailId())
.eq(TAttaInfo::getRelationType, ArchiveConstants.ATTA_RELATION_TYPE_ZIP)
.eq(TAttaInfo::getStatus, ArchiveConstants.ATTA_STATUS_ENABLED)
.like(TAttaInfo::getAttaSrc, "/" + request.getMaterialType() + "/");
long remainingCount = attaInfoService.count(attaWrapper);
// 9. 如果该资料类型下没有附件了,从明细的资料类型字段中移除
if (remainingCount == 0) {
updateDetailMaterialTypeAfterDelete(detail, request.getMaterialType());
}
// 10. 返回结果
String resultMsg = String.format("删除完成,成功:%d个,失败:%d个", successCount, failCount);
if (failCount > 0) {
resultMsg += "。失败的附件:" + failReasons.toString();
}
log.info("删除电子档案附件完成,明细ID:{},结果:{}", request.getDetailId(), resultMsg);
return R.ok(null, resultMsg);
} catch (Exception e) {
log.error("删除电子档案附件失败,明细ID:{}", request.getDetailId(), e);
return R.failed("删除失败:" + e.getMessage());
}
}
/**
* 判断是否是有效的资料类型
*/
private boolean isValidMaterialType(String typeName) {
return ArchiveConstants.MATERIAL_TYPE_STOCK.equals(typeName) ||
ArchiveConstants.MATERIAL_TYPE_NEW.equals(typeName) ||
ArchiveConstants.MATERIAL_TYPE_RENEW.equals(typeName) ||
ArchiveConstants.MATERIAL_TYPE_LEAVE.equals(typeName);
}
/**
* 删除附件后更新明细表的资料类型
* 从资料类型字段中移除指定的资料类型
*
* @param detail 档案明细
* @param removedMaterialType 要移除的资料类型
*/
private void updateDetailMaterialTypeAfterDelete(TElectronicArchiveDetail detail, String removedMaterialType) {
try {
if (StrUtil.isBlank(detail.getMaterialType())) {
return;
}
// 解析资料类型列表
List<String> materialTypes = new ArrayList<>(Arrays.asList(detail.getMaterialType().split(",")));
// 移除指定的资料类型
materialTypes.remove(removedMaterialType);
// 更新明细
if (materialTypes.isEmpty()) {
// 如果所有资料类型都被删除了,将状态改为不可用
detail.setMaterialType("");
detail.setStatus(ArchiveConstants.DETAIL_STATUS_DISABLED);
} else {
detail.setMaterialType(String.join(",", materialTypes));
}
baseMapper.updateById(detail);
log.info("更新档案明细资料类型成功,明细ID:{},新的资料类型:{}", detail.getId(), detail.getMaterialType());
} catch (Exception e) {
log.error("更新档案明细资料类型失败,明细ID:{}", detail.getId(), e);
}
}
/**
* 勾选导出电子档案
* <p>
* 功能说明:
* 1. 根据明细ID、任务ID、资料类型列表导出指定的附件
* 2. 导出格式与批量下载功能保持一致
* 3. 一级文件夹:项目编码-姓名-身份证
* 4. 二级文件夹:资料类型,该文件夹下为具体附件
* 5. 压缩包命名规则为:电子档案勾选导出_时间戳.zip
* 6. 只导出status为可用(0)的附件
* </p>
*
* @param requestList 导出请求列表
* @param response HTTP响应
* @author huych
* @date 2025-12-17
*/
@Override
public void selectedExportArchives(List<ArchiveSelectedExportRequestVO> requestList, HttpServletResponse response) {
log.info("开始勾选导出电子档案,请求数量:{}", requestList.size());
// 反馈列表
List<ArchiveDownloadFeedbackVO> feedbackList = new ArrayList<>();
// 用于存储需要导出的档案及其附件
Map<String, ArchiveDownloadInfo> downloadMap = new LinkedHashMap<>();
try {
// 1. 校验请求并查询档案信息
for (ArchiveSelectedExportRequestVO request : requestList) {
try {
validateAndPrepareSelectedExport(request, downloadMap, feedbackList);
} catch (Exception e) {
log.error("处理勾选导出请求失败,明细ID:{},任务ID:{}",
request.getDetailId(), request.getTaskId(), e);
feedbackList.add(createSelectedExportFeedbackVO(request, "系统异常:" + e.getMessage()));
}
}
// 2. 检查是否有可导出的档案
if (downloadMap.isEmpty()) {
log.warn("没有可导出的档案");
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":1,\"msg\":\"没有可导出的档案\"}");
return;
}
// 3. 设置响应头
String fileName = "电子档案勾选导出_" +
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + ".zip";
response.setContentType("application/zip");
response.setCharacterEncoding("UTF-8");
response.setHeader(CommonConstants.CONTENT_DISPOSITION,
"attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
// 4. 创建ZIP输出流
try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) {
// 5. 下载并打包档案文件
for (Map.Entry<String, ArchiveDownloadInfo> entry : downloadMap.entrySet()) {
String folderName = entry.getKey();
ArchiveDownloadInfo downloadInfo = entry.getValue();
try {
downloadArchiveFiles(zipOut, folderName, downloadInfo);
// 更新反馈状态为成功
updateFeedbackStatus(feedbackList, downloadInfo, "成功", "");
} catch (Exception e) {
log.error("导出档案文件失败,文件夹:{}", folderName, e);
// 更新反馈状态为失败
updateFeedbackStatus(feedbackList, downloadInfo, "失败", "导出文件失败:" + e.getMessage());
}
}
// 6. 生成并添加反馈Excel表
addFeedbackExcel(zipOut, feedbackList);
zipOut.finish();
log.info("电子档案勾选导出完成");
}
} catch (Exception e) {
log.error("勾选导出电子档案失败", e);
throw new RuntimeException("勾选导出失败:" + e.getMessage(), e);
}
}
/**
* 校验请求并准备勾选导出信息
*
* @param request 导出请求
* @param downloadMap 下载信息Map
* @param feedbackList 反馈列表
*/
private void validateAndPrepareSelectedExport(ArchiveSelectedExportRequestVO request,
Map<String, ArchiveDownloadInfo> downloadMap,
List<ArchiveDownloadFeedbackVO> feedbackList) {
// 1. 校验明细是否存在
TElectronicArchiveDetail detail = baseMapper.selectById(request.getDetailId());
if (Common.isEmpty(detail)) {
feedbackList.add(createSelectedExportFeedbackVO(request, "电子档案明细不存在"));
return;
}
// 2. 校验任务是否存在
TElectronicArchiveTask task = taskService.getById(request.getTaskId());
if (task == null) {
feedbackList.add(createSelectedExportFeedbackVO(request, "任务不存在"));
return;
}
// 3. 校验任务和明细的关联关系
LambdaQueryWrapper<TTaskArchiveRelation> relationWrapper = Wrappers.<TTaskArchiveRelation>lambdaQuery()
.eq(TTaskArchiveRelation::getTaskId, request.getTaskId())
.eq(TTaskArchiveRelation::getArchiveDetailId, request.getDetailId());
TTaskArchiveRelation relation = relationService.getOne(relationWrapper);
if (relation == null) {
feedbackList.add(createSelectedExportFeedbackVO(request, "任务和档案明细没有关联关系"));
return;
}
// 4. 校验资料类型是否为空
if (CollUtil.isEmpty(request.getMaterialTypes())) {
feedbackList.add(createSelectedExportFeedbackVO(request, "资料类型列表不能为空"));
return;
}
// 5. 构建文件夹名称:项目编码-姓名-身份证
String folderName = detail.getDeptNo() + "-" + detail.getEmpName() + "-" + detail.getEmpIdcard();
// 6. 按资料类型查询附件
Map<String, List<TAttaInfo>> materialTypeAttaMap = new LinkedHashMap<>();
for (String materialType : request.getMaterialTypes()) {
// 校验资料类型是否有效
if (!isValidMaterialType(materialType)) {
log.warn("资料类型无效,跳过:{}", materialType);
continue;
}
// 查询该明细下该任务的指定资料类型的附件
// 通过附件路径匹配资料类型和任务
List<TAttaInfo> attaList = attaInfoService.list(Wrappers.<TAttaInfo>lambdaQuery()
.eq(TAttaInfo::getDomainId, request.getDetailId())
.eq(TAttaInfo::getRelationType, ArchiveConstants.ATTA_RELATION_TYPE_ZIP)
.eq(TAttaInfo::getStatus, ArchiveConstants.ATTA_STATUS_ENABLED)
.like(TAttaInfo::getAttaSrc, materialType)); // 附件路径中包含资料类型
// 过滤附件:只保留属于指定任务的附件
// 方法:通过创建时间范围过滤(任务创建时间之后的附件)
List<TAttaInfo> taskAttaList = new ArrayList<>();
for (TAttaInfo atta : attaList) {
// 简单判断:附件创建时间在任务创建时间之后
if (atta.getCreateTime() != null && task.getCreateTime() != null &&
!atta.getCreateTime().isBefore(task.getCreateTime())) {
taskAttaList.add(atta);
}
}
// 如果该资料类型下有附件,添加到Map中
if (CollUtil.isNotEmpty(taskAttaList)) {
materialTypeAttaMap.put(materialType, taskAttaList);
// 添加成功的反馈
feedbackList.add(createSelectedExportSuccessFeedbackVO(detail, task, materialType));
} else {
// 添加失败的反馈
log.warn("该资料类型下没有可用的附件,明细ID:{},任务ID:{},资料类型:{}",
request.getDetailId(), request.getTaskId(), materialType);
feedbackList.add(createSelectedExportFeedbackVOWithDetail(detail, task, materialType,
"该资料类型下没有可用的附件"));
}
}
// 7. 如果有可导出的附件,添加到下载Map
if (!materialTypeAttaMap.isEmpty()) {
ArchiveDownloadInfo downloadInfo = downloadMap.get(folderName);
if (downloadInfo == null) {
downloadInfo = new ArchiveDownloadInfo();
downloadInfo.setDetail(detail);
downloadInfo.setMaterialTypeAttaMap(materialTypeAttaMap);
downloadMap.put(folderName, downloadInfo);
} else {
// 合并资料类型附件
downloadInfo.getMaterialTypeAttaMap().putAll(materialTypeAttaMap);
}
// 设置资料类型列表
downloadInfo.setMaterialType(String.join(",", materialTypeAttaMap.keySet()));
}
}
/**
* 创建勾选导出失败反馈VO
*
* @param request 请求VO
* @param failReason 失败原因
* @return 反馈VO
*/
private ArchiveDownloadFeedbackVO createSelectedExportFeedbackVO(ArchiveSelectedExportRequestVO request, String failReason) {
ArchiveDownloadFeedbackVO feedback = new ArchiveDownloadFeedbackVO();
feedback.setEmpName("未知");
feedback.setEmpIdcard("未知");
feedback.setDeptNo("未知");
feedback.setDeptName("未知");
feedback.setMaterialType(request.getMaterialTypes() != null ? String.join(",", request.getMaterialTypes()) : "");
feedback.setDownloadStatus("失败");
feedback.setFailReason(failReason);
return feedback;
}
/**
* 创建勾选导出失败反馈VO(带明细和任务信息)
*
* @param detail 明细
* @param task 任务
* @param materialType 资料类型
* @param failReason 失败原因
* @return 反馈VO
*/
private ArchiveDownloadFeedbackVO createSelectedExportFeedbackVOWithDetail(TElectronicArchiveDetail detail,
TElectronicArchiveTask task,
String materialType,
String failReason) {
ArchiveDownloadFeedbackVO feedback = new ArchiveDownloadFeedbackVO();
feedback.setEmpName(detail.getEmpName());
feedback.setEmpIdcard(detail.getEmpIdcard());
feedback.setDeptNo(detail.getDeptNo());
feedback.setDeptName(detail.getDeptName());
feedback.setMaterialType(materialType);
feedback.setDownloadStatus("失败");
feedback.setFailReason(failReason);
return feedback;
}
/**
* 创建勾选导出成功反馈VO
*
* @param detail 明细
* @param task 任务
* @param materialType 资料类型
* @return 反馈VO
*/
private ArchiveDownloadFeedbackVO createSelectedExportSuccessFeedbackVO(TElectronicArchiveDetail detail,
TElectronicArchiveTask task,
String materialType) {
ArchiveDownloadFeedbackVO feedback = new ArchiveDownloadFeedbackVO();
feedback.setEmpName(detail.getEmpName());
feedback.setEmpIdcard(detail.getEmpIdcard());
feedback.setDeptNo(detail.getDeptNo());
feedback.setDeptName(detail.getDeptName());
feedback.setMaterialType(materialType);
feedback.setDownloadStatus("处理中");
feedback.setFailReason("");
return feedback;
}
/**
* 更新反馈状态
*
* @param feedbackList 反馈列表
* @param downloadInfo 下载信息
* @param status 状态
* @param failReason 失败原因
*/
private void updateFeedbackStatus(List<ArchiveDownloadFeedbackVO> feedbackList,
ArchiveDownloadInfo downloadInfo,
String status,
String failReason) {
TElectronicArchiveDetail detail = downloadInfo.getDetail();
Set<String> materialTypes = downloadInfo.getMaterialTypeAttaMap().keySet();
for (ArchiveDownloadFeedbackVO feedback : feedbackList) {
if (feedback.getEmpName().equals(detail.getEmpName()) &&
feedback.getEmpIdcard().equals(detail.getEmpIdcard()) &&
feedback.getDeptNo().equals(detail.getDeptNo()) &&
materialTypes.contains(feedback.getMaterialType())) {
feedback.setDownloadStatus(status);
feedback.setFailReason(failReason);
}
}
}
}
package com.yifu.cloud.plus.v1.batch.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yifu.cloud.plus.v1.batch.entity.TElectronicArchiveDetail;
import com.yifu.cloud.plus.v1.batch.entity.TElectronicArchiveTask;
import com.yifu.cloud.plus.v1.batch.entity.TTaskArchiveRelation;
import com.yifu.cloud.plus.v1.batch.mapper.TElectronicArchiveTaskMapper;
import com.yifu.cloud.plus.v1.batch.service.TElectronicArchiveDetailService;
import com.yifu.cloud.plus.v1.batch.service.TElectronicArchiveTaskService;
import com.yifu.cloud.plus.v1.batch.service.TTaskArchiveRelationService;
import com.yifu.cloud.plus.v1.batch.vo.TElectronicArchiveTaskSearchVo;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.CommonConstants;
import com.yifu.cloud.plus.v1.yifu.common.core.util.Common;
import com.yifu.cloud.plus.v1.yifu.common.core.util.R;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 电子档案任务表
*
* @author huych
* @date 2025-12-17 11:03:07
*/
@Log4j2
@Service
@RequiredArgsConstructor
public class TElectronicArchiveTaskServiceImpl extends ServiceImpl<TElectronicArchiveTaskMapper, TElectronicArchiveTask> implements TElectronicArchiveTaskService {
private final TTaskArchiveRelationService relationService;
private final TElectronicArchiveDetailService detailService;
/**
* 电子档案任务表简单分页查询
* @param tElectronicArchiveTask 电子档案任务表
* @return
*/
@Override
public IPage<TElectronicArchiveTask> getTElectronicArchiveTaskPage(Page<TElectronicArchiveTask> page, TElectronicArchiveTaskSearchVo tElectronicArchiveTask){
return baseMapper.getTElectronicArchiveTaskPage(page,tElectronicArchiveTask);
}
@Override
public R deleteTaskById(String id) {
TElectronicArchiveTask task = this.getById(id);
if (task == null) {
return R.failed("任务不存在");
}
if (!CommonConstants.ZERO_STRING.equals(task.getStatus())) {
return R.failed("只有待上传状态的任务才可删除!");
}
baseMapper.deleteById(id);
List<TTaskArchiveRelation> relationList = relationService.list(Wrappers.<TTaskArchiveRelation>query().lambda()
.eq(TTaskArchiveRelation::getTaskId, id));
if (Common.isNotNull(relationList)) {
for (TTaskArchiveRelation relation : relationList) {
//删除关联关系表
relationService.removeById(relation.getId());
//删除电子档案明细表
detailService.remove(Wrappers.<TElectronicArchiveDetail>query().lambda()
.eq(TElectronicArchiveDetail::getId, relation.getArchiveDetailId()));
}
}
return R.ok();
}
}
package com.yifu.cloud.plus.v1.batch.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yifu.cloud.plus.v1.batch.entity.TTaskArchiveRelation;
import com.yifu.cloud.plus.v1.batch.mapper.TTaskArchiveRelationMapper;
import com.yifu.cloud.plus.v1.batch.service.TTaskArchiveRelationService;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;
/**
* 电子档案上传任务和档案明细关系表
*
* @author huych
* @date 2025-12-17 11:02:01
*/
@Log4j2
@Service
public class TTaskArchiveRelationServiceImpl extends ServiceImpl<TTaskArchiveRelationMapper, TTaskArchiveRelation> implements TTaskArchiveRelationService {
/**
* 电子档案上传任务和档案明细关系表简单分页查询
* @param tTaskArchiveRelation 电子档案上传任务和档案明细关系表
* @return
*/
@Override
public IPage<TTaskArchiveRelation> getTTaskArchiveRelationPage(Page<TTaskArchiveRelation> page, TTaskArchiveRelation tTaskArchiveRelation){
return baseMapper.getTTaskArchiveRelationPage(page,tTaskArchiveRelation);
}
}
<?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.yifu.cloud.plus.v1.batch.mapper.SysConfigLimitMapper">
<resultMap id="sysConfigLimitMap" type="com.yifu.cloud.plus.v1.batch.entity.SysConfigLimit">
<id property="id" column="ID"/>
<result property="configKey" column="CONFIG_KEY"/>
<result property="configValue" column="CONFIG_VALUE"/>
<result property="remark" column="REMARK"/>
</resultMap>
<!-- 根据key获取value -->
<select id="getSysConfigLimitByKey" resultType="java.lang.Integer">
SELECT a.CONFIG_VALUE
FROM sys_config_limit a
where a.CONFIG_KEY = #{configKey} limit 1
</select>
</mapper>
<?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.yifu.cloud.plus.v1.batch.mapper.TAttaInfoMapper">
<resultMap id="tAttaInfoMap" type="com.yifu.cloud.plus.v1.batch.entity.TAttaInfo">
<id property="id" column="id"/>
<result property="attaName" column="atta_name"/>
<result property="attaSrc" column="atta_src"/>
<result property="attaSize" column="atta_size"/>
<result property="attaType" column="atta_type"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="relationType" column="relation_type"/>
<result property="domainId" column="domain_id"/>
</resultMap>
<sql id="Base_Column_List">
a.id,
a.atta_name,
a.atta_src,
a.atta_size,
a.atta_type,
a.create_by,
a.create_time,
a.relation_type,
a.domain_id
</sql>
<sql id="tAttaInfo_where">
<if test="tAttaInfo != null">
<if test="tAttaInfo.id != null and tAttaInfo.id.trim() != ''">
AND a.id = #{tAttaInfo.id}
</if>
<if test="tAttaInfo.attaName != null and tAttaInfo.attaName.trim() != ''">
AND a.atta_name = #{tAttaInfo.attaName}
</if>
<if test="tAttaInfo.attaSrc != null and tAttaInfo.attaSrc.trim() != ''">
AND a.atta_src = #{tAttaInfo.attaSrc}
</if>
<if test="tAttaInfo.attaSize != null">
AND a.atta_size = #{tAttaInfo.attaSize}
</if>
<if test="tAttaInfo.attaType != null and tAttaInfo.attaType.trim() != ''">
AND a.atta_type = #{tAttaInfo.attaType}
</if>
<if test="tAttaInfo.createBy != null and tAttaInfo.createBy.trim() != ''">
AND a.create_by = #{tAttaInfo.createBy}
</if>
<if test="tAttaInfo.createTime != null">
AND a.create_time = #{tAttaInfo.createTime}
</if>
<if test="tAttaInfo.relationType != null and tAttaInfo.relationType.trim() != ''">
AND a.relation_type = #{tAttaInfo.relationType}
</if>
<if test="tAttaInfo.domainId != null and tAttaInfo.domainId.trim() != ''">
AND a.domain_id = #{tAttaInfo.domainId}
</if>
</if>
</sql>
<!--tAttaInfo简单分页查询-->
<select id="getTAttaInfoPage" resultMap="tAttaInfoMap">
SELECT
<include refid="Base_Column_List"/>
FROM t_atta_info a
<where>
1=1
<include refid="tAttaInfo_where"/>
</where>
</select>
<!--tAttaInfo 获取list-->
<select id="getTAttaInfoList" resultMap="tAttaInfoMap">
SELECT
<include refid="Base_Column_List"/>
FROM t_atta_info a
<where>
1=1
<include refid="tAttaInfo_where"/>
</where>
</select>
<!--根据domainId 获取list-->
<select id="getTAttaInfoListByDoMainId" resultMap="tAttaInfoMap">
SELECT
<include refid="Base_Column_List"/>
FROM t_atta_info a
where a.domain_id = #{domainId}
</select>
<select id="getKeenAtta" resultMap="tAttaInfoMap">
SELECT
<include refid="Base_Column_List"/>
FROM t_atta_info a
<where>
1=1
<if test="values != null and values.size() > 0">
and a.domain_id in
<foreach collection="values" item="param" index="index" open="(" close=")" separator=",">
#{param}
</foreach>
</if>
and a.create_time >= #{dayStart}
and a.relation_type in ("3","4")
</where>
</select>
<update id="updateDomainId">
update t_atta_info
<trim prefix="set" suffixOverrides=",">
domain_id=#{domainId}
</trim>
WHERE
<if test="ids != null and ids.size() > 0">
id in
<foreach collection="ids" item="param" index="index" open="(" close=")" separator=",">
#{param}
</foreach>
</if>
</update>
<select id="getAttaByApplyId" resultMap="tAttaInfoMap">
SELECT
<include refid="Base_Column_List"/>
FROM t_atta_info a
where EXISTS (select 1 from m_salary_prova_atta b where b.SALARY_PROVA_ID = #{applyId} and a.id = b.ATTA_ID);
</select>
<delete id="deleteByDomainId">
delete from t_atta_info where domain_id=#{domainId}
</delete>
<!-- 详档变简档,删除身份证、其他附件、签名之外的附件 -->
<delete id="deleteByDomainIdExceptionCard">
delete from t_atta_info where domain_id=#{domainId} and relation_type not in ('9','24','27','21','22')
</delete>
<delete id="deleteByDomainIdAndType">
delete from t_atta_info where domain_id=#{domainId} and relation_type = #{relationType}
</delete>
<delete id="deleteByDomainIdAndOther">
delete from t_atta_info where domain_id=#{domainId} and relation_type not in ('0','2','26','27')
</delete>
<!-- 简档,仅删除身份证附件和(21(预入职申明签名);22(预入职确认签名);23(预入职pdf文) -->
<delete id="deleteByDomainIdCard">
delete from t_atta_info where domain_id=#{domainId} and relation_type in ('9','24','21','22','23')
</delete>
<select id="getAttInfoCountByParam" resultType="java.lang.Integer">
SELECT
count(1)
from (select id from t_pre_emp_main where `STATUS`='4' and SOUR_TYPE='0'
and DEPT_NO = #{departNo}
and AUDIT_TIME >= #{startDate}
and AUDIT_TIME <![CDATA[ <= ]]> #{endDate}) a
LEFT JOIN (select * from t_atta_info where RELATION_TYPE ='23' GROUP BY DOMAIN_ID ) b on a.id=b.DOMAIN_ID
where b.DOMAIN_ID is not null
</select>
</mapper>
<?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.yifu.cloud.plus.v1.batch.mapper.TElectronicArchiveDetailMapper">
<resultMap id="tElectronicArchiveDetailMap" type="com.yifu.cloud.plus.v1.batch.entity.TElectronicArchiveDetail">
<id property="id" column="ID"/>
<result property="empName" column="EMP_NAME"/>
<result property="empIdcard" column="EMP_IDCARD"/>
<result property="deptNo" column="DEPT_NO"/>
<result property="deptName" column="DEPT_NAME"/>
<result property="customerName" column="CUSTOMER_NAME"/>
<result property="customerCode" column="CUSTOMER_CODE"/>
<result property="materialType" column="MATERIAL_TYPE"/>
<result property="fileProvince" column="FILE_PROVINCE"/>
<result property="fileCity" column="FILE_CITY"/>
<result property="fileTown" column="FILE_TOWN"/>
<result property="status" column="STATUS"/>
<result property="deleteFlag" column="DELETE_FLAG"/>
<result property="createBy" column="CREATE_BY"/>
<result property="createName" column="CREATE_NAME"/>
<result property="createTime" column="CREATE_TIME"/>
<result property="updateBy" column="UPDATE_BY"/>
<result property="updateTime" column="UPDATE_TIME"/>
</resultMap>
<sql id="Base_Column_List">
a.ID,
a.EMP_NAME,
a.EMP_IDCARD,
a.DEPT_NO,
a.DEPT_NAME,
a.CUSTOMER_NAME,
a.CUSTOMER_CODE,
a.MATERIAL_TYPE,
a.FILE_PROVINCE,
a.FILE_CITY,
a.FILE_TOWN,
a.STATUS,
a.DELETE_FLAG,
a.CREATE_BY,
a.CREATE_NAME,
a.CREATE_TIME,
a.UPDATE_BY,
a.UPDATE_TIME
</sql>
<sql id="tElectronicArchiveDetail_where">
<if test="tElectronicArchiveDetail != null">
<if test="tElectronicArchiveDetail.id != null and tElectronicArchiveDetail.id.trim() != ''">
AND a.ID = #{tElectronicArchiveDetail.id}
</if>
<if test="tElectronicArchiveDetail.empName != null and tElectronicArchiveDetail.empName.trim() != ''">
AND a.EMP_NAME like concat('%', #{tElectronicArchiveDetail.empName}, '%')
</if>
<if test="tElectronicArchiveDetail.empIdcard != null and tElectronicArchiveDetail.empIdcard.trim() != ''">
AND a.EMP_IDCARD = #{tElectronicArchiveDetail.empIdcard}
</if>
<if test="tElectronicArchiveDetail.deptNo != null and tElectronicArchiveDetail.deptNo.trim() != ''">
AND a.DEPT_NO = #{tElectronicArchiveDetail.deptNo}
</if>
<if test="tElectronicArchiveDetail.deptName != null and tElectronicArchiveDetail.deptName.trim() != ''">
AND a.DEPT_NAME like concat('%', #{tElectronicArchiveDetail.deptName}, '%')
</if>
<if test="tElectronicArchiveDetail.customerName != null and tElectronicArchiveDetail.customerName.trim() != ''">
AND a.CUSTOMER_NAME like concat('%', #{tElectronicArchiveDetail.customerName}, '%')
</if>
<if test="tElectronicArchiveDetail.customerCode != null and tElectronicArchiveDetail.customerCode.trim() != ''">
AND a.CUSTOMER_CODE = #{tElectronicArchiveDetail.customerCode}
</if>
<if test="tElectronicArchiveDetail.typeList != null and tElectronicArchiveDetail.typeList.size() > 0">
AND a.MATERIAL_TYPE in
<foreach item="item" index="index" collection="tElectronicArchiveDetail.typeList" open="(" separator="," close=")">
'${item}'
</foreach>
</if>
<if test="tElectronicArchiveDetail.fileProvince != null">
AND a.FILE_PROVINCE = #{tElectronicArchiveDetail.fileProvince}
</if>
<if test="tElectronicArchiveDetail.fileCity != null">
AND a.FILE_CITY = #{tElectronicArchiveDetail.fileCity}
</if>
<if test="tElectronicArchiveDetail.fileTown != null">
AND a.FILE_TOWN = #{tElectronicArchiveDetail.fileTown}
</if>
<if test="tElectronicArchiveDetail.status != null and tElectronicArchiveDetail.status.trim() != ''">
AND a.STATUS = #{tElectronicArchiveDetail.status}
</if>
<if test="tElectronicArchiveDetail.createBy != null and tElectronicArchiveDetail.createBy.trim() != ''">
AND a.CREATE_BY = #{tElectronicArchiveDetail.createBy}
</if>
</if>
</sql>
<!--tElectronicArchiveDetail简单分页查询-->
<select id="getTElectronicArchiveDetailPage" resultMap="tElectronicArchiveDetailMap">
SELECT
<include refid="Base_Column_List"/>
FROM t_electronic_archive_detail a
<where>
a.DELETE_FLAG = '0'
<include refid="tElectronicArchiveDetail_where"/>
</where>
</select>
</mapper>
<?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.yifu.cloud.plus.v1.batch.mapper.TElectronicArchiveTaskMapper">
<resultMap id="tElectronicArchiveTaskMap" type="com.yifu.cloud.plus.v1.batch.entity.TElectronicArchiveTask">
<id property="id" column="ID"/>
<result property="taskCode" column="TASK_CODE"/>
<result property="uploadFileName" column="UPLOAD_FILE_NAME"/>
<result property="uploadZipName" column="UPLOAD_ZIP_NAME"/>
<result property="status" column="STATUS"/>
<result property="deleteFlag" column="DELETE_FLAG"/>
<result property="createBy" column="CREATE_BY"/>
<result property="createName" column="CREATE_NAME"/>
<result property="createTime" column="CREATE_TIME"/>
<result property="updateBy" column="UPDATE_BY"/>
<result property="updateTime" column="UPDATE_TIME"/>
</resultMap>
<sql id="Base_Column_List">
a.ID,
a.TASK_CODE,
a.UPLOAD_FILE_NAME,
a.UPLOAD_ZIP_NAME,
a.STATUS,
a.DELETE_FLAG,
a.CREATE_BY,
a.CREATE_NAME,
a.CREATE_TIME,
a.UPDATE_BY,
a.UPDATE_TIME
</sql>
<sql id="tElectronicArchiveTask_where">
<if test="tElectronicArchiveTask != null">
<if test="tElectronicArchiveTask.id != null and tElectronicArchiveTask.id.trim() != ''">
AND a.ID = #{tElectronicArchiveTask.id}
</if>
<if test="tElectronicArchiveTask.taskCode != null and tElectronicArchiveTask.taskCode.trim() != ''">
AND a.TASK_CODE = #{tElectronicArchiveTask.taskCode}
</if>
<if test="tElectronicArchiveTask.typeList != null and tElectronicArchiveTask.typeList.size() > 0">
AND a.STATUS in
<foreach item="item" index="index" collection="tElectronicArchiveTask.typeList" open="(" separator="," close=")">
'${item}'
</foreach>
</if>
<if test="tElectronicArchiveTask.deleteFlag != null and tElectronicArchiveTask.deleteFlag.trim() != ''">
AND a.DELETE_FLAG = #{tElectronicArchiveTask.deleteFlag}
</if>
<if test="tElectronicArchiveTask.createBy != null and tElectronicArchiveTask.createBy.trim() != ''">
AND a.CREATE_BY = #{tElectronicArchiveTask.createBy}
</if>
<if test="tElectronicArchiveTask.createName != null and tElectronicArchiveTask.createName.trim() != ''">
AND a.CREATE_NAME like concat('%', #{tElectronicArchiveTask.createName}, '%')
</if>
<if test="tElectronicArchiveTask.createTimeStart != null">
AND a.CREATE_TIME <![CDATA[ >= ]]> #{tElectronicArchiveTask.createTimeStart}
</if>
<if test="tElectronicArchiveTask.createTimeEnd != null">
AND a.CREATE_TIME <![CDATA[ <= ]]> #{tElectronicArchiveTask.createTimeEnd}
</if>
</if>
</sql>
<!--tElectronicArchiveTask简单分页查询-->
<select id="getTElectronicArchiveTaskPage" resultMap="tElectronicArchiveTaskMap">
SELECT
<include refid="Base_Column_List"/>
FROM t_electronic_archive_task a
<where>
a.DELETE_FLAG = '0'
<include refid="tElectronicArchiveTask_where"/>
</where>
</select>
</mapper>
<?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.yifu.cloud.plus.v1.batch.mapper.TTaskArchiveRelationMapper">
<resultMap id="tTaskArchiveRelationMap" type="com.yifu.cloud.plus.v1.batch.entity.TTaskArchiveRelation">
<id property="id" column="ID"/>
<result property="taskId" column="TASK_ID"/>
<result property="archiveDetailId" column="ARCHIVE_DETAIL_ID"/>
</resultMap>
<sql id="Base_Column_List">
a.ID,
a.TASK_ID,
a.ARCHIVE_DETAIL_ID
</sql>
<sql id="tTaskArchiveRelation_where">
<if test="tTaskArchiveRelation != null">
<if test="tTaskArchiveRelation.id != null and tTaskArchiveRelation.id.trim() != ''">
AND a.ID = #{tTaskArchiveRelation.id}
</if>
<if test="tTaskArchiveRelation.taskId != null and tTaskArchiveRelation.taskId.trim() != ''">
AND a.TASK_ID = #{tTaskArchiveRelation.taskId}
</if>
<if test="tTaskArchiveRelation.archiveDetailId != null and tTaskArchiveRelation.archiveDetailId.trim() != ''">
AND a.ARCHIVE_DETAIL_ID = #{tTaskArchiveRelation.archiveDetailId}
</if>
</if>
</sql>
<!--tTaskArchiveRelation简单分页查询-->
<select id="getTTaskArchiveRelationPage" resultMap="tTaskArchiveRelationMap">
SELECT
<include refid="Base_Column_List"/>
FROM t_task_archive_relation a
<where>
1=1
<include refid="tTaskArchiveRelation_where"/>
</where>
</select>
</mapper>
......@@ -223,4 +223,11 @@ public interface CacheConstants {
* 项目下用户部门及权限缓存
*/
String PROJECT_USER_PERMISSION = "mvp_project_user_permission:";
/**
* @Description: 每日电子档案任务编码缓存
* @Author: huych
* @Date: 2025/12/17
**/
String ARCHIVE_TASK_CODE = "archive_task_code";
}
......@@ -14,7 +14,6 @@ import com.yifu.cloud.plus.v1.yifu.common.core.util.Common;
import com.yifu.cloud.plus.v1.yifu.common.core.util.R;
import com.yifu.cloud.plus.v1.yifu.common.core.vo.BaseSearchListVO;
import com.yifu.cloud.plus.v1.yifu.common.core.vo.BaseSearchVO;
import com.yifu.cloud.plus.v1.yifu.common.core.vo.YifuUser;
import com.yifu.cloud.plus.v1.yifu.common.dapr.config.DaprArchivesProperties;
import com.yifu.cloud.plus.v1.yifu.insurances.vo.AutoDeptVO;
import com.yifu.cloud.plus.v1.yifu.salary.vo.TInsuranceUnpurchasePersonCardVo;
......@@ -262,6 +261,23 @@ public class ArchivesDaprUtil {
return res;
}
/**
* @param vo
* @Description: 获取档案基本信息
* @Author: huyc
* @Date: 2025/12/17 14:48
* @return: com.yifu.cloud.plus.v1.yifu.common.core.util.R<com.yifu.cloud.plus.v1.yifu.archives.entity.TEmployeeInfo>
**/
public R<TEmployeeInfo> getTEmployeeInfoByIdcardAndName(IdNameNoVo vo) {
R<TEmployeeInfo> res = HttpDaprUtil.invokeMethodPost(
daprArchivesProperties.getAppUrl(),daprArchivesProperties.getAppId()
,"/temployeeinfo/inner/getTEmployeeInfoByIdcardAndName" , JSON.toJSONString(vo), TEmployeeInfo.class, SecurityConstants.FROM_IN);
if (Common.isEmpty(res)){
return R.failed("获取档案信息失败!");
}
return res;
}
/**
* @Description: 获取项目档案基本信息
* @Author: hgw
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment