gongxiangp2/scripts/fix-liuwei-jiangcheng.ts

113 lines
5.0 KiB
TypeScript
Raw Permalink 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.

/**
* 处置:刘伟/江澄账号串档修复(事务,可回滚)
*
* 背景:花名册"刘伟(花名江澄,工号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) })