certimate/internal/workflow/engine/state.go
2025-09-25 17:10:19 +08:00

304 lines
7.3 KiB
Go

package engine
import (
"fmt"
"slices"
"strconv"
"sync"
"time"
)
type VariableState struct {
Scope string // 零值时表示全局的,否则表示指定节点的
Key string
Value any
ValueType string
}
func (s VariableState) ValueString() string {
switch s.ValueType {
case "string":
return fmt.Sprintf("%s", s.Value)
case "number":
return fmt.Sprintf("%d", s.Value)
case "boolean":
return strconv.FormatBool(s.Value.(bool))
case "datetime":
valueAsTime := s.Value.(time.Time)
if valueAsTime.IsZero() {
return "-"
}
return valueAsTime.Format(time.RFC3339)
default:
return fmt.Sprintf("[%s]%v", s.ValueType, s.Value)
}
}
type VariableManager interface {
All() []VariableState
Erase()
Add(entry VariableState)
Set(name string, value any, valueType string)
SetScoped(scope string, name string, value any, valueType string)
Get(name string) (*VariableState, bool)
GetScoped(scope string, key string) (*VariableState, bool)
Take(key string) (*VariableState, bool)
TakeScoped(scope string, key string) (*VariableState, bool)
Remove(key string) bool
RemoveScoped(scope string, key string) bool
}
type variableManager struct {
statesMtx sync.RWMutex
states []VariableState
}
var _ VariableManager = (*variableManager)(nil)
func (m *variableManager) All() []VariableState {
m.statesMtx.RLock()
defer m.statesMtx.RUnlock()
if m.states == nil {
return make([]VariableState, 0)
}
return slices.Clone(m.states)
}
func (m *variableManager) Erase() {
m.statesMtx.Lock()
defer m.statesMtx.Unlock()
m.states = make([]VariableState, 0)
}
func (m *variableManager) Add(state VariableState) {
m.statesMtx.Lock()
defer m.statesMtx.Unlock()
if m.states == nil {
m.states = make([]VariableState, 0)
}
for i, item := range m.states {
if item.Scope == state.Scope && item.Key == state.Key {
m.states[i] = state
return
}
}
m.states = append(m.states, state)
}
func (m *variableManager) Set(key string, value any, valueType string) {
m.SetScoped("", key, value, valueType)
}
func (m *variableManager) SetScoped(scope string, key string, value any, valueType string) {
m.Add(VariableState{Scope: scope, Key: key, Value: value, ValueType: valueType})
}
func (m *variableManager) Get(key string) (*VariableState, bool) {
return m.GetScoped("", key)
}
func (m *variableManager) GetScoped(scope string, key string) (*VariableState, bool) {
m.statesMtx.RLock()
defer m.statesMtx.RUnlock()
if m.states == nil {
return nil, false
}
for _, item := range m.states {
if item.Scope == scope && item.Key == key {
return &item, true
}
}
return nil, false
}
func (m *variableManager) Take(key string) (*VariableState, bool) {
return m.TakeScoped("", key)
}
func (m *variableManager) TakeScoped(scope string, key string) (*VariableState, bool) {
m.statesMtx.Lock()
defer m.statesMtx.Unlock()
if m.states == nil {
return nil, false
}
for i, item := range m.states {
if item.Scope == scope && item.Key == key {
m.states = slices.Delete(m.states, i, i+1)
return &item, true
}
}
return nil, false
}
func (m *variableManager) Remove(key string) bool {
return m.RemoveScoped("", key)
}
func (m *variableManager) RemoveScoped(scope string, key string) bool {
_, ok := m.TakeScoped(scope, key)
return ok
}
func newVariableManager() VariableManager {
return &variableManager{
states: make([]VariableState, 0),
}
}
type InOutState struct {
NodeId string
Type string
Name string
Value any
ValueType string
Persistent bool
}
func (s InOutState) ValueString() string {
switch s.ValueType {
case "string":
return s.Value.(string)
case "number":
return fmt.Sprintf("%d", s.Value)
case "boolean":
return strconv.FormatBool(s.Value.(bool))
default:
return fmt.Sprintf("%v", s.Value)
}
}
type InOutManager interface {
All() []InOutState
Erase()
Add(state InOutState)
Set(nodeId string, stype string, name string, value any, valueType string, persistent bool)
Get(nodeId string, name string) (*InOutState, bool)
Take(nodeId string, name string) (*InOutState, bool)
Remove(nodeId string, name string) bool
}
type inoutManager struct {
statesMtx sync.RWMutex
states []InOutState
}
var _ InOutManager = (*inoutManager)(nil)
func (m *inoutManager) All() []InOutState {
m.statesMtx.RLock()
defer m.statesMtx.RUnlock()
if m.states == nil {
return make([]InOutState, 0)
}
return slices.Clone(m.states)
}
func (m *inoutManager) Erase() {
m.statesMtx.Lock()
defer m.statesMtx.Unlock()
m.states = make([]InOutState, 0)
}
func (m *inoutManager) Add(state InOutState) {
m.statesMtx.Lock()
defer m.statesMtx.Unlock()
if m.states == nil {
m.states = make([]InOutState, 0)
}
for i, item := range m.states {
if item.NodeId == state.NodeId && item.Name == state.Name {
m.states[i] = state
return
}
}
m.states = append(m.states, state)
}
func (m *inoutManager) Set(nodeId string, stype string, name string, value any, valueType string, persistent bool) {
m.Add(InOutState{NodeId: nodeId, Type: stype, Name: name, Value: value, ValueType: valueType, Persistent: persistent})
}
func (m *inoutManager) Get(nodeId string, name string) (*InOutState, bool) {
m.statesMtx.RLock()
defer m.statesMtx.RUnlock()
if m.states == nil {
return nil, false
}
for _, item := range m.states {
if item.NodeId == nodeId && item.Name == name {
return &item, true
}
}
return nil, false
}
func (m *inoutManager) Take(nodeId string, name string) (*InOutState, bool) {
m.statesMtx.Lock()
defer m.statesMtx.Unlock()
if m.states == nil {
return nil, false
}
for i, item := range m.states {
if item.NodeId == nodeId && item.Name == name {
m.states = slices.Delete(m.states, i, i+1)
return &item, true
}
}
return nil, false
}
func (m *inoutManager) Remove(nodeId string, name string) bool {
_, ok := m.Take(nodeId, name)
return ok
}
func newInOutManager() InOutManager {
return &inoutManager{
states: make([]InOutState, 0),
}
}
const (
stateIOTypeRef = "ref"
)
const (
stateVarKeyWorkflowId = "workflow.id" // ValueType: "string"
stateVarKeyWorkflowName = "workflow.name" // ValueType: "string"
stateVarKeyRunId = "run.id" // ValueType: "string"
stateVarKeyRunTrigger = "run.trigger" // ValueType: "string"
stateVarKeyNodeId = "node.id" // ValueType: "string"
stateVarKeyNodeName = "node.name" // ValueType: "string"
stateVarKeyNodeSkipped = "node.skipped" // ValueType: "boolean"
stateVarKeyErrorNodeId = "error.nodeId" // ValueType: "string"
stateVarKeyErrorNodeName = "error.nodeName" // ValueType: "string"
stateVarKeyErrorMessage = "error.message" // ValueType: "string"
stateVarKeyCertificateDomain = "certificate.domain" // ValueType: "string"
stateVarKeyCertificateDomains = "certificate.domains" // ValueType: "string"
stateVarKeyCertificateNotBefore = "certificate.notBefore" // ValueType: "datetime"
stateVarKeyCertificateNotAfter = "certificate.notAfter" // ValueType: "datetime"
stateVarKeyCertificateHoursLeft = "certificate.hoursLeft" // ValueType: "number"
stateVarKeyCertificateDaysLeft = "certificate.daysLeft" // ValueType: "number"
stateVarKeyCertificateValidity = "certificate.validity" // ValueType: "boolean"
)