Files
KJD_WebService/TEMUKJD/BLL/ImportConverters/ConverterWXToPddV2.cs
T
chfei b949d0a67e 新增导入格式
新增文件编码识别
新增编码 GB18030
2026-08-21 16:16:38 +08:00

372 lines
10 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using AllBase.Model.Result;
using AllBase.Model.Result.PDDResult;
using NaiBa.Util.Extension;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TemuKJD.BLL.ImportConverters
{
/// <summary>
/// 微信小商店(新版商品详情接口)转拼多多数据源
/// </summary>
internal class ConverterWXToPddV2 : GoodsConverterBase
{
private JObject jsonRoot;
public override string ConverterName => "微信转拼多多数据源V2";
public override PlatformType Platform => PlatformType.WX;
protected override void ReadContent(string filePath)
{
base.ReadContent(filePath);
}
protected override bool IsMatchProvider()
{
try
{
jsonRoot = JsonConvert.DeserializeObject<JObject>(RawData);
if (jsonRoot == null) return false;
// 1. 检查 extraService 是否存在且为对象类型(驼峰命名,区别于V1的 extra_service)
var extraService = jsonRoot.SelectToken("$.extraService");
if (extraService == null || extraService.Type != JTokenType.Object) return false;
// 2. 检查 skuList.skuInfo 是否存在且为数组(区别于V1的 skus 数组)
var skuInfo = jsonRoot.SelectToken("$.skuList.skuInfo");
if (skuInfo == null || skuInfo.Type != JTokenType.Array) return false;
var skuInfoArray = skuInfo as JArray;
if (skuInfoArray.Count == 0) return false;
// 3. 检查第一个SKU是否包含 skuId 字段(区别于V1的 out_sku_id)
var skuId = skuInfoArray[0]?.SelectToken("$.skuId")?.ToString();
if (string.IsNullOrEmpty(skuId)) return false;
// 4. 检查 skuIdList.skuIds 是否存在
var skuIds = jsonRoot.SelectToken("$.skuIdList.skuIds");
if (skuIds == null || skuIds.Type != JTokenType.Array) return false;
return true;
}
catch
{
return false;
}
}
protected override PDDFrontProductInfo5 ConverterProvider()
{
var product = new PDDFrontProductInfo5();
product.Platform = Platform;
// 解析JSON数据
var jsonData = JsonConvert.DeserializeObject<JObject>(RawData);
// 商品ID:优先取 expressInfo.productId,取不到则从文件名解析
product.GoodsId = GetGoodsId(jsonData);
// 商品基本信息
product.Title = jsonData["title"]?.Value<string>() ?? "";
product.Description = jsonData["shortTitle"]?.Value<string>()
?? jsonData["subTitle"]?.Value<string>()
?? "";
// 品牌ID
product.BrandId = jsonData["brandId"]?.Value<long>() ?? 0;
// 类目信息
product.CategoryId = GetCategoryIdFromCats(jsonData);
product.CategoryPath = GetCategoryPathFromCats(jsonData);
// 主图
product.MainImages = GetMainImagesFromNewFormat(jsonData);
// 详情图
product.DetailImages = GetDetailImagesFromNewFormat(jsonData);
// 商品属性(来自 param 数组)
product.Propertys = GetPropertiesFromNewFormat(jsonData);
// SKU和规格信息
product.Skus = GetSkusFromNewFormat(jsonData);
product.Specs = GetSpecsFromNewFormat(jsonData);
// 其他默认值
product.ThumbUrl = jsonData["headImg"]?[0]?.Value<string>() ?? "";
product.MallId = 0; // 该格式中没有mallID
product.Price = GetMinPrice(jsonData); // 从SKU列表中获取最低价
product.Videos = new List<string>(); // 该格式中没有视频信息
return product;
}
/// <summary>
/// 获取商品ID:优先 expressInfo.productId,其次文件名(若含下划线取最后一段数字)
/// </summary>
private long GetGoodsId(JObject jsonData)
{
var productId = jsonData.SelectToken("$.expressInfo.productId")?.ToString();
if (!string.IsNullOrEmpty(productId))
{
var idFromExpress = productId.ParseToLong();
if (idFromExpress > 0) return idFromExpress;
}
var fileName = Path.GetFileNameWithoutExtension(FilePath);
if (!string.IsNullOrEmpty(fileName) && fileName.Contains("_"))
{
var lastSegment = fileName.Split('_').Last();
var idFromFile = lastSegment.ParseToLong();
if (idFromFile > 0) return idFromFile;
}
return fileName.ParseToLong();
}
/// <summary>
/// 从category中获取类目ID(取最细分类目,即最后一个)
/// </summary>
private int GetCategoryIdFromCats(JObject jsonData)
{
var cats = jsonData["category"] as JArray;
if (cats == null || cats.Count == 0)
return 0;
var lastCat = cats.Last;
return lastCat?["catId"]?.Value<int>() ?? 0;
}
/// <summary>
/// 从category中获取类目路径
/// </summary>
private List<int> GetCategoryPathFromCats(JObject jsonData)
{
var result = new List<int>();
var cats = jsonData["category"] as JArray;
if (cats == null)
return result;
foreach (var cat in cats)
{
var catId = cat["catId"]?.Value<int>();
if (catId.HasValue && catId.Value > 0)
result.Add(catId.Value);
}
return result;
}
/// <summary>
/// 获取主图
/// </summary>
private List<string> GetMainImagesFromNewFormat(JObject jsonData)
{
var result = new List<string>();
var headImg = jsonData["headImg"] as JArray;
if (headImg == null)
return result;
foreach (var img in headImg)
{
var url = img?.Value<string>();
if (!string.IsNullOrEmpty(url))
result.Add(url);
}
return result;
}
/// <summary>
/// 获取详情图(detail.detailImg)
/// </summary>
private List<string> GetDetailImagesFromNewFormat(JObject jsonData)
{
var result = new List<string>();
var detail = jsonData["detail"];
if (detail == null)
return result;
var imgs = detail["detailImg"] as JArray;
if (imgs == null)
return result;
foreach (var img in imgs)
{
var url = img?.Value<string>();
if (!string.IsNullOrEmpty(url))
result.Add(url);
}
return result;
}
/// <summary>
/// 获取商品属性(param 数组,每项 categorys[0]为属性名,categorys[1..]为属性值)
/// </summary>
private List<Property> GetPropertiesFromNewFormat(JObject jsonData)
{
var result = new List<Property>();
var paramList = jsonData["param"] as JArray;
if (paramList == null)
return result;
foreach (var param in paramList)
{
var categorys = param["categorys"] as JArray;
if (categorys == null || categorys.Count < 2)
continue;
var key = categorys[0]?["name"]?.Value<string>();
var value = string.Join(";", categorys
.Skip(1)
.Select(c => c["name"]?.Value<string>())
.Where(v => !string.IsNullOrEmpty(v)));
if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value))
{
result.Add(new Property
{
Key = key,
Value = value
});
}
}
return result;
}
/// <summary>
/// 从单个SKU节点中提取规格键值对(部分商品SKU不含规格属性,此时返回空列表)
/// 兼容多种可能出现的字段名
/// </summary>
private Dictionary<string,string> GetSkuAttrPairs(JToken sku)
{
var result = new Dictionary<string, string>();
JArray attrArray = sku["skuAttrs"] as JArray
?? sku["attrs"] as JArray
?? sku["specAttrs"] as JArray
?? sku["saleAttrList"] as JArray
?? sku["attrList"] as JArray;
if (attrArray == null)
return result;
foreach (var attr in attrArray)
{
var key = attr["attrKey"]?.Value<string>() ?? attr["key"]?.Value<string>() ?? attr["name"]?.Value<string>();
var value = attr["attrValue"]?.Value<string>() ?? attr["value"]?.Value<string>();
if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value))
result.Add(key, value);
}
return result;
}
/// <summary>
/// 获取SKU(skuList.skuInfo)
/// </summary>
private List<SkuItem> GetSkusFromNewFormat(JObject jsonData)
{
var result = new List<SkuItem>();
var skus = jsonData.SelectToken("$.skuList.skuInfo") as JArray;
if (skus == null)
return result;
foreach (var sku in skus)
{
var attrPairs = GetSkuAttrPairs(sku);
var specValues = attrPairs.Select(a => $"{a.Key}:{a.Value}").ToList();
// price 单位为分,需要转换为元
var priceInCents = sku["price"]?.Value<decimal>() ?? 0;
var priceInYuan = priceInCents / 100;
var originPriceInCents = sku["originPrice"]?.Value<decimal>() ?? priceInCents;
result.Add(new SkuItem
{
SkuId = sku["skuId"]?.Value<string>() ?? "",
Spec = string.Join(";", specValues),
Price = priceInYuan,
OriginalPrice = originPriceInCents.ToString(), // 保留原始价格(分)
Stock = sku["stockNum"]?.Value<int>() ?? 999, // 无库存字段时使用默认值
SpecImage = sku["thumbImg"]?.Value<string>() ?? sku["skuImg"]?.Value<string>() ?? ""
});
}
return result;
}
/// <summary>
/// 获取规格(从skuList.skuInfo中的规格属性汇总去重,若商品无规格属性则返回空列表)
/// </summary>
private List<SpecItem> GetSpecsFromNewFormat(JObject jsonData)
{
var result = new List<SpecItem>();
var skus = jsonData.SelectToken("$.skuList.skuInfo") as JArray;
if (skus == null)
return result;
foreach (var sku in skus)
{
var attrPairs = GetSkuAttrPairs(sku);
foreach (var item in attrPairs)
{
// 去重
if (result.Any(x => x.SpecKey == item.Key && x.SpecValue == item.Value))
continue;
result.Add(new SpecItem
{
SpecKey = item.Key,
SpecValue = item.Value,
SpecKeyId = 0, // 该格式中没有ID
SpecValueId = 0
});
}
}
return result;
}
/// <summary>
/// 获取最低价格:优先使用 skuList.minPrice,否则从 skuInfo 中计算
/// </summary>
private decimal GetMinPrice(JObject jsonData)
{
var minPriceInCents = jsonData.SelectToken("$.skuList.minPrice")?.Value<decimal>();
if (minPriceInCents.HasValue && minPriceInCents.Value > 0)
return minPriceInCents.Value / 100;
var skus = jsonData.SelectToken("$.skuList.skuInfo") as JArray;
if (skus == null || skus.Count == 0)
return 0;
var minPrice = decimal.MaxValue;
foreach (var sku in skus)
{
var price = sku["price"]?.Value<decimal>() ?? 0;
if (price > 0 && price < minPrice)
minPrice = price;
}
return minPrice > 0 && minPrice != decimal.MaxValue ? minPrice / 100 : 0;
}
}
}