# 问题 修复 文件 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
270 lines
9.2 KiB
Python
270 lines
9.2 KiB
Python
"""PPT/海报管理后台路由"""
|
|
from flask import Blueprint, request, jsonify
|
|
from insurance.admin.ppt_admin_service import PptAdminService
|
|
from insurance.middleware.auth_middleware import permission_required, jwt_required
|
|
|
|
ppt_admin_bp = Blueprint("ppt_admin", __name__)
|
|
ppt_admin_service = PptAdminService()
|
|
|
|
|
|
# ---- 保司管理 ----
|
|
|
|
@ppt_admin_bp.route("/companies", methods=["GET"])
|
|
@jwt_required
|
|
def list_companies():
|
|
params = {
|
|
"page": request.args.get("page", 1, type=int),
|
|
"page_size": request.args.get("page_size", 20, type=int),
|
|
"status": request.args.get("status", type=int),
|
|
"keyword": request.args.get("keyword", ""),
|
|
}
|
|
return jsonify(ppt_admin_service.list_companies(params))
|
|
|
|
|
|
@ppt_admin_bp.route("/companies", methods=["POST"])
|
|
@permission_required("config_manage")
|
|
def create_company():
|
|
return jsonify(ppt_admin_service.create_company(request.get_json(force=True)))
|
|
|
|
|
|
@ppt_admin_bp.route("/companies/<company_id>", methods=["PUT"])
|
|
@permission_required("config_manage")
|
|
def update_company(company_id):
|
|
return jsonify(ppt_admin_service.update_company(company_id, request.get_json(force=True)))
|
|
|
|
|
|
@ppt_admin_bp.route("/companies/<company_id>/status", methods=["PUT"])
|
|
@permission_required("config_manage")
|
|
def update_company_status(company_id):
|
|
return jsonify(ppt_admin_service.update_company_status(company_id, request.get_json(force=True)))
|
|
|
|
|
|
# ---- 产品管理 ----
|
|
|
|
@ppt_admin_bp.route("/products", methods=["GET"])
|
|
@jwt_required
|
|
def list_products():
|
|
params = {
|
|
"page": request.args.get("page", 1, type=int),
|
|
"page_size": request.args.get("page_size", 20, type=int),
|
|
"company_id": request.args.get("company_id", ""),
|
|
"plan_type": request.args.get("plan_type", ""),
|
|
"status": request.args.get("status", type=int),
|
|
}
|
|
return jsonify(ppt_admin_service.list_products(params))
|
|
|
|
|
|
@ppt_admin_bp.route("/products", methods=["POST"])
|
|
@permission_required("config_manage")
|
|
def create_product():
|
|
return jsonify(ppt_admin_service.create_product(request.get_json(force=True)))
|
|
|
|
|
|
@ppt_admin_bp.route("/products/<product_id>", methods=["PUT"])
|
|
@permission_required("config_manage")
|
|
def update_product(product_id):
|
|
return jsonify(ppt_admin_service.update_product(product_id, request.get_json(force=True)))
|
|
|
|
|
|
@ppt_admin_bp.route("/products/<product_id>/status", methods=["PUT"])
|
|
@permission_required("config_manage")
|
|
def update_product_status(product_id):
|
|
return jsonify(ppt_admin_service.update_product_status(product_id, request.get_json(force=True)))
|
|
|
|
|
|
@ppt_admin_bp.route("/products/<product_id>/manual/upload", methods=["POST"])
|
|
@permission_required("config_manage")
|
|
def upload_manual(product_id):
|
|
file = request.files.get("file")
|
|
if not file:
|
|
return jsonify({"code": 1001, "message": "请上传文件", "data": None})
|
|
return jsonify(ppt_admin_service.upload_manual(product_id, file))
|
|
|
|
|
|
@ppt_admin_bp.route("/products/<product_id>/manual/parse", methods=["POST"])
|
|
@permission_required("config_manage")
|
|
def parse_manual(product_id):
|
|
return jsonify(ppt_admin_service.parse_manual(product_id))
|
|
|
|
|
|
@ppt_admin_bp.route("/products/<product_id>/manual/review", methods=["PUT"])
|
|
@permission_required("config_manage")
|
|
def review_manual(product_id):
|
|
return jsonify(ppt_admin_service.review_manual(product_id, request.get_json(force=True)))
|
|
|
|
|
|
# ---- PPT 模板管理 ----
|
|
|
|
@ppt_admin_bp.route("/templates", methods=["GET"])
|
|
@jwt_required
|
|
def list_templates():
|
|
params = {
|
|
"page": request.args.get("page", 1, type=int),
|
|
"page_size": request.args.get("page_size", 20, type=int),
|
|
"plan_type": request.args.get("plan_type", ""),
|
|
"status": request.args.get("status", type=int),
|
|
}
|
|
return jsonify(ppt_admin_service.list_templates(params))
|
|
|
|
|
|
@ppt_admin_bp.route("/templates", methods=["POST"])
|
|
@permission_required("config_manage")
|
|
def create_template():
|
|
return jsonify(ppt_admin_service.create_template(request.get_json(force=True)))
|
|
|
|
|
|
@ppt_admin_bp.route("/templates/<template_id>", methods=["PUT"])
|
|
@permission_required("config_manage")
|
|
def update_template(template_id):
|
|
return jsonify(ppt_admin_service.update_template(template_id, request.get_json(force=True)))
|
|
|
|
|
|
@ppt_admin_bp.route("/templates/<template_id>/status", methods=["PUT"])
|
|
@permission_required("config_manage")
|
|
def update_template_status(template_id):
|
|
return jsonify(ppt_admin_service.update_template_status(template_id, request.get_json(force=True)))
|
|
|
|
|
|
# ---- 海报模板管理 ----
|
|
|
|
@ppt_admin_bp.route("/poster-templates", methods=["GET"])
|
|
@jwt_required
|
|
def list_poster_templates():
|
|
params = {
|
|
"page": request.args.get("page", 1, type=int),
|
|
"page_size": request.args.get("page_size", 20, type=int),
|
|
"status": request.args.get("status", type=int),
|
|
}
|
|
return jsonify(ppt_admin_service.list_poster_templates(params))
|
|
|
|
|
|
@ppt_admin_bp.route("/poster-templates", methods=["POST"])
|
|
@permission_required("config_manage")
|
|
def create_poster_template():
|
|
return jsonify(ppt_admin_service.create_poster_template(request.get_json(force=True)))
|
|
|
|
|
|
@ppt_admin_bp.route("/poster-templates/<int:template_id>", methods=["PUT"])
|
|
@permission_required("config_manage")
|
|
def update_poster_template(template_id):
|
|
return jsonify(ppt_admin_service.update_poster_template(template_id, request.get_json(force=True)))
|
|
|
|
|
|
@ppt_admin_bp.route("/poster-templates/<int:template_id>/status", methods=["PUT"])
|
|
@permission_required("config_manage")
|
|
def update_poster_template_status(template_id):
|
|
return jsonify(ppt_admin_service.update_poster_template_status(template_id, request.get_json(force=True)))
|
|
|
|
|
|
# ---- 文案模板管理 ----
|
|
|
|
@ppt_admin_bp.route("/copy-templates", methods=["GET"])
|
|
@jwt_required
|
|
def list_copy_templates():
|
|
params = {
|
|
"page": request.args.get("page", 1, type=int),
|
|
"page_size": request.args.get("page_size", 20, type=int),
|
|
"status": request.args.get("status", type=int),
|
|
}
|
|
return jsonify(ppt_admin_service.list_copy_templates(params))
|
|
|
|
|
|
@ppt_admin_bp.route("/copy-templates", methods=["POST"])
|
|
@permission_required("config_manage")
|
|
def create_copy_template():
|
|
return jsonify(ppt_admin_service.create_copy_template(request.get_json(force=True)))
|
|
|
|
|
|
@ppt_admin_bp.route("/copy-templates/<int:template_id>", methods=["PUT"])
|
|
@permission_required("config_manage")
|
|
def update_copy_template(template_id):
|
|
return jsonify(ppt_admin_service.update_copy_template(template_id, request.get_json(force=True)))
|
|
|
|
|
|
@ppt_admin_bp.route("/copy-templates/<int:template_id>", methods=["DELETE"])
|
|
@permission_required("config_manage")
|
|
def delete_copy_template(template_id):
|
|
return jsonify(ppt_admin_service.delete_copy_template(template_id))
|
|
|
|
|
|
# ---- 历史记录管理(需要审计权限) ----
|
|
|
|
@ppt_admin_bp.route("/history", methods=["GET"])
|
|
@permission_required("audit_view")
|
|
def list_history():
|
|
params = {
|
|
"page": request.args.get("page", 1, type=int),
|
|
"page_size": request.args.get("page_size", 20, type=int),
|
|
"user_id": request.args.get("user_id", ""),
|
|
"company_id": request.args.get("company_id", ""),
|
|
"action_type": request.args.get("action_type", ""),
|
|
}
|
|
return jsonify(ppt_admin_service.list_history(params))
|
|
|
|
|
|
@ppt_admin_bp.route("/history/<int:history_id>", methods=["DELETE"])
|
|
@permission_required("config_manage")
|
|
def delete_history(history_id):
|
|
return jsonify(ppt_admin_service.delete_history(history_id))
|
|
|
|
|
|
@ppt_admin_bp.route("/history/export", methods=["GET"])
|
|
@permission_required("audit_view")
|
|
def export_history():
|
|
from flask import Response
|
|
params = {
|
|
"user_id": request.args.get("user_id", ""),
|
|
"company_id": request.args.get("company_id", ""),
|
|
"action_type": request.args.get("action_type", ""),
|
|
}
|
|
csv_content = ppt_admin_service.export_history_csv(params)
|
|
return Response(
|
|
csv_content,
|
|
mimetype="text/csv",
|
|
headers={"Content-Disposition": "attachment; filename=ppt_history.csv"},
|
|
)
|
|
|
|
|
|
# ---- 系统配置 ----
|
|
|
|
@ppt_admin_bp.route("/available-models", methods=["GET"])
|
|
@permission_required("config_manage")
|
|
def get_available_models():
|
|
return jsonify(ppt_admin_service.get_available_models())
|
|
|
|
|
|
@ppt_admin_bp.route("/settings", methods=["GET"])
|
|
@permission_required("config_manage")
|
|
def get_settings():
|
|
return jsonify(ppt_admin_service.get_settings())
|
|
|
|
|
|
@ppt_admin_bp.route("/settings", methods=["PUT"])
|
|
@permission_required("config_manage")
|
|
def update_settings():
|
|
updated_by = getattr(request, "user_id", "")
|
|
return jsonify(ppt_admin_service.update_settings(request.get_json(force=True), updated_by))
|
|
|
|
|
|
@ppt_admin_bp.route("/sync-models", methods=["POST"])
|
|
@permission_required("config_manage")
|
|
def sync_models():
|
|
data = request.get_json(force=True)
|
|
return jsonify(ppt_admin_service.sync_models(
|
|
provider=data.get("provider", ""),
|
|
api_key=data.get("api_key", ""),
|
|
base_url=data.get("base_url", ""),
|
|
))
|
|
|
|
|
|
# ---- 数据清理 ----
|
|
|
|
@ppt_admin_bp.route("/cleanup", methods=["POST"])
|
|
@permission_required("config_manage")
|
|
def run_cleanup():
|
|
"""手动触发过期数据清理。"""
|
|
from insurance.utils.cleanup import cleanup_expired_data
|
|
dry_run = request.args.get("dry_run", "false").lower() == "true"
|
|
result = cleanup_expired_data(dry_run=dry_run)
|
|
return jsonify({"code": 0, "data": result})
|