From 45877f0d5cb5cf74d78a1805178162110f410797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E7=81=B0=E7=81=B0?= Date: Sun, 6 Nov 2022 05:38:42 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E5=95=86=E5=93=81=E5=88=97=E8=A1=A8dto=E7=B1=BB?= =?UTF-8?q?=E5=8F=8A=E6=9F=A5=E8=AF=A2=E6=96=B9=E6=B3=95=EF=BC=8C=E9=A6=96?= =?UTF-8?q?=E9=A1=B5=E3=80=90=E5=95=86=E5=93=81=E7=BB=84=E4=BB=B6=E3=80=91?= =?UTF-8?q?=E3=80=90=E5=95=86=E5=93=81tab=E7=BB=84=E3=80=91=E3=80=90?= =?UTF-8?q?=E4=BB=BF=E7=82=B9=E9=A4=90=E7=95=8C=E9=9D=A2=E3=80=91=E3=80=90?= =?UTF-8?q?=E6=A0=8F=E7=9B=AE=E5=88=97=E8=A1=A8=E9=A1=B5=E3=80=91=E3=80=90?= =?UTF-8?q?=E6=8E=A8=E8=8D=90=E5=95=86=E5=93=81=E5=88=97=E8=A1=A8=E3=80=91?= =?UTF-8?q?=E8=BF=9B=E8=A1=8C=E6=9B=BF=E6=8D=A2=E3=80=82=20=E3=80=90?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E3=80=91=E4=BC=98=E5=8C=96=E6=99=AE=E9=80=9A?= =?UTF-8?q?=E5=95=86=E5=93=81=E5=8F=8A=E8=90=A5=E9=94=80=E5=95=86=E5=93=81?= =?UTF-8?q?=E5=86=85=E9=A1=B5=E4=B8=8D=E5=90=8Cdom=E4=B9=8B=E9=97=B4?= =?UTF-8?q?=E9=97=B4=E8=B7=9D=E3=80=82=E5=BE=AE=E8=B0=83=E5=85=B6=E4=BB=96?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E6=A0=B7=E5=BC=8F=20=E3=80=90=E8=B0=83?= =?UTF-8?q?=E6=95=B4=E3=80=91=E7=A7=BB=E9=99=A4.net5=E5=8D=87=E7=BA=A7?= =?UTF-8?q?=E5=88=B0.net6=E4=BF=9D=E7=95=99=E7=9A=84startup.cs=E6=96=87?= =?UTF-8?q?=E4=BB=B6=EF=BC=8C=E4=BD=BF=E7=94=A8program.cs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CoreCms.Net.Configuration/GlobalConstVars.cs | 16 +- CoreCms.Net.DTO/ComponentsDTO/GoodListDTO.cs | 150 +++++++ .../Good/ICoreCmsGoodsRepository.cs | 46 ++- .../Good/ICoreCmsGoodsServices.cs | 44 +- .../Good/CoreCmsGoodsRepository.cs | 390 ++++++++++++++++-- .../Good/CoreCmsGoodsServices.cs | 62 ++- .../Shop/CoreCmsPagesServices.cs | 30 +- .../coreshop-page/coreshop-goodTabBar.vue | 4 +- .../coreshop-page/coreshop-goods.vue | 10 +- .../groupBuyingDetails/groupBuyingDetails.vue | 20 +- .../pinTuan/pinTuanDetails/pinTuanDetails.vue | 26 +- .../seckill/seckillDetails/seckillDetails.vue | 24 +- .../CoreShop/pages/category/index/index.scss | 10 +- .../CoreShop/pages/category/index/index.vue | 18 +- .../CoreShop/pages/category/list/list.vue | 17 +- .../pages/goods/goodDetails/goodDetails.vue | 26 +- .../static/images/common/empty-banner.png | Bin 8014 -> 4778 bytes .../CoreShop/static/images/common/empty.png | Bin 1001 -> 2297 bytes CoreCms.Net.Web.Admin/Program.cs | 305 +++++++++++--- CoreCms.Net.Web.Admin/Startup.cs | 259 ------------ .../Controllers/GoodController.cs | 15 +- CoreCms.Net.Web.WebApi/Program.cs | 349 +++++++++++++--- CoreCms.Net.Web.WebApi/Startup.cs | 314 -------------- 23 files changed, 1316 insertions(+), 819 deletions(-) create mode 100644 CoreCms.Net.DTO/ComponentsDTO/GoodListDTO.cs delete mode 100644 CoreCms.Net.Web.Admin/Startup.cs delete mode 100644 CoreCms.Net.Web.WebApi/Startup.cs diff --git a/CoreCms.Net.Configuration/GlobalConstVars.cs b/CoreCms.Net.Configuration/GlobalConstVars.cs index 94dc4a44..106365a3 100644 --- a/CoreCms.Net.Configuration/GlobalConstVars.cs +++ b/CoreCms.Net.Configuration/GlobalConstVars.cs @@ -418,7 +418,7 @@ namespace CoreCms.Net.Configuration /// 微信自定义交易组件-同步创建售后单 /// public const string TransactionComponentAfterSaleCreateSync = "TransactionComponentAfterSaleCreateSyncQueue"; - + //用户相关 @@ -460,4 +460,18 @@ namespace CoreCms.Net.Configuration } + /// + /// 当字段为空图片,替换前端的地址 + /// + public static class EmptyOrNullImagePath + { + /// + /// 图片地址 + /// + public static string GoodImage = "/static/images/common/empty.png"; + + + } + + } diff --git a/CoreCms.Net.DTO/ComponentsDTO/GoodListDTO.cs b/CoreCms.Net.DTO/ComponentsDTO/GoodListDTO.cs new file mode 100644 index 00000000..94a59479 --- /dev/null +++ b/CoreCms.Net.DTO/ComponentsDTO/GoodListDTO.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Security.Principal; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace CoreCms.Net.DTO.ComponentsDTO +{ + /// + /// 商品列表中需要的字段 + /// + public class GoodListDTO + { + /// + /// 商品ID + /// + public int id { get; set; } + + /// + /// 商品名称 + /// + public string name { get; set; } + + /// + /// 商品简介 + /// + public string brief { get; set; } + + /// + /// 缩略图 + /// + public string image { get; set; } + + /// + /// 视频 + /// + public string video { get; set; } + + /// + /// 商品分类 + /// + public int goodsCategoryId { get; set; } + + /// + /// 商品类别 + /// + public int goodsTypeId { get; set; } + + /// + /// 品牌 + /// + public int brandId { get; set; } + + /// + /// 是否虚拟商品 + /// + public bool isNomalVirtual { get; set; } + + /// + /// 商品单位 + /// + public string unit { get; set; } + + /// + /// 评论次数 + /// + public int commentsCount { get; set; } + + /// + /// 浏览次数 + /// + public int viewCount { get; set; } + + /// + /// 购买次数 + /// + public int buyCount { get; set; } + + /// + /// 商品排序 + /// + public int sort { get; set; } + + /// + /// 标签id逗号分隔 + /// + public string labelIds { get; set; } + + /// + /// 创建时间 + /// + public DateTime? createTime { get; set; } + + /// + /// 是否推荐 + /// + public bool isRecommend { get; set; } + + /// + /// 是否热门 + /// + public bool isHot { get; set; } + + /// + /// 初始销量 + /// + public int initialSales { get; set; } + + + /// + /// 货品价格 + /// + public System.Decimal price { get; set; } + + /// + /// 货品市场价 + /// + + public System.Decimal mktprice { get; set; } + + + /// + /// 积分可抵扣金额 + /// + public System.Decimal pointsDeduction { get; set; } + + + /// + /// 赠送积分 + /// + public System.Int32 points { get; set; } + + + /// + /// 重量(千克) + /// + public System.Decimal weight { get; set; } + + + /// + /// 库存 + /// + public System.Int32 stock { get; set; } + + + } +} diff --git a/CoreCms.Net.IRepository/Good/ICoreCmsGoodsRepository.cs b/CoreCms.Net.IRepository/Good/ICoreCmsGoodsRepository.cs index 1ebf7237..afa44a55 100644 --- a/CoreCms.Net.IRepository/Good/ICoreCmsGoodsRepository.cs +++ b/CoreCms.Net.IRepository/Good/ICoreCmsGoodsRepository.cs @@ -12,6 +12,7 @@ using System; using System.Collections.Generic; using System.Linq.Expressions; using System.Threading.Tasks; +using CoreCms.Net.DTO.ComponentsDTO; using CoreCms.Net.Model.Entities; using CoreCms.Net.Model.FromBody; using CoreCms.Net.Model.ViewModels.Basics; @@ -71,7 +72,7 @@ namespace CoreCms.Net.IRepository /// 数量 /// 是否推荐 /// - Task> GetGoodsRecommendList(int number, bool isRecommend = false); + Task> GetGoodsRecommendList(int number, bool isRecommend = false); /// /// 获取数据总数 @@ -137,6 +138,19 @@ namespace CoreCms.Net.IRepository int pageSize = 20, bool blUseNoLock = false); + /// + /// 重写根据条件及自定义排序查询分页数据(返回DTO) + /// + /// + /// + /// 当前页面索引 + /// 分布大小 + /// 是否使用WITH(NOLOCK) + /// + Task> QueryPageByDTOAsync(Expression> predicate, + string orderBy = "", int pageIndex = 1, int pageSize = 20, bool blUseNoLock = false); + + /// /// 根据条件查询代理池商品分页数据 /// @@ -183,5 +197,35 @@ namespace CoreCms.Net.IRepository Task> QueryGoodWithDefaultProductAsync(Expression> predicate, Expression> orderByPredicate, OrderByType orderByType, bool blUseNoLock = false); + + /// + /// 根据条件查询一定数量数据(用于组件) + /// + /// 条件表达式树 + /// 获取数量 + /// 排序字段 + /// 排序顺序 + /// 是否使用WITH(NOLOCK) + /// 是否缓存 + /// 缓存时间(分钟) + /// + Task> QueryListByComponentsAsync(Expression> predicate, int take, + Expression> orderByPredicate, OrderByType orderByType, bool blUseNoLock = false, + bool isDataCache = false, int cacheTimes = int.MaxValue); + + + /// + /// 根据条件查询一定数量数据(用于组件) + /// + /// 条件表达式树 + /// 获取数量 + /// 排序顺序 + /// 是否使用WITH(NOLOCK) + /// 是否缓存 + /// 缓存时间(分钟) + /// + Task> QueryListByComponentsAsync(Expression> predicate, int take, string orderByType = "", bool blUseNoLock = false, + bool isDataCache = false, int cacheTimes = int.MaxValue); + } } \ No newline at end of file diff --git a/CoreCms.Net.IServices/Good/ICoreCmsGoodsServices.cs b/CoreCms.Net.IServices/Good/ICoreCmsGoodsServices.cs index 91cd455e..d0429daf 100644 --- a/CoreCms.Net.IServices/Good/ICoreCmsGoodsServices.cs +++ b/CoreCms.Net.IServices/Good/ICoreCmsGoodsServices.cs @@ -12,6 +12,7 @@ using System; using System.Collections.Generic; using System.Linq.Expressions; using System.Threading.Tasks; +using CoreCms.Net.DTO.ComponentsDTO; using CoreCms.Net.Model.Entities; using CoreCms.Net.Model.FromBody; using CoreCms.Net.Model.ViewModels.Basics; @@ -135,7 +136,7 @@ namespace CoreCms.Net.IServices /// /// 是否推荐 /// - Task> GetGoodsRecommendList(int number, bool isRecommend = false); + Task> GetGoodsRecommendList(int number, bool isRecommend = false); /// @@ -201,6 +202,17 @@ namespace CoreCms.Net.IServices string orderBy = "", int pageIndex = 1, int pageSize = 20, bool blUseNoLock = false); + /// + /// 重写根据条件及自定义排序查询分页数据(返回DTO) + /// + /// + /// + /// 当前页面索引 + /// 分布大小 + /// 是否使用WITH(NOLOCK) + /// + Task> QueryPageByDTOAsync(Expression> predicate, + string orderBy = "", int pageIndex = 1, int pageSize = 20, bool blUseNoLock = false); /// /// 重写根据条件及自定义排序查询分页数据 @@ -275,5 +287,35 @@ namespace CoreCms.Net.IServices Task> QueryGoodWithDefaultProductAsync(Expression> predicate, Expression> orderByPredicate, OrderByType orderByType, bool blUseNoLock = false); + + /// + /// 根据条件查询一定数量数据(用于组件) + /// + /// 条件表达式树 + /// 获取数量 + /// 排序字段 + /// 排序顺序 + /// 是否使用WITH(NOLOCK) + /// 是否缓存 + /// 缓存时间(分钟) + /// + Task> QueryListByComponentsAsync(Expression> predicate, int take, + Expression> orderByPredicate, OrderByType orderByType, bool blUseNoLock = false, + bool isDataCache = false, int cacheTimes = int.MaxValue); + + + /// + /// 根据条件查询一定数量数据(用于组件) + /// + /// 条件表达式树 + /// 获取数量 + /// 排序顺序 + /// 是否使用WITH(NOLOCK) + /// 是否缓存 + /// 缓存时间(分钟) + /// + Task> QueryListByComponentsAsync(Expression> predicate, int take, string orderByType = "", bool blUseNoLock = false, + bool isDataCache = false, int cacheTimes = int.MaxValue); + } } \ No newline at end of file diff --git a/CoreCms.Net.Repository/Good/CoreCmsGoodsRepository.cs b/CoreCms.Net.Repository/Good/CoreCmsGoodsRepository.cs index 9e2fcc2f..a977ce07 100644 --- a/CoreCms.Net.Repository/Good/CoreCmsGoodsRepository.cs +++ b/CoreCms.Net.Repository/Good/CoreCmsGoodsRepository.cs @@ -13,6 +13,7 @@ using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; using CoreCms.Net.Configuration; +using CoreCms.Net.DTO.ComponentsDTO; using CoreCms.Net.IRepository; using CoreCms.Net.IRepository.UnitOfWork; using CoreCms.Net.Loging; @@ -278,6 +279,8 @@ namespace CoreCms.Net.Repository } } } + //删除dto缓存 + DbClient.DataCache.RemoveDataCache(nameof(GoodListDTO)); jm.data = entity; jm.code = bl ? 0 : 1; @@ -733,6 +736,10 @@ namespace CoreCms.Net.Repository } } //事物处理过程结束 + + //删除dto缓存 + DbClient.DataCache.RemoveDataCache(nameof(GoodListDTO)); + jm.code = bl ? 0 : 1; jm.msg = bl ? GlobalConstVars.EditSuccess : GlobalConstVars.EditFailure; @@ -756,6 +763,12 @@ namespace CoreCms.Net.Repository jm.code = bl ? 0 : 1; jm.msg = bl ? GlobalConstVars.DeleteSuccess : GlobalConstVars.DeleteFailure; + if (bl) + { + //删除dto缓存 + DbClient.DataCache.RemoveDataCache(nameof(GoodListDTO)); + } + return jm; } @@ -882,54 +895,41 @@ namespace CoreCms.Net.Repository /// /// /// - public async Task> GetGoodsRecommendList(int number, bool isRecommend = false) + public async Task> GetGoodsRecommendList(int number, bool isRecommend = false) { - var list = new List(); + var list = new List(); if (isRecommend) { list = await DbClient.Queryable((good, pd) => new JoinQueryInfos( JoinType.Left, good.id == pd.goodsId)) .Where((good, pd) => pd.isDefalut == true && pd.isDel == false && good.isRecommend == true && good.isDel == false && good.isMarketable == true) - .Select((good, pd) => new CoreCmsGoods + .Select((good, pd) => new GoodListDTO() { id = good.id, - bn = good.bn, name = good.name, brief = good.brief, - image = good.image, - images = good.images, + image = !SqlFunc.IsNullOrEmpty(good.image) ? good.image : EmptyOrNullImagePath.GoodImage, video = good.video, - productsDistributionType = good.productsDistributionType, goodsCategoryId = good.goodsCategoryId, goodsTypeId = good.goodsTypeId, brandId = good.brandId, isNomalVirtual = good.isNomalVirtual, - isMarketable = good.isMarketable, unit = good.unit, - //intro = good.intro, - spesDesc = good.spesDesc, - parameters = good.parameters, commentsCount = good.commentsCount, viewCount = good.viewCount, - buyCount = good.buyCount, - uptime = good.uptime, - downtime = good.downtime, + buyCount = SqlFunc.Subqueryable() + .LeftJoin((sOrderItem, sOrder) => sOrder.orderId == sOrderItem.orderId) + .Where((sOrderItem, sOrder) => sOrderItem.goodsId == good.id && (sOrder.payStatus == (int)GlobalEnumVars.OrderPayStatus.Yes || sOrder.payStatus == (int)GlobalEnumVars.OrderPayStatus.PartialYes)) + .Sum(p => p.nums), sort = good.sort, labelIds = good.labelIds, - newSpec = good.newSpec, - openSpec = good.openSpec, createTime = good.createTime, - updateTime = good.updateTime, isRecommend = good.isRecommend, isHot = good.isHot, - isDel = good.isDel, - sn = pd.sn, price = pd.price, - costprice = pd.costprice, mktprice = pd.mktprice, stock = pd.stock, - freezeStock = pd.freezeStock, pointsDeduction = pd.pointsDeduction, points = pd.points, weight = pd.weight, @@ -955,46 +955,33 @@ namespace CoreCms.Net.Repository { list = await DbClient.Queryable((good, pd) => new JoinQueryInfos( JoinType.Left, good.id == pd.goodsId)) - .Where((good, pd) => pd.isDefalut == true && pd.isDel == false) - .Select((good, pd) => new CoreCmsGoods + .Where((good, pd) => pd.isDefalut == true && pd.isDel == false && good.isRecommend == true && good.isDel == false && good.isMarketable == true) + .Select((good, pd) => new GoodListDTO { id = good.id, - bn = good.bn, name = good.name, brief = good.brief, - image = good.image, - images = good.images, + image = !SqlFunc.IsNullOrEmpty(good.image) ? good.image : EmptyOrNullImagePath.GoodImage, video = good.video, - productsDistributionType = good.productsDistributionType, goodsCategoryId = good.goodsCategoryId, goodsTypeId = good.goodsTypeId, brandId = good.brandId, isNomalVirtual = good.isNomalVirtual, - isMarketable = good.isMarketable, unit = good.unit, - //intro = good.intro, - spesDesc = good.spesDesc, - parameters = good.parameters, commentsCount = good.commentsCount, viewCount = good.viewCount, - buyCount = good.buyCount, - uptime = good.uptime, - downtime = good.downtime, + buyCount = SqlFunc.Subqueryable() + .LeftJoin((sOrderItem, sOrder) => sOrder.orderId == sOrderItem.orderId) + .Where((sOrderItem, sOrder) => sOrderItem.goodsId == good.id && (sOrder.payStatus == (int)GlobalEnumVars.OrderPayStatus.Yes || sOrder.payStatus == (int)GlobalEnumVars.OrderPayStatus.PartialYes)) + .Sum(p => p.nums), sort = good.sort, labelIds = good.labelIds, - newSpec = good.newSpec, - openSpec = good.openSpec, createTime = good.createTime, - updateTime = good.updateTime, isRecommend = good.isRecommend, isHot = good.isHot, - isDel = good.isDel, - sn = pd.sn, price = pd.price, - costprice = pd.costprice, mktprice = pd.mktprice, stock = pd.stock, - freezeStock = pd.freezeStock, pointsDeduction = pd.pointsDeduction, points = pd.points, weight = pd.weight, @@ -1655,6 +1642,113 @@ namespace CoreCms.Net.Repository } + #endregion + + #region 重写根据条件及自定义排序查询分页数据(返回DTO) + /// + /// 重写根据条件及自定义排序查询分页数据(返回DTO) + /// + /// + /// + /// 当前页面索引 + /// 分布大小 + /// 是否使用WITH(NOLOCK) + /// + public async Task> QueryPageByDTOAsync(Expression> predicate, string orderBy = "", + int pageIndex = 1, int pageSize = 20, bool blUseNoLock = false) + { + RefAsync totalCount = 0; + List page; + if (blUseNoLock) + { + page = await DbClient.Queryable((good, pd) => new JoinQueryInfos( + JoinType.Left, good.id == pd.goodsId)) + .Where((good, pd) => pd.isDefalut == true && pd.isDel == false && good.isMarketable == true && good.isDel == false) + .Select((good, pd) => new GoodListDTO + { + id = good.id, + name = good.name, + brief = good.brief, + image = !SqlFunc.IsNullOrEmpty(good.image) ? good.image : EmptyOrNullImagePath.GoodImage, + video = good.video, + goodsCategoryId = good.goodsCategoryId, + goodsTypeId = good.goodsTypeId, + brandId = good.brandId, + isNomalVirtual = good.isNomalVirtual, + unit = good.unit, + commentsCount = good.commentsCount, + viewCount = good.viewCount, + buyCount = SqlFunc.Subqueryable() + .LeftJoin((sOrderItem, sOrder) => sOrder.orderId == sOrderItem.orderId) + .Where((sOrderItem, sOrder) => sOrderItem.goodsId == good.id && (sOrder.payStatus == (int)GlobalEnumVars.OrderPayStatus.Yes || sOrder.payStatus == (int)GlobalEnumVars.OrderPayStatus.PartialYes)) + .Sum(p => p.nums), + sort = good.sort, + labelIds = good.labelIds, + createTime = good.createTime, + isRecommend = good.isRecommend, + isHot = good.isHot, + price = pd.price, + mktprice = pd.mktprice, + stock = pd.stock, + pointsDeduction = pd.pointsDeduction, + points = pd.points, + weight = pd.weight, + initialSales = good.initialSales, + }) + .With(SqlWith.NoLock) + .MergeTable() + .Where(predicate) + .OrderBy(orderBy) + .ToPageListAsync(pageIndex, pageSize, totalCount); + } + else + { + page = await DbClient.Queryable((good, pd) => new JoinQueryInfos( + JoinType.Left, good.id == pd.goodsId)) + .Where((good, pd) => pd.isDefalut == true && pd.isDel == false && good.isMarketable == true && good.isDel == false) + .Select((good, pd) => new GoodListDTO + { + id = good.id, + name = good.name, + brief = good.brief, + image = !SqlFunc.IsNullOrEmpty(good.image) ? good.image : EmptyOrNullImagePath.GoodImage, + video = good.video, + goodsCategoryId = good.goodsCategoryId, + goodsTypeId = good.goodsTypeId, + brandId = good.brandId, + isNomalVirtual = good.isNomalVirtual, + unit = good.unit, + commentsCount = good.commentsCount, + viewCount = good.viewCount, + buyCount = SqlFunc.Subqueryable() + .LeftJoin((sOrderItem, sOrder) => sOrder.orderId == sOrderItem.orderId) + .Where((sOrderItem, sOrder) => sOrderItem.goodsId == good.id && (sOrder.payStatus == (int)GlobalEnumVars.OrderPayStatus.Yes || sOrder.payStatus == (int)GlobalEnumVars.OrderPayStatus.PartialYes)) + .Sum(p => p.nums), + sort = good.sort, + labelIds = good.labelIds, + createTime = good.createTime, + isRecommend = good.isRecommend, + isHot = good.isHot, + price = pd.price, + mktprice = pd.mktprice, + stock = pd.stock, + pointsDeduction = pd.pointsDeduction, + points = pd.points, + weight = pd.weight, + initialSales = good.initialSales, + }) + .MergeTable() + .Where(predicate) + .OrderBy(orderBy) + .ToPageListAsync(pageIndex, pageSize, totalCount); + } + var list = new PageList(page, pageIndex, pageSize, totalCount); + return list; + + + } + + #endregion #region 根据条件查询代理池商品分页数据 @@ -2056,5 +2150,219 @@ namespace CoreCms.Net.Repository #endregion + #region 根据条件查询一定数量数据(用于组件) + + /// + /// 根据条件查询一定数量数据(用于组件) + /// + /// 条件表达式树 + /// 获取数量 + /// 排序字段 + /// 排序顺序 + /// 是否使用WITH(NOLOCK) + /// 是否缓存 + /// 缓存时间(分钟) + /// + public async Task> QueryListByComponentsAsync(Expression> predicate, int take, + Expression> orderByPredicate, OrderByType orderByType, bool blUseNoLock = false, bool isDataCache = false, int cacheTimes = int.MaxValue) + { + List page; + if (blUseNoLock) + { + page = await DbClient.Queryable((good, pd) => new JoinQueryInfos( + JoinType.Left, good.id == pd.goodsId)) + .Where((good, pd) => pd.isDefalut == true && pd.isDel == false && good.isMarketable == true && good.isDel == false) + .Select((good, pd) => new GoodListDTO + { + id = good.id, + name = good.name, + brief = good.brief, + image = !SqlFunc.IsNullOrEmpty(good.image) ? good.image : EmptyOrNullImagePath.GoodImage, + video = good.video, + goodsCategoryId = good.goodsCategoryId, + goodsTypeId = good.goodsTypeId, + brandId = good.brandId, + isNomalVirtual = good.isNomalVirtual, + unit = good.unit, + commentsCount = good.commentsCount, + viewCount = good.viewCount, + buyCount = SqlFunc.Subqueryable() + .LeftJoin((sOrderItem, sOrder) => sOrder.orderId == sOrderItem.orderId) + .Where((sOrderItem, sOrder) => sOrderItem.goodsId == good.id && (sOrder.payStatus == (int)GlobalEnumVars.OrderPayStatus.Yes || sOrder.payStatus == (int)GlobalEnumVars.OrderPayStatus.PartialYes)) + .Sum(p => p.nums), + sort = good.sort, + labelIds = good.labelIds, + createTime = good.createTime, + isRecommend = good.isRecommend, + isHot = good.isHot, + price = pd.price, + mktprice = pd.mktprice, + stock = pd.stock, + pointsDeduction = pd.pointsDeduction, + points = pd.points, + weight = pd.weight, + initialSales = good.initialSales, + }) + .With(SqlWith.NoLock) + .MergeTable() + .WhereIF(predicate != null, predicate) + .OrderByIF(orderByPredicate != null, orderByPredicate, orderByType) + .Take(take) + .WithCacheIF(isDataCache, cacheTimes) + .ToListAsync(); + } + else + { + page = await DbClient.Queryable((good, pd) => new JoinQueryInfos( + JoinType.Left, good.id == pd.goodsId)) + .Where((good, pd) => pd.isDefalut == true && pd.isDel == false && good.isMarketable == true && good.isDel == false) + .Select((good, pd) => new GoodListDTO + { + id = good.id, + name = good.name, + brief = good.brief, + image = !SqlFunc.IsNullOrEmpty(good.image) ? good.image : EmptyOrNullImagePath.GoodImage, + video = good.video, + goodsCategoryId = good.goodsCategoryId, + goodsTypeId = good.goodsTypeId, + brandId = good.brandId, + isNomalVirtual = good.isNomalVirtual, + unit = good.unit, + commentsCount = good.commentsCount, + viewCount = good.viewCount, + buyCount = SqlFunc.Subqueryable() + .LeftJoin((sOrderItem, sOrder) => sOrder.orderId == sOrderItem.orderId) + .Where((sOrderItem, sOrder) => sOrderItem.goodsId == good.id && (sOrder.payStatus == (int)GlobalEnumVars.OrderPayStatus.Yes || sOrder.payStatus == (int)GlobalEnumVars.OrderPayStatus.PartialYes)) + .Sum(p => p.nums), + sort = good.sort, + labelIds = good.labelIds, + createTime = good.createTime, + isRecommend = good.isRecommend, + isHot = good.isHot, + price = pd.price, + mktprice = pd.mktprice, + stock = pd.stock, + pointsDeduction = pd.pointsDeduction, + points = pd.points, + weight = pd.weight, + initialSales = good.initialSales, + }) + .MergeTable() + .WhereIF(predicate != null, predicate) + .OrderByIF(orderByPredicate != null, orderByPredicate, orderByType) + .WithCacheIF(isDataCache, cacheTimes) + .Take(take).ToListAsync(); + } + return page; + + } + #endregion + + #region 根据条件查询一定数量数据(用于组件) + + /// + /// 根据条件查询一定数量数据(用于组件) + /// + /// 条件表达式树 + /// 获取数量 + /// 排序顺序 + /// 是否使用WITH(NOLOCK) + /// 是否缓存 + /// 缓存时间(分钟) + /// + public async Task> QueryListByComponentsAsync(Expression> predicate, int take, string orderByType = "", bool blUseNoLock = false, bool isDataCache = false, int cacheTimes = int.MaxValue) + { + List page; + if (blUseNoLock) + { + page = await DbClient.Queryable((good, pd) => new JoinQueryInfos( + JoinType.Left, good.id == pd.goodsId)) + .Where((good, pd) => pd.isDefalut == true && pd.isDel == false && good.isMarketable == true && good.isDel == false) + .Select((good, pd) => new GoodListDTO + { + id = good.id, + name = good.name, + brief = good.brief, + image = !SqlFunc.IsNullOrEmpty(good.image) ? good.image : EmptyOrNullImagePath.GoodImage, + video = good.video, + goodsCategoryId = good.goodsCategoryId, + goodsTypeId = good.goodsTypeId, + brandId = good.brandId, + isNomalVirtual = good.isNomalVirtual, + unit = good.unit, + commentsCount = good.commentsCount, + viewCount = good.viewCount, + buyCount = SqlFunc.Subqueryable() + .LeftJoin((sOrderItem, sOrder) => sOrder.orderId == sOrderItem.orderId) + .Where((sOrderItem, sOrder) => sOrderItem.goodsId == good.id && (sOrder.payStatus == (int)GlobalEnumVars.OrderPayStatus.Yes || sOrder.payStatus == (int)GlobalEnumVars.OrderPayStatus.PartialYes)) + .Sum(p => p.nums), + sort = good.sort, + labelIds = good.labelIds, + createTime = good.createTime, + isRecommend = good.isRecommend, + isHot = good.isHot, + price = pd.price, + mktprice = pd.mktprice, + stock = pd.stock, + pointsDeduction = pd.pointsDeduction, + points = pd.points, + weight = pd.weight, + initialSales = good.initialSales, + }) + .With(SqlWith.NoLock) + .MergeTable() + .WhereIF(predicate != null, predicate) + .OrderByIF(!string.IsNullOrEmpty(orderByType), orderByType) + .Take(take) + .WithCacheIF(isDataCache, cacheTimes) + .ToListAsync(); + } + else + { + page = await DbClient.Queryable((good, pd) => new JoinQueryInfos( + JoinType.Left, good.id == pd.goodsId)) + .Where((good, pd) => pd.isDefalut == true && pd.isDel == false && good.isMarketable == true && good.isDel == false) + .Select((good, pd) => new GoodListDTO + { + id = good.id, + name = good.name, + brief = good.brief, + image = !SqlFunc.IsNullOrEmpty(good.image) ? good.image : EmptyOrNullImagePath.GoodImage, + video = good.video, + goodsCategoryId = good.goodsCategoryId, + goodsTypeId = good.goodsTypeId, + brandId = good.brandId, + isNomalVirtual = good.isNomalVirtual, + unit = good.unit, + commentsCount = good.commentsCount, + viewCount = good.viewCount, + buyCount = SqlFunc.Subqueryable() + .LeftJoin((sOrderItem, sOrder) => sOrder.orderId == sOrderItem.orderId) + .Where((sOrderItem, sOrder) => sOrderItem.goodsId == good.id && (sOrder.payStatus == (int)GlobalEnumVars.OrderPayStatus.Yes || sOrder.payStatus == (int)GlobalEnumVars.OrderPayStatus.PartialYes)) + .Sum(p => p.nums), + sort = good.sort, + labelIds = good.labelIds, + createTime = good.createTime, + isRecommend = good.isRecommend, + isHot = good.isHot, + price = pd.price, + mktprice = pd.mktprice, + stock = pd.stock, + pointsDeduction = pd.pointsDeduction, + points = pd.points, + weight = pd.weight, + initialSales = good.initialSales, + }) + .MergeTable() + .WhereIF(predicate != null, predicate) + .OrderByIF(!string.IsNullOrEmpty(orderByType), orderByType) + .WithCacheIF(isDataCache, cacheTimes) + .Take(take).ToListAsync(); + } + return page; + + } + #endregion + } } diff --git a/CoreCms.Net.Services/Good/CoreCmsGoodsServices.cs b/CoreCms.Net.Services/Good/CoreCmsGoodsServices.cs index 83e5f261..20de0531 100644 --- a/CoreCms.Net.Services/Good/CoreCmsGoodsServices.cs +++ b/CoreCms.Net.Services/Good/CoreCmsGoodsServices.cs @@ -15,6 +15,7 @@ using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; using CoreCms.Net.Configuration; +using CoreCms.Net.DTO.ComponentsDTO; using CoreCms.Net.IRepository; using CoreCms.Net.IRepository.UnitOfWork; using CoreCms.Net.IServices; @@ -910,7 +911,7 @@ namespace CoreCms.Net.Services /// /// /// - public async Task> GetGoodsRecommendList(int number, bool isRecommend = false) + public async Task> GetGoodsRecommendList(int number, bool isRecommend = false) { return await _dal.GetGoodsRecommendList(number, isRecommend); } @@ -998,6 +999,24 @@ namespace CoreCms.Net.Services } #endregion + + #region 重写根据条件及自定义排序查询分页数据(返回DTO) + /// + /// 重写根据条件及自定义排序查询分页数据(返回DTO) + /// + /// + /// + /// 当前页面索引 + /// 分布大小 + /// 是否使用WITH(NOLOCK) + /// + public async Task> QueryPageByDTOAsync(Expression> predicate, + string orderBy = "", int pageIndex = 1, int pageSize = 20, bool blUseNoLock = false) + { + return await _dal.QueryPageByDTOAsync(predicate, orderBy, pageIndex, pageSize, blUseNoLock); + } + #endregion + /// /// 根据条件查询代理池商品分页数据 /// @@ -1058,5 +1077,46 @@ namespace CoreCms.Net.Services return await _dal.QueryGoodWithDefaultProductAsync(predicate, orderByPredicate, orderByType, blUseNoLock); } + /// + /// 根据条件查询一定数量数据(用于组件) + /// + /// 条件表达式树 + /// 获取数量 + /// 排序字段 + /// 排序顺序 + /// 是否使用WITH(NOLOCK) + /// 是否缓存 + /// 缓存时间(分钟) + /// + public async Task> QueryListByComponentsAsync(Expression> predicate, + int take, + Expression> orderByPredicate, OrderByType orderByType, bool blUseNoLock = false, + bool isDataCache = false, int cacheTimes = int.MaxValue) + { + return await _dal.QueryListByComponentsAsync(predicate, take, orderByPredicate, orderByType, blUseNoLock, + isDataCache, cacheTimes); + } + + + /// + /// 根据条件查询一定数量数据(用于组件) + /// + /// 条件表达式树 + /// 获取数量 + /// 排序字段 + /// 排序顺序 + /// 是否使用WITH(NOLOCK) + /// 是否缓存 + /// 缓存时间(分钟) + /// + public async Task> QueryListByComponentsAsync(Expression> predicate, + int take, string orderByType = "", bool blUseNoLock = false, + bool isDataCache = false, int cacheTimes = int.MaxValue) + { + return await _dal.QueryListByComponentsAsync(predicate, take, orderByType, blUseNoLock, + isDataCache, cacheTimes); + } + + } } diff --git a/CoreCms.Net.Services/Shop/CoreCmsPagesServices.cs b/CoreCms.Net.Services/Shop/CoreCmsPagesServices.cs index eb1803b3..cec8e239 100644 --- a/CoreCms.Net.Services/Shop/CoreCmsPagesServices.cs +++ b/CoreCms.Net.Services/Shop/CoreCmsPagesServices.cs @@ -13,6 +13,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using CoreCms.Net.Configuration; +using CoreCms.Net.DTO.ComponentsDTO; using CoreCms.Net.IRepository; using CoreCms.Net.IRepository.UnitOfWork; using CoreCms.Net.IServices; @@ -26,7 +27,9 @@ using CoreCms.Net.Utility.Extensions; using Essensoft.Paylink.Alipay.Domain; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using NPOI.SS.Formula.Functions; using SqlSugar; +using static NPOI.HSSF.Util.HSSFColor; namespace CoreCms.Net.Services @@ -267,10 +270,7 @@ namespace CoreCms.Net.Services foreach (var jToken in list) { var child = (JObject)jToken; - var where = PredicateBuilder.True(); - where = where.And(p => p.isDel == false); - where = where.And(p => p.isMarketable == true); - + var where = PredicateBuilder.True(); if (child != null && child.ContainsKey("type") && child["type"].ToString() == "auto") { //商品分类,同时取所有子分类 @@ -301,7 +301,8 @@ namespace CoreCms.Net.Services } limit = limit > 0 ? limit : 10; - var goods = await _goodsServices.QueryListByClauseAsync(where, limit, p => p.createTime, OrderByType.Desc, true); + var goods = await _goodsServices.QueryListByComponentsAsync(where, limit, p => p.createTime, OrderByType.Desc, true, true, 60); + if (goods != null && goods.Any()) { var result = JArray.FromObject(goods); @@ -318,6 +319,7 @@ namespace CoreCms.Net.Services { var orderBy = string.Empty; string goodidsStr; + var count = 0; if (child != null && child.ContainsKey("list")) { var result = JArray.Parse(child["list"].ToString()); @@ -335,9 +337,11 @@ namespace CoreCms.Net.Services { orderBy = " find_in_set(id,'" + goodidsStr + "') "; } + + count = goodids.Count; } } - var goods = await _goodsServices.QueryListByClauseAsync(where, orderBy, true); + var goods = await _goodsServices.QueryListByComponentsAsync(where, count, orderBy, true, true, 60); if (goods != null && goods.Any()) { var result = JArray.FromObject(goods); @@ -364,9 +368,7 @@ namespace CoreCms.Net.Services case "goods": { var parameters = (JObject)JsonConvert.DeserializeObject(item.parameters); - var where = PredicateBuilder.True(); - where = where.And(p => p.isDel == false); - where = where.And(p => p.isMarketable == true); + var where = PredicateBuilder.True(); if (parameters != null && parameters.ContainsKey("type") && parameters["type"].ToString() == "auto") { //商品分类,同时取所有子分类 @@ -397,7 +399,7 @@ namespace CoreCms.Net.Services } limit = limit > 0 ? limit : 10; - var goods = await _goodsServices.QueryPageAsync(where, " sort desc,id desc ", 1, limit, true); + var goods = await _goodsServices.QueryListByComponentsAsync(where, limit, " sort desc,id desc ", true, true, 60); if (goods != null && goods.Any()) { var result = JArray.FromObject(goods); @@ -414,9 +416,10 @@ namespace CoreCms.Net.Services { var orderBy = string.Empty; string goodidsStr; + var count = 0; if (parameters != null && parameters.ContainsKey("list")) { - JArray result = JArray.Parse(parameters["list"].ToString()); + var result = JArray.Parse(parameters["list"].ToString()); var goodids = new List(); foreach (var ss in result) //查找某个字段与值 { @@ -440,11 +443,12 @@ namespace CoreCms.Net.Services orderBy = " find_in_set(id,'" + goodidsStr + "') "; } } + count = goodids.Count; } - var goods = await _goodsServices.QueryListByClauseAsync(where, orderBy, true); + var goods = await _goodsServices.QueryListByComponentsAsync(where, count, orderBy, true, true, 60); if (goods != null && goods.Any()) { - JArray result = JArray.FromObject(goods); + var result = JArray.FromObject(goods); parameters.Remove("list"); parameters.Add("list", result); } diff --git a/CoreCms.Net.Uni-App/CoreShop/components/coreshop-page/coreshop-goodTabBar.vue b/CoreCms.Net.Uni-App/CoreShop/components/coreshop-page/coreshop-goodTabBar.vue index 4ecf1aeb..78c22a34 100644 --- a/CoreCms.Net.Uni-App/CoreShop/components/coreshop-page/coreshop-goodTabBar.vue +++ b/CoreCms.Net.Uni-App/CoreShop/components/coreshop-page/coreshop-goodTabBar.vue @@ -20,7 +20,7 @@ {{item.mktprice}}元 - {{item.buyCount+'人付款'}} + 已售{{item.buyCount+item.initialSales}}{{item.unit}} @@ -59,7 +59,7 @@ {{item.mktprice}}元 - {{item.buyCount+'人付款'}} + 已售{{item.buyCount+item.initialSales}}{{item.unit}} diff --git a/CoreCms.Net.Uni-App/CoreShop/components/coreshop-page/coreshop-goods.vue b/CoreCms.Net.Uni-App/CoreShop/components/coreshop-page/coreshop-goods.vue index 58e69201..c8b99f34 100644 --- a/CoreCms.Net.Uni-App/CoreShop/components/coreshop-page/coreshop-goods.vue +++ b/CoreCms.Net.Uni-App/CoreShop/components/coreshop-page/coreshop-goods.vue @@ -24,7 +24,7 @@ {{item.mktprice}}元 - {{item.buyCount+'人付款'}} + 已售{{item.buyCount+item.initialSales}}{{item.unit}} @@ -63,7 +63,7 @@ {{item.mktprice}}元 - {{item.buyCount+'人付款'}} + 已售{{item.buyCount+item.initialSales}}{{item.unit}} @@ -102,7 +102,7 @@ - + @@ -111,7 +111,7 @@ {{ item.name }} {{ item.brief }} - 已售{{item.buyCount}}{{item.unit}} + 已售{{item.buyCount+item.initialSales}}{{item.unit}} {{item.viewCount}}人访问 @@ -119,7 +119,7 @@ ¥{{ item.price }} ¥{{ item.mktprice }} - 马上抢 + 马上抢 diff --git a/CoreCms.Net.Uni-App/CoreShop/pages/activity/groupBuying/groupBuyingDetails/groupBuyingDetails.vue b/CoreCms.Net.Uni-App/CoreShop/pages/activity/groupBuying/groupBuyingDetails/groupBuyingDetails.vue index 00765fa3..68927e35 100644 --- a/CoreCms.Net.Uni-App/CoreShop/pages/activity/groupBuying/groupBuyingDetails/groupBuyingDetails.vue +++ b/CoreCms.Net.Uni-App/CoreShop/pages/activity/groupBuying/groupBuyingDetails/groupBuyingDetails.vue @@ -16,7 +16,7 @@ - + ¥ @@ -59,7 +59,7 @@ - + 已售: {{ goodsInfo.buyCount || '0' }} @@ -74,7 +74,7 @@ - + @@ -156,7 +156,7 @@ - + 参数 @@ -173,7 +173,7 @@ - + 详情介绍 @@ -200,7 +200,7 @@ - + 常见问题 @@ -218,12 +218,12 @@ - + 为您推荐 - + @@ -239,7 +239,7 @@ {{item.mktprice}}元 - {{item.buyCount+'人付款'}} + 已售{{item.buyCount+item.initialSales}}{{item.unit}} @@ -276,7 +276,7 @@ {{item.mktprice}}元 - {{item.buyCount+'人付款'}} + 已售{{item.buyCount+item.initialSales}}{{item.unit}} diff --git a/CoreCms.Net.Uni-App/CoreShop/pages/activity/pinTuan/pinTuanDetails/pinTuanDetails.vue b/CoreCms.Net.Uni-App/CoreShop/pages/activity/pinTuan/pinTuanDetails/pinTuanDetails.vue index e407ed7c..8d401599 100644 --- a/CoreCms.Net.Uni-App/CoreShop/pages/activity/pinTuan/pinTuanDetails/pinTuanDetails.vue +++ b/CoreCms.Net.Uni-App/CoreShop/pages/activity/pinTuan/pinTuanDetails/pinTuanDetails.vue @@ -16,7 +16,7 @@ - + ¥ @@ -81,7 +81,7 @@ - + 已售: {{ goodsInfo.buyPinTuanCount || '0' }} @@ -97,7 +97,7 @@ - + @@ -128,7 +128,7 @@ - + 开团信息 @@ -201,7 +201,7 @@ - + 评价({{goodsComments.length}}) @@ -237,13 +237,13 @@ - + 该商品暂无评价 - + 参数 @@ -260,7 +260,7 @@ - + 详情介绍 @@ -314,7 +314,7 @@ - + 常见问题 @@ -332,12 +332,12 @@ - + 为您推荐 - + @@ -353,7 +353,7 @@ {{item.mktprice}}元 - {{item.buyCount+'人付款'}} + 已售{{item.buyCount+item.initialSales}}{{item.unit}} @@ -390,7 +390,7 @@ {{item.mktprice}}元 - {{item.buyCount+'人付款'}} + 已售{{item.buyCount+item.initialSales}}{{item.unit}} diff --git a/CoreCms.Net.Uni-App/CoreShop/pages/activity/seckill/seckillDetails/seckillDetails.vue b/CoreCms.Net.Uni-App/CoreShop/pages/activity/seckill/seckillDetails/seckillDetails.vue index bb38a5e3..a45ab705 100644 --- a/CoreCms.Net.Uni-App/CoreShop/pages/activity/seckill/seckillDetails/seckillDetails.vue +++ b/CoreCms.Net.Uni-App/CoreShop/pages/activity/seckill/seckillDetails/seckillDetails.vue @@ -16,7 +16,7 @@ - + ¥ @@ -59,7 +59,7 @@ - + 销量: {{ goodsInfo.buyCount || '0' }} @@ -74,7 +74,7 @@ - + @@ -119,7 +119,7 @@ - + 评价({{goodsComments.length}}) @@ -155,13 +155,13 @@ - + 该商品暂无评价 - + 参数 @@ -178,7 +178,7 @@ - + 详情介绍 @@ -205,7 +205,7 @@ - + 常见问题 @@ -222,12 +222,12 @@ - + 为您推荐 - + @@ -243,7 +243,7 @@ {{item.mktprice}}元 - {{item.buyCount+'人付款'}} + 已售{{item.buyCount+item.initialSales}}{{item.unit}} @@ -280,7 +280,7 @@ {{item.mktprice}}元 - {{item.buyCount+'人付款'}} + 已售{{item.buyCount+item.initialSales}}{{item.unit}} diff --git a/CoreCms.Net.Uni-App/CoreShop/pages/category/index/index.scss b/CoreCms.Net.Uni-App/CoreShop/pages/category/index/index.scss index 07ec54a1..b5fb01d2 100644 --- a/CoreCms.Net.Uni-App/CoreShop/pages/category/index/index.scss +++ b/CoreCms.Net.Uni-App/CoreShop/pages/category/index/index.scss @@ -67,14 +67,14 @@ .icon { width: 38rpx; height: 38rpx; margin-left: 10rpx; } } } - .items { display: flex; flex-direction: column; padding-bottom: -30rpx; + .items { display: flex; flex-direction: column; .good { display: flex; align-items: center; margin-bottom: 30rpx; .image { width: 160rpx; height: 160rpx; margin-right: 20rpx; border-radius: 8rpx; } - .right { flex: 1; height: 160rpx; overflow: hidden; display: flex; flex-direction: column; align-items: flex-start; justify-content: space-between; padding-right: 14rpx; - .name { font-size: 28rpx; margin-bottom: 10rpx; } + .right { flex: 1; height: 160rpx; overflow: hidden; display: flex; flex-direction: column; align-items: flex-start; justify-content: space-between; + .name { margin-bottom: 10rpx; } .tips { width: 100%; height: 40rpx; line-height: 40rpx; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 24rpx; color: #919293; margin-bottom: 10rpx; } - .price_and_action { width: 100%; display: flex; justify-content: space-between; align-items: center; - .price { font-size: 28rpx; font-weight: 600; } + .price_and_action { width: 100%; display: flex; justify-content: space-between; align-items: center; padding-top: 5px; + .price { font-size: 28rpx; font-weight: 600; color: #e02e24; } .btn-group { display: flex; justify-content: space-between; align-items: center; position: relative; .btn { padding: 0 20rpx; box-sizing: border-box; font-size: 24rpx; height: 44rpx; line-height: 44rpx; &.property_btn { border-radius: 24rpx; } diff --git a/CoreCms.Net.Uni-App/CoreShop/pages/category/index/index.vue b/CoreCms.Net.Uni-App/CoreShop/pages/category/index/index.vue index 36500cc2..ac8107fb 100644 --- a/CoreCms.Net.Uni-App/CoreShop/pages/category/index/index.vue +++ b/CoreCms.Net.Uni-App/CoreShop/pages/category/index/index.vue @@ -74,17 +74,19 @@ - + + + - {{ good.name }} - {{ good.brief }} + {{ good.name }} + + ¥{{ good.price }} - @@ -338,7 +340,9 @@ if (res.status == true) { _this.goodsSkuInfo = res.data.skuList; _this.openSkuPopup(); - uni.hideLoading(); + setTimeout(function () { + uni.hideLoading(); + }, 1000); } else { _this.$refs.uToast.show({ message: res.msg, type: 'error', complete: function () { @@ -355,7 +359,9 @@ if (res.status == true) { _this.goodsSkuInfo = res.data.skuList; _this.openSkuPopup(); - uni.hideLoading(); + setTimeout(function () { + uni.hideLoading(); + }, 1000); } else { _this.$refs.uToast.show({ message: res.msg, type: 'error', complete: function () { diff --git a/CoreCms.Net.Uni-App/CoreShop/pages/category/list/list.vue b/CoreCms.Net.Uni-App/CoreShop/pages/category/list/list.vue index 71da5ccc..02561f78 100644 --- a/CoreCms.Net.Uni-App/CoreShop/pages/category/list/list.vue +++ b/CoreCms.Net.Uni-App/CoreShop/pages/category/list/list.vue @@ -87,7 +87,7 @@ - + @@ -104,7 +104,7 @@ {{item.mktprice}}元 - {{item.buyCount+'人付款'}} + 已售{{item.buyCount+item.initialSales}}{{item.unit}} @@ -142,7 +142,7 @@ {{item.mktprice}}元 - {{item.buyCount+'人付款'}} + 已售{{item.buyCount+item.initialSales}}{{item.unit}} @@ -166,6 +166,7 @@ + @@ -334,9 +335,8 @@ // where: where //}); this.setSearchData(this.searchData, true); - - this.getGoods(); + // this.$refs.waterfallsFlowRef.refresh(); }, onReachBottom() { if (this.loadStatus != 'nomore') { @@ -452,7 +452,12 @@ _this.title = "商品搜索"; } } - _this.goodsList = _this.goodsList.concat(res.data.list); + //_this.goodsList = _this.goodsList.concat(res.data.list); + + + let _list = res.data.list; + _this.goodsList = [..._this.goodsList, ..._list] + if (res.data.brands) { for (let i = 0; i < res.data.brands.length; i++) { diff --git a/CoreCms.Net.Uni-App/CoreShop/pages/goods/goodDetails/goodDetails.vue b/CoreCms.Net.Uni-App/CoreShop/pages/goods/goodDetails/goodDetails.vue index 82d6d061..f909aa0e 100644 --- a/CoreCms.Net.Uni-App/CoreShop/pages/goods/goodDetails/goodDetails.vue +++ b/CoreCms.Net.Uni-App/CoreShop/pages/goods/goodDetails/goodDetails.vue @@ -13,7 +13,7 @@ - + ¥ @@ -46,7 +46,7 @@ - + 销量: {{ goodsInfo.buyCount || '0' }} @@ -62,7 +62,7 @@ - + 促销 @@ -80,7 +80,7 @@ - + @@ -125,7 +125,7 @@ - + 评价({{goodsComments.length}}) @@ -161,13 +161,13 @@ - + 该商品暂无评价 - + 参数 @@ -184,7 +184,7 @@ - + 详情介绍 @@ -211,7 +211,7 @@ - + 常见问题 @@ -228,12 +228,12 @@ - + 为您推荐 - + @@ -249,7 +249,7 @@ {{item.mktprice}}元 - {{item.buyCount+'人付款'}} + 已售{{item.buyCount+item.initialSales}}{{item.unit}} @@ -286,7 +286,7 @@ {{item.mktprice}}元 - {{item.buyCount+'人付款'}} + 已售{{item.buyCount+item.initialSales}}{{item.unit}} diff --git a/CoreCms.Net.Uni-App/CoreShop/static/images/common/empty-banner.png b/CoreCms.Net.Uni-App/CoreShop/static/images/common/empty-banner.png index 3375fa61d4556af628eadc33e9f1036bb49f0b5a..cbceca9e4726512d3bdedb5c44a7069eeaae102a 100644 GIT binary patch literal 4778 zcmcgw3pmtU*B_VYFwuEAI;9xXDJf>mAZ8epOXOBVBqthkYh*5FCd?4^s+?5vswlB*3tdYjv3G%%pGw2y8NB4~gkbhS|~y zR5G4SAo2X_$R-eo)K;3yVeVlEdn}PjM-WzI5P@_SKtmuVW`QgM@dTL*^CnYi3>@%^NEG-Q z=%5U-`bJniG;H+)2iDjm3Knl^y=n{0aPZ??E(?o91_T5k0`w3}HWi7&U@%BsG!l*0 z0TMbK9)n8=)M0S8e$-$|<`CI57MI3kz*aO8yqPDtI5=SXZx`sSPih&Q)i8m8Ap;34 zBnqLs;?hS!2Z#S%lurL7&Eew7zw`Yc6?0s8EHV;L<}gpPiD2U>TUSi6uoi4Gfy-pO zFqys|qj=yrlgs2BXR=^uGy(eKfQgA|aLwap_ z$yxUENk=6~>EIMUtfL1V`Ryd3pmoc_*o+ngvN`J?77&QdMhHYp3IZd48q%SV(=if| z)gcP~VFm^WUIt<={4}fr{{(&o`2T?ij91+F1ONYKA242V^nf z>%+*WqYsd}CwryC2_YH|y~2kh`X$rZbrHO3>U9qzu7 z@BWbLFTBxW=G(4_?o;!Y)RICXLoVJbVZVR6AW{kHNlk3df6jI3Z@1`LwrtgH48S0d zyxhqS`^LvpuE}dMgUAxzAnzBMsXtxXZ+^m>e9Q~E`<@g(``4NgLtmm@}SU2I51mo{@O0J`O2D zJG9Vo#DH=T)M+izgt1veuEbir%-oI6uxecJrGXdn{n}1zn&*l}9pl8;5ct9vGiXtb z@Eg2LIyCv}ej)b_JBFb-{eTqf{H`FL!)SPW-rwYb^!$N4{Opaf7{Z1$7q?~GMBza| zE32k(}q27!pJjt-9Gr@Nu@|+aY1l^4^*~$GQD+O8ZXwr7PvAc$QG30 zU1_P0XW0U+R^5?cePJ_=Z(!-&R~?E8u`RUsnlJb*KgX|N-{|XivnPCp*QG}$@H%6hG zMD@pB#CP&MOY6tNO3*QyesQTt!RGqH>HQ_ww!|zY=00-quGYu7i?1hb1EKmkyBija zoj0w1+E%k8`MYlaO)5^8jr$3V2&m1*)BO?J!I&Xmt@z@Iq$D?*jm*GXod^p^uypIK z`bZiTB*lK`Q+tFE{gx?P@zMK&wvo;0P;<^04^L|u71HBhNqLg}PM~$8f#*M3iwkF1E%~loJ2|Mq`4tv~lX>blTJu}+Y)ivbI*a@H73}1PVdfc2C zp?-P$FWRZkRd>=IT^Ec|S3*>?wvjd!dD5TLncG2vA*aK3>I8y-^NoX!RxMpwL?VG>WNjo&OioFX>n+CN;)is-y-RCfMGC36r&5w-a zLpv}b;I{d>r+vb<-3?#JW%h8Z&25Q#=c&h@AaUW4yWe(E@0AxSL+ikM-)OjGe8_L! z!;Vb0kvZnU(A^W%5-mzAqIarZ6^HZJlx|?Yy^ART7wXpW_WO&6N^>K&74)L8wpQYwH zJ`XI!vxZw51iUCTYj_I>*E(q2@g$`90a~ytv}ikX@$}R$VVQZUbMqZ>*);E_!#3d3 z+MJ=I?k<-l*LCLAd{Uya&6Ij)ws#C@@y%-(6~ksPotoMU*=}+( zBqgw2nUZsvfRyI`Vj3Q3_1wK* z5-)>w{iU}(wwP#=|4iv!0jr?1jr$}%BD+Q5t}LK25T7SQcHLiaqS)p^KWRvyto(`v7DG|7@Mq2h@ou)TfU&yAD;Rq2qmYdx;T6gXQQ+v zaEi_~4e4-0r~Fq<^IUYJ*qp%dJAw8zFG;{;-$01LMDxS_D>Ya{2xF&C z!^<_`)+h?+CH0nsKlok|E&)8bufq_QnObiw>dWI?R0Y^#65yh@JchfHMYWl4goB>Hp$LSZvX^~y!vgiM33OO4<}1zPyVhx6tJ zL_VmA<*b`ey%(Z?Y+6}wuj7 zPj-GQ+CFo9wSoLY=e45kGvZ&}xW5ix8MNSaS^{!T<=pj4f2)gM`S-}y%F!}!@3FuC E3o3a@9smFU literal 8014 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 GxBmlR^LMBK diff --git a/CoreCms.Net.Uni-App/CoreShop/static/images/common/empty.png b/CoreCms.Net.Uni-App/CoreShop/static/images/common/empty.png index 7a17b5eac4f80dd58c9ca2bfdc01e8d153cf4b20..75852343a986f57a1b03b3039b0f3f755c041882 100644 GIT binary patch literal 2297 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k2}mkgS)K$^k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+n3Xd_B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!WpKsVXIz*xcDQqR!L z#MH=KN5ROz$Uxr^h)i`2&8;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+UU-@r)U$VeBcLbtdwuOzWTH?LS3W`avKTB%1XJkii(hGOE?jkSNl+@ny;uz{4yi0i z)elN7&Mz%W21Z<(GRUd|E9aur#FG4?ko^1{SSSW$bak_=yk%W7nBxq3xGDeq!wkCrKY$Q<>xAZ{cM$q+btG2 z&4cPq!R;1HoO<DP`)vO zPKpo7ofIzEKY4D6^shB9ZDya1ER;!V4V`|t&F*#CoV&JlFJymJUVESRQ|?^LOC9Os z$zE$7KmM#>ZF}?j?DqBPk!G{ft}o8{x8#_WRNd?M7gwn^+gQKXyw|^+^HoZ^uk8CD z3*MrN+@Z*#F<-Ajs)kEEX+0Nc;$K{sS`$NQj z&qD7t@!r*4cV39;SLHKDhdjD3cP+Q(=AyVSz0$X)bVeCX?258x@=xvQ^x9)|L9DPU z?2Mh-<~MbdmPD`zC9hTMPPo-qaP{=|lQDmfe$o53`tRh{X0g`})@R$QnO1&?J^Cj3 zciWYTbCtJVP5pe3F?5gG`(Bf6n-6{R$kGok^AI~feWu>L{V|pzlS}j-EKv`ebNsz# ztoH7|8Z)bBzrUXmBptHt@vD6qoad`tZ*DoYzu)!s_tYO|uUiYk`IWBOz3%z9t<>$w zaov9<&pA#0{Igk6Yud6}=4N|n=d`!%F<-Z>{t=xVDtmpg691}=0jEEF72gvx%{WHo z_^D;@>#pyd5z+2H*%~1qqr6Knu%Z3 z8%@8~ec9=G{rySt_lH+a(@F}h7V=_QTmPwH$|j;g~I-?S!$z6-a% zS!&d-5O!qMF4e`k+~IRN9Sr%Cw{Nn45(uZ2N_C8W7 zSwF*jZQG5G#Ea=Z&yRZVUGdS{UPb%%v6FKm_TO6N(WD!*cT3$(|NsB<3{9f${+m4I za*|QfQOm!n5z$R92|Fa!Yb3Z1x1F~(I$zhaUEtlTc@e*D-YwcLu6_+o<-ZXfGwt3BFQtuThi5I*?7O!8 z;xXkT9bCP-i=&GLZT;HXGe7QlE0Xv1vl6pjd70&O^{~q>5D|ibdz*KiwBWRg&%ZTK_tM|v632a*uG>etVwOJxTo^HJoM7;u a(|}>qx(1)Co=i2M#)zk@pUXO@geCy_ouJkL literal 1001 zcmex=>ukC3pCfH06P05XITq?4J21E^7eo0A(TN+S4wfI*Oh zA%-D_nNf*>Nsy6Qkn#T!25F!pnHhnA0S;JL+1VJGIf0U90t`TFnOK-vSveunj0{Z7 zEUbb;ifqD$j!J=vB83wd9%NSzopec5+{h^?Wb%{`Kz$;}#z8cKLOX%V#C|%{4Sv$tgdmVRvHne}-)P53}2U%>Fe+ z>zv`X+s~ea1&6t+&5eqcIA^D!AHG9tUVo?NJAuzH-hRF8zS?iu5ySasR=p4B)10+Q z{N~%Og3bab`IGBDd_QsC?}XKsfVZYor*7GE>J|GXpUds0cV!B%lpo*g19eKk^3)fn z-lwfvl;iRvT7|#3?e2*R_6JQ_XLr1P`p4mcUx~}XckjQ(9@p=v-?P1bt@`W8{eu3c zzOUAKbt>2NPM-GMKy1sjUTfa-`6jZ*nT*}}H{Lk5Gp_3E+b`i)MFlRqyz1(dmdTNt z^If(fa_jbTo=`qpwW6ZF{au**r@Li2S9YjRO173MWw)-s_qC>>ql2TP zp`))j;?nmWrfVft<}e62@4l0~HG0{)$?|3QInIZFlb6xicH7nV(Q{F4&*J5i^~AvQ$R*u+Juk^!a>irc`v?G2DO2Zuu^r zdmDTE)>S;tle#I#@a5#8n_-p(tXse87914XpZq>H)cuysq`+Mo89b%@X{IxcuI-kd zlWcDytGnof-qdoAn@f&+YxK78p1b|$U|6@!1MMYKt~=*nh&y%e@#>fhes$T-kSGb+ zp6z??tK!mWYo|rF z`^~!LQSDbht1O@8JpIM7>GDP#uNM4!x1wHf{->>_E1H&_JuJ3cxo6ASn#mTKMjnm| V - /// 初始化 - /// - public class Program + +var builder = WebApplication.CreateBuilder(args); + +//添加本地路径获取支持 +builder.Services.AddSingleton(new AppSettingsHelper(builder.Environment.ContentRootPath)); +builder.Services.AddSingleton(new LogLockHelper(builder.Environment.ContentRootPath)); + +//Memory缓存 +builder.Services.AddMemoryCacheSetup(); +//Redis缓存 +builder.Services.AddRedisCacheSetup(); + +//添加数据库连接SqlSugar注入支持 +builder.Services.AddSqlSugarSetup(); +//配置跨域(CORS) +builder.Services.AddCorsSetup(); + +//添加session支持(session依赖于cache进行存储) +builder.Services.AddSession(); +// AutoMapper支持 +builder.Services.AddAutoMapper(typeof(AutoMapperConfiguration)); + +//使用 SignalR +builder.Services.AddSignalR(); + +// 引入Payment 依赖注入(支付宝支付/微信支付) +builder.Services.AddAlipay(); +builder.Services.AddWeChatPay(); + +// 在 appsettings.json 中 配置选项 +builder.Services.Configure(builder.Configuration.GetSection("WeChatPay")); +builder.Services.Configure(builder.Configuration.GetSection("Alipay")); + +//注册自定义微信接口配置文件 +builder.Services.Configure(builder.Configuration.GetSection(nameof(CoreCms.Net.WeChat.Service.Options.WeChatOptions))); + +// 注入工厂 HTTP 客户端 +builder.Services.AddHttpClient(); +builder.Services.AddSingleton(); + +//启用客户端IP限制速率 +builder.Services.AddIpPolicyRateLimitSetup(builder.Configuration); + +//Swagger接口文档注入 +builder.Services.AddAdminSwaggerSetup(); + +//jwt授权支持注入 +builder.Services.AddAuthorizationSetupForAdmin(); + +//上下文注入 +builder.Services.AddHttpContextSetup(); + +//服务配置中加入AutoFac控制器替换规则。 +builder.Services.Replace(ServiceDescriptor.Transient()); + +//注册mvc,注册razor引擎视图 +builder.Services.AddMvc(options => { - /// - /// 启动配置 - /// - /// - public static void Main(string[] args) + //实体验证 + options.Filters.Add(); + //异常处理 + options.Filters.Add(); + //Swagger剔除不需要加入api展示的列表 + options.Conventions.Add(new ApiExplorerIgnores()); + + options.EnableEndpointRouting = false; + }) + .AddNewtonsoftJson(p => + { + //数据格式首字母小写 不使用驼峰 + p.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); + //不使用驼峰样式的key + //p.SerializerSettings.ContractResolver = new DefaultContractResolver(); + //忽略循环引用 + p.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + //设置时间格式 + p.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; + }); + +// 雪花漂移算法 +// 创建 IdGeneratorOptions 对象,请在构造函数中输入 WorkerId: +var options = new IdGeneratorOptions(1); +// WorkerIdBitLength 默认值6,支持的 WorkerId 最大值为2^6-1,若 WorkerId 超过64,可设置更大的 WorkerIdBitLength +// options.WorkerIdBitLength = 10; +// ...... 其它参数设置参考 IdGeneratorOptions 定义,一般来说,只要再设置 WorkerIdBitLength (决定 WorkerId 的最大值)。 + +// 保存参数(必须的操作,否则以上设置都不能生效): +YitIdHelper.SetIdGenerator(options); + +// 初始化以后,即可在任何需要生成ID的地方,调用以下方法: +//var newId = YitIdHelper.NextId(); + + + + +#region AutoFac注册============================================================================ + +builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()); +builder.Host.ConfigureContainer(containerBuilder => +{ + containerBuilder.RegisterModule(new AutofacModuleRegister()); + + //获取所有控制器类型并使用属性注入 + var controllerBaseType = typeof(ControllerBase); + containerBuilder.RegisterAssemblyTypes(typeof(Program).Assembly) + .Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType) + .PropertiesAutowired(); +}); + +#endregion + +#region Nlog注册============================================================================ + +builder.Host.ConfigureLogging(logging => + { + logging.ClearProviders(); //移除已经注册的其他日志处理程序 + logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace); //设置最小的日志级别 + }) + .UseNLog(); //NLog: Setup NLog for Dependency injection + +#endregion + + +var app = builder.Build(); + + +#region 解决Ubuntu Nginx 代理不能获取IP问题=================================================================== +app.UseForwardedHeaders(new ForwardedHeadersOptions +{ + ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto +}); +#endregion + +#region 中间件注册=================================================================== + +// 开启Ip限流 +app.UseIpLimitMiddle(); +// 记录请求与返回数据 (注意开启权限,不然本地无法写入) +app.UseRequestResponseLog(); +// 用户访问记录(必须放到外层,不然如果遇到异常,会报错,因为不能返回流)(注意开启权限,不然本地无法写入) +app.UseRecordAccessLogsMildd(GlobalEnumVars.CoreShopSystemCategory.Admin.ToString()); +// 记录ip请求 (注意开启权限,不然本地无法写入) +app.UseIpLogMildd(); +// signalr +app.UseSignalRSendMildd(); + +#endregion + +app.UseSwagger().UseSwaggerUI(c => +{ + //根据版本名称倒序 遍历展示 + typeof(CustomApiVersion.ApiVersions).GetEnumNames().OrderByDescending(e => e).ToList().ForEach( + version => { - var host = CreateHostBuilder(args).Build(); - try - { - //确保NLog.config中连接字符串与appsettings.json中同步 - NLogUtil.EnsureNlogConfig("NLog.config"); - //throw new Exception("测试异常");//for test - //其他项目启动时需要做的事情 - NLogUtil.WriteAll(NLog.LogLevel.Trace, LogType.Web, "网站启动", "网站启动成功"); + c.SwaggerEndpoint($"/swagger/{version}/swagger.json", $"Doc {version}"); + }); + c.RoutePrefix = "doc"; +}); + + +//使用 Session +app.UseSession(); + +if (app.Environment.IsDevelopment()) +{ + // 在开发环境中,使用异常页面,这样可以暴露错误堆栈信息,所以不要放在生产环境。 + app.UseDeveloperExceptionPage(); +} +else +{ + app.UseExceptionHandler("/Home/Error"); + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); +} + +// CORS跨域 +app.UseCors(AppSettingsConstVars.CorsPolicyName); +// 跳转https +//app.UseHttpsRedirection(); + +// 使用cookie +app.UseCookiePolicy(); +// 返回错误码 +app.UseStatusCodePages(); +// Routing +app.UseRouting(); +// 先开启认证 +app.UseAuthentication(); +// 然后是授权中间件 +app.UseAuthorization(); + +app.UseEndpoints(endpoints => +{ + endpoints.MapControllerRoute( + "areas", + "{area:exists}/{controller=Default}/{action=Index}/{id?}" + ); + + endpoints.MapControllerRoute( + "default", + "{controller=Home}/{action=Index}/{id?}"); +}); + + +//设置默认起始页(如default.html) +//此处的路径是相对于wwwroot文件夹的相对路径 +var defaultFilesOptions = new DefaultFilesOptions(); +defaultFilesOptions.DefaultFileNames.Clear(); +defaultFilesOptions.DefaultFileNames.Add("index.html"); +app.UseDefaultFiles(defaultFilesOptions); +// 使用静态文件 +app.UseStaticFiles(); + +try +{ + //确保NLog.config中连接字符串与appsettings.json中同步 + NLogUtil.EnsureNlogConfig("nlog.config"); + //其他项目启动时需要做的事情 + NLogUtil.WriteAll(LogLevel.Trace, LogType.ApiRequest, "后端启动", "后端启动成功"); + + app.Run(); +} +catch (Exception ex) +{ + //使用Nlog写到本地日志文件(万一数据库没创建/连接成功) + NLogUtil.WriteFileLog(LogLevel.Error, LogType.ApiRequest, "后端启动", "初始化数据异常", ex); + throw; +} - host.Run(); - } - catch (Exception ex) - { - //使用nlog写到本地日志文件(万一数据库没创建/连接成功) - NLogUtil.WriteFileLog(NLog.LogLevel.Error, LogType.Web, "网站启动", "初始化数据异常", ex); - throw; - } - } - /// - /// 创建启动支撑 - /// - /// - /// - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .UseServiceProviderFactory(new AutofacServiceProviderFactory()) //<--NOTE THIS - .ConfigureLogging(logging => - { - logging.ClearProviders(); //移除已经注册的其他日志处理程序 - logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace); //设置最小的日志级别 - }) - .UseNLog() //NLog: Setup NLog for Dependency injection - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder - .ConfigureKestrel(serverOptions => - { - serverOptions.AllowSynchronousIO = true;//启用同步 IO - }) - .UseStartup(); - }); - } -} \ No newline at end of file diff --git a/CoreCms.Net.Web.Admin/Startup.cs b/CoreCms.Net.Web.Admin/Startup.cs deleted file mode 100644 index eac89ce6..00000000 --- a/CoreCms.Net.Web.Admin/Startup.cs +++ /dev/null @@ -1,259 +0,0 @@ - -using System.Linq; -using Autofac; -using CoreCms.Net.Auth; -using CoreCms.Net.Configuration; -using CoreCms.Net.Core.AutoFac; -using CoreCms.Net.Core.Config; -using CoreCms.Net.Filter; -using CoreCms.Net.Loging; -using CoreCms.Net.Mapping; -using CoreCms.Net.Middlewares; -using CoreCms.Net.Model.ViewModels.Options; -using CoreCms.Net.Swagger; -using Essensoft.Paylink.Alipay; -using Essensoft.Paylink.WeChatPay; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.HttpOverrides; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Controllers; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Options; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using Yitter.IdGenerator; - -namespace CoreCms.Net.Web.Admin -{ - /// - /// 启动配置 - /// - public class Startup - { - /// - /// 构造函数 - /// - /// - /// - public Startup(IConfiguration configuration, IWebHostEnvironment env) - { - Configuration = configuration; - Env = env; - } - - /// - /// - public IConfiguration Configuration { get; } - - /// - /// - public IWebHostEnvironment Env { get; } - - /// - /// This method gets called by the runtime. Use this method to add services to the container. - /// - /// - public void ConfigureServices(IServiceCollection services) - { - //添加本地路径获取支持 - services.AddSingleton(new AppSettingsHelper(Env.ContentRootPath)); - services.AddSingleton(new LogLockHelper(Env.ContentRootPath)); - - //Memory缓存 - services.AddMemoryCacheSetup(); - //Redis缓存 - services.AddRedisCacheSetup(); - - - //添加数据库连接SqlSugar注入支持 - services.AddSqlSugarSetup(); - //配置跨域(CORS) - services.AddCorsSetup(); - - //添加session支持(session依赖于cache进行存储) - services.AddSession(); - // AutoMapper支持 - services.AddAutoMapper(typeof(AutoMapperConfiguration)); - - //使用 SignalR - services.AddSignalR(); - - // 引入Payment 依赖注入(支付宝支付/微信支付) - services.AddAlipay(); - services.AddWeChatPay(); - - // 在 appsettings.json 中 配置选项 - //注册自定义的支付配置文件,后面将取消使用Paylink,直接走接口请求自定义处理。 - services.Configure(Configuration.GetSection("WeChatPay")); - services.Configure(Configuration.GetSection("Alipay")); - - //注册自定义微信接口配置文件 - services.Configure(Configuration.GetSection(nameof(WeChat.Service.Options.WeChatOptions))); - - // 注入工厂 HTTP 客户端 - services.AddHttpClient(); - services.AddSingleton(); - - //启用客户端IP限制速率 - services.AddIpPolicyRateLimitSetup(Configuration); - - //Swagger接口文档注入 - services.AddAdminSwaggerSetup(); - - //jwt授权支持注入 - services.AddAuthorizationSetupForAdmin(); - //上下文注入 - services.AddHttpContextSetup(); - - //服务配置中加入AutoFac控制器替换规则。 - services.Replace(ServiceDescriptor.Transient()); - - //注册mvc,注册razor引擎视图 - services.AddMvc(options => - { - //实体验证 - options.Filters.Add(); - //异常处理 - options.Filters.Add(); - //Swagger剔除不需要加入api展示的列表 - options.Conventions.Add(new ApiExplorerIgnores()); - - options.EnableEndpointRouting = false; - }) - .AddNewtonsoftJson(p => - { - //数据格式首字母小写 不使用驼峰 - p.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); - //不使用驼峰样式的key - //p.SerializerSettings.ContractResolver = new DefaultContractResolver(); - //忽略循环引用 - p.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; - //设置时间格式 - p.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; - }); - - - // 雪花漂移算法 - // 创建 IdGeneratorOptions 对象,请在构造函数中输入 WorkerId: - var options = new IdGeneratorOptions(1); - // WorkerIdBitLength 默认值6,支持的 WorkerId 最大值为2^6-1,若 WorkerId 超过64,可设置更大的 WorkerIdBitLength - // options.WorkerIdBitLength = 10; - // ...... 其它参数设置参考 IdGeneratorOptions 定义,一般来说,只要再设置 WorkerIdBitLength (决定 WorkerId 的最大值)。 - - // 保存参数(必须的操作,否则以上设置都不能生效): - YitIdHelper.SetIdGenerator(options); - - // 初始化以后,即可在任何需要生成ID的地方,调用以下方法: - //var newId = YitIdHelper.NextId(); - - } - - /// - /// Autofac规则配置 - /// - /// - public void ConfigureContainer(ContainerBuilder builder) - { - builder.RegisterModule(new AutofacModuleRegister()); - - //获取所有控制器类型并使用属性注入 - var controllerBaseType = typeof(ControllerBase); - builder.RegisterAssemblyTypes(typeof(Program).Assembly) - .Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType) - .PropertiesAutowired(); - } - - /// - /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - /// - /// - /// - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - #region 解决Ubuntu Nginx 代理不能获取IP问题 - app.UseForwardedHeaders(new ForwardedHeadersOptions - { - ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto - }); - #endregion - - // 开启Ip限流 - app.UseIpLimitMiddle(); - // 记录请求与返回数据 (注意开启权限,不然本地无法写入) - app.UseRequestResponseLog(); - // 用户访问记录(必须放到外层,不然如果遇到异常,会报错,因为不能返回流)(注意开启权限,不然本地无法写入) - app.UseRecordAccessLogsMildd(GlobalEnumVars.CoreShopSystemCategory.Admin.ToString()); - // 记录ip请求 (注意开启权限,不然本地无法写入) - app.UseIpLogMildd(); - // signalr - app.UseSignalRSendMildd(); - - - app.UseSwagger().UseSwaggerUI(c => - { - //根据版本名称倒序 遍历展示 - typeof(CustomApiVersion.ApiVersions).GetEnumNames().OrderByDescending(e => e).ToList().ForEach( - version => - { - c.SwaggerEndpoint($"/swagger/{version}/swagger.json", $"Doc {version}"); - }); - c.RoutePrefix = "doc"; - }); - - //使用 Session - app.UseSession(); - - if (env.IsDevelopment()) - { - // 在开发环境中,使用异常页面,这样可以暴露错误堆栈信息,所以不要放在生产环境。 - app.UseDeveloperExceptionPage(); - } - else - { - app.UseExceptionHandler("/Home/Error"); - // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. - app.UseHsts(); - } - - // CORS跨域 - app.UseCors(AppSettingsConstVars.CorsPolicyName); - // 跳转https - //app.UseHttpsRedirection(); - // 使用静态文件 - app.UseStaticFiles(); - // 使用cookie - app.UseCookiePolicy(); - // 返回错误码 - app.UseStatusCodePages(); - // Routing - app.UseRouting(); - // 先开启认证 - app.UseAuthentication(); - // 然后是授权中间件 - app.UseAuthorization(); - app.UseEndpoints(endpoints => - { - endpoints.MapControllerRoute( - "areas", - "{area:exists}/{controller=Default}/{action=Index}/{id?}" - ); - - endpoints.MapControllerRoute( - "default", - "{controller=Home}/{action=Index}/{id?}"); - }); - - //设置默认起始页(如default.html) - //此处的路径是相对于wwwroot文件夹的相对路径 - var defaultFilesOptions = new DefaultFilesOptions(); - defaultFilesOptions.DefaultFileNames.Clear(); - defaultFilesOptions.DefaultFileNames.Add("index.html"); - app.UseDefaultFiles(defaultFilesOptions); - app.UseStaticFiles(); - } - } -} \ No newline at end of file diff --git a/CoreCms.Net.Web.WebApi/Controllers/GoodController.cs b/CoreCms.Net.Web.WebApi/Controllers/GoodController.cs index 205b091e..65de64d1 100644 --- a/CoreCms.Net.Web.WebApi/Controllers/GoodController.cs +++ b/CoreCms.Net.Web.WebApi/Controllers/GoodController.cs @@ -18,6 +18,7 @@ using System.Threading.Tasks; using AutoMapper; using CoreCms.Net.Auth.HttpContextUser; using CoreCms.Net.Configuration; +using CoreCms.Net.DTO.ComponentsDTO; using CoreCms.Net.IServices; using CoreCms.Net.Model.Entities; using CoreCms.Net.Model.Entities.Expression; @@ -150,9 +151,7 @@ namespace CoreCms.Net.Web.WebApi.Controllers { var jm = new WebApiCallBack(); - var where = PredicateBuilder.True(); - where = where.And(p => p.isDel == false); - where = where.And(p => p.isMarketable == true); + var where = PredicateBuilder.True(); var className = string.Empty; if (!string.IsNullOrWhiteSpace(entity.where)) @@ -224,14 +223,8 @@ namespace CoreCms.Net.Web.WebApi.Controllers orderBy += "," + entity.order; } //获取数据 - var list = await _goodsServices.QueryPageAsync(where, orderBy, entity.page, entity.limit, false); - if (list.Any()) - { - foreach (var goods in list) - { - goods.images = !string.IsNullOrEmpty(goods.images) ? goods.images.Split(",")[0] : "/static/images/common/empty.png"; - } - } + var list = await _goodsServices.QueryPageByDTOAsync(where, orderBy, entity.page, entity.limit, true); + //获取品牌 var brands = await _brandServices.QueryListByClauseAsync(p => p.isShow == true, p => p.sort, OrderByType.Desc, true, true); diff --git a/CoreCms.Net.Web.WebApi/Program.cs b/CoreCms.Net.Web.WebApi/Program.cs index 8cf4d7e9..bea4432d 100644 --- a/CoreCms.Net.Web.WebApi/Program.cs +++ b/CoreCms.Net.Web.WebApi/Program.cs @@ -1,69 +1,316 @@ using System; +using System.Linq; +using Autofac; using Autofac.Extensions.DependencyInjection; +using CoreCms.Net.Auth; +using CoreCms.Net.Configuration; +using CoreCms.Net.Core.AutoFac; +using CoreCms.Net.Core.Config; +using CoreCms.Net.Filter; using CoreCms.Net.Loging; +using CoreCms.Net.Mapping; +using CoreCms.Net.Middlewares; +using CoreCms.Net.Swagger; +using CoreCms.Net.Task; +using CoreCms.Net.WeChat.Service.Mediator; +using Essensoft.Paylink.Alipay; +using Essensoft.Paylink.WeChatPay; +using Hangfire; +using Hangfire.Dashboard.BasicAuthorization; +using MediatR; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.HttpOverrides; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Serialization; +using Newtonsoft.Json; using NLog.Web; +using Yitter.IdGenerator; using LogLevel = NLog.LogLevel; -namespace CoreCms.Net.Web.WebApi + +var builder = WebApplication.CreateBuilder(args); + +//添加本地路径获取支持 +builder.Services.AddSingleton(new AppSettingsHelper(builder.Environment.ContentRootPath)); +builder.Services.AddSingleton(new LogLockHelper(builder.Environment.ContentRootPath)); + +//Memory缓存 +builder.Services.AddMemoryCacheSetup(); +//Redis缓存 +builder.Services.AddRedisCacheSetup(); + +//添加数据库连接SqlSugar注入支持 +builder.Services.AddSqlSugarSetup(); +//配置跨域(CORS) +builder.Services.AddCorsSetup(); + +//添加session支持(session依赖于cache进行存储) +builder.Services.AddSession(); +// AutoMapper支持 +builder.Services.AddAutoMapper(typeof(AutoMapperConfiguration)); + +//MediatR(只需要注册一个,同项目或类库下就不需要注册多个) +builder.Services.AddMediatR(typeof(TextMessageEventCommand).Assembly); + +//使用 SignalR +builder.Services.AddSignalR(); + +//Redis消息队列 +builder.Services.AddRedisMessageQueueSetup(); + +// 引入Payment 依赖注入(支付宝支付/微信支付) +builder.Services.AddAlipay(); +builder.Services.AddWeChatPay(); + +// 在 appsettings.json 中 配置选项 +builder.Services.Configure(builder.Configuration.GetSection("WeChatPay")); +builder.Services.Configure(builder.Configuration.GetSection("Alipay")); + +//注册自定义微信接口配置文件 +builder.Services.Configure(builder.Configuration.GetSection(nameof(CoreCms.Net.WeChat.Service.Options.WeChatOptions))); + +// 注入工厂 HTTP 客户端 +builder.Services.AddHttpClient(); +builder.Services.AddSingleton(); + +//启用客户端IP限制速率 +builder.Services.AddIpPolicyRateLimitSetup(builder.Configuration); + +//Swagger接口文档注入 +builder.Services.AddAdminSwaggerSetup(); + +//注册Hangfire定时任务 +builder.Services.AddHangFireSetup(); + +//jwt授权支持注入 +builder.Services.AddAuthorizationSetupForAdmin(); + +//上下文注入 +builder.Services.AddHttpContextSetup(); + +//服务配置中加入AutoFac控制器替换规则。 +builder.Services.Replace(ServiceDescriptor.Transient()); + +//注册mvc,注册razor引擎视图 +builder.Services.AddMvc(options => { - /// - /// 启动类 - /// - public class Program + //实体验证 + options.Filters.Add(); + //异常处理 + options.Filters.Add(); + //Swagger剔除不需要加入api展示的列表 + options.Conventions.Add(new ApiExplorerIgnores()); + + options.EnableEndpointRouting = false; +}) + .AddNewtonsoftJson(p => { - /// - /// 启动配置 - /// - /// - public static void Main(string[] args) - { - var host = CreateHostBuilder(args).Build(); - try - { - //确保NLog.config中连接字符串与appsettings.json中同步 - NLogUtil.EnsureNlogConfig("NLog.config"); - //其他项目启动时需要做的事情 - NLogUtil.WriteAll(LogLevel.Trace, LogType.ApiRequest, "接口启动", "接口启动成功"); + //数据格式首字母小写 不使用驼峰 + p.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); + //不使用驼峰样式的key + //p.SerializerSettings.ContractResolver = new DefaultContractResolver(); + //忽略循环引用 + p.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + //设置时间格式 + p.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; + }); - host.Run(); - } - catch (Exception ex) +// 雪花漂移算法 +// 创建 IdGeneratorOptions 对象,请在构造函数中输入 WorkerId: +var options = new IdGeneratorOptions(1); +// WorkerIdBitLength 默认值6,支持的 WorkerId 最大值为2^6-1,若 WorkerId 超过64,可设置更大的 WorkerIdBitLength +// options.WorkerIdBitLength = 10; +// ...... 其它参数设置参考 IdGeneratorOptions 定义,一般来说,只要再设置 WorkerIdBitLength (决定 WorkerId 的最大值)。 + +// 保存参数(必须的操作,否则以上设置都不能生效): +YitIdHelper.SetIdGenerator(options); + +// 初始化以后,即可在任何需要生成ID的地方,调用以下方法: +//var newId = YitIdHelper.NextId(); + + + +#region AutoFac注册============================================================================ + +builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()); +builder.Host.ConfigureContainer(containerBuilder => +{ + //获取所有控制器类型并使用属性注入 + var controllerBaseType = typeof(ControllerBase); + containerBuilder.RegisterAssemblyTypes(typeof(Program).Assembly) + .Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType) + .PropertiesAutowired(); + + containerBuilder.RegisterModule(new AutofacModuleRegister()); + +}); + +#endregion + +#region Nlog注册============================================================================ + +builder.Host.ConfigureLogging(logging => +{ + logging.ClearProviders(); //移除已经注册的其他日志处理程序 + logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace); //设置最小的日志级别 +}) + .UseNLog(); //NLog: Setup NLog for Dependency injection + +#endregion + + +var app = builder.Build(); + + +#region 解决Ubuntu Nginx 代理不能获取IP问题=================================================================== +app.UseForwardedHeaders(new ForwardedHeadersOptions +{ + ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto +}); +#endregion + +#region 中间件注册=================================================================== + +// 开启Ip限流 +app.UseIpLimitMiddle(); +// 记录请求与返回数据 (注意开启权限,不然本地无法写入) +app.UseRequestResponseLog(); +// 用户访问记录(必须放到外层,不然如果遇到异常,会报错,因为不能返回流)(注意开启权限,不然本地无法写入) +app.UseRecordAccessLogsMildd(GlobalEnumVars.CoreShopSystemCategory.Api.ToString()); +// 记录ip请求 (注意开启权限,不然本地无法写入) +app.UseIpLogMildd(); +// signalr +app.UseSignalRSendMildd(); + +#endregion + +//强制显示中文 +System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("zh-CN"); + +app.UseSwagger().UseSwaggerUI(c => +{ + //根据版本名称倒序 遍历展示 + typeof(CustomApiVersion.ApiVersions).GetEnumNames().OrderByDescending(e => e).ToList().ForEach( + version => + { + c.SwaggerEndpoint($"/swagger/{version}/swagger.json", $"Doc {version}"); + }); + c.RoutePrefix = "doc"; +}); + +#region Hangfire定时任务 + +//授权 +var filter = new BasicAuthAuthorizationFilter( + new BasicAuthAuthorizationFilterOptions + { + SslRedirect = false, + // Require secure connection for dashboard + RequireSsl = false, + // Case sensitive login checking + LoginCaseSensitive = false, + // Users + Users = new[] + { + new BasicAuthAuthorizationUser { - //使用nlog写到本地日志文件(万一数据库没创建/连接成功) - NLogUtil.WriteFileLog(LogLevel.Error, LogType.ApiRequest, "接口启动", "初始化数据异常", ex); - throw; + Login = AppSettingsConstVars.HangFireLogin, + PasswordClear = AppSettingsConstVars.HangFirePassWord } } + }); +var hangfireOptions = new Hangfire.DashboardOptions +{ + AppPath = "/",//返回时跳转的地址 + DisplayStorageConnectionString = false,//是否显示数据库连接信息 + Authorization = new[] + { + filter + }, + IsReadOnlyFunc = _ => false +}; + +app.UseHangfireDashboard("/job", hangfireOptions); //可以改变Dashboard的url +HangfireDispose.HangfireService(); + +#endregion + + +//使用 Session +app.UseSession(); + +if (app.Environment.IsDevelopment()) +{ + // 在开发环境中,使用异常页面,这样可以暴露错误堆栈信息,所以不要放在生产环境。 + app.UseDeveloperExceptionPage(); +} +else +{ + app.UseExceptionHandler("/Home/Error"); + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); +} + +// CORS跨域 +app.UseCors(AppSettingsConstVars.CorsPolicyName); +// 跳转https +//app.UseHttpsRedirection(); + +// 使用cookie +app.UseCookiePolicy(); +// 返回错误码 +app.UseStatusCodePages(); +// Routing +app.UseRouting(); +// 先开启认证 +app.UseAuthentication(); +// 然后是授权中间件 +app.UseAuthorization(); + +app.UseEndpoints(endpoints => +{ + endpoints.MapControllerRoute( + "areas", + "{area:exists}/{controller=Default}/{action=Index}/{id?}" + ); + + endpoints.MapControllerRoute( + "default", + "{controller=Home}/{action=Index}/{id?}"); +}); + + +//设置默认起始页(如default.html) +//此处的路径是相对于wwwroot文件夹的相对路径 +var defaultFilesOptions = new DefaultFilesOptions(); +defaultFilesOptions.DefaultFileNames.Clear(); +defaultFilesOptions.DefaultFileNames.Add("index.html"); +app.UseDefaultFiles(defaultFilesOptions); +// 使用静态文件 +app.UseStaticFiles(); + +try +{ + //确保NLog.config中连接字符串与appsettings.json中同步 + NLogUtil.EnsureNlogConfig("nlog.config"); + //其他项目启动时需要做的事情 + NLogUtil.WriteAll(LogLevel.Trace, LogType.ApiRequest, "接口启动", "接口启动成功"); + + app.Run(); +} +catch (Exception ex) +{ + //使用Nlog写到本地日志文件(万一数据库没创建/连接成功) + NLogUtil.WriteFileLog(LogLevel.Error, LogType.ApiRequest, "接口启动", "初始化数据异常", ex); + throw; +} + + - /// - /// 创建启动支撑 - /// - /// - /// - public static IHostBuilder CreateHostBuilder(string[] args) - { - return Host.CreateDefaultBuilder(args) - .UseServiceProviderFactory(new AutofacServiceProviderFactory()) //<--NOTE THIS - .ConfigureLogging(logging => - { - logging.ClearProviders(); //移除已经注册的其他日志处理程序 - logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace); //设置最小的日志级别 - }) - .UseNLog() //NLog: Setup NLog for Dependency injection - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder - .ConfigureKestrel(serverOptions => - { - serverOptions.AllowSynchronousIO = true; //启用同步 IO - }) - .UseStartup(); - }); - } - } -} \ No newline at end of file diff --git a/CoreCms.Net.Web.WebApi/Startup.cs b/CoreCms.Net.Web.WebApi/Startup.cs deleted file mode 100644 index 4bde1c62..00000000 --- a/CoreCms.Net.Web.WebApi/Startup.cs +++ /dev/null @@ -1,314 +0,0 @@ -using Autofac; -using CoreCms.Net.Auth; -using CoreCms.Net.Configuration; -using CoreCms.Net.Core.AutoFac; -using CoreCms.Net.Core.Config; -using CoreCms.Net.Filter; -using CoreCms.Net.Loging; -using CoreCms.Net.Mapping; -using CoreCms.Net.Middlewares; -using CoreCms.Net.Swagger; -using CoreCms.Net.Task; -using Hangfire; -using Hangfire.Dashboard.BasicAuthorization; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Controllers; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Options; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using System; -using System.Linq; -using CoreCms.Net.WeChat.Service.Mediator; -using Essensoft.Paylink.Alipay; -using Essensoft.Paylink.WeChatPay; -using MediatR; -using Microsoft.AspNetCore.HttpOverrides; -using Yitter.IdGenerator; - -namespace CoreCms.Net.Web.WebApi -{ - /// - /// 启动配置 - /// - public class Startup - { - /// - /// 构造函数 - /// - /// - /// - public Startup(IConfiguration configuration, IWebHostEnvironment env) - { - Configuration = configuration; - Env = env; - } - /// - /// 配置属性 - /// - public IConfiguration Configuration { get; } - /// - /// web环境 - /// - public IWebHostEnvironment Env { get; } - - /// This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - //添加本地路径获取支持 - services.AddSingleton(new AppSettingsHelper(Env.ContentRootPath)); - services.AddSingleton(new LogLockHelper(Env.ContentRootPath)); - - //Memory缓存 - services.AddMemoryCacheSetup(); - //Redis缓存 - services.AddRedisCacheSetup(); - - //添加数据库连接SqlSugar注入支持 - services.AddSqlSugarSetup(); - //配置跨域(CORS) - services.AddCorsSetup(); - - //添加session支持(session依赖于cache进行存储) - services.AddSession(); - // AutoMapper支持 - services.AddAutoMapper(typeof(AutoMapperConfiguration)); - - - //MediatR(只需要注册一个,同项目或类库下就不需要注册多个) - services.AddMediatR(typeof(TextMessageEventCommand).Assembly); - - //使用 SignalR - services.AddSignalR(); - - //Redis消息队列 - services.AddRedisMessageQueueSetup(); - - // 引入Payment 依赖注入(支付宝支付/微信支付) - services.AddAlipay(); - services.AddWeChatPay(); - - // 在 appsettings.json 中 配置选项 - services.Configure(Configuration.GetSection("WeChatPay")); - services.Configure(Configuration.GetSection("Alipay")); - - - - //注册自定义微信接口配置文件 - services.Configure(Configuration.GetSection(nameof(WeChat.Service.Options.WeChatOptions))); - - // 注入工厂 HTTP 客户端 - services.AddHttpClient(); - services.AddSingleton(); - - //启用客户端IP限制速率 - services.AddIpPolicyRateLimitSetup(Configuration); - - //Swagger接口文档注入 - services.AddClientSwaggerSetup(); - - //注册Hangfire定时任务 - services.AddHangFireSetup(); - - //授权支持注入 - services.AddAuthorizationSetupForClient(); - //上下文注入 - services.AddHttpContextSetup(); - - //服务配置中加入AutoFac控制器替换规则。 - services.Replace(ServiceDescriptor.Transient()); - - //注册mvc,注册razor引擎视图 - services.AddMvc(options => - { - //实体验证 - options.Filters.Add(); - //异常处理 - options.Filters.Add(); - //Swagger剔除不需要加入api展示的列表 - options.Conventions.Add(new ApiExplorerIgnores()); - }) - .AddNewtonsoftJson(p => - { - //数据格式首字母小写 不使用大驼峰 - p.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); - //不使用驼峰样式的key - //p.SerializerSettings.ContractResolver = new DefaultContractResolver(); - //忽略循环引用 - p.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; - //设置时间格式(必须使用yyyy/MM/dd格式,因为ios系统不支持2018-03-29格式的时间,只识别2018/03/09这种格式。) - p.SerializerSettings.DateFormatString = "yyyy/MM/dd HH:mm:ss"; - }); - - - // 雪花漂移算法 - // 创建 IdGeneratorOptions 对象,请在构造函数中输入 WorkerId: - var options = new IdGeneratorOptions(1); - // WorkerIdBitLength 默认值6,支持的 WorkerId 最大值为2^6-1,若 WorkerId 超过64,可设置更大的 WorkerIdBitLength - // options.WorkerIdBitLength = 10; - // ...... 其它参数设置参考 IdGeneratorOptions 定义,一般来说,只要再设置 WorkerIdBitLength (决定 WorkerId 的最大值)。 - - // 保存参数(必须的操作,否则以上设置都不能生效): - YitIdHelper.SetIdGenerator(options); - - // 初始化以后,即可在任何需要生成ID的地方,调用以下方法: - //var newId = YitIdHelper.NextId(); - - } - - /// - /// Autofac规则配置 - /// - /// - public void ConfigureContainer(ContainerBuilder builder) - { - //获取所有控制器类型并使用属性注入 - var controllerBaseType = typeof(ControllerBase); - builder.RegisterAssemblyTypes(typeof(Program).Assembly) - .Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType) - .PropertiesAutowired(); - - builder.RegisterModule(new AutofacModuleRegister()); - - } - - // public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IOptions senparcSetting, IOptions senparcWeixinSetting) - /// - /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - /// - /// - /// - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - #region 解决Ubuntu Nginx 代理不能获取IP问题 - app.UseForwardedHeaders(new ForwardedHeadersOptions - { - ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto - }); - #endregion - - // 开启Ip限流 - app.UseIpLimitMiddle(); - // 记录请求与返回数据 (注意开启权限,不然本地无法写入) - app.UseRequestResponseLog(); - // 用户访问记录(必须放到外层,不然如果遇到异常,会报错,因为不能返回流)(注意开启权限,不然本地无法写入) - app.UseRecordAccessLogsMildd(GlobalEnumVars.CoreShopSystemCategory.Api.ToString()); - // 记录ip请求 (注意开启权限,不然本地无法写入) - app.UseIpLogMildd(); - - - //强制显示中文 - System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("zh-CN"); - - app.UseSwagger().UseSwaggerUI(c => - { - //根据版本名称倒序 遍历展示 - typeof(CustomApiVersion.ApiVersions).GetEnumNames().OrderByDescending(e => e).ToList().ForEach( - version => - { - c.SwaggerEndpoint($"/swagger/{version}/swagger.json", $"Doc {version}"); - }); - //设置默认跳转到swagger-ui - c.RoutePrefix = "doc"; - //c.RoutePrefix = string.Empty; - }); - - - #region Hangfire定时任务 - - //授权 - var filter = new BasicAuthAuthorizationFilter( - new BasicAuthAuthorizationFilterOptions - { - SslRedirect = false, - // Require secure connection for dashboard - RequireSsl = false, - // Case sensitive login checking - LoginCaseSensitive = false, - // Users - Users = new[] - { - new BasicAuthAuthorizationUser - { - Login = AppSettingsConstVars.HangFireLogin, - PasswordClear = AppSettingsConstVars.HangFirePassWord - } - } - }); - var options = new DashboardOptions - { - AppPath = "/",//返回时跳转的地址 - DisplayStorageConnectionString = false,//是否显示数据库连接信息 - Authorization = new[] - { - filter - }, - IsReadOnlyFunc = _ => false - }; - - app.UseHangfireDashboard("/job", options); //可以改变Dashboard的url - HangfireDispose.HangfireService(); - - #endregion - - //使用 Session - app.UseSession(); - - if (env.IsDevelopment()) - { - // 在开发环境中,使用异常页面,这样可以暴露错误堆栈信息,所以不要放在生产环境。 - app.UseDeveloperExceptionPage(); - } - else - { - app.UseExceptionHandler("/Home/Error"); - // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. - app.UseHsts(); - } - - // CORS跨域 - app.UseCors(AppSettingsConstVars.CorsPolicyName); - - // Routing - app.UseRouting(); - - // 使用静态文件 - app.UseStaticFiles(); - // 先开启认证 - app.UseAuthentication(); - // 然后是授权中间件 - app.UseAuthorization(); - - app.UseEndpoints(endpoints => - { - endpoints.MapControllerRoute( - "areas", - "{area:exists}/{controller=Default}/{action=Index}/{id?}" - ); - - //endpoints.MapControllers(); - endpoints.MapControllerRoute( - "default", - "{controller=Default}/{action=Index}/{id?}"); - }); - - - - //设置默认起始页(如default.html) - //此处的路径是相对于wwwroot文件夹的相对路径 - var defaultFilesOptions = new DefaultFilesOptions(); - defaultFilesOptions.DefaultFileNames.Clear(); - defaultFilesOptions.DefaultFileNames.Add("index.html"); - app.UseDefaultFiles(defaultFilesOptions); - app.UseStaticFiles(); - - } - - - } -} \ No newline at end of file