fadd
This commit is contained in:
204
internal/infrastructure/external/esign/esign_provider.go
vendored
Normal file
204
internal/infrastructure/external/esign/esign_provider.go
vendored
Normal file
@@ -0,0 +1,204 @@
|
||||
package esign
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"hyapi-server/internal/domains/certification/enums"
|
||||
"hyapi-server/internal/domains/certification/ports"
|
||||
sharedesign "hyapi-server/internal/shared/esign"
|
||||
)
|
||||
|
||||
type Provider struct {
|
||||
client *sharedesign.Client
|
||||
}
|
||||
|
||||
func NewProvider(client *sharedesign.Client) *Provider {
|
||||
return &Provider{client: client}
|
||||
}
|
||||
|
||||
func (p *Provider) Platform() enums.SignPlatform {
|
||||
return enums.SignPlatformEsign
|
||||
}
|
||||
|
||||
func (p *Provider) GenerateEnterpriseAuth(ctx context.Context, req *ports.EnterpriseAuthRequest) (*ports.AuthLinkResult, error) {
|
||||
_ = ctx
|
||||
res, err := p.client.GenerateEnterpriseAuth(&sharedesign.EnterpriseAuthRequest{
|
||||
CompanyName: req.CompanyName,
|
||||
UnifiedSocialCode: req.UnifiedSocialCode,
|
||||
LegalPersonName: req.LegalPersonName,
|
||||
LegalPersonID: req.LegalPersonID,
|
||||
TransactorName: req.TransactorName,
|
||||
TransactorMobile: req.TransactorMobile,
|
||||
TransactorID: req.TransactorID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ports.AuthLinkResult{
|
||||
AuthFlowID: res.AuthFlowID,
|
||||
AuthURL: res.AuthURL,
|
||||
AuthShortURL: firstNonEmpty(res.AuthShortURL, res.AuthURL),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Provider) QueryOrgVerified(ctx context.Context, req *ports.OrgIdentityQuery) (bool, error) {
|
||||
_ = ctx
|
||||
identity, err := p.client.QueryOrgIdentityInfo(&sharedesign.QueryOrgIdentityRequest{
|
||||
OrgName: req.OrgName,
|
||||
OrgIDCardNum: req.OrgIdentNo,
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return identity != nil && identity.Data.RealnameStatus == 1, nil
|
||||
}
|
||||
|
||||
func (p *Provider) GenerateContractFile(ctx context.Context, req *ports.ContractGenerateRequest) (*ports.ContractFileResult, error) {
|
||||
_ = ctx
|
||||
components := map[string]string{
|
||||
"jfqym": req.CompanyName,
|
||||
"jfqym2": req.CompanyName,
|
||||
"jfsqdb": req.AuthorizedRepName,
|
||||
"jftyshxydm": req.UnifiedSocialCode,
|
||||
"jflxdz": req.EnterpriseAddress,
|
||||
"xybh": req.AgreementNo,
|
||||
"qsrq1": req.SignDate,
|
||||
"qsrq3": req.SignDate,
|
||||
"qsrq2": req.SignDate,
|
||||
}
|
||||
res, err := p.client.FillTemplate(components)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ports.ContractFileResult{
|
||||
FileID: res.FileID,
|
||||
FileDownloadURL: res.FileDownloadUrl,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Provider) CreateSignFlow(ctx context.Context, req *ports.SignFlowCreateRequest) (*ports.SignFlowResult, error) {
|
||||
_ = ctx
|
||||
flowID, err := p.client.CreateSignFlow(&sharedesign.CreateSignFlowRequest{
|
||||
FileID: req.FileID,
|
||||
SignerAccount: req.PartyAUSCC,
|
||||
SignerName: req.PartyAName,
|
||||
TransactorPhone: req.TransactorMobile,
|
||||
TransactorName: req.TransactorName,
|
||||
TransactorIDCardNum: req.TransactorID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if req.SkipActorURL {
|
||||
return &ports.SignFlowResult{SignFlowID: flowID}, nil
|
||||
}
|
||||
longURL, shortURL, err := p.client.GetSignURL(flowID, req.TransactorMobile, req.PartyAName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ports.SignFlowResult{
|
||||
SignFlowID: flowID,
|
||||
SignURL: firstNonEmpty(longURL, shortURL),
|
||||
ShortURL: shortURL,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Provider) GetActorSignURL(ctx context.Context, req *ports.GetActorSignURLRequest) (*ports.ActorSignURLResult, error) {
|
||||
_ = ctx
|
||||
if req == nil || strings.TrimSpace(req.SignFlowID) == "" {
|
||||
return nil, fmt.Errorf("signFlowId 不能为空")
|
||||
}
|
||||
longURL, shortURL, err := p.client.GetSignURL(req.SignFlowID, req.TransactorMobile, req.PartyAName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ports.ActorSignURLResult{
|
||||
SignFlowID: req.SignFlowID,
|
||||
EmbedURL: firstNonEmpty(longURL, shortURL),
|
||||
ShortURL: shortURL,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Provider) GetSignTaskPreviewURL(ctx context.Context, signFlowID string) (*ports.SignTaskPreviewURLResult, error) {
|
||||
_ = ctx
|
||||
// e签宝无独立预览接口:用已签/合同文件下载链作为预览源(前端用 PDF 打开)
|
||||
files, err := p.DownloadSignedFiles(ctx, signFlowID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(files) == 0 || strings.TrimSpace(files[0].DownloadURL) == "" {
|
||||
return nil, fmt.Errorf("未获取到可预览的合同文件")
|
||||
}
|
||||
return &ports.SignTaskPreviewURLResult{
|
||||
SignFlowID: signFlowID,
|
||||
PreviewURL: files[0].DownloadURL,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Provider) QuerySignStatus(ctx context.Context, flowID string) (*ports.SignStatusResult, error) {
|
||||
_ = ctx
|
||||
detail, err := p.client.QuerySignFlowDetail(flowID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
status := detail.Data.SignFlowStatus
|
||||
return &ports.SignStatusResult{
|
||||
SignFlowID: flowID,
|
||||
Status: fmt.Sprintf("%d", status),
|
||||
Completed: status == 2,
|
||||
Rejected: status == 7,
|
||||
Expired: status == 5,
|
||||
Message: detail.Data.SignFlowDescription,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Provider) DownloadSignedFiles(ctx context.Context, flowID string) ([]*ports.SignedFile, error) {
|
||||
_ = ctx
|
||||
res, err := p.client.DownloadSignedFile(flowID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
files := make([]*ports.SignedFile, 0, len(res.Data.Files))
|
||||
for _, f := range res.Data.Files {
|
||||
files = append(files, &ports.SignedFile{DownloadURL: f.DownloadUrl})
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func (p *Provider) VerifyCallback(ctx context.Context, headers map[string]string, body []byte) error {
|
||||
_ = ctx
|
||||
_ = headers
|
||||
_ = body
|
||||
// e签宝现有回调走独立验签逻辑,此处保持兼容(由原 HandleEsignCallback 处理)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Provider) ParseCallback(ctx context.Context, headers map[string]string, body []byte) (*ports.CallbackEvent, error) {
|
||||
_ = ctx
|
||||
_ = headers
|
||||
var raw map[string]interface{}
|
||||
_ = json.Unmarshal(body, &raw)
|
||||
action, _ := raw["action"].(string)
|
||||
ev := &ports.CallbackEvent{Event: action, Raw: raw}
|
||||
if action == "AUTH_PASS" {
|
||||
ev.AuthPassed = true
|
||||
}
|
||||
if action == "SIGN_FLOW_COMPLETE" || action == "SIGN_MISSON_COMPLETE" {
|
||||
ev.SignCompleted = true
|
||||
}
|
||||
return ev, nil
|
||||
}
|
||||
|
||||
func firstNonEmpty(values ...string) string {
|
||||
for _, v := range values {
|
||||
if strings.TrimSpace(v) != "" {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var _ ports.SignPlatformProvider = (*Provider)(nil)
|
||||
247
internal/infrastructure/external/fadada/fadada_provider.go
vendored
Normal file
247
internal/infrastructure/external/fadada/fadada_provider.go
vendored
Normal file
@@ -0,0 +1,247 @@
|
||||
package fadada
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"hyapi-server/internal/domains/certification/enums"
|
||||
"hyapi-server/internal/domains/certification/ports"
|
||||
sharedfadada "hyapi-server/internal/shared/fadada"
|
||||
)
|
||||
|
||||
type Provider struct {
|
||||
client *sharedfadada.Client
|
||||
}
|
||||
|
||||
func NewProvider(client *sharedfadada.Client) *Provider {
|
||||
return &Provider{client: client}
|
||||
}
|
||||
|
||||
func (p *Provider) Platform() enums.SignPlatform {
|
||||
return enums.SignPlatformFadada
|
||||
}
|
||||
|
||||
func (p *Provider) GenerateEnterpriseAuth(ctx context.Context, req *ports.EnterpriseAuthRequest) (*ports.AuthLinkResult, error) {
|
||||
_ = ctx
|
||||
res, err := p.client.GenerateEnterpriseAuth(&sharedfadada.EnterpriseAuthRequest{
|
||||
ClientCorpID: req.ClientCorpID,
|
||||
ClientUserID: req.ClientUserID,
|
||||
CompanyName: req.CompanyName,
|
||||
UnifiedSocialCode: req.UnifiedSocialCode,
|
||||
LegalPersonName: req.LegalPersonName,
|
||||
LegalPersonID: req.LegalPersonID,
|
||||
TransactorName: req.TransactorName,
|
||||
TransactorMobile: req.TransactorMobile,
|
||||
TransactorID: req.TransactorID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ports.AuthLinkResult{
|
||||
AuthFlowID: res.AuthFlowID,
|
||||
AuthURL: res.AuthURL,
|
||||
AuthShortURL: firstNonEmpty(res.AuthShortURL, res.AuthURL),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Provider) QueryOrgVerified(ctx context.Context, req *ports.OrgIdentityQuery) (bool, error) {
|
||||
_ = ctx
|
||||
return p.client.QueryOrgVerified(&sharedfadada.QueryOrgIdentityRequest{
|
||||
CorpName: req.OrgName,
|
||||
CorpIdentNo: req.OrgIdentNo,
|
||||
})
|
||||
}
|
||||
|
||||
// GenerateContractFile 法大大走签署任务模板(/sign-task/create-with-template),
|
||||
// 此处返回空结果,实际填单由 CreateSignFlow 通过 /sign-task/field/fill-values 完成。
|
||||
func (p *Provider) GenerateContractFile(ctx context.Context, req *ports.ContractGenerateRequest) (*ports.ContractFileResult, error) {
|
||||
_ = ctx
|
||||
_ = req
|
||||
return &ports.ContractFileResult{FileID: "", FileDownloadURL: ""}, nil
|
||||
}
|
||||
|
||||
func (p *Provider) CreateSignFlow(ctx context.Context, req *ports.SignFlowCreateRequest) (*ports.SignFlowResult, error) {
|
||||
_ = ctx
|
||||
createReq := &sharedfadada.CreateSignFlowRequest{
|
||||
Subject: req.Subject,
|
||||
PartyAOpenCorpID: req.PartyAOpenCorpID,
|
||||
PartyAName: req.PartyAName,
|
||||
PartyAUSCC: req.PartyAUSCC,
|
||||
TransactorName: req.TransactorName,
|
||||
TransactorMobile: req.TransactorMobile,
|
||||
TransactorID: req.TransactorID,
|
||||
TransReferenceID: req.TransReferenceID,
|
||||
}
|
||||
if req.Fill != nil {
|
||||
createReq.Fill = &sharedfadada.ContractFillRequest{
|
||||
AgreementNo: req.Fill.AgreementNo,
|
||||
CompanyName: req.Fill.CompanyName,
|
||||
UnifiedSocialCode: req.Fill.UnifiedSocialCode,
|
||||
EnterpriseAddress: req.Fill.EnterpriseAddress,
|
||||
AuthorizedRepName: req.Fill.AuthorizedRepName,
|
||||
SignDate: req.Fill.SignDate,
|
||||
FileName: req.Fill.FileName,
|
||||
}
|
||||
} else {
|
||||
// 签署模板创建必须填控件
|
||||
createReq.Fill = &sharedfadada.ContractFillRequest{
|
||||
CompanyName: req.PartyAName,
|
||||
UnifiedSocialCode: req.PartyAUSCC,
|
||||
AuthorizedRepName: req.TransactorName,
|
||||
}
|
||||
}
|
||||
|
||||
createRes, err := p.client.CreateSignFlow(createReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if req.SkipActorURL {
|
||||
return &ports.SignFlowResult{
|
||||
SignFlowID: createRes.SignTaskID,
|
||||
}, nil
|
||||
}
|
||||
urlRes, err := p.client.GetSignURL(createRes.SignTaskID, sharedfadada.ActorIDPartyA)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
embedURL := strings.TrimSpace(urlRes.EmbedURL)
|
||||
if embedURL == "" {
|
||||
return nil, fmt.Errorf("法大大未返回可嵌入签署链接 actorSignTaskEmbedUrl")
|
||||
}
|
||||
return &ports.SignFlowResult{
|
||||
SignFlowID: createRes.SignTaskID,
|
||||
SignURL: embedURL,
|
||||
ShortURL: strings.TrimSpace(urlRes.SignURL),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Provider) GetActorSignURL(ctx context.Context, req *ports.GetActorSignURLRequest) (*ports.ActorSignURLResult, error) {
|
||||
_ = ctx
|
||||
if req == nil || strings.TrimSpace(req.SignFlowID) == "" {
|
||||
return nil, fmt.Errorf("signFlowId 不能为空")
|
||||
}
|
||||
urlRes, err := p.client.GetSignURL(strings.TrimSpace(req.SignFlowID), sharedfadada.ActorIDPartyA)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
embedURL := strings.TrimSpace(urlRes.EmbedURL)
|
||||
if embedURL == "" {
|
||||
return nil, fmt.Errorf("法大大未返回可嵌入签署链接 actorSignTaskEmbedUrl")
|
||||
}
|
||||
return &ports.ActorSignURLResult{
|
||||
SignFlowID: req.SignFlowID,
|
||||
EmbedURL: embedURL,
|
||||
ShortURL: strings.TrimSpace(urlRes.SignURL),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Provider) GetSignTaskPreviewURL(ctx context.Context, signFlowID string) (*ports.SignTaskPreviewURLResult, error) {
|
||||
_ = ctx
|
||||
signFlowID = strings.TrimSpace(signFlowID)
|
||||
if signFlowID == "" {
|
||||
return nil, fmt.Errorf("signFlowId 不能为空")
|
||||
}
|
||||
res, err := p.client.GetSignTaskPreviewURL(signFlowID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ports.SignTaskPreviewURLResult{
|
||||
SignFlowID: signFlowID,
|
||||
PreviewURL: res.PreviewURL,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Provider) QuerySignStatus(ctx context.Context, flowID string) (*ports.SignStatusResult, error) {
|
||||
_ = ctx
|
||||
st, err := p.client.QuerySignStatus(flowID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ports.SignStatusResult{
|
||||
SignFlowID: flowID,
|
||||
Status: st.SignTaskStatus,
|
||||
Completed: st.Completed,
|
||||
Rejected: st.Terminated,
|
||||
Expired: false,
|
||||
Message: firstNonEmpty(st.TerminationNote, st.RevokeReason),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Provider) DownloadSignedFiles(ctx context.Context, flowID string) ([]*ports.SignedFile, error) {
|
||||
_ = ctx
|
||||
res, err := p.client.DownloadSignedFiles(flowID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
files := make([]*ports.SignedFile, 0, len(res.Files))
|
||||
for _, f := range res.Files {
|
||||
files = append(files, &ports.SignedFile{
|
||||
DownloadURL: f.DownloadURL,
|
||||
DownloadID: f.DownloadID,
|
||||
})
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func (p *Provider) VerifyCallback(ctx context.Context, headers map[string]string, body []byte) error {
|
||||
_ = ctx
|
||||
bizContent := extractBizContent(body)
|
||||
return p.client.VerifyCallback(headers, bizContent)
|
||||
}
|
||||
|
||||
func (p *Provider) ParseCallback(ctx context.Context, headers map[string]string, body []byte) (*ports.CallbackEvent, error) {
|
||||
_ = ctx
|
||||
bizContent := extractBizContent(body)
|
||||
ev, err := p.client.ParseCallback(headers, bizContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := &ports.CallbackEvent{
|
||||
Event: ev.Event,
|
||||
SignFlowID: ev.SignTaskID,
|
||||
AuthFlowID: firstNonEmpty(ev.ClientCorpID, ev.OpenCorpID),
|
||||
Raw: ev.Raw,
|
||||
}
|
||||
if ev.IsSignCompletedCallback() {
|
||||
out.SignCompleted = true
|
||||
}
|
||||
// 企业授权/认证成功事件
|
||||
switch strings.ToLower(ev.Event) {
|
||||
case "corp-authorize", "corp-authorize-success", "corporation_authorize", "auth-pass":
|
||||
out.AuthPassed = true
|
||||
}
|
||||
if strings.EqualFold(ev.AuthResult, "success") || strings.EqualFold(ev.AuthResult, "pass") {
|
||||
out.AuthPassed = true
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func extractBizContent(body []byte) string {
|
||||
raw := strings.TrimSpace(string(body))
|
||||
if raw == "" {
|
||||
return ""
|
||||
}
|
||||
if strings.HasPrefix(raw, "{") {
|
||||
return raw
|
||||
}
|
||||
values, err := url.ParseQuery(raw)
|
||||
if err == nil {
|
||||
if v := values.Get("bizContent"); v != "" {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return raw
|
||||
}
|
||||
|
||||
func firstNonEmpty(values ...string) string {
|
||||
for _, v := range values {
|
||||
if strings.TrimSpace(v) != "" {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var _ ports.SignPlatformProvider = (*Provider)(nil)
|
||||
52
internal/infrastructure/external/signplatform/registry.go
vendored
Normal file
52
internal/infrastructure/external/signplatform/registry.go
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
package signplatform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"hyapi-server/internal/domains/certification/enums"
|
||||
"hyapi-server/internal/domains/certification/ports"
|
||||
)
|
||||
|
||||
type Registry struct {
|
||||
providers map[enums.SignPlatform]ports.SignPlatformProvider
|
||||
order []enums.SignPlatform
|
||||
}
|
||||
|
||||
func NewRegistry(providers ...ports.SignPlatformProvider) *Registry {
|
||||
r := &Registry{
|
||||
providers: make(map[enums.SignPlatform]ports.SignPlatformProvider, len(providers)),
|
||||
order: make([]enums.SignPlatform, 0, len(providers)),
|
||||
}
|
||||
for _, p := range providers {
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
platform := p.Platform()
|
||||
r.providers[platform] = p
|
||||
r.order = append(r.order, platform)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Registry) Get(platform enums.SignPlatform) (ports.SignPlatformProvider, error) {
|
||||
if platform == "" {
|
||||
platform = enums.DefaultSignPlatform()
|
||||
}
|
||||
p, ok := r.providers[platform]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("不支持的签署平台: %s", platform)
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (r *Registry) List() []ports.SignPlatformProvider {
|
||||
out := make([]ports.SignPlatformProvider, 0, len(r.order))
|
||||
for _, platform := range r.order {
|
||||
if p, ok := r.providers[platform]; ok {
|
||||
out = append(out, p)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
var _ ports.SignPlatformRegistry = (*Registry)(nil)
|
||||
@@ -305,11 +305,10 @@ func (s *QiNiuStorageService) DownloadFile(ctx context.Context, fileURL string)
|
||||
isTimeout = true
|
||||
} else if netErr, ok := err.(interface{ Timeout() bool }); ok && netErr.Timeout() {
|
||||
isTimeout = true
|
||||
} else if errStr := err.Error();
|
||||
errStr == "context deadline exceeded" ||
|
||||
errStr == "timeout" ||
|
||||
errStr == "Client.Timeout exceeded" ||
|
||||
errStr == "net/http: request canceled" {
|
||||
} else if errStr := err.Error(); errStr == "context deadline exceeded" ||
|
||||
errStr == "timeout" ||
|
||||
errStr == "Client.Timeout exceeded" ||
|
||||
errStr == "net/http: request canceled" {
|
||||
isTimeout = true
|
||||
}
|
||||
|
||||
|
||||
@@ -154,6 +154,59 @@ func (h *CertificationHandler) ConfirmAuth(c *gin.Context) {
|
||||
h.response.Success(c, result, "状态确认成功")
|
||||
}
|
||||
|
||||
// ListSignPlatforms 可选签署平台列表
|
||||
func (h *CertificationHandler) ListSignPlatforms(c *gin.Context) {
|
||||
result, err := h.appService.ListSignPlatforms(c.Request.Context())
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Success(c, result, "获取签署平台列表成功")
|
||||
}
|
||||
|
||||
// SelectSignPlatform 选择签署平台并生成企业认证链接
|
||||
func (h *CertificationHandler) SelectSignPlatform(c *gin.Context) {
|
||||
var cmd commands.SelectSignPlatformCommand
|
||||
cmd.UserID = h.getCurrentUserID(c)
|
||||
if cmd.UserID == "" {
|
||||
h.response.Unauthorized(c, "用户未登录")
|
||||
return
|
||||
}
|
||||
if err := c.ShouldBindJSON(&cmd); err != nil {
|
||||
h.response.BadRequest(c, "请求参数错误")
|
||||
return
|
||||
}
|
||||
result, err := h.appService.SelectSignPlatform(c.Request.Context(), &cmd)
|
||||
if err != nil {
|
||||
h.logger.Error("选择签署平台失败", zap.Error(err), zap.String("user_id", cmd.UserID))
|
||||
h.response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Success(c, result, "已选择签署平台")
|
||||
}
|
||||
|
||||
// HandleFadadaCallback 法大大回调
|
||||
func (h *CertificationHandler) HandleFadadaCallback(c *gin.Context) {
|
||||
headers := make(map[string]string)
|
||||
for key, values := range c.Request.Header {
|
||||
if len(values) > 0 {
|
||||
headers[key] = values[0]
|
||||
}
|
||||
}
|
||||
body, err := io.ReadAll(c.Request.Body)
|
||||
if err != nil {
|
||||
h.logger.Error("读取法大大回调失败", zap.Error(err))
|
||||
c.String(400, "fail")
|
||||
return
|
||||
}
|
||||
if err := h.appService.HandleFadadaCallback(c.Request.Context(), headers, body); err != nil {
|
||||
h.logger.Error("处理法大大回调失败", zap.Error(err))
|
||||
c.String(400, "fail")
|
||||
return
|
||||
}
|
||||
c.Data(200, "application/json", []byte(`{"msg":"success"}`))
|
||||
}
|
||||
|
||||
// ConfirmSign 前端确认是否完成签署
|
||||
// @Summary 前端确认签署状态
|
||||
// @Description 前端轮询确认合同签署是否完成
|
||||
@@ -219,6 +272,61 @@ func (h *CertificationHandler) ApplyContract(c *gin.Context) {
|
||||
h.response.Success(c, result, "合同申请成功")
|
||||
}
|
||||
|
||||
// RefreshContractSignURL 刷新合同签署 iframe 长链
|
||||
// @Summary 刷新合同签署链接
|
||||
// @Description 重新获取可供 iframe 嵌入的签署长链(法大大约 10 分钟/单次有效)
|
||||
// @Tags 认证管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Success 200 {object} responses.ContractSignUrlResponse "刷新成功"
|
||||
// @Failure 400 {object} map[string]interface{} "请求参数错误"
|
||||
// @Failure 401 {object} map[string]interface{} "未认证"
|
||||
// @Router /api/v1/certifications/refresh-contract-sign-url [post]
|
||||
func (h *CertificationHandler) RefreshContractSignURL(c *gin.Context) {
|
||||
userID := h.getCurrentUserID(c)
|
||||
if userID == "" {
|
||||
h.response.Unauthorized(c, "用户未登录")
|
||||
return
|
||||
}
|
||||
|
||||
result, err := h.appService.RefreshContractSignURL(c.Request.Context(), userID)
|
||||
if err != nil {
|
||||
h.logger.Error("刷新签署链接失败", zap.Error(err), zap.String("user_id", userID))
|
||||
h.response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, result, "签署链接已刷新")
|
||||
}
|
||||
|
||||
// RefreshContractPreviewURL 刷新合同预览链接
|
||||
// @Summary 刷新合同预览链接
|
||||
// @Description 获取签署任务预览链接(法大大 /sign-task/get-preview-url)
|
||||
// @Tags 认证管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Success 200 {object} responses.ContractPreviewUrlResponse "刷新成功"
|
||||
// @Failure 400 {object} map[string]interface{} "请求参数错误"
|
||||
// @Router /api/v1/certifications/refresh-contract-preview-url [post]
|
||||
func (h *CertificationHandler) RefreshContractPreviewURL(c *gin.Context) {
|
||||
userID := h.getCurrentUserID(c)
|
||||
if userID == "" {
|
||||
h.response.Unauthorized(c, "用户未登录")
|
||||
return
|
||||
}
|
||||
|
||||
result, err := h.appService.RefreshContractPreviewURL(c.Request.Context(), userID)
|
||||
if err != nil {
|
||||
h.logger.Error("刷新合同预览链接失败", zap.Error(err), zap.String("user_id", userID))
|
||||
h.response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, result, "预览链接已刷新")
|
||||
}
|
||||
|
||||
// RecognizeBusinessLicense OCR识别营业执照
|
||||
// @Summary OCR识别营业执照
|
||||
// @Description 上传营业执照图片进行OCR识别,自动填充企业信息
|
||||
|
||||
@@ -65,10 +65,17 @@ func (r *CertificationRoutes) Register(router *http.GinRouter) {
|
||||
|
||||
// 3. 申请合同签署
|
||||
authGroup.POST("/apply-contract", r.handler.ApplyContract)
|
||||
// 刷新签署 iframe 长链(法大大 EmbedURL 短期有效)
|
||||
authGroup.POST("/refresh-contract-sign-url", r.handler.RefreshContractSignURL)
|
||||
authGroup.POST("/refresh-contract-preview-url", r.handler.RefreshContractPreviewURL)
|
||||
|
||||
// 前端确认是否完成认证
|
||||
authGroup.POST("/confirm-auth", r.handler.ConfirmAuth)
|
||||
|
||||
// 签署平台选择(审核通过后、企业认证前)
|
||||
authGroup.GET("/sign-platforms", r.handler.ListSignPlatforms)
|
||||
authGroup.POST("/select-sign-platform", r.handler.SelectSignPlatform)
|
||||
|
||||
// 前端确认是否完成签署
|
||||
authGroup.POST("/confirm-sign", r.handler.ConfirmSign)
|
||||
|
||||
@@ -95,7 +102,8 @@ func (r *CertificationRoutes) Register(router *http.GinRouter) {
|
||||
// 回调路由(不需要认证,但需要验证签名)
|
||||
callbackGroup := certificationGroup.Group("/callbacks")
|
||||
{
|
||||
callbackGroup.POST("/esign", r.handler.HandleEsignCallback) // e签宝回调(统一处理企业认证和合同签署回调)
|
||||
callbackGroup.POST("/esign", r.handler.HandleEsignCallback) // e签宝回调
|
||||
callbackGroup.POST("/fadada", r.handler.HandleFadadaCallback) // 法大大回调
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user