REQ-3581: 很多东西

This commit is contained in:
yanglin 2025-02-24 17:15:28 +08:00
parent 63eefa1ce3
commit 607347f001
19 changed files with 299 additions and 151 deletions

View File

@ -13,7 +13,8 @@ import java.util.Arrays;
@Getter @Getter
public enum MQEvent { 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; private final String model;

View File

@ -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;
}

View File

@ -18,6 +18,11 @@ public class SaveContractSnapshotRequest {
@NotBlank(message = "essContractId不能为空") @NotBlank(message = "essContractId不能为空")
private String essContractId; private String essContractId;
/**
* 是否重复下载, 即已经下载过了, 但是需要重新下载
*/
private boolean retryDownload = false;
@Override @Override
public String toString() { public String toString() {
return JSON.toJSONString(this); return JSON.toJSONString(this);

View File

@ -9,7 +9,6 @@ import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import java.util.HashSet;
import java.util.Set; import java.util.Set;
/** /**
@ -57,20 +56,10 @@ public class EssOrg extends BaseEntity<EssOrg> {
return recordExt; return recordExt;
} }
public boolean isPotentialSuperAdmin(Long personId) {
if (superAdminPersonId != null && superAdminPersonId.equals(personId))
return true;
return getOrCreateExt().isPotentialSuperAdmin(personId);
}
public boolean isAuthorized() { public boolean isAuthorized() {
return isAuthorized.isYes(); return isAuthorized.isYes();
} }
public boolean isCreateBy(Long personId) {
return personId.equals(createByPersonId);
}
@Override @Override
public String toString() { public String toString() {
return JSON.toJSONString(this); return JSON.toJSONString(this);
@ -81,12 +70,6 @@ public class EssOrg extends BaseEntity<EssOrg> {
private Set<Long> historySuperAdmins; private Set<Long> historySuperAdmins;
public void addHistorySuperAdmin(Long personId) {
if (historySuperAdmins == null)
historySuperAdmins = new HashSet<>();
historySuperAdmins.add(personId);
}
public boolean isPotentialSuperAdmin(Long personId) { public boolean isPotentialSuperAdmin(Long personId) {
return historySuperAdmins != null && historySuperAdmins.contains(personId); return historySuperAdmins != null && historySuperAdmins.contains(personId);
} }

View File

@ -34,7 +34,7 @@ public class EssPerson extends BaseEntity<EssPerson> implements OrgPerson {
private String personName; private String personName;
/** /**
* 是否已经认证. YES: 已认证, NO: 未认证 * 是否已经认证
*/ */
private EssPersonState state; private EssPersonState state;

View File

@ -1,7 +1,6 @@
package cn.axzo.nanopart.ess.server.ess; package cn.axzo.nanopart.ess.server.ess;
import cn.axzo.basics.common.exception.ServiceException; 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.domain.contract.EssApproveDetail;
import cn.axzo.nanopart.ess.api.enums.EssContractState; import cn.axzo.nanopart.ess.api.enums.EssContractState;
import cn.axzo.nanopart.ess.api.request.CreateContractByFileRequest; 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.EssContract;
import cn.axzo.nanopart.ess.server.entity.EssPerson; import cn.axzo.nanopart.ess.server.entity.EssPerson;
import cn.axzo.nanopart.ess.server.ess.domain.JsonObjectAsString; 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.ess.support.ContractSupport;
import cn.axzo.nanopart.ess.server.utils.BizAssertions; 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.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.essbasic.v20210526.models.ApproverItem; import com.tencentcloudapi.essbasic.v20210526.models.ApproverItem;
import com.tencentcloudapi.essbasic.v20210526.models.ChannelCreateFlowByFilesResponse; import com.tencentcloudapi.essbasic.v20210526.models.ChannelCreateFlowByFilesResponse;
@ -21,7 +22,6 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException; import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -54,18 +54,7 @@ public class ContractManager {
try { try {
contract = contractSupport.saveContractByFile(request); contract = contractSupport.saveContractByFile(request);
} catch (DuplicateKeyException e) { } catch (DuplicateKeyException e) {
log.warn("幂等重复, request={}", request, e); return contractSupport.createDuplicateContractByFileResponse(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;
} }
try { try {
EssPerson superAdmin = orgManager.getSuperAdminOrThrow(request.getCreator().getOuId()); EssPerson superAdmin = orgManager.getSuperAdminOrThrow(request.getCreator().getOuId());
@ -94,7 +83,7 @@ public class ContractManager {
} }
} }
@Transactional @BizTransactional
public void revokeContract(RevokeContractRequest request) { public void revokeContract(RevokeContractRequest request) {
essLogDao.logRequest("revokeContract", request.getEssContractId(), request); essLogDao.logRequest("revokeContract", request.getEssContractId(), request);
EssContract contract = essContractDao.getOrThrow(request.getEssContractId()); EssContract contract = essContractDao.getOrThrow(request.getEssContractId());
@ -105,7 +94,7 @@ public class ContractManager {
updateContractState(contract, EssContractState.CANCEL, null, request.getReason()); updateContractState(contract, EssContractState.CANCEL, null, request.getReason());
} }
@Transactional @BizTransactional
public void updateContractState(EssContract contract, public void updateContractState(EssContract contract,
EssContractState state, EssContractState state,
List<EssApproveDetail> approveDetails, List<EssApproveDetail> approveDetails,

View File

@ -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.GetEmbedWebUrlRequest;
import cn.axzo.nanopart.ess.api.request.GetSignUrlRequest; import cn.axzo.nanopart.ess.api.request.GetSignUrlRequest;
import cn.axzo.nanopart.ess.api.request.RemoveSealAuthorizationRequest; 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.api.request.SealAndPersonRequest;
import cn.axzo.nanopart.ess.server.dao.EssContractDao; import cn.axzo.nanopart.ess.server.dao.EssContractDao;
import cn.axzo.nanopart.ess.server.dao.EssLogDao; 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.entity.EssSealPerson;
import cn.axzo.nanopart.ess.server.ess.domain.OrgAndPerson; 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.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.ess.support.OssService;
import cn.axzo.nanopart.ess.server.utils.BizAssertions; 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 cn.axzo.nanopart.ess.server.utils.IdBuilder;
import com.tencentcloudapi.essbasic.v20210526.models.CreateSignUrlsResponse; import com.tencentcloudapi.essbasic.v20210526.models.CreateSignUrlsResponse;
import com.tencentcloudapi.essbasic.v20210526.models.SignUrlInfo; import com.tencentcloudapi.essbasic.v20210526.models.SignUrlInfo;
@ -32,8 +34,6 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import java.util.function.Function; import java.util.function.Function;
@ -54,10 +54,9 @@ public class EssService {
private final EssPersonDao essPersonDao; private final EssPersonDao essPersonDao;
private final OssService ossService; private final OssService ossService;
private final EssLogDao essLogDao; private final EssLogDao essLogDao;
private final EssSupport essSupport; private final EssBroadcaster essBroadcaster;
private final TransactionTemplate transactionTemplate;
@Transactional @BizTransactional
public String createConsoleLoginUrl(CreateConsoleLoginUrlRequest request) { public String createConsoleLoginUrl(CreateConsoleLoginUrlRequest request) {
String subject = IdBuilder.builder() String subject = IdBuilder.builder()
.append(request.getOuId()) .append(request.getOuId())
@ -86,7 +85,7 @@ public class EssService {
contextPerson, request.getEmbedType(), request.getBusinessId()); contextPerson, request.getEmbedType(), request.getBusinessId());
} }
@Transactional @BizTransactional
public void essAddSealAuthorization(AddSealAuthorizationRequest request) { public void essAddSealAuthorization(AddSealAuthorizationRequest request) {
String subject = IdBuilder.builder() String subject = IdBuilder.builder()
.append(request.getEssSealId()) .append(request.getEssSealId())
@ -105,7 +104,7 @@ public class EssService {
orgManager.addSealAuthorization(request.getEssSealId(), request.getPersonId(), request.getOperatorPersonId()); orgManager.addSealAuthorization(request.getEssSealId(), request.getPersonId(), request.getOperatorPersonId());
} }
@Transactional @BizTransactional
public void essRemoveSealAuthorization(RemoveSealAuthorizationRequest request) { public void essRemoveSealAuthorization(RemoveSealAuthorizationRequest request) {
String subject = IdBuilder.builder() String subject = IdBuilder.builder()
.append(request.getEssSealId()) .append(request.getEssSealId())
@ -174,39 +173,50 @@ public class EssService {
EssContract contract = essContractDao.getOrThrow(essContractId); EssContract contract = essContractDao.getOrThrow(essContractId);
if (StringUtils.isNotBlank(contract.getOssFileKey())) if (StringUtils.isNotBlank(contract.getOssFileKey()))
return ossService.getOssUrl(contract.getOssFileKey()); return ossService.getOssUrl(contract.getOssFileKey());
maybeUploadContractToOss(contract);
return getContractPDFUrlFromEss(contract); return getContractPDFUrlFromEss(contract);
} }
public void saveContractSnapshot(String essContractId) { @BizTransactional
EssContract c = transactionTemplate.execute(unused -> { public void saveContractSnapshot(SaveContractSnapshotRequest request) {
EssContract contract = essContractDao.getOrThrow(essContractId); essLogDao.logRequest("saveContractSnapshot", request.getEssContractId(), request);
contract.getOrCreateExt().setShouldDownloadContract(true); EssContract contract = essContractDao.getOrThrow(request.getEssContractId());
essContractDao.updateExt(contract); contract.getOrCreateExt().setShouldDownloadContract(true);
return contract; essContractDao.updateExt(contract);
}); maybeScheduleDownloadContractPDF(contract, request.isRetryDownload());
maybeUploadContractToOss(c);
} }
public void maybeUploadContractToOss(EssContract contract) { public void maybeScheduleDownloadContractPDF(EssContract contract, boolean retryDownload) {
essSupport.asyncExec(() -> { EssContract reload = essContractDao.findOrNull(contract.getEssContractId());
EssContract c = essContractDao.findOrNull(contract.getEssContractId()); if (shouldDownloadContractPDF(reload))
if (c == null || !c.shouldDownloadContract() || StringUtils.isNotBlank(c.getOssFileKey())) essBroadcaster.fireDownloadContractPDF(contract, retryDownload);
return; }
try {
String pdfUrl = getContractPDFUrlFromEss(c); private boolean shouldDownloadContractPDF(EssContract contract) {
String fileName = String.format("%s.pdf", c.getContractName()); return contract != null && contract.shouldDownloadContract();
String fileKey = ossService.uploadToOss(pdfUrl, fileName); }
transactionTemplate.executeWithoutResult(unused -> {
essContractDao.setOssFileKey(c, fileKey); @BizTransactional
essLogDao.log("uploadContractToOss", c.getEssContractId(), "ossFileKey", fileKey); public void downloadContractPDF(EssContract contract, boolean retryDownload) {
}); if (StringUtils.isNotBlank(contract.getOssFileKey()) && !retryDownload) {
log.info("上传合同到OSS成功, essContractId={}", c.getEssContractId()); log.info("合同已下载PDF, contract={}", contract);
} catch (Exception e) { return;
log.warn("上传合同到OSS失败", e); }
essLogDao.log(e, "uploadContractToOss", c.getEssContractId()); 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) { private String getContractPDFUrlFromEss(EssContract contract) {

View File

@ -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.domain.SealPersons;
import cn.axzo.nanopart.ess.server.ess.support.EssSupport; import cn.axzo.nanopart.ess.server.ess.support.EssSupport;
import cn.axzo.nanopart.ess.server.utils.BizAssertions; import cn.axzo.nanopart.ess.server.utils.BizAssertions;
import cn.axzo.nanopart.ess.server.utils.BizTransactional;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.springframework.dao.DuplicateKeyException; import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -49,7 +49,7 @@ public class OrgManager {
// !! login // !! login
@Transactional @BizTransactional
public OrgAndPerson createConsoleLoginUrl(CreateConsoleLoginUrlRequest request) { public OrgAndPerson createConsoleLoginUrl(CreateConsoleLoginUrlRequest request) {
EssOrg org = essOrgDao.findForUpdateOrNull(request.getOuId()); EssOrg org = essOrgDao.findForUpdateOrNull(request.getOuId());
if (org == null) { if (org == null) {
@ -62,7 +62,7 @@ public class OrgManager {
// !! org and person // !! org and person
@Transactional @BizTransactional
public void addAuthorizedOrgPerson(OrgPerson orgPerson) { public void addAuthorizedOrgPerson(OrgPerson orgPerson) {
EssPerson person = getOrCreateOrgPerson(orgPerson); EssPerson person = getOrCreateOrgPerson(orgPerson);
essPersonDao.setState(person, EssPersonState.AUTHORIZED); essPersonDao.setState(person, EssPersonState.AUTHORIZED);
@ -80,24 +80,14 @@ public class OrgManager {
return essPerson; return essPerson;
} }
@Transactional @BizTransactional
public void setOrgAuthorized(Long ouId, Long superAdmin) { public void setOrgAuthorized(Long ouId, Long superAdmin) {
essOrgDao.setOrgAuthorized(ouId, superAdmin); essOrgDao.setOrgAuthorized(ouId, superAdmin);
addSuperAdmin(ouId, superAdmin);
} }
@Transactional @BizTransactional
public void changeSuperAdmin(Long ouId, Long superAdmin) { public void changeSuperAdmin(Long ouId, Long superAdmin) {
essOrgDao.changeSuperAdmin(ouId, 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) { public EssPerson getSuperAdminOrThrow(Long ouId) {
@ -116,7 +106,7 @@ public class OrgManager {
// !! seal // !! seal
@Transactional @BizTransactional
public boolean maybeAddSeal(Long ouId, String essSealId, EssSealType type) { public boolean maybeAddSeal(Long ouId, String essSealId, EssSealType type) {
EssSeal seal = essSealDao.findByEssSealId(essSealId).orElse(null); EssSeal seal = essSealDao.findByEssSealId(essSealId).orElse(null);
if (seal != null) if (seal != null)
@ -138,19 +128,19 @@ public class OrgManager {
essSealDao.updateState(essSealId, state); essSealDao.updateState(essSealId, state);
} }
@Transactional @BizTransactional
public void addSealAuthorization(String essSealId, Long personId, Long authorizedByPersonId) { public void addSealAuthorization(String essSealId, Long personId, Long authorizedByPersonId) {
maybeAddSealPersons(essSealId, Sets.newHashSet(personId)); maybeAddSealPersons(essSealId, Sets.newHashSet(personId));
essSealPersonDao.setPersonAuthorized(essSealId, personId, authorizedByPersonId); essSealPersonDao.setPersonAuthorized(essSealId, personId, authorizedByPersonId);
} }
@Transactional @BizTransactional
public void removeSealAuthorization(String essSealId, Long personId) { public void removeSealAuthorization(String essSealId, Long personId) {
maybeAddSealPersons(essSealId, Sets.newHashSet(personId)); maybeAddSealPersons(essSealId, Sets.newHashSet(personId));
essSealPersonDao.removeSealAuthorization(essSealId, personId); essSealPersonDao.removeSealAuthorization(essSealId, personId);
} }
@Transactional @BizTransactional
public void maybeAddSealPersons(String essSealId, Set<Long> personIds) { public void maybeAddSealPersons(String essSealId, Set<Long> personIds) {
EssSeal seal = essSealDao.findByEssSealId(essSealId).orElse(null); EssSeal seal = essSealDao.findByEssSealId(essSealId).orElse(null);
BizAssertions.assertNotNull(seal, "印章不存在: {}", essSealId); BizAssertions.assertNotNull(seal, "印章不存在: {}", essSealId);
@ -176,7 +166,7 @@ public class OrgManager {
} }
} }
@Transactional @BizTransactional
public void tryRemoveSealPerson(RemoveSealPersonRequest request) { public void tryRemoveSealPerson(RemoveSealPersonRequest request) {
EssSeal seal = essSealDao.findByEssSealId(request.getEssSealId()).orElse(null); EssSeal seal = essSealDao.findByEssSealId(request.getEssSealId()).orElse(null);
BizAssertions.assertNotNull(seal, "印章不存在: {}", request.getEssSealId()); BizAssertions.assertNotNull(seal, "印章不存在: {}", request.getEssSealId());

View File

@ -141,7 +141,7 @@ class ApiController implements EssApi {
@Override @Override
public ApiResult<Void> saveContractSnapshot(SaveContractSnapshotRequest request) { public ApiResult<Void> saveContractSnapshot(SaveContractSnapshotRequest request) {
log.info("saveContractSnapshot request={}", request); log.info("saveContractSnapshot request={}", request);
essService.saveContractSnapshot(request.getEssContractId()); essService.saveContractSnapshot(request);
return ApiResult.ok(); return ApiResult.ok();
} }

View File

@ -160,7 +160,7 @@ class CallbackController implements EssCallbackApi, InitializingBean {
log.warn("contract not found: {}", changes.getFlowId()); log.warn("contract not found: {}", changes.getFlowId());
} else { } else {
contractManager.updateContractState(contract, state, approveDetails, changes.getFlowMessage()); contractManager.updateContractState(contract, state, approveDetails, changes.getFlowMessage());
essService.maybeUploadContractToOss(contract); essService.maybeScheduleDownloadContractPDF(contract, false);
} }
return changes.getFlowId(); return changes.getFlowId();
}); });

View File

@ -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);
}
}

View File

@ -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.basics.common.BeanMapper;
import cn.axzo.framework.rocketmq.Event; import cn.axzo.framework.rocketmq.Event;
import cn.axzo.framework.rocketmq.EventProducer; import cn.axzo.framework.rocketmq.EventProducer;
import cn.axzo.nanopart.ess.api.domain.contract.EssContractInfo; import cn.axzo.nanopart.ess.api.domain.contract.EssContractInfo;
import cn.axzo.nanopart.ess.api.enums.MQEvent; 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.api.mq.EssContractStateChangeMessage;
import cn.axzo.nanopart.ess.server.dao.EssContractDao; import cn.axzo.nanopart.ess.server.dao.EssContractDao;
import cn.axzo.nanopart.ess.server.entity.EssContract; import cn.axzo.nanopart.ess.server.entity.EssContract;
@ -16,12 +17,12 @@ import org.springframework.stereotype.Component;
*/ */
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
class EssBroadcaster { public class EssBroadcaster {
private final EssContractDao essContractDao; private final EssContractDao essContractDao;
protected final EventProducer<?> eventProducer; protected final EventProducer<?> eventProducer;
void fireContractStateChanged(EssContract contract) { public void fireContractStateChanged(EssContract contract) {
EssContract reloadContract = essContractDao.findOrNull(contract.getEssContractId()); EssContract reloadContract = essContractDao.findOrNull(contract.getEssContractId());
if (reloadContract == null) if (reloadContract == null)
return; return;
@ -36,4 +37,17 @@ class EssBroadcaster {
.build()); .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());
}
} }

View File

@ -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.Event;
import cn.axzo.framework.rocketmq.EventConsumer; import cn.axzo.framework.rocketmq.EventConsumer;
import cn.axzo.framework.rocketmq.EventHandler; import cn.axzo.framework.rocketmq.EventHandler;
import cn.axzo.maokai.api.domain.event.user.OrgUserStatusChangedEvent; 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.EssOrgDao;
import cn.axzo.nanopart.ess.server.dao.EssPersonDao; import cn.axzo.nanopart.ess.server.dao.EssPersonDao;
import cn.axzo.nanopart.ess.server.entity.EssOrg; import cn.axzo.nanopart.ess.server.entity.EssOrg;
import cn.axzo.nanopart.ess.server.entity.EssPerson; 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 cn.axzo.nanopart.ess.server.utils.IdBuilder;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; 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.beans.factory.InitializingBean;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionTemplate; import org.springframework.transaction.support.TransactionTemplate;
@ -30,55 +26,51 @@ import java.util.Collections;
@Slf4j @Slf4j
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
@RocketMQMessageListener( public class PersonResignHandler implements EventHandler, InitializingBean {
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<MessageExt>, EventHandler, InitializingBean {
private final EventConsumer eventConsumer; private final EventConsumer eventConsumer;
private final EssClient essClient; private final EssClient essClient;
private final OrgManager orgManager; private final OrgManager orgManager;
private final EssOrgDao essOrgDao; private final EssOrgDao essOrgDao;
private final EssPersonDao essPersonDao; private final EssPersonDao essPersonDao;
private final EssLogDao essLogDao;
private final TransactionTemplate transactionTemplate; private final TransactionTemplate transactionTemplate;
@Override
public void onMessage(MessageExt message) {
log.info("receive mq message: {}", JSON.toJSONString(message));
super.onEvent(message, eventConsumer);
}
@Override @Override
public void onEvent(Event event, EventConsumer.Context context) { public void onEvent(Event event, EventConsumer.Context context) {
OrgUserStatusChangedEvent userEvent = event.normalizedData(OrgUserStatusChangedEvent.class); OrgUserStatusChangedEvent message = event.normalizedData(OrgUserStatusChangedEvent.class);
log.info("receive user event: {}", JSON.toJSONString(userEvent)); log.info("receive node user event: {}", JSON.toJSONString(message));
if (userEvent.getStatusCode() == 6) if (message.getStatusCode() == 6)
transactionTemplate.executeWithoutResult(unused -> trySetPersonResigned(userEvent)); transactionTemplate.executeWithoutResult(unused -> trySetPersonResigned(message));
else else
log.info("ignore user event: {}", JSON.toJSONString(userEvent)); log.info("ignore node user event: {}", JSON.toJSONString(message));
} }
private void trySetPersonResigned(OrgUserStatusChangedEvent event) { private void trySetPersonResigned(OrgUserStatusChangedEvent event) {
EssOrg org = essOrgDao.findOrNull(event.getOuId()); 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()); EssPerson person = essPersonDao.findOrNull(event.getOuId(), event.getPersonId());
String subject = IdBuilder.builder() String subject = IdBuilder.builder()
.append(event.getOuId()) .append(event.getOuId())
.append(event.getPersonId()) .append(event.getPersonId())
.build(); .build();
if (person == null) { 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; return;
} }
EssPerson superAdmin = orgManager.getSuperAdminOrThrow(event.getOuId()); EssPerson superAdmin = orgManager.getSuperAdminOrThrow(event.getOuId());
boolean isSuperAdmin = org.isPotentialSuperAdmin(event.getPersonId()); boolean isSuperAdmin = superAdmin.getPersonId().equals(event.getPersonId());
essLogDao.log("personResigned", subject, "event", event, "isPotentialSuperAdmin", isSuperAdmin); log.info("set person resigned: {}, isSuperAdmin: {}", JSON.toJSONString(person), isSuperAdmin);
if (isSuperAdmin) { if (isSuperAdmin) {
log.info("ignore super admin resigned: {}", JSON.toJSONString(person)); log.info("ignore super admin resigned: {}", JSON.toJSONString(person));
return; return;
@ -93,5 +85,4 @@ public class PersonResignListener extends BaseListener
eventConsumer.registerHandlers(Collections.singletonList( eventConsumer.registerHandlers(Collections.singletonList(
Event.EventCode.from("org-user:org-user-movement")), this); Event.EventCode.from("org-user:org-user-movement")), this);
} }
} }

View File

@ -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.Constraint;
import cn.axzo.nanopart.ess.api.enums.EssContractState; import cn.axzo.nanopart.ess.api.enums.EssContractState;
import cn.axzo.nanopart.ess.api.request.CreateContractByFileRequest; 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.EssContractDao;
import cn.axzo.nanopart.ess.server.dao.EssLogDao;
import cn.axzo.nanopart.ess.server.dao.EssOrgDao; import cn.axzo.nanopart.ess.server.dao.EssOrgDao;
import cn.axzo.nanopart.ess.server.entity.EssContract; import cn.axzo.nanopart.ess.server.entity.EssContract;
import cn.axzo.nanopart.ess.server.entity.EssOrg; import cn.axzo.nanopart.ess.server.entity.EssOrg;
import cn.axzo.nanopart.ess.server.utils.BizAssertions; import cn.axzo.nanopart.ess.server.utils.BizAssertions;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.Collections; import java.util.Collections;
@ -23,12 +26,14 @@ import static java.util.stream.Collectors.toList;
/** /**
* @author yanglin * @author yanglin
*/ */
@Slf4j
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
public class ContractSupport { public class ContractSupport {
private final EssOrgDao essOrgDao; private final EssOrgDao essOrgDao;
private final EssContractDao essContractDao; private final EssContractDao essContractDao;
private final EssLogDao essLogDao;
public void validateCreateContract(CreateContractInfo contract) { public void validateCreateContract(CreateContractInfo contract) {
checkCreateContractConstraint(contract); checkCreateContractConstraint(contract);
@ -79,4 +84,20 @@ public class ContractSupport {
return contract; 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;
}
} }

View File

@ -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.entity.EssPerson;
import cn.axzo.nanopart.ess.server.utils.BizAssertions; import cn.axzo.nanopart.ess.server.utils.BizAssertions;
import cn.azxo.framework.common.model.CommonResponse; import cn.azxo.framework.common.model.CommonResponse;
import cn.hutool.core.thread.NamedThreadFactory;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -22,11 +21,6 @@ import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Optional; 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 cn.axzo.nanopart.ess.server.utils.BizAssertions.assertResponse;
@ -38,12 +32,6 @@ import static cn.axzo.nanopart.ess.server.utils.BizAssertions.assertResponse;
@RequiredArgsConstructor @RequiredArgsConstructor
public class EssSupport { 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 OrganizationalUnitApi organizationalUnitApi;
private final UserProfileServiceApi userProfileServiceApi; private final UserProfileServiceApi userProfileServiceApi;
@ -105,8 +93,4 @@ public class EssSupport {
return assertResponse(userProfileServiceApi.getPersonProfiles(personIds)); return assertResponse(userProfileServiceApi.getPersonProfiles(personIds));
} }
public void asyncExec(Runnable task) {
executor.execute(task);
}
} }

View File

@ -5,9 +5,7 @@ import cn.axzo.nanopart.ess.server.utils.BizAssertions;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional;
/** /**
* @author yanglin * @author yanglin
@ -17,10 +15,6 @@ public class PersonProfiles {
private final List<PersonProfileDto> profiles; private final List<PersonProfileDto> profiles;
public static PersonProfiles empty() {
return new PersonProfiles(Collections.emptyList());
}
public static PersonProfiles wrap(List<PersonProfileDto> profiles) { public static PersonProfiles wrap(List<PersonProfileDto> profiles) {
return new PersonProfiles(profiles); return new PersonProfiles(profiles);
} }

View File

@ -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<MessageExt> {
private final EventConsumer eventConsumer;
@Override
public void onMessage(MessageExt message) {
log.info("receive mq message: {}", JSON.toJSONString(message));
super.onEvent(message, eventConsumer);
}
}

View File

@ -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<MessageExt> {
private final EventConsumer eventConsumer;
@Override
public void onMessage(MessageExt message) {
log.info("receive mq message: {}", JSON.toJSONString(message));
super.onEvent(message, eventConsumer);
}
}

View File

@ -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 {
}