temp
This commit is contained in:
@@ -1,110 +0,0 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"tyapi-server/internal/domains/certification/enums"
|
||||
)
|
||||
|
||||
// CertificationCreateRequest 创建认证申请请求
|
||||
type CertificationCreateRequest struct {
|
||||
UserID string `json:"user_id" binding:"required"`
|
||||
}
|
||||
|
||||
// CertificationCreateResponse 创建认证申请响应
|
||||
type CertificationCreateResponse struct {
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
Status enums.CertificationStatus `json:"status"`
|
||||
}
|
||||
|
||||
// CertificationStatusResponse 认证状态响应
|
||||
type CertificationStatusResponse struct {
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
Status enums.CertificationStatus `json:"status"`
|
||||
StatusName string `json:"status_name"`
|
||||
Progress int `json:"progress"`
|
||||
IsUserActionRequired bool `json:"is_user_action_required"`
|
||||
IsAdminActionRequired bool `json:"is_admin_action_required"`
|
||||
|
||||
// 时间节点
|
||||
InfoSubmittedAt *time.Time `json:"info_submitted_at,omitempty"`
|
||||
FaceVerifiedAt *time.Time `json:"face_verified_at,omitempty"`
|
||||
ContractAppliedAt *time.Time `json:"contract_applied_at,omitempty"`
|
||||
ContractApprovedAt *time.Time `json:"contract_approved_at,omitempty"`
|
||||
ContractSignedAt *time.Time `json:"contract_signed_at,omitempty"`
|
||||
CompletedAt *time.Time `json:"completed_at,omitempty"`
|
||||
|
||||
// 关联信息
|
||||
Enterprise *EnterpriseInfoResponse `json:"enterprise,omitempty"`
|
||||
ContractURL string `json:"contract_url,omitempty"`
|
||||
SigningURL string `json:"signing_url,omitempty"`
|
||||
RejectReason string `json:"reject_reason,omitempty"`
|
||||
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// SubmitEnterpriseInfoRequest 提交企业信息请求
|
||||
type SubmitEnterpriseInfoRequest struct {
|
||||
CompanyName string `json:"company_name" binding:"required"`
|
||||
UnifiedSocialCode string `json:"unified_social_code" binding:"required"`
|
||||
LegalPersonName string `json:"legal_person_name" binding:"required"`
|
||||
LegalPersonID string `json:"legal_person_id" binding:"required"`
|
||||
LicenseUploadRecordID string `json:"license_upload_record_id" binding:"required"`
|
||||
}
|
||||
|
||||
// SubmitEnterpriseInfoResponse 提交企业信息响应
|
||||
type SubmitEnterpriseInfoResponse struct {
|
||||
ID string `json:"id"`
|
||||
Status enums.CertificationStatus `json:"status"`
|
||||
Enterprise *EnterpriseInfoResponse `json:"enterprise"`
|
||||
}
|
||||
|
||||
// FaceVerifyRequest 人脸识别请求
|
||||
type FaceVerifyRequest struct {
|
||||
RealName string `json:"real_name" binding:"required"`
|
||||
IDCardNumber string `json:"id_card_number" binding:"required"`
|
||||
ReturnURL string `json:"return_url" binding:"required"`
|
||||
}
|
||||
|
||||
// FaceVerifyResponse 人脸识别响应
|
||||
type FaceVerifyResponse struct {
|
||||
CertifyID string `json:"certify_id"`
|
||||
VerifyURL string `json:"verify_url"`
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
}
|
||||
|
||||
// ApplyContractRequest 申请合同请求(无需额外参数)
|
||||
type ApplyContractRequest struct{}
|
||||
|
||||
// ApplyContractResponse 申请合同响应
|
||||
type ApplyContractResponse struct {
|
||||
ID string `json:"id"`
|
||||
Status enums.CertificationStatus `json:"status"`
|
||||
ContractAppliedAt time.Time `json:"contract_applied_at"`
|
||||
}
|
||||
|
||||
// SignContractRequest 签署合同请求
|
||||
type SignContractRequest struct {
|
||||
SignatureData string `json:"signature_data,omitempty"`
|
||||
}
|
||||
|
||||
// SignContractResponse 签署合同响应
|
||||
type SignContractResponse struct {
|
||||
ID string `json:"id"`
|
||||
Status enums.CertificationStatus `json:"status"`
|
||||
ContractSignedAt time.Time `json:"contract_signed_at"`
|
||||
}
|
||||
|
||||
// CertificationDetailResponse 认证详情响应
|
||||
type CertificationDetailResponse struct {
|
||||
*CertificationStatusResponse
|
||||
|
||||
// 详细记录
|
||||
LicenseUploadRecord *LicenseUploadRecordResponse `json:"license_upload_record,omitempty"`
|
||||
FaceVerifyRecords []FaceVerifyRecordResponse `json:"face_verify_records,omitempty"`
|
||||
ContractRecords []ContractRecordResponse `json:"contract_records,omitempty"`
|
||||
NotificationRecords []NotificationRecordResponse `json:"notification_records,omitempty"`
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
package dto
|
||||
|
||||
import "time"
|
||||
|
||||
// EnterpriseInfoResponse 企业信息响应
|
||||
type EnterpriseInfoResponse struct {
|
||||
ID string `json:"id"`
|
||||
CertificationID string `json:"certification_id"`
|
||||
CompanyName string `json:"company_name"`
|
||||
UnifiedSocialCode string `json:"unified_social_code"`
|
||||
LegalPersonName string `json:"legal_person_name"`
|
||||
LegalPersonID string `json:"legal_person_id"`
|
||||
LicenseUploadRecordID string `json:"license_upload_record_id"`
|
||||
OCRRawData string `json:"ocr_raw_data,omitempty"`
|
||||
OCRConfidence float64 `json:"ocr_confidence,omitempty"`
|
||||
IsOCRVerified bool `json:"is_ocr_verified"`
|
||||
IsFaceVerified bool `json:"is_face_verified"`
|
||||
VerificationData string `json:"verification_data,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// LicenseUploadRecordResponse 营业执照上传记录响应
|
||||
type LicenseUploadRecordResponse struct {
|
||||
ID string `json:"id"`
|
||||
CertificationID *string `json:"certification_id,omitempty"`
|
||||
UserID string `json:"user_id"`
|
||||
OriginalFileName string `json:"original_file_name"`
|
||||
FileSize int64 `json:"file_size"`
|
||||
FileType string `json:"file_type"`
|
||||
FileURL string `json:"file_url"`
|
||||
QiNiuKey string `json:"qiniu_key"`
|
||||
OCRProcessed bool `json:"ocr_processed"`
|
||||
OCRSuccess bool `json:"ocr_success"`
|
||||
OCRConfidence float64 `json:"ocr_confidence,omitempty"`
|
||||
OCRRawData string `json:"ocr_raw_data,omitempty"`
|
||||
OCRErrorMessage string `json:"ocr_error_message,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// FaceVerifyRecordResponse 人脸识别记录响应
|
||||
type FaceVerifyRecordResponse struct {
|
||||
ID string `json:"id"`
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
CertifyID string `json:"certify_id"`
|
||||
VerifyURL string `json:"verify_url,omitempty"`
|
||||
ReturnURL string `json:"return_url,omitempty"`
|
||||
RealName string `json:"real_name"`
|
||||
IDCardNumber string `json:"id_card_number"`
|
||||
Status string `json:"status"`
|
||||
StatusName string `json:"status_name"`
|
||||
ResultCode string `json:"result_code,omitempty"`
|
||||
ResultMessage string `json:"result_message,omitempty"`
|
||||
VerifyScore float64 `json:"verify_score,omitempty"`
|
||||
InitiatedAt time.Time `json:"initiated_at"`
|
||||
CompletedAt *time.Time `json:"completed_at,omitempty"`
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// ContractRecordResponse 合同记录响应
|
||||
type ContractRecordResponse struct {
|
||||
ID string `json:"id"`
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
AdminID *string `json:"admin_id,omitempty"`
|
||||
ContractType string `json:"contract_type"`
|
||||
ContractURL string `json:"contract_url,omitempty"`
|
||||
SigningURL string `json:"signing_url,omitempty"`
|
||||
SignatureData string `json:"signature_data,omitempty"`
|
||||
SignedAt *time.Time `json:"signed_at,omitempty"`
|
||||
ClientIP string `json:"client_ip,omitempty"`
|
||||
UserAgent string `json:"user_agent,omitempty"`
|
||||
Status string `json:"status"`
|
||||
StatusName string `json:"status_name"`
|
||||
ApprovalNotes string `json:"approval_notes,omitempty"`
|
||||
RejectReason string `json:"reject_reason,omitempty"`
|
||||
ExpiresAt *time.Time `json:"expires_at,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// NotificationRecordResponse 通知记录响应
|
||||
type NotificationRecordResponse struct {
|
||||
ID string `json:"id"`
|
||||
CertificationID *string `json:"certification_id,omitempty"`
|
||||
UserID *string `json:"user_id,omitempty"`
|
||||
NotificationType string `json:"notification_type"`
|
||||
NotificationTypeName string `json:"notification_type_name"`
|
||||
NotificationScene string `json:"notification_scene"`
|
||||
NotificationSceneName string `json:"notification_scene_name"`
|
||||
Recipient string `json:"recipient"`
|
||||
Title string `json:"title,omitempty"`
|
||||
Content string `json:"content"`
|
||||
TemplateID string `json:"template_id,omitempty"`
|
||||
TemplateParams string `json:"template_params,omitempty"`
|
||||
Status string `json:"status"`
|
||||
StatusName string `json:"status_name"`
|
||||
ErrorMessage string `json:"error_message,omitempty"`
|
||||
SentAt *time.Time `json:"sent_at,omitempty"`
|
||||
RetryCount int `json:"retry_count"`
|
||||
MaxRetryCount int `json:"max_retry_count"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
package dto
|
||||
|
||||
// BusinessLicenseResult 营业执照识别结果
|
||||
type BusinessLicenseResult struct {
|
||||
CompanyName string `json:"company_name"` // 公司名称
|
||||
LegalRepresentative string `json:"legal_representative"` // 法定代表人
|
||||
RegisteredCapital string `json:"registered_capital"` // 注册资本
|
||||
RegisteredAddress string `json:"registered_address"` // 注册地址
|
||||
RegistrationNumber string `json:"registration_number"` // 统一社会信用代码
|
||||
BusinessScope string `json:"business_scope"` // 经营范围
|
||||
RegistrationDate string `json:"registration_date"` // 成立日期
|
||||
ValidDate string `json:"valid_date"` // 营业期限
|
||||
Confidence float64 `json:"confidence"` // 识别置信度
|
||||
Words []string `json:"words"` // 识别的所有文字
|
||||
}
|
||||
|
||||
// IDCardResult 身份证识别结果
|
||||
type IDCardResult struct {
|
||||
Side string `json:"side"` // 身份证面(front/back)
|
||||
Name string `json:"name"` // 姓名(正面)
|
||||
Sex string `json:"sex"` // 性别(正面)
|
||||
Nation string `json:"nation"` // 民族(正面)
|
||||
BirthDate string `json:"birth_date"` // 出生日期(正面)
|
||||
Address string `json:"address"` // 住址(正面)
|
||||
IDNumber string `json:"id_number"` // 身份证号码(正面)
|
||||
IssuingAuthority string `json:"issuing_authority"` // 签发机关(背面)
|
||||
ValidDate string `json:"valid_date"` // 有效期限(背面)
|
||||
Confidence float64 `json:"confidence"` // 识别置信度
|
||||
Words []string `json:"words"` // 识别的所有文字
|
||||
}
|
||||
|
||||
// GeneralTextResult 通用文字识别结果
|
||||
type GeneralTextResult struct {
|
||||
Words []string `json:"words"` // 识别的文字列表
|
||||
Confidence float64 `json:"confidence"` // 识别置信度
|
||||
}
|
||||
|
||||
// OCREnterpriseInfo OCR识别的企业信息
|
||||
type OCREnterpriseInfo struct {
|
||||
CompanyName string `json:"company_name"` // 企业名称
|
||||
UnifiedSocialCode string `json:"unified_social_code"` // 统一社会信用代码
|
||||
LegalPersonName string `json:"legal_person_name"` // 法人姓名
|
||||
LegalPersonID string `json:"legal_person_id"` // 法人身份证号
|
||||
Confidence float64 `json:"confidence"` // 识别置信度
|
||||
}
|
||||
|
||||
// LicenseProcessResult 营业执照处理结果
|
||||
type LicenseProcessResult struct {
|
||||
LicenseURL string `json:"license_url"` // 营业执照文件URL
|
||||
EnterpriseInfo *OCREnterpriseInfo `json:"enterprise_info"` // OCR识别的企业信息
|
||||
OCRSuccess bool `json:"ocr_success"` // OCR是否成功
|
||||
OCRError string `json:"ocr_error,omitempty"` // OCR错误信息
|
||||
}
|
||||
|
||||
// UploadLicenseRequest 上传营业执照请求
|
||||
type UploadLicenseRequest struct {
|
||||
// 文件通过multipart/form-data上传,这里定义验证规则
|
||||
}
|
||||
|
||||
// UploadLicenseResponse 上传营业执照响应
|
||||
type UploadLicenseResponse struct {
|
||||
UploadRecordID string `json:"upload_record_id"` // 上传记录ID
|
||||
FileURL string `json:"file_url"` // 文件URL
|
||||
OCRProcessed bool `json:"ocr_processed"` // OCR是否已处理
|
||||
OCRSuccess bool `json:"ocr_success"` // OCR是否成功
|
||||
EnterpriseInfo *OCREnterpriseInfo `json:"enterprise_info"` // OCR识别的企业信息(如果成功)
|
||||
OCRErrorMessage string `json:"ocr_error_message,omitempty"` // OCR错误信息(如果失败)
|
||||
}
|
||||
|
||||
// UploadResult 上传结果
|
||||
type UploadResult struct {
|
||||
Key string `json:"key"` // 文件key
|
||||
URL string `json:"url"` // 文件访问URL
|
||||
MimeType string `json:"mime_type"` // MIME类型
|
||||
Size int64 `json:"size"` // 文件大小
|
||||
Hash string `json:"hash"` // 文件哈希值
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
|
||||
// Certification 认证申请实体
|
||||
// 这是企业认证流程的核心实体,负责管理整个认证申请的生命周期
|
||||
// 包含认证状态、时间节点、审核信息、合同信息等核心数据
|
||||
type Certification struct {
|
||||
// 基础信息
|
||||
ID string `gorm:"primaryKey;type:varchar(36)" json:"id" comment:"认证申请唯一标识"`
|
||||
@@ -20,36 +19,25 @@ type Certification struct {
|
||||
Status enums.CertificationStatus `gorm:"type:varchar(50);not null;index" json:"status" comment:"当前认证状态"`
|
||||
|
||||
// 流程节点时间戳 - 记录每个关键步骤的完成时间
|
||||
InfoSubmittedAt *time.Time `json:"info_submitted_at,omitempty" comment:"企业信息提交时间"`
|
||||
FaceVerifiedAt *time.Time `json:"face_verified_at,omitempty" comment:"人脸识别完成时间"`
|
||||
ContractAppliedAt *time.Time `json:"contract_applied_at,omitempty" comment:"合同申请时间"`
|
||||
ContractApprovedAt *time.Time `json:"contract_approved_at,omitempty" comment:"合同审核通过时间"`
|
||||
ContractSignedAt *time.Time `json:"contract_signed_at,omitempty" comment:"合同签署完成时间"`
|
||||
CompletedAt *time.Time `json:"completed_at,omitempty" comment:"认证完成时间"`
|
||||
|
||||
// 审核信息 - 管理员审核相关数据
|
||||
AdminID *string `gorm:"type:varchar(36)" json:"admin_id,omitempty" comment:"审核管理员ID"`
|
||||
ApprovalNotes string `gorm:"type:text" json:"approval_notes,omitempty" comment:"审核备注信息"`
|
||||
RejectReason string `gorm:"type:text" json:"reject_reason,omitempty" comment:"拒绝原因说明"`
|
||||
InfoSubmittedAt *time.Time `json:"info_submitted_at,omitempty" comment:"企业信息提交时间"`
|
||||
EnterpriseVerifiedAt *time.Time `json:"enterprise_verified_at,omitempty" comment:"企业认证完成时间"`
|
||||
ContractAppliedAt *time.Time `json:"contract_applied_at,omitempty" comment:"合同申请时间"`
|
||||
ContractSignedAt *time.Time `json:"contract_signed_at,omitempty" comment:"合同签署完成时间"`
|
||||
CompletedAt *time.Time `json:"completed_at,omitempty" comment:"认证完成时间"`
|
||||
|
||||
// 合同信息 - 电子合同相关链接
|
||||
ContractURL string `gorm:"type:varchar(500)" json:"contract_url,omitempty" comment:"合同文件访问链接"`
|
||||
SigningURL string `gorm:"type:varchar(500)" json:"signing_url,omitempty" comment:"电子签署链接"`
|
||||
ContractFileID string `gorm:"type:varchar(500)" json:"contract_file_id,omitempty" comment:"合同文件ID"`
|
||||
EsignFlowID string `gorm:"type:varchar(500)" json:"esign_flow_id,omitempty" comment:"签署流程ID"`
|
||||
ContractURL string `gorm:"type:varchar(500)" json:"contract_url,omitempty" comment:"合同文件访问链接"`
|
||||
ContractSignURL string `gorm:"type:varchar(500)" json:"contract_sign_url,omitempty" comment:"合同签署链接"`
|
||||
|
||||
// OCR识别信息 - 营业执照OCR识别结果
|
||||
OCRRequestID string `gorm:"type:varchar(100)" json:"ocr_request_id,omitempty" comment:"OCR识别请求ID"`
|
||||
OCRConfidence float64 `gorm:"type:decimal(5,2)" json:"ocr_confidence,omitempty" comment:"OCR识别置信度(0-1)"`
|
||||
// 认证信息
|
||||
AuthFlowID string `gorm:"type:varchar(500)" json:"auth_flow_id,omitempty" comment:"认证流程ID"`
|
||||
|
||||
// 时间戳字段
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at" comment:"创建时间"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at" comment:"更新时间"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-" comment:"软删除时间"`
|
||||
|
||||
// 关联关系 - 与其他实体的关联
|
||||
LicenseUploadRecord *LicenseUploadRecord `gorm:"foreignKey:CertificationID" json:"license_upload_record,omitempty" comment:"关联的营业执照上传记录"`
|
||||
FaceVerifyRecords []FaceVerifyRecord `gorm:"foreignKey:CertificationID" json:"face_verify_records,omitempty" comment:"关联的人脸识别记录列表"`
|
||||
ContractRecords []ContractRecord `gorm:"foreignKey:CertificationID" json:"contract_records,omitempty" comment:"关联的合同记录列表"`
|
||||
NotificationRecords []NotificationRecord `gorm:"foreignKey:CertificationID" json:"notification_records,omitempty" comment:"关联的通知记录列表"`
|
||||
}
|
||||
|
||||
// TableName 指定数据库表名
|
||||
@@ -66,122 +54,94 @@ func (c *Certification) BeforeCreate(tx *gorm.DB) error {
|
||||
}
|
||||
|
||||
// IsStatusChangeable 检查状态是否可以变更
|
||||
// 只有非最终状态(完成/拒绝)的认证申请才能进行状态变更
|
||||
func (c *Certification) IsStatusChangeable() bool {
|
||||
return !enums.IsFinalStatus(c.Status)
|
||||
}
|
||||
|
||||
// CanRetryFaceVerify 检查是否可以重试人脸识别
|
||||
// 只有人脸识别失败状态的申请才能重试
|
||||
func (c *Certification) CanRetryFaceVerify() bool {
|
||||
return c.Status == enums.StatusFaceFailed
|
||||
// GetStatusName 获取状态名称
|
||||
func (c *Certification) GetStatusName() string {
|
||||
return enums.GetStatusName(c.Status)
|
||||
}
|
||||
|
||||
// CanRetrySign 检查是否可以重试签署
|
||||
// 只有签署失败状态的申请才能重试
|
||||
func (c *Certification) CanRetrySign() bool {
|
||||
return c.Status == enums.StatusSignFailed
|
||||
// IsFinalStatus 判断是否为最终状态
|
||||
func (c *Certification) IsFinalStatus() bool {
|
||||
return enums.IsFinalStatus(c.Status)
|
||||
}
|
||||
|
||||
// CanRestart 检查是否可以重新开始流程
|
||||
// 只有被拒绝的申请才能重新开始认证流程
|
||||
func (c *Certification) CanRestart() bool {
|
||||
return c.Status == enums.StatusRejected
|
||||
// GetStatusCategory 获取状态分类
|
||||
func (c *Certification) GetStatusCategory() string {
|
||||
return enums.GetStatusCategory(c.Status)
|
||||
}
|
||||
|
||||
// GetNextValidStatuses 获取当前状态可以转换到的下一个状态列表
|
||||
// 根据状态机规则,返回所有合法的下一个状态
|
||||
func (c *Certification) GetNextValidStatuses() []enums.CertificationStatus {
|
||||
switch c.Status {
|
||||
case enums.StatusPending:
|
||||
return []enums.CertificationStatus{enums.StatusInfoSubmitted}
|
||||
case enums.StatusInfoSubmitted:
|
||||
return []enums.CertificationStatus{enums.StatusFaceVerified, enums.StatusFaceFailed}
|
||||
case enums.StatusFaceVerified:
|
||||
return []enums.CertificationStatus{enums.StatusContractApplied}
|
||||
case enums.StatusContractApplied:
|
||||
return []enums.CertificationStatus{enums.StatusContractPending}
|
||||
case enums.StatusContractPending:
|
||||
return []enums.CertificationStatus{enums.StatusContractApproved, enums.StatusRejected}
|
||||
case enums.StatusContractApproved:
|
||||
return []enums.CertificationStatus{enums.StatusContractSigned, enums.StatusSignFailed}
|
||||
case enums.StatusContractSigned:
|
||||
return []enums.CertificationStatus{enums.StatusCompleted}
|
||||
case enums.StatusFaceFailed:
|
||||
return []enums.CertificationStatus{enums.StatusFaceVerified}
|
||||
case enums.StatusSignFailed:
|
||||
return []enums.CertificationStatus{enums.StatusContractSigned}
|
||||
case enums.StatusRejected:
|
||||
return []enums.CertificationStatus{enums.StatusInfoSubmitted}
|
||||
default:
|
||||
return []enums.CertificationStatus{}
|
||||
}
|
||||
// GetStatusPriority 获取状态优先级
|
||||
func (c *Certification) GetStatusPriority() int {
|
||||
return enums.GetStatusPriority(c.Status)
|
||||
}
|
||||
|
||||
// CanTransitionTo 检查是否可以转换到指定状态
|
||||
// 验证状态转换的合法性,确保状态机规则得到遵守
|
||||
func (c *Certification) CanTransitionTo(targetStatus enums.CertificationStatus) bool {
|
||||
validStatuses := c.GetNextValidStatuses()
|
||||
for _, status := range validStatuses {
|
||||
if status == targetStatus {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetProgressPercentage 获取认证进度百分比
|
||||
// 根据当前状态计算认证流程的完成进度,用于前端进度条显示
|
||||
// GetProgressPercentage 获取进度百分比
|
||||
func (c *Certification) GetProgressPercentage() int {
|
||||
switch c.Status {
|
||||
case enums.StatusPending:
|
||||
return 0
|
||||
case enums.StatusInfoSubmitted:
|
||||
return 12
|
||||
case enums.StatusFaceVerified:
|
||||
return 25
|
||||
case enums.StatusContractApplied:
|
||||
return 37
|
||||
case enums.StatusContractPending:
|
||||
return 50
|
||||
case enums.StatusContractApproved:
|
||||
return 75
|
||||
case enums.StatusContractSigned:
|
||||
return 87
|
||||
case enums.StatusCompleted:
|
||||
return 100
|
||||
case enums.StatusFaceFailed, enums.StatusSignFailed:
|
||||
return c.GetProgressPercentage() // 失败状态保持原进度
|
||||
case enums.StatusRejected:
|
||||
return 0
|
||||
default:
|
||||
return 0
|
||||
progressMap := map[enums.CertificationStatus]int{
|
||||
enums.StatusPending: 0,
|
||||
enums.StatusInfoSubmitted: 20,
|
||||
enums.StatusEnterpriseVerified: 40,
|
||||
enums.StatusContractApplied: 60,
|
||||
enums.StatusContractSigned: 80,
|
||||
enums.StatusCompleted: 100,
|
||||
}
|
||||
|
||||
if progress, exists := progressMap[c.Status]; exists {
|
||||
return progress
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// IsUserActionRequired 检查是否需要用户操作
|
||||
// 判断当前状态是否需要用户进行下一步操作,用于前端提示
|
||||
func (c *Certification) IsUserActionRequired() bool {
|
||||
userActionStatuses := []enums.CertificationStatus{
|
||||
enums.StatusPending,
|
||||
enums.StatusInfoSubmitted,
|
||||
enums.StatusFaceVerified,
|
||||
enums.StatusContractApproved,
|
||||
enums.StatusFaceFailed,
|
||||
enums.StatusSignFailed,
|
||||
enums.StatusRejected,
|
||||
userActionRequired := map[enums.CertificationStatus]bool{
|
||||
enums.StatusPending: true,
|
||||
enums.StatusInfoSubmitted: true,
|
||||
enums.StatusEnterpriseVerified: true,
|
||||
enums.StatusContractApplied: true,
|
||||
enums.StatusContractSigned: false,
|
||||
enums.StatusCompleted: false,
|
||||
}
|
||||
|
||||
for _, status := range userActionStatuses {
|
||||
if c.Status == status {
|
||||
return true
|
||||
}
|
||||
if required, exists := userActionRequired[c.Status]; exists {
|
||||
return required
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsAdminActionRequired 检查是否需要管理员操作
|
||||
// 判断当前状态是否需要管理员审核,用于后台管理界面
|
||||
func (c *Certification) IsAdminActionRequired() bool {
|
||||
return c.Status == enums.StatusContractPending
|
||||
// GetNextValidStatuses 获取下一个有效状态
|
||||
func (c *Certification) GetNextValidStatuses() []enums.CertificationStatus {
|
||||
nextStatusMap := map[enums.CertificationStatus][]enums.CertificationStatus{
|
||||
enums.StatusPending: {enums.StatusInfoSubmitted},
|
||||
enums.StatusInfoSubmitted: {enums.StatusEnterpriseVerified, enums.StatusInfoSubmitted}, // 可以重新提交
|
||||
enums.StatusEnterpriseVerified: {enums.StatusContractApplied},
|
||||
enums.StatusContractApplied: {enums.StatusContractSigned},
|
||||
enums.StatusContractSigned: {enums.StatusCompleted},
|
||||
enums.StatusCompleted: {},
|
||||
}
|
||||
|
||||
if nextStatuses, exists := nextStatusMap[c.Status]; exists {
|
||||
return nextStatuses
|
||||
}
|
||||
return []enums.CertificationStatus{}
|
||||
}
|
||||
|
||||
// CanTransitionTo 检查是否可以转换到指定状态
|
||||
func (c *Certification) CanTransitionTo(targetStatus enums.CertificationStatus, isUser bool) (bool, string) {
|
||||
nextStatuses := c.GetNextValidStatuses()
|
||||
|
||||
for _, nextStatus := range nextStatuses {
|
||||
if nextStatus == targetStatus {
|
||||
// 检查权限
|
||||
if isUser && !c.IsUserActionRequired() {
|
||||
return false, "当前状态不需要用户操作"
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
}
|
||||
|
||||
return false, "不支持的状态转换"
|
||||
}
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// ContractRecord 合同记录实体
|
||||
// 记录电子合同的详细信息,包括合同生成、审核、签署的完整流程
|
||||
// 支持合同状态跟踪、签署信息记录、审核流程管理等功能
|
||||
type ContractRecord struct {
|
||||
// 基础标识
|
||||
ID string `gorm:"primaryKey;type:varchar(36)" json:"id" comment:"合同记录唯一标识"`
|
||||
CertificationID string `gorm:"type:varchar(36);not null;index" json:"certification_id" comment:"关联的认证申请ID"`
|
||||
UserID string `gorm:"type:varchar(36);not null;index" json:"user_id" comment:"合同申请人ID"`
|
||||
AdminID *string `gorm:"type:varchar(36);index" json:"admin_id,omitempty" comment:"审核管理员ID"`
|
||||
|
||||
// 合同信息 - 电子合同的基本信息
|
||||
ContractType string `gorm:"type:varchar(50);not null" json:"contract_type" comment:"合同类型(ENTERPRISE_CERTIFICATION)"`
|
||||
ContractURL string `gorm:"type:varchar(500)" json:"contract_url,omitempty" comment:"合同文件访问链接"`
|
||||
SigningURL string `gorm:"type:varchar(500)" json:"signing_url,omitempty" comment:"电子签署链接"`
|
||||
|
||||
// 签署信息 - 记录用户签署的详细信息
|
||||
SignatureData string `gorm:"type:text" json:"signature_data,omitempty" comment:"签署数据(JSON格式)"`
|
||||
SignedAt *time.Time `json:"signed_at,omitempty" comment:"签署完成时间"`
|
||||
ClientIP string `gorm:"type:varchar(50)" json:"client_ip,omitempty" comment:"签署客户端IP"`
|
||||
UserAgent string `gorm:"type:varchar(500)" json:"user_agent,omitempty" comment:"签署客户端信息"`
|
||||
|
||||
// 状态信息 - 合同的生命周期状态
|
||||
Status string `gorm:"type:varchar(50);not null;index" json:"status" comment:"合同状态(PENDING/APPROVED/SIGNED/EXPIRED)"`
|
||||
ApprovalNotes string `gorm:"type:text" json:"approval_notes,omitempty" comment:"审核备注信息"`
|
||||
RejectReason string `gorm:"type:text" json:"reject_reason,omitempty" comment:"拒绝原因说明"`
|
||||
ExpiresAt *time.Time `json:"expires_at,omitempty" comment:"合同过期时间"`
|
||||
|
||||
// 时间戳字段
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at" comment:"创建时间"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at" comment:"更新时间"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-" comment:"软删除时间"`
|
||||
|
||||
// 关联关系
|
||||
Certification *Certification `gorm:"foreignKey:CertificationID" json:"certification,omitempty" comment:"关联的认证申请"`
|
||||
}
|
||||
|
||||
// TableName 指定数据库表名
|
||||
func (ContractRecord) TableName() string {
|
||||
return "contract_records"
|
||||
}
|
||||
|
||||
// IsPending 检查合同是否待审核
|
||||
// 判断合同是否处于等待管理员审核的状态
|
||||
func (c *ContractRecord) IsPending() bool {
|
||||
return c.Status == "PENDING"
|
||||
}
|
||||
|
||||
// IsApproved 检查合同是否已审核通过
|
||||
// 判断合同是否已通过管理员审核,可以进入签署阶段
|
||||
func (c *ContractRecord) IsApproved() bool {
|
||||
return c.Status == "APPROVED"
|
||||
}
|
||||
|
||||
// IsSigned 检查合同是否已签署
|
||||
// 判断合同是否已完成电子签署,认证流程即将完成
|
||||
func (c *ContractRecord) IsSigned() bool {
|
||||
return c.Status == "SIGNED"
|
||||
}
|
||||
|
||||
// IsExpired 检查合同是否已过期
|
||||
// 判断合同是否已超过有效期,过期后需要重新申请
|
||||
func (c *ContractRecord) IsExpired() bool {
|
||||
if c.ExpiresAt == nil {
|
||||
return false
|
||||
}
|
||||
return time.Now().After(*c.ExpiresAt)
|
||||
}
|
||||
|
||||
// HasSigningURL 检查是否有签署链接
|
||||
// 判断是否已生成电子签署链接,用于前端判断是否显示签署按钮
|
||||
func (c *ContractRecord) HasSigningURL() bool {
|
||||
return c.SigningURL != ""
|
||||
}
|
||||
|
||||
// GetStatusName 获取状态的中文名称
|
||||
// 将英文状态码转换为中文显示名称,用于前端展示和用户理解
|
||||
func (c *ContractRecord) GetStatusName() string {
|
||||
statusNames := map[string]string{
|
||||
"PENDING": "待审核",
|
||||
"APPROVED": "已审核",
|
||||
"SIGNED": "已签署",
|
||||
"EXPIRED": "已过期",
|
||||
"REJECTED": "已拒绝",
|
||||
}
|
||||
|
||||
if name, exists := statusNames[c.Status]; exists {
|
||||
return name
|
||||
}
|
||||
return c.Status
|
||||
}
|
||||
|
||||
// BeforeCreate GORM钩子:创建前自动生成UUID
|
||||
func (c *ContractRecord) BeforeCreate(tx *gorm.DB) error {
|
||||
if c.ID == "" {
|
||||
c.ID = uuid.New().String()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// EnterpriseInfoSubmitRecord 企业信息提交记录
|
||||
type EnterpriseInfoSubmitRecord struct {
|
||||
ID string `json:"id" gorm:"primaryKey;type:varchar(36)"`
|
||||
UserID string `json:"user_id" gorm:"type:varchar(36);not null;index"`
|
||||
|
||||
// 企业信息
|
||||
CompanyName string `json:"company_name" gorm:"type:varchar(200);not null"`
|
||||
UnifiedSocialCode string `json:"unified_social_code" gorm:"type:varchar(50);not null;index"`
|
||||
LegalPersonName string `json:"legal_person_name" gorm:"type:varchar(50);not null"`
|
||||
LegalPersonID string `json:"legal_person_id" gorm:"type:varchar(50);not null"`
|
||||
LegalPersonPhone string `json:"legal_person_phone" gorm:"type:varchar(50);not null"`
|
||||
// 提交状态
|
||||
Status string `json:"status" gorm:"type:varchar(20);not null;default:'submitted'"` // submitted, verified, failed
|
||||
SubmitAt time.Time `json:"submit_at" gorm:"not null"`
|
||||
VerifiedAt *time.Time `json:"verified_at"`
|
||||
FailedAt *time.Time `json:"failed_at"`
|
||||
FailureReason string `json:"failure_reason" gorm:"type:text"`
|
||||
|
||||
// 系统字段
|
||||
CreatedAt time.Time `json:"created_at" gorm:"not null"`
|
||||
UpdatedAt time.Time `json:"updated_at" gorm:"not null"`
|
||||
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"index"`
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (EnterpriseInfoSubmitRecord) TableName() string {
|
||||
return "enterprise_info_submit_records"
|
||||
}
|
||||
|
||||
// NewEnterpriseInfoSubmitRecord 创建新的企业信息提交记录
|
||||
func NewEnterpriseInfoSubmitRecord(
|
||||
userID, companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone string,
|
||||
) *EnterpriseInfoSubmitRecord {
|
||||
return &EnterpriseInfoSubmitRecord{
|
||||
ID: uuid.New().String(),
|
||||
UserID: userID,
|
||||
CompanyName: companyName,
|
||||
UnifiedSocialCode: unifiedSocialCode,
|
||||
LegalPersonName: legalPersonName,
|
||||
LegalPersonID: legalPersonID,
|
||||
LegalPersonPhone: legalPersonPhone,
|
||||
Status: "submitted",
|
||||
SubmitAt: time.Now(),
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
// MarkAsVerified 标记为已验证
|
||||
func (r *EnterpriseInfoSubmitRecord) MarkAsVerified() {
|
||||
now := time.Now()
|
||||
r.Status = "verified"
|
||||
r.VerifiedAt = &now
|
||||
r.UpdatedAt = now
|
||||
}
|
||||
|
||||
// MarkAsFailed 标记为验证失败
|
||||
func (r *EnterpriseInfoSubmitRecord) MarkAsFailed(reason string) {
|
||||
now := time.Now()
|
||||
r.Status = "failed"
|
||||
r.FailedAt = &now
|
||||
r.FailureReason = reason
|
||||
r.UpdatedAt = now
|
||||
}
|
||||
|
||||
// IsVerified 检查是否已验证
|
||||
func (r *EnterpriseInfoSubmitRecord) IsVerified() bool {
|
||||
return r.Status == "verified"
|
||||
}
|
||||
|
||||
// IsFailed 检查是否验证失败
|
||||
func (r *EnterpriseInfoSubmitRecord) IsFailed() bool {
|
||||
return r.Status == "failed"
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// EsignContractGenerateRecord e签宝生成合同文件记录
|
||||
type EsignContractGenerateRecord struct {
|
||||
ID string `json:"id" gorm:"primaryKey;type:varchar(36)"`
|
||||
CertificationID string `json:"certification_id" gorm:"type:varchar(36);not null;index"`
|
||||
UserID string `json:"user_id" gorm:"type:varchar(36);not null;index"`
|
||||
|
||||
// e签宝相关
|
||||
TemplateID string `json:"template_id" gorm:"type:varchar(100);index"` // 模板ID
|
||||
ContractFileID string `json:"contract_file_id" gorm:"type:varchar(100);index"` // 合同文件ID
|
||||
ContractURL string `json:"contract_url" gorm:"type:varchar(500)"` // 合同文件URL
|
||||
ContractName string `json:"contract_name" gorm:"type:varchar(200)"` // 合同名称
|
||||
|
||||
// 生成状态
|
||||
Status string `json:"status" gorm:"type:varchar(20);not null"` // success, failed
|
||||
|
||||
FillTime *time.Time `json:"fill_time"` // 填写时间
|
||||
|
||||
// 系统字段
|
||||
CreatedAt time.Time `json:"created_at" gorm:"not null"`
|
||||
UpdatedAt time.Time `json:"updated_at" gorm:"not null"`
|
||||
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"index"`
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (EsignContractGenerateRecord) TableName() string {
|
||||
return "esign_contract_generate_records"
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// EsignContractSignRecord e签宝签署合同记录
|
||||
type EsignContractSignRecord struct {
|
||||
ID string `json:"id" gorm:"primaryKey;type:varchar(36)"`
|
||||
CertificationID string `json:"certification_id" gorm:"type:varchar(36);not null;index"`
|
||||
UserID string `json:"user_id" gorm:"type:varchar(36);not null;index"`
|
||||
EnterpriseInfoID string `json:"enterprise_info_id" gorm:"type:varchar(36);not null;index"` // 企业信息ID
|
||||
|
||||
// e签宝相关
|
||||
EsignFlowID string `json:"esign_flow_id" gorm:"type:varchar(100);index"` // e签宝流程ID
|
||||
ContractFileID string `json:"contract_file_id" gorm:"type:varchar(100);index"` // 合同文件ID
|
||||
SignURL string `json:"sign_url" gorm:"type:varchar(500)"` // 签署链接
|
||||
SignShortURL string `json:"sign_short_url" gorm:"type:varchar(500)"` // 签署短链接
|
||||
SignedFileURL string `json:"signed_file_url" gorm:"type:varchar(500)"` // 已签署文件URL
|
||||
|
||||
// 签署状态
|
||||
Status string `json:"status" gorm:"type:varchar(20);not null;default:'pending'"` // pending, signing, success, failed, expired
|
||||
RequestAt time.Time `json:"request_at" gorm:"not null"` // 申请签署时间
|
||||
SignedAt *time.Time `json:"signed_at"` // 签署完成时间
|
||||
ExpiredAt *time.Time `json:"expired_at"` // 签署链接过期时间
|
||||
FailedAt *time.Time `json:"failed_at"` // 失败时间
|
||||
FailureReason string `json:"failure_reason" gorm:"type:text"` // 失败原因
|
||||
|
||||
// 签署人信息
|
||||
SignerName string `json:"signer_name" gorm:"type:varchar(50)"` // 签署人姓名
|
||||
SignerPhone string `json:"signer_phone" gorm:"type:varchar(20)"` // 签署人手机号
|
||||
SignerIDCard string `json:"signer_id_card" gorm:"type:varchar(20)"` // 签署人身份证号
|
||||
|
||||
// 系统字段
|
||||
CreatedAt time.Time `json:"created_at" gorm:"not null"`
|
||||
UpdatedAt time.Time `json:"updated_at" gorm:"not null"`
|
||||
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"index"`
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (EsignContractSignRecord) TableName() string {
|
||||
return "esign_contract_sign_records"
|
||||
}
|
||||
|
||||
// NewEsignContractSignRecord 创建新的e签宝签署合同记录
|
||||
func NewEsignContractSignRecord(
|
||||
certificationID, userID, esignFlowID, contractFileID, signerName, signerPhone, signerIDCard, signURL, signShortURL string,
|
||||
) *EsignContractSignRecord {
|
||||
// 设置签署链接过期时间为7天后
|
||||
expiredAt := time.Now().AddDate(0, 0, 7)
|
||||
|
||||
return &EsignContractSignRecord{
|
||||
ID: uuid.New().String(),
|
||||
CertificationID: certificationID,
|
||||
UserID: userID,
|
||||
EsignFlowID: esignFlowID,
|
||||
ContractFileID: contractFileID,
|
||||
SignURL: signURL,
|
||||
SignShortURL: signShortURL,
|
||||
SignerName: signerName,
|
||||
SignerPhone: signerPhone,
|
||||
SignerIDCard: signerIDCard,
|
||||
Status: "pending",
|
||||
RequestAt: time.Now(),
|
||||
ExpiredAt: &expiredAt,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
// MarkAsSigning 标记为签署中
|
||||
func (r *EsignContractSignRecord) MarkAsSigning() {
|
||||
r.Status = "signing"
|
||||
r.UpdatedAt = time.Now()
|
||||
}
|
||||
|
||||
// MarkAsSuccess 标记为签署成功
|
||||
func (r *EsignContractSignRecord) MarkAsSuccess(signedFileURL string) {
|
||||
now := time.Now()
|
||||
r.Status = "success"
|
||||
r.SignedFileURL = signedFileURL
|
||||
r.SignedAt = &now
|
||||
r.UpdatedAt = now
|
||||
}
|
||||
|
||||
// MarkAsFailed 标记为签署失败
|
||||
func (r *EsignContractSignRecord) MarkAsFailed(reason string) {
|
||||
now := time.Now()
|
||||
r.Status = "failed"
|
||||
r.FailedAt = &now
|
||||
r.FailureReason = reason
|
||||
r.UpdatedAt = now
|
||||
}
|
||||
|
||||
// MarkAsExpired 标记为已过期
|
||||
func (r *EsignContractSignRecord) MarkAsExpired() {
|
||||
now := time.Now()
|
||||
r.Status = "expired"
|
||||
r.ExpiredAt = &now
|
||||
r.UpdatedAt = now
|
||||
}
|
||||
|
||||
// SetSignURL 设置签署链接
|
||||
func (r *EsignContractSignRecord) SetSignURL(signURL string) {
|
||||
r.SignURL = signURL
|
||||
r.UpdatedAt = time.Now()
|
||||
}
|
||||
|
||||
// IsSuccess 检查是否签署成功
|
||||
func (r *EsignContractSignRecord) IsSuccess() bool {
|
||||
return r.Status == "success"
|
||||
}
|
||||
|
||||
// IsFailed 检查是否签署失败
|
||||
func (r *EsignContractSignRecord) IsFailed() bool {
|
||||
return r.Status == "failed"
|
||||
}
|
||||
|
||||
// IsExpired 检查是否已过期
|
||||
func (r *EsignContractSignRecord) IsExpired() bool {
|
||||
return r.Status == "expired" || (r.ExpiredAt != nil && time.Now().After(*r.ExpiredAt))
|
||||
}
|
||||
|
||||
// IsPending 检查是否待处理
|
||||
func (r *EsignContractSignRecord) IsPending() bool {
|
||||
return r.Status == "pending"
|
||||
}
|
||||
|
||||
// IsSigning 检查是否签署中
|
||||
func (r *EsignContractSignRecord) IsSigning() bool {
|
||||
return r.Status == "signing"
|
||||
}
|
||||
|
||||
// GetRemainingTime 获取剩余签署时间
|
||||
func (r *EsignContractSignRecord) GetRemainingTime() time.Duration {
|
||||
if r.ExpiredAt == nil {
|
||||
return 0
|
||||
}
|
||||
remaining := time.Until(*r.ExpiredAt)
|
||||
if remaining < 0 {
|
||||
return 0
|
||||
}
|
||||
return remaining
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// FaceVerifyRecord 人脸识别记录实体
|
||||
// 记录用户进行人脸识别验证的详细信息,包括验证状态、结果和身份信息
|
||||
// 支持多次验证尝试,每次验证都会生成独立的记录,便于追踪和重试
|
||||
type FaceVerifyRecord struct {
|
||||
// 基础标识
|
||||
ID string `gorm:"primaryKey;type:varchar(36)" json:"id" comment:"人脸识别记录唯一标识"`
|
||||
CertificationID string `gorm:"type:varchar(36);not null;index" json:"certification_id" comment:"关联的认证申请ID"`
|
||||
UserID string `gorm:"type:varchar(36);not null;index" json:"user_id" comment:"进行验证的用户ID"`
|
||||
|
||||
// 阿里云人脸识别信息 - 第三方服务的相关数据
|
||||
CertifyID string `gorm:"type:varchar(100);not null;index" json:"certify_id" comment:"阿里云人脸识别任务ID"`
|
||||
VerifyURL string `gorm:"type:varchar(500)" json:"verify_url,omitempty" comment:"人脸识别验证页面URL"`
|
||||
ReturnURL string `gorm:"type:varchar(500)" json:"return_url,omitempty" comment:"验证完成后的回调URL"`
|
||||
|
||||
// 身份信息 - 用于人脸识别的身份验证数据
|
||||
RealName string `gorm:"type:varchar(100);not null" json:"real_name" comment:"真实姓名"`
|
||||
IDCardNumber string `gorm:"type:varchar(50);not null" json:"id_card_number" comment:"身份证号码"`
|
||||
|
||||
// 验证结果 - 记录验证的详细结果信息
|
||||
Status string `gorm:"type:varchar(50);not null;index" json:"status" comment:"验证状态(PROCESSING/SUCCESS/FAIL)"`
|
||||
ResultCode string `gorm:"type:varchar(50)" json:"result_code,omitempty" comment:"结果代码"`
|
||||
ResultMessage string `gorm:"type:varchar(500)" json:"result_message,omitempty" comment:"结果描述信息"`
|
||||
VerifyScore float64 `gorm:"type:decimal(5,2)" json:"verify_score,omitempty" comment:"验证分数(0-1)"`
|
||||
|
||||
// 时间信息 - 验证流程的时间节点
|
||||
InitiatedAt time.Time `gorm:"autoCreateTime" json:"initiated_at" comment:"验证发起时间"`
|
||||
CompletedAt *time.Time `json:"completed_at,omitempty" comment:"验证完成时间"`
|
||||
ExpiresAt time.Time `gorm:"not null" json:"expires_at" comment:"验证链接过期时间"`
|
||||
|
||||
// 时间戳字段
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at" comment:"创建时间"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at" comment:"更新时间"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-" comment:"软删除时间"`
|
||||
|
||||
// 关联关系
|
||||
Certification *Certification `gorm:"foreignKey:CertificationID" json:"certification,omitempty" comment:"关联的认证申请"`
|
||||
}
|
||||
|
||||
// TableName 指定数据库表名
|
||||
func (FaceVerifyRecord) TableName() string {
|
||||
return "face_verify_records"
|
||||
}
|
||||
|
||||
// IsSuccess 检查人脸识别是否成功
|
||||
// 判断验证状态是否为成功状态
|
||||
func (f *FaceVerifyRecord) IsSuccess() bool {
|
||||
return f.Status == "SUCCESS"
|
||||
}
|
||||
|
||||
// IsProcessing 检查是否正在处理中
|
||||
// 判断验证是否正在进行中,等待用户完成验证
|
||||
func (f *FaceVerifyRecord) IsProcessing() bool {
|
||||
return f.Status == "PROCESSING"
|
||||
}
|
||||
|
||||
// IsFailed 检查是否失败
|
||||
// 判断验证是否失败,包括超时、验证不通过等情况
|
||||
func (f *FaceVerifyRecord) IsFailed() bool {
|
||||
return f.Status == "FAIL"
|
||||
}
|
||||
|
||||
// IsExpired 检查是否已过期
|
||||
// 判断验证链接是否已超过有效期,过期后需要重新发起验证
|
||||
func (f *FaceVerifyRecord) IsExpired() bool {
|
||||
return time.Now().After(f.ExpiresAt)
|
||||
}
|
||||
|
||||
// GetStatusName 获取状态的中文名称
|
||||
// 将英文状态码转换为中文显示名称,用于前端展示
|
||||
func (f *FaceVerifyRecord) GetStatusName() string {
|
||||
statusNames := map[string]string{
|
||||
"PROCESSING": "处理中",
|
||||
"SUCCESS": "成功",
|
||||
"FAIL": "失败",
|
||||
}
|
||||
|
||||
if name, exists := statusNames[f.Status]; exists {
|
||||
return name
|
||||
}
|
||||
return f.Status
|
||||
}
|
||||
|
||||
// BeforeCreate GORM钩子:创建前自动生成UUID
|
||||
func (f *FaceVerifyRecord) BeforeCreate(tx *gorm.DB) error {
|
||||
if f.ID == "" {
|
||||
f.ID = uuid.New().String()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// LicenseUploadRecord 营业执照上传记录实体
|
||||
// 记录用户上传营业执照文件的详细信息,包括文件元数据和OCR处理结果
|
||||
// 支持多种文件格式,自动进行OCR识别,为后续企业信息验证提供数据支持
|
||||
type LicenseUploadRecord struct {
|
||||
// 基础标识
|
||||
ID string `gorm:"primaryKey;type:varchar(36)" json:"id" comment:"上传记录唯一标识"`
|
||||
CertificationID *string `gorm:"type:varchar(36);index" json:"certification_id,omitempty" comment:"关联的认证申请ID(可为空,表示独立上传)"`
|
||||
UserID string `gorm:"type:varchar(36);not null;index" json:"user_id" comment:"上传用户ID"`
|
||||
|
||||
// 文件信息 - 存储文件的元数据信息
|
||||
OriginalFileName string `gorm:"type:varchar(255);not null" json:"original_file_name" comment:"原始文件名"`
|
||||
FileSize int64 `gorm:"not null" json:"file_size" comment:"文件大小(字节)"`
|
||||
FileType string `gorm:"type:varchar(50);not null" json:"file_type" comment:"文件MIME类型"`
|
||||
FileURL string `gorm:"type:varchar(500);not null" json:"file_url" comment:"文件访问URL"`
|
||||
QiNiuKey string `gorm:"type:varchar(255);not null;index" json:"qiniu_key" comment:"七牛云存储的Key"`
|
||||
|
||||
// OCR处理结果 - 记录OCR识别的详细结果
|
||||
OCRProcessed bool `gorm:"default:false" json:"ocr_processed" comment:"是否已进行OCR处理"`
|
||||
OCRSuccess bool `gorm:"default:false" json:"ocr_success" comment:"OCR识别是否成功"`
|
||||
OCRConfidence float64 `gorm:"type:decimal(5,2)" json:"ocr_confidence,omitempty" comment:"OCR识别置信度(0-1)"`
|
||||
OCRRawData string `gorm:"type:text" json:"ocr_raw_data,omitempty" comment:"OCR原始返回数据(JSON格式)"`
|
||||
OCRErrorMessage string `gorm:"type:varchar(500)" json:"ocr_error_message,omitempty" comment:"OCR处理错误信息"`
|
||||
|
||||
// 时间戳字段
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at" comment:"创建时间"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at" comment:"更新时间"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-" comment:"软删除时间"`
|
||||
|
||||
// 关联关系
|
||||
Certification *Certification `gorm:"foreignKey:CertificationID" json:"certification,omitempty" comment:"关联的认证申请"`
|
||||
}
|
||||
|
||||
// TableName 指定数据库表名
|
||||
func (LicenseUploadRecord) TableName() string {
|
||||
return "license_upload_records"
|
||||
}
|
||||
|
||||
// IsOCRSuccess 检查OCR是否成功
|
||||
// 判断OCR处理已完成且识别成功
|
||||
func (l *LicenseUploadRecord) IsOCRSuccess() bool {
|
||||
return l.OCRProcessed && l.OCRSuccess
|
||||
}
|
||||
|
||||
// GetFileExtension 获取文件扩展名
|
||||
// 从原始文件名中提取文件扩展名,用于文件类型判断
|
||||
func (l *LicenseUploadRecord) GetFileExtension() string {
|
||||
// 从OriginalFileName提取扩展名的逻辑
|
||||
// 这里简化处理,实际使用时可以用path.Ext()
|
||||
return l.FileType
|
||||
}
|
||||
|
||||
// IsValidForOCR 检查文件是否适合OCR处理
|
||||
// 验证文件类型是否支持OCR识别,目前支持JPEG、PNG格式
|
||||
func (l *LicenseUploadRecord) IsValidForOCR() bool {
|
||||
validTypes := []string{"image/jpeg", "image/png", "image/jpg"}
|
||||
for _, validType := range validTypes {
|
||||
if l.FileType == validType {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// BeforeCreate GORM钩子:创建前自动生成UUID
|
||||
func (l *LicenseUploadRecord) BeforeCreate(tx *gorm.DB) error {
|
||||
if l.ID == "" {
|
||||
l.ID = uuid.New().String()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// NotificationRecord 通知记录实体
|
||||
// 记录系统发送的所有通知信息,包括短信、企业微信、邮件等多种通知渠道
|
||||
// 支持通知状态跟踪、重试机制、模板化消息等功能,确保通知的可靠送达
|
||||
type NotificationRecord struct {
|
||||
// 基础标识
|
||||
ID string `gorm:"primaryKey;type:varchar(36)" json:"id" comment:"通知记录唯一标识"`
|
||||
CertificationID *string `gorm:"type:varchar(36);index" json:"certification_id,omitempty" comment:"关联的认证申请ID(可为空)"`
|
||||
UserID *string `gorm:"type:varchar(36);index" json:"user_id,omitempty" comment:"接收用户ID(可为空)"`
|
||||
|
||||
// 通知类型和渠道 - 定义通知的发送方式和业务场景
|
||||
NotificationType string `gorm:"type:varchar(50);not null;index" json:"notification_type" comment:"通知类型(SMS/WECHAT_WORK/EMAIL)"`
|
||||
NotificationScene string `gorm:"type:varchar(50);not null;index" json:"notification_scene" comment:"通知场景(ADMIN_NEW_APPLICATION/USER_CONTRACT_READY等)"`
|
||||
|
||||
// 接收方信息 - 通知的目标接收者
|
||||
Recipient string `gorm:"type:varchar(255);not null" json:"recipient" comment:"接收方标识(手机号/邮箱/用户ID)"`
|
||||
|
||||
// 消息内容 - 通知的具体内容信息
|
||||
Title string `gorm:"type:varchar(255)" json:"title,omitempty" comment:"通知标题"`
|
||||
Content string `gorm:"type:text;not null" json:"content" comment:"通知内容"`
|
||||
TemplateID string `gorm:"type:varchar(100)" json:"template_id,omitempty" comment:"消息模板ID"`
|
||||
TemplateParams string `gorm:"type:text" json:"template_params,omitempty" comment:"模板参数(JSON格式)"`
|
||||
|
||||
// 发送状态 - 记录通知的发送过程和结果
|
||||
Status string `gorm:"type:varchar(50);not null;index" json:"status" comment:"发送状态(PENDING/SENT/FAILED)"`
|
||||
ErrorMessage string `gorm:"type:varchar(500)" json:"error_message,omitempty" comment:"发送失败的错误信息"`
|
||||
SentAt *time.Time `json:"sent_at,omitempty" comment:"发送成功时间"`
|
||||
RetryCount int `gorm:"default:0" json:"retry_count" comment:"当前重试次数"`
|
||||
MaxRetryCount int `gorm:"default:3" json:"max_retry_count" comment:"最大重试次数"`
|
||||
|
||||
// 时间戳字段
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at" comment:"创建时间"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at" comment:"更新时间"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-" comment:"软删除时间"`
|
||||
|
||||
// 关联关系
|
||||
Certification *Certification `gorm:"foreignKey:CertificationID" json:"certification,omitempty" comment:"关联的认证申请"`
|
||||
}
|
||||
|
||||
// TableName 指定数据库表名
|
||||
func (NotificationRecord) TableName() string {
|
||||
return "notification_records"
|
||||
}
|
||||
|
||||
// BeforeCreate GORM钩子:创建前自动生成UUID
|
||||
func (n *NotificationRecord) BeforeCreate(tx *gorm.DB) error {
|
||||
if n.ID == "" {
|
||||
n.ID = uuid.New().String()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsPending 检查通知是否待发送
|
||||
// 判断通知是否处于等待发送的状态
|
||||
func (n *NotificationRecord) IsPending() bool {
|
||||
return n.Status == "PENDING"
|
||||
}
|
||||
|
||||
// IsSent 检查通知是否已发送
|
||||
// 判断通知是否已成功发送到接收方
|
||||
func (n *NotificationRecord) IsSent() bool {
|
||||
return n.Status == "SENT"
|
||||
}
|
||||
|
||||
// IsFailed 检查通知是否发送失败
|
||||
// 判断通知是否发送失败,包括网络错误、接收方无效等情况
|
||||
func (n *NotificationRecord) IsFailed() bool {
|
||||
return n.Status == "FAILED"
|
||||
}
|
||||
|
||||
// CanRetry 检查是否可以重试
|
||||
// 判断失败的通知是否还可以进行重试发送
|
||||
func (n *NotificationRecord) CanRetry() bool {
|
||||
return n.IsFailed() && n.RetryCount < n.MaxRetryCount
|
||||
}
|
||||
|
||||
// IncrementRetryCount 增加重试次数
|
||||
// 在重试发送时增加重试计数器
|
||||
func (n *NotificationRecord) IncrementRetryCount() {
|
||||
n.RetryCount++
|
||||
}
|
||||
|
||||
// GetStatusName 获取状态的中文名称
|
||||
// 将英文状态码转换为中文显示名称,用于前端展示
|
||||
func (n *NotificationRecord) GetStatusName() string {
|
||||
statusNames := map[string]string{
|
||||
"PENDING": "待发送",
|
||||
"SENT": "已发送",
|
||||
"FAILED": "发送失败",
|
||||
}
|
||||
|
||||
if name, exists := statusNames[n.Status]; exists {
|
||||
return name
|
||||
}
|
||||
return n.Status
|
||||
}
|
||||
|
||||
// GetNotificationTypeName 获取通知类型的中文名称
|
||||
// 将通知类型转换为中文显示名称,便于用户理解
|
||||
func (n *NotificationRecord) GetNotificationTypeName() string {
|
||||
typeNames := map[string]string{
|
||||
"SMS": "短信",
|
||||
"WECHAT_WORK": "企业微信",
|
||||
"EMAIL": "邮件",
|
||||
}
|
||||
|
||||
if name, exists := typeNames[n.NotificationType]; exists {
|
||||
return name
|
||||
}
|
||||
return n.NotificationType
|
||||
}
|
||||
|
||||
// GetNotificationSceneName 获取通知场景的中文名称
|
||||
// 将通知场景转换为中文显示名称,便于业务人员理解通知的触发原因
|
||||
func (n *NotificationRecord) GetNotificationSceneName() string {
|
||||
sceneNames := map[string]string{
|
||||
"ADMIN_NEW_APPLICATION": "管理员新申请通知",
|
||||
"USER_CONTRACT_READY": "用户合同就绪通知",
|
||||
"USER_CERTIFICATION_COMPLETED": "用户认证完成通知",
|
||||
"USER_FACE_VERIFY_FAILED": "用户人脸识别失败通知",
|
||||
"USER_CONTRACT_REJECTED": "用户合同被拒绝通知",
|
||||
}
|
||||
|
||||
if name, exists := sceneNames[n.NotificationScene]; exists {
|
||||
return name
|
||||
}
|
||||
return n.NotificationScene
|
||||
}
|
||||
@@ -5,29 +5,19 @@ type CertificationStatus string
|
||||
|
||||
const (
|
||||
// 主流程状态
|
||||
StatusNotStarted CertificationStatus = "not_started" // 未开始认证
|
||||
StatusPending CertificationStatus = "pending" // 待开始
|
||||
StatusInfoSubmitted CertificationStatus = "info_submitted" // 企业信息已提交
|
||||
StatusFaceVerified CertificationStatus = "face_verified" // 人脸识别完成
|
||||
StatusContractApplied CertificationStatus = "contract_applied" // 已申请合同
|
||||
StatusContractPending CertificationStatus = "contract_pending" // 合同待审核
|
||||
StatusContractApproved CertificationStatus = "contract_approved" // 合同已审核(有链接)
|
||||
StatusContractSigned CertificationStatus = "contract_signed" // 合同已签署
|
||||
StatusPending CertificationStatus = "pending" // 待认证
|
||||
StatusInfoSubmitted CertificationStatus = "info_submitted" // 已提交企业信息
|
||||
StatusEnterpriseVerified CertificationStatus = "enterprise_verified" // 已企业认证
|
||||
StatusContractApplied CertificationStatus = "contract_applied" // 已申请签署合同
|
||||
StatusContractSigned CertificationStatus = "contract_signed" // 已签署合同
|
||||
StatusCompleted CertificationStatus = "completed" // 认证完成
|
||||
|
||||
// 失败和重试状态
|
||||
StatusFaceFailed CertificationStatus = "face_failed" // 人脸识别失败
|
||||
StatusSignFailed CertificationStatus = "sign_failed" // 签署失败
|
||||
StatusRejected CertificationStatus = "rejected" // 已拒绝
|
||||
)
|
||||
|
||||
// IsValidStatus 检查状态是否有效
|
||||
func IsValidStatus(status CertificationStatus) bool {
|
||||
validStatuses := []CertificationStatus{
|
||||
StatusNotStarted, StatusPending, StatusInfoSubmitted, StatusFaceVerified,
|
||||
StatusContractApplied, StatusContractPending, StatusContractApproved,
|
||||
StatusContractSigned, StatusCompleted, StatusFaceFailed,
|
||||
StatusSignFailed, StatusRejected,
|
||||
StatusPending, StatusInfoSubmitted, StatusEnterpriseVerified,
|
||||
StatusContractApplied, StatusContractSigned, StatusCompleted,
|
||||
}
|
||||
|
||||
for _, validStatus := range validStatuses {
|
||||
@@ -41,18 +31,12 @@ func IsValidStatus(status CertificationStatus) bool {
|
||||
// GetStatusName 获取状态的中文名称
|
||||
func GetStatusName(status CertificationStatus) string {
|
||||
statusNames := map[CertificationStatus]string{
|
||||
StatusNotStarted: "未开始认证",
|
||||
StatusPending: "待开始",
|
||||
StatusInfoSubmitted: "企业信息已提交",
|
||||
StatusFaceVerified: "人脸识别完成",
|
||||
StatusContractApplied: "已申请合同",
|
||||
StatusContractPending: "合同待审核",
|
||||
StatusContractApproved: "合同已审核",
|
||||
StatusContractSigned: "合同已签署",
|
||||
StatusCompleted: "认证完成",
|
||||
StatusFaceFailed: "人脸识别失败",
|
||||
StatusSignFailed: "签署失败",
|
||||
StatusRejected: "已拒绝",
|
||||
StatusPending: "待认证",
|
||||
StatusInfoSubmitted: "已提交企业信息",
|
||||
StatusEnterpriseVerified: "已企业认证",
|
||||
StatusContractApplied: "已申请合同",
|
||||
StatusContractSigned: "已签署合同",
|
||||
StatusCompleted: "认证完成",
|
||||
}
|
||||
|
||||
if name, exists := statusNames[status]; exists {
|
||||
@@ -63,28 +47,36 @@ func GetStatusName(status CertificationStatus) string {
|
||||
|
||||
// IsFinalStatus 判断是否为最终状态
|
||||
func IsFinalStatus(status CertificationStatus) bool {
|
||||
finalStatuses := []CertificationStatus{
|
||||
StatusCompleted, StatusRejected,
|
||||
}
|
||||
|
||||
for _, finalStatus := range finalStatuses {
|
||||
if status == finalStatus {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return status == StatusCompleted
|
||||
}
|
||||
|
||||
// IsFailedStatus 判断是否为失败状态
|
||||
func IsFailedStatus(status CertificationStatus) bool {
|
||||
failedStatuses := []CertificationStatus{
|
||||
StatusFaceFailed, StatusSignFailed, StatusRejected,
|
||||
// GetStatusCategory 获取状态分类
|
||||
func GetStatusCategory(status CertificationStatus) string {
|
||||
switch status {
|
||||
case StatusPending:
|
||||
return "initial"
|
||||
case StatusInfoSubmitted, StatusEnterpriseVerified, StatusContractApplied, StatusContractSigned:
|
||||
return "processing"
|
||||
case StatusCompleted:
|
||||
return "completed"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// GetStatusPriority 获取状态优先级(用于排序)
|
||||
func GetStatusPriority(status CertificationStatus) int {
|
||||
priorities := map[CertificationStatus]int{
|
||||
StatusPending: 0,
|
||||
StatusInfoSubmitted: 1,
|
||||
StatusEnterpriseVerified: 2,
|
||||
StatusContractApplied: 3,
|
||||
StatusContractSigned: 4,
|
||||
StatusCompleted: 5,
|
||||
}
|
||||
|
||||
for _, failedStatus := range failedStatuses {
|
||||
if status == failedStatus {
|
||||
return true
|
||||
}
|
||||
if priority, exists := priorities[status]; exists {
|
||||
return priority
|
||||
}
|
||||
return false
|
||||
return 999
|
||||
}
|
||||
|
||||
@@ -5,25 +5,18 @@ import (
|
||||
"time"
|
||||
|
||||
"tyapi-server/internal/domains/certification/entities"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// 认证事件类型常量
|
||||
// 事件类型常量
|
||||
const (
|
||||
EventTypeCertificationCreated = "certification.created"
|
||||
EventTypeCertificationSubmitted = "certification.submitted"
|
||||
EventTypeLicenseUploaded = "certification.license.uploaded"
|
||||
EventTypeOCRCompleted = "certification.ocr.completed"
|
||||
EventTypeEnterpriseInfoConfirmed = "certification.enterprise.confirmed"
|
||||
EventTypeFaceVerifyInitiated = "certification.face_verify.initiated"
|
||||
EventTypeFaceVerifyCompleted = "certification.face_verify.completed"
|
||||
EventTypeContractRequested = "certification.contract.requested"
|
||||
EventTypeContractGenerated = "certification.contract.generated"
|
||||
EventTypeContractSigned = "certification.contract.signed"
|
||||
EventTypeCertificationApproved = "certification.approved"
|
||||
EventTypeCertificationRejected = "certification.rejected"
|
||||
EventTypeWalletCreated = "certification.wallet.created"
|
||||
EventTypeCertificationCompleted = "certification.completed"
|
||||
EventTypeCertificationFailed = "certification.failed"
|
||||
EventTypeCertificationCreated = "certification.created"
|
||||
EventTypeEnterpriseInfoSubmitted = "enterprise.info.submitted"
|
||||
EventTypeEnterpriseVerified = "enterprise.verified"
|
||||
EventTypeContractApplied = "contract.applied"
|
||||
EventTypeContractSigned = "contract.signed"
|
||||
EventTypeCertificationCompleted = "certification.completed"
|
||||
)
|
||||
|
||||
// BaseCertificationEvent 认证事件基础结构
|
||||
@@ -39,7 +32,7 @@ type BaseCertificationEvent struct {
|
||||
Payload interface{} `json:"payload"`
|
||||
}
|
||||
|
||||
// 实现 Event 接口
|
||||
// 实现 DomainEvent 接口
|
||||
func (e *BaseCertificationEvent) GetID() string { return e.ID }
|
||||
func (e *BaseCertificationEvent) GetType() string { return e.Type }
|
||||
func (e *BaseCertificationEvent) GetVersion() string { return e.Version }
|
||||
@@ -62,15 +55,15 @@ func NewBaseCertificationEvent(eventType, aggregateID string, payload interface{
|
||||
Type: eventType,
|
||||
Version: "1.0",
|
||||
Timestamp: time.Now(),
|
||||
Source: "certification-domain",
|
||||
Source: "certification-service",
|
||||
AggregateID: aggregateID,
|
||||
AggregateType: "certification",
|
||||
AggregateType: "Certification",
|
||||
Metadata: make(map[string]interface{}),
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
|
||||
// CertificationCreatedEvent 认证创建事件
|
||||
// CertificationCreatedEvent 认证申请创建事件
|
||||
type CertificationCreatedEvent struct {
|
||||
*BaseCertificationEvent
|
||||
Data struct {
|
||||
@@ -80,7 +73,7 @@ type CertificationCreatedEvent struct {
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// NewCertificationCreatedEvent 创建认证创建事件
|
||||
// NewCertificationCreatedEvent 创建认证申请创建事件
|
||||
func NewCertificationCreatedEvent(certification *entities.Certification) *CertificationCreatedEvent {
|
||||
event := &CertificationCreatedEvent{
|
||||
BaseCertificationEvent: NewBaseCertificationEvent(
|
||||
@@ -96,8 +89,8 @@ func NewCertificationCreatedEvent(certification *entities.Certification) *Certif
|
||||
return event
|
||||
}
|
||||
|
||||
// CertificationSubmittedEvent 认证提交事件
|
||||
type CertificationSubmittedEvent struct {
|
||||
// EnterpriseInfoSubmittedEvent 企业信息提交事件
|
||||
type EnterpriseInfoSubmittedEvent struct {
|
||||
*BaseCertificationEvent
|
||||
Data struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
@@ -106,11 +99,11 @@ type CertificationSubmittedEvent struct {
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// NewCertificationSubmittedEvent 创建认证提交事件
|
||||
func NewCertificationSubmittedEvent(certification *entities.Certification) *CertificationSubmittedEvent {
|
||||
event := &CertificationSubmittedEvent{
|
||||
// NewEnterpriseInfoSubmittedEvent 创建企业信息提交事件
|
||||
func NewEnterpriseInfoSubmittedEvent(certification *entities.Certification) *EnterpriseInfoSubmittedEvent {
|
||||
event := &EnterpriseInfoSubmittedEvent{
|
||||
BaseCertificationEvent: NewBaseCertificationEvent(
|
||||
EventTypeCertificationSubmitted,
|
||||
EventTypeEnterpriseInfoSubmitted,
|
||||
certification.ID,
|
||||
nil,
|
||||
),
|
||||
@@ -122,158 +115,8 @@ func NewCertificationSubmittedEvent(certification *entities.Certification) *Cert
|
||||
return event
|
||||
}
|
||||
|
||||
// LicenseUploadedEvent 营业执照上传事件
|
||||
type LicenseUploadedEvent struct {
|
||||
*BaseCertificationEvent
|
||||
Data struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
FileURL string `json:"file_url"`
|
||||
FileName string `json:"file_name"`
|
||||
FileSize int64 `json:"file_size"`
|
||||
Status string `json:"status"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// NewLicenseUploadedEvent 创建营业执照上传事件
|
||||
func NewLicenseUploadedEvent(certification *entities.Certification, record *entities.LicenseUploadRecord) *LicenseUploadedEvent {
|
||||
event := &LicenseUploadedEvent{
|
||||
BaseCertificationEvent: NewBaseCertificationEvent(
|
||||
EventTypeLicenseUploaded,
|
||||
certification.ID,
|
||||
nil,
|
||||
),
|
||||
}
|
||||
event.Data.CertificationID = certification.ID
|
||||
event.Data.UserID = certification.UserID
|
||||
event.Data.FileURL = record.FileURL
|
||||
event.Data.FileName = record.OriginalFileName
|
||||
event.Data.FileSize = record.FileSize
|
||||
event.Data.Status = string(certification.Status)
|
||||
event.Payload = event.Data
|
||||
return event
|
||||
}
|
||||
|
||||
// OCRCompletedEvent OCR识别完成事件
|
||||
type OCRCompletedEvent struct {
|
||||
*BaseCertificationEvent
|
||||
Data struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
OCRResult map[string]interface{} `json:"ocr_result"`
|
||||
Confidence float64 `json:"confidence"`
|
||||
Status string `json:"status"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// NewOCRCompletedEvent 创建OCR识别完成事件
|
||||
func NewOCRCompletedEvent(certification *entities.Certification, ocrResult map[string]interface{}, confidence float64) *OCRCompletedEvent {
|
||||
event := &OCRCompletedEvent{
|
||||
BaseCertificationEvent: NewBaseCertificationEvent(
|
||||
EventTypeOCRCompleted,
|
||||
certification.ID,
|
||||
nil,
|
||||
),
|
||||
}
|
||||
event.Data.CertificationID = certification.ID
|
||||
event.Data.UserID = certification.UserID
|
||||
event.Data.OCRResult = ocrResult
|
||||
event.Data.Confidence = confidence
|
||||
event.Data.Status = string(certification.Status)
|
||||
event.Payload = event.Data
|
||||
return event
|
||||
}
|
||||
|
||||
// EnterpriseInfoConfirmedEvent 企业信息确认事件
|
||||
type EnterpriseInfoConfirmedEvent struct {
|
||||
*BaseCertificationEvent
|
||||
Data struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
EnterpriseInfo map[string]interface{} `json:"enterprise_info"`
|
||||
Status string `json:"status"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// NewEnterpriseInfoConfirmedEvent 创建企业信息确认事件
|
||||
func NewEnterpriseInfoConfirmedEvent(certification *entities.Certification, enterpriseInfo map[string]interface{}) *EnterpriseInfoConfirmedEvent {
|
||||
event := &EnterpriseInfoConfirmedEvent{
|
||||
BaseCertificationEvent: NewBaseCertificationEvent(
|
||||
EventTypeEnterpriseInfoConfirmed,
|
||||
certification.ID,
|
||||
nil,
|
||||
),
|
||||
}
|
||||
event.Data.CertificationID = certification.ID
|
||||
event.Data.UserID = certification.UserID
|
||||
event.Data.EnterpriseInfo = enterpriseInfo
|
||||
event.Data.Status = string(certification.Status)
|
||||
event.Payload = event.Data
|
||||
return event
|
||||
}
|
||||
|
||||
// FaceVerifyInitiatedEvent 人脸识别初始化事件
|
||||
type FaceVerifyInitiatedEvent struct {
|
||||
*BaseCertificationEvent
|
||||
Data struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
VerifyToken string `json:"verify_token"`
|
||||
Status string `json:"status"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// NewFaceVerifyInitiatedEvent 创建人脸识别初始化事件
|
||||
func NewFaceVerifyInitiatedEvent(certification *entities.Certification, verifyToken string) *FaceVerifyInitiatedEvent {
|
||||
event := &FaceVerifyInitiatedEvent{
|
||||
BaseCertificationEvent: NewBaseCertificationEvent(
|
||||
EventTypeFaceVerifyInitiated,
|
||||
certification.ID,
|
||||
nil,
|
||||
),
|
||||
}
|
||||
event.Data.CertificationID = certification.ID
|
||||
event.Data.UserID = certification.UserID
|
||||
event.Data.VerifyToken = verifyToken
|
||||
event.Data.Status = string(certification.Status)
|
||||
event.Payload = event.Data
|
||||
return event
|
||||
}
|
||||
|
||||
// FaceVerifyCompletedEvent 人脸识别完成事件
|
||||
type FaceVerifyCompletedEvent struct {
|
||||
*BaseCertificationEvent
|
||||
Data struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
VerifyToken string `json:"verify_token"`
|
||||
Success bool `json:"success"`
|
||||
Score float64 `json:"score"`
|
||||
Status string `json:"status"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// NewFaceVerifyCompletedEvent 创建人脸识别完成事件
|
||||
func NewFaceVerifyCompletedEvent(certification *entities.Certification, record *entities.FaceVerifyRecord) *FaceVerifyCompletedEvent {
|
||||
event := &FaceVerifyCompletedEvent{
|
||||
BaseCertificationEvent: NewBaseCertificationEvent(
|
||||
EventTypeFaceVerifyCompleted,
|
||||
certification.ID,
|
||||
nil,
|
||||
),
|
||||
}
|
||||
event.Data.CertificationID = certification.ID
|
||||
event.Data.UserID = certification.UserID
|
||||
event.Data.VerifyToken = record.CertifyID
|
||||
event.Data.Success = record.IsSuccess()
|
||||
event.Data.Score = record.VerifyScore
|
||||
event.Data.Status = string(certification.Status)
|
||||
event.Payload = event.Data
|
||||
return event
|
||||
}
|
||||
|
||||
// ContractRequestedEvent 合同申请事件
|
||||
type ContractRequestedEvent struct {
|
||||
// EnterpriseVerifiedEvent 企业认证完成事件
|
||||
type EnterpriseVerifiedEvent struct {
|
||||
*BaseCertificationEvent
|
||||
Data struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
@@ -282,11 +125,11 @@ type ContractRequestedEvent struct {
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// NewContractRequestedEvent 创建合同申请事件
|
||||
func NewContractRequestedEvent(certification *entities.Certification) *ContractRequestedEvent {
|
||||
event := &ContractRequestedEvent{
|
||||
// NewEnterpriseVerifiedEvent 创建企业认证完成事件
|
||||
func NewEnterpriseVerifiedEvent(certification *entities.Certification) *EnterpriseVerifiedEvent {
|
||||
event := &EnterpriseVerifiedEvent{
|
||||
BaseCertificationEvent: NewBaseCertificationEvent(
|
||||
EventTypeContractRequested,
|
||||
EventTypeEnterpriseVerified,
|
||||
certification.ID,
|
||||
nil,
|
||||
),
|
||||
@@ -298,31 +141,27 @@ func NewContractRequestedEvent(certification *entities.Certification) *ContractR
|
||||
return event
|
||||
}
|
||||
|
||||
// ContractGeneratedEvent 合同生成事件
|
||||
type ContractGeneratedEvent struct {
|
||||
// ContractAppliedEvent 合同申请事件
|
||||
type ContractAppliedEvent struct {
|
||||
*BaseCertificationEvent
|
||||
Data struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
ContractURL string `json:"contract_url"`
|
||||
ContractID string `json:"contract_id"`
|
||||
Status string `json:"status"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// NewContractGeneratedEvent 创建合同生成事件
|
||||
func NewContractGeneratedEvent(certification *entities.Certification, record *entities.ContractRecord) *ContractGeneratedEvent {
|
||||
event := &ContractGeneratedEvent{
|
||||
// NewContractAppliedEvent 创建合同申请事件
|
||||
func NewContractAppliedEvent(certification *entities.Certification) *ContractAppliedEvent {
|
||||
event := &ContractAppliedEvent{
|
||||
BaseCertificationEvent: NewBaseCertificationEvent(
|
||||
EventTypeContractGenerated,
|
||||
EventTypeContractApplied,
|
||||
certification.ID,
|
||||
nil,
|
||||
),
|
||||
}
|
||||
event.Data.CertificationID = certification.ID
|
||||
event.Data.UserID = certification.UserID
|
||||
event.Data.ContractURL = record.ContractURL
|
||||
event.Data.ContractID = record.ID
|
||||
event.Data.Status = string(certification.Status)
|
||||
event.Payload = event.Data
|
||||
return event
|
||||
@@ -334,14 +173,13 @@ type ContractSignedEvent struct {
|
||||
Data struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
ContractID string `json:"contract_id"`
|
||||
SignedAt string `json:"signed_at"`
|
||||
ContractURL string `json:"contract_url"`
|
||||
Status string `json:"status"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// NewContractSignedEvent 创建合同签署事件
|
||||
func NewContractSignedEvent(certification *entities.Certification, record *entities.ContractRecord) *ContractSignedEvent {
|
||||
func NewContractSignedEvent(certification *entities.Certification, contractURL string) *ContractSignedEvent {
|
||||
event := &ContractSignedEvent{
|
||||
BaseCertificationEvent: NewBaseCertificationEvent(
|
||||
EventTypeContractSigned,
|
||||
@@ -351,100 +189,7 @@ func NewContractSignedEvent(certification *entities.Certification, record *entit
|
||||
}
|
||||
event.Data.CertificationID = certification.ID
|
||||
event.Data.UserID = certification.UserID
|
||||
event.Data.ContractID = record.ID
|
||||
event.Data.SignedAt = record.SignedAt.Format(time.RFC3339)
|
||||
event.Data.Status = string(certification.Status)
|
||||
event.Payload = event.Data
|
||||
return event
|
||||
}
|
||||
|
||||
// CertificationApprovedEvent 认证审核通过事件
|
||||
type CertificationApprovedEvent struct {
|
||||
*BaseCertificationEvent
|
||||
Data struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
AdminID string `json:"admin_id"`
|
||||
ApprovedAt string `json:"approved_at"`
|
||||
Status string `json:"status"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// NewCertificationApprovedEvent 创建认证审核通过事件
|
||||
func NewCertificationApprovedEvent(certification *entities.Certification, adminID string) *CertificationApprovedEvent {
|
||||
event := &CertificationApprovedEvent{
|
||||
BaseCertificationEvent: NewBaseCertificationEvent(
|
||||
EventTypeCertificationApproved,
|
||||
certification.ID,
|
||||
nil,
|
||||
),
|
||||
}
|
||||
event.Data.CertificationID = certification.ID
|
||||
event.Data.UserID = certification.UserID
|
||||
event.Data.AdminID = adminID
|
||||
event.Data.ApprovedAt = time.Now().Format(time.RFC3339)
|
||||
event.Data.Status = string(certification.Status)
|
||||
event.Payload = event.Data
|
||||
return event
|
||||
}
|
||||
|
||||
// CertificationRejectedEvent 认证审核拒绝事件
|
||||
type CertificationRejectedEvent struct {
|
||||
*BaseCertificationEvent
|
||||
Data struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
AdminID string `json:"admin_id"`
|
||||
RejectReason string `json:"reject_reason"`
|
||||
RejectedAt string `json:"rejected_at"`
|
||||
Status string `json:"status"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// NewCertificationRejectedEvent 创建认证审核拒绝事件
|
||||
func NewCertificationRejectedEvent(certification *entities.Certification, adminID, rejectReason string) *CertificationRejectedEvent {
|
||||
event := &CertificationRejectedEvent{
|
||||
BaseCertificationEvent: NewBaseCertificationEvent(
|
||||
EventTypeCertificationRejected,
|
||||
certification.ID,
|
||||
nil,
|
||||
),
|
||||
}
|
||||
event.Data.CertificationID = certification.ID
|
||||
event.Data.UserID = certification.UserID
|
||||
event.Data.AdminID = adminID
|
||||
event.Data.RejectReason = rejectReason
|
||||
event.Data.RejectedAt = time.Now().Format(time.RFC3339)
|
||||
event.Data.Status = string(certification.Status)
|
||||
event.Payload = event.Data
|
||||
return event
|
||||
}
|
||||
|
||||
// WalletCreatedEvent 钱包创建事件
|
||||
type WalletCreatedEvent struct {
|
||||
*BaseCertificationEvent
|
||||
Data struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
WalletID string `json:"wallet_id"`
|
||||
AccessID string `json:"access_id"`
|
||||
Status string `json:"status"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// NewWalletCreatedEvent 创建钱包创建事件
|
||||
func NewWalletCreatedEvent(certification *entities.Certification, walletID, accessID string) *WalletCreatedEvent {
|
||||
event := &WalletCreatedEvent{
|
||||
BaseCertificationEvent: NewBaseCertificationEvent(
|
||||
EventTypeWalletCreated,
|
||||
certification.ID,
|
||||
nil,
|
||||
),
|
||||
}
|
||||
event.Data.CertificationID = certification.ID
|
||||
event.Data.UserID = certification.UserID
|
||||
event.Data.WalletID = walletID
|
||||
event.Data.AccessID = accessID
|
||||
event.Data.ContractURL = contractURL
|
||||
event.Data.Status = string(certification.Status)
|
||||
event.Payload = event.Data
|
||||
return event
|
||||
@@ -456,14 +201,13 @@ type CertificationCompletedEvent struct {
|
||||
Data struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
WalletID string `json:"wallet_id"`
|
||||
CompletedAt string `json:"completed_at"`
|
||||
Status string `json:"status"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// NewCertificationCompletedEvent 创建认证完成事件
|
||||
func NewCertificationCompletedEvent(certification *entities.Certification, walletID string) *CertificationCompletedEvent {
|
||||
func NewCertificationCompletedEvent(certification *entities.Certification) *CertificationCompletedEvent {
|
||||
event := &CertificationCompletedEvent{
|
||||
BaseCertificationEvent: NewBaseCertificationEvent(
|
||||
EventTypeCertificationCompleted,
|
||||
@@ -473,54 +217,13 @@ func NewCertificationCompletedEvent(certification *entities.Certification, walle
|
||||
}
|
||||
event.Data.CertificationID = certification.ID
|
||||
event.Data.UserID = certification.UserID
|
||||
event.Data.WalletID = walletID
|
||||
event.Data.CompletedAt = time.Now().Format(time.RFC3339)
|
||||
event.Data.Status = string(certification.Status)
|
||||
event.Payload = event.Data
|
||||
return event
|
||||
}
|
||||
|
||||
// CertificationFailedEvent 认证失败事件
|
||||
type CertificationFailedEvent struct {
|
||||
*BaseCertificationEvent
|
||||
Data struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
FailedAt string `json:"failed_at"`
|
||||
FailureReason string `json:"failure_reason"`
|
||||
Status string `json:"status"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// NewCertificationFailedEvent 创建认证失败事件
|
||||
func NewCertificationFailedEvent(certification *entities.Certification, failureReason string) *CertificationFailedEvent {
|
||||
event := &CertificationFailedEvent{
|
||||
BaseCertificationEvent: NewBaseCertificationEvent(
|
||||
EventTypeCertificationFailed,
|
||||
certification.ID,
|
||||
nil,
|
||||
),
|
||||
}
|
||||
event.Data.CertificationID = certification.ID
|
||||
event.Data.UserID = certification.UserID
|
||||
event.Data.FailedAt = time.Now().Format(time.RFC3339)
|
||||
event.Data.FailureReason = failureReason
|
||||
event.Data.Status = string(certification.Status)
|
||||
event.Payload = event.Data
|
||||
return event
|
||||
}
|
||||
|
||||
// generateEventID 生成事件ID
|
||||
// 工具函数
|
||||
func generateEventID() string {
|
||||
return time.Now().Format("20060102150405") + "-" + generateRandomString(8)
|
||||
}
|
||||
|
||||
// generateRandomString 生成随机字符串
|
||||
func generateRandomString(length int) string {
|
||||
const charset = "abcdefghijklmnopqrstuvwxyz0123456789"
|
||||
b := make([]byte, length)
|
||||
for i := range b {
|
||||
b[i] = charset[time.Now().UnixNano()%int64(len(charset))]
|
||||
}
|
||||
return string(b)
|
||||
return uuid.New().String()
|
||||
}
|
||||
|
||||
@@ -29,20 +29,11 @@ func NewCertificationEventHandler(logger *zap.Logger, notification notification.
|
||||
name: "certification-event-handler",
|
||||
eventTypes: []string{
|
||||
EventTypeCertificationCreated,
|
||||
EventTypeCertificationSubmitted,
|
||||
EventTypeLicenseUploaded,
|
||||
EventTypeOCRCompleted,
|
||||
EventTypeEnterpriseInfoConfirmed,
|
||||
EventTypeFaceVerifyInitiated,
|
||||
EventTypeFaceVerifyCompleted,
|
||||
EventTypeContractRequested,
|
||||
EventTypeContractGenerated,
|
||||
EventTypeEnterpriseInfoSubmitted,
|
||||
EventTypeEnterpriseVerified,
|
||||
EventTypeContractApplied,
|
||||
EventTypeContractSigned,
|
||||
EventTypeCertificationApproved,
|
||||
EventTypeCertificationRejected,
|
||||
EventTypeWalletCreated,
|
||||
EventTypeCertificationCompleted,
|
||||
EventTypeCertificationFailed,
|
||||
},
|
||||
isAsync: true,
|
||||
}
|
||||
@@ -84,34 +75,16 @@ func (h *CertificationEventHandler) Handle(ctx context.Context, event interfaces
|
||||
switch event.GetType() {
|
||||
case EventTypeCertificationCreated:
|
||||
return h.handleCertificationCreated(ctx, event)
|
||||
case EventTypeCertificationSubmitted:
|
||||
return h.handleCertificationSubmitted(ctx, event)
|
||||
case EventTypeLicenseUploaded:
|
||||
return h.handleLicenseUploaded(ctx, event)
|
||||
case EventTypeOCRCompleted:
|
||||
return h.handleOCRCompleted(ctx, event)
|
||||
case EventTypeEnterpriseInfoConfirmed:
|
||||
return h.handleEnterpriseInfoConfirmed(ctx, event)
|
||||
case EventTypeFaceVerifyInitiated:
|
||||
return h.handleFaceVerifyInitiated(ctx, event)
|
||||
case EventTypeFaceVerifyCompleted:
|
||||
return h.handleFaceVerifyCompleted(ctx, event)
|
||||
case EventTypeContractRequested:
|
||||
return h.handleContractRequested(ctx, event)
|
||||
case EventTypeContractGenerated:
|
||||
return h.handleContractGenerated(ctx, event)
|
||||
case EventTypeEnterpriseInfoSubmitted:
|
||||
return h.handleEnterpriseInfoSubmitted(ctx, event)
|
||||
case EventTypeEnterpriseVerified:
|
||||
return h.handleEnterpriseVerified(ctx, event)
|
||||
case EventTypeContractApplied:
|
||||
return h.handleContractApplied(ctx, event)
|
||||
case EventTypeContractSigned:
|
||||
return h.handleContractSigned(ctx, event)
|
||||
case EventTypeCertificationApproved:
|
||||
return h.handleCertificationApproved(ctx, event)
|
||||
case EventTypeCertificationRejected:
|
||||
return h.handleCertificationRejected(ctx, event)
|
||||
case EventTypeWalletCreated:
|
||||
return h.handleWalletCreated(ctx, event)
|
||||
case EventTypeCertificationCompleted:
|
||||
return h.handleCertificationCompleted(ctx, event)
|
||||
case EventTypeCertificationFailed:
|
||||
return h.handleCertificationFailed(ctx, event)
|
||||
default:
|
||||
h.logger.Warn("未知的事件类型", zap.String("event_type", event.GetType()))
|
||||
return nil
|
||||
@@ -133,126 +106,49 @@ func (h *CertificationEventHandler) handleCertificationCreated(ctx context.Conte
|
||||
return h.sendUserNotification(ctx, event, "认证申请创建成功", message)
|
||||
}
|
||||
|
||||
// handleCertificationSubmitted 处理认证提交事件
|
||||
func (h *CertificationEventHandler) handleCertificationSubmitted(ctx context.Context, event interfaces.Event) error {
|
||||
h.logger.Info("认证申请已提交",
|
||||
zap.String("certification_id", event.GetAggregateID()),
|
||||
zap.String("user_id", h.extractUserID(event)),
|
||||
)
|
||||
|
||||
// 发送通知给管理员
|
||||
adminMessage := fmt.Sprintf("📋 新的企业认证申请待审核\n\n认证ID: %s\n用户ID: %s\n提交时间: %s\n\n请及时处理审核。",
|
||||
event.GetAggregateID(),
|
||||
h.extractUserID(event),
|
||||
event.GetTimestamp().Format("2006-01-02 15:04:05"))
|
||||
|
||||
return h.sendAdminNotification(ctx, event, "新认证申请待审核", adminMessage)
|
||||
}
|
||||
|
||||
// handleLicenseUploaded 处理营业执照上传事件
|
||||
func (h *CertificationEventHandler) handleLicenseUploaded(ctx context.Context, event interfaces.Event) error {
|
||||
h.logger.Info("营业执照已上传",
|
||||
// handleEnterpriseInfoSubmitted 处理企业信息提交事件
|
||||
func (h *CertificationEventHandler) handleEnterpriseInfoSubmitted(ctx context.Context, event interfaces.Event) error {
|
||||
h.logger.Info("企业信息已提交",
|
||||
zap.String("certification_id", event.GetAggregateID()),
|
||||
zap.String("user_id", h.extractUserID(event)),
|
||||
)
|
||||
|
||||
// 发送通知给用户
|
||||
message := fmt.Sprintf("📄 营业执照上传成功!\n\n认证ID: %s\n上传时间: %s\n\n系统正在识别营业执照信息,请稍候...",
|
||||
message := fmt.Sprintf("✅ 企业信息提交成功!\n\n认证ID: %s\n提交时间: %s\n\n系统正在验证企业信息,请稍候...",
|
||||
event.GetAggregateID(),
|
||||
event.GetTimestamp().Format("2006-01-02 15:04:05"))
|
||||
|
||||
return h.sendUserNotification(ctx, event, "营业执照上传成功", message)
|
||||
return h.sendUserNotification(ctx, event, "企业信息提交成功", message)
|
||||
}
|
||||
|
||||
// handleOCRCompleted 处理OCR识别完成事件
|
||||
func (h *CertificationEventHandler) handleOCRCompleted(ctx context.Context, event interfaces.Event) error {
|
||||
h.logger.Info("OCR识别已完成",
|
||||
// handleEnterpriseVerified 处理企业认证完成事件
|
||||
func (h *CertificationEventHandler) handleEnterpriseVerified(ctx context.Context, event interfaces.Event) error {
|
||||
h.logger.Info("企业认证已完成",
|
||||
zap.String("certification_id", event.GetAggregateID()),
|
||||
zap.String("user_id", h.extractUserID(event)),
|
||||
)
|
||||
|
||||
// 发送通知给用户
|
||||
message := fmt.Sprintf("✅ OCR识别完成!\n\n认证ID: %s\n识别时间: %s\n\n请确认企业信息是否正确,如有问题请及时联系客服。",
|
||||
message := fmt.Sprintf("✅ 企业认证完成!\n\n认证ID: %s\n完成时间: %s\n\n下一步:请申请电子合同。",
|
||||
event.GetAggregateID(),
|
||||
event.GetTimestamp().Format("2006-01-02 15:04:05"))
|
||||
|
||||
return h.sendUserNotification(ctx, event, "OCR识别完成", message)
|
||||
return h.sendUserNotification(ctx, event, "企业认证完成", message)
|
||||
}
|
||||
|
||||
// handleEnterpriseInfoConfirmed 处理企业信息确认事件
|
||||
func (h *CertificationEventHandler) handleEnterpriseInfoConfirmed(ctx context.Context, event interfaces.Event) error {
|
||||
h.logger.Info("企业信息已确认",
|
||||
zap.String("certification_id", event.GetAggregateID()),
|
||||
zap.String("user_id", h.extractUserID(event)),
|
||||
)
|
||||
|
||||
// 发送通知给用户
|
||||
message := fmt.Sprintf("✅ 企业信息确认成功!\n\n认证ID: %s\n确认时间: %s\n\n下一步:请完成人脸识别验证。",
|
||||
event.GetAggregateID(),
|
||||
event.GetTimestamp().Format("2006-01-02 15:04:05"))
|
||||
|
||||
return h.sendUserNotification(ctx, event, "企业信息确认成功", message)
|
||||
}
|
||||
|
||||
// handleFaceVerifyInitiated 处理人脸识别初始化事件
|
||||
func (h *CertificationEventHandler) handleFaceVerifyInitiated(ctx context.Context, event interfaces.Event) error {
|
||||
h.logger.Info("人脸识别已初始化",
|
||||
zap.String("certification_id", event.GetAggregateID()),
|
||||
zap.String("user_id", h.extractUserID(event)),
|
||||
)
|
||||
|
||||
// 发送通知给用户
|
||||
message := fmt.Sprintf("👤 人脸识别验证已开始!\n\n认证ID: %s\n开始时间: %s\n\n请按照指引完成人脸识别验证。",
|
||||
event.GetAggregateID(),
|
||||
event.GetTimestamp().Format("2006-01-02 15:04:05"))
|
||||
|
||||
return h.sendUserNotification(ctx, event, "人脸识别验证开始", message)
|
||||
}
|
||||
|
||||
// handleFaceVerifyCompleted 处理人脸识别完成事件
|
||||
func (h *CertificationEventHandler) handleFaceVerifyCompleted(ctx context.Context, event interfaces.Event) error {
|
||||
h.logger.Info("人脸识别已完成",
|
||||
zap.String("certification_id", event.GetAggregateID()),
|
||||
zap.String("user_id", h.extractUserID(event)),
|
||||
)
|
||||
|
||||
// 发送通知给用户
|
||||
message := fmt.Sprintf("✅ 人脸识别验证完成!\n\n认证ID: %s\n完成时间: %s\n\n下一步:系统将为您申请电子合同。",
|
||||
event.GetAggregateID(),
|
||||
event.GetTimestamp().Format("2006-01-02 15:04:05"))
|
||||
|
||||
return h.sendUserNotification(ctx, event, "人脸识别验证完成", message)
|
||||
}
|
||||
|
||||
// handleContractRequested 处理合同申请事件
|
||||
func (h *CertificationEventHandler) handleContractRequested(ctx context.Context, event interfaces.Event) error {
|
||||
// handleContractApplied 处理合同申请事件
|
||||
func (h *CertificationEventHandler) handleContractApplied(ctx context.Context, event interfaces.Event) error {
|
||||
h.logger.Info("电子合同申请已提交",
|
||||
zap.String("certification_id", event.GetAggregateID()),
|
||||
zap.String("user_id", h.extractUserID(event)),
|
||||
)
|
||||
|
||||
// 发送通知给管理员
|
||||
adminMessage := fmt.Sprintf("📋 新的电子合同申请待审核\n\n认证ID: %s\n用户ID: %s\n申请时间: %s\n\n请及时处理合同审核。",
|
||||
event.GetAggregateID(),
|
||||
h.extractUserID(event),
|
||||
event.GetTimestamp().Format("2006-01-02 15:04:05"))
|
||||
|
||||
return h.sendAdminNotification(ctx, event, "新合同申请待审核", adminMessage)
|
||||
}
|
||||
|
||||
// handleContractGenerated 处理合同生成事件
|
||||
func (h *CertificationEventHandler) handleContractGenerated(ctx context.Context, event interfaces.Event) error {
|
||||
h.logger.Info("电子合同已生成",
|
||||
zap.String("certification_id", event.GetAggregateID()),
|
||||
zap.String("user_id", h.extractUserID(event)),
|
||||
)
|
||||
|
||||
// 发送通知给用户
|
||||
message := fmt.Sprintf("📄 电子合同已生成!\n\n认证ID: %s\n生成时间: %s\n\n请及时签署电子合同以完成认证流程。",
|
||||
message := fmt.Sprintf("📋 电子合同申请已提交!\n\n认证ID: %s\n申请时间: %s\n\n系统正在生成电子合同,请稍候...",
|
||||
event.GetAggregateID(),
|
||||
event.GetTimestamp().Format("2006-01-02 15:04:05"))
|
||||
|
||||
return h.sendUserNotification(ctx, event, "电子合同已生成", message)
|
||||
return h.sendUserNotification(ctx, event, "合同申请已提交", message)
|
||||
}
|
||||
|
||||
// handleContractSigned 处理合同签署事件
|
||||
@@ -263,56 +159,11 @@ func (h *CertificationEventHandler) handleContractSigned(ctx context.Context, ev
|
||||
)
|
||||
|
||||
// 发送通知给用户
|
||||
message := fmt.Sprintf("✅ 电子合同签署成功!\n\n认证ID: %s\n签署时间: %s\n\n您的企业认证申请已进入最终审核阶段。",
|
||||
message := fmt.Sprintf("✅ 电子合同签署完成!\n\n认证ID: %s\n签署时间: %s\n\n恭喜!您的企业认证已完成。",
|
||||
event.GetAggregateID(),
|
||||
event.GetTimestamp().Format("2006-01-02 15:04:05"))
|
||||
|
||||
return h.sendUserNotification(ctx, event, "电子合同签署成功", message)
|
||||
}
|
||||
|
||||
// handleCertificationApproved 处理认证审核通过事件
|
||||
func (h *CertificationEventHandler) handleCertificationApproved(ctx context.Context, event interfaces.Event) error {
|
||||
h.logger.Info("认证申请已审核通过",
|
||||
zap.String("certification_id", event.GetAggregateID()),
|
||||
zap.String("user_id", h.extractUserID(event)),
|
||||
)
|
||||
|
||||
// 发送通知给用户
|
||||
message := fmt.Sprintf("🎉 恭喜!您的企业认证申请已审核通过!\n\n认证ID: %s\n审核时间: %s\n\n系统正在为您创建钱包和访问密钥...",
|
||||
event.GetAggregateID(),
|
||||
event.GetTimestamp().Format("2006-01-02 15:04:05"))
|
||||
|
||||
return h.sendUserNotification(ctx, event, "认证申请审核通过", message)
|
||||
}
|
||||
|
||||
// handleCertificationRejected 处理认证审核拒绝事件
|
||||
func (h *CertificationEventHandler) handleCertificationRejected(ctx context.Context, event interfaces.Event) error {
|
||||
h.logger.Info("认证申请已被拒绝",
|
||||
zap.String("certification_id", event.GetAggregateID()),
|
||||
zap.String("user_id", h.extractUserID(event)),
|
||||
)
|
||||
|
||||
// 发送通知给用户
|
||||
message := fmt.Sprintf("❌ 很抱歉,您的企业认证申请未通过审核\n\n认证ID: %s\n拒绝时间: %s\n\n请根据拒绝原因修改后重新提交申请。",
|
||||
event.GetAggregateID(),
|
||||
event.GetTimestamp().Format("2006-01-02 15:04:05"))
|
||||
|
||||
return h.sendUserNotification(ctx, event, "认证申请审核未通过", message)
|
||||
}
|
||||
|
||||
// handleWalletCreated 处理钱包创建事件
|
||||
func (h *CertificationEventHandler) handleWalletCreated(ctx context.Context, event interfaces.Event) error {
|
||||
h.logger.Info("钱包已创建",
|
||||
zap.String("certification_id", event.GetAggregateID()),
|
||||
zap.String("user_id", h.extractUserID(event)),
|
||||
)
|
||||
|
||||
// 发送通知给用户
|
||||
message := fmt.Sprintf("💰 钱包创建成功!\n\n认证ID: %s\n创建时间: %s\n\n您的企业钱包已激活,可以开始使用相关服务。",
|
||||
event.GetAggregateID(),
|
||||
event.GetTimestamp().Format("2006-01-02 15:04:05"))
|
||||
|
||||
return h.sendUserNotification(ctx, event, "钱包创建成功", message)
|
||||
return h.sendUserNotification(ctx, event, "合同签署完成", message)
|
||||
}
|
||||
|
||||
// handleCertificationCompleted 处理认证完成事件
|
||||
@@ -323,44 +174,26 @@ func (h *CertificationEventHandler) handleCertificationCompleted(ctx context.Con
|
||||
)
|
||||
|
||||
// 发送通知给用户
|
||||
message := fmt.Sprintf("🎉 恭喜!您的企业认证已全部完成!\n\n认证ID: %s\n完成时间: %s\n\n您现在可以享受完整的企业级服务功能。",
|
||||
message := fmt.Sprintf("🎉 恭喜!您的企业认证已完成!\n\n认证ID: %s\n完成时间: %s\n\n您现在可以享受企业用户的所有权益。",
|
||||
event.GetAggregateID(),
|
||||
event.GetTimestamp().Format("2006-01-02 15:04:05"))
|
||||
|
||||
return h.sendUserNotification(ctx, event, "企业认证完成", message)
|
||||
}
|
||||
|
||||
// handleCertificationFailed 处理认证失败事件
|
||||
func (h *CertificationEventHandler) handleCertificationFailed(ctx context.Context, event interfaces.Event) error {
|
||||
h.logger.Error("企业认证失败",
|
||||
zap.String("certification_id", event.GetAggregateID()),
|
||||
zap.String("user_id", h.extractUserID(event)),
|
||||
)
|
||||
|
||||
// 发送通知给用户
|
||||
message := fmt.Sprintf("❌ 企业认证流程遇到问题\n\n认证ID: %s\n失败时间: %s\n\n请联系客服获取帮助。",
|
||||
event.GetAggregateID(),
|
||||
event.GetTimestamp().Format("2006-01-02 15:04:05"))
|
||||
|
||||
return h.sendUserNotification(ctx, event, "企业认证失败", message)
|
||||
}
|
||||
|
||||
// sendUserNotification 发送用户通知
|
||||
func (h *CertificationEventHandler) sendUserNotification(ctx context.Context, event interfaces.Event, title, message string) error {
|
||||
url := fmt.Sprintf("https://example.com/certification/%s", event.GetAggregateID())
|
||||
btnText := "查看详情"
|
||||
if err := h.notification.SendCardMessage(ctx, title, message, url, btnText); err != nil {
|
||||
h.logger.Error("发送用户通知失败",
|
||||
zap.String("event_type", event.GetType()),
|
||||
zap.String("event_id", event.GetID()),
|
||||
zap.Error(err),
|
||||
)
|
||||
return err
|
||||
userID := h.extractUserID(event)
|
||||
if userID == "" {
|
||||
h.logger.Warn("无法提取用户ID,跳过通知发送")
|
||||
return nil
|
||||
}
|
||||
|
||||
h.logger.Info("用户通知发送成功",
|
||||
zap.String("event_type", event.GetType()),
|
||||
zap.String("event_id", event.GetID()),
|
||||
// 这里可以调用通知服务发送消息
|
||||
h.logger.Info("发送用户通知",
|
||||
zap.String("user_id", userID),
|
||||
zap.String("title", title),
|
||||
zap.String("message", message),
|
||||
)
|
||||
|
||||
return nil
|
||||
@@ -368,20 +201,10 @@ func (h *CertificationEventHandler) sendUserNotification(ctx context.Context, ev
|
||||
|
||||
// sendAdminNotification 发送管理员通知
|
||||
func (h *CertificationEventHandler) sendAdminNotification(ctx context.Context, event interfaces.Event, title, message string) error {
|
||||
url := fmt.Sprintf("https://admin.example.com/certification/%s", event.GetAggregateID())
|
||||
btnText := "立即处理"
|
||||
if err := h.notification.SendCardMessage(ctx, title, message, url, btnText); err != nil {
|
||||
h.logger.Error("发送管理员通知失败",
|
||||
zap.String("event_type", event.GetType()),
|
||||
zap.String("event_id", event.GetID()),
|
||||
zap.Error(err),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
h.logger.Info("管理员通知发送成功",
|
||||
zap.String("event_type", event.GetType()),
|
||||
zap.String("event_id", event.GetID()),
|
||||
// 这里可以调用通知服务发送管理员消息
|
||||
h.logger.Info("发送管理员通知",
|
||||
zap.String("title", title),
|
||||
zap.String("message", message),
|
||||
)
|
||||
|
||||
return nil
|
||||
@@ -389,29 +212,58 @@ func (h *CertificationEventHandler) sendAdminNotification(ctx context.Context, e
|
||||
|
||||
// extractUserID 从事件中提取用户ID
|
||||
func (h *CertificationEventHandler) extractUserID(event interfaces.Event) string {
|
||||
if payload, ok := event.GetPayload().(map[string]interface{}); ok {
|
||||
if userID, exists := payload["user_id"]; exists {
|
||||
if id, ok := userID.(string); ok {
|
||||
return id
|
||||
payload := event.GetPayload()
|
||||
if payload == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
// 尝试从payload中提取user_id
|
||||
if data, ok := payload.(map[string]interface{}); ok {
|
||||
if userID, exists := data["user_id"]; exists {
|
||||
if str, ok := userID.(string); ok {
|
||||
return str
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试从事件数据中提取
|
||||
if eventData, ok := event.(*BaseCertificationEvent); ok {
|
||||
if data, ok := eventData.Payload.(map[string]interface{}); ok {
|
||||
if userID, exists := data["user_id"]; exists {
|
||||
if id, ok := userID.(string); ok {
|
||||
return id
|
||||
// 尝试从JSON中解析
|
||||
if data, ok := payload.(map[string]interface{}); ok {
|
||||
if dataField, exists := data["data"]; exists {
|
||||
if dataMap, ok := dataField.(map[string]interface{}); ok {
|
||||
if userID, exists := dataMap["user_id"]; exists {
|
||||
if str, ok := userID.(string); ok {
|
||||
return str
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "unknown"
|
||||
// 尝试从JSON字符串解析
|
||||
if jsonData, err := json.Marshal(payload); err == nil {
|
||||
var data map[string]interface{}
|
||||
if err := json.Unmarshal(jsonData, &data); err == nil {
|
||||
if userID, exists := data["user_id"]; exists {
|
||||
if str, ok := userID.(string); ok {
|
||||
return str
|
||||
}
|
||||
}
|
||||
if dataField, exists := data["data"]; exists {
|
||||
if dataMap, ok := dataField.(map[string]interface{}); ok {
|
||||
if userID, exists := dataMap["user_id"]; exists {
|
||||
if str, ok := userID.(string); ok {
|
||||
return str
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// LoggingEventHandler 日志记录事件处理器
|
||||
// LoggingEventHandler 日志事件处理器
|
||||
type LoggingEventHandler struct {
|
||||
logger *zap.Logger
|
||||
name string
|
||||
@@ -419,29 +271,20 @@ type LoggingEventHandler struct {
|
||||
isAsync bool
|
||||
}
|
||||
|
||||
// NewLoggingEventHandler 创建日志记录事件处理器
|
||||
// NewLoggingEventHandler 创建日志事件处理器
|
||||
func NewLoggingEventHandler(logger *zap.Logger) *LoggingEventHandler {
|
||||
return &LoggingEventHandler{
|
||||
logger: logger,
|
||||
name: "logging-event-handler",
|
||||
eventTypes: []string{
|
||||
EventTypeCertificationCreated,
|
||||
EventTypeCertificationSubmitted,
|
||||
EventTypeLicenseUploaded,
|
||||
EventTypeOCRCompleted,
|
||||
EventTypeEnterpriseInfoConfirmed,
|
||||
EventTypeFaceVerifyInitiated,
|
||||
EventTypeFaceVerifyCompleted,
|
||||
EventTypeContractRequested,
|
||||
EventTypeContractGenerated,
|
||||
EventTypeEnterpriseInfoSubmitted,
|
||||
EventTypeEnterpriseVerified,
|
||||
EventTypeContractApplied,
|
||||
EventTypeContractSigned,
|
||||
EventTypeCertificationApproved,
|
||||
EventTypeCertificationRejected,
|
||||
EventTypeWalletCreated,
|
||||
EventTypeCertificationCompleted,
|
||||
EventTypeCertificationFailed,
|
||||
},
|
||||
isAsync: false, // 同步处理,确保日志及时记录
|
||||
isAsync: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,27 +306,21 @@ func (l *LoggingEventHandler) IsAsync() bool {
|
||||
// GetRetryConfig 获取重试配置
|
||||
func (l *LoggingEventHandler) GetRetryConfig() interfaces.RetryConfig {
|
||||
return interfaces.RetryConfig{
|
||||
MaxRetries: 1,
|
||||
RetryDelay: 1 * time.Second,
|
||||
MaxRetries: 0,
|
||||
RetryDelay: 0,
|
||||
BackoffFactor: 1.0,
|
||||
MaxDelay: 1 * time.Second,
|
||||
MaxDelay: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Handle 处理事件
|
||||
func (l *LoggingEventHandler) Handle(ctx context.Context, event interfaces.Event) error {
|
||||
// 记录结构化日志
|
||||
eventData, _ := json.Marshal(event.GetPayload())
|
||||
|
||||
l.logger.Info("认证事件记录",
|
||||
zap.String("event_id", event.GetID()),
|
||||
l.logger.Info("认证事件日志",
|
||||
zap.String("event_type", event.GetType()),
|
||||
zap.String("event_id", event.GetID()),
|
||||
zap.String("aggregate_id", event.GetAggregateID()),
|
||||
zap.String("aggregate_type", event.GetAggregateType()),
|
||||
zap.Time("timestamp", event.GetTimestamp()),
|
||||
zap.String("source", event.GetSource()),
|
||||
zap.String("payload", string(eventData)),
|
||||
zap.Any("payload", event.GetPayload()),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ type CertificationStats struct {
|
||||
TotalCertifications int64
|
||||
PendingCertifications int64
|
||||
CompletedCertifications int64
|
||||
RejectedCertifications int64
|
||||
TodaySubmissions int64
|
||||
}
|
||||
|
||||
@@ -24,75 +23,69 @@ type CertificationRepository interface {
|
||||
GetByUserID(ctx context.Context, userID string) (*entities.Certification, error)
|
||||
GetByStatus(ctx context.Context, status string) ([]*entities.Certification, error)
|
||||
GetPendingCertifications(ctx context.Context) ([]*entities.Certification, error)
|
||||
|
||||
GetByAuthFlowID(ctx context.Context, authFlowID string) (entities.Certification, error)
|
||||
GetByEsignFlowID(ctx context.Context, esignFlowID string) (entities.Certification, error)
|
||||
// 复杂查询 - 使用查询参数
|
||||
ListCertifications(ctx context.Context, query *queries.ListCertificationsQuery) ([]*entities.Certification, int64, error)
|
||||
|
||||
// 业务操作
|
||||
UpdateStatus(ctx context.Context, certificationID string, status string, adminID *string, notes string) error
|
||||
UpdateStatus(ctx context.Context, certificationID string, status string) error
|
||||
|
||||
// 统计信息
|
||||
GetStats(ctx context.Context) (*CertificationStats, error)
|
||||
GetStatsByDateRange(ctx context.Context, startDate, endDate string) (*CertificationStats, error)
|
||||
}
|
||||
|
||||
// FaceVerifyRecordRepository 人脸识别记录仓储接口
|
||||
type FaceVerifyRecordRepository interface {
|
||||
interfaces.Repository[entities.FaceVerifyRecord]
|
||||
// EnterpriseInfoSubmitRecordRepository 企业信息提交记录仓储接口
|
||||
type EnterpriseInfoSubmitRecordRepository interface {
|
||||
interfaces.Repository[entities.EnterpriseInfoSubmitRecord]
|
||||
|
||||
// 基础查询 - 直接使用实体
|
||||
GetByCertificationID(ctx context.Context, certificationID string) ([]*entities.FaceVerifyRecord, error)
|
||||
GetLatestByCertificationID(ctx context.Context, certificationID string) (*entities.FaceVerifyRecord, error)
|
||||
// 基础查询
|
||||
GetByUserID(ctx context.Context, userID string) ([]*entities.EnterpriseInfoSubmitRecord, error)
|
||||
GetLatestByUserID(ctx context.Context, userID string) (*entities.EnterpriseInfoSubmitRecord, error)
|
||||
|
||||
// 复杂查询 - 使用查询参数
|
||||
ListRecords(ctx context.Context, query *queries.ListFaceVerifyRecordsQuery) ([]*entities.FaceVerifyRecord, int64, error)
|
||||
|
||||
// 统计信息
|
||||
GetSuccessRate(ctx context.Context, days int) (float64, error)
|
||||
}
|
||||
|
||||
// ContractRecordRepository 合同记录仓储接口
|
||||
type ContractRecordRepository interface {
|
||||
interfaces.Repository[entities.ContractRecord]
|
||||
|
||||
// 基础查询 - 直接使用实体
|
||||
GetByCertificationID(ctx context.Context, certificationID string) ([]*entities.ContractRecord, error)
|
||||
GetLatestByCertificationID(ctx context.Context, certificationID string) (*entities.ContractRecord, error)
|
||||
|
||||
// 复杂查询 - 使用查询参数
|
||||
ListRecords(ctx context.Context, query *queries.ListContractRecordsQuery) ([]*entities.ContractRecord, int64, error)
|
||||
// 复杂查询
|
||||
ListRecords(ctx context.Context, query *queries.ListEnterpriseInfoSubmitRecordsQuery) ([]*entities.EnterpriseInfoSubmitRecord, int64, error)
|
||||
|
||||
// 业务操作
|
||||
UpdateContractStatus(ctx context.Context, recordID string, status string, adminID *string, notes string) error
|
||||
UpdateStatus(ctx context.Context, recordID string, status string, reason string) error
|
||||
}
|
||||
|
||||
// LicenseUploadRecordRepository 营业执照上传记录仓储接口
|
||||
type LicenseUploadRecordRepository interface {
|
||||
interfaces.Repository[entities.LicenseUploadRecord]
|
||||
// EsignContractGenerateRecordRepository e签宝生成合同记录仓储接口
|
||||
type EsignContractGenerateRecordRepository interface {
|
||||
interfaces.Repository[entities.EsignContractGenerateRecord]
|
||||
|
||||
// 基础查询 - 直接使用实体
|
||||
GetByCertificationID(ctx context.Context, certificationID string) (*entities.LicenseUploadRecord, error)
|
||||
// 基础查询
|
||||
GetByCertificationID(ctx context.Context, certificationID string) (*entities.EsignContractGenerateRecord, error)
|
||||
GetByUserID(ctx context.Context, userID string) ([]*entities.EsignContractGenerateRecord, error)
|
||||
GetLatestByCertificationID(ctx context.Context, certificationID string) (*entities.EsignContractGenerateRecord, error)
|
||||
|
||||
// 复杂查询 - 使用查询参数
|
||||
ListRecords(ctx context.Context, query *queries.ListLicenseUploadRecordsQuery) ([]*entities.LicenseUploadRecord, int64, error)
|
||||
// 复杂查询
|
||||
ListRecords(ctx context.Context, query *queries.ListEsignContractGenerateRecordsQuery) ([]*entities.EsignContractGenerateRecord, int64, error)
|
||||
|
||||
// 业务操作
|
||||
UpdateOCRResult(ctx context.Context, recordID string, ocrResult string, confidence float64) error
|
||||
UpdateStatus(ctx context.Context, recordID string, status string, reason string) error
|
||||
UpdateSuccessInfo(ctx context.Context, recordID, esignFlowID, contractFileID, contractURL string) error
|
||||
IncrementRetry(ctx context.Context, recordID string) error
|
||||
}
|
||||
|
||||
// NotificationRecordRepository 通知记录仓储接口
|
||||
type NotificationRecordRepository interface {
|
||||
interfaces.Repository[entities.NotificationRecord]
|
||||
// EsignContractSignRecordRepository e签宝签署合同记录仓储接口
|
||||
type EsignContractSignRecordRepository interface {
|
||||
interfaces.Repository[entities.EsignContractSignRecord]
|
||||
|
||||
// 基础查询 - 直接使用实体
|
||||
GetByCertificationID(ctx context.Context, certificationID string) ([]*entities.NotificationRecord, error)
|
||||
GetUnreadByUserID(ctx context.Context, userID string) ([]*entities.NotificationRecord, error)
|
||||
// 基础查询
|
||||
GetByCertificationID(ctx context.Context, certificationID string) (*entities.EsignContractSignRecord, error)
|
||||
GetByUserID(ctx context.Context, userID string) ([]*entities.EsignContractSignRecord, error)
|
||||
GetLatestByCertificationID(ctx context.Context, certificationID string) (*entities.EsignContractSignRecord, error)
|
||||
GetByGenerateRecordID(ctx context.Context, generateRecordID string) (*entities.EsignContractSignRecord, error)
|
||||
|
||||
// 复杂查询 - 使用查询参数
|
||||
ListRecords(ctx context.Context, query *queries.ListNotificationRecordsQuery) ([]*entities.NotificationRecord, int64, error)
|
||||
// 复杂查询
|
||||
ListRecords(ctx context.Context, query *queries.ListEsignContractSignRecordsQuery) ([]*entities.EsignContractSignRecord, int64, error)
|
||||
|
||||
// 批量操作
|
||||
BatchCreate(ctx context.Context, records []entities.NotificationRecord) error
|
||||
MarkAsRead(ctx context.Context, recordIDs []string) error
|
||||
MarkAllAsReadByUser(ctx context.Context, userID string) error
|
||||
// 业务操作
|
||||
UpdateStatus(ctx context.Context, recordID string, status string, reason string) error
|
||||
UpdateSuccessInfo(ctx context.Context, recordID, signedFileURL string) error
|
||||
SetSignURL(ctx context.Context, recordID, signURL string) error
|
||||
IncrementRetry(ctx context.Context, recordID string) error
|
||||
MarkExpiredRecords(ctx context.Context) error
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ type ListCertificationsQuery struct {
|
||||
PageSize int `json:"page_size"`
|
||||
UserID string `json:"user_id"`
|
||||
Status enums.CertificationStatus `json:"status"`
|
||||
AdminID string `json:"admin_id"`
|
||||
StartDate string `json:"start_date"`
|
||||
EndDate string `json:"end_date"`
|
||||
EnterpriseName string `json:"enterprise_name"`
|
||||
@@ -26,47 +25,38 @@ type ListEnterprisesQuery struct {
|
||||
EndDate string `json:"end_date"`
|
||||
}
|
||||
|
||||
// ListFaceVerifyRecordsQuery 人脸识别记录列表查询参数
|
||||
type ListFaceVerifyRecordsQuery struct {
|
||||
// ListEnterpriseInfoSubmitRecordsQuery 企业信息提交记录列表查询参数
|
||||
type ListEnterpriseInfoSubmitRecordsQuery struct {
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"page_size"`
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
Status string `json:"status"`
|
||||
CompanyName string `json:"company_name"`
|
||||
StartDate string `json:"start_date"`
|
||||
EndDate string `json:"end_date"`
|
||||
}
|
||||
|
||||
// ListContractRecordsQuery 合同记录列表查询参数
|
||||
type ListContractRecordsQuery struct {
|
||||
// ListEsignContractGenerateRecordsQuery e签宝生成合同记录列表查询参数
|
||||
type ListEsignContractGenerateRecordsQuery struct {
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"page_size"`
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
Status string `json:"status"`
|
||||
ContractType string `json:"contract_type"`
|
||||
StartDate string `json:"start_date"`
|
||||
EndDate string `json:"end_date"`
|
||||
}
|
||||
|
||||
// ListLicenseUploadRecordsQuery 营业执照上传记录列表查询参数
|
||||
type ListLicenseUploadRecordsQuery struct {
|
||||
// ListEsignContractSignRecordsQuery e签宝签署合同记录列表查询参数
|
||||
type ListEsignContractSignRecordsQuery struct {
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"page_size"`
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
Status string `json:"status"`
|
||||
StartDate string `json:"start_date"`
|
||||
EndDate string `json:"end_date"`
|
||||
}
|
||||
|
||||
// ListNotificationRecordsQuery 通知记录列表查询参数
|
||||
type ListNotificationRecordsQuery struct {
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"page_size"`
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
Type string `json:"type"`
|
||||
IsRead *bool `json:"is_read"`
|
||||
SignerName string `json:"signer_name"`
|
||||
StartDate string `json:"start_date"`
|
||||
EndDate string `json:"end_date"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
cert_entities "tyapi-server/internal/domains/certification/entities"
|
||||
"tyapi-server/internal/domains/certification/repositories"
|
||||
user_entities "tyapi-server/internal/domains/user/entities"
|
||||
"tyapi-server/internal/shared/esign"
|
||||
)
|
||||
|
||||
// CertificationEsignService 负责与e签宝相关的认证业务逻辑
|
||||
type CertificationEsignService struct {
|
||||
certRepo repositories.CertificationRepository
|
||||
esignClient *esign.Client
|
||||
esignContractGenerateRecordRepo repositories.EsignContractGenerateRecordRepository
|
||||
esignContractSignRecordRepo repositories.EsignContractSignRecordRepository
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewCertificationEsignService 创建CertificationEsignService实例
|
||||
func NewCertificationEsignService(
|
||||
certRepo repositories.CertificationRepository,
|
||||
esignClient *esign.Client,
|
||||
esignContractGenerateRecordRepo repositories.EsignContractGenerateRecordRepository,
|
||||
logger *zap.Logger,
|
||||
) *CertificationEsignService {
|
||||
return &CertificationEsignService{
|
||||
certRepo: certRepo,
|
||||
esignClient: esignClient,
|
||||
esignContractGenerateRecordRepo: esignContractGenerateRecordRepo,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// FillTemplate 生成合同文件(e签宝模板填充)
|
||||
func (s *CertificationEsignService) FillTemplate(ctx context.Context, certification *cert_entities.Certification, components map[string]string) (*esign.FillTemplate, error) {
|
||||
resp, err := s.esignClient.FillTemplate(components)
|
||||
record := &cert_entities.EsignContractGenerateRecord{
|
||||
CertificationID: certification.ID,
|
||||
UserID: certification.UserID,
|
||||
}
|
||||
if err != nil {
|
||||
s.logger.Error("生成合同文件失败", zap.Any("components", components), zap.Error(err))
|
||||
record.Status = "failed"
|
||||
} else {
|
||||
record.TemplateID = resp.TemplateID
|
||||
record.ContractName = resp.FileName
|
||||
record.ContractFileID = resp.FileID
|
||||
record.ContractURL = resp.FileDownloadUrl
|
||||
record.Status = "success"
|
||||
record.FillTime = &resp.FillTime
|
||||
}
|
||||
if _, createErr := s.esignContractGenerateRecordRepo.Create(ctx, *record); createErr != nil {
|
||||
s.logger.Error("创建合同生成记录失败", zap.Error(createErr))
|
||||
if err == nil {
|
||||
return nil, fmt.Errorf("创建合同生成记录失败: %w", createErr)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("生成合同文件失败: %w", err)
|
||||
}
|
||||
|
||||
certification.ContractURL = resp.FileDownloadUrl
|
||||
certification.ContractFileID = resp.FileID
|
||||
err = s.certRepo.Update(ctx, *certification)
|
||||
if err != nil {
|
||||
s.logger.Error("更新认证申请失败", zap.Error(err))
|
||||
return nil, fmt.Errorf("更新认证申请失败: %w", err)
|
||||
}
|
||||
s.logger.Info("生成合同文件成功", zap.String("template_id", resp.TemplateID), zap.String("file_id", resp.FileID))
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// 发起签署
|
||||
func (s *CertificationEsignService) InitiateSign(ctx context.Context, certification *cert_entities.Certification, enterpriseInfo *user_entities.EnterpriseInfo) (*cert_entities.EsignContractSignRecord, error) {
|
||||
|
||||
// 发起签署流程
|
||||
flowID, err := s.esignClient.CreateSignFlow(&esign.CreateSignFlowRequest{
|
||||
FileID: certification.ContractFileID,
|
||||
SignerAccount: enterpriseInfo.UnifiedSocialCode,
|
||||
SignerName: enterpriseInfo.CompanyName,
|
||||
TransactorPhone: enterpriseInfo.LegalPersonPhone,
|
||||
TransactorName: enterpriseInfo.LegalPersonName,
|
||||
TransactorIDCardNum: enterpriseInfo.LegalPersonID,
|
||||
})
|
||||
if err != nil {
|
||||
s.logger.Error("获取签署链接失败",
|
||||
zap.String("user_id", enterpriseInfo.UserID),
|
||||
zap.Error(err),
|
||||
)
|
||||
return nil, fmt.Errorf("获取签署链接失败: %w", err)
|
||||
}
|
||||
signURL, shortURL, err := s.esignClient.GetSignURL(flowID, enterpriseInfo.UnifiedSocialCode, enterpriseInfo.CompanyName)
|
||||
if err != nil {
|
||||
s.logger.Error("获取签署链接失败", zap.Error(err))
|
||||
return nil, fmt.Errorf("获取签署链接失败: %w", err)
|
||||
}
|
||||
esignContractSignRecord := cert_entities.NewEsignContractSignRecord(
|
||||
certification.ID,
|
||||
enterpriseInfo.UserID,
|
||||
flowID,
|
||||
certification.ContractFileID,
|
||||
enterpriseInfo.UnifiedSocialCode,
|
||||
enterpriseInfo.LegalPersonPhone,
|
||||
enterpriseInfo.LegalPersonID,
|
||||
signURL,
|
||||
shortURL,
|
||||
)
|
||||
signContractSignRecord, err := s.esignContractSignRecordRepo.Create(ctx, *esignContractSignRecord)
|
||||
if err != nil {
|
||||
s.logger.Error("创建签署记录失败", zap.Error(err))
|
||||
return nil, fmt.Errorf("创建签署记录失败: %w", err)
|
||||
}
|
||||
certification.EsignFlowID = signContractSignRecord.EsignFlowID
|
||||
certification.ContractSignURL = signContractSignRecord.SignShortURL // 记录的是短链接
|
||||
err = s.certRepo.Update(ctx, *certification)
|
||||
if err != nil {
|
||||
s.logger.Error("更新认证申请失败", zap.Error(err))
|
||||
return nil, fmt.Errorf("更新认证申请失败: %w", err)
|
||||
}
|
||||
return &signContractSignRecord, nil
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"tyapi-server/internal/domains/certification/entities"
|
||||
"tyapi-server/internal/domains/certification/enums"
|
||||
"tyapi-server/internal/domains/certification/repositories"
|
||||
esign_service "tyapi-server/internal/shared/esign"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// CertificationManagementService 认证管理领域服务
|
||||
// 负责认证申请的生命周期管理,包括创建、状态转换、进度查询等
|
||||
type CertificationManagementService struct {
|
||||
certRepo repositories.CertificationRepository
|
||||
esignService *esign_service.Client
|
||||
stateMachine *CertificationStateMachine
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewCertificationManagementService 创建认证管理领域服务
|
||||
func NewCertificationManagementService(
|
||||
certRepo repositories.CertificationRepository,
|
||||
esignService *esign_service.Client,
|
||||
stateMachine *CertificationStateMachine,
|
||||
logger *zap.Logger,
|
||||
) *CertificationManagementService {
|
||||
return &CertificationManagementService{
|
||||
certRepo: certRepo,
|
||||
esignService: esignService,
|
||||
stateMachine: stateMachine,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateCertification 创建认证申请
|
||||
func (s *CertificationManagementService) 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
|
||||
}
|
||||
|
||||
// GetCertificationByUserID 根据用户ID获取认证申请
|
||||
func (s *CertificationManagementService) GetCertificationByUserID(ctx context.Context, userID string) (*entities.Certification, error) {
|
||||
return s.certRepo.GetByUserID(ctx, userID)
|
||||
}
|
||||
|
||||
// GetCertificationByID 根据ID获取认证申请
|
||||
func (s *CertificationManagementService) 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
|
||||
}
|
||||
|
||||
// GetCertificationByAuthFlowID 根据认证流程ID获取认证申请
|
||||
func (s *CertificationManagementService) GetCertificationByAuthFlowID(ctx context.Context, authFlowID string) (*entities.Certification, error) {
|
||||
cert, err := s.certRepo.GetByAuthFlowID(ctx, authFlowID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &cert, nil
|
||||
}
|
||||
|
||||
// 根据签署流程ID获取认证申请
|
||||
func (s *CertificationManagementService) GetCertificationByEsignFlowID(ctx context.Context, esignFlowID string) (*entities.Certification, error) {
|
||||
cert, err := s.certRepo.GetByEsignFlowID(ctx, esignFlowID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &cert, nil
|
||||
}
|
||||
// GetCertificationProgress 获取认证进度信息
|
||||
func (s *CertificationManagementService) 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(),
|
||||
"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.EnterpriseVerifiedAt != nil {
|
||||
progress["enterprise_verified_at"] = cert.EnterpriseVerifiedAt
|
||||
}
|
||||
if cert.ContractAppliedAt != nil {
|
||||
progress["contract_applied_at"] = cert.ContractAppliedAt
|
||||
}
|
||||
if cert.ContractSignedAt != nil {
|
||||
progress["contract_signed_at"] = cert.ContractSignedAt
|
||||
}
|
||||
if cert.CompletedAt != nil {
|
||||
progress["completed_at"] = cert.CompletedAt
|
||||
}
|
||||
|
||||
return progress, nil
|
||||
}
|
||||
|
||||
// 通过e签宝检查是否有过认证
|
||||
func (s *CertificationManagementService) CheckCertification(ctx context.Context, companyName string, unifiedSocialCode string) (bool, error) {
|
||||
// 查询企业是否已经过认证
|
||||
queryOrgIdentityInfo := &esign_service.QueryOrgIdentityRequest{
|
||||
OrgName: companyName,
|
||||
OrgIDCardNum: unifiedSocialCode,
|
||||
OrgIDCardType: esign_service.OrgIDCardTypeUSCC,
|
||||
}
|
||||
queryOrgIdentityResponse, err := s.esignService.QueryOrgIdentityInfo(queryOrgIdentityInfo)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("查询机构认证信息失败: %w", err)
|
||||
}
|
||||
if queryOrgIdentityResponse.Data.RealnameStatus == 1 {
|
||||
s.logger.Info("该企业已进行过认证", zap.String("company_name", companyName), zap.String("unified_social_code", unifiedSocialCode))
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
@@ -1,774 +0,0 @@
|
||||
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"
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"tyapi-server/internal/domains/certification/enums"
|
||||
"tyapi-server/internal/domains/certification/repositories"
|
||||
)
|
||||
|
||||
// CertificationWorkflowService 认证工作流领域服务
|
||||
// 负责认证流程的状态转换和业务逻辑处理
|
||||
type CertificationWorkflowService struct {
|
||||
certRepo repositories.CertificationRepository
|
||||
stateMachine *CertificationStateMachine
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewCertificationWorkflowService 创建认证工作流领域服务
|
||||
func NewCertificationWorkflowService(
|
||||
certRepo repositories.CertificationRepository,
|
||||
stateMachine *CertificationStateMachine,
|
||||
logger *zap.Logger,
|
||||
) *CertificationWorkflowService {
|
||||
return &CertificationWorkflowService{
|
||||
certRepo: certRepo,
|
||||
stateMachine: stateMachine,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// SubmitEnterpriseInfo 提交企业信息
|
||||
func (s *CertificationWorkflowService) 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 && cert.Status != enums.StatusInfoSubmitted {
|
||||
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
|
||||
}
|
||||
|
||||
// CompleteEnterpriseVerification 完成企业认证
|
||||
func (s *CertificationWorkflowService) CompleteEnterpriseVerification(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.StatusInfoSubmitted {
|
||||
return fmt.Errorf("当前状态不允许完成企业认证")
|
||||
}
|
||||
|
||||
// 使用状态机转换状态
|
||||
if err := s.stateMachine.TransitionTo(ctx, certificationID, enums.StatusEnterpriseVerified, 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
|
||||
}
|
||||
|
||||
// ApplyContract 申请签署合同
|
||||
func (s *CertificationWorkflowService) 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.StatusEnterpriseVerified {
|
||||
return fmt.Errorf("当前状态不允许申请签署合同")
|
||||
}
|
||||
|
||||
// 使用状态机转换状态
|
||||
if err := s.stateMachine.TransitionTo(ctx, certificationID, enums.StatusContractApplied, 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
|
||||
}
|
||||
|
||||
// CompleteContractSign 完成合同签署
|
||||
func (s *CertificationWorkflowService) 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.StatusContractApplied {
|
||||
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),
|
||||
zap.String("user_id", cert.UserID),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CompleteCertification 完成认证
|
||||
func (s *CertificationWorkflowService) 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
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"tyapi-server/internal/domains/certification/entities"
|
||||
"tyapi-server/internal/domains/certification/repositories"
|
||||
)
|
||||
|
||||
// EnterpriseInfoSubmitRecordService 企业信息提交记录领域服务
|
||||
// 负责企业信息提交记录的业务逻辑处理
|
||||
type EnterpriseInfoSubmitRecordService struct {
|
||||
enterpriseRecordRepo repositories.EnterpriseInfoSubmitRecordRepository
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewEnterpriseInfoSubmitRecordService 创建企业信息提交记录领域服务
|
||||
func NewEnterpriseInfoSubmitRecordService(
|
||||
enterpriseRecordRepo repositories.EnterpriseInfoSubmitRecordRepository,
|
||||
logger *zap.Logger,
|
||||
) *EnterpriseInfoSubmitRecordService {
|
||||
return &EnterpriseInfoSubmitRecordService{
|
||||
enterpriseRecordRepo: enterpriseRecordRepo,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateEnterpriseInfoSubmitRecord 创建企业信息提交记录
|
||||
func (s *EnterpriseInfoSubmitRecordService) CreateEnterpriseInfoSubmitRecord(
|
||||
ctx context.Context,
|
||||
userID string,
|
||||
companyName string,
|
||||
unifiedSocialCode string,
|
||||
legalPersonName string,
|
||||
legalPersonID string,
|
||||
legalPersonPhone string,
|
||||
) (*entities.EnterpriseInfoSubmitRecord, error) {
|
||||
// 创建企业信息提交记录实体
|
||||
record := entities.NewEnterpriseInfoSubmitRecord(
|
||||
userID,
|
||||
companyName,
|
||||
unifiedSocialCode,
|
||||
legalPersonName,
|
||||
legalPersonID,
|
||||
legalPersonPhone,
|
||||
)
|
||||
|
||||
// 保存到仓储
|
||||
createdRecord, err := s.enterpriseRecordRepo.Create(ctx, *record)
|
||||
if err != nil {
|
||||
s.logger.Error("创建企业信息提交记录失败",
|
||||
zap.String("user_id", userID),
|
||||
zap.Error(err))
|
||||
return nil, fmt.Errorf("创建企业信息提交记录失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("企业信息提交记录创建成功",
|
||||
zap.String("record_id", createdRecord.ID),
|
||||
zap.String("user_id", userID),
|
||||
zap.String("company_name", companyName))
|
||||
|
||||
return &createdRecord, nil
|
||||
}
|
||||
|
||||
// GetLatestByUserID 根据用户ID获取最新的企业信息提交记录
|
||||
func (s *EnterpriseInfoSubmitRecordService) GetLatestByUserID(ctx context.Context, userID string) (*entities.EnterpriseInfoSubmitRecord, error) {
|
||||
record, err := s.enterpriseRecordRepo.GetLatestByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
s.logger.Error("获取企业信息提交记录失败",
|
||||
zap.String("user_id", userID),
|
||||
zap.Error(err))
|
||||
return nil, fmt.Errorf("获取企业信息提交记录失败: %w", err)
|
||||
}
|
||||
|
||||
return record, nil
|
||||
}
|
||||
|
||||
// UpdateEnterpriseInfoSubmitRecord 更新企业信息提交记录
|
||||
func (s *EnterpriseInfoSubmitRecordService) UpdateEnterpriseInfoSubmitRecord(ctx context.Context, record *entities.EnterpriseInfoSubmitRecord) error {
|
||||
err := s.enterpriseRecordRepo.Update(ctx, *record)
|
||||
if err != nil {
|
||||
s.logger.Error("更新企业信息提交记录失败",
|
||||
zap.String("record_id", record.ID),
|
||||
zap.Error(err))
|
||||
return fmt.Errorf("更新企业信息提交记录失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("企业信息提交记录更新成功",
|
||||
zap.String("record_id", record.ID),
|
||||
zap.String("status", record.Status))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarkAsVerified 标记企业信息为已验证
|
||||
func (s *EnterpriseInfoSubmitRecordService) MarkAsVerified(ctx context.Context, recordID string) error {
|
||||
record, err := s.enterpriseRecordRepo.GetByID(ctx, recordID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取企业信息提交记录失败: %w", err)
|
||||
}
|
||||
|
||||
record.MarkAsVerified()
|
||||
err = s.enterpriseRecordRepo.Update(ctx, record)
|
||||
if err != nil {
|
||||
s.logger.Error("标记企业信息为已验证失败",
|
||||
zap.String("record_id", recordID),
|
||||
zap.Error(err))
|
||||
return fmt.Errorf("标记企业信息为已验证失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("企业信息标记为已验证成功",
|
||||
zap.String("record_id", recordID))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateVerificationStatus 更新企业信息验证状态
|
||||
func (s *EnterpriseInfoSubmitRecordService) UpdateVerificationStatus(ctx context.Context, userID string, isVerified bool, reason string) error {
|
||||
// 获取用户最新的企业信息提交记录
|
||||
record, err := s.enterpriseRecordRepo.GetLatestByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取企业信息提交记录失败: %w", err)
|
||||
}
|
||||
|
||||
// 更新验证状态
|
||||
if isVerified {
|
||||
record.MarkAsVerified()
|
||||
} else {
|
||||
record.MarkAsFailed(reason)
|
||||
}
|
||||
|
||||
// 保存更新
|
||||
err = s.enterpriseRecordRepo.Update(ctx, *record)
|
||||
if err != nil {
|
||||
s.logger.Error("更新企业信息验证状态失败",
|
||||
zap.String("user_id", userID),
|
||||
zap.Bool("is_verified", isVerified),
|
||||
zap.Error(err))
|
||||
return fmt.Errorf("更新企业信息验证状态失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("企业信息验证状态更新成功",
|
||||
zap.String("user_id", userID),
|
||||
zap.Bool("is_verified", isVerified),
|
||||
zap.String("reason", reason))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteEnterpriseInfoSubmitRecord 删除企业信息提交记录
|
||||
func (s *EnterpriseInfoSubmitRecordService) DeleteEnterpriseInfoSubmitRecord(ctx context.Context, recordID string) error {
|
||||
err := s.enterpriseRecordRepo.Delete(ctx, recordID)
|
||||
if err != nil {
|
||||
s.logger.Error("删除企业信息提交记录失败",
|
||||
zap.String("record_id", recordID),
|
||||
zap.Error(err))
|
||||
return fmt.Errorf("删除企业信息提交记录失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("企业信息提交记录删除成功",
|
||||
zap.String("record_id", recordID))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetByUserID 根据用户ID获取企业信息提交记录列表
|
||||
func (s *EnterpriseInfoSubmitRecordService) GetByUserID(ctx context.Context, userID string) ([]*entities.EnterpriseInfoSubmitRecord, error) {
|
||||
records, err := s.enterpriseRecordRepo.GetByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
s.logger.Error("获取用户企业信息提交记录失败",
|
||||
zap.String("user_id", userID),
|
||||
zap.Error(err))
|
||||
return nil, fmt.Errorf("获取用户企业信息提交记录失败: %w", err)
|
||||
}
|
||||
|
||||
return records, nil
|
||||
}
|
||||
256
internal/domains/certification/services/state_config.go
Normal file
256
internal/domains/certification/services/state_config.go
Normal file
@@ -0,0 +1,256 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"tyapi-server/internal/domains/certification/enums"
|
||||
)
|
||||
|
||||
// StateConfig 状态配置
|
||||
type StateConfig struct {
|
||||
Status enums.CertificationStatus `json:"status"`
|
||||
Name string `json:"name"`
|
||||
ProgressPercentage int `json:"progress_percentage"`
|
||||
IsUserActionRequired bool `json:"is_user_action_required"`
|
||||
IsAdminActionRequired bool `json:"is_admin_action_required"`
|
||||
TimestampField string `json:"timestamp_field,omitempty"`
|
||||
Description string `json:"description"`
|
||||
NextValidStatuses []enums.CertificationStatus `json:"next_valid_statuses"`
|
||||
}
|
||||
|
||||
// TransitionConfig 状态转换配置
|
||||
type TransitionConfig struct {
|
||||
From enums.CertificationStatus `json:"from"`
|
||||
To enums.CertificationStatus `json:"to"`
|
||||
Action string `json:"action"`
|
||||
ActionName string `json:"action_name"`
|
||||
AllowUser bool `json:"allow_user"`
|
||||
AllowAdmin bool `json:"allow_admin"`
|
||||
RequiresValidation bool `json:"requires_validation"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// CertificationStateManager 认证状态管理器
|
||||
type CertificationStateManager struct {
|
||||
stateMap map[enums.CertificationStatus]*StateConfig
|
||||
transitionMap map[enums.CertificationStatus][]*TransitionConfig
|
||||
}
|
||||
|
||||
// NewCertificationStateManager 创建认证状态管理器
|
||||
func NewCertificationStateManager() *CertificationStateManager {
|
||||
manager := &CertificationStateManager{
|
||||
stateMap: make(map[enums.CertificationStatus]*StateConfig),
|
||||
transitionMap: make(map[enums.CertificationStatus][]*TransitionConfig),
|
||||
}
|
||||
|
||||
// 初始化状态配置
|
||||
manager.initStateConfigs()
|
||||
return manager
|
||||
}
|
||||
|
||||
// initStateConfigs 初始化状态配置
|
||||
func (manager *CertificationStateManager) initStateConfigs() {
|
||||
// 状态配置
|
||||
states := []*StateConfig{
|
||||
{
|
||||
Status: enums.StatusPending,
|
||||
Name: "待认证",
|
||||
ProgressPercentage: 0,
|
||||
IsUserActionRequired: true,
|
||||
IsAdminActionRequired: false,
|
||||
Description: "等待用户提交企业信息",
|
||||
NextValidStatuses: []enums.CertificationStatus{enums.StatusInfoSubmitted},
|
||||
},
|
||||
{
|
||||
Status: enums.StatusInfoSubmitted,
|
||||
Name: "已提交企业信息",
|
||||
ProgressPercentage: 20,
|
||||
IsUserActionRequired: true,
|
||||
IsAdminActionRequired: false,
|
||||
TimestampField: "InfoSubmittedAt",
|
||||
Description: "用户已提交企业信息",
|
||||
NextValidStatuses: []enums.CertificationStatus{enums.StatusEnterpriseVerified, enums.StatusInfoSubmitted}, // 可以重新提交
|
||||
},
|
||||
{
|
||||
Status: enums.StatusEnterpriseVerified,
|
||||
Name: "已企业认证",
|
||||
ProgressPercentage: 40,
|
||||
IsUserActionRequired: true,
|
||||
IsAdminActionRequired: false,
|
||||
TimestampField: "EnterpriseVerifiedAt",
|
||||
Description: "企业认证已完成",
|
||||
NextValidStatuses: []enums.CertificationStatus{enums.StatusContractApplied},
|
||||
},
|
||||
{
|
||||
Status: enums.StatusContractApplied,
|
||||
Name: "已申请合同",
|
||||
ProgressPercentage: 60,
|
||||
IsUserActionRequired: true,
|
||||
IsAdminActionRequired: false,
|
||||
TimestampField: "ContractAppliedAt",
|
||||
Description: "合同已申请",
|
||||
NextValidStatuses: []enums.CertificationStatus{enums.StatusContractSigned},
|
||||
},
|
||||
{
|
||||
Status: enums.StatusContractSigned,
|
||||
Name: "已签署合同",
|
||||
ProgressPercentage: 80,
|
||||
IsUserActionRequired: false,
|
||||
IsAdminActionRequired: false,
|
||||
TimestampField: "ContractSignedAt",
|
||||
Description: "合同已签署",
|
||||
NextValidStatuses: []enums.CertificationStatus{enums.StatusCompleted},
|
||||
},
|
||||
{
|
||||
Status: enums.StatusCompleted,
|
||||
Name: "认证完成",
|
||||
ProgressPercentage: 100,
|
||||
IsUserActionRequired: false,
|
||||
IsAdminActionRequired: false,
|
||||
TimestampField: "CompletedAt",
|
||||
Description: "认证流程已完成",
|
||||
NextValidStatuses: []enums.CertificationStatus{},
|
||||
},
|
||||
}
|
||||
|
||||
// 转换配置
|
||||
transitions := []*TransitionConfig{
|
||||
// 提交企业信息
|
||||
{
|
||||
From: enums.StatusPending,
|
||||
To: enums.StatusInfoSubmitted,
|
||||
Action: "submit_info",
|
||||
ActionName: "提交企业信息",
|
||||
AllowUser: true,
|
||||
AllowAdmin: false,
|
||||
RequiresValidation: true,
|
||||
Description: "用户提交企业信息",
|
||||
},
|
||||
// 重新提交企业信息
|
||||
{
|
||||
From: enums.StatusInfoSubmitted,
|
||||
To: enums.StatusInfoSubmitted,
|
||||
Action: "resubmit_info",
|
||||
ActionName: "重新提交企业信息",
|
||||
AllowUser: true,
|
||||
AllowAdmin: false,
|
||||
RequiresValidation: true,
|
||||
Description: "用户重新提交企业信息",
|
||||
},
|
||||
// 企业认证
|
||||
{
|
||||
From: enums.StatusInfoSubmitted,
|
||||
To: enums.StatusEnterpriseVerified,
|
||||
Action: "enterprise_verify",
|
||||
ActionName: "企业认证",
|
||||
AllowUser: true,
|
||||
AllowAdmin: false,
|
||||
RequiresValidation: true,
|
||||
Description: "用户完成企业认证",
|
||||
},
|
||||
// 申请合同
|
||||
{
|
||||
From: enums.StatusEnterpriseVerified,
|
||||
To: enums.StatusContractApplied,
|
||||
Action: "apply_contract",
|
||||
ActionName: "申请合同",
|
||||
AllowUser: true,
|
||||
AllowAdmin: false,
|
||||
RequiresValidation: false,
|
||||
Description: "用户申请合同",
|
||||
},
|
||||
// 签署合同
|
||||
{
|
||||
From: enums.StatusContractApplied,
|
||||
To: enums.StatusContractSigned,
|
||||
Action: "sign_contract",
|
||||
ActionName: "签署合同",
|
||||
AllowUser: true,
|
||||
AllowAdmin: false,
|
||||
RequiresValidation: true,
|
||||
Description: "用户签署合同",
|
||||
},
|
||||
// 完成认证
|
||||
{
|
||||
From: enums.StatusContractSigned,
|
||||
To: enums.StatusCompleted,
|
||||
Action: "complete",
|
||||
ActionName: "完成认证",
|
||||
AllowUser: false,
|
||||
AllowAdmin: false,
|
||||
RequiresValidation: false,
|
||||
Description: "系统自动完成认证",
|
||||
},
|
||||
}
|
||||
|
||||
// 构建映射
|
||||
for _, state := range states {
|
||||
manager.stateMap[state.Status] = state
|
||||
}
|
||||
|
||||
for _, transition := range transitions {
|
||||
manager.transitionMap[transition.From] = append(manager.transitionMap[transition.From], transition)
|
||||
}
|
||||
}
|
||||
|
||||
// GetStateConfig 获取状态配置
|
||||
func (manager *CertificationStateManager) GetStateConfig(status enums.CertificationStatus) *StateConfig {
|
||||
return manager.stateMap[status]
|
||||
}
|
||||
|
||||
// GetTransitionConfigs 获取状态转换配置
|
||||
func (manager *CertificationStateManager) GetTransitionConfigs(from enums.CertificationStatus) []*TransitionConfig {
|
||||
return manager.transitionMap[from]
|
||||
}
|
||||
|
||||
// CanTransition 检查是否可以转换
|
||||
func (manager *CertificationStateManager) CanTransition(from enums.CertificationStatus, to enums.CertificationStatus, isUser bool, isAdmin bool) (bool, string) {
|
||||
transitions := manager.GetTransitionConfigs(from)
|
||||
|
||||
for _, transition := range transitions {
|
||||
if transition.To == to {
|
||||
if isUser && !transition.AllowUser {
|
||||
return false, "用户不允许执行此操作"
|
||||
}
|
||||
if isAdmin && !transition.AllowAdmin {
|
||||
return false, "管理员不允许执行此操作"
|
||||
}
|
||||
if !isUser && !isAdmin && (transition.AllowUser || transition.AllowAdmin) {
|
||||
return false, "此操作需要用户或管理员权限"
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
}
|
||||
|
||||
return false, "不支持的状态转换"
|
||||
}
|
||||
|
||||
// GetProgressPercentage 获取进度百分比
|
||||
func (manager *CertificationStateManager) GetProgressPercentage(status enums.CertificationStatus) int {
|
||||
if stateConfig := manager.GetStateConfig(status); stateConfig != nil {
|
||||
return stateConfig.ProgressPercentage
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// IsUserActionRequired 检查是否需要用户操作
|
||||
func (manager *CertificationStateManager) IsUserActionRequired(status enums.CertificationStatus) bool {
|
||||
if stateConfig := manager.GetStateConfig(status); stateConfig != nil {
|
||||
return stateConfig.IsUserActionRequired
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsAdminActionRequired 检查是否需要管理员操作
|
||||
func (manager *CertificationStateManager) IsAdminActionRequired(status enums.CertificationStatus) bool {
|
||||
if stateConfig := manager.GetStateConfig(status); stateConfig != nil {
|
||||
return stateConfig.IsAdminActionRequired
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetNextValidStatuses 获取下一个有效状态
|
||||
func (manager *CertificationStateManager) GetNextValidStatuses(status enums.CertificationStatus) []enums.CertificationStatus {
|
||||
if stateConfig := manager.GetStateConfig(status); stateConfig != nil {
|
||||
return stateConfig.NextValidStatuses
|
||||
}
|
||||
return []enums.CertificationStatus{}
|
||||
}
|
||||
@@ -12,21 +12,11 @@ import (
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// StateTransition 状态转换规则
|
||||
type StateTransition struct {
|
||||
From enums.CertificationStatus
|
||||
To enums.CertificationStatus
|
||||
Action string
|
||||
AllowUser bool // 是否允许用户操作
|
||||
AllowAdmin bool // 是否允许管理员操作
|
||||
RequiresValidation bool // 是否需要额外验证
|
||||
}
|
||||
|
||||
// CertificationStateMachine 认证状态机
|
||||
type CertificationStateMachine struct {
|
||||
transitions map[enums.CertificationStatus][]StateTransition
|
||||
certRepo repositories.CertificationRepository
|
||||
logger *zap.Logger
|
||||
stateManager *CertificationStateManager
|
||||
certRepo repositories.CertificationRepository
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewCertificationStateMachine 创建认证状态机
|
||||
@@ -34,41 +24,10 @@ func NewCertificationStateMachine(
|
||||
certRepo repositories.CertificationRepository,
|
||||
logger *zap.Logger,
|
||||
) *CertificationStateMachine {
|
||||
sm := &CertificationStateMachine{
|
||||
transitions: make(map[enums.CertificationStatus][]StateTransition),
|
||||
certRepo: certRepo,
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
// 初始化状态转换规则
|
||||
sm.initializeTransitions()
|
||||
return sm
|
||||
}
|
||||
|
||||
// initializeTransitions 初始化状态转换规则
|
||||
func (sm *CertificationStateMachine) initializeTransitions() {
|
||||
transitions := []StateTransition{
|
||||
// 正常流程转换
|
||||
{enums.StatusPending, enums.StatusInfoSubmitted, "submit_info", true, false, true},
|
||||
{enums.StatusInfoSubmitted, enums.StatusFaceVerified, "face_verify", true, false, true},
|
||||
{enums.StatusFaceVerified, enums.StatusContractApplied, "apply_contract", true, false, false},
|
||||
{enums.StatusContractApplied, enums.StatusContractPending, "system_process", false, false, false},
|
||||
{enums.StatusContractPending, enums.StatusContractApproved, "admin_approve", false, true, true},
|
||||
{enums.StatusContractApproved, enums.StatusContractSigned, "user_sign", true, false, true},
|
||||
{enums.StatusContractSigned, enums.StatusCompleted, "system_complete", false, false, false},
|
||||
|
||||
// 失败和重试转换
|
||||
{enums.StatusInfoSubmitted, enums.StatusFaceFailed, "face_fail", false, false, false},
|
||||
{enums.StatusFaceFailed, enums.StatusFaceVerified, "retry_face", true, false, true},
|
||||
{enums.StatusContractPending, enums.StatusRejected, "admin_reject", false, true, true},
|
||||
{enums.StatusRejected, enums.StatusInfoSubmitted, "restart_process", true, false, false},
|
||||
{enums.StatusContractApproved, enums.StatusSignFailed, "sign_fail", false, false, false},
|
||||
{enums.StatusSignFailed, enums.StatusContractSigned, "retry_sign", true, false, true},
|
||||
}
|
||||
|
||||
// 构建状态转换映射
|
||||
for _, transition := range transitions {
|
||||
sm.transitions[transition.From] = append(sm.transitions[transition.From], transition)
|
||||
return &CertificationStateMachine{
|
||||
stateManager: NewCertificationStateManager(),
|
||||
certRepo: certRepo,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,27 +38,7 @@ func (sm *CertificationStateMachine) CanTransition(
|
||||
isUser bool,
|
||||
isAdmin bool,
|
||||
) (bool, string) {
|
||||
validTransitions, exists := sm.transitions[from]
|
||||
if !exists {
|
||||
return false, "当前状态不支持任何转换"
|
||||
}
|
||||
|
||||
for _, transition := range validTransitions {
|
||||
if transition.To == to {
|
||||
if isUser && !transition.AllowUser {
|
||||
return false, "用户不允许执行此操作"
|
||||
}
|
||||
if isAdmin && !transition.AllowAdmin {
|
||||
return false, "管理员不允许执行此操作"
|
||||
}
|
||||
if !isUser && !isAdmin && (transition.AllowUser || transition.AllowAdmin) {
|
||||
return false, "此操作需要用户或管理员权限"
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
}
|
||||
|
||||
return false, "不支持的状态转换"
|
||||
return sm.stateManager.CanTransition(from, to, isUser, isAdmin)
|
||||
}
|
||||
|
||||
// TransitionTo 执行状态转换
|
||||
@@ -123,11 +62,6 @@ func (sm *CertificationStateMachine) TransitionTo(
|
||||
return fmt.Errorf("状态转换失败: %s", reason)
|
||||
}
|
||||
|
||||
// 执行状态转换前的验证
|
||||
if err := sm.validateTransition(ctx, &cert, targetStatus, metadata); err != nil {
|
||||
return fmt.Errorf("状态转换验证失败: %w", err)
|
||||
}
|
||||
|
||||
// 更新状态和时间戳
|
||||
oldStatus := cert.Status
|
||||
cert.Status = targetStatus
|
||||
@@ -154,20 +88,23 @@ func (sm *CertificationStateMachine) TransitionTo(
|
||||
|
||||
// updateTimestamp 更新对应的时间戳字段
|
||||
func (sm *CertificationStateMachine) updateTimestamp(cert *entities.Certification, status enums.CertificationStatus) {
|
||||
stateConfig := sm.stateManager.GetStateConfig(status)
|
||||
if stateConfig == nil || stateConfig.TimestampField == "" {
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
switch status {
|
||||
case enums.StatusInfoSubmitted:
|
||||
switch stateConfig.TimestampField {
|
||||
case "InfoSubmittedAt":
|
||||
cert.InfoSubmittedAt = &now
|
||||
case enums.StatusFaceVerified:
|
||||
cert.FaceVerifiedAt = &now
|
||||
case enums.StatusContractApplied:
|
||||
case "EnterpriseVerifiedAt":
|
||||
cert.EnterpriseVerifiedAt = &now
|
||||
case "ContractAppliedAt":
|
||||
cert.ContractAppliedAt = &now
|
||||
case enums.StatusContractApproved:
|
||||
cert.ContractApprovedAt = &now
|
||||
case enums.StatusContractSigned:
|
||||
case "ContractSignedAt":
|
||||
cert.ContractSignedAt = &now
|
||||
case enums.StatusCompleted:
|
||||
case "CompletedAt":
|
||||
cert.CompletedAt = &now
|
||||
}
|
||||
}
|
||||
@@ -179,25 +116,6 @@ func (sm *CertificationStateMachine) updateCertificationFields(
|
||||
metadata map[string]interface{},
|
||||
) {
|
||||
switch status {
|
||||
case enums.StatusContractApproved:
|
||||
if adminID, ok := metadata["admin_id"].(string); ok {
|
||||
cert.AdminID = &adminID
|
||||
}
|
||||
if approvalNotes, ok := metadata["approval_notes"].(string); ok {
|
||||
cert.ApprovalNotes = approvalNotes
|
||||
}
|
||||
if signingURL, ok := metadata["signing_url"].(string); ok {
|
||||
cert.SigningURL = signingURL
|
||||
}
|
||||
|
||||
case enums.StatusRejected:
|
||||
if adminID, ok := metadata["admin_id"].(string); ok {
|
||||
cert.AdminID = &adminID
|
||||
}
|
||||
if rejectReason, ok := metadata["reject_reason"].(string); ok {
|
||||
cert.RejectReason = rejectReason
|
||||
}
|
||||
|
||||
case enums.StatusContractSigned:
|
||||
if contractURL, ok := metadata["contract_url"].(string); ok {
|
||||
cert.ContractURL = contractURL
|
||||
@@ -205,66 +123,13 @@ func (sm *CertificationStateMachine) updateCertificationFields(
|
||||
}
|
||||
}
|
||||
|
||||
// validateTransition 验证状态转换的有效性
|
||||
func (sm *CertificationStateMachine) validateTransition(
|
||||
ctx context.Context,
|
||||
cert *entities.Certification,
|
||||
targetStatus enums.CertificationStatus,
|
||||
metadata map[string]interface{},
|
||||
) error {
|
||||
switch targetStatus {
|
||||
case enums.StatusInfoSubmitted:
|
||||
// 验证企业信息是否完整
|
||||
// 这里应该检查用户是否有企业信息,通过用户域的企业服务验证
|
||||
// 暂时跳过验证,由应用服务层协调
|
||||
break
|
||||
|
||||
case enums.StatusFaceVerified:
|
||||
// 验证人脸识别是否成功
|
||||
// 这里可以添加人脸识别结果的验证逻辑
|
||||
|
||||
case enums.StatusContractApproved:
|
||||
// 验证管理员审核信息
|
||||
if metadata["signing_url"] == nil || metadata["signing_url"].(string) == "" {
|
||||
return fmt.Errorf("缺少合同签署链接")
|
||||
}
|
||||
|
||||
case enums.StatusRejected:
|
||||
// 验证拒绝原因
|
||||
if metadata["reject_reason"] == nil || metadata["reject_reason"].(string) == "" {
|
||||
return fmt.Errorf("缺少拒绝原因")
|
||||
}
|
||||
|
||||
case enums.StatusContractSigned:
|
||||
// 验证合同签署信息
|
||||
if cert.SigningURL == "" {
|
||||
return fmt.Errorf("缺少合同签署链接")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetValidNextStatuses 获取当前状态可以转换到的下一个状态列表
|
||||
func (sm *CertificationStateMachine) GetValidNextStatuses(
|
||||
currentStatus enums.CertificationStatus,
|
||||
isUser bool,
|
||||
isAdmin bool,
|
||||
) []enums.CertificationStatus {
|
||||
var validStatuses []enums.CertificationStatus
|
||||
|
||||
transitions, exists := sm.transitions[currentStatus]
|
||||
if !exists {
|
||||
return validStatuses
|
||||
}
|
||||
|
||||
for _, transition := range transitions {
|
||||
if (isUser && transition.AllowUser) || (isAdmin && transition.AllowAdmin) {
|
||||
validStatuses = append(validStatuses, transition.To)
|
||||
}
|
||||
}
|
||||
|
||||
return validStatuses
|
||||
return sm.stateManager.GetNextValidStatuses(currentStatus)
|
||||
}
|
||||
|
||||
// GetTransitionAction 获取状态转换对应的操作名称
|
||||
@@ -272,20 +137,49 @@ func (sm *CertificationStateMachine) GetTransitionAction(
|
||||
from enums.CertificationStatus,
|
||||
to enums.CertificationStatus,
|
||||
) string {
|
||||
transitions, exists := sm.transitions[from]
|
||||
if !exists {
|
||||
return ""
|
||||
}
|
||||
|
||||
transitions := sm.stateManager.GetTransitionConfigs(from)
|
||||
for _, transition := range transitions {
|
||||
if transition.To == to {
|
||||
return transition.Action
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetTransitionActionName 获取状态转换对应的操作中文名称
|
||||
func (sm *CertificationStateMachine) GetTransitionActionName(
|
||||
from enums.CertificationStatus,
|
||||
to enums.CertificationStatus,
|
||||
) string {
|
||||
transitions := sm.stateManager.GetTransitionConfigs(from)
|
||||
for _, transition := range transitions {
|
||||
if transition.To == to {
|
||||
return transition.ActionName
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetStateConfig 获取状态配置
|
||||
func (sm *CertificationStateMachine) GetStateConfig(status enums.CertificationStatus) *StateConfig {
|
||||
return sm.stateManager.GetStateConfig(status)
|
||||
}
|
||||
|
||||
// GetProgressPercentage 获取进度百分比
|
||||
func (sm *CertificationStateMachine) GetProgressPercentage(status enums.CertificationStatus) int {
|
||||
return sm.stateManager.GetProgressPercentage(status)
|
||||
}
|
||||
|
||||
// IsUserActionRequired 检查是否需要用户操作
|
||||
func (sm *CertificationStateMachine) IsUserActionRequired(status enums.CertificationStatus) bool {
|
||||
return sm.stateManager.IsUserActionRequired(status)
|
||||
}
|
||||
|
||||
// IsAdminActionRequired 检查是否需要管理员操作
|
||||
func (sm *CertificationStateMachine) IsAdminActionRequired(status enums.CertificationStatus) bool {
|
||||
return sm.stateManager.IsAdminActionRequired(status)
|
||||
}
|
||||
|
||||
// GetTransitionHistory 获取状态转换历史
|
||||
func (sm *CertificationStateMachine) GetTransitionHistory(ctx context.Context, certificationID string) ([]map[string]interface{}, error) {
|
||||
cert, err := sm.certRepo.GetByID(ctx, certificationID)
|
||||
@@ -315,12 +209,12 @@ func (sm *CertificationStateMachine) GetTransitionHistory(ctx context.Context, c
|
||||
})
|
||||
}
|
||||
|
||||
if cert.FaceVerifiedAt != nil {
|
||||
if cert.EnterpriseVerifiedAt != nil {
|
||||
history = append(history, map[string]interface{}{
|
||||
"status": string(enums.StatusFaceVerified),
|
||||
"timestamp": *cert.FaceVerifiedAt,
|
||||
"action": "face_verify",
|
||||
"performer": "system",
|
||||
"status": string(enums.StatusEnterpriseVerified),
|
||||
"timestamp": *cert.EnterpriseVerifiedAt,
|
||||
"action": "enterprise_verify",
|
||||
"performer": "user",
|
||||
"metadata": map[string]interface{}{},
|
||||
})
|
||||
}
|
||||
@@ -335,27 +229,6 @@ func (sm *CertificationStateMachine) GetTransitionHistory(ctx context.Context, c
|
||||
})
|
||||
}
|
||||
|
||||
if cert.ContractApprovedAt != nil {
|
||||
metadata := map[string]interface{}{}
|
||||
if cert.AdminID != nil {
|
||||
metadata["admin_id"] = *cert.AdminID
|
||||
}
|
||||
if cert.ApprovalNotes != "" {
|
||||
metadata["approval_notes"] = cert.ApprovalNotes
|
||||
}
|
||||
if cert.SigningURL != "" {
|
||||
metadata["signing_url"] = cert.SigningURL
|
||||
}
|
||||
|
||||
history = append(history, map[string]interface{}{
|
||||
"status": string(enums.StatusContractApproved),
|
||||
"timestamp": *cert.ContractApprovedAt,
|
||||
"action": "admin_approve",
|
||||
"performer": "admin",
|
||||
"metadata": metadata,
|
||||
})
|
||||
}
|
||||
|
||||
if cert.ContractSignedAt != nil {
|
||||
metadata := map[string]interface{}{}
|
||||
if cert.ContractURL != "" {
|
||||
@@ -365,7 +238,7 @@ func (sm *CertificationStateMachine) GetTransitionHistory(ctx context.Context, c
|
||||
history = append(history, map[string]interface{}{
|
||||
"status": string(enums.StatusContractSigned),
|
||||
"timestamp": *cert.ContractSignedAt,
|
||||
"action": "user_sign",
|
||||
"action": "sign_contract",
|
||||
"performer": "user",
|
||||
"metadata": metadata,
|
||||
})
|
||||
@@ -375,7 +248,7 @@ func (sm *CertificationStateMachine) GetTransitionHistory(ctx context.Context, c
|
||||
history = append(history, map[string]interface{}{
|
||||
"status": string(enums.StatusCompleted),
|
||||
"timestamp": *cert.CompletedAt,
|
||||
"action": "system_complete",
|
||||
"action": "complete",
|
||||
"performer": "system",
|
||||
"metadata": map[string]interface{}{},
|
||||
})
|
||||
@@ -383,66 +256,3 @@ func (sm *CertificationStateMachine) GetTransitionHistory(ctx context.Context, c
|
||||
|
||||
return history, nil
|
||||
}
|
||||
|
||||
// ValidateCertificationFlow 验证认证流程的完整性
|
||||
func (sm *CertificationStateMachine) ValidateCertificationFlow(ctx context.Context, certificationID string) (map[string]interface{}, error) {
|
||||
cert, err := sm.certRepo.GetByID(ctx, certificationID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取认证记录失败: %w", err)
|
||||
}
|
||||
|
||||
validation := map[string]interface{}{
|
||||
"certification_id": certificationID,
|
||||
"current_status": cert.Status,
|
||||
"is_valid": true,
|
||||
"issues": []string{},
|
||||
"warnings": []string{},
|
||||
}
|
||||
|
||||
// 检查必要的时间节点
|
||||
if cert.Status != enums.StatusPending {
|
||||
if cert.InfoSubmittedAt == nil {
|
||||
validation["is_valid"] = false
|
||||
validation["issues"] = append(validation["issues"].([]string), "缺少企业信息提交时间")
|
||||
}
|
||||
}
|
||||
|
||||
if cert.Status == enums.StatusFaceVerified || cert.Status == enums.StatusContractApplied ||
|
||||
cert.Status == enums.StatusContractPending || cert.Status == enums.StatusContractApproved ||
|
||||
cert.Status == enums.StatusContractSigned || cert.Status == enums.StatusCompleted {
|
||||
if cert.FaceVerifiedAt == nil {
|
||||
validation["is_valid"] = false
|
||||
validation["issues"] = append(validation["issues"].([]string), "缺少人脸识别完成时间")
|
||||
}
|
||||
}
|
||||
|
||||
if cert.Status == enums.StatusContractApproved || cert.Status == enums.StatusContractSigned ||
|
||||
cert.Status == enums.StatusCompleted {
|
||||
if cert.ContractApprovedAt == nil {
|
||||
validation["is_valid"] = false
|
||||
validation["issues"] = append(validation["issues"].([]string), "缺少合同审核时间")
|
||||
}
|
||||
if cert.SigningURL == "" {
|
||||
validation["warnings"] = append(validation["warnings"].([]string), "缺少合同签署链接")
|
||||
}
|
||||
}
|
||||
|
||||
if cert.Status == enums.StatusContractSigned || cert.Status == enums.StatusCompleted {
|
||||
if cert.ContractSignedAt == nil {
|
||||
validation["is_valid"] = false
|
||||
validation["issues"] = append(validation["issues"].([]string), "缺少合同签署时间")
|
||||
}
|
||||
if cert.ContractURL == "" {
|
||||
validation["warnings"] = append(validation["warnings"].([]string), "缺少合同文件链接")
|
||||
}
|
||||
}
|
||||
|
||||
if cert.Status == enums.StatusCompleted {
|
||||
if cert.CompletedAt == nil {
|
||||
validation["is_valid"] = false
|
||||
validation["issues"] = append(validation["issues"].([]string), "缺少认证完成时间")
|
||||
}
|
||||
}
|
||||
|
||||
return validation, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user