REQ-3581: 动态获取签署链接

This commit is contained in:
yanglin 2025-03-04 13:32:56 +08:00
parent bf7314a0ca
commit 401705544c
14 changed files with 191 additions and 86 deletions

View File

@ -1,11 +1,20 @@
package cn.axzo.nanopart.ess.api;
import java.util.List;
import javax.validation.Valid;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import cn.axzo.framework.domain.web.result.ApiResult;
import cn.axzo.nanopart.ess.api.domain.EssOrgAndSealInfo;
import cn.axzo.nanopart.ess.api.domain.EssSealPersonInfo;
import cn.axzo.nanopart.ess.api.request.AddSealAuthorizationRequest;
import cn.axzo.nanopart.ess.api.request.AddSealPersonRequest;
import cn.axzo.nanopart.ess.api.request.AssignSignUrlRequest;
import cn.axzo.nanopart.ess.api.request.CreateConsoleLoginUrlRequest;
import cn.axzo.nanopart.ess.api.request.CreateContractByFileRequest;
import cn.axzo.nanopart.ess.api.request.DownloadSingedContractPdfRequest;
@ -15,11 +24,11 @@ import cn.axzo.nanopart.ess.api.request.GetOrgAuthStatesRequest;
import cn.axzo.nanopart.ess.api.request.GetPersonAuthStateRequest;
import cn.axzo.nanopart.ess.api.request.GetSealPersonRequest;
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.request.SaveContractSnapshotRequest;
import cn.axzo.nanopart.ess.api.response.AssignSignUrlResponse;
import cn.axzo.nanopart.ess.api.response.CreateConsoleLoginUrlResponse;
import cn.axzo.nanopart.ess.api.response.CreateContractByFileResponse;
import cn.axzo.nanopart.ess.api.response.DownloadSingedContractPdfResponse;
@ -27,13 +36,6 @@ 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;
import javax.validation.Valid;
import java.util.List;
/**
* @author yanglin
@ -110,10 +112,10 @@ public interface EssApi {
@RequestBody @Valid CreateContractByFileRequest request);
/**
* 获取签署链接, 有效期为5分钟
* 获取动态签署链接, 有效期为5分钟
*/
@PostMapping("api/ess/getContractSignUrl")
ApiResult<GetSignUrlResponse> getContractSignUrl(@RequestBody @Valid GetSignUrlRequest request);
@PostMapping("api/ess/assignSignUrl")
ApiResult<AssignSignUrlResponse> assignSignUrl(@RequestBody @Valid AssignSignUrlRequest request);
/**
* 获取合同PDF文件地址. 下载链接有效期为5分钟

View File

@ -16,6 +16,10 @@ import javax.validation.constraints.NotNull;
@Getter
public class OrgPersonInfo implements OrgPerson {
public static OrgPersonInfo create(OrgPerson person) {
return create(person.getOuId(), person.getPersonId());
}
public static OrgPersonInfo create(Long ouId, Long personId) {
OrgPersonInfo orgPersonInfo = new OrgPersonInfo();
orgPersonInfo.setOuId(ouId);

View File

@ -21,6 +21,6 @@ public class SignOption {
/**
* 是否可以转发. false-可以转发 true-不可以转发
*/
private boolean noTransfer = true;
private boolean noTransfer = false;
}

View File

