feat: 新增工单反馈系统
This commit is contained in:
@@ -255,6 +255,63 @@ function migrateSchema() {
|
||||
try { db.exec("ALTER TABLE admin_links ADD COLUMN trusted INTEGER DEFAULT 1"); } catch {}
|
||||
try { db.exec("ALTER TABLE admin_links ADD COLUMN slug TEXT DEFAULT ''"); } catch {}
|
||||
} },
|
||||
// v16: 站内工单——用户反馈、公开回复、内部备注及业务事件
|
||||
{ version: 16, up: () => {
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS tickets (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
ticket_no TEXT NOT NULL UNIQUE,
|
||||
requester_id INTEGER,
|
||||
subject TEXT NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
category TEXT NOT NULL DEFAULT 'other',
|
||||
priority TEXT NOT NULL DEFAULT 'normal',
|
||||
status TEXT NOT NULL DEFAULT 'open',
|
||||
source TEXT NOT NULL DEFAULT 'site',
|
||||
source_url TEXT DEFAULT '',
|
||||
source_type TEXT DEFAULT '',
|
||||
source_id INTEGER DEFAULT 0,
|
||||
browser_info TEXT DEFAULT '',
|
||||
assignee_id INTEGER,
|
||||
created_at DATETIME DEFAULT (datetime('now')),
|
||||
updated_at DATETIME DEFAULT (datetime('now')),
|
||||
last_reply_at DATETIME DEFAULT (datetime('now')),
|
||||
first_response_at DATETIME,
|
||||
resolved_at DATETIME,
|
||||
closed_at DATETIME,
|
||||
FOREIGN KEY (requester_id) REFERENCES users(id) ON DELETE SET NULL,
|
||||
FOREIGN KEY (assignee_id) REFERENCES users(id) ON DELETE SET NULL
|
||||
)`);
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS ticket_messages (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
ticket_id INTEGER NOT NULL,
|
||||
author_id INTEGER,
|
||||
content TEXT NOT NULL,
|
||||
is_internal INTEGER NOT NULL DEFAULT 0,
|
||||
created_at DATETIME DEFAULT (datetime('now')),
|
||||
updated_at DATETIME DEFAULT (datetime('now')),
|
||||
FOREIGN KEY (ticket_id) REFERENCES tickets(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE SET NULL
|
||||
)`);
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS ticket_events (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
ticket_id INTEGER NOT NULL,
|
||||
actor_id INTEGER,
|
||||
event_type TEXT NOT NULL,
|
||||
field_name TEXT DEFAULT '',
|
||||
old_value TEXT DEFAULT '',
|
||||
new_value TEXT DEFAULT '',
|
||||
detail TEXT DEFAULT '',
|
||||
created_at DATETIME DEFAULT (datetime('now')),
|
||||
FOREIGN KEY (ticket_id) REFERENCES tickets(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (actor_id) REFERENCES users(id) ON DELETE SET NULL
|
||||
)`);
|
||||
db.exec('CREATE INDEX IF NOT EXISTS idx_tickets_requester ON tickets(requester_id, updated_at DESC)');
|
||||
db.exec('CREATE INDEX IF NOT EXISTS idx_tickets_status_updated ON tickets(status, updated_at DESC)');
|
||||
db.exec('CREATE INDEX IF NOT EXISTS idx_tickets_assignee ON tickets(assignee_id, status, updated_at DESC)');
|
||||
db.exec('CREATE INDEX IF NOT EXISTS idx_tickets_category ON tickets(category, updated_at DESC)');
|
||||
db.exec('CREATE INDEX IF NOT EXISTS idx_ticket_messages_ticket ON ticket_messages(ticket_id, created_at ASC)');
|
||||
db.exec('CREATE INDEX IF NOT EXISTS idx_ticket_events_ticket ON ticket_events(ticket_id, created_at ASC)');
|
||||
} },
|
||||
];
|
||||
for (const m of migrations) {
|
||||
if (current < m.version) { m.up(); db.exec('PRAGMA user_version = ' + m.version); }
|
||||
|
||||
Reference in New Issue
Block a user