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 3375fa61..cbceca9e 100644 Binary files a/CoreCms.Net.Uni-App/CoreShop/static/images/common/empty-banner.png and b/CoreCms.Net.Uni-App/CoreShop/static/images/common/empty-banner.png differ 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 7a17b5ea..75852343 100644 Binary files a/CoreCms.Net.Uni-App/CoreShop/static/images/common/empty.png and b/CoreCms.Net.Uni-App/CoreShop/static/images/common/empty.png differ diff --git a/CoreCms.Net.Web.Admin/Program.cs b/CoreCms.Net.Web.Admin/Program.cs index a14996b8..040ea6da 100644 --- a/CoreCms.Net.Web.Admin/Program.cs +++ b/CoreCms.Net.Web.Admin/Program.cs @@ -1,67 +1,264 @@ -using Autofac.Extensions.DependencyInjection; +using Autofac; +using Autofac.Extensions.DependencyInjection; +using CoreCms.Net.Configuration; using CoreCms.Net.Loging; +using Essensoft.Paylink.Alipay; +using Essensoft.Paylink.WeChatPay; +using Microsoft.AspNetCore.Authentication.Cookies; +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.Hosting; using Microsoft.Extensions.Logging; using NLog.Web; +using StackExchange.Redis; using System; +using System.Linq; +using System.Reflection; +using CoreCms.Net.Auth; +using CoreCms.Net.Core.AutoFac; +using CoreCms.Net.Core.Config; +using CoreCms.Net.Filter; +using CoreCms.Net.Mapping; +using CoreCms.Net.Middlewares; +using CoreCms.Net.Swagger; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Yitter.IdGenerator; +using LogLevel = NLog.LogLevel; -namespace CoreCms.Net.Web.Admin -{ - /// - /// 初始化 - /// - 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