Compare commits

...

96 Commits

Author SHA1 Message Date
Diego
2cafe745b9 build: 10.9.42
feat: 优化读写调度
feat: rpc日志添加执行时间字段
2025-07-18 09:48:41 +08:00
Diego
210ac2c122 更新解决方案 2025-07-18 00:02:38 +08:00
Diego
4707ce6d58 新增RemoteWebApp项目 2025-07-17 23:41:11 +08:00
2248356998 qq.com
788a8b670d 10.9.40 2025-07-16 21:18:14 +08:00
2248356998 qq.com
5e4f0057e4 更新依赖库 2025-07-15 21:24:19 +08:00
2248356998 qq.com
2960c13ef1 更新依赖库 2025-07-15 03:56:46 +08:00
2248356998 qq.com
f11b7f7ab4 10.9.37 2025-07-13 22:47:14 +08:00
Diego
9bb9cd7419 10.9.35 2025-07-11 07:14:52 +08:00
Diego
ba008ef8ba 更新.NETVersion 2025-07-09 13:35:42 +08:00
Diego
28b533decf 10.9.34 2025-07-09 13:31:08 +08:00
Diego
9ee638c2f1 更新依赖包 2025-07-08 23:59:24 +08:00
Diego
d90fdbaf35 补充更新nuget包 2025-07-08 18:15:50 +08:00
Diego
b55e3db736 10.9.29 2025-07-08 17:55:37 +08:00
Diego
dbee8496cb opcuaServer支持decimal类型 2025-07-08 16:53:28 +08:00
Diego
044e78bea9 opcuaserver回退修改 2025-07-08 15:52:12 +08:00
Diego
fe79128d90 build: 10.9.26
fix: opcuaserver添加变量属性出现错误,已回退
refactor: 网关监控树节点保持展开状态
refactor: cache插件类日志输出
2025-07-08 14:33:27 +08:00
Diego
34120da008 ToDecimal方法添加异步捕获,防止double类型值超限 2025-07-08 11:21:06 +08:00
Diego
4c62bb0b21 10.9.25 2025-07-08 10:19:01 +08:00
Diego
46b16279c7 build: 10.9.24 2025-07-08 09:41:45 +08:00
2248356998 qq.com
0779efc5dd refactor: 冗余服务代码修正 2025-07-08 02:38:05 +08:00
Diego
8acdb780e8 nuget本地源创建文件夹 2025-07-07 19:07:08 +08:00
Diego
2e310b919e 添加Keys文件夹 2025-07-07 16:54:58 +08:00
Diego
4155c07269 10.9.23 2025-07-07 15:20:39 +08:00
Diego
32fa833736 build: 10.9.22 2025-07-07 15:10:06 +08:00
Diego
2258f08555 build: 10.9.19 2025-07-07 13:44:19 +08:00
2248356998 qq.com
d90b32f165 refactor: 调整冗余服务代码 2025-07-07 00:15:46 +08:00
2248356998 qq.com
1492377322 refactor:调整部分插件父类 2025-07-06 01:33:15 +08:00
Diego
32eefbf545 build: 10.9.18
feat: 网关监控树节点添加右侧常用操作按钮
2025-07-04 13:54:35 +08:00
Diego
a825ca5f6f 网关监控树节点添加右侧常用操作按钮 2025-07-04 12:50:07 +08:00
Diego
e270b0c4f6 no message 2025-07-04 09:01:31 +08:00
Diego
6ae44ccf58 10.9.17 2025-07-04 08:36:37 +08:00
Diego
1aa0df6339 feat: 支持变量低内存导出 2025-07-03 23:24:56 +08:00
Diego
62c3693dbe 导出通道和设备支持IEnumerable低内存 2025-07-03 19:34:51 +08:00
Diego
e4e503c97b build: 添加nuget.config,自定义nuget源,方便本地调试自行编译 2025-07-03 12:43:51 +08:00
2248356998 qq.com
5ec1ee7627 fix: 上次更新导致大小端配置映射错误 2025-07-02 21:51:00 +08:00
Diego
79789388fc fix: opcuaserver动态刷新变量节点会导致新建的动态节点无法刷新订阅 2025-07-02 19:11:11 +08:00
Diego
2c4194ee18 refactor: opcua某些不存在的节点ID不再影响整体订阅,只出现日志提示 2025-07-02 15:09:52 +08:00
Diego
1b2be585af fix: 报警分析错误设置循环 2025-07-02 14:26:25 +08:00
Diego
83736647e7 feat: S7PLC增加WString支持 2025-07-02 12:49:55 +08:00
Diego
b06405717d build: 10.9.9
fix: timerx 池 max值取消
feat: mqttrpc支持脚本
2025-07-02 10:03:50 +08:00
2248356998 qq.com
298a1f2ed4 更新docker文件 2025-07-02 07:32:23 +08:00
Diego
74a47a1983 build: 10.9.8
支持redis缓存
2025-07-01 17:55:03 +08:00
Diego
a48a42abe4 modbusslave 异常捕获 2025-07-01 10:51:10 +08:00
Diego
feb1d0a3c5 feat: 增加后台服务生命周期识别 2025-06-30 10:59:18 +08:00
Diego
92bca824e6 10.9.6 2025-06-29 22:19:53 +08:00
2248356998 qq.com
025c699517 feat: s7增加请求id识别 2025-06-29 22:06:53 +08:00
2248356998 qq.com
53f8fbe4b1 refactor: 变量排序导出 2025-06-28 21:43:06 +08:00
2248356998 qq.com
77bfabc41d feat: 改用Mapperly源生成,代替Mapster 2025-06-28 00:00:43 +08:00
Diego
6427ee6ee0 refactor: 降低sqlite依赖 2025-06-27 14:44:00 +08:00
Diego
4c95997d62 build: 10.9.2
fix: taos connection dispose
refactor: opcua AddSubscriptionAsync 添加延时和重试
2025-06-27 11:16:58 +08:00
Diego
1d82cea40d build: 10.9.1
fix: 任务空错误异常
feat: 数据库插件增加字符串变量表和数值变量表两种情况
2025-06-27 03:02:03 +08:00
Diego
e78799424c 添加日志输出筛选 2025-06-25 17:09:05 +08:00
Diego
1000c8d38f docs: 更新采集插件开发文档 2025-06-25 14:22:58 +08:00
Diego
b2589fc634 build: 10.8.24
fix: 变量离线后再次上线,如果值不变,会导致在线状态不刷新
fix: s7离线恢复时,可能触发多次协议握手导致异常
2025-06-25 11:19:06 +08:00
Diego
c80e57a4e8 build: 10.8.24
fix: 变量离线后再次上线,如果值不变,会导致在线状态不刷新
fix: s7离线恢复时,可能触发多次协议握手导致异常
2025-06-25 11:17:04 +08:00
Diego
6510c3e289 feat: 增加一个变量读写表达式常用转换的友好编辑界面 2025-06-24 16:14:35 +08:00
Diego
920e407d05 恢复规则引擎脚本接口 2025-06-24 10:52:00 +08:00
Diego
7314c8901d fix: 用户编辑框初始刷新职位 2025-06-24 09:15:33 +08:00
Diego
4e9f02b48c 更新依赖包 2025-06-24 09:03:34 +08:00
2248356998 qq.com
9ae7602cb4 配置最大连接数 2025-06-24 00:09:07 +08:00
2248356998 qq.com
aa8aa36aef build: 10.8.19
fix: s7 复用地址对象导致读取异常
feat: 规则引擎node添加内部异常捕获
feat: 变量增加属性: 写入后再次读取检查值是否一致
2025-06-23 21:21:27 +08:00
2248356998 qq.com
0174f7c6f2 更新依赖 2025-06-22 23:05:12 +08:00
2248356998 qq.com
df9e7d6ff1 10.8.17 2025-06-22 21:11:37 +08:00
Diego
b40ca920d3 fix: 变量自动刷新运行态 2025-06-20 16:58:45 +08:00
Diego
5a4b0a0e93 修改插件过期提示 2025-06-20 14:43:13 +08:00
Diego
a879edd68b 10.8.12 2025-06-20 13:52:42 +08:00
Diego
62e0a6ee9d gitee登录按钮隐藏 2025-06-19 17:04:22 +08:00
Diego
765e5564d4 10.8.10 2025-06-19 16:56:04 +08:00
Diego
10eecac19b feat: 优化设备状态逻辑 2025-06-19 11:41:43 +08:00
Diego
59241b8faa fix: opcua插件订阅检查失效 2025-06-19 10:42:54 +08:00
Diego
52b3097f04 10.8.7 2025-06-18 22:04:48 +08:00
Diego
d922296b70 feat: 重构插件任务 2025-06-18 17:11:57 +08:00
Diego
aec91da28b 10.8.2
feat: 优化闭包导致的状态机内存占用,高并发时内存显著下降
fix(sqldb): 定时上传模式时,实时表时效
fix(taos): 初始化失败
2025-06-17 17:09:05 +08:00
Diego
013ff394be 10.8.1 2025-06-16 18:25:55 +08:00
Diego
081e07473d 10.8.0 2025-06-16 18:12:28 +08:00
Diego
d33d900592 增加github oauth登录 2025-06-15 00:17:25 +08:00
Diego
29365c4ef9 修改脚本测试方法 2025-06-14 11:56:31 +08:00
Diego
17a6189089 style: 更新SysResourcePage页面样式 2025-06-13 16:03:28 +08:00
Diego
003b8a3763 10.7.56 2025-06-13 13:35:38 +08:00
Diego
1c7f8b5cab 10.7.55 2025-06-13 09:14:22 +08:00
Diego
b7ff9ffca2 更新解决方案 2025-06-13 09:06:42 +08:00
Diego
9bba9bda76 10.7.54 2025-06-12 23:54:10 +08:00
Diego
ec2fcc75d3 合并代码 2025-06-12 20:21:50 +08:00
Diego
57a4038577 pwa安装提示优化 2025-06-12 10:20:52 +08:00
Diego
b78a76e60f 增加采集设备写入数据的控制台文本日志 2025-06-11 23:49:37 +08:00
Diego
28bd751d44 增加tab右键菜单 2025-06-11 22:57:00 +08:00
Diego
dcba7b9810 10.7.50 2025-06-11 21:50:46 +08:00
Diego
7f1a741ce6 优化sql实时表执行性能 2025-06-11 21:39:14 +08:00
Diego
ca2b17d433 10.7.47 2025-06-11 17:08:19 +08:00
Diego
af589eac10 更新依赖 2025-06-11 15:05:03 +08:00
Diego
573670f1f5 10.7.45 2025-06-11 10:26:48 +08:00
Diego
f3ec85a03d 更新测试 2025-06-10 16:26:14 +08:00
Diego
c94308454f build: 10.7.42
feat:更改后台服务多语言处理方式
2025-06-10 15:49:47 +08:00
Diego
c9c2b2b69d 10.7.39 2025-06-09 16:11:37 +08:00
Diego
40574b776f feat(TDengine): 支持websocket连接,无需安装sdk 2025-06-09 12:38:30 +08:00
Diego
f426c1533d fix: taos插件 not found dll错误 2025-06-09 10:07:58 +08:00
1886 changed files with 70683 additions and 20698 deletions

View File

@@ -85,7 +85,7 @@
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
as of the date such litigation is field.
4. Cachetribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without

View File

@@ -0,0 +1,6 @@
## Release 1.0
### New Rules
Rule ID | Category | Severity | Notes
--------|----------|----------|--------------------
TG0001 | Conflict | Error | SetParametersAsyncGenerator

View File

@@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\PackNuget.props" />
<PropertyGroup>
<TargetFrameworks>netstandard2.0;</TargetFrameworks>
<Version>$(SourceGeneratorVersion)</Version>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<NoPackageAnalysis>true</NoPackageAnalysis>
<SignAssembly>false</SignAssembly>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<IncludeBuildOutput>false</IncludeBuildOutput>
<!-- 避免 DLL 被打包到 lib/ -->
<EnableSourceGenerator>true</EnableSourceGenerator>
<!-- 可选 -->
</PropertyGroup>
<ItemGroup>
<None Include="$(OutputPath)\$(TargetFileName)" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="AnalyzerReleases.Shipped.md" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.13.0" PrivateAssets="all" Private="false" />
</ItemGroup>
</Project>

View File

@@ -7,8 +7,3 @@
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
global using Microsoft.Extensions.DependencyInjection;
global using ThingsGateway.Admin.Application;
global using ThingsGateway.Foundation;

View File

