修复 wx 外采 V2 转换器

This commit is contained in:
chfei committed 2026-08-27 15:27:42 +08:00
1 parent ff3ee89383
commit 184f1984ca
1 file changed
+159 -21
@@ -14,6 +14,9 @@ namespace TemuKJD.BLL.ImportConverters
{
/// <summary>
/// 微信小商店(新版商品详情接口)转拼多多数据源
/// 对应示例:title / subTitle / headImg / category / param / brandId /
/// skuIdList / skuList.skuInfo / detail.detailImg / expressInfo 等字段
/// 与 V1(out_product_id / skus / cats_v2 / head_imgs / attrs / extra_service 等下划线字段)格式不同
/// </summary>
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
}
/// <summary>
/// 从单个SKU节点中提取规格键值对(部分商品SKU不含规格属性,此时返回空列表)
/// 兼容多种可能出现的字段名
/// 从单个SKU节点中提取规格键值对(兼容极少数直接把属性挂在sku节点上的情况)
/// 正常情况下规格信息在顶层的 productSaleAttrs 中,见 BuildSkuIdToAttrsMap
/// </summary>
private Dictionary<string,string> GetSkuAttrPairs(JToken sku)
private List<KeyValuePair<string, string>> GetSkuAttrPairs(JToken sku)
{
var result = new Dictionary<string, string>();
var result = new List<KeyValuePair<string, string>>();
JArray attrArray = sku["skuAttrs"] as JArray
?? sku["attrs"] as JArray
@@ -269,14 +269,96 @@ namespace TemuKJD.BLL.ImportConverters
var value = attr["attrValue"]?.Value<string>() ?? attr["value"]?.Value<string>();
if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value))
result.Add(key, value);
result.Add(new KeyValuePair<string, string>(key, value));
}
return result;
}
/// <summary>
/// 获取SKU(skuList.skuInfo)
/// 从顶层 productSaleAttrs 构建 skuId -> [(规格名,规格值)] 的映射
/// productSaleAttrs 结构:[{ "key":"型号", "items":[{ "value":"xxx", "imgUrl":"xxx", "skuIds":["xxx"] }] }]
/// </summary>
private Dictionary<string, List<KeyValuePair<string, string>>> BuildSkuIdToAttrsMap(JObject jsonData)
{
var map = new Dictionary<string, List<KeyValuePair<string, string>>>();
var saleAttrs = jsonData["productSaleAttrs"] as JArray;
if (saleAttrs == null)
return map;
foreach (var attr in saleAttrs)
{
var key = attr["key"]?.Value<string>();
var items = attr["items"] as JArray;
if (string.IsNullOrEmpty(key) || items == null)
continue;
foreach (var item in items)
{
var value = item["value"]?.Value<string>();
var skuIds = item["skuIds"] as JArray;
if (string.IsNullOrEmpty(value) || skuIds == null)
continue;
foreach (var skuIdToken in skuIds)
{
var skuId = skuIdToken?.Value<string>();
if (string.IsNullOrEmpty(skuId))
continue;
if (!map.TryGetValue(skuId, out var list))
{
list = new List<KeyValuePair<string, string>>();
map[skuId] = list;
}
list.Add(new KeyValuePair<string, string>(key, value));
}
}
}
return map;
}
/// <summary>
/// 从顶层 productSaleAttrs 构建 skuId -> 规格图片(imgUrl) 的映射
/// </summary>
private Dictionary<string, string> BuildSkuIdToSpecImageMap(JObject jsonData)
{
var map = new Dictionary<string, string>();
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<string>();
var skuIds = item["skuIds"] as JArray;
if (string.IsNullOrEmpty(imgUrl) || skuIds == null)
continue;
foreach (var skuIdToken in skuIds)
{
var skuId = skuIdToken?.Value<string>();
if (string.IsNullOrEmpty(skuId))
continue;
if (!map.ContainsKey(skuId))
map[skuId] = imgUrl;
}
}
}
return map;
}
/// <summary>
/// 获取SKU(skuList.skuInfo),规格信息优先取自顶层 productSaleAttrs
/// </summary>
private List<SkuItem> 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<string>() ?? "";
// 优先使用 productSaleAttrs 中按 skuId 匹配到的规格,取不到时兼容旧字段
List<KeyValuePair<string, string>> 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<decimal>() ?? priceInCents;
string specImage = null;
if (!string.IsNullOrEmpty(skuId))
skuIdToSpecImageMap.TryGetValue(skuId, out specImage);
result.Add(new SkuItem
{
SkuId = sku["skuId"]?.Value<string>() ?? "",
SkuId = skuId,
Spec = string.Join(";", specValues),
Price = priceInYuan,
OriginalPrice = originPriceInCents.ToString(), // 保留原始价格(分)
Stock = sku["stockNum"]?.Value<int>() ?? 999, // 无库存字段时使用默认值
SpecImage = sku["thumbImg"]?.Value<string>() ?? sku["skuImg"]?.Value<string>() ?? ""
SpecImage = specImage
?? sku["thumbImg"]?.Value<string>()
?? sku["skuImg"]?.Value<string>()
?? ""
});
}
@@ -312,13 +412,49 @@ namespace TemuKJD.BLL.ImportConverters
}
/// <summary>
/// 获取规格(从skuList.skuInfo中的规格属性汇总去重,若商品无规格属性则返回空列表)
/// 获取规格:优先从顶层 productSaleAttrs 汇总(key为规格名,items[].value为规格值),
/// 若不存在该字段则退回从每个SKU自身属性字段中提取(兼容旧/异形数据)
/// </summary>
private List<SpecItem> GetSpecsFromNewFormat(JObject jsonData)
{
var result = new List<SpecItem>();
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<string>();
var items = attr["items"] as JArray;
if (string.IsNullOrEmpty(key) || items == null)
continue;
foreach (var item in items)
{
var value = item["value"]?.Value<string>();
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
});
}