diff --git a/internal/application/certification/certification_application_service_impl.go b/internal/application/certification/certification_application_service_impl.go index 0e90009..ba61454 100644 --- a/internal/application/certification/certification_application_service_impl.go +++ b/internal/application/certification/certification_application_service_impl.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "net/http" + "strings" "time" "tyapi-server/internal/application/certification/dto/commands" @@ -265,8 +266,7 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo( // 4. 提交企业信息:暂时跳过人工审核,直接进入「已提交」状态(第三步企业认证) // 恢复人工审核时改为 cert.SubmitEnterpriseInfoForReview(enterpriseInfo),并将 next_action 改为「请等待管理员审核企业信息」 - // 生成企业认证链接 - authURL, err := s.esignClient.GenerateEnterpriseAuth(&esign.EnterpriseAuthRequest{ + authReq := &esign.EnterpriseAuthRequest{ CompanyName: enterpriseInfo.CompanyName, UnifiedSocialCode: enterpriseInfo.UnifiedSocialCode, LegalPersonName: enterpriseInfo.LegalPersonName, @@ -274,19 +274,28 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo( TransactorName: enterpriseInfo.LegalPersonName, TransactorMobile: enterpriseInfo.LegalPersonPhone, TransactorID: enterpriseInfo.LegalPersonID, - }) + } + authURL, alreadyVerified, err := s.generateEnterpriseAuthOrDetectVerified(txCtx, authReq) if err != nil { return fmt.Errorf("生成企业认证链接失败: %w", err) } - - err = cert.SubmitEnterpriseInfo(enterpriseInfo, authURL.AuthShortURL, authURL.AuthFlowID) - if err != nil { - return fmt.Errorf("提交企业信息失败: %s", err.Error()) - } - - err = s.aggregateService.SaveCertification(txCtx, cert) - if err != nil { - return fmt.Errorf("保存认证信息失败: %s", err.Error()) + if alreadyVerified { + // 三方侧已实名:先进入 info_submitted,再在同事务内自动推进到 enterprise_verified。 + if err = cert.SubmitEnterpriseInfo(enterpriseInfo, "", ""); err != nil { + return fmt.Errorf("提交企业信息失败: %s", err.Error()) + } + if err = s.completeEnterpriseVerification(txCtx, cert, cert.UserID, enterpriseInfo.CompanyName, enterpriseInfo.LegalPersonName); err != nil { + return fmt.Errorf("自动完成企业认证失败: %w", err) + } + } else { + err = cert.SubmitEnterpriseInfo(enterpriseInfo, authURL.AuthShortURL, authURL.AuthFlowID) + if err != nil { + return fmt.Errorf("提交企业信息失败: %s", err.Error()) + } + err = s.aggregateService.SaveCertification(txCtx, cert) + if err != nil { + return fmt.Errorf("保存认证信息失败: %s", err.Error()) + } } // 5. 提交记录与认证状态在同一事务内保存 @@ -296,13 +305,22 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo( respMeta := map[string]interface{}{ "enterprise_info": enterpriseInfo, - "next_action": "请完成企业认证", + "polling": map[string]interface{}{ + "enabled": !alreadyVerified, + "endpoint": "/api/v1/certifications/confirm-auth", + "interval_seconds": 3, + }, + } + if alreadyVerified { + respMeta["next_action"] = "企业已实名,已自动完成认证,可进入合同签署" + respMeta["target_view"] = "contract_sign" + } else { + respMeta["next_action"] = "请完成企业认证" + respMeta["target_view"] = "enterprise_auth" } // 6. 转换为响应 DTO response = s.convertToResponse(cert) - if respMeta != nil { - response.Metadata = respMeta - } + response.Metadata = respMeta return nil }) if err != nil { @@ -875,7 +893,7 @@ func (s *CertificationApplicationServiceImpl) AdminApproveSubmitRecord(ctx conte LegalPersonPhone: record.LegalPersonPhone, EnterpriseAddress: record.EnterpriseAddress, } - authURL, err := s.esignClient.GenerateEnterpriseAuth(&esign.EnterpriseAuthRequest{ + authReq := &esign.EnterpriseAuthRequest{ CompanyName: enterpriseInfo.CompanyName, UnifiedSocialCode: enterpriseInfo.UnifiedSocialCode, LegalPersonName: enterpriseInfo.LegalPersonName, @@ -883,10 +901,17 @@ func (s *CertificationApplicationServiceImpl) AdminApproveSubmitRecord(ctx conte TransactorName: enterpriseInfo.LegalPersonName, TransactorMobile: enterpriseInfo.LegalPersonPhone, TransactorID: enterpriseInfo.LegalPersonID, - }) + } + authURL, alreadyVerified, err := s.generateEnterpriseAuthOrDetectVerified(ctx, authReq) if err != nil { return fmt.Errorf("生成企业认证链接失败: %w", err) } + if alreadyVerified { + if err := cert.ApproveEnterpriseInfoReview("", "", adminID); err != nil { + return fmt.Errorf("更新认证状态失败: %w", err) + } + return s.completeEnterpriseVerification(ctx, cert, cert.UserID, record.CompanyName, record.LegalPersonName) + } if err := cert.ApproveEnterpriseInfoReview(authURL.AuthShortURL, authURL.AuthFlowID, adminID); err != nil { return fmt.Errorf("更新认证状态失败: %w", err) } @@ -964,14 +989,21 @@ func (s *CertificationApplicationServiceImpl) AdminTransitionCertificationStatus LegalPersonName: record.LegalPersonName, LegalPersonID: record.LegalPersonID, LegalPersonPhone: record.LegalPersonPhone, EnterpriseAddress: record.EnterpriseAddress, } - authURL, err := s.esignClient.GenerateEnterpriseAuth(&esign.EnterpriseAuthRequest{ + authReq := &esign.EnterpriseAuthRequest{ CompanyName: enterpriseInfo.CompanyName, UnifiedSocialCode: enterpriseInfo.UnifiedSocialCode, LegalPersonName: enterpriseInfo.LegalPersonName, LegalPersonID: enterpriseInfo.LegalPersonID, TransactorName: enterpriseInfo.LegalPersonName, TransactorMobile: enterpriseInfo.LegalPersonPhone, TransactorID: enterpriseInfo.LegalPersonID, - }) + } + authURL, alreadyVerified, err := s.generateEnterpriseAuthOrDetectVerified(ctx, authReq) if err != nil { return fmt.Errorf("生成企业认证链接失败: %w", err) } + if alreadyVerified { + if err := cert.ApproveEnterpriseInfoReview("", "", cmd.AdminID); err != nil { + return fmt.Errorf("更新认证状态失败: %w", err) + } + return s.completeEnterpriseVerification(ctx, cert, cert.UserID, record.CompanyName, record.LegalPersonName) + } if err := cert.ApproveEnterpriseInfoReview(authURL.AuthShortURL, authURL.AuthFlowID, cmd.AdminID); err != nil { return fmt.Errorf("更新认证状态失败: %w", err) } @@ -1049,6 +1081,49 @@ func (s *CertificationApplicationServiceImpl) convertToResponse(cert *entities.C return response } +func (s *CertificationApplicationServiceImpl) generateEnterpriseAuthOrDetectVerified( + ctx context.Context, + req *esign.EnterpriseAuthRequest, +) (*esign.EnterpriseAuthResult, bool, error) { + authURL, err := s.esignClient.GenerateEnterpriseAuth(req) + if err == nil { + return authURL, false, nil + } + if !isEnterpriseAlreadyRealnamedErr(err) { + return nil, false, err + } + + s.logger.Warn("企业已实名,跳过生成认证链接并转为自动确认", + zap.String("company_name", req.CompanyName), + zap.String("unified_social_code", req.UnifiedSocialCode), + zap.Error(err)) + + identity, identityErr := s.esignClient.QueryOrgIdentityInfo(&esign.QueryOrgIdentityRequest{ + OrgIDCardNum: req.UnifiedSocialCode, + OrgIDCardType: esign.OrgIDCardTypeUSCC, + }) + if identityErr != nil { + identity, identityErr = s.esignClient.QueryOrgIdentityInfo(&esign.QueryOrgIdentityRequest{ + OrgName: req.CompanyName, + }) + } + if identityErr != nil { + return nil, false, fmt.Errorf("企业用户已实名,但查询实名状态失败: %w", identityErr) + } + if identity == nil || identity.Data.RealnameStatus != 1 { + return nil, false, err + } + return nil, true, nil +} + +func isEnterpriseAlreadyRealnamedErr(err error) bool { + if err == nil { + return false + } + msg := err.Error() + return strings.Contains(msg, "企业用户已实名") || strings.Contains(msg, "已实名") +} + // validateApplyContractCommand 验证申请合同命令 func (s *CertificationApplicationServiceImpl) validateApplyContractCommand(cmd *commands.ApplyContractCommand) error { if cmd.UserID == "" {