v110-v112 自定义模式完善 + 模板导出增强 + 多模态兼容优化
- 自定义模式:分析模型输出 delta 唯一改动指令,生图模板 custom_image_prompt.md({delta} 占位符),不再使用负向提示词;generate_design 按 custom_mode 分支,Pinterest 模式保留原创化指令,两模式互不影响
- 多模态分析 response_format 三级回退(json_schema → json_object → none),兼容 DeepSeek
- 模板导出:details 扩展列(细节1/2/3)、target_audience 扩展列(适用人群1)、固定值风格1=休闲/风格2=运动
- 童装特征库更新 + 标题模板外部化 + 图源映射增强
This commit is contained in:
@@ -108,6 +108,48 @@ def _map_pattern(value: Any, gender: Optional[str], pattern_map: Optional[Dict[s
|
||||
return mapping.get(v, value)
|
||||
|
||||
|
||||
# 女童图案值字典映射:db pattern 值 → 女童模板下拉框选项(男童保留原值)。
|
||||
# 女童模板遇到 pattern="印花" 时映射为「卡通」;该字典为「内置默认」,
|
||||
# 运行时可被 config.yaml 顶层 pattern_map.girl_kids 覆盖(缺失/留空回退这里)。
|
||||
_PATTERN_KIDS_MAP = {
|
||||
"girl_kids": {"印花": "卡通"},
|
||||
}
|
||||
|
||||
|
||||
def _resolve_kids_pattern_map(config: Optional[Dict[str, Any]]) -> Dict[str, Any]:
|
||||
"""从 config 读取可配置女童图案映射(config.pattern_map.girl_kids),缺失/留空回退内置默认。
|
||||
|
||||
仅作用于 spu.pattern 字段(gender=girl_kids 时映射;男童/成人/None 保留原值)。
|
||||
"""
|
||||
if not config:
|
||||
return {g: dict(m) for g, m in _PATTERN_KIDS_MAP.items()}
|
||||
kids = None
|
||||
try:
|
||||
pm = config.get("pattern_map") or {}
|
||||
kids = pm if isinstance(pm, dict) else {}
|
||||
except Exception: # noqa: BLE001
|
||||
kids = {}
|
||||
if not isinstance(kids, dict) or not kids:
|
||||
return {g: dict(m) for g, m in _PATTERN_KIDS_MAP.items()}
|
||||
merged = {g: dict(m) for g, m in _PATTERN_KIDS_MAP.items()}
|
||||
for g, m in kids.items():
|
||||
if isinstance(m, dict):
|
||||
merged.setdefault(str(g), {}).update(
|
||||
{str(k): v for k, v in m.items() if v not in (None, "")})
|
||||
return merged
|
||||
|
||||
|
||||
def _map_kids_pattern(value: Any, gender: Optional[str],
|
||||
kids_pattern_map: Optional[Dict[str, Any]] = None) -> Any:
|
||||
"""图案值按童装性别映射(仅 spu.pattern 字段):gender 为 girl_kids 时
|
||||
查女童图案映射表(默认内置、可由 config 覆盖,找不到保留原值),如「印花」→「卡通」。
|
||||
男童/成人保留原值。"""
|
||||
if gender != "girl_kids" or value is None:
|
||||
return value
|
||||
mapping = kids_pattern_map if kids_pattern_map is not None else _PATTERN_KIDS_MAP
|
||||
return mapping.get("girl_kids", {}).get(str(value).strip(), value)
|
||||
|
||||
|
||||
def _resolve_season_map(config: Optional[Dict[str, Any]]) -> Dict[str, Any]:
|
||||
"""从 config 读取可配置季节映射(config.season_map.female),缺失/留空回退内置默认。
|
||||
|
||||
@@ -236,8 +278,9 @@ def _size_rank(size) -> tuple:
|
||||
return (9, 0, "")
|
||||
if s in ("ONESIZE", "ONE SIZE", "FREESIZE", "FREE SIZE", "均码"):
|
||||
return (8, 0, s)
|
||||
if s == "XS":
|
||||
return (2, 0, s)
|
||||
m = re.fullmatch(r"(X+)S", s) # XS/XXS/XXXS(童装常见 XXS)
|
||||
if m:
|
||||
return (2, -len(m.group(1)), s)
|
||||
if s in ("S", "M", "L"):
|
||||
return (2, {"S": 1, "M": 2, "L": 3}[s], s)
|
||||
m = re.fullmatch(r"(X+)(L)", s) # XL/XXL/XXXL/XXXXL/XXXXXL
|
||||
@@ -409,12 +452,15 @@ def _fill_design_fields(router, spu_code: str, oss_code: str, cn_title: str, en_
|
||||
def _build_spu_row(spu: Dict[str, Any], spu_code: str,
|
||||
color: Optional[str] = None,
|
||||
fabric_headers: Optional[List[str]] = None,
|
||||
details_headers: Optional[List[str]] = None,
|
||||
target_audience_headers: Optional[List[str]] = None,
|
||||
gender: Optional[str] = None,
|
||||
component_map: Optional[Dict[str, Any]] = None,
|
||||
season_map: Optional[Dict[str, Any]] = None,
|
||||
pattern_map: Optional[Dict[str, Any]] = None,
|
||||
target_audience_map: Optional[Dict[str, Any]] = None,
|
||||
kids_type_map: Optional[Dict[str, Any]] = None,
|
||||
kids_pattern_map: Optional[Dict[str, Any]] = None,
|
||||
oss_code: str = "",
|
||||
model_info: Optional[Dict[str, str]] = None) -> Dict[str, Any]:
|
||||
"""构造一行 SPU(固定字段:SKC货号=当前生成货号、风格=休闲、商品产地=中国大陆、产地省份=广东省;多颜色时用色值区分)。
|
||||
@@ -431,6 +477,8 @@ def _build_spu_row(spu: Dict[str, Any], spu_code: str,
|
||||
"基础信息-商品层级": "spu",
|
||||
"SKC货号": oss_code or spu_code, # SKC货号 = 当前生成货号 oss_code(无则回退 spu_code)
|
||||
"风格": "休闲", # style 路由为"休闲"(用户要求)
|
||||
"风格1": "休闲", # 新增:匹配到「风格1」列填休闲
|
||||
"风格2": "运动", # 新增:匹配到「风格2」列填运动
|
||||
"商品产地": "中国大陆", # 所有国家统一「中国大陆」,不读经营站点/不做字典匹配(用户要求)
|
||||
"产地省份": "广东省", # 新增:精确匹配「产地省份」列,统一填「广东省」(用户要求)
|
||||
"款式来源": "现货款", # SPU商品属性-款式来源 统一填「现货款」(用户要求)
|
||||
@@ -445,6 +493,7 @@ def _build_spu_row(spu: Dict[str, Any], spu_code: str,
|
||||
v = _map_season(v, gender, season_map)
|
||||
elif dbk == "pattern":
|
||||
v = _map_pattern(v, gender, pattern_map)
|
||||
v = _map_kids_pattern(v, gender, kids_pattern_map) # 女童:印花→卡通(男童/成人保留)
|
||||
elif dbk == "target_audience":
|
||||
v = _map_target_audience(v, gender, target_audience_map)
|
||||
if v not in (None, ""):
|
||||
@@ -453,6 +502,15 @@ def _build_spu_row(spu: Dict[str, Any], spu_code: str,
|
||||
if fabric not in (None, "") and fabric_headers:
|
||||
for h in fabric_headers:
|
||||
row[h] = fabric
|
||||
details = spu.get("details")
|
||||
if details not in (None, "") and details_headers:
|
||||
for h in details_headers:
|
||||
row[h] = details
|
||||
ta = spu.get("target_audience")
|
||||
if ta not in (None, "") and target_audience_headers:
|
||||
ta = _map_target_audience(ta, gender, target_audience_map)
|
||||
for h in target_audience_headers:
|
||||
row[h] = ta
|
||||
if model_info:
|
||||
if model_info.get("model"):
|
||||
row["试穿模特"] = model_info["model"]
|
||||
@@ -489,6 +547,16 @@ def _find_fabric_headers(router) -> List[str]:
|
||||
return [str(k) for k in router.column_map if "面料弹性" in str(k)]
|
||||
|
||||
|
||||
def _find_details_headers(router) -> List[str]:
|
||||
"""定位「细节」扩展列(细节1/细节2/细节3 精确列名匹配);匹配到多个时全部填 spu.details。"""
|
||||
return [str(k) for k in router.column_map if str(k).strip() in ("细节1", "细节2", "细节3")]
|
||||
|
||||
|
||||
def _find_target_audience_headers(router) -> List[str]:
|
||||
"""定位「适用人群」扩展列(适用人群1 精确列名匹配);匹配到则填 target_audience 映射后的值。"""
|
||||
return [str(k) for k in router.column_map if str(k).strip() in ("适用人群1",)]
|
||||
|
||||
|
||||
def _find_sa_size_headers(router) -> List[str]:
|
||||
"""定位所有「沙特阿拉伯码」列(尺码表-沙特阿拉伯码…模糊匹配);匹配到多个时全部填 sku.size。"""
|
||||
return [str(k) for k in router.column_map if "沙特阿拉伯码" in str(k)]
|
||||
@@ -919,6 +987,8 @@ def _insert_product_block(
|
||||
seed_shot_urls: Optional[List[str]] = None,
|
||||
bust_headers: Optional[List[str]] = None,
|
||||
fabric_headers: Optional[List[str]] = None,
|
||||
details_headers: Optional[List[str]] = None,
|
||||
target_audience_headers: Optional[List[str]] = None,
|
||||
sa_size_headers: Optional[List[str]] = None,
|
||||
gender: Optional[str] = None,
|
||||
component_map: Optional[Dict[str, Any]] = None,
|
||||
@@ -926,6 +996,7 @@ def _insert_product_block(
|
||||
pattern_map: Optional[Dict[str, Any]] = None,
|
||||
target_audience_map: Optional[Dict[str, Any]] = None,
|
||||
kids_type_map: Optional[Dict[str, Any]] = None,
|
||||
kids_pattern_map: Optional[Dict[str, Any]] = None,
|
||||
suggested_price_ratio: float = 0.0,
|
||||
suggested_price_headers: Optional[List[str]] = None,
|
||||
suggested_unit_headers: Optional[List[str]] = None,
|
||||
@@ -965,10 +1036,13 @@ def _insert_product_block(
|
||||
# + 全部颜色尺码 SKU 行(色值在 SKU 行区分)
|
||||
block_rows.append(router.insert(
|
||||
_build_spu_row(spu, spu_code, fabric_headers=fabric_headers,
|
||||
details_headers=details_headers,
|
||||
target_audience_headers=target_audience_headers,
|
||||
gender=gender, component_map=component_map,
|
||||
season_map=season_map, pattern_map=pattern_map,
|
||||
target_audience_map=target_audience_map,
|
||||
kids_type_map=kids_type_map,
|
||||
kids_pattern_map=kids_pattern_map,
|
||||
oss_code=oss_code, model_info=model_info),
|
||||
match="exact",
|
||||
))
|
||||
@@ -1008,10 +1082,13 @@ def _insert_product_block(
|
||||
# 单 SPU + 多颜色变体:1 个 SPU 行(无色值)+ 所有颜色所有尺码 SKU 行(色值区分)
|
||||
block_rows.append(router.insert(
|
||||
_build_spu_row(spu, spu_code, fabric_headers=fabric_headers,
|
||||
details_headers=details_headers,
|
||||
target_audience_headers=target_audience_headers,
|
||||
gender=gender, component_map=component_map,
|
||||
season_map=season_map, pattern_map=pattern_map,
|
||||
target_audience_map=target_audience_map,
|
||||
kids_type_map=kids_type_map,
|
||||
kids_pattern_map=kids_pattern_map,
|
||||
oss_code=oss_code, model_info=model_info), match="exact"))
|
||||
multi_variant = len(skus_by_color) > 1
|
||||
for ci, (sc, skus) in enumerate(skus_by_color):
|
||||
@@ -1077,6 +1154,7 @@ def export_product(
|
||||
pattern_map: Optional[Dict[str, Any]] = None,
|
||||
target_audience_map: Optional[Dict[str, Any]] = None,
|
||||
kids_type_map: Optional[Dict[str, Any]] = None,
|
||||
kids_pattern_map: Optional[Dict[str, Any]] = None,
|
||||
) -> Path:
|
||||
"""生成商品上传"已填写"模板(支持多产品合并到同一文件)。
|
||||
|
||||
@@ -1106,6 +1184,8 @@ def export_product(
|
||||
price_headers = _find_price_headers(router) # 申报价格列(美站/日站/英站…模糊匹配,多个全填)
|
||||
bust_headers = _find_bust_headers(router) # 胸围列(基码表-胸围(cm)/胸围全围(cm)…多个全填)
|
||||
fabric_headers = _find_fabric_headers(router) # 面料弹性列(SPU商品属性-面料弹性…检测到才填)
|
||||
details_headers = _find_details_headers(router) # 细节扩展列(细节1/细节2/细节3…精确匹配)
|
||||
target_audience_headers = _find_target_audience_headers(router) # 适用人群扩展列(适用人群1…精确匹配)
|
||||
sa_size_headers = _find_sa_size_headers(router) # 沙特阿拉伯码列(尺码表-沙特阿拉伯码…检测到才填)
|
||||
gender = _template_gender(template_path) # 类目含「男」→male /「女」→female(成分值映射用)
|
||||
suggested_price_headers = _find_suggested_price_headers(router) # 建议售价列(不含单位)
|
||||
@@ -1124,6 +1204,8 @@ def export_product(
|
||||
seed_shot_urls=seed_shot_urls,
|
||||
bust_headers=bust_headers,
|
||||
fabric_headers=fabric_headers,
|
||||
details_headers=details_headers,
|
||||
target_audience_headers=target_audience_headers,
|
||||
sa_size_headers=sa_size_headers,
|
||||
gender=gender,
|
||||
component_map=component_map,
|
||||
@@ -1131,6 +1213,7 @@ def export_product(
|
||||
pattern_map=pattern_map,
|
||||
target_audience_map=target_audience_map,
|
||||
kids_type_map=kids_type_map,
|
||||
kids_pattern_map=kids_pattern_map,
|
||||
suggested_price_ratio=suggested_price_ratio,
|
||||
suggested_price_headers=suggested_price_headers,
|
||||
|
||||
@@ -1160,6 +1243,7 @@ def export_products(
|
||||
pattern_map: Optional[Dict[str, Any]] = None,
|
||||
target_audience_map: Optional[Dict[str, Any]] = None,
|
||||
kids_type_map: Optional[Dict[str, Any]] = None,
|
||||
kids_pattern_map: Optional[Dict[str, Any]] = None,
|
||||
) -> Path:
|
||||
"""批量合并导出:所有产品一次性写入同一模板,只打开/保存一次。
|
||||
|
||||
@@ -1176,6 +1260,8 @@ def export_products(
|
||||
price_headers = _find_price_headers(router) # 申报价格列(美站/日站/英站…模糊匹配,多个全填)
|
||||
bust_headers = _find_bust_headers(router) # 胸围列(基码表-胸围(cm)/胸围全围(cm)…多个全填)
|
||||
fabric_headers = _find_fabric_headers(router) # 面料弹性列(SPU商品属性-面料弹性…检测到才填)
|
||||
details_headers = _find_details_headers(router) # 细节扩展列(细节1/细节2/细节3…精确匹配)
|
||||
target_audience_headers = _find_target_audience_headers(router) # 适用人群扩展列(适用人群1…精确匹配)
|
||||
sa_size_headers = _find_sa_size_headers(router) # 沙特阿拉伯码列(尺码表-沙特阿拉伯码…检测到才填)
|
||||
gender = _template_gender(template_path) # 类目含「男」→male /「女」→female(成分值映射用)
|
||||
suggested_price_headers = _find_suggested_price_headers(router) # 建议售价列(不含单位)
|
||||
@@ -1202,6 +1288,8 @@ def export_products(
|
||||
seed_shot_urls=r.get("seed_shot_urls"),
|
||||
bust_headers=bust_headers,
|
||||
fabric_headers=fabric_headers,
|
||||
details_headers=details_headers,
|
||||
target_audience_headers=target_audience_headers,
|
||||
sa_size_headers=sa_size_headers,
|
||||
gender=gender,
|
||||
component_map=component_map,
|
||||
@@ -1209,6 +1297,7 @@ def export_products(
|
||||
pattern_map=pattern_map,
|
||||
target_audience_map=target_audience_map,
|
||||
kids_type_map=kids_type_map,
|
||||
kids_pattern_map=kids_pattern_map,
|
||||
suggested_price_ratio=suggested_price_ratio,
|
||||
suggested_price_headers=suggested_price_headers,
|
||||
|
||||
|
||||
Reference in New Issue
Block a user