mirror of
https://github.com/oarkflow/mq.git
synced 2025-09-27 04:15:52 +08:00
596 lines
16 KiB
Go
596 lines
16 KiB
Go
package dag
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// WorkflowEngineManager integrates the complete workflow engine capabilities into DAG
|
|
type WorkflowEngineManager struct {
|
|
registry *WorkflowRegistry
|
|
stateManager *AdvancedWorkflowStateManager
|
|
processorFactory *ProcessorFactory
|
|
scheduler *WorkflowScheduler
|
|
executor *WorkflowExecutor
|
|
middleware *WorkflowMiddleware
|
|
security *WorkflowSecurity
|
|
config *WorkflowEngineConfig
|
|
mu sync.RWMutex
|
|
running bool
|
|
}
|
|
|
|
// NewWorkflowScheduler creates a new workflow scheduler
|
|
func NewWorkflowScheduler(stateManager *AdvancedWorkflowStateManager, executor *WorkflowExecutor) *WorkflowScheduler {
|
|
return &WorkflowScheduler{
|
|
stateManager: stateManager,
|
|
executor: executor,
|
|
scheduledTasks: make(map[string]*ScheduledTask),
|
|
}
|
|
}
|
|
|
|
// WorkflowEngineConfig configures the workflow engine
|
|
type WorkflowEngineConfig struct {
|
|
MaxConcurrentExecutions int `json:"max_concurrent_executions"`
|
|
DefaultTimeout time.Duration `json:"default_timeout"`
|
|
EnablePersistence bool `json:"enable_persistence"`
|
|
EnableSecurity bool `json:"enable_security"`
|
|
EnableMiddleware bool `json:"enable_middleware"`
|
|
EnableScheduling bool `json:"enable_scheduling"`
|
|
RetryConfig *RetryConfig `json:"retry_config"`
|
|
}
|
|
|
|
// WorkflowScheduler handles workflow scheduling and timing
|
|
type WorkflowScheduler struct {
|
|
stateManager *AdvancedWorkflowStateManager
|
|
executor *WorkflowExecutor
|
|
scheduledTasks map[string]*ScheduledTask
|
|
mu sync.RWMutex
|
|
running bool
|
|
}
|
|
|
|
// WorkflowRegistry manages workflow definitions
|
|
type WorkflowRegistry struct {
|
|
workflows map[string]*WorkflowDefinition
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
// NewWorkflowRegistry creates a new workflow registry
|
|
func NewWorkflowRegistry() *WorkflowRegistry {
|
|
return &WorkflowRegistry{
|
|
workflows: make(map[string]*WorkflowDefinition),
|
|
}
|
|
}
|
|
|
|
// Store stores a workflow definition
|
|
func (r *WorkflowRegistry) Store(ctx context.Context, definition *WorkflowDefinition) error {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
if definition.ID == "" {
|
|
return fmt.Errorf("workflow ID cannot be empty")
|
|
}
|
|
|
|
r.workflows[definition.ID] = definition
|
|
return nil
|
|
}
|
|
|
|
// Get retrieves a workflow definition
|
|
func (r *WorkflowRegistry) Get(ctx context.Context, id string, version string) (*WorkflowDefinition, error) {
|
|
r.mu.RLock()
|
|
defer r.mu.RUnlock()
|
|
|
|
workflow, exists := r.workflows[id]
|
|
if !exists {
|
|
return nil, fmt.Errorf("workflow not found: %s", id)
|
|
}
|
|
|
|
// If version specified, check version match
|
|
if version != "" && workflow.Version != version {
|
|
return nil, fmt.Errorf("workflow version mismatch: requested %s, found %s", version, workflow.Version)
|
|
}
|
|
|
|
return workflow, nil
|
|
}
|
|
|
|
// List returns all workflow definitions
|
|
func (r *WorkflowRegistry) List(ctx context.Context) ([]*WorkflowDefinition, error) {
|
|
r.mu.RLock()
|
|
defer r.mu.RUnlock()
|
|
|
|
workflows := make([]*WorkflowDefinition, 0, len(r.workflows))
|
|
for _, workflow := range r.workflows {
|
|
workflows = append(workflows, workflow)
|
|
}
|
|
|
|
return workflows, nil
|
|
}
|
|
|
|
// Delete removes a workflow definition
|
|
func (r *WorkflowRegistry) Delete(ctx context.Context, id string) error {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
if _, exists := r.workflows[id]; !exists {
|
|
return fmt.Errorf("workflow not found: %s", id)
|
|
}
|
|
|
|
delete(r.workflows, id)
|
|
return nil
|
|
}
|
|
|
|
// AdvancedWorkflowStateManager manages workflow execution state
|
|
type AdvancedWorkflowStateManager struct {
|
|
executions map[string]*WorkflowExecution
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
// NewAdvancedWorkflowStateManager creates a new state manager
|
|
func NewAdvancedWorkflowStateManager() *AdvancedWorkflowStateManager {
|
|
return &AdvancedWorkflowStateManager{
|
|
executions: make(map[string]*WorkflowExecution),
|
|
}
|
|
}
|
|
|
|
// CreateExecution creates a new workflow execution
|
|
func (sm *AdvancedWorkflowStateManager) CreateExecution(ctx context.Context, workflowID string, input map[string]interface{}) (*WorkflowExecution, error) {
|
|
execution := &WorkflowExecution{
|
|
ID: generateExecutionID(),
|
|
WorkflowID: workflowID,
|
|
Status: ExecutionStatusPending,
|
|
StartTime: time.Now(),
|
|
Context: ctx,
|
|
Input: input,
|
|
NodeExecutions: make(map[string]*NodeExecution),
|
|
}
|
|
|
|
sm.mu.Lock()
|
|
sm.executions[execution.ID] = execution
|
|
sm.mu.Unlock()
|
|
|
|
return execution, nil
|
|
}
|
|
|
|
// GetExecution retrieves an execution by ID
|
|
func (sm *AdvancedWorkflowStateManager) GetExecution(ctx context.Context, executionID string) (*WorkflowExecution, error) {
|
|
sm.mu.RLock()
|
|
defer sm.mu.RUnlock()
|
|
|
|
execution, exists := sm.executions[executionID]
|
|
if !exists {
|
|
return nil, fmt.Errorf("execution not found: %s", executionID)
|
|
}
|
|
|
|
return execution, nil
|
|
}
|
|
|
|
// UpdateExecution updates an execution
|
|
func (sm *AdvancedWorkflowStateManager) UpdateExecution(ctx context.Context, execution *WorkflowExecution) error {
|
|
sm.mu.Lock()
|
|
defer sm.mu.Unlock()
|
|
|
|
sm.executions[execution.ID] = execution
|
|
return nil
|
|
}
|
|
|
|
// ListExecutions returns all executions
|
|
func (sm *AdvancedWorkflowStateManager) ListExecutions(ctx context.Context, filters map[string]interface{}) ([]*WorkflowExecution, error) {
|
|
sm.mu.RLock()
|
|
defer sm.mu.RUnlock()
|
|
|
|
executions := make([]*WorkflowExecution, 0)
|
|
for _, execution := range sm.executions {
|
|
// Apply filters if any
|
|
if workflowID, ok := filters["workflow_id"]; ok {
|
|
if execution.WorkflowID != workflowID {
|
|
continue
|
|
}
|
|
}
|
|
if status, ok := filters["status"]; ok {
|
|
if execution.Status != status {
|
|
continue
|
|
}
|
|
}
|
|
|
|
executions = append(executions, execution)
|
|
}
|
|
|
|
return executions, nil
|
|
}
|
|
|
|
type ScheduledTask struct {
|
|
ID string
|
|
WorkflowID string
|
|
Schedule string
|
|
Input map[string]interface{}
|
|
NextRun time.Time
|
|
LastRun *time.Time
|
|
Enabled bool
|
|
}
|
|
|
|
// Start starts the scheduler
|
|
func (s *WorkflowScheduler) Start(ctx context.Context) error {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
if s.running {
|
|
return fmt.Errorf("scheduler already running")
|
|
}
|
|
|
|
s.running = true
|
|
go s.run(ctx)
|
|
return nil
|
|
}
|
|
|
|
// Stop stops the scheduler
|
|
func (s *WorkflowScheduler) Stop(ctx context.Context) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
s.running = false
|
|
}
|
|
|
|
func (s *WorkflowScheduler) run(ctx context.Context) {
|
|
ticker := time.NewTicker(1 * time.Minute) // Check every minute
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-ticker.C:
|
|
s.checkScheduledTasks(ctx)
|
|
}
|
|
|
|
s.mu.RLock()
|
|
running := s.running
|
|
s.mu.RUnlock()
|
|
|
|
if !running {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *WorkflowScheduler) checkScheduledTasks(ctx context.Context) {
|
|
s.mu.RLock()
|
|
tasks := make([]*ScheduledTask, 0, len(s.scheduledTasks))
|
|
for _, task := range s.scheduledTasks {
|
|
if task.Enabled && time.Now().After(task.NextRun) {
|
|
tasks = append(tasks, task)
|
|
}
|
|
}
|
|
s.mu.RUnlock()
|
|
|
|
for _, task := range tasks {
|
|
go s.executeScheduledTask(ctx, task)
|
|
}
|
|
}
|
|
|
|
func (s *WorkflowScheduler) executeScheduledTask(ctx context.Context, task *ScheduledTask) {
|
|
// Execute the workflow
|
|
if s.executor != nil {
|
|
_, err := s.executor.ExecuteWorkflow(ctx, task.WorkflowID, task.Input)
|
|
if err != nil {
|
|
// Log error (in real implementation)
|
|
fmt.Printf("Failed to execute scheduled workflow %s: %v\n", task.WorkflowID, err)
|
|
}
|
|
}
|
|
|
|
// Update last run and calculate next run
|
|
now := time.Now()
|
|
task.LastRun = &now
|
|
|
|
// Simple scheduling - add 1 hour for demo (in real implementation, parse cron expression)
|
|
task.NextRun = now.Add(1 * time.Hour)
|
|
}
|
|
|
|
// WorkflowExecutor executes workflows using the processor factory
|
|
type WorkflowExecutor struct {
|
|
processorFactory *ProcessorFactory
|
|
stateManager *AdvancedWorkflowStateManager
|
|
config *WorkflowEngineConfig
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
// NewWorkflowExecutor creates a new executor
|
|
func NewWorkflowExecutor(factory *ProcessorFactory, stateManager *AdvancedWorkflowStateManager, config *WorkflowEngineConfig) *WorkflowExecutor {
|
|
return &WorkflowExecutor{
|
|
processorFactory: factory,
|
|
stateManager: stateManager,
|
|
config: config,
|
|
}
|
|
}
|
|
|
|
// Start starts the executor
|
|
func (e *WorkflowExecutor) Start(ctx context.Context) error {
|
|
return nil // No special startup needed
|
|
}
|
|
|
|
// Stop stops the executor
|
|
func (e *WorkflowExecutor) Stop(ctx context.Context) {
|
|
// Cleanup resources if needed
|
|
}
|
|
|
|
// ExecuteWorkflow executes a workflow
|
|
func (e *WorkflowExecutor) ExecuteWorkflow(ctx context.Context, workflowID string, input map[string]interface{}) (*WorkflowExecution, error) {
|
|
// Create execution
|
|
execution, err := e.stateManager.CreateExecution(ctx, workflowID, input)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create execution: %w", err)
|
|
}
|
|
|
|
// Start execution
|
|
execution.Status = ExecutionStatusRunning
|
|
e.stateManager.UpdateExecution(ctx, execution)
|
|
|
|
// Execute asynchronously
|
|
go e.executeWorkflowAsync(ctx, execution)
|
|
|
|
return execution, nil
|
|
}
|
|
|
|
func (e *WorkflowExecutor) executeWorkflowAsync(ctx context.Context, execution *WorkflowExecution) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
execution.Status = ExecutionStatusFailed
|
|
execution.Error = fmt.Errorf("execution panicked: %v", r)
|
|
endTime := time.Now()
|
|
execution.EndTime = &endTime
|
|
e.stateManager.UpdateExecution(ctx, execution)
|
|
}
|
|
}()
|
|
|
|
// For now, simulate workflow execution
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
execution.Status = ExecutionStatusCompleted
|
|
execution.Output = map[string]interface{}{
|
|
"result": "workflow completed successfully",
|
|
"input": execution.Input,
|
|
}
|
|
endTime := time.Now()
|
|
execution.EndTime = &endTime
|
|
|
|
e.stateManager.UpdateExecution(ctx, execution)
|
|
}
|
|
|
|
// WorkflowMiddleware handles middleware processing
|
|
type WorkflowMiddleware struct {
|
|
middlewares []WorkflowMiddlewareFunc
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
type WorkflowMiddlewareFunc func(ctx context.Context, execution *WorkflowExecution, next WorkflowNextFunc) error
|
|
type WorkflowNextFunc func(ctx context.Context, execution *WorkflowExecution) error
|
|
|
|
// NewWorkflowMiddleware creates new middleware manager
|
|
func NewWorkflowMiddleware() *WorkflowMiddleware {
|
|
return &WorkflowMiddleware{
|
|
middlewares: make([]WorkflowMiddlewareFunc, 0),
|
|
}
|
|
}
|
|
|
|
// Use adds middleware to the chain
|
|
func (m *WorkflowMiddleware) Use(middleware WorkflowMiddlewareFunc) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
m.middlewares = append(m.middlewares, middleware)
|
|
}
|
|
|
|
// Execute executes middleware chain
|
|
func (m *WorkflowMiddleware) Execute(ctx context.Context, execution *WorkflowExecution, handler WorkflowNextFunc) error {
|
|
m.mu.RLock()
|
|
middlewares := make([]WorkflowMiddlewareFunc, len(m.middlewares))
|
|
copy(middlewares, m.middlewares)
|
|
m.mu.RUnlock()
|
|
|
|
// Build middleware chain
|
|
chain := handler
|
|
for i := len(middlewares) - 1; i >= 0; i-- {
|
|
middleware := middlewares[i]
|
|
next := chain
|
|
chain = func(ctx context.Context, execution *WorkflowExecution) error {
|
|
return middleware(ctx, execution, next)
|
|
}
|
|
}
|
|
|
|
return chain(ctx, execution)
|
|
}
|
|
|
|
// WorkflowSecurity handles authentication and authorization
|
|
type WorkflowSecurity struct {
|
|
users map[string]*WorkflowUser
|
|
permissions map[string]*WorkflowPermission
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
type WorkflowUser struct {
|
|
ID string `json:"id"`
|
|
Username string `json:"username"`
|
|
Email string `json:"email"`
|
|
Role string `json:"role"`
|
|
Permissions []string `json:"permissions"`
|
|
}
|
|
|
|
type WorkflowPermission struct {
|
|
ID string `json:"id"`
|
|
Resource string `json:"resource"`
|
|
Action string `json:"action"`
|
|
Scope string `json:"scope"`
|
|
}
|
|
|
|
// NewWorkflowSecurity creates new security manager
|
|
func NewWorkflowSecurity() *WorkflowSecurity {
|
|
return &WorkflowSecurity{
|
|
users: make(map[string]*WorkflowUser),
|
|
permissions: make(map[string]*WorkflowPermission),
|
|
}
|
|
}
|
|
|
|
// Authenticate authenticates a user
|
|
func (s *WorkflowSecurity) Authenticate(ctx context.Context, token string) (*WorkflowUser, error) {
|
|
// Simplified authentication - in real implementation, validate JWT or similar
|
|
if token == "admin-token" {
|
|
return &WorkflowUser{
|
|
ID: "admin",
|
|
Username: "admin",
|
|
Role: "admin",
|
|
Permissions: []string{"workflow:read", "workflow:write", "workflow:execute", "workflow:delete"},
|
|
}, nil
|
|
}
|
|
|
|
return nil, fmt.Errorf("invalid token")
|
|
}
|
|
|
|
// Authorize checks if user has permission
|
|
func (s *WorkflowSecurity) Authorize(ctx context.Context, user *WorkflowUser, resource, action string) error {
|
|
requiredPermission := fmt.Sprintf("%s:%s", resource, action)
|
|
|
|
for _, permission := range user.Permissions {
|
|
if permission == requiredPermission || permission == "*" {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return fmt.Errorf("permission denied: %s", requiredPermission)
|
|
}
|
|
|
|
// NewWorkflowEngineManager creates a complete workflow engine manager
|
|
func NewWorkflowEngineManager(config *WorkflowEngineConfig) *WorkflowEngineManager {
|
|
if config == nil {
|
|
config = &WorkflowEngineConfig{
|
|
MaxConcurrentExecutions: 100,
|
|
DefaultTimeout: 30 * time.Minute,
|
|
EnablePersistence: true,
|
|
EnableSecurity: false,
|
|
EnableMiddleware: false,
|
|
EnableScheduling: false,
|
|
}
|
|
}
|
|
|
|
registry := NewWorkflowRegistry()
|
|
stateManager := NewAdvancedWorkflowStateManager()
|
|
processorFactory := NewProcessorFactory()
|
|
|
|
executor := NewWorkflowExecutor(processorFactory, stateManager, config)
|
|
scheduler := NewWorkflowScheduler(stateManager, executor)
|
|
middleware := NewWorkflowMiddleware()
|
|
security := NewWorkflowSecurity()
|
|
|
|
return &WorkflowEngineManager{
|
|
registry: registry,
|
|
stateManager: stateManager,
|
|
processorFactory: processorFactory,
|
|
scheduler: scheduler,
|
|
executor: executor,
|
|
middleware: middleware,
|
|
security: security,
|
|
config: config,
|
|
}
|
|
}
|
|
|
|
// Start starts the workflow engine
|
|
func (m *WorkflowEngineManager) Start(ctx context.Context) error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
if m.running {
|
|
return fmt.Errorf("workflow engine already running")
|
|
}
|
|
|
|
// Start components
|
|
if err := m.executor.Start(ctx); err != nil {
|
|
return fmt.Errorf("failed to start executor: %w", err)
|
|
}
|
|
|
|
if m.config.EnableScheduling {
|
|
if err := m.scheduler.Start(ctx); err != nil {
|
|
return fmt.Errorf("failed to start scheduler: %w", err)
|
|
}
|
|
}
|
|
|
|
m.running = true
|
|
return nil
|
|
}
|
|
|
|
// Stop stops the workflow engine
|
|
func (m *WorkflowEngineManager) Stop(ctx context.Context) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
if !m.running {
|
|
return
|
|
}
|
|
|
|
m.executor.Stop(ctx)
|
|
if m.config.EnableScheduling {
|
|
m.scheduler.Stop(ctx)
|
|
}
|
|
|
|
m.running = false
|
|
}
|
|
|
|
// RegisterWorkflow registers a workflow definition
|
|
func (m *WorkflowEngineManager) RegisterWorkflow(ctx context.Context, definition *WorkflowDefinition) error {
|
|
return m.registry.Store(ctx, definition)
|
|
}
|
|
|
|
// ExecuteWorkflow executes a workflow
|
|
func (m *WorkflowEngineManager) ExecuteWorkflow(ctx context.Context, workflowID string, input map[string]interface{}) (*ExecutionResult, error) {
|
|
execution, err := m.executor.ExecuteWorkflow(ctx, workflowID, input)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &ExecutionResult{
|
|
ID: execution.ID,
|
|
WorkflowID: execution.WorkflowID,
|
|
Status: execution.Status,
|
|
StartTime: execution.StartTime,
|
|
EndTime: execution.EndTime,
|
|
Input: execution.Input,
|
|
Output: execution.Output,
|
|
Error: "",
|
|
}, nil
|
|
}
|
|
|
|
// GetExecution retrieves an execution
|
|
func (m *WorkflowEngineManager) GetExecution(ctx context.Context, executionID string) (*ExecutionResult, error) {
|
|
execution, err := m.stateManager.GetExecution(ctx, executionID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
errorMsg := ""
|
|
if execution.Error != nil {
|
|
errorMsg = execution.Error.Error()
|
|
}
|
|
|
|
return &ExecutionResult{
|
|
ID: execution.ID,
|
|
WorkflowID: execution.WorkflowID,
|
|
Status: execution.Status,
|
|
StartTime: execution.StartTime,
|
|
EndTime: execution.EndTime,
|
|
Input: execution.Input,
|
|
Output: execution.Output,
|
|
Error: errorMsg,
|
|
}, nil
|
|
}
|
|
|
|
// GetRegistry returns the workflow registry
|
|
func (m *WorkflowEngineManager) GetRegistry() *WorkflowRegistry {
|
|
return m.registry
|
|
}
|
|
|
|
// GetStateManager returns the state manager
|
|
func (m *WorkflowEngineManager) GetStateManager() *AdvancedWorkflowStateManager {
|
|
return m.stateManager
|
|
}
|
|
|
|
// GetProcessorFactory returns the processor factory
|
|
func (m *WorkflowEngineManager) GetProcessorFactory() *ProcessorFactory {
|
|
return m.processorFactory
|
|
}
|