😎同步开源代码仓库
This commit is contained in:
parent
f75abc9d4a
commit
369bb601b5
@ -47,7 +47,7 @@
|
||||
<PackageReference Include="SSH.NET" Version="2024.2.0" />
|
||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.4.9" />
|
||||
<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="Yitter.IdGenerator" Version="1.0.14" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -44,12 +44,12 @@ public class DictAttribute : ValidationAttribute, ITransient
|
||||
var dictDataList = sysDictDataServiceProvider.GetDataList(DictTypeCode).Result;
|
||||
|
||||
// 使用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}】!");
|
||||
else
|
||||
return ValidationResult.Success;
|
||||
return ValidationResult.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -11,6 +11,11 @@ namespace Admin.NET.Core;
|
||||
/// </summary>
|
||||
public class ClaimConst
|
||||
{
|
||||
/// <summary>
|
||||
/// 应用Id
|
||||
/// </summary>
|
||||
public const string AppId = "AppId";
|
||||
|
||||
/// <summary>
|
||||
/// 用户Id
|
||||
/// </summary>
|
||||
|
||||
@ -71,6 +71,11 @@ public class ConfigConst
|
||||
/// </summary>
|
||||
public const string SysDomainLogin = "sys_domain_login";
|
||||
|
||||
/// <summary>
|
||||
/// 租户域名隔离登录验证
|
||||
/// </summary>
|
||||
public const string SysTenantHostLogin = "sys_tenant_host_login";
|
||||
|
||||
/// <summary>
|
||||
/// 数据校验日志
|
||||
/// </summary>
|
||||
|
||||
@ -26,8 +26,18 @@ public class SqlSugarConst
|
||||
/// </summary>
|
||||
public const string PrimaryKey = "Id";
|
||||
|
||||
/// <summary>
|
||||
/// 默认应用Id
|
||||
/// </summary>
|
||||
public const long DefaultAppId = 1300000000001;
|
||||
|
||||
/// <summary>
|
||||
/// 默认租户Id
|
||||
/// </summary>
|
||||
public const long DefaultTenantId = 1300000000001;
|
||||
|
||||
/// <summary>
|
||||
/// 默认租户域名
|
||||
/// </summary>
|
||||
public const string DefaultTenantHost = "gitee.com";
|
||||
}
|
||||
82
Admin.NET/Admin.NET.Core/Entity/SysApp.cs
Normal file
82
Admin.NET/Admin.NET.Core/Entity/SysApp.cs
Normal 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; }
|
||||
}
|
||||
41
Admin.NET/Admin.NET.Core/Entity/SysAppMenu.cs
Normal file
41
Admin.NET/Admin.NET.Core/Entity/SysAppMenu.cs
Normal 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; }
|
||||
}
|
||||
@ -85,6 +85,13 @@ public partial class SysCodeGen : EntityBase
|
||||
[MaxLength(128)]
|
||||
public string? BusName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 表唯一字段配置
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "表唯一字段配置", Length = 128)]
|
||||
[MaxLength(128)]
|
||||
public string? TableUniqueConfig { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否生成菜单
|
||||
/// </summary>
|
||||
@ -134,4 +141,10 @@ public partial class SysCodeGen : EntityBase
|
||||
/// </summary>
|
||||
[Navigate(NavigateType.OneToMany, nameof(SysCodeGenTemplateRelation.CodeGenId))]
|
||||
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);
|
||||
}
|
||||
@ -130,5 +130,5 @@ public partial class SysMenu : EntityBase
|
||||
/// 菜单子项
|
||||
/// </summary>
|
||||
[SugarColumn(IsIgnore = true)]
|
||||
public List<SysMenu> Children { get; set; } = new List<SysMenu>();
|
||||
public List<SysMenu> Children { get; set; } = new ();
|
||||
}
|
||||
@ -13,6 +13,20 @@ namespace Admin.NET.Core;
|
||||
[SysTable]
|
||||
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>
|
||||
/// 用户Id
|
||||
/// </summary>
|
||||
@ -26,9 +40,9 @@ public partial class SysTenant : EntityBase
|
||||
public long OrgId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 主机
|
||||
/// 域名
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "主机", Length = 128)]
|
||||
[SugarColumn(ColumnDescription = "域名", Length = 128)]
|
||||
[MaxLength(128)]
|
||||
public string? Host { get; set; }
|
||||
|
||||
|
||||
@ -30,6 +30,12 @@ public class SysUserLdap : EntityTenant
|
||||
[Required]
|
||||
public string Account { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 域用户名
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "域用户名", Length = 32)]
|
||||
public string UserName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 对应EmployeeId(用于数据导入对照)
|
||||
/// </summary>
|
||||
@ -41,4 +47,34 @@ public class SysUserLdap : EntityTenant
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "组织代码", Length = 64)]
|
||||
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; }
|
||||
}
|
||||
@ -15,7 +15,7 @@ public enum CultureLevelEnum
|
||||
/// <summary>
|
||||
/// 其他
|
||||
/// </summary>
|
||||
[Description("其他")]
|
||||
[Description("其他"), Theme("info")]
|
||||
Level0 = 0,
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -15,7 +15,7 @@ public enum DataOpTypeEnum
|
||||
/// <summary>
|
||||
/// 其它
|
||||
/// </summary>
|
||||
[Description("其它")]
|
||||
[Description("其它"), Theme("info")]
|
||||
Other,
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -223,6 +223,12 @@ public enum ErrorCodeEnum
|
||||
[ErrorCodeItemMetadata("开放接口绑定租户禁止删除")]
|
||||
D1031,
|
||||
|
||||
/// <summary>
|
||||
/// 手机号已存在
|
||||
/// </summary>
|
||||
[ErrorCodeItemMetadata("手机号已存在")]
|
||||
D1032,
|
||||
|
||||
/// <summary>
|
||||
/// 父机构不存在
|
||||
/// </summary>
|
||||
@ -319,6 +325,18 @@ public enum ErrorCodeEnum
|
||||
[ErrorCodeItemMetadata("字典状态错误")]
|
||||
D3005,
|
||||
|
||||
/// <summary>
|
||||
/// 字典编码不能以Enum结尾
|
||||
/// </summary>
|
||||
[ErrorCodeItemMetadata("字典编码不能以Enum结尾")]
|
||||
D3006,
|
||||
|
||||
/// <summary>
|
||||
/// 禁止修改枚举类型的字典编码
|
||||
/// </summary>
|
||||
[ErrorCodeItemMetadata("禁止修改枚举类型的字典编码")]
|
||||
D3007,
|
||||
|
||||
/// <summary>
|
||||
/// 菜单已存在
|
||||
/// </summary>
|
||||
@ -535,6 +553,12 @@ public enum ErrorCodeEnum
|
||||
[ErrorCodeItemMetadata("租户从库配置错误")]
|
||||
D1302,
|
||||
|
||||
/// <summary>
|
||||
/// 已存在同名的租户域名
|
||||
/// </summary>
|
||||
[ErrorCodeItemMetadata("已存在同名的租户域名")]
|
||||
D1303,
|
||||
|
||||
/// <summary>
|
||||
/// 该表代码模板已经生成过
|
||||
/// </summary>
|
||||
@ -631,6 +655,24 @@ public enum ErrorCodeEnum
|
||||
[ErrorCodeItemMetadata("已存在同名功能或同名程序及插件")]
|
||||
D1900,
|
||||
|
||||
/// <summary>
|
||||
/// 禁止删除存在关联租户的应用
|
||||
/// </summary>
|
||||
[ErrorCodeItemMetadata("禁止删除存在关联租户的应用")]
|
||||
A1001,
|
||||
|
||||
/// <summary>
|
||||
/// 禁止删除存在关联菜单的应用
|
||||
/// </summary>
|
||||
[ErrorCodeItemMetadata("禁止删除存在关联菜单的应用")]
|
||||
A1002,
|
||||
|
||||
/// <summary>
|
||||
/// 找不到系统应用
|
||||
/// </summary>
|
||||
[ErrorCodeItemMetadata("找不到系统应用")]
|
||||
A1000,
|
||||
|
||||
/// <summary>
|
||||
/// 已存在同名或同编码项目
|
||||
/// </summary>
|
||||
|
||||
@ -15,12 +15,12 @@ public enum FinishStatusEnum
|
||||
/// <summary>
|
||||
/// 已完成
|
||||
/// </summary>
|
||||
[Description("已完成")]
|
||||
[Description("已完成"), Theme("success")]
|
||||
Finish = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 未完成
|
||||
/// </summary>
|
||||
[Description("未完成")]
|
||||
[Description("未完成"), Theme("danger")]
|
||||
UnFinish = 0,
|
||||
}
|
||||
@ -15,7 +15,7 @@ public enum GenderEnum
|
||||
/// <summary>
|
||||
/// 未知的性别
|
||||
/// </summary>
|
||||
[Description("未知的性别")]
|
||||
[Description("未知的性别"), Theme("info")]
|
||||
Unknown = 0,
|
||||
|
||||
/// <summary>
|
||||
@ -33,6 +33,6 @@ public enum GenderEnum
|
||||
/// <summary>
|
||||
/// 未说明的性别
|
||||
/// </summary>
|
||||
[Description("未说明的性别")]
|
||||
[Description("未说明的性别"), Theme("info")]
|
||||
Unspecified = 9
|
||||
}
|
||||
@ -15,7 +15,7 @@ public enum JobCreateTypeEnum
|
||||
/// <summary>
|
||||
/// 内置
|
||||
/// </summary>
|
||||
[Description("内置")]
|
||||
[Description("内置"), Theme("info")]
|
||||
BuiltIn = 0,
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -15,7 +15,7 @@ public enum MenuTypeEnum
|
||||
/// <summary>
|
||||
/// 目录
|
||||
/// </summary>
|
||||
[Description("目录")]
|
||||
[Description("目录"), Theme("warning")]
|
||||
Dir = 1,
|
||||
|
||||
/// <summary>
|
||||
@ -27,6 +27,6 @@ public enum MenuTypeEnum
|
||||
/// <summary>
|
||||
/// 按钮
|
||||
/// </summary>
|
||||
[Description("按钮")]
|
||||
[Description("按钮"), Theme("info")]
|
||||
Btn = 3
|
||||
}
|
||||
@ -15,24 +15,24 @@ public enum MessageTypeEnum
|
||||
/// <summary>
|
||||
/// 普通信息
|
||||
/// </summary>
|
||||
[Description("消息")]
|
||||
[Description("消息"), Theme("info")]
|
||||
Info = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 成功提示
|
||||
/// </summary>
|
||||
[Description("成功")]
|
||||
[Description("成功"), Theme("success")]
|
||||
Success = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 警告提示
|
||||
/// </summary>
|
||||
[Description("警告")]
|
||||
[Description("警告"), Theme("warning")]
|
||||
Warning = 2,
|
||||
|
||||
/// <summary>
|
||||
/// 错误提示
|
||||
/// </summary>
|
||||
[Description("错误")]
|
||||
[Description("错误"), Theme("danger")]
|
||||
Error = 3
|
||||
}
|
||||
@ -15,7 +15,7 @@ public enum NoticeStatusEnum
|
||||
/// <summary>
|
||||
/// 草稿
|
||||
/// </summary>
|
||||
[Description("草稿")]
|
||||
[Description("草稿"), Theme("info")]
|
||||
DRAFT = 0,
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -21,6 +21,6 @@ public enum NoticeUserStatusEnum
|
||||
/// <summary>
|
||||
/// 已读
|
||||
/// </summary>
|
||||
[Description("已读")]
|
||||
[Description("已读"), Theme("info")]
|
||||
READ = 1
|
||||
}
|
||||
@ -15,12 +15,12 @@ public enum StatusEnum
|
||||
/// <summary>
|
||||
/// 启用
|
||||
/// </summary>
|
||||
[Description("启用")]
|
||||
[Description("启用"), Theme("success")]
|
||||
Enable = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 停用
|
||||
/// </summary>
|
||||
[Description("停用")]
|
||||
[Description("停用"), Theme("danger")]
|
||||
Disable = 2,
|
||||
}
|
||||
@ -9,6 +9,7 @@ namespace Admin.NET.Core;
|
||||
/// <summary>
|
||||
/// 事件类型-系统用户操作枚举
|
||||
/// </summary>
|
||||
[SuppressSniffer]
|
||||
[Description("事件类型-系统用户操作枚举")]
|
||||
public enum SysUserEventTypeEnum
|
||||
{
|
||||
|
||||
@ -15,12 +15,12 @@ public enum YesNoEnum
|
||||
/// <summary>
|
||||
/// 是
|
||||
/// </summary>
|
||||
[Description("是")]
|
||||
[Description("是"), Theme("success")]
|
||||
Y = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 否
|
||||
/// </summary>
|
||||
[Description("否")]
|
||||
[Description("否"), Theme("danger")]
|
||||
N = 2
|
||||
}
|
||||
@ -23,4 +23,26 @@ public static class ListExtensions
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -155,7 +155,7 @@ public class OnlineUserHub : Hub<IOnlineUserHub>
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <returns></returns>
|
||||
public async Task ClientsSendMessagetoAll(MessageInput message)
|
||||
public async Task ClientsSendMessageToAll(MessageInput message)
|
||||
{
|
||||
await _sysMessageService.SendAllUser(message);
|
||||
}
|
||||
@ -165,7 +165,7 @@ public class OnlineUserHub : Hub<IOnlineUserHub>
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <returns></returns>
|
||||
public async Task ClientsSendMessagetoOther(MessageInput message)
|
||||
public async Task ClientsSendMessageToOther(MessageInput message)
|
||||
{
|
||||
await _sysMessageService.SendOtherUser(message);
|
||||
}
|
||||
@ -175,7 +175,7 @@ public class OnlineUserHub : Hub<IOnlineUserHub>
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <returns></returns>
|
||||
public async Task ClientsSendMessagetoUsers(MessageInput message)
|
||||
public async Task ClientsSendMessageToUsers(MessageInput message)
|
||||
{
|
||||
await _sysMessageService.SendUsers(message);
|
||||
}
|
||||
|
||||
@ -14,8 +14,8 @@ namespace Admin.NET.Core;
|
||||
public class EnumToDictJob : IJob
|
||||
{
|
||||
private readonly IServiceScopeFactory _scopeFactory;
|
||||
private const string DefaultTagType = null;
|
||||
private const int OrderOffset = 10;
|
||||
private const string DefaultTagType = "info";
|
||||
|
||||
public EnumToDictJob(IServiceScopeFactory scopeFactory)
|
||||
{
|
||||
@ -24,48 +24,46 @@ public class EnumToDictJob : IJob
|
||||
|
||||
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();
|
||||
var sysEnumService = serviceScope.ServiceProvider.GetRequiredService<SysEnumService>();
|
||||
// 获取数据库连接
|
||||
var db = serviceScope.ServiceProvider.GetRequiredService<ISqlSugarClient>().CopyNew();
|
||||
|
||||
// 获取枚举类型列表
|
||||
var enumTypeList = 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 sysEnumService = serviceScope.ServiceProvider.GetRequiredService<SysEnumService>();
|
||||
var sysDictTypeList = GetDictByEnumType(sysEnumService.GetEnumTypeList());
|
||||
|
||||
// 新增的枚举转换字典
|
||||
var newEnumType = enumTypeList.Where(u => !updatedEnumCodes.Contains(u.TypeName)).ToList();
|
||||
var (newDictTypes, newDictDatas) = GetNewSysDicts(newEnumType);
|
||||
// 校验枚举类命名规范,字典相关功能中需要通过后缀判断是否为枚举类型
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
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
|
||||
{
|
||||
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)
|
||||
await db.Updateable(updatedDictTypes).ExecuteCommandAsync(stoppingToken);
|
||||
Console.WriteLine($"【{DateTime.Now}】系统枚举类转字典类型数据: 插入{storageable1.InsertList.Count}条, 更新{storageable1.UpdateList.Count}条, 共{storageable1.TotalList.Count}条。");
|
||||
|
||||
if (updatedDictDatas.Count > 0)
|
||||
await db.Updateable(updatedDictDatas).ExecuteCommandAsync(stoppingToken);
|
||||
var storageable2 = await db.Storageable(sysDictTypeList.SelectMany(u => u.Children).ToList())
|
||||
.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)
|
||||
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);
|
||||
Console.WriteLine($"【{DateTime.Now}】系统枚举项转字典值数据: 插入{storageable2.InsertList.Count}条, 更新{storageable2.UpdateList.Count}条, 共{storageable2.TotalList.Count}条。");
|
||||
|
||||
await db.CommitTranAsync();
|
||||
}
|
||||
@ -75,131 +73,63 @@ public class EnumToDictJob : IJob
|
||||
Log.Error($"系统枚举转换字典操作错误:{error.Message}\n堆栈跟踪:{error.StackTrace}", error);
|
||||
throw;
|
||||
}
|
||||
var originColor = Console.ForegroundColor;
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
Console.WriteLine($"【{DateTime.Now}】系统枚举转换字典");
|
||||
Console.ForegroundColor = originColor;
|
||||
finally
|
||||
{
|
||||
Console.ForegroundColor = originColor;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取需要新增的字典列表
|
||||
/// 用于同步枚举转字典旧数据(后期可删除)
|
||||
/// </summary>
|
||||
/// <param name="addEnumType"></param>
|
||||
/// <returns>
|
||||
/// 一个元组,包含以下元素:
|
||||
/// <list type="table">
|
||||
/// <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)
|
||||
/// <param name="db"></param>
|
||||
/// <param name="list"></param>
|
||||
//[Obsolete]
|
||||
private static async Task SyncEnumToDictInfoAsync(SqlSugarClient db, List<SysDictType> list)
|
||||
{
|
||||
var newDictType = new List<SysDictType>();
|
||||
var newDictData = new List<SysDictData>();
|
||||
if (addEnumType.Count <= 0)
|
||||
return (newDictType, newDictData);
|
||||
|
||||
// 新增字典类型
|
||||
newDictType = addEnumType.Select(u => new SysDictType
|
||||
var codeList = list.Select(u => u.Code).ToList();
|
||||
foreach (var dbDictType in await db.Queryable<SysDictType>().Where(x => codeList.Contains(x.Code)).ToListAsync() ?? new())
|
||||
{
|
||||
Id = YitIdHelper.NextId(),
|
||||
Code = u.TypeName,
|
||||
Name = u.TypeDescribe,
|
||||
Remark = u.TypeRemark,
|
||||
Status = StatusEnum.Enable
|
||||
}).ToList();
|
||||
var enumDictType = list.First(u => u.Code == dbDictType.Code);
|
||||
if (enumDictType.Id == dbDictType.Id) continue;
|
||||
|
||||
// 新增字典数据
|
||||
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(),
|
||||
DictTypeId = t2.Id,
|
||||
Name = u.Describe,
|
||||
Value = u.Value.ToString(),
|
||||
Code = u.Name,
|
||||
Remark = t2.Remark,
|
||||
Id = 900000000000 + CommonUtil.GetFixedHashCode(type.TypeName),
|
||||
Code = type.TypeName,
|
||||
Name = type.TypeDescribe,
|
||||
Remark = type.TypeRemark
|
||||
};
|
||||
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,
|
||||
TagType = u.Theme != "" ? u.Theme : DefaultTagType,
|
||||
}).ToList()
|
||||
}).SelectMany(x => x.Data).ToList();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 删除的情况暂不处理
|
||||
}).ToList();
|
||||
list.Add(dictType);
|
||||
}
|
||||
|
||||
return (updatedSysDictTypes, updatedSysDictData, newSysDictData);
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@ -50,6 +50,11 @@ public sealed class DbConnectionConfig : ConnectionConfig
|
||||
/// 种子配置
|
||||
/// </summary>
|
||||
public SeedSettings SeedSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 隔离方式
|
||||
/// </summary>
|
||||
public TenantTypeEnum TenantType { get; set; } = TenantTypeEnum.Id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
23
Admin.NET/Admin.NET.Core/SeedData/SysAppMenuSeedData.cs
Normal file
23
Admin.NET/Admin.NET.Core/SeedData/SysAppMenuSeedData.cs
Normal 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 });
|
||||
}
|
||||
}
|
||||
25
Admin.NET/Admin.NET.Core/SeedData/SysAppSeedData.cs
Normal file
25
Admin.NET/Admin.NET.Core/SeedData/SysAppSeedData.cs
Normal 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") },
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -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=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=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=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=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=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.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.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)获取
|
||||
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="开启密码强度验证", 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=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=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=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=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") },
|
||||
|
||||
@ -20,16 +20,16 @@ public class SysDictDataSeedData : ISqlSugarEntitySeedData<SysDictData>
|
||||
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=1300000000102, DictTypeId=1300000000101, Value="外键", Code="fk", 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=1300000000104, DictTypeId=1300000000101, Value="选择器", Code="Select", 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=1300000000106, DictTypeId=1300000000101, Value="文本域", Code="InputTextArea", 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=1300000000108, DictTypeId=1300000000101, Value="树选择", Code="ApiTreeSelect", 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=1300000000110, DictTypeId=1300000000101, Value="常量选择器", Code="ConstSelector", 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=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="ConstSelector", 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="ApiTreeSelector", 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="InputNumber", 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="InputTextArea", 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="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=1300000000202, DictTypeId=1300000000102, Value="模糊", Code="like", OrderNo=1, Remark="模糊", Status=StatusEnum.Enable, CreateTime=DateTime.Parse("2022-02-10 00:00:00") },
|
||||
|
||||
@ -21,7 +21,7 @@ public class SysTenantSeedData : ISqlSugarEntitySeedData<SysTenant>
|
||||
|
||||
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") },
|
||||
};
|
||||
}
|
||||
}
|
||||
173
Admin.NET/Admin.NET.Core/Service/Application/Dto/SysAppInput.cs
Normal file
173
Admin.NET/Admin.NET.Core/Service/Application/Dto/SysAppInput.cs
Normal 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; }
|
||||
}
|
||||
103
Admin.NET/Admin.NET.Core/Service/Application/Dto/SysAppOutput.cs
Normal file
103
Admin.NET/Admin.NET.Core/Service/Application/Dto/SysAppOutput.cs
Normal 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; }
|
||||
}
|
||||
183
Admin.NET/Admin.NET.Core/Service/Application/SysAppService.cs
Normal file
183
Admin.NET/Admin.NET.Core/Service/Application/SysAppService.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
@ -25,6 +25,12 @@ public class LoginInput
|
||||
[Required(ErrorMessage = "密码不能为空"), MinLength(3, ErrorMessage = "密码不能少于3个字符")]
|
||||
public string Password { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 租户域名
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "租户域名不能为空")]
|
||||
public string Host { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 验证码Id
|
||||
/// </summary>
|
||||
@ -58,6 +64,12 @@ public class LoginPhoneInput
|
||||
[Required(ErrorMessage = "验证码不能为空"), MinLength(4, ErrorMessage = "验证码不能少于4个字符")]
|
||||
public string Code { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 租户域名
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "租户域名不能为空")]
|
||||
public string? Host { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 登录模式
|
||||
/// </summary>
|
||||
|
||||
@ -9,15 +9,10 @@ namespace Admin.NET.Core.Service;
|
||||
/// <summary>
|
||||
/// 系统域登录信息配置输入参数
|
||||
/// </summary>
|
||||
public class PageSysLdapInput : BasePageInput
|
||||
public class SysLdapInput : BasePageInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 关键字查询
|
||||
/// </summary>
|
||||
public string? SearchKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 主机
|
||||
/// 域名
|
||||
/// </summary>
|
||||
public string? Host { get; set; }
|
||||
}
|
||||
|
||||
@ -55,31 +55,17 @@ public class SysAuthService : IDynamicApiController, ITransient
|
||||
var passwordErrorTimes = _sysCacheService.Get<int>(keyPasswordErrorTimes);
|
||||
var passwordMaxErrorTimes = await _sysConfigService.GetConfigValueByCode<int>(ConfigConst.SysPasswordMaxErrorTimes);
|
||||
// 若未配置或误配置为0、负数, 则默认密码错误次数最大为5次
|
||||
if (passwordMaxErrorTimes < 1)
|
||||
passwordMaxErrorTimes = 5;
|
||||
if (passwordErrorTimes > passwordMaxErrorTimes)
|
||||
throw Oops.Oh(ErrorCodeEnum.D1027);
|
||||
if (passwordMaxErrorTimes < 1) passwordMaxErrorTimes = 5;
|
||||
if (passwordErrorTimes > passwordMaxErrorTimes) throw Oops.Oh(ErrorCodeEnum.D1027);
|
||||
|
||||
// 是否开启验证码
|
||||
if (await _sysConfigService.GetConfigValueByCode<bool>(ConfigConst.SysCaptcha))
|
||||
{
|
||||
// 判断验证码
|
||||
if (!_captcha.Validate(input.CodeId.ToString(), input.Code))
|
||||
throw Oops.Oh(ErrorCodeEnum.D0008);
|
||||
}
|
||||
// 判断是否开启验证码并校验
|
||||
if (await _sysConfigService.GetConfigValueByCode<bool>(ConfigConst.SysCaptcha) && !_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));
|
||||
_ = user ?? throw Oops.Oh(ErrorCodeEnum.D0009);
|
||||
// 获取登录租户和用户
|
||||
var (tenant, user) = await GetLoginUserAndTenant(input.Host, account: input.Account);
|
||||
|
||||
// 账号是否被冻结
|
||||
if (user.Status == StatusEnum.Disable)
|
||||
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 (user.Status == StatusEnum.Disable) throw Oops.Oh(ErrorCodeEnum.D1017);
|
||||
|
||||
// 是否开启域登录验证
|
||||
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);
|
||||
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));
|
||||
throw Oops.Oh(ErrorCodeEnum.D1000);
|
||||
}
|
||||
}
|
||||
else
|
||||
VerifyPassword(input, keyPasswordErrorTimes, passwordErrorTimes, user);
|
||||
VerifyPassword(input.Password, keyPasswordErrorTimes, passwordErrorTimes, user);
|
||||
|
||||
// 登录成功则清空密码错误次数
|
||||
_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>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="password"></param>
|
||||
/// <param name="keyPasswordErrorTimes"></param>
|
||||
/// <param name="passwordErrorTimes"></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 (!user.Password.Equals(MD5Encryption.Encrypt(input.Password)))
|
||||
{
|
||||
_sysCacheService.Set(keyPasswordErrorTimes, ++passwordErrorTimes, TimeSpan.FromMinutes(30));
|
||||
throw Oops.Oh(ErrorCodeEnum.D1000);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 国密SM2解密(前端密码传输SM2加密后的)
|
||||
input.Password = CryptogramUtil.SM2Decrypt(input.Password);
|
||||
if (user.Password.Equals(MD5Encryption.Encrypt(password))) return;
|
||||
|
||||
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>
|
||||
@ -143,23 +178,28 @@ public class SysAuthService : IDynamicApiController, ITransient
|
||||
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);
|
||||
|
||||
// 国密SM2解密(前端密码传输SM2加密后的)
|
||||
password = CryptogramUtil.SM2Decrypt(password);
|
||||
var keyPasswordErrorTimes = $"{CacheConst.KeyPasswordErrorTimes}{user.Account}";
|
||||
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);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!CryptogramUtil.Decrypt(user.Password).Equals(password))
|
||||
throw Oops.Oh(ErrorCodeEnum.D1000);
|
||||
}
|
||||
VerifyPassword(password, keyPasswordErrorTimes, passwordErrorTimes, user);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -176,21 +216,21 @@ public class SysAuthService : IDynamicApiController, ITransient
|
||||
// 校验短信验证码
|
||||
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));
|
||||
_ = user ?? throw Oops.Oh(ErrorCodeEnum.D0009);
|
||||
// 获取登录租户和用户
|
||||
var (tenant, user) = await GetLoginUserAndTenant(input.Host, phone: input.Phone);
|
||||
|
||||
return await CreateToken(user, input.LoginMode);
|
||||
return await CreateToken(user, tenant.AppId, input.LoginMode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成Token令牌 🔖
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="appId"></param>
|
||||
/// <param name="loginMode"></param>
|
||||
/// <returns></returns>
|
||||
[NonAction]
|
||||
internal async Task<LoginOutput> CreateToken(SysUser user, LoginModeEnum loginMode)
|
||||
internal async Task<LoginOutput> CreateToken(SysUser user, long? appId, LoginModeEnum loginMode)
|
||||
{
|
||||
// 默认PC端登录模式
|
||||
if (loginMode == 0)
|
||||
@ -203,6 +243,7 @@ public class SysAuthService : IDynamicApiController, ITransient
|
||||
var tokenExpire = await _sysConfigService.GetTokenExpire();
|
||||
var accessToken = JWTEncryption.Encrypt(new Dictionary<string, object>
|
||||
{
|
||||
{ ClaimConst.AppId, appId },
|
||||
{ ClaimConst.UserId, user.Id },
|
||||
{ ClaimConst.TenantId, user.TenantId },
|
||||
{ ClaimConst.Account, user.Account },
|
||||
@ -357,6 +398,7 @@ public class SysAuthService : IDynamicApiController, ITransient
|
||||
{
|
||||
Account = auth.UserName,
|
||||
Password = CryptogramUtil.SM2Encrypt(auth.Password),
|
||||
Host = SqlSugarConst.DefaultTenantHost
|
||||
});
|
||||
|
||||
_sysCacheService.Remove($"{CacheConst.KeyConfig}{ConfigConst.SysCaptcha}");
|
||||
|
||||
@ -27,10 +27,10 @@ public class SysLdapService : IDynamicApiController, ITransient
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[DisplayName("获取系统域登录配置分页列表")]
|
||||
public async Task<SqlSugarPagedList<SysLdap>> Page(PageSysLdapInput input)
|
||||
public async Task<SqlSugarPagedList<SysLdap>> Page(SysLdapInput input)
|
||||
{
|
||||
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()))
|
||||
.OrderBy(u => u.CreateTime, OrderByType.Desc)
|
||||
.ToPagedListAsync(input.Page, input.PageSize);
|
||||
@ -78,7 +78,7 @@ public class SysLdapService : IDynamicApiController, ITransient
|
||||
[DisplayName("删除系统域登录配置")]
|
||||
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 _rep.DeleteAsync(entity); // 真删除
|
||||
}
|
||||
@ -91,7 +91,7 @@ public class SysLdapService : IDynamicApiController, ITransient
|
||||
[DisplayName("获取系统域登录配置详情")]
|
||||
public async Task<SysLdap> GetDetail([FromQuery] DetailSysLdapInput input)
|
||||
{
|
||||
return await _sysLdapRep.GetByIdAsync(input.Id);
|
||||
return await _sysLdapRep.GetFirstAsync(u => u.Id == input.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -119,18 +119,17 @@ public class SysLdapService : IDynamicApiController, ITransient
|
||||
try
|
||||
{
|
||||
ldapConn.Connect(sysLdap.Host, sysLdap.Port);
|
||||
ldapConn.Bind(sysLdap.Version, sysLdap.BindDn, sysLdap.BindPass);
|
||||
var ldapSearchResults = ldapConn.Search(sysLdap.BaseDn, LdapConnection.ScopeSub, sysLdap.AuthFilter.Replace("$s", account), null, false);
|
||||
string bindPass = CryptogramUtil.Decrypt(sysLdap.BindPass);
|
||||
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;
|
||||
while (ldapSearchResults.HasMore())
|
||||
{
|
||||
var ldapEntry = ldapSearchResults.Next();
|
||||
var sAMAccountName = ldapEntry.GetAttribute(sysLdap.AuthFilter)?.StringValue;
|
||||
if (!string.IsNullOrEmpty(sAMAccountName))
|
||||
{
|
||||
dn = ldapEntry.Dn;
|
||||
break;
|
||||
}
|
||||
var sAmAccountName = ldapEntry.GetAttribute(sysLdap.BindAttrAccount)?.StringValue;
|
||||
if (string.IsNullOrEmpty(sAmAccountName)) continue;
|
||||
dn = ldapEntry.Dn;
|
||||
break;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(dn)) throw Oops.Oh(ErrorCodeEnum.D1002);
|
||||
@ -154,15 +153,39 @@ public class SysLdapService : IDynamicApiController, ITransient
|
||||
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>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[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);
|
||||
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();
|
||||
try
|
||||
{
|
||||
@ -192,15 +215,15 @@ public class SysLdapService : IDynamicApiController, ITransient
|
||||
else
|
||||
{
|
||||
var sysUserLdap = CreateSysUserLdap(attrs, sysLdap.BindAttrAccount, sysLdap.BindAttrEmployeeId, deptCode);
|
||||
if (string.IsNullOrEmpty(sysUserLdap.EmployeeId)) continue;
|
||||
sysUserLdap.Dn = ldapEntry.Dn;
|
||||
userLdapList.Add(sysUserLdap);
|
||||
}
|
||||
}
|
||||
|
||||
if (userLdapList.Count == 0)
|
||||
return;
|
||||
if (userLdapList.Count == 0) return null;
|
||||
|
||||
await App.GetRequiredService<SysUserLdapService>().InsertUserLdaps(sysLdap.TenantId!.Value, userLdapList);
|
||||
await App.GetRequiredService<SysUserLdapService>().InsertUserLdapList(sysLdap.TenantId!.Value, userLdapList);
|
||||
return userLdapList;
|
||||
}
|
||||
catch (LdapException e)
|
||||
{
|
||||
@ -225,7 +248,7 @@ public class SysLdapService : IDynamicApiController, ITransient
|
||||
private static string GetDepartmentCode(LdapAttributeSet attrs, string bindAttrCode)
|
||||
{
|
||||
return bindAttrCode == "objectGUID"
|
||||
? new Guid(attrs.GetAttribute(bindAttrCode)?.ByteValue).ToString()
|
||||
? new Guid(attrs.GetAttribute(bindAttrCode)?.ByteValue!).ToString()
|
||||
: attrs.GetAttribute(bindAttrCode)?.StringValue ?? "0";
|
||||
}
|
||||
|
||||
@ -239,12 +262,22 @@ public class SysLdapService : IDynamicApiController, ITransient
|
||||
/// <returns></returns>
|
||||
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,
|
||||
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>
|
||||
@ -279,7 +312,7 @@ public class SysLdapService : IDynamicApiController, ITransient
|
||||
else
|
||||
{
|
||||
var sysUserLdap = CreateSysUserLdap(attrs, sysLdap.BindAttrAccount, sysLdap.BindAttrEmployeeId, deptCode);
|
||||
|
||||
sysUserLdap.Dn = ldapEntry.Dn;
|
||||
if (string.IsNullOrEmpty(sysUserLdap.EmployeeId)) continue;
|
||||
userLdapList.Add(sysUserLdap);
|
||||
}
|
||||
@ -294,12 +327,13 @@ public class SysLdapService : IDynamicApiController, ITransient
|
||||
[DisplayName("同步域组织")]
|
||||
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();
|
||||
try
|
||||
{
|
||||
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 orgList = new List<SysOrg>();
|
||||
while (ldapSearchResults.HasMore())
|
||||
@ -316,13 +350,12 @@ public class SysLdapService : IDynamicApiController, ITransient
|
||||
}
|
||||
|
||||
var attrs = ldapEntry.GetAttributeSet();
|
||||
if (attrs.Count == 0 || attrs.ContainsKey("OU"))
|
||||
{
|
||||
var sysOrg = CreateSysOrg(attrs, sysLdap, orgList, new SysOrg { Id = 0, Level = 0 });
|
||||
orgList.Add(sysOrg);
|
||||
if (attrs.Count != 0 && !attrs.ContainsKey("OU")) continue;
|
||||
|
||||
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)
|
||||
@ -369,13 +402,12 @@ public class SysLdapService : IDynamicApiController, ITransient
|
||||
}
|
||||
|
||||
var attrs = ldapEntry.GetAttributeSet();
|
||||
if (attrs.Count == 0 || attrs.ContainsKey("OU"))
|
||||
{
|
||||
var sysOrg = CreateSysOrg(attrs, sysLdap, listOrgs, org);
|
||||
listOrgs.Add(sysOrg);
|
||||
if (attrs.Count != 0 && !attrs.ContainsKey("OU")) continue;
|
||||
|
||||
SearchDnLdapDept(ldapConn, sysLdap, listOrgs, ldapEntry.Dn, sysOrg);
|
||||
}
|
||||
var sysOrg = CreateSysOrg(attrs, sysLdap, listOrgs, org);
|
||||
listOrgs.Add(sysOrg);
|
||||
|
||||
SearchDnLdapDept(ldapConn, sysLdap, listOrgs, ldapEntry.Dn, sysOrg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
//
|
||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||
|
||||
using NewLife.Reflection;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Admin.NET.Core.Service;
|
||||
@ -66,8 +65,7 @@ public class SysCacheService : IDynamicApiController, ISingleton
|
||||
[NonAction]
|
||||
public bool Set(string key, object value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(key)) return false;
|
||||
return _cacheProvider.Cache.Set($"{_cacheOptions.Prefix}{key}", value);
|
||||
return !string.IsNullOrWhiteSpace(key) && _cacheProvider.Cache.Set($"{_cacheOptions.Prefix}{key}", value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -80,8 +78,7 @@ public class SysCacheService : IDynamicApiController, ISingleton
|
||||
[NonAction]
|
||||
public bool Set(string key, object value, TimeSpan expire)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(key)) return false;
|
||||
return _cacheProvider.Cache.Set($"{_cacheOptions.Prefix}{key}", value, expire);
|
||||
return !string.IsNullOrWhiteSpace(key) && _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
|
||||
@ -140,28 +137,15 @@ public class SysCacheService : IDynamicApiController, ISingleton
|
||||
|
||||
private static string Key(string cacheName, object[] obs)
|
||||
{
|
||||
foreach (var obj in obs)
|
||||
{
|
||||
if (obj is TimeSpan)
|
||||
{
|
||||
throw new Exception("缓存参数类型不能能是:TimeSpan类型");
|
||||
}
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(cacheName + ":");
|
||||
foreach (var a in obs)
|
||||
{
|
||||
sb.Append($@"<{KeySingle(a)}>");
|
||||
}
|
||||
if (obs.OfType<TimeSpan>().Any()) throw new Exception("缓存参数类型不能能是:TimeSpan类型");
|
||||
StringBuilder sb = new(cacheName + ":");
|
||||
foreach (var a in obs) sb.Append($"<{KeySingle(a)}>");
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string KeySingle(object t)
|
||||
private static string KeySingle(object t)
|
||||
{
|
||||
if (t.GetType().IsClass && !t.GetType().IsPrimitive)
|
||||
{
|
||||
return JsonConvert.SerializeObject(t);
|
||||
}
|
||||
return t?.ToString();
|
||||
return t.GetType().IsClass && !t.GetType().IsPrimitive ? JsonConvert.SerializeObject(t) : t.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -327,6 +311,23 @@ public class SysCacheService : IDynamicApiController, ISingleton
|
||||
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>
|
||||
/// 获取多条HASH
|
||||
/// </summary>
|
||||
@ -352,12 +353,7 @@ public class SysCacheService : IDynamicApiController, ISingleton
|
||||
public T HashGetOne<T>(string key, string field)
|
||||
{
|
||||
var hash = GetHashMap<T>(key);
|
||||
var value = hash.GetValue(field);
|
||||
if (value == null)
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
return (T)hash.GetValue(field);
|
||||
return hash.ContainsKey(field) ? hash[field] : default(T);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -6,6 +6,9 @@
|
||||
|
||||
namespace Admin.NET.Core.Service;
|
||||
|
||||
/// <summary>
|
||||
/// 自定义模板引擎
|
||||
/// </summary>
|
||||
public class CustomViewEngine : ViewEngineModel
|
||||
{
|
||||
private readonly ISqlSugarClient _db;
|
||||
|
||||
@ -161,7 +161,7 @@ public class CodeGenConfig
|
||||
get
|
||||
{
|
||||
string str = "";
|
||||
if (EffectType == "fk")
|
||||
if (EffectType == "ForeignKey")
|
||||
{
|
||||
str = LowerFkEntityName + "_FK_" + LowerFkColumnName;
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ namespace Admin.NET.Core.Service;
|
||||
/// <summary>
|
||||
/// 代码生成参数类
|
||||
/// </summary>
|
||||
public class PageCodeGenInput : BasePageInput
|
||||
public class CodeGenInput : BasePageInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 作者姓名
|
||||
@ -112,7 +112,7 @@ public class PageCodeGenInput : BasePageInput
|
||||
public virtual bool IsApiService { get; set; }
|
||||
}
|
||||
|
||||
public class AddCodeGenInput : PageCodeGenInput
|
||||
public class AddCodeGenInput : CodeGenInput
|
||||
{
|
||||
/// <summary>
|
||||
/// 数据库表名
|
||||
@ -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)));
|
||||
}
|
||||
@ -39,7 +39,7 @@ public class SysCodeGenService : IDynamicApiController, ITransient
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[DisplayName("获取代码生成分页列表")]
|
||||
public async Task<SqlSugarPagedList<SysCodeGen>> Page(PageCodeGenInput input)
|
||||
public async Task<SqlSugarPagedList<SysCodeGen>> Page(CodeGenInput input)
|
||||
{
|
||||
return await _db.Queryable<SysCodeGen>()
|
||||
.Includes(u => u.CodeGenTemplateRelations)
|
||||
@ -405,7 +405,7 @@ public class SysCodeGenService : IDynamicApiController, ITransient
|
||||
#endregion 构造前端需要验证的数据
|
||||
|
||||
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); // 获取连表的实体名和别名
|
||||
|
||||
var data = new CustomViewEngine(_db)
|
||||
@ -547,7 +547,7 @@ public class SysCodeGenService : IDynamicApiController, ITransient
|
||||
#endregion 构造前端需要验证的数据
|
||||
|
||||
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); // 获取连表的实体名和别名
|
||||
|
||||
var data = new CustomViewEngine(_db)
|
||||
@ -628,7 +628,7 @@ public class SysCodeGenService : IDynamicApiController, ITransient
|
||||
private static (string, string) GetJoinTableStr(List<CodeGenConfig> configs)
|
||||
{
|
||||
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 lowerStr = ""; // (o, i, c)
|
||||
foreach (var item in uploads)
|
||||
@ -855,9 +855,9 @@ public class SysCodeGenService : IDynamicApiController, ITransient
|
||||
menuOrder += 10;
|
||||
|
||||
var menuList = new List<SysMenu>() { menuTypePage, menuTypeDetail, menuTypeAdd, menuTypeDelete, menuTypeUpdate, menuTypePrint, menuTypeImport, menuTypeExport };
|
||||
// 加入fk、Upload、ApiTreeSelect 等接口的权限
|
||||
// 加入ForeignKey、Upload、ApiTreeSelector 等接口的权限
|
||||
// 在生成表格时,有些字段只是查询时显示,不需要填写(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)
|
||||
{
|
||||
var menuType1 = new SysMenu
|
||||
@ -873,7 +873,7 @@ public class SysCodeGenService : IDynamicApiController, ITransient
|
||||
menuOrder += 10;
|
||||
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)
|
||||
{
|
||||
var menuType1 = new SysMenu
|
||||
|
||||
@ -192,8 +192,7 @@ public class SysCommonService : IDynamicApiController, ITransient
|
||||
var userId = App.User?.FindFirst(ClaimConst.UserId)?.Value;
|
||||
var resultStream = App.GetRequiredService<SysCacheService>().Get<MemoryStream>(CacheConst.KeyExcelTemp + userId);
|
||||
|
||||
if (resultStream == null)
|
||||
throw Oops.Oh("错误标记文件已过期。");
|
||||
if (resultStream == null) throw Oops.Oh("错误标记文件已过期。");
|
||||
|
||||
return await Task.FromResult(new FileStreamResult(resultStream, "application/octet-stream")
|
||||
{
|
||||
|
||||
@ -61,8 +61,7 @@ public class SysConfigService : IDynamicApiController, ITransient
|
||||
public async Task AddConfig(AddConfigInput input)
|
||||
{
|
||||
var isExist = await _sysConfigRep.IsAnyAsync(u => u.Name == input.Name || u.Code == input.Code);
|
||||
if (isExist)
|
||||
throw Oops.Oh(ErrorCodeEnum.D9000);
|
||||
if (isExist) throw Oops.Oh(ErrorCodeEnum.D9000);
|
||||
|
||||
await _sysConfigRep.InsertAsync(input.Adapt<SysConfig>());
|
||||
}
|
||||
@ -78,8 +77,7 @@ public class SysConfigService : IDynamicApiController, ITransient
|
||||
public async Task UpdateConfig(UpdateConfigInput input)
|
||||
{
|
||||
var isExist = await _sysConfigRep.IsAnyAsync(u => (u.Name == input.Name || u.Code == input.Code) && u.Id != input.Id);
|
||||
if (isExist)
|
||||
throw Oops.Oh(ErrorCodeEnum.D9000);
|
||||
if (isExist) throw Oops.Oh(ErrorCodeEnum.D9000);
|
||||
|
||||
//// 若修改国密SM2密匙则密码重新加密
|
||||
//if (input.Code == ConfigConst.SysSM2Key && CryptogramUtil.CryptoType == CryptogramEnum.SM2.ToString())
|
||||
@ -96,7 +94,7 @@ public class SysConfigService : IDynamicApiController, ITransient
|
||||
var config = input.Adapt<SysConfig>();
|
||||
await _sysConfigRep.AsUpdateable(config).IgnoreColumns(true).ExecuteCommandAsync();
|
||||
|
||||
_sysCacheService.Remove($"{CacheConst.KeyConfig}{config.Code}");
|
||||
RemoveConfigCache(config);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -109,12 +107,12 @@ public class SysConfigService : IDynamicApiController, ITransient
|
||||
public async Task DeleteConfig(DeleteConfigInput input)
|
||||
{
|
||||
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);
|
||||
|
||||
_sysCacheService.Remove($"{CacheConst.KeyConfig}{config.Code}");
|
||||
RemoveConfigCache(config);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -129,12 +127,12 @@ public class SysConfigService : IDynamicApiController, ITransient
|
||||
foreach (var id in ids)
|
||||
{
|
||||
var config = await _sysConfigRep.GetByIdAsync(id);
|
||||
if (config.SysFlag == YesNoEnum.Y) // 禁止删除系统参数
|
||||
continue;
|
||||
// 禁止删除系统参数
|
||||
if (config.SysFlag == YesNoEnum.Y) continue;
|
||||
|
||||
await _sysConfigRep.DeleteAsync(config);
|
||||
|
||||
_sysCacheService.Remove($"{CacheConst.KeyConfig}{config.Code}");
|
||||
RemoveConfigCache(config);
|
||||
}
|
||||
}
|
||||
|
||||
@ -207,7 +205,7 @@ public class SysConfigService : IDynamicApiController, ITransient
|
||||
config.Value = value;
|
||||
await _sysConfigRep.AsUpdateable(config).ExecuteCommandAsync();
|
||||
|
||||
_sysCacheService.Remove($"{CacheConst.KeyConfig}{config.Code}");
|
||||
RemoveConfigCache(config);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -256,10 +254,13 @@ public class SysConfigService : IDynamicApiController, ITransient
|
||||
[DisplayName("批量更新参数配置值")]
|
||||
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();
|
||||
_sysCacheService.Remove($"{CacheConst.KeyConfig}{Config.Code}");
|
||||
var configInfo = await _sysConfigRep.GetFirstAsync(u => u.Code == 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.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}");
|
||||
}
|
||||
}
|
||||
@ -27,19 +27,19 @@ public class SysConstService : IDynamicApiController, ITransient
|
||||
public async Task<List<ConstOutput>> GetList()
|
||||
{
|
||||
var key = $"{CacheConst.KeyConst}list";
|
||||
var constlist = _sysCacheService.Get<List<ConstOutput>>(key);
|
||||
if (constlist == null)
|
||||
var constList = _sysCacheService.Get<List<ConstOutput>>(key);
|
||||
if (constList == null)
|
||||
{
|
||||
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,
|
||||
Code = u.Name,
|
||||
Data = GetData(Convert.ToString(u.Name))
|
||||
}).ToList();
|
||||
_sysCacheService.Set(key, constlist);
|
||||
_sysCacheService.Set(key, constList);
|
||||
}
|
||||
return await Task.FromResult(constlist);
|
||||
return await Task.FromResult(constList);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -51,25 +51,25 @@ public class SysConstService : IDynamicApiController, ITransient
|
||||
public async Task<List<ConstOutput>> GetData([Required] string typeName)
|
||||
{
|
||||
var key = $"{CacheConst.KeyConst}{typeName.ToUpper()}";
|
||||
var constlist = _sysCacheService.Get<List<ConstOutput>>(key);
|
||||
if (constlist == null)
|
||||
var constList = _sysCacheService.Get<List<ConstOutput>>(key);
|
||||
if (constList == null)
|
||||
{
|
||||
var typeList = GetConstAttributeList();
|
||||
var type = typeList.FirstOrDefault(u => u.Name == typeName);
|
||||
if (type != null)
|
||||
{
|
||||
var isEnum = type.BaseType.Name == "Enum";
|
||||
constlist = type.GetFields()?
|
||||
var isEnum = type.BaseType!.Name == "Enum";
|
||||
constList = type.GetFields()?
|
||||
.Where(isEnum, u => u.FieldType.Name == typeName)
|
||||
.Select(u => new ConstOutput
|
||||
{
|
||||
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();
|
||||
_sysCacheService.Set(key, constlist);
|
||||
_sysCacheService.Set(key, constList);
|
||||
}
|
||||
}
|
||||
return await Task.FromResult(constlist);
|
||||
return await Task.FromResult(constList);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -4,8 +4,6 @@
|
||||
//
|
||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Npgsql;
|
||||
|
||||
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 });
|
||||
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类型,具体以列数据类型为准
|
||||
typeBuilder.CreateProperty(dbColumnName, typeof(string), new SugarColumn()
|
||||
{
|
||||
@ -339,67 +337,60 @@ public class SysDatabaseService : IDynamicApiController, ITransient
|
||||
|
||||
input.EntityName = entityType.Name;
|
||||
input.SeedDataName = entityType.Name + "SeedData";
|
||||
if (!string.IsNullOrWhiteSpace(input.Suffix))
|
||||
input.SeedDataName += input.Suffix;
|
||||
var targetPath = GetSeedDataTargetPath(input);
|
||||
if (!string.IsNullOrWhiteSpace(input.Suffix)) input.SeedDataName += input.Suffix;
|
||||
|
||||
// 查询所有数据
|
||||
var query = db.QueryableByObject(entityType);
|
||||
DbColumnInfo orderField = null; // 排序字段
|
||||
// 优先用创建时间排序
|
||||
orderField = dbColumnInfos.Where(u => u.DbColumnName.ToLower() == "create_time" || u.DbColumnName.ToLower() == "createtime").FirstOrDefault();
|
||||
if (orderField != null)
|
||||
query.OrderBy(orderField.DbColumnName);
|
||||
// 其次用Id排序
|
||||
orderField = dbColumnInfos.Where(u => u.DbColumnName.ToLower() == "id").FirstOrDefault();
|
||||
if (orderField != null)
|
||||
query.OrderBy(orderField.DbColumnName);
|
||||
IEnumerable recordsTmp = (IEnumerable)query.ToList();
|
||||
List<dynamic> records = recordsTmp.ToDynamicList();
|
||||
DbColumnInfo orderField = dbColumnInfos.FirstOrDefault(u => u.DbColumnName.ToLower() == "create_time" || u.DbColumnName.ToLower() == "createtime");
|
||||
if (orderField != null) query = query.OrderBy(orderField.DbColumnName);
|
||||
// 再使用第一个主键排序
|
||||
query = query.OrderBy(dbColumnInfos.First(u => u.IsPrimarykey).DbColumnName);
|
||||
var records = ((IEnumerable)await query.ToListAsync()).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))
|
||||
.Where(u => !u.GetCustomAttributes<IgnoreTableAttribute>().Any()).ToList();
|
||||
// 只有一个实体匹配才能过滤
|
||||
if (entityTypes.Count == 1)
|
||||
.Where(u => !u.GetCustomAttributes<IgnoreTableAttribute>().Any())
|
||||
.ToList();
|
||||
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)
|
||||
{
|
||||
var seedDataTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass
|
||||
&& u.GetInterfaces().Any(i => i.HasImplementedRawGeneric(typeof(ISqlSugarEntitySeedData<>)) && i.GenericTypeArguments[0] == entityTypes[0])).ToList();
|
||||
var seedDataTypes = App.EffectiveTypes
|
||||
.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 doNotFilterfullName2 = $"{input.Position}.{input.SeedDataName}"; // Core中的命名空间没有SeedData
|
||||
string doNotFilterFullName1 = $"{input.Position}.SeedData.{input.SeedDataName}";
|
||||
string doNotFilterFullName2 = $"{input.Position}.{input.SeedDataName}"; // Core中的命名空间没有SeedData
|
||||
|
||||
PropertyInfo idPropertySeedData = records[0].GetType().GetProperty("Id");
|
||||
|
||||
for (int i = seedDataTypes.Count - 1; i >= 0; i--)
|
||||
{
|
||||
string fullName = seedDataTypes[i].FullName;
|
||||
if ((fullName == doNotFilterfullName1) || (fullName == doNotFilterfullName2))
|
||||
continue;
|
||||
// 开始删除重复数据
|
||||
if ((fullName == doNotFilterFullName1) || (fullName == doNotFilterFullName2)) continue;
|
||||
|
||||
// 删除重复数据
|
||||
var instance = Activator.CreateInstance(seedDataTypes[i]);
|
||||
var hasDataMethod = seedDataTypes[i].GetMethod("HasData");
|
||||
var seedData = ((IEnumerable)hasDataMethod?.Invoke(instance, null))?.Cast<object>();
|
||||
if (seedData == null) continue;
|
||||
|
||||
var recordsToRemove = new List<object>();
|
||||
List<object> recordsToRemove = new();
|
||||
foreach (var record in records)
|
||||
{
|
||||
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);
|
||||
if (recordId != null && dataId != null && recordId.Equals(dataId))
|
||||
{
|
||||
recordsToRemove.Add(record);
|
||||
break;
|
||||
}
|
||||
recordsToRemove.Add(record);
|
||||
}
|
||||
}
|
||||
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 的属性
|
||||
var jsonIgnoreProperties = entityType.GetProperties().Where(p => (p.GetAttribute<System.Text.Json.Serialization.JsonIgnoreAttribute>() != null ||
|
||||
p.GetAttribute<JsonIgnoreAttribute>() != null) && p.GetAttribute<SugarColumn>() != null).ToList();
|
||||
var jsonIgnoreInfo = new List<List<JsonIgnoredPropertyData>>();
|
||||
if (jsonIgnoreProperties.Count > 0)
|
||||
{
|
||||
int recordIndex = 0;
|
||||
foreach (var r in (IEnumerable)records)
|
||||
{
|
||||
List<JsonIgnoredPropertyData> record = new();
|
||||
foreach (var item in jsonIgnoreProperties)
|
||||
{
|
||||
object v = item.GetValue(r);
|
||||
string strValue = "null";
|
||||
if (v != null)
|
||||
{
|
||||
strValue = v.ToString();
|
||||
if (v.GetType() == typeof(string))
|
||||
strValue = "\"" + strValue + "\"";
|
||||
else if (v.GetType() == typeof(DateTime))
|
||||
strValue = "DateTime.Parse(\"" + ((DateTime)v).ToString("yyyy-MM-dd HH:mm:ss") + "\")";
|
||||
}
|
||||
record.Add(new JsonIgnoredPropertyData { RecordIndex = recordIndex, Name = item.Name, Value = strValue });
|
||||
}
|
||||
recordIndex++;
|
||||
jsonIgnoreInfo.Add(record);
|
||||
}
|
||||
}
|
||||
// var jsonIgnoreProperties = entityType.GetProperties().Where(p => (p.GetAttribute<System.Text.Json.Serialization.JsonIgnoreAttribute>() != null ||
|
||||
// p.GetAttribute<JsonIgnoreAttribute>() != null) && p.GetAttribute<SugarColumn>() != null).ToList();
|
||||
// var jsonIgnoreInfo = new List<List<JsonIgnoredPropertyData>>();
|
||||
// if (jsonIgnoreProperties.Count > 0)
|
||||
// {
|
||||
// int recordIndex = 0;
|
||||
// foreach (var r in (IEnumerable)records)
|
||||
// {
|
||||
// List<JsonIgnoredPropertyData> record = new();
|
||||
// foreach (var item in jsonIgnoreProperties)
|
||||
// {
|
||||
// object v = item.GetValue(r);
|
||||
// string strValue = "null";
|
||||
// if (v != null)
|
||||
// {
|
||||
// strValue = v.ToString();
|
||||
// if (v.GetType() == typeof(string))
|
||||
// strValue = "\"" + strValue + "\"";
|
||||
// else if (v.GetType() == typeof(DateTime))
|
||||
// strValue = "DateTime.Parse(\"" + ((DateTime)v).ToString("yyyy-MM-dd HH:mm:ss") + "\")";
|
||||
// }
|
||||
// record.Add(new JsonIgnoredPropertyData { RecordIndex = recordIndex, Name = item.Name, Value = strValue });
|
||||
// }
|
||||
// recordIndex++;
|
||||
// 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
|
||||
{
|
||||
NameSpace = $"{input.Position}.SeedData",
|
||||
@ -452,17 +475,19 @@ public class SysDatabaseService : IDynamicApiController, ITransient
|
||||
input.SeedDataName,
|
||||
input.ConfigId,
|
||||
tableInfo.Description,
|
||||
JsonIgnoreInfo = jsonIgnoreInfo,
|
||||
RecordsJSON = recordsJSON
|
||||
// JsonIgnoreInfo = jsonIgnoreInfo,
|
||||
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.Collections");
|
||||
builder.AddUsing("System.Collections.Generic");
|
||||
builder.AddUsing("System.Linq");
|
||||
});
|
||||
File.WriteAllText(targetPath, tResult, Encoding.UTF8);
|
||||
|
||||
var targetPath = GetSeedDataTargetPath(input);
|
||||
await File.WriteAllTextAsync(targetPath, tResult, Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -477,27 +502,13 @@ public class SysDatabaseService : IDynamicApiController, ITransient
|
||||
var types = new List<Type>();
|
||||
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());
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
var sugarAttribute = c.GetCustomAttributes(type, true)?.FirstOrDefault();
|
||||
@ -517,6 +528,11 @@ public class SysDatabaseService : IDynamicApiController, ITransient
|
||||
});
|
||||
}
|
||||
return await Task.FromResult(entityInfos);
|
||||
|
||||
bool IsMyAttribute(Attribute[] o)
|
||||
{
|
||||
return o.Any(a => a.GetType() == type);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -71,6 +71,8 @@ public class SysDictTypeService : IDynamicApiController, ITransient
|
||||
[DisplayName("添加字典类型")]
|
||||
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);
|
||||
if (isExist) throw Oops.Oh(ErrorCodeEnum.D3001);
|
||||
|
||||
@ -87,10 +89,12 @@ public class SysDictTypeService : IDynamicApiController, ITransient
|
||||
[DisplayName("更新字典类型")]
|
||||
public async Task UpdateDictType(UpdateDictTypeInput input)
|
||||
{
|
||||
var isExist = await _sysDictTypeRep.IsAnyAsync(u => u.Id == input.Id);
|
||||
if (!isExist) throw Oops.Oh(ErrorCodeEnum.D3000);
|
||||
var dict = await _sysDictTypeRep.GetFirstAsync(x => x.Id == input.Id);
|
||||
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);
|
||||
|
||||
_sysCacheService.Remove($"{CacheConst.KeyDict}{input.Code}");
|
||||
|
||||
@ -32,12 +32,7 @@ public class SysEnumService : IDynamicApiController, ITransient
|
||||
.OrderBy(u => u.Name).ThenBy(u => u.FullName)
|
||||
.ToList();
|
||||
|
||||
var result = new List<EnumTypeOutput>();
|
||||
foreach (var item in enumTypeList)
|
||||
{
|
||||
result.Add(GetEnumDescription(item));
|
||||
}
|
||||
return result;
|
||||
return enumTypeList.Select(GetEnumDescription).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -85,8 +85,13 @@ public class UploadFileInput
|
||||
/// <summary>
|
||||
/// 上传文件Base64
|
||||
/// </summary>
|
||||
public class UploadFileFromBase64Input : SysFile
|
||||
public class UploadFileFromBase64Input
|
||||
{
|
||||
/// <summary>
|
||||
/// 文件名
|
||||
/// </summary>
|
||||
public string FileName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 文件内容
|
||||
/// </summary>
|
||||
|
||||
@ -94,14 +94,11 @@ public class SysFileService : IDynamicApiController, ITransient
|
||||
/// <param name="files"></param>
|
||||
/// <returns></returns>
|
||||
[DisplayName("上传多文件")]
|
||||
public async Task<List<SysFile>> UploadFiles([Required] List<IFormFile> files)
|
||||
public List<SysFile> UploadFiles([Required] List<IFormFile> files)
|
||||
{
|
||||
var filelist = new List<SysFile>();
|
||||
foreach (var file in files)
|
||||
{
|
||||
filelist.Add(await UploadFile(new UploadFileInput { File = file }));
|
||||
}
|
||||
return filelist;
|
||||
var fileList = new List<SysFile>();
|
||||
files.ForEach(file => fileList.Add(UploadFile(new UploadFileInput { File = file }).Result));
|
||||
return fileList;
|
||||
}
|
||||
|
||||
/// <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 fileName = HttpUtility.UrlEncode(file.FileName, Encoding.GetEncoding("UTF-8"));
|
||||
var filePath = Path.Combine(file.FilePath, file.Id.ToString() + file.Suffix);
|
||||
|
||||
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 };
|
||||
}
|
||||
return await GetFileStreamResult(file, fileName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -146,26 +124,33 @@ public class SysFileService : IDynamicApiController, ITransient
|
||||
{
|
||||
var file = await GetFile(id);
|
||||
//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)
|
||||
{
|
||||
var stream = await (await _OSSService.PresignedGetObjectAsync(file.BucketName.ToString(), filePath, 5)).GetAsStreamAsync();
|
||||
return new FileStreamResult(stream.Stream, "application/octet-stream");
|
||||
var stream = await (await _OSSService.PresignedGetObjectAsync(file.BucketName, filePath, 5)).GetAsStreamAsync();
|
||||
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"],
|
||||
App.Configuration["SSHProvider:Port"].ToInt(), App.Configuration["SSHProvider:Username"], App.Configuration["SSHProvider:Password"]))
|
||||
{
|
||||
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");
|
||||
using SSHHelper helper = new(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 };
|
||||
}
|
||||
|
||||
var path = Path.Combine(App.WebHostEnvironment.WebRootPath, filePath);
|
||||
return new FileStreamResult(new FileStream(path, FileMode.Open), "application/octet-stream") { FileDownloadName = fileName + file.Suffix };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -186,19 +171,15 @@ public class SysFileService : IDynamicApiController, ITransient
|
||||
byte[] fileBytes = await response.Content.ReadAsByteArrayAsync();
|
||||
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($"文件不存在");
|
||||
using (SSHHelper helper = new SSHHelper(App.Configuration["SSHProvider:Host"],
|
||||
App.Configuration["SSHProvider:Port"].ToInt(), App.Configuration["SSHProvider:Username"], App.Configuration["SSHProvider:Password"]))
|
||||
{
|
||||
return Convert.ToBase64String(helper.ReadAllBytes(sysFile.FilePath));
|
||||
}
|
||||
using SSHHelper helper = new SSHHelper(App.Configuration["SSHProvider:Host"],
|
||||
App.Configuration["SSHProvider:Port"].ToInt(), App.Configuration["SSHProvider:Username"], App.Configuration["SSHProvider:Password"]);
|
||||
return Convert.ToBase64String(helper.ReadAllBytes(sysFile.FilePath));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -213,7 +194,7 @@ public class SysFileService : IDynamicApiController, ITransient
|
||||
Log.Error($"DownloadFileBase64:文件[{realFile}]不存在");
|
||||
throw Oops.Oh($"文件[{sysFile.FilePath}]不存在");
|
||||
}
|
||||
byte[] fileBytes = File.ReadAllBytes(realFile);
|
||||
byte[] fileBytes = await File.ReadAllBytesAsync(realFile);
|
||||
return Convert.ToBase64String(fileBytes);
|
||||
}
|
||||
}
|
||||
@ -234,22 +215,19 @@ public class SysFileService : IDynamicApiController, ITransient
|
||||
|
||||
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())
|
||||
{
|
||||
var fullPath = string.Concat(file.FilePath, "/", file.Id + file.Suffix);
|
||||
using (SSHHelper helper = new SSHHelper(App.Configuration["SSHProvider:Host"],
|
||||
App.Configuration["SSHProvider:Port"].ToInt(), App.Configuration["SSHProvider:Username"], App.Configuration["SSHProvider:Password"]))
|
||||
{
|
||||
helper.DeleteFile(fullPath);
|
||||
}
|
||||
using SSHHelper helper = new(App.Configuration["SSHProvider:Host"],
|
||||
App.Configuration["SSHProvider:Port"].ToInt(), App.Configuration["SSHProvider:Username"], App.Configuration["SSHProvider:Password"]);
|
||||
helper.DeleteFile(fullPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
var filePath = Path.Combine(App.WebHostEnvironment.WebRootPath, file.FilePath, input.Id.ToString() + file.Suffix);
|
||||
if (File.Exists(filePath))
|
||||
File.Delete(filePath);
|
||||
var filePath = Path.Combine(App.WebHostEnvironment.WebRootPath, file.FilePath ?? "", input.Id + file.Suffix);
|
||||
if (File.Exists(filePath)) File.Delete(filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -311,7 +289,7 @@ public class SysFileService : IDynamicApiController, ITransient
|
||||
var fileMd5 = string.Empty;
|
||||
if (_uploadOptions.EnableMd5)
|
||||
{
|
||||
using (var fileStream = input.File.OpenReadStream())
|
||||
await using (var fileStream = input.File.OpenReadStream())
|
||||
{
|
||||
fileMd5 = OssUtils.ComputeContentMd5(fileStream, fileStream.Length);
|
||||
}
|
||||
@ -322,12 +300,10 @@ public class SysFileService : IDynamicApiController, ITransient
|
||||
}
|
||||
|
||||
// 验证文件类型
|
||||
if (!_uploadOptions.ContentType.Contains(input.File.ContentType))
|
||||
throw Oops.Oh($"{ErrorCodeEnum.D8001}:{input.File.ContentType}");
|
||||
if (!_uploadOptions.ContentType.Contains(input.File.ContentType)) throw Oops.Oh($"{ErrorCodeEnum.D8001}:{input.File.ContentType}");
|
||||
|
||||
// 验证文件大小
|
||||
if (sizeKb > _uploadOptions.MaxSize)
|
||||
throw Oops.Oh($"{ErrorCodeEnum.D8002},允许最大:{_uploadOptions.MaxSize}KB");
|
||||
if (sizeKb > _uploadOptions.MaxSize) throw Oops.Oh($"{ErrorCodeEnum.D8002},允许最大:{_uploadOptions.MaxSize}KB");
|
||||
|
||||
// 获取文件后缀
|
||||
var suffix = Path.GetExtension(input.File.FileName).ToLower(); // 后缀
|
||||
@ -341,14 +317,11 @@ public class SysFileService : IDynamicApiController, ITransient
|
||||
if (suffix == ".jpeg" || suffix == ".jpe")
|
||||
suffix = ".jpg";
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(suffix))
|
||||
throw Oops.Oh(ErrorCodeEnum.D8003);
|
||||
if (string.IsNullOrWhiteSpace(suffix)) throw Oops.Oh(ErrorCodeEnum.D8003);
|
||||
|
||||
// 防止客户端伪造文件类型
|
||||
if (!string.IsNullOrWhiteSpace(input.AllowSuffix) && !input.AllowSuffix.Contains(suffix))
|
||||
throw Oops.Oh(ErrorCodeEnum.D8003);
|
||||
//if (!VerifyFileExtensionName.IsSameType(file.OpenReadStream(), suffix))
|
||||
// throw Oops.Oh(ErrorCodeEnum.D8001);
|
||||
if (!string.IsNullOrWhiteSpace(input.AllowSuffix) && !input.AllowSuffix.Contains(suffix)) throw Oops.Oh(ErrorCodeEnum.D8003);
|
||||
//if (!VerifyFileExtensionName.IsSameType(file.OpenReadStream(), suffix)) throw Oops.Oh(ErrorCodeEnum.D8001);
|
||||
|
||||
// 文件存储位置
|
||||
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())
|
||||
{
|
||||
var fullPath = string.Concat(path.StartsWith('/') ? path : "/" + path, "/", finalName);
|
||||
using (SSHHelper helper = new SSHHelper(App.Configuration["SSHProvider:Host"],
|
||||
App.Configuration["SSHProvider:Port"].ToInt(), App.Configuration["SSHProvider:Username"], App.Configuration["SSHProvider:Password"]))
|
||||
{
|
||||
helper.UploadFile(input.File.OpenReadStream(), fullPath);
|
||||
}
|
||||
using SSHHelper helper = new(App.Configuration["SSHProvider:Host"],
|
||||
App.Configuration["SSHProvider:Port"].ToInt(), App.Configuration["SSHProvider:Username"], App.Configuration["SSHProvider:Password"]);
|
||||
helper.UploadFile(input.File.OpenReadStream(), fullPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -409,7 +380,7 @@ public class SysFileService : IDynamicApiController, ITransient
|
||||
Directory.CreateDirectory(filePath);
|
||||
|
||||
var realFile = Path.Combine(filePath, finalName);
|
||||
using (var stream = File.Create(realFile))
|
||||
await using (var stream = File.Create(realFile))
|
||||
{
|
||||
await input.File.CopyToAsync(stream);
|
||||
}
|
||||
@ -495,8 +466,8 @@ public class SysFileService : IDynamicApiController, ITransient
|
||||
public async Task<List<SysFile>> GetRelationFiles([FromQuery] RelationQueryInput input)
|
||||
{
|
||||
return await _sysFileRep.AsQueryable()
|
||||
.WhereIF(input.RelationId.HasValue && input.RelationId > 0, u => u.RelationId == input.RelationId)
|
||||
.WhereIF(input.BelongId.HasValue && input.BelongId > 0, u => u.BelongId == input.BelongId.Value)
|
||||
.WhereIF(input.RelationId is > 0, u => u.RelationId == input.RelationId)
|
||||
.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.FileTypes), u => input.GetFileTypeBS().Contains(u.FileType))
|
||||
.Select(u => new SysFile
|
||||
|
||||
@ -33,7 +33,7 @@ public class DbJobPersistence : IJobPersistence
|
||||
// 获取所有定义的作业
|
||||
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)
|
||||
|
||||
@ -11,11 +11,8 @@ namespace Admin.NET.Core.Service;
|
||||
/// </summary>
|
||||
public class JobClusterServer : IJobClusterServer
|
||||
{
|
||||
private readonly Random rd = new(DateTime.Now.Millisecond);
|
||||
|
||||
public JobClusterServer()
|
||||
{
|
||||
}
|
||||
private static readonly SqlSugarRepository<SysJobCluster> _sysJobClusterRep = App.GetRequiredService<SqlSugarRepository<SysJobCluster>>();
|
||||
private readonly Random _random = new(DateTime.Now.Millisecond);
|
||||
|
||||
/// <summary>
|
||||
/// 当前作业调度器启动通知
|
||||
@ -23,7 +20,6 @@ public class JobClusterServer : IJobClusterServer
|
||||
/// <param name="context">作业集群服务上下文</param>
|
||||
public async void Start(JobClusterContext context)
|
||||
{
|
||||
var _sysJobClusterRep = App.GetRequiredService<SqlSugarRepository<SysJobCluster>>();
|
||||
// 在作业集群表中,如果 clusterId 不存在,则新增一条(否则更新一条),并设置 status 为 ClusterStatus.Waiting
|
||||
if (await _sysJobClusterRep.IsAnyAsync(u => u.ClusterId == context.ClusterId))
|
||||
{
|
||||
@ -47,21 +43,19 @@ public class JobClusterServer : IJobClusterServer
|
||||
while (true)
|
||||
{
|
||||
// 控制集群心跳频率(放在头部为了防止 IsAnyAsync continue 没sleep占用大量IO和CPU)
|
||||
await Task.Delay(3000 + rd.Next(500, 1000)); // 错开集群同时启动
|
||||
await Task.Delay(3000 + _random.Next(500, 1000)); // 错开集群同时启动
|
||||
|
||||
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 则继续循环
|
||||
// 2) 如果作业集群表中还没有其他服务或只有自己,则插入一条集群服务或调用 await WorkNowAsync(clusterId); 之后 return;
|
||||
// 3) 如果作业集群表中没有 status 为 ClusterStatus.Working 的,调用 await WorkNowAsync(clusterId); 之后 return;
|
||||
if (await _sysJobClusterRep.IsAnyAsync(u => u.Status == ClusterStatus.Working))
|
||||
continue;
|
||||
if (await _sysJobClusterRep.IsAnyAsync(u => u.Status == ClusterStatus.Working)) continue;
|
||||
|
||||
await WorkNowAsync(clusterId);
|
||||
return;
|
||||
@ -77,7 +71,6 @@ public class JobClusterServer : IJobClusterServer
|
||||
/// <param name="context">作业集群服务上下文</param>
|
||||
public async void Stop(JobClusterContext context)
|
||||
{
|
||||
var _sysJobClusterRep = App.GetRequiredService<SqlSugarRepository<SysJobCluster>>();
|
||||
// 在作业集群表中,更新 clusterId 的 status 为 ClusterStatus.Crashed
|
||||
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>
|
||||
public async void Crash(JobClusterContext context)
|
||||
{
|
||||
var _sysJobClusterRep = App.GetRequiredService<SqlSugarRepository<SysJobCluster>>();
|
||||
// 在作业集群表中,更新 clusterId 的 status 为 ClusterStatus.Crashed
|
||||
await _sysJobClusterRep.UpdateAsync(u => new SysJobCluster { Status = ClusterStatus.Crashed }, u => u.ClusterId == context.ClusterId);
|
||||
}
|
||||
@ -100,7 +92,6 @@ public class JobClusterServer : IJobClusterServer
|
||||
/// <returns></returns>
|
||||
private static async Task WorkNowAsync(string clusterId)
|
||||
{
|
||||
var _sysJobClusterRep = App.GetRequiredService<SqlSugarRepository<SysJobCluster>>();
|
||||
// 在作业集群表中,更新 clusterId 的 status 为 ClusterStatus.Working
|
||||
await _sysJobClusterRep.UpdateAsync(u => new SysJobCluster { Status = ClusterStatus.Working }, u => u.ClusterId == clusterId);
|
||||
}
|
||||
|
||||
@ -12,14 +12,13 @@ namespace Admin.NET.Core.Service;
|
||||
public class JobMonitor : IJobMonitor
|
||||
{
|
||||
private readonly IEventPublisher _eventPublisher;
|
||||
private readonly IServiceScope _serviceScope;
|
||||
private readonly SysConfigService _sysConfigService;
|
||||
|
||||
public JobMonitor(IServiceScopeFactory scopeFactory)
|
||||
{
|
||||
_serviceScope = scopeFactory.CreateScope();
|
||||
_sysConfigService = _serviceScope.ServiceProvider.GetRequiredService<SysConfigService>();
|
||||
_eventPublisher = _serviceScope.ServiceProvider.GetRequiredService<IEventPublisher>(); ;
|
||||
var serviceScope = scopeFactory.CreateScope();
|
||||
_sysConfigService = serviceScope.ServiceProvider.GetRequiredService<SysConfigService>();
|
||||
_eventPublisher = serviceScope.ServiceProvider.GetRequiredService<IEventPublisher>(); ;
|
||||
}
|
||||
|
||||
public Task OnExecutingAsync(JobExecutingContext context, CancellationToken stoppingToken)
|
||||
|
||||
@ -83,8 +83,7 @@ public class SysJobService : IDynamicApiController, ITransient
|
||||
public async Task AddJobDetail(AddJobDetailInput input)
|
||||
{
|
||||
var isExist = await _sysJobDetailRep.IsAnyAsync(u => u.JobId == input.JobId && u.Id != input.Id);
|
||||
if (isExist)
|
||||
throw Oops.Oh(ErrorCodeEnum.D1006);
|
||||
if (isExist) throw Oops.Oh(ErrorCodeEnum.D1006);
|
||||
|
||||
// 动态创建作业
|
||||
Type jobType;
|
||||
@ -110,9 +109,7 @@ public class SysJobService : IDynamicApiController, ITransient
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
_schedulerFactory.AddJob(
|
||||
JobBuilder.Create(jobType)
|
||||
.LoadFrom(input.Adapt<SysJobDetail>()).SetJobType(jobType));
|
||||
_schedulerFactory.AddJob(JobBuilder.Create(jobType).LoadFrom(input.Adapt<SysJobDetail>()).SetJobType(jobType));
|
||||
|
||||
// 延迟一下等待持久化写入,再执行其他字段的更新
|
||||
await Task.Delay(500);
|
||||
@ -130,12 +127,10 @@ public class SysJobService : IDynamicApiController, ITransient
|
||||
public async Task UpdateJobDetail(UpdateJobDetailInput input)
|
||||
{
|
||||
var isExist = await _sysJobDetailRep.IsAnyAsync(u => u.JobId == input.JobId && u.Id != input.Id);
|
||||
if (isExist)
|
||||
throw Oops.Oh(ErrorCodeEnum.D1006);
|
||||
if (isExist) throw Oops.Oh(ErrorCodeEnum.D1006);
|
||||
|
||||
var sysJobDetail = await _sysJobDetailRep.GetByIdAsync(input.Id);
|
||||
if (sysJobDetail.JobId != input.JobId)
|
||||
throw Oops.Oh(ErrorCodeEnum.D1704);
|
||||
var sysJobDetail = await _sysJobDetailRep.GetFirstAsync(u => u.Id == input.Id);
|
||||
if (sysJobDetail.JobId != input.JobId) throw Oops.Oh(ErrorCodeEnum.D1704);
|
||||
|
||||
var scheduler = _schedulerFactory.GetJob(sysJobDetail.JobId);
|
||||
var oldScriptCode = sysJobDetail.ScriptCode; // 旧脚本代码
|
||||
@ -143,8 +138,7 @@ public class SysJobService : IDynamicApiController, ITransient
|
||||
|
||||
if (input.CreateType == JobCreateTypeEnum.Script)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input.ScriptCode))
|
||||
throw Oops.Oh(ErrorCodeEnum.D1701);
|
||||
if (string.IsNullOrEmpty(input.ScriptCode)) throw Oops.Oh(ErrorCodeEnum.D1701);
|
||||
|
||||
if (input.ScriptCode != oldScriptCode)
|
||||
{
|
||||
@ -153,8 +147,7 @@ public class SysJobService : IDynamicApiController, ITransient
|
||||
|
||||
if (jobType.GetCustomAttributes(typeof(JobDetailAttribute)).FirstOrDefault() is not JobDetailAttribute jobDetailAttribute)
|
||||
throw Oops.Oh(ErrorCodeEnum.D1702);
|
||||
if (jobDetailAttribute.JobId != input.JobId)
|
||||
throw Oops.Oh(ErrorCodeEnum.D1703);
|
||||
if (jobDetailAttribute.JobId != input.JobId) throw Oops.Oh(ErrorCodeEnum.D1703);
|
||||
|
||||
scheduler?.UpdateDetail(JobBuilder.Create(jobType).LoadFrom(sysJobDetail).SetJobType(jobType));
|
||||
}
|
||||
@ -205,8 +198,7 @@ public class SysJobService : IDynamicApiController, ITransient
|
||||
public async Task AddJobTrigger(AddJobTriggerInput input)
|
||||
{
|
||||
var isExist = await _sysJobTriggerRep.IsAnyAsync(u => u.TriggerId == input.TriggerId && u.Id != input.Id);
|
||||
if (isExist)
|
||||
throw Oops.Oh(ErrorCodeEnum.D1006);
|
||||
if (isExist) throw Oops.Oh(ErrorCodeEnum.D1006);
|
||||
|
||||
var jobTrigger = input.Adapt<SysJobTrigger>();
|
||||
jobTrigger.Args = "[" + jobTrigger.Args + "]";
|
||||
@ -224,8 +216,7 @@ public class SysJobService : IDynamicApiController, ITransient
|
||||
public async Task UpdateJobTrigger(UpdateJobTriggerInput input)
|
||||
{
|
||||
var isExist = await _sysJobTriggerRep.IsAnyAsync(u => u.TriggerId == input.TriggerId && u.Id != input.Id);
|
||||
if (isExist)
|
||||
throw Oops.Oh(ErrorCodeEnum.D1006);
|
||||
if (isExist) throw Oops.Oh(ErrorCodeEnum.D1006);
|
||||
|
||||
var jobTrigger = input.Adapt<SysJobTrigger>();
|
||||
jobTrigger.Args = "[" + jobTrigger.Args + "]";
|
||||
@ -303,8 +294,7 @@ public class SysJobService : IDynamicApiController, ITransient
|
||||
[DisplayName("执行作业")]
|
||||
public void RunJob(JobDetailInput input)
|
||||
{
|
||||
if (_schedulerFactory.TryRunJob(input.JobId, out _) != ScheduleResult.Succeed)
|
||||
throw Oops.Oh(ErrorCodeEnum.D1705);
|
||||
if (_schedulerFactory.TryRunJob(input.JobId, out _) != ScheduleResult.Succeed) throw Oops.Oh(ErrorCodeEnum.D1705);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -17,6 +17,16 @@ public class MenuInput
|
||||
/// 菜单类型(1目录 2菜单 3按钮)
|
||||
/// </summary>
|
||||
public MenuTypeEnum? Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有菜单
|
||||
/// </summary>
|
||||
public bool All { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 应用Id
|
||||
/// </summary>
|
||||
public long AppId { get; set; }
|
||||
}
|
||||
|
||||
public class AddMenuInput : SysMenu
|
||||
|
||||
@ -37,11 +37,13 @@ public class SysEmailService : IDynamicApiController, ITransient
|
||||
var webTitle = await _sysConfigService.GetConfigValueByCode<string>(ConfigConst.SysWebTitle);
|
||||
title = string.IsNullOrWhiteSpace(title) ? $"{webTitle} 系统邮件" : title;
|
||||
var message = new MimeMessage();
|
||||
|
||||
message.From.Add(new MailboxAddress(_emailOptions.DefaultFromEmail, _emailOptions.DefaultFromEmail));
|
||||
if (string.IsNullOrWhiteSpace(toEmail))
|
||||
message.To.Add(new MailboxAddress(_emailOptions.DefaultToEmail, _emailOptions.DefaultToEmail));
|
||||
else
|
||||
message.To.Add(new MailboxAddress(toEmail, toEmail));
|
||||
|
||||
message.To.Add(string.IsNullOrWhiteSpace(toEmail)
|
||||
? new MailboxAddress(_emailOptions.DefaultToEmail, _emailOptions.DefaultToEmail)
|
||||
: new MailboxAddress(toEmail, toEmail));
|
||||
|
||||
message.Subject = title;
|
||||
message.Body = new TextPart("html")
|
||||
{
|
||||
|
||||
@ -16,15 +16,12 @@ public class SysMessageService : IDynamicApiController, ITransient
|
||||
{
|
||||
private readonly SysCacheService _sysCacheService;
|
||||
private readonly IHubContext<OnlineUserHub, IOnlineUserHub> _chatHubContext;
|
||||
private readonly SysConfigService _sysConfigService;
|
||||
|
||||
public SysMessageService(SysCacheService sysCacheService,
|
||||
IHubContext<OnlineUserHub, IOnlineUserHub> chatHubContext,
|
||||
SysConfigService sysConfigService)
|
||||
IHubContext<OnlineUserHub, IOnlineUserHub> chatHubContext)
|
||||
{
|
||||
_sysCacheService = sysCacheService;
|
||||
_chatHubContext = chatHubContext;
|
||||
_sysConfigService = sysConfigService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -47,8 +44,8 @@ public class SysMessageService : IDynamicApiController, ITransient
|
||||
public async Task SendOtherUser(MessageInput input)
|
||||
{
|
||||
var hashKey = _sysCacheService.HashGetAll<SysOnlineUser>(CacheConst.KeyUserOnline);
|
||||
var exceptRecevieUsers = hashKey.Where(u => u.Value.UserId == input.ReceiveUserId).Select(u => u.Value).ToList();
|
||||
await _chatHubContext.Clients.AllExcept(exceptRecevieUsers.Select(t => t.ConnectionId)).ReceiveMessage(input);
|
||||
var exceptReceiveUsers = hashKey.Where(u => u.Value.UserId == input.ReceiveUserId).Select(u => u.Value).ToList();
|
||||
await _chatHubContext.Clients.AllExcept(exceptReceiveUsers.Select(t => t.ConnectionId)).ReceiveMessage(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -60,8 +57,8 @@ public class SysMessageService : IDynamicApiController, ITransient
|
||||
public async Task SendUser(MessageInput input)
|
||||
{
|
||||
var hashKey = _sysCacheService.HashGetAll<SysOnlineUser>(CacheConst.KeyUserOnline);
|
||||
var recevieUsers = hashKey.Where(u => u.Value.UserId == input.ReceiveUserId).Select(u => u.Value).ToList();
|
||||
await recevieUsers.ForEachAsync(u => _chatHubContext.Clients.Client(u.ConnectionId).ReceiveMessage(input));
|
||||
var receiveUsers = hashKey.Where(u => u.Value.UserId == input.ReceiveUserId).Select(u => u.Value).ToList();
|
||||
await receiveUsers.ForEachAsync(u => _chatHubContext.Clients.Client(u.ConnectionId ?? "").ReceiveMessage(input));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -73,7 +70,7 @@ public class SysMessageService : IDynamicApiController, ITransient
|
||||
public async Task SendUsers(MessageInput input)
|
||||
{
|
||||
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();
|
||||
await recevieUsers.ForEachAsync(u => _chatHubContext.Clients.Client(u.ConnectionId).ReceiveMessage(input));
|
||||
var receiveUsers = hashKey.Where(u => input.UserIds.Any(a => a == u.Value.UserId)).Select(u => u.Value).ToList();
|
||||
await receiveUsers.ForEachAsync(u => _chatHubContext.Clients.Client(u.ConnectionId ?? "").ReceiveMessage(input));
|
||||
}
|
||||
}
|
||||
@ -53,10 +53,8 @@ public class SysSmsService : IDynamicApiController, ITransient
|
||||
public bool VerifyCode(SmsVerifyCodeInput input)
|
||||
{
|
||||
var verifyCode = _sysCacheService.Get<string>($"{CacheConst.KeyPhoneVerCode}{input.Phone}");
|
||||
if (string.IsNullOrWhiteSpace(verifyCode))
|
||||
throw Oops.Oh("验证码不存在或已失效,请重新获取!");
|
||||
if (verifyCode != input.Code)
|
||||
throw Oops.Oh("验证码错误!");
|
||||
if (string.IsNullOrWhiteSpace(verifyCode)) throw Oops.Oh("验证码不存在或已失效,请重新获取!");
|
||||
if (verifyCode != input.Code) throw Oops.Oh("验证码错误!");
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -70,8 +68,7 @@ public class SysSmsService : IDynamicApiController, ITransient
|
||||
[DisplayName("阿里云发送短信")]
|
||||
public async Task AliyunSendSms([Required] string phoneNumber)
|
||||
{
|
||||
if (!phoneNumber.TryValidate(ValidationTypes.PhoneNumber).IsValid)
|
||||
throw Oops.Oh("请正确填写手机号码");
|
||||
if (!phoneNumber.TryValidate(ValidationTypes.PhoneNumber).IsValid) throw Oops.Oh("请正确填写手机号码");
|
||||
|
||||
// 生成随机验证码
|
||||
var random = new Random();
|
||||
@ -115,6 +112,10 @@ public class SysSmsService : IDynamicApiController, ITransient
|
||||
[DisplayName("发送短信模板")]
|
||||
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 template = _smsOptions.Aliyun.GetTemplate(input.TemplateId);
|
||||
var sendSmsRequest = new SendSmsRequest
|
||||
@ -146,8 +147,7 @@ public class SysSmsService : IDynamicApiController, ITransient
|
||||
[DisplayName("腾讯云发送短信")]
|
||||
public async Task TencentSendSms([Required] string phoneNumber)
|
||||
{
|
||||
if (!phoneNumber.TryValidate(ValidationTypes.PhoneNumber).IsValid)
|
||||
throw Oops.Oh("请正确填写手机号码");
|
||||
if (!phoneNumber.TryValidate(ValidationTypes.PhoneNumber).IsValid) throw Oops.Oh("请正确填写手机号码");
|
||||
|
||||
// 生成随机验证码
|
||||
var random = new Random();
|
||||
|
||||
@ -72,8 +72,7 @@ public class SysNoticeService : IDynamicApiController, ITransient
|
||||
[DisplayName("更新通知公告")]
|
||||
public async Task UpdateNotice(UpdateNoticeInput input)
|
||||
{
|
||||
if (input.CreateUserId != _userManager.UserId)
|
||||
throw Oops.Oh(ErrorCodeEnum.D7003);
|
||||
if (input.CreateUserId != _userManager.UserId) throw Oops.Oh(ErrorCodeEnum.D7003);
|
||||
|
||||
var notice = input.Adapt<SysNotice>();
|
||||
InitNoticeInfo(notice);
|
||||
@ -91,10 +90,8 @@ public class SysNoticeService : IDynamicApiController, ITransient
|
||||
public async Task DeleteNotice(DeleteNoticeInput input)
|
||||
{
|
||||
var sysNotice = await _sysNoticeRep.GetByIdAsync(input.Id);
|
||||
if (sysNotice.CreateUserId != _userManager.UserId)
|
||||
throw Oops.Oh(ErrorCodeEnum.D7003);
|
||||
if (sysNotice.Status == NoticeStatusEnum.PUBLIC)
|
||||
throw Oops.Oh(ErrorCodeEnum.D7001);
|
||||
if (sysNotice.CreateUserId != _userManager.UserId) throw Oops.Oh(ErrorCodeEnum.D7003);
|
||||
if (sysNotice.Status == NoticeStatusEnum.PUBLIC) throw Oops.Oh(ErrorCodeEnum.D7001);
|
||||
|
||||
await _sysNoticeRep.DeleteAsync(u => u.Id == input.Id);
|
||||
|
||||
@ -109,8 +106,7 @@ public class SysNoticeService : IDynamicApiController, ITransient
|
||||
[DisplayName("发布通知公告")]
|
||||
public async Task Public(NoticeInput input)
|
||||
{
|
||||
if (!(await _sysNoticeRep.IsAnyAsync(u => u.Id == input.Id && u.CreateUserId == _userManager.UserId)))
|
||||
throw Oops.Oh(ErrorCodeEnum.D7003);
|
||||
if (!(await _sysNoticeRep.IsAnyAsync(u => u.Id == input.Id && u.CreateUserId == _userManager.UserId))) throw Oops.Oh(ErrorCodeEnum.D7003);
|
||||
|
||||
// 更新发布状态和时间
|
||||
await _sysNoticeRep.UpdateAsync(u => new SysNotice() { Status = NoticeStatusEnum.PUBLIC, PublicTime = DateTime.Now }, u => u.Id == input.Id);
|
||||
|
||||
@ -31,13 +31,13 @@ public static class OAuthSetup
|
||||
})
|
||||
.AddWeixin(options =>
|
||||
{
|
||||
options.ClientId = authOpt.Weixin?.ClientId;
|
||||
options.ClientSecret = authOpt.Weixin?.ClientSecret;
|
||||
options.ClientId = authOpt.Weixin?.ClientId!;
|
||||
options.ClientSecret = authOpt.Weixin?.ClientSecret!;
|
||||
})
|
||||
.AddGitee(options =>
|
||||
{
|
||||
options.ClientId = authOpt.Gitee?.ClientId;
|
||||
options.ClientSecret = authOpt.Gitee?.ClientSecret;
|
||||
options.ClientId = authOpt.Gitee?.ClientId!;
|
||||
options.ClientSecret = authOpt.Gitee?.ClientSecret!;
|
||||
|
||||
options.ClaimActions.MapJsonKey(OAuthClaim.GiteeAvatarUrl, "avatar_url");
|
||||
});
|
||||
|
||||
@ -39,10 +39,13 @@ public class SysOAuthService : IDynamicApiController, ITransient
|
||||
if (string.IsNullOrWhiteSpace(provider) || !await _httpContextAccessor.HttpContext.IsProviderSupportedAsync(provider))
|
||||
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 properties = new AuthenticationProperties { RedirectUri = url };
|
||||
properties.Items["LoginProvider"] = provider;
|
||||
var properties = new AuthenticationProperties
|
||||
{
|
||||
RedirectUri = url,
|
||||
Items = { ["LoginProvider"] = provider }
|
||||
};
|
||||
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))
|
||||
throw Oops.Oh("不支持的OAuth类型");
|
||||
|
||||
var authenticateResult = await _httpContextAccessor.HttpContext.AuthenticateAsync(provider);
|
||||
var authenticateResult = await _httpContextAccessor.HttpContext!.AuthenticateAsync(provider);
|
||||
if (!authenticateResult.Succeeded)
|
||||
throw Oops.Oh("授权失败");
|
||||
|
||||
@ -110,7 +113,7 @@ public class SysOAuthService : IDynamicApiController, ITransient
|
||||
}
|
||||
|
||||
// 构建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}");
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ public class SysOnlineUserService : IDynamicApiController, ITransient
|
||||
[DisplayName("强制下线")]
|
||||
public async Task ForceOffline(SysOnlineUser user)
|
||||
{
|
||||
await _onlineUserHubContext.Clients.Client(user.ConnectionId).ForceOffline("强制下线");
|
||||
await _onlineUserHubContext.Clients.Client(user.ConnectionId ?? "").ForceOffline("强制下线");
|
||||
await _sysOnlineUerRep.DeleteAsync(user);
|
||||
}
|
||||
|
||||
@ -67,7 +67,7 @@ public class SysOnlineUserService : IDynamicApiController, ITransient
|
||||
|
||||
foreach (var item in userList)
|
||||
{
|
||||
await _onlineUserHubContext.Clients.Client(item.ConnectionId).PublicNotice(notice);
|
||||
await _onlineUserHubContext.Clients.Client(item.ConnectionId ?? "").PublicNotice(notice);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -56,7 +56,7 @@ public class SysOrgService : IDynamicApiController, ITransient
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
var orgTree = new List<SysOrg>();
|
||||
List<SysOrg> orgTree;
|
||||
if (_userManager.SuperAdmin)
|
||||
{
|
||||
orgTree = await iSugarQueryable.ToTreeAsync(u => u.Children, u => u.Pid, input.Id);
|
||||
@ -68,12 +68,11 @@ public class SysOrgService : IDynamicApiController, ITransient
|
||||
HandlerOrgTree(orgTree, userOrgIdList);
|
||||
}
|
||||
|
||||
var sysOrg = await _sysOrgRep.GetByIdAsync(input.Id);
|
||||
if (sysOrg != null)
|
||||
{
|
||||
sysOrg.Children = orgTree;
|
||||
orgTree = new List<SysOrg> { sysOrg };
|
||||
}
|
||||
var sysOrg = await _sysOrgRep.GetSingleAsync(u => u.Id == input.Id);
|
||||
if (sysOrg == null) return orgTree;
|
||||
|
||||
sysOrg.Children = orgTree;
|
||||
orgTree = new List<SysOrg> { sysOrg };
|
||||
return orgTree;
|
||||
}
|
||||
|
||||
@ -261,25 +260,20 @@ public class SysOrgService : IDynamicApiController, ITransient
|
||||
private void DeleteAllUserOrgCache(long orgId, long orgPid)
|
||||
{
|
||||
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 userOrgs = _sysCacheService.Get<List<long>>(userOrgKey);
|
||||
var userId = long.Parse(userOrgKey.Substring(CacheConst.KeyUserOrg));
|
||||
if (userOrgs != null && (userOrgs.Contains(orgId) || userOrgs.Contains(orgPid)))
|
||||
{
|
||||
SqlSugarFilter.DeleteUserOrgCache(userId, _sysOrgRep.Context.CurrentConnectionConfig.ConfigId.ToString());
|
||||
}
|
||||
if (orgPid == 0)
|
||||
{
|
||||
var dataScope = _sysCacheService.Get<int>($"{CacheConst.KeyRoleMaxDataScope}{userId}");
|
||||
if (dataScope == (int)DataScopeEnum.All)
|
||||
{
|
||||
SqlSugarFilter.DeleteUserOrgCache(userId, _sysOrgRep.Context.CurrentConnectionConfig.ConfigId.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
var userOrgList = _sysCacheService.Get<List<long>>(userOrgKey);
|
||||
var userId = long.Parse(userOrgKey.Substring(CacheConst.KeyUserOrg));
|
||||
if (userOrgList != null && (userOrgList.Contains(orgId) || userOrgList.Contains(orgPid)))
|
||||
SqlSugarFilter.DeleteUserOrgCache(userId, _sysOrgRep.Context.CurrentConnectionConfig.ConfigId.ToString());
|
||||
|
||||
if (orgPid != 0) continue;
|
||||
|
||||
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]
|
||||
public async Task<List<long>> GetUserOrgIdList()
|
||||
{
|
||||
if (_userManager.SuperAdmin)
|
||||
return new List<long>();
|
||||
if (_userManager.SuperAdmin) return new();
|
||||
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)
|
||||
{
|
||||
var orgIdList = _sysCacheService.Get<List<long>>($"{CacheConst.KeyUserOrg}{userId}"); // 取缓存
|
||||
if (orgIdList == null || orgIdList.Count < 1)
|
||||
{
|
||||
// 本人创建机构集合
|
||||
var orgList0 = await _sysOrgRep.AsQueryable().Where(u => u.CreateUserId == userId).Select(u => u.Id).ToListAsync();
|
||||
// 扩展机构集合
|
||||
var orgList1 = await _sysUserExtOrgService.GetUserExtOrgList(userId);
|
||||
// 角色机构集合
|
||||
var orgList2 = await GetUserRoleOrgIdList(userId, userOrgId);
|
||||
// 机构并集
|
||||
orgIdList = orgList1.Select(u => u.OrgId).Union(orgList2).Union(orgList0).ToList();
|
||||
// 当前所属机构
|
||||
if (!orgIdList.Contains(userOrgId))
|
||||
orgIdList.Add(userOrgId);
|
||||
_sysCacheService.Set($"{CacheConst.KeyUserOrg}{userId}", orgIdList, TimeSpan.FromDays(7)); // 存缓存
|
||||
}
|
||||
if (orgIdList is { Count: >= 1 }) return orgIdList;
|
||||
|
||||
// 本人创建机构集合
|
||||
var orgList0 = await _sysOrgRep.AsQueryable().Where(u => u.CreateUserId == userId).Select(u => u.Id).ToListAsync();
|
||||
// 扩展机构集合
|
||||
var orgList1 = await _sysUserExtOrgService.GetUserExtOrgList(userId);
|
||||
// 角色机构集合
|
||||
var orgList2 = await GetUserRoleOrgIdList(userId, userOrgId);
|
||||
// 机构并集
|
||||
orgIdList = orgList1.Select(u => u.OrgId).Union(orgList2).Union(orgList0).ToList();
|
||||
// 当前所属机构
|
||||
if (!orgIdList.Contains(userOrgId))
|
||||
orgIdList.Add(userOrgId);
|
||||
_sysCacheService.Set($"{CacheConst.KeyUserOrg}{userId}", orgIdList, TimeSpan.FromDays(7)); // 存缓存
|
||||
return orgIdList;
|
||||
}
|
||||
|
||||
@ -330,8 +322,7 @@ public class SysOrgService : IDynamicApiController, ITransient
|
||||
private async Task<List<long>> GetUserRoleOrgIdList(long userId, long userOrgId)
|
||||
{
|
||||
var roleList = await _sysUserRoleService.GetUserRoleList(userId);
|
||||
if (roleList.Count < 1)
|
||||
return new List<long>(); // 空机构Id集合
|
||||
if (roleList.Count < 1) return new(); // 空机构Id集合
|
||||
|
||||
return await GetUserOrgIdList(roleList, userId, userOrgId);
|
||||
}
|
||||
@ -354,7 +345,7 @@ public class SysOrgService : IDynamicApiController, ITransient
|
||||
// 数据范围的机构集合
|
||||
var dataScopeOrgIdList = new List<long>();
|
||||
|
||||
if (roleList != null && roleList.Count > 0)
|
||||
if (roleList is { Count: > 0 })
|
||||
{
|
||||
roleList.ForEach(u =>
|
||||
{
|
||||
@ -393,20 +384,20 @@ public class SysOrgService : IDynamicApiController, ITransient
|
||||
{
|
||||
var orgId = userOrgId;//var orgId = _userManager.OrgId;
|
||||
var orgIdList = new List<long>();
|
||||
// 若数据范围是全部,则获取所有机构Id集合
|
||||
if (dataScope == (int)DataScopeEnum.All)
|
||||
switch (dataScope)
|
||||
{
|
||||
orgIdList = await _sysOrgRep.AsQueryable().Select(u => u.Id).ToListAsync();
|
||||
}
|
||||
// 若数据范围是本部门及以下,则获取本节点和子节点集合
|
||||
else if (dataScope == (int)DataScopeEnum.DeptChild)
|
||||
{
|
||||
orgIdList = await GetChildIdListWithSelfById(orgId);
|
||||
}
|
||||
// 若数据范围是本部门不含子节点,则直接返回本部门
|
||||
else if (dataScope == (int)DataScopeEnum.Dept)
|
||||
{
|
||||
orgIdList.Add(orgId);
|
||||
// 若数据范围是全部,则获取所有机构Id集合
|
||||
case (int)DataScopeEnum.All:
|
||||
orgIdList = await _sysOrgRep.AsQueryable().Select(u => u.Id).ToListAsync();
|
||||
break;
|
||||
// 若数据范围是本部门及以下,则获取本节点和子节点集合
|
||||
case (int)DataScopeEnum.DeptChild:
|
||||
orgIdList = await GetChildIdListWithSelfById(orgId);
|
||||
break;
|
||||
// 若数据范围是本部门不含子节点,则直接返回本部门
|
||||
case (int)DataScopeEnum.Dept:
|
||||
orgIdList.Add(orgId);
|
||||
break;
|
||||
}
|
||||
return orgIdList;
|
||||
}
|
||||
|
||||
@ -46,8 +46,7 @@ public class SysPluginService : IDynamicApiController, ITransient
|
||||
public async Task AddPlugin(AddPluginInput input)
|
||||
{
|
||||
var isExist = await _sysPluginRep.IsAnyAsync(u => u.Name == input.Name || u.AssemblyName == input.AssemblyName);
|
||||
if (isExist)
|
||||
throw Oops.Oh(ErrorCodeEnum.D1900);
|
||||
if (isExist) throw Oops.Oh(ErrorCodeEnum.D1900);
|
||||
|
||||
// 添加动态程序集/接口
|
||||
input.AssemblyName = CompileAssembly(input.CsharpCode, input.AssemblyName);
|
||||
@ -65,8 +64,7 @@ public class SysPluginService : IDynamicApiController, ITransient
|
||||
public async Task UpdatePlugin(UpdatePluginInput input)
|
||||
{
|
||||
var isExist = await _sysPluginRep.IsAnyAsync(u => (u.Name == input.Name || u.AssemblyName == input.AssemblyName) && u.Id != input.Id);
|
||||
if (isExist)
|
||||
throw Oops.Oh(ErrorCodeEnum.D1900);
|
||||
if (isExist) throw Oops.Oh(ErrorCodeEnum.D1900);
|
||||
|
||||
// 先移除再添加动态程序集/接口
|
||||
RemoveAssembly(input.AssemblyName);
|
||||
|
||||
@ -75,8 +75,7 @@ public class SysPosService : IDynamicApiController, ITransient
|
||||
[DisplayName("增加职位")]
|
||||
public async Task AddPos(AddPosInput input)
|
||||
{
|
||||
if (await _sysPosRep.IsAnyAsync(u => u.Name == input.Name && u.Code == input.Code))
|
||||
throw Oops.Oh(ErrorCodeEnum.D6000);
|
||||
if (await _sysPosRep.IsAnyAsync(u => u.Name == input.Name && u.Code == input.Code)) throw Oops.Oh(ErrorCodeEnum.D6000);
|
||||
|
||||
await _sysPosRep.InsertAsync(input.Adapt<SysPos>());
|
||||
}
|
||||
@ -90,12 +89,10 @@ public class SysPosService : IDynamicApiController, ITransient
|
||||
[DisplayName("更新职位")]
|
||||
public async Task UpdatePos(UpdatePosInput input)
|
||||
{
|
||||
if (await _sysPosRep.IsAnyAsync(u => u.Name == input.Name && u.Code == input.Code && u.Id != input.Id))
|
||||
throw Oops.Oh(ErrorCodeEnum.D6000);
|
||||
if (await _sysPosRep.IsAnyAsync(u => u.Name == input.Name && u.Code == input.Code && u.Id != input.Id)) throw Oops.Oh(ErrorCodeEnum.D6000);
|
||||
|
||||
var sysPos = await _sysPosRep.GetByIdAsync(input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D6003);
|
||||
if (!_userManager.SuperAdmin && sysPos.CreateUserId != _userManager.UserId)
|
||||
throw Oops.Oh(ErrorCodeEnum.D6002);
|
||||
if (!_userManager.SuperAdmin && sysPos.CreateUserId != _userManager.UserId) throw Oops.Oh(ErrorCodeEnum.D6002);
|
||||
|
||||
await _sysPosRep.AsUpdateable(input.Adapt<SysPos>()).IgnoreColumns(true).ExecuteCommandAsync();
|
||||
}
|
||||
@ -110,19 +107,16 @@ public class SysPosService : IDynamicApiController, ITransient
|
||||
public async Task DeletePos(DeletePosInput input)
|
||||
{
|
||||
var sysPos = await _sysPosRep.GetByIdAsync(input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D6003);
|
||||
if (!_userManager.SuperAdmin && sysPos.CreateUserId != _userManager.UserId)
|
||||
throw Oops.Oh(ErrorCodeEnum.D6002);
|
||||
if (!_userManager.SuperAdmin && sysPos.CreateUserId != _userManager.UserId) throw Oops.Oh(ErrorCodeEnum.D6002);
|
||||
|
||||
// 若职位有用户则禁止删除
|
||||
var hasPosEmp = await _sysPosRep.ChangeRepository<SqlSugarRepository<SysUser>>()
|
||||
.IsAnyAsync(u => u.PosId == input.Id);
|
||||
if (hasPosEmp)
|
||||
throw Oops.Oh(ErrorCodeEnum.D6001);
|
||||
if (hasPosEmp) throw Oops.Oh(ErrorCodeEnum.D6001);
|
||||
|
||||
// 若附属职位有用户则禁止删除
|
||||
var hasExtPosEmp = await _sysUserExtOrgService.HasUserPos(input.Id);
|
||||
if (hasExtPosEmp)
|
||||
throw Oops.Oh(ErrorCodeEnum.D6001);
|
||||
if (hasExtPosEmp) throw Oops.Oh(ErrorCodeEnum.D6001);
|
||||
|
||||
await _sysPosRep.DeleteByIdAsync(input.Id);
|
||||
}
|
||||
|
||||
@ -54,8 +54,7 @@ public class SysPrintService : IDynamicApiController, ITransient
|
||||
public async Task AddPrint(AddPrintInput input)
|
||||
{
|
||||
var isExist = await _sysPrintRep.IsAnyAsync(u => u.Name == input.Name);
|
||||
if (isExist)
|
||||
throw Oops.Oh(ErrorCodeEnum.D1800);
|
||||
if (isExist) throw Oops.Oh(ErrorCodeEnum.D1800);
|
||||
|
||||
await _sysPrintRep.InsertAsync(input.Adapt<SysPrint>());
|
||||
}
|
||||
@ -70,8 +69,7 @@ public class SysPrintService : IDynamicApiController, ITransient
|
||||
public async Task UpdatePrint(UpdatePrintInput input)
|
||||
{
|
||||
var isExist = await _sysPrintRep.IsAnyAsync(u => u.Name == input.Name && u.Id != input.Id);
|
||||
if (isExist)
|
||||
throw Oops.Oh(ErrorCodeEnum.D1800);
|
||||
if (isExist) throw Oops.Oh(ErrorCodeEnum.D1800);
|
||||
|
||||
await _sysPrintRep.AsUpdateable(input.Adapt<SysPrint>()).IgnoreColumns(true).ExecuteCommandAsync();
|
||||
}
|
||||
|
||||
@ -76,22 +76,19 @@ public class SysRegionService : IDynamicApiController, ITransient
|
||||
[DisplayName("增加行政区划")]
|
||||
public async Task<long> AddRegion(AddRegionInput input)
|
||||
{
|
||||
input.Code = input.Code.Trim();
|
||||
if (input.Code.Length != 12 && input.Code.Length != 9 && input.Code.Length != 6)
|
||||
throw Oops.Oh(ErrorCodeEnum.R2003);
|
||||
input.Code = input.Code?.Trim() ?? "";
|
||||
if (input.Code.Length != 12 && input.Code.Length != 9 && input.Code.Length != 6) throw Oops.Oh(ErrorCodeEnum.R2003);
|
||||
|
||||
if (input.Pid != 0)
|
||||
{
|
||||
var pRegion = await _sysRegionRep.GetByIdAsync(input.Pid);
|
||||
pRegion ??= await _sysRegionRep.GetFirstAsync(u => u.Code == input.Pid.ToString());
|
||||
if (pRegion == null)
|
||||
throw Oops.Oh(ErrorCodeEnum.D2000);
|
||||
if (pRegion == null) throw Oops.Oh(ErrorCodeEnum.D2000);
|
||||
input.Pid = pRegion.Id;
|
||||
}
|
||||
|
||||
var isExist = await _sysRegionRep.IsAnyAsync(u => u.Name == input.Name && u.Code == input.Code);
|
||||
if (isExist)
|
||||
throw Oops.Oh(ErrorCodeEnum.R2002);
|
||||
if (isExist) throw Oops.Oh(ErrorCodeEnum.R2002);
|
||||
|
||||
var sysRegion = input.Adapt<SysRegion>();
|
||||
var newRegion = await _sysRegionRep.AsInsertable(sysRegion).ExecuteReturnEntityAsync();
|
||||
@ -107,34 +104,28 @@ public class SysRegionService : IDynamicApiController, ITransient
|
||||
[DisplayName("更新行政区划")]
|
||||
public async Task UpdateRegion(UpdateRegionInput input)
|
||||
{
|
||||
input.Code = input.Code.Trim();
|
||||
if (input.Code.Length != 12 && input.Code.Length != 9 && input.Code.Length != 6)
|
||||
throw Oops.Oh(ErrorCodeEnum.R2003);
|
||||
input.Code = input.Code?.Trim() ?? "";
|
||||
if (input.Code.Length != 12 && input.Code.Length != 9 && input.Code.Length != 6) throw Oops.Oh(ErrorCodeEnum.R2003);
|
||||
|
||||
var sysRegion = await _sysRegionRep.GetByIdAsync(input.Id);
|
||||
if (sysRegion == null)
|
||||
throw Oops.Oh(ErrorCodeEnum.D1002);
|
||||
if (sysRegion == null) throw Oops.Oh(ErrorCodeEnum.D1002);
|
||||
|
||||
if (sysRegion.Pid != input.Pid && input.Pid != 0)
|
||||
{
|
||||
var pRegion = await _sysRegionRep.GetByIdAsync(input.Pid);
|
||||
pRegion ??= await _sysRegionRep.GetFirstAsync(u => u.Code == input.Pid.ToString());
|
||||
if (pRegion == null)
|
||||
throw Oops.Oh(ErrorCodeEnum.D2000);
|
||||
if (pRegion == null) throw Oops.Oh(ErrorCodeEnum.D2000);
|
||||
|
||||
input.Pid = pRegion.Id;
|
||||
var regionTreeList = await _sysRegionRep.AsQueryable().ToChildListAsync(u => u.Pid, input.Id, true);
|
||||
var childIdList = regionTreeList.Select(u => u.Id).ToList();
|
||||
if (childIdList.Contains(input.Pid))
|
||||
throw Oops.Oh(ErrorCodeEnum.R2004);
|
||||
if (childIdList.Contains(input.Pid)) throw Oops.Oh(ErrorCodeEnum.R2004);
|
||||
}
|
||||
|
||||
if (input.Id == input.Pid)
|
||||
throw Oops.Oh(ErrorCodeEnum.R2001);
|
||||
if (input.Id == input.Pid) throw Oops.Oh(ErrorCodeEnum.R2001);
|
||||
|
||||
var isExist = await _sysRegionRep.IsAnyAsync(u => (u.Name == input.Name && u.Code == input.Code) && u.Id != sysRegion.Id);
|
||||
if (isExist)
|
||||
throw Oops.Oh(ErrorCodeEnum.R2002);
|
||||
if (isExist) throw Oops.Oh(ErrorCodeEnum.R2002);
|
||||
|
||||
//// 父Id不能为自己的子节点
|
||||
//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 值");
|
||||
|
||||
var syncLevel = await _sysConfigService.GetConfigValueByCode<int>(ConfigConst.SysRegionSyncLevel);
|
||||
if (syncLevel < 1 || syncLevel > 5)
|
||||
syncLevel = 3; // 默认区县级
|
||||
if (syncLevel is < 1 or > 5) syncLevel = 3; // 默认区县级
|
||||
|
||||
var res = await $"https://restapi.amap.com/v3/config/district?subdistrict={syncLevel}&key={key}".GetAsync();
|
||||
if (!res.IsSuccessStatusCode) return;
|
||||
@ -187,7 +177,7 @@ public class SysRegionService : IDynamicApiController, ITransient
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
@ -28,12 +28,12 @@ public class SysRoleOrgService : ITransient
|
||||
await _sysRoleOrgRep.DeleteAsync(u => u.RoleId == input.Id);
|
||||
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,
|
||||
OrgId = u
|
||||
}).ToList();
|
||||
await _sysRoleOrgRep.InsertRangeAsync(roleOrgs);
|
||||
await _sysRoleOrgRep.InsertRangeAsync(roleOrgList);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -135,13 +135,11 @@ public class SysRoleService : IDynamicApiController, ITransient
|
||||
{
|
||||
// 禁止删除系统管理员角色
|
||||
var sysRole = await _sysRoleRep.GetByIdAsync(input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
|
||||
if (sysRole.Code == CommonConst.SysAdminRole)
|
||||
throw Oops.Oh(ErrorCodeEnum.D1019);
|
||||
if (sysRole.Code == CommonConst.SysAdminRole) throw Oops.Oh(ErrorCodeEnum.D1019);
|
||||
|
||||
// 若角色有用户则禁止删除
|
||||
var userIds = await _sysUserRoleService.GetUserIdList(input.Id);
|
||||
if (userIds != null && userIds.Count > 0)
|
||||
throw Oops.Oh(ErrorCodeEnum.D1025);
|
||||
if (userIds != null && userIds.Count > 0) throw Oops.Oh(ErrorCodeEnum.D1025);
|
||||
|
||||
await _sysRoleRep.DeleteAsync(sysRole);
|
||||
|
||||
@ -166,8 +164,7 @@ public class SysRoleService : IDynamicApiController, ITransient
|
||||
[DisplayName("授权角色菜单")]
|
||||
public async Task GrantMenu(RoleMenuInput input)
|
||||
{
|
||||
if (input.MenuIdList == null || input.MenuIdList.Count < 1)
|
||||
return;
|
||||
if (input.MenuIdList == null || input.MenuIdList.Count < 1) return;
|
||||
|
||||
//// 将父节点为0的菜单排除,防止前端全选异常
|
||||
//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());
|
||||
}
|
||||
|
||||
var role = await _sysRoleRep.GetByIdAsync(input.Id);
|
||||
var role = await _sysRoleRep.GetFirstAsync(u => u.Id == input.Id);
|
||||
var dataScope = input.DataScope;
|
||||
if (!_userManager.SuperAdmin)
|
||||
{
|
||||
// 非超级管理员没有全部数据范围权限
|
||||
if (dataScope == (int)DataScopeEnum.All)
|
||||
throw Oops.Oh(ErrorCodeEnum.D1016);
|
||||
|
||||
// 若数据范围自定义,则判断授权数据范围是否有权限
|
||||
if (dataScope == (int)DataScopeEnum.Define)
|
||||
switch (dataScope)
|
||||
{
|
||||
var grantOrgIdList = input.OrgIdList;
|
||||
if (grantOrgIdList.Count > 0)
|
||||
{
|
||||
var orgIdList = await _sysOrgService.GetUserOrgIdList();
|
||||
if (orgIdList.Count < 1)
|
||||
throw Oops.Oh(ErrorCodeEnum.D1016);
|
||||
else if (!grantOrgIdList.All(u => orgIdList.Any(c => c == u)))
|
||||
throw Oops.Oh(ErrorCodeEnum.D1016);
|
||||
}
|
||||
// 非超级管理员没有全部数据范围权限
|
||||
case (int)DataScopeEnum.All: throw Oops.Oh(ErrorCodeEnum.D1016);
|
||||
// 若数据范围自定义,则判断授权数据范围是否有权限
|
||||
case (int)DataScopeEnum.Define:
|
||||
{
|
||||
var grantOrgIdList = input.OrgIdList;
|
||||
if (grantOrgIdList.Count > 0)
|
||||
{
|
||||
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;
|
||||
@ -259,8 +256,7 @@ public class SysRoleService : IDynamicApiController, ITransient
|
||||
[DisplayName("设置角色状态")]
|
||||
public async Task<int> SetStatus(RoleInput input)
|
||||
{
|
||||
if (!Enum.IsDefined(typeof(StatusEnum), input.Status))
|
||||
throw Oops.Oh(ErrorCodeEnum.D3005);
|
||||
if (!Enum.IsDefined(typeof(StatusEnum), input.Status)) throw Oops.Oh(ErrorCodeEnum.D3005);
|
||||
|
||||
return await _sysRoleRep.AsUpdateable()
|
||||
.SetColumns(u => u.Status == input.Status)
|
||||
@ -329,8 +325,7 @@ public class SysRoleService : IDynamicApiController, ITransient
|
||||
[DisplayName("获取角色接口黑名单集合")]
|
||||
public async Task<List<string>> GetRoleApiList([FromQuery] RoleInput input)
|
||||
{
|
||||
var roleApis = await _sysRoleApiService.GetRoleApiList(new List<long> { input.Id });
|
||||
return roleApis;
|
||||
return await _sysRoleApiService.GetRoleApiList(new List<long> { input.Id });
|
||||
|
||||
//var roleButtons = await GetRoleButtonList(new List<long> { input.Id });
|
||||
//return roleApis.Union(roleButtons).ToList();
|
||||
@ -345,44 +340,42 @@ public class SysRoleService : IDynamicApiController, ITransient
|
||||
{
|
||||
var userId = _userManager.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() };
|
||||
|
||||
// 超管账号获取所有接口
|
||||
if (_userManager.SuperAdmin)
|
||||
var allApiList = _sysCommonService.GetApiList();
|
||||
foreach (var apiOutput in allApiList)
|
||||
{
|
||||
var allApiList = _sysCommonService.GetApiList();
|
||||
foreach (var apiOutput in allApiList)
|
||||
{
|
||||
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);
|
||||
foreach (var controller in apiOutput.Children)
|
||||
apiList[0].AddRange(controller.Children.Select(u => u.Route));
|
||||
}
|
||||
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天
|
||||
// 所有按钮权限集合
|
||||
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();
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -17,10 +17,10 @@ public class SysScheduleService : IDynamicApiController, ITransient
|
||||
private readonly SqlSugarRepository<SysSchedule> _sysSchedule;
|
||||
|
||||
public SysScheduleService(UserManager userManager,
|
||||
SqlSugarRepository<SysSchedule> sysSchedle)
|
||||
SqlSugarRepository<SysSchedule> sysSchedule)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_sysSchedule = sysSchedle;
|
||||
_sysSchedule = sysSchedule;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -94,8 +94,7 @@ public class SysScheduleService : IDynamicApiController, ITransient
|
||||
[DisplayName("设置日程状态")]
|
||||
public async Task<int> SetStatus(ScheduleInput input)
|
||||
{
|
||||
if (!Enum.IsDefined(typeof(FinishStatusEnum), input.Status))
|
||||
throw Oops.Oh(ErrorCodeEnum.D3005);
|
||||
if (!Enum.IsDefined(typeof(FinishStatusEnum), input.Status)) throw Oops.Oh(ErrorCodeEnum.D3005);
|
||||
|
||||
return await _sysSchedule.AsUpdateable()
|
||||
.SetColumns(u => u.Status == input.Status)
|
||||
|
||||
@ -31,7 +31,7 @@ public class SysServerService : IDynamicApiController, ITransient
|
||||
ProcessorCount = Environment.ProcessorCount + " 核", // CPU核心数
|
||||
SysRunTime = ComputerUtil.GetRunTime(), // 系统运行时间
|
||||
RemoteIp = ComputerUtil.GetIpFromOnline(), // 外网地址
|
||||
LocalIp = App.HttpContext?.Connection?.LocalIpAddress.MapToIPv4().ToString(), // 本地地址
|
||||
LocalIp = App.HttpContext?.Connection?.LocalIpAddress!.MapToIPv4().ToString(), // 本地地址
|
||||
RuntimeInformation.FrameworkDescription, // NET框架
|
||||
Environment = App.HostEnvironment.IsDevelopment() ? "Development" : "Production",
|
||||
Wwwroot = App.WebHostEnvironment.WebRootPath, // 网站根目录
|
||||
|
||||
@ -29,6 +29,12 @@ public class PageTenantInput : BasePageInput
|
||||
|
||||
public class AddTenantInput : TenantOutput
|
||||
{
|
||||
///// <summary>
|
||||
///// 应用Id
|
||||
///// </summary>
|
||||
//[Required(ErrorMessage = "应用不能为空")]
|
||||
//public new long? AppId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 租户名称
|
||||
/// </summary>
|
||||
@ -40,6 +46,12 @@ public class AddTenantInput : TenantOutput
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "租管账号不能为空"), MinLength(3, ErrorMessage = "租管账号不能少于3个字符")]
|
||||
public override string AdminAccount { get; set; }
|
||||
|
||||
///// <summary>
|
||||
///// 租户域名
|
||||
///// </summary>
|
||||
//[Required(ErrorMessage = "域名不能为空"), MinLength(5, ErrorMessage = "域名不能少于5个字符")]
|
||||
//public new string Host { get; set; }
|
||||
}
|
||||
|
||||
public class UpdateTenantInput : AddTenantInput
|
||||
|
||||
@ -13,6 +13,11 @@ public class TenantOutput : SysTenant
|
||||
/// </summary>
|
||||
public virtual string Name { get; set; }
|
||||
|
||||
///// <summary>
|
||||
///// 关联应用名称
|
||||
///// </summary>
|
||||
//public virtual string AppName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 管理员账号
|
||||
/// </summary>
|
||||
|
||||
@ -22,17 +22,18 @@ public class SysUserLdapService : ITransient
|
||||
/// 批量插入域账号
|
||||
/// </summary>
|
||||
/// <param name="tenantId"></param>
|
||||
/// <param name="sysUserLdaps"></param>
|
||||
/// <param name="sysUserLdapList"></param>
|
||||
/// <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.InsertRangeAsync(sysUserLdaps);
|
||||
await _sysUserLdapRep.InsertRangeAsync(sysUserLdapList);
|
||||
|
||||
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 })
|
||||
.Where((l, u) => l.TenantId == tenantId && u.Status == StatusEnum.Enable && u.IsDelete == false && l.IsDelete == false)
|
||||
.ExecuteCommandAsync();
|
||||
}
|
||||
|
||||
@ -47,8 +48,7 @@ public class SysUserLdapService : ITransient
|
||||
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));
|
||||
if (userLdap != null)
|
||||
await _sysUserLdapRep.DeleteByIdAsync(userLdap.Id);
|
||||
if (userLdap != null) await _sysUserLdapRep.DeleteByIdAsync(userLdap.Id);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(domainAccount))
|
||||
await _sysUserLdapRep.InsertAsync(new SysUserLdap { EmployeeId = account, TenantId = tenantId, UserId = userId, Account = domainAccount });
|
||||
|
||||
@ -14,10 +14,12 @@ namespace Admin.NET.Core.Service;
|
||||
public class SysUserMenuService : IDynamicApiController, ITransient
|
||||
{
|
||||
private readonly SqlSugarRepository<SysUserMenu> _sysUserMenuRep;
|
||||
private readonly UserManager _userManager;
|
||||
|
||||
public SysUserMenuService(SqlSugarRepository<SysUserMenu> sysUserMenuRep)
|
||||
public SysUserMenuService(SqlSugarRepository<SysUserMenu> sysUserMenuRep, UserManager userManager)
|
||||
{
|
||||
_sysUserMenuRep = sysUserMenuRep;
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -26,16 +28,16 @@ public class SysUserMenuService : IDynamicApiController, ITransient
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[UnitOfWork]
|
||||
[ApiDescriptionSettings(Name = "Add"), HttpPost]
|
||||
[DisplayName("收藏菜单")]
|
||||
[ApiDescriptionSettings(Name = "Add"), HttpPost]
|
||||
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
|
||||
{
|
||||
UserId = input.UserId,
|
||||
UserId = _userManager.UserId,
|
||||
MenuId = u
|
||||
}).ToList();
|
||||
await _sysUserMenuRep.InsertRangeAsync(menus);
|
||||
@ -46,48 +48,55 @@ public class SysUserMenuService : IDynamicApiController, ITransient
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[ApiDescriptionSettings(Name = "Delete"), HttpPost]
|
||||
[ApiDescriptionSettings(Name = "DeleteUserMenu"), HttpPost]
|
||||
[DisplayName("取消收藏菜单")]
|
||||
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>
|
||||
/// 根据用户Id删除收藏菜单 🔖
|
||||
/// 获取当前用户收藏的菜单集合 🔖
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <returns></returns>
|
||||
[ApiDescriptionSettings(Name = "DeleteByUserId"), HttpPost]
|
||||
[DisplayName("根据用户Id删除收藏菜单")]
|
||||
public async Task DeleteByUserId(long userId)
|
||||
[DisplayName("获取当前用户收藏的菜单集合")]
|
||||
public async Task<List<MenuOutput>> GetUserMenuList()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据用户Id获取收藏菜单集合 🔖
|
||||
/// 批量删除收藏菜单
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <returns></returns>
|
||||
[DisplayName("根据用户Id获取收藏菜单集合")]
|
||||
public async Task<List<MenuOutput>> GetUserMenuList(long userId)
|
||||
/// <param name="ids"></param>
|
||||
[NonAction]
|
||||
public async Task DeleteMenuList(List<long> ids)
|
||||
{
|
||||
var sysUserMenuList = await _sysUserMenuRep.AsQueryable()
|
||||
.Includes(u => u.SysMenu)
|
||||
.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();
|
||||
if (ids == null || ids.Count == 0) return;
|
||||
await _sysUserMenuRep.DeleteAsync(u => ids.Contains(u.MenuId));
|
||||
}
|
||||
}
|
||||
@ -11,13 +11,10 @@ namespace Admin.NET.Core.Service;
|
||||
/// </summary>
|
||||
public class SysUserRoleService : ITransient
|
||||
{
|
||||
private readonly SysCacheService _sysCacheService;
|
||||
private readonly SqlSugarRepository<SysUserRole> _sysUserRoleRep;
|
||||
|
||||
public SysUserRoleService(SysCacheService sysCacheService,
|
||||
SqlSugarRepository<SysUserRole> sysUserRoleRep)
|
||||
public SysUserRoleService(SqlSugarRepository<SysUserRole> sysUserRoleRep)
|
||||
{
|
||||
_sysCacheService = sysCacheService;
|
||||
_sysUserRoleRep = sysUserRoleRep;
|
||||
}
|
||||
|
||||
|
||||
@ -19,6 +19,7 @@ public class SysUserService : IDynamicApiController, ITransient
|
||||
private readonly SysUserRoleService _sysUserRoleService;
|
||||
private readonly SysConfigService _sysConfigService;
|
||||
private readonly SysOnlineUserService _sysOnlineUserService;
|
||||
private readonly SysUserMenuService _sysUserMenuService;
|
||||
private readonly SysCacheService _sysCacheService;
|
||||
private readonly SysUserLdapService _sysUserLdapService;
|
||||
private readonly SqlSugarRepository<SysUser> _sysUserRep;
|
||||
@ -31,6 +32,7 @@ public class SysUserService : IDynamicApiController, ITransient
|
||||
SysUserRoleService sysUserRoleService,
|
||||
SysConfigService sysConfigService,
|
||||
SysOnlineUserService sysOnlineUserService,
|
||||
SysUserMenuService sysUserMenuService,
|
||||
SysCacheService sysCacheService,
|
||||
SysUserLdapService sysUserLdapService,
|
||||
SqlSugarRepository<SysUser> sysUserRep,
|
||||
@ -43,6 +45,7 @@ public class SysUserService : IDynamicApiController, ITransient
|
||||
_sysUserRoleService = sysUserRoleService;
|
||||
_sysConfigService = sysConfigService;
|
||||
_sysOnlineUserService = sysOnlineUserService;
|
||||
_sysUserMenuService = sysUserMenuService;
|
||||
_sysCacheService = sysCacheService;
|
||||
_sysUserLdapService = sysUserLdapService;
|
||||
_sysUserRep = sysUserRep;
|
||||
@ -100,8 +103,11 @@ public class SysUserService : IDynamicApiController, ITransient
|
||||
[DisplayName("增加用户")]
|
||||
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);
|
||||
|
||||
@ -114,7 +120,7 @@ public class SysUserService : IDynamicApiController, ITransient
|
||||
|
||||
// 增加域账号
|
||||
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);
|
||||
@ -132,11 +138,15 @@ public class SysUserService : IDynamicApiController, ITransient
|
||||
[DisplayName("更新用户")]
|
||||
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)
|
||||
.IgnoreColumns(u => new { u.Password, u.Status }).ExecuteCommandAsync();
|
||||
.IgnoreColumns(u => new { u.Password, u.Status, u.TenantId }).ExecuteCommandAsync();
|
||||
|
||||
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)))
|
||||
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);
|
||||
@ -183,13 +193,11 @@ public class SysUserService : IDynamicApiController, ITransient
|
||||
|
||||
// 若账号为租户默认账号则禁止删除
|
||||
var isTenantUser = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysTenant>>().IsAnyAsync(u => u.UserId == input.Id);
|
||||
if (isTenantUser)
|
||||
throw Oops.Oh(ErrorCodeEnum.D1029);
|
||||
if (isTenantUser) throw Oops.Oh(ErrorCodeEnum.D1029);
|
||||
|
||||
// 若账号为开放接口绑定账号则禁止删除
|
||||
var isOpenAccessUser = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysOpenAccess>>().IsAnyAsync(u => u.BindUserId == input.Id);
|
||||
if (isOpenAccessUser)
|
||||
throw Oops.Oh(ErrorCodeEnum.D1030);
|
||||
if (isOpenAccessUser) throw Oops.Oh(ErrorCodeEnum.D1030);
|
||||
|
||||
// 设置账号Token黑名单
|
||||
await SetUserBalckList(user, StatusEnum.Disable);
|
||||
@ -205,6 +213,9 @@ public class SysUserService : IDynamicApiController, ITransient
|
||||
// 删除域账号
|
||||
await _sysUserLdapService.DeleteUserLdapByUserId(input.Id);
|
||||
|
||||
// 删除用户收藏菜单
|
||||
await _sysUserMenuService.DeleteUserMenuList(input.Id);
|
||||
|
||||
// 执行订阅事件
|
||||
_sysUserEventHandler.OnEvent(this, SysUserEventTypeEnum.Delete, input);
|
||||
}
|
||||
|
||||
@ -13,6 +13,11 @@ public class UserManager : IScoped
|
||||
{
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
/// <summary>
|
||||
/// 应用ID
|
||||
/// </summary>
|
||||
public long AppId => (_httpContextAccessor.HttpContext?.User.FindFirst(ClaimConst.AppId)?.Value).ToLong();
|
||||
|
||||
/// <summary>
|
||||
/// 用户ID
|
||||
/// </summary>
|
||||
|
||||
@ -169,19 +169,18 @@ public static class SqlSugarSetup
|
||||
//}
|
||||
|
||||
// 执行时间超过5秒时
|
||||
if (db.Ado.SqlExecutionTime.TotalSeconds > 5)
|
||||
{
|
||||
var fileName = db.Ado.SqlStackTrace.FirstFileName; // 文件名
|
||||
var fileLine = db.Ado.SqlStackTrace.FirstLine; // 行号
|
||||
var firstMethodName = db.Ado.SqlStackTrace.FirstMethodName; // 方法名
|
||||
var log = $"【{DateTime.Now}——超时SQL】\r\n【所在文件名】:{fileName}\r\n【代码行数】:{fileLine}\r\n【方法名】:{firstMethodName}\r\n" + $"【SQL语句】:{UtilMethods.GetNativeSql(sql, pars)}";
|
||||
Log.Warning(log);
|
||||
App.PrintToMiniProfiler("SqlSugar", "Slow", log);
|
||||
}
|
||||
if (db.Ado.SqlExecutionTime.TotalSeconds <= 5) return;
|
||||
|
||||
var fileName = db.Ado.SqlStackTrace.FirstFileName; // 文件名
|
||||
var fileLine = db.Ado.SqlStackTrace.FirstLine; // 行号
|
||||
var firstMethodName = db.Ado.SqlStackTrace.FirstMethodName; // 方法名
|
||||
var log = $"【{DateTime.Now}——超时SQL】\r\n【所在文件名】:{fileName}\r\n【代码行数】:{fileLine}\r\n【方法名】:{firstMethodName}\r\n" + $"【SQL语句】:{UtilMethods.GetNativeSql(sql, pars)}";
|
||||
Log.Warning(log);
|
||||
App.PrintToMiniProfiler("SqlSugar", "Slow", log);
|
||||
};
|
||||
}
|
||||
// 数据审计
|
||||
db.Aop.DataExecuting = (oldValue, entityInfo) =>
|
||||
db.Aop.DataExecuting = (_, entityInfo) =>
|
||||
{
|
||||
// 若正在处理种子数据则直接返回
|
||||
if (_isHandlingSeedData) return;
|
||||
@ -204,34 +203,33 @@ public static class SqlSugarSetup
|
||||
entityInfo.SetValue(DateTime.Now);
|
||||
}
|
||||
// 若当前用户非空(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 (entityInfo.PropertyName == nameof(EntityTenantId.TenantId))
|
||||
{
|
||||
if (entityValue.TenantId == null || entityValue.TenantId == 0)
|
||||
entityInfo.SetValue(App.User.FindFirst(ClaimConst.TenantId)?.Value);
|
||||
}
|
||||
else if (entityInfo.PropertyName == nameof(EntityBase.CreateUserId))
|
||||
{
|
||||
if (entityValue.CreateUserId == null || entityValue.CreateUserId == 0)
|
||||
entityInfo.SetValue(App.User.FindFirst(ClaimConst.UserId)?.Value);
|
||||
}
|
||||
else if (entityInfo.PropertyName == nameof(EntityBase.CreateUserName))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(entityValue.CreateUserName))
|
||||
entityInfo.SetValue(App.User.FindFirst(ClaimConst.RealName)?.Value);
|
||||
}
|
||||
else if (entityInfo.PropertyName == nameof(EntityBaseData.CreateOrgId))
|
||||
{
|
||||
if (entityValue.CreateOrgId == null || entityValue.CreateOrgId == 0)
|
||||
entityInfo.SetValue(App.User.FindFirst(ClaimConst.OrgId)?.Value);
|
||||
}
|
||||
else if (entityInfo.PropertyName == nameof(EntityBaseData.CreateOrgName))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(entityValue.CreateOrgName))
|
||||
entityInfo.SetValue(App.User.FindFirst(ClaimConst.OrgName)?.Value);
|
||||
}
|
||||
if (entityValue.TenantId == null || entityValue.TenantId == 0)
|
||||
entityInfo.SetValue(App.User.FindFirst(ClaimConst.TenantId)?.Value);
|
||||
}
|
||||
else if (entityInfo.PropertyName == nameof(EntityBase.CreateUserId))
|
||||
{
|
||||
if (entityValue.CreateUserId == null || entityValue.CreateUserId == 0)
|
||||
entityInfo.SetValue(App.User.FindFirst(ClaimConst.UserId)?.Value);
|
||||
}
|
||||
else if (entityInfo.PropertyName == nameof(EntityBase.CreateUserName))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(entityValue.CreateUserName))
|
||||
entityInfo.SetValue(App.User.FindFirst(ClaimConst.RealName)?.Value);
|
||||
}
|
||||
else if (entityInfo.PropertyName == nameof(EntityBaseData.CreateOrgId))
|
||||
{
|
||||
if (entityValue.CreateOrgId == null || entityValue.CreateOrgId == 0)
|
||||
entityInfo.SetValue(App.User.FindFirst(ClaimConst.OrgId)?.Value);
|
||||
}
|
||||
else if (entityInfo.PropertyName == nameof(EntityBaseData.CreateOrgName))
|
||||
{
|
||||
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;
|
||||
for (int j = 0; j < afterColumns.Count; j++)
|
||||
{
|
||||
if (afterColumns[j].Value.Equals(beforeColumns[j].Value))
|
||||
{
|
||||
beforeColumns.Remove(beforeColumns[j]);
|
||||
afterColumns.Remove(afterColumns[j]);
|
||||
j--;
|
||||
}
|
||||
if (!afterColumns[j].Value.Equals(beforeColumns[j].Value)) continue;
|
||||
|
||||
beforeColumns.Remove(beforeColumns[j]);
|
||||
afterColumns.Remove(afterColumns[j]);
|
||||
j--;
|
||||
}
|
||||
}
|
||||
|
||||
@ -306,7 +303,7 @@ public static class SqlSugarSetup
|
||||
Parameters = JSON.Serialize(u.Parameters),
|
||||
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();
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
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)
|
||||
{
|
||||
Log.Information($"初始化数据库 {config.DbType} - {config.ConfigId} - {config.ConnectionString}");
|
||||
if (config.DbType != SqlSugar.DbType.Oracle)
|
||||
dbProvider.DbMaintenance.CreateDatabase();
|
||||
if (config.DbType != SqlSugar.DbType.Oracle) 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;
|
||||
|
||||
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 (config.ConfigId.ToString() == SqlSugarConst.MainConfigId) // 默认库(有系统表特性、没有日志表和租户表特性)
|
||||
{
|
||||
var entityType = seedType.GetInterfaces().First().GetGenericArguments().First();
|
||||
if (config.ConfigId.ToString() == SqlSugarConst.MainConfigId) // 默认库(有系统表特性、没有日志表和租户表特性)
|
||||
{
|
||||
if (entityType.GetCustomAttribute<SysTableAttribute>() == null && (entityType.GetCustomAttribute<LogTableAttribute>() != null || entityType.GetCustomAttribute<TenantAttribute>() != null))
|
||||
continue;
|
||||
}
|
||||
else if (config.ConfigId.ToString() == SqlSugarConst.LogConfigId) // 日志库
|
||||
{
|
||||
if (entityType.GetCustomAttribute<LogTableAttribute>() == null)
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
var att = entityType.GetCustomAttribute<TenantAttribute>(); // 自定义的库
|
||||
if (att == null || att.configId.ToString() != config.ConfigId.ToString()) continue;
|
||||
}
|
||||
if (entityType.GetCustomAttribute<SysTableAttribute>() == null && (entityType.GetCustomAttribute<LogTableAttribute>() != null || entityType.GetCustomAttribute<TenantAttribute>() != null))
|
||||
continue;
|
||||
}
|
||||
else if (config.ConfigId.ToString() == SqlSugarConst.LogConfigId) // 日志库
|
||||
{
|
||||
if (entityType.GetCustomAttribute<LogTableAttribute>() == null)
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
var att = entityType.GetCustomAttribute<TenantAttribute>(); // 自定义的库
|
||||
if (att == null || att.configId.ToString() != config.ConfigId.ToString()) continue;
|
||||
}
|
||||
|
||||
var instance = Activator.CreateInstance(seedType);
|
||||
var hasDataMethod = seedType.GetMethod("HasData");
|
||||
var seedData = ((IEnumerable)hasDataMethod?.Invoke(instance, null))?.Cast<object>();
|
||||
if (seedData == null) continue;
|
||||
var instance = Activator.CreateInstance(seedType);
|
||||
var hasDataMethod = seedType.GetMethod("HasData");
|
||||
var seedData = ((IEnumerable)hasDataMethod?.Invoke(instance, null))?.Cast<object>();
|
||||
if (seedData == null) continue;
|
||||
|
||||
var entityInfo = dbProvider.EntityMaintenance.GetEntityInfo(entityType);
|
||||
Console.WriteLine($"添加数据 {entityInfo.DbTableName} ({config.ConfigId} - {++count}/{sum},数据量:{seedData.Count()})");
|
||||
var entityInfo = dbProvider.EntityMaintenance.GetEntityInfo(entityType);
|
||||
Console.WriteLine($"添加数据 {entityInfo.DbTableName} ({config.ConfigId} - {++count}/{sum},数据量:{seedData.Count()})");
|
||||
|
||||
// 若实体包含Id字段,则设置为当前租户Id递增1
|
||||
if (entityInfo.Columns.Any(u => u.PropertyName == nameof(EntityBaseId.Id)))
|
||||
// 若实体包含Id字段,则设置为当前租户Id递增1
|
||||
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();
|
||||
foreach (var sd in seedData)
|
||||
{
|
||||
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();
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
_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>
|
||||
|
||||
@ -14,16 +14,16 @@ namespace Admin.NET.Core;
|
||||
/// </summary>
|
||||
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)
|
||||
{
|
||||
return dtConverter.ReadJson(reader, objectType, existingValue, serializer);
|
||||
return DtConverter.ReadJson(reader, objectType, existingValue, serializer);
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
dtConverter.WriteJson(writer, value, serializer);
|
||||
DtConverter.WriteJson(writer, value, serializer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -62,56 +62,35 @@ public static class CodeGenUtil
|
||||
return dataType + (dbColumnInfo.IsNullable ? "?" : "");
|
||||
}
|
||||
|
||||
// OracleSQL数据类型对应的字段类型
|
||||
public static string ConvertDataType_OracleSQL(string dataType, int? length, int? scale)
|
||||
{
|
||||
switch (dataType.ToLower())
|
||||
{
|
||||
case "interval year to month":
|
||||
return "int";
|
||||
case "interval year to month": return "int";
|
||||
|
||||
case "interval day to second":
|
||||
return "TimeSpan";
|
||||
case "interval day to second": return "TimeSpan";
|
||||
|
||||
case "smallint":
|
||||
return "Int16";
|
||||
case "smallint": return "Int16";
|
||||
|
||||
case "int":
|
||||
case "integer":
|
||||
return "int";
|
||||
case "integer": return "int";
|
||||
|
||||
case "long":
|
||||
return "long";
|
||||
case "long": return "long";
|
||||
|
||||
case "float":
|
||||
return "float";
|
||||
case "float": return "float";
|
||||
|
||||
case "decimal":
|
||||
return "decimal";
|
||||
case "decimal": return "decimal";
|
||||
|
||||
case "number":
|
||||
if (length != null)
|
||||
if (length == null) return "decimal";
|
||||
return scale switch
|
||||
{
|
||||
if (scale != null && scale > 0)
|
||||
{
|
||||
return "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";
|
||||
> 0 => "decimal",
|
||||
0 or null when length is > 1 and < 12 => "int",
|
||||
0 or null when length > 11 => "long",
|
||||
_ => length == 1 ? "bool" : "decimal"
|
||||
};
|
||||
|
||||
case "char":
|
||||
case "clob":
|
||||
@ -145,7 +124,7 @@ public static class CodeGenUtil
|
||||
}
|
||||
}
|
||||
|
||||
//PostgreSQL数据类型对应的字段类型
|
||||
// PostgreSQL数据类型对应的字段类型
|
||||
public static string ConvertDataType_PostgreSQL(string dataType)
|
||||
{
|
||||
switch (dataType)
|
||||
@ -229,6 +208,7 @@ public static class CodeGenUtil
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
public static string ConvertDataType_Default(string dataType)
|
||||
{
|
||||
return dataType.ToLower() switch
|
||||
@ -262,10 +242,10 @@ public static class CodeGenUtil
|
||||
{
|
||||
"string" => "Input",
|
||||
"int" => "InputNumber",
|
||||
"long" => "Input",
|
||||
"float" => "Input",
|
||||
"double" => "Input",
|
||||
"decimal" => "Input",
|
||||
"long" => "InputNumber",
|
||||
"float" => "InputNumber",
|
||||
"double" => "InputNumber",
|
||||
"decimal" => "InputNumber",
|
||||
"bool" => "Switch",
|
||||
"Guid" => "Input",
|
||||
"DateTime" => "DatePicker",
|
||||
|
||||
@ -17,18 +17,41 @@ namespace Admin.NET.Core;
|
||||
/// </summary>
|
||||
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>
|
||||
/// <param name="PassCount"></param>
|
||||
/// <param name="passCount"></param>
|
||||
/// <param name="allCount"></param>
|
||||
/// <returns></returns>
|
||||
public static string ExecPercent(decimal PassCount, decimal allCount)
|
||||
public static string ExecPercent(decimal passCount, decimal allCount)
|
||||
{
|
||||
string res = "";
|
||||
if (allCount > 0)
|
||||
{
|
||||
var value = (double)Math.Round(PassCount / allCount * 100, 1);
|
||||
var value = (double)Math.Round(passCount / allCount * 100, 1);
|
||||
if (value < 0)
|
||||
res = Math.Round(value + 5 / Math.Pow(10, 0 + 1), 0, MidpointRounding.AwayFromZero).ToString();
|
||||
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)
|
||||
where TSource : class, new() where TTarget : class, new()
|
||||
{
|
||||
var PropMappings = GetExportPropertMap<TSource, TTarget>();
|
||||
var propMappings = GetExportPropertMap<TSource, TTarget>();
|
||||
var data = query.ToList();
|
||||
//相同属性复制值,字典值转换
|
||||
// 相同属性复制值,字典值转换
|
||||
var result = new List<TTarget>();
|
||||
foreach (var item in data)
|
||||
{
|
||||
var newData = new TTarget();
|
||||
foreach (var dict in PropMappings)
|
||||
foreach (var dict in propMappings)
|
||||
{
|
||||
var targeProp = dict.Value.Item3;
|
||||
if (targeProp != null)
|
||||
var targetProp = dict.Value.Item3;
|
||||
if (targetProp != null)
|
||||
{
|
||||
var propertyInfo = dict.Value.Item2;
|
||||
var sourceVal = propertyInfo.GetValue(item, null);
|
||||
@ -152,21 +175,20 @@ public static class CommonUtil
|
||||
}
|
||||
|
||||
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;
|
||||
targeProp.SetValue(newData, newVal);
|
||||
targetProp.SetValue(newData, newVal1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (targeProp.PropertyType.FullName == propertyInfo.PropertyType.FullName)
|
||||
if (targetProp.PropertyType.FullName == propertyInfo.PropertyType.FullName)
|
||||
{
|
||||
targeProp.SetValue(newData, sourceVal);
|
||||
targetProp.SetValue(newData, sourceVal);
|
||||
}
|
||||
else
|
||||
{
|
||||
var newVal = sourceVal.ToString().ParseTo(targeProp.PropertyType);
|
||||
targeProp.SetValue(newData, newVal);
|
||||
var newVal = sourceVal.ToString().ParseTo(targetProp.PropertyType);
|
||||
targetProp.SetValue(newData, newVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -190,23 +212,22 @@ public static class CommonUtil
|
||||
/// <returns></returns>
|
||||
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 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)
|
||||
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}行)";
|
||||
}
|
||||
message += "\r\n字段缺失:" + string.Join(",", res.TemplateErrors.Select(m => m.RequireColumnName).ToList());
|
||||
throw Oops.Oh("导入异常:" + message);
|
||||
int rowNum = drErrorInfo.RowIndex;
|
||||
foreach (var item in drErrorInfo.FieldErrors)
|
||||
message += $"\r\n{item.Key}:{item.Value}(文件第{drErrorInfo.RowIndex}行)";
|
||||
}
|
||||
return res.Data;
|
||||
message += "\r\n字段缺失:" + string.Join(",", res.TemplateErrors.Select(m => m.RequireColumnName).ToList());
|
||||
throw Oops.Oh("导入异常:" + message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -218,7 +239,7 @@ public static class CommonUtil
|
||||
/// <returns></returns>
|
||||
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 res = await importer.Import<T>(file.OpenReadStream(), resultStream, importResultCallback);
|
||||
resultStream.Seek(0, SeekOrigin.Begin);
|
||||
@ -228,24 +249,20 @@ public static class CommonUtil
|
||||
App.GetRequiredService<SysCacheService>().Set(CacheConst.KeyExcelTemp + userId, resultStream, TimeSpan.FromMinutes(5));
|
||||
|
||||
var message = string.Empty;
|
||||
if (res.HasError)
|
||||
{
|
||||
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 (!res.HasError) return res.Data;
|
||||
|
||||
if (message.Length > 200)
|
||||
message = string.Concat(message.AsSpan(0, 200), "...\r\n异常过多,建议下载错误标记文件查看详细错误信息并重新导入。");
|
||||
throw Oops.Oh("导入异常:" + message);
|
||||
if (res.Exception != null)
|
||||
message += $"\r\n{res.Exception.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>
|
||||
@ -409,34 +426,34 @@ public static class CommonUtil
|
||||
else
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// 获取属性映射
|
||||
///// </summary>
|
||||
///// <typeparam name="TTarget"></typeparam>
|
||||
///// <returns>整理导入对象的 属性名称, 字典数据,原属性信息,目标属性信息 </returns>
|
||||
//private static Dictionary<string, Tuple<string, string>> GetExportDicttMap<TTarget>() where TTarget : new()
|
||||
//{
|
||||
// // 整理导入对象的属性名称,目标属性名,字典Code
|
||||
// var propMappings = new Dictionary<string, Tuple<string, string>>();
|
||||
// var tTargetProps = typeof(TTarget).GetProperties();
|
||||
// foreach (var propertyInfo in tTargetProps)
|
||||
// {
|
||||
// var attrs = propertyInfo.GetCustomAttribute<ImportDictAttribute>();
|
||||
// if (attrs != null && !string.IsNullOrWhiteSpace(attrs.TypeCode))
|
||||
// {
|
||||
// propMappings.Add(propertyInfo.Name, new Tuple<string, string>(attrs.TargetPropName, attrs.TypeCode));
|
||||
// }
|
||||
// }
|
||||
/// <summary>
|
||||
/// 获取属性映射
|
||||
/// </summary>
|
||||
/// <typeparam name="TTarget"></typeparam>
|
||||
/// <returns>整理导入对象的 属性名称, 字典数据,原属性信息,目标属性信息 </returns>
|
||||
private static Dictionary<string, Tuple<string, string>> GetExportDictMap<TTarget>() where TTarget : new()
|
||||
{
|
||||
// 整理导入对象的属性名称,目标属性名,字典Code
|
||||
var propMappings = new Dictionary<string, Tuple<string, string>>();
|
||||
var tTargetProps = typeof(TTarget).GetProperties();
|
||||
foreach (var propertyInfo in tTargetProps)
|
||||
{
|
||||
var attrs = propertyInfo.GetCustomAttribute<ImportDictAttribute>();
|
||||
if (attrs != null && !string.IsNullOrWhiteSpace(attrs.TypeCode))
|
||||
{
|
||||
propMappings.Add(propertyInfo.Name, new Tuple<string, string>(attrs.TargetPropName, attrs.TypeCode));
|
||||
}
|
||||
}
|
||||
|
||||
// return propMappings;
|
||||
//}
|
||||
return propMappings;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解析IP地址
|
||||
|
||||
@ -30,7 +30,7 @@ public static class ComputerUtil
|
||||
memoryMetrics.FreeRam = Math.Round(memoryMetrics.Free / 1024, 2) + "GB";
|
||||
memoryMetrics.UsedRam = Math.Round(memoryMetrics.Used / 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();
|
||||
if (cpuRates != null)
|
||||
{
|
||||
@ -45,25 +45,25 @@ public static class ComputerUtil
|
||||
/// <returns></returns>
|
||||
public static String GetOSInfo()
|
||||
{
|
||||
string opeartion = string.Empty;
|
||||
string operation = string.Empty;
|
||||
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)
|
||||
{
|
||||
opeartion = output.Replace("%", string.Empty);
|
||||
operation = output.Replace("%", string.Empty);
|
||||
}
|
||||
}
|
||||
else if (IsUnix())
|
||||
{
|
||||
var output = ShellHelper.Bash("awk -F= '/^VERSION_ID/ {print $2}' /etc/os-release | tr -d '\"'");
|
||||
opeartion = output ?? string.Empty;
|
||||
var output = ShellUtil.Bash("awk -F= '/^VERSION_ID/ {print $2}' /etc/os-release | tr -d '\"'");
|
||||
operation = output ?? string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
opeartion = RuntimeInformation.OSDescription;
|
||||
operation = RuntimeInformation.OSDescription;
|
||||
}
|
||||
return opeartion;
|
||||
return operation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -75,19 +75,18 @@ public static class ComputerUtil
|
||||
var diskInfos = new List<DiskInfo>();
|
||||
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);
|
||||
if (disks.Length < 1) return diskInfos;
|
||||
foreach (var item in disks)
|
||||
{
|
||||
var disk = item.Split(' ', (char)StringSplitOptions.RemoveEmptyEntries);
|
||||
if (disk == null || disk.Length < 5)
|
||||
continue;
|
||||
if (disk.Length < 5) continue;
|
||||
|
||||
var diskInfo = new DiskInfo()
|
||||
{
|
||||
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),
|
||||
Used = Math.Round(long.Parse(disk[2]) / 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())
|
||||
{
|
||||
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);
|
||||
if (disks.Length < 1) return diskInfos;
|
||||
|
||||
@ -109,8 +108,7 @@ public static class ComputerUtil
|
||||
foreach (var item in disks)
|
||||
{
|
||||
var disk = item.Split(' ', (char)StringSplitOptions.RemoveEmptyEntries);
|
||||
if (disk == null || disk.Length < 6)
|
||||
continue;
|
||||
if (disk.Length < 6) continue;
|
||||
|
||||
var diskInfo = new DiskInfo()
|
||||
{
|
||||
@ -126,8 +124,8 @@ public static class ComputerUtil
|
||||
}
|
||||
else
|
||||
{
|
||||
var driv = DriveInfo.GetDrives().Where(u => u.IsReady);
|
||||
foreach (var item in driv)
|
||||
var driveList = DriveInfo.GetDrives().Where(u => u.IsReady);
|
||||
foreach (var item in driveList)
|
||||
{
|
||||
if (item.DriveType == DriveType.CDRom) continue;
|
||||
var obj = new DiskInfo()
|
||||
@ -184,7 +182,7 @@ public static class ComputerUtil
|
||||
}
|
||||
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());
|
||||
}
|
||||
else
|
||||
@ -217,7 +215,7 @@ public static class ComputerUtil
|
||||
}
|
||||
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());
|
||||
}
|
||||
else
|
||||
@ -362,21 +360,14 @@ public class MemoryMetricsClient
|
||||
/// <returns></returns>
|
||||
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 lines = output.Split('\n', (char)StringSplitOptions.RemoveEmptyEntries);
|
||||
if (lines.Length <= 0) return metrics;
|
||||
var memory = output.Split(' ', (char)StringSplitOptions.RemoveEmptyEntries);
|
||||
if (memory.Length != 2) return metrics;
|
||||
|
||||
if (lines != null && lines.Length > 0)
|
||||
{
|
||||
var memory = lines[1].Split(' ', (char)StringSplitOptions.RemoveEmptyEntries);
|
||||
if (memory.Length >= 3)
|
||||
{
|
||||
metrics.Total = double.Parse(memory[0]);
|
||||
metrics.Used = double.Parse(memory[1]);
|
||||
metrics.Free = double.Parse(memory[2]);//m
|
||||
}
|
||||
}
|
||||
metrics.Total = double.Parse(memory[0]) / 1024;
|
||||
metrics.Free = double.Parse(memory[1]) / 1024;
|
||||
metrics.Used = metrics.Total - metrics.Free;
|
||||
return metrics;
|
||||
}
|
||||
|
||||
|
||||
@ -36,10 +36,7 @@ public class DateTimeUtil
|
||||
/// <returns></returns>
|
||||
public static DateTime GetBeginTime(DateTime? dateTime, int days = 0)
|
||||
{
|
||||
if (dateTime == DateTime.MinValue || dateTime == null)
|
||||
return DateTime.Now.AddDays(days);
|
||||
|
||||
return dateTime ?? DateTime.Now;
|
||||
return dateTime == DateTime.MinValue || dateTime == null ? DateTime.Now.AddDays(days) : (DateTime)dateTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -90,16 +87,16 @@ public class DateTimeUtil
|
||||
long hour = (ms - day * dd) / hh;
|
||||
long minute = (ms - day * dd - hour * hh) / mi;
|
||||
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 sHour = hour < 10 ? "0" + hour : "" + hour;//小时
|
||||
string sMinute = minute < 10 ? "0" + minute : "" + minute;//分钟
|
||||
string sSecond = second < 10 ? "0" + second : "" + second;//秒
|
||||
string sMilliSecond = milliSecond < 10 ? "0" + milliSecond : "" + milliSecond;//毫秒
|
||||
sMilliSecond = milliSecond < 100 ? "0" + sMilliSecond : "" + sMilliSecond;
|
||||
//string sMilliSecond = milliSecond < 10 ? "0" + milliSecond : "" + milliSecond;//毫秒
|
||||
//sMilliSecond = milliSecond < 100 ? "0" + sMilliSecond : "" + sMilliSecond;
|
||||
|
||||
return string.Format("{0} 天 {1} 小时 {2} 分 {3} 秒", sDay, sHour, sMinute, sSecond);
|
||||
return $"{sDay} 天 {sHour} 小时 {sMinute} 分 {sSecond} 秒";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -140,12 +137,7 @@ public class DateTimeUtil
|
||||
/// <returns></returns>
|
||||
public static string FormatDateTime(DateTime? dt)
|
||||
{
|
||||
if (dt == null) return string.Empty;
|
||||
|
||||
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");
|
||||
return dt == null ? string.Empty : dt.Value.ToString(dt.Value.Year == DateTime.Now.Year ? "MM-dd HH:mm" : "yyyy-MM-dd HH:mm");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -156,8 +148,8 @@ public class DateTimeUtil
|
||||
{
|
||||
return new List<DateTime>
|
||||
{
|
||||
Convert.ToDateTime(time.ToString("D").ToString()),
|
||||
Convert.ToDateTime(time.AddDays(1).ToString("D").ToString()).AddSeconds(-1)
|
||||
Convert.ToDateTime(time.ToString("D")),
|
||||
Convert.ToDateTime(time.AddDays(1).ToString("D")).AddSeconds(-1)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -78,7 +78,7 @@ public class ExcelHelper
|
||||
/// <param name="filename"></param>
|
||||
/// <param name="addListValidationFun"></param>
|
||||
/// <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);
|
||||
var worksheet = package.Workbook.Worksheets[0];
|
||||
|
||||
@ -10,8 +10,8 @@ namespace Admin.NET.Core;
|
||||
|
||||
public static class MiniExcelUtil
|
||||
{
|
||||
private const string _sheetName = "ImportTemplate";
|
||||
private const string _directory = "export";
|
||||
private const string SheetName = "ImportTemplate";
|
||||
private const string DirectoryName = "export";
|
||||
|
||||
/// <summary>
|
||||
/// 导出模板Excel
|
||||
@ -23,7 +23,7 @@ public static class MiniExcelUtil
|
||||
// 在内存中当开辟空间
|
||||
var memoryStream = new MemoryStream();
|
||||
// 将数据写到内存当中
|
||||
await memoryStream.SaveAsAsync(values, sheetName: _sheetName);
|
||||
await memoryStream.SaveAsAsync(values, sheetName: SheetName);
|
||||
// 从0的位置开始写入
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||
@ -41,7 +41,7 @@ public static class MiniExcelUtil
|
||||
{
|
||||
using MemoryStream stream = new MemoryStream();
|
||||
await file.CopyToAsync(stream);
|
||||
var res = await stream.QueryAsync<T>(sheetName: _sheetName);
|
||||
var res = await stream.QueryAsync<T>(sheetName: SheetName);
|
||||
return res.ToArray();
|
||||
}
|
||||
|
||||
@ -54,7 +54,7 @@ public static class MiniExcelUtil
|
||||
var fileName = string.Format("{0}.xlsx", YitIdHelper.NextId());
|
||||
try
|
||||
{
|
||||
var path = Path.Combine(App.WebHostEnvironment.WebRootPath, _directory);
|
||||
var path = Path.Combine(App.WebHostEnvironment.WebRootPath, DirectoryName);
|
||||
if (!Directory.Exists(path))
|
||||
Directory.CreateDirectory(path);
|
||||
var filePath = Path.Combine(path, fileName);
|
||||
@ -65,6 +65,6 @@ public static class MiniExcelUtil
|
||||
throw Oops.Oh("出现错误:" + error);
|
||||
}
|
||||
var host = CommonUtil.GetLocalhost();
|
||||
return $"{host}/{_directory}/{fileName}";
|
||||
return $"{host}/{DirectoryName}/{fileName}";
|
||||
}
|
||||
}
|
||||
@ -22,13 +22,13 @@ public static class TripleDES
|
||||
[Obsolete]
|
||||
public static void EncryptFile(string inputFile, string outputFile, string password)
|
||||
{
|
||||
using var tdes = new TripleDESCryptoServiceProvider();
|
||||
tdes.Mode = CipherMode.ECB;
|
||||
tdes.Padding = PaddingMode.PKCS7;
|
||||
tdes.Key = Encoding.UTF8.GetBytes(password);
|
||||
using var ties = new TripleDESCryptoServiceProvider();
|
||||
ties.Mode = CipherMode.ECB;
|
||||
ties.Padding = PaddingMode.PKCS7;
|
||||
ties.Key = Encoding.UTF8.GetBytes(password);
|
||||
using var inputFileStream = new FileStream(inputFile, FileMode.Open);
|
||||
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);
|
||||
}
|
||||
|
||||
@ -41,13 +41,13 @@ public static class TripleDES
|
||||
[Obsolete]
|
||||
public static void DecryptFile(string inputFile, string outputFile, string password)
|
||||
{
|
||||
using var tdes = new TripleDESCryptoServiceProvider();
|
||||
tdes.Mode = CipherMode.ECB;
|
||||
tdes.Padding = PaddingMode.PKCS7;
|
||||
tdes.Key = Encoding.UTF8.GetBytes(password);
|
||||
using var ties = new TripleDESCryptoServiceProvider();
|
||||
ties.Mode = CipherMode.ECB;
|
||||
ties.Padding = PaddingMode.PKCS7;
|
||||
ties.Key = Encoding.UTF8.GetBytes(password);
|
||||
using var encryptedFileStream = new FileStream(inputFile, FileMode.Open);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -11,81 +11,81 @@ namespace Admin.NET.Core;
|
||||
/// </summary>
|
||||
public static class VerifyFileExtensionName
|
||||
{
|
||||
private static readonly IDictionary<string, string> dics_ext = new Dictionary<string, string>();
|
||||
private static readonly IDictionary<string, HashSet<int>> ext_dics = new Dictionary<string, HashSet<int>>();
|
||||
private static readonly IDictionary<string, string> DicsExt = new Dictionary<string, string>();
|
||||
private static readonly IDictionary<string, HashSet<int>> ExtDics = new Dictionary<string, HashSet<int>>();
|
||||
|
||||
static VerifyFileExtensionName()
|
||||
{
|
||||
dics_ext.Add("FFD8FFE0", ".jpg");
|
||||
dics_ext.Add("FFD8FFE1", ".jpg");
|
||||
dics_ext.Add("89504E47", ".png");
|
||||
dics_ext.Add("47494638", ".gif");
|
||||
dics_ext.Add("49492A00", ".tif");
|
||||
dics_ext.Add("424D", ".bmp");
|
||||
DicsExt.Add("FFD8FFE0", ".jpg");
|
||||
DicsExt.Add("FFD8FFE1", ".jpg");
|
||||
DicsExt.Add("89504E47", ".png");
|
||||
DicsExt.Add("47494638", ".gif");
|
||||
DicsExt.Add("49492A00", ".tif");
|
||||
DicsExt.Add("424D", ".bmp");
|
||||
|
||||
// PS和CAD
|
||||
dics_ext.Add("38425053", ".psd");
|
||||
dics_ext.Add("41433130", ".dwg"); // CAD
|
||||
dics_ext.Add("252150532D41646F6265", ".ps");
|
||||
DicsExt.Add("38425053", ".psd");
|
||||
DicsExt.Add("41433130", ".dwg"); // CAD
|
||||
DicsExt.Add("252150532D41646F6265", ".ps");
|
||||
|
||||
// 办公文档类
|
||||
dics_ext.Add("D0CF11E0", ".ppt,.doc,.xls"); // ppt、doc、xls
|
||||
dics_ext.Add("504B0304", ".pptx,.docx,.xlsx"); // pptx、docx、xlsx
|
||||
DicsExt.Add("D0CF11E0", ".ppt,.doc,.xls"); // ppt、doc、xls
|
||||
DicsExt.Add("504B0304", ".pptx,.docx,.xlsx"); // pptx、docx、xlsx
|
||||
|
||||
/* 注意由于文本文档录入内容过多,则读取文件头时较为多变-START */
|
||||
dics_ext.Add("0D0A0D0A", ".txt"); // txt
|
||||
dics_ext.Add("0D0A2D2D", ".txt"); // txt
|
||||
dics_ext.Add("0D0AB4B4", ".txt"); // txt
|
||||
dics_ext.Add("B4B4BDA8", ".txt"); // 文件头部为汉字
|
||||
dics_ext.Add("73646673", ".txt"); // txt,文件头部为英文字母
|
||||
dics_ext.Add("32323232", ".txt"); // txt,文件头部内容为数字
|
||||
dics_ext.Add("0D0A09B4", ".txt"); // txt,文件头部内容为数字
|
||||
dics_ext.Add("3132330D", ".txt"); // txt,文件头部内容为数字
|
||||
DicsExt.Add("0D0A0D0A", ".txt"); // txt
|
||||
DicsExt.Add("0D0A2D2D", ".txt"); // txt
|
||||
DicsExt.Add("0D0AB4B4", ".txt"); // txt
|
||||
DicsExt.Add("B4B4BDA8", ".txt"); // 文件头部为汉字
|
||||
DicsExt.Add("73646673", ".txt"); // txt,文件头部为英文字母
|
||||
DicsExt.Add("32323232", ".txt"); // txt,文件头部内容为数字
|
||||
DicsExt.Add("0D0A09B4", ".txt"); // txt,文件头部内容为数字
|
||||
DicsExt.Add("3132330D", ".txt"); // txt,文件头部内容为数字
|
||||
/* 注意由于文本文档录入内容过多,则读取文件头时较为多变-END */
|
||||
|
||||
dics_ext.Add("7B5C727466", ".rtf"); // 日记本
|
||||
DicsExt.Add("7B5C727466", ".rtf"); // 日记本
|
||||
|
||||
dics_ext.Add("255044462D312E", ".pdf");
|
||||
DicsExt.Add("255044462D312E", ".pdf");
|
||||
|
||||
// 视频或音频类
|
||||
dics_ext.Add("3026B275", ".wma");
|
||||
dics_ext.Add("57415645", ".wav");
|
||||
dics_ext.Add("41564920", ".avi");
|
||||
dics_ext.Add("4D546864", ".mid");
|
||||
dics_ext.Add("2E524D46", ".rm");
|
||||
dics_ext.Add("000001BA", ".mpg");
|
||||
dics_ext.Add("000001B3", ".mpg");
|
||||
dics_ext.Add("6D6F6F76", ".mov");
|
||||
dics_ext.Add("3026B2758E66CF11", ".asf");
|
||||
DicsExt.Add("3026B275", ".wma");
|
||||
DicsExt.Add("57415645", ".wav");
|
||||
DicsExt.Add("41564920", ".avi");
|
||||
DicsExt.Add("4D546864", ".mid");
|
||||
DicsExt.Add("2E524D46", ".rm");
|
||||
DicsExt.Add("000001BA", ".mpg");
|
||||
DicsExt.Add("000001B3", ".mpg");
|
||||
DicsExt.Add("6D6F6F76", ".mov");
|
||||
DicsExt.Add("3026B2758E66CF11", ".asf");
|
||||
|
||||
// 压缩包
|
||||
dics_ext.Add("52617221", ".rar");
|
||||
dics_ext.Add("504B03040A000000", ".zip");
|
||||
dics_ext.Add("504B030414000000", ".zip");
|
||||
dics_ext.Add("1F8B08", ".gz");
|
||||
DicsExt.Add("52617221", ".rar");
|
||||
DicsExt.Add("504B03040A000000", ".zip");
|
||||
DicsExt.Add("504B030414000000", ".zip");
|
||||
DicsExt.Add("1F8B08", ".gz");
|
||||
|
||||
// 程序文件
|
||||
dics_ext.Add("3C3F786D6C", ".xml");
|
||||
dics_ext.Add("68746D6C3E", ".html");
|
||||
dics_ext.Add("04034b50", ".apk");
|
||||
DicsExt.Add("3C3F786D6C", ".xml");
|
||||
DicsExt.Add("68746D6C3E", ".html");
|
||||
DicsExt.Add("04034b50", ".apk");
|
||||
//dics_ext.Add("7061636B", ".java");
|
||||
//dics_ext.Add("3C254020", ".jsp");
|
||||
//dics_ext.Add("4D5A9000", ".exe");
|
||||
|
||||
dics_ext.Add("44656C69766572792D646174653A", ".eml"); // 邮件
|
||||
dics_ext.Add("5374616E64617264204A", ".mdb"); // Access数据库文件
|
||||
DicsExt.Add("44656C69766572792D646174653A", ".eml"); // 邮件
|
||||
DicsExt.Add("5374616E64617264204A", ".mdb"); // Access数据库文件
|
||||
|
||||
dics_ext.Add("46726F6D", ".mht");
|
||||
dics_ext.Add("4D494D45", ".mhtml");
|
||||
DicsExt.Add("46726F6D", ".mht");
|
||||
DicsExt.Add("4D494D45", ".mhtml");
|
||||
|
||||
foreach (var dics in dics_ext)
|
||||
foreach (var dics in DicsExt)
|
||||
{
|
||||
foreach (var ext in dics.Value.Split(","))
|
||||
{
|
||||
if (!ext_dics.ContainsKey(ext))
|
||||
ext_dics.Add(ext, new HashSet<int> { dics.Key.Length / 2 });
|
||||
if (!ExtDics.ContainsKey(ext))
|
||||
ExtDics.Add(ext, new HashSet<int> { dics.Key.Length / 2 });
|
||||
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;
|
||||
|
||||
suffix = suffix.ToLower();
|
||||
if (!ext_dics.ContainsKey(suffix))
|
||||
return false;
|
||||
if (!ExtDics.TryGetValue(suffix, out HashSet<int> dic)) return false;
|
||||
|
||||
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);
|
||||
// string fileType = System.Text.Encoding.UTF8.GetString(b);
|
||||
string fileKey = GetFileHeader(b);
|
||||
if (dics_ext.ContainsKey(fileKey))
|
||||
if (DicsExt.ContainsKey(fileKey))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -152,14 +151,12 @@ public static class VerifyFileExtensionName
|
||||
if (src == null || src.Length <= 0)
|
||||
return null;
|
||||
|
||||
string hv;
|
||||
for (int i = 0; i < src.Length; i++)
|
||||
{
|
||||
// 以十六进制(基数 16)无符号整数形式返回一个整数参数的字符串表示形式,并转换为大写
|
||||
hv = Convert.ToString(src[i] & 0xFF, 16).ToUpper();
|
||||
if (hv.Length < 2)
|
||||
builder.Append(0);
|
||||
builder.Append(hv);
|
||||
string hVal = Convert.ToString(src[i] & 0xFF, 16).ToUpper();
|
||||
if (hVal.Length < 2) builder.Append(0);
|
||||
builder.Append(hVal);
|
||||
}
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
@ -89,15 +89,9 @@ public class XlsxFileResultBase : ActionResult
|
||||
var response = context.HttpContext.Response;
|
||||
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)))
|
||||
{
|
||||
downloadFileName += ".xlsx";
|
||||
}
|
||||
if (string.IsNullOrEmpty(Path.GetExtension(downloadFileName))) downloadFileName += ".xlsx";
|
||||
|
||||
context.HttpContext.Response.Headers.Append("Content-Disposition", new[] { "attachment; filename=" + HttpUtility.UrlEncode(downloadFileName) });
|
||||
await stream.CopyToAsync(context.HttpContext.Response.Body);
|
||||
|
||||
@ -12,7 +12,7 @@ namespace @Model.NameSpace;
|
||||
public class @(@Model.ClassName)Dto
|
||||
{
|
||||
@foreach (var column in Model.TableField){
|
||||
if(@column.EffectType == "fk" && @column.FkEntityName != "" && @column.FkColumnName != ""){
|
||||
if(@column.EffectType == "ForeignKey" && @column.FkEntityName != "" && @column.FkColumnName != ""){
|
||||
@:/// <summary>
|
||||
@:/// @column.ColumnComment
|
||||
@:/// </summary>
|
||||
|
||||
@ -3,11 +3,11 @@ import { BasicColumn, FormSchema } from '/@@/components/Table';
|
||||
@foreach (var column in Model.TableField){
|
||||
if(@column.EffectType == "Upload"){
|
||||
@: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)';
|
||||
}else if(@column.EffectType == "Select"){
|
||||
}else if(@column.EffectType == "DictSelector"){
|
||||
@: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)';
|
||||
}else if(@column.EffectType == "ConstSelector"){
|
||||
@:import { codeToName, getSelector } from '/@@/utils/helper/constSelectorHelper';
|
||||
@ -24,7 +24,7 @@ export const columns: BasicColumn[] = [
|
||||
@:sorter: true,
|
||||
if(@column.EffectType == "Upload"){
|
||||
@:slots: { customRender: '@(@column.LowerPropertyName)' },
|
||||
}else if(@column.EffectType == "fk"){
|
||||
}else if(@column.EffectType == "ForeignKey"){
|
||||
@:customRender: ({ record }) => {
|
||||
@:return record.fk@(@column.PropertyName).@(@column.LowerFkColumnName);
|
||||
@:},
|
||||
@ -49,14 +49,14 @@ export const searchFormSchema: FormSchema[] = [
|
||||
@:field: '@column.LowerPropertyName',
|
||||
@:label: '@column.ColumnComment',
|
||||
@:colProps: { span: 8 },
|
||||
if(@column.EffectType == "fk"){
|
||||
if(@column.EffectType == "ForeignKey"){
|
||||
@:component: 'ApiSelect',
|
||||
@:componentProps: {
|
||||
@:api: get@(@column.FkEntityName)Dropdown,
|
||||
@:labelField: 'label',
|
||||
@:valueField: 'value',
|
||||
@:},
|
||||
}else if(@column.EffectType == "Select"){
|
||||
}else if(@column.EffectType == "DictSelector"){
|
||||
@:component: 'ApiSelect',
|
||||
@:componentProps: {
|
||||
@:api: getDataList,
|
||||
@ -75,7 +75,7 @@ if(@column.EffectType == "fk"){
|
||||
@:value: 'code',
|
||||
@:},
|
||||
@:},
|
||||
}else if(@column.EffectType == "ApiTreeSelect"){
|
||||
}else if(@column.EffectType == "ApiTreeSelector"){
|
||||
@:component: '@(@column.EffectType)',
|
||||
@:componentProps: {
|
||||
@:api: get@(@column.FkEntityName)Tree,
|
||||
@ -100,14 +100,14 @@ export const formSchema: FormSchema[] = [
|
||||
@:{
|
||||
@:label: '@column.ColumnComment',
|
||||
@:field: '@column.LowerPropertyName',
|
||||
if(@column.EffectType == "fk"){
|
||||
if(@column.EffectType == "ForeignKey"){
|
||||
@:component: 'ApiSelect',
|
||||
@:componentProps: {
|
||||
@:api: get@(@column.FkEntityName)Dropdown,
|
||||
@:labelField: 'label',
|
||||
@:valueField: 'value',
|
||||
@:},
|
||||
}else if(@column.EffectType == "Select"){
|
||||
}else if(@column.EffectType == "DictSelector"){
|
||||
@:component: 'ApiSelect',
|
||||
@:componentProps: {
|
||||
@:api: getDataList,
|
||||
@ -126,7 +126,7 @@ if(@column.EffectType == "fk"){
|
||||
@:value: 'code',
|
||||
@:},
|
||||
@:},
|
||||
}else if(@column.EffectType == "ApiTreeSelect"){
|
||||
}else if(@column.EffectType == "ApiTreeSelector"){
|
||||
@:component: '@(@column.EffectType)',
|
||||
@:componentProps: {
|
||||
@:api: get@(@column.FkEntityName)Tree,
|
||||
|
||||
@ -15,7 +15,7 @@ public class @(@Model.ClassName)Output
|
||||
@:/// <summary>
|
||||
@:/// @column.ColumnComment
|
||||
@:/// </summary>
|
||||
if(column.EffectType == "fk")
|
||||
if(column.EffectType == "ForeignKey")
|
||||
{
|
||||
@:public @column.NetType @column.PropertyName { get; set; }
|
||||
@:
|
||||
@ -27,7 +27,7 @@ if(column.EffectType == "fk")
|
||||
}else if(column.EffectType == "Upload"){
|
||||
@:public @column.NetType @column.PropertyName { 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; }
|
||||
@:
|
||||
@:/// <summary>
|
||||
@ -43,7 +43,7 @@ if(column.EffectType == "fk")
|
||||
|
||||
|
||||
@foreach (var column in Model.TableField){
|
||||
if (@column.EffectType == "ApiTreeSelect"){
|
||||
if (@column.EffectType == "ApiTreeSelector"){
|
||||
@:// 使用实际实体@(@column.FkTableName),所以这里就删了
|
||||
@:/*
|
||||
@:[SugarTable("@(@column.FkTableName)")]
|
||||
|
||||
@ -85,10 +85,10 @@ if (@column.QueryWhether == "Y"){
|
||||
@if(Model.IsJoinTable){
|
||||
@://处理外键和TreeSelector相关字段的连接
|
||||
@foreach (var column in Model.TableField){
|
||||
if(@column.EffectType == "fk"){
|
||||
if(@column.EffectType == "ForeignKey"){
|
||||
joinTableName += ", " + column.PropertyName.ToLower();
|
||||
@:.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();
|
||||
@:.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
|
||||
@:{
|
||||
@foreach (var column in Model.TableField){
|
||||
if(@column.EffectType == "fk"){
|
||||
if(@column.EffectType == "ForeignKey"){
|
||||
@:@(@column.PropertyName) = u.@(@column.PropertyName),
|
||||
@:@(@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)@(@column.DisplayColumn) = @(@column.PropertyName.ToLower()).@(@column.DisplayColumn),
|
||||
} else if(@column.NetType?.TrimEnd('?').EndsWith("Enum") == true){
|
||||
@ -110,7 +110,7 @@ if (@column.QueryWhether == "Y"){
|
||||
}
|
||||
@:});
|
||||
@foreach (var column in Model.TableField){
|
||||
if(@column.EffectType == "fk"){
|
||||
if(@column.EffectType == "ForeignKey"){
|
||||
|
||||
}else if(@column.EffectType == "Upload"){
|
||||
@://.Mapper(c => c.@(@column.PropertyName)Attachment, c => c.@(@column.PropertyName))
|
||||
@ -119,7 +119,7 @@ if (@column.QueryWhether == "Y"){
|
||||
} else {
|
||||
@:.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);
|
||||
} else {
|
||||
@:return await query.OrderBuilder(input).ToPagedListAsync(input.Page, input.PageSize);
|
||||
@ -216,7 +216,7 @@ if (@column.ColumnKey == "True"){
|
||||
}
|
||||
|
||||
@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>
|
||||
@:/// 获取@(@column.ColumnComment)列表
|
||||
@:/// </summary>
|
||||
@ -254,7 +254,7 @@ if(@column.EffectType == "Upload"){
|
||||
}
|
||||
|
||||
@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);}
|
||||
@:[ApiDescriptionSettings(Name = "@(LowerFirstLetter(@column.FkEntityName))Tree", Description = "获取@(@column.ColumnComment)列表", Order = 920), HttpGet]
|
||||
@:[DisplayName("获取@(@column.FkEntityName)Tree")]
|
||||
|
||||
@ -22,9 +22,9 @@ enum Api {
|
||||
@:Exists@(RemoteField) = '/api/@(@Model.LowerClassName)/exists@(RemoteField)',
|
||||
}
|
||||
@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',
|
||||
}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);}
|
||||
@:Get@(@column.FkEntityName)Tree = '/api/@(@Model.LowerClassName)/@(LowerFirstLetter(@column.FkEntityName))Tree',
|
||||
}else if(@column.EffectType == "Upload"){
|
||||
@ -74,13 +74,13 @@ export const detail@(@Model.ClassName) = (id: any) =>
|
||||
});
|
||||
|
||||
@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 = () =>
|
||||
@:request({
|
||||
@:url: Api.Get@(@column.FkEntityName)@(@column.PropertyName)Dropdown,
|
||||
@: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);}
|
||||
@:export const get@(@column.FkEntityName)Tree = () =>
|
||||
@:request({
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
</el-form-item>
|
||||
}else{
|
||||
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-form-item label="@column.ColumnComment" prop="@(@column.LowerPropertyName)">
|
||||
@:<el-select clearable filterable v-model="state.ruleForm.@(@column.LowerPropertyName)" placeholder="请选择@(@column.ColumnComment)">
|
||||
@ -47,7 +47,7 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</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-form-item label="@column.ColumnComment" prop="@(@column.LowerPropertyName)">
|
||||
<el-cascader
|
||||
@ -87,7 +87,7 @@
|
||||
@:show-word-limit clearable />
|
||||
</el-form-item>
|
||||
</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-form-item label="@column.ColumnComment" prop="@(@column.LowerPropertyName)">
|
||||
@:<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){
|
||||
@: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,
|
||||
}
|
||||
@:add@(@Model.ClassName),
|
||||
@ -207,7 +207,7 @@ if(@Model.TableField.Any(x=>x.EffectType == "Upload")){
|
||||
@if(@Model.TableField.Any(x=>x.EffectType == "ConstSelector")){
|
||||
@: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';
|
||||
}
|
||||
@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';
|
||||
}
|
||||
@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);}
|
||||
}
|
||||
}
|
||||
@ -268,7 +268,7 @@ const rules = ref<FormRules>({
|
||||
@:@column.LowerPropertyName: [
|
||||
@foreach(var rule in @column.RuleItems){
|
||||
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";
|
||||
}
|
||||
if(rule.Type=="required"){
|
||||
@ -389,7 +389,7 @@ const submit = async () => {
|
||||
};
|
||||
|
||||
@foreach (var column in Model.TableField){
|
||||
if(@column.EffectType == "fk" && @column.WhetherAddUpdate == "Y"){
|
||||
if(@column.EffectType == "ForeignKey" && @column.WhetherAddUpdate == "Y"){
|
||||
@://下拉列表@(@column.ColumnComment)
|
||||
@:const @LowerFirstLetter(@column.FkEntityName)@(@column.PropertyName)DropdownList = ref<any>([]);
|
||||
@: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){
|
||||
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);}
|
||||
@:const @LowerFirstLetter(@column.FkEntityName)TreeData = ref<any>([]);
|
||||
@:const get@(@column.FkEntityName)TreeData = async () => {
|
||||
|
||||
@ -44,7 +44,7 @@
|
||||
@:<el-input-number v-model="queryParams.@(@column.LowerPropertyName)" clearable="" placeholder="请输入@(@column.ColumnComment)"/>
|
||||
@:
|
||||
</el-form-item>
|
||||
}else if(@column.EffectType == "fk"){
|
||||
}else if(@column.EffectType == "ForeignKey"){
|
||||
@:<el-form-item label="@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" />
|
||||
@ -52,7 +52,7 @@
|
||||
</el-select>
|
||||
@:
|
||||
</el-form-item>
|
||||
}else if(@column.EffectType == "Select"){
|
||||
}else if(@column.EffectType == "DictSelector"){
|
||||
@:<el-form-item label="@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}`" />
|
||||
@ -126,7 +126,7 @@
|
||||
<el-table-column type="index" label="序号" width="55" align="center"/>
|
||||
@foreach (var column in Model.TableField){
|
||||
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="">
|
||||
@:<template #default="scope">
|
||||
if(@column.EffectType == "Upload"){
|
||||
@ -140,9 +140,9 @@
|
||||
@::initial-index="0"
|
||||
@:fit="scale-down"
|
||||
@:preview-teleported=""/>
|
||||
}else if(@column.EffectType == "fk"){
|
||||
}else if(@column.EffectType == "ForeignKey"){
|
||||
@:<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>
|
||||
}else if(@column.EffectType == "Switch"){
|
||||
@:<el-tag v-if="scope.row.@(@column.LowerPropertyName)"> 是 </el-tag>
|
||||
@ -155,7 +155,7 @@
|
||||
@:
|
||||
</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="" >
|
||||
@:<template #default="scope">
|
||||
@:<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")){
|
||||
@: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';
|
||||
}
|
||||
@if(@Model.TableField.Any(x=>x.EffectType == "EnumSelector")){
|
||||
@ -245,7 +245,7 @@
|
||||
import editDialog from '/@@/views/@(@Model.PagePath)/@(@Model.LowerClassName)/component/editDialog.vue'
|
||||
import { page@(@Model.ClassName), delete@(@Model.ClassName) } from '/@@/api/@(@Model.PagePath)/@(@Model.LowerClassName)';
|
||||
@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)';
|
||||
}
|
||||
}
|
||||
@ -372,7 +372,7 @@
|
||||
};
|
||||
|
||||
@foreach (var column in Model.QueryWhetherList){
|
||||
if(@column.EffectType == "fk"){
|
||||
if(@column.EffectType == "ForeignKey"){
|
||||
@:const @LowerFirstLetter(@column.FkEntityName)@(@column.PropertyName)DropdownList = ref<any>([]);
|
||||
@:const get@(@column.FkEntityName)@(@column.PropertyName)DropdownList = async () => {
|
||||
@: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
Loading…
Reference in New Issue
Block a user