diff --git a/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Application.Contracts/LINGYUN/Abp/Demo/Books/BookGetListInput.cs b/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Application.Contracts/LINGYUN/Abp/Demo/Books/BookGetListInput.cs new file mode 100644 index 000000000..0063bdca4 --- /dev/null +++ b/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Application.Contracts/LINGYUN/Abp/Demo/Books/BookGetListInput.cs @@ -0,0 +1,8 @@ +using Volo.Abp.Application.Dtos; + +namespace LINGYUN.Abp.Demo.Books; + +public class BookGetListInput : PagedAndSortedResultRequestDto +{ + public string? Filter { get; set; } +} diff --git a/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Application.Contracts/LINGYUN/Abp/Demo/Books/CreateUpdateBookDto.cs b/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Application.Contracts/LINGYUN/Abp/Demo/Books/CreateBookDto.cs similarity index 93% rename from aspnet-core/modules/demo/LINGYUN.Abp.Demo.Application.Contracts/LINGYUN/Abp/Demo/Books/CreateUpdateBookDto.cs rename to aspnet-core/modules/demo/LINGYUN.Abp.Demo.Application.Contracts/LINGYUN/Abp/Demo/Books/CreateBookDto.cs index 3fc3a3509..0b004f49d 100644 --- a/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Application.Contracts/LINGYUN/Abp/Demo/Books/CreateUpdateBookDto.cs +++ b/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Application.Contracts/LINGYUN/Abp/Demo/Books/CreateBookDto.cs @@ -2,7 +2,8 @@ using System.ComponentModel.DataAnnotations; namespace LINGYUN.Abp.Demo.Books; -public class CreateUpdateBookDto + +public class CreateBookDto { [Required] [StringLength(128)] @@ -19,4 +20,4 @@ public class CreateUpdateBookDto public float Price { get; set; } public Guid AuthorId { get; set; } -} \ No newline at end of file +} diff --git a/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Application.Contracts/LINGYUN/Abp/Demo/Books/IBookAppService.cs b/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Application.Contracts/LINGYUN/Abp/Demo/Books/IBookAppService.cs index 7a4dbeb19..92f53ccda 100644 --- a/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Application.Contracts/LINGYUN/Abp/Demo/Books/IBookAppService.cs +++ b/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Application.Contracts/LINGYUN/Abp/Demo/Books/IBookAppService.cs @@ -7,14 +7,20 @@ using Volo.Abp.Application.Services; namespace LINGYUN.Abp.Demo.Books; public interface IBookAppService : - ICrudAppService< //Defines CRUD methods - BookDto, //Used to show books - Guid, //Primary key of the book entity - PagedAndSortedResultRequestDto, //Used for paging/sorting - CreateUpdateBookDto>, //Used to create/update a book + IApplicationService, IExporterAppService, IImporterAppService { + Task CreateAsync(CreateBookDto input); + + Task UpdateAsync(Guid id, UpdateBookDto input); + + Task GetAsync(Guid id); + + Task DeleteAsync(Guid id); + + Task> GetListAsync(BookGetListInput input); + Task> GetAuthorLookupAsync(); /// /// 获取实体可访问规则 diff --git a/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Application.Contracts/LINGYUN/Abp/Demo/Books/UpdateBookDto.cs b/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Application.Contracts/LINGYUN/Abp/Demo/Books/UpdateBookDto.cs new file mode 100644 index 000000000..b31255321 --- /dev/null +++ b/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Application.Contracts/LINGYUN/Abp/Demo/Books/UpdateBookDto.cs @@ -0,0 +1,19 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace LINGYUN.Abp.Demo.Books; +public class UpdateBookDto +{ + [Required] + [StringLength(128)] + public string Name { get; set; } + + public BookType? Type { get; set; } + + [DataType(DataType.Date)] + public DateTime? PublishDate { get; set; } + + public float? Price { get; set; } + + public Guid AuthorId { get; set; } +} \ No newline at end of file diff --git a/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Application/LINGYUN/Abp/Demo/Books/BookAppService.cs b/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Application/LINGYUN/Abp/Demo/Books/BookAppService.cs index 8ac18bbbd..1fd70f793 100644 --- a/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Application/LINGYUN/Abp/Demo/Books/BookAppService.cs +++ b/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Application/LINGYUN/Abp/Demo/Books/BookAppService.cs @@ -7,7 +7,6 @@ using LINGYUN.Abp.Exporter; using Microsoft.AspNetCore.Authorization; using System.Linq.Dynamic.Core; using Volo.Abp.Application.Dtos; -using Volo.Abp.Application.Services; using Volo.Abp.Content; using Volo.Abp.Domain.Entities; using Volo.Abp.Localization; @@ -15,15 +14,9 @@ using Volo.Abp.Localization; namespace LINGYUN.Abp.Demo.Books; [Authorize(DemoPermissions.Books.Default)] -public class BookAppService : - CrudAppService< - Book, //The Book entity - BookDto, //Used to show books - Guid, //Primary key of the book entity - PagedAndSortedResultRequestDto, //Used for paging/sorting - CreateUpdateBookDto>, //Used to create/update a book - IBookAppService //implement the IBookAppService +public class BookAppService : DemoApplicationServiceBase, IBookAppService //implement the IBookAppService { + private readonly IBookRepository _bookRepository; private readonly IAuthorRepository _authorRepository; private readonly AuthorManager _authorManager; @@ -38,111 +31,80 @@ public class BookAppService : IBookRepository bookRepository, AuthorManager authorManager, IAuthorRepository authorRepository) - : base(bookRepository) { _exporterProvider = exporterProvider; _importerProvider = importerProvider; _authorManager = authorManager; _authorRepository = authorRepository; - - GetPolicyName = DemoPermissions.Books.Default; - GetListPolicyName = DemoPermissions.Books.Default; - CreatePolicyName = DemoPermissions.Books.Create; - UpdatePolicyName = DemoPermissions.Books.Edit; - DeletePolicyName = DemoPermissions.Books.Delete; + _bookRepository = bookRepository; LocalizationResource = typeof(DemoResource); ObjectMapperContext = typeof(AbpDemoApplicationModule); } - - [DisableDataProtected(DataAccessOperation.Read)]// 更新时禁用查询过滤 - public override Task UpdateAsync(Guid id, CreateUpdateBookDto input) - { - return base.UpdateAsync(id, input); - } - [DisableDataProtected(DataAccessOperation.Read)] - public override Task DeleteAsync(Guid id) + [Authorize(DemoPermissions.Books.Create)] + [DisableDataProtected]// 任何人都可创建 + public async virtual Task CreateAsync(CreateBookDto input) { - return base.DeleteAsync(id); + var author = await _authorRepository.GetAsync(input.AuthorId); + var book = new Book( + GuidGenerator.Create(), + input.Name, + input.Type, + input.PublishDate, + input.Price, + author.Id); + + await _bookRepository.InsertAsync(book); + + var bookDto = ObjectMapper.Map(book); + bookDto.AuthorName = author.Name; + + return bookDto; } - public async virtual Task ImportAsync(BookImportInput input) + [Authorize(DemoPermissions.Books.Edit)] + [DataProtected(DataAccessOperation.Write)]// 仅启用数据更新过滤器 + public async virtual Task UpdateAsync(Guid id, UpdateBookDto input) { - await CheckCreatePolicyAsync(); - await CheckPolicyAsync(DemoPermissions.Books.Import); + var book = await _bookRepository.GetAsync(id); - var stream = input.Content.GetStream(); - var createAuthors = new List(); - var existsAuthors = new List(); - var createManyDtos = await _importerProvider.ImportAsync(stream); - - foreach (var book in createManyDtos) + if (!input.Name.IsNullOrWhiteSpace() && !string.Equals(input.Name, book.Name, StringComparison.InvariantCultureIgnoreCase)) { - var author = existsAuthors.Find(x => x.Name == book.AuthorName) ?? - await _authorRepository.FindByNameAsync(book.AuthorName); - if (author == null) - { - author = await _authorManager.CreateAsync(book.AuthorName, Clock.Now); - createAuthors.Add(author); - } - if (!existsAuthors.Any(x => x.Name == author.Name)) - { - existsAuthors.Add(author); - } - book.AuthorId = author.Id; + book.Name = input.Name; } - - var createManyBooks = createManyDtos.Select(dto => + if (input.Type.HasValue && input.Type != book.Type) { - var book = ObjectMapper.Map(dto); - - TryToSetTenantId(book); - - return book; - }); - - if (createAuthors.Count > 0) + book.Type = input.Type.Value; + } + if (input.PublishDate.HasValue && input.PublishDate != book.PublishDate) { - await _authorRepository.InsertManyAsync(createAuthors, autoSave: true); + book.PublishDate = input.PublishDate.Value; + } + if (input.Price.HasValue && input.Price != book.Price) + { + book.Price = input.Price.Value; } - await Repository.InsertManyAsync(createManyBooks, autoSave: true); - } - - public async virtual Task ExportAsync(BookExportListInput input) - { - await CheckPolicyAsync(DemoPermissions.Books.Export); - - var bookSet = await Repository.GetQueryableAsync(); - var authorSet = await _authorRepository.GetQueryableAsync(); - - var query = from book in bookSet - join author in authorSet on book.AuthorId equals author.Id - select new { book, author }; - - query = query - .OrderBy(NormalizeSorting(input.Sorting)) - .Take(input.MaxResultCount); - - var queryResult = await AsyncExecuter.ToListAsync(query); - - var bookDtos = queryResult.Select(x => + if (input.AuthorId != book.AuthorId) { - var bookDto = ObjectMapper.Map(x.book); - bookDto.AuthorName = x.author.Name; - return bookDto; - }).ToList(); + var newAuthor = await _authorRepository.GetAsync(input.AuthorId); + book.AuthorId = newAuthor.Id; + } - var stream = await _exporterProvider.ExportAsync(bookDtos); + await _bookRepository.UpdateAsync(book); + var author = await _authorRepository.GetAsync(input.AuthorId); - return new RemoteStreamContent(stream, input.FileName); + var bookDto = ObjectMapper.Map(book); + bookDto.Name = author.Name; + + return bookDto; } - public override async Task GetAsync(Guid id) + public async virtual Task GetAsync(Guid id) { //Get the IQueryable from the repository - var queryable = await Repository.GetQueryableAsync(); + var queryable = await _bookRepository.GetQueryableAsync(); //Prepare a query to join books and authors var query = from book in queryable @@ -162,11 +124,9 @@ public class BookAppService : return bookDto; } - public override async Task> GetListAsync(PagedAndSortedResultRequestDto input) + public async virtual Task> GetListAsync(BookGetListInput input) { - //Get the IQueryable from the repository - var queryable = await Repository.GetQueryableAsync(); - + var queryable = await _bookRepository.GetQueryableAsync(); //Prepare a query to join books and authors var query = from book in queryable join author in await _authorRepository.GetQueryableAsync() on book.AuthorId equals author.Id @@ -174,6 +134,7 @@ public class BookAppService : //Paging query = query + .WhereIf(!input.Filter.IsNullOrWhiteSpace(), x => x.book.Name.Contains(input.Filter)) .OrderBy(NormalizeSorting(input.Sorting)) .Skip(input.SkipCount) .Take(input.MaxResultCount); @@ -190,7 +151,7 @@ public class BookAppService : }).ToList(); //Get the total count with another query - var totalCount = await Repository.GetCountAsync(); + var totalCount = await AsyncExecuter.CountAsync(query); return new PagedResultDto( totalCount, @@ -198,6 +159,90 @@ public class BookAppService : ); } + + [Authorize(DemoPermissions.Books.Delete)] + [DataProtected(DataAccessOperation.Delete)]// 仅启用数据删除过滤器 + public async virtual Task DeleteAsync(Guid id) + { + var book = await _bookRepository.GetAsync(id); + + await _bookRepository.DeleteAsync(book); + } + + [Authorize(DemoPermissions.Books.Import)] + [DisableDataProtected]// 任何人都可创建 + public async virtual Task ImportAsync(BookImportInput input) + { + var stream = input.Content.GetStream(); + var createAuthors = new List(); + var existsAuthors = new List(); + var createManyDtos = await _importerProvider.ImportAsync(stream); + + foreach (var book in createManyDtos) + { + var author = existsAuthors.Find(x => x.Name == book.AuthorName) ?? + await _authorRepository.FindByNameAsync(book.AuthorName); + if (author == null) + { + author = await _authorManager.CreateAsync(book.AuthorName, Clock.Now); + createAuthors.Add(author); + } + if (!existsAuthors.Any(x => x.Name == author.Name)) + { + existsAuthors.Add(author); + } + book.AuthorId = author.Id; + } + + var createManyBooks = createManyDtos.Select(dto => + { + var book = new Book( + GuidGenerator.Create(), + dto.Name, + dto.Type, + dto.PublishDate, + dto.Price, + dto.AuthorId); + + return book; + }); + + if (createAuthors.Count > 0) + { + await _authorRepository.InsertManyAsync(createAuthors, autoSave: true); + } + + await _bookRepository.InsertManyAsync(createManyBooks, autoSave: true); + } + + [Authorize(DemoPermissions.Books.Export)] + public async virtual Task ExportAsync(BookExportListInput input) + { + var bookSet = await _bookRepository.GetQueryableAsync(); + var authorSet = await _authorRepository.GetQueryableAsync(); + + var query = from book in bookSet + join author in authorSet on book.AuthorId equals author.Id + select new { book, author }; + + query = query + .OrderBy(NormalizeSorting(input.Sorting)) + .Take(input.MaxResultCount); + + var queryResult = await AsyncExecuter.ToListAsync(query); + + var bookDtos = queryResult.Select(x => + { + var bookDto = ObjectMapper.Map(x.book); + bookDto.AuthorName = x.author.Name; + return bookDto; + }).ToList(); + + var stream = await _exporterProvider.ExportAsync(bookDtos); + + return new RemoteStreamContent(stream, input.FileName); + } + public async Task> GetAuthorLookupAsync() { var authors = await _authorRepository.GetListAsync(); diff --git a/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Application/LINGYUN/Abp/Demo/DemoApplicationMapperProfile.cs b/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Application/LINGYUN/Abp/Demo/DemoApplicationMapperProfile.cs index e269a76df..15d60a2ea 100644 --- a/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Application/LINGYUN/Abp/Demo/DemoApplicationMapperProfile.cs +++ b/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Application/LINGYUN/Abp/Demo/DemoApplicationMapperProfile.cs @@ -15,7 +15,7 @@ public class DemoApplicationMapperProfile : Profile .Ignore(dto => dto.Id) .Ignore(dto => dto.ExtraProperties) .Ignore(dto => dto.ConcurrencyStamp); - CreateMap() + CreateMap() .IgnoreAuditedObjectProperties() .Ignore(dto => dto.Id) .Ignore(dto => dto.ExtraProperties) diff --git a/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Domain/LINGYUN/Abp/Demo/Books/Book.cs b/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Domain/LINGYUN/Abp/Demo/Books/Book.cs index 20c3194fa..79058bc2d 100644 --- a/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Domain/LINGYUN/Abp/Demo/Books/Book.cs +++ b/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Domain/LINGYUN/Abp/Demo/Books/Book.cs @@ -1,4 +1,5 @@ using LINGYUN.Abp.DataProtection; +using Volo.Abp.Data; using Volo.Abp.Domain.Entities.Auditing; namespace LINGYUN.Abp.Demo.Books; @@ -13,4 +14,27 @@ public class Book : AuditedAggregateRoot, IDataProtected public float Price { get; set; } public Guid AuthorId { get; set; } + + protected Book() + { + ExtraProperties = new ExtraPropertyDictionary(); + } + + public Book( + Guid id, + string name, + BookType type, + DateTime publishDate, + float price, + Guid authorId) + : base(id) + { + Name = name; + Type = type; + PublishDate = publishDate; + Price = price; + AuthorId = authorId; + + ExtraProperties = new ExtraPropertyDictionary(); + } } diff --git a/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Domain/LINGYUN/Abp/Demo/Books/BookAuth.cs b/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Domain/LINGYUN/Abp/Demo/Books/BookAuth.cs new file mode 100644 index 000000000..4a57902a5 --- /dev/null +++ b/aspnet-core/modules/demo/LINGYUN.Abp.Demo.Domain/LINGYUN/Abp/Demo/Books/BookAuth.cs @@ -0,0 +1,19 @@ +using LINGYUN.Abp.DataProtection; + +namespace LINGYUN.Abp.Demo.Books; + +public class BookAuth : DataAuthBase +{ + public BookAuth() + { + } + + public BookAuth( + Guid entityId, + string role, + string organizationUnit, + Guid? tenantId = null) + : base(entityId, role, organizationUnit, tenantId) + { + } +} diff --git a/aspnet-core/modules/demo/LINGYUN.Abp.Demo.EntityFrameworkCore/LINGYUN/Abp/Demo/Books/EfCoreBookRepository.cs b/aspnet-core/modules/demo/LINGYUN.Abp.Demo.EntityFrameworkCore/LINGYUN/Abp/Demo/Books/EfCoreBookRepository.cs index 6ad8fbea9..71f4f0183 100644 --- a/aspnet-core/modules/demo/LINGYUN.Abp.Demo.EntityFrameworkCore/LINGYUN/Abp/Demo/Books/EfCoreBookRepository.cs +++ b/aspnet-core/modules/demo/LINGYUN.Abp.Demo.EntityFrameworkCore/LINGYUN/Abp/Demo/Books/EfCoreBookRepository.cs @@ -5,7 +5,7 @@ using LINGYUN.Abp.Demo.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore; namespace LINGYUN.Abp.Demo.Books; -public class EfCoreBookRepository : EfCoreDataProtectionRepository, IBookRepository +public class EfCoreBookRepository : EfCoreDataProtectionRepository, IBookRepository { public EfCoreBookRepository( [NotNull] IDbContextProvider dbContextProvider, diff --git a/aspnet-core/modules/demo/LINGYUN.Abp.Demo.EntityFrameworkCore/LINGYUN/Abp/Demo/EntityFrameworkCore/DemoDbContext.cs b/aspnet-core/modules/demo/LINGYUN.Abp.Demo.EntityFrameworkCore/LINGYUN/Abp/Demo/EntityFrameworkCore/DemoDbContext.cs index 874d1dc9b..2e9eaab78 100644 --- a/aspnet-core/modules/demo/LINGYUN.Abp.Demo.EntityFrameworkCore/LINGYUN/Abp/Demo/EntityFrameworkCore/DemoDbContext.cs +++ b/aspnet-core/modules/demo/LINGYUN.Abp.Demo.EntityFrameworkCore/LINGYUN/Abp/Demo/EntityFrameworkCore/DemoDbContext.cs @@ -15,6 +15,12 @@ public class DemoDbContext : AbpDataProtectionDbContext { } + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + base.OnConfiguring(optionsBuilder); + optionsBuilder.EnableSensitiveDataLogging(); + } + protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); diff --git a/aspnet-core/modules/demo/LINGYUN.Abp.Demo.EntityFrameworkCore/LINGYUN/Abp/Demo/EntityFrameworkCore/DemoDbContextModelCreatingExtensions.cs b/aspnet-core/modules/demo/LINGYUN.Abp.Demo.EntityFrameworkCore/LINGYUN/Abp/Demo/EntityFrameworkCore/DemoDbContextModelCreatingExtensions.cs index 017c0dc2f..c0116fbfd 100644 --- a/aspnet-core/modules/demo/LINGYUN.Abp.Demo.EntityFrameworkCore/LINGYUN/Abp/Demo/EntityFrameworkCore/DemoDbContextModelCreatingExtensions.cs +++ b/aspnet-core/modules/demo/LINGYUN.Abp.Demo.EntityFrameworkCore/LINGYUN/Abp/Demo/EntityFrameworkCore/DemoDbContextModelCreatingExtensions.cs @@ -1,4 +1,5 @@ -using LINGYUN.Abp.Demo.Authors; +using LINGYUN.Abp.DataProtection.EntityFrameworkCore; +using LINGYUN.Abp.Demo.Authors; using LINGYUN.Abp.Demo.Books; using Microsoft.EntityFrameworkCore; using Volo.Abp; @@ -41,5 +42,7 @@ public static class DemoDbContextModelCreatingExtensions b.HasIndex(x => x.Name); }); + + builder.ConfigureEntityAuth(); } } diff --git a/aspnet-core/modules/demo/LINGYUN.Abp.Demo.HttpApi/LINGYUN/Abp/Demo/Books/BookController.cs b/aspnet-core/modules/demo/LINGYUN.Abp.Demo.HttpApi/LINGYUN/Abp/Demo/Books/BookController.cs index 781808e1b..f14396250 100644 --- a/aspnet-core/modules/demo/LINGYUN.Abp.Demo.HttpApi/LINGYUN/Abp/Demo/Books/BookController.cs +++ b/aspnet-core/modules/demo/LINGYUN.Abp.Demo.HttpApi/LINGYUN/Abp/Demo/Books/BookController.cs @@ -25,7 +25,7 @@ public class BookController : AbpControllerBase, IBookAppService [HttpPost] [Authorize(DemoPermissions.Books.Create)] - public virtual Task CreateAsync(CreateUpdateBookDto input) + public virtual Task CreateAsync(CreateBookDto input) { return _service.CreateAsync(input); } @@ -67,7 +67,7 @@ public class BookController : AbpControllerBase, IBookAppService } [HttpGet] - public virtual Task> GetListAsync(PagedAndSortedResultRequestDto input) + public virtual Task> GetListAsync(BookGetListInput input) { return _service.GetListAsync(input); } @@ -75,7 +75,7 @@ public class BookController : AbpControllerBase, IBookAppService [HttpPut] [Route("{id}")] [Authorize(DemoPermissions.Books.Edit)] - public virtual Task UpdateAsync(Guid id, CreateUpdateBookDto input) + public virtual Task UpdateAsync(Guid id, UpdateBookDto input) { return _service.UpdateAsync(id, input); }