sunpengfei
2025-08-06 350aa66d4818f85f042cb2d399691209dc97b97e
feat:字典开发
2个文件已添加
24个文件已修改
909 ■■■■■ 已修改文件
FlexJobApi.Application/Dictionaries/Commands/DictionaryCategoryCommandHandler.cs 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
FlexJobApi.Application/Dictionaries/Commands/DictionaryDataCommandHandler.cs 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
FlexJobApi.Application/Dictionaries/Queries/DictionaryCategoriesQueryHandler.cs 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
FlexJobApi.Application/Dictionaries/Queries/DictionaryDatasQueryHandler.cs 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
FlexJobApi.Application/FlexJobApi.Application.xml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
FlexJobApi.Core/Entities/Common/DictionaryData.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
FlexJobApi.Core/Entities/Users/Menu.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
FlexJobApi.Core/FlexJobApi.Core.xml 196 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
FlexJobApi.Core/Interfaces/ITreeData.cs 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
FlexJobApi.Core/Models/Main/Dictionaries/Queries/GetDictionaryCategorySelectQuery.cs 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
FlexJobApi.Core/Models/Main/Dictionaries/Queries/GetDictionaryDataSelectQuery.cs 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
FlexJobApi.Core/Models/Main/Dictionaries/Queries/GetDictionaryDatasQuery.cs 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
FlexJobApi.Core/Models/User/Menus/Commands/SaveMenuButtonCommand.cs 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
FlexJobApi.Core/Models/User/Menus/Commands/SaveMenuCommand.cs 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
FlexJobApi.Core/Models/User/Menus/Commands/SaveMenuFieldCommand.cs 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
FlexJobApi.Core/Models/User/Roles/Commands/SetRoleIsDisabledCommand.cs 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
FlexJobApi.Core/Utils/DbUtils/DbUtils.cs 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
FlexJobApi.Core/Utils/DbUtils/SelectQuery.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
FlexJobApi.User.Application/Auths/Queries/GetCurrentLogierMenusQueryHandler.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
FlexJobApi.User.Application/FlexJobApi.User.Application.xml 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
FlexJobApi.User.Application/Menus/Commands/SaveMenuButtonCommandHandler.cs 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
FlexJobApi.User.Application/Menus/Commands/SaveMenuCommandHandler.cs 112 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
FlexJobApi.User.Application/Menus/Commands/SaveMenuFieldCommandHandler.cs 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
FlexJobApi.User.Application/Roles/Commands/SetRoleIsDisabledCommandHandler.cs 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
FlexJobApi.User.Application/Roles/Queries/GetRolesQueryHandler.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
FlexJobApi.User.Application/UserInfos/Queries/GetOperationUserInfosQueryHandler.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
FlexJobApi.Application/Dictionaries/Commands/DictionaryCategoryCommandHandler.cs
@@ -35,7 +35,8 @@
        public Task<Guid> Handle(SaveDictionaryCategoryCommand request, CancellationToken cancellationToken)
        {
            return request.SaveData<DictionaryCategory, SaveDictionaryCategoryCommand>(
                 (q, e, r) => q.Any(it => it.Id != request.Id && it.Code == request.Code),
                 it => it.Id != request.Id && it.Code == request.Code,
                 null,
                 cancellationToken);
        }
    }
FlexJobApi.Application/Dictionaries/Commands/DictionaryDataCommandHandler.cs
@@ -1,7 +1,10 @@
using FlexJobApi.Core;
using Furion.DatabaseAccessor;
using Mapster;
using MediatR;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -25,11 +28,25 @@
        public Task<Guid> Handle(SaveDictionaryDataCommand request, CancellationToken cancellationToken)
        {
            return request.SaveData<DictionaryData, SaveDictionaryDataCommand>(
                (q, e, r) => q.Any(it =>
                it =>
                    it.CategoryId == request.CategoryId
                    && it.ParentId == request.ParentId
                    && it.Code == request.Code
                    && it.Content == request.Content), cancellationToken);
                    && it.Content == request.Content
                    && it.Id != request.Id,
                (entity) =>
                {
                    entity.Path = DbUtils.GetTreeDataPath<DictionaryData>(request.ParentId, cancellationToken).Result;
                    if (request.Id.HasValue)
                    {
                        DbUtils.UpdateTreeDataChildrenPath<DictionaryData>(
                           $"{entity.Path}/{entity.Code}/",
                           $"{entity.Path}/{request.Code}/",
                           cancellationToken).Wait();
                    }
                    request.Adapt(entity);
                },
                cancellationToken);
        }
        /// <summary>
