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:
2026-08-28 16:24:50 +08:00
parent 2a96ec0870
commit 71a48e4ed5
40 changed files with 780 additions and 200 deletions
+82
View File
@@ -0,0 +1,82 @@
"""Pinterest 参考模式自定义节点:加载本地图片文件夹 → 校验数量 → 注册进图池(pinterest_custom_load)。
仅自定义模式(pinterest.mode=custom)使用,替代 pinterest_search + pinterest_scrape
直接把 pinterest.custom_image_dir 内的有效图片(jpg/jpeg/png/webp)注册进图池,
再复用 pinterest_analyze 直接送多模态分析,沿用 Pinterest 后续所有步骤
(分析→设计→三合一→OSS→种草图→模板导出)。
数量校验(硬校验,不满足则不启动分析):
1. 文件夹必须有有效图片(>0);
2. 选品清单总数(product.spu_tasks 展开后,state.pinterest_target)必须 ≤ 有效图片数 ——
「选品清单不得大于有效图片数」。
每次运行把图池重建为该文件夹的图片集(自定义模式唯一图源),并清空已消费拉黑
used_images),保证用户每次重新上传/选择文件夹的所有有效图片都会被重新多模态分析。
"""
from typing import Any, Dict
from graph.pinterest import (
image_md5,
list_valid_images,
save_image_pool,
save_used_images,
)
from graph.validate import with_fallback
@with_fallback("pinterest_custom_load")
def pinterest_custom_load_node(state: Dict[str, Any]) -> Dict[str, Any]:
config = state["config"]
output_dir = state["output_dir"]
country = state["country"]
pcfg = config.get("pinterest") or {}
folder = str(pcfg.get("custom_image_dir") or "").strip()
images = list_valid_images(folder)
valid_n = len(images)
target = int(state.get("pinterest_target") or 1)
errors = list(state.get("errors") or [])
stats = dict(state.get("stats") or {})
# —— 硬校验 1:必须有有效图片 ——
if valid_n == 0:
msg = (f"自定义图片文件夹「{folder or '(未填写)'}」中没有有效图片"
f"(请填写 pinterest.custom_image_dir,文件夹内应有 jpg/jpeg/png/webp 图片)")
errors.append({"node": "pinterest_custom_load", "type": "ValidationError",
"message": msg, "trace": ""})
stats["pinterest_custom"] = {"folder": folder, "valid_images": 0, "target": target, "ok": False}
print(f"[pinterest_custom_load] ❌ {msg}")
return {"errors": errors, "stats": stats}
# —— 硬校验 2:选品清单总数 ≤ 有效图片数 ——
if target > valid_n:
msg = (f"选品清单数量({target})大于自定义图片有效数量({valid_n}):"
f"选品清单不得大于有效图片数,请补充图片或减少选品")
errors.append({"node": "pinterest_custom_load", "type": "ValidationError",
"message": msg, "trace": ""})
stats["pinterest_custom"] = {"folder": folder, "valid_images": valid_n, "target": target, "ok": False}
print(f"[pinterest_custom_load] ❌ {msg}")
return {"errors": errors, "stats": stats}
# —— 每次运行重建图池为该文件夹图片集 + 清空已消费拉黑 → 所有有效图片都被重新分析 ——
pool = {"updated_at": "", "images": []}
seen_md5: set = set()
for f in images:
m = str(image_md5(str(f)) or "").strip().lower()
if not m or m in seen_md5:
continue
seen_md5.add(m)
pool["images"].append({"path": str(f), "md5": m, "term": "custom"})
save_image_pool(output_dir, country, pool)
save_used_images(output_dir, country, set())
stats["pinterest_custom"] = {
"folder": folder, "valid_images": valid_n,
"target": target, "loaded": len(pool["images"]), "ok": True,
}
print(f"[pinterest_custom_load] 自定义图源:{folder} → 有效图片 {valid_n} 张(选品清单 {target}),"
f"已注册进图池,直接进入多模态分析")
return {"custom_mode": True, "pinterest_custom_folder": folder,
"custom_load_ok": True, "errors": errors, "stats": stats}