@ -1,21 +1,22 @@
package cn.axzo.nanopart.ess.api.request;
import cn.axzo.nanopart.ess.api.domain.OrgPerson;
import cn.axzo.nanopart.ess.api.enums.SignUrlEndpoint;
import com.alibaba.fastjson.JSON;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import com.alibaba.fastjson.JSON;
import cn.axzo.nanopart.ess.api.domain.OrgPerson;
import cn.axzo.nanopart.ess.api.enums.SignUrlEndpoint;
import lombok.Getter;
import lombok.Setter;
/**
* @author yanglin
*/
@Setter
@Getter
public class GetSignUrlRequest implements OrgPerson {
public class AssignSignUrlRequest implements OrgPerson {
/**
* 合同id
@ -43,11 +44,8 @@ public class GetSignUrlRequest implements OrgPerson {
/**
* 签署方编号, 可以使用该编号指定动态签署方的信息, 该编号在创建合同的时候会返回
* <p/>
* 如果在创建合同的时候存在任意没有指定具体签署人(Approver#signPersion)的情况下, 该字段必传
* <p/>
* 如果在创建合同的时候已经指定了所有的具体签署人(Approver#signPersion), 该字段可以不传
*/
@NotBlank(message = "recipientId不能为空")
private String recipientId;
@Override

View File

@ -10,7 +10,7 @@ import lombok.Setter;
*/
@Setter
@Getter
public class GetSignUrlResponse {
public class AssignSignUrlResponse {
/**
* 有效期为5分钟.

View File

@ -18,7 +18,7 @@
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<version>3.1.1209</version>
<version>3.1.1210</version>
</dependency>
<dependency>
<groupId>cn.axzo.framework</groupId>

View File

@ -1,20 +1,22 @@
package cn.axzo.nanopart.ess.server.dao;
import java.util.List;
import java.util.Optional;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Repository;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
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.server.entity.EssContract;
import cn.axzo.nanopart.ess.server.mapper.EssContractMapper;
import cn.axzo.nanopart.ess.server.utils.BizAssertions;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
/**
* @author yanglin
@ -39,8 +41,10 @@ public class EssContractDao extends ServiceImpl<EssContractMapper, EssContract>
return find(essContractId, false).orElse(null);
}
public EssContract findForUpdateOrNull(String essContractId) {
return find(essContractId, true).orElse(null);
public EssContract getForUpdateOrThrow(String essContractId) {
EssContract contract = find(essContractId, true).orElse(null);
BizAssertions.assertNotNull(contract, "找不到合同信息, 合同id={}", essContractId);
return contract;
}
private Optional<EssContract> find(String essContractId, boolean forUpdate) {
@ -77,6 +81,15 @@ public class EssContractDao extends ServiceImpl<EssContractMapper, EssContract>
.update();
}
public void updateAssigment(EssContract contract) {
if (contract.getAssignment() == null)
return;
lambdaUpdate() //
.eq(EssContract::getId, contract.getId()) //
.set(EssContract::getAssignment, JSON.toJSONString(contract.getAssignment())) //
.update();
}
public void updateExt(EssContract contract) {
if (contract.getRecordExt() == null)
return;

View File

@ -14,4 +14,6 @@ public class Assignment {
private Constraint constraint;
}
private SignPerOrgs signPerOrgs = new SignPerOrgs();
}

View File

@ -0,0 +1,15 @@
package cn.axzo.nanopart.ess.server.entity.domain;
import cn.axzo.nanopart.ess.api.domain.OrgPerson;
import lombok.Getter;
import lombok.Setter;
/**
* @author yanglin
*/
@Setter @Getter
public class SignPerOrg implements OrgPerson {
private Long ouId;
private Long personId;
private String recipientId;
}

View File

@ -0,0 +1,39 @@
package cn.axzo.nanopart.ess.server.entity.domain;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import cn.axzo.nanopart.ess.api.domain.OrgPerson;
import lombok.Getter;
import lombok.Setter;
/**
* @author yanglin
*/
@Setter
@Getter
public class SignPerOrgs {
private List<SignPerOrg> signPerOrg = new ArrayList<>();
public Optional<SignPerOrg> find(Long ouId) {
return signPerOrg.stream() //
.filter(orgPersonInfo -> orgPersonInfo.getOuId().equals(ouId)) //
.findFirst();
}
public void remove(Long ouId) {
signPerOrg.removeIf(org -> org.getOuId().equals(ouId));
}
public void add(OrgPerson person, String recipientId) {
SignPerOrg orgPersonInfo = new SignPerOrg();
orgPersonInfo.setOuId(person.getOuId());
orgPersonInfo.setPersonId(person.getPersonId());
orgPersonInfo.setRecipientId(recipientId);
signPerOrg.add(orgPersonInfo);
}
}

View File

@ -87,7 +87,7 @@ public class ContractManager {
public void revokeContract(RevokeContractRequest request) {
essLogDao.logRequest("revokeContract", request.getEssContractId(), request);
// lock when updating state
EssContract contract = essContractDao.findForUpdateOrNull(request.getEssContractId());
EssContract contract = essContractDao.getForUpdateOrThrow(request.getEssContractId());
BizAssertions.assertFalse(contract.isFinalState(), "合同已是终态 {}, 无法撤销", contract.getState().description());
EssPerson superAdmin = getContractSuperAdmin(contract);
essClient.revokeContract(superAdmin, contract.getEssContractId(), request.getReason());
@ -97,8 +97,7 @@ public class ContractManager {
@BizTransactional
public void updateContractState(EssContract contract, EssContractState state, List<EssApproveDetail> approveDetails,
String essMessage) {
EssContract reload = essContractDao.findForUpdateOrNull(contract.getEssContractId());
BizAssertions.assertNotNull(reload, "合同不存在: {}", contract.getEssContractId());
EssContract reload = essContractDao.getForUpdateOrThrow(contract.getEssContractId());
if (reload.isFinalState()) {
log.warn("合同[{}]已是最终状态[{}], 无法更新状态至{}", reload.getEssContractId(), reload.getState(), state.description());
}

View File

@ -17,6 +17,7 @@ import java.util.regex.Pattern;
import javax.annotation.Resource;
import com.tencentcloudapi.essbasic.v20210526.models.CreateSignUrlsResponse;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
@ -38,6 +39,7 @@ 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;
@ -49,11 +51,13 @@ import com.tencentcloudapi.essbasic.v20210526.models.ChannelDescribeEmployeesRes
import com.tencentcloudapi.essbasic.v20210526.models.ComponentLimit;
import com.tencentcloudapi.essbasic.v20210526.models.CreateConsoleLoginUrlRequest;
import com.tencentcloudapi.essbasic.v20210526.models.CreateConsoleLoginUrlResponse;
import com.tencentcloudapi.essbasic.v20210526.models.CreateFlowForwardsRequest;
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.FlowForwardInfo;
import com.tencentcloudapi.essbasic.v20210526.models.ProxyOrganizationOperator;
import com.tencentcloudapi.essbasic.v20210526.models.SyncProxyOrganizationOperatorsRequest;
import com.tencentcloudapi.essbasic.v20210526.models.UploadFile;
@ -299,21 +303,43 @@ public class EssClient implements InitializingBean {
return response.getFlowResourceUrlInfos()[0].getResourceUrlInfos()[0].getUrl();
}
public String getContractSignUrlPC(String essContractId, EssPerson signPerson) {
void createFlowApprovers(EssPerson superAdmin, String essContractId, String recipientId, EssPerson signPerson) {
FillApproverInfo approver = new FillApproverInfo();
approver.setRecipientId(recipientId);
approver.setOpenId(PersonOpenId.create(signPerson).toOpenId());
approver.setOrganizationOpenId(OrgOpenId.ofPerson(signPerson).toOpenId());
ChannelCreateFlowApproversRequest request = new ChannelCreateFlowApproversRequest();
request.setAgent(agent(superAdmin));
request.setApprovers(new FillApproverInfo[] { approver });
request.setFlowId(essContractId);
request.setFillApproverType(1L);
exec(func() //
.context("ChannelCreateFlowApprovers") //
.subject(idbuilder() //
.append(essContractId) //
.append(signPerson.getPersonId()) //
.build()) //
.request(request) //
.command(() -> manage.ChannelCreateFlowApprovers(request)));
}
public String createPcSignUrl(EssPerson superAdmin, String essContractId, EssPerson signPerson) {
ChannelCreateOrganizationBatchSignUrlRequest request = new ChannelCreateOrganizationBatchSignUrlRequest();
request.setAgent(agent(signPerson));
request.setAgent(agent(superAdmin));
request.setFlowIds(new String[] { essContractId });
request.setOpenId(PersonOpenId.create(signPerson).toOpenId());
ChannelCreateOrganizationBatchSignUrlResponse response = exec(func() //
.context("ChannelCreateOrganizationBatchSignUrl") //
.subject(essContractId) //
.context("ChannelCreateOrganizationBatchSignUrl").subject(idbuilder() //
.append(essContractId) //
.append(signPerson.getPersonId()) //
.build()) //
.request(request) //
.command(() -> manage.ChannelCreateOrganizationBatchSignUrl(request)));
return response.getSignUrl();
}
public CreateSignUrlsResponse getWeixinAppSignUrl(String essContractId, EssPerson superAdmin, OrgPerson signPerson,
String recipientId) {
public String createSignUrls(EssPerson superAdmin, String essContractId, String recipientId,
OrgPerson signPerson) {
CreateSignUrlsRequest request = new CreateSignUrlsRequest();
request.setAgent(agent(superAdmin));
request.setFlowIds(new String[] { essContractId });
@ -323,7 +349,7 @@ public class EssClient implements InitializingBean {
request.setRecipientIds(new String[] { recipientId });
request.setGenerateType("RECIPIENT");
}
return exec(func() //
CreateSignUrlsResponse response = exec(func() //
.context("CreateSignUrls") //
.subject(idbuilder() //
.append(essContractId) //
@ -331,6 +357,25 @@ public class EssClient implements InitializingBean {
.build()) //
.request(request) //
.command(() -> manage.CreateSignUrls(request)));
return response.getSignUrlInfos()[0].getSignUrl();
}
public void forward(EssPerson superAdmin, String essContractId, String recipientId, EssPerson signPerson) {
CreateFlowForwardsRequest request = new CreateFlowForwardsRequest();
request.setAgent(agent(superAdmin));
request.setTargetOpenId(PersonOpenId.create(signPerson).toOpenId());
FlowForwardInfo forward = new FlowForwardInfo();
forward.setFlowId(essContractId);
forward.setRecipientId(recipientId);
request.setFlowForwardInfos(new FlowForwardInfo[] { forward });
exec(func() //
.context("CreateFlowForwards") //
.subject(idbuilder() //
.append(essContractId) //
.append(signPerson.getPersonId()) //
.build()) //
.request(request) //
.command(() -> manage.CreateFlowForwards(request)));
}
public void setEmployeeResigned(EssPerson superAdmin, EssPerson person) {

View File

@ -3,25 +3,15 @@ package cn.axzo.nanopart.ess.server.ess;
import static cn.axzo.nanopart.ess.server.utils.IdBuilder.idbuilder;
import java.util.function.Function;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import com.tencentcloudapi.essbasic.v20210526.models.CreateSignUrlsResponse;
import com.tencentcloudapi.essbasic.v20210526.models.SignUrlInfo;
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.Constraint;
import cn.axzo.nanopart.ess.api.enums.EssContractApproveState;
import cn.axzo.nanopart.ess.api.enums.EssEmbedType.EssSubject;
import cn.axzo.nanopart.ess.api.enums.SignUrlEndpoint;
import cn.axzo.nanopart.ess.api.request.AddSealAuthorizationRequest;
import cn.axzo.nanopart.ess.api.request.AssignSignUrlRequest;
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;
@ -132,38 +122,36 @@ public class EssService {
return new SealAndPerson(seal, sealPerson);
}
public String getContractSignUrl(GetSignUrlRequest request) {
@BizTransactional
public String assignSignUrl(AssignSignUrlRequest request) {
String subject = idbuilder() //
.append(request.getEssContractId()) //
.append(request.getOuId()) //
.append(request.getPersonId()) //
.build();
essLogDao.logRequest("getContractSignUrl", subject, request);
essLogDao.logRequest("assignSignUrl", subject, request);
EssPerson signPerson = essPersonDao.findOrNull(request);
BizAssertions.assertNotNull(signPerson, "当前签署人员未加入单位, 无法签署");
EssContract contract = essContractDao.getOrThrow(request.getEssContractId());
EssContract contract = essContractDao.getForUpdateOrThrow(request.getEssContractId());
BizAssertions.assertFalse(contract.isFinalState(), "合同已是最终状态 {}, 无法签署", contract.getState());
if (contract.getConstraint() == Constraint.ONE_PERSON_PER_ORG)
BizAssertions.assertFalse(contract.isOrgSigned(request.getOuId()), "该单位已签署, 无法再次签署");
Function<String, String> 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.isSignPersonsPreset())
return signUrlFun.apply(null);
BizAssertions.assertFalse(StringUtils.isBlank(request.getRecipientId()), "存在动态签署人的情况下, recipientId不能为空");
EssApproveDetail approveDetail = contract.getApproveDetailOrThrow(request.getRecipientId());
BizAssertions.assertNotEquals(EssContractApproveState.ACCEPT, approveDetail.getState(), "该签署位已签署, 无法再次签署");
Approver approver = contract.getApproverOrThrow(request.getRecipientId());
if (approver.getSignPerson() != null) {
BizAssertions.assertTrue(OrgPerson.equals(approver.getSignPerson(), request), "签署人员不匹配");
return signUrlFun.apply(null);
}
return signUrlFun.apply(request.getRecipientId());
contract.getAssignment().getSignPerOrgs().find(request.getOuId()).ifPresent(org -> {
BizAssertions.assertEquals(org.getRecipientId(), request.getRecipientId(), "单位的签署编号不能变化");
if (!org.getPersonId().equals(request.getPersonId())) {
EssPerson signSuperAdmin = orgManager.getSuperAdminOrThrow(signPerson.getOuId());
essClient.forward(signSuperAdmin, contract.getEssContractId(), request.getRecipientId(), signPerson);
}
});
contract.getAssignment().getSignPerOrgs().remove(request.getOuId());
contract.getAssignment().getSignPerOrgs().add(signPerson, request.getRecipientId());
essContractDao.updateAssigment(contract);
EssPerson superAdmin = contractManager.getContractSuperAdmin(contract);
if (request.getEndpoint() != SignUrlEndpoint.PC)
return essClient.createSignUrls(superAdmin, contract.getEssContractId(), request.getRecipientId(),
signPerson);
essClient.createFlowApprovers(superAdmin, contract.getEssContractId(), request.getRecipientId(), signPerson);
return essClient.createPcSignUrl(superAdmin, contract.getEssContractId(), signPerson);
}
public String getContractPDFUrl(String essContractId) {

View File

@ -22,7 +22,7 @@ import cn.axzo.nanopart.ess.api.request.GetOrgAuthStatesRequest;
import cn.axzo.nanopart.ess.api.request.GetPersonAuthStateRequest;
import cn.axzo.nanopart.ess.api.request.GetSealPersonRequest;
import cn.axzo.nanopart.ess.api.request.GetSealsRequest;
import cn.axzo.nanopart.ess.api.request.GetSignUrlRequest;
import cn.axzo.nanopart.ess.api.request.AssignSignUrlRequest;
import cn.axzo.nanopart.ess.api.request.RemoveSealAuthorizationRequest;
import cn.axzo.nanopart.ess.api.request.RemoveSealPersonRequest;
import cn.axzo.nanopart.ess.api.request.RevokeContractRequest;
@ -34,7 +34,7 @@ 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.api.response.AssignSignUrlResponse;
import cn.axzo.nanopart.ess.server.dao.EssContractDao;
import cn.axzo.nanopart.ess.server.entity.EssContract;
import cn.axzo.nanopart.ess.server.ess.ContractManager;
@ -133,10 +133,10 @@ class ApiController implements EssApi {
}
@Override
public ApiResult<GetSignUrlResponse> getContractSignUrl(GetSignUrlRequest request) {
log.info("getContractSignUrl request={}", request);
GetSignUrlResponse response = new GetSignUrlResponse();
response.setUrl(essService.getContractSignUrl(request));
public ApiResult<AssignSignUrlResponse> assignSignUrl(AssignSignUrlRequest request) {
log.info("assignSignUrl request={}", request);
AssignSignUrlResponse response = new AssignSignUrlResponse();
response.setUrl(essService.assignSignUrl(request));
return ApiResult.ok(response);
}