外协人员批量上传附件优化

This commit is contained in:
2025-11-21 10:50:28 +08:00
parent 916edefed7
commit 2cb508cebf
11 changed files with 4081 additions and 175 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -29,6 +29,8 @@ import javax.annotation.Resource;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.*; import java.util.*;
@@ -100,138 +102,6 @@ public class OutSidePersonController extends BaseController {
return sqlService.getDataOneByPlus(wrapper); return sqlService.getDataOneByPlus(wrapper);
} }
/**
* 根据压缩文件地址下载、解压文件,并根据文件名(身份证号)更新数据库
*
* 功能说明:
* 1. 根据文件地址支持URL或本地路径下载/读取压缩文件
* 2. 解压缩文件支持zip、rar、7z格式得到文件列表
* 3. 遍历文件列表,每个文件的文件名作为身份证号
* 4. 根据身份证号查询lc_risk_hazard_manage表的cardID字段
* 5. 如果匹配到数据,将文件上传到服务器指定目录
* 6. 更新lc_risk_hazard_manage表的actual_corrective_Attachment字段为文件地址
*
* 使用示例:
* <pre>
* // 方式1使用MyBatis Mapper
* &#64;Autowired
* private RiskHazardManageMapper mapper;
*
* String fileUrl = "http://example.com/files.zip";
* String uploadPath = "/data/uploads";
* int count = FileUtil.processCompressedFileByCardId(fileUrl, uploadPath, mapper);
*
* // 方式2使用JPA Repository需要实现接口
* RiskHazardManageMapper mapper = new RiskHazardManageMapper() {
* &#64;Autowired
* private RiskHazardManageRepository repository;
*
* public RiskHazardManage findByCardId(String cardId) {
* return repository.findByCardId(cardId);
* }
*
* public void updateActualCorrectiveAttachment(String cardId, String fileUrl) {
* RiskHazardManage entity = repository.findByCardId(cardId);
* if (entity != null) {
* entity.setActualCorrectiveAttachment(fileUrl);
* repository.save(entity);
* }
* }
* };
* </pre>
*
* @param fileUrl 压缩文件的地址可以是URL如"http://example.com/file.zip"或本地路径如"/path/to/file.zip"
* @return 处理结果:成功处理的文件数量
* @throws IOException IO异常
*/
public int processCompressedFileByCardId( @RequestBody JSONObject jsonObject) throws IOException {
String fileUrl=jsonObject.get("commitmentAttchment").toString();
if(fileUrl.isEmpty()){
return 0;
}
int successCount = 0;
File tempZipFile = null;
File tempDir = null;
String tableName = "lc_outside_person";
Long detailFormId = dbFormService.getDbFormIdByTableName(tableName);
try {
// 1. 下载文件如果是URL或直接使用本地文件
if (fileUrl.startsWith("http://") || fileUrl.startsWith("https://")) {
// 从URL下载文件
tempZipFile = FileUtil.downloadFileFromUrl(fileUrl);
} else {
// 本地文件路径
tempZipFile = new File(fileUrl);
if (!tempZipFile.exists()) {
throw new FileNotFoundException("文件不存在: " + fileUrl);
}
}
// 2. 创建临时解压目录
String tempDirPath = System.getProperty("java.io.tmpdir") + File.separator +
"unzip_" + System.currentTimeMillis();
tempDir = new File(tempDirPath);
if (!tempDir.exists()) {
tempDir.mkdirs();
}
// 3. 解压缩文件
List<File> fileList = FileUtil.unzipFile(tempZipFile, tempDir);
log.info("解压文件成功,共 {} 个文件", fileList.size());
List<JSONObject> editDataList = new ArrayList<>();
// 4. 遍历文件列表,处理每个文件
for (File file : fileList) {
try {
// 4.1 获取文件名(身份证号),去掉扩展名
String fileName = file.getName();
String cardId = fileName;
// 如果文件名包含扩展名,去掉扩展名
int lastDotIndex = fileName.lastIndexOf('.');
if (lastDotIndex > 0) {
cardId = fileName.substring(0, lastDotIndex);
}
// 4.2 根据身份证号查询数据库
Map<String, Object> entity = getOutSideByCardNo(cardId);
if (entity.isEmpty()) {
log.warn("未找到身份证号对应的记录: {}", cardId);
continue;
}
// 4.3 上传文件到服务器
String uploadPath = uploadFile(file);
entity.put("commitmentAttchment", uploadPath);
editDataList.add(JSONUtil.parseObj(entity));
log.info("处理成功 - 身份证号: {}, 文件: {}", cardId, uploadPath);
successCount++;
} catch (Exception e) {
log.error("处理文件失败: {}", file.getName(), e);
}
}
// 更新数据
super.editJsonData(detailFormId, editDataList);
} finally {
// 清理临时文件
if (tempZipFile != null && tempZipFile.exists() &&
(fileUrl.startsWith("http://") || fileUrl.startsWith("https://"))) {
tempZipFile.delete();
}
if (tempDir != null && tempDir.exists()) {
FileUtil.deleteDirectory(tempDir);
}
}
return successCount;
}
public String uploadFile(File file) throws Exception { public String uploadFile(File file) throws Exception {
String fileName = file.getName(); String fileName = file.getName();
//微信图片_20230905094700.png //微信图片_20230905094700.png
@@ -310,8 +180,10 @@ public class OutSidePersonController extends BaseController {
jsonObject.get("commitmentAttchment").toString() : ""; jsonObject.get("commitmentAttchment").toString() : "";
String secretFileUrl = jsonObject.get("secretAttchment") != null ? String secretFileUrl = jsonObject.get("secretAttchment") != null ?
jsonObject.get("secretAttchment").toString() : ""; jsonObject.get("secretAttchment").toString() : "";
String noCriminalFileUrl = jsonObject.get("noCriminalAttachment") != null ?
jsonObject.get("noCriminalAttachment").toString() : "";
if (commitmentFileUrl.isEmpty() && secretFileUrl.isEmpty()) { if (commitmentFileUrl.isEmpty() && secretFileUrl.isEmpty() && noCriminalFileUrl.isEmpty()) {
return 0; return 0;
} }
@@ -324,12 +196,17 @@ public class OutSidePersonController extends BaseController {
// 处理 commitmentAttchment 压缩包 // 处理 commitmentAttchment 压缩包
if (!commitmentFileUrl.isEmpty()) { if (!commitmentFileUrl.isEmpty()) {
processCompressedFile(commitmentFileUrl, "commitmentAttchment", updateDataMap); processCompressedFile(commitmentFileUrl, "commitmentAttchment", "isSignSafe", updateDataMap);
} }
// 处理 secretAttchment 压缩包 // 处理 secretAttchment 压缩包
if (!secretFileUrl.isEmpty()) { if (!secretFileUrl.isEmpty()) {
processCompressedFile(secretFileUrl, "secretAttchment", updateDataMap); processCompressedFile(secretFileUrl, "secretAttchment", "isSignSecret", updateDataMap);
}
// 处理 noCriminalAttachment 压缩包
if (!noCriminalFileUrl.isEmpty()) {
processCompressedFile(noCriminalFileUrl, "noCriminalAttachment", "isGovExxplain", updateDataMap);
} }
// 将Map转换为List用于批量更新 // 将Map转换为List用于批量更新
@@ -347,10 +224,11 @@ public class OutSidePersonController extends BaseController {
/** /**
* 处理压缩文件的通用方法 * 处理压缩文件的通用方法
* @param fileUrl 压缩文件地址 * @param fileUrl 压缩文件地址
* @param fieldName 要更新的字段名commitmentAttchment secretAttchment * @param fieldName 要更新的字段名commitmentAttchment / secretAttchment / noCriminalAttachment
* @param statusFieldName 要同步的状态字段isSignSafe / isSignSecret / isGovExxplain
* @param updateDataMap 用于存储更新数据的Mapkey为身份证号 * @param updateDataMap 用于存储更新数据的Mapkey为身份证号
*/ */
private void processCompressedFile(String fileUrl, String fieldName, private void processCompressedFile(String fileUrl, String fieldName, String statusFieldName,
Map<String, JSONObject> updateDataMap) { Map<String, JSONObject> updateDataMap) {
File tempZipFile = null; File tempZipFile = null;
File tempDir = null; File tempDir = null;
@@ -358,8 +236,9 @@ public class OutSidePersonController extends BaseController {
try { try {
// 1. 下载文件如果是URL或直接使用本地文件 // 1. 下载文件如果是URL或直接使用本地文件
if (fileUrl.startsWith("http://") || fileUrl.startsWith("https://")) { if (fileUrl.startsWith("http://") || fileUrl.startsWith("https://")) {
// 从URL下载文件 // 从URL下载文件(先编码路径,避免中文/特殊字符导致 400
tempZipFile = FileUtil.downloadFileFromUrl(fileUrl); String encodedUrl = encodeUrl(fileUrl);
tempZipFile = FileUtil.downloadFileFromUrl(encodedUrl);
} else { } else {
// 本地文件路径 // 本地文件路径
tempZipFile = new File(fileUrl); tempZipFile = new File(fileUrl);
@@ -413,8 +292,18 @@ public class OutSidePersonController extends BaseController {
updateDataMap.put(cardIdKey, updateData); updateDataMap.put(cardIdKey, updateData);
} }
// 更新对应的字段 // 更新对应的字段(追加路径)
updateData.put(fieldName, uploadPath); String targetFieldKey = getCaseInsensitiveKey(updateData, fieldName);
String existPath = updateData.getStr(targetFieldKey);
if (existPath == null || existPath.isEmpty()) {
updateData.set(targetFieldKey, uploadPath);
} else {
updateData.set(targetFieldKey, existPath + "," + uploadPath);
}
if (statusFieldName != null && !statusFieldName.isEmpty()) {
updateData.set(statusFieldName, "");
}
log.info("处理成功 [{}] - 身份证号: {}, 文件: {}", fieldName, cardId, uploadPath); log.info("处理成功 [{}] - 身份证号: {}, 文件: {}", fieldName, cardId, uploadPath);
@@ -436,4 +325,29 @@ public class OutSidePersonController extends BaseController {
} }
} }
} }
private String encodeUrl(String rawUrl) {
try {
URL url = new URL(rawUrl);
URI uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(),
url.getPath(), url.getQuery(), url.getRef());
return uri.toASCIIString();
} catch (Exception e) {
log.warn("URL 编码失败,使用原始地址: {}", rawUrl, e);
return rawUrl;
}
}
private String getCaseInsensitiveKey(JSONObject jsonObject, String key) {
if (jsonObject.containsKey(key)) {
return key;
}
for (String existingKey : jsonObject.keySet()) {
if (existingKey != null && existingKey.equalsIgnoreCase(key)) {
return existingKey;
}
}
// 默认返回原 key保证 put 时能插入
return key;
}
} }

View File

@@ -65,14 +65,15 @@ public class RiskHazardController extends BaseController {
@GetMapping({"/syncData2RiskHazard"}) @GetMapping({"/syncData2RiskHazard"})
@ApiOperationSupport(order = 1) @ApiOperationSupport(order = 1)
@Operation(summary = "同步数据") @Operation(summary = "同步数据")
public void SyncData2RiskHazard() public void SyncData2RiskHazard() {
{
// 报表 code // 报表 code
String reportCode = "ali_risk_data"; String reportCode = "ali_risk_data";
String syncTableName = "lc_report_data_sync"; String syncTableName = "lc_report_data_sync";
String syncDetailTableName = "lc_report_data_sync_detail"; String syncDetailTableName = "lc_report_data_sync_detail";
String riskTableName = "lc_risk_hazard_manage"; String riskTableName = "lc_risk_hazard_manage";
String campusTableName = "campus_info"; String campusTableName = "campus_info";
Long dbFormId = formService.getDbFormIdByTableName(syncTableName); Long dbFormId = formService.getDbFormIdByTableName(syncTableName);
Long detailFormId = formService.getDbFormIdByTableName(syncDetailTableName); Long detailFormId = formService.getDbFormIdByTableName(syncDetailTableName);
Long riskFormId = formService.getDbFormIdByTableName(riskTableName); Long riskFormId = formService.getDbFormIdByTableName(riskTableName);
@@ -88,45 +89,65 @@ public class RiskHazardController extends BaseController {
Map<String, Object> param3 = new HashMap<>(); Map<String, Object> param3 = new HashMap<>();
ResultDataModel allCampus = super.getDataPage(campusFormId, param3); ResultDataModel allCampus = super.getDataPage(campusFormId, param3);
if (CollectionUtil.isNotEmpty(unSyncs.getRecords())) // 可选优化:提前把 campus 列表转成 Map<campus_id, campus_name>,避免每次 stream 过滤
{ Map<String, Object> campusMap = new HashMap<>();
List<JSONObject> syncList = new ArrayList<>(); if (CollectionUtil.isNotEmpty(allCampus.getRecords())) {
List<JSONObject> syncDetailList = new ArrayList<>(); for (Map<String, Object> campus : allCampus.getRecords()) {
Object campusIdObj = campus.get("campus_id");
if (campusIdObj != null) {
campusMap.put(campusIdObj.toString(), campus.get("campus_name"));
}
}
}
if (CollectionUtil.isNotEmpty(unSyncs.getRecords())) {
List<JSONObject> syncList = new ArrayList<>(); // 主表需要更新的记录
List<JSONObject> syncDetailList = new ArrayList<>(); // 明细表需要更新的记录
for (Map<String, Object> unSync : unSyncs.getRecords()) { for (Map<String, Object> unSync : unSyncs.getRecords()) {
// 根据 同步表主表Id 获取所有未同步的明细表数据 // 根据 同步表主表Id 获取所有未同步的明细表数据
Map<String, Object> param2 = new HashMap<>(); Map<String, Object> param2 = new HashMap<>();
param2.put("syncId", unSync.get("id")); param2.put("syncId", unSync.get("id"));
param2.put("isSync", 0); param2.put("isSync", 0);
ResultDataModel unSyncDetails = super.getDataPage(detailFormId, param2); ResultDataModel unSyncDetails = super.getDataPage(detailFormId, param2);
if (CollectionUtil.isNotEmpty(unSyncDetails.getRecords())) { if (CollectionUtil.isNotEmpty(unSyncDetails.getRecords())) {
List<JSONObject> newRisks=new ArrayList<>();
// 新增和更新分开两个 list
List<JSONObject> riskInsertList = new ArrayList<>();
List<JSONObject> riskUpdateList = new ArrayList<>();
for (Map<String, Object> data : unSyncDetails.getRecords()) { for (Map<String, Object> data : unSyncDetails.getRecords()) {
Object result = data.get("resultData"); Object result = data.get("resultData");
JSONObject sourceData = result instanceof JSONObject JSONObject sourceData = result instanceof JSONObject
? (JSONObject) result ? (JSONObject) result
: JSONUtil.parseObj(String.valueOf(result)); : JSONUtil.parseObj(String.valueOf(result));
// 取 sourceid 作为唯一标识
String sourceId = Func.getMap2Str(sourceData, "sourceid");
// 生成 风险隐患表数据 // 生成 风险隐患表数据
HashMap<String, Object> newRisk = new HashMap<>(); HashMap<String, Object> newRisk = new HashMap<>();
newRisk.put("sync_Id", data.get("id")); newRisk.put("sync_Id", data.get("id"));
newRisk.put("source_Id", sourceData.get("sourceid")); newRisk.put("source_Id", sourceId);
newRisk.put("billNo", sourceData.get("work_order_id")); newRisk.put("billNo", sourceData.get("work_order_id"));
newRisk.put("source", 2); // 来源 默认线上 newRisk.put("source", 2); // 来源 默认线上
newRisk.put("company_name", sourceData.get("company_name")); newRisk.put("company_name", sourceData.get("company_name"));
newRisk.put("company_id", sourceData.get("company_id")); newRisk.put("company_id", sourceData.get("company_id"));
newRisk.put("parkId", sourceData.get("campus_id")); newRisk.put("parkId", sourceData.get("campus_id"));
// 设置 园区名称
// 设置 园区名称(使用预构建的 campusMap
if (sourceData.containsKey("campus_id")) { if (sourceData.containsKey("campus_id")) {
String campusId = Func.getMap2Str(sourceData, "campus_id"); String campusId = Func.getMap2Str(sourceData, "campus_id");
// 根据name查找id Object campusName = campusMap.get(campusId);
Map<String, Object> depMap = allCampus.getRecords().stream() if (Objects.nonNull(campusName)) {
.filter(t -> t.get("campus_id").toString() newRisk.put("parkName", campusName);
.equals(campusId)).findFirst().orElse(null);
if (Objects.nonNull(depMap)) {
newRisk.put("parkName", depMap.get("campus_name"));
} }
} }
//newRisk.put("check_area", sourceData.get("check_area")); //newRisk.put("check_area", sourceData.get("check_area"));
//newRisk.put("check_item", sourceData.get("check_item")); //newRisk.put("check_item", sourceData.get("check_item"));
newRisk.put("check_content", sourceData.get("item_name")); newRisk.put("check_content", sourceData.get("item_name"));
@@ -140,25 +161,50 @@ public class RiskHazardController extends BaseController {
newRisk.put("corrective_confirm_people", sourceData.get("confirm_person_name")); newRisk.put("corrective_confirm_people", sourceData.get("confirm_person_name"));
newRisk.put("remarks", sourceData.get("remark")); newRisk.put("remarks", sourceData.get("remark"));
newRisks.add(JSONUtil.parseObj(newRisk)); // ======= 关键逻辑:根据 source_Id 先查 lc_risk_hazard_manage存在则更新不存在则插入 =======
// 更新明细表为已同步
Map<String, Object> riskQueryParam = new HashMap<>();
riskQueryParam.put("source_Id", sourceId);
ResultDataModel existRisk = super.getDataPage(riskFormId, riskQueryParam);
if (CollectionUtil.isNotEmpty(existRisk.getRecords())) {
// 已存在记录:取第一条记录的 id放入 newRisk加入更新列表
Map<String, Object> exist = existRisk.getRecords().get(0);
newRisk.put("id", exist.get("id"));
riskUpdateList.add(JSONUtil.parseObj(newRisk));
} else {
// 不存在:加入新增列表
riskInsertList.add(JSONUtil.parseObj(newRisk));
}
// ======= 同步明细表状态:置为已同步 =======
data.put("isSync", 1); data.put("isSync", 1);
syncDetailList.add(JSONUtil.parseObj(data)); syncDetailList.add(JSONUtil.parseObj(data));
} }
// 插入新的 风险隐患数据
super.addJsonData(riskFormId,newRisks); // 批量插入新的 风险隐患数据
if (CollectionUtil.isNotEmpty(riskInsertList)) {
super.addJsonData(riskFormId, riskInsertList);
} }
// 批量更新已存在的 风险隐患数据
if (CollectionUtil.isNotEmpty(riskUpdateList)) {
super.editJsonData(riskFormId, riskUpdateList);
}
}
// 更新主表为已同步 // 更新主表为已同步
unSync.put("isSync", 1); unSync.put("isSync", 1);
syncList.add(JSONUtil.parseObj(unSync)); syncList.add(JSONUtil.parseObj(unSync));
}
// 更新同步数据表和同步数据明细表 // 批量更新同步主表和明细表
if (CollectionUtil.isNotEmpty(syncDetailList)) {
super.editJsonData(detailFormId, syncDetailList); super.editJsonData(detailFormId, syncDetailList);
} }
// 更新同步数据表和同步数据明细表 if (CollectionUtil.isNotEmpty(syncList)) {
super.editJsonData(dbFormId, syncList); super.editJsonData(dbFormId, syncList);
} }
} }
}
} }

View File

@@ -1,12 +1,15 @@
package com.jeelowcode.module.biz.service; package com.jeelowcode.module.biz.service;
import com.jeelowcode.tool.framework.datapermission.core.annotation.DataPermission;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* Demo相关 * Demo相关
*/ */
public interface IWorkItemService { public interface IWorkItemService {
Map<String, Object> getWorkItemById(long id); Map<String, Object> getWorkItemById(long id);

View File

@@ -20,6 +20,7 @@ import com.jeelowcode.service.bpm.config.framework.portal.core.dto.ReceiveReques
import com.jeelowcode.service.system.api.IApiAdminUserApi; import com.jeelowcode.service.system.api.IApiAdminUserApi;
import com.jeelowcode.service.system.dto.AdminUserRespDTO; import com.jeelowcode.service.system.dto.AdminUserRespDTO;
import com.jeelowcode.tool.framework.common.util.object.BeanUtils; import com.jeelowcode.tool.framework.common.util.object.BeanUtils;
import com.jeelowcode.tool.framework.datapermission.core.annotation.DataPermission;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -33,6 +34,7 @@ import java.util.*;
*/ */
@Slf4j @Slf4j
@Service @Service
@DataPermission(enable=false)
public class WorkItemServiceImpl implements IWorkItemService { public class WorkItemServiceImpl implements IWorkItemService {