mirror of
				https://github.com/nsnail/NetAdmin.git
				synced 2025-11-04 05:05:27 +08:00 
			
		
		
		
	feat: ✨ 前端表格高级筛选 (#100)
* chore: 🔨 css 基础单位 [skip ci] * fix: 🐛 ca2263 System.Enum.GetValues<TEnum>() [skip ci] * feat: ✨ 前端表格高级筛选 [skip ci]
This commit is contained in:
		@@ -27,6 +27,11 @@ public interface ICrudModule<in TCreateReq, TCreateRsp, TQueryReq, TQueryRsp, in
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Task<int> BulkDeleteAsync(BulkReq<TDelReq> req);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     实体计数
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Task<long> CountAsync(QueryReq<TQueryReq> req);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     创建实体
 | 
			
		||||
    /// </summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -6,10 +6,7 @@ namespace NetAdmin.Application.Repositories;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     默认仓储
 | 
			
		||||
/// </summary>
 | 
			
		||||
public sealed class DefaultRepository<TEntity>(
 | 
			
		||||
    IFreeSql          fSql       //
 | 
			
		||||
  , UnitOfWorkManager uowManger  //
 | 
			
		||||
  , ContextUserToken  userToken) //
 | 
			
		||||
public sealed class DefaultRepository<TEntity>(IFreeSql fSql, UnitOfWorkManager uowManger, ContextUserToken userToken)
 | 
			
		||||
    : DefaultRepository<TEntity, long>(fSql, uowManger)
 | 
			
		||||
    where TEntity : EntityBase
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ public static class ServiceCollectionExtensions
 | 
			
		||||
            (Startup.Args.InsertSeedData ? FreeSqlInitMethods.InsertSeedData : FreeSqlInitMethods.None), freeSql => {
 | 
			
		||||
                // 数据权限过滤器
 | 
			
		||||
                _ = freeSql.GlobalFilter.ApplyOnlyIf<IFieldOwner>( //
 | 
			
		||||
                    Chars.FLG_GLOBAL_FILTER_DATA
 | 
			
		||||
                    Chars.FLG_FREE_SQL_GLOBAL_FILTER_DATA
 | 
			
		||||
                  , () => ContextUserInfo.Create()?.Roles.All(x => x.DataScope == DataScopes.Self) ?? false
 | 
			
		||||
                  , a => a.OwnerId == ContextUserInfo.Create().Id);
 | 
			
		||||
            });
 | 
			
		||||
 
 | 
			
		||||
@@ -26,17 +26,17 @@ namespace NetAdmin.BizServer.Host
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        ///     配置应用程序中间件
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public void Configure(IApplicationBuilder app)
 | 
			
		||||
        public void Configure(IApplicationBuilder app, IHostApplicationLifetime lifeTime)
 | 
			
		||||
        {
 | 
			
		||||
            _ = app                                      //
 | 
			
		||||
                .UseRealIp()                             // 使用RealIp中间件,用于获取真实客户端IP地址
 | 
			
		||||
                .EnableBuffering()                       // 启用请求体缓冲,允许多次读取请求体
 | 
			
		||||
                .UseMiddleware<RequestAuditMiddleware>() // 使用RequestAuditMiddleware中间件,执行请求审计
 | 
			
		||||
            _ = app                                        //
 | 
			
		||||
                .UseMiddleware<SafetyShopHostMiddleware>() // 安全停机中间件
 | 
			
		||||
                .EnableBuffering()                         // 启用请求体缓冲,允许多次读取请求体
 | 
			
		||||
                .UseMiddleware<RequestAuditMiddleware>()   // 使用RequestAuditMiddleware中间件,执行请求审计
 | 
			
		||||
                #if DEBUG
 | 
			
		||||
                .UseOpenApiSkin() // 使用OpenApiSkin中间件(仅在调试模式下),提供Swagger UI皮肤
 | 
			
		||||
                #else
 | 
			
		||||
            .UseVueAdmin()    // 托管管理后台,仅在非调试模式下
 | 
			
		||||
            .UseHttpMetrics() // 使用HttpMetrics中间件,启用HTTP性能监控
 | 
			
		||||
                .UseVueAdmin()    // 托管管理后台,仅在非调试模式下
 | 
			
		||||
                .UseHttpMetrics() // 使用HttpMetrics中间件,启用HTTP性能监控
 | 
			
		||||
                #endif
 | 
			
		||||
                .UseInject(string.Empty)                   // 使用Inject中间件,Furion脚手架的依赖注入支持
 | 
			
		||||
                .UseUnifyResultStatusCodes()               // 使用UnifyResultStatusCodes中间件,用于统一处理结果状态码
 | 
			
		||||
@@ -46,6 +46,7 @@ namespace NetAdmin.BizServer.Host
 | 
			
		||||
                .UseAuthorization()                        // 使用Authorization中间件,启用授权
 | 
			
		||||
                .UseMiddleware<RemoveNullNodeMiddleware>() // 使用RemoveNullNodeMiddleware中间件,删除JSON中的空节点
 | 
			
		||||
                .UseEndpoints();                           // 配置端点以处理请求
 | 
			
		||||
            _ = lifeTime.ApplicationStopping.Register(SafetyShopHostMiddleware.OnStopping);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -19,8 +19,8 @@ namespace NetAdmin.BizServer.Tests;
 | 
			
		||||
///     所有测试
 | 
			
		||||
/// </summary>
 | 
			
		||||
[SuppressMessage("Usage", "xUnit1028:Test method must have valid return type")]
 | 
			
		||||
public class AllTests(WebApplicationFactory<Startup> factory, ITestOutputHelper testOutputHelper) :
 | 
			
		||||
    WebApiTestBase<Startup>(factory, testOutputHelper), IToolsModule, ICacheModule, IApiModule, IConfigModule
 | 
			
		||||
public class AllTests(WebApplicationFactory<Startup> factory, ITestOutputHelper testOutputHelper)
 | 
			
		||||
    : WebApiTestBase<Startup>(factory, testOutputHelper), IToolsModule, ICacheModule, IApiModule, IConfigModule
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc cref="ICrudModule{TCreateReq,TCreateRsp,TQueryReq,TQueryRsp,TUpdateReq,TUpdateRsp,TDelReq}.BulkDeleteAsync" />
 | 
			
		||||
@@ -38,6 +38,18 @@ public class AllTests(WebApplicationFactory<Startup> factory, ITestOutputHelper
 | 
			
		||||
        return default;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QueryConfigReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        throw new NotImplementedException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QueryApiReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        throw new NotImplementedException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<QueryConfigRsp> CreateAsync(CreateConfigReq req)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -41,7 +41,12 @@ public abstract class DistributedCache<TService>(IDistributedCache cache, TServi
 | 
			
		||||
    protected async Task<T> GetAsync<T>(string key)
 | 
			
		||||
    {
 | 
			
		||||
        var cacheRead = await Cache.GetStringAsync(key).ConfigureAwait(false);
 | 
			
		||||
        return cacheRead != null ? cacheRead.ToObject<T>() : default;
 | 
			
		||||
        try {
 | 
			
		||||
            return cacheRead != null ? cacheRead.ToObject<T>() : default;
 | 
			
		||||
        }
 | 
			
		||||
        catch (JsonException) {
 | 
			
		||||
            return default;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ public sealed class ChineseNameAttribute : RegexAttribute
 | 
			
		||||
    ///     Initializes a new instance of the <see cref="ChineseNameAttribute" /> class.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public ChineseNameAttribute() //
 | 
			
		||||
        : base(Chars.RGX_CHINESE_NAME)
 | 
			
		||||
        : base(Chars.RGXL_CHINESE_NAME)
 | 
			
		||||
    {
 | 
			
		||||
        ErrorMessageResourceName = nameof(Ln.中文姓名);
 | 
			
		||||
        ErrorMessageResourceType = typeof(Ln);
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ public sealed class CronAttribute : RegexAttribute
 | 
			
		||||
    ///     Initializes a new instance of the <see cref="CronAttribute" /> class.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public CronAttribute() //
 | 
			
		||||
        : base(Chars.RGX_CRON)
 | 
			
		||||
        : base(Chars.RGXL_CRON)
 | 
			
		||||
    {
 | 
			
		||||
        ErrorMessageResourceName = nameof(Ln.时间表达式);
 | 
			
		||||
        ErrorMessageResourceType = typeof(Ln);
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ public sealed class EmailAttribute : RegexAttribute
 | 
			
		||||
    ///     Initializes a new instance of the <see cref="EmailAttribute" /> class.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public EmailAttribute() //
 | 
			
		||||
        : base(Chars.RGX_EMAIL)
 | 
			
		||||
        : base(Chars.RGXL_EMAIL)
 | 
			
		||||
    {
 | 
			
		||||
        ErrorMessageResourceName = nameof(Ln.电子邮箱);
 | 
			
		||||
        ErrorMessageResourceType = typeof(Ln);
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     Api接口表
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_Api))]
 | 
			
		||||
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_Api))]
 | 
			
		||||
public record Sys_Api : ImmutableEntity<string>, IFieldSummary
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     配置表
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_Config))]
 | 
			
		||||
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_Config))]
 | 
			
		||||
public record Sys_Config : VersionEntity, IFieldEnabled
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     部门表
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_Dept))]
 | 
			
		||||
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_Dept))]
 | 
			
		||||
public record Sys_Dept : VersionEntity, IFieldEnabled, IFieldSummary, IFieldSort
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     字典目录表
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_DicCatalog))]
 | 
			
		||||
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_DicCatalog))]
 | 
			
		||||
[Index($"idx_{{tablename}}_{nameof(Code)}", nameof(Code), true)]
 | 
			
		||||
public record Sys_DicCatalog : VersionEntity
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     字典内容表
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_DicContent))]
 | 
			
		||||
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_DicContent))]
 | 
			
		||||
[Index($"idx_{{tablename}}_{nameof(CatalogId)}_{nameof(Key)}",   $"{nameof(CatalogId)},{nameof(Key)}",   true)]
 | 
			
		||||
[Index($"idx_{{tablename}}_{nameof(CatalogId)}_{nameof(Value)}", $"{nameof(CatalogId)},{nameof(Value)}", true)]
 | 
			
		||||
public record Sys_DicContent : VersionEntity
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     计划作业表
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_Job))]
 | 
			
		||||
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_Job))]
 | 
			
		||||
public record Sys_Job : VersionEntity, IFieldEnabled, IFieldSummary
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc cref="IFieldEnabled.Enabled" />
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     计划作业执行记录表
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_JobRecord))]
 | 
			
		||||
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_JobRecord))]
 | 
			
		||||
[Index($"idx_{{tablename}}_{nameof(JobId)}_{nameof(TimeId)}", $"{nameof(JobId)},{nameof(TimeId)}", true)]
 | 
			
		||||
public record Sys_JobRecord : LiteImmutableEntity
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     菜单表
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_Menu))]
 | 
			
		||||
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_Menu))]
 | 
			
		||||
[Index($"idx_{{tablename}}_{nameof(Name)}", nameof(Name), true)]
 | 
			
		||||
public record Sys_Menu : VersionEntity, IFieldSort
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,9 @@ namespace NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     请求日志表
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_RequestLog))]
 | 
			
		||||
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_RequestLog))]
 | 
			
		||||
[Index($"idx_{{tablename}}_{nameof(ApiId)}",       nameof(ApiId),       false)]
 | 
			
		||||
[Index($"idx_{{tablename}}_{nameof(CreatedTime)}", nameof(CreatedTime), false)]
 | 
			
		||||
public record Sys_RequestLog : ImmutableEntity, IFieldCreatedClient
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     角色表
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_Role))]
 | 
			
		||||
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_Role))]
 | 
			
		||||
[Index("idx_{tablename}_01", nameof(Name), true)]
 | 
			
		||||
public record Sys_Role : VersionEntity, IFieldSort, IFieldEnabled, IFieldSummary, IRegister
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     角色-接口映射表
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_RoleApi))]
 | 
			
		||||
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_RoleApi))]
 | 
			
		||||
public record Sys_RoleApi : ImmutableEntity
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     角色-部门映射表
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_RoleDept))]
 | 
			
		||||
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_RoleDept))]
 | 
			
		||||
[Index($"idx_{{tablename}}_{nameof(RoleId)}_{nameof(DeptId)}", $"{nameof(RoleId)},{nameof(DeptId)}", true)]
 | 
			
		||||
public record Sys_RoleDept : ImmutableEntity
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     角色-菜单映射表
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_RoleMenu))]
 | 
			
		||||
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_RoleMenu))]
 | 
			
		||||
[Index($"idx_{{tablename}}_{nameof(RoleId)}_{nameof(MenuId)}", $"{nameof(RoleId)},{nameof(MenuId)}", true)]
 | 
			
		||||
public record Sys_RoleMenu : ImmutableEntity
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     站内信表
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_SiteMsg))]
 | 
			
		||||
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_SiteMsg))]
 | 
			
		||||
public record Sys_SiteMsg : VersionEntity, IRegister, IFieldSummary
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     站内信-部门映射表
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_SiteMsgDept))]
 | 
			
		||||
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_SiteMsgDept))]
 | 
			
		||||
[Index($"idx_{{tablename}}_{nameof(DeptId)}_{nameof(SiteMsgId)}", $"{nameof(DeptId)},{nameof(SiteMsgId)}", true)]
 | 
			
		||||
public record Sys_SiteMsgDept : ImmutableEntity
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     站内信标记表
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_SiteMsgFlag))]
 | 
			
		||||
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_SiteMsgFlag))]
 | 
			
		||||
[Index($"idx_{{tablename}}_{nameof(SiteMsgId)}_{nameof(UserId)}", $"{nameof(SiteMsgId)},{nameof(UserId)}", true)]
 | 
			
		||||
public record Sys_SiteMsgFlag : MutableEntity
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     站内信-角色映射表
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_SiteMsgRole))]
 | 
			
		||||
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_SiteMsgRole))]
 | 
			
		||||
[Index($"idx_{{tablename}}_{nameof(RoleId)}_{nameof(SiteMsgId)}", $"{nameof(RoleId)},{nameof(SiteMsgId)}", true)]
 | 
			
		||||
public record Sys_SiteMsgRole : ImmutableEntity
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     站内信-用户映射表
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_SiteMsgUser))]
 | 
			
		||||
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_SiteMsgUser))]
 | 
			
		||||
[Index($"idx_{{tablename}}_{nameof(UserId)}_{nameof(SiteMsgId)}", $"{nameof(UserId)},{nameof(SiteMsgId)}", true)]
 | 
			
		||||
public record Sys_SiteMsgUser : ImmutableEntity
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     用户基本信息表
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_User))]
 | 
			
		||||
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_User))]
 | 
			
		||||
[Index($"idx_{{tablename}}_{nameof(UserName)}", nameof(UserName), true)]
 | 
			
		||||
[Index($"idx_{{tablename}}_{nameof(Mobile)}",   nameof(Mobile),   true)]
 | 
			
		||||
[Index($"idx_{{tablename}}_{nameof(Email)}",    nameof(Email),    true)]
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     用户档案表
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_UserProfile))]
 | 
			
		||||
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_UserProfile))]
 | 
			
		||||
public record Sys_UserProfile : VersionEntity, IRegister
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     用户-角色映射表
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_UserRole))]
 | 
			
		||||
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_UserRole))]
 | 
			
		||||
public record Sys_UserRole : VersionEntity
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     验证码表
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_VerifyCode))]
 | 
			
		||||
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_VerifyCode))]
 | 
			
		||||
public record Sys_VerifyCode : VersionEntity
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -5,5 +5,5 @@ namespace NetAdmin.Domain.DbMaps.Tpl;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     示例表
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Tpl_Example))]
 | 
			
		||||
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Tpl_Example))]
 | 
			
		||||
public record Tpl_Example : VersionEntity;
 | 
			
		||||
@@ -11,6 +11,6 @@ public sealed record BulkReq<T> : DataAbstraction
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.请求对象不能为空))]
 | 
			
		||||
    [MinLength(1)]
 | 
			
		||||
    [MaxLength(Numbers.BULK_REQ_LIMIT)]
 | 
			
		||||
    [MaxLength(Numbers.MAX_LIMIT_BULK_REQ)]
 | 
			
		||||
    public IEnumerable<T> Items { get; init; }
 | 
			
		||||
}
 | 
			
		||||
