mirror of
				https://gitee.com/ThingsGateway/ThingsGateway.git
				synced 2025-11-04 01:23:58 +08:00 
			
		
		
		
	Compare commits
	
		
			4 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					b12e923c99 | ||
| 
						 | 
					ab33eed8d3 | ||
| 
						 | 
					d930a9a8eb | ||
| 
						 | 
					af381fce12 | 
@@ -126,35 +126,8 @@ dotnet_style_qualification_for_property = false:silent
 | 
				
			|||||||
dotnet_style_qualification_for_method = false:silent
 | 
					dotnet_style_qualification_for_method = false:silent
 | 
				
			||||||
dotnet_style_qualification_for_event = false:silent
 | 
					dotnet_style_qualification_for_event = false:silent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					dotnet_diagnostic.RCS1146.severity = warning
 | 
				
			||||||
dotnet_diagnostic.CA2208.severity = none
 | 
					dotnet_diagnostic.RCS1059.severity = none
 | 
				
			||||||
dotnet_diagnostic.CA2008.severity = none
 | 
					dotnet_diagnostic.RCS1138.severity = suggestion
 | 
				
			||||||
dotnet_diagnostic.CA1812.severity = none
 | 
					 | 
				
			||||||
dotnet_diagnostic.CA1508.severity = none
 | 
					 | 
				
			||||||
dotnet_diagnostic.CA1512.severity = none
 | 
					 | 
				
			||||||
dotnet_diagnostic.CA1513.severity = none
 | 
					 | 
				
			||||||
dotnet_diagnostic.CA1810.severity = none
 | 
					 | 
				
			||||||
dotnet_diagnostic.CA1814.severity = none
 | 
					 | 
				
			||||||
dotnet_diagnostic.CA1815.severity = none
 | 
					 | 
				
			||||||
dotnet_diagnostic.CA1835.severity = none
 | 
					 | 
				
			||||||
dotnet_diagnostic.CA1819.severity = none
 | 
					 | 
				
			||||||
dotnet_diagnostic.CA1823.severity = none
 | 
					 | 
				
			||||||
dotnet_diagnostic.CA2002.severity = none
 | 
					 | 
				
			||||||
dotnet_diagnostic.CA5350.severity = none
 | 
					 | 
				
			||||||
dotnet_diagnostic.CA5351.severity = none
 | 
					 | 
				
			||||||
dotnet_diagnostic.CA5358.severity = none
 | 
					 | 
				
			||||||
dotnet_diagnostic.CA5384.severity = none
 | 
					 | 
				
			||||||
dotnet_diagnostic.CA5392.severity = none
 | 
					 | 
				
			||||||
dotnet_diagnostic.CA1805.severity = none
 | 
					 | 
				
			||||||
dotnet_diagnostic.CA1851.severity = none
 | 
					 | 
				
			||||||
dotnet_diagnostic.CA1510.severity = none
 | 
					 | 
				
			||||||
dotnet_diagnostic.CA5401.severity = none
 | 
					 | 
				
			||||||
dotnet_diagnostic.CA2022.severity = none
 | 
					 | 
				
			||||||
dotnet_diagnostic.CA1848.severity = none
 | 
					 | 
				
			||||||
dotnet_diagnostic.CA2000.severity = none
 | 
					 | 
				
			||||||
dotnet_diagnostic.CA5394.severity = none
 | 
					 | 
				
			||||||
dotnet_diagnostic.CA3003.severity = none
 | 
					 | 
				
			||||||
dotnet_diagnostic.CA1515.severity = none
 | 
					 | 
				
			||||||
dotnet_diagnostic.CA1849.severity = none
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
dotnet_code_quality.CA1822.api_surface = private, internal
 | 
					dotnet_code_quality.CA1822.api_surface = private, internal
 | 
				
			||||||
@@ -13,14 +13,16 @@ namespace ThingsGateway.Admin.Application;
 | 
				
			|||||||
/// <summary>
 | 
					/// <summary>
 | 
				
			||||||
/// 需要角色授权权限
 | 
					/// 需要角色授权权限
 | 
				
			||||||
/// </summary>
 | 
					/// </summary>
 | 
				
			||||||
