package services import ( "context" "fmt" "mime" "os" "path/filepath" "strings" "time" "go.uber.org/zap" "tyapi-server/internal/application/certification/dto/responses" "tyapi-server/internal/domains/certification/entities" "tyapi-server/internal/domains/certification/enums" "tyapi-server/internal/domains/certification/repositories" sharedOCR "tyapi-server/internal/shared/ocr" sharedStorage "tyapi-server/internal/shared/storage" ) // CertificationService 认证领域服务 type CertificationService struct { certRepo repositories.CertificationRepository licenseRepo repositories.LicenseUploadRecordRepository faceVerifyRepo repositories.FaceVerifyRecordRepository contractRepo repositories.ContractRecordRepository stateMachine *CertificationStateMachine logger *zap.Logger ocrService sharedOCR.OCRService storageService sharedStorage.StorageService } // NewCertificationService 创建认证领域服务 func NewCertificationService( certRepo repositories.CertificationRepository, licenseRepo repositories.LicenseUploadRecordRepository, faceVerifyRepo repositories.FaceVerifyRecordRepository, contractRepo repositories.ContractRecordRepository, stateMachine *CertificationStateMachine, logger *zap.Logger, ocrService sharedOCR.OCRService, storageService sharedStorage.StorageService, ) *CertificationService { return &CertificationService{ certRepo: certRepo, licenseRepo: licenseRepo, faceVerifyRepo: faceVerifyRepo, contractRepo: contractRepo, stateMachine: stateMachine, logger: logger, ocrService: ocrService, storageService: storageService, } } // CreateCertification 创建认证申请 func (s *CertificationService) CreateCertification(ctx context.Context, userID string) (*entities.Certification, error) { // 检查用户是否已有认证申请 existingCert, err := s.certRepo.GetByUserID(ctx, userID) if err == nil && existingCert != nil { return nil, fmt.Errorf("用户已有认证申请") } certification := &entities.Certification{ UserID: userID, Status: enums.StatusPending, } createdCert, err := s.certRepo.Create(ctx, *certification) if err != nil { s.logger.Error("创建认证申请失败", zap.Error(err)) return nil, fmt.Errorf("创建认证申请失败: %w", err) } certification = &createdCert s.logger.Info("认证申请创建成功", zap.String("certification_id", certification.ID), zap.String("user_id", userID), ) return certification, nil } // SubmitEnterpriseInfo 提交企业信息 func (s *CertificationService) SubmitEnterpriseInfo(ctx context.Context, certificationID string) error { cert, err := s.certRepo.GetByID(ctx, certificationID) if err != nil { return fmt.Errorf("认证申请不存在: %w", err) } // 检查当前状态是否可以提交企业信息 if cert.Status != enums.StatusPending { return fmt.Errorf("当前状态不允许提交企业信息") } // 使用状态机转换状态 if err := s.stateMachine.TransitionTo(ctx, certificationID, enums.StatusInfoSubmitted, true, false, nil); err != nil { return fmt.Errorf("状态转换失败: %w", err) } s.logger.Info("企业信息提交成功", zap.String("certification_id", certificationID), zap.String("user_id", cert.UserID), ) return nil } // InitiateFaceVerify 发起人脸识别验证 func (s *CertificationService) InitiateFaceVerify(ctx context.Context, certificationID, realName, idCardNumber string) (*entities.FaceVerifyRecord, error) { cert, err := s.certRepo.GetByID(ctx, certificationID) if err != nil { return nil, fmt.Errorf("认证申请不存在: %w", err) } // 检查当前状态是否可以发起人脸识别 if cert.Status != enums.StatusInfoSubmitted { return nil, fmt.Errorf("当前状态不允许发起人脸识别") } // 创建人脸识别记录 faceVerifyRecord := &entities.FaceVerifyRecord{ CertificationID: certificationID, UserID: cert.UserID, RealName: realName, IDCardNumber: idCardNumber, Status: "PROCESSING", ExpiresAt: time.Now().Add(30 * time.Minute), // 30分钟有效期 } createdFaceRecord, err := s.faceVerifyRepo.Create(ctx, *faceVerifyRecord) if err != nil { s.logger.Error("创建人脸识别记录失败", zap.Error(err)) return nil, fmt.Errorf("创建人脸识别记录失败: %w", err) } faceVerifyRecord = &createdFaceRecord s.logger.Info("人脸识别验证发起成功", zap.String("certification_id", certificationID), zap.String("face_verify_id", faceVerifyRecord.ID), ) return faceVerifyRecord, nil } // CompleteFaceVerify 完成人脸识别验证 func (s *CertificationService) CompleteFaceVerify(ctx context.Context, faceVerifyID string, isSuccess bool) error { faceVerifyRecord, err := s.faceVerifyRepo.GetByID(ctx, faceVerifyID) if err != nil { return fmt.Errorf("人脸识别记录不存在: %w", err) } // 更新人脸识别记录状态 now := time.Now() faceVerifyRecord.CompletedAt = &now if isSuccess { faceVerifyRecord.Status = "SUCCESS" faceVerifyRecord.VerifyScore = 0.95 // 示例分数 } else { faceVerifyRecord.Status = "FAIL" faceVerifyRecord.ResultMessage = "人脸识别验证失败" } if err := s.faceVerifyRepo.Update(ctx, faceVerifyRecord); err != nil { s.logger.Error("更新人脸识别记录失败", zap.Error(err)) return fmt.Errorf("更新人脸识别记录失败: %w", err) } // 根据验证结果转换认证状态 var targetStatus enums.CertificationStatus if isSuccess { targetStatus = enums.StatusFaceVerified } else { targetStatus = enums.StatusFaceFailed } if err := s.stateMachine.TransitionTo(ctx, faceVerifyRecord.CertificationID, targetStatus, false, false, nil); err != nil { return fmt.Errorf("状态转换失败: %w", err) } s.logger.Info("人脸识别验证完成", zap.String("face_verify_id", faceVerifyID), zap.Bool("is_success", isSuccess), ) return nil } // ApplyContract 申请合同 func (s *CertificationService) ApplyContract(ctx context.Context, certificationID string) error { cert, err := s.certRepo.GetByID(ctx, certificationID) if err != nil { return fmt.Errorf("认证申请不存在: %w", err) } // 检查当前状态是否可以申请合同 if cert.Status != enums.StatusFaceVerified { return fmt.Errorf("当前状态不允许申请合同") } // 使用状态机转换状态 if err := s.stateMachine.TransitionTo(ctx, certificationID, enums.StatusContractApplied, true, false, nil); err != nil { return fmt.Errorf("状态转换失败: %w", err) } // 创建合同记录 contractRecord := &entities.ContractRecord{ CertificationID: certificationID, UserID: cert.UserID, Status: "PENDING", ContractType: "ENTERPRISE_CERTIFICATION", } createdContract, err := s.contractRepo.Create(ctx, *contractRecord) if err != nil { s.logger.Error("创建合同记录失败", zap.Error(err)) return fmt.Errorf("创建合同记录失败: %w", err) } contractRecord = &createdContract // 自动转换到待审核状态 if err := s.stateMachine.TransitionTo(ctx, certificationID, enums.StatusContractPending, false, false, nil); err != nil { return fmt.Errorf("状态转换失败: %w", err) } s.logger.Info("合同申请成功", zap.String("certification_id", certificationID), zap.String("contract_id", contractRecord.ID), ) return nil } // ApproveContract 管理员审核合同 func (s *CertificationService) ApproveContract(ctx context.Context, certificationID, adminID, signingURL, approvalNotes string) error { cert, err := s.certRepo.GetByID(ctx, certificationID) if err != nil { return fmt.Errorf("认证申请不存在: %w", err) } // 检查当前状态是否可以审核 if cert.Status != enums.StatusContractPending { return fmt.Errorf("当前状态不允许审核") } // 准备审核元数据 metadata := map[string]interface{}{ "admin_id": adminID, "signing_url": signingURL, "approval_notes": approvalNotes, } // 使用状态机转换状态 if err := s.stateMachine.TransitionTo(ctx, certificationID, enums.StatusContractApproved, false, true, metadata); err != nil { return fmt.Errorf("状态转换失败: %w", err) } s.logger.Info("合同审核通过", zap.String("certification_id", certificationID), zap.String("admin_id", adminID), ) return nil } // RejectContract 管理员拒绝合同 func (s *CertificationService) RejectContract(ctx context.Context, certificationID, adminID, rejectReason string) error { cert, err := s.certRepo.GetByID(ctx, certificationID) if err != nil { return fmt.Errorf("认证申请不存在: %w", err) } // 检查当前状态是否可以拒绝 if cert.Status != enums.StatusContractPending { return fmt.Errorf("当前状态不允许拒绝") } // 准备拒绝元数据 metadata := map[string]interface{}{ "admin_id": adminID, "reject_reason": rejectReason, } // 使用状态机转换状态 if err := s.stateMachine.TransitionTo(ctx, certificationID, enums.StatusRejected, false, true, metadata); err != nil { return fmt.Errorf("状态转换失败: %w", err) } s.logger.Info("合同审核拒绝", zap.String("certification_id", certificationID), zap.String("admin_id", adminID), zap.String("reject_reason", rejectReason), ) return nil } // CompleteContractSign 完成合同签署 func (s *CertificationService) CompleteContractSign(ctx context.Context, certificationID, contractURL string) error { cert, err := s.certRepo.GetByID(ctx, certificationID) if err != nil { return fmt.Errorf("认证申请不存在: %w", err) } // 检查当前状态是否可以签署 if cert.Status != enums.StatusContractApproved { return fmt.Errorf("当前状态不允许签署") } // 准备签署元数据 metadata := map[string]interface{}{ "contract_url": contractURL, } // 使用状态机转换状态 if err := s.stateMachine.TransitionTo(ctx, certificationID, enums.StatusContractSigned, true, false, metadata); err != nil { return fmt.Errorf("状态转换失败: %w", err) } s.logger.Info("合同签署完成", zap.String("certification_id", certificationID), ) return nil } // CompleteCertification 完成认证 func (s *CertificationService) CompleteCertification(ctx context.Context, certificationID string) error { cert, err := s.certRepo.GetByID(ctx, certificationID) if err != nil { return fmt.Errorf("认证申请不存在: %w", err) } // 检查当前状态是否可以完成 if cert.Status != enums.StatusContractSigned { return fmt.Errorf("当前状态不允许完成认证") } // 使用状态机转换状态 if err := s.stateMachine.TransitionTo(ctx, certificationID, enums.StatusCompleted, false, false, nil); err != nil { return fmt.Errorf("状态转换失败: %w", err) } s.logger.Info("认证完成", zap.String("certification_id", certificationID), zap.String("user_id", cert.UserID), ) return nil } // RetryFaceVerify 重试人脸识别 func (s *CertificationService) RetryFaceVerify(ctx context.Context, certificationID string) error { cert, err := s.certRepo.GetByID(ctx, certificationID) if err != nil { return fmt.Errorf("认证申请不存在: %w", err) } // 检查是否可以重试 if !cert.CanRetryFaceVerify() { return fmt.Errorf("当前状态不允许重试人脸识别") } // 使用状态机转换状态 if err := s.stateMachine.TransitionTo(ctx, certificationID, enums.StatusInfoSubmitted, true, false, nil); err != nil { return fmt.Errorf("状态转换失败: %w", err) } s.logger.Info("人脸识别重试已准备", zap.String("certification_id", certificationID), ) return nil } // RestartCertification 重新开始认证流程 func (s *CertificationService) RestartCertification(ctx context.Context, certificationID string) error { cert, err := s.certRepo.GetByID(ctx, certificationID) if err != nil { return fmt.Errorf("认证申请不存在: %w", err) } // 检查是否可以重新开始 if !cert.CanRestart() { return fmt.Errorf("当前状态不允许重新开始") } // 使用状态机转换状态 if err := s.stateMachine.TransitionTo(ctx, certificationID, enums.StatusInfoSubmitted, true, false, nil); err != nil { return fmt.Errorf("状态转换失败: %w", err) } s.logger.Info("认证流程重新开始", zap.String("certification_id", certificationID), ) return nil } // GetCertificationByUserID 根据用户ID获取认证申请 func (s *CertificationService) GetCertificationByUserID(ctx context.Context, userID string) (*entities.Certification, error) { return s.certRepo.GetByUserID(ctx, userID) } // GetCertificationByID 根据ID获取认证申请 func (s *CertificationService) GetCertificationByID(ctx context.Context, certificationID string) (*entities.Certification, error) { cert, err := s.certRepo.GetByID(ctx, certificationID) if err != nil { return nil, err } return &cert, nil } // GetFaceVerifyRecords 获取人脸识别记录 func (s *CertificationService) GetFaceVerifyRecords(ctx context.Context, certificationID string) ([]*entities.FaceVerifyRecord, error) { return s.faceVerifyRepo.GetByCertificationID(ctx, certificationID) } // GetContractRecords 获取合同记录 func (s *CertificationService) GetContractRecords(ctx context.Context, certificationID string) ([]*entities.ContractRecord, error) { return s.contractRepo.GetByCertificationID(ctx, certificationID) } // GetCertificationProgress 获取认证进度信息 func (s *CertificationService) GetCertificationProgress(ctx context.Context, certificationID string) (map[string]interface{}, error) { cert, err := s.certRepo.GetByID(ctx, certificationID) if err != nil { return nil, fmt.Errorf("认证申请不存在: %w", err) } progress := map[string]interface{}{ "certification_id": cert.ID, "user_id": cert.UserID, "current_status": cert.Status, "progress_percentage": cert.GetProgressPercentage(), "is_user_action_required": cert.IsUserActionRequired(), "is_admin_action_required": cert.IsAdminActionRequired(), "next_valid_statuses": cert.GetNextValidStatuses(), "created_at": cert.CreatedAt, "updated_at": cert.UpdatedAt, } // 添加时间节点信息 if cert.InfoSubmittedAt != nil { progress["info_submitted_at"] = cert.InfoSubmittedAt } if cert.FaceVerifiedAt != nil { progress["face_verified_at"] = cert.FaceVerifiedAt } if cert.ContractAppliedAt != nil { progress["contract_applied_at"] = cert.ContractAppliedAt } if cert.ContractApprovedAt != nil { progress["contract_approved_at"] = cert.ContractApprovedAt } if cert.ContractSignedAt != nil { progress["contract_signed_at"] = cert.ContractSignedAt } if cert.CompletedAt != nil { progress["completed_at"] = cert.CompletedAt } return progress, nil } // GetCertificationWithDetails 获取认证申请详细信息(包含关联记录) func (s *CertificationService) GetCertificationWithDetails(ctx context.Context, certificationID string) (*entities.Certification, error) { cert, err := s.certRepo.GetByID(ctx, certificationID) if err != nil { return nil, fmt.Errorf("认证申请不存在: %w", err) } // 获取人脸识别记录 faceRecords, err := s.faceVerifyRepo.GetByCertificationID(ctx, certificationID) if err != nil { s.logger.Warn("获取人脸识别记录失败", zap.Error(err)) } else { // 将指针切片转换为值切片 for _, record := range faceRecords { cert.FaceVerifyRecords = append(cert.FaceVerifyRecords, *record) } } // 获取合同记录 contractRecords, err := s.contractRepo.GetByCertificationID(ctx, certificationID) if err != nil { s.logger.Warn("获取合同记录失败", zap.Error(err)) } else { // 将指针切片转换为值切片 for _, record := range contractRecords { cert.ContractRecords = append(cert.ContractRecords, *record) } } // 获取营业执照上传记录 licenseRecord, err := s.licenseRepo.GetByCertificationID(ctx, certificationID) if err != nil { s.logger.Warn("获取营业执照记录失败", zap.Error(err)) } else { cert.LicenseUploadRecord = licenseRecord } return &cert, nil } // UpdateOCRResult 更新OCR识别结果 func (s *CertificationService) UpdateOCRResult(ctx context.Context, certificationID, ocrRequestID string, confidence float64) error { cert, err := s.certRepo.GetByID(ctx, certificationID) if err != nil { return fmt.Errorf("认证申请不存在: %w", err) } // 更新OCR信息 cert.OCRRequestID = ocrRequestID cert.OCRConfidence = confidence if err := s.certRepo.Update(ctx, cert); err != nil { s.logger.Error("更新OCR结果失败", zap.Error(err)) return fmt.Errorf("更新OCR结果失败: %w", err) } s.logger.Info("OCR结果更新成功", zap.String("certification_id", certificationID), zap.String("ocr_request_id", ocrRequestID), zap.Float64("confidence", confidence), ) return nil } // ValidateLicenseUpload 验证营业执照上传 func (s *CertificationService) ValidateLicenseUpload(ctx context.Context, userID, fileName string, fileSize int64) error { // 检查文件类型 allowedExts := []string{".jpg", ".jpeg", ".png", ".pdf"} ext := strings.ToLower(filepath.Ext(fileName)) isAllowed := false for _, allowedExt := range allowedExts { if ext == allowedExt { isAllowed = true break } } if !isAllowed { return fmt.Errorf("文件格式不支持,仅支持 JPG、PNG、PDF 格式") } // 检查文件大小(限制为10MB) const maxFileSize = 10 * 1024 * 1024 // 10MB if fileSize > maxFileSize { return fmt.Errorf("文件大小不能超过10MB") } // 检查用户是否已有上传记录(可选,根据业务需求决定) // existingRecords, err := s.licenseRepo.GetByUserID(ctx, userID, 1, 1) // if err == nil && len(existingRecords) > 0 { // return fmt.Errorf("用户已有营业执照上传记录") // } return nil } // CreateLicenseUploadRecord 创建营业执照上传记录 func (s *CertificationService) CreateLicenseUploadRecord(ctx context.Context, userID, fileName string, fileSize int64, uploadResult *sharedStorage.UploadResult) (*entities.LicenseUploadRecord, error) { // 获取文件MIME类型 fileType := mime.TypeByExtension(filepath.Ext(fileName)) if fileType == "" { fileType = "application/octet-stream" } // 创建营业执照上传记录 licenseRecord := &entities.LicenseUploadRecord{ UserID: userID, OriginalFileName: fileName, FileURL: uploadResult.URL, FileSize: fileSize, FileType: fileType, QiNiuKey: uploadResult.Key, OCRProcessed: false, OCRSuccess: false, } createdLicense, err := s.licenseRepo.Create(ctx, *licenseRecord) if err != nil { s.logger.Error("创建营业执照记录失败", zap.Error(err)) return nil, fmt.Errorf("创建营业执照记录失败: %w", err) } licenseRecord = &createdLicense s.logger.Info("营业执照上传记录创建成功", zap.String("license_id", licenseRecord.ID), zap.String("user_id", userID), zap.String("file_name", fileName), ) return licenseRecord, nil } // ProcessOCRAsync 异步处理OCR识别 func (s *CertificationService) ProcessOCRAsync(ctx context.Context, licenseID string, fileBytes []byte) error { // 创建临时文件 tempFile, err := os.CreateTemp("", "license-upload-*.jpg") if err != nil { s.logger.Error("创建临时文件失败", zap.Error(err)) return fmt.Errorf("创建临时文件失败: %w", err) } defer func() { tempFile.Close() os.Remove(tempFile.Name()) // 清理临时文件 }() // 将文件内容写入临时文件 if _, err := tempFile.Write(fileBytes); err != nil { s.logger.Error("写入临时文件失败", zap.Error(err)) return fmt.Errorf("写入临时文件失败: %w", err) } // 调用OCR服务识别营业执照 ocrResult, err := s.ocrService.RecognizeBusinessLicense(ctx, fileBytes) if err != nil { s.logger.Error("OCR识别失败", zap.Error(err), zap.String("license_id", licenseID)) // 更新OCR处理状态为失败 if updateErr := s.updateOCRStatus(ctx, licenseID, false, "", 0, err.Error()); updateErr != nil { s.logger.Error("更新OCR失败状态失败", zap.Error(updateErr)) } return err } // 将OCR结果转换为JSON字符串 ocrRawData := fmt.Sprintf(`{"company_name":"%s","unified_social_code":"%s","legal_person_name":"%s","confidence":%.2f}`, ocrResult.CompanyName, ocrResult.UnifiedSocialCode, ocrResult.LegalPersonName, ocrResult.Confidence) // 更新OCR处理状态 success := ocrResult.Confidence >= 0.8 // 置信度大于0.8认为成功 if err := s.updateOCRStatus(ctx, licenseID, true, ocrRawData, ocrResult.Confidence, ""); err != nil { s.logger.Error("更新OCR结果失败", zap.Error(err)) return err } s.logger.Info("OCR识别完成", zap.String("license_id", licenseID), zap.Bool("success", success), zap.Float64("confidence", ocrResult.Confidence), zap.String("company_name", ocrResult.CompanyName), zap.String("legal_person", ocrResult.LegalPersonName), ) return nil } // updateOCRStatus 更新OCR处理状态 func (s *CertificationService) updateOCRStatus(ctx context.Context, licenseID string, processed bool, ocrRawData string, confidence float64, errorMessage string) error { licenseRecord, err := s.licenseRepo.GetByID(ctx, licenseID) if err != nil { return fmt.Errorf("获取营业执照记录失败: %w", err) } licenseRecord.OCRProcessed = processed if processed { licenseRecord.OCRSuccess = confidence >= 0.8 licenseRecord.OCRRawData = ocrRawData licenseRecord.OCRConfidence = confidence } else { licenseRecord.OCRSuccess = false licenseRecord.OCRErrorMessage = errorMessage } if err := s.licenseRepo.Update(ctx, licenseRecord); err != nil { return fmt.Errorf("更新OCR结果失败: %w", err) } return nil } // validateLicenseFile 验证营业执照文件 func (s *CertificationService) validateLicenseFile(fileBytes []byte, fileName string) error { // 检查文件大小(最大10MB) if len(fileBytes) > 10*1024*1024 { return fmt.Errorf("文件大小超过限制,最大支持10MB") } // 检查文件类型 ext := filepath.Ext(fileName) allowedExts := []string{".jpg", ".jpeg", ".png", ".bmp"} isAllowed := false for _, allowedExt := range allowedExts { if ext == allowedExt { isAllowed = true break } } if !isAllowed { return fmt.Errorf("不支持的文件格式,仅支持jpg、jpeg、png、bmp格式") } return nil } // UploadBusinessLicense 上传营业执照,先OCR识别,成功后再上传到七牛 func (s *CertificationService) UploadBusinessLicense(ctx context.Context, userID string, fileBytes []byte, fileName string) (*entities.LicenseUploadRecord, *responses.BusinessLicenseResult, error) { s.logger.Info("开始上传营业执照", zap.String("user_id", userID), zap.String("file_name", fileName), zap.Int("file_size", len(fileBytes)), ) // 1. 验证文件 if err := s.validateLicenseFile(fileBytes, fileName); err != nil { return nil, nil, fmt.Errorf("文件验证失败: %w", err) } // 2. 先OCR识别 s.logger.Info("开始OCR识别营业执照") ocrResult, err := s.ocrService.RecognizeBusinessLicense(ctx, fileBytes) if err != nil { s.logger.Error("OCR识别失败", zap.Error(err)) return nil, nil, fmt.Errorf("营业执照识别失败,请上传清晰的营业执照图片") } if ocrResult.CompanyName == "" || ocrResult.LegalPersonName == "" { s.logger.Warn("OCR识别未提取到关键信息") return nil, nil, fmt.Errorf("营业执照识别失败,请上传清晰的营业执照图片") } // 3. 识别成功后上传到七牛 uploadResult, err := s.storageService.UploadFile(ctx, fileBytes, fileName) if err != nil { s.logger.Error("文件上传失败", zap.Error(err)) return nil, nil, fmt.Errorf("文件上传失败: %w", err) } // 4. 创建上传记录 uploadRecord := &entities.LicenseUploadRecord{ UserID: userID, OriginalFileName: fileName, FileURL: uploadResult.URL, QiNiuKey: uploadResult.Key, FileSize: uploadResult.Size, FileType: uploadResult.MimeType, OCRProcessed: true, OCRSuccess: true, OCRConfidence: ocrResult.Confidence, OCRRawData: "", // 可存储OCR原始数据 OCRErrorMessage: "", } // 5. 保存上传记录并获取新创建的记录 createdRecord, err := s.licenseRepo.Create(ctx, *uploadRecord) if err != nil { s.logger.Error("保存上传记录失败", zap.Error(err)) return nil, nil, fmt.Errorf("保存上传记录失败: %w", err) } uploadRecord.ID = createdRecord.ID s.logger.Info("营业执照上传和OCR识别完成", zap.String("user_id", userID), zap.String("file_url", uploadResult.URL), zap.Bool("ocr_success", uploadRecord.OCRSuccess), ) return uploadRecord, ocrResult, nil } // getOCRStatus 根据OCR结果确定状态 func (s *CertificationService) getOCRStatus(result *responses.BusinessLicenseResult) string { if result.CompanyName == "" && result.LegalPersonName == "" { return "failed" } if result.CompanyName != "" && result.LegalPersonName != "" { return "success" } return "partial" }