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)