109 lines
4.2 KiB
JavaScript
109 lines
4.2 KiB
JavaScript
/**
|
|
* 数据库初始化 — 建表 + 种子数据
|
|
*/
|
|
const Database = require('better-sqlite3');
|
|
const path = require('path');
|
|
require('dotenv').config({ path: path.join(__dirname, '..', '.env') });
|
|
|
|
const dbPath = path.resolve(__dirname, '..', process.env.DB_PATH || 'db/monitor.sqlite');
|
|
const db = new Database(dbPath);
|
|
|
|
db.pragma('journal_mode = WAL');
|
|
db.pragma('foreign_keys = ON');
|
|
|
|
db.exec(`
|
|
-- ========== 商家 ==========
|
|
CREATE TABLE IF NOT EXISTS merchants (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL,
|
|
website TEXT,
|
|
notes TEXT,
|
|
created_at TEXT DEFAULT (datetime('now')),
|
|
updated_at TEXT DEFAULT (datetime('now'))
|
|
);
|
|
|
|
-- ========== 产品 ==========
|
|
CREATE TABLE IF NOT EXISTS products (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
merchant_id INTEGER NOT NULL REFERENCES merchants(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
url TEXT,
|
|
sku TEXT,
|
|
price TEXT,
|
|
in_stock INTEGER DEFAULT 0, -- 0=未知 1=有货 2=缺货
|
|
last_checked TEXT,
|
|
notes TEXT,
|
|
created_at TEXT DEFAULT (datetime('now')),
|
|
updated_at TEXT DEFAULT (datetime('now'))
|
|
);
|
|
|
|
-- ========== Aff 链接 ==========
|
|
CREATE TABLE IF NOT EXISTS aff_links (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
product_id INTEGER NOT NULL REFERENCES products(id) ON DELETE CASCADE,
|
|
platform TEXT NOT NULL DEFAULT 'default', -- e.g. default / telegram / twitter
|
|
url TEXT NOT NULL,
|
|
notes TEXT,
|
|
created_at TEXT DEFAULT (datetime('now'))
|
|
);
|
|
|
|
-- ========== Telegram 频道配置 ==========
|
|
CREATE TABLE IF NOT EXISTS tg_channels (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL,
|
|
chat_id TEXT NOT NULL,
|
|
enabled INTEGER DEFAULT 1,
|
|
notes TEXT,
|
|
created_at TEXT DEFAULT (datetime('now'))
|
|
);
|
|
|
|
-- ========== 监控任务 ==========
|
|
CREATE TABLE IF NOT EXISTS monitor_tasks (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
product_id INTEGER NOT NULL REFERENCES products(id) ON DELETE CASCADE,
|
|
tg_channel_id INTEGER REFERENCES tg_channels(id) ON DELETE SET NULL,
|
|
cron_expr TEXT DEFAULT '*/5 * * * *',
|
|
enabled INTEGER DEFAULT 1,
|
|
last_run TEXT,
|
|
created_at TEXT DEFAULT (datetime('now'))
|
|
);
|
|
|
|
-- ========== 检测记录 ==========
|
|
CREATE TABLE IF NOT EXISTS check_logs (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
task_id INTEGER NOT NULL REFERENCES monitor_tasks(id) ON DELETE CASCADE,
|
|
product_id INTEGER NOT NULL,
|
|
status TEXT NOT NULL, -- in_stock / out_of_stock / error
|
|
message TEXT,
|
|
notified INTEGER DEFAULT 0,
|
|
created_at TEXT DEFAULT (datetime('now'))
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_check_logs_task ON check_logs(task_id);
|
|
CREATE INDEX IF NOT EXISTS idx_check_logs_date ON check_logs(created_at);
|
|
CREATE INDEX IF NOT EXISTS idx_products_merchant ON products(merchant_id);
|
|
`);
|
|
|
|
console.log('✅ 数据库初始化完成:', dbPath);
|
|
|
|
// 种子数据(仅当 merchants 为空时插入)
|
|
const count = db.prepare('SELECT count(*) AS c FROM merchants').get().c;
|
|
if (count === 0) {
|
|
const insertMerchant = db.prepare('INSERT INTO merchants (name, website, notes) VALUES (?, ?, ?)');
|
|
const insertProduct = db.prepare('INSERT INTO products (merchant_id, name, url, price) VALUES (?, ?, ?, ?)');
|
|
const insertChannel = db.prepare('INSERT INTO tg_channels (name, chat_id) VALUES (?, ?)');
|
|
|
|
db.transaction(() => {
|
|
insertMerchant.run('示例商家 A', 'https://example-a.com', '这是一个演示商家');
|
|
insertMerchant.run('示例商家 B', 'https://example-b.com', null);
|
|
insertProduct.run(1, 'VPS 套餐 Basic', 'https://example-a.com/vps-basic', '$4.99/mo');
|
|
insertProduct.run(1, 'VPS 套餐 Pro', 'https://example-a.com/vps-pro', '$9.99/mo');
|
|
insertProduct.run(2, '独服 E3', 'https://example-b.com/dedi-e3', '€29/mo');
|
|
insertChannel.run('测试频道', '-1001234567890');
|
|
})();
|
|
|
|
console.log('🌱 种子数据已插入');
|
|
}
|
|
|
|
db.close();
|