From 2939ec73abbbde65cbe1a6c8c503276c582f63d8 Mon Sep 17 00:00:00 2001 From: yanglin Date: Thu, 20 Feb 2025 09:28:46 +0800 Subject: [PATCH] =?UTF-8?q?REQ-3581:=20=E5=A4=87=E4=BB=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/axzo/nanopart/ess/api/EssApi.java | 41 ++--- .../CreateContractByFile.java | 2 +- .../CreateContractInfo.java | 35 ++-- .../ess/api/domain/DynamicApproverProps.java | 27 --- .../ess/api/domain/EssSealPersonInfo.java | 5 + .../nanopart/ess/api/domain/OperatorInfo.java | 27 +++ .../nanopart/ess/api/domain/OrgPerson.java | 6 + .../ess/api/domain/contract/Approver.java | 26 ++- .../api/domain/contract/EssApproveDetail.java | 8 +- .../api/domain/contract/EssContractInfo.java | 15 +- .../api/domain/contract/OrgPersonInfo.java | 3 +- .../ess/api/enums/ApproverAssignType.java | 13 -- .../nanopart/ess/api/enums/Constraint.java | 11 ++ .../ess/api/enums/EssContractState.java | 12 ++ ...{SignUrlType.java => SignUrlEndpoint.java} | 2 +- .../api/mq/EssContractStateChangeMessage.java | 2 +- .../request/CreateConsoleLoginUrlRequest.java | 5 +- .../request/CreateContractByFileRequest.java | 32 ++++ .../api/request/CreateContractRequest.java | 44 ++--- .../request/GetContractSignUrlRequest.java | 4 +- ...OrgRequest.java => GetSignUrlRequest.java} | 15 +- .../CreateContractByFileResponse.java | 28 +++ .../api/response/CreateContractResponse.java | 24 --- ...gResponse.java => GetSignUrlResponse.java} | 2 +- .../ess/server/dao/EssContractDao.java | 26 +-- .../nanopart/ess/server/dao/EssLogDao.java | 22 ++- .../nanopart/ess/server/dao/EssOrgDao.java | 10 +- .../nanopart/ess/server/dao/EssPersonDao.java | 8 +- .../nanopart/ess/server/dao/EssSealDao.java | 9 - .../ess/server/dao/EssSealPersonDao.java | 23 +-- .../ess/server/entity/EssContract.java | 59 +++--- .../nanopart/ess/server/entity/EssOrg.java | 4 + .../ess/server/entity/EssSealPerson.java | 11 ++ .../entity/domain/AssignedApprovers.java | 18 -- .../ess/server/entity/domain/Assignment.java | 16 ++ .../ess/server/entity/domain/SignByOrg.java | 74 -------- .../ess/server/ess/ContractManager.java | 169 ++++-------------- .../{Broadcaster.java => EssBroadcaster.java} | 7 +- .../nanopart/ess/server/ess/EssClient.java | 91 ++++------ .../nanopart/ess/server/ess/EssService.java | 85 ++++++--- .../nanopart/ess/server/ess/OrgManager.java | 31 ++-- .../ess/server/ess/PersonResignListener.java | 17 +- .../nanopart/ess/server/ess/QueryService.java | 19 +- .../server/ess/controller/ApiController.java | 33 ++-- .../ess/controller/CallbackController.java | 31 ++-- .../ess/controller/PrivateController.java | 9 +- .../ess/server/ess/domain/PersonOpenId.java | 2 +- .../server/ess/support/ContractSupport.java | 87 +++++++++ .../ess/server/ess/support/EssSupport.java | 85 ++++----- .../ess/server/ess/support/OssService.java | 47 ++--- .../server/ess/support/PersonProfiles.java | 7 +- .../ess/server/utils/BizAssertions.java | 54 +++--- 52 files changed, 695 insertions(+), 748 deletions(-) rename ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/{request => domain}/CreateContractByFile.java (90%) rename ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/{request => domain}/CreateContractInfo.java (70%) delete mode 100644 ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/DynamicApproverProps.java create mode 100644 ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/OperatorInfo.java delete mode 100644 ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/enums/ApproverAssignType.java create mode 100644 ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/enums/Constraint.java rename ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/enums/{SignUrlType.java => SignUrlEndpoint.java} (74%) create mode 100644 ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/CreateContractByFileRequest.java rename ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/{AssignWeixinAppUrlByOrgRequest.java => GetSignUrlRequest.java} (60%) create mode 100644 ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/response/CreateContractByFileResponse.java delete mode 100644 ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/response/CreateContractResponse.java rename ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/response/{AssignWeixinAppUrlByOrgResponse.java => GetSignUrlResponse.java} (84%) delete mode 100644 ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/domain/AssignedApprovers.java create mode 100644 ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/domain/Assignment.java delete mode 100644 ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/domain/SignByOrg.java rename ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/{Broadcaster.java => EssBroadcaster.java} (88%) create mode 100644 ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/support/ContractSupport.java diff --git a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/EssApi.java b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/EssApi.java index 5ee8dc93..c76a4aae 100644 --- a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/EssApi.java +++ b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/EssApi.java @@ -4,28 +4,26 @@ import cn.axzo.framework.domain.web.result.ApiResult; import cn.axzo.nanopart.ess.api.domain.EssOrgAndSealInfo; import cn.axzo.nanopart.ess.api.request.AddSealAuthorizationRequest; import cn.axzo.nanopart.ess.api.request.AddSealPersonsRequest; -import cn.axzo.nanopart.ess.api.request.AssignWeixinAppUrlByOrgRequest; import cn.axzo.nanopart.ess.api.request.CreateConsoleLoginUrlRequest; -import cn.axzo.nanopart.ess.api.request.CreateContractRequest; +import cn.axzo.nanopart.ess.api.request.CreateContractByFileRequest; import cn.axzo.nanopart.ess.api.request.DownloadSingedContractPdfRequest; -import cn.axzo.nanopart.ess.api.request.GetContractDetailByBizCodeRequest; import cn.axzo.nanopart.ess.api.request.GetContractDetailByContractIdRequest; import cn.axzo.nanopart.ess.api.request.GetEmbedWebUrlRequest; import cn.axzo.nanopart.ess.api.request.GetOrgAuthStatesRequest; import cn.axzo.nanopart.ess.api.request.GetPersonAuthStateRequest; import cn.axzo.nanopart.ess.api.request.GetSealsRequest; +import cn.axzo.nanopart.ess.api.request.GetSignUrlRequest; import cn.axzo.nanopart.ess.api.request.RemoveSealAuthorizationRequest; import cn.axzo.nanopart.ess.api.request.RemoveSealPersonRequest; import cn.axzo.nanopart.ess.api.request.RevokeContractRequest; -import cn.axzo.nanopart.ess.api.response.AssignWeixinAppUrlByOrgResponse; import cn.axzo.nanopart.ess.api.response.CreateConsoleLoginUrlResponse; -import cn.axzo.nanopart.ess.api.response.CreateContractResponse; +import cn.axzo.nanopart.ess.api.response.CreateContractByFileResponse; import cn.axzo.nanopart.ess.api.response.DownloadSingedContractPdfResponse; -import cn.axzo.nanopart.ess.api.response.GetContractDetailByBizCodeResponse; import cn.axzo.nanopart.ess.api.response.GetContractDetailByContractIdResponse; import cn.axzo.nanopart.ess.api.response.GetEmbedWebUrlResponse; import cn.axzo.nanopart.ess.api.response.GetOrgAuthStatesResponse; import cn.axzo.nanopart.ess.api.response.GetPersonAuthStateResponse; +import cn.axzo.nanopart.ess.api.response.GetSignUrlResponse; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -68,7 +66,7 @@ public interface EssApi { @RequestBody @Valid GetEmbedWebUrlRequest request); /** - * 查询单位已启用的印章以及授权人员列表 + * 查询单位已启用的印章以及授权人员列表等等 */ @PostMapping("api/ess/getOrgEnabledSeals") ApiResult> getOrgEnabledSeals( @@ -103,21 +101,21 @@ public interface EssApi { @RequestBody @Valid RemoveSealAuthorizationRequest request); /** - * 创建合同 + * 通过文件创建合同 */ - @PostMapping("api/ess/createContract") - ApiResult createContract( - @RequestBody @Valid CreateContractRequest request); + @PostMapping("api/ess/createContractByFile") + ApiResult createContractByFile( + @RequestBody @Valid CreateContractByFileRequest request); /** - * 根据单位分配微信小程序签署链接, 有效期为5分钟 + * 获取签署链接, 有效期为5分钟 */ - @PostMapping("api/ess/assignWeixinAppUrlByOrg") - ApiResult assignWeixinAppUrlByOrg( - @RequestBody @Valid AssignWeixinAppUrlByOrgRequest request); + @PostMapping("api/ess/getContractSignUrl") + ApiResult getContractSignUrl( + @RequestBody @Valid GetSignUrlRequest request); /** - * 下载已签署的合同PDF. 下载链接有效期为5分钟 + * 下载合同PDF. 下载链接有效期为5分钟 */ @PostMapping("api/ess/getContractPDFUrl") ApiResult getContractPDFUrl( @@ -133,15 +131,8 @@ public interface EssApi { /** * 获取合同详情, 通过合同id */ - @PostMapping("api/ess/getContractDetailByContractId") - ApiResult getContractDetailByContractId( + @PostMapping("api/ess/getContractByContractId") + ApiResult getContractByContractId( @RequestBody @Valid GetContractDetailByContractIdRequest request); - /** - * 获取合同详情, 通过业务id - */ - @PostMapping("api/ess/getContractDetailByBizCode") - ApiResult getContractDetailByBizCode( - @RequestBody @Valid GetContractDetailByBizCodeRequest request); - } \ No newline at end of file diff --git a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/CreateContractByFile.java b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/CreateContractByFile.java similarity index 90% rename from ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/CreateContractByFile.java rename to ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/CreateContractByFile.java index 9f5ceafc..9bd121e0 100644 --- a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/CreateContractByFile.java +++ b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/CreateContractByFile.java @@ -1,4 +1,4 @@ -package cn.axzo.nanopart.ess.api.request; +package cn.axzo.nanopart.ess.api.domain; import lombok.Getter; import lombok.Setter; diff --git a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/CreateContractInfo.java b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/CreateContractInfo.java similarity index 70% rename from ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/CreateContractInfo.java rename to ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/CreateContractInfo.java index 7cd5a43a..242a9a00 100644 --- a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/CreateContractInfo.java +++ b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/CreateContractInfo.java @@ -1,14 +1,17 @@ -package cn.axzo.nanopart.ess.api.request; +package cn.axzo.nanopart.ess.api.domain; -import cn.axzo.nanopart.ess.api.domain.DynamicApproverProps; import cn.axzo.nanopart.ess.api.domain.contract.Approver; +import cn.axzo.nanopart.ess.api.enums.Constraint; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Getter; import lombok.Setter; +import org.hibernate.validator.constraints.Range; import javax.validation.Valid; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.Collections; import java.util.List; import java.util.Set; @@ -18,7 +21,7 @@ import static java.util.stream.Collectors.toSet; * @author yanglin */ @Setter @Getter -public class CreateContractInfo { +public abstract class CreateContractInfo { /** * 合同名称 @@ -26,31 +29,25 @@ public class CreateContractInfo { @NotBlank(message = "contractName不能为空") private String contractName; - /** - * 是否为动态签署人, 即在创建合同的时候不指定签署人, 而是在获取签署链接时指定签署人 - * dynamicApprover=true时,不需要指定Approver#signPerson - * dynamicApprover=false时, 需要指定Approver#signPerson - */ - private boolean dynamicApprover; - - /** - * 动态签署人属性 - */ - @Valid - private DynamicApproverProps dynamicApproverProps; - /** * 合同签署方 */ @Valid - @NotEmpty + @NotEmpty(message = "approvers不能为空") private List approvers; + /** + * 合同签署方的签署约束种类 + */ + @NotNull(message = "approverConstraint不能为空") + private Constraint constraint = Constraint.ONE_PERSON_PER_ORG; + /** * 签署方签署控件(印章/签名等)的生成方式. * 0: 在合同流程发起时,由发起人指定签署方的签署控件的位置和数量 * 1: 签署方在签署时自行添加签署控件,可以拖动位置和控制数量 */ + @Range(min = 0, max = 1, message = "signBeanTag必须是0或1") private Long signBeanTag = 1L; /** @@ -59,7 +56,7 @@ public class CreateContractInfo { private boolean signOrdered = false; /** - * 合同流程的签署截止时间,格式为Unix标准时间戳 + * 合同流程的签署截止时间,格式为Unix标准时间戳, 透传给腾讯电子签 * 如果未设置签署截止时间,则默认为合同流程创建后的365天时截止 * 如果在签署截止时间前未完成签署,则合同状态会变为已过期,导致合同作废。 */ @@ -67,6 +64,8 @@ public class CreateContractInfo { @JsonIgnore public Set getApproverOuIds() { + if (approvers == null) + return Collections.emptySet(); return approvers.stream() .map(Approver::getOuId) .collect(toSet()); diff --git a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/DynamicApproverProps.java b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/DynamicApproverProps.java deleted file mode 100644 index bdd7b1ba..00000000 --- a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/DynamicApproverProps.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.axzo.nanopart.ess.api.domain; - -import cn.axzo.nanopart.ess.api.enums.ApproverAssignType; -import lombok.Getter; -import lombok.Setter; - -import javax.validation.constraints.NotNull; - -/** - * @author yanglin - */ -@Setter @Getter -public class DynamicApproverProps { - - public static DynamicApproverProps defaultProps() { - DynamicApproverProps props = new DynamicApproverProps(); - props.setAssignType(ApproverAssignType.NONE); - return props; - } - - /** - * 动态签署人的限制信息 - */ - @NotNull(message = "assignType不能为空") - private ApproverAssignType assignType = ApproverAssignType.NONE; - -} \ No newline at end of file diff --git a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/EssSealPersonInfo.java b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/EssSealPersonInfo.java index 0d1ec8fe..5b682b55 100644 --- a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/EssSealPersonInfo.java +++ b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/EssSealPersonInfo.java @@ -34,6 +34,11 @@ public class EssSealPersonInfo { */ private boolean isPersonAuthorized; + /** + * 授权时间, unix时间戳 + */ + private Long authorizeTimeMs; + /** * 是否已经认证(印章授权) */ diff --git a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/OperatorInfo.java b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/OperatorInfo.java new file mode 100644 index 00000000..4907545a --- /dev/null +++ b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/OperatorInfo.java @@ -0,0 +1,27 @@ +package cn.axzo.nanopart.ess.api.domain; + +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotNull; + +/** + * @author yanglin + */ +@Setter +@Getter +public class OperatorInfo implements OrgPerson { + + /** + * 发起方单位id + */ + @NotNull(message = "ouId不能为空") + private Long ouId; + + /** + * 发起方人员id + */ + @NotNull(message = "personId不能为空") + private Long personId; + +} diff --git a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/OrgPerson.java b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/OrgPerson.java index 95d034c8..bd3841d7 100644 --- a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/OrgPerson.java +++ b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/OrgPerson.java @@ -1,5 +1,7 @@ package cn.axzo.nanopart.ess.api.domain; +import java.util.Objects; + /** * @author yanglin */ @@ -9,4 +11,8 @@ public interface OrgPerson { Long getPersonId(); + static boolean equals(OrgPerson a, OrgPerson b) { + return Objects.equals(a.getOuId(), b.getOuId()) + && Objects.equals(a.getPersonId(), b.getPersonId()); + } } \ No newline at end of file diff --git a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/contract/Approver.java b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/contract/Approver.java index 53c608d9..48309677 100644 --- a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/contract/Approver.java +++ b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/contract/Approver.java @@ -8,8 +8,11 @@ import com.alibaba.fastjson.annotation.JSONField; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Getter; import lombok.Setter; +import org.hibernate.validator.constraints.Range; import javax.validation.Valid; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import java.util.List; @@ -18,10 +21,10 @@ import java.util.List; */ @Setter @Getter -public class Approver implements OrgPerson { +public class Approver implements OrgPerson { /** - * 签署方人员信息, 如果是动态签署人, 则不需要传入. 在获取签署链接时再指定签署人 + * 签署方人员信息, 如果是动态签署人, 则传null, 在获取签署链接时再指定签署人 */ @Valid private OrgPersonInfo signPerson; @@ -35,6 +38,7 @@ public class Approver implements OrgPerson { /** * 签署方在签署合同之前,需要强制阅读合同的时长,可指定为3秒至300秒之间的任意值 */ + @Range(min = 3, max = 300, message = "preReadTime必须在3-300之间") @NotNull(message = "preReadTime不能为空") private Long preReadTimeSeconds = 10L; @@ -46,7 +50,7 @@ public class Approver implements OrgPerson { /** * 扩展信息, 内部使用 */ - private JSONObject extension; + private JSONObject internal; public void setEssRecipientId(String essRecipientId) { getOrCreateExtension().put("essRecipientId", essRecipientId); @@ -54,15 +58,20 @@ public class Approver implements OrgPerson { @JsonIgnore @JSONField(serialize = false) public String getEssRecipientId() { - if (extension == null) return null; - return extension.getString("essRecipientId"); + if (internal == null) + return null; + return internal.getString("essRecipientId"); } @JsonIgnore @JSONField(serialize = false) public JSONObject getOrCreateExtension() { - if (extension == null) - extension = new JSONObject(); - return extension; + if (internal == null) + internal = new JSONObject(); + return internal; + } + + public boolean isSignPersonPresent() { + return signPerson != null; } @JsonIgnore @JSONField(serialize = false) @@ -74,4 +83,5 @@ public class Approver implements OrgPerson { public Long getPersonId() { return signPerson == null ? 0L : signPerson.getPersonId(); } + } \ No newline at end of file diff --git a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/contract/EssApproveDetail.java b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/contract/EssApproveDetail.java index 5441fb79..0c9b4fb5 100644 --- a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/contract/EssApproveDetail.java +++ b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/contract/EssApproveDetail.java @@ -12,12 +12,12 @@ import lombok.Setter; public class EssApproveDetail { /** - * 签署人单位 + * 签署人单位, 动态签署人在没有签署的时候可能为空 */ private Long ouId; /** - * 签署人 + * 签署人, 动态签署人在没有签署的时候可能为空 */ private Long personId; @@ -52,12 +52,12 @@ public class EssApproveDetail { private long approverDeadlineMs; /** - * 单位名称 + * 单位名称, 动态签署人在没有签署的时候可能为空 */ private String ouName; /** - * 对应签署人的手机号 + * 对应签署人的手机号, 动态签署人在没有签署的时候可能为空 */ private String phoneNumber; } diff --git a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/contract/EssContractInfo.java b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/contract/EssContractInfo.java index c3e9d250..a988bae7 100644 --- a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/contract/EssContractInfo.java +++ b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/contract/EssContractInfo.java @@ -1,7 +1,6 @@ package cn.axzo.nanopart.ess.api.domain.contract; import cn.axzo.nanopart.ess.api.enums.EssContractState; -import cn.axzo.nanopart.ess.api.utils.YesOrNo; import lombok.Getter; import lombok.Setter; @@ -50,22 +49,12 @@ public class EssContractInfo { private EssContractState state; /** - * 是否为动态签署人 - */ - private YesOrNo isDynamicApprover; - - /** - * 合并签署方信息(非动态签署人) + * 合并签署方信息, 如果是动态签署, 里面的签署人信息可能为nul */ private List approvers; /** - * 动态签署人信息 - */ - private List dynamicApprovers; - - /** - * 签署方的签署情况 + * 合同的签署方的签署情况 */ private List approveDetails; diff --git a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/contract/OrgPersonInfo.java b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/contract/OrgPersonInfo.java index 3ead6c30..26884ea8 100644 --- a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/contract/OrgPersonInfo.java +++ b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/domain/contract/OrgPersonInfo.java @@ -1,5 +1,6 @@ package cn.axzo.nanopart.ess.api.domain.contract; +import cn.axzo.nanopart.ess.api.domain.OrgPerson; import lombok.Getter; import lombok.Setter; @@ -9,7 +10,7 @@ import javax.validation.constraints.NotNull; * @author yanglin */ @Setter @Getter -public class OrgPersonInfo { +public class OrgPersonInfo implements OrgPerson { /** * 单位id diff --git a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/enums/ApproverAssignType.java b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/enums/ApproverAssignType.java deleted file mode 100644 index 7bef4edd..00000000 --- a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/enums/ApproverAssignType.java +++ /dev/null @@ -1,13 +0,0 @@ -package cn.axzo.nanopart.ess.api.enums; - -/** - * @author yanglin - */ -public enum ApproverAssignType { - - // 一个子客一个用户盖章 - ONE_PERSON_PER_ORG, - // 不做限制 - NONE - -} \ No newline at end of file diff --git a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/enums/Constraint.java b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/enums/Constraint.java new file mode 100644 index 00000000..b321843c --- /dev/null +++ b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/enums/Constraint.java @@ -0,0 +1,11 @@ +package cn.axzo.nanopart.ess.api.enums; + +/** + * @author yanglin + */ +public enum Constraint { + + // 一个子客只允许一个用户盖章 + ONE_PERSON_PER_ORG + +} \ No newline at end of file diff --git a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/enums/EssContractState.java b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/enums/EssContractState.java index 7f4717f4..f792685f 100644 --- a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/enums/EssContractState.java +++ b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/enums/EssContractState.java @@ -4,6 +4,12 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import static java.util.stream.Collectors.toList; + /** * @author yanglin */ @@ -35,4 +41,10 @@ public enum EssContractState { } return null; } + + public static List collectFinalStates() { + return Arrays.stream(values()) + .filter(EssContractState::isFinalState) + .collect(toList()); + } } \ No newline at end of file diff --git a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/enums/SignUrlType.java b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/enums/SignUrlEndpoint.java similarity index 74% rename from ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/enums/SignUrlType.java rename to ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/enums/SignUrlEndpoint.java index 23f41a9f..2b474b29 100644 --- a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/enums/SignUrlType.java +++ b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/enums/SignUrlEndpoint.java @@ -3,6 +3,6 @@ package cn.axzo.nanopart.ess.api.enums; /** * @author yanglin */ -public enum SignUrlType { +public enum SignUrlEndpoint { PC, WEIXIN_APP } diff --git a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/mq/EssContractStateChangeMessage.java b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/mq/EssContractStateChangeMessage.java index 2da74273..78f502ac 100644 --- a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/mq/EssContractStateChangeMessage.java +++ b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/mq/EssContractStateChangeMessage.java @@ -12,7 +12,7 @@ import lombok.Setter; public class EssContractStateChangeMessage extends MqMessage { /** - * 合同信息 + * 合同信息, 处理签署详情时需要做幂等 */ private EssContractInfo contract; diff --git a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/CreateConsoleLoginUrlRequest.java b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/CreateConsoleLoginUrlRequest.java index 8c7dd8ef..1c4a9ef3 100644 --- a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/CreateConsoleLoginUrlRequest.java +++ b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/CreateConsoleLoginUrlRequest.java @@ -1,5 +1,6 @@ package cn.axzo.nanopart.ess.api.request; +import cn.axzo.nanopart.ess.api.domain.OrgPerson; import cn.axzo.nanopart.ess.api.enums.EssConsoleUrlEndpoint; import lombok.Getter; import lombok.Setter; @@ -11,7 +12,7 @@ import javax.validation.constraints.NotNull; */ @Setter @Getter -public class CreateConsoleLoginUrlRequest { +public class CreateConsoleLoginUrlRequest implements OrgPerson { /** * 单位id @@ -41,6 +42,6 @@ public class CreateConsoleLoginUrlRequest { /** * 调试参数, 不填 */ - private transient boolean checkOperator = true; + private transient boolean essCheckLoginPerson = true; } diff --git a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/CreateContractByFileRequest.java b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/CreateContractByFileRequest.java new file mode 100644 index 00000000..329efd9f --- /dev/null +++ b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/CreateContractByFileRequest.java @@ -0,0 +1,32 @@ +package cn.axzo.nanopart.ess.api.request; + +import cn.axzo.nanopart.ess.api.domain.CreateContractByFile; +import cn.axzo.nanopart.ess.api.domain.CreateContractInfo; +import com.alibaba.fastjson.annotation.JSONField; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +/** + * @author yanglin + */ +@Setter +@Getter +public class CreateContractByFileRequest extends CreateContractRequest { + + /** + * 通过上传PDF发起合同 + */ + @Valid + @NotNull(message = "byFile不能为空") + private CreateContractByFile byFile; + + @JsonIgnore @JSONField(serialize = false) + public CreateContractInfo getContract() { + return byFile; + } + +} \ No newline at end of file diff --git a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/CreateContractRequest.java b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/CreateContractRequest.java index a2fd4299..fb18ff31 100644 --- a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/CreateContractRequest.java +++ b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/CreateContractRequest.java @@ -1,5 +1,9 @@ package cn.axzo.nanopart.ess.api.request; +import cn.axzo.nanopart.ess.api.domain.CreateContractInfo; +import cn.axzo.nanopart.ess.api.domain.OperatorInfo; +import com.alibaba.fastjson.annotation.JSONField; +import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Getter; import lombok.Setter; @@ -10,8 +14,9 @@ import javax.validation.constraints.NotNull; /** * @author yanglin */ -@Setter @Getter -public class CreateContractRequest { +@Setter +@Getter +public abstract class CreateContractRequest { /** * 发起合同的应用或业务场景 @@ -20,40 +25,21 @@ public class CreateContractRequest { private String appCode; /** - * 业务编码 + * 业务编码, 最大长度200. 使用appCode和bizCode的合同不能重复 + *

+ * 如果需要重复发起合同, 需要使用不同的bizCode, 建议: String.format("%s:%s", '真实业务编码', '序号或UUID') */ @NotBlank(message = "bizCode不能为空") private String bizCode; /** - * 发起方信息 + * 合同发起方信息 */ @Valid - @NotNull(message = "operator不能为空") - private OperatorInfo operator; + @NotNull(message = "creator不能为空") + private OperatorInfo creator; - /** - * 通过上传PDF发起合同 - */ - @Valid - @NotNull(message = "byFile不能为空") - private CreateContractByFile byFile; - - @Setter @Getter - public static class OperatorInfo { - - /** - * 发起方单位id - */ - @NotNull(message = "ouId不能为空") - private Long ouId; - - /** - * 发起方人员id - */ - @NotNull(message = "personId不能为空") - private Long personId; - - } + @JsonIgnore @JSONField(serialize = false) + public abstract CreateContractInfo getContract(); } \ No newline at end of file diff --git a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/GetContractSignUrlRequest.java b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/GetContractSignUrlRequest.java index be2c3f6d..9456bce0 100644 --- a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/GetContractSignUrlRequest.java +++ b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/GetContractSignUrlRequest.java @@ -1,7 +1,7 @@ package cn.axzo.nanopart.ess.api.request; import cn.axzo.nanopart.ess.api.domain.OrgPerson; -import cn.axzo.nanopart.ess.api.enums.SignUrlType; +import cn.axzo.nanopart.ess.api.enums.SignUrlEndpoint; import lombok.Getter; import lombok.Setter; @@ -37,6 +37,6 @@ public class GetContractSignUrlRequest implements OrgPerson { * PC: 电脑端, WEIXIN_APP: 小程序 */ @NotNull(message = "urlType不能为空") - private SignUrlType urlType; + private SignUrlEndpoint urlType; } \ No newline at end of file diff --git a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/AssignWeixinAppUrlByOrgRequest.java b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/GetSignUrlRequest.java similarity index 60% rename from ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/AssignWeixinAppUrlByOrgRequest.java rename to ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/GetSignUrlRequest.java index 79625f21..2ab1418f 100644 --- a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/AssignWeixinAppUrlByOrgRequest.java +++ b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/GetSignUrlRequest.java @@ -1,7 +1,7 @@ package cn.axzo.nanopart.ess.api.request; import cn.axzo.nanopart.ess.api.domain.OrgPerson; -import cn.axzo.nanopart.ess.api.enums.SignUrlType; +import cn.axzo.nanopart.ess.api.enums.SignUrlEndpoint; import lombok.Getter; import lombok.Setter; @@ -13,7 +13,7 @@ import javax.validation.constraints.NotNull; */ @Setter @Getter -public class AssignWeixinAppUrlByOrgRequest implements OrgPerson { +public class GetSignUrlRequest implements OrgPerson { /** * 合同id @@ -36,12 +36,15 @@ public class AssignWeixinAppUrlByOrgRequest implements OrgPerson { /** * PC: 电脑端, WEIXIN_APP: 小程序 */ - @NotNull(message = "urlType不能为空") - private SignUrlType urlType; + @NotNull(message = "endpoint不能为空") + private SignUrlEndpoint endpoint; /** - * 签署方编号, 可以使用该编号指定动态签署方的信息, 或未指定将自动分配 - * 该编号在创建合同的时候会返回 + * 签署方编号, 可以使用该编号指定动态签署方的信息, 该编号在创建合同的时候会返回 + *

+ * 如果在创建合同的时候存在任意没有指定具体签署人(Approver#signPersion)的情况下, 该字段必传 + *

+ * 如果在创建合同的时候已经指定了所有的具体签署人(Approver#signPersion), 该字段可以不传 */ private String recipientId; diff --git a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/response/CreateContractByFileResponse.java b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/response/CreateContractByFileResponse.java new file mode 100644 index 00000000..69c7cbaf --- /dev/null +++ b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/response/CreateContractByFileResponse.java @@ -0,0 +1,28 @@ +package cn.axzo.nanopart.ess.api.response; + +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +/** + * @author yanglin + */ +@Setter @Getter +public class CreateContractByFileResponse { + + /** + * 合同id + */ + private String essContractId; + + /** + * 签署方编号, 可以使用该编号指定动态签署方的信息, 顺序和请求中的approvers一致. + *

+ * 比如在使用了动态签署人的情况下, 就需要保存这个字段 + *

+ * 在获取签署链接的时候回传到获取签署链接的请求中, 用以和创建合同的请求中的签署人的其它信息进行匹配 + */ + private List essRecipientIds; + +} \ No newline at end of file diff --git a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/response/CreateContractResponse.java b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/response/CreateContractResponse.java deleted file mode 100644 index 60dd2f04..00000000 --- a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/response/CreateContractResponse.java +++ /dev/null @@ -1,24 +0,0 @@ -package cn.axzo.nanopart.ess.api.response; - -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -/** - * @author yanglin - */ -@Setter @Getter -public class CreateContractResponse { - - /** - * 合同id - */ - private String essContractId; - - /** - * 签署方编号, 可以使用该编号指定动态签署方的信息, 顺序和请求中的approvers一致 - */ - private List essRecipientIds; - -} \ No newline at end of file diff --git a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/response/AssignWeixinAppUrlByOrgResponse.java b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/response/GetSignUrlResponse.java similarity index 84% rename from ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/response/AssignWeixinAppUrlByOrgResponse.java rename to ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/response/GetSignUrlResponse.java index 9f89e12c..304398d6 100644 --- a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/response/AssignWeixinAppUrlByOrgResponse.java +++ b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/response/GetSignUrlResponse.java @@ -8,7 +8,7 @@ import lombok.Setter; */ @Setter @Getter -public class AssignWeixinAppUrlByOrgResponse { +public class GetSignUrlResponse { /** * 有效期为5分钟. diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/dao/EssContractDao.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/dao/EssContractDao.java index cd1cdc53..478a9868 100644 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/dao/EssContractDao.java +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/dao/EssContractDao.java @@ -20,24 +20,21 @@ import java.util.Optional; @Repository("essContractDao") public class EssContractDao extends ServiceImpl { - public Optional findByBizCode(String appCode, String bizCode) { - return lambdaQuery() - .eq(EssContract::getAppCode, appCode) - .eq(EssContract::getBizCode, bizCode) - .oneOpt(); - } - public EssContract getOrThrow(String essContractId) { - EssContract contract = find(essContractId).orElse(null); + EssContract contract = findOrNull(essContractId); BizAssertions.assertNotNull(contract, "找不到合同信息, essContractId={}", essContractId); return contract; } - public Optional find(String essContractId) { - return find(essContractId, false); + public EssContract findOrNull(String essContractId) { + return find(essContractId, false).orElse(null); } - public Optional find(String essContractId, boolean forUpdate) { + public EssContract findForUpdateOrNull(String essContractId) { + return find(essContractId, true).orElse(null); + } + + private Optional find(String essContractId, boolean forUpdate) { return lambdaQuery() .eq(EssContract::getEssContractId, essContractId) .last(forUpdate, "FOR UPDATE") @@ -68,13 +65,6 @@ public class EssContractDao extends ServiceImpl .update(); } - public void updateAssignedApprovers(EssContract contract) { - lambdaUpdate() - .eq(EssContract::getId, contract.getId()) - .set(EssContract::getAssignedApprovers, JSON.toJSONString(contract.getAssignedApprovers())) - .update(); - } - public void setOssInfo(EssContract contract, String fileKey) { lambdaUpdate() .eq(EssContract::getId, contract.getId()) diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/dao/EssLogDao.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/dao/EssLogDao.java index e40d05cc..e8655e6f 100644 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/dao/EssLogDao.java +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/dao/EssLogDao.java @@ -2,6 +2,7 @@ package cn.axzo.nanopart.ess.server.dao; import cn.axzo.nanopart.ess.server.entity.EssLog; import cn.axzo.nanopart.ess.server.mapper.EssLogMapper; +import cn.axzo.nanopart.ess.server.utils.BizAssertions; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.stereotype.Repository; @@ -10,4 +11,23 @@ import org.springframework.stereotype.Repository; */ @Repository("essLogDao") public class EssLogDao extends ServiceImpl { -} + + public void logRequest(String context, Object subject, Object request) { + log(context, subject, "request", request); + } + + public void log(String context, Object subject, Object... logContents) { + EssLog log = new EssLog(); + log.setContext(context); + log.setSubject(String.valueOf(subject)); + if (logContents != null && logContents.length > 0) { + BizAssertions.assertTrue(logContents.length % 2 == 0, "logContents must be even"); + for (int i = 0; i < logContents.length; i += 2) { + BizAssertions.assertTrue(logContents[i] instanceof String, "logContents key must be String"); + log.addLogContent((String) logContents[i], logContents[i + 1]); + } + } + save(log); + } + +} \ No newline at end of file diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/dao/EssOrgDao.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/dao/EssOrgDao.java index 3dbcb3cd..cc493cb1 100644 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/dao/EssOrgDao.java +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/dao/EssOrgDao.java @@ -24,11 +24,15 @@ public class EssOrgDao extends ServiceImpl { return lambdaQuery().in(EssOrg::getOuId, ouIds).list(); } - public Optional find(Long ouId) { - return find(ouId, false); + public EssOrg findOrNull(Long ouId) { + return find(ouId, false).orElse(null); } - public Optional find(Long ouId, boolean forUpdate) { + public EssOrg findForUpdateOrNull(Long ouId) { + return find(ouId, true).orElse(null); + } + + private Optional find(Long ouId, boolean forUpdate) { return lambdaQuery() .eq(EssOrg::getOuId, ouId) .last(forUpdate, "FOR UPDATE") diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/dao/EssPersonDao.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/dao/EssPersonDao.java index e087b293..e8e887fa 100644 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/dao/EssPersonDao.java +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/dao/EssPersonDao.java @@ -20,18 +20,18 @@ import java.util.Optional; public class EssPersonDao extends ServiceImpl { public EssPerson findOrNull(OrgPerson person) { - return find(person.getOuId(), person.getPersonId(), false).orElse(null); + return findOrNull(person.getOuId(), person.getPersonId()); } public EssPerson findOrNull(Long ouId, Long personId) { return find(ouId, personId, false).orElse(null); } - public Optional find(Long ouId, Long personId) { - return find(ouId, personId, false); + public Optional find(OrgPerson person, boolean forUpdate) { + return find(person.getOuId(), person.getPersonId(), forUpdate); } - public Optional find(Long ouId, Long personId, boolean forUpdate) { + private Optional find(Long ouId, Long personId, boolean forUpdate) { return lambdaQuery() .eq(EssPerson::getOuId, ouId) .eq(EssPerson::getPersonId, personId) diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/dao/EssSealDao.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/dao/EssSealDao.java index 17eb8625..2392979f 100644 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/dao/EssSealDao.java +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/dao/EssSealDao.java @@ -1,14 +1,11 @@ package cn.axzo.nanopart.ess.server.dao; import cn.axzo.nanopart.ess.api.enums.EssSealState; -import cn.axzo.nanopart.ess.api.enums.EssSealType; import cn.axzo.nanopart.ess.server.entity.EssSeal; import cn.axzo.nanopart.ess.server.mapper.EssSealMapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Repository; -import java.util.List; import java.util.Optional; /** @@ -30,10 +27,4 @@ public class EssSealDao extends ServiceImpl { .update(); } - public List getByOrgAndSealTypes(Long ouId, List sealTypes) { - return lambdaQuery() - .eq(EssSeal::getOuId, ouId) - .in(CollectionUtils.isNotEmpty(sealTypes), EssSeal::getType, sealTypes) - .list(); - } } \ No newline at end of file diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/dao/EssSealPersonDao.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/dao/EssSealPersonDao.java index 0152b53c..27c20f11 100644 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/dao/EssSealPersonDao.java +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/dao/EssSealPersonDao.java @@ -1,7 +1,6 @@ package cn.axzo.nanopart.ess.server.dao; import cn.axzo.nanopart.ess.api.utils.YesOrNo; -import cn.axzo.nanopart.ess.server.dao.domain.OuAndPersonId; import cn.axzo.nanopart.ess.server.entity.EssSealPerson; import cn.axzo.nanopart.ess.server.mapper.EssSealPersonMapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; @@ -10,6 +9,7 @@ import org.springframework.stereotype.Repository; import java.util.Collection; import java.util.Collections; +import java.util.Date; import java.util.List; import java.util.Optional; @@ -26,6 +26,7 @@ public class EssSealPersonDao extends ServiceImpl 0, EssSealPerson::getAuthorizedByPersonId, authorizedByPersonId) .set(EssSealPerson::getIsAuthorized, YesOrNo.YES) + .set(EssSealPerson::getAuthorizeTime, new Date()) .update(); } @@ -37,13 +38,6 @@ public class EssSealPersonDao extends ServiceImpl findBySealAndPersonId(String essSealId, Long personId) { - return lambdaQuery() - .eq(EssSealPerson::getEssSealId, essSealId) - .eq(EssSealPerson::getPersonId, personId) - .oneOpt(); - } - public List getBySealAndPersonIds(String essSealId, Collection personIds) { if (CollectionUtils.isEmpty(personIds)) return Collections.emptyList(); @@ -53,19 +47,6 @@ public class EssSealPersonDao extends ServiceImpl getByOuAndPersonIds(Collection ouAndPersonIds) { - if (CollectionUtils.isEmpty(ouAndPersonIds)) - return Collections.emptyList(); - return lambdaQuery() - .nested(w -> { - for (OuAndPersonId ouAndPersonId : ouAndPersonIds) { - w.or().eq(EssSealPerson::getOuId, ouAndPersonId.getOuId()) - .eq(EssSealPerson::getPersonId, ouAndPersonId.getPersonId()); - } - }) - .list(); - } - public Optional find(String essSealId, Long personId) { return find(essSealId, personId, false); } diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/EssContract.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/EssContract.java index 364c056e..c02f7ab4 100644 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/EssContract.java +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/EssContract.java @@ -3,20 +3,20 @@ package cn.axzo.nanopart.ess.server.entity; import cn.axzo.foundation.dao.support.mysql.type.BaseListTypeHandler; import cn.axzo.nanopart.ess.api.domain.contract.Approver; import cn.axzo.nanopart.ess.api.domain.contract.EssApproveDetail; +import cn.axzo.nanopart.ess.api.enums.Constraint; import cn.axzo.nanopart.ess.api.enums.EssContractState; -import cn.axzo.nanopart.ess.api.utils.YesOrNo; -import cn.axzo.nanopart.ess.api.enums.ApproverAssignType; -import cn.axzo.nanopart.ess.server.entity.domain.AssignedApprovers; +import cn.axzo.nanopart.ess.server.entity.domain.Assignment; import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; import com.alibaba.fastjson.JSON; +import com.alibaba.nacos.common.utils.UuidUtils; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler; import lombok.Getter; import lombok.Setter; -import javax.swing.table.DefaultTableCellRenderer; import java.util.List; +import java.util.Optional; /** * @author yanglin @@ -34,7 +34,7 @@ public class EssContract extends BaseEntity { /** * 业务编码 */ - private String bizCode; + private String bizCode = UuidUtils.generateUuid(); /** * 合同发起方单位id @@ -67,11 +67,6 @@ public class EssContract extends BaseEntity { */ private EssContractState state; - /** - * 是否为动态签署人 - */ - private YesOrNo isDynamicApprover; - /** * 合并签署方信息 */ @@ -79,10 +74,10 @@ public class EssContract extends BaseEntity { private List approvers; /** - * 动态签署人信息 + * 签署信息(扩展字段) */ @TableField(typeHandler = FastjsonTypeHandler.class) - private AssignedApprovers assignedApprovers; + private Assignment assignment; /** * 签署方的签署情况 @@ -101,27 +96,49 @@ public class EssContract extends BaseEntity { @TableField(typeHandler = FastjsonTypeHandler.class) private RecordExt recordExt; - public boolean isDynamicApprover() { - return isDynamicApprover.isYes(); + public Assignment getOrCreateAssignment() { + if (assignment == null) + assignment = new Assignment(); + return assignment; } public boolean isOrgSigned(Long ouId) { if (approveDetails == null) return false; return approveDetails.stream() - .anyMatch(detail -> detail.getOuId().equals(ouId)); + .anyMatch(detail -> ouId.equals(detail.getOuId())); } - public int preciseApproverSize() { - return approvers.size(); + public int approverSize() { + return approvers == null ? 0 : approvers.size(); } - public Approver getPreciseApprover(Integer idx) { - return approvers.get(idx); + public Approver getApprover(Integer idx) { + return approvers == null ? null : approvers.get(idx); } - public ApproverAssignType getAssignType() { - return assignedApprovers == null ? null : assignedApprovers.getAssignType(); + public Optional findApproverByRecipientId(String recipientId) { + if (approvers == null) + return Optional.empty(); + return approvers.stream() + .filter(approver -> approver.getEssRecipientId().equals(recipientId)) + .findFirst(); + } + + public Constraint getAssignType() { + return assignment == null ? null : assignment.getConstraint(); + } + + public boolean isAllSignPersonPreset() { + return approvers.stream().allMatch(Approver::isSignPersonPresent); + } + + public boolean isFinalState() { + return state.isFinalState(); + } + + public Constraint getConstraint() { + return assignment == null ? null : assignment.getConstraint(); } @Override diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/EssOrg.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/EssOrg.java index 8e42385f..005b6f95 100644 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/EssOrg.java +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/EssOrg.java @@ -48,6 +48,10 @@ public class EssOrg extends BaseEntity { @TableField(typeHandler = FastjsonTypeHandler.class) private RecordExt recordExt; + public boolean isAuthorized() { + return isAuthorized.isYes(); + } + @Override public String toString() { return JSON.toJSONString(this); diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/EssSealPerson.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/EssSealPerson.java index 774ee15b..d34233c8 100644 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/EssSealPerson.java +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/EssSealPerson.java @@ -10,6 +10,8 @@ import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler; import lombok.Getter; import lombok.Setter; +import java.util.Date; + /** * @author yanglin */ @@ -43,12 +45,21 @@ public class EssSealPerson extends BaseEntity implements OrgPerso */ private Long authorizedByPersonId; + /** + * 授权时间 + */ + private Date authorizeTime; + /** * 扩展字段 */ @TableField(typeHandler = FastjsonTypeHandler.class) private RecordExt recordExt; + public boolean isAuthorized() { + return isAuthorized.isYes(); + } + @Override public String toString() { return JSON.toJSONString(this); diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/domain/AssignedApprovers.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/domain/AssignedApprovers.java deleted file mode 100644 index 29c954f4..00000000 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/domain/AssignedApprovers.java +++ /dev/null @@ -1,18 +0,0 @@ -package cn.axzo.nanopart.ess.server.entity.domain; - -import cn.axzo.nanopart.ess.api.enums.ApproverAssignType; -import lombok.Getter; -import lombok.Setter; - -/** - * @author yanglin - */ -@Setter -@Getter -public class AssignedApprovers { - - private ApproverAssignType assignType; - - private SignByOrg signByOrg = new SignByOrg(); - -} \ No newline at end of file diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/domain/Assignment.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/domain/Assignment.java new file mode 100644 index 00000000..bcc254b7 --- /dev/null +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/domain/Assignment.java @@ -0,0 +1,16 @@ +package cn.axzo.nanopart.ess.server.entity.domain; + +import cn.axzo.nanopart.ess.api.enums.Constraint; +import lombok.Getter; +import lombok.Setter; + +/** + * @author yanglin + */ +@Setter +@Getter +public class Assignment { + + private Constraint constraint; + +} \ No newline at end of file diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/domain/SignByOrg.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/domain/SignByOrg.java deleted file mode 100644 index cf5ba768..00000000 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/domain/SignByOrg.java +++ /dev/null @@ -1,74 +0,0 @@ -package cn.axzo.nanopart.ess.server.entity.domain; - -import cn.axzo.basics.common.exception.ServiceException; -import cn.axzo.nanopart.ess.api.domain.contract.Approver; -import cn.axzo.nanopart.ess.server.utils.BizAssertions; -import lombok.Getter; -import lombok.Setter; - -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -/** - * @author yanglin - */ -@Setter @Getter -public class SignByOrg { - - private final List recipientIds = new ArrayList<>(); - - public Approver getApproverOrThrow(List approvers, String recipientId) { - return approvers.stream() - .filter(a -> a.getEssRecipientId().equals(recipientId)) - .findFirst() - .orElseThrow(InternalError::new); - } - - public String assign(List approvers, Long ouId, @Nullable String recipientId) { - if (recipientId != null) { - OrgRecipientId saved = findByRecipientId(recipientId).orElse(null); - if (saved != null) - BizAssertions.assertTrue(saved.getOuId().equals(ouId), "签署编号已被其它单位占用"); - else - addRecipientId(ouId, recipientId); - return recipientId; - } else { - OrgRecipientId assigned = findByOuId(ouId).orElse(null); - if (assigned != null) - return assigned.getRecipientId(); - for (Approver approver : approvers) { - if (isRecipientIdUsed(approver.getEssRecipientId())) - continue; - addRecipientId(ouId, approver.getEssRecipientId()); - return approver.getEssRecipientId(); - } - throw new ServiceException("签署编号已经用完, 无法将指定新的单位"); - } - } - - public boolean isRecipientIdUsed(String recipientId) { - return findByRecipientId(recipientId).isPresent(); - } - - public Optional findByRecipientId(String recipientId) { - return recipientIds.stream() - .filter(r -> r.getRecipientId().equals(recipientId)) - .findFirst(); - } - - public Optional findByOuId(Long ouId) { - return recipientIds.stream() - .filter(id -> id.getOuId().equals(ouId)) - .findFirst(); - } - - public void addRecipientId(Long ouId, String recipientId) { - recipientIds.add(new OrgRecipientId() {{ - setOuId(ouId); - setRecipientId(recipientId); - }}); - } - -} \ No newline at end of file diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/ContractManager.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/ContractManager.java index f3ffa11b..4cbbcbde 100644 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/ContractManager.java +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/ContractManager.java @@ -1,44 +1,30 @@ package cn.axzo.nanopart.ess.server.ess; import cn.axzo.basics.common.exception.ServiceException; -import cn.axzo.nanopart.ess.api.domain.OrgPerson; -import cn.axzo.nanopart.ess.api.domain.contract.Approver; import cn.axzo.nanopart.ess.api.domain.contract.EssApproveDetail; -import cn.axzo.nanopart.ess.api.enums.ApproverAssignType; import cn.axzo.nanopart.ess.api.enums.EssContractState; -import cn.axzo.nanopart.ess.api.enums.EssSealType; -import cn.axzo.nanopart.ess.api.request.AssignWeixinAppUrlByOrgRequest; -import cn.axzo.nanopart.ess.api.request.CreateContractRequest; +import cn.axzo.nanopart.ess.api.request.CreateContractByFileRequest; import cn.axzo.nanopart.ess.api.request.RevokeContractRequest; -import cn.axzo.nanopart.ess.api.response.CreateContractResponse; +import cn.axzo.nanopart.ess.api.response.CreateContractByFileResponse; import cn.axzo.nanopart.ess.server.dao.EssContractDao; -import cn.axzo.nanopart.ess.server.dao.EssOrgDao; -import cn.axzo.nanopart.ess.server.dao.EssPersonDao; -import cn.axzo.nanopart.ess.server.dao.EssSealDao; -import cn.axzo.nanopart.ess.server.dao.EssSealPersonDao; -import cn.axzo.nanopart.ess.server.dao.domain.OuAndPersonId; +import cn.axzo.nanopart.ess.server.dao.EssLogDao; import cn.axzo.nanopart.ess.server.entity.EssContract; -import cn.axzo.nanopart.ess.server.entity.EssOrg; import cn.axzo.nanopart.ess.server.entity.EssPerson; -import cn.axzo.nanopart.ess.server.entity.EssSeal; -import cn.axzo.nanopart.ess.server.entity.EssSealPerson; import cn.axzo.nanopart.ess.server.ess.domain.JsonObjectAsString; -import cn.axzo.nanopart.ess.server.ess.domain.SealPersons; -import cn.axzo.nanopart.ess.server.ess.support.EssSupport; +import cn.axzo.nanopart.ess.server.ess.support.ContractSupport; import cn.axzo.nanopart.ess.server.utils.BizAssertions; -import com.alibaba.fastjson.JSON; import com.tencentcloudapi.common.exception.TencentCloudSDKException; +import com.tencentcloudapi.essbasic.v20210526.models.ApproverItem; import com.tencentcloudapi.essbasic.v20210526.models.ChannelCreateFlowByFilesResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.collections4.CollectionUtils; -import org.springframework.dao.DuplicateKeyException; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import java.util.Arrays; import java.util.List; +import static cn.axzo.nanopart.ess.server.utils.BizAssertions.fail; import static java.util.stream.Collectors.toList; /** @@ -53,90 +39,53 @@ public class ContractManager { private final OrgManager orgManager; private final EssClient essClient; - private final EssOrgDao essOrgDao; private final EssContractDao essContractDao; - private final Broadcaster broadcaster; - private final EssPersonDao essPersonDao; - private final EssSealDao essSealDao; - private final EssSealPersonDao essSealPersonDao; - private final EssSupport essSupport; + private final EssBroadcaster essBroadcaster; + private final ContractSupport contractSupport; + private final EssLogDao essLogDao; - public CreateContractResponse createContract(CreateContractRequest request) { - checkPermission(request); - EssContract contract = saveContract(request); + @Transactional + public CreateContractByFileResponse + createContractByFile(CreateContractByFileRequest request) { + essLogDao.logRequest("createContractByFile", request.getBizCode(), request); + contractSupport.validateCreateContract(request.getContract()); + EssContract contract = contractSupport.saveContractByFile(request); try { - EssPerson superAdmin = orgManager.getSuperAdminOrThrow(request.getOperator().getOuId()); - List essFileIds = essClient.uploadDocument( - superAdmin, request.getByFile().getBase64Files()); + EssPerson superAdmin = orgManager.getSuperAdminOrThrow(request.getCreator().getOuId()); + List essFileIds = essClient.uploadDocument(superAdmin, request.getByFile().getBase64Files()); ChannelCreateFlowByFilesResponse essResponse = essClient.createContractByFile( superAdmin, essFileIds, request.getByFile(), JsonObjectAsString.create().put(CONTRACT_ID, contract.getId()).toString()); - List essRecipientIds = EssSupport.collectRecipientIds(essResponse.getApprovers()); - for (int i = 0; i < contract.preciseApproverSize(); i++) { - Approver approver = contract.getPreciseApprover(i); - approver.setEssRecipientId(essRecipientIds.get(i)); - } + List essRecipientIds = Arrays.stream(essResponse.getApprovers()) + .map(ApproverItem::getRecipientId) + .collect(toList()); + for (int i = 0; i < contract.approverSize(); i++) + contract.getApprover(i).setEssRecipientId(essRecipientIds.get(i)); essContractDao.setEssContractCreated( contract.getId(), essResponse.getFlowId(), essFileIds, contract.getApprovers()); contract = essContractDao.getOrThrow(essResponse.getFlowId()); - broadcaster.fireContractStateChanged(contract); - CreateContractResponse response = new CreateContractResponse(); + essBroadcaster.fireContractStateChanged(contract); + CreateContractByFileResponse response = new CreateContractByFileResponse(); response.setEssContractId(essResponse.getFlowId()); response.setEssRecipientIds(essRecipientIds); return response; - } catch (TencentCloudSDKException e) { + } catch (TencentCloudSDKException | ServiceException e) { log.warn("创建合同失败", e); if (contract != null) essContractDao.removeById(contract.getId()); - throw new ServiceException("创建合同失败: " + e.getMessage()); - } - } - - private void checkPermission(CreateContractRequest request) { - BizAssertions.assertNotNull(request.getByFile(), "合同PDF文件不能为空"); - List orgs = essOrgDao.getByOuIds(request.getByFile().getApproverOuIds()); - List notAuthorizedOrgs = orgs.stream() - .filter(i -> i.getIsAuthorized().isNo()) - .map(EssOrg::getOuName) - .collect(toList()); - BizAssertions.assertEmpty(notAuthorizedOrgs, - "创建合同失败. 以下单位还未认证: {}", JSON.toJSONString(notAuthorizedOrgs)); - SealPersons sealPersons = SealPersons.wrap(getApproverAsSealPersons(request)); - for (Approver approver : request.getByFile().getApprovers()) { - if (approver.getSignPerson() == null) continue; - EssSealPerson sealPerson = sealPersons.find( - approver.getOuId(), approver.getPersonId()).orElse(null); - BizAssertions.assertFalse(sealPerson == null || sealPerson.getIsAuthorized().isNo(), - "创建合同失败. 部分签署人员没有印章授权, ouId={}, personId={}", approver.getOuId(), approver.getPersonId()); - } - } - - private List getApproverAsSealPersons(CreateContractRequest request) { - List ouAndPersonIds = request.getByFile().getApprovers().stream() - .map(approver -> OuAndPersonId.create(approver.getOuId(), approver.getPersonId())) - .distinct() - .collect(toList()); - return essSealPersonDao.getByOuAndPersonIds(ouAndPersonIds); - } - - private EssContract saveContract(CreateContractRequest request) { - try { - EssContract contract = essSupport.createContract(request); - essContractDao.save(contract); - return contract; - } catch (DuplicateKeyException e) { - throw new ServiceException("创建合同失败. 重复的业务编号: " + request.getBizCode()); + throw fail("创建合同失败: {}", e.getMessage()); } } @Transactional public void revokeContract(RevokeContractRequest request) { + essLogDao.logRequest("revokeContract", request.getEssContractId(), request); EssContract contract = essContractDao.getOrThrow(request.getEssContractId()); - BizAssertions.assertFalse(contract.getState().isFinalState(), - "合同已是最终状态[{}], 无法撤销", contract.getState().getDescription()); - EssPerson contractCreator = getContractCreatorOrThrow(contract); - essClient.revokeContract(contractCreator, contract.getEssContractId(), request.getReason()); + BizAssertions.assertFalse(contract.isFinalState(), + "合同已是最终状态 {}, 无法撤销", contract.getState().getDescription()); + EssPerson superAdmin = getContractSuperAdmin(contract); + essClient.revokeContract(superAdmin, contract.getEssContractId(), request.getReason()); updateContractState(contract, EssContractState.CANCEL, null); } @@ -144,61 +93,19 @@ public class ContractManager { public void updateContractState(EssContract contract, EssContractState state, List approveDetails) { - EssContract reloadContract = essContractDao - .find(contract.getEssContractId(), true) - .orElse(null); + EssContract reloadContract = essContractDao.findForUpdateOrNull(contract.getEssContractId()); BizAssertions.assertNotNull(reloadContract, "合同不存在: {}", contract.getEssContractId()); - //noinspection DataFlowIssue - if (reloadContract.getState().isFinalState()) { - log.warn("合同[{}]已是最终状态[{}], 无法更新状态", - reloadContract.getEssContractId(), - reloadContract.getState().getDescription()); + if (reloadContract.isFinalState()) { + log.warn("合同[{}]已是最终状态[{}], 无法更新状态至{}", + reloadContract.getEssContractId(), reloadContract.getState(), state); } else { essContractDao.updateState(contract, state, approveDetails); - broadcaster.fireContractStateChanged(contract); + essBroadcaster.fireContractStateChanged(contract); } } - @Transactional - public String assignRecipientIdByOrg(AssignWeixinAppUrlByOrgRequest request) { - EssContract contract = essContractDao.find(request.getEssContractId(), true).orElse(null); - BizAssertions.assertNotNull(contract, "找不到合同信息, essContractId={}", request.getEssContractId()); - //noinspection DataFlowIssue - BizAssertions.assertEquals(ApproverAssignType.ONE_PERSON_PER_ORG, contract.getAssignType(), "签署方式不正确"); - String recipientId = contract.getAssignedApprovers() - .getSignByOrg() - .assign(contract.getApprovers(), request.getOuId(), request.getRecipientId()); - Approver approver = contract.getAssignedApprovers() - .getSignByOrg() - .getApproverOrThrow(contract.getApprovers(), recipientId); - checkApproverSeals(request, approver.getSealTypes()); - essContractDao.updateAssignedApprovers(contract); - return recipientId; - } - - public void checkApproverSeals(OrgPerson person, List sealTypes) { - if (CollectionUtils.isEmpty(sealTypes)) - sealTypes = Arrays.stream(EssSealType.values()).collect(toList()); - List seals = essSealDao - .getByOrgAndSealTypes(person.getOuId(), sealTypes); - BizAssertions.assertNotEmpty(seals, "单位没有指定类型的印章"); - boolean found = false; - for (EssSeal seal : seals) { - EssSealPerson sealPerson = essSealPersonDao - .findBySealAndPersonId(seal.getEssSealId(), person.getPersonId()) - .orElse(null); - found = sealPerson != null; - if (found) - break; - } - BizAssertions.assertTrue(found, "签署人员没有指定类型的印章"); - } - - public EssPerson getContractCreatorOrThrow(EssContract contract) { - EssPerson creator = essPersonDao.findOrNull( - contract.getCreatorOuId(), contract.getCreatorPersonId()); - BizAssertions.assertNotNull(creator, "找不到合同发起人信息"); - return creator; + public EssPerson getContractSuperAdmin(EssContract contract) { + return orgManager.getSuperAdminOrThrow(contract.getCreatorOuId()); } } \ No newline at end of file diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/Broadcaster.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/EssBroadcaster.java similarity index 88% rename from ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/Broadcaster.java rename to ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/EssBroadcaster.java index 6bf70982..cddc45d9 100644 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/Broadcaster.java +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/EssBroadcaster.java @@ -16,14 +16,15 @@ import org.springframework.stereotype.Component; */ @Component @RequiredArgsConstructor -class Broadcaster { +class EssBroadcaster { private final EssContractDao essContractDao; protected final EventProducer eventProducer; void fireContractStateChanged(EssContract contract) { - EssContract reloadContract = essContractDao - .find(contract.getEssContractId()).orElse(null); + EssContract reloadContract = essContractDao.findOrNull(contract.getEssContractId()); + if (reloadContract == null) + return; EssContractStateChangeMessage message = new EssContractStateChangeMessage(); message.setContract(BeanMapper.copyBean(reloadContract, EssContractInfo.class)); eventProducer.send(Event.builder() diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/EssClient.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/EssClient.java index eeeb670e..274a789a 100644 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/EssClient.java +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/EssClient.java @@ -1,13 +1,12 @@ package cn.axzo.nanopart.ess.server.ess; -import cn.axzo.basics.common.exception.ServiceException; import cn.axzo.basics.profiles.dto.basic.PersonProfileDto; import cn.axzo.maokai.api.vo.response.OrganizationalUnitVO; +import cn.axzo.nanopart.ess.api.domain.CreateContractInfo; import cn.axzo.nanopart.ess.api.domain.OrgPerson; import cn.axzo.nanopart.ess.api.domain.contract.Approver; import cn.axzo.nanopart.ess.api.enums.EssConsoleUrlEndpoint; import cn.axzo.nanopart.ess.api.enums.EssEmbedType; -import cn.axzo.nanopart.ess.api.request.CreateContractInfo; import cn.axzo.nanopart.ess.api.utils.YesOrNo; import cn.axzo.nanopart.ess.server.dao.EssLogDao; import cn.axzo.nanopart.ess.server.entity.EssLog; @@ -35,7 +34,6 @@ import com.tencentcloudapi.essbasic.v20210526.models.ApproverOption; import com.tencentcloudapi.essbasic.v20210526.models.ChannelCancelFlowRequest; import com.tencentcloudapi.essbasic.v20210526.models.ChannelCreateEmbedWebUrlRequest; import com.tencentcloudapi.essbasic.v20210526.models.ChannelCreateEmbedWebUrlResponse; -import com.tencentcloudapi.essbasic.v20210526.models.ChannelCreateFlowApproversRequest; import com.tencentcloudapi.essbasic.v20210526.models.ChannelCreateFlowByFilesRequest; import com.tencentcloudapi.essbasic.v20210526.models.ChannelCreateFlowByFilesResponse; import com.tencentcloudapi.essbasic.v20210526.models.ChannelCreateOrganizationBatchSignUrlRequest; @@ -51,7 +49,6 @@ import com.tencentcloudapi.essbasic.v20210526.models.CreateSignUrlsRequest; import com.tencentcloudapi.essbasic.v20210526.models.CreateSignUrlsResponse; import com.tencentcloudapi.essbasic.v20210526.models.DescribeResourceUrlsByFlowsRequest; import com.tencentcloudapi.essbasic.v20210526.models.DescribeResourceUrlsByFlowsResponse; -import com.tencentcloudapi.essbasic.v20210526.models.FillApproverInfo; import com.tencentcloudapi.essbasic.v20210526.models.FlowApproverInfo; import com.tencentcloudapi.essbasic.v20210526.models.ProxyOrganizationOperator; import com.tencentcloudapi.essbasic.v20210526.models.SyncProxyOrganizationOperatorsRequest; @@ -62,6 +59,7 @@ import com.tencentcloudapi.essbasic.v20210526.models.UserInfo; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @@ -81,6 +79,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import static cn.axzo.nanopart.ess.server.ess.EssClient.Func.func; +import static cn.axzo.nanopart.ess.server.utils.BizAssertions.fail; import static java.util.stream.Collectors.toList; /** @@ -104,8 +103,8 @@ public class EssClient implements InitializingBean { public String createConsoleLoginUrl(EssOrg org, EssPerson person, EssConsoleUrlEndpoint endpoint, - boolean checkOperator) { - PersonProfileDto personProfile = essSupport.getPersonProfileOrThrow(person.getPersonId()); + boolean essCheckLoginPerson) { + PersonProfileDto personProfile = essSupport.getPersonProfileOrThrow(person); CreateConsoleLoginUrlRequest request = new CreateConsoleLoginUrlRequest(); request.setEndpoint(endpoint.getCode()); request.setEndpoint(request.getEndpoint()); @@ -114,7 +113,7 @@ public class EssClient implements InitializingBean { if (props.isTestEnv()) orgName = orgName.replaceAll("中国", "zhongguo"); request.setProxyOrganizationName(orgName); - if (checkOperator) { + if (essCheckLoginPerson) { request.setProxyOperatorName(personProfile.getRealName()); request.setProxyOperatorMobile(personProfile.getPhone()); } @@ -129,11 +128,11 @@ public class EssClient implements InitializingBean { return response.getConsoleUrl(); } - public String getEmbedWebUrl(EssPerson superAdmin, EssEmbedType embedType, String businessId) { + public String getEmbedWebUrl(EssPerson contextPerson, EssEmbedType embedType, String businessId) { if (embedType.isBusinessIdRequired()) BizAssertions.assertNotBlank(businessId, "内嵌类型为{}时业务ID不能为空", embedType.name()); ChannelCreateEmbedWebUrlRequest request = new ChannelCreateEmbedWebUrlRequest(); - request.setAgent(agent(superAdmin)); + request.setAgent(agent(contextPerson)); request.setBusinessId(businessId); request.setEmbedType(embedType.getEssCode()); request.setHiddenComponents(true); @@ -211,28 +210,25 @@ public class EssClient implements InitializingBean { List contractFileIds, CreateContractInfo contract, String customerData) throws TencentCloudSDKException { - OrgProfiles orgProfiles = OrgProfiles.empty(); - PersonProfiles personProfiles = PersonProfiles.empty(); - if (!contract.isDynamicApprover()) { - orgProfiles = OrgProfiles.wrap(essSupport - .getOrgProfiles(contract.getApprovers().stream() - .map(Approver::getOuId) - .collect(toList()))); - personProfiles = PersonProfiles.wrap(essSupport - .getPersonProfiles(contract.getApprovers().stream() - .map(Approver::getPersonId) - .collect(toList()))); - } + OrgProfiles orgProfiles = OrgProfiles.wrap(essSupport + .getOrgProfiles(contract.getApprovers().stream() + .filter(Approver::isSignPersonPresent) + .map(Approver::getOuId) + .collect(toList()))); + PersonProfiles personProfiles = PersonProfiles.wrap(essSupport + .getPersonProfiles(contract.getApprovers().stream() + .filter(Approver::isSignPersonPresent) + .map(Approver::getPersonId) + .collect(toList()))); ArrayList approvers = new ArrayList<>(); for (Approver approver : contract.getApprovers()) { FlowApproverInfo approverInfo = new FlowApproverInfo(); approvers.add(approverInfo); - if (contract.isDynamicApprover()) { + if (approver.getSignPerson() == null) { ApproverOption approverOption = new ApproverOption(); approverOption.setFillType(1L); approverInfo.setApproverOption(approverOption); } else { - BizAssertions.assertNotNull(approver.getSignPerson(), "非动态签署人合同, 签署人不能为空"); OrganizationalUnitVO orgProfile = orgProfiles.getOrThrow(approver.getOuId()); PersonProfileDto personProfile = personProfiles.getOrThrow(approver.getPersonId()); approverInfo.setName(personProfile.getRealName()); @@ -271,36 +267,9 @@ public class EssClient implements InitializingBean { .command(() -> manage.ChannelCreateFlowByFiles(request))); } - public void assignApprover(String essContractId, - EssPerson contractCreator, - OrgPerson approver, - String recipientId) { - OrganizationalUnitVO orgProfile = essSupport - .getOrgProfileOrThrow(approver.getOuId()); - PersonProfileDto personProfile = essSupport - .getPersonProfileOrThrow(approver.getPersonId()); - FillApproverInfo approverInfo = new FillApproverInfo(); - approverInfo.setRecipientId(recipientId); - approverInfo.setApproverName(personProfile.getRealName()); - approverInfo.setApproverMobile(personProfile.getPhone()); - approverInfo.setOpenId(PersonOpenId.create(approver).toOpenId()); - approverInfo.setOrganizationName(orgProfile.getName()); - approverInfo.setOrganizationOpenId(OrgOpenId.ofPerson(approver).toOpenId()); - ChannelCreateFlowApproversRequest request = new ChannelCreateFlowApproversRequest(); - request.setAgent(agent(contractCreator)); - request.setFlowId(essContractId); - request.setFillApproverType(1L); - request.setApprovers(new FillApproverInfo[]{approverInfo}); - exec(func() - .context("ChannelCreateFlowApprovers") - .subject(essContractId) - .request(request) - .command(() -> manage.ChannelCreateFlowApprovers(request))); - } - - void revokeContract(EssPerson contractCreator, String essContractId, String reason) { + void revokeContract(EssPerson superAdmin, String essContractId, String reason) { ChannelCancelFlowRequest request = new ChannelCancelFlowRequest(); - request.setAgent(agent(contractCreator)); + request.setAgent(agent(superAdmin)); request.setFlowId(essContractId); request.setCancelMessage(reason); exec(func() @@ -335,18 +304,20 @@ public class EssClient implements InitializingBean { return response.getSignUrl(); } - public CreateSignUrlsResponse assignWeixinAppUrlByOrg( - String essContractId, EssPerson creator, + public CreateSignUrlsResponse getWeixinAppSignUrl( + String essContractId, EssPerson superAdmin, OrgPerson signPerson, String recipientId) { CreateSignUrlsRequest request = new CreateSignUrlsRequest(); - request.setAgent(agent(creator)); + request.setAgent(agent(superAdmin)); request.setFlowIds(new String[]{essContractId}); request.setOpenId(PersonOpenId.create(signPerson).toOpenId()); request.setOrganizationOpenId(OrgOpenId.ofPerson(signPerson).toOpenId()); - request.setRecipientIds(new String[]{recipientId}); - request.setGenerateType("RECIPIENT"); + if (StringUtils.isNotBlank(recipientId)) { + request.setRecipientIds(new String[]{recipientId}); + request.setGenerateType("RECIPIENT"); + } return exec(func() - .context("CreateSignUrls:assignWeixinAppUrlByOrg") + .context("CreateSignUrls") .subject(essContractId) .request(request) .command(() -> manage.CreateSignUrls(request))); @@ -375,7 +346,7 @@ public class EssClient implements InitializingBean { .command(() -> manage.SyncProxyOrganizationOperators(request))); } - public ChannelDescribeEmployeesResponse getEmployees(EssPerson superAdmin, EssOrg org, Long offset) { + public ChannelDescribeEmployeesResponse getOrgPerson(EssPerson superAdmin, EssOrg org, Long offset) { ChannelDescribeEmployeesRequest request = new ChannelDescribeEmployeesRequest(); request.setAgent(agent(superAdmin)); request.setOffset(offset); @@ -417,7 +388,7 @@ public class EssClient implements InitializingBean { try { return call(builder); } catch (TencentCloudSDKException e) { - throw new ServiceException(String.format("腾讯返回: %s", e.getMessage()), e); + throw fail(e, String.format("腾讯返回: %s", e.getMessage())); } } diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/EssService.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/EssService.java index 836c3db4..1532b737 100644 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/EssService.java +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/EssService.java @@ -1,16 +1,18 @@ package cn.axzo.nanopart.ess.server.ess; -import cn.axzo.basics.common.exception.ServiceException; -import cn.axzo.nanopart.ess.api.enums.ApproverAssignType; +import cn.axzo.nanopart.ess.api.domain.OrgPerson; +import cn.axzo.nanopart.ess.api.domain.contract.Approver; +import cn.axzo.nanopart.ess.api.enums.Constraint; import cn.axzo.nanopart.ess.api.enums.EssEmbedType.EssContext; -import cn.axzo.nanopart.ess.api.enums.SignUrlType; +import cn.axzo.nanopart.ess.api.enums.SignUrlEndpoint; import cn.axzo.nanopart.ess.api.request.AddSealAuthorizationRequest; -import cn.axzo.nanopart.ess.api.request.AssignWeixinAppUrlByOrgRequest; import cn.axzo.nanopart.ess.api.request.CreateConsoleLoginUrlRequest; import cn.axzo.nanopart.ess.api.request.GetEmbedWebUrlRequest; +import cn.axzo.nanopart.ess.api.request.GetSignUrlRequest; import cn.axzo.nanopart.ess.api.request.RemoveSealAuthorizationRequest; import cn.axzo.nanopart.ess.api.request.SealAndPersonRequest; import cn.axzo.nanopart.ess.server.dao.EssContractDao; +import cn.axzo.nanopart.ess.server.dao.EssLogDao; import cn.axzo.nanopart.ess.server.dao.EssPersonDao; import cn.axzo.nanopart.ess.server.dao.EssSealDao; import cn.axzo.nanopart.ess.server.dao.EssSealPersonDao; @@ -21,6 +23,7 @@ import cn.axzo.nanopart.ess.server.entity.EssSealPerson; import cn.axzo.nanopart.ess.server.ess.domain.OrgAndPerson; import cn.axzo.nanopart.ess.server.ess.domain.SealAndPerson; import cn.axzo.nanopart.ess.server.ess.support.EssProps; +import cn.axzo.nanopart.ess.server.ess.support.EssSupport; import cn.axzo.nanopart.ess.server.ess.support.OssService; import cn.axzo.nanopart.ess.server.utils.BizAssertions; import com.tencentcloudapi.essbasic.v20210526.models.CreateSignUrlsResponse; @@ -29,6 +32,11 @@ import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; +import java.util.function.Function; +import java.util.function.Supplier; + +import static cn.axzo.nanopart.ess.server.utils.BizAssertions.fail; + /** * @author yanglin */ @@ -45,12 +53,15 @@ public class EssService { private final EssPersonDao essPersonDao; private final EssProps essProps; private final OssService ossService; + private final EssLogDao essLogDao; + private final EssSupport essSupport; public String createConsoleLoginUrl(CreateConsoleLoginUrlRequest request) { + essLogDao.logRequest("createConsoleLoginUrl", request.getPersonId(), request); OrgAndPerson orgAndPerson = orgManager.createConsoleLoginUrl(request); return essClient.createConsoleLoginUrl( orgAndPerson.getOrg(), orgAndPerson.getPerson(), - request.getEndpoint(), request.isCheckOperator()); + request.getEndpoint(), request.isEssCheckLoginPerson()); } public String getEmbedWebUrl(GetEmbedWebUrlRequest request) { @@ -58,18 +69,19 @@ public class EssService { if (request.getEmbedType().getEssContext() == EssContext.CONTRACT) { BizAssertions.assertNotNull(request.getBusinessId(), "合同ID不能为空"); EssContract contract = essContractDao.getOrThrow(request.getBusinessId()); - contextPerson = contractManager.getContractCreatorOrThrow(contract); + contextPerson = contractManager.getContractSuperAdmin(contract); } else if (request.getEmbedType().getEssContext() == EssContext.SEAL) { BizAssertions.assertNotNull(request.getBusinessId(), "印章ID不能为空"); contextPerson = orgManager.getSuperAdminOrThrow(request.getOuId()); } else { - return null; + throw fail("不支持的嵌入类型: {}", request.getEmbedType()); } return essClient.getEmbedWebUrl( contextPerson, request.getEmbedType(), request.getBusinessId()); } public void essAddSealAuthorization(AddSealAuthorizationRequest request) { + essLogDao.logRequest("essAddSealAuthorization", request.getPersonId(), request); SealAndPerson sealAndPerson = getSealAndPersonOrThrow(request); EssSealPerson sealPerson = sealAndPerson.getSealPerson(); if (sealPerson.getIsAuthorized().isYes()) @@ -83,6 +95,7 @@ public class EssService { } public void essRemoveSealAuthorization(RemoveSealAuthorizationRequest request) { + essLogDao.logRequest("essRemoveSealAuthorization", request.getPersonId(), request); SealAndPerson sealAndPerson = getSealAndPersonOrThrow(request); if (sealAndPerson.getSealPerson().getIsAuthorized().isNo()) return; @@ -102,35 +115,48 @@ public class EssService { return new SealAndPerson(seal, sealPerson); } - public String assignWeixinAppUrlByOrg(AssignWeixinAppUrlByOrgRequest request) { + public String getContractSignUrl(GetSignUrlRequest request) { + essLogDao.logRequest("getContractSignUrl", request.getEssContractId(), request); EssPerson signPerson = essPersonDao.findOrNull(request); BizAssertions.assertNotNull(signPerson, "当前签署人员未加入单位, 无法签署"); EssContract contract = essContractDao.getOrThrow(request.getEssContractId()); - BizAssertions.assertTrue(contract.isDynamicApprover(), "合同不支持动态签署"); - String recipientId; - if (contract.getAssignedApprovers().getAssignType() == ApproverAssignType.ONE_PERSON_PER_ORG) { + BizAssertions.assertFalse(contract.isFinalState(), + "合同已是最终状态 {}, 无法签署", contract.getState()); + if (contract.getConstraint() == Constraint.ONE_PERSON_PER_ORG) BizAssertions.assertFalse(contract.isOrgSigned(request.getOuId()), "该单位已签署, 无法再次签署"); - recipientId = contractManager.assignRecipientIdByOrg(request); - } else { - throw new ServiceException(String.format("暂不支持的动态签署类型: %s", contract.getAssignedApprovers().getAssignType())); + Function signUrlFun = recipientId -> { + EssPerson superAdmin = contractManager.getContractSuperAdmin(contract); + CreateSignUrlsResponse essResponse = essClient.getWeixinAppSignUrl( + request.getEssContractId(), superAdmin, request, recipientId); + SignUrlInfo signUrlInfo = essResponse.getSignUrlInfos()[0]; + return request.getEndpoint() == SignUrlEndpoint.PC + ? signUrlInfo.getSignQrcodeUrl() + : signUrlInfo.getSignUrl(); + }; + if (contract.isAllSignPersonPreset()) + return signUrlFun.apply(null); + BizAssertions.assertFalse(StringUtils.isBlank(request.getRecipientId()), + "存在动态签署人的情况下, recipientId不能为空"); + Approver approver = contract + .findApproverByRecipientId(request.getRecipientId()) + .orElse(null); + BizAssertions.assertNotNull(approver, "无效的签署编号: {}", request.getRecipientId()); + //noinspection DataFlowIssue + if (approver.getSignPerson() != null) { + BizAssertions.assertTrue(OrgPerson.equals(approver.getSignPerson(), request), "签署人员不匹配"); + return signUrlFun.apply(null); } - EssPerson creator = contractManager.getContractCreatorOrThrow(contract); - CreateSignUrlsResponse essResponse = essClient.assignWeixinAppUrlByOrg( - request.getEssContractId(), creator, request, recipientId); - SignUrlInfo signUrlInfo = essResponse.getSignUrlInfos()[0]; - return request.getUrlType() == SignUrlType.PC - ? signUrlInfo.getSignQrcodeUrl() - : signUrlInfo.getSignUrl(); + return signUrlFun.apply(request.getRecipientId()); } public String getContractPDFUrl(String essContractId) { EssContract contract = essContractDao.getOrThrow(essContractId); if (essProps.isDownloadContractFromOss() - && contract.getState().isFinalState() && StringUtils.isNotBlank(contract.getOssFileKey())) { return ossService.getOssUrl(contract.getOssFileKey()); } - ossService.maybeDownloadToOss(contract, () -> getContractPDFUrlFromEss(contract)); + maybeUploadContractToOss(contract.getEssContractId(), + () -> getContractPDFUrlFromEss(contract)); return getContractPDFUrlFromEss(contract); } @@ -138,4 +164,17 @@ public class EssService { EssPerson creator = orgManager.getSuperAdminOrThrow(contract.getCreatorOuId()); return essClient.getContractPDFUrl(creator, contract.getEssContractId()); } + + public void maybeUploadContractToOss(String essContractId, Supplier essPdfUrl) { + essSupport.asyncExec(() -> { + EssContract effectiveContract = essContractDao.findOrNull(essContractId); + if (!effectiveContract.isFinalState() + || StringUtils.isNotBlank(effectiveContract.getOssFileKey())) + return; + String fileName = String.format("%s.pdf", effectiveContract.getContractName()); + String fileKey = ossService.uploadToOss(essPdfUrl.get(), fileName); + essContractDao.setOssInfo(effectiveContract, fileKey); + }); + } + } \ No newline at end of file diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/OrgManager.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/OrgManager.java index 903a5282..8fdd0f24 100644 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/OrgManager.java +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/OrgManager.java @@ -1,5 +1,6 @@ package cn.axzo.nanopart.ess.server.ess; +import cn.axzo.nanopart.ess.api.domain.OrgPerson; import cn.axzo.nanopart.ess.api.enums.EssPersonState; import cn.axzo.nanopart.ess.api.enums.EssSealState; import cn.axzo.nanopart.ess.api.enums.EssSealType; @@ -50,34 +51,33 @@ public class OrgManager { @Transactional public OrgAndPerson createConsoleLoginUrl(CreateConsoleLoginUrlRequest request) { - EssOrg org = essOrgDao.find(request.getOuId(), true).orElse(null); + EssOrg org = essOrgDao.findForUpdateOrNull(request.getOuId()); if (org == null) { org = essSupport.createOrg(request); essOrgDao.save(org); } - EssPerson person = getOrCreateOrgPerson(request.getOuId(), request.getPersonId()); + EssPerson person = getOrCreateOrgPerson(request); return new OrgAndPerson(org, person); } // !! org and person @Transactional - public void addAuthorizedOrgPerson(Long ouId, Long personId) { - EssPerson person = getOrCreateOrgPerson(ouId, personId); + public void addAuthorizedOrgPerson(OrgPerson orgPerson) { + EssPerson person = getOrCreateOrgPerson(orgPerson); essPersonDao.setState(person, EssPersonState.AUTHORIZED); log.info("add authorized person: {}", person); } - private EssPerson getOrCreateOrgPerson(Long ouId, Long personId) { - EssPerson person = essPersonDao - .find(ouId, personId, true).orElse(null); - if (person == null) { - person = essSupport.createPerson(ouId, personId); - essPersonDao.save(person); - person = essPersonDao.find(ouId, personId, true) + private EssPerson getOrCreateOrgPerson(OrgPerson orgPerson) { + EssPerson essPerson = essPersonDao.find(orgPerson, true).orElse(null); + if (essPerson == null) { + essPerson = essSupport.createPerson(orgPerson); + essPersonDao.save(essPerson); + essPerson = essPersonDao.find(orgPerson, true) .orElseThrow(InternalError::new); } - return person; + return essPerson; } public void setOrgAuthorized(Long ouId, Long authorizePersonId) { @@ -90,14 +90,13 @@ public class OrgManager { public EssPerson getSuperAdminOrThrow(Long ouId) { EssPerson superAdmin = findSuperAdmin(ouId).orElse(null); - BizAssertions.assertNotNull(superAdmin, "单位还未认证: {}", ouId); + BizAssertions.assertNotNull(superAdmin, "单位还未认证"); return superAdmin; } Optional findSuperAdmin(Long ouId) { - EssOrg org = essOrgDao.find(ouId).orElse(null); - BizAssertions.assertNotNull(org, "单位不存在: {}", ouId); - //noinspection DataFlowIssue + EssOrg org = essOrgDao.findOrNull(ouId); + BizAssertions.assertNotNull(org, "单位不存在"); if (org.getSuperAdminPersonId() <= 0L) return Optional.empty(); return Optional.ofNullable(essPersonDao.findOrNull(ouId, org.getSuperAdminPersonId())); diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/PersonResignListener.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/PersonResignListener.java index f9f9cc26..8ea8f6a6 100644 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/PersonResignListener.java +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/PersonResignListener.java @@ -5,7 +5,10 @@ import cn.axzo.framework.rocketmq.Event; import cn.axzo.framework.rocketmq.EventConsumer; import cn.axzo.framework.rocketmq.EventHandler; import cn.axzo.maokai.api.domain.event.user.OrgUserStatusChangedEvent; +import cn.axzo.nanopart.ess.server.dao.EssLogDao; +import cn.axzo.nanopart.ess.server.dao.EssOrgDao; import cn.axzo.nanopart.ess.server.dao.EssPersonDao; +import cn.axzo.nanopart.ess.server.entity.EssOrg; import cn.axzo.nanopart.ess.server.entity.EssPerson; import com.alibaba.fastjson.JSON; import lombok.RequiredArgsConstructor; @@ -38,7 +41,9 @@ public class PersonResignListener extends BaseListener private final EventConsumer eventConsumer; private final EssClient essClient; private final OrgManager orgManager; + private final EssOrgDao essOrgDao; private final EssPersonDao essPersonDao; + private final EssLogDao essLogDao; @Override public void onMessage(MessageExt message) { @@ -57,11 +62,19 @@ public class PersonResignListener extends BaseListener } private void trySetPersonResigned(OrgUserStatusChangedEvent event) { + EssOrg org = essOrgDao.findOrNull(event.getOuId()); + if (org == null) return; EssPerson person = essPersonDao.findOrNull(event.getOuId(), event.getPersonId()); - if (person == null) + if (person == null) { + essLogDao.log("personResigned", event.getPersonId(), + "event", event, "isAuthorized", false); return; + } EssPerson superAdmin = orgManager.getSuperAdminOrThrow(event.getOuId()); - if (superAdmin.getPersonId().equals(event.getPersonId())) { + boolean isSuperAdmin = superAdmin.getPersonId().equals(event.getPersonId()); + essLogDao.log("personResigned", person.getPersonId(), + "event", event, "isSuperAdmin", isSuperAdmin); + if (isSuperAdmin) { log.info("ignore super admin resigned: {}", JSON.toJSONString(person)); return; } diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/QueryService.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/QueryService.java index c9abbd8c..bff3a228 100644 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/QueryService.java +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/QueryService.java @@ -56,8 +56,7 @@ public class QueryService { private final EssSealPersonDao essSealPersonDao; private final EssSupport essSupport; - public List - getEssUnitAuthStates(GetOrgAuthStatesRequest request) { + public List getOrgAuthStates(GetOrgAuthStatesRequest request) { List orgs = essOrgDao.getByOuIds(request.getOuIds()); ArrayList responses = new ArrayList<>(); for (Long ouId : request.getOuIds()) { @@ -118,7 +117,7 @@ public class QueryService { EssOrgAndSealInfo orgAndSeal = new EssOrgAndSealInfo(); essOrgAndSeals.add(orgAndSeal); orgAndSeal.setOrg(BeanMapper.copyBean(org, EssOrgInfo.class)); - orgAndSeal.getOrg().setOrgAuthorized(org.getIsAuthorized().isYes()); + orgAndSeal.getOrg().setOrgAuthorized(org.isAuthorized()); List seals = ouId2Seals.getOrDefault(org.getOuId(), emptyList()); orgAndSeal.setSeals(BeanMapper.copyList(seals, EssSealInfo.class)); for (EssSealInfo sealInfo : orgAndSeal.getSeals()) { @@ -133,14 +132,12 @@ public class QueryService { sealPersonInfo.setSuperAdmin(org.getSuperAdminPersonId().equals(sealPerson.getPersonId())); sealPersonInfo.setSealAuthorized(sealPerson.getIsAuthorized().isYes()); sealPersonInfo.setPersonAuthorized(essPerson.isAuthorized()); - PersonProfileDto personProfile = personProfiles - .find(sealPerson.getPersonId()).orElse(null); - sealPersonInfo.setPersonName( - personProfile == null ? "" : personProfile.getRealName()); - PersonProfileDto authorizedByPersonProfile = personProfiles - .find(sealPerson.getPersonId()).orElse(null); - sealPersonInfo.setAuthorizedByPersonName( - authorizedByPersonProfile == null ? "" : authorizedByPersonProfile.getRealName()); + sealPersonInfo.setAuthorizeTimeMs(sealPerson.getAuthorizeTime() == null + ? null : sealPerson.getAuthorizeTime().getTime()); + PersonProfileDto person = personProfiles.findOrNull(sealPerson.getPersonId()); + sealPersonInfo.setPersonName(person == null ? "" : person.getRealName()); + PersonProfileDto authorizedBy = personProfiles.findOrNull(sealPerson.getPersonId()); + sealPersonInfo.setAuthorizedByPersonName(authorizedBy == null ? "" : authorizedBy.getRealName()); } } } diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/controller/ApiController.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/controller/ApiController.java index c415b29b..6617f172 100644 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/controller/ApiController.java +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/controller/ApiController.java @@ -7,28 +7,26 @@ import cn.axzo.nanopart.ess.api.domain.EssOrgAndSealInfo; import cn.axzo.nanopart.ess.api.domain.contract.EssContractInfo; import cn.axzo.nanopart.ess.api.request.AddSealAuthorizationRequest; import cn.axzo.nanopart.ess.api.request.AddSealPersonsRequest; -import cn.axzo.nanopart.ess.api.request.AssignWeixinAppUrlByOrgRequest; import cn.axzo.nanopart.ess.api.request.CreateConsoleLoginUrlRequest; -import cn.axzo.nanopart.ess.api.request.CreateContractRequest; +import cn.axzo.nanopart.ess.api.request.CreateContractByFileRequest; import cn.axzo.nanopart.ess.api.request.DownloadSingedContractPdfRequest; -import cn.axzo.nanopart.ess.api.request.GetContractDetailByBizCodeRequest; import cn.axzo.nanopart.ess.api.request.GetContractDetailByContractIdRequest; import cn.axzo.nanopart.ess.api.request.GetEmbedWebUrlRequest; import cn.axzo.nanopart.ess.api.request.GetOrgAuthStatesRequest; import cn.axzo.nanopart.ess.api.request.GetPersonAuthStateRequest; import cn.axzo.nanopart.ess.api.request.GetSealsRequest; +import cn.axzo.nanopart.ess.api.request.GetSignUrlRequest; import cn.axzo.nanopart.ess.api.request.RemoveSealAuthorizationRequest; import cn.axzo.nanopart.ess.api.request.RemoveSealPersonRequest; import cn.axzo.nanopart.ess.api.request.RevokeContractRequest; -import cn.axzo.nanopart.ess.api.response.AssignWeixinAppUrlByOrgResponse; import cn.axzo.nanopart.ess.api.response.CreateConsoleLoginUrlResponse; -import cn.axzo.nanopart.ess.api.response.CreateContractResponse; +import cn.axzo.nanopart.ess.api.response.CreateContractByFileResponse; import cn.axzo.nanopart.ess.api.response.DownloadSingedContractPdfResponse; -import cn.axzo.nanopart.ess.api.response.GetContractDetailByBizCodeResponse; import cn.axzo.nanopart.ess.api.response.GetContractDetailByContractIdResponse; import cn.axzo.nanopart.ess.api.response.GetEmbedWebUrlResponse; import cn.axzo.nanopart.ess.api.response.GetOrgAuthStatesResponse; import cn.axzo.nanopart.ess.api.response.GetPersonAuthStateResponse; +import cn.axzo.nanopart.ess.api.response.GetSignUrlResponse; import cn.axzo.nanopart.ess.server.dao.EssContractDao; import cn.axzo.nanopart.ess.server.entity.EssContract; import cn.axzo.nanopart.ess.server.ess.ContractManager; @@ -57,7 +55,7 @@ class ApiController implements EssApi { @Override public ApiResult> getOrgAuthStates(GetOrgAuthStatesRequest request) { - return ApiResult.ok(queryService.getEssUnitAuthStates(request)); + return ApiResult.ok(queryService.getOrgAuthStates(request)); } @Override @@ -109,14 +107,14 @@ class ApiController implements EssApi { } @Override - public ApiResult createContract(CreateContractRequest request) { - return ApiResult.ok(contractManager.createContract(request)); + public ApiResult createContractByFile(CreateContractByFileRequest request) { + return ApiResult.ok(contractManager.createContractByFile(request)); } @Override - public ApiResult assignWeixinAppUrlByOrg(AssignWeixinAppUrlByOrgRequest request) { - AssignWeixinAppUrlByOrgResponse response = new AssignWeixinAppUrlByOrgResponse(); - response.setUrl(essService.assignWeixinAppUrlByOrg(request)); + public ApiResult getContractSignUrl(GetSignUrlRequest request) { + GetSignUrlResponse response = new GetSignUrlResponse(); + response.setUrl(essService.getContractSignUrl(request)); return ApiResult.ok(response); } @@ -135,20 +133,11 @@ class ApiController implements EssApi { @Override public ApiResult - getContractDetailByContractId(GetContractDetailByContractIdRequest request) { + getContractByContractId(GetContractDetailByContractIdRequest request) { EssContract contract = essContractDao.getOrThrow(request.getEssContractId()); GetContractDetailByContractIdResponse response = new GetContractDetailByContractIdResponse(); response.setContract(BeanMapper.copyBean(contract, EssContractInfo.class)); return ApiResult.ok(response); } - @Override - public ApiResult - getContractDetailByBizCode(GetContractDetailByBizCodeRequest request) { - EssContract contract = essContractDao.findByBizCode(request.getAppCode(), request.getBizCode()).orElse(null); - GetContractDetailByBizCodeResponse response = new GetContractDetailByBizCodeResponse(); - response.setContract(BeanMapper.copyBean(contract, EssContractInfo.class)); - return ApiResult.ok(response); - } - } \ No newline at end of file diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/controller/CallbackController.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/controller/CallbackController.java index 1aabc1dd..264da8f8 100644 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/controller/CallbackController.java +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/controller/CallbackController.java @@ -24,7 +24,6 @@ import cn.axzo.nanopart.ess.server.ess.domain.JsonObjectAsString; import cn.axzo.nanopart.ess.server.ess.domain.OrgOpenId; import cn.axzo.nanopart.ess.server.ess.domain.PersonOpenId; import cn.axzo.nanopart.ess.server.ess.support.EssSupport; -import cn.axzo.nanopart.ess.server.ess.support.OssService; import cn.axzo.nanopart.ess.server.utils.IdBuilder; import com.alibaba.fastjson.JSONObject; import lombok.AccessLevel; @@ -56,7 +55,6 @@ class CallbackController implements EssCallbackApi, InitializingBean { private final OrgManager orgManager; private final EssService essService; - private final OssService ossService; private final ContractManager contractManager; private final EssLogDao essLogDao; private final EssContractDao essContractDao; @@ -72,17 +70,18 @@ class CallbackController implements EssCallbackApi, InitializingBean { OrgAuthorizationFinish result = request.readMsgData(OrgAuthorizationFinish.class); PersonOpenId person = PersonOpenId.parse(result.getProxyOperatorOpenId()); if (result.isOpenSuccess()) { - orgManager.addAuthorizedOrgPerson(person.getOuId(), person.getPersonId()); + orgManager.addAuthorizedOrgPerson(person); orgManager.setOrgAuthorized(person.getOuId(), person.getPersonId()); } return IdBuilder.builder() .append(person.getOuId()) - .append(person.getPersonId()).build(); + .append(person.getPersonId()) + .build(); }); // 员工加入子企业的时候发送此通知 registerHandler(CallbackType.ORG_PERSON_JOIN, request -> { PersonOpenId person = PersonOpenId.parse(request.readMsgData(OrgPersonJoin.class).getProxyOperatorOpenId()); - orgManager.addAuthorizedOrgPerson(person.getOuId(), person.getPersonId()); + orgManager.addAuthorizedOrgPerson(person); // 一般情况下只会存在一条记录 for (EssSealPerson sealPerson : essSealPersonDao.getByPersonId(person.getPersonId())) { AddSealAuthorizationRequest addSealAuthorizationRequest = new AddSealAuthorizationRequest(); @@ -97,7 +96,8 @@ class CallbackController implements EssCallbackApi, InitializingBean { } return IdBuilder.builder() .append(person.getOuId()) - .append(person.getPersonId()).build(); + .append(person.getPersonId()) + .build(); }); // 印章回调 registerHandler(CallbackType.SEAL_OPERATE, request -> { @@ -157,7 +157,7 @@ class CallbackController implements EssCallbackApi, InitializingBean { return detail; }) .collect(toList()); - EssContract contract = essContractDao.find(changes.getFlowId()).orElse(null); + EssContract contract = essContractDao.findOrNull(changes.getFlowId()); if (contract == null && StringUtils.isNotBlank(changes.getCustomerData())) { JSONObject customData = JsonObjectAsString.parse(changes.getCustomerData()).asJsonObject(); contract = essContractDao.getById(customData.getLong(ContractManager.CONTRACT_ID)); @@ -165,7 +165,8 @@ class CallbackController implements EssCallbackApi, InitializingBean { if (contract != null) { contractManager.updateContractState(contract, state, approveDetails); EssContract finalContract = contract; - ossService.maybeDownloadToOss(contract, () -> essService.getContractPDFUrlFromEss(finalContract)); + essService.maybeUploadContractToOss(contract.getEssContractId(), + () -> essService.getContractPDFUrlFromEss(finalContract)); } return changes.getFlowId(); }); @@ -184,10 +185,18 @@ class CallbackController implements EssCallbackApi, InitializingBean { try { CallbackType callbackType = CallbackType.parse(request.getMsgType()).orElse(null); CallbackHandler handler = callbackType == null ? null : handlers.get(callbackType); - if (handler != null) - subject = transactionTemplate.execute(unused -> handler.handle(request)); - else + if (handler != null) { + subject = transactionTemplate.execute(unused -> { + try { + return handler.handle(request); + } catch (Exception e) { + log.warn("callback failed", e); + throw new RuntimeException(e); + } + }); + } else { log.info("ignore callback: {}", request); + } return ApiResult.ok("success"); } finally { saveRequest(request, subject); diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/controller/PrivateController.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/controller/PrivateController.java index a78718cc..7735cd60 100644 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/controller/PrivateController.java +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/controller/PrivateController.java @@ -29,16 +29,15 @@ public class PrivateController { private final EssClient essClient; private final EssOrgDao essOrgDao; - @SuppressWarnings("DataFlowIssue") - @PostMapping("/private/ess/getOrgEmployees") - public ApiResult getOrgUsers(@RequestParam("ouId") Long ouId) { - EssOrg org = essOrgDao.find(ouId).orElse(null); + @PostMapping("/private/ess/getOrgPersons") + public ApiResult getOrgPersons(@RequestParam("ouId") Long ouId) { + EssOrg org = essOrgDao.findOrNull(ouId); BizAssertions.assertNotNull(org, "电子签单位不存在"); EssPerson superAdmin = orgManager.getSuperAdminOrThrow(ouId); HashMap staffs = new HashMap<>(); long offset = 0L; while (true) { - ChannelDescribeEmployeesResponse response = essClient.getEmployees(superAdmin, org, offset); + ChannelDescribeEmployeesResponse response = essClient.getOrgPerson(superAdmin, org, offset); if (response.getEmployees() == null || response.getEmployees().length == 0) break; offset = response.getOffset() + 1; diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/domain/PersonOpenId.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/domain/PersonOpenId.java index 3fd306e0..da58d155 100644 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/domain/PersonOpenId.java +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/domain/PersonOpenId.java @@ -10,7 +10,7 @@ import lombok.RequiredArgsConstructor; */ @Getter @RequiredArgsConstructor(access = AccessLevel.PRIVATE) -public class PersonOpenId { +public class PersonOpenId implements OrgPerson { private static final PersonOpenId NONE = create(0L, 0L); diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/support/ContractSupport.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/support/ContractSupport.java new file mode 100644 index 00000000..8950f65e --- /dev/null +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/support/ContractSupport.java @@ -0,0 +1,87 @@ +package cn.axzo.nanopart.ess.server.ess.support; + +import cn.axzo.basics.common.exception.ServiceException; +import cn.axzo.nanopart.ess.api.domain.CreateContractInfo; +import cn.axzo.nanopart.ess.api.domain.contract.Approver; +import cn.axzo.nanopart.ess.api.enums.Constraint; +import cn.axzo.nanopart.ess.api.enums.EssContractState; +import cn.axzo.nanopart.ess.api.request.CreateContractByFileRequest; +import cn.axzo.nanopart.ess.server.dao.EssContractDao; +import cn.axzo.nanopart.ess.server.dao.EssOrgDao; +import cn.axzo.nanopart.ess.server.entity.EssContract; +import cn.axzo.nanopart.ess.server.entity.EssOrg; +import cn.axzo.nanopart.ess.server.utils.BizAssertions; +import com.alibaba.fastjson.JSON; +import lombok.RequiredArgsConstructor; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.stereotype.Component; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; + +import static java.util.stream.Collectors.toList; + +/** + * @author yanglin + */ +@Component +@RequiredArgsConstructor +public class ContractSupport { + + private final EssOrgDao essOrgDao; + private final EssContractDao essContractDao; + + public void validateCreateContract(CreateContractInfo contract) { + checkCreateContractConstraint(contract); + ensureOrgAuthorized(contract); + } + + private void checkCreateContractConstraint(CreateContractInfo contract) { + if (contract.getConstraint() == Constraint.ONE_PERSON_PER_ORG) { + HashSet ouIds = new HashSet<>(); + for (Approver approver : contract.getApprovers()) { + if (approver.getSignPerson() == null) + continue; + BizAssertions.assertFalse(ouIds.contains(approver.getOuId()), + "同一单位不能有多个审批人, constraint={}", Constraint.ONE_PERSON_PER_ORG); + ouIds.add(approver.getOuId()); + } + } + } + + private void ensureOrgAuthorized(CreateContractInfo contract) { + BizAssertions.assertNotNull(contract, "合同信息不能为空"); + List orgs = essOrgDao.getByOuIds(contract.getApproverOuIds()); + List notAuthorizedOrgs = orgs.stream() + .filter(org -> !org.isAuthorized()) + .map(EssOrg::getOuName) + .collect(toList()); + BizAssertions.assertEmpty(notAuthorizedOrgs, + "创建合同失败. 以下单位还未认证: {}", JSON.toJSONString(notAuthorizedOrgs)); + } + + public EssContract saveContractByFile(CreateContractByFileRequest request) { + try { + EssContract contract = new EssContract(); + contract.setAppCode(request.getAppCode()); + contract.setBizCode(request.getBizCode()); + contract.setCreatorOuId(request.getCreator().getOuId()); + contract.setCreatorPersonId(request.getCreator().getPersonId()); + contract.setContractName(request.getByFile().getContractName()); + contract.setEssContractId(""); + contract.setEssFieldIds(Collections.emptyList()); + contract.setState(EssContractState.INIT); + contract.setApprovers(request.getByFile().getApprovers()); + contract.getOrCreateAssignment().setConstraint( + request.getContract().getConstraint()); + contract.setApproveDetails(Collections.emptyList()); + contract.setRecordExt(new EssContract.RecordExt()); + essContractDao.save(contract); + return contract; + } catch (DuplicateKeyException e) { + throw new ServiceException("业务编码对应的合同已存在"); + } + } + +} \ No newline at end of file diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/support/EssSupport.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/support/EssSupport.java index f14cda34..71434cd5 100644 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/support/EssSupport.java +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/support/EssSupport.java @@ -6,31 +6,30 @@ import cn.axzo.basics.profiles.dto.request.QueryPersonProfileByIdOrPhoneDto; import cn.axzo.maokai.api.client.OrganizationalUnitApi; import cn.axzo.maokai.api.vo.request.OrganizationalUnitQuery; import cn.axzo.maokai.api.vo.response.OrganizationalUnitVO; -import cn.axzo.nanopart.ess.api.domain.DynamicApproverProps; -import cn.axzo.nanopart.ess.api.enums.EssContractState; +import cn.axzo.nanopart.ess.api.domain.OrgPerson; import cn.axzo.nanopart.ess.api.enums.EssPersonState; import cn.axzo.nanopart.ess.api.request.CreateConsoleLoginUrlRequest; -import cn.axzo.nanopart.ess.api.request.CreateContractRequest; import cn.axzo.nanopart.ess.api.utils.YesOrNo; -import cn.axzo.nanopart.ess.server.entity.EssContract; import cn.axzo.nanopart.ess.server.entity.EssOrg; import cn.axzo.nanopart.ess.server.entity.EssPerson; -import cn.axzo.nanopart.ess.server.entity.domain.AssignedApprovers; import cn.axzo.nanopart.ess.server.utils.BizAssertions; import cn.azxo.framework.common.model.CommonResponse; -import com.tencentcloudapi.essbasic.v20210526.models.ApproverItem; +import cn.hutool.core.thread.NamedThreadFactory; import lombok.RequiredArgsConstructor; import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Component; -import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Optional; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy; +import java.util.concurrent.TimeUnit; import static cn.axzo.nanopart.ess.server.utils.BizAssertions.assertResponse; -import static java.util.stream.Collectors.toList; /** @@ -40,6 +39,12 @@ import static java.util.stream.Collectors.toList; @RequiredArgsConstructor public class EssSupport { + private final ExecutorService executor = new ThreadPoolExecutor(1, 5, + 0L, TimeUnit.MILLISECONDS, + new ArrayBlockingQueue<>(1024), + new NamedThreadFactory(EssSupport.class.getName(), false), + new CallerRunsPolicy()); + private final OrganizationalUnitApi organizationalUnitApi; private final UserProfileServiceApi userProfileServiceApi; @@ -55,16 +60,16 @@ public class EssSupport { return org; } - public EssPerson createPerson(Long ouId, Long personId) { - PersonProfileDto personProfile = getPersonProfileOrThrow(personId); - EssPerson person = new EssPerson(); - person.setOuId(ouId); - person.setPersonId(personId); - person.setPersonName(personProfile.getRealName()); - person.setState(EssPersonState.CREATED); - person.setCreateAt(new Date()); - person.setUpdateAt(new Date()); - return person; + public EssPerson createPerson(OrgPerson orgPerson) { + PersonProfileDto personProfile = getPersonProfileOrThrow(orgPerson); + EssPerson essPerson = new EssPerson(); + essPerson.setOuId(orgPerson.getOuId()); + essPerson.setPersonId(orgPerson.getPersonId()); + essPerson.setPersonName(personProfile.getRealName()); + essPerson.setState(EssPersonState.CREATED); + essPerson.setCreateAt(new Date()); + essPerson.setUpdateAt(new Date()); + return essPerson; } public OrganizationalUnitVO getOrgProfileOrThrow(Long ouId) { @@ -82,9 +87,9 @@ public class EssSupport { return assertResponse(organizationalUnitApi.list(request)); } - public PersonProfileDto getPersonProfileOrThrow(Long personId) { - PersonProfileDto person = findPersonProfile(personId).orElse(null); - BizAssertions.assertNotNull(person, "人员不存在: {}", personId); + public PersonProfileDto getPersonProfileOrThrow(OrgPerson orgPerson) { + PersonProfileDto person = findPersonProfile(orgPerson).orElse(null); + BizAssertions.assertNotNull(person, "人员不存在: {}", orgPerson.getPersonId()); return person; } @@ -95,8 +100,8 @@ public class EssSupport { return CollectionUtils.isEmpty(profiles) ? Optional.empty() : Optional.of(profiles.get(0)); } - public Optional findPersonProfile(Long personId) { - CommonResponse response = userProfileServiceApi.getPersonProfile(personId); + public Optional findPersonProfile(OrgPerson orgPerson) { + CommonResponse response = userProfileServiceApi.getPersonProfile(orgPerson.getPersonId()); if (response.getCode() == 404) return Optional.empty(); return Optional.ofNullable(assertResponse(response)); @@ -108,38 +113,8 @@ public class EssSupport { return assertResponse(userProfileServiceApi.getPersonProfiles(personIds)); } - public EssContract createContract(CreateContractRequest request) { - EssContract contract = new EssContract(); - contract.setAppCode(request.getAppCode()); - contract.setBizCode(request.getBizCode()); - contract.setCreatorOuId(request.getOperator().getOuId()); - contract.setCreatorPersonId(request.getOperator().getPersonId()); - contract.setContractName(request.getByFile().getContractName()); - contract.setEssContractId(""); - contract.setEssFieldIds(Collections.emptyList()); - contract.setState(EssContractState.INIT); - contract.setIsDynamicApprover(YesOrNo.valueOf( - request.getByFile().isDynamicApprover())); - contract.setApprovers(request.getByFile().getApprovers()); - if (request.getByFile().isDynamicApprover()) { - DynamicApproverProps props = request.getByFile().getDynamicApproverProps(); - if (props == null) - props = DynamicApproverProps.defaultProps(); - AssignedApprovers assignedApprovers = new AssignedApprovers(); - assignedApprovers.setAssignType(props.getAssignType()); - contract.setAssignedApprovers(assignedApprovers); - } - contract.setApproveDetails(Collections.emptyList()); - contract.setRecordExt(new EssContract.RecordExt()); - return contract; - } - - public static List collectRecipientIds(ApproverItem[] approvers) { - if (approvers == null || approvers.length == 0) - return Collections.emptyList(); - return Arrays.stream(approvers) - .map(ApproverItem::getRecipientId) - .collect(toList()); + public void asyncExec(Runnable task) { + executor.execute(task); } } \ No newline at end of file diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/support/OssService.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/support/OssService.java index 1cfbcb6e..2f72c763 100644 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/support/OssService.java +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/support/OssService.java @@ -1,8 +1,5 @@ package cn.axzo.nanopart.ess.server.ess.support; -import cn.axzo.basics.common.exception.ServiceException; -import cn.axzo.nanopart.ess.server.dao.EssContractDao; -import cn.axzo.nanopart.ess.server.entity.EssContract; import cn.axzo.nanopart.ess.server.utils.BizAssertions; import cn.axzo.oss.http.api.ServerFileServiceApi; import cn.axzo.oss.http.model.ApiSignUrlDownloadRequest; @@ -12,7 +9,6 @@ import cn.axzo.oss.http.model.ApiSignUrlUploadResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import java.io.ByteArrayInputStream; @@ -25,9 +21,8 @@ import java.net.URL; import java.net.URLEncoder; import java.util.Collections; import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.function.Supplier; + +import static cn.axzo.nanopart.ess.server.utils.BizAssertions.fail; /** * @author yanglin @@ -37,21 +32,8 @@ import java.util.function.Supplier; @RequiredArgsConstructor public class OssService { - private final EssContractDao essContractDao; private final ServerFileServiceApi serverFileServiceApi; private final EssProps essProps; - private final ExecutorService executor = Executors.newSingleThreadExecutor(); - - public void maybeDownloadToOss(EssContract contract, Supplier essPdfUrl) { - executor.execute(() -> { - if (!contract.getState().isFinalState() - || StringUtils.isNotBlank(contract.getOssFileKey())) - return; - String fileName = String.format("%s.pdf", contract.getContractName()); - String fileKey = saveToOss(essPdfUrl.get(), fileName); - essContractDao.setOssInfo(contract, fileKey); - }); - } public String getOssUrl(String fileKey) { ApiSignUrlDownloadRequest request = new ApiSignUrlDownloadRequest(); @@ -63,17 +45,6 @@ public class OssService { return CollectionUtils.isEmpty(responses) ? null : responses.get(0).getSignUrl(); } - public String saveToOss(String contentUrl, String fileName) { - try { - ApiSignUrlUploadResponse ossResponse = prepareOss(fileName); - streamUpload(ossResponse, urlDownload(contentUrl), fileName); - return ossResponse.getFileKey(); - } catch (Exception e) { - log.warn("下载合同并上传到OSS失败", e); - throw new ServiceException("下载合同并上传到OSS失败", e); - } - } - private ApiSignUrlUploadResponse prepareOss(String fileName) { ApiSignUrlUploadRequest ossRequest = new ApiSignUrlUploadRequest(); ossRequest.setAppCode(essProps.getOssAppCode()); @@ -83,10 +54,22 @@ public class OssService { .assertResponse(serverFileServiceApi.signUrlFetchUpload(ossRequest)); } + public String uploadToOss(String contentUrl, String fileName) { + try { + ApiSignUrlUploadResponse ossResponse = prepareOss(fileName); + streamUpload(ossResponse, urlDownload(contentUrl), fileName); + return ossResponse.getFileKey(); + } catch (Exception e) { + log.warn("下载合同并上传到OSS失败", e); + throw fail(e, "下载合同并上传到OSS失败"); + } + } + private byte[] urlDownload(String url) throws IOException { HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection(); con.setRequestMethod("GET"); - con.setConnectTimeout(4 * 1000); + con.setConnectTimeout(10 * 1000); + con.setReadTimeout(10 * 1000); InputStream inStream = con.getInputStream(); ByteArrayOutputStream outStream = new ByteArrayOutputStream(); byte[] buffer = new byte[2048]; diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/support/PersonProfiles.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/support/PersonProfiles.java index 827cc69b..96e41120 100644 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/support/PersonProfiles.java +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/support/PersonProfiles.java @@ -26,14 +26,15 @@ public class PersonProfiles { } public PersonProfileDto getOrThrow(Long personId) { - PersonProfileDto person = find(personId).orElse(null); + PersonProfileDto person = findOrNull(personId); BizAssertions.assertNotNull(person, "找不到人员信息, personId={}", personId); return person; } - public Optional find(Long personId) { + public PersonProfileDto findOrNull(Long personId) { return profiles.stream() .filter(p -> p.getId().equals(personId)) - .findFirst(); + .findFirst() + .orElse(null); } } \ No newline at end of file diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/utils/BizAssertions.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/utils/BizAssertions.java index 8ae0a676..406af238 100644 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/utils/BizAssertions.java +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/utils/BizAssertions.java @@ -21,6 +21,21 @@ import java.util.Objects; @Slf4j public class BizAssertions { + public static ServiceException fail(String message, Object... args) { + return new ServiceException(MessageFormatter.arrayFormat(message, args).getMessage()); + } + + public static ServiceException fail(Exception e, String message, Object... args) { + return new ServiceException(MessageFormatter.arrayFormat(message, args).getMessage(), e); + } + + /** + * 断言集合为空 + */ + public static void assertEmpty(Collection actual, String message, Object... args) { + AssertUtil.isEmpty(actual, MessageFormatter.arrayFormat(message, args).getMessage()); + } + /** * 断言为NULL */ @@ -42,13 +57,6 @@ public class BizAssertions { AssertUtil.notEmpty(actual, MessageFormatter.arrayFormat(message, args).getMessage()); } - /** - * 断言集合为空 - */ - public static void assertEmpty(Collection actual, String message, Object... args) { - AssertUtil.isEmpty(actual, MessageFormatter.arrayFormat(message, args).getMessage()); - } - /** * 断言数组不为空 */ @@ -100,13 +108,21 @@ public class BizAssertions { } } + private static String messageOrTemplateMessage(String message, String template, Object... args) { + if (message != null) { + return message; + } + return MessageFormatter.arrayFormat(template, args).getMessage(); + } + public static T assertResponse(CommonResponse response) { return assertResponse(response, "error resp={}", JSON.toJSONString(response)); } public static T assertResponse(CommonResponse response, String message, Object... args) { if (response == null || response.getCode() != HttpStatus.HTTP_OK) { - ServiceException e = new ServiceException(MessageFormatter.arrayFormat(message, args).getMessage()); + ServiceException e = new ServiceException(messageOrTemplateMessage( + response == null ? null : response.getMsg(), message, args)); log.warn("remote call response with error", e); throw e; } @@ -119,7 +135,7 @@ public class BizAssertions { public static T assertResponse(ApiResult response, String message, Object... args) { if (!response.isSuccess()) { - ServiceException e = new ServiceException(MessageFormatter.arrayFormat(message, args).getMessage()); + ServiceException e = new ServiceException(messageOrTemplateMessage(response.getMsg(), message, args)); log.warn("remote call response with error", e); throw e; } @@ -132,29 +148,11 @@ public class BizAssertions { public static List assertResponse(ApiListResult response, String message, Object... args) { if (!response.isSuccess()) { - ServiceException e = new ServiceException(MessageFormatter.arrayFormat(message, args).getMessage()); + ServiceException e = new ServiceException(messageOrTemplateMessage(response.getMsg(), message, args)); log.warn("remote call response with error", e); throw e; } return response.getData(); } - - public static T assertResponse(cn.axzo.foundation.result.ApiResult response) { - return assertResponse(response, "error resp={}", JSON.toJSONString(response)); - } - - public static T assertResponse(cn.axzo.foundation.result.ApiResult response, String message, Object... args) { - if (!response.isSuccess()) { - String finalMsg = MessageFormatter.arrayFormat(message, args).getMessage(); - if (StringUtils.isNotBlank(response.getMsg())) { - finalMsg += ": " + response.getMsg(); - } - ServiceException e = new ServiceException(finalMsg); - log.warn("remote call response with error. response={}", JSON.toJSONString(response), e); - throw e; - } - return response.getData(); - } - } \ No newline at end of file