diff --git a/framework/NPin.Framework.Core/Extensions/HttpContextExtensions.cs b/framework/NPin.Framework.Core/Extensions/HttpContextExtensions.cs index f0bca32..a94f352 100644 --- a/framework/NPin.Framework.Core/Extensions/HttpContextExtensions.cs +++ b/framework/NPin.Framework.Core/Extensions/HttpContextExtensions.cs @@ -1,5 +1,6 @@ -using System.Text; -using System.Text.RegularExpressions; +using System.Net; +using System.Text; +using System.Web; using Microsoft.AspNetCore.Http; namespace NPin.Framework.Core.Extensions; @@ -7,93 +8,125 @@ namespace NPin.Framework.Core.Extensions; public static class HttpContextExtensions { /// - /// 设置文件下载名称 - /// - /// - /// - public static void FileInlineHandle(this HttpContext httpContext, string fileName) - { - string encodeFilename = System.Web.HttpUtility.UrlEncode(fileName, Encoding.GetEncoding("UTF-8")); - httpContext.Response.Headers.Add("Content-Disposition", "inline;filename=" + encodeFilename); + /// 设置文件下载名称 + /// + /// + /// + public static void FileInlineHandle(this HttpContext httpContext, string fileName) + { + string encodeFilename = HttpUtility.UrlEncode(fileName, Encoding.GetEncoding("UTF-8")); + httpContext.Response.Headers.Add("Content-Disposition", "inline;filename=" + encodeFilename); + } - } + /// + /// 设置文件附件名称 + /// + /// + /// + public static void FileAttachmentHandle(this HttpContext httpContext, string fileName) + { + string encodeFilename = HttpUtility.UrlEncode(fileName, Encoding.GetEncoding("UTF-8")); + httpContext.Response.Headers.Add("Content-Disposition", "attachment;filename=" + encodeFilename); + } - /// - /// 设置文件附件名称 - /// - /// - /// - public static void FileAttachmentHandle(this HttpContext httpContext, string fileName) + /// + /// 获取语言种类 + /// + /// + /// + public static string GetLanguage(this HttpContext httpContext) + { + string res = "zh-CN"; + var str = httpContext.Request.Headers["Accept-Language"].FirstOrDefault(); + if (str is not null) { - string encodeFilename = System.Web.HttpUtility.UrlEncode(fileName, Encoding.GetEncoding("UTF-8")); - httpContext.Response.Headers.Add("Content-Disposition", "attachment;filename=" + encodeFilename); - + res = str.Split(",")[0]; } - /// - /// 获取语言种类 - /// - /// - /// - public static string GetLanguage(this HttpContext httpContext) - { - string res = "zh-CN"; - var str = httpContext.Request.Headers["Accept-Language"].FirstOrDefault(); - if (str is not null) - { - res = str.Split(",")[0]; - } - return res; + return res; + } - } + /// + /// 判断是否为异步请求 + /// + /// + /// + public static bool IsAjaxRequest(this HttpRequest request) + { + string header = request.Headers["X-Requested-With"]; + return "XMLHttpRequest".Equals(header); + } + + /// + /// 获取客户端Ip + /// + /// + /// + public static string GetClientIp(this HttpContext? context) + { + if (context == null) return ""; - /// - /// 判断是否为异步请求 - /// - /// - /// - public static bool IsAjaxRequest(this HttpRequest request) + var result = context.Request.Headers["X-Forwarded-For"].FirstOrDefault(); + if (string.IsNullOrEmpty(result)) { - string header = request.Headers["X-Requested-With"]; - return "XMLHttpRequest".Equals(header); + result = context.Connection.RemoteIpAddress?.ToString(); } - /// - /// 获取客户端IP - /// - /// - /// - public static string GetClientIp(this HttpContext context) - { - if (context == null) return ""; - var result = context.Request.Headers["X-Forwarded-For"].FirstOrDefault(); - if (string.IsNullOrEmpty(result)) - { - result = context.Connection.RemoteIpAddress?.ToString(); - } - if (string.IsNullOrEmpty(result) || result.Contains("::1")) - result = "127.0.0.1"; - result = result.Replace("::ffff:", "127.0.0.1"); + // 解析 Ip String 到 Ipv4 或 MapToIPv4 + // 若无法解析 IP String,强制转为本地 Ipv4 + result = IPAddress.TryParse(result, out var ipAddr) + ? ipAddr.MapToIPv4().ToString() + : IPAddress.Loopback.ToString(); - //Ip规则效验 - var regResult = Regex.IsMatch(result, @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$"); + // // 本地回环 转换为 ipv4 127.0.0.1 + // if (string.IsNullOrEmpty(result) || result.Contains("::1")) + // { + // result = "127.0.0.1"; + // } + // + // result = result.Replace("::ffff:", "127.0.0.1"); + // + // // Ip规则效验 + // var regResult = Regex.IsMatch(result, @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$"); + // + // result = regResult ? result : "127.0.0.1"; + return result; + } - result = regResult ? result : "127.0.0.1"; - return result; - } + /// + /// 获取客户端Ip + /// + /// + /// + public static IPAddress? GetClientIpAddress(this HttpContext? context) + { + if (context == null) return null; - /// - /// 获取浏览器标识 - /// - /// - /// - public static string GetUserAgent(this HttpContext context) + var result = context.Request.Headers["X-Forwarded-For"].FirstOrDefault(); + if (string.IsNullOrEmpty(result)) { - return context.Request.Headers["User-Agent"]; + result = context.Connection.RemoteIpAddress?.ToString(); } - public static string[]? GetUserPermissions(this HttpContext context, string permissionsName) - { - return context.User.Claims.Where(x => x.Type == permissionsName).Select(x => x.Value).ToArray(); - } + // 解析 Ip String 到 IpAddress + // 若无法解析 IP String,强制转为本地 Ipv4 + return IPAddress.TryParse(result, out var ipAddr) + ? ipAddr + : IPAddress.Loopback; + } + + /// + /// 获取浏览器标识 + /// + /// + /// + public static string GetUserAgent(this HttpContext context) + { + return context.Request.Headers["User-Agent"]; + } + + public static string[] GetUserPermissions(this HttpContext context, string permissionsName) + { + return context.User.Claims.Where(x => x.Type == permissionsName).Select(x => x.Value).ToArray(); + } } \ No newline at end of file diff --git a/module/upms/NPin.Framework.Upms.Domain.Shared/Consts/TokenTypeConst.cs b/module/upms/NPin.Framework.Upms.Domain.Shared/Consts/TokenTypeConst.cs new file mode 100644 index 0000000..26dcea3 --- /dev/null +++ b/module/upms/NPin.Framework.Upms.Domain.Shared/Consts/TokenTypeConst.cs @@ -0,0 +1,16 @@ +namespace NPin.Framework.Upms.Domain.Shared.Consts; + +public class TokenTypeConst +{ + public const string Id = nameof(Id); + public const string Username = nameof(Username); + public const string TenantId = nameof(TenantId); + public const string OrgId = nameof(OrgId); + public const string Email = nameof(Email); + public const string PhoneNumber = nameof(PhoneNumber); + public const string Roles = nameof(Roles); + public const string Organizations = nameof(Organizations); + public const string Permission = nameof(Permission); + public const string RoleInfo = nameof(RoleInfo); + public const string Refresh = nameof(Refresh); +} \ No newline at end of file diff --git a/module/upms/NPin.Framework.Upms.Domain.Shared/Dtos/OrganizationDto.cs b/module/upms/NPin.Framework.Upms.Domain.Shared/Dtos/OrganizationDto.cs new file mode 100644 index 0000000..3cdb227 --- /dev/null +++ b/module/upms/NPin.Framework.Upms.Domain.Shared/Dtos/OrganizationDto.cs @@ -0,0 +1,33 @@ +namespace NPin.Framework.Upms.Domain.Shared.Dtos; + +public class OrganizationDto +{ + public Guid Id { get; set; } + + public string Name { get; set; } = string.Empty; + public string Code { get; set; } = string.Empty; + public string? Remark { get; set; } + + /// + /// 负责人ID + /// + public Guid LeaderId { get; set; } + + /// + /// 负责人名称,仅用户展示,不存储 + /// + public string Leader { get; set; } + + /// + /// 父节点 ID + /// + public Guid ParentId { get; set; } + + public bool IsDeleted { get; } + public DateTime CreationTime { get; } = DateTime.Now; + public Guid? CreatorId { get; } + public DateTime? LastModificationTime { get; } + public Guid? LastModifierId { get; } + public int OrderNum { get; set; } + public bool IsEnabled { get; set; } +} \ No newline at end of file diff --git a/module/upms/NPin.Framework.Upms.Domain.Shared/Dtos/PostDto.cs b/module/upms/NPin.Framework.Upms.Domain.Shared/Dtos/PostDto.cs new file mode 100644 index 0000000..d237836 --- /dev/null +++ b/module/upms/NPin.Framework.Upms.Domain.Shared/Dtos/PostDto.cs @@ -0,0 +1,18 @@ +namespace NPin.Framework.Upms.Domain.Shared.Dtos; + +public class PostDto +{ + public Guid Id { get; set; } + + public string Code { get; set; } = string.Empty; + public string Name { get; set; } = string.Empty; + public string? Remark { get; set; } + + public bool IsDeleted { get; } + public DateTime CreationTime { get; } = DateTime.Now; + public Guid? CreatorId { get; } + public DateTime? LastModificationTime { get; } + public Guid? LastModifierId { get; } + public int OrderNum { get; set; } + public bool IsEnabled { get; set; } +} \ No newline at end of file diff --git a/module/upms/NPin.Framework.Upms.Domain.Shared/Dtos/RoleDto.cs b/module/upms/NPin.Framework.Upms.Domain.Shared/Dtos/RoleDto.cs new file mode 100644 index 0000000..4bdad92 --- /dev/null +++ b/module/upms/NPin.Framework.Upms.Domain.Shared/Dtos/RoleDto.cs @@ -0,0 +1,29 @@ +using NPin.Framework.Upms.Domain.Shared.Enums; + +namespace NPin.Framework.Upms.Domain.Shared.Dtos; + +public class RoleFullDto +{ + public RoleDto Role { get; set; } = new(); + + // Relations + public HashSet OrganizationList { get; set; } = []; +} + +public class RoleDto +{ + public Guid Id { get; set; } + + public string Code { get; set; } = string.Empty; + public string Name { get; set; } = string.Empty; + public DataScopeEnum DataScope { get; set; } = DataScopeEnum.All; + public string? Remark { get; set; } + + public bool IsDeleted { get; } + public DateTime CreationTime { get; } = DateTime.Now; + public Guid? CreatorId { get; } + public DateTime? LastModificationTime { get; } + public Guid? LastModifierId { get; } + public int OrderNum { get; set; } + public bool IsEnabled { get; set; } +} \ No newline at end of file diff --git a/module/upms/NPin.Framework.Upms.Domain.Shared/Dtos/UserDto.cs b/module/upms/NPin.Framework.Upms.Domain.Shared/Dtos/UserDto.cs new file mode 100644 index 0000000..275dc73 --- /dev/null +++ b/module/upms/NPin.Framework.Upms.Domain.Shared/Dtos/UserDto.cs @@ -0,0 +1,51 @@ +using NPin.Framework.Upms.Domain.Shared.Enums; + +namespace NPin.Framework.Upms.Domain.Shared.Dtos; + +/// +/// 完整用户信息Dto +/// 包括所有关联关系 +/// +public class UserFullDto +{ + public UserDto User { get; set; } = new(); + + // Relations + public HashSet Roles { get; set; } = []; + public HashSet Posts { get; set; } = []; + public HashSet Organizations { get; set; } = []; + + public HashSet PostCodes { get; set; } = []; + public HashSet RoleCodes { get; set; } = []; + public HashSet PermissionCodes { get; set; } = []; +} + +public class UserDto +{ + /// + /// 主键ID + /// + public Guid Id { get; set; } + + public bool IsDeleted { get; } + public DateTime CreationTime { get; } = DateTime.Now; + public Guid? CreatorId { get; } + public DateTime? LastModificationTime { get; } + public Guid? LastModifierId { get; } + public bool IsEnabled { get; set; } + public int OrderNum { get; set; } + + public string Username { get; set; } = string.Empty; + public string PhoneNumber { get; set; } + public string? Email { get; set; } + public string? Nickname { get; set; } + public string Password { get; set; } = string.Empty; + public string Salt { get; set; } = string.Empty; + public string? Introduction { get; set; } + public GenderEnum Gender { get; set; } = GenderEnum.Secrecy; + public string? IpAddr { get; set; } + public string? Avatar { get; set; } + + // For user (metadata) + public Dictionary Metadata { get; set; } +} \ No newline at end of file diff --git a/module/upms/NPin.Framework.Upms.Domain.Shared/Enums/DataScopeEnum.cs b/module/upms/NPin.Framework.Upms.Domain.Shared/Enums/DataScopeEnum.cs index c8f2b4d..b9f4f99 100644 --- a/module/upms/NPin.Framework.Upms.Domain.Shared/Enums/DataScopeEnum.cs +++ b/module/upms/NPin.Framework.Upms.Domain.Shared/Enums/DataScopeEnum.cs @@ -18,12 +18,12 @@ public enum DataScopeEnum /// /// 本部门 /// - Dept = 2, + Org = 2, /// /// 部门以及子部门 /// - DeptFollow = 3, + OrgFollow = 3, /// /// 只有自己 diff --git a/module/upms/NPin.Framework.Upms.Domain.Shared/Etos/LoginEventArgs.cs b/module/upms/NPin.Framework.Upms.Domain.Shared/Etos/LoginEventArgs.cs new file mode 100644 index 0000000..3273c05 --- /dev/null +++ b/module/upms/NPin.Framework.Upms.Domain.Shared/Etos/LoginEventArgs.cs @@ -0,0 +1,45 @@ +namespace NPin.Framework.Upms.Domain.Shared.Etos; + +/// +/// 用户登录事件参数 +/// +public class LoginEventArgs +{ + public Guid UserId { get; set; } + + public string Username { get; set; } + + public string Nickname { get; set; } + + public DateTime CreationTime { get; set; } + + /// + /// 登录地点 + /// + public string? LoginLocation { get; set; } + + /// + /// 登录Ipv4地址 + /// + public string? LoginIPv4 { get; set; } + + /// + /// 登录Ipv6地址,仅当支持Ipv6时有值 + /// + public string? LoginIPv6 { get; set; } + + /// + /// 登录浏览器 + /// + public string? Browser { get; set; } + + /// + /// 操作系统 + /// + public string? Os { get; set; } + + /// + /// 登录信息 + /// + public string? LoginMsg { get; set; } +} \ No newline at end of file diff --git a/module/upms/NPin.Framework.Upms.Domain.Shared/Etos/UserCreatedEventArgs.cs b/module/upms/NPin.Framework.Upms.Domain.Shared/Etos/UserCreatedEventArgs.cs new file mode 100644 index 0000000..9a0b383 --- /dev/null +++ b/module/upms/NPin.Framework.Upms.Domain.Shared/Etos/UserCreatedEventArgs.cs @@ -0,0 +1,9 @@ +namespace NPin.Framework.Upms.Domain.Shared.Etos; + +/// +/// 用户创建事件参数 +/// +public class UserCreatedEventArgs +{ + public Guid UserId { get; set; } +} \ No newline at end of file diff --git a/module/upms/NPin.Framework.Upms.Domain.Shared/NPin.Framework.Upms.Domain.Shared.csproj b/module/upms/NPin.Framework.Upms.Domain.Shared/NPin.Framework.Upms.Domain.Shared.csproj index b68603d..8fbf1da 100644 --- a/module/upms/NPin.Framework.Upms.Domain.Shared/NPin.Framework.Upms.Domain.Shared.csproj +++ b/module/upms/NPin.Framework.Upms.Domain.Shared/NPin.Framework.Upms.Domain.Shared.csproj @@ -8,8 +8,6 @@ - - diff --git a/module/upms/NPin.Framework.Upms.Domain/Authorization/DataPermissionExtensions.cs b/module/upms/NPin.Framework.Upms.Domain/Authorization/DataPermissionExtensions.cs new file mode 100644 index 0000000..cfaa65e --- /dev/null +++ b/module/upms/NPin.Framework.Upms.Domain/Authorization/DataPermissionExtensions.cs @@ -0,0 +1,16 @@ +using Volo.Abp.Data; + +namespace NPin.Framework.Upms.Domain.Authorization; + +public static class DataPermissionExtensions +{ + /// + /// 关闭数据权限 + /// + /// + /// + public static IDisposable DisablePermissionHandler(this IDataFilter dataFilter) + { + return dataFilter.Disable(); + } +} \ No newline at end of file diff --git a/module/upms/NPin.Framework.Upms.Domain/Authorization/DefaultPermissionHandler.cs b/module/upms/NPin.Framework.Upms.Domain/Authorization/DefaultPermissionHandler.cs new file mode 100644 index 0000000..91b71f0 --- /dev/null +++ b/module/upms/NPin.Framework.Upms.Domain/Authorization/DefaultPermissionHandler.cs @@ -0,0 +1,35 @@ +using Microsoft.AspNetCore.Http; +using NPin.Framework.Core.Extensions; +using NPin.Framework.Upms.Domain.Shared.Consts; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Users; + +namespace NPin.Framework.Upms.Domain.Authorization; + +/// +/// 默认权限处理器 +/// +public class DefaultPermissionHandler : IPermissionHandler, ITransientDependency +{ + private readonly ICurrentUser _currentUser; + private readonly IHttpContextAccessor _httpContextAccessor; + + public DefaultPermissionHandler(ICurrentUser currentUser, IHttpContextAccessor httpContextAccessor) + { + _currentUser = currentUser; + _httpContextAccessor = httpContextAccessor; + } + + public bool IsPass(string permission) + { + var permissions = _httpContextAccessor.HttpContext.GetUserPermissions(TokenTypeConst.Permission); + + // 超级管理员 + if (permissions.Contains("*:*:*")) + { + return true; + } + + return permissions.Contains(permission); + } +} \ No newline at end of file diff --git a/module/upms/NPin.Framework.Upms.Domain/Authorization/IPermissionHandler.cs b/module/upms/NPin.Framework.Upms.Domain/Authorization/IPermissionHandler.cs new file mode 100644 index 0000000..a2c4808 --- /dev/null +++ b/module/upms/NPin.Framework.Upms.Domain/Authorization/IPermissionHandler.cs @@ -0,0 +1,6 @@ +namespace NPin.Framework.Upms.Domain.Authorization; + +public interface IPermissionHandler +{ + bool IsPass(string permission); +} \ No newline at end of file diff --git a/module/upms/NPin.Framework.Upms.Domain/Authorization/PermissionAttribute.cs b/module/upms/NPin.Framework.Upms.Domain/Authorization/PermissionAttribute.cs new file mode 100644 index 0000000..de3e5c3 --- /dev/null +++ b/module/upms/NPin.Framework.Upms.Domain/Authorization/PermissionAttribute.cs @@ -0,0 +1,12 @@ +namespace NPin.Framework.Upms.Domain.Authorization; + +[AttributeUsage(AttributeTargets.Method)] +public class PermissionAttribute: Attribute +{ + internal string Code { get; set; } + + public PermissionAttribute(string code) + { + Code = code; + } +} \ No newline at end of file diff --git a/module/upms/NPin.Framework.Upms.Domain/Authorization/PermissionGlobalAttribute.cs b/module/upms/NPin.Framework.Upms.Domain/Authorization/PermissionGlobalAttribute.cs new file mode 100644 index 0000000..8aa862b --- /dev/null +++ b/module/upms/NPin.Framework.Upms.Domain/Authorization/PermissionGlobalAttribute.cs @@ -0,0 +1,45 @@ +using System.Net; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.AspNetCore.Mvc.Filters; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Http; + +namespace NPin.Framework.Upms.Domain.Authorization; + +/// +/// 权限处理器 +/// +internal class PermissionGlobalAttribute : ActionFilterAttribute, ITransientDependency +{ + private readonly IPermissionHandler _permissionHandler; + + public PermissionGlobalAttribute(IPermissionHandler permissionHandler) + { + _permissionHandler = permissionHandler; + } + + public override void OnActionExecuting(ActionExecutingContext context) + { + if (context.ActionDescriptor is not ControllerActionDescriptor controllerActionDescriptor) return; + var perAttribute = controllerActionDescriptor.MethodInfo.GetCustomAttributes(inherit: true) + .FirstOrDefault(a => a.GetType() == typeof(PermissionAttribute)) as PermissionAttribute; + // 无权限标识,通过 + if (perAttribute is null) return; + + var passed = _permissionHandler.IsPass(perAttribute.Code); + if (passed) return; + + var model = new RemoteServiceErrorInfo + { + Code = "403", + Message = "您无权访问,请联系管理员", + Details = $"您无权访问该接口-{context.HttpContext.Request.Path.Value}" + }; + var content = new ObjectResult(new { error = model }) + { + StatusCode = (int)HttpStatusCode.Forbidden + }; + context.Result = content; + } +} \ No newline at end of file diff --git a/module/upms/NPin.Framework.Upms.Domain/Authorization/RefreshTokenMiddleware.cs b/module/upms/NPin.Framework.Upms.Domain/Authorization/RefreshTokenMiddleware.cs new file mode 100644 index 0000000..38ec12a --- /dev/null +++ b/module/upms/NPin.Framework.Upms.Domain/Authorization/RefreshTokenMiddleware.cs @@ -0,0 +1,49 @@ +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using NPin.Framework.Upms.Domain.Shared.Consts; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Security.Claims; + +namespace NPin.Framework.Upms.Domain.Authorization; + +/// +/// RefreshToken 处理中间件 +/// +public class RefreshTokenMiddleware : IMiddleware, ITransientDependency +{ + public async Task InvokeAsync(HttpContext context, RequestDelegate next) + { + var refreshToken = context.Request.Headers["refresh_token"].ToString(); + if (!refreshToken.IsNullOrEmpty()) + { + var authResult = await context.AuthenticateAsync(TokenTypeConst.Refresh); + // Token刷新成功 + if (authResult.Succeeded) + { + var userId = Guid.Parse(authResult.Principal.FindFirst(AbpClaimTypes.UserId).Value); + // TODO + // var accessToken = + // var refreshToken = + context.Response.Headers["access_token"] = ""; + context.Response.Headers["refresh_token"] = ""; + + // 请求头替换 + context.Request.Headers["Authorization"] = $"Bearer {""}"; + } + } + await next(context); + } +} + +/// +/// 扩展 +/// +public static class RefreshTokenExtensions +{ + public static IApplicationBuilder UseRefreshToken(this IApplicationBuilder builder) + { + builder.UseMiddleware(); + return builder; + } +} \ No newline at end of file diff --git a/module/upms/NPin.Framework.Upms.Domain/Authorization/SocialAggregateRoot.cs b/module/upms/NPin.Framework.Upms.Domain/Authorization/SocialAggregateRoot.cs new file mode 100644 index 0000000..11e3d14 --- /dev/null +++ b/module/upms/NPin.Framework.Upms.Domain/Authorization/SocialAggregateRoot.cs @@ -0,0 +1,46 @@ +using SqlSugar; +using Volo.Abp.Auditing; +using Volo.Abp.Data; +using Volo.Abp.Domain.Entities; + +namespace NPin.Framework.Upms.Domain.Authorization; + +[SugarTable("Social", "第三方授权表")] +public class SocialAggregateRoot: AggregateRoot, ISoftDelete, IHasCreationTime +{ + [SugarColumn(IsPrimaryKey = true)] + public override Guid Id { get; protected set; } + + public Guid UserId { get; set; } + + public string OpenId { get; set; } + + public string? UnionId { get; set; } + + public string Name { get; set; } + + public string Type { get; set; } + + public bool IsDeleted { get; } + public DateTime CreationTime { get; } + + [SugarColumn(IsIgnore = true)] + public override ExtraPropertyDictionary ExtraProperties { get; protected set; } + + public SocialAggregateRoot() + { + } + + public SocialAggregateRoot(string type, Guid userId, string openId, string? unionId) + { + UserId = userId; + OpenId = openId; + UnionId = unionId; + Type = type; + } + + public SocialAggregateRoot(string type, Guid userId, string openId, string? unionId, string name): this(type, userId, openId, unionId) + { + Name = name; + } +} \ No newline at end of file diff --git a/module/upms/NPin.Framework.Upms.Domain/Entities/LoginLogEntity.cs b/module/upms/NPin.Framework.Upms.Domain/Entities/LoginLogEntity.cs new file mode 100644 index 0000000..da55bc8 --- /dev/null +++ b/module/upms/NPin.Framework.Upms.Domain/Entities/LoginLogEntity.cs @@ -0,0 +1,81 @@ +using System.Net; +using System.Net.Sockets; +using IPTools.Core; +using Microsoft.AspNetCore.Http; +using NPin.Framework.Core.Extensions; +using SqlSugar; +using UAParser; +using Volo.Abp.Auditing; +using Volo.Abp.Domain.Entities; + +namespace NPin.Framework.Upms.Domain.Entities; + +[SugarTable("LoginLog", "登录日志表")] +public class LoginLogEntity : Entity, ICreationAuditedObject +{ + [SugarColumn(IsPrimaryKey = true)] public override Guid Id { get; protected set; } + + public DateTime CreationTime { get; } + public Guid? CreatorId { get; } + + [SugarColumn(ColumnDescription = "登录用户")] + public string? LoginUser { get; set; } + + [SugarColumn(ColumnDescription = "登录用户ID")] + public Guid LoginUserId { get; set; } + + [SugarColumn(ColumnDescription = "登录地点")] + public string? LoginLocation { get; set; } + + [SugarColumn(ColumnDescription = "Ipv4")] + public string? LoginIPv4 { get; set; } + + [SugarColumn(ColumnDescription = "Ipv6")] + public string? LoginIPv6 { get; set; } + + [SugarColumn(ColumnDescription = "浏览器")] + public string? Browser { get; set; } + + [SugarColumn(ColumnDescription = "操作系统")] + public string? Os { get; set; } + + [SugarColumn(ColumnDescription = "登录信息")] + public string? LoginMsg { get; set; } + + public static LoginLogEntity GetInfoByHttpContext(HttpContext httpContext) + { + // var ipInfo = httpContext.GetRemoteIpInfo(); + string ipv4AddrStr = null; + string ipv6AddrStr = null; + var ipAddr = httpContext.GetClientIpAddress(); + if (ipAddr != null) + { + switch (ipAddr.AddressFamily) + { + case AddressFamily.InterNetwork: + ipv4AddrStr = ipAddr.ToString(); + break; + case AddressFamily.InterNetworkV6: + ipv6AddrStr = ipAddr.ToString(); + break; + } + } + + var location = IPAddress.IsLoopback(ipAddr) + ? new IpInfo { Province = "本地", City = "本机" } + : IpTool.Search(ipAddr.ToString()); + + var clientInfo = GetClientInfo(httpContext); + + return new LoginLogEntity + { + Browser = clientInfo.Device.Family, + Os = clientInfo.OS.ToString(), + LoginIPv4 = ipv4AddrStr, + LoginIPv6 = ipv6AddrStr, + LoginLocation = $"{location.Country}-{location.Province}-{location.City}" + }; + + ClientInfo GetClientInfo(HttpContext ctx) => Parser.GetDefault().Parse(ctx.GetUserAgent()); + } +} \ No newline at end of file diff --git a/module/upms/NPin.Framework.Upms.Domain/Entities/RoleEntity.cs b/module/upms/NPin.Framework.Upms.Domain/Entities/RoleEntity.cs index e52d8e6..c0bbc3f 100644 --- a/module/upms/NPin.Framework.Upms.Domain/Entities/RoleEntity.cs +++ b/module/upms/NPin.Framework.Upms.Domain/Entities/RoleEntity.cs @@ -20,7 +20,7 @@ public class RoleEntity : Entity, ISoftDelete, IAuditedObject, IOrderNum, [SugarColumn(ColumnDescription = "角色数据权限范围")] public DataScopeEnum DataScope { get; set; } = DataScopeEnum.All; - + [SugarColumn(ColumnDescription = "描述")] public string? Remark { get; set; } @@ -31,4 +31,12 @@ public class RoleEntity : Entity, ISoftDelete, IAuditedObject, IOrderNum, public Guid? LastModifierId { get; } public int OrderNum { get; set; } public bool IsEnabled { get; set; } + + #region 导航 + + [Navigate(typeof(RoleOrganizationEntity), nameof(RoleOrganizationEntity.RoleId), + nameof(RoleOrganizationEntity.OrgId))] + public List OrganizationList { get; set; } + + #endregion } \ No newline at end of file diff --git a/module/upms/NPin.Framework.Upms.Domain/Entities/RoleOrganizationEntity.cs b/module/upms/NPin.Framework.Upms.Domain/Entities/RoleOrganizationEntity.cs new file mode 100644 index 0000000..8bcf629 --- /dev/null +++ b/module/upms/NPin.Framework.Upms.Domain/Entities/RoleOrganizationEntity.cs @@ -0,0 +1,16 @@ +using SqlSugar; +using Volo.Abp.Domain.Entities; + +namespace NPin.Framework.Upms.Domain.Entities; + +[SugarTable("RelRoleOrganization", "角色-机构 关系表")] +public class RoleOrganizationEntity : Entity +{ + [SugarColumn(IsPrimaryKey = true)] public override Guid Id { get; protected set; } + + [SugarColumn(ColumnDescription = "角色ID")] + public Guid RoleId { get; set; } + + [SugarColumn(ColumnDescription = "机构ID")] + public Guid OrgId { get; set; } +} \ No newline at end of file diff --git a/module/upms/NPin.Framework.Upms.Domain/Entities/UserEntity.cs b/module/upms/NPin.Framework.Upms.Domain/Entities/UserEntity.cs index b8858fc..c7ba9f9 100644 --- a/module/upms/NPin.Framework.Upms.Domain/Entities/UserEntity.cs +++ b/module/upms/NPin.Framework.Upms.Domain/Entities/UserEntity.cs @@ -88,6 +88,9 @@ public class UserEntity : Entity, ISoftDelete, IAuditedObject, IEnabled, I [Navigate(typeof(UserPostEntity), nameof(UserPostEntity.UserId), nameof(UserPostEntity.PostId))] public List Posts { get; set; } + /// + /// 所在的组织机构列表,多对多 + /// [Navigate(typeof(UserOrganizationEntity), nameof(UserOrganizationEntity.UserId), nameof(UserOrganizationEntity.OrganizationId))] public List Organizations { get; set; } diff --git a/module/upms/NPin.Framework.Upms.Domain/EventHandlers/LoginEventHandler.cs b/module/upms/NPin.Framework.Upms.Domain/EventHandlers/LoginEventHandler.cs new file mode 100644 index 0000000..b1ad395 --- /dev/null +++ b/module/upms/NPin.Framework.Upms.Domain/EventHandlers/LoginEventHandler.cs @@ -0,0 +1,32 @@ +using Mapster; +using Microsoft.Extensions.Logging; +using NPin.Framework.Upms.Domain.Entities; +using NPin.Framework.Upms.Domain.Shared.Etos; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.EventBus; + +namespace NPin.Framework.Upms.Domain.EventHandlers; + +public class LoginEventHandler : ILocalEventHandler, ITransientDependency +{ + private readonly ILogger _logger; + private readonly IRepository _repository; + + public LoginEventHandler(ILogger logger, IRepository repository) + { + _logger = logger; + _repository = repository; + } + + public async Task HandleEventAsync(LoginEventArgs eventData) + { + _logger.LogInformation($"用户[{eventData.UserId}:{eventData.Username}]登录"); + var loginLogEntity = eventData.Adapt(); + loginLogEntity.LoginMsg = $"{eventData.Username}登录系统"; + loginLogEntity.LoginUser = eventData.Username; + loginLogEntity.LoginUserId = eventData.UserId; + // 插入 + await _repository.InsertAsync(loginLogEntity); + } +} \ No newline at end of file diff --git a/module/upms/NPin.Framework.Upms.Domain/Extensions/CurrentUserExtensions.cs b/module/upms/NPin.Framework.Upms.Domain/Extensions/CurrentUserExtensions.cs new file mode 100644 index 0000000..9e5e1d2 --- /dev/null +++ b/module/upms/NPin.Framework.Upms.Domain/Extensions/CurrentUserExtensions.cs @@ -0,0 +1,42 @@ +using NPin.Framework.Upms.Domain.Shared.Consts; +using Volo.Abp.Users; + +namespace NPin.Framework.Upms.Domain.Extensions; + +public static class CurrentUserExtensions +{ + /// + /// 获取用户权限代码列表 + /// + /// + /// + public static List GetPermissions(this ICurrentUser currentUser) + { + return currentUser.FindClaims(TokenTypeConst.Permission) + .Select(x => x.Value) + .ToList(); + } + + /// + /// 获取用户组织机构ID列表 + /// + /// + /// + public static List GetOrganizationIds(this ICurrentUser currentUser) + { + return currentUser.FindClaims(TokenTypeConst.Organizations) + .Select(x => Guid.Parse(x.Value)) + .ToList(); + } + + /// + /// 用户是否是通过Refresh进来的 + /// + /// + /// + public static bool IsRefreshToken(this ICurrentUser currentUser) + { + var valOrNull = currentUser.FindClaim(TokenTypeConst.Refresh)?.Value; + return valOrNull is not null && bool.Parse(valOrNull); + } +} \ No newline at end of file diff --git a/module/upms/NPin.Framework.Upms.Domain/Managers/AccountManager.cs b/module/upms/NPin.Framework.Upms.Domain/Managers/AccountManager.cs new file mode 100644 index 0000000..16f4c04 --- /dev/null +++ b/module/upms/NPin.Framework.Upms.Domain/Managers/AccountManager.cs @@ -0,0 +1,12 @@ +using Volo.Abp.Domain.Services; + +namespace NPin.Framework.Upms.Domain.Managers; + +public interface IAccountManager +{ +} + +public class AccountManager : DomainService, IAccountManager +{ + // private readonly +} \ No newline at end of file diff --git a/module/upms/NPin.Framework.Upms.Domain/NPinFrameworkUpmsDomainModule.cs b/module/upms/NPin.Framework.Upms.Domain/NPinFrameworkUpmsDomainModule.cs index 9163450..4ab292e 100644 --- a/module/upms/NPin.Framework.Upms.Domain/NPinFrameworkUpmsDomainModule.cs +++ b/module/upms/NPin.Framework.Upms.Domain/NPinFrameworkUpmsDomainModule.cs @@ -1,6 +1,9 @@ using Microsoft.Extensions.DependencyInjection; using NPin.Framework.Caching.FreeRedis; +using NPin.Framework.Upms.Domain.Authorization; +using NPin.Framework.Upms.Domain.OperLog; using NPin.Framework.Upms.Domain.Shared; +using NPin.Framework.Upms.Domain.Shared.Options; using Volo.Abp.AspNetCore.SignalR; using Volo.Abp.Caching; using Volo.Abp.Domain; @@ -23,10 +26,11 @@ public class NPinFrameworkUpmsDomainModule : AbpModule var configuration = services.GetConfiguration(); services.AddControllers(opts => { - // opts.Filters.Add() + opts.Filters.Add(); + opts.Filters.Add(); }); - + // 配置短信 - // Configure(); + Configure(configuration.GetSection(nameof(AliyunOptions))); } } \ No newline at end of file diff --git a/module/upms/NPin.Framework.Upms.Domain/OperLog/OperLogGlobalAttribute.cs b/module/upms/NPin.Framework.Upms.Domain/OperLog/OperLogGlobalAttribute.cs new file mode 100644 index 0000000..eb60a52 --- /dev/null +++ b/module/upms/NPin.Framework.Upms.Domain/OperLog/OperLogGlobalAttribute.cs @@ -0,0 +1,79 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using NPin.Framework.Core.Extensions; +using NPin.Framework.Upms.Domain.Shared.OperLog; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Users; + +namespace NPin.Framework.Upms.Domain.OperLog; + +public class OperLogGlobalAttribute : ActionFilterAttribute, ITransientDependency +{ + private readonly ILogger _logger; + private IRepository _repository; + private ICurrentUser _currentUser; + + public OperLogGlobalAttribute(ILogger logger, IRepository repository, + ICurrentUser currentUser) + { + _logger = logger; + _repository = repository; + _currentUser = currentUser; + } + + public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) + { + // 获取执行结果 + var resultContext = await next.Invoke(); + + // 判断是否在 控制器方法 上 + if (resultContext.ActionDescriptor is not ControllerActionDescriptor contextActionDescriptor) return; + + // 查找特性 + var operLogAttribute = contextActionDescriptor.MethodInfo.GetCustomAttributes(inherit: true) + .FirstOrDefault(a => a.GetType() == typeof(OperLogAttribute)) as OperLogAttribute; + // 无特性表达式 直接返回 不处理 + if (operLogAttribute is null) return; + + // 获取Ip + var logEntity = OperationLogEntity.GetInfoByHttpContext(resultContext.HttpContext); + + logEntity.OperType = operLogAttribute.OperType; + logEntity.Title = operLogAttribute.Title; + logEntity.RequestMethod = resultContext.HttpContext.Request.Method; + logEntity.Method = resultContext.HttpContext.Request.Path.Value; + logEntity.OperUser = _currentUser.UserName; + + // 请求结果保存 + if (operLogAttribute.IsSaveResponseData) + { + if (resultContext.Result is ContentResult { ContentType: "application/json" } result) + { + logEntity.RequestResult = result.Content?.Replace("\r\n", "").Trim(); + } + + if (resultContext.Result is JsonResult result2) + { + logEntity.RequestResult = result2.Value?.ToString(); + } + + if (resultContext.Result is ObjectResult result3) + { + logEntity.RequestResult = JsonConvert.SerializeObject(result3); + } + } + + // 请求参数保存 + if (operLogAttribute.IsSaveRequestData) + { + // 不建议保存 比较吃性能 + // logEntity.RequestParam = context.HttpContext.GetRequestValue(); + } + + await _repository.InsertAsync(logEntity); + } +} \ No newline at end of file diff --git a/module/upms/NPin.Framework.Upms.Domain/OperLog/OperationLogEntity.cs b/module/upms/NPin.Framework.Upms.Domain/OperLog/OperationLogEntity.cs new file mode 100644 index 0000000..246adab --- /dev/null +++ b/module/upms/NPin.Framework.Upms.Domain/OperLog/OperationLogEntity.cs @@ -0,0 +1,86 @@ +using System.Net; +using System.Net.Sockets; +using IPTools.Core; +using Microsoft.AspNetCore.Http; +using NPin.Framework.Core.Extensions; +using NPin.Framework.Upms.Domain.Entities; +using NPin.Framework.Upms.Domain.Shared.OperLog; +using SqlSugar; +using UAParser; +using Volo.Abp.Auditing; +using Volo.Abp.Domain.Entities; + +namespace NPin.Framework.Upms.Domain.OperLog; + +[SugarTable("OperationLog", "操作日志记录表")] +public class OperationLogEntity: Entity, ICreationAuditedObject +{ + [SugarColumn(IsPrimaryKey = true)] + public override Guid Id { get; protected set; } + + [SugarColumn(ColumnDescription = "日志标题")] + public string? Title { get; set; } + + [SugarColumn(ColumnDescription = "操作类型")] + public OperTypeEnum OperType { get; set; } + + [SugarColumn(ColumnDescription = "请求方式")] + public string? RequestMethod { get; set; } + + [SugarColumn(ColumnDescription = "操作者")] + public string? OperUser { get; set; } + + [SugarColumn(ColumnDescription = "操作者Ipv4")] + public string? OperIPv4 { get; set; } + + [SugarColumn(ColumnDescription = "操作者Ipv6")] + public string? OperIPv6 { get; set; } + + [SugarColumn(ColumnDescription = "操作地点")] + public string? OperLocation { get; set; } + + [SugarColumn(ColumnDescription = "操作方法")] + public string? Method { get; set; } + + [SugarColumn(ColumnDescription = "请求参数")] + public string? RequestParam { get; set; } + + [SugarColumn(ColumnDescription = "请求结果", Length = 9999)] + public string? RequestResult { get; set; } + + public DateTime CreationTime { get; } + public Guid? CreatorId { get; } + + public static OperationLogEntity GetInfoByHttpContext(HttpContext httpContext) + { + // var ipInfo = httpContext.GetRemoteIpInfo(); + string ipv4AddrStr = null; + string ipv6AddrStr = null; + IpInfo info = new IpInfo { Province = "本地", City = "本机" }; + + var ipAddr = httpContext.GetClientIpAddress(); + if (ipAddr != null) + { + switch (ipAddr.AddressFamily) + { + case AddressFamily.InterNetwork: + ipv4AddrStr = ipAddr.ToString(); + break; + case AddressFamily.InterNetworkV6: + ipv6AddrStr = ipAddr.ToString(); + break; + } + + if (!IPAddress.IsLoopback(ipAddr)) + { + info = IpTool.Search(ipAddr.ToString()); + } + } + return new OperationLogEntity + { + OperIPv4 = ipv4AddrStr, + OperIPv6 = ipv6AddrStr, + OperLocation = $"{info.Country}-{info.Province}-{info.City}" + }; + } +} \ No newline at end of file diff --git a/module/upms/NPin.Framework.Upms.Domain/Repositories/IUserRepository.cs b/module/upms/NPin.Framework.Upms.Domain/Repositories/IUserRepository.cs new file mode 100644 index 0000000..5703863 --- /dev/null +++ b/module/upms/NPin.Framework.Upms.Domain/Repositories/IUserRepository.cs @@ -0,0 +1,9 @@ +using NPin.Framework.SqlSugarCore.Abstractions; +using NPin.Framework.Upms.Domain.Entities; + +namespace NPin.Framework.Upms.Domain.Repositories; + +public interface IUserRepository: ISqlSugarRepository +{ + // Task<> +} \ No newline at end of file diff --git a/src/NPin.Application.Contracts/NPin.Application.Contracts.csproj b/src/NPin.Application.Contracts/NPin.Application.Contracts.csproj index 660a61e..9254605 100644 --- a/src/NPin.Application.Contracts/NPin.Application.Contracts.csproj +++ b/src/NPin.Application.Contracts/NPin.Application.Contracts.csproj @@ -9,6 +9,7 @@ + diff --git a/src/NPin.Application.Contracts/NPinApplicationContractsModule.cs b/src/NPin.Application.Contracts/NPinApplicationContractsModule.cs index ba75ed5..858d596 100644 --- a/src/NPin.Application.Contracts/NPinApplicationContractsModule.cs +++ b/src/NPin.Application.Contracts/NPinApplicationContractsModule.cs @@ -1,12 +1,14 @@ using NPin.Domain.Shared; using NPin.Framework.Ddd.Application.Contracts; using NPin.Framework.TenantManagement.Application.Contracts; +using NPin.Framework.Upms.Application.Contracts; namespace NPin.Application.Contracts; [DependsOn( typeof(NPinDomainSharedModule), - // TODO RBAC + // + typeof(NPinFrameworkUpmsApplicationContractsModule), // TODO Bbs typeof(NPinFrameworkTenantManagementApplicationContractsModule), typeof(NPinFrameworkDddApplicationContractsModule) diff --git a/src/NPin.Application/NPin.Application.csproj b/src/NPin.Application/NPin.Application.csproj index a20842a..f5d0cbe 100644 --- a/src/NPin.Application/NPin.Application.csproj +++ b/src/NPin.Application/NPin.Application.csproj @@ -3,6 +3,7 @@ + diff --git a/src/NPin.Application/NPinApplicationModule.cs b/src/NPin.Application/NPinApplicationModule.cs index 7b67707..868f71c 100644 --- a/src/NPin.Application/NPinApplicationModule.cs +++ b/src/NPin.Application/NPinApplicationModule.cs @@ -2,13 +2,16 @@ using NPin.Domain; using NPin.Framework.Ddd.Application; using NPin.Framework.TenantManagement.Application; +using NPin.Framework.Upms.Application; namespace NPin.Application; [DependsOn( typeof(NPinApplicationContractsModule), typeof(NPinDomainModule), - // TODO rbac bbs + // + typeof(NPinFrameworkUpmsApplicationModule), + // TODO bbs typeof(NPinFrameworkTenantManagementApplicationModule), // TODO code-gen typeof(NPinFrameworkDddApplicationModule) diff --git a/src/NPin.Domain.Shared/NPin.Domain.Shared.csproj b/src/NPin.Domain.Shared/NPin.Domain.Shared.csproj index 2fc014b..1848662 100644 --- a/src/NPin.Domain.Shared/NPin.Domain.Shared.csproj +++ b/src/NPin.Domain.Shared/NPin.Domain.Shared.csproj @@ -14,5 +14,6 @@ + diff --git a/src/NPin.Domain.Shared/NPinDomainSharedModule.cs b/src/NPin.Domain.Shared/NPinDomainSharedModule.cs index 56ef87d..4e40d3b 100644 --- a/src/NPin.Domain.Shared/NPinDomainSharedModule.cs +++ b/src/NPin.Domain.Shared/NPinDomainSharedModule.cs @@ -1,10 +1,11 @@ using NPin.Framework.AuditLogging.Domain.Shared; +using NPin.Framework.Upms.Domain.Shared; using Volo.Abp.Domain; namespace NPin.Domain.Shared; [DependsOn( - // TODO RBAC + typeof(NPinFrameworkUpmsDomainSharedModule), // TODO BBS typeof(NPinFrameworkAuditLoggingDomainSharedModule), typeof(AbpDddDomainSharedModule) diff --git a/src/NPin.Domain/NPin.Domain.csproj b/src/NPin.Domain/NPin.Domain.csproj index 85839a9..65d25f5 100644 --- a/src/NPin.Domain/NPin.Domain.csproj +++ b/src/NPin.Domain/NPin.Domain.csproj @@ -13,6 +13,7 @@ + diff --git a/src/NPin.Domain/NPinDomainModule.cs b/src/NPin.Domain/NPinDomainModule.cs index 68f3ec9..3aaf49b 100644 --- a/src/NPin.Domain/NPinDomainModule.cs +++ b/src/NPin.Domain/NPinDomainModule.cs @@ -2,6 +2,7 @@ using NPin.Framework.AuditLogging.Domain; using NPin.Framework.Mapster; using NPin.Framework.TenantManagement.Domain; +using NPin.Framework.Upms.Domain; using Volo.Abp.Caching; using Volo.Abp.Domain; @@ -11,7 +12,7 @@ namespace NPin.Domain; typeof(NPinDomainSharedModule), // typeof(NPinFrameworkTenantManagementDomainModule), - // TODO Rbac + typeof(NPinFrameworkUpmsDomainModule), // TODO Bbs typeof(NPinFrameworkAuditLoggingDomainModule), // diff --git a/src/NPin.SqlSugarCore/NPin.SqlSugarCore.csproj b/src/NPin.SqlSugarCore/NPin.SqlSugarCore.csproj index 52477d4..4abaa0a 100644 --- a/src/NPin.SqlSugarCore/NPin.SqlSugarCore.csproj +++ b/src/NPin.SqlSugarCore/NPin.SqlSugarCore.csproj @@ -11,6 +11,7 @@ + diff --git a/src/NPin.SqlSugarCore/NPinDbContext.cs b/src/NPin.SqlSugarCore/NPinDbContext.cs index 0ba18b9..bd91538 100644 --- a/src/NPin.SqlSugarCore/NPinDbContext.cs +++ b/src/NPin.SqlSugarCore/NPinDbContext.cs @@ -1,6 +1,11 @@ -namespace NPin.SqlSugarCore; +using NPin.Framework.Upms.SqlSugarCore; +using Volo.Abp.DependencyInjection; -public class NPinDbContext +namespace NPin.SqlSugarCore; + +public class NPinDbContext: NPinUpmsDbContext { - + public NPinDbContext(IAbpLazyServiceProvider lazyServiceProvider) : base(lazyServiceProvider) + { + } } \ No newline at end of file diff --git a/src/NPin.SqlSugarCore/NPinSqlSugarCoreModule.cs b/src/NPin.SqlSugarCore/NPinSqlSugarCoreModule.cs index bf90ce1..3af713a 100644 --- a/src/NPin.SqlSugarCore/NPinSqlSugarCoreModule.cs +++ b/src/NPin.SqlSugarCore/NPinSqlSugarCoreModule.cs @@ -3,12 +3,15 @@ using NPin.Framework.AuditLogging.SqlSugarCore; using NPin.Framework.Mapster; using NPin.Framework.SqlSugarCore; using NPin.Framework.TenantManagement.SqlSugarCore; +using NPin.Framework.Upms.SqlSugarCore; namespace NPin.SqlSugarCore; [DependsOn( typeof(NPinDomainModule), - // TODO rbac bbs codegen + typeof(NPinFrameworkUpmsSqlSugarCoreModule), + // TODO bbs + // TODO codegen typeof(NPinFrameworkAuditLoggingSqlSugarCoreModule), typeof(NPinFrameworkTenantManagementSqlSugarCoreModule), // @@ -19,7 +22,7 @@ public class NPinSqlSugarCoreModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { - // context.Services.AddNPinDbContext(); + context.Services.AddNPinDbContext(); // 默认不开放,可根据项目需要是否直接对外开放db // context.Services.AddTransient(x => x.GetRequiredService().SqlSugarClient); } diff --git a/src/NPin.Web/GeoLite2-City.mmdb b/src/NPin.Web/GeoLite2-City.mmdb new file mode 100644 index 0000000..f5e23dc Binary files /dev/null and b/src/NPin.Web/GeoLite2-City.mmdb differ