abp vNext微服務中的業務開發

前幾篇分別介紹了abp vNext微服務框架、開發環境搭建和vue element admin前端框架接入,在vue element admin中實現用戶角色管理基本功能後就能夠開始進行業務開發了,本篇會詳細的介紹如何在abp vNext中開發業務接口和前端頁面實現。前端

業務接口開發

業務接口就是針對業務api接口,經過abp vNext微服務中實現併發布業務接口後,前端獲取接口並進行界面開發,如此就實現了abp vNext微服務的先後端分離開發。vue

step1:建立實體(model)

abp vNext微服務框架中的業務開發一樣採用了經典的ddd架構風格和ef core 的code first模式,因此一切的業務都從領域模型(domain)開始。建立數據字典模型以下:web

    public class DataDictionary : AuditedAggregateRoot<Guid>,ISoftDelete
    {
        [NotNull]
        public string Name { get; set; }

        public string Code { get; set; }

        [NotNull]
        public string FullName { get; set; }

        public Guid? CategoryID { get; set; }

        public string Notes { get; set; }

        public Guid? PID { get; set; }

[NotNull] public int SEQ { get; set; } public bool IsEdit { get; set; } public bool IsDeleted { get; set; } }

step2:加入AbpDbContext:

public DbSet<DataDictionary> Dictionary { get; set; }

step3:構建ef實體:

            builder.Entity<DataDictionary>(b =>
            {
                b.ToTable("Dictionary");

                b.ConfigureConcurrencyStamp();
                b.ConfigureExtraProperties();
                b.ConfigureAudited();

                b.Property(x => x.Name).IsRequired().HasMaxLength(ProductConsts.MaxNameLength);
                b.Property(x => x.Code).HasMaxLength(ProductConsts.MaxCodeLength);
                b.Property(x => x.FullName).IsRequired().HasMaxLength(ProductConsts.MaxFullNameLength);
                b.Property(x => x.Notes).HasMaxLength(ProductConsts.MaxNotesLength);
                b.Property(x => x.SEQ).IsRequired();

                b.HasIndex(q => q.Code);
                b.HasIndex(q => q.Name);
                b.HasIndex(q => q.CategoryID);
            });

step4:使用ef遷移數據庫:

 Add-Migration
 Update-Database

查看輸出日誌是否成功遷移。sql

建立數據字典應用服務接口數據庫

step5:建立數據字典接口和dto以下:

 

    public interface IDictionaryAppService : IApplicationService
    {
        Task<PagedResultDto<DictionaryDto>> GetAll(GetDictionaryInputDto input);

        Task<DictionaryDto> Get(Guid id);

        Task<DictionaryDto> Create(CreateDictionaryDto input);

        Task<DictionaryDto> Update(Guid id, UpdateDictionary input);

        Task Delete(List<Guid> ids);
    }
    public class CreateDictionaryDto
    {
        [Required]
        [StringLength(ProductConsts.MaxNameLength)]
        public string Name { get; set; }

        [StringLength(ProductConsts.MaxCodeLength)]
        public string Code { get; set; }

        public Guid? CategoryID { get; set; }

        [StringLength(ProductConsts.MaxNotesLength)]
        public string Notes { get; set; }

        public Guid? PID { get; set; }

        public int SEQ { get; set; }

        public bool IsEdit { get; set; }
    }
    public class DictionaryDto : AuditedEntityDto<Guid>
    {
        public string Name { get; set; }

        public string Code { get; set; }

        public string FullName { get; set; }

        public Guid? CategoryID { get; set; }

        public string Notes { get; set; }

        public Guid? PID { get; set; }

        public int SEQ { get; set; }

        public bool IsEdit { get; set; }
    }
    public class GetDictionaryInputDto: PagedAndSortedResultRequestDto
    {
        public string Filter { get; set; }

        public Guid CategoryID { get; set; }
    }
    public class UpdateDictionary
    {
        [StringLength(ProductConsts.MaxNotesLength)]
        public string Notes { get; set; }

        public Guid? PID { get; set; }

        public int SEQ { get; set; }
    }

實現數據字典應用服務後端

step6:應用層實現以下:

 繼承基類:api

public class DictionaryAppService : ApplicationService, IDictionaryAppService

注入倉儲:前端框架

        private readonly IRepository<DataDictionary, Guid> _repository;public DictionaryAppService(
            IRepository<DataDictionary, Guid> repository)
        {
            _repository = repository;
        }

實現接口架構

新增:併發

        public async Task<DictionaryDto> Create(CreateDictionaryDto input)
        {
            var existingDic = await _repository.FirstOrDefaultAsync(d => d.Name == input.Name);
            if (existingDic != null)
            {
                throw new BusinessException("名稱: "+input.Name+"已存在");
            }var result = await _repository.InsertAsync(new DataDictionary
            {
                Id = GuidGenerator.Create(),
                Name = input.Name,
                Code = input.Code,
                FullName = input.Name,
                CategoryID = input.CategoryID,
                Notes = input.Notes,
                PID = input.PID,
                SEQ = input.SEQ,
                IsEdit=input.IsEdit
            });

            return ObjectMapper.Map<DataDictionary, DictionaryDto>(result);
        }

