Files
tyapi-server/internal/domains/certification/services/state_machine.go
2025-07-11 21:05:58 +08:00

288 lines
8.5 KiB
Go

package services
import (
"context"
"fmt"
"time"
"tyapi-server/internal/domains/certification/entities"
"tyapi-server/internal/domains/certification/enums"
"tyapi-server/internal/domains/certification/repositories"
"go.uber.org/zap"
)
// StateTransition 状态转换规则
type StateTransition struct {
From enums.CertificationStatus
To enums.CertificationStatus
Action string
AllowUser bool // 是否允许用户操作
AllowAdmin bool // 是否允许管理员操作
RequiresValidation bool // 是否需要额外验证
}
// CertificationStateMachine 认证状态机
type CertificationStateMachine struct {
transitions map[enums.CertificationStatus][]StateTransition
certRepo repositories.CertificationRepository
logger *zap.Logger
}
// NewCertificationStateMachine 创建认证状态机
func NewCertificationStateMachine(
certRepo repositories.CertificationRepository,
logger *zap.Logger,
) *CertificationStateMachine {
sm := &CertificationStateMachine{
transitions: make(map[enums.CertificationStatus][]StateTransition),
certRepo: certRepo,
logger: logger,
}
// 初始化状态转换规则
sm.initializeTransitions()
return sm
}
// initializeTransitions 初始化状态转换规则
func (sm *CertificationStateMachine) initializeTransitions() {
transitions := []StateTransition{
// 正常流程转换
{enums.StatusPending, enums.StatusInfoSubmitted, "submit_info", true, false, true},
{enums.StatusInfoSubmitted, enums.StatusFaceVerified, "face_verify", true, false, true},
{enums.StatusFaceVerified, enums.StatusContractApplied, "apply_contract", true, false, false},
{enums.StatusContractApplied, enums.StatusContractPending, "system_process", false, false, false},
{enums.StatusContractPending, enums.StatusContractApproved, "admin_approve", false, true, true},
{enums.StatusContractApproved, enums.StatusContractSigned, "user_sign", true, false, true},
{enums.StatusContractSigned, enums.StatusCompleted, "system_complete", false, false, false},
// 失败和重试转换
{enums.StatusInfoSubmitted, enums.StatusFaceFailed, "face_fail", false, false, false},
{enums.StatusFaceFailed, enums.StatusFaceVerified, "retry_face", true, false, true},
{enums.StatusContractPending, enums.StatusRejected, "admin_reject", false, true, true},
{enums.StatusRejected, enums.StatusInfoSubmitted, "restart_process", true, false, false},
{enums.StatusContractApproved, enums.StatusSignFailed, "sign_fail", false, false, false},
{enums.StatusSignFailed, enums.StatusContractSigned, "retry_sign", true, false, true},
}
// 构建状态转换映射
for _, transition := range transitions {
sm.transitions[transition.From] = append(sm.transitions[transition.From], transition)
}
}
// CanTransition 检查是否可以转换到指定状态
func (sm *CertificationStateMachine) CanTransition(
from enums.CertificationStatus,
to enums.CertificationStatus,
isUser bool,
isAdmin bool,
) (bool, string) {
validTransitions, exists := sm.transitions[from]
if !exists {
return false, "当前状态不支持任何转换"
}
for _, transition := range validTransitions {
if transition.To == to {
if isUser && !transition.AllowUser {
return false, "用户不允许执行此操作"
}
if isAdmin && !transition.AllowAdmin {
return false, "管理员不允许执行此操作"
}
if !isUser && !isAdmin && (transition.AllowUser || transition.AllowAdmin) {
return false, "此操作需要用户或管理员权限"
}
return true, ""
}
}
return false, "不支持的状态转换"
}
// TransitionTo 执行状态转换
func (sm *CertificationStateMachine) TransitionTo(
ctx context.Context,
certificationID string,
targetStatus enums.CertificationStatus,
isUser bool,
isAdmin bool,
metadata map[string]interface{},
) error {
// 获取当前认证记录
cert, err := sm.certRepo.GetByID(ctx, certificationID)
if err != nil {
return fmt.Errorf("获取认证记录失败: %w", err)
}
// 检查是否可以转换
canTransition, reason := sm.CanTransition(cert.Status, targetStatus, isUser, isAdmin)
if !canTransition {
return fmt.Errorf("状态转换失败: %s", reason)
}
// 执行状态转换前的验证
if err := sm.validateTransition(ctx, cert, targetStatus, metadata); err != nil {
return fmt.Errorf("状态转换验证失败: %w", err)
}
// 更新状态和时间戳
oldStatus := cert.Status
cert.Status = targetStatus
sm.updateTimestamp(cert, targetStatus)
// 更新其他字段
sm.updateCertificationFields(cert, targetStatus, metadata)
// 保存到数据库
if err := sm.certRepo.Update(ctx, cert); err != nil {
return fmt.Errorf("保存状态转换失败: %w", err)
}
sm.logger.Info("认证状态转换成功",
zap.String("certification_id", certificationID),
zap.String("from_status", string(oldStatus)),
zap.String("to_status", string(targetStatus)),
zap.Bool("is_user", isUser),
zap.Bool("is_admin", isAdmin),
)
return nil
}
// updateTimestamp 更新对应的时间戳字段
func (sm *CertificationStateMachine) updateTimestamp(cert *entities.Certification, status enums.CertificationStatus) {
now := time.Now()
switch status {
case enums.StatusInfoSubmitted:
cert.InfoSubmittedAt = &now
case enums.StatusFaceVerified:
cert.FaceVerifiedAt = &now
case enums.StatusContractApplied:
cert.ContractAppliedAt = &now
case enums.StatusContractApproved:
cert.ContractApprovedAt = &now
case enums.StatusContractSigned:
cert.ContractSignedAt = &now
case enums.StatusCompleted:
cert.CompletedAt = &now
}
}
// updateCertificationFields 根据状态更新认证记录的其他字段
func (sm *CertificationStateMachine) updateCertificationFields(
cert *entities.Certification,
status enums.CertificationStatus,
metadata map[string]interface{},
) {
switch status {
case enums.StatusContractApproved:
if adminID, ok := metadata["admin_id"].(string); ok {
cert.AdminID = &adminID
}
if approvalNotes, ok := metadata["approval_notes"].(string); ok {
cert.ApprovalNotes = approvalNotes
}
if signingURL, ok := metadata["signing_url"].(string); ok {
cert.SigningURL = signingURL
}
case enums.StatusRejected:
if adminID, ok := metadata["admin_id"].(string); ok {
cert.AdminID = &adminID
}
if rejectReason, ok := metadata["reject_reason"].(string); ok {
cert.RejectReason = rejectReason
}
case enums.StatusContractSigned:
if contractURL, ok := metadata["contract_url"].(string); ok {
cert.ContractURL = contractURL
}
}
}
// validateTransition 验证状态转换的有效性
func (sm *CertificationStateMachine) validateTransition(
ctx context.Context,
cert *entities.Certification,
targetStatus enums.CertificationStatus,
metadata map[string]interface{},
) error {
switch targetStatus {
case enums.StatusInfoSubmitted:
// 验证企业信息是否完整
if cert.EnterpriseID == nil {
return fmt.Errorf("企业信息未提交")
}
case enums.StatusFaceVerified:
// 验证人脸识别是否成功
// 这里可以添加人脸识别结果的验证逻辑
case enums.StatusContractApproved:
// 验证管理员审核信息
if metadata["signing_url"] == nil || metadata["signing_url"].(string) == "" {
return fmt.Errorf("缺少合同签署链接")
}
case enums.StatusRejected:
// 验证拒绝原因
if metadata["reject_reason"] == nil || metadata["reject_reason"].(string) == "" {
return fmt.Errorf("缺少拒绝原因")
}
case enums.StatusContractSigned:
// 验证合同签署信息
if cert.SigningURL == "" {
return fmt.Errorf("缺少合同签署链接")
}
}
return nil
}
// GetValidNextStatuses 获取当前状态可以转换到的下一个状态列表
func (sm *CertificationStateMachine) GetValidNextStatuses(
currentStatus enums.CertificationStatus,
isUser bool,
isAdmin bool,
) []enums.CertificationStatus {
var validStatuses []enums.CertificationStatus
transitions, exists := sm.transitions[currentStatus]
if !exists {
return validStatuses
}
for _, transition := range transitions {
if (isUser && transition.AllowUser) || (isAdmin && transition.AllowAdmin) {
validStatuses = append(validStatuses, transition.To)
}
}
return validStatuses
}
// GetTransitionAction 获取状态转换对应的操作名称
func (sm *CertificationStateMachine) GetTransitionAction(
from enums.CertificationStatus,
to enums.CertificationStatus,
) string {
transitions, exists := sm.transitions[from]
if !exists {
return ""
}
for _, transition := range transitions {
if transition.To == to {
return transition.Action
}
}
return ""
}