You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
419 lines
20 KiB
419 lines
20 KiB
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.Text.RegularExpressions;
|
|
|
|
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<string, (Purchaser purchaser, IList<PurchaseSchemeProductSku> purchaseSchemeProductSkus)> productChaches;
|
|
|
|
private IDictionary<string, string> _1688ProductDetailRequestHeader;
|
|
private IDictionary<string, string> _1688FactoryCardRequestHeader;
|
|
|
|
private List<int> _1688ColorPropertyFieldIdList;
|
|
private List<string> locationIdList;
|
|
private List<string> priceIdList;
|
|
private List<string> purchaserNameIdList;
|
|
|
|
private IList<string> invalidPurchaserNameList;
|
|
|
|
public PurchaseProductAPIService(RestApiService restApiService, IMemoryCache memoryCache)
|
|
{
|
|
invalidPurchaserNameList = new List<string>() { "超级工厂", "实力工厂", "实力供应商" };
|
|
this.memoryCache = memoryCache;
|
|
this.restApiService = restApiService;
|
|
_1688ProductDetailRequestHeader = new Dictionary<string, string>()
|
|
{
|
|
{ "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<string, string>()
|
|
{
|
|
{ "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<int>() { 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<string>() { "1081181309101", "16347413030323", "13772573013156" };
|
|
|
|
//var firstPrice = jobject["data"]["1081181309582"] != null ?
|
|
// jobject["data"]["1081181309582"]["data"]["priceModel"]["currentPrices"][0].Value<decimal>("price") :
|
|
// jobject["data"]["16347413030316"]["data"]["priceModel"]["currentPrices"][0].Value<decimal>("price");
|
|
priceIdList = new List<string>() { "1081181309582", "1081181309582", "16347413030316", "13772573013151" };
|
|
|
|
purchaserNameIdList = new List<string>() { "38229149", "38229148", "38229150" };
|
|
}
|
|
|
|
public PurchaseSkuBasicInfoResponse GetProductInfo(PurchaseSkuBasicInfoRequest request)
|
|
{
|
|
var cacheKey = $"{request.PurchaseProductId}_{request.PriceMode}";
|
|
if (memoryCache.TryGetValue<PurchaseSkuBasicInfoResponse>(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<string>("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<decimal>("price"),
|
|
PurchaseSkuId = j.Value<string>("sku_id"),
|
|
PurchaseSkuSpecId = j.Value<string>("spec_id"),
|
|
Title = Regex.Replace(j.Value<string>("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<string>("user_num_id");
|
|
var purchaserName = jobject["item"]["seller_info"].Value<string>("title");
|
|
if (string.IsNullOrEmpty(purchaserName))
|
|
purchaserName = jobject["item"]["seller_info"].Value<string>("shop_name");
|
|
var purchaserLocation = jobject["item"].Value<string>("location");
|
|
var memberId = jobject["item"]["seller_info"].Value<string>("sid");
|
|
var purchaserId2 = jobject["item"]["seller_info"].Value<string>("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<string>("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<string>("properties").Split(';', StringSplitOptions.RemoveEmptyEntries);
|
|
foreach (var p in properties)
|
|
{
|
|
var imgJToken = prop_img.FirstOrDefault(g => g.Value<string>("properties") == p);
|
|
if (imgJToken != null)
|
|
return imgJToken.Value<string>("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*</script>)");
|
|
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*</script>)");
|
|
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, @"<title>(.*)-(.*)-(.*)</title>");
|
|
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();
|
|
break;
|
|
}
|
|
}
|
|
|
|
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<int>("fid")));
|
|
if (fidJToken == null)
|
|
fidJToken = jobject["globalData"]["skuModel"]["skuProps"].FirstOrDefault(j => j.Value<string>("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<string>("name"),
|
|
imageUrl = j.Value<string>("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<decimal>("price");
|
|
break;
|
|
}
|
|
}
|
|
|
|
var list = new List<PurchaseSkuItemBasicInfoResponse>();
|
|
|
|
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<decimal>("price");
|
|
|
|
list.Add(new PurchaseSkuItemBasicInfoResponse()
|
|
{
|
|
PurchaseProductId = request.PurchaseProductId,
|
|
Price = skuPrice == 0M ? firstPrice : skuPrice,
|
|
Title = name.Replace(">", string.Empty),
|
|
PurchaseSkuId = value.Value<string>("skuId"),
|
|
PurchaseSkuSpecId = value.Value<string>("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 (Purchaser purchaser, IList<PurchaseSchemeProductSku> purchaseSchemeProductSkus)? LoadFromQTSpider(Platform platform, string productId, string skuId, string purchaseProductId, PurchaseOrderMode priceMode)
|
|
//{
|
|
// try
|
|
// {
|
|
// var response = quanTanProductClient.GetProductInfo(purchaseProductId, qtAppId, qtAppSecret);
|
|
// if (response.Status != 200)
|
|
// return null;
|
|
// return (new Purchaser()
|
|
// {
|
|
// Id = response.Data.Supplier.VenderId,
|
|
// Name = response.Data.Supplier.VerdenName,
|
|
// Location = response.Data.Supplier.Location
|
|
// }, response.Data.ProductSku.Select(qtsku => new PurchaseSchemeProductSku()
|
|
// {
|
|
// ProductId = productId,
|
|
// SkuId = skuId,
|
|
// PurchaseProductId = purchaseProductId,
|
|
// Price = qtsku.Price,
|
|
// Title = qtsku.Title,
|
|
// PurchaseSkuId = qtsku.SkuId,
|
|
// PurchaseSkuSpecId = string.Empty,
|
|
// Logo = qtsku.Logo
|
|
// }).ToList());
|
|
// }
|
|
// catch
|
|
// {
|
|
// return null;
|
|
// }
|
|
//}
|
|
}
|
|
}
|
|
|