chore: add legacy binding fallback audit logs
This commit is contained in:
@@ -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'
|
||||
Reference in New Issue
Block a user