"""把「催几轮后升级」这一行种进平台设置 ## 为什么单独一条迁移 `assistant.room_escalate_after_reminders`(默认 3)是升级行为**唯一**的旋钮,而 它在 635 落地时只以代码里的默认值存在 —— 平台设置页列的是 `platform_settings` 里**实际有的行**,所以这个键在页面上根本不出现:一个管理员 想把它改成 2 或者 5,没有入口,除非他先从运维手册里抄出准确的键名和类型再手工 新建一行。 同一批设置里另外五个键(群内应答两个开关、群频次、保留期、静默提问)都是种进去 的,唯独这一个漏了。「配置入口指向真开关」是这个仓库反复付过学费的一条:一个 文档里写着、页面上不存在的旋钮,等于没有。 ## 幂等 & 不覆盖 管理员可能已经手工建过这一行(照着运维手册)。**已经存在的行不覆盖** —— 一次 发布把管理员的决定改回默认值,是这类种子最典型也最难发现的伤害。 """ from __future__ import annotations import json import sqlalchemy as sa from alembic import op revision = "636" down_revision = "635" branch_labels = None depends_on = None SETTINGS = "platform_settings" _KEY = "assistant.room_escalate_after_reminders" _VALUE = 3 _DESC = ( "工单群里催办到第几轮还没动就升级:把这条路由的备份负责人(没有就退到 IT " "管理员)加进群,并说清为什么。升级对一个房间只发生一次。" "不是一套新的时间表 —— 用的是 support.ticket_sla_reminder 已有的水位。" ) def _has_table(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 _has_table(bind, SETTINGS): return exists = bind.execute( sa.text(f"SELECT 1 FROM {SETTINGS} WHERE `key` = :k"), {"k": _KEY}, ).scalar() if exists: return bind.execute( sa.text( f"INSERT INTO {SETTINGS} (`key`, `value`, `description`) " "VALUES (:k, :v, :d)", ), {"k": _KEY, "v": json.dumps(_VALUE), "d": _DESC}, ) def downgrade() -> None: bind = op.get_bind() if not _has_table(bind, SETTINGS): return bind.execute( sa.text(f"DELETE FROM {SETTINGS} WHERE `key` = :k"), {"k": _KEY}, )