大修改

This commit is contained in:
Yeomai committed 2026-09-30 03:38:10 +08:00
1 parent a1cbc9404a
commit e81481623c
47 files changed
+1453 -285

No files matched your search

+6 -1
View File
@@ -115,7 +115,12 @@ export function parseTupleList(payload: string): SqlValue[][] {
/** 取出某张表全部 INSERT 行。表不存在时返回空数组。 */
export function readTable(sql: string, table: string): SqlValue[][] {
const re = new RegExp('INSERT INTO `' + table.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '` VALUES (.*?);\\n', 'gs');
// 结尾同时兼容 LF 与 CRLF:Windows 上导出的 dump 是 CRLF,
// 只写 \n 会全部匹配失败,表现为「所有表都是空数组」这种静默错误。
const re = new RegExp(
'INSERT INTO `' + table.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '` VALUES (.*?);(?:\\r?\\n|$)',
'gs',
);
const rows: SqlValue[][] = [];
for (const m of sql.matchAll(re)) {
rows.push(...parseTupleList(m[1]!));
@@ -1,6 +1,7 @@
import { describe, expect, it } from 'vitest';
import { buildDataset } from './build';
import { DEFAULT_DUMP_PATH, loadLegacyDump } from './load';
import { readTable } from './parser';
import {
HOME_COURSE_TITLE_MAP,
SETTINGS_KEY_MAP,
@@ -153,3 +154,21 @@ describe('buildDataset(真实旧库数据)', () => {
expect(all).toContain('https://data.example.com');
});
});
describe('readTable 换行兼容', () => {
const lf = "INSERT INTO `t` VALUES (1,'a');\n";
const crlf = "INSERT INTO `t` VALUES (1,'a');\r\nINSERT INTO `t` VALUES (2,'b');\r\n";
it('LF 换行的 dump 能解析', () => {
expect(readTable(lf, 't')).toEqual([[1, 'a']]);
});
// 回归:只匹配 \n 时,Windows 导出的 CRLF dump 会全部读成空数组,
// 迁移会静默产出空数据(真实踩过)
it('CRLF 换行的 dump 能解析(Windows 导出的格式)', () => {
expect(readTable(crlf, 't')).toEqual([
[1, 'a'],
[2, 'b'],
]);
});
});
+3
View File
@@ -78,6 +78,9 @@ export function transformUser(row: Row) {
nickname: strOrNull(row.用户名)?.slice(0, 32) ?? null,
headicon_id: int(row.HeadiconID),
status: 1,
// 旧表「是否登录」:'1' 表示用户主动使用过登录功能。新版静默登录不会改这一位,
// 所以迁移时必须原样搬过来,否则老用户会被当成没登录过。
is_login: str(row.是否登录) === '1' ? 1 : 0,
registered_at: registeredAt ?? new Date(0),
last_login_at: parseLegacyDate(row.最后登录日期),
};