using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.IO; using System.Linq; using System.Linq.Dynamic.Core; using System.Linq.Expressions; using System.Reflection; using System.Security.Principal; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using AutoMapper.Internal; using DocumentFormat.OpenXml.Math; using DocumentFormat.OpenXml.Office2010.ExcelAc; using DocumentFormat.OpenXml.Spreadsheet; using DocumentFormat.OpenXml.Vml.Office; using DocumentFormat.OpenXml.Wordprocessing; using EFCore.BulkExtensions; using JetBrains.Annotations; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; 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 Volo.Abp.EventBus.Local; using Volo.Abp.SettingManagement; using Volo.Abp.Uow; using Win_in.Sfs.Basedata.Application.Contracts; using Win_in.Sfs.Shared.Application.Contracts; using Win_in.Sfs.Shared.Application.Contracts.ExportAndImport; using Win_in.Sfs.Shared.Domain; using Win_in.Sfs.Shared.Domain.Shared; using Win_in.Sfs.Shared.Event; namespace Win_in.Sfs.Shared.Application; /// /// 应用服务基类 /// public abstract class SfsCrudWithDetailsAppServiceBase : CrudAppService , ISfsCrudWithDetailsAppService , ISfsGetByNumberAppService where TEntity : class, IEntity where TEntityDto : class, IEntityDto, new() where TRequestInput : SfsRequestInputBase where TDetail : SfsDetailEntityBase where TDetailDTO : class, new() where TImportInput : class, new() { protected readonly ISfsRepositoryBase _repository; /// /// 实体名称 /// protected readonly string EntityClassName = typeof(TEntity).Name; protected SfsCrudWithDetailsAppServiceBase(ISfsRepositoryBase repository) : base(repository) { _repository = repository; } protected IDistributedCache Cache => LazyServiceProvider.LazyGetRequiredService>(); protected IDocumentSettingAppService DocumentSettingAppService => LazyServiceProvider.LazyGetRequiredService(); protected IExportImportService ExportImportService => LazyServiceProvider.LazyGetRequiredService(); protected IHttpContextAccessor HttpContextAccessor => LazyServiceProvider.LazyGetRequiredService(); protected ILocalEventBus LocalEventBus => LazyServiceProvider.LazyGetRequiredService(); /// /// /// protected ISettingManager SettingManager => LazyServiceProvider.LazyGetRequiredService(); /// /// 添加明细列表 /// /// 实体Id /// 明细列表 [HttpPost("details/")] public virtual async Task AddDetailListAsync(Guid id, List list) { var entity = await _repository.GetAsync(id).ConfigureAwait(false); Check.NotNull(entity, EntityClassName); var details = ObjectMapper.Map, List>(list); if (entity is SfsMasterAggregateRootBase masterEntity) { masterEntity.AddDetails(details); } } /// /// 新增实体 /// /// CreateInput [HttpPost("")] public override async Task CreateAsync(TCreateInput input) { await CheckCreatePolicyAsync().ConfigureAwait(continueOnCapturedContext: false); var entity = input.ToObject(); SetIdForGuids(entity); TryToSetTenantId(entity); await Repository.InsertAsync(entity, autoSave: true).ConfigureAwait(continueOnCapturedContext: false); var dto = entity.ToObject(); await Cache.SetItemAsync(dto.Id.ToString(), dto, SfsCacheConst.SeveralMinutes).ConfigureAwait(false); return dto; } /// /// 删除实体 /// /// 实体Id [HttpDelete("{id}")] public override async Task DeleteAsync(Guid id) { await Repository.DeleteAsync(id).ConfigureAwait(false); await Cache.DeleteItemAsync(id.ToString()).ConfigureAwait(false); return; } /// /// 删除明细 /// /// 实体Id /// 明细Id /// [HttpDelete("details/{id}")] public virtual async Task DeleteDetailAsync(Guid id, Guid detailId) { var entity = await _repository.GetAsync(id).ConfigureAwait(false); Check.NotNull(entity, EntityClassName); if (entity is SfsMasterAggregateRootBase masterEntity) { masterEntity.RemoveDetail(detailId); } } /// /// 导出数据 /// [HttpPost("export")] public virtual async Task ExportAsync(TRequestInput input) { var expression = input.Condition.Filters?.Count > 0 ? input.Condition.Filters.ToLambda() : p => true; var entities = await _repository.GetPagedListAsync(expression, input.SkipCount, input.MaxResultCount, input.Sorting, true).ConfigureAwait(false); var list = ObjectMapper.Map, List>(entities); var hasDetails = typeof(TEntity) is SfsMasterAggregateRootBase detailEntity; return ExportImportService.Export(list, detailsProptyName: hasDetails ? nameof(detailEntity.Details) : null); } /// /// 按条件获取全部数据列表 /// /// RequestInput /// 是否包含明细 /// /// [HttpPost("get-all")] public virtual async Task> GetAllListByFilterAsync(TRequestInput sfsRequestInput, bool includeDetails = false, CancellationToken cancellationToken = default) { Expression> expression = sfsRequestInput.Condition.Filters?.Count > 0 ? sfsRequestInput.Condition.Filters.ToLambda() : p => true; return await GetAllListAsync(expression, sfsRequestInput.Sorting, includeDetails, cancellationToken).ConfigureAwait(false); } /// /// 按Id获取实体 /// /// 实体Id /// [HttpGet("{id}")] public override async Task GetAsync(Guid id) { var dto = await Cache.GetOrAddItemAsync( id.ToString(), async () => await base.GetAsync(id).ConfigureAwait(false), SfsCacheConst.SeveralMinutes).ConfigureAwait(false); return dto; } /// /// 按编号获取实体 /// /// 编号 /// [HttpGet("by-number")] public virtual async Task GetByNumberAsync(string number) { var entity = (await _repository.GetQueryableAsync().ConfigureAwait(false)).Where("Number=@0", number).FirstOrDefault(); var dto = ObjectMapper.Map(entity); return dto; } /// /// 按条件获取数量 /// request sample /// { /// "maxResultCount": 1000, /// "skipCount": 0, /// "sorting": "", /// "condition": { "filters": []} /// } /// /// RequestInput /// /// [HttpPost("count")] public virtual async Task GetCountByFilterAsync(TRequestInput sfsRequestInput, CancellationToken cancellationToken = default) { Expression> expression = sfsRequestInput.Condition.Filters?.Count > 0 ? sfsRequestInput.Condition.Filters.ToLambda() : p => true; var count = await _repository.GetCountAsync(expression, cancellationToken).ConfigureAwait(false); return count; } /// /// 获取明细 /// /// 实体Id /// 明细Id /// [HttpGet("details/{id}")] public virtual async Task GetDetailAsync(Guid id, Guid detailId) { var entity = await _repository.GetAsync(id).ConfigureAwait(false); Check.NotNull(entity, EntityClassName); if (entity is SfsMasterAggregateRootBase masterEntity) { var detail = masterEntity.GetDetail(detailId); var dto = ObjectMapper.Map(detail); return dto; } return null; } /// /// 按条件获取明细列表 /// /// 实体Id /// 明细RequestInput /// [HttpGet("details/")] public virtual async Task> GetDetailListAsync(Guid id, TDetailRequestInput requestInput) { var entity = await _repository.GetAsync(id).ConfigureAwait(false); Check.NotNull(entity, EntityClassName); if (entity is SfsMasterAggregateRootBase masterEntity) { var details = masterEntity.GetDetailList(d => d.MasterID == id).ToList(); var dtos = ObjectMapper.Map, List>(details); return dtos; } return null; } /// /// 屏蔽基类方法 /// /// /// [NonAction] public override async Task> GetListAsync(TRequestInput input) { return await base.GetListAsync(input).ConfigureAwait(false); } /// /// 按条件获取分页列表 /// request sample /// { /// "maxResultCount": 1000, /// "skipCount": 0, /// "sorting": "", /// "condition": { "filters": []} /// } /// /// RequestInput /// 是否包含明细 /// /// [HttpPost("list")] public virtual async Task> GetPagedListByFilterAsync(TRequestInput sfsRequestInput, bool includeDetails = false, CancellationToken cancellationToken = default) { Expression> expression = sfsRequestInput.Condition.Filters?.Count > 0 ? sfsRequestInput.Condition.Filters.ToLambda() : p => true; return await GetPagedListAsync(expression, sfsRequestInput.SkipCount, sfsRequestInput.MaxResultCount, sfsRequestInput.Sorting, includeDetails, cancellationToken).ConfigureAwait(false); } /// /// 按关键字获取分页列表 /// /// 关键字 /// 跳过数 /// 最大结果数 /// 排序 /// 是否包含明细 /// /// [HttpPost("search")] public virtual async Task> GetPagedListByKeyWordAsync(string keyWord, int skipCount, int maxResultCount, string sorting, bool includeDetails = false, CancellationToken cancellationToken = default) { var expression = BuildSearchExpression(keyWord); return await GetPagedListAsync(expression, skipCount, maxResultCount, sorting, includeDetails, cancellationToken).ConfigureAwait(false); } /// /// 导入数据 /// [HttpPost("import")] [Consumes("multipart/form-data")] [UnitOfWork] public virtual async Task ImportAsync([FromForm] SfsImportRequestInput requestInput, [Required] IFormFile file) { using var ms = new MemoryStream(); await file.OpenReadStream().CopyToAsync(ms).ConfigureAwait(false); var inputFileBytes = ms.GetAllBytes(); var result = await ImportInternalAsync(requestInput, inputFileBytes); var bytes = result.FileContents; result.FileContents = null; HttpContextAccessor.HttpContext.Response.Headers.AccessControlExposeHeaders="X-Response"; HttpContextAccessor.HttpContext.Response.Headers.Add("X-Response", JsonSerializer.Serialize(new { result.ExceptionMessage, result.FileName, result.FileCode, result })); Console.WriteLine(@"导入错误信息:"+result.ExceptionMessage); var resultAction = new TestResult(bytes, ExportImportService.ContentType) { FileDownloadName = result.FileName }; resultAction.errorNum = result.ErrorNum; resultAction.successNum = resultAction.successNum; return resultAction; } public class TestResult: FileContentResult { public byte[]_bytes { get; set; } public int errorNum { get; set; } public int successNum { get; set; } public TestResult([NotNull] byte[] fileContents, [NotNull] string contentType) : base(fileContents, contentType) { } public TestResult([NotNull] byte[] fileContents, [NotNull] MediaTypeHeaderValue contentType) : base(fileContents, contentType) { } } /// /// 获取导入模板 /// [HttpPost("import-template")] public virtual IActionResult ImportTemplateAsync() { return ExportImportService.GetImportTemplate(); } /// /// 修改实体 /// /// 实体Id /// UpdateInput /// [HttpPut("{id}")] public override async Task UpdateAsync(Guid id, TCreateInput input) { try { //Value Inject 替换 AutoMapper await CheckUpdatePolicyAsync().ConfigureAwait(continueOnCapturedContext: false); TEntity entity = await GetEntityByIdAsync(id).ConfigureAwait(continueOnCapturedContext: false); entity.FromObject(input); await Repository.UpdateAsync(entity, autoSave: true).ConfigureAwait(continueOnCapturedContext: false); var dto = entity.ToObject(); await Cache.SetItemAsync(dto.Id.ToString(), dto, SfsCacheConst.SeveralMinutes).ConfigureAwait(false); return dto; } catch (Exception ex) { var message = ex.Message.Contains("Database operation expected to affect 1 row(s) but actually affected 0 row(s)") ? $" {typeof(TEntityDto).Name} 已经被他人更改, 请刷新数据后再更新." : ex.ToString(); throw new UserFriendlyException(message); } } /// /// 更新明细 /// /// 实体Id /// 明细Id /// 明细UpdateDto /// [HttpPut("details/{id}")] public virtual async Task UpdateDetailAsync(Guid id, Guid detailId, TDetailDTO updateDTO) { var entity = await _repository.GetAsync(id).ConfigureAwait(false); Check.NotNull(entity, EntityClassName); var detail = ObjectMapper.Map(updateDTO); if (entity is SfsMasterAggregateRootBase masterEntity) { masterEntity.ReplaceDetail(detailId, detail); } } /// /// 构造搜索条件表达式 /// /// 搜索关键字 /// protected virtual Expression> BuildSearchExpression(string keyWord) { // p => p.Id.ToString().Contains(keyWord); var expression = typeof(TEntity).GetExpressionByProperty("Number", keyWord); return expression; } protected virtual async Task GenerateNumberAsync(string typeName, DateTime time) { var documentInput = new DocumentSettingGenerateInput(typeName, Clock.Normalize(time)); var maxCount = 10_000; var tryCount = 0; while (true) { var number = await DocumentSettingAppService.GenerateNumberAsync(documentInput).ConfigureAwait(false); var entity = await GetByNumberAsync(number).ConfigureAwait(false); if (entity == null) { return number; } tryCount++; if (tryCount >= maxCount) { throw new UserFriendlyException($"无法生成 {documentInput.Type} 在 {documentInput.Time} 单据编号,请联系管理员处理。"); } } } /// /// 按表达式条件获取分页列表 /// /// /// /// /// /// protected async Task> GetAllListAsync(Expression> expression, string sorting, bool includeDetails, CancellationToken cancellationToken) { var entities = await _repository.GetListAsync(expression, sorting, includeDetails, cancellationToken).ConfigureAwait(false); var dtos = ObjectMapper.Map, List>(entities); return dtos; } /// /// 导入查询实体,可重写 /// /// /// protected virtual async Task GetEntityAsync(TImportInput importInput) { var source = await _repository.GetDbSetAsync().ConfigureAwait(false); return source.AsQueryable().WhereByKey(importInput)?.FirstOrDefault(); } /// /// 按表达式条件获取分页列表 /// /// /// /// /// /// /// /// protected async Task> GetPagedListAsync(Expression> expression, int skipCount, int maxResultCount, string sorting, bool includeDetails, CancellationToken cancellationToken) { var totalCount = await _repository.GetCountAsync(expression, cancellationToken).ConfigureAwait(false); var entities = await _repository.GetPagedListAsync(expression, skipCount, maxResultCount, sorting, includeDetails, cancellationToken).ConfigureAwait(false); var dtos = ObjectMapper.Map, List>(entities); return new PagedResultDto(totalCount, dtos); } /// /// 导入数据具体实现,可重写 /// [UnitOfWork] protected virtual async Task ImportInternalAsync(SfsImportRequestInput requestInput, byte[] inputFileBytes) { try { var hasDetails = typeof(TEntity).GetInterfaces().Any(o => o.IsGenericType && o.GetGenericTypeDefinition() == typeof(IMasterEntity<>)); var modelList = ExportImportService.Import(inputFileBytes); var modelDict = new Dictionary>(); var entityDict = new Dictionary(); foreach (var model in modelList) { // DataAnnotations 静态验证 var validationRresults = new List(); modelDict.Add(model, validationRresults); Validator.TryValidateObject(model, new ValidationContext(model, null, null), validationRresults); } // 如果没有验证错误或允许部分导入 if (!modelDict.SelectMany(o => o.Value).Any() || requestInput.IsAllowPartImport) { // 遍历并处理导入 if (hasDetails) // { var groups = modelList.AsQueryable().GroupByKey(); foreach (var item in groups) { var model = item.ToList().FirstOrDefault(); var validationRresults = modelDict[model]; await ImportValidationAsync(requestInput, modelDict, entityDict, model, validationRresults, item.ToList()).ConfigureAwait(false); } } else { foreach (var item in modelDict) { var model = item.Key; var validationRresults = item.Value; await ImportValidationAsync(requestInput, modelDict, entityDict, model, validationRresults).ConfigureAwait(false); } } } // 批量更新 if (entityDict.Any()) { entityDict=await ImportProcessingEntityAsync(entityDict).ConfigureAwait(false); // 调用批量验证 var entityListStatus = await ValidateImportEntities(entityDict).ConfigureAwait(false); if (entityListStatus) { await SaveImportAsync(entityDict).ConfigureAwait(false); } } //将需要新增的数据进行发布 var addList= entityDict.Where(p => p.Value == EntityState.Added).Select(p=>p.Key).ToList(); await PublishCreatedAsync(addList).ConfigureAwait(false); // 创建导入报告 var reportFile = ExportImportService.GetImportReport(inputFileBytes, modelDict); // 创建返回值 return new SfsImportResult { TotalNum = modelList.Count, ErrorNum = modelDict.Count(o => o.Value.Any()), FileName = reportFile.FileDownloadName, FileContents = reportFile.FileContents }; } catch (Exception ex) { Logger.LogException(ex); return new SfsImportResult() { ExceptionMessage = ex.Message }; } } /// /// 发布新增事件 /// /// /// private async Task PublishCreatedAsync(List entities) { try { await LocalEventBus.PublishAsync(new SfsCreatedEntityEventData>(entities),false).ConfigureAwait(false); } catch (Exception ex) { Logger.LogDebug($"{typeof(TEntity).Name} Created Event:{ex.Message}", null); Console.WriteLine(ex.Source); throw; } } /// /// 用来重写 导入数据时可以加工数据 /// /// /// protected virtual async Task> ImportProcessingEntityAsync(Dictionary dictionary) { return dictionary; } /// /// 导入保存到数据库,可重写 /// protected virtual async Task SaveImportAsync(Dictionary dict) { var entityList = dict.Keys.ToList(); var context = await _repository.GetDbContextAsync().ConfigureAwait(false); var list = new List(); if (entityList.Count > 0) { foreach (var entity in entityList) { foreach (var propertyInfo in entity.GetType().GetProperties()) { if (propertyInfo.PropertyType.IsListType() && propertyInfo.Name == "Details") { var entityDetails= propertyInfo.GetValue(entity, null); list.AddRange(((List)entityDetails)!); } } } var bulkConfig = new BulkConfig() { SetOutputIdentity = true, PreserveInsertOrder = true }; await context.BulkInsertOrUpdateAsync(entityList, bulkConfig).ConfigureAwait(false); await context.BulkInsertAsync(list).ConfigureAwait(false); } } /// /// 导入批量验证,可重写 /// protected virtual async Task ValidateImportEntities(Dictionary dict) { return await Task.FromResult(true).ConfigureAwait(false); } /// /// 导入单个输入,可重写 /// /// /// /// protected virtual async Task ValidateImportModelAsync(TImportInput model, List validationRresult) { await Task.CompletedTask.ConfigureAwait(false); } /// /// 验证单个实体,可重写 /// /// /// /// /// protected virtual async Task ValidateImportEntityAsync(TImportInput model, TEntity entity, List validationRresult) { await Task.CompletedTask.ConfigureAwait(false); } private async Task ImportValidationAsync(SfsImportRequestInput requestInput, Dictionary> modelDict, Dictionary entityDict, TImportInput model, List validationRresults, List detailModels = null) { var entity = await GetEntityAsync(model).ConfigureAwait(false); if (requestInput.Method == EnumImportMethod.Append) { if (entity != null) { validationRresults.Add(new ValidationResult($"无法添加,数据已存在", new string[] { "错误" })); } } else if (requestInput.Method == EnumImportMethod.Update) { if (entity == null) { validationRresults.Add(new ValidationResult($"无法更新,数据不存在", new string[] { "错误" })); } } // 不包含错误或运行部分插入时,执行动态验证 if (!modelDict.SelectMany(o => o.Value).Any() || requestInput.IsAllowPartImport) { var hasEntity = entity != null; await ValidateImportModelAsync(model, validationRresults).ConfigureAwait(false); } // 不包含错误或运行部分插入时,加入批量操作列表 if (!modelDict.SelectMany(o => o.Value).Any() || requestInput.IsAllowPartImport) { if (entity is IHasWorker worker) { worker.Worker = CurrentUser.GetUserName(); } var hasEntity = entity != null; if (entity == null) { entity = ObjectMapper.Map(model); if (entity is SfsMasterAggregateRootBase entityWithIdAndNumber) { entityWithIdAndNumber.SetIdAndNumberWithDetails(GuidGenerator, await GenerateNumberAsync(typeof(TEntity).Name, DateTime.Now.Date).ConfigureAwait(false)); } else if (entity is ISetId entityWithId) { entityWithId.SetId(GuidGenerator.Create()); } if (entity is CreationAuditedAggregateRoot creation) { creation.CreationTime = DateTime.Now; creation.CreatorId = CurrentUser.Id; } // 清空现有子表 (entity as SfsMasterAggregateRootBase)?.Details.Clear(); } else { var id = entity.Id; ObjectMapper.Map(model, entity); // ObjectMapper.Map 清空了 id,因此需要把预存的 id 取回 if (entity is ISetId entityWithId) { entityWithId.SetId(id); } } // 设置子表 if (detailModels != null && entity is SfsMasterAggregateRootBase masterEntity) { foreach (var item in detailModels) { var detail = ObjectMapper.Map(item); masterEntity.Details.Add(detail); if (detail is ISetId entityWithId) { entityWithId.SetId(GuidGenerator.Create()); } detail.SetIdAndNumber(GuidGenerator, masterEntity.Id, masterEntity.Number); if (detail is CreationAuditedAggregateRoot creation) { creation.CreationTime = DateTime.Now; creation.CreatorId = CurrentUser.Id; } } } await ValidateImportEntityAsync(model, entity, validationRresults).ConfigureAwait(false); entityDict.Add(entity, hasEntity ? EntityState.Modified : EntityState.Added); } } }