merge: 同步 origin/qyqy_develop(风控扫描、知识检索、客服 Agent、协商等)
冲突处理:均为双方各自新增,按并集保留—— - .gitignore:本地 data/logs 忽略项 + 远端 .dsh-drop/ - app/core/config.py:offsite/promotion 与 risk_scan 配置项并存 - app/service/agent/bootstrap.py:场外/推介/风控/NL2SQL/知识检索 工具与 Agent 全部注册 - app/worker/__main__.py:场外邮件 Worker 接线 + runtime 关系服务/投影清理注入并存 收尾:新增 20260911_merge_risk_heads 收敛迁移双 head;按 docs/21 生成 config/jwt/dev 开发密钥。
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
"""合并远程分支带来的迁移分叉。
|
||||
|
||||
背景:同步 `origin/qyqy_develop` 后,远端新增的 `20260911_risk_rule_index`
|
||||
与本地已收敛的 `20260911_merge_review_separation` 形成两个 head。两条链都挂在
|
||||
`20260910_drop_review_separation` 之后,互不依赖,因此用一个空的合并 revision
|
||||
把图收敛为单 head。
|
||||
|
||||
本迁移不改任何表结构:`upgrade`/`downgrade` 都是空操作。
|
||||
"""
|
||||
|
||||
revision = "20260911_merge_risk_heads"
|
||||
down_revision = ("20260911_merge_review_separation", "20260911_risk_rule_index")
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""空迁移:只合并分支头,不做结构变更。"""
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""回滚只是取消合并标记,两条分支的迁移本身不受影响。"""
|
||||
@@ -0,0 +1,80 @@
|
||||
"""给 `fin_risk_alert.trigger_rule_codes` 补 JSON 多值索引(docs/25 P3 #21)。
|
||||
|
||||
修订原因:规则命中查询走的是 `JSON_CONTAINS(trigger_rule_codes, '"RW-018"')`
|
||||
(`risk_repository.py` 的 `contains`),而 `docs/25` 第 21 条实测该表**没有**任何可用于
|
||||
该表达式的索引 —— `information_schema.STATISTICS` 里 `fin_risk_alert` 只有 11 个普通
|
||||
BTREE 索引(`status`、`customer_id`、`created_at` 等)+ 主键 + `alert_no` 唯一键,
|
||||
`EXPLAIN` 显示 `type=ALL`、`possible_keys=NULL`,即每次按规则码筛选都是全表扫描。
|
||||
|
||||
为什么用**多值索引**而不是普通索引:`trigger_rule_codes` 是 `json` 列,存的是数组
|
||||
(实测取值形如 `["RW-007", "RW-002", "RW-012"]`)。MySQL 8.0.17 起支持在 JSON 数组上
|
||||
建多值索引,且优化器在 `JSON_CONTAINS` / `MEMBER OF` / `JSON_OVERLAPS` 上可以使用它 ——
|
||||
这正是本条要解决的那个谓词。当前实例版本 8.0.27,满足条件。
|
||||
|
||||
`CAST(... AS CHAR(16) ARRAY)` 中的 16:规则码是 `RW-###`(5 字符),留足余量而不至于让
|
||||
索引项过大。**注意**这不是"截断匹配"—— 多值索引要求列上所有取值都能装进声明长度,
|
||||
否则 `ALTER` 会直接失败(报 3903),而不是悄悄少索引几行。
|
||||
|
||||
前置条件已在 `tools/probe_risk_index.py` 里验证并留档
|
||||
(`docs/evidence/risk-index-probe.json`):3 行数据全部是 JSON 数组,
|
||||
`non_array_rows = 0`。
|
||||
|
||||
**本条只解决一半**:另一处索引失效是若干 `like(f"%{keyword}%")` 全表扫 —— 前后都有
|
||||
通配符的模糊匹配在 B-tree 上无法索引,唯一出路是全文索引(中文需要分词组件,属于部署
|
||||
依赖)。因此这里**不**假装解决它,只把它记为已知限制,见迁移提交说明与 `docs/25`。
|
||||
|
||||
基线合规(`AGENTS.md` 第 2/3/4 条):本迁移只**新增一个索引**,不建表、不加列、不改列,
|
||||
不重命名、不删除任何已有表或字段,也不改变任何已有字段的类型、可空性与业务含义。
|
||||
`docs/00-新数据库基线设计.md` 未修改。
|
||||
|
||||
幂等性:先查 `information_schema.STATISTICS` 再决定是否 `ALTER`,重复执行不会报 1061。
|
||||
`downgrade` 与之对称,回退后结构与迁移前完全一致。
|
||||
"""
|
||||
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.engine import Connection
|
||||
|
||||
from alembic import op
|
||||
|
||||
# revision 名必须 ≤ 32 字符:`alembic_version.version_num` 是 VARCHAR(32),
|
||||
# 超长会在写版本号时报 1406 Data too long —— 而 DDL 是非事务的,那时索引已经建好了。
|
||||
revision = "20260911_risk_rule_index"
|
||||
down_revision = "20260910_drop_review_separation"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
TABLE = "fin_risk_alert"
|
||||
INDEX = "idx_fin_risk_alert_trigger_rule_codes"
|
||||
EXPRESSION = "CAST(`trigger_rule_codes` AS CHAR(16) ARRAY)"
|
||||
|
||||
|
||||
def _index_exists(bind: Connection) -> bool:
|
||||
found = bind.execute(
|
||||
text(
|
||||
"""
|
||||
SELECT 1
|
||||
FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = :table
|
||||
AND INDEX_NAME = :name
|
||||
LIMIT 1
|
||||
"""
|
||||
),
|
||||
{"table": TABLE, "name": INDEX},
|
||||
).first()
|
||||
return found is not None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
if _index_exists(bind):
|
||||
return
|
||||
# 表名/索引名/表达式都是本模块常量,不含外部输入。
|
||||
op.execute(f"ALTER TABLE `{TABLE}` ADD INDEX `{INDEX}` (({EXPRESSION}))")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
if not _index_exists(bind):
|
||||
return
|
||||
op.execute(f"ALTER TABLE `{TABLE}` DROP INDEX `{INDEX}`")
|
||||
Reference in New Issue
Block a user