f
This commit is contained in:
818
internal/domains/certification/entities/certification.go
Normal file
818
internal/domains/certification/entities/certification.go
Normal file
@@ -0,0 +1,818 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"hyapi-server/internal/domains/certification/entities/value_objects"
|
||||
"hyapi-server/internal/domains/certification/enums"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Certification 认证聚合根
|
||||
// 这是企业认证流程的核心聚合根,封装了完整的认证业务逻辑和状态管理
|
||||
type Certification struct {
|
||||
// === 基础信息 ===
|
||||
ID string `gorm:"primaryKey;type:varchar(64)" json:"id" comment:"认证申请唯一标识"`
|
||||
UserID string `gorm:"type:varchar(36);not null;unique" json:"user_id" comment:"申请用户ID"`
|
||||
Status enums.CertificationStatus `gorm:"type:varchar(50);not null;index" json:"status" 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:"认证完成时间"`
|
||||
ContractFileCreatedAt *time.Time `json:"contract_file_created_at,omitempty" comment:"合同文件生成时间"`
|
||||
|
||||
// === e签宝相关信息 ===
|
||||
AuthFlowID string `gorm:"type:varchar(500)" json:"auth_flow_id,omitempty" comment:"企业认证流程ID"`
|
||||
AuthURL string `gorm:"type:varchar(500)" json:"auth_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:"合同签署链接"`
|
||||
|
||||
// === 失败信息 ===
|
||||
FailureReason enums.FailureReason `gorm:"type:varchar(100)" json:"failure_reason,omitempty" comment:"失败原因"`
|
||||
FailureMessage string `gorm:"type:text" json:"failure_message,omitempty" comment:"失败详细信息"`
|
||||
RetryCount int `gorm:"default:0" json:"retry_count" comment:"重试次数"`
|
||||
|
||||
// === 审计信息 ===
|
||||
LastTransitionAt *time.Time `json:"last_transition_at,omitempty" comment:"最后状态转换时间"`
|
||||
LastTransitionBy enums.ActorType `gorm:"type:varchar(20)" json:"last_transition_by,omitempty" comment:"最后操作者类型"`
|
||||
LastTransitionActor string `gorm:"type:varchar(100)" json:"last_transition_actor,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:"软删除时间"`
|
||||
|
||||
// === 领域事件 (不持久化) ===
|
||||
domainEvents []interface{} `gorm:"-" json:"-"`
|
||||
}
|
||||
|
||||
// TableName 指定数据库表名
|
||||
func (Certification) TableName() string {
|
||||
return "certifications"
|
||||
}
|
||||
|
||||
// BeforeCreate GORM钩子:创建前自动生成UUID
|
||||
func (c *Certification) BeforeCreate(tx *gorm.DB) error {
|
||||
if c.ID == "" {
|
||||
c.ID = uuid.New().String()
|
||||
}
|
||||
|
||||
// 设置初始状态
|
||||
if c.Status == "" {
|
||||
c.Status = enums.StatusPending
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ================ 工厂方法 ================
|
||||
|
||||
// NewCertification 创建新的认证申请
|
||||
func NewCertification(userID string) (*Certification, error) {
|
||||
if userID == "" {
|
||||
return nil, errors.New("用户ID不能为空")
|
||||
}
|
||||
|
||||
certification := &Certification{
|
||||
ID: uuid.New().String(),
|
||||
UserID: userID,
|
||||
Status: enums.StatusPending,
|
||||
RetryCount: 0,
|
||||
domainEvents: make([]interface{}, 0),
|
||||
}
|
||||
|
||||
// 添加领域事件
|
||||
certification.addDomainEvent(&CertificationCreatedEvent{
|
||||
CertificationID: certification.ID,
|
||||
UserID: userID,
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
|
||||
return certification, nil
|
||||
}
|
||||
|
||||
// ================ 状态转换方法 ================
|
||||
|
||||
// CanTransitionTo 检查是否可以转换到目标状态
|
||||
func (c *Certification) CanTransitionTo(targetStatus enums.CertificationStatus, actor enums.ActorType) (bool, string) {
|
||||
// 检查状态转换规则
|
||||
if !enums.CanTransitionTo(c.Status, targetStatus) {
|
||||
return false, fmt.Sprintf("不允许从 %s 转换到 %s", enums.GetStatusName(c.Status), enums.GetStatusName(targetStatus))
|
||||
}
|
||||
|
||||
// 检查操作者权限
|
||||
if !c.validateActorPermission(targetStatus, actor) {
|
||||
return false, fmt.Sprintf("%s 无权执行此状态转换", enums.GetActorTypeName(actor))
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
|
||||
// TransitionTo 执行状态转换
|
||||
func (c *Certification) TransitionTo(targetStatus enums.CertificationStatus, actor enums.ActorType, actorID string, reason string) error {
|
||||
// 验证转换合法性
|
||||
canTransition, message := c.CanTransitionTo(targetStatus, actor)
|
||||
if !canTransition {
|
||||
return fmt.Errorf("状态转换失败: %s", message)
|
||||
}
|
||||
|
||||
oldStatus := c.Status
|
||||
|
||||
// 执行状态转换
|
||||
c.Status = targetStatus
|
||||
c.updateTimestampByStatus(targetStatus)
|
||||
c.updateTransitionAudit(actor, actorID)
|
||||
|
||||
// 清除失败信息(如果转换到成功状态)
|
||||
if !enums.IsFailureStatus(targetStatus) {
|
||||
c.clearFailureInfo()
|
||||
}
|
||||
|
||||
// 添加状态转换事件
|
||||
c.addDomainEvent(&CertificationStatusChangedEvent{
|
||||
CertificationID: c.ID,
|
||||
UserID: c.UserID,
|
||||
FromStatus: oldStatus,
|
||||
ToStatus: targetStatus,
|
||||
Actor: actor,
|
||||
ActorID: actorID,
|
||||
Reason: reason,
|
||||
TransitionedAt: time.Now(),
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ================ 业务操作方法 ================
|
||||
|
||||
// SubmitEnterpriseInfoForReview 提交企业信息进入人工审核(不调用 e签宝,不生成认证链接)
|
||||
func (c *Certification) SubmitEnterpriseInfoForReview(enterpriseInfo *value_objects.EnterpriseInfo) error {
|
||||
// 已处于待审核:幂等,直接成功
|
||||
if c.Status == enums.StatusInfoPendingReview {
|
||||
return nil
|
||||
}
|
||||
if c.Status != enums.StatusPending && c.Status != enums.StatusInfoRejected {
|
||||
return fmt.Errorf("当前状态 %s 不允许提交企业信息", enums.GetStatusName(c.Status))
|
||||
}
|
||||
if err := enterpriseInfo.Validate(); err != nil {
|
||||
return fmt.Errorf("企业信息验证失败: %w", err)
|
||||
}
|
||||
if err := c.TransitionTo(enums.StatusInfoPendingReview, enums.ActorTypeUser, c.UserID, "用户提交企业信息,等待人工审核"); err != nil {
|
||||
return err
|
||||
}
|
||||
c.addDomainEvent(&EnterpriseInfoSubmittedEvent{
|
||||
CertificationID: c.ID,
|
||||
UserID: c.UserID,
|
||||
EnterpriseInfo: enterpriseInfo,
|
||||
SubmittedAt: time.Now(),
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// SubmitEnterpriseInfo 提交企业信息(直接进入已提交,含认证链接;用于无审核或管理员审核通过后补链)
|
||||
func (c *Certification) SubmitEnterpriseInfo(enterpriseInfo *value_objects.EnterpriseInfo, authURL string, authFlowID string) error {
|
||||
// 验证当前状态
|
||||
if c.Status != enums.StatusPending && c.Status != enums.StatusInfoRejected {
|
||||
return fmt.Errorf("当前状态 %s 不允许提交企业信息", enums.GetStatusName(c.Status))
|
||||
}
|
||||
|
||||
// 验证企业信息
|
||||
if err := enterpriseInfo.Validate(); err != nil {
|
||||
return fmt.Errorf("企业信息验证失败: %w", err)
|
||||
}
|
||||
if authURL != "" {
|
||||
c.AuthURL = authURL
|
||||
}
|
||||
if authFlowID != "" {
|
||||
c.AuthFlowID = authFlowID
|
||||
}
|
||||
// 状态转换
|
||||
if err := c.TransitionTo(enums.StatusInfoSubmitted, enums.ActorTypeUser, c.UserID, "用户提交企业信息"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 添加业务事件
|
||||
c.addDomainEvent(&EnterpriseInfoSubmittedEvent{
|
||||
CertificationID: c.ID,
|
||||
UserID: c.UserID,
|
||||
EnterpriseInfo: enterpriseInfo,
|
||||
SubmittedAt: time.Now(),
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ApproveEnterpriseInfoReview 管理员审核通过:从待审核转为已提交,并写入企业认证链接
|
||||
func (c *Certification) ApproveEnterpriseInfoReview(authURL, authFlowID string, actorID string) error {
|
||||
if c.Status != enums.StatusInfoPendingReview {
|
||||
return fmt.Errorf("当前状态 %s 不允许执行审核通过", enums.GetStatusName(c.Status))
|
||||
}
|
||||
c.AuthURL = authURL
|
||||
c.AuthFlowID = authFlowID
|
||||
if err := c.TransitionTo(enums.StatusInfoSubmitted, enums.ActorTypeAdmin, actorID, "管理员审核通过"); err != nil {
|
||||
return err
|
||||
}
|
||||
now := time.Now()
|
||||
c.InfoSubmittedAt = &now
|
||||
return nil
|
||||
}
|
||||
|
||||
// RejectEnterpriseInfoReview 管理员审核拒绝
|
||||
func (c *Certification) RejectEnterpriseInfoReview(actorID, message string) error {
|
||||
if c.Status != enums.StatusInfoPendingReview {
|
||||
return fmt.Errorf("当前状态 %s 不允许执行审核拒绝", enums.GetStatusName(c.Status))
|
||||
}
|
||||
c.setFailureInfo(enums.FailureReasonManualReviewRejected, message)
|
||||
if err := c.TransitionTo(enums.StatusInfoRejected, enums.ActorTypeAdmin, actorID, "管理员审核拒绝"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 完成企业认证
|
||||
func (c *Certification) CompleteEnterpriseVerification() error {
|
||||
if c.Status != enums.StatusInfoSubmitted {
|
||||
return fmt.Errorf("当前状态 %s 不允许完成企业认证", enums.GetStatusName(c.Status))
|
||||
}
|
||||
|
||||
if err := c.TransitionTo(enums.StatusEnterpriseVerified, enums.ActorTypeSystem, "system", "企业认证成功"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.addDomainEvent(&EnterpriseVerificationSuccessEvent{
|
||||
CertificationID: c.ID,
|
||||
UserID: c.UserID,
|
||||
AuthFlowID: "",
|
||||
VerifiedAt: time.Now(),
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HandleEnterpriseVerificationCallback 处理企业认证回调
|
||||
func (c *Certification) HandleEnterpriseVerificationCallback(success bool, authFlowID string, failureReason enums.FailureReason, message string) error {
|
||||
// 验证当前状态
|
||||
if c.Status != enums.StatusInfoSubmitted {
|
||||
return fmt.Errorf("当前状态 %s 不允许处理企业认证回调", enums.GetStatusName(c.Status))
|
||||
}
|
||||
|
||||
c.AuthFlowID = authFlowID
|
||||
|
||||
if success {
|
||||
// 认证成功
|
||||
if err := c.TransitionTo(enums.StatusEnterpriseVerified, enums.ActorTypeEsign, "esign_system", "企业认证成功"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.addDomainEvent(&EnterpriseVerificationSuccessEvent{
|
||||
CertificationID: c.ID,
|
||||
UserID: c.UserID,
|
||||
AuthFlowID: authFlowID,
|
||||
VerifiedAt: time.Now(),
|
||||
})
|
||||
} else {
|
||||
// 认证失败
|
||||
c.setFailureInfo(failureReason, message)
|
||||
|
||||
if err := c.TransitionTo(enums.StatusInfoRejected, enums.ActorTypeEsign, "esign_system", "企业认证失败"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.addDomainEvent(&EnterpriseVerificationFailedEvent{
|
||||
CertificationID: c.ID,
|
||||
UserID: c.UserID,
|
||||
AuthFlowID: authFlowID,
|
||||
FailureReason: failureReason,
|
||||
FailureMessage: message,
|
||||
FailedAt: time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ApplyContract 申请合同签署
|
||||
func (c *Certification) ApplyContract(EsignFlowID string, ContractSignURL string) error {
|
||||
// 验证当前状态
|
||||
if c.Status != enums.StatusEnterpriseVerified {
|
||||
return fmt.Errorf("当前状态 %s 不允许申请合同", enums.GetStatusName(c.Status))
|
||||
}
|
||||
// 状态转换
|
||||
if err := c.TransitionTo(enums.StatusContractApplied, enums.ActorTypeUser, c.UserID, "用户申请合同签署"); err != nil {
|
||||
return err
|
||||
}
|
||||
c.EsignFlowID = EsignFlowID
|
||||
c.ContractSignURL = ContractSignURL
|
||||
now := time.Now()
|
||||
c.ContractFileCreatedAt = &now
|
||||
// 添加业务事件
|
||||
c.addDomainEvent(&ContractAppliedEvent{
|
||||
CertificationID: c.ID,
|
||||
UserID: c.UserID,
|
||||
AppliedAt: time.Now(),
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddContractFileID 生成合同文件
|
||||
func (c *Certification) AddContractFileID(contractFileID string, contractURL string) error {
|
||||
c.ContractFileID = contractFileID
|
||||
c.ContractURL = contractURL
|
||||
now := time.Now()
|
||||
c.ContractFileCreatedAt = &now
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateContractInfo 更新合同信息
|
||||
func (c *Certification) UpdateContractInfo(contractInfo *value_objects.ContractInfo) error {
|
||||
// 验证合同信息
|
||||
if err := contractInfo.Validate(); err != nil {
|
||||
return fmt.Errorf("合同信息验证失败: %w", err)
|
||||
}
|
||||
|
||||
// 更新合同相关字段
|
||||
c.ContractFileID = contractInfo.ContractFileID
|
||||
c.EsignFlowID = contractInfo.EsignFlowID
|
||||
c.ContractURL = contractInfo.ContractURL
|
||||
c.ContractSignURL = contractInfo.ContractSignURL
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SignSuccess 签署成功
|
||||
func (c *Certification) SignSuccess() error {
|
||||
// 验证当前状态
|
||||
if c.Status != enums.StatusContractApplied {
|
||||
return fmt.Errorf("当前状态 %s 不允许处理合同签署回调", enums.GetStatusName(c.Status))
|
||||
}
|
||||
|
||||
if err := c.TransitionTo(enums.StatusContractSigned, enums.ActorTypeEsign, "esign_system", "合同签署成功"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.addDomainEvent(&ContractSignedEvent{
|
||||
CertificationID: c.ID,
|
||||
UserID: c.UserID,
|
||||
SignedAt: time.Now(),
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContractRejection 处理合同拒签
|
||||
func (c *Certification) ContractRejection(message string) error {
|
||||
// 验证当前状态
|
||||
if c.Status != enums.StatusContractApplied {
|
||||
return fmt.Errorf("当前状态 %s 不允许处理合同拒签", enums.GetStatusName(c.Status))
|
||||
}
|
||||
|
||||
// 设置失败信息
|
||||
c.setFailureInfo(enums.FailureReasonContractRejectedByUser, message)
|
||||
|
||||
// 状态转换
|
||||
if err := c.TransitionTo(enums.StatusContractRejected, enums.ActorTypeEsign, "esign_system", "合同签署被拒绝"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 添加业务事件
|
||||
c.addDomainEvent(&ContractSignFailedEvent{
|
||||
CertificationID: c.ID,
|
||||
UserID: c.UserID,
|
||||
FailureReason: enums.FailureReasonContractRejectedByUser,
|
||||
FailureMessage: message,
|
||||
FailedAt: time.Now(),
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContractExpiration 处理合同过期
|
||||
func (c *Certification) ContractExpiration() error {
|
||||
// 验证当前状态
|
||||
if c.Status != enums.StatusContractApplied {
|
||||
return fmt.Errorf("当前状态 %s 不允许处理合同过期", enums.GetStatusName(c.Status))
|
||||
}
|
||||
|
||||
// 设置失败信息
|
||||
c.setFailureInfo(enums.FailureReasonContractExpired, "合同签署已超时")
|
||||
|
||||
// 状态转换
|
||||
if err := c.TransitionTo(enums.StatusContractExpired, enums.ActorTypeSystem, "system", "合同签署超时"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 添加业务事件
|
||||
c.addDomainEvent(&ContractSignFailedEvent{
|
||||
CertificationID: c.ID,
|
||||
UserID: c.UserID,
|
||||
FailureReason: enums.FailureReasonContractExpired,
|
||||
FailureMessage: "合同签署已超时",
|
||||
FailedAt: time.Now(),
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RetryFromFailure 从失败状态重试
|
||||
func (c *Certification) RetryFromFailure(actor enums.ActorType, actorID string) error {
|
||||
if !enums.IsFailureStatus(c.Status) {
|
||||
return errors.New("当前状态不是失败状态,无需重试")
|
||||
}
|
||||
|
||||
// 检查重试次数限制
|
||||
if c.RetryCount >= 3 {
|
||||
return errors.New("已达到最大重试次数限制")
|
||||
}
|
||||
|
||||
// 检查失败原因是否可重试
|
||||
if !enums.IsRetryable(c.FailureReason) {
|
||||
return fmt.Errorf("失败原因 %s 不支持重试", enums.GetFailureReasonName(c.FailureReason))
|
||||
}
|
||||
|
||||
var targetStatus enums.CertificationStatus
|
||||
var reason string
|
||||
|
||||
switch c.Status {
|
||||
case enums.StatusInfoRejected:
|
||||
targetStatus = enums.StatusInfoSubmitted
|
||||
reason = "重新提交企业信息"
|
||||
case enums.StatusContractRejected, enums.StatusContractExpired:
|
||||
targetStatus = enums.StatusEnterpriseVerified
|
||||
reason = "重置状态,准备重新申请合同"
|
||||
default:
|
||||
return fmt.Errorf("不支持从状态 %s 重试", enums.GetStatusName(c.Status))
|
||||
}
|
||||
|
||||
// 增加重试次数
|
||||
c.RetryCount++
|
||||
|
||||
// 状态转换
|
||||
if err := c.TransitionTo(targetStatus, actor, actorID, reason); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 添加重试事件
|
||||
c.addDomainEvent(&CertificationRetryEvent{
|
||||
CertificationID: c.ID,
|
||||
UserID: c.UserID,
|
||||
FromStatus: c.Status,
|
||||
ToStatus: targetStatus,
|
||||
RetryCount: c.RetryCount,
|
||||
RetriedAt: time.Now(),
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CompleteCertification 完成认证
|
||||
func (c *Certification) CompleteCertification() error {
|
||||
// 验证当前状态
|
||||
if c.Status != enums.StatusContractSigned {
|
||||
return fmt.Errorf("当前状态 %s 不允许完成认证", enums.GetStatusName(c.Status))
|
||||
}
|
||||
|
||||
// 状态转换
|
||||
if err := c.TransitionTo(enums.StatusCompleted, enums.ActorTypeSystem, "system", "系统处理完成,认证成功"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 添加业务事件
|
||||
c.addDomainEvent(&CertificationCompletedEvent{
|
||||
CertificationID: c.ID,
|
||||
UserID: c.UserID,
|
||||
CompletedAt: time.Now(),
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ================ 查询方法 ================
|
||||
// GetDataByStatus 根据当前状态获取对应的数据
|
||||
func (c *Certification) GetDataByStatus() map[string]interface{} {
|
||||
data := map[string]interface{}{}
|
||||
switch c.Status {
|
||||
case enums.StatusInfoPendingReview:
|
||||
// 待审核,无额外数据
|
||||
case enums.StatusInfoSubmitted:
|
||||
data["auth_url"] = c.AuthURL
|
||||
case enums.StatusInfoRejected:
|
||||
data["failure_reason"] = c.FailureReason
|
||||
data["failure_message"] = c.FailureMessage
|
||||
case enums.StatusEnterpriseVerified:
|
||||
data["ContractURL"] = c.ContractURL
|
||||
case enums.StatusContractApplied:
|
||||
data["contract_sign_url"] = c.ContractSignURL
|
||||
case enums.StatusContractSigned:
|
||||
case enums.StatusCompleted:
|
||||
data["completed_at"] = c.CompletedAt
|
||||
case enums.StatusContractRejected:
|
||||
data["failure_reason"] = c.FailureReason
|
||||
data["failure_message"] = c.FailureMessage
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// GetProgress 获取认证进度百分比
|
||||
func (c *Certification) GetProgress() int {
|
||||
return enums.GetProgressPercentage(c.Status)
|
||||
}
|
||||
|
||||
// IsUserActionRequired 是否需要用户操作
|
||||
func (c *Certification) IsUserActionRequired() bool {
|
||||
return enums.IsUserActionRequired(c.Status)
|
||||
}
|
||||
|
||||
// GetCurrentStatusName 获取当前状态名称
|
||||
func (c *Certification) GetCurrentStatusName() string {
|
||||
return enums.GetStatusName(c.Status)
|
||||
}
|
||||
|
||||
// GetUserActionHint 获取用户操作提示
|
||||
func (c *Certification) GetUserActionHint() string {
|
||||
return enums.GetUserActionHint(c.Status)
|
||||
}
|
||||
|
||||
// GetAvailableActions 获取当前可执行的操作
|
||||
func (c *Certification) GetAvailableActions() []string {
|
||||
actions := make([]string, 0)
|
||||
|
||||
switch c.Status {
|
||||
case enums.StatusPending:
|
||||
actions = append(actions, "submit_enterprise_info")
|
||||
case enums.StatusInfoPendingReview:
|
||||
// 等待人工审核,无用户操作
|
||||
case enums.StatusEnterpriseVerified:
|
||||
actions = append(actions, "apply_contract")
|
||||
case enums.StatusInfoRejected, enums.StatusContractRejected, enums.StatusContractExpired:
|
||||
if enums.IsRetryable(c.FailureReason) && c.RetryCount < 3 {
|
||||
actions = append(actions, "retry")
|
||||
}
|
||||
}
|
||||
|
||||
return actions
|
||||
}
|
||||
|
||||
// IsFinalStatus 是否为最终状态
|
||||
func (c *Certification) IsFinalStatus() bool {
|
||||
return enums.IsFinalStatus(c.Status)
|
||||
}
|
||||
|
||||
// IsCompleted 是否已完成
|
||||
func (c *Certification) IsCompleted() bool {
|
||||
return c.Status == enums.StatusCompleted
|
||||
}
|
||||
|
||||
// GetNextValidStatuses 获取下一个有效状态
|
||||
func (c *Certification) GetNextValidStatuses() []enums.CertificationStatus {
|
||||
return enums.GetNextValidStatuses(c.Status)
|
||||
}
|
||||
|
||||
// GetFailureInfo 获取失败信息
|
||||
func (c *Certification) GetFailureInfo() (enums.FailureReason, string) {
|
||||
return c.FailureReason, c.FailureMessage
|
||||
}
|
||||
|
||||
// IsContractFileExpired 判断合同文件是否过期(生成后50分钟过期)
|
||||
func (c *Certification) IsContractFileExpired() bool {
|
||||
if c.ContractFileCreatedAt == nil && c.Status == enums.StatusEnterpriseVerified {
|
||||
// 60分钟前
|
||||
t := time.Now().Add(-60 * time.Minute)
|
||||
c.ContractFileCreatedAt = &t
|
||||
return true
|
||||
}
|
||||
if c.ContractFileCreatedAt != nil {
|
||||
return time.Since(*c.ContractFileCreatedAt) > 50*time.Minute
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsContractFileNeedUpdate 是否需要更新合同文件
|
||||
func (c *Certification) IsContractFileNeedUpdate() bool {
|
||||
if c.IsContractFileExpired() && c.Status == enums.StatusEnterpriseVerified {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ================ 业务规则验证 ================
|
||||
|
||||
// ValidateBusinessRules 验证业务规则
|
||||
func (c *Certification) ValidateBusinessRules() error {
|
||||
// 基础验证
|
||||
if c.UserID == "" {
|
||||
return errors.New("用户ID不能为空")
|
||||
}
|
||||
|
||||
if !enums.IsValidStatus(c.Status) {
|
||||
return fmt.Errorf("无效的认证状态: %s", c.Status)
|
||||
}
|
||||
|
||||
// 状态相关验证
|
||||
switch c.Status {
|
||||
case enums.StatusContractSigned:
|
||||
if c.ContractFileID == "" || c.EsignFlowID == "" {
|
||||
return errors.New("合同签署状态下必须有完整的合同信息")
|
||||
}
|
||||
case enums.StatusCompleted:
|
||||
if c.CompletedAt == nil {
|
||||
return errors.New("认证完成状态下必须有完成时间")
|
||||
}
|
||||
}
|
||||
|
||||
// 失败状态验证
|
||||
if enums.IsFailureStatus(c.Status) {
|
||||
if c.FailureReason == "" {
|
||||
return errors.New("失败状态下必须有失败原因")
|
||||
}
|
||||
if !enums.IsValidFailureReason(c.FailureReason) {
|
||||
return fmt.Errorf("无效的失败原因: %s", c.FailureReason)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateActorPermission 验证操作者权限
|
||||
func (c *Certification) validateActorPermission(targetStatus enums.CertificationStatus, actor enums.ActorType) bool {
|
||||
// 定义状态转换的权限规则(目标状态 -> 允许的操作者)
|
||||
permissions := map[enums.CertificationStatus][]enums.ActorType{
|
||||
enums.StatusInfoPendingReview: {enums.ActorTypeUser},
|
||||
enums.StatusInfoSubmitted: {enums.ActorTypeUser, enums.ActorTypeAdmin},
|
||||
enums.StatusEnterpriseVerified: {enums.ActorTypeEsign, enums.ActorTypeSystem, enums.ActorTypeAdmin},
|
||||
enums.StatusInfoRejected: {enums.ActorTypeEsign, enums.ActorTypeSystem, enums.ActorTypeAdmin},
|
||||
enums.StatusContractApplied: {enums.ActorTypeUser, enums.ActorTypeAdmin},
|
||||
enums.StatusContractSigned: {enums.ActorTypeEsign, enums.ActorTypeSystem, enums.ActorTypeAdmin},
|
||||
enums.StatusContractRejected: {enums.ActorTypeEsign, enums.ActorTypeSystem, enums.ActorTypeAdmin},
|
||||
enums.StatusContractExpired: {enums.ActorTypeEsign, enums.ActorTypeSystem, enums.ActorTypeAdmin},
|
||||
enums.StatusCompleted: {enums.ActorTypeSystem, enums.ActorTypeAdmin},
|
||||
}
|
||||
|
||||
allowedActors, exists := permissions[targetStatus]
|
||||
if !exists {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, allowedActor := range allowedActors {
|
||||
if actor == allowedActor {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// ================ 辅助方法 ================
|
||||
|
||||
// updateTimestampByStatus 根据状态更新对应的时间戳
|
||||
func (c *Certification) updateTimestampByStatus(status enums.CertificationStatus) {
|
||||
now := time.Now()
|
||||
|
||||
switch status {
|
||||
case enums.StatusInfoSubmitted:
|
||||
c.InfoSubmittedAt = &now
|
||||
case enums.StatusEnterpriseVerified:
|
||||
c.EnterpriseVerifiedAt = &now
|
||||
case enums.StatusContractApplied:
|
||||
c.ContractAppliedAt = &now
|
||||
case enums.StatusContractSigned:
|
||||
c.ContractSignedAt = &now
|
||||
case enums.StatusCompleted:
|
||||
c.CompletedAt = &now
|
||||
}
|
||||
}
|
||||
|
||||
// updateTransitionAudit 更新状态转换审计信息
|
||||
func (c *Certification) updateTransitionAudit(actor enums.ActorType, actorID string) {
|
||||
now := time.Now()
|
||||
c.LastTransitionAt = &now
|
||||
c.LastTransitionBy = actor
|
||||
c.LastTransitionActor = actorID
|
||||
}
|
||||
|
||||
// setFailureInfo 设置失败信息
|
||||
func (c *Certification) setFailureInfo(reason enums.FailureReason, message string) {
|
||||
c.FailureReason = reason
|
||||
c.FailureMessage = message
|
||||
}
|
||||
|
||||
// clearFailureInfo 清除失败信息
|
||||
func (c *Certification) clearFailureInfo() {
|
||||
c.FailureReason = ""
|
||||
c.FailureMessage = ""
|
||||
}
|
||||
|
||||
// ================ 领域事件管理 ================
|
||||
|
||||
// addDomainEvent 添加领域事件
|
||||
func (c *Certification) addDomainEvent(event interface{}) {
|
||||
if c.domainEvents == nil {
|
||||
c.domainEvents = make([]interface{}, 0)
|
||||
}
|
||||
c.domainEvents = append(c.domainEvents, event)
|
||||
}
|
||||
|
||||
// GetDomainEvents 获取领域事件
|
||||
func (c *Certification) GetDomainEvents() []interface{} {
|
||||
return c.domainEvents
|
||||
}
|
||||
|
||||
// ClearDomainEvents 清除领域事件
|
||||
func (c *Certification) ClearDomainEvents() {
|
||||
c.domainEvents = make([]interface{}, 0)
|
||||
}
|
||||
|
||||
// ================ 领域事件定义 ================
|
||||
|
||||
// CertificationCreatedEvent 认证创建事件
|
||||
type CertificationCreatedEvent struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
// CertificationStatusChangedEvent 认证状态变更事件
|
||||
type CertificationStatusChangedEvent struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
FromStatus enums.CertificationStatus `json:"from_status"`
|
||||
ToStatus enums.CertificationStatus `json:"to_status"`
|
||||
Actor enums.ActorType `json:"actor"`
|
||||
ActorID string `json:"actor_id"`
|
||||
Reason string `json:"reason"`
|
||||
TransitionedAt time.Time `json:"transitioned_at"`
|
||||
}
|
||||
|
||||
// EnterpriseInfoSubmittedEvent 企业信息提交事件
|
||||
type EnterpriseInfoSubmittedEvent struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
EnterpriseInfo *value_objects.EnterpriseInfo `json:"enterprise_info"`
|
||||
SubmittedAt time.Time `json:"submitted_at"`
|
||||
}
|
||||
|
||||
// EnterpriseVerificationSuccessEvent 企业认证成功事件
|
||||
type EnterpriseVerificationSuccessEvent struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
AuthFlowID string `json:"auth_flow_id"`
|
||||
VerifiedAt time.Time `json:"verified_at"`
|
||||
}
|
||||
|
||||
// EnterpriseVerificationFailedEvent 企业认证失败事件
|
||||
type EnterpriseVerificationFailedEvent struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
AuthFlowID string `json:"auth_flow_id"`
|
||||
FailureReason enums.FailureReason `json:"failure_reason"`
|
||||
FailureMessage string `json:"failure_message"`
|
||||
FailedAt time.Time `json:"failed_at"`
|
||||
}
|
||||
|
||||
// ContractAppliedEvent 合同申请事件
|
||||
type ContractAppliedEvent struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
AppliedAt time.Time `json:"applied_at"`
|
||||
}
|
||||
|
||||
// ContractSignedEvent 合同签署成功事件
|
||||
type ContractSignedEvent struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
ContractURL string `json:"contract_url"`
|
||||
SignedAt time.Time `json:"signed_at"`
|
||||
}
|
||||
|
||||
// ContractSignFailedEvent 合同签署失败事件
|
||||
type ContractSignFailedEvent struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
FailureReason enums.FailureReason `json:"failure_reason"`
|
||||
FailureMessage string `json:"failure_message"`
|
||||
FailedAt time.Time `json:"failed_at"`
|
||||
}
|
||||
|
||||
// CertificationCompletedEvent 认证完成事件
|
||||
type CertificationCompletedEvent struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
CompletedAt time.Time `json:"completed_at"`
|
||||
}
|
||||
|
||||
// CertificationRetryEvent 认证重试事件
|
||||
type CertificationRetryEvent struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
FromStatus enums.CertificationStatus `json:"from_status"`
|
||||
ToStatus enums.CertificationStatus `json:"to_status"`
|
||||
RetryCount int `json:"retry_count"`
|
||||
RetriedAt time.Time `json:"retried_at"`
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
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"`
|
||||
EnterpriseAddress string `json:"enterprise_address" gorm:"type:varchar(200);not null"` // 新增企业地址
|
||||
|
||||
// 授权代表信息(gorm 指定列名,确保与表 enterprise_info_submit_records 列一致并正确读入)
|
||||
AuthorizedRepName string `json:"authorized_rep_name" gorm:"column:authorized_rep_name;type:varchar(50);comment:授权代表姓名"`
|
||||
AuthorizedRepID string `json:"authorized_rep_id" gorm:"column:authorized_rep_id;type:varchar(50);comment:授权代表身份证号"`
|
||||
AuthorizedRepPhone string `json:"authorized_rep_phone" gorm:"column:authorized_rep_phone;type:varchar(50);comment:授权代表手机号"`
|
||||
// 授权代表身份证正反面图片URL列表(JSON字符串),按顺序存储[人像面, 国徽面]
|
||||
AuthorizedRepIDImageURLs string `json:"authorized_rep_id_image_urls" gorm:"column:authorized_rep_id_image_urls;type:text;comment:授权代表身份证正反面图片URL列表(JSON字符串)"`
|
||||
|
||||
// 企业资质与场地材料
|
||||
BusinessLicenseImageURL string `json:"business_license_image_url" gorm:"type:varchar(500);comment:营业执照图片URL"`
|
||||
OfficePlaceImageURLs string `json:"office_place_image_urls" gorm:"type:text;comment:办公场地图片URL列表(JSON字符串)"`
|
||||
// 应用场景
|
||||
APIUsage string `json:"api_usage" gorm:"type:text;comment:接口用途及业务场景说明"`
|
||||
ScenarioAttachmentURLs string `json:"scenario_attachment_urls" gorm:"type:text;comment:场景附件图片URL列表(JSON字符串)"`
|
||||
|
||||
// 提交状态
|
||||
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"`
|
||||
|
||||
// 人工审核信息
|
||||
ManualReviewStatus string `json:"manual_review_status" gorm:"type:varchar(20);not null;default:'pending';comment:人工审核状态(pending,approved,rejected)"`
|
||||
ManualReviewRemark string `json:"manual_review_remark" gorm:"type:text;comment:人工审核备注"`
|
||||
ManualReviewedAt *time.Time `json:"manual_reviewed_at" gorm:"comment:人工审核时间"`
|
||||
ManualReviewerID string `json:"manual_reviewer_id" gorm:"type:varchar(36);comment:人工审核人ID"`
|
||||
|
||||
// 系统字段
|
||||
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, enterpriseAddress string,
|
||||
) *EnterpriseInfoSubmitRecord {
|
||||
return &EnterpriseInfoSubmitRecord{
|
||||
ID: uuid.New().String(),
|
||||
UserID: userID,
|
||||
CompanyName: companyName,
|
||||
UnifiedSocialCode: unifiedSocialCode,
|
||||
LegalPersonName: legalPersonName,
|
||||
LegalPersonID: legalPersonID,
|
||||
LegalPersonPhone: legalPersonPhone,
|
||||
EnterpriseAddress: enterpriseAddress,
|
||||
Status: "submitted",
|
||||
ManualReviewStatus: "pending",
|
||||
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
|
||||
}
|
||||
|
||||
// MarkManualApproved 标记人工审核通过
|
||||
func (r *EnterpriseInfoSubmitRecord) MarkManualApproved(reviewerID, remark string) {
|
||||
now := time.Now()
|
||||
r.ManualReviewStatus = "approved"
|
||||
r.ManualReviewedAt = &now
|
||||
r.ManualReviewerID = reviewerID
|
||||
r.ManualReviewRemark = remark
|
||||
r.UpdatedAt = now
|
||||
}
|
||||
|
||||
// MarkManualRejected 标记人工审核拒绝
|
||||
func (r *EnterpriseInfoSubmitRecord) MarkManualRejected(reviewerID, remark string) {
|
||||
now := time.Now()
|
||||
r.ManualReviewStatus = "rejected"
|
||||
r.ManualReviewedAt = &now
|
||||
r.ManualReviewerID = reviewerID
|
||||
r.ManualReviewRemark = remark
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,516 @@
|
||||
package value_objects
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ContractInfo 合同信息值对象
|
||||
// 封装电子合同相关的核心信息,包含合同状态和签署流程管理
|
||||
type ContractInfo struct {
|
||||
// 合同基本信息
|
||||
ContractFileID string `json:"contract_file_id"` // 合同文件ID
|
||||
EsignFlowID string `json:"esign_flow_id"` // e签宝签署流程ID
|
||||
ContractURL string `json:"contract_url"` // 合同文件访问链接
|
||||
ContractSignURL string `json:"contract_sign_url"` // 合同签署链接
|
||||
|
||||
// 合同元数据
|
||||
ContractTitle string `json:"contract_title"` // 合同标题
|
||||
ContractVersion string `json:"contract_version"` // 合同版本
|
||||
TemplateID string `json:"template_id"` // 模板ID
|
||||
|
||||
// 签署相关信息
|
||||
SignerAccount string `json:"signer_account"` // 签署人账号
|
||||
SignerName string `json:"signer_name"` // 签署人姓名
|
||||
TransactorPhone string `json:"transactor_phone"` // 经办人手机号
|
||||
TransactorName string `json:"transactor_name"` // 经办人姓名
|
||||
TransactorIDCardNum string `json:"transactor_id_card_num"` // 经办人身份证号
|
||||
|
||||
// 时间信息
|
||||
GeneratedAt *time.Time `json:"generated_at,omitempty"` // 合同生成时间
|
||||
SignFlowCreatedAt *time.Time `json:"sign_flow_created_at,omitempty"` // 签署流程创建时间
|
||||
SignedAt *time.Time `json:"signed_at,omitempty"` // 签署完成时间
|
||||
ExpiresAt *time.Time `json:"expires_at,omitempty"` // 签署链接过期时间
|
||||
|
||||
// 状态信息
|
||||
Status string `json:"status"` // 合同状态
|
||||
SignProgress int `json:"sign_progress"` // 签署进度
|
||||
|
||||
// 附加信息
|
||||
Metadata map[string]interface{} `json:"metadata,omitempty"` // 元数据
|
||||
}
|
||||
|
||||
// ContractStatus 合同状态常量
|
||||
const (
|
||||
ContractStatusDraft = "draft" // 草稿
|
||||
ContractStatusGenerated = "generated" // 已生成
|
||||
ContractStatusSigning = "signing" // 签署中
|
||||
ContractStatusSigned = "signed" // 已签署
|
||||
ContractStatusExpired = "expired" // 已过期
|
||||
ContractStatusRejected = "rejected" // 被拒绝
|
||||
ContractStatusCancelled = "cancelled" // 已取消
|
||||
)
|
||||
|
||||
// NewContractInfo 创建合同信息值对象
|
||||
func NewContractInfo(contractFileID, esignFlowID, contractURL, contractSignURL string) (*ContractInfo, error) {
|
||||
info := &ContractInfo{
|
||||
ContractFileID: strings.TrimSpace(contractFileID),
|
||||
EsignFlowID: strings.TrimSpace(esignFlowID),
|
||||
ContractURL: strings.TrimSpace(contractURL),
|
||||
ContractSignURL: strings.TrimSpace(contractSignURL),
|
||||
Status: ContractStatusGenerated,
|
||||
SignProgress: 0,
|
||||
Metadata: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
if err := info.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("合同信息验证失败: %w", err)
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// Validate 验证合同信息的完整性和格式
|
||||
func (c *ContractInfo) Validate() error {
|
||||
if err := c.validateContractFileID(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.validateEsignFlowID(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.validateContractURL(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.validateContractSignURL(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.validateSignerInfo(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.validateStatus(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateContractFileID 验证合同文件ID
|
||||
func (c *ContractInfo) validateContractFileID() error {
|
||||
if c.ContractFileID == "" {
|
||||
return errors.New("合同文件ID不能为空")
|
||||
}
|
||||
|
||||
// 简单的格式验证
|
||||
if len(c.ContractFileID) < 10 {
|
||||
return errors.New("合同文件ID格式不正确")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateEsignFlowID 验证e签宝流程ID
|
||||
func (c *ContractInfo) validateEsignFlowID() error {
|
||||
if c.EsignFlowID == "" {
|
||||
return errors.New("e签宝流程ID不能为空")
|
||||
}
|
||||
|
||||
// 简单的格式验证
|
||||
if len(c.EsignFlowID) < 10 {
|
||||
return errors.New("e签宝流程ID格式不正确")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateContractURL 验证合同访问链接
|
||||
func (c *ContractInfo) validateContractURL() error {
|
||||
if c.ContractURL == "" {
|
||||
return errors.New("合同访问链接不能为空")
|
||||
}
|
||||
|
||||
// URL格式验证
|
||||
urlPattern := `^https?://.*`
|
||||
matched, err := regexp.MatchString(urlPattern, c.ContractURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("合同访问链接格式验证错误: %w", err)
|
||||
}
|
||||
|
||||
if !matched {
|
||||
return errors.New("合同访问链接格式不正确,必须以http://或https://开头")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateContractSignURL 验证合同签署链接
|
||||
func (c *ContractInfo) validateContractSignURL() error {
|
||||
if c.ContractSignURL == "" {
|
||||
return errors.New("合同签署链接不能为空")
|
||||
}
|
||||
|
||||
// URL格式验证
|
||||
urlPattern := `^https?://.*`
|
||||
matched, err := regexp.MatchString(urlPattern, c.ContractSignURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("合同签署链接格式验证错误: %w", err)
|
||||
}
|
||||
|
||||
if !matched {
|
||||
return errors.New("合同签署链接格式不正确,必须以http://或https://开头")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateSignerInfo 验证签署人信息
|
||||
func (c *ContractInfo) validateSignerInfo() error {
|
||||
// 如果有签署人信息,进行验证
|
||||
if c.SignerAccount != "" || c.SignerName != "" || c.TransactorPhone != "" {
|
||||
if c.SignerAccount == "" {
|
||||
return errors.New("签署人账号不能为空")
|
||||
}
|
||||
|
||||
if c.SignerName == "" {
|
||||
return errors.New("签署人姓名不能为空")
|
||||
}
|
||||
|
||||
if c.TransactorPhone != "" {
|
||||
// 手机号格式验证
|
||||
phonePattern := `^1[3-9]\d{9}$`
|
||||
matched, err := regexp.MatchString(phonePattern, c.TransactorPhone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("经办人手机号格式验证错误: %w", err)
|
||||
}
|
||||
|
||||
if !matched {
|
||||
return errors.New("经办人手机号格式不正确")
|
||||
}
|
||||
}
|
||||
|
||||
if c.TransactorIDCardNum != "" {
|
||||
// 身份证号格式验证
|
||||
idPattern := `^[1-9]\d{5}(19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$`
|
||||
matched, err := regexp.MatchString(idPattern, c.TransactorIDCardNum)
|
||||
if err != nil {
|
||||
return fmt.Errorf("经办人身份证号格式验证错误: %w", err)
|
||||
}
|
||||
|
||||
if !matched {
|
||||
return errors.New("经办人身份证号格式不正确")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateStatus 验证合同状态
|
||||
func (c *ContractInfo) validateStatus() error {
|
||||
validStatuses := []string{
|
||||
ContractStatusDraft,
|
||||
ContractStatusGenerated,
|
||||
ContractStatusSigning,
|
||||
ContractStatusSigned,
|
||||
ContractStatusExpired,
|
||||
ContractStatusRejected,
|
||||
ContractStatusCancelled,
|
||||
}
|
||||
|
||||
for _, status := range validStatuses {
|
||||
if c.Status == status {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("无效的合同状态: %s", c.Status)
|
||||
}
|
||||
|
||||
// SetSignerInfo 设置签署人信息
|
||||
func (c *ContractInfo) SetSignerInfo(signerAccount, signerName, transactorPhone, transactorName, transactorIDCardNum string) error {
|
||||
c.SignerAccount = strings.TrimSpace(signerAccount)
|
||||
c.SignerName = strings.TrimSpace(signerName)
|
||||
c.TransactorPhone = strings.TrimSpace(transactorPhone)
|
||||
c.TransactorName = strings.TrimSpace(transactorName)
|
||||
c.TransactorIDCardNum = strings.TrimSpace(transactorIDCardNum)
|
||||
|
||||
return c.validateSignerInfo()
|
||||
}
|
||||
|
||||
// UpdateStatus 更新合同状态
|
||||
func (c *ContractInfo) UpdateStatus(status string) error {
|
||||
oldStatus := c.Status
|
||||
c.Status = status
|
||||
|
||||
if err := c.validateStatus(); err != nil {
|
||||
c.Status = oldStatus // 回滚
|
||||
return err
|
||||
}
|
||||
|
||||
// 根据状态更新进度
|
||||
c.updateProgressByStatus()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateProgressByStatus 根据状态更新进度
|
||||
func (c *ContractInfo) updateProgressByStatus() {
|
||||
progressMap := map[string]int{
|
||||
ContractStatusDraft: 0,
|
||||
ContractStatusGenerated: 25,
|
||||
ContractStatusSigning: 50,
|
||||
ContractStatusSigned: 100,
|
||||
ContractStatusExpired: 50,
|
||||
ContractStatusRejected: 50,
|
||||
ContractStatusCancelled: 0,
|
||||
}
|
||||
|
||||
if progress, exists := progressMap[c.Status]; exists {
|
||||
c.SignProgress = progress
|
||||
}
|
||||
}
|
||||
|
||||
// MarkAsSigning 标记为签署中
|
||||
func (c *ContractInfo) MarkAsSigning() error {
|
||||
c.Status = ContractStatusSigning
|
||||
c.SignProgress = 50
|
||||
now := time.Now()
|
||||
c.SignFlowCreatedAt = &now
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarkAsSigned 标记为已签署
|
||||
func (c *ContractInfo) MarkAsSigned() error {
|
||||
c.Status = ContractStatusSigned
|
||||
c.SignProgress = 100
|
||||
now := time.Now()
|
||||
c.SignedAt = &now
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarkAsExpired 标记为已过期
|
||||
func (c *ContractInfo) MarkAsExpired() error {
|
||||
c.Status = ContractStatusExpired
|
||||
now := time.Now()
|
||||
c.ExpiresAt = &now
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarkAsRejected 标记为被拒绝
|
||||
func (c *ContractInfo) MarkAsRejected() error {
|
||||
c.Status = ContractStatusRejected
|
||||
c.SignProgress = 50
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsExpired 检查合同是否已过期
|
||||
func (c *ContractInfo) IsExpired() bool {
|
||||
if c.ExpiresAt == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return time.Now().After(*c.ExpiresAt)
|
||||
}
|
||||
|
||||
// IsSigned 检查合同是否已签署
|
||||
func (c *ContractInfo) IsSigned() bool {
|
||||
return c.Status == ContractStatusSigned
|
||||
}
|
||||
|
||||
// CanSign 检查是否可以签署
|
||||
func (c *ContractInfo) CanSign() bool {
|
||||
return c.Status == ContractStatusGenerated || c.Status == ContractStatusSigning
|
||||
}
|
||||
|
||||
// GetStatusName 获取状态的中文名称
|
||||
func (c *ContractInfo) GetStatusName() string {
|
||||
statusNames := map[string]string{
|
||||
ContractStatusDraft: "草稿",
|
||||
ContractStatusGenerated: "已生成",
|
||||
ContractStatusSigning: "签署中",
|
||||
ContractStatusSigned: "已签署",
|
||||
ContractStatusExpired: "已过期",
|
||||
ContractStatusRejected: "被拒绝",
|
||||
ContractStatusCancelled: "已取消",
|
||||
}
|
||||
|
||||
if name, exists := statusNames[c.Status]; exists {
|
||||
return name
|
||||
}
|
||||
return c.Status
|
||||
}
|
||||
|
||||
// GetDisplayTitle 获取显示用的合同标题
|
||||
func (c *ContractInfo) GetDisplayTitle() string {
|
||||
if c.ContractTitle != "" {
|
||||
return c.ContractTitle
|
||||
}
|
||||
return "企业认证服务合同"
|
||||
}
|
||||
|
||||
// GetMaskedSignerAccount 获取脱敏的签署人账号
|
||||
func (c *ContractInfo) GetMaskedSignerAccount() string {
|
||||
if len(c.SignerAccount) <= 6 {
|
||||
return c.SignerAccount
|
||||
}
|
||||
|
||||
// 保留前3位和后3位,中间用*替代
|
||||
return c.SignerAccount[:3] + "***" + c.SignerAccount[len(c.SignerAccount)-3:]
|
||||
}
|
||||
|
||||
// GetMaskedTransactorPhone 获取脱敏的经办人手机号
|
||||
func (c *ContractInfo) GetMaskedTransactorPhone() string {
|
||||
if len(c.TransactorPhone) != 11 {
|
||||
return c.TransactorPhone
|
||||
}
|
||||
|
||||
// 保留前3位和后4位,中间用*替代
|
||||
return c.TransactorPhone[:3] + "****" + c.TransactorPhone[7:]
|
||||
}
|
||||
|
||||
// GetMaskedTransactorIDCardNum 获取脱敏的经办人身份证号
|
||||
func (c *ContractInfo) GetMaskedTransactorIDCardNum() string {
|
||||
if len(c.TransactorIDCardNum) != 18 {
|
||||
return c.TransactorIDCardNum
|
||||
}
|
||||
|
||||
// 保留前6位和后4位,中间用*替代
|
||||
return c.TransactorIDCardNum[:6] + "********" + c.TransactorIDCardNum[14:]
|
||||
}
|
||||
|
||||
// AddMetadata 添加元数据
|
||||
func (c *ContractInfo) AddMetadata(key string, value interface{}) {
|
||||
if c.Metadata == nil {
|
||||
c.Metadata = make(map[string]interface{})
|
||||
}
|
||||
c.Metadata[key] = value
|
||||
}
|
||||
|
||||
// GetMetadata 获取元数据
|
||||
func (c *ContractInfo) GetMetadata(key string) (interface{}, bool) {
|
||||
if c.Metadata == nil {
|
||||
return nil, false
|
||||
}
|
||||
value, exists := c.Metadata[key]
|
||||
return value, exists
|
||||
}
|
||||
|
||||
// Equals 比较两个合同信息是否相等
|
||||
func (c *ContractInfo) Equals(other *ContractInfo) bool {
|
||||
if other == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return c.ContractFileID == other.ContractFileID &&
|
||||
c.EsignFlowID == other.EsignFlowID &&
|
||||
c.Status == other.Status
|
||||
}
|
||||
|
||||
// Clone 创建合同信息的副本
|
||||
func (c *ContractInfo) Clone() *ContractInfo {
|
||||
cloned := &ContractInfo{
|
||||
ContractFileID: c.ContractFileID,
|
||||
EsignFlowID: c.EsignFlowID,
|
||||
ContractURL: c.ContractURL,
|
||||
ContractSignURL: c.ContractSignURL,
|
||||
ContractTitle: c.ContractTitle,
|
||||
ContractVersion: c.ContractVersion,
|
||||
TemplateID: c.TemplateID,
|
||||
SignerAccount: c.SignerAccount,
|
||||
SignerName: c.SignerName,
|
||||
TransactorPhone: c.TransactorPhone,
|
||||
TransactorName: c.TransactorName,
|
||||
TransactorIDCardNum: c.TransactorIDCardNum,
|
||||
Status: c.Status,
|
||||
SignProgress: c.SignProgress,
|
||||
}
|
||||
|
||||
// 复制时间字段
|
||||
if c.GeneratedAt != nil {
|
||||
generatedAt := *c.GeneratedAt
|
||||
cloned.GeneratedAt = &generatedAt
|
||||
}
|
||||
if c.SignFlowCreatedAt != nil {
|
||||
signFlowCreatedAt := *c.SignFlowCreatedAt
|
||||
cloned.SignFlowCreatedAt = &signFlowCreatedAt
|
||||
}
|
||||
if c.SignedAt != nil {
|
||||
signedAt := *c.SignedAt
|
||||
cloned.SignedAt = &signedAt
|
||||
}
|
||||
if c.ExpiresAt != nil {
|
||||
expiresAt := *c.ExpiresAt
|
||||
cloned.ExpiresAt = &expiresAt
|
||||
}
|
||||
|
||||
// 复制元数据
|
||||
if c.Metadata != nil {
|
||||
cloned.Metadata = make(map[string]interface{})
|
||||
for k, v := range c.Metadata {
|
||||
cloned.Metadata[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return cloned
|
||||
}
|
||||
|
||||
// String 返回合同信息的字符串表示
|
||||
func (c *ContractInfo) String() string {
|
||||
return fmt.Sprintf("合同信息[文件ID:%s, 流程ID:%s, 状态:%s, 进度:%d%%]",
|
||||
c.ContractFileID,
|
||||
c.EsignFlowID,
|
||||
c.GetStatusName(),
|
||||
c.SignProgress)
|
||||
}
|
||||
|
||||
// ToMap 转换为map格式(用于序列化)
|
||||
func (c *ContractInfo) ToMap() map[string]interface{} {
|
||||
result := map[string]interface{}{
|
||||
"contract_file_id": c.ContractFileID,
|
||||
"esign_flow_id": c.EsignFlowID,
|
||||
"contract_url": c.ContractURL,
|
||||
"contract_sign_url": c.ContractSignURL,
|
||||
"contract_title": c.ContractTitle,
|
||||
"contract_version": c.ContractVersion,
|
||||
"template_id": c.TemplateID,
|
||||
"signer_account": c.SignerAccount,
|
||||
"signer_name": c.SignerName,
|
||||
"transactor_phone": c.TransactorPhone,
|
||||
"transactor_name": c.TransactorName,
|
||||
"transactor_id_card_num": c.TransactorIDCardNum,
|
||||
"status": c.Status,
|
||||
"sign_progress": c.SignProgress,
|
||||
}
|
||||
|
||||
// 添加时间字段
|
||||
if c.GeneratedAt != nil {
|
||||
result["generated_at"] = c.GeneratedAt
|
||||
}
|
||||
if c.SignFlowCreatedAt != nil {
|
||||
result["sign_flow_created_at"] = c.SignFlowCreatedAt
|
||||
}
|
||||
if c.SignedAt != nil {
|
||||
result["signed_at"] = c.SignedAt
|
||||
}
|
||||
if c.ExpiresAt != nil {
|
||||
result["expires_at"] = c.ExpiresAt
|
||||
}
|
||||
|
||||
// 添加元数据
|
||||
if c.Metadata != nil {
|
||||
result["metadata"] = c.Metadata
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,365 @@
|
||||
package value_objects
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// EnterpriseInfo 企业信息值对象
|
||||
// 封装企业认证所需的核心信息,包含完整的业务规则验证
|
||||
type EnterpriseInfo struct {
|
||||
// 企业基本信息
|
||||
CompanyName string `json:"company_name"` // 企业名称
|
||||
UnifiedSocialCode string `json:"unified_social_code"` // 统一社会信用代码
|
||||
|
||||
// 法定代表人信息
|
||||
LegalPersonName string `json:"legal_person_name"` // 法定代表人姓名
|
||||
LegalPersonID string `json:"legal_person_id"` // 法定代表人身份证号
|
||||
LegalPersonPhone string `json:"legal_person_phone"` // 法定代表人手机号
|
||||
|
||||
// 企业详细信息
|
||||
RegisteredAddress string `json:"registered_address"` // 注册地址
|
||||
EnterpriseAddress string `json:"enterprise_address"` // 企业地址(新增)
|
||||
}
|
||||
|
||||
// NewEnterpriseInfo 创建企业信息值对象
|
||||
func NewEnterpriseInfo(companyName, unifiedSocialCode, legalPersonName, legalPersonID, legalPersonPhone, enterpriseAddress string) (*EnterpriseInfo, error) {
|
||||
info := &EnterpriseInfo{
|
||||
CompanyName: strings.TrimSpace(companyName),
|
||||
UnifiedSocialCode: strings.TrimSpace(unifiedSocialCode),
|
||||
LegalPersonName: strings.TrimSpace(legalPersonName),
|
||||
LegalPersonID: strings.TrimSpace(legalPersonID),
|
||||
LegalPersonPhone: strings.TrimSpace(legalPersonPhone),
|
||||
EnterpriseAddress: strings.TrimSpace(enterpriseAddress),
|
||||
}
|
||||
|
||||
if err := info.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("企业信息验证失败: %w", err)
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// Validate 验证企业信息的完整性和格式
|
||||
func (e *EnterpriseInfo) Validate() error {
|
||||
if err := e.validateCompanyName(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := e.validateUnifiedSocialCode(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := e.validateLegalPersonName(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := e.validateLegalPersonID(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := e.validateLegalPersonPhone(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := e.validateEnterpriseAddress(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateCompanyName 验证企业名称
|
||||
func (e *EnterpriseInfo) validateCompanyName() error {
|
||||
if e.CompanyName == "" {
|
||||
return errors.New("企业名称不能为空")
|
||||
}
|
||||
|
||||
if len(e.CompanyName) < 2 {
|
||||
return errors.New("企业名称长度不能少于2个字符")
|
||||
}
|
||||
|
||||
if len(e.CompanyName) > 100 {
|
||||
return errors.New("企业名称长度不能超过100个字符")
|
||||
}
|
||||
|
||||
// 检查是否包含非法字符(允许括号)
|
||||
invalidChars := []string{
|
||||
"`", "~", "!", "@", "#", "$", "%", "^", "&", "*",
|
||||
"+", "=", "{", "}", "[", "]", "【", "】", "\\", "|", ";", ":", "'", "\"", "<", ">", ",", ".", "?", "/",
|
||||
}
|
||||
for _, char := range invalidChars {
|
||||
if strings.Contains(e.CompanyName, char) {
|
||||
return fmt.Errorf("企业名称不能包含特殊字符: %s", char)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateUnifiedSocialCode 验证统一社会信用代码
|
||||
func (e *EnterpriseInfo) validateUnifiedSocialCode() error {
|
||||
if e.UnifiedSocialCode == "" {
|
||||
return errors.New("统一社会信用代码不能为空")
|
||||
}
|
||||
|
||||
// 统一社会信用代码格式验证(18位数字和字母)
|
||||
pattern := `^[0-9A-HJ-NPQRTUWXY]{2}[0-9]{6}[0-9A-HJ-NPQRTUWXY]{10}$`
|
||||
matched, err := regexp.MatchString(pattern, e.UnifiedSocialCode)
|
||||
if err != nil {
|
||||
return fmt.Errorf("统一社会信用代码格式验证错误: %w", err)
|
||||
}
|
||||
|
||||
if !matched {
|
||||
return errors.New("统一社会信用代码格式不正确,应为18位数字和字母组合")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateLegalPersonName 验证法定代表人姓名
|
||||
func (e *EnterpriseInfo) validateLegalPersonName() error {
|
||||
if e.LegalPersonName == "" {
|
||||
return errors.New("法定代表人姓名不能为空")
|
||||
}
|
||||
|
||||
if len(e.LegalPersonName) < 2 {
|
||||
return errors.New("法定代表人姓名长度不能少于2个字符")
|
||||
}
|
||||
|
||||
if len(e.LegalPersonName) > 50 {
|
||||
return errors.New("法定代表人姓名长度不能超过50个字符")
|
||||
}
|
||||
|
||||
// 中文姓名格式验证
|
||||
pattern := "^[一-龥·]+$"
|
||||
matched, err := regexp.MatchString(pattern, e.LegalPersonName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("法定代表人姓名格式验证错误: %w", err)
|
||||
}
|
||||
|
||||
if !matched {
|
||||
return errors.New("法定代表人姓名只能包含中文字符和间隔号")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateLegalPersonID 验证法定代表人身份证号
|
||||
func (e *EnterpriseInfo) validateLegalPersonID() error {
|
||||
if e.LegalPersonID == "" {
|
||||
return errors.New("法定代表人身份证号不能为空")
|
||||
}
|
||||
|
||||
// 身份证号格式验证(18位)
|
||||
if len(e.LegalPersonID) != 18 {
|
||||
return errors.New("身份证号必须为18位")
|
||||
}
|
||||
|
||||
pattern := `^[1-9]\d{5}(19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$`
|
||||
matched, err := regexp.MatchString(pattern, e.LegalPersonID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("身份证号格式验证错误: %w", err)
|
||||
}
|
||||
|
||||
if !matched {
|
||||
return errors.New("身份证号格式不正确")
|
||||
}
|
||||
|
||||
// 身份证号校验码验证
|
||||
if !e.validateIDChecksum() {
|
||||
return errors.New("身份证号校验码错误")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateIDChecksum 验证身份证号校验码
|
||||
func (e *EnterpriseInfo) validateIDChecksum() bool {
|
||||
if len(e.LegalPersonID) != 18 {
|
||||
return false
|
||||
}
|
||||
|
||||
// 加权因子
|
||||
weights := []int{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}
|
||||
// 校验码对应表
|
||||
checkCodes := []string{"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"}
|
||||
|
||||
sum := 0
|
||||
for i := 0; i < 17; i++ {
|
||||
digit := int(e.LegalPersonID[i] - '0')
|
||||
sum += digit * weights[i]
|
||||
}
|
||||
|
||||
checkCodeIndex := sum % 11
|
||||
expectedCheckCode := checkCodes[checkCodeIndex]
|
||||
actualCheckCode := strings.ToUpper(string(e.LegalPersonID[17]))
|
||||
|
||||
return expectedCheckCode == actualCheckCode
|
||||
}
|
||||
|
||||
// validateLegalPersonPhone 验证法定代表人手机号
|
||||
func (e *EnterpriseInfo) validateLegalPersonPhone() error {
|
||||
if e.LegalPersonPhone == "" {
|
||||
return errors.New("法定代表人手机号不能为空")
|
||||
}
|
||||
|
||||
// 手机号格式验证(11位数字,1开头)
|
||||
pattern := `^1[3-9]\d{9}$`
|
||||
matched, err := regexp.MatchString(pattern, e.LegalPersonPhone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("手机号格式验证错误: %w", err)
|
||||
}
|
||||
|
||||
if !matched {
|
||||
return errors.New("手机号格式不正确,应为11位数字且以1开头")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateEnterpriseAddress 验证企业地址
|
||||
func (e *EnterpriseInfo) validateEnterpriseAddress() error {
|
||||
if strings.TrimSpace(e.EnterpriseAddress) == "" {
|
||||
return errors.New("企业地址不能为空")
|
||||
}
|
||||
if len(e.EnterpriseAddress) < 5 {
|
||||
return errors.New("企业地址长度不能少于5个字符")
|
||||
}
|
||||
if len(e.EnterpriseAddress) > 200 {
|
||||
return errors.New("企业地址长度不能超过200个字符")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsComplete 检查企业信息是否完整
|
||||
func (e *EnterpriseInfo) IsComplete() bool {
|
||||
return e.CompanyName != "" &&
|
||||
e.UnifiedSocialCode != "" &&
|
||||
e.LegalPersonName != "" &&
|
||||
e.LegalPersonID != "" &&
|
||||
e.LegalPersonPhone != "" &&
|
||||
e.EnterpriseAddress != ""
|
||||
}
|
||||
|
||||
// IsDetailComplete 检查企业详细信息是否完整
|
||||
func (e *EnterpriseInfo) IsDetailComplete() bool {
|
||||
return e.IsComplete() &&
|
||||
e.RegisteredAddress != "" &&
|
||||
e.EnterpriseAddress != ""
|
||||
}
|
||||
|
||||
// GetDisplayName 获取显示用的企业名称
|
||||
func (e *EnterpriseInfo) GetDisplayName() string {
|
||||
if e.CompanyName == "" {
|
||||
return "未知企业"
|
||||
}
|
||||
return e.CompanyName
|
||||
}
|
||||
|
||||
// GetMaskedUnifiedSocialCode 获取脱敏的统一社会信用代码
|
||||
func (e *EnterpriseInfo) GetMaskedUnifiedSocialCode() string {
|
||||
if len(e.UnifiedSocialCode) != 18 {
|
||||
return e.UnifiedSocialCode
|
||||
}
|
||||
|
||||
// 保留前6位和后4位,中间用*替代
|
||||
return e.UnifiedSocialCode[:6] + "********" + e.UnifiedSocialCode[14:]
|
||||
}
|
||||
|
||||
// GetMaskedLegalPersonID 获取脱敏的法定代表人身份证号
|
||||
func (e *EnterpriseInfo) GetMaskedLegalPersonID() string {
|
||||
if len(e.LegalPersonID) != 18 {
|
||||
return e.LegalPersonID
|
||||
}
|
||||
|
||||
// 保留前6位和后4位,中间用*替代
|
||||
return e.LegalPersonID[:6] + "********" + e.LegalPersonID[14:]
|
||||
}
|
||||
|
||||
// GetMaskedLegalPersonPhone 获取脱敏的法定代表人手机号
|
||||
func (e *EnterpriseInfo) GetMaskedLegalPersonPhone() string {
|
||||
if len(e.LegalPersonPhone) != 11 {
|
||||
return e.LegalPersonPhone
|
||||
}
|
||||
|
||||
// 保留前3位和后4位,中间用*替代
|
||||
return e.LegalPersonPhone[:3] + "****" + e.LegalPersonPhone[7:]
|
||||
}
|
||||
|
||||
// Equals 比较两个企业信息是否相等
|
||||
func (e *EnterpriseInfo) Equals(other *EnterpriseInfo) bool {
|
||||
if other == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return e.CompanyName == other.CompanyName &&
|
||||
e.UnifiedSocialCode == other.UnifiedSocialCode &&
|
||||
e.LegalPersonName == other.LegalPersonName &&
|
||||
e.LegalPersonID == other.LegalPersonID &&
|
||||
e.LegalPersonPhone == other.LegalPersonPhone
|
||||
}
|
||||
|
||||
// Clone 创建企业信息的副本
|
||||
func (e *EnterpriseInfo) Clone() *EnterpriseInfo {
|
||||
return &EnterpriseInfo{
|
||||
CompanyName: e.CompanyName,
|
||||
UnifiedSocialCode: e.UnifiedSocialCode,
|
||||
LegalPersonName: e.LegalPersonName,
|
||||
LegalPersonID: e.LegalPersonID,
|
||||
LegalPersonPhone: e.LegalPersonPhone,
|
||||
RegisteredAddress: e.RegisteredAddress,
|
||||
EnterpriseAddress: e.EnterpriseAddress,
|
||||
}
|
||||
}
|
||||
|
||||
// String 返回企业信息的字符串表示
|
||||
func (e *EnterpriseInfo) String() string {
|
||||
return fmt.Sprintf("企业信息[名称:%s, 信用代码:%s, 法人:%s]",
|
||||
e.CompanyName,
|
||||
e.GetMaskedUnifiedSocialCode(),
|
||||
e.LegalPersonName)
|
||||
}
|
||||
|
||||
// ToMap 转换为map格式(用于序列化)
|
||||
func (e *EnterpriseInfo) ToMap() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"company_name": e.CompanyName,
|
||||
"unified_social_code": e.UnifiedSocialCode,
|
||||
"legal_person_name": e.LegalPersonName,
|
||||
"legal_person_id": e.LegalPersonID,
|
||||
"legal_person_phone": e.LegalPersonPhone,
|
||||
"registered_address": e.RegisteredAddress,
|
||||
"enterprise_address": e.EnterpriseAddress,
|
||||
}
|
||||
}
|
||||
|
||||
// FromMap 从map格式创建企业信息(用于反序列化)
|
||||
func FromMap(data map[string]interface{}) (*EnterpriseInfo, error) {
|
||||
getString := func(key string) string {
|
||||
if val, exists := data[key]; exists {
|
||||
if str, ok := val.(string); ok {
|
||||
return strings.TrimSpace(str)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
info := &EnterpriseInfo{
|
||||
CompanyName: getString("company_name"),
|
||||
UnifiedSocialCode: getString("unified_social_code"),
|
||||
LegalPersonName: getString("legal_person_name"),
|
||||
LegalPersonID: getString("legal_person_id"),
|
||||
LegalPersonPhone: getString("legal_person_phone"),
|
||||
RegisteredAddress: getString("registered_address"),
|
||||
EnterpriseAddress: getString("enterprise_address"),
|
||||
}
|
||||
|
||||
if err := info.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("从Map创建企业信息失败: %w", err)
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
215
internal/domains/certification/enums/actor_type.go
Normal file
215
internal/domains/certification/enums/actor_type.go
Normal file
@@ -0,0 +1,215 @@
|
||||
package enums
|
||||
|
||||
// ActorType 操作者类型枚举
|
||||
type ActorType string
|
||||
|
||||
const (
|
||||
// === 操作者类型 ===
|
||||
ActorTypeUser ActorType = "user" // 用户操作
|
||||
ActorTypeSystem ActorType = "system" // 系统操作
|
||||
ActorTypeAdmin ActorType = "admin" // 管理员操作
|
||||
ActorTypeEsign ActorType = "esign" // e签宝回调操作
|
||||
)
|
||||
|
||||
// AllActorTypes 所有操作者类型列表
|
||||
var AllActorTypes = []ActorType{
|
||||
ActorTypeUser,
|
||||
ActorTypeSystem,
|
||||
ActorTypeAdmin,
|
||||
ActorTypeEsign,
|
||||
}
|
||||
|
||||
// IsValidActorType 检查操作者类型是否有效
|
||||
func IsValidActorType(actorType ActorType) bool {
|
||||
for _, validType := range AllActorTypes {
|
||||
if actorType == validType {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetActorTypeName 获取操作者类型的中文名称
|
||||
func GetActorTypeName(actorType ActorType) string {
|
||||
typeNames := map[ActorType]string{
|
||||
ActorTypeUser: "用户",
|
||||
ActorTypeSystem: "系统",
|
||||
ActorTypeAdmin: "管理员",
|
||||
ActorTypeEsign: "e签宝",
|
||||
}
|
||||
|
||||
if name, exists := typeNames[actorType]; exists {
|
||||
return name
|
||||
}
|
||||
return string(actorType)
|
||||
}
|
||||
|
||||
// GetActorTypeDescription 获取操作者类型描述
|
||||
func GetActorTypeDescription(actorType ActorType) string {
|
||||
descriptions := map[ActorType]string{
|
||||
ActorTypeUser: "用户主动操作",
|
||||
ActorTypeSystem: "系统自动操作",
|
||||
ActorTypeAdmin: "管理员操作",
|
||||
ActorTypeEsign: "e签宝回调操作",
|
||||
}
|
||||
|
||||
if desc, exists := descriptions[actorType]; exists {
|
||||
return desc
|
||||
}
|
||||
return string(actorType)
|
||||
}
|
||||
|
||||
// IsAutomatedActor 判断是否为自动化操作者
|
||||
func IsAutomatedActor(actorType ActorType) bool {
|
||||
automatedActors := map[ActorType]bool{
|
||||
ActorTypeUser: false, // 用户手动操作
|
||||
ActorTypeSystem: true, // 系统自动操作
|
||||
ActorTypeAdmin: false, // 管理员手动操作
|
||||
ActorTypeEsign: true, // e签宝自动回调
|
||||
}
|
||||
|
||||
if automated, exists := automatedActors[actorType]; exists {
|
||||
return automated
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsHumanActor 判断是否为人工操作者
|
||||
func IsHumanActor(actorType ActorType) bool {
|
||||
return !IsAutomatedActor(actorType)
|
||||
}
|
||||
|
||||
// GetActorTypePriority 获取操作者类型优先级(用于日志排序等)
|
||||
func GetActorTypePriority(actorType ActorType) int {
|
||||
priorities := map[ActorType]int{
|
||||
ActorTypeUser: 1, // 用户操作最重要
|
||||
ActorTypeAdmin: 2, // 管理员操作次之
|
||||
ActorTypeEsign: 3, // e签宝回调
|
||||
ActorTypeSystem: 4, // 系统操作最后
|
||||
}
|
||||
|
||||
if priority, exists := priorities[actorType]; exists {
|
||||
return priority
|
||||
}
|
||||
return 999
|
||||
}
|
||||
|
||||
// GetPermissionLevel 获取权限级别
|
||||
func GetPermissionLevel(actorType ActorType) int {
|
||||
levels := map[ActorType]int{
|
||||
ActorTypeUser: 1, // 普通用户权限
|
||||
ActorTypeSystem: 2, // 系统权限
|
||||
ActorTypeEsign: 2, // e签宝权限(与系统同级)
|
||||
ActorTypeAdmin: 3, // 管理员最高权限
|
||||
}
|
||||
|
||||
if level, exists := levels[actorType]; exists {
|
||||
return level
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// CanPerformAction 检查操作者是否可以执行指定操作
|
||||
func CanPerformAction(actorType ActorType, action string) bool {
|
||||
permissions := map[ActorType][]string{
|
||||
ActorTypeUser: {
|
||||
"submit_enterprise_info", // 提交企业信息
|
||||
"apply_contract", // 申请合同
|
||||
"view_certification", // 查看认证信息
|
||||
},
|
||||
ActorTypeSystem: {
|
||||
"auto_transition", // 自动状态转换
|
||||
"system_maintenance", // 系统维护
|
||||
"data_cleanup", // 数据清理
|
||||
},
|
||||
ActorTypeAdmin: {
|
||||
"manual_transition", // 手动状态转换
|
||||
"view_all_certifications", // 查看所有认证
|
||||
"system_configuration", // 系统配置
|
||||
"user_management", // 用户管理
|
||||
},
|
||||
ActorTypeEsign: {
|
||||
"callback_notification", // 回调通知
|
||||
"status_update", // 状态更新
|
||||
"verification_result", // 验证结果
|
||||
},
|
||||
}
|
||||
|
||||
if actions, exists := permissions[actorType]; exists {
|
||||
for _, permittedAction := range actions {
|
||||
if permittedAction == action {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetAllowedActions 获取操作者允许执行的所有操作
|
||||
func GetAllowedActions(actorType ActorType) []string {
|
||||
permissions := map[ActorType][]string{
|
||||
ActorTypeUser: {
|
||||
"submit_enterprise_info",
|
||||
"apply_contract",
|
||||
"view_certification",
|
||||
},
|
||||
ActorTypeSystem: {
|
||||
"auto_transition",
|
||||
"system_maintenance",
|
||||
"data_cleanup",
|
||||
},
|
||||
ActorTypeAdmin: {
|
||||
"manual_transition",
|
||||
"view_all_certifications",
|
||||
"system_configuration",
|
||||
"user_management",
|
||||
},
|
||||
ActorTypeEsign: {
|
||||
"callback_notification",
|
||||
"status_update",
|
||||
"verification_result",
|
||||
},
|
||||
}
|
||||
|
||||
if actions, exists := permissions[actorType]; exists {
|
||||
return actions
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// GetActorTypeFromContext 从上下文推断操作者类型
|
||||
func GetActorTypeFromContext(context map[string]interface{}) ActorType {
|
||||
// 检查是否为e签宝回调
|
||||
if _, exists := context["esign_callback"]; exists {
|
||||
return ActorTypeEsign
|
||||
}
|
||||
|
||||
// 检查是否为管理员操作
|
||||
if isAdmin, exists := context["is_admin"]; exists && isAdmin.(bool) {
|
||||
return ActorTypeAdmin
|
||||
}
|
||||
|
||||
// 检查是否为用户操作
|
||||
if userID, exists := context["user_id"]; exists && userID != nil {
|
||||
return ActorTypeUser
|
||||
}
|
||||
|
||||
// 默认为系统操作
|
||||
return ActorTypeSystem
|
||||
}
|
||||
|
||||
// FormatActorInfo 格式化操作者信息
|
||||
func FormatActorInfo(actorType ActorType, actorID string) string {
|
||||
switch actorType {
|
||||
case ActorTypeUser:
|
||||
return "用户(" + actorID + ")"
|
||||
case ActorTypeAdmin:
|
||||
return "管理员(" + actorID + ")"
|
||||
case ActorTypeSystem:
|
||||
return "系统"
|
||||
case ActorTypeEsign:
|
||||
return "e签宝回调"
|
||||
default:
|
||||
return string(actorType) + "(" + actorID + ")"
|
||||
}
|
||||
}
|
||||
302
internal/domains/certification/enums/certification_status.go
Normal file
302
internal/domains/certification/enums/certification_status.go
Normal file
@@ -0,0 +1,302 @@
|
||||
package enums
|
||||
|
||||
// CertificationStatus 认证状态枚举
|
||||
type CertificationStatus string
|
||||
|
||||
const (
|
||||
// === 主流程状态 ===
|
||||
StatusPending CertificationStatus = "pending" // 待认证
|
||||
StatusInfoPendingReview CertificationStatus = "info_pending_review" // 企业信息待人工审核
|
||||
StatusInfoSubmitted CertificationStatus = "info_submitted" // 已提交企业信息(审核通过)
|
||||
StatusEnterpriseVerified CertificationStatus = "enterprise_verified" // 已企业认证
|
||||
StatusContractApplied CertificationStatus = "contract_applied" // 已申请签署合同
|
||||
StatusContractSigned CertificationStatus = "contract_signed" // 已签署合同
|
||||
StatusCompleted CertificationStatus = "completed" // 认证完成
|
||||
|
||||
// === 失败状态 ===
|
||||
StatusInfoRejected CertificationStatus = "info_rejected" // 企业信息被拒绝
|
||||
StatusContractRejected CertificationStatus = "contract_rejected" // 合同被拒签
|
||||
StatusContractExpired CertificationStatus = "contract_expired" // 合同签署超时
|
||||
)
|
||||
|
||||
// AllStatuses 所有有效状态列表
|
||||
var AllStatuses = []CertificationStatus{
|
||||
StatusPending,
|
||||
StatusInfoPendingReview,
|
||||
StatusInfoSubmitted,
|
||||
StatusEnterpriseVerified,
|
||||
StatusContractApplied,
|
||||
StatusContractSigned,
|
||||
StatusCompleted,
|
||||
StatusInfoRejected,
|
||||
StatusContractRejected,
|
||||
StatusContractExpired,
|
||||
}
|
||||
|
||||
// MainFlowStatuses 主流程状态列表
|
||||
var MainFlowStatuses = []CertificationStatus{
|
||||
StatusPending,
|
||||
StatusInfoPendingReview,
|
||||
StatusInfoSubmitted,
|
||||
StatusEnterpriseVerified,
|
||||
StatusContractApplied,
|
||||
StatusContractSigned,
|
||||
}
|
||||
|
||||
// FailureStatuses 失败状态列表
|
||||
var FailureStatuses = []CertificationStatus{
|
||||
StatusInfoRejected,
|
||||
StatusContractRejected,
|
||||
StatusContractExpired,
|
||||
}
|
||||
|
||||
// IsValidStatus 检查状态是否有效
|
||||
func IsValidStatus(status CertificationStatus) bool {
|
||||
for _, validStatus := range AllStatuses {
|
||||
if status == validStatus {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetStatusName 获取状态的中文名称
|
||||
func GetStatusName(status CertificationStatus) string {
|
||||
statusNames := map[CertificationStatus]string{
|
||||
StatusPending: "待认证",
|
||||
StatusInfoPendingReview: "企业信息待审核",
|
||||
StatusInfoSubmitted: "已提交企业信息",
|
||||
StatusEnterpriseVerified: "已企业认证",
|
||||
StatusContractApplied: "已申请签署合同",
|
||||
StatusContractSigned: "已签署合同",
|
||||
StatusCompleted: "认证完成",
|
||||
StatusInfoRejected: "企业信息被拒绝",
|
||||
StatusContractRejected: "合同被拒签",
|
||||
StatusContractExpired: "合同签署超时",
|
||||
}
|
||||
|
||||
if name, exists := statusNames[status]; exists {
|
||||
return name
|
||||
}
|
||||
return string(status)
|
||||
}
|
||||
|
||||
// IsFinalStatus 判断是否为最终状态
|
||||
func IsFinalStatus(status CertificationStatus) bool {
|
||||
return status == StatusCompleted
|
||||
}
|
||||
|
||||
// IsFailureStatus 判断是否为失败状态
|
||||
func IsFailureStatus(status CertificationStatus) bool {
|
||||
for _, failureStatus := range FailureStatuses {
|
||||
if status == failureStatus {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsMainFlowStatus 判断是否为主流程状态
|
||||
func IsMainFlowStatus(status CertificationStatus) bool {
|
||||
for _, mainStatus := range MainFlowStatuses {
|
||||
if status == mainStatus {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetStatusCategory 获取状态分类
|
||||
func GetStatusCategory(status CertificationStatus) string {
|
||||
if IsMainFlowStatus(status) {
|
||||
return "主流程"
|
||||
}
|
||||
if IsFailureStatus(status) {
|
||||
return "失败状态"
|
||||
}
|
||||
if status == StatusCompleted {
|
||||
return "完成"
|
||||
}
|
||||
return "未知"
|
||||
}
|
||||
|
||||
// GetStatusPriority 获取状态优先级(用于排序)
|
||||
func GetStatusPriority(status CertificationStatus) int {
|
||||
priorities := map[CertificationStatus]int{
|
||||
StatusPending: 1,
|
||||
StatusInfoPendingReview: 2,
|
||||
StatusInfoSubmitted: 3,
|
||||
StatusEnterpriseVerified: 4,
|
||||
StatusContractApplied: 5,
|
||||
StatusContractSigned: 6,
|
||||
StatusCompleted: 7,
|
||||
StatusInfoRejected: 8,
|
||||
StatusContractRejected: 9,
|
||||
StatusContractExpired: 10,
|
||||
}
|
||||
|
||||
if priority, exists := priorities[status]; exists {
|
||||
return priority
|
||||
}
|
||||
return 999
|
||||
}
|
||||
|
||||
// GetProgressPercentage 获取进度百分比
|
||||
func GetProgressPercentage(status CertificationStatus) int {
|
||||
progressMap := map[CertificationStatus]int{
|
||||
StatusPending: 0,
|
||||
StatusInfoPendingReview: 15,
|
||||
StatusInfoSubmitted: 25,
|
||||
StatusEnterpriseVerified: 50,
|
||||
StatusContractApplied: 75,
|
||||
StatusContractSigned: 100,
|
||||
StatusCompleted: 100,
|
||||
StatusInfoRejected: 25,
|
||||
StatusContractRejected: 75,
|
||||
StatusContractExpired: 75,
|
||||
}
|
||||
|
||||
if progress, exists := progressMap[status]; exists {
|
||||
return progress
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// IsUserActionRequired 检查是否需要用户操作
|
||||
func IsUserActionRequired(status CertificationStatus) bool {
|
||||
userActionRequired := map[CertificationStatus]bool{
|
||||
StatusPending: true, // 需要提交企业信息
|
||||
StatusInfoPendingReview: false, // 等待人工审核
|
||||
StatusInfoSubmitted: false, // 等待完成企业认证
|
||||
StatusEnterpriseVerified: true, // 需要申请合同
|
||||
StatusContractApplied: true, // 需要签署合同
|
||||
StatusContractSigned: false, // 合同已签署,等待系统处理
|
||||
StatusCompleted: false, // 已完成
|
||||
StatusInfoRejected: true, // 需要重新提交
|
||||
StatusContractRejected: true, // 需要重新申请
|
||||
StatusContractExpired: true, // 需要重新申请
|
||||
}
|
||||
|
||||
if required, exists := userActionRequired[status]; exists {
|
||||
return required
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetUserActionHint 获取用户操作提示
|
||||
func GetUserActionHint(status CertificationStatus) string {
|
||||
hints := map[CertificationStatus]string{
|
||||
StatusPending: "请提交企业信息",
|
||||
StatusInfoPendingReview: "企业信息已提交,请等待管理员审核",
|
||||
StatusInfoSubmitted: "请完成企业认证",
|
||||
StatusEnterpriseVerified: "企业认证完成,请申请签署合同",
|
||||
StatusContractApplied: "请在规定时间内完成合同签署",
|
||||
StatusContractSigned: "合同已签署,等待系统处理",
|
||||
StatusCompleted: "认证已完成",
|
||||
StatusInfoRejected: "企业信息验证失败,请修正后重新提交",
|
||||
StatusContractRejected: "合同签署被拒绝,可重新申请",
|
||||
StatusContractExpired: "合同签署已超时,请重新申请",
|
||||
}
|
||||
|
||||
if hint, exists := hints[status]; exists {
|
||||
return hint
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetNextValidStatuses 获取当前状态的下一个有效状态列表
|
||||
func GetNextValidStatuses(currentStatus CertificationStatus) []CertificationStatus {
|
||||
nextStatusMap := map[CertificationStatus][]CertificationStatus{
|
||||
StatusPending: {
|
||||
StatusInfoPendingReview, // 用户提交企业信息,进入待审核
|
||||
StatusInfoSubmitted, // 暂时跳过人工审核,直接进入已提交
|
||||
StatusCompleted,
|
||||
},
|
||||
StatusInfoPendingReview: {
|
||||
StatusInfoSubmitted,
|
||||
StatusInfoRejected,
|
||||
StatusCompleted,
|
||||
},
|
||||
StatusInfoSubmitted: {
|
||||
StatusEnterpriseVerified,
|
||||
StatusInfoRejected,
|
||||
// 管理员/系统可直接完成认证
|
||||
StatusCompleted,
|
||||
},
|
||||
StatusEnterpriseVerified: {
|
||||
StatusContractApplied,
|
||||
// 管理员/系统可直接完成认证(无合同场景)
|
||||
StatusCompleted,
|
||||
},
|
||||
StatusContractApplied: {
|
||||
StatusContractSigned,
|
||||
StatusContractRejected,
|
||||
StatusContractExpired,
|
||||
// 管理员/系统可在合同流程中直接完成认证
|
||||
StatusCompleted,
|
||||
},
|
||||
StatusContractSigned: {
|
||||
StatusCompleted, // 可以转换到完成状态
|
||||
},
|
||||
StatusCompleted: {
|
||||
// 最终状态,无后续状态
|
||||
},
|
||||
StatusInfoRejected: {
|
||||
StatusInfoSubmitted, // 可以重新提交
|
||||
// 管理员/系统可直接标记为完成
|
||||
StatusCompleted,
|
||||
},
|
||||
StatusContractRejected: {
|
||||
StatusEnterpriseVerified, // 重置到企业认证状态
|
||||
// 管理员/系统可直接标记为完成
|
||||
StatusCompleted,
|
||||
},
|
||||
StatusContractExpired: {
|
||||
StatusEnterpriseVerified, // 重置到企业认证状态
|
||||
// 管理员/系统可直接标记为完成
|
||||
StatusCompleted,
|
||||
},
|
||||
}
|
||||
|
||||
if nextStatuses, exists := nextStatusMap[currentStatus]; exists {
|
||||
return nextStatuses
|
||||
}
|
||||
return []CertificationStatus{}
|
||||
}
|
||||
|
||||
// CanTransitionTo 检查是否可以从当前状态转换到目标状态
|
||||
func CanTransitionTo(currentStatus, targetStatus CertificationStatus) bool {
|
||||
validNextStatuses := GetNextValidStatuses(currentStatus)
|
||||
for _, validStatus := range validNextStatuses {
|
||||
if validStatus == targetStatus {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetTransitionReason 获取状态转换的原因描述
|
||||
func GetTransitionReason(from, to CertificationStatus) string {
|
||||
transitionReasons := map[string]string{
|
||||
string(StatusPending) + "->" + string(StatusInfoPendingReview): "用户提交企业信息,等待人工审核",
|
||||
string(StatusInfoPendingReview) + "->" + string(StatusInfoSubmitted): "管理员审核通过",
|
||||
string(StatusInfoPendingReview) + "->" + string(StatusInfoRejected): "管理员审核拒绝",
|
||||
string(StatusPending) + "->" + string(StatusInfoSubmitted): "用户提交企业信息",
|
||||
string(StatusInfoSubmitted) + "->" + string(StatusEnterpriseVerified): "e签宝企业认证成功",
|
||||
string(StatusInfoSubmitted) + "->" + string(StatusInfoRejected): "e签宝企业认证失败",
|
||||
string(StatusEnterpriseVerified) + "->" + string(StatusContractApplied): "用户申请签署合同",
|
||||
string(StatusContractApplied) + "->" + string(StatusContractSigned): "e签宝合同签署成功",
|
||||
string(StatusContractSigned) + "->" + string(StatusCompleted): "系统处理完成,认证成功",
|
||||
string(StatusContractApplied) + "->" + string(StatusContractRejected): "用户拒绝签署合同",
|
||||
string(StatusContractApplied) + "->" + string(StatusContractExpired): "合同签署超时",
|
||||
string(StatusInfoRejected) + "->" + string(StatusInfoSubmitted): "用户重新提交企业信息",
|
||||
string(StatusContractRejected) + "->" + string(StatusEnterpriseVerified): "重置状态,准备重新申请",
|
||||
string(StatusContractExpired) + "->" + string(StatusEnterpriseVerified): "重置状态,准备重新申请",
|
||||
}
|
||||
|
||||
key := string(from) + "->" + string(to)
|
||||
if reason, exists := transitionReasons[key]; exists {
|
||||
return reason
|
||||
}
|
||||
return "未知转换"
|
||||
}
|
||||
272
internal/domains/certification/enums/failure_reason.go
Normal file
272
internal/domains/certification/enums/failure_reason.go
Normal file
@@ -0,0 +1,272 @@
|
||||
package enums
|
||||
|
||||
// FailureReason 失败原因枚举
|
||||
type FailureReason string
|
||||
|
||||
const (
|
||||
// === 企业信息验证失败原因 ===
|
||||
FailureReasonEnterpriseNotExists FailureReason = "enterprise_not_exists" // 企业不存在
|
||||
FailureReasonEnterpriseInfoMismatch FailureReason = "enterprise_info_mismatch" // 企业信息不匹配
|
||||
FailureReasonEnterpriseStatusAbnormal FailureReason = "enterprise_status_abnormal" // 企业状态异常
|
||||
FailureReasonLegalPersonMismatch FailureReason = "legal_person_mismatch" // 法定代表人信息不匹配
|
||||
FailureReasonEsignVerificationFailed FailureReason = "esign_verification_failed" // e签宝验证失败
|
||||
FailureReasonInvalidDocument FailureReason = "invalid_document" // 证件信息无效
|
||||
FailureReasonManualReviewRejected FailureReason = "manual_review_rejected" // 人工审核拒绝
|
||||
|
||||
// === 合同签署失败原因 ===
|
||||
FailureReasonContractRejectedByUser FailureReason = "contract_rejected_by_user" // 用户拒绝签署
|
||||
FailureReasonContractExpired FailureReason = "contract_expired" // 合同签署超时
|
||||
FailureReasonSignProcessFailed FailureReason = "sign_process_failed" // 签署流程失败
|
||||
FailureReasonContractGenFailed FailureReason = "contract_gen_failed" // 合同生成失败
|
||||
FailureReasonEsignFlowError FailureReason = "esign_flow_error" // e签宝流程错误
|
||||
|
||||
// === 系统错误原因 ===
|
||||
FailureReasonSystemError FailureReason = "system_error" // 系统错误
|
||||
FailureReasonNetworkError FailureReason = "network_error" // 网络错误
|
||||
FailureReasonTimeout FailureReason = "timeout" // 操作超时
|
||||
FailureReasonUnknownError FailureReason = "unknown_error" // 未知错误
|
||||
)
|
||||
|
||||
// AllFailureReasons 所有失败原因列表
|
||||
var AllFailureReasons = []FailureReason{
|
||||
// 企业信息验证失败
|
||||
FailureReasonEnterpriseNotExists,
|
||||
FailureReasonEnterpriseInfoMismatch,
|
||||
FailureReasonEnterpriseStatusAbnormal,
|
||||
FailureReasonLegalPersonMismatch,
|
||||
FailureReasonEsignVerificationFailed,
|
||||
FailureReasonInvalidDocument,
|
||||
FailureReasonManualReviewRejected,
|
||||
// 合同签署失败
|
||||
FailureReasonContractRejectedByUser,
|
||||
FailureReasonContractExpired,
|
||||
FailureReasonSignProcessFailed,
|
||||
FailureReasonContractGenFailed,
|
||||
FailureReasonEsignFlowError,
|
||||
|
||||
// 系统错误
|
||||
FailureReasonSystemError,
|
||||
FailureReasonNetworkError,
|
||||
FailureReasonTimeout,
|
||||
FailureReasonUnknownError,
|
||||
}
|
||||
|
||||
// EnterpriseVerificationFailureReasons 企业验证失败原因列表
|
||||
var EnterpriseVerificationFailureReasons = []FailureReason{
|
||||
FailureReasonEnterpriseNotExists,
|
||||
FailureReasonEnterpriseInfoMismatch,
|
||||
FailureReasonEnterpriseStatusAbnormal,
|
||||
FailureReasonLegalPersonMismatch,
|
||||
FailureReasonEsignVerificationFailed,
|
||||
FailureReasonInvalidDocument,
|
||||
}
|
||||
|
||||
// ContractSignFailureReasons 合同签署失败原因列表
|
||||
var ContractSignFailureReasons = []FailureReason{
|
||||
FailureReasonContractRejectedByUser,
|
||||
FailureReasonContractExpired,
|
||||
FailureReasonSignProcessFailed,
|
||||
FailureReasonContractGenFailed,
|
||||
FailureReasonEsignFlowError,
|
||||
}
|
||||
|
||||
// SystemErrorReasons 系统错误原因列表
|
||||
var SystemErrorReasons = []FailureReason{
|
||||
FailureReasonSystemError,
|
||||
FailureReasonNetworkError,
|
||||
FailureReasonTimeout,
|
||||
FailureReasonUnknownError,
|
||||
}
|
||||
|
||||
// IsValidFailureReason 检查失败原因是否有效
|
||||
func IsValidFailureReason(reason FailureReason) bool {
|
||||
for _, validReason := range AllFailureReasons {
|
||||
if reason == validReason {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetFailureReasonName 获取失败原因的中文名称
|
||||
func GetFailureReasonName(reason FailureReason) string {
|
||||
reasonNames := map[FailureReason]string{
|
||||
// 企业信息验证失败
|
||||
FailureReasonEnterpriseNotExists: "企业不存在",
|
||||
FailureReasonEnterpriseInfoMismatch: "企业信息不匹配",
|
||||
FailureReasonEnterpriseStatusAbnormal: "企业状态异常",
|
||||
FailureReasonLegalPersonMismatch: "法定代表人信息不匹配",
|
||||
FailureReasonEsignVerificationFailed: "e签宝验证失败",
|
||||
FailureReasonInvalidDocument: "证件信息无效",
|
||||
FailureReasonManualReviewRejected: "人工审核拒绝",
|
||||
// 合同签署失败
|
||||
FailureReasonContractRejectedByUser: "用户拒绝签署",
|
||||
FailureReasonContractExpired: "合同签署超时",
|
||||
FailureReasonSignProcessFailed: "签署流程失败",
|
||||
FailureReasonContractGenFailed: "合同生成失败",
|
||||
FailureReasonEsignFlowError: "e签宝流程错误",
|
||||
|
||||
// 系统错误
|
||||
FailureReasonSystemError: "系统错误",
|
||||
FailureReasonNetworkError: "网络错误",
|
||||
FailureReasonTimeout: "操作超时",
|
||||
FailureReasonUnknownError: "未知错误",
|
||||
}
|
||||
|
||||
if name, exists := reasonNames[reason]; exists {
|
||||
return name
|
||||
}
|
||||
return string(reason)
|
||||
}
|
||||
|
||||
// GetFailureReasonCategory 获取失败原因分类
|
||||
func GetFailureReasonCategory(reason FailureReason) string {
|
||||
categories := map[FailureReason]string{
|
||||
// 企业信息验证失败
|
||||
FailureReasonEnterpriseNotExists: "企业验证",
|
||||
FailureReasonEnterpriseInfoMismatch: "企业验证",
|
||||
FailureReasonEnterpriseStatusAbnormal: "企业验证",
|
||||
FailureReasonLegalPersonMismatch: "企业验证",
|
||||
FailureReasonEsignVerificationFailed: "企业验证",
|
||||
FailureReasonInvalidDocument: "企业验证",
|
||||
FailureReasonManualReviewRejected: "人工审核",
|
||||
// 合同签署失败
|
||||
FailureReasonContractRejectedByUser: "合同签署",
|
||||
FailureReasonContractExpired: "合同签署",
|
||||
FailureReasonSignProcessFailed: "合同签署",
|
||||
FailureReasonContractGenFailed: "合同签署",
|
||||
FailureReasonEsignFlowError: "合同签署",
|
||||
|
||||
// 系统错误
|
||||
FailureReasonSystemError: "系统错误",
|
||||
FailureReasonNetworkError: "系统错误",
|
||||
FailureReasonTimeout: "系统错误",
|
||||
FailureReasonUnknownError: "系统错误",
|
||||
}
|
||||
|
||||
if category, exists := categories[reason]; exists {
|
||||
return category
|
||||
}
|
||||
return "未知"
|
||||
}
|
||||
|
||||
// IsEnterpriseVerificationFailure 判断是否为企业验证失败
|
||||
func IsEnterpriseVerificationFailure(reason FailureReason) bool {
|
||||
for _, verifyReason := range EnterpriseVerificationFailureReasons {
|
||||
if reason == verifyReason {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsContractSignFailure 判断是否为合同签署失败
|
||||
func IsContractSignFailure(reason FailureReason) bool {
|
||||
for _, signReason := range ContractSignFailureReasons {
|
||||
if reason == signReason {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsSystemError 判断是否为系统错误
|
||||
func IsSystemError(reason FailureReason) bool {
|
||||
for _, systemReason := range SystemErrorReasons {
|
||||
if reason == systemReason {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetSuggestedAction 获取建议的后续操作
|
||||
func GetSuggestedAction(reason FailureReason) string {
|
||||
actions := map[FailureReason]string{
|
||||
// 企业信息验证失败
|
||||
FailureReasonEnterpriseNotExists: "请检查企业名称和统一社会信用代码是否正确",
|
||||
FailureReasonEnterpriseInfoMismatch: "请核对企业信息是否与工商登记信息一致",
|
||||
FailureReasonEnterpriseStatusAbnormal: "请确认企业状态正常,如有疑问请联系客服",
|
||||
FailureReasonLegalPersonMismatch: "请核对法定代表人信息是否正确",
|
||||
FailureReasonEsignVerificationFailed: "请稍后重试,如持续失败请联系客服",
|
||||
FailureReasonInvalidDocument: "请检查证件信息是否有效",
|
||||
FailureReasonManualReviewRejected: "请根据审核意见修正后重新提交,或联系客服",
|
||||
// 合同签署失败
|
||||
FailureReasonContractRejectedByUser: "您可以重新申请签署合同",
|
||||
FailureReasonContractExpired: "请重新申请签署合同",
|
||||
FailureReasonSignProcessFailed: "请重新尝试签署,如持续失败请联系客服",
|
||||
FailureReasonContractGenFailed: "系统正在处理,请稍后重试",
|
||||
FailureReasonEsignFlowError: "请稍后重试,如持续失败请联系客服",
|
||||
|
||||
// 系统错误
|
||||
FailureReasonSystemError: "系统暂时不可用,请稍后重试",
|
||||
FailureReasonNetworkError: "网络连接异常,请检查网络后重试",
|
||||
FailureReasonTimeout: "操作超时,请重新尝试",
|
||||
FailureReasonUnknownError: "发生未知错误,请联系客服",
|
||||
}
|
||||
|
||||
if action, exists := actions[reason]; exists {
|
||||
return action
|
||||
}
|
||||
return "请联系客服处理"
|
||||
}
|
||||
|
||||
// IsRetryable 判断是否可以重试
|
||||
func IsRetryable(reason FailureReason) bool {
|
||||
retryableReasons := map[FailureReason]bool{
|
||||
// 企业信息验证失败 - 用户数据问题,可重试
|
||||
FailureReasonEnterpriseNotExists: true,
|
||||
FailureReasonEnterpriseInfoMismatch: true,
|
||||
FailureReasonEnterpriseStatusAbnormal: false, // 企业状态问题,需要外部解决
|
||||
FailureReasonLegalPersonMismatch: true,
|
||||
FailureReasonEsignVerificationFailed: true, // 可能是临时问题
|
||||
FailureReasonInvalidDocument: true,
|
||||
FailureReasonManualReviewRejected: true, // 用户可修正后重新提交
|
||||
// 合同签署失败
|
||||
FailureReasonContractRejectedByUser: true, // 用户可以改变主意
|
||||
FailureReasonContractExpired: true, // 可以重新申请
|
||||
FailureReasonSignProcessFailed: true, // 可能是临时问题
|
||||
FailureReasonContractGenFailed: true, // 可能是临时问题
|
||||
FailureReasonEsignFlowError: true, // 可能是临时问题
|
||||
|
||||
// 系统错误 - 大部分可重试
|
||||
FailureReasonSystemError: true,
|
||||
FailureReasonNetworkError: true,
|
||||
FailureReasonTimeout: true,
|
||||
FailureReasonUnknownError: false, // 未知错误,不建议自动重试
|
||||
}
|
||||
|
||||
if retryable, exists := retryableReasons[reason]; exists {
|
||||
return retryable
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetRetrySuggestion 获取重试建议
|
||||
func GetRetrySuggestion(reason FailureReason) string {
|
||||
if !IsRetryable(reason) {
|
||||
return "此问题不建议重试,请联系客服处理"
|
||||
}
|
||||
|
||||
suggestions := map[FailureReason]string{
|
||||
FailureReasonEnterpriseNotExists: "请修正企业信息后重新提交",
|
||||
FailureReasonEnterpriseInfoMismatch: "请核对企业信息后重新提交",
|
||||
FailureReasonLegalPersonMismatch: "请确认法定代表人信息后重新提交",
|
||||
FailureReasonEsignVerificationFailed: "请稍后重新尝试",
|
||||
FailureReasonInvalidDocument: "请检查证件信息后重新提交",
|
||||
FailureReasonManualReviewRejected: "请根据审核意见修正企业信息后重新提交",
|
||||
FailureReasonContractRejectedByUser: "如需要可重新申请合同",
|
||||
FailureReasonContractExpired: "请重新申请合同签署",
|
||||
FailureReasonSignProcessFailed: "请重新尝试签署",
|
||||
FailureReasonContractGenFailed: "请稍后重新申请",
|
||||
FailureReasonEsignFlowError: "请稍后重新尝试",
|
||||
FailureReasonSystemError: "请稍后重试",
|
||||
FailureReasonNetworkError: "请检查网络连接后重试",
|
||||
FailureReasonTimeout: "请重新尝试操作",
|
||||
}
|
||||
|
||||
if suggestion, exists := suggestions[reason]; exists {
|
||||
return suggestion
|
||||
}
|
||||
return "请重新尝试操作"
|
||||
}
|
||||
229
internal/domains/certification/events/certification_events.go
Normal file
229
internal/domains/certification/events/certification_events.go
Normal file
@@ -0,0 +1,229 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"hyapi-server/internal/domains/certification/entities"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// 事件类型常量
|
||||
const (
|
||||
EventTypeCertificationCreated = "certification.created"
|
||||
EventTypeEnterpriseInfoSubmitted = "enterprise.info.submitted"
|
||||
EventTypeEnterpriseVerified = "enterprise.verified"
|
||||
EventTypeContractApplied = "contract.applied"
|
||||
EventTypeContractSigned = "contract.signed"
|
||||
EventTypeCertificationCompleted = "certification.completed"
|
||||
)
|
||||
|
||||
// BaseCertificationEvent 认证事件基础结构
|
||||
type BaseCertificationEvent struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Version string `json:"version"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Source string `json:"source"`
|
||||
AggregateID string `json:"aggregate_id"`
|
||||
AggregateType string `json:"aggregate_type"`
|
||||
Metadata map[string]interface{} `json:"metadata"`
|
||||
Payload interface{} `json:"payload"`
|
||||
}
|
||||
|
||||
// 实现 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 }
|
||||
func (e *BaseCertificationEvent) GetTimestamp() time.Time { return e.Timestamp }
|
||||
func (e *BaseCertificationEvent) GetSource() string { return e.Source }
|
||||
func (e *BaseCertificationEvent) GetAggregateID() string { return e.AggregateID }
|
||||
func (e *BaseCertificationEvent) GetAggregateType() string { return e.AggregateType }
|
||||
func (e *BaseCertificationEvent) GetPayload() interface{} { return e.Payload }
|
||||
func (e *BaseCertificationEvent) GetMetadata() map[string]interface{} { return e.Metadata }
|
||||
func (e *BaseCertificationEvent) Marshal() ([]byte, error) { return json.Marshal(e) }
|
||||
func (e *BaseCertificationEvent) Unmarshal(data []byte) error { return json.Unmarshal(data, e) }
|
||||
func (e *BaseCertificationEvent) GetDomainVersion() string { return e.Version }
|
||||
func (e *BaseCertificationEvent) GetCausationID() string { return e.ID }
|
||||
func (e *BaseCertificationEvent) GetCorrelationID() string { return e.ID }
|
||||
|
||||
// NewBaseCertificationEvent 创建基础认证事件
|
||||
func NewBaseCertificationEvent(eventType, aggregateID string, payload interface{}) *BaseCertificationEvent {
|
||||
return &BaseCertificationEvent{
|
||||
ID: generateEventID(),
|
||||
Type: eventType,
|
||||
Version: "1.0",
|
||||
Timestamp: time.Now(),
|
||||
Source: "certification-service",
|
||||
AggregateID: aggregateID,
|
||||
AggregateType: "Certification",
|
||||
Metadata: make(map[string]interface{}),
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
|
||||
// CertificationCreatedEvent 认证申请创建事件
|
||||
type CertificationCreatedEvent struct {
|
||||
*BaseCertificationEvent
|
||||
Data struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
Status string `json:"status"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// NewCertificationCreatedEvent 创建认证申请创建事件
|
||||
func NewCertificationCreatedEvent(certification *entities.Certification) *CertificationCreatedEvent {
|
||||
event := &CertificationCreatedEvent{
|
||||
BaseCertificationEvent: NewBaseCertificationEvent(
|
||||
EventTypeCertificationCreated,
|
||||
certification.ID,
|
||||
nil,
|
||||
),
|
||||
}
|
||||
event.Data.CertificationID = certification.ID
|
||||
event.Data.UserID = certification.UserID
|
||||
event.Data.Status = string(certification.Status)
|
||||
event.Payload = event.Data
|
||||
return event
|
||||
}
|
||||
|
||||
// EnterpriseInfoSubmittedEvent 企业信息提交事件
|
||||
type EnterpriseInfoSubmittedEvent struct {
|
||||
*BaseCertificationEvent
|
||||
Data struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
Status string `json:"status"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// NewEnterpriseInfoSubmittedEvent 创建企业信息提交事件
|
||||
func NewEnterpriseInfoSubmittedEvent(certification *entities.Certification) *EnterpriseInfoSubmittedEvent {
|
||||
event := &EnterpriseInfoSubmittedEvent{
|
||||
BaseCertificationEvent: NewBaseCertificationEvent(
|
||||
EventTypeEnterpriseInfoSubmitted,
|
||||
certification.ID,
|
||||
nil,
|
||||
),
|
||||
}
|
||||
event.Data.CertificationID = certification.ID
|
||||
event.Data.UserID = certification.UserID
|
||||
event.Data.Status = string(certification.Status)
|
||||
event.Payload = event.Data
|
||||
return event
|
||||
}
|
||||
|
||||
// EnterpriseVerifiedEvent 企业认证完成事件
|
||||
type EnterpriseVerifiedEvent struct {
|
||||
*BaseCertificationEvent
|
||||
Data struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
Status string `json:"status"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// NewEnterpriseVerifiedEvent 创建企业认证完成事件
|
||||
func NewEnterpriseVerifiedEvent(certification *entities.Certification) *EnterpriseVerifiedEvent {
|
||||
event := &EnterpriseVerifiedEvent{
|
||||
BaseCertificationEvent: NewBaseCertificationEvent(
|
||||
EventTypeEnterpriseVerified,
|
||||
certification.ID,
|
||||
nil,
|
||||
),
|
||||
}
|
||||
event.Data.CertificationID = certification.ID
|
||||
event.Data.UserID = certification.UserID
|
||||
event.Data.Status = string(certification.Status)
|
||||
event.Payload = event.Data
|
||||
return event
|
||||
}
|
||||
|
||||
// ContractAppliedEvent 合同申请事件
|
||||
type ContractAppliedEvent struct {
|
||||
*BaseCertificationEvent
|
||||
Data struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
Status string `json:"status"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// NewContractAppliedEvent 创建合同申请事件
|
||||
func NewContractAppliedEvent(certification *entities.Certification) *ContractAppliedEvent {
|
||||
event := &ContractAppliedEvent{
|
||||
BaseCertificationEvent: NewBaseCertificationEvent(
|
||||
EventTypeContractApplied,
|
||||
certification.ID,
|
||||
nil,
|
||||
),
|
||||
}
|
||||
event.Data.CertificationID = certification.ID
|
||||
event.Data.UserID = certification.UserID
|
||||
event.Data.Status = string(certification.Status)
|
||||
event.Payload = event.Data
|
||||
return event
|
||||
}
|
||||
|
||||
// ContractSignedEvent 合同签署事件
|
||||
type ContractSignedEvent struct {
|
||||
*BaseCertificationEvent
|
||||
Data struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
ContractURL string `json:"contract_url"`
|
||||
Status string `json:"status"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// NewContractSignedEvent 创建合同签署事件
|
||||
func NewContractSignedEvent(certification *entities.Certification, contractURL string) *ContractSignedEvent {
|
||||
event := &ContractSignedEvent{
|
||||
BaseCertificationEvent: NewBaseCertificationEvent(
|
||||
EventTypeContractSigned,
|
||||
certification.ID,
|
||||
nil,
|
||||
),
|
||||
}
|
||||
event.Data.CertificationID = certification.ID
|
||||
event.Data.UserID = certification.UserID
|
||||
event.Data.ContractURL = contractURL
|
||||
event.Data.Status = string(certification.Status)
|
||||
event.Payload = event.Data
|
||||
return event
|
||||
}
|
||||
|
||||
// CertificationCompletedEvent 认证完成事件
|
||||
type CertificationCompletedEvent struct {
|
||||
*BaseCertificationEvent
|
||||
Data struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
CompletedAt string `json:"completed_at"`
|
||||
Status string `json:"status"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// NewCertificationCompletedEvent 创建认证完成事件
|
||||
func NewCertificationCompletedEvent(certification *entities.Certification) *CertificationCompletedEvent {
|
||||
event := &CertificationCompletedEvent{
|
||||
BaseCertificationEvent: NewBaseCertificationEvent(
|
||||
EventTypeCertificationCompleted,
|
||||
certification.ID,
|
||||
nil,
|
||||
),
|
||||
}
|
||||
event.Data.CertificationID = certification.ID
|
||||
event.Data.UserID = certification.UserID
|
||||
event.Data.CompletedAt = time.Now().Format(time.RFC3339)
|
||||
event.Data.Status = string(certification.Status)
|
||||
event.Payload = event.Data
|
||||
return event
|
||||
}
|
||||
|
||||
// 工具函数
|
||||
func generateEventID() string {
|
||||
return uuid.New().String()
|
||||
}
|
||||
326
internal/domains/certification/events/event_handlers.go
Normal file
326
internal/domains/certification/events/event_handlers.go
Normal file
@@ -0,0 +1,326 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"hyapi-server/internal/infrastructure/external/notification"
|
||||
"hyapi-server/internal/shared/interfaces"
|
||||
)
|
||||
|
||||
// CertificationEventHandler 认证事件处理器
|
||||
type CertificationEventHandler struct {
|
||||
logger *zap.Logger
|
||||
notification notification.WeChatWorkService
|
||||
name string
|
||||
eventTypes []string
|
||||
isAsync bool
|
||||
}
|
||||
|
||||
// NewCertificationEventHandler 创建认证事件处理器
|
||||
func NewCertificationEventHandler(logger *zap.Logger, notification notification.WeChatWorkService) *CertificationEventHandler {
|
||||
return &CertificationEventHandler{
|
||||
logger: logger,
|
||||
notification: notification,
|
||||
name: "certification-event-handler",
|
||||
eventTypes: []string{
|
||||
EventTypeCertificationCreated,
|
||||
EventTypeEnterpriseInfoSubmitted,
|
||||
EventTypeEnterpriseVerified,
|
||||
EventTypeContractApplied,
|
||||
EventTypeContractSigned,
|
||||
EventTypeCertificationCompleted,
|
||||
},
|
||||
isAsync: true,
|
||||
}
|
||||
}
|
||||
|
||||
// GetName 获取处理器名称
|
||||
func (h *CertificationEventHandler) GetName() string {
|
||||
return h.name
|
||||
}
|
||||
|
||||
// GetEventTypes 获取支持的事件类型
|
||||
func (h *CertificationEventHandler) GetEventTypes() []string {
|
||||
return h.eventTypes
|
||||
}
|
||||
|
||||
// IsAsync 是否为异步处理器
|
||||
func (h *CertificationEventHandler) IsAsync() bool {
|
||||
return h.isAsync
|
||||
}
|
||||
|
||||
// GetRetryConfig 获取重试配置
|
||||
func (h *CertificationEventHandler) GetRetryConfig() interfaces.RetryConfig {
|
||||
return interfaces.RetryConfig{
|
||||
MaxRetries: 3,
|
||||
RetryDelay: 5 * time.Second,
|
||||
BackoffFactor: 2.0,
|
||||
MaxDelay: 30 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
// Handle 处理事件
|
||||
func (h *CertificationEventHandler) Handle(ctx context.Context, event interfaces.Event) error {
|
||||
h.logger.Info("处理认证事件",
|
||||
zap.String("event_type", event.GetType()),
|
||||
zap.String("event_id", event.GetID()),
|
||||
zap.String("aggregate_id", event.GetAggregateID()),
|
||||
)
|
||||
|
||||
switch event.GetType() {
|
||||
case EventTypeCertificationCreated:
|
||||
return h.handleCertificationCreated(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 EventTypeCertificationCompleted:
|
||||
return h.handleCertificationCompleted(ctx, event)
|
||||
default:
|
||||
h.logger.Warn("未知的事件类型", zap.String("event_type", event.GetType()))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// handleCertificationCreated 处理认证创建事件
|
||||
func (h *CertificationEventHandler) handleCertificationCreated(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)
|
||||
}
|
||||
|
||||
// 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请完成企业认证...",
|
||||
event.GetAggregateID(),
|
||||
event.GetTimestamp().Format("2006-01-02 15:04:05"))
|
||||
|
||||
return h.sendUserNotification(ctx, event, "企业信息提交成功", message)
|
||||
}
|
||||
|
||||
// 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("✅ 企业认证完成!\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)
|
||||
}
|
||||
|
||||
// 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)),
|
||||
)
|
||||
|
||||
// 发送通知给用户
|
||||
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)
|
||||
}
|
||||
|
||||
// handleContractSigned 处理合同签署事件
|
||||
func (h *CertificationEventHandler) handleContractSigned(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)
|
||||
}
|
||||
|
||||
// handleCertificationCompleted 处理认证完成事件
|
||||
func (h *CertificationEventHandler) handleCertificationCompleted(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)
|
||||
}
|
||||
|
||||
// sendUserNotification 发送用户通知
|
||||
func (h *CertificationEventHandler) sendUserNotification(ctx context.Context, event interfaces.Event, title, message string) error {
|
||||
userID := h.extractUserID(event)
|
||||
if userID == "" {
|
||||
h.logger.Warn("无法提取用户ID,跳过通知发送")
|
||||
return nil
|
||||
}
|
||||
|
||||
// 这里可以调用通知服务发送消息
|
||||
h.logger.Info("发送用户通知",
|
||||
zap.String("user_id", userID),
|
||||
zap.String("title", title),
|
||||
zap.String("message", message),
|
||||
)
|
||||
h.logger.Info("发送用户通知", zap.String("user_id", userID), zap.String("title", title), zap.String("message", message))
|
||||
return nil
|
||||
}
|
||||
|
||||
// sendAdminNotification 发送管理员通知
|
||||
func (h *CertificationEventHandler) sendAdminNotification(ctx context.Context, event interfaces.Event, title, message string) error {
|
||||
// 这里可以调用通知服务发送管理员消息
|
||||
h.logger.Info("发送管理员通知",
|
||||
zap.String("title", title),
|
||||
zap.String("message", message),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// extractUserID 从事件中提取用户ID
|
||||
func (h *CertificationEventHandler) extractUserID(event interfaces.Event) string {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试从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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试从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 日志事件处理器
|
||||
type LoggingEventHandler struct {
|
||||
logger *zap.Logger
|
||||
name string
|
||||
eventTypes []string
|
||||
isAsync bool
|
||||
}
|
||||
|
||||
// NewLoggingEventHandler 创建日志事件处理器
|
||||
func NewLoggingEventHandler(logger *zap.Logger) *LoggingEventHandler {
|
||||
return &LoggingEventHandler{
|
||||
logger: logger,
|
||||
name: "logging-event-handler",
|
||||
eventTypes: []string{
|
||||
EventTypeCertificationCreated,
|
||||
EventTypeEnterpriseInfoSubmitted,
|
||||
EventTypeEnterpriseVerified,
|
||||
EventTypeContractApplied,
|
||||
EventTypeContractSigned,
|
||||
EventTypeCertificationCompleted,
|
||||
},
|
||||
isAsync: false,
|
||||
}
|
||||
}
|
||||
|
||||
// GetName 获取处理器名称
|
||||
func (l *LoggingEventHandler) GetName() string {
|
||||
return l.name
|
||||
}
|
||||
|
||||
// GetEventTypes 获取支持的事件类型
|
||||
func (l *LoggingEventHandler) GetEventTypes() []string {
|
||||
return l.eventTypes
|
||||
}
|
||||
|
||||
// IsAsync 是否为异步处理器
|
||||
func (l *LoggingEventHandler) IsAsync() bool {
|
||||
return l.isAsync
|
||||
}
|
||||
|
||||
// GetRetryConfig 获取重试配置
|
||||
func (l *LoggingEventHandler) GetRetryConfig() interfaces.RetryConfig {
|
||||
return interfaces.RetryConfig{
|
||||
MaxRetries: 0,
|
||||
RetryDelay: 0,
|
||||
BackoffFactor: 1.0,
|
||||
MaxDelay: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Handle 处理事件
|
||||
func (l *LoggingEventHandler) Handle(ctx context.Context, event interfaces.Event) error {
|
||||
l.logger.Info("认证事件日志",
|
||||
zap.String("event_type", event.GetType()),
|
||||
zap.String("event_id", event.GetID()),
|
||||
zap.String("aggregate_id", event.GetAggregateID()),
|
||||
zap.Time("timestamp", event.GetTimestamp()),
|
||||
zap.Any("payload", event.GetPayload()),
|
||||
)
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hyapi-server/internal/domains/certification/entities"
|
||||
"hyapi-server/internal/domains/certification/enums"
|
||||
"hyapi-server/internal/shared/interfaces"
|
||||
)
|
||||
|
||||
// CertificationCommandRepository 认证命令仓储接口
|
||||
// 专门处理认证数据的变更操作,符合CQRS模式
|
||||
type CertificationCommandRepository interface {
|
||||
// 基础CRUD操作
|
||||
Create(ctx context.Context, cert entities.Certification) error
|
||||
Update(ctx context.Context, cert entities.Certification) error
|
||||
Delete(ctx context.Context, id string) error
|
||||
|
||||
// 业务特定的更新操作
|
||||
UpdateStatus(ctx context.Context, id string, status enums.CertificationStatus) error
|
||||
UpdateAuthFlowID(ctx context.Context, id string, authFlowID string) error
|
||||
UpdateContractInfo(ctx context.Context, id string, contractFileID, esignFlowID, contractURL, contractSignURL string) error
|
||||
UpdateFailureInfo(ctx context.Context, id string, reason enums.FailureReason, message string) error
|
||||
|
||||
// 批量操作
|
||||
BatchUpdateStatus(ctx context.Context, ids []string, status enums.CertificationStatus) error
|
||||
|
||||
// 事务支持
|
||||
WithTx(tx interfaces.Transaction) CertificationCommandRepository
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"hyapi-server/internal/domains/certification/entities"
|
||||
"hyapi-server/internal/domains/certification/enums"
|
||||
"hyapi-server/internal/domains/certification/repositories/queries"
|
||||
)
|
||||
|
||||
// CertificationQueryRepository 认证查询仓储接口
|
||||
// 专门处理认证数据的查询操作,符合CQRS模式
|
||||
type CertificationQueryRepository interface {
|
||||
// 基础查询操作
|
||||
GetByID(ctx context.Context, id string) (*entities.Certification, error)
|
||||
GetByUserID(ctx context.Context, userID string) (*entities.Certification, error)
|
||||
Exists(ctx context.Context, id string) (bool, error)
|
||||
ExistsByUserID(ctx context.Context, userID string) (bool, error)
|
||||
|
||||
// 列表查询
|
||||
List(ctx context.Context, query *queries.ListCertificationsQuery) ([]*entities.Certification, int64, error)
|
||||
ListByUserIDs(ctx context.Context, userIDs []string) ([]*entities.Certification, error)
|
||||
ListByStatus(ctx context.Context, status enums.CertificationStatus, limit int) ([]*entities.Certification, error)
|
||||
|
||||
// 业务查询
|
||||
FindByAuthFlowID(ctx context.Context, authFlowID string) (*entities.Certification, error)
|
||||
FindByEsignFlowID(ctx context.Context, esignFlowID string) (*entities.Certification, error)
|
||||
ListPendingRetry(ctx context.Context, maxRetryCount int) ([]*entities.Certification, error)
|
||||
GetPendingCertifications(ctx context.Context) ([]*entities.Certification, error)
|
||||
GetExpiredContracts(ctx context.Context) ([]*entities.Certification, error)
|
||||
GetCertificationsByDateRange(ctx context.Context, startDate, endDate time.Time) ([]*entities.Certification, error)
|
||||
GetUserActiveCertification(ctx context.Context, userID string) (*entities.Certification, error)
|
||||
|
||||
CountByFailureReason(ctx context.Context, reason enums.FailureReason) (int64, error)
|
||||
GetProgressStatistics(ctx context.Context) (*CertificationProgressStats, error)
|
||||
|
||||
// 搜索查询
|
||||
SearchByCompanyName(ctx context.Context, companyName string, limit int) ([]*entities.Certification, error)
|
||||
SearchByLegalPerson(ctx context.Context, legalPersonName string, limit int) ([]*entities.Certification, error)
|
||||
|
||||
// 缓存相关
|
||||
InvalidateCache(ctx context.Context, keys ...string) error
|
||||
RefreshCache(ctx context.Context, certificationID string) error
|
||||
}
|
||||
|
||||
// CertificationTimePeriod 时间周期枚举
|
||||
type CertificationTimePeriod string
|
||||
|
||||
const (
|
||||
PeriodDaily CertificationTimePeriod = "daily"
|
||||
PeriodWeekly CertificationTimePeriod = "weekly"
|
||||
PeriodMonthly CertificationTimePeriod = "monthly"
|
||||
PeriodYearly CertificationTimePeriod = "yearly"
|
||||
)
|
||||
|
||||
|
||||
// CertificationProgressStats 进度统计信息
|
||||
type CertificationProgressStats struct {
|
||||
StatusProgress map[enums.CertificationStatus]int64 `json:"status_progress"`
|
||||
ProgressDistribution map[int]int64 `json:"progress_distribution"` // key: progress percentage
|
||||
|
||||
// 各阶段耗时统计
|
||||
StageTimeStats map[string]*CertificationStageTimeInfo `json:"stage_time_stats"`
|
||||
}
|
||||
|
||||
// CertificationStageTimeInfo 阶段耗时信息
|
||||
type CertificationStageTimeInfo struct {
|
||||
StageName string `json:"stage_name"`
|
||||
AverageTime time.Duration `json:"average_time"`
|
||||
MinTime time.Duration `json:"min_time"`
|
||||
MaxTime time.Duration `json:"max_time"`
|
||||
SampleCount int64 `json:"sample_count"`
|
||||
}
|
||||
|
||||
// CertificationRetryStats 重试统计信息
|
||||
type CertificationRetryStats struct {
|
||||
TotalRetries int64 `json:"total_retries"`
|
||||
SuccessfulRetries int64 `json:"successful_retries"`
|
||||
FailedRetries int64 `json:"failed_retries"`
|
||||
RetrySuccessRate float64 `json:"retry_success_rate"`
|
||||
|
||||
// 各阶段重试统计
|
||||
EnterpriseRetries int64 `json:"enterprise_retries"`
|
||||
ContractRetries int64 `json:"contract_retries"`
|
||||
|
||||
// 重试原因分布
|
||||
RetryReasonStats map[enums.FailureReason]int64 `json:"retry_reason_stats"`
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hyapi-server/internal/domains/certification/entities"
|
||||
)
|
||||
|
||||
// ListSubmitRecordsFilter 提交记录列表筛选(以状态机 certification 状态为准)
|
||||
type ListSubmitRecordsFilter struct {
|
||||
CertificationStatus string // 认证状态筛选,如 info_pending_review / info_submitted / info_rejected,空为全部
|
||||
CompanyName string // 企业名称(模糊搜索)
|
||||
LegalPersonPhone string // 法人手机号
|
||||
LegalPersonName string // 法人姓名(模糊搜索)
|
||||
Page int
|
||||
PageSize int
|
||||
}
|
||||
|
||||
// ListSubmitRecordsResult 列表结果
|
||||
type ListSubmitRecordsResult struct {
|
||||
Records []*entities.EnterpriseInfoSubmitRecord
|
||||
Total int64
|
||||
}
|
||||
|
||||
type EnterpriseInfoSubmitRecordRepository interface {
|
||||
Create(ctx context.Context, record *entities.EnterpriseInfoSubmitRecord) error
|
||||
Update(ctx context.Context, record *entities.EnterpriseInfoSubmitRecord) error
|
||||
Exists(ctx context.Context, ID string) (bool, error)
|
||||
FindByID(ctx context.Context, id string) (*entities.EnterpriseInfoSubmitRecord, error)
|
||||
FindLatestByUserID(ctx context.Context, userID string) (*entities.EnterpriseInfoSubmitRecord, error)
|
||||
FindLatestVerifiedByUserID(ctx context.Context, userID string) (*entities.EnterpriseInfoSubmitRecord, error)
|
||||
// ExistsByUnifiedSocialCodeExcludeUser 检查该统一社会信用代码是否已被其他用户提交(已提交/已通过验证,排除指定用户)
|
||||
ExistsByUnifiedSocialCodeExcludeUser(ctx context.Context, unifiedSocialCode string, excludeUserID string) (bool, error)
|
||||
List(ctx context.Context, filter ListSubmitRecordsFilter) (*ListSubmitRecordsResult, error)
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
package queries
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"hyapi-server/internal/domains/certification/enums"
|
||||
)
|
||||
|
||||
// GetCertificationQuery 获取单个认证查询
|
||||
type GetCertificationQuery struct {
|
||||
ID string `json:"id" validate:"required"`
|
||||
UserID string `json:"user_id,omitempty"` // 可选的用户ID,用于权限验证
|
||||
}
|
||||
|
||||
// ListCertificationsQuery 认证列表查询
|
||||
type ListCertificationsQuery struct {
|
||||
// 分页参数
|
||||
Page int `json:"page" validate:"min=1"`
|
||||
PageSize int `json:"page_size" validate:"min=1,max=100"`
|
||||
|
||||
// 排序参数
|
||||
SortBy string `json:"sort_by"` // 排序字段: created_at, updated_at, status, progress
|
||||
SortOrder string `json:"sort_order"` // 排序方向: asc, desc
|
||||
|
||||
// 过滤条件
|
||||
UserID string `json:"user_id,omitempty"`
|
||||
Status enums.CertificationStatus `json:"status,omitempty"`
|
||||
Statuses []enums.CertificationStatus `json:"statuses,omitempty"`
|
||||
FailureReason enums.FailureReason `json:"failure_reason,omitempty"`
|
||||
|
||||
// 时间范围过滤
|
||||
CreatedAfter *time.Time `json:"created_after,omitempty"`
|
||||
CreatedBefore *time.Time `json:"created_before,omitempty"`
|
||||
UpdatedAfter *time.Time `json:"updated_after,omitempty"`
|
||||
UpdatedBefore *time.Time `json:"updated_before,omitempty"`
|
||||
|
||||
// 企业信息过滤
|
||||
CompanyName string `json:"company_name,omitempty"`
|
||||
LegalPersonName string `json:"legal_person_name,omitempty"`
|
||||
|
||||
// 业务状态过滤
|
||||
IsCompleted *bool `json:"is_completed,omitempty"`
|
||||
IsFailed *bool `json:"is_failed,omitempty"`
|
||||
IsUserActionRequired *bool `json:"is_user_action_required,omitempty"`
|
||||
|
||||
// 高级过滤
|
||||
MinRetryCount *int `json:"min_retry_count,omitempty"`
|
||||
MaxRetryCount *int `json:"max_retry_count,omitempty"`
|
||||
MinProgress *int `json:"min_progress,omitempty"`
|
||||
MaxProgress *int `json:"max_progress,omitempty"`
|
||||
|
||||
// 搜索参数
|
||||
SearchKeyword string `json:"search_keyword,omitempty"` // 通用搜索关键词
|
||||
|
||||
// 包含关联数据
|
||||
IncludeMetadata bool `json:"include_metadata,omitempty"`
|
||||
}
|
||||
|
||||
// DefaultValues 设置默认值
|
||||
func (q *ListCertificationsQuery) DefaultValues() {
|
||||
if q.Page <= 0 {
|
||||
q.Page = 1
|
||||
}
|
||||
if q.PageSize <= 0 {
|
||||
q.PageSize = 20
|
||||
}
|
||||
if q.SortBy == "" {
|
||||
q.SortBy = "created_at"
|
||||
}
|
||||
if q.SortOrder == "" {
|
||||
q.SortOrder = "desc"
|
||||
}
|
||||
}
|
||||
|
||||
// GetOffset 计算分页偏移量
|
||||
func (q *ListCertificationsQuery) GetOffset() int {
|
||||
return (q.Page - 1) * q.PageSize
|
||||
}
|
||||
|
||||
// GetLimit 获取查询限制数量
|
||||
func (q *ListCertificationsQuery) GetLimit() int {
|
||||
return q.PageSize
|
||||
}
|
||||
|
||||
// HasTimeFilter 检查是否有时间过滤条件
|
||||
func (q *ListCertificationsQuery) HasTimeFilter() bool {
|
||||
return q.CreatedAfter != nil || q.CreatedBefore != nil ||
|
||||
q.UpdatedAfter != nil || q.UpdatedBefore != nil
|
||||
}
|
||||
|
||||
// HasStatusFilter 检查是否有状态过滤条件
|
||||
func (q *ListCertificationsQuery) HasStatusFilter() bool {
|
||||
return q.Status != "" || len(q.Statuses) > 0
|
||||
}
|
||||
|
||||
// HasSearchFilter 检查是否有搜索过滤条件
|
||||
func (q *ListCertificationsQuery) HasSearchFilter() bool {
|
||||
return q.CompanyName != "" || q.LegalPersonName != "" || q.SearchKeyword != ""
|
||||
}
|
||||
|
||||
// GetSearchFields 获取搜索字段映射
|
||||
func (q *ListCertificationsQuery) GetSearchFields() map[string]string {
|
||||
fields := make(map[string]string)
|
||||
|
||||
if q.CompanyName != "" {
|
||||
fields["company_name"] = q.CompanyName
|
||||
}
|
||||
if q.LegalPersonName != "" {
|
||||
fields["legal_person_name"] = q.LegalPersonName
|
||||
}
|
||||
if q.SearchKeyword != "" {
|
||||
fields["keyword"] = q.SearchKeyword
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
// CertificationStatisticsQuery 认证统计查询
|
||||
type CertificationStatisticsQuery struct {
|
||||
// 时间范围
|
||||
StartDate time.Time `json:"start_date" validate:"required"`
|
||||
EndDate time.Time `json:"end_date" validate:"required"`
|
||||
|
||||
// 统计周期
|
||||
Period string `json:"period" validate:"oneof=daily weekly monthly yearly"`
|
||||
|
||||
// 分组维度
|
||||
GroupBy []string `json:"group_by,omitempty"` // status, failure_reason, user_type, date
|
||||
|
||||
// 过滤条件
|
||||
UserIDs []string `json:"user_ids,omitempty"`
|
||||
Statuses []enums.CertificationStatus `json:"statuses,omitempty"`
|
||||
|
||||
// 统计类型
|
||||
IncludeProgressStats bool `json:"include_progress_stats,omitempty"`
|
||||
IncludeRetryStats bool `json:"include_retry_stats,omitempty"`
|
||||
IncludeTimeStats bool `json:"include_time_stats,omitempty"`
|
||||
}
|
||||
|
||||
// Validate 验证统计查询参数
|
||||
func (q *CertificationStatisticsQuery) Validate() error {
|
||||
if q.EndDate.Before(q.StartDate) {
|
||||
return fmt.Errorf("结束时间不能早于开始时间")
|
||||
}
|
||||
|
||||
// 检查时间范围是否合理(不超过1年)
|
||||
if q.EndDate.Sub(q.StartDate) > 365*24*time.Hour {
|
||||
return fmt.Errorf("查询时间范围不能超过1年")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTimeRange 获取时间范围描述
|
||||
func (q *CertificationStatisticsQuery) GetTimeRange() string {
|
||||
return fmt.Sprintf("%s 到 %s",
|
||||
q.StartDate.Format("2006-01-02"),
|
||||
q.EndDate.Format("2006-01-02"))
|
||||
}
|
||||
|
||||
// SearchCertificationsQuery 搜索认证查询
|
||||
type SearchCertificationsQuery struct {
|
||||
// 搜索关键词
|
||||
Keyword string `json:"keyword" validate:"required,min=2"`
|
||||
|
||||
// 搜索字段
|
||||
SearchFields []string `json:"search_fields,omitempty"` // company_name, legal_person_name, unified_social_code
|
||||
|
||||
// 过滤条件
|
||||
Statuses []enums.CertificationStatus `json:"statuses,omitempty"`
|
||||
UserID string `json:"user_id,omitempty"`
|
||||
|
||||
// 分页参数
|
||||
Page int `json:"page" validate:"min=1"`
|
||||
PageSize int `json:"page_size" validate:"min=1,max=50"`
|
||||
|
||||
// 排序参数
|
||||
SortBy string `json:"sort_by"`
|
||||
SortOrder string `json:"sort_order"`
|
||||
|
||||
// 搜索选项
|
||||
ExactMatch bool `json:"exact_match,omitempty"` // 是否精确匹配
|
||||
IgnoreCase bool `json:"ignore_case,omitempty"` // 是否忽略大小写
|
||||
}
|
||||
|
||||
// DefaultValues 设置搜索查询默认值
|
||||
func (q *SearchCertificationsQuery) DefaultValues() {
|
||||
if q.Page <= 0 {
|
||||
q.Page = 1
|
||||
}
|
||||
if q.PageSize <= 0 {
|
||||
q.PageSize = 10
|
||||
}
|
||||
if q.SortBy == "" {
|
||||
q.SortBy = "created_at"
|
||||
}
|
||||
if q.SortOrder == "" {
|
||||
q.SortOrder = "desc"
|
||||
}
|
||||
if len(q.SearchFields) == 0 {
|
||||
q.SearchFields = []string{"company_name", "legal_person_name"}
|
||||
}
|
||||
|
||||
// 默认忽略大小写
|
||||
q.IgnoreCase = true
|
||||
}
|
||||
|
||||
// GetLimit 获取查询限制数量
|
||||
func (q *SearchCertificationsQuery) GetLimit() int {
|
||||
return q.PageSize
|
||||
}
|
||||
|
||||
// GetSearchPattern 获取搜索模式
|
||||
func (q *SearchCertificationsQuery) GetSearchPattern() string {
|
||||
if q.ExactMatch {
|
||||
return q.Keyword
|
||||
}
|
||||
|
||||
// 模糊搜索,添加通配符
|
||||
return "%" + q.Keyword + "%"
|
||||
}
|
||||
|
||||
// UserCertificationsQuery 用户认证查询
|
||||
type UserCertificationsQuery struct {
|
||||
UserID string `json:"user_id" validate:"required"`
|
||||
|
||||
// 状态过滤
|
||||
Status enums.CertificationStatus `json:"status,omitempty"`
|
||||
IncludeCompleted bool `json:"include_completed,omitempty"`
|
||||
IncludeFailed bool `json:"include_failed,omitempty"`
|
||||
|
||||
// 时间过滤
|
||||
After *time.Time `json:"after,omitempty"`
|
||||
Before *time.Time `json:"before,omitempty"`
|
||||
|
||||
// 分页
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"page_size"`
|
||||
|
||||
// 排序
|
||||
SortBy string `json:"sort_by"`
|
||||
SortOrder string `json:"sort_order"`
|
||||
}
|
||||
|
||||
// DefaultValues 设置用户认证查询默认值
|
||||
func (q *UserCertificationsQuery) DefaultValues() {
|
||||
if q.Page <= 0 {
|
||||
q.Page = 1
|
||||
}
|
||||
if q.PageSize <= 0 {
|
||||
q.PageSize = 10
|
||||
}
|
||||
if q.SortBy == "" {
|
||||
q.SortBy = "created_at"
|
||||
}
|
||||
if q.SortOrder == "" {
|
||||
q.SortOrder = "desc"
|
||||
}
|
||||
}
|
||||
|
||||
// ShouldIncludeStatus 检查是否应该包含指定状态
|
||||
func (q *UserCertificationsQuery) ShouldIncludeStatus(status enums.CertificationStatus) bool {
|
||||
// 如果指定了特定状态,只返回该状态
|
||||
if q.Status != "" {
|
||||
return status == q.Status
|
||||
}
|
||||
|
||||
// 根据包含选项决定
|
||||
if enums.IsFinalStatus(status) && !q.IncludeCompleted {
|
||||
return false
|
||||
}
|
||||
|
||||
if enums.IsFailureStatus(status) && !q.IncludeFailed {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,349 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"hyapi-server/internal/domains/certification/entities"
|
||||
"hyapi-server/internal/domains/certification/enums"
|
||||
"hyapi-server/internal/domains/certification/repositories"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// CertificationAggregateService 认证聚合服务接口
|
||||
// 负责认证聚合根的生命周期管理和状态转换协调
|
||||
type CertificationAggregateService interface {
|
||||
// 聚合根管理
|
||||
CreateCertification(ctx context.Context, userID string) (*entities.Certification, error)
|
||||
LoadCertification(ctx context.Context, certificationID string) (*entities.Certification, error)
|
||||
SaveCertification(ctx context.Context, cert *entities.Certification) error
|
||||
LoadCertificationByUserID(ctx context.Context, userID string) (*entities.Certification, error)
|
||||
LoadCertificationByAuthFlowId(ctx context.Context, authFlowId string) (*entities.Certification, error)
|
||||
LoadCertificationByEsignFlowId(ctx context.Context, esignFlowId string) (*entities.Certification, error)
|
||||
// 业务规则验证
|
||||
ValidateBusinessRules(ctx context.Context, cert *entities.Certification) error
|
||||
CheckInvariance(ctx context.Context, cert *entities.Certification) error
|
||||
|
||||
// 查询方法
|
||||
ExistsByUserID(ctx context.Context, userID string) (bool, error)
|
||||
}
|
||||
|
||||
// CertificationAggregateServiceImpl 认证聚合服务实现
|
||||
type CertificationAggregateServiceImpl struct {
|
||||
commandRepo repositories.CertificationCommandRepository
|
||||
queryRepo repositories.CertificationQueryRepository
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewCertificationAggregateService 创建认证聚合服务
|
||||
func NewCertificationAggregateService(
|
||||
commandRepo repositories.CertificationCommandRepository,
|
||||
queryRepo repositories.CertificationQueryRepository,
|
||||
logger *zap.Logger,
|
||||
) CertificationAggregateService {
|
||||
return &CertificationAggregateServiceImpl{
|
||||
commandRepo: commandRepo,
|
||||
queryRepo: queryRepo,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// ================ 聚合根管理 ================
|
||||
|
||||
// CreateCertification 创建认证申请
|
||||
func (s *CertificationAggregateServiceImpl) CreateCertification(ctx context.Context, userID string) (*entities.Certification, error) {
|
||||
s.logger.Info("创建认证申请", zap.String("user_id", userID))
|
||||
|
||||
// 1. 检查用户是否已有认证申请
|
||||
exists, err := s.ExistsByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
s.logger.Error("检查用户认证是否存在失败", zap.Error(err), zap.String("user_id", userID))
|
||||
return nil, fmt.Errorf("检查用户认证是否存在失败: %w", err)
|
||||
}
|
||||
if exists {
|
||||
s.logger.Info("用户已有认证申请,不允许创建新申请",
|
||||
zap.String("user_id", userID))
|
||||
return nil, fmt.Errorf("用户已有认证申请")
|
||||
}
|
||||
|
||||
// 2. 创建新的认证聚合根
|
||||
cert, err := entities.NewCertification(userID)
|
||||
if err != nil {
|
||||
s.logger.Error("创建认证实体失败", zap.Error(err), zap.String("user_id", userID))
|
||||
return nil, fmt.Errorf("创建认证实体失败: %w", err)
|
||||
}
|
||||
|
||||
// 3. 验证业务规则
|
||||
if err := s.ValidateBusinessRules(ctx, cert); err != nil {
|
||||
s.logger.Error("认证业务规则验证失败", zap.Error(err))
|
||||
return nil, fmt.Errorf("业务规则验证失败: %w", err)
|
||||
}
|
||||
|
||||
// 4. 保存聚合根
|
||||
if err := s.SaveCertification(ctx, cert); err != nil {
|
||||
s.logger.Error("保存认证申请失败", zap.Error(err))
|
||||
return nil, fmt.Errorf("保存认证申请失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("认证申请创建成功",
|
||||
zap.String("user_id", userID),
|
||||
zap.String("certification_id", cert.ID))
|
||||
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// LoadCertification 加载认证聚合根
|
||||
func (s *CertificationAggregateServiceImpl) LoadCertification(ctx context.Context, certificationID string) (*entities.Certification, error) {
|
||||
s.logger.Debug("加载认证聚合根", zap.String("certification_id", certificationID))
|
||||
|
||||
// 从查询仓储加载
|
||||
cert, err := s.queryRepo.GetByID(ctx, certificationID)
|
||||
if err != nil {
|
||||
s.logger.Error("加载认证聚合根失败", zap.Error(err), zap.String("certification_id", certificationID))
|
||||
return nil, fmt.Errorf("认证申请不存在: %w", err)
|
||||
}
|
||||
|
||||
// 验证聚合根完整性
|
||||
if err := s.CheckInvariance(ctx, cert); err != nil {
|
||||
s.logger.Error("认证聚合根完整性验证失败", zap.Error(err))
|
||||
return nil, fmt.Errorf("认证数据完整性验证失败: %w", err)
|
||||
}
|
||||
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// LoadCertificationByUserID 加载用户认证聚合根
|
||||
func (s *CertificationAggregateServiceImpl) LoadCertificationByUserID(ctx context.Context, userID string) (*entities.Certification, error) {
|
||||
s.logger.Debug("加载用户认证聚合根", zap.String("user_id", userID))
|
||||
|
||||
// 从查询仓储加载
|
||||
cert, err := s.queryRepo.GetByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
s.logger.Error("加载用户认证聚合根失败", zap.Error(err), zap.String("user_id", userID))
|
||||
return nil, fmt.Errorf("认证申请不存在: %w", err)
|
||||
}
|
||||
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// LoadCertificationByAuthFlowId 加载认证聚合根
|
||||
func (s *CertificationAggregateServiceImpl) LoadCertificationByAuthFlowId(ctx context.Context, authFlowId string) (*entities.Certification, error) {
|
||||
s.logger.Debug("加载认证聚合根", zap.String("auth_flow_id", authFlowId))
|
||||
|
||||
// 从查询仓储加载
|
||||
cert, err := s.queryRepo.FindByAuthFlowID(ctx, authFlowId)
|
||||
if err != nil {
|
||||
s.logger.Error("加载认证聚合根失败", zap.Error(err), zap.String("auth_flow_id", authFlowId))
|
||||
return nil, fmt.Errorf("认证申请不存在: %w", err)
|
||||
}
|
||||
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// LoadCertificationByEsignFlowId 加载认证聚合根
|
||||
func (s *CertificationAggregateServiceImpl) LoadCertificationByEsignFlowId(ctx context.Context, esignFlowId string) (*entities.Certification, error) {
|
||||
s.logger.Debug("加载认证聚合根", zap.String("esign_flow_id", esignFlowId))
|
||||
|
||||
// 从查询仓储加载
|
||||
cert, err := s.queryRepo.FindByEsignFlowID(ctx, esignFlowId)
|
||||
if err != nil {
|
||||
s.logger.Error("加载认证聚合根失败", zap.Error(err), zap.String("esign_flow_id", esignFlowId))
|
||||
return nil, fmt.Errorf("认证申请不存在: %w", err)
|
||||
}
|
||||
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// SaveCertification 保存认证聚合根
|
||||
func (s *CertificationAggregateServiceImpl) SaveCertification(ctx context.Context, cert *entities.Certification) error {
|
||||
s.logger.Debug("保存认证聚合根", zap.String("certification_id", cert.ID))
|
||||
|
||||
// 1. 验证业务规则
|
||||
if err := s.ValidateBusinessRules(ctx, cert); err != nil {
|
||||
return fmt.Errorf("业务规则验证失败: %w", err)
|
||||
}
|
||||
|
||||
// 2. 检查聚合根是否存在
|
||||
exists, err := s.queryRepo.Exists(ctx, cert.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("检查认证存在性失败: %w", err)
|
||||
}
|
||||
|
||||
// 3. 保存到命令仓储
|
||||
if exists {
|
||||
err = s.commandRepo.Update(ctx, *cert)
|
||||
if err != nil {
|
||||
s.logger.Error("更新认证聚合根失败", zap.Error(err))
|
||||
return fmt.Errorf("更新认证失败: %w", err)
|
||||
}
|
||||
} else {
|
||||
err = s.commandRepo.Create(ctx, *cert)
|
||||
if err != nil {
|
||||
s.logger.Error("创建认证聚合根失败", zap.Error(err))
|
||||
return fmt.Errorf("创建认证失败: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
s.logger.Debug("认证聚合根保存成功", zap.String("certification_id", cert.ID))
|
||||
return nil
|
||||
}
|
||||
|
||||
// ================ 业务规则验证 ================
|
||||
|
||||
// ValidateBusinessRules 验证业务规则
|
||||
func (s *CertificationAggregateServiceImpl) ValidateBusinessRules(ctx context.Context, cert *entities.Certification) error {
|
||||
s.logger.Debug("验证认证业务规则", zap.String("certification_id", cert.ID))
|
||||
|
||||
// 1. 实体内部业务规则验证
|
||||
if err := cert.ValidateBusinessRules(); err != nil {
|
||||
return fmt.Errorf("实体业务规则验证失败: %w", err)
|
||||
}
|
||||
|
||||
// 2. 跨聚合根业务规则验证
|
||||
if err := s.validateCrossAggregateRules(ctx, cert); err != nil {
|
||||
return fmt.Errorf("跨聚合根业务规则验证失败: %w", err)
|
||||
}
|
||||
|
||||
// 3. 领域级业务规则验证
|
||||
if err := s.validateDomainRules(ctx, cert); err != nil {
|
||||
return fmt.Errorf("领域业务规则验证失败: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckInvariance 检查聚合根不变量
|
||||
func (s *CertificationAggregateServiceImpl) CheckInvariance(ctx context.Context, cert *entities.Certification) error {
|
||||
s.logger.Debug("检查认证聚合根不变量", zap.String("certification_id", cert.ID))
|
||||
|
||||
// 1. 基础不变量检查
|
||||
if cert.ID == "" {
|
||||
return fmt.Errorf("认证ID不能为空")
|
||||
}
|
||||
|
||||
if cert.UserID == "" {
|
||||
return fmt.Errorf("用户ID不能为空")
|
||||
}
|
||||
|
||||
if !enums.IsValidStatus(cert.Status) {
|
||||
return fmt.Errorf("无效的认证状态: %s", cert.Status)
|
||||
}
|
||||
|
||||
// 2. 状态相关不变量检查
|
||||
if err := s.validateStatusInvariance(cert); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 3. 时间戳不变量检查
|
||||
if err := s.validateTimestampInvariance(cert); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ================ 查询方法 ================
|
||||
|
||||
// Exists 判断认证是否存在
|
||||
func (s *CertificationAggregateServiceImpl) ExistsByUserID(ctx context.Context, userID string) (bool, error) {
|
||||
return s.queryRepo.ExistsByUserID(ctx, userID)
|
||||
}
|
||||
|
||||
// ================ 私有方法 ================
|
||||
|
||||
// validateCrossAggregateRules 验证跨聚合根业务规则
|
||||
func (s *CertificationAggregateServiceImpl) validateCrossAggregateRules(ctx context.Context, cert *entities.Certification) error {
|
||||
// TODO: 实现跨聚合根业务规则验证
|
||||
// 例如:检查用户是否有权限申请认证、检查企业信息是否已被其他用户使用等
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateDomainRules 验证领域级业务规则
|
||||
func (s *CertificationAggregateServiceImpl) validateDomainRules(ctx context.Context, cert *entities.Certification) error {
|
||||
// TODO: 实现领域级业务规则验证
|
||||
// 例如:检查认证流程是否符合法规要求、检查时间窗口限制等
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateStatusInvariance 验证状态相关不变量
|
||||
func (s *CertificationAggregateServiceImpl) validateStatusInvariance(cert *entities.Certification) error {
|
||||
switch cert.Status {
|
||||
case enums.StatusEnterpriseVerified:
|
||||
if cert.AuthFlowID == "" {
|
||||
return fmt.Errorf("企业认证状态下必须有认证流程ID")
|
||||
}
|
||||
if cert.EnterpriseVerifiedAt == nil {
|
||||
return fmt.Errorf("企业认证状态下必须有认证完成时间")
|
||||
}
|
||||
|
||||
case enums.StatusContractApplied:
|
||||
if cert.AuthFlowID == "" {
|
||||
return fmt.Errorf("合同申请状态下必须有企业认证流程ID")
|
||||
}
|
||||
if cert.ContractAppliedAt == nil {
|
||||
return fmt.Errorf("合同申请状态下必须有合同申请时间")
|
||||
}
|
||||
|
||||
case enums.StatusContractSigned:
|
||||
if cert.ContractFileID == "" || cert.EsignFlowID == "" {
|
||||
return fmt.Errorf("合同签署状态下必须有完整的合同信息")
|
||||
}
|
||||
if cert.ContractSignedAt == nil {
|
||||
return fmt.Errorf("合同签署状态下必须有签署完成时间")
|
||||
}
|
||||
|
||||
case enums.StatusCompleted:
|
||||
if cert.ContractFileID == "" || cert.EsignFlowID == "" || cert.ContractURL == "" {
|
||||
return fmt.Errorf("认证完成状态下必须有完整的合同信息")
|
||||
}
|
||||
if cert.ContractSignedAt == nil {
|
||||
return fmt.Errorf("认证完成状态下必须有合同签署时间")
|
||||
}
|
||||
if cert.CompletedAt == nil {
|
||||
return fmt.Errorf("认证完成状态下必须有完成时间")
|
||||
}
|
||||
}
|
||||
|
||||
// 失败状态检查
|
||||
if enums.IsFailureStatus(cert.Status) {
|
||||
if cert.FailureReason == "" {
|
||||
return fmt.Errorf("失败状态下必须有失败原因")
|
||||
}
|
||||
if !enums.IsValidFailureReason(cert.FailureReason) {
|
||||
return fmt.Errorf("无效的失败原因: %s", cert.FailureReason)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateTimestampInvariance 验证时间戳不变量
|
||||
func (s *CertificationAggregateServiceImpl) validateTimestampInvariance(cert *entities.Certification) error {
|
||||
// 检查时间戳的逻辑顺序
|
||||
if cert.InfoSubmittedAt != nil && cert.EnterpriseVerifiedAt != nil {
|
||||
if cert.InfoSubmittedAt.After(*cert.EnterpriseVerifiedAt) {
|
||||
return fmt.Errorf("企业信息提交时间不能晚于企业认证时间")
|
||||
}
|
||||
}
|
||||
|
||||
if cert.EnterpriseVerifiedAt != nil && cert.ContractAppliedAt != nil {
|
||||
if cert.EnterpriseVerifiedAt.After(*cert.ContractAppliedAt) {
|
||||
return fmt.Errorf("企业认证时间不能晚于合同申请时间")
|
||||
}
|
||||
}
|
||||
|
||||
if cert.ContractAppliedAt != nil && cert.ContractSignedAt != nil {
|
||||
if cert.ContractAppliedAt.After(*cert.ContractSignedAt) {
|
||||
return fmt.Errorf("合同申请时间不能晚于合同签署时间")
|
||||
}
|
||||
}
|
||||
|
||||
if cert.ContractSignedAt != nil && cert.CompletedAt != nil {
|
||||
if cert.ContractSignedAt.After(*cert.CompletedAt) {
|
||||
return fmt.Errorf("合同签署时间不能晚于认证完成时间")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,633 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"hyapi-server/internal/domains/certification/entities"
|
||||
"hyapi-server/internal/domains/certification/entities/value_objects"
|
||||
"hyapi-server/internal/domains/certification/enums"
|
||||
|
||||
"hyapi-server/internal/shared/esign"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// WorkflowResult 工作流执行结果
|
||||
type WorkflowResult struct {
|
||||
Success bool `json:"success"`
|
||||
CertificationID string `json:"certification_id"`
|
||||
CurrentStatus enums.CertificationStatus `json:"current_status"`
|
||||
Message string `json:"message"`
|
||||
Data map[string]interface{} `json:"data,omitempty"`
|
||||
ExecutedAt time.Time `json:"executed_at"`
|
||||
}
|
||||
|
||||
// SubmitEnterpriseInfoCommand 提交企业信息命令
|
||||
type SubmitEnterpriseInfoCommand struct {
|
||||
UserID string `json:"user_id"`
|
||||
EnterpriseInfo *value_objects.EnterpriseInfo `json:"enterprise_info"`
|
||||
}
|
||||
|
||||
// 完成企业认证命令
|
||||
type CompleteEnterpriseVerificationCommand struct {
|
||||
AuthFlowId string `json:"auth_flow_id"`
|
||||
}
|
||||
|
||||
// ApplyContractCommand 申请合同命令
|
||||
type ApplyContractCommand struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
UserID string `json:"user_id"`
|
||||
}
|
||||
|
||||
// EsignCallbackCommand e签宝回调命令
|
||||
type EsignCallbackCommand struct {
|
||||
CertificationID string `json:"certification_id"`
|
||||
CallbackType string `json:"callback_type"` // "auth_result" | "sign_result" | "flow_status"
|
||||
}
|
||||
|
||||
// CertificationWorkflowOrchestrator 认证工作流编排器接口
|
||||
// 负责编排认证业务流程,协调各个领域服务的协作
|
||||
type CertificationWorkflowOrchestrator interface {
|
||||
// 用户操作用例
|
||||
// 提交企业信息
|
||||
SubmitEnterpriseInfo(ctx context.Context, cmd *SubmitEnterpriseInfoCommand) (*WorkflowResult, error)
|
||||
// 完成企业认证
|
||||
CompleteEnterpriseVerification(ctx context.Context, cmd *CompleteEnterpriseVerificationCommand) (*WorkflowResult, error)
|
||||
// 申请合同签署
|
||||
ApplyContract(ctx context.Context, cmd *ApplyContractCommand) (*WorkflowResult, error)
|
||||
|
||||
// e签宝回调处理
|
||||
HandleEnterpriseVerificationCallback(ctx context.Context, cmd *EsignCallbackCommand) (*WorkflowResult, error)
|
||||
HandleContractSignCallback(ctx context.Context, cmd *EsignCallbackCommand) (*WorkflowResult, error)
|
||||
|
||||
// 异常处理
|
||||
HandleFailure(ctx context.Context, certificationID string, failureType string, reason string) (*WorkflowResult, error)
|
||||
|
||||
// 查询操作
|
||||
GetCertification(ctx context.Context, userID string) (*WorkflowResult, error)
|
||||
GetWorkflowStatus(ctx context.Context, certificationID string) (*WorkflowResult, error)
|
||||
}
|
||||
|
||||
// CertificationWorkflowOrchestratorImpl 认证工作流编排器实现
|
||||
type CertificationWorkflowOrchestratorImpl struct {
|
||||
aggregateService CertificationAggregateService
|
||||
logger *zap.Logger
|
||||
esignClient *esign.Client
|
||||
}
|
||||
|
||||
// NewCertificationWorkflowOrchestrator 创建认证工作流编排器
|
||||
func NewCertificationWorkflowOrchestrator(
|
||||
aggregateService CertificationAggregateService,
|
||||
logger *zap.Logger,
|
||||
esignClient *esign.Client,
|
||||
) CertificationWorkflowOrchestrator {
|
||||
return &CertificationWorkflowOrchestratorImpl{
|
||||
aggregateService: aggregateService,
|
||||
logger: logger,
|
||||
esignClient: esignClient,
|
||||
}
|
||||
}
|
||||
|
||||
// ================ 用户操作用例 ================
|
||||
|
||||
// GetCertification 获取认证详情
|
||||
func (o *CertificationWorkflowOrchestratorImpl) GetCertification(
|
||||
ctx context.Context,
|
||||
userID string,
|
||||
) (*WorkflowResult, error) {
|
||||
exists, err := o.aggregateService.ExistsByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
o.logger.Error("获取认证信息失败", zap.Error(err))
|
||||
return nil, fmt.Errorf("获取认证信息失败: %w", err)
|
||||
}
|
||||
var cert *entities.Certification
|
||||
if !exists {
|
||||
cert, err = o.aggregateService.CreateCertification(ctx, userID)
|
||||
if err != nil {
|
||||
o.logger.Error("创建认证信息失败", zap.Error(err))
|
||||
return nil, fmt.Errorf("创建认证信息失败: %w", err)
|
||||
}
|
||||
} else {
|
||||
cert, err = o.aggregateService.LoadCertificationByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
o.logger.Error("获取认证信息失败", zap.Error(err))
|
||||
return nil, fmt.Errorf("认证信息不存在: %w", err)
|
||||
}
|
||||
}
|
||||
meta := cert.GetDataByStatus()
|
||||
return o.createSuccessResult(userID, cert.Status, "获取认证信息成功", meta), nil
|
||||
}
|
||||
|
||||
// SubmitEnterpriseInfo 用户提交企业信息
|
||||
func (o *CertificationWorkflowOrchestratorImpl) SubmitEnterpriseInfo(
|
||||
ctx context.Context,
|
||||
cmd *SubmitEnterpriseInfoCommand,
|
||||
) (*WorkflowResult, error) {
|
||||
o.logger.Info("开始处理企业信息提交",
|
||||
zap.String("user_id", cmd.UserID))
|
||||
|
||||
// 1. 检查用户认证是否存在
|
||||
exists, err := o.aggregateService.ExistsByUserID(ctx, cmd.UserID)
|
||||
if err != nil {
|
||||
return o.createFailureResult(cmd.UserID, "", fmt.Sprintf("检查用户认证是否存在失败: %s", err.Error())), err
|
||||
}
|
||||
if !exists {
|
||||
// 创建
|
||||
_, err := o.aggregateService.CreateCertification(ctx, cmd.UserID)
|
||||
if err != nil {
|
||||
return o.createFailureResult(cmd.UserID, "", fmt.Sprintf("创建认证信息失败: %s", err.Error())), err
|
||||
}
|
||||
}
|
||||
// 1.1 验证企业信息
|
||||
err = cmd.EnterpriseInfo.Validate()
|
||||
if err != nil {
|
||||
return o.createFailureResult(cmd.UserID, "", fmt.Sprintf("企业信息验证失败: %s", err.Error())), err
|
||||
}
|
||||
// 2. 加载认证聚合根
|
||||
cert, err := o.aggregateService.LoadCertificationByUserID(ctx, cmd.UserID)
|
||||
if err != nil {
|
||||
return o.createFailureResult(cmd.UserID, "", fmt.Sprintf("加载认证信息失败: %s", err.Error())), err
|
||||
}
|
||||
|
||||
// 3. 验证业务前置条件(暂时没啥用,后面的都会校验)
|
||||
if err := o.validateEnterpriseInfoSubmissionPreconditions(cert, cmd.UserID); err != nil {
|
||||
return o.createFailureResult(cmd.UserID, cert.Status, err.Error()), err
|
||||
}
|
||||
|
||||
// 5. 调用e签宝看是否进行过认证
|
||||
respMeta := map[string]interface{}{}
|
||||
|
||||
identity, err := o.esignClient.QueryOrgIdentityInfo(&esign.QueryOrgIdentityRequest{
|
||||
OrgName: cmd.EnterpriseInfo.CompanyName,
|
||||
})
|
||||
if identity != nil && identity.Data.RealnameStatus == 1 {
|
||||
o.logger.Info("企业认证成功", zap.Any("identity", identity))
|
||||
err = cert.CompleteEnterpriseVerification()
|
||||
if err != nil {
|
||||
return o.createFailureResult(cmd.UserID, cert.Status, err.Error()), err
|
||||
}
|
||||
respMeta = map[string]interface{}{
|
||||
"enterprise_info": cmd.EnterpriseInfo,
|
||||
"next_action": "企业已认证,可进行后续操作",
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
o.logger.Error("e签宝查询企业认证信息失败或未进行企业认证", zap.Error(err))
|
||||
}
|
||||
authURL, err := o.esignClient.GenerateEnterpriseAuth(&esign.EnterpriseAuthRequest{
|
||||
CompanyName: cmd.EnterpriseInfo.CompanyName,
|
||||
UnifiedSocialCode: cmd.EnterpriseInfo.UnifiedSocialCode,
|
||||
LegalPersonName: cmd.EnterpriseInfo.LegalPersonName,
|
||||
LegalPersonID: cmd.EnterpriseInfo.LegalPersonID,
|
||||
TransactorName: cmd.EnterpriseInfo.LegalPersonName,
|
||||
TransactorMobile: cmd.EnterpriseInfo.LegalPersonPhone,
|
||||
TransactorID: cmd.EnterpriseInfo.LegalPersonID,
|
||||
})
|
||||
if err != nil {
|
||||
o.logger.Error("生成企业认证链接失败", zap.Error(err))
|
||||
return o.createFailureResult(cmd.UserID, cert.Status, err.Error()), err
|
||||
}
|
||||
err = cert.SubmitEnterpriseInfo(cmd.EnterpriseInfo, authURL.AuthShortURL, authURL.AuthFlowID)
|
||||
if err != nil {
|
||||
return o.createFailureResult(cmd.UserID, cert.Status, err.Error()), err
|
||||
}
|
||||
respMeta = map[string]interface{}{
|
||||
"enterprise_info": cmd.EnterpriseInfo,
|
||||
"authUrl": authURL.AuthURL,
|
||||
"next_action": "请完成企业认证",
|
||||
}
|
||||
}
|
||||
|
||||
err = o.aggregateService.SaveCertification(ctx, cert)
|
||||
if err != nil {
|
||||
return o.createFailureResult(cmd.UserID, cert.Status, err.Error()), err
|
||||
}
|
||||
|
||||
// 6. 构建成功结果
|
||||
return o.createSuccessResult(cmd.UserID, enums.StatusInfoSubmitted, "企业信息提交成功", respMeta), nil
|
||||
}
|
||||
|
||||
// CompleteEnterpriseVerification 完成企业认证
|
||||
func (o *CertificationWorkflowOrchestratorImpl) CompleteEnterpriseVerification(
|
||||
ctx context.Context,
|
||||
cmd *CompleteEnterpriseVerificationCommand,
|
||||
) (*WorkflowResult, error) {
|
||||
cert, err := o.aggregateService.LoadCertificationByAuthFlowId(ctx, cmd.AuthFlowId)
|
||||
if err != nil {
|
||||
return o.createFailureResult(cmd.AuthFlowId, "", fmt.Sprintf("加载认证信息失败: %s", err.Error())), err
|
||||
}
|
||||
err = cert.CompleteEnterpriseVerification()
|
||||
if err != nil {
|
||||
return o.createFailureResult(cmd.AuthFlowId, "", fmt.Sprintf("完成企业认证失败: %s", err.Error())), err
|
||||
}
|
||||
err = o.aggregateService.SaveCertification(ctx, cert)
|
||||
if err != nil {
|
||||
return o.createFailureResult(cmd.AuthFlowId, "", fmt.Sprintf("保存认证信息失败: %s", err.Error())), err
|
||||
}
|
||||
o.logger.Info("完成企业认证", zap.String("certification_id", cert.ID))
|
||||
return o.createSuccessResult(cmd.AuthFlowId, enums.StatusEnterpriseVerified, "企业认证成功", map[string]interface{}{}), nil
|
||||
}
|
||||
|
||||
// ApplyContract 用户申请合同签署
|
||||
func (o *CertificationWorkflowOrchestratorImpl) ApplyContract(
|
||||
ctx context.Context,
|
||||
cmd *ApplyContractCommand,
|
||||
) (*WorkflowResult, error) {
|
||||
return nil, nil
|
||||
// o.logger.Info("开始处理合同申请",
|
||||
// zap.String("certification_id", cmd.CertificationID),
|
||||
// zap.String("user_id", cmd.UserID))
|
||||
|
||||
// // 1. 验证命令完整性
|
||||
// if err := o.validateApplyContractCommand(cmd); err != nil {
|
||||
// return o.createFailureResult(cmd.CertificationID, "", fmt.Sprintf("命令验证失败: %s", err.Error())), err
|
||||
// }
|
||||
|
||||
// // 2. 加载认证聚合根
|
||||
// cert, err := o.aggregateService.LoadCertification(ctx, cmd.CertificationID)
|
||||
// if err != nil {
|
||||
// return o.createFailureResult(cmd.CertificationID, "", fmt.Sprintf("加载认证信息失败: %s", err.Error())), err
|
||||
// }
|
||||
|
||||
// // 3. 验证业务前置条件
|
||||
// if err := o.validateContractApplicationPreconditions(cert, cmd.UserID); err != nil {
|
||||
// return o.createFailureResult(cmd.CertificationID, cert.Status, err.Error()), err
|
||||
// }
|
||||
|
||||
// // 4. 执行状态转换
|
||||
// result, err := o.aggregateService.TransitionState(
|
||||
// ctx,
|
||||
// cmd.CertificationID,
|
||||
// enums.StatusContractApplied,
|
||||
// enums.ActorTypeUser,
|
||||
// cmd.UserID,
|
||||
// "用户申请合同签署",
|
||||
// map[string]interface{}{},
|
||||
// )
|
||||
// if err != nil {
|
||||
// o.logger.Error("合同申请状态转换失败", zap.Error(err))
|
||||
// return o.createFailureResult(cmd.CertificationID, cert.Status, fmt.Sprintf("状态转换失败: %s", err.Error())), err
|
||||
// }
|
||||
|
||||
// // 5. 生成合同和签署链接
|
||||
// contractInfo, err := o.generateContractAndSignURL(ctx, cmd.CertificationID, cert)
|
||||
// if err != nil {
|
||||
// o.logger.Error("生成合同失败", zap.Error(err))
|
||||
// // 需要回滚状态
|
||||
// return o.createFailureResult(cmd.CertificationID, cert.Status, fmt.Sprintf("生成合同失败: %s", err.Error())), err
|
||||
// }
|
||||
|
||||
// // 6. 构建成功结果
|
||||
// return o.createSuccessResult(cmd.CertificationID, enums.StatusContractApplied, "合同申请成功", map[string]interface{}{
|
||||
// "contract_sign_url": contractInfo.ContractSignURL,
|
||||
// "contract_url": contractInfo.ContractURL,
|
||||
// "next_action": "请在规定时间内完成合同签署",
|
||||
// }, result), nil
|
||||
}
|
||||
|
||||
// ================ e签宝回调处理 ================
|
||||
|
||||
// HandleEnterpriseVerificationCallback 处理企业认证回调
|
||||
func (o *CertificationWorkflowOrchestratorImpl) HandleEnterpriseVerificationCallback(
|
||||
ctx context.Context,
|
||||
cmd *EsignCallbackCommand,
|
||||
) (*WorkflowResult, error) {
|
||||
o.logger.Info("开始处理企业认证回调",
|
||||
zap.String("certification_id", cmd.CertificationID),
|
||||
zap.String("callback_type", cmd.CallbackType))
|
||||
return nil, nil
|
||||
// // 1. 验证回调数据
|
||||
// if err := o.callbackHandler.ValidateCallbackData(cmd.CallbackData); err != nil {
|
||||
// return o.createFailureResult(cmd.CertificationID, "", fmt.Sprintf("回调数据验证失败: %s", err.Error())), err
|
||||
// }
|
||||
|
||||
// // 2. 加载认证聚合根
|
||||
// cert, err := o.aggregateService.LoadCertification(ctx, cmd.CertificationID)
|
||||
// if err != nil {
|
||||
// return o.createFailureResult(cmd.CertificationID, "", fmt.Sprintf("加载认证信息失败: %s", err.Error())), err
|
||||
// }
|
||||
|
||||
// // 3. 验证回调处理前置条件
|
||||
// if cert.Status != enums.StatusInfoSubmitted {
|
||||
// return o.createFailureResult(cmd.CertificationID, cert.Status,
|
||||
// fmt.Sprintf("当前状态 %s 不允许处理企业认证回调", enums.GetStatusName(cert.Status))),
|
||||
// fmt.Errorf("无效的状态转换")
|
||||
// }
|
||||
|
||||
// // 4. 处理回调
|
||||
// err = o.callbackHandler.HandleCallback(ctx, cmd.CertificationID, cmd.CallbackData)
|
||||
// if err != nil {
|
||||
// o.logger.Error("处理企业认证回调失败", zap.Error(err))
|
||||
// return o.createFailureResult(cmd.CertificationID, cert.Status, fmt.Sprintf("回调处理失败: %s", err.Error())), err
|
||||
// }
|
||||
|
||||
// // 5. 重新加载认证信息获取最新状态
|
||||
// updatedCert, err := o.aggregateService.LoadCertification(ctx, cmd.CertificationID)
|
||||
// if err != nil {
|
||||
// return o.createFailureResult(cmd.CertificationID, cert.Status, "加载更新后的认证信息失败"), err
|
||||
// }
|
||||
|
||||
// // 6. 构建结果
|
||||
// message := "企业认证回调处理成功"
|
||||
// data := map[string]interface{}{
|
||||
// "auth_flow_id": cmd.CallbackData.FlowID,
|
||||
// "status": cmd.CallbackData.Status,
|
||||
// }
|
||||
|
||||
// if updatedCert.Status == enums.StatusEnterpriseVerified {
|
||||
// message = "企业认证成功"
|
||||
// data["next_action"] = "可以申请合同签署"
|
||||
// } else if updatedCert.Status == enums.StatusInfoRejected {
|
||||
// message = "企业认证失败"
|
||||
// data["next_action"] = "请修正企业信息后重新提交"
|
||||
// data["failure_reason"] = enums.GetFailureReasonName(updatedCert.FailureReason)
|
||||
// data["failure_message"] = updatedCert.FailureMessage
|
||||
// }
|
||||
|
||||
// return o.createSuccessResult(cmd.CertificationID, updatedCert.Status, message, data, nil), nil
|
||||
}
|
||||
|
||||
// HandleContractSignCallback 处理合同签署回调
|
||||
func (o *CertificationWorkflowOrchestratorImpl) HandleContractSignCallback(
|
||||
ctx context.Context,
|
||||
cmd *EsignCallbackCommand,
|
||||
) (*WorkflowResult, error) {
|
||||
o.logger.Info("开始处理合同签署回调",
|
||||
zap.String("certification_id", cmd.CertificationID),
|
||||
zap.String("callback_type", cmd.CallbackType))
|
||||
|
||||
// // 1. 验证回调数据
|
||||
// if err := o.callbackHandler.ValidateCallbackData(cmd.CallbackData); err != nil {
|
||||
// return o.createFailureResult(cmd.CertificationID, "", fmt.Sprintf("回调数据验证失败: %s", err.Error())), err
|
||||
// }
|
||||
|
||||
// // 2. 加载认证聚合根
|
||||
// cert, err := o.aggregateService.LoadCertification(ctx, cmd.CertificationID)
|
||||
// if err != nil {
|
||||
// return o.createFailureResult(cmd.CertificationID, "", fmt.Sprintf("加载认证信息失败: %s", err.Error())), err
|
||||
// }
|
||||
|
||||
// // 3. 验证回调处理前置条件
|
||||
// if cert.Status != enums.StatusContractApplied {
|
||||
// return o.createFailureResult(cmd.CertificationID, cert.Status,
|
||||
// fmt.Sprintf("当前状态 %s 不允许处理合同签署回调", enums.GetStatusName(cert.Status))),
|
||||
// fmt.Errorf("无效的状态转换")
|
||||
// }
|
||||
|
||||
// // 4. 处理回调
|
||||
// err = o.callbackHandler.HandleCallback(ctx, cmd.CertificationID, cmd.CallbackData)
|
||||
// if err != nil {
|
||||
// o.logger.Error("处理合同签署回调失败", zap.Error(err))
|
||||
// return o.createFailureResult(cmd.CertificationID, cert.Status, fmt.Sprintf("回调处理失败: %s", err.Error())), err
|
||||
// }
|
||||
|
||||
// // 5. 重新加载认证信息获取最新状态
|
||||
// updatedCert, err := o.aggregateService.LoadCertification(ctx, cmd.CertificationID)
|
||||
// if err != nil {
|
||||
// return o.createFailureResult(cmd.CertificationID, cert.Status, "加载更新后的认证信息失败"), err
|
||||
// }
|
||||
|
||||
// // 6. 构建结果
|
||||
// message := "合同签署回调处理成功"
|
||||
// data := map[string]interface{}{
|
||||
// "esign_flow_id": cmd.CallbackData.FlowID,
|
||||
// "status": cmd.CallbackData.Status,
|
||||
// }
|
||||
|
||||
// if updatedCert.Status == enums.StatusContractSigned {
|
||||
// message = "认证完成"
|
||||
// data["next_action"] = "认证流程已完成"
|
||||
// data["contract_url"] = updatedCert.ContractURL
|
||||
// } else if enums.IsFailureStatus(updatedCert.Status) {
|
||||
// message = "合同签署失败"
|
||||
// data["next_action"] = "可以重新申请合同签署"
|
||||
// data["failure_reason"] = enums.GetFailureReasonName(updatedCert.FailureReason)
|
||||
// data["failure_message"] = updatedCert.FailureMessage
|
||||
// }
|
||||
|
||||
// return o.createSuccessResult(cmd.CertificationID, updatedCert.Status, message, data, nil), nil
|
||||
return nil, nil
|
||||
|
||||
}
|
||||
|
||||
// ================ 异常处理 ================
|
||||
|
||||
// HandleFailure 处理业务失败
|
||||
func (o *CertificationWorkflowOrchestratorImpl) HandleFailure(
|
||||
ctx context.Context,
|
||||
certificationID string,
|
||||
failureType string,
|
||||
reason string,
|
||||
) (*WorkflowResult, error) {
|
||||
return nil, nil
|
||||
// o.logger.Info("开始处理业务失败",
|
||||
// zap.String("certification_id", certificationID),
|
||||
// zap.String("failure_type", failureType),
|
||||
// zap.String("reason", reason))
|
||||
|
||||
// // 1. 加载认证聚合根
|
||||
// cert, err := o.aggregateService.LoadCertification(ctx, certificationID)
|
||||
// if err != nil {
|
||||
// return o.createFailureResult(certificationID, "", fmt.Sprintf("加载认证信息失败: %s", err.Error())), err
|
||||
// }
|
||||
|
||||
// // 2. 根据失败类型执行相应处理
|
||||
// var targetStatus enums.CertificationStatus
|
||||
// var failureReason enums.FailureReason
|
||||
|
||||
// switch failureType {
|
||||
// case "enterprise_verification_failed":
|
||||
// targetStatus = enums.StatusInfoRejected
|
||||
// failureReason = enums.FailureReasonEsignVerificationFailed
|
||||
// case "contract_sign_failed":
|
||||
// targetStatus = enums.StatusContractRejected
|
||||
// failureReason = enums.FailureReasonSignProcessFailed
|
||||
// case "contract_expired":
|
||||
// targetStatus = enums.StatusContractExpired
|
||||
// failureReason = enums.FailureReasonContractExpired
|
||||
// default:
|
||||
// return o.createFailureResult(certificationID, cert.Status, fmt.Sprintf("未知的失败类型: %s", failureType)),
|
||||
// fmt.Errorf("未知的失败类型")
|
||||
// }
|
||||
|
||||
// // 3. 执行状态转换
|
||||
// metadata := map[string]interface{}{
|
||||
// "failure_reason": failureReason,
|
||||
// "failure_message": reason,
|
||||
// }
|
||||
|
||||
// result, err := o.aggregateService.TransitionState(
|
||||
// ctx,
|
||||
// certificationID,
|
||||
// targetStatus,
|
||||
// enums.ActorTypeSystem,
|
||||
// "failure_handler",
|
||||
// fmt.Sprintf("系统处理失败: %s", reason),
|
||||
// metadata,
|
||||
// )
|
||||
// if err != nil {
|
||||
// return o.createFailureResult(certificationID, cert.Status, fmt.Sprintf("失败处理状态转换失败: %s", err.Error())), err
|
||||
// }
|
||||
|
||||
// return o.createSuccessResult(certificationID, targetStatus, "失败处理完成", map[string]interface{}{
|
||||
// "failure_type": failureType,
|
||||
// "failure_reason": enums.GetFailureReasonName(failureReason),
|
||||
// "can_retry": enums.IsRetryable(failureReason),
|
||||
// }, result), nil
|
||||
}
|
||||
|
||||
// ================ 查询操作 ================
|
||||
|
||||
// GetWorkflowStatus 获取工作流状态
|
||||
func (o *CertificationWorkflowOrchestratorImpl) GetWorkflowStatus(
|
||||
ctx context.Context,
|
||||
certificationID string,
|
||||
) (*WorkflowResult, error) {
|
||||
// 加载认证聚合根
|
||||
cert, err := o.aggregateService.LoadCertification(ctx, certificationID)
|
||||
if err != nil {
|
||||
return o.createFailureResult(certificationID, "", fmt.Sprintf("加载认证信息失败: %s", err.Error())), err
|
||||
}
|
||||
|
||||
// 构建状态信息
|
||||
data := map[string]interface{}{
|
||||
"status": cert.Status,
|
||||
"status_name": enums.GetStatusName(cert.Status),
|
||||
"progress": cert.GetProgress(),
|
||||
"is_final": cert.IsFinalStatus(),
|
||||
"is_completed": cert.IsCompleted(),
|
||||
"user_action_required": cert.IsUserActionRequired(),
|
||||
"next_action": o.getNextActionForStatus(cert.Status),
|
||||
"available_actions": cert.GetAvailableActions(),
|
||||
}
|
||||
|
||||
// 添加失败信息(如果存在)
|
||||
if enums.IsFailureStatus(cert.Status) {
|
||||
data["failure_reason"] = enums.GetFailureReasonName(cert.FailureReason)
|
||||
data["failure_message"] = cert.FailureMessage
|
||||
data["can_retry"] = enums.IsRetryable(cert.FailureReason)
|
||||
data["retry_count"] = cert.RetryCount
|
||||
}
|
||||
|
||||
// 添加时间戳信息
|
||||
if cert.InfoSubmittedAt != nil {
|
||||
data["info_submitted_at"] = cert.InfoSubmittedAt
|
||||
}
|
||||
if cert.EnterpriseVerifiedAt != nil {
|
||||
data["enterprise_verified_at"] = cert.EnterpriseVerifiedAt
|
||||
}
|
||||
if cert.ContractAppliedAt != nil {
|
||||
data["contract_applied_at"] = cert.ContractAppliedAt
|
||||
}
|
||||
if cert.ContractSignedAt != nil {
|
||||
data["contract_signed_at"] = cert.ContractSignedAt
|
||||
}
|
||||
|
||||
return o.createSuccessResult(certificationID, cert.Status, "工作流状态查询成功", data), nil
|
||||
}
|
||||
|
||||
// ================ 辅助方法 ================
|
||||
|
||||
// validateApplyContractCommand 验证申请合同命令
|
||||
func (o *CertificationWorkflowOrchestratorImpl) validateApplyContractCommand(cmd *ApplyContractCommand) error {
|
||||
if cmd.CertificationID == "" {
|
||||
return fmt.Errorf("认证ID不能为空")
|
||||
}
|
||||
if cmd.UserID == "" {
|
||||
return fmt.Errorf("用户ID不能为空")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateEnterpriseInfoSubmissionPreconditions 验证企业信息提交前置条件
|
||||
func (o *CertificationWorkflowOrchestratorImpl) validateEnterpriseInfoSubmissionPreconditions(cert *entities.Certification, userID string) error {
|
||||
if cert.UserID != userID {
|
||||
return fmt.Errorf("用户无权限操作此认证申请")
|
||||
}
|
||||
if cert.Status != enums.StatusPending && cert.Status != enums.StatusInfoRejected {
|
||||
return fmt.Errorf("当前状态 %s 不允许提交企业信息", enums.GetStatusName(cert.Status))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateContractApplicationPreconditions 验证合同申请前置条件
|
||||
func (o *CertificationWorkflowOrchestratorImpl) validateContractApplicationPreconditions(cert *entities.Certification, userID string) error {
|
||||
if cert.UserID != userID {
|
||||
return fmt.Errorf("用户无权限操作此认证申请")
|
||||
}
|
||||
if cert.Status != enums.StatusEnterpriseVerified {
|
||||
return fmt.Errorf("必须先完成企业认证才能申请合同")
|
||||
}
|
||||
if cert.AuthFlowID == "" {
|
||||
return fmt.Errorf("缺少企业认证流程ID")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// triggerEnterpriseVerification 触发企业认证
|
||||
func (o *CertificationWorkflowOrchestratorImpl) triggerEnterpriseVerification(ctx context.Context, certificationID string, enterpriseInfo *value_objects.EnterpriseInfo) error {
|
||||
// TODO: 调用e签宝API进行企业认证
|
||||
o.logger.Info("触发企业认证",
|
||||
zap.String("certification_id", certificationID),
|
||||
zap.String("company_name", enterpriseInfo.CompanyName))
|
||||
return nil
|
||||
}
|
||||
|
||||
// generateContractAndSignURL 生成合同和签署链接
|
||||
func (o *CertificationWorkflowOrchestratorImpl) generateContractAndSignURL(ctx context.Context, certificationID string, cert *entities.Certification) (*value_objects.ContractInfo, error) {
|
||||
// TODO: 调用e签宝API生成合同和签署链接
|
||||
o.logger.Info("生成合同和签署链接", zap.String("certification_id", certificationID))
|
||||
|
||||
// 临时返回模拟数据
|
||||
contractInfo, err := value_objects.NewContractInfo(
|
||||
"contract_file_"+certificationID,
|
||||
"esign_flow_"+certificationID,
|
||||
"https://example.com/contract/"+certificationID,
|
||||
"https://example.com/sign/"+certificationID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return contractInfo, nil
|
||||
}
|
||||
|
||||
// getNextActionForStatus 获取状态对应的下一步操作提示
|
||||
func (o *CertificationWorkflowOrchestratorImpl) getNextActionForStatus(status enums.CertificationStatus) string {
|
||||
return enums.GetUserActionHint(status)
|
||||
}
|
||||
|
||||
// createSuccessResult 创建成功结果
|
||||
func (o *CertificationWorkflowOrchestratorImpl) createSuccessResult(
|
||||
certificationID string,
|
||||
status enums.CertificationStatus,
|
||||
message string,
|
||||
data map[string]interface{},
|
||||
) *WorkflowResult {
|
||||
return &WorkflowResult{
|
||||
Success: true,
|
||||
CertificationID: certificationID,
|
||||
CurrentStatus: status,
|
||||
Message: message,
|
||||
Data: data,
|
||||
ExecutedAt: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
// createFailureResult 创建失败结果
|
||||
func (o *CertificationWorkflowOrchestratorImpl) createFailureResult(
|
||||
certificationID string,
|
||||
status enums.CertificationStatus,
|
||||
message string,
|
||||
) *WorkflowResult {
|
||||
return &WorkflowResult{
|
||||
Success: false,
|
||||
CertificationID: certificationID,
|
||||
CurrentStatus: status,
|
||||
Message: message,
|
||||
Data: map[string]interface{}{},
|
||||
ExecutedAt: time.Now(),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hyapi-server/internal/config"
|
||||
"hyapi-server/internal/domains/api/dto"
|
||||
"hyapi-server/internal/domains/api/services/processors"
|
||||
"hyapi-server/internal/domains/api/services/processors/qygl"
|
||||
"hyapi-server/internal/domains/certification/entities"
|
||||
"hyapi-server/internal/domains/certification/entities/value_objects"
|
||||
"hyapi-server/internal/domains/certification/repositories"
|
||||
"hyapi-server/internal/infrastructure/external/alicloud"
|
||||
"hyapi-server/internal/infrastructure/external/tianyancha"
|
||||
"hyapi-server/internal/infrastructure/external/westdex"
|
||||
"hyapi-server/internal/infrastructure/external/yushan"
|
||||
"hyapi-server/internal/shared/interfaces"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// EnterpriseInfoSubmitRecordService 企业信息提交记录领域服务
|
||||
// 负责与westdex等外部服务交互
|
||||
// 领域服务应无状态
|
||||
|
||||
type EnterpriseInfoSubmitRecordService struct {
|
||||
westdexService *westdex.WestDexService
|
||||
tianYanChaService *tianyancha.TianYanChaService
|
||||
alicloudService *alicloud.AlicloudService
|
||||
yushanService *yushan.YushanService
|
||||
validator interfaces.RequestValidator
|
||||
repositories repositories.EnterpriseInfoSubmitRecordRepository
|
||||
appConfig config.AppConfig
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewEnterpriseInfoSubmitRecordService 构造函数
|
||||
func NewEnterpriseInfoSubmitRecordService(
|
||||
westdexService *westdex.WestDexService,
|
||||
tianYanChaService *tianyancha.TianYanChaService,
|
||||
alicloudService *alicloud.AlicloudService,
|
||||
yushanService *yushan.YushanService,
|
||||
validator interfaces.RequestValidator,
|
||||
repositories repositories.EnterpriseInfoSubmitRecordRepository,
|
||||
appConfig config.AppConfig,
|
||||
logger *zap.Logger,
|
||||
) *EnterpriseInfoSubmitRecordService {
|
||||
return &EnterpriseInfoSubmitRecordService{
|
||||
westdexService: westdexService,
|
||||
tianYanChaService: tianYanChaService,
|
||||
alicloudService: alicloudService,
|
||||
yushanService: yushanService,
|
||||
validator: validator,
|
||||
repositories: repositories,
|
||||
appConfig: appConfig,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Save 保存企业信息提交记录
|
||||
func (s *EnterpriseInfoSubmitRecordService) Save(ctx context.Context, enterpriseInfoSubmitRecord *entities.EnterpriseInfoSubmitRecord) error {
|
||||
exists, err := s.repositories.Exists(ctx, enterpriseInfoSubmitRecord.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists {
|
||||
return s.repositories.Update(ctx, enterpriseInfoSubmitRecord)
|
||||
}
|
||||
return s.repositories.Create(ctx, enterpriseInfoSubmitRecord)
|
||||
}
|
||||
|
||||
// ValidateWithWestdex 调用QYGL5CMP处理器验证企业信息
|
||||
func (s *EnterpriseInfoSubmitRecordService) ValidateWithWestdex(ctx context.Context, info *value_objects.EnterpriseInfo) error {
|
||||
if info == nil {
|
||||
return errors.New("企业信息不能为空")
|
||||
}
|
||||
// 先做本地校验
|
||||
if err := info.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 开发环境下跳过外部验证
|
||||
// if s.appConfig.IsDevelopment() {
|
||||
// s.logger.Info("开发环境:跳过企业信息外部验证",
|
||||
// zap.String("company_name", info.CompanyName),
|
||||
// zap.String("legal_person", info.LegalPersonName))
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// 构建QYGL5CMP请求参数
|
||||
reqDto := dto.QYGL5CMPReq{
|
||||
EntName: info.CompanyName,
|
||||
LegalPerson: info.LegalPersonName,
|
||||
EntCode: info.UnifiedSocialCode,
|
||||
IDCard: info.LegalPersonID,
|
||||
MobileNo: info.LegalPersonPhone,
|
||||
}
|
||||
|
||||
// 序列化请求参数
|
||||
paramsBytes, err := json.Marshal(reqDto)
|
||||
if err != nil {
|
||||
return fmt.Errorf("序列化请求参数失败: %w", err)
|
||||
}
|
||||
|
||||
// 创建处理器依赖
|
||||
deps := &processors.ProcessorDependencies{
|
||||
WestDexService: s.westdexService,
|
||||
TianYanChaService: s.tianYanChaService,
|
||||
AlicloudService: s.alicloudService,
|
||||
YushanService: s.yushanService,
|
||||
Validator: s.validator,
|
||||
}
|
||||
|
||||
// 调用QYGL23T7处理器进行验证
|
||||
responseBytes, err := qygl.ProcessQYGL23T7Request(ctx, paramsBytes, deps)
|
||||
if err != nil {
|
||||
// 检查是否是数据源错误企业信息不一致
|
||||
if errors.Is(err, processors.ErrDatasource) {
|
||||
return fmt.Errorf("数据源异常: %w", err)
|
||||
}
|
||||
return fmt.Errorf("企业信息验证失败: %w", err)
|
||||
}
|
||||
|
||||
// 解析响应结果
|
||||
var response map[string]interface{}
|
||||
if err := json.Unmarshal(responseBytes, &response); err != nil {
|
||||
return fmt.Errorf("解析响应结果失败: %w", err)
|
||||
}
|
||||
|
||||
// 检查验证状态
|
||||
status, ok := response["status"].(float64)
|
||||
if !ok {
|
||||
return fmt.Errorf("响应格式错误")
|
||||
}
|
||||
|
||||
// 根据状态码判断验证结果
|
||||
switch int(status) {
|
||||
case 0:
|
||||
// 验证通过
|
||||
s.logger.Info("企业信息验证通过",
|
||||
zap.String("company_name", info.CompanyName),
|
||||
zap.String("legal_person", info.LegalPersonName))
|
||||
return nil
|
||||
case 1:
|
||||
// 企业信息不一致
|
||||
return fmt.Errorf("企业信息不一致")
|
||||
case 2:
|
||||
// 身份证信息不一致
|
||||
return fmt.Errorf("身份证信息不一致")
|
||||
default:
|
||||
return fmt.Errorf("未知的验证状态: %d", int(status))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user