package certification import ( "context" "errors" "fmt" "time" "go.uber.org/zap" "gorm.io/gorm" "tyapi-server/internal/application/certification/dto/commands" "tyapi-server/internal/application/certification/dto/queries" "tyapi-server/internal/application/certification/dto/responses" "tyapi-server/internal/domains/certification/entities" "tyapi-server/internal/domains/certification/enums" "tyapi-server/internal/domains/certification/services" user_entities "tyapi-server/internal/domains/user/entities" user_services "tyapi-server/internal/domains/user/services" "tyapi-server/internal/shared/database" esign_service "tyapi-server/internal/shared/esign" ) // CertificationApplicationServiceImpl 认证应用服务实现 // 负责业务流程编排、事务管理、数据转换,不直接操作仓库 type CertificationApplicationServiceImpl struct { certManagementService *services.CertificationManagementService certWorkflowService *services.CertificationWorkflowService certificationEsignService *services.CertificationEsignService enterpriseService *user_services.EnterpriseService esignService *esign_service.Client enterpriseRecordService *services.EnterpriseInfoSubmitRecordService smsCodeService *user_services.SMSCodeService txManager *database.TransactionManager logger *zap.Logger } // NewCertificationApplicationService 创建认证应用服务 func NewCertificationApplicationService( certManagementService *services.CertificationManagementService, certWorkflowService *services.CertificationWorkflowService, certificationEsignService *services.CertificationEsignService, enterpriseService *user_services.EnterpriseService, esignService *esign_service.Client, enterpriseRecordService *services.EnterpriseInfoSubmitRecordService, smsCodeService *user_services.SMSCodeService, txManager *database.TransactionManager, logger *zap.Logger, ) CertificationApplicationService { return &CertificationApplicationServiceImpl{ certManagementService: certManagementService, certWorkflowService: certWorkflowService, certificationEsignService: certificationEsignService, enterpriseService: enterpriseService, esignService: esignService, enterpriseRecordService: enterpriseRecordService, smsCodeService: smsCodeService, txManager: txManager, logger: logger, } } // SubmitEnterpriseInfo 提交企业信息 // 业务流程:1. 验证企业信息 2. 创建或获取认证申请 3. 使用事务执行状态转换和创建记录 func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(ctx context.Context, cmd *commands.SubmitEnterpriseInfoCommand) (*responses.CertificationResponse, error) { // 1. 验证短信验证码 if err := s.smsCodeService.VerifyCode(ctx, cmd.LegalPersonPhone, cmd.VerificationCode, user_entities.SMSSceneCertification); err != nil { return nil, fmt.Errorf("验证码错误或已过期") } // 2. 验证企业信息(检查统一社会信用代码是否已存在) exists, err := s.enterpriseService.CheckUnifiedSocialCodeExists(ctx, cmd.UnifiedSocialCode, "") if err != nil { return nil, fmt.Errorf("检查企业信息失败: %w", err) } if exists { return nil, fmt.Errorf("统一社会信用代码已存在") } // 3. 获取或创建认证申请 certification, err := s.certManagementService.GetCertificationByUserID(ctx, cmd.UserID) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { certification, err = s.certManagementService.CreateCertification(ctx, cmd.UserID) if err != nil { return nil, fmt.Errorf("创建认证申请失败: %w", err) } } else { return nil, fmt.Errorf("获取认证申请失败: %w", err) } } // 4. 创建记录 if certification.Status != enums.StatusPending && certification.Status != enums.StatusInfoSubmitted { return nil, fmt.Errorf("当前状态不允许提交企业信息") } _, err = s.enterpriseRecordService.CreateEnterpriseInfoSubmitRecord(ctx, cmd.UserID, cmd.CompanyName, cmd.UnifiedSocialCode, cmd.LegalPersonName, cmd.LegalPersonID, cmd.LegalPersonPhone) if err != nil { return nil, fmt.Errorf("企业信息提交失败: %w", err) } s.logger.Info("企业信息提交成功", zap.String("user_id", cmd.UserID), zap.String("certification_id", certification.ID), zap.String("company_name", cmd.CompanyName), ) // 转换状态 err = s.certWorkflowService.SubmitEnterpriseInfo(ctx, certification.ID) if err != nil { return nil, fmt.Errorf("转换状态失败: %w", err) } // 5. 检查企业是否已经认证 hasCertification, err := s.certManagementService.CheckCertification(ctx, cmd.CompanyName, cmd.UnifiedSocialCode) if err != nil { return nil, fmt.Errorf("检查企业认证状态失败: %w", err) } if hasCertification { // 如果企业已经认证,则直接完成认证 err = s.completeEnterpriseAuth(ctx, certification) if err != nil { return nil, fmt.Errorf("完成企业认证失败: %w", err) } } // 6. 重新获取认证申请数据 certification, err = s.certManagementService.GetCertificationByID(ctx, certification.ID) if err != nil { return nil, fmt.Errorf("获取认证申请失败: %w", err) } return s.buildCertificationResponse(certification), nil } // GetEnterpriseAuthURL 获取企业认证链接 // 业务流程:1. 获取认证申请 2. 获取企业信息提交记录 3. 生成e签宝认证文件 4. 返回认证链接 func (s *CertificationApplicationServiceImpl) GetEnterpriseAuthURL(ctx context.Context, userID string) (*responses.EnterpriseAuthURLResponse, error) { // 1. 获取认证申请 certification, err := s.certManagementService.GetCertificationByUserID(ctx, userID) if err != nil { return nil, fmt.Errorf("用户尚未创建认证申请: %w", err) } // 2. 检查认证状态 if certification.Status != enums.StatusInfoSubmitted { return nil, fmt.Errorf("当前状态不允许进行企业认证") } // 3. 获取企业信息提交记录 enterpriseRecord, err := s.enterpriseRecordService.GetLatestByUserID(ctx, userID) if err != nil { return nil, fmt.Errorf("获取企业信息失败: %w", err) } // 4. 生成e签宝认证文件 authReq := &esign_service.EnterpriseAuthRequest{ CompanyName: enterpriseRecord.CompanyName, UnifiedSocialCode: enterpriseRecord.UnifiedSocialCode, LegalPersonName: enterpriseRecord.LegalPersonName, LegalPersonID: enterpriseRecord.LegalPersonID, TransactorName: enterpriseRecord.LegalPersonName, TransactorMobile: enterpriseRecord.LegalPersonPhone, TransactorID: enterpriseRecord.LegalPersonID, } authResp, err := s.esignService.GenerateEnterpriseAuth(authReq) if err != nil { s.logger.Error("生成企业认证文件失败", zap.String("user_id", userID), zap.String("certification_id", certification.ID), zap.Error(err), ) return nil, fmt.Errorf("生成企业认证文件失败: %w", err) } s.logger.Info("获取企业认证链接成功", zap.String("user_id", userID), zap.String("certification_id", certification.ID), zap.String("esign_flow_id", authResp.AuthFlowID), ) return &responses.EnterpriseAuthURLResponse{ AuthURL: authResp.AuthURL, ShortURL: authResp.AuthShortURL, ExpireAt: time.Now().AddDate(0, 0, 7).Format(time.RFC3339), }, nil } // ApplyContract 申请合同文件 // 业务流程:1. 获取认证申请 2. 获取企业信息 3. 生成e签宝合同文件 func (s *CertificationApplicationServiceImpl) ApplyContract(ctx context.Context, userID string) (*responses.CertificationResponse, error) { // 1. 获取认证申请 certification, err := s.certManagementService.GetCertificationByUserID(ctx, userID) if err != nil { return nil, fmt.Errorf("用户尚未创建认证申请: %w", err) } // 2. 获取企业信息 enterpriseInfo, err := s.enterpriseService.GetEnterpriseInfo(ctx, userID) if err != nil { return nil, fmt.Errorf("获取企业信息失败: %w", err) } // 4. 生成e签宝合同 components := map[string]string{ "JFQY": "海南学宇思网络科技有限公司", "JFFR": "刘福思", "YFQY": enterpriseInfo.CompanyName, "YFFR": enterpriseInfo.LegalPersonName, "QDRQ": time.Now().Format("2006-01-02"), } _, err = s.certificationEsignService.FillTemplate(ctx, certification, components) if err != nil { return nil, fmt.Errorf("生成e签宝合同失败: %w", err) } // 6. 重新获取更新后的认证申请数据 updatedCertification, err := s.certManagementService.GetCertificationByID(ctx, certification.ID) if err != nil { return nil, err } return s.buildCertificationResponse(updatedCertification), nil } // GetContractSignURL 获取合同签署链接 // 业务流程:1. 获取认证申请 2. 获取企业信息 3. 获取签署链接 func (s *CertificationApplicationServiceImpl) GetContractSignURL(ctx context.Context, cmd *commands.GetContractSignURLCommand) (*responses.ContractSignURLResponse, error) { // 1. 获取认证申请 certification, err := s.certManagementService.GetCertificationByUserID(ctx, cmd.UserID) if err != nil { return nil, fmt.Errorf("用户尚未创建认证申请: %w", err) } // 2. 检查认证状态 if certification.Status != enums.StatusContractApplied { return nil, fmt.Errorf("当前状态不允许获取签署链接") } // 3. 获取企业信息 enterpriseInfo, err := s.enterpriseService.GetEnterpriseInfo(ctx, cmd.UserID) if err != nil { return nil, fmt.Errorf("获取企业信息失败: %w", err) } if certification.ContractFileID == "" { return nil, fmt.Errorf("请先申请合同文件") } // 5. 发起签署 signRecord, err := s.certificationEsignService.InitiateSign(ctx, certification, enterpriseInfo) if err != nil { return nil, fmt.Errorf("获取签署链接失败: %w", err) } // 转换状态 err = s.certWorkflowService.ApplyContract(ctx, certification.ID) if err != nil { return nil, fmt.Errorf("转换状态失败: %w", err) } // 6. 计算过期时间(7天后) expireAt := time.Now().AddDate(0, 0, 7).Format(time.RFC3339) s.logger.Info("获取签署链接成功", zap.String("user_id", cmd.UserID), zap.String("certification_id", certification.ID), zap.String("sign_flow_id", signRecord.EsignFlowID), ) return &responses.ContractSignURLResponse{ SignURL: signRecord.SignURL, ShortURL: signRecord.SignShortURL, SignFlowID: signRecord.EsignFlowID, ExpireAt: expireAt, }, nil } // CompleteContractSign 完成合同签署 // 业务流程:1. 获取认证申请 2. 完成合同签署 3. 自动完成认证 func (s *CertificationApplicationServiceImpl) CompleteContractSign(ctx context.Context, cmd *commands.CompleteContractSignCommand) error { // 1. 获取认证申请 certification, err := s.certManagementService.GetCertificationByUserID(ctx, cmd.UserID) if err != nil { return fmt.Errorf("用户尚未创建认证申请: %w", err) } if certification.Status != enums.StatusContractApplied { return fmt.Errorf("当前状态不允许完成合同签署") } // 2. 完成合同签署(状态转换) if err := s.certWorkflowService.CompleteContractSign(ctx, certification.ID, cmd.ContractURL); err != nil { return err } // 3. 重新获取认证申请 updatedCertification, err := s.certManagementService.GetCertificationByID(ctx, certification.ID) if err != nil { return err } // 4. 如果合同已签署,自动完成认证 if updatedCertification.Status == enums.StatusContractSigned { if err := s.certWorkflowService.CompleteCertification(ctx, certification.ID); err != nil { return err } } s.logger.Info("合同签署完成", zap.String("user_id", cmd.UserID), zap.String("certification_id", certification.ID), zap.String("contract_url", cmd.ContractURL), ) return nil } // GetCertificationStatus 获取认证状态 // 业务流程:1. 获取认证申请 2. 构建响应数据 func (s *CertificationApplicationServiceImpl) GetCertificationStatus(ctx context.Context, query *queries.GetCertificationStatusQuery) (*responses.CertificationResponse, error) { // 1. 获取认证申请 certification, err := s.certManagementService.GetCertificationByUserID(ctx, query.UserID) if err != nil { // 如果用户没有认证申请,返回一个表示未开始的状态 if errors.Is(err, gorm.ErrRecordNotFound) { return &responses.CertificationResponse{ ID: "", UserID: query.UserID, Status: "not_started", StatusName: "未开始认证", Progress: 0, IsUserActionRequired: true, InfoSubmittedAt: nil, EnterpriseVerifiedAt: nil, ContractAppliedAt: nil, ContractSignedAt: nil, CompletedAt: nil, ContractURL: "", CreatedAt: time.Time{}, UpdatedAt: time.Time{}, }, nil } return nil, err } // 2. 构建响应 return s.buildCertificationResponse(certification), nil } // GetCertificationDetails 获取认证详情 // 业务流程:1. 获取认证申请 2. 获取企业信息 3. 构建响应数据 func (s *CertificationApplicationServiceImpl) GetCertificationDetails(ctx context.Context, query *queries.GetCertificationDetailsQuery) (*responses.CertificationResponse, error) { // 1. 获取认证申请 certification, err := s.certManagementService.GetCertificationByUserID(ctx, query.UserID) if err != nil { // 如果用户没有认证申请,返回错误 if errors.Is(err, gorm.ErrRecordNotFound) { return nil, fmt.Errorf("用户尚未创建认证申请") } return nil, err } // 2. 构建响应 response := s.buildCertificationResponse(certification) // 3. 添加企业信息 if certification.UserID != "" { enterpriseInfo, err := s.enterpriseService.GetEnterpriseInfo(ctx, certification.UserID) if err == nil && enterpriseInfo != nil { response.Enterprise = s.buildEnterpriseInfoResponse(enterpriseInfo) } } return response, nil } // GetCertificationProgress 获取认证进度 // 业务流程:1. 获取认证申请 2. 获取进度信息 func (s *CertificationApplicationServiceImpl) GetCertificationProgress(ctx context.Context, userID string) (map[string]interface{}, error) { // 1. 获取认证申请 certification, err := s.certManagementService.GetCertificationByUserID(ctx, userID) if err != nil { // 如果用户没有认证申请,返回未开始状态 if errors.Is(err, gorm.ErrRecordNotFound) { return map[string]interface{}{ "certification_id": "", "user_id": userID, "current_status": "not_started", "status_name": "未开始认证", "progress_percentage": 0, "is_user_action_required": true, "next_valid_statuses": []string{"pending"}, "message": "用户尚未开始认证流程", "created_at": nil, "updated_at": nil, }, nil } return nil, err } // 2. 获取认证进度 return s.certManagementService.GetCertificationProgress(ctx, certification.ID) } // buildCertificationResponse 构建认证响应 func (s *CertificationApplicationServiceImpl) buildCertificationResponse(certification *entities.Certification) *responses.CertificationResponse { return &responses.CertificationResponse{ ID: certification.ID, UserID: certification.UserID, Status: certification.Status, StatusName: certification.GetStatusName(), Progress: certification.GetProgressPercentage(), IsUserActionRequired: certification.IsUserActionRequired(), InfoSubmittedAt: certification.InfoSubmittedAt, EnterpriseVerifiedAt: certification.EnterpriseVerifiedAt, ContractAppliedAt: certification.ContractAppliedAt, ContractSignedAt: certification.ContractSignedAt, CompletedAt: certification.CompletedAt, ContractURL: certification.ContractURL, CreatedAt: certification.CreatedAt, UpdatedAt: certification.UpdatedAt, } } // buildEnterpriseInfoResponse 构建企业信息响应 func (s *CertificationApplicationServiceImpl) buildEnterpriseInfoResponse(enterpriseInfo *user_entities.EnterpriseInfo) *responses.EnterpriseInfoResponse { return &responses.EnterpriseInfoResponse{ ID: enterpriseInfo.ID, CompanyName: enterpriseInfo.CompanyName, UnifiedSocialCode: enterpriseInfo.UnifiedSocialCode, LegalPersonName: enterpriseInfo.LegalPersonName, LegalPersonID: enterpriseInfo.LegalPersonID, CreatedAt: enterpriseInfo.CreatedAt, UpdatedAt: enterpriseInfo.UpdatedAt, } } // 企业认证成功后操作 func (s *CertificationApplicationServiceImpl) completeEnterpriseAuth(ctx context.Context, certification *entities.Certification) error { err := s.txManager.ExecuteInTx(ctx, func(txCtx context.Context) error { // 1. 获取企业信息提交记录 enterpriseRecord, err := s.enterpriseRecordService.GetLatestByUserID(txCtx, certification.UserID) if err != nil { return fmt.Errorf("获取企业信息失败: %w", err) } // 2. 转换状态 if err := s.certWorkflowService.CompleteEnterpriseVerification(txCtx, certification.ID); err != nil { return err } // 3. 创建企业信息 _, err = s.enterpriseService.CreateEnterpriseInfo(txCtx, certification.UserID, enterpriseRecord.CompanyName, enterpriseRecord.UnifiedSocialCode, enterpriseRecord.LegalPersonName, enterpriseRecord.LegalPersonID) if err != nil { s.logger.Warn("创建用户企业信息失败", zap.Error(err)) return err } return nil }) if err != nil { return fmt.Errorf("完成企业认证失败: %w", err) } return nil }