diff --git a/TEMUKJD/BLL/ImportConverters/ConvertToPddFactory.cs b/TEMUKJD/BLL/ImportConverters/ConvertToPddFactory.cs index 25dc756..ce208b0 100644 --- a/TEMUKJD/BLL/ImportConverters/ConvertToPddFactory.cs +++ b/TEMUKJD/BLL/ImportConverters/ConvertToPddFactory.cs @@ -17,6 +17,8 @@ namespace TemuKJD.BLL.ImportConverts new ConverterWXToPddV1(), new ConverterWXToPddV2(), new ConverterWXToPddV3(), + new ConverterWXToPddV4(), + new ConverterWXToPddV5(), }; public static PDDFrontProductInfo5 Convert(string filePath) { diff --git a/TEMUKJD/BLL/ImportConverters/ConverterWXToPddV4.cs b/TEMUKJD/BLL/ImportConverters/ConverterWXToPddV4.cs new file mode 100644 index 0000000..b5a6223 --- /dev/null +++ b/TEMUKJD/BLL/ImportConverters/ConverterWXToPddV4.cs @@ -0,0 +1,454 @@ +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; + +namespace TemuKJD.BLL.ImportConverters +{ + /// + /// 微信转拼多多数据源 V4版本 + /// 针对微信商品详情API(/channels/ec/product/get)返回的完整数据结构 + /// + /// 数据结构特点: + /// - 顶层为 { detail: { product: { product_key, info, product_skus, ... } } } + /// - info 节点包含商品基本信息、主图、详情图、参数、SKU列表等 + /// - product_skus 节点包含完整的SKU信息(含库存、价格、规格) + /// - product_sale_attrs 节点包含销售属性(颜色、尺码) + /// - extra_service 节点包含售后服务信息 + /// - qualification_list 节点包含资质信息 + /// + internal class ConverterWXToPddV4 : GoodsConverterBase + { + private JObject _jsonRoot; + private JObject _productNode; // detail.product + private JObject _infoNode; // detail.product.info + private JArray _skuList; // detail.product.info.product_skus + private JArray _saleAttrs; // detail.product.info.product_sale_attrs + + public override string ConverterName => "微信转拼多多数据源 V4"; + + public override PlatformType Platform => PlatformType.WX; + + protected override bool IsMatchProvider() + { + try + { + _jsonRoot = JsonConvert.DeserializeObject(RawData); + if (_jsonRoot == null) return false; + + // 1. 检查是否为详情API格式(detail.product 结构) + var detail = _jsonRoot.SelectToken("$.detail") as JObject; + if (detail == null) return false; + + var product = detail.SelectToken("$.product") as JObject; + if (product == null) return false; + + // 2. 检查 product_key 是否存在 + var productKey = product.SelectToken("$.product_key") as JObject; + if (productKey == null) return false; + + var productId = productKey.SelectToken("$.product_id")?.ToString(); + if (string.IsNullOrEmpty(productId)) return false; + + // 3. 检查 info 节点是否存在 + var info = product.SelectToken("$.info") as JObject; + if (info == null) return false; + + // 4. 检查是否有标题 + var title = info.SelectToken("$.title")?.ToString(); + if (string.IsNullOrEmpty(title)) return false; + + // 5. 检查是否有SKU列表 + var skus = product.SelectToken("$.product_skus") as JArray; + if (skus == null || skus.Count == 0) return false; + + // 缓存节点 + _productNode = product; + _infoNode = info; + _skuList = skus; + _saleAttrs = info.SelectToken("$.product_sale_attrs") as JArray; + + return true; + } + catch + { + return false; + } + } + + protected override PDDFrontProductInfo5 ConverterProvider() + { + var product = new PDDFrontProductInfo5 + { + Platform = Platform, + GoodsId = ParseGoodsId() + }; + + // ========== 1. 商品基本信息 ========== + product.Title = GetStringValue(_infoNode, "title"); + product.Description = GetStringValue(_infoNode, "short_title"); + if (string.IsNullOrEmpty(product.Description)) + { + product.Description = GetStringValue(_infoNode, "ai_gen_short_title"); + } + + // ========== 2. 品牌信息 ========== + product.BrandId = GetLongValue(_infoNode, "brand_id"); + + // ========== 3. 类目信息 ========== + product.CategoryId = GetCategoryId(); + product.CategoryPath = GetCategoryPath(); + + // ========== 4. 主图 ========== + product.MainImages = GetMainImages(); + + // ========== 5. 详情图 ========== + product.DetailImages = GetDetailImages(); + + // ========== 6. 商品属性 ========== + product.Propertys = GetProperties(); + + // ========== 7. SKU和规格信息 ========== + var skuResult = GetSkusAndSpecs(); + product.Skus = skuResult.Item1; + product.Specs = skuResult.Item2; + + // ========== 8. 其他信息 ========== + product.ThumbUrl = product.MainImages.FirstOrDefault() ?? string.Empty; + product.MallId = 0; + product.Price = GetMinPrice(); + product.Videos = GetVideoUrls(); + + return product; + } + + #region 核心字段解析 + + /// + /// 解析商品ID + /// + private long ParseGoodsId() + { + var productId = _productNode.SelectToken("$.product_key.product_id")?.ToString(); + if (long.TryParse(productId, out var result)) + return result; + + // 回退到文件路径 + return Path.GetFileNameWithoutExtension(FilePath).ParseToLong(); + } + + /// + /// 获取类目ID(取最细分类目) + /// + private int GetCategoryId() + { + var category = _infoNode["category"] as JArray; + if (category == null || category.Count == 0) + return 0; + + // 取最后一个类目(最细分类目) + var lastCat = category.Last as JObject; + return lastCat?["cat_id"]?.Value() ?? 0; + } + + /// + /// 获取类目路径 + /// + private List GetCategoryPath() + { + var result = new List(); + var category = _infoNode["category"] as JArray; + if (category == null) + return result; + + foreach (var cat in category) + { + var catId = cat["cat_id"]?.Value(); + if (catId.HasValue && catId.Value > 0) + result.Add(catId.Value); + } + + return result; + } + + /// + /// 获取主图列表 + /// + private List GetMainImages() + { + var result = new List(); + + // 优先使用 head_img 数组 + var headImg = _infoNode["head_img"] as JArray; + if (headImg != null && headImg.Count > 0) + { + foreach (var img in headImg) + { + var url = img?.Value(); + if (!string.IsNullOrEmpty(url)) + result.Add(url); + } + return result; + } + + // 回退到 head_img_infos + var headImgInfos = _infoNode["head_img_infos"] as JArray; + if (headImgInfos != null) + { + foreach (var img in headImgInfos) + { + var url = img["img_url"]?.Value(); + if (!string.IsNullOrEmpty(url)) + result.Add(url); + } + } + + return result; + } + + /// + /// 获取详情图列表 + /// + private List GetDetailImages() + { + var result = new List(); + var detail = _infoNode["detail"] as JObject; + if (detail == null) + return result; + + var detailImg = detail["detail_img"] as JArray; + if (detailImg == null) + return result; + + foreach (var img in detailImg) + { + var url = img?.Value(); + if (!string.IsNullOrEmpty(url)) + result.Add(url); + } + + return result; + } + + /// + /// 获取商品属性列表(从 param 节点解析) + /// + private List GetProperties() + { + var result = new List(); + var param = _infoNode["param"] as JArray; + if (param == null) + return result; + + foreach (var item in param) + { + var categorys = item["categorys"] as JArray; + if (categorys == null || categorys.Count < 2) + continue; + + var key = categorys[0]?["name"]?.Value(); + var value = categorys[1]?["name"]?.Value(); + + if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value)) + { + // 去重 + if (!result.Any(p => p.Key == key)) + { + result.Add(new Property + { + Key = key, + Value = value + }); + } + } + } + + return result; + } + + /// + /// 获取视频URL列表 + /// + private List GetVideoUrls() + { + var result = new List(); + var headVideoInfo = _infoNode["head_video_info"] as JArray; + if (headVideoInfo == null) + return result; + + foreach (var video in headVideoInfo) + { + var url = video["video_url"]?.Value(); + if (!string.IsNullOrEmpty(url)) + result.Add(url); + } + + return result; + } + + #endregion + + #region SKU和规格解析 + + /// + /// 获取SKU和规格信息 + /// + private Tuple, List> GetSkusAndSpecs() + { + var skuList = new List(); + var specList = new List(); + + if (_skuList == null || _skuList.Count == 0) + return new Tuple, List>(skuList, specList); + + // 收集所有规格 + var specDict = new Dictionary>(); + + foreach (var sku in _skuList) + { + var skuObj = sku as JObject; + if (skuObj == null) continue; + + // 获取 SKU 基本信息 + var skuKey = skuObj["sku_key"] as JObject; + var skuInfo = skuObj["product_sku_info"] as JObject; + if (skuKey == null || skuInfo == null) continue; + + var skuId = skuKey["sku_id"]?.Value() ?? string.Empty; + var outSkuId = skuKey["out_sku_id"]?.Value() ?? string.Empty; + + // 解析销售属性 + var saleParams = skuInfo["sale_param"] as JArray; + var specValues = new List(); + + if (saleParams != null) + { + foreach (var param in saleParams) + { + var paramV2 = param["param_v2"] as JObject; + if (paramV2 == null) continue; + + var attrName = paramV2["attr_name"]?.Value() ?? string.Empty; + var attrValue = paramV2["attr_value_name"]?.Value() ?? string.Empty; + + if (!string.IsNullOrEmpty(attrName) && !string.IsNullOrEmpty(attrValue)) + { + specValues.Add($"{attrName}:{attrValue}"); + + // 收集规格 + if (!specDict.ContainsKey(attrName)) + specDict[attrName] = new HashSet(); + specDict[attrName].Add(attrValue); + } + } + } + + // 价格(单位:分) + var salePrice = skuInfo["sale_price"]?.Value() ?? 0; + + // 库存 + var stockInfo = skuInfo["stock_info"] as JObject; + var stockNum = 0; + if (stockInfo != null) + { + var stockStr = stockInfo["stock_num"]?.Value(); + if (!string.IsNullOrEmpty(stockStr)) + { + int.TryParse(stockStr, out stockNum); + } + } + + // 缩略图 + var thumbImg = skuInfo["thumb_img"]?.Value() ?? string.Empty; + + skuList.Add(new SkuItem + { + SkuId = !string.IsNullOrEmpty(outSkuId) ? outSkuId : skuId, + Spec = string.Join(";", specValues), + Price = salePrice / 100, // 分转元 + OriginalPrice = salePrice.ToString(), + Stock = stockNum, + SpecImage = thumbImg + }); + } + + // 构建规格列表 + foreach (var spec in specDict) + { + foreach (var value in spec.Value) + { + specList.Add(new SpecItem + { + SpecKey = spec.Key, + SpecValue = value, + SpecKeyId = 0, + SpecValueId = 0 + }); + } + } + + return new Tuple, List>(skuList, specList); + } + + /// + /// 获取最低价格(元) + /// + private decimal GetMinPrice() + { + if (_skuList == null || _skuList.Count == 0) + return 0; + + var minPrice = decimal.MaxValue; + + foreach (var sku in _skuList) + { + var skuObj = sku as JObject; + if (skuObj == null) continue; + + var skuInfo = skuObj["product_sku_info"] as JObject; + if (skuInfo == null) continue; + + var price = skuInfo["sale_price"]?.Value() ?? 0; + if (price > 0 && price < minPrice) + minPrice = price; + } + + // 回退到 sku_list.min_price + if (minPrice == decimal.MaxValue) + { + var skuListInfo = _infoNode["sku_list"] as JObject; + var minPriceToken = skuListInfo?["min_price"]?.Value() ?? 0; + return minPriceToken / 100; + } + + return minPrice > 0 ? minPrice / 100 : 0; + } + + #endregion + + #region 辅助方法 + + private string GetStringValue(JObject obj, string path) + { + return obj?.SelectToken(path)?.Value() ?? string.Empty; + } + + private long GetLongValue(JObject obj, string path) + { + var token = obj?.SelectToken(path); + if (token == null) return 0; + + if (token.Type == JTokenType.String) + return long.TryParse(token.Value(), out var result) ? result : 0; + + return token.Value(); + } + + #endregion + } +} \ No newline at end of file diff --git a/TEMUKJD/BLL/ImportConverters/ConverterWXToPddV5.cs b/TEMUKJD/BLL/ImportConverters/ConverterWXToPddV5.cs new file mode 100644 index 0000000..7697754 --- /dev/null +++ b/TEMUKJD/BLL/ImportConverters/ConverterWXToPddV5.cs @@ -0,0 +1,529 @@ +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; + +namespace TemuKJD.BLL.ImportConverters +{ + /// + /// 微信转拼多多数据源 V5版本 + /// 针对扁平化结构(含 _raw 节点 + 顶层冗余字段) + /// + /// 数据结构特点: + /// - 顶层包含 _raw、_src、appid、brandId、catId 等元信息 + /// - 核心商品数据在 _raw 节点内(驼峰命名:headImg、skuList、detail等) + /// - 顶层同时冗余了部分字段(headImg、headImgs、saleAttrs、skuSpecs等) + /// - SKU价格使用 isDiscounted + estimatePrice 计算优惠后价格 + /// - 类目路径为字符串 "1208/1242/1244" 格式 + /// + internal class ConverterWXToPddV5 : GoodsConverterBase + { + private JObject _jsonRoot; + private JObject _rawNode; // _raw 节点 + private JArray _skuInfoList; // _raw.skuList.skuInfo + private JArray _saleAttrs; // 顶层 saleAttrs 或 _raw.saleAttrs + private JArray _skuSpecs; // 顶层 skuSpecs + + public override string ConverterName => "微信转拼多多数据源 V5"; + + public override PlatformType Platform => PlatformType.WX; + + protected override bool IsMatchProvider() + { + try + { + _jsonRoot = JsonConvert.DeserializeObject(RawData); + if (_jsonRoot == null) return false; + + // 1. 检查 _raw 节点是否存在 + var raw = _jsonRoot.SelectToken("$._raw") as JObject; + if (raw == null) return false; + + // 2. 检查标题是否存在 + var title = raw.SelectToken("$.title")?.ToString(); + if (string.IsNullOrEmpty(title)) return false; + + // 3. 检查 headImg 是否存在且为数组 + var headImg = raw.SelectToken("$.headImg") as JArray; + if (headImg == null || headImg.Count == 0) return false; + + // 4. 检查 skuList.skuInfo 是否存在且为数组 + var skuList = raw.SelectToken("$.skuList") as JObject; + if (skuList == null) return false; + + var skuInfo = skuList.SelectToken("$.skuInfo") as JArray; + if (skuInfo == null || skuInfo.Count == 0) return false; + + // 5. 检查第一个SKU是否有skuId + var firstSku = skuInfo.FirstOrDefault() as JObject; + if (firstSku == null) return false; + + var skuId = firstSku.SelectToken("$.skuId")?.ToString(); + if (string.IsNullOrEmpty(skuId)) return false; + + // 缓存节点 + _rawNode = raw; + _skuInfoList = skuInfo; + _saleAttrs = _jsonRoot.SelectToken("$.saleAttrs") as JArray + ?? raw.SelectToken("$.saleAttrs") as JArray; + _skuSpecs = _jsonRoot.SelectToken("$.skuSpecs") as JArray; + + return true; + } + catch + { + return false; + } + } + + protected override PDDFrontProductInfo5 ConverterProvider() + { + var product = new PDDFrontProductInfo5 + { + Platform = Platform, + GoodsId = ParseGoodsId() + }; + + // ========== 1. 商品基本信息 ========== + product.Title = GetStringValue(_rawNode, "title"); + product.Description = GetStringValue(_rawNode, "subTitle"); + if (string.IsNullOrEmpty(product.Description)) + { + product.Description = GetStringValue(_jsonRoot, "subTitle"); + } + + // ========== 2. 品牌信息 ========== + product.BrandId = GetLongValue(_rawNode, "brandId"); + + // ========== 3. 类目信息 ========== + product.CategoryId = GetCategoryId(); + product.CategoryPath = GetCategoryPath(); + + // ========== 4. 主图 ========== + product.MainImages = GetMainImages(); + + // ========== 5. 详情图 ========== + product.DetailImages = GetDetailImages(); + + // ========== 6. 商品属性 ========== + product.Propertys = GetProperties(); + + // ========== 7. SKU和规格信息 ========== + var skuResult = GetSkusAndSpecs(); + product.Skus = skuResult.Item1; + product.Specs = skuResult.Item2; + + // ========== 8. 其他信息 ========== + product.ThumbUrl = product.MainImages.FirstOrDefault() ?? string.Empty; + product.MallId = 0; + product.Price = GetMinPrice(); + product.Videos = new List(); + + return product; + } + + #region 核心字段解析 + + /// + /// 解析商品ID + /// + private long ParseGoodsId() + { + // 优先从 productId / itemId 解析 + var productId = GetStringValue(_jsonRoot, "productId"); + if (string.IsNullOrEmpty(productId)) + productId = GetStringValue(_jsonRoot, "itemId"); + + if (long.TryParse(productId, out var result)) + return result; + + // 回退到 expressInfo.productId + var expressProductId = _rawNode.SelectToken("$.expressInfo.productId")?.ToString(); + if (long.TryParse(expressProductId, out var result2)) + return result2; + + // 最后回退到文件路径 + return Path.GetFileNameWithoutExtension(FilePath).ParseToLong(); + } + + /// + /// 获取类目ID(从 catId 字符串 "1208/1242/1244" 解析) + /// + private int GetCategoryId() + { + // 优先从顶层 catId 解析 + var catIdStr = GetStringValue(_jsonRoot, "catId"); + var catId = ParseLastCatId(catIdStr); + if (catId > 0) return catId; + + // 回退到 _raw.category 数组 + var category = _rawNode["category"] as JArray; + if (category != null && category.Count > 0) + { + var lastCat = category.Last as JObject; + var lastCatId = lastCat?["catId"]?.Value(); + if (int.TryParse(lastCatId, out var result)) + return result; + } + + // 回退到 nodeCatId + var nodeCatId = GetStringValue(_rawNode, "nodeCatId"); + if (int.TryParse(nodeCatId, out var nodeResult)) + return nodeResult; + + return 0; + } + + /// + /// 获取类目路径 + /// + private List GetCategoryPath() + { + var result = new List(); + + // 优先从 _raw.category 数组解析 + var category = _rawNode["category"] as JArray; + if (category != null && category.Count > 0) + { + foreach (var cat in category) + { + var _catIdStr = cat["catId"]?.Value(); + if (int.TryParse(_catIdStr, out var catId) && catId > 0) + result.Add(catId); + } + if (result.Count > 0) return result; + } + + // 回退到顶层 catId 字符串 + var catIdStr = GetStringValue(_jsonRoot, "catId"); + if (!string.IsNullOrEmpty(catIdStr)) + { + foreach (var part in catIdStr.Split('/')) + { + if (int.TryParse(part.Trim(), out var catId) && catId > 0) + result.Add(catId); + } + } + + return result; + } + + /// + /// 从 "1208/1242/1244" 解析最后一个类目ID + /// + private int ParseLastCatId(string catIdStr) + { + if (string.IsNullOrEmpty(catIdStr)) + return 0; + + var parts = catIdStr.Split('/'); + if (parts.Length == 0) return 0; + + var lastPart = parts[parts.Length - 1].Trim(); + return int.TryParse(lastPart, out var result) ? result : 0; + } + + /// + /// 获取主图列表 + /// + private List GetMainImages() + { + var result = new List(); + + // 优先从 _raw.headImg 解析 + var headImg = _rawNode["headImg"] as JArray; + if (headImg != null && headImg.Count > 0) + { + foreach (var img in headImg) + { + var url = img?.Value(); + if (!string.IsNullOrEmpty(url)) + result.Add(url); + } + return result; + } + + // 回退到顶层 headImg / headImgs + var topHeadImg = _jsonRoot.SelectToken("$.headImg") as JArray + ?? _jsonRoot.SelectToken("$.headImgs") as JArray; + if (topHeadImg != null) + { + foreach (var img in topHeadImg) + { + var url = img?.Value(); + if (!string.IsNullOrEmpty(url)) + result.Add(url); + } + } + + return result; + } + + /// + /// 获取详情图列表 + /// + private List GetDetailImages() + { + var result = new List(); + + // 从 _raw.detail.detailImg 解析 + var detail = _rawNode["detail"] as JObject; + if (detail == null) + return result; + + var detailImg = detail["detailImg"] as JArray; + if (detailImg == null) + return result; + + foreach (var img in detailImg) + { + var url = img?.Value(); + if (!string.IsNullOrEmpty(url)) + result.Add(url); + } + + return result; + } + + /// + /// 获取商品属性列表(从 _raw.param 解析) + /// + private List GetProperties() + { + var result = new List(); + var param = _rawNode["param"] as JArray; + if (param == null) + return result; + + foreach (var item in param) + { + var categorys = item["categorys"] as JArray; + if (categorys == null || categorys.Count < 2) + continue; + + var key = categorys[0]?["name"]?.Value(); + var value = categorys[1]?["name"]?.Value(); + + if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value)) + { + // 去重 + if (!result.Any(p => p.Key == key)) + { + result.Add(new Property + { + Key = key, + Value = value + }); + } + } + } + + return result; + } + + #endregion + + #region SKU和规格解析 + + /// + /// 获取SKU和规格信息 + /// + private Tuple ,List> GetSkusAndSpecs() + { + var skuList = new List(); + var specList = new List(); + + if (_skuInfoList == null || _skuInfoList.Count == 0) + return new Tuple, List>(skuList, specList); + + // 构建 skuSpecs 映射(skuId -> spec文本) + var skuSpecDict = new Dictionary(); + if (_skuSpecs != null) + { + foreach (var spec in _skuSpecs) + { + var skuId = spec["skuId"]?.Value(); + var specText = spec["spec"]?.Value(); + if (!string.IsNullOrEmpty(skuId) && !string.IsNullOrEmpty(specText)) + { + skuSpecDict[skuId] = specText; + } + } + } + + // 收集所有规格 + var specDict = new Dictionary>(); + + foreach (var sku in _skuInfoList) + { + var skuObj = sku as JObject; + if (skuObj == null) continue; + + var skuId = skuObj["skuId"]?.Value() ?? string.Empty; + if (string.IsNullOrEmpty(skuId)) continue; + + // 解析规格 + string specText = string.Empty; + var specValues = new List(); + + if (skuSpecDict.TryGetValue(skuId, out var cachedSpec)) + { + specText = cachedSpec; + // 解析 "颜色分类:牛油果/尺码:S" 格式 + foreach (var part in cachedSpec.Split('/')) + { + var kv = part.Split(':'); + if (kv.Length == 2) + { + var key = kv[0].Trim(); + var value = kv[1].Trim(); + specValues.Add($"{key}:{value}"); + + if (!specDict.ContainsKey(key)) + specDict[key] = new HashSet(); + specDict[key].Add(value); + } + } + } + + // 价格解析:优先使用 estimatePrice(优惠后),否则用 originPrice + var originPrice = skuObj["originPrice"]?.Value() ?? 0; + var estimatePrice = skuObj["estimatePrice"]?.Value() ?? 0; + var price = skuObj["price"]?.Value() ?? 0; + + // 实际售价:优先 estimatePrice,其次 price,最后 originPrice + var actualPrice = estimatePrice > 0 ? estimatePrice + : price > 0 ? price + : originPrice; + + // 库存:从 spuTotalStock 均分(SKU级别无库存字段) + var totalStock = GetTotalStock(); + var skuCount = _skuInfoList.Count; + var stockPerSku = skuCount > 0 ? totalStock / skuCount : 0; + + skuList.Add(new SkuItem + { + SkuId = skuId, + Spec = specText, + Price = actualPrice / 100, // 分转元 + OriginalPrice = originPrice.ToString(), + Stock = stockPerSku, + SpecImage = string.Empty // 该结构无SKU图片 + }); + } + + // 构建规格列表(优先从 saleAttrs 构建) + if (_saleAttrs != null && _saleAttrs.Count > 0) + { + foreach (var attr in _saleAttrs) + { + var key = attr["key"]?.Value() ?? string.Empty; + var values = attr["values"] as JArray; + if (values == null) continue; + + foreach (var value in values) + { + var valueStr = value?.Value() ?? string.Empty; + if (!string.IsNullOrEmpty(valueStr)) + { + specList.Add(new SpecItem + { + SpecKey = key, + SpecValue = valueStr, + SpecKeyId = 0, + SpecValueId = 0 + }); + } + } + } + } + else + { + // 回退到从SKU中收集的规格 + foreach (var spec in specDict) + { + foreach (var value in spec.Value) + { + specList.Add(new SpecItem + { + SpecKey = spec.Key, + SpecValue = value, + SpecKeyId = 0, + SpecValueId = 0 + }); + } + } + } + + return new Tuple, List>(skuList, specList); + } + + /// + /// 获取总库存 + /// + private int GetTotalStock() + { + var spuTotalStock = _rawNode["spuTotalStock"] as JObject; + if (spuTotalStock == null) return 0; + + var stockNumStr = spuTotalStock["stockNum"]?.Value(); + if (int.TryParse(stockNumStr, out var result)) + return result; + + return 0; + } + + /// + /// 获取最低价格(元) + /// + private decimal GetMinPrice() + { + if (_skuInfoList == null || _skuInfoList.Count == 0) + return 0; + + var minPrice = decimal.MaxValue; + + foreach (var sku in _skuInfoList) + { + var skuObj = sku as JObject; + if (skuObj == null) continue; + + var estimatePrice = skuObj["estimatePrice"]?.Value() ?? 0; + var price = skuObj["price"]?.Value() ?? 0; + var actualPrice = estimatePrice > 0 ? estimatePrice : price; + + if (actualPrice > 0 && actualPrice < minPrice) + minPrice = actualPrice; + } + + return minPrice > 0 && minPrice != decimal.MaxValue ? minPrice / 100 : 0; + } + + #endregion + + #region 辅助方法 + + private string GetStringValue(JObject obj, string path) + { + return obj?.SelectToken(path)?.Value() ?? string.Empty; + } + + private long GetLongValue(JObject obj, string path) + { + var token = obj?.SelectToken(path); + if (token == null) return 0; + + if (token.Type == JTokenType.String) + return long.TryParse(token.Value(), out var result) ? result : 0; + + return token.Value(); + } + + #endregion + } +} \ No newline at end of file diff --git a/TEMUKJD/WXForms/ProductForms/WXUserCollectAuthProdListForm.cs b/TEMUKJD/WXForms/ProductForms/WXUserCollectAuthProdListForm.cs index 14a2714..3e329e4 100644 --- a/TEMUKJD/WXForms/ProductForms/WXUserCollectAuthProdListForm.cs +++ b/TEMUKJD/WXForms/ProductForms/WXUserCollectAuthProdListForm.cs @@ -1210,7 +1210,7 @@ namespace TemuKJD.WXForms.ProductForms OpenFileDialog openFileDialog = new OpenFileDialog(); openFileDialog.Multiselect = true; // 设置过滤条件,只显示文本文件 - openFileDialog.Filter = "文本文件 (*.txt)|*.txt|All files (*.*)|*.*"; + openFileDialog.Filter = "文本文件 (*.txt;*.json)|*.txt;*.json|All files (*.*)|*.*"; // 显示对话框并获取结果 if (openFileDialog.ShowDialog() == DialogResult.OK) { diff --git a/test/KJD.Test/UnitTest1.cs b/test/KJD.Test/UnitTest1.cs index 17f81b7..c7efb1f 100644 --- a/test/KJD.Test/UnitTest1.cs +++ b/test/KJD.Test/UnitTest1.cs @@ -138,6 +138,16 @@ namespace KJD.Test } } + + + [Test] + public void TestWXToPDDV4() + { + string filePath = @"C:\Users\chaih\Documents\xwechat_files\hongfei357_a567\msg\file\2026-10\10000033063024.json"; + var frontProdInfo = ConverterToPddFactory.Convert(filePath); + var isMatch = frontProdInfo != null; + Console.WriteLine(frontProdInfo.Skus.Count); + } } }