Files
2026-07-21 15:53:29 +08:00

274 lines
9.8 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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,
}
}