@@ -0,0 +1,439 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System.Text;
namespace Microsoft.AspNetCore.Components;
[Generator]
public sealed partial class SetParametersAsyncGenerator : IIncrementalGenerator
{
private const string m_DoNotGenerateSetParametersAsyncAttribute = """
using System;
namespace Microsoft.AspNetCore.Components
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
internal sealed class DoNotGenerateSetParametersAsyncAttribute : Attribute { }
}
""";
private const string m_GenerateSetParametersAsyncAttribute = """
using System;
namespace Microsoft.AspNetCore.Components
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
internal sealed class GenerateSetParametersAsyncAttribute : Attribute
{
public bool RequireExactMatch { get; set; }
}
}
""";
private const string m_GlobalGenerateSetParametersAsyncAttribute = """
using System;
namespace Microsoft.AspNetCore.Components
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
internal sealed class GlobalGenerateSetParametersAsyncAttribute : Attribute
{
public bool Enable { get; }
public GlobalGenerateSetParametersAsyncAttribute(bool enable = true) { Enable = enable; }
}
}
""";
private static readonly DiagnosticDescriptor ParameterNameConflict = new DiagnosticDescriptor(
id: "TG0001",
title: "Parameter name conflict",
messageFormat: "Parameter names are case insensitive. {0} conflicts with {1}.",
category: "Conflict",
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: "Parameter names must be case insensitive to be usable in routes. Rename the parameter to not be in conflict with other parameters.");
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// 注入 attribute 源码
context.RegisterPostInitializationOutput(ctx =>
{
ctx.AddSource("DoNotGenerateSetParametersAsyncAttribute.g.cs", SourceText.From(m_DoNotGenerateSetParametersAsyncAttribute, Encoding.UTF8));
ctx.AddSource("GenerateSetParametersAsyncAttribute.g.cs", SourceText.From(m_GenerateSetParametersAsyncAttribute, Encoding.UTF8));
ctx.AddSource("GlobalGenerateSetParametersAsyncAttribute.g.cs", SourceText.From(m_GlobalGenerateSetParametersAsyncAttribute, Encoding.UTF8));
});
// 筛选 ClassDeclarationSyntax
var classDeclarations = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: static (node, _) => node is ClassDeclarationSyntax,
transform: static (ctx, _) => (ClassDeclarationSyntax)ctx.Node)
.Where(static c => c is not null);
// 合并 Compilation
var compilationProvider = context.CompilationProvider;
var candidateClasses = classDeclarations.Combine(compilationProvider);
context.RegisterSourceOutput(candidateClasses, static (spc, tuple) =>
{
var (classDeclaration, compilation) = tuple;
Execute(spc, compilation, classDeclaration);
});
}
private static void Execute(SourceProductionContext context, Compilation compilation, ClassDeclarationSyntax classDeclaration)
{
var model = compilation.GetSemanticModel(classDeclaration.SyntaxTree);
var classSymbol = model.GetDeclaredSymbol(classDeclaration);
if (classSymbol is null || classSymbol.Name == "_Imports")
return;
var positiveAttr = compilation.GetTypeByMetadataName("Microsoft.AspNetCore.Components.GenerateSetParametersAsyncAttribute");
var negativeAttr = compilation.GetTypeByMetadataName("Microsoft.AspNetCore.Components.DoNotGenerateSetParametersAsyncAttribute");
if (classSymbol.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, negativeAttr)))
return;
if (!IsPartial(classSymbol) || !IsComponent(classDeclaration, classSymbol, compilation))
return;
var globalEnable = compilation.Assembly.GetAttributes()
.FirstOrDefault(a => a.AttributeClass?.ToDisplayString() == "Microsoft.AspNetCore.Components.GlobalGenerateSetParametersAsyncAttribute")
?.ConstructorArguments.FirstOrDefault().Value as bool? ?? false;
var hasPositive = classSymbol.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, positiveAttr));
if (!globalEnable && !hasPositive)
return;
GenerateSetParametersAsyncMethod(context, classSymbol);
}
private static void GenerateSetParametersAsyncMethod(SourceProductionContext context, INamedTypeSymbol class_symbol)
{
var force_exact_match = class_symbol.GetAttributes().Any(a => a.NamedArguments.Any(na => na.Key == "RequireExactMatch" && na.Value.Value is bool v && v));
var namespaceName = class_symbol.ContainingNamespace.ToDisplayString();
var type_kind = class_symbol.TypeKind switch { TypeKind.Class => "class", TypeKind.Interface => "interface", _ => "struct" };
var type_parameters = string.Join(", ", class_symbol.TypeArguments.Select(t => t.Name));
type_parameters = string.IsNullOrEmpty(type_parameters) ? type_parameters : "<" + type_parameters + ">";
context.AddCode(class_symbol.ToDisplayString() + "_override.cs", $@"
using Microsoft.AspNetCore.Components;
using System.Collections.Generic;
using System.Threading.Tasks;
#pragma warning disable CA2007
#pragma warning disable CS0162
#pragma warning disable CS8632
namespace {namespaceName}
{{
public partial class {class_symbol.Name}{type_parameters}
{{
private bool _initialized;
/// <summary>
/// <inheritdoc/>
/// </summary>
public override Task SetParametersAsync(ParameterView parameters)
{{
Dictionary<string,object?> parameterValues = new();
foreach (var parameter in parameters)
{{
if(BlazorImplementation__WriteSingleParameter(parameter.Name, parameter.Value)==false)
{{
// 如果没有处理参数,则添加到参数列表中
parameterValues.Add(parameter.Name, parameter.Value);
}}
}}
if(parameterValues.Count > 0)
{{
parameters.SetParameterProperties(this);
}}
if (!_initialized)
{{
_initialized = true;
return RunInitAndSetParametersAsync();
}}
else
{{
return CallOnParametersSetAsync();
}}
}}
// We do not want the debugger to consider NavigationExceptions caught by this method as user-unhandled.
#if NET9_0_OR_GREATER
[System.Diagnostics.DebuggerDisableUserUnhandledExceptions]
#endif
private async Task RunInitAndSetParametersAsync()
{{
Task task;
try
{{
OnInitialized();
task = OnInitializedAsync();
}}
catch (Exception ex) when (ex is not NavigationException)
{{
throw;
}}
if (task.Status != TaskStatus.RanToCompletion && task.Status != TaskStatus.Canceled)
{{
// Call state has changed here so that we render after the sync part of OnInitAsync has run
// and wait for it to finish before we continue. If no async work has been done yet, we want
// to defer calling StateHasChanged up until the first bit of async code happens or until
// the end. Additionally, we want to avoid calling StateHasChanged if no
// async work is to be performed.
StateHasChanged();
try
{{
await task;
}}
catch // avoiding exception filters for AOT runtime support
{{
// Ignore exceptions from task cancellations.
// Awaiting a canceled task may produce either an OperationCanceledException (if produced as a consequence of
// CancellationToken.ThrowIfCancellationRequested()) or a TaskCanceledException (produced as a consequence of awaiting Task.FromCanceled).
// It's much easier to check the state of the Task (i.e. Task.IsCanceled) rather than catch two distinct exceptions.
if (!task.IsCanceled)
{{
throw;
}}
}}
// Don't call StateHasChanged here. CallOnParametersSetAsync should handle that for us.
}}
await CallOnParametersSetAsync();
}}
// We do not want the debugger to consider NavigationExceptions caught by this method as user-unhandled.
#if NET9_0_OR_GREATER
[System.Diagnostics.DebuggerDisableUserUnhandledExceptions]
#endif
private Task CallOnParametersSetAsync()
{{
Task task;
try
{{
OnParametersSet();
task = OnParametersSetAsync();
}}
catch (Exception ex) when (ex is not NavigationException)
{{
#if NET9_0_OR_GREATER
System.Diagnostics.Debugger.BreakForUserUnhandledException(ex);
#endif
throw;
}}
// If no async work is to be performed, i.e. the task has already ran to completion
// or was canceled by the time we got to inspect it, avoid going async and re-invoking
// StateHasChanged at the culmination of the async work.
var shouldAwaitTask = task.Status != TaskStatus.RanToCompletion &&
task.Status != TaskStatus.Canceled;
// We always call StateHasChanged here as we want to trigger a rerender after OnParametersSet and
// the synchronous part of OnParametersSetAsync has run.
StateHasChanged();
return shouldAwaitTask ?
CallStateHasChangedOnAsyncCompletion(task) :
Task.CompletedTask;
}}
// We do not want the debugger to stop more than once per user-unhandled exception.
#if NET9_0_OR_GREATER
[System.Diagnostics.DebuggerDisableUserUnhandledExceptions]
#endif
private async Task CallStateHasChangedOnAsyncCompletion(Task task)
{{
try
{{
await task;
}}
catch // avoiding exception filters for AOT runtime support
{{
// Ignore exceptions from task cancellations, but don't bother issuing a state change.
if (task.IsCanceled)
{{
return;
}}
throw;
}}
StateHasChanged();
}}
}}
}}
#pragma warning restore CS8632
#pragma warning restore CS0162
#pragma warning restore CA2007
");
var bases = class_symbol.GetTypeHierarchy().Where(t => !SymbolEqualityComparer.Default.Equals(t, class_symbol));
var members = class_symbol.GetMembers() // members of the type itself
.Concat(bases.SelectMany(t => t.GetMembers().Where(m => m.DeclaredAccessibility != Accessibility.Private))) // plus accessible members of any base
.Distinct(SymbolEqualityComparer.Default);
var property_symbols = members.OfType<IPropertySymbol>();
var writable_property_symbols = property_symbols.Where(ps =>
!ps.IsReadOnly || ps.GetAttributes().Any(a =>
a.AttributeClass?.Name is "CascadingParameter" or "CascadingParameterAttribute")
);
var parameter_symbols = writable_property_symbols
.Where(ps => ps.GetAttributes().Any(a => false
|| a.AttributeClass.Name == "Parameter"
|| a.AttributeClass.Name == "ParameterAttribute"
|| a.AttributeClass.Name == "CascadingParameter"
|| a.AttributeClass.Name == "CascadingParameterAttribute"
));
var name_conflicts = parameter_symbols.GroupBy(ps => ps.Name.ToLowerInvariant()).Where(g => g.Count() > 1);
foreach (var conflict in name_conflicts)
{
var key = conflict.Key;
var conflicting_parameters = conflict.ToList();
foreach (var parameter in conflicting_parameters)
{
var this_name = parameter.Name;
var conflicting_name = conflicting_parameters.Select(p => p.Name).FirstOrDefault(n => n != this_name);
foreach (var location in parameter.Locations)
{
context.ReportDiagnostic(Diagnostic.Create(ParameterNameConflict, location, this_name, conflicting_name));
}
}
}
var all = parameter_symbols.ToList();
var catch_all_parameter = parameter_symbols.FirstOrDefault(p =>
{
var parameter_attr = p.GetAttributes().FirstOrDefault(a => a.AttributeClass!.Name.StartsWith("Parameter"));
return parameter_attr?.NamedArguments.Any(n => n.Key == "CaptureUnmatchedValues" && n.Value.Value is bool v && v) == true;
});
var lower_case_match_cases = parameter_symbols.Except(new[] { catch_all_parameter }).Select(p => $"case \"{p.Name.ToLowerInvariant()}\": this.{p.Name} = ({p.Type.ToDisplayString()}) value; break;");
var lower_case_match_default = catch_all_parameter == null ? @"default: {return false;}" : $@"
default:
{{
this.{catch_all_parameter.Name} ??= new System.Collections.Generic.Dictionary<string, object>();
var writable_dict = this.{catch_all_parameter.Name};
if (!writable_dict.TryAdd(name, value))
{{
writable_dict[name] = value;
}}
break;
}}";
var exact_match_cases = parameter_symbols.Except(new[] { catch_all_parameter }).Select(p => $"case \"{p!.Name}\": this.{p.Name} = ({p.Type.ToDisplayString()}) value; break;");
string exact_match_default;
if (force_exact_match)
{
if (catch_all_parameter == null) // exact matches are forced, and we do not have a catch-all parameter, therefore we need to throw on unmatched parameter
{
exact_match_default = @"default: { return false;";
}
else // exact matches are forced, and we DO have a catch-all parameter, therefore we simply add that unmatched parameter to the dictionary
{
exact_match_default = $@"
default:
{{
this.{catch_all_parameter.Name} ??= new System.Collections.Generic.Dictionary<string, object>();
var writable_dict = this.{catch_all_parameter.Name};
if (!writable_dict.TryAdd(name, value))
{{
writable_dict[name] = value;
}}
break;
}}";
}
}
else
{
// exact matches are not forced, so if there is no exact match, we fall back to compare it in lower case
exact_match_default = $@"
default:
{{
switch (name.ToLowerInvariant())
{{
{string.Join("\n", lower_case_match_cases)}
{lower_case_match_default}
}}
break;
}}
";
}
context.AddCode(class_symbol.ToDisplayString() + "_implementation.cs", $@"
using System;
#pragma warning disable CS0162
#pragma warning disable CS0618
#pragma warning disable CS8632
namespace {namespaceName}
{{
public partial class {class_symbol.Name}{type_parameters}
{{
private bool BlazorImplementation__WriteSingleParameter(string name, object value)
{{
if(name != ""Body"")
{{
switch (name)
{{
{string.Join("\n", exact_match_cases)}
{exact_match_default}
}}
return true;
}}
return false;
}}
}}
}}
#pragma warning restore CS8632
#pragma warning restore CS0618
#pragma warning restore CS0162");
}
private static bool IsPartial(INamedTypeSymbol symbol)
{
return symbol.DeclaringSyntaxReferences
.Select(r => r.GetSyntax())
.OfType<ClassDeclarationSyntax>()
.Any(c => c.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword)));
}
private static bool IsComponent(ClassDeclarationSyntax classDeclaration, INamedTypeSymbol symbol, Compilation compilation)
{
if (HasUserDefinedSetParametersAsync(symbol))
return false;
if (classDeclaration.SyntaxTree.FilePath.EndsWith(".razor") || classDeclaration.SyntaxTree.FilePath.EndsWith(".razor.cs"))
return true;
var iComponent = compilation.GetTypeByMetadataName("Microsoft.AspNetCore.Components.IComponent");
var componentBase = compilation.GetTypeByMetadataName("Microsoft.AspNetCore.Components.ComponentBase");
if (iComponent == null || componentBase == null)
return false;
return symbol.AllInterfaces.Contains(iComponent) || SymbolEqualityComparer.Default.Equals(symbol.BaseType, componentBase);
}
private static bool HasUserDefinedSetParametersAsync(INamedTypeSymbol classSymbol)
{
return classSymbol
.GetMembers("SetParametersAsync")
.OfType<IMethodSymbol>()
.Any(m =>
m.Parameters.Length == 1 &&
m.Parameters[0].Type.ToDisplayString() == "Microsoft.AspNetCore.Components.ParameterView" &&
m.DeclaredAccessibility == Accessibility.Public &&
!m.IsStatic);
}
}

View File

@@ -0,0 +1,15 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using System.Text;
namespace Microsoft.AspNetCore.Components
{
internal static class SourceGeneratorContextExtension
{
public static void AddCode(this SourceProductionContext context, string hint_name, string code)
{
context.AddSource(hint_name.Replace("<", "_").Replace(">", "_"), SourceText.From(code, Encoding.UTF8));
}
}
}

View File

@@ -0,0 +1,19 @@
using Microsoft.CodeAnalysis;
namespace Microsoft.AspNetCore.Components
{
public static class TypeSymbolExtension
{
public static IEnumerable<INamedTypeSymbol> GetTypeHierarchy(this INamedTypeSymbol symbol)
{
yield return symbol;
if (symbol.BaseType != null)
{
foreach (var type in GetTypeHierarchy(symbol.BaseType))
{
yield return type;
}
}
}
}
}

View File

@@ -0,0 +1,49 @@
param($installPath, $toolsPath, $package, $project)
$analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers" ) * -Resolve
foreach($analyzersPath in $analyzersPaths)
{
# Install the language agnostic analyzers.
if (Test-Path $analyzersPath)
{
foreach ($analyzerFilePath in Get-ChildItem $analyzersPath -Filter *.dll)
{
if($project.Object.AnalyzerReferences)
{
$project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName)
}
}
}
}
# $project.Type gives the language name like (C# or VB.NET)
$languageFolder = ""
if($project.Type -eq "C#")
{
$languageFolder = "cs"
}
if($project.Type -eq "VB.NET")
{
$languageFolder = "vb"
}
if($languageFolder -eq "")
{
return
}
foreach($analyzersPath in $analyzersPaths)
{
# Install language specific analyzers.
$languageAnalyzersPath = join-path $analyzersPath $languageFolder
if (Test-Path $languageAnalyzersPath)
{
foreach ($analyzerFilePath in Get-ChildItem $languageAnalyzersPath -Filter *.dll)
{
if($project.Object.AnalyzerReferences)
{
$project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName)
}
}
}
}

View File

@@ -0,0 +1,56 @@
param($installPath, $toolsPath, $package, $project)
$analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers" ) * -Resolve
foreach($analyzersPath in $analyzersPaths)
{
# Uninstall the language agnostic analyzers.
if (Test-Path $analyzersPath)
{
foreach ($analyzerFilePath in Get-ChildItem $analyzersPath -Filter *.dll)
{
if($project.Object.AnalyzerReferences)
{
$project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName)
}
}
}
}
# $project.Type gives the language name like (C# or VB.NET)
$languageFolder = ""
if($project.Type -eq "C#")
{
$languageFolder = "cs"
}
if($project.Type -eq "VB.NET")
{
$languageFolder = "vb"
}
if($languageFolder -eq "")
{
return
}
foreach($analyzersPath in $analyzersPaths)
{
# Uninstall language specific analyzers.
$languageAnalyzersPath = join-path $analyzersPath $languageFolder
if (Test-Path $languageAnalyzersPath)
{
foreach ($analyzerFilePath in Get-ChildItem $languageAnalyzersPath -Filter *.dll)
{
if($project.Object.AnalyzerReferences)
{
try
{
$project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName)
}
catch
{
}
}
}
}
}

View File

@@ -90,13 +90,12 @@ public sealed class OperDescAttribute : MoAttribute
OperDescAttribute.WriteToQueue(log);
}
}
private static SqlSugarClient _db = DbContext.GetDB<SysOperateLog>();
/// <summary>
/// 将日志消息写入数据库中
/// </summary>
private static async Task ProcessQueue()
{
var db = DbContext.Db.GetConnectionScopeWithAttr<SysOperateLog>().CopyNew();
var appLifetime = App.RootServices!.GetService<IHostApplicationLifetime>()!;
while (!appLifetime.ApplicationStopping.IsCancellationRequested)
{
@@ -105,7 +104,7 @@ public sealed class OperDescAttribute : MoAttribute
var data = _logMessageQueue.ToListWithDequeue(); // 从日志队列中获取数据
if (data.Count > 0)
{
await db.InsertableWithAttr(data).ExecuteCommandAsync(appLifetime.ApplicationStopping).ConfigureAwait(false);//入库
await _db.InsertableWithAttr(data).ExecuteCommandAsync(appLifetime.ApplicationStopping).ConfigureAwait(false);//入库
}
}
catch (Exception ex)

View File

@@ -33,22 +33,22 @@ public static class CacheConst
/// <summary>
/// 资源表缓存Key
/// </summary>
public const string Cache_SysResource = $"{CacheConst.Cache_Prefix_Admin}SysResource:";
public const string Cache_SysResource = $"{CacheConst.Cache_Prefix_Admin}SysResource:List";
/// <summary>
/// 角色表缓存Key
/// </summary>
public const string Cache_SysRole = $"{CacheConst.Cache_Prefix_Admin}SysRole:";
public const string Cache_SysRole = $"{CacheConst.Cache_Prefix_Admin}SysRole:List";
/// <summary>
/// 用户表缓存Key
/// </summary>
public const string Cache_SysUser = $"{CacheConst.Cache_Prefix_Admin}SysUser:";
public const string Cache_SysUser = $"{CacheConst.Cache_Prefix_Admin}SysUser:Hash";
/// <summary>
/// 用户账号关系缓存Key
/// </summary>
public const string Cache_SysUserAccount = $"{CacheConst.Cache_Prefix_Admin}SysUserAccount:";
public const string Cache_SysUserAccount = $"{CacheConst.Cache_Prefix_Admin}SysUserAccount:Hash";
/// <summary>
/// 职位表缓存Key
@@ -58,7 +58,7 @@ public static class CacheConst
/// <summary>
/// 机构表缓存Key
/// </summary>
public const string Cache_SysOrg = $"{CacheConst.Cache_Prefix_Admin}SysOrg:";
public const string Cache_SysOrg = $"{CacheConst.Cache_Prefix_Admin}SysOrg:List";
/// <summary>
/// 公司表缓存Key
@@ -67,12 +67,12 @@ public static class CacheConst
/// <summary>
/// 公司表缓存Key
/// </summary>
public const string Cache_SysOrgTenant = $"{CacheConst.Cache_Prefix_Admin}OrgTenant:";
public const string Cache_SysOrgTenant = $"{CacheConst.Cache_Prefix_Admin}OrgTenant:Hash";
/// <summary>
/// Token表缓存Key
/// </summary>
public const string Cache_Token = $"{CacheConst.Cache_Prefix_Admin}Token:";
public const string Cache_Token = $"{CacheConst.Cache_Prefix_Admin}Token:Hash";
#region

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using Mapster;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@@ -41,9 +39,9 @@ public class OpenApiController : ControllerBase
[AllowAnonymous]
public async Task<OpenApiLoginOutput> LoginAsync([FromBody] OpenApiLoginInput input)
{
var output = await _authService.LoginAsync(input.Adapt<LoginInput>(), false).ConfigureAwait(false);
var output = await _authService.LoginAsync(input.AdaptLoginInput(), false).ConfigureAwait(false);
var openApiLoginOutput = output.Adapt<OpenApiLoginOutput>();
var openApiLoginOutput = output.AdaptOpenApiLoginOutput();
return openApiLoginOutput;
}

View File

@@ -8,10 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using SqlSugar;
using System.ComponentModel.DataAnnotations;
namespace ThingsGateway.Admin.Application;

View File

@@ -8,10 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using SqlSugar;
namespace ThingsGateway.Admin.Application;
/// <summary>

View File

@@ -8,10 +8,6 @@
// QQ群605534569
// ------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using SqlSugar;
using System.ComponentModel.DataAnnotations;
namespace ThingsGateway.Admin.Application;

View File

@@ -8,12 +8,10 @@
// QQ群605534569
// ------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using SqlSugar;
using System.ComponentModel.DataAnnotations;
namespace ThingsGateway.Admin.Application;
/// <summary>

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using SqlSugar;
namespace ThingsGateway.Admin.Application;
/// <summary>

View File

@@ -8,14 +8,10 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Components.Routing;
using Newtonsoft.Json;
using SqlSugar;
using System.ComponentModel.DataAnnotations;
namespace ThingsGateway.Admin.Application;

View File

@@ -8,10 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using SqlSugar;
using System.ComponentModel.DataAnnotations;
namespace ThingsGateway.Admin.Application;

View File

@@ -8,14 +8,9 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using Mapster;
using SqlSugar;
using Riok.Mapperly.Abstractions;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
namespace ThingsGateway.Admin.Application;
@@ -31,7 +26,7 @@ public class SysUser : BaseEntity
///</summary>
[SugarColumn(ColumnDescription = "头像", ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = true)]
[AutoGenerateColumn(Visible = true, Sortable = false, Filterable = false)]
[AdaptIgnore]
[MapperIgnore]
public virtual string? Avatar { get; set; }
/// <summary>

View File

@@ -8,11 +8,7 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using SqlSugar;
using ThingsGateway.List;
using ThingsGateway.Common.List;
namespace ThingsGateway.Admin.Application;

View File

@@ -8,31 +8,30 @@
// QQ群605534569
//------------------------------------------------------------------------------
using Microsoft.Extensions.Localization;
using ThingsGateway.Extension;
namespace ThingsGateway.Foundation;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 语言资源
/// </summary>
public class DefaultResource
/// <inheritdoc/>
[ThingsGateway.DependencyInjection.SuppressSniffer]
public static class SchemeHelper
{
private static IStringLocalizer localizer;
/// <summary>
/// Localizer
/// </summary>
public static IStringLocalizer Localizer
public static string GetOrCreate()
{
get
var path = "Keys/SchemeKey.txt";
if (File.Exists(path))
{
if (localizer == null)
{
localizer = LocalizerUtil.GetLocalizer.Invoke(typeof(DefaultResource));
}
return localizer;
var data = File.ReadAllText(path);
return data;
}
else
{
var data = DateTime.UtcNow.ToDefaultDateTimeFormat();
Directory.CreateDirectory("Keys");
File.WriteAllText(path, data);
return data;
}
}
//使用频率高的多语言应初始化构建
}

View File

@@ -79,7 +79,7 @@ public class RequestAuditFilter : IAsyncActionFilter, IOrderedFilter
logData.TimeOperationElapsedMilliseconds = timeOperation.ElapsedMilliseconds;
var resultHttpContext = (resultContext as FilterContext).HttpContext;
var resultHttpContext = (resultContext as Microsoft.AspNetCore.Mvc.Filters.FilterContext).HttpContext;
// 获取 HttpContext 和 HttpRequest 对象
var httpContext = context.HttpContext;
@@ -267,7 +267,7 @@ public class RequestAuditFilter : IAsyncActionFilter, IOrderedFilter
}
else
{
logger.Log(LogLevel.Warning, $"{logData.Method}:{logData.Path}-{logData.Operation}{Environment.NewLine}{logData.Exception.ToSystemTextJsonString()}");
logger.Log(LogLevel.Warning, $"{logData.Method}:{logData.Path}-{logData.Operation}{Environment.NewLine}{logData.Exception?.ToSystemTextJsonString()}{Environment.NewLine}{logData.Validation?.ToSystemTextJsonString()}");
}
}

