diff --git a/scripts/audit-flower-mismatch.ts b/scripts/audit-flower-mismatch.ts new file mode 100644 index 0000000..afcc4b3 --- /dev/null +++ b/scripts/audit-flower-mismatch.ts @@ -0,0 +1,49 @@ +/** + * 串档排查脚本(只读): + * 找出"无花名(外部/离职人员)却带有 M 级等级/负责人标志"的可疑档案。 + * + * 规则(用户确认):有花名 = 正式员工;无花名 = 外部人员/离职人员。 + * 正式员工才应有 M/P 等级;无花名的档案若带等级,极可能是花名册同名串档。 + * + * 用法:npx tsx --env-file=.env scripts/audit-flower-mismatch.ts + */ +import { PrismaClient } from '@prisma/client' +const prisma = new PrismaClient() + +;(async () => { + console.log('=== 花名与等级一致性审计(只读)===\n') + + // 有绑定且带等级但无花名的档案(疑似串档) + const suspicious = await prisma.performanceSurveyEmployeeProfile.findMany({ + where: { + positionLevel: { not: null }, + OR: [{ flowerName: null }, { flowerName: '' }], + }, + select: { + id: true, displayName: true, realName: true, flowerName: true, jobNumber: true, + dingtalkUserId: true, departmentPath: true, positionLevel: true, levelType: true, + isLeadership: true, employmentState: true, + }, + orderBy: { departmentPath: 'asc' }, + }) + + // 过滤:只看有素材库绑定的人(无绑定的花名册人员即使无花名带等级也不是我们关注的) + const boundIds = new Set( + (await prisma.userEmployeeBinding.findMany({ + where: { status: 'ACTIVE' }, + select: { employeeProfileId: true }, + })).map((b) => b.employeeProfileId), + ) + const relevant = suspicious.filter((p) => boundIds.has(p.id)) + + console.log(`无花名但带等级 + 有绑定的档案: ${relevant.length} 条\n`) + for (const p of relevant) { + console.log(`【${p.displayName}】真名=${p.realName ?? '?'} 花名=${p.flowerName ?? '(无)'} 工号=${p.jobNumber ?? '?'}`) + console.log(` 部门=${p.departmentPath ?? '?'}`) + console.log(` 等级=${p.positionLevel}(${p.levelType ?? '?'}) 负责人=${p.isLeadership} 在职=${p.employmentState}`) + console.log(` 钉钉ID=${p.dingtalkUserId ?? '(无)'} 档案ID=${p.id}\n`) + } + + console.log('=== 审计结束(未写入任何数据)===') + await prisma.$disconnect() +})().catch((e) => { console.error(e); process.exit(1) })