# 问题 修复 文件 1 前端构建失败(引号错误) size="small type=" → size="small" type=" PosterHistoryPage.vue 2 migrate_014 ORM vs 缺失列 全部改为原始 SQL,不再引用 ORM 模型 migrate_014.py 3 cleanup 字段名错误 output_path → ppt_path cleanup.py 4 文案生成 case 越权 添加 case.user_id != user_id 校验 poster/service.py 5 存储路径未接通持久化卷 全部改用 get_storage_root()(默认 /app/api/storage/insurance) config.py, ppt/routes.py, poster/service.py, poster/tasks.py 高风险问题修复 # 问题 修复 文件 6 migrate_019 rollback 撤销成功字段 每个 ALTER 后立即 commit,失败只回滚当前语句 migrate_019.py 7 迁移锁 Windows 不兼容 + 句柄未持久化 全局变量保存锁句柄,支持 Windows msvcrt api/insurance/db/__init__.py 8 PDF 校验异常时放行 异常返回 False(文件损坏) security.py 9 健康检查始终返回成功 缺少关键资源时返回 503 + missing 列表 poster/routes.py 10 短密钥掩码泄露原值 ≤4 字符返回 **** ppt_admin_service.py 11 设置无键名白名单 添加 _ALLOWED_SETTING_KEYS 白名单 ppt_admin_service.py 12 容器重启任务永久 stuck 添加 recover_stale_tasks() 启动恢复函数 poster/tasks.py, ppt/parse_worker.py
112 lines
4.3 KiB
Python
112 lines
4.3 KiB
Python
"""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
|
||
from insurance.ppt.routes import ppt_bp
|
||
from insurance.admin.ppt_admin_routes import ppt_admin_bp
|
||
from insurance.poster.routes import poster_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")
|
||
app.register_blueprint(ppt_bp, url_prefix="/insurance/ppt")
|
||
app.register_blueprint(ppt_admin_bp, url_prefix="/insurance/admin/ppt")
|
||
app.register_blueprint(poster_bp, url_prefix="/insurance/poster")
|
||
|
||
# 注册全局异常处理器
|
||
from insurance.utils.error_handler import register_error_handlers
|
||
register_error_handlers(app)
|
||
|
||
# 注册增强健康检查端点
|
||
_register_health_checks(app)
|
||
|
||
|
||
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
|