From 5ef8e77eeb4e6eba0bbe919aeda1154da72a3cff Mon Sep 17 00:00:00 2001 From: chenpei Date: Fri, 28 Aug 2026 15:54:00 +0800 Subject: [PATCH] =?UTF-8?q?=E7=A6=BB=E8=81=8C=E8=81=94=E5=8A=A8=E5=8E=BB?= =?UTF-8?q?=E6=8E=8930=E5=A4=A9=E7=BC=93=E5=86=B2=E4=B8=8E=E6=97=A5?= =?UTF-8?q?=E6=9C=9F=E6=9A=82=E7=BC=93=EF=BC=8C=E8=8A=B1=E5=90=8D=E5=86=8C?= =?UTF-8?q?=E6=A0=87=E8=AE=B0=E7=A6=BB=E8=81=8C=E5=8D=B3=E6=89=98=E7=AE=A1?= =?UTF-8?q?=E5=BD=92=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CLAUDE.md | 4 ++-- lib/auto-personnel-rules.ts | 2 +- lib/organization-sync.ts | 39 ++++++------------------------------ scripts/sync-organization.ts | 9 +++------ 4 files changed, 12 insertions(+), 42 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 2215026..91c9674 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -154,8 +154,8 @@ scgi_temp_path /tmp/scgi; 上游标记离职后自动执行:归档素材库账号 + 把素材托管给所在部门。 -- **默认只处理离职满 30 天者**(`RESIGNATION_MIN_DAYS`),避开交接期、返聘与上游数据延迟 -- 离职日期缺失者暂缓,交人工核实 +- 人事已确认花名册标记离职即为事实离职:**离职即托管+归档,无天数缓冲**(`RESIGNATION_MIN_DAYS` 已废弃) +- 离职日期缺失者同样处理(日期仅作展示,不作门槛) - 超级管理员账号不由自动流程处理 ### 素材归属与署名分离 diff --git a/lib/auto-personnel-rules.ts b/lib/auto-personnel-rules.ts index c394f8f..6d5681c 100644 --- a/lib/auto-personnel-rules.ts +++ b/lib/auto-personnel-rules.ts @@ -6,7 +6,7 @@ * (employmentState 非 RESIGNED)时,账号应自动激活,无需管理员手动激活。 * 2. 自动角色:花名册 M 级(levelType='M')或负责人(isLeadership)→ 管理层角色; * P 级 → 普通用户。角色由花名册数据派生,不靠人工设置。 - * 3. 离职自动归档:已在 organization-sync.applyResignationEffects 处理(离职满 30 天)。 + * 3. 离职自动归档:已在 organization-sync.applyResignationEffects 处理(离职即处理,无天数缓冲)。 * * 供登录链路(凭证登录 / 钉钉登录)与同步链路复用。 */ diff --git a/lib/organization-sync.ts b/lib/organization-sync.ts index 51139d2..15683fd 100644 --- a/lib/organization-sync.ts +++ b/lib/organization-sync.ts @@ -441,8 +441,6 @@ export interface ResignationEffectResult { candidates: number /** 满足处理条件、实际纳入本次联动的人数 */ pending: number - /** 因离职时间不足或日期缺失而暂缓的人数 */ - deferred: number archivedAccounts: number custodiedMaterials: number details: Array<{ @@ -452,7 +450,6 @@ export interface ResignationEffectResult { materials: number resignedAt: Date | null }> - deferredDetails: Array<{ displayName: string; reason: string }> failures: Array<{ username: string; reason: string }> dryRun: boolean } @@ -467,13 +464,13 @@ export interface ResignationEffectResult { * * 仅处理已通过钉钉唯一值建立正式绑定的账号,不按姓名猜测。 * - * `minDaysSinceResignation` 用于避开交接期、返聘与上游数据延迟:只处理离职已满 - * 指定天数的人;离职日期缺失者一并暂缓,交人工核实,不做推断。 + * 人事已确认:花名册标记离职即为事实离职,因此离职即托管+归档, + * 不再设天数缓冲;离职日期缺失者同样处理(日期仅作展示,不作门槛)。 */ export async function applyResignationEffects( - options: { dryRun?: boolean; minDaysSinceResignation?: number } = {}, + options: { dryRun?: boolean } = {}, ): Promise { - const { dryRun = false, minDaysSinceResignation } = options + const { dryRun = false } = options // 通过正式绑定关系找出"人已离职、账号还活着"的情况 const bindings = await prisma.userEmployeeBinding.findMany({ @@ -492,41 +489,18 @@ export async function applyResignationEffects( const result: ResignationEffectResult = { candidates: bindings.length, pending: 0, - deferred: 0, archivedAccounts: 0, custodiedMaterials: 0, details: [], - deferredDetails: [], failures: [], dryRun, } - const cutoff = - typeof minDaysSinceResignation === 'number' - ? new Date(Date.now() - minDaysSinceResignation * 24 * 60 * 60 * 1000) - : null - for (const binding of bindings) { const username = binding.user.username const displayName = binding.employeeProfile.displayName const resignedAt = binding.employeeProfile.resignedAt - if (cutoff) { - if (!resignedAt) { - result.deferred += 1 - result.deferredDetails.push({ displayName, reason: '离职日期缺失,需人工核实' }) - continue - } - if (resignedAt > cutoff) { - result.deferred += 1 - result.deferredDetails.push({ - displayName, - reason: '离职未满 ' + minDaysSinceResignation + ' 天(' + resignedAt.toLocaleDateString('zh-CN') + ')', - }) - continue - } - } - const materialCount = await prisma.material.count({ where: { userId: binding.userId, deletedAt: null }, }) @@ -604,7 +578,6 @@ export function ensureDailyOrganizationSyncStarted() { } const syncHour = Math.min(23, Math.max(0, Number(process.env.ORGANIZATION_SYNC_HOUR || 2))) - const minDays = Math.max(0, Number(process.env.RESIGNATION_MIN_DAYS || 30)) const runOnce = async () => { try { @@ -628,10 +601,10 @@ export function ensureDailyOrganizationSyncStarted() { } try { - const effect = await applyResignationEffects({ minDaysSinceResignation: minDays }) + const effect = await applyResignationEffects() if (effect.archivedAccounts > 0 || effect.custodiedMaterials > 0) { console.info( - `[organization-sync] 离职联动:归档 ${effect.archivedAccounts} 个账号,托管 ${effect.custodiedMaterials} 条素材,暂缓 ${effect.deferred} 人`, + `[organization-sync] 离职联动:归档 ${effect.archivedAccounts} 个账号,托管 ${effect.custodiedMaterials} 条素材`, ) } if (effect.failures.length > 0) { diff --git a/scripts/sync-organization.ts b/scripts/sync-organization.ts index cc8d557..1ae3a05 100644 --- a/scripts/sync-organization.ts +++ b/scripts/sync-organization.ts @@ -63,13 +63,10 @@ async function main() { console.log(' └ 状态未知 ' + r.markedUnknown) console.log('\n===== 三、离职联动 =====') - // 只处理离职已满 30 天的人:避开交接期、返聘与上游数据延迟; - // 离职日期缺失者一并暂缓,交人工核实 - const MIN_DAYS = Number(process.env.RESIGNATION_MIN_DAYS || 30) - const e = await applyResignationEffects({ dryRun: !apply, minDaysSinceResignation: MIN_DAYS }) + // 人事确认:花名册标记离职即为事实离职,立即托管+归档,无天数缓冲 + const e = await applyResignationEffects({ dryRun: !apply }) console.log(' 人已离职、账号仍活跃 ' + e.candidates + ' 人') - console.log(' ├ 本次纳入处理 ' + e.pending + ' 人(离职已满 ' + MIN_DAYS + ' 天)') - console.log(' └ 暂缓 ' + e.deferred + ' 人') + console.log(' └ 本次纳入处理 ' + e.pending + ' 人') if (apply) { console.log('\n 已归档账号 ' + e.archivedAccounts) console.log(' 已托管素材 ' + e.custodiedMaterials + ' 条')