// 把 orders.jsonl 里的历史订单导进 MySQL。可重复跑(按单号 upsert,不覆盖已有激活码) // 用法:node server/migrate.js const fs = require('fs') const path = require('path') const store = require('./store.js') const FILE = process.env.DATA_DIR ? path.join(process.env.DATA_DIR, 'orders.jsonl') : path.join(__dirname, 'data', 'orders.jsonl') async function main() { await store.ready() if (store.backend !== 'mysql') { throw new Error('现在走的是 JSONL 后端,先在 config.js 里配好 db(库名 + 账号)再跑迁移') } if (!fs.existsSync(FILE)) { console.log('没有找到 ' + FILE + ',不用迁移') return } // 一行一条、同一单号可能有多行(改状态就追加),后写的覆盖先写的 const records = new Map() let badLines = 0 for (const line of fs.readFileSync(FILE, 'utf8').split('\n')) { if (!line.trim()) continue try { const record = JSON.parse(line) records.set(record.outTradeNo, record) } catch (err) { badLines++ } } let withCode = 0 let amount = 0 const firstSeen = new Map() // openid -> 最早的下单时间,用来还原「首次使用时间」 for (const record of records.values()) { await store.saveOrder(record) if (record.code) withCode++ amount += record.amount || 0 const seen = firstSeen.get(record.openid) if (record.openid && (!seen || record.createdAt < seen)) firstSeen.set(record.openid, record.createdAt) } // 历史订单里的用户也补进用户表,首次使用时间取他们最早那笔单 for (const [openid, createdAt] of firstSeen) await store.touchUser(openid, createdAt) // 核对:库里的单数和码数应该不小于文件里读出来的 const stats = await store.stats() console.log('迁移完成:') console.log(` 文件里有 ${records.size} 笔单(${withCode} 笔带激活码,跳过 ${badLines} 行坏数据)`) console.log( ` 库里有 ${stats.orders} 笔单、${stats.codes} 个激活码、${stats.users} 个用户,其中 ${stats.pending} 笔还没发完` ) console.log(` 本次导入的金额合计 ${amount} 分`) console.log(' 请在「我的」页确认激活码都在,再决定要不要把 orders.jsonl 归档') } main().catch((err) => { console.error('迁移失败:', err.message) process.exit(1) })