View File

@@ -1,40 +0,0 @@
using Microsoft.AspNetCore.Authentication.OAuth;
using System.Text.Json;
namespace ThingsGateway.Admin.Application;
/// <summary>OAuthOptions 配置类</summary>
public abstract class AdminOAuthOptions : OAuthOptions
{
/// <summary>默认构造函数</summary>
protected AdminOAuthOptions()
{
ConfigureClaims();
this.Events.OnRemoteFailure = context =>
{
var redirectUri = string.IsNullOrEmpty(HomePath) ? "/" : HomePath;
context.Response.Redirect(redirectUri);
context.HandleResponse();
return Task.CompletedTask;
};
}
/// <summary>配置 Claims 映射</summary>
protected virtual void ConfigureClaims()
{
}
public virtual string GetName(JsonElement element)
{
JsonElement.ObjectEnumerator target = element.EnumerateObject();
return target.TryGetValue("name");
}
/// <summary>获得/设置 登陆后首页</summary>
public string HomePath { get; set; } = "/";
}

View File

@@ -1,12 +0,0 @@
namespace ThingsGateway.Admin.Application;
public class GiteeOAuthUser
{
public string Id { get; set; }
public string Login { get; set; }
public string Name { get; set; }
public string Avatar_Url { get; set; }
}

View File

@@ -1,22 +0,0 @@
using System.Text.Json;
namespace ThingsGateway.Admin.Application;
public static class OAuthUserExtensions
{
public static GiteeOAuthUser ToAuthUser(this JsonElement element)
{
GiteeOAuthUser authUser = new GiteeOAuthUser();
JsonElement.ObjectEnumerator target = element.EnumerateObject();
authUser.Id = target.TryGetValue("id");
authUser.Login = target.TryGetValue("login");
authUser.Name = target.TryGetValue("name");
authUser.Avatar_Url = target.TryGetValue("avatar_url");
return authUser;
}
public static string TryGetValue(this JsonElement.ObjectEnumerator target, string propertyName)
{
return target.FirstOrDefault<JsonProperty>((Func<JsonProperty, bool>)(t => t.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase))).Value.ToString() ?? string.Empty;
}
}

View File

@@ -8,5 +8,15 @@
// QQ群605534569
//------------------------------------------------------------------------------
global using ThingsGateway;
global using ThingsGateway.NewLife.Extension;
global using BootstrapBlazor.Components;
global using Microsoft.Extensions.Localization;
global using Microsoft.Extensions.Options;
global using System.Diagnostics.CodeAnalysis;
global using System.Globalization;
global using ThingsGateway.Common;
global using ThingsGateway.DB;
global using ThingsGateway.NewLife.Extension;
global using ThingsGateway.SqlSugar;

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using ThingsGateway.NewLife;
namespace ThingsGateway.Admin.Application;

View File

