274 lines
9.8 KiB
Go
274 lines
9.8 KiB
Go
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,
|
||
}
|
||
}
|