#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 海报API通用模型定义 支持多种模板类型的海报生成 """ from typing import List, Dict, Any, Optional, Union from pydantic import BaseModel, Field # 基础模板信息 class TemplateInfo(BaseModel): """模板信息模型""" id: str = Field(..., description="模板ID") name: str = Field(..., description="模板名称") description: str = Field(..., description="模板描述") size: List[int] = Field(..., description="模板尺寸 [宽, 高]") required_fields: List[str] = Field(..., description="必填字段列表") optional_fields: List[str] = Field(default=[], description="可选字段列表") class Config: schema_extra = { "example": { "id": "vibrant", "name": "Vibrant活力模板", "description": "适合旅游景点、娱乐活动的活力海报模板", "size": [900, 1200], "required_fields": ["title", "slogan", "price", "ticket_type", "content_items"], "optional_fields": ["remarks", "tag", "pagination"] } } # 通用内容模型(支持任意字段) class PosterContent(BaseModel): """通用海报内容模型,支持动态字段""" class Config: extra = "allow" # 允许额外字段 schema_extra = { "example": { "title": "海洋奇幻世界", "slogan": "探索深海秘境,感受蓝色奇迹的无限魅力", "price": "299", "ticket_type": "成人票", "content_items": [ "海洋馆门票1张(含所有展区)", "海豚表演VIP座位" ] } } # 保持向后兼容的Vibrant内容模型 class VibrantPosterContent(PosterContent): """Vibrant海报内容模型(向后兼容)""" title: Optional[str] = Field(None, description="主标题(8-12字符)") slogan: Optional[str] = Field(None, description="副标题/宣传语(10-20字符)") price: Optional[str] = Field(None, description="价格,如果没有价格则用'欢迎咨询'") ticket_type: Optional[str] = Field(None, description="票种类型(如成人票、套餐票等)") content_button: Optional[str] = Field(None, description="内容按钮文字") content_items: Optional[List[str]] = Field(None, description="套餐内容列表(3-5个项目)") remarks: Optional[List[str]] = Field(None, description="备注信息(1-3条)") tag: Optional[str] = Field(None, description="标签") pagination: Optional[str] = Field(None, description="分页信息") # 通用海报生成请求 class PosterGenerationRequest(BaseModel): """通用海报生成请求模型""" # 模板相关 template_id: str = Field(..., description="模板ID") # 内容相关(二选一) content: Optional[Dict[str, Any]] = Field(None, description="直接提供的海报内容") source_data: Optional[Dict[str, Any]] = Field(None, description="源数据,用于AI生成内容") # 生成参数 topic_name: Optional[str] = Field(None, description="主题名称,用于文件命名") image_path: Optional[str] = Field(None, description="指定图片路径,如果不提供则随机选择") image_dir: Optional[str] = Field(None, description="图片目录,如果不提供使用默认目录") output_dir: Optional[str] = Field(None, description="输出目录,如果不提供使用默认目录") # AI参数 temperature: Optional[float] = Field(default=0.7, description="AI生成温度参数") class Config: schema_extra = { "example": { "template_id": "vibrant", "source_data": { "scenic_info": { "name": "天津冒险湾", "location": "天津市", "type": "水上乐园" }, "product_info": { "name": "冒险湾门票", "price": 299, "type": "成人票" }, "tweet_info": { "title": "夏日清凉首选", "content": "天津冒险湾水上乐园,多种刺激项目等你来挑战..." } }, "topic_name": "天津冒险湾", "temperature": 0.7 } } # 内容生成请求 class ContentGenerationRequest(BaseModel): """内容生成请求模型""" template_id: str = Field(..., description="模板ID") source_data: Dict[str, Any] = Field(..., description="源数据,用于AI生成内容") temperature: Optional[float] = Field(default=0.7, description="AI生成温度参数") class Config: schema_extra = { "example": { "template_id": "vibrant", "source_data": { "scenic_info": {"name": "天津冒险湾", "type": "水上乐园"}, "product_info": {"name": "门票", "price": 299}, "tweet_info": {"title": "夏日清凉", "content": "水上乐园体验"} }, "temperature": 0.7 } } # 保持向后兼容 class VibrantPosterRequest(PosterGenerationRequest): """Vibrant海报生成请求模型(向后兼容)""" template_id: str = Field(default="vibrant", description="模板ID,默认为vibrant") # 兼容旧的字段结构 scenic_info: Optional[Dict[str, Any]] = Field(None, description="景区信息") product_info: Optional[Dict[str, Any]] = Field(None, description="产品信息") tweet_info: Optional[Dict[str, Any]] = Field(None, description="推文信息") def __init__(self, **data): # 转换旧格式到新格式 if 'scenic_info' in data or 'product_info' in data or 'tweet_info' in data: source_data = {} for key in ['scenic_info', 'product_info', 'tweet_info']: if key in data and data[key] is not None: source_data[key] = data.pop(key) if source_data and 'source_data' not in data: data['source_data'] = source_data super().__init__(**data) # 标准API响应基础类 class BaseAPIResponse(BaseModel): """API响应基础模型""" success: bool = Field(..., description="操作是否成功") message: str = Field(default="", description="响应消息") request_id: str = Field(..., description="请求ID") timestamp: str = Field(..., description="响应时间戳") # 通用海报生成响应 class PosterGenerationResponse(BaseAPIResponse): """通用海报生成响应模型""" data: Optional[Dict[str, Any]] = Field(None, description="响应数据") class Config: schema_extra = { "example": { "success": True, "message": "海报生成成功", "request_id": "poster-20240715-123456-a1b2c3d4", "timestamp": "2024-07-15T12:34:56Z", "data": { "template_id": "vibrant", "topic_name": "天津冒险湾", "poster_path": "/result/posters/vibrant_海洋奇幻世界_20240715_123456.png", "content": { "title": "海洋奇幻世界", "slogan": "探索深海秘境,感受蓝色奇迹的无限魅力", "price": "299" }, "metadata": { "image_used": "/data/images/ocean_park.jpg", "generation_method": "ai_generated", "template_size": [900, 1200], "processing_time": 3.2 } } } } # 内容生成响应 class ContentGenerationResponse(BaseAPIResponse): """内容生成响应模型""" data: Optional[Dict[str, Any]] = Field(None, description="生成的内容") class Config: schema_extra = { "example": { "success": True, "message": "内容生成成功", "request_id": "content-20240715-123456-a1b2c3d4", "timestamp": "2024-07-15T12:34:56Z", "data": { "template_id": "vibrant", "content": { "title": "冒险湾水世界", "slogan": "夏日激情体验,清凉无限乐趣", "price": "299", "ticket_type": "成人票" }, "metadata": { "generation_method": "ai_generated", "temperature": 0.7 } } } } # 模板列表响应 class TemplateListResponse(BaseAPIResponse): """模板列表响应模型""" data: Optional[List[TemplateInfo]] = Field(None, description="模板列表") class Config: schema_extra = { "example": { "success": True, "message": "获取模板列表成功", "request_id": "templates-20240715-123456", "timestamp": "2024-07-15T12:34:56Z", "data": [ { "id": "vibrant", "name": "Vibrant活力模板", "description": "适合旅游景点、娱乐活动的活力海报模板", "size": [900, 1200], "required_fields": ["title", "slogan", "price"], "optional_fields": ["remarks", "tag"] } ] } } # 保持向后兼容的响应模型 class VibrantPosterResponse(BaseModel): """Vibrant海报生成响应模型(向后兼容)""" request_id: str = Field(..., description="请求ID") topic_name: str = Field(..., description="主题名称") poster_path: str = Field(..., description="生成的海报文件路径") generated_content: Dict[str, Any] = Field(..., description="生成或使用的海报内容") image_used: str = Field(..., description="使用的图片路径") generation_method: str = Field(..., description="生成方式:direct(直接使用提供内容)或ai_generated(AI生成)") class Config: schema_extra = { "example": { "request_id": "vibrant-20240715-123456-a1b2c3d4", "topic_name": "天津冒险湾", "poster_path": "/result/posters/vibrant_海洋奇幻世界_20240715_123456.png", "generated_content": { "title": "海洋奇幻世界", "slogan": "探索深海秘境,感受蓝色奇迹的无限魅力", "price": "299", "ticket_type": "成人票", "content_items": ["海洋馆门票1张", "海豚表演VIP座位"] }, "image_used": "/data/images/ocean_park.jpg", "generation_method": "ai_generated" } } class BatchVibrantPosterRequest(BaseModel): """批量Vibrant海报生成请求模型""" base_path: str = Field(..., description="包含多个topic目录的基础路径") image_dir: Optional[str] = Field(None, description="图片目录") scenic_info_file: Optional[str] = Field(None, description="景区信息文件路径") product_info_file: Optional[str] = Field(None, description="产品信息文件路径") output_base: Optional[str] = Field(default="result/posters", description="输出基础目录") parallel_count: Optional[int] = Field(default=3, description="并发处理数量") temperature: Optional[float] = Field(default=0.7, description="AI生成温度参数") class Config: schema_extra = { "example": { "base_path": "/root/TravelContentCreator/result/run_20250710_165327", "image_dir": "/root/TravelContentCreator/data/images", "scenic_info_file": "/root/TravelContentCreator/resource/data/Object/天津冒险湾.txt", "product_info_file": "/root/TravelContentCreator/resource/data/Product/product.bak", "output_base": "result/posters", "parallel_count": 3, "temperature": 0.7 } } class BatchVibrantPosterResponse(BaseModel): """批量Vibrant海报生成响应模型""" request_id: str = Field(..., description="批量处理请求ID") total_topics: int = Field(..., description="总共处理的topic数量") successful_count: int = Field(..., description="成功生成的海报数量") failed_count: int = Field(..., description="失败的海报数量") output_base_dir: str = Field(..., description="输出基础目录") successful_topics: List[str] = Field(..., description="成功处理的topic列表") failed_topics: List[Dict[str, str]] = Field(..., description="失败的topic及错误信息") class Config: schema_extra = { "example": { "request_id": "batch-vibrant-20240715-123456", "total_topics": 5, "successful_count": 4, "failed_count": 1, "output_base_dir": "/result/posters/run_20250710_165327", "successful_topics": ["topic_1", "topic_2", "topic_3", "topic_4"], "failed_topics": [ {"topic": "topic_5", "error": "图片文件不存在"} ] } }