wulf-pulse/migrations/036_create_ticket_workflow_tables.sql

205 lines
9.9 KiB
MySQL
Raw Normal View History

-- Ticket Workflow Engine Tables
-- Refactors the monolithic workflow engine into a flexible, pipeline-like system
-- with per-workflow and per-step on/off switches, visual step editing, and
-- extensible step executors following the webhook pipeline engine pattern.
-- ============================================================================
-- Ticket Workflows — workflow definitions (analogous to webhook_pipelines)
-- ============================================================================
CREATE TABLE IF NOT EXISTS ticket_workflows (
id SERIAL PRIMARY KEY,
name VARCHAR(200) NOT NULL,
description TEXT,
is_active BOOLEAN DEFAULT true,
trigger_event VARCHAR(50) NOT NULL, -- 'ticket.created', 'ticket.updated'
trigger_conditions JSONB NOT NULL DEFAULT '[]', -- array of {field, operator, value}
sort_order INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- ============================================================================
-- Ticket Workflow Steps — steps within a workflow (analogous to pipeline_steps)
-- ============================================================================
CREATE TABLE IF NOT EXISTS ticket_workflow_steps (
id SERIAL PRIMARY KEY,
workflow_id INTEGER NOT NULL REFERENCES ticket_workflows(id) ON DELETE CASCADE,
step_order INTEGER NOT NULL,
step_type VARCHAR(50) NOT NULL, -- 'classify','validate','ai_classify','ai_title','ai_troubleshooting','delay','update_ticket','filter'
name VARCHAR(200) NOT NULL,
config JSONB NOT NULL DEFAULT '{}', -- step-specific configuration
on_failure VARCHAR(20) DEFAULT 'continue', -- 'continue', 'stop', 'skip_to'
skip_to_step INTEGER,
is_active BOOLEAN DEFAULT true,
condition JSONB, -- optional condition to execute this step: {field, operator, value}
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- ============================================================================
-- Ticket Workflow Executions — execution log (replaces workflow_executions)
-- ============================================================================
CREATE TABLE IF NOT EXISTS ticket_workflow_executions (
id SERIAL PRIMARY KEY,
workflow_id INTEGER NOT NULL REFERENCES ticket_workflows(id) ON DELETE CASCADE,
ticket_id BIGINT NOT NULL,
ticket_number VARCHAR(50),
status VARCHAR(20) NOT NULL DEFAULT 'pending', -- 'pending','running','completed','failed','skipped'
classification_method VARCHAR(20), -- 'robotic', 'ai', 'hybrid'
branch VARCHAR(20), -- 'service_desk', 'noc', 'soc'
context JSONB NOT NULL DEFAULT '{}', -- accumulated data from steps
field_changes JSONB, -- final changes applied to ticket
started_at TIMESTAMP DEFAULT NOW(),
completed_at TIMESTAMP,
duration_ms INTEGER,
error_message TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
-- ============================================================================
-- Ticket Workflow Execution Steps — per-step audit trail (replaces workflow_execution_steps)
-- ============================================================================
CREATE TABLE IF NOT EXISTS ticket_workflow_execution_steps (
id SERIAL PRIMARY KEY,
execution_id INTEGER NOT NULL REFERENCES ticket_workflow_executions(id) ON DELETE CASCADE,
step_order INTEGER NOT NULL,
step_type VARCHAR(50) NOT NULL,
step_name VARCHAR(200),
status VARCHAR(20) NOT NULL DEFAULT 'pending', -- 'pending','running','completed','failed','skipped'
input_data JSONB,
output_data JSONB,
started_at TIMESTAMP,
completed_at TIMESTAMP,
duration_ms INTEGER,
error_message TEXT
);
-- ============================================================================
-- Indexes
-- ============================================================================
CREATE INDEX IF NOT EXISTS idx_ticket_workflows_event ON ticket_workflows(trigger_event, is_active, sort_order);
CREATE INDEX IF NOT EXISTS idx_ticket_workflow_steps_workflow ON ticket_workflow_steps(workflow_id, step_order);
CREATE INDEX IF NOT EXISTS idx_ticket_workflow_executions_workflow ON ticket_workflow_executions(workflow_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_ticket_workflow_executions_ticket ON ticket_workflow_executions(ticket_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_ticket_workflow_executions_status ON ticket_workflow_executions(status, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_ticket_workflow_execution_steps_exec ON ticket_workflow_execution_steps(execution_id, step_order);
-- ============================================================================
-- SEED: "Ticket Triage" Workflow (ports current hardcoded workflow-engine.ts logic)
-- ============================================================================
INSERT INTO ticket_workflows (name, description, is_active, trigger_event, trigger_conditions, sort_order) VALUES
('Ticket Triage',
'Automatically classifies new tickets using robotic keyword matching, validates the classification, enhances with AI when needed, and writes back to Autotask.',
true,
'ticket.created',
'[
{
"field": "ticket_category",
"operator": "in",
"value": [2, 3, 159, 161]
},
{
"field": "creator_resource_id",
"operator": "not_in",
"value": [30861471]
},
{
"field": "person_id",
"operator": "not_in",
"value": [30861575]
},
{
"field": "company_id",
"operator": "not_in",
"value": [29861409, 29783545, 29861361, 29702433]
}
]',
10);
-- Step 1: Classify - Branch Routing
INSERT INTO ticket_workflow_steps (workflow_id, step_order, step_type, name, config, is_active) VALUES
((SELECT id FROM ticket_workflows WHERE name = 'Ticket Triage'), 1, 'classify', 'Branch Routing', '{
"rule_type": "branch_routing",
"result_field": "branch",
"default_value": "service_desk"
}', true);
-- Step 2: Classify - Ticket Type
INSERT INTO ticket_workflow_steps (workflow_id, step_order, step_type, name, config, is_active) VALUES
((SELECT id FROM ticket_workflows WHERE name = 'Ticket Triage'), 2, 'classify', 'Ticket Type', '{
"rule_type": "ticket_type",
"result_field": "ticket_type"
}', true);
-- Step 3: Classify - Issue Classification
INSERT INTO ticket_workflow_steps (workflow_id, step_order, step_type, name, config, is_active) VALUES
((SELECT id FROM ticket_workflows WHERE name = 'Ticket Triage'), 3, 'classify', 'Issue Classification', '{
"rule_type": "issue_classification",
"result_field": "issue_type",
"result_field_2": "sub_issue_type"
}', true);
-- Step 4: Classify - Priority
INSERT INTO ticket_workflow_steps (workflow_id, step_order, step_type, name, config, is_active) VALUES
((SELECT id FROM ticket_workflows WHERE name = 'Ticket Triage'), 4, 'classify', 'Priority', '{
"rule_type": "priority",
"result_field": "priority"
}', true);
-- Step 5: Classify - Queue Routing
INSERT INTO ticket_workflow_steps (workflow_id, step_order, step_type, name, config, is_active) VALUES
((SELECT id FROM ticket_workflows WHERE name = 'Ticket Triage'), 5, 'classify', 'Queue Routing', '{
"rule_type": "queue_routing",
"result_field": "queue_id"
}', true);
-- Step 6: Validate Classification
INSERT INTO ticket_workflow_steps (workflow_id, step_order, step_type, name, config, is_active, on_failure) VALUES
((SELECT id FROM ticket_workflows WHERE name = 'Ticket Triage'), 6, 'validate', 'Validate Classification', '{
"required_fields": []
}', true, 'continue');
-- Step 7: AI Classify (conditional - only if validation failed)
INSERT INTO ticket_workflow_steps (workflow_id, step_order, step_type, name, config, is_active, condition) VALUES
((SELECT id FROM ticket_workflows WHERE name = 'Ticket Triage'), 7, 'ai_classify', 'AI Classification', '{
"template_purpose": "ambiguous_classification",
"skip_if_valid": true
}', true, '{
"field": "context.validation.is_valid",
"operator": "equals",
"value": false
}');
-- Step 8: AI Title Cleanup (conditional - only if title needs cleanup)
INSERT INTO ticket_workflow_steps (workflow_id, step_order, step_type, name, config, is_active, condition) VALUES
((SELECT id FROM ticket_workflows WHERE name = 'Ticket Triage'), 8, 'ai_title', 'AI Title Cleanup', '{
"template_purpose": "title_cleanup"
}', true, '{
"field": "context.classification.ai_reasons",
"operator": "contains",
"value": "Title"
}');
-- Step 9: Delay before Autotask update
INSERT INTO ticket_workflow_steps (workflow_id, step_order, step_type, name, config, is_active) VALUES
((SELECT id FROM ticket_workflows WHERE name = 'Ticket Triage'), 9, 'delay', 'Delay Before Update', '{
"duration_ms": "{{settings.autotask_update_delay_ms}}"
}', true);
-- Step 10: Update Ticket in Autotask
INSERT INTO ticket_workflow_steps (workflow_id, step_order, step_type, name, config, is_active, on_failure) VALUES
((SELECT id FROM ticket_workflows WHERE name = 'Ticket Triage'), 10, 'update_ticket', 'Update Autotask Ticket', '{
"use_field_changes": true
}', true, 'stop');
-- Step 11: AI Troubleshooting (conditional - only for incidents)
INSERT INTO ticket_workflow_steps (workflow_id, step_order, step_type, name, config, is_active, condition) VALUES
((SELECT id FROM ticket_workflows WHERE name = 'Ticket Triage'), 11, 'ai_troubleshooting', 'Generate Troubleshooting Steps', '{
"template_purpose": "troubleshooting_steps",
"create_note": true
}', true, '{
"field": "context.field_changes.ticket_type.after",
"operator": "equals",
"value": 2
}');