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

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

View File

@@ -29,6 +29,8 @@ import javax.annotation.Resource;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
@@ -100,138 +102,6 @@ public class OutSidePersonController extends BaseController {
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 {
String fileName = file.getName();
//微信图片_20230905094700.png
@@ -310,8 +180,10 @@ public class OutSidePersonController extends BaseController {
jsonObject.get("commitmentAttchment").toString() : "";
String secretFileUrl = jsonObject.get("secretAttchment") != null ?
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;
}
@@ -324,12 +196,17 @@ public class OutSidePersonController extends BaseController {
// 处理 commitmentAttchment 压缩包
if (!commitmentFileUrl.isEmpty()) {
processCompressedFile(commitmentFileUrl, "commitmentAttchment", updateDataMap);
processCompressedFile(commitmentFileUrl, "commitmentAttchment", "isSignSafe", updateDataMap);
}
// 处理 secretAttchment 压缩包
if (!secretFileUrl.isEmpty()) {
processCompressedFile(secretFileUrl, "secretAttchment", updateDataMap);
processCompressedFile(secretFileUrl, "secretAttchment", "isSignSecret", updateDataMap);
}
// 处理 noCriminalAttachment 压缩包
if (!noCriminalFileUrl.isEmpty()) {
processCompressedFile(noCriminalFileUrl, "noCriminalAttachment", "isGovExxplain", updateDataMap);
}
// 将Map转换为List用于批量更新
@@ -347,10 +224,11 @@ public class OutSidePersonController extends BaseController {
/**
* 处理压缩文件的通用方法
* @param fileUrl 压缩文件地址
* @param fieldName 要更新的字段名commitmentAttchment secretAttchment
* @param fieldName 要更新的字段名commitmentAttchment / secretAttchment / noCriminalAttachment
* @param statusFieldName 要同步的状态字段isSignSafe / isSignSecret / isGovExxplain
* @param updateDataMap 用于存储更新数据的Mapkey为身份证号
*/
private void processCompressedFile(String fileUrl, String fieldName,
private void processCompressedFile(String fileUrl, String fieldName, String statusFieldName,
Map<String, JSONObject> updateDataMap) {
File tempZipFile = null;
File tempDir = null;
@@ -358,8 +236,9 @@ public class OutSidePersonController extends BaseController {
try {
// 1. 下载文件如果是URL或直接使用本地文件
if (fileUrl.startsWith("http://") || fileUrl.startsWith("https://")) {
// 从URL下载文件
tempZipFile = FileUtil.downloadFileFromUrl(fileUrl);
// 从URL下载文件(先编码路径,避免中文/特殊字符导致 400
String encodedUrl = encodeUrl(fileUrl);
tempZipFile = FileUtil.downloadFileFromUrl(encodedUrl);
} else {
// 本地文件路径
tempZipFile = new File(fileUrl);
@@ -413,8 +292,18 @@ public class OutSidePersonController extends BaseController {
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);
@@ -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"})
@ApiOperationSupport(order = 1)
@Operation(summary = "同步数据")
public void SyncData2RiskHazard()
{
public void SyncData2RiskHazard() {
// 报表 code
String reportCode="ali_risk_data";
String syncTableName="lc_report_data_sync";
String syncDetailTableName="lc_report_data_sync_detail";
String riskTableName="lc_risk_hazard_manage";
String campusTableName="campus_info";
String reportCode = "ali_risk_data";
String syncTableName = "lc_report_data_sync";
String syncDetailTableName = "lc_report_data_sync_detail";
String riskTableName = "lc_risk_hazard_manage";
String campusTableName = "campus_info";
Long dbFormId = formService.getDbFormIdByTableName(syncTableName);
Long detailFormId = formService.getDbFormIdByTableName(syncDetailTableName);
Long riskFormId = formService.getDbFormIdByTableName(riskTableName);
@@ -88,45 +89,65 @@ public class RiskHazardController extends BaseController {
Map<String, Object> param3 = new HashMap<>();
ResultDataModel allCampus = super.getDataPage(campusFormId, param3);
if (CollectionUtil.isNotEmpty(unSyncs.getRecords()))
{
List<JSONObject> syncList = new ArrayList<>();
List<JSONObject> syncDetailList = new ArrayList<>();
// 可选优化:提前把 campus 列表转成 Map<campus_id, campus_name>,避免每次 stream 过滤
Map<String, Object> campusMap = new HashMap<>();
if (CollectionUtil.isNotEmpty(allCampus.getRecords())) {
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()) {
// 根据 同步表主表Id 获取所有未同步的明细表数据
Map<String, Object> param2 = new HashMap<>();
param2.put("syncId", unSync.get("id"));
param2.put("isSync", 0);
ResultDataModel unSyncDetails = super.getDataPage(detailFormId, param2);
if(CollectionUtil.isNotEmpty(unSyncDetails.getRecords())) {
List<JSONObject> newRisks=new ArrayList<>();
if (CollectionUtil.isNotEmpty(unSyncDetails.getRecords())) {
// 新增和更新分开两个 list
List<JSONObject> riskInsertList = new ArrayList<>();
List<JSONObject> riskUpdateList = new ArrayList<>();
for (Map<String, Object> data : unSyncDetails.getRecords()) {
Object result = data.get("resultData");
JSONObject sourceData = result instanceof JSONObject
? (JSONObject) result
: JSONUtil.parseObj(String.valueOf(result));
// 取 sourceid 作为唯一标识
String sourceId = Func.getMap2Str(sourceData, "sourceid");
// 生成 风险隐患表数据
HashMap<String, Object> newRisk = new HashMap<>();
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("source", 2); //来源 默认线上
newRisk.put("source", 2); // 来源 默认线上
newRisk.put("company_name", sourceData.get("company_name"));
newRisk.put("company_id", sourceData.get("company_id"));
newRisk.put("parkId", sourceData.get("campus_id"));
// 设置 园区名称
if(sourceData.containsKey("campus_id")) {
// 设置 园区名称(使用预构建的 campusMap
if (sourceData.containsKey("campus_id")) {
String campusId = Func.getMap2Str(sourceData, "campus_id");
// 根据name查找id
Map<String, Object> depMap = allCampus.getRecords().stream()
.filter(t -> t.get("campus_id").toString()
.equals(campusId)).findFirst().orElse(null);
if (Objects.nonNull(depMap)) {
newRisk.put("parkName", depMap.get("campus_name"));
Object campusName = campusMap.get(campusId);
if (Objects.nonNull(campusName)) {
newRisk.put("parkName", campusName);
}
}
//newRisk.put("check_area", sourceData.get("check_area"));
//newRisk.put("check_item", sourceData.get("check_item"));
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("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);
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);
syncList.add(JSONUtil.parseObj(unSync));
}
// 更新同步数据表和同步数据明细表
// 批量更新同步主表和明细表
if (CollectionUtil.isNotEmpty(syncDetailList)) {
super.editJsonData(detailFormId, syncDetailList);
}
// 更新同步数据表和同步数据明细表
super.editJsonData(dbFormId, syncList);
if (CollectionUtil.isNotEmpty(syncList)) {
super.editJsonData(dbFormId, syncList);
}
}
}
}

View File

@@ -1,12 +1,15 @@
package com.jeelowcode.module.biz.service;
import com.jeelowcode.tool.framework.datapermission.core.annotation.DataPermission;
import java.util.List;
import java.util.Map;
/**
* Demo相关
*/
public interface IWorkItemService {
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.dto.AdminUserRespDTO;
import com.jeelowcode.tool.framework.common.util.object.BeanUtils;
import com.jeelowcode.tool.framework.datapermission.core.annotation.DataPermission;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -33,6 +34,7 @@ import java.util.*;
*/
@Slf4j
@Service
@DataPermission(enable=false)
public class WorkItemServiceImpl implements IWorkItemService {