mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-23 20:13:22 +08:00
release:6.0.4.38
refactor: 支持blazor hybrid; feat(s7): 支持多写;
This commit is contained in:
@@ -25,6 +25,8 @@ using System.Text;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Text.Json;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
using JwtRegisteredClaimNames = Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
@@ -403,7 +405,7 @@ public class JWTEncryption
|
||||
var tokenHandler = new JsonWebTokenHandler();
|
||||
try
|
||||
{
|
||||
var tokenValidationResult = tokenHandler.ValidateToken(accessToken, tokenValidationParameters);
|
||||
var tokenValidationResult = tokenHandler.ValidateTokenAsync(accessToken, tokenValidationParameters).GetFalseAwaitResult();
|
||||
if (!tokenValidationResult.IsValid) return (false, null, tokenValidationResult);
|
||||
|
||||
var jsonWebToken = tokenValidationResult.SecurityToken as JsonWebToken;
|
@@ -122,7 +122,25 @@ public static class StartupExtensions
|
||||
|
||||
return parameterInstances;
|
||||
}
|
||||
/// <summary>
|
||||
/// 解析方法参数实例
|
||||
/// </summary>
|
||||
private static object[] ResolveMethodParameterInstances(IServiceProvider service, MethodInfo method)
|
||||
{
|
||||
// 获取方法所有参数
|
||||
var parameters = method.GetParameters();
|
||||
var parameterInstances = new object[parameters.Length];
|
||||
parameterInstances[0] = service;
|
||||
|
||||
// 解析服务
|
||||
for (var i = 1; i < parameters.Length; i++)
|
||||
{
|
||||
var parameter = parameters[i];
|
||||
parameterInstances[i] = service.GetRequiredService(parameter.ParameterType);
|
||||
}
|
||||
|
||||
return parameterInstances;
|
||||
}
|
||||
/// <summary>
|
||||
/// 对配置文件名进行分组
|
||||
/// </summary>
|
||||
@@ -173,6 +191,26 @@ public static class StartupExtensions
|
||||
method.Invoke(startup, ResolveMethodParameterInstances(app, method));
|
||||
}
|
||||
}
|
||||
|
||||
// 遍历所有
|
||||
foreach (var startup in startups)
|
||||
{
|
||||
var type = startup.GetType();
|
||||
|
||||
// 获取所有符合依赖注入格式的方法,如返回值 void,且第一个参数是 IApplicationBuilder 类型
|
||||
var configureMethods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(u => u.ReturnType == typeof(void)
|
||||
&& u.GetParameters().Length > 0
|
||||
&& u.GetParameters().First().ParameterType == typeof(IServiceProvider));
|
||||
|
||||
if (!configureMethods.Any()) continue;
|
||||
|
||||
// 自动安装属性调用
|
||||
foreach (var method in configureMethods)
|
||||
{
|
||||
method.Invoke(startup, ResolveMethodParameterInstances(App.RootServices, method));
|
||||
}
|
||||
}
|
||||
AppStartups.Clear();
|
||||
}
|
||||
|
@@ -13,6 +13,8 @@ using BootstrapBlazor.Components;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
using ThingsGateway.Core;
|
||||
|
||||
using RouteAttribute = Microsoft.AspNetCore.Mvc.RouteAttribute;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
@@ -11,6 +11,9 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.Core;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
/// <summary>
|
@@ -11,6 +11,10 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.Core;
|
||||
using ThingsGateway.Foundation;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
/// <summary>
|
@@ -12,6 +12,8 @@ using BootstrapBlazor.Components;
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
/// <summary>
|
||||
@@ -26,6 +28,8 @@ public class GatewayExportController : ControllerBase
|
||||
private readonly IDeviceService _deviceService;
|
||||
private readonly IRpcLogService _rpcLogService;
|
||||
private readonly IVariableService _variableService;
|
||||
private readonly IImportExportService _importExportService;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="GatewayExportController"/>
|
||||
@@ -35,7 +39,8 @@ public class GatewayExportController : ControllerBase
|
||||
IDeviceService deviceService,
|
||||
IVariableService variableService,
|
||||
IBackendLogService backendLogService,
|
||||
IRpcLogService rpcLogService
|
||||
IRpcLogService rpcLogService,
|
||||
IImportExportService importExportService
|
||||
)
|
||||
{
|
||||
_backendLogService = backendLogService;
|
||||
@@ -43,6 +48,8 @@ public class GatewayExportController : ControllerBase
|
||||
_channelService = channelService;
|
||||
_deviceService = deviceService;
|
||||
_variableService = variableService;
|
||||
_importExportService = importExportService;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -54,8 +61,9 @@ public class GatewayExportController : ControllerBase
|
||||
{
|
||||
input.IsPage = false;
|
||||
input.IsVirtualScroll = false;
|
||||
var fileStreamResult = await _deviceService.ExportDeviceAsync(input, PluginTypeEnum.Business);
|
||||
return fileStreamResult;
|
||||
var sheets = await _deviceService.ExportDeviceAsync(input, PluginTypeEnum.Business);
|
||||
return await _importExportService.ExportAsync<Device>(sheets, "BusinessDevice", false);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -68,8 +76,8 @@ public class GatewayExportController : ControllerBase
|
||||
input.IsPage = false;
|
||||
input.IsVirtualScroll = false;
|
||||
|
||||
var fileStreamResult = await _channelService.ExportChannelAsync(input);
|
||||
return fileStreamResult;
|
||||
var sheets = await _channelService.ExportChannelAsync(input);
|
||||
return await _importExportService.ExportAsync<Channel>(sheets, "Channel", false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -82,8 +90,8 @@ public class GatewayExportController : ControllerBase
|
||||
input.IsPage = false;
|
||||
input.IsVirtualScroll = false;
|
||||
|
||||
var fileStreamResult = await _deviceService.ExportDeviceAsync(input, PluginTypeEnum.Collect);
|
||||
return fileStreamResult;
|
||||
var sheets = await _deviceService.ExportDeviceAsync(input, PluginTypeEnum.Collect);
|
||||
return await _importExportService.ExportAsync<Device>(sheets, "CollectDevice", false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -96,7 +104,7 @@ public class GatewayExportController : ControllerBase
|
||||
input.IsPage = false;
|
||||
input.IsVirtualScroll = false;
|
||||
|
||||
var fileStreamResult = await _variableService.ExportVariableAsync(input);
|
||||
return fileStreamResult;
|
||||
var sheets = await _variableService.ExportVariableAsync(input);
|
||||
return await _importExportService.ExportAsync<Variable>(sheets, "Variable", false);
|
||||
}
|
||||
}
|
@@ -17,6 +17,9 @@ using NewLife.Extension;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
|
||||
using ThingsGateway.Core;
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
/// <summary>
|
||||
@@ -35,7 +38,7 @@ public class RuntimeInfoControler : ControllerBase
|
||||
[HttpGet("deviceList")]
|
||||
public SqlSugarPagedList<DeviceData> GetCollectDeviceList([FromQuery] DevicePageInput input)
|
||||
{
|
||||
var data = GlobalData.CollectDevices.Values
|
||||
var data = GlobalData.ReadOnlyCollectDevices.Select(a => a.Value)
|
||||
.WhereIF(!string.IsNullOrEmpty(input.Name), u => u.Name.Contains(input.Name))
|
||||
.WhereIF(input.ChannelId != null, u => u.ChannelId == input.ChannelId)
|
||||
.WhereIF(!string.IsNullOrEmpty(input.PluginName), u => u.PluginName == input.PluginName)
|
@@ -10,12 +10,13 @@
|
||||
|
||||
using Microsoft.AspNetCore.StaticFiles;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace ThingsGateway.Server;
|
||||
|
||||
/// <inheritdoc/>
|
||||
internal static class CacheExtensions
|
||||
public static class CacheExtensions
|
||||
{
|
||||
private static int? _age;
|
||||
|
64
src/ThingsGateway.ASPNetCore/Locales/en-US.json
Normal file
64
src/ThingsGateway.ASPNetCore/Locales/en-US.json
Normal file
@@ -0,0 +1,64 @@
|
||||
{
|
||||
|
||||
"ThingsGateway.Gateway.Application.ConfigInfoControler": {
|
||||
"ConfigInfoControler": "Get configuration information",
|
||||
"GetChannelList": "Get channel information",
|
||||
"GetCollectDeviceList": "Get device information",
|
||||
"GetVariableList": "Get variable information"
|
||||
},
|
||||
"ThingsGateway.Gateway.Application.ControlControler": {
|
||||
"ControlControler": "Device control",
|
||||
"PasueCollectThread": "Control collection thread start/stop",
|
||||
"PasueBusinessThread": "Control business thread start/stop",
|
||||
"RestartCollectDeviceThread": "Restart collection thread",
|
||||
"RestartBusinessDeviceThread": "Restart business thread",
|
||||
"WriteDeviceMethods": "Write variables"
|
||||
},
|
||||
"ThingsGateway.Gateway.Application.RuntimeInfoControler": {
|
||||
"RuntimeInfoControler": "Get runtime information",
|
||||
"GetCollectDeviceList": "Get device information",
|
||||
"GetVariableList": "Get variable information",
|
||||
"GetRealAlarmList": "Get real-time alarm information"
|
||||
},
|
||||
|
||||
|
||||
"ThingsGateway.FileService": {
|
||||
"FileNullError": "File cannot be empty",
|
||||
"FileLengthError": "File size cannot exceed {0} M",
|
||||
"FileTypeError": "Not supported format {0}"
|
||||
},
|
||||
|
||||
//controller
|
||||
"ThingsGateway.Admin.Application.AuthController": {
|
||||
//auth
|
||||
"AuthController": "Login API",
|
||||
"LoginAsync": "Login",
|
||||
"LogoutAsync": "Logout"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.TestController": {
|
||||
//auth
|
||||
"TestController": "Test API",
|
||||
"Test": "Test"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.OpenApiAuthController": {
|
||||
//auth
|
||||
"OpenApiAuthController": "Login API",
|
||||
"LoginAsync": "Login",
|
||||
"LogoutAsync": "Logout"
|
||||
},
|
||||
|
||||
"ThingsGateway.UnifyResultProvider": {
|
||||
"TokenOver": "Login has expired, please login again",
|
||||
"NoPermission": "Access denied, no permission"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.AuthService": {
|
||||
"SingleLoginWarn": "Your account is logged in elsewhere",
|
||||
"UserNull": "User {0} does not exist",
|
||||
"MustDesc": "Password needs to be encrypted with DESC before passing",
|
||||
"PasswordError": "Too many password errors, please try again in {0} minutes",
|
||||
"UserDisable": "Account {0} has been disabled",
|
||||
"UserNoModule": "This account has not been assigned a module. Please contact the administrator",
|
||||
"AuthErrorMax": "Account password error, will be locked for {1} minutes after exceeding {0} times, error count {2}"
|
||||
}
|
||||
}
|
64
src/ThingsGateway.ASPNetCore/Locales/zh-CN.json
Normal file
64
src/ThingsGateway.ASPNetCore/Locales/zh-CN.json
Normal file
@@ -0,0 +1,64 @@
|
||||
{
|
||||
|
||||
"ThingsGateway.Gateway.Application.ConfigInfoControler": {
|
||||
"ConfigInfoControler": "获取配置信息",
|
||||
"GetChannelList": "获取通道信息",
|
||||
"GetCollectDeviceList": "获取设备信息",
|
||||
"GetVariableList": "获取变量信息"
|
||||
},
|
||||
"ThingsGateway.Gateway.Application.ControlControler": {
|
||||
"ControlControler": "设备控制",
|
||||
"PasueCollectThread": "控制采集线程启停",
|
||||
"PasueBusinessThread": "控制业务线程启停",
|
||||
"RestartCollectDeviceThread": "重启采集线程",
|
||||
"RestartBusinessDeviceThread": "重启业务线程",
|
||||
"WriteDeviceMethods": "写入变量"
|
||||
},
|
||||
"ThingsGateway.Gateway.Application.RuntimeInfoControler": {
|
||||
"RuntimeInfoControler": "获取运行态信息",
|
||||
"GetCollectDeviceList": "获取设备信息",
|
||||
"GetVariableList": "获取变量信息",
|
||||
"GetRealAlarmList": "获取实时报警信息"
|
||||
},
|
||||
|
||||
|
||||
"ThingsGateway.FileService": {
|
||||
"FileNullError": "文件不能为空",
|
||||
"FileLengthError": "文件大小不允许超过 {0} M",
|
||||
"FileTypeError": "不支持 {0} 格式"
|
||||
},
|
||||
|
||||
//controller
|
||||
"ThingsGateway.Admin.Application.AuthController": {
|
||||
//auth
|
||||
"AuthController": "登录API",
|
||||
"LoginAsync": "登录",
|
||||
"LogoutAsync": "注销"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.TestController": {
|
||||
//auth
|
||||
"TestController": "测试API",
|
||||
"Test": "测试"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.OpenApiAuthController": {
|
||||
//auth
|
||||
"OpenApiAuthController": "登录API",
|
||||
"LoginAsync": "登录",
|
||||
"LogoutAsync": "注销"
|
||||
},
|
||||
|
||||
"ThingsGateway.UnifyResultProvider": {
|
||||
"TokenOver": "登录已过期,请重新登录",
|
||||
"NoPermission": "禁止访问,没有权限"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.AuthService": {
|
||||
"SingleLoginWarn": "您的账号已在别处登录",
|
||||
"UserNull": "用户 {0} 不存在",
|
||||
"PasswordError": "密码错误次数过多,请 {0} 分钟后再试",
|
||||
"AuthErrorMax": "账号密码错误,超过 {0} 次后将锁定 {1} 分钟,错误次数 {2} ",
|
||||
"UserDisable": "账号 {0} 已停用",
|
||||
"MustDesc": "密码需要DESC加密后传入"
|
||||
}
|
||||
|
||||
}
|
@@ -8,6 +8,8 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Microsoft.AspNetCore.Builder;
|
@@ -9,6 +9,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace ThingsGateway.Server;
|
||||
|
@@ -10,6 +10,7 @@
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
using System.Security.Claims;
|
||||
|
102
src/ThingsGateway.ASPNetCore/Services/ApiPermissionService.cs
Normal file
102
src/ThingsGateway.ASPNetCore/Services/ApiPermissionService.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://kimdiego2098.github.io/
|
||||
// QQ群:605534569
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using Mapster;
|
||||
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
|
||||
using NewLife;
|
||||
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
|
||||
using ThingsGateway;
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.Core.Extension;
|
||||
|
||||
public class ApiPermissionService : IApiPermissionService
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public List<OpenApiPermissionTreeSelector> ApiPermissionTreeSelector()
|
||||
{
|
||||
var cacheKey = $"{nameof(ApiPermissionTreeSelector)}-{CultureInfo.CurrentUICulture.Name}";
|
||||
var permissions = NetCoreApp.CacheService.GetOrCreate(cacheKey, entry =>
|
||||
{
|
||||
List<OpenApiPermissionTreeSelector> permissions = new();//权限列表
|
||||
|
||||
// 获取所有需要数据权限的控制器
|
||||
var controllerTypes =
|
||||
NetCoreApp.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(RolePermissionAttribute), false));
|
||||
foreach (var controller in controllerTypes)
|
||||
{
|
||||
//获取数据权限特性
|
||||
var route = controller.GetCustomAttributes<Microsoft.AspNetCore.Mvc.RouteAttribute>().FirstOrDefault();
|
||||
if (route == null) continue;
|
||||
|
||||
var description = controller.GetTypeDisplayName();
|
||||
var routeName = ResourceUtil.GetRouteName(controller.Name, route.Template);//赋值路由名称
|
||||
OpenApiPermissionTreeSelector openApiGroup = new() { ApiName = description ?? routeName, ApiRoute = routeName };
|
||||
//获取所有方法
|
||||
var menthods = controller.GetRuntimeMethods();
|
||||
//遍历方法
|
||||
foreach (var menthod in menthods)
|
||||
{
|
||||
//获取忽略数据权限特性
|
||||
var ignoreRolePermission = menthod.GetCustomAttribute<IgnoreRolePermissionAttribute>();
|
||||
if (ignoreRolePermission == null)//如果是空的代表需要数据权限
|
||||
{
|
||||
//获取接口描述
|
||||
var methodDesc = controller.GetMethodDisplayName(menthod.Name);
|
||||
//if (methodDesc != null)
|
||||
{
|
||||
//默认路由名称
|
||||
var apiRoute = menthod.Name.ToLowerCamelCase();
|
||||
//获取get特性
|
||||
var requestGet = menthod.GetCustomAttribute<Microsoft.AspNetCore.Mvc.HttpGetAttribute>();
|
||||
if (requestGet != null)//如果是get方法
|
||||
apiRoute = requestGet.Template;
|
||||
else
|
||||
{
|
||||
//获取post特性
|
||||
var requestPost = menthod.GetCustomAttribute<Microsoft.AspNetCore.Mvc.HttpPostAttribute>();
|
||||
if (requestPost != null)//如果是post方法
|
||||
apiRoute = requestPost.Template;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
//apiRoute = route.Template + $"/{apiRoute}";
|
||||
apiRoute = routeName + $"/{apiRoute}";
|
||||
|
||||
//添加到权限列表
|
||||
openApiGroup.Children ??= new();
|
||||
openApiGroup.Children.Add(new OpenApiPermissionTreeSelector
|
||||
{
|
||||
ApiName = methodDesc,
|
||||
ApiRoute = apiRoute,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
permissions.Add(openApiGroup);
|
||||
}
|
||||
return permissions;
|
||||
});
|
||||
return permissions;
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://kimdiego2098.github.io/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
|
||||
using System.Net;
|
||||
using System.Security.Claims;
|
||||
|
||||
using UAParser;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
public class AspNetCoreAppService : IAppService
|
||||
{
|
||||
public string GetReturnUrl(string returnUrl)
|
||||
{
|
||||
var url = QueryHelpers.AddQueryString(CookieAuthenticationDefaults.LoginPath, new Dictionary<string, string?>
|
||||
{
|
||||
["ReturnUrl"] = returnUrl
|
||||
});
|
||||
return url;
|
||||
}
|
||||
public ClientInfo? ClientInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
var str = App.HttpContext?.Request?.Headers?.UserAgent;
|
||||
ClientInfo? clientInfo = null;
|
||||
if (str.HasValue)
|
||||
{
|
||||
clientInfo = Parser.GetDefault().Parse(str);
|
||||
}
|
||||
return clientInfo;
|
||||
}
|
||||
}
|
||||
|
||||
public ClaimsPrincipal? User => App.User;
|
||||
|
||||
public IPAddress? RemoteIpAddress => App.HttpContext?.Connection?.RemoteIpAddress;
|
||||
|
||||
public int LocalPort => App.HttpContext.Connection.LocalPort;
|
||||
}
|
70
src/ThingsGateway.ASPNetCore/Services/AuthRazorService.cs
Normal file
70
src/ThingsGateway.ASPNetCore/Services/AuthRazorService.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://kimdiego2098.github.io/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using ThingsGateway.Core.Json.Extension;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
|
||||
public class AuthRazorService : IAuthRazorService
|
||||
{
|
||||
|
||||
private AjaxService AjaxService { get;set; }
|
||||
public AuthRazorService(AjaxService ajaxService)
|
||||
{
|
||||
AjaxService = ajaxService;
|
||||
}
|
||||
/// <summary>
|
||||
/// 用户登录
|
||||
/// </summary>
|
||||
public async Task<UnifyResult<LoginOutput>> LoginAsync(LoginInput input)
|
||||
{
|
||||
var ajaxOption = new AjaxOption
|
||||
{
|
||||
Url = "/api/auth/login",
|
||||
Method = "POST",
|
||||
Data = input,
|
||||
};
|
||||
var str = await AjaxService.InvokeAsync(ajaxOption);
|
||||
if (str != null)
|
||||
{
|
||||
var ret = str.RootElement.GetRawText().FromSystemTextJsonString<UnifyResult<LoginOutput>>();
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注销当前用户
|
||||
/// </summary>
|
||||
public async Task<UnifyResult<object>> LoginOutAsync()
|
||||
{
|
||||
var ajaxOption = new AjaxOption
|
||||
{
|
||||
Url = "/api/auth/logout",
|
||||
Method = "POST",
|
||||
};
|
||||
using var str = await AjaxService.InvokeAsync(ajaxOption);
|
||||
if (str != null)
|
||||
{
|
||||
var ret = str.RootElement.GetRawText().FromSystemTextJsonString<UnifyResult<object>>();
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
}
|
||||
}
|
@@ -17,6 +17,8 @@ using SqlSugar;
|
||||
|
||||
using System.Security.Claims;
|
||||
|
||||
using ThingsGateway.Core;
|
||||
|
||||
using Yitter.IdGenerator;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
@@ -70,7 +70,7 @@ public class FileService : IFileService
|
||||
/// <param name="pPath">保存路径</param>
|
||||
/// <param name="file">文件</param>
|
||||
/// <returns>最终全路径</returns>
|
||||
public async Task<string> UploadFileAsync(string pPath, IBrowserFile file)
|
||||
public async Task<string> UploadFileAsync( IBrowserFile file,string pPath= "imports")
|
||||
{
|
||||
return await file.StorageLocal(pPath);
|
||||
}
|
@@ -38,7 +38,7 @@ public interface IFileService
|
||||
/// <param name="pPath">保存路径</param>
|
||||
/// <param name="file">文件流</param>
|
||||
/// <returns>最终全路径</returns>
|
||||
Task<string> UploadFileAsync(string pPath, IBrowserFile file);
|
||||
Task<string> UploadFileAsync( IBrowserFile file, string pPath="imports");
|
||||
|
||||
/// <summary>
|
||||
/// 验证文件信息
|
@@ -19,6 +19,7 @@ using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.Core;
|
||||
|
||||
using Yitter.IdGenerator;
|
||||
@@ -47,26 +48,7 @@ public class ImportExportService : IImportExportService
|
||||
public async Task<FileStreamResult> ExportAsync<T>(object input, string fileName, bool isDynamicExcelColumn = true) where T : class
|
||||
{
|
||||
|
||||
if (!fileName.Contains("."))
|
||||
fileName += ".xlsx";
|
||||
|
||||
var path = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "exports");
|
||||
Directory.CreateDirectory(path);
|
||||
|
||||
string searchPattern = $"*{fileName}"; // 文件名匹配模式
|
||||
string[] files = Directory.GetFiles(path, searchPattern);
|
||||
|
||||
//删除同后缀的文件
|
||||
var whereFiles = files.Where(file => File.GetLastWriteTime(file) < DateTime.Now.AddMinutes(-2));
|
||||
|
||||
foreach (var file in whereFiles)
|
||||
{
|
||||
if (File.Exists(file))
|
||||
{
|
||||
File.SetAttributes(file, FileAttributes.Normal);
|
||||
File.Delete(file);
|
||||
}
|
||||
}
|
||||
var path = ImportExportUtil.GetFileDir(ref fileName);
|
||||
|
||||
fileName = YitIdHelper.NextId() + fileName;
|
||||
var filePath = Path.Combine(path, fileName);
|
||||
@@ -92,7 +74,7 @@ public class ImportExportService : IImportExportService
|
||||
public async Task<string> UploadFileAsync(IBrowserFile file)
|
||||
{
|
||||
_fileService.Verification(file);
|
||||
return await _fileService.UploadFileAsync("imports", file);
|
||||
return await _fileService.UploadFileAsync(file);
|
||||
}
|
||||
|
||||
#endregion 导入
|
90
src/ThingsGateway.ASPNetCore/Startup.cs
Normal file
90
src/ThingsGateway.ASPNetCore/Startup.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://kimdiego2098.github.io/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.Admin.Razor;
|
||||
using ThingsGateway.Logging;
|
||||
|
||||
using UAParser;
|
||||
|
||||
namespace ThingsGateway.Server;
|
||||
|
||||
[AppStartup(-99999)]
|
||||
public class Startup : AppStartup
|
||||
{
|
||||
public void ConfigBlazorServer(IServiceCollection services)
|
||||
{
|
||||
|
||||
|
||||
#region api日志
|
||||
|
||||
//Monitor日志配置
|
||||
services.AddMonitorLogging(options =>
|
||||
{
|
||||
options.JsonIndented = true;// 是否美化 JSON
|
||||
options.GlobalEnabled = false;//全局启用
|
||||
options.ConfigureLogger((logger, logContext, context) =>
|
||||
{
|
||||
var httpContext = context.HttpContext;//获取httpContext
|
||||
//获取头
|
||||
var userAgent = httpContext.Request.Headers["User-Agent"];
|
||||
if (string.IsNullOrEmpty(userAgent)) userAgent = "Other";//如果没有这个头就指定一个
|
||||
|
||||
var parser = Parser.GetDefault();
|
||||
//获取客户端信息
|
||||
var client = parser.Parse(userAgent);
|
||||
// 获取控制器/操作描述器
|
||||
var controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
|
||||
//操作名称默认是控制器名加方法名,自定义操作名称要在action上加Description特性
|
||||
var option = $"{controllerActionDescriptor.ControllerName}/{controllerActionDescriptor.ActionName}";
|
||||
|
||||
var desc = NetCoreApp.CreateLocalizerByType(controllerActionDescriptor.ControllerTypeInfo.AsType())[controllerActionDescriptor.MethodInfo.Name];
|
||||
//获取特性
|
||||
option = desc.Value;//则将操作名称赋值为控制器上写的title
|
||||
|
||||
logContext.Set(LoggingConst.CateGory, option);//传操作名称
|
||||
logContext.Set(LoggingConst.Operation, option);//传操作名称
|
||||
logContext.Set(LoggingConst.Client, client);//客户端信息
|
||||
logContext.Set(LoggingConst.Path, httpContext.Request.Path.Value);//请求地址
|
||||
logContext.Set(LoggingConst.Method, httpContext.Request.Method);//请求方法
|
||||
});
|
||||
});
|
||||
//日志写入数据库配置
|
||||
services.AddDatabaseLogging<DatabaseLoggingWriter>(options =>
|
||||
{
|
||||
options.WriteFilter = (logMsg) =>
|
||||
{
|
||||
return logMsg.LogName == "System.Logging.LoggingMonitor";//只写入LoggingMonitor日志
|
||||
};
|
||||
});
|
||||
|
||||
#endregion api日志
|
||||
|
||||
|
||||
services.AddSingleton<IUnifyResultProvider, UnifyResultProvider>();
|
||||
services.AddSingleton<IAuthService, AuthService>();
|
||||
services.AddScoped<IAuthRazorService, AuthRazorService>();
|
||||
services.AddSingleton<IAppService, AspNetCoreAppService>();
|
||||
services.AddSingleton<IApiPermissionService, ApiPermissionService>();
|
||||
|
||||
services.AddSingleton<IFileService, FileService>();
|
||||
services.AddSingleton<IImportExportService, ImportExportService>();
|
||||
|
||||
services.AddSingleton<ISignalrNoticeService, SignalrNoticeService>();
|
||||
services.AddSingleton<IAuthService, AuthService>();
|
||||
|
||||
}
|
||||
|
||||
}
|
35
src/ThingsGateway.ASPNetCore/ThingsGateway.ASPNetCore.csproj
Normal file
35
src/ThingsGateway.ASPNetCore/ThingsGateway.ASPNetCore.csproj
Normal file
@@ -0,0 +1,35 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
<Import Project="$(SolutionDir)Version.props" />
|
||||
<Import Project="$(SolutionDir)PackNuget.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0;</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
<ProjectReference Include="$(SolutionDir)\ThingsGateway.Admin.Razor\ThingsGateway.Admin.Razor.csproj" />
|
||||
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.7" />
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<PropertyGroup Condition="'$(SolutionName)'=='ThingsGateway - Admin'">
|
||||
<DefineConstants>Admin</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(SolutionName)' != 'ThingsGateway - Admin'">
|
||||
<ProjectReference Include="$(SolutionDir)\ThingsGateway.Gateway.Razor\ThingsGateway.Gateway.Razor.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Locales\*.json">
|
||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
|
||||
|
17
src/ThingsGateway.ASPNetCore/_Imports.razor
Normal file
17
src/ThingsGateway.ASPNetCore/_Imports.razor
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
@using System.Net.Http
|
||||
@using System.Net.Http.Json
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||
@using Microsoft.JSInterop
|
||||
@using Microsoft.AspNetCore.Authorization
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@using Microsoft.Extensions.Localization
|
||||
|
||||
@using System.ComponentModel
|
||||
@using System.ComponentModel.DataAnnotations
|
||||
|
||||
|
||||
@using ThingsGateway.Razor;
|
@@ -8,23 +8,4 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.AspNetCore.Http.Connections.Features;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
public class UserIdProvider : IUserIdProvider
|
||||
{
|
||||
public string GetUserId(HubConnectionContext connection)
|
||||
{
|
||||
var feature = connection.Features.Get<IHttpContextFeature>();
|
||||
var UserId = feature!.HttpContext!.Request.Headers[ClaimConst.UserId].FirstOrDefault()?.ToLong();
|
||||
|
||||
if (UserId > 0)
|
||||
{
|
||||
return $"{UserId}";
|
||||
}
|
||||
|
||||
return connection.ConnectionId;
|
||||
}
|
||||
}
|
||||
global using Microsoft.Extensions.Options;
|
@@ -32,11 +32,13 @@ public class OperDescAttribute : MoAttribute
|
||||
/// 日志消息队列(线程安全)
|
||||
/// </summary>
|
||||
private static readonly ConcurrentQueue<SysOperateLog> _logMessageQueue = new();
|
||||
private static readonly IAppService AppService;
|
||||
|
||||
static OperDescAttribute()
|
||||
{
|
||||
// 创建长时间运行的后台任务,并将日志消息队列中数据写入存储中
|
||||
Task.Factory.StartNew(ProcessQueue, TaskCreationOptions.LongRunning);
|
||||
AppService = NetCoreApp.RootServices.GetService<IAppService>();
|
||||
}
|
||||
|
||||
public OperDescAttribute(string description, bool isRecordPar = true, object localizerType = null)
|
||||
@@ -89,8 +91,8 @@ public class OperDescAttribute : MoAttribute
|
||||
private static async Task ProcessQueue()
|
||||
{
|
||||
var db = DbContext.Db.GetConnectionScopeWithAttr<SysOperateLog>().CopyNew();
|
||||
var appLifetime = App.RootServices!.GetService<IHostApplicationLifetime>()!;
|
||||
while (!(appLifetime.ApplicationStopping.IsCancellationRequested || appLifetime.ApplicationStopped.IsCancellationRequested))
|
||||
var appLifetime = NetCoreApp.RootServices!.GetService<IHostApplicationLifetime>()!;
|
||||
while (!((appLifetime?.ApplicationStopping ?? default).IsCancellationRequested || (appLifetime?.ApplicationStopped ?? default).IsCancellationRequested))
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -98,7 +100,7 @@ public class OperDescAttribute : MoAttribute
|
||||
{
|
||||
await db.InsertableWithAttr(_logMessageQueue.ToListWithDequeue()).ExecuteCommandAsync();//入库
|
||||
}
|
||||
await Task.Delay(3000, appLifetime.ApplicationStopping).ConfigureAwait(false);
|
||||
await Task.Delay(3000, appLifetime?.ApplicationStopping ?? default).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -109,13 +111,8 @@ public class OperDescAttribute : MoAttribute
|
||||
|
||||
private SysOperateLog GetOperLog(Type? localizerType, MethodContext context)
|
||||
{
|
||||
var str = App.HttpContext?.Request?.Headers?.UserAgent;
|
||||
var methodBase = context.Method;
|
||||
ClientInfo? clientInfo = null;
|
||||
if (str.HasValue)
|
||||
{
|
||||
clientInfo = Parser.GetDefault().Parse(str);
|
||||
}
|
||||
ClientInfo? clientInfo = AppService.ClientInfo;
|
||||
string? paramJson = null;
|
||||
if (IsRecordPar)
|
||||
{
|
||||
@@ -134,10 +131,10 @@ public class OperDescAttribute : MoAttribute
|
||||
//操作日志表实体
|
||||
var log = new SysOperateLog
|
||||
{
|
||||
Name = (localizerType == null ? App.CreateLocalizerByType(typeof(OperDescAttribute)) : App.CreateLocalizerByType(localizerType))![Description],
|
||||
Name = (localizerType == null ? NetCoreApp.CreateLocalizerByType(typeof(OperDescAttribute)) : NetCoreApp.CreateLocalizerByType(localizerType))![Description],
|
||||
Category = LogCateGoryEnum.Operate,
|
||||
ExeStatus = true,
|
||||
OpIp = App.HttpContext?.Connection?.RemoteIpAddress?.MapToIPv4()?.ToString(),
|
||||
OpIp = AppService?.RemoteIpAddress?.MapToIPv4()?.ToString(),
|
||||
OpBrowser = clientInfo?.UA?.Family + clientInfo?.UA?.Major,
|
||||
OpOs = clientInfo?.OS?.Family + clientInfo?.OS?.Major,
|
||||
OpTime = DateTime.Now,
|
||||
|
@@ -27,8 +27,8 @@ internal class AdminTaskService : BackgroundService
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
//实现 删除过期日志 功能,不需要精确的时间
|
||||
var daysAgo = App.Configuration.GetSection("LogJob:DaysAgo").Get<int?>() ?? 1;
|
||||
var verificatInfoService = App.RootServices.GetService<IVerificatInfoService>();
|
||||
var daysAgo = NetCoreApp.Configuration.GetSection("LogJob:DaysAgo").Get<int?>() ?? 1;
|
||||
var verificatInfoService = NetCoreApp.RootServices.GetService<IVerificatInfoService>();
|
||||
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
|
@@ -82,7 +82,7 @@ public class HardwareInfoService : BackgroundService
|
||||
string currentPath = Directory.GetCurrentDirectory();
|
||||
DriveInfo drive = new(Path.GetPathRoot(currentPath));
|
||||
|
||||
HardwareInfoConfig = App.Configuration.GetSection(nameof(HardwareInfoConfig)).Get<HardwareInfoConfig?>() ?? new HardwareInfoConfig();
|
||||
HardwareInfoConfig = NetCoreApp.Configuration.GetSection(nameof(HardwareInfoConfig)).Get<HardwareInfoConfig?>() ?? new HardwareInfoConfig();
|
||||
HardwareInfoConfig.DaysAgo = Math.Min(Math.Max(HardwareInfoConfig.DaysAgo, 1), 7);
|
||||
HardwareInfoConfig.RealInterval = 30;
|
||||
HardwareInfoConfig.Enable = true;
|
||||
@@ -90,7 +90,7 @@ public class HardwareInfoService : BackgroundService
|
||||
APPInfo.DriveInfo = drive;
|
||||
APPInfo.OsArchitecture = Environment.OSVersion.Platform.ToString() + " " + RuntimeInformation.OSArchitecture.ToString(); // 系统架构
|
||||
APPInfo.FrameworkDescription = RuntimeInformation.FrameworkDescription; // NET框架
|
||||
APPInfo.Environment = App.IsDevelopment ? "Development" : "Production";
|
||||
APPInfo.Environment = NetCoreApp.IsDevelopment ? "Development" : "Production";
|
||||
APPInfo.UUID = DESCEncryption.Encrypt(APPInfo.MachineInfo.UUID + APPInfo.MachineInfo.Guid + APPInfo.MachineInfo.DiskID);
|
||||
|
||||
APPInfo.UpdateTime = TimerX.Now.ToDefaultDateTimeFormat();
|
||||
@@ -136,7 +136,7 @@ public class HardwareInfoService : BackgroundService
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (!error)
|
||||
_logger.LogWarning(ex, App.CreateLocalizerByType(this.GetType())["GetHardwareInfoFail"]);
|
||||
_logger.LogWarning(ex, NetCoreApp.CreateLocalizerByType(this.GetType())["GetHardwareInfoFail"]);
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
@@ -1,9 +1,5 @@
|
||||
{
|
||||
"ThingsGateway.FileService": {
|
||||
"FileNullError": "File cannot be empty",
|
||||
"FileLengthError": "File size cannot exceed {0} M",
|
||||
"FileTypeError": "Not supported format {0}"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.APPInfo": {
|
||||
"Environment": "HostEnvironment",
|
||||
"FrameworkDescription": ".NETFramework",
|
||||
@@ -19,24 +15,6 @@
|
||||
"Battery": "Battery"
|
||||
},
|
||||
|
||||
//controller
|
||||
"ThingsGateway.Admin.Application.AuthController": {
|
||||
//auth
|
||||
"AuthController": "Login API",
|
||||
"LoginAsync": "Login",
|
||||
"LogoutAsync": "Logout"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.TestController": {
|
||||
//auth
|
||||
"TestController": "Test API",
|
||||
"Test": "Test"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.OpenApiAuthController": {
|
||||
//auth
|
||||
"OpenApiAuthController": "Login API",
|
||||
"LoginAsync": "Login",
|
||||
"LogoutAsync": "Logout"
|
||||
},
|
||||
|
||||
//oper
|
||||
"ThingsGateway.Admin.Application.OperDescAttribute": {
|
||||
@@ -83,10 +61,6 @@
|
||||
"ExitSession": "Force session off"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.UnifyResultProvider": {
|
||||
"TokenOver": "Login has expired, please login again",
|
||||
"NoPermission": "Access denied, no permission"
|
||||
},
|
||||
|
||||
//service
|
||||
"ThingsGateway.Admin.Application.SessionService": {
|
||||
@@ -96,15 +70,6 @@
|
||||
"ThingsGateway.Admin.Application.HardwareInfoService": {
|
||||
"GetHardwareInfoFail": "Get Hardwareinfo Fail"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.AuthService": {
|
||||
"SingleLoginWarn": "Your account is logged in elsewhere",
|
||||
"UserNull": "User {0} does not exist",
|
||||
"MustDesc": "Password needs to be encrypted with DESC before passing",
|
||||
"PasswordError": "Too many password errors, please try again in {0} minutes",
|
||||
"UserDisable": "Account {0} has been disabled",
|
||||
"UserNoModule": "This account has not been assigned a module. Please contact the administrator",
|
||||
"AuthErrorMax": "Account password error, will be locked for {1} minutes after exceeding {0} times, error count {2}"
|
||||
},
|
||||
|
||||
//dto
|
||||
"ThingsGateway.Admin.Application.UserSelectorOutput": {
|
||||
|
@@ -1,10 +1,4 @@
|
||||
{
|
||||
"ThingsGateway.FileService": {
|
||||
"FileNullError": "文件不能为空",
|
||||
"FileLengthError": "文件大小不允许超过 {0} M",
|
||||
"FileTypeError": "不支持 {0} 格式"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.APPInfo": {
|
||||
"Environment": "主机环境",
|
||||
"FrameworkDescription": "NET框架",
|
||||
@@ -20,24 +14,6 @@
|
||||
"Battery": "电池"
|
||||
},
|
||||
|
||||
//controller
|
||||
"ThingsGateway.Admin.Application.AuthController": {
|
||||
//auth
|
||||
"AuthController": "登录API",
|
||||
"LoginAsync": "登录",
|
||||
"LogoutAsync": "注销"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.TestController": {
|
||||
//auth
|
||||
"TestController": "测试API",
|
||||
"Test": "测试"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.OpenApiAuthController": {
|
||||
//auth
|
||||
"OpenApiAuthController": "登录API",
|
||||
"LoginAsync": "登录",
|
||||
"LogoutAsync": "注销"
|
||||
},
|
||||
//oper
|
||||
"ThingsGateway.Admin.Application.OperDescAttribute": {
|
||||
//dict
|
||||
@@ -83,11 +59,6 @@
|
||||
"ExitSession": "强退会话"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.UnifyResultProvider": {
|
||||
"TokenOver": "登录已过期,请重新登录",
|
||||
"NoPermission": "禁止访问,没有权限"
|
||||
},
|
||||
|
||||
//service
|
||||
"ThingsGateway.Admin.Application.SessionService": {
|
||||
},
|
||||
@@ -96,14 +67,6 @@
|
||||
"ThingsGateway.Admin.Application.HardwareInfoService": {
|
||||
"GetHardwareInfoFail": "获取硬件信息出错"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.AuthService": {
|
||||
"SingleLoginWarn": "您的账号已在别处登录",
|
||||
"UserNull": "用户 {0} 不存在",
|
||||
"PasswordError": "密码错误次数过多,请 {0} 分钟后再试",
|
||||
"AuthErrorMax": "账号密码错误,超过 {0} 次后将锁定 {1} 分钟,错误次数 {2} ",
|
||||
"UserDisable": "账号 {0} 已停用",
|
||||
"MustDesc": "密码需要DESC加密后传入"
|
||||
},
|
||||
|
||||
//dto
|
||||
"ThingsGateway.Admin.Application.UserSelectorOutput": {
|
||||
|
@@ -8,24 +8,24 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Net;
|
||||
using System.Security.Claims;
|
||||
|
||||
using UAParser;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 即时通讯集线器
|
||||
/// </summary>
|
||||
public interface ISysHub
|
||||
{
|
||||
/// <summary>
|
||||
/// 退出登录
|
||||
/// </summary>
|
||||
/// <param name="message">消息</param>
|
||||
/// <returns></returns>
|
||||
Task LoginOut(string message);
|
||||
|
||||
/// <summary>
|
||||
/// 新消息
|
||||
/// </summary>
|
||||
/// <param name="message">消息</param>
|
||||
/// <returns></returns>
|
||||
Task NewMessage(SignalRMessage message);
|
||||
public interface IAppService
|
||||
{
|
||||
public ClientInfo? ClientInfo { get; }
|
||||
|
||||
public ClaimsPrincipal? User { get; }
|
||||
public IPAddress? RemoteIpAddress { get; }
|
||||
|
||||
public string GetReturnUrl(string returnUrl);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@@ -146,7 +146,7 @@ public class SysDictService : BaseService<SysDict>, ISysDictService
|
||||
[OperDesc("EditWebsitePolicy")]
|
||||
public async Task EditWebsitePolicyAsync(WebsitePolicy input)
|
||||
{
|
||||
var websiteOptions = App.Configuration?.GetSection(nameof(WebsiteOptions)).Get<WebsiteOptions>()!;
|
||||
var websiteOptions = NetCoreApp.Configuration?.GetSection(nameof(WebsiteOptions)).Get<WebsiteOptions>()!;
|
||||
if (websiteOptions.Demo)
|
||||
{
|
||||
throw Oops.Bah(Localizer["DemoCanotUpdateWebsitePolicy"]);
|
||||
@@ -184,7 +184,7 @@ public class SysDictService : BaseService<SysDict>, ISysDictService
|
||||
public async Task<AppConfig> GetAppConfigAsync()
|
||||
{
|
||||
var key = $"{CacheConst.Cache_SysDict}{DictTypeEnum.System}{nameof(AppConfig)}";//系统配置key
|
||||
var appConfig = App.CacheService.Get<AppConfig>(key);
|
||||
var appConfig = NetCoreApp.CacheService.Get<AppConfig>(key);
|
||||
if (appConfig == null)
|
||||
{
|
||||
List<SysDict> sysDicts = await GetSystemConfigAsync();
|
||||
@@ -211,7 +211,7 @@ public class SysDictService : BaseService<SysDict>, ISysDictService
|
||||
//网站设置
|
||||
appConfig.WebsitePolicy.WebStatus = sysDicts.FirstOrDefault(a => a.Category == nameof(WebsitePolicy) && a.Name == nameof(WebsitePolicy.WebStatus))?.Code.ToBoolean() ?? true;
|
||||
appConfig.WebsitePolicy.CloseTip = sysDicts.FirstOrDefault(a => a.Category == nameof(WebsitePolicy) && a.Name == nameof(WebsitePolicy.CloseTip))?.Code ?? "";
|
||||
App.CacheService.Set(key, appConfig);
|
||||
NetCoreApp.CacheService.Set(key, appConfig);
|
||||
}
|
||||
|
||||
return appConfig;
|
||||
@@ -227,12 +227,12 @@ public class SysDictService : BaseService<SysDict>, ISysDictService
|
||||
{
|
||||
var key = CacheConst.Cache_SysDict + DictTypeEnum.Define;
|
||||
var field = $"{category}:sysdict:{name}";
|
||||
var sysDict = App.CacheService.HashGetOne<SysDict>(key, field);
|
||||
var sysDict = NetCoreApp.CacheService.HashGetOne<SysDict>(key, field);
|
||||
if (sysDict == null)
|
||||
{
|
||||
using var db = GetDB();
|
||||
sysDict = await db.Queryable<SysDict>().FirstAsync(a => a.DictType == DictTypeEnum.System && a.Category == category && a.Name == a.Name);
|
||||
App.CacheService.HashAdd(key, field, sysDict);
|
||||
NetCoreApp.CacheService.HashAdd(key, field, sysDict);
|
||||
}
|
||||
return sysDict;
|
||||
}
|
||||
@@ -244,12 +244,12 @@ public class SysDictService : BaseService<SysDict>, ISysDictService
|
||||
public async Task<List<SysDict>> GetSystemConfigAsync()
|
||||
{
|
||||
var key = $"{CacheConst.Cache_SysDict}{DictTypeEnum.System}";//系统配置key
|
||||
var sysDicts = App.CacheService.Get<List<SysDict>>(key);
|
||||
var sysDicts = NetCoreApp.CacheService.Get<List<SysDict>>(key);
|
||||
if (sysDicts == null)
|
||||
{
|
||||
using var db = GetDB();
|
||||
sysDicts = await db.Queryable<SysDict>().Where(a => a.DictType == DictTypeEnum.System).ToListAsync();
|
||||
App.CacheService.Set(key, sysDicts);
|
||||
NetCoreApp.CacheService.Set(key, sysDicts);
|
||||
}
|
||||
|
||||
return sysDicts;
|
||||
@@ -310,9 +310,9 @@ public class SysDictService : BaseService<SysDict>, ISysDictService
|
||||
/// <returns></returns>
|
||||
private void RefreshCache(DictTypeEnum define)
|
||||
{
|
||||
App.CacheService.Remove($"{CacheConst.Cache_SysDict}{define}");
|
||||
NetCoreApp.CacheService.Remove($"{CacheConst.Cache_SysDict}{define}");
|
||||
if (define == DictTypeEnum.System)
|
||||
App.CacheService.Remove($"{CacheConst.Cache_SysDict}{define}{nameof(AppConfig)}");
|
||||
NetCoreApp.CacheService.Remove($"{CacheConst.Cache_SysDict}{define}{nameof(AppConfig)}");
|
||||
}
|
||||
|
||||
#endregion 方法
|
||||
|
@@ -0,0 +1,19 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://kimdiego2098.github.io/
|
||||
// QQ群:605534569
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
|
||||
public interface IApiPermissionService
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public List<OpenApiPermissionTreeSelector> ApiPermissionTreeSelector();
|
||||
|
||||
}
|
@@ -10,13 +10,16 @@
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 通讯器常量
|
||||
/// </summary>
|
||||
public class HubConst
|
||||
|
||||
public interface IAuthRazorService
|
||||
{
|
||||
/// <summary>
|
||||
/// 系统HubUrl
|
||||
/// 用户登录
|
||||
/// </summary>
|
||||
public const string SysHubUrl = "/hubs/thingsgateway";
|
||||
Task<UnifyResult<LoginOutput>> LoginAsync(LoginInput input);
|
||||
|
||||
/// <summary>
|
||||
/// 注销当前用户
|
||||
/// </summary>
|
||||
Task<UnifyResult<object>> LoginOutAsync();
|
||||
}
|
@@ -10,7 +10,7 @@
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Data;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
@@ -25,13 +25,6 @@ public interface ISysOperateLogService
|
||||
/// <param name="category">日志分类</param>
|
||||
Task DeleteAsync(LogCateGoryEnum category);
|
||||
|
||||
/// <summary>
|
||||
/// 导出操作日志文件
|
||||
/// </summary>
|
||||
/// <param name="input">查询条件</param>
|
||||
/// <returns>文件流</returns>
|
||||
Task<FileStreamResult> ExportFileAsync(QueryPageOptions input);
|
||||
|
||||
/// <summary>
|
||||
/// 获取最新的十条日志
|
||||
/// </summary>
|
||||
|
@@ -10,8 +10,6 @@
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using NewLife.Extension;
|
||||
|
||||
using SqlSugar;
|
||||
@@ -22,12 +20,6 @@ namespace ThingsGateway.Admin.Application;
|
||||
|
||||
public class SysOperateLogService : BaseService<SysOperateLog>, ISysOperateLogService
|
||||
{
|
||||
private readonly IImportExportService _importExportService;
|
||||
|
||||
public SysOperateLogService(IImportExportService importExportService)
|
||||
{
|
||||
_importExportService = importExportService;
|
||||
}
|
||||
|
||||
#region 查询
|
||||
|
||||
@@ -100,40 +92,6 @@ public class SysOperateLogService : BaseService<SysOperateLog>, ISysOperateLogSe
|
||||
|
||||
#endregion 删除
|
||||
|
||||
#region 导出
|
||||
|
||||
/// <summary>
|
||||
/// 导出日志
|
||||
/// </summary>
|
||||
/// <param name="input">IDataReader,为空时导出全部</param>
|
||||
/// <returns>文件流</returns>
|
||||
[OperDesc("ExportOperLog", isRecordPar: false)]
|
||||
public async Task<FileStreamResult> ExportFileAsync(IDataReader? input = null)
|
||||
{
|
||||
if (input != null)
|
||||
{
|
||||
return await _importExportService.ExportAsync<SysOperateLog>(input, "OperateLog");
|
||||
}
|
||||
using var db = GetDB();
|
||||
var query = db.Queryable<SysOperateLog>().ExportIgnoreColumns();
|
||||
var sqlObj = query.ToSql();
|
||||
using IDataReader? dataReader = await db.Ado.GetDataReaderAsync(sqlObj.Key, sqlObj.Value);
|
||||
return await _importExportService.ExportAsync<SysOperateLog>(dataReader, "OperateLog");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导出日志
|
||||
/// </summary>
|
||||
/// <param name="input">查询条件</param>
|
||||
/// <returns>文件流</returns>
|
||||
public async Task<FileStreamResult> ExportFileAsync(QueryPageOptions input)
|
||||
{
|
||||
using var db = GetDB();
|
||||
var query = db.GetQuery<SysOperateLog>(input).ExportIgnoreColumns();
|
||||
var sqlObj = query.ToSql();
|
||||
using IDataReader? dataReader = await db.Ado.GetDataReaderAsync(sqlObj.Key, sqlObj.Value);
|
||||
return await ExportFileAsync(dataReader);
|
||||
}
|
||||
|
||||
#endregion 导出
|
||||
}
|
||||
|
@@ -22,12 +22,12 @@ public class RelationService : BaseService<SysRelation>, IRelationService
|
||||
public async Task<List<SysRelation>> GetRelationByCategoryAsync(RelationCategoryEnum category)
|
||||
{
|
||||
var key = $"{CacheConst.Cache_SysRelation}{category}";
|
||||
var sysRelations = App.CacheService.Get<List<SysRelation>>(key);
|
||||
var sysRelations = NetCoreApp.CacheService.Get<List<SysRelation>>(key);
|
||||
if (sysRelations == null)
|
||||
{
|
||||
using var db = GetDB();
|
||||
sysRelations = await db.Queryable<SysRelation>().Where(it => it.Category == category).ToListAsync();
|
||||
App.CacheService.Set(key, sysRelations ?? new());//赋值空集合
|
||||
NetCoreApp.CacheService.Set(key, sysRelations ?? new());//赋值空集合
|
||||
}
|
||||
|
||||
return sysRelations;
|
||||
@@ -196,7 +196,7 @@ public class RelationService : BaseService<SysRelation>, IRelationService
|
||||
public void RefreshCache(RelationCategoryEnum category)
|
||||
{
|
||||
var key = $"{CacheConst.Cache_SysRelation}{category}";
|
||||
App.CacheService.Remove(key);
|
||||
NetCoreApp.CacheService.Remove(key);
|
||||
}
|
||||
|
||||
#endregion 缓存
|
||||
|
@@ -100,12 +100,12 @@ public class SysResourceService : BaseService<SysResource>, ISysResourceService
|
||||
/// <returns>全部资源列表</returns>
|
||||
public async Task<List<SysResource>> GetAllAsync()
|
||||
{
|
||||
var sysResources = App.CacheService.Get<List<SysResource>>(CacheKey);
|
||||
var sysResources = NetCoreApp.CacheService.Get<List<SysResource>>(CacheKey);
|
||||
if (sysResources == null)
|
||||
{
|
||||
using var db = GetDB();
|
||||
sysResources = await db.Queryable<SysResource>().ToListAsync();
|
||||
App.CacheService.Set(CacheKey, sysResources);
|
||||
NetCoreApp.CacheService.Set(CacheKey, sysResources);
|
||||
}
|
||||
return sysResources;
|
||||
}
|
||||
@@ -215,9 +215,9 @@ public class SysResourceService : BaseService<SysResource>, ISysResourceService
|
||||
/// </summary>
|
||||
public void RefreshCache()
|
||||
{
|
||||
App.CacheService.Remove(CacheKey);
|
||||
NetCoreApp.CacheService.Remove(CacheKey);
|
||||
//删除超级管理员的缓存
|
||||
App.RootServices.GetRequiredService<ISysUserService>().DeleteUserFromCache(RoleConst.SuperAdminId);
|
||||
NetCoreApp.RootServices.GetRequiredService<ISysUserService>().DeleteUserFromCache(RoleConst.SuperAdminId);
|
||||
}
|
||||
|
||||
#endregion 缓存
|
||||
|
@@ -36,12 +36,12 @@ public class SysRoleService : BaseService<SysRole>, ISysRoleService
|
||||
public async Task<List<SysRole>> GetAllAsync()
|
||||
{
|
||||
var key = CacheConst.Cache_SysRole;
|
||||
var sysRoles = App.CacheService.Get<List<SysRole>>(key);
|
||||
var sysRoles = NetCoreApp.CacheService.Get<List<SysRole>>(key);
|
||||
if (sysRoles == null)
|
||||
{
|
||||
using var db = GetDB();
|
||||
sysRoles = await db.Queryable<SysRole>().ToListAsync();
|
||||
App.CacheService.Set(key, sysRoles);
|
||||
NetCoreApp.CacheService.Set(key, sysRoles);
|
||||
}
|
||||
return sysRoles;
|
||||
}
|
||||
@@ -361,7 +361,7 @@ public class SysRoleService : BaseService<SysRole>, ISysRoleService
|
||||
/// </summary>
|
||||
public void RefreshCache()
|
||||
{
|
||||
App.CacheService.Remove(CacheConst.Cache_SysRole);//删除KEY
|
||||
NetCoreApp.CacheService.Remove(CacheConst.Cache_SysRole);//删除KEY
|
||||
}
|
||||
|
||||
#endregion 授权
|
||||
|
@@ -0,0 +1,34 @@
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
public interface IEventService<TEntry>
|
||||
{
|
||||
Task Publish(string key, TEntry payload);
|
||||
void Subscribe(string key, Func<TEntry, Task> callback);
|
||||
void UnSubscribe(string key);
|
||||
}
|
||||
public class EventService<TEntry> : IEventService<TEntry>
|
||||
{
|
||||
private Dictionary<string, Func<TEntry, Task>> Cache { get; } = new(50);
|
||||
|
||||
public async Task Publish(string key, TEntry payload)
|
||||
{
|
||||
|
||||
if (Cache.TryGetValue(key, out var func))
|
||||
{
|
||||
await func(payload);
|
||||
}
|
||||
}
|
||||
|
||||
public void Subscribe(string key, Func<TEntry, Task> callback)
|
||||
{
|
||||
Cache.TryAdd(key, callback);
|
||||
}
|
||||
|
||||
public void UnSubscribe(string key)
|
||||
{
|
||||
Cache.Remove(key);
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://kimdiego2098.github.io/
|
||||
// QQ群:605534569
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
public interface ISysHub
|
||||
{
|
||||
void UpdateVerificat(long clientId, long verificatId = 0, bool isConnect = true);
|
||||
}
|
@@ -8,17 +8,20 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
public class SignalrNoticeService : ISignalrNoticeService
|
||||
{
|
||||
private IHubContext<SysHub, ISysHub> HubContext;
|
||||
|
||||
public SignalrNoticeService(IHubContext<SysHub, ISysHub> hubContext)
|
||||
private IEventService<SignalRMessage>? SignalRMessageDispatchService { get; set; }
|
||||
private IEventService<string>? StringDispatchService { get; set; }
|
||||
public SignalrNoticeService(IEventService<SignalRMessage> signalRMessageDispatchService,
|
||||
IEventService<string> stringDispatchService
|
||||
)
|
||||
{
|
||||
HubContext = hubContext;
|
||||
SignalRMessageDispatchService = signalRMessageDispatchService;
|
||||
StringDispatchService = stringDispatchService;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -26,7 +29,12 @@ public class SignalrNoticeService : ISignalrNoticeService
|
||||
{
|
||||
//发送消息给用户
|
||||
if (clientIds != null)
|
||||
await HubContext.Clients.Users(clientIds.Select(a => a.ToString())).NewMessage(message);
|
||||
{
|
||||
foreach (var clientId in clientIds)
|
||||
{
|
||||
await SignalRMessageDispatchService.Publish(clientId.ToString(), message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -34,6 +42,11 @@ public class SignalrNoticeService : ISignalrNoticeService
|
||||
{
|
||||
//发送消息给用户
|
||||
if (clientIds != null)
|
||||
await HubContext.Clients.Users(clientIds.Select(a => a.ToString())).LoginOut(message);
|
||||
{
|
||||
foreach (var clientId in clientIds)
|
||||
{
|
||||
await StringDispatchService.Publish(clientId.ToString(), message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -8,14 +8,11 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.AspNetCore.Http.Connections.Features;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
|
||||
using ThingsGateway.Core.Extension;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
|
||||
public class SysHub : Hub<ISysHub>
|
||||
public class SysHub : ISysHub
|
||||
{
|
||||
private readonly IVerificatInfoService _verificatInfoService;
|
||||
|
||||
@@ -24,63 +21,36 @@ public class SysHub : Hub<ISysHub>
|
||||
_verificatInfoService = verificatInfoService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 连接
|
||||
/// </summary>
|
||||
public override async Task OnConnectedAsync()
|
||||
{
|
||||
var feature = Context.Features.Get<IHttpContextFeature>();
|
||||
var VerificatId = feature!.HttpContext!.Request.Headers[ClaimConst.VerificatId].FirstOrDefault().ToLong();
|
||||
if (VerificatId > 0)
|
||||
{
|
||||
var userIdentifier = Context.UserIdentifier;//自定义的Id
|
||||
UpdateVerificat(userIdentifier.ToLong(), verificat: VerificatId);//更新cache
|
||||
}
|
||||
await base.OnConnectedAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 断开连接
|
||||
/// </summary>
|
||||
public override async Task OnDisconnectedAsync(Exception? exception)
|
||||
{
|
||||
var userIdentifier = Context.UserIdentifier.ToLong();//自定义的Id
|
||||
var feature = Context.Features.Get<IHttpContextFeature>();
|
||||
var VerificatId = feature!.HttpContext!.Request.Headers[ClaimConst.VerificatId].FirstOrDefault().ToLong();
|
||||
UpdateVerificat(userIdentifier, verificat: VerificatId, isConnect: false);//更新cache
|
||||
await base.OnDisconnectedAsync(exception);
|
||||
}
|
||||
|
||||
#region 方法
|
||||
|
||||
/// <summary>
|
||||
/// 更新cache
|
||||
/// </summary>
|
||||
/// <param name="userId">用户id</param>
|
||||
/// <param name="verificat">上线时的验证id</param>
|
||||
/// <param name="clientId">用户id</param>
|
||||
/// <param name="verificatId">上线时的验证id</param>
|
||||
/// <param name="isConnect">上线</param>
|
||||
private void UpdateVerificat(long userId, long verificat = 0, bool isConnect = true)
|
||||
public void UpdateVerificat(long clientId, long verificatId = 0, bool isConnect = true)
|
||||
{
|
||||
if (userId != 0)
|
||||
if (clientId != 0)
|
||||
{
|
||||
//获取cache当前用户的verificat信息列表
|
||||
if (isConnect)
|
||||
{
|
||||
//获取cache中当前verificat
|
||||
var verificatInfo = _verificatInfoService.GetOne(verificat);
|
||||
var verificatInfo = _verificatInfoService.GetOne(verificatId);
|
||||
if (verificatInfo != null)
|
||||
{
|
||||
verificatInfo.ClientIds.Add(userId);//添加到客户端列表
|
||||
verificatInfo.ClientIds.Add(clientId);//添加到客户端列表
|
||||
_verificatInfoService.Update(verificatInfo);//更新Cache
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//获取当前客户端ID所在的verificat信息
|
||||
var verificatInfo = _verificatInfoService.GetOne(verificat);
|
||||
var verificatInfo = _verificatInfoService.GetOne(verificatId);
|
||||
if (verificatInfo != null)
|
||||
{
|
||||
verificatInfo.ClientIds.RemoveWhere(it => it == userId);//从客户端列表删除
|
||||
verificatInfo.ClientIds.RemoveWhere(it => it == clientId);//从客户端列表删除
|
||||
_verificatInfoService.Update(verificatInfo);//更新Cache
|
||||
}
|
||||
}
|
||||
@@ -88,4 +58,5 @@ public class SysHub : Hub<ISysHub>
|
||||
}
|
||||
|
||||
#endregion 方法
|
||||
|
||||
}
|
@@ -73,7 +73,7 @@ public class SysUserService : BaseService<SysUser>, ISysUserService
|
||||
public async Task<SysUser?> GetUserByIdAsync(long userId)
|
||||
{
|
||||
//先从Cache拿
|
||||
var sysUser = App.CacheService.HashGetOne<SysUser>(CacheConst.Cache_SysUser, userId.ToString());
|
||||
var sysUser = NetCoreApp.CacheService.HashGetOne<SysUser>(CacheConst.Cache_SysUser, userId.ToString());
|
||||
sysUser ??= await GetUserFromDb(userId);//从数据库拿用户信息
|
||||
return sysUser;
|
||||
}
|
||||
@@ -86,7 +86,7 @@ public class SysUserService : BaseService<SysUser>, ISysUserService
|
||||
public async Task<long> GetIdByAccountAsync(string account)
|
||||
{
|
||||
//先从Cache拿
|
||||
var userId = App.CacheService.HashGetOne<long>(CacheConst.Cache_SysUserAccount, account);
|
||||
var userId = NetCoreApp.CacheService.HashGetOne<long>(CacheConst.Cache_SysUserAccount, account);
|
||||
if (userId == 0)
|
||||
{
|
||||
//单查获取用户账号对应ID
|
||||
@@ -95,7 +95,7 @@ public class SysUserService : BaseService<SysUser>, ISysUserService
|
||||
if (userId != 0)
|
||||
{
|
||||
//插入Cache
|
||||
App.CacheService.HashAdd(CacheConst.Cache_SysUserAccount, account, userId);
|
||||
NetCoreApp.CacheService.HashAdd(CacheConst.Cache_SysUserAccount, account, userId);
|
||||
}
|
||||
}
|
||||
return userId;
|
||||
@@ -552,15 +552,15 @@ public class SysUserService : BaseService<SysUser>, ISysUserService
|
||||
public void DeleteUserFromCache(IEnumerable<long> ids)
|
||||
{
|
||||
var userIds = ids.Select(it => it.ToString()).ToArray();//id转string列表
|
||||
var sysUsers = App.CacheService.HashGet<SysUser>(CacheConst.Cache_SysUser, userIds);//获取用户列表
|
||||
var sysUsers = NetCoreApp.CacheService.HashGet<SysUser>(CacheConst.Cache_SysUser, userIds);//获取用户列表
|
||||
if (sysUsers.Any())
|
||||
{
|
||||
var accounts = sysUsers.Where(it => it != null).Select(it => it.Account);//账号集合
|
||||
var phones = sysUsers.Select(it => it.Phone);//手机号集合
|
||||
//删除用户信息
|
||||
App.CacheService.HashDel<SysUser>(CacheConst.Cache_SysUser, userIds);
|
||||
NetCoreApp.CacheService.HashDel<SysUser>(CacheConst.Cache_SysUser, userIds);
|
||||
//删除账号
|
||||
App.CacheService.HashDel<long>(CacheConst.Cache_SysUserAccount, accounts.ToArray());
|
||||
NetCoreApp.CacheService.HashDel<long>(CacheConst.Cache_SysUserAccount, accounts.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -677,8 +677,8 @@ public class SysUserService : BaseService<SysUser>, ISysUserService
|
||||
}
|
||||
|
||||
//插入Cache
|
||||
App.CacheService.HashAdd(CacheConst.Cache_SysUserAccount, sysUser.Account, sysUser.Id);
|
||||
App.CacheService.HashAdd(CacheConst.Cache_SysUser, sysUser.Id.ToString(), sysUser);
|
||||
NetCoreApp.CacheService.HashAdd(CacheConst.Cache_SysUserAccount, sysUser.Account, sysUser.Id);
|
||||
NetCoreApp.CacheService.HashAdd(CacheConst.Cache_SysUser, sysUser.Id.ToString(), sysUser);
|
||||
|
||||
return sysUser;
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@ using Microsoft.Extensions.Configuration;
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using ThingsGateway.Core;
|
||||
using ThingsGateway.Core.Extension;
|
||||
using ThingsGateway.Core.Json.Extension;
|
||||
|
||||
@@ -161,7 +162,7 @@ public class UserCenterService : BaseService<SysUser>, IUserCenterService
|
||||
[OperDesc("UpdatePassword", isRecordPar: false)]
|
||||
public async Task UpdatePasswordAsync(UpdatePasswordInput input)
|
||||
{
|
||||
var websiteOptions = App.Configuration?.GetSection(nameof(WebsiteOptions)).Get<WebsiteOptions>()!;
|
||||
var websiteOptions = NetCoreApp.Configuration?.GetSection(nameof(WebsiteOptions)).Get<WebsiteOptions>()!;
|
||||
if (websiteOptions.Demo)
|
||||
{
|
||||
throw Oops.Bah(Localizer["DemoCanotUpdatePassword"]);
|
||||
|
@@ -26,7 +26,7 @@ public class VerificatInfoService : BaseService<VerificatInfo>, IVerificatInfoSe
|
||||
public VerificatInfo GetOne(long id)
|
||||
{
|
||||
//先从Cache拿
|
||||
var verificatInfo = App.CacheService.HashGetOne<VerificatInfo>(CacheConst.Cache_Token, id.ToString());
|
||||
var verificatInfo = NetCoreApp.CacheService.HashGetOne<VerificatInfo>(CacheConst.Cache_Token, id.ToString());
|
||||
verificatInfo ??= GetFromDb(id);
|
||||
if (verificatInfo != null)
|
||||
if (verificatInfo.VerificatTimeout.AddSeconds(30) < DateTime.Now)
|
||||
@@ -48,7 +48,7 @@ public class VerificatInfoService : BaseService<VerificatInfo>, IVerificatInfoSe
|
||||
|
||||
private void SetCahce(VerificatInfo verificatInfo)
|
||||
{
|
||||
App.CacheService.HashAdd<VerificatInfo>(CacheConst.Cache_Token, verificatInfo.Id.ToString(), verificatInfo);
|
||||
NetCoreApp.CacheService.HashAdd<VerificatInfo>(CacheConst.Cache_Token, verificatInfo.Id.ToString(), verificatInfo);
|
||||
}
|
||||
|
||||
public List<VerificatInfo>? GetListByUserId(long userId)
|
||||
@@ -176,12 +176,12 @@ public class VerificatInfoService : BaseService<VerificatInfo>, IVerificatInfoSe
|
||||
|
||||
private void RemoveCache()
|
||||
{
|
||||
App.CacheService.Remove(CacheConst.Cache_Token);
|
||||
NetCoreApp.CacheService.Remove(CacheConst.Cache_Token);
|
||||
}
|
||||
|
||||
private void RemoveCache(long id)
|
||||
{
|
||||
App.CacheService.HashDel<VerificatInfo>(CacheConst.Cache_Token, id.ToString());
|
||||
NetCoreApp.CacheService.HashDel<VerificatInfo>(CacheConst.Cache_Token, id.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -11,12 +11,19 @@
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
|
||||
using Yitter.IdGenerator;
|
||||
|
||||
namespace ThingsGateway;
|
||||
|
||||
public class WebSugarAopService : ISugarAopService
|
||||
{
|
||||
private IAppService _appService;
|
||||
public WebSugarAopService(IAppService appService)
|
||||
{
|
||||
_appService = appService;
|
||||
}
|
||||
/// <summary>
|
||||
/// Aop设置
|
||||
/// </summary>
|
||||
@@ -83,7 +90,7 @@ public class WebSugarAopService : ISugarAopService
|
||||
if (entityInfo.PropertyName == nameof(BaseEntity.CreateTime))
|
||||
entityInfo.SetValue(DateTime.Now);
|
||||
|
||||
if (App.User != null)
|
||||
if (_appService.User != null)
|
||||
{
|
||||
//创建人
|
||||
if (entityInfo.PropertyName == nameof(BaseEntity.CreateUserId))
|
||||
@@ -99,7 +106,7 @@ public class WebSugarAopService : ISugarAopService
|
||||
if (entityInfo.PropertyName == nameof(BaseEntity.UpdateTime))
|
||||
entityInfo.SetValue(DateTime.Now);
|
||||
//更新人
|
||||
if (App.User != null)
|
||||
if (_appService.User != null)
|
||||
{
|
||||
if (entityInfo.PropertyName == nameof(BaseEntity.UpdateUserId))
|
||||
entityInfo.SetValue(UserManager.UserId);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user