Files
zszq-trs/YLErpWeb/fe-tests/tools/exportHistoricalSwapEvents.js
T

108 lines
4.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* exportHistoricalSwapEvents.js — 一次性历史数据导出(开发期工具,⚠ 需要数据库连接)
* ============================================================================
* 用途:把生产/测试库里真实的 swap_event 记录导出成
* fe-tests/fixtures/historical_swap_events.json
* 供 historicalReconciliation.test.js 离线对账。
*
* 数据库边界:本脚本【是唯一】触碰数据库的地方,但它【不属于 npm test 路径】
* (文件名非 *.test.jsjest testMatch 不会执行它)。CI 跑测试时零 DB 依赖。
*
* 运行(在 YLErpWeb/fe-tests 下):
* npm i mysql2 # 仅开发期依赖,不进生产
* MYSQL_HOST=... MYSQL_USER=... MYSQL_PASS=... MYSQL_DB=... \
* node tools/exportHistoricalSwapEvents.js [--from 2026-01-01] [--to 2026-08-08] [--limit 200]
*
* 导出后请把结果提交进仓库(fixture 文件本身不含任何凭据)。
*/
'use strict';
const fs = require('fs');
const path = require('path');
async function main() {
let mysql;
try {
mysql = require('mysql2/promise');
} catch (e) {
console.error('缺少 mysql2,请先 `npm i mysql2`(仅开发期依赖)');
process.exit(2);
}
const cfg = {
host: process.env.MYSQL_HOST,
user: process.env.MYSQL_USER,
password: process.env.MYSQL_PASS,
database: process.env.MYSQL_DB,
};
if (!cfg.host || !cfg.user || !cfg.database) {
console.error('请提供环境变量 MYSQL_HOST / MYSQL_USER / MYSQL_PASS / MYSQL_DB');
process.exit(2);
}
const args = process.argv.slice(2);
const getArg = (name, dflt) => {
const i = args.indexOf(name);
return i >= 0 && args[i + 1] ? args[i + 1] : dflt;
};
const from = getArg('--from', '1970-01-01');
const to = getArg('--to', '9999-12-31');
const limit = parseInt(getArg('--limit', '200'), 10);
const conn = await mysql.createConnection(cfg);
// event_type: 2=平仓(unwind) 3=结息(income) 4=多空平仓 5=多空互换(按实际枚举调整)
const [rows] = await conn.execute(
`SELECT id, event_type, data, create_time
FROM swap_event
WHERE event_type IN (2,3,4,5)
AND create_time BETWEEN ? AND ?
ORDER BY create_time DESC
LIMIT ?`,
[from, to, limit]
);
await conn.end();
const records = [];
for (const row of rows) {
let data = {};
try { data = JSON.parse(row.data || '{}'); } catch (e) { continue; }
// swap_event.data 是后端序列化的 UnwindData,字段需按实际结构映射。
// 下面仅抽取对账所需的最小字段;不同 event_type 的字段名以实际库结构为准。
const deal = data.deal || data.Deal || {};
const floatPosition = data.floatPosition || data.FloatPosition || {};
const interestList = data.interestList || data.InterestList || [];
const marginList = data.marginList || data.MarginList || [];
records.push({
id: `EVT_${row.id}`,
desc: `导出记录 event_type=${row.event_type} @ ${row.create_time}`,
eventType: row.event_type,
deal,
floatPosition,
interestList,
marginList,
expected: {
SwapCloseAmount: Number(deal.SwapCloseAmount != null ? deal.SwapCloseAmount : floatPosition.SwapCloseAmount),
SwapRealizedPnL: Number(deal.SwapRealizedPnL != null ? deal.SwapRealizedPnL : floatPosition.SwapRealizedPnL),
SwapMarginAmount: Number(deal.SwapMarginAmount != null ? deal.SwapMarginAmount : floatPosition.SwapMarginAmount),
},
});
}
const out = {
meta: {
schemaVersion: 1,
source: `exported from ${cfg.database} (${from}..${to}, limit ${limit})`,
note: '由 tools/exportHistoricalSwapEvents.js 生成;运行时无需 DB。',
},
records,
};
const outPath = path.join(__dirname, '..', 'fixtures', 'historical_swap_events.json');
fs.writeFileSync(outPath, JSON.stringify(out, null, 2));
console.log(`已导出 ${records.length} 条记录 → ${outPath}`);
}
main().catch(e => { console.error(e); process.exit(1); });