From e5d08bf31d0e7c1ea3606d549898fbfa0bad50b6 Mon Sep 17 00:00:00 2001 From: shanj <18996038927@163.com> Date: Tue, 19 Sep 2023 18:57:41 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BA=A7=E5=93=81=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/ProductSyncController.cs | 23 ++ .../Sync/ProductSyncBusiness.cs | 214 ++++++++++++++++++ BBWYB.Server.Business/TaskSchedulerManager.cs | 2 + BBWYB.Server.Model/Db/Product/Product.cs | 54 +++++ BBWYB.Server.Model/Db/Product/ProductSku.cs | 59 +++++ .../QuanTan_Supplier_ProductListResponse.cs | 6 + ...QuanTan_Supplier_ProductSkuListResponse.cs | 6 + .../Client/Impl/OP_QuanTanClient.cs | 9 +- .../Response/Product/OP_ProductResponse.cs | 1 + .../Response/Product/OP_ProductSkuResponse.cs | 3 + 10 files changed, 373 insertions(+), 4 deletions(-) create mode 100644 BBWYB.Server.API/Controllers/ProductSyncController.cs create mode 100644 BBWYB.Server.Business/Sync/ProductSyncBusiness.cs create mode 100644 BBWYB.Server.Model/Db/Product/Product.cs create mode 100644 BBWYB.Server.Model/Db/Product/ProductSku.cs diff --git a/BBWYB.Server.API/Controllers/ProductSyncController.cs b/BBWYB.Server.API/Controllers/ProductSyncController.cs new file mode 100644 index 0000000..b196c48 --- /dev/null +++ b/BBWYB.Server.API/Controllers/ProductSyncController.cs @@ -0,0 +1,23 @@ +using BBWYB.Server.Business.Sync; +using Microsoft.AspNetCore.Mvc; + +namespace BBWYB.Server.API.Controllers +{ + public class ProductSyncController : BaseApiController + { + private ProductSyncBusiness productSyncBusiness; + public ProductSyncController(IHttpContextAccessor httpContextAccessor, ProductSyncBusiness productSyncBusiness) : base(httpContextAccessor) + { + this.productSyncBusiness = productSyncBusiness; + } + + /// + /// 全店同步产品 + /// + [HttpPost] + public void SyncAllShopAllProduct() + { + productSyncBusiness.SyncAllShopAllProduct(); + } + } +} diff --git a/BBWYB.Server.Business/Sync/ProductSyncBusiness.cs b/BBWYB.Server.Business/Sync/ProductSyncBusiness.cs new file mode 100644 index 0000000..4bcca99 --- /dev/null +++ b/BBWYB.Server.Business/Sync/ProductSyncBusiness.cs @@ -0,0 +1,214 @@ +using BBWYB.Common.Log; +using BBWYB.Common.Models; +using BBWYB.Server.Model.Db; +using BBWYB.Server.Model.Dto; +using FreeSql; +using SDKAdapter.OperationPlatform.Models; +using Yitter.IdGenerator; +using System.Linq; +using Newtonsoft.Json; +using BBWYB.Server.Model; + +namespace BBWYB.Server.Business.Sync +{ + public class ProductSyncBusiness : BaseBusiness, IDenpendency + { + private ProductBusiness productBusiness; + private VenderBusiness venderBusiness; + private TaskSchedulerManager taskSchedulerManager; + + public ProductSyncBusiness(IFreeSql fsql, NLogManager nLogManager, IIdGenerator idGenerator, ProductBusiness productBusiness, VenderBusiness venderBusiness, TaskSchedulerManager taskSchedulerManager) : base(fsql, nLogManager, idGenerator) + { + this.productBusiness = productBusiness; + this.venderBusiness = venderBusiness; + this.taskSchedulerManager = taskSchedulerManager; + } + + /// + /// 同步所有店铺的全部产品 + /// + public void SyncAllShopAllProduct() + { + //var shopList = venderBusiness.GetShopList(platform: Enums.Platform.拳探); + var shopList = venderBusiness.GetShopList(shopId: 9); + foreach (var shop in shopList) + { + Task.Factory.StartNew(() => SyncProduct(shop, true), CancellationToken.None, TaskCreationOptions.LongRunning, taskSchedulerManager.SyncProductTaskScheduler); + } + } + + + private void SyncProduct(ShopResponse shop, bool IsSyncAllProduct = false) + { + try + { + var shopId = long.Parse(shop.ShopId); + var dbProductList = fsql.Select().Where(p => p.ShopId == shopId).ToList(); + var dbProductSkuList = fsql.Select().Where(p => p.ShopId == shopId).ToList(); + + List productList = new List(); + List productSkuList = new List(); + + List insertProductList = new List(); + List insertProductSkuList = new List(); + + List> updateProductList = new List>(); + List> updateProductSkuList = new List>(); + + var request = new OP_QueryProductRequest() + { + PageSize = 50, + PageIndex = 1, + AppKey = shop.AppKey, + AppSecret = shop.AppSecret, + AppToken = shop.AppToken, + Platform = SDKAdapter.AdapterEnums.PlatformType.拳探 + }; + + var requestSku = new OP_QueryProductSkuRequest() + { + AppKey = shop.AppKey, + AppSecret = shop.AppSecret, + AppToken = shop.AppToken, + Platform = SDKAdapter.AdapterEnums.PlatformType.拳探, + //Spu = product.Id, + PageSize = 50, + PageIndex = 1 + }; + + while (true) + { + var pListRes = productBusiness.GetProductList(request); + if (pListRes == null || pListRes.Items == null || pListRes.Items.Count == 0) + break; + productList.AddRange(pListRes.Items); + + foreach (var product in pListRes.Items) + { + requestSku.Spu = product.Id; + var psListRes = productBusiness.GetProductSkuList(requestSku); + if (psListRes == null || psListRes.Items == null || psListRes.Items.Count == 0) + continue; + productSkuList.AddRange(psListRes.Items); + } + + if (pListRes.Items.Count < 50 || !IsSyncAllProduct) + break; + request.PageIndex++; + Thread.Sleep(1000); + } + + #region 找出新增的产品 + var newProductList = productList.Where(p => !dbProductList.Any(dp => dp.Id == p.Id)).ToList(); + if (newProductList.Count() > 0) + { + insertProductList.AddRange(newProductList.Select(p => new Product() + { + Id = p.Id, + Logo = p.Logo, + ProductName = p.Title, + ShopId = int.Parse(shop.ShopId), + State = p.State, + SyncTime = DateTime.Now, + UpdateTime = DateTime.Now, + UpperTime = p.CreateTime + })); + } + #endregion + + #region 找出变化的产品 (状态,标题) + var stateChangeProductList = productList.Where(p => dbProductList.Any(dp => dp.Id == p.Id && (dp.State != p.State || dp.ProductName != p.Title))).ToList(); + if (stateChangeProductList.Count() > 0) + { + foreach (var product in stateChangeProductList) + { + var update = fsql.Update(product.Id).Set(p => p.State, product.State) + .Set(p => p.ProductName, product.Title); + updateProductList.Add(update); + } + } + #endregion + + #region 找出接口查不出的产品 + var goneProductList = dbProductList.Where(dp => !productList.Any(p => p.Id == dp.Id)).ToList(); + if (goneProductList.Count() > 0) + { + var goneProductIdList = goneProductList.Select(p => p.Id).ToList(); + var update = fsql.Update().Set(p => p.State, 0).Where(p => goneProductIdList.Contains(p.Id)); + updateProductList.Add(update); + } + #endregion + + #region 找出新增的SKU + var newProductSkuList = productSkuList.Where(ps => !dbProductSkuList.Any(dps => dps.Id == ps.Id)).ToList(); + if (newProductSkuList.Count() > 0) + { + insertProductSkuList.AddRange(newProductSkuList.Select(ps => new ProductSku() + { + Id = ps.Id, + Logo = ps.Logo, + Price = ps.Price, + ProductId = ps.ProductId, + ShopId = int.Parse(shop.ShopId), + SkuName = ps.Title, + State = ps.State, + SyncTime = DateTime.Now, + UpdateTime = DateTime.Now, + UpperTime = ps.CreateTime + })); + } + #endregion + + #region 找出状态变化的SKU + var stateChangeProductSkuList = productSkuList.Where(ps => dbProductSkuList.Any(dps => dps.Id == ps.Id && (dps.State != ps.State || dps.SkuName != ps.Title))).ToList(); + if (stateChangeProductSkuList.Count() > 0) + { + foreach (var productSku in stateChangeProductSkuList) + { + var update = fsql.Update(productSku.Id).Set(p => p.State, productSku.State) + .Set(p => p.SkuName, productSku.Title); + updateProductSkuList.Add(update); + } + } + #endregion + + #region 找出接口查不出的SKU + var goneProductSkuList = dbProductSkuList.Where(dps => !productSkuList.Any(ps => ps.Id == dps.Id)).ToList(); + if (goneProductSkuList.Count() > 0) + { + var goneProductSkuIdList = goneProductSkuList.Select(ps => ps.Id).ToList(); + var update = fsql.Update().Set(ps => ps.State, 0).Where(ps => goneProductSkuIdList.Contains(ps.Id)); + updateProductSkuList.Add(update); + } + #endregion + + + fsql.Transaction(() => + { + if (insertProductList.Count() > 0) + fsql.Insert(insertProductList).ExecuteAffrows(); + if (insertProductSkuList.Count() > 0) + fsql.Insert(insertProductSkuList).ExecuteAffrows(); + + if (updateProductList.Count() > 0) + { + foreach (var update in updateProductList) + update.ExecuteAffrows(); + } + + if (updateProductSkuList.Count() > 0) + { + foreach (var update in updateProductSkuList) + update.ExecuteAffrows(); + } + }); + + Console.WriteLine($"{shop.ShopName}同步完毕,新增spu{insertProductList.Count()}个,新增sku{insertProductSkuList.Count()}个,更新spu{updateProductList.Count()}个,更新sku{updateProductSkuList.Count()}个"); + } + catch (Exception ex) + { + nLogManager.Default().Error(ex, $"SyncProduct ShopId:{shop.ShopName}"); + } + } + } +} diff --git a/BBWYB.Server.Business/TaskSchedulerManager.cs b/BBWYB.Server.Business/TaskSchedulerManager.cs index fa971c1..6bab173 100644 --- a/BBWYB.Server.Business/TaskSchedulerManager.cs +++ b/BBWYB.Server.Business/TaskSchedulerManager.cs @@ -4,6 +4,7 @@ namespace BBWYB.Server.Business { public class TaskSchedulerManager { + public LimitedConcurrencyLevelTaskScheduler SyncProductTaskScheduler { get; private set; } public LimitedConcurrencyLevelTaskScheduler SyncOrderTaskScheduler { get; private set; } public LimitedConcurrencyLevelTaskScheduler PurchaseOrderCallbackTaskScheduler { get; private set; } @@ -13,6 +14,7 @@ namespace BBWYB.Server.Business { SyncOrderTaskScheduler = new LimitedConcurrencyLevelTaskScheduler(10); PurchaseOrderCallbackTaskScheduler = new LimitedConcurrencyLevelTaskScheduler(10); + SyncProductTaskScheduler = new LimitedConcurrencyLevelTaskScheduler(10); } } } diff --git a/BBWYB.Server.Model/Db/Product/Product.cs b/BBWYB.Server.Model/Db/Product/Product.cs new file mode 100644 index 0000000..ee4f128 --- /dev/null +++ b/BBWYB.Server.Model/Db/Product/Product.cs @@ -0,0 +1,54 @@ +using FreeSql.DataAnnotations; + +namespace BBWYB.Server.Model.Db +{ + + /// + /// 商品表(SPU) + /// + [Table(Name = "product", DisableSyncStructure = true)] + public partial class Product + { + + [Column(StringLength = 50, IsPrimary = true, IsNullable = false)] + public string Id { get; set; } + + + public string Logo { get; set; } + + /// + /// 商品名称 + /// + [Column(StringLength = 200)] + public string ProductName { get; set; } + + [Column(DbType = "bigint")] + public long? ShopId { get; set; } + + /// + /// 状态(0下架 1上架) + /// + [Column(DbType = "int")] + public int? State { get; set; } + + /// + /// 同步时间 + /// + [Column(DbType = "datetime")] + public DateTime? SyncTime { get; set; } + + /// + /// 更新时间 + /// + [Column(DbType = "datetime")] + public DateTime? UpdateTime { get; set; } + + /// + /// 最近上架时间 + /// + [Column(DbType = "datetime")] + public DateTime? UpperTime { get; set; } + + } + +} diff --git a/BBWYB.Server.Model/Db/Product/ProductSku.cs b/BBWYB.Server.Model/Db/Product/ProductSku.cs new file mode 100644 index 0000000..0a385ba --- /dev/null +++ b/BBWYB.Server.Model/Db/Product/ProductSku.cs @@ -0,0 +1,59 @@ +using FreeSql.DataAnnotations; + +namespace BBWYB.Server.Model.Db +{ + + /// + /// 商品Sku表 + /// + [ Table(Name = "productsku", DisableSyncStructure = true)] + public partial class ProductSku { + + /// + /// SkuId + /// + [Column(StringLength = 50, IsPrimary = true, IsNullable = false)] + public string Id { get; set; } + + + public string Logo { get; set; } + + [Column(DbType = "decimal(18,2)")] + public decimal? Price { get; set; } = 0.00M; + + [Column(StringLength = 50)] + public string ProductId { get; set; } + + [Column(DbType = "bigint")] + public long? ShopId { get; set; } + + [Column(StringLength = 200)] + public string SkuName { get; set; } + + /// + /// 0下架 1上架 + /// + [Column(DbType = "int")] + public int? State { get; set; } + + /// + /// 同步时间 + /// + [Column(DbType = "datetime")] + public DateTime? SyncTime { get; set; } + + /// + /// 更新时间 + /// + [Column(DbType = "datetime")] + public DateTime? UpdateTime { get; set; } + + /// + /// 最近上架时间 + /// + [Column(DbType = "datetime")] + public DateTime? UpperTime { get; set; } + + } + +} diff --git a/QuanTan.SDK/Models/Supplier/Response/Product/QuanTan_Supplier_ProductListResponse.cs b/QuanTan.SDK/Models/Supplier/Response/Product/QuanTan_Supplier_ProductListResponse.cs index c56f556..49f68d0 100644 --- a/QuanTan.SDK/Models/Supplier/Response/Product/QuanTan_Supplier_ProductListResponse.cs +++ b/QuanTan.SDK/Models/Supplier/Response/Product/QuanTan_Supplier_ProductListResponse.cs @@ -9,6 +9,12 @@ public string Image { get; set; } public string StoreId { get; set; } + + public int? IsShow { get; set; } + + public int? Status { get; set; } + + public DateTime? UpperTime { get; set; } } public class QuanTan_Supplier_ProductListResponse : QuanTanListResponse diff --git a/QuanTan.SDK/Models/Supplier/Response/Product/QuanTan_Supplier_ProductSkuListResponse.cs b/QuanTan.SDK/Models/Supplier/Response/Product/QuanTan_Supplier_ProductSkuListResponse.cs index 51d8c36..ec89b75 100644 --- a/QuanTan.SDK/Models/Supplier/Response/Product/QuanTan_Supplier_ProductSkuListResponse.cs +++ b/QuanTan.SDK/Models/Supplier/Response/Product/QuanTan_Supplier_ProductSkuListResponse.cs @@ -17,6 +17,12 @@ public string CategoryId { get; set; } public string StoreId { get; set; } + + public int? IsShow { get; set; } + + public int? Status { get; set; } + + public DateTime? UpperTime { get; set; } } public class QuanTan_Supplier_ProductSkuListResponse : QuanTanListResponse diff --git a/SDKAdapter/OperationPlatform/Client/Impl/OP_QuanTanClient.cs b/SDKAdapter/OperationPlatform/Client/Impl/OP_QuanTanClient.cs index 6cd3169..e0cfb85 100644 --- a/SDKAdapter/OperationPlatform/Client/Impl/OP_QuanTanClient.cs +++ b/SDKAdapter/OperationPlatform/Client/Impl/OP_QuanTanClient.cs @@ -3,6 +3,7 @@ using BBWYB.Common.Models; using QuanTan.SDK.Client.Supplier; using QuanTan.SDK.Models.Supplier; using SDKAdapter.OperationPlatform.Models; +using static System.Net.WebRequestMethods; namespace SDKAdapter.OperationPlatform.Client { @@ -41,10 +42,10 @@ namespace SDKAdapter.OperationPlatform.Client { Id = qtp.ProductId, BrandName = string.Empty, - CreateTime = null, + CreateTime = qtp.UpperTime, Logo = qtp.Image, ProductItemNum = string.Empty, - State = 0, + State = qtp.IsShow == 1 && qtp.Status == 1 ? 1 : 0, Title = qtp.ProductName }).ToList() }; @@ -69,12 +70,12 @@ namespace SDKAdapter.OperationPlatform.Client Count = qtResponse.Data.Count, Items = qtResponse.Data.List.Select(qtps => new OP_ProductSkuResponse() { - CreateTime = null, + CreateTime = qtps.UpperTime, Id = qtps.ProductSku, Logo = qtps.SkuImage, Price = qtps.SkuPrice, ProductId = qtps.ProductId, - State = 0, + State = qtps.IsShow == 1 && qtps.Status == 1 ? 1 : 0, Title = qtps.SkuName }).ToList() }; diff --git a/SDKAdapter/OperationPlatform/Models/Response/Product/OP_ProductResponse.cs b/SDKAdapter/OperationPlatform/Models/Response/Product/OP_ProductResponse.cs index 85a0a2c..4f6b3b9 100644 --- a/SDKAdapter/OperationPlatform/Models/Response/Product/OP_ProductResponse.cs +++ b/SDKAdapter/OperationPlatform/Models/Response/Product/OP_ProductResponse.cs @@ -20,6 +20,7 @@ /// /// 商品状态 /// 京东【-1:删除 1:从未上架 2:自主下架 4:系统下架 8:上架 513:从未上架待审 514:自主下架待审 516:系统下架待审 520:上架待审核 1028:系统下架审核失败】 + /// 拳探【0下架 1上架】 /// public int State { get; set; } diff --git a/SDKAdapter/OperationPlatform/Models/Response/Product/OP_ProductSkuResponse.cs b/SDKAdapter/OperationPlatform/Models/Response/Product/OP_ProductSkuResponse.cs index ce130e8..3d39a54 100644 --- a/SDKAdapter/OperationPlatform/Models/Response/Product/OP_ProductSkuResponse.cs +++ b/SDKAdapter/OperationPlatform/Models/Response/Product/OP_ProductSkuResponse.cs @@ -20,6 +20,9 @@ /// /// 京东【1:上架 2:下架 4:删除】 /// + /// + /// 拳探【0下架 1上架】 + /// /// public int State { get; set; }