v89-v91 模板增强 + 图源映射 + 多模态提示词可配置化
- 图源映射统一:热点采集与 Pinterest 模式均走 config.product.mark_dirs 配置,按任务序号随机抽模特图/平铺图 - 商品产地固定:统一为「中国大陆」+「产地省份=广东省」(不再读站点/字典映射) - 模板 SKU 字段检测:按建议售价同一套路检测 SKU分类/SKU数量/SKU数量单位,必填时填入单品/1/件 - 多模态分析提示词可配置:prompts/pinterest_analyze_system.md + user.md,支持国家覆盖,不丢文件回退内置 - 自定义图片模式:新增 pinterest_custom_load_node,图片数量硬校验,选品清单 ≤ 有效图片数 - 模板导出优化:写入前按货号末 3 位升序排序,不再产生空白 xlsx - 修复 v90 project review 10 项(503 致命终止、线程安全、原子写入等)
This commit is contained in:
+45
-71
@@ -135,85 +135,59 @@ def image_ratio_ok(path, target_ratio: float = 3 / 4, tolerance: float = 0.06) -
|
||||
return False
|
||||
|
||||
|
||||
def build_mark_model_map(db_path, material_root) -> Dict[str, str]:
|
||||
"""启动任务前检测 spu.mark 字段,建立 {mark: 模特文件夹名} 字典。
|
||||
|
||||
规则:
|
||||
- 读取 spu 表全部 mark 值(去重);
|
||||
- 每个 mark 映射到 material_library/<mark> 目录(目录名与 mark 一致);
|
||||
- 目录不存在时回退到 category 默认目录(T-shirt);
|
||||
- 目前库中 mark=1 → 映射到 material_library/T-shirt。
|
||||
"""
|
||||
mark_map: Dict[str, str] = {}
|
||||
try:
|
||||
root = Path(material_root)
|
||||
if not root.exists():
|
||||
return mark_map
|
||||
folders = [d.name for d in sorted(root.iterdir()) if d.is_dir()]
|
||||
if not folders:
|
||||
return mark_map
|
||||
# 读 spu.mark 实际值(去重)
|
||||
marks: List[str] = []
|
||||
try:
|
||||
conn = _connect(db_path)
|
||||
rows = conn.execute("SELECT DISTINCT mark FROM SPU WHERE mark IS NOT NULL AND mark != ''").fetchall()
|
||||
conn.close()
|
||||
marks = [str(r["mark"]).strip() for r in rows if str(r["mark"]).strip()]
|
||||
except Exception: # noqa: BLE001
|
||||
marks = []
|
||||
if not marks:
|
||||
marks = ["1"] # 库无 mark 数据时按默认 1 处理
|
||||
for m in marks:
|
||||
if m in folders:
|
||||
mark_map[m] = m
|
||||
else:
|
||||
# mark 无同名目录 → 回退默认 T-shirt(当前 mark=1 → T-shirt)
|
||||
mark_map[m] = "T-shirt" if "T-shirt" in folders else folders[0]
|
||||
print(f"[product] mark→模特目录映射: {mark_map}")
|
||||
except Exception as e: # noqa: BLE001
|
||||
print(f"[product] mark→模特目录映射构建失败: {e}")
|
||||
return mark_map
|
||||
|
||||
|
||||
def find_model_images_for_mark(db_path, material_root, mark, category: str = "T-shirt",
|
||||
ratio: float = 3 / 4, tolerance: float = 0.06) -> List[Path]:
|
||||
"""按 spu.mark 定位模特目录,过滤非目标比例图片,返回合格图片列表。
|
||||
|
||||
- mark 有对应目录(material_library/<mark>)→ 用该目录;
|
||||
- 否则回退 category(如 T-shirt);
|
||||
- 过滤掉非 3:4 比例(默认容差 6%)的图片;
|
||||
- 返回过滤后的图片列表(供调用方随机抽取)。
|
||||
"""
|
||||
root = Path(material_root)
|
||||
if not root.exists():
|
||||
return []
|
||||
d = None
|
||||
if mark is not None:
|
||||
cand = root / str(mark)
|
||||
if cand.is_dir():
|
||||
d = cand
|
||||
if d is None:
|
||||
cand = root / category
|
||||
if cand.is_dir():
|
||||
d = cand
|
||||
if d is None:
|
||||
# 兜底:第一个有图的目录(跳过无图目录)
|
||||
for sub in sorted(root.iterdir()):
|
||||
if not sub.is_dir():
|
||||
continue
|
||||
if any(f.is_file() and f.suffix.lower() in IMG_EXTS for f in sub.iterdir()):
|
||||
d = sub
|
||||
break
|
||||
if d is None:
|
||||
def _dir_images(d: Path, ratio: float, tolerance: float) -> List[Path]:
|
||||
"""返回目录内满足比例过滤(默认3:4)的图片列表;目录不存在/无图片返回空。"""
|
||||
if d is None or not d.is_dir():
|
||||
return []
|
||||
imgs = [f for f in sorted(d.iterdir()) if f.is_file() and f.suffix.lower() in IMG_EXTS]
|
||||
if not imgs:
|
||||
return []
|
||||
ok = [f for f in imgs if image_ratio_ok(f, ratio, tolerance)]
|
||||
if len(ok) < len(imgs):
|
||||
print(f"[product] 模特目录 {d.name}/ 过滤非 {int(ratio * 100)}:{int(ratio * 100) + 1} 比例:"
|
||||
print(f"[product] 目录 {d.name}/ 过滤非 {int(ratio * 100)}:{int(ratio * 100) + 1} 比例:"
|
||||
f"{len(imgs)} → {len(ok)} 张")
|
||||
return ok
|
||||
|
||||
|
||||
def build_mark_sources(material_root, mark_dirs: Optional[Dict] = None,
|
||||
category: str = "T-shirt", mark: str = "1",
|
||||
ratio: float = 3 / 4, tolerance: float = 0.06) -> Dict[str, Any]:
|
||||
"""按可配置的 mark→图源映射,返回指定 mark 的图源选择信息。
|
||||
|
||||
图源 = {"model": [Path...], "flat": [Path...]}(模特图 / 平铺图,各带英文键)。
|
||||
从 config.product.mark_dirs 读 mark 对应的两个子目录名(model_dir/flat_dir),
|
||||
在 material_library 下解析 → 过滤 3:4 → 返回「有图的文件夹」图片列表。
|
||||
mark 未配置时回退 mark_dirs["1"];完全无配置则回退 category 默认目录。
|
||||
返回结构:{"model": [imgs], "flat": [imgs]}。
|
||||
"""
|
||||
root = Path(material_root)
|
||||
result: Dict[str, Any] = {"model": [], "flat": []}
|
||||
if not root.exists():
|
||||
return result
|
||||
mcfg = mark_dirs or {}
|
||||
cfg = mcfg.get(str(mark)) or mcfg.get("1") or {} # 优先按 mark,其次回退默认 "1"
|
||||
model_name = str(cfg.get("model_dir") or "").strip()
|
||||
flat_name = str(cfg.get("flat_dir") or "").strip()
|
||||
if model_name:
|
||||
result["model"] = _dir_images(root / model_name, ratio, tolerance)
|
||||
if flat_name:
|
||||
result["flat"] = _dir_images(root / flat_name, ratio, tolerance)
|
||||
# 回退:某类目录未配置时,使用 category 默认目录补充模特图
|
||||
if not result["model"] and not result["flat"]:
|
||||
d = root / category
|
||||
if d.is_dir():
|
||||
result["model"] = _dir_images(d, ratio, tolerance)
|
||||
if not result["model"] and not result["flat"]:
|
||||
for sub in sorted(root.iterdir()):
|
||||
if not sub.is_dir():
|
||||
continue
|
||||
imgs = _dir_images(sub, ratio, tolerance)
|
||||
if imgs:
|
||||
result["model"] = imgs
|
||||
break
|
||||
return result
|
||||
|
||||
|
||||
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):
|
||||
|
||||
Reference in New Issue
Block a user