修改代码
增加云商品数据
This commit is contained in:
1 parent
2f406c840f
commit
db4f030c17
58 files changed
+1550
-120
No files matched your search
@@ -0,0 +1,107 @@
|
||||
using Store.Modules.CloudProduct.Entities;
|
||||
using Store.Modules.CloudProduct.Enums;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace Store.Modules.CloudProduct.ImportConverters
|
||||
{
|
||||
/// <summary>
|
||||
/// 导入商品数据转换器
|
||||
/// </summary>
|
||||
public interface IGoodsConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// 原始数据(JSON 原文)
|
||||
/// </summary>
|
||||
string RawData { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取转换器名称
|
||||
/// </summary>
|
||||
string ConverterName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 支持的平台类型
|
||||
/// </summary>
|
||||
PlatformType Platform { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 读取内容
|
||||
/// </summary>
|
||||
/// <param name="content">JSON 原文内容</param>
|
||||
void ReadContent(string content);
|
||||
|
||||
/// <summary>
|
||||
/// 判断是否匹配当前转换器
|
||||
/// </summary>
|
||||
/// <returns>是否匹配</returns>
|
||||
bool IsMatch();
|
||||
|
||||
/// <summary>
|
||||
/// 转换为标准商品模型
|
||||
/// </summary>
|
||||
GoodsEntity Convert();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转换器基类:统一实现读取、校验与转换骨架,子类只需实现匹配判定与字段映射。
|
||||
/// </summary>
|
||||
public abstract class GoodsConverter : IGoodsConverter
|
||||
{
|
||||
private bool _isMatch = false;
|
||||
|
||||
public string RawData { get; private set; } = null!;
|
||||
|
||||
public abstract string ConverterName { get; }
|
||||
|
||||
public abstract PlatformType Platform { get; }
|
||||
|
||||
void IGoodsConverter.ReadContent(string content)
|
||||
{
|
||||
ReadContent(content);
|
||||
}
|
||||
|
||||
protected virtual void ReadContent(string content)
|
||||
{
|
||||
RawData = content;
|
||||
}
|
||||
|
||||
public virtual bool IsMatch()
|
||||
{
|
||||
_isMatch = true;
|
||||
return IsMatchCore();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 由子类实现的具体匹配判定
|
||||
/// </summary>
|
||||
protected abstract bool IsMatchCore();
|
||||
|
||||
/// <summary>
|
||||
/// 带校验的转换(基类统一实现)
|
||||
/// </summary>
|
||||
public virtual GoodsEntity Convert()
|
||||
{
|
||||
if (_isMatch == false) throw new Exception("未校验数据");
|
||||
try
|
||||
{
|
||||
JsonNode.Parse(RawData);
|
||||
}
|
||||
catch { throw new Exception("数据解析失败,不是有效的JSON格式"); }
|
||||
|
||||
var goods = ConvertCore();
|
||||
if (goods == null)
|
||||
throw new Exception("转换失败");
|
||||
|
||||
// 基础校验
|
||||
if (goods.GoodsId == 0)
|
||||
throw new Exception("商品ID不能为空");
|
||||
|
||||
return goods;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 由子类实现的具体字段映射
|
||||
/// </summary>
|
||||
protected abstract GoodsEntity ConvertCore();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using Store.Modules.CloudProduct.Entities;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Store.Modules.CloudProduct.ImportConverters
|
||||
{
|
||||
/// <summary>
|
||||
/// 导入商品转换工厂:按顺序尝试各转换器,返回标准商品模型。
|
||||
/// </summary>
|
||||
public class GoodsConverterFactory
|
||||
{
|
||||
private static readonly List<IGoodsConverter> _converters = new List<IGoodsConverter>
|
||||
{
|
||||
new PddGoodsConverter(),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// 根据本地 JSON 文件路径转换。
|
||||
/// </summary>
|
||||
public static GoodsEntity? Convert(string filePath)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(filePath))
|
||||
throw new ArgumentNullException(nameof(filePath));
|
||||
|
||||
if (!File.Exists(filePath))
|
||||
throw new FileNotFoundException("找不到导入文件", filePath);
|
||||
|
||||
var content = File.ReadAllText(filePath);
|
||||
return ConvertContent(content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据 JSON 内容字符串转换。
|
||||
/// </summary>
|
||||
public static GoodsEntity? ConvertContent(string content)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(content))
|
||||
throw new ArgumentNullException(nameof(content));
|
||||
|
||||
foreach (var converter in _converters)
|
||||
{
|
||||
converter.ReadContent(content);
|
||||
if (converter.IsMatch())
|
||||
{
|
||||
var goods = converter.Convert();
|
||||
if (goods != null)
|
||||
return goods;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
using Store.Modules.CloudProduct.Entities;
|
||||
using Store.Modules.CloudProduct.Enums;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace Store.Modules.CloudProduct.ImportConverters
|
||||
{
|
||||
/// <summary>
|
||||
/// PDD 前端商品详情数据转换器(数据源格式见 data.txt)
|
||||
/// </summary>
|
||||
internal class PddGoodsConverter : GoodsConverter
|
||||
{
|
||||
public override string ConverterName => nameof(PddGoodsConverter);
|
||||
|
||||
public override PlatformType Platform => PlatformType.PDD;
|
||||
|
||||
protected override bool IsMatchCore()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (JsonNode.Parse(RawData) is not JsonObject root)
|
||||
return false;
|
||||
|
||||
// data.txt:顶层包含 goods.goods_id
|
||||
if (root["goods"] is JsonObject goodsObj && goodsObj["goods_id"] != null)
|
||||
return true;
|
||||
|
||||
// 也兼容顶层直接是商品对象的情况
|
||||
if (root["goods_id"] != null && (root["goods_name"] != null || root["title"] != null))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected override GoodsEntity ConvertCore()
|
||||
{
|
||||
if (JsonNode.Parse(RawData) is not JsonObject root)
|
||||
throw new Exception("无法解析 PDD 商品数据");
|
||||
|
||||
var goodsNode = root["goods"] as JsonObject ?? root;
|
||||
var priceNode = root["price"] as JsonObject;
|
||||
|
||||
var goods = new GoodsEntity
|
||||
{
|
||||
Platform = (byte)PlatformType.PDD,
|
||||
GoodsId = GetLong(goodsNode, "goods_id"),
|
||||
Title = GetString(goodsNode, "goods_name")
|
||||
?? GetString(goodsNode, "short_name")
|
||||
?? string.Empty,
|
||||
Description = GetString(goodsNode, "share_desc") ?? GetString(goodsNode, "short_name"),
|
||||
ThumbUrl = GetString(goodsNode, "hd_url") ?? GetString(goodsNode, "thumb_url") ?? GetString(goodsNode, "hd_thumb_url") ?? GetString(goodsNode, "image_url"),
|
||||
CatId1 = GetIntOrNull(goodsNode, "cat_id_1"),
|
||||
CatId2 = GetIntOrNull(goodsNode, "cat_id_2"),
|
||||
CatId3 = GetIntOrNull(goodsNode, "cat_id_3"),
|
||||
CatId4 = GetIntOrNull(goodsNode, "cat_id_4"),
|
||||
SoldQuantity = GetInt(goodsNode, "sold_quantity"),
|
||||
Status = 0,
|
||||
};
|
||||
|
||||
var price = GetPriceRange(priceNode, root);
|
||||
goods.MinPrice = price.Min;
|
||||
goods.MaxPrice = price.Max;
|
||||
|
||||
return goods;
|
||||
}
|
||||
|
||||
private static string? GetString(JsonObject? obj, string key)
|
||||
{
|
||||
if (obj == null || !obj.TryGetPropertyValue(key, out var node) || node == null)
|
||||
return null;
|
||||
|
||||
if (node is JsonValue value && value.TryGetValue<string>(out var str))
|
||||
return str;
|
||||
|
||||
return node.ToJsonString();
|
||||
}
|
||||
|
||||
private static long GetLong(JsonObject? obj, string key)
|
||||
{
|
||||
if (obj == null || !obj.TryGetPropertyValue(key, out var node) || node == null)
|
||||
return 0;
|
||||
|
||||
if (node is JsonValue value)
|
||||
{
|
||||
if (value.TryGetValue<long>(out var l)) return l;
|
||||
if (value.TryGetValue<decimal>(out var d)) return (long)d;
|
||||
if (long.TryParse(value.ToString(), out var l2)) return l2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int GetInt(JsonObject? obj, string key)
|
||||
{
|
||||
if (obj == null || !obj.TryGetPropertyValue(key, out var node) || node == null)
|
||||
return 0;
|
||||
|
||||
if (node is JsonValue value)
|
||||
{
|
||||
if (value.TryGetValue<int>(out var i)) return i;
|
||||
if (value.TryGetValue<long>(out var l)) return (int)l;
|
||||
if (int.TryParse(value.ToString(), out var i2)) return i2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int? GetIntOrNull(JsonObject? obj, string key)
|
||||
{
|
||||
if (obj == null || !obj.TryGetPropertyValue(key, out var node) || node == null)
|
||||
return null;
|
||||
|
||||
if (node is JsonValue value)
|
||||
{
|
||||
if (value.TryGetValue<int>(out var i) && i > 0) return i;
|
||||
if (value.TryGetValue<long>(out var l) && l > 0) return (int)l;
|
||||
if (int.TryParse(value.ToString(), out var i2) && i2 > 0) return i2;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static long? GetLongOrNull(JsonObject? obj, string key)
|
||||
{
|
||||
if (obj == null || !obj.TryGetPropertyValue(key, out var node) || node == null)
|
||||
return null;
|
||||
|
||||
if (node is JsonValue value)
|
||||
{
|
||||
if (value.TryGetValue<long>(out var d)) return d;
|
||||
if (value.TryGetValue<long>(out var l)) return l;
|
||||
if (long.TryParse(value.ToString(), out var d2)) return d2;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算价格区间。优先使用顶层 price 节点,其次遍历 sku。
|
||||
/// </summary>
|
||||
private static (long? Min, long? Max) GetPriceRange(JsonObject? priceNode, JsonObject root)
|
||||
{
|
||||
long? min = null;
|
||||
long? max = null;
|
||||
|
||||
if (priceNode != null)
|
||||
{
|
||||
min = GetLongOrNull(priceNode, "min_group_price");
|
||||
max = GetLongOrNull(priceNode, "max_group_price");
|
||||
if (min.HasValue || max.HasValue)
|
||||
return (min, max);
|
||||
}
|
||||
|
||||
if (root["sku"] is JsonArray skuArray && skuArray.Count > 0)
|
||||
{
|
||||
foreach (var item in skuArray.OfType<JsonObject>())
|
||||
{
|
||||
var price = GetLongOrNull(item, "group_price") ?? GetLongOrNull(item, "normal_price");
|
||||
if (price == null) continue;
|
||||
|
||||
min = min.HasValue ? Math.Min(min.Value, price.Value) : price;
|
||||
max = max.HasValue ? Math.Max(max.Value, price.Value) : price;
|
||||
}
|
||||
|
||||
if (min.HasValue || max.HasValue)
|
||||
return (min, max);
|
||||
}
|
||||
|
||||
return (null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user