刪除:

        public async Task Delete(List<Guid> ids)
        {
            foreach (var id in ids)
            {
                await _repository.DeleteAsync(id);
            }
        }

查詢:

        public async Task<DictionaryDto> Get(Guid id)
        {
            var query = await _repository.GetAsync(id);
            var dto = ObjectMapper.Map<DataDictionary, DictionaryDto>(query);return dto;
        }

        public async Task<PagedResultDto<DictionaryDto>> GetAll(GetDictionaryInputDto input)
        {
            var query = _repository
                .Where(d => d.CategoryID == input.CategoryID)
                .WhereIf(!string.IsNullOrEmpty(input.Filter), d => d.Name.Contains(input.Filter) ||
                                                                   d.Code.Contains(input.Filter));

            var items = await query.OrderBy(input.Sorting ?? "SEQ")
                                        .Skip(input.SkipCount)
                                        .Take(input.MaxResultCount).ToListAsync();

            var totalCount = await query.CountAsync();
var dtos = ObjectMapper.Map<List<DataDictionary>, List<DictionaryDto>>(items);return new PagedResultDto<DictionaryDto>(totalCount, dtos);
        }

修改:

        public async Task<DictionaryDto> Update(Guid id, UpdateDictionary input)
        {var dic = await _repository.GetAsync(id);
            dic.Notes = input.Notes;
            dic.SEQ = input.SEQ;

            return ObjectMapper.Map<DataDictionary, DictionaryDto>(dic);
        }

增長數據字典權限

step7:權限

ProductManagementPermissions中增長靜態類DataDictionary:

        public static class DataDictionary
        {
            public const string Default = BasicDataManagement + ".DataDictionary";
            public const string Delete = Default + ".Delete";
            public const string Update = Default + ".Update";
            public const string Create = Default + ".Create";
        }

step8:構造權限

ProductManagementPermissionDefinitionProvider中增長dataDictionary:

            var dataDictionary = basicDataManagementGroup.AddPermission(ProductManagementPermissions.DataDictionary.Default, L("DataDictionary"));
            dataDictionary.AddChild(ProductManagementPermissions.DataDictionary.Create, L("Permission:Create"));
            dataDictionary.AddChild(ProductManagementPermissions.DataDictionary.Delete, L("Permission:Delete"));
            dataDictionary.AddChild(ProductManagementPermissions.DataDictionary.Update, L("Permission:Edit"));

增長完成後在數據字典應用服務中分別加上權限過濾器

[Authorize(ProductManagementPermissions.DataDictionary.Default)]
[Authorize(ProductManagementPermissions.DataDictionary.Create)]
[Authorize(ProductManagementPermissions.DataDictionary.Delete)]
[Authorize(ProductManagementPermissions.DataDictionary.Update)]

數據字典DTO映射

step9:ProductManagementApplicationAutoMapperProfile中增長數據字典模型-Dto映射:

          CreateMap<DataDictionary, DictionaryDto>();

數據字典web api實現

step10:abp vNext中沒有使用原先的應用服務動態api,如今api須要在控制器中實現

    [RemoteService]
    [Area("basicData")]
    [Route("api/basicData/dataDictionary")]
    public class DataDictionaryController : AbpController, IDictionaryAppService
    {
        private readonly IDictionaryAppService _dictionaryAppService;

        public DataDictionaryController(IDictionaryAppService dictionaryAppService)
        {
            _dictionaryAppService = dictionaryAppService;
        }

        [HttpPost]
        public Task<DictionaryDto> Create(CreateDictionaryDto input)
        {
            return _dictionaryAppService.Create(input);
        }

        [HttpPost]
        [Route("Delete")]
        public Task Delete(List<Guid> ids)
        {
            return _dictionaryAppService.Delete(ids);
        }

        [HttpGet]
        [Route("{id}")]
        public Task<DictionaryDto> Get(Guid id)
        {
            return _dictionaryAppService.Get(id);
        }

        [HttpGet]
        [Route("all")]
        public Task<PagedResultDto<DictionaryDto>> GetAll(GetDictionaryInputDto input)
        {
            return _dictionaryAppService.GetAll(input);
        }

        [HttpPut]
        [Route("{id}")]
        public Task<DictionaryDto> Update(Guid id, UpdateDictionary input)
        {
            return _dictionaryAppService.Update(id, input);
        }
    }

swagger文檔

 

總結

abp vNext的業務開發模式並無作太多改動,雖然接口開發的過程有些繁瑣,可是對於業務拆分和模型解耦來講是很是有必要的。ddd的架構風格讓開發人員無需依賴於sql語法,能夠使用抽象的倉儲進行輕鬆高效的查詢,這對於代碼維護和項目交接來講是十分幸運的。此外,abp vNext還將身份、權限、模型驗證、異常處理等進行了完美封裝,使得開發人員能夠專一於業務開發。

相關文章
相關標籤/搜索