import 'dotenv/config'; import { PrismaClient } from '@prisma/client'; import { encrypt, hash } from '../src/common/id-card-cipher.js'; /** * id_card_no 加密回填脚本(迁移 A 之后、迁移 B 之前运行一次)。 * * 遍历 ltc_elder / ltc_staff,读取明文 `id_card_no`,计算 * `id_card_no_hash = SHA-256(明文)`、`id_card_no_enc = AES-256-GCM(明文)`,回写两列。 * 幂等:可重复执行(按主键逐行覆盖)。 * * 运行:pnpm exec tsx prisma/encrypt-idcard.ts */ const prisma = new PrismaClient(); async function backfill(table: 'ltc_elder' | 'ltc_staff'): Promise { const rows = await prisma.$queryRawUnsafe>( `SELECT id, id_card_no FROM ${table} WHERE id_card_no IS NOT NULL AND id_card_no <> ''`, ); let updated = 0; for (const row of rows) { const plain = row.id_card_no as string; await prisma.$executeRawUnsafe( `UPDATE ${table} SET id_card_no_hash = ?, id_card_no_enc = ? WHERE id = ?`, hash(plain), encrypt(plain), row.id, ); updated += 1; } console.log(`${table}: 回填 ${updated} 行`); return updated; } async function main(): Promise { const elderCount = await backfill('ltc_elder'); const staffCount = await backfill('ltc_staff'); console.log(`回填完成:elder=${elderCount} staff=${staffCount}`); } main() .catch((e) => { console.error(e); process.exitCode = 1; }) .finally(async () => { await prisma.$disconnect(); });