From 184f1984ca577d5853eb2ddb0b2d44e05644c453 Mon Sep 17 00:00:00 2001 From: chfei Date: Thu, 27 Aug 2026 15:27:42 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20wx=20=E5=A4=96=E9=87=87=20?= =?UTF-8?q?V2=20=E8=BD=AC=E6=8D=A2=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ImportConverters/ConverterWXToPddV2.cs | 180 ++++++++++++++++-- 1 file changed, 159 insertions(+), 21 deletions(-) diff --git a/TEMUKJD/BLL/ImportConverters/ConverterWXToPddV2.cs b/TEMUKJD/BLL/ImportConverters/ConverterWXToPddV2.cs index 63d3139..67a57e9 100644 --- a/TEMUKJD/BLL/ImportConverters/ConverterWXToPddV2.cs +++ b/TEMUKJD/BLL/ImportConverters/ConverterWXToPddV2.cs @@ -14,6 +14,9 @@ namespace TemuKJD.BLL.ImportConverters { /// /// 微信小商店(新版商品详情接口)转拼多多数据源 + /// 对应示例:title / subTitle / headImg / category / param / brandId / + /// skuIdList / skuList.skuInfo / detail.detailImg / expressInfo 等字段 + /// 与 V1(out_product_id / skus / cats_v2 / head_imgs / attrs / extra_service 等下划线字段)格式不同 /// internal class ConverterWXToPddV2 : GoodsConverterBase { @@ -22,10 +25,7 @@ namespace TemuKJD.BLL.ImportConverters public override string ConverterName => "微信转拼多多数据源V2"; public override PlatformType Platform => PlatformType.WX; - protected override void ReadContent(string filePath) - { - base.ReadContent(filePath); - } + protected override bool IsMatchProvider() { @@ -247,12 +247,12 @@ namespace TemuKJD.BLL.ImportConverters } /// - /// 从单个SKU节点中提取规格键值对(部分商品SKU不含规格属性,此时返回空列表) - /// 兼容多种可能出现的字段名 + /// 从单个SKU节点中提取规格键值对(兼容极少数直接把属性挂在sku节点上的情况) + /// 正常情况下规格信息在顶层的 productSaleAttrs 中,见 BuildSkuIdToAttrsMap /// - private Dictionary GetSkuAttrPairs(JToken sku) + private List> GetSkuAttrPairs(JToken sku) { - var result = new Dictionary(); + var result = new List>(); JArray attrArray = sku["skuAttrs"] as JArray ?? sku["attrs"] as JArray @@ -269,14 +269,96 @@ namespace TemuKJD.BLL.ImportConverters var value = attr["attrValue"]?.Value() ?? attr["value"]?.Value(); if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value)) - result.Add(key, value); + result.Add(new KeyValuePair(key, value)); } return result; } /// - /// 获取SKU(skuList.skuInfo) + /// 从顶层 productSaleAttrs 构建 skuId -> [(规格名,规格值)] 的映射 + /// productSaleAttrs 结构:[{ "key":"型号", "items":[{ "value":"xxx", "imgUrl":"xxx", "skuIds":["xxx"] }] }] + /// + private Dictionary>> BuildSkuIdToAttrsMap(JObject jsonData) + { + var map = new Dictionary>>(); + var saleAttrs = jsonData["productSaleAttrs"] as JArray; + if (saleAttrs == null) + return map; + + foreach (var attr in saleAttrs) + { + var key = attr["key"]?.Value(); + var items = attr["items"] as JArray; + if (string.IsNullOrEmpty(key) || items == null) + continue; + + foreach (var item in items) + { + var value = item["value"]?.Value(); + var skuIds = item["skuIds"] as JArray; + if (string.IsNullOrEmpty(value) || skuIds == null) + continue; + + foreach (var skuIdToken in skuIds) + { + var skuId = skuIdToken?.Value(); + if (string.IsNullOrEmpty(skuId)) + continue; + + if (!map.TryGetValue(skuId, out var list)) + { + list = new List>(); + map[skuId] = list; + } + list.Add(new KeyValuePair(key, value)); + } + } + } + + return map; + } + + /// + /// 从顶层 productSaleAttrs 构建 skuId -> 规格图片(imgUrl) 的映射 + /// + private Dictionary BuildSkuIdToSpecImageMap(JObject jsonData) + { + var map = new Dictionary(); + var saleAttrs = jsonData["productSaleAttrs"] as JArray; + if (saleAttrs == null) + return map; + + foreach (var attr in saleAttrs) + { + var items = attr["items"] as JArray; + if (items == null) + continue; + + foreach (var item in items) + { + var imgUrl = item["imgUrl"]?.Value(); + var skuIds = item["skuIds"] as JArray; + if (string.IsNullOrEmpty(imgUrl) || skuIds == null) + continue; + + foreach (var skuIdToken in skuIds) + { + var skuId = skuIdToken?.Value(); + if (string.IsNullOrEmpty(skuId)) + continue; + + if (!map.ContainsKey(skuId)) + map[skuId] = imgUrl; + } + } + } + + return map; + } + + /// + /// 获取SKU(skuList.skuInfo),规格信息优先取自顶层 productSaleAttrs /// private List GetSkusFromNewFormat(JObject jsonData) { @@ -286,9 +368,20 @@ namespace TemuKJD.BLL.ImportConverters if (skus == null) return result; + var skuIdToAttrsMap = BuildSkuIdToAttrsMap(jsonData); + var skuIdToSpecImageMap = BuildSkuIdToSpecImageMap(jsonData); + foreach (var sku in skus) { - var attrPairs = GetSkuAttrPairs(sku); + var skuId = sku["skuId"]?.Value() ?? ""; + + // 优先使用 productSaleAttrs 中按 skuId 匹配到的规格,取不到时兼容旧字段 + List> attrPairs; + if (!string.IsNullOrEmpty(skuId) && skuIdToAttrsMap.TryGetValue(skuId, out var mappedPairs)) + attrPairs = mappedPairs; + else + attrPairs = GetSkuAttrPairs(sku); + var specValues = attrPairs.Select(a => $"{a.Key}:{a.Value}").ToList(); // price 单位为分,需要转换为元 @@ -297,14 +390,21 @@ namespace TemuKJD.BLL.ImportConverters var originPriceInCents = sku["originPrice"]?.Value() ?? priceInCents; + string specImage = null; + if (!string.IsNullOrEmpty(skuId)) + skuIdToSpecImageMap.TryGetValue(skuId, out specImage); + result.Add(new SkuItem { - SkuId = sku["skuId"]?.Value() ?? "", + SkuId = skuId, Spec = string.Join(";", specValues), Price = priceInYuan, OriginalPrice = originPriceInCents.ToString(), // 保留原始价格(分) Stock = sku["stockNum"]?.Value() ?? 999, // 无库存字段时使用默认值 - SpecImage = sku["thumbImg"]?.Value() ?? sku["skuImg"]?.Value() ?? "" + SpecImage = specImage + ?? sku["thumbImg"]?.Value() + ?? sku["skuImg"]?.Value() + ?? "" }); } @@ -312,13 +412,49 @@ namespace TemuKJD.BLL.ImportConverters } /// - /// 获取规格(从skuList.skuInfo中的规格属性汇总去重,若商品无规格属性则返回空列表) + /// 获取规格:优先从顶层 productSaleAttrs 汇总(key为规格名,items[].value为规格值), + /// 若不存在该字段则退回从每个SKU自身属性字段中提取(兼容旧/异形数据) /// private List GetSpecsFromNewFormat(JObject jsonData) { var result = new List(); - var skus = jsonData.SelectToken("$.skuList.skuInfo") as JArray; + var saleAttrs = jsonData["productSaleAttrs"] as JArray; + if (saleAttrs != null && saleAttrs.Count > 0) + { + foreach (var attr in saleAttrs) + { + var key = attr["key"]?.Value(); + var items = attr["items"] as JArray; + if (string.IsNullOrEmpty(key) || items == null) + continue; + + foreach (var item in items) + { + var value = item["value"]?.Value(); + if (string.IsNullOrEmpty(value)) + continue; + + // 去重 + if (result.Any(x => x.SpecKey == key && x.SpecValue == value)) + continue; + + result.Add(new SpecItem + { + SpecKey = key, + SpecValue = value, + SpecKeyId = 0, // 该格式中没有ID + SpecValueId = 0 + }); + } + } + + if (result.Count > 0) + return result; + } + + // 兼容处理:没有 productSaleAttrs 时,尝试从每个SKU自身属性字段中提取 + var skus = jsonData.SelectToken("$.skuList.skuInfo") as JArray; if (skus == null) return result; @@ -326,17 +462,19 @@ namespace TemuKJD.BLL.ImportConverters { var attrPairs = GetSkuAttrPairs(sku); - foreach (var item in attrPairs) + foreach (var pair in attrPairs) { - // 去重 - if (result.Any(x => x.SpecKey == item.Key && x.SpecValue == item.Value)) + var key = pair.Key; + var value = pair.Value; + + if (result.Any(x => x.SpecKey == key && x.SpecValue == value)) continue; result.Add(new SpecItem { - SpecKey = item.Key, - SpecValue = item.Value, - SpecKeyId = 0, // 该格式中没有ID + SpecKey = key, + SpecValue = value, + SpecKeyId = 0, SpecValueId = 0 }); }