😎1、调整事件总线 2、增加系统教程菜单 3、调整代码生成模板 4、增加租户账号删除判断 5、升级nuget包

This commit is contained in:
zuohuaijun 2024-07-04 13:56:04 +08:00
parent 1d159ccca6
commit b18c7652d0
13 changed files with 108 additions and 41 deletions

View File

@ -33,11 +33,11 @@
<PackageReference Include="RabbitMQ.Client" Version="6.8.1" />
<PackageReference Include="SixLabors.ImageSharp.Web" Version="3.1.2" />
<PackageReference Include="SKIT.FlurlHttpClient.Wechat.Api" Version="3.3.0" />
<PackageReference Include="SKIT.FlurlHttpClient.Wechat.TenpayV3" Version="3.5.0" />
<PackageReference Include="SKIT.FlurlHttpClient.Wechat.TenpayV3" Version="3.6.0" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.160" />
<PackageReference Include="SSH.NET" Version="2024.1.0" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.4.3" />
<PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1038" />
<PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1040" />
<PackageReference Include="UAParser" Version="3.1.47" />
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
</ItemGroup>

View File

@ -1,4 +1,4 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
@ -205,6 +205,12 @@ public enum ErrorCodeEnum
[ErrorCodeItemMetadata("新密码不能与旧密码相同")]
D1028,
/// <summary>
/// 系统默认账号禁止删除
/// </summary>
[ErrorCodeItemMetadata("系统默认账号禁止删除")]
D1029,
/// <summary>
/// 父机构不存在
/// </summary>
@ -254,9 +260,9 @@ public enum ErrorCodeEnum
D2007,
/// <summary>
/// 租户默认机构禁止删除
/// 系统默认机构禁止删除
/// </summary>
[ErrorCodeItemMetadata("租户默认机构禁止删除")]
[ErrorCodeItemMetadata("系统默认机构禁止删除")]
D2008,
/// <summary>

View File

@ -0,0 +1,31 @@
// 麻省理工学院许可证
//
// 版权所有 (c) 2021-2023 zuohuaijun大名科技天津有限公司 联系电话/微信18020030720 QQ515096995
//
// 特此免费授予获得本软件的任何人以处理本软件的权利,但须遵守以下条件:在所有副本或重要部分的软件中必须包括上述版权声明和本许可声明。
//
// 软件按“原样”提供,不提供任何形式的明示或暗示的保证,包括但不限于对适销性、适用性和非侵权的保证。
// 在任何情况下,作者或版权持有人均不对任何索赔、损害或其他责任负责,无论是因合同、侵权或其他方式引起的,与软件或其使用或其他交易有关。
namespace Admin.NET.Core;
public class EventHandlerMonitor : IEventHandlerMonitor
{
public Task OnExecutingAsync(EventHandlerExecutingContext context)
{
//_logger.LogInformation("执行之前:{EventId}", context.Source.EventId);
return Task.CompletedTask;
}
public Task OnExecutedAsync(EventHandlerExecutedContext context)
{
//_logger.LogInformation("执行之后:{EventId}", context.Source.EventId);
if (context.Exception != null)
{
Log.Error($"EventHandlerMonitor.OnExecutedAsync 执行出错啦:{context.Source.EventId}", context.Exception);
}
return Task.CompletedTask;
}
}

View File

@ -13,10 +13,32 @@ public class RetryEventHandlerExecutor : IEventHandlerExecutor
{
public async Task ExecuteAsync(EventHandlerExecutingContext context, Func<EventHandlerExecutingContext, Task> handler)
{
// 如果执行失败,每隔 1s 重试,最多三次
var eventSubscribeAttribute = context.Attribute;
// 判断是否自定义了重试失败回调服务
var fallbackPolicyService = eventSubscribeAttribute?.FallbackPolicy == null
? null
: App.GetService(eventSubscribeAttribute.FallbackPolicy) as IEventFallbackPolicy;
await Retry.InvokeAsync(async () =>
{
await handler(context);
}, 3, 1000);
try
{
await handler(context);
}
catch (Exception ex)
{
Log.Error($"Invoke EventHandler {context.Source.EventId} Error", ex);
throw;
}
}
, eventSubscribeAttribute?.NumRetries ?? 0
, eventSubscribeAttribute?.RetryTimeout ?? 1000
, exceptionTypes: eventSubscribeAttribute?.ExceptionTypes
, fallbackPolicy: fallbackPolicyService == null ? null : async (Exception ex) => { await fallbackPolicyService.CallbackAsync(context, ex); }
, retryAction: (total, times) =>
{
// 输出重试日志
Log.Warning($"Retrying {times}/{total} times for EventHandler {context.Source.EventId}");
});
}
}

View File

