This commit is contained in:
2025-07-20 20:53:26 +08:00
parent 83bf9aea7d
commit 8ad1d7288e
158 changed files with 18156 additions and 13188 deletions

View File

@@ -2,112 +2,74 @@ 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/repositories"
"tyapi-server/internal/domains/certification/enums"
"tyapi-server/internal/domains/certification/services"
user_entities "tyapi-server/internal/domains/user/entities"
user_repositories "tyapi-server/internal/domains/user/repositories"
"tyapi-server/internal/shared/ocr"
"tyapi-server/internal/shared/storage"
user_services "tyapi-server/internal/domains/user/services"
"tyapi-server/internal/shared/database"
esign_service "tyapi-server/internal/shared/esign"
)
// CertificationApplicationServiceImpl 认证应用服务实现
// 负责业务流程编排、事务管理、数据转换,不直接操作仓库
type CertificationApplicationServiceImpl struct {
certRepo repositories.CertificationRepository
licenseRepo repositories.LicenseUploadRecordRepository
faceVerifyRepo repositories.FaceVerifyRecordRepository
contractRepo repositories.ContractRecordRepository
certService *services.CertificationService
stateMachine *services.CertificationStateMachine
storageService storage.StorageService
ocrService ocr.OCRService
enterpriseInfoRepo user_repositories.EnterpriseInfoRepository
logger *zap.Logger
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(
certRepo repositories.CertificationRepository,
licenseRepo repositories.LicenseUploadRecordRepository,
faceVerifyRepo repositories.FaceVerifyRecordRepository,
contractRepo repositories.ContractRecordRepository,
certService *services.CertificationService,
stateMachine *services.CertificationStateMachine,
storageService storage.StorageService,
ocrService ocr.OCRService,
enterpriseInfoRepo user_repositories.EnterpriseInfoRepository,
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{
certRepo: certRepo,
licenseRepo: licenseRepo,
faceVerifyRepo: faceVerifyRepo,
contractRepo: contractRepo,
certService: certService,
stateMachine: stateMachine,
storageService: storageService,
ocrService: ocrService,
enterpriseInfoRepo: enterpriseInfoRepo,
logger: logger,
certManagementService: certManagementService,
certWorkflowService: certWorkflowService,
certificationEsignService: certificationEsignService,
enterpriseService: enterpriseService,
esignService: esignService,
enterpriseRecordService: enterpriseRecordService,
smsCodeService: smsCodeService,
txManager: txManager,
logger: logger,
}
}
// CreateCertification 创建认证申请
func (s *CertificationApplicationServiceImpl) CreateCertification(ctx context.Context, cmd *commands.CreateCertificationCommand) (*responses.CertificationResponse, error) {
// 使用领域服务创建认证申请
certification, err := s.certService.CreateCertification(ctx, cmd.UserID)
if err != nil {
return nil, err
// 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("验证码错误或已过期")
}
// 构建响应
response := &responses.CertificationResponse{
ID: certification.ID,
UserID: certification.UserID,
Status: certification.Status,
StatusName: string(certification.Status),
Progress: certification.GetProgressPercentage(),
IsUserActionRequired: certification.IsUserActionRequired(),
IsAdminActionRequired: certification.IsAdminActionRequired(),
InfoSubmittedAt: certification.InfoSubmittedAt,
FaceVerifiedAt: certification.FaceVerifiedAt,
ContractAppliedAt: certification.ContractAppliedAt,
ContractApprovedAt: certification.ContractApprovedAt,
ContractSignedAt: certification.ContractSignedAt,
CompletedAt: certification.CompletedAt,
ContractURL: certification.ContractURL,
SigningURL: certification.SigningURL,
RejectReason: certification.RejectReason,
CreatedAt: certification.CreatedAt,
UpdatedAt: certification.UpdatedAt,
}
s.logger.Info("认证申请创建成功",
zap.String("certification_id", certification.ID),
zap.String("user_id", cmd.UserID),
)
return response, nil
}
// CreateEnterpriseInfo 创建企业信息
func (s *CertificationApplicationServiceImpl) CreateEnterpriseInfo(ctx context.Context, cmd *commands.CreateEnterpriseInfoCommand) (*responses.EnterpriseInfoResponse, error) {
// 检查用户是否已有企业信息
existingInfo, err := s.enterpriseInfoRepo.GetByUserID(ctx, cmd.UserID)
if err == nil && existingInfo != nil {
return nil, fmt.Errorf("用户已有企业信息")
}
// 检查统一社会信用代码是否已存在
exists, err := s.enterpriseInfoRepo.CheckUnifiedSocialCodeExists(ctx, cmd.UnifiedSocialCode, "")
// 2. 验证企业信息(检查统一社会信用代码是否已存在)
exists, err := s.enterpriseService.CheckUnifiedSocialCodeExists(ctx, cmd.UnifiedSocialCode, "")
if err != nil {
return nil, fmt.Errorf("检查企业信息失败: %w", err)
}
@@ -115,494 +77,379 @@ func (s *CertificationApplicationServiceImpl) CreateEnterpriseInfo(ctx context.C
return nil, fmt.Errorf("统一社会信用代码已存在")
}
// 创建企业信息
enterpriseInfo := &user_entities.EnterpriseInfo{
UserID: cmd.UserID,
CompanyName: cmd.CompanyName,
UnifiedSocialCode: cmd.UnifiedSocialCode,
LegalPersonName: cmd.LegalPersonName,
LegalPersonID: cmd.LegalPersonID,
// 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)
}
}
createdEnterpriseInfo, err := s.enterpriseInfoRepo.Create(ctx, *enterpriseInfo)
if err != nil {
s.logger.Error("创建企业信息失败", zap.Error(err))
return nil, fmt.Errorf("创建企业信息失败: %w", err)
// 4. 创建记录
if certification.Status != enums.StatusPending && certification.Status != enums.StatusInfoSubmitted {
return nil, fmt.Errorf("当前状态不允许提交企业信息")
}
s.logger.Info("企业信息创建成功",
_, 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("enterprise_id", enterpriseInfo.ID),
zap.String("certification_id", certification.ID),
zap.String("company_name", cmd.CompanyName),
)
return &responses.EnterpriseInfoResponse{
ID: createdEnterpriseInfo.ID,
CompanyName: enterpriseInfo.CompanyName,
UnifiedSocialCode: enterpriseInfo.UnifiedSocialCode,
LegalPersonName: enterpriseInfo.LegalPersonName,
LegalPersonID: enterpriseInfo.LegalPersonID,
IsOCRVerified: enterpriseInfo.IsOCRVerified,
IsFaceVerified: enterpriseInfo.IsFaceVerified,
CreatedAt: enterpriseInfo.CreatedAt,
UpdatedAt: enterpriseInfo.UpdatedAt,
}, nil
// 转换状态
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
}
// UploadLicense 上传营业执照
func (s *CertificationApplicationServiceImpl) UploadLicense(ctx context.Context, cmd *commands.UploadLicenseCommand) (*responses.UploadLicenseResponse, error) {
// 1. 业务规则验证 - 调用领域服务
if err := s.certService.ValidateLicenseUpload(ctx, cmd.UserID, cmd.FileName, cmd.FileSize); err != nil {
return nil, err
}
// 2. 上传文件到存储服务
uploadResult, err := s.storageService.UploadFile(ctx, cmd.FileBytes, cmd.FileName)
if err != nil {
s.logger.Error("上传营业执照失败", zap.Error(err))
return nil, fmt.Errorf("上传营业执照失败: %w", err)
}
// 3. 创建营业执照上传记录 - 调用领域服务
licenseRecord, err := s.certService.CreateLicenseUploadRecord(ctx, cmd.UserID, cmd.FileName, cmd.FileSize, uploadResult)
if err != nil {
s.logger.Error("创建营业执照记录失败", zap.Error(err))
return nil, fmt.Errorf("创建营业执照记录失败: %w", err)
}
// 4. 异步处理OCR识别 - 使用任务队列或后台任务
go s.processOCRAsync(ctx, licenseRecord.ID, cmd.FileBytes)
s.logger.Info("营业执照上传成功",
zap.String("user_id", cmd.UserID),
zap.String("license_id", licenseRecord.ID),
zap.String("file_url", uploadResult.URL),
)
// 5. 构建响应
response := &responses.UploadLicenseResponse{
UploadRecordID: licenseRecord.ID,
FileURL: uploadResult.URL,
OCRProcessed: false,
OCRSuccess: false,
}
// 6. 如果OCR处理很快完成尝试获取结果
// 这里可以添加一个简单的轮询机制或者使用WebSocket推送结果
// 暂时返回基础信息前端可以通过查询接口获取OCR结果
return response, nil
}
// UploadBusinessLicense 上传营业执照并同步OCR识别
func (s *CertificationApplicationServiceImpl) UploadBusinessLicense(ctx context.Context, userID string, fileBytes []byte, fileName string) (*responses.UploadLicenseResponse, error) {
s.logger.Info("开始处理营业执照上传",
zap.String("user_id", userID),
zap.String("file_name", fileName),
)
// 调用领域服务进行上传和OCR识别
uploadRecord, ocrResult, err := s.certService.UploadBusinessLicense(ctx, userID, fileBytes, fileName)
if err != nil {
s.logger.Error("营业执照上传失败", zap.Error(err))
return nil, err
}
// 构建响应
response := &responses.UploadLicenseResponse{
UploadRecordID: uploadRecord.ID,
FileURL: uploadRecord.FileURL,
OCRProcessed: uploadRecord.OCRProcessed,
OCRSuccess: uploadRecord.OCRSuccess,
OCRConfidence: uploadRecord.OCRConfidence,
OCRErrorMessage: uploadRecord.OCRErrorMessage,
}
// 如果OCR成功添加识别结果
if ocrResult != nil && uploadRecord.OCRSuccess {
response.EnterpriseName = ocrResult.CompanyName
response.CreditCode = ocrResult.UnifiedSocialCode
response.LegalPerson = ocrResult.LegalPersonName
}
s.logger.Info("营业执照上传完成",
zap.String("user_id", userID),
zap.String("upload_record_id", uploadRecord.ID),
zap.Bool("ocr_success", uploadRecord.OCRSuccess),
)
return response, nil
}
// SubmitEnterpriseInfo 提交企业信息
func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(ctx context.Context, cmd *commands.SubmitEnterpriseInfoCommand) (*responses.EnterpriseInfoResponse, error) {
// 根据用户ID获取认证申请
certification, err := s.certRepo.GetByUserID(ctx, cmd.UserID)
// 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)
}
// 设置认证ID
cmd.CertificationID = certification.ID
// 2. 检查认证状态
if certification.Status != enums.StatusInfoSubmitted {
return nil, fmt.Errorf("当前状态不允许进行企业认证")
}
// 调用领域服务提交企业信息
if err := s.certService.SubmitEnterpriseInfo(ctx, certification.ID); err != nil {
// 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
}
// 创建企业信息
enterpriseInfo := &user_entities.EnterpriseInfo{
UserID: cmd.UserID,
CompanyName: cmd.CompanyName,
UnifiedSocialCode: cmd.UnifiedSocialCode,
LegalPersonName: cmd.LegalPersonName,
LegalPersonID: cmd.LegalPersonID,
}
// 2. 构建响应
return s.buildCertificationResponse(certification), nil
}
*enterpriseInfo, err = s.enterpriseInfoRepo.Create(ctx, *enterpriseInfo)
// 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 {
s.logger.Error("创建企业信息失败", zap.Error(err))
return nil, fmt.Errorf("创建企业信息失败: %w", err)
// 如果用户没有认证申请,返回错误
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("用户尚未创建认证申请")
}
return nil, err
}
s.logger.Info("企业信息提交成功",
zap.String("user_id", cmd.UserID),
zap.String("certification_id", certification.ID),
zap.String("enterprise_id", enterpriseInfo.ID),
)
// 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,
IsOCRVerified: enterpriseInfo.IsOCRVerified,
IsFaceVerified: enterpriseInfo.IsFaceVerified,
CreatedAt: enterpriseInfo.CreatedAt,
UpdatedAt: enterpriseInfo.UpdatedAt,
}, nil
}
}
// InitiateFaceVerify 发起人脸识别验证
func (s *CertificationApplicationServiceImpl) InitiateFaceVerify(ctx context.Context, cmd *commands.InitiateFaceVerifyCommand) (*responses.FaceVerifyResponse, error) {
// 根据用户ID获取认证申请 - 这里需要从Handler传入用户ID
// 由于cmd中没有UserID字段我们需要修改Handler的调用方式
// 暂时使用certificationID来获取认证申请
certification, err := s.certRepo.GetByID(ctx, cmd.CertificationID)
if err != nil {
return nil, fmt.Errorf("认证申请不存在: %w", err)
}
// 调用领域服务发起人脸识别
faceVerifyRecord, err := s.certService.InitiateFaceVerify(ctx, certification.ID, cmd.RealName, cmd.IDCardNumber)
if err != nil {
return nil, err
}
// 构建验证URL这里应该根据实际的人脸识别服务生成
verifyURL := fmt.Sprintf("/api/certification/face-verify/%s?return_url=%s", faceVerifyRecord.ID, cmd.ReturnURL)
s.logger.Info("人脸识别验证发起成功",
zap.String("certification_id", certification.ID),
zap.String("face_verify_id", faceVerifyRecord.ID),
)
return &responses.FaceVerifyResponse{
CertifyID: faceVerifyRecord.ID,
VerifyURL: verifyURL,
ExpiresAt: faceVerifyRecord.ExpiresAt,
}, nil
}
// ApplyContract 申请合同
func (s *CertificationApplicationServiceImpl) ApplyContract(ctx context.Context, userID string) (*responses.CertificationResponse, error) {
// 根据用户ID获取认证申请
certification, err := s.certRepo.GetByUserID(ctx, userID)
if err != nil {
return nil, fmt.Errorf("用户尚未创建认证申请: %w", err)
}
// 调用领域服务申请合同
if err := s.certService.ApplyContract(ctx, certification.ID); err != nil {
return nil, err
}
// 重新获取更新后的认证申请
updatedCertification, err := s.certRepo.GetByID(ctx, certification.ID)
if err != nil {
return nil, err
}
s.logger.Info("合同申请成功",
zap.String("user_id", userID),
zap.String("certification_id", certification.ID),
)
return s.buildCertificationResponse(&updatedCertification), nil
}
// GetCertificationStatus 获取认证状态
func (s *CertificationApplicationServiceImpl) GetCertificationStatus(ctx context.Context, query *queries.GetCertificationStatusQuery) (*responses.CertificationResponse, error) {
// 根据用户ID获取认证申请
certification, err := s.certRepo.GetByUserID(ctx, query.UserID)
if err != nil {
// 如果用户没有认证申请,返回一个表示未开始的状态
if err.Error() == "认证申请不存在" || err.Error() == "record not found" {
return &responses.CertificationResponse{
ID: "",
UserID: query.UserID,
Status: "not_started",
StatusName: "未开始认证",
Progress: 0,
IsUserActionRequired: true,
IsAdminActionRequired: false,
InfoSubmittedAt: nil,
FaceVerifiedAt: nil,
ContractAppliedAt: nil,
ContractApprovedAt: nil,
ContractSignedAt: nil,
CompletedAt: nil,
ContractURL: "",
SigningURL: "",
RejectReason: "",
CreatedAt: time.Time{},
UpdatedAt: time.Time{},
}, nil
// 企业认证成功后操作
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)
}
return nil, err
}
// 构建响应
response := s.buildCertificationResponse(certification)
return response, nil
}
// GetCertificationDetails 获取认证详情
func (s *CertificationApplicationServiceImpl) GetCertificationDetails(ctx context.Context, query *queries.GetCertificationDetailsQuery) (*responses.CertificationResponse, error) {
// 根据用户ID获取认证申请
certification, err := s.certRepo.GetByUserID(ctx, query.UserID)
if err != nil {
// 如果用户没有认证申请,返回错误
if err.Error() == "认证申请不存在" || err.Error() == "record not found" {
return nil, fmt.Errorf("用户尚未创建认证申请")
// 2. 转换状态
if err := s.certWorkflowService.CompleteEnterpriseVerification(txCtx, certification.ID); err != nil {
return err
}
return nil, err
}
// 获取认证申请详细信息
certificationWithDetails, err := s.certService.GetCertificationWithDetails(ctx, certification.ID)
if err != nil {
return nil, err
}
// 构建响应
response := s.buildCertificationResponse(certificationWithDetails)
// 添加企业信息
if certification.UserID != "" {
enterpriseInfo, err := s.enterpriseInfoRepo.GetByUserID(ctx, certification.UserID)
if err == nil && enterpriseInfo != nil {
response.Enterprise = &responses.EnterpriseInfoResponse{
ID: enterpriseInfo.ID,
CompanyName: enterpriseInfo.CompanyName,
UnifiedSocialCode: enterpriseInfo.UnifiedSocialCode,
LegalPersonName: enterpriseInfo.LegalPersonName,
LegalPersonID: enterpriseInfo.LegalPersonID,
IsOCRVerified: enterpriseInfo.IsOCRVerified,
IsFaceVerified: enterpriseInfo.IsFaceVerified,
CreatedAt: enterpriseInfo.CreatedAt,
UpdatedAt: enterpriseInfo.UpdatedAt,
}
// 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 response, nil
}
// CompleteFaceVerify 完成人脸识别验证
func (s *CertificationApplicationServiceImpl) CompleteFaceVerify(ctx context.Context, faceVerifyID string, isSuccess bool) error {
return s.certService.CompleteFaceVerify(ctx, faceVerifyID, isSuccess)
}
// ApproveContract 管理员审核合同
func (s *CertificationApplicationServiceImpl) ApproveContract(ctx context.Context, certificationID, adminID, signingURL, approvalNotes string) error {
return s.certService.ApproveContract(ctx, certificationID, adminID, signingURL, approvalNotes)
}
// RejectContract 管理员拒绝合同
func (s *CertificationApplicationServiceImpl) RejectContract(ctx context.Context, certificationID, adminID, rejectReason string) error {
return s.certService.RejectContract(ctx, certificationID, adminID, rejectReason)
}
// CompleteContractSign 完成合同签署
func (s *CertificationApplicationServiceImpl) CompleteContractSign(ctx context.Context, certificationID, contractURL string) error {
return s.certService.CompleteContractSign(ctx, certificationID, contractURL)
}
// CompleteCertification 完成认证
func (s *CertificationApplicationServiceImpl) CompleteCertification(ctx context.Context, certificationID string) error {
return s.certService.CompleteCertification(ctx, certificationID)
}
// RetryStep 重试认证步骤
func (s *CertificationApplicationServiceImpl) RetryStep(ctx context.Context, cmd *commands.RetryStepCommand) error {
switch cmd.Step {
case "face_verify":
return s.certService.RetryFaceVerify(ctx, cmd.CertificationID)
case "restart":
return s.certService.RestartCertification(ctx, cmd.CertificationID)
default:
return fmt.Errorf("不支持的重试步骤: %s", cmd.Step)
}
}
// GetCertificationProgress 获取认证进度
func (s *CertificationApplicationServiceImpl) GetCertificationProgress(ctx context.Context, userID string) (map[string]interface{}, error) {
// 根据用户ID获取认证申请
certification, err := s.certRepo.GetByUserID(ctx, userID)
return nil
})
if err != nil {
// 如果用户没有认证申请,返回未开始状态
if err.Error() == "认证申请不存在" || err.Error() == "record not found" {
return map[string]interface{}{
"certification_id": "",
"user_id": userID,
"current_status": "not_started",
"status_name": "未开始认证",
"progress_percentage": 0,
"is_user_action_required": true,
"is_admin_action_required": false,
"next_valid_statuses": []string{"pending"},
"message": "用户尚未开始认证流程",
"created_at": nil,
"updated_at": nil,
}, nil
}
return nil, err
return fmt.Errorf("完成企业认证失败: %w", err)
}
// 获取认证进度
return s.certService.GetCertificationProgress(ctx, certification.ID)
}
// RetryFaceVerify 重试人脸识别
func (s *CertificationApplicationServiceImpl) RetryFaceVerify(ctx context.Context, userID string) (*responses.FaceVerifyResponse, error) {
// 根据用户ID获取认证申请
certification, err := s.certRepo.GetByUserID(ctx, userID)
if err != nil {
return nil, fmt.Errorf("用户尚未创建认证申请: %w", err)
}
// 调用领域服务重试人脸识别
if err := s.certService.RetryFaceVerify(ctx, certification.ID); err != nil {
return nil, err
}
// 重新发起人脸识别
faceVerifyRecord, err := s.certService.InitiateFaceVerify(ctx, certification.ID, "", "")
if err != nil {
return nil, err
}
// 构建验证URL
verifyURL := fmt.Sprintf("/api/certification/face-verify/%s", faceVerifyRecord.ID)
return &responses.FaceVerifyResponse{
CertifyID: faceVerifyRecord.ID,
VerifyURL: verifyURL,
ExpiresAt: faceVerifyRecord.ExpiresAt,
}, nil
}
// RetryContractSign 重试合同签署
func (s *CertificationApplicationServiceImpl) RetryContractSign(ctx context.Context, userID string) (*responses.CertificationResponse, error) {
// 根据用户ID获取认证申请
certification, err := s.certRepo.GetByUserID(ctx, userID)
if err != nil {
return nil, fmt.Errorf("用户尚未创建认证申请: %w", err)
}
// 重新获取更新后的认证申请
updatedCertification, err := s.certRepo.GetByID(ctx, certification.ID)
if err != nil {
return nil, err
}
s.logger.Info("合同签署重试准备完成",
zap.String("user_id", userID),
zap.String("certification_id", certification.ID),
)
return s.buildCertificationResponse(&updatedCertification), nil
}
// RestartCertification 重新开始认证
func (s *CertificationApplicationServiceImpl) RestartCertification(ctx context.Context, certificationID string) error {
return s.certService.RestartCertification(ctx, certificationID)
}
// buildCertificationResponse 构建认证响应
func (s *CertificationApplicationServiceImpl) buildCertificationResponse(certification *entities.Certification) *responses.CertificationResponse {
return &responses.CertificationResponse{
ID: certification.ID,
UserID: certification.UserID,
Status: certification.Status,
StatusName: string(certification.Status),
Progress: certification.GetProgressPercentage(),
IsUserActionRequired: certification.IsUserActionRequired(),
IsAdminActionRequired: certification.IsAdminActionRequired(),
InfoSubmittedAt: certification.InfoSubmittedAt,
FaceVerifiedAt: certification.FaceVerifiedAt,
ContractAppliedAt: certification.ContractAppliedAt,
ContractApprovedAt: certification.ContractApprovedAt,
ContractSignedAt: certification.ContractSignedAt,
CompletedAt: certification.CompletedAt,
ContractURL: certification.ContractURL,
SigningURL: certification.SigningURL,
RejectReason: certification.RejectReason,
CreatedAt: certification.CreatedAt,
UpdatedAt: certification.UpdatedAt,
}
}
// processOCRAsync 异步处理OCR识别
func (s *CertificationApplicationServiceImpl) processOCRAsync(ctx context.Context, licenseID string, fileBytes []byte) {
// 调用领域服务处理OCR识别
if err := s.certService.ProcessOCRAsync(ctx, licenseID, fileBytes); err != nil {
s.logger.Error("OCR处理失败",
zap.String("license_id", licenseID),
zap.Error(err),
)
}
}
// GetLicenseOCRResult 获取营业执照OCR识别结果
func (s *CertificationApplicationServiceImpl) GetLicenseOCRResult(ctx context.Context, recordID string) (*responses.UploadLicenseResponse, error) {
// 获取营业执照上传记录
licenseRecord, err := s.licenseRepo.GetByID(ctx, recordID)
if err != nil {
s.logger.Error("获取营业执照记录失败", zap.Error(err))
return nil, fmt.Errorf("获取营业执照记录失败: %w", err)
}
// 构建响应
response := &responses.UploadLicenseResponse{
UploadRecordID: licenseRecord.ID,
FileURL: licenseRecord.FileURL,
OCRProcessed: licenseRecord.OCRProcessed,
OCRSuccess: licenseRecord.OCRSuccess,
OCRConfidence: licenseRecord.OCRConfidence,
OCRErrorMessage: licenseRecord.OCRErrorMessage,
}
// 如果OCR成功解析OCR结果
if licenseRecord.OCRSuccess && licenseRecord.OCRRawData != "" {
// 这里可以解析OCR原始数据提取企业信息
// 简化处理,直接返回原始数据中的关键信息
// 实际项目中可以使用JSON解析
response.EnterpriseName = "已识别" // 从OCR数据中提取
response.CreditCode = "已识别" // 从OCR数据中提取
response.LegalPerson = "已识别" // 从OCR数据中提取
}
return response, nil
return nil
}