From a319af0e91e765ea771222ecd8915ae8cd110c1c Mon Sep 17 00:00:00 2001 From: chfei Date: Fri, 30 Oct 2026 11:30:26 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=8A=E4=BC=A0=E8=BF=9D?= =?UTF-8?q?=E8=A7=84=E5=95=86=E5=93=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProductForms/WXProdViolationManage.cs | 297 +++++++++--------- Work/Services/CloudGoodsService.cs | 186 +++++++++++ Work/Services/ImageCacheService.cs | 20 +- 3 files changed, 347 insertions(+), 156 deletions(-) diff --git a/TEMUKJD/PDDForms/ProductForms/WXProdViolationManage.cs b/TEMUKJD/PDDForms/ProductForms/WXProdViolationManage.cs index 5e8cb41..90c491d 100644 --- a/TEMUKJD/PDDForms/ProductForms/WXProdViolationManage.cs +++ b/TEMUKJD/PDDForms/ProductForms/WXProdViolationManage.cs @@ -12,168 +12,171 @@ using System.Threading.Tasks; using TemuKJD.BLL; using TemuKJD.WXForms.ProductForms; using Work; +using Work.Services; namespace TemuKJD.PDDForms.ProductForms { - // 微信店铺违规商品抓取 - public class WXProdViolationManage - { - public void GetShopProdViolation() - { - Task.Run(() => - { - if (!Directory.Exists("wxProdViolation")) - Directory.CreateDirectory("wxProdViolation"); + // 微信店铺违规商品抓取 + public class WXProdViolationManage + { + public void GetShopProdViolation() + { + Task.Run(() => + { + if (!Directory.Exists("wxProdViolation")) + Directory.CreateDirectory("wxProdViolation"); - while (true) - { - //break; - foreach (var shop in GlobalConfig.ShopLoginList) - { - if (shop.LoginStatus != "登录成功") - continue; + while (true) + { + //break; + foreach (var shop in GlobalConfig.ShopLoginList) + { + if (shop.LoginStatus != "登录成功") + continue; - Thread.Sleep(Randomx.GetId(500, 1500)); + Thread.Sleep(Randomx.GetId(500, 1500)); - WXProdViolationGetInfo info = new WXProdViolationGetInfo() { shopId = shop.ShopId, updateDay = DateTime.Now.ToString("yyMMdd") }; - try - { - info = NaiBa.Util.Helper.ConfigHelper.LoadConfig("wxProdViolation\\" + shop.ShopId); - if (info == null) - info = new WXProdViolationGetInfo() { shopId = shop.ShopId, updateDay = DateTime.MinValue.ToString("yyMMdd") }; + WXProdViolationGetInfo info = new WXProdViolationGetInfo() { shopId = shop.ShopId, updateDay = DateTime.Now.ToString("yyMMdd") }; + try + { + info = NaiBa.Util.Helper.ConfigHelper.LoadConfig("wxProdViolation\\" + shop.ShopId); + if (info == null) + info = new WXProdViolationGetInfo() { shopId = shop.ShopId, updateDay = DateTime.MinValue.ToString("yyMMdd") }; - if (info.updateDay == DateTime.Now.ToString("yyMMdd")) - { - // 今日同步过,跳过,暂时关闭改为每次打开立即读取 - //continue; - } + if (info.updateDay == DateTime.Now.ToString("yyMMdd")) + { + // 今日同步过,跳过,暂时关闭改为每次打开立即读取 + //continue; + } - info.failReason = ""; + info.failReason = ""; - var result = getShopiolationProd(shop); - Thread.Sleep(Randomx.GetId(1000, 2000)); - if (!result.Contains("\"code\":0")) - { - WXForms.ShopForms.WXShopListForm.CheckShopLogin(shop); + var result = getShopiolationProd(shop); + CloudGoodsService.ReportViolationsFromJson(result); - info.failReason = "读取失败"; - NaiBa.Util.Helper.ConfigHelper.SaveConfig("wxProdViolation\\" + shop.ShopId, info); - continue; - } + Thread.Sleep(Randomx.GetId(1000, 2000)); + if (!result.Contains("\"code\":0")) + { + WXForms.ShopForms.WXShopListForm.CheckShopLogin(shop); - JObject recordObj = JsonHelper.JObjectParse(result)["record"] as JObject; - JArray jArr = JsonHelper.JObjectParse(result)["record"]["items"] as JArray; - if (recordObj != null && jArr == null) - { - jArr = new JArray(); - } - foreach(var jObj in jArr) - { - string sVioInfo = JsonHelper.SerializeObject(jObj); - var punishUuid = BaseRegex.GetStringJson(ref sVioInfo, "punishUuid"); - var punishReason = BaseRegex.GetStringJson(ref sVioInfo, "punishReason"); - var punishTime = BaseClass.StampToDateTime(BaseRegex.GetIntJson(ref sVioInfo, "punishTime")); - var productId = BaseRegex.GetStringJson(ref sVioInfo, "productId"); - var productDesc = BaseRegex.GetStringJson(ref sVioInfo, "productDesc"); - //相似同款低质商品 不当使用他人权利 - if (!punishReason.Contains("发布违禁信息") && !punishReason.Contains("材质夸大宣传") && !punishReason.Contains("相似同款低质商品") && - !punishReason.Contains("价格违规") && !punishReason.Contains("售假") && !punishReason.Contains("品牌资质问题") && - !punishReason.Contains("不当使用他人权利") && !punishReason.Contains("用户反馈虚假宣传")) - continue; - if (!string.IsNullOrWhiteSpace(productDesc) && !string.IsNullOrWhiteSpace(productId)) - { - var url = BaseClass.BuildPDDObjString("product/mdy_violation_prod"); - var data = UserLogin.GetSignaturex(); - data += "&wx_safe_status=违规商品"; - data += "¬es=" + GetHtml.UrlEncodeUtf8(("违规轮询-" + shop.ShopId + "-" + punishUuid + "-" + punishReason + "-" + punishTime.ToString("yy/MM/dd HH:mm")).Trim()); - data += "&product_name=" + productDesc; - var html = GetHtml.PostUtf8(url, data); - if (string.IsNullOrEmpty(html) || !html.Contains("\"code\":200")) - html = GetHtml.PostUtf8(url, data); - if (!html.Contains("\"code\":200")) - { - info.failReason = "更新商品状态失败"; - break; - } + info.failReason = "读取失败"; + NaiBa.Util.Helper.ConfigHelper.SaveConfig("wxProdViolation\\" + shop.ShopId, info); + continue; + } - if(html.Contains("商品不存在")) - { - var getProdRst = WXUpload.searchProductWithTitle(shop, GetHtml.UrlEncodeUtf8(productDesc)); - var spuCode = BaseRegex.GetStringJson(ref getProdRst, "spuCode"); - var secrec_spu_id = ""; - var spu_id = ""; - if (string.IsNullOrEmpty(spuCode) || spuCode.Length < 10) - { - continue; - } - if (spuCode.StartsWith("EC1") && spuCode.EndsWith("A")) - { - spu_id = spuCode.Substring(3, spuCode.Length - 4); - } - else if (spuCode.StartsWith("P1-")) - { - spu_id = spuCode.Substring(3); - } - else - { - secrec_spu_id = spuCode; - } + JObject recordObj = JsonHelper.JObjectParse(result)["record"] as JObject; + JArray jArr = JsonHelper.JObjectParse(result)["record"]["items"] as JArray; + if (recordObj != null && jArr == null) + { + jArr = new JArray(); + } + foreach (var jObj in jArr) + { + string sVioInfo = JsonHelper.SerializeObject(jObj); + var punishUuid = BaseRegex.GetStringJson(ref sVioInfo, "punishUuid"); + var punishReason = BaseRegex.GetStringJson(ref sVioInfo, "punishReason"); + var punishTime = BaseClass.StampToDateTime(BaseRegex.GetIntJson(ref sVioInfo, "punishTime")); + var productId = BaseRegex.GetStringJson(ref sVioInfo, "productId"); + var productDesc = BaseRegex.GetStringJson(ref sVioInfo, "productDesc"); + //相似同款低质商品 不当使用他人权利 + if (!punishReason.Contains("发布违禁信息") && !punishReason.Contains("材质夸大宣传") && !punishReason.Contains("相似同款低质商品") && + !punishReason.Contains("价格违规") && !punishReason.Contains("售假") && !punishReason.Contains("品牌资质问题") && + !punishReason.Contains("不当使用他人权利") && !punishReason.Contains("用户反馈虚假宣传")) + continue; + if (!string.IsNullOrWhiteSpace(productDesc) && !string.IsNullOrWhiteSpace(productId)) + { + var url = BaseClass.BuildPDDObjString("product/mdy_violation_prod"); + var data = UserLogin.GetSignaturex(); + data += "&wx_safe_status=违规商品"; + data += "¬es=" + GetHtml.UrlEncodeUtf8(("违规轮询-" + shop.ShopId + "-" + punishUuid + "-" + punishReason + "-" + punishTime.ToString("yy/MM/dd HH:mm")).Trim()); + data += "&product_name=" + productDesc; + var html = GetHtml.PostUtf8(url, data); + if (string.IsNullOrEmpty(html) || !html.Contains("\"code\":200")) + html = GetHtml.PostUtf8(url, data); + if (!html.Contains("\"code\":200")) + { + info.failReason = "更新商品状态失败"; + break; + } - url = BaseClass.BuildPDDObjString("product/mdy_violation_prod"); - data = UserLogin.GetSignaturex(); - data += "&wx_safe_status=违规商品"; - data += "¬es=" + ("违规轮询-" + shop.ShopId + "-" + punishUuid + "-" + punishReason).Trim(); - data += "&secret_spu_id=" + secrec_spu_id; - data += "&spu_id=" + spu_id; - html = GetHtml.PostUtf8(url, data); - if (string.IsNullOrEmpty(html) || !html.Contains("\"code\":200")) - html = GetHtml.PostUtf8(url, data); - if (!html.Contains("\"code\":200")) - { - info.failReason = "更新商品状态失败"; - break; - } - } - } - } + if (html.Contains("商品不存在")) + { + var getProdRst = WXUpload.searchProductWithTitle(shop, GetHtml.UrlEncodeUtf8(productDesc)); + var spuCode = BaseRegex.GetStringJson(ref getProdRst, "spuCode"); + var secrec_spu_id = ""; + var spu_id = ""; + if (string.IsNullOrEmpty(spuCode) || spuCode.Length < 10) + { + continue; + } + if (spuCode.StartsWith("EC1") && spuCode.EndsWith("A")) + { + spu_id = spuCode.Substring(3, spuCode.Length - 4); + } + else if (spuCode.StartsWith("P1-")) + { + spu_id = spuCode.Substring(3); + } + else + { + secrec_spu_id = spuCode; + } - if (string.IsNullOrEmpty(info.failReason)) - info.updateDay = DateTime.Now.ToString("yyMMdd"); + url = BaseClass.BuildPDDObjString("product/mdy_violation_prod"); + data = UserLogin.GetSignaturex(); + data += "&wx_safe_status=违规商品"; + data += "¬es=" + ("违规轮询-" + shop.ShopId + "-" + punishUuid + "-" + punishReason).Trim(); + data += "&secret_spu_id=" + secrec_spu_id; + data += "&spu_id=" + spu_id; + html = GetHtml.PostUtf8(url, data); + if (string.IsNullOrEmpty(html) || !html.Contains("\"code\":200")) + html = GetHtml.PostUtf8(url, data); + if (!html.Contains("\"code\":200")) + { + info.failReason = "更新商品状态失败"; + break; + } + } + } + } - NaiBa.Util.Helper.ConfigHelper.SaveConfig("wxProdViolation\\" + shop.ShopId, info); - Thread.Sleep(5000); - } - catch (Exception ex) - { - try - { - if (info == null) - info = new WXProdViolationGetInfo() { shopId = shop.ShopId, updateDay = DateTime.Now.ToString("yyMMdd") }; - info.failReason = ex.Message; - NaiBa.Util.Helper.ConfigHelper.SaveConfig("wxProdViolation\\" + shop.ShopId, info); - } - catch { } - } - } - // 休眠两个小时 - Thread.Sleep(1000 * 60 * 60 * 2); - } - }); - } + if (string.IsNullOrEmpty(info.failReason)) + info.updateDay = DateTime.Now.ToString("yyMMdd"); - public static string getShopiolationProd(ShopLoginInfo shop) - { - string url = "https://store.weixin.qq.com/shop-faas/mmecnodeviolationsec/violation/cgi/get?token=&lang=zh_CN&violationType=finder&shopId="; - string referer = "https://store.weixin.qq.com/shop/violation/home"; - var result = WXHttpResponseBLL.WeiXinGetResponse(shop, url, referer); - return result; - } - } + NaiBa.Util.Helper.ConfigHelper.SaveConfig("wxProdViolation\\" + shop.ShopId, info); + Thread.Sleep(5000); + } + catch (Exception ex) + { + try + { + if (info == null) + info = new WXProdViolationGetInfo() { shopId = shop.ShopId, updateDay = DateTime.Now.ToString("yyMMdd") }; + info.failReason = ex.Message; + NaiBa.Util.Helper.ConfigHelper.SaveConfig("wxProdViolation\\" + shop.ShopId, info); + } + catch { } + } + } + // 休眠两个小时 + Thread.Sleep(1000 * 60 * 60 * 2); + } + }); + } - public class WXProdViolationGetInfo - { - public string shopId { get; set; } - public string updateDay { get; set; } // yyMMdd - public string failReason { get; set; } // "", "掉线"... - } + public static string getShopiolationProd(ShopLoginInfo shop) + { + string url = "https://store.weixin.qq.com/shop-faas/mmecnodeviolationsec/violation/cgi/get?token=&lang=zh_CN&violationType=finder&shopId="; + string referer = "https://store.weixin.qq.com/shop/violation/home"; + var result = WXHttpResponseBLL.WeiXinGetResponse(shop, url, referer); + return result; + } + } + + public class WXProdViolationGetInfo + { + public string shopId { get; set; } + public string updateDay { get; set; } // yyMMdd + public string failReason { get; set; } // "", "掉线"... + } } diff --git a/Work/Services/CloudGoodsService.cs b/Work/Services/CloudGoodsService.cs index 6064db0..ce00b46 100644 --- a/Work/Services/CloudGoodsService.cs +++ b/Work/Services/CloudGoodsService.cs @@ -2,6 +2,7 @@ using AllBase.Helper; using AllBase.Model.Result; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Linq; @@ -46,6 +47,155 @@ namespace Work.Services return ret.Data; } + /// + /// 在后台解析微信处罚 JSON 并上报;无商品或处理失败时返回空列表,异常在内部记录。 + /// + 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> 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(); + 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() : await ReportViolations(reports); + } + + /// + /// 批量上报违规商品。 + /// + public static async Task> ReportViolations(List 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>>(html); + if (ret == null) + { + throw new Exception("批量上报违规商品返回为空。"); + } + if (ret.Code != 200) + { + throw new Exception(ret.Msg); + } + return ret.Data ?? throw new Exception("批量上报违规商品返回数据为空。"); + } + + /// + /// 批量查询商品违规状态。 + /// + public static async Task> GetViolationStatus(List goodsIds) + { + if (goodsIds == null) + { + throw new ArgumentNullException(nameof(goodsIds)); + } + + var html = await GetHtml.PostJsonAsync(ServerUrl + "/api/CloudGoods/GetViolationStatus", goodsIds); + var ret = JsonConvert.DeserializeObject>>(html); + if (ret == null) + { + throw new Exception("批量查询商品违规状态返回为空。"); + } + if (ret.Code != 200) + { + throw new Exception(ret.Msg); + } + return ret.Data ?? throw new Exception("批量查询商品违规状态返回数据为空。"); + } + #region user /// /// 搜索收藏 @@ -117,6 +267,42 @@ namespace Work.Services return string.IsNullOrEmpty(BaseClass.ParentUserName) ? BaseClass.UserName : BaseClass.ParentUserName; } } + /// + /// 违规商品上报项。 + /// + public class ReportGoodsViolationItem + { + [JsonProperty("goodsId")] + public long GoodsId { get; set; } + + [JsonProperty("reasons")] + public string Reasons { get; set; } = string.Empty; + } + + /// + /// 单个商品的违规上报结果。 + /// + public class ReportGoodsViolationResult + { + [JsonProperty("goodsId")] + public long GoodsId { get; set; } + + [JsonProperty("isOffShelf")] + public bool IsOffShelf { get; set; } + } + + /// + /// 商品违规状态。 + /// + public class GoodsViolationStatus + { + [JsonProperty("goodsId")] + public long GoodsId { get; set; } + + [JsonProperty("isViolation")] + public bool IsViolation { get; set; } + } + /// /// 商品搜索请求 /// diff --git a/Work/Services/ImageCacheService.cs b/Work/Services/ImageCacheService.cs index a91d8db..2acb731 100644 --- a/Work/Services/ImageCacheService.cs +++ b/Work/Services/ImageCacheService.cs @@ -12,15 +12,9 @@ using System.Threading.Tasks; namespace Work.Services; -public class ImageCache -{ - public string Md5 { get; set; } - public int Platform { get; set; } - [JsonProperty("platform_url")] - public string PlatformUrl { get; set; } - public bool IsCache { get; set; } -} - +/// +/// 图片缓存服务 +/// public class ImageCacheService { private static string ServerUrl = "http://103.90.155.78:9001"; @@ -136,3 +130,11 @@ public class ImageCacheService } } +public class ImageCache +{ + public string Md5 { get; set; } + public int Platform { get; set; } + [JsonProperty("platform_url")] + public string PlatformUrl { get; set; } + public bool IsCache { get; set; } +}