baodan/api/insurance/routes.py

162 lines
6.5 KiB
Python
Raw Normal View History

"""Insurance Blueprint 路由注册
在基础平台的 main.py 中添加以下代码即可挂载所有自研路由
from insurance.routes import register_insurance_routes
register_insurance_routes(app)
"""
import os
from flask import Flask
def register_insurance_routes(app: Flask):
"""注册所有 insurance 模块的 Blueprint 到基础平台主应用。"""
from insurance.auth.routes import auth_bp
from insurance.chat.routes import chat_bp
from insurance.chat.agent_routes import agent_bp
from insurance.recommend.routes import recommend_bp
from insurance.kb.routes import kb_bp
from insurance.kb.retrieval_routes import retrieval_bp
from insurance.admin.routes import admin_bp
from insurance.stats.routes import stats_bp
from insurance.wecom.routes import wecom_bp
2026-07-23 13:10:50 +08:00
from insurance.ppt.routes import ppt_bp
2026-07-23 15:04:16 +08:00
from insurance.admin.ppt_admin_routes import ppt_admin_bp
from insurance.poster.routes import poster_bp
from insurance.generation.routes import workspace_bp
前工程开发已经推进到 Phase 6 基础能力,但正式验收还没有完成。更准确地说:Phase 0~5 的主要代码链路已经落地,Phase 6 完成了治理框架,尚缺生产化和真实数据验收。 阶段 当前状态 说明 Phase 0~3 基本完成 Document IR、证据链、人工确认、不可变 PlanData Snapshot、海报/PPT 投影已实现 Phase 4 代码完成 场景、策略、模板版本,校验/发布门禁,确定性 resolver 和严格槽位合并已实现 Phase 5 代码完成 输入冻结、PPTX/海报对账、失败关闭、幂等、心跳、重试和冻结输入重放已实现 Phase 6 基础完成 质量看板、结构化告警、Golden 审批、留存清理、孤儿检查和灰度开关已实现 正式上线 未完成 缺真实样本、生产模板、业务规则签字和灰度观察 目前验证基线: PPT/海报专项测试:107 passed, 1 skipped Vue TypeScript 检查:通过 前端生产构建:通过 代码变更仍在工作区,尚未提交 仓库全量测试仍有既有失败/挂起项,暂时不能宣称全仓测试完全绿色 仍未完成的代码任务主要有: 影子解析差异流水线 目前有灰度开关,但还没有完整的“新旧解析同时运行、字段差异入库、按保司/profile 聚合”的影子比较任务。 自动视觉回归 目前实现的是 DOM 模块、溢出、尺寸、文本和数值检查;还缺基于真实模板和标准图片的像素差异、字体缺失、遮挡和裁切回归。 告警通道接入 后台已经能产生结构化质量告警,但尚未自动推送到邮件、企微或其他通知通道。 留存任务生产化 dry-run、实删服务和失败审计已经具备,但尚未接入周期性 Celery/定时任务,也没有自动重试失败清理批次。 旧链路最终下线 旧 PPT 解析器和海报紧凑解析仍保留为回滚路径。需要全量灰度稳定后才能删除或彻底关闭写入口。 全仓测试收口 需要处理现有无关失败和挂起测试,建立真正全绿的 CI 基线。 仍需外部输入和生产环境完成的事项: 至少 30 份脱敏 Golden PDF,并完成双人标注和精确率验收。 业务专家确认派生公式、缺失值、可比较性和结论策略。 上传并标注真实生产 PPTX 的语义 shape、页面类型和容量。 安装生产字体并建立视觉基准图片。 实际执行数据库迁移 038~040。 完成留存 dry-run、实删演练以及 10% → 30% → 100% 灰度。 观察期通过后开启 SCENARIO_ENGINE_V2,目前它仍默认关闭;真实清理开关也默认关闭。 完整状态记录在 [PPT与海报Phase4至6补充实施记录](D:/work/code/python/coding/baodanagent/docs/PPT与海报Phase4至6补充实施记录_20260802.md)。
2026-08-02 16:34:06 +08:00
from insurance.document.routes import document_bp
from insurance.plan_data.routes import plan_snapshot_bp
# 使用 /insurance/ 前缀避免与 Dify 的 /api/ 路由冲突
app.register_blueprint(auth_bp, url_prefix="/insurance/auth")
app.register_blueprint(chat_bp, url_prefix="/insurance/chat")
app.register_blueprint(agent_bp, url_prefix="/insurance/agents")
app.register_blueprint(recommend_bp, url_prefix="/insurance/recommend")
app.register_blueprint(kb_bp, url_prefix="/insurance/kb")
app.register_blueprint(retrieval_bp, url_prefix="/insurance/retrieval")
app.register_blueprint(admin_bp, url_prefix="/insurance/admin")
app.register_blueprint(stats_bp, url_prefix="/insurance/stats")
app.register_blueprint(wecom_bp, url_prefix="/insurance/wecom")
2026-07-23 13:10:50 +08:00
app.register_blueprint(ppt_bp, url_prefix="/insurance/ppt")
2026-07-23 15:04:16 +08:00
app.register_blueprint(ppt_admin_bp, url_prefix="/insurance/admin/ppt")
app.register_blueprint(poster_bp, url_prefix="/insurance/poster")
app.register_blueprint(workspace_bp, url_prefix="/insurance/workspace")
前工程开发已经推进到 Phase 6 基础能力,但正式验收还没有完成。更准确地说:Phase 0~5 的主要代码链路已经落地,Phase 6 完成了治理框架,尚缺生产化和真实数据验收。 阶段 当前状态 说明 Phase 0~3 基本完成 Document IR、证据链、人工确认、不可变 PlanData Snapshot、海报/PPT 投影已实现 Phase 4 代码完成 场景、策略、模板版本,校验/发布门禁,确定性 resolver 和严格槽位合并已实现 Phase 5 代码完成 输入冻结、PPTX/海报对账、失败关闭、幂等、心跳、重试和冻结输入重放已实现 Phase 6 基础完成 质量看板、结构化告警、Golden 审批、留存清理、孤儿检查和灰度开关已实现 正式上线 未完成 缺真实样本、生产模板、业务规则签字和灰度观察 目前验证基线: PPT/海报专项测试:107 passed, 1 skipped Vue TypeScript 检查:通过 前端生产构建:通过 代码变更仍在工作区,尚未提交 仓库全量测试仍有既有失败/挂起项,暂时不能宣称全仓测试完全绿色 仍未完成的代码任务主要有: 影子解析差异流水线 目前有灰度开关,但还没有完整的“新旧解析同时运行、字段差异入库、按保司/profile 聚合”的影子比较任务。 自动视觉回归 目前实现的是 DOM 模块、溢出、尺寸、文本和数值检查;还缺基于真实模板和标准图片的像素差异、字体缺失、遮挡和裁切回归。 告警通道接入 后台已经能产生结构化质量告警,但尚未自动推送到邮件、企微或其他通知通道。 留存任务生产化 dry-run、实删服务和失败审计已经具备,但尚未接入周期性 Celery/定时任务,也没有自动重试失败清理批次。 旧链路最终下线 旧 PPT 解析器和海报紧凑解析仍保留为回滚路径。需要全量灰度稳定后才能删除或彻底关闭写入口。 全仓测试收口 需要处理现有无关失败和挂起测试,建立真正全绿的 CI 基线。 仍需外部输入和生产环境完成的事项: 至少 30 份脱敏 Golden PDF,并完成双人标注和精确率验收。 业务专家确认派生公式、缺失值、可比较性和结论策略。 上传并标注真实生产 PPTX 的语义 shape、页面类型和容量。 安装生产字体并建立视觉基准图片。 实际执行数据库迁移 038~040。 完成留存 dry-run、实删演练以及 10% → 30% → 100% 灰度。 观察期通过后开启 SCENARIO_ENGINE_V2,目前它仍默认关闭;真实清理开关也默认关闭。 完整状态记录在 [PPT与海报Phase4至6补充实施记录](D:/work/code/python/coding/baodanagent/docs/PPT与海报Phase4至6补充实施记录_20260802.md)。
2026-08-02 16:34:06 +08:00
app.register_blueprint(document_bp, url_prefix="/insurance/documents")
app.register_blueprint(plan_snapshot_bp, url_prefix="/insurance/plan-snapshots")
# 注册 Celery 自研任务(不修改 BaoDan Celery 基座)
_register_celery_tasks(app)
# 恢复过期任务(启动时清理无心跳的 running/queued 任务)
_recover_stale_tasks(app)
# 注册全局异常处理器
from insurance.utils.error_handler import register_error_handlers
register_error_handlers(app)
# 注册增强健康检查端点
_register_health_checks(app)
def _register_celery_tasks(app: Flask):
"""将自研 Celery 任务注册到 BaoDan 的 Celery 实例。
不修改 BaoDan ext_celery.py通过动态添加 imports 实现
"""
try:
celery_app = app.extensions.get("celery")
if celery_app:
# 将自研任务模块添加到 Celery imports
insurance_tasks = [
"insurance.generation.celery_tasks",
]
current_imports = list(celery_app.conf.get("imports", []) or [])
for task_module in insurance_tasks:
if task_module not in current_imports:
current_imports.append(task_module)
celery_app.conf.update(imports=current_imports)
app.logger.info(f"已注册自研 Celery 任务: {insurance_tasks}")
else:
app.logger.debug("Celery 未初始化,跳过自研任务注册")
except Exception as e:
app.logger.warning(f"注册自研 Celery 任务失败: {e}")
def _recover_stale_tasks(app: Flask):
"""启动时恢复过期任务。"""
try:
with app.app_context():
from insurance.generation.task_service import recover_stale_tasks
recover_stale_tasks()
from insurance.generation.celery_tasks import recover_stale_manual_tasks
recover_stale_manual_tasks()
from insurance.poster.tasks import recover_stale_tasks as recover_stale_poster_tasks
recover_stale_poster_tasks()
except Exception as e:
app.logger.warning(f"恢复过期任务失败: {e}")
def _register_health_checks(app: Flask):
"""注册增强健康检查端点。"""
from flask import jsonify
@app.route("/insurance/health/ready", methods=["GET"])
def health_ready():
"""就绪检查DB、Redis、storage 可写、必要表字段。"""
checks = {}
# DB 检查
try:
from insurance.db.compat import db
from sqlalchemy import text
db.session.execute(text("SELECT 1"))
checks["database"] = "ok"
except Exception as e:
checks["database"] = f"error: {e}"
# Redis 检查
try:
from insurance.db.compat import redis_client
if redis_client:
redis_client.ping()
checks["redis"] = "ok"
else:
checks["redis"] = "not_configured"
except Exception as e:
checks["redis"] = f"error: {e}"
# Storage 可写检查
try:
from flask import current_app
upload_dir = current_app.config.get("UPLOAD_FOLDER", "uploads")
os.makedirs(upload_dir, exist_ok=True)
test_file = os.path.join(upload_dir, ".health_check")
with open(test_file, "w") as f:
f.write("ok")
os.remove(test_file)
checks["storage"] = "ok"
except Exception as e:
checks["storage"] = f"error: {e}"
# 关键表检查
try:
from insurance.db.compat import db
from sqlalchemy import text
tables = ["insurance_ppt_templates", "poster_records", "system_settings"]
missing = []
for t in tables:
try:
db.session.execute(text(f"SELECT 1 FROM {t} LIMIT 0"))
except Exception:
missing.append(t)
if missing:
checks["tables"] = f"missing: {', '.join(missing)}"
else:
checks["tables"] = "ok"
except Exception as e:
checks["tables"] = f"error: {e}"
all_ok = all(v == "ok" or v == "not_configured" for v in checks.values())
status_code = 200 if all_ok else 503
return jsonify({"status": "ready" if all_ok else "degraded", "checks": checks}), status_code