diff --git a/BBWY.Common/Extensions/CopyExtensions.cs b/BBWY.Common/Extensions/CopyExtensions.cs new file mode 100644 index 00000000..d87833f3 --- /dev/null +++ b/BBWY.Common/Extensions/CopyExtensions.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace BBWY.Common.Extensions +{ + public static class CopyExtensions + { + public static T Copy(this T p) + { + return JsonConvert.DeserializeObject(JsonConvert.SerializeObject(p)); + } + } +} diff --git a/BBWY.Server.API/Controllers/PurchaseSchemeController.cs b/BBWY.Server.API/Controllers/PurchaseSchemeController.cs index 64e35246..a6bb132a 100644 --- a/BBWY.Server.API/Controllers/PurchaseSchemeController.cs +++ b/BBWY.Server.API/Controllers/PurchaseSchemeController.cs @@ -70,5 +70,16 @@ namespace BBWY.Server.API.Controllers { return purchaseSchemeBusiness.GetSharePurchaser(querySchemeRequest); } + + /// + /// 批量修改拳探采购方案归属采购商 + /// + /// + /// + [HttpPost] + public IList BatchUpdateQTPurchaseSchemeBelongPurchaser([FromBody]BatchUpdateQTPurchaseSchemeBelongPurchaserRequest request) + { + return purchaseSchemeBusiness.BatchUpdateQTPurchaseSchemeBelongPurchaser(request); + } } } diff --git a/BBWY.Server.Business/PurchaseScheme/PurchaseProductAPIService.cs b/BBWY.Server.Business/PurchaseScheme/PurchaseProductAPIService.cs new file mode 100644 index 00000000..e7d7f956 --- /dev/null +++ b/BBWY.Server.Business/PurchaseScheme/PurchaseProductAPIService.cs @@ -0,0 +1,295 @@ +using BBWY.Common.Extensions; +using BBWY.Common.Http; +using BBWY.Common.Models; +using BBWY.Server.Model; +using BBWY.Server.Model.Db; +using BBWY.Server.Model.Dto; +using Microsoft.Extensions.Caching.Memory; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text.RegularExpressions; + +namespace BBWY.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 char[] skuPropertiesSplitChar = new char[] { ';' }; + + public PurchaseProductAPIService(RestApiService restApiService, IMemoryCache memoryCache) + { + 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"} + }; + purchaseProductCacheTimeSpan = TimeSpan.FromDays(1); + } + + 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 = j.Value("properties_name"), + 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"); + + return new PurchaseSkuBasicInfoResponse() + { + Purchaser = new Model.Db.Purchaser() + { + Id = purchaserId, + Location = purchaserLocation, + Name = purchaserName, + Platform = request.Platform + }, + ItemList = list, + PurchasePlatform = request.Platform, + PurchaseProductId = request.PurchaseProductId + }; + } + } + 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(skuPropertiesSplitChar, 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) + return null; + + var jsonStr = match.Groups[2].Value; + var jobject = JObject.Parse(jsonStr); + + //16347413030323 + var purchaser = new Purchaser() + { + Id = jobject["globalData"]["tempModel"]["sellerUserId"].ToString(), + Name = jobject["globalData"]["tempModel"]["companyName"].ToString(), + Location = jobject["data"]["1081181309101"] != null ? + jobject["data"]["1081181309101"]["data"]["location"].ToString() : + jobject["data"]["16347413030323"]["data"]["location"].ToString(), + Platform = Enums.Platform.阿里巴巴 + }; + + var colorsProperty = jobject["globalData"]["skuModel"]["skuProps"].FirstOrDefault(j => j.Value("fid") == 3216 || + j.Value("fid") == 1627207 || + j.Value("fid") == 1234 || + j.Value("fid") == 3151)["value"] + .Children() + .Select(j => new + { + name = j.Value("name"), + imageUrl = j.Value("imageUrl") + }).ToList(); + + var firstPrice = jobject["data"]["1081181309582"] != null ? + jobject["data"]["1081181309582"]["data"]["priceModel"]["currentPrices"][0].Value("price") : + jobject["data"]["16347413030316"]["data"]["priceModel"]["currentPrices"][0].Value("price"); + + 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, + 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.阿里巴巴 + }; + } + catch + { + + return null; + } + } + + //private (Purchaser purchaser, IList 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; + // } + //} + } +} diff --git a/BBWY.Server.Business/PurchaseScheme/PurchaseSchemeBusiness.cs b/BBWY.Server.Business/PurchaseScheme/PurchaseSchemeBusiness.cs index 5633fc6b..8c89b5d9 100644 --- a/BBWY.Server.Business/PurchaseScheme/PurchaseSchemeBusiness.cs +++ b/BBWY.Server.Business/PurchaseScheme/PurchaseSchemeBusiness.cs @@ -1,7 +1,9 @@ using BBWY.Common.Extensions; using BBWY.Common.Models; +using BBWY.Server.Model; using BBWY.Server.Model.Db; using BBWY.Server.Model.Dto; +using FreeSql; using System; using System.Collections.Generic; using System.Linq; @@ -11,7 +13,11 @@ namespace BBWY.Server.Business { public class PurchaseSchemeBusiness : BaseBusiness, IDenpendency { - public PurchaseSchemeBusiness(IFreeSql fsql, NLogManager nLogManager, IIdGenerator idGenerator) : base(fsql, nLogManager, idGenerator) { } + private PurchaseProductAPIService purchaseProductAPIService; + public PurchaseSchemeBusiness(IFreeSql fsql, NLogManager nLogManager, IIdGenerator idGenerator, PurchaseProductAPIService purchaseProductAPIService) : base(fsql, nLogManager, idGenerator) + { + this.purchaseProductAPIService = purchaseProductAPIService; + } private void ExtractNewPurchaser(IList purchaserSchemeList, IList addPurchaserList) where T : InputPurchaseSchemeRequest { @@ -223,13 +229,13 @@ namespace BBWY.Server.Business var purchaserList = fsql.Select().Where(p => sharePurchaserIdList.Contains(p.Id)).ToList(); foreach (var p in purchaserList) { - var scheme = purchaseSchemeList.FirstOrDefault(ps=>ps.PurchaserId == p.Id); + var scheme = purchaseSchemeList.FirstOrDefault(ps => ps.PurchaserId == p.Id); if (scheme != null) { p.DefaultCost = scheme.DefaultCost; p.LastPurchaseTime = scheme.LastPurchaseTime; } - } + } return purchaserList; } @@ -255,5 +261,104 @@ namespace BBWY.Server.Business fsql.Delete().Where(p => p.SkuPurchaseSchemeId == schemeId).ExecuteAffrows(); }); } + + + /// + /// 获取采购Sku的基本信息 + /// + /// + /// + public PurchaseSkuBasicInfoResponse GetPurchaseSkuBasicInfo(PurchaseSkuBasicInfoRequest request) + { + return purchaseProductAPIService.GetProductInfo(request); + } + + public IList BatchGetPurchaseSkuBasicInfo(BatchPurchaseSkuBasicInfoRequest request) + { + var list = new List(); + foreach (var param in request.Params) + { + foreach (var purchaseId in param.PurchaseProductIds) + { + var response = GetPurchaseSkuBasicInfo(new PurchaseSkuBasicInfoRequest() + { + FirstApiMode = request.FirstApiMode, + PriceMode = request.PriceMode, + Platform = param.Platform, + PurchaseProductId = purchaseId + }); + if (response != null) + { + if (param.PurchaseSkuIds != null && param.PurchaseSkuIds.Count() > 0) + { + for (var i = 0; i < response.ItemList.Count(); i++) + { + var skuInfo = response.ItemList[i]; + if (!param.PurchaseSkuIds.Any(s => s == skuInfo.PurchaseSkuId)) + { + response.ItemList.RemoveAt(i); + i--; + } + } + } + list.Add(response); + } + } + } + return list; + } + + /// + /// 批量修改拳探采购方案归属采购商 + /// + /// 修改成功的拳探skuId + /// + public IList BatchUpdateQTPurchaseSchemeBelongPurchaser(BatchUpdateQTPurchaseSchemeBelongPurchaserRequest request) + { + var purchaseSchemeList = GetPurchaseSchemeList(new QuerySchemeRequest() + { + PurchasePlatform = Enums.Platform.拳探, + PurchaserId = request.PurchaserId + }); + IList updateSkuIdList = new List(); + IList> updatePurchaseSchemeList = new List>(); + foreach (var purchaseScheme in purchaseSchemeList) + { + var firstProduct = purchaseScheme.PurchaseSchemeProductList.FirstOrDefault(); + var purchaseBasicInfoList = BatchGetPurchaseSkuBasicInfo(new BatchPurchaseSkuBasicInfoRequest() + { + FirstApiMode = Enums.PurchaseProductAPIMode.Spider, + PriceMode = Enums.PurchaseOrderMode.批发, + Params = new List() + { + new BatchPurchaseSkuBasicInfoParamRequest() + { + Platform = Enums.Platform.拳探, + PurchaseProductIds = new string[]{firstProduct.PurchaseProductId} + } + } + }); + + if (purchaseBasicInfoList.Count() == 0) + continue; + var p = purchaseBasicInfoList.FirstOrDefault().Purchaser; + if (p != null && p.Id != purchaseScheme.PurchaserId) + { + var update = fsql.Update(purchaseScheme.Id).Set(ps => ps.PurchaserId, p.Id); + updatePurchaseSchemeList.Add(update); + updateSkuIdList.Add(purchaseScheme.SkuId); + } + } + + if (updatePurchaseSchemeList.Count() > 0) + { + fsql.Transaction(() => + { + foreach (var update in updatePurchaseSchemeList) + update.ExecuteAffrows(); + }); + } + return updateSkuIdList; + } } } diff --git a/BBWY.Server.Model/Dto/Request/PurchaseScheme/BatchUpdateQTPurchaseSchemeBelongPurchaserRequest.cs b/BBWY.Server.Model/Dto/Request/PurchaseScheme/BatchUpdateQTPurchaseSchemeBelongPurchaserRequest.cs new file mode 100644 index 00000000..2dbf1b5a --- /dev/null +++ b/BBWY.Server.Model/Dto/Request/PurchaseScheme/BatchUpdateQTPurchaseSchemeBelongPurchaserRequest.cs @@ -0,0 +1,10 @@ +namespace BBWY.Server.Model.Dto +{ + public class BatchUpdateQTPurchaseSchemeBelongPurchaserRequest + { + /// + /// 采购商Id 可空 + /// + public string PurchaserId { get; set; } + } +} diff --git a/BBWY.Server.Model/Dto/Request/PurchaseScheme/PurcasheSkuBasicInfoRequest.cs b/BBWY.Server.Model/Dto/Request/PurchaseScheme/PurcasheSkuBasicInfoRequest.cs new file mode 100644 index 00000000..91cb83be --- /dev/null +++ b/BBWY.Server.Model/Dto/Request/PurchaseScheme/PurcasheSkuBasicInfoRequest.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; + +namespace BBWY.Server.Model.Dto +{ + public class PurchaseSkuBasicInfoRequest + { + public Enums.Platform Platform { get; set; } + + /// + /// 采购商品Id + /// + public string PurchaseProductId { get; set; } + + public Enums.PurchaseOrderMode PriceMode { get; set; } + + public Enums.PurchaseProductAPIMode FirstApiMode { get; set; } + } + + public class BatchPurchaseSkuBasicInfoRequest + { + public IList Params { get; set; } + + public Enums.PurchaseOrderMode PriceMode { get; set; } + + public Enums.PurchaseProductAPIMode FirstApiMode { get; set; } + } + + public class BatchPurchaseSkuBasicInfoParamRequest + { + public Enums.Platform Platform { get; set; } + + /// + /// 采购商品Id(采购spu) + /// + public string[] PurchaseProductIds { get; set; } + + /// + /// 需要保留的采购SkuId,如果传递了该数组,将过滤不在该数组的采购sku + /// + public string[] PurchaseSkuIds { get; set; } + } +} diff --git a/BBWY.Server.Model/Dto/Response/PurchaseScheme/PurchaseProductBasicInfoResponse.cs b/BBWY.Server.Model/Dto/Response/PurchaseScheme/PurchaseProductBasicInfoResponse.cs new file mode 100644 index 00000000..6c3704c8 --- /dev/null +++ b/BBWY.Server.Model/Dto/Response/PurchaseScheme/PurchaseProductBasicInfoResponse.cs @@ -0,0 +1,58 @@ +using BBWY.Server.Model.Db; +using System.Collections.Generic; + +namespace BBWY.Server.Model.Dto +{ + /// + /// 采购Sku基础信息对象 + /// + public class PurchaseSkuBasicInfoResponse + { + public Enums.Platform PurchasePlatform { get; set; } + + public string PurchaseProductId { get; set; } + + /// + /// 采购SKU基础信息列表 + /// + public IList ItemList { get; set; } + + /// + /// 采购商 + /// + public Purchaser Purchaser { get; set; } + } + + /// + /// 采购Sku基础信息对象 + /// + public class PurchaseSkuItemBasicInfoResponse + { + /// + /// 采购SPU + /// + public string PurchaseProductId { get; set; } + + /// + /// 采购SKU + /// + public string PurchaseSkuId { get; set; } + + /// + /// 采购SPEC 1688独有属性 下单需要 + /// + public string PurchaseSkuSpecId { get; set; } + + /// + /// SKU标题 + /// + public string Title { get; set; } + + public string Logo { get; set; } + + /// + /// 单价 + /// + public decimal Price { get; set; } + } +} diff --git a/BBWY.Server.Model/Enums.cs b/BBWY.Server.Model/Enums.cs index cc90dd3a..41e71598 100644 --- a/BBWY.Server.Model/Enums.cs +++ b/BBWY.Server.Model/Enums.cs @@ -33,6 +33,15 @@ 代发 = 1 } + /// + /// 采购商品API模式 Spider = 0,OneBound = 1 + /// + public enum PurchaseProductAPIMode + { + Spider = 0, + OneBound = 1 + } + /// /// 仓储类型 ///