diff --git a/src/main/java/com/nbport/zgwl/ZgwlEndApplication.java b/src/main/java/com/nbport/zgwl/ZgwlEndApplication.java index d1efab7..3a8e11c 100644 --- a/src/main/java/com/nbport/zgwl/ZgwlEndApplication.java +++ b/src/main/java/com/nbport/zgwl/ZgwlEndApplication.java @@ -7,7 +7,8 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; @MapperScan({ "com.nbport.zgwl.mapper", "com.nbport.zgwl.partner.mapper", - "com.nbport.zgwl.manifest.mapper" + "com.nbport.zgwl.manifest.mapper", + "com.nbport.zgwl.contractlaunch.mapper" }) @SpringBootApplication public class ZgwlEndApplication { diff --git a/src/main/java/com/nbport/zgwl/contractlaunch/controller/ContractLaunchController.java b/src/main/java/com/nbport/zgwl/contractlaunch/controller/ContractLaunchController.java new file mode 100644 index 0000000..c5d0b5e --- /dev/null +++ b/src/main/java/com/nbport/zgwl/contractlaunch/controller/ContractLaunchController.java @@ -0,0 +1,268 @@ +package com.nbport.zgwl.contractlaunch.controller; + +import com.nbport.zgwl.common.PageResult; +import com.nbport.zgwl.common.R; +import com.nbport.zgwl.contractlaunch.dto.ContractLaunchApproveDto; +import com.nbport.zgwl.contractlaunch.dto.ContractLaunchArchiveDto; +import com.nbport.zgwl.contractlaunch.dto.ContractLaunchDto; +import com.nbport.zgwl.contractlaunch.dto.ContractLaunchQueryDto; +import com.nbport.zgwl.contractlaunch.dto.ContractLaunchSealDto; +import com.nbport.zgwl.contractlaunch.service.ContractLaunchService; +import com.nbport.zgwl.contractlaunch.utils.ContractLaunchConstants; +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; +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.RestController; + +import java.util.Map; + +/** + * 合同发起 Controller + * + * 这里除了正常 CRUD,还额外提供了几类“测试按钮”接口: + * 1. 模拟法务审批通过 / 驳回 + * 2. 模拟合同采集 + * 3. 合同归档 + * 4. 回写签章结果 + * + * 这些测试接口在法务系统不可用期间便于你完整演示流程, + * 后续接入真实系统后,保留或删除都很方便。 + */ +@RestController +@RequiredArgsConstructor +@RequestMapping(ContractLaunchConstants.REQUEST_PREFIX) +public class ContractLaunchController { + + private final ContractLaunchService contractLaunchService; + + /** + * 分页查询合同列表。 + * + * 支持按合同名称、合同编号、合同类型、状态、创建时间范围过滤。 + * 返回结果附带合同方摘要、文件列表、履行计划(不含日志,列表页日志量大)。 + * + * 对应前端功能:合同列表页 + * + * 请求体 ContractLaunchQueryDto 字段: + * contractName - 合同名称(模糊) + * contractCode - 合同编号 + * contractType - 合同类型 + * status - 状态(draft/reviewing/rejected/pending_sign/signing/...) + * dateStart - 创建时间范围-开始(yyyy-MM-dd) + * dateEnd - 创建时间范围-结束(yyyy-MM-dd) + */ + @PostMapping("/list") + public R> queryList(@RequestBody ContractLaunchQueryDto queryDto) { + return R.success(contractLaunchService.queryPage(queryDto)); + } + + /** + * 查询合同详情(含所有子表数据)。 + * + * 返回合同基本信息 + 合同方列表 + 文件列表 + 履行计划 + 操作日志。 + * + * 对应前端功能:合同详情页 / 编辑页面回显 + */ + @GetMapping("/{id}") + public R getDetail(@PathVariable("id") Long id) { + return R.success(contractLaunchService.getDetail(id)); + } + + /** + * 创建合同草稿。 + * + * 流程:生成 contractSysId(CLS-xxx)和 contractCode → 保存主表 → 保存子表(合同方/文件/履行计划)→ 记录操作日志 + * 状态变为:draft(草稿) + * + * 必填校验项:合同名称、合同类型、合同类型编码、计价方式、币种、签订方式、合同期限类型、至少一个甲方+乙方 + * + * 对应前端功能:新建合同 → 保存草稿 + */ + @PostMapping + public R createDraft(@RequestBody ContractLaunchDto contract) { + return R.success(contractLaunchService.createDraft(contract)); + } + + /** + * 编辑合同草稿。 + * + * 仅 draft(草稿)或 rejected(已驳回)状态允许编辑。 + * 逻辑:先删子表旧数据,再以当前数据重新插入(替换式更新)。 + * 保留原有的 contractSysId、contractCode、创建人/创建时间 不变。 + * + * 对应前端功能:编辑草稿 → 保存 + */ + @PutMapping + public R updateDraft(@RequestBody ContractLaunchDto contract) { + return R.success(contractLaunchService.updateDraft(contract)); + } + + /** + * 提交法务审批。 + * + * 状态流转:draft/rejected → reviewing + * 调用结算平台 pushContract 接口将合同数据推送给法务系统开始审批流程。 + * 目前法务接口不可用,仅做状态变更 + 记录日志(TODO:联调时打开 settlementPushService.pushContract 调用)。 + * + * 推送法务的报文包含:合同基本字段 + contractOpposites(相对方) + contractKxPlans(履行计划) + 附件等 + * + * 对应前端功能:提交审批按钮 + */ + @PostMapping("/{id}/submit") + public R submitToLaw(@PathVariable("id") Long id) { + return R.success(contractLaunchService.submitToLaw(id)); + } + + /** + * 【测试按钮】模拟法务审批通过。 + * + * 状态流转:reviewing → pending_sign(待签署) + * 生成模拟的 lawContractCode 和 lawContractId 记录到合同上,以便后续流程使用。 + * 可在请求体中传入 approveMessage(审批意见)、lawContractCode、lawContractId 覆盖自动生成的值。 + * + * 真实场景下,此方法应由法务系统的回调接口替代(见 lawApproveCallback)。 + * + * 对应前端功能:测试按钮 → 模拟审批通过 + */ + @PostMapping("/{id}/mock-approve") + public R mockApprove(@PathVariable("id") Long id, + @RequestBody(required = false) ContractLaunchApproveDto approveDto) { + return R.success(contractLaunchService.mockApprove(id, approveDto)); + } + + /** + * 【测试按钮】模拟法务审批驳回。 + * + * 状态流转:reviewing → rejected(已驳回) + * 可在请求体中传入 approveMessage(驳回原因)。 + * 驳回后合同可再次编辑和提交。 + * + * 对应前端功能:测试按钮 → 模拟驳回 + */ + @PostMapping("/{id}/mock-reject") + public R mockReject(@PathVariable("id") Long id, + @RequestBody(required = false) ContractLaunchApproveDto approveDto) { + return R.success(contractLaunchService.mockReject(id, approveDto)); + } + + /** + * 法务审批结果回调入口(供法务系统调用)。 + * + * submitToLaw 时推给法务的 pushUrl 默认指向此接口:POST /contract/launch/callback/law/approve + * 法务审批完成后,会往 pushUrl POST 审批结果。 + * + * 当前法务接口不可用,但此回调接口已预先实现,可接受测试请求验证流程。 + * 请求体使用 Map 接收以兼容不同版本的字段名,内部通过 getFirstText 按多个 key 依次尝试提取。 + * + * 识别合同方式:从报文中取 contractSysId 定位到本系统的合同 + * 判断通过/驳回:从 approveStatus/status/result 等多个字段名中取值判断 + * + * 状态流转:reviewing → pending_sign(通过) / rejected(驳回) + * + * 对应前端功能:法务系统审批完成后自动回调(目前用法务系统暂未对接) + */ + @PostMapping("/callback/law/approve") + public R lawApproveCallback(@RequestBody Map callbackPayload) { + return R.success(contractLaunchService.handleLawApproveCallback(callbackPayload)); + } + + /** + * 签署发起回调(浙江CA安全签调用)。 + * + * 在浙江CA安全签平台创建签署合同后,回写 sealContractId 到本系统合同。 + * 状态流转:pending_sign → signing(签署中) + * + * 请求体 ContractLaunchSealDto 字段: + * sealContractId - 签章平台生成的合同ID(必填) + * sealContractStatus - 签署状态(1:签署中) + * + * 对应前端功能:调起浙江CA签署后,回写签章合同ID + */ + @PostMapping("/{id}/seal/save") + public R saveSealInfo(@PathVariable("id") Long id, + @RequestBody ContractLaunchSealDto sealDto) { + return R.success(contractLaunchService.saveSealInfo(id, sealDto)); + } + + /** + * 签署完成回调(浙江CA安全签/线下签署完成后调用)。 + * + * 所有签署方完成签署后,将签署完成的合同文件信息回写。 + * 状态流转:signing → pending_collect(待采集) + * + * 请求体 ContractLaunchSealDto 字段: + * sealContractStatus - 签署状态(2:签署完成) + * signedFileName - 签署完成后的文件名 + * signedFilePath - 签署文件存储路径 + * signedFileType - 文件类型 + * signedFileSize - 文件大小 + * signedOfflineReason - 线下签署原因(如果未在线签署) + * + * 签署文件会自动保存到 file 子表(fileCategory = "signed"),先删旧签署文件再插入新的。 + * + * 对应前端功能:签署完成 → 回写签署结果 + */ + @PostMapping("/{id}/seal/finish") + public R finishSeal(@PathVariable("id") Long id, + @RequestBody ContractLaunchSealDto sealDto) { + return R.success(contractLaunchService.markSealCompleted(id, sealDto)); + } + + /** + * 【测试按钮】模拟合同采集。 + * + * 合同签署完成后,调用数据采集 V3.0 的 collectContract 接口将合同信息推送到法务系统存档。 + * 目前法务接口不可用,仅做状态变更 + 记录日志(TODO:联调时打开 dataCollectionPushService.collectContract 调用)。 + * + * 状态流转:pending_collect → pending_archive(待归档) + * + * 推送数据采集的报文包含:合同基本字段 + 用印信息(c_seal)+ 签订信息(c_sign)+ 附件等 + * + * 对应前端功能:测试按钮 → 模拟采集 + */ + @PostMapping("/{id}/mock-collect") + public R mockCollect(@PathVariable("id") Long id) { + return R.success(contractLaunchService.mockCollect(id)); + } + + /** + * 合同归档。 + * + * 合同全生命周期最后一步,状态变为 completed(已完成)。 + * 同时将归档信息推送到法务系统存档(TODO:联调时打开 settlementPushService.pushArchive 调用)。 + * + * 请求体 ContractLaunchArchiveDto 字段: + * archiveKeeper - 档案保管人手机号 + * archiveKeeperUnit - 档案保管单位名称 + * archiveKeeperUnitCode - 档案保管单位编码 + * archiveFiles - 归档附件列表 + * + * 归档附件自动保存到 file 子表(fileCategory = "archive")。 + * + * 对应前端功能:归档按钮 → 合同完结 + */ + @PostMapping("/{id}/archive") + public R archive(@PathVariable("id") Long id, + @RequestBody(required = false) ContractLaunchArchiveDto archiveDto) { + return R.success(contractLaunchService.archive(id, archiveDto)); + } + + /** + * 删除合同草稿(逻辑删除)。 + * + * 仅 draft(草稿)或 rejected(已驳回)状态允许删除。 + * 逻辑:设置 isDeleted = 'Y',不物理删除数据。 + * + * 对应前端功能:删除草稿按钮 + */ + @DeleteMapping("/{id}") + public R deleteDraft(@PathVariable("id") Long id) { + contractLaunchService.deleteDraft(id); + return R.success("删除成功"); + } +} diff --git a/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchApproveDto.java b/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchApproveDto.java new file mode 100644 index 0000000..0d800cf --- /dev/null +++ b/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchApproveDto.java @@ -0,0 +1,34 @@ +package com.nbport.zgwl.contractlaunch.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 模拟审批 / 驳回参数 + * + * 当前法务接口不可用,通过「测试按钮」手动审批时携带此参数。 + * 真实联调后,此 DTO 可由法务回调接口的报文替代。 + */ +@Data +public class ContractLaunchApproveDto implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** 审批意见 */ + private String approveMessage; + + /** + * 法务回调返回的合同编号。 + * 模拟时自动生成,真实联调时由法务系统返回。 + */ + private String lawContractCode; + + /** + * 法务回调返回的合同ID。 + * 模拟时自动生成,真实联调时由法务系统返回。 + */ + private String lawContractId; +} diff --git a/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchArchiveDto.java b/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchArchiveDto.java new file mode 100644 index 0000000..ca7d96a --- /dev/null +++ b/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchArchiveDto.java @@ -0,0 +1,30 @@ +package com.nbport.zgwl.contractlaunch.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * 合同归档参数 + */ +@Data +public class ContractLaunchArchiveDto implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** 档案保管人手机号 */ + private String archiveKeeper; + + /** 档案保管单位名称 */ + private String archiveKeeperUnit; + + /** 档案保管单位编码 */ + private String archiveKeeperUnitCode; + + /** 归档附件列表(纸质或电子档案材料) */ + private List archiveFiles = new ArrayList<>(); +} diff --git a/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchDto.java b/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchDto.java new file mode 100644 index 0000000..55a3999 --- /dev/null +++ b/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchDto.java @@ -0,0 +1,359 @@ +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; +import java.util.ArrayList; +import java.util.List; + +/** + * 合同发起主对象 + * + * 这里尽量把前后端会直接用到的字段都放在一个 DTO 里, + * 方便你后续排查和修改,不做过深的分层。 + */ +@Data +public class ContractLaunchDto implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + // ==================== 主键与编号 ==================== + + /** 主键ID */ + private Long id; + + /** 合同系统唯一编码(本系统生成,格式 CLS-时间戳-随机数) */ + private String contractSysId; + + /** 业务合同编号(如 MM-20260522-1234,前端可手动输入,不传则自动生成) */ + private String contractCode; + + /** 法务系统生成的合同编号(审批通过后法务返回) */ + private String lawContractCode; + + /** 法务系统返回的合同ID(审批通过后法务返回) */ + private String lawContractId; + + // ==================== 合同基本信息 ==================== + + /** 合同名称 */ + private String contractName; + + /** 合同类型中文名(如 买卖合同、服务合同) */ + private String contractType; + + /** 合同类型编码(如 MM/GY/ZL,见文档附录字典) */ + private String contractTypeCode; + + /** 合同来源(ECP:结算平台, DSSP:大宗平台) */ + private String contractSource; + + /** 创建方式(template:从模板创建, manual:手动创建) */ + private String creationMode; + + /** 选择的模板ID */ + private Long templateId; + + /** 选择的模板编号 */ + private String templateCode; + + /** 选择的模板名称 */ + private String templateName; + + // ==================== 流程状态 ==================== + + /** + * 合同状态流转: + * draft → reviewing → (approved→pending_sign / rejected) + * pending_sign → signing → pending_collect → pending_archive → completed + */ + private String status; + + /** 审批状态(pending:待审批, approved:已通过, rejected:已驳回) */ + private String approveStatus; + + /** 审批意见/消息 */ + private String approveMessage; + + // ==================== 签章状态 ==================== + + /** 签章平台合同ID(浙江CA安全签创建后返回) */ + private String sealContractId; + + /** 签章状态(1:签署中, 2:签署完成, 3:签署失败, null:未发起) */ + private Integer sealContractStatus; + + // ==================== 金额与币种 ==================== + + /** 合同标的额(元) */ + private Double contractAmount; + + /** 币种(RMB/USD/HKD/EUR/IDR) */ + private String currencyName; + + /** 合同期限类型(0:固定期限, 1:无固定期限) */ + private String contractPeriodType; + + /** 期限说明(无固定期限时必填) */ + private String periodExplain; + + /** 合同开始日期 */ + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private LocalDateTime effectiveStart; + + /** 合同结束日期 */ + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private LocalDateTime effectiveEnd; + + /** 付款方式 */ + private String paymentMethod; + + /** 合同主要内容 */ + private String primaryContent; + + /** 金额说明(计价方式为无金额时填写) */ + private String amountExplain; + + /** 计价方式(0:无金额, 1:固定总价, 2:预估总价, 3:无法预估总价, 4:固定单价) */ + private String valuationMode; + + // ==================== 签署相关 ==================== + + /** 签订方式(GZ:公章, HTZYZ:合同专用章, 等) */ + private String sealType; + + /** 文本来源(standardText:推荐文本, exampleText:示范文本, draftBySelf:自行起草, otherText:对方文本) */ + private String textSource; + + /** 授权方式(当签订方式为"公章/合同专用章+被授权人签字"时必填) */ + private String mandateType; + + /** 货类 */ + private String cargoType; + + /** 协助部门名称 */ + private String assistDepartmentName; + + /** 执行人账号(手机号) */ + private String executorAccount; + + /** 创建人手机号 */ + private String creatorPhone; + + /** 二级单位合同编码 */ + private String selfCode; + + /** 补录主合同编码(用于关联主合同,多个用逗号分割) */ + private String mainContractCode; + + /** + * 签约主体编码,推送法务字段 signingSubjectCode。 + * 与 orgId 分开保存,避免被当前登录人的组织ID覆盖。 + */ + private String signingSubjectCode; + + /** + * 固定期间日期类型,推送法务字段 dateType:0-年,1-月,2-日 + */ + private String dateType; + + // ==================== 业务标识 ==================== + + /** 是否补录合同(1:补录, 2:非补录) */ + private Integer isAddit; + + /** 签署方式(0:纸质合同, 1:电子合同,默认1) */ + private String isElectron; + + /** 收支方向(1:付款, 2:收款, 3:收支双向, 0:无) */ + private String paymentDirection; + + /** + * 回调推送地址,法务审批完成后回调本系统使用。 + * 真实地址后续可以由配置文件统一生成。 + */ + private String pushUrl; + + /** 是否重大合同(1:是, 0:否) */ + private Integer isMajorContract; + + /** 是否公开招标(1:是, 0:否) */ + private Integer isOpenBidding; + + /** 是否列入当年预算(1:是, 0:否) */ + private Integer isYearBudget; + + /** 是否共享(1:是, 0:否) */ + private String isShare; + + /** 签约主体是否涉外(1:是, 0:否) */ + private String contractNature; + + /** 签约主体是否涉港澳台(1:是, 0:否) */ + private String isSgat; + + /** 是否正常 */ + private String isNormal; + + /** 是否终止 */ + private String isTerminated; + + /** 是否单方协议(Y/N) */ + private String isSingleAgreement; + + /** 招标编号 */ + private String biddingNo; + + /** 公开招标说明 */ + private String openBiddingRemark; + + /** 签订依据说明 */ + private String contractGistRemark; + + /** + * 额外金额类型,多个用逗号拼接:DJ-定金,YJ-押金,YFK-预付款,BZJ-保证金 + */ + private String ewAmountType; + + /** 定金金额 */ + private Double djAmount; + + /** 押金金额 */ + private Double yjAmount; + + /** 预付款金额 */ + private Double yfkAmount; + + /** 保证金金额 */ + private Double bzjAmount; + + /** 正文/摘要 */ + private String bodySummary; + + // ==================== 文件信息 ==================== + + /** 合同正文文件名 */ + private String contractTextFileName; + + /** 合同正文文件路径 */ + private String contractTextFilePath; + + /** 合同正文文件类型 */ + private String contractTextFileType; + + /** 合同正文文件大小(字节) */ + private Long contractTextFileSize; + + // ==================== 签署完成信息 ==================== + + /** 签署完成后的合同文件名 */ + private String signedFileName; + + /** 签署完成后的合同文件路径 */ + private String signedFilePath; + + /** 签署完成后的合同文件类型 */ + private String signedFileType; + + /** 签署完成后的合同文件大小(字节) */ + private Long signedFileSize; + + /** 未在线签署的原因(线下签署时填写) */ + private String signedOfflineReason; + + // ==================== 签署与归档时间 ==================== + + /** 签署日期 */ + @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; + + /** 归档日期 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime archiveDate; + + /** 档案保管人手机号 */ + private String archiveKeeper; + + /** 档案保管单位 */ + private String archiveKeeperUnit; + + /** 档案保管单位编码 */ + private String archiveKeeperUnitCode; + + // ==================== 业务扩展字段(财务提醒) ==================== + + /** 开票时间(用于账单开票提醒,提前3天提醒) */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime invoiceDate; + + /** 收款时间(用于应收账款提醒,提前3天提醒) */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime receiveDate; + + /** 提前提醒天数 */ + private Integer remindDays; + + // ==================== 创建信息 ==================== + + /** 发起人 */ + private String initiator; + + /** 发起人部门 */ + private String initiatorDept; + + /** 公司名称(当前登录人所在公司) */ + private String companyName; + + /** 公司/组织编码 */ + private String orgId; + + /** 查询范围(personal:个人, company:公司, admin:管理员) */ + private String queryScope; + + /** 创建人 */ + private String createBy; + + /** 创建时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime createTime; + + /** 更新人 */ + private String updateBy; + + /** 更新时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime updateTime; + + /** 删除标记(N:未删除, Y:已删除) */ + private String isDeleted; + + /** 备注 */ + private String remark; + + // ==================== 子表数据(非数据库字段) ==================== + + /** + * 前端表格直接展示用的相对方摘要 + */ + private String counterpartySummary; + + /** 合同方列表(甲方/乙方/丙方等) */ + private List partyList = new ArrayList<>(); + + /** 附件文件列表(正文/签订依据/其他附件/签署文件/归档文件) */ + private List fileList = new ArrayList<>(); + + /** 履行计划列表 */ + private List performancePlans = new ArrayList<>(); + + /** 操作日志列表 */ + private List logList = new ArrayList<>(); +} diff --git a/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchFileDto.java b/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchFileDto.java new file mode 100644 index 0000000..145709b --- /dev/null +++ b/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchFileDto.java @@ -0,0 +1,43 @@ +package com.nbport.zgwl.contractlaunch.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 合同文件信息 + */ +@Data +public class ContractLaunchFileDto implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** 主键ID */ + private Long id; + + /** 关联合同主表 ID */ + private Long contractId; + + /** 文件分类:body-正文, gist-签订依据, other-其他附件, signed-签署文件, archive-归档文件 */ + private String fileCategory; + + /** 文件名 */ + private String fileName; + + /** 文件路径(上传到法务系统后返回的 path,或本系统存储路径) */ + private String filePath; + + /** 文件类型(docx/pdf 等) */ + private String fileType; + + /** 文件大小(字节) */ + private Long fileSize; + + /** 排序号 */ + private Integer sortOrder; + + /** 备注 */ + private String remark; +} diff --git a/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchLogDto.java b/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchLogDto.java new file mode 100644 index 0000000..dbc4bdc --- /dev/null +++ b/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchLogDto.java @@ -0,0 +1,52 @@ +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; + +/** + * 合同操作日志 + */ +@Data +public class ContractLaunchLogDto implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** 主键ID */ + private Long id; + + /** 关联合同主表 ID */ + private Long contractId; + + /** 操作类型(save/submit/approve/reject/seal_start/seal_finish/collect/archive/delete) */ + private String actionType; + + /** 操作名称(中文描述,如"保存草稿"、"提交法务") */ + private String actionName; + + /** 操作状态(success/failed/pending) */ + private String actionStatus; + + /** 操作人姓名 */ + private String operatorName; + + /** 操作人手机号 */ + private String operatorPhone; + + /** 操作时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime actionTime; + + /** 操作内容描述 */ + private String actionContent; + + /** 回调/操作的原始报文(JSON 文本存储,用于追溯) */ + private String callbackPayload; + + /** 备注 */ + private String remark; +} diff --git a/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchPartyDto.java b/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchPartyDto.java new file mode 100644 index 0000000..dc80865 --- /dev/null +++ b/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchPartyDto.java @@ -0,0 +1,88 @@ +package com.nbport.zgwl.contractlaunch.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 合同方信息 + */ +@Data +public class ContractLaunchPartyDto implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** 主键ID */ + private Long id; + + /** 关联合同主表 ID */ + private Long contractId; + + /** 角色编码(A:甲方/我方, B:乙方/相对方, C:丙方 等) */ + private String roleCode; + + /** 角色名称(甲方/乙方/丙方) */ + private String roleName; + + /** 排序号 */ + private Integer sortOrder; + + /** 关联相对方主表 ID(partner 模块的 customerSysid) */ + private Long partnerId; + + /** 法务系统的相对方ID(jtId,采集后返回) */ + private String oppositeId; + + /** 公司全称 */ + private String companyName; + + /** 公司类型(client/supplier/Partner/clientAndSup/other) */ + private String companyType; + + /** 相对方性质(1:境内组织,2:境内个人,3:境外组织,4:境外个人) */ + private String oppositeCharacter; + + /** 联系人姓名 */ + private String contactName; + + /** 联系人电话 */ + private String contactPhone; + + /** 联系人邮箱 */ + private String contactEmail; + + /** 联系地址 */ + private String contactAddress; + + /** 注册地址 */ + private String registeredAddress; + + /** 法定代表人 */ + private String legalRepresentative; + + /** 统一社会信用代码 */ + private String creditCode; + + /** 开户银行 */ + private String bankName; + + /** 银行账户 */ + private String bankAccount; + + /** 自然人证件类型 */ + private String naturalPersonIdType; + + /** 自然人证件号码 */ + private String naturalPersonIdNumber; + + /** 是否集团内(Y/N) */ + private String isGroupInternal; + + /** 是否涉港澳台(Y/N) */ + private String isHongKongMacaoTaiwan; + + /** 备注 */ + private String remark; +} diff --git a/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchPlanDto.java b/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchPlanDto.java new file mode 100644 index 0000000..cce5fd3 --- /dev/null +++ b/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchPlanDto.java @@ -0,0 +1,62 @@ +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; + +/** + * 履行计划 + * + * 对应结算平台推送合同接口的 contractKxPlans 字段。 + * 与法务接口文档中的款项计划一一映射。 + */ +@Data +public class ContractLaunchPlanDto implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** 主键ID */ + private Long id; + + /** 关联合同主表 ID */ + private Long contractId; + + /** 履行事项(如:第一笔付款) */ + private String planMatter; + + /** 履约条件(如:达到xx条件) */ + private String planWhere; + + /** 节点日期 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime planTime; + + /** 提前提醒天数 */ + private Integer planDay; + + /** 款项金额 */ + private Double planMoney; + + /** + * 所属主体编码,推送法务字段 planSubjectCode。 + */ + private String planSubjectCode; + + /** + * 所属主体名称,推送法务字段 planSubjectName。 + */ + private String planSubjectName; + + /** 备注 */ + private String planDetail; + + /** 履行状态(0:待履行, 1:已履行, 2:逾期) */ + private String planStatus; + + /** 备注 */ + private String remark; +} diff --git a/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchQueryDto.java b/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchQueryDto.java new file mode 100644 index 0000000..ba57dcf --- /dev/null +++ b/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchQueryDto.java @@ -0,0 +1,37 @@ +package com.nbport.zgwl.contractlaunch.dto; + +import com.nbport.zgwl.dto.BasePageDto; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 合同发起列表查询参数 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class ContractLaunchQueryDto extends BasePageDto implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** 合同名称(模糊搜索) */ + private String contractName; + + /** 合同编号 */ + private String contractCode; + + /** 合同类型 */ + private String contractType; + + /** 状态(draft/reviewing/rejected/pending_sign/signing/pending_collect/pending_archive/completed) */ + private String status; + + /** 创建时间范围-开始 yyyy-MM-dd */ + private String dateStart; + + /** 创建时间范围-结束 yyyy-MM-dd */ + private String dateEnd; +} diff --git a/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchSealDto.java b/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchSealDto.java new file mode 100644 index 0000000..b978f8f --- /dev/null +++ b/src/main/java/com/nbport/zgwl/contractlaunch/dto/ContractLaunchSealDto.java @@ -0,0 +1,40 @@ +package com.nbport.zgwl.contractlaunch.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 签章回写参数 + * + * 签署完成后,浙江CA安全签平台通过回调通知我方, + * 我方把签署结果回写到合同的 seal_* 字段。 + */ +@Data +public class ContractLaunchSealDto implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** 签章平台生成的合同ID(用于后续查询签署进度) */ + private String sealContractId; + + /** 签章状态(1:签署中, 2:签署完成, 3:签署失败) */ + private Integer sealContractStatus; + + /** 签署完成后的合同文件名 */ + private String signedFileName; + + /** 签署完成后的合同文件路径(我方存储) */ + private String signedFilePath; + + /** 签署完成后的合同文件类型 */ + private String signedFileType; + + /** 签署完成后的合同文件大小 */ + private Long signedFileSize; + + /** 未在线签署的原因(线下签署时填写) */ + private String signedOfflineReason; +} diff --git a/src/main/java/com/nbport/zgwl/contractlaunch/mapper/ContractLaunchMapper.java b/src/main/java/com/nbport/zgwl/contractlaunch/mapper/ContractLaunchMapper.java new file mode 100644 index 0000000..fb90bec --- /dev/null +++ b/src/main/java/com/nbport/zgwl/contractlaunch/mapper/ContractLaunchMapper.java @@ -0,0 +1,96 @@ +package com.nbport.zgwl.contractlaunch.mapper; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.nbport.zgwl.contractlaunch.dto.ContractLaunchDto; +import com.nbport.zgwl.contractlaunch.dto.ContractLaunchFileDto; +import com.nbport.zgwl.contractlaunch.dto.ContractLaunchLogDto; +import com.nbport.zgwl.contractlaunch.dto.ContractLaunchPartyDto; +import com.nbport.zgwl.contractlaunch.dto.ContractLaunchPlanDto; +import com.nbport.zgwl.contractlaunch.dto.ContractLaunchQueryDto; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +public interface ContractLaunchMapper { + + /** 获取主表序列(Oracle 主键生成) */ + Long selectMainSeqNextVal(); + + /** 获取合同方表序列 */ + Long selectPartySeqNextVal(); + + /** 获取文件表序列 */ + Long selectFileSeqNextVal(); + + /** 获取履行计划表序列 */ + Long selectPlanSeqNextVal(); + + /** 获取日志表序列 */ + Long selectLogSeqNextVal(); + + /** 分页查询合同列表 */ + IPage selectContractPage(Page page, @Param("query") ContractLaunchQueryDto queryDto); + + /** 根据主键查询合同 */ + ContractLaunchDto selectContractById(@Param("id") Long id); + + /** 根据 contractSysId 查询合同(法务回调时用于定位) */ + ContractLaunchDto selectContractByContractSysId(@Param("contractSysId") String contractSysId); + + /** 新增合同 */ + int insertContract(ContractLaunchDto contract); + + /** 修改合同 */ + int updateContract(ContractLaunchDto contract); + + /** 逻辑删除合同 */ + int deleteContract(@Param("id") Long id, @Param("updateBy") String updateBy); + + /** 批量插入合同方 */ + int batchInsertParty(@Param("list") List list, + @Param("createBy") String createBy, + @Param("updateBy") String updateBy); + + /** 批量插入文件 */ + int batchInsertFile(@Param("list") List list, + @Param("createBy") String createBy, + @Param("updateBy") String updateBy); + + /** 批量插入履行计划 */ + int batchInsertPlan(@Param("list") List list, + @Param("createBy") String createBy, + @Param("updateBy") String updateBy); + + /** 插入单条操作日志 */ + int insertLog(ContractLaunchLogDto logDto); + + /** 删除合同下的所有合同方(逻辑删除) */ + int deletePartyByContractId(@Param("contractId") Long contractId, + @Param("updateBy") String updateBy); + + /** 删除合同下的所有文件(逻辑删除) */ + int deleteFileByContractId(@Param("contractId") Long contractId, + @Param("updateBy") String updateBy); + + /** 按分类删除合同下的文件(如重新上传正文时只删正文,不删其他附件) */ + int deleteFileByContractIdAndCategory(@Param("contractId") Long contractId, + @Param("fileCategory") String fileCategory, + @Param("updateBy") String updateBy); + + /** 删除合同下的所有履行计划(逻辑删除) */ + int deletePlanByContractId(@Param("contractId") Long contractId, + @Param("updateBy") String updateBy); + + /** 批量查询合同方(按 contractIds) */ + List selectPartyByContractIds(@Param("contractIds") List contractIds); + + /** 批量查询文件(按 contractIds) */ + List selectFileByContractIds(@Param("contractIds") List contractIds); + + /** 批量查询履行计划(按 contractIds) */ + List selectPlanByContractIds(@Param("contractIds") List contractIds); + + /** 批量查询操作日志(按 contractIds) */ + List selectLogByContractIds(@Param("contractIds") List contractIds); +} diff --git a/src/main/java/com/nbport/zgwl/contractlaunch/service/ContractLaunchService.java b/src/main/java/com/nbport/zgwl/contractlaunch/service/ContractLaunchService.java new file mode 100644 index 0000000..3dd31ec --- /dev/null +++ b/src/main/java/com/nbport/zgwl/contractlaunch/service/ContractLaunchService.java @@ -0,0 +1,52 @@ +package com.nbport.zgwl.contractlaunch.service; + +import com.nbport.zgwl.common.PageResult; +import com.nbport.zgwl.contractlaunch.dto.ContractLaunchApproveDto; +import com.nbport.zgwl.contractlaunch.dto.ContractLaunchArchiveDto; +import com.nbport.zgwl.contractlaunch.dto.ContractLaunchDto; +import com.nbport.zgwl.contractlaunch.dto.ContractLaunchQueryDto; +import com.nbport.zgwl.contractlaunch.dto.ContractLaunchSealDto; + +import java.util.Map; + +public interface ContractLaunchService { + + /** 分页查询合同列表 */ + PageResult queryPage(ContractLaunchQueryDto queryDto); + + /** 查询合同详情(含合同方/文件/履行计划/日志) */ + ContractLaunchDto getDetail(Long id); + + /** 创建合同草稿 */ + ContractLaunchDto createDraft(ContractLaunchDto contract); + + /** 编辑合同草稿 */ + ContractLaunchDto updateDraft(ContractLaunchDto contract); + + /** 提交法务审批(调用结算平台 pushContract 接口) */ + ContractLaunchDto submitToLaw(Long id); + + /** 模拟审批通过(测试按钮,TODO:替换为真实法务回调) */ + ContractLaunchDto mockApprove(Long id, ContractLaunchApproveDto approveDto); + + /** 模拟审批驳回(测试按钮,TODO:替换为真实法务回调) */ + ContractLaunchDto mockReject(Long id, ContractLaunchApproveDto approveDto); + + /** 接收法务审批回调(处理审批结果,更新合同状态) */ + ContractLaunchDto handleLawApproveCallback(Map callbackPayload); + + /** 回写签章信息(签署发起时调用,记录 sealContractId) */ + ContractLaunchDto saveSealInfo(Long id, ContractLaunchSealDto sealDto); + + /** 签署完成回调(回写签署文件路径,状态变更为待采集) */ + ContractLaunchDto markSealCompleted(Long id, ContractLaunchSealDto sealDto); + + /** 模拟合同采集(调用数据采集 collectContract 接口,TODO:替换为真实调用) */ + ContractLaunchDto mockCollect(Long id); + + /** 合同归档(状态变更为已完成,TODO:同时推送归档到法务) */ + ContractLaunchDto archive(Long id, ContractLaunchArchiveDto archiveDto); + + /** 删除草稿(仅 draft/rejected 状态允许) */ + void deleteDraft(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 new file mode 100644 index 0000000..c86dc48 --- /dev/null +++ b/src/main/java/com/nbport/zgwl/contractlaunch/service/ContractLaunchServiceImpl.java @@ -0,0 +1,937 @@ +package com.nbport.zgwl.contractlaunch.service; + +import com.alibaba.fastjson2.JSON; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.nbport.zgwl.common.PageResult; +import com.nbport.zgwl.contractlaunch.dto.ContractLaunchApproveDto; +import com.nbport.zgwl.contractlaunch.dto.ContractLaunchArchiveDto; +import com.nbport.zgwl.contractlaunch.dto.ContractLaunchDto; +import com.nbport.zgwl.contractlaunch.dto.ContractLaunchFileDto; +import com.nbport.zgwl.contractlaunch.dto.ContractLaunchLogDto; +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.mapper.ContractLaunchMapper; +import com.nbport.zgwl.contractlaunch.utils.ContractLaunchCodeUtils; +import com.nbport.zgwl.contractlaunch.utils.ContractLaunchConstants; +import com.nbport.zgwl.contractpush.dto.PushPayloadDTO; +import com.nbport.zgwl.contractpush.service.DataCollectionPushService; +import com.nbport.zgwl.contractpush.service.SettlementPushService; +import com.nbport.zgwl.entity.SysUserEntity; +import com.nbport.zgwl.exception.ServiceException; +import com.nbport.zgwl.utils.AdminSecurityManage; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +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.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * 合同发起主服务 + * + * 这里尽量把核心流程都写在一个 Service 里, + * 方便你后续自己看和改,不额外再拆很多 handler。 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class ContractLaunchServiceImpl implements ContractLaunchService { + + private final ContractLaunchMapper contractLaunchMapper; + private final AdminSecurityManage adminSecurityManage; + private final SettlementPushService settlementPushService; + private final DataCollectionPushService dataCollectionPushService; + + /** 分页查询,附带合同方/文件/履行计划(列表页不查日志) */ + @Override + public PageResult queryPage(ContractLaunchQueryDto queryDto) { + Page page = new Page<>(queryDto.getPageNo(), queryDto.getPageSize()); + Page resultPage = (Page) contractLaunchMapper.selectContractPage(page, queryDto); + PageResult pageResult = new PageResult<>(resultPage); + fillChildren(pageResult.getData(), false); + return pageResult; + } + + /** 查询详情,含日志 */ + @Override + public ContractLaunchDto getDetail(Long id) { + ContractLaunchDto contract = contractLaunchMapper.selectContractById(id); + if (contract == null) { + throw new ServiceException("合同不存在"); + } + fillChildren(Collections.singletonList(contract), true); + return contract; + } + + /** + * 创建合同草稿。 + * + * 完整流程: + * 1. 获取当前登录用户信息 + * 2. 数据清洗(normalizeContract:去空格、补默认值) + * 3. 数据校验(validateDraft:必填项检查) + * 4. 生成主键ID 和 contractSysId(CLS-时间戳)、contractCode(类型编码-日期-随机数) + * 5. 填充创建人/创建时间/公司/部门/状态 draft + * 6. 保存主表 + 子表(合同方/文件/履行计划) + * 7. 记录操作日志 + * + * 注意:此时仅在本系统保存,未推送到法务系统。 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public ContractLaunchDto createDraft(ContractLaunchDto contract) { + SysUserEntity currentUser = adminSecurityManage.getUser(); + normalizeContract(contract); + validateDraft(contract); + + LocalDateTime now = LocalDateTime.now(); + contract.setId(contractLaunchMapper.selectMainSeqNextVal()); + contract.setContractSysId(ContractLaunchCodeUtils.buildContractSysId()); + if (contract.getContractCode() == null || contract.getContractCode().isBlank()) { + contract.setContractCode(ContractLaunchCodeUtils.buildContractCode(contract.getContractTypeCode())); + } + contract.setContractSource(contract.getContractSource() == null || contract.getContractSource().isBlank() ? "ECP" : contract.getContractSource()); + contract.setStatus(ContractLaunchConstants.STATUS_DRAFT); + contract.setApproveStatus(ContractLaunchConstants.APPROVE_STATUS_PENDING); + contract.setInitiator(currentUser.getUsername()); + contract.setInitiatorDept(currentUser.getSysOrgEntity() == null ? "" : currentUser.getSysOrgEntity().getOrgName()); + contract.setCompanyName(currentUser.getSysOrgEntity() == null ? "" : currentUser.getSysOrgEntity().getOrgName()); + contract.setOrgId(currentUser.getSysOrgEntity() == null ? "" : currentUser.getSysOrgEntity().getId()); + contract.setCreatorPhone(currentUser.getMobilePhone()); + contract.setCreateBy(currentUser.getUsername()); + contract.setCreateTime(now); + contract.setUpdateBy(currentUser.getUsername()); + contract.setUpdateTime(now); + contract.setIsDeleted(ContractLaunchConstants.IS_DELETED_NO); + contract.setQueryScope("personal"); + contractLaunchMapper.insertContract(contract); + saveChildren(contract, currentUser.getUsername()); + insertLog(contract.getId(), ContractLaunchConstants.ACTION_SAVE, "保存草稿", ContractLaunchConstants.ACTION_STATUS_SUCCESS, "合同草稿创建成功", null, currentUser); + return getDetail(contract.getId()); + } + + /** + * 编辑合同草稿。 + * + * 仅 draft(草稿)或 rejected(已驳回)可编辑。 + * 采用替换式更新:先删子表(合同方/文件/履行计划)旧数据,再以当前数据重新插入。 + * 保留以下字段不变:contractSysId、contractCode、lawContractCode、lawContractId、 + * 状态、审批状态、发起人、公司、orgId、创建人/创建时间。 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public ContractLaunchDto updateDraft(ContractLaunchDto contract) { + if (contract.getId() == null) { + throw new ServiceException("修改时 id 不能为空"); + } + ContractLaunchDto dbContract = getDetail(contract.getId()); + if (!canEdit(dbContract.getStatus())) { + throw new ServiceException("当前状态不允许编辑"); + } + + SysUserEntity currentUser = adminSecurityManage.getUser(); + normalizeContract(contract); + validateDraft(contract); + contract.setContractSysId(dbContract.getContractSysId()); + contract.setContractCode(dbContract.getContractCode()); + contract.setLawContractCode(dbContract.getLawContractCode()); + contract.setLawContractId(dbContract.getLawContractId()); + contract.setStatus(dbContract.getStatus()); + contract.setApproveStatus(dbContract.getApproveStatus()); + contract.setInitiator(dbContract.getInitiator()); + contract.setInitiatorDept(dbContract.getInitiatorDept()); + contract.setCompanyName(dbContract.getCompanyName()); + contract.setOrgId(dbContract.getOrgId()); + contract.setCreatorPhone(dbContract.getCreatorPhone()); + contract.setCreateBy(dbContract.getCreateBy()); + contract.setCreateTime(dbContract.getCreateTime()); + contract.setUpdateBy(currentUser.getUsername()); + contract.setUpdateTime(LocalDateTime.now()); + contract.setIsDeleted(ContractLaunchConstants.IS_DELETED_NO); + contractLaunchMapper.updateContract(contract); + replaceChildren(contract, currentUser.getUsername()); + insertLog(contract.getId(), ContractLaunchConstants.ACTION_SAVE, "编辑草稿", ContractLaunchConstants.ACTION_STATUS_SUCCESS, "合同草稿修改成功", null, currentUser); + return getDetail(contract.getId()); + } + + /** + * 提交法务审批。 + * + * 状态流转:draft/rejected → reviewing(待审批) + * + * 主要做两件事: + * 1. 更新合同状态为 reviewing + * 2. 构建结算平台 pushContract 接口的完整报文(含合同方、履行计划、附件等) + * 3. 记录提交日志(日志中保存了完整的推送报文,方便排查) + * + * TODO(真实联调时打开): + * PushPayloadDTO requestDTO = new PushPayloadDTO(); + * requestDTO.setPayload(settlementPayload); + * settlementPushService.pushContract(requestDTO); + * + * 法务审批完成后会通过 pushUrl 回调本系统的 /callback/law/approve 接口。 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public ContractLaunchDto submitToLaw(Long id) { + ContractLaunchDto contract = getDetail(id); + if (!canSubmit(contract.getStatus())) { + throw new ServiceException("当前状态不允许提交法务"); + } + SysUserEntity currentUser = adminSecurityManage.getUser(); + contract.setStatus(ContractLaunchConstants.STATUS_REVIEWING); + contract.setApproveStatus(ContractLaunchConstants.APPROVE_STATUS_PENDING); + contract.setApproveMessage("已提交法务,等待审批"); + contract.setUpdateBy(currentUser.getUsername()); + contract.setUpdateTime(LocalDateTime.now()); + contractLaunchMapper.updateContract(contract); + + Map settlementPayload = buildSettlementPushPayload(contract); + insertLog(id, ContractLaunchConstants.ACTION_SUBMIT, "提交法务", ContractLaunchConstants.ACTION_STATUS_PENDING, "合同已提交法务接口,当前先保留 mock 流程", settlementPayload, currentUser); + + // TODO: 法务接口当前不可用,真实联调时打开下面调用即可 + // PushPayloadDTO requestDTO = new PushPayloadDTO(); + // requestDTO.setPayload(settlementPayload); + // Map response = settlementPushService.pushContract(requestDTO); + + return getDetail(id); + } + + /** + * 【测试按钮】模拟法务审批通过。 + * + * 状态流转:reviewing → pending_sign(待签署) + * + * 模拟法务系统返回 lawContractCode 和 lawContractId,供后续签署流程使用。 + * 如果 approveDto 中传入了值则使用传入值,否则自动生成模拟值: + * - lawContractCode = "LAW-" + 合同类型编码-日期-随机数 + * - lawContractId = "LAWID-" + 合同ID + * + * 真实场景下此逻辑由法务回调接口 handleLawApproveCallback 替代。 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public ContractLaunchDto mockApprove(Long id, ContractLaunchApproveDto approveDto) { + ContractLaunchDto contract = getDetail(id); + if (!ContractLaunchConstants.STATUS_REVIEWING.equals(contract.getStatus())) { + throw new ServiceException("仅审批中的合同可模拟通过"); + } + SysUserEntity currentUser = adminSecurityManage.getUser(); + contract.setStatus(ContractLaunchConstants.STATUS_PENDING_SIGN); + contract.setApproveStatus(ContractLaunchConstants.APPROVE_STATUS_APPROVED); + contract.setApproveMessage(approveDto == null || approveDto.getApproveMessage() == null || approveDto.getApproveMessage().isBlank() + ? "法务审批通过(测试按钮模拟)" + : approveDto.getApproveMessage()); + contract.setLawContractCode(approveDto == null || approveDto.getLawContractCode() == null || approveDto.getLawContractCode().isBlank() + ? "LAW-" + ContractLaunchCodeUtils.buildContractCode(contract.getContractTypeCode()) + : approveDto.getLawContractCode()); + contract.setLawContractId(approveDto == null || approveDto.getLawContractId() == null || approveDto.getLawContractId().isBlank() + ? "LAWID-" + id + : approveDto.getLawContractId()); + contract.setUpdateBy(currentUser.getUsername()); + contract.setUpdateTime(LocalDateTime.now()); + contractLaunchMapper.updateContract(contract); + insertLog(id, ContractLaunchConstants.ACTION_APPROVE, "模拟审批通过", ContractLaunchConstants.ACTION_STATUS_SUCCESS, contract.getApproveMessage(), approveDto, currentUser); + return getDetail(id); + } + + /** + * 【测试按钮】模拟法务审批驳回。 + * + * 状态流转:reviewing → rejected(已驳回) + * 驳回后合同回到草稿状态,可以再次编辑后重新提交。 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public ContractLaunchDto mockReject(Long id, ContractLaunchApproveDto approveDto) { + ContractLaunchDto contract = getDetail(id); + if (!ContractLaunchConstants.STATUS_REVIEWING.equals(contract.getStatus())) { + throw new ServiceException("仅审批中的合同可模拟驳回"); + } + SysUserEntity currentUser = adminSecurityManage.getUser(); + contract.setStatus(ContractLaunchConstants.STATUS_REJECTED); + contract.setApproveStatus(ContractLaunchConstants.APPROVE_STATUS_REJECTED); + contract.setApproveMessage(approveDto == null || approveDto.getApproveMessage() == null || approveDto.getApproveMessage().isBlank() + ? "法务审批驳回(测试按钮模拟)" + : approveDto.getApproveMessage()); + contract.setUpdateBy(currentUser.getUsername()); + contract.setUpdateTime(LocalDateTime.now()); + contractLaunchMapper.updateContract(contract); + insertLog(id, ContractLaunchConstants.ACTION_REJECT, "模拟审批驳回", ContractLaunchConstants.ACTION_STATUS_SUCCESS, contract.getApproveMessage(), approveDto, currentUser); + return getDetail(id); + } + + /** + * 接收法务审批回调。 + * + * 法务系统审批完成后,通过 pushUrl POST 审批结果到本接口。 + * + * 处理逻辑: + * 1. 从回调报文中提取 contractSysId 定位到本系统合同 + * 2. 判断审批结果(通过/驳回) + * - 通过:status → pending_sign(待签署),记录法务返回的合同编号和合同ID + * - 驳回:status → rejected(已驳回),记录驳回原因 + * 3. 记录操作日志 + * + * 兼容设计: + * - 使用 getFirstText 按多个 key 依次尝试提取字段(如 approveStatus/status/result) + * - 审批通过状态兼容:1/true/success/approved/pass/agree/审批通过 + * - 合同编号兼容:lawContractCode/contractCode/htCode/code + * - 合同ID兼容:lawContractId/contractId/id/businessId + */ + @Override + @Transactional(rollbackFor = Exception.class) + public ContractLaunchDto handleLawApproveCallback(Map callbackPayload) { + ContractLaunchDto contract = getContractByLawCallback(callbackPayload); + SysUserEntity currentUser = adminSecurityManage.getUser(); + + boolean approved = isLawCallbackApproved(callbackPayload); + contract.setApproveStatus(approved + ? ContractLaunchConstants.APPROVE_STATUS_APPROVED + : ContractLaunchConstants.APPROVE_STATUS_REJECTED); + contract.setStatus(approved + ? ContractLaunchConstants.STATUS_PENDING_SIGN + : ContractLaunchConstants.STATUS_REJECTED); + contract.setApproveMessage(getFirstText(callbackPayload, + "approveMessage", "message", "msg", "opinion", "remark")); + + String lawContractCode = getFirstText(callbackPayload, + "lawContractCode", "contractCode", "htCode", "code"); + String lawContractId = getFirstText(callbackPayload, + "lawContractId", "contractId", "id", "businessId"); + if (lawContractCode != null && !lawContractCode.isBlank()) { + contract.setLawContractCode(lawContractCode); + } + if (lawContractId != null && !lawContractId.isBlank()) { + contract.setLawContractId(lawContractId); + } + + contract.setUpdateBy(currentUser.getUsername()); + contract.setUpdateTime(LocalDateTime.now()); + contractLaunchMapper.updateContract(contract); + insertLog( + contract.getId(), + approved ? ContractLaunchConstants.ACTION_APPROVE : ContractLaunchConstants.ACTION_REJECT, + approved ? "法务审批回调通过" : "法务审批回调驳回", + ContractLaunchConstants.ACTION_STATUS_SUCCESS, + contract.getApproveMessage(), + callbackPayload, + currentUser + ); + return getDetail(contract.getId()); + } + + /** + * 回写签章信息(签署发起时调用)。 + * + * 浙江CA安全签平台创建签署合同后,回调此方法回写 sealContractId。 + * 状态流转:pending_sign → signing(签署中) + * + * sealContractId 由浙江CA安全签平台生成,后续通过此 ID 查询签署进度。 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public ContractLaunchDto saveSealInfo(Long id, ContractLaunchSealDto sealDto) { + ContractLaunchDto contract = getDetail(id); + SysUserEntity currentUser = adminSecurityManage.getUser(); + contract.setSealContractId(sealDto.getSealContractId()); + contract.setSealContractStatus(sealDto.getSealContractStatus()); + contract.setStatus(ContractLaunchConstants.STATUS_SIGNING); + contract.setUpdateBy(currentUser.getUsername()); + contract.setUpdateTime(LocalDateTime.now()); + contractLaunchMapper.updateContract(contract); + insertLog(id, ContractLaunchConstants.ACTION_SEAL_START, "开始签署", ContractLaunchConstants.ACTION_STATUS_SUCCESS, "签章平台合同已创建", sealDto, currentUser); + return getDetail(id); + } + + /** + * 签署完成回调。 + * + * 所有签署方签署完成后(或在浙江CA签署完成、或线下签署后手动上传), + * 回写签署完成的合同文件信息。 + * + * 状态流转:signing → pending_collect(待采集) + * + * 执行步骤: + * 1. 回写 signedFileName/signedFilePath/signedFileType/signedFileSize + * 2. 如果有线下签署原因(signedOfflineReason),一并保存 + * 3. 将签署文件保存到 file 子表(fileCategory = "signed") + * 4. 记录操作日志 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public ContractLaunchDto markSealCompleted(Long id, ContractLaunchSealDto sealDto) { + ContractLaunchDto contract = getDetail(id); + SysUserEntity currentUser = adminSecurityManage.getUser(); + contract.setSealContractId(sealDto.getSealContractId() == null || sealDto.getSealContractId().isBlank() ? contract.getSealContractId() : sealDto.getSealContractId()); + contract.setSealContractStatus(sealDto.getSealContractStatus()); + contract.setSignedFileName(sealDto.getSignedFileName()); + contract.setSignedFilePath(sealDto.getSignedFilePath()); + contract.setSignedFileType(sealDto.getSignedFileType()); + contract.setSignedFileSize(sealDto.getSignedFileSize()); + contract.setSignedOfflineReason(sealDto.getSignedOfflineReason()); + contract.setStatus(ContractLaunchConstants.STATUS_PENDING_COLLECT); + contract.setUpdateBy(currentUser.getUsername()); + contract.setUpdateTime(LocalDateTime.now()); + contractLaunchMapper.updateContract(contract); + saveSignedFile(id, sealDto, currentUser.getUsername()); + insertLog(id, ContractLaunchConstants.ACTION_SEAL_FINISH, "签署完成", ContractLaunchConstants.ACTION_STATUS_SUCCESS, "合同签署已完成,待采集", sealDto, currentUser); + return getDetail(id); + } + + /** + * 【测试按钮】模拟合同采集。 + * + * 合同签署完成后,将合同最新信息(含用印信息和签订信息)推送到法务系统。 + * + * 状态流转:pending_collect → pending_archive(待归档) + * + * 构建数据采集 V3.0 collectContract 接口的报文,包含: + * - 合同基本字段(c_name, c_code, c_type 等) + * - 用印信息 c_seal(签章类型、签章日期等) + * - 签订信息 c_sign(签署日期、生效日期、签署附件等) + * - 履行计划 c_lxPlan + * - 附件(正文 c_zw / 其他 c_attachs / 签署文件 c_signAttachs) + * + * TODO(真实联调时打开): + * PushPayloadDTO requestDTO = new PushPayloadDTO(); + * requestDTO.setPayload(payload); + * dataCollectionPushService.collectContract(requestDTO); + */ + @Override + @Transactional(rollbackFor = Exception.class) + public ContractLaunchDto mockCollect(Long id) { + ContractLaunchDto contract = getDetail(id); + if (!ContractLaunchConstants.STATUS_PENDING_COLLECT.equals(contract.getStatus())) { + throw new ServiceException("当前状态不允许执行采集"); + } + SysUserEntity currentUser = adminSecurityManage.getUser(); + Map payload = buildDataCollectionContractPayload(contract); + insertLog(id, ContractLaunchConstants.ACTION_COLLECT, "模拟合同采集", ContractLaunchConstants.ACTION_STATUS_PENDING, "合同采集接口当前先注释,使用测试按钮流转", payload, currentUser); + + // TODO: 法务接口当前不可用,真实联调时打开下面调用即可 + // PushPayloadDTO requestDTO = new PushPayloadDTO(); + // requestDTO.setPayload(payload); + // dataCollectionPushService.collectContract(requestDTO); + + contract.setStatus(ContractLaunchConstants.STATUS_PENDING_ARCHIVE); + contract.setUpdateBy(currentUser.getUsername()); + contract.setUpdateTime(LocalDateTime.now()); + contractLaunchMapper.updateContract(contract); + return getDetail(id); + } + + /** + * 合同归档。 + * + * 合同全生命周期最后一步。将归档信息(保管人、保管单位、归档附件)保存到合同, + * 同时推送到法务系统存档。 + * + * 状态流转:pending_archive → completed(已完成) + * + * 执行步骤: + * 1. 填写归档日期、保管人信息 + * 2. 保存归档附件到 file 子表(fileCategory = "archive") + * 3. 构建归档报文 pushArchive 接口的 payload + * 4. 记录操作日志 + * + * TODO(真实联调时打开): + * settlementPushService.pushArchive(requestDTO) + * dataCollectionPushService.collectContractArchive(requestDTO) + */ + @Override + @Transactional(rollbackFor = Exception.class) + public ContractLaunchDto archive(Long id, ContractLaunchArchiveDto archiveDto) { + ContractLaunchDto contract = getDetail(id); + if (!ContractLaunchConstants.STATUS_PENDING_ARCHIVE.equals(contract.getStatus())) { + throw new ServiceException("当前状态不允许归档"); + } + SysUserEntity currentUser = adminSecurityManage.getUser(); + contract.setArchiveDate(LocalDateTime.now()); + contract.setArchiveKeeper(archiveDto == null ? null : archiveDto.getArchiveKeeper()); + contract.setArchiveKeeperUnit(archiveDto == null ? null : archiveDto.getArchiveKeeperUnit()); + contract.setArchiveKeeperUnitCode(archiveDto == null ? null : archiveDto.getArchiveKeeperUnitCode()); + contract.setStatus(ContractLaunchConstants.STATUS_COMPLETED); + contract.setUpdateBy(currentUser.getUsername()); + contract.setUpdateTime(LocalDateTime.now()); + contractLaunchMapper.updateContract(contract); + + if (archiveDto != null && archiveDto.getArchiveFiles() != null && !archiveDto.getArchiveFiles().isEmpty()) { + for (ContractLaunchFileDto fileDto : archiveDto.getArchiveFiles()) { + fileDto.setId(contractLaunchMapper.selectFileSeqNextVal()); + fileDto.setContractId(id); + fileDto.setFileCategory(ContractLaunchConstants.FILE_CATEGORY_ARCHIVE); + } + contractLaunchMapper.batchInsertFile(archiveDto.getArchiveFiles(), currentUser.getUsername(), currentUser.getUsername()); + } + + Map payload = buildArchivePayload(contract, archiveDto); + insertLog(id, ContractLaunchConstants.ACTION_ARCHIVE, "合同归档", ContractLaunchConstants.ACTION_STATUS_PENDING, "合同归档接口当前先注释,使用测试按钮流转", payload, currentUser); + + // TODO: 法务接口当前不可用,真实联调时打开下面调用即可 + // PushPayloadDTO requestDTO = new PushPayloadDTO(); + // requestDTO.setPayload(payload); + // settlementPushService.pushArchive(requestDTO); + // dataCollectionPushService.collectContractArchive(requestDTO); + + return getDetail(id); + } + + /** + * 删除合同草稿(逻辑删除)。 + * + * 仅 draft(草稿)或 rejected(已驳回)状态允许删除。 + * 设置 isDeleted = 'Y',不物理删除数据。 + * 已提交法务审批或进行中的合同不允许删除。 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteDraft(Long id) { + ContractLaunchDto contract = getDetail(id); + if (!canDelete(contract.getStatus())) { + throw new ServiceException("仅草稿或驳回状态允许删除"); + } + SysUserEntity currentUser = adminSecurityManage.getUser(); + contractLaunchMapper.deleteContract(id, currentUser.getUsername()); + insertLog(id, ContractLaunchConstants.ACTION_DELETE, "删除合同", ContractLaunchConstants.ACTION_STATUS_SUCCESS, "合同已逻辑删除", null, currentUser); + } + + /** + * 批量填充子表数据(合同方/文件/履行计划/日志)。 + * + * 一次性查完所有子表再按 contractId 分组赋值,避免 N+1 查询。 + * includeLogs: 列表页不查日志(日志数据量大),详情页才查。 + */ + private void fillChildren(List list, boolean includeLogs) { + if (list == null || list.isEmpty()) { + return; + } + List ids = list.stream().map(ContractLaunchDto::getId).filter(Objects::nonNull).collect(Collectors.toList()); + if (ids.isEmpty()) { + return; + } + Map> partyMap = contractLaunchMapper.selectPartyByContractIds(ids).stream() + .collect(Collectors.groupingBy(ContractLaunchPartyDto::getContractId)); + Map> fileMap = contractLaunchMapper.selectFileByContractIds(ids).stream() + .collect(Collectors.groupingBy(ContractLaunchFileDto::getContractId)); + Map> planMap = contractLaunchMapper.selectPlanByContractIds(ids).stream() + .collect(Collectors.groupingBy(ContractLaunchPlanDto::getContractId)); + Map> logMap = includeLogs + ? contractLaunchMapper.selectLogByContractIds(ids).stream().collect(Collectors.groupingBy(ContractLaunchLogDto::getContractId)) + : Collections.emptyMap(); + + list.forEach(item -> { + item.setPartyList(new ArrayList<>(partyMap.getOrDefault(item.getId(), Collections.emptyList()))); + item.setFileList(new ArrayList<>(fileMap.getOrDefault(item.getId(), Collections.emptyList()))); + item.setPerformancePlans(new ArrayList<>(planMap.getOrDefault(item.getId(), Collections.emptyList()))); + item.setLogList(new ArrayList<>(logMap.getOrDefault(item.getId(), Collections.emptyList()))); + item.setCounterpartySummary( + item.getPartyList().stream() + .filter(party -> !"A".equalsIgnoreCase(party.getRoleCode())) + .map(ContractLaunchPartyDto::getCompanyName) + .filter(name -> name != null && !name.isBlank()) + .collect(Collectors.joining(";")) + ); + }); + } + + /** 保存子表数据(合同方/文件/履行计划,插入前生成主键 ID) */ + private void saveChildren(ContractLaunchDto contract, String operator) { + if (contract.getPartyList() != null) { + for (int i = 0; i < contract.getPartyList().size(); i++) { + ContractLaunchPartyDto party = contract.getPartyList().get(i); + party.setId(contractLaunchMapper.selectPartySeqNextVal()); + party.setContractId(contract.getId()); + party.setSortOrder(i + 1); + } + if (!contract.getPartyList().isEmpty()) { + contractLaunchMapper.batchInsertParty(contract.getPartyList(), operator, operator); + } + } + if (contract.getFileList() != null) { + for (int i = 0; i < contract.getFileList().size(); i++) { + ContractLaunchFileDto file = contract.getFileList().get(i); + file.setId(contractLaunchMapper.selectFileSeqNextVal()); + file.setContractId(contract.getId()); + file.setSortOrder(i + 1); + } + if (!contract.getFileList().isEmpty()) { + contractLaunchMapper.batchInsertFile(contract.getFileList(), operator, operator); + } + } + if (contract.getPerformancePlans() != null) { + for (ContractLaunchPlanDto plan : contract.getPerformancePlans()) { + plan.setId(contractLaunchMapper.selectPlanSeqNextVal()); + plan.setContractId(contract.getId()); + if (plan.getPlanStatus() == null || plan.getPlanStatus().isBlank()) { + plan.setPlanStatus("0"); + } + } + if (!contract.getPerformancePlans().isEmpty()) { + contractLaunchMapper.batchInsertPlan(contract.getPerformancePlans(), operator, operator); + } + } + } + + /** 替换子表数据(先删旧数据,再保存新数据) */ + private void replaceChildren(ContractLaunchDto contract, String operator) { + contractLaunchMapper.deletePartyByContractId(contract.getId(), operator); + contractLaunchMapper.deleteFileByContractId(contract.getId(), operator); + contractLaunchMapper.deletePlanByContractId(contract.getId(), operator); + saveChildren(contract, operator); + } + + /** 保存签署文件到 file 子表(先删旧签署文件,再插入新的) */ + private void saveSignedFile(Long contractId, ContractLaunchSealDto sealDto, String operator) { + if (sealDto.getSignedFilePath() == null || sealDto.getSignedFilePath().isBlank()) { + return; + } + contractLaunchMapper.deleteFileByContractIdAndCategory(contractId, ContractLaunchConstants.FILE_CATEGORY_SIGNED, operator); + ContractLaunchFileDto fileDto = new ContractLaunchFileDto(); + fileDto.setId(contractLaunchMapper.selectFileSeqNextVal()); + fileDto.setContractId(contractId); + fileDto.setFileCategory(ContractLaunchConstants.FILE_CATEGORY_SIGNED); + fileDto.setFileName(sealDto.getSignedFileName() == null || sealDto.getSignedFileName().isBlank() + ? "已签署合同文件" + : sealDto.getSignedFileName()); + fileDto.setFilePath(sealDto.getSignedFilePath()); + fileDto.setFileType(sealDto.getSignedFileType()); + fileDto.setFileSize(sealDto.getSignedFileSize()); + fileDto.setSortOrder(1); + contractLaunchMapper.batchInsertFile(Collections.singletonList(fileDto), operator, operator); + } + + /** 写入操作日志 */ + private void insertLog(Long contractId, String actionType, String actionName, String actionStatus, String actionContent, Object payload, SysUserEntity currentUser) { + ContractLaunchLogDto logDto = new ContractLaunchLogDto(); + logDto.setId(contractLaunchMapper.selectLogSeqNextVal()); + logDto.setContractId(contractId); + logDto.setActionType(actionType); + logDto.setActionName(actionName); + logDto.setActionStatus(actionStatus); + logDto.setOperatorName(currentUser.getUsername()); + logDto.setOperatorPhone(currentUser.getMobilePhone()); + logDto.setActionTime(LocalDateTime.now()); + logDto.setActionContent(actionContent); + logDto.setCallbackPayload(payload == null ? null : JSON.toJSONString(payload)); + contractLaunchMapper.insertLog(logDto); + } + + /** 数据清洗:去空格、补默认值 */ + private void normalizeContract(ContractLaunchDto contract) { + if (contract == null) { + throw new ServiceException("合同数据不能为空"); + } + contract.setContractName(trim(contract.getContractName())); + contract.setContractCode(trim(contract.getContractCode())); + contract.setContractType(trim(contract.getContractType())); + contract.setContractTypeCode(trim(contract.getContractTypeCode())); + contract.setPaymentMethod(trim(contract.getPaymentMethod())); + contract.setPrimaryContent(trim(contract.getPrimaryContent())); + contract.setAmountExplain(trim(contract.getAmountExplain())); + contract.setAssistDepartmentName(trim(contract.getAssistDepartmentName())); + contract.setExecutorAccount(trim(contract.getExecutorAccount())); + contract.setPeriodExplain(trim(contract.getPeriodExplain())); + contract.setContractGistRemark(trim(contract.getContractGistRemark())); + contract.setBodySummary(trim(contract.getBodySummary())); + contract.setSigningSubjectCode(trim(contract.getSigningSubjectCode())); + contract.setDateType(trim(contract.getDateType())); + contract.setPushUrl(trim(contract.getPushUrl())); + contract.setEwAmountType(trim(contract.getEwAmountType())); + if (contract.getDateType() == null || contract.getDateType().isBlank()) { + contract.setDateType("0"); + } + if (contract.getTextSource() == null || contract.getTextSource().isBlank()) { + contract.setTextSource("standardText"); + } + if (contract.getIsElectron() == null || contract.getIsElectron().isBlank()) { + contract.setIsElectron("1"); + } + if (contract.getPaymentDirection() == null || contract.getPaymentDirection().isBlank()) { + contract.setPaymentDirection("0"); + } + if (contract.getPushUrl() == null || contract.getPushUrl().isBlank()) { + contract.setPushUrl("/contract/launch/callback/law/approve"); + } + if (contract.getPartyList() == null) { + contract.setPartyList(new ArrayList<>()); + } + if (contract.getFileList() == null) { + contract.setFileList(new ArrayList<>()); + } + if (contract.getPerformancePlans() == null) { + contract.setPerformancePlans(new ArrayList<>()); + } + } + + /** 校验草稿必填项 */ + private void validateDraft(ContractLaunchDto contract) { + if (contract.getContractName() == null || contract.getContractName().isBlank()) { + throw new ServiceException("合同名称不能为空"); + } + if (contract.getContractTypeCode() == null || contract.getContractTypeCode().isBlank()) { + throw new ServiceException("合同类型编码不能为空"); + } + if (contract.getContractType() == null || contract.getContractType().isBlank()) { + throw new ServiceException("合同类型不能为空"); + } + 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.getContractPeriodType() == null || contract.getContractPeriodType().isBlank()) { + throw new ServiceException("合同期限类型不能为空"); + } + if ("0".equals(contract.getContractPeriodType()) + && (contract.getEffectiveStart() == null || contract.getEffectiveEnd() == null)) { + throw new ServiceException("固定期限合同必须填写开始和结束时间"); + } + if ("1".equals(contract.getContractPeriodType()) + && (contract.getPeriodExplain() == null || contract.getPeriodExplain().isBlank())) { + throw new ServiceException("无固定期限合同必须填写期限说明"); + } + if (contract.getPartyList() == null || contract.getPartyList().isEmpty()) { + throw new ServiceException("至少需要维护一个合同方"); + } + boolean hasPartyA = contract.getPartyList().stream().anyMatch(item -> "A".equalsIgnoreCase(item.getRoleCode())); + boolean hasCounterparty = contract.getPartyList().stream().anyMatch(item -> !"A".equalsIgnoreCase(item.getRoleCode())); + if (!hasPartyA) { + throw new ServiceException("必须维护我方信息"); + } + if (!hasCounterparty) { + throw new ServiceException("至少维护一个相对方"); + } + } + + private boolean canEdit(String status) { + return ContractLaunchConstants.STATUS_DRAFT.equals(status) || ContractLaunchConstants.STATUS_REJECTED.equals(status); + } + + private boolean canSubmit(String status) { + return ContractLaunchConstants.STATUS_DRAFT.equals(status) || ContractLaunchConstants.STATUS_REJECTED.equals(status); + } + + private boolean canDelete(String status) { + return ContractLaunchConstants.STATUS_DRAFT.equals(status) || ContractLaunchConstants.STATUS_REJECTED.equals(status); + } + + /** 通过法务回调报文查找对应合同(先取 contractSysId 再查库) */ + private ContractLaunchDto getContractByLawCallback(Map payload) { + if (payload == null || payload.isEmpty()) { + throw new ServiceException("法务回调报文不能为空"); + } + String contractSysId = getFirstText(payload, "contractSysId", "businessNo", "thirdContractId"); + if (contractSysId == null || contractSysId.isBlank()) { + throw new ServiceException("法务回调缺少 contractSysId,无法定位合同"); + } + ContractLaunchDto contract = contractLaunchMapper.selectContractByContractSysId(contractSysId); + if (contract == null) { + throw new ServiceException("未找到法务回调对应的合同:" + contractSysId); + } + fillChildren(Collections.singletonList(contract), true); + return contract; + } + + /** 判断法务回调是否审批通过(兼容多种字段名和值) */ + private boolean isLawCallbackApproved(Map payload) { + String status = getFirstText(payload, "approveStatus", "status", "result", "auditStatus"); + if (status == null || status.isBlank()) { + return true; + } + String normalized = status.trim().toLowerCase(); + return normalized.equals("1") + || normalized.equals("true") + || normalized.equals("success") + || normalized.equals("approved") + || normalized.equals("pass") + || normalized.equals("agree") + || normalized.equals("通过") + || normalized.equals("审批通过"); + } + + /** 从回调报文中提取指定 key 的文本值(多个 key 依次尝试,取第一个非空) */ + private String getFirstText(Map payload, String... keys) { + if (payload == null) { + return null; + } + for (String key : keys) { + Object value = payload.get(key); + if (value == null) { + continue; + } + String text = String.valueOf(value).trim(); + if (!text.isBlank()) { + return text; + } + } + return null; + } + + /** 构建结算平台推送合同报文(pushContract 接口的 payload) */ + private Map buildSettlementPushPayload(ContractLaunchDto contract) { + Map payload = new LinkedHashMap<>(); + payload.put("contractSource", contract.getContractSource()); + payload.put("isAddit", contract.getIsAddit() == null ? 2 : contract.getIsAddit()); + payload.put("operate", "create"); + 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("creatorPhone", contract.getCreatorPhone()); + payload.put("dateType", contract.getDateType()); + payload.put("currencyName", contract.getCurrencyName()); + payload.put("contractGistRemark", contract.getContractGistRemark()); + payload.put("contractSysId", contract.getContractSysId()); + payload.put("selfCode", contract.getSelfCode()); + payload.put("contractName", contract.getContractName()); + payload.put("startTime", contract.getEffectiveStart()); + payload.put("endTime", contract.getEffectiveEnd()); + payload.put("amountExplain", contract.getAmountExplain()); + payload.put("isText", contract.getTextSource()); + payload.put("primaryContent", contract.getPrimaryContent()); + payload.put("isElectron", contract.getIsElectron()); + payload.put("paymentDirection", contract.getPaymentDirection()); + payload.put("contractTypeCode", contract.getContractTypeCode()); + payload.put("bodySummary", contract.getBodySummary()); + payload.put("signingSubjectCode", + contract.getSigningSubjectCode() == null || contract.getSigningSubjectCode().isBlank() + ? contract.getOrgId() + : contract.getSigningSubjectCode()); + payload.put("executorAccount", contract.getExecutorAccount()); + 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("pushUrl", contract.getPushUrl()); + payload.put("isYearBudget", contract.getIsYearBudget() == null ? 0 : contract.getIsYearBudget()); + payload.put("isShare", contract.getIsShare()); + payload.put("assistDepartmentName", contract.getAssistDepartmentName()); + payload.put("contractNature", contract.getContractNature()); + payload.put("contractCode", contract.getContractCode()); + payload.put("ewAmountType", contract.getEwAmountType()); + payload.put("djAmount", contract.getDjAmount()); + payload.put("yjAmount", contract.getYjAmount()); + payload.put("yfkAmount", contract.getYfkAmount()); + payload.put("bzjAmount", contract.getBzjAmount()); + payload.put("contractOpposites", contract.getPartyList().stream() + .filter(item -> !"A".equalsIgnoreCase(item.getRoleCode())) + .map(item -> { + Map map = new LinkedHashMap<>(); + map.put("oppCharacter", "2".equals(item.getOppositeCharacter()) ? "ZZR" : "QY"); + map.put("credirCode", item.getCreditCode()); + map.put("idCard", item.getNaturalPersonIdNumber()); + map.put("oppositeName", item.getCompanyName()); + return map; + }).collect(Collectors.toList())); + payload.put("contractKxPlans", contract.getPerformancePlans().stream().map(plan -> { + Map map = new LinkedHashMap<>(); + map.put("planWhere", plan.getPlanWhere()); + map.put("planMoney", plan.getPlanMoney()); + map.put("planDetail", plan.getPlanDetail()); + map.put("planTime", plan.getPlanTime()); + map.put("planMatter", plan.getPlanMatter()); + map.put("planDay", plan.getPlanDay()); + map.put("planSubjectCode", plan.getPlanSubjectCode()); + 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)); + return payload; + } + + /** 构建数据采集合同报文(collectContract 接口的 payload) */ + private Map buildDataCollectionContractPayload(ContractLaunchDto contract) { + Map payload = new LinkedHashMap<>(); + payload.put("c_name", contract.getContractName()); + payload.put("c_code", contract.getLawContractCode() == null || contract.getLawContractCode().isBlank() ? contract.getContractCode() : contract.getLawContractCode()); + payload.put("c_weNames", contract.getCompanyName()); + payload.put("c_oppositeNames", contract.getCounterpartySummary()); + payload.put("c_textSource", contract.getTextSource()); + payload.put("c_mainContent", contract.getPrimaryContent()); + payload.put("c_direction", contract.getPaymentDirection()); + payload.put("c_method", contract.getValuationMode()); + payload.put("c_subjectAMT", contract.getContractAmount()); + payload.put("c_currency", contract.getCurrencyName()); + payload.put("c_amountExplain", contract.getAmountExplain()); + payload.put("c_type", contract.getContractTypeCode()); + payload.put("c_termType", contract.getContractPeriodType()); + payload.put("c_termExplain", contract.getPeriodExplain()); + payload.put("c_termStart", contract.getEffectiveStart()); + payload.put("c_termEnd", contract.getEffectiveEnd()); + payload.put("c_qyztGAT", contract.getIsSgat()); + payload.put("c_executor", contract.getExecutorAccount()); + payload.put("c_openBidding", contract.getIsOpenBidding()); + payload.put("c_currentBudget", contract.getIsYearBudget()); + payload.put("c_major", contract.getIsMajorContract()); + payload.put("c_signMethod", contract.getSealType()); + 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_lxPlan", contract.getPerformancePlans()); + Map seal = new LinkedHashMap<>(); + seal.put("seal_isSeal", "1"); + seal.put("seal_approveSame", "1"); + seal.put("seal_type", contract.getSealType()); + seal.put("seal_date", contract.getSignDate()); + payload.put("c_seal", seal); + Map sign = new LinkedHashMap<>(); + 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_executor", contract.getExecutorAccount()); + payload.put("c_sign", sign); + return payload; + } + + /** 构建归档报文(pushArchive 接口的 payload) */ + private Map buildArchivePayload(ContractLaunchDto contract, ContractLaunchArchiveDto archiveDto) { + Map payload = new LinkedHashMap<>(); + payload.put("contractCode", contract.getLawContractCode()); + payload.put("contractSysId", contract.getContractSysId()); + payload.put("Keeper", archiveDto == null ? null : archiveDto.getArchiveKeeper()); + 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()); + return payload; + } + + /** 按 fileCategory 提取文件列表,转为法务接口要求的附件格式 */ + private List> buildFilePayloadList(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("fileSize", item.getFileSize()); + map.put("fileType", item.getFileType()); + return map; + }).collect(Collectors.toList()); + } + + private String trim(String value) { + return value == null ? null : value.trim(); + } +} diff --git a/src/main/java/com/nbport/zgwl/contractlaunch/utils/ContractLaunchCodeUtils.java b/src/main/java/com/nbport/zgwl/contractlaunch/utils/ContractLaunchCodeUtils.java new file mode 100644 index 0000000..c8358a5 --- /dev/null +++ b/src/main/java/com/nbport/zgwl/contractlaunch/utils/ContractLaunchCodeUtils.java @@ -0,0 +1,42 @@ +package com.nbport.zgwl.contractlaunch.utils; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.concurrent.ThreadLocalRandom; + +/** + * 合同编号工具 + * + * contractSysId:本系统合同唯一标识,格式 CLS-yyyyMMddHHmmss-随机4位 + * contractCode:业务合同编号,格式 合同类型编码-yyyyMMdd-随机4位(如 MM-20260522-1234) + */ +public final class ContractLaunchCodeUtils { + + private ContractLaunchCodeUtils() { + } + + /** 生成合同系统唯一编码(CLS-时间戳-随机4位) */ + public static String buildContractSysId() { + return "CLS-" + formatNow("yyyyMMddHHmmss") + "-" + randomDigits(4); + } + + /** 生成业务合同编号(合同类型编码-日期-随机4位,如 MM-20260522-1234) */ + public static String buildContractCode(String contractTypeCode) { + String typeCode = contractTypeCode == null || contractTypeCode.isBlank() + ? "QT" + : contractTypeCode; + return typeCode + "-" + formatNow("yyyyMMdd") + "-" + randomDigits(4); + } + + private static String formatNow(String pattern) { + return new SimpleDateFormat(pattern).format(new Date()); + } + + private static String randomDigits(int length) { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < length; i++) { + builder.append(ThreadLocalRandom.current().nextInt(10)); + } + return builder.toString(); + } +} diff --git a/src/main/java/com/nbport/zgwl/contractlaunch/utils/ContractLaunchConstants.java b/src/main/java/com/nbport/zgwl/contractlaunch/utils/ContractLaunchConstants.java new file mode 100644 index 0000000..fd60d2c --- /dev/null +++ b/src/main/java/com/nbport/zgwl/contractlaunch/utils/ContractLaunchConstants.java @@ -0,0 +1,51 @@ +package com.nbport.zgwl.contractlaunch.utils; + +/** + * 合同发起模块常量 + * + * 这里把状态值集中放一起,方便你后续改状态文案或补真实流程。 + */ +public final class ContractLaunchConstants { + + private ContractLaunchConstants() { + } + + public static final String REQUEST_PREFIX = "/contract/launch"; + + public static final String STATUS_DRAFT = "draft"; + public static final String STATUS_REVIEWING = "reviewing"; + public static final String STATUS_REJECTED = "rejected"; + public static final String STATUS_PENDING_SIGN = "pending_sign"; + public static final String STATUS_SIGNING = "signing"; + public static final String STATUS_PENDING_COLLECT = "pending_collect"; + public static final String STATUS_PENDING_ARCHIVE = "pending_archive"; + public static final String STATUS_COMPLETED = "completed"; + public static final String STATUS_TERMINATED = "terminated"; + + public static final String APPROVE_STATUS_PENDING = "pending"; + public static final String APPROVE_STATUS_APPROVED = "approved"; + public static final String APPROVE_STATUS_REJECTED = "rejected"; + + public static final String FILE_CATEGORY_BODY = "body"; + public static final String FILE_CATEGORY_OTHER = "other"; + public static final String FILE_CATEGORY_GIST = "gist"; + public static final String FILE_CATEGORY_SIGNED = "signed"; + public static final String FILE_CATEGORY_ARCHIVE = "archive"; + + public static final String ACTION_SAVE = "save"; + public static final String ACTION_SUBMIT = "submit"; + public static final String ACTION_APPROVE = "approve"; + public static final String ACTION_REJECT = "reject"; + public static final String ACTION_SEAL_START = "seal_start"; + public static final String ACTION_SEAL_FINISH = "seal_finish"; + public static final String ACTION_COLLECT = "collect"; + public static final String ACTION_ARCHIVE = "archive"; + public static final String ACTION_DELETE = "delete"; + + public static final String ACTION_STATUS_SUCCESS = "success"; + public static final String ACTION_STATUS_FAILED = "failed"; + public static final String ACTION_STATUS_PENDING = "pending"; + + public static final String IS_DELETED_NO = "N"; + public static final String IS_DELETED_YES = "Y"; +} diff --git a/src/main/java/com/nbport/zgwl/entity/SysOrgEntity.java b/src/main/java/com/nbport/zgwl/entity/SysOrgEntity.java new file mode 100644 index 0000000..404c71c --- /dev/null +++ b/src/main/java/com/nbport/zgwl/entity/SysOrgEntity.java @@ -0,0 +1,25 @@ +package com.nbport.zgwl.entity; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 组织信息 + * + * 当前项目里先作为一个简单对象使用, + * 后续接入真实权限系统时,可以直接替换成真实组织实体。 + */ +@Data +public class SysOrgEntity implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** 组织ID */ + private String id; + + /** 组织名称 */ + private String orgName; +} diff --git a/src/main/java/com/nbport/zgwl/entity/SysUserEntity.java b/src/main/java/com/nbport/zgwl/entity/SysUserEntity.java index 6577d67..6e11b6e 100644 --- a/src/main/java/com/nbport/zgwl/entity/SysUserEntity.java +++ b/src/main/java/com/nbport/zgwl/entity/SysUserEntity.java @@ -28,4 +28,12 @@ public class SysUserEntity extends BaseEntity { @TableField("email") private String email; + + /** + * 当前登录人的所属组织 + * + * 这里不映射数据库字段,只用于业务代码里取组织信息。 + */ + @TableField(exist = false) + private SysOrgEntity sysOrgEntity; } diff --git a/src/main/java/com/nbport/zgwl/manifest/utils/ManifestCodeUtils.java b/src/main/java/com/nbport/zgwl/manifest/utils/ManifestCodeUtils.java index 754ccb6..aee943f 100644 --- a/src/main/java/com/nbport/zgwl/manifest/utils/ManifestCodeUtils.java +++ b/src/main/java/com/nbport/zgwl/manifest/utils/ManifestCodeUtils.java @@ -16,6 +16,38 @@ import java.util.concurrent.ThreadLocalRandom; public final class ManifestCodeUtils { private static final Map CONTRACT_TYPE_CODE_MAP = Map.ofEntries( + Map.entry("MM", "MM"), + Map.entry("GY", "GY"), + Map.entry("ZY", "ZY"), + Map.entry("JK", "JK"), + Map.entry("BZ", "BZ"), + Map.entry("ZL", "ZL"), + Map.entry("RZ", "RZ"), + Map.entry("BL", "BL"), + Map.entry("CL", "CL"), + Map.entry("GC", "GC"), + Map.entry("YS", "YS"), + Map.entry("JS", "JS"), + Map.entry("BG", "BG"), + Map.entry("CC", "CC"), + Map.entry("WT", "WT"), + Map.entry("WY", "WY"), + Map.entry("HJ", "HJ"), + Map.entry("ZJ", "ZJ"), + Map.entry("HH", "HH"), + Map.entry("YG", "YG"), + Map.entry("YW", "YW"), + Map.entry("QT", "QT"), + Map.entry("YWBFP", "YWBFP"), + Map.entry("YWBFS", "YWBFS"), + Map.entry("YWIOP", "YWIOP"), + Map.entry("YWIOS", "YWIOS"), + Map.entry("YWDOP", "YWDOP"), + Map.entry("YWDOS", "YWDOS"), + Map.entry("YWBCP", "YWBCP"), + Map.entry("YWBCS", "YWBCS"), + Map.entry("YWOTP", "YWOTP"), + Map.entry("YWOTS", "YWOTS"), Map.entry("买卖合同", "MM"), Map.entry("采购合同", "CG"), Map.entry("销售合同", "XS"), diff --git a/src/main/java/com/nbport/zgwl/utils/AdminSecurityManage.java b/src/main/java/com/nbport/zgwl/utils/AdminSecurityManage.java new file mode 100644 index 0000000..fab800f --- /dev/null +++ b/src/main/java/com/nbport/zgwl/utils/AdminSecurityManage.java @@ -0,0 +1,30 @@ +package com.nbport.zgwl.utils; + +import com.nbport.zgwl.entity.SysOrgEntity; +import com.nbport.zgwl.entity.SysUserEntity; +import org.springframework.stereotype.Component; + +/** + * 登录用户获取组件 + * + * 当前系统还没有真正接入统一认证, + * 所以这里先返回一个 mock 用户,便于业务代码先跑通。 + * 后续接入真实系统时,直接把这里替换成真正的登录态获取逻辑即可。 + */ +@Component +public class AdminSecurityManage { + + public SysUserEntity getUser() { + SysOrgEntity orgEntity = new SysOrgEntity(); + orgEntity.setId("ORG001"); + orgEntity.setOrgName("浙港物流平台有限公司"); + + SysUserEntity user = new SysUserEntity(); + user.setId(1L); + user.setUsername("admin"); + user.setMobilePhone("13800000000"); + user.setEmail("admin@test.com"); + user.setSysOrgEntity(orgEntity); + return user; + } +} diff --git a/src/main/resources/mapper/contractlaunch/ContractLaunchMapper.xml b/src/main/resources/mapper/contractlaunch/ContractLaunchMapper.xml new file mode 100644 index 0000000..b2cd5c0 --- /dev/null +++ b/src/main/resources/mapper/contractlaunch/ContractLaunchMapper.xml @@ -0,0 +1,532 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select ID, CONTRACT_SYS_ID, CONTRACT_CODE, LAW_CONTRACT_CODE, LAW_CONTRACT_ID, + CONTRACT_NAME, CONTRACT_TYPE, CONTRACT_TYPE_CODE, CONTRACT_SOURCE, CREATION_MODE, + TEMPLATE_ID, TEMPLATE_CODE, TEMPLATE_NAME, STATUS, APPROVE_STATUS, APPROVE_MESSAGE, + SEAL_CONTRACT_ID, SEAL_CONTRACT_STATUS, CONTRACT_AMOUNT, CURRENCY_NAME, + CONTRACT_PERIOD_TYPE, PERIOD_EXPLAIN, EFFECTIVE_START, EFFECTIVE_END, + PAYMENT_METHOD, PRIMARY_CONTENT, AMOUNT_EXPLAIN, VALUATION_MODE, SEAL_TYPE, + TEXT_SOURCE, MANDATE_TYPE, CARGO_TYPE, ASSIST_DEPARTMENT_NAME, EXECUTOR_ACCOUNT, + CREATOR_PHONE, SELF_CODE, MAIN_CONTRACT_CODE, SIGNING_SUBJECT_CODE, DATE_TYPE, IS_ADDIT, IS_ELECTRON, + PAYMENT_DIRECTION, PUSH_URL, IS_MAJOR_CONTRACT, IS_OPEN_BIDDING, IS_YEAR_BUDGET, IS_SHARE, + CONTRACT_NATURE, IS_SGAT, IS_NORMAL, IS_TERMINATED, IS_SINGLE_AGREEMENT, + BIDDING_NO, OPEN_BIDDING_REMARK, CONTRACT_GIST_REMARK, + EW_AMOUNT_TYPE, DJ_AMOUNT, YJ_AMOUNT, YFK_AMOUNT, BZJ_AMOUNT, BODY_SUMMARY, + CONTRACT_TEXT_FILE_NAME, CONTRACT_TEXT_FILE_PATH, CONTRACT_TEXT_FILE_TYPE, CONTRACT_TEXT_FILE_SIZE, + SIGNED_FILE_NAME, SIGNED_FILE_PATH, SIGNED_FILE_TYPE, SIGNED_FILE_SIZE, SIGNED_OFFLINE_REASON, + SIGN_DATE, ACTIVE_DATE, ARCHIVE_DATE, ARCHIVE_KEEPER, ARCHIVE_KEEPER_UNIT, + ARCHIVE_KEEPER_UNIT_CODE, INVOICE_DATE, RECEIVE_DATE, REMIND_DAYS, + INITIATOR, INITIATOR_DEPT, COMPANY_NAME, ORG_ID, QUERY_SCOPE, + CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, IS_DELETED, REMARK + from SEAL_CONTRACT_LAUNCH_MAIN + + + + + + + + + + + + + + + + insert into SEAL_CONTRACT_LAUNCH_MAIN ( + ID, CONTRACT_SYS_ID, CONTRACT_CODE, LAW_CONTRACT_CODE, LAW_CONTRACT_ID, + CONTRACT_NAME, CONTRACT_TYPE, CONTRACT_TYPE_CODE, CONTRACT_SOURCE, CREATION_MODE, + TEMPLATE_ID, TEMPLATE_CODE, TEMPLATE_NAME, STATUS, APPROVE_STATUS, APPROVE_MESSAGE, + SEAL_CONTRACT_ID, SEAL_CONTRACT_STATUS, CONTRACT_AMOUNT, CURRENCY_NAME, + CONTRACT_PERIOD_TYPE, PERIOD_EXPLAIN, EFFECTIVE_START, EFFECTIVE_END, + PAYMENT_METHOD, PRIMARY_CONTENT, AMOUNT_EXPLAIN, VALUATION_MODE, SEAL_TYPE, + TEXT_SOURCE, MANDATE_TYPE, CARGO_TYPE, ASSIST_DEPARTMENT_NAME, EXECUTOR_ACCOUNT, + CREATOR_PHONE, SELF_CODE, MAIN_CONTRACT_CODE, SIGNING_SUBJECT_CODE, DATE_TYPE, IS_ADDIT, IS_ELECTRON, + PAYMENT_DIRECTION, PUSH_URL, IS_MAJOR_CONTRACT, IS_OPEN_BIDDING, IS_YEAR_BUDGET, IS_SHARE, + CONTRACT_NATURE, IS_SGAT, IS_NORMAL, IS_TERMINATED, IS_SINGLE_AGREEMENT, + BIDDING_NO, OPEN_BIDDING_REMARK, CONTRACT_GIST_REMARK, + EW_AMOUNT_TYPE, DJ_AMOUNT, YJ_AMOUNT, YFK_AMOUNT, BZJ_AMOUNT, BODY_SUMMARY, + CONTRACT_TEXT_FILE_NAME, CONTRACT_TEXT_FILE_PATH, CONTRACT_TEXT_FILE_TYPE, CONTRACT_TEXT_FILE_SIZE, + SIGNED_FILE_NAME, SIGNED_FILE_PATH, SIGNED_FILE_TYPE, SIGNED_FILE_SIZE, SIGNED_OFFLINE_REASON, + SIGN_DATE, ACTIVE_DATE, ARCHIVE_DATE, ARCHIVE_KEEPER, ARCHIVE_KEEPER_UNIT, + ARCHIVE_KEEPER_UNIT_CODE, INVOICE_DATE, RECEIVE_DATE, REMIND_DAYS, + INITIATOR, INITIATOR_DEPT, COMPANY_NAME, ORG_ID, QUERY_SCOPE, + CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, IS_DELETED, REMARK + ) values ( + #{id}, #{contractSysId}, #{contractCode}, #{lawContractCode}, #{lawContractId}, + #{contractName}, #{contractType}, #{contractTypeCode}, #{contractSource}, #{creationMode}, + #{templateId}, #{templateCode}, #{templateName}, #{status}, #{approveStatus}, #{approveMessage}, + #{sealContractId}, #{sealContractStatus}, #{contractAmount}, #{currencyName}, + #{contractPeriodType}, #{periodExplain}, #{effectiveStart}, #{effectiveEnd}, + #{paymentMethod}, #{primaryContent}, #{amountExplain}, #{valuationMode}, #{sealType}, + #{textSource}, #{mandateType}, #{cargoType}, #{assistDepartmentName}, #{executorAccount}, + #{creatorPhone}, #{selfCode}, #{mainContractCode}, #{signingSubjectCode}, #{dateType}, #{isAddit}, #{isElectron}, + #{paymentDirection}, #{pushUrl}, #{isMajorContract}, #{isOpenBidding}, #{isYearBudget}, #{isShare}, + #{contractNature}, #{isSgat}, #{isNormal}, #{isTerminated}, #{isSingleAgreement}, + #{biddingNo}, #{openBiddingRemark}, #{contractGistRemark}, + #{ewAmountType}, #{djAmount}, #{yjAmount}, #{yfkAmount}, #{bzjAmount}, #{bodySummary}, + #{contractTextFileName}, #{contractTextFilePath}, #{contractTextFileType}, #{contractTextFileSize}, + #{signedFileName}, #{signedFilePath}, #{signedFileType}, #{signedFileSize}, #{signedOfflineReason}, + #{signDate}, #{activeDate}, #{archiveDate}, #{archiveKeeper}, #{archiveKeeperUnit}, + #{archiveKeeperUnitCode}, #{invoiceDate}, #{receiveDate}, #{remindDays}, + #{initiator}, #{initiatorDept}, #{companyName}, #{orgId}, #{queryScope}, + #{createBy}, #{createTime}, #{updateBy}, #{updateTime}, #{isDeleted}, #{remark} + ) + + + + update SEAL_CONTRACT_LAUNCH_MAIN + + LAW_CONTRACT_CODE = #{lawContractCode}, + LAW_CONTRACT_ID = #{lawContractId}, + CONTRACT_NAME = #{contractName}, + CONTRACT_TYPE = #{contractType}, + CONTRACT_TYPE_CODE = #{contractTypeCode}, + CONTRACT_SOURCE = #{contractSource}, + CREATION_MODE = #{creationMode}, + TEMPLATE_ID = #{templateId}, + TEMPLATE_CODE = #{templateCode}, + TEMPLATE_NAME = #{templateName}, + STATUS = #{status}, + APPROVE_STATUS = #{approveStatus}, + APPROVE_MESSAGE = #{approveMessage}, + SEAL_CONTRACT_ID = #{sealContractId}, + SEAL_CONTRACT_STATUS = #{sealContractStatus}, + CONTRACT_AMOUNT = #{contractAmount}, + CURRENCY_NAME = #{currencyName}, + CONTRACT_PERIOD_TYPE = #{contractPeriodType}, + PERIOD_EXPLAIN = #{periodExplain}, + EFFECTIVE_START = #{effectiveStart}, + EFFECTIVE_END = #{effectiveEnd}, + PAYMENT_METHOD = #{paymentMethod}, + PRIMARY_CONTENT = #{primaryContent}, + AMOUNT_EXPLAIN = #{amountExplain}, + VALUATION_MODE = #{valuationMode}, + SEAL_TYPE = #{sealType}, + TEXT_SOURCE = #{textSource}, + MANDATE_TYPE = #{mandateType}, + CARGO_TYPE = #{cargoType}, + ASSIST_DEPARTMENT_NAME = #{assistDepartmentName}, + EXECUTOR_ACCOUNT = #{executorAccount}, + CREATOR_PHONE = #{creatorPhone}, + SELF_CODE = #{selfCode}, + MAIN_CONTRACT_CODE = #{mainContractCode}, + SIGNING_SUBJECT_CODE = #{signingSubjectCode}, + DATE_TYPE = #{dateType}, + IS_ADDIT = #{isAddit}, + IS_ELECTRON = #{isElectron}, + PAYMENT_DIRECTION = #{paymentDirection}, + PUSH_URL = #{pushUrl}, + IS_MAJOR_CONTRACT = #{isMajorContract}, + IS_OPEN_BIDDING = #{isOpenBidding}, + IS_YEAR_BUDGET = #{isYearBudget}, + IS_SHARE = #{isShare}, + CONTRACT_NATURE = #{contractNature}, + IS_SGAT = #{isSgat}, + IS_NORMAL = #{isNormal}, + IS_TERMINATED = #{isTerminated}, + IS_SINGLE_AGREEMENT = #{isSingleAgreement}, + BIDDING_NO = #{biddingNo}, + OPEN_BIDDING_REMARK = #{openBiddingRemark}, + CONTRACT_GIST_REMARK = #{contractGistRemark}, + EW_AMOUNT_TYPE = #{ewAmountType}, + DJ_AMOUNT = #{djAmount}, + YJ_AMOUNT = #{yjAmount}, + YFK_AMOUNT = #{yfkAmount}, + BZJ_AMOUNT = #{bzjAmount}, + BODY_SUMMARY = #{bodySummary}, + CONTRACT_TEXT_FILE_NAME = #{contractTextFileName}, + CONTRACT_TEXT_FILE_PATH = #{contractTextFilePath}, + CONTRACT_TEXT_FILE_TYPE = #{contractTextFileType}, + CONTRACT_TEXT_FILE_SIZE = #{contractTextFileSize}, + SIGNED_FILE_NAME = #{signedFileName}, + SIGNED_FILE_PATH = #{signedFilePath}, + SIGNED_FILE_TYPE = #{signedFileType}, + SIGNED_FILE_SIZE = #{signedFileSize}, + SIGNED_OFFLINE_REASON = #{signedOfflineReason}, + SIGN_DATE = #{signDate}, + ACTIVE_DATE = #{activeDate}, + ARCHIVE_DATE = #{archiveDate}, + ARCHIVE_KEEPER = #{archiveKeeper}, + ARCHIVE_KEEPER_UNIT = #{archiveKeeperUnit}, + ARCHIVE_KEEPER_UNIT_CODE = #{archiveKeeperUnitCode}, + INVOICE_DATE = #{invoiceDate}, + RECEIVE_DATE = #{receiveDate}, + REMIND_DAYS = #{remindDays}, + INITIATOR = #{initiator}, + INITIATOR_DEPT = #{initiatorDept}, + COMPANY_NAME = #{companyName}, + ORG_ID = #{orgId}, + QUERY_SCOPE = #{queryScope}, + UPDATE_BY = #{updateBy}, + UPDATE_TIME = #{updateTime}, + IS_DELETED = #{isDeleted}, + REMARK = #{remark}, + + where ID = #{id} + and IS_DELETED = 'N' + + + + update SEAL_CONTRACT_LAUNCH_MAIN + set IS_DELETED = 'Y', + UPDATE_BY = #{updateBy}, + UPDATE_TIME = SYSDATE + where ID = #{id} + and IS_DELETED = 'N' + + + + INSERT ALL + + INTO SEAL_CONTRACT_LAUNCH_PARTY ( + ID, CONTRACT_ID, ROLE_CODE, ROLE_NAME, SORT_ORDER, PARTNER_ID, OPPOSITE_ID, + COMPANY_NAME, COMPANY_TYPE, OPPOSITE_CHARACTER, CONTACT_NAME, CONTACT_PHONE, + CONTACT_EMAIL, CONTACT_ADDRESS, REGISTERED_ADDRESS, LEGAL_REPRESENTATIVE, + CREDIT_CODE, BANK_NAME, BANK_ACCOUNT, NATURAL_PERSON_ID_TYPE, NATURAL_PERSON_ID_NUMBER, + IS_GROUP_INTERNAL, IS_HONG_KONG_MACAO_TAIWAN, CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, IS_DELETED, REMARK + ) VALUES ( + #{item.id}, #{item.contractId}, #{item.roleCode}, #{item.roleName}, #{item.sortOrder}, #{item.partnerId}, #{item.oppositeId}, + #{item.companyName}, #{item.companyType}, #{item.oppositeCharacter}, #{item.contactName}, #{item.contactPhone}, + #{item.contactEmail}, #{item.contactAddress}, #{item.registeredAddress}, #{item.legalRepresentative}, + #{item.creditCode}, #{item.bankName}, #{item.bankAccount}, #{item.naturalPersonIdType}, #{item.naturalPersonIdNumber}, + #{item.isGroupInternal}, #{item.isHongKongMacaoTaiwan}, #{createBy}, SYSDATE, #{updateBy}, SYSDATE, 'N', #{item.remark} + ) + + SELECT 1 FROM DUAL + + + + INSERT ALL + + INTO SEAL_CONTRACT_LAUNCH_FILE ( + ID, CONTRACT_ID, FILE_CATEGORY, FILE_NAME, FILE_PATH, 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.sortOrder}, #{createBy}, SYSDATE, #{updateBy}, SYSDATE, 'N', #{item.remark} + ) + + SELECT 1 FROM DUAL + + + + INSERT ALL + + INTO SEAL_CONTRACT_LAUNCH_PLAN ( + ID, CONTRACT_ID, PLAN_MATTER, PLAN_WHERE, PLAN_TIME, PLAN_DAY, PLAN_MONEY, + PLAN_SUBJECT_CODE, PLAN_SUBJECT_NAME, PLAN_DETAIL, PLAN_STATUS, + CREATE_BY, CREATE_TIME, UPDATE_BY, UPDATE_TIME, IS_DELETED, REMARK + ) VALUES ( + #{item.id}, #{item.contractId}, #{item.planMatter}, #{item.planWhere}, #{item.planTime}, #{item.planDay}, #{item.planMoney}, + #{item.planSubjectCode}, #{item.planSubjectName}, #{item.planDetail}, #{item.planStatus}, + #{createBy}, SYSDATE, #{updateBy}, SYSDATE, 'N', #{item.remark} + ) + + SELECT 1 FROM DUAL + + + + insert into SEAL_CONTRACT_LAUNCH_LOG ( + ID, CONTRACT_ID, ACTION_TYPE, ACTION_NAME, ACTION_STATUS, OPERATOR_NAME, OPERATOR_PHONE, + ACTION_TIME, ACTION_CONTENT, CALLBACK_PAYLOAD, REMARK + ) values ( + #{id}, #{contractId}, #{actionType}, #{actionName}, #{actionStatus}, #{operatorName}, #{operatorPhone}, + #{actionTime}, #{actionContent}, #{callbackPayload}, #{remark} + ) + + + + update SEAL_CONTRACT_LAUNCH_PARTY + set IS_DELETED = 'Y', + UPDATE_BY = #{updateBy}, + UPDATE_TIME = SYSDATE + where CONTRACT_ID = #{contractId} + and IS_DELETED = 'N' + + + + update SEAL_CONTRACT_LAUNCH_FILE + set IS_DELETED = 'Y', + UPDATE_BY = #{updateBy}, + UPDATE_TIME = SYSDATE + where CONTRACT_ID = #{contractId} + and IS_DELETED = 'N' + + + + update SEAL_CONTRACT_LAUNCH_FILE + set IS_DELETED = 'Y', + UPDATE_BY = #{updateBy}, + UPDATE_TIME = SYSDATE + where CONTRACT_ID = #{contractId} + and FILE_CATEGORY = #{fileCategory} + and IS_DELETED = 'N' + + + + update SEAL_CONTRACT_LAUNCH_PLAN + set IS_DELETED = 'Y', + UPDATE_BY = #{updateBy}, + UPDATE_TIME = SYSDATE + where CONTRACT_ID = #{contractId} + and IS_DELETED = 'N' + + + + + + + + + + + diff --git a/src/main/resources/mapper/manifest/ContractTemplateMapper.xml b/src/main/resources/mapper/manifest/ContractTemplateMapper.xml index e214ae7..db8246d 100644 --- a/src/main/resources/mapper/manifest/ContractTemplateMapper.xml +++ b/src/main/resources/mapper/manifest/ContractTemplateMapper.xml @@ -30,7 +30,7 @@ - select id, template_code, template_name, source, contract_type, self_party_role, + select id, template_code, template_name, source, contract_type, version, status, variable_count, description, usage_tip, template_file_name, template_file_path, publish_org_ids_json, publish_org_names_json, variable_mappings_json, @@ -73,7 +73,7 @@ insert into contract_template_manage ( - id, template_code, template_name, source, contract_type, self_party_role, + id, template_code, template_name, source, contract_type, version, status, variable_count, description, usage_tip, template_file_name, template_file_path, publish_org_ids_json, publish_org_names_json, variable_mappings_json, diff --git a/src/main/resources/mapper/partner/PartnerManageMapper.xml b/src/main/resources/mapper/partner/PartnerManageMapper.xml index 0807db2..55c7038 100644 --- a/src/main/resources/mapper/partner/PartnerManageMapper.xml +++ b/src/main/resources/mapper/partner/PartnerManageMapper.xml @@ -12,8 +12,14 @@ + + + + + + @@ -60,7 +66,8 @@ - select CUSTOMER_SYSID, CUSTOMER_NAME_C, CUSTOMER_TAXNO, CUSTOMER_REGADDR, + select CUSTOMER_SYSID, CUSTOMER_REGNO, CUSTOMER_NO, CUSTOMER_NAME_C, CUSTOMER_TAXNO, + CUSTOMER_ADDRESS, OFFICEPHONE, CUSTOMER_BANK, CUSTOMER_BANKACCOUNT, CUSTOMER_REGADDR, EMAIL_ADDRESS, OPPOSITE_ID, OPPOSITE_CHARACTER, GAT_FLAG, GROUP_FLAG, COUNTRY, COUNTRY_NAME, LEGAL_PERSON_NAME, CERTIFICATE_TYPE, CERTIFICATE_NUM, TIN_CODE, REGISTERED_CAPITAL, REG_CAPITAL, diff --git a/upload-files/20260529/2a478b9d74b3485ab696ef172cb5db3e.docx b/upload-files/20260529/2a478b9d74b3485ab696ef172cb5db3e.docx new file mode 100644 index 0000000..8dbb937 Binary files /dev/null and b/upload-files/20260529/2a478b9d74b3485ab696ef172cb5db3e.docx differ diff --git a/upload-files/20260529/56ffed2147d943adae16d1c966d1a48f.docx b/upload-files/20260529/56ffed2147d943adae16d1c966d1a48f.docx new file mode 100644 index 0000000..8dbb937 Binary files /dev/null and b/upload-files/20260529/56ffed2147d943adae16d1c966d1a48f.docx differ diff --git a/upload-files/20260529/97a2c5ece28b48f9843d1ae27656a09f.docx b/upload-files/20260529/97a2c5ece28b48f9843d1ae27656a09f.docx new file mode 100644 index 0000000..8dbb937 Binary files /dev/null and b/upload-files/20260529/97a2c5ece28b48f9843d1ae27656a09f.docx differ diff --git a/zgwl_contract_launch_field_patch_oracle.sql b/zgwl_contract_launch_field_patch_oracle.sql new file mode 100644 index 0000000..ee10f6c --- /dev/null +++ b/zgwl_contract_launch_field_patch_oracle.sql @@ -0,0 +1,33 @@ +-- 合同发起模块字段补丁 +-- 适用场景:如果你已经执行过旧版 zgwl_contract_launch_oracle.sql,再执行本文件补齐本次新增字段。 +-- 如果是新库,直接执行 zgwl_contract_launch_oracle.sql 即可,不需要执行本补丁。 + +ALTER TABLE SEAL_CONTRACT_LAUNCH_MAIN ADD SIGNING_SUBJECT_CODE VARCHAR2(500 CHAR); +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.SIGNING_SUBJECT_CODE IS '签约主体编码,多个用逗号分隔'; + +ALTER TABLE SEAL_CONTRACT_LAUNCH_MAIN ADD DATE_TYPE VARCHAR2(10 CHAR); +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.DATE_TYPE IS '固定期间日期类型:0年 1月 2日'; + +ALTER TABLE SEAL_CONTRACT_LAUNCH_MAIN ADD PUSH_URL VARCHAR2(500 CHAR); +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.PUSH_URL IS '法务审批回调地址'; + +ALTER TABLE SEAL_CONTRACT_LAUNCH_MAIN ADD EW_AMOUNT_TYPE VARCHAR2(100 CHAR); +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.EW_AMOUNT_TYPE IS '额外金额类型:DJ定金 YJ押金 YFK预付款 BZJ保证金'; + +ALTER TABLE SEAL_CONTRACT_LAUNCH_MAIN ADD DJ_AMOUNT NUMBER(20, 2); +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.DJ_AMOUNT IS '定金金额'; + +ALTER TABLE SEAL_CONTRACT_LAUNCH_MAIN ADD YJ_AMOUNT NUMBER(20, 2); +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.YJ_AMOUNT IS '押金金额'; + +ALTER TABLE SEAL_CONTRACT_LAUNCH_MAIN ADD YFK_AMOUNT NUMBER(20, 2); +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.YFK_AMOUNT IS '预付款金额'; + +ALTER TABLE SEAL_CONTRACT_LAUNCH_MAIN ADD BZJ_AMOUNT NUMBER(20, 2); +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.BZJ_AMOUNT IS '保证金金额'; + +ALTER TABLE SEAL_CONTRACT_LAUNCH_PLAN ADD PLAN_SUBJECT_CODE VARCHAR2(100 CHAR); +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PLAN.PLAN_SUBJECT_CODE IS '所属主体编码'; + +ALTER TABLE SEAL_CONTRACT_LAUNCH_PLAN ADD PLAN_SUBJECT_NAME VARCHAR2(200 CHAR); +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PLAN.PLAN_SUBJECT_NAME IS '所属主体名称'; diff --git a/zgwl_contract_launch_oracle.sql b/zgwl_contract_launch_oracle.sql new file mode 100644 index 0000000..0c4885a --- /dev/null +++ b/zgwl_contract_launch_oracle.sql @@ -0,0 +1,455 @@ +-- ============================================= +-- Oracle version for contract launch module +-- Execute with: +-- sqlplus developer/dev123@localhost/XEPDB1 @zgwl_contract_launch_oracle.sql +-- ============================================= + +WHENEVER SQLERROR EXIT SQL.SQLCODE; + +BEGIN + EXECUTE IMMEDIATE 'DROP TABLE SEAL_CONTRACT_LAUNCH_LOG'; +EXCEPTION + WHEN OTHERS THEN + IF SQLCODE != -942 THEN RAISE; END IF; +END; +/ + +BEGIN + EXECUTE IMMEDIATE 'DROP TABLE SEAL_CONTRACT_LAUNCH_PLAN'; +EXCEPTION + WHEN OTHERS THEN + IF SQLCODE != -942 THEN RAISE; END IF; +END; +/ + +BEGIN + EXECUTE IMMEDIATE 'DROP TABLE SEAL_CONTRACT_LAUNCH_FILE'; +EXCEPTION + WHEN OTHERS THEN + IF SQLCODE != -942 THEN RAISE; END IF; +END; +/ + +BEGIN + EXECUTE IMMEDIATE 'DROP TABLE SEAL_CONTRACT_LAUNCH_PARTY'; +EXCEPTION + WHEN OTHERS THEN + IF SQLCODE != -942 THEN RAISE; END IF; +END; +/ + +BEGIN + EXECUTE IMMEDIATE 'DROP TABLE SEAL_CONTRACT_LAUNCH_MAIN'; +EXCEPTION + WHEN OTHERS THEN + IF SQLCODE != -942 THEN RAISE; END IF; +END; +/ + +BEGIN + EXECUTE IMMEDIATE 'DROP SEQUENCE SEQ_SEAL_CONTRACT_LAUNCH_LOG'; +EXCEPTION + WHEN OTHERS THEN + IF SQLCODE != -2289 THEN RAISE; END IF; +END; +/ + +BEGIN + EXECUTE IMMEDIATE 'DROP SEQUENCE SEQ_SEAL_CONTRACT_LAUNCH_PLAN'; +EXCEPTION + WHEN OTHERS THEN + IF SQLCODE != -2289 THEN RAISE; END IF; +END; +/ + +BEGIN + EXECUTE IMMEDIATE 'DROP SEQUENCE SEQ_SEAL_CONTRACT_LAUNCH_FILE'; +EXCEPTION + WHEN OTHERS THEN + IF SQLCODE != -2289 THEN RAISE; END IF; +END; +/ + +BEGIN + EXECUTE IMMEDIATE 'DROP SEQUENCE SEQ_SEAL_CONTRACT_LAUNCH_PARTY'; +EXCEPTION + WHEN OTHERS THEN + IF SQLCODE != -2289 THEN RAISE; END IF; +END; +/ + +BEGIN + EXECUTE IMMEDIATE 'DROP SEQUENCE SEQ_SEAL_CONTRACT_LAUNCH_MAIN'; +EXCEPTION + WHEN OTHERS THEN + IF SQLCODE != -2289 THEN RAISE; END IF; +END; +/ + +CREATE TABLE SEAL_CONTRACT_LAUNCH_MAIN ( + ID NUMBER(19) NOT NULL, + CONTRACT_SYS_ID VARCHAR2(64 CHAR) NOT NULL, + CONTRACT_CODE VARCHAR2(64 CHAR), + LAW_CONTRACT_CODE VARCHAR2(64 CHAR), + LAW_CONTRACT_ID VARCHAR2(64 CHAR), + CONTRACT_NAME VARCHAR2(200 CHAR) NOT NULL, + CONTRACT_TYPE VARCHAR2(64 CHAR) NOT NULL, + CONTRACT_TYPE_CODE VARCHAR2(64 CHAR) NOT NULL, + CONTRACT_SOURCE VARCHAR2(32 CHAR) DEFAULT 'ECP' NOT NULL, + CREATION_MODE VARCHAR2(20 CHAR) NOT NULL, + TEMPLATE_ID NUMBER(19), + TEMPLATE_CODE VARCHAR2(64 CHAR), + TEMPLATE_NAME VARCHAR2(200 CHAR), + STATUS VARCHAR2(30 CHAR) NOT NULL, + APPROVE_STATUS VARCHAR2(30 CHAR), + APPROVE_MESSAGE VARCHAR2(500 CHAR), + SEAL_CONTRACT_ID VARCHAR2(64 CHAR), + SEAL_CONTRACT_STATUS NUMBER(10), + CONTRACT_AMOUNT NUMBER(20, 2), + CURRENCY_NAME VARCHAR2(20 CHAR), + CONTRACT_PERIOD_TYPE VARCHAR2(10 CHAR), + PERIOD_EXPLAIN VARCHAR2(500 CHAR), + EFFECTIVE_START DATE, + EFFECTIVE_END DATE, + PAYMENT_METHOD VARCHAR2(500 CHAR), + PRIMARY_CONTENT VARCHAR2(2000 CHAR), + AMOUNT_EXPLAIN VARCHAR2(2000 CHAR), + VALUATION_MODE VARCHAR2(10 CHAR), + SEAL_TYPE VARCHAR2(50 CHAR), + TEXT_SOURCE VARCHAR2(50 CHAR), + MANDATE_TYPE VARCHAR2(50 CHAR), + CARGO_TYPE VARCHAR2(50 CHAR), + ASSIST_DEPARTMENT_NAME VARCHAR2(200 CHAR), + EXECUTOR_ACCOUNT VARCHAR2(64 CHAR), + CREATOR_PHONE VARCHAR2(32 CHAR), + SELF_CODE VARCHAR2(64 CHAR), + MAIN_CONTRACT_CODE VARCHAR2(200 CHAR), + SIGNING_SUBJECT_CODE VARCHAR2(500 CHAR), + DATE_TYPE VARCHAR2(10 CHAR), + IS_ADDIT INTEGER DEFAULT 2, + IS_ELECTRON VARCHAR2(4 CHAR) DEFAULT '1', + PAYMENT_DIRECTION VARCHAR2(10 CHAR), + PUSH_URL VARCHAR2(500 CHAR), + IS_MAJOR_CONTRACT INTEGER DEFAULT 0, + IS_OPEN_BIDDING INTEGER DEFAULT 0, + IS_YEAR_BUDGET INTEGER DEFAULT 0, + IS_SHARE VARCHAR2(4 CHAR) DEFAULT '0', + CONTRACT_NATURE VARCHAR2(4 CHAR) DEFAULT '0', + IS_SGAT VARCHAR2(4 CHAR) DEFAULT '0', + IS_NORMAL VARCHAR2(4 CHAR) DEFAULT '1', + IS_TERMINATED VARCHAR2(4 CHAR) DEFAULT '0', + IS_SINGLE_AGREEMENT VARCHAR2(4 CHAR) DEFAULT '0', + BIDDING_NO VARCHAR2(100 CHAR), + OPEN_BIDDING_REMARK VARCHAR2(500 CHAR), + CONTRACT_GIST_REMARK VARCHAR2(1000 CHAR), + EW_AMOUNT_TYPE VARCHAR2(100 CHAR), + DJ_AMOUNT NUMBER(20, 2), + YJ_AMOUNT NUMBER(20, 2), + YFK_AMOUNT NUMBER(20, 2), + BZJ_AMOUNT NUMBER(20, 2), + BODY_SUMMARY VARCHAR2(2000 CHAR), + CONTRACT_TEXT_FILE_NAME VARCHAR2(255 CHAR), + CONTRACT_TEXT_FILE_PATH VARCHAR2(500 CHAR), + CONTRACT_TEXT_FILE_TYPE VARCHAR2(50 CHAR), + CONTRACT_TEXT_FILE_SIZE NUMBER(19), + SIGNED_FILE_NAME VARCHAR2(255 CHAR), + SIGNED_FILE_PATH VARCHAR2(500 CHAR), + SIGNED_FILE_TYPE VARCHAR2(50 CHAR), + SIGNED_FILE_SIZE NUMBER(19), + SIGNED_OFFLINE_REASON VARCHAR2(1000 CHAR), + SIGN_DATE DATE, + ACTIVE_DATE DATE, + ARCHIVE_DATE DATE, + ARCHIVE_KEEPER VARCHAR2(64 CHAR), + ARCHIVE_KEEPER_UNIT VARCHAR2(200 CHAR), + ARCHIVE_KEEPER_UNIT_CODE VARCHAR2(100 CHAR), + INVOICE_DATE DATE, + RECEIVE_DATE DATE, + REMIND_DAYS NUMBER(10) DEFAULT 3, + INITIATOR VARCHAR2(64 CHAR), + INITIATOR_DEPT VARCHAR2(200 CHAR), + COMPANY_NAME VARCHAR2(200 CHAR), + ORG_ID VARCHAR2(64 CHAR), + QUERY_SCOPE VARCHAR2(20 CHAR), + CREATE_BY VARCHAR2(64 CHAR), + CREATE_TIME DATE DEFAULT SYSDATE, + UPDATE_BY VARCHAR2(64 CHAR), + UPDATE_TIME DATE DEFAULT SYSDATE, + IS_DELETED VARCHAR2(2 CHAR) DEFAULT 'N', + REMARK VARCHAR2(1000 CHAR), + CONSTRAINT PK_SEAL_CONTRACT_LAUNCH_MAIN PRIMARY KEY (ID), + CONSTRAINT UK_SEAL_CONTRACT_SYS_ID UNIQUE (CONTRACT_SYS_ID) +); + +CREATE TABLE SEAL_CONTRACT_LAUNCH_PARTY ( + ID NUMBER(19) NOT NULL, + CONTRACT_ID NUMBER(19) NOT NULL, + ROLE_CODE VARCHAR2(10 CHAR) NOT NULL, + ROLE_NAME VARCHAR2(20 CHAR) NOT NULL, + SORT_ORDER NUMBER(10) DEFAULT 1, + PARTNER_ID NUMBER(19), + OPPOSITE_ID VARCHAR2(64 CHAR), + COMPANY_NAME VARCHAR2(200 CHAR) NOT NULL, + COMPANY_TYPE VARCHAR2(50 CHAR), + OPPOSITE_CHARACTER VARCHAR2(20 CHAR), + CONTACT_NAME VARCHAR2(100 CHAR), + CONTACT_PHONE VARCHAR2(50 CHAR), + CONTACT_EMAIL VARCHAR2(100 CHAR), + CONTACT_ADDRESS VARCHAR2(500 CHAR), + REGISTERED_ADDRESS VARCHAR2(500 CHAR), + LEGAL_REPRESENTATIVE VARCHAR2(100 CHAR), + CREDIT_CODE VARCHAR2(100 CHAR), + BANK_NAME VARCHAR2(200 CHAR), + BANK_ACCOUNT VARCHAR2(100 CHAR), + NATURAL_PERSON_ID_TYPE VARCHAR2(20 CHAR), + NATURAL_PERSON_ID_NUMBER VARCHAR2(100 CHAR), + IS_GROUP_INTERNAL VARCHAR2(4 CHAR), + IS_HONG_KONG_MACAO_TAIWAN VARCHAR2(4 CHAR), + CREATE_BY VARCHAR2(64 CHAR), + CREATE_TIME DATE DEFAULT SYSDATE, + UPDATE_BY VARCHAR2(64 CHAR), + UPDATE_TIME DATE DEFAULT SYSDATE, + IS_DELETED VARCHAR2(2 CHAR) DEFAULT 'N', + REMARK VARCHAR2(1000 CHAR), + CONSTRAINT PK_SEAL_CONTRACT_LAUNCH_PARTY PRIMARY KEY (ID) +); + +CREATE TABLE SEAL_CONTRACT_LAUNCH_FILE ( + ID NUMBER(19) NOT NULL, + CONTRACT_ID NUMBER(19) NOT NULL, + FILE_CATEGORY VARCHAR2(30 CHAR) NOT NULL, + FILE_NAME VARCHAR2(255 CHAR) NOT NULL, + FILE_PATH VARCHAR2(500 CHAR) NOT NULL, + FILE_TYPE VARCHAR2(50 CHAR), + FILE_SIZE NUMBER(19), + SORT_ORDER NUMBER(10) DEFAULT 1, + CREATE_BY VARCHAR2(64 CHAR), + CREATE_TIME DATE DEFAULT SYSDATE, + UPDATE_BY VARCHAR2(64 CHAR), + UPDATE_TIME DATE DEFAULT SYSDATE, + IS_DELETED VARCHAR2(2 CHAR) DEFAULT 'N', + REMARK VARCHAR2(1000 CHAR), + CONSTRAINT PK_SEAL_CONTRACT_LAUNCH_FILE PRIMARY KEY (ID) +); + +CREATE TABLE SEAL_CONTRACT_LAUNCH_PLAN ( + ID NUMBER(19) NOT NULL, + CONTRACT_ID NUMBER(19) NOT NULL, + PLAN_MATTER VARCHAR2(500 CHAR) NOT NULL, + PLAN_WHERE VARCHAR2(500 CHAR) NOT NULL, + PLAN_TIME DATE NOT NULL, + PLAN_DAY NUMBER(10) DEFAULT 3, + PLAN_MONEY NUMBER(20, 2), + PLAN_SUBJECT_CODE VARCHAR2(100 CHAR), + PLAN_SUBJECT_NAME VARCHAR2(200 CHAR), + PLAN_DETAIL VARCHAR2(1000 CHAR), + PLAN_STATUS VARCHAR2(20 CHAR) DEFAULT '0', + CREATE_BY VARCHAR2(64 CHAR), + CREATE_TIME DATE DEFAULT SYSDATE, + UPDATE_BY VARCHAR2(64 CHAR), + UPDATE_TIME DATE DEFAULT SYSDATE, + IS_DELETED VARCHAR2(2 CHAR) DEFAULT 'N', + REMARK VARCHAR2(1000 CHAR), + CONSTRAINT PK_SEAL_CONTRACT_LAUNCH_PLAN PRIMARY KEY (ID) +); + +CREATE TABLE SEAL_CONTRACT_LAUNCH_LOG ( + ID NUMBER(19) NOT NULL, + CONTRACT_ID NUMBER(19) NOT NULL, + ACTION_TYPE VARCHAR2(50 CHAR) NOT NULL, + ACTION_NAME VARCHAR2(100 CHAR) NOT NULL, + ACTION_STATUS VARCHAR2(20 CHAR) NOT NULL, + OPERATOR_NAME VARCHAR2(64 CHAR), + OPERATOR_PHONE VARCHAR2(32 CHAR), + ACTION_TIME DATE DEFAULT SYSDATE, + ACTION_CONTENT VARCHAR2(2000 CHAR), + CALLBACK_PAYLOAD CLOB, + REMARK VARCHAR2(1000 CHAR), + CONSTRAINT PK_SEAL_CONTRACT_LAUNCH_LOG PRIMARY KEY (ID) +); + +COMMENT ON TABLE SEAL_CONTRACT_LAUNCH_MAIN IS '合同发起主表'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.ID IS '主键ID'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.CONTRACT_SYS_ID IS '平台合同唯一编码'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.CONTRACT_CODE IS '业务合同编码'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.LAW_CONTRACT_CODE IS '法务系统合同编号'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.LAW_CONTRACT_ID IS '法务系统合同ID'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.CONTRACT_NAME IS '合同名称'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.CONTRACT_TYPE IS '合同类型中文名称'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.CONTRACT_TYPE_CODE IS '合同类型编码'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.CONTRACT_SOURCE IS '合同来源'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.CREATION_MODE IS '创建方式:template/manual'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.TEMPLATE_ID IS '模板ID'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.TEMPLATE_CODE IS '模板编号'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.TEMPLATE_NAME IS '模板名称'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.STATUS IS '合同当前状态'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.APPROVE_STATUS IS '审批状态'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.APPROVE_MESSAGE IS '审批说明'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.SEAL_CONTRACT_ID IS '签章平台合同ID'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.SEAL_CONTRACT_STATUS IS '签章平台合同状态'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.CONTRACT_AMOUNT IS '合同金额'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.CURRENCY_NAME IS '币种'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.CONTRACT_PERIOD_TYPE IS '合同期限类型:0固定期限 1无固定期限'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.PERIOD_EXPLAIN IS '期限说明'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.EFFECTIVE_START IS '生效开始日期'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.EFFECTIVE_END IS '生效结束日期'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.PAYMENT_METHOD IS '付款方式'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.PRIMARY_CONTENT IS '合同主要内容'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.AMOUNT_EXPLAIN IS '金额说明'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.VALUATION_MODE IS '计价方式'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.SEAL_TYPE IS '签订方式'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.TEXT_SOURCE IS '文本来源'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.MANDATE_TYPE IS '委托类型'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.CARGO_TYPE IS '货种'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.ASSIST_DEPARTMENT_NAME IS '协助部门'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.EXECUTOR_ACCOUNT IS '合同执行人账号'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.CREATOR_PHONE IS '创建人手机号'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.SELF_CODE IS '二级单位合同编码'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.MAIN_CONTRACT_CODE IS '主合同编码'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.SIGNING_SUBJECT_CODE IS '签约主体编码,多个用逗号分隔'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.DATE_TYPE IS '固定期间日期类型:0年 1月 2日'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.IS_ADDIT IS '是否补录:1是 2否'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.IS_ELECTRON IS '签署方式:0纸质 1电子'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.PAYMENT_DIRECTION IS '收支方向'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.PUSH_URL IS '法务审批回调地址'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.IS_MAJOR_CONTRACT IS '是否重大合同'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.IS_OPEN_BIDDING IS '是否公开招标'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.IS_YEAR_BUDGET IS '是否列入当年预算'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.IS_SHARE IS '是否共享'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.CONTRACT_NATURE IS '签约主体是否涉外'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.IS_SGAT IS '是否涉港澳台'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.IS_NORMAL IS '是否正常'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.IS_TERMINATED IS '是否终止'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.IS_SINGLE_AGREEMENT IS '是否单方协议'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.BIDDING_NO IS '招标编号'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.OPEN_BIDDING_REMARK IS '公开招标备注'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.CONTRACT_GIST_REMARK IS '签订依据说明'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.EW_AMOUNT_TYPE IS '额外金额类型:DJ定金 YJ押金 YFK预付款 BZJ保证金'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.DJ_AMOUNT IS '定金金额'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.YJ_AMOUNT IS '押金金额'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.YFK_AMOUNT IS '预付款金额'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.BZJ_AMOUNT IS '保证金金额'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.BODY_SUMMARY IS '正文摘要'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.CONTRACT_TEXT_FILE_NAME IS '合同正文文件名'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.CONTRACT_TEXT_FILE_PATH IS '合同正文文件路径'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.CONTRACT_TEXT_FILE_TYPE IS '合同正文文件类型'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.CONTRACT_TEXT_FILE_SIZE IS '合同正文文件大小'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.SIGNED_FILE_NAME IS '已签署文件名'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.SIGNED_FILE_PATH IS '已签署文件路径'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.SIGNED_FILE_TYPE IS '已签署文件类型'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.SIGNED_FILE_SIZE IS '已签署文件大小'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.SIGNED_OFFLINE_REASON IS '线下签署原因'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.SIGN_DATE IS '签署日期'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.ACTIVE_DATE IS '合同生效日期'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.ARCHIVE_DATE IS '归档时间'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.ARCHIVE_KEEPER IS '档案保管人'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.ARCHIVE_KEEPER_UNIT IS '档案保管单位'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.ARCHIVE_KEEPER_UNIT_CODE IS '档案保管单位编码'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.INVOICE_DATE IS '开票时间'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.RECEIVE_DATE IS '收款时间'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.REMIND_DAYS IS '提醒提前天数'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.INITIATOR IS '发起人'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.INITIATOR_DEPT IS '发起部门'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.COMPANY_NAME IS '发起公司'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.ORG_ID IS '组织ID'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.QUERY_SCOPE IS '查询范围'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.CREATE_BY IS '创建人'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.CREATE_TIME IS '创建时间'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.UPDATE_BY IS '更新人'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.UPDATE_TIME IS '更新时间'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.IS_DELETED IS '逻辑删除标记'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_MAIN.REMARK IS '备注'; + +COMMENT ON TABLE SEAL_CONTRACT_LAUNCH_PARTY IS '合同方信息表'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.ID IS '主键ID'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.CONTRACT_ID IS '合同主表ID'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.ROLE_CODE IS '角色编码:A/B/C'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.ROLE_NAME IS '角色名称:甲方/乙方/丙方'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.SORT_ORDER IS '排序号'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.PARTNER_ID IS '相对方管理表主键'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.OPPOSITE_ID IS '法务相对方ID'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.COMPANY_NAME IS '公司名称'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.COMPANY_TYPE IS '公司类型'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.OPPOSITE_CHARACTER IS '公司性质'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.CONTACT_NAME IS '联系人'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.CONTACT_PHONE IS '联系电话'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.CONTACT_EMAIL IS '联系邮箱'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.CONTACT_ADDRESS IS '通讯地址'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.REGISTERED_ADDRESS IS '注册地址'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.LEGAL_REPRESENTATIVE IS '法定代表人'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.CREDIT_CODE IS '统一社会信用代码'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.BANK_NAME IS '开户行'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.BANK_ACCOUNT IS '银行账号'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.NATURAL_PERSON_ID_TYPE IS '自然人证件类型'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.NATURAL_PERSON_ID_NUMBER IS '自然人证件号码'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.IS_GROUP_INTERNAL IS '是否集团内'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.IS_HONG_KONG_MACAO_TAIWAN IS '是否涉港澳台'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.CREATE_BY IS '创建人'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.CREATE_TIME IS '创建时间'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.UPDATE_BY IS '更新人'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.UPDATE_TIME IS '更新时间'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.IS_DELETED IS '逻辑删除标记'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PARTY.REMARK IS '备注'; + +COMMENT ON TABLE SEAL_CONTRACT_LAUNCH_FILE IS '合同附件表'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_FILE.ID IS '主键ID'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_FILE.CONTRACT_ID IS '合同主表ID'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_FILE.FILE_CATEGORY IS '文件分类:body/other/gist/signed/archive'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_FILE.FILE_NAME IS '文件名称'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_FILE.FILE_PATH IS '文件路径'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_FILE.FILE_TYPE IS '文件类型'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_FILE.FILE_SIZE IS '文件大小'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_FILE.SORT_ORDER IS '排序号'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_FILE.CREATE_BY IS '创建人'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_FILE.CREATE_TIME IS '创建时间'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_FILE.UPDATE_BY IS '更新人'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_FILE.UPDATE_TIME IS '更新时间'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_FILE.IS_DELETED IS '逻辑删除标记'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_FILE.REMARK IS '备注'; + +COMMENT ON TABLE SEAL_CONTRACT_LAUNCH_PLAN IS '合同履行计划表'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PLAN.ID IS '主键ID'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PLAN.CONTRACT_ID IS '合同主表ID'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PLAN.PLAN_MATTER IS '履行事项'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PLAN.PLAN_WHERE IS '履约条件'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PLAN.PLAN_TIME IS '节点日期'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PLAN.PLAN_DAY IS '提前提醒天数'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PLAN.PLAN_MONEY IS '款项金额'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PLAN.PLAN_SUBJECT_CODE IS '所属主体编码'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PLAN.PLAN_SUBJECT_NAME IS '所属主体名称'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PLAN.PLAN_DETAIL IS '备注'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PLAN.PLAN_STATUS IS '反馈状态'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PLAN.CREATE_BY IS '创建人'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PLAN.CREATE_TIME IS '创建时间'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PLAN.UPDATE_BY IS '更新人'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PLAN.UPDATE_TIME IS '更新时间'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PLAN.IS_DELETED IS '逻辑删除标记'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_PLAN.REMARK IS '备注'; + +COMMENT ON TABLE SEAL_CONTRACT_LAUNCH_LOG IS '合同操作日志表'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_LOG.ID IS '主键ID'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_LOG.CONTRACT_ID IS '合同主表ID'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_LOG.ACTION_TYPE IS '动作类型'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_LOG.ACTION_NAME IS '动作名称'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_LOG.ACTION_STATUS IS '动作状态'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_LOG.OPERATOR_NAME IS '操作人'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_LOG.OPERATOR_PHONE IS '操作人手机号'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_LOG.ACTION_TIME IS '操作时间'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_LOG.ACTION_CONTENT IS '操作说明'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_LOG.CALLBACK_PAYLOAD IS '回调或接口报文'; +COMMENT ON COLUMN SEAL_CONTRACT_LAUNCH_LOG.REMARK IS '备注'; + +CREATE INDEX IDX_CONTRACT_LAUNCH_STATUS ON SEAL_CONTRACT_LAUNCH_MAIN (STATUS); +CREATE INDEX IDX_CONTRACT_LAUNCH_CODE ON SEAL_CONTRACT_LAUNCH_MAIN (CONTRACT_CODE); +CREATE INDEX IDX_CONTRACT_LAUNCH_NAME ON SEAL_CONTRACT_LAUNCH_MAIN (CONTRACT_NAME); +CREATE INDEX IDX_CONTRACT_LAUNCH_CREATE_TIME ON SEAL_CONTRACT_LAUNCH_MAIN (CREATE_TIME); +CREATE INDEX IDX_CONTRACT_LAUNCH_PARTY_CONTRACT ON SEAL_CONTRACT_LAUNCH_PARTY (CONTRACT_ID); +CREATE INDEX IDX_CONTRACT_LAUNCH_FILE_CONTRACT ON SEAL_CONTRACT_LAUNCH_FILE (CONTRACT_ID); +CREATE INDEX IDX_CONTRACT_LAUNCH_PLAN_CONTRACT ON SEAL_CONTRACT_LAUNCH_PLAN (CONTRACT_ID); +CREATE INDEX IDX_CONTRACT_LAUNCH_LOG_CONTRACT ON SEAL_CONTRACT_LAUNCH_LOG (CONTRACT_ID); + +CREATE SEQUENCE SEQ_SEAL_CONTRACT_LAUNCH_MAIN START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; +CREATE SEQUENCE SEQ_SEAL_CONTRACT_LAUNCH_PARTY START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; +CREATE SEQUENCE SEQ_SEAL_CONTRACT_LAUNCH_FILE START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; +CREATE SEQUENCE SEQ_SEAL_CONTRACT_LAUNCH_PLAN START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE; +CREATE SEQUENCE SEQ_SEAL_CONTRACT_LAUNCH_LOG START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE;