离职联动去掉30天缓冲与日期暂缓,花名册标记离职即托管归档

This commit is contained in:
chenpei 2026-08-28 15:54:00 +08:00
parent 97bcfcf5c9
commit 5ef8e77eeb
4 changed files with 12 additions and 42 deletions

View File

@ -154,8 +154,8 @@ scgi_temp_path <proxy_dir>/tmp/scgi;
上游标记离职后自动执行:归档素材库账号 + 把素材托管给所在部门。
- **默认只处理离职满 30 天者**(`RESIGNATION_MIN_DAYS`),避开交接期、返聘与上游数据延迟
- 离职日期缺失者暂缓,交人工核实
- 人事已确认花名册标记离职即为事实离职:**离职即托管+归档,无天数缓冲**(`RESIGNATION_MIN_DAYS` 已废弃)
- 离职日期缺失者同样处理(日期仅作展示,不作门槛)
- 超级管理员账号不由自动流程处理
### 素材归属与署名分离

View File

@ -6,7 +6,7 @@
* (employmentState 非 RESIGNED)时,账号应自动激活,无需管理员手动激活。
* 2. 自动角色:花名册 M 级(levelType='M')或负责人(isLeadership)→ 管理层角色;
* P 级 → 普通用户。角色由花名册数据派生,不靠人工设置。
* 3. 离职自动归档:已在 organization-sync.applyResignationEffects 处理(离职满 30 天)。
* 3. 离职自动归档:已在 organization-sync.applyResignationEffects 处理(离职即处理,无天数缓冲)。
*
* 供登录链路(凭证登录 / 钉钉登录)与同步链路复用。
*/

View File

@ -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<ResignationEffectResult> {
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) {

View File

@ -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 + ' 条')