@@ -9,9 +9,7 @@
// ------------------------------------------------------------------------------
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Runtime.InteropServices;
@@ -52,16 +50,16 @@ public class HardwareJob : IJob, IHardwareJob
#endregion
private MemoryCache MemoryCache = new() { };
private const string CacheKey = "HistoryHardwareInfo";
private ICache MemoryCache => App.CacheService;
private const string CacheKey = $"{CacheConst.Cache_HardwareInfo}HistoryHardwareInfo";
/// <inheritdoc/>
public async Task<List<HistoryHardwareInfo>> GetHistoryHardwareInfos()
{
var historyHardwareInfos = MemoryCache.Get<List<HistoryHardwareInfo>>(CacheKey);
if (historyHardwareInfos == null)
{
using var db = DbContext.Db.GetConnectionScopeWithAttr<HistoryHardwareInfo>().CopyNew();
historyHardwareInfos = await db.Queryable<HistoryHardwareInfo>().Where(a => a.Date > DateTime.Now.AddDays(-3)).ToListAsync().ConfigureAwait(false);
using var db = DbContext.GetDB<HistoryHardwareInfo>(); ;
historyHardwareInfos = await db.Queryable<HistoryHardwareInfo>().Where(a => a.Date > DateTime.Now.AddDays(-3)).Take(1000).ToListAsync().ConfigureAwait(false);
MemoryCache.Set(CacheKey, historyHardwareInfos);
}
@@ -70,6 +68,7 @@ public class HardwareJob : IJob, IHardwareJob
private bool error = false;
private DateTime hisInsertTime = default;
private SqlSugarClient _db = DbContext.GetDB<HistoryHardwareInfo>();
public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
{
@@ -79,8 +78,7 @@ public class HardwareJob : IJob, IHardwareJob
{
if (HardwareInfo.MachineInfo == null)
{
await MachineInfo.RegisterAsync().ConfigureAwait(false);
HardwareInfo.MachineInfo = MachineInfo.Current;
HardwareInfo.MachineInfo = MachineInfo.GetCurrent();
string currentPath = Directory.GetCurrentDirectory();
DriveInfo drive = new(Path.GetPathRoot(currentPath));
@@ -110,7 +108,7 @@ public class HardwareJob : IJob, IHardwareJob
catch (Exception ex)
{
if (!error)
_logger.LogWarning(ex, _localizer["GetHardwareInfoFail"]);
_logger.LogWarning(ex, "Get Hardwareinfo Fail");
error = true;
}
@@ -121,7 +119,6 @@ public class HardwareJob : IJob, IHardwareJob
if (DateTime.Now > hisInsertTime.Add(TimeSpan.FromMilliseconds(HardwareInfoOptions.HistoryInterval)))
{
hisInsertTime = DateTime.Now;
using var db = DbContext.Db.GetConnectionScopeWithAttr<HistoryHardwareInfo>().CopyNew();
{
var his = new HistoryHardwareInfo()
{
@@ -132,12 +129,12 @@ public class HardwareJob : IJob, IHardwareJob
CpuUsage = (HardwareInfo.MachineInfo.CpuRate * 100).ToInt(),
Temperature = (HardwareInfo.MachineInfo.Temperature).ToInt(),
};
await db.Insertable(his).ExecuteCommandAsync(stoppingToken).ConfigureAwait(false);
await _db.Insertable(his).ExecuteCommandAsync(stoppingToken).ConfigureAwait(false);
MemoryCache.Remove(CacheKey);
}
var sevenDaysAgo = TimerX.Now.AddDays(-HardwareInfoOptions.DaysAgo);
//删除特定信息
var result = await db.Deleteable<HistoryHardwareInfo>(a => a.Date <= sevenDaysAgo).ExecuteCommandAsync(stoppingToken).ConfigureAwait(false);
var result = await _db.Deleteable<HistoryHardwareInfo>(a => a.Date <= sevenDaysAgo).ExecuteCommandAsync(stoppingToken).ConfigureAwait(false);
if (result > 0)
{
MemoryCache.Remove(CacheKey);
@@ -152,7 +149,7 @@ public class HardwareJob : IJob, IHardwareJob
catch (Exception ex)
{
if (!error)
_logger.LogWarning(ex, _localizer["GetHardwareInfoFail"]);
_logger.LogWarning(ex, "Get Hardwareinfo Fail");
error = true;
}
}

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using SqlSugar;
namespace ThingsGateway.Admin.Application;
/// <inheritdoc/>

View File

@@ -27,7 +27,7 @@ public class LogJob : IJob
private static async Task DeleteSysOperateLog(int daysAgo, CancellationToken stoppingToken)
{
using var db = DbContext.Db.GetConnectionScopeWithAttr<SysOperateLog>().CopyNew();
using var db = DbContext.GetDB<SysOperateLog>();
var time = DateTime.Now.AddDays(-daysAgo);
await db.DeleteableWithAttr<SysOperateLog>().Where(u => u.OpTime < time).ExecuteCommandAsync(stoppingToken).ConfigureAwait(false); // 删除操作日志
}

View File

@@ -1,459 +1,402 @@
{
"ThingsGateway.Admin.Application.AppConfig": {
"LoginPolicy": "LoginPolicy",
"PagePolicy": "PagePolicy",
"PasswordPolicy": "PasswordPolicy",
"WebsitePolicy": "WebsitePolicy"
},
"ThingsGateway.Admin.Application.AuthController": {
"AuthController": "Login API",
"LoginAsync": "Login",
"LogoutAsync": "Logout"
},
"ThingsGateway.Admin.Application.AuthService": {
"AuthErrorMax": "Account password error, will be locked for {1} minutes after exceeding {0} times, error count {2}",
"MustDesc": "Password needs to be encrypted with DESC before passing",
"OrgDisable": "The affiliated company/department has been deactivated, please contact the administrator",
"PasswordError": "Too many password errors, please try again in {0} minutes",
"SingleLoginWarn": "Your account is logged in elsewhere",
"TenantNull": "The tenant does not exist",
"UserDisable": "Account {0} has been disabled",
"UserNoModule": "This account has not been assigned a module. Please contact the administrator",
"UserNull": "User {0} does not exist"
},
"ThingsGateway.Admin.Application.BaseDataEntity": {
"CreateOrgId": "CreateOrgId"
},
"ThingsGateway.Admin.Application.BaseEntity": {
"SortCode": "SortCode",
"CreateTime": "CreateTime",
"CreateUser": "CreateUser",
"SortCode": "SortCode",
"UpdateTime": "UpdateTime",
"UpdateUser": "UpdateUser"
},
"ThingsGateway.Admin.Application.BlazorAuthenticationHandler": {
"UserExpire": "User expired, please login again"
},
"ThingsGateway.Admin.Application.SysUser": {
"Disable": "Disable",
"Enable": "Enable",
"GrantRole": "GrantRole",
"ExitVerificat": "You have been forcibly logged out",
"PasswordEdited": "Password changed, logged out",
"Avatar": "Avatar",
"Account": "Account",
"Account.Required": "Account.Required",
"Password": "Password",
"Status": "Status",
"Phone": "Phone",
"Email": "Email",
"LastLoginIp": "LastLoginIp",
"LastLoginDevice": "LastLoginDevice",
"LastLoginTime": "LastLoginTime",
"LastLoginAddress": "LastLoginAddress",
"LatestLoginIp": "LatestLoginIp",
"LatestLoginTime": "LatestLoginTime",
"LatestLoginDevice": "LatestLoginDevice",
"LatestLoginAddress": "LatestLoginAddress",
"OrgNames": "OrgNames",
"PositionName": "PositionName",
"OrgId": "Org",
"PositionId": "Position",
"DirectorId": "Director",
"CheckSelf": "Prohibit {0} yourself",
"CanotDeleteAdminUser": "Cannot delete built-in super admin user",
"CanotEditAdminUser": "Cannot edit super admin user",
"CanotGrantAdmin": "Cannot assign admins roles",
"EmailDup": "Duplicate email {0} exists",
"AccountDup": "Duplicate account {0} exists",
"CanotDeleteSelf": "Cannot delete yourself",
"EmailError": "Email format error {0}",
"PhoneError": "Phone number format error {0}",
"NoOrg": "The organization does not exist",
"DirectorSelf": "Cannot set oneself as the supervisor",
"DemoCanotUpdatePassword": "DEMO environment does not allow password modification",
"OldPasswordError": "Incorrect old password",
"ConfirmPasswordDiff": "Passwords entered twice are inconsistent",
"PasswordLengthLess": "Password length cannot be less than {0}",
"PasswordMustNum ": "Password must contain numbers",
"PasswordMustLow": "Password must contain lowercase letters",
"PasswordMustUpp": "Password must contain uppercase letters",
"PasswordMustSpecial": "Password must contain special characters"
},
"ThingsGateway.Admin.Application.SysRole": {
"Code": "Code",
"Name": "Name",
"Name.Required": "{0} is required",
"Category": "Category",
"OrgId": "Org",
"Global": "Global",
"Status": "Status",
"CanotDeleteAdmin": "Cannot delete built-in super admin role",
"CanotEditAdmin": "Cannot edit super admin role",
"CanotGrantAdmin": "Cannot assign admins roles",
"NameDup": "Duplicate role name {0}",
"OrgNotNull": "Organization cannot be null",
"SameOrgNameDup": "Duplicate role name exists: {0}",
"CannotRoleScopeAll": "Organization role cannot select global data scope",
"CodeDup": "Duplicate code exists: {0}"
},
"ThingsGateway.Admin.Application.RoleCategoryEnum": {
"Global": "Global",
"Org": "Org"
},
"ThingsGateway.Admin.Application.DataScopeEnum": {
"SCOPE_SELF": "Self",
"SCOPE_ALL": "All",
"SCOPE_ORG": "OnlyOrg",
"SCOPE_ORG_CHILD": "OrgChild",
"SCOPE_ORG_DEFINE": "Define"
"SCOPE_ORG_DEFINE": "Define",
"SCOPE_SELF": "Self"
},
"ThingsGateway.Admin.Application.DefaultDataScope": {
"ScopeCategory": "DataScope",
"ScopeDefineOrgIdList": "DefineOrgList"
},
"ThingsGateway.Admin.Application.SysResource": {
"Title": "Title",
"Module": "Module",
"Title.Required": "{0} is required",
"Href.Required": "{0} is required",
"Icon": "Icon",
"Href": "Path",
"Code": "Code",
"Category": "Category",
"Target": "Target",
"NavLinkMatch": "NavLinkMatch",
"ParentId": "Parent",
"ResourceDup": "Duplicate name {0} exists",
"ResourceParentChoiceSelf": "Parent cannot choose itself",
"ResourceParentNull": "Parent does not exist {0}",
"NotFoundResource": "System exception, menu not found",
"ModuleIdDiff": "Module is inconsistent with parent menu",
"CanotDeleteSystemResource": "Cannot delete system resource {0}",
"ResourceMenuHrefNotNull": "Menu href cannot null"
"ThingsGateway.Admin.Application.DictTypeEnum": {
"Define": "Business",
"System": "System"
},
"ThingsGateway.Admin.Application.SysOrgCopyInput": {
"TargetId": "Target",
"ContainsChild": "ContainsChild",
"ContainsPosition": "ContainsPosition"
},
"ThingsGateway.Admin.Application.SysPosition": {
"Category.Required": "{0} is a required field",
"Name.Required": "{0} is a required field",
"Code.Required": "{0} is a required field",
"OrgId.MinValue": "{0} is a required field",
"Category": "Category",
"Name": "Name",
"Code": "Code",
"Status": "Status",
"OrgId": "Organization",
"Remark": "Remarks",
"Dup": "Duplicate position exists with Category {0} and Name {1}",
"CodeDup": "Duplicate code {0} exists",
"NameDup": "Duplicate name {0} exists",
"CanotContainsSelf": "Cannot contain itself",
"TargetNameDup": "Target node has duplicate name {0}",
"ParentChoiceSelf": "Parent cannot be itself",
"ParentNull": "Parent does not exist {0}",
"DeleteUserFirst": "Please remove the users under the position first"
},
"ThingsGateway.Admin.Application.SysOrg": {
"Category.Required": "{0} is a required field",
"Name.Required": "{0} is a required field",
"Code.Required": "{0} is a required field",
"Category": "Category",
"Name": "Name",
"Code": "Code",
"Status": "Status",
"ParentId": "ParentOrg",
"Names": "Names",
"Remark": "Remarks",
"DirectorId": "Director",
"Dup": "Duplicate organization exists with Category {0} and Name {1}",
"CodeDup": "Duplicate code {0} exists",
"NameDup": "Duplicate name {0} exists",
"CanotContainsSelf": "Cannot contain itself",
"TargetNameDup": "Target node has duplicate name {0}",
"ParentChoiceSelf": "Parent cannot be itself",
"ParentNull": "Parent does not exist {0}",
"DeleteUserFirst": "Please remove the users under the organization first",
"DeleteRoleFirst": "Please remove the roles under the organization first",
"DeletePositionFirst": "Please remove the positions under the organization first",
"RootOrg": "Unable to create top-level organization"
},
"ThingsGateway.Admin.Application.OrgEnum": {
"COMPANY": "Company",
"DEPT": "Dept"
},
"ThingsGateway.Admin.Application.PositionCategoryEnum": {
"HIGH": "High",
"MIDDLE": "Middle",
"LOW": "Low"
},
//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.Admin.Application.FileService": {
"FileNullError": "File cannot be empty",
"FileLengthError": "File size cannot exceed {0} M",
"FileNullError": "File cannot be empty",
"FileTypeError": "Not supported format {0}"
},
"ThingsGateway.Admin.Application.UnifyResultProvider": {
"TokenOver": "Login has expired, please login again",
"NoPermission": "Access denied, no permission"
},
"ThingsGateway.Admin.Application.AuthService": {
"TenantNull": "The tenant does not exist",
"OrgDisable": "The affiliated company/department has been deactivated, please contact the administrator",
"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}"
},
"ThingsGateway.Admin.Application.HardwareInfo": {
"Environment": "HostEnvironment",
"FrameworkDescription": ".NETFramework",
"OsArchitecture": "System Architecture",
"UUID": "UUID",
"UpdateTime": "UpdateTime"
"UpdateTime": "UpdateTime",
"UUID": "UUID"
},
"ThingsGateway.Admin.Application.HistoryHardwareInfo": {
"DriveUsage": "Disk Usage",
"MemoryUsage": "Memory",
"CpuUsage": "CPU Usage",
"Temperature": "Temperature",
"Battery": "Battery"
},
//oper
"ThingsGateway.Admin.Application.OperDescAttribute": {
//dict
"SaveDict": "Modify dictionary",
"DeleteDict": "Delete dictionary",
"EditLoginPolicy": "Modify login policy",
"EditPasswordPolicy": "Modify password policy",
"EditPagePolicy": "Modify page policy",
"EditWebsitePolicy": "Modify website settings",
//operlog
"DeleteOperLog": "Delete operation log",
"ExportOperLog": "Export operation log",
//resource
"SaveResource": "Modify resource",
"DeleteResource": "Delete resource",
//role
"SaveRole": "Modify role",
"DeleteRole": "Delete role",
"RoleGrantResource": "Role grant resource",
"RoleGrantUser": "Role grant user",
"RoleGrantApiPermission": "Role grant OpenApi",
"GrantApi": "GrantApi",
"GrantUser": "GrantUser",
"GrantRole": "GrantRole",
"GrantResource": "GrantResource",
//user
"SaveUser": "Modify user",
"DeleteuSER": "Delete user",
"ResetPassword": "Reset pw",
"UserGrantRole": "User grant role",
"UserGrantResource": "User grant resource",
"UserGrantApiPermission": "User grant OpenApi",
//usercenter
"UpdateUserInfo": "Update personal information",
"WorkbenchInfo": "Update personal workbench",
"UpdatePassword": "Update personal password",
//session
"ExitVerificat": "Force token off",
"ExitSession": "Force session off",
"CopyOrg": "Copy Organization",
"DeleteOrg": "Delete Organization",
"SaveOrg": "Save Organization",
"DeletePosition": "Delete Position",
"SavePosition": "Save Position",
"NoPermission": "No Permission",
"CopyResource": "CopyResource",
"ChangeParentResource": "ChangeParentResource"
},
//service
"ThingsGateway.Admin.Application.HardwareJob": {
"GetHardwareInfoFail": "Get Hardwareinfo Fail"
},
//dto
"ThingsGateway.Admin.Application.UserSelectorOutput": {
"ThingsGateway.Admin.Application.HistoryHardwareInfo": {
"Battery": "Battery",
"CpuUsage": "CPU Usage",
"DriveUsage": "Disk Usage",
"MemoryUsage": "Memory",
"Temperature": "Temperature"
},
"ThingsGateway.Admin.Application.LogCateGoryEnum": {
"Exception": "Exception",
"Login": "Login",
"Logout": "Logout",
"Operate": "Operation"
},
"ThingsGateway.Admin.Application.LogEnum": {
"FAIL": "Fail",
"SUCCESS": "Success"
},
"ThingsGateway.Admin.Application.LoginInput": {
"Account": "Account",
"OrgId": "Org"
"Account.Required": "{0} is required",
"Password": "Password",
"Password.Required": "{0} is required"
},
"ThingsGateway.Admin.Application.LoginPolicy": {
"ErrorCount": "Login error count lock threshold",
"ErrorCount.MinValue": "{0} value is too small",
"ErrorLockTime": "Login error lock duration (min)",
"ErrorLockTime.MinValue": "{0} value is too small",
"ErrorResetTime": "Login error count expiration duration (min)",
"ErrorResetTime.MinValue": "{0} value is too small",
"SingleOpen": "Single user login switch",
"VerificatExpireTime": "Login expiration time (min)",
"VerificatExpireTime.MinValue": "{0} value is too small"
},
"ThingsGateway.Admin.Application.LogoutInput": {
"VerificatId.Required": "{0} is required"
},
"ThingsGateway.Admin.Application.OpenApiAuthController": {
"LoginAsync": "Login",
"LogoutAsync": "Logout",
"OpenApiAuthController": "Login API"
},
"ThingsGateway.Admin.Application.OperateLogPageInput": {
"Account": "Account",
"Category": "Category",
"SearchDate": "SearchDate"
},
"ThingsGateway.Admin.Application.OperDescAttribute": {
"ChangeParentResource": "ChangeParentResource",
"CopyOrg": "Copy Organization",
"CopyResource": "CopyResource",
"DeleteDict": "Delete dictionary",
"DeleteOperLog": "Delete operation log",
"DeleteOrg": "Delete Organization",
"DeletePosition": "Delete Position",
"DeleteResource": "Delete resource",
"DeleteRole": "Delete role",
"DeleteuSER": "Delete user",
"EditLoginPolicy": "Modify login policy",
"EditPagePolicy": "Modify page policy",
"EditPasswordPolicy": "Modify password policy",
"EditWebsitePolicy": "Modify website settings",
"ExitSession": "Force session off",
"ExitVerificat": "Force token off",
"ExportOperLog": "Export operation log",
"GrantApi": "GrantApi",
"GrantResource": "GrantResource",
"GrantRole": "GrantRole",
"GrantUser": "GrantUser",
"NoPermission": "No Permission",
"ResetPassword": "Reset pw",
"RoleGrantApiPermission": "Role grant OpenApi",
"RoleGrantResource": "Role grant resource",
"RoleGrantUser": "Role grant user",
"SaveDict": "Modify dictionary",
"SaveOrg": "Save Organization",
"SavePosition": "Save Position",
"SaveResource": "Modify resource",
"SaveRole": "Modify role",
"SaveUser": "Modify user",
"UpdatePassword": "Update personal password",
"UpdateUserInfo": "Update personal information",
"UserGrantApiPermission": "User grant OpenApi",
"UserGrantResource": "User grant resource",
"UserGrantRole": "User grant role",
"WorkbenchInfo": "Update personal workbench"
},
"ThingsGateway.Admin.Application.OrgEnum": {
"COMPANY": "Company",
"DEPT": "Dept"
},
"ThingsGateway.Admin.Application.PagePolicy": {
"Razor": "Default homepage",
"Shortcuts": "Default shortcuts"
},
"ThingsGateway.Admin.Application.PasswordPolicy": {
"DefaultPassword": "Default user password",
"DefaultPassword.Required": "{0} is required",
"PasswordContainChar": "Contain special characters",
"PasswordContainLower": "Contain lowercase letters",
"PasswordContainNum": "Contain numbers",
"PasswordContainUpper": "Contain uppercase letters",
"PasswordMinLen": "Minimum password length",
"PasswordMinLen.MinValue": "{0} value is too small"
},
"ThingsGateway.Admin.Application.PositionCategoryEnum": {
"HIGH": "High",
"LOW": "Low",
"MIDDLE": "Middle"
},
"ThingsGateway.Admin.Application.ResourceCategoryEnum": {
"Button": "Button",
"Menu": "Menu",
"Module": "Module"
},
"ThingsGateway.Admin.Application.ResourceTableSearchModel": {
"Module": "Module",
"Href": "Path",
"Module": "Module",
"Title": "Title"
},
"ThingsGateway.Admin.Application.WorkbenchInfo": {
"Razor": "Homepage",
"Shortcuts": "Shortcuts"
"ThingsGateway.Admin.Application.RoleCategoryEnum": {
"Global": "Global",
"Org": "Org"
},
"ThingsGateway.Admin.Application.UpdatePasswordInput": {
"Password": "Password",
"NewPassword": "New password",
"ConfirmPassword": "Confirm password",
"Password.Required": "{0} is required",
"NewPassword.Required": "{0} is required",
"ConfirmPassword.Required": "{0} is required"
},
"ThingsGateway.Admin.Application.VerificatInfo": {
"Expire": "Expire(min)",
"Online": "Online",
"VerificatTimeout": "VerificatTimeout",
"Device": "Device",
"LoginIp": "LoginIp",
"LoginTime": "LoginTime"
},
"ThingsGateway.Admin.Application.SessionOutput": {
"Account": "Account",
"Online": "Online status",
"LatestLoginIp": "Latest login IP",
"LatestLoginTime": "Latest login time",
"Online": "Online status",
"VerificatCount": "Token count"
},
"ThingsGateway.Admin.Application.SysDict": {
"Category.Required": "{0} is required",
"Name.Required": "{0} is required",
"Code.Required": "{0} is required",
"Category": "Category",
"Name": "Name",
"Category.Required": "{0} is required",
"Code": "Code",
"Remark": "Remark",
"Code.Required": "{0} is required",
"DemoCanotUpdateWebsitePolicy": "DEMO environment does not allow modifying website settings",
"DictDup": "Duplicate configuration exists, category {0}, name {1}"
"DictDup": "Duplicate configuration exists, category {0}, name {1}",
"Name": "Name",
"Name.Required": "{0} is required",
"Remark": "Remark"
},
"ThingsGateway.Admin.Application.SysOperateLog": {
"Category": "Category",
"ClassName": "ClassName",
"ExeMessage": "ExeMessage",
"MethodName": "MethodName",
"ParamJson": "ParamJson",
"ReqMethod": "RequestMethod",
"ReqUrl": "RequestUrl",
"ResultJson": "ResultJson",
"Category": "Category",
"ExeStatus": "ExeStatus",
"MethodName": "MethodName",
"Name": "Name",
"OpAccount": "OpAccount",
"OpBrowser": "OpBrowser",
"OpIp": "OpIp",
"OpOs": "OpOs",
"OpTime": "OpTime",
"ParamJson": "ParamJson",
"ReqMethod": "RequestMethod",
"ReqUrl": "RequestUrl",
"ResultJson": "ResultJson",
"VerificatId": "VerificatId"
},
"ThingsGateway.Admin.Application.OperateLogPageInput": {
"SearchDate": "SearchDate",
"Account": "Account",
"Category": "Category"
"ThingsGateway.Admin.Application.SysOrg": {
"CanotContainsSelf": "Cannot contain itself",
"Category": "Category",
"Category.Required": "{0} is a required field",
"Code": "Code",
"Code.Required": "{0} is a required field",
"CodeDup": "Duplicate code {0} exists",
"DeletePositionFirst": "Please remove the positions under the organization first",
"DeleteRoleFirst": "Please remove the roles under the organization first",
"DeleteUserFirst": "Please remove the users under the organization first",
"DirectorId": "Director",
"Dup": "Duplicate organization exists with Category {0} and Name {1}",
"Name": "Name",
"Name.Required": "{0} is a required field",
"NameDup": "Duplicate name {0} exists",
"Names": "Names",
"ParentChoiceSelf": "Parent cannot be itself",
"ParentId": "ParentOrg",
"ParentNull": "Parent does not exist {0}",
"Remark": "Remarks",
"RootOrg": "Unable to create top-level organization",
"Status": "Status",
"TargetNameDup": "Target node has duplicate name {0}"
},
"ThingsGateway.Admin.Application.LoginInput": {
"Account": "Account",
"Password": "Password",
"Account.Required": "{0} is required",
"Password.Required": "{0} is required"
"ThingsGateway.Admin.Application.SysOrgCopyInput": {
"ContainsChild": "ContainsChild",
"ContainsPosition": "ContainsPosition",
"TargetId": "Target"
},
"ThingsGateway.Admin.Application.LogoutInput": {
"VerificatId.Required": "{0} is required"
"ThingsGateway.Admin.Application.SysPosition": {
"CanotContainsSelf": "Cannot contain itself",
"Category": "Category",
"Category.Required": "{0} is a required field",
"Code": "Code",
"Code.Required": "{0} is a required field",
"CodeDup": "Duplicate code {0} exists",
"DeleteUserFirst": "Please remove the users under the position first",
"Dup": "Duplicate position exists with Category {0} and Name {1}",
"Name": "Name",
"Name.Required": "{0} is a required field",
"NameDup": "Duplicate name {0} exists",
"OrgId": "Organization",
"OrgId.MinValue": "{0} is a required field",
"ParentChoiceSelf": "Parent cannot be itself",
"ParentNull": "Parent does not exist {0}",
"Remark": "Remarks",
"Status": "Status",
"TargetNameDup": "Target node has duplicate name {0}"
},
"ThingsGateway.Admin.Application.AppConfig": {
"LoginPolicy": "LoginPolicy",
"PasswordPolicy": "PasswordPolicy",
"PagePolicy": "PagePolicy",
"WebsitePolicy": "WebsitePolicy"
},
"ThingsGateway.Admin.Application.LoginPolicy": {
"SingleOpen": "Single user login switch",
"ErrorLockTime": "Login error lock duration (min)",
"ErrorResetTime": "Login error count expiration duration (min)",
"ErrorCount": "Login error count lock threshold",
"VerificatExpireTime": "Login expiration time (min)",
"ErrorLockTime.MinValue": "{0} value is too small",
"ErrorResetTime.MinValue": "{0} value is too small",
"ErrorCount.MinValue": "{0} value is too small",
"VerificatExpireTime.MinValue": "{0} value is too small"
},
"ThingsGateway.Admin.Application.PagePolicy": {
"Shortcuts": "Default shortcuts",
"Razor": "Default homepage"
},
"ThingsGateway.Admin.Application.PasswordPolicy": {
"DefaultPassword": "Default user password",
"DefaultPassword.Required": "{0} is required",
"PasswordMinLen": "Minimum password length",
"PasswordMinLen.MinValue": "{0} value is too small",
"PasswordContainNum": "Contain numbers",
"PasswordContainLower": "Contain lowercase letters",
"PasswordContainUpper": "Contain uppercase letters",
"PasswordContainChar": "Contain special characters"
},
"ThingsGateway.Admin.Application.WebsitePolicy": {
"WebStatus": "WebStatus",
"CloseTip": "CloseTip",
"CloseTip.Required": "{0} is required"
},
//enum
"ThingsGateway.Admin.Application.ResourceCategoryEnum": {
"ThingsGateway.Admin.Application.SysResource": {
"CanotDeleteSystemResource": "Cannot delete system resource {0}",
"Category": "Category",
"Code": "Code",
"Href": "Path",
"Href.Required": "{0} is required",
"Icon": "Icon",
"Module": "Module",
"Menu": "Menu",
"Button": "Button"
"ModuleIdDiff": "Module is inconsistent with parent menu",
"NavLinkMatch": "NavLinkMatch",
"NotFoundResource": "System exception, menu not found",
"ParentId": "Parent",
"ResourceDup": "Duplicate name {0} exists",
"ResourceMenuHrefNotNull": "Menu href cannot null",
"ResourceParentChoiceSelf": "Parent cannot choose itself",
"ResourceParentNull": "Parent does not exist {0}",
"Target": "Target",
"Title": "Title",
"Title.Required": "{0} is required"
},
"ThingsGateway.Admin.Application.SysRole": {
"CannotRoleScopeAll": "Organization role cannot select global data scope",
"CanotDeleteAdmin": "Cannot delete built-in super admin role",
"CanotEditAdmin": "Cannot edit super admin role",
"CanotGrantAdmin": "Cannot assign admins roles",
"Category": "Category",
"Code": "Code",
"CodeDup": "Duplicate code exists: {0}",
"Global": "Global",
"Name": "Name",
"Name.Required": "{0} is required",
"NameDup": "Duplicate role name {0}",
"OrgId": "Org",
"OrgNotNull": "Organization cannot be null",
"SameOrgNameDup": "Duplicate role name exists: {0}",
"Status": "Status"
},
"ThingsGateway.Admin.Application.SysUser": {
"Account": "Account",
"Account.Required": "Account.Required",
"AccountDup": "Duplicate account {0} exists",
"Avatar": "Avatar",
"CanotDeleteAdminUser": "Cannot delete built-in super admin user",
"CanotDeleteSelf": "Cannot delete yourself",
"CanotEditAdminUser": "Cannot edit super admin user",
"CanotGrantAdmin": "Cannot assign admins roles",
"CheckSelf": "Prohibit {0} yourself",
"ConfirmPasswordDiff": "Passwords entered twice are inconsistent",
"DemoCanotUpdatePassword": "DEMO environment does not allow password modification",
"DirectorId": "Director",
"DirectorSelf": "Cannot set oneself as the supervisor",
"Disable": "Disable",
"Email": "Email",
"EmailDup": "Duplicate email {0} exists",
"EmailError": "Email format error {0}",
"Enable": "Enable",
"ExitVerificat": "You have been forcibly logged out",
"GrantRole": "GrantRole",
"LastLoginAddress": "LastLoginAddress",
"LastLoginDevice": "LastLoginDevice",
"LastLoginIp": "LastLoginIp",
"LastLoginTime": "LastLoginTime",
"LatestLoginAddress": "LatestLoginAddress",
"LatestLoginDevice": "LatestLoginDevice",
"LatestLoginIp": "LatestLoginIp",
"LatestLoginTime": "LatestLoginTime",
"NoOrg": "The organization does not exist",
"OldPasswordError": "Incorrect old password",
"OrgId": "Org",
"OrgNames": "OrgNames",
"Password": "Password",
"PasswordEdited": "Password changed, logged out",
"PasswordLengthLess": "Password length cannot be less than {0}",
"PasswordMustLow": "Password must contain lowercase letters",
"PasswordMustNum": "Password must contain numbers",
"PasswordMustSpecial": "Password must contain special characters",
"PasswordMustUpp": "Password must contain uppercase letters",
"Phone": "Phone",
"PhoneError": "Phone number format error {0}",
"PositionId": "Position",
"PositionName": "PositionName",
"Status": "Status"
},
"ThingsGateway.Admin.Application.TargetEnum": {
"_self": "Current window",
"_blank": "New window",
"_parent": "Parent window",
"_self": "Current window",
"_top": "Top window"
},
"ThingsGateway.Admin.Application.DictTypeEnum": {
"System": "System",
"Define": "Business"
"ThingsGateway.Admin.Application.TestController": {
"Test": "Test",
"TestController": "Test API"
},
"ThingsGateway.Admin.Application.LogCateGoryEnum": {
"Login": "Login",
"Logout": "Logout",
"Operate": "Operation",
"Exception": "Exception"
"ThingsGateway.Admin.Application.UnifyResultProvider": {
"NoPermission": "Access denied, no permission",
"TokenOver": "Login has expired, please login again"
},
"ThingsGateway.Admin.Application.LogEnum": {
"SUCCESS": "Success",
"FAIL": "Fail"
"ThingsGateway.Admin.Application.UpdatePasswordInput": {
"ConfirmPassword": "Confirm password",
"ConfirmPassword.Required": "{0} is required",
"NewPassword": "New password",
"NewPassword.Required": "{0} is required",
"Password": "Password",
"Password.Required": "{0} is required"
},
"ThingsGateway.Admin.Application.UserSelectorOutput": {
"Account": "Account",
"OrgId": "Org"
},
"ThingsGateway.Admin.Application.VerificatInfo": {
"Device": "Device",
"Expire": "Expire(min)",
"LoginIp": "LoginIp",
"LoginTime": "LoginTime",
"Online": "Online",
"VerificatTimeout": "VerificatTimeout"
},
"ThingsGateway.Admin.Application.WebsitePolicy": {
"CloseTip": "CloseTip",
"CloseTip.Required": "{0} is required",
"WebStatus": "WebStatus"
},
"ThingsGateway.Admin.Application.WorkbenchInfo": {
"Razor": "Homepage",
"Shortcuts": "Shortcuts"
}
}
}

View File

@@ -1,462 +1,402 @@
{
"ThingsGateway.Admin.Application.AppConfig": {
"LoginPolicy": "登录策略",
"PagePolicy": "页面设置",
"PasswordPolicy": "密码策略",
"WebsitePolicy": "网站设置"
},
"ThingsGateway.Admin.Application.AuthController": {
"AuthController": "登录API",
"LoginAsync": "登录",
"LogoutAsync": "注销"
},
"ThingsGateway.Admin.Application.AuthService": {
"AuthErrorMax": "账号密码错误,超过 {0} 次后将锁定 {1} 分钟,错误次数 {2} ",
"MustDesc": "密码需要DESC加密后传入",
"OrgDisable": "所属公司/部门已停用,请联系管理员",
"PasswordError": "密码错误次数过多,请 {0} 分钟后再试",
"SingleLoginWarn": "您的账号已在别处登录",
"TenantNull": "租户不存在",
"UserDisable": "账号 {0} 已停用",
"UserNoModule": "该账号未分配模块,请联系管理员",
"UserNull": "用户 {0} 不存在"
},
"ThingsGateway.Admin.Application.BaseDataEntity": {
"CreateOrgId": "创建机构Id"
},
"ThingsGateway.Admin.Application.BaseEntity": {
"SortCode": "排序",
"CreateTime": "创建时间",
"CreateUser": "创建人",
"SortCode": "排序",
"UpdateTime": "更新时间",
"UpdateUser": "更新人"
},
"ThingsGateway.Admin.Application.BlazorAuthenticationHandler": {
"UserExpire": "用户登录已过期,请重新登录"
},
"ThingsGateway.Admin.Application.SysUser": {
"Disable": "禁用",
"Enable": "启用",
"GrantRole": "分配角色",
"ExitVerificat": "您已被强制下线",
"PasswordEdited": "密码被修改,已退出登录",
"Avatar": "头像",
"Account": "账号",
"Account.Required": " {0} 是必填项",
"Password": "密码",
"Status": "状态",
"Phone": "手机",
"Email": "邮箱",
"LastLoginIp": "上次登录ip",
"LastLoginDevice": "上次登录设备",
"LastLoginTime": "上次登录时间",
"LastLoginAddress": "上次登录地点",
"LatestLoginIp": "最新登录ip",
"LatestLoginTime": "最新登录时间",
"LatestLoginDevice": "最新登录设备",
"LatestLoginAddress": "最新登录地点",
"OrgNames": "机构名称",
"PositionName": "职位名称",
"OrgId": "机构",
"PositionId": "职位",
"DirectorId": "主管",
"CheckSelf": "禁止 {0} 自己",
"CanotDeleteAdminUser": "不可删除系统内置超管用户",
"CanotEditAdminUser": "不可编辑超管用户",
"CanotGrantAdmin": "不能分配超管角色",
"EmailDup": "存在重复的邮箱 {0}",
"AccountDup": "存在重复的账号 {0}",
"CanotDeleteSelf": "不可删除自己",
"EmailError": "邮箱 {0} 格式错误",
"PhoneError": "手机号码 {0} 格式错误",
"NoOrg": "组织机构不存在",
"DirectorSelf": "不能设置自己为主管",
"DemoCanotUpdatePassword": "DEMO环境不允许修改密码",
"OldPasswordError": "原密码错误",
"ConfirmPasswordDiff": "两次输入的密码不一致",
"PasswordLengthLess": "密码长度不能小于 {0} ",
"PasswordMustNum ": "密码必须包含数字",
"PasswordMustLow": "密码必须包含小写字母",
"PasswordMustUpp": "密码必须包含大写字母",
"PasswordMustSpecial": "密码必须包含特殊字符"
},
"ThingsGateway.Admin.Application.SysRole": {
"Code": "编码",
"Name": "名称",
"Name.Required": " {0} 是必填项",
"Category": "分类",
"Global": "全局",
"Status": "状态",
"OrgId": "机构",
"CanotDeleteAdmin": "不可删除系统内置超管角色",
"CanotEditAdmin": "不可编辑超管角色",
"CanotGrantAdmin": "不能分配超管角色",
"NameDup": "存在重复的角色名称 {0}",
"OrgNotNull": "机构不能为空",
"SameOrgNameDup": "存在重复的角色名称 {0}",
"CannotRoleScopeAll": "机构角色不能选择全局数据范围",
"CodeDup": "存在重复的编码 {0}"
},
"ThingsGateway.Admin.Application.RoleCategoryEnum": {
"Global": "全局",
"Org": "机构"
},
"ThingsGateway.Admin.Application.DataScopeEnum": {
"SCOPE_SELF": "仅自己",
"SCOPE_ALL": "全部",
"SCOPE_ORG": "仅所属组织",
"SCOPE_ORG_CHILD": "所属组织及以下",
"SCOPE_ORG_DEFINE": "自定义"
"SCOPE_ORG_DEFINE": "自定义",
"SCOPE_SELF": "仅自己"
},
"ThingsGateway.Admin.Application.DefaultDataScope": {
"ScopeCategory": "数据范围",
"ScopeDefineOrgIdList": "自定义列表"
},
"ThingsGateway.Admin.Application.SysResource": {
"Title": "标题",
"Module": "模块",
"Title.Required": "{0} 是必填项",
"Href.Required": "{0} 是必填项",
"Icon": "图标",
"Href": "路径",
"Code": "编码",
"Category": "分类",
"Target": "跳转类型",
"NavLinkMatch": "匹配类型",
"ParentId": "上级菜单",
"ResourceDup": "存在重复的名称 {0}",
"ResourceParentChoiceSelf": "父级不能选择自己",
"ResourceParentNull": "父级不存在 {0}",
"NotFoundResource": "系统异常,没找到该菜单",
"ModuleIdDiff": "模块与上级菜单不一致",
"CanotDeleteSystemResource": "不可删除系统资源 {0}",
"ResourceMenuHrefNotNull": "菜单的路径不能为空"
"ThingsGateway.Admin.Application.DictTypeEnum": {
"Define": "业务配置",
"System": "系统配置"
},
"ThingsGateway.Admin.Application.SysOrgCopyInput": {
"TargetId": "目标机构",
"ContainsChild": "包含下级",
"ContainsPosition": "包含职位"
"ThingsGateway.Admin.Application.FileService": {
"FileLengthError": "文件大小不允许超过 {0} M",
"FileNullError": "文件不能为空",
"FileTypeError": "不支持 {0} 格式"
},
"ThingsGateway.Admin.Application.SysPosition": {
"Category.Required": "{0} 是必填项",
"Name.Required": "{0} 是必填项",
"Code.Required": "{0} 是必填项",
"OrgId.MinValue": "{0} 是必填项",
"Category": "分类",
"Name": "名称",
"Code": "代码",
"Status": "状态",
"OrgId": "机构",
"Remark": "备注",
"Dup": "存在重复的岗位 分类 {0} 名称 {1}",
"CodeDup": "存在重复的编码 {0}",
"NameDup": "存在重复的名称 {0}",
"CanotContainsSelf": "不可包含自己",
"TargetNameDup": "目标节点存在重复的名称 {0}",
"ParentChoiceSelf": "父级不能选择自己",
"ParentNull": "父级不存在 {0}",
"DeleteUserFirst": "请先删除职位下的用户"
"ThingsGateway.Admin.Application.HardwareInfo": {
"Environment": "主机环境",
"FrameworkDescription": "NET框架",
"OsArchitecture": "系统架构",
"UpdateTime": "更新时间",
"UUID": "唯一编码"
},
"ThingsGateway.Admin.Application.SysOrg": {
"Category.Required": "{0} 是必填项",
"Name.Required": "{0} 是必填项",
"Code.Required": "{0} 是必填项",
"ThingsGateway.Admin.Application.HardwareJob": {
"GetHardwareInfoFail": "获取硬件信息出错"
},
"ThingsGateway.Admin.Application.HistoryHardwareInfo": {
"Battery": "电池",
"CpuUsage": "CPU使用率",
"DriveUsage": "磁盘使用率",
"MemoryUsage": "内存",
"Temperature": "温度"
},
"ThingsGateway.Admin.Application.LogCateGoryEnum": {
"Exception": "异常",
"Login": "登录",
"Logout": "注销",
"Operate": "操作"
},
"ThingsGateway.Admin.Application.LogEnum": {
"FAIL": "失败",
"SUCCESS": "成功"
},
"ThingsGateway.Admin.Application.LoginInput": {
"Account": "登录账号",
"Account.Required": "{0} 是必填项",
"Password": "登录密码",
"Password.Required": "{0} 是必填项"
},
"ThingsGateway.Admin.Application.LoginPolicy": {
"ErrorCount": "登录错误次数锁定阈值",
"ErrorCount.MinValue": " {0} 值太小",
"ErrorLockTime": "登录错误锁定时长(分)",
"ErrorLockTime.MinValue": " {0} 值太小",
"ErrorResetTime": "登录错误次数过期时长(分)",
"ErrorResetTime.MinValue": " {0} 值太小",
"SingleOpen": "单用户登录开关",
"VerificatExpireTime": "登录过期时间(分)",
"VerificatExpireTime.MinValue": " {0} 值太小"
},
"ThingsGateway.Admin.Application.LogoutInput": {
"VerificatId.Required": "{0} 是必填项"
},
"ThingsGateway.Admin.Application.OpenApiAuthController": {
"LoginAsync": "登录",
"LogoutAsync": "注销",
"OpenApiAuthController": "登录API"
},
"ThingsGateway.Admin.Application.OperateLogPageInput": {
"Account": "操作账号",
"Category": "分类",
"Name": "名称",
"Code": "代码",
"Status": "状态",
"ParentId": "上级机构",
"Names": "机构全称",
"Remark": "备注",
"DirectorId": "主管",
"Dup": "存在重复的机构 分类 {0} 名称 {1}",
"CodeDup": "存在重复的编码 {0}",
"NameDup": "存在重复的名称 {0}",
"CanotContainsSelf": "不可包含自己",
"TargetNameDup": "目标节点存在重复的名称 {0}",
"ParentChoiceSelf": "父级不能选择自己",
"ParentNull": "父级不存在 {0}",
"DeleteUserFirst": "请先删除机构下的用户",
"DeleteRoleFirst": "请先删除机构下的角色",
"DeletePositionFirst": "请先删除机构下的职位",
"RootOrg": "无法创建顶层机构"
"SearchDate": "时间范围"
},
"ThingsGateway.Admin.Application.OperDescAttribute": {
"ChangeParentResource": "更改父节点",
"CopyOrg": "复制机构",
"CopyResource": "复制资源",
"DeleteDict": "删除字典",
"DeleteOperLog": "删除操作日志",
"DeleteOrg": "删除机构",
"DeletePosition": "删除岗位",
"DeleteResource": "删除资源",
"DeleteRole": "删除角色",
"DeleteuSER": "删除用户",
"EditLoginPolicy": "修改登录策略",
"EditPagePolicy": "修改页面策略",
"EditPasswordPolicy": "修改密码策略",
"EditWebsitePolicy": "修改网站设置",
"ExitSession": "强退会话",
"ExitVerificat": "强退令牌",
"ExportOperLog": "导出操作日志",
"GrantApi": "API",
"GrantResource": "资源",
"GrantRole": "角色",
"GrantUser": "用户",
"NoPermission": "无权限操作",
"ResetPassword": "重置密码",
"RoleGrantApiPermission": "角色授权OpenApi",
"RoleGrantResource": "角色授权资源",
"RoleGrantUser": "角色授权用户",
"SaveDict": "修改字典",
"SaveOrg": "保存机构",
"SavePosition": "保存岗位",
"SaveResource": "修改资源",
"SaveRole": "修改角色",
"SaveUser": "修改用户",
"UpdatePassword": "更新个人密码",
"UpdateUserInfo": "更新个人信息",
"UserGrantApiPermission": "用户授权OpenApi",
"UserGrantResource": "用户授权资源",
"UserGrantRole": "用户授权角色",
"WorkbenchInfo": "更新个人工作台"
},
"ThingsGateway.Admin.Application.OrgEnum": {
"COMPANY": "公司",
"DEPT": "部门"
},
"ThingsGateway.Admin.Application.PagePolicy": {
"Razor": "默认主页",
"Shortcuts": "默认快捷方式"
},
"ThingsGateway.Admin.Application.PasswordPolicy": {
"DefaultPassword": "默认用户密码",
"DefaultPassword.Required": " {0} 是必填项",
"PasswordContainChar": "包含特殊字符",
"PasswordContainLower": "包含小写字母",
"PasswordContainNum": "包含数字",
"PasswordContainUpper": "包含大写字母",
"PasswordMinLen": "密码最小长度",
"PasswordMinLen.MinValue": " {0} 值太小"
},
"ThingsGateway.Admin.Application.PositionCategoryEnum": {
"HIGH": "高层",
"MIDDLE": "层",
"LOW": "层"
"LOW": "层",
"MIDDLE": "层"
},
//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.Admin.Application.FileService": {
"FileNullError": "文件不能为空",
"FileLengthError": "文件大小不允许超过 {0} M",
"FileTypeError": "不支持 {0} 格式"
},
"ThingsGateway.Admin.Application.UnifyResultProvider": {
"TokenOver": "登录已过期,请重新登录",
"NoPermission": "禁止访问,没有权限"
},
"ThingsGateway.Admin.Application.AuthService": {
"TenantNull": "租户不存在",
"OrgDisable": "所属公司/部门已停用,请联系管理员",
"SingleLoginWarn": "您的账号已在别处登录",
"UserNull": "用户 {0} 不存在",
"PasswordError": "密码错误次数过多,请 {0} 分钟后再试",
"AuthErrorMax": "账号密码错误,超过 {0} 次后将锁定 {1} 分钟,错误次数 {2} ",
"UserDisable": "账号 {0} 已停用",
"MustDesc": "密码需要DESC加密后传入",
"UserNoModule": "该账号未分配模块,请联系管理员"
},
"ThingsGateway.Admin.Application.HardwareInfo": {
"Environment": "主机环境",
"FrameworkDescription": "NET框架",
"OsArchitecture": "系统架构",
"UUID": "唯一编码",
"UpdateTime": "更新时间"
},
"ThingsGateway.Admin.Application.HistoryHardwareInfo": {
"DriveUsage": "磁盘使用率",
"MemoryUsage": "内存",
"CpuUsage": "CPU使用率",
"Temperature": "温度",
"Battery": "电池"
},
//oper
"ThingsGateway.Admin.Application.OperDescAttribute": {
//dict
"SaveDict": "修改字典",
"DeleteDict": "删除字典",
"EditLoginPolicy": "修改登录策略",
"EditPasswordPolicy": "修改密码策略",
"EditPagePolicy": "修改页面策略",
"EditWebsitePolicy": "修改网站设置",
//operlog
"DeleteOperLog": "删除操作日志",
"ExportOperLog": "导出操作日志",
//resource
"SaveResource": "修改资源",
"DeleteResource": "删除资源",
//role
"SaveRole": "修改角色",
"DeleteRole": "删除角色",
"RoleGrantResource": "角色授权资源",
"RoleGrantUser": "角色授权用户",
"RoleGrantApiPermission": "角色授权OpenApi",
"GrantApi": "API",
"GrantUser": "用户",
"GrantRole": "角色",
"GrantResource": "资源",
//user
"SaveUser": "修改用户",
"DeleteuSER": "删除用户",
"ResetPassword": "重置密码",
"UserGrantRole": "用户授权角色",
"UserGrantResource": "用户授权资源",
"UserGrantApiPermission": "用户授权OpenApi",
//usercenter
"UpdateUserInfo": "更新个人信息",
"WorkbenchInfo": "更新个人工作台",
"UpdatePassword": "更新个人密码",
//session
"ExitVerificat": "强退令牌",
"ExitSession": "强退会话",
"CopyOrg": "复制机构",
"DeleteOrg": "删除机构",
"SaveOrg": "保存机构",
"DeletePosition": "删除岗位",
"SavePosition": "保存岗位",
"NoPermission": "无权限操作",
"CopyResource": "复制资源",
"ChangeParentResource": "更改父节点"
},
//service
"ThingsGateway.Admin.Application.HardwareJob": {
"GetHardwareInfoFail": "获取硬件信息出错"
},
//dto
"ThingsGateway.Admin.Application.UserSelectorOutput": {
"Account": "账号",
"OrgId": "机构"
"ThingsGateway.Admin.Application.ResourceCategoryEnum": {
"Button": "按钮",
"Menu": "菜单",
"Module": "模块"
},
"ThingsGateway.Admin.Application.ResourceTableSearchModel": {
"Module": "模块",
"Href": "路径",
"Module": "模块",
"Title": "标题"
},
"ThingsGateway.Admin.Application.WorkbenchInfo": {
"Razor": "主页",
"Shortcuts": "快捷方式"
"ThingsGateway.Admin.Application.RoleCategoryEnum": {
"Global": "全局",
"Org": "机构"
},
"ThingsGateway.Admin.Application.UpdatePasswordInput": {
"Password": "密码",
"NewPassword": "新密码",
"ConfirmPassword": "确认密码",
"Password.Required": " {0} 是必填项",
"NewPassword.Required": " {0} 是必填项",
"ConfirmPassword.Required": " {0} 是必填项"
},
"ThingsGateway.Admin.Application.VerificatInfo": {
"Expire": "过期时间(分)",
"Online": "在线状态",
"VerificatTimeout": "超时时间",
"Device": "登录设备",
"LoginIp": "登录IP",
"LoginTime": "登录时间"
},
"ThingsGateway.Admin.Application.SessionOutput": {
"Account": "账号",
"Online": "在线状态",
"LatestLoginIp": "最新登录ip",
"LatestLoginTime": "最新登录时间",
"Online": "在线状态",
"VerificatCount": "令牌数量"
},
"ThingsGateway.Admin.Application.SysDict": {
"Category.Required": "{0} 是必填项",
"Name.Required": "{0} 是必填项",
"Code.Required": "{0} 是必填项",
"Category": "分类",
"Name": "名称",
"Category.Required": "{0} 是必填项",
"Code": "代码",
"Remark": "备注",
"Code.Required": "{0} 是必填项",
"DemoCanotUpdateWebsitePolicy": "DEMO环境不允许修改网站设置",
"DictDup": "存在重复的配置 分类 {0} 名称 {1}",
"DemoCanotUpdateWebsitePolicy": "DEMO环境不允许修改网站设置"
"Name": "名称",
"Name.Required": "{0} 是必填项",
"Remark": "备注"
},
"ThingsGateway.Admin.Application.SysOperateLog": {
"Category": "日志分类",
"ClassName": "类名",
"ExeMessage": "具体消息",
"MethodName": "方法名称",
"ParamJson": "请求参数",
"ReqMethod": "请求方式",
"ReqUrl": "请求地址",
"ResultJson": "返回结果",
"Category": "日志分类",
"ExeStatus": "执行状态",
"MethodName": "方法名称",
"Name": "日志名称",
"OpAccount": "账号",
"OpBrowser": "浏览器",
"OpIp": "ip",
"OpOs": "系统",
"OpTime": "操作时间",
"ParamJson": "请求参数",
"ReqMethod": "请求方式",
"ReqUrl": "请求地址",
"ResultJson": "返回结果",
"VerificatId": "验证Id"
},
"ThingsGateway.Admin.Application.OperateLogPageInput": {
"SearchDate": "时间范围",
"Account": "操作账号",
"Category": "分类"
"ThingsGateway.Admin.Application.SysOrg": {
"CanotContainsSelf": "不可包含自己",
"Category": "分类",
"Category.Required": "{0} 是必填项",
"Code": "代码",
"Code.Required": "{0} 是必填项",
"CodeDup": "存在重复的编码 {0}",
"DeletePositionFirst": "请先删除机构下的职位",
"DeleteRoleFirst": "请先删除机构下的角色",
"DeleteUserFirst": "请先删除机构下的用户",
"DirectorId": "主管",
"Dup": "存在重复的机构 分类 {0} 名称 {1}",
"Name": "名称",
"Name.Required": "{0} 是必填项",
"NameDup": "存在重复的名称 {0}",
"Names": "机构全称",
"ParentChoiceSelf": "父级不能选择自己",
"ParentId": "上级机构",
"ParentNull": "父级不存在 {0}",
"Remark": "备注",
"RootOrg": "无法创建顶层机构",
"Status": "状态",
"TargetNameDup": "目标节点存在重复的名称 {0}"
},
"ThingsGateway.Admin.Application.LoginInput": {
"Account": "登录账号",
"Password": "登录密码",
"Account.Required": "{0} 是必填项",
"Password.Required": "{0} 是必填项"
"ThingsGateway.Admin.Application.SysOrgCopyInput": {
"ContainsChild": "包含下级",
"ContainsPosition": "包含职位",
"TargetId": "目标机构"
},
"ThingsGateway.Admin.Application.LogoutInput": {
"VerificatId.Required": "{0} 是必填项"
"ThingsGateway.Admin.Application.SysPosition": {
"CanotContainsSelf": "不可包含自己",
"Category": "分类",
"Category.Required": "{0} 是必填项",
"Code": "代码",
"Code.Required": "{0} 是必填项",
"CodeDup": "存在重复的编码 {0}",
"DeleteUserFirst": "请先删除职位下的用户",
"Dup": "存在重复的岗位 分类 {0} 名称 {1}",
"Name": "名称",
"Name.Required": "{0} 是必填项",
"NameDup": "存在重复的名称 {0}",
"OrgId": "机构",
"OrgId.MinValue": "{0} 是必填项",
"ParentChoiceSelf": "父级不能选择自己",
"ParentNull": "父级不存在 {0}",
"Remark": "备注",
"Status": "状态",
"TargetNameDup": "目标节点存在重复的名称 {0}"
},
"ThingsGateway.Admin.Application.AppConfig": {
"LoginPolicy": "登录策略",
"PasswordPolicy": "密码策略",
"PagePolicy": "页面设置",
"WebsitePolicy": "网站设置"
},
"ThingsGateway.Admin.Application.LoginPolicy": {
"SingleOpen": "单用户登录开关",
"ErrorLockTime": "登录错误锁定时长(分)",
"ErrorResetTime": "登录错误次数过期时长(分)",
"ErrorCount": "登录错误次数锁定阈值",
"VerificatExpireTime": "登录过期时间(分)",
"ErrorLockTime.MinValue": " {0} 值太小",
"ErrorResetTime.MinValue": " {0} 值太小",
"ErrorCount.MinValue": " {0} 值太小",
"VerificatExpireTime.MinValue": " {0} 值太小"
},
"ThingsGateway.Admin.Application.PagePolicy": {
"Shortcuts": "默认快捷方式",
"Razor": "默认主页"
},
"ThingsGateway.Admin.Application.PasswordPolicy": {
"DefaultPassword": "默认用户密码",
"DefaultPassword.Required": " {0} 是必填项",
"PasswordMinLen": "密码最小长度",
"PasswordMinLen.MinValue": " {0} 值太小",
"PasswordContainNum": "包含数字",
"PasswordContainLower": "包含小写字母",
"PasswordContainUpper": "包含大写字母",
"PasswordContainChar": "包含特殊字符"
},
"ThingsGateway.Admin.Application.WebsitePolicy": {
"WebStatus": "是否开放",
"CloseTip": "关闭提示",
"CloseTip.Required": " {0} 是必填项"
},
//enum
"ThingsGateway.Admin.Application.ResourceCategoryEnum": {
"ThingsGateway.Admin.Application.SysResource": {
"CanotDeleteSystemResource": "不可删除系统资源 {0}",
"Category": "分类",
"Code": "编码",
"Href": "路径",
"Href.Required": "{0} 是必填项",
"Icon": "图标",
"Module": "模块",
"Menu": "菜单",
"Button": "按钮"
"ModuleIdDiff": "模块与上级菜单不一致",
"NavLinkMatch": "匹配类型",
"NotFoundResource": "系统异常,没找到该菜单",
"ParentId": "上级菜单",
"ResourceDup": "存在重复的名称 {0}",
"ResourceMenuHrefNotNull": "菜单的路径不能为空",
"ResourceParentChoiceSelf": "父级不能选择自己",
"ResourceParentNull": "父级不存在 {0}",
"Target": "跳转类型",
"Title": "标题",
"Title.Required": "{0} 是必填项"
},
"ThingsGateway.Admin.Application.SysRole": {
"CannotRoleScopeAll": "机构角色不能选择全局数据范围",
"CanotDeleteAdmin": "不可删除系统内置超管角色",
"CanotEditAdmin": "不可编辑超管角色",
"CanotGrantAdmin": "不能分配超管角色",
"Category": "分类",
"Code": "编码",
"CodeDup": "存在重复的编码 {0}",
"Global": "全局",
"Name": "名称",
"Name.Required": " {0} 是必填项",
"NameDup": "存在重复的角色名称 {0}",
"OrgId": "机构",
"OrgNotNull": "机构不能为空",
"SameOrgNameDup": "存在重复的角色名称 {0}",
"Status": "状态"
},
"ThingsGateway.Admin.Application.SysUser": {
"Account": "账号",
"Account.Required": " {0} 是必填项",
"AccountDup": "存在重复的账号 {0}",
"Avatar": "头像",
"CanotDeleteAdminUser": "不可删除系统内置超管用户",
"CanotDeleteSelf": "不可删除自己",
"CanotEditAdminUser": "不可编辑超管用户",
"CanotGrantAdmin": "不能分配超管角色",
"CheckSelf": "禁止 {0} 自己",
"ConfirmPasswordDiff": "两次输入的密码不一致",
"DemoCanotUpdatePassword": "DEMO环境不允许修改密码",
"DirectorId": "主管",
"DirectorSelf": "不能设置自己为主管",
"Disable": "禁用",
"Email": "邮箱",
"EmailDup": "存在重复的邮箱 {0}",
"EmailError": "邮箱 {0} 格式错误",
"Enable": "启用",
"ExitVerificat": "您已被强制下线",
"GrantRole": "分配角色",
"LastLoginAddress": "上次登录地点",
"LastLoginDevice": "上次登录设备",
"LastLoginIp": "上次登录ip",
"LastLoginTime": "上次登录时间",
"LatestLoginAddress": "最新登录地点",
"LatestLoginDevice": "最新登录设备",
"LatestLoginIp": "最新登录ip",
"LatestLoginTime": "最新登录时间",
"NoOrg": "组织机构不存在",
"OldPasswordError": "原密码错误",
"OrgId": "机构",
"OrgNames": "机构名称",
"Password": "密码",
"PasswordEdited": "密码被修改,已退出登录",
"PasswordLengthLess": "密码长度不能小于 {0} ",
"PasswordMustLow": "密码必须包含小写字母",
"PasswordMustNum": "密码必须包含数字",
"PasswordMustSpecial": "密码必须包含特殊字符",
"PasswordMustUpp": "密码必须包含大写字母",
"Phone": "手机",
"PhoneError": "手机号码 {0} 格式错误",
"PositionId": "职位",
"PositionName": "职位名称",
"Status": "状态"
},
"ThingsGateway.Admin.Application.TargetEnum": {
"_self": "本窗口",
"_blank": "新窗口",
"_parent": "父级窗口",
"_self": "本窗口",
"_top": "顶级窗口"
},
"ThingsGateway.Admin.Application.DictTypeEnum": {
"System": "系统配置",
"Define": "业务配置"
"ThingsGateway.Admin.Application.TestController": {
"Test": "测试",
"TestController": "测试API"
},
"ThingsGateway.Admin.Application.LogCateGoryEnum": {
"Login": "登录",
"Logout": "注销",
"Operate": "操作",
"Exception": "异常"
"ThingsGateway.Admin.Application.UnifyResultProvider": {
"NoPermission": "禁止访问,没有权限",
"TokenOver": "登录已过期,请重新登录"
},
"ThingsGateway.Admin.Application.LogEnum": {
"SUCCESS": "成功",
"FAIL": "失败"
"ThingsGateway.Admin.Application.UpdatePasswordInput": {
"ConfirmPassword": "确认密码",
"ConfirmPassword.Required": " {0} 是必填项",
"NewPassword": "新密码",
"NewPassword.Required": " {0} 是必填项",
"Password": "密码",
"Password.Required": " {0} 是必填项"
},
"ThingsGateway.Admin.Application.UserSelectorOutput": {
"Account": "账号",
"OrgId": "机构"
},
"ThingsGateway.Admin.Application.VerificatInfo": {
"Device": "登录设备",
"Expire": "过期时间(分)",
"LoginIp": "登录IP",
"LoginTime": "登录时间",
"Online": "在线状态",
"VerificatTimeout": "超时时间"
},
"ThingsGateway.Admin.Application.WebsitePolicy": {
"CloseTip": "关闭提示",
"CloseTip.Required": " {0} 是必填项",
"WebStatus": "是否开放"
},
"ThingsGateway.Admin.Application.WorkbenchInfo": {
"Razor": "主页",
"Shortcuts": "快捷方式"
}
}
}

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using SqlSugar;
using System.Collections.Concurrent;
using System.Reflection;
@@ -17,7 +15,6 @@ using ThingsGateway.Extension;
using ThingsGateway.FriendlyException;
using ThingsGateway.Logging;
using ThingsGateway.NewLife.Json.Extension;
using ThingsGateway.Razor;
namespace ThingsGateway.Admin.Application;
@@ -144,7 +141,7 @@ public class DatabaseLoggingWriter : IDatabaseLoggingWriter
if (flush)
{
SqlSugarClient ??= DbContext.Db.GetConnectionScopeWithAttr<SysOperateLog>().CopyNew();
SqlSugarClient ??= DbContext.GetDB<SysOperateLog>();
await SqlSugarClient.InsertableWithAttr(_operateLogMessageQueue.ToListWithDequeue()).ExecuteCommandAsync().ConfigureAwait(false);//入库
return true;
}
@@ -203,7 +200,7 @@ public class DatabaseLoggingWriter : IDatabaseLoggingWriter
if (flush)
{
SqlSugarClient ??= DbContext.Db.GetConnectionScopeWithAttr<SysOperateLog>().CopyNew();
SqlSugarClient ??= DbContext.GetDB<SysOperateLog>();
await SqlSugarClient.InsertableWithAttr(_operateLogMessageQueue.ToListWithDequeue()).ExecuteCommandAsync().ConfigureAwait(false);//入库
return true;
}

View File

@@ -0,0 +1,31 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using Riok.Mapperly.Abstractions;
namespace ThingsGateway.Admin.Application;
[Mapper(UseDeepCloning = true, EnumMappingStrategy = EnumMappingStrategy.ByName, RequiredMappingStrategy = RequiredMappingStrategy.None)]
public static partial class AdminMapper
{
public static partial LoginInput AdaptLoginInput(this OpenApiLoginInput src);
public static partial OpenApiLoginOutput AdaptOpenApiLoginOutput(this LoginOutput src);
public static partial SessionOutput AdaptSessionOutput(this SysUser src);
public static partial SysUser AdaptSysUser(this SysUser src);
public static partial UserSelectorOutput AdaptUserSelectorOutput(this SysUser src);
public static partial List<SysResource> AdaptListSysResource(this IEnumerable<SysResource> src);
public static partial AppConfig AdaptAppConfig(this AppConfig src);
public static partial WorkbenchInfo AdaptWorkbenchInfo(this WorkbenchInfo src);
public static partial QueryData<UserSelectorOutput> AdaptQueryDataUserSelectorOutput(this QueryData<SysUser> src);
public static partial LoginInput AdaptLoginInput(this LoginInput src);
}

View File

@@ -1,18 +1,13 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Collections.Concurrent;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using ThingsGateway.Extension;
@@ -50,7 +45,7 @@ public class AdminOAuthHandler<TOptions>(
/// </summary>
private static async Task Insertable()
{
var db = DbContext.Db.GetConnectionScopeWithAttr<SysOperateLog>().CopyNew();
var db = DbContext.GetDB<SysOperateLog>();
var appLifetime = App.RootServices!.GetService<IHostApplicationLifetime>()!;
while (!appLifetime.ApplicationStopping.IsCancellationRequested)
{
@@ -80,6 +75,7 @@ public class AdminOAuthHandler<TOptions>(
AuthenticationProperties properties,
OAuthTokenResponse tokens)
{
Backchannel.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", tokens.AccessToken);
properties.RedirectUri = Options.HomePath;
properties.IsPersistent = true;
var appConfig = await configService.GetAppConfigAsync().ConfigureAwait(false);
@@ -90,7 +86,7 @@ public class AdminOAuthHandler<TOptions>(
properties.ExpiresUtc = TimeProvider.System.GetUtcNow().AddSeconds(result);
expire = (int)(result / 60.0);
}
var user = await HandleUserInfoAsync(tokens).ConfigureAwait(false);
var user = await Options.HandleUserInfoAsync(Context, tokens).ConfigureAwait(false);
var loginEvent = await GetLogin(expire).ConfigureAwait(false);
await UpdateUser(loginEvent).ConfigureAwait(false);
@@ -148,43 +144,8 @@ public class AdminOAuthHandler<TOptions>(
}
/// <summary>处理用户信息方法</summary>
protected virtual async Task<JsonElement> HandleUserInfoAsync(OAuthTokenResponse tokens)
{
var request = new HttpRequestMessage(HttpMethod.Get, BuildUserInfoUrl(tokens));
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 JsonDocument.Parse(content).RootElement;
}
throw new OAuthTokenException($"OAuth user info endpoint failure: {await Display(response).ConfigureAwait(false)}");
}
/// <summary>生成用户信息请求地址方法</summary>
protected virtual string BuildUserInfoUrl(OAuthTokenResponse tokens)
{
return QueryHelpers.AddQueryString(Options.UserInformationEndpoint, new Dictionary<string, string>
{
{ "access_token", tokens.AccessToken }
});
}
/// <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();
}
private async Task<LoginEvent> GetLogin(int expire)
{
@@ -247,7 +208,7 @@ public class AdminOAuthHandler<TOptions>(
#endregion ,
using var db = DbContext.Db.GetConnectionScopeWithAttr<SysUser>().CopyNew();
using var db = DbContext.GetDB<SysUser>();
//更新用户登录信息
if (await db.Updateable(sysUser).UpdateColumns(it => new
{

View File

@@ -0,0 +1,87 @@
using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.WebUtilities;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
namespace ThingsGateway.Admin.Application;
/// <summary>OAuthOptions 配置类</summary>
public abstract class AdminOAuthOptions : OAuthOptions
{
/// <summary>默认构造函数</summary>
protected AdminOAuthOptions()
{
ConfigureClaims();
this.Events.OnRemoteFailure = context =>
{
var redirectUri = string.IsNullOrEmpty(HomePath) ? "/" : HomePath;
context.Response.Redirect(redirectUri);
context.HandleResponse();
return Task.CompletedTask;
};
Backchannel = new HttpClient(new HttpClientHandler
{
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
});
Backchannel.DefaultRequestHeaders.UserAgent.Add(
new ProductInfoHeaderValue("ThingsGateway", "1.0"));
}
/// <summary>配置 Claims 映射</summary>
protected virtual void ConfigureClaims()
{
}
public virtual string GetName(JsonElement element)
{
JsonElement.ObjectEnumerator target = element.EnumerateObject();
return target.TryGetValue("name");
}
/// <summary>获得/设置 登陆后首页</summary>
public string HomePath { get; set; } = "/";
/// <summary>处理用户信息方法</summary>
public virtual async Task<JsonElement> HandleUserInfoAsync(HttpContext context, OAuthTokenResponse tokens)
{
var request = new HttpRequestMessage(HttpMethod.Get, BuildUserInfoUrl(tokens));
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 JsonDocument.Parse(content).RootElement;
}
throw new OAuthTokenException($"OAuth user info endpoint failure: {await Display(response).ConfigureAwait(false)}");
}
/// <summary>生成用户信息请求地址方法</summary>
protected virtual string BuildUserInfoUrl(OAuthTokenResponse tokens)
{
return QueryHelpers.AddQueryString(UserInformationEndpoint, new Dictionary<string, string>
{
{ "access_token", tokens.AccessToken }
});
}
/// <summary>生成错误信息方法</summary>
protected 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();
}
}

View File

@@ -3,16 +3,20 @@ using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.WebUtilities;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using ThingsGateway.NewLife.Log;
namespace ThingsGateway.Admin.Application;
public class GiteeOAuthOptions : AdminOAuthOptions
{
INoticeService _noticeService;
IVerificatInfoService _verificatInfoService;
public GiteeOAuthOptions() : base()
{
_noticeService = App.GetService<INoticeService>();
_verificatInfoService = App.GetService<IVerificatInfoService>();
this.SignInScheme = ClaimConst.Scheme;
this.AuthorizationEndpoint = "https://gitee.com/oauth/authorize";
this.TokenEndpoint = "https://gitee.com/oauth/token";
@@ -29,11 +33,14 @@ public class GiteeOAuthOptions : AdminOAuthOptions
Events.OnRedirectToAuthorizationEndpoint = context =>
{
//context.RedirectUri = context.RedirectUri.Replace("http%3A%2F%2F", "https%3A%2F%2F"); // 强制替换
context.Response.Redirect(context.RedirectUri);
return Task.CompletedTask;
};
Events.OnRemoteFailure = context =>
{
XTrace.WriteException(context.Failure);
return Task.CompletedTask;
};
}
/// <summary>刷新 Token 方法</summary>
@@ -60,16 +67,7 @@ public class GiteeOAuthOptions : AdminOAuthOptions
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)
{
@@ -77,7 +75,7 @@ public class GiteeOAuthOptions : AdminOAuthOptions
return target.TryGetValue("name");
}
private static async Task HandlerGiteeStarredUrl(OAuthCreatingTicketContext context, string repoFullName = "ThingsGateway/ThingsGateway")
private async Task HandlerGiteeStarredUrl(OAuthCreatingTicketContext context, string repoFullName = "ThingsGateway/ThingsGateway")
{
if (string.IsNullOrWhiteSpace(context.AccessToken))
throw new InvalidOperationException("Access token is missing.");
@@ -89,7 +87,7 @@ public class GiteeOAuthOptions : AdminOAuthOptions
{ "access_token", context.AccessToken }
};
var request = new HttpRequestMessage(HttpMethod.Put, QueryHelpers.AddQueryString(uri, queryString))
var request = new HttpRequestMessage(HttpMethod.Get, QueryHelpers.AddQueryString(uri, queryString))
{
Headers = { Accept = { new MediaTypeWithQualityHeaderValue("application/json") } }
};
@@ -99,7 +97,17 @@ public class GiteeOAuthOptions : AdminOAuthOptions
if (!response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new Exception($"Failed to star repository: {response.StatusCode}, {content}");
var id = context.Identity.Claims.FirstOrDefault(a => a.Type == ClaimConst.VerificatId).Value;
var verificatInfoIds = _verificatInfoService.GetOne(id.ToLong());
_ = Task.Run(async () =>
{
await Task.Delay(5000).ConfigureAwait(false);
await _noticeService.NavigationMesage(verificatInfoIds.ClientIds, "https://gitee.com/ThingsGateway/ThingsGateway", "创作不易如有帮助请star仓库").ConfigureAwait(false);
});
//throw new Exception($"Failed to star repository: {response.StatusCode}, {content}");
}

View File

@@ -0,0 +1,122 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.Http;
using System.Net.Http.Headers;
using System.Text.Json;
using ThingsGateway.NewLife.Log;
namespace ThingsGateway.Admin.Application;
public class GitHubOAuthOptions : AdminOAuthOptions
{
INoticeService _noticeService;
IVerificatInfoService _verificatInfoService;
public GitHubOAuthOptions() : base()
{
_noticeService = App.GetService<INoticeService>();
_verificatInfoService = App.GetService<IVerificatInfoService>();
SignInScheme = ClaimConst.Scheme;
AuthorizationEndpoint = "https://github.com/login/oauth/authorize";
TokenEndpoint = "https://github.com/login/oauth/access_token";
UserInformationEndpoint = "https://api.github.com/user";
HomePath = "/";
CallbackPath = "/signin-github";
Scope.Add("read:user");
Scope.Add("public_repo"); // 需要用于 Star 仓库
Events.OnCreatingTicket = async context =>
{
await HandleGitHubStarAsync(context).ConfigureAwait(false);
};
Events.OnRedirectToAuthorizationEndpoint = context =>
{
context.Response.Redirect(context.RedirectUri);
return Task.CompletedTask;
};
Events.OnRemoteFailure = context =>
{
XTrace.WriteException(context.Failure);
return Task.CompletedTask;
};
}
protected override void ConfigureClaims()
{
ClaimActions.MapJsonKey(ClaimConst.AvatarUrl, "avatar_url");
ClaimActions.MapJsonKey(ClaimConst.Account, "login");
base.ConfigureClaims();
}
public override string GetName(JsonElement element)
{
if (element.TryGetProperty("login", out var loginProp))
{
return loginProp.GetString() ?? string.Empty;
}
return string.Empty;
}
private async Task HandleGitHubStarAsync(OAuthCreatingTicketContext context, string repoFullName = "ThingsGateway/ThingsGateway")
{
if (string.IsNullOrWhiteSpace(context.AccessToken))
throw new InvalidOperationException("Access token is missing.");
var request = new HttpRequestMessage(HttpMethod.Put, $"https://api.github.com/user/starred/{repoFullName}")
{
Headers =
{
Accept = { new MediaTypeWithQualityHeaderValue("application/vnd.github+json") },
Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken),
},
Content = new StringContent(string.Empty) // GitHub Star 接口需要空内容
};
request.Headers.UserAgent.Add(new ProductInfoHeaderValue("ThingsGateway", "1.0")); // GitHub API 要求 User-Agent
var response = await context.Backchannel.SendAsync(request, context.HttpContext.RequestAborted).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var id = context.Identity.Claims.FirstOrDefault(a => a.Type == ClaimConst.VerificatId).Value;
var verificatInfoIds = _verificatInfoService.GetOne(id.ToLong());
_ = Task.Run(async () =>
{
await Task.Delay(5000).ConfigureAwait(false);
await _noticeService.NavigationMesage(verificatInfoIds.ClientIds, "https://github.com/ThingsGateway/ThingsGateway", "创作不易如有帮助请star仓库").ConfigureAwait(false);
});
}
}
/// <summary>处理用户信息方法</summary>
public override async Task<JsonElement> HandleUserInfoAsync(HttpContext context, OAuthTokenResponse tokens)
{
var request = new HttpRequestMessage(HttpMethod.Get, UserInformationEndpoint);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.github+json"));
request.Headers.UserAgent.Add(new ProductInfoHeaderValue("ThingsGateway", "1.0")); // GitHub API 要求 User-Agent
var response = await Backchannel.SendAsync(request, context.RequestAborted).ConfigureAwait(false);
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
return JsonDocument.Parse(content).RootElement;
}
throw new OAuthTokenException($"OAuth user info endpoint failure: {await Display(response).ConfigureAwait(false)}");
}
}

View File

@@ -0,0 +1,6 @@
namespace ThingsGateway.Admin.Application;
public class GithubOAuthSettings : GiteeOAuthSettings
{
}

View File

@@ -0,0 +1,11 @@
using System.Text.Json;
namespace ThingsGateway.Admin.Application;
public static class OAuthUserExtensions
{
public static string TryGetValue(this JsonElement.ObjectEnumerator target, string propertyName)
{
return target.FirstOrDefault<JsonProperty>((Func<JsonProperty, bool>)(t => t.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase))).Value.ToString() ?? string.Empty;
}
}

View File

@@ -10,7 +10,6 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Http;
using System.Security.Claims;
@@ -116,7 +115,7 @@ public class BlazorAuthenticationHandler : AppAuthorizeHandler
{
// 路由名称
var routeName = routeData.PageType.CustomAttributes.FirstOrDefault(x =>
x.AttributeType == typeof(RouteAttribute))?.ConstructorArguments?[0].Value as string;
x.AttributeType == typeof(Microsoft.AspNetCore.Components.RouteAttribute))?.ConstructorArguments?[0].Value as string;
if (routeName == null) return true;
if ((!user.PermissionCodeList.Contains(routeName.CutStart("/")) && !user.PermissionCodeList.Contains(routeName))) //如果当前路由信息不包含在角色授权路由列表中则认证失败

View File

@@ -11,11 +11,9 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Localization;
using ThingsGateway.DataValidation;
using ThingsGateway.FriendlyException;
using ThingsGateway.Razor;
using ThingsGateway.UnifyResult;
namespace ThingsGateway.Admin.Application;

View File

@@ -18,7 +18,7 @@ public class SysRelationSeedData : ISqlSugarEntitySeedData<SysRelation>
/// <inheritdoc/>
public IEnumerable<SysRelation> SeedData()
{
var db = DbContext.Db.GetConnectionScopeWithAttr<SysRelation>().CopyNew();
using var db = DbContext.GetDB<SysRelation>();
if (db.Queryable<SysRelation>().Any(a => a.ObjectId == RoleConst.SuperAdminId))
return Enumerable.Empty<SysRelation>();
var data = SeedDataUtil.GetSeedData<SysRelation>(PathExtensions.CombinePathWithOs("SeedData", "Admin", "seed_sys_relation.json"));

View File

@@ -9,19 +9,15 @@
// ------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.Options;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Globalization;
using System.Reflection;
using ThingsGateway.Extension;
using ThingsGateway.Common.Extension;
namespace ThingsGateway.Admin.Application;

View File

@@ -10,7 +10,6 @@
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Localization;
using System.Security.Claims;
@@ -324,7 +323,7 @@ public class AuthService : IAuthService
#endregion ,
using var db = DbContext.Db.GetConnectionScopeWithAttr<SysUser>().CopyNew();
using var db = DbContext.GetDB<SysUser>();
//更新用户登录信息
if (await db.Updateable(sysUser).UpdateColumns(it => new
{

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
namespace ThingsGateway.Admin.Application;
/// <summary>

View File

@@ -8,11 +8,8 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using ThingsGateway.FriendlyException;
using ThingsGateway.NewLife.Json.Extension;
using ThingsGateway.Razor;
namespace ThingsGateway.Admin.Application;

View File

@@ -10,6 +10,8 @@
using System.Collections.Concurrent;
using ThingsGateway.NewLife.DictionaryExtensions;
namespace ThingsGateway.Admin.Application;
/// <summary>

View File

@@ -10,7 +10,6 @@
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
using System.Text;
using System.Web;

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using ThingsGateway.Extension.Generic;
namespace ThingsGateway.Admin.Application;

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
namespace ThingsGateway.Admin.Application;
/// <summary>

View File

@@ -8,10 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using SqlSugar;
using System.Data;
namespace ThingsGateway.Admin.Application;

View File

@@ -8,10 +8,6 @@
// QQ群605534569
// ------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using SqlSugar;
namespace ThingsGateway.Admin.Application;
/// <summary>

View File

@@ -8,10 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using SqlSugar;
using ThingsGateway.Extension.Generic;
using ThingsGateway.FriendlyException;

View File

@@ -8,10 +8,6 @@
// QQ群605534569
// ------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using SqlSugar;
namespace ThingsGateway.Admin.Application;
/// <summary>

View File

@@ -8,10 +8,6 @@
// QQ群605534569
// ------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using SqlSugar;
using ThingsGateway.FriendlyException;
namespace ThingsGateway.Admin.Application;

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using System.ComponentModel.DataAnnotations;
using ThingsGateway.Extension.Generic;

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
namespace ThingsGateway.Admin.Application;
/// <summary>

View File

@@ -8,14 +8,8 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using Microsoft.Extensions.DependencyInjection;
using SqlSugar;
using System.Globalization;
using ThingsGateway.FriendlyException;
namespace ThingsGateway.Admin.Application;
@@ -24,7 +18,7 @@ internal sealed class SysResourceService : BaseService<SysResource>, ISysResourc
{
private readonly IRelationService _relationService;
private string CacheKey = $"{CacheConst.Cache_SysResource}-{CultureInfo.CurrentUICulture.Name}";
private string CacheKey = $"{CacheConst.Cache_SysResource}";
public SysResourceService(IRelationService relationService)
{
@@ -33,7 +27,6 @@ internal sealed class SysResourceService : BaseService<SysResource>, ISysResourc
#region
[OperDesc("CopyResource")]
public async Task CopyAsync(IEnumerable<long> ids, long moduleId)
{
@@ -144,12 +137,12 @@ internal sealed class SysResourceService : BaseService<SysResource>, ISysResourc
/// <returns>全部资源列表</returns>
public async Task<List<SysResource>> GetAllAsync()
{
var sysResources = App.CacheService.Get<List<SysResource>>(CacheKey);
var sysResources = App.CacheService.Get<List<SysResource>>(CacheConst.Cache_SysResource);
if (sysResources == null)
{
using var db = GetDB();
sysResources = await db.Queryable<SysResource>().ToListAsync().ConfigureAwait(false);
App.CacheService.Set(CacheKey, sysResources);
App.CacheService.Set(CacheConst.Cache_SysResource, sysResources);
}
return sysResources;
}
@@ -259,7 +252,7 @@ internal sealed class SysResourceService : BaseService<SysResource>, ISysResourc
/// </summary>
public void RefreshCache()
{
App.CacheService.Remove(CacheKey);
App.CacheService.Remove(CacheConst.Cache_SysResource);
//删除超级管理员的缓存
App.RootServices.GetRequiredService<ISysUserService>().DeleteUserFromCache(RoleConst.SuperAdminId);
}

View File

@@ -8,10 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using SqlSugar;
namespace ThingsGateway.Admin.Application;
/// <summary>

View File

@@ -8,10 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using SqlSugar;
using ThingsGateway.FriendlyException;
using ThingsGateway.NewLife.Json.Extension;

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
namespace ThingsGateway.Admin.Application;
/// <summary>

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
namespace ThingsGateway.Admin.Application;
public interface ISessionService

View File

@@ -8,12 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using Mapster;
using SqlSugar;
namespace ThingsGateway.Admin.Application;
internal sealed class SessionService : BaseService<SysUser>, ISessionService
@@ -69,7 +63,7 @@ internal sealed class SessionService : BaseService<SysUser>, ISessionService
var r = items.Select((it) =>
{
var reuslt = it.Adapt<SessionOutput>();
var reuslt = it.AdaptSessionOutput();
if (verificatInfoDicts.TryGetValue(it.Id, out var verificatInfos))
{
reuslt.VerificatCount = verificatInfos.Count;//令牌数量
@@ -94,7 +88,7 @@ internal sealed class SessionService : BaseService<SysUser>, ISessionService
var r = items.Select((it) =>
{
var reuslt = it.Adapt<SessionOutput>();
var reuslt = it.AdaptSessionOutput();
if (verificatInfoDicts.TryGetValue(it.Id, out var verificatInfos))
{
reuslt.VerificatCount = verificatInfos.Count;//令牌数量
@@ -117,7 +111,7 @@ internal sealed class SessionService : BaseService<SysUser>, ISessionService
var r = items.Select((it) =>
{
var reuslt = it.Adapt<SessionOutput>();
var reuslt = it.AdaptSessionOutput();
if (verificatInfoDicts.TryGetValue(it.Id, out var verificatInfos))
{
reuslt.VerificatCount = verificatInfos.Count;//令牌数量

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
namespace ThingsGateway.Admin.Application;
/// <summary>

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
namespace ThingsGateway.Admin.Application;
/// <summary>

View File

@@ -8,14 +8,9 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using Mapster;
using SqlSugar;
using ThingsGateway.Common.Extension;
using ThingsGateway.Common.Extension.Generic;
using ThingsGateway.DataEncryption;
using ThingsGateway.Extension;
using ThingsGateway.Extension.Generic;
using ThingsGateway.FriendlyException;
using ThingsGateway.NewLife.Json.Extension;
@@ -453,7 +448,7 @@ internal sealed class SysUserService : BaseService<SysUser>, ISysUserService
if (changedType == ItemChangedType.Add)
{
var sysUser = input.Adapt<SysUser>();
var sysUser = input.AdaptSysUser();
//获取默认密码
sysUser.Avatar = input.Avatar;
sysUser.Password = await GetDefaultPassWord(true).ConfigureAwait(false);//设置密码
@@ -873,7 +868,7 @@ internal sealed class SysUserService : BaseService<SysUser>, ISysUserService
sysUser.OrgAndPosIdList.AddRange(sysUser.OrgId, sysUser.PositionId ?? 0);//添加组织和职位Id
if (sysUser.DirectorId != null)
{
sysUser.DirectorInfo = (await GetUserByIdAsync(sysUser.DirectorId.Value).ConfigureAwait(false)).Adapt<UserSelectorOutput>();//获取主管信息
sysUser.DirectorInfo = (await GetUserByIdAsync(sysUser.DirectorId.Value).ConfigureAwait(false)).AdaptUserSelectorOutput();//获取主管信息
}
//获取按钮码

View File

@@ -8,16 +8,13 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using System.Text.RegularExpressions;
using ThingsGateway.Common.Extension;
using ThingsGateway.DataEncryption;
using ThingsGateway.Extension;
using ThingsGateway.Extension.Generic;
using ThingsGateway.FriendlyException;
using ThingsGateway.NewLife.Json.Extension;
using ThingsGateway.Razor;
namespace ThingsGateway.Admin.Application;

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using SqlSugar;
namespace ThingsGateway.Admin.Application;
/// <summary>
@@ -37,6 +35,8 @@ internal sealed class VerificatInfoService : BaseService<VerificatInfo>, IVerifi
private VerificatInfo? GetFromDb(long id)
{
if (id == 0)
return null;
using var db = GetDB();
var verificatInfo = db.Queryable<VerificatInfo>().First(u => u.Id == id);
if (verificatInfo != null)

View File

@@ -8,13 +8,13 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.Reflection;
using System.Text;
using ThingsGateway.Extension;
using ThingsGateway.UnifyResult;
namespace ThingsGateway.Admin.Application;
@@ -41,7 +41,6 @@ public class Startup : AppStartup
services.AddSingleton<IVerificatInfoService, VerificatInfoService>();
services.AddScoped<IAuthRazorService, AuthRazorService>();
services.AddSingleton<IApiPermissionService, ApiPermissionService>();
services.AddSingleton<IFileService, FileService>();
services.AddSingleton<IImportExportService, ImportExportService>();
@@ -65,10 +64,77 @@ public class Startup : AppStartup
services.AddSingleton(typeof(IEventService<>), typeof(EventService<>));
#region
services.AddConsoleFormatter(options =>
{
options.WriteFilter = (logMsg) =>
{
if (App.HostApplicationLifetime.ApplicationStopping.IsCancellationRequested && logMsg.LogLevel >= LogLevel.Warning) return false;
if (string.IsNullOrEmpty(logMsg.Message)) return false;
else return true;
};
options.MessageFormat = (logMsg) =>
{
//如果不是LoggingMonitor日志才格式化
if (logMsg.LogName != "System.Logging.LoggingMonitor")
{
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine("【日志级别】:" + logMsg.LogLevel);
stringBuilder.AppendLine("【日志类名】:" + logMsg.LogName);
stringBuilder.AppendLine("【日志时间】:" + DateTime.Now.ToDefaultDateTimeFormat());
stringBuilder.AppendLine("【日志内容】:" + logMsg.Message);
if (logMsg.Exception != null)
{
stringBuilder.AppendLine("【异常信息】:" + logMsg.Exception);
}
return stringBuilder.ToString();
}
else
{
return logMsg.Message;
}
};
options.WriteHandler = (logMsg, scopeProvider, writer, fmtMsg, opt) =>
{
ConsoleColor consoleColor = ConsoleColor.White;
switch (logMsg.LogLevel)
{
case LogLevel.Information:
consoleColor = ConsoleColor.DarkGreen;
break;
case LogLevel.Warning:
consoleColor = ConsoleColor.DarkYellow;
break;
case LogLevel.Error:
consoleColor = ConsoleColor.DarkRed;
break;
}
writer.WriteWithColor(fmtMsg, ConsoleColor.Black, consoleColor);
};
});
#endregion
//日志写入数据库配置
services.AddDatabaseLogging<DatabaseLoggingWriter>(options =>
{
options.NameFilter = (name) =>
{
return (
name == "System.Logging.RequestAudit"
);
};
});
}
public void Use(IApplicationBuilder applicationBuilder)
public void Use(IServiceProvider serviceProvider)
{
NewLife.Log.XTrace.UnhandledExceptionLogEnable = () => !App.HostApplicationLifetime.ApplicationStopping.IsCancellationRequested;
//检查ConfigId
var configIdGroup = DbContext.DbConfigs.GroupBy(it => it.ConfigId);
foreach (var configId in configIdGroup)
@@ -79,7 +145,7 @@ public class Startup : AppStartup
//遍历配置
DbContext.DbConfigs?.ForEach(it =>
{
var connection = DbContext.Db.GetConnection(it.ConfigId);//获取数据库连接对象
var connection = DbContext.GetDB().GetConnection(it.ConfigId);//获取数据库连接对象
if (it.InitDatabase == true)
connection.DbMaintenance.CreateDatabase();//创建数据库,如果存在则不创建
});

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="$(SolutionDir)Version.props" />
<Import Project="$(SolutionDir)PackNuget.props" />
<Import Project="..\..\Version.props" />
<Import Project="..\..\PackNuget.props" />
<PropertyGroup>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
@@ -18,7 +18,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Rougamo.Fody" Version="5.0.0" />
<PackageReference Include="Riok.Mapperly" Version="4.2.1" ExcludeAssets="runtime" PrivateAssets="all" />
<PackageReference Include="Rougamo.Fody" Version="5.0.1" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" />
@@ -27,9 +28,9 @@
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.5" />
<PackageReference Include="System.Formats.Asn1" Version="9.0.5" />
<PackageReference Include="System.Threading.RateLimiting" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(NET9Version)" />
<PackageReference Include="System.Formats.Asn1" Version="$(NET9Version)" />
<PackageReference Include="System.Threading.RateLimiting" Version="$(NET9Version)" />
</ItemGroup>
<ItemGroup>
<Content Remove="SeedData\Admin\*.json" />
@@ -41,13 +42,20 @@
<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="\" />
<None Remove="..\..\..\README.md" Pack="false" PackagePath="\" />
<None Remove="..\..\..\README.zh-CN.md" Pack="false" PackagePath="\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ThingsGateway.Razor\ThingsGateway.Razor.csproj" />
<ProjectReference Include="..\ThingsGateway.DB\ThingsGateway.DB.csproj" />
</ItemGroup>
<!--<Target Name="Mapster" AfterTargets="AfterBuild">
<Exec WorkingDirectory="$(ProjectDir)" Command="dotnet tool restore" />
<Exec WorkingDirectory="$(ProjectDir)" Command="dotnet mapster extension -o MapsterGenerator -a &quot;$(TargetDir)$(ProjectName).dll&quot;" />
<Exec WorkingDirectory="$(ProjectDir)" Command="dotnet mapster mapper -o MapsterGenerator -a &quot;$(TargetDir)$(ProjectName).dll&quot;" />
</Target>-->
</Project>

View File

@@ -21,7 +21,7 @@ namespace ThingsGateway.Admin.Application
Settings = new UserAgentSettings();
}
private MemoryCache MemoryCache { get; set; } = new();
private ICache MemoryCache => App.CacheService;
/// <summary>
/// Parses the specified user agent string.

View File

@@ -40,7 +40,7 @@ public static class ClearTokenUtil
public static async Task DeleteUserTokenByOrgIds(HashSet<long> orgIds)
{
// 获取用户ID列表
var userIds = await DbContext.Db.CopyNew().QueryableWithAttr<SysUser>().Where(it => orgIds.Contains(it.OrgId)).Select(it => it.Id).ToListAsync().ConfigureAwait(false);
var userIds = await DbContext.GetDB<SysUser>().Queryable<SysUser>().Where(it => orgIds.Contains(it.OrgId)).Select(it => it.Id).ToListAsync().ConfigureAwait(false);
//从redis中删除所属机构的用户token
App.CacheService.HashDel<VerificatInfo>(CacheConst.Cache_Token, userIds.Select(it => it.ToString()).ToArray());
}

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using ThingsGateway.NewLife;
namespace ThingsGateway.Admin.Application;

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
namespace ThingsGateway.Admin.Application;
/// <inheritdoc/>

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
namespace ThingsGateway.Admin.Application;
/// <inheritdoc/>

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
namespace ThingsGateway.Admin.Application;
/// <inheritdoc/>

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
namespace ThingsGateway.Admin.Application;
/// <inheritdoc/>

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
namespace ThingsGateway.Admin.Application;
/// <inheritdoc/>

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using SqlSugar;
using ThingsGateway.Extension.Generic;
namespace ThingsGateway.Admin.Application;

View File

@@ -9,7 +9,6 @@
//------------------------------------------------------------------------------
using ThingsGateway.Admin.Application;
using ThingsGateway.NewLife.Extension;
namespace ThingsGateway.Admin.Razor;

View File

@@ -5,6 +5,9 @@
@<div>
<span class="mx-3">@item.ConfirmMessage</span>
<Button Text=@Localizers["Jump"] Color="Color.Link" OnClick="()=>NavigationManager.NavigateTo(item.Uri)"></Button>
<a href=@item.Uri target="_blank">
@item.Uri
</a>
</div>;
}

View File

@@ -8,11 +8,8 @@
// QQ群605534569
//------------------------------------------------------------------------------
using Mapster;
using ThingsGateway.Admin.Application;
using ThingsGateway.NewLife;
using ThingsGateway.NewLife.Extension;
namespace ThingsGateway.Admin.Razor;
@@ -88,7 +85,7 @@ public class BlazorAppContext
if (UserManager.UserId > 0)
{
url = url.StartsWith('/') ? url : $"/{url}";
var sysResources = (await ResourceService.GetAllAsync()).Adapt<List<SysResource>>();
var sysResources = (await ResourceService.GetAllAsync()).AdaptListSysResource();
if (TitleLocalizer != null)
{
sysResources.ForEach(a =>
@@ -121,7 +118,7 @@ public class BlazorAppContext
CurrentModuleId = moduleId.Value;
}
UserWorkBench = await UserCenterService.GetLoginWorkbenchAsync(UserManager.UserId);
OwnMenus = (await UserCenterService.GetOwnMenuAsync(UserManager.UserId, 0)).Adapt<List<SysResource>>();
OwnMenus = (await UserCenterService.GetOwnMenuAsync(UserManager.UserId, 0)).AdaptListSysResource();
if (TitleLocalizer != null)
{
@@ -156,7 +153,7 @@ public class BlazorAppContext
CurrentUser = (await SysUserService.GetUserByIdAsync(UserManager.UserId))!;
}
}
TimeTick timeTick = new("50000");
TimeTick timeTick = new("60000");
/// <summary>
/// 是否拥有按钮授权
/// </summary>

View File

@@ -16,6 +16,10 @@ global using Microsoft.Extensions.Options;
global using System.Diagnostics.CodeAnalysis;
global using ThingsGateway.Common;
global using ThingsGateway.DB;
global using ThingsGateway.NewLife.Extension;
global using ThingsGateway.Razor;
global using ThingsGateway.SqlSugar;
[assembly: SuppressMessage("Reliability", "CA2007", Justification = "<挂起>", Scope = "module")]
[assembly: GlobalGenerateSetParametersAsync(true)]

View File

@@ -1,74 +1,73 @@
{
"ThingsGateway.Admin.Razor.LoginConnectionHub": {
"Jump": "Jump"
},
"ThingsGateway.Admin.Razor.UserLogin": {
"Account": "Username",
"Password": "Password",
"Login": "Login"
},
"ThingsGateway.Admin.Razor._Imports": {
"Org": "Org",
"Position": "Position",
"Role": "Role",
"RoleList": "Role List",
"PositionList": "Position List",
"OrgList": "Org List",
"MaxCount": "Exceeding the quantity limit",
"All": "All",
"Copy": "Copy",
"Choice": "Choice",
"Picture": "Picture",
"Root": "Root",
"Save": "Save",
"Copy": "Copy",
"CurrentVerificat": "CurrentVerificat",
"EmptyText": "Empty",
"SelectPlaceHolder": "Please select ...",
"ExportButtonText": "Export/Import"
"ExportButtonText": "Export/Import",
"MaxCount": "Exceeding the quantity limit",
"Org": "Org",
"OrgList": "Org List",
"Picture": "Picture",
"Position": "Position",
"PositionList": "Position List",
"Role": "Role",
"RoleList": "Role List",
"Root": "Root",
"Save": "Save",
"SelectPlaceHolder": "Please select ..."
},
"ThingsGateway.Admin.Razor.ChoiceModuleComponent": {
"SetDefaultModule": "Set as default module"
},
"ThingsGateway.Admin.Razor.HardwareInfoPage": {
"SystemInfo": "System Information",
"AvailableDisk": "Available Disk",
"AvailableMemory": "Available Memory",
"CpuUsage": "CPU Usage",
"Data": "Data",
"DateTime": "Date Time",
"DiskUsage": "Disk Usage",
"HardwareInfo": "Hardware Resources",
"HardwareInfoChart": "Hardware Resources History Chart",
"CpuUsage": "CPU Usage",
"AvailableMemory": "Available Memory",
"TotalMemory": "Total Memory",
"WorkingSet": "WorkingSet",
"MemoryUsage": "Memory",
"AvailableDisk": "Available Disk",
"TotalDisk": "Total Disk",
"DiskUsage": "Disk Usage",
"HistoryHardwareInfo": "Historical Trends",
"DateTime": "Date Time",
"Data": "Data",
"MemoryUsage": "Memory",
"OSName": "OS Name",
"OSVersion": "OS Version"
"OSVersion": "OS Version",
"SystemInfo": "System Information",
"TotalDisk": "Total Disk",
"TotalMemory": "Total Memory",
"WorkingSet": "WorkingSet"
},
"ThingsGateway.Admin.Razor.LoginConnectionHub": {
"Jump": "Jump"
},
"ThingsGateway.Admin.Razor.OperLogPage": {
"Date": "Date",
"Count": "Count",
"Operate": "Succeed",
"Date": "Date",
"Exception": "Failed",
"Login": "Login",
"Logout": "Logout",
"Operate": "Succeed",
"SysOperateLog": "Operation Log"
},
"ThingsGateway.Admin.Razor.SessionPage": {
"VerificatInfo": "Token List",
"ExitSession": "Force Logout"
"ExitSession": "Force Logout",
"VerificatInfo": "Token List"
},
"ThingsGateway.Admin.Razor.UserCenterPage": {
"UpdatePasswordInfo": "Change Password",
"UpdateUserInfo": "Update Personal Information",
"UpdateWorkbenchInfo": "Update Personal Workbench",
"UserInfo": "Personal Information",
"WorkbenchInfo": "Personal Workbench"
},
"ThingsGateway.Admin.Razor.UserLogin": {
"Account": "Username",
"Login": "Login",
"Password": "Password"
},
"ThingsGateway.Admin.Razor.VerificatListDialog": {
"ExitVerificat": "Force Logout Token"
},
"ThingsGateway.Admin.Razor.UserCenterPage": {
"UpdateUserInfo": "Update Personal Information",
"UserInfo": "Personal Information",
"UpdatePasswordInfo": "Change Password",
"UpdateWorkbenchInfo": "Update Personal Workbench",
"WorkbenchInfo": "Personal Workbench"
}
}
}

View File

@@ -1,74 +1,73 @@
{
"ThingsGateway.Admin.Razor.LoginConnectionHub": {
"Jump": "跳转"
},
"ThingsGateway.Admin.Razor.UserLogin": {
"Account": "登录账号",
"Password": "登录密码",
"Login": "登录"
},
"ThingsGateway.Admin.Razor._Imports": {
"All": "全部",
"Choice": "选择",
"Copy": "复制",
"CurrentVerificat": "当前令牌",
"EmptyText": "空",
"ExportButtonText": "导出/导入",
"MaxCount": "超过数量限制",
"Org": "机构",
"OrgList": "机构列表",
"Picture": "头像",
"Position": "岗位",
"PositionList": "岗位列表",
"Role": "角色",
"RoleList": "角色列表",
"PositionList": "岗位列表",
"OrgList": "机构列表",
"MaxCount": "超过数量限制",
"All": "全部",
"Copy": "复制",
"Choice": "选择",
"Picture": "头像",
"Root": "根目录",
"Save": "保存",
"CurrentVerificat": "当前令牌",
"SelectPlaceHolder": "请选择 ...",
"EmptyText": "空",
"ExportButtonText": "导出/导入"
"SelectPlaceHolder": "请选择 ..."
},
"ThingsGateway.Admin.Razor.ChoiceModuleComponent": {
"SetDefaultModule": "设置为默认模块"
},
"ThingsGateway.Admin.Razor.HardwareInfoPage": {
"SystemInfo": "系统信息",
"AvailableDisk": "可用磁盘",
"AvailableMemory": "可用内存",
"CpuUsage": "CPU使用率",
"Data": "数据",
"DateTime": "时间",
"DiskUsage": "磁盘使用率",
"HardwareInfo": "硬件资源",
"HardwareInfoChart": "硬件资源历史曲线",
"CpuUsage": "CPU使用率",
"AvailableMemory": "可用内存",
"TotalMemory": "总内存",
"WorkingSet": "进程内存",
"MemoryUsage": "内存",
"AvailableDisk": "可用磁盘",
"TotalDisk": "总磁盘",
"DiskUsage": "磁盘使用率",
"HistoryHardwareInfo": "历史曲线",
"DateTime": "时间",
"Data": "数据",
"MemoryUsage": "内存",
"OSName": "系统名称",
"OSVersion": "系统版本"
"OSVersion": "系统版本",
"SystemInfo": "系统信息",
"TotalDisk": "总磁盘",
"TotalMemory": "总内存",
"WorkingSet": "进程内存"
},
"ThingsGateway.Admin.Razor.LoginConnectionHub": {
"Jump": "跳转"
},
"ThingsGateway.Admin.Razor.OperLogPage": {
"Date": "日期",
"Count": "数量",
"Operate": "成功",
"Date": "日期",
"Exception": "失败",
"Login": "登录",
"Logout": "注销",
"Operate": "成功",
"SysOperateLog": "操作日志"
},
"ThingsGateway.Admin.Razor.SessionPage": {
"VerificatInfo": "令牌列表",
"ExitSession": "强退会话"
"ExitSession": "强退会话",
"VerificatInfo": "令牌列表"
},
"ThingsGateway.Admin.Razor.UserCenterPage": {
"UpdatePasswordInfo": "修改密码",
"UpdateUserInfo": "更新个人信息",
"UpdateWorkbenchInfo": "更新个人工作台",
"UserInfo": "个人信息",
"WorkbenchInfo": "个人工作台"
},
"ThingsGateway.Admin.Razor.UserLogin": {
"Account": "登录账号",
"Login": "登录",
"Password": "登录密码"
},
"ThingsGateway.Admin.Razor.VerificatListDialog": {
"ExitVerificat": "强退令牌"
},
"ThingsGateway.Admin.Razor.UserCenterPage": {
"UpdateUserInfo": "更新个人信息",
"UserInfo": "个人信息",
"UpdatePasswordInfo": "修改密码",
"UpdateWorkbenchInfo": "更新个人工作台",
"WorkbenchInfo": "个人工作台"
}
}
}

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using Mapster;
using Microsoft.AspNetCore.Components.Forms;
using ThingsGateway.Admin.Application;
@@ -30,7 +28,7 @@ public partial class AppConfigPage
protected override async Task OnParametersSetAsync()
{
AppConfig = (await SysDictService.GetAppConfigAsync()).Adapt<AppConfig>();
AppConfig = (await SysDictService.GetAppConfigAsync()).AdaptAppConfig();
await base.OnParametersSetAsync();
}

View File

@@ -12,7 +12,6 @@ using Microsoft.AspNetCore.Components.Forms;
using ThingsGateway.Admin.Application;
using ThingsGateway.Extension.Generic;
using ThingsGateway.NewLife.Extension;
namespace ThingsGateway.Admin.Razor;

View File

@@ -90,8 +90,8 @@
<h6> @((100 - (availableMemory * 100.00 / memory)).ToString("F2") + " %") </h6>
<span> @Localizer["WorkingSet"] <i> @(HardwareJob.HardwareInfo.WorkingSet + " MB")</i></span>
<span> @Localizer["AvailableMemory"] <i> @((availableMemory / 1024.00 / 1024 / 1024).ToString("F2") + " GB")</i></span>
<span> @Localizer["TotalMemory"] <i> @((memory / 1024.00 / 1024 / 1024).ToString("F2") + " GB")</i></span>
<span> @Localizer["AvailableMemory"] <i> @((availableMemory / 1024.00 / 1024).ToString("F2") + " GB")</i></span>
<span> @Localizer["TotalMemory"] <i> @((memory / 1024.00 / 1024).ToString("F2") + " GB")</i></span>
</div>
</Circle>

View File

@@ -64,6 +64,7 @@ public partial class HardwareInfoPage : IDisposable
private async Task<ChartDataSource> OnInit()
{
if (App.HostApplicationLifetime.ApplicationStopping.IsCancellationRequested) return (new ChartDataSource());
if (ChartDataSource == null)
{
var hisHardwareInfos = await HardwareJob.GetHistoryHardwareInfos();

View File

@@ -26,6 +26,7 @@ public partial class OperLogPage
private async Task<ChartDataSource> OnInit()
{
if (App.HostApplicationLifetime.ApplicationStopping.IsCancellationRequested) return (new ChartDataSource());
if (ChartDataSource == null)
{
var dayStatisticsOutputs = await SysOperateLogService.StatisticsByDayAsync(7);

View File

@@ -8,10 +8,7 @@
// QQ群605534569
//------------------------------------------------------------------------------
using SqlSugar;
using ThingsGateway.Admin.Application;
using ThingsGateway.NewLife.Extension;
namespace ThingsGateway.Admin.Razor;

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using SqlSugar;
using ThingsGateway.Admin.Application;
namespace ThingsGateway.Admin.Razor;

View File

@@ -8,10 +8,7 @@
// QQ群605534569
//------------------------------------------------------------------------------
using SqlSugar;
using ThingsGateway.Admin.Application;
using ThingsGateway.NewLife.Extension;
namespace ThingsGateway.Admin.Razor;

View File

@@ -33,7 +33,7 @@
</PopConfirmButton>
<PopConfirmButton Color=Color.Warning IsDisabled="SelectedRows.Count!=1||!AuthorizeButton(AdminOperConst.Edit)" Text=@OperDescLocalizer["ChangeParentResource"] Icon="fa fa-copy" OnConfirm="OnChangeParent">
<BodyTemplate>
<div class="min-height-500 overflow-y-auto">
<div class="overflow-y-auto" style="height:500px">
<TreeView Items="MenuTreeItems" IsVirtualize="true" OnTreeItemClick="a=>{ChangeParentId=a.Value.Id;return Task.CompletedTask;}" />
</div>
</BodyTemplate>

View File

@@ -8,10 +8,7 @@
// QQ群605534569
//------------------------------------------------------------------------------
using SqlSugar;
using ThingsGateway.Admin.Application;
using ThingsGateway.NewLife.Extension;
namespace ThingsGateway.Admin.Razor;

View File

@@ -10,7 +10,6 @@
using ThingsGateway.Admin.Application;
using ThingsGateway.Extension.Generic;
using ThingsGateway.NewLife.Extension;
namespace ThingsGateway.Admin.Razor;

View File

@@ -9,7 +9,6 @@
//------------------------------------------------------------------------------
using ThingsGateway.Admin.Application;
using ThingsGateway.NewLife.Extension;
namespace ThingsGateway.Admin.Razor;

Some files were not shown because too many files have changed in this diff Show More