Commit f0bbbb7f authored by fangxinjiang's avatar fangxinjiang

Merge remote-tracking branch 'origin/MVP1.7.18' into MVP1.7.18

parents 5d9b622e cd63095b
......@@ -33,8 +33,9 @@ spec: # 资源规范字段
#dapr.io/sidecar-memory-limit: "800Mi" #Dapr sidecar可以使用的最大内存量。默认情况下未设置 请参阅 https://kubernetes.io/docs/tasks/administer-cluster/manage-resources/quota-memory-cpu-namespace/ 的有效值。
#dapr.io/sidecar-cpu-request: 1 #Dapr sidecar要求的 CPU 数量
#dapr.io/sidecar-memory-request #Dapr sidecar 请求的内存数量
dapr.io/app-max-request-size: "600" # 明确指定应用请求大小
dapr.io/http-max-request-size: "600" #增加http和grpc服务器请求正文参数的最大大小,单位为MB,以处理大文件的上传。 默认值为 4 MB
dapr.io/http-read-buffer-size: "200" #Dapr has a default limit of 4KB for the http header read buffer size. When sending http headers that are bigger than the default 4KB, you can increase this value. Otherwise, you may encounter a Too big request header
dapr.io/http-read-buffer-size: "4096" #Dapr has a default limit of 4KB for the http header read buffer size. When sending http headers that are bigger than the default 4KB, you can increase this value. Otherwise, you may encounter a Too big request header
dapr.io/sidecar-listen-addresses: "0.0.0.0"
# 更多dapr配置请参考 https://www.bookstack.cn/read/dapr-1.5-zh/cc77b74e2cc2f4d4.md
......
......@@ -33,8 +33,9 @@ spec: # 资源规范字段
#dapr.io/sidecar-memory-limit: "800Mi" #Dapr sidecar可以使用的最大内存量。默认情况下未设置 请参阅 https://kubernetes.io/docs/tasks/administer-cluster/manage-resources/quota-memory-cpu-namespace/ 的有效值。
#dapr.io/sidecar-cpu-request: 1 #Dapr sidecar要求的 CPU 数量
#dapr.io/sidecar-memory-request #Dapr sidecar 请求的内存数量
dapr.io/http-max-request-size: "600" #增加http和grpc服务器请求正文参数的最大大小,单位为MB,以处理大文件的上传。 默认值为 4 MB
dapr.io/http-read-buffer-size: "16" #Dapr has a default limit of 4KB for the http header read buffer size. When sending http headers that are bigger than the default 4KB, you can increase this value. Otherwise, you may encounter a Too big request header
dapr.io/app-max-request-size: "600" # 明确指定应用请求大小
dapr.io/http-max-request-size: "600" #增加http和grpc服务器请求正文参数的最大大小,单位为MB,以处理大文件的上传。 默认值为 4 MB
dapr.io/http-read-buffer-size: "4096" #Dapr has a default limit of 4KB for the http header read buffer size. When sending http headers that are bigger than the default 4KB, you can increase this value. Otherwise, you may encounter a Too big request header
#dapr.io/sidecar-listen-addresses: "0.0.0.0" 开通外部访问,仅限测试环境
# 更多dapr配置请参考 https://www.bookstack.cn/read/dapr-1.5-zh/cc77b74e2cc2f4d4.md
......
......@@ -136,25 +136,6 @@ public class TElectronicArchiveTaskController {
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);
}
/**
* 单个新增人员附件
* 前端先调用 /fileUpload/uploadArchiveFile 接口上传文件获取附件ID列表
......
......@@ -34,10 +34,11 @@ public interface ElectronicArchiveImportService {
/**
* 第三步:异步处理上传
*
* @param taskId 任务ID
* @return 处理结果
* @param taskId 任务ID
* @param userId 用户ID
* @param userName 用户名
*/
R<?> asyncProcessUpload(String taskId);
void asyncProcessUpload(String taskId, String userId, String userName);
/**
* 生成任务编号
......
......@@ -22,6 +22,21 @@ public interface FileUploadService {
R<TAttaInfo> uploadFileReturnAtta(MultipartFile file, String filePath, String type, String domainId, String taskId) throws IOException;
/**
* 上传文件并返回附件信息(支持传递用户ID,用于异步场景)
*
* @param file 文件
* @param filePath 文件路径
* @param type 类型
* @param domainId 域ID
* @param taskId 任务ID
* @param userId 用户ID
* @param userName 用户名
* @return 附件信息
* @throws IOException IO异常
*/
R<TAttaInfo> uploadFileReturnAtta(MultipartFile file, String filePath, String type, String domainId, String taskId, String userId, String userName) throws IOException;
R<T> uploadAsso(MultipartFile file, String filePath, String type, String domain, String uploadType);
R<T> uploadAsso(MultipartFile file, String filePath, String type, String domain, String uploadType, String taskId);
......
......@@ -33,9 +33,11 @@ import com.yifu.cloud.plus.v1.yifu.common.security.util.SecurityUtils;
import com.yifu.cloud.plus.v1.yifu.insurances.constants.InsurancesConstants;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.context.annotation.Lazy;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
......@@ -70,14 +72,22 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
private final ArchivesDaprUtil archivesDaprUtil;
private final CacheManager cacheManager;
private final OSSUtil ossUtil;
private final SysConfigLimitMapper configLimitMapper;
/**
* 自注入代理对象,用于解决@Async注解在类内部调用时失效的问题
*
* @Lazy 延迟加载,避免循环依赖
*/
@Autowired
@Lazy
private ElectronicArchiveImportService self;
//初始化zip导入上限值
private int maxLimit = 1;
private AtomicInteger atomicInteger = new AtomicInteger(0);
private final SysConfigLimitMapper configLimitMapper;
/**
* 第一步:导入Excel模板文件
......@@ -95,7 +105,7 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
ExcelUtil<ArchiveImportVO> util = new ExcelUtil<>(ArchiveImportVO.class);
List<ErrorMessage<ArchiveImportVO>> errorMessageList = new ArrayList<>();
// 将输入流转换为字节数组,以便后续上传到OSS
byte[] fileBytes;
try {
......@@ -104,7 +114,7 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
log.error("读取文件流失败", e);
return R.failed("读取文件失败:" + e.getMessage());
}
try {
// 使用字节数组创建新的输入流用于Excel读取
InputStream excelInputStream = new java.io.ByteArrayInputStream(fileBytes);
......@@ -163,15 +173,15 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
taskService.save(task);
// 保存任务表附件
saveTaskAttachment(task.getId(), fileName, inputStream, "task_file"+"/" + task.getId() + "_" + fileName);
saveTaskAttachment(task.getId(), fileName, inputStream, "task_file" + "/" + task.getId() + "_" + fileName);
// 上传文件到阿里云OSS根目录
uploadFileToOSS("task_file"+"/" + task.getId() + "_" + fileName, fileBytes);
uploadFileToOSS("task_file" + "/" + task.getId() + "_" + fileName, fileBytes);
// 建立任务和档案详情的关联关系
createTaskArchiveRelations(task.getId(), successList);
return R.ok(task.getId(),"全部校验通过");
return R.ok(task.getId(), "全部校验通过");
} catch (Exception e) {
log.error("导入Excel文件失败", e);
......@@ -188,7 +198,7 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
TEmployeeInfo empInfo;
TSettleDomainSelectVo settleResult;
R<TEmployeeInfo> empResult;
Map<String,String> map = new HashMap<>();
Map<String, String> map = new HashMap<>();
List<String> deptNoList = new ArrayList<>();
......@@ -210,7 +220,7 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
if (Common.isNotNull(user) && isCheck) {
if (Common.isEmpty(deptNoList) || !deptNoList.contains(data.getDeptNo())) {
ErrorMessage<ArchiveImportVO> errorMessage = new ErrorMessage<>(data.getRowIndex()
,InsurancesConstants.DEPT_NO_NOT_IN_USER_DEPT_LIST,data);
, InsurancesConstants.DEPT_NO_NOT_IN_USER_DEPT_LIST, data);
errorMessageList.add(errorMessage);
continue;
}
......@@ -219,7 +229,7 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
String uniqueKey = data.getEmpIdcard() + "_" + data.getEmpName();
if (null != map.get(uniqueKey)) {
ErrorMessage<ArchiveImportVO> errorMessage = new ErrorMessage<>(data.getRowIndex()
,"第" + data.getRowIndex() + "行:姓名和身份证信息存在重复数据",data);
, "第" + data.getRowIndex() + "行:姓名和身份证信息存在重复数据", data);
errorMessageList.add(errorMessage);
continue;
} else {
......@@ -230,7 +240,7 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
settleResult = archivesDaprUtil.selectDeptByNo(data.getDeptNo());
if (Common.isEmpty(settleResult) || Common.isEmpty(settleResult.getCustomerCode())) {
ErrorMessage<ArchiveImportVO> errorMessage = new ErrorMessage<>(data.getRowIndex()
,"第" + data.getRowIndex() + "行:项目编码[" + data.getDeptNo() + "]信息不存在",data);
, "第" + data.getRowIndex() + "行:项目编码[" + data.getDeptNo() + "]信息不存在", data);
errorMessageList.add(errorMessage);
continue;
}
......@@ -242,7 +252,7 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
empResult = archivesDaprUtil.getTEmployeeInfoByIdcardAndName(vo);
if (empResult == null || empResult.getData() == null) {
ErrorMessage<ArchiveImportVO> errorMessage = new ErrorMessage<>(data.getRowIndex()
,"第" + data.getRowIndex() + "行:人员档案信息不存在",data);
, "第" + data.getRowIndex() + "行:人员档案信息不存在", data);
errorMessageList.add(errorMessage);
continue;
}
......@@ -271,12 +281,12 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
detail.setStatus(ArchiveConstants.DETAIL_STATUS_DISABLED);
detail.setDeleteFlag(CommonConstants.ZERO_STRING);
// 设置档案所在地(需要从empInfo中获取)
detail.setFileProvince(empInfo.getFileProvince());
detail.setFileCity(empInfo.getFileCity());
detail.setFileTown(empInfo.getFileTown());
detail.setFileAddress(buildFileAddress(empInfo.getFileProvince(), empInfo.getFileCity(), empInfo.getFileTown()));
detailService.save(detail);
// 设置档案所在地(需要从empInfo中获取)
detail.setFileProvince(empInfo.getFileProvince());
detail.setFileCity(empInfo.getFileCity());
detail.setFileTown(empInfo.getFileTown());
detail.setFileAddress(buildFileAddress(empInfo.getFileProvince(), empInfo.getFileCity(), empInfo.getFileTown()));
detailService.save(detail);
}
successList.add(data);
......@@ -289,7 +299,7 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
/**
* 保存任务表附件
*/
private void saveTaskAttachment(String taskId, String fileName, InputStream inputStream,String filePath) {
private void saveTaskAttachment(String taskId, String fileName, InputStream inputStream, String filePath) {
try {
TAttaInfo attaInfo = new TAttaInfo();
attaInfo.setAttaName(fileName);
......@@ -307,16 +317,16 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
/**
* 上传文件到阿里云OSS
*
* @param fileName 文件名
*
* @param fileName 文件名
* @param fileBytes 文件字节数组
*/
private void uploadFileToOSS(String fileName, byte[] fileBytes) {
try {
// 使用字节数组创建输入流
InputStream ossInputStream = new java.io.ByteArrayInputStream(fileBytes);
// 上传到OSS根目录
ossUtil.uploadFileByStream(ossInputStream, fileName, "yf-hr-efile-source");
......@@ -328,22 +338,22 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
/**
* 获取项目权限
*
* @author huych
* @param userId
* @return {@link List< String>}
* @author huych
*/
public List<String> getDeptNoList(String userId){
public List<String> getDeptNoList(String userId) {
List<String> deptList = new ArrayList<>(16);
try{
try {
R<TSettleDomainListVo> settleDomainList = archivesDaprUtil.getSettleDomainIdsByUserId(userId);
if(Common.isNotNull(settleDomainList) && settleDomainList.getCode() == CommonConstants.SUCCESS){
if (Common.isNotNull(settleDomainList) && settleDomainList.getCode() == CommonConstants.SUCCESS) {
TSettleDomainListVo data = settleDomainList.getData();
if (Optional.ofNullable(data).isPresent()){
if (Optional.ofNullable(data).isPresent()) {
deptList = data.getDeptNos();
}
}
}catch (Exception e){
log.error("查询项目信息出错:"+e);
} catch (Exception e) {
log.error("查询项目信息出错:" + e);
}
return deptList;
}
......@@ -359,7 +369,7 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
String uniqueKey = data.getEmpIdcard() + "_" + data.getEmpName() + "_" + data.getDeptNo();
uniqueMap.put(uniqueKey, data);
}
// 为每个唯一的人员创建关联关系
for (ArchiveImportVO data : uniqueMap.values()) {
// 查找对应的电子档案明细
......@@ -370,7 +380,7 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
.eq(TElectronicArchiveDetail::getDeleteFlag, CommonConstants.ZERO_STRING)
.last(CommonConstants.LAST_ONE_SQL);
TElectronicArchiveDetail detail = detailService.getOne(wrapper);
if (detail != null) {
// 创建关联记录
TTaskArchiveRelation relation = new TTaskArchiveRelation();
......@@ -420,7 +430,7 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
// 3. 校验同时上传任务数
maxLimit = configLimitMapper.getSysConfigLimitByKey("UPDOLOAD_MAX_LIMIT");
if (atomicInteger.incrementAndGet() > maxLimit){
if (atomicInteger.incrementAndGet() > maxLimit) {
atomicInteger.decrementAndGet();
return R.failed("有其他客服正在上传中,请5分钟后重试");
}
......@@ -436,7 +446,7 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
if (!tempDir.exists() && !tempDir.mkdirs()) {
throw new IOException("Failed to create directory: " + tempDirPath);
}
tempZipFile = new File(tempDir, file.getOriginalFilename());
file.transferTo(tempZipFile);
......@@ -451,8 +461,9 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
zipAtta.setAttaSrc(tempZipFile.getAbsolutePath()); // 临时存储文件路径
attaInfoService.save(zipAtta);
// 调用异步处理
asyncProcessUpload(taskId);
// 调用异步处理(通过self代理对象调用,使@Async生效)
// 传递用户ID和用户名,因为异步线程无法获取用户上下文
self.asyncProcessUpload(taskId, user.getId(), user.getNickname());
return R.ok("ZIP文件提交成功,正在处理中");
......@@ -472,10 +483,14 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
/**
* 第三步:异步处理上传
*
* @param taskId 任务ID
* @param userId 用户ID(用于上传反馈文件)
* @param userName 用户名(用于上传反馈文件)
*/
@Override
@Async
public R<?> asyncProcessUpload(String taskId) {
public void asyncProcessUpload(String taskId, String userId, String userName) {
List<ArchiveFeedbackVO> feedbackList = new ArrayList<>();
File tempDir = null;
......@@ -485,7 +500,8 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
// 查询任务及关联的明细
TElectronicArchiveTask task = taskService.getById(taskId);
if (task == null) {
return R.failed("任务不存在");
log.error("任务不存在,任务ID:{}", taskId);
return;
}
// 查询ZIP附件(从附件导入中获取)
......@@ -495,21 +511,24 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
if (CollUtil.isEmpty(zipAttas)) {
updateTaskStatus(task, ArchiveConstants.TASK_STATUS_NOT_UPLOADED);
return R.failed("ZIP附件不存在");
log.error("ZIP附件不存在,任务ID:{}", taskId);
return;
}
TAttaInfo zipAtta = zipAttas.get(0);
// 从附件记录中获取ZIP文件路径
if (StrUtil.isBlank(zipAtta.getAttaSrc())) {
updateTaskStatus(task, ArchiveConstants.TASK_STATUS_NOT_UPLOADED);
return R.failed("ZIP文件路径为空");
log.error("ZIP文件路径为空,任务ID:{}", taskId);
return;
}
zipSourceFile = new File(zipAtta.getAttaSrc());
if (!zipSourceFile.exists()) {
updateTaskStatus(task, ArchiveConstants.TASK_STATUS_NOT_UPLOADED);
return R.failed("ZIP文件不存在");
log.error("ZIP文件不存在,任务ID:{},文件路径:{}", taskId, zipAtta.getAttaSrc());
return;
}
// 获取任务关联的明细
......@@ -518,7 +537,8 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
if (CollUtil.isEmpty(relations)) {
updateTaskStatus(task, ArchiveConstants.TASK_STATUS_NOT_UPLOADED);
return R.failed("任务没有关联的明细数据");
log.error("任务没有关联的明细数据,任务ID:{}", taskId);
return;
}
List<String> detailIds = relations.stream()
......@@ -527,138 +547,139 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
List<TElectronicArchiveDetail> details = detailService.listByIds(detailIds);
// 创建临时目录用于解压ZIP文件
String tempDirPath = System.getProperty("java.io.tmpdir") + File.separator + "archive_file" + File.separator
+ System.currentTimeMillis() + File.separator;
tempDir = new File(tempDirPath);
if (!tempDir.exists() && !tempDir.mkdirs()) {
updateTaskStatus(task, ArchiveConstants.TASK_STATUS_NOT_UPLOADED);
return R.failed("创建临时目录失败");
}
// 解压ZIP文件
try {
// 验证ZIP文件是否可读
if (!zipSourceFile.canRead()) {
log.error("ZIP文件不可读,路径:{}", zipSourceFile.getAbsolutePath());
// 创建临时目录用于解压ZIP文件
String tempDirPath = System.getProperty("java.io.tmpdir") + File.separator + "archive_file" + File.separator
+ System.currentTimeMillis() + File.separator;
tempDir = new File(tempDirPath);
if (!tempDir.exists() && !tempDir.mkdirs()) {
updateTaskStatus(task, ArchiveConstants.TASK_STATUS_NOT_UPLOADED);
return R.failed("ZIP文件不可读");
log.error("创建临时目录失败,任务ID:{},目录路径:{}", taskId, tempDirPath);
return;
}
// 使用指定字符集解压(支持中文文件名)
// Windows系统通常使用GBK编码,Linux使用UTF-8
// 解压ZIP文件
try {
ZipUtil.unzip(zipSourceFile, tempDir, java.nio.charset.Charset.forName("GBK"));
} catch (Exception e1) {
// 如果GBK失败,尝试UTF-8
ZipUtil.unzip(zipSourceFile, tempDir, java.nio.charset.StandardCharsets.UTF_8);
// 验证ZIP文件是否可读
if (!zipSourceFile.canRead()) {
log.error("ZIP文件不可读,路径:{}", zipSourceFile.getAbsolutePath());
updateTaskStatus(task, ArchiveConstants.TASK_STATUS_NOT_UPLOADED);
return;
}
// 使用指定字符集解压(支持中文文件名)
// Windows系统通常使用GBK编码,Linux使用UTF-8
try {
ZipUtil.unzip(zipSourceFile, tempDir, java.nio.charset.Charset.forName("GBK"));
} catch (Exception e1) {
// 如果GBK失败,尝试UTF-8
ZipUtil.unzip(zipSourceFile, tempDir, java.nio.charset.StandardCharsets.UTF_8);
}
} catch (Exception e) {
log.error("解压ZIP文件失败,任务ID:{},ZIP路径:{},临时目录:{}",
taskId, zipSourceFile.getAbsolutePath(), tempDirPath, e);
updateTaskStatus(task, ArchiveConstants.TASK_STATUS_NOT_UPLOADED);
return;
}
} catch (Exception e) {
log.error("解压ZIP文件失败,任务ID:{},ZIP路径:{},临时目录:{}",
taskId, zipSourceFile.getAbsolutePath(), tempDirPath, e);
updateTaskStatus(task, ArchiveConstants.TASK_STATUS_NOT_UPLOADED);
return R.failed("解压ZIP文件失败:" + e.getMessage() + ",请检查ZIP文件是否完整且格式正确");
}
// 获取ZIP包的根目录(第一级目录,身份证+姓名目录)
File[] zipRootDirs = tempDir.listFiles(File::isDirectory);
Map<String, List<File>> zipDirMap = new HashMap<>();
if (zipRootDirs != null && zipRootDirs.length > 0) {
// 将ZIP中的目录按照 身份证_姓名 建立映射,支持同一个人有多个文件夹
for (File personDir : zipRootDirs) {
String dirName = personDir.getName();
Map<String, String> idNameMap = parseIdCardAndName(dirName);
if (!idNameMap.isEmpty() && idNameMap.containsKey("idCard") && idNameMap.containsKey("name")) {
String uniqueKey = idNameMap.get("idCard") + "_" + idNameMap.get("name");
// 使用List存储,支持同一个人有多个文件夹
zipDirMap.computeIfAbsent(uniqueKey, k -> new ArrayList<>()).add(personDir);
// 获取ZIP包的根目录(第一级目录,身份证+姓名目录)
File[] zipRootDirs = tempDir.listFiles(File::isDirectory);
Map<String, List<File>> zipDirMap = new HashMap<>();
if (zipRootDirs != null && zipRootDirs.length > 0) {
// 将ZIP中的目录按照 身份证_姓名 建立映射,支持同一个人有多个文件夹
for (File personDir : zipRootDirs) {
String dirName = personDir.getName();
Map<String, String> idNameMap = parseIdCardAndName(dirName);
if (!idNameMap.isEmpty() && idNameMap.containsKey("idCard") && idNameMap.containsKey("name")) {
String uniqueKey = idNameMap.get("idCard") + "_" + idNameMap.get("name");
// 使用List存储,支持同一个人有多个文件夹
zipDirMap.computeIfAbsent(uniqueKey, k -> new ArrayList<>()).add(personDir);
}
}
}
}
// 按照第一步Excel导入的明细列表生成反馈表
// 用于记录每个人员的处理结果(一个人可能有多个资料类型)
Map<String, List<String>> personMaterialTypesMap = new HashMap<>();
Map<String, String> personProcessResultMap = new HashMap<>();
Map<String, String> personFailReasonMap = new HashMap<>();
// 按照第一步Excel导入的明细列表生成反馈表
// 用于记录每个人员的处理结果(一个人可能有多个资料类型)
Map<String, List<String>> personMaterialTypesMap = new HashMap<>();
Map<String, String> personProcessResultMap = new HashMap<>();
Map<String, String> personFailReasonMap = new HashMap<>();
// 遍历第一步导入的所有明细
for (TElectronicArchiveDetail detail : details) {
String uniqueKey = detail.getEmpIdcard() + "_" + detail.getEmpName();
// 检查ZIP中是否存在该人员的目录
List<File> personDirs = zipDirMap.get(uniqueKey);
if (personDirs == null || personDirs.isEmpty()) {
// ZIP中没有该人员的文件夹,记录为跳过
personProcessResultMap.put(uniqueKey, ArchiveConstants.PROCESS_RESULT_SKIP);
personFailReasonMap.put(uniqueKey, "压缩包中未找到对应文件夹,跳过处理");
personMaterialTypesMap.put(uniqueKey, new ArrayList<>());
} else {
// ZIP中存在该人员的文件夹,处理其附件(可能有多个文件夹)
List<String> materialTypes = new ArrayList<>();
List<String> failReasons = new ArrayList<>();
// 遍历该人员的所有文件夹
for (File personDir : personDirs) {
processPersonDirectoryWithResult(personDir, detail, materialTypes, failReasons, taskId);
}
personMaterialTypesMap.put(uniqueKey, materialTypes);
// 判断处理结果
if (materialTypes.isEmpty()) {
// 没有成功上传任何资料类型,记录为失败
personProcessResultMap.put(uniqueKey, ArchiveConstants.PROCESS_RESULT_FAILED);
if (failReasons.isEmpty()) {
personFailReasonMap.put(uniqueKey, "文件夹为空或格式不符");
} else {
personFailReasonMap.put(uniqueKey, String.join(";", failReasons));
}
// 遍历第一步导入的所有明细
for (TElectronicArchiveDetail detail : details) {
String uniqueKey = detail.getEmpIdcard() + "_" + detail.getEmpName();
// 检查ZIP中是否存在该人员的目录
List<File> personDirs = zipDirMap.get(uniqueKey);
if (personDirs == null || personDirs.isEmpty()) {
// ZIP中没有该人员的文件夹,记录为跳过
personProcessResultMap.put(uniqueKey, ArchiveConstants.PROCESS_RESULT_SKIP);
personFailReasonMap.put(uniqueKey, "压缩包中未找到对应文件夹,跳过处理");
personMaterialTypesMap.put(uniqueKey, new ArrayList<>());
} else {
// 有成功上传的资料类型
if (failReasons.isEmpty()) {
personProcessResultMap.put(uniqueKey, ArchiveConstants.PROCESS_RESULT_SUCCESS);
personFailReasonMap.put(uniqueKey, "");
} else {
// 部分成功,部分失败
// ZIP中存在该人员的文件夹,处理其附件(可能有多个文件夹)
List<String> materialTypes = new ArrayList<>();
List<String> failReasons = new ArrayList<>();
// 遍历该人员的所有文件夹
for (File personDir : personDirs) {
processPersonDirectoryWithResult(personDir, detail, materialTypes, failReasons, taskId, userId, userName);
}
personMaterialTypesMap.put(uniqueKey, materialTypes);
// 判断处理结果
if (materialTypes.isEmpty()) {
// 没有成功上传任何资料类型,记录为失败
personProcessResultMap.put(uniqueKey, ArchiveConstants.PROCESS_RESULT_FAILED);
personFailReasonMap.put(uniqueKey, String.join(";", failReasons));
if (failReasons.isEmpty()) {
personFailReasonMap.put(uniqueKey, "文件夹为空或格式不符");
} else {
personFailReasonMap.put(uniqueKey, String.join(";", failReasons));
}
} else {
// 有成功上传的资料类型
if (failReasons.isEmpty()) {
personProcessResultMap.put(uniqueKey, ArchiveConstants.PROCESS_RESULT_SUCCESS);
personFailReasonMap.put(uniqueKey, "");
} else {
// 部分成功,部分失败
personProcessResultMap.put(uniqueKey, ArchiveConstants.PROCESS_RESULT_FAILED);
personFailReasonMap.put(uniqueKey, String.join(";", failReasons));
}
}
}
}
}
// 按照第一步Excel的顺序生成反馈表
for (TElectronicArchiveDetail detail : details) {
String uniqueKey = detail.getEmpIdcard() + "_" + detail.getEmpName();
ArchiveFeedbackVO feedback = new ArchiveFeedbackVO();
feedback.setEmpName(detail.getEmpName());
feedback.setEmpIdcard(detail.getEmpIdcard());
feedback.setDeptNo(detail.getDeptNo());
feedback.setDeptName(detail.getDeptName());
// 设置资料类型(多个用逗号拼接)
List<String> materialTypes = personMaterialTypesMap.get(uniqueKey);
if (materialTypes != null && !materialTypes.isEmpty()) {
feedback.setMaterialType(String.join(",", materialTypes));
} else {
feedback.setMaterialType("");
// 按照第一步Excel的顺序生成反馈表
for (TElectronicArchiveDetail detail : details) {
String uniqueKey = detail.getEmpIdcard() + "_" + detail.getEmpName();
ArchiveFeedbackVO feedback = new ArchiveFeedbackVO();
feedback.setEmpName(detail.getEmpName());
feedback.setEmpIdcard(detail.getEmpIdcard());
feedback.setDeptNo(detail.getDeptNo());
feedback.setDeptName(detail.getDeptName());
// 设置资料类型(多个用逗号拼接)
List<String> materialTypes = personMaterialTypesMap.get(uniqueKey);
if (materialTypes != null && !materialTypes.isEmpty()) {
feedback.setMaterialType(String.join(",", materialTypes));
} else {
feedback.setMaterialType("");
}
feedback.setProcessResult(personProcessResultMap.get(uniqueKey));
feedback.setFailReason(personFailReasonMap.get(uniqueKey));
feedbackList.add(feedback);
}
feedback.setProcessResult(personProcessResultMap.get(uniqueKey));
feedback.setFailReason(personFailReasonMap.get(uniqueKey));
feedbackList.add(feedback);
}
// 处理完成,更新任务状态
task.setStatus(ArchiveConstants.TASK_STATUS_GENERATED);
taskService.updateById(task);
// 生成反馈表
generateFeedbackExcel(task, feedbackList);
return R.ok("处理完成");
// 生成反馈表(传递用户信息)
generateFeedbackExcel(task, feedbackList, userId, userName);
log.info("异步任务处理完成,任务ID:{}", taskId);
} catch (Exception e) {
log.error("异步处理任务失败,任务ID:{}", taskId, e);
......@@ -667,7 +688,6 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
if (task != null) {
updateTaskStatus(task, ArchiveConstants.TASK_STATUS_NOT_UPLOADED);
}
return R.failed("处理失败:" + e.getMessage());
} finally {
// 清理临时解压目录
if (tempDir != null && tempDir.exists()) {
......@@ -676,7 +696,7 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
} catch (Exception e) {
}
}
// 清理上传的ZIP文件
if (zipSourceFile != null && zipSourceFile.exists()) {
try {
......@@ -823,15 +843,18 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
/**
* 处理单个人员目录(返回处理结果,用于按Excel顺序生成反馈表)
*
* @param personDir 人员目录
* @param detail 电子档案明细
*
* @param personDir 人员目录
* @param detail 电子档案明细
* @param materialTypes 成功处理的资料类型列表(输出参数)
* @param failReasons 失败原因列表(输出参数)
* @param taskId 任务ID
* @param failReasons 失败原因列表(输出参数)
* @param taskId 任务ID
* @param userId 用户ID
* @param userName 用户名
*/
private void processPersonDirectoryWithResult(File personDir, TElectronicArchiveDetail detail,
List<String> materialTypes, List<String> failReasons, String taskId) {
List<String> materialTypes, List<String> failReasons, String taskId,
String userId, String userName) {
// 处理第三级目录(资料类型或直接是文件)
File[] subFiles = personDir.listFiles();
if (subFiles == null || subFiles.length == 0) {
......@@ -842,7 +865,7 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
// 用于记录该人员的所有资料类型
Set<String> materialTypesSet = new HashSet<>();
// 收集直接文件(作为存量类型处理)
List<File> stockFiles = new ArrayList<>();
......@@ -866,23 +889,23 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
// 不是有效的资料类型,跳过
continue;
}
// 按资料类型分组处理目录下的所有文件
String result = processMaterialTypeDirectoryWithResult(subFile, detail, materialType, taskId);
if ("success".equals(result)) {
materialTypesSet.add(materialType);
} else {
failReasons.add(materialType + ":" + result);
}
// 按资料类型分组处理目录下的所有文件
String result = processMaterialTypeDirectoryWithResult(subFile, detail, materialType, taskId, userId, userName);
if ("success".equals(result)) {
materialTypesSet.add(materialType);
} else {
failReasons.add(materialType + ":" + result);
}
} else {
// 直接是文件,收集起来统一作为存量类型处理
stockFiles.add(subFile);
}
}
// 统一处理所有直接文件(存量类型)
if (!stockFiles.isEmpty()) {
String result = processMaterialTypeFilesWithResult(stockFiles, detail, ArchiveConstants.MATERIAL_TYPE_STOCK, taskId);
String result = processMaterialTypeFilesWithResult(stockFiles, detail, ArchiveConstants.MATERIAL_TYPE_STOCK, taskId, userId, userName);
if ("success".equals(result)) {
materialTypesSet.add(ArchiveConstants.MATERIAL_TYPE_STOCK);
} else {
......@@ -902,47 +925,49 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
* 同一资料类型下的所有文件作为一个整体处理:先验证所有文件,全部通过后才统一上传
*/
private void processMaterialTypeDirectory(File directory, TElectronicArchiveDetail detail,
String materialType, List<ArchiveFeedbackVO> feedbackList,
Set<String> materialTypes, String taskId) {
String materialType, List<ArchiveFeedbackVO> feedbackList,
Set<String> materialTypes, String taskId) {
// 收集该资料类型目录下的所有文件(递归收集子目录中的文件)
List<File> allFiles = new ArrayList<>();
collectAllFiles(directory, allFiles);
if (allFiles.isEmpty()) {
// 文件夹为空
ArchiveFeedbackVO feedback = createFeedbackVO(detail, materialType,
ArchiveFeedbackVO feedback = createFeedbackVO(detail, materialType,
ArchiveConstants.PROCESS_RESULT_FAILED, "文件夹为空");
feedbackList.add(feedback);
return;
}
// 统一处理该资料类型下的所有文件
processMaterialTypeFiles(allFiles, detail, materialType, feedbackList, materialTypes, taskId);
}
/**
* 按资料类型处理目录下的所有文件(返回处理结果)
*
* @param directory 资料类型目录
* @param detail 电子档案明细
*
* @param directory 资料类型目录
* @param detail 电子档案明细
* @param materialType 资料类型
* @param taskId 任务ID
* @param taskId 任务ID
* @param userId 用户ID
* @param userName 用户名
* @return "success" 表示成功,其他表示失败原因
*/
private String processMaterialTypeDirectoryWithResult(File directory, TElectronicArchiveDetail detail,
String materialType, String taskId) {
String materialType, String taskId, String userId, String userName) {
// 收集该资料类型目录下的所有文件(递归收集子目录中的文件)
List<File> allFiles = new ArrayList<>();
collectAllFiles(directory, allFiles);
if (allFiles.isEmpty()) {
return "文件夹为空";
}
// 统一处理该资料类型下的所有文件
return processMaterialTypeFilesWithResult(allFiles, detail, materialType, taskId);
return processMaterialTypeFilesWithResult(allFiles, detail, materialType, taskId, userId, userName);
}
/**
* 递归收集目录下的所有文件
*/
......@@ -951,7 +976,7 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
if (files == null || files.length == 0) {
return;
}
for (File file : files) {
if (file.isDirectory()) {
// 递归收集子目录中的文件
......@@ -961,7 +986,7 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
}
}
}
/**
* 统一处理同一资料类型下的所有文件
* 1. 先验证所有文件格式,如果有一个失败则整体失败
......@@ -969,68 +994,68 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
* 3. 只生成一条反馈记录
*/
private void processMaterialTypeFiles(List<File> files, TElectronicArchiveDetail detail,
String materialType, List<ArchiveFeedbackVO> feedbackList,
Set<String> materialTypes, String taskId) {
String materialType, List<ArchiveFeedbackVO> feedbackList,
Set<String> materialTypes, String taskId) {
// 第一步:验证所有文件格式
for (File file : files) {
String fileName = file.getName();
if (checkFileFormat(fileName)) {
// 有文件格式不符,整体失败,不再处理后续文件
ArchiveFeedbackVO feedback = createFeedbackVO(detail, materialType,
ArchiveConstants.PROCESS_RESULT_FAILED,
ArchiveFeedbackVO feedback = createFeedbackVO(detail, materialType,
ArchiveConstants.PROCESS_RESULT_FAILED,
"文件格式不符,文件格式需为gif|jpg|jpeg|png|bmp|xls|xlsx|csv|pdf|docx|doc|m4a|mp3|zip|rar|txt:" + fileName);
feedbackList.add(feedback);
return;
}
}
// 第二步:所有文件格式验证通过,统一上传到阿里云
try {
// 构建OSS目录路径
String ossDirectory = buildOssDirectory(detail, materialType);
// 依次上传每个文件
for (File file : files) {
String fileName = file.getName();
// 转换为MultipartFile
MultipartFile multipartFile = fileToMultipartFile(file);
if (multipartFile == null) {
// 文件转换失败,整体失败
ArchiveFeedbackVO feedback = createFeedbackVO(detail, materialType,
ArchiveFeedbackVO feedback = createFeedbackVO(detail, materialType,
ArchiveConstants.PROCESS_RESULT_FAILED, "文件转换失败:" + fileName);
feedbackList.add(feedback);
return;
}
// 上传文件到阿里云OSS,uploadFileReturnAtta会自动生成TAttaInfo记录
R<TAttaInfo> uploadResult = fileUploadService.uploadFileReturnAtta(
multipartFile,
multipartFile,
ossDirectory,
CommonConstants.FOUR_STRING,
detail.getId(),
taskId
);
if (!R.isSuccess(uploadResult)) {
// 上传失败,整体失败
ArchiveFeedbackVO feedback = createFeedbackVO(detail, materialType,
ArchiveConstants.PROCESS_RESULT_FAILED,
ArchiveFeedbackVO feedback = createFeedbackVO(detail, materialType,
ArchiveConstants.PROCESS_RESULT_FAILED,
"文件上传失败:" + fileName + ",原因:" + (uploadResult != null ? uploadResult.getMsg() : "未知错误"));
feedbackList.add(feedback);
return;
}
}
// 第三步:所有文件上传成功,记录资料类型并生成成功反馈
materialTypes.add(materialType);
ArchiveFeedbackVO feedback = createFeedbackVO(detail, materialType,
ArchiveFeedbackVO feedback = createFeedbackVO(detail, materialType,
ArchiveConstants.PROCESS_RESULT_SUCCESS, "");
feedbackList.add(feedback);
} catch (Exception e) {
log.error("上传文件失败,资料类型:{},电子档案详情ID:{},任务ID:{}", materialType, detail.getId(), taskId, e);
ArchiveFeedbackVO feedback = createFeedbackVO(detail, materialType,
ArchiveFeedbackVO feedback = createFeedbackVO(detail, materialType,
ArchiveConstants.PROCESS_RESULT_FAILED, "文件上传异常,原因:" + e.getMessage());
feedbackList.add(feedback);
}
......@@ -1038,15 +1063,17 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
/**
* 统一处理同一资料类型下的所有文件(返回处理结果)
*
* @param files 文件列表
* @param detail 电子档案明细
*
* @param files 文件列表
* @param detail 电子档案明细
* @param materialType 资料类型
* @param taskId 任务ID
* @param taskId 任务ID
* @param userId 用户ID
* @param userName 用户名
* @return "success" 表示成功,其他表示失败原因
*/
private String processMaterialTypeFilesWithResult(List<File> files, TElectronicArchiveDetail detail,
String materialType, String taskId) {
String materialType, String taskId, String userId, String userName) {
// 第一步:验证所有文件格式
for (File file : files) {
String fileName = file.getName();
......@@ -1055,41 +1082,44 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
return "文件格式不符,文件格式需为gif|jpg|jpeg|png|bmp|xls|xlsx|csv|pdf|docx|doc|m4a|mp3|zip|rar|txt:" + fileName;
}
}
// 第二步:所有文件格式验证通过,统一上传到阿里云
try {
// 构建OSS目录路径
String ossDirectory = buildOssDirectory(detail, materialType);
// 依次上传每个文件
for (File file : files) {
String fileName = file.getName();
// 转换为MultipartFile
MultipartFile multipartFile = fileToMultipartFile(file);
if (multipartFile == null) {
// 文件转换失败,整体失败
return "文件转换失败:" + fileName;
}
// 上传文件到阿里云OSS,uploadFileReturnAtta会自动生成TAttaInfo记录
R<TAttaInfo> uploadResult = fileUploadService.uploadFileReturnAtta(
multipartFile,
ossDirectory,
CommonConstants.FOUR_STRING,
detail.getId(),
taskId
);
if (!R.isSuccess(uploadResult)) {
// 上传失败,整体失败
return "文件上传失败:" + fileName + ",原因:" + (uploadResult != null ? uploadResult.getMsg() : "未知错误");
}
// 上传文件到阿里云OSS,uploadFileReturnAtta会自动生成TAttaInfo记录
// 传递用户信息,支持异步场景
R<TAttaInfo> uploadResult = fileUploadService.uploadFileReturnAtta(
multipartFile,
ossDirectory,
CommonConstants.FOUR_STRING,
detail.getId(),
taskId,
userId,
userName
);
if (!R.isSuccess(uploadResult)) {
// 上传失败,整体失败
return "文件上传失败:" + fileName + ",原因:" + (uploadResult != null ? uploadResult.getMsg() : "未知错误");
}
}
// 第三步:所有文件上传成功
return "success";
} catch (Exception e) {
log.error("上传文件失败,资料类型:{},电子档案详情ID:{},任务ID:{}", materialType, detail.getId(), taskId, e);
return "文件上传异常,原因:" + e.getMessage();
......@@ -1102,7 +1132,7 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
*/
private String buildOssDirectory(TElectronicArchiveDetail detail, String materialType) {
// 目录格式:项目编码-姓名-身份证号/资料类型
return detail.getDeptNo() + "-" + detail.getEmpName() + "-" + detail.getEmpIdcard() +
return detail.getDeptNo() + "-" + detail.getEmpName() + "-" + detail.getEmpIdcard() +
"/" + materialType;
}
......@@ -1186,15 +1216,22 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
/**
* 生成反馈表Excel
*
* @param task 任务信息
* @param feedbackList 反馈列表
* @param userId 用户ID
* @param userName 用户名
*/
private void generateFeedbackExcel(TElectronicArchiveTask task, List<ArchiveFeedbackVO> feedbackList) {
private void generateFeedbackExcel(TElectronicArchiveTask task, List<ArchiveFeedbackVO> feedbackList,
String userId, String userName) {
if (CollUtil.isEmpty(feedbackList)) {
log.warn("反馈列表为空,跳过生成反馈表,任务ID:{}", task.getId());
return;
}
try {
// 创建临时文件
String tempFilePath = System.getProperty("java.io.tmpdir") + File.separator +
String tempFilePath = System.getProperty("java.io.tmpdir") + File.separator +
task.getTaskCode() + "结果反馈表" + ".xlsx";
File feedbackFile = new File(tempFilePath);
......@@ -1202,29 +1239,36 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
EasyExcel.write(feedbackFile, ArchiveFeedbackVO.class)
.sheet("处理结果反馈")
.doWrite(feedbackList);
log.info("反馈表Excel文件生成成功,文件路径:{},记录数:{}", tempFilePath, feedbackList.size());
// 上传反馈表到OSS
YifuUser user = SecurityUtils.getUser();
if (user != null) {
// 上传反馈表到OSS(传递用户信息)
if (StrUtil.isNotBlank(userId)) {
R<TAttaInfo> uploadResult = fileUploadService.uploadFileReturnAtta(
fileToMultipartFile(feedbackFile),
"archive/feedback",
ArchiveConstants.ATTA_RELATION_TYPE_FEEDBACK,
task.getId()
task.getId(),
null, // taskId为null
userId,
userName
);
if (R.isSuccess(uploadResult)) {
log.info("反馈表生成成功");
log.info("反馈表上传成功,任务ID:{},附件ID:{}", task.getId(), uploadResult.getData().getId());
} else {
log.error("反馈表上传失败");
log.error("反馈表上传失败,任务ID:{},失败原因:{}", task.getId(), uploadResult.getMsg());
}
} else {
log.error("用户ID为空,无法上传反馈表,任务ID:{}", task.getId());
}
// 删除临时文件
FileUtil.del(feedbackFile);
log.info("临时反馈表文件已删除:{}", tempFilePath);
} catch (Exception e) {
log.error("生成反馈表失败:", e);
log.error("生成反馈表失败,任务ID:{},异常信息:", task.getId(), e);
}
}
......@@ -1534,13 +1578,13 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
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());
detail.setFileAddress(buildFileAddress(empInfo.getFileProvince(), empInfo.getFileCity(), empInfo.getFileTown()));
detailService.save(detail);
detail.setFileProvince(empInfo.getFileProvince());
detail.setFileCity(empInfo.getFileCity());
detail.setFileTown(empInfo.getFileTown());
detail.setFileAddress(buildFileAddress(empInfo.getFileProvince(), empInfo.getFileCity(), empInfo.getFileTown()));
detailService.save(detail);
} else {
} else {
// 存在则更新资料类型(追加)
Set<String> existTypes = new HashSet<>();
if (StrUtil.isNotBlank(detail.getMaterialType())) {
......@@ -1611,10 +1655,10 @@ public class ElectronicArchiveImportServiceImpl implements ElectronicArchiveImpo
/**
* 构建档案所在地地址字符串
* 格式:安徽省/合肥市 或 安徽省/合肥市/庐阳区
*
*
* @param provinceCode 省代码
* @param cityCode 市代码
* @param townCode 区代码
* @param cityCode 市代码
* @param townCode 区代码
* @return 档案所在地地址字符串
*/
private String buildFileAddress(Integer provinceCode, Integer cityCode, Integer townCode) {
......
package com.yifu.cloud.plus.v1.batch.service.impl;
import cn.hutool.core.util.StrUtil;
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;
......@@ -14,17 +15,12 @@ import com.yifu.cloud.plus.v1.yifu.common.core.vo.YifuUser;
import com.yifu.cloud.plus.v1.yifu.common.security.util.SecurityUtils;
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
......@@ -49,6 +45,18 @@ public class FileUploadServiceImpl implements FileUploadService {
@Override
public R<TAttaInfo> uploadFileReturnAtta(MultipartFile file, String filePath, String type, String domainId, String taskId) throws IOException {
// 从当前上下文获取用户信息
YifuUser user = SecurityUtils.getUser();
if (user != null) {
return uploadFileReturnAtta(file, filePath, type, domainId, taskId, user.getId(), user.getNickname());
} else {
return uploadFileReturnAtta(file, filePath, type, domainId, taskId, null, null);
}
}
@Override
public R<TAttaInfo> uploadFileReturnAtta(MultipartFile file, String filePath, String type, String domainId,
String taskId, String userId, String userName) throws IOException {
if (null == file) {
return R.failed("文件删除异常,请重新上传!");
}
......@@ -98,12 +106,23 @@ public class FileUploadServiceImpl implements FileUploadService {
attaInfo.setDomainId(domainId);
attaInfo.setTaskId(taskId);
Set<String> dbAuthsSet = new HashSet<>();
Collection<? extends GrantedAuthority> authorities = AuthorityUtils
.createAuthorityList(dbAuthsSet.toArray(new String[0]));
YifuUser user = SecurityUtils.getUser();
attaInfo.setCreateBy(user.getId());
attaInfo.setCreateName(user.getNickname());
// 优先使用传入的用户信息(支持异步场景)
if (Common.isNotNull(userId) && StrUtil.isNotBlank(userId)) {
attaInfo.setCreateBy(userId);
attaInfo.setCreateName(userName);
} else {
// 从上下文获取(同步场景)
try {
YifuUser user = SecurityUtils.getUser();
if (user != null) {
attaInfo.setCreateBy(user.getId());
attaInfo.setCreateName(user.getNickname());
}
} catch (Exception e) {
log.warn("获取用户上下文失败,附件创建人将为空", e);
}
}
attaInfo = tAttaInfoService.add(attaInfo);
} catch (Exception e) {
log.error("OSS文件上传异常:" + e.getMessage());
......
......@@ -207,10 +207,10 @@ public class TElectronicArchiveDetailServiceImpl extends ServiceImpl<TElectronic
*/
@Override
public void batchDownloadArchives(List<ArchiveBatchDownloadRequestVO> requestList, HttpServletResponse response) {
// 反馈列表
List<ArchiveDownloadFeedbackVO> feedbackList = new ArrayList<>();
// 用于存储需要下载的档案及其附件
Map<String, ArchiveDownloadInfo> downloadMap = new LinkedHashMap<>();
......@@ -220,26 +220,29 @@ public class TElectronicArchiveDetailServiceImpl extends ServiceImpl<TElectronic
List<Long> roleList = user.getClientRoleMap().get(ClientNameConstants.CLIENT_MVP);
if (CommonConstants.ZERO_STRING.equals(user.getSystemFlag()) || (Common.isNotNull(roleList)
&& (roleList.stream().anyMatch(role -> role == 1569585030423158786L)
|| roleList.stream().anyMatch(role -> role == 2003347621962379266L)))) {
|| roleList.stream().anyMatch(role -> role == 2003347621962379266L)))) {
isCheck = false;
}
if (Common.isNotNull(user) && isCheck) {
deptNoList = getDeptNoList(user.getId());
}
try {
//重复数据检测
Map<String,String> map = new HashMap<>();
// 1. 校验请求并查询档案信息
for (ArchiveBatchDownloadRequestVO request : requestList) {
try {
validateAndPrepareDownload(request, downloadMap, feedbackList, isCheck, deptNoList);
validateAndPrepareDownload(request, downloadMap, feedbackList, isCheck, deptNoList, map);
} catch (Exception e) {
log.error("处理下载请求失败,姓名:{},身份证:{},项目编码:{}",
log.error("处理下载请求失败,姓名:{},身份证:{},项目编码:{}",
request.getEmpName(), request.getEmpIdcard(), request.getDeptNo(), e);
feedbackList.add(createFeedbackVO(request, "系统异常:" + e.getMessage(),null));
}
}
// 2. 校验文件总大小是否超过500MB
long totalSize = calculateTotalFileSize(downloadMap);
long maxSize = 500 * 1024 * 1024L; // 500MB
......@@ -247,39 +250,39 @@ public class TElectronicArchiveDetailServiceImpl extends ServiceImpl<TElectronic
log.warn("批量下载文件总大小超过限制,总大小:{}MB,限制:500MB", totalSize / 1024 / 1024);
response.setContentType("application/json;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
String errorMsg = String.format("{\"code\":1,\"msg\":\"下载文件总大小超过限制,当前总大小:%.2fMB,最大允许:500MB\"}",
String errorMsg = String.format("{\"code\":1,\"msg\":\"下载文件总大小超过限制,当前总大小:%.2fMB,最大允许:500MB\"}",
totalSize / 1024.0 / 1024.0);
response.getWriter().write(errorMsg);
return;
}
// 3. 设置响应头
String fileName = "电子档案批量下载_" +
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);
// 更新反馈状态为成功
// 使用materialTypeAttaMap的所有key来匹配,确保所有资料类型的反馈都被更新
Set<String> materialTypes = downloadInfo.getMaterialTypeAttaMap().keySet();
for (ArchiveDownloadFeedbackVO feedback : feedbackList) {
if (feedback.getEmpName().equals(downloadInfo.getDetail().getEmpName()) &&
feedback.getEmpIdcard().equals(downloadInfo.getDetail().getEmpIdcard()) &&
feedback.getDeptNo().equals(downloadInfo.getDetail().getDeptNo()) &&
(materialTypes.contains(feedback.getMaterialType()) || "全部资料类型".equals(feedback.getMaterialType()))) {
feedback.getEmpIdcard().equals(downloadInfo.getDetail().getEmpIdcard()) &&
feedback.getDeptNo().equals(downloadInfo.getDetail().getDeptNo()) &&
(materialTypes.contains(feedback.getMaterialType()) || "全部资料类型".equals(feedback.getMaterialType()))) {
feedback.setDownloadStatus("成功");
feedback.setFailReason("");
}
......@@ -290,22 +293,22 @@ public class TElectronicArchiveDetailServiceImpl extends ServiceImpl<TElectronic
Set<String> materialTypes = downloadInfo.getMaterialTypeAttaMap().keySet();
for (ArchiveDownloadFeedbackVO feedback : feedbackList) {
if (feedback.getEmpName().equals(downloadInfo.getDetail().getEmpName()) &&
feedback.getEmpIdcard().equals(downloadInfo.getDetail().getEmpIdcard()) &&
feedback.getDeptNo().equals(downloadInfo.getDetail().getDeptNo()) &&
(materialTypes.contains(feedback.getMaterialType()) || "全部资料类型".equals(feedback.getMaterialType()))) {
feedback.getEmpIdcard().equals(downloadInfo.getDetail().getEmpIdcard()) &&
feedback.getDeptNo().equals(downloadInfo.getDetail().getDeptNo()) &&
(materialTypes.contains(feedback.getMaterialType()) || "全部资料类型".equals(feedback.getMaterialType()))) {
feedback.setDownloadStatus("失败");
feedback.setFailReason("下载文件失败:" + e.getMessage());
}
}
}
}
// 6. 生成并添加反馈Excel表
addFeedbackExcel(zipOut, feedbackList);
zipOut.finish();
}
} catch (Exception e) {
log.error("批量下载电子档案失败", e);
}
......@@ -345,7 +348,8 @@ public class TElectronicArchiveDetailServiceImpl extends ServiceImpl<TElectronic
Map<String, ArchiveDownloadInfo> downloadMap,
List<ArchiveDownloadFeedbackVO> feedbackList,
boolean isCheck,
List<String> deptNoList) {
List<String> deptNoList,
Map<String,String> map) {
// 1. 根据姓名、身份证、项目编码、项目名称查询电子档案明细
LambdaQueryWrapper<TElectronicArchiveDetail> wrapper = Wrappers.<TElectronicArchiveDetail>lambdaQuery()
......@@ -362,6 +366,24 @@ public class TElectronicArchiveDetailServiceImpl extends ServiceImpl<TElectronic
// 查询档案明细
TElectronicArchiveDetail detail = baseMapper.selectOne(wrapper);
StringBuilder s = new StringBuilder();
s.append(request.getEmpIdcard());
s.append("_");
s.append(request.getDeptNo());
s.append("_");
s.append(request.getDeptName());
s.append("_");
s.append(request.getEmpName());
s.append("_");
s.append(request.getMaterialType());
if (null != map.get(s.toString())) {
feedbackList.add(createFeedbackVO(request, "存在重复的导出人员信息",detail));
return;
}else {
map.put(s.toString(), "1");
}
//项目权限校验
if (isCheck && !deptNoList.contains(request.getDeptNo())) {
feedbackList.add(createFeedbackVO(request, "没有该项目的权限",detail));
......
......@@ -4,6 +4,7 @@ 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.TElectronicArchiveDetailMapper;
......@@ -67,7 +68,9 @@ public class TElectronicArchiveTaskServiceImpl extends ServiceImpl<TElectronicAr
//删除关联关系表
relationService.removeById(relation.getId());
//删除电子档案明细表
detailMapper.deleteById(relation.getArchiveDetailId());
detailMapper.delete(Wrappers.<TElectronicArchiveDetail>query().lambda()
.eq(TElectronicArchiveDetail::getStatus,CommonConstants.ZERO_STRING)
.eq(TElectronicArchiveDetail::getId,relation.getArchiveDetailId()));
}
}
return R.ok();
......
......@@ -241,37 +241,37 @@ public class OSSUtil {
log.error("oss客户端获取错误,无此客户端",clientIndex);
}else{
OSSObject ossObject = null;
for(OSSObjectBean key:srcList){
// ossObject包含文件所在的存储空间名称、文件名称、文件元信息以及一个输入流。
ossObject = client.getObject(bucketName, key.getKey());
if(null != ossObject){
// 读取文件内容。
log.info("Object content:");
inputStream = ossObject.getObjectContent();
//添加到zip
zip.putNextEntry(new ZipEntry(key.getName()));
byte[] buf = new byte[1024];
int len;
while ((len = inputStream.read(buf)) != -1){
zip.write(buf, 0, len);
}
}
// 数据读取完成后,获取的流必须关闭,否则会造成连接泄漏,导致请求无连接可用,程序无法正常工作。
IoUtil.close(inputStream);
zip.closeEntry();
}
}
// 关闭OSSClient。
} catch (OSSException oe) {
ossexceptionlog(oe);
} catch (ClientException ce) {
clientExceptionLog(ce);
} catch (IOException e) {
e.printStackTrace();
} finally {
IoUtil.close(inputStream);
}
}
for(OSSObjectBean key:srcList){
// ossObject包含文件所在的存储空间名称、文件名称、文件元信息以及一个输入流。
ossObject = client.getObject(bucketName, key.getKey());
if(null != ossObject){
// 读取文件内容。
log.info("Object content:");
inputStream = ossObject.getObjectContent();
//添加到zip
zip.putNextEntry(new ZipEntry(key.getName()));
byte[] buf = new byte[1024];
int len;
while ((len = inputStream.read(buf)) != -1){
zip.write(buf, 0, len);
}
}
// 数据读取完成后,获取的流必须关闭,否则会造成连接泄漏,导致请求无连接可用,程序无法正常工作。
IoUtil.close(inputStream);
zip.closeEntry();
}
}
// 关闭OSSClient。
} catch (OSSException oe) {
ossexceptionlog(oe);
} catch (ClientException ce) {
clientExceptionLog(ce);
} catch (IOException e) {
e.printStackTrace();
} finally {
IoUtil.close(inputStream);
}
}
/**
* @author fang
......
......@@ -39,7 +39,7 @@ public interface TInsurancePolicyMapper extends BaseMapper<TInsurancePolicy> {
*/
List<TInsurancePolicy> getInsurancePolicyList(@Param("param") InsurancePolicyListParam param);
Page<PolicyListVo> policyPageToAutoSelect(Page page,@Param("param") InsuredParam param);
IPage<PolicyListVo> policyPageToAutoSelect(Page page,@Param("param") InsuredParam param);
}
......
......@@ -75,5 +75,5 @@ public interface TInsurancePolicyService extends IService<TInsurancePolicy> {
R<List<ErrorMessage>> importInsurancePolicy(InputStream inputStream);
Page<PolicyListVo> policyPageToAutoSelect(Page<PolicyListVo> page, InsuredParam param, String mId);
IPage<PolicyListVo> policyPageToAutoSelect(Page<PolicyListVo> page, InsuredParam param, String mId);
}
......@@ -430,7 +430,7 @@ public class TInsurancePolicyServiceImpl extends ServiceImpl<TInsurancePolicyMap
@Override
public Page<PolicyListVo> policyPageToAutoSelect(Page<PolicyListVo> page, InsuredParam param, String mId) {
public IPage<PolicyListVo> policyPageToAutoSelect(Page<PolicyListVo> page, InsuredParam param, String mId) {
// 查保单未过期的数据
String today = LocalDateUtil.getDateSrt(LocalDate.now());
param.setPolicyEnd(today);
......
......@@ -1295,7 +1295,7 @@
<!-- 派增(0待审核、1待办理、3办理成功、4办理失败9 审核不通过)、派减(5待审核 6待办理 7 办理成功 8 办理失败 ) -->
and a.fund_status in ('0', '1', '3', '5', '6', '8')
<if test="type != null and type == '1'">
<if test="type != null and type = '1'">
and d.create_name not like '自动化%'
</if>
</select>
......
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