[AttributeUsage(AttributeTargets.Class)]
 | 
					[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
 | 
				
			||||||
public sealed class RolePermissionAttribute : Attribute
 | 
					public sealed class RolePermissionAttribute : Attribute
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// <summary>
 | 
					/// <summary>
 | 
				
			||||||
/// 忽略角色授权权限
 | 
					/// 忽略角色授权权限
 | 
				
			||||||
/// </summary>
 | 
					/// </summary>
 | 
				
			||||||
 | 
					[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
 | 
				
			||||||
public sealed class IgnoreRolePermissionAttribute : Attribute
 | 
					public sealed class IgnoreRolePermissionAttribute : Attribute
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,7 +11,7 @@
 | 
				
			|||||||
namespace ThingsGateway.Admin.Application;
 | 
					namespace ThingsGateway.Admin.Application;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
					[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
				
			||||||
public class CacheConst
 | 
					public static class CacheConst
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// Token表缓存Key
 | 
					    /// Token表缓存Key
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,7 @@ namespace ThingsGateway.Admin.Application;
 | 
				
			|||||||
/// <summary>
 | 
					/// <summary>
 | 
				
			||||||
/// 通讯器常量
 | 
					/// 通讯器常量
 | 
				
			||||||
/// </summary>
 | 
					/// </summary>
 | 
				
			||||||
public class HubConst
 | 
					public static class HubConst
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// 系统HubUrl
 | 
					    /// 系统HubUrl
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
 | 
				
			|||||||
/// 资源表常量
 | 
					/// 资源表常量
 | 
				
			||||||
/// </summary>
 | 
					/// </summary>
 | 
				
			||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
					[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
				
			||||||
public class ResourceConst
 | 
					public static class ResourceConst
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// 系统内置编码
 | 
					    /// 系统内置编码
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
 | 
				
			|||||||
/// 角色常量
 | 
					/// 角色常量
 | 
				
			||||||
/// </summary>
 | 
					/// </summary>
 | 
				
			||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
					[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
				
			||||||
public class RoleConst
 | 
					public static class RoleConst
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// api角色
 | 
					    /// api角色
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
 | 
				
			|||||||
/// SqlSugar系统常量
 | 
					/// SqlSugar系统常量
 | 
				
			||||||
/// </summary>
 | 
					/// </summary>
 | 
				
			||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
					[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
				
			||||||
public class SqlSugarConst
 | 
					public static class SqlSugarConst
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// DB_Admin
 | 
					    /// DB_Admin
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,14 +1,4 @@
 | 
				
			|||||||
// ------------------------------------------------------------------------
 | 
					
 | 
				
			||||||
// 版权信息
 | 
					 | 
				
			||||||
// 版权归百小僧及百签科技(广东)有限公司所有。
 | 
					 | 
				
			||||||
// 所有权利保留。
 | 
					 | 
				
			||||||
// 官方网站:https://baiqian.com
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// 许可证信息
 | 
					 | 
				
			||||||
// 项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。
 | 
					 | 
				
			||||||
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
 | 
					 | 
				
			||||||
// ------------------------------------------------------------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using ThingsGateway.DependencyInjection;
 | 
					using ThingsGateway.DependencyInjection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace System;
 | 
					namespace System;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,14 +1,4 @@
 | 
				
			|||||||
// ------------------------------------------------------------------------
 | 
					
 | 
				
			||||||
// 版权信息
 | 
					 | 
				
			||||||
// 版权归百小僧及百签科技(广东)有限公司所有。
 | 
					 | 
				
			||||||
// 所有权利保留。
 | 
					 | 
				
			||||||
// 官方网站:https://baiqian.com
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// 许可证信息
 | 
					 | 
				
			||||||
// 项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。
 | 
					 | 
				
			||||||
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
 | 
					 | 
				
			||||||
// ------------------------------------------------------------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using ThingsGateway.DependencyInjection;
 | 
					using ThingsGateway.DependencyInjection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace System;
 | 
					namespace System;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,83 +33,6 @@ public class AdminOAuthHandler<TOptions>(
 | 
				
			|||||||
) : OAuthHandler<TOptions>(options, logger, encoder)
 | 
					) : OAuthHandler<TOptions>(options, logger, encoder)
 | 
				
			||||||
    where TOptions : AdminOAuthOptions, new()
 | 
					    where TOptions : AdminOAuthOptions, new()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    private async Task<LoginEvent> GetLogin()
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        var sysUser = await sysUserService.GetUserByIdAsync(RoleConst.SuperAdminId).ConfigureAwait(false);//获取用户信息
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        var appConfig = await configService.GetAppConfigAsync().ConfigureAwait(false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        var expire = appConfig.LoginPolicy.VerificatExpireTime;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        var loginEvent = new LoginEvent
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Ip = appService.RemoteIpAddress,
 | 
					 | 
				
			||||||
            Device = appService.UserAgent?.Platform,
 | 
					 | 
				
			||||||
            Expire = expire,
 | 
					 | 
				
			||||||
            SysUser = sysUser,
 | 
					 | 
				
			||||||
            VerificatId = CommonUtils.GetSingleId()
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        //获取verificat列表
 | 
					 | 
				
			||||||
        var tokenTimeout = loginEvent.DateTime.AddMinutes(loginEvent.Expire);
 | 
					 | 
				
			||||||
        //生成verificat信息
 | 
					 | 
				
			||||||
        var verificatInfo = new VerificatInfo
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Device = loginEvent.Device,
 | 
					 | 
				
			||||||
            Expire = loginEvent.Expire,
 | 
					 | 
				
			||||||
            VerificatTimeout = tokenTimeout,
 | 
					 | 
				
			||||||
            Id = loginEvent.VerificatId,
 | 
					 | 
				
			||||||
            UserId = loginEvent.SysUser.Id,
 | 
					 | 
				
			||||||
            LoginIp = loginEvent.Ip,
 | 
					 | 
				
			||||||
            LoginTime = loginEvent.DateTime
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        //添加到verificat列表
 | 
					 | 
				
			||||||
        verificatInfoService.Add(verificatInfo);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return loginEvent;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    /// <summary>
 | 
					 | 
				
			||||||
    /// 登录事件
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    /// <param name="loginEvent"></param>
 | 
					 | 
				
			||||||
    /// <returns></returns>
 | 
					 | 
				
			||||||
    private async Task UpdateUser(LoginEvent loginEvent)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        var sysUser = loginEvent.SysUser;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region 登录/密码策略
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        var key = CacheConst.Cache_LoginErrorCount + sysUser.Account;//获取登录错误次数Key值
 | 
					 | 
				
			||||||
        App.CacheService.Remove(key);//移除登录错误次数
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        //获取用户verificat列表
 | 
					 | 
				
			||||||
        var userToken = verificatInfoService.GetOne(loginEvent.VerificatId);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion 登录/密码策略
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #region 重新赋值属性,设置本次登录信息为最新的信息
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        sysUser.LastLoginIp = sysUser.LatestLoginIp;
 | 
					 | 
				
			||||||
        sysUser.LastLoginTime = sysUser.LatestLoginTime;
 | 
					 | 
				
			||||||
        sysUser.LatestLoginIp = loginEvent.Ip;
 | 
					 | 
				
			||||||
        sysUser.LatestLoginTime = loginEvent.DateTime;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        #endregion 重新赋值属性,设置本次登录信息为最新的信息
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        using var db = DbContext.Db.GetConnectionScopeWithAttr<SysUser>().CopyNew();
 | 
					 | 
				
			||||||
        //更新用户登录信息
 | 
					 | 
				
			||||||
        if (await db.Updateable(sysUser).UpdateColumns(it => new
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            it.LastLoginIp,
 | 
					 | 
				
			||||||
            it.LastLoginTime,
 | 
					 | 
				
			||||||
            it.LatestLoginIp,
 | 
					 | 
				
			||||||
            it.LatestLoginTime,
 | 
					 | 
				
			||||||
        }).ExecuteCommandAsync().ConfigureAwait(false) > 0)
 | 
					 | 
				
			||||||
            App.CacheService.HashAdd(CacheConst.Cache_SysUser, sysUser.Id.ToString(), sysUser);//更新Cache信息
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static AdminOAuthHandler()
 | 
					    static AdminOAuthHandler()
 | 
				
			||||||
@@ -152,7 +75,6 @@ public class AdminOAuthHandler<TOptions>(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected override async Task<AuthenticationTicket> CreateTicketAsync(
 | 
					    protected override async Task<AuthenticationTicket> CreateTicketAsync(
 | 
				
			||||||
        ClaimsIdentity identity,
 | 
					        ClaimsIdentity identity,
 | 
				
			||||||
        AuthenticationProperties properties,
 | 
					        AuthenticationProperties properties,
 | 
				
			||||||
@@ -160,14 +82,17 @@ public class AdminOAuthHandler<TOptions>(
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        properties.RedirectUri = Options.HomePath;
 | 
					        properties.RedirectUri = Options.HomePath;
 | 
				
			||||||
        properties.IsPersistent = true;
 | 
					        properties.IsPersistent = true;
 | 
				
			||||||
 | 
					        var appConfig = await configService.GetAppConfigAsync().ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int expire = appConfig.LoginPolicy.VerificatExpireTime;
 | 
				
			||||||
        if (!string.IsNullOrEmpty(tokens.ExpiresIn) && int.TryParse(tokens.ExpiresIn, out var result))
 | 
					        if (!string.IsNullOrEmpty(tokens.ExpiresIn) && int.TryParse(tokens.ExpiresIn, out var result))
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            properties.ExpiresUtc = TimeProvider.System.GetUtcNow().AddSeconds(result);
 | 
					            properties.ExpiresUtc = TimeProvider.System.GetUtcNow().AddSeconds(result);
 | 
				
			||||||
 | 
					            expire = (int)(result / 60.0);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        var user = await HandleUserInfoAsync(tokens).ConfigureAwait(false);
 | 
					        var user = await HandleUserInfoAsync(tokens).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var loginEvent = await GetLogin().ConfigureAwait(false);
 | 
					        var loginEvent = await GetLogin(expire).ConfigureAwait(false);
 | 
				
			||||||
        await UpdateUser(loginEvent).ConfigureAwait(false);
 | 
					        await UpdateUser(loginEvent).ConfigureAwait(false);
 | 
				
			||||||
        identity.AddClaim(new Claim(ClaimConst.VerificatId, loginEvent.VerificatId.ToString()));
 | 
					        identity.AddClaim(new Claim(ClaimConst.VerificatId, loginEvent.VerificatId.ToString()));
 | 
				
			||||||
        identity.AddClaim(new Claim(ClaimConst.UserId, RoleConst.SuperAdminId.ToString()));
 | 
					        identity.AddClaim(new Claim(ClaimConst.UserId, RoleConst.SuperAdminId.ToString()));
 | 
				
			||||||
@@ -222,29 +147,6 @@ public class AdminOAuthHandler<TOptions>(
 | 
				
			|||||||
        return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name);
 | 
					        return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// <summary>刷新 Token 方法</summary>
 | 
					 | 
				
			||||||
    protected virtual async Task<OAuthTokenResponse> RefreshTokenAsync(OAuthTokenResponse oAuthToken)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        var query = new Dictionary<string, string>
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            { "refresh_token", oAuthToken.RefreshToken },
 | 
					 | 
				
			||||||
            { "grant_type", "refresh_token" }
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        var request = new HttpRequestMessage(HttpMethod.Post, QueryHelpers.AddQueryString(Options.TokenEndpoint, query));
 | 
					 | 
				
			||||||
        request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        var response = await Backchannel.SendAsync(request, Context.RequestAborted).ConfigureAwait(false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (response.IsSuccessStatusCode)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return OAuthTokenResponse.Success(JsonDocument.Parse(content));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return OAuthTokenResponse.Failed(new OAuthTokenException($"OAuth token endpoint failure: {await Display(response).ConfigureAwait(false)}"));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// <summary>处理用户信息方法</summary>
 | 
					    /// <summary>处理用户信息方法</summary>
 | 
				
			||||||
    protected virtual async Task<JsonElement> HandleUserInfoAsync(OAuthTokenResponse tokens)
 | 
					    protected virtual async Task<JsonElement> HandleUserInfoAsync(OAuthTokenResponse tokens)
 | 
				
			||||||
@@ -283,7 +185,93 @@ public class AdminOAuthHandler<TOptions>(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return output.ToString();
 | 
					        return output.ToString();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private async Task<LoginEvent> GetLogin(int expire)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        var sysUser = await sysUserService.GetUserByIdAsync(RoleConst.SuperAdminId).ConfigureAwait(false);//获取用户信息
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var loginEvent = new LoginEvent
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Ip = appService.RemoteIpAddress,
 | 
				
			||||||
 | 
					            Device = appService.UserAgent?.Platform,
 | 
				
			||||||
 | 
					            Expire = expire,
 | 
				
			||||||
 | 
					            SysUser = sysUser,
 | 
				
			||||||
 | 
					            VerificatId = CommonUtils.GetSingleId()
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //获取verificat列表
 | 
				
			||||||
 | 
					        var tokenTimeout = loginEvent.DateTime.AddMinutes(loginEvent.Expire);
 | 
				
			||||||
 | 
					        //生成verificat信息
 | 
				
			||||||
 | 
					        var verificatInfo = new VerificatInfo
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Device = loginEvent.Device,
 | 
				
			||||||
 | 
					            Expire = loginEvent.Expire,
 | 
				
			||||||
 | 
					            VerificatTimeout = tokenTimeout,
 | 
				
			||||||
 | 
					            Id = loginEvent.VerificatId,
 | 
				
			||||||
 | 
					            UserId = loginEvent.SysUser.Id,
 | 
				
			||||||
 | 
					            LoginIp = loginEvent.Ip,
 | 
				
			||||||
 | 
					            LoginTime = loginEvent.DateTime
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //添加到verificat列表
 | 
				
			||||||
 | 
					        verificatInfoService.Add(verificatInfo);
 | 
				
			||||||
 | 
					        return loginEvent;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// <summary>
 | 
				
			||||||
 | 
					    /// 登录事件
 | 
				
			||||||
 | 
					    /// </summary>
 | 
				
			||||||
 | 
					    /// <param name="loginEvent"></param>
 | 
				
			||||||
 | 
					    /// <returns></returns>
 | 
				
			||||||
 | 
					    private async Task UpdateUser(LoginEvent loginEvent)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        var sysUser = loginEvent.SysUser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #region 登录/密码策略
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var key = CacheConst.Cache_LoginErrorCount + sysUser.Account;//获取登录错误次数Key值
 | 
				
			||||||
 | 
					        App.CacheService.Remove(key);//移除登录错误次数
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //获取用户verificat列表
 | 
				
			||||||
 | 
					        var userToken = verificatInfoService.GetOne(loginEvent.VerificatId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #endregion 登录/密码策略
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #region 重新赋值属性,设置本次登录信息为最新的信息
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        sysUser.LastLoginIp = sysUser.LatestLoginIp;
 | 
				
			||||||
 | 
					        sysUser.LastLoginTime = sysUser.LatestLoginTime;
 | 
				
			||||||
 | 
					        sysUser.LatestLoginIp = loginEvent.Ip;
 | 
				
			||||||
 | 
					        sysUser.LatestLoginTime = loginEvent.DateTime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #endregion 重新赋值属性,设置本次登录信息为最新的信息
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        using var db = DbContext.Db.GetConnectionScopeWithAttr<SysUser>().CopyNew();
 | 
				
			||||||
 | 
					        //更新用户登录信息
 | 
				
			||||||
 | 
					        if (await db.Updateable(sysUser).UpdateColumns(it => new
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            it.LastLoginIp,
 | 
				
			||||||
 | 
					            it.LastLoginTime,
 | 
				
			||||||
 | 
					            it.LatestLoginIp,
 | 
				
			||||||
 | 
					            it.LatestLoginTime,
 | 
				
			||||||
 | 
					        }).ExecuteCommandAsync().ConfigureAwait(false) > 0)
 | 
				
			||||||
 | 
					            App.CacheService.HashAdd(CacheConst.Cache_SysUser, sysUser.Id.ToString(), sysUser);//更新Cache信息
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// <summary>自定义 Token 异常</summary>
 | 
					/// <summary>自定义 Token 异常</summary>
 | 
				
			||||||
public class OAuthTokenException(string message) : Exception(message);
 | 
					public class OAuthTokenException : Exception
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public OAuthTokenException() : base()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public OAuthTokenException(string? message, Exception? innerException) : base(message, innerException)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public OAuthTokenException(string? message) : base(message)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Authentication.OAuth;
 | 
				
			|||||||
using Microsoft.AspNetCore.WebUtilities;
 | 
					using Microsoft.AspNetCore.WebUtilities;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using System.Net.Http.Headers;
 | 
					using System.Net.Http.Headers;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
using System.Text.Json;
 | 
					using System.Text.Json;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace ThingsGateway.Admin.Application;
 | 
					namespace ThingsGateway.Admin.Application;
 | 
				
			||||||
@@ -32,6 +33,42 @@ public class GiteeOAuthOptions : AdminOAuthOptions
 | 
				
			|||||||
            context.Response.Redirect(context.RedirectUri);
 | 
					            context.Response.Redirect(context.RedirectUri);
 | 
				
			||||||
            return Task.CompletedTask;
 | 
					            return Task.CompletedTask;
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// <summary>刷新 Token 方法</summary>
 | 
				
			||||||
 | 
					    protected virtual async Task<OAuthTokenResponse> RefreshTokenAsync(TicketReceivedContext ticketReceivedContext, string refreshToken)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        var query = new Dictionary<string, string>
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            { "refresh_token", refreshToken },
 | 
				
			||||||
 | 
					            { "grant_type", "refresh_token" }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var request = new HttpRequestMessage(HttpMethod.Post, QueryHelpers.AddQueryString(TokenEndpoint, query));
 | 
				
			||||||
 | 
					        request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var response = await Backchannel.SendAsync(request, ticketReceivedContext.HttpContext.RequestAborted).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (response.IsSuccessStatusCode)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return OAuthTokenResponse.Success(JsonDocument.Parse(content));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return OAuthTokenResponse.Failed(new OAuthTokenException($"OAuth token endpoint failure: {await Display(response).ConfigureAwait(false)}"));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// <summary>生成错误信息方法</summary>
 | 
				
			||||||
 | 
					    protected static async Task<string> Display(HttpResponseMessage response)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        var output = new StringBuilder();
 | 
				
			||||||
 | 
					        output.Append($"Status: {response.StatusCode}; ");
 | 
				
			||||||
 | 
					        output.Append($"Headers: {response.Headers}; ");
 | 
				
			||||||
 | 
					        output.Append($"Body: {await response.Content.ReadAsStringAsync().ConfigureAwait(false)};");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return output.ToString();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public override string GetName(JsonElement element)
 | 
					    public override string GetName(JsonElement element)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,7 @@ namespace ThingsGateway.Admin.Application;
 | 
				
			|||||||
/// <summary>
 | 
					/// <summary>
 | 
				
			||||||
/// 日志常量
 | 
					/// 日志常量
 | 
				
			||||||
/// </summary>
 | 
					/// </summary>
 | 
				
			||||||
public class LoggingConst
 | 
					public static class LoggingConst
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// 分类
 | 
					    /// 分类
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -87,7 +87,7 @@ public class BlazorAuthenticationHandler : AppAuthorizeHandler
 | 
				
			|||||||
            var roles = await _sysRoleService.GetRoleListByUserIdAsync(userId).ConfigureAwait(false);
 | 
					            var roles = await _sysRoleService.GetRoleListByUserIdAsync(userId).ConfigureAwait(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //这里鉴别用户使能状态
 | 
					            //这里鉴别用户使能状态
 | 
				
			||||||
            if (user == null || !user.Status)
 | 
					            if (user?.Status != true)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                return false;
 | 
					                return false;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -137,7 +137,7 @@ public class BlazorAuthenticationHandler : AppAuthorizeHandler
 | 
				
			|||||||
        else
 | 
					        else
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            //这里鉴别用户使能状态
 | 
					            //这里鉴别用户使能状态
 | 
				
			||||||
            if (user == null || !user.Status)
 | 
					            if (user?.Status != true)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                return false;
 | 
					                return false;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -334,7 +334,7 @@ internal sealed class SysResourceService : BaseService<SysResource>, ISysResourc
 | 
				
			|||||||
            flatList.Add(node);
 | 
					            flatList.Add(node);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // 如果当前节点有子节点,则递归处理每个子节点
 | 
					            // 如果当前节点有子节点,则递归处理每个子节点
 | 
				
			||||||
            if (node.Children != null && node.Children.Count > 0)
 | 
					            if (node.Children?.Count > 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                foreach (var child in node.Children)
 | 
					                foreach (var child in node.Children)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,7 +47,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	<ItemGroup>
 | 
						<ItemGroup>
 | 
				
			||||||
		<ProjectReference Include="..\ThingsGateway.Razor\ThingsGateway.Razor.csproj" />
 | 
							<ProjectReference Include="..\ThingsGateway.Razor\ThingsGateway.Razor.csproj" />
 | 
				
			||||||
		<ProjectReference Include="..\ThingsGateway.SqlSugar\ThingsGateway.SqlSugar.csproj" />
 | 
							<ProjectReference Include="..\ThingsGateway.DB\ThingsGateway.DB.csproj" />
 | 
				
			||||||
	</ItemGroup>
 | 
						</ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</Project>
 | 
					</Project>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,7 @@ using Microsoft.Extensions.DependencyInjection;
 | 
				
			|||||||
namespace ThingsGateway.Admin.Application;
 | 
					namespace ThingsGateway.Admin.Application;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
					[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
				
			||||||
public class ClearTokenUtil
 | 
					public static class ClearTokenUtil
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    private static IRelationService RelationService;
 | 
					    private static IRelationService RelationService;
 | 
				
			||||||
    private static ISysUserService SysUserService;
 | 
					    private static ISysUserService SysUserService;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,7 @@ using Microsoft.Extensions.DependencyInjection;
 | 
				
			|||||||
namespace ThingsGateway.Admin.Application;
 | 
					namespace ThingsGateway.Admin.Application;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
					[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
				
			||||||
public class NoticeUtil
 | 
					public static class NoticeUtil
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    private static INoticeService NoticeService;
 | 
					    private static INoticeService NoticeService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/// <inheritdoc/>
 | 
					/// <inheritdoc/>
 | 
				
			||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
					[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
				
			||||||
public class OpenApiUtil
 | 
					public static class OpenApiUtil
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// 构建树节点,传入的列表已经是树结构
 | 
					    /// 构建树节点,传入的列表已经是树结构
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/// <inheritdoc/>
 | 
					/// <inheritdoc/>
 | 
				
			||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
					[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
				
			||||||
public class OrgUtil
 | 
					public static class OrgUtil
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// 构造选择项,ID/TITLE
 | 
					    /// 构造选择项,ID/TITLE
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/// <inheritdoc/>
 | 
					/// <inheritdoc/>
 | 
				
			||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
					[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
				
			||||||
public class PositionUtil
 | 
					public static class PositionUtil
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/// <inheritdoc/>
 | 
					/// <inheritdoc/>
 | 
				
			||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
					[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
				
			||||||
public class RoleUtil
 | 
					public static class RoleUtil
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/// <inheritdoc/>
 | 
					/// <inheritdoc/>
 | 
				
			||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
					[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
				
			||||||
public class UserUtil
 | 
					public static class UserUtil
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ using ThingsGateway.Extension.Generic;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace ThingsGateway.Admin.Application;
 | 
					namespace ThingsGateway.Admin.Application;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class VerificatInfoUtil
 | 
					public static class VerificatInfoUtil
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    private static IVerificatInfoService VerificatInfoService { get; set; }
 | 
					    private static IVerificatInfoService VerificatInfoService { get; set; }
 | 
				
			||||||
    static VerificatInfoUtil()
 | 
					    static VerificatInfoUtil()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,7 +11,7 @@
 | 
				
			|||||||
namespace ThingsGateway.Admin.Razor;
 | 
					namespace ThingsGateway.Admin.Razor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
					[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
				
			||||||
public class AdminOperConst
 | 
					public static class AdminOperConst
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public const string Add = "新增";
 | 
					    public const string Add = "新增";
 | 
				
			||||||
    public const string Delete = "删除";
 | 
					    public const string Delete = "删除";
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,7 +48,7 @@ public partial class SysUserAvatarEdit : IDisposable
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private async Task OnAvatarUpload(UploadFile file)
 | 
					    private async Task OnAvatarUpload(UploadFile file)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (file != null && file.File != null)
 | 
					        if (file?.File != null)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var format = file.File.ContentType;
 | 
					            var format = file.File.ContentType;
 | 
				
			||||||
            ReadAvatarToken ??= new CancellationTokenSource();
 | 
					            ReadAvatarToken ??= new CancellationTokenSource();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,7 +48,7 @@ public partial class UserInfoEditComponent
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private async Task OnAvatarUpload(UploadFile file)
 | 
					    private async Task OnAvatarUpload(UploadFile file)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (file != null && file.File != null)
 | 
					        if (file?.File != null)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var format = file.File.ContentType;
 | 
					            var format = file.File.ContentType;
 | 
				
			||||||
            ReadAvatarToken ??= new CancellationTokenSource();
 | 
					            ReadAvatarToken ??= new CancellationTokenSource();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,7 +16,7 @@ namespace ThingsGateway.Admin.Razor;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/// <inheritdoc/>
 | 
					/// <inheritdoc/>
 | 
				
			||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
					[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
				
			||||||
public class ResourceUtil
 | 
					public static class ResourceUtil
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +1 @@
 | 
				
			|||||||
// ------------------------------------------------------------------------
 | 
					global using ThingsGateway.Admin.Application;
 | 
				
			||||||
// 版权信息
 | 
					 | 
				
			||||||
// 版权归百小僧及百签科技(广东)有限公司所有。
 | 
					 | 
				
			||||||
// 所有权利保留。
 | 
					 | 
				
			||||||
// 官方网站:https://baiqian.com
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// 许可证信息
 | 
					 | 
				
			||||||
// 项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。
 | 
					 | 
				
			||||||
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
 | 
					 | 
				
			||||||
// ------------------------------------------------------------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
global using ThingsGateway.Admin.Application;
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Application;
 | 
				
			|||||||
/// 授权用户常量
 | 
					/// 授权用户常量
 | 
				
			||||||
/// </summary>
 | 
					/// </summary>
 | 
				
			||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
					[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
				
			||||||
public class ClaimConst
 | 
					public static class ClaimConst
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// 账号
 | 
					    /// 账号
 | 
				
			||||||
@@ -225,7 +225,7 @@ public static class SqlSugarExtensions
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private static IEnumerable<T> Sort<T>(this IEnumerable<T> list, BasePageInput basePageInput)
 | 
					    private static IEnumerable<T> Sort<T>(this IEnumerable<T> list, BasePageInput basePageInput)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (basePageInput != null && basePageInput.SortField != null)
 | 
					        if (basePageInput?.SortField != null)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            for (int i = 0; i < basePageInput.SortField.Count; i++)
 | 
					            for (int i = 0; i < basePageInput.SortField.Count; i++)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
							
								
								
									
										11
									
								
								src/Admin/ThingsGateway.DB/GlobalUsings.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/Admin/ThingsGateway.DB/GlobalUsings.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					//------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
				
			||||||
 | 
					//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
				
			||||||
 | 
					//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
				
			||||||
 | 
					//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
				
			||||||
 | 
					//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
				
			||||||
 | 
					//  使用文档:https://thingsgateway.cn/
 | 
				
			||||||
 | 
					//  QQ群:605534569
 | 
				
			||||||
 | 
					//------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					global using ThingsGateway.NewLife.Extension;
 | 
				
			||||||
@@ -25,7 +25,7 @@ namespace ThingsGateway.Admin.Application;
 | 
				
			|||||||
/// 种子数据工具类
 | 
					/// 种子数据工具类
 | 
				
			||||||
/// </summary>
 | 
					/// </summary>
 | 
				
			||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
					[ThingsGateway.DependencyInjection.SuppressSniffer]
 | 
				
			||||||
public class SeedDataUtil
 | 
					public static class SeedDataUtil
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// 获取List列表
 | 
					    /// 获取List列表
 | 
				
			||||||
							
								
								
									
										33
									
								
								src/Admin/ThingsGateway.DB/ThingsGateway.DB.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/Admin/ThingsGateway.DB/ThingsGateway.DB.csproj
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					<Project Sdk="Microsoft.NET.Sdk">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<Import Project="$(SolutionDir)Version.props" />
 | 
				
			||||||
 | 
						<Import Project="$(SolutionDir)PackNuget.props" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<PropertyGroup>
 | 
				
			||||||
 | 
							<GenerateDocumentationFile>True</GenerateDocumentationFile>
 | 
				
			||||||
 | 
						</PropertyGroup>
 | 
				
			||||||
 | 
						<PropertyGroup>
 | 
				
			||||||
 | 
							<TargetFrameworks>net8.0;net9.0;</TargetFrameworks>
 | 
				
			||||||
 | 
						</PropertyGroup>
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						<ItemGroup>
 | 
				
			||||||
 | 
							<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.5" />
 | 
				
			||||||
 | 
						</ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<ItemGroup>
 | 
				
			||||||
 | 
							<None Include="..\README.md" Pack="true" PackagePath="\" />
 | 
				
			||||||
 | 
							<None Include="..\README.zh-CN.md" Pack="true" PackagePath="\" />
 | 
				
			||||||
 | 
							<None Remove="$(SolutionDir)..\README.md" Pack="false" PackagePath="\" />
 | 
				
			||||||
 | 
							<None Remove="$(SolutionDir)..\README.zh-CN.md" Pack="false" PackagePath="\" />
 | 
				
			||||||
 | 
						</ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<ItemGroup>
 | 
				
			||||||
 | 
							<ProjectReference Include="..\ThingsGateway.Razor\ThingsGateway.Razor.csproj" />
 | 
				
			||||||
 | 
							<ProjectReference Include="..\ThingsGateway.SqlSugar\ThingsGateway.SqlSugar.csproj" />
 | 
				
			||||||
 | 
							<!--<PackageReference Include="SqlSugarCore" Version="5.1.4.195" />-->
 | 
				
			||||||
 | 
						</ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</Project>
 | 
				
			||||||
@@ -471,7 +471,7 @@ public static class App
 | 
				
			|||||||
        IEnumerable<string> pathOfExternalAssemblies = Array.Empty<string>();
 | 
					        IEnumerable<string> pathOfExternalAssemblies = Array.Empty<string>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 加载 appsettings.json 配置的外部程序集
 | 
					        // 加载 appsettings.json 配置的外部程序集
 | 
				
			||||||
        if (Settings.ExternalAssemblies != null && Settings.ExternalAssemblies.Length > 0)
 | 
					        if (Settings.ExternalAssemblies?.Length > 0)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var externalDlls = new List<string>();
 | 
					            var externalDlls = new List<string>();
 | 
				
			||||||
            foreach (var item in Settings.ExternalAssemblies)
 | 
					            foreach (var item in Settings.ExternalAssemblies)
 | 
				
			||||||
@@ -552,7 +552,7 @@ public static class App
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 处理排除的程序集
 | 
					        // 处理排除的程序集
 | 
				
			||||||
        if (Settings.ExcludeAssemblies != null && Settings.ExcludeAssemblies.Length > 0)
 | 
					        if (Settings.ExcludeAssemblies?.Length > 0)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            scanAssemblies = scanAssemblies.Where(ass => !Settings.ExcludeAssemblies.Contains(ass.GetName().Name, StringComparer.OrdinalIgnoreCase));
 | 
					            scanAssemblies = scanAssemblies.Where(ass => !Settings.ExcludeAssemblies.Contains(ass.GetName().Name, StringComparer.OrdinalIgnoreCase));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -455,7 +455,7 @@ public static class ObjectExtensions
 | 
				
			|||||||
                foreach (var property in propertys)
 | 
					                foreach (var property in propertys)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    var p = oldType.GetProperty(property.Name);
 | 
					                    var p = oldType.GetProperty(property.Name);
 | 
				
			||||||
                    if (property.CanWrite && p != null && p.CanRead)
 | 
					                    if (property.CanWrite && p?.CanRead == true)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        property.SetValue(o, ChangeType(p.GetValue(obj, null), property.PropertyType), null);
 | 
					                        property.SetValue(o, ChangeType(p.GetValue(obj, null), property.PropertyType), null);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@@ -647,7 +647,7 @@ public static class ObjectExtensions
 | 
				
			|||||||
    /// <returns><see cref="bool"/> 实例,true 表示空集合,false 表示非空集合</returns>
 | 
					    /// <returns><see cref="bool"/> 实例,true 表示空集合,false 表示非空集合</returns>
 | 
				
			||||||
    internal static bool IsEmpty<T>(this IEnumerable<T> collection)
 | 
					    internal static bool IsEmpty<T>(this IEnumerable<T> collection)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return collection == null || !collection.Any();
 | 
					        return collection?.Any() != true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -54,8 +54,7 @@ public class FromConvertBinderProvider : IModelBinderProvider
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        // 判断是否定义 [FromConvert] 特性
 | 
					        // 判断是否定义 [FromConvert] 特性
 | 
				
			||||||
        if (context.Metadata is DefaultModelMetadata actMetadata
 | 
					        if (context.Metadata is DefaultModelMetadata actMetadata
 | 
				
			||||||
            && actMetadata.Attributes.ParameterAttributes != null
 | 
					            && actMetadata.Attributes.ParameterAttributes?.Count > 0
 | 
				
			||||||
            && actMetadata.Attributes.ParameterAttributes.Count > 0
 | 
					 | 
				
			||||||
            && actMetadata.Attributes.ParameterAttributes.Any(u => u.GetType() == typeof(FromConvertAttribute)))
 | 
					            && actMetadata.Attributes.ParameterAttributes.Any(u => u.GetType() == typeof(FromConvertAttribute)))
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return new FromConvertBinder(_modelBinderConverts);
 | 
					            return new FromConvertBinder(_modelBinderConverts);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,7 +25,7 @@ public sealed class AppAuthorizeAttribute : AuthorizeAttribute
 | 
				
			|||||||
    /// <param name="policies">多个策略</param>
 | 
					    /// <param name="policies">多个策略</param>
 | 
				
			||||||
    public AppAuthorizeAttribute(params string[] policies)
 | 
					    public AppAuthorizeAttribute(params string[] policies)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (policies != null && policies.Length > 0) Policies = policies;
 | 
					        if (policies?.Length > 0) Policies = policies;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,7 +47,7 @@ public sealed class DependsOnAttribute : Attribute
 | 
				
			|||||||
        var components = new List<Type>();
 | 
					        var components = new List<Type>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 遍历所有依赖组件
 | 
					        // 遍历所有依赖组件
 | 
				
			||||||
        if (dependComponents != null && dependComponents.Length > 0)
 | 
					        if (dependComponents?.Length > 0)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            foreach (var component in dependComponents)
 | 
					            foreach (var component in dependComponents)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -102,7 +102,7 @@ public sealed class DependsOnAttribute : Attribute
 | 
				
			|||||||
            var components = new List<Type>();
 | 
					            var components = new List<Type>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // 遍历所有依赖组件
 | 
					            // 遍历所有依赖组件
 | 
				
			||||||
            if (value != null && value.Length > 0)
 | 
					            if (value?.Length > 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                foreach (var component in value)
 | 
					                foreach (var component in value)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -65,7 +65,7 @@ internal static class Penetrates
 | 
				
			|||||||
        IEnumerable<string> exposedHeaders = corsAccessorSettings.FixedClientToken == true
 | 
					        IEnumerable<string> exposedHeaders = corsAccessorSettings.FixedClientToken == true
 | 
				
			||||||
            ? _defaultExposedHeaders
 | 
					            ? _defaultExposedHeaders
 | 
				
			||||||
            : Array.Empty<string>();
 | 
					            : Array.Empty<string>();
 | 
				
			||||||
        if (corsAccessorSettings.WithExposedHeaders != null && corsAccessorSettings.WithExposedHeaders.Length > 0)
 | 
					        if (corsAccessorSettings.WithExposedHeaders?.Length > 0)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            exposedHeaders = exposedHeaders.Concat(corsAccessorSettings.WithExposedHeaders).Distinct(StringComparer.OrdinalIgnoreCase);
 | 
					            exposedHeaders = exposedHeaders.Concat(corsAccessorSettings.WithExposedHeaders).Distinct(StringComparer.OrdinalIgnoreCase);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,7 @@ namespace ThingsGateway.DataEncryption;
 | 
				
			|||||||
/// AES 加解密
 | 
					/// AES 加解密
 | 
				
			||||||
/// </summary>
 | 
					/// </summary>
 | 
				
			||||||
[SuppressSniffer]
 | 
					[SuppressSniffer]
 | 
				
			||||||
public class AESEncryption
 | 
					public static class AESEncryption
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// 加密
 | 
					    /// 加密
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,7 +20,7 @@ namespace ThingsGateway.DataEncryption;
 | 
				
			|||||||
/// DES 加解密
 | 
					/// DES 加解密
 | 
				
			||||||
/// </summary>
 | 
					/// </summary>
 | 
				
			||||||
[SuppressSniffer]
 | 
					[SuppressSniffer]
 | 
				
			||||||
public class DESEncryption
 | 
					public static class DESEncryption
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// 加密
 | 
					    /// 加密
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,7 +18,7 @@ namespace ThingsGateway.DataEncryption;
 | 
				
			|||||||
/// KSort 加密(数据签名)
 | 
					/// KSort 加密(数据签名)
 | 
				
			||||||
/// </summary>
 | 
					/// </summary>
 | 
				
			||||||
[SuppressSniffer]
 | 
					[SuppressSniffer]
 | 
				
			||||||
public class KSortEncryption
 | 
					public static class KSortEncryption
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    private static DateTime _timeStampStartTime = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
 | 
					    private static DateTime _timeStampStartTime = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,7 @@ namespace ThingsGateway.DataEncryption;
 | 
				
			|||||||
/// PBKDF2 加密
 | 
					/// PBKDF2 加密
 | 
				
			||||||
/// </summary>
 | 
					/// </summary>
 | 
				
			||||||
[SuppressSniffer]
 | 
					[SuppressSniffer]
 | 
				
			||||||
public class PBKDF2Encryption
 | 
					public static class PBKDF2Encryption
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    private const string SaltHashSeparator = ":";
 | 
					    private const string SaltHashSeparator = ":";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,7 +18,7 @@ namespace ThingsGateway.DataEncryption;
 | 
				
			|||||||
/// SHA1 加密
 | 
					/// SHA1 加密
 | 
				
			||||||
/// </summary>
 | 
					/// </summary>
 | 
				
			||||||
[SuppressSniffer]
 | 
					[SuppressSniffer]
 | 
				
			||||||
public class SHA1Encryption
 | 
					public static class SHA1Encryption
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// SHA1 加密
 | 
					    /// SHA1 加密
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,6 +18,7 @@ namespace System.ComponentModel.DataAnnotations;
 | 
				
			|||||||
/// 数据类型验证特性
 | 
					/// 数据类型验证特性
 | 
				
			||||||
/// </summary>
 | 
					/// </summary>
 | 
				
			||||||
[SuppressSniffer]
 | 
					[SuppressSniffer]
 | 
				
			||||||
 | 
					[AttributeUsage(AttributeTargets.All, AllowMultiple = false)]
 | 
				
			||||||
public sealed class DataValidationAttribute : ValidationAttribute
 | 
					public sealed class DataValidationAttribute : ValidationAttribute
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -215,7 +215,7 @@ public static class DependencyInjectionServiceCollectionExtensions
 | 
				
			|||||||
    private static void AddDispatchProxy(IServiceCollection services, Type dependencyType, Type type, Type proxyType, Type inter, bool hasTarget = true)
 | 
					    private static void AddDispatchProxy(IServiceCollection services, Type dependencyType, Type type, Type proxyType, Type inter, bool hasTarget = true)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        proxyType ??= GlobalServiceProxyType;
 | 
					        proxyType ??= GlobalServiceProxyType;
 | 
				
			||||||
        if (proxyType == null || (type != null && type.IsDefined(typeof(SuppressProxyAttribute), true))) return;
 | 
					        if (proxyType == null || (type?.IsDefined(typeof(SuppressProxyAttribute), true) == true)) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var lifetime = TryGetServiceLifetime(dependencyType);
 | 
					        var lifetime = TryGetServiceLifetime(dependencyType);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -220,8 +220,7 @@ internal sealed class DynamicApiControllerApplicationModelConvention : IApplicat
 | 
				
			|||||||
        // 解决 Gitee 该 Issue:https://gitee.com/dotnetchina/Furion/issues/I59B74
 | 
					        // 解决 Gitee 该 Issue:https://gitee.com/dotnetchina/Furion/issues/I59B74
 | 
				
			||||||
        if (CheckIsForceWithDefaultRoute(controllerApiDescriptionSettings)
 | 
					        if (CheckIsForceWithDefaultRoute(controllerApiDescriptionSettings)
 | 
				
			||||||
            && !string.IsNullOrWhiteSpace(_dynamicApiControllerSettings.DefaultRoutePrefix)
 | 
					            && !string.IsNullOrWhiteSpace(_dynamicApiControllerSettings.DefaultRoutePrefix)
 | 
				
			||||||
            && controller.Selectors[0] != null
 | 
					            && controller.Selectors[0]?.AttributeRouteModel != null
 | 
				
			||||||
            && controller.Selectors[0].AttributeRouteModel != null
 | 
					 | 
				
			||||||
            && !ForceWithDefaultPrefixRouteControllerTypes.Contains(controller.ControllerType))
 | 
					            && !ForceWithDefaultPrefixRouteControllerTypes.Contains(controller.ControllerType))
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            // 读取模块
 | 
					            // 读取模块
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -126,7 +126,7 @@ public static class DynamicApiControllerServiceCollectionExtensions
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        var partManager = mvcBuilder.PartManager;
 | 
					        var partManager = mvcBuilder.PartManager;
 | 
				
			||||||
        // 载入程序集部件
 | 
					        // 载入程序集部件
 | 
				
			||||||
        if (partManager != null && assemblies != null && assemblies.Any())
 | 
					        if (partManager != null && assemblies?.Any() == true)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            foreach (var assembly in assemblies)
 | 
					            foreach (var assembly in assemblies)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,7 +48,7 @@ internal sealed class DynamicApiRuntimeChangeProvider : IDynamicApiRuntimeChange
 | 
				
			|||||||
    /// <param name="assemblies">程序集</param>
 | 
					    /// <param name="assemblies">程序集</param>
 | 
				
			||||||
    public void AddAssemblies(params Assembly[] assemblies)
 | 
					    public void AddAssemblies(params Assembly[] assemblies)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (assemblies != null && assemblies.Length > 0)
 | 
					        if (assemblies?.Length > 0)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            foreach (var assembly in assemblies)
 | 
					            foreach (var assembly in assemblies)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -63,7 +63,7 @@ internal sealed class DynamicApiRuntimeChangeProvider : IDynamicApiRuntimeChange
 | 
				
			|||||||
    /// <param name="assemblies">程序集</param>
 | 
					    /// <param name="assemblies">程序集</param>
 | 
				
			||||||
    public void AddAssembliesWithNotifyChanges(params Assembly[] assemblies)
 | 
					    public void AddAssembliesWithNotifyChanges(params Assembly[] assemblies)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (assemblies != null && assemblies.Length > 0)
 | 
					        if (assemblies?.Length > 0)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            AddAssemblies(assemblies);
 | 
					            AddAssemblies(assemblies);
 | 
				
			||||||
            NotifyChanges();
 | 
					            NotifyChanges();
 | 
				
			||||||
@@ -76,7 +76,7 @@ internal sealed class DynamicApiRuntimeChangeProvider : IDynamicApiRuntimeChange
 | 
				
			|||||||
    /// <param name="assemblyNames">程序集名称</param>
 | 
					    /// <param name="assemblyNames">程序集名称</param>
 | 
				
			||||||
    public void RemoveAssemblies(params string[] assemblyNames)
 | 
					    public void RemoveAssemblies(params string[] assemblyNames)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (assemblyNames != null && assemblyNames.Length > 0)
 | 
					        if (assemblyNames?.Length > 0)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            foreach (var assemblyName in assemblyNames)
 | 
					            foreach (var assemblyName in assemblyNames)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -93,7 +93,7 @@ internal sealed class DynamicApiRuntimeChangeProvider : IDynamicApiRuntimeChange
 | 
				
			|||||||
    /// <param name="assemblies">程序集</param>
 | 
					    /// <param name="assemblies">程序集</param>
 | 
				
			||||||
    public void RemoveAssemblies(params Assembly[] assemblies)
 | 
					    public void RemoveAssemblies(params Assembly[] assemblies)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (assemblies != null && assemblies.Length > 0)
 | 
					        if (assemblies?.Length > 0)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            RemoveAssemblies(assemblies.Select(ass => ass.GetName().Name).ToArray());
 | 
					            RemoveAssemblies(assemblies.Select(ass => ass.GetName().Name).ToArray());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -105,7 +105,7 @@ internal sealed class DynamicApiRuntimeChangeProvider : IDynamicApiRuntimeChange
 | 
				
			|||||||
    /// <param name="assemblyNames">程序集名称</param>
 | 
					    /// <param name="assemblyNames">程序集名称</param>
 | 
				
			||||||
    public void RemoveAssembliesWithNotifyChanges(params string[] assemblyNames)
 | 
					    public void RemoveAssembliesWithNotifyChanges(params string[] assemblyNames)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (assemblyNames != null && assemblyNames.Length > 0)
 | 
					        if (assemblyNames?.Length > 0)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            RemoveAssemblies(assemblyNames);
 | 
					            RemoveAssemblies(assemblyNames);
 | 
				
			||||||
            NotifyChanges();
 | 
					            NotifyChanges();
 | 
				
			||||||
@@ -118,7 +118,7 @@ internal sealed class DynamicApiRuntimeChangeProvider : IDynamicApiRuntimeChange
 | 
				
			|||||||
    /// <param name="assemblies">程序集</param>
 | 
					    /// <param name="assemblies">程序集</param>
 | 
				
			||||||
    public void RemoveAssembliesWithNotifyChanges(params Assembly[] assemblies)
 | 
					    public void RemoveAssembliesWithNotifyChanges(params Assembly[] assemblies)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (assemblies != null && assemblies.Length > 0)
 | 
					        if (assemblies?.Length > 0)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            RemoveAssemblies(assemblies);
 | 
					            RemoveAssemblies(assemblies);
 | 
				
			||||||
            NotifyChanges();
 | 
					            NotifyChanges();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,6 +49,14 @@ public class AppFriendlyException : Exception
 | 
				
			|||||||
        ErrorCode = OriginErrorCode = errorCode;
 | 
					        ErrorCode = OriginErrorCode = errorCode;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public AppFriendlyException(string? message) : base(message)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public AppFriendlyException(string? message, Exception? innerException) : base(message, innerException)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// 错误码
 | 
					    /// 错误码
 | 
				
			||||||
    /// </summary>
 | 
					    /// </summary>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -102,7 +102,7 @@ public sealed class Retry
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // 如果填写了 exceptionTypes 且异常类型不在 exceptionTypes 之内,则终止重试
 | 
					                // 如果填写了 exceptionTypes 且异常类型不在 exceptionTypes 之内,则终止重试
 | 
				
			||||||
                if (exceptionTypes != null && exceptionTypes.Length > 0 && !exceptionTypes.Any(u => u.IsAssignableFrom(ex.GetType())))
 | 
					                if (exceptionTypes?.Length > 0 && !exceptionTypes.Any(u => u.IsAssignableFrom(ex.GetType())))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    if (finalThrow)
 | 
					                    if (finalThrow)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -604,7 +604,7 @@ public sealed class LoggingMonitorAttribute : Attribute, IAsyncActionFilter, IAs
 | 
				
			|||||||
    private string TrySerializeObject(object obj, LoggingMonitorMethod monitorMethod, out bool succeed)
 | 
					    private string TrySerializeObject(object obj, LoggingMonitorMethod monitorMethod, out bool succeed)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // 排除 IQueryable<> 泛型
 | 
					        // 排除 IQueryable<> 泛型
 | 
				
			||||||
        if (obj != null && obj.GetType().HasImplementedRawGeneric(typeof(IQueryable<>)))
 | 
					        if (obj?.GetType().HasImplementedRawGeneric(typeof(IQueryable<>)) == true)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            succeed = true;
 | 
					            succeed = true;
 | 
				
			||||||
            return "{}";
 | 
					            return "{}";
 | 
				
			||||||
@@ -961,8 +961,7 @@ public sealed class LoggingMonitorAttribute : Attribute, IAsyncActionFilter, IAs
 | 
				
			|||||||
        // token 信息
 | 
					        // token 信息
 | 
				
			||||||
        // 判断是否是授权访问
 | 
					        // 判断是否是授权访问
 | 
				
			||||||
        var isAuth = actionMethod.GetFoundAttribute<AllowAnonymousAttribute>(true) == null
 | 
					        var isAuth = actionMethod.GetFoundAttribute<AllowAnonymousAttribute>(true) == null
 | 
				
			||||||
            && resultHttpContext.User != null
 | 
					            && resultHttpContext.User?.Identity.IsAuthenticated == true;
 | 
				
			||||||
            && resultHttpContext.User.Identity.IsAuthenticated;
 | 
					 | 
				
			||||||
        // 获取响应头信息
 | 
					        // 获取响应头信息
 | 
				
			||||||
        var accessToken = resultHttpContext.Response.Headers["access-token"].ToString();
 | 
					        var accessToken = resultHttpContext.Response.Headers["access-token"].ToString();
 | 
				
			||||||
        var authorization = string.IsNullOrWhiteSpace(accessToken)
 | 
					        var authorization = string.IsNullOrWhiteSpace(accessToken)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,7 +47,7 @@ public sealed partial class StringLoggingPart
 | 
				
			|||||||
    /// <param name="args"></param>
 | 
					    /// <param name="args"></param>
 | 
				
			||||||
    public StringLoggingPart SetArgs(params object[] args)
 | 
					    public StringLoggingPart SetArgs(params object[] args)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (args != null && args.Length > 0) Args = args;
 | 
					        if (args?.Length > 0) Args = args;
 | 
				
			||||||
        return this;
 | 
					        return this;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,7 +47,7 @@ public static class ObjectMapperServiceCollectionExtensions
 | 
				
			|||||||
        var config = TypeAdapterConfig.GlobalSettings;
 | 
					        var config = TypeAdapterConfig.GlobalSettings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 扫描所有继承  IRegister 接口的对象映射配置
 | 
					        // 扫描所有继承  IRegister 接口的对象映射配置
 | 
				
			||||||
        if (assemblies != null && assemblies.Length > 0) config.Scan(assemblies);
 | 
					        if (assemblies?.Length > 0) config.Scan(assemblies);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 配置支持依赖注入
 | 
					        // 配置支持依赖注入
 | 
				
			||||||
        services.AddSingleton(config);
 | 
					        services.AddSingleton(config);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -235,7 +235,7 @@ public sealed class SchedulerBuilder
 | 
				
			|||||||
        var schedulerBuilder = new SchedulerBuilder(jobBuilder);
 | 
					        var schedulerBuilder = new SchedulerBuilder(jobBuilder);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 批量添加触发器
 | 
					        // 批量添加触发器
 | 
				
			||||||
        if (triggerBuilders != null && triggerBuilders.Length > 0)
 | 
					        if (triggerBuilders?.Length > 0)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            schedulerBuilder.TriggerBuilders.AddRange(triggerBuilders);
 | 
					            schedulerBuilder.TriggerBuilders.AddRange(triggerBuilders);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -192,7 +192,7 @@ public static class ScheduleExtensions
 | 
				
			|||||||
            var underScoreCasePropertyName = Penetrates.GetNaming(propertyName, NamingConventions.UnderScoreCase);
 | 
					            var underScoreCasePropertyName = Penetrates.GetNaming(propertyName, NamingConventions.UnderScoreCase);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // 处理忽略属性问题
 | 
					            // 处理忽略属性问题
 | 
				
			||||||
            if (ignorePropertyNames != null && ignorePropertyNames.Length > 0)
 | 
					            if (ignorePropertyNames?.Length > 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (ignorePropertyNames.Contains(propertyName, StringComparer.OrdinalIgnoreCase)
 | 
					                if (ignorePropertyNames.Contains(propertyName, StringComparer.OrdinalIgnoreCase)
 | 
				
			||||||
                    || ignorePropertyNames.Contains(camelCasePropertyName, StringComparer.OrdinalIgnoreCase)
 | 
					                    || ignorePropertyNames.Contains(camelCasePropertyName, StringComparer.OrdinalIgnoreCase)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -327,7 +327,7 @@ internal sealed class ScheduleHostedService : BackgroundService
 | 
				
			|||||||
                                };
 | 
					                                };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                // 是否定义 FallbackAsync 方法
 | 
					                                // 是否定义 FallbackAsync 方法
 | 
				
			||||||
                                var isDefinedFallbackAsyncMethod = jobHandler != null && jobHandler.GetType().GetMethod(nameof(IJob.FallbackAsync)
 | 
					                                var isDefinedFallbackAsyncMethod = jobHandler?.GetType().GetMethod(nameof(IJob.FallbackAsync)
 | 
				
			||||||
                                    , BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly
 | 
					                                    , BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly
 | 
				
			||||||
                                    , null
 | 
					                                    , null
 | 
				
			||||||
                                    , new[] { typeof(JobExecutedContext), typeof(CancellationToken) }
 | 
					                                    , new[] { typeof(JobExecutedContext), typeof(CancellationToken) }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -101,7 +101,7 @@ public class HttpJob : IJob
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 添加请求头
 | 
					        // 添加请求头
 | 
				
			||||||
        if (httpJobMessage.Headers != null && httpJobMessage.Headers.Count > 0)
 | 
					        if (httpJobMessage.Headers?.Count > 0)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            foreach (var (name, value) in httpJobMessage.Headers)
 | 
					            foreach (var (name, value) in httpJobMessage.Headers)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -711,7 +711,7 @@ public static class SpecificationDocumentBuilder
 | 
				
			|||||||
            .Union(
 | 
					            .Union(
 | 
				
			||||||
                actions.SelectMany(u => GetActionGroups(u))
 | 
					                actions.SelectMany(u => GetActionGroups(u))
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            .Where(u => u != null && u.Visible)
 | 
					            .Where(u => u?.Visible == true)
 | 
				
			||||||
            // 分组后取最大排序
 | 
					            // 分组后取最大排序
 | 
				
			||||||
            .GroupBy(u => u.Group)
 | 
					            .GroupBy(u => u.Group)
 | 
				
			||||||
            .Select(u => new GroupExtraInfo
 | 
					            .Select(u => new GroupExtraInfo
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,7 +47,7 @@ public static class TP
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 添加项
 | 
					        // 添加项
 | 
				
			||||||
        if (items != null && items.Length > 0)
 | 
					        if (items?.Length > 0)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var propMaxLength = items.Where(u => _lazyRegex.Value.IsMatch(u))
 | 
					            var propMaxLength = items.Where(u => _lazyRegex.Value.IsMatch(u))
 | 
				
			||||||
                .DefaultIfEmpty(string.Empty)
 | 
					                .DefaultIfEmpty(string.Empty)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,7 +39,7 @@
 | 
				
			|||||||
		<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
 | 
							<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
 | 
				
			||||||
		<PackageReference Include="Mapster" Version="7.4.0" />
 | 
							<PackageReference Include="Mapster" Version="7.4.0" />
 | 
				
			||||||
		<PackageReference Include="MiniProfiler.AspNetCore.Mvc" Version="4.5.4" />
 | 
							<PackageReference Include="MiniProfiler.AspNetCore.Mvc" Version="4.5.4" />
 | 
				
			||||||
		<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.3" />
 | 
							<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.4" />
 | 
				
			||||||
	</ItemGroup>
 | 
						</ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
 | 
						<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -343,7 +343,7 @@ public partial class Crontab
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            // 如果存在且唯一,则进入下一轮判断
 | 
					            // 如果存在且唯一,则进入下一轮判断
 | 
				
			||||||
            // 接下来的判断是处理 SUN + L 的情况,如 SUNL == 0L == SUNDAY,它们都是合法的 Cron 值
 | 
					            // 接下来的判断是处理 SUN + L 的情况,如 SUNL == 0L == SUNDAY,它们都是合法的 Cron 值
 | 
				
			||||||
            if (replaceVal != null && replaceVal.Count == 1)
 | 
					            if (replaceVal?.Count == 1)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var missingParser = "";
 | 
					                var missingParser = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -140,10 +140,10 @@ public static class UnifyContext
 | 
				
			|||||||
        if (unifyResultSettings == null) return;
 | 
					        if (unifyResultSettings == null) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 篡改响应状态码
 | 
					        // 篡改响应状态码
 | 
				
			||||||
        if (unifyResultSettings.AdaptStatusCodes != null && unifyResultSettings.AdaptStatusCodes.Length > 0)
 | 
					        if (unifyResultSettings.AdaptStatusCodes?.Length > 0)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var adaptStatusCode = unifyResultSettings.AdaptStatusCodes.FirstOrDefault(u => u[0] == statusCode);
 | 
					            var adaptStatusCode = unifyResultSettings.AdaptStatusCodes.FirstOrDefault(u => u[0] == statusCode);
 | 
				
			||||||
            if (adaptStatusCode != null && adaptStatusCode.Length > 0 && adaptStatusCode[0] > 0)
 | 
					            if (adaptStatusCode?.Length > 0 && adaptStatusCode[0] > 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                context.Response.StatusCode = adaptStatusCode[1];
 | 
					                context.Response.StatusCode = adaptStatusCode[1];
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -139,8 +139,7 @@ internal static class TypeExtensions
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 类型限定名是否以 <> 开头且以 AnonymousType 结尾
 | 
					        // 类型限定名是否以 <> 开头且以 AnonymousType 结尾
 | 
				
			||||||
        return type.FullName is not null
 | 
					        return type.FullName?.StartsWith("<>") == true
 | 
				
			||||||
               && type.FullName.StartsWith("<>")
 | 
					 | 
				
			||||||
               && type.FullName.Contains("AnonymousType");
 | 
					               && type.FullName.Contains("AnonymousType");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -463,7 +462,7 @@ internal static class TypeExtensions
 | 
				
			|||||||
            var elementType = type.GetElementType();
 | 
					            var elementType = type.GetElementType();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // 检查元素类型是否是 KeyValuePair<,> 类型
 | 
					            // 检查元素类型是否是 KeyValuePair<,> 类型
 | 
				
			||||||
            if (elementType is null || !elementType.IsKeyValuePair())
 | 
					            if (elementType?.IsKeyValuePair() != true)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                return false;
 | 
					                return false;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -135,7 +135,7 @@ internal static class V5_ObjectExtensions
 | 
				
			|||||||
        var runtimeProperty = obj.GetType().GetRuntimeProperty("Count");
 | 
					        var runtimeProperty = obj.GetType().GetRuntimeProperty("Count");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 反射获取 Count 属性值
 | 
					        // 反射获取 Count 属性值
 | 
				
			||||||
        if (runtimeProperty is not null && runtimeProperty.CanRead && runtimeProperty.PropertyType == typeof(int))
 | 
					        if (runtimeProperty?.CanRead == true && runtimeProperty.PropertyType == typeof(int))
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            count = (int)runtimeProperty.GetValue(obj)!;
 | 
					            count = (int)runtimeProperty.GetValue(obj)!;
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
@@ -332,7 +332,7 @@ internal static class V5_ObjectExtensions
 | 
				
			|||||||
            var property = current.GetType().GetProperty(part, bindingFlags);
 | 
					            var property = current.GetType().GetProperty(part, bindingFlags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // 空检查
 | 
					            // 空检查
 | 
				
			||||||
            if (property is null || !property.CanRead)
 | 
					            if (property?.CanRead != true)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                return null;
 | 
					                return null;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1591,7 +1591,7 @@ public sealed partial class HttpRequestBuilder
 | 
				
			|||||||
    public HttpRequestBuilder SetBaseAddress(Uri? baseAddress)
 | 
					    public HttpRequestBuilder SetBaseAddress(Uri? baseAddress)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // 检查基地址是否是绝对路径地址
 | 
					        // 检查基地址是否是绝对路径地址
 | 
				
			||||||
        if (baseAddress is not null && !baseAddress.IsAbsoluteUri)
 | 
					        if (baseAddress?.IsAbsoluteUri == false)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            throw new ArgumentException("The base address must be absolute.", nameof(baseAddress));
 | 
					            throw new ArgumentException("The base address must be absolute.", nameof(baseAddress));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -136,7 +136,7 @@ public partial class Clay
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            handler(this, new ClayEventArgs(identifier, Contains(identifier)));
 | 
					            handler(this, new ClayEventArgs(identifier, Contains(identifier)));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        catch (Exception)
 | 
					        catch
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            // ignored
 | 
					            // ignored
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -177,15 +177,6 @@ public partial class Clay
 | 
				
			|||||||
        enumerableClay = this;
 | 
					        enumerableClay = this;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// <summary>
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    /// <param name="clay">dynamic 类型的 <see cref="Clay" /></param>
 | 
					 | 
				
			||||||
    /// <param name="enumerableClay">
 | 
					 | 
				
			||||||
    ///     <see cref="IEnumerable{T}" />
 | 
					 | 
				
			||||||
    /// </param>
 | 
					 | 
				
			||||||
    /// <param name="rawClay">
 | 
					 | 
				
			||||||
    ///     <see cref="Clay" />
 | 
					 | 
				
			||||||
    /// </param>
 | 
					 | 
				
			||||||
    public void Deconstruct(out dynamic clay, out IEnumerable<dynamic?> enumerableClay, out Clay rawClay)
 | 
					    public void Deconstruct(out dynamic clay, out IEnumerable<dynamic?> enumerableClay, out Clay rawClay)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        clay = this;
 | 
					        clay = this;
 | 
				
			||||||
@@ -888,7 +879,7 @@ public partial class Clay
 | 
				
			|||||||
        foreach (var item in values)
 | 
					        foreach (var item in values)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            // 检查值是否为空值或基本类型的值
 | 
					            // 检查值是否为空值或基本类型的值
 | 
				
			||||||
            if (item is null || item.GetType().IsBasicType())
 | 
					            if (item?.GetType().IsBasicType() != false)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                throw new InvalidOperationException("Cannot extend a single object with null or basic type values.");
 | 
					                throw new InvalidOperationException("Cannot extend a single object with null or basic type values.");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,9 +12,11 @@ using System.ComponentModel.DataAnnotations;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace ThingsGateway;
 | 
					namespace ThingsGateway;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// <summary>
 | 
					/// <summary>
 | 
				
			||||||
/// 最小值校验
 | 
					/// 最小值校验
 | 
				
			||||||
/// </summary>
 | 
					/// </summary>
 | 
				
			||||||
 | 
					[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
 | 
				
			||||||
public sealed class MinValueAttribute : ValidationAttribute
 | 
					public sealed class MinValueAttribute : ValidationAttribute
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -244,7 +244,6 @@ public ref struct SpanReader
 | 
				
			|||||||
        while (true)
 | 
					        while (true)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var bt = ReadByte();
 | 
					            var bt = ReadByte();
 | 
				
			||||||
            if (bt < 0) throw new Exception($"The data stream is out of range! The integer read is {rs: n0}");
 | 
					 | 
				
			||||||
            b = (Byte)bt;
 | 
					            b = (Byte)bt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // 必须转为Int32,否则可能溢出
 | 
					            // 必须转为Int32,否则可能溢出
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -120,7 +120,7 @@ public class MemoryCache : Cache
 | 
				
			|||||||
    /// <summary>是否包含缓存项</summary>
 | 
					    /// <summary>是否包含缓存项</summary>
 | 
				
			||||||
    /// <param name="key"></param>
 | 
					    /// <param name="key"></param>
 | 
				
			||||||
    /// <returns></returns>
 | 
					    /// <returns></returns>
 | 
				
			||||||
    public override Boolean ContainsKey(String key) => _cache.TryGetValue(key, out var item) && item != null && !item.Expired;
 | 
					    public override Boolean ContainsKey(String key) => _cache.TryGetValue(key, out var item) && item?.Expired == false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// <summary>添加缓存项,已存在时更新</summary>
 | 
					    /// <summary>添加缓存项,已存在时更新</summary>
 | 
				
			||||||
    /// <typeparam name="T">值类型</typeparam>
 | 
					    /// <typeparam name="T">值类型</typeparam>
 | 
				
			||||||
@@ -166,7 +166,7 @@ public class MemoryCache : Cache
 | 
				
			|||||||
    [return: MaybeNull]
 | 
					    [return: MaybeNull]
 | 
				
			||||||
    public override T Get<T>(String key)
 | 
					    public override T Get<T>(String key)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (!_cache.TryGetValue(key, out var item) || item == null || item.Expired) return default;
 | 
					        if (!_cache.TryGetValue(key, out var item) || item?.Expired != false) return default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return item.Visit<T>();
 | 
					        return item.Visit<T>();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -712,7 +712,7 @@ public class MemoryCache : Cache
 | 
				
			|||||||
            for (var i = 0; i < slist.Count && over > 0; i++)
 | 
					            for (var i = 0; i < slist.Count && over > 0; i++)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var ss = slist.Values[i];
 | 
					                var ss = slist.Values[i];
 | 
				
			||||||
                if (ss != null && ss.Count > 0)
 | 
					                if (ss?.Count > 0)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    foreach (var item in ss)
 | 
					                    foreach (var item in ss)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -402,7 +402,7 @@ public class ObjectPool<T> : DisposeBase, IPool<T> where T : notnull
 | 
				
			|||||||
    /// <param name="args"></param>
 | 
					    /// <param name="args"></param>
 | 
				
			||||||
    public void WriteLog(String format, params Object?[] args)
 | 
					    public void WriteLog(String format, params Object?[] args)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (Log == null || !Log.Enable) return;
 | 
					        if (Log?.Enable != true) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Log.Info(Name + "." + format, args);
 | 
					        Log.Info(Name + "." + format, args);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,7 +15,7 @@ namespace ThingsGateway.NewLife;
 | 
				
			|||||||
/// <summary>
 | 
					/// <summary>
 | 
				
			||||||
/// FileUtil
 | 
					/// FileUtil
 | 
				
			||||||
/// </summary>
 | 
					/// </summary>
 | 
				
			||||||
public class FileUtil
 | 
					public static class FileUtil
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// 读取文件
 | 
					    /// 读取文件
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -633,7 +633,7 @@ public class MachineInfo
 | 
				
			|||||||
        if (!_excludes.Contains(nameof(Temperature)))
 | 
					        if (!_excludes.Contains(nameof(Temperature)))
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var temp = ReadWmic(@"/namespace:\\root\wmi path MSAcpi_ThermalZoneTemperature", "CurrentTemperature");
 | 
					            var temp = ReadWmic(@"/namespace:\\root\wmi path MSAcpi_ThermalZoneTemperature", "CurrentTemperature");
 | 
				
			||||||
            if (temp != null && temp.Count > 0)
 | 
					            if (temp?.Count > 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (temp.TryGetValue("CurrentTemperature", out var str) && !str.IsNullOrEmpty())
 | 
					                if (temp.TryGetValue("CurrentTemperature", out var str) && !str.IsNullOrEmpty())
 | 
				
			||||||
                    Temperature = (str.SplitAsInt().Average() - 2732) / 10.0;
 | 
					                    Temperature = (str.SplitAsInt().Average() - 2732) / 10.0;
 | 
				
			||||||
@@ -651,7 +651,7 @@ public class MachineInfo
 | 
				
			|||||||
        else if (!_excludes.Contains(nameof(Battery)))
 | 
					        else if (!_excludes.Contains(nameof(Battery)))
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var battery = ReadWmic("path win32_battery", "EstimatedChargeRemaining");
 | 
					            var battery = ReadWmic("path win32_battery", "EstimatedChargeRemaining");
 | 
				
			||||||
            if (battery != null && battery.Count > 0)
 | 
					            if (battery?.Count > 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if (battery.TryGetValue("EstimatedChargeRemaining", out var str) && !str.IsNullOrEmpty())
 | 
					                if (battery.TryGetValue("EstimatedChargeRemaining", out var str) && !str.IsNullOrEmpty())
 | 
				
			||||||
                    Battery = str.SplitAsInt().Average() / 100.0;
 | 
					                    Battery = str.SplitAsInt().Average() / 100.0;
 | 
				
			||||||
@@ -937,7 +937,7 @@ public class MachineInfo
 | 
				
			|||||||
        foreach (var item in ss)
 | 
					        foreach (var item in ss)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var ks = item?.Split('=');
 | 
					            var ks = item?.Split('=');
 | 
				
			||||||
            if (ks != null && ks.Length >= 2)
 | 
					            if (ks?.Length >= 2)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var k = ks[0].Trim();
 | 
					                var k = ks[0].Trim();
 | 
				
			||||||
                var v = ks[1].Trim().TrimInvisible();
 | 
					                var v = ks[1].Trim().TrimInvisible();
 | 
				
			||||||
@@ -1079,7 +1079,7 @@ public class MachineInfo
 | 
				
			|||||||
        if (root.IsNullOrEmpty()) return 0;
 | 
					        if (root.IsNullOrEmpty()) return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var driveInfo = new DriveInfo(root);
 | 
					        var driveInfo = new DriveInfo(root);
 | 
				
			||||||
        if (driveInfo == null || !driveInfo.IsReady) return -1;
 | 
					        if (driveInfo?.IsReady != true) return -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try
 | 
					        try
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@ namespace ThingsGateway.NewLife.Common;
 | 
				
			|||||||
/// <remarks>
 | 
					/// <remarks>
 | 
				
			||||||
/// 文档 https://newlifex.com/core/pinyin
 | 
					/// 文档 https://newlifex.com/core/pinyin
 | 
				
			||||||
/// </remarks>
 | 
					/// </remarks>
 | 
				
			||||||
public class PinYin
 | 
					public static class PinYin
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    #region 数组信息
 | 
					    #region 数组信息
 | 
				
			||||||
    private static readonly Int32[] pyValue = new[] {
 | 
					    private static readonly Int32[] pyValue = new[] {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -199,7 +199,7 @@ public static class Runtime
 | 
				
			|||||||
        if (processId != ProcessId) gc = false;
 | 
					        if (processId != ProcessId) gc = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var log = XTrace.Log;
 | 
					        var log = XTrace.Log;
 | 
				
			||||||
        if (log != null && log.Enable && log.Level <= LogLevel.Debug)
 | 
					        if (log?.Enable == true && log.Level <= LogLevel.Debug)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            p ??= Process.GetCurrentProcess();
 | 
					            p ??= Process.GetCurrentProcess();
 | 
				
			||||||
            var gcm = GC.GetTotalMemory(false) / 1024;
 | 
					            var gcm = GC.GetTotalMemory(false) / 1024;
 | 
				
			||||||
@@ -243,7 +243,7 @@ public static class Runtime
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (log != null && log.Enable && log.Level <= LogLevel.Debug)
 | 
					        if (log?.Enable == true && log.Level <= LogLevel.Debug)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            p ??= Process.GetProcessById(processId);
 | 
					            p ??= Process.GetProcessById(processId);
 | 
				
			||||||
            p.Refresh();
 | 
					            p.Refresh();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -108,7 +108,7 @@ public static class ConfigHelper
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                dic[cfg.Key] = cfg.Value;
 | 
					                dic[cfg.Key] = cfg.Value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (cfg.Childs != null && cfg.Childs.Count > 0)
 | 
					                if (cfg.Childs?.Count > 0)
 | 
				
			||||||
                    dic[cfg.Key] = cfg.Childs;
 | 
					                    dic[cfg.Key] = cfg.Childs;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -54,6 +54,6 @@ public class ConfigSection : IConfigSection
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /// <summary>已重载。</summary>
 | 
					    /// <summary>已重载。</summary>
 | 
				
			||||||
    /// <returns></returns>
 | 
					    /// <returns></returns>
 | 
				
			||||||
    public override String ToString() => Childs != null && Childs.Count > 0 ? $"{Key}[{Childs.Count}]" : $"{Key}={Value}";
 | 
					    public override String ToString() => Childs?.Count > 0 ? $"{Key}[{Childs.Count}]" : $"{Key}={Value}";
 | 
				
			||||||
    #endregion
 | 
					    #endregion
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -75,7 +75,7 @@ public class InIConfigProvider : FileConfigProvider
 | 
				
			|||||||
        var sb = new StringBuilder();
 | 
					        var sb = new StringBuilder();
 | 
				
			||||||
        foreach (var item in section.Childs.ToArray())
 | 
					        foreach (var item in section.Childs.ToArray())
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (item.Childs != null && item.Childs.Count > 0)
 | 
					            if (item.Childs?.Count > 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                // 段前空一行
 | 
					                // 段前空一行
 | 
				
			||||||
                sb.AppendLine();
 | 
					                sb.AppendLine();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,7 +41,7 @@ public class WeakAction<TArgs>
 | 
				
			|||||||
            var target = Target;
 | 
					            var target = Target;
 | 
				
			||||||
            if (target == null && Method.IsStatic) return true;
 | 
					            if (target == null && Method.IsStatic) return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return target != null && target.IsAlive;
 | 
					            return target?.IsAlive == true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    #endregion
 | 
					    #endregion
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -65,7 +65,7 @@ public static class DateExtensions
 | 
				
			|||||||
    /// <param name="beginTime">开始时间</param>
 | 
					    /// <param name="beginTime">开始时间</param>
 | 
				
			||||||
    /// <param name="endTime">结束时间</param>
 | 
					    /// <param name="endTime">结束时间</param>
 | 
				
			||||||
    /// <returns>时间差</returns>
 | 
					    /// <returns>时间差</returns>
 | 
				
			||||||
    public static string GetDiffTime(this in DateTime beginTime, in DateTime endTime)
 | 
					    public static string GetDiffTime(this DateTime beginTime, DateTime endTime)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        TimeSpan timeDifference = endTime - beginTime;
 | 
					        TimeSpan timeDifference = endTime - beginTime;
 | 
				
			||||||
        if (timeDifference.TotalDays >= 1)
 | 
					        if (timeDifference.TotalDays >= 1)
 | 
				
			||||||
@@ -88,7 +88,7 @@ public static class DateExtensions
 | 
				
			|||||||
    /// <param name="beginTime">开始时间</param>
 | 
					    /// <param name="beginTime">开始时间</param>
 | 
				
			||||||
    /// <param name="endTime">结束时间</param>
 | 
					    /// <param name="endTime">结束时间</param>
 | 
				
			||||||
    /// <returns>时间差</returns>
 | 
					    /// <returns>时间差</returns>
 | 
				
			||||||
    public static string GetDiffTime(this in DateTimeOffset beginTime, in DateTimeOffset endTime)
 | 
					    public static string GetDiffTime(this DateTimeOffset beginTime, DateTimeOffset endTime)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        TimeSpan timeDifference = endTime - beginTime;
 | 
					        TimeSpan timeDifference = endTime - beginTime;
 | 
				
			||||||
        if (timeDifference.TotalDays >= 1)
 | 
					        if (timeDifference.TotalDays >= 1)
 | 
				
			||||||
@@ -108,7 +108,7 @@ public static class DateExtensions
 | 
				
			|||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// 返回yyyy-MM-ddTHH:mm:ss.fffffffzzz时间格式字符串
 | 
					    /// 返回yyyy-MM-ddTHH:mm:ss.fffffffzzz时间格式字符串
 | 
				
			||||||
    /// </summary>
 | 
					    /// </summary>
 | 
				
			||||||
    public static string ToDefaultDateTimeFormat(this in DateTime dt, TimeSpan offset)
 | 
					    public static string ToDefaultDateTimeFormat(this DateTime dt, TimeSpan offset)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (dt.Kind == DateTimeKind.Utc)
 | 
					        if (dt.Kind == DateTimeKind.Utc)
 | 
				
			||||||
            return new DateTimeOffset(dt.ToLocalTime(), offset).ToString("yyyy-MM-ddTHH:mm:ss.fffffffzzz");
 | 
					            return new DateTimeOffset(dt.ToLocalTime(), offset).ToString("yyyy-MM-ddTHH:mm:ss.fffffffzzz");
 | 
				
			||||||
@@ -129,7 +129,7 @@ public static class DateExtensions
 | 
				
			|||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// 返回yyyy-MM-ddTHH:mm:ss.fffffffzzz时间格式字符串
 | 
					    /// 返回yyyy-MM-ddTHH:mm:ss.fffffffzzz时间格式字符串
 | 
				
			||||||
    /// </summary>
 | 
					    /// </summary>
 | 
				
			||||||
    public static string ToDefaultDateTimeFormat(this in DateTime dt)
 | 
					    public static string ToDefaultDateTimeFormat(this DateTime dt)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return dt.ToString("yyyy-MM-ddTHH:mm:ss.fffffffzzz");
 | 
					        return dt.ToString("yyyy-MM-ddTHH:mm:ss.fffffffzzz");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -137,7 +137,7 @@ public static class DateExtensions
 | 
				
			|||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// 返回yyyy-MM-dd HH-mm-ss-fff zz时间格式字符串
 | 
					    /// 返回yyyy-MM-dd HH-mm-ss-fff zz时间格式字符串
 | 
				
			||||||
    /// </summary>
 | 
					    /// </summary>
 | 
				
			||||||
    public static string ToFileDateTimeFormat(this in DateTime dt)
 | 
					    public static string ToFileDateTimeFormat(this DateTime dt)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return ToDefaultDateTimeFormat(dt).Replace(":", "-");
 | 
					        return ToDefaultDateTimeFormat(dt).Replace(":", "-");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -145,7 +145,7 @@ public static class DateExtensions
 | 
				
			|||||||
    /// <summary>
 | 
					    /// <summary>
 | 
				
			||||||
    /// 返回yyyy-MM-dd HH-mm-ss-fff zz时间格式字符串
 | 
					    /// 返回yyyy-MM-dd HH-mm-ss-fff zz时间格式字符串
 | 
				
			||||||
    /// </summary>
 | 
					    /// </summary>
 | 
				
			||||||
    public static string ToFileDateTimeFormat(this in DateTime dt, TimeSpan offset)
 | 
					    public static string ToFileDateTimeFormat(this DateTime dt, TimeSpan offset)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return ToDefaultDateTimeFormat(dt, offset).Replace(":", "-");
 | 
					        return ToDefaultDateTimeFormat(dt, offset).Replace(":", "-");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,31 +6,18 @@ namespace ThingsGateway.NewLife.Extension;
 | 
				
			|||||||
/// <summary>网络结点扩展</summary>
 | 
					/// <summary>网络结点扩展</summary>
 | 
				
			||||||
public static class EndPointExtensions
 | 
					public static class EndPointExtensions
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /// <summary>
 | 
					 | 
				
			||||||
    /// 
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    /// <param name="endpoint"></param>
 | 
					 | 
				
			||||||
    /// <returns></returns>
 | 
					 | 
				
			||||||
    public static String ToAddress(this EndPoint endpoint)
 | 
					    public static String ToAddress(this EndPoint endpoint)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return ((IPEndPoint)endpoint).ToAddress();
 | 
					        return ((IPEndPoint)endpoint).ToAddress();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// <summary>
 | 
					
 | 
				
			||||||
    /// 
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    /// <param name="endpoint"></param>
 | 
					 | 
				
			||||||
    /// <returns></returns>
 | 
					 | 
				
			||||||
    public static String ToAddress(this IPEndPoint endpoint)
 | 
					    public static String ToAddress(this IPEndPoint endpoint)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return String.Format("{0}:{1}", endpoint.Address, endpoint.Port);
 | 
					        return String.Format("{0}:{1}", endpoint.Address, endpoint.Port);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    private static readonly String[] SplitColon = new String[] { ":" };
 | 
					    private static readonly String[] SplitColon = new String[] { ":" };
 | 
				
			||||||
    /// <summary>
 | 
					
 | 
				
			||||||
    /// 
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    /// <param name="address"></param>
 | 
					 | 
				
			||||||
    /// <returns></returns>
 | 
					 | 
				
			||||||
    public static IPEndPoint ToEndPoint(this String address)
 | 
					    public static IPEndPoint ToEndPoint(this String address)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        var array = address.Split(SplitColon, StringSplitOptions.RemoveEmptyEntries);
 | 
					        var array = address.Split(SplitColon, StringSplitOptions.RemoveEmptyEntries);
 | 
				
			||||||
@@ -44,11 +31,7 @@ public static class EndPointExtensions
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static readonly String[] SplitComma = new String[] { "," };
 | 
					    private static readonly String[] SplitComma = new String[] { "," };
 | 
				
			||||||
    /// <summary>
 | 
					
 | 
				
			||||||
    /// 
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    /// <param name="addresses"></param>
 | 
					 | 
				
			||||||
    /// <returns></returns>
 | 
					 | 
				
			||||||
    public static IEnumerable<IPEndPoint> ToEndPoints(this String addresses)
 | 
					    public static IEnumerable<IPEndPoint> ToEndPoints(this String addresses)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        var array = addresses.Split(SplitComma, StringSplitOptions.RemoveEmptyEntries);
 | 
					        var array = addresses.Split(SplitComma, StringSplitOptions.RemoveEmptyEntries);
 | 
				
			||||||
 
 | 
				
			|||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user