using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Linq.Dynamic.Core; using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; using AutoMapper; using Dy_Exchange.Enums; using Dy_Exchange.Localization; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Localization; using NPOI.SS.UserModel; using NPOI.XSSF.UserModel; using Volo.Abp; using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Services; using Volo.Abp.Caching; using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Entities.Auditing; using Volo.Abp.Domain.Repositories; using WinIn.FasterZ.Inventory.AppBase; using WinIn.FasterZ.Inventory.AppBase.Filters; using WinIn.FasterZ.Inventory.AppBase.TableColumnTypeDto; using WinIn.FasterZ.Wms.AppBase.Extensions; using WinIn.FasterZ.Wms.AppBaseBusiness.ExportCustomUserSetting; #pragma warning disable CS8602 // Dereference of a possibly null reference. namespace WinIn.FasterZ.Wms.AppBase; [Route("Api/[controller]")] public class ZbxBase : CrudAppService, IZbxBase where TEntity : class, IEntity where TEntityDto : IEntityDto { #region 定义 /// /// redis缓存时间 分钟 /// private const int CacheMinute = 30; private readonly IRepository _repository; private IMapper _mapper; private IStringLocalizer Localizer => LazyServiceProvider.LazyGetRequiredService>(); private ExportCustomUserSettingAppService ExportCustomUserSettingAppService => LazyServiceProvider.LazyGetRequiredService(); private IDistributedCache Cache => LazyServiceProvider.LazyGetRequiredService>(); #endregion /// /// 构造方法 /// /// protected ZbxBase(IRepository repository) : base(repository) { _repository = repository; } #region 公开接口 #region 单体操作 /// /// 【基础】-【新增】 /// /// /// [HttpPost("Base/Create")] [Authorize] public override async Task CreateAsync(TCreateInput input) { await CheckCreatePolicyAsync().ConfigureAwait(false); var entity = input!.ToObject(); //判断id是否是00000-0000 如果是则赋值 var mainId = (Guid)entity.GetType().GetProperty("Id")?.GetValue(entity)!; if (mainId == Guid.Empty) { mainId = Guid.NewGuid(); entity.GetType().GetProperty("Id")?.SetValue(entity, mainId); } #region 给所有字表的 Id和MasterId赋值 否则默认的会是000000-000-....的id 插入时会报错 var propertyInfos = entity.GetType().GetProperties(); foreach (var propertyInfo in propertyInfos) { //判断是否是List集合 if (propertyInfo.Name == "Details" && propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(List<>)) { var listProperty = typeof(TEntity).GetProperty("Details"); // 获取 List 的元素类型 if (listProperty != null) { var listItemType = listProperty.PropertyType.GetGenericArguments()[0]; // 获取元素类型的 ID 属性 var detailIdProperty = listItemType.GetProperty("Id"); var masterIdProperty = listItemType.GetProperty("MasterId"); if (detailIdProperty != null) { // 获取 List 属性的值 var list = (IList)listProperty.GetValue(entity); // 遍历 List 集合中的每个元素,给 ID 属性赋值 if (list != null) { foreach (var item in list) { if ((Guid)detailIdProperty.GetValue(item)! == Guid.Empty) { detailIdProperty.SetValue(item, Guid.NewGuid()); } } } } if (masterIdProperty != null) { // 获取 List 属性的值 var list = (IList)listProperty.GetValue(entity); // 遍历 List 集合中的每个元素,给 ID 属性赋值 if (list != null) { foreach (var item in list) { masterIdProperty.SetValue(item, mainId); } } } } } } #endregion TryToSetTenantId(entity); await Repository.InsertAsync(entity, true).ConfigureAwait(false); return await MapToGetOutputDtoAsync(entity).ConfigureAwait(false); } /// /// 【基础】-【删除】 /// /// /// [HttpDelete("Base/Delete")] [Authorize] public override async Task DeleteAsync(TKey id) { await CheckDeletePolicyAsync().ConfigureAwait(false); await _repository.DeleteAsync(id, true).ConfigureAwait(false); } /// /// 【基础】-【修改】 /// /// /// /// [HttpPut("Base/Update")] [Authorize] public override async Task UpdateAsync(TKey id, TUpdateInput input) { await CheckUpdatePolicyAsync().ConfigureAwait(true); var entity = await GetEntityByIdAsync(id).ConfigureAwait(true); //当一条库存的一部分被人修改了 数据无法提交 例如库存10个 修改了其中4个 这时乐观锁就被修改了 会抛出异常 if (entity.GetType().GetProperty("ConcurrencyStamp").GetValue(entity).ToString() != input.GetType().GetProperty("ConcurrencyStamp").GetValue(input).ToString()) { throw new UserFriendlyException($"您操作的数据已经被修改:\r\n" + $"已经由【{Newtonsoft.Json.JsonConvert.SerializeObject(entity)}】\r\n" + $"变更为【{Newtonsoft.Json.JsonConvert.SerializeObject(input)}】\r\n"); } Type? inputDetailDtoType = null; Type? entityDetailType = null; var inputDetailDtoTypeFlag = false; //input是否有子集合Details var entityDetailTypeFlag = false; //entity是否有子集合Details var entityProperties = entity.GetType().GetProperties(); var inputProperties = input.GetType().GetProperties(); //InputDto的 foreach (var propertyInfo in inputProperties) { if (propertyInfo.Name == "Details" && propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(List<>)) { var listProperty = typeof(TUpdateInput).GetProperty("Details"); if (listProperty != null) { inputDetailDtoType = listProperty.PropertyType.GetGenericArguments()[0]; inputDetailDtoTypeFlag = true; } } } if (inputDetailDtoTypeFlag) { //实体的 foreach (var propertyInfo in entityProperties) { //判断是否是List集合 if (propertyInfo.Name == "Details" && propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(List<>)) { var listProperty = typeof(TEntity).GetProperty("Details"); // 获取 List 的元素类型 if (listProperty != null) { entityDetailType = listProperty.PropertyType.GetGenericArguments()[0]; entityDetailTypeFlag = true; } } } } if (inputDetailDtoTypeFlag && entityDetailTypeFlag) { var config = new MapperConfiguration(cfg => { // 动态创建映射关系 cfg.CreateMap(typeof(TEntityDto), typeof(TEntity)); cfg.CreateMap(typeof(TUpdateInput), typeof(TEntity)); cfg.CreateMap(inputDetailDtoType, entityDetailType); }); _mapper = new Mapper(config); } else { var config = new MapperConfiguration(cfg => { // 动态创建映射关系 //todo 这里先要判断ID是否是000000-000000 cfg.CreateMap(typeof(TEntityDto), typeof(TEntity)); cfg.CreateMap(typeof(TUpdateInput), typeof(TEntity)) .ForMember("Id", opt => opt.Ignore()) .ForMember("ConcurrencyStamp", opt => opt.Ignore()); }); _mapper = new Mapper(config); } MapProperties(input, entity); //entity.GetType().GetProperty("Id")?.SetValue(entity,id); await ReMoveCaCheAsync(id).ConfigureAwait(false); await _repository.UpdateAsync(entity, true).ConfigureAwait(true); return await MapToGetOutputDtoAsync(entity).ConfigureAwait(false); } #endregion #region 批量操作 /// /// 【基础】-【批量】-【新增】 /// /// /// [HttpPost("Base/Create-Many")] [Authorize] public async Task> CreateManyAsync(List inputList) { var dtoList = new List(); foreach (var input in inputList) { dtoList.Add(await CreateAsync(input).ConfigureAwait(false)); } return dtoList; } /// /// 【基础】-【批量】-【删除】 /// /// /// [HttpDelete("Base/Delete-Many")] [Authorize] public async Task DeleteAsync(IEnumerable keyList) { await _repository.DeleteManyAsync(keyList.AsEnumerable(), true).ConfigureAwait(false); } #endregion /// /// 【基础】-【分页查询】【有筛选条件】 /// /// /// /// /// [HttpPost("Base/Get-List-Page-By-Filter")] [Authorize] public virtual async Task> GetPageListByFilterAsync( SfsRequestInputBase sfsRequestInputBase, bool includeDetails = false, CancellationToken cancellationToken = default) { await CheckGetListPolicyAsync(); var expression = sfsRequestInputBase.Condition.Filters?.Count > 0 ? sfsRequestInputBase.Condition.Filters.ToLambda() : p => true; var resultEntities = await GetQueryListAsync(expression, sfsRequestInputBase.SkipCount, sfsRequestInputBase.MaxResultCount, sfsRequestInputBase.Sorting, includeDetails, cancellationToken); var resultDtos = ObjectMapper.Map, List>(resultEntities); //获取总数 var totalCount = await GetCountAsync(expression, cancellationToken); return new PagedResultDto(totalCount, resultDtos); } /// /// 【基础】-【获取所有数据】 /// /// /// /// /// [HttpPost("Base/Get-All-List")] [Authorize] public virtual async Task> GetAllListAsync() { await CheckGetListPolicyAsync(); var entities = await _repository.GetListAsync(); return await MapToGetListOutputDtosAsync(entities); } /// /// 【基础】-【导出Excel】【有筛选条件】 /// /// 查询条件 /// 是否冗余主表数据 /// 是否导出子表 /// 用户ID /// [HttpPost("Base/Export-To-Excel")] [Authorize] public virtual async Task ExportToExcelAsync(SfsRequestInputBase sfsRequestInputBase, bool isRedundance, Guid userId, bool isDetailExport = true) { var isHasDetail = false; //是否包含从表 var data = (await GetPageListByFilterAsync(sfsRequestInputBase, true)).Items; var fileStream = new MemoryStream(); //文件流 IWorkbook workbook = new XSSFWorkbook(); var sheet = workbook.CreateSheet(Localizer[typeof(TEntity).Name]); var splitDetailsColumnNumber = 1; //分割主表和从表的列数量 var excelDetailsCellStyle = SetExcelDetailsCellStyle(workbook); //子表单元格样式 var excelSplitCellStyle = SetSplitCellStyle(workbook); //分割单元格样式 var excelOnlyMainCellStyle = SetExcelOnlyMainCellStyle(workbook); var excelHeadCellStyle = SetExcelHeadCellStyle(workbook); // 获取主表的属性 创建主表 表头 var mainAllProperties = typeof(TEntityDto).GetProperties(); var mainProperties = mainAllProperties.Where(p => p.Name != "Details").ToArray(); //去除details属性否则导出时会带出来 #region 用户个性导出 主表 //获取个性导出的字段 var mainUserColumn = await ExportCustomUserSettingAppService.GetByUserIdAndExportTableNameAsync(userId, typeof(TEntity).Name); if (mainUserColumn.Any(p => p.CustomUserSetting == Enum_ExportCustomUserSetting.Yes)) { var showUserColumn = mainUserColumn.Where(p => p.CustomUserSetting == Enum_ExportCustomUserSetting.Yes) .Select(p => p.ExportColumnName?.ToLower()).Aggregate((a, b) => a + " " + b)?.Split(' ').ToList(); mainProperties = mainProperties.Where(p => showUserColumn.Contains(p.Name.ToLower())).ToArray(); } #endregion var headerRow = sheet.CreateRow(0); //标头列 for (var i = 0; i < mainProperties.Length; i++) { var englishName = mainProperties[i].Name; //本地化 var localizerName = Localizer[typeof(TEntity).Name + englishName]; var headCell = headerRow.CreateCell(i); headCell.SetCellValue(localizerName); headCell.CellStyle = excelHeadCellStyle; } // 获取从表的属性 创建从表 表头 var detailProperties = typeof(TEntityDto).GetProperty("Details")?.PropertyType.GetGenericArguments()[0] .GetProperties(); if (detailProperties != null) { isHasDetail = true; if (!isDetailExport) //是否要导出子表 { isHasDetail = false; } if (isHasDetail) { headerRow.CreateCell(mainProperties.Length).SetCellValue("---【分割】---"); #region 用户个性导出 从表 //获取个性导出的字段 var detailDtoName = mainAllProperties.First(p => p.Name == "Details"); var detailUserColumn = await ExportCustomUserSettingAppService.GetByUserIdAndExportTableNameAsync( userId, detailDtoName.PropertyType.GenericTypeArguments.First().Name.Replace("Dto", "")); var detailNotShowUserColumn = detailUserColumn .Where(p => p.CustomUserSetting == Enum_ExportCustomUserSetting.No).Select(p => p.ExportColumnName) .ToList(); if (detailUserColumn.Any()) { detailProperties = detailProperties.Where(p => !detailNotShowUserColumn.Contains(p.Name)).ToArray(); } #endregion for (var i = 0; i < detailProperties.Length; i++) { headerRow.CreateCell(mainProperties.Length + splitDetailsColumnNumber + i) .SetCellValue(detailProperties[i].Name); var headCell = headerRow.GetCell(mainProperties.Length + splitDetailsColumnNumber + i); headCell.CellStyle = excelHeadCellStyle; } } } // 填充数据行 var rowIndex = 1; foreach (var mainDto in data) { if (isHasDetail) { // 获取从表数据 var detailsIndex = mainAllProperties.FindIndex(p => p.Name == "Details"); // 子表 var detailList = (IEnumerable)mainAllProperties[detailsIndex].GetValue(mainDto); var startMainRowIndex = rowIndex; for (var datailCount = 0; datailCount < detailList.Count(); datailCount++) { var dataRow = sheet.CreateRow(rowIndex); if (isRedundance) { // 填充主表数据 for (var i = 0; i < mainProperties.Length; i++) { var value = mainProperties[i].GetValue(mainDto); dataRow.CreateCell(i).SetCellValue(value?.ToString()); } } else { if (datailCount == 0) { // 填充主表数据 for (var i = 0; i < mainProperties.Length; i++) { var value = mainProperties[i].GetValue(mainDto); dataRow.CreateCell(i).SetCellValue(value?.ToString()); } } } rowIndex++; } var overMainRowIndex = rowIndex; foreach (var detail in detailList) { if (startMainRowIndex <= overMainRowIndex) { //填充子表数据 var detailRow = sheet.GetRow(startMainRowIndex); var splitCell = detailRow.CreateCell(mainProperties.Length); splitCell.CellStyle = excelSplitCellStyle; for (var i = 0; i < detailProperties.Length; i++) { var value = detailProperties[i].GetValue(detail); detailRow.CreateCell(mainProperties.Length + splitDetailsColumnNumber + i) .SetCellValue(value?.ToString()); var detailCell = detailRow.GetCell(mainProperties.Length + splitDetailsColumnNumber + i); detailCell.CellStyle = excelDetailsCellStyle; } } startMainRowIndex++; } } else { var dataRow = sheet.CreateRow(rowIndex); // 填充主表数据 for (var i = 0; i < mainProperties.Length; i++) { var value = mainProperties[i].GetValue(mainDto); dataRow.CreateCell(i).SetCellValue(value?.ToString()); } if (rowIndex % 2 == 0) { dataRow.RowStyle = excelOnlyMainCellStyle; } } //添加1个空行将2条数据分割开 rowIndex++; } #region 自动调整列宽 // 自动调整列宽 注意:这个影响性能 会遍历所有行 并且找出宽度最大值 //sheet.AutoSizeColumn(i); if (isHasDetail) { for (var i = 0; i < mainProperties.Length + splitDetailsColumnNumber + detailProperties.Length; i++) { var colWidth = Math.Max(sheet.GetColumnWidth(i) + 150, 265 * 15); if (colWidth > 255 * 256) //excel列有最大宽度限制 { colWidth = 6000; } sheet.SetColumnWidth(i, colWidth); sheet.SetColumnWidth(mainProperties.Length, 3600); } } else { for (var i = 0; i < mainProperties.Length; i++) { var colWidth = Math.Max(sheet.GetColumnWidth(i) + 150, 265 * 15); if (colWidth > 255 * 256) //excel列有最大宽度限制 { colWidth = 6000; } sheet.SetColumnWidth(i, colWidth); } } #endregion // 保存Excel文件到MemoryStream workbook.Write(fileStream, true); fileStream.Position = 0; // 创建FileContentResult返回Excel文件 var fileContentResult = new FileContentResult(fileStream.ToArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") { FileDownloadName = Localizer[typeof(TEntity).Name] + ".xlsx" }; await Task.CompletedTask; return fileContentResult; } /// /// 【基础】-【获取 增 改 查基础的Dto中属性的数据类型】 /// /// [HttpPost("Base/Get-Dto-Column-Type")] [Authorize] public virtual async Task> GetDtoColumnTypeAsync() { var tableColumnTypeDtos = new List { GetTableColumnTypeByTable(typeof(TEntity), "S"), GetTableColumnTypeByTable(typeof(TCreateInput), "C"), GetTableColumnTypeByTable(typeof(TUpdateInput), "U"), GetTableColumnTypeByTable(typeof(TEntity), "G") }; await Task.CompletedTask; return tableColumnTypeDtos; } /// /// 【基础】-【获取数据】-【缓存中读取】 /// /// [HttpPost("Base/Get-Cache-By-Id/{id}")] [Authorize] public virtual async Task GetCacheByIdAsync(TKey id) { var entity = await Cache.GetOrAddAsync( $"{typeof(TEntityDto).Name}:{id}".ToString(), async () => await GetEntityByIdAsync(id), GetCacheTime); var dto = ObjectMapper.Map(entity!); return dto; } /// /// 【基础】-【获取数据】 /// /// /// [HttpPost("Base/Get-By-Id/{id}")] [Authorize] public override Task GetAsync(TKey id) { return base.GetAsync(id); } /// /// 【基础】-【获取数据】 /// /// /// [HttpPost("Base/Get-List")] [Authorize] public override Task> GetListAsync(TPagedAndSortedResultRequestDto input) { return base.GetListAsync(input); } #endregion #region 私有处理 /// /// 清除缓存 /// /// /// protected async Task ReMoveCaCheAsync(TKey id) { await Cache.RemoveAsync(id?.ToString()); } /// /// 按表达式条件获取分页列表 /// /// /// /// /// /// /// /// private async Task> GetQueryListAsync(Expression> expression, int skipCount, int maxResultCount, string sorting, bool includeDetails = false, CancellationToken cancellationToken = default) { var query = await Repository.WithDetailsAsync(); //var query = await Repository.GetQueryableAsync(); var entities = query.Where(expression); entities = GetSortingQueryable(entities, sorting); var str = entities.ToQueryString(); Console.WriteLine("---------查询开始---------"); Console.WriteLine(); Console.WriteLine(str); Console.WriteLine(); Console.WriteLine("---------查询结束---------"); var result = entities.Skip(skipCount).Take(maxResultCount).ToList(); return result; } /// /// 设置排序 /// /// /// /// private IQueryable GetSortingQueryable(IQueryable entities, string sorting) { if (string.IsNullOrEmpty(sorting)) { var createTimePropertyInfo = typeof(TEntity).GetProperty(nameof(AuditedEntity.CreationTime)); if (createTimePropertyInfo != null) { entities = entities.OrderBy(nameof(AuditedEntity.CreationTime) + " DESC"); } else { entities = entities.OrderBy("Id DESC"); } } else { entities = entities.OrderBy(sorting); } return entities; } /// /// 获取Dto的属性名字和数据类型 /// /// /// /// private AllTableColumnTypeDto GetTableColumnTypeByTable(Type dtoType, string strDtoType) { var gDto = new AllTableColumnTypeDto { DtoType = strDtoType, DtoName = dtoType.FullName, ColumnsTypes = new List() }; var propertyInfos = dtoType.GetProperties(); foreach (var propertyInfo in propertyInfos) { var columnType = new ColumnType(); columnType.IsEnum = false; if (propertyInfo.PropertyType.GenericTypeArguments.Length > 0) { if (propertyInfo.PropertyType.GenericTypeArguments[0].IsEnum) { columnType.IsEnum = true; } columnType.Z_ColumnBaseType = propertyInfo.PropertyType.GenericTypeArguments[0].FullName!; } else { columnType.Z_ColumnBaseType = propertyInfo.PropertyType.FullName!; } columnType.Z_ColumnName = propertyInfo.Name; columnType.Z_ColumnType = propertyInfo.PropertyType.Name; gDto.ColumnsTypes.Add(columnType); } return gDto; } /// /// 【记录数量查询】【有筛选条件】 /// /// /// /// private async Task GetCountAsync(Expression> expression, CancellationToken cancellationToken = default) { var count = await _repository.LongCountAsync(expression, cancellationToken); return count; } /// /// 获取缓存redis时间 /// /// private static DistributedCacheEntryOptions GetCacheTime() { var random = new Random(); //解决雪崩 添加随机缓存时间 var time = CacheMinute + random.Next(10, 30); return new DistributedCacheEntryOptions { AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(time) }; } #region Excel导出的样式设置 /// /// 导出设置子表单元格样式 /// /// /// private static ICellStyle SetExcelDetailsCellStyle(IWorkbook workbook) { var cellStyle = workbook.CreateCellStyle(); cellStyle.FillBackgroundColor = IndexedColors.Grey25Percent.Index; cellStyle.FillForegroundColor = IndexedColors.Grey25Percent.Index; cellStyle.FillPattern = FillPattern.SolidForeground; return cellStyle; } /// /// 导出设置只有主表时的交替行 单元格样式 /// /// /// private static ICellStyle SetExcelOnlyMainCellStyle(IWorkbook workbook) { var cellStyle = workbook.CreateCellStyle(); cellStyle.FillBackgroundColor = IndexedColors.Grey25Percent.Index; cellStyle.FillForegroundColor = IndexedColors.Grey25Percent.Index; cellStyle.FillPattern = FillPattern.SolidForeground; return cellStyle; } /// /// 设置分割单元格的样式 /// /// /// private static ICellStyle SetSplitCellStyle(IWorkbook workbook) { var cellStyle = workbook.CreateCellStyle(); cellStyle.BorderLeft = BorderStyle.MediumDashed; cellStyle.BorderRight = BorderStyle.MediumDashed; cellStyle.LeftBorderColor = IndexedColors.BrightGreen.Index; cellStyle.RightBorderColor = IndexedColors.Grey25Percent.Index; cellStyle.FillBackgroundColor = IndexedColors.White.Index; cellStyle.FillForegroundColor = IndexedColors.White.Index; cellStyle.FillPattern = FillPattern.ThickVerticalBands; return cellStyle; } /// /// 导出设置表头单元格样式 /// /// /// private static ICellStyle SetExcelHeadCellStyle(IWorkbook workbook) { var cellStyle = workbook.CreateCellStyle(); cellStyle.FillBackgroundColor = IndexedColors.LightOrange.Index; cellStyle.FillForegroundColor = IndexedColors.LightOrange.Index; cellStyle.FillPattern = FillPattern.SolidForeground; return cellStyle; } public static void MapProperties(TSrc source, TDest destination) { if (source == null || destination == null) { throw new ArgumentNullException("Source and destination objects must not be null."); } var sourceType = typeof(TSrc); var destType = typeof(TDest); var sourceProperties = sourceType.GetProperties(); var destProperties = destType.GetProperties(); foreach (var sourceProperty in sourceProperties) { var destProperty = Array.Find(destProperties, p => p.Name == sourceProperty.Name && p.PropertyType == sourceProperty.PropertyType); if (destProperty != null && destProperty.CanWrite && destProperty.Name != "Id" && destProperty.Name != "ConcurrencyStamp") { var value = sourceProperty.GetValue(source); destProperty.SetValue(destination, value); } } } #endregion #endregion }