330 lines
13 KiB
Python
Raw Normal View History

2025-07-10 17:51:37 +08:00
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
2025-07-17 16:15:02 +08:00
海报服务层 - 简化版本
封装核心功能重点优化图片使用追踪
2025-07-10 17:51:37 +08:00
"""
import logging
import uuid
2025-07-17 16:15:02 +08:00
import time
from typing import List, Dict, Any, Optional
2025-07-10 17:51:37 +08:00
from datetime import datetime
from core.config import ConfigManager, PosterConfig
from core.ai import AIAgent
from utils.file_io import OutputManager
2025-07-17 16:15:02 +08:00
from utils.image_processor import ImageProcessor
2025-07-10 17:51:37 +08:00
from poster.poster_generator import PosterGenerator
2025-07-17 16:15:02 +08:00
from api.services.database_service import DatabaseService
2025-07-10 17:51:37 +08:00
logger = logging.getLogger(__name__)
class PosterService:
"""海报服务类"""
def __init__(self, ai_agent: AIAgent, config_manager: ConfigManager, output_manager: OutputManager):
"""
初始化海报服务
Args:
ai_agent: AI代理
config_manager: 配置管理器
output_manager: 输出管理器
"""
self.ai_agent = ai_agent
self.config_manager = config_manager
self.output_manager = output_manager
# 初始化各个组件
self.poster_generator = PosterGenerator(config_manager, output_manager)
2025-07-17 16:15:02 +08:00
# 初始化数据库服务
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]:
2025-07-10 17:51:37 +08:00
"""
2025-07-17 16:15:02 +08:00
简化的海报生成方法
2025-07-10 17:51:37 +08:00
Args:
2025-07-17 16:15:02 +08:00
content_id: 内容ID
product_id: 产品ID
scenic_spot_id: 景区ID
image_ids: 图像ID列表
generate_collage: 是否生成拼图
2025-07-10 17:51:37 +08:00
Returns:
2025-07-17 16:15:02 +08:00
包含base64图像数据和图片使用信息的字典
2025-07-10 17:51:37 +08:00
"""
2025-07-17 16:15:02 +08:00
start_time = time.time()
logger.info(f"开始生成海报: content_id={content_id}, product_id={product_id}, scenic_spot_id={scenic_spot_id}")
2025-07-10 17:51:37 +08:00
2025-07-17 16:15:02 +08:00
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": {}
}
2025-07-10 17:51:37 +08:00
2025-07-17 16:15:02 +08:00
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)
# 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)
# 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)
# 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")
# 5. 构建海报内容
poster_content = self._build_poster_content_from_info(
result["content_info"], result["product_info"], result["scenic_spot_info"]
)
# 6. 生成海报只使用vibrant模板
poster_path = self.poster_generator.generate_poster(
poster_content,
str(content_id or product_id or scenic_spot_id or "unknown"),
"vibrant"
)
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
2025-07-10 17:51:37 +08:00
2025-07-17 16:15:02 +08:00
def get_image_usage_info(self, image_ids: List[int]) -> Dict[str, Any]:
2025-07-10 17:51:37 +08:00
"""
2025-07-17 16:15:02 +08:00
获取图像使用情况信息
2025-07-10 17:51:37 +08:00
2025-07-17 16:15:02 +08:00
Args:
image_ids: 图像ID列表
2025-07-10 17:51:37 +08:00
Returns:
2025-07-17 16:15:02 +08:00
图像使用情况信息
2025-07-10 17:51:37 +08:00
"""
2025-07-17 16:15:02 +08:00
request_id = f"usage-{datetime.now().strftime('%Y%m%d-%H%M%S')}-{str(uuid.uuid4())[:8]}"
2025-07-10 17:51:37 +08:00
2025-07-17 16:15:02 +08:00
image_usage_info = []
total_usage_count = 0
usage_counts = []
2025-07-10 17:51:37 +08:00
2025-07-17 16:15:02 +08:00
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
}
2025-07-10 17:51:37 +08:00
2025-07-17 16:15:02 +08:00
def _update_image_usage(self, image_id: int, context: str):
"""更新图片使用追踪"""
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
2025-07-10 17:51:37 +08:00
2025-07-17 16:15:02 +08:00
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
}