feat(ws2): ADR-093 路由統一 — BIGINT + NotificationMatrix + feature flag

## 修復

### T2.1 BigInteger overflow 修復
- `db/models.py`: telegram_chat_id Integer → BigInteger
  (原 int32 無法容納群組 ID -1003711974679)

### T2.2 移除 CAST workaround
- `approval_db.py:739`: 移除 CAST(:telegram_chat_id AS BIGINT)
  ORM 已正確使用 BigInteger,workaround 可退役

### T2.3 Redis key 一致性修復
- `heartbeat_report_service.py:575`: telegram:polling_leader → telegram:polling:leader
  (telegram_gateway.py 使用冒號分隔,heartbeat 用底線是 bug)

## 新增

### T2.4 notification_matrix.py
- `services/notification_matrix.py`: ADR-093 路由矩陣
  - Destination(DM/GROUP/BOTH) + RoutingRule dataclass
  - NOTIFICATION_ROUTING dict(TYPE-1 ~ TYPE-8M 完整映射)
  - resolve_chat_ids(type, dm, group, *, tg_group_cutover=False) 灰階切流 API

### T2.5 telegram_gateway.py feature flag 保護
- line 43: 加 notification_matrix import
- line 1827-1834: TG_GROUP_CUTOVER=False 時維持舊行為
  TG_GROUP_CUTOVER=True 時解除 _interactive_types 黑名單,由矩陣控制

### T2.6 Migration SQL
- `migrations/adr093_notification_routing.sql`:
  - CREATE TABLE approval_records (telegram_chat_id BIGINT)
  - CREATE ROLE awoooi_migrator (IF NOT EXISTS)
  - 含舊環境 ALTER COLUMN int→bigint 保護

## 測試同步
- `tests/integration/setup_test_schema.sql`: telegram_chat_id BIGINT

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-04-25 01:45:16 +08:00
parent 054d0ae422
commit 6d5fd3c124
8 changed files with 176 additions and 8 deletions

View File

@@ -727,8 +727,6 @@ class ApprovalDBService:
以 incident_id 查找最新 PENDING approval record 並回填。
"""
async with get_db_context() as db:
# 2026-04-10 Claude Sonnet 4.6: 用 raw SQL 避免 SQLAlchemy 推斷 INTEGER
# telegram_chat_id 為 BIGINTORM update() 會誤用 $N::INTEGER 導致 int32 overflow
from sqlalchemy import text as _text
params: dict = {
"incident_id": incident_id,
@@ -738,7 +736,7 @@ class ApprovalDBService:
chat_clause = ""
if telegram_chat_id is not None:
params["telegram_chat_id"] = telegram_chat_id
chat_clause = ", telegram_chat_id = CAST(:telegram_chat_id AS BIGINT)"
chat_clause = ", telegram_chat_id = :telegram_chat_id"
await db.execute(
_text(f"""
UPDATE approval_records