@@ -7,10 +7,10 @@ public sealed record PagedQueryReq<T> : QueryReq<T>, IPagedInfo
 | 
			
		||||
    where T : DataAbstraction, new()
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc cref="IPagedInfo.Page" />
 | 
			
		||||
    [Range(1, Numbers.QUERY_MAX_PAGE_NO)]
 | 
			
		||||
    [Range(1, Numbers.MAX_LIMIT_QUERY_PAGE_NO)]
 | 
			
		||||
    public int Page { get; init; } = 1;
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="IPagedInfo.PageSize" />
 | 
			
		||||
    [Range(1, Numbers.QUERY_MAX_PAGE_SIZE)]
 | 
			
		||||
    public int PageSize { get; init; } = Numbers.QUERY_DEF_PAGE_SIZE;
 | 
			
		||||
    [Range(1, Numbers.MAX_LIMIT_QUERY_PAGE_SIZE)]
 | 
			
		||||
    public int PageSize { get; init; } = Numbers.DEF_PAGE_SIZE_QUERY;
 | 
			
		||||
}
 | 
			
		||||
@@ -9,8 +9,8 @@ public record QueryReq<T> : DataAbstraction
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     取前n条
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Range(1, Numbers.QUERY_LIMIT)]
 | 
			
		||||
    public int Count { get; init; } = Numbers.QUERY_LIMIT;
 | 
			
		||||
    [Range(1, Numbers.MAX_LIMIT_QUERY)]
 | 
			
		||||
    public int Count { get; init; } = Numbers.MAX_LIMIT_QUERY;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     动态查询条件
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,16 @@ public sealed record GetAllEntriesRsp : DataAbstraction
 | 
			
		||||
        Data   = data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     绝对过期时间
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public DateTime? AbsExpTime => AbsExp == -1 ? null : DateTime.FromBinary(AbsExp).ToLocalTime();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     滑动过期时间
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public DateTime? SldExpTime => SldExp == -1 ? null : DateTime.FromBinary(SldExp).ToLocalTime();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     绝对过期时间
 | 
			
		||||
    /// </summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,10 @@ namespace NetAdmin.Domain.Dto.Sys.Config;
 | 
			
		||||
/// </summary>
 | 
			
		||||
