mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-27 13:47:10 +08:00
Compare commits
80 Commits
10.9.64.0
...
10.11.27.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8404e20c5e | ||
|
|
662aa162e9 | ||
|
|
5927738c32 | ||
|
|
3c9f97a5c3 | ||
|
|
179ca0aa0e | ||
|
|
fc09a52da1 | ||
|
|
5436b91c89 | ||
|
|
d1c46f51a6 | ||
|
|
4539d8d198 | ||
|
|
9ea9529a5f | ||
|
|
4e6be23aac | ||
|
|
2fabbd236b | ||
|
|
163a66530e | ||
|
|
29073a00c4 | ||
|
|
c6d4d1ecfa | ||
|
|
ba16889cad | ||
|
|
5aaed35b0f | ||
|
|
df067c91eb | ||
|
|
2078b4a60b | ||
|
|
20a2e3ff8e | ||
|
|
61a973b1b5 | ||
|
|
cbd72e2081 | ||
|
|
4e0377b20c | ||
|
|
fd318d3cdc | ||
|
|
515bdb9700 | ||
|
|
46c1780017 | ||
|
|
fe78a4c3ca | ||
|
|
2d7effadf9 | ||
|
|
346c560f8b | ||
|
|
8e3bd89f61 | ||
|
|
6da142d080 | ||
|
|
ff7d029e6f | ||
|
|
21b4695683 | ||
|
|
02ad494a26 | ||
|
|
280366e1b2 | ||
|
|
6660ce3e34 | ||
|
|
7499162c1a | ||
|
|
40208a5cd6 | ||
|
|
fa347f4f68 | ||
|
|
d7df6fc605 | ||
|
|
eb4bb2fd48 | ||
|
|
faa9858974 | ||
|
|
1b3d2dda49 | ||
|
|
a8a9453611 | ||
|
|
e84f42ce14 | ||
|
|
6f814cf6b8 | ||
|
|
e36432e4e9 | ||
|
|
ebd71e807b | ||
|
|
34000d8d7d | ||
|
|
e785f6660c | ||
|
|
831c611797 | ||
|
|
453817ef86 | ||
|
|
8ce0b981c1 | ||
|
|
4e5c51b54c | ||
|
|
3cc9d31f28 | ||
|
|
10391f869b | ||
|
|
fba0723a6d | ||
|
|
2db3f78f0c | ||
|
|
badf61fe01 | ||
|
|
d74e0952dc | ||
|
|
fb1699ce80 | ||
|
|
44adddbcd4 | ||
|
|
0eab889452 | ||
|
|
e14d39a459 | ||
|
|
7575264ede | ||
|
|
3e1a077b96 | ||
|
|
a921cb8400 | ||
|
|
4de7c31ed7 | ||
|
|
08326a2cfd | ||
|
|
e045de5acb | ||
|
|
d3bef31aa6 | ||
|
|
347d4d6e5d | ||
|
|
b006a066e1 | ||
|
|
e331bc5d3c | ||
|
|
2e81806231 | ||
|
|
37b48cf221 | ||
|
|
5997487434 | ||
|
|
4ce843182f | ||
|
|
a4aa000cf0 | ||
|
|
01aa6ca066 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -364,6 +364,8 @@ FodyWeavers.xsd
|
|||||||
|
|
||||||
/src/*Pro*/
|
/src/*Pro*/
|
||||||
/src/*Pro*
|
/src/*Pro*
|
||||||
|
/src/**/*Pro*
|
||||||
/src/*pro*
|
/src/*pro*
|
||||||
/src/*pro*/
|
/src/*pro*/
|
||||||
/src/ThingsGateway.Server/Configuration/GiteeOAuthSettings.json
|
/src/ThingsGateway.Server/Configuration/GiteeOAuthSettings.json
|
||||||
|
/src/.idea/
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
<IncludeBuildOutput>false</IncludeBuildOutput>
|
<IncludeBuildOutput>false</IncludeBuildOutput>
|
||||||
<!-- 避免 DLL 被打包到 lib/ -->
|
<!-- 避免 DLL 被打包到 lib/ -->
|
||||||
<EnableSourceGenerator>true</EnableSourceGenerator>
|
<EnableSourceGenerator>true</EnableSourceGenerator>
|
||||||
|
|
||||||
<!-- 可选 -->
|
<!-- 可选 -->
|
||||||
|
|
||||||
|
|
||||||
@@ -26,6 +27,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.13.0" PrivateAssets="all" Private="false" />
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" PrivateAssets="all" Private="false" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ using Microsoft.AspNetCore.Components;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
#pragma warning disable CA1849
|
||||||
#pragma warning disable CA2007
|
#pragma warning disable CA2007
|
||||||
#pragma warning disable CS0162
|
#pragma warning disable CS0162
|
||||||
#pragma warning disable CS8632
|
#pragma warning disable CS8632
|
||||||
@@ -276,6 +277,7 @@ namespace {namespaceName}
|
|||||||
#pragma warning restore CS8632
|
#pragma warning restore CS8632
|
||||||
#pragma warning restore CS0162
|
#pragma warning restore CS0162
|
||||||
#pragma warning restore CA2007
|
#pragma warning restore CA2007
|
||||||
|
#pragma warning restore CA1849
|
||||||
");
|
");
|
||||||
var bases = class_symbol.GetTypeHierarchy().Where(t => !SymbolEqualityComparer.Default.Equals(t, class_symbol));
|
var bases = class_symbol.GetTypeHierarchy().Where(t => !SymbolEqualityComparer.Default.Equals(t, class_symbol));
|
||||||
var members = class_symbol.GetMembers() // members of the type itself
|
var members = class_symbol.GetMembers() // members of the type itself
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
<div align="center"><h1 align="center">ThingsBlazor</a></h1></div>
|
<div align="center"><h1 align="center">ThingsBlazor</h1></div>
|
||||||
<div align="center"><h3 align="center">权限管理框架</h3></div>
|
<div align="center"><h3 align="center">权限管理框架</h3></div>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -38,9 +38,9 @@ public sealed class OperDescAttribute : MoAttribute
|
|||||||
|
|
||||||
static OperDescAttribute()
|
static OperDescAttribute()
|
||||||
{
|
{
|
||||||
// 创建长时间运行的后台任务,并将日志消息队列中数据写入存储中
|
|
||||||
Task.Factory.StartNew(ProcessQueue, TaskCreationOptions.LongRunning);
|
|
||||||
AppService = App.RootServices.GetService<IAppService>();
|
AppService = App.RootServices.GetService<IAppService>();
|
||||||
|
// 创建长时间运行的后台任务,并将日志消息队列中数据写入存储中
|
||||||
|
Task.Factory.StartNew(ProcessQueueAsync, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
public OperDescAttribute(string description, bool isRecordPar = true, object localizerType = null)
|
public OperDescAttribute(string description, bool isRecordPar = true, object localizerType = null)
|
||||||
@@ -93,7 +93,7 @@ public sealed class OperDescAttribute : MoAttribute
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 将日志消息写入数据库中
|
/// 将日志消息写入数据库中
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static async Task ProcessQueue()
|
private static async Task ProcessQueueAsync()
|
||||||
{
|
{
|
||||||
var appLifetime = App.RootServices!.GetService<IHostApplicationLifetime>()!;
|
var appLifetime = App.RootServices!.GetService<IHostApplicationLifetime>()!;
|
||||||
while (!appLifetime.ApplicationStopping.IsCancellationRequested)
|
while (!appLifetime.ApplicationStopping.IsCancellationRequested)
|
||||||
|
|||||||
@@ -34,17 +34,20 @@ public class FileController : ControllerBase
|
|||||||
return BadRequest("Invalid file name.");
|
return BadRequest("Invalid file name.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", fileName);
|
var root = Directory.GetCurrentDirectory();
|
||||||
|
var wwwroot = Path.Combine(root, "wwwroot");
|
||||||
if (!System.IO.File.Exists(filePath))
|
var filePath = Path.Combine(wwwroot, fileName);
|
||||||
|
#pragma warning disable CA3003
|
||||||
|
if ((!(fileName.StartsWith(@"../Logs") || fileName.StartsWith(@"..\Logs")) && filePath.Contains("..")) || !System.IO.File.Exists(filePath))
|
||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
#pragma warning restore CA3003
|
||||||
|
|
||||||
var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||||
|
|
||||||
Response.Headers.Append("Access-Control-Expose-Headers", "Content-Disposition");
|
Response.Headers.Append("Access-Control-Expose-Headers", "Content-Disposition");
|
||||||
|
|
||||||
return File(fileStream, "application/octet-stream", (fileName.Replace('/', '_')));
|
return File(fileStream, "application/octet-stream", (Path.GetFileName(filePath).Replace('/', '_')));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ public class VerificatInfo : PrimaryIdEntity
|
|||||||
[AutoGenerateColumn(Filterable = true, Sortable = true)]
|
[AutoGenerateColumn(Filterable = true, Sortable = true)]
|
||||||
[SugarColumn(ColumnDescription = "Id", IsPrimaryKey = true)]
|
[SugarColumn(ColumnDescription = "Id", IsPrimaryKey = true)]
|
||||||
[IgnoreExcel]
|
[IgnoreExcel]
|
||||||
|
[System.ComponentModel.DataAnnotations.Key]
|
||||||
public override long Id { get; set; }
|
public override long Id { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -21,16 +21,7 @@
|
|||||||
"UserNoModule": "This account has not been assigned a module. Please contact the administrator",
|
"UserNoModule": "This account has not been assigned a module. Please contact the administrator",
|
||||||
"UserNull": "User {0} does not exist"
|
"UserNull": "User {0} does not exist"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Admin.Application.BaseDataEntity": {
|
|
||||||
"CreateOrgId": "CreateOrgId"
|
|
||||||
},
|
|
||||||
"ThingsGateway.Admin.Application.BaseEntity": {
|
|
||||||
"CreateTime": "CreateTime",
|
|
||||||
"CreateUser": "CreateUser",
|
|
||||||
"SortCode": "SortCode",
|
|
||||||
"UpdateTime": "UpdateTime",
|
|
||||||
"UpdateUser": "UpdateUser"
|
|
||||||
},
|
|
||||||
"ThingsGateway.Admin.Application.BlazorAuthenticationHandler": {
|
"ThingsGateway.Admin.Application.BlazorAuthenticationHandler": {
|
||||||
"UserExpire": "User expired, please login again"
|
"UserExpire": "User expired, please login again"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -21,16 +21,7 @@
|
|||||||
"UserNoModule": "该账号未分配模块,请联系管理员",
|
"UserNoModule": "该账号未分配模块,请联系管理员",
|
||||||
"UserNull": "用户 {0} 不存在"
|
"UserNull": "用户 {0} 不存在"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Admin.Application.BaseDataEntity": {
|
|
||||||
"CreateOrgId": "创建机构Id"
|
|
||||||
},
|
|
||||||
"ThingsGateway.Admin.Application.BaseEntity": {
|
|
||||||
"CreateTime": "创建时间",
|
|
||||||
"CreateUser": "创建人",
|
|
||||||
"SortCode": "排序",
|
|
||||||
"UpdateTime": "更新时间",
|
|
||||||
"UpdateUser": "更新人"
|
|
||||||
},
|
|
||||||
"ThingsGateway.Admin.Application.BlazorAuthenticationHandler": {
|
"ThingsGateway.Admin.Application.BlazorAuthenticationHandler": {
|
||||||
"UserExpire": "用户登录已过期,请重新登录"
|
"UserExpire": "用户登录已过期,请重新登录"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ThingsGateway.Gateway.Application;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
public class USheetDatas
|
public class USheetDatas
|
||||||
{
|
{
|
||||||
@@ -30,7 +30,7 @@ public class AdminOAuthHandler<TOptions>(
|
|||||||
{
|
{
|
||||||
static AdminOAuthHandler()
|
static AdminOAuthHandler()
|
||||||
{
|
{
|
||||||
Task.Factory.StartNew(Insertable, TaskCreationOptions.LongRunning);
|
Task.Factory.StartNew(InsertableAsync, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -41,7 +41,7 @@ public class AdminOAuthHandler<TOptions>(
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 创建访问日志
|
/// 创建访问日志
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static async Task Insertable()
|
private static async Task InsertableAsync()
|
||||||
{
|
{
|
||||||
var db = DbContext.GetDB<SysOperateLog>();
|
var db = DbContext.GetDB<SysOperateLog>();
|
||||||
var appLifetime = App.RootServices!.GetService<IHostApplicationLifetime>()!;
|
var appLifetime = App.RootServices!.GetService<IHostApplicationLifetime>()!;
|
||||||
|
|||||||
@@ -128,18 +128,19 @@ internal sealed class SysOrgService : BaseService<SysOrg>, ISysOrgService
|
|||||||
[OperDesc("DeleteOrg")]
|
[OperDesc("DeleteOrg")]
|
||||||
public async Task<bool> DeleteOrgAsync(IEnumerable<long> ids)
|
public async Task<bool> DeleteOrgAsync(IEnumerable<long> ids)
|
||||||
{
|
{
|
||||||
|
var sysDeleteOrgList = new List<long>();//需要删除的组织ID集合
|
||||||
|
var sysOrgList = await GetAllAsync().ConfigureAwait(false);//获取所有组织
|
||||||
|
foreach (var it in ids)
|
||||||
|
{
|
||||||
|
var children = SysOrgService.GetSysOrgChildren(sysOrgList, it);//查找下级组织
|
||||||
|
sysDeleteOrgList.AddRange(children.Select(it => it.Id).ToList());
|
||||||
|
sysDeleteOrgList.Add(it);
|
||||||
|
}
|
||||||
//获取所有ID
|
//获取所有ID
|
||||||
if (ids.Any())
|
if (sysDeleteOrgList.Count != 0)
|
||||||
{
|
{
|
||||||
using var db = GetDB();
|
using var db = GetDB();
|
||||||
var sysOrgList = await GetAllAsync().ConfigureAwait(false);//获取所有组织
|
|
||||||
var sysDeleteOrgList = new List<long>();//需要删除的组织ID集合
|
|
||||||
foreach (var it in ids)
|
|
||||||
{
|
|
||||||
var children = SysOrgService.GetSysOrgChildren(sysOrgList, it);//查找下级组织
|
|
||||||
sysDeleteOrgList.AddRange(children.Select(it => it.Id).ToList());
|
|
||||||
sysDeleteOrgList.Add(it);
|
|
||||||
}
|
|
||||||
//如果组织下有用户则不能删除
|
//如果组织下有用户则不能删除
|
||||||
if (await db.Queryable<SysUser>().AnyAsync(it => sysDeleteOrgList.Contains(it.OrgId)).ConfigureAwait(false))
|
if (await db.Queryable<SysUser>().AnyAsync(it => sysDeleteOrgList.Contains(it.OrgId)).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ public class SysPositionService : BaseService<SysPosition>, ISysPositionService
|
|||||||
}
|
}
|
||||||
|
|
||||||
var dels = (await GetAllAsync().ConfigureAwait(false)).Where(a => ids.Contains(a.Id));
|
var dels = (await GetAllAsync().ConfigureAwait(false)).Where(a => ids.Contains(a.Id));
|
||||||
await SysUserService.CheckApiDataScopeAsync(dels.Select(a => a.OrgId).ToList(), dels.Select(a => a.CreateUserId).ToList()).ConfigureAwait(false);
|
await SysUserService.CheckApiDataScopeAsync(dels.Select(a => a.OrgId), dels.Select(a => a.CreateUserId)).ConfigureAwait(false);
|
||||||
//删除职位
|
//删除职位
|
||||||
var result = await base.DeleteAsync(ids).ConfigureAwait(false);
|
var result = await base.DeleteAsync(ids).ConfigureAwait(false);
|
||||||
if (result)
|
if (result)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public interface ISysResourceService
|
|||||||
/// <param name="resourceList">资源列表</param>
|
/// <param name="resourceList">资源列表</param>
|
||||||
/// <param name="parentId">父ID</param>
|
/// <param name="parentId">父ID</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
IEnumerable<SysResource> ConstructMenuTrees(IEnumerable<SysResource> resourceList, long parentId = 0);
|
IEnumerable<SysResource> ConstructMenuTrees(List<SysResource> resourceList, long parentId = 0);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 复制资源到其他模块
|
/// 复制资源到其他模块
|
||||||
@@ -44,7 +44,7 @@ public interface ISysResourceService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="ids">id列表</param>
|
/// <param name="ids">id列表</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<bool> DeleteResourceAsync(IEnumerable<long> ids);
|
Task<bool> DeleteResourceAsync(HashSet<long> ids);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 从缓存/数据库读取全部资源列表
|
/// 从缓存/数据库读取全部资源列表
|
||||||
|
|||||||
@@ -75,10 +75,10 @@ internal sealed class SysResourceService : BaseService<SysResource>, ISysResourc
|
|||||||
/// <param name="ids">id列表</param>
|
/// <param name="ids">id列表</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[OperDesc("DeleteResource")]
|
[OperDesc("DeleteResource")]
|
||||||
public async Task<bool> DeleteResourceAsync(IEnumerable<long> ids)
|
public async Task<bool> DeleteResourceAsync(HashSet<long> ids)
|
||||||
{
|
{
|
||||||
//删除
|
//删除
|
||||||
if (ids.Any())
|
if (ids.Count != 0)
|
||||||
{
|
{
|
||||||
//获取所有菜单和按钮
|
//获取所有菜单和按钮
|
||||||
var resourceList = await GetAllAsync().ConfigureAwait(false);
|
var resourceList = await GetAllAsync().ConfigureAwait(false);
|
||||||
@@ -86,10 +86,11 @@ internal sealed class SysResourceService : BaseService<SysResource>, ISysResourc
|
|||||||
var delSysResources = resourceList.Where(it => ids.Contains(it.Id));
|
var delSysResources = resourceList.Where(it => ids.Contains(it.Id));
|
||||||
//找到要删除的模块
|
//找到要删除的模块
|
||||||
var delModules = resourceList.Where(a => a.Category == ResourceCategoryEnum.Module).Where(it => ids.Contains(it.Id));
|
var delModules = resourceList.Where(a => a.Category == ResourceCategoryEnum.Module).Where(it => ids.Contains(it.Id));
|
||||||
if (delModules.Any())
|
|
||||||
|
//获取模块下的所有列表
|
||||||
|
var delHashSet = delModules.Select(a => a.Id).ToHashSet();
|
||||||
|
if (delHashSet.Count != 0)
|
||||||
{
|
{
|
||||||
//获取模块下的所有列表
|
|
||||||
var delHashSet = delModules.Select(a => a.Id).ToHashSet();
|
|
||||||
var delModuleResources = resourceList.Where(it => delHashSet.Contains(it.Module));
|
var delModuleResources = resourceList.Where(it => delHashSet.Contains(it.Module));
|
||||||
delSysResources = delSysResources.Concat(delModuleResources).ToHashSet();
|
delSysResources = delSysResources.Concat(delModuleResources).ToHashSet();
|
||||||
}
|
}
|
||||||
@@ -345,17 +346,14 @@ internal sealed class SysResourceService : BaseService<SysResource>, ISysResourc
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IEnumerable<SysResource> ConstructMenuTrees(IEnumerable<SysResource> resourceList, long parentId = 0)
|
public IEnumerable<SysResource> ConstructMenuTrees(List<SysResource> resourceList, long parentId = 0)
|
||||||
{
|
{
|
||||||
//找下级资源ID列表
|
//找下级资源ID列表
|
||||||
var resources = resourceList.Where(it => it.ParentId == parentId).OrderBy(it => it.SortCode);
|
var resources = resourceList.Where(it => it.ParentId == parentId).OrderBy(it => it.SortCode);
|
||||||
if (resources.Any())//如果数量大于0
|
foreach (var item in resources)//遍历资源
|
||||||
{
|
{
|
||||||
foreach (var item in resources)//遍历资源
|
var children = ConstructMenuTrees(resourceList, item.Id).ToList();//添加子节点
|
||||||
{
|
item.Children = children.Count > 0 ? children : null;
|
||||||
var children = ConstructMenuTrees(resourceList, item.Id).ToList();//添加子节点
|
|
||||||
item.Children = children.Count > 0 ? children : null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return resources;
|
return resources;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public interface ISysRoleService
|
|||||||
/// 删除角色
|
/// 删除角色
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="ids">id列表</param>
|
/// <param name="ids">id列表</param>
|
||||||
Task<bool> DeleteRoleAsync(IEnumerable<long> ids);
|
Task<bool> DeleteRoleAsync(HashSet<long> ids);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 从缓存/数据库获取全部角色信息
|
/// 从缓存/数据库获取全部角色信息
|
||||||
@@ -43,7 +43,7 @@ public interface ISysRoleService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="input">角色id列表</param>
|
/// <param name="input">角色id列表</param>
|
||||||
/// <returns>角色列表</returns>
|
/// <returns>角色列表</returns>
|
||||||
Task<IEnumerable<SysRole>> GetRoleListByIdListAsync(IEnumerable<long> input);
|
Task<IEnumerable<SysRole>> GetRoleListByIdListAsync(HashSet<long> input);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 根据用户id获取角色列表
|
/// 根据用户id获取角色列表
|
||||||
|
|||||||
@@ -61,42 +61,46 @@ internal sealed class SysRoleService : BaseService<SysRole>, ISysRoleService
|
|||||||
|
|
||||||
var topOrgList = sysOrgList.Where(it => it.ParentId == 0);//获取顶级机构
|
var topOrgList = sysOrgList.Where(it => it.ParentId == 0);//获取顶级机构
|
||||||
var globalRole = sysRoles.Where(it => it.Category == RoleCategoryEnum.Global);//获取全局角色
|
var globalRole = sysRoles.Where(it => it.Category == RoleCategoryEnum.Global);//获取全局角色
|
||||||
if (globalRole.Any())
|
var children = globalRole.Select(it => new RoleTreeOutput
|
||||||
{
|
{
|
||||||
result.Add(new RoleTreeOutput()
|
Id = it.Id,
|
||||||
{
|
Name = it.Name,
|
||||||
Id = CommonUtils.GetSingleId(),
|
IsRole = true
|
||||||
Name = Localizer["Global"],
|
}).ToList();
|
||||||
Children = globalRole.Select(it => new RoleTreeOutput
|
|
||||||
{
|
result.Add(new RoleTreeOutput()
|
||||||
Id = it.Id,
|
{
|
||||||
Name = it.Name,
|
Id = CommonUtils.GetSingleId(),
|
||||||
IsRole = true
|
Name = Localizer["Global"],
|
||||||
}).ToList()
|
Children = children
|
||||||
});//添加全局角色
|
});//添加全局角色
|
||||||
}
|
|
||||||
//遍历顶级机构
|
//遍历顶级机构
|
||||||
foreach (var org in topOrgList)
|
foreach (var org in topOrgList)
|
||||||
{
|
{
|
||||||
var childIds = await _sysOrgService.GetOrgChildIdsAsync(org.Id, true, sysOrgList).ConfigureAwait(false);//获取机构下的所有子级ID
|
var childIds = await _sysOrgService.GetOrgChildIdsAsync(org.Id, true, sysOrgList).ConfigureAwait(false);//获取机构下的所有子级ID
|
||||||
var childRoles = sysRoles.Where(it => it.OrgId != 0 && childIds.Contains(it.OrgId));//获取机构下的所有角色
|
var childRoles = sysRoles.Where(it => it.OrgId != 0 && childIds.Contains(it.OrgId));//获取机构下的所有角色
|
||||||
if (childRoles.Any())
|
|
||||||
|
List<RoleTreeOutput> childrenRoleTreeOutputs = new();
|
||||||
|
|
||||||
|
foreach (var it in childRoles)
|
||||||
|
{
|
||||||
|
childrenRoleTreeOutputs.Add(new RoleTreeOutput()
|
||||||
|
{
|
||||||
|
Id = it.Id,
|
||||||
|
Name = it.Name,
|
||||||
|
IsRole = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (childrenRoleTreeOutputs.Count > 0)
|
||||||
|
|
||||||
{
|
{
|
||||||
var roleTreeOutput = new RoleTreeOutput
|
var roleTreeOutput = new RoleTreeOutput
|
||||||
{
|
{
|
||||||
Id = org.Id,
|
Id = org.Id,
|
||||||
Name = org.Name,
|
Name = org.Name,
|
||||||
IsRole = false
|
IsRole = false,
|
||||||
|
Children = childrenRoleTreeOutputs
|
||||||
};//实例化角色树
|
};//实例化角色树
|
||||||
foreach (var it in childRoles)
|
|
||||||
{
|
|
||||||
roleTreeOutput.Children.Add(new RoleTreeOutput()
|
|
||||||
{
|
|
||||||
Id = it.Id,
|
|
||||||
Name = it.Name,
|
|
||||||
IsRole = true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
result.Add(roleTreeOutput);
|
result.Add(roleTreeOutput);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -147,7 +151,7 @@ internal sealed class SysRoleService : BaseService<SysRole>, ISysRoleService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="input">角色id列表</param>
|
/// <param name="input">角色id列表</param>
|
||||||
/// <returns>角色列表</returns>
|
/// <returns>角色列表</returns>
|
||||||
public async Task<IEnumerable<SysRole>> GetRoleListByIdListAsync(IEnumerable<long> input)
|
public async Task<IEnumerable<SysRole>> GetRoleListByIdListAsync(HashSet<long> input)
|
||||||
{
|
{
|
||||||
var roles = await GetAllAsync().ConfigureAwait(false);
|
var roles = await GetAllAsync().ConfigureAwait(false);
|
||||||
var roleList = roles.Where(it => input.Contains(it.Id));
|
var roleList = roles.Where(it => input.Contains(it.Id));
|
||||||
@@ -162,7 +166,7 @@ internal sealed class SysRoleService : BaseService<SysRole>, ISysRoleService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="ids">id列表</param>
|
/// <param name="ids">id列表</param>
|
||||||
[OperDesc("DeleteRole")]
|
[OperDesc("DeleteRole")]
|
||||||
public async Task<bool> DeleteRoleAsync(IEnumerable<long> ids)
|
public async Task<bool> DeleteRoleAsync(HashSet<long> ids)
|
||||||
{
|
{
|
||||||
var sysRoles = await GetAllAsync().ConfigureAwait(false);//获取所有角色
|
var sysRoles = await GetAllAsync().ConfigureAwait(false);//获取所有角色
|
||||||
var hasSuperAdmin = sysRoles.Any(it => it.Id == RoleConst.SuperAdminRoleId && ids.Contains(it.Id));//判断是否有超级管理员
|
var hasSuperAdmin = sysRoles.Any(it => it.Id == RoleConst.SuperAdminRoleId && ids.Contains(it.Id));//判断是否有超级管理员
|
||||||
@@ -170,10 +174,10 @@ internal sealed class SysRoleService : BaseService<SysRole>, ISysRoleService
|
|||||||
throw Oops.Bah(Localizer["CanotDeleteAdmin"]);
|
throw Oops.Bah(Localizer["CanotDeleteAdmin"]);
|
||||||
|
|
||||||
var dels = (await GetAllAsync().ConfigureAwait(false)).Where(a => ids.Contains(a.Id));
|
var dels = (await GetAllAsync().ConfigureAwait(false)).Where(a => ids.Contains(a.Id));
|
||||||
await SysUserService.CheckApiDataScopeAsync(dels.Select(a => a.OrgId).ToList(), dels.Select(a => a.CreateUserId).ToList()).ConfigureAwait(false);
|
await SysUserService.CheckApiDataScopeAsync(dels.Select(a => a.OrgId), dels.Select(a => a.CreateUserId)).ConfigureAwait(false);
|
||||||
|
|
||||||
//数据库是string所以这里转下
|
//数据库是string所以这里转下
|
||||||
var targetIds = ids.Select(it => it.ToString());
|
var targetIds = ids.Select(it => it.ToString()).ToList();
|
||||||
//定义删除的关系
|
//定义删除的关系
|
||||||
var delRelations = new List<RelationCategoryEnum> {
|
var delRelations = new List<RelationCategoryEnum> {
|
||||||
RelationCategoryEnum.RoleHasResource,
|
RelationCategoryEnum.RoleHasResource,
|
||||||
@@ -184,7 +188,7 @@ internal sealed class SysRoleService : BaseService<SysRole>, ISysRoleService
|
|||||||
//事务
|
//事务
|
||||||
var result = await db.UseTranAsync(async () =>
|
var result = await db.UseTranAsync(async () =>
|
||||||
{
|
{
|
||||||
await db.Deleteable<SysRole>().In(ids.ToList()).ExecuteCommandHasChangeAsync().ConfigureAwait(false);//删除
|
await db.Deleteable<SysRole>().In(ids).ExecuteCommandHasChangeAsync().ConfigureAwait(false);//删除
|
||||||
//删除关系表角色与资源关系,角色与权限关系
|
//删除关系表角色与资源关系,角色与权限关系
|
||||||
await db.Deleteable<SysRelation>(it => ids.Contains(it.ObjectId) && delRelations.Contains(it.Category)).ExecuteCommandAsync().ConfigureAwait(false);
|
await db.Deleteable<SysRelation>(it => ids.Contains(it.ObjectId) && delRelations.Contains(it.Category)).ExecuteCommandAsync().ConfigureAwait(false);
|
||||||
//删除关系表角色与用户关系
|
//删除关系表角色与用户关系
|
||||||
@@ -317,7 +321,7 @@ internal sealed class SysRoleService : BaseService<SysRole>, ISysRoleService
|
|||||||
#region 角色权限处理.
|
#region 角色权限处理.
|
||||||
var defaultDataScope = sysRole.DefaultDataScope;//获取默认数据范围
|
var defaultDataScope = sysRole.DefaultDataScope;//获取默认数据范围
|
||||||
|
|
||||||
if (menusList.Any())
|
if (relationRoles.Count != 0)
|
||||||
{
|
{
|
||||||
//获取权限授权树
|
//获取权限授权树
|
||||||
var permissions = App.GetService<IApiPermissionService>().PermissionTreeSelector(menusList.Select(it => it.Href));
|
var permissions = App.GetService<IApiPermissionService>().PermissionTreeSelector(menusList.Select(it => it.Href));
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ public class SessionOutput : PrimaryIdEntity
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 主键Id
|
/// 主键Id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[System.ComponentModel.DataAnnotations.Key]
|
||||||
public override long Id { get; set; }
|
public override long Id { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public interface ISysUserService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="ids">用户ID列表。</param>
|
/// <param name="ids">用户ID列表。</param>
|
||||||
/// <returns>是否删除成功。</returns>
|
/// <returns>是否删除成功。</returns>
|
||||||
Task<bool> DeleteUserAsync(IEnumerable<long> ids);
|
Task<bool> DeleteUserAsync(HashSet<long> ids);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 从缓存中删除用户信息。
|
/// 从缓存中删除用户信息。
|
||||||
|
|||||||
@@ -377,9 +377,9 @@ internal sealed class SysUserService : BaseService<SysUser>, ISysUserService
|
|||||||
/// 获取用户拥有的资源
|
/// 获取用户拥有的资源
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">用户id</param>
|
/// <param name="id">用户id</param>
|
||||||
public async Task<GrantResourceData> OwnResourceAsync(long id)
|
public Task<GrantResourceData> OwnResourceAsync(long id)
|
||||||
{
|
{
|
||||||
return await _roleService.OwnResourceAsync(id, RelationCategoryEnum.UserHasResource).ConfigureAwait(false);
|
return _roleService.OwnResourceAsync(id, RelationCategoryEnum.UserHasResource);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -505,10 +505,10 @@ internal sealed class SysUserService : BaseService<SysUser>, ISysUserService
|
|||||||
var password = await GetDefaultPassWord(true).ConfigureAwait(false);//获取默认密码,这里不走Aop所以需要加密一下
|
var password = await GetDefaultPassWord(true).ConfigureAwait(false);//获取默认密码,这里不走Aop所以需要加密一下
|
||||||
using var db = GetDB();
|
using var db = GetDB();
|
||||||
//重置密码
|
//重置密码
|
||||||
if (await db.UpdateSetColumnsTrueAsync<SysUser>(it => new SysUser
|
if ((await db.UpdateSetColumnsTrueAsync<SysUser>(it => new SysUser
|
||||||
{
|
{
|
||||||
Password = password
|
Password = password
|
||||||
}, it => it.Id == id).ConfigureAwait(false))
|
}, it => it.Id == id).ConfigureAwait(false)) > 0)
|
||||||
{
|
{
|
||||||
DeleteUserFromCache(id);//从cache删除用户信息
|
DeleteUserFromCache(id);//从cache删除用户信息
|
||||||
var verificatInfoIds = _verificatInfoService.GetListByUserId(id);
|
var verificatInfoIds = _verificatInfoService.GetListByUserId(id);
|
||||||
@@ -587,7 +587,7 @@ internal sealed class SysUserService : BaseService<SysUser>, ISysUserService
|
|||||||
#region 用户权限处理.
|
#region 用户权限处理.
|
||||||
|
|
||||||
//获取菜单信息
|
//获取菜单信息
|
||||||
if (menusList.Any())
|
if (relationUsers.Count != 0)
|
||||||
{
|
{
|
||||||
//获取权限授权树
|
//获取权限授权树
|
||||||
var permissions = App.GetService<IApiPermissionService>().PermissionTreeSelector(menusList.Select(it => it.Href));
|
var permissions = App.GetService<IApiPermissionService>().PermissionTreeSelector(menusList.Select(it => it.Href));
|
||||||
@@ -642,7 +642,7 @@ internal sealed class SysUserService : BaseService<SysUser>, ISysUserService
|
|||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
[OperDesc("DeleteUser")]
|
[OperDesc("DeleteUser")]
|
||||||
public async Task<bool> DeleteUserAsync(IEnumerable<long> ids)
|
public async Task<bool> DeleteUserAsync(HashSet<long> ids)
|
||||||
{
|
{
|
||||||
using var db = GetDB();
|
using var db = GetDB();
|
||||||
var containsSuperAdmin = await db.Queryable<SysUser>().Where(it => it.Id == RoleConst.SuperAdminId && ids.Contains(it.Id)).AnyAsync().ConfigureAwait(false);//判断是否有超管
|
var containsSuperAdmin = await db.Queryable<SysUser>().Where(it => it.Id == RoleConst.SuperAdminId && ids.Contains(it.Id)).AnyAsync().ConfigureAwait(false);//判断是否有超管
|
||||||
@@ -672,7 +672,7 @@ internal sealed class SysUserService : BaseService<SysUser>, ISysUserService
|
|||||||
.ExecuteCommandAsync().ConfigureAwait(false);
|
.ExecuteCommandAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
//删除用户
|
//删除用户
|
||||||
await db.Deleteable<SysUser>().In(ids.ToList()).ExecuteCommandHasChangeAsync().ConfigureAwait(false);//删除
|
await db.Deleteable<SysUser>().In(ids).ExecuteCommandHasChangeAsync().ConfigureAwait(false);//删除
|
||||||
|
|
||||||
//删除关系表用户与资源关系,用户与权限关系,用户与角色关系
|
//删除关系表用户与资源关系,用户与权限关系,用户与角色关系
|
||||||
await db.Deleteable<SysRelation>(it => ids.Contains(it.ObjectId) && delRelations.Contains(it.Category)).ExecuteCommandAsync().ConfigureAwait(false);
|
await db.Deleteable<SysRelation>(it => ids.Contains(it.ObjectId) && delRelations.Contains(it.Category)).ExecuteCommandAsync().ConfigureAwait(false);
|
||||||
@@ -718,16 +718,16 @@ internal sealed class SysUserService : BaseService<SysUser>, ISysUserService
|
|||||||
public void DeleteUserFromCache(IEnumerable<long> ids)
|
public void DeleteUserFromCache(IEnumerable<long> ids)
|
||||||
{
|
{
|
||||||
var userIds = ids.Select(it => it.ToString()).ToArray();//id转string列表
|
var userIds = ids.Select(it => it.ToString()).ToArray();//id转string列表
|
||||||
var sysUsers = App.CacheService.HashGet<SysUser>(CacheConst.Cache_SysUser, userIds).Where(it => it != null);//获取用户列表
|
var sysUsers = App.CacheService.HashGet<SysUser>(CacheConst.Cache_SysUser, userIds);//获取用户列表
|
||||||
if (sysUsers.Any() == true)
|
if (sysUsers.Count != 0)
|
||||||
{
|
{
|
||||||
var accounts = sysUsers.Where(it => it != null).Select(it => it.Account).ToArray();//账号集合
|
var accounts = sysUsers.Where(it => it != null).Select(it => it.Account).ToArray();//账号集合
|
||||||
var phones = sysUsers.Select(it => it.Phone);//手机号集合
|
var phones = sysUsers.Select(it => it?.Phone);//手机号集合
|
||||||
|
|
||||||
if (sysUsers.Any(it => it.TenantId != null))//如果有租户id不是空的表示是多租户模式
|
if (sysUsers.Any(it => it?.TenantId != null))//如果有租户id不是空的表示是多租户模式
|
||||||
{
|
{
|
||||||
var userAccountKey = CacheConst.Cache_SysUserAccount;
|
var userAccountKey = CacheConst.Cache_SysUserAccount;
|
||||||
var tenantIds = sysUsers.Where(it => it.TenantId != null).Select(it => it.TenantId.Value).Distinct().ToArray();//租户id列表
|
var tenantIds = sysUsers.Where(it => it?.TenantId != null).Select(it => it.TenantId.Value).Distinct().ToArray();//租户id列表
|
||||||
foreach (var tenantId in tenantIds)
|
foreach (var tenantId in tenantIds)
|
||||||
{
|
{
|
||||||
userAccountKey = $"{userAccountKey}:{tenantId}";
|
userAccountKey = $"{userAccountKey}:{tenantId}";
|
||||||
|
|||||||
@@ -185,12 +185,12 @@ internal sealed class UserCenterService : BaseService<SysUser>, IUserCenterServi
|
|||||||
using var db = GetDB();
|
using var db = GetDB();
|
||||||
|
|
||||||
//更新指定字段
|
//更新指定字段
|
||||||
var result = await db.UpdateSetColumnsTrueAsync<SysUser>(it => new SysUser
|
var result = (await db.UpdateSetColumnsTrueAsync<SysUser>(it => new SysUser
|
||||||
{
|
{
|
||||||
Email = input.Email,
|
Email = input.Email,
|
||||||
Phone = input.Phone,
|
Phone = input.Phone,
|
||||||
Avatar = input.Avatar,
|
Avatar = input.Avatar,
|
||||||
}, it => it.Id == UserManager.UserId).ConfigureAwait(false);
|
}, it => it.Id == UserManager.UserId).ConfigureAwait(false)) > 0;
|
||||||
if (result)
|
if (result)
|
||||||
_userService.DeleteUserFromCache(UserManager.UserId);//cache删除用户数据
|
_userService.DeleteUserFromCache(UserManager.UserId);//cache删除用户数据
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ internal sealed class VerificatInfoService : BaseService<VerificatInfo>, IVerifi
|
|||||||
public void Delete(long id)
|
public void Delete(long id)
|
||||||
{
|
{
|
||||||
using var db = GetDB();
|
using var db = GetDB();
|
||||||
db.Deleteable<VerificatInfo>(id).ExecuteCommand();
|
db.Deleteable<VerificatInfo>(a => a.Id == id).ExecuteCommand();
|
||||||
VerificatInfoService.RemoveCache(id);
|
VerificatInfoService.RemoveCache(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||||
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net8.0;net9.0;</TargetFrameworks>
|
<TargetFrameworks>net8.0;net9.0;</TargetFrameworks>
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://thingsgateway.cn/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
public static class USheetDataHelpers
|
||||||
|
{
|
||||||
|
public static USheetDatas GetUSheetDatas(Dictionary<string, object> data)
|
||||||
|
{
|
||||||
|
var uSheetDatas = new USheetDatas();
|
||||||
|
|
||||||
|
foreach (var a in data)
|
||||||
|
{
|
||||||
|
var value = (a.Value as IEnumerable<Dictionary<string, object>>).ToList();
|
||||||
|
|
||||||
|
var uSheetData = new USheetData();
|
||||||
|
uSheetData.id = a.Key;
|
||||||
|
uSheetData.name = a.Key;
|
||||||
|
|
||||||
|
for (int row1 = 0; row1 < value.Count; row1++)
|
||||||
|
{
|
||||||
|
if (row1 == 0)
|
||||||
|
{
|
||||||
|
Dictionary<int, USheetCelldata> usheetColldata = new();
|
||||||
|
int col = 0;
|
||||||
|
foreach (var colData in value[row1])
|
||||||
|
{
|
||||||
|
usheetColldata.Add(col, new USheetCelldata() { v = colData.Key });
|
||||||
|
col++;
|
||||||
|
}
|
||||||
|
uSheetData.cellData.Add(row1, usheetColldata);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Dictionary<int, USheetCelldata> usheetColldata = new();
|
||||||
|
int col = 0;
|
||||||
|
foreach (var colData in value[row1])
|
||||||
|
{
|
||||||
|
usheetColldata.Add(col, new USheetCelldata() { v = colData.Value });
|
||||||
|
col++;
|
||||||
|
}
|
||||||
|
uSheetData.cellData.Add(row1 + 1, usheetColldata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uSheetData.rowCount = uSheetData.cellData.Count + 100;
|
||||||
|
uSheetData.columnCount = uSheetData.cellData.FirstOrDefault().Value?.Count ?? 0;
|
||||||
|
uSheetDatas.sheets.Add(a.Key, uSheetData);
|
||||||
|
}
|
||||||
|
return uSheetDatas;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,12 +31,13 @@ public partial class ChoiceTable<TItem> where TItem : class, new()
|
|||||||
|
|
||||||
public async Task OnAddAsync(IEnumerable<TItem> selectorOutputs)
|
public async Task OnAddAsync(IEnumerable<TItem> selectorOutputs)
|
||||||
{
|
{
|
||||||
if (MaxCount > 0 && selectorOutputs.Count() + SelectedRows.Count > MaxCount)
|
var data = selectorOutputs is IReadOnlyCollection<TItem> list ? list : selectorOutputs.ToList();
|
||||||
|
if (MaxCount > 0 && data.Count + SelectedRows.Count > MaxCount)
|
||||||
{
|
{
|
||||||
await ToastService.Warning(AdminLocalizer["MaxCount"]);
|
await ToastService.Warning(AdminLocalizer["MaxCount"]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
foreach (var item in selectorOutputs)
|
foreach (var item in data)
|
||||||
{
|
{
|
||||||
SelectedRows.Add(item);
|
SelectedRows.Add(item);
|
||||||
await table2.QueryAsync();
|
await table2.QueryAsync();
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
@namespace ThingsGateway.Gateway.Razor
|
@namespace ThingsGateway.Admin.Razor
|
||||||
@using ThingsGateway.Admin.Application
|
@using ThingsGateway.Admin.Application
|
||||||
@using ThingsGateway.Admin.Razor
|
@using ThingsGateway.Admin.Razor
|
||||||
@using ThingsGateway.Gateway.Application
|
|
||||||
|
|
||||||
<div class="h-600px">
|
<div class="h-600px">
|
||||||
<UniverSheet @ref="_sheetExcel" OnReadyAsync="OnReadyAsync"></UniverSheet>
|
<UniverSheet @ref="_sheetExcel" OnReadyAsync="OnReadyAsync"></UniverSheet>
|
||||||
@@ -8,9 +8,10 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using ThingsGateway.Admin.Application;
|
||||||
using ThingsGateway.NewLife.Json.Extension;
|
using ThingsGateway.NewLife.Json.Extension;
|
||||||
|
|
||||||
namespace ThingsGateway.Gateway.Razor;
|
namespace ThingsGateway.Admin.Razor;
|
||||||
|
|
||||||
public partial class USheet
|
public partial class USheet
|
||||||
{
|
{
|
||||||
@@ -30,7 +30,7 @@ public class BlazorAppContext
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 全部菜单
|
/// 全部菜单
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<SysResource> AllMenus { get; private set; }
|
public List<SysResource> AllMenus { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 当前用户
|
/// 当前用户
|
||||||
@@ -42,22 +42,22 @@ public class BlazorAppContext
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 用户个人菜单
|
/// 用户个人菜单
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<MenuItem> OwnMenuItems { get; private set; }
|
public List<MenuItem> OwnMenuItems { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 不同模块的菜单
|
/// 不同模块的菜单
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<MenuItem> AllOwnMenuItems { get; private set; }
|
public List<MenuItem> AllOwnMenuItems { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 用户个人菜单,多个模块
|
/// 用户个人菜单,多个模块
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<SysResource> OwnMenus { get; private set; }
|
public List<SysResource> OwnMenus { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 用户个人菜单,非树形
|
/// 用户个人菜单,非树形
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<MenuItem> OwnSameLevelMenuItems { get; private set; }
|
public List<MenuItem> OwnSameLevelMenuItems { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 个人工作台
|
/// 个人工作台
|
||||||
@@ -67,9 +67,9 @@ public class BlazorAppContext
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 用户个人快捷方式菜单
|
/// 用户个人快捷方式菜单
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<SysResource> UserWorkbenchOutputs { get; private set; }
|
public List<SysResource> UserWorkbenchOutputs { get; private set; }
|
||||||
|
|
||||||
public IEnumerable<SysResource> AllResource { get; private set; }
|
public List<SysResource> AllResource { get; private set; }
|
||||||
|
|
||||||
private ISysResourceService ResourceService { get; }
|
private ISysResourceService ResourceService { get; }
|
||||||
private ISysUserService SysUserService { get; }
|
private ISysUserService SysUserService { get; }
|
||||||
@@ -93,7 +93,7 @@ public class BlazorAppContext
|
|||||||
AllResource = sysResources;
|
AllResource = sysResources;
|
||||||
var ids = CurrentUser.ModuleList.Select(a => a.Id).ToHashSet();
|
var ids = CurrentUser.ModuleList.Select(a => a.Id).ToHashSet();
|
||||||
CurrentUser.ModuleList = AllResource.Where(a => ids.Contains(a.Id)).OrderBy(a => a.SortCode).ToList();
|
CurrentUser.ModuleList = AllResource.Where(a => ids.Contains(a.Id)).OrderBy(a => a.SortCode).ToList();
|
||||||
AllMenus = AllResource.Where(a => a.Category == ResourceCategoryEnum.Menu);
|
AllMenus = AllResource.Where(a => a.Category == ResourceCategoryEnum.Menu).ToList();
|
||||||
|
|
||||||
if (moduleId == null)
|
if (moduleId == null)
|
||||||
{
|
{
|
||||||
@@ -123,8 +123,8 @@ public class BlazorAppContext
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
var ownMenus = OwnMenus.Where(a => a.Module == CurrentModuleId);
|
var ownMenus = OwnMenus.Where(a => a.Module == CurrentModuleId);
|
||||||
OwnMenuItems = ResourceUtil.BuildMenuTrees(ownMenus).ToList();
|
OwnMenuItems = AdminResourceUtil.BuildMenuTrees(ownMenus).ToList();
|
||||||
AllOwnMenuItems = ResourceUtil.BuildMenuTrees(OwnMenus).ToList();
|
AllOwnMenuItems = AdminResourceUtil.BuildMenuTrees(OwnMenus).ToList();
|
||||||
OwnSameLevelMenuItems = ownMenus.Where(a => !a.Href.IsNullOrWhiteSpace()).Select(item => new MenuItem()
|
OwnSameLevelMenuItems = ownMenus.Where(a => !a.Href.IsNullOrWhiteSpace()).Select(item => new MenuItem()
|
||||||
{
|
{
|
||||||
Match = item.NavLinkMatch ?? Microsoft.AspNetCore.Components.Routing.NavLinkMatch.All,
|
Match = item.NavLinkMatch ?? Microsoft.AspNetCore.Components.Routing.NavLinkMatch.All,
|
||||||
@@ -132,8 +132,8 @@ public class BlazorAppContext
|
|||||||
Icon = item.Icon,
|
Icon = item.Icon,
|
||||||
Url = item.Href,
|
Url = item.Href,
|
||||||
Target = item.Target.ToString(),
|
Target = item.Target.ToString(),
|
||||||
});
|
}).ToList();
|
||||||
UserWorkbenchOutputs = AllMenus.Where(it => UserWorkBench.Shortcuts.Contains(it.Id));
|
UserWorkbenchOutputs = AllMenus.Where(it => UserWorkBench.Shortcuts.Contains(it.Id)).ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public partial class EditPagePolicy
|
|||||||
|
|
||||||
protected override Task OnParametersSetAsync()
|
protected override Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
ShortcutsTreeViewItems = ResourceUtil.BuildTreeItemList(AppContext.OwnMenus.WhereIf(!ShortcutsSearchText.IsNullOrEmpty(), a => a.Title.Contains(ShortcutsSearchText)), Model.Shortcuts, null);
|
ShortcutsTreeViewItems = AdminResourceUtil.BuildTreeItemList(AppContext.OwnMenus.WhereIf(!ShortcutsSearchText.IsNullOrEmpty(), a => a.Title.Contains(ShortcutsSearchText)), Model.Shortcuts, null);
|
||||||
return base.OnParametersSetAsync();
|
return base.OnParametersSetAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,6 +48,6 @@ public partial class EditPagePolicy
|
|||||||
{
|
{
|
||||||
await Task.CompletedTask;
|
await Task.CompletedTask;
|
||||||
ShortcutsSearchText = searchText;
|
ShortcutsSearchText = searchText;
|
||||||
return ResourceUtil.BuildTreeItemList(AppContext.OwnMenus.WhereIf(!ShortcutsSearchText.IsNullOrEmpty(), a => a.Title.Contains(ShortcutsSearchText)), Model.Shortcuts, null);
|
return AdminResourceUtil.BuildTreeItemList(AppContext.OwnMenus.WhereIf(!ShortcutsSearchText.IsNullOrEmpty(), a => a.Title.Contains(ShortcutsSearchText)), Model.Shortcuts, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public partial class MenuChoiceDialog
|
|||||||
var all = (await SysResourceService.GetAllAsync());
|
var all = (await SysResourceService.GetAllAsync());
|
||||||
var items = all.Where(a => a.Category == ResourceCategoryEnum.Menu && a.Module == ModuleId);
|
var items = all.Where(a => a.Category == ResourceCategoryEnum.Menu && a.Module == ModuleId);
|
||||||
ModuleTitle = all.FirstOrDefault(a => a.Id == ModuleId)?.Title;
|
ModuleTitle = all.FirstOrDefault(a => a.Id == ModuleId)?.Title;
|
||||||
Items = ResourceUtil.BuildTreeItemList(items, new List<long> { Value }, RenderTreeItem);
|
Items = AdminResourceUtil.BuildTreeItemList(items, new List<long> { Value }, RenderTreeItem);
|
||||||
await base.OnParametersSetAsync();
|
await base.OnParametersSetAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,12 +26,12 @@
|
|||||||
OnQueryAsync="OnQueryAsync" CustomerSearchModel="@CustomerSearchModel"
|
OnQueryAsync="OnQueryAsync" CustomerSearchModel="@CustomerSearchModel"
|
||||||
OnSaveAsync="Save" OnDeleteAsync="Delete">
|
OnSaveAsync="Save" OnDeleteAsync="Delete">
|
||||||
<TableToolbarTemplate>
|
<TableToolbarTemplate>
|
||||||
<PopConfirmButton Color=Color.Warning IsDisabled="SelectedRows.Count<=0||!AuthorizeButton(AdminOperConst.Add)" Text=@OperDescLocalizer["CopyResource"] Icon="fa fa-copy" OnConfirm="OnCopy">
|
<PopConfirmButton Color=Color.Warning IsKeepDisabled="SelectedRows.Count <= 0 || !AuthorizeButton(AdminOperConst.Add)" Text=@OperDescLocalizer["CopyResource"] Icon="fa fa-copy" OnConfirm="OnCopy">
|
||||||
<BodyTemplate>
|
<BodyTemplate>
|
||||||
<Select Items="ModuleSelectedItems" @bind-Value=CopyModule ShowLabel="false" />
|
<Select Items="ModuleSelectedItems" @bind-Value=CopyModule ShowLabel="false" />
|
||||||
</BodyTemplate>
|
</BodyTemplate>
|
||||||
</PopConfirmButton>
|
</PopConfirmButton>
|
||||||
<PopConfirmButton Color=Color.Warning IsDisabled="SelectedRows.Count!=1||!AuthorizeButton(AdminOperConst.Edit)" Text=@OperDescLocalizer["ChangeParentResource"] Icon="fa fa-copy" OnConfirm="OnChangeParent">
|
<PopConfirmButton Color=Color.Warning IsKeepDisabled="SelectedRows.Count != 1 || !AuthorizeButton(AdminOperConst.Edit)" Text=@OperDescLocalizer["ChangeParentResource"] Icon="fa fa-copy" OnConfirm="OnChangeParent">
|
||||||
<BodyTemplate>
|
<BodyTemplate>
|
||||||
<div class="overflow-y-auto" style="height:500px">
|
<div class="overflow-y-auto" style="height:500px">
|
||||||
<TreeView Items="MenuTreeItems" IsVirtualize="true" OnTreeItemClick="a=>{ChangeParentId=a.Value.Id;return Task.CompletedTask;}" />
|
<TreeView Items="MenuTreeItems" IsVirtualize="true" OnTreeItemClick="a=>{ChangeParentId=a.Value.Id;return Task.CompletedTask;}" />
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ public partial class SysResourcePage
|
|||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
ModuleSelectedItems = ResourceUtil.BuildModuleSelectList((await SysResourceService.GetAllAsync())).ToList();
|
ModuleSelectedItems = AdminResourceUtil.BuildModuleSelectList((await SysResourceService.GetAllAsync())).ToList();
|
||||||
MenuItems = ResourceUtil.BuildMenuSelectList((await SysResourceService.GetAllAsync())).Concat(new List<SelectedItem>() { new("0", AdminLocalizer["Root"]) }).ToList();
|
MenuItems = AdminResourceUtil.BuildMenuSelectList((await SysResourceService.GetAllAsync())).Concat(new List<SelectedItem>() { new("0", AdminLocalizer["Root"]) }).ToList();
|
||||||
|
|
||||||
await base.OnParametersSetAsync();
|
await base.OnParametersSetAsync();
|
||||||
}
|
}
|
||||||
@@ -49,7 +49,7 @@ public partial class SysResourcePage
|
|||||||
|
|
||||||
private async Task<QueryData<SysResource>> OnQueryAsync(QueryPageOptions options)
|
private async Task<QueryData<SysResource>> OnQueryAsync(QueryPageOptions options)
|
||||||
{
|
{
|
||||||
MenuTreeItems = new List<TreeViewItem<SysResource>>() { new TreeViewItem<SysResource>(new SysResource()) { Text = AdminLocalizer["Root"] } }.Concat(ResourceUtil.BuildTreeItemList((await SysResourceService.GetAllAsync()).Where(a => a.Module == CustomerSearchModel.Module), new(), null)).ToList();
|
MenuTreeItems = new List<TreeViewItem<SysResource>>() { new TreeViewItem<SysResource>(new SysResource()) { Text = AdminLocalizer["Root"] } }.Concat(AdminResourceUtil.BuildTreeItemList((await SysResourceService.GetAllAsync()).Where(a => a.Module == CustomerSearchModel.Module), new(), null)).ToList();
|
||||||
|
|
||||||
var data = await SysResourceService.PageAsync(options, CustomerSearchModel);
|
var data = await SysResourceService.PageAsync(options, CustomerSearchModel);
|
||||||
return data;
|
return data;
|
||||||
@@ -93,7 +93,7 @@ public partial class SysResourcePage
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var result = await SysResourceService.DeleteResourceAsync(sysResources.Select(a => a.Id));
|
var result = await SysResourceService.DeleteResourceAsync(sysResources.Select(a => a.Id).ToHashSet());
|
||||||
if (ReloadUser != null)
|
if (ReloadUser != null)
|
||||||
{
|
{
|
||||||
await ReloadUser();
|
await ReloadUser();
|
||||||
@@ -136,14 +136,14 @@ public partial class SysResourcePage
|
|||||||
private async Task<IEnumerable<TableTreeNode<SysResource>>> OnTreeExpand(SysResource menu)
|
private async Task<IEnumerable<TableTreeNode<SysResource>>> OnTreeExpand(SysResource menu)
|
||||||
{
|
{
|
||||||
var sysResources = await SysResourceService.GetAllAsync();
|
var sysResources = await SysResourceService.GetAllAsync();
|
||||||
var result = ResourceUtil.BuildTableTrees(sysResources, menu.Id);
|
var result = AdminResourceUtil.BuildTableTrees(sysResources, menu.Id);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<IEnumerable<TableTreeNode<SysResource>>> TreeNodeConverter(IEnumerable<SysResource> items)
|
private static async Task<IEnumerable<TableTreeNode<SysResource>>> TreeNodeConverter(IEnumerable<SysResource> items)
|
||||||
{
|
{
|
||||||
await Task.CompletedTask;
|
await Task.CompletedTask;
|
||||||
var result = ResourceUtil.BuildTableTrees(items, 0);
|
var result = AdminResourceUtil.BuildTableTrees(items, 0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ public partial class SysRolePage
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return await SysRoleService.DeleteRoleAsync(sysRoles.Select(a => a.Id));
|
return await SysRoleService.DeleteRoleAsync(sysRoles.Select(a => a.Id).ToHashSet());
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public partial class GrantResourceDialog
|
|||||||
{
|
{
|
||||||
var items = (await SysResourceService.GetAllAsync()).Where(a => a.Category != ResourceCategoryEnum.Module).OrderBy(a => a.Module).ThenBy(a => a.Id).ToList();
|
var items = (await SysResourceService.GetAllAsync()).Where(a => a.Category != ResourceCategoryEnum.Module).OrderBy(a => a.Module).ThenBy(a => a.Id).ToList();
|
||||||
|
|
||||||
Items = ResourceUtil.BuildTreeItemList(items, Value, RenderTreeItem);
|
Items = AdminResourceUtil.BuildTreeItemList(items, Value, RenderTreeItem);
|
||||||
ModuleList = (await SysResourceService.GetAllAsync()).Where(a => a.Category == ResourceCategoryEnum.Module).ToList();
|
ModuleList = (await SysResourceService.GetAllAsync()).Where(a => a.Category == ResourceCategoryEnum.Module).ToList();
|
||||||
await base.OnInitializedAsync();
|
await base.OnInitializedAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public partial class SysUserEdit
|
|||||||
BoolItems = LocalizerUtil.GetBoolItems(Model.GetType(), nameof(Model.Status));
|
BoolItems = LocalizerUtil.GetBoolItems(Model.GetType(), nameof(Model.Status));
|
||||||
var items = await SysPositionService.SelectorAsync(new PositionSelectorInput());
|
var items = await SysPositionService.SelectorAsync(new PositionSelectorInput());
|
||||||
Items = PositionUtil.BuildCascaderItemList(items);
|
Items = PositionUtil.BuildCascaderItemList(items);
|
||||||
ModuleSelectedItems = ResourceUtil.BuildModuleSelectList((await SysResourceService.GetAllAsync())).ToList();
|
ModuleSelectedItems = AdminResourceUtil.BuildModuleSelectList((await SysResourceService.GetAllAsync())).ToList();
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
await base.OnInitializedAsync();
|
await base.OnInitializedAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public partial class SysUserPage
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return await SysUserService.DeleteUserAsync(sysUsers.Select(a => a.Id));
|
return await SysUserService.DeleteUserAsync(sysUsers.Select(a => a.Id).ToHashSet());
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj" />
|
<ProjectReference Include="..\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj" />
|
||||||
<PackageReference Include="BootstrapBlazor.Chart" Version="9.0.0" />
|
<PackageReference Include="BootstrapBlazor.Chart" Version="9.0.0" />
|
||||||
|
<PackageReference Include="BootstrapBlazor.UniverSheet" Version="9.0.5" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)'=='net8.0'">
|
<ItemGroup Condition="'$(TargetFramework)'=='net8.0'">
|
||||||
@@ -17,6 +18,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
|
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
|
||||||
|
|
||||||
<!--<UseRazorSourceGenerator>false</UseRazorSourceGenerator>-->
|
<!--<UseRazorSourceGenerator>false</UseRazorSourceGenerator>-->
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Razor;
|
|||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||||
public static class ResourceUtil
|
public static class AdminResourceUtil
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造选择项,ID/TITLE
|
/// 构造选择项,ID/TITLE
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
<Target Name="AdminPostPublish" AfterTargets="Publish">
|
<Target Name="AdminPostPublish" AfterTargets="Publish">
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<!-- setting up the variable for convenience -->
|
<!-- setting up the variable for convenience -->
|
||||||
<AdminFiles Include="bin\$(Configuration)\$(TargetFramework)\SeedData\**" />
|
<AdminFiles Include="$(OutputPath)\$(TargetFramework)\SeedData\**" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
#推送:docker push registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway
|
#推送:docker push registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway
|
||||||
|
|
||||||
#aspnetcore9.0环境
|
#aspnetcore9.0环境
|
||||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
|
#FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
|
||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:8.0-noble AS base
|
||||||
COPY . /app
|
COPY . /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
#默认web
|
#默认web
|
||||||
@@ -13,6 +14,8 @@ EXPOSE 5000
|
|||||||
|
|
||||||
# 添加时区环境变量,亚洲,上海
|
# 添加时区环境变量,亚洲,上海
|
||||||
ENV TimeZone=Asia/Shanghai
|
ENV TimeZone=Asia/Shanghai
|
||||||
|
# 转发头
|
||||||
|
ENV ASPNETCORE_FORWARDEDHEADERS_ENABLED=true
|
||||||
# 使用软连接,并且将时区配置覆盖/etc/timezone
|
# 使用软连接,并且将时区配置覆盖/etc/timezone
|
||||||
RUN ln -snf /usr/share/zoneinfo/$TimeZone /etc/localtime && echo $TimeZone > /etc/timezone
|
RUN ln -snf /usr/share/zoneinfo/$TimeZone /etc/localtime && echo $TimeZone > /etc/timezone
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
#推送:docker push registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64
|
#推送:docker push registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64
|
||||||
|
|
||||||
#aspnetcore9.0环境
|
#aspnetcore9.0环境
|
||||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine-arm64v8 AS base
|
#FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine-arm64v8 AS base
|
||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:8.0-noble-arm64v8 AS base
|
||||||
COPY . /app
|
COPY . /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
#默认web
|
#默认web
|
||||||
@@ -13,6 +14,8 @@ EXPOSE 5000
|
|||||||
|
|
||||||
# 添加时区环境变量,亚洲,上海
|
# 添加时区环境变量,亚洲,上海
|
||||||
ENV TimeZone=Asia/Shanghai
|
ENV TimeZone=Asia/Shanghai
|
||||||
|
# 转发头
|
||||||
|
ENV ASPNETCORE_FORWARDEDHEADERS_ENABLED=true
|
||||||
# 使用软连接,并且将时区配置覆盖/etc/timezone
|
# 使用软连接,并且将时区配置覆盖/etc/timezone
|
||||||
RUN ln -snf /usr/share/zoneinfo/$TimeZone /etc/localtime && echo $TimeZone > /etc/timezone
|
RUN ln -snf /usr/share/zoneinfo/$TimeZone /etc/localtime && echo $TimeZone > /etc/timezone
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
<BlazorReconnector @rendermode="new InteractiveServerRenderMode(false)" />
|
<BlazorReconnector @rendermode="new InteractiveServerRenderMode(false)" />
|
||||||
|
|
||||||
<script src=@($"_content/BootstrapBlazor/js/bootstrap.blazor.bundle.min.js?v={this.GetType().Assembly.GetName().Version}")></script>
|
<script src=@($"_content/BootstrapBlazor/js/bootstrap.blazor.bundle.min.js?v={this.GetType().Assembly.GetName().Version}")></script>
|
||||||
<script src=@($"{WebsiteConst.DefaultResourceUrl}js/culture.js?v={this.GetType().Assembly.GetName().Version}")></script>
|
<script src=@($"{WebsiteConst.DefaultResourceUrl}js/localStorageUtil.js?v={this.GetType().Assembly.GetName().Version}")></script>
|
||||||
<script src="_framework/blazor.web.js"></script>
|
<script src="_framework/blazor.web.js"></script>
|
||||||
<!-- PWA Service Worker -->
|
<!-- PWA Service Worker -->
|
||||||
<script type="text/javascript">'serviceWorker' in navigator && navigator.serviceWorker.register('./service-worker.js')</script>
|
<script type="text/javascript">'serviceWorker' in navigator && navigator.serviceWorker.register('./service-worker.js')</script>
|
||||||
|
|||||||
@@ -89,8 +89,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</Side>
|
</Side>
|
||||||
<Main>
|
<Main>
|
||||||
<Tab @ref=_tab ClickTabToNavigation="true" ShowToolbar="true" ShowContextMenu="true" ShowContextMenuFullScreen="true" ShowExtendButtons="false" ShowClose="true" AllowDrag=true
|
<Tab @ref=_tab ClickTabToNavigation="true" ShowToolbar="true" ShowContextMenu="true" ShowExtendButtons="false" ShowClose="true" AllowDrag=true
|
||||||
AdditionalAssemblies="@App.RazorAssemblies" Menus="@MenuService.AllOwnMenuItems"
|
ShowFullscreenToolbarButton=false ShowContextMenuFullScreen=false ShowFullScreen=false AdditionalAssemblies="@App.RazorAssemblies" Menus="@MenuService.AllOwnMenuItems"
|
||||||
DefaultUrl=@("/") Body=@(Body!) OnCloseTabItemAsync=@((a)=>
|
DefaultUrl=@("/") Body=@(Body!) OnCloseTabItemAsync=@((a)=>
|
||||||
{
|
{
|
||||||
return Task.FromResult(!(a.Url=="/"||a.Url.IsNullOrEmpty()));
|
return Task.FromResult(!(a.Url=="/"||a.Url.IsNullOrEmpty()));
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
</app>
|
</app>
|
||||||
|
|
||||||
<script src="_content/BootstrapBlazor/js/bootstrap.blazor.bundle.min.js"></script>
|
<script src="_content/BootstrapBlazor/js/bootstrap.blazor.bundle.min.js"></script>
|
||||||
<script src=@($"{WebsiteConst.DefaultResourceUrl}js/culture.js")></script>
|
<script src=@($"{WebsiteConst.DefaultResourceUrl}js/localStorageUtil.js")></script>
|
||||||
<script src="_framework/blazor.server.js"></script>
|
<script src="_framework/blazor.server.js"></script>
|
||||||
|
|
||||||
<!-- PWA Service Worker -->
|
<!-- PWA Service Worker -->
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ using System.Text;
|
|||||||
|
|
||||||
using ThingsGateway.Admin.Application;
|
using ThingsGateway.Admin.Application;
|
||||||
using ThingsGateway.DB;
|
using ThingsGateway.DB;
|
||||||
|
using ThingsGateway.NewLife;
|
||||||
using ThingsGateway.NewLife.Log;
|
using ThingsGateway.NewLife.Log;
|
||||||
|
|
||||||
namespace ThingsGateway.AdminServer;
|
namespace ThingsGateway.AdminServer;
|
||||||
@@ -64,7 +65,7 @@ public class Program
|
|||||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||||
builder.Host.UseSystemd();
|
builder.Host.UseSystemd();
|
||||||
|
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if (Runtime.IsLegacyWindows)
|
||||||
builder.Logging.ClearProviders(); //去除默认的事件日志提供者,某些情况下会日志输出异常,导致程序崩溃
|
builder.Logging.ClearProviders(); //去除默认的事件日志提供者,某些情况下会日志输出异常,导致程序崩溃
|
||||||
}).ConfigureBuilder(builder =>
|
}).ConfigureBuilder(builder =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -45,11 +45,11 @@ public class Startup : AppStartup
|
|||||||
options.ServicesStopConcurrently = true;
|
options.ServicesStopConcurrently = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
//// 事件总线
|
// 事件总线
|
||||||
//services.AddEventBus(options =>
|
services.AddEventBus(options =>
|
||||||
//{
|
{
|
||||||
|
|
||||||
//});
|
});
|
||||||
|
|
||||||
// 任务调度
|
// 任务调度
|
||||||
services.AddSchedule(options => options.AddPersistence<JobPersistence>());
|
services.AddSchedule(options => options.AddPersistence<JobPersistence>());
|
||||||
@@ -183,19 +183,22 @@ public class Startup : AppStartup
|
|||||||
services.AddScoped<IAuthorizationHandler, BlazorServerAuthenticationHandler>();
|
services.AddScoped<IAuthorizationHandler, BlazorServerAuthenticationHandler>();
|
||||||
services.AddScoped<AuthenticationStateProvider, BlazorServerAuthenticationStateProvider>();
|
services.AddScoped<AuthenticationStateProvider, BlazorServerAuthenticationStateProvider>();
|
||||||
|
|
||||||
|
if (!NewLife.Runtime.IsLegacyWindows)
|
||||||
|
{
|
||||||
#if NET9_0_OR_GREATER
|
#if NET9_0_OR_GREATER
|
||||||
var certificate = X509CertificateLoader.LoadPkcs12FromFile("ThingsGateway.pfx", "ThingsGateway", X509KeyStorageFlags.EphemeralKeySet);
|
var certificate = X509CertificateLoader.LoadPkcs12FromFile("ThingsGateway.pfx", "ThingsGateway", X509KeyStorageFlags.EphemeralKeySet);
|
||||||
#else
|
#else
|
||||||
var certificate = new X509Certificate2("ThingsGateway.pfx", "ThingsGateway", X509KeyStorageFlags.EphemeralKeySet);
|
var certificate = new X509Certificate2("ThingsGateway.pfx", "ThingsGateway", X509KeyStorageFlags.EphemeralKeySet);
|
||||||
#endif
|
#endif
|
||||||
services.AddDataProtection()
|
services.AddDataProtection()
|
||||||
.PersistKeysToFileSystem(new DirectoryInfo("Keys"))
|
.PersistKeysToFileSystem(new DirectoryInfo("Keys"))
|
||||||
.ProtectKeysWithCertificate(certificate)
|
.ProtectKeysWithCertificate(certificate)
|
||||||
.UseCryptographicAlgorithms(new AuthenticatedEncryptorConfiguration
|
.UseCryptographicAlgorithms(new AuthenticatedEncryptorConfiguration
|
||||||
{
|
{
|
||||||
EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
|
EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
|
||||||
ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
|
ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Use(IApplicationBuilder applicationBuilder, IWebHostEnvironment env)
|
public void Use(IApplicationBuilder applicationBuilder, IWebHostEnvironment env)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net8.0;net9.0;</TargetFrameworks>
|
<TargetFrameworks>net8.0;net9.0;</TargetFrameworks>
|
||||||
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<!--<Import Project="Admin.targets" Condition=" '$(Configuration)' != 'Debug' " />-->
|
<!--<Import Project="Admin.targets" Condition=" '$(Configuration)' != 'Debug' " />-->
|
||||||
|
|||||||
@@ -30,9 +30,27 @@ public class ImportPreviewOutputBase
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 返回状态
|
/// 返回状态
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ConcurrentList<(int Row, bool Success, string? ErrorMessage)> Results { get; set; } = new();
|
public ConcurrentList<ImportPreviewResult> Results { get; set; } = new();
|
||||||
}
|
}
|
||||||
|
public class ImportPreviewResult
|
||||||
|
{
|
||||||
|
public ImportPreviewResult()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
public ImportPreviewResult(int row, bool success, string error)
|
||||||
|
{
|
||||||
|
this.Row = row;
|
||||||
|
this.Success = success;
|
||||||
|
this.ErrorMessage = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Row { get; set; }
|
||||||
|
|
||||||
|
public bool Success { get; set; }
|
||||||
|
|
||||||
|
public string? ErrorMessage { get; set; }
|
||||||
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 导入预览
|
/// 导入预览
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ThingsGateway.Gateway.Application;
|
namespace ThingsGateway.Common;
|
||||||
|
|
||||||
public class SmartTriggerScheduler
|
public class SmartTriggerScheduler
|
||||||
{
|
{
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ThingsGateway.Gateway.Application;
|
namespace ThingsGateway.Common;
|
||||||
|
|
||||||
public sealed class StringOrdinalIgnoreCaseEqualityComparer : EqualityComparer<string>
|
public sealed class StringOrdinalIgnoreCaseEqualityComparer : EqualityComparer<string>
|
||||||
{
|
{
|
||||||
@@ -443,10 +443,7 @@ public static class ObjectExtensions
|
|||||||
where TAttribute : Attribute
|
where TAttribute : Attribute
|
||||||
{
|
{
|
||||||
// 空检查
|
// 空检查
|
||||||
if (type == null)
|
ArgumentNullException.ThrowIfNull(type);
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(type));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查特性并获取特性对象
|
// 检查特性并获取特性对象
|
||||||
return type.IsDefined(typeof(TAttribute), inherit)
|
return type.IsDefined(typeof(TAttribute), inherit)
|
||||||
|
|||||||
@@ -27,7 +27,11 @@ public class WebsiteOptions : IConfigurableOptions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Demo { get; set; }
|
public bool Demo { get; set; }
|
||||||
|
|
||||||
public bool WebPageEnable { get; set; } = true;
|
|
||||||
|
public int MaxBlazorConnections { get; set; } = 5;
|
||||||
|
public bool BlazorConnectionLimitEnable { get; set; } = false;
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否显示关于页面
|
/// 是否显示关于页面
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||||
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net8.0;net9.0;</TargetFrameworks>
|
<TargetFrameworks>net8.0;net9.0;</TargetFrameworks>
|
||||||
@@ -13,7 +14,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.6" />
|
<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.6" />
|
||||||
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
|
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
|
||||||
<PackageReference Include="BootstrapBlazor" Version="9.8.1" />
|
<PackageReference Include="BootstrapBlazor" Version="9.10.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ public abstract class PrimaryIdEntity : IPrimaryIdEntity
|
|||||||
[SugarColumn(ColumnDescription = "Id", IsPrimaryKey = true)]
|
[SugarColumn(ColumnDescription = "Id", IsPrimaryKey = true)]
|
||||||
[IgnoreExcel]
|
[IgnoreExcel]
|
||||||
[AutoGenerateColumn(Visible = false, IsVisibleWhenEdit = false, IsVisibleWhenAdd = false, Sortable = true, DefaultSort = true, DefaultSortOrder = SortOrder.Asc)]
|
[AutoGenerateColumn(Visible = false, IsVisibleWhenEdit = false, IsVisibleWhenAdd = false, Sortable = true, DefaultSort = true, DefaultSortOrder = SortOrder.Asc)]
|
||||||
|
[System.ComponentModel.DataAnnotations.Key]
|
||||||
public virtual long Id { get; set; }
|
public virtual long Id { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
// ------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Components.Forms;
|
using Microsoft.AspNetCore.Components.Forms;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
namespace ThingsGateway.DB;
|
namespace ThingsGateway.DB;
|
||||||
|
|
||||||
@@ -41,4 +42,31 @@ public static class FileExtensions
|
|||||||
}
|
}
|
||||||
return fileName;
|
return fileName;
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 存储本地文件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pPath">存储的第一层目录</param>
|
||||||
|
/// <param name="file"></param>
|
||||||
|
/// <returns>文件全路径</returns>
|
||||||
|
public static async Task<string> StorageLocal(this IFormFile file, string pPath = "imports")
|
||||||
|
{
|
||||||
|
string uploadFileFolder = App.WebHostEnvironment?.WebRootPath ?? "wwwroot"!;//赋值路径
|
||||||
|
var now = CommonUtils.GetSingleId();
|
||||||
|
var filePath = Path.Combine(uploadFileFolder, pPath);
|
||||||
|
if (!Directory.Exists(filePath))//如果不存在就创建文件夹
|
||||||
|
Directory.CreateDirectory(filePath);
|
||||||
|
//var fileSuffix = Path.GetExtension(file.Name).ToLower();// 文件后缀
|
||||||
|
var fileObjectName = $"{now}{file.Name}";//存储后的文件名
|
||||||
|
var fileName = Path.Combine(filePath, fileObjectName);//获取文件全路径
|
||||||
|
fileName = fileName.Replace("\\", "/");//格式化一系
|
||||||
|
//存储文件
|
||||||
|
using (var stream = File.Create(Path.Combine(filePath, fileObjectName)))
|
||||||
|
{
|
||||||
|
await file.CopyToAsync(stream).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ public static class QueryPageOptionsExtensions
|
|||||||
return datas;
|
return datas;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static IEnumerable<T> GetQuery<T>(this IEnumerable<T> query, QueryPageOptions option, Func<IEnumerable<T>, IEnumerable<T>>? queryFunc = null, FilterKeyValueAction where = null)
|
public static IEnumerable<T> GetQuery<T>(this IEnumerable<T> query, QueryPageOptions option, Func<IEnumerable<T>, IEnumerable<T>>? queryFunc = null, FilterKeyValueAction where = null)
|
||||||
{
|
{
|
||||||
if (queryFunc != null)
|
if (queryFunc != null)
|
||||||
@@ -123,7 +125,36 @@ public static class QueryPageOptionsExtensions
|
|||||||
};
|
};
|
||||||
var items = datas.GetData(option, out var totalCount, where);
|
var items = datas.GetData(option, out var totalCount, where);
|
||||||
ret.TotalCount = totalCount;
|
ret.TotalCount = totalCount;
|
||||||
|
|
||||||
|
if (totalCount > 0)
|
||||||
|
{
|
||||||
|
if (!items.Any() && option.PageIndex != 1)
|
||||||
|
{
|
||||||
|
option.PageIndex = 1;
|
||||||
|
items = datas.GetData(option, out totalCount, where);
|
||||||
|
}
|
||||||
|
}
|
||||||
ret.Items = items.ToList();
|
ret.Items = items.ToList();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据查询条件返回QueryData
|
||||||
|
/// </summary>
|
||||||
|
public static QueryData<SelectedItem> GetQueryData<T>(this IEnumerable<T> datas, VirtualizeQueryOption option, Func<IEnumerable<T>, IEnumerable<SelectedItem>> func, FilterKeyValueAction where = null)
|
||||||
|
{
|
||||||
|
var ret = new QueryData<SelectedItem>()
|
||||||
|
{
|
||||||
|
IsSorted = false,
|
||||||
|
IsFiltered = false,
|
||||||
|
IsAdvanceSearch = false,
|
||||||
|
IsSearch = !option.SearchText.IsNullOrWhiteSpace()
|
||||||
|
};
|
||||||
|
|
||||||
|
var items = datas.Skip((option.StartIndex)).Take(option.Count);
|
||||||
|
ret.TotalCount = datas.Count();
|
||||||
|
|
||||||
|
ret.Items = func(items).ToList();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -209,16 +209,10 @@ public static class SqlSugarExtensions
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public static async Task<bool> UpdateRangeAsync<T>(this SqlSugarClient db, List<T> updateObjs) where T : class, new()
|
public static Task<int> UpdateSetColumnsTrueAsync<T>(this SqlSugarClient db, Expression<Func<T, T>> columns, Expression<Func<T, bool>> whereExpression) where T : class, new()
|
||||||
{
|
{
|
||||||
return await db.Updateable(updateObjs).ExecuteCommandAsync().ConfigureAwait(false) > 0;
|
return db.Updateable<T>().SetColumns(columns, appendColumnsByDataFilter: true).Where(whereExpression)
|
||||||
}
|
.ExecuteCommandAsync();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public static async Task<bool> UpdateSetColumnsTrueAsync<T>(this SqlSugarClient db, Expression<Func<T, T>> columns, Expression<Func<T, bool>> whereExpression) where T : class, new()
|
|
||||||
{
|
|
||||||
return await db.Updateable<T>().SetColumns(columns, appendColumnsByDataFilter: true).Where(whereExpression)
|
|
||||||
.ExecuteCommandAsync().ConfigureAwait(false) > 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<T> Sort<T>(this IEnumerable<T> list, BasePageInput basePageInput)
|
private static IEnumerable<T> Sort<T>(this IEnumerable<T> list, BasePageInput basePageInput)
|
||||||
|
|||||||
13
src/Admin/ThingsGateway.DB/Locales/en-US.json
Normal file
13
src/Admin/ThingsGateway.DB/Locales/en-US.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
|
||||||
|
"ThingsGateway.Admin.Application.BaseDataEntity": {
|
||||||
|
"CreateOrgId": "CreateOrgId"
|
||||||
|
},
|
||||||
|
"ThingsGateway.Admin.Application.BaseEntity": {
|
||||||
|
"CreateTime": "CreateTime",
|
||||||
|
"CreateUser": "CreateUser",
|
||||||
|
"SortCode": "SortCode",
|
||||||
|
"UpdateTime": "UpdateTime",
|
||||||
|
"UpdateUser": "UpdateUser"
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/Admin/ThingsGateway.DB/Locales/zh-CN.json
Normal file
13
src/Admin/ThingsGateway.DB/Locales/zh-CN.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
|
||||||
|
"ThingsGateway.DB.BaseDataEntity": {
|
||||||
|
"CreateOrgId": "创建机构Id"
|
||||||
|
},
|
||||||
|
"ThingsGateway.DB.BaseEntity": {
|
||||||
|
"CreateTime": "创建时间",
|
||||||
|
"CreateUser": "创建人",
|
||||||
|
"SortCode": "排序",
|
||||||
|
"UpdateTime": "更新时间",
|
||||||
|
"UpdateUser": "更新人"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,12 +43,12 @@ public class SugarAopService : ISugarAopService
|
|||||||
}
|
}
|
||||||
if (sql.StartsWith("INSERT"))
|
if (sql.StartsWith("INSERT"))
|
||||||
{
|
{
|
||||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
Console.ForegroundColor = ConsoleColor.Blue;
|
||||||
DbContext.WriteLog($"添加{config.ConfigId}库操作");
|
DbContext.WriteLog($"添加{config.ConfigId}库操作");
|
||||||
}
|
}
|
||||||
if (sql.StartsWith("DELETE"))
|
if (sql.StartsWith("DELETE"))
|
||||||
{
|
{
|
||||||
Console.ForegroundColor = ConsoleColor.Red;
|
Console.ForegroundColor = ConsoleColor.Blue;
|
||||||
DbContext.WriteLog($"删除{config.ConfigId}库操作");
|
DbContext.WriteLog($"删除{config.ConfigId}库操作");
|
||||||
}
|
}
|
||||||
DbContext.WriteLogWithSql(UtilMethods.GetNativeSql(sql, pars));
|
DbContext.WriteLogWithSql(UtilMethods.GetNativeSql(sql, pars));
|
||||||
@@ -62,7 +62,7 @@ public class SugarAopService : ISugarAopService
|
|||||||
if (ex.Parameters == null) return;
|
if (ex.Parameters == null) return;
|
||||||
Console.ForegroundColor = ConsoleColor.Red;
|
Console.ForegroundColor = ConsoleColor.Red;
|
||||||
DbContext.WriteLog($"{config.ConfigId}库操作异常");
|
DbContext.WriteLog($"{config.ConfigId}库操作异常");
|
||||||
DbContext.WriteErrorLogWithSql(UtilMethods.GetNativeSql(ex.Sql, (SugarParameter[])ex.Parameters));
|
DbContext.WriteErrorLogWithSql(UtilMethods.GetNativeSql(ex.Sql, ex.Parameters));
|
||||||
NewLife.Log.XTrace.WriteException(ex);
|
NewLife.Log.XTrace.WriteException(ex);
|
||||||
Console.ForegroundColor = ConsoleColor.White;
|
Console.ForegroundColor = ConsoleColor.White;
|
||||||
};
|
};
|
||||||
@@ -110,10 +110,6 @@ public class SugarAopService : ISugarAopService
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//查询数据转换
|
|
||||||
db.Aop.DataExecuted = (value, entity) =>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
db.Aop.OnLogExecuted = (sql, pars) =>
|
db.Aop.OnLogExecuted = (sql, pars) =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public class BaseService<T> : IDataService<T>, IDisposable where T : class, new(
|
|||||||
public async Task<bool> DeleteAsync(IEnumerable<T> models)
|
public async Task<bool> DeleteAsync(IEnumerable<T> models)
|
||||||
{
|
{
|
||||||
using var db = GetDB();
|
using var db = GetDB();
|
||||||
return await db.Deleteable<T>().In(models.ToList()).ExecuteCommandHasChangeAsync().ConfigureAwait(false);
|
return await db.Deleteable<T>(models.ToList()).ExecuteCommandHasChangeAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -140,18 +140,22 @@ public class BaseService<T> : IDataService<T>, IDisposable where T : class, new(
|
|||||||
return (await db.UpdateableT(model).ExecuteCommandAsync().ConfigureAwait(false)) > 0;
|
return (await db.UpdateableT(model).ExecuteCommandAsync().ConfigureAwait(false)) > 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public virtual async Task<bool> SaveAsync(List<T> model, ItemChangedType changedType)
|
public virtual async Task<bool> SaveAsync(List<T> model, ItemChangedType changedType)
|
||||||
|
{
|
||||||
|
return (await SaveReturnCountAsync(model, changedType).ConfigureAwait(false)) > 0;
|
||||||
|
}
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public async Task<int> SaveReturnCountAsync(List<T> model, ItemChangedType changedType)
|
||||||
{
|
{
|
||||||
using var db = GetDB();
|
using var db = GetDB();
|
||||||
if (changedType == ItemChangedType.Add)
|
if (changedType == ItemChangedType.Add)
|
||||||
{
|
{
|
||||||
return (await db.Insertable(model).ExecuteCommandAsync().ConfigureAwait(false)) > 0;
|
return (await db.Insertable(model).ExecuteCommandAsync().ConfigureAwait(false));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return (await db.Updateable(model).ExecuteCommandAsync().ConfigureAwait(false)) > 0;
|
return (await db.Updateable(model).ExecuteCommandAsync().ConfigureAwait(false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ public static class CodeFirstUtils
|
|||||||
var seedDataTypes = App.EffectiveTypes
|
var seedDataTypes = App.EffectiveTypes
|
||||||
.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass
|
.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass
|
||||||
&& u.GetInterfaces().Any(i => i.HasImplementedRawGeneric(typeof(ISqlSugarEntitySeedData<>))) && u.Assembly.FullName == assemblyName);
|
&& u.GetInterfaces().Any(i => i.HasImplementedRawGeneric(typeof(ISqlSugarEntitySeedData<>))) && u.Assembly.FullName == assemblyName);
|
||||||
if (!seedDataTypes.Any()) return;
|
|
||||||
foreach (var seedType in seedDataTypes)//遍历种子类
|
foreach (var seedType in seedDataTypes)//遍历种子类
|
||||||
{
|
{
|
||||||
//使用与指定参数匹配程度最高的构造函数来创建指定类型的实例。
|
//使用与指定参数匹配程度最高的构造函数来创建指定类型的实例。
|
||||||
@@ -62,10 +61,12 @@ public static class CodeFirstUtils
|
|||||||
// seedDataTable.TableName = db.EntityMaintenance.GetEntityInfo(entityType).DbTableName;//获取表名
|
// seedDataTable.TableName = db.EntityMaintenance.GetEntityInfo(entityType).DbTableName;//获取表名
|
||||||
var ignoreAdd = seedDataMethod!.GetCustomAttribute<IgnoreSeedDataAddAttribute>();//读取忽略插入特性
|
var ignoreAdd = seedDataMethod!.GetCustomAttribute<IgnoreSeedDataAddAttribute>();//读取忽略插入特性
|
||||||
var ignoreUpdate = seedDataMethod!.GetCustomAttribute<IgnoreSeedDataUpdateAttribute>();//读取忽略更新特性
|
var ignoreUpdate = seedDataMethod!.GetCustomAttribute<IgnoreSeedDataUpdateAttribute>();//读取忽略更新特性
|
||||||
|
|
||||||
|
var seedDataList = seedData.ToList();
|
||||||
if (entityInfo.Columns.Any(u => u.IsPrimarykey))//判断种子数据是否有主键
|
if (entityInfo.Columns.Any(u => u.IsPrimarykey))//判断种子数据是否有主键
|
||||||
{
|
{
|
||||||
// 按主键进行批量增加和更新
|
// 按主键进行批量增加和更新
|
||||||
var storage = db.StorageableByObject(seedData.ToList()).ToStorage();
|
var storage = db.StorageableByObject(seedDataList).ToStorage();
|
||||||
if (ignoreAdd == null)
|
if (ignoreAdd == null)
|
||||||
storage.AsInsertable.ExecuteCommand();//执行插入
|
storage.AsInsertable.ExecuteCommand();//执行插入
|
||||||
if (ignoreUpdate == null && config.IsUpdateSeedData) storage.AsUpdateable.ExecuteCommand();//只有没有忽略更新的特性才执行更新
|
if (ignoreUpdate == null && config.IsUpdateSeedData) storage.AsUpdateable.ExecuteCommand();//只有没有忽略更新的特性才执行更新
|
||||||
@@ -75,7 +76,7 @@ public static class CodeFirstUtils
|
|||||||
//全量插入
|
//全量插入
|
||||||
// 无主键则只进行插入
|
// 无主键则只进行插入
|
||||||
if (!db.Queryable(entityInfo.DbTableName, entityInfo.DbTableName).Any() && ignoreAdd == null)
|
if (!db.Queryable(entityInfo.DbTableName, entityInfo.DbTableName).Any() && ignoreAdd == null)
|
||||||
db.InsertableByObject(seedData.ToList()).ExecuteCommand();
|
db.InsertableByObject(seedDataList).ExecuteCommand();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,7 +90,6 @@ public static class CodeFirstUtils
|
|||||||
// 获取所有实体表-初始化表结构
|
// 获取所有实体表-初始化表结构
|
||||||
var entityTypes = App.EffectiveTypes.Where(u =>
|
var entityTypes = App.EffectiveTypes.Where(u =>
|
||||||
!u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false) && u.Assembly.FullName == assemblyName);
|
!u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false) && u.Assembly.FullName == assemblyName);
|
||||||
if (!entityTypes.Any()) return;//没有就退出
|
|
||||||
foreach (var entityType in entityTypes)
|
foreach (var entityType in entityTypes)
|
||||||
{
|
{
|
||||||
var tenantAtt = entityType.GetCustomAttribute<TenantAttribute>();//获取Sqlsugar多库特性
|
var tenantAtt = entityType.GetCustomAttribute<TenantAttribute>();//获取Sqlsugar多库特性
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ public static class DbContext
|
|||||||
await db.Fastest<TITEM>().PageSize(size).BulkCopyAsync(datas).ConfigureAwait(false);
|
await db.Fastest<TITEM>().PageSize(size).BulkCopyAsync(datas).ConfigureAwait(false);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
await db.Insertable(datas is IReadOnlyList<TITEM> values ? values : datas.ToList()).PageSize(size).ExecuteCommandAsync().ConfigureAwait(false);
|
await db.Insertable(datas is IReadOnlyCollection<TITEM> values ? values : datas.ToList()).PageSize(size).ExecuteCommandAsync().ConfigureAwait(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,7 +155,7 @@ public static class DbContext
|
|||||||
await db.Fastest<TITEM>().PageSize(size).BulkUpdateAsync(datas).ConfigureAwait(false);
|
await db.Fastest<TITEM>().PageSize(size).BulkUpdateAsync(datas).ConfigureAwait(false);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
await db.Updateable(datas is IReadOnlyList<TITEM> values ? values : datas.ToList()).PageSize(size).ExecuteCommandAsync().ConfigureAwait(false);
|
await db.Updateable(datas is IReadOnlyCollection<TITEM> values ? values : datas.ToList()).PageSize(size).ExecuteCommandAsync().ConfigureAwait(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||||
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net8.0;net9.0;</TargetFrameworks>
|
<TargetFrameworks>net8.0;net9.0;</TargetFrameworks>
|
||||||
@@ -18,6 +19,12 @@
|
|||||||
<None Remove="..\..\..\README.zh-CN.md" Pack="false" PackagePath="\" />
|
<None Remove="..\..\..\README.zh-CN.md" Pack="false" PackagePath="\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="Locales\en-US.json" />
|
||||||
|
<EmbeddedResource Include="Locales\zh-CN.json" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<!--<PackageReference Include="ThingsGateway.Razor" Version="$(SourceGeneratorVersion)" />-->
|
<!--<PackageReference Include="ThingsGateway.Razor" Version="$(SourceGeneratorVersion)" />-->
|
||||||
<!--<ProjectReference Include="..\ThingsGateway.Razor\ThingsGateway.Razor.csproj" />-->
|
<!--<ProjectReference Include="..\ThingsGateway.Razor\ThingsGateway.Razor.csproj" />-->
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
https://gitee.com/dotnetchina/Furion/commit/8bf85f6908c1630268e45eeec607267a03947d2b
|
https://gitee.com/dotnetchina/Furion/commit/f1c07d65cccb623aca9d1905bf2e1ac6e4f4b714
|
||||||
@@ -27,18 +27,27 @@ using System.Security.Claims;
|
|||||||
using ThingsGateway.ConfigurableOptions;
|
using ThingsGateway.ConfigurableOptions;
|
||||||
using ThingsGateway.NewLife.Caching;
|
using ThingsGateway.NewLife.Caching;
|
||||||
using ThingsGateway.NewLife.Collections;
|
using ThingsGateway.NewLife.Collections;
|
||||||
|
using ThingsGateway.NewLife.Extension;
|
||||||
using ThingsGateway.NewLife.Log;
|
using ThingsGateway.NewLife.Log;
|
||||||
using ThingsGateway.Reflection;
|
using ThingsGateway.Reflection;
|
||||||
using ThingsGateway.Templates;
|
using ThingsGateway.Templates;
|
||||||
|
|
||||||
namespace ThingsGateway;
|
namespace ThingsGateway;
|
||||||
|
|
||||||
|
|
||||||
|
public static class WebEnableVariable
|
||||||
|
{
|
||||||
|
public static bool WebEnable => Environment.GetEnvironmentVariable(nameof(WebEnable)).ToBoolean(true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 全局应用类
|
/// 全局应用类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SuppressSniffer]
|
[SuppressSniffer]
|
||||||
public static class App
|
public static class App
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 私有设置,避免重复解析
|
/// 私有设置,避免重复解析
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -157,7 +166,7 @@ public static class App
|
|||||||
var httpContextAccessor = RootServices?.GetService<IHttpContextAccessor>();
|
var httpContextAccessor = RootServices?.GetService<IHttpContextAccessor>();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return httpContextAccessor.HttpContext;
|
return httpContextAccessor?.HttpContext;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -545,10 +554,9 @@ public static class App
|
|||||||
{
|
{
|
||||||
types = ass.GetTypes();
|
types = ass.GetTypes();
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
XTrace.Log.Warn($"Error load `{ass.FullName}` assembly.");
|
XTrace.Log.Warn($"Error load `{ass.FullName}` assembly. : {ex.Message}");
|
||||||
Console.WriteLine($"Error load `{ass.FullName}` assembly.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return types.Where(u => u.IsPublic && !u.IsDefined(typeof(SuppressSnifferAttribute), false));
|
return types.Where(u => u.IsPublic && !u.IsDefined(typeof(SuppressSnifferAttribute), false));
|
||||||
|
|||||||
@@ -213,12 +213,18 @@ public static class AppServiceCollectionExtensions
|
|||||||
// 缓存
|
// 缓存
|
||||||
if (cacheOptions.CacheType == CacheType.Memory)
|
if (cacheOptions.CacheType == CacheType.Memory)
|
||||||
{
|
{
|
||||||
services.AddSingleton<ICache, MemoryCache>(a => new()
|
services.AddSingleton<ICache>(a =>
|
||||||
{
|
{
|
||||||
Capacity = cacheOptions.MemoryCacheOptions.Capacity,
|
Cache.Default = new MemoryCache()
|
||||||
Expire = cacheOptions.MemoryCacheOptions.Expire,
|
{
|
||||||
Period = cacheOptions.MemoryCacheOptions.Period
|
Capacity = cacheOptions.MemoryCacheOptions.Capacity,
|
||||||
});
|
Expire = cacheOptions.MemoryCacheOptions.Expire,
|
||||||
|
Period = cacheOptions.MemoryCacheOptions.Period
|
||||||
|
};
|
||||||
|
return Cache.Default;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (cacheOptions.CacheType == CacheType.Redis)
|
else if (cacheOptions.CacheType == CacheType.Redis)
|
||||||
{
|
{
|
||||||
@@ -278,7 +284,6 @@ public static class AppServiceCollectionExtensions
|
|||||||
&& u.GetParameters().Length > 0
|
&& u.GetParameters().Length > 0
|
||||||
&& u.GetParameters().First().ParameterType == typeof(IServiceCollection));
|
&& u.GetParameters().First().ParameterType == typeof(IServiceCollection));
|
||||||
|
|
||||||
if (!serviceMethods.Any()) continue;
|
|
||||||
|
|
||||||
// 自动安装属性调用
|
// 自动安装属性调用
|
||||||
foreach (var method in serviceMethods)
|
foreach (var method in serviceMethods)
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ public static class HostBuilderExtensions
|
|||||||
|
|
||||||
hostBuilder.UseSetting(WebHostDefaults.HostingStartupAssembliesKey, combineAssembliesName);
|
hostBuilder.UseSetting(WebHostDefaults.HostingStartupAssembliesKey, combineAssembliesName);
|
||||||
|
|
||||||
// 实现假的 Starup,解决泛型主机启动问题
|
// 实现假的 Startup,解决泛型主机启动问题
|
||||||
hostBuilder.UseStartup<FakeStartup>();
|
hostBuilder.UseStartup<FakeStartup>();
|
||||||
return hostBuilder;
|
return hostBuilder;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ using System.Runtime.CompilerServices;
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
using ThingsGateway.NewLife;
|
||||||
|
|
||||||
namespace ThingsGateway.Extensions;
|
namespace ThingsGateway.Extensions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -187,7 +189,7 @@ public static class ObjectExtensions
|
|||||||
{
|
{
|
||||||
if (current == last) func(arg);
|
if (current == last) func(arg);
|
||||||
task.Dispose();
|
task.Dispose();
|
||||||
});
|
}, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,8 +209,8 @@ public static class ObjectExtensions
|
|||||||
Task.Delay(milliseconds).ContinueWith(task =>
|
Task.Delay(milliseconds).ContinueWith(task =>
|
||||||
{
|
{
|
||||||
if (current == last) func();
|
if (current == last) func();
|
||||||
task.Dispose();
|
task.TryDispose();
|
||||||
});
|
}, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ public class StartupFilter : IStartupFilter
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 输出当前环境标识
|
// 输出当前环境标识
|
||||||
context.Response.Headers["environment"] = envName;
|
context.Response.Headers["Environment"] = envName;
|
||||||
|
|
||||||
// 输出框架版本
|
// 输出框架版本
|
||||||
context.Response.Headers[nameof(ThingsGateway)] = version;
|
context.Response.Headers[nameof(ThingsGateway)] = version;
|
||||||
|
|||||||
@@ -85,11 +85,14 @@ internal static class InternalApp
|
|||||||
// 存储根服务(解决 Web 主机还未启动时在 HostedService 中使用 App.GetService 问题
|
// 存储根服务(解决 Web 主机还未启动时在 HostedService 中使用 App.GetService 问题
|
||||||
services.AddHostedService<GenericHostLifetimeEventsHostedService>();
|
services.AddHostedService<GenericHostLifetimeEventsHostedService>();
|
||||||
|
|
||||||
// 注册 Startup 过滤器
|
if (WebEnableVariable.WebEnable == true)
|
||||||
services.AddTransient<IStartupFilter, StartupFilter>();
|
{
|
||||||
|
// 注册 Startup 过滤器
|
||||||
|
services.AddTransient<IStartupFilter, StartupFilter>();
|
||||||
|
|
||||||
// 注册 HttpContextAccessor 服务
|
// 注册 HttpContextAccessor 服务
|
||||||
services.AddHttpContextAccessor();
|
services.AddHttpContextAccessor();
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化应用服务
|
// 初始化应用服务
|
||||||
services.AddApp();
|
services.AddApp();
|
||||||
@@ -212,6 +215,10 @@ internal static class InternalApp
|
|||||||
// 获取环境变量名,如果没找到,则读取 NETCORE_ENVIRONMENT 环境变量信息识别(用于非 Web 环境)
|
// 获取环境变量名,如果没找到,则读取 NETCORE_ENVIRONMENT 环境变量信息识别(用于非 Web 环境)
|
||||||
var envName = hostEnvironment?.EnvironmentName ?? Environment.GetEnvironmentVariable("NETCORE_ENVIRONMENT") ?? "Unknown";
|
var envName = hostEnvironment?.EnvironmentName ?? Environment.GetEnvironmentVariable("NETCORE_ENVIRONMENT") ?? "Unknown";
|
||||||
|
|
||||||
|
// 获取 JSON 文件扫描配置(2025.07.25),修复 docker 中挂载大文件数据卷导致启动缓慢的问题
|
||||||
|
var jsonFileScanner = configuration.GetSection("AppSettings:JsonFileScanner")
|
||||||
|
.Get<JsonFileScanner>() ?? new JsonFileScanner();
|
||||||
|
|
||||||
// 读取忽略的配置文件
|
// 读取忽略的配置文件
|
||||||
var ignoreConfigurationFiles = (configuration.GetSection("IgnoreConfigurationFiles")
|
var ignoreConfigurationFiles = (configuration.GetSection("IgnoreConfigurationFiles")
|
||||||
.Get<string[]>()
|
.Get<string[]>()
|
||||||
@@ -237,7 +244,7 @@ internal static class InternalApp
|
|||||||
// 循环加载
|
// 循环加载
|
||||||
foreach (var jsonFile in files)
|
foreach (var jsonFile in files)
|
||||||
{
|
{
|
||||||
configurationBuilder.AddJsonFile(jsonFile, optional: true, reloadOnChange: true);
|
configurationBuilder.AddJsonFile(jsonFile, optional: jsonFileScanner.Optional, reloadOnChange: jsonFileScanner.ReloadOnChange);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ namespace ThingsGateway;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class AppSettingsOptions : IConfigurableOptions<AppSettingsOptions>
|
public sealed class AppSettingsOptions : IConfigurableOptions<AppSettingsOptions>
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否启用规范化文档
|
/// 是否启用规范化文档
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -50,7 +51,10 @@ public sealed class AppSettingsOptions : IConfigurableOptions<AppSettingsOptions
|
|||||||
/// 【部署】二级虚拟目录
|
/// 【部署】二级虚拟目录
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string VirtualPath { get; set; }
|
public string VirtualPath { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// JSON 文件扫描配置
|
||||||
|
/// </summary>
|
||||||
|
public JsonFileScanner JsonFileScanner { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 后期配置
|
/// 后期配置
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -66,3 +70,20 @@ public sealed class AppSettingsOptions : IConfigurableOptions<AppSettingsOptions
|
|||||||
options.VirtualPath ??= string.Empty;
|
options.VirtualPath ??= string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// JSON 文件扫描配置
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>修复 docker 中挂载大文件数据卷导致启动缓慢的问题。</remarks>
|
||||||
|
public class JsonFileScanner
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 是否可选
|
||||||
|
/// </summary>
|
||||||
|
public bool Optional { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否改变的时候重载
|
||||||
|
/// </summary>
|
||||||
|
public bool ReloadOnChange { get; set; } = true;
|
||||||
|
}
|
||||||
341
src/Admin/ThingsGateway.Furion/App/Options/MiniRunOptions.cs
Normal file
341
src/Admin/ThingsGateway.Furion/App/Options/MiniRunOptions.cs
Normal file
@@ -0,0 +1,341 @@
|
|||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// 版权信息
|
||||||
|
// 版权归百小僧及百签科技(广东)有限公司所有。
|
||||||
|
// 所有权利保留。
|
||||||
|
// 官方网站:https://baiqian.com
|
||||||
|
//
|
||||||
|
// 许可证信息
|
||||||
|
// 项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。
|
||||||
|
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
|
using ThingsGateway;
|
||||||
|
|
||||||
|
namespace System;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="WebApplication"/> 方式配置选项
|
||||||
|
/// </summary>
|
||||||
|
[SuppressSniffer]
|
||||||
|
public sealed class MiniRunOptions : IRunOptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 内部构造函数
|
||||||
|
/// </summary>
|
||||||
|
internal MiniRunOptions()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 默认配置
|
||||||
|
/// </summary>
|
||||||
|
public static MiniRunOptions Default { get; } = new MiniRunOptions();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 默认配置(带启动参数)
|
||||||
|
/// </summary>
|
||||||
|
public static MiniRunOptions Main(string[] args)
|
||||||
|
{
|
||||||
|
return Default.WithArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 默认配置(静默启动)
|
||||||
|
/// </summary>
|
||||||
|
public static MiniRunOptions DefaultSilence { get; } = new MiniRunOptions().Silence();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 默认配置(静默启动 + 启动参数)
|
||||||
|
/// </summary>
|
||||||
|
public static MiniRunOptions MainSilence(string[] args)
|
||||||
|
{
|
||||||
|
return DefaultSilence.WithArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 配置 <see cref="WebApplicationOptions"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
/// <returns><see cref="MiniRunOptions"/></returns>
|
||||||
|
public MiniRunOptions ConfigureOptions(WebApplicationOptions options)
|
||||||
|
{
|
||||||
|
Options = options;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 配置 <see cref="IWebHostBuilder"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configureAction"></param>
|
||||||
|
/// <returns><see cref="MiniRunOptions"/></returns>
|
||||||
|
public MiniRunOptions ConfigureBuilder(Action<IWebHostBuilder> configureAction)
|
||||||
|
{
|
||||||
|
ActionBuilder = configureAction;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 配置 <see cref="IHostBuilder"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configureAction"></param>
|
||||||
|
/// <returns><see cref="MiniRunOptions"/></returns>
|
||||||
|
public MiniRunOptions ConfigureFirstActionBuilder(Action<IHostBuilder> configureAction)
|
||||||
|
{
|
||||||
|
FirstActionBuilder = configureAction;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 配置 <see cref="IServiceCollection"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configureAction"></param>
|
||||||
|
/// <returns><see cref="MiniRunOptions"/></returns>
|
||||||
|
public MiniRunOptions ConfigureServices(Action<IServiceCollection> configureAction)
|
||||||
|
{
|
||||||
|
ActionServices = configureAction;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 配置 <see cref="InjectOptions"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configureAction"></param>
|
||||||
|
/// <returns><see cref="MiniRunOptions"/></returns>
|
||||||
|
public MiniRunOptions ConfigureInject(Action<IWebHostBuilder, InjectOptions> configureAction)
|
||||||
|
{
|
||||||
|
ActionInject = configureAction;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 配置 <see cref="WebApplication"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configureAction">配置委托</param>
|
||||||
|
/// <returns><see cref="MiniRunOptions"/></returns>
|
||||||
|
public MiniRunOptions Configure(Action<IHost> configureAction)
|
||||||
|
{
|
||||||
|
ActionConfigure = configureAction;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 配置 <see cref="ConfigurationManager"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configureAction">配置委托</param>
|
||||||
|
/// <returns><see cref="MiniRunOptions"/></returns>
|
||||||
|
public MiniRunOptions ConfigureConfiguration(Action<IHostEnvironment, IConfiguration> configureAction)
|
||||||
|
{
|
||||||
|
ActionConfigurationManager = configureAction;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加应用服务组件
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TComponent">组件类型</typeparam>
|
||||||
|
/// <returns></returns>
|
||||||
|
public MiniRunOptions AddComponent<TComponent>()
|
||||||
|
where TComponent : class, IServiceComponent, new()
|
||||||
|
{
|
||||||
|
ServiceComponents.Add(typeof(TComponent), null);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加应用服务组件
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TComponent">组件类型</typeparam>
|
||||||
|
/// <typeparam name="TComponentOptions"></typeparam>
|
||||||
|
/// <param name="options">组件参数</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public MiniRunOptions AddComponent<TComponent, TComponentOptions>(TComponentOptions options)
|
||||||
|
where TComponent : class, IServiceComponent, new()
|
||||||
|
{
|
||||||
|
ServiceComponents.Add(typeof(TComponent), options);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加应用服务组件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="componentType">组件类型</param>
|
||||||
|
/// <param name="options">组件参数</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public MiniRunOptions AddComponent(Type componentType, object options)
|
||||||
|
{
|
||||||
|
ServiceComponents.Add(componentType, options);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加应用中间件组件
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TComponent">组件类型</typeparam>
|
||||||
|
/// <returns></returns>
|
||||||
|
public MiniRunOptions UseComponent<TComponent>()
|
||||||
|
where TComponent : class, IApplicationComponent, new()
|
||||||
|
{
|
||||||
|
ApplicationComponents.Add(typeof(TComponent), null);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加应用中间件组件
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TComponent">组件类型</typeparam>
|
||||||
|
/// <typeparam name="TComponentOptions"></typeparam>
|
||||||
|
/// <param name="options">组件参数</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public MiniRunOptions UseComponent<TComponent, TComponentOptions>(TComponentOptions options)
|
||||||
|
where TComponent : class, IApplicationComponent, new()
|
||||||
|
{
|
||||||
|
ApplicationComponents.Add(typeof(TComponent), options);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加应用中间件组件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="componentType">组件类型</param>
|
||||||
|
/// <param name="options">组件参数</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public MiniRunOptions UseComponent(Type componentType, object options)
|
||||||
|
{
|
||||||
|
ApplicationComponents.Add(componentType, options);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加 IWebHostBuilder 组件
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TComponent">组件类型</typeparam>
|
||||||
|
/// <returns></returns>
|
||||||
|
public MiniRunOptions AddWebComponent<TComponent>()
|
||||||
|
where TComponent : class, IWebComponent, new()
|
||||||
|
{
|
||||||
|
WebComponents.Add(typeof(TComponent), null);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加 IWebHostBuilder 组件
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TComponent">组件类型</typeparam>
|
||||||
|
/// <typeparam name="TComponentOptions"></typeparam>
|
||||||
|
/// <param name="options">组件参数</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public MiniRunOptions AddWebComponent<TComponent, TComponentOptions>(TComponentOptions options)
|
||||||
|
where TComponent : class, IWebComponent, new()
|
||||||
|
{
|
||||||
|
WebComponents.Add(typeof(TComponent), options);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加 IWebHostBuilder 组件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="componentType">组件类型</param>
|
||||||
|
/// <param name="options">组件参数</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public MiniRunOptions AddWebComponent(Type componentType, object options)
|
||||||
|
{
|
||||||
|
WebComponents.Add(componentType, options);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 标识主机静默启动
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>不阻塞程序运行</remarks>
|
||||||
|
/// <param name="silence">静默启动</param>
|
||||||
|
/// <param name="logging">静默启动日志状态,默认 false</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public MiniRunOptions Silence(bool silence = true, bool logging = false)
|
||||||
|
{
|
||||||
|
IsSilence = silence;
|
||||||
|
SilenceLogging = logging;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置进程启动参数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="args">启动参数</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public MiniRunOptions WithArgs(string[] args)
|
||||||
|
{
|
||||||
|
Args = args;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="WebApplicationOptions"/>
|
||||||
|
/// </summary>
|
||||||
|
internal WebApplicationOptions Options { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义 <see cref="IServiceCollection"/> 委托
|
||||||
|
/// </summary>
|
||||||
|
internal Action<IServiceCollection> ActionServices { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义 <see cref="IWebHostBuilder"/> 委托
|
||||||
|
/// </summary>
|
||||||
|
internal Action<IHostBuilder> FirstActionBuilder { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义 <see cref="IWebHostBuilder"/> 委托
|
||||||
|
/// </summary>
|
||||||
|
internal Action<IWebHostBuilder> ActionBuilder { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义 <see cref="InjectOptions"/> 委托
|
||||||
|
/// </summary>
|
||||||
|
internal Action<IWebHostBuilder, InjectOptions> ActionInject { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义 <see cref="IHost"/> 委托
|
||||||
|
/// </summary>
|
||||||
|
internal Action<IHost> ActionConfigure { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义 <see cref="IConfiguration"/> 委托
|
||||||
|
/// </summary>
|
||||||
|
internal Action<IHostEnvironment, IConfiguration> ActionConfigurationManager { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 应用服务组件
|
||||||
|
/// </summary>
|
||||||
|
internal Dictionary<Type, object> ServiceComponents { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// IWebHostBuilder 组件
|
||||||
|
/// </summary>
|
||||||
|
internal Dictionary<Type, object> WebComponents { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 应用中间件组件
|
||||||
|
/// </summary>
|
||||||
|
internal Dictionary<Type, object> ApplicationComponents { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 静默启动
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>不阻塞程序运行</remarks>
|
||||||
|
internal bool IsSilence { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 静默启动日志状态
|
||||||
|
/// </summary>
|
||||||
|
internal bool SilenceLogging { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 命令行参数
|
||||||
|
/// </summary>
|
||||||
|
internal string[] Args { get; set; }
|
||||||
|
}
|
||||||
@@ -602,6 +602,33 @@ public static class Serve
|
|||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 启动 WebApplication 主机
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>未包含 Web 基础功能,需手动注册服务/中间件</remarks>
|
||||||
|
/// <param name="options">配置选项</param>
|
||||||
|
/// <param name="urls">默认 5000/5001 端口</param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns><see cref="IHost"/></returns>
|
||||||
|
public static async Task<IHost> RunAsync(MiniRunOptions options, string urls = default, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
// 构建 WebApplication 对象
|
||||||
|
BuildMiniApplication(options, urls, out var app);
|
||||||
|
|
||||||
|
// 是否静默启动
|
||||||
|
if (!options.IsSilence)
|
||||||
|
{
|
||||||
|
// 配置启动地址和端口
|
||||||
|
await app.RunAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await app.StartAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构建 WebApplication 对象
|
/// 构建 WebApplication 对象
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -616,8 +643,8 @@ public static class Serve
|
|||||||
|
|
||||||
// 初始化 WebApplicationBuilder
|
// 初始化 WebApplicationBuilder
|
||||||
var builder = (options.Options == null
|
var builder = (options.Options == null
|
||||||
? WebApplication.CreateBuilder(args)
|
? WebApplication.CreateBuilder(args)
|
||||||
: WebApplication.CreateBuilder(options.Options));
|
: WebApplication.CreateBuilder(options.Options));
|
||||||
|
|
||||||
// 调用自定义配置服务
|
// 调用自定义配置服务
|
||||||
options?.FirstActionBuilder?.Invoke(builder);
|
options?.FirstActionBuilder?.Invoke(builder);
|
||||||
@@ -674,7 +701,7 @@ public static class Serve
|
|||||||
var applicationPartManager = app.Services.GetService<ApplicationPartManager>();
|
var applicationPartManager = app.Services.GetService<ApplicationPartManager>();
|
||||||
|
|
||||||
applicationPartManager?.ApplicationParts?.RemoveWhere(p => App.BakImageNames.Any(b => b == p.Name));
|
applicationPartManager?.ApplicationParts?.RemoveWhere(p => App.BakImageNames.Any(b => b == p.Name));
|
||||||
// 配置所有 Starup Configure
|
// 配置所有 Startup Configure
|
||||||
UseStartups(app);
|
UseStartups(app);
|
||||||
UseStartups(app.Services);
|
UseStartups(app.Services);
|
||||||
|
|
||||||
@@ -793,12 +820,138 @@ public static class Serve
|
|||||||
var applicationPartManager = app.Services.GetService<ApplicationPartManager>();
|
var applicationPartManager = app.Services.GetService<ApplicationPartManager>();
|
||||||
|
|
||||||
applicationPartManager?.ApplicationParts?.RemoveWhere(p => App.BakImageNames.Any(b => b == p.Name));
|
applicationPartManager?.ApplicationParts?.RemoveWhere(p => App.BakImageNames.Any(b => b == p.Name));
|
||||||
// 配置所有 Starup Configure
|
// 配置所有 Startup Configure
|
||||||
UseStartups(app.Services);
|
UseStartups(app.Services);
|
||||||
// 释放内存
|
// 释放内存
|
||||||
App.AppStartups.Clear();
|
App.AppStartups.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构建 IHost 对象
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options">配置选项</param>
|
||||||
|
/// <param name="urls">默认 5000/5001 端口</param>
|
||||||
|
/// <param name="app"><see cref="IHost"/></param>
|
||||||
|
public static void BuildMiniApplication(MiniRunOptions options, string urls, out IHost app)
|
||||||
|
{
|
||||||
|
// 获取命令行参数
|
||||||
|
var args = options.Args ?? Environment.GetCommandLineArgs().Skip(1).ToArray();
|
||||||
|
|
||||||
|
|
||||||
|
var builder = Host.CreateDefaultBuilder(args);
|
||||||
|
|
||||||
|
// 静默启动排除指定日志类名
|
||||||
|
if (options.IsSilence && !options.SilenceLogging)
|
||||||
|
{
|
||||||
|
builder = builder.ConfigureLogging(logging =>
|
||||||
|
{
|
||||||
|
logging.AddFilter((provider, category, logLevel) => !SilenceExcludesOfLogCategoryName.Any(u => category.StartsWith(u)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 配置 Web 主机
|
||||||
|
builder = builder.ConfigureWebHost(webHostBuilder =>
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
// 调用自定义配置服务
|
||||||
|
options?.FirstActionBuilder?.Invoke(builder);
|
||||||
|
|
||||||
|
// 注册 WebApplicationBuilder 组件
|
||||||
|
if (options.WebComponents.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var (componentType, opt) in options.WebComponents)
|
||||||
|
{
|
||||||
|
webHostBuilder.AddWebComponent(componentType, opt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
webHostBuilder.Configure((WebHostBuilderContext app, IApplicationBuilder applicationBuilder) =>
|
||||||
|
{
|
||||||
|
|
||||||
|
// 添加自定义配置
|
||||||
|
options.ActionConfigurationManager?.Invoke(app.HostingEnvironment, app.Configuration);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// 初始化框架
|
||||||
|
webHostBuilder.Inject(options.ActionInject);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 配置服务
|
||||||
|
if (options.ServiceComponents.Count > 0)
|
||||||
|
{
|
||||||
|
webHostBuilder = webHostBuilder.ConfigureServices(services =>
|
||||||
|
{
|
||||||
|
// 注册应用服务组件
|
||||||
|
foreach (var (componentType, opt) in options.ServiceComponents)
|
||||||
|
{
|
||||||
|
services.AddComponent(componentType, opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置启动地址和端口
|
||||||
|
var startUrls = !string.IsNullOrWhiteSpace(urls) ? urls : webHostBuilder.GetSetting(nameof(urls));
|
||||||
|
|
||||||
|
// 自定义启动端口
|
||||||
|
if (!string.IsNullOrWhiteSpace(startUrls))
|
||||||
|
{
|
||||||
|
webHostBuilder = webHostBuilder.UseUrls(startUrls);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 调用自定义配置
|
||||||
|
options?.ActionBuilder?.Invoke(webHostBuilder);
|
||||||
|
|
||||||
|
// 配置中间件
|
||||||
|
if (options.ApplicationComponents.Count > 0)
|
||||||
|
{
|
||||||
|
webHostBuilder = webHostBuilder.Configure((context, app) =>
|
||||||
|
{
|
||||||
|
// 注册应用中间件组件
|
||||||
|
foreach (var (componentType, opt) in options.ApplicationComponents)
|
||||||
|
{
|
||||||
|
app.UseComponent(context.HostingEnvironment, componentType, opt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
builder = builder.ConfigureServices(services =>
|
||||||
|
{
|
||||||
|
// 调用自定义配置服务
|
||||||
|
options?.ActionServices?.Invoke(services);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 构建主机
|
||||||
|
app = builder.Build();
|
||||||
|
|
||||||
|
InternalApp.RootServices ??= app.Services;
|
||||||
|
|
||||||
|
var applicationPartManager = app.Services.GetService<ApplicationPartManager>();
|
||||||
|
|
||||||
|
applicationPartManager?.ApplicationParts?.RemoveWhere(p => App.BakImageNames.Any(b => b == p.Name));
|
||||||
|
// 配置所有 Startup Configure
|
||||||
|
UseStartups(app.Services);
|
||||||
|
// 释放内存
|
||||||
|
App.AppStartups.Clear();
|
||||||
|
// 调用自定义配置
|
||||||
|
options?.ActionConfigure?.Invoke(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构建 IHost 对象
|
/// 构建 IHost 对象
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -852,7 +1005,7 @@ public static class Serve
|
|||||||
var applicationPartManager = app.Services.GetService<ApplicationPartManager>();
|
var applicationPartManager = app.Services.GetService<ApplicationPartManager>();
|
||||||
applicationPartManager?.ApplicationParts?.RemoveWhere(p => App.BakImageNames.Any(b => b == p.Name));
|
applicationPartManager?.ApplicationParts?.RemoveWhere(p => App.BakImageNames.Any(b => b == p.Name));
|
||||||
|
|
||||||
// 配置所有 Starup Configure
|
// 配置所有 Startup Configure
|
||||||
UseStartups(app.Services);
|
UseStartups(app.Services);
|
||||||
// 释放内存
|
// 释放内存
|
||||||
App.AppStartups.Clear();
|
App.AppStartups.Clear();
|
||||||
@@ -908,7 +1061,6 @@ public static class Serve
|
|||||||
&& u.GetParameters().Length > 0
|
&& u.GetParameters().Length > 0
|
||||||
&& u.GetParameters().First().ParameterType == typeof(IServiceProvider));
|
&& u.GetParameters().First().ParameterType == typeof(IServiceProvider));
|
||||||
|
|
||||||
if (!configureMethods.Any()) continue;
|
|
||||||
|
|
||||||
// 自动安装属性调用
|
// 自动安装属性调用
|
||||||
foreach (var method in configureMethods)
|
foreach (var method in configureMethods)
|
||||||
@@ -935,7 +1087,6 @@ public static class Serve
|
|||||||
&& u.GetParameters().Length > 0
|
&& u.GetParameters().Length > 0
|
||||||
&& u.GetParameters().First().ParameterType == typeof(IApplicationBuilder));
|
&& u.GetParameters().First().ParameterType == typeof(IApplicationBuilder));
|
||||||
|
|
||||||
if (!configureMethods.Any()) continue;
|
|
||||||
|
|
||||||
// 自动安装属性调用
|
// 自动安装属性调用
|
||||||
foreach (var method in configureMethods)
|
foreach (var method in configureMethods)
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
#pragma warning disable CA1822 // 将成员标记为 static
|
|
||||||
|
|
||||||
namespace ThingsGateway;
|
namespace ThingsGateway;
|
||||||
|
|
||||||
|
|||||||
@@ -150,18 +150,18 @@ public static class DependencyInjectionServiceCollectionExtensions
|
|||||||
{
|
{
|
||||||
Register(services, dependencyType, type, injectionAttribute);
|
Register(services, dependencyType, type, injectionAttribute);
|
||||||
}
|
}
|
||||||
|
var list = canInjectInterfaces.ToList();
|
||||||
if (!canInjectInterfaces.Any()) return;
|
if (list.Count == 0) return;
|
||||||
|
|
||||||
// 只注册第一个接口
|
// 只注册第一个接口
|
||||||
if (injectionAttribute.Pattern is InjectionPatterns.FirstInterface or InjectionPatterns.SelfWithFirstInterface)
|
if (injectionAttribute.Pattern is InjectionPatterns.FirstInterface or InjectionPatterns.SelfWithFirstInterface)
|
||||||
{
|
{
|
||||||
Register(services, dependencyType, type, injectionAttribute, canInjectInterfaces.Last());
|
Register(services, dependencyType, type, injectionAttribute, list.Last());
|
||||||
}
|
}
|
||||||
// 注册多个接口
|
// 注册多个接口
|
||||||
else if (injectionAttribute.Pattern is InjectionPatterns.ImplementedInterfaces or InjectionPatterns.All)
|
else if (injectionAttribute.Pattern is InjectionPatterns.ImplementedInterfaces or InjectionPatterns.All)
|
||||||
{
|
{
|
||||||
foreach (var inter in canInjectInterfaces)
|
foreach (var inter in list)
|
||||||
{
|
{
|
||||||
Register(services, dependencyType, type, injectionAttribute, inter);
|
Register(services, dependencyType, type, injectionAttribute, inter);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,12 +47,14 @@ internal sealed class NamedServiceProvider<TService> : INamedServiceProvider<TSe
|
|||||||
{
|
{
|
||||||
var services = _serviceProvider.GetServices<TService>();
|
var services = _serviceProvider.GetServices<TService>();
|
||||||
|
|
||||||
|
#pragma warning disable CA1851
|
||||||
if (services
|
if (services
|
||||||
.OfType<AspectDispatchProxy>()
|
.OfType<AspectDispatchProxy>()
|
||||||
.FirstOrDefault(u => ResovleServiceName(((dynamic)u).Target.GetType()) == serviceName) is not TService service)
|
.FirstOrDefault(u => ResolveServiceName(((dynamic)u).Target.GetType()) == serviceName) is not TService service)
|
||||||
{
|
{
|
||||||
service = services.FirstOrDefault(u => ResovleServiceName(u.GetType()) == serviceName);
|
service = services.FirstOrDefault(u => ResolveServiceName(u.GetType()) == serviceName);
|
||||||
}
|
}
|
||||||
|
#pragma warning restore CA1851
|
||||||
|
|
||||||
return service;
|
return service;
|
||||||
}
|
}
|
||||||
@@ -80,12 +82,14 @@ internal sealed class NamedServiceProvider<TService> : INamedServiceProvider<TSe
|
|||||||
// 解析所有实现
|
// 解析所有实现
|
||||||
var services = _serviceProvider.GetServices<TService>();
|
var services = _serviceProvider.GetServices<TService>();
|
||||||
|
|
||||||
|
#pragma warning disable CA1851
|
||||||
if (services
|
if (services
|
||||||
.OfType<AspectDispatchProxy>()
|
.OfType<AspectDispatchProxy>()
|
||||||
.FirstOrDefault(u => ResovleServiceName(((dynamic)u).Target.GetType()) == serviceName) is not TService service)
|
.FirstOrDefault(u => ResolveServiceName(((dynamic)u).Target.GetType()) == serviceName) is not TService service)
|
||||||
{
|
{
|
||||||
service = services.FirstOrDefault(u => ResovleServiceName(u.GetType()) == serviceName);
|
service = services.FirstOrDefault(u => ResolveServiceName(u.GetType()) == serviceName);
|
||||||
}
|
}
|
||||||
|
#pragma warning restore CA1851
|
||||||
|
|
||||||
// 如果服务不存在,抛出异常
|
// 如果服务不存在,抛出异常
|
||||||
return service ?? throw new InvalidOperationException($"Named service `{serviceName}` is not registered in container.");
|
return service ?? throw new InvalidOperationException($"Named service `{serviceName}` is not registered in container.");
|
||||||
@@ -112,7 +116,7 @@ internal sealed class NamedServiceProvider<TService> : INamedServiceProvider<TSe
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="type"></param>
|
/// <param name="type"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private static string ResovleServiceName(Type type)
|
private static string ResolveServiceName(Type type)
|
||||||
{
|
{
|
||||||
if (type.IsDefined(typeof(InjectionAttribute)))
|
if (type.IsDefined(typeof(InjectionAttribute)))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -356,7 +356,7 @@ internal sealed class EventBusHostedService : BackgroundService
|
|||||||
GC.WaitForPendingFinalizers();
|
GC.WaitForPendingFinalizers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, stoppingToken);
|
}, stoppingToken, TaskCreationOptions.None, TaskScheduler.Default);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ internal sealed partial class ChannelEventPublisher : IEventPublisher
|
|||||||
await Task.Delay(TimeSpan.FromMilliseconds(delay), eventSource.CancellationToken).ConfigureAwait(false);
|
await Task.Delay(TimeSpan.FromMilliseconds(delay), eventSource.CancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
await _eventSourceStorer.WriteAsync(eventSource, eventSource.CancellationToken).ConfigureAwait(false);
|
await _eventSourceStorer.WriteAsync(eventSource, eventSource.CancellationToken).ConfigureAwait(false);
|
||||||
}, eventSource.CancellationToken);
|
}, eventSource.CancellationToken, TaskCreationOptions.None, TaskScheduler.Default);
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,13 +27,15 @@ public sealed class Retry
|
|||||||
/// <param name="exceptionTypes">异常类型,可多个</param>
|
/// <param name="exceptionTypes">异常类型,可多个</param>
|
||||||
/// <param name="fallbackPolicy">重试失败回调</param>
|
/// <param name="fallbackPolicy">重试失败回调</param>
|
||||||
/// <param name="retryAction">重试时调用方法</param>
|
/// <param name="retryAction">重试时调用方法</param>
|
||||||
|
/// <param name="shouldExit">退出条件</param>
|
||||||
public static void Invoke(Action action
|
public static void Invoke(Action action
|
||||||
, int numRetries
|
, int numRetries
|
||||||
, int retryTimeout = 1000
|
, int retryTimeout = 1000
|
||||||
, bool finalThrow = true
|
, bool finalThrow = true
|
||||||
, Type[] exceptionTypes = default
|
, Type[] exceptionTypes = default
|
||||||
, Action<Exception> fallbackPolicy = default
|
, Action<Exception> fallbackPolicy = default
|
||||||
, Action<int, int> retryAction = default)
|
, Action<int, int> retryAction = default
|
||||||
|
, Func<bool> shouldExit = default)
|
||||||
{
|
{
|
||||||
if (action == null) throw new ArgumentNullException(nameof(action));
|
if (action == null) throw new ArgumentNullException(nameof(action));
|
||||||
|
|
||||||
@@ -46,7 +48,7 @@ public sealed class Retry
|
|||||||
{
|
{
|
||||||
fallbackPolicy?.Invoke(ex);
|
fallbackPolicy?.Invoke(ex);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}, retryAction).GetAwaiter().GetResult();
|
}, retryAction, shouldExit).GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -59,6 +61,7 @@ public sealed class Retry
|
|||||||
/// <param name="exceptionTypes">异常类型,可多个</param>
|
/// <param name="exceptionTypes">异常类型,可多个</param>
|
||||||
/// <param name="fallbackPolicy">重试失败回调</param>
|
/// <param name="fallbackPolicy">重试失败回调</param>
|
||||||
/// <param name="retryAction">重试时调用方法</param>
|
/// <param name="retryAction">重试时调用方法</param>
|
||||||
|
/// <param name="shouldExit">退出条件</param>
|
||||||
/// <returns><see cref="Task"/></returns>
|
/// <returns><see cref="Task"/></returns>
|
||||||
public static async Task InvokeAsync(Func<Task> action
|
public static async Task InvokeAsync(Func<Task> action
|
||||||
, int numRetries
|
, int numRetries
|
||||||
@@ -66,7 +69,8 @@ public sealed class Retry
|
|||||||
, bool finalThrow = true
|
, bool finalThrow = true
|
||||||
, Type[] exceptionTypes = default
|
, Type[] exceptionTypes = default
|
||||||
, Func<Exception, Task> fallbackPolicy = default
|
, Func<Exception, Task> fallbackPolicy = default
|
||||||
, Action<int, int> retryAction = default)
|
, Action<int, int> retryAction = default
|
||||||
|
, Func<bool> shouldExit = default)
|
||||||
{
|
{
|
||||||
if (action == null) throw new ArgumentNullException(nameof(action));
|
if (action == null) throw new ArgumentNullException(nameof(action));
|
||||||
|
|
||||||
@@ -117,6 +121,12 @@ public sealed class Retry
|
|||||||
|
|
||||||
// 如果可重试异常数大于 0,则间隔指定时间后继续执行
|
// 如果可重试异常数大于 0,则间隔指定时间后继续执行
|
||||||
if (retryTimeout > 0) await Task.Delay(retryTimeout).ConfigureAwait(false);
|
if (retryTimeout > 0) await Task.Delay(retryTimeout).ConfigureAwait(false);
|
||||||
|
|
||||||
|
// 处理退出机制
|
||||||
|
if (shouldExit != null && shouldExit())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ public static class IEndpointRouteBuilderExtensions
|
|||||||
&& u.IsDefined(typeof(MapHubAttribute), true)
|
&& u.IsDefined(typeof(MapHubAttribute), true)
|
||||||
&& (typeof(Hub).IsAssignableFrom(u) || u.HasImplementedRawGeneric(typeof(Hub<>))));
|
&& (typeof(Hub).IsAssignableFrom(u) || u.HasImplementedRawGeneric(typeof(Hub<>))));
|
||||||
|
|
||||||
|
#pragma warning disable CA1851
|
||||||
if (!hubs.Any()) return;
|
if (!hubs.Any()) return;
|
||||||
|
|
||||||
// 反射获取 MapHub 拓展方法
|
// 反射获取 MapHub 拓展方法
|
||||||
@@ -65,5 +66,6 @@ public static class IEndpointRouteBuilderExtensions
|
|||||||
hub.GetMethod("HubEndpointConventionBuilderSettings", BindingFlags.Public | BindingFlags.Static)
|
hub.GetMethod("HubEndpointConventionBuilderSettings", BindingFlags.Public | BindingFlags.Static)
|
||||||
?.Invoke(null, new object[] { hubEndpointConventionBuilder });
|
?.Invoke(null, new object[] { hubEndpointConventionBuilder });
|
||||||
}
|
}
|
||||||
|
#pragma warning restore CA1851
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// 版权信息
|
||||||
|
// 版权归百小僧及百签科技(广东)有限公司所有。
|
||||||
|
// 所有权利保留。
|
||||||
|
// 官方网站:https://baiqian.com
|
||||||
|
//
|
||||||
|
// 许可证信息
|
||||||
|
// 项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。
|
||||||
|
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace ThingsGateway.JsonSerialization;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="System.Text.Json.Nodes.JsonObject"/> 类型序列化
|
||||||
|
/// </summary>
|
||||||
|
[SuppressSniffer]
|
||||||
|
public class NewtonsoftJsonJsonObjectJsonConverter : JsonConverter<System.Text.Json.Nodes.JsonObject>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 反序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader"></param>
|
||||||
|
/// <param name="objectType"></param>
|
||||||
|
/// <param name="existingValue"></param>
|
||||||
|
/// <param name="hasExistingValue"></param>
|
||||||
|
/// <param name="serializer"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override System.Text.Json.Nodes.JsonObject ReadJson(JsonReader reader, Type objectType, System.Text.Json.Nodes.JsonObject existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
return System.Text.Json.Nodes.JsonObject.Parse(JObject.Load(reader).ToString()).AsObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="serializer"></param>
|
||||||
|
public override void WriteJson(JsonWriter writer, System.Text.Json.Nodes.JsonObject value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
writer.WriteRawValue(value.ToJsonString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="System.Text.Json.Nodes.JsonArray"/> 类型序列化
|
||||||
|
/// </summary>
|
||||||
|
[SuppressSniffer]
|
||||||
|
public class NewtonsoftJsonJsonArrayJsonConverter : JsonConverter<System.Text.Json.Nodes.JsonArray>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 反序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader"></param>
|
||||||
|
/// <param name="objectType"></param>
|
||||||
|
/// <param name="existingValue"></param>
|
||||||
|
/// <param name="hasExistingValue"></param>
|
||||||
|
/// <param name="serializer"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override System.Text.Json.Nodes.JsonArray ReadJson(JsonReader reader, Type objectType, System.Text.Json.Nodes.JsonArray existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
return System.Text.Json.Nodes.JsonArray.Parse(JArray.Load(reader).ToString()).AsArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 序列化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="serializer"></param>
|
||||||
|
public override void WriteJson(JsonWriter writer, System.Text.Json.Nodes.JsonArray value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
writer.WriteRawValue(value.ToJsonString());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -127,7 +127,8 @@ public sealed class DatabaseLogger : ILogger, IDisposable
|
|||||||
// 设置日志消息模板
|
// 设置日志消息模板
|
||||||
logMsg.Message = _options.MessageFormat != null
|
logMsg.Message = _options.MessageFormat != null
|
||||||
? _options.MessageFormat(logMsg)
|
? _options.MessageFormat(logMsg)
|
||||||
: Penetrates.OutputStandardMessage(logMsg, _options.DateFormat, withTraceId: _options.WithTraceId, withStackFrame: _options.WithStackFrame, provider: _options.FormatProvider);
|
: string.Empty;
|
||||||
|
//: Penetrates.OutputStandardMessage(logMsg, _options.DateFormat, withTraceId: _options.WithTraceId, withStackFrame: _options.WithStackFrame, provider: _options.FormatProvider);
|
||||||
|
|
||||||
// 空检查
|
// 空检查
|
||||||
if (logMsg.Message is null)
|
if (logMsg.Message is null)
|
||||||
|
|||||||
@@ -162,8 +162,8 @@ public sealed class DatabaseLoggerProvider : ILoggerProvider, ISupportExternalSc
|
|||||||
_databaseLoggingWriter = _serviceScope.ServiceProvider.GetRequiredService(databaseLoggingWriterType) as IDatabaseLoggingWriter;
|
_databaseLoggingWriter = _serviceScope.ServiceProvider.GetRequiredService(databaseLoggingWriterType) as IDatabaseLoggingWriter;
|
||||||
|
|
||||||
// 创建长时间运行的后台任务,并将日志消息队列中数据写入存储中
|
// 创建长时间运行的后台任务,并将日志消息队列中数据写入存储中
|
||||||
_processQueueTask = Task.Factory.StartNew(ProcessQueueAsync
|
_processQueueTask = Task.Factory.StartNew(ProcessQueueAsync, CancellationToken.None
|
||||||
, TaskCreationOptions.LongRunning);
|
, TaskCreationOptions.LongRunning, TaskScheduler.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ public sealed class FileLoggerProvider : ILoggerProvider, ISupportExternalScope
|
|||||||
_fileLoggingWriter = new FileLoggingWriter(this);
|
_fileLoggingWriter = new FileLoggingWriter(this);
|
||||||
|
|
||||||
// 创建长时间运行的后台任务,并将日志消息队列中数据写入文件中
|
// 创建长时间运行的后台任务,并将日志消息队列中数据写入文件中
|
||||||
_processQueueTask = Task.Factory.StartNew(ProcessQueueAsync, TaskCreationOptions.LongRunning);
|
_processQueueTask = Task.Factory.StartNew(ProcessQueueAsync, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ using ThingsGateway;
|
|||||||
using ThingsGateway.DataValidation;
|
using ThingsGateway.DataValidation;
|
||||||
using ThingsGateway.Extensions;
|
using ThingsGateway.Extensions;
|
||||||
using ThingsGateway.FriendlyException;
|
using ThingsGateway.FriendlyException;
|
||||||
|
using ThingsGateway.JsonSerialization;
|
||||||
using ThingsGateway.Logging;
|
using ThingsGateway.Logging;
|
||||||
using ThingsGateway.Templates;
|
using ThingsGateway.Templates;
|
||||||
using ThingsGateway.UnifyResult;
|
using ThingsGateway.UnifyResult;
|
||||||
@@ -639,6 +640,11 @@ public sealed class LoggingMonitorAttribute : Attribute, IAsyncActionFilter, IAs
|
|||||||
// 解决 JsonElement 序列化问题
|
// 解决 JsonElement 序列化问题
|
||||||
jsonSerializerSettings.Converters.Add(new JsonElementConverter());
|
jsonSerializerSettings.Converters.Add(new JsonElementConverter());
|
||||||
|
|
||||||
|
// 解决 JsonObject 和 JsonArray 序列化问题
|
||||||
|
jsonSerializerSettings.Converters.Add(new NewtonsoftJsonJsonObjectJsonConverter());
|
||||||
|
jsonSerializerSettings.Converters.Add(new NewtonsoftJsonJsonArrayJsonConverter());
|
||||||
|
|
||||||
|
|
||||||
// 解决 DateTimeOffset 序列化/反序列化问题
|
// 解决 DateTimeOffset 序列化/反序列化问题
|
||||||
if (obj is DateTimeOffset)
|
if (obj is DateTimeOffset)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ public static class OptionsBuilderExtensions
|
|||||||
var builderInterfaces = optionsBuilderType.GetInterfaces()
|
var builderInterfaces = optionsBuilderType.GetInterfaces()
|
||||||
.Where(u => optionsBuilderDependency.IsAssignableFrom(u) && u != optionsBuilderDependency);
|
.Where(u => optionsBuilderDependency.IsAssignableFrom(u) && u != optionsBuilderDependency);
|
||||||
|
|
||||||
|
#pragma warning disable CA1851
|
||||||
if (!builderInterfaces.Any())
|
if (!builderInterfaces.Any())
|
||||||
{
|
{
|
||||||
return optionsBuilder;
|
return optionsBuilder;
|
||||||
@@ -95,6 +96,7 @@ public static class OptionsBuilderExtensions
|
|||||||
{
|
{
|
||||||
InvokeMapMethod(optionsBuilder, optionsBuilderType, builderInterface);
|
InvokeMapMethod(optionsBuilder, optionsBuilderType, builderInterface);
|
||||||
}
|
}
|
||||||
|
#pragma warning restore CA1851
|
||||||
|
|
||||||
return optionsBuilder;
|
return optionsBuilder;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,10 @@ public sealed class DailyAtAttribute : CronAttribute
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造函数
|
/// 构造函数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="field">字段值</param>
|
||||||
/// <param name="fields">字段值</param>
|
/// <param name="fields">字段值</param>
|
||||||
public DailyAtAttribute(params object[] fields)
|
public DailyAtAttribute(object field, params object[] fields)
|
||||||
: base("@daily", fields)
|
: base("@daily", new[] { field }.Concat(fields).ToArray())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,9 +20,10 @@ public sealed class HourlyAtAttribute : CronAttribute
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造函数
|
/// 构造函数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="field">字段值</param>
|
||||||
/// <param name="fields">字段值</param>
|
/// <param name="fields">字段值</param>
|
||||||
public HourlyAtAttribute(params object[] fields)
|
public HourlyAtAttribute(object field, params object[] fields)
|
||||||
: base("@hourly", fields)
|
: base("@hourly", new[] { field }.Concat(fields).ToArray())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,9 +20,10 @@ public sealed class MinutelyAtAttribute : CronAttribute
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造函数
|
/// 构造函数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="field">字段值</param>
|
||||||
/// <param name="fields">字段值</param>
|
/// <param name="fields">字段值</param>
|
||||||
public MinutelyAtAttribute(params object[] fields)
|
public MinutelyAtAttribute(object field, params object[] fields)
|
||||||
: base("@minutely", fields)
|
: base("@minutely", new[] { field }.Concat(fields).ToArray())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,9 +20,10 @@ public sealed class MonthlyAtAttribute : CronAttribute
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造函数
|
/// 构造函数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="field">字段值</param>
|
||||||
/// <param name="fields">字段值</param>
|
/// <param name="fields">字段值</param>
|
||||||
public MonthlyAtAttribute(params object[] fields)
|
public MonthlyAtAttribute(object field, params object[] fields)
|
||||||
: base("@monthly", fields)
|
: base("@monthly", new[] { field }.Concat(fields).ToArray())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,15 +14,16 @@ namespace ThingsGateway.Schedule;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 特定秒开始作业触发器特性
|
/// 特定秒开始作业触发器特性
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SecondlyAtAttribute, AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
[SuppressSniffer, AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||||
public sealed class SecondlyAtAttribute : CronAttribute
|
public sealed class SecondlyAtAttribute : CronAttribute
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造函数
|
/// 构造函数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="field">字段值</param>
|
||||||
/// <param name="fields">字段值</param>
|
/// <param name="fields">字段值</param>
|
||||||
public SecondlyAtAttribute(params object[] fields)
|
public SecondlyAtAttribute(object field, params object[] fields)
|
||||||
: base("@secondly", fields)
|
: base("@secondly", new[] { field }.Concat(fields).ToArray())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,9 +20,10 @@ public sealed class WeeklyAtAttribute : CronAttribute
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造函数
|
/// 构造函数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="field">字段值</param>
|
||||||
/// <param name="fields">字段值</param>
|
/// <param name="fields">字段值</param>
|
||||||
public WeeklyAtAttribute(params object[] fields)
|
public WeeklyAtAttribute(object field, params object[] fields)
|
||||||
: base("@weekly", fields)
|
: base("@weekly", new[] { field }.Concat(fields).ToArray())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,9 +20,10 @@ public sealed class YearlyAtAttribute : CronAttribute
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造函数
|
/// 构造函数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="field">字段值</param>
|
||||||
/// <param name="fields">字段值</param>
|
/// <param name="fields">字段值</param>
|
||||||
public YearlyAtAttribute(params object[] fields)
|
public YearlyAtAttribute(object field, params object[] fields)
|
||||||
: base("@yearly", fields)
|
: base("@yearly", new[] { field }.Concat(fields).ToArray())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,12 +93,14 @@ internal sealed class JobCancellationToken : IJobCancellationToken
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException) { }
|
catch (Exception ex)
|
||||||
catch (Exception ex) when (!(ex is OperationCanceledException ||
|
{
|
||||||
ex is ObjectDisposedException ||
|
// 输出非任务取消异常日志
|
||||||
(ex is AggregateException aggEx && aggEx.InnerExceptions.All(e => e is OperationCanceledException || e is ObjectDisposedException))))
|
if (!(ex is OperationCanceledException || (ex is AggregateException aggEx && aggEx.InnerExceptions.Count == 1 && aggEx.InnerExceptions[0] is TaskCanceledException)))
|
||||||
{ }
|
{
|
||||||
catch { }
|
// 待输出
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
|
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace ThingsGateway.Schedule;
|
namespace ThingsGateway.Schedule;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -152,7 +154,31 @@ public abstract class JobExecutionContext
|
|||||||
writer.WriteEndObject();
|
writer.WriteEndObject();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 检查作业任务是否处于正常状态
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="schedulerFactory"><see cref="ISchedulerFactory"/></param>
|
||||||
|
/// <returns><see cref="bool"/></returns>
|
||||||
|
public bool IsNormalStatus(ISchedulerFactory schedulerFactory = null)
|
||||||
|
{
|
||||||
|
// 解析作业计划工厂服务
|
||||||
|
schedulerFactory ??= ServiceProvider.GetRequiredService<ISchedulerFactory>();
|
||||||
|
|
||||||
|
// 情况 1:检查作业是否存在
|
||||||
|
if (schedulerFactory.TryGetJob(JobId, out var scheduler) != ScheduleResult.Succeed)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 情况 2:检查作业触发器是否存在
|
||||||
|
if (scheduler.TryGetTrigger(TriggerId, out var trigger) != ScheduleResult.Succeed)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 情况 3:检查作业触发器是否正常运行
|
||||||
|
return trigger.IsNormalStatus();
|
||||||
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 作业执行上下文转字符串输出输出
|
/// 作业执行上下文转字符串输出输出
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ public sealed class ScheduleUIMiddleware
|
|||||||
case "remove":
|
case "remove":
|
||||||
_schedulerFactory.RemoveJob(jobId);
|
_schedulerFactory.RemoveJob(jobId);
|
||||||
break;
|
break;
|
||||||
// 立即执行
|
// 手动执行
|
||||||
case "run":
|
case "run":
|
||||||
_schedulerFactory.RunJob(jobId);
|
_schedulerFactory.RunJob(jobId);
|
||||||
break;
|
break;
|
||||||
@@ -264,7 +264,7 @@ public sealed class ScheduleUIMiddleware
|
|||||||
case "remove":
|
case "remove":
|
||||||
scheduler1?.RemoveTrigger(triggerId);
|
scheduler1?.RemoveTrigger(triggerId);
|
||||||
break;
|
break;
|
||||||
// 立即执行
|
// 手动执行
|
||||||
case "run":
|
case "run":
|
||||||
scheduler1?.Run(triggerId);
|
scheduler1?.Run(triggerId);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"files": {
|
"files": {
|
||||||
"main.css": "/__schedule__/static/css/main.fbe5db1c.css",
|
"main.css": "/__schedule__/static/css/main.765127e9.css",
|
||||||
"main.js": "/__schedule__/static/js/main.851eb0b3.js",
|
"main.js": "/__schedule__/static/js/main.326c761f.js",
|
||||||
"index.html": "/__schedule__/index.html",
|
"index.html": "/__schedule__/index.html",
|
||||||
"main.fbe5db1c.css.map": "/__schedule__/static/css/main.fbe5db1c.css.map",
|
"main.765127e9.css.map": "/__schedule__/static/css/main.765127e9.css.map",
|
||||||
"main.851eb0b3.js.map": "/__schedule__/static/js/main.851eb0b3.js.map"
|
"main.326c761f.js.map": "/__schedule__/static/js/main.326c761f.js.map"
|
||||||
},
|
},
|
||||||
"entrypoints": [
|
"entrypoints": [
|
||||||
"static/css/main.fbe5db1c.css",
|
"static/css/main.765127e9.css",
|
||||||
"static/js/main.851eb0b3.js"
|
"static/js/main.326c761f.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1 +1,19 @@
|
|||||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/__schedule__/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Schedule Dashboard"/><link rel="apple-touch-icon" href="/__schedule__/logo192.png"/><script src="/__schedule__/apiconfig.js"></script><title>Schedule Dashboard</title><script defer="defer" src="/__schedule__/static/js/main.851eb0b3.js"></script><link href="/__schedule__/static/css/main.fbe5db1c.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>document.title=window.apiconfig.title</script></body></html>
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="icon" href="/__schedule__/favicon.ico" />
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<meta name="description" content="Schedule Dashboard" />
|
||||||
|
<link rel="apple-touch-icon" href="/__schedule__/logo192.png" />
|
||||||
|
<script src="/__schedule__/apiconfig.js"></script>
|
||||||
|
<title>Schedule Dashboard</title>
|
||||||
|
<script defer="defer" src="/__schedule__/static/js/main.326c761f.js"></script>
|
||||||
|
<link href="/__schedule__/static/css/main.765127e9.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div>
|
||||||
|
<script>document.title = window.apiconfig.title</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user