Files
pod_trend_agent/graph/product.py
T
3218485270 f493bde8a9 POD 趋势感知 Agent:缓存热点模式 + 三图合成 + 热点去重/风格去重 + review 兜底
- 缓存热点批量流程(有采集缓存不触发 Google)
- 简报不足直接从采集缓存生成(轻量补齐)
- 三图合成(模特/印花/底图)+ 底图压缩 <2MB
- 热点去重→风格去重自动切换 + 不适合类目 review 兜底
- 透明背景(background=transparent)+ 提示词清洗(敏感词/背景描述)
- 任务前 basemap 校验 + 模板国家校验 + 模特任务级分配
2026-08-22 14:14:01 +08:00

103 lines
3.9 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.
"""SPU/SKU 数据库查询 + 底图/模特图资源查找(产品图生成流水线的数据层)。
数据关系(已核实 spu_sku.db):
- SPU.code = 款号(如 DG004
- SKU.code = "款号-颜色编码"(如 DG004-BL01),SKU.color = 中文色名(黑/灰/...
- basemap 目录 = basemap/<款号>/<SKU.code>/xxx.jpg
- material_library/<品类>/ 存放模特图
- SKU.img_url_2~5 = CDN 图 URL(底图/细节/模特图,仅作参考字段)
"""
import sqlite3
from pathlib import Path
from typing import Any, Dict, List, Optional
# 支持的图片格式:模特图/底图均按此识别(png/jpg 等常见格式全覆盖;AVIF/GIF/TIFF 亦支持)
IMG_EXTS = {".jpg", ".jpeg", ".png", ".webp", ".bmp", ".avif", ".gif", ".tiff", ".tif"}
def _connect(db_path) -> sqlite3.Connection:
conn = sqlite3.connect(str(db_path))
conn.row_factory = sqlite3.Row
return conn
def list_spus(db_path, country: Optional[str] = None) -> List[Dict[str, Any]]:
"""全部 SPU(可选按国家过滤)。"""
conn = _connect(db_path)
sql = "SELECT id, code, style, material, printing_type, target_audience, pattern, country, mark FROM SPU"
params: list = []
if country:
sql += " WHERE country = ?"
params.append(country)
sql += " ORDER BY code"
rows = conn.execute(sql, params).fetchall()
conn.close()
return [dict(r) for r in rows]
def list_colors(db_path, spu_code: str) -> List[Dict[str, Any]]:
"""款号 → 颜色列表(SKU.code 去重,附中文色名、CDN 底图 URL、最低价)。"""
conn = _connect(db_path)
rows = conn.execute(
"""SELECT s.code AS sku_code, s.color, s.img_url_2 AS img_url, MIN(s.price) AS price
FROM SKU s JOIN SPU p ON s.spu_id = p.id
WHERE p.code = ?
GROUP BY s.code, s.color ORDER BY s.code""", (spu_code,)).fetchall()
conn.close()
return [dict(r) for r in rows]
def find_basemap(basemap_root, spu_code: str, sku_code: str) -> Optional[Path]:
"""basemap/<款号>/<SKU.code>/ 下第一张图片;无返回 None。"""
d = Path(basemap_root) / spu_code / sku_code
if not d.exists():
return None
for f in sorted(d.iterdir()):
if f.is_file() and f.suffix.lower() in IMG_EXTS:
return f
return None
def list_model_images(material_root, category: str = "T-shirt") -> List[Path]:
"""material_library/<品类>/ 下所有图片;无返回空列表。"""
d = Path(material_root) / category
if not d.exists():
return []
return [f for f in sorted(d.iterdir()) if f.is_file() and f.suffix.lower() in IMG_EXTS]
def find_first_model_folder(material_root, preferred: Optional[str] = None):
"""material_library 下「第一个有图片的子目录」及其图片列表。
- preferred(如 config 的 model_category)优先:该目录有图就直接用;
- 否则按子目录名排序,取第一个有图的目录;
- 全空返回 (None, [])。
返回 (dir_name or None, images: List[Path])。
"""
root = Path(material_root)
if not root.exists():
return None, []
candidates = []
if preferred:
d = root / preferred
if d.is_dir():
candidates.append(d)
candidates += [d for d in sorted(root.iterdir()) if d.is_dir()]
seen = set()
for d in candidates:
if d in seen:
continue
seen.add(d)
imgs = [f for f in sorted(d.iterdir()) if f.is_file() and f.suffix.lower() in IMG_EXTS]
if imgs:
return d.name, imgs
return None, []
def first_available_sku(db_path, basemap_root, spu_code: str) -> Optional[str]:
"""返回该款号下第一个「本地有底图」的 SKU.code;无则 None。"""
for c in list_colors(db_path, spu_code):
if find_basemap(basemap_root, spu_code, c["sku_code"]) is not None:
return c["sku_code"]
return None