Files
tyapi-server/internal/domains/certification/services/certification_service.go
2025-07-13 16:36:20 +08:00

775 lines
25 KiB
Go
Raw 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 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"
}