小程序修正
This commit is contained in:
1 parent
5a92bab835
commit
6ae4c109ce
12 files changed
+232
-62
No files matched your search
@@ -28,23 +28,26 @@ const prisma = new PrismaClient();
|
||||
|
||||
const flag = (name: string): boolean => process.argv.includes(`--${name}`);
|
||||
|
||||
/** 清空顺序:先子后父。 */
|
||||
const CLEAR_ORDER = [
|
||||
'paymentNotifyLog',
|
||||
'order',
|
||||
'entitlement',
|
||||
'lesson',
|
||||
'homeCourse',
|
||||
'freeLesson',
|
||||
'homeFreeCourse',
|
||||
'banner',
|
||||
'article',
|
||||
'courseTip',
|
||||
'setting',
|
||||
'product',
|
||||
'course',
|
||||
'user',
|
||||
] as const;
|
||||
/**
|
||||
* 清空顺序:先子后父(有外键,顺序不能乱)。
|
||||
* 同时给出真实表名,用于重置自增。
|
||||
*/
|
||||
const CLEAR_ORDER: Array<{ model: string; table: string }> = [
|
||||
{ model: 'paymentNotifyLog', table: 'payment_notify_log' },
|
||||
{ model: 'order', table: 'orders' },
|
||||
{ model: 'entitlement', table: 'entitlements' },
|
||||
{ model: 'lesson', table: 'lessons' },
|
||||
{ model: 'homeCourse', table: 'home_courses' },
|
||||
{ model: 'freeLesson', table: 'free_lessons' },
|
||||
{ model: 'homeFreeCourse', table: 'home_free_courses' },
|
||||
{ model: 'banner', table: 'banners' },
|
||||
{ model: 'article', table: 'articles' },
|
||||
{ model: 'courseTip', table: 'course_tips' },
|
||||
{ model: 'setting', table: 'settings' },
|
||||
{ model: 'product', table: 'products' },
|
||||
{ model: 'course', table: 'courses' },
|
||||
{ model: 'user', table: 'users' },
|
||||
];
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const dryRun = flag('dry-run');
|
||||
@@ -108,14 +111,41 @@ async function main(): Promise<void> {
|
||||
|
||||
// ── 写库 ───────────────────────────────────────────────────────────────
|
||||
console.log('\n── 写入新库 ────────────────────────────');
|
||||
|
||||
type Delegates = Record<string, { deleteMany: () => Promise<unknown> }>;
|
||||
const delegates = prisma as unknown as Delegates;
|
||||
|
||||
// 清空与重置自增必须放在事务外:MySQL 的 ALTER TABLE 会隐式提交事务。
|
||||
for (const { model } of CLEAR_ORDER) await delegates[model]!.deleteMany();
|
||||
|
||||
// 关键:deleteMany 不会重置自增计数器。
|
||||
// 上一次失败的迁移虽然回滚了数据,但计数器已经前进,
|
||||
// 导致再次插入时 id 不是从 1 开始,与 buildDataset 里按顺序推导的映射对不上,
|
||||
// 进而触发外键约束失败。
|
||||
for (const { table } of CLEAR_ORDER) {
|
||||
await prisma.$executeRawUnsafe(`ALTER TABLE \`${table}\` AUTO_INCREMENT = 1`);
|
||||
}
|
||||
|
||||
await prisma.$transaction(
|
||||
async (tx) => {
|
||||
type Delegates = Record<string, { deleteMany: () => Promise<unknown> }>;
|
||||
const delegates = tx as unknown as Delegates;
|
||||
for (const model of CLEAR_ORDER) await delegates[model]!.deleteMany();
|
||||
|
||||
await tx.course.createMany({ data: ds.courses });
|
||||
await tx.user.createMany({ data: ds.users });
|
||||
|
||||
// 校验实际自增 id 与推导出的映射一致。
|
||||
// 若不一致,lessons / entitlements 会引用到错误的课程——这类错误外键查不出来,
|
||||
// 但会让用户点开 A 课程看到 B 课程的内容,所以必须显式拦住。
|
||||
const createdCourses = await tx.course.findMany({ select: { id: true, code: true } });
|
||||
const idByCode = new Map(createdCourses.map((c) => [c.code, c.id]));
|
||||
ds.courses.forEach((course, index) => {
|
||||
const actual = idByCode.get(course.code);
|
||||
if (actual !== index + 1) {
|
||||
throw new Error(
|
||||
`课程 id 与预期不符:${course.title} 期望 id=${index + 1},实际 id=${actual ?? '缺失'}。` +
|
||||
'为避免课程数据错位,迁移已中止。请清空新库后重跑。',
|
||||
);
|
||||
}
|
||||
});
|
||||
await tx.lesson.createMany({ data: ds.lessons });
|
||||
await tx.entitlement.createMany({ data: ds.entitlements });
|
||||
await tx.homeCourse.createMany({ data: ds.homeCourses });
|
||||
|
||||
Reference in new issue
Block a user