116 lines
3.2 KiB
Go
116 lines
3.2 KiB
Go
package state_machine
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
|
|
"github.com/gogf/gf/v2/os/glog"
|
|
|
|
"server/internal/consts"
|
|
"server/utility/ecode"
|
|
)
|
|
|
|
// AdStateMachine 广告状态机
|
|
type AdStateMachine struct {
|
|
mu sync.RWMutex // 读写锁,保证并发安全
|
|
// 状态流转规则
|
|
transitions map[consts.AdState][]consts.AdState
|
|
// 终止状态
|
|
terminalStates map[consts.AdState]bool
|
|
}
|
|
|
|
// NewAdStateMachine 创建新的广告状态机
|
|
func NewAdStateMachine() *AdStateMachine {
|
|
sm := &AdStateMachine{
|
|
transitions: make(map[consts.AdState][]consts.AdState),
|
|
terminalStates: make(map[consts.AdState]bool),
|
|
}
|
|
|
|
// 初始化状态流转规则
|
|
sm.initTransitions()
|
|
return sm
|
|
}
|
|
|
|
// initTransitions 初始化状态流转规则
|
|
func (sm *AdStateMachine) initTransitions() {
|
|
// 定义状态流转规则
|
|
sm.transitions = map[consts.AdState][]consts.AdState{
|
|
consts.StateFetchSuccess: {consts.StateDisplayFailed, consts.StateDisplaySuccess},
|
|
consts.StateDisplaySuccess: {consts.StateNotWatched, consts.StateWatched},
|
|
consts.StateWatched: {consts.StateNotClicked, consts.StateClicked},
|
|
consts.StateClicked: {consts.StateNotDownloaded, consts.StateDownloaded},
|
|
consts.StateNotDownloaded: {consts.StateDownloaded},
|
|
}
|
|
|
|
// 定义终止状态
|
|
sm.terminalStates = map[consts.AdState]bool{
|
|
consts.StateFetchFailed: true,
|
|
consts.StateDisplayFailed: true,
|
|
consts.StateNotWatched: true,
|
|
consts.StateNotClicked: true,
|
|
consts.StateDownloaded: true,
|
|
}
|
|
}
|
|
|
|
// CanTransition 检查是否可以转换到目标状态
|
|
func (sm *AdStateMachine) CanTransition(fromState, toState consts.AdState) bool {
|
|
sm.mu.RLock()
|
|
defer sm.mu.RUnlock()
|
|
|
|
// 检查是否为终止状态
|
|
if sm.terminalStates[fromState] {
|
|
return false
|
|
}
|
|
|
|
// 检查允许的转换
|
|
allowedStates, exists := sm.transitions[fromState]
|
|
if !exists {
|
|
return false
|
|
}
|
|
|
|
for _, allowed := range allowedStates {
|
|
if allowed == toState {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// Transition 执行状态转换
|
|
func (sm *AdStateMachine) Transition(ctx context.Context, flowID string, userID int64, fromState, toState consts.AdState, reason string) error {
|
|
sm.mu.Lock()
|
|
defer sm.mu.Unlock()
|
|
|
|
// 验证状态转换
|
|
if !sm.CanTransition(fromState, toState) {
|
|
glog.Warningf(ctx, "Invalid state transition: %s -> %s, FlowID: %s, UserID: %d, Reason: %s",
|
|
consts.GetStateDescription(fromState), consts.GetStateDescription(toState), flowID, userID, reason)
|
|
return ecode.Params.Sub("invalid_state_transition")
|
|
}
|
|
|
|
// 记录状态转换日志
|
|
glog.Infof(ctx, "State transition: %s -> %s, FlowID: %s, UserID: %d, Reason: %s",
|
|
consts.GetStateDescription(fromState), consts.GetStateDescription(toState), flowID, userID, reason)
|
|
|
|
return nil
|
|
}
|
|
|
|
// IsTerminalState 检查是否为终止状态
|
|
func (sm *AdStateMachine) IsTerminalState(state consts.AdState) bool {
|
|
sm.mu.RLock()
|
|
defer sm.mu.RUnlock()
|
|
return sm.terminalStates[state]
|
|
}
|
|
|
|
// GetAvailableTransitions 获取当前状态可用的转换
|
|
func (sm *AdStateMachine) GetAvailableTransitions(currentState consts.AdState) []consts.AdState {
|
|
sm.mu.RLock()
|
|
defer sm.mu.RUnlock()
|
|
|
|
if transitions, exists := sm.transitions[currentState]; exists {
|
|
return transitions
|
|
}
|
|
return []consts.AdState{}
|
|
}
|