@ -160,7 +160,7 @@ public class DatabaseLoggingWriter : IDatabaseLoggingWriter, IDisposable
}
// 记录操作日志
if (!(await _sysConfigService.GetConfigValue<bool>(CommonConst.SysOpLog))) return;
if (!await _sysConfigService.GetConfigValue<bool>(CommonConst.SysOpLog)) return;
await _db.Insertable(new SysLogOp
{
ControllerName = loggingMonitor.controllerName,

View File

@ -197,9 +197,10 @@ public class SysMenuSeedData : ISqlSugarEntitySeedData<SysMenu>
new SysMenu{ Id=1310000000641, Pid=1310000000601, Title="系统接口", Path="/develop/api", Name="sysApi", Component="layout/routerView/iframe", IsIframe=true, OutLink="http://localhost:5005", Icon="ele-Help", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=130 },
new SysMenu{ Id=1310000000701, Pid=0, Title="帮助文档", Path="/doc", Name="doc", Component="Layout", Icon="ele-Notebook", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=14000 },
new SysMenu{ Id=1310000000711, Pid=1310000000701, Title="后台教程", Path="/doc/furion", Name="sysFurion", Component="layout/routerView/link", IsIframe=false, IsKeepAlive=false, OutLink="https://furion.baiqian.ltd/", Icon="ele-Promotion", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
new SysMenu{ Id=1310000000712, Pid=1310000000701, Title="前端教程", Path="/doc/element", Name="sysElement", Component="layout/routerView/link", IsIframe=false, IsKeepAlive=false, OutLink="https://element-plus.gitee.io/zh-CN/", Icon="ele-Position", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=110 },
new SysMenu{ Id=1310000000713, Pid=1310000000701, Title="SqlSugar", Path="/doc/SqlSugar", Name="sysSqlSugar", Component="layout/routerView/link", IsIframe=false, IsKeepAlive=false, OutLink="https://www.donet5.com/Home/Doc", Icon="ele-Coin", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=120 },
new SysMenu{ Id=1310000000711, Pid=1310000000701, Title="框架教程", Path="/doc/admin", Name="sysAdmin", Component="layout/routerView/link", IsIframe=false, IsKeepAlive=false, OutLink="http://101.43.53.74:5050/", Icon="ele-Sunny", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
new SysMenu{ Id=1310000000712, Pid=1310000000701, Title="后台教程", Path="/doc/furion", Name="sysFurion", Component="layout/routerView/link", IsIframe=false, IsKeepAlive=false, OutLink="https://furion.baiqian.ltd/", Icon="ele-Promotion", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=110 },
new SysMenu{ Id=1310000000713, Pid=1310000000701, Title="前端教程", Path="/doc/element", Name="sysElement", Component="layout/routerView/link", IsIframe=false, IsKeepAlive=false, OutLink="https://element-plus.gitee.io/zh-CN/", Icon="ele-Position", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=120 },
new SysMenu{ Id=1310000000714, Pid=1310000000701, Title="SqlSugar", Path="/doc/SqlSugar", Name="sysSqlSugar", Component="layout/routerView/link", IsIframe=false, IsKeepAlive=false, OutLink="https://www.donet5.com/Home/Doc", Icon="ele-Coin", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=130 },
new SysMenu{ Id=1310000000801, Pid=0, Title="关于项目", Path="/about", Name="about", Component="/about/index", Icon="ele-InfoFilled", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2023-03-12 00:00:00"), OrderNo=15000 },
};

View File

@ -172,6 +172,10 @@ public class SysUserService : IDynamicApiController, ITransient
throw Oops.Oh(ErrorCodeEnum.D1014);
if (user.Id == _userManager.UserId)
throw Oops.Oh(ErrorCodeEnum.D1001);
// 若账号为租户默认账号则禁止删除
var isTenantUser = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysTenant>>().IsAnyAsync(u => u.UserId == input.Id);
if (isTenantUser)
throw Oops.Oh(ErrorCodeEnum.D1029);
// 强制下线
await _sysOnlineUserService.ForceOffline(user.Id);

View File

@ -133,10 +133,7 @@ public static class SqlSugarSetup
{
if (ex.Parametres == null) return;
var log = $"【{DateTime.Now}——错误SQL】\r\n{UtilMethods.GetNativeSql(ex.Sql, (SugarParameter[])ex.Parametres)}\r\n";
var originColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.DarkRed;
Console.WriteLine(log);
Console.ForegroundColor = originColor;
Log.Error(log, ex);
App.PrintToMiniProfiler("SqlSugar", "Error", log);
};
db.Aop.OnLogExecuted = (sql, pars) =>
@ -156,10 +153,7 @@ public static class SqlSugarSetup
var fileLine = db.Ado.SqlStackTrace.FirstLine; // 行号
var firstMethodName = db.Ado.SqlStackTrace.FirstMethodName; // 方法名
var log = $"【{DateTime.Now}——超时SQL】\r\n【所在文件名】{fileName}\r\n【代码行数】{fileLine}\r\n【方法名】{firstMethodName}\r\n" + $"【SQL语句】{UtilMethods.GetNativeSql(sql, pars)}";
var originColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine(log);
Console.ForegroundColor = originColor;
Log.Warning(log);
App.PrintToMiniProfiler("SqlSugar", "Slow", log);
}
};

View File

@ -8,6 +8,7 @@ using Admin.NET.Core;
using Admin.NET.Core.Service;
using AspNetCoreRateLimit;
using Furion;
using Furion.Logging;
using Furion.SpecificationDocument;
using Furion.VirtualFileServer;
using IGeekFan.AspNetCore.Knife4jUI;
@ -123,6 +124,14 @@ public class Startup : AppStartup
options.LogEnabled = false;
// 事件执行器(失败重试)
options.AddExecutor<RetryEventHandlerExecutor>();
// 事件执行器(重试后依然处理未处理异常的处理器)
options.UnobservedTaskExceptionHandler = (obj, args) =>
{
if (args.Exception?.Message != null)
Log.Error($"EeventBus 有未处理异常 {args.Exception?.Message} ", args.Exception);
};
// 事件执行器-监视器(每一次处理都会进入)
options.AddMonitor<EventHandlerMonitor>();
#region Redis消息队列

View File

@ -25,10 +25,10 @@ namespace @Model.NameSpace;
[ApiDescriptionSettings(@(@Model.ProjectLastName)Const.GroupName, Order = 100)]
public class @(@Model.ClassName)Service : IDynamicApiController, ITransient
{
private readonly SqlSugarRepository<@(@Model.ClassName)> _rep;
public @(@Model.ClassName)Service(SqlSugarRepository<@(@Model.ClassName)> rep)
private readonly SqlSugarRepository<@(@Model.ClassName)> _@(@Model.LowerClassName)Rep;
public @(@Model.ClassName)Service(SqlSugarRepository<@(@Model.ClassName)> @(@Model.LowerClassName)Rep)
{
_rep = rep;
_@(@Model.LowerClassName)Rep = @(@Model.LowerClassName)Rep;
}
/// <summary>
@ -44,7 +44,7 @@ public class @(@Model.ClassName)Service : IDynamicApiController, ITransient
@if (haveLikeCdt) {
@:input.SearchKey = input.SearchKey?.Trim();
}
var query = _rep.AsQueryable()
var query = _@(@Model.LowerClassName)Rep.AsQueryable()
@{string conditionFlag = "";}
@if (haveLikeCdt) {
@:.WhereIF(!string.IsNullOrEmpty(input.SearchKey), u =>
@ -125,7 +125,7 @@ if (@column.QueryWhether == "Y"){
public async Task<long> Add(Add@(@Model.ClassName)Input input)
{
var entity = input.Adapt<@(@Model.ClassName)>();
await _rep.InsertAsync(entity);
await _@(@Model.LowerClassName)Rep.InsertAsync(entity);
return entity.Id;
}
@ -141,11 +141,11 @@ if (@column.QueryWhether == "Y"){
{
@foreach (var column in Model.TableField){
if (@column.ColumnKey == "True"){
@:var entity = await _rep.GetFirstAsync(u => u.@(@column.PropertyName) == input.@(@column.PropertyName)) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
@:var entity = await _@(@Model.LowerClassName)Rep.GetFirstAsync(u => u.@(@column.PropertyName) == input.@(@column.PropertyName)) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
}
}
await _rep.FakeDeleteAsync(entity); //假删除
//await _rep.DeleteAsync(entity); //真删除
await _@(@Model.LowerClassName)Rep.FakeDeleteAsync(entity); //假删除
//await _@(@Model.LowerClassName)Rep.DeleteAsync(entity); //真删除
}
/// <summary>
@ -159,7 +159,7 @@ if (@column.ColumnKey == "True"){
public async Task Update(Update@(@Model.ClassName)Input input)
{
var entity = input.Adapt<@(@Model.ClassName)>();
await _rep.AsUpdateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
await _@(@Model.LowerClassName)Rep.AsUpdateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
}
/// <summary>
@ -174,7 +174,7 @@ if (@column.ColumnKey == "True"){
{
@foreach (var column in Model.TableField){
if (@column.ColumnKey == "True"){
@:return await _rep.GetFirstAsync(u => u.@(@column.PropertyName) == input.@(@column.PropertyName));
@:return await _@(@Model.LowerClassName)Rep.GetFirstAsync(u => u.@(@column.PropertyName) == input.@(@column.PropertyName));
}
}
}
@ -189,7 +189,7 @@ if (@column.ColumnKey == "True"){
[DisplayName("获取@(@Model.BusName)列表")]
public async Task<List<@(@Model.ClassName)Output>> List([FromQuery] @(@Model.ClassName)Input input)
{
return await _rep.AsQueryable().Select<@(@Model.ClassName)Output>().ToListAsync();
return await _@(@Model.LowerClassName)Rep.AsQueryable().Select<@(@Model.ClassName)Output>().ToListAsync();
}
@foreach (var column in Model.TableField){
@ -202,7 +202,7 @@ if(@column.EffectType == "fk" && (@column.WhetherAddUpdate == "Y" || column.Quer
@:[DisplayName("获取@(@column.ColumnComment)列表")]
@:public async Task<dynamic> @(@column.FkEntityName)@(@column.PropertyName)Dropdown()
@:{
@:return await _rep.Context.Queryable<@(@column.FkEntityName)>()
@:return await _@(@Model.LowerClassName)Rep.Context.Queryable<@(@column.FkEntityName)>()
@:.Select(u => new
@:{
@:Label = u.@(@column.FkColumnName),
@ -238,7 +238,7 @@ if(@column.EffectType == "ApiTreeSelect" && !definedObjects.ContainsKey("@(@colu
@:[DisplayName("获取@(@column.FkEntityName)Tree")]
@:public async Task<dynamic> @(@column.FkEntityName)Tree()
@:{
@:return await _rep.Context.Queryable<@(@column.FkEntityName)>().ToTreeAsync(u => u.Children, u => u.@(@column.PidColumn), 0);
@:return await _@(@Model.LowerClassName)Rep.Context.Queryable<@(@column.FkEntityName)>().ToTreeAsync(u => u.Children, u => u.@(@column.PidColumn), 0);
@:}
}
}

View File

@ -24,7 +24,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Rezero.Api" Version="1.7.7" />
<PackageReference Include="Rezero.Api" Version="1.7.8" />
</ItemGroup>
<ItemGroup>

View File

@ -2,7 +2,7 @@
"name": "admin.net.pro",
"type": "module",
"version": "2.4.33",
"lastBuildTime": "2024.6.28",
"lastBuildTime": "2024.07.04",
"description": "Admin.NET 站在巨人肩膀上的 .NET 通用权限开发框架",
"author": "zuohuaijun",
"license": "MIT",
@ -50,7 +50,7 @@
"push.js": "^1.0.12",
"qrcodejs2-fixes": "^0.0.2",
"qs": "^6.12.2",
"relation-graph": "^2.2.1",
"relation-graph": "^2.2.2",
"screenfull": "^6.0.2",
"sm-crypto-v2": "^1.9.0",
"sortablejs": "^1.15.2",
@ -68,8 +68,8 @@
"vue-signature-pad": "^3.0.2",
"vue3-tree-org": "^4.2.2",
"vuedraggable": "4.0.3",
"vxe-pc-ui": "^4.0.44",
"vxe-table": "^4.7.40",
"vxe-pc-ui": "^4.0.48",
"vxe-table": "^4.7.43",
"vxe-table-plugin-element": "^4.0.4",
"vxe-table-plugin-export-xlsx": "^4.0.4",
"xe-utils": "^3.5.28",
@ -88,14 +88,14 @@
"@vue/compiler-sfc": "^3.4.31",
"code-inspector-plugin": "^0.14.2",
"eslint": "^9.6.0",
"eslint-plugin-vue": "^9.26.0",
"eslint-plugin-vue": "^9.27.0",
"less": "^4.2.0",
"prettier": "^3.3.2",
"rollup-plugin-visualizer": "^5.12.0",
"sass": "^1.77.6",
"terser": "^5.31.1",
"typescript": "^5.5.3",
"vite": "^5.3.2",
"vite": "^5.3.3",
"vite-plugin-cdn-import": "^1.0.1",
"vite-plugin-compression2": "^1.1.2",
"vite-plugin-vue-setup-extend": "^0.4.0",

View File

@ -67,7 +67,7 @@
<EditColumn ref="editColumnRef" @handleQueryColumn="handleQueryColumn" />
<AddTable ref="addTableRef" @addTableSubmitted="addTableSubmitted" />
<AddColumn ref="addColumnRef" @handleQueryColumn="handleQueryColumn" />
<GenEntity ref="genEntityRef" @handleQueryColumn="handleQueryColumn" />
<GenEntity ref="genEntityRef" :application-namespaces="state.appNamespaces" />
<GenSeedData ref="genSeedDataRef" :application-namespaces="state.appNamespaces" />
</div>
</template>