From 472351617754ee048e97bce5265876e75090004a Mon Sep 17 00:00:00 2001 From: zhouxiaofeng <2946471396@qq.com> Date: Mon, 1 Jun 2026 16:28:08 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=87=E4=BB=B6=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/ContractLaunchFileDto.java | 3 + .../mapper/ContractLaunchMapper.java | 5 + .../ContractLaunchLegalFileBridgeService.java | 126 ++++++++++++++++++ .../service/ContractLaunchServiceImpl.java | 49 +++++-- .../service/DataCollectionPushService.java | 9 ++ .../utils/ContractPushHttpUtils.java | 29 ++++ .../nbport/zgwl/service/LocalFileService.java | 14 ++ .../contractlaunch/ContractLaunchMapper.xml | 18 ++- zgwl_contract_launch_oracle.sql | 2 + 9 files changed, 239 insertions(+), 16 deletions(-) create mode 100644 src/main/java/com/nbport/zgwl/contractlaunch/service/ContractLaunchLegalFileBridgeService.java diff --git a/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchFileDto.java b/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchFileDto.java index 145709b..5a18e34 100644 --- a/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchFileDto.java +++ b/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchFileDto.java @@ -29,6 +29,9 @@ public class ContractLaunchFileDto implements Serializable { /** 文件路径(上传到法务系统后返回的 path,或本系统存储路径) */ private String filePath; + /** 法务系统可用的附件路径(上传法务后缓存) */ + private String legalFileUrl; + /** 文件类型(docx/pdf 等) */ private String fileType; diff --git a/src/main/java/com/nbport/zgwl/contractlaunch/mapper/ContractLaunchMapper.java b/src/main/java/com/nbport/zgwl/contractlaunch/mapper/ContractLaunchMapper.java index fb90bec..dbfb96e 100644 --- a/src/main/java/com/nbport/zgwl/contractlaunch/mapper/ContractLaunchMapper.java +++ b/src/main/java/com/nbport/zgwl/contractlaunch/mapper/ContractLaunchMapper.java @@ -78,6 +78,11 @@ public interface ContractLaunchMapper { @Param("fileCategory") String fileCategory, @Param("updateBy") String updateBy); + /** 回写法务附件路径缓存 */ + int updateFileLegalUrl(@Param("id") Long id, + @Param("legalFileUrl") String legalFileUrl, + @Param("updateBy") String updateBy); + /** 删除合同下的所有履行计划(逻辑删除) */ int deletePlanByContractId(@Param("contractId") Long contractId, @Param("updateBy") String updateBy); diff --git a/src/main/java/com/nbport/zgwl/contractlaunch/service/ContractLaunchLegalFileBridgeService.java b/src/main/java/com/nbport/zgwl/contractlaunch/service/ContractLaunchLegalFileBridgeService.java new file mode 100644 index 0000000..1e5ffe3 --- /dev/null +++ b/src/main/java/com/nbport/zgwl/contractlaunch/service/ContractLaunchLegalFileBridgeService.java @@ -0,0 +1,126 @@ +package com.nbport.zgwl.contractlaunch.service; + +import cn.hutool.core.util.StrUtil; +import com.nbport.zgwl.contractlaunch.dto.ContractLaunchFileDto; +import com.nbport.zgwl.contractlaunch.mapper.ContractLaunchMapper; +import com.nbport.zgwl.contractpush.service.DataCollectionPushService; +import com.nbport.zgwl.exception.ServiceException; +import com.nbport.zgwl.service.LocalFileService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.LinkedHashMap; +import java.util.Map; + +@Service +@RequiredArgsConstructor +public class ContractLaunchLegalFileBridgeService { + + private final DataCollectionPushService dataCollectionPushService; + private final LocalFileService localFileService; + private final ContractLaunchMapper contractLaunchMapper; + + public String ensureLegalFilePath(ContractLaunchFileDto fileDto) { + if (fileDto == null) { + return null; + } + String cachedLegalPath = StrUtil.trim(fileDto.getLegalFileUrl()); + if (StrUtil.isNotBlank(cachedLegalPath)) { + return cachedLegalPath; + } + String filePath = StrUtil.trim(fileDto.getFilePath()); + if (StrUtil.isBlank(filePath)) { + return null; + } + if (isLegalRemotePath(filePath)) { + return filePath; + } + byte[] bytes = localFileService.readFileBytes(filePath); + Map uploadResult = dataCollectionPushService.upload(bytes, resolveFileName(fileDto), null); + String legalPath = extractUploadedPath(uploadResult); + if (StrUtil.isBlank(legalPath)) { + throw new ServiceException("法务附件上传成功但未返回文件路径"); + } + fileDto.setLegalFileUrl(legalPath); + if (fileDto.getId() != null) { + contractLaunchMapper.updateFileLegalUrl(fileDto.getId(), legalPath, "system"); + } + return legalPath; + } + + public Map buildSettlementPayload(ContractLaunchFileDto fileDto) { + String legalPath = ensureLegalFilePath(fileDto); + if (StrUtil.isBlank(legalPath)) { + return null; + } + Map map = new LinkedHashMap<>(); + map.put("fileName", resolveFileName(fileDto)); + map.put("filePath", legalPath); + map.put("fileSize", fileDto.getFileSize()); + map.put("fileType", resolveSuffix(fileDto)); + return map; + } + + public Map buildCollectionPayload(ContractLaunchFileDto fileDto) { + String legalPath = ensureLegalFilePath(fileDto); + if (StrUtil.isBlank(legalPath)) { + return null; + } + Map map = new LinkedHashMap<>(); + map.put("size", fileDto.getFileSize() == null ? "" : String.valueOf(fileDto.getFileSize())); + map.put("name", resolveFileName(fileDto)); + map.put("suffix", resolveSuffix(fileDto)); + map.put("path", legalPath); + return map; + } + + private String resolveFileName(ContractLaunchFileDto fileDto) { + String fileName = StrUtil.trim(fileDto.getFileName()); + if (StrUtil.isNotBlank(fileName)) { + return fileName; + } + throw new ServiceException("附件文件名不能为空"); + } + + private String resolveSuffix(ContractLaunchFileDto fileDto) { + String fileType = StrUtil.trim(fileDto.getFileType()); + if (StrUtil.isNotBlank(fileType)) { + return fileType.startsWith(".") ? fileType.substring(1) : fileType; + } + String fileName = resolveFileName(fileDto); + int index = fileName.lastIndexOf('.'); + return index >= 0 && index < fileName.length() - 1 ? fileName.substring(index + 1) : ""; + } + + private boolean isLegalRemotePath(String filePath) { + return filePath.startsWith("/cj/") + || filePath.startsWith("http://") + || filePath.startsWith("https://"); + } + + @SuppressWarnings("unchecked") + private String extractUploadedPath(Map uploadResult) { + if (uploadResult == null || uploadResult.isEmpty()) { + return null; + } + Object directData = uploadResult.get("data"); + if (directData instanceof String && StrUtil.isNotBlank((String) directData)) { + return (String) directData; + } + if (directData instanceof Map) { + Object nested = ((Map) directData).get("data"); + if (nested instanceof String && StrUtil.isNotBlank((String) nested)) { + return (String) nested; + } + Object path = ((Map) directData).get("path"); + if (path instanceof String && StrUtil.isNotBlank((String) path)) { + return (String) path; + } + } + Object result = uploadResult.get("result"); + if (result instanceof String && StrUtil.isNotBlank((String) result)) { + return (String) result; + } + return null; + } +} diff --git a/src/main/java/com/nbport/zgwl/contractlaunch/service/ContractLaunchServiceImpl.java b/src/main/java/com/nbport/zgwl/contractlaunch/service/ContractLaunchServiceImpl.java index 5912767..062ccc4 100644 --- a/src/main/java/com/nbport/zgwl/contractlaunch/service/ContractLaunchServiceImpl.java +++ b/src/main/java/com/nbport/zgwl/contractlaunch/service/ContractLaunchServiceImpl.java @@ -12,6 +12,7 @@ import com.nbport.zgwl.contractlaunch.dto.ContractLaunchPartyDto; import com.nbport.zgwl.contractlaunch.dto.ContractLaunchPlanDto; import com.nbport.zgwl.contractlaunch.dto.ContractLaunchQueryDto; import com.nbport.zgwl.contractlaunch.dto.ContractLaunchSealDto; +import com.nbport.zgwl.contractlaunch.service.ContractLaunchLegalFileBridgeService; import com.nbport.zgwl.contractlaunch.mapper.ContractLaunchMapper; import com.nbport.zgwl.contractlaunch.utils.ContractLaunchCodeUtils; import com.nbport.zgwl.contractlaunch.utils.ContractLaunchConstants; @@ -50,6 +51,7 @@ public class ContractLaunchServiceImpl implements ContractLaunchService { private final AdminSecurityManage adminSecurityManage; private final SettlementPushService settlementPushService; private final DataCollectionPushService dataCollectionPushService; + private final ContractLaunchLegalFileBridgeService legalFileBridgeService; /** 分页查询,附带合同方/文件/履行计划(列表页不查日志) */ @Override @@ -1088,9 +1090,9 @@ public class ContractLaunchServiceImpl implements ContractLaunchService { map.put("planSubjectName", plan.getPlanSubjectName()); return map; }).collect(Collectors.toList())); - payload.put("contractText", buildFilePayloadList(contract, ContractLaunchConstants.FILE_CATEGORY_BODY)); - payload.put("otherAttach", buildFilePayloadList(contract, ContractLaunchConstants.FILE_CATEGORY_OTHER)); - payload.put("contractGistFile", buildFilePayloadList(contract, ContractLaunchConstants.FILE_CATEGORY_GIST)); + payload.put("contractText", buildSettlementFilePayloadList(contract, ContractLaunchConstants.FILE_CATEGORY_BODY)); + payload.put("otherAttach", buildSettlementFilePayloadList(contract, ContractLaunchConstants.FILE_CATEGORY_OTHER)); + payload.put("contractGistFile", buildSettlementFilePayloadList(contract, ContractLaunchConstants.FILE_CATEGORY_GIST)); return payload; } @@ -1122,9 +1124,9 @@ public class ContractLaunchServiceImpl implements ContractLaunchService { payload.put("c_creator", contract.getInitiator()); payload.put("c_creatorPhone", contract.getCreatorPhone()); payload.put("c_createDept", contract.getInitiatorDept()); - payload.put("c_zw", buildFilePayloadList(contract, ContractLaunchConstants.FILE_CATEGORY_BODY)); - payload.put("c_attachs", buildFilePayloadList(contract, ContractLaunchConstants.FILE_CATEGORY_OTHER)); - payload.put("c_signAttachs", buildFilePayloadList(contract, ContractLaunchConstants.FILE_CATEGORY_SIGNED)); + payload.put("c_zw", buildCollectionFilePayloadList(contract, ContractLaunchConstants.FILE_CATEGORY_BODY)); + payload.put("c_attachs", buildCollectionFilePayloadList(contract, ContractLaunchConstants.FILE_CATEGORY_OTHER)); + payload.put("c_signAttachs", buildCollectionFilePayloadList(contract, ContractLaunchConstants.FILE_CATEGORY_GIST)); payload.put("c_lxPlan", contract.getPerformancePlans()); Map seal = new LinkedHashMap<>(); seal.put("seal_isSeal", "1"); @@ -1136,7 +1138,7 @@ public class ContractLaunchServiceImpl implements ContractLaunchService { sign.put("sign_isSign", "1"); sign.put("sign_date", contract.getSignDate()); sign.put("sign_activeDate", contract.getActiveDate()); - sign.put("sign_attachs", buildFilePayloadList(contract, ContractLaunchConstants.FILE_CATEGORY_SIGNED)); + sign.put("sign_attachs", buildCollectionFilePayloadList(contract, ContractLaunchConstants.FILE_CATEGORY_SIGNED)); sign.put("sign_executor", contract.getExecutorAccount()); payload.put("c_sign", sign); return payload; @@ -1151,22 +1153,45 @@ public class ContractLaunchServiceImpl implements ContractLaunchService { payload.put("KeeperUnit", archiveDto == null ? null : archiveDto.getArchiveKeeperUnit()); payload.put("KeeperUnitCode", archiveDto == null ? null : archiveDto.getArchiveKeeperUnitCode()); payload.put("archivePapers", List.of()); - payload.put("archiveEles", (archiveDto == null || archiveDto.getArchiveFiles() == null) ? List.of() : archiveDto.getArchiveFiles()); + payload.put("archiveEles", buildSettlementArchiveEleList(archiveDto)); return payload; } - /** 按 fileCategory 提取文件列表,转为法务接口要求的附件格式 */ - private List> buildFilePayloadList(ContractLaunchDto contract, String fileCategory) { + private List> buildSettlementFilePayloadList(ContractLaunchDto contract, String fileCategory) { return contract.getFileList().stream() .filter(item -> fileCategory.equals(item.getFileCategory())) .map(item -> { Map map = new LinkedHashMap<>(); map.put("fileName", item.getFileName()); - map.put("filePath", item.getFilePath()); + map.put("filePath", legalFileBridgeService.ensureLegalFilePath(item)); map.put("fileSize", item.getFileSize()); map.put("fileType", item.getFileType()); return map; - }).collect(Collectors.toList()); + }) + .filter(item -> item.get("filePath") != null) + .collect(Collectors.toList()); + } + + private List> buildCollectionFilePayloadList(ContractLaunchDto contract, String fileCategory) { + return contract.getFileList().stream() + .filter(item -> fileCategory.equals(item.getFileCategory())) + .map(item -> legalFileBridgeService.buildCollectionPayload(item)) + .filter(item -> item != null && item.get("path") != null) + .collect(Collectors.toList()); + } + + private List> buildSettlementArchiveEleList(ContractLaunchArchiveDto archiveDto) { + if (archiveDto == null || archiveDto.getArchiveFiles() == null || archiveDto.getArchiveFiles().isEmpty()) { + return List.of(); + } + return archiveDto.getArchiveFiles().stream() + .map(item -> { + Map map = new LinkedHashMap<>(); + map.put("archivesName", item.getFileName()); + map.put("approvalAttachs", List.of(legalFileBridgeService.buildSettlementPayload(item))); + return map; + }) + .collect(Collectors.toList()); } private String trim(String value) { diff --git a/src/main/java/com/nbport/zgwl/contractpush/service/DataCollectionPushService.java b/src/main/java/com/nbport/zgwl/contractpush/service/DataCollectionPushService.java index 3d9a966..5e0e0fb 100644 --- a/src/main/java/com/nbport/zgwl/contractpush/service/DataCollectionPushService.java +++ b/src/main/java/com/nbport/zgwl/contractpush/service/DataCollectionPushService.java @@ -60,6 +60,15 @@ public class DataCollectionPushService { return httpUtils.postMultipart(properties.getDataCollection().getUploadUrl(), file, headers); } + public Map upload(byte[] bytes, String originalFileName, String token) { + Map headers = requestHandler.buildDataCollectionHeaders( + resolveCollectionToken(token), + properties.getDataCollection().getSystemKey(), + true + ); + return httpUtils.postMultipart(properties.getDataCollection().getUploadUrl(), bytes, originalFileName, headers); + } + /** 相对方信息采集:将相对方信息推送给法务系统 */ public Map collectOpposites(PushPayloadDTO requestDTO) { return postCollectionJson(properties.getDataCollection().getCollectOppositesUrl(), requestDTO, false); diff --git a/src/main/java/com/nbport/zgwl/contractpush/utils/ContractPushHttpUtils.java b/src/main/java/com/nbport/zgwl/contractpush/utils/ContractPushHttpUtils.java index 23c131b..8547615 100644 --- a/src/main/java/com/nbport/zgwl/contractpush/utils/ContractPushHttpUtils.java +++ b/src/main/java/com/nbport/zgwl/contractpush/utils/ContractPushHttpUtils.java @@ -68,6 +68,26 @@ public class ContractPushHttpUtils { return parseResponse(response.getBody()); } + public Map postMultipart(String url, byte[] fileBytes, String originalFilename, Map headers) { + if (fileBytes == null || fileBytes.length == 0) { + throw new ServiceException("上传文件不能为空"); + } + if (originalFilename == null || originalFilename.trim().isEmpty()) { + throw new ServiceException("上传文件名不能为空"); + } + + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA); + addHeaders(httpHeaders, headers); + + MultiValueMap formData = new LinkedMultiValueMap<>(); + formData.add("file", toByteArrayResource(fileBytes, originalFilename)); + + HttpEntity> entity = new HttpEntity<>(formData, httpHeaders); + ResponseEntity response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class); + return parseResponse(response.getBody()); + } + /** 将 MultipartFile 转为 ByteArrayResource(保留原始文件名) */ private ByteArrayResource toByteArrayResource(MultipartFile file) { try { @@ -82,6 +102,15 @@ public class ContractPushHttpUtils { } } + private ByteArrayResource toByteArrayResource(byte[] bytes, String originalFilename) { + return new ByteArrayResource(bytes) { + @Override + public String getFilename() { + return originalFilename; + } + }; + } + private void addHeaders(HttpHeaders httpHeaders, Map headers) { if (headers == null || headers.isEmpty()) { return; diff --git a/src/main/java/com/nbport/zgwl/service/LocalFileService.java b/src/main/java/com/nbport/zgwl/service/LocalFileService.java index f77c3f1..30c74e1 100644 --- a/src/main/java/com/nbport/zgwl/service/LocalFileService.java +++ b/src/main/java/com/nbport/zgwl/service/LocalFileService.java @@ -78,4 +78,18 @@ public class LocalFileService { throw new ServiceException("文件下载失败"); } } + + public byte[] readFileBytes(String fileName) { + try { + Path rootPath = Paths.get(localPath).toAbsolutePath().normalize(); + Path filePath = FileStorageUtils.resolveFilePath(rootPath, fileName); + if (!Files.exists(filePath) || Files.isDirectory(filePath)) { + throw new ServiceException(ServiceErrorCodeRange.FILE_NOT_FOUND, "文件不存在"); + } + return Files.readAllBytes(filePath); + } catch (IOException e) { + log.error("文件读取失败", e); + throw new ServiceException("文件读取失败"); + } + } } diff --git a/src/main/resources/mapper/contractlaunch/ContractLaunchMapper.xml b/src/main/resources/mapper/contractlaunch/ContractLaunchMapper.xml index d282264..3b10884 100644 --- a/src/main/resources/mapper/contractlaunch/ContractLaunchMapper.xml +++ b/src/main/resources/mapper/contractlaunch/ContractLaunchMapper.xml @@ -129,6 +129,7 @@ + @@ -411,10 +412,10 @@ INSERT ALL INTO SEAL_CONTRACT_LAUNCH_FILE ( - ID, CONTRACT_ID, FILE_CATEGORY, FILE_NAME, FILE_PATH, FILE_TYPE, FILE_SIZE, + ID, CONTRACT_ID, FILE_CATEGORY, FILE_NAME, FILE_PATH, LEGAL_FILE_URL, FILE_TYPE, FILE_SIZE, SORT_ORDER, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, IS_DELETED, REMARK ) VALUES ( - #{item.id}, #{item.contractId}, #{item.fileCategory}, #{item.fileName}, #{item.filePath}, #{item.fileType}, #{item.fileSize}, + #{item.id}, #{item.contractId}, #{item.fileCategory}, #{item.fileName}, #{item.filePath}, #{item.legalFileUrl}, #{item.fileType}, #{item.fileSize}, #{item.sortOrder}, #{createBy}, SYSDATE, #{updateBy}, SYSDATE, 'N', #{item.remark} ) @@ -471,7 +472,16 @@ UPDATE_BY = #{updateBy}, UPDATE_TIME = SYSDATE where CONTRACT_ID = #{contractId} - and FILE_CATEGORY = #{fileCategory} + and FILE_CATEGORY = #{fileCategory} + and IS_DELETED = 'N' + + + + update SEAL_CONTRACT_LAUNCH_FILE + set LEGAL_FILE_URL = #{legalFileUrl}, + UPDATE_BY = #{updateBy}, + UPDATE_TIME = SYSDATE + where ID = #{id} and IS_DELETED = 'N' @@ -500,7 +510,7 @@