[Abp vNext微服务实践] - 业务开发

前几篇分别介绍了abp vNext微服务框架、开发环境搭建和vue element admin前端框架接入,在vue element admin中实现用户角色管理基本功能后就可以开始进行业务开发了,本篇会详细的介绍如何在abp vNext中开发业务接口和前端页面实现。

业务接口开发#

业务接口就是针对业务api接口,通过abp vNext微服务中实现并发布业务接口后,前端获取接口并进行界面开发,如此就实现了abp vNext微服务的前后端分离开发。

step1:创建实体(model)#

abp vNext微服务框架中的业务开发同样采用了经典的ddd架构风格和ef core 的code first模式,所以一切的业务都从领域模型(domain)开始。创建数据字典模型如下:

    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

查看输出日志是否成功迁移。

创建数据字典应用服务接口

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:应用层实现如下:#

 继承基类:

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还将身份、权限、模型验证、异常处理等进行了完美封装,使得开发人员可以专注于业务开发。

posted @ 2021-01-29 09:17  韩梦芫  阅读(387)  评论(0编辑  收藏  举报