chore: add legacy binding fallback audit logs

This commit is contained in:
wren
2026-05-07 17:52:01 +08:00
parent f8eb2dc817
commit e1adcf30d2
3 changed files with 166 additions and 0 deletions
@@ -0,0 +1,73 @@
# 旧规则绑定表下线观察与删表草案
## 当前策略
- 新主链路:`leaudit_rule_group_bindings`
- 旧兼容表:`leaudit_rule_type_bindings`
- 当前仅保留 `UpdateBinding / DeleteBinding` 对历史 `BindingId` 的 fallback
## 已完成
- 文档类型保存不再双写旧表
- `CreateBinding` 不再 fallback 写旧表
- 规则绑定主读链路优先读取新分组绑定
## 观察期要看什么
### 1. 后端日志
当前已经在以下场景增加明确日志:
- `rule binding legacy fallback hit on update`
- `rule binding legacy fallback hit on delete`
如果观察期内不再出现以上日志,说明已经基本没有历史旧 `BindingId` 命中。
### 2. 数据检查
执行:
```bash
psql ... -f scripts/precheck_drop_legacy_rule_type_bindings.sql
```
重点看:
- `doc_types_legacy_only = 0`
- 新链路绑定明细完整
- 旧表活动绑定仅剩历史冗余数据
## 建议收口顺序
1. 保持现状进入观察期
2. 连续一段时间无 fallback 日志
3. 删除 `UpdateBinding / DeleteBinding` 的旧表 fallback
4. 再执行删表 SQL
## 删表前条件
- 前端主链路全部走评查点分组绑定接口
- 不再有外部脚本调用旧 `/api/rule-sets/bindings/{bindingId}` 历史 ID
- 观察期日志为 0
- `doc_types_legacy_only = 0`
## 删表 SQL 草案
```sql
BEGIN;
DROP TABLE IF EXISTS leaudit_rule_type_bindings;
COMMIT;
```
## 回滚草案
如果只是删 fallback 代码但还没删表,直接回滚代码即可。
如果已经删表,必须依赖:
- 事先数据库备份
- 或按历史 DDL 重建表结构后再回灌数据
因此删表动作必须放在数据库备份之后执行。
@@ -3,6 +3,7 @@
from __future__ import annotations
import hashlib
import logging
from fastapi_common.fastapi_common_sqlalchemy.database import GetAsyncSession
from fastapi_common.fastapi_common_web.domain.responses import StatusCodeEnum
from fastapi_common.fastapi_common_web.exception.LeauditException import LeauditException
@@ -21,6 +22,8 @@ from fastapi_modules.fastapi_leaudit.services import IOssService, IRuleService
from fastapi_modules.fastapi_leaudit.services.impl.ossServiceImpl import OssServiceImpl
from fastapi_modules.fastapi_leaudit.services.impl.ruleGroupSupport import sync_doc_type_bindings_from_group
LOGGER = logging.getLogger(__name__)
class RuleServiceImpl(IRuleService):
"""规则服务实现。"""
@@ -756,6 +759,13 @@ class RuleServiceImpl(IRuleService):
if not Row:
raise LeauditException(StatusCodeEnum.HTTP_404_NOT_FOUND, "绑定记录不存在")
LOGGER.warning(
"rule binding legacy fallback hit on update: binding_id=%s doc_type_id=%s rule_set_id=%s",
BindingId,
Row.get("doc_type_id"),
Row.get("rule_set_id"),
)
SetClauses: list[str] = []
Params: dict[str, object] = {"bid": BindingId}
@@ -838,6 +848,11 @@ class RuleServiceImpl(IRuleService):
text("DELETE FROM leaudit_rule_type_bindings WHERE id = :bid"),
{"bid": BindingId},
)
if Result.rowcount:
LOGGER.warning(
"rule binding legacy fallback hit on delete: binding_id=%s",
BindingId,
)
await Session.commit()
if Result.rowcount == 0:
raise LeauditException(StatusCodeEnum.HTTP_404_NOT_FOUND, "绑定记录不存在")
@@ -0,0 +1,78 @@
-- 删除 leaudit_rule_type_bindings 前的检查 SQL(仅检查,不执行删除)
-- 使用方式:
-- psql ... -f scripts/precheck_drop_legacy_rule_type_bindings.sql
\echo '=== 1. 文档类型总量 / 新旧绑定覆盖情况 ==='
WITH doc_types AS (
SELECT id
FROM leaudit_document_types
WHERE deleted_at IS NULL
),
new_bindings AS (
SELECT DISTINCT child.document_type_id
FROM leaudit_evaluation_point_groups child
JOIN leaudit_rule_group_bindings rgb
ON rgb.group_id = child.id
AND rgb.deleted_at IS NULL
AND rgb.is_active = TRUE
WHERE child.deleted_at IS NULL
AND COALESCE(child.pid, 0) <> 0
AND child.document_type_id IS NOT NULL
),
legacy_bindings AS (
SELECT DISTINCT doc_type_id
FROM leaudit_rule_type_bindings
WHERE deleted_at IS NULL
AND is_active = TRUE
)
SELECT
(SELECT COUNT(*) FROM doc_types) AS doc_types_total,
(SELECT COUNT(*) FROM new_bindings) AS doc_types_with_new_binding,
(SELECT COUNT(*) FROM legacy_bindings) AS doc_types_with_legacy_binding,
(
SELECT COUNT(*)
FROM legacy_bindings lb
WHERE NOT EXISTS (
SELECT 1
FROM new_bindings nb
WHERE nb.document_type_id = lb.doc_type_id
)
) AS doc_types_legacy_only;
\echo '=== 2. 仍可能依赖旧表的活动绑定明细 ==='
SELECT
b.id,
b.doc_type_id,
b.doc_type_code,
b.rule_set_id,
b.binding_mode,
b.priority,
b.region,
b.note
FROM leaudit_rule_type_bindings b
WHERE b.deleted_at IS NULL
AND b.is_active = TRUE
ORDER BY b.doc_type_id ASC, b.priority DESC, b.id ASC;
\echo '=== 3. 新链路活动绑定明细 ==='
SELECT
rgb.id AS group_binding_id,
child.id AS child_group_id,
child.document_type_id,
rgb.rule_set_id,
rgb.priority,
rgb.is_active,
rgb.note
FROM leaudit_rule_group_bindings rgb
JOIN leaudit_evaluation_point_groups child
ON child.id = rgb.group_id
WHERE rgb.deleted_at IS NULL
AND rgb.is_active = TRUE
AND child.deleted_at IS NULL
AND COALESCE(child.pid, 0) <> 0
ORDER BY child.document_type_id ASC, child.sort_order ASC, rgb.priority DESC, rgb.id ASC;
\echo '=== 4. 删除前必须人工确认 ==='
\echo '1) 观察期内未再出现 legacy fallback update/delete 日志'
\echo '2) doc_types_legacy_only = 0'
\echo '3) 前端/脚本/外部调用不再使用 /api/rule-sets/bindings 历史 BindingId'