288 lines
8.5 KiB
Go
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 ""
|
|
}
|