新增导入格式

新增文件编码识别
新增编码 GB18030
This commit is contained in:
chfei committed 2026-08-21 16:16:38 +08:00
1 parent 1aa0954e43
commit b949d0a67e
9 files changed
+19668 -4

No files matched your search

+2
View File
@@ -173,7 +173,9 @@
<Compile Include="Helper\CommonSimRequest.cs" />
<Compile Include="Helper\DataOpenSSL.cs" />
<Compile Include="Helper\FissionDataBaseConfig.cs" />
<Compile Include="Helper\GB18030EncodingProvider.cs" />
<Compile Include="Helper\GlobalConfig.cs" />
<Compile Include="Helper\UDE.cs" />
<Compile Include="HttpUtility.cs" />
<Compile Include="IECookies.cs" />
<Compile Include="ImageBuild.cs" />
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -14,6 +14,7 @@ namespace TemuKJD.BLL.ImportConverts
{
new ConverterToPddV5(),
new ConverterWXToPddV1(),
new ConverterWXToPddV2(),
};
public static PDDFrontProductInfo5 Convert(string filePath)
{
@@ -0,0 +1,372 @@
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;
}
}
}
@@ -64,7 +64,7 @@ namespace TemuKJD.BLL.ImportConverters
{
if (File.Exists(filePath) == false) throw new Exception("文件不存在");
FilePath = filePath;
using (StreamReader sr = new StreamReader(filePath, Work.Api.UploadPinduoduoMain.GetFileEncoding(filePath)))
using (StreamReader sr = new StreamReader(filePath, Ude.CharsetDetector.GetEncoding(filePath)))
{
RawData = sr.ReadToEnd();
}
+1 -1
View File
@@ -1922,7 +1922,7 @@ namespace TemuKJD.BLL
maxPrice = Math.Max(maxPrice, skuInfo.price.Value);
minPrice = Math.Min(minPrice, skuInfo.price.Value);
var specList = skuItem.Spec.Split(';').ToList();
var specList = skuItem.Spec.Split(new char[';'], StringSplitOptions.RemoveEmptyEntries).ToList();
foreach (var spec in specList)
{
string specKey = spec.Split(':')[0];
+4 -1
View File
@@ -112,7 +112,9 @@
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System">
<HintPath>C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.2\System.dll</HintPath>
</Reference>
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
@@ -145,6 +147,7 @@
<Compile Include="BLL\AuthorBLL.cs" />
<Compile Include="BLL\HttpResponseWxAiTalkBLL.cs" />
<Compile Include="BLL\ImportConverters\ConverterWXToPddV1.cs" />
<Compile Include="BLL\ImportConverters\ConverterWXToPddV2.cs" />
<Compile Include="BLL\ImportConverters\ConvertToPddFactory.cs" />
<Compile Include="BLL\ImportConverters\ConverterToPddV5.cs" />
<Compile Include="BLL\ImportConverters\IGoodsConverter.cs" />
+13 -1
View File
@@ -1,5 +1,7 @@
using TemuKJD.BLL;
using System.Text;
using TemuKJD.BLL;
using TemuKJD.BLL.ImportConverts;
using Work.GoodDetail;
namespace KJD.Test
{
@@ -89,5 +91,15 @@ namespace KJD.Test
}
}
}
[Test]
public void TestWXToPDDV2()
{
string filePath = @"C:\Users\chaih\Documents\xwechat_files\hongfei357_a567\msg\file\2026-08\10000525304068.txt";
var frontProdInfo = ConverterToPddFactory.Convert(filePath);
var isMatch = frontProdInfo != null;
}
}
}