@@ -40,7 +57,7 @@
        /// <returns></returns>
        public Task<int> Handle(SetDictionaryDataIsDisabledCommand request, CancellationToken cancellationToken)
        {
            return request.SetIsDisable<DictionaryData>(cancellationToken: cancellationToken);
            return request.SetIsDisabled<DictionaryData>(cancellationToken: cancellationToken);
        }
    }
}
FlexJobApi.Application/Dictionaries/Queries/DictionaryCategoriesQueryHandler.cs
@@ -17,7 +17,7 @@
    public class DictionaryCategoriesQueryHandler(
            IRepository<DictionaryCategory> rep
        ) : IRequestHandler<GetDictionaryCategoriesQuery, PagedListQueryResult<GetDictionaryCategoriesQueryResultItem>>,
            IRequestHandler<GetDictionaryCategorySelectQuery, List<SelectQueryResultItem<Guid>>>
            IRequestHandler<GetDictionaryCategorySelectQuery, List<SelectQueryResultOption<Guid, GetDictionaryCategorySelectQueryOption>>>
    {
        private readonly IRepository<DictionaryCategory> rep = rep;
@@ -29,10 +29,9 @@
        /// <returns></returns>
        public Task<PagedListQueryResult<GetDictionaryCategoriesQueryResultItem>> Handle(GetDictionaryCategoriesQuery request, CancellationToken cancellationToken)
        {
            return request.PageModel.ToPagedListAsync<DictionaryCategory, GetDictionaryCategoriesQueryResultItem>(
            return request.PageModel.GetPagedListAsync<DictionaryCategory, GetDictionaryCategoriesQueryResultItem>(
                q =>
                {
                    q = q.OrderBy(it => it.Sort).ThenBy(it => it.CreatedTime);
                    if (request.Keywords.IsNotNull())
                    {
                        q = q.Where(it =>
@@ -50,23 +49,13 @@
        /// <param name="request"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public Task<List<SelectQueryResultItem<Guid>>> Handle(GetDictionaryCategorySelectQuery request, CancellationToken cancellationToken)
        public Task<List<SelectQueryResultOption<Guid, GetDictionaryCategorySelectQueryOption>>> Handle(GetDictionaryCategorySelectQuery request, CancellationToken cancellationToken)
        {
            var items = rep.AsQueryable().AsNoTracking()
                .Select(it => new SelectQueryResultItem<Guid>
                {
                    Value = it.Id,
                    Label = it.Name,
                    Data = new
                    {
                        it.Id,
                        it.Name,
                        it.Remark,
                        it.FieldNames
                    }
                })
                .ToListAsync(cancellationToken);
            return items;
            return request.GetSelect<DictionaryCategory, Guid, GetDictionaryCategorySelectQueryOption>(
                it => it.Id,
                it => it.Name,
                null,
                cancellationToken);
        }
    }
}
FlexJobApi.Application/Dictionaries/Queries/DictionaryDatasQueryHandler.cs
@@ -1,5 +1,6 @@
using FlexJobApi.Core;
using Furion.DatabaseAccessor;
using Furion.FriendlyException;
using Mapster;
using MediatR;
using Microsoft.EntityFrameworkCore;
@@ -16,7 +17,8 @@
    /// </summary>
    public class DictionaryDatasQueryHandler(
            IRepository<DictionaryData> rep
        ) : IRequestHandler<GetDictionaryDatasQuery, PagedListQueryResult<GetDictionaryDatasQueryResultItem>>
        ) : IRequestHandler<GetDictionaryDatasQuery, PagedListQueryResult<GetDictionaryDatasQueryResultItem>>,
            IRequestHandler<GetDictionaryDataSelectQuery, List<SelectQueryResultOption<Guid, GetDictionaryDataSelectQueryResultOption>>>
    {
        private readonly IRepository<DictionaryData> rep = rep;
@@ -28,13 +30,21 @@
        /// <returns></returns>
        public Task<PagedListQueryResult<GetDictionaryDatasQueryResultItem>> Handle(GetDictionaryDatasQuery request, CancellationToken cancellationToken)
        {
            return request.PageModel.ToPagedListAsync<DictionaryData, GetDictionaryDatasQueryResultItem>(
            return request.PageModel.GetPagedListAsync<DictionaryData, GetDictionaryDatasQueryResultItem>(
                q =>
                {
                    q = q.OrderBy(it => it.Sort).ThenBy(it => it.CreatedTime);
                    if (request.CategoryId.HasValue)
                    {
                        q = q.Where(it => it.CategoryId == request.CategoryId);
                    }
                    else if (request.CategoryCode.IsNotNull())
                    {
                        q = q.Where(it => it.Category.Code == request.CategoryCode);
                    }
                    else
                    {
                        throw Oops.Oh(EnumErrorCodeType.s400, "请填写类别Id或编号");
                    }
                    if (request.Keywords.IsNotNull())
                    {
@@ -50,5 +60,40 @@
                    return q;
                }, cancellationToken: cancellationToken);
        }
        /// <summary>
        /// 查询数据字典选择器
        /// </summary>
        /// <param name="request"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public Task<List<SelectQueryResultOption<Guid, GetDictionaryDataSelectQueryResultOption>>> Handle(GetDictionaryDataSelectQuery request, CancellationToken cancellationToken)
        {
            return request.GetSelect<DictionaryData, Guid, GetDictionaryDataSelectQueryResultOption>(
                it => it.Id,
                it => it.Content,
                q =>
                {
                    q = q
                        .OrderBy(it => it.Sort).ThenBy(it => it.CreatedTime)
                        .Where(it =>
                            it.ParentId == request.ParentId
                            && !it.IsDisabled);
                    if (request.CategoryId.HasValue)
                    {
                        q = q.Where(it => it.CategoryId == request.CategoryId);
                    }
                    else if (request.CategoryCode.IsNotNull())
                    {
                        q = q.Where(it => it.Category.Code == request.CategoryCode);
                    }
                    else
                    {
                        throw Oops.Oh(EnumErrorCodeType.s400, "请填写类别Id或编号");
                    }
                    return q;
                },
                cancellationToken);
        }
    }
}
FlexJobApi.Application/FlexJobApi.Application.xml
@@ -90,5 +90,13 @@
            <param name="cancellationToken"></param>
            <returns></returns>
        </member>
        <member name="M:FlexJobApi.Application.DictionaryDatasQueryHandler.Handle(FlexJobApi.Core.GetDictionaryDataSelectQuery,System.Threading.CancellationToken)">
            <summary>
            查询数据字典选择器
            </summary>
            <param name="request"></param>
            <param name="cancellationToken"></param>
            <returns></returns>
        </member>
    </members>
</doc>
FlexJobApi.Core/Entities/Common/DictionaryData.cs
@@ -13,7 +13,7 @@
    /// <summary>
    /// 字典数据
    /// </summary>
    public class DictionaryData : CommonEntity, IEntityTypeBuilder<DictionaryData>, IIsDisabled
    public class DictionaryData : CommonEntity, IEntityTypeBuilder<DictionaryData>, ITreeData<DictionaryData>, IIsDisabled
    {
        public DictionaryData()
        {
FlexJobApi.Core/Entities/Users/Menu.cs
@@ -13,7 +13,7 @@
    /// <summary>
    /// 菜单
    /// </summary>
    public class Menu : CommonEntity, IEntityTypeBuilder<Menu>, IIsDisabled
    public class Menu : CommonEntity, IEntityTypeBuilder<Menu>, ITreeData<Menu>, IIsDisabled
    {
        public Menu()
        {
FlexJobApi.Core/FlexJobApi.Core.xml
@@ -2129,6 +2129,37 @@
            物理删除
            </summary>
        </member>
        <member name="T:FlexJobApi.Core.ITreeData`1">
            <summary>
            树形数据
            </summary>
            <typeparam name="TEntity"></typeparam>
        </member>
        <member name="P:FlexJobApi.Core.ITreeData`1.ParentId">
            <summary>
            上级Id
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.ITreeData`1.Parent">
            <summary>
            上级
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.ITreeData`1.Children">
            <summary>
            下级
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.ITreeData`1.Path">
            <summary>
            字典路径
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.ITreeData`1.Code">
            <summary>
            编号
            </summary>
        </member>
        <member name="T:FlexJobApi.Core.DeleteDictionaryCategoryCommand">
            <summary>
            删除数据字典类别
@@ -2267,6 +2298,96 @@
        <member name="T:FlexJobApi.Core.GetDictionaryCategorySelectQuery">
            <summary>
            查询数据字典类别选择器数据
            </summary>
        </member>
        <member name="T:FlexJobApi.Core.GetDictionaryCategorySelectQueryOption">
            <summary>
            查询数据字典类别选择器数据-结果-选项
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.GetDictionaryCategorySelectQueryOption.Id">
            <summary>
            Id
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.GetDictionaryCategorySelectQueryOption.Code">
            <summary>
            编号
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.GetDictionaryCategorySelectQueryOption.Name">
            <summary>
            名称
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.GetDictionaryCategorySelectQueryOption.FieldNames">
            <summary>
            字段名(逗号隔开)
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.GetDictionaryCategorySelectQueryOption.Remark">
            <summary>
            备注
            </summary>
        </member>
        <member name="T:FlexJobApi.Core.GetDictionaryDataSelectQuery">
            <summary>
            查询数据字典选择器
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.GetDictionaryDataSelectQuery.CategoryId">
            <summary>
            类别Id
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.GetDictionaryDataSelectQuery.ParentId">
            <summary>
            上级Id
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.GetDictionaryDataSelectQueryResultOption.Id">
            <summary>
            Id
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.GetDictionaryDataSelectQueryResultOption.Path">
            <summary>
            字典路径
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.GetDictionaryDataSelectQueryResultOption.Code">
            <summary>
            编号
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.GetDictionaryDataSelectQueryResultOption.Content">
            <summary>
            显示内容
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.GetDictionaryDataSelectQueryResultOption.Field1">
            <summary>
            字段1
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.GetDictionaryDataSelectQueryResultOption.Field2">
            <summary>
            字段2
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.GetDictionaryDataSelectQueryResultOption.Field3">
            <summary>
            字段3
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.GetDictionaryDataSelectQueryResultOption.Field4">
            <summary>
            字段4
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.GetDictionaryDataSelectQueryResultOption.Field5">
            <summary>
            字段5
            </summary>
        </member>
        <member name="T:FlexJobApi.Core.GetDictionaryDatasQuery">
@@ -2474,11 +2595,6 @@
            保存菜单按钮
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.SaveMenuButtonCommand.Id">
            <summary>
            Id
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.SaveMenuButtonCommand.ParentId">
            <summary>
            菜单Id
@@ -2527,11 +2643,6 @@
        <member name="T:FlexJobApi.Core.SaveMenuCommand">
            <summary>
            保存菜单
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.SaveMenuCommand.Id">
            <summary>
            Id
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.SaveMenuCommand.UserType">
@@ -2717,11 +2828,6 @@
        <member name="T:FlexJobApi.Core.SaveMenuFieldCommand">
            <summary>
            保存菜单字段
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.SaveMenuFieldCommand.Id">
            <summary>
            Id
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.SaveMenuFieldCommand.ParentId">
@@ -3349,16 +3455,6 @@
            设置角色是否禁用
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.SetRoleIsDisabledCommand.Ids">
            <summary>
            Id
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.SetRoleIsDisabledCommand.IsDisabled">
            <summary>
            是否禁用
            </summary>
        </member>
        <member name="T:FlexJobApi.Core.SetRoleUserInfosCommand">
            <summary>
            设置角色用户
@@ -3810,7 +3906,39 @@
            数据库工具
            </summary>
        </member>
        <member name="M:FlexJobApi.Core.DbUtils.ToPagedListAsync``2(FlexJobApi.Core.PagedListQueryPageModel,System.Func{System.Linq.IQueryable{``0},System.Linq.IQueryable{``0}},System.Linq.Expressions.Expression{System.Func{``0,``1}},System.Threading.CancellationToken)">
        <member name="M:FlexJobApi.Core.DbUtils.GetTreeDataPath``1(System.Nullable{System.Guid},System.Threading.CancellationToken)">
            <summary>
            获取树形数据路径
            </summary>
            <typeparam name="TEntity"></typeparam>
            <param name="parentId"></param>
            <param name="cancellationToken"></param>
            <returns></returns>
        </member>
        <member name="M:FlexJobApi.Core.DbUtils.UpdateTreeDataChildrenPath``1(System.String,System.String,System.Threading.CancellationToken)">
            <summary>
            更新树形数据下级路径
            </summary>
            <param name="oldPath"></param>
            <param name="newPath"></param>
            <param name="cancellationToken"></param>
            <returns></returns>
        </member>
        <member name="M:FlexJobApi.Core.DbUtils.GetSelect``3(FlexJobApi.Core.SelectQuery{``1,``2},System.Func{``2,``1},System.Func{``2,System.String},System.Func{System.Linq.IQueryable{``0},System.Linq.IQueryable{``0}},System.Threading.CancellationToken)">
            <summary>
            查询选择器数据
            </summary>
            <typeparam name="TEntity"></typeparam>
            <typeparam name="TValue"></typeparam>
            <typeparam name="TData"></typeparam>
            <param name="request"></param>
            <param name="getValue"></param>
            <param name="getLabel"></param>
            <param name="query"></param>
            <param name="cancellationToken"></param>
            <returns></returns>
        </member>
        <member name="M:FlexJobApi.Core.DbUtils.GetPagedListAsync``2(FlexJobApi.Core.PagedListQueryPageModel,System.Func{System.Linq.IQueryable{``0},System.Linq.IQueryable{``0}},System.Linq.Expressions.Expression{System.Func{``0,``1}},System.Threading.CancellationToken)">
            <summary>
            查询分页列表数据
            </summary>
@@ -3831,7 +3959,7 @@
            <param name="orders"></param>
            <returns></returns>
        </member>
        <member name="M:FlexJobApi.Core.DbUtils.SetIsDisable``1(FlexJobApi.Core.SetIsDisabledCommand,System.Func{System.Linq.IQueryable{``0},System.Linq.IQueryable{``0}},System.Threading.CancellationToken)">
        <member name="M:FlexJobApi.Core.DbUtils.SetIsDisabled``1(FlexJobApi.Core.SetIsDisabledCommand,System.Func{System.Linq.IQueryable{``0},System.Linq.IQueryable{``0}},System.Threading.CancellationToken)">
            <summary>
            设置是否禁用
            </summary>
@@ -3851,7 +3979,7 @@
            <param name="cancellationToken"></param>
            <returns></returns>
        </member>
        <member name="M:FlexJobApi.Core.DbUtils.SaveData``2(``1,System.Func{System.Linq.IQueryable{``0},``0,``1,System.Boolean},System.Threading.CancellationToken)">
        <member name="M:FlexJobApi.Core.DbUtils.SaveData``2(``1,System.Linq.Expressions.Expression{System.Func{``0,System.Boolean}},System.Action{``0},System.Threading.CancellationToken)">
            <summary>
            保存数据
            </summary>
@@ -3859,6 +3987,7 @@
            <typeparam name="TRequest"></typeparam>
            <param name="request"></param>
            <param name="checkExist"></param>
            <param name="update"></param>
            <param name="cancellationToken"></param>
            <returns></returns>
        </member>
@@ -3970,28 +4099,29 @@
            Id
            </summary>
        </member>
        <member name="T:FlexJobApi.Core.SelectQuery`1">
        <member name="T:FlexJobApi.Core.SelectQuery`2">
            <summary>
            选择器查询
            </summary>
            <typeparam name="TValue"></typeparam>
            <typeparam name="TData"></typeparam>
        </member>
        <member name="T:FlexJobApi.Core.SelectQueryResultItem`1">
        <member name="T:FlexJobApi.Core.SelectQueryResultOption`2">
            <summary>
            选择器查询-结果-项
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.SelectQueryResultItem`1.Value">
        <member name="P:FlexJobApi.Core.SelectQueryResultOption`2.Value">
            <summary>
            值
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.SelectQueryResultItem`1.Label">
        <member name="P:FlexJobApi.Core.SelectQueryResultOption`2.Label">
            <summary>
            标签
            </summary>
        </member>
        <member name="P:FlexJobApi.Core.SelectQueryResultItem`1.Data">
        <member name="P:FlexJobApi.Core.SelectQueryResultOption`2.Data">
            <summary>
            数据
            </summary>
FlexJobApi.Core/Interfaces/ITreeData.cs
New file
@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FlexJobApi.Core
{
    /// <summary>
    /// 树形数据
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    public interface ITreeData<TEntity>
        where TEntity : CommonEntity, ITreeData<TEntity>, new()
    {
        /// <summary>
        /// 上级Id
        /// </summary>
        Guid? ParentId { get; set; }
        /// <summary>
        /// 上级
        /// </summary>
        TEntity Parent { get; set; }
        /// <summary>
        /// 下级
        /// </summary>
        List<TEntity> Children { get; set; }
        /// <summary>
        /// 字典路径
        /// </summary>
        string Path { get; set; }
        /// <summary>
        /// 编号
        /// </summary>
        string Code { get; set; }
    }
}
FlexJobApi.Core/Models/Main/Dictionaries/Queries/GetDictionaryCategorySelectQuery.cs
@@ -2,6 +2,7 @@
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -12,7 +13,38 @@
    /// 查询数据字典类别选择器数据
    /// </summary>
    [Resource([EnumResourceController.Dictionary])]
    public class GetDictionaryCategorySelectQuery : SelectQuery<Guid>
    public class GetDictionaryCategorySelectQuery : SelectQuery<Guid, GetDictionaryCategorySelectQueryOption>
    {
    }
    /// <summary>
    /// 查询数据字典类别选择器数据-结果-选项
    /// </summary>
    public class GetDictionaryCategorySelectQueryOption
    {
        /// <summary>
        /// Id
        /// </summary>
        public Guid Id { get; set; }
        /// <summary>
        /// 编号
        /// </summary>
        public string Code { get; set; }
        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 字段名(逗号隔开)
        /// </summary>
        public string FieldNames { get; set; }
        /// <summary>
        /// 备注
        /// </summary>
        public string Remark { get; set; }
    }
}
FlexJobApi.Core/Models/Main/Dictionaries/Queries/GetDictionaryDataSelectQuery.cs
New file
@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FlexJobApi.Core
{
    /// <summary>
    /// 查询数据字典选择器
    /// </summary>
    [Resource([EnumResourceController.Dictionary])]
    public class GetDictionaryDataSelectQuery : SelectQuery<Guid, GetDictionaryDataSelectQueryResultOption>
    {
        /// <summary>
        /// 类别Id(Id/编号二选一)
        /// </summary>
        public Guid? CategoryId { get; set; }
        /// <summary>
        /// 类别编号(Id/编号二选一)
        /// </summary>
        public string CategoryCode { get; set; }
        /// <summary>
        /// 上级Id
        /// </summary>
        public Guid? ParentId { get; set; }
    }
    public class GetDictionaryDataSelectQueryResultOption
    {
        /// <summary>
        /// Id
        /// </summary>
        public Guid Id { get; set; }
        /// <summary>
        /// 字典路径
        /// </summary>
        public string Path { get; set; }
        /// <summary>
        /// 编号
        /// </summary>
        [MaxLength(128)]
        public string Code { get; set; }
        /// <summary>
        /// 显示内容
        /// </summary>
        [Required]
        public string Content { get; set; }
        /// <summary>
        /// 字段1
        /// </summary>
        public string Field1 { get; set; }
        /// <summary>
        /// 字段2
        /// </summary>
        public string Field2 { get; set; }
        /// <summary>
        /// 字段3
        /// </summary>
        public string Field3 { get; set; }
        /// <summary>
        /// 字段4
        /// </summary>
        public string Field4 { get; set; }
        /// <summary>
        /// 字段5
        /// </summary>
        public string Field5 { get; set; }
    }
}
FlexJobApi.Core/Models/Main/Dictionaries/Queries/GetDictionaryDatasQuery.cs
@@ -15,11 +15,16 @@
    public class GetDictionaryDatasQuery : PagedListQuery<PagedListQueryResult<GetDictionaryDatasQueryResultItem>, GetDictionaryDatasQueryResultItem>
    {
        /// <summary>
        /// 类别Id
        /// 类别Id(Id/编号二选一)
        /// </summary>
        public Guid? CategoryId { get; set; }
        /// <summary>
        /// 类别编号(Id/编号二选一)
        /// </summary>
        public string CategoryCode { get; set; }
        /// <summary>
        /// 关键字
        /// </summary>
        public string Keywords { get; set; }
FlexJobApi.Core/Models/User/Menus/Commands/SaveMenuButtonCommand.cs
@@ -11,13 +11,8 @@
    /// 保存菜单按钮
    /// </summary>
    [Resource([EnumResourceController.Menu])]
    public class SaveMenuButtonCommand : IRequest<Guid>
    public class SaveMenuButtonCommand : SaveDataCommand
    {
        /// <summary>
        /// Id
        /// </summary>
        public Guid? Id { get; set; }
        /// <summary>
        /// 菜单Id
        /// </summary>
FlexJobApi.Core/Models/User/Menus/Commands/SaveMenuCommand.cs
@@ -12,17 +12,12 @@
    /// 保存菜单
    /// </summary>
    [Resource([EnumResourceController.Menu])]
    public class SaveMenuCommand : IRequest<Guid>
    public class SaveMenuCommand : SaveDataCommand
    {
        public SaveMenuCommand()
        {
            Groups = [];
        }
        /// <summary>
        /// Id
        /// </summary>
        public Guid? Id { get; set; }
        /// <summary>
        /// 用户类型
FlexJobApi.Core/Models/User/Menus/Commands/SaveMenuFieldCommand.cs
@@ -11,13 +11,8 @@
    /// 保存菜单字段
    /// </summary>
    [Resource([EnumResourceController.Menu])]
    public class SaveMenuFieldCommand : IRequest<Guid>
    public class SaveMenuFieldCommand : SaveDataCommand
    {
        /// <summary>
        /// Id
        /// </summary>
        public Guid? Id { get; set; }
        /// <summary>
        /// 菜单Id
        /// </summary>
FlexJobApi.Core/Models/User/Roles/Commands/SetRoleIsDisabledCommand.cs
@@ -12,22 +12,8 @@
    /// 设置角色是否禁用
    /// </summary>
    [Resource([EnumResourceController.Role])]
    public class SetRoleIsDisabledCommand : IRequest<int>
    public class SetRoleIsDisabledCommand : SetIsDisabledCommand
    {
        public SetRoleIsDisabledCommand()
        {
            Ids = [];
        }
        /// <summary>
        /// Id
        /// </summary>
        [Required]
        public List<Guid> Ids { get; set; }
        /// <summary>
        /// 是否禁用
        /// </summary>
        public bool IsDisabled { get; set; }
    }
}
FlexJobApi.Core/Utils/DbUtils/DbUtils.cs
@@ -16,6 +16,7 @@
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
namespace FlexJobApi.Core
{
@@ -24,6 +25,105 @@
    /// </summary>
    public static class DbUtils
    {
        /// <summary>
        /// 获取树形数据路径
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="parentId"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public static async Task<string> GetTreeDataPath<TEntity>(
             Guid? parentId,
            CancellationToken cancellationToken = default)
            where TEntity : CommonEntity, ITreeData<TEntity>, new()
        {
            var rep = Db.GetRepository<TEntity>();
            if (parentId.HasValue)
            {
                var parent = await rep.AsQueryable().AsNoTracking()
                    .Where(it => it.Id == parentId)
                    .Select(it => new
                    {
                        it.Path,
                        it.Code
                    })
                    .FirstOrDefaultAsync(cancellationToken);
                var summary = typeof(TEntity).GetSummary();
                if (parent == null) throw Oops.Oh(EnumErrorCodeType.s404, $"上级{summary}");
                return $"{parent.Path}{parent.Code}/";
            }
            else
            {
                return "/";
            }
        }
        /// <summary>
        /// 更新树形数据下级路径
        /// </summary>
        /// <param name="oldPath"></param>
        /// <param name="newPath"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public static async Task UpdateTreeDataChildrenPath<TEntity>(
            string oldPath,
            string newPath,
            CancellationToken cancellationToken)
            where TEntity : CommonEntity, ITreeData<TEntity>, new()
        {
            var rep = Db.GetRepository<TEntity>();
            var models = await rep.AsQueryable()
                .Where(it => it.Path.StartsWith(oldPath))
                .ToListAsync(cancellationToken);
            if (models.IsNotNull())
            {
                foreach (var model in models)
                {
                    model.Path = model.Path.Replace(oldPath, newPath);
                }
                await rep.UpdateAsync(models);
            }
        }
        /// <summary>
        /// 查询选择器数据
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        /// <typeparam name="TData"></typeparam>
        /// <param name="request"></param>
        /// <param name="getValue"></param>
        /// <param name="getLabel"></param>
        /// <param name="query"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public static async Task<List<SelectQueryResultOption<TValue, TData>>> GetSelect<TEntity, TValue, TData>(
            this SelectQuery<TValue, TData> request,
            Func<TData, TValue> getValue,
            Func<TData, string> getLabel,
            Func<IQueryable<TEntity>, IQueryable<TEntity>> query = null,
            CancellationToken cancellationToken = default)
            where TEntity : CommonEntity, new()
        {
            var rep = Db.GetRepository<TEntity>();
            var q = rep.AsQueryable().AsNoTracking();
            if (query != null) q = query(q);
            else q = q.OrderBy(it => it.Sort).ThenBy(it => it.CreatedTime);
            var models = await q
                .ProjectToType<TData>()
                .ToListAsync(cancellationToken);
            var options = new List<SelectQueryResultOption<TValue, TData>>();
            foreach (var model in models)
            {
                var option = new SelectQueryResultOption<TValue, TData>();
                option.Data = model;
                option.Value = getValue(model);
                option.Label = getLabel(model);
                options.Add(option);
            }
            return options;
        }
        /// <summary>
        /// 查询分页列表数据
        /// </summary>
@@ -34,7 +134,7 @@
        /// <param name="selector"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public static async Task<PagedListQueryResult<TItem>> ToPagedListAsync<TEntity, TItem>(
        public static async Task<PagedListQueryResult<TItem>> GetPagedListAsync<TEntity, TItem>(
            this PagedListQueryPageModel page, 
            Func<IQueryable<TEntity>, IQueryable<TEntity>> query = null,
            Expression<Func<TEntity, TItem>> selector = null,
@@ -45,6 +145,7 @@
            var rep = Db.GetRepository<TEntity>();
            var q = rep.AsQueryable().AsNoTracking();
            if (query != null) q = query(q);
            else q = q.OrderBy(it => it.Sort).ThenBy(it => it.CreatedTime);
            q = q.OrderBy(page.OrderInput);
            var s = selector == null
                ? q.ProjectToType<TItem>()
@@ -65,7 +166,9 @@
        /// <param name="q"></param>
        /// <param name="orders"></param>
        /// <returns></returns>
        public static IQueryable<T> OrderBy<T>(this IQueryable<T> q, List<PagedListQueryPageModelOrderInput> orders)
        public static IQueryable<T> OrderBy<T>(
            this IQueryable<T> q,
            List<PagedListQueryPageModelOrderInput> orders)
        {
            if (orders.IsNull()) return q;
@@ -126,7 +229,10 @@
        /// <param name="query"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public static async Task<int> SetIsDisable<TEntity>(this SetIsDisabledCommand request, Func<IQueryable<TEntity>, IQueryable<TEntity>> query = null, CancellationToken cancellationToken = default)
        public static async Task<int> SetIsDisabled<TEntity>(
            this SetIsDisabledCommand request,
            Func<IQueryable<TEntity>, IQueryable<TEntity>> query = null,
            CancellationToken cancellationToken = default)
            where TEntity : CommonEntity, IIsDisabled, new()
        {
            var rep = Db.GetRepository<TEntity>();
@@ -150,7 +256,10 @@
        /// <param name="query"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public static async Task<int> DeleteData<TEntity>(this DeleteDataCommand request, Func<IQueryable<TEntity>, IQueryable<TEntity>> query = null, CancellationToken cancellationToken = default)
        public static async Task<int> DeleteData<TEntity>(
            this DeleteDataCommand request, Func<IQueryable<TEntity>,
            IQueryable<TEntity>> query = null,
            CancellationToken cancellationToken = default)
            where TEntity : CommonEntity, new()
        {
            var rep = Db.GetRepository<TEntity>();
@@ -171,29 +280,36 @@
        /// <typeparam name="TRequest"></typeparam>
        /// <param name="request"></param>
        /// <param name="checkExist"></param>
        /// <param name="update"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public static async Task<Guid> SaveData<TEntity, TRequest>(this TRequest request, Func<IQueryable<TEntity>, TEntity, TRequest, bool> checkExist = null, CancellationToken cancellationToken = default)
        public static async Task<Guid> SaveData<TEntity, TRequest>(
            this TRequest request,
            Expression<Func<TEntity, bool>> checkExist = null,
            Action<TEntity> update = null,
            CancellationToken cancellationToken = default)
            where TEntity : CommonEntity, new()
            where TRequest : SaveDataCommand, new()
        {
            var xmlDoc = await XmlDocUtils.GetXmlDocAsync();
            var summary = await typeof(TEntity).GetSummary(xmlDoc);
            var rep = Db.GetRepository<TEntity>();
            if (checkExist != null && await rep.AsQueryable().AsNoTracking().AnyAsync(checkExist))
                throw Oops.Oh(EnumErrorCodeType.s405, $"该{summary ?? "信息"}");
            if (request.Id.HasValue)
            {
                var entity = await rep.AsQueryable().FirstOrDefaultAsync(it => it.Id == request.Id, cancellationToken);
                if (entity == null) throw Oops.Oh(EnumErrorCodeType.s404, $"该{summary ?? "信息"}");
                if (checkExist != null && checkExist(rep.AsQueryable().AsNoTracking(), entity, request)) throw Oops.Oh(EnumErrorCodeType.s405, $"该{summary ?? "信息"}");
                request.Adapt(entity);
                if (update != null) update(entity);
                else request.Adapt(entity);
                await rep.UpdateAsync(entity);
                return entity.Id;
            }
            else
            {
                var entity = new TEntity();
                if (checkExist != null && checkExist(rep.AsQueryable().AsNoTracking(), entity, request)) throw Oops.Oh(EnumErrorCodeType.s405, $"该{summary ?? "信息"}");
                request.Adapt(entity);
                if (update != null) update(entity);
                else request.Adapt(entity);
                await rep.InsertAsync(entity);
                return entity.Id;
            }
FlexJobApi.Core/Utils/DbUtils/SelectQuery.cs
@@ -11,7 +11,8 @@
    /// 选择器查询
    /// </summary>
    /// <typeparam name="TValue"></typeparam>
    public abstract class SelectQuery<TValue> : IRequest<List<SelectQueryResultItem<TValue>>>
    /// <typeparam name="TData"></typeparam>
    public abstract class SelectQuery<TValue, TData> : IRequest<List<SelectQueryResultOption<TValue, TData>>>
    {
    }
@@ -19,7 +20,7 @@
    /// <summary>
    /// 选择器查询-结果-项
    /// </summary>
    public class SelectQueryResultItem<TValue>
    public class SelectQueryResultOption<TValue, TData>
    {
        /// <summary>
        /// 值
FlexJobApi.User.Application/Auths/Queries/GetCurrentLogierMenusQueryHandler.cs
@@ -34,7 +34,7 @@
            var all = await (from m in repMenu.AsQueryable().AsNoTracking()
                             join rm in repRoleMenu.AsQueryable().AsNoTracking() on m.Id equals rm.MenuId
                             join ur in repUserInfoRole.AsQueryable().AsNoTracking() on rm.RoleId equals ur.RoleId
                             where ur.UserInfoId == logier.UserInfoId
                             where ur.UserInfoId == logier.UserInfoId && !m.IsDisabled
                             && (m.Type == EnumMenuType.Menu || m.Type == EnumMenuType.Page || m.Type == EnumMenuType.Modal)
                             select m).ProjectToType<GetMenusQueryResultItem>().ToListAsync();
            var models = all.Where(it => it.ParentId == null).ToList();
FlexJobApi.User.Application/FlexJobApi.User.Application.xml
@@ -77,13 +77,6 @@
        <member name="M:FlexJobApi.User.Application.SaveMenuButtonCommandHandler.Handle(FlexJobApi.Core.SaveMenuButtonCommand,System.Threading.CancellationToken)">
            <inheritdoc/>
        </member>
        <member name="M:FlexJobApi.User.Application.SaveMenuButtonCommandHandler.CheckExist(FlexJobApi.Core.Menu)">
            <summary>
            校验菜单是否重复
            </summary>
            <param name="entity"></param>
            <returns></returns>
        </member>
        <member name="T:FlexJobApi.User.Application.SaveMenuCommandHandler">
            <summary>
            保存菜单
@@ -118,33 +111,6 @@
            <param name="request"></param>
            <returns></returns>
        </member>
        <member name="M:FlexJobApi.User.Application.SaveMenuCommandHandler.CheckExist(FlexJobApi.Core.Menu,System.Nullable{System.Guid},System.String)">
            <summary>
            校验菜单是否重复
            </summary>
            <param name="entity"></param>
            <param name="parentId"></param>
            <param name="code"></param>
            <returns></returns>
        </member>
        <member name="M:FlexJobApi.User.Application.SaveMenuCommandHandler.GetPathAsync(System.Nullable{System.Guid},System.String,System.Threading.CancellationToken)">
            <summary>
            获取地址
            </summary>
            <param name="parentId"></param>
            <param name="code"></param>
            <param name="cancellationToken"></param>
            <returns></returns>
        </member>
        <member name="M:FlexJobApi.User.Application.SaveMenuCommandHandler.UpdateChildrensPath(System.String,System.String,System.Threading.CancellationToken)">
            <summary>
            更新下级菜单路径
            </summary>
            <param name="oldPath"></param>
            <param name="newPath"></param>
            <param name="cancellationToken"></param>
            <returns></returns>
        </member>
        <member name="T:FlexJobApi.User.Application.SaveMenuFieldCommandHandler">
            <summary>
            
@@ -157,13 +123,6 @@
        </member>
        <member name="M:FlexJobApi.User.Application.SaveMenuFieldCommandHandler.Handle(FlexJobApi.Core.SaveMenuFieldCommand,System.Threading.CancellationToken)">
            <inheritdoc/>
        </member>
        <member name="M:FlexJobApi.User.Application.SaveMenuFieldCommandHandler.CheckExist(FlexJobApi.Core.Menu)">
            <summary>
            校验菜单是否重复
            </summary>
            <param name="entity"></param>
            <returns></returns>
        </member>
        <member name="T:FlexJobApi.User.Application.SetMenuSwitchCommandHandler">
            <summary>
@@ -271,7 +230,7 @@
            设置角色是否禁用
            </summary>
        </member>
        <member name="M:FlexJobApi.User.Application.SetRoleIsDisabledCommandHandler.#ctor(Furion.DatabaseAccessor.IRepository{FlexJobApi.Core.Role})">
        <member name="M:FlexJobApi.User.Application.SetRoleIsDisabledCommandHandler.#ctor">
            <summary>
            设置角色是否禁用
            </summary>
FlexJobApi.User.Application/Menus/Commands/SaveMenuButtonCommandHandler.cs
@@ -26,44 +26,29 @@
        {
            var parent = await rep.FirstOrDefaultAsync(it => it.Id == request.ParentId);
            if (parent == null) throw Oops.Oh(EnumErrorCodeType.s404, "上级菜单");
            return await request.SaveData<Menu, SaveMenuButtonCommand>(
                it =>
                    it.ParentId == request.ParentId
                    && it.Type == EnumMenuType.Button
                    && it.Group == request.Group
                    && it.Location == request.Location
                    && it.Code == request.Code
                    && it.Id != request.Id,
                (entity) =>
                {
            if (request.Id.HasValue)
            {
                var entity = await rep.FirstOrDefaultAsync(it => it.Id == request.Id);
                if (entity == null) throw Oops.Oh(EnumErrorCodeType.s404, "该菜单");
                if (entity.ParentId != request.ParentId) throw Oops.Oh(EnumErrorCodeType.s410, "上级Id");
                request.Adapt(entity);
                if (await CheckExist(entity)) throw Oops.Oh(EnumErrorCodeType.s406, "菜单编号");
                await rep.UpdateAsync(entity);
                return entity.Id;
            }
            else
            {
                var entity = new Menu();
                entity.Path = $"{parent.Path}{parent.Code}/";
                entity.Type = EnumMenuType.Button;
                entity.VisitLevel = parent.VisitLevel;
                    }
                request.Adapt(entity);
                if (await CheckExist(entity)) throw Oops.Oh(EnumErrorCodeType.s406, "菜单编号");
                await rep.InsertAsync(entity);
                return entity.Id;
            }
        }
        /// <summary>
        /// 校验菜单是否重复
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        private async Task<bool> CheckExist(Menu entity)
        {
            return await rep.AsQueryable().AsNoTracking()
                .AnyAsync(it =>
                    it.ParentId == entity.ParentId
                    && it.Type == entity.Type
                    && it.Group == entity.Group
                    && it.Location == entity.Location
                    && it.Code == entity.Code
                    && it.Id != entity.Id);
                },
                cancellationToken);
        }
    }
}
FlexJobApi.User.Application/Menus/Commands/SaveMenuCommandHandler.cs
@@ -1,4 +1,5 @@
using FlexJobApi.Core;
using Consul;
using FlexJobApi.Core;
using Furion.DatabaseAccessor;
using Furion.DataValidation;
using Furion.FriendlyException;
@@ -29,35 +30,29 @@
        /// <inheritdoc/>
        public async Task<Guid> Handle(SaveMenuCommand request, CancellationToken cancellationToken)
        {
            return await request.SaveData<Menu, SaveMenuCommand>(
                it =>
                    it.UserType == request.UserType
                    && it.ClientType == request.ClientType
                    && it.ParentId == request.ParentId
                    && it.Code == request.Code
                    && it.Id != request.Id,
                (entity) =>
                {
                    entity.Path = DbUtils.GetTreeDataPath<DictionaryData>(request.ParentId, cancellationToken).Result;
            if (request.Id.HasValue)
            {
                var entity = await rep.AsQueryable()
                    .Include(it => it.Children)
                    .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);
                        DbUtils.UpdateTreeDataChildrenPath<DictionaryData>(
                           $"{entity.Path}/{entity.Code}/",
                           $"{entity.Path}/{request.Code}/",
                           cancellationToken).Wait();
                    }
                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;
            }
                },
                cancellationToken);
        }
        /// <summary>
@@ -172,75 +167,6 @@
                    g.Fields.Where(f => f.Id.HasValue).Select(f => f.Id!.Value)))
                .SelectMany(it => it)
                .ToList();
        }
        /// <summary>
        /// 校验菜单是否重复
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="parentId"></param>
        /// <param name="code"></param>
        /// <returns></returns>
        private async Task<bool> 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);
        }
        /// <summary>
        /// 获取地址
        /// </summary>
        /// <param name="parentId"></param>
        /// <param name="code"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        private async Task<string> 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 "/";
            }
        }
        /// <summary>
        /// 更新下级菜单路径
        /// </summary>
        /// <param name="oldPath"></param>
        /// <param name="newPath"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        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);
            }
        }
    }
}
FlexJobApi.User.Application/Menus/Commands/SaveMenuFieldCommandHandler.cs
@@ -26,44 +26,28 @@
        {
            var parent = await rep.FirstOrDefaultAsync(it => it.Id == request.ParentId);
            if (parent == null) throw Oops.Oh(EnumErrorCodeType.s404, "上级菜单");
            return await request.SaveData<Menu, SaveMenuFieldCommand>(
                it =>
                    it.ParentId == request.ParentId
                    && it.Type == EnumMenuType.Field
                    && it.Group == request.Group
                    && it.Code == request.Code
                    && it.Id != request.Id,
                (entity) =>
                {
            if (request.Id.HasValue)
            {
                var entity = await rep.FirstOrDefaultAsync(it => it.Id == request.Id);
                if (entity == null) throw Oops.Oh(EnumErrorCodeType.s404, "该菜单");
                if (entity.ParentId != request.ParentId) throw Oops.Oh(EnumErrorCodeType.s410, "上级Id");
                request.Adapt(entity);
                if (await CheckExist(entity)) throw Oops.Oh(EnumErrorCodeType.s406, "菜单编号");
                await rep.UpdateAsync(entity);
                return entity.Id;
            }
            else
            {
                var entity = new Menu();
                entity.Path = $"{parent.Path}{parent.Code}/";
                entity.Type = EnumMenuType.Field;
                entity.VisitLevel = parent.VisitLevel;
                    }
                request.Adapt(entity);
                if (await CheckExist(entity)) throw Oops.Oh(EnumErrorCodeType.s406, "菜单编号");
                await rep.InsertAsync(entity);
                return entity.Id;
                },
                cancellationToken);
            }
        }
        /// <summary>
        /// 校验菜单是否重复
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        private async Task<bool> CheckExist(Menu entity)
        {
            return await rep.AsQueryable().AsNoTracking()
                .AnyAsync(it =>
                    it.ParentId == entity.ParentId
                    && it.Type == entity.Type
                    && it.Group == entity.Group
                    && it.Code == entity.Code
                    && it.Id != entity.Id);
        }
    }
}
FlexJobApi.User.Application/Roles/Commands/SetRoleIsDisabledCommandHandler.cs
@@ -14,23 +14,12 @@
    /// 设置角色是否禁用
    /// </summary>
    [Resource([EnumResourceController.Role])]
    public class SetRoleIsDisabledCommandHandler(
            IRepository<Role> rep
        ) : IRequestHandler<SetRoleIsDisabledCommand, int>
    public class SetRoleIsDisabledCommandHandler() : IRequestHandler<SetRoleIsDisabledCommand, int>
    {
        private readonly IRepository<Role> rep = rep;
        /// <inheritdoc/>
        public async Task<int> Handle(SetRoleIsDisabledCommand request, CancellationToken cancellationToken)
        public Task<int> Handle(SetRoleIsDisabledCommand request, CancellationToken cancellationToken)
        {
            var entities = await rep.AsQueryable()
                .Where(it => request.Ids.Contains(it.Id) && it.IsDisabled != request.IsDisabled)
                .ToListAsync();
            foreach (var entity in entities)
            {
                entity.IsDisabled = request.IsDisabled;
            }
            return entities.Count;
            return request.SetIsDisabled<Role>(cancellationToken: cancellationToken);
        }
    }
}
FlexJobApi.User.Application/Roles/Queries/GetRolesQueryHandler.cs
@@ -27,7 +27,7 @@
        /// <inheritdoc/>
        public async Task<PagedListQueryResult<GetRolesQueryResultItem>> Handle(GetRolesQuery request, CancellationToken cancellationToken)
        {
            var result = await request.PageModel.ToPagedListAsync<Role, GetRolesQueryResultItem>(
            var result = await request.PageModel.GetPagedListAsync<Role, GetRolesQueryResultItem>(
                q =>
                {
                    q = q.OrderBy(it => it.Sort).ThenBy(it => it.CreatedTime);
FlexJobApi.User.Application/UserInfos/Queries/GetOperationUserInfosQueryHandler.cs
@@ -24,7 +24,7 @@
        /// <inheritdoc/>
        public async Task<PagedListQueryResult<GetOperationUserInfosQueryResultItem>> Handle(GetOperationUserInfosQuery request, CancellationToken cancellationToken)
        {
            var result = await request.PageModel.ToPagedListAsync<UserInfo, GetOperationUserInfosQueryResultItem>(
            var result = await request.PageModel.GetPagedListAsync<UserInfo, GetOperationUserInfosQueryResultItem>(
                q =>
                {
                    q = q.OrderByDescending(it => it.Level).ThenByDescending(it => it.CreatedTime)