😎同步开源代码仓库

This commit is contained in:
zuohuaijun 2024-11-25 16:27:14 +08:00
parent f75abc9d4a
commit 369bb601b5
265 changed files with 5213 additions and 7741 deletions

View File

@ -47,7 +47,7 @@
<PackageReference Include="SSH.NET" Version="2024.2.0" /> <PackageReference Include="SSH.NET" Version="2024.2.0" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.4.9" /> <PackageReference Include="System.Linq.Dynamic.Core" Version="1.4.9" />
<PackageReference Include="System.Net.Http" Version="4.3.4" /> <PackageReference Include="System.Net.Http" Version="4.3.4" />
<PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1127" /> <PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1128" />
<PackageReference Include="UAParser" Version="3.1.47" /> <PackageReference Include="UAParser" Version="3.1.47" />
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" /> <PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
</ItemGroup> </ItemGroup>

View File

@ -44,12 +44,12 @@ public class DictAttribute : ValidationAttribute, ITransient
var dictDataList = sysDictDataServiceProvider.GetDataList(DictTypeCode).Result; var dictDataList = sysDictDataServiceProvider.GetDataList(DictTypeCode).Result;
// 使用HashSet来提高查找效率 // 使用HashSet来提高查找效率
var dictCodes = new HashSet<string>(dictDataList.Select(u => u.Code)); var valueList = (value?.GetType().IsEnum ?? DictTypeCode.EndsWith("Enum")) ? dictDataList.Select(u => u.Name) : dictDataList.Select(u => u.Code);
var dictHash = new HashSet<string>(valueList);
if (!dictCodes.Contains(valueAsString)) if (!dictHash.Contains(valueAsString))
return new ValidationResult($"提示:{ErrorMessage}|字典【{DictTypeCode}】不包含【{valueAsString}】!"); return new ValidationResult($"提示:{ErrorMessage}|字典【{DictTypeCode}】不包含【{valueAsString}】!");
else return ValidationResult.Success;
return ValidationResult.Success;
} }
/// <summary> /// <summary>

View File

@ -11,6 +11,11 @@ namespace Admin.NET.Core;
/// </summary> /// </summary>
public class ClaimConst public class ClaimConst
{ {
/// <summary>
/// 应用Id
/// </summary>
public const string AppId = "AppId";
/// <summary> /// <summary>
/// 用户Id /// 用户Id
/// </summary> /// </summary>

View File

@ -71,6 +71,11 @@ public class ConfigConst
/// </summary> /// </summary>
public const string SysDomainLogin = "sys_domain_login"; public const string SysDomainLogin = "sys_domain_login";
/// <summary>
/// 租户域名隔离登录验证
/// </summary>
public const string SysTenantHostLogin = "sys_tenant_host_login";
/// <summary> /// <summary>
/// 数据校验日志 /// 数据校验日志
/// </summary> /// </summary>

View File

@ -26,8 +26,18 @@ public class SqlSugarConst
/// </summary> /// </summary>
public const string PrimaryKey = "Id"; public const string PrimaryKey = "Id";
/// <summary>
/// 默认应用Id
/// </summary>
public const long DefaultAppId = 1300000000001;
/// <summary> /// <summary>
/// 默认租户Id /// 默认租户Id
/// </summary> /// </summary>
public const long DefaultTenantId = 1300000000001; public const long DefaultTenantId = 1300000000001;
/// <summary>
/// 默认租户域名
/// </summary>
public const string DefaultTenantHost = "gitee.com";
} }

View File

@ -0,0 +1,82 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core;
/// <summary>
/// 系统应用表
/// </summary>
[SysTable]
[SugarTable(null, "系统应用表")]
public partial class SysApp : EntityBase
{
/// <summary>
/// 名称
/// </summary>
[SugarColumn(ColumnDescription = "名称", Length = 32), Required, MaxLength(32)]
public virtual string Name { get; set; }
/// <summary>
/// 图标
/// </summary>
[SugarColumn(ColumnDescription = "图标", Length = 256), Required, MaxLength(256)]
public virtual string? Logo { get; set; }
/// <summary>
/// 标题
/// </summary>
[SugarColumn(ColumnDescription = "标题", Length = 32), MaxLength(32)]
public string Title { get; set; }
/// <summary>
/// 副标题
/// </summary>
[SugarColumn(ColumnDescription = "副标题", Length = 32), MaxLength(32)]
public string ViceTitle { get; set; }
/// <summary>
/// 副描述
/// </summary>
[SugarColumn(ColumnDescription = "副描述", Length = 64), MaxLength(64)]
public string? ViceDesc { get; set; }
/// <summary>
/// 水印
/// </summary>
[SugarColumn(ColumnDescription = "水印", Length = 32), MaxLength(32)]
public string? Watermark { get; set; }
/// <summary>
/// 版权信息
/// </summary>
[SugarColumn(ColumnDescription = "版权信息", Length = 64), MaxLength(64)]
public string? Copyright { get; set; }
/// <summary>
/// ICP备案号
/// </summary>
[SugarColumn(ColumnDescription = "ICP备案号", Length = 32), MaxLength(32)]
public string? Icp { get; set; }
/// <summary>
/// 排序
/// </summary>
[IgnoreUpdateSeedColumn]
[SugarColumn(ColumnDescription = "排序")]
public int OrderNo { get; set; } = 100;
/// <summary>
/// 备注
/// </summary>
[SugarColumn(ColumnDescription = "备注", Length = 256), MaxLength(256)]
public string? Remark { get; set; }
/// <summary>
/// 应用租户
/// </summary>
[Navigate(NavigateType.OneToMany, nameof(SysTenant.AppId))]
public List<SysTenant> TenantList { get; set; }
}

View File

@ -0,0 +1,41 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core;
/// <summary>
/// 系统应用菜单表
/// </summary>
[SugarTable(null, "系统应用菜单表")]
[SysTable]
public partial class SysAppMenu : EntityBaseId
{
/// <summary>
/// 应用Id
/// </summary>
[SugarColumn(ColumnDescription = "应用Id")]
public long AppId { get; set; }
/// <summary>
/// 应用
/// </summary>
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
[Navigate(NavigateType.OneToOne, nameof(AppId))]
public SysApp SysApp { get; set; }
/// <summary>
/// 菜单Id
/// </summary>
[SugarColumn(ColumnDescription = "菜单Id")]
public long MenuId { get; set; }
/// <summary>
/// 菜单
/// </summary>
[Navigate(NavigateType.OneToOne, nameof(MenuId))]
public SysMenu SysMenu { get; set; }
}

View File

@ -85,6 +85,13 @@ public partial class SysCodeGen : EntityBase
[MaxLength(128)] [MaxLength(128)]
public string? BusName { get; set; } public string? BusName { get; set; }
/// <summary>
/// 表唯一字段配置
/// </summary>
[SugarColumn(ColumnDescription = "表唯一字段配置", Length = 128)]
[MaxLength(128)]
public string? TableUniqueConfig { get; set; }
/// <summary> /// <summary>
/// 是否生成菜单 /// 是否生成菜单
/// </summary> /// </summary>
@ -134,4 +141,10 @@ public partial class SysCodeGen : EntityBase
/// </summary> /// </summary>
[Navigate(NavigateType.OneToMany, nameof(SysCodeGenTemplateRelation.CodeGenId))] [Navigate(NavigateType.OneToMany, nameof(SysCodeGenTemplateRelation.CodeGenId))]
public List<SysCodeGenTemplateRelation> CodeGenTemplateRelations { get; set; } public List<SysCodeGenTemplateRelation> CodeGenTemplateRelations { get; set; }
/// <summary>
/// 表唯一字段列表
/// </summary>
[SugarColumn(IsIgnore = true)]
public virtual List<TableUniqueConfigItem> TableUniqueList => string.IsNullOrWhiteSpace(TableUniqueConfig) ? null : JSON.Deserialize<List<TableUniqueConfigItem>>(TableUniqueConfig);
} }

View File

@ -130,5 +130,5 @@ public partial class SysMenu : EntityBase
/// 菜单子项 /// 菜单子项
/// </summary> /// </summary>
[SugarColumn(IsIgnore = true)] [SugarColumn(IsIgnore = true)]
public List<SysMenu> Children { get; set; } = new List<SysMenu>(); public List<SysMenu> Children { get; set; } = new ();
} }

View File

@ -13,6 +13,20 @@ namespace Admin.NET.Core;
[SysTable] [SysTable]
public partial class SysTenant : EntityBase public partial class SysTenant : EntityBase
{ {
/// <summary>
/// 应用Id
/// </summary>
[SugarColumn(ColumnDescription = "应用Id", DefaultValue = "1300000000001")]
public long? AppId { get; set; }
/// <summary>
/// 应用
/// </summary>
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
[Navigate(NavigateType.OneToOne, nameof(AppId))]
public SysApp SysApp { get; set; }
/// <summary> /// <summary>
/// 用户Id /// 用户Id
/// </summary> /// </summary>
@ -26,9 +40,9 @@ public partial class SysTenant : EntityBase
public long OrgId { get; set; } public long OrgId { get; set; }
/// <summary> /// <summary>
/// 主机 /// 域名
/// </summary> /// </summary>
[SugarColumn(ColumnDescription = "主机", Length = 128)] [SugarColumn(ColumnDescription = "域名", Length = 128)]
[MaxLength(128)] [MaxLength(128)]
public string? Host { get; set; } public string? Host { get; set; }

View File

@ -30,6 +30,12 @@ public class SysUserLdap : EntityTenant
[Required] [Required]
public string Account { get; set; } public string Account { get; set; }
/// <summary>
/// 域用户名
/// </summary>
[SugarColumn(ColumnDescription = "域用户名", Length = 32)]
public string UserName { get; set; }
/// <summary> /// <summary>
/// 对应EmployeeId(用于数据导入对照) /// 对应EmployeeId(用于数据导入对照)
/// </summary> /// </summary>
@ -41,4 +47,34 @@ public class SysUserLdap : EntityTenant
/// </summary> /// </summary>
[SugarColumn(ColumnDescription = "组织代码", Length = 64)] [SugarColumn(ColumnDescription = "组织代码", Length = 64)]
public string? DeptCode { get; set; } public string? DeptCode { get; set; }
/// <summary>
/// 最后设置密码时间
/// </summary>
[SugarColumn(ColumnDescription = "最后设置密码时间")]
public DateTime? PwdLastSetTime { get; set; }
/// <summary>
/// 邮箱
/// </summary>
[SugarColumn(ColumnDescription = "组织代码", Length = 64)]
public string? Mail { get; set; }
/// <summary>
/// 检查账户是否已过期
/// </summary>
[SugarColumn(ColumnDescription = "检查账户是否已过期")]
public bool AccountExpiresFlag { get; set; } = false;
/// <summary>
/// 密码设置是否永不过期
/// </summary>
[SugarColumn(ColumnDescription = "密码设置是否永不过期")]
public bool DontExpiresFlag { get; set; } = false;
/// <summary>
/// DN
/// </summary>
[SugarColumn(ColumnDescription = "DN", Length = 512)]
public string Dn { get; set; }
} }

View File

@ -15,7 +15,7 @@ public enum CultureLevelEnum
/// <summary> /// <summary>
/// 其他 /// 其他
/// </summary> /// </summary>
[Description("其他")] [Description("其他"), Theme("info")]
Level0 = 0, Level0 = 0,
/// <summary> /// <summary>

View File

@ -15,7 +15,7 @@ public enum DataOpTypeEnum
/// <summary> /// <summary>
/// 其它 /// 其它
/// </summary> /// </summary>
[Description("其它")] [Description("其它"), Theme("info")]
Other, Other,
/// <summary> /// <summary>

View File

@ -223,6 +223,12 @@ public enum ErrorCodeEnum
[ErrorCodeItemMetadata("开放接口绑定租户禁止删除")] [ErrorCodeItemMetadata("开放接口绑定租户禁止删除")]
D1031, D1031,
/// <summary>
/// 手机号已存在
/// </summary>
[ErrorCodeItemMetadata("手机号已存在")]
D1032,
/// <summary> /// <summary>
/// 父机构不存在 /// 父机构不存在
/// </summary> /// </summary>
@ -319,6 +325,18 @@ public enum ErrorCodeEnum
[ErrorCodeItemMetadata("字典状态错误")] [ErrorCodeItemMetadata("字典状态错误")]
D3005, D3005,
/// <summary>
/// 字典编码不能以Enum结尾
/// </summary>
[ErrorCodeItemMetadata("字典编码不能以Enum结尾")]
D3006,
/// <summary>
/// 禁止修改枚举类型的字典编码
/// </summary>
[ErrorCodeItemMetadata("禁止修改枚举类型的字典编码")]
D3007,
/// <summary> /// <summary>
/// 菜单已存在 /// 菜单已存在
/// </summary> /// </summary>
@ -535,6 +553,12 @@ public enum ErrorCodeEnum
[ErrorCodeItemMetadata("租户从库配置错误")] [ErrorCodeItemMetadata("租户从库配置错误")]
D1302, D1302,
/// <summary>
/// 已存在同名的租户域名
/// </summary>
[ErrorCodeItemMetadata("已存在同名的租户域名")]
D1303,
/// <summary> /// <summary>
/// 该表代码模板已经生成过 /// 该表代码模板已经生成过
/// </summary> /// </summary>
@ -631,6 +655,24 @@ public enum ErrorCodeEnum
[ErrorCodeItemMetadata("已存在同名功能或同名程序及插件")] [ErrorCodeItemMetadata("已存在同名功能或同名程序及插件")]
D1900, D1900,
/// <summary>
/// 禁止删除存在关联租户的应用
/// </summary>
[ErrorCodeItemMetadata("禁止删除存在关联租户的应用")]
A1001,
/// <summary>
/// 禁止删除存在关联菜单的应用
/// </summary>
[ErrorCodeItemMetadata("禁止删除存在关联菜单的应用")]
A1002,
/// <summary>
/// 找不到系统应用
/// </summary>
[ErrorCodeItemMetadata("找不到系统应用")]
A1000,
/// <summary> /// <summary>
/// 已存在同名或同编码项目 /// 已存在同名或同编码项目
/// </summary> /// </summary>

View File

@ -15,12 +15,12 @@ public enum FinishStatusEnum
/// <summary> /// <summary>
/// 已完成 /// 已完成
/// </summary> /// </summary>
[Description("已完成")] [Description("已完成"), Theme("success")]
Finish = 1, Finish = 1,
/// <summary> /// <summary>
/// 未完成 /// 未完成
/// </summary> /// </summary>
[Description("未完成")] [Description("未完成"), Theme("danger")]
UnFinish = 0, UnFinish = 0,
} }

View File

@ -15,7 +15,7 @@ public enum GenderEnum
/// <summary> /// <summary>
/// 未知的性别 /// 未知的性别
/// </summary> /// </summary>
[Description("未知的性别")] [Description("未知的性别"), Theme("info")]
Unknown = 0, Unknown = 0,
/// <summary> /// <summary>
@ -33,6 +33,6 @@ public enum GenderEnum
/// <summary> /// <summary>
/// 未说明的性别 /// 未说明的性别
/// </summary> /// </summary>
[Description("未说明的性别")] [Description("未说明的性别"), Theme("info")]
Unspecified = 9 Unspecified = 9
} }

View File

@ -15,7 +15,7 @@ public enum JobCreateTypeEnum
/// <summary> /// <summary>
/// 内置 /// 内置
/// </summary> /// </summary>
[Description("内置")] [Description("内置"), Theme("info")]
BuiltIn = 0, BuiltIn = 0,
/// <summary> /// <summary>

View File

@ -15,7 +15,7 @@ public enum MenuTypeEnum
/// <summary> /// <summary>
/// 目录 /// 目录
/// </summary> /// </summary>
[Description("目录")] [Description("目录"), Theme("warning")]
Dir = 1, Dir = 1,
/// <summary> /// <summary>
@ -27,6 +27,6 @@ public enum MenuTypeEnum
/// <summary> /// <summary>
/// 按钮 /// 按钮
/// </summary> /// </summary>
[Description("按钮")] [Description("按钮"), Theme("info")]
Btn = 3 Btn = 3
} }

View File

@ -15,24 +15,24 @@ public enum MessageTypeEnum
/// <summary> /// <summary>
/// 普通信息 /// 普通信息
/// </summary> /// </summary>
[Description("消息")] [Description("消息"), Theme("info")]
Info = 0, Info = 0,
/// <summary> /// <summary>
/// 成功提示 /// 成功提示
/// </summary> /// </summary>
[Description("成功")] [Description("成功"), Theme("success")]
Success = 1, Success = 1,
/// <summary> /// <summary>
/// 警告提示 /// 警告提示
/// </summary> /// </summary>
[Description("警告")] [Description("警告"), Theme("warning")]
Warning = 2, Warning = 2,
/// <summary> /// <summary>
/// 错误提示 /// 错误提示
/// </summary> /// </summary>
[Description("错误")] [Description("错误"), Theme("danger")]
Error = 3 Error = 3
} }

View File

@ -15,7 +15,7 @@ public enum NoticeStatusEnum
/// <summary> /// <summary>
/// 草稿 /// 草稿
/// </summary> /// </summary>
[Description("草稿")] [Description("草稿"), Theme("info")]
DRAFT = 0, DRAFT = 0,
/// <summary> /// <summary>

View File

@ -21,6 +21,6 @@ public enum NoticeUserStatusEnum
/// <summary> /// <summary>
/// 已读 /// 已读
/// </summary> /// </summary>
[Description("已读")] [Description("已读"), Theme("info")]
READ = 1 READ = 1
} }

View File

@ -15,12 +15,12 @@ public enum StatusEnum
/// <summary> /// <summary>
/// 启用 /// 启用
/// </summary> /// </summary>
[Description("启用")] [Description("启用"), Theme("success")]
Enable = 1, Enable = 1,
/// <summary> /// <summary>
/// 停用 /// 停用
/// </summary> /// </summary>
[Description("停用")] [Description("停用"), Theme("danger")]
Disable = 2, Disable = 2,
} }

View File

@ -9,6 +9,7 @@ namespace Admin.NET.Core;
/// <summary> /// <summary>
/// 事件类型-系统用户操作枚举 /// 事件类型-系统用户操作枚举
/// </summary> /// </summary>
[SuppressSniffer]
[Description("事件类型-系统用户操作枚举")] [Description("事件类型-系统用户操作枚举")]
public enum SysUserEventTypeEnum public enum SysUserEventTypeEnum
{ {

View File

@ -15,12 +15,12 @@ public enum YesNoEnum
/// <summary> /// <summary>
/// 是 /// 是
/// </summary> /// </summary>
[Description("是")] [Description("是"), Theme("success")]
Y = 1, Y = 1,
/// <summary> /// <summary>
/// 否 /// 否
/// </summary> /// </summary>
[Description("否")] [Description("否"), Theme("danger")]
N = 2 N = 2
} }

View File

@ -23,4 +23,26 @@ public static class ListExtensions
await action(value); await action(value);
} }
} }
public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> consumer)
{
foreach (T item in enumerable)
{
consumer(item);
}
}
public static void AddRange<T>(this IList<T> list, IEnumerable<T> items)
{
if (list is List<T> list2)
{
list2.AddRange(items);
return;
}
foreach (T item in items)
{
list.Add(item);
}
}
} }

View File

@ -155,7 +155,7 @@ public class OnlineUserHub : Hub<IOnlineUserHub>
/// </summary> /// </summary>
/// <param name="message"></param> /// <param name="message"></param>
/// <returns></returns> /// <returns></returns>
public async Task ClientsSendMessagetoAll(MessageInput message) public async Task ClientsSendMessageToAll(MessageInput message)
{ {
await _sysMessageService.SendAllUser(message); await _sysMessageService.SendAllUser(message);
} }
@ -165,7 +165,7 @@ public class OnlineUserHub : Hub<IOnlineUserHub>
/// </summary> /// </summary>
/// <param name="message"></param> /// <param name="message"></param>
/// <returns></returns> /// <returns></returns>
public async Task ClientsSendMessagetoOther(MessageInput message) public async Task ClientsSendMessageToOther(MessageInput message)
{ {
await _sysMessageService.SendOtherUser(message); await _sysMessageService.SendOtherUser(message);
} }
@ -175,7 +175,7 @@ public class OnlineUserHub : Hub<IOnlineUserHub>
/// </summary> /// </summary>
/// <param name="message"></param> /// <param name="message"></param>
/// <returns></returns> /// <returns></returns>
public async Task ClientsSendMessagetoUsers(MessageInput message) public async Task ClientsSendMessageToUsers(MessageInput message)
{ {
await _sysMessageService.SendUsers(message); await _sysMessageService.SendUsers(message);
} }

View File

@ -14,8 +14,8 @@ namespace Admin.NET.Core;
public class EnumToDictJob : IJob public class EnumToDictJob : IJob
{ {
private readonly IServiceScopeFactory _scopeFactory; private readonly IServiceScopeFactory _scopeFactory;
private const string DefaultTagType = null;
private const int OrderOffset = 10; private const int OrderOffset = 10;
private const string DefaultTagType = "info";
public EnumToDictJob(IServiceScopeFactory scopeFactory) public EnumToDictJob(IServiceScopeFactory scopeFactory)
{ {
@ -24,48 +24,46 @@ public class EnumToDictJob : IJob
public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken) public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
{ {
var originColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine($"【{DateTime.Now}】系统枚举转换字典");
using var serviceScope = _scopeFactory.CreateScope(); using var serviceScope = _scopeFactory.CreateScope();
var sysEnumService = serviceScope.ServiceProvider.GetRequiredService<SysEnumService>();
// 获取数据库连接
var db = serviceScope.ServiceProvider.GetRequiredService<ISqlSugarClient>().CopyNew(); var db = serviceScope.ServiceProvider.GetRequiredService<ISqlSugarClient>().CopyNew();
// 获取枚举类型列表 var sysEnumService = serviceScope.ServiceProvider.GetRequiredService<SysEnumService>();
var enumTypeList = sysEnumService.GetEnumTypeList(); var sysDictTypeList = GetDictByEnumType(sysEnumService.GetEnumTypeList());
var enumCodeList = enumTypeList.Select(u => u.TypeName);
// 查询数据库中已存在的枚举类型代码
//var exp = Expressionable.Create<SysDictType, SingleColumnEntity<string>>().And((t1, t2) => t1.Code == t2.ColumnName).ToExpression();
//var sysDictTypeList = await db.Queryable<SysDictType>().Includes(t1 => t1.Children).BulkListQuery(exp, enumCodeList, stoppingToken);
var sysDictTypeList = await db.Queryable<SysDictType>().Includes(u => u.Children)
.Where(u => enumCodeList.Contains(u.Code)).ToListAsync(stoppingToken);
// 更新的枚举转换字典
var updatedEnumCodes = sysDictTypeList.Select(u => u.Code);
var updatedEnumType = enumTypeList.Where(u => updatedEnumCodes.Contains(u.TypeName)).ToList();
var sysDictTypeDict = sysDictTypeList.ToDictionary(u => u.Code, u => u);
var (updatedDictTypes, updatedDictDatas, newSysDictDatas) = GetUpdatedDicts(updatedEnumType, sysDictTypeDict);
// 新增的枚举转换字典 // 校验枚举类命名规范,字典相关功能中需要通过后缀判断是否为枚举类型
var newEnumType = enumTypeList.Where(u => !updatedEnumCodes.Contains(u.TypeName)).ToList(); Console.ForegroundColor = ConsoleColor.Red;
var (newDictTypes, newDictDatas) = GetNewSysDicts(newEnumType); foreach (var dictType in sysDictTypeList.Where(x => !x.Code.EndsWith("Enum")))
Console.WriteLine($"【{DateTime.Now}】系统枚举转换字典的枚举类名称必须以Enum结尾: {dictType.Code} ({dictType.Name})");
sysDictTypeList = sysDictTypeList.Where(x => x.Code.EndsWith("Enum")).ToList();
// 执行数据库操作 await SyncEnumToDictInfoAsync(db, sysDictTypeList);
Console.ForegroundColor = ConsoleColor.Yellow;
try try
{ {
await db.BeginTranAsync(); await db.BeginTranAsync();
var storageable1 = await db.Storageable(sysDictTypeList)
.SplitUpdate(it => it.Any())
.SplitInsert(_ => true)
.ToStorageAsync();
await storageable1.BulkCopyAsync();
await storageable1.BulkUpdateAsync();
if (updatedDictTypes.Count > 0) Console.WriteLine($"【{DateTime.Now}】系统枚举类转字典类型数据: 插入{storageable1.InsertList.Count}条, 更新{storageable1.UpdateList.Count}条, 共{storageable1.TotalList.Count}条。");
await db.Updateable(updatedDictTypes).ExecuteCommandAsync(stoppingToken);
if (updatedDictDatas.Count > 0) var storageable2 = await db.Storageable(sysDictTypeList.SelectMany(u => u.Children).ToList())
await db.Updateable(updatedDictDatas).ExecuteCommandAsync(stoppingToken); .WhereColumns(u => new { u.DictTypeId, u.Code })
.SplitUpdate(u => u.Any())
.SplitInsert(_ => true)
.ToStorageAsync();
await storageable2.BulkCopyAsync();
await storageable2.BulkUpdateAsync(nameof(SysDictData.Value), nameof(SysDictData.Code), nameof(SysDictData.Name));
if (newSysDictDatas.Count > 0) Console.WriteLine($"【{DateTime.Now}】系统枚举项转字典值数据: 插入{storageable2.InsertList.Count}条, 更新{storageable2.UpdateList.Count}条, 共{storageable2.TotalList.Count}条。");
await db.Insertable(newSysDictDatas).ExecuteCommandAsync(stoppingToken);
if (newDictTypes.Count > 0)
await db.Insertable(newDictTypes).ExecuteCommandAsync(stoppingToken);
if (newDictDatas.Count > 0)
await db.Insertable(newDictDatas).ExecuteCommandAsync(stoppingToken);
await db.CommitTranAsync(); await db.CommitTranAsync();
} }
@ -75,131 +73,63 @@ public class EnumToDictJob : IJob
Log.Error($"系统枚举转换字典操作错误:{error.Message}\n堆栈跟踪{error.StackTrace}", error); Log.Error($"系统枚举转换字典操作错误:{error.Message}\n堆栈跟踪{error.StackTrace}", error);
throw; throw;
} }
var originColor = Console.ForegroundColor; finally
Console.ForegroundColor = ConsoleColor.Green; {
Console.WriteLine($"【{DateTime.Now}】系统枚举转换字典"); Console.ForegroundColor = originColor;
Console.ForegroundColor = originColor; }
} }
/// <summary> /// <summary>
/// 获取需要新增的字典列表 /// 用于同步枚举转字典旧数据(后期可删除)
/// </summary> /// </summary>
/// <param name="addEnumType"></param> /// <param name="db"></param>
/// <returns> /// <param name="list"></param>
/// 一个元组,包含以下元素: //[Obsolete]
/// <list type="table"> private static async Task SyncEnumToDictInfoAsync(SqlSugarClient db, List<SysDictType> list)
/// <item><term>SysDictTypes</term><description>字典类型列表</description></item>
/// <item><term>SysDictDatas</term><description>字典数据列表</description></item>
/// </list>
/// </returns>
private (List<SysDictType>, List<SysDictData>) GetNewSysDicts(List<EnumTypeOutput> addEnumType)
{ {
var newDictType = new List<SysDictType>(); var codeList = list.Select(u => u.Code).ToList();
var newDictData = new List<SysDictData>(); foreach (var dbDictType in await db.Queryable<SysDictType>().Where(x => codeList.Contains(x.Code)).ToListAsync() ?? new())
if (addEnumType.Count <= 0)
return (newDictType, newDictData);
// 新增字典类型
newDictType = addEnumType.Select(u => new SysDictType
{ {
Id = YitIdHelper.NextId(), var enumDictType = list.First(u => u.Code == dbDictType.Code);
Code = u.TypeName, if (enumDictType.Id == dbDictType.Id) continue;
Name = u.TypeDescribe,
Remark = u.TypeRemark,
Status = StatusEnum.Enable
}).ToList();
// 新增字典数据 // 数据不一致则删除
newDictData = addEnumType.Join(newDictType, t1 => t1.TypeName, t2 => t2.Code, (t1, t2) => new _ = db.Deleteable<SysDictData>().Where(u => u.DictTypeId == dbDictType.Id).ExecuteCommandAsync();
_ = db.Deleteable<SysDictType>().Where(u => u.Id == dbDictType.Id).ExecuteCommandAsync();
Console.WriteLine($"【{DateTime.Now}】删除字典数据: {dbDictType.Name}-{dbDictType.Code}");
}
}
/// <summary>
/// 枚举信息转字典
/// </summary>
/// <param name="enumTypeList"></param>
/// <returns></returns>
private List<SysDictType> GetDictByEnumType(List<EnumTypeOutput> enumTypeList)
{
var orderNo = 1;
var list = new List<SysDictType>();
foreach (var type in enumTypeList)
{ {
Data = t1.EnumEntities.Select(u => new SysDictData var dictType = new SysDictType
{ {
Id = YitIdHelper.NextId(), Id = 900000000000 + CommonUtil.GetFixedHashCode(type.TypeName),
DictTypeId = t2.Id, Code = type.TypeName,
Name = u.Describe, Name = type.TypeDescribe,
Value = u.Value.ToString(), Remark = type.TypeRemark
Code = u.Name, };
Remark = t2.Remark, dictType.Children = type.EnumEntities.Select(u => new SysDictData
{
Id = dictType.Id + orderNo++,
DictTypeId = dictType.Id,
Name = u.Name,
Value = u.Describe,
Code = u.Value.ToString(),
OrderNo = u.Value + OrderOffset, OrderNo = u.Value + OrderOffset,
TagType = u.Theme != "" ? u.Theme : DefaultTagType, TagType = u.Theme != "" ? u.Theme : DefaultTagType,
}).ToList() }).ToList();
}).SelectMany(x => x.Data).ToList(); list.Add(dictType);
return (newDictType, newDictData);
}
/// <summary>
/// 获取需要更新的字典列表
/// </summary>
/// <param name="updatedEnumType"></param>
/// <param name="sysDictTypeDict"></param>
/// <returns>
/// 一个元组,包含以下元素:
/// <list type="table">
/// <item><term>SysDictTypes</term><description>更新字典类型列表</description>
/// </item>
/// <item><term>SysDictDatas</term><description>更新字典数据列表</description>
/// </item>
/// <item><term>SysDictDatas</term><description>新增字典数据列表</description>
/// </item>
/// </list>
/// </returns>
private (List<SysDictType>, List<SysDictData>, List<SysDictData>) GetUpdatedDicts(List<EnumTypeOutput> updatedEnumType, Dictionary<string, SysDictType> sysDictTypeDict)
{
var updatedSysDictTypes = new List<SysDictType>();
var updatedSysDictData = new List<SysDictData>();
var newSysDictData = new List<SysDictData>();
foreach (var e in updatedEnumType)
{
if (!sysDictTypeDict.TryGetValue(e.TypeName, out var value))
continue;
var updatedDictType = value;
updatedDictType.Name = e.TypeDescribe;
updatedDictType.Remark = e.TypeRemark;
updatedSysDictTypes.Add(updatedDictType);
var updatedDictData = updatedDictType.Children.Where(u => u.DictTypeId == updatedDictType.Id).ToList();
// 遍历需要更新的字典数据
foreach (var dictData in updatedDictData)
{
var enumData = e.EnumEntities.FirstOrDefault(u => dictData.Code == u.Name);
if (enumData != null)
{
dictData.Value = enumData.Value.ToString();
dictData.OrderNo = enumData.Value + OrderOffset;
dictData.Name = enumData.Describe;
dictData.TagType = enumData.Theme != "" ? enumData.Theme : dictData.TagType != "" ? dictData.TagType : DefaultTagType;
updatedSysDictData.Add(dictData);
}
}
// 新增的枚举值名称列表
var newEnumDataNameList = e.EnumEntities.Select(u => u.Name).Except(updatedDictData.Select(u => u.Code));
foreach (var newEnumDataName in newEnumDataNameList)
{
var enumData = e.EnumEntities.FirstOrDefault(u => newEnumDataName == u.Name);
if (enumData != null)
{
var dictData = new SysDictData
{
Id = YitIdHelper.NextId(),
DictTypeId = updatedDictType.Id,
Name = enumData.Describe,
Value = enumData.Value.ToString(),
Code = enumData.Name,
Remark = updatedDictType.Remark,
OrderNo = enumData.Value + OrderOffset,
TagType = enumData.Theme != "" ? enumData.Theme : DefaultTagType,
};
dictData.TagType = enumData.Theme != "" ? enumData.Theme : dictData.TagType != "" ? dictData.TagType : DefaultTagType;
newSysDictData.Add(dictData);
}
}
// 删除的情况暂不处理
} }
return list;
return (updatedSysDictTypes, updatedSysDictData, newSysDictData);
} }
} }

View File

@ -50,6 +50,11 @@ public sealed class DbConnectionConfig : ConnectionConfig
/// 种子配置 /// 种子配置
/// </summary> /// </summary>
public SeedSettings SeedSettings { get; set; } public SeedSettings SeedSettings { get; set; }
/// <summary>
/// 隔离方式
/// </summary>
public TenantTypeEnum TenantType { get; set; } = TenantTypeEnum.Id;
} }
/// <summary> /// <summary>

View File

@ -0,0 +1,23 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core;
/// <summary>
/// 系统应用菜单表种子数据
/// </summary>
public class SysAppMenuSeedData : ISqlSugarEntitySeedData<SysAppMenu>
{
/// <summary>
/// 种子数据
/// </summary>
/// <returns></returns>
public IEnumerable<SysAppMenu> HasData()
{
long id = SqlSugarConst.DefaultAppId;
return new SysMenuSeedData().HasData().Select(u => new SysAppMenu { Id = id++, AppId = SqlSugarConst.DefaultAppId, MenuId = u.Id });
}
}

View File

@ -0,0 +1,25 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core;
/// <summary>
/// 系统应用表种子数据
/// </summary>
public class SysAppSeedData : ISqlSugarEntitySeedData<SysApp>
{
/// <summary>
/// 种子数据
/// </summary>
/// <returns></returns>
public IEnumerable<SysApp> HasData()
{
return new[]
{
new SysApp{ Id=SqlSugarConst.DefaultAppId, Name="默认应用", Logo="/upload/logo.png", Title="Admin.NET", ViceTitle="Admin.NET", ViceDesc="站在巨人肩膀上的 .NET 通用权限开发框架", Watermark="Admin.NET", Copyright="Copyright \u00a9 2021-present Admin.NET All rights reserved.", Icp="省ICP备12345678号", Remark="系统默认应用", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
};
}
}

View File

@ -31,13 +31,14 @@ public class SysConfigSeedData : ISqlSugarEntitySeedData<SysConfig>
new SysConfig{ Id=1300000000191, Name="RefreshToken过期时间", Code=ConfigConst.SysRefreshTokenExpire, Value="20160", SysFlag=YesNoEnum.Y, Remark="刷新Token过期时间分钟一般 refresh_token 的有效时间 > 2 * access_token 的有效时间)", OrderNo=100, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysConfig{ Id=1300000000191, Name="RefreshToken过期时间", Code=ConfigConst.SysRefreshTokenExpire, Value="20160", SysFlag=YesNoEnum.Y, Remark="刷新Token过期时间分钟一般 refresh_token 的有效时间 > 2 * access_token 的有效时间)", OrderNo=100, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysConfig{ Id=1300000000201, Name="发送异常日志邮件", Code=ConfigConst.SysErrorMail, Value="False", SysFlag=YesNoEnum.Y, Remark="是否发送异常日志邮件", OrderNo=110, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysConfig{ Id=1300000000201, Name="发送异常日志邮件", Code=ConfigConst.SysErrorMail, Value="False", SysFlag=YesNoEnum.Y, Remark="是否发送异常日志邮件", OrderNo=110, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysConfig{ Id=1300000000211, Name="域登录验证", Code=ConfigConst.SysDomainLogin, Value="False", SysFlag=YesNoEnum.Y, Remark="是否开启域登录验证", OrderNo=120, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysConfig{ Id=1300000000211, Name="域登录验证", Code=ConfigConst.SysDomainLogin, Value="False", SysFlag=YesNoEnum.Y, Remark="是否开启域登录验证", OrderNo=120, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysConfig{ Id=1300000000221, Name="数据校验日志", Code=ConfigConst.SysValidationLog, Value="True", SysFlag=YesNoEnum.Y, Remark="是否数据校验日志", OrderNo=130, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysConfig{ Id=1300000000221, Name="租户隔离登录验证", Code=ConfigConst.SysTenantHostLogin, Value="False", SysFlag=YesNoEnum.Y, Remark="租户隔离登录验证", OrderNo=370, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysConfig{ Id=1300000000231, Name="行政区划同步层级", Code=ConfigConst.SysRegionSyncLevel, Value="3", SysFlag=YesNoEnum.Y, Remark="行政区划同步层级 1-省级,2-市级,3-区县级,4-街道级,5-村级", OrderNo=140, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysConfig{ Id=1300000000231, Name="数据校验日志", Code=ConfigConst.SysValidationLog, Value="True", SysFlag=YesNoEnum.Y, Remark="是否数据校验日志", OrderNo=130, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysConfig{ Id=1300000000241, Name="开启强制修改密码", Code=ConfigConst.SysForceChangePassword, Value="False", SysFlag=YesNoEnum.Y, Remark="开启强制修改密码", OrderNo=150, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysConfig{ Id=1300000000241, Name="行政区划同步层级", Code=ConfigConst.SysRegionSyncLevel, Value="3", SysFlag=YesNoEnum.Y, Remark="行政区划同步层级 1-省级,2-市级,3-区县级,4-街道级,5-村级", OrderNo=140, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysConfig{ Id=1300000000251, Name="开启强制修改密码", Code=ConfigConst.SysForceChangePassword, Value="False", SysFlag=YesNoEnum.Y, Remark="开启强制修改密码", OrderNo=150, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
// 新业务系统记得更改密匙,通过接口(http://localhost:5005/api/sysCommon/smKeyPair)获取 // 新业务系统记得更改密匙,通过接口(http://localhost:5005/api/sysCommon/smKeyPair)获取
new SysConfig{ Id=1300000000251, Name="国密SM2密匙", Code=ConfigConst.SysSM2Key, Value="04851D329AA3E38C2E7670AFE70E6E70E92F8769CA27C8766B12209A0FFBA4493B603EF7A0B9B1E16F0E8930C0406EA0B179B68DF28E25334BDEC4AE76D907E9E9;3A61D1D30C6302DABFF36201D936D0143EEF0C850AF28C5CA6D5C045AF8C5C8A", SysFlag=YesNoEnum.Y, Remark="国密SM2密匙", OrderNo=160, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2024-11-21 00:00:00") }, new SysConfig{ Id=1300000000261, Name="国密SM2密匙", Code=ConfigConst.SysSM2Key, Value="04851D329AA3E38C2E7670AFE70E6E70E92F8769CA27C8766B12209A0FFBA4493B603EF7A0B9B1E16F0E8930C0406EA0B179B68DF28E25334BDEC4AE76D907E9E9;3A61D1D30C6302DABFF36201D936D0143EEF0C850AF28C5CA6D5C045AF8C5C8A", SysFlag=YesNoEnum.Y, Remark="国密SM2密匙", OrderNo=160, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2024-11-21 00:00:00") },
new SysConfig{ Id=1300000000261, Name="开启密码强度验证", Code=ConfigConst.SysPasswordStrength, Value="False", SysFlag=YesNoEnum.Y, Remark="开启强制修改密码", OrderNo=150, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysConfig{ Id=1300000000271, Name="开启密码强度验证", Code=ConfigConst.SysPasswordStrength, Value="False", SysFlag=YesNoEnum.Y, Remark="开启强制修改密码", OrderNo=150, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysConfig{ Id=1300000000271, Name="密码强度验证正则表达式", Code=ConfigConst.SysPasswordStrengthExpression, Value="(?=^.{6,16}$)(?=.*\\d)(?=.*\\W+)(?=.*[A-Z])(?=.*[a-z])(?!.*\\n).*$", SysFlag=YesNoEnum.Y, Remark="必须须包含大小写字母、数字和特殊字符的组合长度在6-16之间", OrderNo=150, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2024-11-21 00:00:00") }, new SysConfig{ Id=1300000000281, Name="密码强度验证正则表达式", Code=ConfigConst.SysPasswordStrengthExpression, Value="(?=^.{6,16}$)(?=.*\\d)(?=.*\\W+)(?=.*[A-Z])(?=.*[a-z])(?!.*\\n).*$", SysFlag=YesNoEnum.Y, Remark="必须须包含大小写字母、数字和特殊字符的组合长度在6-16之间", OrderNo=150, GroupCode=ConfigConst.SysDefaultGroup, CreateTime=DateTime.Parse("2024-11-21 00:00:00") },
new SysConfig{ Id=1310000000301, Name="系统主标题", Code=ConfigConst.SysWebTitle, Value="Admin.NET.Pro", SysFlag=YesNoEnum.Y, Remark="系统主标题", OrderNo=300, GroupCode=ConfigConst.SysWebConfigGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysConfig{ Id=1310000000301, Name="系统主标题", Code=ConfigConst.SysWebTitle, Value="Admin.NET.Pro", SysFlag=YesNoEnum.Y, Remark="系统主标题", OrderNo=300, GroupCode=ConfigConst.SysWebConfigGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysConfig{ Id=1310000000311, Name="系统副标题", Code=ConfigConst.SysWebViceTitle, Value="Admin.NET.Pro", SysFlag=YesNoEnum.Y, Remark="系统副标题", OrderNo=310, GroupCode=ConfigConst.SysWebConfigGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysConfig{ Id=1310000000311, Name="系统副标题", Code=ConfigConst.SysWebViceTitle, Value="Admin.NET.Pro", SysFlag=YesNoEnum.Y, Remark="系统副标题", OrderNo=310, GroupCode=ConfigConst.SysWebConfigGroup, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },

View File

@ -20,16 +20,16 @@ public class SysDictDataSeedData : ISqlSugarEntitySeedData<SysDictData>
return new[] return new[]
{ {
new SysDictData{ Id=1300000000101, DictTypeId=1300000000101, Value="输入框", Code="Input", OrderNo=100, Remark="输入框", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysDictData{ Id=1300000000101, DictTypeId=1300000000101, Value="输入框", Code="Input", OrderNo=100, Remark="输入框", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysDictData{ Id=1300000000102, DictTypeId=1300000000101, Value="外键", Code="fk", OrderNo=100, Remark="外键", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysDictData{ Id=1300000000102, DictTypeId=1300000000101, Value="字典选择器", Code="DictSelector", OrderNo=100, Remark="字典选择器", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysDictData{ Id=1300000000103, DictTypeId=1300000000101, Value="时间选择", Code="DatePicker", OrderNo=100, Remark="时间选择", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysDictData{ Id=1300000000103, DictTypeId=1300000000101, Value="常量选择器", Code="ConstSelector", OrderNo=100, Remark="常量选择器", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysDictData{ Id=1300000000104, DictTypeId=1300000000101, Value="选择器", Code="Select", OrderNo=100, Remark="选择器", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysDictData{ Id=1300000000104, DictTypeId=1300000000101, Value="枚举选择器", Code="EnumSelector", OrderNo=100, Remark="枚举选择器", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysDictData{ Id=1300000000105, DictTypeId=1300000000101, Value="数字输入框", Code="InputNumber", OrderNo=100, Remark="数字输入框", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysDictData{ Id=1300000000105, DictTypeId=1300000000101, Value="树选择器", Code="ApiTreeSelector", OrderNo=100, Remark="树选择器", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysDictData{ Id=1300000000106, DictTypeId=1300000000101, Value="文本域", Code="InputTextArea", OrderNo=100, Remark="文本域", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysDictData{ Id=1300000000106, DictTypeId=1300000000101, Value="外键", Code="ForeignKey", OrderNo=100, Remark="外键", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysDictData{ Id=1300000000107, DictTypeId=1300000000101, Value="上传", Code="Upload", OrderNo=100, Remark="上传", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysDictData{ Id=1300000000107, DictTypeId=1300000000101, Value="数字输入框", Code="InputNumber", OrderNo=100, Remark="数字输入框", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysDictData{ Id=1300000000108, DictTypeId=1300000000101, Value="树选择", Code="ApiTreeSelect", OrderNo=100, Remark="树选择", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysDictData{ Id=1300000000108, DictTypeId=1300000000101, Value="时间选择", Code="DatePicker", OrderNo=100, Remark="时间选择", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysDictData{ Id=1300000000109, DictTypeId=1300000000101, Value="开关", Code="Switch", OrderNo=100, Remark="开关", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysDictData{ Id=1300000000109, DictTypeId=1300000000101, Value="文本域", Code="InputTextArea", OrderNo=100, Remark="文本域", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysDictData{ Id=1300000000110, DictTypeId=1300000000101, Value="常量选择器", Code="ConstSelector", OrderNo=100, Remark="常量选择器", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysDictData{ Id=1300000000110, DictTypeId=1300000000101, Value="上传", Code="Upload", OrderNo=100, Remark="上传", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysDictData{ Id=1300000000111, DictTypeId=1300000000101, Value="枚举选择器", Code="EnumSelector", OrderNo=100, Remark="枚举选择器", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysDictData{ Id=1300000000111, DictTypeId=1300000000101, Value="开关", Code="Switch", OrderNo=100, Remark="开关", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysDictData{ Id=1300000000201, DictTypeId=1300000000102, Value="等于", Code="==", OrderNo=1, Remark="等于", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysDictData{ Id=1300000000201, DictTypeId=1300000000102, Value="等于", Code="==", OrderNo=1, Remark="等于", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
new SysDictData{ Id=1300000000202, DictTypeId=1300000000102, Value="模糊", Code="like", OrderNo=1, Remark="模糊", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysDictData{ Id=1300000000202, DictTypeId=1300000000102, Value="模糊", Code="like", OrderNo=1, Remark="模糊", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },

View File

@ -21,7 +21,7 @@ public class SysTenantSeedData : ISqlSugarEntitySeedData<SysTenant>
return new[] return new[]
{ {
new SysTenant{ Id=SqlSugarConst.DefaultTenantId, OrgId=SqlSugarConst.DefaultTenantId, UserId=1300000000111, Host="https://gitee.com", TenantType=TenantTypeEnum.Id, DbType=defaultDbConfig.DbType, Connection=defaultDbConfig.ConnectionString, ConfigId=SqlSugarConst.MainConfigId, Remark="系统默认", CreateTime=DateTime.Parse("2022-02-10 00:00:00") }, new SysTenant{ Id=SqlSugarConst.DefaultTenantId, AppId=SqlSugarConst.DefaultTenantId, OrgId=SqlSugarConst.DefaultTenantId, UserId=1300000000111, Host=SqlSugarConst.DefaultTenantHost, TenantType=TenantTypeEnum.Id, DbType=defaultDbConfig.DbType, Connection=defaultDbConfig.ConnectionString, ConfigId=SqlSugarConst.MainConfigId, Remark="系统默认", CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
}; };
} }
} }

View File

@ -0,0 +1,173 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core.Service;
/// <summary>
/// 应用基础输入参数
/// </summary>
public class SysAppInput
{
/// <summary>
/// 主键Id
/// </summary>
public virtual long? Id { get; set; }
/// <summary>
/// 名称
/// </summary>
[Required(ErrorMessage = "名称不能为空")]
public virtual string Name { get; set; }
/// <summary>
/// 标题
/// </summary>
[Required(ErrorMessage = "标题不能为空")]
public virtual string Title { get; set; }
/// <summary>
/// 副标题
/// </summary>
[Required(ErrorMessage = "副标题不能为空")]
public virtual string ViceTitle { get; set; }
/// <summary>
/// 副描述
/// </summary>
public virtual string? ViceDesc { get; set; }
/// <summary>
/// 水印
/// </summary>
public virtual string? Watermark { get; set; }
/// <summary>
/// 版权信息
/// </summary>
public virtual string? Copyright { get; set; }
/// <summary>
/// ICP备案号
/// </summary>
public virtual string? Icp { get; set; }
/// <summary>
/// 排序
/// </summary>
[Required(ErrorMessage = "排序不能为空")]
public virtual int? OrderNo { get; set; }
/// <summary>
/// 备注
/// </summary>
public virtual string? Remark { get; set; }
/// <summary>
/// 图标
/// </summary>
public virtual string? Logo { get; set; }
}
/// <summary>
/// 增加应用输入参数
/// </summary>
public class AddSysAppInput
{
/// <summary>
/// 图标
/// </summary>
[Required(ErrorMessage = "Logo不能为空")]
public string Logo { get; set; }
/// <summary>
/// 名称
/// </summary>
[Required(ErrorMessage = "名称不能为空")]
public string Name { get; set; }
/// <summary>
/// 标题
/// </summary>
[Required(ErrorMessage = "标题不能为空")]
public string Title { get; set; }
/// <summary>
/// 副标题
/// </summary>
[Required(ErrorMessage = "副标题不能为空")]
public string ViceTitle { get; set; }
/// <summary>
/// 副描述
/// </summary>
[Required(ErrorMessage = "副描述不能为空")]
public string ViceDesc { get; set; }
/// <summary>
/// 水印
/// </summary>
[Required(ErrorMessage = "水印不能为空")]
public string Watermark { get; set; }
/// <summary>
/// 版权信息
/// </summary>
[Required(ErrorMessage = "版权信息不能为空")]
public string Copyright { get; set; }
/// <summary>
/// ICP备案号
/// </summary>
[Required(ErrorMessage = "备案号不能为空")]
public string Icp { get; set; }
/// <summary>
/// 排序
/// </summary>
[Required(ErrorMessage = "排序不能为空")]
public int OrderNo { get; set; } = 100;
/// <summary>
/// 备注
/// </summary>
[MaxLength(256, ErrorMessage = "备注字符长度不能超过256")]
public string Remark { get; set; }
}
/// <summary>
/// 更新应用输入参数
/// </summary>
public class UpdateSysAppInput : AddSysAppInput
{
/// <summary>
/// 主键Id
/// </summary>
[Required(ErrorMessage = "主键Id不能为空")]
public long Id { get; set; }
}
/// <summary>
/// 授权应用菜单
/// </summary>
public class UpdateAppMenuInput : BaseIdInput
{
/// <summary>
/// 菜单Id集合
/// </summary>
public List<long> MenuIdList { get; set; }
}
/// <summary>
/// 租户iId
/// </summary>
public class ChangeAppInput : BaseIdInput
{
/// <summary>
/// 租户Id
/// </summary>
[Required(ErrorMessage = "租户不能为空")]
public long TenantId { get; set; }
}

View File

@ -0,0 +1,103 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core.Service;
/// <summary>
/// 应用输出参数
/// </summary>
public class SysAppOutput
{
/// <summary>
/// 主键Id
/// </summary>
public long Id { get; set; }
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 图标
/// </summary>
public string? Logo { get; set; }
/// <summary>
/// 标题
/// </summary>
public string Title { get; set; }
/// <summary>
/// 副标题
/// </summary>
public string ViceTitle { get; set; }
/// <summary>
/// 副描述
/// </summary>
public string? ViceDesc { get; set; }
/// <summary>
/// 水印
/// </summary>
public string? Watermark { get; set; }
/// <summary>
/// 版权信息
/// </summary>
public string? Copyright { get; set; }
/// <summary>
/// ICP备案号
/// </summary>
public string? Icp { get; set; }
/// <summary>
/// 排序
/// </summary>
public int OrderNo { get; set; }
/// <summary>
/// 备注
/// </summary>
public string? Remark { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime? CreateTime { get; set; }
/// <summary>
/// 更新时间
/// </summary>
public DateTime? UpdateTime { get; set; }
/// <summary>
/// 创建者Id
/// </summary>
public long? CreateUserId { get; set; }
/// <summary>
/// 创建者姓名
/// </summary>
public string? CreateUserName { get; set; }
/// <summary>
/// 修改者Id
/// </summary>
public long? UpdateUserId { get; set; }
/// <summary>
/// 修改者姓名
/// </summary>
public string? UpdateUserName { get; set; }
/// <summary>
/// 软删除
/// </summary>
public bool IsDelete { get; set; }
}

View File

@ -0,0 +1,183 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core.Service;
/// <summary>
/// 系统应用服务 🧩
/// </summary>
[ApiDescriptionSettings(Name = "SysApp", Order = 495, Description = "系统应用")]
public class SysAppService : IDynamicApiController, ITransient
{
private readonly SqlSugarRepository<SysAppMenu> _sysAppMenuRep;
private readonly SqlSugarRepository<SysApp> _sysAppRep;
private readonly SysAuthService _sysAuthService;
private readonly UserManager _userManager;
public SysAppService(SqlSugarRepository<SysApp> sysAppRep,
SqlSugarRepository<SysAppMenu> sysAppMenuRep,
SysAuthService sysAuthService,
UserManager userManager)
{
_sysAppRep = sysAppRep;
_userManager = userManager;
_sysAppMenuRep = sysAppMenuRep;
_sysAuthService = sysAuthService;
}
/// <summary>
/// 分页查询应用 🔖
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("分页查询应用")]
[ApiDescriptionSettings(Name = "Page"), HttpPost]
public async Task<SqlSugarPagedList<SysAppOutput>> Page(BasePageInput input)
{
input.Keyword = input.Keyword?.Trim();
var query = _sysAppRep.AsQueryable()
.WhereIF(!string.IsNullOrWhiteSpace(input.Keyword), u => u.Name.Contains(input.Keyword) ||
u.Title.Contains(input.Keyword) || u.ViceTitle.Contains(input.Keyword) ||
u.ViceDesc.Contains(input.Keyword) || u.Remark.Contains(input.Keyword))
.OrderBy(u => new { u.OrderNo, u.Id })
.Select<SysAppOutput>();
return await query.OrderBuilder(input).ToPagedListAsync(input.Page, input.PageSize);
}
/// <summary>
/// 增加应用 🔖
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("增加应用")]
[ApiDescriptionSettings(Name = "Add"), HttpPost]
public async Task<long> Add(AddSysAppInput input)
{
var entity = input.Adapt<SysApp>();
return await _sysAppRep.InsertAsync(entity) ? entity.Id : 0;
}
/// <summary>
/// 更新应用 🔖
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("更新应用")]
[ApiDescriptionSettings(Name = "Update"), HttpPost]
public async Task Update(UpdateSysAppInput input)
{
_ = await _sysAppRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
var entity = input.Adapt<SysApp>();
await _sysAppRep.AsUpdateable(entity).ExecuteCommandAsync();
}
/// <summary>
/// 删除应用 🔖
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("删除应用")]
[ApiDescriptionSettings(Name = "Delete"), HttpPost]
public async Task Delete(BaseIdInput input)
{
var entity = await _sysAppRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
// 禁止删除存在关联租户的应用
if (await _sysAppRep.Context.Queryable<SysTenant>().AnyAsync(u => u.AppId == input.Id)) throw Oops.Oh(ErrorCodeEnum.A1001);
// 禁止删除存在关联菜单的应用
if (await _sysAppMenuRep.AsQueryable().AnyAsync(u => u.AppId == input.Id)) throw Oops.Oh(ErrorCodeEnum.A1002);
await _sysAppRep.DeleteAsync(entity);
}
/// <summary>
/// 获取应用菜单 🔖
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[DisplayName("获取应用菜单")]
public async Task<List<long>> GetMenuList([FromQuery] long id)
{
return await _sysAppMenuRep.AsQueryable().Where(u => u.AppId == id).Select(u => u.MenuId).ToListAsync();
}
/// <summary>
/// 授权应用菜单 🔖
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[UnitOfWork]
[DisplayName("授权应用菜单")]
public async Task GrantMenu(UpdateAppMenuInput input)
{
input.MenuIdList ??= new();
await _sysAppMenuRep.DeleteAsync(u => u.AppId == input.Id);
var list = input.MenuIdList.Select(id => new SysAppMenu { AppId = input.Id, MenuId = id }).ToList();
await _sysAppMenuRep.InsertRangeAsync(list);
// 清除应用下其他模块越权的授权数据,包括角色菜单,用户收藏菜单
var tenantIds = await _sysAppRep.Context.Queryable<SysTenant>().Where(u => u.AppId == input.Id).Select(u => u.Id).ToListAsync();
var roleIds = await _sysAppRep.Context.Queryable<SysRole>().Where(u => tenantIds.Contains((long)u.TenantId)).Select(u => u.Id).ToListAsync();
var userIds = await _sysAppRep.Context.Queryable<SysUser>().Where(u => tenantIds.Contains((long)u.TenantId)).Select(u => u.Id).ToListAsync();
await _sysAppRep.Context.Deleteable<SysRoleMenu>().Where(u => roleIds.Contains(u.RoleId) && !input.MenuIdList.Contains(u.MenuId)).ExecuteCommandAsync();
await _sysAppRep.Context.Deleteable<SysUserMenu>().Where(u => userIds.Contains(u.UserId) && !input.MenuIdList.Contains(u.MenuId)).ExecuteCommandAsync();
}
/// <summary>
/// 获取应用数据 🔖
/// </summary>
/// <returns></returns>
[DisplayName("获取应用数据")]
public async Task<dynamic> GetAppData()
{
var list = await _sysAppRep.AsQueryable().Includes(u => u.TenantList).ToListAsync();
return list.Where(u => u.TenantList.Count > 0).Select(u => new
{
u.Id,
Value = u.Id,
Label = u.Name,
Children = u.TenantList.Select(t => new
{
t.Id,
Value = t.Id,
Label = t.Host ?? (t.Id + ""),
})
});
}
/// <summary>
/// 切换应用 🔖
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("切换应用")]
public async Task<LoginOutput> ChangeApp(ChangeAppInput input)
{
_ = await _sysAppRep.Context.Queryable<SysTenant>().FirstAsync(u => u.Id == input.TenantId) ?? throw Oops.Oh(ErrorCodeEnum.Z1003);
_ = await _sysAppRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
var user = await _sysAppRep.Context.Queryable<SysUser>().FirstAsync(u => u.Id == _userManager.UserId);
user.TenantId = input.TenantId;
return await _sysAuthService.CreateToken(user, input.Id, LoginModeEnum.PC);
}
/// <summary>
/// 获取当前应用信息
/// </summary>
/// <returns></returns>
[NonAction]
public async Task<SysApp> GetCurrentAppInfo()
{
var appId = _userManager.AppId > 0 ? _userManager.AppId : SqlSugarConst.DefaultAppId;
return await _sysAppRep.GetFirstAsync(u => u.Id == appId) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
}
}

View File

@ -25,6 +25,12 @@ public class LoginInput
[Required(ErrorMessage = "密码不能为空"), MinLength(3, ErrorMessage = "密码不能少于3个字符")] [Required(ErrorMessage = "密码不能为空"), MinLength(3, ErrorMessage = "密码不能少于3个字符")]
public string Password { get; set; } public string Password { get; set; }
/// <summary>
/// 租户域名
/// </summary>
[Required(ErrorMessage = "租户域名不能为空")]
public string Host { get; set; }
/// <summary> /// <summary>
/// 验证码Id /// 验证码Id
/// </summary> /// </summary>
@ -58,6 +64,12 @@ public class LoginPhoneInput
[Required(ErrorMessage = "验证码不能为空"), MinLength(4, ErrorMessage = "验证码不能少于4个字符")] [Required(ErrorMessage = "验证码不能为空"), MinLength(4, ErrorMessage = "验证码不能少于4个字符")]
public string Code { get; set; } public string Code { get; set; }
/// <summary>
/// 租户域名
/// </summary>
[Required(ErrorMessage = "租户域名不能为空")]
public string? Host { get; set; }
/// <summary> /// <summary>
/// 登录模式 /// 登录模式
/// </summary> /// </summary>

View File

@ -9,15 +9,10 @@ namespace Admin.NET.Core.Service;
/// <summary> /// <summary>
/// 系统域登录信息配置输入参数 /// 系统域登录信息配置输入参数
/// </summary> /// </summary>
public class PageSysLdapInput : BasePageInput public class SysLdapInput : BasePageInput
{ {
/// <summary> /// <summary>
/// 关键字查询 /// 域名
/// </summary>
public string? SearchKey { get; set; }
/// <summary>
/// 主机
/// </summary> /// </summary>
public string? Host { get; set; } public string? Host { get; set; }
} }

View File

@ -55,31 +55,17 @@ public class SysAuthService : IDynamicApiController, ITransient
var passwordErrorTimes = _sysCacheService.Get<int>(keyPasswordErrorTimes); var passwordErrorTimes = _sysCacheService.Get<int>(keyPasswordErrorTimes);
var passwordMaxErrorTimes = await _sysConfigService.GetConfigValueByCode<int>(ConfigConst.SysPasswordMaxErrorTimes); var passwordMaxErrorTimes = await _sysConfigService.GetConfigValueByCode<int>(ConfigConst.SysPasswordMaxErrorTimes);
// 若未配置或误配置为0、负数, 则默认密码错误次数最大为5次 // 若未配置或误配置为0、负数, 则默认密码错误次数最大为5次
if (passwordMaxErrorTimes < 1) if (passwordMaxErrorTimes < 1) passwordMaxErrorTimes = 5;
passwordMaxErrorTimes = 5; if (passwordErrorTimes > passwordMaxErrorTimes) throw Oops.Oh(ErrorCodeEnum.D1027);
if (passwordErrorTimes > passwordMaxErrorTimes)
throw Oops.Oh(ErrorCodeEnum.D1027);
// 是否开启验证码 // 判断是否开启验证码并校验
if (await _sysConfigService.GetConfigValueByCode<bool>(ConfigConst.SysCaptcha)) if (await _sysConfigService.GetConfigValueByCode<bool>(ConfigConst.SysCaptcha) && !_captcha.Validate(input.CodeId.ToString(), input.Code)) throw Oops.Oh(ErrorCodeEnum.D0008);
{
// 判断验证码
if (!_captcha.Validate(input.CodeId.ToString(), input.Code))
throw Oops.Oh(ErrorCodeEnum.D0008);
}
// 账号是否存在 // 获取登录租户和用户
var user = await _sysUserRep.AsQueryable().Includes(t => t.SysOrg).ClearFilter().FirstAsync(u => u.Account.Equals(input.Account)); var (tenant, user) = await GetLoginUserAndTenant(input.Host, account: input.Account);
_ = user ?? throw Oops.Oh(ErrorCodeEnum.D0009);
// 账号是否被冻结 // 账号是否被冻结
if (user.Status == StatusEnum.Disable) if (user.Status == StatusEnum.Disable) throw Oops.Oh(ErrorCodeEnum.D1017);
throw Oops.Oh(ErrorCodeEnum.D1017);
// 租户是否被禁用
var tenant = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysTenant>>().GetByIdAsync(user.TenantId);
if (tenant != null && tenant.Status == StatusEnum.Disable)
throw Oops.Oh(ErrorCodeEnum.Z1003);
// 是否开启域登录验证 // 是否开启域登录验证
if (await _sysConfigService.GetConfigValueByCode<bool>(ConfigConst.SysDomainLogin)) if (await _sysConfigService.GetConfigValueByCode<bool>(ConfigConst.SysDomainLogin))
@ -87,51 +73,100 @@ public class SysAuthService : IDynamicApiController, ITransient
var userLdap = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysUserLdap>>().GetFirstAsync(u => u.UserId == user.Id && u.TenantId == tenant.Id); var userLdap = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysUserLdap>>().GetFirstAsync(u => u.UserId == user.Id && u.TenantId == tenant.Id);
if (userLdap == null) if (userLdap == null)
{ {
VerifyPassword(input, keyPasswordErrorTimes, passwordErrorTimes, user); VerifyPassword(input.Password, keyPasswordErrorTimes, passwordErrorTimes, user);
} }
else if (!await App.GetRequiredService<SysLdapService>().AuthAccount(tenant.Id, userLdap.Account, input.Password)) else if (!await App.GetRequiredService<SysLdapService>().AuthAccount(tenant.Id, userLdap.Account, CryptogramUtil.Decrypt(input.Password)))
{ {
_sysCacheService.Set(keyPasswordErrorTimes, ++passwordErrorTimes, TimeSpan.FromMinutes(30)); _sysCacheService.Set(keyPasswordErrorTimes, ++passwordErrorTimes, TimeSpan.FromMinutes(30));
throw Oops.Oh(ErrorCodeEnum.D1000); throw Oops.Oh(ErrorCodeEnum.D1000);
} }
} }
else else
VerifyPassword(input, keyPasswordErrorTimes, passwordErrorTimes, user); VerifyPassword(input.Password, keyPasswordErrorTimes, passwordErrorTimes, user);
// 登录成功则清空密码错误次数 // 登录成功则清空密码错误次数
_sysCacheService.Remove(keyPasswordErrorTimes); _sysCacheService.Remove(keyPasswordErrorTimes);
return await CreateToken(user, input.LoginMode); return await CreateToken(user, tenant.AppId, input.LoginMode);
}
/// <summary>
/// 获取登录租户和用户
/// </summary>
/// <param name="host"></param>
/// <param name="account"></param>
/// <param name="phone"></param>
/// <returns></returns>
[NonAction]
public async Task<(SysTenant tenant, SysUser user)> GetLoginUserAndTenant(string host, string account = null, string phone = null)
{
// 是否租户隔离登录验证
var isTenantHostLogin = await _sysConfigService.GetConfigValueByCode<bool>(ConfigConst.SysTenantHostLogin);
SysUser user;
SysTenant tenant;
// 租户隔离登录
if (isTenantHostLogin)
{
// 若租户域名为空或为本地域名,则取默认租户域名
if (string.IsNullOrWhiteSpace(host) || host.StartsWith("localhost")) host = SqlSugarConst.DefaultTenantHost;
// 租户是否存在或已禁用
tenant = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysTenant>>().GetFirstAsync(u => u.Host == host.ToLower());
if (tenant?.Status != StatusEnum.Enable) throw Oops.Oh(ErrorCodeEnum.Z1003);
// 根据入参类型、租户查询登录用户
user = await _sysUserRep.AsQueryable().Includes(t => t.SysOrg).ClearFilter()
.Where(u => u.TenantId == tenant.Id || u.AccountType == AccountTypeEnum.SuperAdmin)
.WhereIF(!string.IsNullOrWhiteSpace(account), u => u.Account.Equals(account))
.WhereIF(!string.IsNullOrWhiteSpace(phone), u => u.Phone.Equals(phone))
.FirstAsync();
_ = user ?? throw Oops.Oh(ErrorCodeEnum.D0009);
// 若登录的是超级管理员,则引用当前绑定的租户,这样登陆后操作的租户数据会与该租户关联
if (user.AccountType == AccountTypeEnum.SuperAdmin) user.TenantId = tenant.Id;
}
else
{
// 根据入参类型查询登录用户
user = await _sysUserRep.AsQueryable().Includes(t => t.SysOrg).ClearFilter()
.WhereIF(!string.IsNullOrWhiteSpace(account), u => u.Account.Equals(account))
.WhereIF(!string.IsNullOrWhiteSpace(phone), u => u.Phone.Equals(phone))
.FirstAsync();
_ = user ?? throw Oops.Oh(ErrorCodeEnum.D0009);
// 租户是否存在或已禁用
tenant = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysTenant>>().GetFirstAsync(u => u.Id == user.TenantId);
if (tenant?.Status != StatusEnum.Enable) throw Oops.Oh(ErrorCodeEnum.Z1003);
}
return (tenant, user);
} }
/// <summary> /// <summary>
/// 验证用户密码 /// 验证用户密码
/// </summary> /// </summary>
/// <param name="input"></param> /// <param name="password"></param>
/// <param name="keyPasswordErrorTimes"></param> /// <param name="keyPasswordErrorTimes"></param>
/// <param name="passwordErrorTimes"></param> /// <param name="passwordErrorTimes"></param>
/// <param name="user"></param> /// <param name="user"></param>
private void VerifyPassword(LoginInput input, string keyPasswordErrorTimes, int passwordErrorTimes, SysUser user) private void VerifyPassword(string password, string keyPasswordErrorTimes, int passwordErrorTimes, SysUser user)
{ {
if (CryptogramUtil.CryptoType == CryptogramEnum.MD5.ToString()) if (CryptogramUtil.CryptoType == CryptogramEnum.MD5.ToString())
{ {
if (!user.Password.Equals(MD5Encryption.Encrypt(input.Password))) if (user.Password.Equals(MD5Encryption.Encrypt(password))) return;
{
_sysCacheService.Set(keyPasswordErrorTimes, ++passwordErrorTimes, TimeSpan.FromMinutes(30));
throw Oops.Oh(ErrorCodeEnum.D1000);
}
}
else
{
// 国密SM2解密前端密码传输SM2加密后的
input.Password = CryptogramUtil.SM2Decrypt(input.Password);
if (!CryptogramUtil.Decrypt(user.Password).Equals(input.Password)) _sysCacheService.Set(keyPasswordErrorTimes, ++passwordErrorTimes, TimeSpan.FromMinutes(30));
{ throw Oops.Oh(ErrorCodeEnum.D1000);
_sysCacheService.Set(keyPasswordErrorTimes, ++passwordErrorTimes, TimeSpan.FromMinutes(30));
throw Oops.Oh(ErrorCodeEnum.D1000);
}
} }
// 国密SM2解密前端密码传输SM2加密后的
password = CryptogramUtil.SM2Decrypt(password);
if (CryptogramUtil.Decrypt(user.Password).Equals(password)) return;
_sysCacheService.Set(keyPasswordErrorTimes, ++passwordErrorTimes, TimeSpan.FromMinutes(30));
throw Oops.Oh(ErrorCodeEnum.D1000);
} }
/// <summary> /// <summary>
@ -143,23 +178,28 @@ public class SysAuthService : IDynamicApiController, ITransient
public virtual async Task<bool> UnLockScreen([Required, FromQuery] string password) public virtual async Task<bool> UnLockScreen([Required, FromQuery] string password)
{ {
// 账号是否存在 // 账号是否存在
var user = await _sysUserRep.GetByIdAsync(_userManager.UserId); var user = await _sysUserRep.GetFirstAsync(u => u.Id == _userManager.UserId);
_ = user ?? throw Oops.Oh(ErrorCodeEnum.D0009); _ = user ?? throw Oops.Oh(ErrorCodeEnum.D0009);
// 国密SM2解密前端密码传输SM2加密后的 var keyPasswordErrorTimes = $"{CacheConst.KeyPasswordErrorTimes}{user.Account}";
password = CryptogramUtil.SM2Decrypt(password); var passwordErrorTimes = _sysCacheService.Get<int>(keyPasswordErrorTimes);
// 密码是否正确 // 是否开启域登录验证
if (CryptogramUtil.CryptoType == CryptogramEnum.MD5.ToString()) if (await _sysConfigService.GetConfigValueByCode<bool>(ConfigConst.SysDomainLogin))
{ {
if (!user.Password.Equals(MD5Encryption.Encrypt(password))) var userLdap = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysUserLdap>>().GetFirstAsync(u => u.UserId == user.Id && u.TenantId == user.TenantId);
if (userLdap == null)
{
VerifyPassword(password, keyPasswordErrorTimes, passwordErrorTimes, user);
}
else if (!await App.GetRequiredService<SysLdapService>().AuthAccount(user.TenantId.Value, userLdap.Account, CryptogramUtil.Decrypt(password)))
{
_sysCacheService.Set(keyPasswordErrorTimes, ++passwordErrorTimes, TimeSpan.FromMinutes(30));
throw Oops.Oh(ErrorCodeEnum.D1000); throw Oops.Oh(ErrorCodeEnum.D1000);
}
} }
else else
{ VerifyPassword(password, keyPasswordErrorTimes, passwordErrorTimes, user);
if (!CryptogramUtil.Decrypt(user.Password).Equals(password))
throw Oops.Oh(ErrorCodeEnum.D1000);
}
return true; return true;
} }
@ -176,21 +216,21 @@ public class SysAuthService : IDynamicApiController, ITransient
// 校验短信验证码 // 校验短信验证码
App.GetRequiredService<SysSmsService>().VerifyCode(new SmsVerifyCodeInput { Phone = input.Phone, Code = input.Code }); App.GetRequiredService<SysSmsService>().VerifyCode(new SmsVerifyCodeInput { Phone = input.Phone, Code = input.Code });
// 账号是否存在 // 获取登录租户和用户
var user = await _sysUserRep.AsQueryable().Includes(u => u.SysOrg).ClearFilter().FirstAsync(u => u.Phone.Equals(input.Phone)); var (tenant, user) = await GetLoginUserAndTenant(input.Host, phone: input.Phone);
_ = user ?? throw Oops.Oh(ErrorCodeEnum.D0009);
return await CreateToken(user, input.LoginMode); return await CreateToken(user, tenant.AppId, input.LoginMode);
} }
/// <summary> /// <summary>
/// 生成Token令牌 🔖 /// 生成Token令牌 🔖
/// </summary> /// </summary>
/// <param name="user"></param> /// <param name="user"></param>
/// <param name="appId"></param>
/// <param name="loginMode"></param> /// <param name="loginMode"></param>
/// <returns></returns> /// <returns></returns>
[NonAction] [NonAction]
internal async Task<LoginOutput> CreateToken(SysUser user, LoginModeEnum loginMode) internal async Task<LoginOutput> CreateToken(SysUser user, long? appId, LoginModeEnum loginMode)
{ {
// 默认PC端登录模式 // 默认PC端登录模式
if (loginMode == 0) if (loginMode == 0)
@ -203,6 +243,7 @@ public class SysAuthService : IDynamicApiController, ITransient
var tokenExpire = await _sysConfigService.GetTokenExpire(); var tokenExpire = await _sysConfigService.GetTokenExpire();
var accessToken = JWTEncryption.Encrypt(new Dictionary<string, object> var accessToken = JWTEncryption.Encrypt(new Dictionary<string, object>
{ {
{ ClaimConst.AppId, appId },
{ ClaimConst.UserId, user.Id }, { ClaimConst.UserId, user.Id },
{ ClaimConst.TenantId, user.TenantId }, { ClaimConst.TenantId, user.TenantId },
{ ClaimConst.Account, user.Account }, { ClaimConst.Account, user.Account },
@ -357,6 +398,7 @@ public class SysAuthService : IDynamicApiController, ITransient
{ {
Account = auth.UserName, Account = auth.UserName,
Password = CryptogramUtil.SM2Encrypt(auth.Password), Password = CryptogramUtil.SM2Encrypt(auth.Password),
Host = SqlSugarConst.DefaultTenantHost
}); });
_sysCacheService.Remove($"{CacheConst.KeyConfig}{ConfigConst.SysCaptcha}"); _sysCacheService.Remove($"{CacheConst.KeyConfig}{ConfigConst.SysCaptcha}");

View File

@ -27,10 +27,10 @@ public class SysLdapService : IDynamicApiController, ITransient
/// <param name="input"></param> /// <param name="input"></param>
/// <returns></returns> /// <returns></returns>
[DisplayName("获取系统域登录配置分页列表")] [DisplayName("获取系统域登录配置分页列表")]
public async Task<SqlSugarPagedList<SysLdap>> Page(PageSysLdapInput input) public async Task<SqlSugarPagedList<SysLdap>> Page(SysLdapInput input)
{ {
return await _sysLdapRep.AsQueryable() return await _sysLdapRep.AsQueryable()
.WhereIF(!string.IsNullOrWhiteSpace(input.SearchKey), u => u.Host.Contains(input.SearchKey.Trim())) .WhereIF(!string.IsNullOrWhiteSpace(input.Keyword), u => u.Host.Contains(input.Keyword.Trim()))
.WhereIF(!string.IsNullOrWhiteSpace(input.Host), u => u.Host.Contains(input.Host.Trim())) .WhereIF(!string.IsNullOrWhiteSpace(input.Host), u => u.Host.Contains(input.Host.Trim()))
.OrderBy(u => u.CreateTime, OrderByType.Desc) .OrderBy(u => u.CreateTime, OrderByType.Desc)
.ToPagedListAsync(input.Page, input.PageSize); .ToPagedListAsync(input.Page, input.PageSize);
@ -78,7 +78,7 @@ public class SysLdapService : IDynamicApiController, ITransient
[DisplayName("删除系统域登录配置")] [DisplayName("删除系统域登录配置")]
public async Task Delete(DeleteSysLdapInput input) public async Task Delete(DeleteSysLdapInput input)
{ {
var entity = await _sysLdapRep.GetByIdAsync(input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002); var entity = await _sysLdapRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
await _sysLdapRep.FakeDeleteAsync(entity); // 假删除 await _sysLdapRep.FakeDeleteAsync(entity); // 假删除
//await _rep.DeleteAsync(entity); // 真删除 //await _rep.DeleteAsync(entity); // 真删除
} }
@ -91,7 +91,7 @@ public class SysLdapService : IDynamicApiController, ITransient
[DisplayName("获取系统域登录配置详情")] [DisplayName("获取系统域登录配置详情")]
public async Task<SysLdap> GetDetail([FromQuery] DetailSysLdapInput input) public async Task<SysLdap> GetDetail([FromQuery] DetailSysLdapInput input)
{ {
return await _sysLdapRep.GetByIdAsync(input.Id); return await _sysLdapRep.GetFirstAsync(u => u.Id == input.Id);
} }
/// <summary> /// <summary>
@ -119,18 +119,17 @@ public class SysLdapService : IDynamicApiController, ITransient
try try
{ {
ldapConn.Connect(sysLdap.Host, sysLdap.Port); ldapConn.Connect(sysLdap.Host, sysLdap.Port);
ldapConn.Bind(sysLdap.Version, sysLdap.BindDn, sysLdap.BindPass); string bindPass = CryptogramUtil.Decrypt(sysLdap.BindPass);
var ldapSearchResults = ldapConn.Search(sysLdap.BaseDn, LdapConnection.ScopeSub, sysLdap.AuthFilter.Replace("$s", account), null, false); ldapConn.Bind(sysLdap.Version, sysLdap.BindDn, bindPass);
var ldapSearchResults = ldapConn.Search(sysLdap.BaseDn, LdapConnection.ScopeSub, sysLdap.AuthFilter.Replace("%s", account), null, false);
string dn = string.Empty; string dn = string.Empty;
while (ldapSearchResults.HasMore()) while (ldapSearchResults.HasMore())
{ {
var ldapEntry = ldapSearchResults.Next(); var ldapEntry = ldapSearchResults.Next();
var sAMAccountName = ldapEntry.GetAttribute(sysLdap.AuthFilter)?.StringValue; var sAmAccountName = ldapEntry.GetAttribute(sysLdap.BindAttrAccount)?.StringValue;
if (!string.IsNullOrEmpty(sAMAccountName)) if (string.IsNullOrEmpty(sAmAccountName)) continue;
{ dn = ldapEntry.Dn;
dn = ldapEntry.Dn; break;
break;
}
} }
if (string.IsNullOrEmpty(dn)) throw Oops.Oh(ErrorCodeEnum.D1002); if (string.IsNullOrEmpty(dn)) throw Oops.Oh(ErrorCodeEnum.D1002);
@ -154,15 +153,39 @@ public class SysLdapService : IDynamicApiController, ITransient
return true; return true;
} }
/// <summary>
/// 同步域用户 🔖
/// </summary>
/// <param name="tenantId"></param>
/// <returns></returns>
[DisplayName("同步域用户")]
[NonAction]
public async Task<List<SysUserLdap>> SyncUserTenant(long tenantId)
{
var sysLdap = await _sysLdapRep.GetFirstAsync(c => c.TenantId == tenantId && c.IsDelete == false && c.Status == StatusEnum.Enable) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
return await SyncUser(sysLdap);
}
/// <summary> /// <summary>
/// 同步域用户 🔖 /// 同步域用户 🔖
/// </summary> /// </summary>
/// <param name="input"></param> /// <param name="input"></param>
/// <returns></returns> /// <returns></returns>
[DisplayName("同步域用户")] [DisplayName("同步域用户")]
public async Task SyncUser(SyncSysLdapInput input) public async Task<List<SysUserLdap>> SyncUser(SyncSysLdapInput input)
{ {
var sysLdap = await _sysLdapRep.GetByIdAsync(input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002); var sysLdap = await _sysLdapRep.GetByIdAsync(input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
return await SyncUser(sysLdap);
}
/// <summary>
/// 同步域用户 🔖
/// </summary>
/// <param name="sysLdap"></param>
/// <returns></returns>
private async Task<List<SysUserLdap>> SyncUser(SysLdap sysLdap)
{
if (sysLdap == null) throw Oops.Oh(ErrorCodeEnum.D1002);
var ldapConn = new LdapConnection(); var ldapConn = new LdapConnection();
try try
{ {
@ -192,15 +215,15 @@ public class SysLdapService : IDynamicApiController, ITransient
else else
{ {
var sysUserLdap = CreateSysUserLdap(attrs, sysLdap.BindAttrAccount, sysLdap.BindAttrEmployeeId, deptCode); var sysUserLdap = CreateSysUserLdap(attrs, sysLdap.BindAttrAccount, sysLdap.BindAttrEmployeeId, deptCode);
if (string.IsNullOrEmpty(sysUserLdap.EmployeeId)) continue; sysUserLdap.Dn = ldapEntry.Dn;
userLdapList.Add(sysUserLdap); userLdapList.Add(sysUserLdap);
} }
} }
if (userLdapList.Count == 0) if (userLdapList.Count == 0) return null;
return;
await App.GetRequiredService<SysUserLdapService>().InsertUserLdaps(sysLdap.TenantId!.Value, userLdapList); await App.GetRequiredService<SysUserLdapService>().InsertUserLdapList(sysLdap.TenantId!.Value, userLdapList);
return userLdapList;
} }
catch (LdapException e) catch (LdapException e)
{ {
@ -225,7 +248,7 @@ public class SysLdapService : IDynamicApiController, ITransient
private static string GetDepartmentCode(LdapAttributeSet attrs, string bindAttrCode) private static string GetDepartmentCode(LdapAttributeSet attrs, string bindAttrCode)
{ {
return bindAttrCode == "objectGUID" return bindAttrCode == "objectGUID"
? new Guid(attrs.GetAttribute(bindAttrCode)?.ByteValue).ToString() ? new Guid(attrs.GetAttribute(bindAttrCode)?.ByteValue!).ToString()
: attrs.GetAttribute(bindAttrCode)?.StringValue ?? "0"; : attrs.GetAttribute(bindAttrCode)?.StringValue ?? "0";
} }
@ -239,12 +262,22 @@ public class SysLdapService : IDynamicApiController, ITransient
/// <returns></returns> /// <returns></returns>
private static SysUserLdap CreateSysUserLdap(LdapAttributeSet attrs, string bindAttrAccount, string bindAttrEmployeeId, string deptCode) private static SysUserLdap CreateSysUserLdap(LdapAttributeSet attrs, string bindAttrAccount, string bindAttrEmployeeId, string deptCode)
{ {
return new SysUserLdap var userLdap = new SysUserLdap
{ {
Account = !attrs.ContainsKey(bindAttrAccount) ? null : attrs.GetAttribute(bindAttrAccount)?.StringValue, Account = !attrs.ContainsKey(bindAttrAccount) ? null : attrs.GetAttribute(bindAttrAccount)?.StringValue,
EmployeeId = !attrs.ContainsKey(bindAttrEmployeeId) ? null : attrs.GetAttribute(bindAttrEmployeeId)?.StringValue, EmployeeId = !attrs.ContainsKey(bindAttrEmployeeId) ? null : attrs.GetAttribute(bindAttrEmployeeId)?.StringValue,
DeptCode = deptCode DeptCode = deptCode,
UserName = !attrs.ContainsKey("name") ? null : attrs.GetAttribute("name")?.StringValue,
Mail = !attrs.ContainsKey("mail") ? null : attrs.GetAttribute("mail")?.StringValue
}; };
var pwdLastSet = !attrs.ContainsKey("pwdLastSet") ? null : attrs.GetAttribute("pwdLastSet")?.StringValue;
if (!pwdLastSet!.Equals("0")) userLdap.PwdLastSetTime = DateTime.FromFileTime(Convert.ToInt64(pwdLastSet));
var userAccountControl = !attrs.ContainsKey("userAccountControl") ? null : attrs.GetAttribute("userAccountControl")?.StringValue;
if ((Convert.ToInt32(userAccountControl) & 0x2) == 0x2) // 检查账户是否已过期通过检查userAccountControl属性的特定位
userLdap.AccountExpiresFlag = true;
if ((Convert.ToInt32(userAccountControl) & 0x10000) == 0x10000) // 检查账户密码设置是否永不过期
userLdap.DontExpiresFlag = true;
return userLdap;
} }
/// <summary> /// <summary>
@ -279,7 +312,7 @@ public class SysLdapService : IDynamicApiController, ITransient
else else
{ {
var sysUserLdap = CreateSysUserLdap(attrs, sysLdap.BindAttrAccount, sysLdap.BindAttrEmployeeId, deptCode); var sysUserLdap = CreateSysUserLdap(attrs, sysLdap.BindAttrAccount, sysLdap.BindAttrEmployeeId, deptCode);
sysUserLdap.Dn = ldapEntry.Dn;
if (string.IsNullOrEmpty(sysUserLdap.EmployeeId)) continue; if (string.IsNullOrEmpty(sysUserLdap.EmployeeId)) continue;
userLdapList.Add(sysUserLdap); userLdapList.Add(sysUserLdap);
} }
@ -294,12 +327,13 @@ public class SysLdapService : IDynamicApiController, ITransient
[DisplayName("同步域组织")] [DisplayName("同步域组织")]
public async Task SyncDept(SyncSysLdapInput input) public async Task SyncDept(SyncSysLdapInput input)
{ {
var sysLdap = await _sysLdapRep.GetByIdAsync(input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002); var sysLdap = await _sysLdapRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
var ldapConn = new LdapConnection(); var ldapConn = new LdapConnection();
try try
{ {
ldapConn.Connect(sysLdap.Host, sysLdap.Port); ldapConn.Connect(sysLdap.Host, sysLdap.Port);
ldapConn.Bind(sysLdap.Version, sysLdap.BindDn, sysLdap.BindPass); string bindPass = CryptogramUtil.Decrypt(sysLdap.BindPass);
ldapConn.Bind(sysLdap.Version, sysLdap.BindDn, bindPass);
var ldapSearchResults = ldapConn.Search(sysLdap.BaseDn, LdapConnection.ScopeOne, "(objectClass=*)", null, false); var ldapSearchResults = ldapConn.Search(sysLdap.BaseDn, LdapConnection.ScopeOne, "(objectClass=*)", null, false);
var orgList = new List<SysOrg>(); var orgList = new List<SysOrg>();
while (ldapSearchResults.HasMore()) while (ldapSearchResults.HasMore())
@ -316,13 +350,12 @@ public class SysLdapService : IDynamicApiController, ITransient
} }
var attrs = ldapEntry.GetAttributeSet(); var attrs = ldapEntry.GetAttributeSet();
if (attrs.Count == 0 || attrs.ContainsKey("OU")) if (attrs.Count != 0 && !attrs.ContainsKey("OU")) continue;
{
var sysOrg = CreateSysOrg(attrs, sysLdap, orgList, new SysOrg { Id = 0, Level = 0 });
orgList.Add(sysOrg);
SearchDnLdapDept(ldapConn, sysLdap, orgList, ldapEntry.Dn, sysOrg); var sysOrg = CreateSysOrg(attrs, sysLdap, orgList, new SysOrg { Id = 0, Level = 0 });
} orgList.Add(sysOrg);
SearchDnLdapDept(ldapConn, sysLdap, orgList, ldapEntry.Dn, sysOrg);
} }
if (orgList.Count == 0) if (orgList.Count == 0)
@ -369,13 +402,12 @@ public class SysLdapService : IDynamicApiController, ITransient
} }
var attrs = ldapEntry.GetAttributeSet(); var attrs = ldapEntry.GetAttributeSet();
if (attrs.Count == 0 || attrs.ContainsKey("OU")) if (attrs.Count != 0 && !attrs.ContainsKey("OU")) continue;
{
var sysOrg = CreateSysOrg(attrs, sysLdap, listOrgs, org);
listOrgs.Add(sysOrg);
SearchDnLdapDept(ldapConn, sysLdap, listOrgs, ldapEntry.Dn, sysOrg); var sysOrg = CreateSysOrg(attrs, sysLdap, listOrgs, org);
} listOrgs.Add(sysOrg);
SearchDnLdapDept(ldapConn, sysLdap, listOrgs, ldapEntry.Dn, sysOrg);
} }
} }

View File

@ -4,7 +4,6 @@
// //
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
using NewLife.Reflection;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace Admin.NET.Core.Service; namespace Admin.NET.Core.Service;
@ -66,8 +65,7 @@ public class SysCacheService : IDynamicApiController, ISingleton
[NonAction] [NonAction]
public bool Set(string key, object value) public bool Set(string key, object value)
{ {
if (string.IsNullOrWhiteSpace(key)) return false; return !string.IsNullOrWhiteSpace(key) && _cacheProvider.Cache.Set($"{_cacheOptions.Prefix}{key}", value);
return _cacheProvider.Cache.Set($"{_cacheOptions.Prefix}{key}", value);
} }
/// <summary> /// <summary>
@ -80,8 +78,7 @@ public class SysCacheService : IDynamicApiController, ISingleton
[NonAction] [NonAction]
public bool Set(string key, object value, TimeSpan expire) public bool Set(string key, object value, TimeSpan expire)
{ {
if (string.IsNullOrWhiteSpace(key)) return false; return !string.IsNullOrWhiteSpace(key) && _cacheProvider.Cache.Set($"{_cacheOptions.Prefix}{key}", value, expire);
return _cacheProvider.Cache.Set($"{_cacheOptions.Prefix}{key}", value, expire);
} }
public async Task<TR> AdGetAsync<TR>(String cacheName, Func<Task<TR>> del, TimeSpan? expiry = default(TimeSpan?)) where TR : class public async Task<TR> AdGetAsync<TR>(String cacheName, Func<Task<TR>> del, TimeSpan? expiry = default(TimeSpan?)) where TR : class
@ -140,28 +137,15 @@ public class SysCacheService : IDynamicApiController, ISingleton
private static string Key(string cacheName, object[] obs) private static string Key(string cacheName, object[] obs)
{ {
foreach (var obj in obs) if (obs.OfType<TimeSpan>().Any()) throw new Exception("缓存参数类型不能能是:TimeSpan类型");
{ StringBuilder sb = new(cacheName + ":");
if (obj is TimeSpan) foreach (var a in obs) sb.Append($"<{KeySingle(a)}>");
{
throw new Exception("缓存参数类型不能能是:TimeSpan类型");
}
}
StringBuilder sb = new StringBuilder(cacheName + ":");
foreach (var a in obs)
{
sb.Append($@"<{KeySingle(a)}>");
}
return sb.ToString(); return sb.ToString();
} }
public static string KeySingle(object t) private static string KeySingle(object t)
{ {
if (t.GetType().IsClass && !t.GetType().IsPrimitive) return t.GetType().IsClass && !t.GetType().IsPrimitive ? JsonConvert.SerializeObject(t) : t.ToString();
{
return JsonConvert.SerializeObject(t);
}
return t?.ToString();
} }
/// <summary> /// <summary>
@ -327,6 +311,23 @@ public class SysCacheService : IDynamicApiController, ISingleton
hash.Add(hashKey, value); hash.Add(hashKey, value);
} }
/// <summary>
/// 添加或更新一条HASH
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="key"></param>
/// <param name="hashKey"></param>
/// <param name="value"></param>
[NonAction]
public void HashAddOrUpdate<T>(string key, string hashKey, T value)
{
var hash = GetHashMap<T>(key);
if (hash.ContainsKey(hashKey))
hash[hashKey] = value;
else
hash.Add(hashKey, value);
}
/// <summary> /// <summary>
/// 获取多条HASH /// 获取多条HASH
/// </summary> /// </summary>
@ -352,12 +353,7 @@ public class SysCacheService : IDynamicApiController, ISingleton
public T HashGetOne<T>(string key, string field) public T HashGetOne<T>(string key, string field)
{ {
var hash = GetHashMap<T>(key); var hash = GetHashMap<T>(key);
var value = hash.GetValue(field); return hash.ContainsKey(field) ? hash[field] : default(T);
if (value == null)
{
return default(T);
}
return (T)hash.GetValue(field);
} }
/// <summary> /// <summary>

View File

@ -6,6 +6,9 @@
namespace Admin.NET.Core.Service; namespace Admin.NET.Core.Service;
/// <summary>
/// 自定义模板引擎
/// </summary>
public class CustomViewEngine : ViewEngineModel public class CustomViewEngine : ViewEngineModel
{ {
private readonly ISqlSugarClient _db; private readonly ISqlSugarClient _db;

View File

@ -161,7 +161,7 @@ public class CodeGenConfig
get get
{ {
string str = ""; string str = "";
if (EffectType == "fk") if (EffectType == "ForeignKey")
{ {
str = LowerFkEntityName + "_FK_" + LowerFkColumnName; str = LowerFkEntityName + "_FK_" + LowerFkColumnName;
} }

View File

@ -9,7 +9,7 @@ namespace Admin.NET.Core.Service;
/// <summary> /// <summary>
/// 代码生成参数类 /// 代码生成参数类
/// </summary> /// </summary>
public class PageCodeGenInput : BasePageInput public class CodeGenInput : BasePageInput
{ {
/// <summary> /// <summary>
/// 作者姓名 /// 作者姓名
@ -112,7 +112,7 @@ public class PageCodeGenInput : BasePageInput
public virtual bool IsApiService { get; set; } public virtual bool IsApiService { get; set; }
} }
public class AddCodeGenInput : PageCodeGenInput public class AddCodeGenInput : CodeGenInput
{ {
/// <summary> /// <summary>
/// 数据库表名 /// 数据库表名

View File

@ -0,0 +1,31 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core.Service;
/// <summary>
/// 表唯一配置项
/// </summary>
public class TableUniqueConfigItem
{
/// <summary>
/// 字段列表
/// </summary>
public List<string> Columns { get; set; }
/// <summary>
/// 描述信息
/// </summary>
public string Message { get; set; }
/// <summary>
/// 格式化查询条件
/// </summary>
/// <param name="separator">分隔符</param>
/// <param name="format">模板字符串</param>
/// <returns></returns>
public string Format(string separator, string format) => string.Join(separator, Columns.Select(name => string.Format(format, name)));
}

View File

@ -39,7 +39,7 @@ public class SysCodeGenService : IDynamicApiController, ITransient
/// <param name="input"></param> /// <param name="input"></param>
/// <returns></returns> /// <returns></returns>
[DisplayName("获取代码生成分页列表")] [DisplayName("获取代码生成分页列表")]
public async Task<SqlSugarPagedList<SysCodeGen>> Page(PageCodeGenInput input) public async Task<SqlSugarPagedList<SysCodeGen>> Page(CodeGenInput input)
{ {
return await _db.Queryable<SysCodeGen>() return await _db.Queryable<SysCodeGen>()
.Includes(u => u.CodeGenTemplateRelations) .Includes(u => u.CodeGenTemplateRelations)
@ -405,7 +405,7 @@ public class SysCodeGenService : IDynamicApiController, ITransient
#endregion #endregion
var queryWhetherList = tableFieldList.Where(u => u.QueryWhether == YesNoEnum.Y.ToString()).ToList(); // 前端查询集合 var queryWhetherList = tableFieldList.Where(u => u.QueryWhether == YesNoEnum.Y.ToString()).ToList(); // 前端查询集合
var joinTableList = tableFieldList.Where(u => u.EffectType == "Upload" || u.EffectType == "fk" || u.EffectType == "ApiTreeSelect").ToList(); // 需要连表查询的字段 var joinTableList = tableFieldList.Where(u => u.EffectType == "Upload" || u.EffectType == "ForeignKey" || u.EffectType == "ApiTreeSelector").ToList(); // 需要连表查询的字段
(string joinTableNames, string lowerJoinTableNames) = GetJoinTableStr(joinTableList); // 获取连表的实体名和别名 (string joinTableNames, string lowerJoinTableNames) = GetJoinTableStr(joinTableList); // 获取连表的实体名和别名
var data = new CustomViewEngine(_db) var data = new CustomViewEngine(_db)
@ -547,7 +547,7 @@ public class SysCodeGenService : IDynamicApiController, ITransient
#endregion #endregion
var queryWhetherList = tableFieldList.Where(u => u.QueryWhether == YesNoEnum.Y.ToString()).ToList(); // 前端查询集合 var queryWhetherList = tableFieldList.Where(u => u.QueryWhether == YesNoEnum.Y.ToString()).ToList(); // 前端查询集合
var joinTableList = tableFieldList.Where(u => u.EffectType == "Upload" || u.EffectType == "fk" || u.EffectType == "ApiTreeSelect").ToList(); // 需要连表查询的字段 var joinTableList = tableFieldList.Where(u => u.EffectType == "Upload" || u.EffectType == "ForeignKey" || u.EffectType == "ApiTreeSelector").ToList(); // 需要连表查询的字段
(string joinTableNames, string lowerJoinTableNames) = GetJoinTableStr(joinTableList); // 获取连表的实体名和别名 (string joinTableNames, string lowerJoinTableNames) = GetJoinTableStr(joinTableList); // 获取连表的实体名和别名
var data = new CustomViewEngine(_db) var data = new CustomViewEngine(_db)
@ -628,7 +628,7 @@ public class SysCodeGenService : IDynamicApiController, ITransient
private static (string, string) GetJoinTableStr(List<CodeGenConfig> configs) private static (string, string) GetJoinTableStr(List<CodeGenConfig> configs)
{ {
var uploads = configs.Where(u => u.EffectType == "Upload").ToList(); var uploads = configs.Where(u => u.EffectType == "Upload").ToList();
var fks = configs.Where(u => u.EffectType == "fk").ToList(); var fks = configs.Where(u => u.EffectType == "ForeignKey").ToList();
string str = ""; // <Order, OrderItem, Custom> string str = ""; // <Order, OrderItem, Custom>
string lowerStr = ""; // (o, i, c) string lowerStr = ""; // (o, i, c)
foreach (var item in uploads) foreach (var item in uploads)
@ -855,9 +855,9 @@ public class SysCodeGenService : IDynamicApiController, ITransient
menuOrder += 10; menuOrder += 10;
var menuList = new List<SysMenu>() { menuTypePage, menuTypeDetail, menuTypeAdd, menuTypeDelete, menuTypeUpdate, menuTypePrint, menuTypeImport, menuTypeExport }; var menuList = new List<SysMenu>() { menuTypePage, menuTypeDetail, menuTypeAdd, menuTypeDelete, menuTypeUpdate, menuTypePrint, menuTypeImport, menuTypeExport };
// 加入fk、Upload、ApiTreeSelect 等接口的权限 // 加入ForeignKey、Upload、ApiTreeSelector 等接口的权限
// 在生成表格时有些字段只是查询时显示不需要填写WhetherAddUpdate所以这些字段没必要生成相应接口 // 在生成表格时有些字段只是查询时显示不需要填写WhetherAddUpdate所以这些字段没必要生成相应接口
var fkTableList = tableFieldList.Where(u => u.EffectType == "fk" && (u.WhetherAddUpdate == "Y" || u.QueryWhether == "Y")).ToList(); var fkTableList = tableFieldList.Where(u => u.EffectType == "ForeignKey" && (u.WhetherAddUpdate == "Y" || u.QueryWhether == "Y")).ToList();
foreach (var @column in fkTableList) foreach (var @column in fkTableList)
{ {
var menuType1 = new SysMenu var menuType1 = new SysMenu
@ -873,7 +873,7 @@ public class SysCodeGenService : IDynamicApiController, ITransient
menuOrder += 10; menuOrder += 10;
menuList.Add(menuType1); menuList.Add(menuType1);
} }
var treeSelectTableList = tableFieldList.Where(u => u.EffectType == "ApiTreeSelect").ToList(); var treeSelectTableList = tableFieldList.Where(u => u.EffectType == "ApiTreeSelector").ToList();
foreach (var @column in treeSelectTableList) foreach (var @column in treeSelectTableList)
{ {
var menuType1 = new SysMenu var menuType1 = new SysMenu

View File

@ -192,8 +192,7 @@ public class SysCommonService : IDynamicApiController, ITransient
var userId = App.User?.FindFirst(ClaimConst.UserId)?.Value; var userId = App.User?.FindFirst(ClaimConst.UserId)?.Value;
var resultStream = App.GetRequiredService<SysCacheService>().Get<MemoryStream>(CacheConst.KeyExcelTemp + userId); var resultStream = App.GetRequiredService<SysCacheService>().Get<MemoryStream>(CacheConst.KeyExcelTemp + userId);
if (resultStream == null) if (resultStream == null) throw Oops.Oh("错误标记文件已过期。");
throw Oops.Oh("错误标记文件已过期。");
return await Task.FromResult(new FileStreamResult(resultStream, "application/octet-stream") return await Task.FromResult(new FileStreamResult(resultStream, "application/octet-stream")
{ {

View File

@ -61,8 +61,7 @@ public class SysConfigService : IDynamicApiController, ITransient
public async Task AddConfig(AddConfigInput input) public async Task AddConfig(AddConfigInput input)
{ {
var isExist = await _sysConfigRep.IsAnyAsync(u => u.Name == input.Name || u.Code == input.Code); var isExist = await _sysConfigRep.IsAnyAsync(u => u.Name == input.Name || u.Code == input.Code);
if (isExist) if (isExist) throw Oops.Oh(ErrorCodeEnum.D9000);
throw Oops.Oh(ErrorCodeEnum.D9000);
await _sysConfigRep.InsertAsync(input.Adapt<SysConfig>()); await _sysConfigRep.InsertAsync(input.Adapt<SysConfig>());
} }
@ -78,8 +77,7 @@ public class SysConfigService : IDynamicApiController, ITransient
public async Task UpdateConfig(UpdateConfigInput input) public async Task UpdateConfig(UpdateConfigInput input)
{ {
var isExist = await _sysConfigRep.IsAnyAsync(u => (u.Name == input.Name || u.Code == input.Code) && u.Id != input.Id); var isExist = await _sysConfigRep.IsAnyAsync(u => (u.Name == input.Name || u.Code == input.Code) && u.Id != input.Id);
if (isExist) if (isExist) throw Oops.Oh(ErrorCodeEnum.D9000);
throw Oops.Oh(ErrorCodeEnum.D9000);
//// 若修改国密SM2密匙则密码重新加密 //// 若修改国密SM2密匙则密码重新加密
//if (input.Code == ConfigConst.SysSM2Key && CryptogramUtil.CryptoType == CryptogramEnum.SM2.ToString()) //if (input.Code == ConfigConst.SysSM2Key && CryptogramUtil.CryptoType == CryptogramEnum.SM2.ToString())
@ -96,7 +94,7 @@ public class SysConfigService : IDynamicApiController, ITransient
var config = input.Adapt<SysConfig>(); var config = input.Adapt<SysConfig>();
await _sysConfigRep.AsUpdateable(config).IgnoreColumns(true).ExecuteCommandAsync(); await _sysConfigRep.AsUpdateable(config).IgnoreColumns(true).ExecuteCommandAsync();
_sysCacheService.Remove($"{CacheConst.KeyConfig}{config.Code}"); RemoveConfigCache(config);
} }
/// <summary> /// <summary>
@ -109,12 +107,12 @@ public class SysConfigService : IDynamicApiController, ITransient
public async Task DeleteConfig(DeleteConfigInput input) public async Task DeleteConfig(DeleteConfigInput input)
{ {
var config = await _sysConfigRep.GetByIdAsync(input.Id); var config = await _sysConfigRep.GetByIdAsync(input.Id);
if (config.SysFlag == YesNoEnum.Y) // 禁止删除系统参数 // 禁止删除系统参数
throw Oops.Oh(ErrorCodeEnum.D9001); if (config.SysFlag == YesNoEnum.Y) throw Oops.Oh(ErrorCodeEnum.D9001);
await _sysConfigRep.DeleteAsync(config); await _sysConfigRep.DeleteAsync(config);
_sysCacheService.Remove($"{CacheConst.KeyConfig}{config.Code}"); RemoveConfigCache(config);
} }
/// <summary> /// <summary>
@ -129,12 +127,12 @@ public class SysConfigService : IDynamicApiController, ITransient
foreach (var id in ids) foreach (var id in ids)
{ {
var config = await _sysConfigRep.GetByIdAsync(id); var config = await _sysConfigRep.GetByIdAsync(id);
if (config.SysFlag == YesNoEnum.Y) // 禁止删除系统参数 // 禁止删除系统参数
continue; if (config.SysFlag == YesNoEnum.Y) continue;
await _sysConfigRep.DeleteAsync(config); await _sysConfigRep.DeleteAsync(config);
_sysCacheService.Remove($"{CacheConst.KeyConfig}{config.Code}"); RemoveConfigCache(config);
} }
} }
@ -207,7 +205,7 @@ public class SysConfigService : IDynamicApiController, ITransient
config.Value = value; config.Value = value;
await _sysConfigRep.AsUpdateable(config).ExecuteCommandAsync(); await _sysConfigRep.AsUpdateable(config).ExecuteCommandAsync();
_sysCacheService.Remove($"{CacheConst.KeyConfig}{config.Code}"); RemoveConfigCache(config);
} }
/// <summary> /// <summary>
@ -256,10 +254,13 @@ public class SysConfigService : IDynamicApiController, ITransient
[DisplayName("批量更新参数配置值")] [DisplayName("批量更新参数配置值")]
public async Task BatchUpdateConfig(List<BatchConfigInput> input) public async Task BatchUpdateConfig(List<BatchConfigInput> input)
{ {
foreach (var Config in input) foreach (var config in input)
{ {
await _sysConfigRep.AsUpdateable().SetColumns(u => u.Value == Config.Value).Where(u => u.Code == Config.Code).ExecuteCommandAsync(); var configInfo = await _sysConfigRep.GetFirstAsync(u => u.Code == config.Code);
_sysCacheService.Remove($"{CacheConst.KeyConfig}{Config.Code}"); if (configInfo == null) continue;
await _sysConfigRep.AsUpdateable().SetColumns(u => u.Value == config.Value).Where(u => u.Code == config.Code).ExecuteCommandAsync();
RemoveConfigCache(configInfo);
} }
} }
@ -354,4 +355,16 @@ public class SysConfigService : IDynamicApiController, ITransient
await UpdateConfigValue(ConfigConst.SysSecondVer, (input.SysSecondVer ?? false).ToString()); await UpdateConfigValue(ConfigConst.SysSecondVer, (input.SysSecondVer ?? false).ToString());
await UpdateConfigValue(ConfigConst.SysCaptcha, (input.SysCaptcha ?? true).ToString()); await UpdateConfigValue(ConfigConst.SysCaptcha, (input.SysCaptcha ?? true).ToString());
} }
/// <summary>
/// 清除配置缓存
/// </summary>
/// <param name="config"></param>
private void RemoveConfigCache(SysConfig config)
{
_sysCacheService.Remove($"{CacheConst.KeyConfig}Value:{config.Code}");
_sysCacheService.Remove($"{CacheConst.KeyConfig}Remark:{config.Code}");
_sysCacheService.Remove($"{CacheConst.KeyConfig}{config.GroupCode}:GroupWithCache");
_sysCacheService.Remove($"{CacheConst.KeyConfig}{config.Code}");
}
} }

View File

@ -27,19 +27,19 @@ public class SysConstService : IDynamicApiController, ITransient
public async Task<List<ConstOutput>> GetList() public async Task<List<ConstOutput>> GetList()
{ {
var key = $"{CacheConst.KeyConst}list"; var key = $"{CacheConst.KeyConst}list";
var constlist = _sysCacheService.Get<List<ConstOutput>>(key); var constList = _sysCacheService.Get<List<ConstOutput>>(key);
if (constlist == null) if (constList == null)
{ {
var typeList = GetConstAttributeList(); var typeList = GetConstAttributeList();
constlist = typeList.Select(u => new ConstOutput constList = typeList.Select(u => new ConstOutput
{ {
Name = u.CustomAttributes.ToList().FirstOrDefault()?.ConstructorArguments.ToList().FirstOrDefault().Value?.ToString() ?? u.Name, Name = u.CustomAttributes.ToList().FirstOrDefault()?.ConstructorArguments.ToList().FirstOrDefault().Value?.ToString() ?? u.Name,
Code = u.Name, Code = u.Name,
Data = GetData(Convert.ToString(u.Name)) Data = GetData(Convert.ToString(u.Name))
}).ToList(); }).ToList();
_sysCacheService.Set(key, constlist); _sysCacheService.Set(key, constList);
} }
return await Task.FromResult(constlist); return await Task.FromResult(constList);
} }
/// <summary> /// <summary>
@ -51,25 +51,25 @@ public class SysConstService : IDynamicApiController, ITransient
public async Task<List<ConstOutput>> GetData([Required] string typeName) public async Task<List<ConstOutput>> GetData([Required] string typeName)
{ {
var key = $"{CacheConst.KeyConst}{typeName.ToUpper()}"; var key = $"{CacheConst.KeyConst}{typeName.ToUpper()}";
var constlist = _sysCacheService.Get<List<ConstOutput>>(key); var constList = _sysCacheService.Get<List<ConstOutput>>(key);
if (constlist == null) if (constList == null)
{ {
var typeList = GetConstAttributeList(); var typeList = GetConstAttributeList();
var type = typeList.FirstOrDefault(u => u.Name == typeName); var type = typeList.FirstOrDefault(u => u.Name == typeName);
if (type != null) if (type != null)
{ {
var isEnum = type.BaseType.Name == "Enum"; var isEnum = type.BaseType!.Name == "Enum";
constlist = type.GetFields()? constList = type.GetFields()?
.Where(isEnum, u => u.FieldType.Name == typeName) .Where(isEnum, u => u.FieldType.Name == typeName)
.Select(u => new ConstOutput .Select(u => new ConstOutput
{ {
Name = u.Name, Name = u.Name,
Code = isEnum ? (int)u.GetValue(BindingFlags.Instance) : u.GetValue(BindingFlags.Instance) Code = isEnum ? (int)u.GetValue(BindingFlags.Instance)! : u.GetValue(BindingFlags.Instance)
}).ToList(); }).ToList();
_sysCacheService.Set(key, constlist); _sysCacheService.Set(key, constList);
} }
} }
return await Task.FromResult(constlist); return await Task.FromResult(constList);
} }
/// <summary> /// <summary>

View File

@ -4,8 +4,6 @@
// //
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Npgsql; using Npgsql;
namespace Admin.NET.Core.Service; namespace Admin.NET.Core.Service;
@ -213,7 +211,7 @@ public class SysDatabaseService : IDynamicApiController, ITransient
var typeBuilder = db.DynamicBuilder().CreateClass(input.TableName, new SugarTable() { TableName = input.TableName, TableDescription = input.Description }); var typeBuilder = db.DynamicBuilder().CreateClass(input.TableName, new SugarTable() { TableName = input.TableName, TableDescription = input.Description });
input.DbColumnInfoList.ForEach(u => input.DbColumnInfoList.ForEach(u =>
{ {
var dbColumnName = config.DbSettings.EnableUnderLine ? UtilMethods.ToUnderLine(u.DbColumnName.Trim()) : u.DbColumnName.Trim(); var dbColumnName = config!.DbSettings.EnableUnderLine ? UtilMethods.ToUnderLine(u.DbColumnName.Trim()) : u.DbColumnName.Trim();
// 虚拟类都默认string类型具体以列数据类型为准 // 虚拟类都默认string类型具体以列数据类型为准
typeBuilder.CreateProperty(dbColumnName, typeof(string), new SugarColumn() typeBuilder.CreateProperty(dbColumnName, typeof(string), new SugarColumn()
{ {
@ -339,67 +337,60 @@ public class SysDatabaseService : IDynamicApiController, ITransient
input.EntityName = entityType.Name; input.EntityName = entityType.Name;
input.SeedDataName = entityType.Name + "SeedData"; input.SeedDataName = entityType.Name + "SeedData";
if (!string.IsNullOrWhiteSpace(input.Suffix)) if (!string.IsNullOrWhiteSpace(input.Suffix)) input.SeedDataName += input.Suffix;
input.SeedDataName += input.Suffix;
var targetPath = GetSeedDataTargetPath(input);
// 查询所有数据 // 查询所有数据
var query = db.QueryableByObject(entityType); var query = db.QueryableByObject(entityType);
DbColumnInfo orderField = null; // 排序字段
// 优先用创建时间排序 // 优先用创建时间排序
orderField = dbColumnInfos.Where(u => u.DbColumnName.ToLower() == "create_time" || u.DbColumnName.ToLower() == "createtime").FirstOrDefault(); DbColumnInfo orderField = dbColumnInfos.FirstOrDefault(u => u.DbColumnName.ToLower() == "create_time" || u.DbColumnName.ToLower() == "createtime");
if (orderField != null) if (orderField != null) query = query.OrderBy(orderField.DbColumnName);
query.OrderBy(orderField.DbColumnName); // 再使用第一个主键排序
// 其次用Id排序 query = query.OrderBy(dbColumnInfos.First(u => u.IsPrimarykey).DbColumnName);
orderField = dbColumnInfos.Where(u => u.DbColumnName.ToLower() == "id").FirstOrDefault(); var records = ((IEnumerable)await query.ToListAsync()).ToDynamicList();
if (orderField != null)
query.OrderBy(orderField.DbColumnName);
IEnumerable recordsTmp = (IEnumerable)query.ToList();
List<dynamic> records = recordsTmp.ToDynamicList();
// 过滤已存在的数据 // 过滤已存在的数据
if (input.FilterExistingData && records.Count() > 0) if (input.FilterExistingData && records.Any())
{ {
// 获取实体类型 // 获取实体类型-所有种数据数据类型
var entityTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false) && u.FullName.EndsWith("." + input.EntityName)) var entityTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false) && u.FullName.EndsWith("." + input.EntityName))
.Where(u => !u.GetCustomAttributes<IgnoreTableAttribute>().Any()).ToList(); .Where(u => !u.GetCustomAttributes<IgnoreTableAttribute>().Any())
// 只有一个实体匹配才能过滤 .ToList();
if (entityTypes.Count == 1) if (entityTypes.Count == 1) // 只有一个实体匹配才能过滤
{ {
// 获取实体的主键对应的属性名称 // 获取实体的主键对应的属性名称
var pkInfo = entityTypes[0].GetProperties().Where(u => u.GetCustomAttribute<SugarColumn>() != null && u.GetCustomAttribute<SugarColumn>().IsPrimaryKey).First(); var pkInfo = entityTypes[0].GetProperties().FirstOrDefault(u => u.GetCustomAttribute<SugarColumn>()?.IsPrimaryKey == true);
if (pkInfo != null) if (pkInfo != null)
{ {
var seedDataTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass var seedDataTypes = App.EffectiveTypes
&& u.GetInterfaces().Any(i => i.HasImplementedRawGeneric(typeof(ISqlSugarEntitySeedData<>)) && i.GenericTypeArguments[0] == entityTypes[0])).ToList(); .Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.GetInterfaces().Any(
i => i.HasImplementedRawGeneric(typeof(ISqlSugarEntitySeedData<>)) && i.GenericTypeArguments[0] == entityTypes[0]
)
)
.ToList();
// 可能会重名的种子数据不作为过滤项 // 可能会重名的种子数据不作为过滤项
string doNotFilterfullName1 = $"{input.Position}.SeedData.{input.SeedDataName}"; string doNotFilterFullName1 = $"{input.Position}.SeedData.{input.SeedDataName}";
string doNotFilterfullName2 = $"{input.Position}.{input.SeedDataName}"; // Core中的命名空间没有SeedData string doNotFilterFullName2 = $"{input.Position}.{input.SeedDataName}"; // Core中的命名空间没有SeedData
PropertyInfo idPropertySeedData = records[0].GetType().GetProperty("Id"); PropertyInfo idPropertySeedData = records[0].GetType().GetProperty("Id");
for (int i = seedDataTypes.Count - 1; i >= 0; i--) for (int i = seedDataTypes.Count - 1; i >= 0; i--)
{ {
string fullName = seedDataTypes[i].FullName; string fullName = seedDataTypes[i].FullName;
if ((fullName == doNotFilterfullName1) || (fullName == doNotFilterfullName2)) if ((fullName == doNotFilterFullName1) || (fullName == doNotFilterFullName2)) continue;
continue;
// 开始删除重复数据 // 删除重复数据
var instance = Activator.CreateInstance(seedDataTypes[i]); var instance = Activator.CreateInstance(seedDataTypes[i]);
var hasDataMethod = seedDataTypes[i].GetMethod("HasData"); var hasDataMethod = seedDataTypes[i].GetMethod("HasData");
var seedData = ((IEnumerable)hasDataMethod?.Invoke(instance, null))?.Cast<object>(); var seedData = ((IEnumerable)hasDataMethod?.Invoke(instance, null))?.Cast<object>();
if (seedData == null) continue; if (seedData == null) continue;
var recordsToRemove = new List<object>(); List<object> recordsToRemove = new();
foreach (var record in records) foreach (var record in records)
{ {
object recordId = pkInfo.GetValue(record); object recordId = pkInfo.GetValue(record);
foreach (var d1 in seedData) if (seedData.Select(d1 => idPropertySeedData.GetValue(d1)).Any(dataId => recordId != null && dataId != null && recordId.Equals(dataId)))
{ {
object dataId = idPropertySeedData.GetValue(d1); recordsToRemove.Add(record);
if (recordId != null && dataId != null && recordId.Equals(dataId))
{
recordsToRemove.Add(record);
break;
}
} }
} }
foreach (var itemToRemove in recordsToRemove) foreach (var itemToRemove in recordsToRemove)
@ -410,39 +401,71 @@ public class SysDatabaseService : IDynamicApiController, ITransient
} }
} }
} }
var timeConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd HH:mm:ss" };
var recordsJSON = JsonConvert.SerializeObject(records, Formatting.Indented, timeConverter);
// 检查有没有 System.Text.Json.Serialization.JsonIgnore 的属性 // 检查有没有 System.Text.Json.Serialization.JsonIgnore 的属性
var jsonIgnoreProperties = entityType.GetProperties().Where(p => (p.GetAttribute<System.Text.Json.Serialization.JsonIgnoreAttribute>() != null || // var jsonIgnoreProperties = entityType.GetProperties().Where(p => (p.GetAttribute<System.Text.Json.Serialization.JsonIgnoreAttribute>() != null ||
p.GetAttribute<JsonIgnoreAttribute>() != null) && p.GetAttribute<SugarColumn>() != null).ToList(); // p.GetAttribute<JsonIgnoreAttribute>() != null) && p.GetAttribute<SugarColumn>() != null).ToList();
var jsonIgnoreInfo = new List<List<JsonIgnoredPropertyData>>(); // var jsonIgnoreInfo = new List<List<JsonIgnoredPropertyData>>();
if (jsonIgnoreProperties.Count > 0) // if (jsonIgnoreProperties.Count > 0)
{ // {
int recordIndex = 0; // int recordIndex = 0;
foreach (var r in (IEnumerable)records) // foreach (var r in (IEnumerable)records)
{ // {
List<JsonIgnoredPropertyData> record = new(); // List<JsonIgnoredPropertyData> record = new();
foreach (var item in jsonIgnoreProperties) // foreach (var item in jsonIgnoreProperties)
{ // {
object v = item.GetValue(r); // object v = item.GetValue(r);
string strValue = "null"; // string strValue = "null";
if (v != null) // if (v != null)
{ // {
strValue = v.ToString(); // strValue = v.ToString();
if (v.GetType() == typeof(string)) // if (v.GetType() == typeof(string))
strValue = "\"" + strValue + "\""; // strValue = "\"" + strValue + "\"";
else if (v.GetType() == typeof(DateTime)) // else if (v.GetType() == typeof(DateTime))
strValue = "DateTime.Parse(\"" + ((DateTime)v).ToString("yyyy-MM-dd HH:mm:ss") + "\")"; // strValue = "DateTime.Parse(\"" + ((DateTime)v).ToString("yyyy-MM-dd HH:mm:ss") + "\")";
} // }
record.Add(new JsonIgnoredPropertyData { RecordIndex = recordIndex, Name = item.Name, Value = strValue }); // record.Add(new JsonIgnoredPropertyData { RecordIndex = recordIndex, Name = item.Name, Value = strValue });
} // }
recordIndex++; // recordIndex++;
jsonIgnoreInfo.Add(record); // jsonIgnoreInfo.Add(record);
} // }
} // }
var tContent = File.ReadAllText(templatePath); // 获取所有字段信息
var propertyList = entityType.GetProperties().Where(x => false == (x.GetCustomAttribute<SugarColumn>()?.IsIgnore ?? false)).ToList();
for (var i = 0; i < propertyList.Count; i++)
{
if (propertyList[i].Name != nameof(EntityBaseId.Id) || !(propertyList[i].GetCustomAttribute<SugarColumn>()?.IsPrimaryKey ?? true)) continue;
var temp = propertyList[i];
for (var j = i; j > 0; j--) propertyList[j] = propertyList[j - 1];
propertyList[0] = temp;
}
// 拼接数据
var recordList = records.Select(obj => string.Join(", ", propertyList.Select(prop =>
{
var propType = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
object value = prop.GetValue(obj);
if (value == null) value = "null";
else if (propType == typeof(string))
{
value = $"\"{value}\"";
}
else if (propType.IsEnum)
{
value = $"{propType.Name}.{value}";
}
else if (propType == typeof(bool))
{
value = (bool)value ? "true" : "false";
}
else if (propType == typeof(DateTime))
{
value = $"DateTime.Parse(\"{((DateTime)value):yyyy-MM-dd HH:mm:ss.fff}\")";
}
return $"{prop.Name}={value}";
}))).ToList();
var tContent = await File.ReadAllTextAsync(templatePath);
var data = new var data = new
{ {
NameSpace = $"{input.Position}.SeedData", NameSpace = $"{input.Position}.SeedData",
@ -452,17 +475,19 @@ public class SysDatabaseService : IDynamicApiController, ITransient
input.SeedDataName, input.SeedDataName,
input.ConfigId, input.ConfigId,
tableInfo.Description, tableInfo.Description,
JsonIgnoreInfo = jsonIgnoreInfo, // JsonIgnoreInfo = jsonIgnoreInfo,
RecordsJSON = recordsJSON RecordList = recordList
}; };
var tResult = _viewEngine.RunCompile(tContent, data, builderAction: builder => var tResult = await _viewEngine.RunCompileAsync(tContent, data, builderAction: builder =>
{ {
builder.AddAssemblyReferenceByName("System.Linq"); builder.AddAssemblyReferenceByName("System.Linq");
builder.AddAssemblyReferenceByName("System.Collections"); builder.AddAssemblyReferenceByName("System.Collections");
builder.AddUsing("System.Collections.Generic"); builder.AddUsing("System.Collections.Generic");
builder.AddUsing("System.Linq"); builder.AddUsing("System.Linq");
}); });
File.WriteAllText(targetPath, tResult, Encoding.UTF8);
var targetPath = GetSeedDataTargetPath(input);
await File.WriteAllTextAsync(targetPath, tResult, Encoding.UTF8);
} }
/// <summary> /// <summary>
@ -477,27 +502,13 @@ public class SysDatabaseService : IDynamicApiController, ITransient
var types = new List<Type>(); var types = new List<Type>();
if (_codeGenOptions.EntityAssemblyNames != null) if (_codeGenOptions.EntityAssemblyNames != null)
{ {
foreach (var assemblyName in _codeGenOptions.EntityAssemblyNames) foreach (var asm in _codeGenOptions.EntityAssemblyNames.Select(Assembly.Load))
{ {
Assembly asm = Assembly.Load(assemblyName);
types.AddRange(asm.GetExportedTypes().ToList()); types.AddRange(asm.GetExportedTypes().ToList());
} }
} }
bool IsMyAttribute(Attribute[] o)
{
foreach (Attribute a in o)
{
if (a.GetType() == type)
return true;
}
return false;
}
Type[] cosType = types.Where(o =>
{
return IsMyAttribute(Attribute.GetCustomAttributes(o, true));
}
).ToArray();
Type[] cosType = types.Where(u => IsMyAttribute(Attribute.GetCustomAttributes(u, true))).ToArray();
foreach (var c in cosType) foreach (var c in cosType)
{ {
var sugarAttribute = c.GetCustomAttributes(type, true)?.FirstOrDefault(); var sugarAttribute = c.GetCustomAttributes(type, true)?.FirstOrDefault();
@ -517,6 +528,11 @@ public class SysDatabaseService : IDynamicApiController, ITransient
}); });
} }
return await Task.FromResult(entityInfos); return await Task.FromResult(entityInfos);
bool IsMyAttribute(Attribute[] o)
{
return o.Any(a => a.GetType() == type);
}
} }
/// <summary> /// <summary>

View File

@ -71,6 +71,8 @@ public class SysDictTypeService : IDynamicApiController, ITransient
[DisplayName("添加字典类型")] [DisplayName("添加字典类型")]
public async Task AddDictType(AddDictTypeInput input) public async Task AddDictType(AddDictTypeInput input)
{ {
if (input.Code.ToLower().EndsWith("enum")) throw Oops.Oh(ErrorCodeEnum.D3006);
var isExist = await _sysDictTypeRep.IsAnyAsync(u => u.Code == input.Code); var isExist = await _sysDictTypeRep.IsAnyAsync(u => u.Code == input.Code);
if (isExist) throw Oops.Oh(ErrorCodeEnum.D3001); if (isExist) throw Oops.Oh(ErrorCodeEnum.D3001);
@ -87,10 +89,12 @@ public class SysDictTypeService : IDynamicApiController, ITransient
[DisplayName("更新字典类型")] [DisplayName("更新字典类型")]
public async Task UpdateDictType(UpdateDictTypeInput input) public async Task UpdateDictType(UpdateDictTypeInput input)
{ {
var isExist = await _sysDictTypeRep.IsAnyAsync(u => u.Id == input.Id); var dict = await _sysDictTypeRep.GetFirstAsync(x => x.Id == input.Id);
if (!isExist) throw Oops.Oh(ErrorCodeEnum.D3000); if (dict == null) throw Oops.Oh(ErrorCodeEnum.D3000);
isExist = await _sysDictTypeRep.IsAnyAsync(u => u.Code == input.Code && u.Id != input.Id); if (dict.Code.ToLower().EndsWith("enum") && input.Code != dict.Code) throw Oops.Oh(ErrorCodeEnum.D3007);
var isExist = await _sysDictTypeRep.IsAnyAsync(u => u.Code == input.Code && u.Id != input.Id);
if (isExist) throw Oops.Oh(ErrorCodeEnum.D3001); if (isExist) throw Oops.Oh(ErrorCodeEnum.D3001);
_sysCacheService.Remove($"{CacheConst.KeyDict}{input.Code}"); _sysCacheService.Remove($"{CacheConst.KeyDict}{input.Code}");

View File

@ -32,12 +32,7 @@ public class SysEnumService : IDynamicApiController, ITransient
.OrderBy(u => u.Name).ThenBy(u => u.FullName) .OrderBy(u => u.Name).ThenBy(u => u.FullName)
.ToList(); .ToList();
var result = new List<EnumTypeOutput>(); return enumTypeList.Select(GetEnumDescription).ToList();
foreach (var item in enumTypeList)
{
result.Add(GetEnumDescription(item));
}
return result;
} }
/// <summary> /// <summary>

View File

@ -85,8 +85,13 @@ public class UploadFileInput
/// <summary> /// <summary>
/// 上传文件Base64 /// 上传文件Base64
/// </summary> /// </summary>
public class UploadFileFromBase64Input : SysFile public class UploadFileFromBase64Input
{ {
/// <summary>
/// 文件名
/// </summary>
public string FileName { get; set; }
/// <summary> /// <summary>
/// 文件内容 /// 文件内容
/// </summary> /// </summary>

View File

@ -94,14 +94,11 @@ public class SysFileService : IDynamicApiController, ITransient
/// <param name="files"></param> /// <param name="files"></param>
/// <returns></returns> /// <returns></returns>
[DisplayName("上传多文件")] [DisplayName("上传多文件")]
public async Task<List<SysFile>> UploadFiles([Required] List<IFormFile> files) public List<SysFile> UploadFiles([Required] List<IFormFile> files)
{ {
var filelist = new List<SysFile>(); var fileList = new List<SysFile>();
foreach (var file in files) files.ForEach(file => fileList.Add(UploadFile(new UploadFileInput { File = file }).Result));
{ return fileList;
filelist.Add(await UploadFile(new UploadFileInput { File = file }));
}
return filelist;
} }
/// <summary> /// <summary>
@ -114,26 +111,7 @@ public class SysFileService : IDynamicApiController, ITransient
{ {
var file = input.Id > 0 ? await GetFile(input.Id) : await _sysFileRep.CopyNew().GetFirstAsync(u => u.Url == input.Url); var file = input.Id > 0 ? await GetFile(input.Id) : await _sysFileRep.CopyNew().GetFirstAsync(u => u.Url == input.Url);
var fileName = HttpUtility.UrlEncode(file.FileName, Encoding.GetEncoding("UTF-8")); var fileName = HttpUtility.UrlEncode(file.FileName, Encoding.GetEncoding("UTF-8"));
var filePath = Path.Combine(file.FilePath, file.Id.ToString() + file.Suffix); return await GetFileStreamResult(file, fileName);
if (_OSSProviderOptions.Enabled)
{
var stream = await (await _OSSService.PresignedGetObjectAsync(file.BucketName.ToString(), filePath, 5)).GetAsStreamAsync();
return new FileStreamResult(stream.Stream, "application/octet-stream") { FileDownloadName = fileName + file.Suffix };
}
else if (App.Configuration["SSHProvider:Enabled"].ToBoolean())
{
using (SSHHelper helper = new SSHHelper(App.Configuration["SSHProvider:Host"],
App.Configuration["SSHProvider:Port"].ToInt(), App.Configuration["SSHProvider:Username"], App.Configuration["SSHProvider:Password"]))
{
return new FileStreamResult(helper.OpenRead(filePath), "application/octet-stream") { FileDownloadName = fileName + file.Suffix };
}
}
else
{
var path = Path.Combine(App.WebHostEnvironment.WebRootPath, filePath);
return new FileStreamResult(new FileStream(path, FileMode.Open), "application/octet-stream") { FileDownloadName = fileName + file.Suffix };
}
} }
/// <summary> /// <summary>
@ -146,26 +124,33 @@ public class SysFileService : IDynamicApiController, ITransient
{ {
var file = await GetFile(id); var file = await GetFile(id);
//var fileName = HttpUtility.UrlEncode(file.FileName, Encoding.GetEncoding("UTF-8")); //var fileName = HttpUtility.UrlEncode(file.FileName, Encoding.GetEncoding("UTF-8"));
var filePath = Path.Combine(file.FilePath, file.Id.ToString() + file.Suffix); return await GetFileStreamResult(file, file.Id + "");
}
/// <summary>
/// 获取文件流
/// </summary>
/// <param name="file"></param>
/// <param name="fileName"></param>
/// <returns></returns>
private async Task<IActionResult> GetFileStreamResult(SysFile file, string fileName)
{
var filePath = Path.Combine(file.FilePath ?? "", file.Id + file.Suffix);
if (_OSSProviderOptions.Enabled) if (_OSSProviderOptions.Enabled)
{ {
var stream = await (await _OSSService.PresignedGetObjectAsync(file.BucketName.ToString(), filePath, 5)).GetAsStreamAsync(); var stream = await (await _OSSService.PresignedGetObjectAsync(file.BucketName, filePath, 5)).GetAsStreamAsync();
return new FileStreamResult(stream.Stream, "application/octet-stream"); return new FileStreamResult(stream.Stream, "application/octet-stream") { FileDownloadName = fileName + file.Suffix };
} }
else if (App.Configuration["SSHProvider:Enabled"].ToBoolean())
if (App.Configuration["SSHProvider:Enabled"].ToBoolean())
{ {
using (SSHHelper helper = new SSHHelper(App.Configuration["SSHProvider:Host"], using SSHHelper helper = new(App.Configuration["SSHProvider:Host"],
App.Configuration["SSHProvider:Port"].ToInt(), App.Configuration["SSHProvider:Username"], App.Configuration["SSHProvider:Password"])) App.Configuration["SSHProvider:Port"].ToInt(), App.Configuration["SSHProvider:Username"], App.Configuration["SSHProvider:Password"]);
{ return new FileStreamResult(helper.OpenRead(filePath), "application/octet-stream") { FileDownloadName = fileName + file.Suffix };
return new FileStreamResult(helper.OpenRead(filePath), "application/octet-stream");
}
}
else
{
var path = Path.Combine(App.WebHostEnvironment.WebRootPath, filePath);
return new FileStreamResult(new FileStream(path, FileMode.Open), "application/octet-stream");
} }
var path = Path.Combine(App.WebHostEnvironment.WebRootPath, filePath);
return new FileStreamResult(new FileStream(path, FileMode.Open), "application/octet-stream") { FileDownloadName = fileName + file.Suffix };
} }
/// <summary> /// <summary>
@ -186,19 +171,15 @@ public class SysFileService : IDynamicApiController, ITransient
byte[] fileBytes = await response.Content.ReadAsByteArrayAsync(); byte[] fileBytes = await response.Content.ReadAsByteArrayAsync();
return Convert.ToBase64String(fileBytes); return Convert.ToBase64String(fileBytes);
} }
else throw new HttpRequestException($"Request failed with status code: {response.StatusCode}");
{
throw new HttpRequestException($"Request failed with status code: {response.StatusCode}");
}
} }
else if (App.Configuration["SSHProvider:Enabled"].ToBoolean())
if (App.Configuration["SSHProvider:Enabled"].ToBoolean())
{ {
var sysFile = await _sysFileRep.CopyNew().GetFirstAsync(u => u.Url == url) ?? throw Oops.Oh($"文件不存在"); var sysFile = await _sysFileRep.CopyNew().GetFirstAsync(u => u.Url == url) ?? throw Oops.Oh($"文件不存在");
using (SSHHelper helper = new SSHHelper(App.Configuration["SSHProvider:Host"], using SSHHelper helper = new SSHHelper(App.Configuration["SSHProvider:Host"],
App.Configuration["SSHProvider:Port"].ToInt(), App.Configuration["SSHProvider:Username"], App.Configuration["SSHProvider:Password"])) App.Configuration["SSHProvider:Port"].ToInt(), App.Configuration["SSHProvider:Username"], App.Configuration["SSHProvider:Password"]);
{ return Convert.ToBase64String(helper.ReadAllBytes(sysFile.FilePath));
return Convert.ToBase64String(helper.ReadAllBytes(sysFile.FilePath));
}
} }
else else
{ {
@ -213,7 +194,7 @@ public class SysFileService : IDynamicApiController, ITransient
Log.Error($"DownloadFileBase64:文件[{realFile}]不存在"); Log.Error($"DownloadFileBase64:文件[{realFile}]不存在");
throw Oops.Oh($"文件[{sysFile.FilePath}]不存在"); throw Oops.Oh($"文件[{sysFile.FilePath}]不存在");
} }
byte[] fileBytes = File.ReadAllBytes(realFile); byte[] fileBytes = await File.ReadAllBytesAsync(realFile);
return Convert.ToBase64String(fileBytes); return Convert.ToBase64String(fileBytes);
} }
} }
@ -234,22 +215,19 @@ public class SysFileService : IDynamicApiController, ITransient
if (_OSSProviderOptions.Enabled) if (_OSSProviderOptions.Enabled)
{ {
await _OSSService.RemoveObjectAsync(file.BucketName.ToString(), string.Concat(file.FilePath, "/", $"{input.Id}{file.Suffix}")); await _OSSService.RemoveObjectAsync(file.BucketName, string.Concat(file.FilePath, "/", $"{input.Id}{file.Suffix}"));
} }
else if (App.Configuration["SSHProvider:Enabled"].ToBoolean()) else if (App.Configuration["SSHProvider:Enabled"].ToBoolean())
{ {
var fullPath = string.Concat(file.FilePath, "/", file.Id + file.Suffix); var fullPath = string.Concat(file.FilePath, "/", file.Id + file.Suffix);
using (SSHHelper helper = new SSHHelper(App.Configuration["SSHProvider:Host"], using SSHHelper helper = new(App.Configuration["SSHProvider:Host"],
App.Configuration["SSHProvider:Port"].ToInt(), App.Configuration["SSHProvider:Username"], App.Configuration["SSHProvider:Password"])) App.Configuration["SSHProvider:Port"].ToInt(), App.Configuration["SSHProvider:Username"], App.Configuration["SSHProvider:Password"]);
{ helper.DeleteFile(fullPath);
helper.DeleteFile(fullPath);
}
} }
else else
{ {
var filePath = Path.Combine(App.WebHostEnvironment.WebRootPath, file.FilePath, input.Id.ToString() + file.Suffix); var filePath = Path.Combine(App.WebHostEnvironment.WebRootPath, file.FilePath ?? "", input.Id + file.Suffix);
if (File.Exists(filePath)) if (File.Exists(filePath)) File.Delete(filePath);
File.Delete(filePath);
} }
} }
} }
@ -311,7 +289,7 @@ public class SysFileService : IDynamicApiController, ITransient
var fileMd5 = string.Empty; var fileMd5 = string.Empty;
if (_uploadOptions.EnableMd5) if (_uploadOptions.EnableMd5)
{ {
using (var fileStream = input.File.OpenReadStream()) await using (var fileStream = input.File.OpenReadStream())
{ {
fileMd5 = OssUtils.ComputeContentMd5(fileStream, fileStream.Length); fileMd5 = OssUtils.ComputeContentMd5(fileStream, fileStream.Length);
} }
@ -322,12 +300,10 @@ public class SysFileService : IDynamicApiController, ITransient
} }
// 验证文件类型 // 验证文件类型
if (!_uploadOptions.ContentType.Contains(input.File.ContentType)) if (!_uploadOptions.ContentType.Contains(input.File.ContentType)) throw Oops.Oh($"{ErrorCodeEnum.D8001}:{input.File.ContentType}");
throw Oops.Oh($"{ErrorCodeEnum.D8001}:{input.File.ContentType}");
// 验证文件大小 // 验证文件大小
if (sizeKb > _uploadOptions.MaxSize) if (sizeKb > _uploadOptions.MaxSize) throw Oops.Oh($"{ErrorCodeEnum.D8002},允许最大:{_uploadOptions.MaxSize}KB");
throw Oops.Oh($"{ErrorCodeEnum.D8002},允许最大:{_uploadOptions.MaxSize}KB");
// 获取文件后缀 // 获取文件后缀
var suffix = Path.GetExtension(input.File.FileName).ToLower(); // 后缀 var suffix = Path.GetExtension(input.File.FileName).ToLower(); // 后缀
@ -341,14 +317,11 @@ public class SysFileService : IDynamicApiController, ITransient
if (suffix == ".jpeg" || suffix == ".jpe") if (suffix == ".jpeg" || suffix == ".jpe")
suffix = ".jpg"; suffix = ".jpg";
} }
if (string.IsNullOrWhiteSpace(suffix)) if (string.IsNullOrWhiteSpace(suffix)) throw Oops.Oh(ErrorCodeEnum.D8003);
throw Oops.Oh(ErrorCodeEnum.D8003);
// 防止客户端伪造文件类型 // 防止客户端伪造文件类型
if (!string.IsNullOrWhiteSpace(input.AllowSuffix) && !input.AllowSuffix.Contains(suffix)) if (!string.IsNullOrWhiteSpace(input.AllowSuffix) && !input.AllowSuffix.Contains(suffix)) throw Oops.Oh(ErrorCodeEnum.D8003);
throw Oops.Oh(ErrorCodeEnum.D8003); //if (!VerifyFileExtensionName.IsSameType(file.OpenReadStream(), suffix)) throw Oops.Oh(ErrorCodeEnum.D8001);
//if (!VerifyFileExtensionName.IsSameType(file.OpenReadStream(), suffix))
// throw Oops.Oh(ErrorCodeEnum.D8001);
// 文件存储位置 // 文件存储位置
var path = string.IsNullOrWhiteSpace(input.SavePath) ? _uploadOptions.Path : input.SavePath; var path = string.IsNullOrWhiteSpace(input.SavePath) ? _uploadOptions.Path : input.SavePath;
@ -395,11 +368,9 @@ public class SysFileService : IDynamicApiController, ITransient
else if (App.Configuration["SSHProvider:Enabled"].ToBoolean()) else if (App.Configuration["SSHProvider:Enabled"].ToBoolean())
{ {
var fullPath = string.Concat(path.StartsWith('/') ? path : "/" + path, "/", finalName); var fullPath = string.Concat(path.StartsWith('/') ? path : "/" + path, "/", finalName);
using (SSHHelper helper = new SSHHelper(App.Configuration["SSHProvider:Host"], using SSHHelper helper = new(App.Configuration["SSHProvider:Host"],
App.Configuration["SSHProvider:Port"].ToInt(), App.Configuration["SSHProvider:Username"], App.Configuration["SSHProvider:Password"])) App.Configuration["SSHProvider:Port"].ToInt(), App.Configuration["SSHProvider:Username"], App.Configuration["SSHProvider:Password"]);
{ helper.UploadFile(input.File.OpenReadStream(), fullPath);
helper.UploadFile(input.File.OpenReadStream(), fullPath);
}
} }
else else
{ {
@ -409,7 +380,7 @@ public class SysFileService : IDynamicApiController, ITransient
Directory.CreateDirectory(filePath); Directory.CreateDirectory(filePath);
var realFile = Path.Combine(filePath, finalName); var realFile = Path.Combine(filePath, finalName);
using (var stream = File.Create(realFile)) await using (var stream = File.Create(realFile))
{ {
await input.File.CopyToAsync(stream); await input.File.CopyToAsync(stream);
} }
@ -495,8 +466,8 @@ public class SysFileService : IDynamicApiController, ITransient
public async Task<List<SysFile>> GetRelationFiles([FromQuery] RelationQueryInput input) public async Task<List<SysFile>> GetRelationFiles([FromQuery] RelationQueryInput input)
{ {
return await _sysFileRep.AsQueryable() return await _sysFileRep.AsQueryable()
.WhereIF(input.RelationId.HasValue && input.RelationId > 0, u => u.RelationId == input.RelationId) .WhereIF(input.RelationId is > 0, u => u.RelationId == input.RelationId)
.WhereIF(input.BelongId.HasValue && input.BelongId > 0, u => u.BelongId == input.BelongId.Value) .WhereIF(input.BelongId is > 0, u => u.BelongId == input.BelongId.Value)
.WhereIF(!string.IsNullOrWhiteSpace(input.RelationName), u => u.RelationName == input.RelationName) .WhereIF(!string.IsNullOrWhiteSpace(input.RelationName), u => u.RelationName == input.RelationName)
.WhereIF(!string.IsNullOrWhiteSpace(input.FileTypes), u => input.GetFileTypeBS().Contains(u.FileType)) .WhereIF(!string.IsNullOrWhiteSpace(input.FileTypes), u => input.GetFileTypeBS().Contains(u.FileType))
.Select(u => new SysFile .Select(u => new SysFile

View File

@ -33,7 +33,7 @@ public class DbJobPersistence : IJobPersistence
// 获取所有定义的作业 // 获取所有定义的作业
var allJobs = App.EffectiveTypes.ScanToBuilders().ToList(); var allJobs = App.EffectiveTypes.ScanToBuilders().ToList();
// 若数据库不存在任何作业,则直接返回 // 若数据库不存在任何作业,则直接返回
if (!db.Queryable<SysJobDetail>().Any(u => true)) return allJobs; if (!await db.Queryable<SysJobDetail>().AnyAsync(u => true, stoppingToken)) return allJobs;
// 遍历所有定义的作业 // 遍历所有定义的作业
foreach (var schedulerBuilder in allJobs) foreach (var schedulerBuilder in allJobs)

View File

@ -11,11 +11,8 @@ namespace Admin.NET.Core.Service;
/// </summary> /// </summary>
public class JobClusterServer : IJobClusterServer public class JobClusterServer : IJobClusterServer
{ {
private readonly Random rd = new(DateTime.Now.Millisecond); private static readonly SqlSugarRepository<SysJobCluster> _sysJobClusterRep = App.GetRequiredService<SqlSugarRepository<SysJobCluster>>();
private readonly Random _random = new(DateTime.Now.Millisecond);
public JobClusterServer()
{
}
/// <summary> /// <summary>
/// 当前作业调度器启动通知 /// 当前作业调度器启动通知
@ -23,7 +20,6 @@ public class JobClusterServer : IJobClusterServer
/// <param name="context">作业集群服务上下文</param> /// <param name="context">作业集群服务上下文</param>
public async void Start(JobClusterContext context) public async void Start(JobClusterContext context)
{ {
var _sysJobClusterRep = App.GetRequiredService<SqlSugarRepository<SysJobCluster>>();
// 在作业集群表中,如果 clusterId 不存在,则新增一条(否则更新一条),并设置 status 为 ClusterStatus.Waiting // 在作业集群表中,如果 clusterId 不存在,则新增一条(否则更新一条),并设置 status 为 ClusterStatus.Waiting
if (await _sysJobClusterRep.IsAnyAsync(u => u.ClusterId == context.ClusterId)) if (await _sysJobClusterRep.IsAnyAsync(u => u.ClusterId == context.ClusterId))
{ {
@ -47,21 +43,19 @@ public class JobClusterServer : IJobClusterServer
while (true) while (true)
{ {
// 控制集群心跳频率(放在头部为了防止 IsAnyAsync continue 没sleep占用大量IO和CPU // 控制集群心跳频率(放在头部为了防止 IsAnyAsync continue 没sleep占用大量IO和CPU
await Task.Delay(3000 + rd.Next(500, 1000)); // 错开集群同时启动 await Task.Delay(3000 + _random.Next(500, 1000)); // 错开集群同时启动
try try
{ {
ICache _cache = App.GetRequiredService<ICacheProvider>().Cache; ICache cache = App.GetRequiredService<ICacheProvider>().Cache;
// 使用分布式锁 // 使用分布式锁
using (_cache.AcquireLock("lock:JobClusterServer:WaitingForAsync", 1000)) using (cache.AcquireLock("lock:JobClusterServer:WaitingForAsync", 1000))
{ {
var _sysJobClusterRep = App.GetRequiredService<SqlSugarRepository<SysJobCluster>>();
// 在这里查询数据库,根据以下两种情况处理 // 在这里查询数据库,根据以下两种情况处理
// 1) 如果作业集群表已有 status 为 ClusterStatus.Working 则继续循环 // 1) 如果作业集群表已有 status 为 ClusterStatus.Working 则继续循环
// 2) 如果作业集群表中还没有其他服务或只有自己,则插入一条集群服务或调用 await WorkNowAsync(clusterId); 之后 return; // 2) 如果作业集群表中还没有其他服务或只有自己,则插入一条集群服务或调用 await WorkNowAsync(clusterId); 之后 return;
// 3) 如果作业集群表中没有 status 为 ClusterStatus.Working 的,调用 await WorkNowAsync(clusterId); 之后 return; // 3) 如果作业集群表中没有 status 为 ClusterStatus.Working 的,调用 await WorkNowAsync(clusterId); 之后 return;
if (await _sysJobClusterRep.IsAnyAsync(u => u.Status == ClusterStatus.Working)) if (await _sysJobClusterRep.IsAnyAsync(u => u.Status == ClusterStatus.Working)) continue;
continue;
await WorkNowAsync(clusterId); await WorkNowAsync(clusterId);
return; return;
@ -77,7 +71,6 @@ public class JobClusterServer : IJobClusterServer
/// <param name="context">作业集群服务上下文</param> /// <param name="context">作业集群服务上下文</param>
public async void Stop(JobClusterContext context) public async void Stop(JobClusterContext context)
{ {
var _sysJobClusterRep = App.GetRequiredService<SqlSugarRepository<SysJobCluster>>();
// 在作业集群表中,更新 clusterId 的 status 为 ClusterStatus.Crashed // 在作业集群表中,更新 clusterId 的 status 为 ClusterStatus.Crashed
await _sysJobClusterRep.UpdateAsync(u => new SysJobCluster { Status = ClusterStatus.Crashed }, u => u.ClusterId == context.ClusterId); await _sysJobClusterRep.UpdateAsync(u => new SysJobCluster { Status = ClusterStatus.Crashed }, u => u.ClusterId == context.ClusterId);
} }
@ -88,7 +81,6 @@ public class JobClusterServer : IJobClusterServer
/// <param name="context">作业集群服务上下文</param> /// <param name="context">作业集群服务上下文</param>
public async void Crash(JobClusterContext context) public async void Crash(JobClusterContext context)
{ {
var _sysJobClusterRep = App.GetRequiredService<SqlSugarRepository<SysJobCluster>>();
// 在作业集群表中,更新 clusterId 的 status 为 ClusterStatus.Crashed // 在作业集群表中,更新 clusterId 的 status 为 ClusterStatus.Crashed
await _sysJobClusterRep.UpdateAsync(u => new SysJobCluster { Status = ClusterStatus.Crashed }, u => u.ClusterId == context.ClusterId); await _sysJobClusterRep.UpdateAsync(u => new SysJobCluster { Status = ClusterStatus.Crashed }, u => u.ClusterId == context.ClusterId);
} }
@ -100,7 +92,6 @@ public class JobClusterServer : IJobClusterServer
/// <returns></returns> /// <returns></returns>
private static async Task WorkNowAsync(string clusterId) private static async Task WorkNowAsync(string clusterId)
{ {
var _sysJobClusterRep = App.GetRequiredService<SqlSugarRepository<SysJobCluster>>();
// 在作业集群表中,更新 clusterId 的 status 为 ClusterStatus.Working // 在作业集群表中,更新 clusterId 的 status 为 ClusterStatus.Working
await _sysJobClusterRep.UpdateAsync(u => new SysJobCluster { Status = ClusterStatus.Working }, u => u.ClusterId == clusterId); await _sysJobClusterRep.UpdateAsync(u => new SysJobCluster { Status = ClusterStatus.Working }, u => u.ClusterId == clusterId);
} }

View File

@ -12,14 +12,13 @@ namespace Admin.NET.Core.Service;
public class JobMonitor : IJobMonitor public class JobMonitor : IJobMonitor
{ {
private readonly IEventPublisher _eventPublisher; private readonly IEventPublisher _eventPublisher;
private readonly IServiceScope _serviceScope;
private readonly SysConfigService _sysConfigService; private readonly SysConfigService _sysConfigService;
public JobMonitor(IServiceScopeFactory scopeFactory) public JobMonitor(IServiceScopeFactory scopeFactory)
{ {
_serviceScope = scopeFactory.CreateScope(); var serviceScope = scopeFactory.CreateScope();
_sysConfigService = _serviceScope.ServiceProvider.GetRequiredService<SysConfigService>(); _sysConfigService = serviceScope.ServiceProvider.GetRequiredService<SysConfigService>();
_eventPublisher = _serviceScope.ServiceProvider.GetRequiredService<IEventPublisher>(); ; _eventPublisher = serviceScope.ServiceProvider.GetRequiredService<IEventPublisher>(); ;
} }
public Task OnExecutingAsync(JobExecutingContext context, CancellationToken stoppingToken) public Task OnExecutingAsync(JobExecutingContext context, CancellationToken stoppingToken)

View File

@ -83,8 +83,7 @@ public class SysJobService : IDynamicApiController, ITransient
public async Task AddJobDetail(AddJobDetailInput input) public async Task AddJobDetail(AddJobDetailInput input)
{ {
var isExist = await _sysJobDetailRep.IsAnyAsync(u => u.JobId == input.JobId && u.Id != input.Id); var isExist = await _sysJobDetailRep.IsAnyAsync(u => u.JobId == input.JobId && u.Id != input.Id);
if (isExist) if (isExist) throw Oops.Oh(ErrorCodeEnum.D1006);
throw Oops.Oh(ErrorCodeEnum.D1006);
// 动态创建作业 // 动态创建作业
Type jobType; Type jobType;
@ -110,9 +109,7 @@ public class SysJobService : IDynamicApiController, ITransient
throw new NotSupportedException(); throw new NotSupportedException();
} }
_schedulerFactory.AddJob( _schedulerFactory.AddJob(JobBuilder.Create(jobType).LoadFrom(input.Adapt<SysJobDetail>()).SetJobType(jobType));
JobBuilder.Create(jobType)
.LoadFrom(input.Adapt<SysJobDetail>()).SetJobType(jobType));
// 延迟一下等待持久化写入,再执行其他字段的更新 // 延迟一下等待持久化写入,再执行其他字段的更新
await Task.Delay(500); await Task.Delay(500);
@ -130,12 +127,10 @@ public class SysJobService : IDynamicApiController, ITransient
public async Task UpdateJobDetail(UpdateJobDetailInput input) public async Task UpdateJobDetail(UpdateJobDetailInput input)
{ {
var isExist = await _sysJobDetailRep.IsAnyAsync(u => u.JobId == input.JobId && u.Id != input.Id); var isExist = await _sysJobDetailRep.IsAnyAsync(u => u.JobId == input.JobId && u.Id != input.Id);
if (isExist) if (isExist) throw Oops.Oh(ErrorCodeEnum.D1006);
throw Oops.Oh(ErrorCodeEnum.D1006);
var sysJobDetail = await _sysJobDetailRep.GetByIdAsync(input.Id); var sysJobDetail = await _sysJobDetailRep.GetFirstAsync(u => u.Id == input.Id);
if (sysJobDetail.JobId != input.JobId) if (sysJobDetail.JobId != input.JobId) throw Oops.Oh(ErrorCodeEnum.D1704);
throw Oops.Oh(ErrorCodeEnum.D1704);
var scheduler = _schedulerFactory.GetJob(sysJobDetail.JobId); var scheduler = _schedulerFactory.GetJob(sysJobDetail.JobId);
var oldScriptCode = sysJobDetail.ScriptCode; // 旧脚本代码 var oldScriptCode = sysJobDetail.ScriptCode; // 旧脚本代码
@ -143,8 +138,7 @@ public class SysJobService : IDynamicApiController, ITransient
if (input.CreateType == JobCreateTypeEnum.Script) if (input.CreateType == JobCreateTypeEnum.Script)
{ {
if (string.IsNullOrEmpty(input.ScriptCode)) if (string.IsNullOrEmpty(input.ScriptCode)) throw Oops.Oh(ErrorCodeEnum.D1701);
throw Oops.Oh(ErrorCodeEnum.D1701);
if (input.ScriptCode != oldScriptCode) if (input.ScriptCode != oldScriptCode)
{ {
@ -153,8 +147,7 @@ public class SysJobService : IDynamicApiController, ITransient
if (jobType.GetCustomAttributes(typeof(JobDetailAttribute)).FirstOrDefault() is not JobDetailAttribute jobDetailAttribute) if (jobType.GetCustomAttributes(typeof(JobDetailAttribute)).FirstOrDefault() is not JobDetailAttribute jobDetailAttribute)
throw Oops.Oh(ErrorCodeEnum.D1702); throw Oops.Oh(ErrorCodeEnum.D1702);
if (jobDetailAttribute.JobId != input.JobId) if (jobDetailAttribute.JobId != input.JobId) throw Oops.Oh(ErrorCodeEnum.D1703);
throw Oops.Oh(ErrorCodeEnum.D1703);
scheduler?.UpdateDetail(JobBuilder.Create(jobType).LoadFrom(sysJobDetail).SetJobType(jobType)); scheduler?.UpdateDetail(JobBuilder.Create(jobType).LoadFrom(sysJobDetail).SetJobType(jobType));
} }
@ -205,8 +198,7 @@ public class SysJobService : IDynamicApiController, ITransient
public async Task AddJobTrigger(AddJobTriggerInput input) public async Task AddJobTrigger(AddJobTriggerInput input)
{ {
var isExist = await _sysJobTriggerRep.IsAnyAsync(u => u.TriggerId == input.TriggerId && u.Id != input.Id); var isExist = await _sysJobTriggerRep.IsAnyAsync(u => u.TriggerId == input.TriggerId && u.Id != input.Id);
if (isExist) if (isExist) throw Oops.Oh(ErrorCodeEnum.D1006);
throw Oops.Oh(ErrorCodeEnum.D1006);
var jobTrigger = input.Adapt<SysJobTrigger>(); var jobTrigger = input.Adapt<SysJobTrigger>();
jobTrigger.Args = "[" + jobTrigger.Args + "]"; jobTrigger.Args = "[" + jobTrigger.Args + "]";
@ -224,8 +216,7 @@ public class SysJobService : IDynamicApiController, ITransient
public async Task UpdateJobTrigger(UpdateJobTriggerInput input) public async Task UpdateJobTrigger(UpdateJobTriggerInput input)
{ {
var isExist = await _sysJobTriggerRep.IsAnyAsync(u => u.TriggerId == input.TriggerId && u.Id != input.Id); var isExist = await _sysJobTriggerRep.IsAnyAsync(u => u.TriggerId == input.TriggerId && u.Id != input.Id);
if (isExist) if (isExist) throw Oops.Oh(ErrorCodeEnum.D1006);
throw Oops.Oh(ErrorCodeEnum.D1006);
var jobTrigger = input.Adapt<SysJobTrigger>(); var jobTrigger = input.Adapt<SysJobTrigger>();
jobTrigger.Args = "[" + jobTrigger.Args + "]"; jobTrigger.Args = "[" + jobTrigger.Args + "]";
@ -303,8 +294,7 @@ public class SysJobService : IDynamicApiController, ITransient
[DisplayName("执行作业")] [DisplayName("执行作业")]
public void RunJob(JobDetailInput input) public void RunJob(JobDetailInput input)
{ {
if (_schedulerFactory.TryRunJob(input.JobId, out _) != ScheduleResult.Succeed) if (_schedulerFactory.TryRunJob(input.JobId, out _) != ScheduleResult.Succeed) throw Oops.Oh(ErrorCodeEnum.D1705);
throw Oops.Oh(ErrorCodeEnum.D1705);
} }
/// <summary> /// <summary>

View File

@ -17,6 +17,16 @@ public class MenuInput
/// 菜单类型1目录 2菜单 3按钮 /// 菜单类型1目录 2菜单 3按钮
/// </summary> /// </summary>
public MenuTypeEnum? Type { get; set; } public MenuTypeEnum? Type { get; set; }
/// <summary>
/// 获取所有菜单
/// </summary>
public bool All { get; set; }
/// <summary>
/// 应用Id
/// </summary>
public long AppId { get; set; }
} }
public class AddMenuInput : SysMenu public class AddMenuInput : SysMenu

View File

@ -37,11 +37,13 @@ public class SysEmailService : IDynamicApiController, ITransient
var webTitle = await _sysConfigService.GetConfigValueByCode<string>(ConfigConst.SysWebTitle); var webTitle = await _sysConfigService.GetConfigValueByCode<string>(ConfigConst.SysWebTitle);
title = string.IsNullOrWhiteSpace(title) ? $"{webTitle} 系统邮件" : title; title = string.IsNullOrWhiteSpace(title) ? $"{webTitle} 系统邮件" : title;
var message = new MimeMessage(); var message = new MimeMessage();
message.From.Add(new MailboxAddress(_emailOptions.DefaultFromEmail, _emailOptions.DefaultFromEmail)); message.From.Add(new MailboxAddress(_emailOptions.DefaultFromEmail, _emailOptions.DefaultFromEmail));
if (string.IsNullOrWhiteSpace(toEmail))
message.To.Add(new MailboxAddress(_emailOptions.DefaultToEmail, _emailOptions.DefaultToEmail)); message.To.Add(string.IsNullOrWhiteSpace(toEmail)
else ? new MailboxAddress(_emailOptions.DefaultToEmail, _emailOptions.DefaultToEmail)
message.To.Add(new MailboxAddress(toEmail, toEmail)); : new MailboxAddress(toEmail, toEmail));
message.Subject = title; message.Subject = title;
message.Body = new TextPart("html") message.Body = new TextPart("html")
{ {

View File

@ -16,15 +16,12 @@ public class SysMessageService : IDynamicApiController, ITransient
{ {
private readonly SysCacheService _sysCacheService; private readonly SysCacheService _sysCacheService;
private readonly IHubContext<OnlineUserHub, IOnlineUserHub> _chatHubContext; private readonly IHubContext<OnlineUserHub, IOnlineUserHub> _chatHubContext;
private readonly SysConfigService _sysConfigService;
public SysMessageService(SysCacheService sysCacheService, public SysMessageService(SysCacheService sysCacheService,
IHubContext<OnlineUserHub, IOnlineUserHub> chatHubContext, IHubContext<OnlineUserHub, IOnlineUserHub> chatHubContext)
SysConfigService sysConfigService)
{ {
_sysCacheService = sysCacheService; _sysCacheService = sysCacheService;
_chatHubContext = chatHubContext; _chatHubContext = chatHubContext;
_sysConfigService = sysConfigService;
} }
/// <summary> /// <summary>
@ -47,8 +44,8 @@ public class SysMessageService : IDynamicApiController, ITransient
public async Task SendOtherUser(MessageInput input) public async Task SendOtherUser(MessageInput input)
{ {
var hashKey = _sysCacheService.HashGetAll<SysOnlineUser>(CacheConst.KeyUserOnline); var hashKey = _sysCacheService.HashGetAll<SysOnlineUser>(CacheConst.KeyUserOnline);
var exceptRecevieUsers = hashKey.Where(u => u.Value.UserId == input.ReceiveUserId).Select(u => u.Value).ToList(); var exceptReceiveUsers = hashKey.Where(u => u.Value.UserId == input.ReceiveUserId).Select(u => u.Value).ToList();
await _chatHubContext.Clients.AllExcept(exceptRecevieUsers.Select(t => t.ConnectionId)).ReceiveMessage(input); await _chatHubContext.Clients.AllExcept(exceptReceiveUsers.Select(t => t.ConnectionId)).ReceiveMessage(input);
} }
/// <summary> /// <summary>
@ -60,8 +57,8 @@ public class SysMessageService : IDynamicApiController, ITransient
public async Task SendUser(MessageInput input) public async Task SendUser(MessageInput input)
{ {
var hashKey = _sysCacheService.HashGetAll<SysOnlineUser>(CacheConst.KeyUserOnline); var hashKey = _sysCacheService.HashGetAll<SysOnlineUser>(CacheConst.KeyUserOnline);
var recevieUsers = hashKey.Where(u => u.Value.UserId == input.ReceiveUserId).Select(u => u.Value).ToList(); var receiveUsers = hashKey.Where(u => u.Value.UserId == input.ReceiveUserId).Select(u => u.Value).ToList();
await recevieUsers.ForEachAsync(u => _chatHubContext.Clients.Client(u.ConnectionId).ReceiveMessage(input)); await receiveUsers.ForEachAsync(u => _chatHubContext.Clients.Client(u.ConnectionId ?? "").ReceiveMessage(input));
} }
/// <summary> /// <summary>
@ -73,7 +70,7 @@ public class SysMessageService : IDynamicApiController, ITransient
public async Task SendUsers(MessageInput input) public async Task SendUsers(MessageInput input)
{ {
var hashKey = _sysCacheService.HashGetAll<SysOnlineUser>(CacheConst.KeyUserOnline); var hashKey = _sysCacheService.HashGetAll<SysOnlineUser>(CacheConst.KeyUserOnline);
var recevieUsers = hashKey.Where(u => input.UserIds.Any(a => a == u.Value.UserId)).Select(u => u.Value).ToList(); var receiveUsers = hashKey.Where(u => input.UserIds.Any(a => a == u.Value.UserId)).Select(u => u.Value).ToList();
await recevieUsers.ForEachAsync(u => _chatHubContext.Clients.Client(u.ConnectionId).ReceiveMessage(input)); await receiveUsers.ForEachAsync(u => _chatHubContext.Clients.Client(u.ConnectionId ?? "").ReceiveMessage(input));
} }
} }

View File

@ -53,10 +53,8 @@ public class SysSmsService : IDynamicApiController, ITransient
public bool VerifyCode(SmsVerifyCodeInput input) public bool VerifyCode(SmsVerifyCodeInput input)
{ {
var verifyCode = _sysCacheService.Get<string>($"{CacheConst.KeyPhoneVerCode}{input.Phone}"); var verifyCode = _sysCacheService.Get<string>($"{CacheConst.KeyPhoneVerCode}{input.Phone}");
if (string.IsNullOrWhiteSpace(verifyCode)) if (string.IsNullOrWhiteSpace(verifyCode)) throw Oops.Oh("验证码不存在或已失效,请重新获取!");
throw Oops.Oh("验证码不存在或已失效,请重新获取!"); if (verifyCode != input.Code) throw Oops.Oh("验证码错误!");
if (verifyCode != input.Code)
throw Oops.Oh("验证码错误!");
return true; return true;
} }
@ -70,8 +68,7 @@ public class SysSmsService : IDynamicApiController, ITransient
[DisplayName("阿里云发送短信")] [DisplayName("阿里云发送短信")]
public async Task AliyunSendSms([Required] string phoneNumber) public async Task AliyunSendSms([Required] string phoneNumber)
{ {
if (!phoneNumber.TryValidate(ValidationTypes.PhoneNumber).IsValid) if (!phoneNumber.TryValidate(ValidationTypes.PhoneNumber).IsValid) throw Oops.Oh("请正确填写手机号码");
throw Oops.Oh("请正确填写手机号码");
// 生成随机验证码 // 生成随机验证码
var random = new Random(); var random = new Random();
@ -115,6 +112,10 @@ public class SysSmsService : IDynamicApiController, ITransient
[DisplayName("发送短信模板")] [DisplayName("发送短信模板")]
public async Task AliyunSendSmsTemplate(AliyunSendSmsTemplateInput input) public async Task AliyunSendSmsTemplate(AliyunSendSmsTemplateInput input)
{ {
if (!input.PhoneNumber.TryValidate(ValidationTypes.PhoneNumber).IsValid) throw Oops.Oh("请正确填写手机号码");
if (string.IsNullOrWhiteSpace(input.TemplateParam.ToString())) throw Oops.Oh("短信内容不能为空");
var client = CreateAliyunClient(); var client = CreateAliyunClient();
var template = _smsOptions.Aliyun.GetTemplate(input.TemplateId); var template = _smsOptions.Aliyun.GetTemplate(input.TemplateId);
var sendSmsRequest = new SendSmsRequest var sendSmsRequest = new SendSmsRequest
@ -146,8 +147,7 @@ public class SysSmsService : IDynamicApiController, ITransient
[DisplayName("腾讯云发送短信")] [DisplayName("腾讯云发送短信")]
public async Task TencentSendSms([Required] string phoneNumber) public async Task TencentSendSms([Required] string phoneNumber)
{ {
if (!phoneNumber.TryValidate(ValidationTypes.PhoneNumber).IsValid) if (!phoneNumber.TryValidate(ValidationTypes.PhoneNumber).IsValid) throw Oops.Oh("请正确填写手机号码");
throw Oops.Oh("请正确填写手机号码");
// 生成随机验证码 // 生成随机验证码
var random = new Random(); var random = new Random();

View File

@ -72,8 +72,7 @@ public class SysNoticeService : IDynamicApiController, ITransient
[DisplayName("更新通知公告")] [DisplayName("更新通知公告")]
public async Task UpdateNotice(UpdateNoticeInput input) public async Task UpdateNotice(UpdateNoticeInput input)
{ {
if (input.CreateUserId != _userManager.UserId) if (input.CreateUserId != _userManager.UserId) throw Oops.Oh(ErrorCodeEnum.D7003);
throw Oops.Oh(ErrorCodeEnum.D7003);
var notice = input.Adapt<SysNotice>(); var notice = input.Adapt<SysNotice>();
InitNoticeInfo(notice); InitNoticeInfo(notice);
@ -91,10 +90,8 @@ public class SysNoticeService : IDynamicApiController, ITransient
public async Task DeleteNotice(DeleteNoticeInput input) public async Task DeleteNotice(DeleteNoticeInput input)
{ {
var sysNotice = await _sysNoticeRep.GetByIdAsync(input.Id); var sysNotice = await _sysNoticeRep.GetByIdAsync(input.Id);
if (sysNotice.CreateUserId != _userManager.UserId) if (sysNotice.CreateUserId != _userManager.UserId) throw Oops.Oh(ErrorCodeEnum.D7003);
throw Oops.Oh(ErrorCodeEnum.D7003); if (sysNotice.Status == NoticeStatusEnum.PUBLIC) throw Oops.Oh(ErrorCodeEnum.D7001);
if (sysNotice.Status == NoticeStatusEnum.PUBLIC)
throw Oops.Oh(ErrorCodeEnum.D7001);
await _sysNoticeRep.DeleteAsync(u => u.Id == input.Id); await _sysNoticeRep.DeleteAsync(u => u.Id == input.Id);
@ -109,8 +106,7 @@ public class SysNoticeService : IDynamicApiController, ITransient
[DisplayName("发布通知公告")] [DisplayName("发布通知公告")]
public async Task Public(NoticeInput input) public async Task Public(NoticeInput input)
{ {
if (!(await _sysNoticeRep.IsAnyAsync(u => u.Id == input.Id && u.CreateUserId == _userManager.UserId))) if (!(await _sysNoticeRep.IsAnyAsync(u => u.Id == input.Id && u.CreateUserId == _userManager.UserId))) throw Oops.Oh(ErrorCodeEnum.D7003);
throw Oops.Oh(ErrorCodeEnum.D7003);
// 更新发布状态和时间 // 更新发布状态和时间
await _sysNoticeRep.UpdateAsync(u => new SysNotice() { Status = NoticeStatusEnum.PUBLIC, PublicTime = DateTime.Now }, u => u.Id == input.Id); await _sysNoticeRep.UpdateAsync(u => new SysNotice() { Status = NoticeStatusEnum.PUBLIC, PublicTime = DateTime.Now }, u => u.Id == input.Id);

View File

@ -31,13 +31,13 @@ public static class OAuthSetup
}) })
.AddWeixin(options => .AddWeixin(options =>
{ {
options.ClientId = authOpt.Weixin?.ClientId; options.ClientId = authOpt.Weixin?.ClientId!;
options.ClientSecret = authOpt.Weixin?.ClientSecret; options.ClientSecret = authOpt.Weixin?.ClientSecret!;
}) })
.AddGitee(options => .AddGitee(options =>
{ {
options.ClientId = authOpt.Gitee?.ClientId; options.ClientId = authOpt.Gitee?.ClientId!;
options.ClientSecret = authOpt.Gitee?.ClientSecret; options.ClientSecret = authOpt.Gitee?.ClientSecret!;
options.ClaimActions.MapJsonKey(OAuthClaim.GiteeAvatarUrl, "avatar_url"); options.ClaimActions.MapJsonKey(OAuthClaim.GiteeAvatarUrl, "avatar_url");
}); });

View File

@ -39,10 +39,13 @@ public class SysOAuthService : IDynamicApiController, ITransient
if (string.IsNullOrWhiteSpace(provider) || !await _httpContextAccessor.HttpContext.IsProviderSupportedAsync(provider)) if (string.IsNullOrWhiteSpace(provider) || !await _httpContextAccessor.HttpContext.IsProviderSupportedAsync(provider))
throw Oops.Oh("不支持的OAuth类型"); throw Oops.Oh("不支持的OAuth类型");
var request = _httpContextAccessor.HttpContext.Request; var request = _httpContextAccessor.HttpContext!.Request;
var url = $"{request.Scheme}://{request.Host}{request.PathBase}{request.Path}Callback?provider={provider}&redirectUrl={redirectUrl}"; var url = $"{request.Scheme}://{request.Host}{request.PathBase}{request.Path}Callback?provider={provider}&redirectUrl={redirectUrl}";
var properties = new AuthenticationProperties { RedirectUri = url }; var properties = new AuthenticationProperties
properties.Items["LoginProvider"] = provider; {
RedirectUri = url,
Items = { ["LoginProvider"] = provider }
};
return await Task.FromResult(new ChallengeResult(provider, properties)); return await Task.FromResult(new ChallengeResult(provider, properties));
} }
@ -59,7 +62,7 @@ public class SysOAuthService : IDynamicApiController, ITransient
if (string.IsNullOrWhiteSpace(provider) || !await _httpContextAccessor.HttpContext.IsProviderSupportedAsync(provider)) if (string.IsNullOrWhiteSpace(provider) || !await _httpContextAccessor.HttpContext.IsProviderSupportedAsync(provider))
throw Oops.Oh("不支持的OAuth类型"); throw Oops.Oh("不支持的OAuth类型");
var authenticateResult = await _httpContextAccessor.HttpContext.AuthenticateAsync(provider); var authenticateResult = await _httpContextAccessor.HttpContext!.AuthenticateAsync(provider);
if (!authenticateResult.Succeeded) if (!authenticateResult.Succeeded)
throw Oops.Oh("授权失败"); throw Oops.Oh("授权失败");
@ -110,7 +113,7 @@ public class SysOAuthService : IDynamicApiController, ITransient
} }
// 构建Token令牌默认回调登录为PC模式 // 构建Token令牌默认回调登录为PC模式
var token = await App.GetRequiredService<SysAuthService>().CreateToken(wechatUser.SysUser, LoginModeEnum.PC); var token = await App.GetRequiredService<SysAuthService>().CreateToken(wechatUser.SysUser, SqlSugarConst.DefaultAppId, LoginModeEnum.PC);
return new RedirectResult($"{redirectUrl}/#/login?token={token.AccessToken}"); return new RedirectResult($"{redirectUrl}/#/login?token={token.AccessToken}");
} }

View File

@ -49,7 +49,7 @@ public class SysOnlineUserService : IDynamicApiController, ITransient
[DisplayName("强制下线")] [DisplayName("强制下线")]
public async Task ForceOffline(SysOnlineUser user) public async Task ForceOffline(SysOnlineUser user)
{ {
await _onlineUserHubContext.Clients.Client(user.ConnectionId).ForceOffline("强制下线"); await _onlineUserHubContext.Clients.Client(user.ConnectionId ?? "").ForceOffline("强制下线");
await _sysOnlineUerRep.DeleteAsync(user); await _sysOnlineUerRep.DeleteAsync(user);
} }
@ -67,7 +67,7 @@ public class SysOnlineUserService : IDynamicApiController, ITransient
foreach (var item in userList) foreach (var item in userList)
{ {
await _onlineUserHubContext.Clients.Client(item.ConnectionId).PublicNotice(notice); await _onlineUserHubContext.Clients.Client(item.ConnectionId ?? "").PublicNotice(notice);
} }
} }

View File

@ -56,7 +56,7 @@ public class SysOrgService : IDynamicApiController, ITransient
.ToListAsync(); .ToListAsync();
} }
var orgTree = new List<SysOrg>(); List<SysOrg> orgTree;
if (_userManager.SuperAdmin) if (_userManager.SuperAdmin)
{ {
orgTree = await iSugarQueryable.ToTreeAsync(u => u.Children, u => u.Pid, input.Id); orgTree = await iSugarQueryable.ToTreeAsync(u => u.Children, u => u.Pid, input.Id);
@ -68,12 +68,11 @@ public class SysOrgService : IDynamicApiController, ITransient
HandlerOrgTree(orgTree, userOrgIdList); HandlerOrgTree(orgTree, userOrgIdList);
} }
var sysOrg = await _sysOrgRep.GetByIdAsync(input.Id); var sysOrg = await _sysOrgRep.GetSingleAsync(u => u.Id == input.Id);
if (sysOrg != null) if (sysOrg == null) return orgTree;
{
sysOrg.Children = orgTree; sysOrg.Children = orgTree;
orgTree = new List<SysOrg> { sysOrg }; orgTree = new List<SysOrg> { sysOrg };
}
return orgTree; return orgTree;
} }
@ -261,25 +260,20 @@ public class SysOrgService : IDynamicApiController, ITransient
private void DeleteAllUserOrgCache(long orgId, long orgPid) private void DeleteAllUserOrgCache(long orgId, long orgPid)
{ {
var userOrgKeyList = _sysCacheService.GetKeysByPrefixKey(CacheConst.KeyUserOrg); var userOrgKeyList = _sysCacheService.GetKeysByPrefixKey(CacheConst.KeyUserOrg);
if (userOrgKeyList != null && userOrgKeyList.Count > 0) if (userOrgKeyList is not { Count: > 0 }) return;
foreach (var userOrgKey in userOrgKeyList)
{ {
foreach (var userOrgKey in userOrgKeyList) var userOrgList = _sysCacheService.Get<List<long>>(userOrgKey);
{ var userId = long.Parse(userOrgKey.Substring(CacheConst.KeyUserOrg));
var userOrgs = _sysCacheService.Get<List<long>>(userOrgKey); if (userOrgList != null && (userOrgList.Contains(orgId) || userOrgList.Contains(orgPid)))
var userId = long.Parse(userOrgKey.Substring(CacheConst.KeyUserOrg)); SqlSugarFilter.DeleteUserOrgCache(userId, _sysOrgRep.Context.CurrentConnectionConfig.ConfigId.ToString());
if (userOrgs != null && (userOrgs.Contains(orgId) || userOrgs.Contains(orgPid)))
{ if (orgPid != 0) continue;
SqlSugarFilter.DeleteUserOrgCache(userId, _sysOrgRep.Context.CurrentConnectionConfig.ConfigId.ToString());
} var dataScope = _sysCacheService.Get<int>($"{CacheConst.KeyRoleMaxDataScope}{userId}");
if (orgPid == 0) if (dataScope == (int)DataScopeEnum.All)
{ SqlSugarFilter.DeleteUserOrgCache(userId, _sysOrgRep.Context.CurrentConnectionConfig.ConfigId.ToString());
var dataScope = _sysCacheService.Get<int>($"{CacheConst.KeyRoleMaxDataScope}{userId}");
if (dataScope == (int)DataScopeEnum.All)
{
SqlSugarFilter.DeleteUserOrgCache(userId, _sysOrgRep.Context.CurrentConnectionConfig.ConfigId.ToString());
}
}
}
} }
} }
@ -290,8 +284,7 @@ public class SysOrgService : IDynamicApiController, ITransient
[NonAction] [NonAction]
public async Task<List<long>> GetUserOrgIdList() public async Task<List<long>> GetUserOrgIdList()
{ {
if (_userManager.SuperAdmin) if (_userManager.SuperAdmin) return new();
return new List<long>();
return await GetUserOrgIdList(_userManager.UserId, _userManager.OrgId); return await GetUserOrgIdList(_userManager.UserId, _userManager.OrgId);
} }
@ -303,21 +296,20 @@ public class SysOrgService : IDynamicApiController, ITransient
public async Task<List<long>> GetUserOrgIdList(long userId, long userOrgId) public async Task<List<long>> GetUserOrgIdList(long userId, long userOrgId)
{ {
var orgIdList = _sysCacheService.Get<List<long>>($"{CacheConst.KeyUserOrg}{userId}"); // 取缓存 var orgIdList = _sysCacheService.Get<List<long>>($"{CacheConst.KeyUserOrg}{userId}"); // 取缓存
if (orgIdList == null || orgIdList.Count < 1) if (orgIdList is { Count: >= 1 }) return orgIdList;
{
// 本人创建机构集合 // 本人创建机构集合
var orgList0 = await _sysOrgRep.AsQueryable().Where(u => u.CreateUserId == userId).Select(u => u.Id).ToListAsync(); var orgList0 = await _sysOrgRep.AsQueryable().Where(u => u.CreateUserId == userId).Select(u => u.Id).ToListAsync();
// 扩展机构集合 // 扩展机构集合
var orgList1 = await _sysUserExtOrgService.GetUserExtOrgList(userId); var orgList1 = await _sysUserExtOrgService.GetUserExtOrgList(userId);
// 角色机构集合 // 角色机构集合
var orgList2 = await GetUserRoleOrgIdList(userId, userOrgId); var orgList2 = await GetUserRoleOrgIdList(userId, userOrgId);
// 机构并集 // 机构并集
orgIdList = orgList1.Select(u => u.OrgId).Union(orgList2).Union(orgList0).ToList(); orgIdList = orgList1.Select(u => u.OrgId).Union(orgList2).Union(orgList0).ToList();
// 当前所属机构 // 当前所属机构
if (!orgIdList.Contains(userOrgId)) if (!orgIdList.Contains(userOrgId))
orgIdList.Add(userOrgId); orgIdList.Add(userOrgId);
_sysCacheService.Set($"{CacheConst.KeyUserOrg}{userId}", orgIdList, TimeSpan.FromDays(7)); // 存缓存 _sysCacheService.Set($"{CacheConst.KeyUserOrg}{userId}", orgIdList, TimeSpan.FromDays(7)); // 存缓存
}
return orgIdList; return orgIdList;
} }
@ -330,8 +322,7 @@ public class SysOrgService : IDynamicApiController, ITransient
private async Task<List<long>> GetUserRoleOrgIdList(long userId, long userOrgId) private async Task<List<long>> GetUserRoleOrgIdList(long userId, long userOrgId)
{ {
var roleList = await _sysUserRoleService.GetUserRoleList(userId); var roleList = await _sysUserRoleService.GetUserRoleList(userId);
if (roleList.Count < 1) if (roleList.Count < 1) return new(); // 空机构Id集合
return new List<long>(); // 空机构Id集合
return await GetUserOrgIdList(roleList, userId, userOrgId); return await GetUserOrgIdList(roleList, userId, userOrgId);
} }
@ -354,7 +345,7 @@ public class SysOrgService : IDynamicApiController, ITransient
// 数据范围的机构集合 // 数据范围的机构集合
var dataScopeOrgIdList = new List<long>(); var dataScopeOrgIdList = new List<long>();
if (roleList != null && roleList.Count > 0) if (roleList is { Count: > 0 })
{ {
roleList.ForEach(u => roleList.ForEach(u =>
{ {
@ -393,20 +384,20 @@ public class SysOrgService : IDynamicApiController, ITransient
{ {
var orgId = userOrgId;//var orgId = _userManager.OrgId; var orgId = userOrgId;//var orgId = _userManager.OrgId;
var orgIdList = new List<long>(); var orgIdList = new List<long>();
// 若数据范围是全部则获取所有机构Id集合 switch (dataScope)
if (dataScope == (int)DataScopeEnum.All)
{ {
orgIdList = await _sysOrgRep.AsQueryable().Select(u => u.Id).ToListAsync(); // 若数据范围是全部则获取所有机构Id集合
} case (int)DataScopeEnum.All:
// 若数据范围是本部门及以下,则获取本节点和子节点集合 orgIdList = await _sysOrgRep.AsQueryable().Select(u => u.Id).ToListAsync();
else if (dataScope == (int)DataScopeEnum.DeptChild) break;
{ // 若数据范围是本部门及以下,则获取本节点和子节点集合
orgIdList = await GetChildIdListWithSelfById(orgId); case (int)DataScopeEnum.DeptChild:
} orgIdList = await GetChildIdListWithSelfById(orgId);
// 若数据范围是本部门不含子节点,则直接返回本部门 break;
else if (dataScope == (int)DataScopeEnum.Dept) // 若数据范围是本部门不含子节点,则直接返回本部门
{ case (int)DataScopeEnum.Dept:
orgIdList.Add(orgId); orgIdList.Add(orgId);
break;
} }
return orgIdList; return orgIdList;
} }

View File

@ -46,8 +46,7 @@ public class SysPluginService : IDynamicApiController, ITransient
public async Task AddPlugin(AddPluginInput input) public async Task AddPlugin(AddPluginInput input)
{ {
var isExist = await _sysPluginRep.IsAnyAsync(u => u.Name == input.Name || u.AssemblyName == input.AssemblyName); var isExist = await _sysPluginRep.IsAnyAsync(u => u.Name == input.Name || u.AssemblyName == input.AssemblyName);
if (isExist) if (isExist) throw Oops.Oh(ErrorCodeEnum.D1900);
throw Oops.Oh(ErrorCodeEnum.D1900);
// 添加动态程序集/接口 // 添加动态程序集/接口
input.AssemblyName = CompileAssembly(input.CsharpCode, input.AssemblyName); input.AssemblyName = CompileAssembly(input.CsharpCode, input.AssemblyName);
@ -65,8 +64,7 @@ public class SysPluginService : IDynamicApiController, ITransient
public async Task UpdatePlugin(UpdatePluginInput input) public async Task UpdatePlugin(UpdatePluginInput input)
{ {
var isExist = await _sysPluginRep.IsAnyAsync(u => (u.Name == input.Name || u.AssemblyName == input.AssemblyName) && u.Id != input.Id); var isExist = await _sysPluginRep.IsAnyAsync(u => (u.Name == input.Name || u.AssemblyName == input.AssemblyName) && u.Id != input.Id);
if (isExist) if (isExist) throw Oops.Oh(ErrorCodeEnum.D1900);
throw Oops.Oh(ErrorCodeEnum.D1900);
// 先移除再添加动态程序集/接口 // 先移除再添加动态程序集/接口
RemoveAssembly(input.AssemblyName); RemoveAssembly(input.AssemblyName);

View File

@ -75,8 +75,7 @@ public class SysPosService : IDynamicApiController, ITransient
[DisplayName("增加职位")] [DisplayName("增加职位")]
public async Task AddPos(AddPosInput input) public async Task AddPos(AddPosInput input)
{ {
if (await _sysPosRep.IsAnyAsync(u => u.Name == input.Name && u.Code == input.Code)) if (await _sysPosRep.IsAnyAsync(u => u.Name == input.Name && u.Code == input.Code)) throw Oops.Oh(ErrorCodeEnum.D6000);
throw Oops.Oh(ErrorCodeEnum.D6000);
await _sysPosRep.InsertAsync(input.Adapt<SysPos>()); await _sysPosRep.InsertAsync(input.Adapt<SysPos>());
} }
@ -90,12 +89,10 @@ public class SysPosService : IDynamicApiController, ITransient
[DisplayName("更新职位")] [DisplayName("更新职位")]
public async Task UpdatePos(UpdatePosInput input) public async Task UpdatePos(UpdatePosInput input)
{ {
if (await _sysPosRep.IsAnyAsync(u => u.Name == input.Name && u.Code == input.Code && u.Id != input.Id)) if (await _sysPosRep.IsAnyAsync(u => u.Name == input.Name && u.Code == input.Code && u.Id != input.Id)) throw Oops.Oh(ErrorCodeEnum.D6000);
throw Oops.Oh(ErrorCodeEnum.D6000);
var sysPos = await _sysPosRep.GetByIdAsync(input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D6003); var sysPos = await _sysPosRep.GetByIdAsync(input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D6003);
if (!_userManager.SuperAdmin && sysPos.CreateUserId != _userManager.UserId) if (!_userManager.SuperAdmin && sysPos.CreateUserId != _userManager.UserId) throw Oops.Oh(ErrorCodeEnum.D6002);
throw Oops.Oh(ErrorCodeEnum.D6002);
await _sysPosRep.AsUpdateable(input.Adapt<SysPos>()).IgnoreColumns(true).ExecuteCommandAsync(); await _sysPosRep.AsUpdateable(input.Adapt<SysPos>()).IgnoreColumns(true).ExecuteCommandAsync();
} }
@ -110,19 +107,16 @@ public class SysPosService : IDynamicApiController, ITransient
public async Task DeletePos(DeletePosInput input) public async Task DeletePos(DeletePosInput input)
{ {
var sysPos = await _sysPosRep.GetByIdAsync(input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D6003); var sysPos = await _sysPosRep.GetByIdAsync(input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D6003);
if (!_userManager.SuperAdmin && sysPos.CreateUserId != _userManager.UserId) if (!_userManager.SuperAdmin && sysPos.CreateUserId != _userManager.UserId) throw Oops.Oh(ErrorCodeEnum.D6002);
throw Oops.Oh(ErrorCodeEnum.D6002);
// 若职位有用户则禁止删除 // 若职位有用户则禁止删除
var hasPosEmp = await _sysPosRep.ChangeRepository<SqlSugarRepository<SysUser>>() var hasPosEmp = await _sysPosRep.ChangeRepository<SqlSugarRepository<SysUser>>()
.IsAnyAsync(u => u.PosId == input.Id); .IsAnyAsync(u => u.PosId == input.Id);
if (hasPosEmp) if (hasPosEmp) throw Oops.Oh(ErrorCodeEnum.D6001);
throw Oops.Oh(ErrorCodeEnum.D6001);
// 若附属职位有用户则禁止删除 // 若附属职位有用户则禁止删除
var hasExtPosEmp = await _sysUserExtOrgService.HasUserPos(input.Id); var hasExtPosEmp = await _sysUserExtOrgService.HasUserPos(input.Id);
if (hasExtPosEmp) if (hasExtPosEmp) throw Oops.Oh(ErrorCodeEnum.D6001);
throw Oops.Oh(ErrorCodeEnum.D6001);
await _sysPosRep.DeleteByIdAsync(input.Id); await _sysPosRep.DeleteByIdAsync(input.Id);
} }

View File

@ -54,8 +54,7 @@ public class SysPrintService : IDynamicApiController, ITransient
public async Task AddPrint(AddPrintInput input) public async Task AddPrint(AddPrintInput input)
{ {
var isExist = await _sysPrintRep.IsAnyAsync(u => u.Name == input.Name); var isExist = await _sysPrintRep.IsAnyAsync(u => u.Name == input.Name);
if (isExist) if (isExist) throw Oops.Oh(ErrorCodeEnum.D1800);
throw Oops.Oh(ErrorCodeEnum.D1800);
await _sysPrintRep.InsertAsync(input.Adapt<SysPrint>()); await _sysPrintRep.InsertAsync(input.Adapt<SysPrint>());
} }
@ -70,8 +69,7 @@ public class SysPrintService : IDynamicApiController, ITransient
public async Task UpdatePrint(UpdatePrintInput input) public async Task UpdatePrint(UpdatePrintInput input)
{ {
var isExist = await _sysPrintRep.IsAnyAsync(u => u.Name == input.Name && u.Id != input.Id); var isExist = await _sysPrintRep.IsAnyAsync(u => u.Name == input.Name && u.Id != input.Id);
if (isExist) if (isExist) throw Oops.Oh(ErrorCodeEnum.D1800);
throw Oops.Oh(ErrorCodeEnum.D1800);
await _sysPrintRep.AsUpdateable(input.Adapt<SysPrint>()).IgnoreColumns(true).ExecuteCommandAsync(); await _sysPrintRep.AsUpdateable(input.Adapt<SysPrint>()).IgnoreColumns(true).ExecuteCommandAsync();
} }

View File

@ -76,22 +76,19 @@ public class SysRegionService : IDynamicApiController, ITransient
[DisplayName("增加行政区划")] [DisplayName("增加行政区划")]
public async Task<long> AddRegion(AddRegionInput input) public async Task<long> AddRegion(AddRegionInput input)
{ {
input.Code = input.Code.Trim(); input.Code = input.Code?.Trim() ?? "";
if (input.Code.Length != 12 && input.Code.Length != 9 && input.Code.Length != 6) if (input.Code.Length != 12 && input.Code.Length != 9 && input.Code.Length != 6) throw Oops.Oh(ErrorCodeEnum.R2003);
throw Oops.Oh(ErrorCodeEnum.R2003);
if (input.Pid != 0) if (input.Pid != 0)
{ {
var pRegion = await _sysRegionRep.GetByIdAsync(input.Pid); var pRegion = await _sysRegionRep.GetByIdAsync(input.Pid);
pRegion ??= await _sysRegionRep.GetFirstAsync(u => u.Code == input.Pid.ToString()); pRegion ??= await _sysRegionRep.GetFirstAsync(u => u.Code == input.Pid.ToString());
if (pRegion == null) if (pRegion == null) throw Oops.Oh(ErrorCodeEnum.D2000);
throw Oops.Oh(ErrorCodeEnum.D2000);
input.Pid = pRegion.Id; input.Pid = pRegion.Id;
} }
var isExist = await _sysRegionRep.IsAnyAsync(u => u.Name == input.Name && u.Code == input.Code); var isExist = await _sysRegionRep.IsAnyAsync(u => u.Name == input.Name && u.Code == input.Code);
if (isExist) if (isExist) throw Oops.Oh(ErrorCodeEnum.R2002);
throw Oops.Oh(ErrorCodeEnum.R2002);
var sysRegion = input.Adapt<SysRegion>(); var sysRegion = input.Adapt<SysRegion>();
var newRegion = await _sysRegionRep.AsInsertable(sysRegion).ExecuteReturnEntityAsync(); var newRegion = await _sysRegionRep.AsInsertable(sysRegion).ExecuteReturnEntityAsync();
@ -107,34 +104,28 @@ public class SysRegionService : IDynamicApiController, ITransient
[DisplayName("更新行政区划")] [DisplayName("更新行政区划")]
public async Task UpdateRegion(UpdateRegionInput input) public async Task UpdateRegion(UpdateRegionInput input)
{ {
input.Code = input.Code.Trim(); input.Code = input.Code?.Trim() ?? "";
if (input.Code.Length != 12 && input.Code.Length != 9 && input.Code.Length != 6) if (input.Code.Length != 12 && input.Code.Length != 9 && input.Code.Length != 6) throw Oops.Oh(ErrorCodeEnum.R2003);
throw Oops.Oh(ErrorCodeEnum.R2003);
var sysRegion = await _sysRegionRep.GetByIdAsync(input.Id); var sysRegion = await _sysRegionRep.GetByIdAsync(input.Id);
if (sysRegion == null) if (sysRegion == null) throw Oops.Oh(ErrorCodeEnum.D1002);
throw Oops.Oh(ErrorCodeEnum.D1002);
if (sysRegion.Pid != input.Pid && input.Pid != 0) if (sysRegion.Pid != input.Pid && input.Pid != 0)
{ {
var pRegion = await _sysRegionRep.GetByIdAsync(input.Pid); var pRegion = await _sysRegionRep.GetByIdAsync(input.Pid);
pRegion ??= await _sysRegionRep.GetFirstAsync(u => u.Code == input.Pid.ToString()); pRegion ??= await _sysRegionRep.GetFirstAsync(u => u.Code == input.Pid.ToString());
if (pRegion == null) if (pRegion == null) throw Oops.Oh(ErrorCodeEnum.D2000);
throw Oops.Oh(ErrorCodeEnum.D2000);
input.Pid = pRegion.Id; input.Pid = pRegion.Id;
var regionTreeList = await _sysRegionRep.AsQueryable().ToChildListAsync(u => u.Pid, input.Id, true); var regionTreeList = await _sysRegionRep.AsQueryable().ToChildListAsync(u => u.Pid, input.Id, true);
var childIdList = regionTreeList.Select(u => u.Id).ToList(); var childIdList = regionTreeList.Select(u => u.Id).ToList();
if (childIdList.Contains(input.Pid)) if (childIdList.Contains(input.Pid)) throw Oops.Oh(ErrorCodeEnum.R2004);
throw Oops.Oh(ErrorCodeEnum.R2004);
} }
if (input.Id == input.Pid) if (input.Id == input.Pid) throw Oops.Oh(ErrorCodeEnum.R2001);
throw Oops.Oh(ErrorCodeEnum.R2001);
var isExist = await _sysRegionRep.IsAnyAsync(u => (u.Name == input.Name && u.Code == input.Code) && u.Id != sysRegion.Id); var isExist = await _sysRegionRep.IsAnyAsync(u => (u.Name == input.Name && u.Code == input.Code) && u.Id != sysRegion.Id);
if (isExist) if (isExist) throw Oops.Oh(ErrorCodeEnum.R2002);
throw Oops.Oh(ErrorCodeEnum.R2002);
//// 父Id不能为自己的子节点 //// 父Id不能为自己的子节点
//var regionTreeList = await _sysRegionRep.AsQueryable().ToChildListAsync(u => u.Pid, input.Id, true); //var regionTreeList = await _sysRegionRep.AsQueryable().ToChildListAsync(u => u.Pid, input.Id, true);
@ -171,8 +162,7 @@ public class SysRegionService : IDynamicApiController, ITransient
throw Oops.Oh("请正确输入高德地图开发者 Key 值"); throw Oops.Oh("请正确输入高德地图开发者 Key 值");
var syncLevel = await _sysConfigService.GetConfigValueByCode<int>(ConfigConst.SysRegionSyncLevel); var syncLevel = await _sysConfigService.GetConfigValueByCode<int>(ConfigConst.SysRegionSyncLevel);
if (syncLevel < 1 || syncLevel > 5) if (syncLevel is < 1 or > 5) syncLevel = 3; // 默认区县级
syncLevel = 3; // 默认区县级
var res = await $"https://restapi.amap.com/v3/config/district?subdistrict={syncLevel}&key={key}".GetAsync(); var res = await $"https://restapi.amap.com/v3/config/district?subdistrict={syncLevel}&key={key}".GetAsync();
if (!res.IsSuccessStatusCode) return; if (!res.IsSuccessStatusCode) return;
@ -187,7 +177,7 @@ public class SysRegionService : IDynamicApiController, ITransient
} }
await _sysRegionRep.AsDeleteable().ExecuteCommandAsync(); await _sysRegionRep.AsDeleteable().ExecuteCommandAsync();
_sysRegionRep.Context.Fastest<SysRegion>().BulkCopy(regionList); await _sysRegionRep.Context.Fastest<SysRegion>().BulkCopyAsync(regionList);
} }
private void GetChildren(List<SysRegion> regionList, List<GDRegionResponse> responses, int level, long pid) private void GetChildren(List<SysRegion> regionList, List<GDRegionResponse> responses, int level, long pid)

View File

@ -28,12 +28,12 @@ public class SysRoleOrgService : ITransient
await _sysRoleOrgRep.DeleteAsync(u => u.RoleId == input.Id); await _sysRoleOrgRep.DeleteAsync(u => u.RoleId == input.Id);
if (input.DataScope == (int)DataScopeEnum.Define) if (input.DataScope == (int)DataScopeEnum.Define)
{ {
var roleOrgs = input.OrgIdList.Select(u => new SysRoleOrg var roleOrgList = input.OrgIdList.Select(u => new SysRoleOrg
{ {
RoleId = input.Id, RoleId = input.Id,
OrgId = u OrgId = u
}).ToList(); }).ToList();
await _sysRoleOrgRep.InsertRangeAsync(roleOrgs); await _sysRoleOrgRep.InsertRangeAsync(roleOrgList);
} }
} }

View File

@ -135,13 +135,11 @@ public class SysRoleService : IDynamicApiController, ITransient
{ {
// 禁止删除系统管理员角色 // 禁止删除系统管理员角色
var sysRole = await _sysRoleRep.GetByIdAsync(input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002); var sysRole = await _sysRoleRep.GetByIdAsync(input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
if (sysRole.Code == CommonConst.SysAdminRole) if (sysRole.Code == CommonConst.SysAdminRole) throw Oops.Oh(ErrorCodeEnum.D1019);
throw Oops.Oh(ErrorCodeEnum.D1019);
// 若角色有用户则禁止删除 // 若角色有用户则禁止删除
var userIds = await _sysUserRoleService.GetUserIdList(input.Id); var userIds = await _sysUserRoleService.GetUserIdList(input.Id);
if (userIds != null && userIds.Count > 0) if (userIds != null && userIds.Count > 0) throw Oops.Oh(ErrorCodeEnum.D1025);
throw Oops.Oh(ErrorCodeEnum.D1025);
await _sysRoleRep.DeleteAsync(sysRole); await _sysRoleRep.DeleteAsync(sysRole);
@ -166,8 +164,7 @@ public class SysRoleService : IDynamicApiController, ITransient
[DisplayName("授权角色菜单")] [DisplayName("授权角色菜单")]
public async Task GrantMenu(RoleMenuInput input) public async Task GrantMenu(RoleMenuInput input)
{ {
if (input.MenuIdList == null || input.MenuIdList.Count < 1) if (input.MenuIdList == null || input.MenuIdList.Count < 1) return;
return;
//// 将父节点为0的菜单排除防止前端全选异常 //// 将父节点为0的菜单排除防止前端全选异常
//var pMenuIds = await _sysRoleRep.ChangeRepository<SqlSugarRepository<SysMenu>>().AsQueryable().Where(u => input.MenuIdList.Contains(u.Id) && u.Pid == 0).ToListAsync(u => u.Id); //var pMenuIds = await _sysRoleRep.ChangeRepository<SqlSugarRepository<SysMenu>>().AsQueryable().Where(u => input.MenuIdList.Contains(u.Id) && u.Pid == 0).ToListAsync(u => u.Id);
@ -211,26 +208,26 @@ public class SysRoleService : IDynamicApiController, ITransient
SqlSugarFilter.DeleteUserOrgCache(userId, _sysRoleRep.Context.CurrentConnectionConfig.ConfigId.ToString()); SqlSugarFilter.DeleteUserOrgCache(userId, _sysRoleRep.Context.CurrentConnectionConfig.ConfigId.ToString());
} }
var role = await _sysRoleRep.GetByIdAsync(input.Id); var role = await _sysRoleRep.GetFirstAsync(u => u.Id == input.Id);
var dataScope = input.DataScope; var dataScope = input.DataScope;
if (!_userManager.SuperAdmin) if (!_userManager.SuperAdmin)
{ {
// 非超级管理员没有全部数据范围权限 switch (dataScope)
if (dataScope == (int)DataScopeEnum.All)
throw Oops.Oh(ErrorCodeEnum.D1016);
// 若数据范围自定义,则判断授权数据范围是否有权限
if (dataScope == (int)DataScopeEnum.Define)
{ {
var grantOrgIdList = input.OrgIdList; // 非超级管理员没有全部数据范围权限
if (grantOrgIdList.Count > 0) case (int)DataScopeEnum.All: throw Oops.Oh(ErrorCodeEnum.D1016);
{ // 若数据范围自定义,则判断授权数据范围是否有权限
var orgIdList = await _sysOrgService.GetUserOrgIdList(); case (int)DataScopeEnum.Define:
if (orgIdList.Count < 1) {
throw Oops.Oh(ErrorCodeEnum.D1016); var grantOrgIdList = input.OrgIdList;
else if (!grantOrgIdList.All(u => orgIdList.Any(c => c == u))) if (grantOrgIdList.Count > 0)
throw Oops.Oh(ErrorCodeEnum.D1016); {
} var orgIdList = await _sysOrgService.GetUserOrgIdList();
if (orgIdList.Count < 1) throw Oops.Oh(ErrorCodeEnum.D1016);
if (!grantOrgIdList.All(u => orgIdList.Any(c => c == u))) throw Oops.Oh(ErrorCodeEnum.D1016);
}
break;
}
} }
} }
role.DataScope = (DataScopeEnum)dataScope; role.DataScope = (DataScopeEnum)dataScope;
@ -259,8 +256,7 @@ public class SysRoleService : IDynamicApiController, ITransient
[DisplayName("设置角色状态")] [DisplayName("设置角色状态")]
public async Task<int> SetStatus(RoleInput input) public async Task<int> SetStatus(RoleInput input)
{ {
if (!Enum.IsDefined(typeof(StatusEnum), input.Status)) if (!Enum.IsDefined(typeof(StatusEnum), input.Status)) throw Oops.Oh(ErrorCodeEnum.D3005);
throw Oops.Oh(ErrorCodeEnum.D3005);
return await _sysRoleRep.AsUpdateable() return await _sysRoleRep.AsUpdateable()
.SetColumns(u => u.Status == input.Status) .SetColumns(u => u.Status == input.Status)
@ -329,8 +325,7 @@ public class SysRoleService : IDynamicApiController, ITransient
[DisplayName("获取角色接口黑名单集合")] [DisplayName("获取角色接口黑名单集合")]
public async Task<List<string>> GetRoleApiList([FromQuery] RoleInput input) public async Task<List<string>> GetRoleApiList([FromQuery] RoleInput input)
{ {
var roleApis = await _sysRoleApiService.GetRoleApiList(new List<long> { input.Id }); return await _sysRoleApiService.GetRoleApiList(new List<long> { input.Id });
return roleApis;
//var roleButtons = await GetRoleButtonList(new List<long> { input.Id }); //var roleButtons = await GetRoleButtonList(new List<long> { input.Id });
//return roleApis.Union(roleButtons).ToList(); //return roleApis.Union(roleButtons).ToList();
@ -345,44 +340,42 @@ public class SysRoleService : IDynamicApiController, ITransient
{ {
var userId = _userManager.UserId; var userId = _userManager.UserId;
var apiList = _sysCacheService.Get<List<List<string>>>(CacheConst.KeyUserApi + userId); var apiList = _sysCacheService.Get<List<List<string>>>(CacheConst.KeyUserApi + userId);
if (apiList == null) if (apiList != null) return apiList;
apiList = new List<List<string>>() { new(), new() };
// 超管账号获取所有接口
if (_userManager.SuperAdmin)
{ {
apiList = new List<List<string>>() { new(), new() }; var allApiList = _sysCommonService.GetApiList();
foreach (var apiOutput in allApiList)
// 超管账号获取所有接口
if (_userManager.SuperAdmin)
{ {
var allApiList = _sysCommonService.GetApiList(); foreach (var controller in apiOutput.Children)
foreach (var apiOutput in allApiList) apiList[0].AddRange(controller.Children.Select(u => u.Route));
{
foreach (var controller in apiOutput.Children)
apiList[0].AddRange(controller.Children.Select(u => u.Route));
}
// 所有按钮权限集合
var allButtonList = await GetButtonList();
// 没有接口对应的按钮权限集合
var diffButtonList = allButtonList.Except(apiList[0]).ToList(); // 差集
apiList[0].AddRange(diffButtonList);
} }
else
{
// 当前账号所有角色集合
var roleIdList = await _sysUserRoleService.GetUserRoleIdList(_userManager.UserId);
// 已勾选按钮权限集合 // 所有按钮权限集合
apiList[0] = await GetRoleButtonList(roleIdList); var allButtonList = await GetButtonList();
// 没有接口对应的按钮权限集合
// 未勾选按钮权限集合(放到接口黑名单里面) var diffButtonList = allButtonList.Except(apiList[0]).ToList(); // 差集
var allButtonList = await GetButtonList(); apiList[0].AddRange(diffButtonList);
apiList[1] = allButtonList.Except(apiList[0]).ToList(); // 差集
// 接口黑名单集合
var roleApiList = await _sysRoleApiService.GetRoleApiList(roleIdList);
apiList[1].AddRange(roleApiList);
}
_sysCacheService.Set(CacheConst.KeyUserApi + userId, apiList, TimeSpan.FromDays(7)); // 缓存7天
} }
else
{
// 当前账号所有角色集合
var roleIdList = await _sysUserRoleService.GetUserRoleIdList(_userManager.UserId);
// 已勾选按钮权限集合
apiList[0] = await GetRoleButtonList(roleIdList);
// 未勾选按钮权限集合(放到接口黑名单里面)
var allButtonList = await GetButtonList();
apiList[1] = allButtonList.Except(apiList[0]).ToList(); // 差集
// 接口黑名单集合
var roleApiList = await _sysRoleApiService.GetRoleApiList(roleIdList);
apiList[1].AddRange(roleApiList);
}
_sysCacheService.Set(CacheConst.KeyUserApi + userId, apiList, TimeSpan.FromDays(7)); // 缓存7天
return apiList; return apiList;
} }

View File

@ -17,10 +17,10 @@ public class SysScheduleService : IDynamicApiController, ITransient
private readonly SqlSugarRepository<SysSchedule> _sysSchedule; private readonly SqlSugarRepository<SysSchedule> _sysSchedule;
public SysScheduleService(UserManager userManager, public SysScheduleService(UserManager userManager,
SqlSugarRepository<SysSchedule> sysSchedle) SqlSugarRepository<SysSchedule> sysSchedule)
{ {
_userManager = userManager; _userManager = userManager;
_sysSchedule = sysSchedle; _sysSchedule = sysSchedule;
} }
/// <summary> /// <summary>
@ -94,8 +94,7 @@ public class SysScheduleService : IDynamicApiController, ITransient
[DisplayName("设置日程状态")] [DisplayName("设置日程状态")]
public async Task<int> SetStatus(ScheduleInput input) public async Task<int> SetStatus(ScheduleInput input)
{ {
if (!Enum.IsDefined(typeof(FinishStatusEnum), input.Status)) if (!Enum.IsDefined(typeof(FinishStatusEnum), input.Status)) throw Oops.Oh(ErrorCodeEnum.D3005);
throw Oops.Oh(ErrorCodeEnum.D3005);
return await _sysSchedule.AsUpdateable() return await _sysSchedule.AsUpdateable()
.SetColumns(u => u.Status == input.Status) .SetColumns(u => u.Status == input.Status)

View File

@ -31,7 +31,7 @@ public class SysServerService : IDynamicApiController, ITransient
ProcessorCount = Environment.ProcessorCount + " 核", // CPU核心数 ProcessorCount = Environment.ProcessorCount + " 核", // CPU核心数
SysRunTime = ComputerUtil.GetRunTime(), // 系统运行时间 SysRunTime = ComputerUtil.GetRunTime(), // 系统运行时间
RemoteIp = ComputerUtil.GetIpFromOnline(), // 外网地址 RemoteIp = ComputerUtil.GetIpFromOnline(), // 外网地址
LocalIp = App.HttpContext?.Connection?.LocalIpAddress.MapToIPv4().ToString(), // 本地地址 LocalIp = App.HttpContext?.Connection?.LocalIpAddress!.MapToIPv4().ToString(), // 本地地址
RuntimeInformation.FrameworkDescription, // NET框架 RuntimeInformation.FrameworkDescription, // NET框架
Environment = App.HostEnvironment.IsDevelopment() ? "Development" : "Production", Environment = App.HostEnvironment.IsDevelopment() ? "Development" : "Production",
Wwwroot = App.WebHostEnvironment.WebRootPath, // 网站根目录 Wwwroot = App.WebHostEnvironment.WebRootPath, // 网站根目录

View File

@ -29,6 +29,12 @@ public class PageTenantInput : BasePageInput
public class AddTenantInput : TenantOutput public class AddTenantInput : TenantOutput
{ {
///// <summary>
///// 应用Id
///// </summary>
//[Required(ErrorMessage = "应用不能为空")]
//public new long? AppId { get; set; }
/// <summary> /// <summary>
/// 租户名称 /// 租户名称
/// </summary> /// </summary>
@ -40,6 +46,12 @@ public class AddTenantInput : TenantOutput
/// </summary> /// </summary>
[Required(ErrorMessage = "租管账号不能为空"), MinLength(3, ErrorMessage = "租管账号不能少于3个字符")] [Required(ErrorMessage = "租管账号不能为空"), MinLength(3, ErrorMessage = "租管账号不能少于3个字符")]
public override string AdminAccount { get; set; } public override string AdminAccount { get; set; }
///// <summary>
///// 租户域名
///// </summary>
//[Required(ErrorMessage = "域名不能为空"), MinLength(5, ErrorMessage = "域名不能少于5个字符")]
//public new string Host { get; set; }
} }
public class UpdateTenantInput : AddTenantInput public class UpdateTenantInput : AddTenantInput

View File

@ -13,6 +13,11 @@ public class TenantOutput : SysTenant
/// </summary> /// </summary>
public virtual string Name { get; set; } public virtual string Name { get; set; }
///// <summary>
///// 关联应用名称
///// </summary>
//public virtual string AppName { get; set; }
/// <summary> /// <summary>
/// 管理员账号 /// 管理员账号
/// </summary> /// </summary>

View File

@ -22,17 +22,18 @@ public class SysUserLdapService : ITransient
/// 批量插入域账号 /// 批量插入域账号
/// </summary> /// </summary>
/// <param name="tenantId"></param> /// <param name="tenantId"></param>
/// <param name="sysUserLdaps"></param> /// <param name="sysUserLdapList"></param>
/// <returns></returns> /// <returns></returns>
public async Task InsertUserLdaps(long tenantId, List<SysUserLdap> sysUserLdaps) public async Task InsertUserLdapList(long tenantId, List<SysUserLdap> sysUserLdapList)
{ {
await _sysUserLdapRep.DeleteAsync(u => u.TenantId == tenantId); await _sysUserLdapRep.DeleteAsync(u => u.TenantId == tenantId);
await _sysUserLdapRep.InsertRangeAsync(sysUserLdaps); await _sysUserLdapRep.InsertRangeAsync(sysUserLdapList);
await _sysUserLdapRep.AsUpdateable() await _sysUserLdapRep.AsUpdateable()
.InnerJoin<SysUser>((l, u) => l.EmployeeId == u.Account && u.Status == StatusEnum.Enable && u.IsDelete == false && l.IsDelete == false) .InnerJoin<SysUser>((l, u) => l.EmployeeId == u.Account)
.SetColumns((l, u) => new SysUserLdap { UserId = u.Id }) .SetColumns((l, u) => new SysUserLdap { UserId = u.Id })
.Where((l, u) => l.TenantId == tenantId && u.Status == StatusEnum.Enable && u.IsDelete == false && l.IsDelete == false)
.ExecuteCommandAsync(); .ExecuteCommandAsync();
} }
@ -47,8 +48,7 @@ public class SysUserLdapService : ITransient
public async Task AddUserLdap(long tenantId, long userId, string account, string domainAccount) public async Task AddUserLdap(long tenantId, long userId, string account, string domainAccount)
{ {
var userLdap = await _sysUserLdapRep.GetFirstAsync(u => u.TenantId == tenantId && u.IsDelete == false && (u.Account == account || u.UserId == userId || u.EmployeeId == domainAccount)); var userLdap = await _sysUserLdapRep.GetFirstAsync(u => u.TenantId == tenantId && u.IsDelete == false && (u.Account == account || u.UserId == userId || u.EmployeeId == domainAccount));
if (userLdap != null) if (userLdap != null) await _sysUserLdapRep.DeleteByIdAsync(userLdap.Id);
await _sysUserLdapRep.DeleteByIdAsync(userLdap.Id);
if (!string.IsNullOrWhiteSpace(domainAccount)) if (!string.IsNullOrWhiteSpace(domainAccount))
await _sysUserLdapRep.InsertAsync(new SysUserLdap { EmployeeId = account, TenantId = tenantId, UserId = userId, Account = domainAccount }); await _sysUserLdapRep.InsertAsync(new SysUserLdap { EmployeeId = account, TenantId = tenantId, UserId = userId, Account = domainAccount });

View File

@ -14,10 +14,12 @@ namespace Admin.NET.Core.Service;
public class SysUserMenuService : IDynamicApiController, ITransient public class SysUserMenuService : IDynamicApiController, ITransient
{ {
private readonly SqlSugarRepository<SysUserMenu> _sysUserMenuRep; private readonly SqlSugarRepository<SysUserMenu> _sysUserMenuRep;
private readonly UserManager _userManager;
public SysUserMenuService(SqlSugarRepository<SysUserMenu> sysUserMenuRep) public SysUserMenuService(SqlSugarRepository<SysUserMenu> sysUserMenuRep, UserManager userManager)
{ {
_sysUserMenuRep = sysUserMenuRep; _sysUserMenuRep = sysUserMenuRep;
_userManager = userManager;
} }
/// <summary> /// <summary>
@ -26,16 +28,16 @@ public class SysUserMenuService : IDynamicApiController, ITransient
/// <param name="input"></param> /// <param name="input"></param>
/// <returns></returns> /// <returns></returns>
[UnitOfWork] [UnitOfWork]
[ApiDescriptionSettings(Name = "Add"), HttpPost]
[DisplayName("收藏菜单")] [DisplayName("收藏菜单")]
[ApiDescriptionSettings(Name = "Add"), HttpPost]
public async Task AddUserMenu(UserMenuInput input) public async Task AddUserMenu(UserMenuInput input)
{ {
await _sysUserMenuRep.DeleteAsync(u => u.UserId == input.UserId); await _sysUserMenuRep.DeleteAsync(u => u.UserId == _userManager.UserId);
if (input.MenuIdList == null || input.MenuIdList.Count < 1) return; if (input.MenuIdList == null || input.MenuIdList.Count == 0) return;
var menus = input.MenuIdList.Select(u => new SysUserMenu var menus = input.MenuIdList.Select(u => new SysUserMenu
{ {
UserId = input.UserId, UserId = _userManager.UserId,
MenuId = u MenuId = u
}).ToList(); }).ToList();
await _sysUserMenuRep.InsertRangeAsync(menus); await _sysUserMenuRep.InsertRangeAsync(menus);
@ -46,48 +48,55 @@ public class SysUserMenuService : IDynamicApiController, ITransient
/// </summary> /// </summary>
/// <param name="input"></param> /// <param name="input"></param>
/// <returns></returns> /// <returns></returns>
[ApiDescriptionSettings(Name = "Delete"), HttpPost] [ApiDescriptionSettings(Name = "DeleteUserMenu"), HttpPost]
[DisplayName("取消收藏菜单")] [DisplayName("取消收藏菜单")]
public async Task DeleteUserMenu(UserMenuInput input) public async Task DeleteUserMenu(UserMenuInput input)
{ {
await _sysUserMenuRep.DeleteAsync(u => u.UserId == input.UserId && input.MenuIdList.Contains(u.MenuId)); await _sysUserMenuRep.DeleteAsync(u => u.UserId == _userManager.UserId && input.MenuIdList.Contains(u.MenuId));
} }
/// <summary> /// <summary>
/// 根据用户Id删除收藏菜单 🔖 /// 获取当前用户收藏的菜单集合 🔖
/// </summary> /// </summary>
/// <param name="userId"></param>
/// <returns></returns> /// <returns></returns>
[ApiDescriptionSettings(Name = "DeleteByUserId"), HttpPost] [DisplayName("获取当前用户收藏的菜单集合")]
[DisplayName("根据用户Id删除收藏菜单")] public async Task<List<MenuOutput>> GetUserMenuList()
public async Task DeleteByUserId(long userId) {
var sysUserMenuList = await _sysUserMenuRep.AsQueryable()
.Includes(u => u.SysMenu)
.Where(u => u.UserId == _userManager.UserId).ToListAsync();
return sysUserMenuList.Where(u => u.SysMenu != null).Select(u => u.SysMenu).ToList().Adapt<List<MenuOutput>>();
}
/// <summary>
/// 获取当前用户收藏的菜单Id集合 🔖
/// </summary>
/// <returns></returns>
[DisplayName("获取当前用户收藏的菜单Id集合")]
public async Task<List<long>> GetUserMenuIdList()
{
return await _sysUserMenuRep.AsQueryable()
.Where(u => u.UserId == _userManager.UserId).Select(u => u.MenuId).ToListAsync();
}
/// <summary>
/// 删除指定用户的收藏菜单
/// </summary>
/// <returns></returns>
[NonAction]
public async Task DeleteUserMenuList(long userId)
{ {
await _sysUserMenuRep.DeleteAsync(u => u.UserId == userId); await _sysUserMenuRep.DeleteAsync(u => u.UserId == userId);
} }
/// <summary> /// <summary>
/// 根据用户Id获取收藏菜单集合 🔖 /// 批量删除收藏菜单
/// </summary> /// </summary>
/// <param name="userId"></param> /// <param name="ids"></param>
/// <returns></returns> [NonAction]
[DisplayName("根据用户Id获取收藏菜单集合")] public async Task DeleteMenuList(List<long> ids)
public async Task<List<MenuOutput>> GetUserMenuList(long userId)
{ {
var sysUserMenuList = await _sysUserMenuRep.AsQueryable() if (ids == null || ids.Count == 0) return;
.Includes(u => u.SysMenu) await _sysUserMenuRep.DeleteAsync(u => ids.Contains(u.MenuId));
.Where(u => u.UserId == userId).ToListAsync();
return sysUserMenuList.Where(u => u.SysMenu != null).Select(u => u.SysMenu).ToList().Adapt<List<MenuOutput>>();
}
/// <summary>
/// 根据用户Id获取收藏菜单Id集合 🔖
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
[DisplayName("根据用户Id获取收藏菜单Id集合")]
public async Task<List<long>> GetUserMenuIdList(long userId)
{
return await _sysUserMenuRep.AsQueryable()
.Where(u => u.UserId == userId).Select(u => u.MenuId).ToListAsync();
} }
} }

View File

@ -11,13 +11,10 @@ namespace Admin.NET.Core.Service;
/// </summary> /// </summary>
public class SysUserRoleService : ITransient public class SysUserRoleService : ITransient
{ {
private readonly SysCacheService _sysCacheService;
private readonly SqlSugarRepository<SysUserRole> _sysUserRoleRep; private readonly SqlSugarRepository<SysUserRole> _sysUserRoleRep;
public SysUserRoleService(SysCacheService sysCacheService, public SysUserRoleService(SqlSugarRepository<SysUserRole> sysUserRoleRep)
SqlSugarRepository<SysUserRole> sysUserRoleRep)
{ {
_sysCacheService = sysCacheService;
_sysUserRoleRep = sysUserRoleRep; _sysUserRoleRep = sysUserRoleRep;
} }

View File

@ -19,6 +19,7 @@ public class SysUserService : IDynamicApiController, ITransient
private readonly SysUserRoleService _sysUserRoleService; private readonly SysUserRoleService _sysUserRoleService;
private readonly SysConfigService _sysConfigService; private readonly SysConfigService _sysConfigService;
private readonly SysOnlineUserService _sysOnlineUserService; private readonly SysOnlineUserService _sysOnlineUserService;
private readonly SysUserMenuService _sysUserMenuService;
private readonly SysCacheService _sysCacheService; private readonly SysCacheService _sysCacheService;
private readonly SysUserLdapService _sysUserLdapService; private readonly SysUserLdapService _sysUserLdapService;
private readonly SqlSugarRepository<SysUser> _sysUserRep; private readonly SqlSugarRepository<SysUser> _sysUserRep;
@ -31,6 +32,7 @@ public class SysUserService : IDynamicApiController, ITransient
SysUserRoleService sysUserRoleService, SysUserRoleService sysUserRoleService,
SysConfigService sysConfigService, SysConfigService sysConfigService,
SysOnlineUserService sysOnlineUserService, SysOnlineUserService sysOnlineUserService,
SysUserMenuService sysUserMenuService,
SysCacheService sysCacheService, SysCacheService sysCacheService,
SysUserLdapService sysUserLdapService, SysUserLdapService sysUserLdapService,
SqlSugarRepository<SysUser> sysUserRep, SqlSugarRepository<SysUser> sysUserRep,
@ -43,6 +45,7 @@ public class SysUserService : IDynamicApiController, ITransient
_sysUserRoleService = sysUserRoleService; _sysUserRoleService = sysUserRoleService;
_sysConfigService = sysConfigService; _sysConfigService = sysConfigService;
_sysOnlineUserService = sysOnlineUserService; _sysOnlineUserService = sysOnlineUserService;
_sysUserMenuService = sysUserMenuService;
_sysCacheService = sysCacheService; _sysCacheService = sysCacheService;
_sysUserLdapService = sysUserLdapService; _sysUserLdapService = sysUserLdapService;
_sysUserRep = sysUserRep; _sysUserRep = sysUserRep;
@ -100,8 +103,11 @@ public class SysUserService : IDynamicApiController, ITransient
[DisplayName("增加用户")] [DisplayName("增加用户")]
public virtual async Task<long> AddUser(AddUserInput input) public virtual async Task<long> AddUser(AddUserInput input)
{ {
var isExist = await _sysUserRep.AsQueryable().ClearFilter().AnyAsync(u => u.Account == input.Account); // 是否租户隔离登录验证
if (isExist) throw Oops.Oh(ErrorCodeEnum.D1003); var isTenantHostLogin = await _sysConfigService.GetConfigValueByCode<bool>(ConfigConst.SysTenantHostLogin);
var query = _sysUserRep.AsQueryable().ClearFilter().WhereIF(isTenantHostLogin, u => u.TenantId == _userManager.TenantId || u.AccountType == AccountTypeEnum.SuperAdmin);
if (await query.AnyAsync(u => u.Account == input.Account)) throw Oops.Oh(ErrorCodeEnum.D1003);
if (!string.IsNullOrWhiteSpace(input.Phone) && await query.AnyAsync(u => u.Phone == input.Phone)) throw Oops.Oh(ErrorCodeEnum.D1032);
var password = await _sysConfigService.GetConfigValueByCode<string>(ConfigConst.SysPassword); var password = await _sysConfigService.GetConfigValueByCode<string>(ConfigConst.SysPassword);
@ -114,7 +120,7 @@ public class SysUserService : IDynamicApiController, ITransient
// 增加域账号 // 增加域账号
if (!string.IsNullOrWhiteSpace(input.DomainAccount)) if (!string.IsNullOrWhiteSpace(input.DomainAccount))
await _sysUserLdapService.AddUserLdap(newUser.TenantId.Value, newUser.Id, newUser.Account, input.DomainAccount); await _sysUserLdapService.AddUserLdap(newUser.TenantId!.Value, newUser.Id, newUser.Account, input.DomainAccount);
// 执行订阅事件 // 执行订阅事件
_sysUserEventHandler.OnEvent(this, SysUserEventTypeEnum.Add, input); _sysUserEventHandler.OnEvent(this, SysUserEventTypeEnum.Add, input);
@ -132,11 +138,15 @@ public class SysUserService : IDynamicApiController, ITransient
[DisplayName("更新用户")] [DisplayName("更新用户")]
public virtual async Task UpdateUser(UpdateUserInput input) public virtual async Task UpdateUser(UpdateUserInput input)
{ {
if (await _sysUserRep.AsQueryable().ClearFilter().AnyAsync(u => u.Account == input.Account && u.Id != input.Id)) // 是否租户隔离登录验证
throw Oops.Oh(ErrorCodeEnum.D1003); var isTenantHostLogin = await _sysConfigService.GetConfigValueByCode<bool>(ConfigConst.SysTenantHostLogin);
var query = _sysUserRep.AsQueryable().ClearFilter().Where(u => u.Id != input.Id)
.WhereIF(isTenantHostLogin, u => u.TenantId == _userManager.TenantId || u.AccountType == AccountTypeEnum.SuperAdmin);
if (await query.AnyAsync(u => u.Account == input.Account)) throw Oops.Oh(ErrorCodeEnum.D1003);
if (!string.IsNullOrWhiteSpace(input.Phone) && await query.AnyAsync(u => u.Phone == input.Phone)) throw Oops.Oh(ErrorCodeEnum.D1032);
await _sysUserRep.AsUpdateable(input.Adapt<SysUser>()).IgnoreColumns(true) await _sysUserRep.AsUpdateable(input.Adapt<SysUser>()).IgnoreColumns(true)
.IgnoreColumns(u => new { u.Password, u.Status }).ExecuteCommandAsync(); .IgnoreColumns(u => new { u.Password, u.Status, u.TenantId }).ExecuteCommandAsync();
await UpdateRoleAndExtOrg(input); await UpdateRoleAndExtOrg(input);
@ -149,7 +159,7 @@ public class SysUserService : IDynamicApiController, ITransient
if (input.OrgId != user.OrgId || !input.RoleIdList.OrderBy(u => u).SequenceEqual(roleIds.OrderBy(u => u))) if (input.OrgId != user.OrgId || !input.RoleIdList.OrderBy(u => u).SequenceEqual(roleIds.OrderBy(u => u)))
await _sysOnlineUserService.ForceOffline(input.Id); await _sysOnlineUserService.ForceOffline(input.Id);
// 更新域账号 // 更新域账号
await _sysUserLdapService.AddUserLdap(user.TenantId.Value, user.Id, user.Account, input.DomainAccount); await _sysUserLdapService.AddUserLdap(user.TenantId!.Value, user.Id, user.Account, input.DomainAccount);
// 执行订阅事件 // 执行订阅事件
_sysUserEventHandler.OnEvent(this, SysUserEventTypeEnum.Update, input); _sysUserEventHandler.OnEvent(this, SysUserEventTypeEnum.Update, input);
@ -183,13 +193,11 @@ public class SysUserService : IDynamicApiController, ITransient
// 若账号为租户默认账号则禁止删除 // 若账号为租户默认账号则禁止删除
var isTenantUser = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysTenant>>().IsAnyAsync(u => u.UserId == input.Id); var isTenantUser = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysTenant>>().IsAnyAsync(u => u.UserId == input.Id);
if (isTenantUser) if (isTenantUser) throw Oops.Oh(ErrorCodeEnum.D1029);
throw Oops.Oh(ErrorCodeEnum.D1029);
// 若账号为开放接口绑定账号则禁止删除 // 若账号为开放接口绑定账号则禁止删除
var isOpenAccessUser = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysOpenAccess>>().IsAnyAsync(u => u.BindUserId == input.Id); var isOpenAccessUser = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysOpenAccess>>().IsAnyAsync(u => u.BindUserId == input.Id);
if (isOpenAccessUser) if (isOpenAccessUser) throw Oops.Oh(ErrorCodeEnum.D1030);
throw Oops.Oh(ErrorCodeEnum.D1030);
// 设置账号Token黑名单 // 设置账号Token黑名单
await SetUserBalckList(user, StatusEnum.Disable); await SetUserBalckList(user, StatusEnum.Disable);
@ -205,6 +213,9 @@ public class SysUserService : IDynamicApiController, ITransient
// 删除域账号 // 删除域账号
await _sysUserLdapService.DeleteUserLdapByUserId(input.Id); await _sysUserLdapService.DeleteUserLdapByUserId(input.Id);
// 删除用户收藏菜单
await _sysUserMenuService.DeleteUserMenuList(input.Id);
// 执行订阅事件 // 执行订阅事件
_sysUserEventHandler.OnEvent(this, SysUserEventTypeEnum.Delete, input); _sysUserEventHandler.OnEvent(this, SysUserEventTypeEnum.Delete, input);
} }

View File

@ -13,6 +13,11 @@ public class UserManager : IScoped
{ {
private readonly IHttpContextAccessor _httpContextAccessor; private readonly IHttpContextAccessor _httpContextAccessor;
/// <summary>
/// 应用ID
/// </summary>
public long AppId => (_httpContextAccessor.HttpContext?.User.FindFirst(ClaimConst.AppId)?.Value).ToLong();
/// <summary> /// <summary>
/// 用户ID /// 用户ID
/// </summary> /// </summary>

View File

@ -169,19 +169,18 @@ public static class SqlSugarSetup
//} //}
// 执行时间超过5秒时 // 执行时间超过5秒时
if (db.Ado.SqlExecutionTime.TotalSeconds > 5) if (db.Ado.SqlExecutionTime.TotalSeconds <= 5) return;
{
var fileName = db.Ado.SqlStackTrace.FirstFileName; // 文件名 var fileName = db.Ado.SqlStackTrace.FirstFileName; // 文件名
var fileLine = db.Ado.SqlStackTrace.FirstLine; // 行号 var fileLine = db.Ado.SqlStackTrace.FirstLine; // 行号
var firstMethodName = db.Ado.SqlStackTrace.FirstMethodName; // 方法名 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 log = $"【{DateTime.Now}——超时SQL】\r\n【所在文件名】{fileName}\r\n【代码行数】{fileLine}\r\n【方法名】{firstMethodName}\r\n" + $"【SQL语句】{UtilMethods.GetNativeSql(sql, pars)}";
Log.Warning(log); Log.Warning(log);
App.PrintToMiniProfiler("SqlSugar", "Slow", log); App.PrintToMiniProfiler("SqlSugar", "Slow", log);
}
}; };
} }
// 数据审计 // 数据审计
db.Aop.DataExecuting = (oldValue, entityInfo) => db.Aop.DataExecuting = (_, entityInfo) =>
{ {
// 若正在处理种子数据则直接返回 // 若正在处理种子数据则直接返回
if (_isHandlingSeedData) return; if (_isHandlingSeedData) return;
@ -204,34 +203,33 @@ public static class SqlSugarSetup
entityInfo.SetValue(DateTime.Now); entityInfo.SetValue(DateTime.Now);
} }
// 若当前用户非空web线程时 // 若当前用户非空web线程时
if (App.User != null) if (App.User == null) return;
dynamic entityValue = entityInfo.EntityValue;
if (entityInfo.PropertyName == nameof(EntityTenantId.TenantId))
{ {
dynamic entityValue = entityInfo.EntityValue; if (entityValue.TenantId == null || entityValue.TenantId == 0)
if (entityInfo.PropertyName == nameof(EntityTenantId.TenantId)) entityInfo.SetValue(App.User.FindFirst(ClaimConst.TenantId)?.Value);
{ }
if (entityValue.TenantId == null || entityValue.TenantId == 0) else if (entityInfo.PropertyName == nameof(EntityBase.CreateUserId))
entityInfo.SetValue(App.User.FindFirst(ClaimConst.TenantId)?.Value); {
} if (entityValue.CreateUserId == null || entityValue.CreateUserId == 0)
else if (entityInfo.PropertyName == nameof(EntityBase.CreateUserId)) entityInfo.SetValue(App.User.FindFirst(ClaimConst.UserId)?.Value);
{ }
if (entityValue.CreateUserId == null || entityValue.CreateUserId == 0) else if (entityInfo.PropertyName == nameof(EntityBase.CreateUserName))
entityInfo.SetValue(App.User.FindFirst(ClaimConst.UserId)?.Value); {
} if (string.IsNullOrWhiteSpace(entityValue.CreateUserName))
else if (entityInfo.PropertyName == nameof(EntityBase.CreateUserName)) entityInfo.SetValue(App.User.FindFirst(ClaimConst.RealName)?.Value);
{ }
if (string.IsNullOrWhiteSpace(entityValue.CreateUserName)) else if (entityInfo.PropertyName == nameof(EntityBaseData.CreateOrgId))
entityInfo.SetValue(App.User.FindFirst(ClaimConst.RealName)?.Value); {
} if (entityValue.CreateOrgId == null || entityValue.CreateOrgId == 0)
else if (entityInfo.PropertyName == nameof(EntityBaseData.CreateOrgId)) entityInfo.SetValue(App.User.FindFirst(ClaimConst.OrgId)?.Value);
{ }
if (entityValue.CreateOrgId == null || entityValue.CreateOrgId == 0) else if (entityInfo.PropertyName == nameof(EntityBaseData.CreateOrgName))
entityInfo.SetValue(App.User.FindFirst(ClaimConst.OrgId)?.Value); {
} if (string.IsNullOrWhiteSpace(entityValue.CreateOrgName))
else if (entityInfo.PropertyName == nameof(EntityBaseData.CreateOrgName)) entityInfo.SetValue(App.User.FindFirst(ClaimConst.OrgName)?.Value);
{
if (string.IsNullOrWhiteSpace(entityValue.CreateOrgName))
entityInfo.SetValue(App.User.FindFirst(ClaimConst.OrgName)?.Value);
}
} }
} }
// 编辑/更新 // 编辑/更新
@ -283,12 +281,11 @@ public static class SqlSugarSetup
var beforeColumns = u.BeforeData[i].Columns; var beforeColumns = u.BeforeData[i].Columns;
for (int j = 0; j < afterColumns.Count; j++) for (int j = 0; j < afterColumns.Count; j++)
{ {
if (afterColumns[j].Value.Equals(beforeColumns[j].Value)) if (!afterColumns[j].Value.Equals(beforeColumns[j].Value)) continue;
{
beforeColumns.Remove(beforeColumns[j]); beforeColumns.Remove(beforeColumns[j]);
afterColumns.Remove(afterColumns[j]); afterColumns.Remove(afterColumns[j]);
j--; j--;
}
} }
} }
@ -306,7 +303,7 @@ public static class SqlSugarSetup
Parameters = JSON.Serialize(u.Parameters), Parameters = JSON.Serialize(u.Parameters),
Elapsed = u.Time == null ? 0 : (long)u.Time.Value.TotalMilliseconds Elapsed = u.Time == null ? 0 : (long)u.Time.Value.TotalMilliseconds
}; };
var logDb = ITenant.IsAnyConnection(SqlSugarConst.LogConfigId) ? ITenant.GetConnectionScope(SqlSugarConst.LogConfigId) : db; var logDb = ITenant.IsAnyConnection(SqlSugarConst.LogConfigId) ? ITenant.GetConnectionScope(SqlSugarConst.LogConfigId) : ITenant.GetConnectionScope(SqlSugarConst.MainConfigId);
await logDb.CopyNew().Insertable(logDiff).ExecuteCommandAsync(); await logDb.CopyNew().Insertable(logDiff).ExecuteCommandAsync();
Console.ForegroundColor = ConsoleColor.Red; Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(DateTime.Now + $"\r\n*****开始差异日志*****\r\n{Environment.NewLine}{JSON.Serialize(logDiff)}{Environment.NewLine}*****结束差异日志*****\r\n"); Console.WriteLine(DateTime.Now + $"\r\n*****开始差异日志*****\r\n{Environment.NewLine}{JSON.Serialize(logDiff)}{Environment.NewLine}*****结束差异日志*****\r\n");
@ -326,8 +323,7 @@ public static class SqlSugarSetup
if (config.DbSettings.EnableInitDb) if (config.DbSettings.EnableInitDb)
{ {
Log.Information($"初始化数据库 {config.DbType} - {config.ConfigId} - {config.ConnectionString}"); Log.Information($"初始化数据库 {config.DbType} - {config.ConfigId} - {config.ConnectionString}");
if (config.DbType != SqlSugar.DbType.Oracle) if (config.DbType != SqlSugar.DbType.Oracle) dbProvider.DbMaintenance.CreateDatabase();
dbProvider.DbMaintenance.CreateDatabase();
} }
// 初始化表结构 // 初始化表结构
@ -357,91 +353,99 @@ public static class SqlSugarSetup
} }
// 初始化种子数据 // 初始化种子数据
if (config.SeedSettings.EnableInitSeed) if (config.SeedSettings.EnableInitSeed) InitSeedData(db, config);
}
/// <summary>
/// 初始化种子数据
/// </summary>
/// <param name="db"></param>
/// <param name="config"></param>
private static void InitSeedData(SqlSugarScope db, DbConnectionConfig config)
{
SqlSugarScopeProvider dbProvider = db.GetConnectionScope(config.ConfigId);
_isHandlingSeedData = true;
Log.Information($"初始化种子数据 {config.DbType} - {config.ConfigId}");
var seedDataTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.GetInterfaces().Any(i => i.HasImplementedRawGeneric(typeof(ISqlSugarEntitySeedData<>))))
.Where(u => !u.IsDefined(typeof(TenantSeedAttribute), false))
.WhereIF(config.SeedSettings.EnableIncreSeed, u => u.IsDefined(typeof(IncreSeedAttribute), false))
.OrderBy(u => u.GetCustomAttributes(typeof(SeedDataAttribute), false).Length > 0 ? ((SeedDataAttribute)u.GetCustomAttributes(typeof(SeedDataAttribute), false)[0]).Order : 0).ToList();
int count = 0, sum = seedDataTypes.Count;
foreach (var seedType in seedDataTypes)
{ {
_isHandlingSeedData = true; var entityType = seedType.GetInterfaces().First().GetGenericArguments().First();
if (config.ConfigId.ToString() == SqlSugarConst.MainConfigId) // 默认库(有系统表特性、没有日志表和租户表特性)
Log.Information($"初始化种子数据 {config.DbType} - {config.ConfigId}");
var seedDataTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.GetInterfaces().Any(i => i.HasImplementedRawGeneric(typeof(ISqlSugarEntitySeedData<>))))
.Where(u => !u.IsDefined(typeof(TenantSeedAttribute), false))
.WhereIF(config.SeedSettings.EnableIncreSeed, u => u.IsDefined(typeof(IncreSeedAttribute), false))
.OrderBy(u => u.GetCustomAttributes(typeof(SeedDataAttribute), false).Length > 0 ? ((SeedDataAttribute)u.GetCustomAttributes(typeof(SeedDataAttribute), false)[0]).Order : 0).ToList();
int count = 0, sum = seedDataTypes.Count;
foreach (var seedType in seedDataTypes)
{ {
var entityType = seedType.GetInterfaces().First().GetGenericArguments().First(); if (entityType.GetCustomAttribute<SysTableAttribute>() == null && (entityType.GetCustomAttribute<LogTableAttribute>() != null || entityType.GetCustomAttribute<TenantAttribute>() != null))
if (config.ConfigId.ToString() == SqlSugarConst.MainConfigId) // 默认库(有系统表特性、没有日志表和租户表特性) continue;
{ }
if (entityType.GetCustomAttribute<SysTableAttribute>() == null && (entityType.GetCustomAttribute<LogTableAttribute>() != null || entityType.GetCustomAttribute<TenantAttribute>() != null)) else if (config.ConfigId.ToString() == SqlSugarConst.LogConfigId) // 日志库
continue; {
} if (entityType.GetCustomAttribute<LogTableAttribute>() == null)
else if (config.ConfigId.ToString() == SqlSugarConst.LogConfigId) // 日志库 continue;
{ }
if (entityType.GetCustomAttribute<LogTableAttribute>() == null) else
continue; {
} var att = entityType.GetCustomAttribute<TenantAttribute>(); // 自定义的库
else if (att == null || att.configId.ToString() != config.ConfigId.ToString()) continue;
{ }
var att = entityType.GetCustomAttribute<TenantAttribute>(); // 自定义的库
if (att == null || att.configId.ToString() != config.ConfigId.ToString()) continue;
}
var instance = Activator.CreateInstance(seedType); var instance = Activator.CreateInstance(seedType);
var hasDataMethod = seedType.GetMethod("HasData"); var hasDataMethod = seedType.GetMethod("HasData");
var seedData = ((IEnumerable)hasDataMethod?.Invoke(instance, null))?.Cast<object>(); var seedData = ((IEnumerable)hasDataMethod?.Invoke(instance, null))?.Cast<object>();
if (seedData == null) continue; if (seedData == null) continue;
var entityInfo = dbProvider.EntityMaintenance.GetEntityInfo(entityType); var entityInfo = dbProvider.EntityMaintenance.GetEntityInfo(entityType);
Console.WriteLine($"添加数据 {entityInfo.DbTableName} ({config.ConfigId} - {++count}/{sum},数据量:{seedData.Count()})"); Console.WriteLine($"添加数据 {entityInfo.DbTableName} ({config.ConfigId} - {++count}/{sum},数据量:{seedData.Count()})");
// 若实体包含Id字段则设置为当前租户Id递增1 // 若实体包含Id字段则设置为当前租户Id递增1
if (entityInfo.Columns.Any(u => u.PropertyName == nameof(EntityBaseId.Id))) if (entityInfo.Columns.Any(u => u.PropertyName == nameof(EntityBaseId.Id)))
{
var seedId = config.ConfigId.ToLong();
foreach (var sd in seedData)
{ {
var seedId = config.ConfigId.ToLong(); var id = sd.GetType().GetProperty(nameof(EntityBaseId.Id))!.GetValue(sd, null);
foreach (var sd in seedData) if (id != null && (id.ToString() == "0" || string.IsNullOrWhiteSpace(id.ToString())))
{ sd.GetType().GetProperty(nameof(EntityBaseId.Id))!.SetValue(sd, ++seedId);
var id = sd.GetType().GetProperty(nameof(EntityBaseId.Id))!.GetValue(sd, null);
if (id != null && (id.ToString() == "0" || string.IsNullOrWhiteSpace(id.ToString())))
sd.GetType().GetProperty(nameof(EntityBaseId.Id))!.SetValue(sd, ++seedId);
}
}
if (entityType.GetCustomAttribute<SplitTableAttribute>(true) != null)
{
// 拆分表的操作需要实体类型,而通过反射很难实现
// 所以这里将Init方法写在“种子数据类”内部再传入 db 反射调用
var hasInitMethod = seedType.GetMethod("Init");
var parameters = new object[] { db };
hasInitMethod?.Invoke(instance, parameters);
}
else
{
if (entityInfo.Columns.Any(u => u.IsPrimarykey))
{
// 按主键进行批量增加和更新
var storage = dbProvider.StorageableByObject(seedData.ToList()).ToStorage();
// 先修改再插入,否则会更新修改时间字段
if (seedType.GetCustomAttribute<IgnoreUpdateSeedAttribute>() == null) // 有忽略更新种子特性时则不更新
{
int updateCount = storage.AsUpdateable.IgnoreColumns(entityInfo.Columns.Where(u => u.PropertyInfo.GetCustomAttribute<IgnoreUpdateSeedColumnAttribute>() != null).Select(u => u.PropertyName).ToArray()).ExecuteCommand();
Console.WriteLine($" 修改 {updateCount}/{seedData.Count()} 条记录");
}
int insertCount = storage.AsInsertable.ExecuteCommand();
Console.WriteLine($" 插入 {insertCount}/{seedData.Count()} 条记录");
}
else
{
// 无主键则只进行插入
if (!dbProvider.Queryable(entityInfo.DbTableName, entityInfo.DbTableName).Any())
dbProvider.InsertableByObject(seedData.ToList()).ExecuteCommand();
}
} }
} }
_isHandlingSeedData = false; if (entityType.GetCustomAttribute<SplitTableAttribute>(true) != null)
{
// 拆分表的操作需要实体类型,而通过反射很难实现
// 所以这里将Init方法写在“种子数据类”内部再传入 db 反射调用
var hasInitMethod = seedType.GetMethod("Init");
var parameters = new object[] { db };
hasInitMethod?.Invoke(instance, parameters);
}
else
{
if (entityInfo.Columns.Any(u => u.IsPrimarykey))
{
// 按主键进行批量增加和更新
var storage = dbProvider.StorageableByObject(seedData.ToList()).ToStorage();
// 先修改再插入,否则会更新修改时间字段
if (seedType.GetCustomAttribute<IgnoreUpdateSeedAttribute>() == null) // 有忽略更新种子特性时则不更新
{
int updateCount = storage.AsUpdateable.IgnoreColumns(entityInfo.Columns.Where(u => u.PropertyInfo.GetCustomAttribute<IgnoreUpdateSeedColumnAttribute>() != null).Select(u => u.PropertyName).ToArray()).ExecuteCommand();
Console.WriteLine($" 修改 {updateCount}/{seedData.Count()} 条记录");
}
int insertCount = storage.AsInsertable.ExecuteCommand();
Console.WriteLine($" 插入 {insertCount}/{seedData.Count()} 条记录");
}
else
{
// 无主键则只进行插入
if (!dbProvider.Queryable(entityInfo.DbTableName, entityInfo.DbTableName).Any())
dbProvider.InsertableByObject(seedData.ToList()).ExecuteCommand();
}
}
} }
_isHandlingSeedData = false;
} }
/// <summary> /// <summary>

View File

@ -14,16 +14,16 @@ namespace Admin.NET.Core;
/// </summary> /// </summary>
public class ChinaDateTimeConverter : DateTimeConverterBase public class ChinaDateTimeConverter : DateTimeConverterBase
{ {
private static readonly IsoDateTimeConverter dtConverter = new() { DateTimeFormat = "yyyy-MM-dd HH:mm:ss" }; private static readonly IsoDateTimeConverter DtConverter = new() { DateTimeFormat = "yyyy-MM-dd HH:mm:ss" };
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{ {
return dtConverter.ReadJson(reader, objectType, existingValue, serializer); return DtConverter.ReadJson(reader, objectType, existingValue, serializer);
} }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{ {
dtConverter.WriteJson(writer, value, serializer); DtConverter.WriteJson(writer, value, serializer);
} }
} }

View File

@ -62,56 +62,35 @@ public static class CodeGenUtil
return dataType + (dbColumnInfo.IsNullable ? "?" : ""); return dataType + (dbColumnInfo.IsNullable ? "?" : "");
} }
// OracleSQL数据类型对应的字段类型
public static string ConvertDataType_OracleSQL(string dataType, int? length, int? scale) public static string ConvertDataType_OracleSQL(string dataType, int? length, int? scale)
{ {
switch (dataType.ToLower()) switch (dataType.ToLower())
{ {
case "interval year to month": case "interval year to month": return "int";
return "int";
case "interval day to second": case "interval day to second": return "TimeSpan";
return "TimeSpan";
case "smallint": case "smallint": return "Int16";
return "Int16";
case "int": case "int":
case "integer": case "integer": return "int";
return "int";
case "long": case "long": return "long";
return "long";
case "float": case "float": return "float";
return "float";
case "decimal": case "decimal": return "decimal";
return "decimal";
case "number": case "number":
if (length != null) if (length == null) return "decimal";
return scale switch
{ {
if (scale != null && scale > 0) > 0 => "decimal",
{ 0 or null when length is > 1 and < 12 => "int",
return "decimal"; 0 or null when length > 11 => "long",
} _ => length == 1 ? "bool" : "decimal"
else if ((scale != null && scale == 0) || scale == null) };
{
if (length > 1 && length < 12)
{
return "int";
}
else if (length > 11)
{
return "long";
}
}
if (length == 1)
{
return "bool";
}
}
return "decimal";
case "char": case "char":
case "clob": case "clob":
@ -145,7 +124,7 @@ public static class CodeGenUtil
} }
} }
//PostgreSQL数据类型对应的字段类型 // PostgreSQL数据类型对应的字段类型
public static string ConvertDataType_PostgreSQL(string dataType) public static string ConvertDataType_PostgreSQL(string dataType)
{ {
switch (dataType) switch (dataType)
@ -229,6 +208,7 @@ public static class CodeGenUtil
} }
} }
//
public static string ConvertDataType_Default(string dataType) public static string ConvertDataType_Default(string dataType)
{ {
return dataType.ToLower() switch return dataType.ToLower() switch
@ -262,10 +242,10 @@ public static class CodeGenUtil
{ {
"string" => "Input", "string" => "Input",
"int" => "InputNumber", "int" => "InputNumber",
"long" => "Input", "long" => "InputNumber",
"float" => "Input", "float" => "InputNumber",
"double" => "Input", "double" => "InputNumber",
"decimal" => "Input", "decimal" => "InputNumber",
"bool" => "Switch", "bool" => "Switch",
"Guid" => "Input", "Guid" => "Input",
"DateTime" => "DatePicker", "DateTime" => "DatePicker",

View File

@ -17,18 +17,41 @@ namespace Admin.NET.Core;
/// </summary> /// </summary>
public static class CommonUtil public static class CommonUtil
{ {
/// <summary>
/// 根据字符串获取固定整型哈希值
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public static int GetFixedHashCode(string str)
{
if (string.IsNullOrWhiteSpace(str)) return 0;
unchecked
{
int hash1 = (5381 << 16) + 5381;
int hash2 = hash1;
for (int i = 0; i < str.Length; i += 2)
{
hash1 = ((hash1 << 5) + hash1) ^ str[i];
if (i == str.Length - 1)
break;
hash2 = ((hash2 << 5) + hash2) ^ str[i + 1];
}
return Math.Abs(hash1 + (hash2 * 1566083941));
}
}
/// <summary> /// <summary>
/// 生成百分数 /// 生成百分数
/// </summary> /// </summary>
/// <param name="PassCount"></param> /// <param name="passCount"></param>
/// <param name="allCount"></param> /// <param name="allCount"></param>
/// <returns></returns> /// <returns></returns>
public static string ExecPercent(decimal PassCount, decimal allCount) public static string ExecPercent(decimal passCount, decimal allCount)
{ {
string res = ""; string res = "";
if (allCount > 0) if (allCount > 0)
{ {
var value = (double)Math.Round(PassCount / allCount * 100, 1); var value = (double)Math.Round(passCount / allCount * 100, 1);
if (value < 0) if (value < 0)
res = Math.Round(value + 5 / Math.Pow(10, 0 + 1), 0, MidpointRounding.AwayFromZero).ToString(); res = Math.Round(value + 5 / Math.Pow(10, 0 + 1), 0, MidpointRounding.AwayFromZero).ToString();
else else
@ -132,17 +155,17 @@ public static class CommonUtil
public static async Task<IActionResult> ExportExcelData<TSource, TTarget>(ISugarQueryable<TSource> query, Func<TSource, TTarget, TTarget> action = null) public static async Task<IActionResult> ExportExcelData<TSource, TTarget>(ISugarQueryable<TSource> query, Func<TSource, TTarget, TTarget> action = null)
where TSource : class, new() where TTarget : class, new() where TSource : class, new() where TTarget : class, new()
{ {
var PropMappings = GetExportPropertMap<TSource, TTarget>(); var propMappings = GetExportPropertMap<TSource, TTarget>();
var data = query.ToList(); var data = query.ToList();
//相同属性复制值,字典值转换 // 相同属性复制值,字典值转换
var result = new List<TTarget>(); var result = new List<TTarget>();
foreach (var item in data) foreach (var item in data)
{ {
var newData = new TTarget(); var newData = new TTarget();
foreach (var dict in PropMappings) foreach (var dict in propMappings)
{ {
var targeProp = dict.Value.Item3; var targetProp = dict.Value.Item3;
if (targeProp != null) if (targetProp != null)
{ {
var propertyInfo = dict.Value.Item2; var propertyInfo = dict.Value.Item2;
var sourceVal = propertyInfo.GetValue(item, null); var sourceVal = propertyInfo.GetValue(item, null);
@ -152,21 +175,20 @@ public static class CommonUtil
} }
var map = dict.Value.Item1; var map = dict.Value.Item1;
if (map != null && map.TryGetValue(sourceVal, out string value)) if (map != null && map.TryGetValue(sourceVal, out string newVal1))
{ {
var newVal = value; targetProp.SetValue(newData, newVal1);
targeProp.SetValue(newData, newVal);
} }
else else
{ {
if (targeProp.PropertyType.FullName == propertyInfo.PropertyType.FullName) if (targetProp.PropertyType.FullName == propertyInfo.PropertyType.FullName)
{ {
targeProp.SetValue(newData, sourceVal); targetProp.SetValue(newData, sourceVal);
} }
else else
{ {
var newVal = sourceVal.ToString().ParseTo(targeProp.PropertyType); var newVal = sourceVal.ToString().ParseTo(targetProp.PropertyType);
targeProp.SetValue(newData, newVal); targetProp.SetValue(newData, newVal);
} }
} }
} }
@ -190,23 +212,22 @@ public static class CommonUtil
/// <returns></returns> /// <returns></returns>
public static async Task<ICollection<T>> ImportExcelData<T>([Required] IFormFile file) where T : class, new() public static async Task<ICollection<T>> ImportExcelData<T>([Required] IFormFile file) where T : class, new()
{ {
var importer = new ExcelImporter(); IImporter importer = new ExcelImporter();
var res = await importer.Import<T>(file.OpenReadStream()); var res = await importer.Import<T>(file.OpenReadStream());
var message = string.Empty; var message = string.Empty;
if (res.HasError)
if (!res.HasError) return res.Data;
if (res.Exception != null)
message += $"\r\n{res.Exception.Message}";
foreach (DataRowErrorInfo drErrorInfo in res.RowErrors)
{ {
if (res.Exception != null) int rowNum = drErrorInfo.RowIndex;
message += $"\r\n{res.Exception.Message}"; foreach (var item in drErrorInfo.FieldErrors)
foreach (DataRowErrorInfo drErrorInfo in res.RowErrors) message += $"\r\n{item.Key}{item.Value}(文件第{drErrorInfo.RowIndex}行)";
{
int rowNum = drErrorInfo.RowIndex;
foreach (var item in drErrorInfo.FieldErrors)
message += $"\r\n{item.Key}{item.Value}(文件第{drErrorInfo.RowIndex}行)";
}
message += "\r\n字段缺失" + string.Join("", res.TemplateErrors.Select(m => m.RequireColumnName).ToList());
throw Oops.Oh("导入异常:" + message);
} }
return res.Data; message += "\r\n字段缺失" + string.Join("", res.TemplateErrors.Select(m => m.RequireColumnName).ToList());
throw Oops.Oh("导入异常:" + message);
} }
/// <summary> /// <summary>
@ -218,7 +239,7 @@ public static class CommonUtil
/// <returns></returns> /// <returns></returns>
public static async Task<ICollection<T>> ImportExcelData<T>([Required] IFormFile file, Func<ImportResult<T>, ImportResult<T>> importResultCallback = null) where T : class, new() public static async Task<ICollection<T>> ImportExcelData<T>([Required] IFormFile file, Func<ImportResult<T>, ImportResult<T>> importResultCallback = null) where T : class, new()
{ {
var importer = new ExcelImporter(); IImporter importer = new ExcelImporter();
var resultStream = new MemoryStream(); var resultStream = new MemoryStream();
var res = await importer.Import<T>(file.OpenReadStream(), resultStream, importResultCallback); var res = await importer.Import<T>(file.OpenReadStream(), resultStream, importResultCallback);
resultStream.Seek(0, SeekOrigin.Begin); resultStream.Seek(0, SeekOrigin.Begin);
@ -228,24 +249,20 @@ public static class CommonUtil
App.GetRequiredService<SysCacheService>().Set(CacheConst.KeyExcelTemp + userId, resultStream, TimeSpan.FromMinutes(5)); App.GetRequiredService<SysCacheService>().Set(CacheConst.KeyExcelTemp + userId, resultStream, TimeSpan.FromMinutes(5));
var message = string.Empty; var message = string.Empty;
if (res.HasError) if (!res.HasError) return res.Data;
{
if (res.Exception != null)
message += $"\r\n{res.Exception.Message}";
foreach (DataRowErrorInfo drErrorInfo in res.RowErrors)
{
int rowNum = drErrorInfo.RowIndex;
foreach (var item in drErrorInfo.FieldErrors)
message += $"\r\n{item.Key}{item.Value}(文件第{drErrorInfo.RowIndex}行)";
}
if (res.TemplateErrors.Count > 0)
message += "\r\n字段缺失" + string.Join("", res.TemplateErrors.Select(m => m.RequireColumnName).ToList());
if (message.Length > 200) if (res.Exception != null)
message = string.Concat(message.AsSpan(0, 200), "...\r\n异常过多建议下载错误标记文件查看详细错误信息并重新导入。"); message += $"\r\n{res.Exception.Message}";
throw Oops.Oh("导入异常:" + message); foreach (DataRowErrorInfo drErrorInfo in res.RowErrors)
{
message = drErrorInfo.FieldErrors.Aggregate(message, (current, item) => current + $"\r\n{item.Key}{item.Value}(文件第{drErrorInfo.RowIndex}行)");
} }
return res.Data; if (res.TemplateErrors.Count > 0)
message += "\r\n字段缺失" + string.Join("", res.TemplateErrors.Select(m => m.RequireColumnName).ToList());
if (message.Length > 200)
message = message.Substring(0, 200) + "...\r\n异常过多建议下载错误标记文件查看详细错误信息并重新导入。";
throw Oops.Oh("导入异常:" + message);
} }
/// <summary> /// <summary>
@ -409,34 +426,34 @@ public static class CommonUtil
else else
{ {
propMappings.Add(propertyInfo.Name, new Tuple<Dictionary<object, string>, PropertyInfo, PropertyInfo>( propMappings.Add(propertyInfo.Name, new Tuple<Dictionary<object, string>, PropertyInfo, PropertyInfo>(
null, sourceProps.TryGetValue(propertyInfo.Name, out PropertyInfo value) ? value : null, propertyInfo)); null, sourceProps.TryGetValue(propertyInfo.Name, out PropertyInfo prop) ? prop : null, propertyInfo));
} }
} }
return propMappings; return propMappings;
} }
///// <summary> /// <summary>
///// 获取属性映射 /// 获取属性映射
///// </summary> /// </summary>
///// <typeparam name="TTarget"></typeparam> /// <typeparam name="TTarget"></typeparam>
///// <returns>整理导入对象的 属性名称, 字典数据,原属性信息,目标属性信息 </returns> /// <returns>整理导入对象的 属性名称, 字典数据,原属性信息,目标属性信息 </returns>
//private static Dictionary<string, Tuple<string, string>> GetExportDicttMap<TTarget>() where TTarget : new() private static Dictionary<string, Tuple<string, string>> GetExportDictMap<TTarget>() where TTarget : new()
//{ {
// // 整理导入对象的属性名称目标属性名字典Code // 整理导入对象的属性名称目标属性名字典Code
// var propMappings = new Dictionary<string, Tuple<string, string>>(); var propMappings = new Dictionary<string, Tuple<string, string>>();
// var tTargetProps = typeof(TTarget).GetProperties(); var tTargetProps = typeof(TTarget).GetProperties();
// foreach (var propertyInfo in tTargetProps) foreach (var propertyInfo in tTargetProps)
// { {
// var attrs = propertyInfo.GetCustomAttribute<ImportDictAttribute>(); var attrs = propertyInfo.GetCustomAttribute<ImportDictAttribute>();
// if (attrs != null && !string.IsNullOrWhiteSpace(attrs.TypeCode)) if (attrs != null && !string.IsNullOrWhiteSpace(attrs.TypeCode))
// { {
// propMappings.Add(propertyInfo.Name, new Tuple<string, string>(attrs.TargetPropName, attrs.TypeCode)); propMappings.Add(propertyInfo.Name, new Tuple<string, string>(attrs.TargetPropName, attrs.TypeCode));
// } }
// } }
// return propMappings; return propMappings;
//} }
/// <summary> /// <summary>
/// 解析IP地址 /// 解析IP地址

View File

@ -30,7 +30,7 @@ public static class ComputerUtil
memoryMetrics.FreeRam = Math.Round(memoryMetrics.Free / 1024, 2) + "GB"; memoryMetrics.FreeRam = Math.Round(memoryMetrics.Free / 1024, 2) + "GB";
memoryMetrics.UsedRam = Math.Round(memoryMetrics.Used / 1024, 2) + "GB"; memoryMetrics.UsedRam = Math.Round(memoryMetrics.Used / 1024, 2) + "GB";
memoryMetrics.TotalRam = Math.Round(memoryMetrics.Total / 1024, 2) + "GB"; memoryMetrics.TotalRam = Math.Round(memoryMetrics.Total / 1024, 2) + "GB";
memoryMetrics.RamRate = Math.Ceiling(100 * memoryMetrics.Used / memoryMetrics.Total).ToString() + "%"; memoryMetrics.RamRate = Math.Ceiling(100 * memoryMetrics.Used / memoryMetrics.Total) + "%";
var cpuRates = GetCPURates(); var cpuRates = GetCPURates();
if (cpuRates != null) if (cpuRates != null)
{ {
@ -45,25 +45,25 @@ public static class ComputerUtil
/// <returns></returns> /// <returns></returns>
public static String GetOSInfo() public static String GetOSInfo()
{ {
string opeartion = string.Empty; string operation = string.Empty;
if (IsMacOS()) if (IsMacOS())
{ {
var output = ShellHelper.Bash("sw_vers | awk 'NR<=2{printf \"%s \", $NF}'"); var output = ShellUtil.Bash("sw_vers | awk 'NR<=2{printf \"%s \", $NF}'");
if (output != null) if (output != null)
{ {
opeartion = output.Replace("%", string.Empty); operation = output.Replace("%", string.Empty);
} }
} }
else if (IsUnix()) else if (IsUnix())
{ {
var output = ShellHelper.Bash("awk -F= '/^VERSION_ID/ {print $2}' /etc/os-release | tr -d '\"'"); var output = ShellUtil.Bash("awk -F= '/^VERSION_ID/ {print $2}' /etc/os-release | tr -d '\"'");
opeartion = output ?? string.Empty; operation = output ?? string.Empty;
} }
else else
{ {
opeartion = RuntimeInformation.OSDescription; operation = RuntimeInformation.OSDescription;
} }
return opeartion; return operation;
} }
/// <summary> /// <summary>
@ -75,19 +75,18 @@ public static class ComputerUtil
var diskInfos = new List<DiskInfo>(); var diskInfos = new List<DiskInfo>();
if (IsMacOS()) if (IsMacOS())
{ {
var output = ShellHelper.Bash(@"df -m | awk '/^\/dev\/disk/ {print $1,$2,$3,$4,$5}'"); var output = ShellUtil.Bash(@"df -m | awk '/^\/dev\/disk/ {print $1,$2,$3,$4,$5}'");
var disks = output.Split('\n', StringSplitOptions.RemoveEmptyEntries); var disks = output.Split('\n', StringSplitOptions.RemoveEmptyEntries);
if (disks.Length < 1) return diskInfos; if (disks.Length < 1) return diskInfos;
foreach (var item in disks) foreach (var item in disks)
{ {
var disk = item.Split(' ', (char)StringSplitOptions.RemoveEmptyEntries); var disk = item.Split(' ', (char)StringSplitOptions.RemoveEmptyEntries);
if (disk == null || disk.Length < 5) if (disk.Length < 5) continue;
continue;
var diskInfo = new DiskInfo() var diskInfo = new DiskInfo()
{ {
DiskName = disk[0], DiskName = disk[0],
TypeName = ShellHelper.Bash("diskutil info " + disk[0] + " | awk '/File System Personality/ {print $4}'").Replace("\n", string.Empty), TypeName = ShellUtil.Bash("diskutil info " + disk[0] + " | awk '/File System Personality/ {print $4}'").Replace("\n", string.Empty),
TotalSize = Math.Round(long.Parse(disk[1]) / 1024.0m, 2, MidpointRounding.AwayFromZero), TotalSize = Math.Round(long.Parse(disk[1]) / 1024.0m, 2, MidpointRounding.AwayFromZero),
Used = Math.Round(long.Parse(disk[2]) / 1024.0m, 2, MidpointRounding.AwayFromZero), Used = Math.Round(long.Parse(disk[2]) / 1024.0m, 2, MidpointRounding.AwayFromZero),
AvailableFreeSpace = Math.Round(long.Parse(disk[3]) / 1024.0m, 2, MidpointRounding.AwayFromZero), AvailableFreeSpace = Math.Round(long.Parse(disk[3]) / 1024.0m, 2, MidpointRounding.AwayFromZero),
@ -98,7 +97,7 @@ public static class ComputerUtil
} }
else if (IsUnix()) else if (IsUnix())
{ {
var output = ShellHelper.Bash(@"df -mT | awk '/^\/dev\/(sd|vd|xvd|nvme|sda|vda)/ {print $1,$2,$3,$4,$5,$6}'"); var output = ShellUtil.Bash(@"df -mT | awk '/^\/dev\/(sd|vd|xvd|nvme|sda|vda|mapper)/ {print $1,$2,$3,$4,$5,$6}'");
var disks = output.Split('\n', StringSplitOptions.RemoveEmptyEntries); var disks = output.Split('\n', StringSplitOptions.RemoveEmptyEntries);
if (disks.Length < 1) return diskInfos; if (disks.Length < 1) return diskInfos;
@ -109,8 +108,7 @@ public static class ComputerUtil
foreach (var item in disks) foreach (var item in disks)
{ {
var disk = item.Split(' ', (char)StringSplitOptions.RemoveEmptyEntries); var disk = item.Split(' ', (char)StringSplitOptions.RemoveEmptyEntries);
if (disk == null || disk.Length < 6) if (disk.Length < 6) continue;
continue;
var diskInfo = new DiskInfo() var diskInfo = new DiskInfo()
{ {
@ -126,8 +124,8 @@ public static class ComputerUtil
} }
else else
{ {
var driv = DriveInfo.GetDrives().Where(u => u.IsReady); var driveList = DriveInfo.GetDrives().Where(u => u.IsReady);
foreach (var item in driv) foreach (var item in driveList)
{ {
if (item.DriveType == DriveType.CDRom) continue; if (item.DriveType == DriveType.CDRom) continue;
var obj = new DiskInfo() var obj = new DiskInfo()
@ -184,7 +182,7 @@ public static class ComputerUtil
} }
else if (IsUnix()) else if (IsUnix())
{ {
string output = ShellUtil.Bash("top -b -n1 | grep \"Cpu(s)\" | awk '{print $2 + $4}'"); string output = ShellUtil.Bash("awk '{u=$2+$4; t=$2+$4+$5; if (NR==1){u1=u; t1=t;} else print ($2+$4-u1) * 100 / (t-t1); }' <(grep 'cpu ' /proc/stat) <(sleep 1;grep 'cpu ' /proc/stat)");
cpuRates.Add(output.Trim()); cpuRates.Add(output.Trim());
} }
else else
@ -217,7 +215,7 @@ public static class ComputerUtil
} }
else if (IsUnix()) else if (IsUnix())
{ {
string output = ShellUtil.Bash("uptime -s").Trim(); string output = ShellUtil.Bash("date -d \"$(awk -F. '{print $1}' /proc/uptime) second ago\" +\"%Y-%m-%d %H:%M:%S\"").Trim();
runTime = DateTimeUtil.FormatTime((DateTime.Now - output.ParseToDateTime()).TotalMilliseconds.ToString().Split('.')[0].ParseToLong()); runTime = DateTimeUtil.FormatTime((DateTime.Now - output.ParseToDateTime()).TotalMilliseconds.ToString().Split('.')[0].ParseToLong());
} }
else else
@ -362,21 +360,14 @@ public class MemoryMetricsClient
/// <returns></returns> /// <returns></returns>
public static MemoryMetrics GetUnixMetrics() public static MemoryMetrics GetUnixMetrics()
{ {
string output = ShellUtil.Bash("free -m | awk '{print $2,$3,$4,$5,$6}'"); string output = ShellUtil.Bash("awk '/MemTotal/ {total=$2} /MemAvailable/ {available=$2} END {print total,available}' /proc/meminfo");
var metrics = new MemoryMetrics(); var metrics = new MemoryMetrics();
var lines = output.Split('\n', (char)StringSplitOptions.RemoveEmptyEntries); var memory = output.Split(' ', (char)StringSplitOptions.RemoveEmptyEntries);
if (lines.Length <= 0) return metrics; if (memory.Length != 2) return metrics;
if (lines != null && lines.Length > 0) metrics.Total = double.Parse(memory[0]) / 1024;
{ metrics.Free = double.Parse(memory[1]) / 1024;
var memory = lines[1].Split(' ', (char)StringSplitOptions.RemoveEmptyEntries); metrics.Used = metrics.Total - metrics.Free;
if (memory.Length >= 3)
{
metrics.Total = double.Parse(memory[0]);
metrics.Used = double.Parse(memory[1]);
metrics.Free = double.Parse(memory[2]);//m
}
}
return metrics; return metrics;
} }

View File

@ -36,10 +36,7 @@ public class DateTimeUtil
/// <returns></returns> /// <returns></returns>
public static DateTime GetBeginTime(DateTime? dateTime, int days = 0) public static DateTime GetBeginTime(DateTime? dateTime, int days = 0)
{ {
if (dateTime == DateTime.MinValue || dateTime == null) return dateTime == DateTime.MinValue || dateTime == null ? DateTime.Now.AddDays(days) : (DateTime)dateTime;
return DateTime.Now.AddDays(days);
return dateTime ?? DateTime.Now;
} }
/// <summary> /// <summary>
@ -90,16 +87,16 @@ public class DateTimeUtil
long hour = (ms - day * dd) / hh; long hour = (ms - day * dd) / hh;
long minute = (ms - day * dd - hour * hh) / mi; long minute = (ms - day * dd - hour * hh) / mi;
long second = (ms - day * dd - hour * hh - minute * mi) / ss; long second = (ms - day * dd - hour * hh - minute * mi) / ss;
long milliSecond = ms - day * dd - hour * hh - minute * mi - second * ss; //long milliSecond = ms - day * dd - hour * hh - minute * mi - second * ss;
string sDay = day < 10 ? "0" + day : "" + day; //天 string sDay = day < 10 ? "0" + day : "" + day; //天
string sHour = hour < 10 ? "0" + hour : "" + hour;//小时 string sHour = hour < 10 ? "0" + hour : "" + hour;//小时
string sMinute = minute < 10 ? "0" + minute : "" + minute;//分钟 string sMinute = minute < 10 ? "0" + minute : "" + minute;//分钟
string sSecond = second < 10 ? "0" + second : "" + second;//秒 string sSecond = second < 10 ? "0" + second : "" + second;//秒
string sMilliSecond = milliSecond < 10 ? "0" + milliSecond : "" + milliSecond;//毫秒 //string sMilliSecond = milliSecond < 10 ? "0" + milliSecond : "" + milliSecond;//毫秒
sMilliSecond = milliSecond < 100 ? "0" + sMilliSecond : "" + sMilliSecond; //sMilliSecond = milliSecond < 100 ? "0" + sMilliSecond : "" + sMilliSecond;
return string.Format("{0} 天 {1} 小时 {2} 分 {3} 秒", sDay, sHour, sMinute, sSecond); return $"{sDay} 天 {sHour} 小时 {sMinute} 分 {sSecond} 秒";
} }
/// <summary> /// <summary>
@ -140,12 +137,7 @@ public class DateTimeUtil
/// <returns></returns> /// <returns></returns>
public static string FormatDateTime(DateTime? dt) public static string FormatDateTime(DateTime? dt)
{ {
if (dt == null) return string.Empty; return dt == null ? string.Empty : dt.Value.ToString(dt.Value.Year == DateTime.Now.Year ? "MM-dd HH:mm" : "yyyy-MM-dd HH:mm");
if (dt.Value.Year == DateTime.Now.Year)
return dt.Value.ToString("MM-dd HH:mm");
else
return dt.Value.ToString("yyyy-MM-dd HH:mm");
} }
/// <summary> /// <summary>
@ -156,8 +148,8 @@ public class DateTimeUtil
{ {
return new List<DateTime> return new List<DateTime>
{ {
Convert.ToDateTime(time.ToString("D").ToString()), Convert.ToDateTime(time.ToString("D")),
Convert.ToDateTime(time.AddDays(1).ToString("D").ToString()).AddSeconds(-1) Convert.ToDateTime(time.AddDays(1).ToString("D")).AddSeconds(-1)
}; };
} }

View File

@ -78,7 +78,7 @@ public class ExcelHelper
/// <param name="filename"></param> /// <param name="filename"></param>
/// <param name="addListValidationFun"></param> /// <param name="addListValidationFun"></param>
/// <returns></returns> /// <returns></returns>
public static IActionResult ExportTemplate<T>(List<T> list, string filename = "导入模板", Func<ExcelWorksheet, PropertyInfo, IEnumerable<string>> addListValidationFun = null) public static IActionResult ExportTemplate<T>(IEnumerable<T> list, string filename = "导入模板", Func<ExcelWorksheet, PropertyInfo, IEnumerable<string>> addListValidationFun = null)
{ {
using var package = new ExcelPackage((ExportData(list, filename) as XlsxFileResult)!.Stream); using var package = new ExcelPackage((ExportData(list, filename) as XlsxFileResult)!.Stream);
var worksheet = package.Workbook.Worksheets[0]; var worksheet = package.Workbook.Worksheets[0];

View File

@ -10,8 +10,8 @@ namespace Admin.NET.Core;
public static class MiniExcelUtil public static class MiniExcelUtil
{ {
private const string _sheetName = "ImportTemplate"; private const string SheetName = "ImportTemplate";
private const string _directory = "export"; private const string DirectoryName = "export";
/// <summary> /// <summary>
/// 导出模板Excel /// 导出模板Excel
@ -23,7 +23,7 @@ public static class MiniExcelUtil
// 在内存中当开辟空间 // 在内存中当开辟空间
var memoryStream = new MemoryStream(); var memoryStream = new MemoryStream();
// 将数据写到内存当中 // 将数据写到内存当中
await memoryStream.SaveAsAsync(values, sheetName: _sheetName); await memoryStream.SaveAsAsync(values, sheetName: SheetName);
// 从0的位置开始写入 // 从0的位置开始写入
memoryStream.Seek(0, SeekOrigin.Begin); memoryStream.Seek(0, SeekOrigin.Begin);
return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
@ -41,7 +41,7 @@ public static class MiniExcelUtil
{ {
using MemoryStream stream = new MemoryStream(); using MemoryStream stream = new MemoryStream();
await file.CopyToAsync(stream); await file.CopyToAsync(stream);
var res = await stream.QueryAsync<T>(sheetName: _sheetName); var res = await stream.QueryAsync<T>(sheetName: SheetName);
return res.ToArray(); return res.ToArray();
} }
@ -54,7 +54,7 @@ public static class MiniExcelUtil
var fileName = string.Format("{0}.xlsx", YitIdHelper.NextId()); var fileName = string.Format("{0}.xlsx", YitIdHelper.NextId());
try try
{ {
var path = Path.Combine(App.WebHostEnvironment.WebRootPath, _directory); var path = Path.Combine(App.WebHostEnvironment.WebRootPath, DirectoryName);
if (!Directory.Exists(path)) if (!Directory.Exists(path))
Directory.CreateDirectory(path); Directory.CreateDirectory(path);
var filePath = Path.Combine(path, fileName); var filePath = Path.Combine(path, fileName);
@ -65,6 +65,6 @@ public static class MiniExcelUtil
throw Oops.Oh("出现错误:" + error); throw Oops.Oh("出现错误:" + error);
} }
var host = CommonUtil.GetLocalhost(); var host = CommonUtil.GetLocalhost();
return $"{host}/{_directory}/{fileName}"; return $"{host}/{DirectoryName}/{fileName}";
} }
} }

View File

@ -22,13 +22,13 @@ public static class TripleDES
[Obsolete] [Obsolete]
public static void EncryptFile(string inputFile, string outputFile, string password) public static void EncryptFile(string inputFile, string outputFile, string password)
{ {
using var tdes = new TripleDESCryptoServiceProvider(); using var ties = new TripleDESCryptoServiceProvider();
tdes.Mode = CipherMode.ECB; ties.Mode = CipherMode.ECB;
tdes.Padding = PaddingMode.PKCS7; ties.Padding = PaddingMode.PKCS7;
tdes.Key = Encoding.UTF8.GetBytes(password); ties.Key = Encoding.UTF8.GetBytes(password);
using var inputFileStream = new FileStream(inputFile, FileMode.Open); using var inputFileStream = new FileStream(inputFile, FileMode.Open);
using var encryptedFileStream = new FileStream(outputFile, FileMode.Create); using var encryptedFileStream = new FileStream(outputFile, FileMode.Create);
using var cryptoStream = new CryptoStream(encryptedFileStream, tdes.CreateEncryptor(), CryptoStreamMode.Write); using var cryptoStream = new CryptoStream(encryptedFileStream, ties.CreateEncryptor(), CryptoStreamMode.Write);
inputFileStream.CopyTo(cryptoStream); inputFileStream.CopyTo(cryptoStream);
} }
@ -41,13 +41,13 @@ public static class TripleDES
[Obsolete] [Obsolete]
public static void DecryptFile(string inputFile, string outputFile, string password) public static void DecryptFile(string inputFile, string outputFile, string password)
{ {
using var tdes = new TripleDESCryptoServiceProvider(); using var ties = new TripleDESCryptoServiceProvider();
tdes.Mode = CipherMode.ECB; ties.Mode = CipherMode.ECB;
tdes.Padding = PaddingMode.PKCS7; ties.Padding = PaddingMode.PKCS7;
tdes.Key = Encoding.UTF8.GetBytes(password); ties.Key = Encoding.UTF8.GetBytes(password);
using var encryptedFileStream = new FileStream(inputFile, FileMode.Open); using var encryptedFileStream = new FileStream(inputFile, FileMode.Open);
using var decryptedFileStream = new FileStream(outputFile, FileMode.Create); using var decryptedFileStream = new FileStream(outputFile, FileMode.Create);
using var cryptoStream = new CryptoStream(encryptedFileStream, tdes.CreateDecryptor(), CryptoStreamMode.Read); using var cryptoStream = new CryptoStream(encryptedFileStream, ties.CreateDecryptor(), CryptoStreamMode.Read);
cryptoStream.CopyTo(decryptedFileStream); cryptoStream.CopyTo(decryptedFileStream);
} }
} }

View File

@ -11,81 +11,81 @@ namespace Admin.NET.Core;
/// </summary> /// </summary>
public static class VerifyFileExtensionName public static class VerifyFileExtensionName
{ {
private static readonly IDictionary<string, string> dics_ext = new Dictionary<string, string>(); private static readonly IDictionary<string, string> DicsExt = new Dictionary<string, string>();
private static readonly IDictionary<string, HashSet<int>> ext_dics = new Dictionary<string, HashSet<int>>(); private static readonly IDictionary<string, HashSet<int>> ExtDics = new Dictionary<string, HashSet<int>>();
static VerifyFileExtensionName() static VerifyFileExtensionName()
{ {
dics_ext.Add("FFD8FFE0", ".jpg"); DicsExt.Add("FFD8FFE0", ".jpg");
dics_ext.Add("FFD8FFE1", ".jpg"); DicsExt.Add("FFD8FFE1", ".jpg");
dics_ext.Add("89504E47", ".png"); DicsExt.Add("89504E47", ".png");
dics_ext.Add("47494638", ".gif"); DicsExt.Add("47494638", ".gif");
dics_ext.Add("49492A00", ".tif"); DicsExt.Add("49492A00", ".tif");
dics_ext.Add("424D", ".bmp"); DicsExt.Add("424D", ".bmp");
// PS和CAD // PS和CAD
dics_ext.Add("38425053", ".psd"); DicsExt.Add("38425053", ".psd");
dics_ext.Add("41433130", ".dwg"); // CAD DicsExt.Add("41433130", ".dwg"); // CAD
dics_ext.Add("252150532D41646F6265", ".ps"); DicsExt.Add("252150532D41646F6265", ".ps");
// 办公文档类 // 办公文档类
dics_ext.Add("D0CF11E0", ".ppt,.doc,.xls"); // ppt、doc、xls DicsExt.Add("D0CF11E0", ".ppt,.doc,.xls"); // ppt、doc、xls
dics_ext.Add("504B0304", ".pptx,.docx,.xlsx"); // pptx、docx、xlsx DicsExt.Add("504B0304", ".pptx,.docx,.xlsx"); // pptx、docx、xlsx
/* 注意由于文本文档录入内容过多,则读取文件头时较为多变-START */ /* 注意由于文本文档录入内容过多,则读取文件头时较为多变-START */
dics_ext.Add("0D0A0D0A", ".txt"); // txt DicsExt.Add("0D0A0D0A", ".txt"); // txt
dics_ext.Add("0D0A2D2D", ".txt"); // txt DicsExt.Add("0D0A2D2D", ".txt"); // txt
dics_ext.Add("0D0AB4B4", ".txt"); // txt DicsExt.Add("0D0AB4B4", ".txt"); // txt
dics_ext.Add("B4B4BDA8", ".txt"); // 文件头部为汉字 DicsExt.Add("B4B4BDA8", ".txt"); // 文件头部为汉字
dics_ext.Add("73646673", ".txt"); // txt,文件头部为英文字母 DicsExt.Add("73646673", ".txt"); // txt,文件头部为英文字母
dics_ext.Add("32323232", ".txt"); // txt,文件头部内容为数字 DicsExt.Add("32323232", ".txt"); // txt,文件头部内容为数字
dics_ext.Add("0D0A09B4", ".txt"); // txt,文件头部内容为数字 DicsExt.Add("0D0A09B4", ".txt"); // txt,文件头部内容为数字
dics_ext.Add("3132330D", ".txt"); // txt,文件头部内容为数字 DicsExt.Add("3132330D", ".txt"); // txt,文件头部内容为数字
/* 注意由于文本文档录入内容过多,则读取文件头时较为多变-END */ /* 注意由于文本文档录入内容过多,则读取文件头时较为多变-END */
dics_ext.Add("7B5C727466", ".rtf"); // 日记本 DicsExt.Add("7B5C727466", ".rtf"); // 日记本
dics_ext.Add("255044462D312E", ".pdf"); DicsExt.Add("255044462D312E", ".pdf");
// 视频或音频类 // 视频或音频类
dics_ext.Add("3026B275", ".wma"); DicsExt.Add("3026B275", ".wma");
dics_ext.Add("57415645", ".wav"); DicsExt.Add("57415645", ".wav");
dics_ext.Add("41564920", ".avi"); DicsExt.Add("41564920", ".avi");
dics_ext.Add("4D546864", ".mid"); DicsExt.Add("4D546864", ".mid");
dics_ext.Add("2E524D46", ".rm"); DicsExt.Add("2E524D46", ".rm");
dics_ext.Add("000001BA", ".mpg"); DicsExt.Add("000001BA", ".mpg");
dics_ext.Add("000001B3", ".mpg"); DicsExt.Add("000001B3", ".mpg");
dics_ext.Add("6D6F6F76", ".mov"); DicsExt.Add("6D6F6F76", ".mov");
dics_ext.Add("3026B2758E66CF11", ".asf"); DicsExt.Add("3026B2758E66CF11", ".asf");
// 压缩包 // 压缩包
dics_ext.Add("52617221", ".rar"); DicsExt.Add("52617221", ".rar");
dics_ext.Add("504B03040A000000", ".zip"); DicsExt.Add("504B03040A000000", ".zip");
dics_ext.Add("504B030414000000", ".zip"); DicsExt.Add("504B030414000000", ".zip");
dics_ext.Add("1F8B08", ".gz"); DicsExt.Add("1F8B08", ".gz");
// 程序文件 // 程序文件
dics_ext.Add("3C3F786D6C", ".xml"); DicsExt.Add("3C3F786D6C", ".xml");
dics_ext.Add("68746D6C3E", ".html"); DicsExt.Add("68746D6C3E", ".html");
dics_ext.Add("04034b50", ".apk"); DicsExt.Add("04034b50", ".apk");
//dics_ext.Add("7061636B", ".java"); //dics_ext.Add("7061636B", ".java");
//dics_ext.Add("3C254020", ".jsp"); //dics_ext.Add("3C254020", ".jsp");
//dics_ext.Add("4D5A9000", ".exe"); //dics_ext.Add("4D5A9000", ".exe");
dics_ext.Add("44656C69766572792D646174653A", ".eml"); // 邮件 DicsExt.Add("44656C69766572792D646174653A", ".eml"); // 邮件
dics_ext.Add("5374616E64617264204A", ".mdb"); // Access数据库文件 DicsExt.Add("5374616E64617264204A", ".mdb"); // Access数据库文件
dics_ext.Add("46726F6D", ".mht"); DicsExt.Add("46726F6D", ".mht");
dics_ext.Add("4D494D45", ".mhtml"); DicsExt.Add("4D494D45", ".mhtml");
foreach (var dics in dics_ext) foreach (var dics in DicsExt)
{ {
foreach (var ext in dics.Value.Split(",")) foreach (var ext in dics.Value.Split(","))
{ {
if (!ext_dics.ContainsKey(ext)) if (!ExtDics.ContainsKey(ext))
ext_dics.Add(ext, new HashSet<int> { dics.Key.Length / 2 }); ExtDics.Add(ext, new HashSet<int> { dics.Key.Length / 2 });
else else
ext_dics[ext].Add(dics.Key.Length / 2); ExtDics[ext].Add(dics.Key.Length / 2);
} }
} }
} }
@ -102,18 +102,17 @@ public static class VerifyFileExtensionName
return false; return false;
suffix = suffix.ToLower(); suffix = suffix.ToLower();
if (!ext_dics.ContainsKey(suffix)) if (!ExtDics.TryGetValue(suffix, out HashSet<int> dic)) return false;
return false;
try try
{ {
foreach (var Len in ext_dics[suffix]) foreach (var len in dic)
{ {
byte[] b = new byte[Len]; byte[] b = new byte[len];
_ = stream.Read(b, 0, b.Length); _ = stream.Read(b, 0, b.Length);
// string fileType = System.Text.Encoding.UTF8.GetString(b); // string fileType = System.Text.Encoding.UTF8.GetString(b);
string fileKey = GetFileHeader(b); string fileKey = GetFileHeader(b);
if (dics_ext.ContainsKey(fileKey)) if (DicsExt.ContainsKey(fileKey))
return true; return true;
} }
} }
@ -152,14 +151,12 @@ public static class VerifyFileExtensionName
if (src == null || src.Length <= 0) if (src == null || src.Length <= 0)
return null; return null;
string hv;
for (int i = 0; i < src.Length; i++) for (int i = 0; i < src.Length; i++)
{ {
// 以十六进制(基数 16无符号整数形式返回一个整数参数的字符串表示形式并转换为大写 // 以十六进制(基数 16无符号整数形式返回一个整数参数的字符串表示形式并转换为大写
hv = Convert.ToString(src[i] & 0xFF, 16).ToUpper(); string hVal = Convert.ToString(src[i] & 0xFF, 16).ToUpper();
if (hv.Length < 2) if (hVal.Length < 2) builder.Append(0);
builder.Append(0); builder.Append(hVal);
builder.Append(hv);
} }
return builder.ToString(); return builder.ToString();
} }

View File

@ -89,15 +89,9 @@ public class XlsxFileResultBase : ActionResult
var response = context.HttpContext.Response; var response = context.HttpContext.Response;
response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
if (downloadFileName == null) downloadFileName ??= Guid.NewGuid().ToString("N") + ".xlsx";
{
downloadFileName = Guid.NewGuid().ToString("N") + ".xlsx";
}
if (string.IsNullOrEmpty(Path.GetExtension(downloadFileName))) if (string.IsNullOrEmpty(Path.GetExtension(downloadFileName))) downloadFileName += ".xlsx";
{
downloadFileName += ".xlsx";
}
context.HttpContext.Response.Headers.Append("Content-Disposition", new[] { "attachment; filename=" + HttpUtility.UrlEncode(downloadFileName) }); context.HttpContext.Response.Headers.Append("Content-Disposition", new[] { "attachment; filename=" + HttpUtility.UrlEncode(downloadFileName) });
await stream.CopyToAsync(context.HttpContext.Response.Body); await stream.CopyToAsync(context.HttpContext.Response.Body);

View File

@ -12,7 +12,7 @@ namespace @Model.NameSpace;
public class @(@Model.ClassName)Dto public class @(@Model.ClassName)Dto
{ {
@foreach (var column in Model.TableField){ @foreach (var column in Model.TableField){
if(@column.EffectType == "fk" && @column.FkEntityName != "" && @column.FkColumnName != ""){ if(@column.EffectType == "ForeignKey" && @column.FkEntityName != "" && @column.FkColumnName != ""){
@:/// <summary> @:/// <summary>
@:/// @column.ColumnComment @:/// @column.ColumnComment
@:/// </summary> @:/// </summary>

View File

@ -3,11 +3,11 @@ import { BasicColumn, FormSchema } from '/@@/components/Table';
@foreach (var column in Model.TableField){ @foreach (var column in Model.TableField){
if(@column.EffectType == "Upload"){ if(@column.EffectType == "Upload"){
@:import { uploadFile } from '/@@/api/sys/admin'; @:import { uploadFile } from '/@@/api/sys/admin';
}else if(@column.EffectType == "fk"){ }else if(@column.EffectType == "ForeignKey"){
@:import { get@(@column.FkEntityName)Dropdown } from '/@@/api/main/@(@Model.ClassName)'; @:import { get@(@column.FkEntityName)Dropdown } from '/@@/api/main/@(@Model.ClassName)';
}else if(@column.EffectType == "Select"){ }else if(@column.EffectType == "DictSelector"){
@:import { getDataList } from '/@@/api/sys/admin'; @:import { getDataList } from '/@@/api/sys/admin';
}else if(@column.EffectType == "ApiTreeSelect"){ }else if(@column.EffectType == "ApiTreeSelector"){
@:import { get@(@column.FkEntityName)Tree } from '/@@/api/main/@(@Model.ClassName)'; @:import { get@(@column.FkEntityName)Tree } from '/@@/api/main/@(@Model.ClassName)';
}else if(@column.EffectType == "ConstSelector"){ }else if(@column.EffectType == "ConstSelector"){
@:import { codeToName, getSelector } from '/@@/utils/helper/constSelectorHelper'; @:import { codeToName, getSelector } from '/@@/utils/helper/constSelectorHelper';
@ -24,7 +24,7 @@ export const columns: BasicColumn[] = [
@:sorter: true, @:sorter: true,
if(@column.EffectType == "Upload"){ if(@column.EffectType == "Upload"){
@:slots: { customRender: '@(@column.LowerPropertyName)' }, @:slots: { customRender: '@(@column.LowerPropertyName)' },
}else if(@column.EffectType == "fk"){ }else if(@column.EffectType == "ForeignKey"){
@:customRender: ({ record }) => { @:customRender: ({ record }) => {
@:return record.fk@(@column.PropertyName).@(@column.LowerFkColumnName); @:return record.fk@(@column.PropertyName).@(@column.LowerFkColumnName);
@:}, @:},
@ -49,14 +49,14 @@ export const searchFormSchema: FormSchema[] = [
@:field: '@column.LowerPropertyName', @:field: '@column.LowerPropertyName',
@:label: '@column.ColumnComment', @:label: '@column.ColumnComment',
@:colProps: { span: 8 }, @:colProps: { span: 8 },
if(@column.EffectType == "fk"){ if(@column.EffectType == "ForeignKey"){
@:component: 'ApiSelect', @:component: 'ApiSelect',
@:componentProps: { @:componentProps: {
@:api: get@(@column.FkEntityName)Dropdown, @:api: get@(@column.FkEntityName)Dropdown,
@:labelField: 'label', @:labelField: 'label',
@:valueField: 'value', @:valueField: 'value',
@:}, @:},
}else if(@column.EffectType == "Select"){ }else if(@column.EffectType == "DictSelector"){
@:component: 'ApiSelect', @:component: 'ApiSelect',
@:componentProps: { @:componentProps: {
@:api: getDataList, @:api: getDataList,
@ -75,7 +75,7 @@ if(@column.EffectType == "fk"){
@:value: 'code', @:value: 'code',
@:}, @:},
@:}, @:},
}else if(@column.EffectType == "ApiTreeSelect"){ }else if(@column.EffectType == "ApiTreeSelector"){
@:component: '@(@column.EffectType)', @:component: '@(@column.EffectType)',
@:componentProps: { @:componentProps: {
@:api: get@(@column.FkEntityName)Tree, @:api: get@(@column.FkEntityName)Tree,
@ -100,14 +100,14 @@ export const formSchema: FormSchema[] = [
@:{ @:{
@:label: '@column.ColumnComment', @:label: '@column.ColumnComment',
@:field: '@column.LowerPropertyName', @:field: '@column.LowerPropertyName',
if(@column.EffectType == "fk"){ if(@column.EffectType == "ForeignKey"){
@:component: 'ApiSelect', @:component: 'ApiSelect',
@:componentProps: { @:componentProps: {
@:api: get@(@column.FkEntityName)Dropdown, @:api: get@(@column.FkEntityName)Dropdown,
@:labelField: 'label', @:labelField: 'label',
@:valueField: 'value', @:valueField: 'value',
@:}, @:},
}else if(@column.EffectType == "Select"){ }else if(@column.EffectType == "DictSelector"){
@:component: 'ApiSelect', @:component: 'ApiSelect',
@:componentProps: { @:componentProps: {
@:api: getDataList, @:api: getDataList,
@ -126,7 +126,7 @@ if(@column.EffectType == "fk"){
@:value: 'code', @:value: 'code',
@:}, @:},
@:}, @:},
}else if(@column.EffectType == "ApiTreeSelect"){ }else if(@column.EffectType == "ApiTreeSelector"){
@:component: '@(@column.EffectType)', @:component: '@(@column.EffectType)',
@:componentProps: { @:componentProps: {
@:api: get@(@column.FkEntityName)Tree, @:api: get@(@column.FkEntityName)Tree,

View File

@ -15,7 +15,7 @@ public class @(@Model.ClassName)Output
@:/// <summary> @:/// <summary>
@:/// @column.ColumnComment @:/// @column.ColumnComment
@:/// </summary> @:/// </summary>
if(column.EffectType == "fk") if(column.EffectType == "ForeignKey")
{ {
@:public @column.NetType @column.PropertyName { get; set; } @:public @column.NetType @column.PropertyName { get; set; }
@: @:
@ -27,7 +27,7 @@ if(column.EffectType == "fk")
}else if(column.EffectType == "Upload"){ }else if(column.EffectType == "Upload"){
@:public @column.NetType @column.PropertyName { get; set; } @:public @column.NetType @column.PropertyName { get; set; }
@:public SysFile @(@column.PropertyName)Attachment { get; set; } @:public SysFile @(@column.PropertyName)Attachment { get; set; }
}else if(column.EffectType == "ApiTreeSelect"){ }else if(column.EffectType == "ApiTreeSelector"){
@:public @column.NetType @column.PropertyName { get; set; } @:public @column.NetType @column.PropertyName { get; set; }
@: @:
@:/// <summary> @:/// <summary>
@ -43,7 +43,7 @@ if(column.EffectType == "fk")
@foreach (var column in Model.TableField){ @foreach (var column in Model.TableField){
if (@column.EffectType == "ApiTreeSelect"){ if (@column.EffectType == "ApiTreeSelector"){
@:// 使用实际实体@(@column.FkTableName),所以这里就删了 @:// 使用实际实体@(@column.FkTableName),所以这里就删了
@:/* @:/*
@:[SugarTable("@(@column.FkTableName)")] @:[SugarTable("@(@column.FkTableName)")]

View File

@ -85,10 +85,10 @@ if (@column.QueryWhether == "Y"){
@if(Model.IsJoinTable){ @if(Model.IsJoinTable){
@://处理外键和TreeSelector相关字段的连接 @://处理外键和TreeSelector相关字段的连接
@foreach (var column in Model.TableField){ @foreach (var column in Model.TableField){
if(@column.EffectType == "fk"){ if(@column.EffectType == "ForeignKey"){
joinTableName += ", " + column.PropertyName.ToLower(); joinTableName += ", " + column.PropertyName.ToLower();
@:.LeftJoin<@(@column.FkEntityName)>((@(@joinTableName)) => u.@(@column.PropertyName) == @(@column.PropertyName.ToLower()).@(@column.FkLinkColumnName) ) @:.LeftJoin<@(@column.FkEntityName)>((@(@joinTableName)) => u.@(@column.PropertyName) == @(@column.PropertyName.ToLower()).@(@column.FkLinkColumnName) )
} else if(@column.EffectType == "ApiTreeSelect"){ } else if(@column.EffectType == "ApiTreeSelector"){
joinTableName += ", " + column.PropertyName.ToLower(); joinTableName += ", " + column.PropertyName.ToLower();
@:.LeftJoin<@(@column.FkEntityName)>((@(@joinTableName)) => u.@(@column.PropertyName) == @(@column.PropertyName.ToLower()).@(@column.ValueColumn) ) @:.LeftJoin<@(@column.FkEntityName)>((@(@joinTableName)) => u.@(@column.PropertyName) == @(@column.PropertyName.ToLower()).@(@column.ValueColumn) )
} }
@ -96,10 +96,10 @@ if (@column.QueryWhether == "Y"){
@:.Select((@(@joinTableName)) => new @(@Model.ClassName)Output @:.Select((@(@joinTableName)) => new @(@Model.ClassName)Output
@:{ @:{
@foreach (var column in Model.TableField){ @foreach (var column in Model.TableField){
if(@column.EffectType == "fk"){ if(@column.EffectType == "ForeignKey"){
@:@(@column.PropertyName) = u.@(@column.PropertyName), @:@(@column.PropertyName) = u.@(@column.PropertyName),
@:@(@column.PropertyName)@(@column.FkColumnName) = @(@column.PropertyName.ToLower()).@(@column.FkColumnName), @:@(@column.PropertyName)@(@column.FkColumnName) = @(@column.PropertyName.ToLower()).@(@column.FkColumnName),
} else if(@column.EffectType == "ApiTreeSelect"){ } else if(@column.EffectType == "ApiTreeSelector"){
@:@(@column.PropertyName) = u.@(@column.PropertyName), @:@(@column.PropertyName) = u.@(@column.PropertyName),
@:@(@column.PropertyName)@(@column.DisplayColumn) = @(@column.PropertyName.ToLower()).@(@column.DisplayColumn), @:@(@column.PropertyName)@(@column.DisplayColumn) = @(@column.PropertyName.ToLower()).@(@column.DisplayColumn),
} else if(@column.NetType?.TrimEnd('?').EndsWith("Enum") == true){ } else if(@column.NetType?.TrimEnd('?').EndsWith("Enum") == true){
@ -110,7 +110,7 @@ if (@column.QueryWhether == "Y"){
} }
@:}); @:});
@foreach (var column in Model.TableField){ @foreach (var column in Model.TableField){
if(@column.EffectType == "fk"){ if(@column.EffectType == "ForeignKey"){
}else if(@column.EffectType == "Upload"){ }else if(@column.EffectType == "Upload"){
@://.Mapper(c => c.@(@column.PropertyName)Attachment, c => c.@(@column.PropertyName)) @://.Mapper(c => c.@(@column.PropertyName)Attachment, c => c.@(@column.PropertyName))
@ -119,7 +119,7 @@ if (@column.QueryWhether == "Y"){
} else { } else {
@:.Select<@(@Model.ClassName)Output>(); @:.Select<@(@Model.ClassName)Output>();
} }
@if(@Model.TableField.Any(x=>x.EffectType == "fk")){ @if(@Model.TableField.Any(x=>x.EffectType == "ForeignKey")){
@:return await query.OrderBuilder(input,"[u].","[@(PKName)]").ToPagedListAsync(input.Page, input.PageSize); @:return await query.OrderBuilder(input,"[u].","[@(PKName)]").ToPagedListAsync(input.Page, input.PageSize);
} else { } else {
@:return await query.OrderBuilder(input).ToPagedListAsync(input.Page, input.PageSize); @:return await query.OrderBuilder(input).ToPagedListAsync(input.Page, input.PageSize);
@ -216,7 +216,7 @@ if (@column.ColumnKey == "True"){
} }
@foreach (var column in Model.TableField){ @foreach (var column in Model.TableField){
if(@column.EffectType == "fk" && (@column.WhetherAddUpdate == "Y" || column.QueryWhether == "Y")){ if(@column.EffectType == "ForeignKey" && (@column.WhetherAddUpdate == "Y" || column.QueryWhether == "Y")){
@:/// <summary> @:/// <summary>
@:/// 获取@(@column.ColumnComment)列表 @:/// 获取@(@column.ColumnComment)列表
@:/// </summary> @:/// </summary>
@ -254,7 +254,7 @@ if(@column.EffectType == "Upload"){
} }
@foreach (var column in Model.TableField){ @foreach (var column in Model.TableField){
if(@column.EffectType == "ApiTreeSelect" && !definedObjects.ContainsKey("@(@column.FkEntityName)Tree")){ if(@column.EffectType == "ApiTreeSelector" && !definedObjects.ContainsKey("@(@column.FkEntityName)Tree")){
@{definedObjects.Add("@(@column.FkEntityName)Tree", 1);} @{definedObjects.Add("@(@column.FkEntityName)Tree", 1);}
@:[ApiDescriptionSettings(Name = "@(LowerFirstLetter(@column.FkEntityName))Tree", Description = "获取@(@column.ColumnComment)列表", Order = 920), HttpGet] @:[ApiDescriptionSettings(Name = "@(LowerFirstLetter(@column.FkEntityName))Tree", Description = "获取@(@column.ColumnComment)列表", Order = 920), HttpGet]
@:[DisplayName("获取@(@column.FkEntityName)Tree")] @:[DisplayName("获取@(@column.FkEntityName)Tree")]

View File

@ -22,9 +22,9 @@ enum Api {
@:Exists@(RemoteField) = '/api/@(@Model.LowerClassName)/exists@(RemoteField)', @:Exists@(RemoteField) = '/api/@(@Model.LowerClassName)/exists@(RemoteField)',
} }
@foreach (var column in Model.TableField){ @foreach (var column in Model.TableField){
if(@column.EffectType == "fk" && (@column.WhetherAddUpdate == "Y" || column.QueryWhether == "Y")){ if(@column.EffectType == "ForeignKey" && (@column.WhetherAddUpdate == "Y" || column.QueryWhether == "Y")){
@:Get@(@column.FkEntityName)@(@column.PropertyName)Dropdown = '/api/@(@Model.LowerClassName)/@(LowerFirstLetter(@column.FkEntityName))@(@column.PropertyName)Dropdown', @:Get@(@column.FkEntityName)@(@column.PropertyName)Dropdown = '/api/@(@Model.LowerClassName)/@(LowerFirstLetter(@column.FkEntityName))@(@column.PropertyName)Dropdown',
}else if(@column.EffectType == "ApiTreeSelect" && !definedObjects.ContainsKey("Get@(@column.FkEntityName)Tree")){ }else if(@column.EffectType == "ApiTreeSelector" && !definedObjects.ContainsKey("Get@(@column.FkEntityName)Tree")){
@{definedObjects.Add("Get@(@column.FkEntityName)Tree", 1);} @{definedObjects.Add("Get@(@column.FkEntityName)Tree", 1);}
@:Get@(@column.FkEntityName)Tree = '/api/@(@Model.LowerClassName)/@(LowerFirstLetter(@column.FkEntityName))Tree', @:Get@(@column.FkEntityName)Tree = '/api/@(@Model.LowerClassName)/@(LowerFirstLetter(@column.FkEntityName))Tree',
}else if(@column.EffectType == "Upload"){ }else if(@column.EffectType == "Upload"){
@ -74,13 +74,13 @@ export const detail@(@Model.ClassName) = (id: any) =>
}); });
@foreach (var column in Model.TableField){ @foreach (var column in Model.TableField){
if(@column.EffectType == "fk" && (@column.WhetherAddUpdate == "Y" || column.QueryWhether == "Y")){ if(@column.EffectType == "ForeignKey" && (@column.WhetherAddUpdate == "Y" || column.QueryWhether == "Y")){
@:export const get@(@column.FkEntityName)@(@column.PropertyName)Dropdown = () => @:export const get@(@column.FkEntityName)@(@column.PropertyName)Dropdown = () =>
@:request({ @:request({
@:url: Api.Get@(@column.FkEntityName)@(@column.PropertyName)Dropdown, @:url: Api.Get@(@column.FkEntityName)@(@column.PropertyName)Dropdown,
@:method: 'get' @:method: 'get'
@:}); @:});
}else if(@column.EffectType == "ApiTreeSelect" && !definedObjects.ContainsKey("get@(@column.FkEntityName)Tree")){ }else if(@column.EffectType == "ApiTreeSelector" && !definedObjects.ContainsKey("get@(@column.FkEntityName)Tree")){
@{definedObjects.Add("get@(@column.FkEntityName)Tree", 1);} @{definedObjects.Add("get@(@column.FkEntityName)Tree", 1);}
@:export const get@(@column.FkEntityName)Tree = () => @:export const get@(@column.FkEntityName)Tree = () =>
@:request({ @:request({

View File

@ -31,7 +31,7 @@
</el-form-item> </el-form-item>
}else{ }else{
if (@column.WhetherAddUpdate == "Y"){ if (@column.WhetherAddUpdate == "Y"){
if(@column.EffectType == "fk"){ if(@column.EffectType == "ForeignKey"){
@:<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20"> @:<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
@:<el-form-item label="@column.ColumnComment" prop="@(@column.LowerPropertyName)"> @:<el-form-item label="@column.ColumnComment" prop="@(@column.LowerPropertyName)">
@:<el-select clearable filterable v-model="state.ruleForm.@(@column.LowerPropertyName)" placeholder="请选择@(@column.ColumnComment)"> @:<el-select clearable filterable v-model="state.ruleForm.@(@column.LowerPropertyName)" placeholder="请选择@(@column.ColumnComment)">
@ -47,7 +47,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
}else if(@column.EffectType == "ApiTreeSelect"){ }else if(@column.EffectType == "ApiTreeSelector"){
@:<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20"> @:<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
@:<el-form-item label="@column.ColumnComment" prop="@(@column.LowerPropertyName)"> @:<el-form-item label="@column.ColumnComment" prop="@(@column.LowerPropertyName)">
<el-cascader <el-cascader
@ -87,7 +87,7 @@
@:show-word-limit clearable /> @:show-word-limit clearable />
</el-form-item> </el-form-item>
</el-col> </el-col>
}else if(@column.EffectType == "Select"){ }else if(@column.EffectType == "DictSelector"){
@:<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20"> @:<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
@:<el-form-item label="@column.ColumnComment" prop="@(@column.LowerPropertyName)"> @:<el-form-item label="@column.ColumnComment" prop="@(@column.LowerPropertyName)">
@:<el-select clearable v-model="state.ruleForm.@(@column.LowerPropertyName)" placeholder="请选择@(@column.ColumnComment)"> @:<el-select clearable v-model="state.ruleForm.@(@column.LowerPropertyName)" placeholder="请选择@(@column.ColumnComment)">
@ -191,7 +191,7 @@ import type { FormRules } from "element-plus";
@if(Model.RemoteVerify){ @if(Model.RemoteVerify){
@:exists@(RemoteField), @:exists@(RemoteField),
} }
@foreach (var column in Model.TableField.Where(x=>x.EffectType == "fk").ToList()){ @foreach (var column in Model.TableField.Where(x=>x.EffectType == "ForeignKey").ToList()){
@:get@(@column.FkEntityName)@(@column.PropertyName)Dropdown, @:get@(@column.FkEntityName)@(@column.PropertyName)Dropdown,
} }
@:add@(@Model.ClassName), @:add@(@Model.ClassName),
@ -207,7 +207,7 @@ if(@Model.TableField.Any(x=>x.EffectType == "Upload")){
@if(@Model.TableField.Any(x=>x.EffectType == "ConstSelector")){ @if(@Model.TableField.Any(x=>x.EffectType == "ConstSelector")){
@:import { getConstType } from "/@@/utils/constHelper"; @:import { getConstType } from "/@@/utils/constHelper";
} }
@if(@Model.TableField.Any(x=>x.EffectType == "Select") || @Model.TableField.Any(x=>x.EffectType == "EnumSelector")){ @if(@Model.TableField.Any(x=>x.EffectType == "DictSelector") || @Model.TableField.Any(x=>x.EffectType == "EnumSelector")){
@:import { getDictDataItem as di, getDictDataList as dl } from '/@@/utils/dict-utils'; @:import { getDictDataItem as di, getDictDataList as dl } from '/@@/utils/dict-utils';
} }
@if(@Model.TableField.Any(x=>x.EffectType == "EnumSelector")){ @if(@Model.TableField.Any(x=>x.EffectType == "EnumSelector")){
@ -217,7 +217,7 @@ if(@Model.TableField.Any(x=>x.EffectType == "Upload")){
@:import { formatDate } from '/@@/utils/formatTime'; @:import { formatDate } from '/@@/utils/formatTime';
} }
@foreach (var column in Model.TableField){ @foreach (var column in Model.TableField){
if(@column.EffectType == "ApiTreeSelect" && !definedObjects.ContainsKey("import__@(@column.FkEntityName)Tree")){ if(@column.EffectType == "ApiTreeSelector" && !definedObjects.ContainsKey("import__@(@column.FkEntityName)Tree")){
@{definedObjects.Add("import__@(@column.FkEntityName)Tree", 1);} @{definedObjects.Add("import__@(@column.FkEntityName)Tree", 1);}
} }
} }
@ -268,7 +268,7 @@ const rules = ref<FormRules>({
@:@column.LowerPropertyName: [ @:@column.LowerPropertyName: [
@foreach(var rule in @column.RuleItems){ @foreach(var rule in @column.RuleItems){
string trigger="blur"; string trigger="blur";
if(@column.EffectType == "DatePicker" || @column.EffectType == "Select" ||@column.EffectType == "ApiTreeSelect"){ if(@column.EffectType == "DatePicker" || @column.EffectType == "DictSelector" ||@column.EffectType == "ApiTreeSelector"){
trigger="change"; trigger="change";
} }
if(rule.Type=="required"){ if(rule.Type=="required"){
@ -389,7 +389,7 @@ const submit = async () => {
}; };
@foreach (var column in Model.TableField){ @foreach (var column in Model.TableField){
if(@column.EffectType == "fk" && @column.WhetherAddUpdate == "Y"){ if(@column.EffectType == "ForeignKey" && @column.WhetherAddUpdate == "Y"){
@://下拉列表@(@column.ColumnComment) @://下拉列表@(@column.ColumnComment)
@:const @LowerFirstLetter(@column.FkEntityName)@(@column.PropertyName)DropdownList = ref<any>([]); @:const @LowerFirstLetter(@column.FkEntityName)@(@column.PropertyName)DropdownList = ref<any>([]);
@:const get@(@column.FkEntityName)@(@column.PropertyName)DropdownList = async () => { @:const get@(@column.FkEntityName)@(@column.PropertyName)DropdownList = async () => {
@ -406,7 +406,7 @@ if(@column.EffectType == "fk" && @column.WhetherAddUpdate == "Y"){
} }
@foreach (var column in Model.TableField){ @foreach (var column in Model.TableField){
if(@column.EffectType == "ApiTreeSelect" && !definedObjects.ContainsKey("define_get@(@column.FkEntityName)TreeData")){ if(@column.EffectType == "ApiTreeSelector" && !definedObjects.ContainsKey("define_get@(@column.FkEntityName)TreeData")){
@{definedObjects.Add("define_get@(@column.FkEntityName)TreeData", 1);} @{definedObjects.Add("define_get@(@column.FkEntityName)TreeData", 1);}
@:const @LowerFirstLetter(@column.FkEntityName)TreeData = ref<any>([]); @:const @LowerFirstLetter(@column.FkEntityName)TreeData = ref<any>([]);
@:const get@(@column.FkEntityName)TreeData = async () => { @:const get@(@column.FkEntityName)TreeData = async () => {

View File

@ -44,7 +44,7 @@
@:<el-input-number v-model="queryParams.@(@column.LowerPropertyName)" clearable="" placeholder="请输入@(@column.ColumnComment)"/> @:<el-input-number v-model="queryParams.@(@column.LowerPropertyName)" clearable="" placeholder="请输入@(@column.ColumnComment)"/>
@: @:
</el-form-item> </el-form-item>
}else if(@column.EffectType == "fk"){ }else if(@column.EffectType == "ForeignKey"){
@:<el-form-item label="@column.ColumnComment"> @:<el-form-item label="@column.ColumnComment">
@:<el-select clearable="" filterable="" v-model="queryParams.@(@column.LowerPropertyName)" placeholder="请选择@(@column.ColumnComment)"> @:<el-select clearable="" filterable="" v-model="queryParams.@(@column.LowerPropertyName)" placeholder="请选择@(@column.ColumnComment)">
@:<el-option v-for="(item,index) in @LowerFirstLetter(@column.FkEntityName)@(@column.PropertyName)DropdownList" :key="index" :value="item.value" :label="item.label" /> @:<el-option v-for="(item,index) in @LowerFirstLetter(@column.FkEntityName)@(@column.PropertyName)DropdownList" :key="index" :value="item.value" :label="item.label" />
@ -52,7 +52,7 @@
</el-select> </el-select>
@: @:
</el-form-item> </el-form-item>
}else if(@column.EffectType == "Select"){ }else if(@column.EffectType == "DictSelector"){
@:<el-form-item label="@column.ColumnComment"> @:<el-form-item label="@column.ColumnComment">
@:<el-select clearable="" v-model="queryParams.@(@column.LowerPropertyName)" placeholder="请选择@(@column.ColumnComment)"> @:<el-select clearable="" v-model="queryParams.@(@column.LowerPropertyName)" placeholder="请选择@(@column.ColumnComment)">
@:<el-option v-for="(item,index) in dl('@(@column.DictTypeCode)')" :key="index" :value="item.code" :label="`[${item.code}] ${item.value}`" /> @:<el-option v-for="(item,index) in dl('@(@column.DictTypeCode)')" :key="index" :value="item.code" :label="`[${item.code}] ${item.value}`" />
@ -126,7 +126,7 @@
<el-table-column type="index" label="序号" width="55" align="center"/> <el-table-column type="index" label="序号" width="55" align="center"/>
@foreach (var column in Model.TableField){ @foreach (var column in Model.TableField){
if(@column.WhetherTable == "Y"){ if(@column.WhetherTable == "Y"){
if(@column.EffectType == "Upload"||@column.EffectType == "fk"||@column.EffectType == "ApiTreeSelect"||@column.EffectType == "Switch"||@column.EffectType == "ConstSelector"){ if(@column.EffectType == "Upload"||@column.EffectType == "ForeignKey"||@column.EffectType == "ApiTreeSelector"||@column.EffectType == "Switch"||@column.EffectType == "ConstSelector"){
@:<el-table-column prop="@column.LowerPropertyName" label="@column.ColumnComment" @(column.WhetherSortable == "Y" ? "sortable='custom'" : "") show-overflow-tooltip=""> @:<el-table-column prop="@column.LowerPropertyName" label="@column.ColumnComment" @(column.WhetherSortable == "Y" ? "sortable='custom'" : "") show-overflow-tooltip="">
@:<template #default="scope"> @:<template #default="scope">
if(@column.EffectType == "Upload"){ if(@column.EffectType == "Upload"){
@ -140,9 +140,9 @@
@::initial-index="0" @::initial-index="0"
@:fit="scale-down" @:fit="scale-down"
@:preview-teleported=""/> @:preview-teleported=""/>
}else if(@column.EffectType == "fk"){ }else if(@column.EffectType == "ForeignKey"){
@:<span>{{scope.row.@LowerFirstLetter(@column.PropertyName)@(@column.FkColumnName)}}</span> @:<span>{{scope.row.@LowerFirstLetter(@column.PropertyName)@(@column.FkColumnName)}}</span>
}else if(@column.EffectType == "ApiTreeSelect"){ }else if(@column.EffectType == "ApiTreeSelector"){
@:<span>{{scope.row.@LowerFirstLetter(@column.PropertyName)@(column.DisplayColumn)}}</span> @:<span>{{scope.row.@LowerFirstLetter(@column.PropertyName)@(column.DisplayColumn)}}</span>
}else if(@column.EffectType == "Switch"){ }else if(@column.EffectType == "Switch"){
@:<el-tag v-if="scope.row.@(@column.LowerPropertyName)"> 是 </el-tag> @:<el-tag v-if="scope.row.@(@column.LowerPropertyName)"> 是 </el-tag>
@ -155,7 +155,7 @@
@: @:
</el-table-column> </el-table-column>
} }
else if(@column.EffectType == "Select"){ else if(@column.EffectType == "DictSelector"){
@:<el-table-column prop="@column.LowerPropertyName" label="@column.ColumnComment" @(column.WhetherSortable == "Y" ? "sortable='custom'" : "") show-overflow-tooltip="" > @:<el-table-column prop="@column.LowerPropertyName" label="@column.ColumnComment" @(column.WhetherSortable == "Y" ? "sortable='custom'" : "") show-overflow-tooltip="" >
@:<template #default="scope"> @:<template #default="scope">
@:<el-tag :type="di('@(@column.DictTypeCode)', scope.row.@(@column.LowerPropertyName))?.tagType"> {{di("@(@column.DictTypeCode)", scope.row.@(@column.LowerPropertyName))?.value}} </el-tag> @:<el-tag :type="di('@(@column.DictTypeCode)', scope.row.@(@column.LowerPropertyName))?.tagType"> {{di("@(@column.DictTypeCode)", scope.row.@(@column.LowerPropertyName))?.value}} </el-tag>
@ -221,7 +221,7 @@
@if(@Model.TableField.Any(x=>x.EffectType == "ConstSelector")){ @if(@Model.TableField.Any(x=>x.EffectType == "ConstSelector")){
@:import { codeToName, getConstType } from "/@@/utils/constHelper"; @:import { codeToName, getConstType } from "/@@/utils/constHelper";
} }
@if(@Model.TableField.Any(x=>x.EffectType == "Select") || @Model.TableField.Any(x=>x.EffectType == "EnumSelector")){ @if(@Model.TableField.Any(x=>x.EffectType == "DictSelector") || @Model.TableField.Any(x=>x.EffectType == "EnumSelector")){
@:import { getDictDataItem as di, getDictDataList as dl } from '/@@/utils/dict-utils'; @:import { getDictDataItem as di, getDictDataList as dl } from '/@@/utils/dict-utils';
} }
@if(@Model.TableField.Any(x=>x.EffectType == "EnumSelector")){ @if(@Model.TableField.Any(x=>x.EffectType == "EnumSelector")){
@ -245,7 +245,7 @@
import editDialog from '/@@/views/@(@Model.PagePath)/@(@Model.LowerClassName)/component/editDialog.vue' import editDialog from '/@@/views/@(@Model.PagePath)/@(@Model.LowerClassName)/component/editDialog.vue'
import { page@(@Model.ClassName), delete@(@Model.ClassName) } from '/@@/api/@(@Model.PagePath)/@(@Model.LowerClassName)'; import { page@(@Model.ClassName), delete@(@Model.ClassName) } from '/@@/api/@(@Model.PagePath)/@(@Model.LowerClassName)';
@foreach (var column in Model.QueryWhetherList){ @foreach (var column in Model.QueryWhetherList){
if(@column.EffectType == "fk"){ if(@column.EffectType == "ForeignKey"){
@:import { get@(@column.FkEntityName)@(@column.PropertyName)Dropdown } from '/@@/api/@(@Model.PagePath)/@(@Model.LowerClassName)'; @:import { get@(@column.FkEntityName)@(@column.PropertyName)Dropdown } from '/@@/api/@(@Model.PagePath)/@(@Model.LowerClassName)';
} }
} }
@ -372,7 +372,7 @@
}; };
@foreach (var column in Model.QueryWhetherList){ @foreach (var column in Model.QueryWhetherList){
if(@column.EffectType == "fk"){ if(@column.EffectType == "ForeignKey"){
@:const @LowerFirstLetter(@column.FkEntityName)@(@column.PropertyName)DropdownList = ref<any>([]); @:const @LowerFirstLetter(@column.FkEntityName)@(@column.PropertyName)DropdownList = ref<any>([]);
@:const get@(@column.FkEntityName)@(@column.PropertyName)DropdownList = async () => { @:const get@(@column.FkEntityName)@(@column.PropertyName)DropdownList = async () => {
@:let list = await get@(@column.FkEntityName)@(@column.PropertyName)Dropdown(); @:let list = await get@(@column.FkEntityName)@(@column.PropertyName)Dropdown();

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