优化了poster模块的接口
This commit is contained in:
parent
fe5fe5e5e2
commit
b8140ed820
Binary file not shown.
@ -56,12 +56,11 @@ app.add_middleware(
|
||||
)
|
||||
|
||||
# 导入路由
|
||||
from api.routers import tweet, poster, poster_unified, prompt, document, data, integration, content_integration
|
||||
from api.routers import tweet, poster, prompt, document, data, integration, content_integration
|
||||
|
||||
# 包含路由
|
||||
app.include_router(tweet.router, prefix="/api/v1/tweet", tags=["tweet"])
|
||||
app.include_router(poster.router, prefix="/api/v1/poster", tags=["poster"])
|
||||
app.include_router(poster_unified.router, prefix="/api/v2/poster", tags=["poster-unified"])
|
||||
app.include_router(prompt.router, prefix="/api/v1/prompt", tags=["prompt"])
|
||||
app.include_router(document.router, prefix="/api/v1/document", tags=["document"])
|
||||
app.include_router(data.router, prefix="/api/v1", tags=["data"])
|
||||
|
||||
Binary file not shown.
@ -2,101 +2,122 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
海报API模型定义
|
||||
海报API模型定义 - 简化版本
|
||||
只保留核心功能,重点优化图片ID使用追踪
|
||||
"""
|
||||
|
||||
from typing import List, Dict, Any, Optional, Tuple
|
||||
from typing import List, Dict, Any, Optional
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class PosterRequest(BaseModel):
|
||||
class PosterGenerateRequest(BaseModel):
|
||||
"""海报生成请求模型"""
|
||||
content: Dict[str, Any] = Field(..., description="内容数据,包含标题、正文等")
|
||||
topic_index: str = Field(..., description="主题索引,用于文件命名")
|
||||
template_name: Optional[str] = Field(None, description="模板名称,如果为None则根据配置选择")
|
||||
content_id: Optional[int] = Field(None, description="内容ID")
|
||||
product_id: Optional[int] = Field(None, description="产品ID")
|
||||
scenic_spot_id: Optional[int] = Field(None, description="景区ID")
|
||||
image_ids: Optional[List[int]] = Field(None, description="图像ID列表")
|
||||
generate_collage: bool = Field(False, description="是否生成拼图")
|
||||
|
||||
class Config:
|
||||
schema_extra = {
|
||||
"example": {
|
||||
"content": {
|
||||
"title": "【北京故宫】避开人潮的秘密路线,90%的人都不知道!",
|
||||
"content": "故宫,作为中国最著名的文化遗产之一...",
|
||||
"tag": ["北京旅游", "故宫", "旅游攻略", "避暑胜地"]
|
||||
},
|
||||
"topic_index": "1",
|
||||
"template_name": "vibrant"
|
||||
"content_id": 1,
|
||||
"product_id": 2,
|
||||
"scenic_spot_id": 3,
|
||||
"image_ids": [1, 2, 3],
|
||||
"generate_collage": True
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PosterResponse(BaseModel):
|
||||
class ImageUsageInfo(BaseModel):
|
||||
"""图像使用信息模型 - 重点追踪图片使用情况"""
|
||||
image_id: int = Field(..., description="图像ID")
|
||||
usage_count: int = Field(..., description="使用次数")
|
||||
first_used_at: str = Field(..., description="首次使用时间")
|
||||
last_used_at: str = Field(..., description="最后使用时间")
|
||||
usage_context: List[str] = Field(default_factory=list, description="使用场景列表")
|
||||
|
||||
|
||||
class PosterGenerateResponse(BaseModel):
|
||||
"""海报生成响应模型"""
|
||||
request_id: str = Field(..., description="请求ID")
|
||||
topic_index: str = Field(..., description="主题索引")
|
||||
poster_path: str = Field(..., description="生成的海报文件路径")
|
||||
template_name: str = Field(..., description="使用的模板名称")
|
||||
poster_base64: str = Field(..., description="海报图像的base64编码")
|
||||
content_info: Optional[Dict[str, Any]] = Field(None, description="内容信息")
|
||||
product_info: Optional[Dict[str, Any]] = Field(None, description="产品信息")
|
||||
scenic_spot_info: Optional[Dict[str, Any]] = Field(None, description="景区信息")
|
||||
used_image_ids: List[int] = Field(default_factory=list, description="使用的图像ID列表")
|
||||
image_usage_info: List[ImageUsageInfo] = Field(default_factory=list, description="图像使用详情")
|
||||
collage_base64: Optional[str] = Field(None, description="拼图的base64编码")
|
||||
metadata: Dict[str, Any] = Field(default_factory=dict, description="处理元数据")
|
||||
|
||||
class Config:
|
||||
schema_extra = {
|
||||
"example": {
|
||||
"request_id": "poster-20240715-123456-a1b2c3d4",
|
||||
"topic_index": "1",
|
||||
"poster_path": "/result/run_20230715_123456/topic_1/poster_vibrant.png",
|
||||
"template_name": "vibrant"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class TemplateListResponse(BaseModel):
|
||||
"""模板列表响应模型"""
|
||||
templates: List[str] = Field(..., description="可用的模板列表")
|
||||
default_template: str = Field(..., description="默认模板")
|
||||
|
||||
class Config:
|
||||
schema_extra = {
|
||||
"example": {
|
||||
"templates": ["vibrant", "business", "collage"],
|
||||
"default_template": "vibrant"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PosterTextRequest(BaseModel):
|
||||
"""海报文案生成请求模型"""
|
||||
system_prompt: str = Field(..., description="系统提示词")
|
||||
user_prompt: str = Field(..., description="用户提示词")
|
||||
context_data: Optional[Dict[str, Any]] = Field(None, description="上下文数据,用于填充提示词中的占位符")
|
||||
temperature: Optional[float] = Field(0.3, description="生成温度参数")
|
||||
top_p: Optional[float] = Field(0.4, description="top_p参数")
|
||||
|
||||
class Config:
|
||||
schema_extra = {
|
||||
"example": {
|
||||
"system_prompt": "你是一位专业的旅游海报文案撰写专家...",
|
||||
"user_prompt": "请为{location}的{attraction}创作一段简短有力的海报文案...",
|
||||
"context_data": {
|
||||
"location": "北京",
|
||||
"attraction": "故宫"
|
||||
"poster_base64": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...",
|
||||
"content_info": {
|
||||
"id": 1,
|
||||
"title": "【北京故宫】避开人潮的秘密路线",
|
||||
"content": "故宫,作为中国最著名的文化遗产之一...",
|
||||
"tag": "北京旅游,故宫,旅游攻略"
|
||||
},
|
||||
"temperature": 0.3,
|
||||
"top_p": 0.4
|
||||
"used_image_ids": [1, 2, 3],
|
||||
"image_usage_info": [
|
||||
{
|
||||
"image_id": 1,
|
||||
"usage_count": 5,
|
||||
"first_used_at": "2024-07-15 10:30:00",
|
||||
"last_used_at": "2024-07-15 10:30:00",
|
||||
"usage_context": ["poster_generation", "collage_creation"]
|
||||
}
|
||||
],
|
||||
"collage_base64": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQ...",
|
||||
"metadata": {
|
||||
"total_images_used": 3,
|
||||
"has_collage": True,
|
||||
"processing_time": "2.5s"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PosterTextResponse(BaseModel):
|
||||
"""海报文案生成响应模型"""
|
||||
request_id: str = Field(..., description="请求ID")
|
||||
text_content: Dict[str, Any] = Field(..., description="生成的文案内容")
|
||||
class ImageUsageRequest(BaseModel):
|
||||
"""图像使用查询请求模型"""
|
||||
image_ids: List[int] = Field(..., description="要查询的图像ID列表")
|
||||
|
||||
class Config:
|
||||
schema_extra = {
|
||||
"example": {
|
||||
"request_id": "text-20240715-123456-a1b2c3d4",
|
||||
"text_content": {
|
||||
"title": "紫禁城的秘密",
|
||||
"subtitle": "600年历史,等你探索",
|
||||
"description": "穿越时光,触摸历史,感受帝王的荣耀与辉煌"
|
||||
"image_ids": [1, 2, 3, 4, 5]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ImageUsageResponse(BaseModel):
|
||||
"""图像使用情况响应模型"""
|
||||
request_id: str = Field(..., description="请求ID")
|
||||
image_usage_info: List[ImageUsageInfo] = Field(..., description="图像使用详情")
|
||||
summary: Dict[str, Any] = Field(..., description="使用情况汇总")
|
||||
|
||||
class Config:
|
||||
schema_extra = {
|
||||
"example": {
|
||||
"request_id": "usage-20240715-123456-a1b2c3d4",
|
||||
"image_usage_info": [
|
||||
{
|
||||
"image_id": 1,
|
||||
"usage_count": 5,
|
||||
"first_used_at": "2024-07-15 10:30:00",
|
||||
"last_used_at": "2024-07-15 10:30:00",
|
||||
"usage_context": ["poster_generation", "collage_creation"]
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"total_images": 5,
|
||||
"total_usage_count": 15,
|
||||
"most_used_image_id": 1,
|
||||
"least_used_image_id": 5
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@ -2,7 +2,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
海报API路由
|
||||
海报API路由 - 简化版本
|
||||
只保留核心功能,重点优化图片使用追踪
|
||||
"""
|
||||
|
||||
import logging
|
||||
@ -14,9 +15,8 @@ from core.ai import AIAgent
|
||||
from utils.file_io import OutputManager
|
||||
from api.services.poster import PosterService
|
||||
from api.models.poster import (
|
||||
PosterRequest, PosterResponse,
|
||||
TemplateListResponse,
|
||||
PosterTextRequest, PosterTextResponse
|
||||
PosterGenerateRequest, PosterGenerateResponse,
|
||||
ImageUsageRequest, ImageUsageResponse
|
||||
)
|
||||
|
||||
# 从依赖注入模块导入依赖
|
||||
@ -37,80 +37,51 @@ def get_poster_service(
|
||||
return PosterService(ai_agent, config_manager, output_manager)
|
||||
|
||||
|
||||
@router.post("/generate", response_model=PosterResponse, summary="生成海报")
|
||||
@router.post("/generate", response_model=PosterGenerateResponse, summary="生成海报")
|
||||
async def generate_poster(
|
||||
request: PosterRequest,
|
||||
request: PosterGenerateRequest,
|
||||
poster_service: PosterService = Depends(get_poster_service)
|
||||
):
|
||||
"""
|
||||
生成海报
|
||||
|
||||
- **content**: 内容数据,包含标题、正文等
|
||||
- **topic_index**: 主题索引,用于文件命名
|
||||
- **template_name**: 模板名称,如果为None则根据配置选择
|
||||
- **content_id**: 内容ID(可选)
|
||||
- **product_id**: 产品ID(可选)
|
||||
- **scenic_spot_id**: 景区ID(可选)
|
||||
- **image_ids**: 图像ID列表(可选)
|
||||
- **generate_collage**: 是否生成拼图
|
||||
"""
|
||||
try:
|
||||
request_id, topic_index, poster_path, template_name = poster_service.generate_poster(
|
||||
content=request.content,
|
||||
topic_index=request.topic_index,
|
||||
template_name=request.template_name
|
||||
result = poster_service.generate_poster_simplified(
|
||||
content_id=request.content_id,
|
||||
product_id=request.product_id,
|
||||
scenic_spot_id=request.scenic_spot_id,
|
||||
image_ids=request.image_ids,
|
||||
generate_collage=request.generate_collage
|
||||
)
|
||||
|
||||
return PosterResponse(
|
||||
request_id=request_id,
|
||||
topic_index=topic_index,
|
||||
poster_path=poster_path,
|
||||
template_name=template_name
|
||||
)
|
||||
return PosterGenerateResponse(**result)
|
||||
except ValueError as e:
|
||||
logger.error(f"参数错误: {e}")
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
except Exception as e:
|
||||
logger.error(f"生成海报失败: {e}", exc_info=True)
|
||||
raise HTTPException(status_code=500, detail=f"生成海报失败: {str(e)}")
|
||||
|
||||
|
||||
@router.get("/templates", response_model=TemplateListResponse, summary="获取可用模板列表")
|
||||
async def get_templates(
|
||||
poster_service: PosterService = Depends(get_poster_service)
|
||||
):
|
||||
"""获取可用的海报模板列表"""
|
||||
try:
|
||||
templates, default_template = poster_service.get_available_templates()
|
||||
|
||||
return TemplateListResponse(
|
||||
templates=templates,
|
||||
default_template=default_template
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"获取模板列表失败: {e}", exc_info=True)
|
||||
raise HTTPException(status_code=500, detail=f"获取模板列表失败: {str(e)}")
|
||||
|
||||
|
||||
@router.post("/text", response_model=PosterTextResponse, summary="生成海报文案")
|
||||
async def generate_poster_text(
|
||||
request: PosterTextRequest,
|
||||
@router.post("/image-usage", response_model=ImageUsageResponse, summary="查询图像使用情况")
|
||||
async def get_image_usage(
|
||||
request: ImageUsageRequest,
|
||||
poster_service: PosterService = Depends(get_poster_service)
|
||||
):
|
||||
"""
|
||||
生成海报文案
|
||||
查询图像使用情况
|
||||
|
||||
- **system_prompt**: 系统提示词
|
||||
- **user_prompt**: 用户提示词
|
||||
- **context_data**: 上下文数据,用于填充提示词中的占位符
|
||||
- **temperature**: 生成温度参数
|
||||
- **top_p**: top_p参数
|
||||
- **image_ids**: 要查询的图像ID列表
|
||||
"""
|
||||
try:
|
||||
request_id, text_content = await poster_service.generate_poster_text(
|
||||
system_prompt=request.system_prompt,
|
||||
user_prompt=request.user_prompt,
|
||||
context_data=request.context_data,
|
||||
temperature=request.temperature,
|
||||
top_p=request.top_p
|
||||
)
|
||||
|
||||
return PosterTextResponse(
|
||||
request_id=request_id,
|
||||
text_content=text_content
|
||||
)
|
||||
result = poster_service.get_image_usage_info(request.image_ids)
|
||||
return ImageUsageResponse(**result)
|
||||
except Exception as e:
|
||||
logger.error(f"生成海报文案失败: {e}", exc_info=True)
|
||||
raise HTTPException(status_code=500, detail=f"生成海报文案失败: {str(e)}")
|
||||
logger.error(f"查询图像使用情况失败: {e}", exc_info=True)
|
||||
raise HTTPException(status_code=500, detail=f"查询图像使用情况失败: {str(e)}")
|
||||
@ -1,314 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
统一海报API路由
|
||||
支持多种模板类型的海报生成,配置化管理
|
||||
"""
|
||||
|
||||
import logging
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks
|
||||
from typing import Dict, Any, List
|
||||
|
||||
from core.ai import AIAgent
|
||||
from api.services.poster_service import UnifiedPosterService
|
||||
from api.models.vibrant_poster import (
|
||||
PosterGenerationRequest, PosterGenerationResponse,
|
||||
ContentGenerationRequest, ContentGenerationResponse,
|
||||
TemplateListResponse, TemplateInfo,
|
||||
BaseAPIResponse
|
||||
)
|
||||
|
||||
# 从依赖注入模块导入依赖
|
||||
from api.dependencies import get_ai_agent
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 创建路由
|
||||
router = APIRouter()
|
||||
|
||||
# 依赖注入函数
|
||||
def get_unified_poster_service(
|
||||
ai_agent: AIAgent = Depends(get_ai_agent)
|
||||
) -> UnifiedPosterService:
|
||||
"""获取统一海报服务"""
|
||||
return UnifiedPosterService(ai_agent)
|
||||
|
||||
|
||||
def create_response(success: bool, message: str, data: Any = None, request_id: str = None) -> Dict[str, Any]:
|
||||
"""创建标准响应"""
|
||||
if request_id is None:
|
||||
request_id = f"req-{datetime.now().strftime('%Y%m%d-%H%M%S')}-{str(uuid.uuid4())[:8]}"
|
||||
|
||||
return {
|
||||
"success": success,
|
||||
"message": message,
|
||||
"request_id": request_id,
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
"data": data
|
||||
}
|
||||
|
||||
|
||||
@router.get("/templates", response_model=TemplateListResponse, summary="获取所有可用模板")
|
||||
async def get_templates(
|
||||
service: UnifiedPosterService = Depends(get_unified_poster_service)
|
||||
):
|
||||
"""
|
||||
获取所有可用的海报模板列表
|
||||
|
||||
返回每个模板的详细信息,包括:
|
||||
- 模板ID和名称
|
||||
- 模板描述
|
||||
- 模板尺寸
|
||||
- 必填字段和可选字段
|
||||
"""
|
||||
try:
|
||||
templates = service.get_available_templates()
|
||||
response_data = create_response(
|
||||
success=True,
|
||||
message="获取模板列表成功",
|
||||
data=templates
|
||||
)
|
||||
return TemplateListResponse(**response_data)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"获取模板列表失败: {e}", exc_info=True)
|
||||
raise HTTPException(status_code=500, detail=f"获取模板列表失败: {str(e)}")
|
||||
|
||||
|
||||
@router.get("/templates/{template_id}", response_model=BaseAPIResponse, summary="获取指定模板信息")
|
||||
async def get_template_info(
|
||||
template_id: str,
|
||||
service: UnifiedPosterService = Depends(get_unified_poster_service)
|
||||
):
|
||||
"""
|
||||
获取指定模板的详细信息
|
||||
|
||||
参数:
|
||||
- **template_id**: 模板ID
|
||||
"""
|
||||
try:
|
||||
template_info = service.get_template_info(template_id)
|
||||
if not template_info:
|
||||
raise HTTPException(status_code=404, detail=f"模板 {template_id} 不存在")
|
||||
|
||||
response_data = create_response(
|
||||
success=True,
|
||||
message="获取模板信息成功",
|
||||
data=template_info.dict()
|
||||
)
|
||||
return BaseAPIResponse(**response_data)
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"获取模板信息失败: {e}", exc_info=True)
|
||||
raise HTTPException(status_code=500, detail=f"获取模板信息失败: {str(e)}")
|
||||
|
||||
|
||||
@router.post("/content/generate", response_model=ContentGenerationResponse, summary="生成海报内容")
|
||||
async def generate_content(
|
||||
request: ContentGenerationRequest,
|
||||
service: UnifiedPosterService = Depends(get_unified_poster_service)
|
||||
):
|
||||
"""
|
||||
根据源数据生成海报内容,不生成实际图片
|
||||
|
||||
用于:
|
||||
1. 预览生成的内容
|
||||
2. 调试和测试内容生成
|
||||
3. 分步骤生成(先生成内容,再生成图片)
|
||||
|
||||
参数:
|
||||
- **template_id**: 模板ID
|
||||
- **source_data**: 源数据,用于AI生成内容
|
||||
- **temperature**: AI生成温度参数
|
||||
"""
|
||||
try:
|
||||
content = await service.generate_content(
|
||||
template_id=request.template_id,
|
||||
source_data=request.source_data,
|
||||
temperature=request.temperature
|
||||
)
|
||||
|
||||
response_data = create_response(
|
||||
success=True,
|
||||
message="内容生成成功",
|
||||
data={
|
||||
"template_id": request.template_id,
|
||||
"content": content,
|
||||
"metadata": {
|
||||
"generation_method": "ai_generated",
|
||||
"temperature": request.temperature
|
||||
}
|
||||
}
|
||||
)
|
||||
return ContentGenerationResponse(**response_data)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"生成内容失败: {e}", exc_info=True)
|
||||
raise HTTPException(status_code=500, detail=f"生成内容失败: {str(e)}")
|
||||
|
||||
|
||||
@router.post("/generate", response_model=PosterGenerationResponse, summary="生成海报")
|
||||
async def generate_poster(
|
||||
request: PosterGenerationRequest,
|
||||
service: UnifiedPosterService = Depends(get_unified_poster_service)
|
||||
):
|
||||
"""
|
||||
生成海报图片
|
||||
|
||||
支持两种模式:
|
||||
1. 直接提供内容(content字段)
|
||||
2. 提供源数据让AI生成内容(source_data字段)
|
||||
|
||||
参数:
|
||||
- **template_id**: 模板ID
|
||||
- **content**: 直接提供的海报内容(可选)
|
||||
- **source_data**: 源数据,用于AI生成内容(可选)
|
||||
- **topic_name**: 主题名称,用于文件命名
|
||||
- **image_path**: 指定图片路径(可选)
|
||||
- **image_dir**: 图片目录(可选)
|
||||
- **output_dir**: 输出目录(可选)
|
||||
- **temperature**: AI生成温度参数
|
||||
"""
|
||||
try:
|
||||
result = await service.generate_poster(
|
||||
template_id=request.template_id,
|
||||
content=request.content,
|
||||
source_data=request.source_data,
|
||||
topic_name=request.topic_name,
|
||||
image_path=request.image_path,
|
||||
image_dir=request.image_dir,
|
||||
output_dir=request.output_dir,
|
||||
temperature=request.temperature
|
||||
)
|
||||
|
||||
response_data = create_response(
|
||||
success=True,
|
||||
message="海报生成成功",
|
||||
data=result,
|
||||
request_id=result["request_id"]
|
||||
)
|
||||
return PosterGenerationResponse(**response_data)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"生成海报失败: {e}", exc_info=True)
|
||||
raise HTTPException(status_code=500, detail=f"生成海报失败: {str(e)}")
|
||||
|
||||
|
||||
@router.post("/batch", response_model=BaseAPIResponse, summary="批量生成海报")
|
||||
async def batch_generate_posters(
|
||||
template_id: str,
|
||||
base_path: str,
|
||||
image_dir: str = None,
|
||||
source_files: Dict[str, str] = None,
|
||||
output_base: str = "result/posters",
|
||||
parallel_count: int = 3,
|
||||
temperature: float = 0.7,
|
||||
service: UnifiedPosterService = Depends(get_unified_poster_service)
|
||||
):
|
||||
"""
|
||||
批量生成海报
|
||||
|
||||
自动扫描指定目录下的topic文件夹,为每个topic生成海报。
|
||||
|
||||
参数:
|
||||
- **template_id**: 模板ID
|
||||
- **base_path**: 包含多个topic目录的基础路径
|
||||
- **image_dir**: 图片目录(可选)
|
||||
- **source_files**: 源文件配置字典(可选)
|
||||
- **output_base**: 输出基础目录
|
||||
- **parallel_count**: 并发处理数量
|
||||
- **temperature**: AI生成温度参数
|
||||
"""
|
||||
try:
|
||||
result = await service.batch_generate_posters(
|
||||
template_id=template_id,
|
||||
base_path=base_path,
|
||||
image_dir=image_dir,
|
||||
source_files=source_files or {},
|
||||
output_base=output_base,
|
||||
parallel_count=parallel_count,
|
||||
temperature=temperature
|
||||
)
|
||||
|
||||
response_data = create_response(
|
||||
success=True,
|
||||
message=f"批量生成完成,成功: {result['successful_count']}, 失败: {result['failed_count']}",
|
||||
data=result,
|
||||
request_id=result["request_id"]
|
||||
)
|
||||
return BaseAPIResponse(**response_data)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"批量生成海报失败: {e}", exc_info=True)
|
||||
raise HTTPException(status_code=500, detail=f"批量生成海报失败: {str(e)}")
|
||||
|
||||
|
||||
@router.post("/config/reload", response_model=BaseAPIResponse, summary="重新加载配置")
|
||||
async def reload_config(
|
||||
service: UnifiedPosterService = Depends(get_unified_poster_service)
|
||||
):
|
||||
"""
|
||||
重新加载海报配置
|
||||
|
||||
用于在不重启服务的情况下更新配置,包括:
|
||||
- 提示词模板
|
||||
- 模板配置
|
||||
- 默认参数
|
||||
"""
|
||||
try:
|
||||
service.reload_config()
|
||||
response_data = create_response(
|
||||
success=True,
|
||||
message="配置重新加载成功"
|
||||
)
|
||||
return BaseAPIResponse(**response_data)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"重新加载配置失败: {e}", exc_info=True)
|
||||
raise HTTPException(status_code=500, detail=f"重新加载配置失败: {str(e)}")
|
||||
|
||||
|
||||
@router.get("/health", summary="健康检查")
|
||||
async def health_check():
|
||||
"""服务健康检查"""
|
||||
return create_response(
|
||||
success=True,
|
||||
message="统一海报服务运行正常",
|
||||
data={
|
||||
"service": "unified_poster",
|
||||
"status": "healthy",
|
||||
"version": "2.0.0"
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@router.get("/config", summary="获取服务配置")
|
||||
async def get_service_config(
|
||||
service: UnifiedPosterService = Depends(get_unified_poster_service)
|
||||
):
|
||||
"""获取服务配置信息"""
|
||||
try:
|
||||
config_info = {
|
||||
"default_image_dir": service.config_manager.get_default_config("image_dir"),
|
||||
"default_output_dir": service.config_manager.get_default_config("output_dir"),
|
||||
"default_font_dir": service.config_manager.get_default_config("font_dir"),
|
||||
"default_template": service.config_manager.get_default_config("template"),
|
||||
"supported_image_formats": ["png", "jpg", "jpeg", "webp"],
|
||||
"available_templates": len(service.get_available_templates())
|
||||
}
|
||||
|
||||
response_data = create_response(
|
||||
success=True,
|
||||
message="获取配置成功",
|
||||
data=config_info
|
||||
)
|
||||
return BaseAPIResponse(**response_data)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"获取配置失败: {e}", exc_info=True)
|
||||
raise HTTPException(status_code=500, detail=f"获取配置失败: {str(e)}")
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -632,7 +632,7 @@ class DatabaseService:
|
||||
conn = self.db_pool.get_connection()
|
||||
cursor = conn.cursor(dictionary=True)
|
||||
cursor.execute(
|
||||
"SELECT id FROM product WHERE productName = %s AND isDelete = 0",
|
||||
"SELECT id FROM product WHERE name = %s AND isDelete = 0",
|
||||
(product_name,)
|
||||
)
|
||||
result = cursor.fetchone()
|
||||
@ -648,3 +648,258 @@ class DatabaseService:
|
||||
except Exception as e:
|
||||
logger.error(f"查询产品ID失败: {e}")
|
||||
return None
|
||||
|
||||
|
||||
|
||||
def get_image_by_id(self, image_id: int) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
根据ID获取图像信息
|
||||
|
||||
Args:
|
||||
image_id: 图像ID
|
||||
|
||||
Returns:
|
||||
图像信息字典,如果未找到则返回None
|
||||
"""
|
||||
if not self.db_pool:
|
||||
logger.error("数据库连接池未初始化")
|
||||
return None
|
||||
|
||||
try:
|
||||
conn = self.db_pool.get_connection()
|
||||
cursor = conn.cursor(dictionary=True)
|
||||
cursor.execute(
|
||||
"SELECT * FROM material WHERE id = %s AND materialType = 'image' AND isDelete = 0",
|
||||
(image_id,)
|
||||
)
|
||||
result = cursor.fetchone()
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
if result:
|
||||
logger.info(f"找到图像信息: ID={image_id}, 名称={result['materialName']}")
|
||||
return result
|
||||
else:
|
||||
logger.warning(f"未找到图像信息: ID={image_id}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"查询图像信息失败: {e}")
|
||||
return None
|
||||
|
||||
def get_images_by_ids(self, image_ids: List[int]) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
根据ID列表批量获取图像信息
|
||||
|
||||
Args:
|
||||
image_ids: 图像ID列表
|
||||
|
||||
Returns:
|
||||
图像信息列表
|
||||
"""
|
||||
if not self.db_pool or not image_ids:
|
||||
return []
|
||||
|
||||
try:
|
||||
conn = self.db_pool.get_connection()
|
||||
cursor = conn.cursor(dictionary=True)
|
||||
|
||||
# 构建IN查询
|
||||
placeholders = ','.join(['%s'] * len(image_ids))
|
||||
query = f"SELECT * FROM material WHERE id IN ({placeholders}) AND materialType = 'image' AND isDelete = 0"
|
||||
cursor.execute(query, image_ids)
|
||||
results = cursor.fetchall()
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
logger.info(f"批量查询图像信息: 请求{len(image_ids)}个,找到{len(results)}个")
|
||||
return results
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"批量查询图像信息失败: {e}")
|
||||
return []
|
||||
|
||||
def get_content_by_id(self, content_id: int) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
根据ID获取内容信息
|
||||
|
||||
Args:
|
||||
content_id: 内容ID
|
||||
|
||||
Returns:
|
||||
内容信息字典,如果未找到则返回None
|
||||
"""
|
||||
if not self.db_pool:
|
||||
logger.error("数据库连接池未初始化")
|
||||
return None
|
||||
|
||||
try:
|
||||
conn = self.db_pool.get_connection()
|
||||
cursor = conn.cursor(dictionary=True)
|
||||
cursor.execute(
|
||||
"SELECT * FROM content WHERE id = %s AND isDelete = 0",
|
||||
(content_id,)
|
||||
)
|
||||
result = cursor.fetchone()
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
if result:
|
||||
logger.info(f"找到内容信息: ID={content_id}, 标题={result['title']}")
|
||||
return result
|
||||
else:
|
||||
logger.warning(f"未找到内容信息: ID={content_id}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"查询内容信息失败: {e}")
|
||||
return None
|
||||
|
||||
def get_content_by_topic_index(self, topic_index: str) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
根据主题索引获取内容信息
|
||||
|
||||
Args:
|
||||
topic_index: 主题索引
|
||||
|
||||
Returns:
|
||||
内容信息字典,如果未找到则返回None
|
||||
"""
|
||||
if not self.db_pool:
|
||||
logger.error("数据库连接池未初始化")
|
||||
return None
|
||||
|
||||
try:
|
||||
conn = self.db_pool.get_connection()
|
||||
cursor = conn.cursor(dictionary=True)
|
||||
cursor.execute(
|
||||
"SELECT * FROM content WHERE topicIndex = %s AND isDelete = 0 ORDER BY createTime DESC LIMIT 1",
|
||||
(topic_index,)
|
||||
)
|
||||
result = cursor.fetchone()
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
if result:
|
||||
logger.info(f"找到内容信息: topicIndex={topic_index}, 标题={result['title']}")
|
||||
return result
|
||||
else:
|
||||
logger.warning(f"未找到内容信息: topicIndex={topic_index}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"查询内容信息失败: {e}")
|
||||
return None
|
||||
|
||||
def get_images_by_folder_id(self, folder_id: int) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
根据文件夹ID获取图像列表
|
||||
|
||||
Args:
|
||||
folder_id: 文件夹ID
|
||||
|
||||
Returns:
|
||||
图像信息列表
|
||||
"""
|
||||
if not self.db_pool:
|
||||
return []
|
||||
|
||||
try:
|
||||
conn = self.db_pool.get_connection()
|
||||
cursor = conn.cursor(dictionary=True)
|
||||
cursor.execute(
|
||||
"SELECT * FROM material WHERE folderId = %s AND materialType = 'image' AND isDelete = 0 ORDER BY createTime DESC",
|
||||
(folder_id,)
|
||||
)
|
||||
results = cursor.fetchall()
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
logger.info(f"根据文件夹ID获取图像: folderId={folder_id}, 找到{len(results)}个图像")
|
||||
return results
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"根据文件夹ID获取图像失败: {e}")
|
||||
return []
|
||||
|
||||
def get_folder_by_id(self, folder_id: int) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
根据ID获取文件夹信息
|
||||
|
||||
Args:
|
||||
folder_id: 文件夹ID
|
||||
|
||||
Returns:
|
||||
文件夹信息字典,如果未找到则返回None
|
||||
"""
|
||||
if not self.db_pool:
|
||||
logger.error("数据库连接池未初始化")
|
||||
return None
|
||||
|
||||
try:
|
||||
conn = self.db_pool.get_connection()
|
||||
cursor = conn.cursor(dictionary=True)
|
||||
cursor.execute(
|
||||
"SELECT * FROM material_folder WHERE id = %s AND isDelete = 0",
|
||||
(folder_id,)
|
||||
)
|
||||
result = cursor.fetchone()
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
if result:
|
||||
logger.info(f"找到文件夹信息: ID={folder_id}, 名称={result['folderName']}")
|
||||
return result
|
||||
else:
|
||||
logger.warning(f"未找到文件夹信息: ID={folder_id}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"查询文件夹信息失败: {e}")
|
||||
return None
|
||||
|
||||
def get_related_images_for_content(self, content_id: int, limit: int = 10) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
获取与内容相关的图像列表
|
||||
|
||||
Args:
|
||||
content_id: 内容ID
|
||||
limit: 限制数量
|
||||
|
||||
Returns:
|
||||
相关图像列表
|
||||
"""
|
||||
if not self.db_pool:
|
||||
return []
|
||||
|
||||
try:
|
||||
conn = self.db_pool.get_connection()
|
||||
cursor = conn.cursor(dictionary=True)
|
||||
|
||||
# 获取内容信息
|
||||
cursor.execute(
|
||||
"SELECT * FROM content WHERE id = %s AND isDelete = 0",
|
||||
(content_id,)
|
||||
)
|
||||
content = cursor.fetchone()
|
||||
|
||||
if not content:
|
||||
cursor.close()
|
||||
conn.close()
|
||||
return []
|
||||
|
||||
# 获取相关的图像(这里可以根据业务逻辑调整查询条件)
|
||||
cursor.execute(
|
||||
"SELECT * FROM material WHERE materialType = 'image' AND isDelete = 0 ORDER BY RAND() LIMIT %s",
|
||||
(limit,)
|
||||
)
|
||||
results = cursor.fetchall()
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
logger.info(f"获取相关内容图像: contentId={content_id}, 找到{len(results)}个图像")
|
||||
return results
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"获取相关内容图像失败: {e}")
|
||||
return []
|
||||
@ -2,20 +2,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
海报服务层
|
||||
封装现有功能,提供API调用
|
||||
海报服务层 - 简化版本
|
||||
封装核心功能,重点优化图片使用追踪
|
||||
"""
|
||||
|
||||
import logging
|
||||
import uuid
|
||||
from typing import List, Dict, Any, Optional, Tuple
|
||||
import time
|
||||
from typing import List, Dict, Any, Optional
|
||||
from datetime import datetime
|
||||
|
||||
from core.config import ConfigManager, PosterConfig
|
||||
from core.ai import AIAgent
|
||||
from utils.file_io import OutputManager
|
||||
from utils.image_processor import ImageProcessor
|
||||
from poster.poster_generator import PosterGenerator
|
||||
from poster.text_generator import PosterContentGenerator
|
||||
from api.services.database_service import DatabaseService
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -38,86 +40,291 @@ class PosterService:
|
||||
|
||||
# 初始化各个组件
|
||||
self.poster_generator = PosterGenerator(config_manager, output_manager)
|
||||
self.text_generator = PosterContentGenerator(ai_agent)
|
||||
|
||||
def generate_poster(self, content: Dict[str, Any], topic_index: str,
|
||||
template_name: Optional[str] = None) -> Tuple[str, str, str, str]:
|
||||
# 初始化数据库服务
|
||||
self.db_service = DatabaseService(config_manager)
|
||||
|
||||
# 图片使用追踪存储(实际应用中应该使用数据库)
|
||||
self._image_usage_tracker = {}
|
||||
|
||||
def generate_poster_simplified(self, content_id: Optional[int] = None,
|
||||
product_id: Optional[int] = None,
|
||||
scenic_spot_id: Optional[int] = None,
|
||||
image_ids: Optional[List[int]] = None,
|
||||
generate_collage: bool = False) -> Dict[str, Any]:
|
||||
"""
|
||||
生成海报
|
||||
简化的海报生成方法
|
||||
|
||||
Args:
|
||||
content: 内容数据,包含标题、正文等
|
||||
topic_index: 主题索引,用于文件命名
|
||||
template_name: 模板名称,如果为None则根据配置选择
|
||||
content_id: 内容ID
|
||||
product_id: 产品ID
|
||||
scenic_spot_id: 景区ID
|
||||
image_ids: 图像ID列表
|
||||
generate_collage: 是否生成拼图
|
||||
|
||||
Returns:
|
||||
请求ID、主题索引、生成的海报文件路径和使用的模板名称
|
||||
包含base64图像数据和图片使用信息的字典
|
||||
"""
|
||||
logger.info(f"开始为主题 {topic_index} 生成海报")
|
||||
start_time = time.time()
|
||||
logger.info(f"开始生成海报: content_id={content_id}, product_id={product_id}, scenic_spot_id={scenic_spot_id}")
|
||||
|
||||
# 生成海报
|
||||
poster_path = self.poster_generator.generate_poster(content, topic_index, template_name)
|
||||
result = {
|
||||
"request_id": f"poster-{datetime.now().strftime('%Y%m%d-%H%M%S')}-{str(uuid.uuid4())[:8]}",
|
||||
"poster_base64": "",
|
||||
"content_info": None,
|
||||
"product_info": None,
|
||||
"scenic_spot_info": None,
|
||||
"used_image_ids": [],
|
||||
"image_usage_info": [],
|
||||
"collage_base64": None,
|
||||
"metadata": {}
|
||||
}
|
||||
|
||||
# 获取使用的模板名称
|
||||
if template_name is None:
|
||||
template_name = self.poster_generator._select_template()
|
||||
try:
|
||||
# 1. 获取内容信息
|
||||
if content_id:
|
||||
content_info = self.db_service.get_content_by_id(content_id)
|
||||
if content_info:
|
||||
result["content_info"] = self._build_content_info(content_info)
|
||||
|
||||
# 生成请求ID
|
||||
request_id = f"poster-{datetime.now().strftime('%Y%m%d-%H%M%S')}-{str(uuid.uuid4())[:8]}"
|
||||
# 2. 获取产品信息
|
||||
if product_id:
|
||||
product_info = self.db_service.get_product_by_id(product_id)
|
||||
if product_info:
|
||||
result["product_info"] = self._build_product_info(product_info)
|
||||
|
||||
logger.info(f"海报生成完成,请求ID: {request_id}, 主题索引: {topic_index}, 模板: {template_name}")
|
||||
return request_id, topic_index, poster_path, template_name
|
||||
# 3. 获取景区信息
|
||||
if scenic_spot_id:
|
||||
scenic_spot_info = self.db_service.get_scenic_spot_by_id(scenic_spot_id)
|
||||
if scenic_spot_info:
|
||||
result["scenic_spot_info"] = self._build_scenic_spot_info(scenic_spot_info)
|
||||
|
||||
def get_available_templates(self) -> Tuple[List[str], str]:
|
||||
"""
|
||||
获取可用的模板列表
|
||||
# 4. 处理图像信息并追踪使用情况
|
||||
image_paths = []
|
||||
if image_ids:
|
||||
images = self.db_service.get_images_by_ids(image_ids)
|
||||
for img in images:
|
||||
image_paths.append(img['filePath'])
|
||||
result["used_image_ids"].append(img['id'])
|
||||
# 更新图片使用追踪
|
||||
self._update_image_usage(img['id'], "poster_generation")
|
||||
|
||||
Returns:
|
||||
可用的模板列表和默认模板
|
||||
"""
|
||||
# 获取配置
|
||||
poster_config = self.config_manager.get_config('poster', PosterConfig)
|
||||
|
||||
# 获取可用模板
|
||||
available_templates = poster_config.available_templates
|
||||
|
||||
# 获取默认模板
|
||||
default_template = poster_config.template_selection
|
||||
if default_template == "random" and available_templates:
|
||||
default_template = available_templates[0]
|
||||
|
||||
return available_templates, default_template
|
||||
|
||||
async def generate_poster_text(self, system_prompt: str, user_prompt: str,
|
||||
context_data: Optional[Dict[str, Any]] = None,
|
||||
temperature: Optional[float] = None,
|
||||
top_p: Optional[float] = None) -> Tuple[str, Dict[str, Any]]:
|
||||
"""
|
||||
生成海报文案
|
||||
|
||||
Args:
|
||||
system_prompt: 系统提示词
|
||||
user_prompt: 用户提示词
|
||||
context_data: 上下文数据,用于填充提示词中的占位符
|
||||
temperature: 生成温度参数
|
||||
top_p: top_p参数
|
||||
|
||||
Returns:
|
||||
请求ID和生成的文案内容
|
||||
"""
|
||||
logger.info("开始生成海报文案")
|
||||
|
||||
# 生成文案
|
||||
text_content = await self.text_generator.generate_text_for_poster(
|
||||
system_prompt=system_prompt,
|
||||
user_prompt=user_prompt,
|
||||
context_data=context_data,
|
||||
temperature=temperature,
|
||||
top_p=top_p
|
||||
# 5. 构建海报内容
|
||||
poster_content = self._build_poster_content_from_info(
|
||||
result["content_info"], result["product_info"], result["scenic_spot_info"]
|
||||
)
|
||||
|
||||
# 生成请求ID
|
||||
request_id = f"text-{datetime.now().strftime('%Y%m%d-%H%M%S')}-{str(uuid.uuid4())[:8]}"
|
||||
# 6. 生成海报(只使用vibrant模板)
|
||||
poster_path = self.poster_generator.generate_poster(
|
||||
poster_content,
|
||||
str(content_id or product_id or scenic_spot_id or "unknown"),
|
||||
"vibrant"
|
||||
)
|
||||
|
||||
logger.info(f"海报文案生成完成,请求ID: {request_id}")
|
||||
return request_id, text_content
|
||||
if poster_path:
|
||||
# 转换为base64
|
||||
result["poster_base64"] = ImageProcessor.image_to_base64(poster_path)
|
||||
|
||||
# 7. 处理拼图
|
||||
if generate_collage and len(image_paths) > 1:
|
||||
collage_result = ImageProcessor.process_images_for_poster(
|
||||
image_paths,
|
||||
target_size=(900, 1200),
|
||||
create_collage=True
|
||||
)
|
||||
|
||||
if collage_result.get("collage_image"):
|
||||
result["collage_base64"] = collage_result["collage_image"]["base64"]
|
||||
# 更新拼图中使用的图片使用追踪
|
||||
for img_id in result["used_image_ids"]:
|
||||
self._update_image_usage(img_id, "collage_creation")
|
||||
|
||||
# 8. 构建图片使用信息
|
||||
result["image_usage_info"] = self._get_image_usage_info(result["used_image_ids"])
|
||||
|
||||
# 9. 添加元数据
|
||||
processing_time = time.time() - start_time
|
||||
result["metadata"] = {
|
||||
"total_images_used": len(result["used_image_ids"]),
|
||||
"has_collage": result["collage_base64"] is not None,
|
||||
"processing_time": f"{processing_time:.2f}s",
|
||||
"template_used": "vibrant"
|
||||
}
|
||||
|
||||
logger.info(f"海报生成完成,处理时间: {processing_time:.2f}s")
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"生成海报失败: {e}", exc_info=True)
|
||||
result["metadata"]["error"] = str(e)
|
||||
return result
|
||||
|
||||
def get_image_usage_info(self, image_ids: List[int]) -> Dict[str, Any]:
|
||||
"""
|
||||
获取图像使用情况信息
|
||||
|
||||
Args:
|
||||
image_ids: 图像ID列表
|
||||
|
||||
Returns:
|
||||
图像使用情况信息
|
||||
"""
|
||||
request_id = f"usage-{datetime.now().strftime('%Y%m%d-%H%M%S')}-{str(uuid.uuid4())[:8]}"
|
||||
|
||||
image_usage_info = []
|
||||
total_usage_count = 0
|
||||
usage_counts = []
|
||||
|
||||
for img_id in image_ids:
|
||||
usage_info = self._get_single_image_usage_info(img_id)
|
||||
if usage_info:
|
||||
image_usage_info.append(usage_info)
|
||||
total_usage_count += usage_info["usage_count"]
|
||||
usage_counts.append(usage_info["usage_count"])
|
||||
|
||||
# 计算汇总信息
|
||||
summary = {
|
||||
"total_images": len(image_ids),
|
||||
"total_usage_count": total_usage_count,
|
||||
"most_used_image_id": None,
|
||||
"least_used_image_id": None
|
||||
}
|
||||
|
||||
if usage_counts:
|
||||
max_usage = max(usage_counts)
|
||||
min_usage = min(usage_counts)
|
||||
summary["most_used_image_id"] = next(
|
||||
(info["image_id"] for info in image_usage_info if info["usage_count"] == max_usage),
|
||||
None
|
||||
)
|
||||
summary["least_used_image_id"] = next(
|
||||
(info["image_id"] for info in image_usage_info if info["usage_count"] == min_usage),
|
||||
None
|
||||
)
|
||||
|
||||
return {
|
||||
"request_id": request_id,
|
||||
"image_usage_info": image_usage_info,
|
||||
"summary": summary
|
||||
}
|
||||
|
||||
def _update_image_usage(self, image_id: int, context: str):
|
||||
"""更新图片使用追踪"""
|
||||
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
if image_id not in self._image_usage_tracker:
|
||||
self._image_usage_tracker[image_id] = {
|
||||
"usage_count": 0,
|
||||
"first_used_at": current_time,
|
||||
"last_used_at": current_time,
|
||||
"usage_context": []
|
||||
}
|
||||
|
||||
tracker = self._image_usage_tracker[image_id]
|
||||
tracker["usage_count"] += 1
|
||||
tracker["last_used_at"] = current_time
|
||||
if context not in tracker["usage_context"]:
|
||||
tracker["usage_context"].append(context)
|
||||
|
||||
def _get_single_image_usage_info(self, image_id: int) -> Optional[Dict[str, Any]]:
|
||||
"""获取单个图片的使用信息"""
|
||||
if image_id in self._image_usage_tracker:
|
||||
tracker = self._image_usage_tracker[image_id]
|
||||
return {
|
||||
"image_id": image_id,
|
||||
"usage_count": tracker["usage_count"],
|
||||
"first_used_at": tracker["first_used_at"],
|
||||
"last_used_at": tracker["last_used_at"],
|
||||
"usage_context": tracker["usage_context"]
|
||||
}
|
||||
else:
|
||||
# 如果图片从未被使用过,返回默认信息
|
||||
return {
|
||||
"image_id": image_id,
|
||||
"usage_count": 0,
|
||||
"first_used_at": "",
|
||||
"last_used_at": "",
|
||||
"usage_context": []
|
||||
}
|
||||
|
||||
def _get_image_usage_info(self, image_ids: List[int]) -> List[Dict[str, Any]]:
|
||||
"""获取图片使用信息列表"""
|
||||
usage_info = []
|
||||
for img_id in image_ids:
|
||||
info = self._get_single_image_usage_info(img_id)
|
||||
if info:
|
||||
usage_info.append(info)
|
||||
return usage_info
|
||||
|
||||
def _build_content_info(self, content_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""构建内容信息"""
|
||||
return {
|
||||
"id": content_data["id"],
|
||||
"title": content_data["title"],
|
||||
"content": content_data["content"],
|
||||
"tag": content_data["tag"]
|
||||
}
|
||||
|
||||
def _build_product_info(self, product_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""构建产品信息"""
|
||||
return {
|
||||
"id": product_data["id"],
|
||||
"name": product_data["name"],
|
||||
"description": product_data.get("description"),
|
||||
"real_price": float(product_data["realPrice"]) if product_data.get("realPrice") else None,
|
||||
"origin_price": float(product_data["originPrice"]) if product_data.get("originPrice") else None
|
||||
}
|
||||
|
||||
def _build_scenic_spot_info(self, scenic_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""构建景区信息"""
|
||||
return {
|
||||
"id": scenic_data["id"],
|
||||
"name": scenic_data["name"],
|
||||
"description": scenic_data.get("description"),
|
||||
"address": scenic_data.get("address")
|
||||
}
|
||||
|
||||
def _build_poster_content_from_info(self, content_info: Optional[Dict[str, Any]],
|
||||
product_info: Optional[Dict[str, Any]],
|
||||
scenic_spot_info: Optional[Dict[str, Any]]) -> Dict[str, Any]:
|
||||
"""从信息构建海报内容"""
|
||||
title = ""
|
||||
content_parts = []
|
||||
tags = []
|
||||
|
||||
# 构建标题
|
||||
if content_info:
|
||||
title = content_info["title"]
|
||||
if content_info.get("content"):
|
||||
content_parts.append(content_info["content"])
|
||||
if content_info.get("tag"):
|
||||
tags.extend(content_info["tag"].split(","))
|
||||
else:
|
||||
# 如果没有内容信息,使用景区和产品信息构建标题
|
||||
if scenic_spot_info and product_info:
|
||||
title = f"{scenic_spot_info['name']} - {product_info['name']}"
|
||||
elif scenic_spot_info:
|
||||
title = scenic_spot_info['name']
|
||||
elif product_info:
|
||||
title = product_info['name']
|
||||
|
||||
# 添加景区信息
|
||||
if scenic_spot_info and scenic_spot_info.get("description"):
|
||||
content_parts.append(f"景区介绍: {scenic_spot_info['description']}")
|
||||
tags.append(scenic_spot_info["name"])
|
||||
|
||||
# 添加产品信息
|
||||
if product_info:
|
||||
if product_info.get("description"):
|
||||
content_parts.append(f"产品介绍: {product_info['description']}")
|
||||
if product_info.get("real_price"):
|
||||
content_parts.append(f"价格: ¥{product_info['real_price']}")
|
||||
tags.append(product_info["name"])
|
||||
|
||||
content = "\n\n".join(content_parts) if content_parts else "暂无详细内容"
|
||||
|
||||
return {
|
||||
"title": title,
|
||||
"content": content,
|
||||
"tag": tags
|
||||
}
|
||||
@ -51,7 +51,7 @@ class PromptService:
|
||||
self.db_pool = self._init_db_pool()
|
||||
|
||||
# 创建必要的目录结构
|
||||
self._create_resource_directories()
|
||||
# self._create_resource_directories()
|
||||
|
||||
def _create_resource_directories(self):
|
||||
pass
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user