using FlexJobApi.Core; using Furion.DatabaseAccessor; using Furion.DataValidation; using Furion.FriendlyException; using Mapster; using MediatR; using Microsoft.CodeAnalysis; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; namespace FlexJobApi.User.Application { /// /// 保存菜单 /// public class SaveMenuCommandHandler( IRepository rep ) : IRequestHandler { private readonly IRepository rep = rep; /// public async Task Handle(SaveMenuCommand request, CancellationToken cancellationToken) { if (request.Id.HasValue) { var entity = await rep.AsQueryable() .Include(it => it.Childrens) .FirstOrDefaultAsync(it => it.Id == request.Id, cancellationToken); if (entity == null) throw Oops.Oh(EnumErrorCodeType.s404, "菜单"); if (entity.UserType != request.UserType) throw Oops.Oh(EnumErrorCodeType.s410, "用户类型"); if (entity.ClientType != request.ClientType) throw Oops.Oh(EnumErrorCodeType.s410, "客户端类型"); var path = await GetPathAsync(request.ParentId, request.Code, cancellationToken); if (await CheckExist(entity, request.ParentId, request.Code)) throw Oops.Oh(EnumErrorCodeType.s405, "菜单编号"); await UpdateChildrensPath($"{entity.Path}/{entity.Code}", $"{path}/{request.Code}", cancellationToken); request.Adapt(entity); SaveChildrens(entity, request); await rep.UpdateAsync(entity); return entity.Id; } else { var entity = new Menu(); request.Adapt(entity); entity.Path = await GetPathAsync(entity.ParentId, entity.Code, cancellationToken); if (await CheckExist(entity, request.ParentId, request.Code)) throw Oops.Oh(EnumErrorCodeType.s405, "菜单编号"); SaveChildrens(entity, request); await rep.InsertAsync(entity); return entity.Id; } } /// /// 保存下级 /// /// /// /// private void SaveChildrens(Menu entity, SaveMenuCommand request) { // 获取子集Id var childrenIds = GetRequestChildrenIds(request); // 删除子级 entity.Childrens = entity.Childrens.Where(it => childrenIds.Contains(it.Id)).ToList(); // 遍历分组 foreach (var group in request.Groups) { // 遍历按钮位置 foreach (var buttonLocation in group.ButtonLocations) { // 遍历按钮 添加或更新 foreach (var button in buttonLocation.Buttons) { var buttonEntity = entity.Childrens.FirstOrDefault(it => it.Id == button.Id); if (buttonEntity == null) { if (button.Id.HasValue) throw Oops.Oh(EnumErrorCodeType.s404, $"当前分组{group.Group}-位置{buttonLocation.Location}-按钮{button.Code}"); buttonEntity = new Menu { UserType = entity.UserType, ClientType = entity.ClientType, Type = EnumMenuType.Button, }; entity.Childrens.Add(buttonEntity); } else if (buttonEntity.Type != EnumMenuType.Button) throw Oops.Oh(EnumErrorCodeType.s400, $"当前分组{group.Group}-字段{button.Code}并非一个按钮"); buttonEntity.Path = $"{entity.Path}{entity.Code}/"; buttonEntity.Group = group.Group; buttonEntity.Location = buttonLocation.Location; button.Adapt(buttonEntity); } } // 遍历字段 添加或更新 foreach (var field in group.Fields) { var fieldEntity = entity.Childrens.FirstOrDefault(it => it.Id == field.Id); if (fieldEntity == null) { if (field.Id.HasValue) throw Oops.Oh(EnumErrorCodeType.s404, $"当前分组{group.Group}-字段{field.Code}"); fieldEntity = new Menu { UserType = entity.UserType, ClientType = entity.ClientType, Type = EnumMenuType.Field, }; entity.Childrens.Add(fieldEntity); } else if (fieldEntity.Type != EnumMenuType.Field) throw Oops.Oh(EnumErrorCodeType.s400, $"当前分组{group.Group}-位置{fieldEntity.Location}-按钮{fieldEntity.Code}并非一个字段"); fieldEntity.Path = $"{entity.Path}{entity.Code}/"; fieldEntity.Group = group.Group; field.Adapt(fieldEntity); } } CheckRepeatChildrens(entity); } /// /// 校验重复子集 /// /// private void CheckRepeatChildrens(Menu entity) { var repeats = entity.Childrens .GroupBy(it => { return it.Type == EnumMenuType.Button ? $"{it.Group}:{it.Type}:{it.Location}:{it.Code}" : $"{it.Group}:{it.Type}:{it.Code}"; }) .Select(it => new { it.Key, Count = it.Count() }) .Where(it => it.Count > 1) .SplitJoin(it => it.Key, ","); if (repeats.IsNotNull()) throw Oops.Oh(EnumErrorCodeType.s406, repeats); } /// /// 获取请求子集Id /// /// /// private List GetRequestChildrenIds(SaveMenuCommand request) { return request.Groups .Select(g => g.ButtonLocations.SelectMany(bl => bl.Buttons.Where(b => b.Id.HasValue).Select(b => b.Id!.Value)) .Union( g.Fields.Where(f => f.Id.HasValue).Select(f => f.Id!.Value))) .SelectMany(it => it) .ToList(); } /// /// 校验菜单是否重复 /// /// /// /// /// private async Task CheckExist(Menu entity, Guid? parentId, string code) { return await rep.AsQueryable().AsNoTracking() .AnyAsync(it => it.UserType == entity.UserType && it.ClientType == entity.ClientType && it.ParentId == parentId && it.Code == code && it.Id != entity.Id); } /// /// 获取地址 /// /// /// /// /// private async Task GetPathAsync(Guid? parentId, string code, CancellationToken cancellationToken) { if (parentId.HasValue) { var parent = await rep.AsQueryable().AsNoTracking() .Where(it => it.Id == parentId) .Select(it => new { it.Path, it.Code }) .FirstOrDefaultAsync(cancellationToken); if (parent == null) throw Oops.Oh(EnumErrorCodeType.s404, "上级菜单"); return $"{parent.Path}{parent.Code}/"; } else { return "/"; } } /// /// 更新下级菜单路径 /// /// /// /// /// private async Task UpdateChildrensPath(string oldPath, string newPath, CancellationToken cancellationToken) { var models = await rep.AsQueryable() .Where(it => it.Path.StartsWith(oldPath)) .Where(it => it.Type == EnumMenuType.Menu || it.Type == EnumMenuType.Page || it.Type == EnumMenuType.Modal) .ToListAsync(cancellationToken); if (models.IsNotNull()) { foreach (var model in models) { model.Path = model.Path.Replace(oldPath, newPath); } await rep.UpdateAsync(models); } } } }