"""Seed the ``identity_sync.account_backstop`` daily cron. Freezes leavers whose Feishu account the event path missed — the backstop safety net of the 2026-07-01 ADR「厂商账号生命周期收归入离职事件」. Runs daily at 02:20 UTC; it rides the existing ``feishu_contact`` target's enablement (no separate switch), so on a deployment where that target is still OFF (pre-cutover) the job runs but no-ops. Idempotent: skips if a job row with the same name already exists. No schema change — reuses ``scheduled_jobs`` + the identity-sync state tables. """ from __future__ import annotations import json import uuid import sqlalchemy as sa from alembic import op revision = "373" down_revision = "372" branch_labels = None depends_on = None _JOB = { "name": "identity_sync.account_backstop", "display_name": "数据同步中心 · 离职账号兜底收敛", "description": ( "每日 02:20 UTC 扫描离职者在厂商侧仍活跃的账号,经账号管家 coordinator " "幂等冻结(飞书 is_frozen=true)。事件路为主,这是兜底安全网;骑既有 " "feishu_contact target 启用状态,无独立开关;批量上限防误扫风暴。" ), "cron": "20 2 * * *", "config": {"batch_limit": 200}, } def _table_exists(bind, name: str) -> bool: return bind.execute( sa.text( "SELECT 1 FROM information_schema.TABLES " "WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = :n" ), {"n": name}, ).scalar() is not None def upgrade() -> None: bind = op.get_bind() if not _table_exists(bind, "scheduled_jobs"): return exists = bind.execute( sa.text("SELECT 1 FROM scheduled_jobs WHERE name = :n LIMIT 1"), {"n": _JOB["name"]}, ).scalar() if exists: return bind.execute( sa.text( "INSERT INTO scheduled_jobs " "(id, name, display_name, description, handler_key, " " trigger_type, cron_expr, timezone, enabled, config, " " run_count, failure_count, created_at, updated_at) " "VALUES (:id, :n, :dn, :d, :hk, 'cron', :ce, 'UTC', 1, :cfg, " " 0, 0, NOW(), NOW())" ), { "id": str(uuid.uuid4()), "n": _JOB["name"], "dn": _JOB["display_name"], "d": _JOB["description"], "hk": _JOB["name"], "ce": _JOB["cron"], "cfg": json.dumps(_JOB["config"]), }, ) def downgrade() -> None: bind = op.get_bind() bind.execute( sa.text("DELETE FROM scheduled_jobs WHERE name = :n"), {"n": _JOB["name"]}, )