Files
vps-management-bot/aff-monitor/db/init.js
2026-03-21 01:10:53 +08:00

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();