baodan/api/insurance/poster/routes.py

343 lines
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""海报功能路由。"""
import os
from flask import Blueprint, request, jsonify, send_file
from insurance.middleware.auth_middleware import jwt_required
from insurance.utils.response import success, error, ErrorCode
poster_bp = Blueprint("poster", __name__)
poster_service = None
def _get_service():
global poster_service
if poster_service is None:
from insurance.poster.service import PosterService
poster_service = PosterService()
return poster_service
def _user_manual_upload_enabled() -> bool:
from insurance.config import get_config
return get_config("POSTER_USER_MANUAL_UPLOAD_ENABLED", "true").strip().lower() in (
"1", "true", "yes", "on",
)
# ─── 健康检查 ─────────────────────────────────────────────
@poster_bp.route("/health", methods=["GET"])
def health():
"""海报模块健康检查reviewed 产品、模板、文案模板、模型配置。"""
from flask import jsonify
checks = {}
critical_missing = []
try:
from insurance.models.ppt_config import PptProduct
count = PptProduct.query.filter(
PptProduct.manual_parse_status == "reviewed",
PptProduct.status == 1,
).count()
checks["reviewed_products"] = count
if count == 0:
critical_missing.append("reviewed_products")
except Exception as e:
checks["reviewed_products"] = f"error: {e}"
critical_missing.append("reviewed_products")
try:
from insurance.models.poster_template_model import PosterTemplate
count = PosterTemplate.query.filter_by(status=1).count()
checks["poster_templates"] = count
if count == 0:
critical_missing.append("poster_templates")
except Exception as e:
checks["poster_templates"] = f"error: {e}"
critical_missing.append("poster_templates")
try:
from insurance.models.poster_copy_template import PosterCopyTemplate
count = PosterCopyTemplate.query.filter_by(status=1).count()
checks["copy_templates"] = count
if count == 0:
critical_missing.append("copy_templates")
except Exception as e:
checks["copy_templates"] = f"error: {e}"
critical_missing.append("copy_templates")
try:
from insurance.models.system_setting import SystemSetting
keys = ["poster_image_provider", "poster_image_api_key"]
settings = {s.key: s.value for s in SystemSetting.query.filter(SystemSetting.key.in_(keys)).all()}
checks["image_model_configured"] = bool(settings.get("poster_image_api_key"))
except Exception as e:
checks["image_model_configured"] = f"error: {e}"
if critical_missing:
checks["status"] = "degraded"
checks["missing"] = critical_missing
return jsonify(checks), 503
checks["status"] = "ok"
return success(checks)
# ─── 产品列表 ─────────────────────────────────────────────
@poster_bp.route("/products", methods=["GET"])
@jwt_required
def list_products():
"""获取已 reviewed 的产品列表。"""
return jsonify(_get_service().get_reviewed_products())
@poster_bp.route("/product-sources", methods=["GET"])
@jwt_required
def list_product_sources():
"""获取公共产品和当前用户已确认的私有产品资料。"""
user_id = str(getattr(request, "user_id", "guest"))
from insurance.poster.product_material_service import ProductMaterialService
manual_enabled = _user_manual_upload_enabled()
library_result = _get_service().get_reviewed_products()
return jsonify({
"code": 0,
"message": "success",
"data": {
"libraryProducts": library_result.get("data", []),
"myMaterials": (
ProductMaterialService().list_available(user_id)
if manual_enabled else []
),
"userManualUploadEnabled": manual_enabled,
},
})
# ─── 用户产品小册子 ───────────────────────────────────────
@poster_bp.route("/product-materials", methods=["GET"])
@jwt_required
def list_product_materials():
user_id = str(getattr(request, "user_id", "guest"))
from insurance.poster.product_material_service import ProductMaterialService
if not _user_manual_upload_enabled():
return jsonify({"code": 0, "data": {"total": 0, "items": []}})
return jsonify(ProductMaterialService().list_materials(user_id, {
"page": request.args.get("page", 1, type=int),
"page_size": request.args.get("page_size", 50, type=int),
"search": request.args.get("search", ""),
}))
@poster_bp.route("/product-materials", methods=["POST"])
@jwt_required
def upload_product_material():
user_id = str(getattr(request, "user_id", "guest"))
file = request.files.get("file")
from insurance.poster.product_material_service import ProductMaterialService
if not _user_manual_upload_enabled():
return error(4108, "用户上传产品小册子功能暂未开放", 403)
return jsonify(ProductMaterialService().upload(
user_id=user_id,
file=file,
password=request.form.get("password", ""),
company_id=request.form.get("companyId", ""),
plan_type=request.form.get("planType", ""),
))
@poster_bp.route("/product-materials/<int:material_id>", methods=["GET"])
@jwt_required
def get_product_material(material_id):
user_id = str(getattr(request, "user_id", "guest"))
from insurance.poster.product_material_service import ProductMaterialService
return jsonify(ProductMaterialService().get(material_id, user_id))
@poster_bp.route("/product-materials/<int:material_id>/confirm", methods=["PUT"])
@jwt_required
def confirm_product_material(material_id):
user_id = str(getattr(request, "user_id", "guest"))
from insurance.poster.product_material_service import ProductMaterialService
return jsonify(ProductMaterialService().confirm(
material_id,
user_id,
request.get_json(silent=True) or {},
))
@poster_bp.route("/product-materials/<int:material_id>/retry", methods=["POST"])
@jwt_required
def retry_product_material(material_id):
user_id = str(getattr(request, "user_id", "guest"))
from insurance.poster.product_material_service import ProductMaterialService
return jsonify(ProductMaterialService().retry(material_id, user_id))
@poster_bp.route("/product-materials/<int:material_id>/submit-review", methods=["POST"])
@jwt_required
def submit_product_material_review(material_id):
user_id = str(getattr(request, "user_id", "guest"))
from insurance.poster.product_material_service import ProductMaterialService
return jsonify(ProductMaterialService().submit_review(material_id, user_id))
@poster_bp.route("/product-materials/<int:material_id>", methods=["DELETE"])
@jwt_required
def delete_product_material(material_id):
user_id = str(getattr(request, "user_id", "guest"))
from insurance.poster.product_material_service import ProductMaterialService
return jsonify(ProductMaterialService().delete(material_id, user_id))
@poster_bp.route("/product-materials/<int:material_id>/file", methods=["GET"])
@jwt_required
def get_product_material_file(material_id):
user_id = str(getattr(request, "user_id", "guest"))
from insurance.poster.product_material_service import ProductMaterialService
material, filepath = ProductMaterialService().get_file(material_id, user_id)
if not material:
return error(4106, "产品资料不存在或无权访问", 404)
if not filepath:
return error(4107, "小册子原文件不存在", 404)
return send_file(
filepath,
mimetype="application/pdf",
as_attachment=False,
download_name=material.original_name,
)
# ─── 计划书上传 ───────────────────────────────────────────
@poster_bp.route("/case-upload", methods=["POST"])
@jwt_required
def upload_case():
"""上传计划书 PDF + 触发解析。"""
user_id = str(getattr(request, "user_id", "guest"))
product_id = request.form.get("productId", "")
product_source = {
"type": request.form.get("productSourceType", "") or (
"library_product" if product_id else ""
),
"id": request.form.get("productSourceId", "") or product_id,
}
password = request.form.get("password", "")
file = request.files.get("file")
if not file:
return error(ErrorCode.PARAM_ERROR, "请上传文件")
return jsonify(_get_service().upload_case(
user_id,
product_id,
file,
password,
product_source=product_source,
))
@poster_bp.route("/case-upload/<int:record_id>", methods=["GET"])
@jwt_required
def get_case_upload(record_id):
"""获取解析结果。"""
user_id = str(getattr(request, "user_id", "guest"))
return jsonify(_get_service().get_case_upload(record_id, user_id))
@poster_bp.route("/case-upload/<int:record_id>/confirm", methods=["PUT"])
@jwt_required
def confirm_case_upload(record_id):
"""核对/修正解析数据。"""
user_id = str(getattr(request, "user_id", "guest"))
return jsonify(_get_service().confirm_case_upload(record_id, user_id, request.get_json(silent=True) or {}))
# ─── 模板列表 ─────────────────────────────────────────────
@poster_bp.route("/templates", methods=["GET"])
@jwt_required
def list_templates():
"""获取可用海报模板列表。"""
return jsonify(_get_service().get_templates())
@poster_bp.route("/copy-templates", methods=["GET"])
@jwt_required
def list_copy_templates():
"""获取可用文案模板列表。"""
return jsonify(_get_service().get_copy_templates())
# ─── 文案生成 ─────────────────────────────────────────────
@poster_bp.route("/generate-copy", methods=["POST"])
@jwt_required
def generate_copy():
"""生成文案template/ai 模式)。"""
user_id = str(getattr(request, "user_id", "guest"))
return jsonify(_get_service().generate_copy(user_id, request.get_json(silent=True) or {}))
# ─── 海报生成 ─────────────────────────────────────────────
@poster_bp.route("/generate", methods=["POST"])
@jwt_required
def generate_poster():
"""生成海报图片。"""
user_id = str(getattr(request, "user_id", "guest"))
return jsonify(_get_service().generate_poster(user_id, request.get_json(silent=True) or {}))
# ─── 下载海报 ─────────────────────────────────────────────
@poster_bp.route("/download/<int:record_id>", methods=["GET"])
@jwt_required
def download_poster(record_id):
"""下载海报文件。"""
from insurance.models.poster_record import PosterRecord
from insurance.config import get_storage_root
user_id = str(getattr(request, "user_id", "guest"))
record = PosterRecord.query.get(record_id)
if not record or record.user_id != user_id:
return error(ErrorCode.NOT_FOUND, "记录不存在")
if not record.export_url:
return error(ErrorCode.NOT_FOUND, "文件不存在")
# 路径安全校验:仅允许 outputs/posters 目录
file_abs = os.path.abspath(record.export_url)
posters_dir = os.path.abspath(os.path.join(get_storage_root(), "outputs", "posters"))
if not file_abs.startswith(posters_dir) or not os.path.exists(file_abs):
return error(ErrorCode.NOT_FOUND, "文件不存在")
return send_file(file_abs, as_attachment=True,
download_name=f"poster_{record_id}.png", mimetype="image/png")
# ─── 记录列表 ─────────────────────────────────────────────
@poster_bp.route("/records", methods=["GET"])
@jwt_required
def list_records():
"""当前用户的海报生成记录。"""
user_id = str(getattr(request, "user_id", "guest"))
params = {
"page": request.args.get("page", 1, type=int),
"page_size": request.args.get("page_size", 20, type=int),
}
return jsonify(_get_service().list_records(user_id, params))
@poster_bp.route("/records/<int:record_id>", methods=["GET"])
@jwt_required
def get_record(record_id):
"""记录详情。"""
user_id = str(getattr(request, "user_id", "guest"))
return jsonify(_get_service().get_record(record_id, user_id))