public sealed record QueryConfigRsp : Sys_Config
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc cref="IFieldCreatedTime.CreatedTime" />
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
 | 
			
		||||
    public override DateTime CreatedTime { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="IFieldEnabled.Enabled" />
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
 | 
			
		||||
    public override bool Enabled { get; init; }
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@ public record CreateSiteMsgReq : Sys_SiteMsg
 | 
			
		||||
    ///     部门编号列表
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [MinLength(1)]
 | 
			
		||||
    [MaxLength(Numbers.BULK_REQ_LIMIT)]
 | 
			
		||||
    [MaxLength(Numbers.MAX_LIMIT_BULK_REQ)]
 | 
			
		||||
    public IReadOnlyCollection<long> DeptIds { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Sys_SiteMsg.MsgType" />
 | 
			
		||||
@@ -30,7 +30,7 @@ public record CreateSiteMsgReq : Sys_SiteMsg
 | 
			
		||||
    ///     角色编号列表
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [MinLength(1)]
 | 
			
		||||
    [MaxLength(Numbers.BULK_REQ_LIMIT)]
 | 
			
		||||
    [MaxLength(Numbers.MAX_LIMIT_BULK_REQ)]
 | 
			
		||||
    public IReadOnlyCollection<long> RoleIds { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Sys_SiteMsg.Title" />
 | 
			
		||||
@@ -42,6 +42,6 @@ public record CreateSiteMsgReq : Sys_SiteMsg
 | 
			
		||||
    ///     用户编号列表
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [MinLength(1)]
 | 
			
		||||
    [MaxLength(Numbers.BULK_REQ_LIMIT)]
 | 
			
		||||
    [MaxLength(Numbers.MAX_LIMIT_BULK_REQ)]
 | 
			
		||||
    public IReadOnlyCollection<long> UserIds { get; init; }
 | 
			
		||||
}
 | 
			
		||||
@@ -21,6 +21,10 @@ public sealed record QuerySiteMsgRsp : Sys_SiteMsg
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
 | 
			
		||||
    public override DateTime CreatedTime { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="IFieldCreatedUser.CreatedUserName" />
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
 | 
			
		||||
    public override string CreatedUserName { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Sys_SiteMsg.Depts" />
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
 | 
			
		||||
    public new IEnumerable<QueryDeptRsp> Depts { get; init; }
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@ public abstract record CreateUpdateUserReq : Sys_User
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.角色编号列表不能为空))]
 | 
			
		||||
    [MinLength(1)]
 | 
			
		||||
    [MaxLength(Numbers.BULK_REQ_LIMIT)]
 | 
			
		||||
    [MaxLength(Numbers.MAX_LIMIT_BULK_REQ)]
 | 
			
		||||
    public IReadOnlyCollection<long> RoleIds { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Sys_User.Summary" />
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@ public sealed record LoginRsp : DataAbstraction
 | 
			
		||||
    public void SetToRspHeader()
 | 
			
		||||
    {
 | 
			
		||||
        // 设置响应报文头
 | 
			
		||||
        App.HttpContext.Response.Headers[Chars.FLG_ACCESS_TOKEN]   = AccessToken;
 | 
			
		||||
        App.HttpContext.Response.Headers[Chars.FLG_X_ACCESS_TOKEN] = RefreshToken;
 | 
			
		||||
        App.HttpContext.Response.Headers[Chars.FLG_HTTP_HEADER_VALUE_ACCESS_TOKEN] = AccessToken;
 | 
			
		||||
        App.HttpContext.Response.Headers[Chars.FLG_HTTP_HEADER_KEY_X_ACCESS_TOKEN] = RefreshToken;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -24,6 +24,7 @@ public sealed record SqlCommandAfterEvent : SqlCommandBeforeEvent
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public override string ToString()
 | 
			
		||||
    {
 | 
			
		||||
        return string.Format(CultureInfo.InvariantCulture, "SQL-{0}: {2} ms {1}", Id, Sql, ElapsedMicroseconds / 1000);
 | 
			
		||||
        return string.Format(CultureInfo.InvariantCulture, "SQL-{0}: {2} ms {1}", Id
 | 
			
		||||
                           , Sql?.Sub(0, Numbers.MAX_LIMIT_PRINT_LEN_SQL), ElapsedMicroseconds / 1000);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -40,9 +40,9 @@ public abstract class WorkBase<TLogger>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    protected Task<IRedLock> GetLockerAsync(string lockId)
 | 
			
		||||
    {
 | 
			
		||||
        return _redLocker.RedLockFactory.CreateLockAsync(lockId, TimeSpan.FromSeconds(Numbers.RED_LOCK_EXPIRY_TIME_SECS)
 | 
			
		||||
                                                       , TimeSpan.FromSeconds(Numbers.RED_LOCK_WAIT_TIME_SECS)
 | 
			
		||||
                                                       , TimeSpan.FromSeconds(Numbers.RED_LOCK_RETRY_TIME_SECS));
 | 
			
		||||
        return _redLocker.RedLockFactory.CreateLockAsync(lockId, TimeSpan.FromSeconds(Numbers.SECS_RED_LOCK_EXPIRY)
 | 
			
		||||
                                                       , TimeSpan.FromSeconds(Numbers.SECS_RED_LOCK_WAIT)
 | 
			
		||||
                                                       , TimeSpan.FromSeconds(Numbers.SECS_RED_LOCK_RETRY));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ namespace NetAdmin.Host.Controllers;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     控制器基类
 | 
			
		||||
/// </summary>
 | 
			
		||||
public abstract class ControllerBase<TCache, TService>(TCache cache) : IDynamicApiController
 | 
			
		||||
public abstract class ControllerBase<TCache, TService>(TCache cache = default) : IDynamicApiController
 | 
			
		||||
    where TCache : ICache<IDistributedCache, TService> //
 | 
			
		||||
    where TService : IService
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -1,24 +0,0 @@
 | 
			
		||||
using NetAdmin.Application.Services;
 | 
			
		||||
using NetAdmin.Cache;
 | 
			
		||||
 | 
			
		||||
namespace NetAdmin.Host.Controllers;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     健康控制器
 | 
			
		||||
/// </summary>
 | 
			
		||||
[ApiDescriptionSettings("Health")]
 | 
			
		||||
public sealed class HealthController(ICache<IDistributedCache, IService> cache)
 | 
			
		||||
    : ControllerBase<ICache<IDistributedCache, IService>, IService>(cache)
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     健康检查
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [AllowAnonymous]
 | 
			
		||||
    [HttpGet]
 | 
			
		||||
    #pragma warning disable CA1822, S3400
 | 
			
		||||
    public string Check()
 | 
			
		||||
        #pragma warning restore S3400, CA1822
 | 
			
		||||
    {
 | 
			
		||||
        return GlobalStatic.ProductVersion;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								src/backend/NetAdmin.Host/Controllers/ProbeController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/backend/NetAdmin.Host/Controllers/ProbeController.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
using NetAdmin.Application.Services;
 | 
			
		||||
using NetAdmin.Cache;
 | 
			
		||||
using NetAdmin.Host.Middlewares;
 | 
			
		||||
 | 
			
		||||
namespace NetAdmin.Host.Controllers;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     探针组件
 | 
			
		||||
/// </summary>
 | 
			
		||||
[ApiDescriptionSettings("Probe")]
 | 
			
		||||
public sealed class ProbeController : ControllerBase<ICache<IDistributedCache, IService>, IService>
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     健康检查
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [AllowAnonymous]
 | 
			
		||||
    [HttpGet]
 | 
			
		||||
    #pragma warning disable CA1822, S3400
 | 
			
		||||
    public object HealthCheck()
 | 
			
		||||
        #pragma warning restore S3400, CA1822
 | 
			
		||||
    {
 | 
			
		||||
        return new {
 | 
			
		||||
                       HostName           = Environment.MachineName
 | 
			
		||||
                     , CurrentConnections = SafetyShopHostMiddleware.Connections
 | 
			
		||||
                     , GlobalStatic.ProductVersion
 | 
			
		||||
                   };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -13,7 +13,7 @@ public static class HttpContextExtensions
 | 
			
		||||
    public static async Task RemoveJsonNodeWithNullValueAsync(this HttpContext me)
 | 
			
		||||
    {
 | 
			
		||||
        // 非json格式,退出
 | 
			
		||||
        if (!(me.Response.ContentType?.Contains(Chars.FLG_APPLICATION_JSON) ?? false)) {
 | 
			
		||||
        if (!(me.Response.ContentType?.Contains(Chars.FLG_HTTP_HEADER_VALUE_APPLICATION_JSON) ?? false)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
using Microsoft.AspNetCore.HttpOverrides;
 | 
			
		||||
#if DEBUG
 | 
			
		||||
using IGeekFan.AspNetCore.Knife4jUI;
 | 
			
		||||
 | 
			
		||||
@@ -42,16 +41,4 @@ public static class IApplicationBuilderExtensions
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    #endif
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     获取客户端真实Ip
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static IApplicationBuilder UseRealIp(this IApplicationBuilder me)
 | 
			
		||||
    {
 | 
			
		||||
        return me.UseForwardedHeaders(new ForwardedHeadersOptions //
 | 
			
		||||
                                      {
 | 
			
		||||
                                          ForwardedHeaders = ForwardedHeaders.XForwardedFor |
 | 
			
		||||
                                                             ForwardedHeaders.XForwardedProto
 | 
			
		||||
                                      });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -105,8 +105,7 @@ public static class ServiceCollectionExtensions
 | 
			
		||||
    public static IServiceCollection AddConsoleFormatter(this IServiceCollection me)
 | 
			
		||||
    {
 | 
			
		||||
        return me.AddConsoleFormatter(options => {
 | 
			
		||||
            var logLevels = Enum.GetValues<LogLevels>()
 | 
			
		||||
                                .ToDictionary(x => x, x => x.GetDisplay());
 | 
			
		||||
            var logLevels = Enum.GetValues<LogLevels>().ToDictionary(x => x, x => x.GetDisplay());
 | 
			
		||||
 | 
			
		||||
            #if DEBUG
 | 
			
		||||
            options.WriteHandler = (message, _, _, _, _) => {
 | 
			
		||||
@@ -242,11 +241,6 @@ public static class ServiceCollectionExtensions
 | 
			
		||||
      , LogMessage                              message //
 | 
			
		||||
      , Dictionary<LogLevels, DisplayAttribute> logLevels)
 | 
			
		||||
    {
 | 
			
		||||
        // 日志过长
 | 
			
		||||
        if (msg.Length > Numbers.CONSOLE_LINE_LEN_LIMIT) {
 | 
			
		||||
            msg = $"{Ln.日志长度超过限制} {Numbers.CONSOLE_LINE_LEN_LIMIT}";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        msg = _consoleColors.Aggregate( //
 | 
			
		||||
            msg, (current, regex) => regex.Key.Replace(current, regex.Value));
 | 
			
		||||
        msg = msg.ReplaceLineEndings(string.Empty);
 | 
			
		||||
 
 | 
			
		||||
@@ -23,12 +23,12 @@ public abstract class ApiResultHandler<T>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public IActionResult OnException(ExceptionContext context, ExceptionMetadata metadata)
 | 
			
		||||
    {
 | 
			
		||||
        var lineException = context.Exception switch { NetAdminException ex => ex, _ => null };
 | 
			
		||||
        var errorCode     = lineException?.Code ?? ErrorCodes.Unhandled;
 | 
			
		||||
        var naException = context.Exception switch { NetAdminException ex => ex, _ => null };
 | 
			
		||||
        var errorCode   = naException?.Code ?? ErrorCodes.Unhandled;
 | 
			
		||||
        var result = RestfulResult(errorCode, metadata.Data
 | 
			
		||||
                      ,                       lineException is NetAdminValidateException vEx
 | 
			
		||||
                      ,                       naException is NetAdminValidateException vEx
 | 
			
		||||
                                       ? vEx.ValidateResults
 | 
			
		||||
                                       : lineException?.Message ?? errorCode.ResDesc<ErrorCodes>());
 | 
			
		||||
                                       : naException?.Message ?? errorCode.ResDesc<ErrorCodes>());
 | 
			
		||||
 | 
			
		||||
        SetErrorCodeToHeader(context.HttpContext, errorCode);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,12 @@ public sealed class GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logge
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task OnExceptionAsync(ExceptionContext context)
 | 
			
		||||
    {
 | 
			
		||||
        logger.Error(context.Exception);
 | 
			
		||||
        if (context.Exception is NetAdminException and not NetAdminUnexpectedException) {
 | 
			
		||||
            logger.Warn(context.Exception);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            logger.Error(context.Exception);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 将异常设置到HttpContext.Features中 以便中间件能获取到他
 | 
			
		||||
        context.HttpContext.Features.Set<IExceptionHandlerFeature>(
 | 
			
		||||
 
 | 
			
		||||
@@ -5,9 +5,6 @@ namespace NetAdmin.Host.Middlewares;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     请求审计中间件
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <remarks>
 | 
			
		||||
///     放在所有中间件最前面
 | 
			
		||||
/// </remarks>
 | 
			
		||||
public sealed class RequestAuditMiddleware(
 | 
			
		||||
    RequestDelegate                               next
 | 
			
		||||
  , IOptions<DynamicApiControllerSettingsOptions> dynamicApiControllerSettingsOptions
 | 
			
		||||
@@ -17,7 +14,7 @@ public sealed class RequestAuditMiddleware(
 | 
			
		||||
        = new($"/{dynamicApiControllerSettingsOptions.Value.DefaultRoutePrefix}");
 | 
			
		||||
 | 
			
		||||
    private readonly PathString _healthCheckRoutePrefix
 | 
			
		||||
        = new($"/{dynamicApiControllerSettingsOptions.Value.DefaultRoutePrefix}/health/check");
 | 
			
		||||
        = new($"/{dynamicApiControllerSettingsOptions.Value.DefaultRoutePrefix}/probe/health.check");
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     主函数
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,44 @@
 | 
			
		||||
namespace NetAdmin.Host.Middlewares;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     安全停机中间件
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <remarks>
 | 
			
		||||
///     放在所有中间件最前面
 | 
			
		||||
/// </remarks>
 | 
			
		||||
public sealed class SafetyShopHostMiddleware(RequestDelegate next)
 | 
			
		||||
{
 | 
			
		||||
    private static long _connections;
 | 
			
		||||
    private static bool _hostStopping;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     当前连接数
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static long Connections => Interlocked.Read(ref _connections);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     停机处理
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static void OnStopping()
 | 
			
		||||
    {
 | 
			
		||||
        Volatile.Write(ref _hostStopping, true);
 | 
			
		||||
        while (Interlocked.Read(ref _connections) > 0) {
 | 
			
		||||
            Thread.Sleep(10);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     主函数
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public async Task InvokeAsync(HttpContext context)
 | 
			
		||||
    {
 | 
			
		||||
        if (Volatile.Read(ref _hostStopping)) {
 | 
			
		||||
            context.Response.StatusCode = (int)HttpStatusCode.ServiceUnavailable;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        _ = Interlocked.Increment(ref _connections);
 | 
			
		||||
        await next(context).ConfigureAwait(false);
 | 
			
		||||
        _ = Interlocked.Decrement(ref _connections);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -36,10 +36,12 @@ public sealed class RequestLogger(
 | 
			
		||||
                                                        _textContentTypes
 | 
			
		||||
                                                      , x => context.Request.ContentType?.Contains(
 | 
			
		||||
                                                            x, StringComparison.OrdinalIgnoreCase) ?? false)
 | 
			
		||||
                                                        ? await context.ReadBodyContentAsync().ConfigureAwait(false)
 | 
			
		||||
                                                        ? (await context.ReadBodyContentAsync().ConfigureAwait(false))
 | 
			
		||||
                                                        ?.Sub(0, Numbers.MAX_LIMIT_PRINT_LEN_CONTENT)
 | 
			
		||||
                                                        : string.Empty
 | 
			
		||||
                                                  , RequestUrl = context.Request.GetRequestUrlAddress()
 | 
			
		||||
                                                  , ResponseBody = responseBody
 | 
			
		||||
                                                  , ResponseBody
 | 
			
		||||
                                                        = responseBody?.Sub(0, Numbers.MAX_LIMIT_PRINT_LEN_CONTENT)
 | 
			
		||||
                                                  , ServerIp = context.GetLocalIpAddressToIPv4()?.IpV4ToInt32()
 | 
			
		||||
                                                  , ApiId = context.Request.Path.Value?.TrimStart('/')
 | 
			
		||||
                                                  , RequestHeaders = context.Request.Headers.Json()
 | 
			
		||||
@@ -51,7 +53,10 @@ public sealed class RequestLogger(
 | 
			
		||||
                                                  , CreatedUserId = associatedUser?.UserId
 | 
			
		||||
                                                  , CreatedUserName = associatedUser?.UserName
 | 
			
		||||
                                                  , CreatedUserAgent = context.Request.Headers.UserAgent.ToString()
 | 
			
		||||
                                                  , CreatedClientIp = context.GetRemoteIpAddressToIPv4()?.IpV4ToInt32()
 | 
			
		||||
                                                  , CreatedClientIp = context.GetRealIpAddress()
 | 
			
		||||
                                                                             ?.MapToIPv4()
 | 
			
		||||
                                                                             .ToString()
 | 
			
		||||
                                                                             .IpV4ToInt32()
 | 
			
		||||
                                                };
 | 
			
		||||
 | 
			
		||||
        // 打印日志
 | 
			
		||||
 
 | 
			
		||||
@@ -59,7 +59,7 @@ public sealed class SqlAuditor : ISingleton
 | 
			
		||||
    private static void SetCreatedClientIp(AuditValueEventArgs e)
 | 
			
		||||
    {
 | 
			
		||||
        if (e.Value is null or 0) {
 | 
			
		||||
            e.Value = App.HttpContext?.GetRemoteIpAddressToIPv4().IpV4ToInt32();
 | 
			
		||||
            e.Value = App.HttpContext?.GetRealIpAddress()?.MapToIPv4().ToString().IpV4ToInt32();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -80,7 +80,7 @@ public sealed class SqlAuditor : ISingleton
 | 
			
		||||
    private static void SetCreatedUserAgent(AuditValueEventArgs e)
 | 
			
		||||
    {
 | 
			
		||||
        if (e.Value is null or "") {
 | 
			
		||||
            e.Value = App.HttpContext?.Request.Headers[Chars.FLG_HTTP_HEADER_USER_AGENT].ToString();
 | 
			
		||||
            e.Value = App.HttpContext?.Request.Headers[Chars.FLG_HTTP_HEADER_KEY_USER_AGENT].ToString();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -14,8 +14,8 @@ public sealed record CaptchaOptions : OptionAbstraction
 | 
			
		||||
    {
 | 
			
		||||
        var rtn = new char[32];
 | 
			
		||||
        for (var i = 0; i != 32; i++) {
 | 
			
		||||
            rtn[i] = Chars.FLG_VISIBLE_ASCIIS[
 | 
			
		||||
                (int)(Math.Abs(Math.Sin(_seed * (i + 1))) * Chars.FLG_VISIBLE_ASCIIS.Length)];
 | 
			
		||||
            rtn[i] = Chars.FLGL_VISIBLE_ASCIIS[
 | 
			
		||||
                (int)(Math.Abs(Math.Sin(_seed * (i + 1))) * Chars.FLGL_VISIBLE_ASCIIS.Length)];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SecretKey = new string(rtn);
 | 
			
		||||
 
 | 
			
		||||
@@ -10,80 +10,78 @@ namespace NetAdmin.Infrastructure.Constant;
 | 
			
		||||
/// </remarks>
 | 
			
		||||
public static class Chars
 | 
			
		||||
{
 | 
			
		||||
    public const string FLG_ACCESS_TOKEN               = "ACCESS-TOKEN";
 | 
			
		||||
    public const string FLG_ACCESS_TOKEN_HEADER_KEY    = "Authorization";
 | 
			
		||||
    public const string FLG_APPLICATION_JSON           = "application/json";
 | 
			
		||||
    public const string FLG_AUTH_SCHEMA                = "Bearer";
 | 
			
		||||
    public const string FLG_CONSUL_REG_HOSTNAME        = "CONSUL_REG_HOSTNAME";
 | 
			
		||||
    public const string FLG_CONSUL_REG_PORT            = "CONSUL_REG_PORT";
 | 
			
		||||
    public const string FLG_CONTEXT_MEMBER_INFO        = nameof(FLG_CONTEXT_MEMBER_INFO);
 | 
			
		||||
    public const string FLG_CONTEXT_OWNER_DEPT_ID      = nameof(FLG_CONTEXT_OWNER_DEPT_ID);
 | 
			
		||||
    public const string FLG_CONTEXT_USER_ID            = nameof(FLG_CONTEXT_USER_ID);
 | 
			
		||||
    public const string FLG_CONTEXT_USER_INFO          = nameof(FLG_CONTEXT_USER_INFO);
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_NVARCHAR     = "nvarchar";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_NVARCHAR_255 = "nvarchar(255)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_NVARCHAR_MAX = "nvarchar(max)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_SMALL_INT    = "smallint";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_TEXT         = "text";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_VARCHAR      = "varchar";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_VARCHAR_1022 = "varchar(1022)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_VARCHAR_127  = "varchar(127)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_VARCHAR_15   = "varchar(15)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_VARCHAR_255  = "varchar(255)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_VARCHAR_31   = "varchar(31)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_VARCHAR_4094 = "varchar(4094)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_VARCHAR_510  = "varchar(510)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_VARCHAR_63   = "varchar(63)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_VARCHAR_7    = "varchar(7)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_VARCHAR_MAX  = "varchar(max)";
 | 
			
		||||
    public const string FLG_GLOBAL_FILTER_DATA         = nameof(FLG_GLOBAL_FILTER_DATA);
 | 
			
		||||
    public const string FLG_GLOBAL_FILTER_DELETE       = nameof(FLG_GLOBAL_FILTER_DELETE);
 | 
			
		||||
    public const string FLG_GLOBAL_FILTER_MEMBER       = nameof(FLG_GLOBAL_FILTER_MEMBER);
 | 
			
		||||
    public const string FLG_GLOBAL_FILTER_SELF         = nameof(FLG_GLOBAL_FILTER_SELF);
 | 
			
		||||
    public const string FLG_GLOBAL_FILTER_TENANT       = nameof(FLG_GLOBAL_FILTER_TENANT);
 | 
			
		||||
    public const string FLG_HEALTH_CHECK_PATH_PREFIX   = "health/check";
 | 
			
		||||
    public const string FLG_HTTP_HEADER_REFERER        = "Referer";
 | 
			
		||||
    public const string FLG_HTTP_HEADER_USER_AGENT     = "User-Agent";
 | 
			
		||||
    public const string FLG_HTTP_METHOD_CONNECT        = "CONNECT";
 | 
			
		||||
    public const string FLG_HTTP_METHOD_DELETE         = "DELETE";
 | 
			
		||||
    public const string FLG_HTTP_METHOD_GET            = "GET";
 | 
			
		||||
    public const string FLG_HTTP_METHOD_HEAD           = "HEAD";
 | 
			
		||||
    public const string FLG_HTTP_METHOD_OPTIONS        = "OPTIONS";
 | 
			
		||||
    public const string FLG_HTTP_METHOD_PATCH          = "PATCH";
 | 
			
		||||
    public const string FLG_HTTP_METHOD_POST           = "POST";
 | 
			
		||||
    public const string FLG_HTTP_METHOD_PUT            = "PUT";
 | 
			
		||||
    public const string FLG_HTTP_METHOD_TRACE          = "TRACE";
 | 
			
		||||
    public const string FLG_RANDOM_UNAME_PWD           = "VcXlp7WY";
 | 
			
		||||
    public const string FLG_REDIS_INSTANCE_DATA_CACHE  = "DataCache";
 | 
			
		||||
    public const string FLG_SNOWFLAKE_WORK_ID          = "SNOWFLAKE_WORK_ID";
 | 
			
		||||
    public const string FLG_SYSTEM_PREFIX              = "sc_";
 | 
			
		||||
    public const string FLG_TABLE_NAME_PREFIX          = "";
 | 
			
		||||
    public const string FLG_CONTEXT_MEMBER_INFO                       = nameof(FLG_CONTEXT_MEMBER_INFO);
 | 
			
		||||
    public const string FLG_CONTEXT_OWNER_DEPT_ID                     = nameof(FLG_CONTEXT_OWNER_DEPT_ID);
 | 
			
		||||
    public const string FLG_CONTEXT_USER_ID                           = nameof(FLG_CONTEXT_USER_ID);
 | 
			
		||||
    public const string FLG_CONTEXT_USER_INFO                         = nameof(FLG_CONTEXT_USER_INFO);
 | 
			
		||||
    public const string FLG_DB_EXCEPTION_PRIVATE_KEY_CONFLICT         = "PRIMARY KEY";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_NVARCHAR                    = "nvarchar";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_NVARCHAR_1022               = "nvarchar(1022)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_NVARCHAR_127                = "nvarchar(127)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_NVARCHAR_15                 = "nvarchar(15)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_NVARCHAR_255                = "nvarchar(255)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_NVARCHAR_31                 = "nvarchar(31)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_NVARCHAR_4094               = "nvarchar(4094)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_NVARCHAR_510                = "nvarchar(510)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_NVARCHAR_63                 = "nvarchar(63)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_NVARCHAR_7                  = "nvarchar(7)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_NVARCHAR_MAX                = "nvarchar(max)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_SMALL_INT                   = "smallint";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_TEXT                        = "text";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_VARCHAR                     = "varchar";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_VARCHAR_1022                = "varchar(1022)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_VARCHAR_127                 = "varchar(127)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_VARCHAR_15                  = "varchar(15)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_VARCHAR_255                 = "varchar(255)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_VARCHAR_31                  = "varchar(31)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_VARCHAR_4094                = "varchar(4094)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_VARCHAR_510                 = "varchar(510)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_VARCHAR_63                  = "varchar(63)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_VARCHAR_7                   = "varchar(7)";
 | 
			
		||||
    public const string FLG_DB_FIELD_TYPE_VARCHAR_MAX                 = "varchar(max)";
 | 
			
		||||
    public const string FLG_DB_TABLE_NAME_PREFIX                      = "";
 | 
			
		||||
    public const string FLG_FREE_SQL_GLOBAL_FILTER_DATA               = nameof(FLG_FREE_SQL_GLOBAL_FILTER_DATA);
 | 
			
		||||
    public const string FLG_FREE_SQL_GLOBAL_FILTER_DELETE             = nameof(FLG_FREE_SQL_GLOBAL_FILTER_DELETE);
 | 
			
		||||
    public const string FLG_FREE_SQL_GLOBAL_FILTER_MEMBER             = nameof(FLG_FREE_SQL_GLOBAL_FILTER_MEMBER);
 | 
			
		||||
    public const string FLG_FREE_SQL_GLOBAL_FILTER_SELF               = nameof(FLG_FREE_SQL_GLOBAL_FILTER_SELF);
 | 
			
		||||
    public const string FLG_FREE_SQL_GLOBAL_FILTER_TENANT             = nameof(FLG_FREE_SQL_GLOBAL_FILTER_TENANT);
 | 
			
		||||
    public const string FLG_HTTP_HEADER_KEY_AUTHORIZATION             = "Authorization";
 | 
			
		||||
    public const string FLG_HTTP_HEADER_KEY_REFERER                   = "Referer";
 | 
			
		||||
    public const string FLG_HTTP_HEADER_KEY_USER_AGENT                = "User-Agent";
 | 
			
		||||
    public const string FLG_HTTP_HEADER_KEY_X_ACCESS_TOKEN            = "X-ACCESS-TOKEN";
 | 
			
		||||
    public const string FLG_HTTP_HEADER_KEY_X_ACCESS_TOKEN_HEADER_KEY = "X-Authorization";
 | 
			
		||||
    public const string FLG_HTTP_HEADER_KEY_X_FORWARDED_FOR           = "X-Forwarded-For";
 | 
			
		||||
    public const string FLG_HTTP_HEADER_KEY_X_REAL_IP                 = "X-Real-IP";
 | 
			
		||||
    public const string FLG_HTTP_HEADER_VALUE_ACCESS_TOKEN            = "ACCESS-TOKEN";
 | 
			
		||||
    public const string FLG_HTTP_HEADER_VALUE_APPLICATION_JSON        = "application/json";
 | 
			
		||||
    public const string FLG_HTTP_HEADER_VALUE_APPLICATION_URLENCODED  = "application/x-www-form-urlencoded";
 | 
			
		||||
    public const string FLG_HTTP_HEADER_VALUE_AUTH_SCHEMA             = "Bearer";
 | 
			
		||||
    public const string FLG_HTTP_METHOD_CONNECT                       = "CONNECT";
 | 
			
		||||
    public const string FLG_HTTP_METHOD_DELETE                        = "DELETE";
 | 
			
		||||
    public const string FLG_HTTP_METHOD_GET                           = "GET";
 | 
			
		||||
    public const string FLG_HTTP_METHOD_HEAD                          = "HEAD";
 | 
			
		||||
    public const string FLG_HTTP_METHOD_OPTIONS                       = "OPTIONS";
 | 
			
		||||
    public const string FLG_HTTP_METHOD_PATCH                         = "PATCH";
 | 
			
		||||
    public const string FLG_HTTP_METHOD_POST                          = "POST";
 | 
			
		||||
    public const string FLG_HTTP_METHOD_PUT                           = "PUT";
 | 
			
		||||
    public const string FLG_HTTP_METHOD_TRACE                         = "TRACE";
 | 
			
		||||
    public const string FLG_PATH_PREFIX_HEALTH_CHECK                  = "probe/health.check";
 | 
			
		||||
    public const string FLG_RANDOM_UNAME_PWD                          = "VcXlp7WY";
 | 
			
		||||
    public const string FLG_REDIS_INSTANCE_DATA_CACHE                 = "DataCache";
 | 
			
		||||
    public const string FLG_SNOWFLAKE_WORK_ID                         = "SNOWFLAKE_WORK_ID";
 | 
			
		||||
    public const string FLG_SYSTEM_PREFIX                             = "sc_";
 | 
			
		||||
 | 
			
		||||
    public const string FLG_UA_MOBILE
 | 
			
		||||
    public const string FLGL_HTTP_HEADER_VALUE_UA_MOBILE
 | 
			
		||||
        = "Mozilla/5.0 (Linux; Android 9; Redmi Note 8 Pro Build/PPR1.180610.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.96 Mobile Safari/537.36";
 | 
			
		||||
 | 
			
		||||
    public const string FLG_UA_PC
 | 
			
		||||
    public const string FLGL_HTTP_HEADER_VALUE_UA_PC
 | 
			
		||||
        = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36";
 | 
			
		||||
 | 
			
		||||
    public const string FLG_VISIBLE_ASCIIS
 | 
			
		||||
    public const string FLGL_VISIBLE_ASCIIS
 | 
			
		||||
        = """!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~""";
 | 
			
		||||
 | 
			
		||||
    public const string FLG_X_ACCESS_TOKEN            = "X-ACCESS-TOKEN";
 | 
			
		||||
    public const string FLG_X_ACCESS_TOKEN_HEADER_KEY = "X-Authorization";
 | 
			
		||||
 | 
			
		||||
    public const string RGX_CERTIFICATE = "^[a-zA-Z0-9-_]+$";
 | 
			
		||||
 | 
			
		||||
    public const string RGX_CHINESE_NAME
 | 
			
		||||
        = """^(?:赵|钱|孙|李|周|吴|郑|王|冯|陈|褚|卫|蒋|沈|韩|杨|朱|秦|尤|许|何|吕|施|张|孔|曹|严|华|金|魏|陶|姜|戚|谢|邹|喻|柏|水|窦|章|云|苏|潘|葛|奚|范|彭|郎|鲁|韦|昌|马|苗|凤|花|方|俞|任|袁|柳|酆|鲍|史|唐|费|廉|岑|薛|雷|贺|倪|汤|滕|殷|罗|毕|郝|邬|安|常|乐|于|时|傅|皮|卞|齐|康|伍|余|元|卜|顾|孟|平|黄|和|穆|萧|尹|姚|邵|湛|汪|祁|毛|禹|狄|米|贝|明|臧|计|伏|成|戴|谈|宋|茅|庞|熊|纪|舒|屈|项|祝|董|梁|杜|阮|蓝|闵|席|季|麻|强|贾|路|娄|危|江|童|颜|郭|梅|盛|林|刁|钟|徐|邱|骆|高|夏|蔡|田|樊|胡|凌|霍|虞|万|支|柯|昝|管|卢|莫|经|房|裘|缪|干|解|应|宗|丁|宣|贲|邓|郁|单|杭|洪|包|诸|左|石|崔|吉|钮|龚|程|嵇|邢|滑|裴|陆|荣|翁|荀|羊|於|惠|甄|曲|家|封|芮|羿|储|靳|汲|邴|糜|松|井|段|富|巫|乌|焦|巴|弓|牧|隗|山|谷|车|侯|宓|蓬|全|郗|班|仰|秋|仲|伊|宫|宁|仇|栾|暴|甘|钭|厉|戎|祖|武|符|刘|景|詹|束|龙|叶|幸|司|韶|郜|黎|蓟|薄|印|宿|白|怀|蒲|邰|从|鄂|索|咸|籍|赖|卓|蔺|屠|蒙|池|乔|阴|胥|能|苍|双|闻|莘|党|翟|谭|贡|劳|逄|姬|申|扶|堵|冉|宰|郦|雍|郤|璩|桑|桂|濮|牛|寿|通|边|扈|燕|冀|郏|浦|尚|农|温|别|庄|晏|柴|瞿|阎|充|慕|连|茹|习|宦|艾|鱼|容|向|古|易|慎|戈|廖|庾|终|暨|居|衡|步|都|耿|满|弘|匡|国|文|寇|广|禄|阙|东|欧|殳|沃|利|蔚|越|夔|隆|师|巩|厍|聂|晁|勾|敖|融|冷|訾|辛|阚|那|简|饶|空|曾|毋|沙|乜|养|鞠|须|丰|巢|关|蒯|相|查|後|荆|红|游|竺|权|逯|盖|益|桓|公|万俟|司马|上官|欧阳|夏侯|诸葛|闻人|东方|赫连|皇甫|尉迟|公羊|澹台|公冶|宗政|濮阳|淳于|单于|太叔|申屠|公孙|仲孙|轩辕|令狐|钟离|宇文|长孙|慕容|鲜于|闾丘|司徒|司空|亓官|司寇|仉|督|子车|颛孙|端木|巫马|公西|漆雕|乐正|壤驷|公良|拓跋|夹谷|宰父|谷梁|晋|楚|闫|法|汝|鄢|涂|钦|段干|百里|东郭|南门|呼延|归|海|羊舌|微生|岳|帅|缑|亢|况|后|有|琴|梁丘|左丘|东门|西门|商|牟|佘|佴|伯|赏|南宫|墨|哈|谯|笪|年|爱|阳|佟|第五|言|福)[\u4e00-\u9fa5]{1,3}$""";
 | 
			
		||||
 | 
			
		||||
    public const string RGX_CRON
 | 
			
		||||
        = "^\\s*($|#|\\w+\\s*=|(\\?|\\*|(?:[0-5]?\\d)(?:(?:-|\\/|\\,)(?:[0-5]?\\d))?(?:,(?:[0-5]?\\d)(?:(?:-|\\/|\\,)(?:[0-5]?\\d))?)*)\\s+(\\?|\\*|(?:[0-5]?\\d)(?:(?:-|\\/|\\,)(?:[0-5]?\\d))?(?:,(?:[0-5]?\\d)(?:(?:-|\\/|\\,)(?:[0-5]?\\d))?)*)\\s+(\\?|\\*|(?:[01]?\\d|2[0-3])(?:(?:-|\\/|\\,)(?:[01]?\\d|2[0-3]))?(?:,(?:[01]?\\d|2[0-3])(?:(?:-|\\/|\\,)(?:[01]?\\d|2[0-3]))?)*)\\s+(\\?|\\*|(?:0?[1-9]|[12]\\d|3[01])(?:(?:-|\\/|\\,)(?:0?[1-9]|[12]\\d|3[01]))?(?:,(?:0?[1-9]|[12]\\d|3[01])(?:(?:-|\\/|\\,)(?:0?[1-9]|[12]\\d|3[01]))?)*)\\s+(\\?|\\*|(?:[1-9]|1[012])(?:(?:-|\\/|\\,)(?:[1-9]|1[012]))?(?:L|W)?(?:,(?:[1-9]|1[012])(?:(?:-|\\/|\\,)(?:[1-9]|1[012]))?(?:L|W)?)*|\\?|\\*|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(?:(?:-)(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?(?:,(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(?:(?:-)(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?)*)\\s+(\\?|\\*|(?:[0-6])(?:(?:-|\\/|\\,|#)(?:[0-6]))?(?:L)?(?:,(?:[0-6])(?:(?:-|\\/|\\,|#)(?:[0-6]))?(?:L)?)*|\\?|\\*|(?:MON|TUE|WED|THU|FRI|SAT|SUN)(?:(?:-)(?:MON|TUE|WED|THU|FRI|SAT|SUN))?(?:,(?:MON|TUE|WED|THU|FRI|SAT|SUN)(?:(?:-)(?:MON|TUE|WED|THU|FRI|SAT|SUN))?)*)(|\\s)+(\\?|\\*|(?:|\\d{4})(?:(?:-|\\/|\\,)(?:|\\d{4}))?(?:,(?:|\\d{4})(?:(?:-|\\/|\\,)(?:|\\d{4}))?)*))$";
 | 
			
		||||
 | 
			
		||||
    public const string RGX_EMAIL
 | 
			
		||||
        = """^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$""";
 | 
			
		||||
 | 
			
		||||
    public const string RGX_INVITE_CODE = """^\d{8}$""";
 | 
			
		||||
 | 
			
		||||
    public const string RGX_CERTIFICATE         = "^[a-zA-Z0-9-_]+$";
 | 
			
		||||
    public const string RGX_INVITE_CODE         = """^\d{8}$""";
 | 
			
		||||
    public const string RGX_MOBILE              = """^1(3\d|4[5-9]|5[0-35-9]|6[6]|7[2-8]|8\d|9[0-35-9])\d{8}$""";
 | 
			
		||||
    public const string RGX_PASSWORD            = "^(?![0-9]+$)(?![a-zA-Z]+$).{8,16}$";
 | 
			
		||||
    public const string RGX_PAY_PASSWORD        = """^\d{6}$""";
 | 
			
		||||
@@ -93,6 +91,18 @@ public static class Chars
 | 
			
		||||
    public const string RGX_USERNAME            = "^[a-zA-Z0-9_]{4,16}$";
 | 
			
		||||
    public const string RGX_VERIFY_CODE         = """^\d{4}$""";
 | 
			
		||||
 | 
			
		||||
    public const string RGXL_CHINESE_NAME
 | 
			
		||||
        = """^(?:赵|钱|孙|李|周|吴|郑|王|冯|陈|褚|卫|蒋|沈|韩|杨|朱|秦|尤|许|何|吕|施|张|孔|曹|严|华|金|魏|陶|姜|戚|谢|邹|喻|柏|水|窦|章|云|苏|潘|葛|奚|范|彭|郎|鲁|韦|昌|马|苗|凤|花|方|俞|任|袁|柳|酆|鲍|史|唐|费|廉|岑|薛|雷|贺|倪|汤|滕|殷|罗|毕|郝|邬|安|常|乐|于|时|傅|皮|卞|齐|康|伍|余|元|卜|顾|孟|平|黄|和|穆|萧|尹|姚|邵|湛|汪|祁|毛|禹|狄|米|贝|明|臧|计|伏|成|戴|谈|宋|茅|庞|熊|纪|舒|屈|项|祝|董|梁|杜|阮|蓝|闵|席|季|麻|强|贾|路|娄|危|江|童|颜|郭|梅|盛|林|刁|钟|徐|邱|骆|高|夏|蔡|田|樊|胡|凌|霍|虞|万|支|柯|昝|管|卢|莫|经|房|裘|缪|干|解|应|宗|丁|宣|贲|邓|郁|单|杭|洪|包|诸|左|石|崔|吉|钮|龚|程|嵇|邢|滑|裴|陆|荣|翁|荀|羊|於|惠|甄|曲|家|封|芮|羿|储|靳|汲|邴|糜|松|井|段|富|巫|乌|焦|巴|弓|牧|隗|山|谷|车|侯|宓|蓬|全|郗|班|仰|秋|仲|伊|宫|宁|仇|栾|暴|甘|钭|厉|戎|祖|武|符|刘|景|詹|束|龙|叶|幸|司|韶|郜|黎|蓟|薄|印|宿|白|怀|蒲|邰|从|鄂|索|咸|籍|赖|卓|蔺|屠|蒙|池|乔|阴|胥|能|苍|双|闻|莘|党|翟|谭|贡|劳|逄|姬|申|扶|堵|冉|宰|郦|雍|郤|璩|桑|桂|濮|牛|寿|通|边|扈|燕|冀|郏|浦|尚|农|温|别|庄|晏|柴|瞿|阎|充|慕|连|茹|习|宦|艾|鱼|容|向|古|易|慎|戈|廖|庾|终|暨|居|衡|步|都|耿|满|弘|匡|国|文|寇|广|禄|阙|东|欧|殳|沃|利|蔚|越|夔|隆|师|巩|厍|聂|晁|勾|敖|融|冷|訾|辛|阚|那|简|饶|空|曾|毋|沙|乜|养|鞠|须|丰|巢|关|蒯|相|查|後|荆|红|游|竺|权|逯|盖|益|桓|公|万俟|司马|上官|欧阳|夏侯|诸葛|闻人|东方|赫连|皇甫|尉迟|公羊|澹台|公冶|宗政|濮阳|淳于|单于|太叔|申屠|公孙|仲孙|轩辕|令狐|钟离|宇文|长孙|慕容|鲜于|闾丘|司徒|司空|亓官|司寇|仉|督|子车|颛孙|端木|巫马|公西|漆雕|乐正|壤驷|公良|拓跋|夹谷|宰父|谷梁|晋|楚|闫|法|汝|鄢|涂|钦|段干|百里|东郭|南门|呼延|归|海|羊舌|微生|岳|帅|缑|亢|况|后|有|琴|梁丘|左丘|东门|西门|商|牟|佘|佴|伯|赏|南宫|墨|哈|谯|笪|年|爱|阳|佟|第五|言|福)[\u4e00-\u9fa5]{1,3}$""";
 | 
			
		||||
 | 
			
		||||
    public const string RGXL_CRON
 | 
			
		||||
        = "^\\s*($|#|\\w+\\s*=|(\\?|\\*|(?:[0-5]?\\d)(?:(?:-|\\/|\\,)(?:[0-5]?\\d))?(?:,(?:[0-5]?\\d)(?:(?:-|\\/|\\,)(?:[0-5]?\\d))?)*)\\s+(\\?|\\*|(?:[0-5]?\\d)(?:(?:-|\\/|\\,)(?:[0-5]?\\d))?(?:,(?:[0-5]?\\d)(?:(?:-|\\/|\\,)(?:[0-5]?\\d))?)*)\\s+(\\?|\\*|(?:[01]?\\d|2[0-3])(?:(?:-|\\/|\\,)(?:[01]?\\d|2[0-3]))?(?:,(?:[01]?\\d|2[0-3])(?:(?:-|\\/|\\,)(?:[01]?\\d|2[0-3]))?)*)\\s+(\\?|\\*|(?:0?[1-9]|[12]\\d|3[01])(?:(?:-|\\/|\\,)(?:0?[1-9]|[12]\\d|3[01]))?(?:,(?:0?[1-9]|[12]\\d|3[01])(?:(?:-|\\/|\\,)(?:0?[1-9]|[12]\\d|3[01]))?)*)\\s+(\\?|\\*|(?:[1-9]|1[012])(?:(?:-|\\/|\\,)(?:[1-9]|1[012]))?(?:L|W)?(?:,(?:[1-9]|1[012])(?:(?:-|\\/|\\,)(?:[1-9]|1[012]))?(?:L|W)?)*|\\?|\\*|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(?:(?:-)(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?(?:,(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(?:(?:-)(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?)*)\\s+(\\?|\\*|(?:[0-6])(?:(?:-|\\/|\\,|#)(?:[0-6]))?(?:L)?(?:,(?:[0-6])(?:(?:-|\\/|\\,|#)(?:[0-6]))?(?:L)?)*|\\?|\\*|(?:MON|TUE|WED|THU|FRI|SAT|SUN)(?:(?:-)(?:MON|TUE|WED|THU|FRI|SAT|SUN))?(?:,(?:MON|TUE|WED|THU|FRI|SAT|SUN)(?:(?:-)(?:MON|TUE|WED|THU|FRI|SAT|SUN))?)*)(|\\s)+(\\?|\\*|(?:|\\d{4})(?:(?:-|\\/|\\,)(?:|\\d{4}))?(?:,(?:|\\d{4})(?:(?:-|\\/|\\,)(?:|\\d{4}))?)*))$";
 | 
			
		||||
 | 
			
		||||
    public const string RGXL_EMAIL
 | 
			
		||||
        = """^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$""";
 | 
			
		||||
 | 
			
		||||
    public const string RGXL_IP_V4
 | 
			
		||||
        = @"^(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})){3}$";
 | 
			
		||||
 | 
			
		||||
    public const string TPL_DATE_HH_MM_SS_FFFFFF       = "HH:mm:ss.ffffff";
 | 
			
		||||
    public const string TPL_DATE_YYYY_MM_DD            = "yyyy-MM-dd";
 | 
			
		||||
    public const string TPL_DATE_YYYY_MM_DD_HH_MM_SS   = "yyyy-MM-dd HH:mm:ss";
 | 
			
		||||
 
 | 
			
		||||
@@ -10,17 +10,22 @@ namespace NetAdmin.Infrastructure.Constant;
 | 
			
		||||
/// </remarks>
 | 
			
		||||
public static class Numbers
 | 
			
		||||
{
 | 
			
		||||
    public const int  BULK_REQ_LIMIT            = 100;             // 批量请求允许的最大数量
 | 
			
		||||
    public const int  CONSOLE_LINE_LEN_LIMIT    = 8192;            // 控制台输出的最大长度
 | 
			
		||||
    public const long DEF_SORT_VAL              = 100;             // 排序默认值
 | 
			
		||||
    public const long DIC_CATALOG_ID_GEO_AREA   = 379794295185413; // 字典目录编号-行政区划字典
 | 
			
		||||
    public const int  HTTP_STATUS_BIZ_FAIL      = 900;             // Http状态码-业务异常
 | 
			
		||||
    public const int  QUERY_DEF_PAGE_SIZE       = 20;              // 分页查询默认的页容量
 | 
			
		||||
    public const int  QUERY_LIMIT               = 100;             // 非分页查询允许返回的最大条数
 | 
			
		||||
    public const int  QUERY_MAX_PAGE_NO         = 1000;            // 分页查询允许最大的页码
 | 
			
		||||
    public const int  QUERY_MAX_PAGE_SIZE       = 100;             // 分页查询允许最大的页容量
 | 
			
		||||
    public const int  RED_LOCK_EXPIRY_TIME_SECS = 30;              // red lock: 锁锁定过期时间,锁区域内的逻辑执行如果超过过期时间,锁将被释放
 | 
			
		||||
    public const int  RED_LOCK_RETRY_TIME_SECS  = 1;               // red lock: 锁等待时间内,多久尝试获取一次
 | 
			
		||||
    public const int  RED_LOCK_WAIT_TIME_SECS   = 10;              // red lock: 锁等待时间,相同的 resource 如果当前的锁被其他线程占用,最多等待时间
 | 
			
		||||
    public const int  TIMEOUT_SECS_JOB          = 600;             // 超时时间:作业
 | 
			
		||||
    public const int  DEF_PAGE_SIZE_QUERY = 20;  // 分页查询默认的页容量
 | 
			
		||||
    public const long DEF_SORT_VAL        = 100; // 排序默认值
 | 
			
		||||
 | 
			
		||||
    public const int  HTTP_STATUS_BIZ_FAIL    = 900;             // Http状态码-业务异常
 | 
			
		||||
    public const long ID_DIC_CATALOG_GEO_AREA = 379794295185413; // 唯一编号:字典目录-行政区划字典
 | 
			
		||||
 | 
			
		||||
    public const int MAX_LIMIT_BULK_REQ          = 100;   // 最大限制:批量请求数
 | 
			
		||||
    public const int MAX_LIMIT_PRINT_LEN_CONTENT = 4096;  // 最大限制:打印长度(HTTP 内容)
 | 
			
		||||
    public const int MAX_LIMIT_PRINT_LEN_SQL     = 4096;  // 最大限制:打印长度(SQL 语句)
 | 
			
		||||
    public const int MAX_LIMIT_QUERY             = 1000;  // 最大限制:非分页查询条数
 | 
			
		||||
    public const int MAX_LIMIT_QUERY_PAGE_NO     = 10000; // 最大限制:分页查询页码
 | 
			
		||||
    public const int MAX_LIMIT_QUERY_PAGE_SIZE   = 100;   // 最大限制:分页查询页容量
 | 
			
		||||
 | 
			
		||||
    public const int SECS_CACHE_DEFAULT   = 60;  // 秒:缓存时间-默认
 | 
			
		||||
    public const int SECS_RED_LOCK_EXPIRY = 30;  // 秒:RedLock-锁过期时间,锁区域内的逻辑执行如果超过过期时间,锁将被释放
 | 
			
		||||
    public const int SECS_RED_LOCK_RETRY  = 1;   // 秒:RedLock-锁等待时间内,多久尝试获取一次
 | 
			
		||||
    public const int SECS_RED_LOCK_WAIT   = 10;  // 秒:RedLock-锁等待时间,相同的 resource 如果当前的锁被其他线程占用,最多等待时间
 | 
			
		||||
    public const int SECS_TIMEOUT_JOB     = 600; // 秒:超时时间-作业
 | 
			
		||||
}
 | 
			
		||||
@@ -19,4 +19,12 @@ public enum Orders
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [ResourceDescription<Ln>(nameof(Ln.倒序排序))]
 | 
			
		||||
    Descending = 2
 | 
			
		||||
 | 
			
		||||
   ,
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     随机排序
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [ResourceDescription<Ln>(nameof(Ln.随机排序))]
 | 
			
		||||
    Random = 3
 | 
			
		||||
}
 | 
			
		||||
@@ -4,5 +4,5 @@ namespace NetAdmin.Infrastructure.Exceptions;
 | 
			
		||||
///     加锁失败异常
 | 
			
		||||
/// </summary>
 | 
			
		||||
#pragma warning disable RCS1194
 | 
			
		||||
public sealed class NetAdminGetLockerException : NetAdminUnexpectedException;
 | 
			
		||||
public sealed class NetAdminGetLockerException : NetAdminException;
 | 
			
		||||
#pragma warning restore RCS1194
 | 
			
		||||
@@ -0,0 +1,25 @@
 | 
			
		||||
namespace NetAdmin.Infrastructure.Extensions;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     HttpContext 扩展方法
 | 
			
		||||
/// </summary>
 | 
			
		||||
public static class HttpContextExtensions
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     获取客户端真实IP
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static IPAddress GetRealIpAddress(this HttpContext me)
 | 
			
		||||
    {
 | 
			
		||||
        #pragma warning disable IDE0046
 | 
			
		||||
        if (me.Request.Headers.TryGetValue(Chars.FLG_HTTP_HEADER_KEY_X_FORWARDED_FOR, out var ips1) &&
 | 
			
		||||
            #pragma warning restore IDE0046
 | 
			
		||||
            IPAddress.TryParse(ips1.FirstOrDefault()?.Split(',').FirstOrDefault(), out var ip1)) {
 | 
			
		||||
            return ip1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return me.Request.Headers.TryGetValue(Chars.FLG_HTTP_HEADER_KEY_X_REAL_IP, out var ips2) &&
 | 
			
		||||
               IPAddress.TryParse(ips2.FirstOrDefault()?.Split(',').FirstOrDefault(), out var ip2)
 | 
			
		||||
            ? ip2
 | 
			
		||||
            : me.Connection.RemoteIpAddress;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -19,7 +19,8 @@ public static class HttpRequestMessageExtensions
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static async Task<HttpRequestMessage> LogAsync<T>(this HttpRequestMessage me, ILogger<T> logger)
 | 
			
		||||
    {
 | 
			
		||||
        logger.Info($"HTTP Request: {await me.BuildJsonAsync().ConfigureAwait(false)}");
 | 
			
		||||
        logger.Info(
 | 
			
		||||
            $"HTTP Request: {(await me.BuildJsonAsync().ConfigureAwait(false))?.Sub(0, Numbers.MAX_LIMIT_PRINT_LEN_CONTENT)}");
 | 
			
		||||
        return me;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -11,7 +11,9 @@ public static class HttpResponseMessageExtensions
 | 
			
		||||
    public static async Task LogAsync<T>(this HttpResponseMessage me, ILogger<T> logger
 | 
			
		||||
                                       , Func<string, string>     bodyPreHandle = null)
 | 
			
		||||
    {
 | 
			
		||||
        logger.Info(await me.BuildJsonAsync(bodyPreHandle).ConfigureAwait(false));
 | 
			
		||||
        logger.Info(
 | 
			
		||||
            (await me.BuildJsonAsync(bodyPreHandle).ConfigureAwait(false))?.Sub(
 | 
			
		||||
                0, Numbers.MAX_LIMIT_PRINT_LEN_CONTENT));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
@@ -20,7 +22,8 @@ public static class HttpResponseMessageExtensions
 | 
			
		||||
    public static async Task LogExceptionAsync<T>(this HttpResponseMessage me, string errors, ILogger<T> logger
 | 
			
		||||
                                                , Func<string, string>     bodyHandle = null)
 | 
			
		||||
    {
 | 
			
		||||
        logger.Warn($"{errors}: {await me.BuildJsonAsync(bodyHandle).ConfigureAwait(false)}");
 | 
			
		||||
        logger.Warn(
 | 
			
		||||
            $"{errors}: {(await me.BuildJsonAsync(bodyHandle).ConfigureAwait(false))?.Sub(0, Numbers.MAX_LIMIT_PRINT_LEN_CONTENT)}");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -7,12 +7,12 @@
 | 
			
		||||
    <Import Project="$(SolutionDir)/build/prebuild.targets"/>
 | 
			
		||||
    <ItemGroup>
 | 
			
		||||
        <PackageReference Include="Cronos" Version="0.8.4"/>
 | 
			
		||||
        <PackageReference Include="FreeSql.DbContext.NS" Version="3.2.813-preview20240208-ns1"/>
 | 
			
		||||
        <PackageReference Include="FreeSql.Provider.Sqlite.NS" Version="3.2.813-preview20240208-ns1"/>
 | 
			
		||||
        <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.1.31"/>
 | 
			
		||||
        <PackageReference Include="Furion.Extras.ObjectMapper.Mapster.NS" Version="4.9.1.31-ns2"/>
 | 
			
		||||
        <PackageReference Include="Furion.Pure.NS" Version="4.9.1.31-ns2"/>
 | 
			
		||||
        <PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.0-preview.1.24081.5"/>
 | 
			
		||||
        <PackageReference Include="FreeSql.DbContext.NS" Version="3.2.821-ns1"/>
 | 
			
		||||
        <PackageReference Include="FreeSql.Provider.Sqlite.NS" Version="3.2.821-ns1"/>
 | 
			
		||||
        <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.2.19"/>
 | 
			
		||||
        <PackageReference Include="Furion.Extras.ObjectMapper.Mapster.NS" Version="4.9.2.19-ns3"/>
 | 
			
		||||
        <PackageReference Include="Furion.Pure.NS" Version="4.9.2.19-ns3"/>
 | 
			
		||||
        <PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.1"/>
 | 
			
		||||
        <PackageReference Include="Minio" Version="6.0.2"/>
 | 
			
		||||
        <PackageReference Include="NSExt" Version="2.1.0"/>
 | 
			
		||||
        <PackageReference Include="RedLock.net" Version="2.3.2"/>
 | 
			
		||||
 
 | 
			
		||||
@@ -315,8 +315,9 @@ public sealed class UserAgentParser
 | 
			
		||||
 | 
			
		||||
    private bool SetPlatform()
 | 
			
		||||
    {
 | 
			
		||||
        var kv = _platforms.First(x => //
 | 
			
		||||
                                      Regex.IsMatch(_agent, $"{Regex.Escape(x.Key)}", RegexOptions.IgnoreCase));
 | 
			
		||||
        var kv = _platforms.FirstOrDefault(x => //
 | 
			
		||||
                                               Regex.IsMatch(_agent, $"{Regex.Escape(x.Key)}"
 | 
			
		||||
                                                           , RegexOptions.IgnoreCase));
 | 
			
		||||
 | 
			
		||||
        if (kv.Key == null) {
 | 
			
		||||
            Platform = "Unknown Platform";
 | 
			
		||||
 
 | 
			
		||||
@@ -297,8 +297,8 @@
 | 
			
		||||
      // 数据缓存
 | 
			
		||||
      {
 | 
			
		||||
        "Name": "DataCache",
 | 
			
		||||
        "ConnStr": "localhost:6379",
 | 
			
		||||
        "Database": 0
 | 
			
		||||
        "ConnStr": "localhost:6379,abortConnect=false",
 | 
			
		||||
        "DataBase": 0,
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
 
 | 
			
		||||
@@ -24,12 +24,12 @@ public sealed class Startup : Host.Startup
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     配置应用程序中间件
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public void Configure(IApplicationBuilder app)
 | 
			
		||||
    public void Configure(IApplicationBuilder app, IHostApplicationLifetime lifeTime)
 | 
			
		||||
    {
 | 
			
		||||
        _ = app                                      //
 | 
			
		||||
            .UseRealIp()                             // 使用RealIp中间件,用于获取真实客户端IP地址
 | 
			
		||||
            .EnableBuffering()                       // 启用请求体缓冲,允许多次读取请求体
 | 
			
		||||
            .UseMiddleware<RequestAuditMiddleware>() // 使用RequestAuditMiddleware中间件,执行请求审计
 | 
			
		||||
        _ = app                                        //
 | 
			
		||||
            .UseMiddleware<SafetyShopHostMiddleware>() // 安全停机中间件
 | 
			
		||||
            .EnableBuffering()                         // 启用请求体缓冲,允许多次读取请求体
 | 
			
		||||
            .UseMiddleware<RequestAuditMiddleware>()   // 使用RequestAuditMiddleware中间件,执行请求审计
 | 
			
		||||
            #if DEBUG
 | 
			
		||||
            .UseOpenApiSkin() // 使用OpenApiSkin中间件(仅在调试模式下),提供Swagger UI皮肤
 | 
			
		||||
            #else
 | 
			
		||||
@@ -41,6 +41,7 @@ public sealed class Startup : Host.Startup
 | 
			
		||||
            .UseRouting()                              // 使用Routing中间件,配置路由映射
 | 
			
		||||
            .UseMiddleware<RemoveNullNodeMiddleware>() // 使用RemoveNullNodeMiddleware中间件,删除JSON中的空节点
 | 
			
		||||
            .UseEndpoints();                           // 配置端点以处理请求
 | 
			
		||||
        _ = lifeTime.ApplicationStopping.Register(SafetyShopHostMiddleware.OnStopping);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,13 @@ public sealed class ApiService(
 | 
			
		||||
        throw new NotImplementedException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QueryApiReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        req.ThrowIfInvalid();
 | 
			
		||||
        return QueryInternal(req).CountAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<QueryApiRsp> CreateAsync(CreateApiReq req)
 | 
			
		||||
    {
 | 
			
		||||
@@ -140,9 +147,12 @@ public sealed class ApiService(
 | 
			
		||||
 | 
			
		||||
    private ISelect<Sys_Api> QueryInternal(QueryReq<QueryApiReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
 | 
			
		||||
                     .WhereDynamic(req.Filter)
 | 
			
		||||
                     .OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
        var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
 | 
			
		||||
        if (req.Order == Orders.Random) {
 | 
			
		||||
            return ret.OrderByRandom();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
 | 
			
		||||
        if (!req.Prop?.Equals(nameof(req.Filter.CreatedTime), StringComparison.OrdinalIgnoreCase) ?? true) {
 | 
			
		||||
            ret = ret.OrderByDescending(a => a.CreatedTime);
 | 
			
		||||
 
 | 
			
		||||
@@ -10,11 +10,21 @@ namespace NetAdmin.SysComponent.Application.Services.Sys;
 | 
			
		||||
public sealed class CacheService(IConnectionMultiplexer connectionMultiplexer) //
 | 
			
		||||
    : ServiceBase<ICacheService>, ICacheService
 | 
			
		||||
{
 | 
			
		||||
    private readonly InstanceNode _redisInstance;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Initializes a new instance of the <see cref="CacheService" /> class.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public CacheService(IConnectionMultiplexer connectionMultiplexer, IOptions<RedisOptions> redisOptions) //
 | 
			
		||||
        : this(connectionMultiplexer)                                                                      //
 | 
			
		||||
    {
 | 
			
		||||
        _redisInstance = redisOptions.Value.Instances.First(x => x.Name == Chars.FLG_REDIS_INSTANCE_DATA_CACHE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public async Task<CacheStatisticsRsp> CacheStatisticsAsync()
 | 
			
		||||
    {
 | 
			
		||||
        var database = connectionMultiplexer.GetDatabase();
 | 
			
		||||
 | 
			
		||||
        var database = connectionMultiplexer.GetDatabase(_redisInstance.Database);
 | 
			
		||||
        return new CacheStatisticsRsp((string)await database.ExecuteAsync("info").ConfigureAwait(false)) {
 | 
			
		||||
                   DbSize = (long)await database.ExecuteAsync("dbSize").ConfigureAwait(false)
 | 
			
		||||
               };
 | 
			
		||||
@@ -24,7 +34,7 @@ public sealed class CacheService(IConnectionMultiplexer connectionMultiplexer) /
 | 
			
		||||
    public async Task<PagedQueryRsp<GetAllEntriesRsp>> GetAllEntriesAsync(PagedQueryReq<GetAllEntriesReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        req.ThrowIfInvalid();
 | 
			
		||||
        var database = connectionMultiplexer.GetDatabase((int?)req.Filter?.DbIndex ?? 0);
 | 
			
		||||
        var database = connectionMultiplexer.GetDatabase(_redisInstance.Database);
 | 
			
		||||
        var redisResults = (RedisResult[])await database
 | 
			
		||||
                                                .ExecuteAsync("scan", (req.Page - 1) * req.PageSize, "count"
 | 
			
		||||
                                                    ,                 req.PageSize)
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,13 @@ public sealed class ConfigService(DefaultRepository<Sys_Config> rpo) //
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QueryConfigReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        req.ThrowIfInvalid();
 | 
			
		||||
        return QueryInternal(req).CountAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public async Task<QueryConfigRsp> CreateAsync(CreateConfigReq req)
 | 
			
		||||
    {
 | 
			
		||||
@@ -113,8 +120,12 @@ public sealed class ConfigService(DefaultRepository<Sys_Config> rpo) //
 | 
			
		||||
                     .Include(a => a.UserRegisterRole)
 | 
			
		||||
                     .WhereDynamicFilter(req.DynamicFilter)
 | 
			
		||||
                     .WhereIf( //
 | 
			
		||||
                         req.Filter?.Enabled.HasValue ?? false, a => a.Enabled == req.Filter.Enabled.Value)
 | 
			
		||||
                     .OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
                         req.Filter?.Enabled.HasValue ?? false, a => a.Enabled == req.Filter.Enabled.Value);
 | 
			
		||||
        if (req.Order == Orders.Random) {
 | 
			
		||||
            return ret.OrderByRandom();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
        if (!req.Prop?.Equals(nameof(req.Filter.Id), StringComparison.OrdinalIgnoreCase) ?? true) {
 | 
			
		||||
            ret = ret.OrderByDescending(a => a.Id);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -18,17 +18,28 @@ public sealed class ConstantService : ServiceBase<IConstantService>, IConstantSe
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public IDictionary<string, Dictionary<string, string[]>> GetEnums()
 | 
			
		||||
    {
 | 
			
		||||
        return App.EffectiveTypes.Where(x => x.IsEnum && x.GetCustomAttribute<ExportAttribute>(false) != null)
 | 
			
		||||
                  .ToDictionary(x => x.Name, x => //
 | 
			
		||||
                                    x.GetEnumValues()
 | 
			
		||||
                                     .Cast<Enum>()
 | 
			
		||||
                                     .ToDictionary( //
 | 
			
		||||
                                         y => y.ToString()
 | 
			
		||||
                                       , y => new[] {
 | 
			
		||||
                                                        Convert.ToInt64(y, CultureInfo.InvariantCulture)
 | 
			
		||||
                                                               .ToString(CultureInfo.InvariantCulture)
 | 
			
		||||
                                                      , y.ResDesc<Ln>()
 | 
			
		||||
                                                    }));
 | 
			
		||||
        var ret = App.EffectiveTypes.Where(x => x.IsEnum && x.GetCustomAttribute<ExportAttribute>(false) != null)
 | 
			
		||||
                     .ToDictionary(x => x.Name, x => //
 | 
			
		||||
                                       x.GetEnumValues()
 | 
			
		||||
                                        .Cast<Enum>()
 | 
			
		||||
                                        .ToDictionary( //
 | 
			
		||||
                                            y => y.ToString()
 | 
			
		||||
                                          , y => new[] {
 | 
			
		||||
                                                           Convert.ToInt64(y, CultureInfo.InvariantCulture)
 | 
			
		||||
                                                                  .ToString(CultureInfo.InvariantCulture)
 | 
			
		||||
                                                         , y.ResDesc<Ln>()
 | 
			
		||||
                                                       }));
 | 
			
		||||
 | 
			
		||||
        ret.Add($"{nameof(HttpStatusCode)}s", Enum.GetNames<HttpStatusCode>()
 | 
			
		||||
                                                  .ToDictionary( //
 | 
			
		||||
                                                      x => x, x => new[] {
 | 
			
		||||
                                                                             Convert.ToInt64( //
 | 
			
		||||
                                                                                     Enum.Parse<HttpStatusCode>(x)
 | 
			
		||||
                                                                                   , CultureInfo.InvariantCulture)
 | 
			
		||||
                                                                                 .ToString(CultureInfo.InvariantCulture)
 | 
			
		||||
                                                                           , x
 | 
			
		||||
                                                                         }));
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,13 @@ public sealed class DeptService(DefaultRepository<Sys_Dept> rpo) //
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QueryDeptReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        req.ThrowIfInvalid();
 | 
			
		||||
        return QueryInternal(req).CountAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    /// <exception cref="NetAdminInvalidOperationException">Parent_department_does_not_exist</exception>
 | 
			
		||||
    public async Task<QueryDeptRsp> CreateAsync(CreateDeptReq req)
 | 
			
		||||
@@ -88,12 +95,11 @@ public sealed class DeptService(DefaultRepository<Sys_Dept> rpo) //
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    /// <exception cref="NetAdminUnexpectedException">NetAdminUnexpectedException</exception>
 | 
			
		||||
    public async Task<QueryDeptRsp> UpdateAsync(UpdateDeptReq req)
 | 
			
		||||
    {
 | 
			
		||||
        req.ThrowIfInvalid();
 | 
			
		||||
        return await Rpo.UpdateDiy.SetSource(req).ExecuteAffrowsAsync().ConfigureAwait(false) <= 0
 | 
			
		||||
            ? throw new NetAdminUnexpectedException()
 | 
			
		||||
            ? null
 | 
			
		||||
            : (await QueryInternal(new QueryReq<QueryDeptReq> { Filter = new QueryDeptReq { Id = req.Id } }, true)
 | 
			
		||||
                     .ToTreeListAsync()
 | 
			
		||||
                     .ConfigureAwait(false))[0]
 | 
			
		||||
@@ -118,6 +124,10 @@ public sealed class DeptService(DefaultRepository<Sys_Dept> rpo) //
 | 
			
		||||
            ret = ret.AsTreeCte();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (req.Order == Orders.Random) {
 | 
			
		||||
            return ret.OrderByRandom();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
 | 
			
		||||
        if (!req.Prop?.Equals(nameof(req.Filter.Sort), StringComparison.OrdinalIgnoreCase) ?? true) {
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,13 @@ public sealed class DicCatalogService(DefaultRepository<Sys_DicCatalog> rpo) //
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QueryDicCatalogReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        req.ThrowIfInvalid();
 | 
			
		||||
        return QueryInternal(req).CountAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    /// <exception cref="NetAdminInvalidOperationException">The_parent_node_does_not_exist</exception>
 | 
			
		||||
    public async Task<QueryDicCatalogRsp> CreateAsync(CreateDicCatalogReq req)
 | 
			
		||||
@@ -88,7 +95,6 @@ public sealed class DicCatalogService(DefaultRepository<Sys_DicCatalog> rpo) //
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    /// <exception cref="NetAdminInvalidOperationException">The_parent_node_does_not_exist</exception>
 | 
			
		||||
    /// <exception cref="NetAdminUnexpectedException">NetAdminUnexpectedException</exception>
 | 
			
		||||
    public async Task<QueryDicCatalogRsp> UpdateAsync(UpdateDicCatalogReq req)
 | 
			
		||||
    {
 | 
			
		||||
        req.ThrowIfInvalid();
 | 
			
		||||
@@ -98,7 +104,7 @@ public sealed class DicCatalogService(DefaultRepository<Sys_DicCatalog> rpo) //
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (await Rpo.UpdateDiy.SetSource(req).ExecuteAffrowsAsync().ConfigureAwait(false) <= 0) {
 | 
			
		||||
            throw new NetAdminUnexpectedException();
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var ret = await Rpo.Where(a => a.Id == req.Id).ToOneAsync().ConfigureAwait(false);
 | 
			
		||||
@@ -113,9 +119,12 @@ public sealed class DicCatalogService(DefaultRepository<Sys_DicCatalog> rpo) //
 | 
			
		||||
 | 
			
		||||
    private ISelect<Sys_DicCatalog> QueryInternal(QueryReq<QueryDicCatalogReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
 | 
			
		||||
                     .WhereDynamic(req.Filter)
 | 
			
		||||
                     .OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
        var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
 | 
			
		||||
        if (req.Order == Orders.Random) {
 | 
			
		||||
            return ret.OrderByRandom();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
        if (!req.Prop?.Equals(nameof(req.Filter.Id), StringComparison.OrdinalIgnoreCase) ?? true) {
 | 
			
		||||
            ret = ret.OrderByDescending(a => a.Id);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,13 @@ public sealed class DicContentService(DefaultRepository<Sys_DicContent> rpo) //
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QueryDicContentReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        req.ThrowIfInvalid();
 | 
			
		||||
        return QueryInternal(req).CountAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    /// <exception cref="NetAdminInvalidOperationException">Dictionary_directory_does_not_exist</exception>
 | 
			
		||||
    public async Task<QueryDicContentRsp> CreateAsync(CreateDicContentReq req)
 | 
			
		||||
@@ -90,7 +97,6 @@ public sealed class DicContentService(DefaultRepository<Sys_DicContent> rpo) //
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    /// <exception cref="NetAdminInvalidOperationException">Dictionary_directory_does_not_exist</exception>
 | 
			
		||||
    /// <exception cref="NetAdminUnexpectedException">NetAdminUnexpectedException</exception>
 | 
			
		||||
    public async Task<QueryDicContentRsp> UpdateAsync(UpdateDicContentReq req)
 | 
			
		||||
    {
 | 
			
		||||
        req.ThrowIfInvalid();
 | 
			
		||||
@@ -103,7 +109,7 @@ public sealed class DicContentService(DefaultRepository<Sys_DicContent> rpo) //
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (await Rpo.UpdateDiy.SetSource(req).ExecuteAffrowsAsync().ConfigureAwait(false) <= 0) {
 | 
			
		||||
            throw new NetAdminUnexpectedException();
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var ret = await Rpo.Where(a => a.Id == req.Id).ToOneAsync().ConfigureAwait(false);
 | 
			
		||||
@@ -118,9 +124,12 @@ public sealed class DicContentService(DefaultRepository<Sys_DicContent> rpo) //
 | 
			
		||||
 | 
			
		||||
    private ISelect<Sys_DicContent> QueryInternal(QueryReq<QueryDicContentReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
 | 
			
		||||
                     .WhereDynamic(req.Filter)
 | 
			
		||||
                     .OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
        var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
 | 
			
		||||
        if (req.Order == Orders.Random) {
 | 
			
		||||
            return ret.OrderByRandom();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
        if (!req.Prop?.Equals(nameof(req.Filter.Id), StringComparison.OrdinalIgnoreCase) ?? true) {
 | 
			
		||||
            ret = ret.OrderByDescending(a => a.Id);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,13 @@ public sealed class JobRecordService(DefaultRepository<Sys_JobRecord> rpo) //
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QueryJobRecordReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        req.ThrowIfInvalid();
 | 
			
		||||
        return QueryInternal(req).CountAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public async Task<QueryJobRecordRsp> CreateAsync(CreateJobRecordReq req)
 | 
			
		||||
    {
 | 
			
		||||
@@ -106,8 +113,12 @@ public sealed class JobRecordService(DefaultRepository<Sys_JobRecord> rpo) //
 | 
			
		||||
                     .WhereDynamic(req.Filter)
 | 
			
		||||
                     .WhereIf( //
 | 
			
		||||
                         req.Keywords?.Length > 0
 | 
			
		||||
                       , a => a.JobId == req.Keywords.Int64Try(0) || a.Id == req.Keywords.Int64Try(0))
 | 
			
		||||
                     .OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
                       , a => a.JobId == req.Keywords.Int64Try(0) || a.Id == req.Keywords.Int64Try(0));
 | 
			
		||||
        if (req.Order == Orders.Random) {
 | 
			
		||||
            return ret.OrderByRandom();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
        if (!req.Prop?.Equals(nameof(req.Filter.Id), StringComparison.OrdinalIgnoreCase) ?? true) {
 | 
			
		||||
            ret = ret.OrderByDescending(a => a.Id);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,13 @@ public sealed class JobService(DefaultRepository<Sys_Job> rpo, IJobRecordService
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QueryJobReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        req.ThrowIfInvalid();
 | 
			
		||||
        return QueryInternal(req).CountAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public async Task<QueryJobRsp> CreateAsync(CreateJobReq req)
 | 
			
		||||
    {
 | 
			
		||||
@@ -119,13 +126,14 @@ public sealed class JobService(DefaultRepository<Sys_Job> rpo, IJobRecordService
 | 
			
		||||
                                                                     }
 | 
			
		||||
                                           ]
 | 
			
		||||
                                       };
 | 
			
		||||
        var job = await QueryInternal(new QueryReq<QueryJobReq> { DynamicFilter = df, Count = 1 }, true)
 | 
			
		||||
                        .Where(a => !Rpo.Orm.Select<Sys_JobRecord>()
 | 
			
		||||
                                        .As("b")
 | 
			
		||||
                                        .Where(b => b.JobId == a.Id && b.TimeId == a.NextTimeId)
 | 
			
		||||
                                        .Any())
 | 
			
		||||
                        .ToOneAsync()
 | 
			
		||||
                        .ConfigureAwait(false);
 | 
			
		||||
        var job
 | 
			
		||||
            = await QueryInternal(new QueryReq<QueryJobReq> { DynamicFilter = df, Count = 1, Order = Orders.Random })
 | 
			
		||||
                    .Where(a => !Rpo.Orm.Select<Sys_JobRecord>()
 | 
			
		||||
                                    .As("b")
 | 
			
		||||
                                    .Where(b => b.JobId == a.Id && b.TimeId == a.NextTimeId)
 | 
			
		||||
                                    .Any())
 | 
			
		||||
                    .ToOneAsync()
 | 
			
		||||
                    .ConfigureAwait(false);
 | 
			
		||||
        return job == null
 | 
			
		||||
            ? null
 | 
			
		||||
            : await UpdateAsync(job.Adapt<UpdateJobReq>() with {
 | 
			
		||||
@@ -175,7 +183,7 @@ public sealed class JobService(DefaultRepository<Sys_Job> rpo, IJobRecordService
 | 
			
		||||
    {
 | 
			
		||||
        return Rpo.UpdateDiy.Set(a => a.Status == JobStatues.Idle)
 | 
			
		||||
                  .Where(a => a.Status       == JobStatues.Running &&
 | 
			
		||||
                              a.LastExecTime < DateTime.Now.AddSeconds(-Numbers.TIMEOUT_SECS_JOB))
 | 
			
		||||
                              a.LastExecTime < DateTime.Now.AddSeconds(-Numbers.SECS_TIMEOUT_JOB))
 | 
			
		||||
                  .ExecuteAffrowsAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -211,17 +219,23 @@ public sealed class JobService(DefaultRepository<Sys_Job> rpo, IJobRecordService
 | 
			
		||||
                             .GetNextOccurrence(DateTime.UtcNow, TimeZoneInfo.Utc);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private ISelect<Sys_Job> QueryInternal(QueryReq<QueryJobReq> req, bool orderByRandom = false)
 | 
			
		||||
    private ISelect<Sys_Job> QueryInternal(QueryReq<QueryJobReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        var ret = Rpo.Select.Include(a => a.User)
 | 
			
		||||
                     .WhereDynamicFilter(req.DynamicFilter)
 | 
			
		||||
                     .WhereDynamic(req.Filter)
 | 
			
		||||
                     .WhereIf( //
 | 
			
		||||
                         req.Keywords?.Length > 0
 | 
			
		||||
                       , a => a.Id == req.Keywords.Int64Try(0) || a.JobName.Contains(req.Keywords))
 | 
			
		||||
                     .OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
        return !orderByRandom && (!req.Prop?.Equals(nameof(req.Filter.Id), StringComparison.OrdinalIgnoreCase) ?? true)
 | 
			
		||||
            ? ret.OrderByDescending(a => a.LastExecTime)
 | 
			
		||||
            : ret.OrderByRandom();
 | 
			
		||||
                       , a => a.Id == req.Keywords.Int64Try(0) || a.JobName.Contains(req.Keywords));
 | 
			
		||||
        if (req.Order == Orders.Random) {
 | 
			
		||||
            return ret.OrderByRandom();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
        if (!req.Prop?.Equals(nameof(req.Filter.LastExecTime), StringComparison.OrdinalIgnoreCase) ?? true) {
 | 
			
		||||
            ret = ret.OrderByDescending(a => a.LastExecTime);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -25,6 +25,13 @@ public sealed class MenuService(DefaultRepository<Sys_Menu> rpo, IUserService us
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QueryMenuReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        req.ThrowIfInvalid();
 | 
			
		||||
        return QueryInternal(req).CountAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public async Task<QueryMenuRsp> CreateAsync(CreateMenuReq req)
 | 
			
		||||
    {
 | 
			
		||||
@@ -37,12 +44,12 @@ public sealed class MenuService(DefaultRepository<Sys_Menu> rpo, IUserService us
 | 
			
		||||
    public async Task<int> DeleteAsync(DelReq req)
 | 
			
		||||
    {
 | 
			
		||||
        req.ThrowIfInvalid();
 | 
			
		||||
        var effect = await Rpo.DeleteAsync(a => a.Id == req.Id).ConfigureAwait(false);
 | 
			
		||||
        effect += await Rpo.Orm.Delete<Sys_RoleMenu>()
 | 
			
		||||
                           .Where(a => a.MenuId == req.Id)
 | 
			
		||||
                           .ExecuteAffrowsAsync()
 | 
			
		||||
                           .ConfigureAwait(false);
 | 
			
		||||
        return effect;
 | 
			
		||||
        var ret = await Rpo.DeleteAsync(a => a.Id == req.Id).ConfigureAwait(false);
 | 
			
		||||
        _ = await Rpo.Orm.Delete<Sys_RoleMenu>()
 | 
			
		||||
                     .Where(a => a.MenuId == req.Id)
 | 
			
		||||
                     .ExecuteAffrowsAsync()
 | 
			
		||||
                     .ConfigureAwait(false);
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
@@ -76,12 +83,11 @@ public sealed class MenuService(DefaultRepository<Sys_Menu> rpo, IUserService us
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    /// <exception cref="NetAdminUnexpectedException">NetAdminUnexpectedException</exception>
 | 
			
		||||
    public async Task<QueryMenuRsp> UpdateAsync(UpdateMenuReq req)
 | 
			
		||||
    {
 | 
			
		||||
        req.ThrowIfInvalid();
 | 
			
		||||
        if (await Rpo.UpdateDiy.SetSource(req).ExecuteAffrowsAsync().ConfigureAwait(false) <= 0) {
 | 
			
		||||
            throw new NetAdminUnexpectedException();
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var ret = await Rpo.Where(a => a.Id == req.Id).ToOneAsync().ConfigureAwait(false);
 | 
			
		||||
@@ -124,11 +130,12 @@ public sealed class MenuService(DefaultRepository<Sys_Menu> rpo, IUserService us
 | 
			
		||||
 | 
			
		||||
    private ISelect<Sys_Menu> QueryInternal(QueryReq<QueryMenuReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        return Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
 | 
			
		||||
                  .WhereDynamic(req.Filter)
 | 
			
		||||
                  .OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending)
 | 
			
		||||
                  .OrderByDescending(a => a.Sort)
 | 
			
		||||
                  .OrderBy(a => a.Name)
 | 
			
		||||
                  .OrderBy(a => a.Id);
 | 
			
		||||
        var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
 | 
			
		||||
        return req.Order == Orders.Random
 | 
			
		||||
            ? ret.OrderByRandom()
 | 
			
		||||
            : ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending)
 | 
			
		||||
                 .OrderByDescending(a => a.Sort)
 | 
			
		||||
                 .OrderBy(a => a.Name)
 | 
			
		||||
                 .OrderBy(a => a.Id);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -25,6 +25,13 @@ public sealed class RequestLogService(DefaultRepository<Sys_RequestLog> rpo) //
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QueryRequestLogReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        req.ThrowIfInvalid();
 | 
			
		||||
        return QueryInternal(req).CountAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public async Task<QueryRequestLogRsp> CreateAsync(CreateRequestLogReq req)
 | 
			
		||||
    {
 | 
			
		||||
@@ -106,10 +113,12 @@ public sealed class RequestLogService(DefaultRepository<Sys_RequestLog> rpo) //
 | 
			
		||||
 | 
			
		||||
    private ISelect<Sys_RequestLog> QueryInternal(QueryReq<QueryRequestLogReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        var ret = Rpo.Select.Include(a => a.Api)
 | 
			
		||||
                     .WhereDynamicFilter(req.DynamicFilter)
 | 
			
		||||
                     .WhereDynamic(req.Filter)
 | 
			
		||||
                     .OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
        var ret = Rpo.Select.Include(a => a.Api).WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
 | 
			
		||||
        if (req.Order == Orders.Random) {
 | 
			
		||||
            return ret.OrderByRandom();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
 | 
			
		||||
        if (!req.Prop?.Equals(nameof(req.Filter.CreatedTime), StringComparison.OrdinalIgnoreCase) ?? true) {
 | 
			
		||||
            ret = ret.OrderByDescending(a => a.CreatedTime);
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,13 @@ public sealed class RoleService(DefaultRepository<Sys_Role> rpo) //
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QueryRoleReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        req.ThrowIfInvalid();
 | 
			
		||||
        return QueryInternal(req).CountAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public async Task<QueryRoleRsp> CreateAsync(CreateRoleReq req)
 | 
			
		||||
    {
 | 
			
		||||
@@ -116,8 +123,12 @@ public sealed class RoleService(DefaultRepository<Sys_Role> rpo) //
 | 
			
		||||
                     .WhereIf( //
 | 
			
		||||
                         req.Keywords?.Length > 0
 | 
			
		||||
                       , a => a.Id == req.Keywords.Int64Try(0) || a.Name.Contains(req.Keywords) ||
 | 
			
		||||
                              a.Summary.Contains(req.Keywords))
 | 
			
		||||
                     .OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
                              a.Summary.Contains(req.Keywords));
 | 
			
		||||
        if (req.Order == Orders.Random) {
 | 
			
		||||
            return ret.OrderByRandom();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
 | 
			
		||||
        if (!req.Prop?.Equals(nameof(req.Filter.Sort), StringComparison.OrdinalIgnoreCase) ?? true) {
 | 
			
		||||
            ret = ret.OrderByDescending(a => a.Sort);
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,13 @@ public sealed class SiteMsgDeptService(DefaultRepository<Sys_SiteMsgDept> rpo) /
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QuerySiteMsgDeptReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        req.ThrowIfInvalid();
 | 
			
		||||
        return QueryInternal(req).CountAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public async Task<QuerySiteMsgDeptRsp> CreateAsync(CreateSiteMsgDeptReq req)
 | 
			
		||||
    {
 | 
			
		||||
@@ -102,9 +109,12 @@ public sealed class SiteMsgDeptService(DefaultRepository<Sys_SiteMsgDept> rpo) /
 | 
			
		||||
 | 
			
		||||
    private ISelect<Sys_SiteMsgDept> QueryInternal(QueryReq<QuerySiteMsgDeptReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
 | 
			
		||||
                     .WhereDynamic(req.Filter)
 | 
			
		||||
                     .OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
        var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
 | 
			
		||||
        if (req.Order == Orders.Random) {
 | 
			
		||||
            return ret.OrderByRandom();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
        if (!req.Prop?.Equals(nameof(req.Filter.Id), StringComparison.OrdinalIgnoreCase) ?? true) {
 | 
			
		||||
            ret = ret.OrderByDescending(a => a.Id);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,13 @@ public sealed class SiteMsgFlagService(DefaultRepository<Sys_SiteMsgFlag> rpo) /
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QuerySiteMsgFlagReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        req.ThrowIfInvalid();
 | 
			
		||||
        return QueryInternal(req).CountAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public async Task<QuerySiteMsgFlagRsp> CreateAsync(CreateSiteMsgFlagReq req)
 | 
			
		||||
    {
 | 
			
		||||
@@ -108,9 +115,12 @@ public sealed class SiteMsgFlagService(DefaultRepository<Sys_SiteMsgFlag> rpo) /
 | 
			
		||||
 | 
			
		||||
    private ISelect<Sys_SiteMsgFlag> QueryInternal(QueryReq<QuerySiteMsgFlagReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
 | 
			
		||||
                     .WhereDynamic(req.Filter)
 | 
			
		||||
                     .OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
        var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
 | 
			
		||||
        if (req.Order == Orders.Random) {
 | 
			
		||||
            return ret.OrderByRandom();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
        if (!req.Prop?.Equals(nameof(req.Filter.Id), StringComparison.OrdinalIgnoreCase) ?? true) {
 | 
			
		||||
            ret = ret.OrderByDescending(a => a.Id);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,13 @@ public sealed class SiteMsgRoleService(DefaultRepository<Sys_SiteMsgRole> rpo) /
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QuerySiteMsgRoleReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        req.ThrowIfInvalid();
 | 
			
		||||
        return QueryInternal(req).CountAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public async Task<QuerySiteMsgRoleRsp> CreateAsync(CreateSiteMsgRoleReq req)
 | 
			
		||||
    {
 | 
			
		||||
@@ -102,9 +109,12 @@ public sealed class SiteMsgRoleService(DefaultRepository<Sys_SiteMsgRole> rpo) /
 | 
			
		||||
 | 
			
		||||
    private ISelect<Sys_SiteMsgRole> QueryInternal(QueryReq<QuerySiteMsgRoleReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
 | 
			
		||||
                     .WhereDynamic(req.Filter)
 | 
			
		||||
                     .OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
        var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
 | 
			
		||||
        if (req.Order == Orders.Random) {
 | 
			
		||||
            return ret.OrderByRandom();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
        if (!req.Prop?.Equals(nameof(req.Filter.Id), StringComparison.OrdinalIgnoreCase) ?? true) {
 | 
			
		||||
            ret = ret.OrderByDescending(a => a.Id);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -12,8 +12,10 @@ using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
 | 
			
		||||
namespace NetAdmin.SysComponent.Application.Services.Sys;
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc cref="ISiteMsgService" />
 | 
			
		||||
public sealed class SiteMsgService(DefaultRepository<Sys_SiteMsg> rpo, ContextUserInfo contextUserInfo
 | 
			
		||||
                                 , ISiteMsgFlagService            siteMsgFlagService) //
 | 
			
		||||
public sealed class SiteMsgService(
 | 
			
		||||
    DefaultRepository<Sys_SiteMsg> rpo
 | 
			
		||||
  , ContextUserInfo                contextUserInfo
 | 
			
		||||
  , ISiteMsgFlagService            siteMsgFlagService) //
 | 
			
		||||
    : RepositoryService<Sys_SiteMsg, ISiteMsgService>(rpo), ISiteMsgService
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
@@ -30,6 +32,13 @@ public sealed class SiteMsgService(DefaultRepository<Sys_SiteMsg> rpo, ContextUs
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QuerySiteMsgReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        req.ThrowIfInvalid();
 | 
			
		||||
        return QueryInternal(req).CountAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public async Task<QuerySiteMsgRsp> CreateAsync(CreateSiteMsgReq req)
 | 
			
		||||
    {
 | 
			
		||||
@@ -109,7 +118,7 @@ public sealed class SiteMsgService(DefaultRepository<Sys_SiteMsg> rpo, ContextUs
 | 
			
		||||
                         .Count(out var total)
 | 
			
		||||
                         .ToListAsync(a => new {
 | 
			
		||||
                                                   a.CreatedTime
 | 
			
		||||
                                                 , a.Creator
 | 
			
		||||
                                                 , a.CreatedUserName
 | 
			
		||||
                                                 , a.Id
 | 
			
		||||
                                                 , a.MsgType
 | 
			
		||||
                                                 , a.Summary
 | 
			
		||||
@@ -275,8 +284,12 @@ public sealed class SiteMsgService(DefaultRepository<Sys_SiteMsg> rpo, ContextUs
 | 
			
		||||
                     .WhereIf( //
 | 
			
		||||
                         req.Keywords?.Length > 0
 | 
			
		||||
                       , a => a.Id == req.Keywords.Int64Try(0) || a.Title.Contains(req.Keywords) ||
 | 
			
		||||
                              a.Summary.Contains(req.Keywords))
 | 
			
		||||
                     .OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
                              a.Summary.Contains(req.Keywords));
 | 
			
		||||
        if (req.Order == Orders.Random) {
 | 
			
		||||
            return ret.OrderByRandom();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
        if (!req.Prop?.Equals(nameof(req.Filter.Id), StringComparison.OrdinalIgnoreCase) ?? true) {
 | 
			
		||||
            ret = ret.OrderByDescending(a => a.Id);
 | 
			
		||||
        }
 | 
			
		||||
@@ -300,9 +313,9 @@ public sealed class SiteMsgService(DefaultRepository<Sys_SiteMsg> rpo, ContextUs
 | 
			
		||||
                  .WhereDynamicFilter(req.DynamicFilter)
 | 
			
		||||
                  .Where((a, _, c, d, e, f) =>
 | 
			
		||||
                             (SqlExt.EqualIsNull(f.UserSiteMsgStatus) ||
 | 
			
		||||
                              f.UserSiteMsgStatus != UserSiteMsgStatues.Deleted) && (a.MsgType == SiteMsgTypes.Public ||
 | 
			
		||||
                                 c.DeptId == contextUserInfo.DeptId || roleIds.Contains(d.RoleId) ||
 | 
			
		||||
                                 e.UserId == contextUserInfo.Id))
 | 
			
		||||
                              f.UserSiteMsgStatus != UserSiteMsgStatues.Deleted) &&
 | 
			
		||||
                             (a.MsgType == SiteMsgTypes.Public || c.DeptId == contextUserInfo.DeptId ||
 | 
			
		||||
                              roleIds.Contains(d.RoleId)       || e.UserId == contextUserInfo.Id))
 | 
			
		||||
                  .GroupBy((a, _, _, _, _, _) => a.Id);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -26,6 +26,13 @@ public sealed class SiteMsgUserService(DefaultRepository<Sys_SiteMsgUser> rpo) /
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QuerySiteMsgUserReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        req.ThrowIfInvalid();
 | 
			
		||||
        return QueryInternal(req).CountAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public async Task<QuerySiteMsgUserRsp> CreateAsync(CreateSiteMsgUserReq req)
 | 
			
		||||
    {
 | 
			
		||||
@@ -102,9 +109,12 @@ public sealed class SiteMsgUserService(DefaultRepository<Sys_SiteMsgUser> rpo) /
 | 
			
		||||
 | 
			
		||||
    private ISelect<Sys_SiteMsgUser> QueryInternal(QueryReq<QuerySiteMsgUserReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
 | 
			
		||||
                     .WhereDynamic(req.Filter)
 | 
			
		||||
                     .OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
        var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
 | 
			
		||||
        if (req.Order == Orders.Random) {
 | 
			
		||||
            return ret.OrderByRandom();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
        if (!req.Prop?.Equals(nameof(req.Filter.Id), StringComparison.OrdinalIgnoreCase) ?? true) {
 | 
			
		||||
            ret = ret.OrderByDescending(a => a.Id);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,13 @@ public sealed class UserProfileService(DefaultRepository<Sys_UserProfile> rpo) /
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QueryUserProfileReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        req.ThrowIfInvalid();
 | 
			
		||||
        return QueryInternal(req).CountAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public async Task<QueryUserProfileRsp> CreateAsync(CreateUserProfileReq req)
 | 
			
		||||
    {
 | 
			
		||||
@@ -146,18 +153,21 @@ public sealed class UserProfileService(DefaultRepository<Sys_UserProfile> rpo) /
 | 
			
		||||
        QueryReq<QueryUserProfileReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        #pragma warning disable CA1305
 | 
			
		||||
        return Rpo.Orm.Select<Sys_UserProfile, Sys_DicContent, Sys_DicContent, Sys_DicContent, Sys_DicContent>()
 | 
			
		||||
                  .LeftJoin((a, b, _, __, ___) =>
 | 
			
		||||
                                a.NationArea.ToString() == b.Value && b.CatalogId == Numbers.DIC_CATALOG_ID_GEO_AREA)
 | 
			
		||||
                  .LeftJoin((a, _, c, __, ___) =>
 | 
			
		||||
                                a.CompanyArea.ToString() == c.Value && c.CatalogId == Numbers.DIC_CATALOG_ID_GEO_AREA)
 | 
			
		||||
                  .LeftJoin((a, _, __, d, ___) =>
 | 
			
		||||
                                a.HomeArea.ToString() == d.Value && d.CatalogId == Numbers.DIC_CATALOG_ID_GEO_AREA)
 | 
			
		||||
                  .LeftJoin((a, _, __, ___, e) => a.EmergencyContactArea.ToString() == e.Value &&
 | 
			
		||||
                                                  e.CatalogId == Numbers.DIC_CATALOG_ID_GEO_AREA)
 | 
			
		||||
                  .WhereDynamicFilter(req.DynamicFilter)
 | 
			
		||||
                  .OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending)
 | 
			
		||||
                  .OrderByDescending((a, _, __, ___, ____) => a.Id);
 | 
			
		||||
        var ret = Rpo.Orm.Select<Sys_UserProfile, Sys_DicContent, Sys_DicContent, Sys_DicContent, Sys_DicContent>()
 | 
			
		||||
                     .LeftJoin((a, b, _, __, ___) =>
 | 
			
		||||
                                   a.NationArea.ToString() == b.Value && b.CatalogId == Numbers.ID_DIC_CATALOG_GEO_AREA)
 | 
			
		||||
                     .LeftJoin((a, _, c, __, ___) =>
 | 
			
		||||
                                   a.CompanyArea.ToString() == c.Value &&
 | 
			
		||||
                                   c.CatalogId              == Numbers.ID_DIC_CATALOG_GEO_AREA)
 | 
			
		||||
                     .LeftJoin((a, _, __, d, ___) =>
 | 
			
		||||
                                   a.HomeArea.ToString() == d.Value && d.CatalogId == Numbers.ID_DIC_CATALOG_GEO_AREA)
 | 
			
		||||
                     .LeftJoin((a, _, __, ___, e) => a.EmergencyContactArea.ToString() == e.Value &&
 | 
			
		||||
                                                     e.CatalogId == Numbers.ID_DIC_CATALOG_GEO_AREA)
 | 
			
		||||
                     .WhereDynamicFilter(req.DynamicFilter);
 | 
			
		||||
        return req.Order == Orders.Random
 | 
			
		||||
            ? ret.OrderByRandom()
 | 
			
		||||
            : ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending)
 | 
			
		||||
                 .OrderByDescending((a, _, __, ___, ____) => a.Id);
 | 
			
		||||
        #pragma warning restore CA1305
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -13,10 +13,11 @@ using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
 | 
			
		||||
namespace NetAdmin.SysComponent.Application.Services.Sys;
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc cref="IUserService" />
 | 
			
		||||
public sealed class UserService(DefaultRepository<Sys_User> rpo                //
 | 
			
		||||
                              , IUserProfileService         userProfileService //
 | 
			
		||||
                              , IVerifyCodeService          verifyCodeService  //
 | 
			
		||||
                              , IEventPublisher             eventPublisher)    //
 | 
			
		||||
public sealed class UserService(
 | 
			
		||||
    DefaultRepository<Sys_User> rpo                //
 | 
			
		||||
  , IUserProfileService         userProfileService //
 | 
			
		||||
  , IVerifyCodeService          verifyCodeService  //
 | 
			
		||||
  , IEventPublisher             eventPublisher)    //
 | 
			
		||||
    : RepositoryService<Sys_User, IUserService>(rpo), IUserService
 | 
			
		||||
{
 | 
			
		||||
    private readonly Expression<Func<Sys_User, Sys_User>> _selectUserFields = a => new Sys_User {
 | 
			
		||||
@@ -63,6 +64,15 @@ public sealed class UserService(DefaultRepository<Sys_User> rpo                /
 | 
			
		||||
                         .ConfigureAwait(false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QueryUserReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        req.ThrowIfInvalid();
 | 
			
		||||
        #pragma warning disable VSTHRD103
 | 
			
		||||
        return QueryInternal(req).CountAsync();
 | 
			
		||||
        #pragma warning restore VSTHRD103
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public async Task<QueryUserRsp> CreateAsync(CreateUserReq req)
 | 
			
		||||
    {
 | 
			
		||||
@@ -88,20 +98,17 @@ public sealed class UserService(DefaultRepository<Sys_User> rpo                /
 | 
			
		||||
    public async Task<int> DeleteAsync(DelReq req)
 | 
			
		||||
    {
 | 
			
		||||
        req.ThrowIfInvalid();
 | 
			
		||||
        var effect = 0;
 | 
			
		||||
 | 
			
		||||
        // 删除主表
 | 
			
		||||
        effect += await Rpo.DeleteAsync(req.Id).ConfigureAwait(false);
 | 
			
		||||
        var ret = await Rpo.DeleteAsync(req.Id).ConfigureAwait(false);
 | 
			
		||||
 | 
			
		||||
        // 删除分表
 | 
			
		||||
        effect += await Rpo.Orm.Delete<Sys_UserRole>(new { UserId = req.Id })
 | 
			
		||||
                           .ExecuteAffrowsAsync()
 | 
			
		||||
                           .ConfigureAwait(false);
 | 
			
		||||
        _ = await Rpo.Orm.Delete<Sys_UserRole>(new { UserId = req.Id }).ExecuteAffrowsAsync().ConfigureAwait(false);
 | 
			
		||||
 | 
			
		||||
        // 删除档案表
 | 
			
		||||
        effect += await userProfileService.DeleteAsync(req).ConfigureAwait(false);
 | 
			
		||||
        _ = await userProfileService.DeleteAsync(req).ConfigureAwait(false);
 | 
			
		||||
 | 
			
		||||
        return effect;
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
@@ -255,7 +262,7 @@ public sealed class UserService(DefaultRepository<Sys_User> rpo                /
 | 
			
		||||
                     .UpdateColumns(a => a.Avatar)
 | 
			
		||||
                     .ExecuteAffrowsAsync()
 | 
			
		||||
                     .ConfigureAwait(false) <= 0) {
 | 
			
		||||
            throw new NetAdminUnexpectedException();
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var ret = (await QueryAsync(new QueryReq<QueryUserReq> { Filter = new QueryUserReq { Id = UserToken.Id } })
 | 
			
		||||
@@ -289,7 +296,7 @@ public sealed class UserService(DefaultRepository<Sys_User> rpo                /
 | 
			
		||||
                     .UpdateColumns(a => a.Email)
 | 
			
		||||
                     .ExecuteAffrowsAsync()
 | 
			
		||||
                     .ConfigureAwait(false) <= 0) {
 | 
			
		||||
            throw new NetAdminUnexpectedException();
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var ret = (await QueryAsync(new QueryReq<QueryUserReq> { Filter = new QueryUserReq { Id = UserToken.Id } })
 | 
			
		||||
@@ -341,7 +348,7 @@ public sealed class UserService(DefaultRepository<Sys_User> rpo                /
 | 
			
		||||
                     .UpdateColumns(a => a.Mobile)
 | 
			
		||||
                     .ExecuteAffrowsAsync()
 | 
			
		||||
                     .ConfigureAwait(false) <= 0) {
 | 
			
		||||
            throw new NetAdminUnexpectedException();
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var ret = (await QueryAsync(new QueryReq<QueryUserReq> { Filter = new QueryUserReq { Id = UserToken.Id } })
 | 
			
		||||
@@ -370,7 +377,7 @@ public sealed class UserService(DefaultRepository<Sys_User> rpo                /
 | 
			
		||||
                           .UpdateColumns(a => a.Password)
 | 
			
		||||
                           .ExecuteAffrowsAsync()
 | 
			
		||||
                           .ConfigureAwait(false);
 | 
			
		||||
        return ret <= 0 ? throw new NetAdminUnexpectedException() : (uint)ret;
 | 
			
		||||
        return (uint)ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
@@ -478,9 +485,12 @@ public sealed class UserService(DefaultRepository<Sys_User> rpo                /
 | 
			
		||||
 | 
			
		||||
    private ISelect<Sys_User> QueryInternal(QueryReq<QueryUserReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
 | 
			
		||||
                     .WhereDynamic(req.Filter)
 | 
			
		||||
                     .OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
        var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
 | 
			
		||||
        if (req.Order == Orders.Random) {
 | 
			
		||||
            return ret.OrderByRandom();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
        if (!req.Prop?.Equals(nameof(req.Filter.Id), StringComparison.OrdinalIgnoreCase) ?? true) {
 | 
			
		||||
            ret = ret.OrderByDescending(a => a.Id);
 | 
			
		||||
        }
 | 
			
		||||
@@ -511,8 +521,12 @@ public sealed class UserService(DefaultRepository<Sys_User> rpo                /
 | 
			
		||||
                         req.Keywords?.Length > 0
 | 
			
		||||
                       , a => a.Id == req.Keywords.Int64Try(0) || a.UserName.Contains(req.Keywords) ||
 | 
			
		||||
                              a.Mobile.Contains(req.Keywords)  || a.Email.Contains(req.Keywords)    ||
 | 
			
		||||
                              a.Summary.Contains(req.Keywords))
 | 
			
		||||
                     .OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
                              a.Summary.Contains(req.Keywords));
 | 
			
		||||
        if (req.Order == Orders.Random) {
 | 
			
		||||
            return ret.OrderByRandom();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
 | 
			
		||||
        if (!req.Prop?.Equals(nameof(req.Filter.CreatedTime), StringComparison.OrdinalIgnoreCase) ?? true) {
 | 
			
		||||
            ret = ret.OrderByDescending(a => a.CreatedTime);
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,13 @@ public sealed class VerifyCodeService(DefaultRepository<Sys_VerifyCode> rpo, IEv
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QueryVerifyCodeReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        req.ThrowIfInvalid();
 | 
			
		||||
        return QueryInternal(req).CountAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public async Task<QueryVerifyCodeRsp> CreateAsync(CreateVerifyCodeReq req)
 | 
			
		||||
    {
 | 
			
		||||
@@ -180,9 +187,12 @@ public sealed class VerifyCodeService(DefaultRepository<Sys_VerifyCode> rpo, IEv
 | 
			
		||||
 | 
			
		||||
    private ISelect<Sys_VerifyCode> QueryInternal(QueryReq<QueryVerifyCodeReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
 | 
			
		||||
                     .WhereDynamic(req.Filter)
 | 
			
		||||
                     .OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
        var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
 | 
			
		||||
        if (req.Order == Orders.Random) {
 | 
			
		||||
            return ret.OrderByRandom();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
        if (!req.Prop?.Equals(nameof(req.Filter.Id), StringComparison.OrdinalIgnoreCase) ?? true) {
 | 
			
		||||
            ret = ret.OrderByDescending(a => a.Id);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,13 @@ public sealed class ExampleService(DefaultRepository<Tpl_Example> rpo) //
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QueryExampleReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        req.ThrowIfInvalid();
 | 
			
		||||
        return QueryInternal(req).CountAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public async Task<QueryExampleRsp> CreateAsync(CreateExampleReq req)
 | 
			
		||||
    {
 | 
			
		||||
@@ -102,9 +109,12 @@ public sealed class ExampleService(DefaultRepository<Tpl_Example> rpo) //
 | 
			
		||||
 | 
			
		||||
    private ISelect<Tpl_Example> QueryInternal(QueryReq<QueryExampleReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
 | 
			
		||||
                     .WhereDynamic(req.Filter)
 | 
			
		||||
                     .OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
        var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
 | 
			
		||||
        if (req.Order == Orders.Random) {
 | 
			
		||||
            return ret.OrderByRandom();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
        if (!req.Prop?.Equals(nameof(req.Filter.Id), StringComparison.OrdinalIgnoreCase) ?? true) {
 | 
			
		||||
            ret = ret.OrderByDescending(a => a.Id);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,12 @@ public sealed class ApiCache(IDistributedCache cache, IApiService service) //
 | 
			
		||||
        return Service.BulkDeleteAsync(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QueryApiReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        return Service.CountAsync(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<QueryApiRsp> CreateAsync(CreateApiReq req)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ public sealed class CacheCache(IDistributedCache cache, ICacheService service) /
 | 
			
		||||
    {
 | 
			
		||||
        #if !DEBUG
 | 
			
		||||
        return GetOrCreateAsync( //
 | 
			
		||||
            GetCacheKey(string.Empty), Service.CacheStatisticsAsync, TimeSpan.FromMinutes(1));
 | 
			
		||||
            GetCacheKey(string.Empty), Service.CacheStatisticsAsync, TimeSpan.FromSeconds(Numbers.CACHE_SECS_DEFAULT));
 | 
			
		||||
        #else
 | 
			
		||||
        return Service.CacheStatisticsAsync();
 | 
			
		||||
        #endif
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ public sealed class CaptchaCache(IDistributedCache cache, ICaptchaService servic
 | 
			
		||||
    {
 | 
			
		||||
        var captchaRsp = await Service.GetCaptchaImageAsync().ConfigureAwait(false);
 | 
			
		||||
        await CreateAsync(GetCacheKey(captchaRsp.Id, nameof(CaptchaCache)), captchaRsp.SawOffsetX
 | 
			
		||||
,                                                                           TimeSpan.FromMinutes(1))
 | 
			
		||||
                        , TimeSpan.FromSeconds(Numbers.SECS_CACHE_DEFAULT))
 | 
			
		||||
            .ConfigureAwait(false);
 | 
			
		||||
        return captchaRsp;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,12 @@ public sealed class ConfigCache(IDistributedCache cache, IConfigService service)
 | 
			
		||||
        return Service.BulkDeleteAsync(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QueryConfigReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        return Service.CountAsync(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<QueryConfigRsp> CreateAsync(CreateConfigReq req)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,12 @@ public sealed class DeptCache(IDistributedCache cache, IDeptService service) //
 | 
			
		||||
        return Service.BulkDeleteAsync(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QueryDeptReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        return Service.CountAsync(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<QueryDeptRsp> CreateAsync(CreateDeptReq req)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -65,7 +65,7 @@ public sealed class DicCache(IDistributedCache cache, IDicService service) //
 | 
			
		||||
        #if !DEBUG
 | 
			
		||||
        return GetOrCreateAsync(                                                  //
 | 
			
		||||
            GetCacheKey(req.GetHashCode().ToString(CultureInfo.InvariantCulture)) //
 | 
			
		||||
          , () => Service.GetDicValueAsync(req), TimeSpan.FromMinutes(1));
 | 
			
		||||
          , () => Service.GetDicValueAsync(req), TimeSpan.FromSeconds(Numbers.CACHE_SECS_DEFAULT));
 | 
			
		||||
        #else
 | 
			
		||||
        return Service.GetDicValueAsync(req);
 | 
			
		||||
        #endif
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,12 @@ public sealed class DicCatalogCache(IDistributedCache cache, IDicCatalogService
 | 
			
		||||
        return Service.BulkDeleteAsync(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QueryDicCatalogReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        return Service.CountAsync(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<QueryDicCatalogRsp> CreateAsync(CreateDicCatalogReq req)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,12 @@ public sealed class DicContentCache(IDistributedCache cache, IDicContentService
 | 
			
		||||
        return Service.BulkDeleteAsync(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QueryDicContentReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        return Service.CountAsync(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<QueryDicContentRsp> CreateAsync(CreateDicContentReq req)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,12 @@ public sealed class JobCache(IDistributedCache cache, IJobService service)
 | 
			
		||||
        return Service.BulkDeleteAsync(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QueryJobReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        return Service.CountAsync(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<QueryJobRsp> CreateAsync(CreateJobReq req)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,12 @@ public sealed class JobRecordCache(IDistributedCache cache, IJobRecordService se
 | 
			
		||||
        return Service.BulkDeleteAsync(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QueryJobRecordReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        return Service.CountAsync(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<QueryJobRecordRsp> CreateAsync(CreateJobRecordReq req)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,12 @@ public sealed class MenuCache(IDistributedCache cache, IMenuService service) //
 | 
			
		||||
        return Service.BulkDeleteAsync(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QueryMenuReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        return Service.CountAsync(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<QueryMenuRsp> CreateAsync(CreateMenuReq req)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,12 @@ public sealed class RequestLogCache(IDistributedCache cache, IRequestLogService
 | 
			
		||||
        return Service.BulkDeleteAsync(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<long> CountAsync(QueryReq<QueryRequestLogReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        return Service.CountAsync(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<QueryRequestLogRsp> CreateAsync(CreateRequestLogReq req)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user