using BBWYB.Common.Extensions; using BBWYB.Common.Http; using BBWYB.Common.Models; using BBWYB.Server.Model; using BBWYB.Server.Model.Db; using BBWYB.Server.Model.Dto; using Microsoft.Extensions.Caching.Memory; using Newtonsoft.Json.Linq; using System.Net.Http.Headers; using System.Text.RegularExpressions; using static Google.Protobuf.Reflection.SourceCodeInfo.Types; namespace BBWYB.Server.Business { public class PurchaseProductAPIService : IDenpendency { private RestApiService restApiService; private IMemoryCache memoryCache; private string oneBoundKey = "t5060712539"; private string oneBoundSecret = "20211103"; //private string qtAppId = "BBWY2023022001"; //private string qtAppSecret = "908e131365d5448ca651ba20ed7ddefe"; private TimeSpan purchaseProductCacheTimeSpan; //private TimeSpan _1688SessionIdTimeSpan; //private ConcurrentDictionary purchaseSchemeProductSkus)> productChaches; private IDictionary _1688ProductDetailRequestHeader; private IDictionary _1688FactoryCardRequestHeader; private List _1688ColorPropertyFieldIdList; private List locationIdList; private List priceIdList; private List purchaserNameIdList; private IList invalidPurchaserNameList; private IDictionary> provinceDictionary; public PurchaseProductAPIService(RestApiService restApiService, IMemoryCache memoryCache) { invalidPurchaserNameList = new List() { "超级工厂", "实力工厂", "实力供应商" }; this.memoryCache = memoryCache; this.restApiService = restApiService; _1688ProductDetailRequestHeader = new Dictionary() { { "Host","detail.1688.com"}, { "User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36 Edg/104.0.1293.70"}, { "Accept","text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"}, { "Accept-Encoding","gzip, deflate, br"}, { "Accept-Language","zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6"} }; _1688FactoryCardRequestHeader = new Dictionary() { { "Host","sale.1688.com"}, { "User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36 Edg/104.0.1293.70"}, { "Accept","text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"}, { "Accept-Encoding","gzip, deflate, br"}, { "Accept-Language","zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6"} }; purchaseProductCacheTimeSpan = TimeSpan.FromDays(1); _1688ColorPropertyFieldIdList = new List() { 3216, 1627207, 1234, 3151, 7853, 446, 374, 404, 100019516, 3114, 2068, 100018474 }; //jobject["data"]["1081181309101"] != null ? // jobject["data"]["1081181309101"]["data"]["location"].ToString() : // jobject["data"]["16347413030323"]["data"]["location"].ToString(), locationIdList = new List() { "1081181309101", "16347413030323", "13772573013156" }; //var firstPrice = jobject["data"]["1081181309582"] != null ? // jobject["data"]["1081181309582"]["data"]["priceModel"]["currentPrices"][0].Value("price") : // jobject["data"]["16347413030316"]["data"]["priceModel"]["currentPrices"][0].Value("price"); priceIdList = new List() { "1081181309582", "1081181309582", "16347413030316", "13772573013151" }; purchaserNameIdList = new List() { "38229149", "38229148", "38229150" }; provinceDictionary = new Dictionary>() { { "省", new List() { "黑龙江","吉林","辽宁","河北","山西","陕西","甘肃","青海","河南","湖北","湖南","山东","江苏","安徽","浙江","江西","福建","台湾","贵州","四川","云南","广东","海南" } }, { "自治区", new List(){ "内蒙古","西藏"}}, { "回族自治区", new List(){"宁夏"}}, { "维吾尔自治区", new List(){"新疆"}}, { "壮族自治区", new List(){"广西"}}, { "特别行政区",new List(){ "香港","澳门"}}, { "市",new List(){ "重庆","北京","天津","上海"}} }; } public PurchaseSkuBasicInfoResponse GetProductInfo(PurchaseSkuBasicInfoRequest request) { var cacheKey = $"{request.PurchaseProductId}_{request.PriceMode}"; if (memoryCache.TryGetValue(cacheKey, out var tuple)) return tuple.Copy(); PurchaseSkuBasicInfoResponse response = null; if (request.FirstApiMode == Enums.PurchaseProductAPIMode.Spider) { response = LoadFromSpider(request); if (response == null) response = LoadFromOneBound(request); } else if (request.FirstApiMode == Enums.PurchaseProductAPIMode.OneBound) { response = LoadFromOneBound(request); if (response == null) response = LoadFromSpider(request); } if (response != null) { try { memoryCache.Set(cacheKey, response, purchaseProductCacheTimeSpan); } catch { } } return response?.Copy(); } private PurchaseSkuBasicInfoResponse LoadFromOneBound(PurchaseSkuBasicInfoRequest request) { try { string platformStr = string.Empty; if (request.Platform == Enums.Platform.阿里巴巴) platformStr = "1688"; if (string.IsNullOrEmpty(platformStr)) return null; var result = restApiService.SendRequest("https://api-gw.onebound.cn/", $"{platformStr}/item_get", $"key={oneBoundKey}&secret={oneBoundSecret}&num_iid={request.PurchaseProductId}&lang=zh-CN&cache=no&agent={(request.PriceMode == Enums.PurchaseOrderMode.批发 ? 0 : 1)}", null, HttpMethod.Get, paramPosition: ParamPosition.Query, enableRandomTimeStamp: true); if (result.StatusCode != System.Net.HttpStatusCode.OK) throw new Exception($"{result.StatusCode} {result.Content}"); var jobject = JObject.Parse(result.Content); var isOK = jobject.Value("error_code") == "0000"; if (isOK) { var skuJArray = (JArray)jobject["item"]["skus"]["sku"]; if (skuJArray.Count == 0) { //errorMsg = $"商品{purchaseSchemeProduct.PurchaseProductId}缺少sku信息"; return null; } var list = skuJArray.Select(j => new PurchaseSkuItemBasicInfoResponse() { PurchaseProductId = request.PurchaseProductId, Price = j.Value("price"), PurchaseSkuId = j.Value("sku_id"), PurchaseSkuSpecId = j.Value("spec_id"), Title = Regex.Replace(j.Value("properties_name"), @"\d:\d:.{2,5}:", string.Empty), Logo = GetOneBoundSkuLogo(j, (JArray)jobject["item"]["prop_imgs"]["prop_img"]) }).ToList(); var purchaserId = jobject["item"]["seller_info"].Value("user_num_id"); var purchaserName = jobject["item"]["seller_info"].Value("title"); if (string.IsNullOrEmpty(purchaserName)) purchaserName = jobject["item"]["seller_info"].Value("shop_name"); var purchaserLocation = jobject["item"].Value("location"); var memberId = jobject["item"]["seller_info"].Value("sid"); var purchaserId2 = jobject["item"]["seller_info"].Value("shop_name"); return new PurchaseSkuBasicInfoResponse() { Purchaser = new Purchaser() { Id = purchaserId, Location = purchaserLocation, Name = purchaserName, Platform = request.Platform, Id2 = purchaserId2, MemberId = memberId }, ItemList = list, PurchasePlatform = request.Platform, PurchaseProductId = request.PurchaseProductId, APIMode = Enums.PurchaseProductAPIMode.OneBound }; } else if (jobject.Value("error_code") == "2000") { return new PurchaseSkuBasicInfoResponse() { IsInvalid = true, APIMode = request.FirstApiMode, PurchaseProductId = request.PurchaseProductId, PurchasePlatform = request.Platform }; } else { return null; } } catch { } { return null; } } private string GetOneBoundSkuLogo(JToken skuJToken, JArray prop_img) { if (!prop_img.HasValues) return "pack://application:,,,/Resources/Images/defaultItem.png"; var properties = skuJToken.Value("properties").Split(';', StringSplitOptions.RemoveEmptyEntries); foreach (var p in properties) { var imgJToken = prop_img.FirstOrDefault(g => g.Value("properties") == p); if (imgJToken != null) return imgJToken.Value("url"); } return "pack://application:,,,/Resources/Images/defaultItem.png"; } private PurchaseSkuBasicInfoResponse LoadFromSpider(PurchaseSkuBasicInfoRequest request) { switch (request.Platform) { case Enums.Platform.阿里巴巴: return LoadFrom1688Spider(request); //case Platform.拳探: // return LoadFromQTSpider(platform, productId, skuId, purchaseProductId, priceMode); } return null; } private PurchaseSkuBasicInfoResponse LoadFrom1688Spider(PurchaseSkuBasicInfoRequest request) { //https://detail.1688.com/offer/672221374773.html?clickid=65f3772cd5d16f190ce4991414607&sessionid=3de47a0c26dcbfde4692064bd55861&sk=order //globalData/tempModel/sellerUserId //globalData/tempModel/companyName //data/1081181309101/data/location //data/1081181309582/data/pirceModel/[currentPrices]/[0]price try { var _1688pageResult = restApiService.SendRequest("https://detail.1688.com", $"offer/{request.PurchaseProductId}.html", $"clickid={Guid.NewGuid().ToString().Md5Encrypt()}&sessionid={Guid.NewGuid().ToString().Md5Encrypt()}&sk={(request.PriceMode == Enums.PurchaseOrderMode.批发 ? "order" : "consign")}", _1688ProductDetailRequestHeader, HttpMethod.Get, httpClientName: "gzip"); if (_1688pageResult.StatusCode != System.Net.HttpStatusCode.OK) return null; var match = Regex.Match(_1688pageResult.Content, @"(window\.__INIT_DATA=)(.*)(\r*\n*\s*)"); if (!match.Success) { if (_1688pageResult.Content.Contains("商品已下架")) { return new PurchaseSkuBasicInfoResponse() { IsInvalid = true, PurchasePlatform = request.Platform, APIMode = request.FirstApiMode, PurchaseProductId = request.PurchaseProductId }; } return null; } var jsonStr = match.Groups[2].Value; var jobject = JObject.Parse(jsonStr); var memberId = jobject["globalData"]?["tempModel"]?["sellerMemberId"]?.ToString(); #region 验证purchaserName var purchaserName = jobject["globalData"]["tempModel"]["companyName"].ToString(); var tag = string.Empty; if (invalidPurchaserNameList.Any(x => purchaserName.Contains(x))) { tag = invalidPurchaserNameList.FirstOrDefault(x => purchaserName.Contains(x)); var storeDataMatch = Regex.Match(_1688pageResult.Content, @"(window\.__STORE_DATA=)(.*)(\r*\n*\s*)"); if (storeDataMatch.Success) { try { var jsonStr_storeData = storeDataMatch.Groups[2].Value; var jobject_storeData = JObject.Parse(jsonStr_storeData); foreach (var purchaserNodeId in purchaserNameIdList) { var purchaserTempName = jobject_storeData["components"]?[purchaserNodeId]?["moduleData"]?["companyName"].ToString(); if (!string.IsNullOrEmpty(purchaserTempName)) { purchaserName = purchaserTempName; break; } } } catch { } } if (invalidPurchaserNameList.Any(x => purchaserName.Contains(x)) && !string.IsNullOrEmpty(memberId)) { //https://sale.1688.com/factory/card.html?memberId=b2b-4204371240a61bf var _1688FactoryCardResult = restApiService.SendRequest("https://sale.1688.com", "factory/card.html", $"memberId={memberId}", _1688FactoryCardRequestHeader, HttpMethod.Get, httpClientName: "gzip"); if (_1688FactoryCardResult.StatusCode == System.Net.HttpStatusCode.OK) { var titleMatch = Regex.Match(_1688FactoryCardResult.Content, @"(.*)-(.*)-(.*)"); if (titleMatch.Success) { purchaserName = titleMatch.Groups[1].Value; } } } } #endregion var location = ""; for (var i = 0; i < locationIdList.Count(); i++) { if (jobject["data"][locationIdList[i]] != null) { location = jobject["data"][locationIdList[i]]["data"]["location"]?.ToString(); if (string.IsNullOrEmpty(location)) continue; break; } } if (string.IsNullOrEmpty(location)) { var jobjectdata = jobject["data"] as JObject; foreach (var jproperty in jobjectdata.Properties()) { if (jobjectdata[jproperty.Name].Value("componentType") == "@ali/tdmod-od-pc-attribute-new") { location = jobjectdata[jproperty.Name]["data"].FirstOrDefault(x => x.Value("name") == "产地")?.Value("value"); if (!string.IsNullOrEmpty(location)) break; } } if (string.IsNullOrEmpty(location)) { if (purchaserName.Contains("市") && purchaserName.Contains("区")) { location = purchaserName.Substring(0, purchaserName.IndexOf("区") + 1); } else if (purchaserName.Contains("市")) { location = purchaserName.Substring(0, purchaserName.IndexOf("市") + 1); } } } location = FormatteLocation(location); var purchaser = new Purchaser() { Id = jobject["globalData"]["tempModel"]["sellerUserId"].ToString(), Id2 = jobject["globalData"]["tempModel"]["sellerLoginId"]?.ToString(), Name = purchaserName, MemberId = memberId, Location = location, Platform = Enums.Platform.阿里巴巴, Tag = tag }; //_1688ColorPropertyFieldIdList.Add(100018474); var fidJToken = jobject["globalData"]["skuModel"]["skuProps"].FirstOrDefault(j => _1688ColorPropertyFieldIdList.Contains(j.Value("fid"))); if (fidJToken == null) fidJToken = jobject["globalData"]["skuModel"]["skuProps"].FirstOrDefault(j => j.Value("prop").Contains("颜色")); if (fidJToken == null && jobject["globalData"]["skuModel"]["skuProps"].Children().Count() == 1) fidJToken = jobject["globalData"]["skuModel"]["skuProps"].FirstOrDefault(); var colorsProperty = fidJToken["value"].Children() .Select(j => new { name = j.Value("name"), imageUrl = j.Value("imageUrl") }).ToList(); var firstPrice = 0.0M; for (var i = 0; i < priceIdList.Count(); i++) { if (jobject["data"][priceIdList[i]] != null) { firstPrice = jobject["data"][priceIdList[i]]["data"]["priceModel"]["currentPrices"][0].Value("price"); break; } } var list = new List(); foreach (var jsku in jobject["globalData"]["skuModel"]["skuInfoMap"].Children()) { var jskuProperty = jsku as JProperty; var name = jskuProperty.Name; var matchName = name.Contains(">") ? name.Substring(0, name.IndexOf(">")) : name; var value = jskuProperty.Value; var skuPrice = value.Value("price"); list.Add(new PurchaseSkuItemBasicInfoResponse() { PurchaseProductId = request.PurchaseProductId, Price = skuPrice == 0M ? firstPrice : skuPrice, Title = name.Replace(">", string.Empty), PurchaseSkuId = value.Value("skuId"), PurchaseSkuSpecId = value.Value("specId"), Logo = colorsProperty.FirstOrDefault(c => c.name == matchName)?.imageUrl ?? "pack://application:,,,/Resources/Images/defaultItem.png" }); } return new PurchaseSkuBasicInfoResponse() { ItemList = list, Purchaser = purchaser, PurchaseProductId = request.PurchaseProductId, PurchasePlatform = Enums.Platform.阿里巴巴, ProductName = jobject["globalData"]["tempModel"]["offerTitle"]?.ToString(), ProductLogo = list.FirstOrDefault()?.Logo, APIMode = Enums.PurchaseProductAPIMode.Spider }; } catch { return null; } } private string FormatteLocation(string location) { if (string.IsNullOrEmpty(location)) return location; location = location.Trim().Replace(" ", string.Empty); foreach (var provincekey in provinceDictionary.Keys) { var province = provinceDictionary[provincekey].FirstOrDefault(p => location.StartsWith(p)); if (string.IsNullOrEmpty(province)) continue; if (location.Contains(provincekey)) return location; location = location.Replace(province, $"{province}{provincekey}"); if (location.EndsWith(provincekey)) return location; if (!location.EndsWith("市")) location = $"{location}市"; } //Console.WriteLine(location); return location; } } }