😘 恢复在线用户管理机制
This commit is contained in:
parent
ea78c0c3aa
commit
8b55d8078a
@ -31,9 +31,9 @@
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
|
||||
</Content>
|
||||
<Content Remove="Configuration\HttpRemotes.*.json" />
|
||||
<Content Include="Configuration\HttpRemotes.*.json">
|
||||
<DependentUpon>HttpRemotes.json</DependentUpon>
|
||||
<Content Remove="Configuration\HttpRemote.*.json" />
|
||||
<Content Include="Configuration\HttpRemote.*.json">
|
||||
<DependentUpon>HttpRemote.json</DependentUpon>
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
"Account": "",
|
||||
"Password": ""
|
||||
},
|
||||
"HttpRemotes": {
|
||||
"HttpRemote": {
|
||||
"WorkWeixin": { // 企业微信
|
||||
"EnabledLog": true,
|
||||
"UseCookies": false,
|
||||
@ -8,7 +8,7 @@
|
||||
"Account": "",
|
||||
"Password": ""
|
||||
},
|
||||
"HttpRemotes": {
|
||||
"HttpRemote": {
|
||||
"WorkWeixin": { // 企业微信
|
||||
"EnabledLog": true,
|
||||
"UseCookies": false,
|
||||
@ -4,6 +4,7 @@
|
||||
//
|
||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||
|
||||
global using Admin.NET.Application.Entity;
|
||||
global using Admin.NET.Core;
|
||||
global using Admin.NET.Core.Service;
|
||||
global using Furion;
|
||||
@ -11,6 +12,7 @@ global using Furion.DependencyInjection;
|
||||
global using Furion.DynamicApiController;
|
||||
global using Furion.EventBus;
|
||||
global using Furion.FriendlyException;
|
||||
global using Mapster;
|
||||
global using Microsoft.AspNetCore.Authorization;
|
||||
global using Microsoft.AspNetCore.Mvc;
|
||||
global using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
@ -11,7 +11,7 @@ namespace Admin.NET.Application;
|
||||
/// <summary>
|
||||
/// 远程请求配置
|
||||
/// </summary>
|
||||
public sealed class HttpRemotesOptions : IConfigurableOptions
|
||||
public sealed class HttpRemoteOptions : IConfigurableOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// 企业微信
|
||||
@ -24,7 +24,6 @@ public class AppAuthService : IDynamicApiController, ITransient
|
||||
private readonly SysRoleService _sysRoleService;
|
||||
private readonly SysOnlineUserService _sysOnlineUserService;
|
||||
private readonly SysConfigService _sysConfigService;
|
||||
private readonly SysAuthService _sysAuthService;
|
||||
private readonly ICaptcha _captcha;
|
||||
private readonly SysCacheService _sysCacheService;
|
||||
|
||||
@ -34,11 +33,9 @@ public class AppAuthService : IDynamicApiController, ITransient
|
||||
SysRoleService sysRoleService,
|
||||
SysOnlineUserService sysOnlineUserService,
|
||||
SysConfigService sysConfigService,
|
||||
SysAuthService sysAuthService,
|
||||
ICaptcha captcha,
|
||||
SysCacheService sysCacheService)
|
||||
{
|
||||
_sysAuthService = sysAuthService;
|
||||
_appUserManager = appUserManager;
|
||||
_sysUserRep = sysUserRep;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
@ -82,7 +79,7 @@ public class AppAuthService : IDynamicApiController, ITransient
|
||||
throw Oops.Oh(ErrorCodeEnum.Z1003);
|
||||
|
||||
// 账号是否存在
|
||||
var user = await _sysUserRep.AsQueryable().Includes(t => t.SysOrg).IgnoreTenant().FirstAsync(u => u.Account.Equals(input.Account));
|
||||
var user = await _sysUserRep.AsQueryable().Includes(t => t.SysOrg).ClearFilter<ITenantIdFilter>().FirstAsync(u => u.Account.Equals(input.Account));
|
||||
_ = user ?? throw Oops.Oh(ErrorCodeEnum.D0009);
|
||||
|
||||
// 账号是否被冻结
|
||||
@ -103,7 +100,8 @@ public class AppAuthService : IDynamicApiController, ITransient
|
||||
|
||||
// 登录成功则清空密码错误次数
|
||||
_sysCacheService.Remove(keyPasswordErrorTimes);
|
||||
return await _sysAuthService.CreateToken(user, LoginModeEnum.APP);
|
||||
|
||||
return await CreateToken(user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -149,16 +147,63 @@ public class AppAuthService : IDynamicApiController, ITransient
|
||||
throw Oops.Oh("验证码错误!");
|
||||
|
||||
// 账号是否存在
|
||||
var user = await _sysUserRep.AsQueryable().Includes(u => u.SysOrg).IgnoreTenant().FirstAsync(u => u.Phone.Equals(input.Phone));
|
||||
var user = await _sysUserRep.AsQueryable().Includes(u => u.SysOrg).ClearFilter<ITenantIdFilter>().FirstAsync(u => u.Phone.Equals(input.Phone));
|
||||
_ = user ?? throw Oops.Oh(ErrorCodeEnum.D0009);
|
||||
return await _sysAuthService.CreateToken(user, LoginModeEnum.APP);
|
||||
|
||||
return await CreateToken(user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前登陆用户信息 🔖
|
||||
/// 生成Token令牌 🔖
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="loginMode"></param>
|
||||
/// <returns></returns>
|
||||
[NonAction]
|
||||
public virtual async Task<LoginOutput> CreateToken(SysUser user, LoginModeEnum loginMode = LoginModeEnum.APP)
|
||||
{
|
||||
// 单用户登录
|
||||
await _sysOnlineUserService.SingleLogin(user.Id, loginMode);
|
||||
|
||||
// 生成Token令牌
|
||||
var tokenExpire = await _sysConfigService.GetTokenExpire();
|
||||
var accessToken = JWTEncryption.Encrypt(new Dictionary<string, object>
|
||||
{
|
||||
{ AppClaimConst.UserId, user.Id },
|
||||
{ AppClaimConst.TenantId, user.TenantId },
|
||||
{ AppClaimConst.Account, user.Account },
|
||||
{ AppClaimConst.RealName, user.RealName },
|
||||
{ AppClaimConst.AccountType, user.AccountType },
|
||||
{ AppClaimConst.OrgId, user.OrgId },
|
||||
{ AppClaimConst.OrgName, user.SysOrg?.Name },
|
||||
{ AppClaimConst.OrgType, user.SysOrg?.Type },
|
||||
{ AppClaimConst.OrgLevel, user.SysOrg?.Level },
|
||||
{ ClaimConst.LoginMode, loginMode },
|
||||
{ ClaimConst.TokenVersion, user.TokenVersion },
|
||||
}, tokenExpire);
|
||||
|
||||
// 生成刷新Token令牌
|
||||
var refreshTokenExpire = await _sysConfigService.GetRefreshTokenExpire();
|
||||
var refreshToken = JWTEncryption.GenerateRefreshToken(accessToken, refreshTokenExpire);
|
||||
|
||||
// 设置响应报文头
|
||||
_httpContextAccessor.HttpContext.SetTokensOfResponseHeaders(accessToken, refreshToken);
|
||||
|
||||
// 缓存用户Token版本
|
||||
_sysCacheService.Set($"{CacheConst.KeyUserToken}{user.Id}", $"{user.TokenVersion}");
|
||||
|
||||
return new LoginOutput
|
||||
{
|
||||
AccessToken = accessToken,
|
||||
RefreshToken = refreshToken
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取登录账号 🔖
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[DisplayName("获取当前登陆用户信息")]
|
||||
[DisplayName("获取登录账号")]
|
||||
public virtual async Task<LoginUserOutput> GetUserInfo()
|
||||
{
|
||||
var user = await _sysUserRep.GetByIdAsync(_appUserManager.UserId) ?? throw Oops.Oh(ErrorCodeEnum.D1011).StatusCode(401);
|
||||
@ -171,6 +216,8 @@ public class AppAuthService : IDynamicApiController, ITransient
|
||||
.LeftJoin<SysRole>((u, a) => u.RoleId == a.Id)
|
||||
.Where(u => u.UserId == user.Id)
|
||||
.Select((u, a) => new RoleDto { Id = a.Id, Name = a.Name, Code = a.Code }).ToListAsync();
|
||||
// 接口集合
|
||||
var apis = (await _sysRoleService.GetUserApiList())[0];
|
||||
|
||||
return new LoginUserOutput
|
||||
{
|
||||
@ -188,8 +235,8 @@ public class AppAuthService : IDynamicApiController, ITransient
|
||||
OrgName = org?.Name,
|
||||
OrgType = org?.Type,
|
||||
PosName = pos?.Name,
|
||||
Roles = roles,
|
||||
Apis = _appUserManager.Permissions,
|
||||
Apis = apis,
|
||||
Roles = roles
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -6,45 +6,16 @@
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Admin.NET.Application;
|
||||
namespace Admin.NET.Application.Service.App;
|
||||
|
||||
/// <summary>
|
||||
/// 业务层用户状态管理
|
||||
/// </summary>
|
||||
/// <param name="sysCacheService"></param>
|
||||
/// <param name="httpContextAccessor"></param>
|
||||
public class AppUserManager(
|
||||
SysCacheService sysCacheService,
|
||||
IHttpContextAccessor httpContextAccessor)
|
||||
: UserManager(sysCacheService, httpContextAccessor), IUserManagerExtProps
|
||||
public class AppUserManager : UserManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 工号
|
||||
/// </summary>
|
||||
public string JobNum => base.GetExtProp<string>(nameof(JobNum));
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
/// <summary>
|
||||
/// 最新登录Ip
|
||||
/// </summary>
|
||||
public string LastLoginIp => base.GetExtProp<string>(nameof(LastLoginIp));
|
||||
|
||||
/// <summary>
|
||||
/// 最新登录Ip
|
||||
/// </summary>
|
||||
public string LastLoginDevice => base.GetExtProp<string>(nameof(LastLoginDevice));
|
||||
|
||||
/// <summary>
|
||||
/// 初始化扩展属性
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <returns></returns>
|
||||
public Dictionary<string, object> GetInitExtProps(SysUser user)
|
||||
public AppUserManager(IHttpContextAccessor httpContextAccessor) : base(httpContextAccessor)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
{ nameof(JobNum), user.JobNum },
|
||||
{ nameof(LastLoginIp), user.LastLoginIp },
|
||||
{ nameof(LastLoginDevice), user.LastLoginDevice },
|
||||
};
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
|
||||
// 扩展属性
|
||||
}
|
||||
@ -28,7 +28,7 @@ public class Startup : AppStartup
|
||||
});
|
||||
|
||||
// 添加远程请求服务配置
|
||||
services.AddConfigurableOptions<HttpRemotesOptions>();
|
||||
services.AddConfigurableOptions<HttpRemoteOptions>();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
|
||||
@ -53,7 +53,7 @@
|
||||
<PackageReference Include="SKIT.FlurlHttpClient.Wechat.Api" Version="3.11.0" />
|
||||
<PackageReference Include="SKIT.FlurlHttpClient.Wechat.TenpayV3" Version="3.13.0" />
|
||||
<PackageReference Include="SqlSugar.MongoDbCore" Version="5.1.4.255" />
|
||||
<PackageReference Include="SqlSugarCore" Version="5.1.4.201" />
|
||||
<PackageReference Include="SqlSugarCore" Version="5.1.4.202" />
|
||||
<PackageReference Include="SSH.NET" Version="2025.0.0" />
|
||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.6.7" />
|
||||
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
||||
|
||||
@ -16,18 +16,12 @@ namespace Admin.NET.Core.ApiKeyAuth;
|
||||
public class ApiKeyAuthenticationHandler : AuthenticationHandler<ApiKeyAuthenticationOptions>
|
||||
{
|
||||
public const string AuthenticationScheme = "ApiKey";
|
||||
private readonly SysAuthService _sysAuthService;
|
||||
private readonly UserManager _userManager;
|
||||
|
||||
public ApiKeyAuthenticationHandler(IOptionsMonitor<ApiKeyAuthenticationOptions> options,
|
||||
ILoggerFactory logger,
|
||||
UrlEncoder encoder,
|
||||
UserManager userManager,
|
||||
SysAuthService sysAuthService)
|
||||
UrlEncoder encoder)
|
||||
: base(options, logger, encoder)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_sysAuthService = sysAuthService;
|
||||
}
|
||||
|
||||
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||
@ -45,12 +39,15 @@ public class ApiKeyAuthenticationHandler : AuthenticationHandler<ApiKeyAuthentic
|
||||
identity.AddClaims(
|
||||
[
|
||||
new Claim(ClaimConst.UserId, acsInfo.BindUserId + ""),
|
||||
new Claim(ClaimConst.TokenVersion, "1")
|
||||
new Claim(ClaimConst.TenantId, acsInfo.BindTenantId + ""),
|
||||
new Claim(ClaimConst.Account, acsInfo.BindUser.Account + ""),
|
||||
new Claim(ClaimConst.RealName, acsInfo.BindUser.RealName),
|
||||
new Claim(ClaimConst.AccountType, ((int)acsInfo.BindUser.AccountType).ToString()),
|
||||
new Claim(ClaimConst.OrgId, acsInfo.BindUser.OrgId + ""),
|
||||
new Claim(ClaimConst.OrgName, acsInfo.BindUser.SysOrg?.Name + ""),
|
||||
new Claim(ClaimConst.OrgType, acsInfo.BindUser.SysOrg?.Type + ""),
|
||||
new Claim(ClaimConst.TokenVersion,"1")
|
||||
]);
|
||||
|
||||
// 设置用户状态缓存
|
||||
if (!_userManager.Exist(acsInfo.Id)) await _sysAuthService.CreateToken(acsInfo.BindUser);
|
||||
|
||||
var user = new ClaimsPrincipal(identity);
|
||||
return AuthenticateResult.Success(new AuthenticationTicket(user, AuthenticationScheme));
|
||||
}
|
||||
|
||||
@ -0,0 +1,92 @@
|
||||
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
||||
//
|
||||
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
||||
//
|
||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||
|
||||
namespace Admin.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// 通用接口参数验证特性类
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class CommonValidationAttribute : ValidationAttribute
|
||||
{
|
||||
private readonly Dictionary<string, string> _conditions;
|
||||
private static readonly Dictionary<string, Delegate> CompiledConditions = [];
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="conditionPairs">条件对参数,长度必须为偶数<br/>
|
||||
/// 奇数字符串参数:动态条件<br/>
|
||||
/// 偶数字符串参数:提示消息
|
||||
/// </param>
|
||||
/// <example>
|
||||
/// <code lang="C">
|
||||
/// public class ModelInput {
|
||||
///
|
||||
///
|
||||
/// public string A { get; set; }
|
||||
///
|
||||
///
|
||||
/// [CommonValidation(
|
||||
/// "A == 1 <value>&&</value> B == null", "当 A == 1时,B不能为空",
|
||||
/// "C == 2 <value>&&</value> B == null", "当 C == 2时,B不能为空"
|
||||
/// )]
|
||||
/// public string B { get; set; }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public CommonValidationAttribute(params string[] conditionPairs)
|
||||
{
|
||||
if (conditionPairs.Length % 2 != 0) throw new ArgumentException("条件对必须以偶数个字符串的形式提供。");
|
||||
|
||||
var conditions = new Dictionary<string, string>();
|
||||
for (int i = 0; i < conditionPairs.Length; i += 2)
|
||||
conditions.Add(conditionPairs[i], conditionPairs[i + 1]);
|
||||
|
||||
_conditions = conditions;
|
||||
}
|
||||
|
||||
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
|
||||
{
|
||||
foreach (var (expr, errorMessage) in _conditions)
|
||||
{
|
||||
var conditionKey = $"{validationContext.ObjectType.FullName}.{expr}";
|
||||
if (!CompiledConditions.TryGetValue(conditionKey, out var condition))
|
||||
{
|
||||
condition = CreateCondition(validationContext.ObjectType, expr);
|
||||
CompiledConditions[conditionKey] = condition;
|
||||
}
|
||||
|
||||
if ((bool)condition.DynamicInvoke(validationContext.ObjectInstance)!)
|
||||
{
|
||||
return new ValidationResult(errorMessage ?? $"[{validationContext.MemberName}]校验失败");
|
||||
}
|
||||
}
|
||||
|
||||
return ValidationResult.Success;
|
||||
}
|
||||
|
||||
private Delegate CreateCondition(Type modelType, string expression)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 创建参数表达式
|
||||
var parameter = Expression.Parameter(typeof(object), "x");
|
||||
|
||||
// 构建 Lambda 表达式
|
||||
var lambda = DynamicExpressionParser.ParseLambda([Expression.Parameter(modelType, "x")], typeof(bool), expression);
|
||||
|
||||
// 创建新的 Lambda 表达式,接受 object 参数并调用编译后的表达式
|
||||
var invokeExpression = Expression.Invoke(lambda, Expression.Convert(parameter, modelType));
|
||||
var finalLambda = Expression.Lambda<Func<object, bool>>(invokeExpression, parameter);
|
||||
|
||||
return finalLambda.Compile();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new ArgumentException($"无法解析表达式 '{expression}': {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7,9 +7,15 @@
|
||||
namespace Admin.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// 用户状态属性扩展接口
|
||||
/// 自定义规范化结果特性
|
||||
/// </summary>
|
||||
public interface IUserManagerExtProps
|
||||
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)]
|
||||
public class CustomUnifyResultAttribute : Attribute
|
||||
{
|
||||
Dictionary<string, object> GetInitExtProps(SysUser user);
|
||||
public string Name { get; set; }
|
||||
|
||||
public CustomUnifyResultAttribute(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
@ -11,16 +11,6 @@ namespace Admin.NET.Core;
|
||||
/// </summary>
|
||||
public class CacheConst
|
||||
{
|
||||
/// <summary>
|
||||
/// 接口信息缓存
|
||||
/// </summary>
|
||||
public const string KeyAllApi = "sys_all_api";
|
||||
|
||||
/// <summary>
|
||||
/// 用户会话缓存
|
||||
/// </summary>
|
||||
public const string KeyUserManager = "sys_user_manager:";
|
||||
|
||||
/// <summary>
|
||||
/// 用户Token版本缓存
|
||||
/// </summary>
|
||||
|
||||
@ -11,85 +11,73 @@ namespace Admin.NET.Core;
|
||||
/// </summary>
|
||||
public class ClaimConst
|
||||
{
|
||||
/// <summary>
|
||||
/// 应用Id
|
||||
/// </summary>
|
||||
public const string AppId = "AppId";
|
||||
|
||||
/// <summary>
|
||||
/// 租户Id
|
||||
/// </summary>
|
||||
public const string TenantId = "TenantId";
|
||||
|
||||
/// <summary>
|
||||
/// 用户Id
|
||||
/// </summary>
|
||||
public const string UserId = "UserId";
|
||||
|
||||
/// <summary>
|
||||
/// 账号
|
||||
/// </summary>
|
||||
public const string Account = "Account";
|
||||
|
||||
/// <summary>
|
||||
/// 真实姓名
|
||||
/// </summary>
|
||||
public const string RealName = "RealName";
|
||||
|
||||
/// <summary>
|
||||
/// 昵称
|
||||
/// </summary>
|
||||
public const string NickName = "NickName";
|
||||
|
||||
/// <summary>
|
||||
/// 账号类型
|
||||
/// </summary>
|
||||
public const string AccountType = "AccountType";
|
||||
|
||||
/// <summary>
|
||||
/// 组织机构Id
|
||||
/// </summary>
|
||||
public const string OrgId = "OrgId";
|
||||
|
||||
/// <summary>
|
||||
/// 组织机构名称
|
||||
/// </summary>
|
||||
public const string OrgName = "OrgName";
|
||||
|
||||
/// <summary>
|
||||
/// 组织机构类型
|
||||
/// </summary>
|
||||
public const string OrgType = "OrgType";
|
||||
|
||||
/// <summary>
|
||||
/// 组织机构级别
|
||||
/// </summary>
|
||||
public const string OrgLevel = "OrgLevel";
|
||||
|
||||
/// <summary>
|
||||
/// 登录模式PC、APP
|
||||
/// </summary>
|
||||
public const string LoginMode = "LoginMode";
|
||||
|
||||
/// <summary>
|
||||
/// Token版本号
|
||||
/// </summary>
|
||||
public const string TokenVersion = "TokenVersion";
|
||||
|
||||
/// <summary>
|
||||
/// 应用Id
|
||||
/// </summary>
|
||||
[Obsolete("参数已从请求上下文中移除,请通过UserManger获取")]
|
||||
public const string AppId = "AppId";
|
||||
|
||||
/// <summary>
|
||||
/// 租户Id
|
||||
/// </summary>
|
||||
[Obsolete("参数已从请求上下文中移除,请通过UserManger获取")]
|
||||
public const string TenantId = "TenantId";
|
||||
|
||||
/// <summary>
|
||||
/// 账号
|
||||
/// </summary>
|
||||
[Obsolete("参数已从请求上下文中移除,请通过UserManger获取")]
|
||||
public const string Account = "Account";
|
||||
|
||||
/// <summary>
|
||||
/// 真实姓名
|
||||
/// </summary>
|
||||
[Obsolete("参数已从请求上下文中移除,请通过UserManger获取")]
|
||||
public const string RealName = "RealName";
|
||||
|
||||
/// <summary>
|
||||
/// 昵称
|
||||
/// </summary>
|
||||
[Obsolete("参数已从请求上下文中移除,请通过UserManger获取")]
|
||||
public const string NickName = "NickName";
|
||||
|
||||
/// <summary>
|
||||
/// 账号类型
|
||||
/// </summary>
|
||||
[Obsolete("参数已从请求上下文中移除,请通过UserManger获取")]
|
||||
public const string AccountType = "AccountType";
|
||||
|
||||
/// <summary>
|
||||
/// 组织机构Id
|
||||
/// </summary>
|
||||
[Obsolete("参数已从请求上下文中移除,请通过UserManger获取")]
|
||||
public const string OrgId = "OrgId";
|
||||
|
||||
/// <summary>
|
||||
/// 组织机构名称
|
||||
/// </summary>
|
||||
[Obsolete("参数已从请求上下文中移除,请通过UserManger获取")]
|
||||
public const string OrgName = "OrgName";
|
||||
|
||||
/// <summary>
|
||||
/// 组织机构类型
|
||||
/// </summary>
|
||||
[Obsolete("参数已从请求上下文中移除,请通过UserManger获取")]
|
||||
public const string OrgType = "OrgType";
|
||||
|
||||
/// <summary>
|
||||
/// 组织机构级别
|
||||
/// </summary>
|
||||
[Obsolete("参数已从请求上下文中移除,请通过UserManger获取")]
|
||||
public const string OrgLevel = "OrgLevel";
|
||||
|
||||
/// <summary>
|
||||
/// 登录模式PC、APP
|
||||
/// </summary>
|
||||
[Obsolete("参数已从请求上下文中移除,请通过UserManger获取")]
|
||||
public const string LoginMode = "LoginMode";
|
||||
|
||||
/// <summary>
|
||||
/// 微信OpenId
|
||||
/// </summary>
|
||||
[Obsolete("参数已从请求上下文中移除,请通过UserManger获取")]
|
||||
public const string OpenId = "OpenId";
|
||||
}
|
||||
@ -30,9 +30,4 @@ public class SqlSugarConst
|
||||
/// 默认租户Id
|
||||
/// </summary>
|
||||
public const long DefaultTenantId = 1300000000001;
|
||||
|
||||
/// <summary>
|
||||
/// 默认应用Id
|
||||
/// </summary>
|
||||
public const long DefaultAppId = 1300000000001;
|
||||
}
|
||||
@ -61,7 +61,6 @@ public abstract class EntityBase : EntityBaseId, IDeletedFilter
|
||||
/// <summary>
|
||||
/// 修改者Id
|
||||
/// </summary>
|
||||
[IgnoreUpdateSeedColumn]
|
||||
[SugarColumn(ColumnDescription = "修改者Id")]
|
||||
public virtual long? UpdateUserId { get; set; }
|
||||
|
||||
@ -76,14 +75,12 @@ public abstract class EntityBase : EntityBaseId, IDeletedFilter
|
||||
/// <summary>
|
||||
/// 修改者姓名
|
||||
/// </summary>
|
||||
[IgnoreUpdateSeedColumn]
|
||||
[SugarColumn(ColumnDescription = "修改者姓名", Length = 64)]
|
||||
public virtual string? UpdateUserName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 软删除
|
||||
/// </summary>
|
||||
[IgnoreUpdateSeedColumn]
|
||||
[SugarColumn(ColumnDescription = "软删除")]
|
||||
public virtual bool IsDelete { get; set; } = false;
|
||||
}
|
||||
|
||||
@ -37,15 +37,4 @@ public interface IOrgIdFilter
|
||||
/// 创建者部门Id
|
||||
/// </summary>
|
||||
long? CreateOrgId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用Id接口过滤器
|
||||
/// </summary>
|
||||
public interface IAppIdFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// 系统层应用Id
|
||||
/// </summary>
|
||||
long? AppId { get; set; }
|
||||
}
|
||||
20
Admin.NET/Admin.NET.Core/Enum/SerialTypeEnum.cs
Normal file
20
Admin.NET/Admin.NET.Core/Enum/SerialTypeEnum.cs
Normal file
@ -0,0 +1,20 @@
|
||||
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
||||
//
|
||||
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
||||
//
|
||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||
|
||||
namespace Admin.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// 系统流水号类型枚举
|
||||
/// </summary>
|
||||
[Description("系统流水号类型枚举")]
|
||||
public enum SerialTypeEnum
|
||||
{
|
||||
/// <summary>
|
||||
/// 其他
|
||||
/// </summary>
|
||||
[Description("其他")]
|
||||
Other = 999,
|
||||
}
|
||||
@ -9,7 +9,7 @@ namespace Admin.NET.Core;
|
||||
/// <summary>
|
||||
/// Http远程服务扩展
|
||||
/// </summary>
|
||||
public static class HttpRemotesExtension
|
||||
public static class HttpRemoteExtension
|
||||
{
|
||||
private static readonly HttpRequestOptionsKey<string> HttpNameKey = new("__HTTP_CLIENT_NAME__");
|
||||
private static readonly HttpRequestOptionsKey<HttpRemoteApiAttribute> AttrKey = new(nameof(HttpRemoteApiAttribute));
|
||||
@ -23,7 +23,7 @@ public static class HttpRemotesExtension
|
||||
/// <returns></returns>
|
||||
public static IServiceCollection AddHttpRemoteClientService(this IServiceCollection services)
|
||||
{
|
||||
var options = App.Configuration.GetSection("HttpRemotes").GetChildren().Select(u => u.Get<HttpRemoteItem>());
|
||||
var options = App.Configuration.GetSection("HttpRemote").GetChildren().Select(u => u.Get<HttpRemoteItem>());
|
||||
foreach (var opt in options)
|
||||
{
|
||||
services.AddHttpClient(opt.HttpName, client =>
|
||||
|
||||
@ -370,75 +370,16 @@ public static class RepositoryExtension
|
||||
//});
|
||||
}
|
||||
|
||||
#region 忽略租户
|
||||
|
||||
/// <summary>
|
||||
/// 忽略租户
|
||||
/// </summary>
|
||||
/// <param name="queryable"></param>
|
||||
/// <param name="ignore">是否忽略 默认true</param>
|
||||
/// <returns> </returns>
|
||||
public static ISugarQueryable<T> IgnoreTenant<T>(this ISugarQueryable<T> queryable, bool ignore = true)
|
||||
{
|
||||
return ignore ? queryable.ClearFilter<ITenantIdFilter>() : queryable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 忽略租户
|
||||
/// </summary>
|
||||
/// <param name="queryable"></param>
|
||||
/// <param name="ignore">是否忽略 默认true</param>
|
||||
/// <returns> </returns>
|
||||
public static ISugarQueryable<T, T2> IgnoreTenant<T, T2>(this ISugarQueryable<T, T2> queryable, bool ignore = true)
|
||||
{
|
||||
return ignore ? queryable.ClearFilter<ITenantIdFilter>() : queryable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 忽略租户
|
||||
/// </summary>
|
||||
/// <param name="queryable"></param>
|
||||
/// <param name="ignore">是否忽略 默认true</param>
|
||||
/// <returns> </returns>
|
||||
public static ISugarQueryable<T, T2, T3> IgnoreTenant<T, T2, T3>(this ISugarQueryable<T, T2, T3> queryable, bool ignore = true)
|
||||
{
|
||||
return ignore ? queryable.ClearFilter<ITenantIdFilter>() : queryable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 忽略租户
|
||||
/// </summary>
|
||||
/// <param name="queryable"></param>
|
||||
/// <param name="ignore">是否忽略 默认true</param>
|
||||
/// <returns> </returns>
|
||||
public static ISugarQueryable<T, T2, T3, T4> IgnoreTenant<T, T2, T3, T4>(this ISugarQueryable<T, T2, T3, T4> queryable, bool ignore = true)
|
||||
{
|
||||
return ignore ? queryable.ClearFilter<ITenantIdFilter>() : queryable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 忽略租户
|
||||
/// </summary>
|
||||
/// <param name="queryable"></param>
|
||||
/// <param name="ignore">是否忽略 默认true</param>
|
||||
/// <returns> </returns>
|
||||
public static ISugarQueryable<T, T2, T3, T4, T5> IgnoreTenant<T, T2, T3, T4, T5>(this ISugarQueryable<T, T2, T3, T4, T5> queryable, bool ignore = true)
|
||||
{
|
||||
return ignore ? queryable.ClearFilter<ITenantIdFilter>() : queryable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 忽略租户
|
||||
/// </summary>
|
||||
/// <param name="queryable"></param>
|
||||
/// <param name="ignore">是否忽略 默认true</param>
|
||||
/// <returns> </returns>
|
||||
public static ISugarQueryable<T, T2, T3, T4, T5, T6> IgnoreTenant<T, T2, T3, T4, T5, T6>(this ISugarQueryable<T, T2, T3, T4, T5, T6> queryable, bool ignore = true)
|
||||
{
|
||||
return ignore ? queryable.ClearFilter<ITenantIdFilter>() : queryable;
|
||||
}
|
||||
|
||||
#endregion 忽略租户
|
||||
///// <summary>
|
||||
///// 忽略租户
|
||||
///// </summary>
|
||||
///// <param name="queryable"></param>
|
||||
///// <param name="ignore">是否忽略 默认true</param>
|
||||
///// <returns> </returns>
|
||||
//public static ISugarQueryable<T> IgnoreTenant<T>(this ISugarQueryable<T> queryable, bool ignore = true)
|
||||
//{
|
||||
// return ignore ? queryable.ClearFilter<ITenantIdFilter>() : queryable;
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 只更新某些列
|
||||
|
||||
@ -19,14 +19,10 @@ public class OnlineUserHub : Hub<IOnlineUserHub>
|
||||
|
||||
private readonly SysMessageService _sysMessageService;
|
||||
private readonly IHubContext<OnlineUserHub, IOnlineUserHub> _onlineUserHubContext;
|
||||
private readonly UserManager _userManager;
|
||||
|
||||
public OnlineUserHub(
|
||||
UserManager userManager,
|
||||
SysMessageService sysMessageService,
|
||||
public OnlineUserHub(SysMessageService sysMessageService,
|
||||
IHubContext<OnlineUserHub, IOnlineUserHub> onlineUserHubContext)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_sysMessageService = sysMessageService;
|
||||
_onlineUserHubContext = onlineUserHubContext;
|
||||
}
|
||||
@ -38,22 +34,28 @@ public class OnlineUserHub : Hub<IOnlineUserHub>
|
||||
public override async Task OnConnectedAsync()
|
||||
{
|
||||
var httpContext = Context.GetHttpContext();
|
||||
if (_userManager.UserId < 0 || string.IsNullOrWhiteSpace(_userManager.Account)) return;
|
||||
var loginMode = _userManager.LoginMode == 0 ? LoginModeEnum.PC : LoginModeEnum.APP; // 默认PC登录模式
|
||||
var userId = (httpContext.User.FindFirst(ClaimConst.UserId)?.Value).ToLong();
|
||||
var account = httpContext.User.FindFirst(ClaimConst.Account)?.Value;
|
||||
if (userId < 0 || string.IsNullOrWhiteSpace(account)) return;
|
||||
|
||||
var realName = httpContext.User.FindFirst(ClaimConst.RealName)?.Value;
|
||||
var tenantId = (httpContext.User.FindFirst(ClaimConst.TenantId)?.Value).ToLong();
|
||||
var loginMode = (LoginModeEnum)(httpContext.User.FindFirst(ClaimConst.LoginMode)?.Value).ToInt();
|
||||
loginMode = loginMode == 0 ? LoginModeEnum.PC : loginMode; // 默认PC登录模式
|
||||
var device = httpContext.GetClientDeviceInfo().Trim();
|
||||
var ipAddress = httpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault();
|
||||
|
||||
var user = new OnlineUser
|
||||
{
|
||||
ConnectionId = Context.ConnectionId,
|
||||
UserId = _userManager.UserId,
|
||||
UserName = _userManager.Account,
|
||||
RealName = _userManager.RealName,
|
||||
UserId = userId,
|
||||
UserName = account,
|
||||
RealName = realName,
|
||||
Time = DateTime.Now,
|
||||
Ip = string.IsNullOrWhiteSpace(ipAddress) ? httpContext.GetRemoteIpAddressToIPv4(true) : ipAddress,
|
||||
Browser = httpContext.GetClientBrowser(),
|
||||
Os = httpContext.GetClientOs(),
|
||||
TenantId = _userManager.TenantId ?? 0,
|
||||
TenantId = tenantId,
|
||||
LoginMode = loginMode,
|
||||
Device = device
|
||||
};
|
||||
@ -64,7 +66,7 @@ public class OnlineUserHub : Hub<IOnlineUserHub>
|
||||
await _onlineUserHubContext.Groups.AddToGroupAsync(Context.ConnectionId, groupName);
|
||||
|
||||
// 更新在线用户列表
|
||||
var userList = SysCacheService.HashGetAll<OnlineUser>(CacheConst.KeyUserOnline).Values.Where(u => u.TenantId == _userManager.TenantId).ToList();
|
||||
var userList = SysCacheService.HashGetAll<OnlineUser>(CacheConst.KeyUserOnline).Values.Where(u => u.TenantId == tenantId).ToList();
|
||||
await _onlineUserHubContext.Clients.Groups(groupName).OnlineUserList(new OnlineUserList
|
||||
{
|
||||
RealName = user.RealName,
|
||||
|
||||
@ -7,15 +7,17 @@
|
||||
namespace Admin.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// 系统每日作业任务(每天 00:00:00 执行)
|
||||
/// 清理日志作业任务(每天 00:00:00 执行)
|
||||
/// </summary>
|
||||
[JobDetail("job_sys_daily", Description = "系统每日作业任务", GroupName = "default", Concurrent = false)]
|
||||
[Daily(TriggerId = "trigger_sys_daily", Description = "系统每日作业任务")]
|
||||
public class SysDailyJob(IServiceScopeFactory serviceScopeFactory) : IJob
|
||||
[JobDetail("sys_job_log", Description = "清理操作日志", GroupName = "default", Concurrent = false)]
|
||||
[Daily(TriggerId = "sys_trigger_log", Description = "清理操作日志")]
|
||||
public class SysLogJob(IServiceScopeFactory serviceScopeFactory) : IJob
|
||||
{
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory = serviceScopeFactory;
|
||||
|
||||
public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
|
||||
{
|
||||
using var serviceScope = serviceScopeFactory.CreateScope();
|
||||
using var serviceScope = _serviceScopeFactory.CreateScope();
|
||||
|
||||
var db = serviceScope.ServiceProvider.GetRequiredService<ISqlSugarClient>().CopyNew();
|
||||
var sysConfigService = serviceScope.ServiceProvider.GetRequiredService<SysConfigService>();
|
||||
@ -28,8 +30,6 @@ public class SysDailyJob(IServiceScopeFactory serviceScopeFactory) : IJob
|
||||
await db.Deleteable<SysLogOp>().Where(u => u.CreateTime < DateTime.Now.AddDays(-daysAgo)).ExecuteCommandAsync(stoppingToken);
|
||||
// 删除差异日志
|
||||
await db.Deleteable<SysLogDiff>().Where(u => u.CreateTime < DateTime.Now.AddDays(-daysAgo)).ExecuteCommandAsync(stoppingToken);
|
||||
// 删除请求日志
|
||||
await db.Deleteable<SysLogHttp>().Where(u => u.CreateTime < DateTime.Now.AddDays(-daysAgo)).ExecuteCommandAsync(stoppingToken);
|
||||
// 删除作业触发器运行记录
|
||||
await db.Deleteable<SysJobTriggerRecord>().Where(u => u.CreatedTime < DateTime.Now.AddDays(-daysAgo)).ExecuteCommandAsync(stoppingToken);
|
||||
|
||||
@ -39,11 +39,5 @@ public class SysDailyJob(IServiceScopeFactory serviceScopeFactory) : IJob
|
||||
Console.WriteLine(message);
|
||||
Log.Information(message);
|
||||
Console.ForegroundColor = originColor;
|
||||
|
||||
// 重置用户Token版本号
|
||||
serviceScope.ServiceProvider.GetRequiredService<SysUserService>().ResetTokenVersion();
|
||||
message = $"【启动任务】重置用户Token版本号 {DateTime.Now}";
|
||||
Console.WriteLine(message);
|
||||
Log.Information(message);
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,8 @@
|
||||
//
|
||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||
|
||||
using Furion.Shapeless;
|
||||
|
||||
namespace Admin.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
@ -11,16 +13,13 @@ namespace Admin.NET.Core;
|
||||
/// </summary>
|
||||
public class DatabaseLoggingWriter : IDatabaseLoggingWriter, IDisposable
|
||||
{
|
||||
private readonly IServiceScope _serviceScope;
|
||||
private readonly IEventPublisher _eventPublisher;
|
||||
private readonly ILogger<DatabaseLoggingWriter> _logger;
|
||||
private readonly SysConfigService _sysConfigService;
|
||||
private readonly IEventPublisher _eventPublisher;
|
||||
private readonly IServiceScope _serviceScope;
|
||||
private readonly SqlSugarScopeProvider _db;
|
||||
|
||||
public DatabaseLoggingWriter(
|
||||
IServiceScopeFactory serviceScopeFactory,
|
||||
ILogger<DatabaseLoggingWriter> logger,
|
||||
IEventPublisher eventPublisher)
|
||||
public DatabaseLoggingWriter(IServiceScopeFactory serviceScopeFactory, IEventPublisher eventPublisher, ILogger<DatabaseLoggingWriter> logger)
|
||||
{
|
||||
_serviceScope = serviceScopeFactory.CreateScope();
|
||||
_sysConfigService = _serviceScope.ServiceProvider.GetRequiredService<SysConfigService>();
|
||||
@ -95,24 +94,33 @@ public class DatabaseLoggingWriter : IDatabaseLoggingWriter, IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
var loggingMonitor = JSON.Deserialize<dynamic>(jsonStr);
|
||||
// 获取当前操作者
|
||||
var loggingMonitor = JSON.Deserialize<LoggingMonitorDto>(jsonStr);
|
||||
var userInfo = GetUserInfo(loggingMonitor);
|
||||
string account = "", realName = "", userId = "", tenantId = "";
|
||||
if (loggingMonitor.authorizationClaims != null)
|
||||
{
|
||||
var authDict = (loggingMonitor.authorizationClaims as IEnumerable<dynamic>)!.ToDictionary(u => u.type.ToString(), u => u.value.ToString());
|
||||
account = authDict?.GetValueOrDefault(ClaimConst.Account);
|
||||
realName = authDict?.GetValueOrDefault(ClaimConst.RealName);
|
||||
tenantId = authDict?.GetValueOrDefault(ClaimConst.TenantId);
|
||||
userId = authDict?.GetValueOrDefault(ClaimConst.UserId);
|
||||
}
|
||||
|
||||
// 优先获取 X-Forwarded-For 头部信息携带的IP地址(如nginx代理配置转发)
|
||||
var reqHeaders = loggingMonitor.RequestHeaders.ToDictionary(u => u.Key, u => u.Value);
|
||||
var remoteIPv4 = reqHeaders.GetValueOrDefault("X-Forwarded-For")?.ToString();
|
||||
var remoteIPv4 = ((JArray)loggingMonitor.requestHeaders).OfType<JObject>()
|
||||
.FirstOrDefault(header => (string)header["key"] == "X-Forwarded-For")?["value"]?.ToString();
|
||||
|
||||
// 获取IP地理位置
|
||||
if (string.IsNullOrEmpty(remoteIPv4)) remoteIPv4 = loggingMonitor.RemoteIPv4;
|
||||
if (string.IsNullOrEmpty(remoteIPv4))
|
||||
remoteIPv4 = loggingMonitor.remoteIPv4;
|
||||
(string ipLocation, double? longitude, double? latitude) = CommonHelper.GetIpAddress(remoteIPv4);
|
||||
|
||||
// 获取设备信息
|
||||
var os = "";
|
||||
var browser = "";
|
||||
if (loggingMonitor.UserAgent != null)
|
||||
var os = "";
|
||||
if (loggingMonitor.userAgent != null)
|
||||
{
|
||||
var client = Parser.GetDefault().Parse(loggingMonitor.UserAgent);
|
||||
var client = Parser.GetDefault().Parse(loggingMonitor.userAgent.ToString());
|
||||
browser = $"{client.UA.Family} {client.UA.Major}.{client.UA.Minor} / {client.Device.Family}";
|
||||
os = $"{client.OS.Family} {client.OS.Major} {client.OS.Minor}";
|
||||
}
|
||||
@ -120,120 +128,120 @@ public class DatabaseLoggingWriter : IDatabaseLoggingWriter, IDisposable
|
||||
// 捕捉异常,否则会由于 unhandled exception 导致程序崩溃
|
||||
try
|
||||
{
|
||||
var logEntity = new SysLogOp
|
||||
{
|
||||
ControllerName = loggingMonitor.DisplayName,
|
||||
ActionName = loggingMonitor.ActionTypeName,
|
||||
DisplayTitle = loggingMonitor.DisplayTitle,
|
||||
Status = loggingMonitor.ReturnInformation?.HttpStatusCode?.ToString(),
|
||||
RemoteIp = remoteIPv4,
|
||||
Location = ipLocation,
|
||||
Longitude = longitude,
|
||||
Latitude = latitude,
|
||||
Browser = browser,
|
||||
Os = os,
|
||||
Elapsed = loggingMonitor.TimeOperationElapsedMilliseconds,
|
||||
Message = logMsg.Message,
|
||||
HttpMethod = loggingMonitor.HttpMethod,
|
||||
RequestUrl = loggingMonitor.RequestUrl,
|
||||
RequestParam = loggingMonitor.Parameters is { Count: > 0 } ? JSON.Serialize(loggingMonitor.Parameters[0].Value) : null,
|
||||
ReturnResult = loggingMonitor.ReturnInformation?.Value != null ? JSON.Serialize(loggingMonitor.ReturnInformation?.Value) : null,
|
||||
Exception = loggingMonitor.Exception == null ? JSON.Serialize(logMsg.Exception) : JSON.Serialize(loggingMonitor.Exception),
|
||||
LogDateTime = logMsg.LogDateTime,
|
||||
EventId = logMsg.EventId.Id,
|
||||
ThreadId = logMsg.ThreadId,
|
||||
TraceId = logMsg.TraceId,
|
||||
Account = userInfo.Account,
|
||||
RealName = userInfo.RealName,
|
||||
CreateUserId = userInfo.UserId,
|
||||
CreateUserName = userInfo.RealName,
|
||||
TenantId = userInfo.TenantId,
|
||||
LogLevel = logMsg.LogLevel,
|
||||
};
|
||||
|
||||
// 记录异常日志-发送邮件
|
||||
if (logMsg.Exception != null || loggingMonitor.Exception != null)
|
||||
if (logMsg.Exception != null || loggingMonitor.exception != null)
|
||||
{
|
||||
await _db.Insertable(logEntity.Adapt<SysLogEx>()).ExecuteCommandAsync();
|
||||
await _db.Insertable(new SysLogEx
|
||||
{
|
||||
ControllerName = loggingMonitor.controllerName,
|
||||
ActionName = loggingMonitor.actionTypeName,
|
||||
DisplayTitle = loggingMonitor.displayTitle,
|
||||
Status = loggingMonitor.returnInformation?.httpStatusCode,
|
||||
RemoteIp = remoteIPv4,
|
||||
Location = ipLocation,
|
||||
Longitude = longitude,
|
||||
Latitude = latitude,
|
||||
Browser = browser, // loggingMonitor.userAgent,
|
||||
Os = os, // loggingMonitor.osDescription + " " + loggingMonitor.osArchitecture,
|
||||
Elapsed = loggingMonitor.timeOperationElapsedMilliseconds,
|
||||
LogDateTime = logMsg.LogDateTime,
|
||||
Account = account,
|
||||
RealName = realName,
|
||||
HttpMethod = loggingMonitor.httpMethod,
|
||||
RequestUrl = loggingMonitor.requestUrl,
|
||||
RequestParam = (loggingMonitor.parameters == null || loggingMonitor.parameters.Count == 0) ? null : JSON.Serialize(loggingMonitor.parameters[0].value),
|
||||
ReturnResult = loggingMonitor.returnInformation == null ? null : JSON.Serialize(loggingMonitor.returnInformation),
|
||||
EventId = logMsg.EventId.Id,
|
||||
ThreadId = logMsg.ThreadId,
|
||||
TraceId = logMsg.TraceId,
|
||||
Exception = JSON.Serialize(loggingMonitor.exception),
|
||||
Message = logMsg.Message,
|
||||
CreateUserId = string.IsNullOrWhiteSpace(userId) ? 0 : long.Parse(userId),
|
||||
TenantId = string.IsNullOrWhiteSpace(tenantId) ? 0 : long.Parse(tenantId),
|
||||
LogLevel = logMsg.LogLevel
|
||||
}).ExecuteCommandAsync();
|
||||
|
||||
// 将异常日志发送到邮件
|
||||
await _eventPublisher.PublishAsync(CommonConst.SendErrorMail, logMsg.Exception ?? loggingMonitor.Exception);
|
||||
await _eventPublisher.PublishAsync(CommonConst.SendErrorMail, logMsg.Exception ?? loggingMonitor.exception);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 记录访问日志-登录退出
|
||||
if (loggingMonitor.ActionName == "login" || loggingMonitor.ActionName == "loginPhone" || loggingMonitor.ActionName == "logout")
|
||||
if (loggingMonitor.actionName == "login" || loggingMonitor.actionName == "loginPhone" || loggingMonitor.actionName == "logout")
|
||||
{
|
||||
await _db.Insertable(logEntity.Adapt<SysLogVis>()).ExecuteCommandAsync();
|
||||
if (loggingMonitor.actionName != "logout")
|
||||
{
|
||||
dynamic para = Clay.Parse((loggingMonitor.parameters == null) ? null : JSON.Serialize(loggingMonitor.parameters[0].value));
|
||||
if (loggingMonitor.actionName == "login")
|
||||
account = para.account;
|
||||
else if (loggingMonitor.actionName == "loginPhone")
|
||||
account = para.phone;
|
||||
}
|
||||
|
||||
await _db.Insertable(new SysLogVis
|
||||
{
|
||||
ControllerName = loggingMonitor.displayName,
|
||||
ActionName = loggingMonitor.actionTypeName,
|
||||
DisplayTitle = loggingMonitor.displayTitle,
|
||||
Status = loggingMonitor.returnInformation?.httpStatusCode,
|
||||
RemoteIp = remoteIPv4,
|
||||
Location = ipLocation,
|
||||
Longitude = longitude,
|
||||
Latitude = latitude,
|
||||
Browser = browser, // loggingMonitor.userAgent,
|
||||
Os = os, // loggingMonitor.osDescription + " " + loggingMonitor.osArchitecture,
|
||||
Elapsed = loggingMonitor.timeOperationElapsedMilliseconds,
|
||||
LogDateTime = logMsg.LogDateTime,
|
||||
Account = account,
|
||||
RealName = realName,
|
||||
CreateUserId = string.IsNullOrWhiteSpace(userId) ? 0 : long.Parse(userId),
|
||||
TenantId = string.IsNullOrWhiteSpace(tenantId) ? 0 : long.Parse(tenantId),
|
||||
LogLevel = logMsg.LogLevel
|
||||
}).ExecuteCommandAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
// 记录操作日志
|
||||
if (!await _sysConfigService.GetConfigValueByCode<bool>(ConfigConst.SysOpLog)) return;
|
||||
await _db.Insertable(logEntity.Adapt<SysLogOp>()).ExecuteCommandAsync();
|
||||
await _db.Insertable(new SysLogOp
|
||||
{
|
||||
ControllerName = loggingMonitor.controllerName,
|
||||
ActionName = loggingMonitor.actionTypeName,
|
||||
DisplayTitle = loggingMonitor.displayTitle,
|
||||
Status = loggingMonitor.returnInformation?.httpStatusCode,
|
||||
RemoteIp = remoteIPv4,
|
||||
Location = ipLocation,
|
||||
Longitude = longitude,
|
||||
Latitude = latitude,
|
||||
Browser = browser, // loggingMonitor.userAgent,
|
||||
Os = os, // loggingMonitor.osDescription + " " + loggingMonitor.osArchitecture,
|
||||
Elapsed = loggingMonitor.timeOperationElapsedMilliseconds,
|
||||
LogDateTime = logMsg.LogDateTime,
|
||||
Account = account,
|
||||
RealName = realName,
|
||||
HttpMethod = loggingMonitor.httpMethod,
|
||||
RequestUrl = loggingMonitor.requestUrl,
|
||||
RequestParam = (loggingMonitor.parameters == null || loggingMonitor.parameters.Count == 0) ? null : JSON.Serialize(loggingMonitor.parameters[0].value),
|
||||
ReturnResult = loggingMonitor.returnInformation == null ? null : JSON.Serialize(loggingMonitor.returnInformation),
|
||||
EventId = logMsg.EventId.Id,
|
||||
ThreadId = logMsg.ThreadId,
|
||||
TraceId = logMsg.TraceId,
|
||||
Exception = loggingMonitor.exception == null ? null : JSON.Serialize(loggingMonitor.exception),
|
||||
Message = logMsg.Message,
|
||||
CreateUserId = string.IsNullOrWhiteSpace(userId) ? 0 : long.Parse(userId),
|
||||
TenantId = string.IsNullOrWhiteSpace(tenantId) ? 0 : long.Parse(tenantId),
|
||||
LogLevel = logMsg.LogLevel
|
||||
}).ExecuteCommandAsync();
|
||||
|
||||
await Task.Delay(50); // 延迟 0.05 秒写入数据库,有效减少高频写入数据库导致死锁问题
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "操作日志入库");
|
||||
// 将异常日志发送到邮件
|
||||
await _eventPublisher.PublishAsync(CommonConst.SendErrorMail, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从日志消息中获取用户信息
|
||||
/// </summary>
|
||||
/// <param name="loggingMonitor"></param>
|
||||
/// <returns></returns>
|
||||
private LoggingUserInfo GetUserInfo(LoggingMonitorDto loggingMonitor)
|
||||
{
|
||||
var userManger = LazyHelper.GetService<UserManager>().Value;
|
||||
LoggingUserInfo result = new();
|
||||
if (loggingMonitor.AuthorizationClaims != null)
|
||||
{
|
||||
result.UserId = long.TryParse(loggingMonitor.AuthorizationClaims
|
||||
.ToDictionary(u => u.Type, u => u.Value)
|
||||
.GetValueOrDefault(ClaimConst.UserId) ?? ""
|
||||
, out var temp) ? temp : null;
|
||||
|
||||
var userManager = userManger.Get(result.UserId);
|
||||
result.TenantId = long.TryParse(userManager?.TenantId?.ToString() ?? "", out temp) ? temp : null;
|
||||
result.RealName = userManager?.RealName;
|
||||
result.Account = userManager?.Account;
|
||||
}
|
||||
|
||||
// 退出登陆时没有用户状态,尝试从userManger中获取
|
||||
if (string.IsNullOrWhiteSpace(result.Account) && result.UserId != null)
|
||||
{
|
||||
var userManager = userManger.GetOrRefresh(result.UserId);
|
||||
result.Account = userManager?.TenantId?.ToString();
|
||||
result.RealName = userManager?.RealName;
|
||||
result.Account = userManager?.Account;
|
||||
}
|
||||
|
||||
// 用户登陆时没有userId,需要根据入参获取
|
||||
if (loggingMonitor.ActionName == "login" && loggingMonitor.Parameters is { Count: > 0 })
|
||||
{
|
||||
try
|
||||
{
|
||||
result.Account = (loggingMonitor.Parameters[0].Value as JObject)!.GetValue("account")?.ToString();
|
||||
if (!string.IsNullOrEmpty(result.Account))
|
||||
{
|
||||
var user = _db.Queryable<SysUser>().First(u => u.Account == result.Account);
|
||||
result.TenantId = user?.TenantId;
|
||||
result.RealName = user?.RealName;
|
||||
result.UserId = user?.Id;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放服务作用域
|
||||
/// </summary>
|
||||
|
||||
@ -15,14 +15,12 @@ public class ElasticSearchLoggingWriter : IDatabaseLoggingWriter, IDisposable
|
||||
{
|
||||
private readonly IServiceScope _serviceScope;
|
||||
private readonly ElasticsearchClient _esClient;
|
||||
private readonly SysCacheService _sysCacheService;
|
||||
private readonly SysConfigService _sysConfigService;
|
||||
|
||||
public ElasticSearchLoggingWriter(IServiceScopeFactory scopeFactory)
|
||||
{
|
||||
_serviceScope = scopeFactory.CreateScope();
|
||||
_esClient = _serviceScope.ServiceProvider.GetRequiredService<ElasticsearchClient>();
|
||||
_sysCacheService = _serviceScope.ServiceProvider.GetRequiredService<SysCacheService>();
|
||||
_sysConfigService = _serviceScope.ServiceProvider.GetRequiredService<SysConfigService>();
|
||||
}
|
||||
|
||||
@ -42,21 +40,20 @@ public class ElasticSearchLoggingWriter : IDatabaseLoggingWriter, IDisposable
|
||||
return;
|
||||
|
||||
// 获取当前操作者
|
||||
var userManager = LazyHelper.GetService<UserManager>();
|
||||
string account = "", realName = "", userId = "", tenantId = "";
|
||||
if (loggingMonitor.authorizationClaims != null)
|
||||
{
|
||||
UserManagerDao manager = null;
|
||||
foreach (var item in loggingMonitor.authorizationClaims)
|
||||
{
|
||||
if (item.type != ClaimConst.UserId) continue;
|
||||
userId = item.value;
|
||||
manager = userManager.Value.GetOrRefresh(userId);
|
||||
break;
|
||||
if (item.type == ClaimConst.Account)
|
||||
account = item.value;
|
||||
if (item.type == ClaimConst.RealName)
|
||||
realName = item.value;
|
||||
if (item.type == ClaimConst.TenantId)
|
||||
tenantId = item.value;
|
||||
if (item.type == ClaimConst.UserId)
|
||||
userId = item.value;
|
||||
}
|
||||
tenantId = manager?.TenantId.ToString();
|
||||
realName = manager?.RealName;
|
||||
account = manager?.Account;
|
||||
}
|
||||
|
||||
string remoteIPv4 = loggingMonitor.remoteIPv4;
|
||||
|
||||
@ -19,7 +19,7 @@ public class HttpLoggingHandler : DelegatingHandler, ITransient
|
||||
{
|
||||
_eventPublisher = eventPublisher;
|
||||
_sysConfigService = sysConfigService;
|
||||
_optionMap = App.Configuration.GetSection("HttpRemotes").GetChildren()
|
||||
_optionMap = App.Configuration.GetSection("HttpRemote").GetChildren()
|
||||
.Select(u => u.Get<HttpRemoteItem>())
|
||||
.ToDictionary(opt => opt.HttpName, opt => opt);
|
||||
}
|
||||
@ -38,7 +38,7 @@ public class HttpLoggingHandler : DelegatingHandler, ITransient
|
||||
if (!enabledLog || attr?.IgnoreLog == true) return await base.SendAsync(request, cancellationToken);
|
||||
|
||||
var stopWatch = Stopwatch.StartNew();
|
||||
var userManger = LazyHelper.GetService<UserManager>().Value;
|
||||
var userManger = App.GetService<UserManager>();
|
||||
var urlList = request.RequestUri?.LocalPath.Split("/") ?? [];
|
||||
var sysLogHttp = new SysLogHttp
|
||||
{
|
||||
|
||||
@ -6,8 +6,6 @@
|
||||
|
||||
using Furion.SpecificationDocument;
|
||||
using Lazy.Captcha.Core;
|
||||
using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
|
||||
namespace Admin.NET.Core.Service;
|
||||
|
||||
@ -22,9 +20,7 @@ public class SysAuthService : IDynamicApiController, ITransient
|
||||
private readonly SqlSugarRepository<SysUser> _sysUserRep;
|
||||
private readonly SysConfigService _sysConfigService;
|
||||
private readonly SysCacheService _sysCacheService;
|
||||
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly IApiDescriptionGroupCollectionProvider _apiProvider;
|
||||
private readonly ICaptcha _captcha;
|
||||
private readonly IEventPublisher _eventPublisher;
|
||||
|
||||
@ -32,14 +28,12 @@ public class SysAuthService : IDynamicApiController, ITransient
|
||||
SqlSugarRepository<SysUser> sysUserRep,
|
||||
SysConfigService sysConfigService,
|
||||
SysCacheService sysCacheService,
|
||||
IApiDescriptionGroupCollectionProvider apiProvider,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ICaptcha captcha,
|
||||
IEventPublisher eventPublisher)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_sysUserRep = sysUserRep;
|
||||
_apiProvider = apiProvider;
|
||||
_sysConfigService = sysConfigService;
|
||||
_sysCacheService = sysCacheService;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
@ -120,7 +114,7 @@ public class SysAuthService : IDynamicApiController, ITransient
|
||||
//}
|
||||
|
||||
// 判断账号是否存在
|
||||
var user = await _sysUserRep.AsQueryable().Includes(t => t.SysOrg).IgnoreTenant()
|
||||
var user = await _sysUserRep.AsQueryable().Includes(t => t.SysOrg).ClearFilter<ITenantIdFilter>()
|
||||
//.WhereIF(tenantId > 0, u => u.TenantId == tenantId)
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(account), u => u.Account.Equals(account))
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(phone), u => u.Phone.Equals(phone))
|
||||
@ -235,11 +229,18 @@ public class SysAuthService : IDynamicApiController, ITransient
|
||||
await App.GetRequiredService<SysOnlineUserService>().SingleLogin(user.Id, loginMode);
|
||||
|
||||
// 生成Token令牌
|
||||
user.TokenVersion += 1;
|
||||
var tokenExpire = await _sysConfigService.GetTokenExpire();
|
||||
var accessToken = JWTEncryption.Encrypt(new Dictionary<string, object>
|
||||
{
|
||||
{ ClaimConst.UserId, user.Id },
|
||||
{ ClaimConst.TenantId, user.TenantId },
|
||||
{ ClaimConst.Account, user.Account },
|
||||
{ ClaimConst.RealName, user.RealName },
|
||||
{ ClaimConst.AccountType, user.AccountType },
|
||||
{ ClaimConst.OrgId, user.OrgId },
|
||||
{ ClaimConst.OrgName, user.SysOrg?.Name },
|
||||
{ ClaimConst.OrgType, user.SysOrg?.Type },
|
||||
{ ClaimConst.OrgLevel, user.SysOrg?.Level },
|
||||
{ ClaimConst.TokenVersion, user.TokenVersion },
|
||||
}, tokenExpire);
|
||||
|
||||
@ -260,7 +261,6 @@ public class SysAuthService : IDynamicApiController, ITransient
|
||||
user.LastLoginDevice = CommonHelper.GetClientDeviceInfo(_httpContextAccessor.HttpContext?.Request?.Headers?.UserAgent);
|
||||
await _sysUserRep.AsUpdateable(user).UpdateColumns(u => new
|
||||
{
|
||||
u.TokenVersion,
|
||||
u.LastLoginIp,
|
||||
u.LastLoginAddress,
|
||||
u.LastLoginTime,
|
||||
@ -273,9 +273,6 @@ public class SysAuthService : IDynamicApiController, ITransient
|
||||
// 发布系统登录事件
|
||||
await _eventPublisher.PublishAsync(UserEventTypeEnum.Login, user);
|
||||
|
||||
// 缓存用户状态
|
||||
await SetUserManager(user, loginMode);
|
||||
|
||||
return new LoginOutput
|
||||
{
|
||||
AccessToken = accessToken,
|
||||
@ -284,67 +281,10 @@ public class SysAuthService : IDynamicApiController, ITransient
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置用户状态
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="loginMode"></param>
|
||||
private async Task SetUserManager(SysUser user, LoginModeEnum loginMode = LoginModeEnum.PC)
|
||||
{
|
||||
var db = _sysUserRep.Context.CopyNew();
|
||||
user.SysPos ??= await db.Queryable<SysPos>().FirstAsync(u => u.Id == user.PosId);
|
||||
user.SysOrg ??= await db.Queryable<SysOrg>().FirstAsync(u => u.Id == user.OrgId);
|
||||
|
||||
var roleIds = await db.Queryable<SysRole>().InnerJoinIF<SysUserRole>(user.AccountType != AccountTypeEnum.SuperAdmin, (u, a) => a.RoleId == u.Id && a.UserId == user.Id).Select((u, a) => u.Id).ToListAsync();
|
||||
var posIds = await db.Queryable<SysPos>().InnerJoinIF<SysUserExtOrg>(user.AccountType != AccountTypeEnum.SuperAdmin, (u, a) => a.PosId == u.Id && a.UserId == user.Id).Select((u, a) => u.Id).ToListAsync();
|
||||
posIds = posIds.Concat([user.PosId]).Where(u => u != 0).ToList();
|
||||
|
||||
var menuIds = await db.Queryable<SysMenu>().InnerJoinIF<SysRoleMenu>(user.AccountType != AccountTypeEnum.SuperAdmin, (u, a) => a.MenuId == u.Id && roleIds.Contains(a.RoleId))
|
||||
.Where(u => u.Status == StatusEnum.Enable)
|
||||
.Select((u, a) => u.Id)
|
||||
.ToListAsync();
|
||||
var maxDataScope = await db.Queryable<SysRole>().Where(u => roleIds.Contains(u.Id)).Select(u => u.DataScope).ToListAsync();
|
||||
if (!maxDataScope.Any()) maxDataScope = [DataScopeEnum.Self];
|
||||
|
||||
var orgIds = GetUserOrgIdList(user, roleIds);
|
||||
var permissions = GetUserPermissions(user, roleIds);
|
||||
var unauthorizedPermissions = GetUserUnPermissions(user, roleIds);
|
||||
|
||||
// 缓存用户状态
|
||||
_userManager.Set(new()
|
||||
{
|
||||
UserId = user.Id,
|
||||
TenantId = user.TenantId,
|
||||
Account = user.Account,
|
||||
RealName = user.RealName,
|
||||
NickName = user.NickName,
|
||||
AccountType = user.AccountType,
|
||||
OrgId = user.OrgId,
|
||||
OrgCode = user.SysOrg?.Code,
|
||||
OrgName = user.SysOrg?.Name,
|
||||
OrgType = user.SysOrg?.Type,
|
||||
OrgLevel = user.SysOrg?.Level,
|
||||
PosId = user.PosId,
|
||||
PosName = user.SysPos?.Name,
|
||||
PosCode = user.SysPos?.Code,
|
||||
LoginMode = loginMode,
|
||||
TokenVersion = user.TokenVersion,
|
||||
OrgIds = orgIds,
|
||||
PosIds = posIds,
|
||||
RoleIds = roleIds,
|
||||
MenuIds = menuIds,
|
||||
Permissions = permissions,
|
||||
UnauthorizedPermissions = unauthorizedPermissions,
|
||||
DataScopeList = user.AccountType == AccountTypeEnum.SuperAdmin ? [DataScopeEnum.All] : maxDataScope,
|
||||
AppPermissions = loginMode == LoginModeEnum.APP ? LazyHelper.GetService<SysCommonService>().Value.GetAppApiList() : null,
|
||||
ExtProps = App.GetServices<IUserManagerExtProps>()?.SelectMany(u => u.GetInitExtProps(user)).Where(u => !string.IsNullOrWhiteSpace(u.Key)).ToDictionary(u => u.Key, u => u.Value)
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前登陆用户信息 🔖
|
||||
/// 获取登录账号 🔖
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[DisplayName("获取当前登陆用户信息")]
|
||||
[DisplayName("获取登录账号")]
|
||||
public virtual async Task<LoginUserOutput> GetUserInfo()
|
||||
{
|
||||
var user = await _sysUserRep.GetByIdAsync(_userManager.UserId) ?? throw Oops.Oh(ErrorCodeEnum.D1011).StatusCode(401);
|
||||
@ -357,6 +297,8 @@ public class SysAuthService : IDynamicApiController, ITransient
|
||||
.LeftJoin<SysRole>((u, a) => u.RoleId == a.Id)
|
||||
.Where(u => u.UserId == user.Id)
|
||||
.Select((u, a) => new RoleDto { Id = a.Id, Name = a.Name, Code = a.Code }).ToListAsync();
|
||||
// 接口集合
|
||||
var apis = (await App.GetRequiredService<SysRoleService>().GetUserApiList())[0];
|
||||
// 个性化水印文字(若系统水印为空则不显示)
|
||||
var watermarkText = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysTenant>>().AsQueryable().Where(u => u.Id == user.TenantId).Select(u => u.Watermark).FirstAsync();
|
||||
if (!string.IsNullOrWhiteSpace(watermarkText))
|
||||
@ -379,7 +321,7 @@ public class SysAuthService : IDynamicApiController, ITransient
|
||||
OrgName = org?.Name,
|
||||
OrgType = org?.Type,
|
||||
PosName = pos?.Name,
|
||||
Apis = _userManager.Permissions,
|
||||
Apis = apis,
|
||||
Roles = roles,
|
||||
WatermarkText = watermarkText,
|
||||
LastChangePasswordTime = user.LastChangePasswordTime
|
||||
@ -406,14 +348,13 @@ public class SysAuthService : IDynamicApiController, ITransient
|
||||
{
|
||||
var httpContext = _httpContextAccessor.HttpContext ?? throw Oops.Oh(ErrorCodeEnum.D1016);
|
||||
|
||||
var userId = httpContext.User.FindFirst(ClaimConst.UserId)?.Value;
|
||||
var version = httpContext.User.FindFirst(ClaimConst.TokenVersion)?.Value;
|
||||
if (string.IsNullOrWhiteSpace(userId) || string.IsNullOrWhiteSpace(version) || string.IsNullOrWhiteSpace(_userManager.Account))
|
||||
var accessToken = httpContext.Request.Headers.Authorization.ToString();
|
||||
if (string.IsNullOrWhiteSpace(accessToken) || string.IsNullOrWhiteSpace(_userManager.Account))
|
||||
throw Oops.Oh(ErrorCodeEnum.D1011);
|
||||
|
||||
// 写入Token黑名单
|
||||
var tokenExpire = await _sysConfigService.GetTokenExpire();
|
||||
_sysCacheService.Set($"{CacheConst.KeyTokenBlacklist}{userId}:{version}", _userManager.Account, TimeSpan.FromMinutes(tokenExpire));
|
||||
_sysCacheService.Set($"{CacheConst.KeyTokenBlacklist}:{accessToken}", _userManager.Account, TimeSpan.FromMinutes(tokenExpire));
|
||||
|
||||
// 发布系统退出事件
|
||||
await _eventPublisher.PublishAsync(UserEventTypeEnum.Logout, _userManager);
|
||||
@ -486,167 +427,4 @@ public class SysAuthService : IDynamicApiController, ITransient
|
||||
return 401;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取系统所有接口列表
|
||||
/// </summary>
|
||||
[NonAction]
|
||||
public List<ApiOutput> GetSysAllApiInfoList()
|
||||
{
|
||||
return _sysCacheService.GetOrAdd(CacheConst.KeyAllApi, _ =>
|
||||
{
|
||||
var apiList = new List<ApiOutput>();
|
||||
var apiDescriptionGroups = _apiProvider.ApiDescriptionGroups.Items;
|
||||
foreach (ApiDescriptionGroup group in apiDescriptionGroups)
|
||||
{
|
||||
foreach (var action in group.Items)
|
||||
{
|
||||
// 控制器信息
|
||||
if (action.ActionDescriptor is not ControllerActionDescriptor actionDescriptor) continue;
|
||||
var apiDescription = actionDescriptor.ControllerTypeInfo.GetCustomAttribute<ApiDescriptionSettingsAttribute>(true);
|
||||
|
||||
// 路由
|
||||
var route = action.RelativePath!.Contains('{') ? action.RelativePath[..(action.RelativePath.IndexOf('{') - 1)] : action.RelativePath; // 去掉路由参数
|
||||
route = route[(route.IndexOf('/') + 1)..]; // 去掉路由前缀
|
||||
|
||||
// 获取分组名称和接口名称
|
||||
string groupName = null, displayText = null;
|
||||
foreach (var attr in action.ActionDescriptor.EndpointMetadata)
|
||||
{
|
||||
switch (attr)
|
||||
{
|
||||
case ApiDescriptionSettingsAttribute settings:
|
||||
if (!string.IsNullOrWhiteSpace(settings.GroupName)) groupName = settings.GroupName;
|
||||
break;
|
||||
|
||||
case DisplayNameAttribute displayName:
|
||||
if (!string.IsNullOrWhiteSpace(displayName.DisplayName)) displayText = displayName.DisplayName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
apiList.Add(new ApiOutput
|
||||
{
|
||||
Route = route,
|
||||
HttpMethod = action.HttpMethod,
|
||||
Order = apiDescription?.Order ?? 0,
|
||||
Action = actionDescriptor.ActionName,
|
||||
Name = actionDescriptor.ControllerName,
|
||||
GroupName = groupName ?? group.GroupName,
|
||||
IsAppApi = actionDescriptor.ControllerTypeInfo.GetCustomAttribute<AppApiDescriptionAttribute>(true) != null,
|
||||
Desc = displayText ?? (string.IsNullOrWhiteSpace(apiDescription?.Description) ? actionDescriptor.ControllerName : apiDescription?.Description),
|
||||
});
|
||||
}
|
||||
}
|
||||
return apiList;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户权限
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="roleIds"></param>
|
||||
/// <returns></returns>
|
||||
private List<string> GetUserPermissions(SysUser user, List<long> roleIds)
|
||||
{
|
||||
var superAdmin = user.AccountType == AccountTypeEnum.SuperAdmin;
|
||||
var db = _sysUserRep.Context.CopyNew();
|
||||
|
||||
var allApi = GetSysAllApiInfoList().Select(u => u.Route).ToArray();
|
||||
var menuPermissions = db.Queryable<SysMenu>()
|
||||
.Where(u => u.Status == StatusEnum.Enable && u.Type == MenuTypeEnum.Btn)
|
||||
.Select(u => u.Permission)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
// 超管返回所有权限
|
||||
if (superAdmin) return allApi.Union(menuPermissions).Distinct().Order().ToList();
|
||||
|
||||
// 普通用户返回已授权标识 + 未不在菜单中的权限标识
|
||||
var permissions = db.Queryable<SysMenu>()
|
||||
.InnerJoin<SysRoleMenu>((u, a) => u.Id == a.MenuId)
|
||||
.Where((u, a) => u.Status == StatusEnum.Enable && u.Type == MenuTypeEnum.Btn)
|
||||
.Where((u, a) => roleIds.Contains(a.RoleId) && SqlFunc.Subqueryable<SysRoleApi>()
|
||||
.Where(z => roleIds.Contains(z.RoleId) && u.Permission == z.Route).NotAny())
|
||||
.Select(u => u.Permission)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
return allApi.Except(menuPermissions).Union(permissions).Distinct().Order().ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户不能访问的权限
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="roleIds"></param>
|
||||
/// <returns></returns>
|
||||
private List<string> GetUserUnPermissions(SysUser user, List<long> roleIds)
|
||||
{
|
||||
var superAdmin = user.AccountType == AccountTypeEnum.SuperAdmin;
|
||||
var db = _sysUserRep.Context.CopyNew();
|
||||
if (superAdmin) return [];
|
||||
return db.Union(db.Queryable<SysMenu>()
|
||||
.InnerJoin<SysRoleMenu>((u, a) => u.Id == a.MenuId)
|
||||
.Where((u, a) => u.Status == StatusEnum.Enable && u.Type == MenuTypeEnum.Btn)
|
||||
.Where((u, a) => !roleIds.Contains(a.RoleId))
|
||||
.Select(u => u.Permission),
|
||||
db.Queryable<SysRoleApi>().Where(u => roleIds.Contains(u.RoleId)).Select(u => u.Route))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户已授权的机构Id
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="roleIds"></param>
|
||||
/// <returns></returns>
|
||||
private List<long> GetUserOrgIdList(SysUser user, List<long> roleIds)
|
||||
{
|
||||
var db = _sysUserRep.Context.CopyNew();
|
||||
var orgIds = db.Union(
|
||||
// 获取用户表中的机构Id
|
||||
db.Queryable<SysUser>().IgnoreTenant().Select(u => new { UserId = u.Id, u.OrgId }),
|
||||
// 获取用户创建的机构Id
|
||||
db.Queryable<SysOrg>().IgnoreTenant().Where(u => u.CreateUserId != null).Select(u => new { UserId = u.CreateUserId.Value, OrgId = u.Id }),
|
||||
// 获取用户扩展机构Id
|
||||
db.Queryable<SysUserExtOrg>().Select(u => new { u.UserId, u.OrgId }),
|
||||
// 获取用户角色关联的机构Id
|
||||
db.Queryable<SysRoleOrg>().InnerJoin<SysUserRole>((u, a) => u.RoleId == a.RoleId).Select((u, a) => new { a.UserId, u.OrgId }),
|
||||
// 获取包含全部数据权限的机构Id
|
||||
db.Queryable<SysOrg>().IgnoreTenant().Where(u => SqlFunc.Subqueryable<SysUserRole>().InnerJoin<SysRole>((x, y) => x.RoleId == y.Id).Where((x, y) => x.UserId == user.Id && y.DataScope == DataScopeEnum.All).Any()).Select(u => new { UserId = user.Id, OrgId = u.Id }),
|
||||
// 超管获取全部机构Id
|
||||
db.Queryable<SysOrg>().IgnoreTenant().Where(u => user.AccountType == AccountTypeEnum.SuperAdmin).Select(u => new { UserId = user.Id, OrgId = u.Id }))
|
||||
.Where(u => SqlFunc.IsNull(u.OrgId, 0) != 0 && u.UserId == user.Id)
|
||||
.Select(u => u.OrgId)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
// 如果存在本部门及以下,则获取本部门及以下机构的Id
|
||||
var dataScopes = db.Queryable<SysRole>().Where(u => roleIds.Contains(u.Id)).Select(u => u.DataScope).Distinct().ToList();
|
||||
if (dataScopes.All(u => u != DataScopeEnum.All) && dataScopes.Any(u => u == DataScopeEnum.DeptChild))
|
||||
{
|
||||
var childOrg = db.Queryable<SysOrg>().IgnoreTenant().ToTree(u => u.Children, u => u.Pid, user.OrgId);
|
||||
if (childOrg is not { Count: > 0 }) return orgIds;
|
||||
var queue = new Queue<SysOrg>(childOrg);
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
var org = queue.Dequeue();
|
||||
if (org.Children is { Count: > 0 }) queue.EnqueueRange(org.Children);
|
||||
orgIds.Add(org.Id);
|
||||
}
|
||||
}
|
||||
return orgIds;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刷新用户状态
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
[NonAction]
|
||||
public async Task RefreshUserManager(long userId)
|
||||
{
|
||||
var user = await _sysUserRep.AsQueryable().IgnoreTenant().Includes(u => u.SysOrg).FirstAsync(u => u.Id == userId);
|
||||
await SetUserManager(user, CommonHelper.IsMobile(_httpContextAccessor.HttpContext?.Request.Headers.UserAgent ?? "") ? LoginModeEnum.APP : LoginModeEnum.PC);
|
||||
}
|
||||
}
|
||||
@ -15,9 +15,7 @@ public class SysCacheService : IDynamicApiController, ISingleton
|
||||
private static ICacheProvider _cacheProvider;
|
||||
private readonly CacheOptions _cacheOptions;
|
||||
|
||||
public SysCacheService(
|
||||
ICacheProvider cacheProvider,
|
||||
IOptions<CacheOptions> cacheOptions)
|
||||
public SysCacheService(ICacheProvider cacheProvider, IOptions<CacheOptions> cacheOptions)
|
||||
{
|
||||
_cacheProvider = cacheProvider;
|
||||
_cacheOptions = cacheOptions.Value;
|
||||
@ -430,20 +428,9 @@ public class SysCacheService : IDynamicApiController, ISingleton
|
||||
[ApiDescriptionSettings(Name = "Clear"), HttpPost]
|
||||
public void Clear()
|
||||
{
|
||||
// 超管用户操作,清空所有缓存
|
||||
var userManager = App.GetService<UserManager>();
|
||||
if (userManager.SuperAdmin)
|
||||
{
|
||||
_cacheProvider.Cache.Clear();
|
||||
Cache.Default.Clear();
|
||||
return;
|
||||
}
|
||||
_cacheProvider.Cache.Clear();
|
||||
|
||||
// 排除非本租户、以及超管的 用户状态 缓存
|
||||
var sysUserRep = App.GetService<SqlSugarRepository<SysUser>>();
|
||||
var userIds = sysUserRep.AsQueryable().Where(u => u.AccountType != AccountTypeEnum.SuperAdmin).Select(u => u.Id).ToList().Select(u => u.ToString()).ToList();
|
||||
var keys = _cacheProvider.Cache.Keys.Where(key => !key.StartsWith(CacheConst.KeyUserManager) || userIds.Any(key.EndsWith)).ToList();
|
||||
keys.ForEach(key => _cacheProvider.Cache.Remove(key));
|
||||
Cache.Default.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -457,17 +444,6 @@ public class SysCacheService : IDynamicApiController, ISingleton
|
||||
return _cacheProvider.Cache.ContainsKey($"{_cacheOptions.Prefix}{key}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查缓存是否不存在
|
||||
/// </summary>
|
||||
/// <param name="key">键</param>
|
||||
/// <returns></returns>
|
||||
[NonAction]
|
||||
public bool NotExistKey(string key)
|
||||
{
|
||||
return !_cacheProvider.Cache.ContainsKey($"{_cacheOptions.Prefix}{key}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据键名前缀删除缓存 🔖
|
||||
/// </summary>
|
||||
|
||||
@ -17,7 +17,6 @@ public class SysCodeGenService : IDynamicApiController, ITransient
|
||||
{
|
||||
private readonly ISqlSugarClient _db;
|
||||
private readonly CodeGenOptions _codeGenOptions;
|
||||
private readonly SysCacheService _sysCacheService;
|
||||
private readonly DbConnectionOptions _dbConnectionOptions;
|
||||
private readonly SysCodeGenConfigService _codeGenConfigService;
|
||||
private readonly IViewEngine _viewEngine;
|
||||
@ -26,14 +25,12 @@ public class SysCodeGenService : IDynamicApiController, ITransient
|
||||
IOptions<CodeGenOptions> codeGenOptions,
|
||||
IOptions<DbConnectionOptions> dbConnectionOptions,
|
||||
SysCodeGenConfigService codeGenConfigService,
|
||||
SysCacheService sysCacheService,
|
||||
IViewEngine viewEngine)
|
||||
{
|
||||
_db = db;
|
||||
_dbConnectionOptions = dbConnectionOptions.Value;
|
||||
_codeGenOptions = codeGenOptions.Value;
|
||||
_codeGenConfigService = codeGenConfigService;
|
||||
_sysCacheService = sysCacheService;
|
||||
_viewEngine = viewEngine;
|
||||
}
|
||||
|
||||
@ -603,7 +600,7 @@ public class SysCodeGenService : IDynamicApiController, ITransient
|
||||
await _db.Insertable(menus).ExecuteCommandAsync();
|
||||
|
||||
// 删除角色菜单按钮缓存
|
||||
_sysCacheService.RemoveByPrefixKey(CacheConst.KeyUserManager);
|
||||
App.GetRequiredService<SysCacheService>().RemoveByPrefixKey(CacheConst.KeyUserApi);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -14,7 +14,6 @@ namespace Admin.NET.Core.Service;
|
||||
/// 系统通用服务 🧩
|
||||
/// </summary>
|
||||
[ApiDescriptionSettings(Order = 101, Description = "通用接口")]
|
||||
[AllowAnonymous]
|
||||
public class SysCommonService : IDynamicApiController, ITransient
|
||||
{
|
||||
private readonly SqlSugarRepository<SysTextAbbr> _sysTextAbbrRep;
|
||||
@ -172,8 +171,16 @@ public class SysCommonService : IDynamicApiController, ITransient
|
||||
[DisplayName("获取所有移动端接口")]
|
||||
public List<string> GetAppApiList()
|
||||
{
|
||||
var allApi = App.GetService<SysAuthService>().GetSysAllApiInfoList();
|
||||
return allApi.Where(u => u.IsAppApi).Select(u => u.Route).ToList();
|
||||
List<string> apiList = [];
|
||||
var queue = new Queue<ApiOutput>(GetApiList());
|
||||
var item = queue.Dequeue();
|
||||
while (item != null)
|
||||
{
|
||||
if (item.Children is { Count: > 0 }) queue.EnqueueRange(item.Children);
|
||||
else apiList.Add(item.Route);
|
||||
item = queue.Count > 0 ? queue.Dequeue() : null;
|
||||
}
|
||||
return apiList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -179,7 +179,6 @@ public class SysDbBackupService : IDynamicApiController, ITransient
|
||||
/// <param name="fileName">备份文件名</param>
|
||||
[ApiDescriptionSettings(Name = "Download"), HttpGet]
|
||||
[DisplayName("下载备份")]
|
||||
[AllowAnonymous]
|
||||
[NonUnify]
|
||||
public IActionResult Download(string fileName)
|
||||
{
|
||||
|
||||
@ -194,7 +194,7 @@ public class SysDictDataService : IDynamicApiController, ITransient
|
||||
if (dictDataList == null)
|
||||
{
|
||||
dictDataList = await _sysDictDataRep.Change<SysDictType>().AsQueryable()
|
||||
.LeftJoin<SysDictData>((u, a) => u.Id == a.DictTypeId).IgnoreTenant()
|
||||
.LeftJoin<SysDictData>((u, a) => u.Id == a.DictTypeId).ClearFilter<ITenantIdFilter>()
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(code), u => u.Code == code)
|
||||
.WhereIF(typeId != null && typeId > 0, u => u.Id == typeId)
|
||||
.Where((u, a) => u.Status == StatusEnum.Enable && a.Status == StatusEnum.Enable)
|
||||
|
||||
@ -164,7 +164,7 @@ public class SysDictTypeService : IDynamicApiController, ITransient
|
||||
public async Task<dynamic> GetAllDictList()
|
||||
{
|
||||
var ds = await _sysDictTypeRep.AsQueryable()
|
||||
.InnerJoin<SysDictData>((u, a) => u.Id == a.DictTypeId).IgnoreTenant()
|
||||
.InnerJoin<SysDictData>((u, a) => u.Id == a.DictTypeId).ClearFilter<ITenantIdFilter>()
|
||||
.Where((u, a) => u.Status == StatusEnum.Enable && a.Status == StatusEnum.Enable)
|
||||
.Select((u, a) => new { TypeCode = u.Code, a.Label, a.Value, a.Code, a.Remark, a.OrderNo, a.TagType, a.ExtData })
|
||||
.ToListAsync();
|
||||
|
||||
@ -61,7 +61,7 @@ public class SysFileService : IDynamicApiController, ITransient
|
||||
public async Task<SqlSugarPagedList<SysFile>> Page(PageFileInput input)
|
||||
{
|
||||
// 获取所有公开文件
|
||||
var publicList = _sysFileRep.AsQueryable().IgnoreTenant().Where(u => u.IsPublic == true);
|
||||
var publicList = _sysFileRep.AsQueryable().ClearFilter<ITenantIdFilter>().Where(u => u.IsPublic == true);
|
||||
// 获取私有文件
|
||||
var privateList = _sysFileRep.AsQueryable().Where(u => u.IsPublic == false);
|
||||
// 合并公开和私有并分页
|
||||
@ -179,7 +179,7 @@ public class SysFileService : IDynamicApiController, ITransient
|
||||
[DisplayName("下载指定文件Base64格式")]
|
||||
public async Task<string> DownloadFileBase64([FromBody] string url)
|
||||
{
|
||||
var sysFile = await _sysFileRep.AsQueryable().IgnoreTenant().FirstAsync(u => u.Url == url) ?? throw Oops.Oh($"文件不存在");
|
||||
var sysFile = await _sysFileRep.AsQueryable().ClearFilter<ITenantIdFilter>().FirstAsync(u => u.Url == url) ?? throw Oops.Oh($"文件不存在");
|
||||
return await _customFileProvider.DownloadFileBase64Async(sysFile);
|
||||
}
|
||||
|
||||
@ -222,7 +222,7 @@ public class SysFileService : IDynamicApiController, ITransient
|
||||
public async Task<SysFile> GetFile([FromQuery] long id, [FromQuery] bool isClearFilter = false)
|
||||
{
|
||||
var file = isClearFilter
|
||||
? await _sysFileRep.CopyNew().AsQueryable().IgnoreTenant().FirstAsync(u => u.Id == id)
|
||||
? await _sysFileRep.CopyNew().AsQueryable().ClearFilter<ITenantIdFilter>().FirstAsync(u => u.Id == id)
|
||||
: await _sysFileRep.CopyNew().GetByIdAsync(id);
|
||||
return file ?? throw Oops.Oh(ErrorCodeEnum.D8000);
|
||||
}
|
||||
@ -456,7 +456,7 @@ public class SysFileService : IDynamicApiController, ITransient
|
||||
public async Task<List<SysFile>> GetFileListByDataId([FromQuery] long dataId, [FromQuery] bool isClearFilter = false)
|
||||
{
|
||||
return isClearFilter
|
||||
? await _sysFileRep.CopyNew().AsQueryable().IgnoreTenant().Where(u => u.DataId == dataId).ToListAsync()
|
||||
? await _sysFileRep.CopyNew().AsQueryable().ClearFilter<ITenantIdFilter>().Where(u => u.DataId == dataId).ToListAsync()
|
||||
: await _sysFileRep.CopyNew().GetListAsync(u => u.DataId == dataId);
|
||||
}
|
||||
|
||||
|
||||
@ -35,7 +35,6 @@ public class SysMenuService : IDynamicApiController, ITransient
|
||||
/// 获取登录菜单树 🔖
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[AllowAnonymous]
|
||||
[DisplayName("获取登录菜单树")]
|
||||
public async Task<List<MenuOutput>> GetLoginMenuTree()
|
||||
{
|
||||
@ -52,10 +51,11 @@ public class SysMenuService : IDynamicApiController, ITransient
|
||||
}
|
||||
else
|
||||
{
|
||||
var menuIdList = await GetMenuIdList();
|
||||
var menuTree = await _sysMenuRep.AsQueryable()
|
||||
.Where(u => u.Status == StatusEnum.Enable)
|
||||
.OrderBy(u => new { u.OrderNo, u.Id })
|
||||
.Distinct().ToTreeAsync(u => u.Children, u => u.Pid, 0, _userManager.MenuIds.Select(d => (object)d).ToArray());
|
||||
.Distinct().ToTreeAsync(u => u.Children, u => u.Pid, 0, menuIdList.Select(d => (object)d).ToArray());
|
||||
DeleteBtnFromMenuTree(menuTree);
|
||||
return menuTree.Adapt<List<MenuOutput>>();
|
||||
}
|
||||
@ -99,7 +99,7 @@ public class SysMenuService : IDynamicApiController, ITransient
|
||||
|
||||
return _userManager.SuperAdmin ?
|
||||
await _sysMenuRep.AsQueryable().OrderBy(u => new { u.OrderNo, u.Id })
|
||||
.Distinct().ToTreeAsync(u => u.Children, u => u.Pid, 0) :
|
||||
.Distinct().Distinct().ToTreeAsync(u => u.Children, u => u.Pid, 0) :
|
||||
await _sysMenuRep.AsQueryable().OrderBy(u => new { u.OrderNo, u.Id })
|
||||
.Distinct().ToTreeAsync(u => u.Children, u => u.Pid, 0, menuIdList.Select(d => (object)d).ToArray());
|
||||
}
|
||||
@ -135,12 +135,7 @@ public class SysMenuService : IDynamicApiController, ITransient
|
||||
var sysMenu = input.Adapt<SysMenu>();
|
||||
CheckMenuParam(sysMenu);
|
||||
|
||||
var newMenuId = await _sysMenuRep.InsertReturnSnowflakeIdAsync(sysMenu);
|
||||
// 将新增的菜单Id增加到当前账号角色菜单集合
|
||||
await AddRoleMenuId(newMenuId);
|
||||
|
||||
// 菜单按钮增加了权限标识,全部用户状态都需要更新
|
||||
if (input.Type == MenuTypeEnum.Btn) LazyHelper.GetService<UserManager>().Value.RemoveAll();
|
||||
await _sysMenuRep.InsertAsync(sysMenu);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -152,33 +147,31 @@ public class SysMenuService : IDynamicApiController, ITransient
|
||||
[DisplayName("更新菜单")]
|
||||
public async Task UpdateMenu(UpdateMenuInput input)
|
||||
{
|
||||
if (input.Id == input.Pid) throw Oops.Oh(ErrorCodeEnum.D4008);
|
||||
if (input.Id == input.Pid)
|
||||
throw Oops.Oh(ErrorCodeEnum.D4008);
|
||||
|
||||
var isExist = input.Type != MenuTypeEnum.Btn
|
||||
? await _sysMenuRep.IsAnyAsync(u => u.Title == input.Title && u.Type == input.Type && u.Pid == input.Pid && u.Id != input.Id)
|
||||
: await _sysMenuRep.IsAnyAsync(u => u.Pid == input.Pid && u.Permission == input.Permission && u.Id != input.Id);
|
||||
if (isExist) throw Oops.Oh(ErrorCodeEnum.D4000);
|
||||
if (isExist)
|
||||
throw Oops.Oh(ErrorCodeEnum.D4000);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(input.Name))
|
||||
{
|
||||
if (await _sysMenuRep.IsAnyAsync(u => u.Id != input.Id && u.Name == input.Name)) throw Oops.Oh(ErrorCodeEnum.D4009);
|
||||
if (await _sysMenuRep.IsAnyAsync(u => u.Id != input.Id && u.Name == input.Name))
|
||||
throw Oops.Oh(ErrorCodeEnum.D4009);
|
||||
}
|
||||
|
||||
if (input.Pid != 0)
|
||||
{
|
||||
if (await _sysMenuRep.IsAnyAsync(u => u.Id == input.Pid && u.Type == MenuTypeEnum.Btn)) throw Oops.Oh(ErrorCodeEnum.D4010);
|
||||
if (await _sysMenuRep.IsAnyAsync(u => u.Id == input.Pid && u.Type == MenuTypeEnum.Btn))
|
||||
throw Oops.Oh(ErrorCodeEnum.D4010);
|
||||
}
|
||||
|
||||
// 校验菜单参数
|
||||
var sysMenu = input.Adapt<SysMenu>();
|
||||
CheckMenuParam(sysMenu);
|
||||
|
||||
// 菜单按钮增加了权限标识,全部用户状态都需要更新
|
||||
if (input.Type == MenuTypeEnum.Btn || await _sysMenuRep.IsAnyAsync(u => u.Id == input.Id && u.Type == MenuTypeEnum.Btn))
|
||||
{
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveAll();
|
||||
}
|
||||
|
||||
await _sysMenuRep.AsUpdateable(sysMenu).ExecuteCommandAsync();
|
||||
}
|
||||
|
||||
@ -202,9 +195,6 @@ public class SysMenuService : IDynamicApiController, ITransient
|
||||
|
||||
// 级联删除用户收藏菜单
|
||||
await _sysUserMenuService.DeleteMenuList(menuIdList);
|
||||
|
||||
// 删除菜单全部数据都需要更新
|
||||
if (menuTreeList.Any(u => u.Type == MenuTypeEnum.Btn)) LazyHelper.GetService<UserManager>().Value.RemoveAll();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -213,11 +203,11 @@ public class SysMenuService : IDynamicApiController, ITransient
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[DisplayName("设置菜单状态")]
|
||||
public async Task<bool> SetStatus(BaseStatusInput input)
|
||||
public async Task<int> SetStatus(BaseStatusInput input)
|
||||
{
|
||||
var menu = await _sysMenuRep.GetByIdAsync(input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
|
||||
if (menu.Type == MenuTypeEnum.Btn) LazyHelper.GetService<UserManager>().Value.RemoveAll();
|
||||
return await _sysMenuRep.UpdateAsync(u => new() { Status = input.Status }, u => u.Id == input.Id);
|
||||
menu.Status = input.Status;
|
||||
return await _sysMenuRep.AsUpdateable(menu).UpdateColumns(u => new { u.Status }).ExecuteCommandAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -260,15 +250,4 @@ public class SysMenuService : IDynamicApiController, ITransient
|
||||
var roleIdList = await _sysUserRoleService.GetUserRoleIdList(_userManager.UserId);
|
||||
return await _sysRoleMenuService.GetRoleMenuIdList(roleIdList);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前账号增加菜单后更新对应角色菜单集合
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private async Task AddRoleMenuId(long menuId)
|
||||
{
|
||||
if (_userManager.SuperAdmin) return;
|
||||
await _sysRoleMenuService.GrantRoleMenu(new RoleMenuInput { Id = _userManager.RoleIds[0], MenuIdList = [menuId] }, false);
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveByRoleId([_userManager.RoleIds[0]]);
|
||||
}
|
||||
}
|
||||
@ -14,7 +14,6 @@ namespace Admin.NET.Core.Service;
|
||||
/// <summary>
|
||||
/// 系统短信服务 🧩
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
[ApiDescriptionSettings(Order = 150, Description = "短信发送")]
|
||||
public class SysSmsService : IDynamicApiController, ITransient
|
||||
{
|
||||
|
||||
@ -85,7 +85,7 @@ public class SysOAuthService : IDynamicApiController, ITransient
|
||||
}
|
||||
|
||||
// 若账号不存在则新建
|
||||
var wechatUser = await _sysOAuthUserRep.AsQueryable().Includes(u => u.SysUser).IgnoreTenant().FirstAsync(u => u.OpenId == openIdClaim.Value);
|
||||
var wechatUser = await _sysOAuthUserRep.AsQueryable().Includes(u => u.SysUser).ClearFilter<ITenantIdFilter>().FirstAsync(u => u.OpenId == openIdClaim.Value);
|
||||
if (wechatUser == null)
|
||||
{
|
||||
var userId = await App.GetRequiredService<SysUserService>().AddUser(new AddUserInput()
|
||||
@ -109,7 +109,7 @@ public class SysOAuthService : IDynamicApiController, ITransient
|
||||
PlatformType = platformType
|
||||
});
|
||||
|
||||
wechatUser = await _sysOAuthUserRep.AsQueryable().Includes(u => u.SysUser).IgnoreTenant().FirstAsync(u => u.OpenId == openIdClaim.Value);
|
||||
wechatUser = await _sysOAuthUserRep.AsQueryable().Includes(u => u.SysUser).ClearFilter<ITenantIdFilter>().FirstAsync(u => u.OpenId == openIdClaim.Value);
|
||||
}
|
||||
|
||||
// 构建Token令牌,默认回调登录为PC模式
|
||||
|
||||
@ -17,22 +17,15 @@ public class SysOpenAccessService : IDynamicApiController, ITransient
|
||||
{
|
||||
private readonly SqlSugarRepository<SysOpenAccess> _sysOpenAccessRep;
|
||||
private readonly SysCacheService _sysCacheService;
|
||||
private readonly SysAuthService _sysAuthService;
|
||||
private readonly UserManager _userManager;
|
||||
|
||||
/// <summary>
|
||||
/// 开放接口身份服务构造函数
|
||||
/// </summary>
|
||||
public SysOpenAccessService(
|
||||
SqlSugarRepository<SysOpenAccess> sysOpenAccessRep,
|
||||
SysCacheService sysCacheService,
|
||||
SysAuthService sysAuthService,
|
||||
UserManager userManager)
|
||||
public SysOpenAccessService(SqlSugarRepository<SysOpenAccess> sysOpenAccessRep,
|
||||
SysCacheService sysCacheService)
|
||||
{
|
||||
_sysOpenAccessRep = sysOpenAccessRep;
|
||||
_sysCacheService = sysCacheService;
|
||||
_sysAuthService = sysAuthService;
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -163,17 +156,6 @@ public class SysOpenAccessService : IDynamicApiController, ITransient
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据 Key 获取对象
|
||||
/// </summary>
|
||||
/// <param name="openAccess"></param>
|
||||
/// <returns></returns>
|
||||
[NonAction]
|
||||
private async Task TryRefreshUserManager(SysOpenAccess openAccess)
|
||||
{
|
||||
if (!_userManager.Exist(openAccess.BindUser.Id)) await _sysAuthService.RefreshUserManager(openAccess.BindUser.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Signature 身份验证事件默认实现
|
||||
/// </summary>
|
||||
@ -206,9 +188,15 @@ public class SysOpenAccessService : IDynamicApiController, ITransient
|
||||
identity.AddClaims(
|
||||
[
|
||||
new Claim(ClaimConst.UserId, openAccess.BindUserId + ""),
|
||||
new Claim(ClaimConst.TenantId, openAccess.BindTenantId + ""),
|
||||
new Claim(ClaimConst.Account, openAccess.BindUser.Account + ""),
|
||||
new Claim(ClaimConst.RealName, openAccess.BindUser.RealName),
|
||||
new Claim(ClaimConst.AccountType, ((int)openAccess.BindUser.AccountType).ToString()),
|
||||
new Claim(ClaimConst.OrgId, openAccess.BindUser.OrgId + ""),
|
||||
new Claim(ClaimConst.OrgName, openAccess.BindUser.SysOrg?.Name + ""),
|
||||
new Claim(ClaimConst.OrgType, openAccess.BindUser.SysOrg?.Type + ""),
|
||||
new Claim(ClaimConst.TokenVersion, openAccess.BindUser.TokenVersion + ""),
|
||||
]);
|
||||
openAccessService.TryRefreshUserManager(openAccess).GetAwaiter().GetResult();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
};
|
||||
|
||||
@ -132,15 +132,15 @@ public class SysOrgService : IDynamicApiController, ITransient
|
||||
}
|
||||
|
||||
// 删除与此父机构有关的用户机构缓存
|
||||
var userManager = LazyHelper.GetService<UserManager>().Value;
|
||||
if (input.Pid == 0)
|
||||
{
|
||||
userManager.RemoveAll();
|
||||
DeleteAllUserOrgCache(0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
var pOrg = await _sysOrgRep.GetByIdAsync(input.Pid);
|
||||
if (pOrg != null) userManager.RemoveByOrgId([pOrg.Id, input.Pid]);
|
||||
if (pOrg != null)
|
||||
DeleteAllUserOrgCache(pOrg.Id, pOrg.Pid);
|
||||
}
|
||||
|
||||
var newOrg = await _sysOrgRep.AsInsertable(input.Adapt<SysOrg>()).ExecuteReturnEntityAsync();
|
||||
@ -158,7 +158,6 @@ public class SysOrgService : IDynamicApiController, ITransient
|
||||
DeleteAllUserOrgCache(0, 0);
|
||||
await _sysOrgRep.AsDeleteable().ExecuteCommandAsync();
|
||||
await _sysOrgRep.AsInsertable(orgs).ExecuteCommandAsync();
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveAll();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -171,30 +170,42 @@ public class SysOrgService : IDynamicApiController, ITransient
|
||||
[DisplayName("更新机构")]
|
||||
public async Task UpdateOrg(UpdateOrgInput input)
|
||||
{
|
||||
if (!_userManager.SuperAdmin && input.Pid == 0) throw Oops.Oh(ErrorCodeEnum.D2010);
|
||||
if (input.Id == input.Pid) throw Oops.Oh(ErrorCodeEnum.D2001);
|
||||
if (!_userManager.SuperAdmin && input.Pid == 0)
|
||||
throw Oops.Oh(ErrorCodeEnum.D2010);
|
||||
|
||||
if (await _sysOrgRep.IsAnyAsync(u => u.Name == input.Name && u.Code == input.Code && u.Id != input.Id)) throw Oops.Oh(ErrorCodeEnum.D2002);
|
||||
if (input.Pid != 0)
|
||||
{
|
||||
//var pOrg = await _sysOrgRep.GetByIdAsync(input.Pid);
|
||||
//_ = pOrg ?? throw Oops.Oh(ErrorCodeEnum.D2000);
|
||||
|
||||
// 若父机构发生变化则清空用户机构缓存
|
||||
var sysOrg = await _sysOrgRep.GetByIdAsync(input.Id);
|
||||
if (sysOrg != null && sysOrg.Pid != input.Pid)
|
||||
{
|
||||
// 删除与此机构、新父机构有关的用户机构缓存
|
||||
DeleteAllUserOrgCache(sysOrg.Id, input.Pid);
|
||||
}
|
||||
}
|
||||
if (input.Id == input.Pid)
|
||||
throw Oops.Oh(ErrorCodeEnum.D2001);
|
||||
|
||||
if (await _sysOrgRep.IsAnyAsync(u => u.Name == input.Name && u.Code == input.Code && u.Id != input.Id))
|
||||
throw Oops.Oh(ErrorCodeEnum.D2002);
|
||||
|
||||
// 父Id不能为自己的子节点
|
||||
var childIdList = await GetChildIdListWithSelfById(input.Id);
|
||||
if (childIdList.Contains(input.Pid)) throw Oops.Oh(ErrorCodeEnum.D2001);
|
||||
if (childIdList.Contains(input.Pid))
|
||||
throw Oops.Oh(ErrorCodeEnum.D2001);
|
||||
|
||||
// 是否有权限操作此机构
|
||||
if (!_userManager.SuperAdmin)
|
||||
{
|
||||
var orgIdList = _userManager.OrgIds;
|
||||
if (orgIdList.Count < 1 || !orgIdList.Contains(input.Id)) throw Oops.Oh(ErrorCodeEnum.D2003);
|
||||
var orgIdList = await GetUserOrgIdList();
|
||||
if (orgIdList.Count < 1 || !orgIdList.Contains(input.Id))
|
||||
throw Oops.Oh(ErrorCodeEnum.D2003);
|
||||
}
|
||||
|
||||
await _sysOrgRep.AsUpdateable(input.Adapt<SysOrg>()).IgnoreColumns(true).ExecuteCommandAsync();
|
||||
|
||||
if (input.Pid != 0)
|
||||
{
|
||||
// 若父机构发生变化则清空用户机构缓存
|
||||
var sysOrg = await _sysOrgRep.GetByIdAsync(input.Id);
|
||||
if (sysOrg != null && sysOrg.Pid != input.Pid) LazyHelper.GetService<UserManager>().Value.RemoveByOrgId([input.Pid, input.Id]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -213,22 +224,26 @@ public class SysOrgService : IDynamicApiController, ITransient
|
||||
if (!_userManager.SuperAdmin)
|
||||
{
|
||||
var orgIdList = await GetUserOrgIdList();
|
||||
if (orgIdList.Count < 1 || !orgIdList.Contains(sysOrg.Id)) throw Oops.Oh(ErrorCodeEnum.D2003);
|
||||
if (orgIdList.Count < 1 || !orgIdList.Contains(sysOrg.Id))
|
||||
throw Oops.Oh(ErrorCodeEnum.D2003);
|
||||
}
|
||||
|
||||
// 若机构为租户默认机构禁止删除
|
||||
var isTenantOrg = await _sysOrgRep.ChangeRepository<SqlSugarRepository<SysTenant>>()
|
||||
.IsAnyAsync(u => u.OrgId == input.Id);
|
||||
if (isTenantOrg) throw Oops.Oh(ErrorCodeEnum.D2008);
|
||||
if (isTenantOrg)
|
||||
throw Oops.Oh(ErrorCodeEnum.D2008);
|
||||
|
||||
// 若机构有用户则禁止删除
|
||||
var orgHasEmp = await _sysOrgRep.ChangeRepository<SqlSugarRepository<SysUser>>()
|
||||
.IsAnyAsync(u => u.OrgId == input.Id);
|
||||
if (orgHasEmp) throw Oops.Oh(ErrorCodeEnum.D2004);
|
||||
if (orgHasEmp)
|
||||
throw Oops.Oh(ErrorCodeEnum.D2004);
|
||||
|
||||
// 若扩展机构有用户则禁止删除
|
||||
var hasExtOrgEmp = await _sysUserExtOrgService.HasUserOrg(sysOrg.Id);
|
||||
if (hasExtOrgEmp) throw Oops.Oh(ErrorCodeEnum.D2005);
|
||||
if (hasExtOrgEmp)
|
||||
throw Oops.Oh(ErrorCodeEnum.D2005);
|
||||
|
||||
// 若子机构有用户则禁止删除
|
||||
var childOrgTreeList = await _sysOrgRep.AsQueryable().ToChildListAsync(u => u.Pid, input.Id, true);
|
||||
@ -237,7 +252,8 @@ public class SysOrgService : IDynamicApiController, ITransient
|
||||
// 若子机构有用户则禁止删除
|
||||
var cOrgHasEmp = await _sysOrgRep.ChangeRepository<SqlSugarRepository<SysUser>>()
|
||||
.IsAnyAsync(u => childOrgIdList.Contains(u.OrgId));
|
||||
if (cOrgHasEmp) throw Oops.Oh(ErrorCodeEnum.D2007);
|
||||
if (cOrgHasEmp)
|
||||
throw Oops.Oh(ErrorCodeEnum.D2007);
|
||||
|
||||
// 删除与此机构、父机构有关的用户机构缓存
|
||||
DeleteAllUserOrgCache(sysOrg.Id, sysOrg.Pid);
|
||||
@ -250,8 +266,6 @@ public class SysOrgService : IDynamicApiController, ITransient
|
||||
|
||||
// 级联删除用户机构数据
|
||||
await _sysUserExtOrgService.DeleteUserExtOrgByOrgIdList(childOrgIdList);
|
||||
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveByOrgId([sysOrg.Pid, sysOrg.Id]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -329,6 +343,26 @@ public class SysOrgService : IDynamicApiController, ITransient
|
||||
return await GetUserOrgIdList(roleList, userId, userOrgId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判定用户是否有某角色权限
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="role">角色代码</param>
|
||||
/// <returns></returns>
|
||||
[NonAction]
|
||||
public async Task<bool> GetUserHasRole(long userId, SysRole role)
|
||||
{
|
||||
if (_userManager.SuperAdmin) return true;
|
||||
|
||||
var userOrgId = _userManager.OrgId;
|
||||
var roleList = await _sysUserRoleService.GetUserRoleList(userId);
|
||||
if (roleList != null && roleList.Exists(r => r.Code == role.Code)) return true;
|
||||
|
||||
roleList = [role];
|
||||
var orgIds = await GetUserOrgIdList(roleList, userId, userOrgId);
|
||||
return orgIds.Contains(userOrgId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据角色Id集合获取机构Id集合
|
||||
/// </summary>
|
||||
@ -366,8 +400,8 @@ public class SysOrgService : IDynamicApiController, ITransient
|
||||
});
|
||||
}
|
||||
|
||||
// // 缓存当前用户最大角色数据范围
|
||||
// _sysCacheService.Set(CacheConst.KeyRoleMaxDataScope + userId, strongerDataScopeType, TimeSpan.FromDays(7));
|
||||
// 缓存当前用户最大角色数据范围
|
||||
_sysCacheService.Set(CacheConst.KeyRoleMaxDataScope + userId, strongerDataScopeType, TimeSpan.FromDays(7));
|
||||
|
||||
// 根据角色集合获取机构集合
|
||||
var roleOrgIdList = await _sysRoleOrgService.GetRoleOrgIdList(customDataScopeRoleIdList);
|
||||
|
||||
@ -78,6 +78,7 @@ public class SysPosService : IDynamicApiController, ITransient
|
||||
public async Task AddPos(AddPosInput input)
|
||||
{
|
||||
if (await _sysPosRep.IsAnyAsync(u => u.Name == input.Name && u.Code == input.Code)) throw Oops.Oh(ErrorCodeEnum.D6000);
|
||||
|
||||
await _sysPosRep.InsertAsync(input.Adapt<SysPos>());
|
||||
}
|
||||
|
||||
@ -96,7 +97,6 @@ public class SysPosService : IDynamicApiController, ITransient
|
||||
if (!_userManager.SuperAdmin && sysPos.CreateUserId != _userManager.UserId) throw Oops.Oh(ErrorCodeEnum.D6002);
|
||||
|
||||
await _sysPosRep.AsUpdateable(input.Adapt<SysPos>()).IgnoreColumns(true).ExecuteCommandAsync();
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveByPosId([input.Id]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -121,7 +121,6 @@ public class SysPosService : IDynamicApiController, ITransient
|
||||
if (hasExtPosEmp) throw Oops.Oh(ErrorCodeEnum.D6001);
|
||||
|
||||
await _sysPosRep.DeleteByIdAsync(input.Id);
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveByPosId([input.Id]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -130,7 +130,7 @@ public class SysReportDataSourceService : IDynamicApiController, ITransient
|
||||
|
||||
// 从租户获取数据源
|
||||
var tenantList = await _tenantRep.AsQueryable()
|
||||
.LeftJoin<SysOrg>((u, o) => u.OrgId == o.Id).IgnoreTenant()
|
||||
.LeftJoin<SysOrg>((u, o) => u.OrgId == o.Id).ClearFilter<ITenantIdFilter>()
|
||||
.Where((u, o) => u.TenantType == TenantTypeEnum.Db)
|
||||
.Select((u, o) => new { u.Id, o.Name, u.DbType, u.Connection })
|
||||
.ToListAsync();
|
||||
|
||||
@ -36,7 +36,6 @@ public class SysRoleApiService : ITransient
|
||||
Route = u
|
||||
}).ToList();
|
||||
await _sysRoleApiRep.InsertRangeAsync(roleApis);
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveByRoleId([input.Id]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -59,7 +58,6 @@ public class SysRoleApiService : ITransient
|
||||
public async Task DeleteRoleApiByRoleId(long roleId)
|
||||
{
|
||||
await _sysRoleApiRep.DeleteAsync(u => u.RoleId == roleId);
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveByRoleId([roleId]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -77,6 +75,5 @@ public class SysRoleApiService : ITransient
|
||||
u.RoleId = newRoleId;
|
||||
});
|
||||
await _sysRoleApiRep.InsertRangeAsync(roleApiList);
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveByRoleId([newRoleId]);
|
||||
}
|
||||
}
|
||||
@ -34,20 +34,16 @@ public class SysRoleMenuService : ITransient
|
||||
/// 授权角色菜单
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="isDelete"></param>
|
||||
/// <returns></returns>
|
||||
public async Task GrantRoleMenu(RoleMenuInput input, bool isDelete = true)
|
||||
public async Task GrantRoleMenu(RoleMenuInput input)
|
||||
{
|
||||
if (isDelete)
|
||||
await _sysRoleMenuRep.DeleteAsync(u => u.RoleId == input.Id);
|
||||
|
||||
await _sysRoleMenuRep.DeleteAsync(u => u.RoleId == input.Id);
|
||||
var menus = input.MenuIdList.Select(u => new SysRoleMenu
|
||||
{
|
||||
RoleId = input.Id,
|
||||
MenuId = u
|
||||
}).ToList();
|
||||
await _sysRoleMenuRep.InsertRangeAsync(menus);
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveByRoleId([input.Id]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -57,9 +53,7 @@ public class SysRoleMenuService : ITransient
|
||||
/// <returns></returns>
|
||||
public async Task DeleteRoleMenuByMenuIdList(List<long> menuIdList)
|
||||
{
|
||||
var roleIds = await _sysRoleMenuRep.AsQueryable().Where(u => menuIdList.Contains(u.MenuId)).Select(u => u.RoleId).Distinct().ToListAsync();
|
||||
await _sysRoleMenuRep.DeleteAsync(u => menuIdList.Contains(u.MenuId));
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveByRoleId(roleIds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -70,7 +64,6 @@ public class SysRoleMenuService : ITransient
|
||||
public async Task DeleteRoleMenuByRoleId(long roleId)
|
||||
{
|
||||
await _sysRoleMenuRep.DeleteAsync(u => u.RoleId == roleId);
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveByRoleId([roleId]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -88,6 +81,5 @@ public class SysRoleMenuService : ITransient
|
||||
u.RoleId = newRoleId;
|
||||
});
|
||||
await _sysRoleMenuRep.InsertRangeAsync(roleMenuList);
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveByRoleId([newRoleId]);
|
||||
}
|
||||
}
|
||||
@ -35,7 +35,6 @@ public class SysRoleOrgService : ITransient
|
||||
}).ToList();
|
||||
await _sysRoleOrgRep.InsertRangeAsync(roleOrgList);
|
||||
}
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveByRoleId([input.Id]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -51,7 +50,7 @@ public class SysRoleOrgService : ITransient
|
||||
.Where(u => roleIdList.Contains(u.RoleId))
|
||||
.Select(u => u.OrgId).ToListAsync();
|
||||
}
|
||||
return new List<long>();
|
||||
else return new List<long>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -61,9 +60,7 @@ public class SysRoleOrgService : ITransient
|
||||
/// <returns></returns>
|
||||
public async Task DeleteRoleOrgByOrgIdList(List<long> orgIdList)
|
||||
{
|
||||
var roleIdList = await _sysRoleOrgRep.AsQueryable().Where(u => orgIdList.Contains(u.OrgId)).Select(u => u.RoleId).Distinct().ToListAsync();
|
||||
await _sysRoleOrgRep.DeleteAsync(u => orgIdList.Contains(u.OrgId));
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveByRoleId(roleIdList);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -74,7 +71,6 @@ public class SysRoleOrgService : ITransient
|
||||
public async Task DeleteRoleOrgByRoleId(long roleId)
|
||||
{
|
||||
await _sysRoleOrgRep.DeleteAsync(u => u.RoleId == roleId);
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveByRoleId([roleId]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -92,8 +88,5 @@ public class SysRoleOrgService : ITransient
|
||||
u.RoleId = newRoleId;
|
||||
});
|
||||
await _sysRoleOrgRep.InsertRangeAsync(roleOrgList);
|
||||
|
||||
// 清除关联用户状态
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveByRoleId([newRoleId]);
|
||||
}
|
||||
}
|
||||
@ -126,8 +126,6 @@ public class SysRoleService : IDynamicApiController, ITransient
|
||||
|
||||
await _sysRoleRep.AsUpdateable(input.Adapt<SysRole>()).IgnoreColumns(true)
|
||||
.IgnoreColumns(u => new { u.DataScope }).ExecuteCommandAsync();
|
||||
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveByRoleId([input.Id]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -164,9 +162,6 @@ public class SysRoleService : IDynamicApiController, ITransient
|
||||
|
||||
// 级联删除角色表格数据
|
||||
await _sysRoleTableService.DeleteRolTableByRoleId(sysRole.Id);
|
||||
|
||||
// 删除关联的用户状态
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveByRoleId([input.Id]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -206,9 +201,6 @@ public class SysRoleService : IDynamicApiController, ITransient
|
||||
|
||||
// 复制角色表格数据
|
||||
await _sysRoleTableService.CopyRolTableByRoleId(input.Id, newRoleId);
|
||||
|
||||
// 清除关联用户状态
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveByRoleId([newRoleId]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -221,8 +213,18 @@ public class SysRoleService : IDynamicApiController, ITransient
|
||||
{
|
||||
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);
|
||||
//var menuIds = input.MenuIdList.Except(pMenuIds); // 差集
|
||||
//await _sysRoleMenuService.GrantRoleMenu(new RoleMenuInput()
|
||||
//{
|
||||
// Id = input.Id,
|
||||
// MenuIdList = menuIds.ToList()
|
||||
//});
|
||||
|
||||
await _sysRoleMenuService.GrantRoleMenu(input);
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveByRoleId([input.Id]);
|
||||
|
||||
await ClearUserApiCache(input.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -267,7 +269,7 @@ public class SysRoleService : IDynamicApiController, ITransient
|
||||
var grantOrgIdList = input.OrgIdList;
|
||||
if (grantOrgIdList.Count > 0)
|
||||
{
|
||||
var orgIdList = _userManager.OrgIds;
|
||||
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);
|
||||
}
|
||||
@ -283,8 +285,6 @@ public class SysRoleService : IDynamicApiController, ITransient
|
||||
role.DataScope = (DataScopeEnum)dataScope;
|
||||
await _sysRoleRep.AsUpdateable(role).UpdateColumns(u => new { u.DataScope }).ExecuteCommandAsync();
|
||||
await _sysRoleOrgService.GrantRoleOrg(input);
|
||||
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveByRoleId([input.Id]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -296,8 +296,8 @@ public class SysRoleService : IDynamicApiController, ITransient
|
||||
[DisplayName("授权角色接口")]
|
||||
public async Task GrantApi(RoleApiInput input)
|
||||
{
|
||||
await ClearUserApiCache(input.Id);
|
||||
await _sysRoleApiService.GrantRoleApi(input);
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveByRoleId([input.Id]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -310,10 +310,6 @@ public class SysRoleService : IDynamicApiController, ITransient
|
||||
public async Task GrantUser(RoleUserInput input)
|
||||
{
|
||||
await _sysUserRoleService.GrantRoleUser(input);
|
||||
|
||||
// 清除关联用户状态
|
||||
var userManager = LazyHelper.GetService<UserManager>().Value;
|
||||
foreach (var userId in input.UserIdList) userManager.Remove(userId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -326,15 +322,10 @@ public class SysRoleService : IDynamicApiController, ITransient
|
||||
{
|
||||
if (!Enum.IsDefined(input.Status)) throw Oops.Oh(ErrorCodeEnum.D3005);
|
||||
|
||||
var result = await _sysRoleRep.AsUpdateable()
|
||||
.SetColumns(u => u.Status, input.Status)
|
||||
return await _sysRoleRep.AsUpdateable()
|
||||
.SetColumns(u => u.Status == input.Status)
|
||||
.Where(u => u.Id == input.Id)
|
||||
.ExecuteCommandAsync();
|
||||
|
||||
// 清除关联用户状态
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveByRoleId([input.Id]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -374,7 +365,7 @@ public class SysRoleService : IDynamicApiController, ITransient
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[DisplayName("根据角色Id获取菜单Id集合")]
|
||||
public async Task<List<long>> GetOwnMenuList([FromQuery] RoleInput input)
|
||||
public async Task<List<long>> GetOwnMenuList([FromQuery] BaseIdInput input)
|
||||
{
|
||||
return await _sysRoleMenuService.GetRoleMenuIdList(new List<long> { input.Id });
|
||||
}
|
||||
@ -385,7 +376,7 @@ public class SysRoleService : IDynamicApiController, ITransient
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[DisplayName("根据角色Id获取机构Id集合")]
|
||||
public async Task<List<long>> GetOwnOrgList([FromQuery] RoleInput input)
|
||||
public async Task<List<long>> GetOwnOrgList([FromQuery] BaseIdInput input)
|
||||
{
|
||||
return await _sysRoleOrgService.GetRoleOrgIdList(new List<long> { input.Id });
|
||||
}
|
||||
@ -396,11 +387,92 @@ public class SysRoleService : IDynamicApiController, ITransient
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
[DisplayName("获取角色接口黑名单集合")]
|
||||
public async Task<List<string>> GetRoleApiList([FromQuery] RoleInput input)
|
||||
public async Task<List<string>> GetRoleApiList([FromQuery] BaseIdInput input)
|
||||
{
|
||||
return await _sysRoleApiService.GetRoleApiList(new List<long> { input.Id });
|
||||
|
||||
//var roleButtons = await GetRoleButtonList(new List<long> { input.Id });
|
||||
//return roleApis.Union(roleButtons).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户接口集合 🔖
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[DisplayName("获取用户接口集合")]
|
||||
public async Task<List<List<string>>> GetUserApiList()
|
||||
{
|
||||
var userId = _userManager.UserId;
|
||||
var apiList = _sysCacheService.Get<List<List<string>>>(CacheConst.KeyUserApi + userId);
|
||||
if (apiList != null) return apiList;
|
||||
|
||||
apiList = [[], []];
|
||||
// 所有按钮权限集合
|
||||
var allButtonList = await GetButtonList();
|
||||
// 超管账号获取所有接口
|
||||
if (_userManager.SuperAdmin)
|
||||
{
|
||||
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 diffButtonList = allButtonList.Except(apiList[0]).ToList(); // 差集
|
||||
apiList[0].AddRange(diffButtonList);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 当前账号所有角色集合
|
||||
var roleIdList = await _sysUserRoleService.GetUserRoleIdList(_userManager.UserId);
|
||||
// 已有按钮权限集合
|
||||
var menuIdList = await _sysRoleMenuService.GetRoleMenuIdList(roleIdList);
|
||||
apiList[0] = await GetButtonList(menuIdList, false);
|
||||
|
||||
// 未有按钮权限集合(放到接口黑名单里面)
|
||||
apiList[1] = allButtonList.Except(apiList[0]).ToList(); // 差集
|
||||
// 接口黑名单集合
|
||||
var roleApiList = await _sysRoleApiService.GetRoleApiList(roleIdList);
|
||||
apiList[1].AddRange(roleApiList);
|
||||
}
|
||||
|
||||
// 排序接口名称
|
||||
apiList[0].Sort();
|
||||
apiList[1].Sort();
|
||||
_sysCacheService.Set(CacheConst.KeyUserApi + userId, apiList, TimeSpan.FromDays(7)); // 缓存7天
|
||||
return apiList;
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// 获取用户按钮权限集合
|
||||
///// </summary>
|
||||
///// <returns></returns>
|
||||
//[NonAction]
|
||||
//public async Task<List<string>> GetUserButtonList()
|
||||
//{
|
||||
// var menuIdList = new List<long>();
|
||||
// if (!_userManager.SuperAdmin)
|
||||
// {
|
||||
// var roleIdList = await _sysUserRoleService.GetUserRoleIdList(_userManager.UserId);
|
||||
// menuIdList = await _sysRoleMenuService.GetRoleMenuIdList(roleIdList);
|
||||
// }
|
||||
// return await GetButtonList(menuIdList);
|
||||
//}
|
||||
|
||||
///// <summary>
|
||||
///// 获取角色按钮权限集合
|
||||
///// </summary>
|
||||
///// <param name="roleIds"></param>
|
||||
///// <returns></returns>
|
||||
//[NonAction]
|
||||
//public async Task<List<string>> GetRoleButtonList(List<long> roleIds)
|
||||
//{
|
||||
// var menuIdList = await _sysRoleMenuService.GetRoleMenuIdList(roleIds);
|
||||
// return await GetButtonList(menuIdList);
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 根据菜单Id集合获取按钮集合
|
||||
/// </summary>
|
||||
@ -413,7 +485,21 @@ public class SysRoleService : IDynamicApiController, ITransient
|
||||
.WhereIF(menuIds != null && menuIds.Count > 0, u => menuIds.Contains(u.Id))
|
||||
.WhereIF(!isAll, u => u.Status == StatusEnum.Enable)
|
||||
.Where(u => u.Type == MenuTypeEnum.Btn)
|
||||
.Select(u => u.Permission)
|
||||
.ToListAsync();
|
||||
.Select(u => u.Permission).ToListAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除与该角色相关的用户接口缓存
|
||||
/// </summary>
|
||||
/// <param name="roleId"></param>
|
||||
/// <returns></returns>
|
||||
[NonAction]
|
||||
public async Task ClearUserApiCache(long roleId)
|
||||
{
|
||||
var userIdList = await _sysUserRoleService.GetUserIdList(roleId);
|
||||
foreach (var userId in userIdList)
|
||||
{
|
||||
_sysCacheService.Remove(CacheConst.KeyUserApi + userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -61,9 +61,6 @@ public class SysRoleTableService : ITransient
|
||||
}
|
||||
|
||||
await _sysRoleTableRep.InsertRangeAsync(sysRoleTableList);
|
||||
|
||||
// 清除关联用户状态
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveByRoleId([input.Id]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -170,9 +167,6 @@ public class SysRoleTableService : ITransient
|
||||
public async Task DeleteRolTableByRoleId(long roleId)
|
||||
{
|
||||
await _sysRoleTableRep.DeleteAsync(u => u.RoleId == roleId);
|
||||
|
||||
// 清除关联用户状态
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveByRoleId([roleId]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -190,8 +184,5 @@ public class SysRoleTableService : ITransient
|
||||
u.RoleId = newRoleId;
|
||||
});
|
||||
await _sysRoleTableRep.InsertRangeAsync(roleTableList);
|
||||
|
||||
// 清除关联用户状态
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveByRoleId([newRoleId]);
|
||||
}
|
||||
}
|
||||
@ -9,7 +9,6 @@ namespace Admin.NET.Core.Service;
|
||||
/// <summary>
|
||||
/// 系统日程服务 🧩
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
[ApiDescriptionSettings(Order = 295, Description = "系统日程")]
|
||||
public class SysScheduleService : IDynamicApiController, ITransient
|
||||
{
|
||||
|
||||
@ -215,7 +215,7 @@ public class SysSerialService : IDynamicApiController, ITransient
|
||||
try
|
||||
{
|
||||
// 查询系统流水号配置
|
||||
var seq = await _sysSerialRep.AsQueryable().IgnoreTenant().FirstAsync(u =>
|
||||
var seq = await _sysSerialRep.AsQueryable().ClearFilter<ITenantIdFilter>().FirstAsync(u =>
|
||||
u.Type == type && u.Status == StatusEnum.Enable && u.TenantId == tenantId) ?? throw Oops.Oh(ErrorCodeEnum.S0002);
|
||||
|
||||
// 尝试重置系统流水号配置
|
||||
|
||||
@ -27,7 +27,6 @@ public class SysTenantService : IDynamicApiController, ITransient
|
||||
private readonly SysConfigService _sysConfigService;
|
||||
private readonly SysCacheService _sysCacheService;
|
||||
private readonly IEventPublisher _eventPublisher;
|
||||
private readonly UserManager _userManager;
|
||||
|
||||
public SysTenantService(SqlSugarRepository<SysTenant> sysTenantRep,
|
||||
SqlSugarRepository<SysOrg> sysOrgRep,
|
||||
@ -43,10 +42,8 @@ public class SysTenantService : IDynamicApiController, ITransient
|
||||
SysRoleMenuService sysRoleMenuService,
|
||||
SysConfigService sysConfigService,
|
||||
SysCacheService sysCacheService,
|
||||
IEventPublisher eventPublisher,
|
||||
UserManager userManager)
|
||||
IEventPublisher eventPublisher)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_sysTenantRep = sysTenantRep;
|
||||
_sysOrgRep = sysOrgRep;
|
||||
_sysRoleRep = sysRoleRep;
|
||||
@ -73,8 +70,8 @@ public class SysTenantService : IDynamicApiController, ITransient
|
||||
public async Task<SqlSugarPagedList<TenantOutput>> Page(PageTenantInput input)
|
||||
{
|
||||
return await _sysTenantRep.AsQueryable()
|
||||
.LeftJoin<SysUser>((u, a) => u.UserId == a.Id).IgnoreTenant()
|
||||
.LeftJoin<SysOrg>((u, a, b) => u.OrgId == b.Id).IgnoreTenant()
|
||||
.LeftJoin<SysUser>((u, a) => u.UserId == a.Id).ClearFilter<ITenantIdFilter>()
|
||||
.LeftJoin<SysOrg>((u, a, b) => u.OrgId == b.Id).ClearFilter<ITenantIdFilter>()
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(input.Phone), (u, a) => a.Phone.Contains(input.Phone.Trim()))
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(input.Name), (u, a, b) => b.Name.Contains(input.Name.Trim()))
|
||||
.WhereIF(!input.IncludeDefault, u => u.Id.ToString() != SqlSugarConst.MainConfigId) // 排除默认主库/主租户
|
||||
@ -124,7 +121,7 @@ public class SysTenantService : IDynamicApiController, ITransient
|
||||
if (await _sysOrgRep.IsAnyAsync(u => u.Name == input.Name))
|
||||
throw Oops.Oh(ErrorCodeEnum.D1300);
|
||||
|
||||
if (await _sysUserRep.AsQueryable().IgnoreTenant().AnyAsync(u => u.Account == input.AdminAccount))
|
||||
if (await _sysUserRep.AsQueryable().ClearFilter<ITenantIdFilter>().AnyAsync(u => u.Account == input.AdminAccount))
|
||||
throw Oops.Oh(ErrorCodeEnum.D1301);
|
||||
|
||||
// 从库配置判断
|
||||
@ -241,7 +238,7 @@ public class SysTenantService : IDynamicApiController, ITransient
|
||||
|
||||
// 默认租户管理员角色菜单集合(工作台、账号管理、角色管理、机构管理、职位管理、个人中心、通知公告)
|
||||
var menuPidList = new List<long> { 1300000000101, 1310000000111, 1310000000131, 1310000000141, 1310000000151, 1310000000161, 1310000000171 };
|
||||
var menuIdList = await _sysMenuRep.AsQueryable().IgnoreTenant()
|
||||
var menuIdList = await _sysMenuRep.AsQueryable().ClearFilter<ITenantIdFilter>()
|
||||
.Where(u => menuPidList.Contains(u.Id) || menuPidList.Contains(u.Pid)).Select(u => u.Id).ToListAsync();
|
||||
await _sysRoleMenuService.GrantRoleMenu(new RoleMenuInput() { Id = newRole.Id, MenuIdList = menuIdList });
|
||||
|
||||
@ -272,7 +269,7 @@ public class SysTenantService : IDynamicApiController, ITransient
|
||||
await CacheTenant(input.Id);
|
||||
|
||||
// 删除与租户相关的表数据
|
||||
var users = await _sysUserRep.AsQueryable().IgnoreTenant().Where(u => u.TenantId == input.Id).ToListAsync();
|
||||
var users = await _sysUserRep.AsQueryable().ClearFilter<ITenantIdFilter>().Where(u => u.TenantId == input.Id).ToListAsync();
|
||||
var userIds = users.Select(u => u.Id).ToList();
|
||||
await _sysUserRep.AsDeleteable().Where(u => userIds.Contains(u.Id)).ExecuteCommandAsync();
|
||||
|
||||
@ -282,7 +279,7 @@ public class SysTenantService : IDynamicApiController, ITransient
|
||||
|
||||
await _sysRoleRep.AsDeleteable().Where(u => u.TenantId == input.Id).ExecuteCommandAsync();
|
||||
|
||||
var roleIds = await _sysRoleRep.AsQueryable().IgnoreTenant().Where(u => u.TenantId == input.Id).Select(u => u.Id).ToListAsync();
|
||||
var roleIds = await _sysRoleRep.AsQueryable().ClearFilter<ITenantIdFilter>().Where(u => u.TenantId == input.Id).Select(u => u.Id).ToListAsync();
|
||||
await _sysRoleMenuRep.AsDeleteable().Where(u => roleIds.Contains(u.RoleId)).ExecuteCommandAsync();
|
||||
|
||||
await _sysOrgRep.AsDeleteable().Where(u => u.TenantId == input.Id).ExecuteCommandAsync();
|
||||
@ -340,15 +337,14 @@ public class SysTenantService : IDynamicApiController, ITransient
|
||||
public async Task GrantMenu(RoleMenuInput input)
|
||||
{
|
||||
// 获取租户管理员角色【sys_admin】
|
||||
var adminRole = await _sysRoleRep.AsQueryable().IgnoreTenant()
|
||||
var adminRole = await _sysRoleRep.AsQueryable().ClearFilter<ITenantIdFilter>()
|
||||
.FirstAsync(u => u.Code == CommonConst.SysAdminRole && u.TenantId == input.Id && u.IsDelete == false);
|
||||
if (adminRole == null) return;
|
||||
|
||||
input.Id = adminRole.Id; // 重置租户管理员角色Id
|
||||
await _sysRoleMenuService.GrantRoleMenu(input);
|
||||
|
||||
// 清除关联用户状态
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveByRoleId([input.Id]);
|
||||
await _sysRoleService.ClearUserApiCache(input.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -475,7 +471,7 @@ public class SysTenantService : IDynamicApiController, ITransient
|
||||
[DisplayName("获取租户下的用户列表")]
|
||||
public async Task<List<SysUser>> UserList(TenantIdInput input)
|
||||
{
|
||||
return await _sysUserRep.AsQueryable().IgnoreTenant().Where(u => u.TenantId == input.TenantId).ToListAsync();
|
||||
return await _sysUserRep.AsQueryable().ClearFilter<ITenantIdFilter>().Where(u => u.TenantId == input.TenantId).ToListAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -485,7 +481,7 @@ public class SysTenantService : IDynamicApiController, ITransient
|
||||
[DisplayName("获取所有租户数据库列表")]
|
||||
public async Task<List<DbOutput>> GetTenantDbList()
|
||||
{
|
||||
var tenantDbList = await _sysTenantRep.AsQueryable().IgnoreTenant()
|
||||
var tenantDbList = await _sysTenantRep.AsQueryable().ClearFilter<ITenantIdFilter>()
|
||||
.LeftJoin<SysOrg>((u, a) => u.OrgId == a.Id)
|
||||
//.GroupBy(u => new { u.DbType, u.Connection })
|
||||
.Where(u => u.Status == StatusEnum.Enable)
|
||||
@ -565,7 +561,7 @@ public class SysTenantService : IDynamicApiController, ITransient
|
||||
//// 还可以根据域名判断租户
|
||||
//var host = App.HttpContext.Request.Host.ToString();
|
||||
|
||||
if (tenantId < 1) tenantId = _userManager?.TenantId ?? 0;
|
||||
if (tenantId < 1) tenantId = long.Parse(App.User?.FindFirst(ClaimConst.TenantId)?.Value ?? "0");
|
||||
if (tenantId < 1) tenantId = SqlSugarConst.DefaultTenantId;
|
||||
var tenant = await _sysTenantRep.GetFirstAsync(u => u.Id == tenantId) ?? throw Oops.Oh($"租户信息不存在:{tenantId}");
|
||||
|
||||
@ -618,7 +614,7 @@ public class SysTenantService : IDynamicApiController, ITransient
|
||||
[DisplayName("保存系统信息")]
|
||||
public async Task SaveSysInfo(SysInfoInput input)
|
||||
{
|
||||
input.TenantId = input.TenantId < 1 ? (_userManager.TenantId ?? 0) : input.TenantId;
|
||||
input.TenantId = input.TenantId < 1 ? long.Parse(App.User?.FindFirst(ClaimConst.TenantId)?.Value ?? "0") : input.TenantId;
|
||||
var tenant = await _sysTenantRep.GetFirstAsync(u => u.Id == input.TenantId) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
|
||||
tenant = input.Adapt<SysTenant>();
|
||||
tenant.Id = input.TenantId;
|
||||
|
||||
@ -9,7 +9,6 @@ namespace Admin.NET.Core.Service;
|
||||
/// <summary>
|
||||
/// 系统用户菜单快捷导航服务 🧩
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
[ApiDescriptionSettings(Order = 445, Description = "菜单快捷导航")]
|
||||
public class SysUserMenuService : IDynamicApiController, ITransient
|
||||
{
|
||||
|
||||
@ -39,7 +39,7 @@ public class SysUserRoleService : ITransient
|
||||
await _sysUserRoleRep.InsertRangeAsync(userRoles);
|
||||
|
||||
// 清除缓存
|
||||
_sysCacheService.Remove(CacheConst.KeyUserManager + input.UserId);
|
||||
_sysCacheService.Remove(CacheConst.KeyUserApi + input.UserId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -57,6 +57,12 @@ public class SysUserRoleService : ITransient
|
||||
RoleId = input.Id
|
||||
}).ToList();
|
||||
await _sysUserRoleRep.InsertRangeAsync(userRoles);
|
||||
|
||||
// 清除缓存
|
||||
foreach (var userId in input.UserIdList)
|
||||
{
|
||||
_sysCacheService.Remove(CacheConst.KeyUserApi + userId);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -66,10 +72,18 @@ public class SysUserRoleService : ITransient
|
||||
/// <returns></returns>
|
||||
public async Task DeleteUserRoleByRoleId(long roleId)
|
||||
{
|
||||
await _sysUserRoleRep.DeleteAsync(u => u.RoleId == roleId);
|
||||
var userIdList = await _sysUserRoleRep.AsQueryable()
|
||||
.Where(u => u.RoleId == roleId)
|
||||
.Select(u => u.UserId)
|
||||
.ToListAsync();
|
||||
|
||||
// 清除关联用户状态
|
||||
LazyHelper.GetService<UserManager>().Value.RemoveByRoleId([roleId]);
|
||||
// 清除缓存
|
||||
foreach (var userId in userIdList)
|
||||
{
|
||||
_sysCacheService.Remove(CacheConst.KeyUserApi + userId);
|
||||
}
|
||||
|
||||
await _sysUserRoleRep.DeleteAsync(u => u.RoleId == roleId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -81,8 +95,8 @@ public class SysUserRoleService : ITransient
|
||||
{
|
||||
await _sysUserRoleRep.DeleteAsync(u => u.UserId == userId);
|
||||
|
||||
// 清除关联用户状态
|
||||
LazyHelper.GetService<UserManager>().Value.Remove(userId);
|
||||
// 清除缓存
|
||||
_sysCacheService.Remove(CacheConst.KeyUserApi + userId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -61,7 +61,7 @@ public class SysUserService : IDynamicApiController, ITransient
|
||||
public virtual async Task<SqlSugarPagedList<UserOutput>> Page(PageUserInput input)
|
||||
{
|
||||
// 获取用户拥有的机构集合
|
||||
var userOrgIdList = _userManager.OrgIds;
|
||||
var userOrgIdList = await _sysOrgService.GetUserOrgIdList();
|
||||
List<long> orgList = null;
|
||||
List<long> extOrgUserIdList = null;
|
||||
if (input.OrgId > 0) // 指定机构查询时
|
||||
@ -85,7 +85,6 @@ public class SysUserService : IDynamicApiController, ITransient
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(input.RealName), u => u.RealName.Contains(input.RealName))
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(input.PosName), (u, a, b) => b.Name.Contains(input.PosName))
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(input.Phone), u => u.Phone.Contains(input.Phone))
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(input.Keyword), u => u.Account.Contains(input.Keyword) || u.RealName.Contains(input.Keyword) || u.Phone.Contains(input.Keyword) || u.JobNum.Contains(input.Keyword))
|
||||
.OrderBy(u => new { u.OrderNo, u.Id })
|
||||
.Select((u, a, b) => new UserOutput
|
||||
{
|
||||
@ -110,7 +109,7 @@ public class SysUserService : IDynamicApiController, ITransient
|
||||
// 是否有权操作此账号
|
||||
if (!_userManager.SuperAdmin && input.AccountType is AccountTypeEnum.SuperAdmin) throw Oops.Oh(ErrorCodeEnum.D1033);
|
||||
|
||||
var query = _sysUserRep.AsQueryable().IgnoreTenant();
|
||||
var query = _sysUserRep.AsQueryable().ClearFilter<ITenantIdFilter>();
|
||||
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);
|
||||
|
||||
@ -143,18 +142,18 @@ public class SysUserService : IDynamicApiController, ITransient
|
||||
[DisplayName("更新用户")]
|
||||
public virtual async Task UpdateUser(UpdateUserInput input)
|
||||
{
|
||||
var query = _sysUserRep.AsQueryable().IgnoreTenant().Where(u => u.Id != input.Id);
|
||||
var query = _sysUserRep.AsQueryable().ClearFilter<ITenantIdFilter>().Where(u => u.Id != input.Id);
|
||||
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 user = await _sysUserRep.GetByIdAsync(input.Id);
|
||||
// var roleIds = await _sysUserRoleService.GetUserRoleIdList(input.Id);
|
||||
// if (input.OrgId != user.OrgId || !input.RoleIdList.OrderBy(u => u).SequenceEqual(roleIds.OrderBy(u => u)))
|
||||
// {
|
||||
// // 强制下线账号和失效Token
|
||||
// await OfflineAndExpireToken(user);
|
||||
// }
|
||||
var roleIds = await _sysUserRoleService.GetUserRoleIdList(input.Id);
|
||||
if (input.OrgId != user.OrgId || !input.RoleIdList.OrderBy(u => u).SequenceEqual(roleIds.OrderBy(u => u)))
|
||||
{
|
||||
// 强制下线账号和失效Token
|
||||
await OfflineAndExpireToken(user);
|
||||
}
|
||||
|
||||
// 更新用户
|
||||
await _sysUserRep.AsUpdateable(input.Adapt<SysUser>()).IgnoreColumns(true).IgnoreColumns(u => new { u.Password, u.Status, u.TenantId }).ExecuteCommandAsync();
|
||||
@ -168,9 +167,6 @@ public class SysUserService : IDynamicApiController, ITransient
|
||||
// 更新域账号
|
||||
await _sysUserLdapService.AddUserLdap(user.TenantId!.Value, user.Id, user.Account, input.DomainAccount);
|
||||
|
||||
// 清除用户状态
|
||||
_userManager.Remove(input.Id);
|
||||
|
||||
// 发布更新用户事件
|
||||
await _eventPublisher.PublishAsync(UserEventTypeEnum.Update, input);
|
||||
}
|
||||
@ -183,6 +179,7 @@ public class SysUserService : IDynamicApiController, ITransient
|
||||
private async Task UpdateRoleAndExtOrg(AddUserInput input)
|
||||
{
|
||||
await GrantRole(new UserRoleInput { UserId = input.Id, RoleIdList = input.RoleIdList });
|
||||
|
||||
await _sysUserExtOrgService.UpdateUserExtOrg(input.Id, input.ExtOrgIdList);
|
||||
}
|
||||
|
||||
@ -228,8 +225,6 @@ public class SysUserService : IDynamicApiController, ITransient
|
||||
|
||||
// 发布删除用户事件
|
||||
await _eventPublisher.PublishAsync(UserEventTypeEnum.Delete, input);
|
||||
|
||||
LazyHelper.GetService<UserManager>().Value.Remove(user.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -250,10 +245,8 @@ public class SysUserService : IDynamicApiController, ITransient
|
||||
[DisplayName("更新用户基本信息")]
|
||||
public virtual async Task<int> UpdateBaseInfo(SysUser user)
|
||||
{
|
||||
var count = await _sysUserRep.AsUpdateable(user).IgnoreColumns(u => new { u.CreateTime, u.Account, u.Password, u.AccountType, u.OrgId, u.PosId }).ExecuteCommandAsync();
|
||||
// 清除用户状态
|
||||
_userManager.Remove(user.Id);
|
||||
return count;
|
||||
return await _sysUserRep.AsUpdateable(user)
|
||||
.IgnoreColumns(u => new { u.CreateTime, u.Account, u.Password, u.AccountType, u.OrgId, u.PosId }).ExecuteCommandAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -265,12 +258,14 @@ public class SysUserService : IDynamicApiController, ITransient
|
||||
[DisplayName("设置用户状态")]
|
||||
public virtual async Task<int> SetStatus(BaseStatusInput input)
|
||||
{
|
||||
if (_userManager.UserId == input.Id) throw Oops.Oh(ErrorCodeEnum.D1026);
|
||||
if (_userManager.UserId == input.Id)
|
||||
throw Oops.Oh(ErrorCodeEnum.D1026);
|
||||
|
||||
var user = await _sysUserRep.GetByIdAsync(input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D0009);
|
||||
user.ValidateIsSuperAdminAccountType(ErrorCodeEnum.D1015);
|
||||
|
||||
if (!Enum.IsDefined(input.Status)) throw Oops.Oh(ErrorCodeEnum.D3005);
|
||||
if (!Enum.IsDefined(input.Status))
|
||||
throw Oops.Oh(ErrorCodeEnum.D3005);
|
||||
|
||||
if (input.Status != StatusEnum.Enable)
|
||||
{
|
||||
@ -284,8 +279,6 @@ public class SysUserService : IDynamicApiController, ITransient
|
||||
// 发布设置用户状态事件
|
||||
await _eventPublisher.PublishAsync(UserEventTypeEnum.SetStatus, input);
|
||||
|
||||
_userManager.Remove(user.Id);
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
@ -298,14 +291,16 @@ public class SysUserService : IDynamicApiController, ITransient
|
||||
[DisplayName("授权用户角色")]
|
||||
public async Task GrantRole(UserRoleInput input)
|
||||
{
|
||||
var user = await _sysUserRep.GetByIdAsync(input.UserId) ?? throw Oops.Oh(ErrorCodeEnum.D0009);
|
||||
|
||||
// 若角色发生改变则进行更新
|
||||
var roles = await _sysUserRoleService.GetUserRoleIdList(input.UserId);
|
||||
if (!roles.SequenceEqual(input.RoleIdList))
|
||||
{
|
||||
// 更新用户角色
|
||||
await _sysUserRoleService.GrantUserRole(input);
|
||||
// // 强制下线账号和失效Token
|
||||
// await OfflineAndExpireToken(user);
|
||||
// 强制下线账号和失效Token
|
||||
await OfflineAndExpireToken(user);
|
||||
// 发布更新用户角色事件
|
||||
await _eventPublisher.PublishAsync(UserEventTypeEnum.UpdateRole, input);
|
||||
}
|
||||
@ -416,10 +411,10 @@ public class SysUserService : IDynamicApiController, ITransient
|
||||
if (int.TryParse(sysConfig.Value, out int expirationTime) && expirationTime > 0)
|
||||
{
|
||||
var user = await _sysUserRep.GetByIdAsync(_userManager.UserId);
|
||||
|
||||
if (user.LastChangePasswordTime == null) return false;
|
||||
|
||||
if ((DateTime.Now - user.LastChangePasswordTime.Value).Days > expirationTime) return false;
|
||||
if (user.LastChangePasswordTime == null)
|
||||
return false;
|
||||
if ((DateTime.Now - user.LastChangePasswordTime.Value).Days > expirationTime)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -472,19 +467,6 @@ public class SysUserService : IDynamicApiController, ITransient
|
||||
return await _sysUserExtOrgService.GetUserExtOrgList(userId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置用户Token版本
|
||||
/// </summary>
|
||||
[NonAction]
|
||||
public void ResetTokenVersion()
|
||||
{
|
||||
_sysUserRep.AsUpdateable()
|
||||
.Where(u => u.TokenVersion > int.MaxValue / 2)
|
||||
.SetColumns(u => u.TokenVersion, 1)
|
||||
.UpdateColumns(u => u.TokenVersion)
|
||||
.ExecuteCommand();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 强制下线账号和失效Token
|
||||
/// </summary>
|
||||
@ -496,8 +478,5 @@ public class SysUserService : IDynamicApiController, ITransient
|
||||
|
||||
// 强制下线账号
|
||||
await _sysOnlineUserService.ForceOfflineByUserId(user.Id);
|
||||
|
||||
// 清除用户状态
|
||||
_userManager.Remove(user.Id);
|
||||
}
|
||||
}
|
||||
@ -4,284 +4,97 @@
|
||||
//
|
||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Admin.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// 当前登录用户信息
|
||||
/// </summary>
|
||||
public class UserManager(
|
||||
SysCacheService sysCacheService,
|
||||
IHttpContextAccessor httpContextAccessor) : UserManagerDao, IScoped
|
||||
public class UserManager : IScoped
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户状态
|
||||
/// </summary>
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
protected virtual UserManagerDao _manager { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 代理对象
|
||||
/// </summary>
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
protected virtual UserManagerDao Manager
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_manager == null || _manager.UserId != UserId) _manager = sysCacheService.Get<UserManagerDao>(CacheConst.KeyUserManager + UserId);
|
||||
return _manager;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用户Id
|
||||
/// </summary>
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
public override long UserId => (httpContextAccessor.HttpContext?.User.FindFirst(nameof(UserId))?.Value).ToLong();
|
||||
|
||||
/// <summary>
|
||||
/// Token版本号
|
||||
/// </summary>
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
public override int TokenVersion => (httpContextAccessor.HttpContext?.User.FindFirst(nameof(UserId))?.Value).ToInt();
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
/// <summary>
|
||||
/// 应用Id
|
||||
/// </summary>
|
||||
public override long? AppId => Manager?.AppId;
|
||||
public long AppId => (_httpContextAccessor.HttpContext?.User.FindFirst(ClaimConst.AppId)?.Value).ToLong();
|
||||
|
||||
/// <summary>
|
||||
/// 租户Id
|
||||
/// </summary>
|
||||
public override long? TenantId => Manager?.TenantId;
|
||||
public long TenantId => (_httpContextAccessor.HttpContext?.User.FindFirst(ClaimConst.TenantId)?.Value).ToLong();
|
||||
|
||||
/// <summary>
|
||||
/// 用户Id
|
||||
/// </summary>
|
||||
public long UserId => (_httpContextAccessor.HttpContext?.User.FindFirst(ClaimConst.UserId)?.Value).ToLong();
|
||||
|
||||
/// <summary>
|
||||
/// 用户账号
|
||||
/// </summary>
|
||||
public override string Account => Manager?.Account;
|
||||
public string Account => _httpContextAccessor.HttpContext?.User.FindFirst(ClaimConst.Account)?.Value;
|
||||
|
||||
/// <summary>
|
||||
/// 真实姓名
|
||||
/// </summary>
|
||||
public override string RealName => Manager?.RealName;
|
||||
public string RealName => _httpContextAccessor.HttpContext?.User.FindFirst(ClaimConst.RealName)?.Value;
|
||||
|
||||
/// <summary>
|
||||
/// 昵称
|
||||
/// </summary>
|
||||
public override string NickName => Manager?.NickName;
|
||||
public string NickName => _httpContextAccessor.HttpContext?.User.FindFirst(ClaimConst.NickName)?.Value;
|
||||
|
||||
/// <summary>
|
||||
/// 账号类型
|
||||
/// </summary>
|
||||
public override AccountTypeEnum? AccountType => Manager?.AccountType;
|
||||
public AccountTypeEnum? AccountType => int.TryParse(_httpContextAccessor.HttpContext?.User.FindFirst(ClaimConst.AccountType)?.Value, out var val) ? (AccountTypeEnum?)val : null;
|
||||
|
||||
/// <summary>
|
||||
/// 是否超级管理员
|
||||
/// </summary>
|
||||
public bool SuperAdmin => _httpContextAccessor.HttpContext?.User.FindFirst(ClaimConst.AccountType)?.Value == ((int)AccountTypeEnum.SuperAdmin).ToString();
|
||||
|
||||
/// <summary>
|
||||
/// 是否系统管理员
|
||||
/// </summary>
|
||||
public bool SysAdmin => _httpContextAccessor.HttpContext?.User.FindFirst(ClaimConst.AccountType)?.Value == ((int)AccountTypeEnum.SysAdmin).ToString();
|
||||
|
||||
/// <summary>
|
||||
/// 组织机构Id
|
||||
/// </summary>
|
||||
public override long OrgId => Manager?.OrgId ?? 0;
|
||||
public long OrgId => (_httpContextAccessor.HttpContext?.User.FindFirst(ClaimConst.OrgId)?.Value).ToLong();
|
||||
|
||||
/// <summary>
|
||||
/// 组织机构名称
|
||||
/// </summary>
|
||||
public override string OrgName => Manager?.OrgName;
|
||||
public string OrgName => (_httpContextAccessor.HttpContext?.User.FindFirst(ClaimConst.OrgName)?.Value);
|
||||
|
||||
/// <summary>
|
||||
/// 组织机构Id
|
||||
/// </summary>
|
||||
public override string OrgType => Manager?.OrgType;
|
||||
|
||||
/// <summary>
|
||||
/// 职位Id
|
||||
/// </summary>
|
||||
public override long? PosId => Manager?.PosId;
|
||||
|
||||
/// <summary>
|
||||
/// 职位名称
|
||||
/// </summary>
|
||||
public override string PosName => Manager?.PosName;
|
||||
|
||||
/// <summary>
|
||||
/// 职位编码
|
||||
/// </summary>
|
||||
public override string PosCode => Manager?.PosCode;
|
||||
public string OrgType => (_httpContextAccessor.HttpContext?.User.FindFirst(ClaimConst.OrgType)?.Value);
|
||||
|
||||
/// <summary>
|
||||
/// 组织机构级别
|
||||
/// </summary>
|
||||
public override int? OrgLevel => Manager?.OrgLevel;
|
||||
public int OrgLevel => (_httpContextAccessor.HttpContext?.User.FindFirst(ClaimConst.OrgLevel)?.Value).ToInt();
|
||||
|
||||
/// <summary>
|
||||
/// 登录模式
|
||||
/// </summary>
|
||||
public override LoginModeEnum? LoginMode => Manager?.LoginMode;
|
||||
public int LoginMode => (_httpContextAccessor.HttpContext?.User.FindFirst(ClaimConst.LoginMode)?.Value).ToInt();
|
||||
|
||||
/// <summary>
|
||||
/// Token版本号
|
||||
/// </summary>
|
||||
public int TokenVersion => (_httpContextAccessor.HttpContext?.User.FindFirst(ClaimConst.TokenVersion)?.Value).ToInt();
|
||||
|
||||
/// <summary>
|
||||
/// 微信OpenId
|
||||
/// </summary>
|
||||
public override string OpenId => Manager?.OpenId;
|
||||
public string OpenId => _httpContextAccessor.HttpContext?.User.FindFirst(ClaimConst.OpenId)?.Value;
|
||||
|
||||
/// <summary>
|
||||
/// 数据范围权限列表
|
||||
/// </summary>
|
||||
public override List<DataScopeEnum> DataScopeList => Manager?.DataScopeList;
|
||||
|
||||
/// <summary>
|
||||
/// 角色Id集
|
||||
/// </summary>
|
||||
public override List<long> RoleIds => Manager?.RoleIds;
|
||||
|
||||
/// <summary>
|
||||
/// 机构Id集
|
||||
/// </summary>
|
||||
public override List<long> OrgIds => Manager?.OrgIds;
|
||||
|
||||
/// <summary>
|
||||
/// 职位Id集
|
||||
/// </summary>
|
||||
public override List<long> PosIds => Manager?.PosIds;
|
||||
|
||||
/// <summary>
|
||||
/// 菜单Id集
|
||||
/// </summary>
|
||||
public override List<long> MenuIds => Manager?.MenuIds;
|
||||
|
||||
/// <summary>
|
||||
/// 权限集
|
||||
/// </summary>
|
||||
public override List<string> Permissions => Manager?.Permissions;
|
||||
|
||||
/// <summary>
|
||||
/// App权限集
|
||||
/// </summary>
|
||||
public override List<string> AppPermissions => Manager?.AppPermissions;
|
||||
|
||||
/// <summary>
|
||||
/// 无权权限集
|
||||
/// </summary>
|
||||
public override List<string> UnauthorizedPermissions => Manager?.UnauthorizedPermissions;
|
||||
|
||||
/// <summary>
|
||||
/// 扩展属性
|
||||
/// </summary>
|
||||
public override Dictionary<string, dynamic> ExtProps => Manager?.ExtProps;
|
||||
|
||||
/// <summary>
|
||||
/// 用户状态是否存在
|
||||
/// </summary>
|
||||
public bool Exist(long userId)
|
||||
public UserManager(IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
return sysCacheService.ExistKey(CacheConst.KeyUserManager + userId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置用户状态
|
||||
/// </summary>
|
||||
public void Set(UserManagerDao userManager)
|
||||
{
|
||||
sysCacheService.Set(CacheConst.KeyUserManager + userManager.UserId, userManager);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除指定用户状态
|
||||
/// </summary>
|
||||
public void Remove(long userId)
|
||||
{
|
||||
sysCacheService.Remove(CacheConst.KeyUserManager + userId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有用户状态
|
||||
/// </summary>
|
||||
public void RemoveAll()
|
||||
{
|
||||
sysCacheService.RemoveByPrefixKey(CacheConst.KeyUserManager);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据角色Id集删除用户状态
|
||||
/// </summary>
|
||||
public void RemoveByRoleId(List<long> roleIds)
|
||||
{
|
||||
if (roleIds == null || roleIds.Count == 0) return;
|
||||
var list = GetUserManagerList();
|
||||
foreach (var id in roleIds) list?.Where(x => x.RoleIds.Contains(id)).ToList()?.ForEach(x => sysCacheService.Remove(CacheConst.KeyUserManager + x.UserId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据机构Id集删除用户状态
|
||||
/// </summary>
|
||||
public void RemoveByOrgId(List<long> orgIds)
|
||||
{
|
||||
if (orgIds == null || orgIds.Count == 0) return;
|
||||
var list = GetUserManagerList();
|
||||
foreach (var id in orgIds) list?.Where(x => x.OrgIds.Contains(id)).ToList()?.ForEach(x => sysCacheService.Remove(CacheConst.KeyUserManager + x.UserId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据职位Id集删除用户状态
|
||||
/// </summary>
|
||||
public void RemoveByPosId(List<long> posIds)
|
||||
{
|
||||
if (posIds == null || posIds.Count == 0) return;
|
||||
var list = GetUserManagerList();
|
||||
foreach (var id in posIds) list?.Where(x => x.PosIds.Contains(id)).ToList()?.ForEach(x => sysCacheService.Remove(CacheConst.KeyUserManager + x.UserId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定用户状态
|
||||
/// </summary>
|
||||
public UserManagerDao Get(dynamic userId)
|
||||
{
|
||||
return sysCacheService.Get<UserManagerDao>(CacheConst.KeyUserManager + userId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户状态列表
|
||||
/// </summary>
|
||||
public List<UserManagerDao> GetUserManagerList()
|
||||
{
|
||||
var keys = sysCacheService.GetKeysByPrefixKey(CacheConst.KeyUserManager);
|
||||
return keys?.Select(x => sysCacheService.Get<UserManagerDao>(x)).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定用户状态,如果不存在则刷新
|
||||
/// </summary>
|
||||
public UserManagerDao GetOrRefresh(dynamic userId = null)
|
||||
{
|
||||
userId ??= UserId;
|
||||
var userManager = sysCacheService.Get<UserManagerDao>(CacheConst.KeyUserManager + userId);
|
||||
if (userManager == null)
|
||||
{
|
||||
if ((Nullable.GetUnderlyingType(userId.GetType()) ?? userId.GetType()) != typeof(long))
|
||||
{
|
||||
if (long.TryParse(userId.ToString(), out long tempId)) userId = tempId;
|
||||
else return null;
|
||||
}
|
||||
LazyHelper.GetService<SysAuthService>().Value.RefreshUserManager(userId).GetAwaiter().GetResult();
|
||||
}
|
||||
return sysCacheService.Get<UserManagerDao>(CacheConst.KeyUserManager + userId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取扩展属性
|
||||
/// </summary>
|
||||
public T GetExtProp<T>(string key, T defaultValue = default)
|
||||
{
|
||||
if (ExtProps == null || !ExtProps.TryGetValue(key, out var value) || value is null) return defaultValue;
|
||||
if (value is JsonElement element) return element.Deserialize<T>();
|
||||
try
|
||||
{
|
||||
return (T)Convert.ChangeType(value, typeof(T));
|
||||
}
|
||||
catch
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
}
|
||||
@ -1,169 +0,0 @@
|
||||
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
||||
//
|
||||
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
||||
//
|
||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||
|
||||
namespace Admin.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// 用户会话信息
|
||||
/// </summary>
|
||||
public class UserManagerDao
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户Id
|
||||
/// </summary>
|
||||
public virtual long UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// token版本
|
||||
/// </summary>
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
public virtual int TokenVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 平台应用Id
|
||||
/// </summary>
|
||||
public virtual long? AppId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 租户Id
|
||||
/// </summary>
|
||||
public virtual long? TenantId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 账号
|
||||
/// </summary>
|
||||
public virtual string Account { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 真实姓名
|
||||
/// </summary>
|
||||
public virtual string RealName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 昵称
|
||||
/// </summary>
|
||||
public virtual string NickName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 账户类型
|
||||
/// </summary>
|
||||
public virtual AccountTypeEnum? AccountType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否超级管理员
|
||||
/// </summary>
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
public bool SuperAdmin => AccountType == AccountTypeEnum.SuperAdmin;
|
||||
|
||||
/// <summary>
|
||||
/// 是否系统管理员
|
||||
/// </summary>
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
public bool SysAdmin => AccountType == AccountTypeEnum.SysAdmin;
|
||||
|
||||
/// <summary>
|
||||
/// 数据范围权限列表
|
||||
/// </summary>
|
||||
public virtual List<DataScopeEnum> DataScopeList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 最大数据范围权限
|
||||
/// </summary>
|
||||
public DataScopeEnum MaxDataScope => DataScopeList.Any(u => u == DataScopeEnum.All) ? DataScopeEnum.All : DataScopeList.Any(u => u == DataScopeEnum.Define) ? DataScopeEnum.Define : DataScopeList.Min();
|
||||
|
||||
/// <summary>
|
||||
/// 组织机构Id
|
||||
/// </summary>
|
||||
public virtual long OrgId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 组织机构编码
|
||||
/// </summary>
|
||||
public virtual string OrgCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 组织机构名称
|
||||
/// </summary>
|
||||
public virtual string OrgName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 组织机构类型
|
||||
/// </summary>
|
||||
public virtual string OrgType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 组织机构级别
|
||||
/// </summary>
|
||||
public virtual int? OrgLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 职位Id
|
||||
/// </summary>
|
||||
public virtual long? PosId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 职位名称
|
||||
/// </summary>
|
||||
public virtual string PosName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 职位编码
|
||||
/// </summary>
|
||||
public virtual string PosCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 角色Id集
|
||||
/// </summary>
|
||||
public virtual List<long> RoleIds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 机构Id集
|
||||
/// </summary>
|
||||
public virtual List<long> OrgIds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 职位Id集
|
||||
/// </summary>
|
||||
public virtual List<long> PosIds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 菜单Id集
|
||||
/// </summary>
|
||||
public virtual List<long> MenuIds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 移动端权限集
|
||||
/// </summary>
|
||||
public virtual List<string> AppPermissions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 权限集
|
||||
/// </summary>
|
||||
public virtual List<string> Permissions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 无权-权限集
|
||||
/// </summary>
|
||||
public virtual List<string> UnauthorizedPermissions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 登录模式
|
||||
/// </summary>
|
||||
public virtual LoginModeEnum? LoginMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 微信OpenId
|
||||
/// </summary>
|
||||
public virtual string OpenId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 扩展属性
|
||||
/// </summary>
|
||||
public virtual Dictionary<string, object> ExtProps { get; set; }
|
||||
}
|
||||
@ -436,7 +436,6 @@ public static class SqlSugarExtension
|
||||
// 执行前处理种子数据
|
||||
if (handleBefore != null) foreach (var sd in seedData) handleBefore(sd);
|
||||
|
||||
dbProvider.QueryFilter.ClearAndBackup();
|
||||
int total, insertCount = 0, updateCount = 0;
|
||||
if (entityType.GetCustomAttribute<SplitTableAttribute>(true) != null)
|
||||
{
|
||||
@ -476,7 +475,6 @@ public static class SqlSugarExtension
|
||||
}
|
||||
}
|
||||
}
|
||||
dbProvider.QueryFilter.Restore();
|
||||
return (total, insertCount, updateCount);
|
||||
}
|
||||
|
||||
@ -522,6 +520,11 @@ public static class SqlSugarExtension
|
||||
/// </summary>
|
||||
private static readonly ConcurrentDictionary<Type, Dictionary<string, (PropertyInfo Prop, BindTextAbbrAttribute Attr)>?> _textAbbrPropCache = new();
|
||||
|
||||
/// <summary>
|
||||
/// 系统通用服务
|
||||
/// </summary>
|
||||
private static readonly Lazy<SysCommonService> _lazySysCommonService = new(() => App.GetService<SysCommonService>());
|
||||
|
||||
/// <summary>
|
||||
/// 初始化文本简称数据
|
||||
/// </summary>
|
||||
@ -583,7 +586,7 @@ public static class SqlSugarExtension
|
||||
if (string.IsNullOrWhiteSpace(value)) return;
|
||||
|
||||
// 使用线程安全的延迟初始化服务实例获取文本缩写
|
||||
var abbrValue = LazyHelper.GetService<SysCommonService>().Value
|
||||
var abbrValue = _lazySysCommonService.Value
|
||||
.GetNameAbbr(new() { Text = value, All = attribute.SaveFullAbbr })
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
@ -599,6 +602,11 @@ public static class SqlSugarExtension
|
||||
/// </summary>
|
||||
private static readonly ConcurrentDictionary<Type, Dictionary<string, (PropertyInfo Prop, BindSerialAttribute Attr)>?> _serialPropCache = new();
|
||||
|
||||
/// <summary>
|
||||
/// 系统通用服务
|
||||
/// </summary>
|
||||
private static readonly Lazy<SysSerialService> _lazySysSerialService = new(() => App.GetService<SysSerialService>());
|
||||
|
||||
/// <summary>
|
||||
/// 自动生成流水号到绑定字段
|
||||
/// </summary>
|
||||
@ -633,8 +641,8 @@ public static class SqlSugarExtension
|
||||
var (_, attribute) = propData;
|
||||
|
||||
// 使用线程安全的延迟初始化服务实例获取流水号
|
||||
var serial = LazyHelper.GetService<SysSerialService>()
|
||||
.Value.NextSeqNo(attribute.Type, attribute.IsGlobal)
|
||||
var serial = _lazySysSerialService.Value
|
||||
.NextSeqNo(attribute.Type, attribute.IsGlobal)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
entityInfo.SetValue(serial);
|
||||
|
||||
@ -28,10 +28,6 @@ public static class SqlSugarFilter
|
||||
sysCacheService.Remove($"{CacheConst.KeyRoleMaxDataScope}{userId}");
|
||||
// 用户权限缓存(接口集合)
|
||||
sysCacheService.Remove($"{CacheConst.KeyUserApi}{userId}");
|
||||
|
||||
// 清除用户状态
|
||||
sysCacheService.Remove($"{CacheConst.KeyUserManager}{userId}");
|
||||
|
||||
// 删除用户机构(数据范围)缓存——过滤器
|
||||
_cache.Remove($"db:{dbConfigId}:orgList:{userId}");
|
||||
}
|
||||
@ -58,10 +54,6 @@ public static class SqlSugarFilter
|
||||
var userId = App.User?.FindFirst(ClaimConst.UserId)?.Value;
|
||||
if (string.IsNullOrWhiteSpace(userId)) return;
|
||||
|
||||
// 获取用户状态
|
||||
var userManager = LazyHelper.GetService<UserManager>().Value.GetOrRefresh(userId);
|
||||
if (userManager == null) return;
|
||||
|
||||
// 配置用户机构集合缓存
|
||||
var cacheKey = $"db:{db.CurrentConnectionConfig.ConfigId}:orgList:{userId}";
|
||||
var orgFilter = _cache.Get<ConcurrentDictionary<Type, LambdaExpression>>(cacheKey);
|
||||
@ -71,7 +63,12 @@ public static class SqlSugarFilter
|
||||
if (maxDataScope == (int)DataScopeEnum.All) return;
|
||||
|
||||
// 获取用户所属机构,保证同一作用域
|
||||
var orgIds = userManager.OrgIds;
|
||||
var orgIds = new List<long>();
|
||||
Scoped.Create((factory, scope) =>
|
||||
{
|
||||
var services = scope.ServiceProvider;
|
||||
orgIds = services.GetRequiredService<SysOrgService>().GetUserOrgIdList().GetAwaiter().GetResult();
|
||||
});
|
||||
if (orgIds == null || orgIds.Count == 0) return;
|
||||
|
||||
// 获取业务实体数据表
|
||||
@ -112,12 +109,19 @@ public static class SqlSugarFilter
|
||||
var userId = App.User?.FindFirst(ClaimConst.UserId)?.Value;
|
||||
if (string.IsNullOrWhiteSpace(userId)) return maxDataScope;
|
||||
|
||||
// 获取用户状态
|
||||
var userManager = LazyHelper.GetService<UserManager>().Value.GetOrRefresh(userId);
|
||||
if (userManager == null) return (int)DataScopeEnum.Self;
|
||||
|
||||
// 获取用户最大数据范围--- 全部数据 -> 自定义
|
||||
maxDataScope = (int)userManager.MaxDataScope;
|
||||
// 获取用户最大数据范围---仅本人数据
|
||||
maxDataScope = App.GetRequiredService<SysCacheService>().Get<int>(CacheConst.KeyRoleMaxDataScope + userId);
|
||||
// 若为0则获取用户机构组织集合建立缓存
|
||||
if (maxDataScope == 0)
|
||||
{
|
||||
// 获取用户所属机构,保证同一作用域
|
||||
Scoped.Create((factory, scope) =>
|
||||
{
|
||||
var services = scope.ServiceProvider;
|
||||
services.GetRequiredService<SysOrgService>().GetUserOrgIdList().GetAwaiter().GetResult();
|
||||
maxDataScope = services.GetRequiredService<SysCacheService>().Get<int>(CacheConst.KeyRoleMaxDataScope + userId);
|
||||
});
|
||||
}
|
||||
if (maxDataScope != (int)DataScopeEnum.Self) return maxDataScope;
|
||||
|
||||
// 配置用户数据范围缓存
|
||||
@ -126,7 +130,8 @@ public static class SqlSugarFilter
|
||||
if (dataScopeFilter == null)
|
||||
{
|
||||
// 获取业务实体数据表
|
||||
var entityTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsSubclassOf(typeof(EntityBaseData))).ToList();
|
||||
var entityTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass
|
||||
&& u.IsSubclassOf(typeof(EntityBaseData)));
|
||||
if (!entityTypes.Any()) return maxDataScope;
|
||||
|
||||
dataScopeFilter = new ConcurrentDictionary<Type, LambdaExpression>();
|
||||
@ -134,7 +139,8 @@ public static class SqlSugarFilter
|
||||
{
|
||||
// 排除非当前数据库实体
|
||||
var tAtt = entityType.GetCustomAttribute<TenantAttribute>();
|
||||
if ((tAtt != null && db.CurrentConnectionConfig.ConfigId.ToString() != tAtt.configId.ToString())) continue;
|
||||
if ((tAtt != null && db.CurrentConnectionConfig.ConfigId.ToString() != tAtt.configId.ToString()))
|
||||
continue;
|
||||
|
||||
//var lambda = DynamicExpressionParser.ParseLambda(new[] {
|
||||
// Expression.Parameter(entityType, "u") }, typeof(bool), $"u.{nameof(EntityBaseData.CreateUserId)}=@0", userId);
|
||||
@ -166,8 +172,7 @@ public static class SqlSugarFilter
|
||||
{
|
||||
// 获取自定义实体过滤器
|
||||
var entityFilterTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass
|
||||
&& u.GetInterfaces().Any(i => i.HasImplementedRawGeneric(typeof(IEntityFilter))))
|
||||
.ToList();
|
||||
&& u.GetInterfaces().Any(i => i.HasImplementedRawGeneric(typeof(IEntityFilter))));
|
||||
if (!entityFilterTypes.Any()) return;
|
||||
|
||||
var tableFilterItems = new List<TableFilterItem<object>>();
|
||||
@ -181,9 +186,9 @@ public static class SqlSugarFilter
|
||||
foreach (var u in entityFilters)
|
||||
{
|
||||
var tableFilterItem = (TableFilterItem<object>)u;
|
||||
var entityType = tableFilterItem.GetType().GetProperty("type", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(tableFilterItem, null) as Type;
|
||||
var entityType = tableFilterItem.GetType().GetProperty("type", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(tableFilterItem, null) as Type;
|
||||
// 排除非当前数据库实体
|
||||
var tAtt = entityType?.GetCustomAttribute<TenantAttribute>();
|
||||
var tAtt = entityType.GetCustomAttribute<TenantAttribute>();
|
||||
if ((tAtt != null && db.CurrentConnectionConfig.ConfigId.ToString() != tAtt.configId.ToString()) ||
|
||||
(tAtt == null && db.CurrentConnectionConfig.ConfigId.ToString() != SqlSugarConst.MainConfigId))
|
||||
continue;
|
||||
|
||||
@ -37,7 +37,7 @@ public class SqlSugarRepository<T> : SimpleClient<T>, ISqlSugarRepository<T> whe
|
||||
return;
|
||||
|
||||
// 若未贴任何表特性或当前未登录或是默认租户Id,则返回默认库连接
|
||||
var tenantId = LazyHelper.GetService<UserManager>().Value.TenantId?.ToString();
|
||||
var tenantId = App.User?.FindFirst(ClaimConst.TenantId)?.Value;
|
||||
if (string.IsNullOrWhiteSpace(tenantId) || tenantId == SqlSugarConst.MainConfigId) return;
|
||||
|
||||
// 根据租户Id切换库连接, 为空则返回默认库连接
|
||||
|
||||
@ -163,8 +163,7 @@ public static class SqlSugarSetup
|
||||
dbProvider.Aop.OnError = ex =>
|
||||
{
|
||||
if (ex.Parametres == null) return;
|
||||
var log =
|
||||
$"【{DateTime.Now}——错误SQL】\r\n{UtilMethods.GetNativeSql(ex.Sql, (SugarParameter[])ex.Parametres)}\r\n";
|
||||
var log = $"【{DateTime.Now}——错误SQL】\r\n{UtilMethods.GetNativeSql(ex.Sql, (SugarParameter[])ex.Parametres)}\r\n";
|
||||
Log.Error(log, ex);
|
||||
};
|
||||
if (enableConsoleSql)
|
||||
@ -183,8 +182,7 @@ public static class SqlSugarSetup
|
||||
var originColor = Console.ForegroundColor;
|
||||
if (sql.StartsWith("SELECT", StringComparison.OrdinalIgnoreCase))
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
if (sql.StartsWith("UPDATE", StringComparison.OrdinalIgnoreCase) ||
|
||||
sql.StartsWith("INSERT", StringComparison.OrdinalIgnoreCase))
|
||||
if (sql.StartsWith("UPDATE", StringComparison.OrdinalIgnoreCase) || sql.StartsWith("INSERT", StringComparison.OrdinalIgnoreCase))
|
||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
if (sql.StartsWith("DELETE", StringComparison.OrdinalIgnoreCase))
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
@ -192,7 +190,6 @@ public static class SqlSugarSetup
|
||||
Console.ForegroundColor = originColor;
|
||||
};
|
||||
}
|
||||
|
||||
dbProvider.Aop.OnLogExecuted = (sql, pars) =>
|
||||
{
|
||||
//// 若参数值超过100个字符则进行截取
|
||||
@ -209,13 +206,10 @@ public static class SqlSugarSetup
|
||||
var fileName = dbProvider.Ado.SqlStackTrace.FirstFileName; // 文件名
|
||||
var fileLine = dbProvider.Ado.SqlStackTrace.FirstLine; // 行号
|
||||
var firstMethodName = dbProvider.Ado.SqlStackTrace.FirstMethodName; // 方法名
|
||||
var log =
|
||||
$"【{DateTime.Now}——超时SQL】\r\n【所在文件名】:{fileName}\r\n【代码行数】:{fileLine}\r\n【方法名】:{firstMethodName}\r\n" +
|
||||
$"【SQL语句】:{UtilMethods.GetNativeSql(sql, pars)}";
|
||||
var log = $"【{DateTime.Now}——超时SQL】\r\n【所在文件名】:{fileName}\r\n【代码行数】:{fileLine}\r\n【方法名】:{firstMethodName}\r\n" + $"【SQL语句】:{UtilMethods.GetNativeSql(sql, pars)}";
|
||||
Log.Warning(log);
|
||||
};
|
||||
|
||||
var userManager = LazyHelper.GetService<UserManager>().Value;
|
||||
// 数据审计
|
||||
dbProvider.Aop.DataExecuting = (oldValue, entityInfo) =>
|
||||
{
|
||||
@ -247,43 +241,34 @@ public static class SqlSugarSetup
|
||||
if (createTime == null || createTime.Equals(DateTime.MinValue))
|
||||
entityInfo.SetValue(DateTime.Now);
|
||||
}
|
||||
|
||||
// 若当前用户非空(web线程时)
|
||||
if (App.User == null) return;
|
||||
|
||||
dynamic entityValue = entityInfo.EntityValue;
|
||||
// 若应用Id为空则赋值当前用户应用Id,若当前用户应用Id为空,则取默认应用Id
|
||||
if (entityInfo.PropertyName == nameof(IAppIdFilter.AppId) && entityValue is IAppIdFilter)
|
||||
{
|
||||
var appId = entityInfo.EntityColumnInfo.PropertyInfo.GetValue(entityInfo.EntityValue)!;
|
||||
if (appId == null || appId.Equals(0))
|
||||
entityInfo.SetValue(userManager.AppId ?? SqlSugarConst.DefaultAppId);
|
||||
}
|
||||
else if (entityInfo.PropertyName == nameof(EntityTenantId.TenantId))
|
||||
if (entityInfo.PropertyName == nameof(EntityTenantId.TenantId))
|
||||
{
|
||||
if (entityValue.TenantId == null || entityValue.TenantId == 0)
|
||||
entityInfo.SetValue(userManager.TenantId?.ToString() ??
|
||||
SqlSugarConst.DefaultTenantId.ToString());
|
||||
entityInfo.SetValue(App.User.FindFirst(ClaimConst.TenantId)?.Value ?? SqlSugarConst.DefaultTenantId.ToString());
|
||||
}
|
||||
else if (entityInfo.PropertyName == nameof(EntityBase.CreateUserId))
|
||||
{
|
||||
if (entityValue.CreateUserId == null || entityValue.CreateUserId == 0)
|
||||
entityInfo.SetValue(userManager.UserId);
|
||||
entityInfo.SetValue(App.User.FindFirst(ClaimConst.UserId)?.Value);
|
||||
}
|
||||
else if (entityInfo.PropertyName == nameof(EntityBase.CreateUserName))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(entityValue.CreateUserName))
|
||||
entityInfo.SetValue(userManager.RealName);
|
||||
entityInfo.SetValue(App.User.FindFirst(ClaimConst.RealName)?.Value);
|
||||
}
|
||||
else if (entityInfo.PropertyName == nameof(EntityBaseData.CreateOrgId))
|
||||
{
|
||||
if (entityValue.CreateOrgId == null || entityValue.CreateOrgId == 0)
|
||||
entityInfo.SetValue(userManager.OrgId);
|
||||
entityInfo.SetValue(App.User.FindFirst(ClaimConst.OrgId)?.Value);
|
||||
}
|
||||
else if (entityInfo.PropertyName == nameof(EntityBaseData.CreateOrgName))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(entityValue.CreateOrgName))
|
||||
entityInfo.SetValue(userManager.OrgName);
|
||||
entityInfo.SetValue(App.User.FindFirst(ClaimConst.OrgName)?.Value);
|
||||
}
|
||||
}
|
||||
// 编辑/更新 操作
|
||||
@ -294,7 +279,7 @@ public static class SqlSugarSetup
|
||||
else if (entityInfo.PropertyName == nameof(EntityBase.UpdateUserId))
|
||||
entityInfo.SetValue(App.User?.FindFirst(ClaimConst.UserId)?.Value);
|
||||
else if (entityInfo.PropertyName == nameof(EntityBase.UpdateUserName))
|
||||
entityInfo.SetValue(userManager.RealName);
|
||||
entityInfo.SetValue(App.User?.FindFirst(ClaimConst.RealName)?.Value);
|
||||
}
|
||||
|
||||
// 设置绑定简称字段数据
|
||||
@ -308,13 +293,14 @@ public static class SqlSugarSetup
|
||||
};
|
||||
|
||||
// 超管不受任何过滤器限制
|
||||
if (userManager.AccountType == AccountTypeEnum.SuperAdmin) return;
|
||||
if (App.User?.FindFirst(ClaimConst.AccountType)?.Value == ((int)AccountTypeEnum.SuperAdmin).ToString())
|
||||
return;
|
||||
|
||||
// 配置假删除过滤器
|
||||
dbProvider.QueryFilter.AddTableFilter<IDeletedFilter>(u => u.IsDelete == false);
|
||||
|
||||
// 配置租户过滤器
|
||||
var tenantId = userManager.TenantId?.ToString();
|
||||
var tenantId = App.User?.FindFirst(ClaimConst.TenantId)?.Value;
|
||||
if (!string.IsNullOrWhiteSpace(tenantId))
|
||||
dbProvider.QueryFilter.AddTableFilter<ITenantIdFilter>(u => u.TenantId == long.Parse(tenantId));
|
||||
|
||||
@ -345,23 +331,17 @@ public static class SqlSugarSetup
|
||||
// 操作前记录(字段描述、列名、值、表名、表描述)
|
||||
BeforeData = JSON.Serialize(u.BeforeData),
|
||||
// 传进来的对象(如果对象为空,则使用首个数据的表名作为业务对象)
|
||||
BusinessData = u.BusinessData == null
|
||||
? u.AfterData.FirstOrDefault()?.TableName
|
||||
: JSON.Serialize(u.BusinessData),
|
||||
BusinessData = u.BusinessData == null ? u.AfterData.FirstOrDefault()?.TableName : JSON.Serialize(u.BusinessData),
|
||||
// 枚举(insert、update、delete)
|
||||
DiffType = u.DiffType.ToString().ToUpper(),
|
||||
Sql = u.Sql,
|
||||
Parameters = JSON.Serialize(u.Parameters.Select(e => new
|
||||
{ e.ParameterName, e.Value, TypeName = e.DbType.ToString() })),
|
||||
Parameters = JSON.Serialize(u.Parameters.Select(e => new { e.ParameterName, e.Value, TypeName = e.DbType.ToString() })),
|
||||
Elapsed = u.Time == null ? 0 : (long)u.Time.Value.TotalMilliseconds
|
||||
};
|
||||
var logDb = ITenant.IsAnyConnection(SqlSugarConst.LogConfigId)
|
||||
? ITenant.GetConnectionScope(SqlSugarConst.LogConfigId)
|
||||
: ITenant.GetConnectionScope(SqlSugarConst.MainConfigId);
|
||||
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");
|
||||
Console.WriteLine(DateTime.Now + $"\r\n*****开始差异日志*****\r\n{Environment.NewLine}{JSON.Serialize(logDiff)}{Environment.NewLine}*****结束差异日志*****\r\n");
|
||||
};
|
||||
}
|
||||
|
||||
@ -408,44 +388,33 @@ public static class SqlSugarSetup
|
||||
/// <param name="dbProvider"></param>
|
||||
/// <param name="enableIncreTable"></param>
|
||||
/// <param name="entityNames"></param>
|
||||
public static void InitTable(SqlSugarScopeProvider dbProvider, bool enableIncreTable,
|
||||
List<string> entityNames = null)
|
||||
public static void InitTable(SqlSugarScopeProvider dbProvider, bool enableIncreTable, List<string> entityNames = null)
|
||||
{
|
||||
var config = dbProvider.CurrentConnectionConfig;
|
||||
|
||||
var totalWatch = Stopwatch.StartNew(); // 开始总计时
|
||||
Log.Information($"初始化表结构 {config.DbType} - {config.ConfigId}");
|
||||
var entityTypes = App.EffectiveTypes.Where(u =>
|
||||
!u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false))
|
||||
var entityTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false))
|
||||
.Where(u => !u.GetCustomAttributes<IgnoreTableAttribute>().Any())
|
||||
.WhereIF(enableIncreTable, u => u.IsDefined(typeof(IncreTableAttribute), false)).ToList();
|
||||
|
||||
if (config.ConfigId.ToString() == SqlSugarConst.MainConfigId) // 默认库(有系统表特性、没有日志表和租户表特性)
|
||||
entityTypes = entityTypes.Where(u =>
|
||||
u.GetCustomAttributes<SysTableAttribute>().Any() ||
|
||||
(!u.GetCustomAttributes<LogTableAttribute>().Any() &&
|
||||
!u.GetCustomAttributes<TenantAttribute>().Any()))
|
||||
.ToList();
|
||||
entityTypes = entityTypes.Where(u => u.GetCustomAttributes<SysTableAttribute>().Any() || (!u.GetCustomAttributes<LogTableAttribute>().Any() && !u.GetCustomAttributes<TenantAttribute>().Any())).ToList();
|
||||
else if (config.ConfigId.ToString() == SqlSugarConst.LogConfigId) // 日志库
|
||||
entityTypes = entityTypes.Where(u => u.GetCustomAttributes<LogTableAttribute>().Any()).ToList();
|
||||
else
|
||||
entityTypes = entityTypes.Where(u =>
|
||||
u.GetCustomAttribute<TenantAttribute>()?.configId.ToString() == config.ConfigId.ToString())
|
||||
.ToList(); // 自定义的库
|
||||
entityTypes = entityTypes.Where(u => u.GetCustomAttribute<TenantAttribute>()?.configId.ToString() == config.ConfigId.ToString()).ToList(); // 自定义的库
|
||||
|
||||
// 过滤指定实体
|
||||
if (entityNames is { Count: > 0 })
|
||||
entityTypes = entityTypes.Where(u => entityNames.Contains(u.Name)).ToList();
|
||||
|
||||
// 删除视图再初始化表结构,防止因为视图导致无法同步表结构
|
||||
var viewTypeList = App.EffectiveTypes.Where(u =>
|
||||
!u.IsInterface && !u.IsAbstract && u.IsClass &&
|
||||
u.GetInterfaces().Any(i => i.HasImplementedRawGeneric(typeof(ISqlSugarView)))).ToList();
|
||||
var viewTypeList = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.GetInterfaces().Any(i => i.HasImplementedRawGeneric(typeof(ISqlSugarView)))).ToList();
|
||||
foreach (var viewType in viewTypeList)
|
||||
{
|
||||
var entityInfo = dbProvider.EntityMaintenance.GetEntityInfo(viewType) ?? throw new Exception("获取视图实体配置有误");
|
||||
if (dbProvider.DbMaintenance.GetViewInfoList(false)
|
||||
.Any(it => it.Name.EqualIgnoreCase(entityInfo.DbTableName)))
|
||||
if (dbProvider.DbMaintenance.GetViewInfoList(false).Any(it => it.Name.EqualIgnoreCase(entityInfo.DbTableName)))
|
||||
dbProvider.DbMaintenance.DropView(entityInfo.DbTableName);
|
||||
}
|
||||
|
||||
@ -457,8 +426,7 @@ public static class SqlSugarSetup
|
||||
stopWatch.Stop(); // 停止计时
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
Console.WriteLine(
|
||||
$"初始化表 {entityType,-64} ({config.ConfigId} - {Interlocked.Increment(ref taskIndex):D003}/{size:D003}) 耗时:{stopWatch.ElapsedMilliseconds} ms");
|
||||
Console.WriteLine($"初始化表 {entityType,-64} ({config.ConfigId} - {Interlocked.Increment(ref taskIndex):D003}/{size:D003}) 耗时:{stopWatch.ElapsedMilliseconds} ms");
|
||||
}));
|
||||
Task.WaitAll(taskList.ToArray());
|
||||
|
||||
@ -473,14 +441,16 @@ public static class SqlSugarSetup
|
||||
/// <param name="dbProvider"></param>
|
||||
/// <param name="enableIncreSeed"></param>
|
||||
/// <param name="seedTypes"></param>
|
||||
public static void InitSeedData(SqlSugarScopeProvider dbProvider, bool enableIncreSeed,
|
||||
List<SeedType> seedTypes = null)
|
||||
public static void InitSeedData(SqlSugarScopeProvider dbProvider, bool enableIncreSeed, List<SeedType> seedTypes = null)
|
||||
{
|
||||
var config = dbProvider.CurrentConnectionConfig;
|
||||
|
||||
var totalWatch = Stopwatch.StartNew(); // 开始总计时
|
||||
Log.Information($"初始化种子数据 {config.DbType} - {config.ConfigId}");
|
||||
var seedDataTypes = SeedDataHelper.GetInitSeedDataTypeList(enableIncreSeed);
|
||||
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(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();
|
||||
|
||||
// 过滤指定程序集种子
|
||||
if (seedTypes != null && seedTypes.Count > 0)
|
||||
@ -489,11 +459,9 @@ public static class SqlSugarSetup
|
||||
var tmpSeedTypes = new List<Type>();
|
||||
foreach (var seedType in seedTypes)
|
||||
{
|
||||
var tmpSeedType = seedDataTypes.FirstOrDefault(u =>
|
||||
u.Name == seedType.Name && u.Assembly.ManifestModule.Name == seedType.AssemblyName);
|
||||
var tmpSeedType = seedDataTypes.FirstOrDefault(u => u.Name == seedType.Name && u.Assembly.ManifestModule.Name == seedType.AssemblyName);
|
||||
if (tmpSeedType != null) tmpSeedTypes.Add(tmpSeedType);
|
||||
}
|
||||
|
||||
if (tmpSeedTypes.Count > 0)
|
||||
seedDataTypes = tmpSeedTypes;
|
||||
}
|
||||
@ -511,8 +479,7 @@ public static class SqlSugarSetup
|
||||
stopWatch.Stop(); // 停止计时
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
Console.WriteLine(
|
||||
$"初始化种子数据 {seedType.FullName,-58} ({config.ConfigId} - {Interlocked.Increment(ref taskIndex):D003}/{size:D003},数据量:{tuple.Value.Item1:D003},新增 {tuple.Value.Item2:D003} 条记录,更新 {tuple.Value.Item3:D003} 条记录,耗时:{stopWatch.ElapsedMilliseconds:N0} ms)");
|
||||
Console.WriteLine($"初始化种子数据 {seedType.FullName,-58} ({config.ConfigId} - {Interlocked.Increment(ref taskIndex):D003}/{size:D003},数据量:{tuple.Value.Item1:D003},新增 {tuple.Value.Item2:D003} 条记录,更新 {tuple.Value.Item3:D003} 条记录,耗时:{stopWatch.ElapsedMilliseconds:N0} ms)");
|
||||
}
|
||||
|
||||
totalWatch.Stop(); // 停止总计时
|
||||
@ -530,9 +497,7 @@ public static class SqlSugarSetup
|
||||
|
||||
var totalWatch = Stopwatch.StartNew(); // 开始总计时
|
||||
Log.Information($"初始化视图 {config.DbType} - {config.ConfigId}");
|
||||
var viewTypeList = App.EffectiveTypes.Where(u =>
|
||||
!u.IsInterface && !u.IsAbstract && u.IsClass &&
|
||||
u.GetInterfaces().Any(i => i.HasImplementedRawGeneric(typeof(ISqlSugarView)))).ToList();
|
||||
var viewTypeList = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.GetInterfaces().Any(i => i.HasImplementedRawGeneric(typeof(ISqlSugarView)))).ToList();
|
||||
|
||||
int taskIndex = 0, size = viewTypeList.Count;
|
||||
var taskList = viewTypeList.Select(viewType => Task.Run(() =>
|
||||
@ -544,20 +509,17 @@ public static class SqlSugarSetup
|
||||
var entityInfo = dbProvider.EntityMaintenance.GetEntityInfo(viewType) ?? throw new Exception("获取视图实体配置有误");
|
||||
|
||||
// 如果视图存在,则删除视图
|
||||
if (dbProvider.DbMaintenance.GetViewInfoList(false)
|
||||
.Any(it => it.Name.EqualIgnoreCase(entityInfo.DbTableName)))
|
||||
if (dbProvider.DbMaintenance.GetViewInfoList(false).Any(it => it.Name.EqualIgnoreCase(entityInfo.DbTableName)))
|
||||
dbProvider.DbMaintenance.DropView(entityInfo.DbTableName);
|
||||
|
||||
// 获取初始化视图查询SQL
|
||||
var sql = viewType.GetMethod(nameof(ISqlSugarView.GetQueryableSqlString))
|
||||
?.Invoke(Activator.CreateInstance(viewType), [dbProvider]) as string;
|
||||
var sql = viewType.GetMethod(nameof(ISqlSugarView.GetQueryableSqlString))?.Invoke(Activator.CreateInstance(viewType), [dbProvider]) as string;
|
||||
if (string.IsNullOrWhiteSpace(sql)) throw new Exception("视图初始化Sql语句不能为空");
|
||||
|
||||
try
|
||||
{
|
||||
// 创建视图
|
||||
dbProvider.Ado.ExecuteCommand($"CREATE VIEW {entityInfo.DbTableName} AS " + Environment.NewLine + " " +
|
||||
sql);
|
||||
dbProvider.Ado.ExecuteCommand($"CREATE VIEW {entityInfo.DbTableName} AS " + Environment.NewLine + " " + sql);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -568,8 +530,7 @@ public static class SqlSugarSetup
|
||||
// 停止计时
|
||||
stopWatch.Stop();
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
Console.WriteLine(
|
||||
$"初始化视图 {viewType.FullName,-58} ({config.ConfigId} - {Interlocked.Increment(ref taskIndex):D003}/{size:D003},耗时:{stopWatch.ElapsedMilliseconds:N0} ms)");
|
||||
Console.WriteLine($"初始化视图 {viewType.FullName,-58} ({config.ConfigId} - {Interlocked.Increment(ref taskIndex):D003}/{size:D003},耗时:{stopWatch.ElapsedMilliseconds:N0} ms)");
|
||||
}));
|
||||
Task.WaitAll(taskList.ToArray());
|
||||
|
||||
@ -596,9 +557,7 @@ public static class SqlSugarSetup
|
||||
var entityTypes = App.EffectiveTypes
|
||||
.Where(u => !u.GetCustomAttributes<IgnoreTableAttribute>().Any())
|
||||
.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false) &&
|
||||
!u.IsDefined(typeof(SysTableAttribute), false) &&
|
||||
!u.IsDefined(typeof(LogTableAttribute), false) && !u.IsDefined(typeof(TenantAttribute), false))
|
||||
.ToList();
|
||||
!u.IsDefined(typeof(SysTableAttribute), false) && !u.IsDefined(typeof(LogTableAttribute), false) && !u.IsDefined(typeof(TenantAttribute), false)).ToList();
|
||||
if (entityTypes.Count == 0) return;
|
||||
|
||||
foreach (var entityType in entityTypes)
|
||||
@ -622,7 +581,9 @@ public static class SqlSugarSetup
|
||||
/// <param name="tenantId">租户Id</param>
|
||||
public static void InitTenantData(ITenant iTenant, long dbConfigId, long tenantId)
|
||||
{
|
||||
var seedDataTypes = SeedDataHelper.GetTenantSeedDataTypeList();
|
||||
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))
|
||||
.OrderBy(u => u.GetCustomAttributes(typeof(SeedDataAttribute), false).Length > 0 ? ((SeedDataAttribute)u.GetCustomAttributes(typeof(SeedDataAttribute), false)[0]).Order : 0).ToList();
|
||||
if (seedDataTypes.Count < 1) return;
|
||||
|
||||
var db = iTenant.GetConnectionScope(dbConfigId);
|
||||
@ -644,7 +605,6 @@ public static class SqlSugarSetup
|
||||
sd.GetType().GetProperty(nameof(EntityTenantId.TenantId))!.SetValue(sd, tenantId);
|
||||
}
|
||||
}
|
||||
|
||||
// 若实体包含Pid字段,则设置为当前租户Id
|
||||
if (entityInfo.Columns.Any(u => u.PropertyName == nameof(SysOrg.Pid)))
|
||||
{
|
||||
@ -653,7 +613,6 @@ public static class SqlSugarSetup
|
||||
sd.GetType().GetProperty(nameof(SysOrg.Pid))!.SetValue(sd, tenantId);
|
||||
}
|
||||
}
|
||||
|
||||
// 若实体包含Id字段,则设置为当前租户Id递增1
|
||||
if (entityInfo.Columns.Any(u => u.PropertyName == nameof(EntityBaseId.Id)))
|
||||
{
|
||||
@ -676,10 +635,7 @@ public static class SqlSugarSetup
|
||||
var storage = db.StorageableByObject(seedData).ToStorage();
|
||||
storage.AsInsertable.ExecuteCommand();
|
||||
if (seedType.GetCustomAttribute<IgnoreUpdateSeedAttribute>() == null) // 有忽略更新种子特性时则不更新
|
||||
storage.AsUpdateable
|
||||
.IgnoreColumns(entityInfo.Columns
|
||||
.Where(c => c.PropertyInfo.GetCustomAttribute<IgnoreUpdateSeedColumnAttribute>() != null)
|
||||
.Select(c => c.PropertyName).ToArray()).ExecuteCommand();
|
||||
storage.AsUpdateable.IgnoreColumns(entityInfo.Columns.Where(c => c.PropertyInfo.GetCustomAttribute<IgnoreUpdateSeedColumnAttribute>() != null).Select(c => c.PropertyName).ToArray()).ExecuteCommand();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
|
||||
using Admin.NET.Core;
|
||||
using Admin.NET.Core.Service;
|
||||
using Furion;
|
||||
using Furion.Authorization;
|
||||
using Furion.DataEncryption;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
@ -49,7 +50,6 @@ namespace Admin.NET.Web.Core
|
||||
else
|
||||
{
|
||||
context.Fail(new AuthorizationFailureReason(this, "登录已过期,请重新登录。"));
|
||||
context.StatusCode(StatusCodes.Status401Unauthorized);
|
||||
var currentHttpContext = context.GetCurrentHttpContext();
|
||||
// 跳过签名 SignatureAuthentication 引发的失败
|
||||
if (currentHttpContext.Items.ContainsKey(SignatureAuthenticationDefaults.AuthenticateFailMsgKey)) return;
|
||||
@ -58,47 +58,46 @@ namespace Admin.NET.Web.Core
|
||||
}
|
||||
|
||||
// 验证Token黑名单
|
||||
var userId = httpContext.User.FindFirst(ClaimConst.UserId)?.Value;
|
||||
var version = httpContext.User.FindFirst(ClaimConst.TokenVersion)?.Value;
|
||||
if (sysCacheService.ExistKey($"{CacheConst.KeyTokenBlacklist}{userId}:{version}"))
|
||||
var accessToken = httpContext.Request.Headers.Authorization.ToString();
|
||||
if (sysCacheService.ExistKey($"{CacheConst.KeyTokenBlacklist}:{accessToken}"))
|
||||
{
|
||||
context.Fail(new AuthorizationFailureReason(this, "令牌已失效,请重新登录。"));
|
||||
context.StatusCode(StatusCodes.Status401Unauthorized);
|
||||
context.GetCurrentHttpContext().SignoutToSwagger();
|
||||
return;
|
||||
}
|
||||
|
||||
// 刷新 用户状态
|
||||
if (sysCacheService.NotExistKey($"{CacheConst.KeyUserManager}{userId}"))
|
||||
{
|
||||
var sysAuthService = serviceScope.ServiceProvider.GetRequiredService<SysAuthService>();
|
||||
await sysAuthService.RefreshUserManager(long.Parse(userId!));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(userId))
|
||||
// 验证Token版本号
|
||||
var userId = httpContext.User.FindFirst(ClaimConst.UserId)?.Value;
|
||||
var tokenVersion1 = httpContext.User.FindFirst(ClaimConst.TokenVersion)?.Value;
|
||||
var tokenVersion2 = sysCacheService.Get<string>($"{CacheConst.KeyUserToken}{userId}");
|
||||
if (string.IsNullOrWhiteSpace(tokenVersion2) && !string.IsNullOrWhiteSpace(userId))
|
||||
{
|
||||
// 查库并缓存用户Token版本
|
||||
var user = await serviceScope.ServiceProvider.GetRequiredService<ISqlSugarClient>().Queryable<SysUser>().FirstAsync(u => u.Id == long.Parse(userId));
|
||||
if (user == null || user.Status == StatusEnum.Disable)
|
||||
{
|
||||
context.Fail(new AuthorizationFailureReason(this, "账号不存在或已被停用,请联系管理员。"));
|
||||
context.StatusCode(StatusCodes.Status401Unauthorized);
|
||||
context.GetCurrentHttpContext().SignoutToSwagger();
|
||||
return;
|
||||
}
|
||||
sysCacheService.Set($"{CacheConst.KeyUserToken}{user.Id}", $"{user.TokenVersion}");
|
||||
tokenVersion2 = user.TokenVersion.ToString();
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(tokenVersion1) || tokenVersion1 != tokenVersion2)
|
||||
{
|
||||
context.Fail(new AuthorizationFailureReason(this, "令牌已失效,请重新登录。"));
|
||||
context.GetCurrentHttpContext().SignoutToSwagger();
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证租户有效期
|
||||
var tenantId = LazyHelper.GetService<UserManager>().Value.TenantId?.ToString();
|
||||
var tenantId = httpContext.User.FindFirst(ClaimConst.TenantId)?.Value;
|
||||
if (!string.IsNullOrWhiteSpace(tenantId))
|
||||
{
|
||||
var tenant = sysCacheService.Get<List<SysTenant>>(CacheConst.KeyTenant)?.FirstOrDefault(u => u.Id == long.Parse(tenantId));
|
||||
if (tenant != null && tenant.ExpirationTime != null && DateTime.Now > tenant.ExpirationTime)
|
||||
{
|
||||
context.Fail(new AuthorizationFailureReason(this, "租户已过期,请联系管理员。"));
|
||||
context.StatusCode(StatusCodes.Status401Unauthorized);
|
||||
context.GetCurrentHttpContext().SignoutToSwagger();
|
||||
}
|
||||
}
|
||||
@ -117,31 +116,32 @@ namespace Admin.NET.Web.Core
|
||||
/// <returns></returns>
|
||||
private static async Task<bool> CheckAuthorizeAsync(DefaultHttpContext httpContext)
|
||||
{
|
||||
var userManager = LazyHelper.GetService<UserManager>().Value;
|
||||
|
||||
// 排除超管权限判断
|
||||
if (userManager?.AccountType == AccountTypeEnum.SuperAdmin) return true;
|
||||
if (App.User.FindFirst(ClaimConst.AccountType)?.Value == ((int)AccountTypeEnum.SuperAdmin).ToString())
|
||||
return true;
|
||||
|
||||
var serviceScope = httpContext.RequestServices.CreateScope();
|
||||
|
||||
// 当前接口路由
|
||||
var path = httpContext.Request.Path.ToString();
|
||||
|
||||
// 移动端接口权限判断
|
||||
if (userManager?.LoginMode == LoginModeEnum.APP)
|
||||
if (App.User.FindFirst(ClaimConst.LoginMode)?.Value == ((int)LoginModeEnum.APP).ToString())
|
||||
{
|
||||
var appPermissions = userManager.AppPermissions ?? [];
|
||||
return appPermissions.Exists(u => path.EndsWith(u, StringComparison.CurrentCultureIgnoreCase));
|
||||
var appApiList = serviceScope.ServiceProvider.GetRequiredService<SysCommonService>().GetAppApiList();
|
||||
return appApiList.Exists(u => path.EndsWith(u, StringComparison.CurrentCultureIgnoreCase));
|
||||
}
|
||||
|
||||
// 获取当前用户按钮权限集合和接口黑名单
|
||||
var unauthorizedPermissions = userManager?.UnauthorizedPermissions ?? [];
|
||||
var permissions = userManager?.Permissions ?? [];
|
||||
var sysRoleService = serviceScope.ServiceProvider.GetRequiredService<SysRoleService>();
|
||||
var roleApis = await sysRoleService.GetUserApiList();
|
||||
|
||||
// 若当前路由在按钮权限集合里面则放行
|
||||
if (permissions.Exists(u => path.EndsWith(u, StringComparison.CurrentCultureIgnoreCase)))
|
||||
if (roleApis[0].Exists(u => path.EndsWith(u, StringComparison.CurrentCultureIgnoreCase)))
|
||||
return true;
|
||||
|
||||
// 若当前路由在已接口黑名单里面则禁止
|
||||
return unauthorizedPermissions.TrueForAll(u => !path.EndsWith(u, StringComparison.CurrentCultureIgnoreCase));
|
||||
return roleApis[1].TrueForAll(u => !path.EndsWith(u, StringComparison.CurrentCultureIgnoreCase));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -59,8 +59,8 @@ public partial class @(@Model.ClassName)Service : IDynamicApiController, ITransi
|
||||
@:,SqlSugarRepository<@(@column.FkEntityName)> @(@column.LowerFkEntityName)Rep
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
)
|
||||
{
|
||||
_@(@Model.LowerClassName)Rep = @(@Model.LowerClassName)Rep;
|
||||
@ -81,12 +81,12 @@ public partial class @(@Model.ClassName)Service : IDynamicApiController, ITransi
|
||||
/// 分页查询@(@Model.BusName)
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
/// <returns></returns>
|
||||
[ApiDescriptionSettings(Name = "Page", Description = "分页查询", Order = 1000), HttpPost]
|
||||
[DisplayName("分页查询@(@Model.BusName)")]
|
||||
public async Task<SqlSugarPagedList<Page@(@Model.ClassName)Output>> Page(Page@(@Model.ClassName)Input input)
|
||||
{
|
||||
return await @(@Model.ClassName)Mid.GetQuery(_@(@Model.LowerClassName)Rep, input).Select<Page@(@Model.ClassName)Output>().OrderBuilder(input).ToPagedListAsync(input.Page, input.PageSize);
|
||||
return await @(@Model.ClassName)Mid.GetQuery(_@(@Model.LowerClassName)Rep, input).Select<Page@(@Model.ClassName)Output>().OrderBuilder(input).ToPagedListAsync(input.Page, input.PageSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -171,8 +171,8 @@ if (@column.ColumnKey == "True"){
|
||||
@foreach (var column in Model.TableField){
|
||||
if (@column.ColumnKey == "True"){
|
||||
@:return await _@(@Model.LowerClassName)Rep.GetFirstAsync(u => u.@(@column.PropertyName) == input.@(@column.PropertyName));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -252,7 +252,7 @@ if (@column.ColumnKey == "True"){
|
||||
@foreach (var column in Model.TableField){
|
||||
if (@column.IsGroupBy == "Y"){
|
||||
@: .GroupByIF(input.GroupBy.Contains("@column.PropertyName"), u => u.@column.PropertyName)
|
||||
}
|
||||
}
|
||||
}
|
||||
@: //.Having(it => SqlFunc.AggregateCount(it.Id) > 0)//聚合函数过滤
|
||||
}
|
||||
@ -289,7 +289,7 @@ if (@column.ColumnKey == "True"){
|
||||
[DisplayName("根据输入参数获取@(@Model.BusName)统计")]
|
||||
public async Task<List<@(@Model.ClassName)Output>> GetAggregTotalSum(Page@(@Model.ClassName)Input input)
|
||||
{
|
||||
|
||||
|
||||
var query = @(@Model.ClassName)Mid.GetQuery(_@(@Model.LowerClassName)Rep, input);
|
||||
// 输入参数示例
|
||||
//input = new Page@(@Model.ClassName)Input
|
||||
@ -346,7 +346,7 @@ if (@column.ColumnKey == "True"){
|
||||
@:{
|
||||
@: query = query.Having(string.Join(" AND ", aggregator.HavingConditions));
|
||||
@:}
|
||||
|
||||
|
||||
@:// 执行查询
|
||||
@:return await query.Select<@(@Model.ClassName)Output>(string.Join(", ", selectParts)).ToListAsync();
|
||||
}
|
||||
@ -357,9 +357,9 @@ if (@column.ColumnKey == "True"){
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
@foreach (var column in Model.TableField){
|
||||
if(@column.EffectType == "ForeignKey" && (@column.WhetherAddUpdate == "Y" || column.QueryWhether == "Y")){
|
||||
if(@column.EffectType == "ForeignKey" && (@column.WhetherAddUpdate == "Y" || column.QueryWhether == "Y")){
|
||||
@:/// <summary>
|
||||
@:/// 获取@(@column.ColumnComment)列表
|
||||
@:/// </summary>
|
||||
@ -380,7 +380,7 @@ if(@column.EffectType == "ForeignKey" && (@column.WhetherAddUpdate == "Y" || col
|
||||
}
|
||||
|
||||
@foreach (var column in Model.TableField){
|
||||
if(@column.EffectType == "Upload"){
|
||||
if(@column.EffectType == "Upload"){
|
||||
@:/// <summary>
|
||||
@:/// 上传@(@column.ColumnComment)
|
||||
@:/// </summary>
|
||||
@ -391,13 +391,13 @@ if(@column.EffectType == "Upload"){
|
||||
@:public async Task<SysFile> Upload@(@column.PropertyName)([Required] IFormFile file)
|
||||
@:{
|
||||
@:var service = App.GetRequiredService<SysFileService>();
|
||||
@:return await service.UploadFile(new UploadFileInput { File = file}, "upload/@(@column.PropertyName)" );
|
||||
@:}
|
||||
@:return await service.UploadFile(new UploadFileInput { File = file}, "upload/@(@column.PropertyName)" );
|
||||
@:}
|
||||
}
|
||||
}
|
||||
|
||||
@foreach (var column in Model.TableField){
|
||||
|
||||
|
||||
if(@column.EffectType == "ApiTreeSelector" && !definedObjects.ContainsKey("@(@column.FkEntityName)Tree")){
|
||||
@:/// <summary>
|
||||
@:/// 获取@(@Model.BusName)树列表
|
||||
@ -411,19 +411,19 @@ if(@column.EffectType == "ApiTreeSelector" && !definedObjects.ContainsKey("@(@co
|
||||
|
||||
@:// 有筛选条件时返回list列表(防止构造不出树)
|
||||
@:var isSearch=false;
|
||||
|
||||
|
||||
foreach (var columnTree in Model.TableField){
|
||||
if (@columnTree.QueryWhether == "Y"){
|
||||
if (@columnTree.NetType?.TrimEnd('?') == "string"){
|
||||
@:if(!string.IsNullOrWhiteSpace(input.@columnTree.PropertyName))isSearch=true;
|
||||
|
||||
|
||||
}else if((@columnTree.NetType?.TrimEnd('?') == "int" || @columnTree.NetType?.TrimEnd('?') == "long")){
|
||||
@:if(input.@columnTree.PropertyName>0)isSearch=true;
|
||||
|
||||
|
||||
}else if((@columnTree.NetType?.TrimEnd('?') == "DateTime" && @columnTree.QueryType == "~")){
|
||||
@:if(input.@(@columnTree.PropertyName)Range != null)isSearch=true;
|
||||
} }}
|
||||
|
||||
|
||||
|
||||
@:if (isSearch||!string.IsNullOrEmpty(input.SearchKey?.Trim()))
|
||||
@:{
|
||||
@ -471,4 +471,4 @@ string LowerFirstLetter(string text)
|
||||
{
|
||||
return text.ToString()[..1].ToLower() + text[1..]; // 首字母小写
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,7 +57,7 @@ public class GoViewSysService : IDynamicApiController
|
||||
tenant.Captcha = true;
|
||||
_sysCacheService.Set(CacheConst.KeyTenant, tenantList);
|
||||
|
||||
var sysUser = await _sysUserRep.AsQueryable().IgnoreTenant().FirstAsync(u => u.Account.Equals(input.Username));
|
||||
var sysUser = await _sysUserRep.AsQueryable().ClearFilter<ITenantIdFilter>().FirstAsync(u => u.Account.Equals(input.Username));
|
||||
return new GoViewLoginOutput()
|
||||
{
|
||||
Userinfo = new GoViewLoginUserInfo
|
||||
|
||||
Loading…
Reference in New Issue
Block a user