baodan/api/insurance/admin/ppt_admin_routes.py
wsb1224 e3479f0546 上线阻断问题全部修复
#	问题	修复	文件
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
2026-07-27 13:52:09 +08:00

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})