This commit is contained in:
2026-06-30 11:04:16 +08:00
parent 6d12e4e4ef
commit 130a920486
4 changed files with 199 additions and 23 deletions
@@ -4,6 +4,7 @@ import com.nbport.zgwl.common.PageResult;
import com.nbport.zgwl.common.R; import com.nbport.zgwl.common.R;
import com.nbport.zgwl.contractlaunch.dto.ContractLaunchApproveDto; import com.nbport.zgwl.contractlaunch.dto.ContractLaunchApproveDto;
import com.nbport.zgwl.contractlaunch.dto.ContractLaunchArchiveDto; import com.nbport.zgwl.contractlaunch.dto.ContractLaunchArchiveDto;
import com.nbport.zgwl.contractlaunch.dto.ContractLaunchCollectSaveDto;
import com.nbport.zgwl.contractlaunch.dto.ContractLaunchDto; import com.nbport.zgwl.contractlaunch.dto.ContractLaunchDto;
import com.nbport.zgwl.contractlaunch.dto.ContractLaunchLawCallbackDto; import com.nbport.zgwl.contractlaunch.dto.ContractLaunchLawCallbackDto;
import com.nbport.zgwl.contractlaunch.dto.ContractLaunchLawCallbackRequestDto; import com.nbport.zgwl.contractlaunch.dto.ContractLaunchLawCallbackRequestDto;
@@ -299,6 +300,32 @@ public class ContractLaunchController {
return R.success(contractLaunchService.mockCollect(id)); return R.success(contractLaunchService.mockCollect(id));
} }
/**
* 保存采集信息(用印信息 + 签订信息)。
*
* 签署完成后,在「合同采集」阶段(pending_collect),
* 用户通过采集弹窗补充/修改用印信息和签订信息后保存到合同记录。
* 与 mock-collect 分开,允许用户在触发采集前先保存草稿。
*
* 请求体 ContractLaunchCollectSaveDto 字段:
* sealIsSeal - 是否用印
* sealApproveSame - 用印和审批文本是否一致
* sealQuantity - 用印数量
* sealDate - 用印时间
* signDate - 签订日期
* activeDate - 合同生效日期
* executorAccount - 合同履行执行人
* signedFileName - 签署文件名
* signedFilePath - 签署文件路径
* signedFileType - 签署文件类型
* signedFileSize - 签署文件大小
*/
@PostMapping("/{id}/collect/save")
public R<ContractLaunchDto> saveCollectInfo(@PathVariable("id") Long id,
@RequestBody ContractLaunchCollectSaveDto collectDto) {
return R.success(contractLaunchService.saveCollectInfo(id, collectDto));
}
/** /**
* 合同归档。 * 合同归档。
* *
@@ -0,0 +1,58 @@
package com.nbport.zgwl.contractlaunch.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 合同采集信息保存参数。
*
* 签署完成后,在「合同采集」阶段(pending_collect → pending_archive),
* 用户通过采集弹窗补充用印信息、签订信息后保存到合同记录,
* 再调用 dataCollectionPushService.collectContract 推送到法务系统。
*/
@Data
public class ContractLaunchCollectSaveDto implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/** 是否用印(1-是,0-否) */
private String sealIsSeal;
/** 用印和审批文本是否一致(1-是,0-否) */
private String sealApproveSame;
/** 用印数量 */
private Integer sealQuantity;
/** 用印时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime sealDate;
/** 签订日期 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime signDate;
/** 合同生效日期 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime activeDate;
/** 合同履行执行人(账号) */
private String executorAccount;
/** 签署文件名 */
private String signedFileName;
/** 签署文件路径 */
private String signedFilePath;
/** 签署文件类型 */
private String signedFileType;
/** 签署文件大小 */
private Long signedFileSize;
}
@@ -3,6 +3,7 @@ package com.nbport.zgwl.contractlaunch.service;
import com.nbport.zgwl.common.PageResult; import com.nbport.zgwl.common.PageResult;
import com.nbport.zgwl.contractlaunch.dto.ContractLaunchApproveDto; import com.nbport.zgwl.contractlaunch.dto.ContractLaunchApproveDto;
import com.nbport.zgwl.contractlaunch.dto.ContractLaunchArchiveDto; import com.nbport.zgwl.contractlaunch.dto.ContractLaunchArchiveDto;
import com.nbport.zgwl.contractlaunch.dto.ContractLaunchCollectSaveDto;
import com.nbport.zgwl.contractlaunch.dto.ContractLaunchDto; import com.nbport.zgwl.contractlaunch.dto.ContractLaunchDto;
import com.nbport.zgwl.contractlaunch.dto.ContractLaunchLawCallbackDto; import com.nbport.zgwl.contractlaunch.dto.ContractLaunchLawCallbackDto;
import com.nbport.zgwl.contractlaunch.dto.ContractLaunchLawCallbackSampleDto; import com.nbport.zgwl.contractlaunch.dto.ContractLaunchLawCallbackSampleDto;
@@ -50,6 +51,9 @@ public interface ContractLaunchService {
/** 签署完成回调(回写签署文件路径,状态变更为待采集) */ /** 签署完成回调(回写签署文件路径,状态变更为待采集) */
ContractLaunchDto markSealCompleted(Long id, ContractLaunchSealDto sealDto); ContractLaunchDto markSealCompleted(Long id, ContractLaunchSealDto sealDto);
/** 保存采集信息(用印+签订信息),用于采集弹窗保存草稿 */
ContractLaunchDto saveCollectInfo(Long id, ContractLaunchCollectSaveDto collectDto);
/** 模拟合同采集(调用数据采集 collectContract 接口,TODO:替换为真实调用) */ /** 模拟合同采集(调用数据采集 collectContract 接口,TODO:替换为真实调用) */
ContractLaunchDto mockCollect(Long id); ContractLaunchDto mockCollect(Long id);
@@ -11,6 +11,7 @@ import com.nbport.zgwl.contractlaunch.dto.ContractLaunchApproveDto;
import com.nbport.zgwl.contractlaunch.dto.ContractLaunchArchiveDto; import com.nbport.zgwl.contractlaunch.dto.ContractLaunchArchiveDto;
import com.nbport.zgwl.contractlaunch.dto.ContractLaunchArchiveEleDto; import com.nbport.zgwl.contractlaunch.dto.ContractLaunchArchiveEleDto;
import com.nbport.zgwl.contractlaunch.dto.ContractLaunchArchivePaperDto; import com.nbport.zgwl.contractlaunch.dto.ContractLaunchArchivePaperDto;
import com.nbport.zgwl.contractlaunch.dto.ContractLaunchCollectSaveDto;
import com.nbport.zgwl.contractlaunch.dto.ContractLaunchDto; import com.nbport.zgwl.contractlaunch.dto.ContractLaunchDto;
import com.nbport.zgwl.contractlaunch.dto.ContractLaunchFileDto; import com.nbport.zgwl.contractlaunch.dto.ContractLaunchFileDto;
import com.nbport.zgwl.contractlaunch.dto.ContractLaunchLawCallbackDto; import com.nbport.zgwl.contractlaunch.dto.ContractLaunchLawCallbackDto;
@@ -125,15 +126,11 @@ public class ContractLaunchServiceImpl implements ContractLaunchService {
if (securityManager.isSuperAdmin()) { if (securityManager.isSuperAdmin()) {
return "admin"; return "admin";
} }
String permissionScope = resolveScopeByDataPermission(currentUser.getDataPermission());
if (permissionScope != null) {
return permissionScope;
}
if (authManager.isUserCanAccessSys(currentUser.getId(), SysEnum.MANAGE)) { if (authManager.isUserCanAccessSys(currentUser.getId(), SysEnum.MANAGE)) {
return isAdminUser(currentUser) ? "company" : "personal"; return "company";
} }
if (authManager.isUserCanAccessSys(currentUser.getId(), SysEnum.BUSINESS)) { if (authManager.isUserCanAccessSys(currentUser.getId(), SysEnum.BUSINESS)) {
return isAdminUser(currentUser) ? "company" : "personal"; return "company";
} }
return "personal"; return "personal";
} }
@@ -278,6 +275,10 @@ public class ContractLaunchServiceImpl implements ContractLaunchService {
} }
SysUserEntity currentUser = adminSecurityManage.getUser(); SysUserEntity currentUser = adminSecurityManage.getUser();
boolean singleAgreement = isSingleAgreement(contract); boolean singleAgreement = isSingleAgreement(contract);
// 非单方协议:提交法务前做严格校验
if (!singleAgreement) {
validateSubmitForLaw(contract);
}
contract.setStatus(ContractLaunchConstants.STATUS_REVIEWING); contract.setStatus(ContractLaunchConstants.STATUS_REVIEWING);
contract.setApproveStatus(ContractLaunchConstants.APPROVE_STATUS_PENDING); contract.setApproveStatus(ContractLaunchConstants.APPROVE_STATUS_PENDING);
contract.setApproveMessage(singleAgreement ? "已提交内部审批,等待审批" : "已提交法务,等待审批"); contract.setApproveMessage(singleAgreement ? "已提交内部审批,等待审批" : "已提交法务,等待审批");
@@ -551,6 +552,54 @@ public class ContractLaunchServiceImpl implements ContractLaunchService {
* requestDTO.setPayload(payload); * requestDTO.setPayload(payload);
* dataCollectionPushService.collectContract(requestDTO); * dataCollectionPushService.collectContract(requestDTO);
*/ */
/**
* 保存采集信息(用印+签订信息)。
*
* 在 pending_collect 状态时,用户通过采集弹窗补充/修改
* 用印信息和签订信息后保存到合同记录。
* 不触发状态变更,可多次保存;后续再通过 mockCollect(或真实联调的 collect)推送法务。
*/
@Override
@Transactional(rollbackFor = Exception.class)
public ContractLaunchDto saveCollectInfo(Long id, ContractLaunchCollectSaveDto collectDto) {
ContractLaunchDto contract = getDetail(id);
if (isSingleAgreement(contract)) {
throw new ServiceException("单方协议无需执行法务采集");
}
if (!ContractLaunchConstants.STATUS_PENDING_COLLECT.equals(contract.getStatus())) {
throw new ServiceException("仅待采集状态允许保存采集信息");
}
SysUserEntity currentUser = adminSecurityManage.getUser();
// 用印信息
contract.setSealIsSeal(collectDto.getSealIsSeal());
contract.setSealApproveSame(collectDto.getSealApproveSame());
contract.setSealQuantity(collectDto.getSealQuantity());
contract.setSealDate(collectDto.getSealDate());
// 签订信息
contract.setSignDate(collectDto.getSignDate());
contract.setActiveDate(collectDto.getActiveDate());
contract.setExecutorAccount(collectDto.getExecutorAccount());
// 签署文件
if (collectDto.getSignedFilePath() != null && !collectDto.getSignedFilePath().isBlank()) {
contract.setSignedFileName(collectDto.getSignedFileName());
contract.setSignedFilePath(collectDto.getSignedFilePath());
contract.setSignedFileType(collectDto.getSignedFileType());
contract.setSignedFileSize(collectDto.getSignedFileSize());
}
contract.setUpdateBy(currentUser.getUsername());
contract.setUpdateTime(LocalDateTime.now());
contractLaunchMapper.updateContract(contract);
insertLog(id, ContractLaunchConstants.ACTION_COLLECT, "保存采集信息",
ContractLaunchConstants.ACTION_STATUS_SUCCESS, "用户通过采集弹窗保存用印/签订信息", null, currentUser);
return getDetail(id);
}
/**
* 【测试按钮】模拟合同采集。
*/
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public ContractLaunchDto mockCollect(Long id) { public ContractLaunchDto mockCollect(Long id) {
@@ -1159,7 +1208,11 @@ public class ContractLaunchServiceImpl implements ContractLaunchService {
} else if (isOppositeParty(item)) { } else if (isOppositeParty(item)) {
item.setRoleName("相对方"); item.setRoleName("相对方");
} }
if (isOppositeParty(item)) {
item.setSubjectCode(null);
} else {
item.setSubjectCode(trim(item.getSubjectCode())); item.setSubjectCode(trim(item.getSubjectCode()));
}
item.setCompanyName(trim(item.getCompanyName())); item.setCompanyName(trim(item.getCompanyName()));
item.setCreditCode(trim(item.getCreditCode())); item.setCreditCode(trim(item.getCreditCode()));
item.setOppositeId(trim(item.getOppositeId())); item.setOppositeId(trim(item.getOppositeId()));
@@ -1284,21 +1337,7 @@ public class ContractLaunchServiceImpl implements ContractLaunchService {
if (contract.getContractType() == null || contract.getContractType().isBlank()) { if (contract.getContractType() == null || contract.getContractType().isBlank()) {
contract.setContractType(contract.getContractTypeCode()); contract.setContractType(contract.getContractTypeCode());
} }
if (contract.getValuationMode() == null || contract.getValuationMode().isBlank()) { // 以下字段在提交法务时才强制必填(validateSubmitForLaw),草稿保存时放宽
throw new ServiceException("计价方式不能为空");
}
if (contract.getCurrencyName() == null || contract.getCurrencyName().isBlank()) {
throw new ServiceException("币种不能为空");
}
if (contract.getPaymentMethod() == null || contract.getPaymentMethod().isBlank()) {
throw new ServiceException("付款方式不能为空");
}
if (contract.getSealType() == null || contract.getSealType().isBlank()) {
throw new ServiceException("签订方式不能为空");
}
if (contract.getSealQuantity() != null && contract.getSealQuantity() < 1) {
throw new ServiceException("用印数量必须大于等于1");
}
if (contract.getContractPeriodType() == null || contract.getContractPeriodType().isBlank()) { if (contract.getContractPeriodType() == null || contract.getContractPeriodType().isBlank()) {
throw new ServiceException("合同期限类型不能为空"); throw new ServiceException("合同期限类型不能为空");
} }
@@ -1313,7 +1352,7 @@ public class ContractLaunchServiceImpl implements ContractLaunchService {
// if (contract.getActiveDate() == null) { // if (contract.getActiveDate() == null) {
// throw new ServiceException("合同生效日期不能为空"); // throw new ServiceException("合同生效日期不能为空");
// } // }
validateActiveDateWithinTerm(contract); // activeDate 已移至采集阶段,草稿保存时不校验
if (contract.getPartyList() == null || contract.getPartyList().isEmpty()) { if (contract.getPartyList() == null || contract.getPartyList().isEmpty()) {
throw new ServiceException("至少需要维护一个合同方"); throw new ServiceException("至少需要维护一个合同方");
} }
@@ -1364,6 +1403,7 @@ public class ContractLaunchServiceImpl implements ContractLaunchService {
&& !item.getFilePath().isBlank()))) { && !item.getFilePath().isBlank()))) {
throw new ServiceException("当前签订方式必须上传授权委托书附件"); throw new ServiceException("当前签订方式必须上传授权委托书附件");
} }
// 履行计划在草稿和提交法务时均必填
validatePerformancePlans(contract, false); validatePerformancePlans(contract, false);
} }
@@ -1833,6 +1873,53 @@ public class ContractLaunchServiceImpl implements ContractLaunchService {
validatePerformancePlans(contract, true); validatePerformancePlans(contract, true);
} }
/**
* 提交法务前的严格校验:草稿校验 + pushContract 必填项补全 + 履行计划校验
*/
private void validateSubmitForLaw(ContractLaunchDto contract) {
// 先跑草稿基本校验(不含已移除的采集字段)
validateDraft(contract);
// 补全 pushContract 必填项
if (contract.getValuationMode() == null || contract.getValuationMode().isBlank()) {
throw new ServiceException("计价方式不能为空");
}
if (contract.getCurrencyName() == null || contract.getCurrencyName().isBlank()) {
throw new ServiceException("币种不能为空");
}
if (contract.getSealType() == null || contract.getSealType().isBlank()) {
throw new ServiceException("签订方式不能为空");
}
if (contract.getIsElectron() == null || contract.getIsElectron().isBlank()) {
throw new ServiceException("签署方式不能为空");
}
if (contract.getPaymentDirection() == null || contract.getPaymentDirection().isBlank()) {
throw new ServiceException("收支方向不能为空");
}
if (contract.getTextSource() == null || contract.getTextSource().isBlank()) {
throw new ServiceException("文本来源不能为空");
}
if (contract.getContractSource() == null || contract.getContractSource().isBlank()) {
throw new ServiceException("合同来源不能为空");
}
if (contract.getExecutorAccount() == null || contract.getExecutorAccount().isBlank()) {
throw new ServiceException("合同执行人不能为空");
}
if (contract.getIsMajorContract() == null) {
throw new ServiceException("是否重大合同不能为空");
}
if (contract.getIsOpenBidding() == null) {
throw new ServiceException("是否公开招标不能为空");
}
if (contract.getIsSgat() == null || contract.getIsSgat().isBlank()) {
throw new ServiceException("是否涉及港澳台不能为空");
}
if (contract.getNeedAmendments() == null || contract.getNeedAmendments().isBlank()) {
throw new ServiceException("是否需修订条款不能为空");
}
// 履行计划校验(提交法务时必填)
validatePerformancePlans(contract, false);
}
private void validatePerformancePlans(ContractLaunchDto contract, boolean requirePlanId) { private void validatePerformancePlans(ContractLaunchDto contract, boolean requirePlanId) {
if (contract.getPerformancePlans() == null || contract.getPerformancePlans().isEmpty()) { if (contract.getPerformancePlans() == null || contract.getPerformancePlans().isEmpty()) {
throw new ServiceException("履行计划不能为空"); throw new ServiceException("履行计划不能为空");