feat: Phase2影响分析 — EXPLAIN预检+大表检测(简化版)+执行策略+风险评估table_sizes支持 (1141测试)

新增:
- explain_preview.py: EXPLAIN预检服务(MySQL/PG), 影响行数预估, 全表扫描检测
- ticket.executor: 三种执行策略(stop_on_error/continue/transaction)
- risk_evaluator: table_sizes参数, 大表ALTER/UPDATE/DELETE自动升级high
- test_phase2_features.py: 22个测试(EXPLAIN+执行策略+大表检测)

简化:
- 砍掉CTX-001上下文感知规则(复杂二次扫描), 合并大表检测到risk_evaluator
- 审核引擎保持纯静态分析, 不再导入_RULE_REGISTRY做二次扫描

模型:
- Ticket加exec_strategy字段
- TicketSqlItem模型
- schema加sql_type/exec_strategy/risk_level/workflow_id
This commit is contained in:
logan committed 2026-05-25 11:02:51 +08:00
1 parent 7be0bf1a7c
commit f6f9142ce8
7 files changed
+574 -22

No files matched your search

+22 -1
View File
@@ -498,10 +498,25 @@ async def create_ticket(
# 3. 验证工单内容安全性
_validate_ticket_content(data)
# 3.5 EXPLAIN 预检(大表检测合并到 risk_evaluator,不再单独查表大小)
table_sizes = {}
explain_warnings = []
try:
from app.services.explain_preview import run_explain_preview
import asyncio as _asyncio
explain_result = await _asyncio.wait_for(
run_explain_preview(data.sql_content, instance, data.target_database),
timeout=8.0,
)
explain_warnings = explain_result.warnings
table_sizes = explain_result.table_sizes or {}
except Exception as e:
import asyncio as _asyncio
logger.warning("EXPLAIN 预检失败(非致命): %s", e)
# 4. 自动审核 SQL(带超时保护)
env = "production" if instance.env == "production" else "development"
v2_engine = SQLAuditEngineV2(db_session=db, env=env)
import asyncio as _asyncio
try:
v2_report = await _asyncio.wait_for(
v2_engine.audit(data.sql_content, db_type=data.db_type, instance_id=data.instance_id),
@@ -525,8 +540,13 @@ async def create_ticket(
sql_type=data.sql_type or "",
audit_findings=audit_findings_raw,
statement_count=len(stmt_list),
table_sizes=table_sizes,
)
# EXPLAIN 结果可能升级风险等级
if explain_warnings and risk_level == "low":
risk_level = "medium"
# 6. 匹配审批流 — 优先按 instance.workflow_id,其次按 risk_level 匹配默认流
workflow = None
if instance.workflow_id:
@@ -560,6 +580,7 @@ async def create_ticket(
priority=data.priority,
risk_level=risk_level,
workflow_id=workflow.id if workflow else None,
exec_strategy=data.exec_strategy or "stop_on_error",
applicant_id=current_user.id,
status=initial_status,
audit_result=json.dumps(v2_report.to_dict(), ensure_ascii=False) if v2_report and v2_report.findings else None,