From f18a173d2587f6584d386558ac4a1beaba7b7bb8 Mon Sep 17 00:00:00 2001 From: zhouxiaofeng <2946471396@qq.com> Date: Wed, 8 Jul 2026 08:36:44 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=86=E8=8A=82=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ContractLaunchController.java | 19 ++++ .../contractlaunch/dto/ContractLaunchDto.java | 6 +- .../service/ContractLaunchService.java | 4 + .../service/ContractLaunchServiceImpl.java | 102 +++++++++++------- .../utils/ContractLaunchConstants.java | 2 + .../service/SettlementPushService.java | 38 ++++++- .../ContractTemplateController.java | 7 ++ .../service/ContractTemplateService.java | 3 + .../service/ContractTemplateServiceImpl.java | 16 +++ .../zgwl/partner/dto/PartnerManageDto.java | 3 +- .../service/PartnerManageServiceImpl.java | 5 + .../utils/PartnerSyncPayloadBuilder.java | 2 +- .../seal/controller/SealProxyController.java | 48 ++++++--- .../zgwl/seal/service/SealTokenService.java | 13 ++- 14 files changed, 206 insertions(+), 62 deletions(-) diff --git a/src/main/java/com/nbport/zgwl/contractlaunch/controller/ContractLaunchController.java b/src/main/java/com/nbport/zgwl/contractlaunch/controller/ContractLaunchController.java index 2cca4ed..8e7c425 100644 --- a/src/main/java/com/nbport/zgwl/contractlaunch/controller/ContractLaunchController.java +++ b/src/main/java/com/nbport/zgwl/contractlaunch/controller/ContractLaunchController.java @@ -23,7 +23,9 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; import java.util.Map; @@ -354,6 +356,23 @@ public class ContractLaunchController { } } + /** + * 上传法务水印合同文件。 + * + * 当法务审批回调未返回带水印的合同文件时,允许用户手动上传。 + * 上传后的文件路径替换 contractTextFilePath,供后续在线签订使用。 + * + * 请求方式:multipart/form-data + * 参数:file(MultipartFile) + * + * 对应前端功能:签署准备页面的"上传带水印合同"区域 + */ + @PostMapping("/{id}/watermark/upload") + public R uploadWatermarkFile(@PathVariable("id") Long id, + @RequestParam("file") MultipartFile file) { + return R.success(contractLaunchService.uploadWatermarkFile(id, file)); + } + /** * 合同归档。 * diff --git a/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchDto.java b/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchDto.java index d3e40bd..b1eeb2e 100644 --- a/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchDto.java +++ b/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchDto.java @@ -185,13 +185,13 @@ public class ContractLaunchDto implements Serializable { private String pushUrl; /** 是否重大合同(1:是, 0:否) */ - private Integer isMajorContract; + private String isMajorContract; /** 是否公开招标(1:是, 0:否) */ - private Integer isOpenBidding; + private String isOpenBidding; /** 是否列入当年预算(1:是, 0:否) */ - private Integer isYearBudget; + private String isYearBudget; /** 是否共享(1:是, 0:否) */ private String isShare; diff --git a/src/main/java/com/nbport/zgwl/contractlaunch/service/ContractLaunchService.java b/src/main/java/com/nbport/zgwl/contractlaunch/service/ContractLaunchService.java index e65ce6c..fd4658e 100644 --- a/src/main/java/com/nbport/zgwl/contractlaunch/service/ContractLaunchService.java +++ b/src/main/java/com/nbport/zgwl/contractlaunch/service/ContractLaunchService.java @@ -9,6 +9,7 @@ import com.nbport.zgwl.contractlaunch.dto.ContractLaunchLawCallbackDto; import com.nbport.zgwl.contractlaunch.dto.ContractLaunchLawCallbackSampleDto; import com.nbport.zgwl.contractlaunch.dto.ContractLaunchQueryDto; import com.nbport.zgwl.contractlaunch.dto.ContractLaunchSealDto; +import org.springframework.web.multipart.MultipartFile; import java.util.Map; @@ -65,6 +66,9 @@ public interface ContractLaunchService { /** 相对方确认已签署完成 */ Map confirmCounterparty(Long id, Long partyId); + /** 上传法务水印合同文件(当审批回调未带回水印文件时,手动上传代替) */ + ContractLaunchDto uploadWatermarkFile(Long id, MultipartFile file); + /** 模拟合同采集(调用数据采集 collectContract 接口,TODO:替换为真实调用) */ ContractLaunchDto mockCollect(Long id); 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 32ffd57..ae2e8e3 100644 --- a/src/main/java/com/nbport/zgwl/contractlaunch/service/ContractLaunchServiceImpl.java +++ b/src/main/java/com/nbport/zgwl/contractlaunch/service/ContractLaunchServiceImpl.java @@ -41,10 +41,12 @@ import com.nbport.zgwl.service.LocalFileService; import com.nbport.zgwl.entity.SysUserEntity; import com.nbport.zgwl.exception.ServiceException; import com.nbport.zgwl.utils.AdminSecurityManage; +import com.nbport.zgwl.utils.FileStorageUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -499,7 +501,8 @@ public class ContractLaunchServiceImpl implements ContractLaunchService { contract.setSealContractId(sealDto.getSealContractId() == null || sealDto.getSealContractId().isBlank() ? contract.getSealContractId() : sealDto.getSealContractId()); contract.setSealContractStatus(sealDto.getSealContractStatus()); contract.setSignedFileName(sealDto.getSignedFileName()); - contract.setSignedFilePath(resolveSignedFilePath(sealDto)); + String signedFilePath = resolveSignedFilePath(sealDto); + contract.setSignedFilePath(signedFilePath); contract.setSignedFileUrl(sealDto.getSignedFileUrl() != null && !sealDto.getSignedFileUrl().isBlank() ? sealDto.getSignedFileUrl() : (isExternalUrl(sealDto.getSignedFilePath()) ? sealDto.getSignedFilePath() : null)); @@ -514,7 +517,7 @@ public class ContractLaunchServiceImpl implements ContractLaunchService { contract.setUpdateBy(currentUser.getUsername()); contract.setUpdateTime(completedTime); contractLaunchMapper.updateContract(contract); - saveSignedFile(id, sealDto, currentUser.getUsername()); + saveSignedFile(id, signedFilePath, sealDto, currentUser.getUsername()); // 区分在线签署完成和线下签署上传两种场景记录日志 boolean isOfflineUpload = sealDto.getSignedOfflineReason() != null && !sealDto.getSignedOfflineReason().isBlank(); if (isOfflineUpload) { @@ -556,6 +559,29 @@ public class ContractLaunchServiceImpl implements ContractLaunchService { * dataCollectionPushService.collectContract(requestDTO); */ + @Override + @Transactional(rollbackFor = Exception.class) + public ContractLaunchDto uploadWatermarkFile(Long id, MultipartFile file) { + ContractLaunchDto contract = getDetail(id); + if (!ContractLaunchConstants.STATUS_PENDING_SIGN.equals(contract.getStatus())) { + throw new ServiceException("仅待签署状态的合同可上传水印文件"); + } + String relativePath = localFileService.upload(file); + contract.setContractTextFileName(file.getOriginalFilename()); + contract.setContractTextFilePath(relativePath); + String suffix = FileStorageUtils.getFileSuffix(file.getOriginalFilename()); + contract.setContractTextFileType(suffix.startsWith(".") ? suffix.substring(1) : suffix); + contract.setContractTextFileSize(file.getSize()); + contract.setUpdateBy(adminSecurityManage.getUser().getUsername()); + contract.setUpdateTime(LocalDateTime.now()); + contractLaunchMapper.updateContract(contract); + insertLog(id, ContractLaunchConstants.ACTION_SEAL_UPLOAD, + "上传水印合同文件", ContractLaunchConstants.ACTION_STATUS_SUCCESS, + "手动上传带法务水印的合同文件: " + file.getOriginalFilename(), null, + adminSecurityManage.getUser()); + return getDetail(id); + } + /** * 保存采集信息(用印+签订信息)。 * @@ -1171,8 +1197,8 @@ public class ContractLaunchServiceImpl implements ContractLaunchService { } /** 保存签署文件到 file 子表(先删旧签署文件,再插入新的) */ - private void saveSignedFile(Long contractId, ContractLaunchSealDto sealDto, String operator) { - if (sealDto.getSignedFilePath() == null || sealDto.getSignedFilePath().isBlank()) { + private void saveSignedFile(Long contractId, String signedFilePath, ContractLaunchSealDto sealDto, String operator) { + if (signedFilePath == null || signedFilePath.isBlank()) { return; } contractLaunchMapper.deleteFileByContractIdAndCategory(contractId, ContractLaunchConstants.FILE_CATEGORY_SIGNED, operator); @@ -1183,7 +1209,7 @@ public class ContractLaunchServiceImpl implements ContractLaunchService { fileDto.setFileName(sealDto.getSignedFileName() == null || sealDto.getSignedFileName().isBlank() ? "已签署合同文件" : sealDto.getSignedFileName()); - fileDto.setFilePath(sealDto.getSignedFilePath()); + fileDto.setFilePath(signedFilePath); fileDto.setFileType(sealDto.getSignedFileType()); fileDto.setFileSize(sealDto.getSignedFileSize()); fileDto.setSortOrder(1); @@ -1243,10 +1269,10 @@ public class ContractLaunchServiceImpl implements ContractLaunchService { contract.setIsElectron("1"); } if (contract.getIsOpenBidding() == null) { - contract.setIsOpenBidding(0); + contract.setIsOpenBidding("0"); } if (contract.getIsYearBudget() == null) { - contract.setIsYearBudget(0); + contract.setIsYearBudget("0"); } if (contract.getPaymentDirection() == null || contract.getPaymentDirection().isBlank()) { contract.setPaymentDirection("0"); @@ -1490,7 +1516,7 @@ public class ContractLaunchServiceImpl implements ContractLaunchService { if (!isSingleAgreement(contract) && !hasCounterparty) { throw new ServiceException("至少维护一个相对方"); } - if (Integer.valueOf(1).equals(contract.getIsOpenBidding()) + if ("1".equals(contract.getIsOpenBidding()) && (contract.getBiddingNo() == null || contract.getBiddingNo().isBlank())) { throw new ServiceException("公开招标时必须填写关联招标号"); } @@ -1561,8 +1587,8 @@ public class ContractLaunchServiceImpl implements ContractLaunchService { payload.put("mainContractCode", contract.getMainContractCode()); payload.put("periodExplain", contract.getPeriodExplain()); payload.put("valuationMode", contract.getValuationMode()); - payload.put("isMajorContract", contract.getIsMajorContract() == null ? 0 : contract.getIsMajorContract()); - payload.put("sealType", contract.getSealType()); + payload.put("isMajorContract", contract.getIsMajorContract() == null ? "0" : contract.getIsMajorContract()); + payload.put("sealType", "YWZ".equals(contract.getSealType()) ? "QT" : contract.getSealType()); payload.put("creatorPhone", contract.getCreatorPhone()); // dateType 仅在固定期限时传递,无固定期限不传 if ("0".equals(contract.getContractPeriodType())) { @@ -1587,9 +1613,9 @@ public class ContractLaunchServiceImpl implements ContractLaunchService { payload.put("contractPeriod", contract.getContractPeriodType()); payload.put("contractAmount", contract.getContractAmount()); payload.put("isSgat", contract.getIsSgat()); - payload.put("isOpenBidding", contract.getIsOpenBidding() == null ? 0 : contract.getIsOpenBidding()); + payload.put("isOpenBidding", contract.getIsOpenBidding() == null ? "0" : contract.getIsOpenBidding()); payload.put("pushUrl", contract.getPushUrl()); - payload.put("isYearBudget", contract.getIsYearBudget() == null ? 0 : contract.getIsYearBudget()); + payload.put("isYearBudget", contract.getIsYearBudget() == null ? "0" : contract.getIsYearBudget()); payload.put("isShare", contract.getIsShare()); payload.put("assistDepartmentName", contract.getAssistDepartmentName()); payload.put("contractNature", contract.getContractNature()); @@ -1679,18 +1705,18 @@ public class ContractLaunchServiceImpl implements ContractLaunchService { payload.put("c_qyztGAT", contract.getIsSgat()); payload.put("c_qyztSW", contract.getContractNature()); payload.put("c_executor", contract.getExecutorAccount()); - payload.put("c_openBidding", contract.getIsOpenBidding() == null ? "0" : String.valueOf(contract.getIsOpenBidding())); + payload.put("c_openBidding", contract.getIsOpenBidding() == null ? "0" : contract.getIsOpenBidding()); // 关联招标号:公开招标选是时必填 - if (Integer.valueOf(1).equals(contract.getIsOpenBidding()) && contract.getBiddingNo() != null) { + if ("1".equals(contract.getIsOpenBidding()) && contract.getBiddingNo() != null) { payload.put("c_biddingNo", contract.getBiddingNo()); } else { payload.put("c_biddingNo", null); } // c_currentBudget 仅在收支方向为付款(1)时传,与 V3.0 文档一致 if ("1".equals(contract.getPaymentDirection())) { - payload.put("c_currentBudget", contract.getIsYearBudget() == null ? "0" : String.valueOf(contract.getIsYearBudget())); + payload.put("c_currentBudget", contract.getIsYearBudget() == null ? "0" : contract.getIsYearBudget()); } - payload.put("c_major", contract.getIsMajorContract() == null ? "0" : String.valueOf(contract.getIsMajorContract())); + payload.put("c_major", contract.getIsMajorContract() == null ? "0" : contract.getIsMajorContract()); payload.put("c_signMethod", contract.getSealType()); // payload.put("c_authAttachs", buildCollectionFilePayloadList(contract, ContractLaunchConstants.FILE_CATEGORY_AUTH)); payload.put("c_creator", contract.getInitiator()); @@ -1787,6 +1813,10 @@ public class ContractLaunchServiceImpl implements ContractLaunchService { */ private void applyLawWatermarkFile(ContractLaunchDto contract, ContractLaunchLawCallbackFileDto watermarkFile) { if (watermarkFile == null || watermarkFile.getFilePath() == null || watermarkFile.getFilePath().isBlank()) { + contract.setContractTextFileName(null); + contract.setContractTextFilePath(null); + contract.setContractTextFileType(null); + contract.setContractTextFileSize(null); return; } contract.setContractTextFileName(trim(watermarkFile.getFileName())); @@ -1911,7 +1941,7 @@ public class ContractLaunchServiceImpl implements ContractLaunchService { log.info("签章文件为外部URL,先通过签章API获取新下载地址: {}", filePath); String token = sealTokenService.getAccessToken(); // 通过签章API获取服务端可用的下载URL - String freshUrl = fetchSealDownloadUrl(sealDto.getSealContractId(), token, filePath); + String freshUrl = fetchSealDownloadUrl(sealDto.getSealContractId(), token); log.info("获取到签章下载地址: {}", freshUrl); String localPath = localFileService.downloadFromUrl(freshUrl, token); log.info("签章文件已下载到本地: {}", localPath); @@ -1923,30 +1953,24 @@ public class ContractLaunchServiceImpl implements ContractLaunchService { /** * 通过签章平台API获取下载URL(服务端IP绑定的新URL) */ - private String fetchSealDownloadUrl(String sealContractId, String token, String fallbackUrl) { - try { - String apiUrl = sealConfig.getBaseUrl() + "/saas/api/contract/download"; - JSONObject body = new JSONObject(); - body.put("contractId", sealContractId); - String response = SealHttpClient.post(apiUrl, body.toJSONString(), token, "application/json"); - log.info("签章平台下载API响应: {}", response); - JSONObject result = JSON.parseObject(response); - if (result != null && result.getBooleanValue("isSuccess")) { - Object dataObj = result.get("data"); - log.info("签章平台data类型: {}, 值: {}", dataObj != null ? dataObj.getClass().getSimpleName() : "null", dataObj); - if (dataObj instanceof JSONObject) { - String url = ((JSONObject) dataObj).getString("downloadUrl"); - if (url != null && !url.isBlank()) return url; - } else if (dataObj instanceof String && !((String) dataObj).isBlank()) { - return (String) dataObj; - } - } else { - log.warn("签章平台下载API返回失败: {}", response); + private String fetchSealDownloadUrl(String sealContractId, String token) { + String apiUrl = sealConfig.getBaseUrl() + "/saas/api/contract/download"; + JSONObject body = new JSONObject(); + body.put("contractId", sealContractId); + String response = SealHttpClient.post(apiUrl, body.toJSONString(), token, "application/json"); + log.info("签章平台下载API响应: {}", response); + JSONObject result = JSON.parseObject(response); + if (result != null && result.getBooleanValue("isSuccess")) { + Object dataObj = result.get("data"); + log.info("签章平台data类型: {}, 值: {}", dataObj != null ? dataObj.getClass().getSimpleName() : "null", dataObj); + if (dataObj instanceof JSONObject) { + String url = ((JSONObject) dataObj).getString("downloadUrl"); + if (url != null && !url.isBlank()) return url; + } else if (dataObj instanceof String && !((String) dataObj).isBlank()) { + return (String) dataObj; } - } catch (Exception e) { - log.warn("通过签章API获取下载URL失败,使用原始URL: {}", e.getMessage()); } - return fallbackUrl; + throw new ServiceException("签章平台下载接口返回失败,无法获取签署文件下载地址"); } private boolean isExternalUrl(String path) { diff --git a/src/main/java/com/nbport/zgwl/contractlaunch/utils/ContractLaunchConstants.java b/src/main/java/com/nbport/zgwl/contractlaunch/utils/ContractLaunchConstants.java index fb2d46b..f4bdec1 100644 --- a/src/main/java/com/nbport/zgwl/contractlaunch/utils/ContractLaunchConstants.java +++ b/src/main/java/com/nbport/zgwl/contractlaunch/utils/ContractLaunchConstants.java @@ -63,6 +63,8 @@ public final class ContractLaunchConstants { public static final String ACTION_SEAL_CALLBACK = "seal_callback"; /** 线下签署文件上传 */ public static final String ACTION_SEAL_UPLOAD = "seal_upload"; + /** 签章平台二维码生成(等待扫码确认) */ + public static final String ACTION_SEAL_QRCODE = "seal_qrcode"; public static final String ACTION_STATUS_SUCCESS = "success"; public static final String ACTION_STATUS_FAILED = "failed"; diff --git a/src/main/java/com/nbport/zgwl/contractpush/service/SettlementPushService.java b/src/main/java/com/nbport/zgwl/contractpush/service/SettlementPushService.java index 17ba553..8812feb 100644 --- a/src/main/java/com/nbport/zgwl/contractpush/service/SettlementPushService.java +++ b/src/main/java/com/nbport/zgwl/contractpush/service/SettlementPushService.java @@ -12,6 +12,8 @@ import com.nbport.zgwl.contractpush.utils.ContractPushUrlUtils; import com.nbport.zgwl.exception.ServiceException; import org.springframework.stereotype.Service; +import java.nio.charset.StandardCharsets; +import java.util.Base64; import java.util.LinkedHashMap; import java.util.Map; @@ -26,6 +28,9 @@ public class SettlementPushService { private final ContractPushHttpUtils httpUtils; private final ContractPushRequestHandler requestHandler; + private volatile String cachedToken; + private volatile long tokenExpireTime; + public SettlementPushService(ContractPushProperties properties, ContractPushHttpUtils httpUtils, ContractPushRequestHandler requestHandler) { @@ -91,19 +96,48 @@ public class SettlementPushService { return result; } - /** 获取结算平台 token,优先用入参,没有则自动调用 getToken 接口获取 */ + /** 获取结算平台 token,优先用入参,没有则使用缓存或自动调用 getToken 接口获取 */ private String resolveSettlementToken(String inputToken) { if (StrUtil.isNotBlank(inputToken)) { return inputToken; } + if (cachedToken != null && System.currentTimeMillis() < tokenExpireTime) { + return cachedToken; + } Map tokenResult = getToken(new PushTokenRequestDTO()); Object data = tokenResult.get("data"); if (data instanceof Map) { Object accessToken = ((Map) data).get("access_token"); if (accessToken != null) { - return String.valueOf(accessToken); + String token = String.valueOf(accessToken); + cachedToken = token; + tokenExpireTime = parseJwtExp(token); + return token; } } throw new ServiceException("获取结算平台 token 失败"); } + + + + /** 从 JWT token 中解析 exp 作为过期时间,解析失败则兜底 50 分钟 */ + private long parseJwtExp(String token) { + long defaultTtl = 50 * 60 * 1000L; + try { + String[] parts = token.split("\\."); + if (parts.length < 2) return System.currentTimeMillis() + defaultTtl; + byte[] decoded = Base64.getUrlDecoder().decode(parts[1]); + String json = new String(decoded, StandardCharsets.UTF_8); + int expIdx = json.indexOf("\"exp\""); + if (expIdx < 0) return System.currentTimeMillis() + defaultTtl; + int colonIdx = json.indexOf(':', expIdx); + int commaIdx = json.indexOf(',', colonIdx); + int braceIdx = json.indexOf('}', colonIdx); + int end = commaIdx > 0 && commaIdx < braceIdx ? commaIdx : braceIdx; + long exp = Long.parseLong(json.substring(colonIdx + 1, end).trim()); + return exp * 1000 - 60_000; + } catch (Exception e) { + return System.currentTimeMillis() + defaultTtl; + } + } } diff --git a/src/main/java/com/nbport/zgwl/manifest/controller/ContractTemplateController.java b/src/main/java/com/nbport/zgwl/manifest/controller/ContractTemplateController.java index ba37e0d..e5f7163 100644 --- a/src/main/java/com/nbport/zgwl/manifest/controller/ContractTemplateController.java +++ b/src/main/java/com/nbport/zgwl/manifest/controller/ContractTemplateController.java @@ -8,6 +8,7 @@ import com.nbport.zgwl.manifest.dto.TemplateOrgOptionDto; import com.nbport.zgwl.manifest.service.ContractTemplateService; import com.nbport.zgwl.manifest.utils.ManifestManageConstants; import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -71,4 +72,10 @@ public class ContractTemplateController { public R changeStatus(@PathVariable("id") Long id, @PathVariable("status") String status) { return R.success(contractTemplateService.changeTemplateStatus(id, status)); } + + @DeleteMapping("/{id}") + public R delete(@PathVariable("id") Long id) { + contractTemplateService.deleteTemplate(id); + return R.success(); + } } diff --git a/src/main/java/com/nbport/zgwl/manifest/service/ContractTemplateService.java b/src/main/java/com/nbport/zgwl/manifest/service/ContractTemplateService.java index f239d33..2b61a36 100644 --- a/src/main/java/com/nbport/zgwl/manifest/service/ContractTemplateService.java +++ b/src/main/java/com/nbport/zgwl/manifest/service/ContractTemplateService.java @@ -30,6 +30,9 @@ public interface ContractTemplateService { /** 切换模板状态 */ ContractTemplateDto changeTemplateStatus(Long id, String status); + /** 删除模板(逻辑删除) */ + void deleteTemplate(Long id); + /** 获取发布范围组织选项 */ List queryOrgOptions(); } diff --git a/src/main/java/com/nbport/zgwl/manifest/service/ContractTemplateServiceImpl.java b/src/main/java/com/nbport/zgwl/manifest/service/ContractTemplateServiceImpl.java index 521db17..75723cf 100644 --- a/src/main/java/com/nbport/zgwl/manifest/service/ContractTemplateServiceImpl.java +++ b/src/main/java/com/nbport/zgwl/manifest/service/ContractTemplateServiceImpl.java @@ -158,6 +158,22 @@ public class ContractTemplateServiceImpl implements ContractTemplateService { return getTemplateDetail(id); } + /** 删除模板(逻辑删除) */ + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteTemplate(Long id) { + ContractTemplateDto dbTemplate = contractTemplateMapper.selectTemplateById(id); + if (dbTemplate == null) { + throw new ServiceException("模板不存在"); + } + contractTemplateMapper.deleteTemplateById( + id, + ManifestManageConstants.DEFAULT_OPERATOR, + LocalDateTime.now(), + ManifestManageConstants.DEL_FLAG_DELETED + ); + } + /** * 获取发布范围组织选项(扁平列表,供发布范围下拉使用) * 数据来源于组织树,平铺所有节点 diff --git a/src/main/java/com/nbport/zgwl/partner/dto/PartnerManageDto.java b/src/main/java/com/nbport/zgwl/partner/dto/PartnerManageDto.java index df9b583..5737468 100644 --- a/src/main/java/com/nbport/zgwl/partner/dto/PartnerManageDto.java +++ b/src/main/java/com/nbport/zgwl/partner/dto/PartnerManageDto.java @@ -32,8 +32,7 @@ public class PartnerManageDto implements Serializable { // ============== 客户基本信息字段 ============== /** 客户系统ID */ - private Long customerSysid; // 注意:数据库中是VARCHAR(64),不是Long - + private Long customerSysid; /** 客户注册号 */ private String customerRegno; diff --git a/src/main/java/com/nbport/zgwl/partner/service/PartnerManageServiceImpl.java b/src/main/java/com/nbport/zgwl/partner/service/PartnerManageServiceImpl.java index 8c73e79..c3d52bf 100644 --- a/src/main/java/com/nbport/zgwl/partner/service/PartnerManageServiceImpl.java +++ b/src/main/java/com/nbport/zgwl/partner/service/PartnerManageServiceImpl.java @@ -21,6 +21,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; @@ -246,10 +247,12 @@ public class PartnerManageServiceImpl implements PartnerManageService { LocalDateTime now = LocalDateTime.now(); + // 构造批量报文(法务接口要求 JSON 数组 [{...},{...}]) List> batchPayload = PartnerSyncPayloadBuilder.buildCollectOppositesBatchPayload(partners); log.info("法务系统批量同步参数,共 {} 条: {}", partners.size(), JsonUtils.toJson(batchPayload)); + // TODO: 真实联调时打开以下调用 // PushPayloadDTO requestDTO = new PushPayloadDTO(); // requestDTO.setPayload(Map.of("opposites", batchPayload)); // 或直接传 List,取决于 collectOpposites 签名 @@ -288,6 +291,8 @@ public class PartnerManageServiceImpl implements PartnerManageService { successCount++; } + + return successCount; } diff --git a/src/main/java/com/nbport/zgwl/partner/utils/PartnerSyncPayloadBuilder.java b/src/main/java/com/nbport/zgwl/partner/utils/PartnerSyncPayloadBuilder.java index 44650f1..244e7fc 100644 --- a/src/main/java/com/nbport/zgwl/partner/utils/PartnerSyncPayloadBuilder.java +++ b/src/main/java/com/nbport/zgwl/partner/utils/PartnerSyncPayloadBuilder.java @@ -55,7 +55,7 @@ public final class PartnerSyncPayloadBuilder { payload.put("o_zjType", partner.getCertificateType()); payload.put("o_zjNo", partner.getCertificateNum()); payload.put("o_tin", partner.getTinCode()); - payload.put("o_country", partner.getCountry()); + payload.put("o_country", partner.getCountryName()); payload.put("o_legalPerson", partner.getLegalPersonName()); payload.put("o_address", partner.getCustomerRegaddr()); payload.put("o_capital", partner.getRegCapital()); diff --git a/src/main/java/com/nbport/zgwl/seal/controller/SealProxyController.java b/src/main/java/com/nbport/zgwl/seal/controller/SealProxyController.java index 550027d..0bfc131 100644 --- a/src/main/java/com/nbport/zgwl/seal/controller/SealProxyController.java +++ b/src/main/java/com/nbport/zgwl/seal/controller/SealProxyController.java @@ -81,7 +81,7 @@ public class SealProxyController { String token = sealTokenService.forceRefreshToken(); Map data = new HashMap<>(); data.put("access_token", token); - data.put("expires_in", 7200); + data.put("expires_in", sealTokenService.getCachedExpiresIn()); return R.success(data); } catch (Exception e) { log.error("获取Token失败", e); @@ -125,7 +125,7 @@ public class SealProxyController { logPayload.put("response", response); sealLogService.logSealAction(null, ContractLaunchConstants.ACTION_SEAL_CREATE, "创建签署流程", - result.getCode() != null && "0000".equals(String.valueOf(result.getCode())) + result.isSuccess() ? ContractLaunchConstants.ACTION_STATUS_SUCCESS : ContractLaunchConstants.ACTION_STATUS_FAILED, "在签章平台创建签署流程,合同名称: " + contractName, @@ -148,7 +148,15 @@ public class SealProxyController { try { JSONObject body = buildSignQrCodeBody(params); String response = proxyPost("/saas/api/qrcode/getStandardSignQrCode", body.toJSONString()); - return parseSealResponse(response); + R result = parseSealResponse(response); + if (result.isSuccess()) { + String sealContractId = extractContractId(params); + Long contractId = sealLogService.findContractIdBySealContractId(sealContractId); + sealLogService.logSealAction(contractId, ContractLaunchConstants.ACTION_SEAL_QRCODE, + "标准签章", ContractLaunchConstants.ACTION_STATUS_PENDING, + "标准签章二维码已生成,等待扫码确认,签章平台合同ID: " + sealContractId, params); + } + return result; } catch (Exception e) { log.error("获取标准签章二维码失败", e); return R.error("获取标准签章二维码失败: " + e.getMessage()); @@ -160,7 +168,15 @@ public class SealProxyController { try { JSONObject body = buildPreSignBody(params); String response = proxyPost("/saas/api/qrcode/getPreSignQrCode", body.toJSONString()); - return parseSealResponse(response); + R result = parseSealResponse(response); + if (result.isSuccess()) { + String sealContractId = extractContractId(params); + Long contractId = sealLogService.findContractIdBySealContractId(sealContractId); + sealLogService.logSealAction(contractId, ContractLaunchConstants.ACTION_SEAL_QRCODE, + "预盖章", ContractLaunchConstants.ACTION_STATUS_PENDING, + "预盖章二维码已生成,等待扫码确认,签章平台合同ID: " + sealContractId, params); + } + return result; } catch (Exception e) { log.error("获取预盖章二维码失败", e); return R.error("获取预盖章二维码失败: " + e.getMessage()); @@ -173,7 +189,15 @@ public class SealProxyController { JSONObject body = new JSONObject(); body.put("notifyUrl", params.get("notifyUrl")); String response = proxyPost("/saas/api/qrcode/getTrusteeshipSealQrCode", body.toJSONString()); - return parseSealResponse(response); + R result = parseSealResponse(response); + if (result.isSuccess()) { + String sealContractId = extractContractId(params); + Long contractId = sealLogService.findContractIdBySealContractId(sealContractId); + sealLogService.logSealAction(contractId, ContractLaunchConstants.ACTION_SEAL_QRCODE, + "托管章", ContractLaunchConstants.ACTION_STATUS_PENDING, + "托管章二维码已生成,等待扫码确认" + (sealContractId != null ? ",签章平台合同ID: " + sealContractId : ""), params); + } + return result; } catch (Exception e) { log.error("获取托管章二维码失败", e); return R.error("获取托管章二维码失败: " + e.getMessage()); @@ -191,7 +215,7 @@ public class SealProxyController { Long contractId = sealLogService.findContractIdBySealContractId(sealContractId); sealLogService.logSealAction(contractId, ContractLaunchConstants.ACTION_SEAL_SIGN, "托管签署", - result.getCode() != null && "0000".equals(String.valueOf(result.getCode())) + result.isSuccess() ? ContractLaunchConstants.ACTION_STATUS_SUCCESS : ContractLaunchConstants.ACTION_STATUS_FAILED, "执行托管签署,签章平台合同ID: " + sealContractId, @@ -234,7 +258,7 @@ public class SealProxyController { Long contractId = sealLogService.findContractIdBySealContractId(sealContractId); sealLogService.logSealAction(contractId, ContractLaunchConstants.ACTION_SEAL_TIMESTAMP, "时间戳签章", - result.getCode() != null && "0000".equals(String.valueOf(result.getCode())) + result.isSuccess() ? ContractLaunchConstants.ACTION_STATUS_SUCCESS : ContractLaunchConstants.ACTION_STATUS_FAILED, "执行时间戳签章,签章平台合同ID: " + sealContractId, @@ -278,7 +302,7 @@ public class SealProxyController { Long contractId = sealLogService.findContractIdBySealContractId(sealContractId); sealLogService.logSealAction(contractId, ContractLaunchConstants.ACTION_SEAL_COMMENT, "批注签章", - result.getCode() != null && "0000".equals(String.valueOf(result.getCode())) + result.isSuccess() ? ContractLaunchConstants.ACTION_STATUS_SUCCESS : ContractLaunchConstants.ACTION_STATUS_FAILED, "执行批注签章,签章平台合同ID: " + sealContractId, @@ -309,7 +333,7 @@ public class SealProxyController { Long contractId = sealLogService.findContractIdBySealContractId(sealContractId); sealLogService.logSealAction(contractId, ContractLaunchConstants.ACTION_SEAL_COMPLETE, "完成签署流程", - result.getCode() != null && "0000".equals(String.valueOf(result.getCode())) + result.isSuccess() ? ContractLaunchConstants.ACTION_STATUS_SUCCESS : ContractLaunchConstants.ACTION_STATUS_FAILED, "在签章平台完结签署流程,签章平台合同ID: " + sealContractId, @@ -340,7 +364,7 @@ public class SealProxyController { Long contractId = sealLogService.findContractIdBySealContractId(sealContractId); sealLogService.logSealAction(contractId, ContractLaunchConstants.ACTION_SEAL_CANCEL, "取消签署流程", - result.getCode() != null && "0000".equals(String.valueOf(result.getCode())) + result.isSuccess() ? ContractLaunchConstants.ACTION_STATUS_SUCCESS : ContractLaunchConstants.ACTION_STATUS_FAILED, "在签章平台取消签署流程,签章平台合同ID: " + sealContractId, @@ -366,7 +390,7 @@ public class SealProxyController { body.put("name", params.get("name")); body.put("mobile", params.get("mobile")); body.put("idCode", params.get("idCode")); - String response = proxyPost("/saas/api/user/getUserUniqueId ", body.toJSONString()); + String response = proxyPost("/saas/api/user/getUserUniqueId", body.toJSONString()); return parseSealResponse(response); } catch (Exception e) { log.error("获取用户身份编码失败", e); @@ -430,7 +454,7 @@ public class SealProxyController { try { JSONObject body = new JSONObject(); body.put("requestId", params.get("requestId")); - String response = proxyPost("/api/user/getUserInfos", body.toJSONString()); + String response = proxyPost("/saas/api/user/getUserInfos", body.toJSONString()); return parseSealResponse(response); } catch (Exception e) { log.error("获取用户隐私数据失败", e); diff --git a/src/main/java/com/nbport/zgwl/seal/service/SealTokenService.java b/src/main/java/com/nbport/zgwl/seal/service/SealTokenService.java index 480c011..0777dda 100644 --- a/src/main/java/com/nbport/zgwl/seal/service/SealTokenService.java +++ b/src/main/java/com/nbport/zgwl/seal/service/SealTokenService.java @@ -30,6 +30,8 @@ public class SealTokenService { private volatile long tokenExpireTime = 0; + private volatile long cachedExpiresIn; + private final ReentrantLock lock = new ReentrantLock(); private static final long TOKEN_EXPIRE_SECONDS = 7200; @@ -91,9 +93,9 @@ public class SealTokenService { if (result.getBooleanValue("isSuccess")) { JSONObject data = result.getJSONObject("data"); cachedToken = data.getString("access_token"); - long expiresIn = data.getLongValue("expires_in", TOKEN_EXPIRE_SECONDS); - tokenExpireTime = System.currentTimeMillis() + expiresIn * 1000; - log.info("签章平台Token获取成功, 有效期: {}秒", expiresIn); + cachedExpiresIn = data.getLongValue("expires_in", TOKEN_EXPIRE_SECONDS); + tokenExpireTime = System.currentTimeMillis() + cachedExpiresIn * 1000; + log.info("签章平台Token获取成功, 有效期: {}秒", cachedExpiresIn); } else { String msg = result.getString("msg"); log.error("签章平台Token获取失败: {}", msg); @@ -107,11 +109,16 @@ public class SealTokenService { } } + public long getCachedExpiresIn() { + return cachedExpiresIn; + } + public void clearToken() { try { lock.lock(); cachedToken = null; tokenExpireTime = 0; + cachedExpiresIn = 0; } finally { lock.unlock(); }