Files
hyapi-server/internal/application/certification/platform_flow.go

274 lines
9.8 KiB
Go
Raw Normal View History

2026-07-21 15:53:29 +08:00
package certification
import (
"context"
"fmt"
"strings"
"time"
"hyapi-server/internal/application/certification/dto/commands"
"hyapi-server/internal/application/certification/dto/responses"
"hyapi-server/internal/domains/certification/entities"
"hyapi-server/internal/domains/certification/enums"
"hyapi-server/internal/domains/certification/ports"
user_entities "hyapi-server/internal/domains/user/entities"
"go.uber.org/zap"
)
// ListSignPlatforms 可选签署平台列表
func (s *CertificationApplicationServiceImpl) ListSignPlatforms(ctx context.Context) (*responses.SignPlatformsResponse, error) {
_ = ctx
items := make([]*responses.SignPlatformItem, 0, 2)
for _, p := range s.platformRegistry.List() {
platform := p.Platform()
item := &responses.SignPlatformItem{
Platform: string(platform),
Name: enums.GetSignPlatformName(platform),
Available: true,
Description: "",
}
switch platform {
case enums.SignPlatformEsign:
item.Description = "通过 e签宝 完成企业实名认证与合同签署"
case enums.SignPlatformFadada:
item.Description = "通过法大大完成企业实名认证与合同签署"
}
items = append(items, item)
}
return &responses.SignPlatformsResponse{Platforms: items}, nil
}
// SelectSignPlatform 用户选择签署平台并生成企业认证链接
func (s *CertificationApplicationServiceImpl) SelectSignPlatform(
ctx context.Context,
cmd *commands.SelectSignPlatformCommand,
) (*responses.CertificationResponse, error) {
if cmd == nil || cmd.UserID == "" {
return nil, fmt.Errorf("用户ID不能为空")
}
platform := enums.SignPlatform(strings.TrimSpace(cmd.SignPlatform))
if !enums.IsValidSignPlatform(platform) {
return nil, fmt.Errorf("无效的签署平台: %s", cmd.SignPlatform)
}
cert, err := s.aggregateService.LoadCertificationByUserID(ctx, cmd.UserID)
if err != nil {
return nil, fmt.Errorf("加载认证信息失败: %w", err)
}
record, err := s.enterpriseInfoSubmitRecordRepo.FindLatestByUserID(ctx, cmd.UserID)
if err != nil || record == nil {
return nil, fmt.Errorf("未找到企业信息提交记录")
}
if record.ManualReviewStatus != "approved" {
return nil, fmt.Errorf("请先等待管理员审核通过后再选择签署平台")
}
// 已选择且已有认证链接:幂等返回
if cert.SignPlatform == platform && cert.Status == enums.StatusInfoSubmitted && cert.AuthURL != "" {
resp := s.convertToResponse(cert)
meta, _ := s.AddStatusMetadata(ctx, cert)
resp.Metadata = meta
return resp, nil
}
if cert.SignPlatform != "" && cert.SignPlatform != platform {
return nil, fmt.Errorf("签署平台已选择为 %s不可更改", enums.GetSignPlatformName(cert.SignPlatform))
}
if cert.Status != enums.StatusInfoPendingReview && !(cert.Status == enums.StatusInfoSubmitted && cert.AuthURL == "") {
return nil, fmt.Errorf("当前状态不允许选择签署平台: %s", enums.GetStatusName(cert.Status))
}
if err := cert.SetSignPlatform(platform); err != nil {
return nil, err
}
provider, err := s.platformRegistry.Get(platform)
if err != nil {
return nil, err
}
authReq := &ports.EnterpriseAuthRequest{
ClientCorpID: record.UnifiedSocialCode,
ClientUserID: record.LegalPersonID,
CompanyName: record.CompanyName,
UnifiedSocialCode: record.UnifiedSocialCode,
LegalPersonName: record.LegalPersonName,
LegalPersonID: record.LegalPersonID,
TransactorName: record.LegalPersonName,
TransactorMobile: record.LegalPersonPhone,
TransactorID: record.LegalPersonID,
}
authRes, alreadyVerified, err := s.generateEnterpriseAuthOrDetectVerifiedWithProvider(ctx, provider, authReq)
if err != nil {
return nil, fmt.Errorf("生成企业认证链接失败: %w", err)
}
if alreadyVerified {
// 已授权/已实名:跳过授权页,直接推进到 enterprise_verified
err = s.txManager.ExecuteInTx(ctx, func(txCtx context.Context) error {
clientCorpID := strings.TrimSpace(record.UnifiedSocialCode)
if cert.Status == enums.StatusInfoPendingReview {
if err := cert.ApproveEnterpriseInfoReview("", clientCorpID, cmd.UserID); err != nil {
return err
}
} else if cert.Status == enums.StatusInfoSubmitted {
if clientCorpID != "" && cert.AuthFlowID == "" {
cert.AuthFlowID = clientCorpID
}
} else {
return fmt.Errorf("当前状态不允许完成已授权企业认证: %s", enums.GetStatusName(cert.Status))
}
return s.completeEnterpriseVerification(txCtx, cert, cert.UserID, record.CompanyName, record.LegalPersonName)
})
if err != nil {
return nil, err
}
} else {
authURL := authRes.AuthShortURL
if authURL == "" {
authURL = authRes.AuthURL
}
if err := cert.ApproveEnterpriseInfoReview(authURL, authRes.AuthFlowID, cmd.UserID); err != nil {
return nil, err
}
if err := s.aggregateService.SaveCertification(ctx, cert); err != nil {
return nil, fmt.Errorf("保存认证信息失败: %w", err)
}
}
s.logger.Info("用户已选择签署平台",
zap.String("user_id", cmd.UserID),
zap.String("sign_platform", string(platform)),
zap.Bool("already_verified", alreadyVerified))
resp := s.convertToResponse(cert)
meta, _ := s.AddStatusMetadata(ctx, cert)
resp.Metadata = meta
return resp, nil
}
// HandleFadadaCallback 法大大回调
func (s *CertificationApplicationServiceImpl) HandleFadadaCallback(ctx context.Context, headers map[string]string, body []byte) error {
provider, err := s.platformRegistry.Get(enums.SignPlatformFadada)
if err != nil {
return err
}
if err := provider.VerifyCallback(ctx, headers, body); err != nil {
return fmt.Errorf("法大大回调验签失败: %w", err)
}
ev, err := provider.ParseCallback(ctx, headers, body)
if err != nil {
return fmt.Errorf("法大大回调解析失败: %w", err)
}
s.logger.Info("收到法大大回调",
zap.String("event", ev.Event),
zap.Bool("auth_passed", ev.AuthPassed),
zap.Bool("sign_completed", ev.SignCompleted),
zap.String("sign_flow_id", ev.SignFlowID))
// 认证完成主要依赖 ConfirmAuth / details 轮询;签署完成按任务 ID 兜底推进
if ev.SignCompleted && ev.SignFlowID != "" {
cert, err := s.queryRepository.FindByEsignFlowID(ctx, ev.SignFlowID)
if err == nil && cert != nil && cert.Status == enums.StatusContractApplied {
_, _ = s.checkAndUpdateSignStatus(ctx, cert)
}
}
return nil
}
func (s *CertificationApplicationServiceImpl) resolveProvider(cert *entities.Certification) (ports.SignPlatformProvider, error) {
return s.platformRegistry.Get(cert.ResolvedSignPlatform())
}
func (s *CertificationApplicationServiceImpl) generateEnterpriseAuthOrDetectVerifiedWithProvider(
ctx context.Context,
provider ports.SignPlatformProvider,
authReq *ports.EnterpriseAuthRequest,
) (*ports.AuthLinkResult, bool, error) {
authRes, err := provider.GenerateEnterpriseAuth(ctx, authReq)
if err == nil {
return authRes, false, nil
}
// 法大大企业已对本应用授权210002= 授权已完成,直接跳过授权页
if isEnterpriseAlreadyAuthorizedErr(err) {
s.logger.Info("第三方返回企业已授权,跳过认证链接并视为认证完成",
zap.String("company_name", authReq.CompanyName),
zap.String("uscc", authReq.UnifiedSocialCode),
zap.String("platform", string(provider.Platform())),
zap.Error(err))
return nil, true, nil
}
// e签宝等提示已实名时再查一次实名状态确认
if !isEnterpriseAlreadyRealnamedErr(err) && !strings.Contains(strings.ToLower(err.Error()), "identified") {
return nil, false, err
}
ok, qErr := provider.QueryOrgVerified(ctx, &ports.OrgIdentityQuery{
OrgName: authReq.CompanyName,
OrgIdentNo: authReq.UnifiedSocialCode,
})
if qErr != nil {
return nil, false, fmt.Errorf("%v; 且查询实名状态失败: %w", err, qErr)
}
if ok {
s.logger.Info("第三方返回企业已实名,跳过认证链接并视为认证完成",
zap.String("company_name", authReq.CompanyName),
zap.String("platform", string(provider.Platform())))
return nil, true, nil
}
return nil, false, err
}
func (s *CertificationApplicationServiceImpl) appendPlatformSelectMetadata(
ctx context.Context,
cert *entities.Certification,
metadata map[string]interface{},
) map[string]interface{} {
if metadata == nil {
metadata = map[string]interface{}{}
}
record, err := s.enterpriseInfoSubmitRecordRepo.FindLatestByUserID(ctx, cert.UserID)
if err != nil || record == nil {
return metadata
}
metadata["manual_review_status"] = record.ManualReviewStatus
if record.ManualReviewStatus == "approved" &&
cert.Status == enums.StatusInfoPendingReview &&
(cert.SignPlatform == "" || cert.AuthURL == "") {
metadata["need_select_platform"] = true
platforms := make([]map[string]string, 0, 2)
for _, p := range s.platformRegistry.List() {
platforms = append(platforms, map[string]string{
"platform": string(p.Platform()),
"name": enums.GetSignPlatformName(p.Platform()),
})
}
metadata["available_platforms"] = platforms
metadata["next_action"] = "请选择签署平台以继续企业认证"
}
if cert.SignPlatform != "" {
metadata["sign_platform"] = string(cert.SignPlatform)
metadata["sign_platform_name"] = enums.GetSignPlatformName(cert.SignPlatform)
}
return metadata
}
// ensureContractFill 签署模板路径补齐协议编号与日期(协议编号生成规则与 e签宝一致
func ensureContractFill(cert *entities.Certification, companyName, uscc, address, repName string) *ports.ContractGenerateRequest {
if cert.ContractCode == "" {
cert.SetContractCode(user_entities.GenerateContractCode(user_entities.ContractTypeCooperation))
}
signDate := time.Now().Format("2006年01月02日")
return &ports.ContractGenerateRequest{
AgreementNo: cert.ContractCode,
CompanyName: companyName,
UnifiedSocialCode: uscc,
EnterpriseAddress: address,
AuthorizedRepName: repName,
SignDate: signDate,
}
}