文件问题

This commit is contained in:
2026-06-01 16:28:08 +08:00
parent bed00bd3e3
commit 4723516177
9 changed files with 239 additions and 16 deletions
@@ -29,6 +29,9 @@ public class ContractLaunchFileDto implements Serializable {
/** 文件路径(上传到法务系统后返回的 path,或本系统存储路径) */
private String filePath;
/** 法务系统可用的附件路径(上传法务后缓存) */
private String legalFileUrl;
/** 文件类型(docx/pdf 等) */
private String fileType;
@@ -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);
@@ -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<String, Object> 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<String, Object> buildSettlementPayload(ContractLaunchFileDto fileDto) {
String legalPath = ensureLegalFilePath(fileDto);
if (StrUtil.isBlank(legalPath)) {
return null;
}
Map<String, Object> 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<String, Object> buildCollectionPayload(ContractLaunchFileDto fileDto) {
String legalPath = ensureLegalFilePath(fileDto);
if (StrUtil.isBlank(legalPath)) {
return null;
}
Map<String, Object> 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<String, Object> 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<String, Object>) directData).get("data");
if (nested instanceof String && StrUtil.isNotBlank((String) nested)) {
return (String) nested;
}
Object path = ((Map<String, Object>) 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;
}
}
@@ -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<String, Object> 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<Map<String, Object>> buildFilePayloadList(ContractLaunchDto contract, String fileCategory) {
private List<Map<String, Object>> buildSettlementFilePayloadList(ContractLaunchDto contract, String fileCategory) {
return contract.getFileList().stream()
.filter(item -> fileCategory.equals(item.getFileCategory()))
.map(item -> {
Map<String, Object> 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<Map<String, Object>> 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<Map<String, Object>> buildSettlementArchiveEleList(ContractLaunchArchiveDto archiveDto) {
if (archiveDto == null || archiveDto.getArchiveFiles() == null || archiveDto.getArchiveFiles().isEmpty()) {
return List.of();
}
return archiveDto.getArchiveFiles().stream()
.map(item -> {
Map<String, Object> 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) {
@@ -60,6 +60,15 @@ public class DataCollectionPushService {
return httpUtils.postMultipart(properties.getDataCollection().getUploadUrl(), file, headers);
}
public Map<String, Object> upload(byte[] bytes, String originalFileName, String token) {
Map<String, String> headers = requestHandler.buildDataCollectionHeaders(
resolveCollectionToken(token),
properties.getDataCollection().getSystemKey(),
true
);
return httpUtils.postMultipart(properties.getDataCollection().getUploadUrl(), bytes, originalFileName, headers);
}
/** 相对方信息采集:将相对方信息推送给法务系统 */
public Map<String, Object> collectOpposites(PushPayloadDTO requestDTO) {
return postCollectionJson(properties.getDataCollection().getCollectOppositesUrl(), requestDTO, false);
@@ -68,6 +68,26 @@ public class ContractPushHttpUtils {
return parseResponse(response.getBody());
}
public Map<String, Object> postMultipart(String url, byte[] fileBytes, String originalFilename, Map<String, String> 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<String, Object> formData = new LinkedMultiValueMap<>();
formData.add("file", toByteArrayResource(fileBytes, originalFilename));
HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(formData, httpHeaders);
ResponseEntity<String> 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<String, String> headers) {
if (headers == null || headers.isEmpty()) {
return;
@@ -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("文件读取失败");
}
}
}
@@ -129,6 +129,7 @@
<result property="fileCategory" column="FILE_CATEGORY"/>
<result property="fileName" column="FILE_NAME"/>
<result property="filePath" column="FILE_PATH"/>
<result property="legalFileUrl" column="LEGAL_FILE_URL"/>
<result property="fileType" column="FILE_TYPE"/>
<result property="fileSize" column="FILE_SIZE"/>
<result property="sortOrder" column="SORT_ORDER"/>
@@ -411,10 +412,10 @@
INSERT ALL
<foreach collection="list" item="item">
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}
)
</foreach>
@@ -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>
<update id="updateFileLegalUrl">
update SEAL_CONTRACT_LAUNCH_FILE
set LEGAL_FILE_URL = #{legalFileUrl},
UPDATE_BY = #{updateBy},
UPDATE_TIME = SYSDATE
where ID = #{id}
and IS_DELETED = 'N'
</update>
@@ -500,7 +510,7 @@
</select>
<select id="selectFileByContractIds" resultMap="FileResult">
select ID, CONTRACT_ID, FILE_CATEGORY, FILE_NAME, FILE_PATH, FILE_TYPE, FILE_SIZE, SORT_ORDER, REMARK
select ID, CONTRACT_ID, FILE_CATEGORY, FILE_NAME, FILE_PATH, LEGAL_FILE_URL, FILE_TYPE, FILE_SIZE, SORT_ORDER, REMARK
from SEAL_CONTRACT_LAUNCH_FILE
where IS_DELETED = 'N'
and CONTRACT_ID in