87 lines
3.0 KiB
JavaScript
87 lines
3.0 KiB
JavaScript
|
|
/**
|
|||
|
|
* Migration 004 — PID & Slug 字段
|
|||
|
|
*
|
|||
|
|
* - internal_pid: 系统内部编号,如 VPS-000001(唯一,自动生成)
|
|||
|
|
* - provider_pid: 商家产品 ID,从购买链接自动解析(如 pid=28)
|
|||
|
|
* - slug: 前台友好 URL,唯一
|
|||
|
|
*/
|
|||
|
|
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');
|
|||
|
|
|
|||
|
|
function ensureColumn(table, column, sql) {
|
|||
|
|
const cols = db.prepare(`PRAGMA table_info(${table})`).all().map(c => c.name);
|
|||
|
|
if (!cols.includes(column)) {
|
|||
|
|
db.exec(`ALTER TABLE ${table} ADD COLUMN ${sql}`);
|
|||
|
|
console.log(`+ ${table}.${column}`);
|
|||
|
|
} else {
|
|||
|
|
console.log(` ${table}.${column} (already exists)`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 添加新字段
|
|||
|
|
ensureColumn('products', 'internal_pid', 'internal_pid TEXT');
|
|||
|
|
ensureColumn('products', 'provider_pid', 'provider_pid TEXT');
|
|||
|
|
ensureColumn('products', 'slug', 'slug TEXT');
|
|||
|
|
|
|||
|
|
// 创建唯一索引(如果尚不存在)
|
|||
|
|
try {
|
|||
|
|
db.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_products_internal_pid ON products(internal_pid) WHERE internal_pid IS NOT NULL');
|
|||
|
|
console.log('+ index: idx_products_internal_pid');
|
|||
|
|
} catch (e) {
|
|||
|
|
console.log(' index idx_products_internal_pid:', e.message);
|
|||
|
|
}
|
|||
|
|
try {
|
|||
|
|
db.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_products_slug ON products(slug) WHERE slug IS NOT NULL');
|
|||
|
|
console.log('+ index: idx_products_slug');
|
|||
|
|
} catch (e) {
|
|||
|
|
console.log(' index idx_products_slug:', e.message);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 为已有产品回填 internal_pid 和 slug
|
|||
|
|
const { generateInternalPid, generateSlug, parseProviderPid } = require('../src/utils/pidHelper');
|
|||
|
|
|
|||
|
|
const products = db.prepare('SELECT id, name, merchant_id, url, buy_url, internal_pid, slug, provider_pid FROM products').all();
|
|||
|
|
const updateStmt = db.prepare('UPDATE products SET internal_pid = ?, slug = ?, provider_pid = ? WHERE id = ?');
|
|||
|
|
|
|||
|
|
const getMerchantName = db.prepare('SELECT name FROM merchants WHERE id = ?');
|
|||
|
|
const existingSlugs = new Set(
|
|||
|
|
db.prepare("SELECT slug FROM products WHERE slug IS NOT NULL AND slug != ''").all().map(r => r.slug)
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
db.transaction(() => {
|
|||
|
|
for (const p of products) {
|
|||
|
|
let ipid = p.internal_pid;
|
|||
|
|
let slug = p.slug;
|
|||
|
|
let ppid = p.provider_pid;
|
|||
|
|
|
|||
|
|
// 生成 internal_pid
|
|||
|
|
if (!ipid) {
|
|||
|
|
ipid = generateInternalPid(db);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 生成 slug
|
|||
|
|
if (!slug) {
|
|||
|
|
const merchant = getMerchantName.get(p.merchant_id);
|
|||
|
|
const merchantName = merchant ? merchant.name : '';
|
|||
|
|
slug = generateSlug(p.name, merchantName, existingSlugs);
|
|||
|
|
existingSlugs.add(slug);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 解析 provider_pid
|
|||
|
|
if (!ppid) {
|
|||
|
|
ppid = parseProviderPid(p.buy_url) || parseProviderPid(p.url) || null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
updateStmt.run(ipid, slug, ppid, p.id);
|
|||
|
|
console.log(` product #${p.id}: internal_pid=${ipid}, slug=${slug}, provider_pid=${ppid || '(none)'}`);
|
|||
|
|
}
|
|||
|
|
})();
|
|||
|
|
|
|||
|
|
console.log('✅ migration-004 done:', dbPath);
|
|||
|
|
db.close();
|