Files
KJD/Work/Services/CloudGoodsService.cs
T
2026-10-30 11:30:26 +08:00

505 lines
12 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;
using AllBase.Helper;
using AllBase.Model.Result;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Work.Services
{
/// <summary>
/// 云商品数据服务
/// </summary>
public class CloudGoodsService
{
private static string ServerUrl = "http://103.90.155.78:9001";
static CloudGoodsService()
{
if (EnvironmentHelper.IsDevelopment)
{
ServerUrl = "http://localhost:14312";
}
}
public static async Task<PageData<CloudGoods>> GetGoods(GoodsSearchRequest request)
{
var html = await GetHtml.PostJsonAsync(ServerUrl + "/api/CloudGoods/SearchGoods", request);
var ret = JsonConvert.DeserializeObject<Result<PageData<CloudGoods>>>(html);
if (ret.Code != 200)
{
throw new Exception(ret.Msg);
}
return ret.Data;
}
public static string GetGoodsDetail(long goodsId)
{
var html = GetHtml.PostJson(ServerUrl + $"/api/CloudGoods/GetGoodsRawData?goodsId={goodsId}", "");
var ret = JsonConvert.DeserializeObject<Result<string>>(html);
if (ret.Code != 200)
{
throw new Exception(ret.Msg);
}
return ret.Data;
}
/// <summary>
/// 在后台解析微信处罚 JSON 并上报;无商品或处理失败时返回空列表,异常在内部记录。
/// </summary>
public static void ReportViolationsFromJson(string json)
{
try
{
Task.Run(() => ReportViolationsFromJsonCore(json)).ConfigureAwait(false);
}
catch (Exception ex)
{
try
{
LogSave.AddFile("解析并上报违规商品失败:" + ex.Message);
}
catch
{
// 日志失败也不能影响调用方。
}
}
}
private static async Task<List<ReportGoodsViolationResult>> ReportViolationsFromJsonCore(string json)
{
if (string.IsNullOrWhiteSpace(json))
{
throw new ArgumentException("处罚 JSON 文本不能为空。", nameof(json));
}
JObject root;
try
{
root = JObject.Parse(json);
}
catch (JsonException ex)
{
throw new ArgumentException("处罚 JSON 格式不正确。", nameof(json), ex);
}
var record = root["record"] as JObject;
if (root["code"]?.ToString() != "0" || record?["code"]?.ToString() != "0")
{
throw new ArgumentException("处罚数据的 code 或 record.code 不为 0,或缺少 record。", nameof(json));
}
if (!(record["items"] is JArray records))
{
throw new ArgumentException("处罚数据缺少 record.items 数组。", nameof(json));
}
var items = new List<ReportGoodsViolationItem>();
foreach (var token in records)
{
if (!(token is JObject punishment))
{
throw new ArgumentException("record.items 中的处罚记录必须是对象。", nameof(json));
}
var objects = punishment["punishObject"];
if (objects == null || objects.Type == JTokenType.Null)
{
continue;
}
if (!(objects is JArray products))
{
throw new ArgumentException("punishObject 必须是数组。", nameof(json));
}
foreach (var product in products)
{
if (!(product is JObject target))
{
throw new ArgumentException("punishObject 中的处罚对象必须是对象。", nameof(json));
}
var id = target["productId"];
// 订单等非商品处罚对象不包含 productId。
if (id == null)
{
continue;
}
if ((id.Type != JTokenType.String && id.Type != JTokenType.Integer)
|| !long.TryParse(id.ToString(), out var goodsId) || goodsId <= 0)
{
throw new ArgumentException("商品 productId 必须是有效的正整数编号。", nameof(json));
}
var reason = punishment["punishReason"];
if (reason?.Type != JTokenType.String || string.IsNullOrWhiteSpace((string)reason))
{
throw new ArgumentException($"商品 {goodsId} 缺少有效的 punishReason。", nameof(json));
}
items.Add(new ReportGoodsViolationItem { GoodsId = goodsId, Reasons = ((string)reason).Trim() });
}
}
var reports = items.GroupBy(item => item.GoodsId).Select(group => new ReportGoodsViolationItem
{
GoodsId = group.Key,
Reasons = string.Join(";", group.Select(item => item.Reasons).Distinct())
}).ToList();
return reports.Count == 0 ? new List<ReportGoodsViolationResult>() : await ReportViolations(reports);
}
/// <summary>
/// 批量上报违规商品。
/// </summary>
public static async Task<List<ReportGoodsViolationResult>> ReportViolations(List<ReportGoodsViolationItem> items)
{
if (items == null)
{
throw new ArgumentNullException(nameof(items));
}
if (items.Any(item => item == null))
{
throw new ArgumentException("违规商品列表不能包含空项。", nameof(items));
}
var html = await GetHtml.PostJsonAsync(ServerUrl + "/api/CloudGoods/ReportViolations", items);
var ret = JsonConvert.DeserializeObject<Result<List<ReportGoodsViolationResult>>>(html);
if (ret == null)
{
throw new Exception("批量上报违规商品返回为空。");
}
if (ret.Code != 200)
{
throw new Exception(ret.Msg);
}
return ret.Data ?? throw new Exception("批量上报违规商品返回数据为空。");
}
/// <summary>
/// 批量查询商品违规状态。
/// </summary>
public static async Task<List<GoodsViolationStatus>> GetViolationStatus(List<long> goodsIds)
{
if (goodsIds == null)
{
throw new ArgumentNullException(nameof(goodsIds));
}
var html = await GetHtml.PostJsonAsync(ServerUrl + "/api/CloudGoods/GetViolationStatus", goodsIds);
var ret = JsonConvert.DeserializeObject<Result<List<GoodsViolationStatus>>>(html);
if (ret == null)
{
throw new Exception("批量查询商品违规状态返回为空。");
}
if (ret.Code != 200)
{
throw new Exception(ret.Msg);
}
return ret.Data ?? throw new Exception("批量查询商品违规状态返回数据为空。");
}
#region user
/// <summary>
/// 搜索收藏
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static async Task<PageData<CloudGoods>> SearchFavorites(UserFavoritesGoodsSearchRequest request)
{
request.UserName = GetUserName();
var html = await GetHtml.PostJsonAsync(ServerUrl + "/api/CloudGoods/SearchFavorites", request);
var ret = JsonConvert.DeserializeObject<Result<PageData<CloudGoods>>>(html);
if (ret.Code != 200)
{
throw new Exception(ret.Msg);
}
return ret.Data;
}
/// <summary>
/// 收藏
/// </summary>
/// <param name="goodsIds"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static async Task<int> Favorite(List<long> goodsIds)
{
var html = await GetHtml.PostJsonAsync(ServerUrl + $"/api/CloudGoods/AddFavorite", new
{
UserName = GetUserName(),
GoodsIds = goodsIds,
});
var ret = JsonConvert.DeserializeObject<Result<int>>(html);
if (ret.Code != 200)
{
throw new Exception(ret.Msg);
}
return ret.Data;
}
/// <summary>
/// 取消收藏
/// </summary>
/// <param name="goodsIds"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static async Task<int> RemoveFavorites(List<long> goodsIds)
{
var html = await GetHtml.PostJsonAsync(ServerUrl + $"/api/CloudGoods/RemoveFavorites", new
{
UserName = GetUserName(),
GoodsIds = goodsIds,
});
var ret = JsonConvert.DeserializeObject<Result<int>>(html);
if (ret.Code != 200)
{
throw new Exception(ret.Msg);
}
return ret.Data;
}
#endregion
public static string GetUserName()
{
return string.IsNullOrEmpty(BaseClass.ParentUserName) ? BaseClass.UserName : BaseClass.ParentUserName;
}
}
/// <summary>
/// 违规商品上报项。
/// </summary>
public class ReportGoodsViolationItem
{
[JsonProperty("goodsId")]
public long GoodsId { get; set; }
[JsonProperty("reasons")]
public string Reasons { get; set; } = string.Empty;
}
/// <summary>
/// 单个商品的违规上报结果。
/// </summary>
public class ReportGoodsViolationResult
{
[JsonProperty("goodsId")]
public long GoodsId { get; set; }
[JsonProperty("isOffShelf")]
public bool IsOffShelf { get; set; }
}
/// <summary>
/// 商品违规状态。
/// </summary>
public class GoodsViolationStatus
{
[JsonProperty("goodsId")]
public long GoodsId { get; set; }
[JsonProperty("isViolation")]
public bool IsViolation { get; set; }
}
/// <summary>
/// 商品搜索请求
/// </summary>
public class GoodsSearchRequest
{
/// <summary>
/// 标题(模糊搜索)
/// </summary>
public string Title { get; set; } = string.Empty;
/// <summary>
/// 最低价格(元)
/// </summary>
public long? MinPrice { get; set; }
/// <summary>
/// 最高价格(元)
/// </summary>
public long? MaxPrice { get; set; }
/// <summary>
/// 一级类目ID
/// </summary>
public int? CatId1 { get; set; }
/// <summary>
/// 二级类目ID
/// </summary>
public int? CatId2 { get; set; }
/// <summary>
/// 三级类目ID
/// </summary>
public int? CatId3 { get; set; }
/// <summary>
/// 四级类目ID
/// </summary>
public int? CatId4 { get; set; }
/// <summary>
/// 最低销量
/// </summary>
public int? MinSoldQuantity { get; set; }
/// <summary>
/// 最高销量
/// </summary>
public int? MaxSoldQuantity { get; set; }
/// <summary>
/// 平台类型
/// </summary>
public PlatformType Platform { get; set; }
/// <summary>
/// 页码(默认1)
/// </summary>
public int PageIndex { get; set; } = 1;
/// <summary>
/// 每页数量(默认20)
/// </summary>
public int PageSize { get; set; } = 20;
}
public class UserFavoritesGoodsSearchRequest : GoodsSearchRequest
{
public string UserName { get; set; }
}
/// <summary>
/// 商品搜索返回结果
/// </summary>
public class GoodsSearchResponse
{
/// <summary>
/// 商品列表
/// </summary>
[JsonProperty("list")]
public List<CloudGoods> List { get; set; } = new List<CloudGoods>();
/// <summary>
/// 总记录数
/// </summary>
[JsonProperty("total")]
public int Total { get; set; }
/// <summary>
/// 当前页码
/// </summary>
[JsonProperty("pageIndex")]
public int PageIndex { get; set; }
/// <summary>
/// 每页数量
/// </summary>
[JsonProperty("pageSize")]
public int PageSize { get; set; }
/// <summary>
/// 总页数
/// </summary>
[JsonProperty("totalPages")]
public int TotalPages { get; set; }
}
public class CloudGoods : BindableBase
{
public bool IsSelected
{
get => GetValue<bool>();
set => SetValue(value);
}
/// <summary>
/// 自增ID
/// </summary>
public long Id { get; set; }
/// <summary>
/// 平台类型
/// </summary>
public byte Platform { get; set; }
/// <summary>
/// 商品编号
/// </summary>
public long GoodsId { get; set; }
/// <summary>
/// 标题
/// </summary>
public string Title { get; set; } = string.Empty;
/// <summary>
/// 描述
/// </summary>
public string? Description { get; set; }
/// <summary>
/// 缩略图
/// </summary>
public string? ThumbUrl { get; set; }
/// <summary>
/// 一级类目ID
/// </summary>
public int? CatId1 { get; set; }
/// <summary>
/// 二级类目ID
/// </summary>
public int? CatId2 { get; set; }
/// <summary>
/// 三级类目ID
/// </summary>
public int? CatId3 { get; set; }
/// <summary>
/// 四级类目ID
/// </summary>
public int? CatId4 { get; set; }
/// <summary>
/// 最低价快照(元),导入时计算
/// </summary>
public decimal MinPrice { get; set; }
/// <summary>
/// 最高价快照(元),导入时计算
/// </summary>
public decimal MaxPrice { get; set; }
public string Price => $"{MinPrice / 100m:N2}";
/// <summary>
/// 销量
/// </summary>
public int SoldQuantity { get; set; }
/// <summary>
/// 状态:0正常 1已删除
/// </summary>
public byte Status { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreatedTime { get; set; }
/// <summary>
/// 更新时间(自动更新)
/// </summary>
public DateTime UpdatedTime { get; set; }
/// <summary>
/// 索引同步状态:0未同步 1已同步
/// </summary>
public byte IndexStatus { get; set; }
}
}