前幾篇分別介紹了abp vNext微服務框架、開發環境搭建和vue element admin前端框架接入,在vue element admin中實現用戶角色管理基本功能後就能夠開始進行業務開發了,本篇會詳細的介紹如何在abp vNext中開發業務接口和前端頁面實現。html
業務接口就是針對業務api接口,經過abp vNext微服務中實現併發布業務接口後,前端獲取接口並進行界面開發,如此就實現了abp vNext微服務的先後端分離開發。前端
abp vNext微服務框架中的業務開發一樣採用了經典的ddd架構風格和ef core 的code first模式,因此一切的業務都從領域模型(domain)開始。建立數據字典模型以下:vue
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; } }
public DbSet<DataDictionary> Dictionary { get; set; }
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); });
Add-Migration Update-Database
查看輸出日誌是否成功遷移。web
建立數據字典應用服務接口sql
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; } }
實現數據字典應用服務數據庫
繼承基類:後端
public class DictionaryAppService : ApplicationService, IDictionaryAppService
注入倉儲:api
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); }
增長數據字典權限
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"; }
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映射
CreateMap<DataDictionary, DictionaryDto>();
數據字典web 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還將身份、權限、模型驗證、異常處理等進行了完美封裝,使得開發人員能夠專一於業務開發。