From e9f1ef5c01a29dbfea9bf0c0050115216f4a9b06 Mon Sep 17 00:00:00 2001
From: LemonNoCry <773596523@qq.com>
Date: Fri, 14 Apr 2023 10:43:47 +0800
Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20=E5=A2=9E=E5=8A=A0=E6=95=B0?=
=?UTF-8?q?=E6=8D=AE=E5=BA=93=E7=AE=A1=E7=90=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Blog.Core.Api/Blog.Core.Api.csproj | 1 +
Blog.Core.Api/Blog.Core.Model.xml | 5 +
Blog.Core.Api/Blog.Core.xml | 40 ++
.../Controllers/BaseApiController.cs | 155 +++----
.../Controllers/Systems/DataBaseController.cs | 188 +++++++++
Blog.Core.Common/Blog.Core.Common.csproj | 3 +
Blog.Core.Common/DB/BaseDBConfig.cs | 19 +-
Blog.Core.Common/DB/EntityUtility.cs | 53 +++
.../Extensions/DictionaryExtensions.cs | 18 +
.../Policys/PermissionHandler.cs | 391 +++++++++---------
.../ServiceExtensions/SqlsugarSetup.cs | 218 +++++-----
.../Systems/DataBase/DataBaseReadType.cs | 10 +
.../Systems/DataBase/DatabaseOutput.cs | 10 +
.../Systems/DataBase/DbColumnInfoOutput.cs | 36 ++
.../Systems/DataBase/EditColumnInput.cs | 9 +
.../Systems/DataBase/EditTableInput.cs | 10 +
16 files changed, 789 insertions(+), 377 deletions(-)
create mode 100644 Blog.Core.Api/Controllers/Systems/DataBaseController.cs
create mode 100644 Blog.Core.Common/DB/EntityUtility.cs
create mode 100644 Blog.Core.Common/Extensions/DictionaryExtensions.cs
create mode 100644 Blog.Core.Model/Systems/DataBase/DataBaseReadType.cs
create mode 100644 Blog.Core.Model/Systems/DataBase/DatabaseOutput.cs
create mode 100644 Blog.Core.Model/Systems/DataBase/DbColumnInfoOutput.cs
create mode 100644 Blog.Core.Model/Systems/DataBase/EditColumnInput.cs
create mode 100644 Blog.Core.Model/Systems/DataBase/EditTableInput.cs
diff --git a/Blog.Core.Api/Blog.Core.Api.csproj b/Blog.Core.Api/Blog.Core.Api.csproj
index f3f9bef..2f8c41e 100644
--- a/Blog.Core.Api/Blog.Core.Api.csproj
+++ b/Blog.Core.Api/Blog.Core.Api.csproj
@@ -106,6 +106,7 @@
+
diff --git a/Blog.Core.Api/Blog.Core.Model.xml b/Blog.Core.Api/Blog.Core.Model.xml
index 79666d2..1eb3423 100644
--- a/Blog.Core.Api/Blog.Core.Model.xml
+++ b/Blog.Core.Api/Blog.Core.Model.xml
@@ -1957,6 +1957,11 @@
找不到指定资源
+
+
+ 数据库读取类型
+
+
表格数据,支持分页
diff --git a/Blog.Core.Api/Blog.Core.xml b/Blog.Core.Api/Blog.Core.xml
index b47bbf5..51ffc1f 100644
--- a/Blog.Core.Api/Blog.Core.xml
+++ b/Blog.Core.Api/Blog.Core.xml
@@ -1299,6 +1299,46 @@
+
+
+ 数据库管理
+
+
+
+
+ 获取库配置
+
+
+
+
+
+ 获取表信息
+
+ 配置Id
+ 读取类型
+
+
+
+
+ 获取表字段
+
+ 表名
+ ConfigId
+ 读取类型
+
+
+
+
+ 编辑表备注
+
+
+
+
+
+ 编辑列备注
+
+
+
多租户-多库方案 测试
diff --git a/Blog.Core.Api/Controllers/BaseApiController.cs b/Blog.Core.Api/Controllers/BaseApiController.cs
index 5a374de..97a938e 100644
--- a/Blog.Core.Api/Controllers/BaseApiController.cs
+++ b/Blog.Core.Api/Controllers/BaseApiController.cs
@@ -4,82 +4,85 @@ using System.Collections.Generic;
namespace Blog.Core.Controllers
{
- public class BaseApiController : Controller
- {
- [NonAction]
- public MessageModel Success(T data, string msg = "成功")
- {
- return new MessageModel()
- {
- success = true,
- msg = msg,
- response = data,
- };
- }
- // [NonAction]
- //public MessageModel Success(T data, string msg = "成功",bool success = true)
- //{
- // return new MessageModel()
- // {
- // success = success,
- // msg = msg,
- // response = data,
- // };
- //}
- [NonAction]
- public MessageModel Success(string msg = "成功")
- {
- return new MessageModel()
- {
- success = true,
- msg = msg,
- response = null,
- };
- }
- [NonAction]
- public MessageModel Failed(string msg = "失败", int status = 500)
- {
- return new MessageModel()
- {
- success = false,
- status = status,
- msg = msg,
- response = null,
- };
- }
- [NonAction]
- public MessageModel Failed(string msg = "失败", int status = 500)
- {
- return new MessageModel()
- {
- success = false,
- status = status,
- msg = msg,
- response = default,
- };
- }
- [NonAction]
- public MessageModel> SuccessPage(int page, int dataCount, int pageSize, List data, int pageCount, string msg = "获取成功")
- {
+ public class BaseApiController : Controller
+ {
+ [NonAction]
+ public MessageModel Success(T data, string msg = "成功")
+ {
+ return new MessageModel()
+ {
+ success = true,
+ msg = msg,
+ response = data,
+ };
+ }
- return new MessageModel>()
- {
- success = true,
- msg = msg,
- response = new PageModel(page, dataCount, pageSize, data)
+ // [NonAction]
+ //public MessageModel Success(T data, string msg = "成功",bool success = true)
+ //{
+ // return new MessageModel()
+ // {
+ // success = success,
+ // msg = msg,
+ // response = data,
+ // };
+ //}
+ [NonAction]
+ public MessageModel Success(string msg = "成功")
+ {
+ return new MessageModel()
+ {
+ success = true,
+ msg = msg,
+ response = null,
+ };
+ }
+
+ [NonAction]
+ public MessageModel Failed(string msg = "失败", int status = 500)
+ {
+ return new MessageModel()
+ {
+ success = false,
+ status = status,
+ msg = msg,
+ response = null,
+ };
+ }
- };
- }
- [NonAction]
- public MessageModel> SuccessPage(PageModel pageModel, string msg = "获取成功")
- {
+ [NonAction]
+ public MessageModel Failed(string msg = "失败", int status = 500)
+ {
+ return new MessageModel()
+ {
+ success = false,
+ status = status,
+ msg = msg,
+ response = default,
+ };
+ }
- return new MessageModel>()
- {
- success = true,
- msg = msg,
- response = pageModel
- };
- }
- }
-}
+ [NonAction]
+ public MessageModel> SuccessPage(int page, int dataCount, int pageSize, List data,
+ int pageCount, string msg = "获取成功")
+ {
+ return new MessageModel>()
+ {
+ success = true,
+ msg = msg,
+ response = new PageModel(page, dataCount, pageSize, data)
+ };
+ }
+
+ [NonAction]
+ public MessageModel> SuccessPage(PageModel pageModel, string msg = "获取成功")
+ {
+ return new MessageModel>()
+ {
+ success = true,
+ msg = msg,
+ response = pageModel
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/Blog.Core.Api/Controllers/Systems/DataBaseController.cs b/Blog.Core.Api/Controllers/Systems/DataBaseController.cs
new file mode 100644
index 0000000..434bd19
--- /dev/null
+++ b/Blog.Core.Api/Controllers/Systems/DataBaseController.cs
@@ -0,0 +1,188 @@
+using System.Diagnostics.CodeAnalysis;
+using Blog.Core.Common;
+using Blog.Core.Common.DB;
+using Blog.Core.Controllers;
+using Blog.Core.Model;
+using Blog.Core.Model.Models;
+using Blog.Core.Model.Systems.DataBase;
+using Blog.Core.Model.Tenants;
+using Mapster;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using SqlSugar;
+
+namespace Blog.Core.Api.Controllers.Systems;
+
+///
+/// 数据库管理
+///
+[Route("api/Systems/[controller]/[action]")]
+[ApiController]
+[Authorize(Permissions.Name)]
+public class DataBaseController : BaseApiController
+{
+ private readonly ISqlSugarClient _db;
+
+ public DataBaseController(ISqlSugarClient db)
+ {
+ _db = db;
+ }
+
+ [return: NotNull]
+ public ISqlSugarClient GetTenantDb(string configId)
+ {
+ if (!_db.AsTenant().IsAnyConnection(configId))
+ {
+ var tenant = _db.Queryable().WithCache()
+ .Where(s => s.TenantType == TenantTypeEnum.Db)
+ .Where(s => s.ConfigId == configId)
+ .First();
+ if (tenant != null)
+ {
+ _db.AsTenant().AddConnection(tenant.GetConnectionConfig());
+ }
+ }
+
+ var db = _db.AsTenant().GetConnectionScope(configId);
+ if (db is null)
+ {
+ throw new ApplicationException("无效的数据库配置");
+ }
+
+ return db;
+ }
+
+ ///
+ /// 获取库配置
+ ///
+ ///
+ [HttpGet]
+ public async Task>> GetAllConfig()
+ {
+ //增加多租户的连接
+ var allConfigs = new List(BaseDBConfig.AllConfigs);
+ var tenants = await _db.Queryable().WithCache()
+ .Where(s => s.TenantType == TenantTypeEnum.Db)
+ .ToListAsync();
+ if (tenants.Any())
+ {
+ allConfigs.AddRange(tenants.Select(tenant => tenant.GetConnectionConfig()));
+ }
+
+ var configs = await Task.FromResult(allConfigs);
+ return Success(configs.Adapt>());
+ }
+
+ ///
+ /// 获取表信息
+ ///
+ /// 配置Id
+ /// 读取类型
+ ///
+ [HttpGet]
+ public MessageModel> GetTableInfoList(string configId,
+ DataBaseReadType readType = DataBaseReadType.Db)
+ {
+ if (configId.IsNullOrEmpty())
+ {
+ configId = MainDb.CurrentDbConnId;
+ }
+
+ var provider = GetTenantDb(configId);
+ List data = null;
+ switch (readType)
+ {
+ case DataBaseReadType.Db:
+ data = provider.DbMaintenance.GetTableInfoList(false);
+ break;
+ case DataBaseReadType.Entity:
+ if (EntityUtility.TenantEntitys.TryGetValue(configId, out var types))
+ {
+ data = types.Select(s => provider.EntityMaintenance.GetEntityInfo(s))
+ .Select(s => new {Name = s.DbTableName, Description = s.TableDescription})
+ .Adapt>();
+ }
+
+ break;
+ }
+
+
+ return Success(data);
+ }
+
+ ///
+ /// 获取表字段
+ ///
+ /// 表名
+ /// ConfigId
+ /// 读取类型
+ ///
+ [HttpGet]
+ public MessageModel> GetColumnInfosByTableName(string tableName, string configId = null,
+ DataBaseReadType readType = DataBaseReadType.Db)
+ {
+ if (string.IsNullOrWhiteSpace(tableName))
+ return Failed>("表名不能为空");
+
+ if (configId.IsNullOrEmpty())
+ {
+ configId = MainDb.CurrentDbConnId;
+ }
+
+ List data = null;
+ var provider = GetTenantDb(configId);
+ switch (readType)
+ {
+ case DataBaseReadType.Db:
+ data = provider.DbMaintenance.GetColumnInfosByTableName(tableName, false)
+ .Adapt>();
+ break;
+ case DataBaseReadType.Entity:
+ if (EntityUtility.TenantEntitys.TryGetValue(configId, out var types))
+ {
+ var type = types.FirstOrDefault(s => s.Name == tableName);
+ data = provider.EntityMaintenance.GetEntityInfo(type).Columns.Adapt>();
+ }
+
+ break;
+ }
+
+
+ return Success(data);
+ }
+
+ ///
+ /// 编辑表备注
+ ///
+ ///
+ [HttpPut]
+ public MessageModel PutTableEditRemark([FromBody] EditTableInput input)
+ {
+ var provider = GetTenantDb(input.ConfigId);
+ if (provider.DbMaintenance.IsAnyTableRemark(input.TableName))
+ {
+ provider.DbMaintenance.DeleteTableRemark(input.TableName);
+ }
+
+ provider.DbMaintenance.AddTableRemark(input.TableName, input.Description);
+ return Success();
+ }
+
+ ///
+ /// 编辑列备注
+ ///
+ ///
+ [HttpPut]
+ public MessageModel PutColumnEditRemark([FromBody] EditColumnInput input)
+ {
+ var provider = GetTenantDb(input.ConfigId);
+ if (provider.DbMaintenance.IsAnyColumnRemark(input.DbColumnName, input.TableName))
+ {
+ provider.DbMaintenance.DeleteColumnRemark(input.DbColumnName, input.TableName);
+ }
+
+ provider.DbMaintenance.AddColumnRemark(input.DbColumnName, input.TableName, input.ColumnDescription);
+
+ return Success();
+ }
+}
\ No newline at end of file
diff --git a/Blog.Core.Common/Blog.Core.Common.csproj b/Blog.Core.Common/Blog.Core.Common.csproj
index 0662bac..aeba847 100644
--- a/Blog.Core.Common/Blog.Core.Common.csproj
+++ b/Blog.Core.Common/Blog.Core.Common.csproj
@@ -18,6 +18,9 @@
+
+
+
diff --git a/Blog.Core.Common/DB/BaseDBConfig.cs b/Blog.Core.Common/DB/BaseDBConfig.cs
index 313b8a0..495414f 100644
--- a/Blog.Core.Common/DB/BaseDBConfig.cs
+++ b/Blog.Core.Common/DB/BaseDBConfig.cs
@@ -3,18 +3,23 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using SqlSugar;
namespace Blog.Core.Common.DB
{
public class BaseDBConfig
{
+ public static readonly List AllConfigs = new(); //所有库配置
+ public static readonly List AllSlaveConfigs = new(); //从库配置
+ public static List ValidConfig = new(); //有效的库连接(除去Log库)
+ public static ConnectionConfig LogConfig; //日志库
+
/* 之前的单库操作已经删除,如果想要之前的代码,可以查看我的GitHub的历史记录
* 目前是多库操作,默认加载的是appsettings.json设置为true的第一个db连接。
*/
public static (List allDbs, List slaveDbs) MutiConnectionString => MutiInitConn();
- public static List AllConfig = new(); //所有的库连接
- public static List ValidConfig = new(); //有效的库连接(除去Log库)
- public static ConnectionConfig LogConfig; //日志库
+
+
private static string DifDBConnOfSecurity(params string[] conn)
{
@@ -54,7 +59,7 @@ namespace Blog.Core.Common.DB
List listdatabaseSlaveDB = new List(); //从库
// 单库,且不开启读写分离,只保留一个
- if (!AppSettings.app(new string[] { "CQRSEnabled" }).ObjToBool() && !AppSettings.app(new string[] { "MutiDBEnabled" }).ObjToBool())
+ if (!AppSettings.app(new string[] {"CQRSEnabled"}).ObjToBool() && !AppSettings.app(new string[] {"MutiDBEnabled"}).ObjToBool())
{
if (listdatabase.Count == 1)
{
@@ -62,7 +67,7 @@ namespace Blog.Core.Common.DB
}
else
{
- var dbFirst = listdatabase.FirstOrDefault(d => d.ConnId == AppSettings.app(new string[] { "MainDB" }).ObjToString());
+ var dbFirst = listdatabase.FirstOrDefault(d => d.ConnId == AppSettings.app(new string[] {"MainDB"}).ObjToString());
if (dbFirst == null)
{
dbFirst = listdatabase.FirstOrDefault();
@@ -75,11 +80,11 @@ namespace Blog.Core.Common.DB
// 读写分离,且必须是单库模式,获取从库
- if (AppSettings.app(new string[] { "CQRSEnabled" }).ObjToBool() && !AppSettings.app(new string[] { "MutiDBEnabled" }).ObjToBool())
+ if (AppSettings.app(new string[] {"CQRSEnabled"}).ObjToBool() && !AppSettings.app(new string[] {"MutiDBEnabled"}).ObjToBool())
{
if (listdatabase.Count > 1)
{
- listdatabaseSlaveDB = listdatabase.Where(d => d.ConnId != AppSettings.app(new string[] { "MainDB" }).ObjToString()).ToList();
+ listdatabaseSlaveDB = listdatabase.Where(d => d.ConnId != AppSettings.app(new string[] {"MainDB"}).ObjToString()).ToList();
}
}
diff --git a/Blog.Core.Common/DB/EntityUtility.cs b/Blog.Core.Common/DB/EntityUtility.cs
new file mode 100644
index 0000000..f997a1e
--- /dev/null
+++ b/Blog.Core.Common/DB/EntityUtility.cs
@@ -0,0 +1,53 @@
+using Blog.Core.Common.Extensions;
+using Blog.Core.Model;
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Reflection;
+
+namespace Blog.Core.Common.DB;
+
+public class EntityUtility
+{
+ private static readonly Lazy>> _tenantEntitys = new(() =>
+ {
+ Dictionary> dic = new Dictionary>();
+ var assembly = Assembly.Load("Blog.Core.Model");
+ //扫描 实体
+ foreach (var type in assembly.GetTypes().Where(s => s.IsClass && !s.IsAbstract))
+ {
+ var tenant = type.GetCustomAttribute();
+ if (tenant != null)
+ {
+ dic.TryAdd(tenant.configId.ToString(), type);
+ continue;
+ }
+
+ if (type.IsSubclassOf(typeof(RootEntityTkey<>)))
+ {
+ dic.TryAdd(MainDb.CurrentDbConnId, type);
+ continue;
+ }
+
+ var table = type.GetCustomAttribute();
+ if (table != null)
+ {
+ dic.TryAdd(MainDb.CurrentDbConnId, type);
+ continue;
+ }
+
+ Debug.Assert(type.Namespace != null, "type.Namespace != null");
+ if (type.Namespace.StartsWith("Blog.Core.Model.Models"))
+ {
+ dic.TryAdd(MainDb.CurrentDbConnId, type);
+ continue;
+ }
+ }
+
+ return dic;
+ });
+
+ public static Dictionary> TenantEntitys => _tenantEntitys.Value;
+}
\ No newline at end of file
diff --git a/Blog.Core.Common/Extensions/DictionaryExtensions.cs b/Blog.Core.Common/Extensions/DictionaryExtensions.cs
new file mode 100644
index 0000000..ffcf910
--- /dev/null
+++ b/Blog.Core.Common/Extensions/DictionaryExtensions.cs
@@ -0,0 +1,18 @@
+using System.Collections.Generic;
+
+namespace Blog.Core.Common.Extensions;
+
+public static class DictionaryExtensions
+{
+ public static void TryAdd(this IDictionary> dic, TKey key, TValue value)
+ {
+ if (dic.TryGetValue(key, out var old))
+ {
+ old.Add(value);
+ }
+ else
+ {
+ dic.Add(key, new List {value});
+ }
+ }
+}
\ No newline at end of file
diff --git a/Blog.Core.Extensions/Authorizations/Policys/PermissionHandler.cs b/Blog.Core.Extensions/Authorizations/Policys/PermissionHandler.cs
index 121b9b1..23627cc 100644
--- a/Blog.Core.Extensions/Authorizations/Policys/PermissionHandler.cs
+++ b/Blog.Core.Extensions/Authorizations/Policys/PermissionHandler.cs
@@ -14,212 +14,229 @@ using System.Linq;
using System.Security.Claims;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
+using Blog.Core.Model.Models;
namespace Blog.Core.AuthHelper
{
- ///
- /// 权限授权处理器
- ///
- public class PermissionHandler : AuthorizationHandler
- {
- ///
- /// 验证方案提供对象
- ///
- public IAuthenticationSchemeProvider Schemes { get; set; }
+ ///
+ /// 权限授权处理器
+ ///
+ public class PermissionHandler : AuthorizationHandler
+ {
+ ///
+ /// 验证方案提供对象
+ ///
+ public IAuthenticationSchemeProvider Schemes { get; set; }
- private readonly IRoleModulePermissionServices _roleModulePermissionServices;
- private readonly IHttpContextAccessor _accessor;
- private readonly ISysUserInfoServices _userServices;
- private readonly IUser _user;
+ private readonly IRoleModulePermissionServices _roleModulePermissionServices;
+ private readonly IHttpContextAccessor _accessor;
+ private readonly ISysUserInfoServices _userServices;
+ private readonly IUser _user;
- ///
- /// 构造函数注入
- ///
- ///
- ///
- ///
- ///
- ///
- public PermissionHandler(IAuthenticationSchemeProvider schemes, IRoleModulePermissionServices roleModulePermissionServices, IHttpContextAccessor accessor, ISysUserInfoServices userServices, IUser user)
- {
- _accessor = accessor;
- _userServices = userServices;
- _user = user;
- Schemes = schemes;
- _roleModulePermissionServices = roleModulePermissionServices;
- }
+ ///
+ /// 构造函数注入
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public PermissionHandler(IAuthenticationSchemeProvider schemes,
+ IRoleModulePermissionServices roleModulePermissionServices, IHttpContextAccessor accessor,
+ ISysUserInfoServices userServices, IUser user)
+ {
+ _accessor = accessor;
+ _userServices = userServices;
+ _user = user;
+ Schemes = schemes;
+ _roleModulePermissionServices = roleModulePermissionServices;
+ }
- // 重写异步处理程序
- protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
- {
- var httpContext = _accessor.HttpContext;
+ // 重写异步处理程序
+ protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context,
+ PermissionRequirement requirement)
+ {
+ var httpContext = _accessor.HttpContext;
- // 获取系统中所有的角色和菜单的关系集合
- if (!requirement.Permissions.Any())
- {
- var data = await _roleModulePermissionServices.RoleModuleMaps();
- var list = new List();
- // ids4和jwt切换
- // ids4
- if (Permissions.IsUseIds4)
- {
- list = (from item in data
- where item.IsDeleted == false
- orderby item.Id
- select new PermissionItem
- {
- Url = item.Module?.LinkUrl,
- Role = item.Role?.Id.ObjToString(),
- }).ToList();
- }
- // jwt
- else
- {
- list = (from item in data
- where item.IsDeleted == false
- orderby item.Id
- select new PermissionItem
- {
- Url = item.Module?.LinkUrl,
- Role = item.Role?.Name.ObjToString(),
- }).ToList();
- }
+ // 获取系统中所有的角色和菜单的关系集合
+ if (!requirement.Permissions.Any())
+ {
+ var data = await _roleModulePermissionServices.RoleModuleMaps();
+ var list = new List();
+ // ids4和jwt切换
+ // ids4
+ if (Permissions.IsUseIds4)
+ {
+ list = (from item in data
+ where item.IsDeleted == false
+ orderby item.Id
+ select new PermissionItem
+ {
+ Url = item.Module?.LinkUrl,
+ Role = item.Role?.Id.ObjToString(),
+ }).ToList();
+ }
+ // jwt
+ else
+ {
+ list = (from item in data
+ where item.IsDeleted == false
+ orderby item.Id
+ select new PermissionItem
+ {
+ Url = item.Module?.LinkUrl,
+ Role = item.Role?.Name.ObjToString(),
+ }).ToList();
+ }
- requirement.Permissions = list;
- }
+ requirement.Permissions = list;
+ }
- if (httpContext != null)
- {
- var questUrl = httpContext.Request.Path.Value.ToLower();
+ if (httpContext != null)
+ {
+ var questUrl = httpContext.Request.Path.Value.ToLower();
- // 整体结构类似认证中间件UseAuthentication的逻辑,具体查看开源地址
- // https://github.com/dotnet/aspnetcore/blob/master/src/Security/Authentication/Core/src/AuthenticationMiddleware.cs
- httpContext.Features.Set(new AuthenticationFeature
- {
- OriginalPath = httpContext.Request.Path,
- OriginalPathBase = httpContext.Request.PathBase
- });
+ // 整体结构类似认证中间件UseAuthentication的逻辑,具体查看开源地址
+ // https://github.com/dotnet/aspnetcore/blob/master/src/Security/Authentication/Core/src/AuthenticationMiddleware.cs
+ httpContext.Features.Set(new AuthenticationFeature
+ {
+ OriginalPath = httpContext.Request.Path,
+ OriginalPathBase = httpContext.Request.PathBase
+ });
- // Give any IAuthenticationRequestHandler schemes a chance to handle the request
- // 主要作用是: 判断当前是否需要进行远程验证,如果是就进行远程验证
- var handlers = httpContext.RequestServices.GetRequiredService();
- foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync())
- {
- if (await handlers.GetHandlerAsync(httpContext, scheme.Name) is IAuthenticationRequestHandler handler && await handler.HandleRequestAsync())
- {
- context.Fail();
- return;
- }
- }
+ // Give any IAuthenticationRequestHandler schemes a chance to handle the request
+ // 主要作用是: 判断当前是否需要进行远程验证,如果是就进行远程验证
+ var handlers = httpContext.RequestServices.GetRequiredService();
+ foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync())
+ {
+ if (await handlers.GetHandlerAsync(httpContext, scheme.Name) is IAuthenticationRequestHandler
+ handler && await handler.HandleRequestAsync())
+ {
+ context.Fail();
+ return;
+ }
+ }
+
+ //判断请求是否拥有凭据,即有没有登录
+ var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
+ if (defaultAuthenticate != null)
+ {
+ var result = await httpContext.AuthenticateAsync(defaultAuthenticate.Name);
+ // 是否开启测试环境
+ var isTestCurrent = AppSettings.app(new string[] {"AppSettings", "UseLoadTest"}).ObjToBool();
- //判断请求是否拥有凭据,即有没有登录
- var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
- if (defaultAuthenticate != null)
- {
- var result = await httpContext.AuthenticateAsync(defaultAuthenticate.Name);
+ //result?.Principal不为空即登录成功
+ if (result?.Principal != null || isTestCurrent)
+ {
+ if (!isTestCurrent) httpContext.User = result.Principal;
- // 是否开启测试环境
- var isTestCurrent = AppSettings.app(new string[] { "AppSettings", "UseLoadTest" }).ObjToBool();
+ // 获取当前用户的角色信息
+ var currentUserRoles = new List();
+ // ids4和jwt切换
+ // ids4
+ if (Permissions.IsUseIds4)
+ {
+ currentUserRoles = (from item in httpContext.User.Claims
+ where item.Type == "role"
+ select item.Value).ToList();
+ }
+ else
+ {
+ // jwt
+ currentUserRoles = (from item in httpContext.User.Claims
+ where item.Type == requirement.ClaimType
+ select item.Value).ToList();
+ }
- //result?.Principal不为空即登录成功
- if (result?.Principal != null || isTestCurrent)
- {
- if (!isTestCurrent) httpContext.User = result.Principal;
+ //超级管理员 默认拥有所有权限
+ if (currentUserRoles.All(s => s != "SuperAdmin"))
+ {
+ var isMatchRole = false;
+ var permisssionRoles = requirement.Permissions.Where(w => currentUserRoles.Contains(w.Role));
+ foreach (var item in permisssionRoles)
+ {
+ try
+ {
+ if (Regex.Match(questUrl, item.Url?.ObjToString().ToLower())?.Value == questUrl)
+ {
+ isMatchRole = true;
+ break;
+ }
+ }
+ catch (Exception)
+ {
+ // ignored
+ }
+ }
- // 获取当前用户的角色信息
- var currentUserRoles = new List();
- // ids4和jwt切换
- // ids4
- if (Permissions.IsUseIds4)
- {
- currentUserRoles = (from item in httpContext.User.Claims
- where item.Type == "role"
- select item.Value).ToList();
- }
- else
- {
- // jwt
- currentUserRoles = (from item in httpContext.User.Claims
- where item.Type == requirement.ClaimType
- select item.Value).ToList();
- }
+ //验证权限
+ if (currentUserRoles.Count <= 0 || !isMatchRole)
+ {
+ context.Fail();
+ return;
+ }
+ }
+
+ // 判断token是否过期,过期则重新登录
+ var isExp = false;
+ // ids4和jwt切换
+ // ids4
+ if (Permissions.IsUseIds4)
+ {
+ isExp = (httpContext.User.Claims.SingleOrDefault(s => s.Type == "exp")?.Value) != null &&
+ DateHelper.StampToDateTime(httpContext.User.Claims
+ .SingleOrDefault(s => s.Type == "exp")?.Value) >= DateTime.Now;
+ }
+ else
+ {
+ // jwt
+ isExp =
+ (httpContext.User.Claims.SingleOrDefault(s => s.Type == ClaimTypes.Expiration)
+ ?.Value) != null &&
+ DateTime.Parse(httpContext.User.Claims
+ .SingleOrDefault(s => s.Type == ClaimTypes.Expiration)?.Value) >= DateTime.Now;
+ }
- var isMatchRole = false;
- var permisssionRoles = requirement.Permissions.Where(w => currentUserRoles.Contains(w.Role));
- foreach (var item in permisssionRoles)
- {
- try
- {
- if (Regex.Match(questUrl, item.Url?.ObjToString().ToLower())?.Value == questUrl)
- {
- isMatchRole = true;
- break;
- }
- }
- catch (Exception)
- {
- // ignored
- }
- }
+ if (!isExp)
+ {
+ context.Fail(new AuthorizationFailureReason(this, "授权已过期,请重新授权"));
+ return;
+ }
- //验证权限
- if (currentUserRoles.Count <= 0 || !isMatchRole)
- {
- context.Fail();
- return;
- }
+ //校验签发时间
+ if (!Permissions.IsUseIds4)
+ {
+ var value = httpContext.User.Claims
+ .SingleOrDefault(s => s.Type == JwtRegisteredClaimNames.Iat)?.Value;
+ if (value != null)
+ {
+ var user = await _userServices.QueryById(_user.ID, true);
+ if (user.CriticalModifyTime > value.ObjToDate())
+ {
+ _user.MessageModel = new ApiResponse(StatusCode.CODE401, "很抱歉,授权已失效,请重新授权")
+ .MessageModel;
+ context.Fail(new AuthorizationFailureReason(this, _user.MessageModel.msg));
+ return;
+ }
+ }
+ }
- // 判断token是否过期,过期则重新登录
- var isExp = false;
- // ids4和jwt切换
- // ids4
- if (Permissions.IsUseIds4)
- {
- isExp = (httpContext.User.Claims.SingleOrDefault(s => s.Type == "exp")?.Value) != null && DateHelper.StampToDateTime(httpContext.User.Claims.SingleOrDefault(s => s.Type == "exp")?.Value) >= DateTime.Now;
- }
- else
- {
- // jwt
- isExp = (httpContext.User.Claims.SingleOrDefault(s => s.Type == ClaimTypes.Expiration)?.Value) != null && DateTime.Parse(httpContext.User.Claims.SingleOrDefault(s => s.Type == ClaimTypes.Expiration)?.Value) >= DateTime.Now;
- }
+ context.Succeed(requirement);
+ return;
+ }
+ }
- if (!isExp)
- {
- context.Fail(new AuthorizationFailureReason(this, "授权已过期,请重新授权"));
- return;
- }
+ //判断没有登录时,是否访问登录的url,并且是Post请求,并且是form表单提交类型,否则为失败
+ if (!(questUrl.Equals(requirement.LoginPath.ToLower(), StringComparison.Ordinal) &&
+ (!httpContext.Request.Method.Equals("POST") || !httpContext.Request.HasFormContentType)))
+ {
+ context.Fail();
+ return;
+ }
+ }
- //校验签发时间
- if (!Permissions.IsUseIds4)
- {
- var value = httpContext.User.Claims.SingleOrDefault(s => s.Type == JwtRegisteredClaimNames.Iat)?.Value;
- if (value != null)
- {
- var user = await _userServices.QueryById(_user.ID, true);
- if (user.CriticalModifyTime > value.ObjToDate())
- {
- _user.MessageModel = new ApiResponse(StatusCode.CODE401, "很抱歉,授权已失效,请重新授权").MessageModel;
- context.Fail(new AuthorizationFailureReason(this, _user.MessageModel.msg));
- return;
- }
- }
- }
-
- context.Succeed(requirement);
- return;
- }
- }
-
- //判断没有登录时,是否访问登录的url,并且是Post请求,并且是form表单提交类型,否则为失败
- if (!(questUrl.Equals(requirement.LoginPath.ToLower(), StringComparison.Ordinal) && (!httpContext.Request.Method.Equals("POST") || !httpContext.Request.HasFormContentType)))
- {
- context.Fail();
- return;
- }
- }
-
- //context.Succeed(requirement);
- }
- }
+ //context.Succeed(requirement);
+ }
+ }
}
\ No newline at end of file
diff --git a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs
index 83dff77..4ac2d2e 100644
--- a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs
+++ b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs
@@ -13,126 +13,130 @@ using System.Threading.Tasks;
namespace Blog.Core.Extensions
{
- ///
- /// SqlSugar 启动服务
- ///
- public static class SqlsugarSetup
- {
- private static readonly MemoryCache Cache = new MemoryCache(new MemoryCacheOptions());
+ ///
+ /// SqlSugar 启动服务
+ ///
+ public static class SqlsugarSetup
+ {
+ private static readonly MemoryCache Cache = new MemoryCache(new MemoryCacheOptions());
- public static void AddSqlsugarSetup(this IServiceCollection services)
- {
- if (services == null) throw new ArgumentNullException(nameof(services));
+ public static void AddSqlsugarSetup(this IServiceCollection services)
+ {
+ if (services == null) throw new ArgumentNullException(nameof(services));
- // 默认添加主数据库连接
- MainDb.CurrentDbConnId = AppSettings.app(new string[] { "MainDB" });
+ // 默认添加主数据库连接
+ MainDb.CurrentDbConnId = AppSettings.app(new string[] {"MainDB"});
- // SqlSugarScope是线程安全,可使用单例注入
- // 参考:https://www.donet5.com/Home/Doc?typeId=1181
- services.AddSingleton(o =>
- {
- var memoryCache = o.GetRequiredService();
+ BaseDBConfig.MutiConnectionString.slaveDbs.ForEach(s =>
+ {
+ BaseDBConfig.AllSlaveConfigs.Add(new SlaveConnectionConfig()
+ {
+ HitRate = s.HitRate,
+ ConnectionString = s.Connection
+ });
+ });
- // 从库
- var listConfig_Slave = new List();
- BaseDBConfig.MutiConnectionString.slaveDbs.ForEach(s =>
- {
- listConfig_Slave.Add(new SlaveConnectionConfig()
- {
- HitRate = s.HitRate,
- ConnectionString = s.Connection
- });
- });
+ BaseDBConfig.MutiConnectionString.allDbs.ForEach(m =>
+ {
+ var config = new ConnectionConfig()
+ {
+ ConfigId = m.ConnId.ObjToString().ToLower(),
+ ConnectionString = m.Connection,
+ DbType = (DbType) m.DbType,
+ IsAutoCloseConnection = true,
+ // Check out more information: https://github.com/anjoy8/Blog.Core/issues/122
+ //IsShardSameThread = false,
+ MoreSettings = new ConnMoreSettings()
+ {
+ //IsWithNoLockQuery = true,
+ IsAutoRemoveDataCache = true,
+ SqlServerCodeFirstNvarchar = true,
+ },
+ // 从库
+ SlaveConnectionConfigs = BaseDBConfig.AllSlaveConfigs,
+ // 自定义特性
+ ConfigureExternalServices = new ConfigureExternalServices()
+ {
+ EntityService = (property, column) =>
+ {
+ if (column.IsPrimarykey && property.PropertyType == typeof(int))
+ {
+ column.IsIdentity = true;
+ }
+ }
+ },
+ InitKeyType = InitKeyType.Attribute
+ };
+ if (SqlSugarConst.LogConfigId.ToLower().Equals(m.ConnId.ToLower()))
+ {
+ BaseDBConfig.LogConfig = config;
+ }
+ else
+ {
+ BaseDBConfig.ValidConfig.Add(config);
+ }
- BaseDBConfig.MutiConnectionString.allDbs.ForEach(m =>
- {
- var config = new ConnectionConfig()
- {
- ConfigId = m.ConnId.ObjToString().ToLower(),
- ConnectionString = m.Connection,
- DbType = (DbType)m.DbType,
- IsAutoCloseConnection = true,
- // Check out more information: https://github.com/anjoy8/Blog.Core/issues/122
- //IsShardSameThread = false,
- MoreSettings = new ConnMoreSettings()
- {
- //IsWithNoLockQuery = true,
- IsAutoRemoveDataCache = true,
- SqlServerCodeFirstNvarchar = true,
- },
- // 从库
- SlaveConnectionConfigs = listConfig_Slave,
- // 自定义特性
- ConfigureExternalServices = new ConfigureExternalServices()
- {
- DataInfoCacheService = new SqlSugarMemoryCacheService(memoryCache),
- EntityService = (property, column) =>
- {
- if (column.IsPrimarykey && property.PropertyType == typeof(int))
- {
- column.IsIdentity = true;
- }
- }
- },
- InitKeyType = InitKeyType.Attribute
- };
- if (SqlSugarConst.LogConfigId.ToLower().Equals(m.ConnId.ToLower()))
- {
- BaseDBConfig.LogConfig = config;
- }
- else
- {
- BaseDBConfig.ValidConfig.Add(config);
- }
+ BaseDBConfig.AllConfigs.Add(config);
+ });
- BaseDBConfig.AllConfig.Add(config);
- });
+ if (BaseDBConfig.LogConfig is null)
+ {
+ throw new ApplicationException("未配置Log库连接");
+ }
- if (BaseDBConfig.LogConfig is null)
- {
- throw new ApplicationException("未配置Log库连接");
- }
- return new SqlSugarScope(BaseDBConfig.AllConfig, db =>
- {
- BaseDBConfig.ValidConfig.ForEach(config =>
- {
- var dbProvider = db.GetConnectionScope((string)config.ConfigId);
+ // SqlSugarScope是线程安全,可使用单例注入
+ // 参考:https://www.donet5.com/Home/Doc?typeId=1181
+ services.AddSingleton(o =>
+ {
+ var memoryCache = o.GetRequiredService();
- // 打印SQL语句
- dbProvider.Aop.OnLogExecuting = (s, parameters) => SqlSugarAop.OnLogExecuting(dbProvider, s, parameters, config);
+ foreach (var config in BaseDBConfig.AllConfigs)
+ {
+ config.ConfigureExternalServices.DataInfoCacheService = new SqlSugarMemoryCacheService(memoryCache);
+ }
- // 数据审计
- dbProvider.Aop.DataExecuting = SqlSugarAop.DataExecuting;
+ return new SqlSugarScope(BaseDBConfig.AllConfigs, db =>
+ {
+ BaseDBConfig.ValidConfig.ForEach(config =>
+ {
+ var dbProvider = db.GetConnectionScope((string) config.ConfigId);
- // 配置实体假删除过滤器
- RepositorySetting.SetDeletedEntityFilter(dbProvider);
- // 配置实体数据权限
- RepositorySetting.SetTenantEntityFilter(dbProvider);
- });
- });
- });
- }
+ // 打印SQL语句
+ dbProvider.Aop.OnLogExecuting = (s, parameters) =>
+ SqlSugarAop.OnLogExecuting(dbProvider, s, parameters, config);
- private static string GetWholeSql(SugarParameter[] paramArr, string sql)
- {
- foreach (var param in paramArr)
- {
- sql.Replace(param.ParameterName, param.Value.ObjToString());
- }
+ // 数据审计
+ dbProvider.Aop.DataExecuting = SqlSugarAop.DataExecuting;
- return sql;
- }
+ // 配置实体假删除过滤器
+ RepositorySetting.SetDeletedEntityFilter(dbProvider);
+ // 配置实体数据权限
+ RepositorySetting.SetTenantEntityFilter(dbProvider);
+ });
+ });
+ });
+ }
- private static string GetParas(SugarParameter[] pars)
- {
- string key = "【SQL参数】:";
- foreach (var param in pars)
- {
- key += $"{param.ParameterName}:{param.Value}\n";
- }
+ private static string GetWholeSql(SugarParameter[] paramArr, string sql)
+ {
+ foreach (var param in paramArr)
+ {
+ sql.Replace(param.ParameterName, param.Value.ObjToString());
+ }
- return key;
- }
- }
+ return sql;
+ }
+
+ private static string GetParas(SugarParameter[] pars)
+ {
+ string key = "【SQL参数】:";
+ foreach (var param in pars)
+ {
+ key += $"{param.ParameterName}:{param.Value}\n";
+ }
+
+ return key;
+ }
+ }
}
\ No newline at end of file
diff --git a/Blog.Core.Model/Systems/DataBase/DataBaseReadType.cs b/Blog.Core.Model/Systems/DataBase/DataBaseReadType.cs
new file mode 100644
index 0000000..f2ae1ed
--- /dev/null
+++ b/Blog.Core.Model/Systems/DataBase/DataBaseReadType.cs
@@ -0,0 +1,10 @@
+namespace Blog.Core.Model.Systems.DataBase;
+
+///
+/// 数据库读取类型
+///
+public enum DataBaseReadType
+{
+ Db,
+ Entity
+}
\ No newline at end of file
diff --git a/Blog.Core.Model/Systems/DataBase/DatabaseOutput.cs b/Blog.Core.Model/Systems/DataBase/DatabaseOutput.cs
new file mode 100644
index 0000000..8cefaeb
--- /dev/null
+++ b/Blog.Core.Model/Systems/DataBase/DatabaseOutput.cs
@@ -0,0 +1,10 @@
+using SqlSugar;
+
+namespace Blog.Core.Model.Systems.DataBase;
+
+public class DatabaseOutput
+{
+ public string ConfigId { get; set; }
+
+ public DbType DbType { get; set; }
+}
\ No newline at end of file
diff --git a/Blog.Core.Model/Systems/DataBase/DbColumnInfoOutput.cs b/Blog.Core.Model/Systems/DataBase/DbColumnInfoOutput.cs
new file mode 100644
index 0000000..6cada2f
--- /dev/null
+++ b/Blog.Core.Model/Systems/DataBase/DbColumnInfoOutput.cs
@@ -0,0 +1,36 @@
+namespace Blog.Core.Model.Systems.DataBase;
+
+public class DbColumnInfoOutput
+{
+ public string TableName { get; set; }
+
+ public int TableId { get; set; }
+
+ public string DbColumnName { get; set; }
+
+ public string PropertyName { get; set; }
+
+ public string DataType { get; set; }
+
+ public int Length { get; set; }
+
+ public string ColumnDescription { get; set; }
+
+ public string DefaultValue { get; set; }
+
+ public bool IsNullable { get; set; }
+
+ public bool IsIdentity { get; set; }
+
+ public bool IsPrimarykey { get; set; }
+
+ public object Value { get; set; }
+
+ public int DecimalDigits { get; set; }
+
+ public int Scale { get; set; }
+
+ public bool IsArray { get; set; }
+
+ internal bool IsJson { get; set; }
+}
\ No newline at end of file
diff --git a/Blog.Core.Model/Systems/DataBase/EditColumnInput.cs b/Blog.Core.Model/Systems/DataBase/EditColumnInput.cs
new file mode 100644
index 0000000..88c6697
--- /dev/null
+++ b/Blog.Core.Model/Systems/DataBase/EditColumnInput.cs
@@ -0,0 +1,9 @@
+namespace Blog.Core.Model.Systems.DataBase;
+
+public class EditColumnInput
+{
+ public string ConfigId { get; set; }
+ public string TableName { get; set; }
+ public string DbColumnName { get; set; }
+ public string ColumnDescription { get; set; }
+}
\ No newline at end of file
diff --git a/Blog.Core.Model/Systems/DataBase/EditTableInput.cs b/Blog.Core.Model/Systems/DataBase/EditTableInput.cs
new file mode 100644
index 0000000..496a6dd
--- /dev/null
+++ b/Blog.Core.Model/Systems/DataBase/EditTableInput.cs
@@ -0,0 +1,10 @@
+namespace Blog.Core.Model.Systems.DataBase;
+
+public class EditTableInput
+{
+ public string ConfigId { get; set; }
+
+ public string TableName { get; set; }
+
+ public string Description { get; set; }
+}
\ No newline at end of file