/** * 处置:刘伟/江澄账号串档修复(事务,可回滚) * * 背景:花名册"刘伟(花名江澄,工号JS19071901,M1,品类六部运营主管)"的档案 * 被素材库"刘伟"账号(外部交流,钉钉ID 36135934671751)错误绑定; * 素材库"江澄"账号(品类六部正主,钉钉ID 1617610635671751)反而无绑定。 * * 处置(用户确认): * 1. 硬删除素材库"刘伟"账号(无素材/无日志,影响已评估为零) * 2. 档案 6eb6fd4e(M1/JS19071901)部门修正为品类六部 * 3. 建立"江澄"账号 ↔ 该档案的活跃绑定(MANUAL_VERIFIED) * 4. "江澄"账号角色升为 DEPT_ADMIN(M1 负责人) * * 用法:npx tsx --env-file=.env scripts/fix-liuwei-jiangcheng.ts --apply * 不加 --apply 为预演 */ import { PrismaClient } from '@prisma/client' const prisma = new PrismaClient() const APPLY = process.argv.includes('--apply') const LIUWEI_USER_ID = 'f8fe5ab5-29ab-44c9-833d-7a7247f39d7d' const JIANGCHENG_USER_ID = 'da2f7fc3-8453-4ae4-a61c-7fc060ed7a49' const PROFILE_ID = '6eb6fd4e-6cdd-43ca-8697-7f48a116171a' const CATEGORY6_DEPT_PATH = '江苏简尚品牌管理有限公司/运营中心A/事业三部/品类六部' ;(async () => { console.log(APPLY ? '=== 实际执行模式 ===' : '=== 预演模式(不写入)===') // 预检:确认关键对象存在 const liuwei = await prisma.user.findUnique({ where: { id: LIUWEI_USER_ID } }) const jiangcheng = await prisma.user.findUnique({ where: { id: JIANGCHENG_USER_ID } }) const profile = await prisma.performanceSurveyEmployeeProfile.findUnique({ where: { id: PROFILE_ID } }) if (!liuwei) throw new Error('刘伟账号不存在') if (!jiangcheng) throw new Error('江澄账号不存在') if (!profile) throw new Error('品类六部档案不存在') console.log(`刘伟账号: ${liuwei.username} (${liuwei.dingtalkUserId})`) console.log(`江澄账号: ${jiangcheng.username} (${jiangcheng.dingtalkUserId})`) console.log(`档案: ${profile.displayName} | 当前部门: ${profile.departmentPath} | 等级: ${profile.positionLevel}(${profile.levelType})`) console.log('') // 预检:刘伟账号必须无素材/无日志 const mats = await prisma.material.count({ where: { userId: LIUWEI_USER_ID } }) const logs = await prisma.materialDownloadLog.count({ where: { userId: LIUWEI_USER_ID } }).catch(() => 0) if (mats > 0 || logs > 0) { throw new Error(`刘伟账号还有数据(素材${mats}/日志${logs}),拒绝删除,请人工检查`) } console.log(`安全预检通过:刘伟账号素材=${mats} 日志=${logs}`) if (!APPLY) { console.log('\n[预演] 将执行:') console.log(' 1. 删除刘伟账号(含其绑定)') console.log(` 2. 档案 ${PROFILE_ID} 部门 → ${CATEGORY6_DEPT_PATH}`) console.log(' 3. 江澄账号 ↔ 档案 建立 ACTIVE 绑定(MANUAL_VERIFIED)') console.log(' 4. 江澄账号角色 → DEPT_ADMIN') console.log('\n(未写入任何数据,加 --apply 执行)') await prisma.$disconnect() return } await prisma.$transaction(async (tx) => { // 1. 删除刘伟账号(级联删绑定;素材/日志已确认为 0) await tx.userEmployeeBinding.deleteMany({ where: { userId: LIUWEI_USER_ID } }) await tx.user.delete({ where: { id: LIUWEI_USER_ID } }) console.log('✓ 已删除刘伟账号及其绑定') // 2. 档案部门修正为品类六部 await tx.performanceSurveyEmployeeProfile.update({ where: { id: PROFILE_ID }, data: { departmentPath: CATEGORY6_DEPT_PATH, departmentName: '品类六部', }, }) console.log(`✓ 档案部门已修正: ${CATEGORY6_DEPT_PATH}`) // 3. 江澄账号 ↔ 档案 绑定 await tx.userEmployeeBinding.create({ data: { userId: JIANGCHENG_USER_ID, employeeProfileId: PROFILE_ID, bindingSource: 'MANUAL_VERIFIED', status: 'ACTIVE', verifiedAt: new Date(), note: '人工处置:刘伟/江澄同名串档修复(2026-08-14)', }, }) console.log('✓ 江澄账号已建立绑定') // 4. 江澄角色升 DEPT_ADMIN await tx.user.update({ where: { id: JIANGCHENG_USER_ID }, data: { role: 'DEPT_ADMIN' }, }) console.log('✓ 江澄账号角色 → DEPT_ADMIN') }) // 验证 const verify = await prisma.user.findUnique({ where: { id: JIANGCHENG_USER_ID }, select: { username: true, role: true, employeeBindings: { where: { status: 'ACTIVE' }, select: { employeeProfile: { select: { displayName: true, departmentPath: true, positionLevel: true, levelType: true } } } }, }, }) console.log('\n=== 验证 ===') console.log(JSON.stringify(verify, null, 2)) const liuweiAfter = await prisma.user.findUnique({ where: { id: LIUWEI_USER_ID } }).catch(() => null) console.log(`刘伟账号删除后存在: ${Boolean(liuweiAfter)}`) await prisma.$disconnect() })().catch((e) => { console.error(e); process.exit(1) })