From 607347f0010e8dc65faf950521d37365ee0745e6 Mon Sep 17 00:00:00 2001 From: yanglin Date: Mon, 24 Feb 2025 17:15:28 +0800 Subject: [PATCH] =?UTF-8?q?REQ-3581:=20=E5=BE=88=E5=A4=9A=E4=B8=9C?= =?UTF-8?q?=E8=A5=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../axzo/nanopart/ess/api/enums/MQEvent.java | 3 +- .../api/mq/EssContractDownloadPDFEvent.java | 24 ++++++ .../request/SaveContractSnapshotRequest.java | 5 ++ .../nanopart/ess/server/entity/EssOrg.java | 17 ---- .../nanopart/ess/server/entity/EssPerson.java | 2 +- .../ess/server/ess/ContractManager.java | 21 ++--- .../nanopart/ess/server/ess/EssService.java | 82 +++++++++++-------- .../nanopart/ess/server/ess/OrgManager.java | 30 +++---- .../server/ess/controller/ApiController.java | 2 +- .../ess/controller/CallbackController.java | 2 +- .../ess/mq/DownloadContractHandler.java | 48 +++++++++++ .../server/ess/{ => mq}/EssBroadcaster.java | 20 ++++- .../PersonResignHandler.java} | 57 ++++++------- .../server/ess/support/ContractSupport.java | 21 +++++ .../ess/server/ess/support/EssSupport.java | 16 ---- .../server/ess/support/PersonProfiles.java | 6 -- .../ess/server/mq/NanopartEssListener.java | 38 +++++++++ .../ess/server/mq/OrganizationalListener.java | 37 +++++++++ .../ess/server/utils/BizTransactional.java | 19 +++++ 19 files changed, 299 insertions(+), 151 deletions(-) create mode 100644 ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/mq/EssContractDownloadPDFEvent.java create mode 100644 ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/mq/DownloadContractHandler.java rename ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/{ => mq}/EssBroadcaster.java (61%) rename ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/{PersonResignListener.java => mq/PersonResignHandler.java} (58%) create mode 100644 ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/mq/NanopartEssListener.java create mode 100644 ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/mq/OrganizationalListener.java create mode 100644 ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/utils/BizTransactional.java diff --git a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/enums/MQEvent.java b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/enums/MQEvent.java index 60836032..d047e2c8 100644 --- a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/enums/MQEvent.java +++ b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/enums/MQEvent.java @@ -13,7 +13,8 @@ import java.util.Arrays; @Getter public enum MQEvent { - ESS_CONTRACT_STATE_CHANGE("nanopart", "ess-contract-state-change", "腾讯电子签合同状态变化") + ESS_CONTRACT_STATE_CHANGE("nanopart", "ess-contract-state-change", "腾讯电子签合同状态变化"), + ESS_CONTRACT_DOWNLOAD_PDF("nanopart", "ess-contract-download-pdf", "腾讯电子签合同下载PDF"), ; private final String model; diff --git a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/mq/EssContractDownloadPDFEvent.java b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/mq/EssContractDownloadPDFEvent.java new file mode 100644 index 00000000..207138bd --- /dev/null +++ b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/mq/EssContractDownloadPDFEvent.java @@ -0,0 +1,24 @@ +package cn.axzo.nanopart.ess.api.mq; + +import cn.axzo.nanopart.ess.api.domain.contract.EssContractInfo; +import lombok.Getter; +import lombok.Setter; + +/** + * @author yanglin + */ +@Setter +@Getter +public class EssContractDownloadPDFEvent extends MqMessage { + + /** + * 合同信息 + */ + private EssContractInfo contract; + + /** + * 是否重复下载 + */ + private boolean retryDownload; + +} diff --git a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/SaveContractSnapshotRequest.java b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/SaveContractSnapshotRequest.java index 95ac7dc5..90fb2db7 100644 --- a/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/SaveContractSnapshotRequest.java +++ b/ess/ess-api/src/main/java/cn/axzo/nanopart/ess/api/request/SaveContractSnapshotRequest.java @@ -18,6 +18,11 @@ public class SaveContractSnapshotRequest { @NotBlank(message = "essContractId不能为空") private String essContractId; + /** + * 是否重复下载, 即已经下载过了, 但是需要重新下载 + */ + private boolean retryDownload = false; + @Override public String toString() { return JSON.toJSONString(this); 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 07fd150e..47dd8d3e 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 @@ -9,7 +9,6 @@ import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler; import lombok.Getter; import lombok.Setter; -import java.util.HashSet; import java.util.Set; /** @@ -57,20 +56,10 @@ public class EssOrg extends BaseEntity { return recordExt; } - public boolean isPotentialSuperAdmin(Long personId) { - if (superAdminPersonId != null && superAdminPersonId.equals(personId)) - return true; - return getOrCreateExt().isPotentialSuperAdmin(personId); - } - public boolean isAuthorized() { return isAuthorized.isYes(); } - public boolean isCreateBy(Long personId) { - return personId.equals(createByPersonId); - } - @Override public String toString() { return JSON.toJSONString(this); @@ -81,12 +70,6 @@ public class EssOrg extends BaseEntity { private Set historySuperAdmins; - public void addHistorySuperAdmin(Long personId) { - if (historySuperAdmins == null) - historySuperAdmins = new HashSet<>(); - historySuperAdmins.add(personId); - } - public boolean isPotentialSuperAdmin(Long personId) { return historySuperAdmins != null && historySuperAdmins.contains(personId); } diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/EssPerson.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/EssPerson.java index 091a91e7..0c230234 100644 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/EssPerson.java +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/entity/EssPerson.java @@ -34,7 +34,7 @@ public class EssPerson extends BaseEntity implements OrgPerson { private String personName; /** - * 是否已经认证. YES: 已认证, NO: 未认证 + * 是否已经认证 */ private EssPersonState state; 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 14623355..bd5f1077 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,7 +1,6 @@ package cn.axzo.nanopart.ess.server.ess; import cn.axzo.basics.common.exception.ServiceException; -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.EssContractState; import cn.axzo.nanopart.ess.api.request.CreateContractByFileRequest; @@ -12,8 +11,10 @@ import cn.axzo.nanopart.ess.server.dao.EssLogDao; import cn.axzo.nanopart.ess.server.entity.EssContract; import cn.axzo.nanopart.ess.server.entity.EssPerson; import cn.axzo.nanopart.ess.server.ess.domain.JsonObjectAsString; +import cn.axzo.nanopart.ess.server.ess.mq.EssBroadcaster; import cn.axzo.nanopart.ess.server.ess.support.ContractSupport; import cn.axzo.nanopart.ess.server.utils.BizAssertions; +import cn.axzo.nanopart.ess.server.utils.BizTransactional; import com.tencentcloudapi.common.exception.TencentCloudSDKException; import com.tencentcloudapi.essbasic.v20210526.models.ApproverItem; import com.tencentcloudapi.essbasic.v20210526.models.ChannelCreateFlowByFilesResponse; @@ -21,7 +22,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.dao.DuplicateKeyException; import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; import java.util.Arrays; import java.util.List; @@ -54,18 +54,7 @@ public class ContractManager { try { contract = contractSupport.saveContractByFile(request); } catch (DuplicateKeyException e) { - log.warn("幂等重复, request={}", request, e); - EssContract savedContract = essContractDao - .findByIdempotentCode(request.getAppCode(), request.getIdempotentCode()) - .orElseThrow(InternalError::new); - essLogDao.log("createContractByFile", savedContract.getEssContractId(), - "message", "幂等重复", "request", request); - CreateContractByFileResponse response = new CreateContractByFileResponse(); - response.setEssContractId(savedContract.getEssContractId()); - response.setEssRecipientIds(savedContract.getApprovers().stream() - .map(Approver::getEssRecipientId) - .collect(toList())); - return response; + return contractSupport.createDuplicateContractByFileResponse(request); } try { EssPerson superAdmin = orgManager.getSuperAdminOrThrow(request.getCreator().getOuId()); @@ -94,7 +83,7 @@ public class ContractManager { } } - @Transactional + @BizTransactional public void revokeContract(RevokeContractRequest request) { essLogDao.logRequest("revokeContract", request.getEssContractId(), request); EssContract contract = essContractDao.getOrThrow(request.getEssContractId()); @@ -105,7 +94,7 @@ public class ContractManager { updateContractState(contract, EssContractState.CANCEL, null, request.getReason()); } - @Transactional + @BizTransactional public void updateContractState(EssContract contract, EssContractState state, List approveDetails, 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 ce462318..4603fd93 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 @@ -10,6 +10,7 @@ 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.SaveContractSnapshotRequest; import cn.axzo.nanopart.ess.api.request.SealAndPersonRequest; import cn.axzo.nanopart.ess.server.dao.EssContractDao; import cn.axzo.nanopart.ess.server.dao.EssLogDao; @@ -22,9 +23,10 @@ import cn.axzo.nanopart.ess.server.entity.EssSeal; 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.EssSupport; +import cn.axzo.nanopart.ess.server.ess.mq.EssBroadcaster; import cn.axzo.nanopart.ess.server.ess.support.OssService; import cn.axzo.nanopart.ess.server.utils.BizAssertions; +import cn.axzo.nanopart.ess.server.utils.BizTransactional; import cn.axzo.nanopart.ess.server.utils.IdBuilder; import com.tencentcloudapi.essbasic.v20210526.models.CreateSignUrlsResponse; import com.tencentcloudapi.essbasic.v20210526.models.SignUrlInfo; @@ -32,8 +34,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.transaction.support.TransactionTemplate; import java.util.function.Function; @@ -54,10 +54,9 @@ public class EssService { private final EssPersonDao essPersonDao; private final OssService ossService; private final EssLogDao essLogDao; - private final EssSupport essSupport; - private final TransactionTemplate transactionTemplate; + private final EssBroadcaster essBroadcaster; - @Transactional + @BizTransactional public String createConsoleLoginUrl(CreateConsoleLoginUrlRequest request) { String subject = IdBuilder.builder() .append(request.getOuId()) @@ -86,7 +85,7 @@ public class EssService { contextPerson, request.getEmbedType(), request.getBusinessId()); } - @Transactional + @BizTransactional public void essAddSealAuthorization(AddSealAuthorizationRequest request) { String subject = IdBuilder.builder() .append(request.getEssSealId()) @@ -105,7 +104,7 @@ public class EssService { orgManager.addSealAuthorization(request.getEssSealId(), request.getPersonId(), request.getOperatorPersonId()); } - @Transactional + @BizTransactional public void essRemoveSealAuthorization(RemoveSealAuthorizationRequest request) { String subject = IdBuilder.builder() .append(request.getEssSealId()) @@ -174,39 +173,50 @@ public class EssService { EssContract contract = essContractDao.getOrThrow(essContractId); if (StringUtils.isNotBlank(contract.getOssFileKey())) return ossService.getOssUrl(contract.getOssFileKey()); - maybeUploadContractToOss(contract); return getContractPDFUrlFromEss(contract); } - public void saveContractSnapshot(String essContractId) { - EssContract c = transactionTemplate.execute(unused -> { - EssContract contract = essContractDao.getOrThrow(essContractId); - contract.getOrCreateExt().setShouldDownloadContract(true); - essContractDao.updateExt(contract); - return contract; - }); - maybeUploadContractToOss(c); + @BizTransactional + public void saveContractSnapshot(SaveContractSnapshotRequest request) { + essLogDao.logRequest("saveContractSnapshot", request.getEssContractId(), request); + EssContract contract = essContractDao.getOrThrow(request.getEssContractId()); + contract.getOrCreateExt().setShouldDownloadContract(true); + essContractDao.updateExt(contract); + maybeScheduleDownloadContractPDF(contract, request.isRetryDownload()); } - public void maybeUploadContractToOss(EssContract contract) { - essSupport.asyncExec(() -> { - EssContract c = essContractDao.findOrNull(contract.getEssContractId()); - if (c == null || !c.shouldDownloadContract() || StringUtils.isNotBlank(c.getOssFileKey())) - return; - try { - String pdfUrl = getContractPDFUrlFromEss(c); - String fileName = String.format("%s.pdf", c.getContractName()); - String fileKey = ossService.uploadToOss(pdfUrl, fileName); - transactionTemplate.executeWithoutResult(unused -> { - essContractDao.setOssFileKey(c, fileKey); - essLogDao.log("uploadContractToOss", c.getEssContractId(), "ossFileKey", fileKey); - }); - log.info("上传合同到OSS成功, essContractId={}", c.getEssContractId()); - } catch (Exception e) { - log.warn("上传合同到OSS失败", e); - essLogDao.log(e, "uploadContractToOss", c.getEssContractId()); - } - }); + public void maybeScheduleDownloadContractPDF(EssContract contract, boolean retryDownload) { + EssContract reload = essContractDao.findOrNull(contract.getEssContractId()); + if (shouldDownloadContractPDF(reload)) + essBroadcaster.fireDownloadContractPDF(contract, retryDownload); + } + + private boolean shouldDownloadContractPDF(EssContract contract) { + return contract != null && contract.shouldDownloadContract(); + } + + @BizTransactional + public void downloadContractPDF(EssContract contract, boolean retryDownload) { + if (StringUtils.isNotBlank(contract.getOssFileKey()) && !retryDownload) { + log.info("合同已下载PDF, contract={}", contract); + return; + } + if (!shouldDownloadContractPDF(contract)) { + log.info("合同不需要下载PDF, contract={}", contract); + return; + } + try { + String pdfUrl = getContractPDFUrlFromEss(contract); + String fileName = String.format("%s.pdf", contract.getContractName()); + String fileKey = ossService.uploadToOss(pdfUrl, fileName); + essContractDao.setOssFileKey(contract, fileKey); + essLogDao.log("uploadContractToOss", contract.getEssContractId(), + "ossFileKey", fileKey, "forceDownload", retryDownload); + log.info("上传合同到OSS成功, essContractId={}", contract.getEssContractId()); + } catch (Exception e) { + log.warn("上传合同到OSS失败", e); + essLogDao.log(e, "uploadContractToOss", contract.getEssContractId()); + } } private String getContractPDFUrlFromEss(EssContract contract) { 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 397d52f2..3d26dd9f 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 @@ -19,13 +19,13 @@ import cn.axzo.nanopart.ess.server.ess.domain.OrgAndPerson; import cn.axzo.nanopart.ess.server.ess.domain.SealPersons; import cn.axzo.nanopart.ess.server.ess.support.EssSupport; import cn.axzo.nanopart.ess.server.utils.BizAssertions; +import cn.axzo.nanopart.ess.server.utils.BizTransactional; import com.google.common.collect.Sets; 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.List; import java.util.Optional; @@ -49,7 +49,7 @@ public class OrgManager { // !! login - @Transactional + @BizTransactional public OrgAndPerson createConsoleLoginUrl(CreateConsoleLoginUrlRequest request) { EssOrg org = essOrgDao.findForUpdateOrNull(request.getOuId()); if (org == null) { @@ -62,7 +62,7 @@ public class OrgManager { // !! org and person - @Transactional + @BizTransactional public void addAuthorizedOrgPerson(OrgPerson orgPerson) { EssPerson person = getOrCreateOrgPerson(orgPerson); essPersonDao.setState(person, EssPersonState.AUTHORIZED); @@ -80,24 +80,14 @@ public class OrgManager { return essPerson; } - @Transactional + @BizTransactional public void setOrgAuthorized(Long ouId, Long superAdmin) { essOrgDao.setOrgAuthorized(ouId, superAdmin); - addSuperAdmin(ouId, superAdmin); } - @Transactional + @BizTransactional public void changeSuperAdmin(Long ouId, Long superAdmin) { essOrgDao.changeSuperAdmin(ouId, superAdmin); - addSuperAdmin(ouId, superAdmin); - } - - @Transactional - public void addSuperAdmin(Long ouId, Long superAdmin) { - EssOrg org = essOrgDao.findForUpdateOrNull(ouId); - if (org == null) return; - org.getOrCreateExt().addHistorySuperAdmin(superAdmin); - essOrgDao.updateExt(org); } public EssPerson getSuperAdminOrThrow(Long ouId) { @@ -116,7 +106,7 @@ public class OrgManager { // !! seal - @Transactional + @BizTransactional public boolean maybeAddSeal(Long ouId, String essSealId, EssSealType type) { EssSeal seal = essSealDao.findByEssSealId(essSealId).orElse(null); if (seal != null) @@ -138,19 +128,19 @@ public class OrgManager { essSealDao.updateState(essSealId, state); } - @Transactional + @BizTransactional public void addSealAuthorization(String essSealId, Long personId, Long authorizedByPersonId) { maybeAddSealPersons(essSealId, Sets.newHashSet(personId)); essSealPersonDao.setPersonAuthorized(essSealId, personId, authorizedByPersonId); } - @Transactional + @BizTransactional public void removeSealAuthorization(String essSealId, Long personId) { maybeAddSealPersons(essSealId, Sets.newHashSet(personId)); essSealPersonDao.removeSealAuthorization(essSealId, personId); } - @Transactional + @BizTransactional public void maybeAddSealPersons(String essSealId, Set personIds) { EssSeal seal = essSealDao.findByEssSealId(essSealId).orElse(null); BizAssertions.assertNotNull(seal, "印章不存在: {}", essSealId); @@ -176,7 +166,7 @@ public class OrgManager { } } - @Transactional + @BizTransactional public void tryRemoveSealPerson(RemoveSealPersonRequest request) { EssSeal seal = essSealDao.findByEssSealId(request.getEssSealId()).orElse(null); BizAssertions.assertNotNull(seal, "印章不存在: {}", request.getEssSealId()); 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 66becf02..72d417cc 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 @@ -141,7 +141,7 @@ class ApiController implements EssApi { @Override public ApiResult saveContractSnapshot(SaveContractSnapshotRequest request) { log.info("saveContractSnapshot request={}", request); - essService.saveContractSnapshot(request.getEssContractId()); + essService.saveContractSnapshot(request); return ApiResult.ok(); } 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 89f75acb..1fa62cad 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 @@ -160,7 +160,7 @@ class CallbackController implements EssCallbackApi, InitializingBean { log.warn("contract not found: {}", changes.getFlowId()); } else { contractManager.updateContractState(contract, state, approveDetails, changes.getFlowMessage()); - essService.maybeUploadContractToOss(contract); + essService.maybeScheduleDownloadContractPDF(contract, false); } return changes.getFlowId(); }); diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/mq/DownloadContractHandler.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/mq/DownloadContractHandler.java new file mode 100644 index 00000000..e2ba491f --- /dev/null +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/mq/DownloadContractHandler.java @@ -0,0 +1,48 @@ +package cn.axzo.nanopart.ess.server.ess.mq; + +import cn.axzo.framework.rocketmq.Event; +import cn.axzo.framework.rocketmq.EventConsumer; +import cn.axzo.framework.rocketmq.EventHandler; +import cn.axzo.nanopart.ess.api.enums.MQEvent; +import cn.axzo.nanopart.ess.api.mq.EssContractDownloadPDFEvent; +import cn.axzo.nanopart.ess.server.dao.EssContractDao; +import cn.axzo.nanopart.ess.server.entity.EssContract; +import cn.axzo.nanopart.ess.server.ess.EssService; +import com.alibaba.fastjson.JSON; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.stereotype.Component; + +import java.util.Collections; + +/** + * @author yanglin + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class DownloadContractHandler implements EventHandler, InitializingBean { + + private final EventConsumer eventConsumer; + private final EssContractDao essContractDao; + private final EssService essService; + + @Override + public void onEvent(Event event, EventConsumer.Context context) { + EssContractDownloadPDFEvent message = event.normalizedData(EssContractDownloadPDFEvent.class); + log.info("receive download contract pdf event: {}", JSON.toJSONString(message)); + EssContract contract = essContractDao.findOrNull(message.getContract().getEssContractId()); + if (contract == null) + log.info("try download contract pdf but contract not found: {}", JSON.toJSONString(message)); + else + essService.downloadContractPDF(contract, message.isRetryDownload()); + } + + @Override + public void afterPropertiesSet() { + eventConsumer.registerHandlers(Collections.singletonList( + MQEvent.ESS_CONTRACT_DOWNLOAD_PDF.getEventCode()), this); + } + +} \ No newline at end of file diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/EssBroadcaster.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/mq/EssBroadcaster.java similarity index 61% rename from ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/EssBroadcaster.java rename to ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/mq/EssBroadcaster.java index cddc45d9..fdfad651 100644 --- a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/EssBroadcaster.java +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/mq/EssBroadcaster.java @@ -1,10 +1,11 @@ -package cn.axzo.nanopart.ess.server.ess; +package cn.axzo.nanopart.ess.server.ess.mq; import cn.axzo.basics.common.BeanMapper; import cn.axzo.framework.rocketmq.Event; import cn.axzo.framework.rocketmq.EventProducer; import cn.axzo.nanopart.ess.api.domain.contract.EssContractInfo; import cn.axzo.nanopart.ess.api.enums.MQEvent; +import cn.axzo.nanopart.ess.api.mq.EssContractDownloadPDFEvent; import cn.axzo.nanopart.ess.api.mq.EssContractStateChangeMessage; import cn.axzo.nanopart.ess.server.dao.EssContractDao; import cn.axzo.nanopart.ess.server.entity.EssContract; @@ -16,12 +17,12 @@ import org.springframework.stereotype.Component; */ @Component @RequiredArgsConstructor -class EssBroadcaster { +public class EssBroadcaster { private final EssContractDao essContractDao; protected final EventProducer eventProducer; - void fireContractStateChanged(EssContract contract) { + public void fireContractStateChanged(EssContract contract) { EssContract reloadContract = essContractDao.findOrNull(contract.getEssContractId()); if (reloadContract == null) return; @@ -36,4 +37,17 @@ class EssBroadcaster { .build()); } + public void fireDownloadContractPDF(EssContract contract, boolean forceDownload) { + EssContractDownloadPDFEvent message = new EssContractDownloadPDFEvent(); + message.setContract(BeanMapper.copyBean(contract, EssContractInfo.class)); + message.setRetryDownload(forceDownload); + eventProducer.send(Event.builder() + .eventCode(MQEvent.ESS_CONTRACT_DOWNLOAD_PDF.getEventCode()) + .shardingKey(contract.getEssContractId()) + .targetId(contract.getEssContractId()) + .targetType("ess-contract") + .data(message) + .build()); + } + } \ No newline at end of file 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/mq/PersonResignHandler.java similarity index 58% rename from ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/PersonResignListener.java rename to ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/mq/PersonResignHandler.java index ea330386..f78cc7a2 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/mq/PersonResignHandler.java @@ -1,23 +1,19 @@ -package cn.axzo.nanopart.ess.server.ess; +package cn.axzo.nanopart.ess.server.ess.mq; -import cn.axzo.framework.rocketmq.BaseListener; 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 cn.axzo.nanopart.ess.server.ess.EssClient; +import cn.axzo.nanopart.ess.server.ess.OrgManager; import cn.axzo.nanopart.ess.server.utils.IdBuilder; import com.alibaba.fastjson.JSON; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.rocketmq.common.message.MessageExt; -import org.apache.rocketmq.spring.annotation.ConsumeMode; -import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; -import org.apache.rocketmq.spring.core.RocketMQListener; import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Component; import org.springframework.transaction.support.TransactionTemplate; @@ -30,55 +26,51 @@ import java.util.Collections; @Slf4j @Component @RequiredArgsConstructor -@RocketMQMessageListener( - maxReconsumeTimes = 3, - consumeMode = ConsumeMode.ORDERLY, - nameServer = "${rocketmq.name-server}", - topic = "topic_organizational_${spring.profiles.active}", - consumerGroup = "GID_ess_person_resign_${spring.application.name}_${spring.profiles.active}" -) -public class PersonResignListener extends BaseListener - implements RocketMQListener, EventHandler, InitializingBean { +public class PersonResignHandler implements EventHandler, InitializingBean { private final EventConsumer eventConsumer; private final EssClient essClient; private final OrgManager orgManager; private final EssOrgDao essOrgDao; private final EssPersonDao essPersonDao; - private final EssLogDao essLogDao; private final TransactionTemplate transactionTemplate; - @Override - public void onMessage(MessageExt message) { - log.info("receive mq message: {}", JSON.toJSONString(message)); - super.onEvent(message, eventConsumer); - } - @Override public void onEvent(Event event, EventConsumer.Context context) { - OrgUserStatusChangedEvent userEvent = event.normalizedData(OrgUserStatusChangedEvent.class); - log.info("receive user event: {}", JSON.toJSONString(userEvent)); - if (userEvent.getStatusCode() == 6) - transactionTemplate.executeWithoutResult(unused -> trySetPersonResigned(userEvent)); + OrgUserStatusChangedEvent message = event.normalizedData(OrgUserStatusChangedEvent.class); + log.info("receive node user event: {}", JSON.toJSONString(message)); + if (message.getStatusCode() == 6) + transactionTemplate.executeWithoutResult(unused -> trySetPersonResigned(message)); else - log.info("ignore user event: {}", JSON.toJSONString(userEvent)); + log.info("ignore node user event: {}", JSON.toJSONString(message)); } private void trySetPersonResigned(OrgUserStatusChangedEvent event) { EssOrg org = essOrgDao.findOrNull(event.getOuId()); - if (org == null) return; + if (org == null) { + log.info("未找到单位: {}", JSON.toJSONString(event)); + return; + } + if (!org.isAuthorized()) { + log.info("ignore unauthorized org: {}", JSON.toJSONString(org)); + return; + } EssPerson person = essPersonDao.findOrNull(event.getOuId(), event.getPersonId()); String subject = IdBuilder.builder() .append(event.getOuId()) .append(event.getPersonId()) .build(); if (person == null) { - essLogDao.log("personResigned", subject, "event", event, "isAuthorized", false); + log.info("ignore person resigned: {}, personNotFound", JSON.toJSONString(event)); + return; + } + if (!person.isAuthorized()) { + log.info("ignore unauthorized person resigned: {}", JSON.toJSONString(person)); return; } EssPerson superAdmin = orgManager.getSuperAdminOrThrow(event.getOuId()); - boolean isSuperAdmin = org.isPotentialSuperAdmin(event.getPersonId()); - essLogDao.log("personResigned", subject, "event", event, "isPotentialSuperAdmin", isSuperAdmin); + boolean isSuperAdmin = superAdmin.getPersonId().equals(event.getPersonId()); + log.info("set person resigned: {}, isSuperAdmin: {}", JSON.toJSONString(person), isSuperAdmin); if (isSuperAdmin) { log.info("ignore super admin resigned: {}", JSON.toJSONString(person)); return; @@ -93,5 +85,4 @@ public class PersonResignListener extends BaseListener eventConsumer.registerHandlers(Collections.singletonList( Event.EventCode.from("org-user:org-user-movement")), this); } - } 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 index 19f9f909..9a94cc63 100644 --- 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 @@ -5,13 +5,16 @@ 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.api.response.CreateContractByFileResponse; import cn.axzo.nanopart.ess.server.dao.EssContractDao; +import cn.axzo.nanopart.ess.server.dao.EssLogDao; 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 lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import java.util.Collections; @@ -23,12 +26,14 @@ import static java.util.stream.Collectors.toList; /** * @author yanglin */ +@Slf4j @Component @RequiredArgsConstructor public class ContractSupport { private final EssOrgDao essOrgDao; private final EssContractDao essContractDao; + private final EssLogDao essLogDao; public void validateCreateContract(CreateContractInfo contract) { checkCreateContractConstraint(contract); @@ -79,4 +84,20 @@ public class ContractSupport { return contract; } + public CreateContractByFileResponse + createDuplicateContractByFileResponse(CreateContractByFileRequest request) { + log.warn("幂等重复, request={}", request); + EssContract savedContract = essContractDao + .findByIdempotentCode(request.getAppCode(), request.getIdempotentCode()) + .orElseThrow(InternalError::new); + essLogDao.log("createContractByFile", savedContract.getEssContractId(), + "message", "幂等重复", "request", request); + CreateContractByFileResponse response = new CreateContractByFileResponse(); + response.setEssContractId(savedContract.getEssContractId()); + response.setEssRecipientIds(savedContract.getApprovers().stream() + .map(Approver::getEssRecipientId) + .collect(toList())); + return response; + } + } \ 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 bf2c0cf6..74f61e9c 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 @@ -13,7 +13,6 @@ import cn.axzo.nanopart.ess.server.entity.EssOrg; import cn.axzo.nanopart.ess.server.entity.EssPerson; import cn.axzo.nanopart.ess.server.utils.BizAssertions; import cn.azxo.framework.common.model.CommonResponse; -import cn.hutool.core.thread.NamedThreadFactory; import lombok.RequiredArgsConstructor; import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Component; @@ -22,11 +21,6 @@ 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; @@ -38,12 +32,6 @@ import static cn.axzo.nanopart.ess.server.utils.BizAssertions.assertResponse; @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; @@ -105,8 +93,4 @@ public class EssSupport { return assertResponse(userProfileServiceApi.getPersonProfiles(personIds)); } - 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/PersonProfiles.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/ess/support/PersonProfiles.java index 96e41120..c9c5b7f5 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 @@ -5,9 +5,7 @@ import cn.axzo.nanopart.ess.server.utils.BizAssertions; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; -import java.util.Collections; import java.util.List; -import java.util.Optional; /** * @author yanglin @@ -17,10 +15,6 @@ public class PersonProfiles { private final List profiles; - public static PersonProfiles empty() { - return new PersonProfiles(Collections.emptyList()); - } - public static PersonProfiles wrap(List profiles) { return new PersonProfiles(profiles); } diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/mq/NanopartEssListener.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/mq/NanopartEssListener.java new file mode 100644 index 00000000..45e9ad6e --- /dev/null +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/mq/NanopartEssListener.java @@ -0,0 +1,38 @@ +package cn.axzo.nanopart.ess.server.mq; + +import cn.axzo.framework.rocketmq.BaseListener; +import cn.axzo.framework.rocketmq.EventConsumer; +import com.alibaba.fastjson.JSON; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.spring.annotation.ConsumeMode; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.stereotype.Component; + +/** + * @author yanglin + */ +@Slf4j +@Component +@RequiredArgsConstructor +@RocketMQMessageListener( + maxReconsumeTimes = 3, + consumeMode = ConsumeMode.ORDERLY, + nameServer = "${rocketmq.name-server}", + topic = "topic_nanopart_${spring.profiles.active}", + consumerGroup = "ess_download_contract_pdf_${spring.application.name}_${spring.profiles.active}" +) +public class NanopartEssListener extends BaseListener + implements RocketMQListener { + + private final EventConsumer eventConsumer; + + @Override + public void onMessage(MessageExt message) { + log.info("receive mq message: {}", JSON.toJSONString(message)); + super.onEvent(message, eventConsumer); + } + +} \ No newline at end of file diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/mq/OrganizationalListener.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/mq/OrganizationalListener.java new file mode 100644 index 00000000..702888df --- /dev/null +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/mq/OrganizationalListener.java @@ -0,0 +1,37 @@ +package cn.axzo.nanopart.ess.server.mq; + +import cn.axzo.framework.rocketmq.BaseListener; +import cn.axzo.framework.rocketmq.EventConsumer; +import com.alibaba.fastjson.JSON; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.spring.annotation.ConsumeMode; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.stereotype.Component; + +/** + * @author yanglin + */ +@Slf4j +@Component +@RequiredArgsConstructor +@RocketMQMessageListener( + maxReconsumeTimes = 3, + consumeMode = ConsumeMode.ORDERLY, + nameServer = "${rocketmq.name-server}", + topic = "topic_organizational_${spring.profiles.active}", + consumerGroup = "ess_person_resign_${spring.application.name}_${spring.profiles.active}" +) +public class OrganizationalListener extends BaseListener implements RocketMQListener { + + private final EventConsumer eventConsumer; + + @Override + public void onMessage(MessageExt message) { + log.info("receive mq message: {}", JSON.toJSONString(message)); + super.onEvent(message, eventConsumer); + } + +} \ No newline at end of file diff --git a/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/utils/BizTransactional.java b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/utils/BizTransactional.java new file mode 100644 index 00000000..b2b84d02 --- /dev/null +++ b/ess/ess-server/src/main/java/cn/axzo/nanopart/ess/server/utils/BizTransactional.java @@ -0,0 +1,19 @@ +package cn.axzo.nanopart.ess.server.utils; + +import org.springframework.transaction.annotation.Transactional; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author yanglin + */ +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +@Transactional(rollbackFor = Exception.class) +public @interface BizTransactional { +} \ No newline at end of file