This commit is contained in:
2025-07-28 01:46:39 +08:00
parent b03129667a
commit 357639462a
219 changed files with 21634 additions and 8138 deletions

View File

@@ -16,18 +16,21 @@ import (
// 这是企业认证流程的核心聚合根,封装了完整的认证业务逻辑和状态管理
type Certification struct {
// === 基础信息 ===
ID string `gorm:"primaryKey;type:varchar(36)" json:"id" comment:"认证申请唯一标识"`
UserID string `gorm:"type:varchar(36);not null;index" json:"user_id" comment:"申请用户ID"`
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:"合同签署完成时间"`
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:"合同文件访问链接"`
@@ -110,12 +113,6 @@ func (c *Certification) CanTransitionTo(targetStatus enums.CertificationStatus,
if !c.validateActorPermission(targetStatus, actor) {
return false, fmt.Sprintf("%s 无权执行此状态转换", enums.GetActorTypeName(actor))
}
// 检查业务规则
if err := c.validateBusinessRules(targetStatus, actor); err != nil {
return false, err.Error()
}
return true, ""
}
@@ -157,7 +154,7 @@ func (c *Certification) TransitionTo(targetStatus enums.CertificationStatus, act
// ================ 业务操作方法 ================
// SubmitEnterpriseInfo 提交企业信息
func (c *Certification) SubmitEnterpriseInfo(enterpriseInfo *value_objects.EnterpriseInfo) error {
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))
@@ -167,7 +164,12 @@ func (c *Certification) SubmitEnterpriseInfo(enterpriseInfo *value_objects.Enter
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
@@ -184,6 +186,26 @@ func (c *Certification) SubmitEnterpriseInfo(enterpriseInfo *value_objects.Enter
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 {
// 验证当前状态
@@ -227,17 +249,19 @@ func (c *Certification) HandleEnterpriseVerificationCallback(success bool, authF
}
// ApplyContract 申请合同签署
func (c *Certification) ApplyContract() error {
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,
@@ -248,6 +272,15 @@ func (c *Certification) ApplyContract() error {
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 {
// 验证合同信息
@@ -264,57 +297,76 @@ func (c *Certification) UpdateContractInfo(contractInfo *value_objects.ContractI
return nil
}
// HandleContractSignCallback 处理合同签署回调
func (c *Certification) HandleContractSignCallback(success bool, contractURL string, failureReason enums.FailureReason, message string) error {
// SignSuccess 签署成功
func (c *Certification) SignSuccess() error {
// 验证当前状态
if c.Status != enums.StatusContractApplied {
return fmt.Errorf("当前状态 %s 不允许处理合同签署回调", enums.GetStatusName(c.Status))
}
if success {
// 签署成功 - 认证完成
c.ContractURL = contractURL
if err := c.TransitionTo(enums.StatusContractSigned, enums.ActorTypeEsign, "esign_system", "合同签署成功,认证完成"); err != nil {
return err
}
c.addDomainEvent(&ContractSignedEvent{
CertificationID: c.ID,
UserID: c.UserID,
ContractURL: contractURL,
SignedAt: time.Now(),
})
c.addDomainEvent(&CertificationCompletedEvent{
CertificationID: c.ID,
UserID: c.UserID,
CompletedAt: time.Now(),
})
} else {
// 签署失败
c.setFailureInfo(failureReason, message)
var targetStatus enums.CertificationStatus
if failureReason == enums.FailureReasonContractExpired {
targetStatus = enums.StatusContractExpired
} else {
targetStatus = enums.StatusContractRejected
}
if err := c.TransitionTo(targetStatus, enums.ActorTypeEsign, "esign_system", "合同签署失败"); err != nil {
return err
}
c.addDomainEvent(&ContractSignFailedEvent{
CertificationID: c.ID,
UserID: c.UserID,
FailureReason: failureReason,
FailureMessage: message,
FailedAt: time.Now(),
})
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
}
@@ -369,7 +421,58 @@ func (c *Certification) RetryFromFailure(actor enums.ActorType, actorID string)
return nil
}
// CompleteCertification 完成认证
func (c *Certification) CompleteCertification() error {
// 验证当前状态
if c.Status != enums.StatusContractSigned {
return fmt.Errorf("当前状态 %s 不允许完成认证", enums.GetStatusName(c.Status))
}
// 验证合同信息完整性
if c.ContractFileID == "" || c.EsignFlowID == "" || c.ContractURL == "" {
return errors.New("合同信息不完整,无法完成认证")
}
// 状态转换
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.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:
data["contract_url"] = c.ContractURL
case enums.StatusCompleted:
data["contract_url"] = c.ContractURL
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 {
@@ -414,9 +517,9 @@ func (c *Certification) IsFinalStatus() bool {
return enums.IsFinalStatus(c.Status)
}
// IsCompleted 是否已完成认证
// IsCompleted 是否已完成
func (c *Certification) IsCompleted() bool {
return c.Status == enums.StatusContractSigned
return c.Status == enums.StatusCompleted
}
// GetNextValidStatuses 获取下一个有效状态
@@ -429,6 +532,28 @@ 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 验证业务规则
@@ -445,17 +570,20 @@ func (c *Certification) ValidateBusinessRules() error {
// 状态相关验证
switch c.Status {
case enums.StatusEnterpriseVerified:
if c.AuthFlowID == "" {
return errors.New("企业认证状态下必须有认证流程ID")
}
case enums.StatusContractApplied:
if c.AuthFlowID == "" {
return errors.New("合同申请状态下必须有企业认证流程ID")
if c.ContractURL == "" {
return errors.New("企业认证成功后合同文件ID和合同URL不能为空")
}
case enums.StatusContractSigned:
if c.ContractFileID == "" || c.EsignFlowID == "" {
return errors.New("合同签署状态下必须有完整的合同信息")
}
case enums.StatusCompleted:
if c.ContractFileID == "" || c.EsignFlowID == "" || c.ContractURL == "" {
return errors.New("认证完成状态下必须有完整的合同信息")
}
if c.CompletedAt == nil {
return errors.New("认证完成状态下必须有完成时间")
}
}
// 失败状态验证
@@ -475,13 +603,14 @@ func (c *Certification) ValidateBusinessRules() error {
func (c *Certification) validateActorPermission(targetStatus enums.CertificationStatus, actor enums.ActorType) bool {
// 定义状态转换的权限规则
permissions := map[enums.CertificationStatus][]enums.ActorType{
enums.StatusInfoSubmitted: {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.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]
@@ -498,44 +627,6 @@ func (c *Certification) validateActorPermission(targetStatus enums.Certification
return false
}
// validateBusinessRules 验证业务规则
func (c *Certification) validateBusinessRules(targetStatus enums.CertificationStatus, actor enums.ActorType) error {
// 用户操作验证
if actor == enums.ActorTypeUser {
switch targetStatus {
case enums.StatusInfoSubmitted:
// 用户提交企业信息时的验证
if c.Status != enums.StatusPending && c.Status != enums.StatusInfoRejected {
return fmt.Errorf("当前状态 %s 不允许提交企业信息", enums.GetStatusName(c.Status))
}
case enums.StatusContractApplied:
// 用户申请合同时的验证
if c.Status != enums.StatusEnterpriseVerified {
return fmt.Errorf("必须先完成企业认证才能申请合同")
}
if c.AuthFlowID == "" {
return errors.New("缺少企业认证流程ID")
}
}
}
// e签宝回调验证
if actor == enums.ActorTypeEsign {
switch targetStatus {
case enums.StatusEnterpriseVerified, enums.StatusInfoRejected:
if c.Status != enums.StatusInfoSubmitted {
return fmt.Errorf("当前状态 %s 不允许处理企业认证回调", enums.GetStatusName(c.Status))
}
case enums.StatusContractSigned, enums.StatusContractRejected, enums.StatusContractExpired:
if c.Status != enums.StatusContractApplied {
return fmt.Errorf("当前状态 %s 不允许处理合同签署回调", enums.GetStatusName(c.Status))
}
}
}
return nil
}
// ================ 辅助方法 ================
// updateTimestampByStatus 根据状态更新对应的时间戳
@@ -551,6 +642,8 @@ func (c *Certification) updateTimestampByStatus(status enums.CertificationStatus
c.ContractAppliedAt = &now
case enums.StatusContractSigned:
c.ContractSignedAt = &now
case enums.StatusCompleted:
c.CompletedAt = &now
}
}