彻底重构-1

This commit is contained in:
yml
2026-04-09 09:06:08 +08:00
parent b509394b09
commit df2eb34478
92 changed files with 2226 additions and 1261 deletions
+28 -23
View File
@@ -1,43 +1,48 @@
import fs from 'node:fs'
import path from 'node:path'
import { DatabaseSync } from 'node:sqlite'
import { Pool } from 'pg'
import { runtimeConfig } from '../config/runtime.js'
let databaseInstance = null
let poolInstance = null
export function getDb() {
if (!databaseInstance) {
const filePath = resolveDatabasePath()
fs.mkdirSync(path.dirname(filePath), { recursive: true })
databaseInstance = new DatabaseSync(filePath)
databaseInstance.exec('PRAGMA foreign_keys = ON;')
databaseInstance.exec('PRAGMA journal_mode = WAL;')
if (!poolInstance) {
poolInstance = new Pool({
connectionString: String(runtimeConfig.database?.url || '').trim(),
ssl: runtimeConfig.database?.ssl ? { rejectUnauthorized: false } : false,
max: Number(runtimeConfig.database?.maxConnections || 10),
})
}
return databaseInstance
return poolInstance
}
export function runInTransaction(fn) {
const db = getDb()
db.exec('BEGIN')
export async function query(text, params = []) {
const pool = getDb()
return pool.query(text, params)
}
export async function withTransaction(fn) {
const client = await getDb().connect()
try {
const result = fn(db)
db.exec('COMMIT')
await client.query('BEGIN')
const result = await fn(client)
await client.query('COMMIT')
return result
} catch (error) {
db.exec('ROLLBACK')
await client.query('ROLLBACK')
throw error
} finally {
client.release()
}
}
function resolveDatabasePath() {
const configured = String(runtimeConfig.database.filePath || '').trim()
if (!configured) {
throw new Error('缺少数据库文件路径配置')
export async function closeDb() {
if (!poolInstance) {
return
}
return path.resolve(configured)
const pool = poolInstance
poolInstance = null
await pool.end()
}
+24 -16
View File
@@ -1,33 +1,28 @@
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
import { getDb } from './client.js'
import { closeDb, query, withTransaction } from './client.js'
const CURRENT_DIR = path.dirname(fileURLToPath(import.meta.url))
const MIGRATIONS_DIR = path.join(CURRENT_DIR, 'migrations')
export function runDatabaseMigrations() {
const db = getDb()
export async function runDatabaseMigrations() {
const files = fs.readdirSync(MIGRATIONS_DIR)
.filter((name) => name.endsWith('.sql'))
.sort()
db.exec(`
await query(`
CREATE TABLE IF NOT EXISTS schema_migrations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
id BIGSERIAL PRIMARY KEY,
filename TEXT NOT NULL UNIQUE,
applied_at TEXT NOT NULL
);
applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
`)
const appliedRows = db.prepare('SELECT filename FROM schema_migrations').all()
const applied = new Set(appliedRows.map((row) => row.filename))
const insertApplied = db.prepare(`
INSERT INTO schema_migrations (filename, applied_at)
VALUES (?, ?)
`)
const appliedRows = await query('SELECT filename FROM schema_migrations')
const applied = new Set(appliedRows.rows.map((row) => String(row.filename)))
for (const filename of files) {
if (applied.has(filename)) {
@@ -35,8 +30,13 @@ export function runDatabaseMigrations() {
}
const sql = fs.readFileSync(path.join(MIGRATIONS_DIR, filename), 'utf8')
db.exec(sql)
insertApplied.run(filename, new Date().toISOString())
await withTransaction(async (client) => {
await client.query(sql)
await client.query(
'INSERT INTO schema_migrations (filename, applied_at) VALUES ($1, NOW())',
[filename],
)
})
}
}
@@ -44,4 +44,12 @@ const currentFilePath = fileURLToPath(import.meta.url)
if (process.argv[1] && path.resolve(process.argv[1]) === currentFilePath) {
runDatabaseMigrations()
.then(async () => {
await closeDb()
})
.catch(async (error) => {
console.error(error)
await closeDb()
process.exit(1)
})
}
@@ -0,0 +1,272 @@
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE TABLE IF NOT EXISTS admin_users (
id BIGSERIAL PRIMARY KEY,
username TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
role TEXT NOT NULL,
status TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL
);
CREATE TABLE IF NOT EXISTS admin_audit_logs (
id BIGSERIAL PRIMARY KEY,
actor_user_id BIGINT,
actor_username TEXT NOT NULL DEFAULT '',
actor_role TEXT NOT NULL DEFAULT '',
action TEXT NOT NULL,
target_type TEXT NOT NULL,
target_id TEXT NOT NULL,
payload_json JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_admin_audit_logs_created_at ON admin_audit_logs(created_at DESC);
CREATE INDEX IF NOT EXISTS idx_admin_audit_logs_target ON admin_audit_logs(target_type, target_id);
CREATE TABLE IF NOT EXISTS fulfillment_profiles (
id BIGSERIAL PRIMARY KEY,
profile_key TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
executor_key TEXT NOT NULL,
requires_claim BOOLEAN NOT NULL DEFAULT FALSE,
auto_dispatch BOOLEAN NOT NULL DEFAULT FALSE,
inventory_strategy TEXT NOT NULL DEFAULT 'static_pool',
config_json JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL
);
CREATE TABLE IF NOT EXISTS fulfillment_profile_requirements (
id BIGSERIAL PRIMARY KEY,
profile_id BIGINT NOT NULL REFERENCES fulfillment_profiles(id) ON DELETE CASCADE,
role_key TEXT NOT NULL,
credential_type TEXT NOT NULL,
quantity_per_unit INTEGER NOT NULL DEFAULT 1,
is_required BOOLEAN NOT NULL DEFAULT TRUE,
config_json JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL,
UNIQUE(profile_id, role_key)
);
CREATE TABLE IF NOT EXISTS sku_fulfillment_bindings (
id BIGSERIAL PRIMARY KEY,
sku_code TEXT NOT NULL,
provider TEXT NOT NULL DEFAULT '',
platform TEXT NOT NULL DEFAULT '',
shop_id TEXT NOT NULL DEFAULT '',
profile_id BIGINT NOT NULL REFERENCES fulfillment_profiles(id) ON DELETE CASCADE,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
priority INTEGER NOT NULL DEFAULT 100,
config_json JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_sku_fulfillment_bindings_lookup
ON sku_fulfillment_bindings(sku_code, provider, platform, shop_id, enabled, priority);
CREATE TABLE IF NOT EXISTS orders (
id BIGSERIAL PRIMARY KEY,
provider TEXT NOT NULL,
platform TEXT NOT NULL,
shop_id TEXT NOT NULL DEFAULT '',
shop_name TEXT NOT NULL DEFAULT '',
platform_order_id TEXT NOT NULL,
order_status TEXT NOT NULL DEFAULT 'created',
pay_status TEXT NOT NULL DEFAULT 'unpaid',
buyer_id TEXT NOT NULL DEFAULT '',
buyer_name TEXT NOT NULL DEFAULT '',
receiver_contact TEXT NOT NULL DEFAULT '',
total_amount INTEGER NOT NULL DEFAULT 0,
currency TEXT NOT NULL DEFAULT 'CNY',
raw_payload_json JSONB NOT NULL DEFAULT '{}'::jsonb,
paid_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL,
UNIQUE(provider, platform, shop_id, platform_order_id)
);
CREATE TABLE IF NOT EXISTS order_items (
id BIGSERIAL PRIMARY KEY,
order_id BIGINT NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
sku_code TEXT NOT NULL,
sku_name TEXT NOT NULL DEFAULT '',
quantity INTEGER NOT NULL DEFAULT 1,
spec_json JSONB NOT NULL DEFAULT '{}'::jsonb,
item_snapshot_json JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_order_items_order_id ON order_items(order_id);
CREATE INDEX IF NOT EXISTS idx_order_items_sku_code ON order_items(sku_code);
CREATE TABLE IF NOT EXISTS inventory_items (
id BIGSERIAL PRIMARY KEY,
batch_no TEXT NOT NULL DEFAULT '',
sku_code TEXT NOT NULL,
credential_type TEXT NOT NULL,
display_value TEXT NOT NULL DEFAULT '',
payload_json JSONB NOT NULL DEFAULT '{}'::jsonb,
source_type TEXT NOT NULL DEFAULT 'static_import',
status TEXT NOT NULL DEFAULT 'available',
invalid_reason TEXT NOT NULL DEFAULT '',
metadata_json JSONB NOT NULL DEFAULT '{}'::jsonb,
consumed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_inventory_items_unique_payload
ON inventory_items(sku_code, credential_type, md5(payload_json::text));
CREATE INDEX IF NOT EXISTS idx_inventory_items_pool
ON inventory_items(sku_code, credential_type, status, id);
CREATE TABLE IF NOT EXISTS fulfillment_tasks (
id BIGSERIAL PRIMARY KEY,
order_id BIGINT NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
order_item_id BIGINT NOT NULL REFERENCES order_items(id) ON DELETE CASCADE,
unit_index INTEGER NOT NULL DEFAULT 1,
task_no TEXT NOT NULL UNIQUE,
provider TEXT NOT NULL,
platform TEXT NOT NULL,
shop_id TEXT NOT NULL DEFAULT '',
shop_name TEXT NOT NULL DEFAULT '',
platform_order_id TEXT NOT NULL,
profile_id BIGINT NOT NULL REFERENCES fulfillment_profiles(id) ON DELETE RESTRICT,
executor_key TEXT NOT NULL,
task_status TEXT NOT NULL DEFAULT 'pending_payment',
inventory_status TEXT NOT NULL DEFAULT 'pending',
delivery_status TEXT NOT NULL DEFAULT 'pending',
result_code TEXT NOT NULL DEFAULT '',
result_message TEXT NOT NULL DEFAULT '',
claim_token TEXT NOT NULL DEFAULT '',
claim_expires_at TIMESTAMPTZ,
automation_mode TEXT NOT NULL DEFAULT 'manual',
requires_claim BOOLEAN NOT NULL DEFAULT FALSE,
user_action_status TEXT NOT NULL DEFAULT 'not_required',
attempt_count INTEGER NOT NULL DEFAULT 0,
last_error TEXT NOT NULL DEFAULT '',
context_json JSONB NOT NULL DEFAULT '{}'::jsonb,
claimed_at TIMESTAMPTZ,
role_confirmed_at TIMESTAMPTZ,
redeemed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL,
UNIQUE(order_item_id, unit_index)
);
CREATE INDEX IF NOT EXISTS idx_fulfillment_tasks_status ON fulfillment_tasks(task_status, delivery_status);
CREATE INDEX IF NOT EXISTS idx_fulfillment_tasks_order ON fulfillment_tasks(order_id, order_item_id);
CREATE INDEX IF NOT EXISTS idx_fulfillment_tasks_claim_token ON fulfillment_tasks(claim_token);
CREATE TABLE IF NOT EXISTS claim_tokens (
id BIGSERIAL PRIMARY KEY,
task_id BIGINT NOT NULL UNIQUE REFERENCES fulfillment_tasks(id) ON DELETE CASCADE,
token TEXT NOT NULL UNIQUE,
status TEXT NOT NULL DEFAULT 'active',
expired_at TIMESTAMPTZ NOT NULL,
used_at TIMESTAMPTZ,
max_use_count INTEGER NOT NULL DEFAULT 1,
used_count INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_claim_tokens_status ON claim_tokens(status, expired_at);
CREATE TABLE IF NOT EXISTS task_inventory_bindings (
id BIGSERIAL PRIMARY KEY,
task_id BIGINT NOT NULL REFERENCES fulfillment_tasks(id) ON DELETE CASCADE,
inventory_item_id BIGINT NOT NULL REFERENCES inventory_items(id) ON DELETE RESTRICT,
role_key TEXT NOT NULL,
quantity INTEGER NOT NULL DEFAULT 1,
binding_status TEXT NOT NULL DEFAULT 'reserved',
consumed_at TIMESTAMPTZ,
released_at TIMESTAMPTZ,
metadata_json JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL,
UNIQUE(task_id, role_key, inventory_item_id)
);
CREATE INDEX IF NOT EXISTS idx_task_inventory_bindings_task ON task_inventory_bindings(task_id);
CREATE INDEX IF NOT EXISTS idx_task_inventory_bindings_inventory ON task_inventory_bindings(inventory_item_id);
CREATE TABLE IF NOT EXISTS task_events (
id BIGSERIAL PRIMARY KEY,
task_id BIGINT NOT NULL REFERENCES fulfillment_tasks(id) ON DELETE CASCADE,
event_type TEXT NOT NULL,
payload_json JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_task_events_task_created_at ON task_events(task_id, created_at DESC);
CREATE TABLE IF NOT EXISTS tencent_browser_contexts (
id BIGSERIAL PRIMARY KEY,
task_id BIGINT NOT NULL UNIQUE REFERENCES fulfillment_tasks(id) ON DELETE CASCADE,
browser_session_id TEXT NOT NULL DEFAULT '',
login_type TEXT NOT NULL DEFAULT '',
nickname TEXT NOT NULL DEFAULT '',
role_id TEXT NOT NULL DEFAULT '',
role_name TEXT NOT NULL DEFAULT '',
area TEXT NOT NULL DEFAULT '',
partition_name TEXT NOT NULL DEFAULT '',
screenshot_path TEXT NOT NULL DEFAULT '',
artifacts_json JSONB NOT NULL DEFAULT '{}'::jsonb,
state_json JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL
);
CREATE TABLE IF NOT EXISTS message_deliveries (
id BIGSERIAL PRIMARY KEY,
provider TEXT NOT NULL DEFAULT '',
platform TEXT NOT NULL DEFAULT '',
shop_id TEXT NOT NULL DEFAULT '',
shop_name TEXT NOT NULL DEFAULT '',
channel TEXT NOT NULL,
order_id BIGINT REFERENCES orders(id) ON DELETE SET NULL,
task_id BIGINT REFERENCES fulfillment_tasks(id) ON DELETE SET NULL,
platform_order_id TEXT NOT NULL DEFAULT '',
recipient_key TEXT NOT NULL DEFAULT '',
message_content TEXT NOT NULL DEFAULT '',
claim_url TEXT NOT NULL DEFAULT '',
status TEXT NOT NULL DEFAULT 'pending',
request_url TEXT NOT NULL DEFAULT '',
request_headers_json JSONB NOT NULL DEFAULT '{}'::jsonb,
request_body_json JSONB NOT NULL DEFAULT '{}'::jsonb,
response_status INTEGER NOT NULL DEFAULT 0,
response_json JSONB NOT NULL DEFAULT '{}'::jsonb,
error_message TEXT NOT NULL DEFAULT '',
sent_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_message_deliveries_task_channel ON message_deliveries(task_id, channel, created_at DESC);
CREATE TABLE IF NOT EXISTS webhook_events (
id BIGSERIAL PRIMARY KEY,
provider TEXT NOT NULL DEFAULT '',
platform TEXT NOT NULL DEFAULT '',
shop_id TEXT NOT NULL DEFAULT '',
shop_name TEXT NOT NULL DEFAULT '',
event_type TEXT NOT NULL,
event_key TEXT NOT NULL,
signature_valid BOOLEAN NOT NULL DEFAULT FALSE,
headers_json JSONB NOT NULL DEFAULT '{}'::jsonb,
query_json JSONB NOT NULL DEFAULT '{}'::jsonb,
body_json JSONB NOT NULL DEFAULT '{}'::jsonb,
processed BOOLEAN NOT NULL DEFAULT FALSE,
process_error TEXT NOT NULL DEFAULT '',
related_order_id BIGINT REFERENCES orders(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_webhook_events_lookup ON webhook_events(provider, platform, shop_id, event_key);
CREATE INDEX IF NOT EXISTS idx_webhook_events_processed ON webhook_events(processed, created_at DESC);
-129
View File
@@ -1,129 +0,0 @@
PRAGMA foreign_keys = ON;
CREATE TABLE IF NOT EXISTS orders (
id INTEGER PRIMARY KEY AUTOINCREMENT,
platform TEXT NOT NULL,
platform_order_id TEXT NOT NULL,
order_status TEXT NOT NULL DEFAULT 'created',
pay_status TEXT NOT NULL DEFAULT 'unpaid',
buyer_id TEXT NOT NULL DEFAULT '',
buyer_name TEXT NOT NULL DEFAULT '',
receiver_contact TEXT NOT NULL DEFAULT '',
total_amount INTEGER NOT NULL DEFAULT 0,
currency TEXT NOT NULL DEFAULT 'CNY',
raw_payload_json TEXT NOT NULL DEFAULT '{}',
paid_at TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
UNIQUE(platform, platform_order_id)
);
CREATE TABLE IF NOT EXISTS order_items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
order_id INTEGER NOT NULL,
sku_code TEXT NOT NULL,
sku_name TEXT NOT NULL DEFAULT '',
quantity INTEGER NOT NULL DEFAULT 1,
spec_json TEXT NOT NULL DEFAULT '{}',
delivery_mode TEXT NOT NULL DEFAULT 'claim_link',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
FOREIGN KEY(order_id) REFERENCES orders(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_order_items_order_id ON order_items(order_id);
CREATE INDEX IF NOT EXISTS idx_order_items_sku_code ON order_items(sku_code);
CREATE TABLE IF NOT EXISTS cdk_inventory (
id INTEGER PRIMARY KEY AUTOINCREMENT,
batch_no TEXT NOT NULL DEFAULT '',
sku_code TEXT NOT NULL,
cdk_code TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'available',
reserved_by_task_id INTEGER,
invalid_reason TEXT NOT NULL DEFAULT '',
delivered_at TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
UNIQUE(sku_code, cdk_code)
);
CREATE INDEX IF NOT EXISTS idx_cdk_inventory_sku_code ON cdk_inventory(sku_code);
CREATE INDEX IF NOT EXISTS idx_cdk_inventory_status ON cdk_inventory(status);
CREATE INDEX IF NOT EXISTS idx_cdk_inventory_reserved_task ON cdk_inventory(reserved_by_task_id);
CREATE TABLE IF NOT EXISTS delivery_tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
order_id INTEGER NOT NULL,
order_item_id INTEGER NOT NULL,
platform_order_id TEXT NOT NULL,
task_no TEXT NOT NULL,
task_status TEXT NOT NULL DEFAULT 'pending_payment',
login_type TEXT NOT NULL DEFAULT '',
claim_token_id INTEGER,
reserved_cdk_id INTEGER,
browser_session_id TEXT NOT NULL DEFAULT '',
nickname TEXT NOT NULL DEFAULT '',
role_id TEXT NOT NULL DEFAULT '',
role_name TEXT NOT NULL DEFAULT '',
area TEXT NOT NULL DEFAULT '',
partition_name TEXT NOT NULL DEFAULT '',
result_code TEXT NOT NULL DEFAULT '',
result_message TEXT NOT NULL DEFAULT '',
screenshot_path TEXT NOT NULL DEFAULT '',
artifacts_json TEXT NOT NULL DEFAULT '{}',
last_error TEXT NOT NULL DEFAULT '',
retry_count INTEGER NOT NULL DEFAULT 0,
expires_at TEXT,
claimed_at TEXT,
role_confirmed_at TEXT,
redeemed_at TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
FOREIGN KEY(order_id) REFERENCES orders(id) ON DELETE CASCADE,
FOREIGN KEY(order_item_id) REFERENCES order_items(id) ON DELETE CASCADE,
FOREIGN KEY(reserved_cdk_id) REFERENCES cdk_inventory(id) ON DELETE SET NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_delivery_tasks_task_no ON delivery_tasks(task_no);
CREATE INDEX IF NOT EXISTS idx_delivery_tasks_order_id ON delivery_tasks(order_id);
CREATE INDEX IF NOT EXISTS idx_delivery_tasks_order_item_id ON delivery_tasks(order_item_id);
CREATE INDEX IF NOT EXISTS idx_delivery_tasks_status ON delivery_tasks(task_status);
CREATE INDEX IF NOT EXISTS idx_delivery_tasks_platform_order_id ON delivery_tasks(platform_order_id);
CREATE TABLE IF NOT EXISTS claim_tokens (
id INTEGER PRIMARY KEY AUTOINCREMENT,
task_id INTEGER NOT NULL,
token TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'active',
expired_at TEXT NOT NULL,
used_at TEXT,
max_use_count INTEGER NOT NULL DEFAULT 1,
used_count INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
FOREIGN KEY(task_id) REFERENCES delivery_tasks(id) ON DELETE CASCADE
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_claim_tokens_token ON claim_tokens(token);
CREATE INDEX IF NOT EXISTS idx_claim_tokens_task_id ON claim_tokens(task_id);
CREATE INDEX IF NOT EXISTS idx_claim_tokens_status ON claim_tokens(status);
CREATE TABLE IF NOT EXISTS webhook_events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
platform TEXT NOT NULL,
event_type TEXT NOT NULL,
event_key TEXT NOT NULL,
signature_valid INTEGER NOT NULL DEFAULT 0,
headers_json TEXT NOT NULL DEFAULT '{}',
query_json TEXT NOT NULL DEFAULT '{}',
body_json TEXT NOT NULL DEFAULT '{}',
processed INTEGER NOT NULL DEFAULT 0,
process_error TEXT NOT NULL DEFAULT '',
related_order_id INTEGER,
created_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_webhook_events_platform_key ON webhook_events(platform, event_key);
CREATE INDEX IF NOT EXISTS idx_webhook_events_processed ON webhook_events(processed);
CREATE INDEX IF NOT EXISTS idx_webhook_events_created_at ON webhook_events(created_at);
@@ -1,12 +0,0 @@
CREATE TABLE IF NOT EXISTS admin_users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
role TEXT NOT NULL DEFAULT 'operator',
status TEXT NOT NULL DEFAULT 'active',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_admin_users_role ON admin_users(role);
CREATE INDEX IF NOT EXISTS idx_admin_users_status ON admin_users(status);
@@ -1,17 +0,0 @@
CREATE TABLE IF NOT EXISTS admin_audit_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
actor_user_id INTEGER NOT NULL,
actor_username TEXT NOT NULL,
actor_role TEXT NOT NULL,
action TEXT NOT NULL,
target_type TEXT NOT NULL,
target_id TEXT NOT NULL DEFAULT '',
payload_json TEXT NOT NULL DEFAULT '{}',
created_at TEXT NOT NULL,
FOREIGN KEY (actor_user_id) REFERENCES admin_users(id)
);
CREATE INDEX IF NOT EXISTS idx_admin_audit_logs_actor_user_id ON admin_audit_logs(actor_user_id);
CREATE INDEX IF NOT EXISTS idx_admin_audit_logs_action ON admin_audit_logs(action);
CREATE INDEX IF NOT EXISTS idx_admin_audit_logs_target_type ON admin_audit_logs(target_type);
CREATE INDEX IF NOT EXISTS idx_admin_audit_logs_created_at ON admin_audit_logs(created_at);
@@ -1,29 +0,0 @@
CREATE TABLE IF NOT EXISTS message_deliveries (
id INTEGER PRIMARY KEY AUTOINCREMENT,
platform TEXT NOT NULL,
channel TEXT NOT NULL,
order_id INTEGER,
task_id INTEGER,
platform_order_id TEXT NOT NULL DEFAULT '',
recipient_key TEXT NOT NULL DEFAULT '',
message_content TEXT NOT NULL DEFAULT '',
claim_url TEXT NOT NULL DEFAULT '',
status TEXT NOT NULL DEFAULT 'pending',
request_url TEXT NOT NULL DEFAULT '',
request_headers_json TEXT NOT NULL DEFAULT '{}',
request_body_json TEXT NOT NULL DEFAULT '{}',
response_status INTEGER NOT NULL DEFAULT 0,
response_json TEXT NOT NULL DEFAULT '{}',
error_message TEXT NOT NULL DEFAULT '',
sent_at TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE SET NULL,
FOREIGN KEY (task_id) REFERENCES delivery_tasks(id) ON DELETE SET NULL
);
CREATE INDEX IF NOT EXISTS idx_message_deliveries_order_id ON message_deliveries(order_id);
CREATE INDEX IF NOT EXISTS idx_message_deliveries_task_id ON message_deliveries(task_id);
CREATE INDEX IF NOT EXISTS idx_message_deliveries_platform_order_id ON message_deliveries(platform_order_id);
CREATE INDEX IF NOT EXISTS idx_message_deliveries_status ON message_deliveries(status);
CREATE INDEX IF NOT EXISTS idx_message_deliveries_created_at ON message_deliveries(created_at);
@@ -1,94 +0,0 @@
PRAGMA foreign_keys = OFF;
CREATE TABLE IF NOT EXISTS orders__new (
id INTEGER PRIMARY KEY AUTOINCREMENT,
provider TEXT NOT NULL DEFAULT 'agiso',
platform TEXT NOT NULL,
shop_id TEXT NOT NULL DEFAULT '',
shop_name TEXT NOT NULL DEFAULT '',
platform_order_id TEXT NOT NULL,
order_status TEXT NOT NULL DEFAULT 'created',
pay_status TEXT NOT NULL DEFAULT 'unpaid',
buyer_id TEXT NOT NULL DEFAULT '',
buyer_name TEXT NOT NULL DEFAULT '',
receiver_contact TEXT NOT NULL DEFAULT '',
total_amount INTEGER NOT NULL DEFAULT 0,
currency TEXT NOT NULL DEFAULT 'CNY',
raw_payload_json TEXT NOT NULL DEFAULT '{}',
paid_at TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
INSERT INTO orders__new (
id,
provider,
platform,
shop_id,
shop_name,
platform_order_id,
order_status,
pay_status,
buyer_id,
buyer_name,
receiver_contact,
total_amount,
currency,
raw_payload_json,
paid_at,
created_at,
updated_at
)
SELECT
id,
'agiso',
platform,
'',
'',
platform_order_id,
order_status,
pay_status,
buyer_id,
buyer_name,
receiver_contact,
total_amount,
currency,
raw_payload_json,
paid_at,
created_at,
updated_at
FROM orders;
DROP TABLE orders;
ALTER TABLE orders__new RENAME TO orders;
CREATE UNIQUE INDEX IF NOT EXISTS idx_orders_provider_platform_shop_order
ON orders(provider, platform, shop_id, platform_order_id);
ALTER TABLE webhook_events ADD COLUMN provider TEXT NOT NULL DEFAULT 'agiso';
ALTER TABLE webhook_events ADD COLUMN shop_id TEXT NOT NULL DEFAULT '';
ALTER TABLE webhook_events ADD COLUMN shop_name TEXT NOT NULL DEFAULT '';
UPDATE webhook_events
SET provider = CASE
WHEN trim(provider) = '' THEN 'agiso'
ELSE provider
END;
CREATE INDEX IF NOT EXISTS idx_webhook_events_provider_platform_shop
ON webhook_events(provider, platform, shop_id);
ALTER TABLE message_deliveries ADD COLUMN provider TEXT NOT NULL DEFAULT 'agiso';
ALTER TABLE message_deliveries ADD COLUMN shop_id TEXT NOT NULL DEFAULT '';
ALTER TABLE message_deliveries ADD COLUMN shop_name TEXT NOT NULL DEFAULT '';
UPDATE message_deliveries
SET provider = CASE
WHEN trim(provider) = '' THEN 'agiso'
ELSE provider
END;
CREATE INDEX IF NOT EXISTS idx_message_deliveries_provider_platform_shop
ON message_deliveries(provider, platform, shop_id);
PRAGMA foreign_keys = ON;
@@ -1,3 +0,0 @@
UPDATE orders
SET total_amount = total_amount * 100
WHERE total_amount != 0;
@@ -1,11 +0,0 @@
UPDATE orders
SET platform = 'xianyu'
WHERE lower(trim(platform)) = 'aldsidle';
UPDATE webhook_events
SET platform = 'xianyu'
WHERE lower(trim(platform)) = 'aldsidle';
UPDATE message_deliveries
SET platform = 'xianyu'
WHERE lower(trim(platform)) = 'aldsidle';