From d09de26d6ea095c626bdfa623a23d91d1902aae8 Mon Sep 17 00:00:00 2001 From: hudingwen <765472804@qq.com> Date: Sun, 12 Feb 2023 22:00:04 +0800 Subject: [PATCH 01/84] =?UTF-8?q?=E5=90=8C=E6=AD=A5=E4=B8=BB=E5=88=86?= =?UTF-8?q?=E6=94=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Api/Blog.Core.Model.xml | 137 ++++++ Blog.Core.Api/Blog.Core.xml | 160 +++++++ Blog.Core.Api/Controllers/TrojanController.cs | 451 ++++++++++++++++++ Blog.Core.Api/Controllers/WeChatController.cs | 12 + Blog.Core.Api/Dockerfile | 2 +- Blog.Core.IServices/BASE/IBaseServices.cs | 1 + Blog.Core.IServices/ITrojanUsersServices.cs | 14 + Blog.Core.Model/Models/TrojanCusServers.cs | 26 + Blog.Core.Model/Models/TrojanDetails.cs | 63 +++ Blog.Core.Model/Models/TrojanServers.cs | 31 ++ Blog.Core.Model/Models/TrojanUrlServers.cs | 26 + Blog.Core.Model/Models/TrojanUsers.cs | 39 ++ .../ViewModels/TrojanLimitFlowDto.cs | 23 + Blog.Core.Model/ViewModels/TrojanServerDto.cs | 14 + .../ViewModels/TrojanServerSpliceDto.cs | 28 ++ .../ViewModels/TrojanUseDetailDto.cs | 35 ++ Blog.Core.Repository/BASE/BaseRepository.cs | 13 + Blog.Core.Repository/BASE/IBaseRepository.cs | 6 + Blog.Core.Services/BASE/BaseServices.cs | 9 + Blog.Core.Services/TrojanUsersServices.cs | 18 + Blog.Core.Tasks/Blog.Core.Tasks.csproj | 1 + .../HostedService/Job1TimedService.cs | 60 +++ .../HostedService/Job2TimedService.cs | 47 ++ .../QuartzNet/Jobs/Job_Trojan_Quartz.cs | 79 +++ .../QuartzNet/Jobs/Job_URL_Quartz.cs | 51 ++ 25 files changed, 1345 insertions(+), 1 deletion(-) create mode 100644 Blog.Core.Api/Controllers/TrojanController.cs create mode 100644 Blog.Core.IServices/ITrojanUsersServices.cs create mode 100644 Blog.Core.Model/Models/TrojanCusServers.cs create mode 100644 Blog.Core.Model/Models/TrojanDetails.cs create mode 100644 Blog.Core.Model/Models/TrojanServers.cs create mode 100644 Blog.Core.Model/Models/TrojanUrlServers.cs create mode 100644 Blog.Core.Model/Models/TrojanUsers.cs create mode 100644 Blog.Core.Model/ViewModels/TrojanLimitFlowDto.cs create mode 100644 Blog.Core.Model/ViewModels/TrojanServerDto.cs create mode 100644 Blog.Core.Model/ViewModels/TrojanServerSpliceDto.cs create mode 100644 Blog.Core.Model/ViewModels/TrojanUseDetailDto.cs create mode 100644 Blog.Core.Services/TrojanUsersServices.cs create mode 100644 Blog.Core.Tasks/HostedService/Job1TimedService.cs create mode 100644 Blog.Core.Tasks/HostedService/Job2TimedService.cs create mode 100644 Blog.Core.Tasks/QuartzNet/Jobs/Job_Trojan_Quartz.cs create mode 100644 Blog.Core.Tasks/QuartzNet/Jobs/Job_URL_Quartz.cs diff --git a/Blog.Core.Api/Blog.Core.Model.xml b/Blog.Core.Api/Blog.Core.Model.xml index 36975d11..ea9857bc 100644 --- a/Blog.Core.Api/Blog.Core.Model.xml +++ b/Blog.Core.Api/Blog.Core.Model.xml @@ -982,6 +982,76 @@ Tibug 博文 + + + users自定义服务器 + + + + + 用户流量每月汇总表 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Trojan服务器 + + + + + users自定义URL服务器 + + + + + Trojan用户 + + + + + 历史流量记录 + + 用户跟角色关联表 @@ -2407,6 +2477,73 @@ + + + 限制流量dto + 作者:胡丁文 + 时间:2020-4-27 16:57:07 + + + + + 用户 + + + + + 流量(-1为无限,单位为最小单位byte) + + + + + Trojan服务器拼接服务器和订阅地址 + + + + + 普通订阅连接 + + + + + clash订阅连接 + + + + + 备用clash订阅连接 + + + + + Trojan用户流量统计分组 + + + + + 用户ID + + + + + 月度 + + + + + 上传流量 + + + + + 下载流量 + + + + + 下载流量 + + 微信接口消息DTO diff --git a/Blog.Core.Api/Blog.Core.xml b/Blog.Core.Api/Blog.Core.xml index 3321d53a..a14a9006 100644 --- a/Blog.Core.Api/Blog.Core.xml +++ b/Blog.Core.Api/Blog.Core.xml @@ -650,6 +650,159 @@ + + + 获取Trojan用户 + + + + + + + + + 获取Trojan用户-下拉列表用 + + + + + + 添加Trojan用户 + + + + + + + 更新Trojan用户 + + + + + + + 删除用户 + + + + + + + 重置流量 + + + + + + + 限制流量 + + + + + + + 重置链接密码 + + + + + + + 获取Trojan服务器 + + + + + + 获取拼接后的Trojan服务器 + + passwordshow + + + + + 删除Trojan服务器 + + + + + + + 更新Trojan服务器 + + + + + + + 添加Trojan服务器 + + + + + + + 获取Cus服务器 + + + + + + 删除Cus服务器 + + + + + + + 更新Cus服务器 + + + + + + + 添加Cus服务器 + + + + + + + 获取Url服务器 + + + + + + 删除Url服务器 + + + + + + + 更新Url服务器 + + + + + + + 添加Url服务器 + + + + + + + 获取订阅数据 + + 链接密码 + 是否使用base64加密 + + 用户管理 @@ -1062,6 +1215,13 @@ 卡片消息对象 + + + 推送卡片消息接口 + + 卡片消息对象 + + 推送文本消息 diff --git a/Blog.Core.Api/Controllers/TrojanController.cs b/Blog.Core.Api/Controllers/TrojanController.cs new file mode 100644 index 00000000..8ec64c8a --- /dev/null +++ b/Blog.Core.Api/Controllers/TrojanController.cs @@ -0,0 +1,451 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Blog.Core.Common; +using Blog.Core.Common.Extensions; +using Blog.Core.Common.Helper; +using Blog.Core.Common.HttpContextUser; +using Blog.Core.IServices; +using Blog.Core.IServices.BASE; +using Blog.Core.Model; +using Blog.Core.Model.Models; +using Blog.Core.Model.ViewModels; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Blog.Core.Controllers +{ + [Route("api/[controller]/[action]")] + [ApiController] + [Authorize(Permissions.Name)] + public class TrojanController : ControllerBase + { + private ITrojanUsersServices _trojanUsersServices; + public IBaseServices _baseServicesServers; + public IBaseServices _baseServicesDetails; + public IBaseServices _baseServicesCusServers; + public IBaseServices _baseServicesUrlServers; + private IUser _user; + public TrojanController(ITrojanUsersServices trojanUsersServices,IUser user + , IBaseServices baseServicesServers + , IBaseServices baseServicesDetails + , IBaseServices baseServicesCusServers + , IBaseServices baseServicesUrlServers) + { + _baseServicesDetails = baseServicesDetails; + _baseServicesServers = baseServicesServers; + _trojanUsersServices = trojanUsersServices; + _baseServicesCusServers = baseServicesCusServers; + _baseServicesUrlServers = baseServicesUrlServers; + _user = user; + } + /// + /// 获取Trojan用户 + /// + /// + /// + /// + /// + [HttpGet] + public async Task>> GetUser([FromQuery]PaginationModel pagination, [FromQuery] string name, [FromQuery] bool isuse) + { + var whereFind = LinqHelper.True(); + if (!string.IsNullOrEmpty(name)) + whereFind = whereFind.And(t=>t.username.Contains(name)); + if (isuse) + whereFind = whereFind.And(t => t.upload > 0 || t.download > 0); + var data = await _trojanUsersServices.QueryPage(whereFind, pagination.PageIndex, pagination.PageSize); + if (data.data.Count > 0) + { + var ids = data.data.Select(t => t.id).ToList(); + var where = LinqHelper.True(); + where = where.And(t => ids.Contains(t.userId));//.And(t => t.calDate < DateTime.Now).And(t => t.calDate > DateTime.Now.AddMonths(-12)); + var userDetails = await _baseServicesDetails.Query(where); + foreach (var trojanUser in data.data) + { + var ls = from t in userDetails + where t.userId == trojanUser.id + group t by new { moth = t.calDate.ToString("yyyy-MM"), id = t.userId } into g + orderby g.Key.moth descending + select new TrojanUseDetailDto { userId = g.Key.id, moth = g.Key.moth, up = g.Sum(t => Convert.ToDecimal(t.upload)), down = g.Sum(t => Convert.ToDecimal(t.download)) }; + var lsData = ls.ToList(); + trojanUser.useList = lsData; + } + } + return MessageModel>.Success("获取成功", data); + } + + /// + /// 获取Trojan用户-下拉列表用 + /// + /// + [HttpGet] + public async Task> GetAllTrojanUser() + { + var data = await _trojanUsersServices.QueryTable("select id,username from users"); + return MessageModel.Success("获取成功", data); + } + /// + /// 添加Trojan用户 + /// + /// + /// + [HttpPost] + public async Task> AddUser([FromBody]TrojanUsers user) + { + var find = await _trojanUsersServices.Query(t => t.username == user.username); + if(find!=null && find.Count>0) return MessageModel.Fail("用户名已存在"); + var pass = StringHelper.GetGUID(); + var passEcrypt = ShaHelper.Sha224(pass); + //user.quota = 0; + user.upload = 0; + user.download = 0; + user.password = passEcrypt; + user.passwordshow = pass; + var data = await _trojanUsersServices.Add(user); + return MessageModel.Success("添加成功", data); + } + /// + /// 更新Trojan用户 + /// + /// + /// + [HttpPut] + public async Task> UpdateUser([FromBody]TrojanUsers user) + { + var find = await _trojanUsersServices.QueryById(user.id); + if (find == null) return MessageModel.Fail("用户名不存在"); + find.username = user.username; + var data = await _trojanUsersServices.Update(find, new List { "username" }); + return MessageModel.Success("更新成功", data); + } + + /// + /// 删除用户 + /// + /// + /// + [HttpPut] + public async Task> DelUser([FromBody]int[] users) + { + var data = await _trojanUsersServices.Query(t => users.Contains(t.id)); + var list = data.Select(t => t.id.ToString()).ToArray(); + await _trojanUsersServices.DeleteByIds(list); + return MessageModel.Success("删除成功"); + } + /// + /// 重置流量 + /// + /// + /// + [HttpPut] + public async Task> ResetFlow([FromBody]int[] users) + { + var data = await _trojanUsersServices.Query(t => users.Contains(t.id)); + foreach (var item in data) + { + item.upload = 0; + item.download = 0; + await _trojanUsersServices.Update(item, new List { "upload", "download" }); + } + return MessageModel.Success("重置流量成功"); + } + /// + /// 限制流量 + /// + /// + /// + [HttpPut] + public async Task> LimitFlow([FromBody] TrojanLimitFlowDto limit) + { + var data = await _trojanUsersServices.Query(t => limit.users.Contains(t.id)); + foreach (var item in data) + { + item.quota = limit.quota; + await _trojanUsersServices.Update(item, new List { "quota" }); + } + return MessageModel.Success("限制流量成功"); + } + /// + /// 重置链接密码 + /// + /// + /// + [HttpPut] + public async Task> ResetPass([FromBody]int[] users) + { + var data = await _trojanUsersServices.Query(t => users.Contains(t.id)); + var pass = StringHelper.GetGUID(); + var passEcrypt = ShaHelper.Sha224(pass); + foreach (var item in data) + { + item.password = passEcrypt; + item.passwordshow = pass; + await _trojanUsersServices.Update(item, new List { "password" , "passwordshow" }); + } + return MessageModel.Success("重置链接密码成功"); + } + /// + /// 获取Trojan服务器 + /// + /// + [HttpGet] + public async Task>> GetServers() + { + var data = await _baseServicesServers.Query(); + data = data.OrderBy(t => t.servername).ToList(); + return MessageModel>.Success("获取成功", data); + } + /// + /// 获取拼接后的Trojan服务器 + /// + /// passwordshow + /// + [HttpGet] + public async Task> GetSpliceServers(string id) + { + var data = await _baseServicesServers.Query(); + data = data.OrderBy(t => t.servername).ToList(); + var res = new TrojanServerSpliceDto(); + res.normalApi = AppSettings.app(new string[] { "trojan", "normalApi" }).ObjToString(); + res.clashApi = AppSettings.app(new string[] { "trojan", "clashApi" }).ObjToString(); + res.clashApiBackup = AppSettings.app(new string[] { "trojan", "clashApiBackup" }).ObjToString(); + foreach (var item in data) + { + var serverSplice = GetSplice(item, id); + res.list.Add(new TrojanServerDto { name = item.servername, value = serverSplice }); + } + return MessageModel.Success("获取成功", res); ; + + } + /// + /// 删除Trojan服务器 + /// + /// + /// + [HttpPut] + public async Task>> DelServers([FromBody]int[] servers) + { + var data = await _baseServicesServers.DeleteByIds(servers.Select(t=>t.ToString()).ToArray()); + if (data) + return MessageModel>.Success("删除成功"); + else + return MessageModel>.Fail("删除失败"); + } + /// + /// 更新Trojan服务器 + /// + /// + /// + [HttpPut] + public async Task>> UpdateServers(TrojanServers server) + { + var data = await _baseServicesServers.Update(server); + return MessageModel>.Success("更新成功"); + } + /// + /// 添加Trojan服务器 + /// + /// + /// + [HttpPost] + public async Task>> AddServers(TrojanServers server) + { + var data = await _baseServicesServers.Add(server); + return MessageModel>.Success("添加成功"); + } + + /// + /// 获取Cus服务器 + /// + /// + [HttpGet] + public async Task>> GetCusServers() + { + var data = await _baseServicesCusServers.Query(); + data = data.OrderBy(t => t.servername).ToList(); + return MessageModel>.Success("获取成功", data); + } + /// + /// 删除Cus服务器 + /// + /// + /// + [HttpPut] + public async Task>> DelCusServers([FromBody] int[] servers) + { + var data = await _baseServicesCusServers.DeleteByIds(servers.Select(t => t.ToString()).ToArray()); + if (data) + return MessageModel>.Success("删除成功"); + else + return MessageModel>.Fail("删除失败"); + } + /// + /// 更新Cus服务器 + /// + /// + /// + [HttpPut] + public async Task>> UpdateCusServers(TrojanCusServers server) + { + var data = await _baseServicesCusServers.Update(server); + return MessageModel>.Success("更新成功"); + } + /// + /// 添加Cus服务器 + /// + /// + /// + [HttpPost] + public async Task>> AddCusServers(TrojanCusServers server) + { + var data = await _baseServicesCusServers.Add(server); + return MessageModel>.Success("添加成功"); + } + + + /// + /// 获取Url服务器 + /// + /// + [HttpGet] + public async Task>> GetUrlServers() + { + var data = await _baseServicesUrlServers.Query(); + data = data.OrderBy(t => t.servername).ToList(); + return MessageModel>.Success("获取成功", data); + } + /// + /// 删除Url服务器 + /// + /// + /// + [HttpPut] + public async Task>> DelUrlServers([FromBody] int[] servers) + { + var data = await _baseServicesUrlServers.DeleteByIds(servers.Select(t => t.ToString()).ToArray()); + if (data) + return MessageModel>.Success("删除成功"); + else + return MessageModel>.Fail("删除失败"); + } + /// + /// 更新Url服务器 + /// + /// + /// + [HttpPut] + public async Task>> UpdateUrlServers(TrojanUrlServers server) + { + var data = await _baseServicesUrlServers.Update(server); + return MessageModel>.Success("更新成功"); + } + /// + /// 添加Url服务器 + /// + /// + /// + [HttpPost] + public async Task>> AddUrlServers(TrojanUrlServers server) + { + var data = await _baseServicesUrlServers.Add(server); + return MessageModel>.Success("添加成功"); + } + private string GetSplice(TrojanServers item,string passwordshow) + { + if ("0".Equals(item.servertype)) + return $"trojan://{passwordshow}@{item.serveraddress}:{item.serverport}?allowinsecure=0&tfo=0&peer={(string.IsNullOrEmpty(item.serverpeer) ? item.serverpeer : item.serveraddress)}#{item.servername}"; + else if ("1".Equals(item.servertype)) + return $"trojan://{passwordshow}@{item.serveraddress}:{item.serverport}?wspath={item.serverpath}&ws=1&peer={(string.IsNullOrEmpty(item.serverpeer) ? item.serverpeer : item.serveraddress)}#{item.servername}"; + else + return $"servertype:({item.servertype})错误"; + } + private List GetSplice(List items, string passwordshow) + { + List ls = new List(); + foreach (var item in items) + { + ls.Add(GetSplice(item, passwordshow)); + } + return ls; + } + /// + /// 获取订阅数据 + /// + /// 链接密码 + /// 是否使用base64加密 + /// + [HttpGet] + [AllowAnonymous] + public async Task RSS(string id,bool isUseBase64=true) + { + StringBuilder sb = new StringBuilder(); + try + { + var user = (await _trojanUsersServices.Query(t => t.passwordshow == id)).FirstOrDefault(); + if (user == null) throw new Exception("用户不存在"); + var data = await _baseServicesServers.Query(t => (t.userid == user.id || t.userid <= 0) && t.serverenable); + if (data != null) + { + data = data.OrderBy(t => t.servername).ToList(); + foreach (var item in data) + { + sb.AppendLine(GetSplice(item, user.passwordshow)); + } + } + var cusData = await _baseServicesCusServers.Query(t=> (t.userid == user.id || t.userid <=0) && t.serverenable); + if (cusData != null) + { + cusData = cusData.OrderBy(t => t.servername).ToList(); + foreach (var item in cusData) + { + sb.AppendLine(item.serveraddress); + } + } + var urlData = await _baseServicesUrlServers.Query(t => (t.userid == user.id || t.userid <= 0) && t.serverenable); + if (urlData != null) + { + urlData = urlData.OrderBy(t => t.servername).ToList(); + foreach (var item in urlData) + { + try + { + var urlStrObj = await HttpHelper.GetAsync(item.serveraddress); + var lines = ""; + try + { + lines = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(urlStrObj)); + } + catch (Exception) + { + lines = urlStrObj; + } + finally + { + sb.AppendLine(lines); + } + } + catch (Exception ex) + { + sb.AppendLine($"trojan://xxxxxx@xxxxxx.xx:443?allowinsecure=0&tfo=0#{ex.Message}"); + } + } + } + } + catch (Exception ex) + { + sb.AppendLine($"trojan://xxxxxx@xxxxxx.xx:443?allowinsecure=0&tfo=0#{ex.Message}"); + } + if (isUseBase64) + { + return Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(sb.ToString())); + } + else{ + return sb.ToString(); + } + } + } +} \ No newline at end of file diff --git a/Blog.Core.Api/Controllers/WeChatController.cs b/Blog.Core.Api/Controllers/WeChatController.cs index 4c4877f6..c215f563 100644 --- a/Blog.Core.Api/Controllers/WeChatController.cs +++ b/Blog.Core.Api/Controllers/WeChatController.cs @@ -146,6 +146,18 @@ public async Task> PushCardMsg(WeChatCardMs return await _weChatConfigServices.PushCardMsg(msg, pushUserIP); } /// + /// 推送卡片消息接口 + /// + /// 卡片消息对象 + /// + [HttpGet] + [AllowAnonymous] + public async Task> PushCardMsgGet([FromQuery] WeChatCardMsgDataDto msg) + { + string pushUserIP = $"{Request.HttpContext.Connection.RemoteIpAddress}:{Request.HttpContext.Connection.RemotePort}"; + return await _weChatConfigServices.PushCardMsg(msg, pushUserIP); + } + /// /// 推送文本消息 /// /// 消息对象 diff --git a/Blog.Core.Api/Dockerfile b/Blog.Core.Api/Dockerfile index 43618077..1eb0572a 100644 --- a/Blog.Core.Api/Dockerfile +++ b/Blog.Core.Api/Dockerfile @@ -3,7 +3,7 @@ #FROM swr.cn-south-1.myhuaweicloud.com/mcr/aspnet:5.0-alpine #FROM mcr.microsoft.com/dotnet/core/aspnet:5.0-buster-slim -FROM mcr.microsoft.com/dotnet/aspnet:5.0-buster-slim +FROM mcr.microsoft.com/dotnet/aspnet:6.0-bullseye-slim RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime RUN echo 'Asia/Shanghai' >/etc/timezone diff --git a/Blog.Core.IServices/BASE/IBaseServices.cs b/Blog.Core.IServices/BASE/IBaseServices.cs index 27daae98..de4a7a06 100644 --- a/Blog.Core.IServices/BASE/IBaseServices.cs +++ b/Blog.Core.IServices/BASE/IBaseServices.cs @@ -26,6 +26,7 @@ public interface IBaseServices where TEntity : class Task DeleteByIds(object[] ids); Task Update(TEntity model); + Task Update(List model); Task Update(TEntity entity, string where); Task Update(object operateAnonymousObjects); diff --git a/Blog.Core.IServices/ITrojanUsersServices.cs b/Blog.Core.IServices/ITrojanUsersServices.cs new file mode 100644 index 00000000..92bac1d8 --- /dev/null +++ b/Blog.Core.IServices/ITrojanUsersServices.cs @@ -0,0 +1,14 @@ +using Blog.Core.IServices.BASE; +using Blog.Core.Model.Models; + +namespace Blog.Core.IServices +{ + /// + /// TrojanUsersServices + /// + public interface ITrojanUsersServices : IBaseServices + { + + } +} + diff --git a/Blog.Core.Model/Models/TrojanCusServers.cs b/Blog.Core.Model/Models/TrojanCusServers.cs new file mode 100644 index 00000000..03f02539 --- /dev/null +++ b/Blog.Core.Model/Models/TrojanCusServers.cs @@ -0,0 +1,26 @@ + +using System; +using System.Linq; +using System.Text; +using SqlSugar; + +namespace Blog.Core.Model.Models +{ + /// + ///users自定义服务器 + /// + [SugarTable("users_cus", "users自定义服务器")] + [TenantAttribute("WMBLOG_MYSQL_2")] //('代表是哪个数据库,名字是appsettings.json 的 ConnId') + public partial class TrojanCusServers + { + + [SugarColumn(IsNullable = false, IsPrimaryKey = true, IsIdentity = true)] + public int id { set; get; } + public int userid { get; set; } + public string servername { set; get; } + public string serveraddress { set; get; } + [SugarColumn(IsNullable = true)] + public string serverremark { get; set; } + public bool serverenable { get; set; } + } +} diff --git a/Blog.Core.Model/Models/TrojanDetails.cs b/Blog.Core.Model/Models/TrojanDetails.cs new file mode 100644 index 00000000..dda53d9b --- /dev/null +++ b/Blog.Core.Model/Models/TrojanDetails.cs @@ -0,0 +1,63 @@ + +//模板自动生成(请勿修改) +//作者:胡丁文 +using System; +using System.Linq; +using System.Text; +using SqlSugar; + +namespace Blog.Core.Model.Models +{ + /// + ///用户流量每月汇总表 + /// + [SugarTable("users_detail", "用户流量每月汇总表")] + [TenantAttribute("WMBLOG_MYSQL_2")] //('代表是哪个数据库,名字是appsettings.json 的 ConnId') + public partial class TrojanDetails + { + + /// + /// + /// + [SugarColumn(IsPrimaryKey = true, IsIdentity = true)] + public int id { get; set; } + + /// + /// + /// + public int userId { get; set; } + + /// + /// + /// + public DateTime calDate { get; set; } + + /// + /// + /// + public ulong download { get; set; } + + /// + /// + /// + public ulong upload { get; set; } + + /// + /// + /// + [SugarColumn(IsNullable = true)] + public int? CreateId { get; set; } + + /// + /// + /// + [SugarColumn(IsNullable = true)] + public string CreateBy { get; set; } + + /// + /// + /// + [SugarColumn(IsNullable = true)] + public DateTime? CreateTime { get; set; } + } +} diff --git a/Blog.Core.Model/Models/TrojanServers.cs b/Blog.Core.Model/Models/TrojanServers.cs new file mode 100644 index 00000000..d9d8275f --- /dev/null +++ b/Blog.Core.Model/Models/TrojanServers.cs @@ -0,0 +1,31 @@ + +using System; +using System.Linq; +using System.Text; +using SqlSugar; + +namespace Blog.Core.Model.Models +{ + /// + ///Trojan服务器 + /// + [SugarTable("servers", "Trojan服务器")] + [TenantAttribute("WMBLOG_MYSQL_2")] //('代表是哪个数据库,名字是appsettings.json 的 ConnId') + public partial class TrojanServers + { + + [SugarColumn(IsNullable = false, IsPrimaryKey = true, IsIdentity = true)] + public int id { set; get; } + public int userid { get; set; } + public string servername { set; get; } + public string serveraddress { set; get; } + public int serverport { get; set; } + [SugarColumn(IsNullable = true)] + public string serverremark { get; set; } + public bool serverenable { get; set; } + public string serverpeer { get; set; } + [SugarColumn(IsNullable = true)] + public string serverpath { get; set; } + public string servertype { get; set; } + } +} diff --git a/Blog.Core.Model/Models/TrojanUrlServers.cs b/Blog.Core.Model/Models/TrojanUrlServers.cs new file mode 100644 index 00000000..db48343a --- /dev/null +++ b/Blog.Core.Model/Models/TrojanUrlServers.cs @@ -0,0 +1,26 @@ + +using System; +using System.Linq; +using System.Text; +using SqlSugar; + +namespace Blog.Core.Model.Models +{ + /// + ///users自定义URL服务器 + /// + [SugarTable("users_url", "users自定义URL服务器")] + [TenantAttribute("WMBLOG_MYSQL_2")] //('代表是哪个数据库,名字是appsettings.json 的 ConnId') + public partial class TrojanUrlServers + { + + [SugarColumn(IsNullable = false, IsPrimaryKey = true, IsIdentity = true)] + public int id { set; get; } + public int userid { get; set; } + public string servername { set; get; } + public string serveraddress { set; get; } + [SugarColumn(IsNullable = true)] + public string serverremark { get; set; } + public bool serverenable { get; set; } + } +} diff --git a/Blog.Core.Model/Models/TrojanUsers.cs b/Blog.Core.Model/Models/TrojanUsers.cs new file mode 100644 index 00000000..796c044e --- /dev/null +++ b/Blog.Core.Model/Models/TrojanUsers.cs @@ -0,0 +1,39 @@ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Blog.Core.Model.ViewModels; +using SqlSugar; + +namespace Blog.Core.Model.Models +{ + /// + ///Trojan用户 + /// + [SugarTable("users", "Trojan用户表")] + [TenantAttribute("WMBLOG_MYSQL_2")] //('代表是哪个数据库,名字是appsettings.json 的 ConnId') + public partial class TrojanUsers + { + + [SugarColumn(IsNullable = false, IsPrimaryKey = true, IsIdentity = true)] + public int id { set; get; } + public string username { set; get; } + public string password { set; get; } + public Int64 quota { set; get; } + public UInt64 download { set; get; } + public UInt64 upload { set; get; } + public string passwordshow { set; get; } + [SugarColumn(IsNullable = true)] + public int CreateId { get; set; } + [SugarColumn(IsNullable = true)] + public string CreateBy { get; set; } + [SugarColumn(IsNullable = true)] + public DateTime? CreateTime { get; set; } + /// + /// 历史流量记录 + /// + [SugarColumn(IsIgnore = true)] + public List useList { get; set; } + } +} diff --git a/Blog.Core.Model/ViewModels/TrojanLimitFlowDto.cs b/Blog.Core.Model/ViewModels/TrojanLimitFlowDto.cs new file mode 100644 index 00000000..c0efd347 --- /dev/null +++ b/Blog.Core.Model/ViewModels/TrojanLimitFlowDto.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Blog.Core.Model.ViewModels +{ + /// + /// 限制流量dto + /// 作者:胡丁文 + /// 时间:2020-4-27 16:57:07 + /// + public class TrojanLimitFlowDto + { + /// + /// 用户 + /// + public int[] users { get; set; } + /// + /// 流量(-1为无限,单位为最小单位byte) + /// + public Int64 quota { get; set; } + } +} diff --git a/Blog.Core.Model/ViewModels/TrojanServerDto.cs b/Blog.Core.Model/ViewModels/TrojanServerDto.cs new file mode 100644 index 00000000..89cb87e3 --- /dev/null +++ b/Blog.Core.Model/ViewModels/TrojanServerDto.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Blog.Core.Model.ViewModels +{ + public class TrojanServerDto + { + public string name { get; set; } + public string value { get; set; } + } +} diff --git a/Blog.Core.Model/ViewModels/TrojanServerSpliceDto.cs b/Blog.Core.Model/ViewModels/TrojanServerSpliceDto.cs new file mode 100644 index 00000000..e83adfd5 --- /dev/null +++ b/Blog.Core.Model/ViewModels/TrojanServerSpliceDto.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Blog.Core.Model.ViewModels +{ + /// + /// Trojan服务器拼接服务器和订阅地址 + /// + public class TrojanServerSpliceDto + { + /// + /// 普通订阅连接 + /// + public string normalApi { get; set; } + /// + /// clash订阅连接 + /// + public string clashApi { get; set; } + /// + /// 备用clash订阅连接 + /// + public string clashApiBackup { get; set; } + public List list { get; set; } = new List(); + } +} diff --git a/Blog.Core.Model/ViewModels/TrojanUseDetailDto.cs b/Blog.Core.Model/ViewModels/TrojanUseDetailDto.cs new file mode 100644 index 00000000..7746b105 --- /dev/null +++ b/Blog.Core.Model/ViewModels/TrojanUseDetailDto.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Blog.Core.Model.ViewModels +{ + /// + /// Trojan用户流量统计分组 + /// + public class TrojanUseDetailDto + { + /// + /// 用户ID + /// + public int userId { get; set; } + /// + /// 月度 + /// + public string moth { get; set; } + /// + /// 上传流量 + /// + public decimal up { get; set; } + /// + /// 下载流量 + /// + public decimal down { get; set; } + /// + /// 下载流量 + /// + public decimal total { get { return up + down; } } + } +} diff --git a/Blog.Core.Repository/BASE/BaseRepository.cs b/Blog.Core.Repository/BASE/BaseRepository.cs index 82900689..ead9f9af 100644 --- a/Blog.Core.Repository/BASE/BaseRepository.cs +++ b/Blog.Core.Repository/BASE/BaseRepository.cs @@ -144,6 +144,19 @@ public async Task Update(TEntity entity) //这种方式会以主键为条件 return await _db.Updateable(entity).ExecuteCommandHasChangeAsync(); } + /// + /// 更新实体数据 + /// + /// 博文实体类 + /// + public async Task Update(List entity) + { + ////这种方式会以主键为条件 + //var i = await Task.Run(() => _db.Updateable(entity).ExecuteCommand()); + //return i > 0; + //这种方式会以主键为条件 + return await _db.Updateable(entity).ExecuteCommandHasChangeAsync(); + } public async Task Update(TEntity entity, string where) { diff --git a/Blog.Core.Repository/BASE/IBaseRepository.cs b/Blog.Core.Repository/BASE/IBaseRepository.cs index 29783505..9b72a00d 100644 --- a/Blog.Core.Repository/BASE/IBaseRepository.cs +++ b/Blog.Core.Repository/BASE/IBaseRepository.cs @@ -69,6 +69,12 @@ public interface IBaseRepository where TEntity : class /// /// Task Update(TEntity model); + /// + /// 更新model + /// + /// + /// + Task Update(List model); /// /// 根据model,更新,带where条件 diff --git a/Blog.Core.Services/BASE/BaseServices.cs b/Blog.Core.Services/BASE/BaseServices.cs index ec685be2..232c6fae 100644 --- a/Blog.Core.Services/BASE/BaseServices.cs +++ b/Blog.Core.Services/BASE/BaseServices.cs @@ -76,6 +76,15 @@ public async Task Update(TEntity entity) { return await BaseDal.Update(entity); } + /// + /// 更新实体数据 + /// + /// 博文实体类 + /// + public async Task Update(List entity) + { + return await BaseDal.Update(entity); + } public async Task Update(TEntity entity, string where) { return await BaseDal.Update(entity, where); diff --git a/Blog.Core.Services/TrojanUsersServices.cs b/Blog.Core.Services/TrojanUsersServices.cs new file mode 100644 index 00000000..f42f51d9 --- /dev/null +++ b/Blog.Core.Services/TrojanUsersServices.cs @@ -0,0 +1,18 @@ +using Blog.Core.Common; +using Blog.Core.IRepository.Base; +using Blog.Core.IServices; +using Blog.Core.Model.Models; +using Blog.Core.Services.BASE; +using System.Linq; +using System.Threading.Tasks; + +namespace Blog.Core.Services +{ + /// + /// TrojanUsersServices + /// + public class TrojanUsersServices : BaseServices, ITrojanUsersServices + { + + } +} diff --git a/Blog.Core.Tasks/Blog.Core.Tasks.csproj b/Blog.Core.Tasks/Blog.Core.Tasks.csproj index 5e173559..bb748f64 100644 --- a/Blog.Core.Tasks/Blog.Core.Tasks.csproj +++ b/Blog.Core.Tasks/Blog.Core.Tasks.csproj @@ -11,6 +11,7 @@ + diff --git a/Blog.Core.Tasks/HostedService/Job1TimedService.cs b/Blog.Core.Tasks/HostedService/Job1TimedService.cs new file mode 100644 index 00000000..9777affc --- /dev/null +++ b/Blog.Core.Tasks/HostedService/Job1TimedService.cs @@ -0,0 +1,60 @@ +using Blog.Core.Common; +using Blog.Core.IServices; +using Microsoft.Extensions.Hosting; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Blog.Core.Tasks +{ + public class Job1TimedService : IHostedService, IDisposable + { + private Timer _timer; + private readonly IBlogArticleServices _blogArticleServices; + + // 这里可以注入 + public Job1TimedService(IBlogArticleServices blogArticleServices) + { + _blogArticleServices = blogArticleServices; + } + + public Task StartAsync(CancellationToken cancellationToken) + { + Console.WriteLine("Job 1 is starting."); + + _timer = new Timer(DoWork, null, TimeSpan.Zero, + TimeSpan.FromSeconds(60 * 60));//一个小时 + + return Task.CompletedTask; + } + + private void DoWork(object state) + { + try + { + var model = _blogArticleServices.GetBlogDetails(1).Result; + Console.WriteLine($"Job 1 启动成功,获取id=1的博客title为:{model?.btitle}"); + } + catch (Exception ex) + { + Console.WriteLine($"Error:{ex.Message}"); + } + + ConsoleHelper.WriteSuccessLine($"Job 1: {DateTime.Now}"); + } + + public Task StopAsync(CancellationToken cancellationToken) + { + Console.WriteLine("Job 1 is stopping."); + + _timer?.Change(Timeout.Infinite, 0); + + return Task.CompletedTask; + } + + public void Dispose() + { + _timer?.Dispose(); + } + } +} diff --git a/Blog.Core.Tasks/HostedService/Job2TimedService.cs b/Blog.Core.Tasks/HostedService/Job2TimedService.cs new file mode 100644 index 00000000..ee7f2c5c --- /dev/null +++ b/Blog.Core.Tasks/HostedService/Job2TimedService.cs @@ -0,0 +1,47 @@ +using Blog.Core.Common; +using Microsoft.Extensions.Hosting; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Blog.Core.Tasks +{ + public class Job2TimedService : IHostedService, IDisposable + { + private Timer _timer; + + // 这里可以注入 + public Job2TimedService() + { + } + + public Task StartAsync(CancellationToken cancellationToken) + { + Console.WriteLine("Job 2 is starting."); + + _timer = new Timer(DoWork, null, TimeSpan.Zero, + TimeSpan.FromSeconds(60 * 60 * 2));//两个小时 + + return Task.CompletedTask; + } + + private void DoWork(object state) + { + ConsoleHelper.WriteWarningLine($"Job 2: {DateTime.Now}"); + } + + public Task StopAsync(CancellationToken cancellationToken) + { + Console.WriteLine("Job 2 is stopping."); + + _timer?.Change(Timeout.Infinite, 0); + + return Task.CompletedTask; + } + + public void Dispose() + { + _timer?.Dispose(); + } + } +} diff --git a/Blog.Core.Tasks/QuartzNet/Jobs/Job_Trojan_Quartz.cs b/Blog.Core.Tasks/QuartzNet/Jobs/Job_Trojan_Quartz.cs new file mode 100644 index 00000000..f9640749 --- /dev/null +++ b/Blog.Core.Tasks/QuartzNet/Jobs/Job_Trojan_Quartz.cs @@ -0,0 +1,79 @@ +using Blog.Core.Repository.UnitOfWorks; +using Blog.Core.IServices; +using Blog.Core.IServices.BASE; +using Blog.Core.Model.Models; +using Microsoft.Extensions.Logging; +using Quartz; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +/// +/// 这里要注意下,命名空间和程序集是一样的,不然反射不到(任务类要去JobSetup添加注入) +/// +namespace Blog.Core.Tasks +{ + public class Job_Trojan_Quartz : JobBase, IJob + { + private readonly IUnitOfWorkManage _unitOfWorkManage; + public IBaseServices_DetailServices; + private readonly ITrojanUsersServices _TrojanUsers; + private readonly ILogger _logger; + + public Job_Trojan_Quartz(IUnitOfWorkManage unitOfWorkManage, IBaseServices iusers_DetailServices, ITrojanUsersServices trojanUsers, ITasksQzServices tasksQzServices, ILogger logger) + { + _tasksQzServices = tasksQzServices; + _unitOfWorkManage = unitOfWorkManage; + _DetailServices = iusers_DetailServices; + _TrojanUsers = trojanUsers; + _logger = logger; + } + public async Task Execute(IJobExecutionContext context) + { + //var param = context.MergedJobDataMap; + // 可以直接获取 JobDetail 的值 + var jobKey = context.JobDetail.Key; + var jobId = jobKey.Name; + var executeLog = await ExecuteJob(context, async () => await Run(context, jobId.ObjToInt())); + + } + public async Task Run(IJobExecutionContext context, int jobid) + { + if (jobid > 0) + { + try + { + //获取每月用户的数据 + _unitOfWorkManage.BeginTran(); + var now = DateTime.Now.AddMonths(-1); + + var list = await _TrojanUsers.Query(); + List ls = new List(); + foreach (var us in list) + { + TrojanDetails u = new TrojanDetails(); + u.calDate = now; + u.userId = us.id; + u.download = us.download; + u.upload = us.upload; + //清零 + us.download = 0; + us.upload = 0; + ls.Add(u); + } + await _TrojanUsers.Update(list); + await _DetailServices.Add(ls); + _unitOfWorkManage.CommitTran(); + } + catch (Exception) + { + _unitOfWorkManage.RollbackTran(); + throw; + } + } + } + } + + + +} diff --git a/Blog.Core.Tasks/QuartzNet/Jobs/Job_URL_Quartz.cs b/Blog.Core.Tasks/QuartzNet/Jobs/Job_URL_Quartz.cs new file mode 100644 index 00000000..666a8d41 --- /dev/null +++ b/Blog.Core.Tasks/QuartzNet/Jobs/Job_URL_Quartz.cs @@ -0,0 +1,51 @@ +using Blog.Core.Common.Helper; +using Blog.Core.Repository.UnitOfWorks; +using Blog.Core.IServices; +using Blog.Core.IServices.BASE; +using Blog.Core.Model.Models; +using Microsoft.Extensions.Logging; +using Quartz; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +/// +/// 这里要注意下,命名空间和程序集是一样的,不然反射不到(任务类要去JobSetup添加注入) +/// +namespace Blog.Core.Tasks +{ + public class Job_URL_Quartz : JobBase, IJob + { + private readonly ILogger _logger; + + public Job_URL_Quartz(ITasksQzServices tasksQzServices, ILogger logger) + { + _tasksQzServices = tasksQzServices; + _logger = logger; + } + public async Task Execute(IJobExecutionContext context) + { + // 可以直接获取 JobDetail 的值 + var jobKey = context.JobDetail.Key; + var jobId = jobKey.Name; + var executeLog = await ExecuteJob(context, async () => await Run(context, jobId.ObjToInt())); + + } + public async Task Run(IJobExecutionContext context, int jobid) + { + if (jobid > 0) + { + JobDataMap data = context.JobDetail.JobDataMap; + string pars = data.GetString("JobParam"); + if (!string.IsNullOrWhiteSpace(pars)) + { + var log = await HttpHelper.GetAsync(pars); + _logger.LogInformation(log); + } + } + } + } + + + +} From 192bdbd59e8d42792ca6140e0f16f4d8ad636909 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Sat, 11 Mar 2023 10:14:42 +0800 Subject: [PATCH 02/84] Update ValuesController.cs --- Blog.Core.Api/Controllers/ValuesController.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Blog.Core.Api/Controllers/ValuesController.cs b/Blog.Core.Api/Controllers/ValuesController.cs index 52eb0ea1..1347ca16 100644 --- a/Blog.Core.Api/Controllers/ValuesController.cs +++ b/Blog.Core.Api/Controllers/ValuesController.cs @@ -298,7 +298,6 @@ public object Post([FromBody] BlogArticle blogArticle, int id) /// /// [HttpPost] - [Route("TestPostPara")] [AllowAnonymous] public object TestPostPara(string name) { From d7190d677cfcf04f05e682deb63e51f9a678bc84 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Sat, 11 Mar 2023 11:07:20 +0800 Subject: [PATCH 03/84] feat: :+1: beautiful api --- Blog.Core.Api/Blog.Core.xml | 6 -- Blog.Core.Api/Controllers/BlogController.cs | 6 +- .../Controllers/DepartmentController.cs | 7 -- Blog.Core.Api/Controllers/ModuleController.cs | 5 +- .../Controllers/MonitorController.cs | 7 -- Blog.Core.Api/Controllers/NacosController.cs | 1 - Blog.Core.Api/Controllers/PayController.cs | 4 +- Blog.Core.Api/Controllers/RoleController.cs | 3 +- .../Controllers/TasksQzController.cs | 64 ++++++++----------- Blog.Core.Api/Controllers/TopicController.cs | 4 +- .../Controllers/TopicDetailController.cs | 5 +- .../Controllers/TransactionController.cs | 5 +- Blog.Core.Api/Controllers/UserController.cs | 7 +- .../Controllers/UserRoleController.cs | 3 +- .../Controllers/WeChatCompanyController.cs | 1 - .../Controllers/WeChatConfigController.cs | 1 - Blog.Core.Api/Controllers/WeChatController.cs | 5 +- .../Controllers/WeChatPushLogController.cs | 1 - .../Controllers/WeChatSubController.cs | 1 - Blog.Core.Services/TasksLogServices.cs | 30 +++++---- 20 files changed, 53 insertions(+), 113 deletions(-) diff --git a/Blog.Core.Api/Blog.Core.xml b/Blog.Core.Api/Blog.Core.xml index af6321da..89cb3213 100644 --- a/Blog.Core.Api/Blog.Core.xml +++ b/Blog.Core.Api/Blog.Core.xml @@ -580,18 +580,12 @@ 获取任务运行日志 - - - 任务概况 - - - diff --git a/Blog.Core.Api/Controllers/BlogController.cs b/Blog.Core.Api/Controllers/BlogController.cs index 6525448e..83fad967 100644 --- a/Blog.Core.Api/Controllers/BlogController.cs +++ b/Blog.Core.Api/Controllers/BlogController.cs @@ -1,8 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq.Expressions; +using System.Linq.Expressions; using System.Text.RegularExpressions; -using System.Threading.Tasks; using Blog.Core.Common.Helper; using Blog.Core.IServices; using Blog.Core.Model; @@ -11,7 +8,6 @@ using Blog.Core.SwaggerHelper; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; using StackExchange.Profiling; using static Blog.Core.Extensions.CustomApiVersion; diff --git a/Blog.Core.Api/Controllers/DepartmentController.cs b/Blog.Core.Api/Controllers/DepartmentController.cs index 1674883f..bca7bf6d 100644 --- a/Blog.Core.Api/Controllers/DepartmentController.cs +++ b/Blog.Core.Api/Controllers/DepartmentController.cs @@ -4,17 +4,10 @@ using Blog.Core.Model; using Blog.Core.Model.Models; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Hosting; using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Linq.Expressions; using System.Text; -using System.Threading.Tasks; namespace Blog.Core.Api.Controllers { diff --git a/Blog.Core.Api/Controllers/ModuleController.cs b/Blog.Core.Api/Controllers/ModuleController.cs index 334a8ea4..2da284ba 100644 --- a/Blog.Core.Api/Controllers/ModuleController.cs +++ b/Blog.Core.Api/Controllers/ModuleController.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq.Expressions; -using System.Threading.Tasks; +using System.Linq.Expressions; using Blog.Core.Common.HttpContextUser; using Blog.Core.IServices; using Blog.Core.Model; diff --git a/Blog.Core.Api/Controllers/MonitorController.cs b/Blog.Core.Api/Controllers/MonitorController.cs index c0dbed52..77b4e5bf 100644 --- a/Blog.Core.Api/Controllers/MonitorController.cs +++ b/Blog.Core.Api/Controllers/MonitorController.cs @@ -6,19 +6,12 @@ using Blog.Core.Model; using Blog.Core.Model.ViewModels; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.SignalR; -using Microsoft.Extensions.Logging; using Newtonsoft.Json; -using System; -using System.Collections.Generic; using System.Diagnostics; -using System.IO; -using System.Linq; using System.Runtime.InteropServices; using System.Text; -using System.Threading.Tasks; using Blog.Core.Extensions.Middlewares; namespace Blog.Core.Controllers diff --git a/Blog.Core.Api/Controllers/NacosController.cs b/Blog.Core.Api/Controllers/NacosController.cs index a8701b39..e5223851 100644 --- a/Blog.Core.Api/Controllers/NacosController.cs +++ b/Blog.Core.Api/Controllers/NacosController.cs @@ -4,7 +4,6 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Nacos.V2; -using System.Threading.Tasks; namespace Blog.Core.Api.Controllers { diff --git a/Blog.Core.Api/Controllers/PayController.cs b/Blog.Core.Api/Controllers/PayController.cs index 0cbe0541..6c05c249 100644 --- a/Blog.Core.Api/Controllers/PayController.cs +++ b/Blog.Core.Api/Controllers/PayController.cs @@ -1,10 +1,8 @@ -using System.Threading.Tasks; -using Blog.Core.IServices; +using Blog.Core.IServices; using Blog.Core.Model; using Blog.Core.Model.ViewModels; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; namespace Blog.Core.Controllers { diff --git a/Blog.Core.Api/Controllers/RoleController.cs b/Blog.Core.Api/Controllers/RoleController.cs index 36df73a4..c96502c8 100644 --- a/Blog.Core.Api/Controllers/RoleController.cs +++ b/Blog.Core.Api/Controllers/RoleController.cs @@ -1,5 +1,4 @@ -using System.Threading.Tasks; -using Blog.Core.Common.HttpContextUser; +using Blog.Core.Common.HttpContextUser; using Blog.Core.IServices; using Blog.Core.Model; using Blog.Core.Model.Models; diff --git a/Blog.Core.Api/Controllers/TasksQzController.cs b/Blog.Core.Api/Controllers/TasksQzController.cs index e5c866c3..ca9d37bc 100644 --- a/Blog.Core.Api/Controllers/TasksQzController.cs +++ b/Blog.Core.Api/Controllers/TasksQzController.cs @@ -1,17 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; +using System.Linq.Expressions; using System.Reflection; -using System.Threading.Tasks; -using Blog.Core.Common.Extensions; -using Blog.Core.Common.WebApiClients.HttpApis; using Blog.Core.IServices; using Blog.Core.Model; using Blog.Core.Model.Models; using Blog.Core.Model.ViewModels; using Blog.Core.Repository.UnitOfWorks; -using Blog.Core.Services; using Blog.Core.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -29,10 +22,10 @@ public class TasksQzController : ControllerBase private readonly ISchedulerCenter _schedulerCenter; private readonly IUnitOfWorkManage _unitOfWorkManage; - public TasksQzController(ITasksQzServices tasksQzServices, ISchedulerCenter schedulerCenter, IUnitOfWorkManage unitOfWorkManage,ITasksLogServices tasksLogServices) + public TasksQzController(ITasksQzServices tasksQzServices, ISchedulerCenter schedulerCenter, IUnitOfWorkManage unitOfWorkManage, ITasksLogServices tasksLogServices) { _unitOfWorkManage = unitOfWorkManage; - _tasksQzServices = tasksQzServices; + _tasksQzServices = tasksQzServices; _schedulerCenter = schedulerCenter; _tasksLogServices = tasksLogServices; } @@ -63,7 +56,7 @@ public async Task>> Get(int page = 1, string key item.Triggers = await _schedulerCenter.GetTaskStaus(item); } } - return MessageModel>.Message(data.dataCount >= 0, "获取成功", data); + return MessageModel>.Message(data.dataCount >= 0, "获取成功", data); } /// @@ -91,32 +84,33 @@ public async Task> Post([FromBody] TasksQz tasksQz) var ResuleModel = await _schedulerCenter.AddScheduleJobAsync(tasksQz); data.success = ResuleModel.success; if (ResuleModel.success) - { + { data.msg = $"{data.msg}=>启动成功=>{ResuleModel.msg}"; } else - { + { data.msg = $"{data.msg}=>启动失败=>{ResuleModel.msg}"; } } } else - { + { data.msg = "添加失败"; - } + } } catch (Exception) { throw; } finally - { if(data.success) + { + if (data.success) _unitOfWorkManage.CommitTran(); else _unitOfWorkManage.RollbackTran(); } - return data; + return data; } @@ -140,7 +134,7 @@ public async Task> Put([FromBody] TasksQz tasksQz) data.msg = "修改成功"; data.response = tasksQz?.Id.ObjToString(); if (tasksQz.IsStart) - { + { var ResuleModelStop = await _schedulerCenter.StopScheduleJobAsync(tasksQz); data.msg = $"{data.msg}=>停止:{ResuleModelStop.msg}"; var ResuleModelStar = await _schedulerCenter.AddScheduleJobAsync(tasksQz); @@ -168,7 +162,7 @@ public async Task> Put([FromBody] TasksQz tasksQz) _unitOfWorkManage.CommitTran(); else _unitOfWorkManage.RollbackTran(); - } + } } return data; } @@ -212,7 +206,7 @@ public async Task> Delete(int jobId) _unitOfWorkManage.CommitTran(); else _unitOfWorkManage.RollbackTran(); - } + } } else { @@ -234,7 +228,7 @@ public async Task> StartJob(int jobId) var model = await _tasksQzServices.QueryById(jobId); if (model != null) { - _unitOfWorkManage.BeginTran(); + _unitOfWorkManage.BeginTran(); try { model.IsStart = true; @@ -270,7 +264,7 @@ public async Task> StartJob(int jobId) _unitOfWorkManage.CommitTran(); else _unitOfWorkManage.RollbackTran(); - } + } } else { @@ -326,10 +320,10 @@ public async Task> StopJob(int jobId) [HttpGet] public async Task> PauseJob(int jobId) { - var data = new MessageModel(); + var data = new MessageModel(); var model = await _tasksQzServices.QueryById(jobId); if (model != null) - { + { _unitOfWorkManage.BeginTran(); try { @@ -364,7 +358,7 @@ public async Task> PauseJob(int jobId) _unitOfWorkManage.CommitTran(); else _unitOfWorkManage.RollbackTran(); - } + } } else { @@ -384,7 +378,7 @@ public async Task> ResumeJob(int jobId) var model = await _tasksQzServices.QueryById(jobId); if (model != null) - { + { _unitOfWorkManage.BeginTran(); try { @@ -420,7 +414,7 @@ public async Task> ResumeJob(int jobId) _unitOfWorkManage.CommitTran(); else _unitOfWorkManage.RollbackTran(); - } + } } else { @@ -480,7 +474,7 @@ public async Task> ReCovery(int jobId) _unitOfWorkManage.CommitTran(); else _unitOfWorkManage.RollbackTran(); - } + } } else { @@ -493,7 +487,7 @@ public async Task> ReCovery(int jobId) /// 获取任务命名空间 /// /// - [HttpGet] + [HttpGet] public MessageModel> GetTaskNameSpace() { var baseType = typeof(IJob); @@ -506,7 +500,7 @@ public MessageModel> GetTaskNameSpace() var implementTypes = types.Where(x => x.IsClass).Select(item => new QuartzReflectionViewModel { nameSpace = item.Namespace, nameClass = item.Name, remark = "" }).ToList(); return MessageModel>.Success("获取成功", implementTypes); } - + /// /// 立即执行任务 /// @@ -531,12 +525,9 @@ public async Task> ExecuteJob(int jobId) /// /// 获取任务运行日志 /// - /// - /// - /// /// [HttpGet] - public async Task>> GetTaskLogs(int jobId, int page = 1, int pageSize = 10, DateTime? runTimeStart =null, DateTime? runTimeEnd = null) + public async Task>> GetTaskLogs(int jobId, int page = 1, int pageSize = 10, DateTime? runTimeStart = null, DateTime? runTimeEnd = null) { var model = await _tasksLogServices.GetTaskLogs(jobId, page, pageSize, runTimeStart, runTimeEnd); return MessageModel>.Message(model.dataCount >= 0, "获取成功", model); @@ -544,12 +535,9 @@ public async Task>> GetTaskLogs(int jobId, int /// /// 任务概况 /// - /// - /// - /// /// [HttpGet] - public async Task> GetTaskOverview(int jobId, int page = 1, int pageSize = 10, DateTime? runTimeStart = null, DateTime? runTimeEnd = null, string type ="month") + public async Task> GetTaskOverview(int jobId, int page = 1, int pageSize = 10, DateTime? runTimeStart = null, DateTime? runTimeEnd = null, string type = "month") { var model = await _tasksLogServices.GetTaskOverview(jobId, runTimeStart, runTimeEnd, type); return MessageModel.Message(true, "获取成功", model); diff --git a/Blog.Core.Api/Controllers/TopicController.cs b/Blog.Core.Api/Controllers/TopicController.cs index 1fe2d4a3..308ad082 100644 --- a/Blog.Core.Api/Controllers/TopicController.cs +++ b/Blog.Core.Api/Controllers/TopicController.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using Blog.Core.IServices; +using Blog.Core.IServices; using Blog.Core.Model; using Blog.Core.Model.Models; using Microsoft.AspNetCore.Authorization; diff --git a/Blog.Core.Api/Controllers/TopicDetailController.cs b/Blog.Core.Api/Controllers/TopicDetailController.cs index 264fe2df..55af1a75 100644 --- a/Blog.Core.Api/Controllers/TopicDetailController.cs +++ b/Blog.Core.Api/Controllers/TopicDetailController.cs @@ -1,7 +1,4 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using Blog.Core.Common.Helper; +using Blog.Core.Common.Helper; using Blog.Core.IServices; using Blog.Core.Model; using Blog.Core.Model.Models; diff --git a/Blog.Core.Api/Controllers/TransactionController.cs b/Blog.Core.Api/Controllers/TransactionController.cs index dd6b0384..67ab576e 100644 --- a/Blog.Core.Api/Controllers/TransactionController.cs +++ b/Blog.Core.Api/Controllers/TransactionController.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Blog.Core.IServices; +using Blog.Core.IServices; using Blog.Core.Model; using Blog.Core.Model.Models; using Blog.Core.Repository.UnitOfWorks; diff --git a/Blog.Core.Api/Controllers/UserController.cs b/Blog.Core.Api/Controllers/UserController.cs index 6a2a18fa..43f7f610 100644 --- a/Blog.Core.Api/Controllers/UserController.cs +++ b/Blog.Core.Api/Controllers/UserController.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using AutoMapper; +using AutoMapper; using Blog.Core.AuthHelper.OverWrite; using Blog.Core.Common.Helper; using Blog.Core.Common.HttpContextUser; @@ -13,7 +9,6 @@ using Blog.Core.Repository.UnitOfWorks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; namespace Blog.Core.Controllers { diff --git a/Blog.Core.Api/Controllers/UserRoleController.cs b/Blog.Core.Api/Controllers/UserRoleController.cs index d14d6a73..c21ec6f4 100644 --- a/Blog.Core.Api/Controllers/UserRoleController.cs +++ b/Blog.Core.Api/Controllers/UserRoleController.cs @@ -1,5 +1,4 @@ -using System.Threading.Tasks; -using AutoMapper; +using AutoMapper; using Blog.Core.IServices; using Blog.Core.Model; using Blog.Core.Model.Models; diff --git a/Blog.Core.Api/Controllers/WeChatCompanyController.cs b/Blog.Core.Api/Controllers/WeChatCompanyController.cs index 4fa6eea5..dc12930b 100644 --- a/Blog.Core.Api/Controllers/WeChatCompanyController.cs +++ b/Blog.Core.Api/Controllers/WeChatCompanyController.cs @@ -1,4 +1,3 @@ -using System.Threading.Tasks; using Blog.Core.IServices; using Blog.Core.Model; using Blog.Core.Model.Models; diff --git a/Blog.Core.Api/Controllers/WeChatConfigController.cs b/Blog.Core.Api/Controllers/WeChatConfigController.cs index c597cb3f..1f3b705d 100644 --- a/Blog.Core.Api/Controllers/WeChatConfigController.cs +++ b/Blog.Core.Api/Controllers/WeChatConfigController.cs @@ -1,4 +1,3 @@ -using System.Threading.Tasks; using Blog.Core.IServices; using Blog.Core.Model; using Blog.Core.Model.Models; diff --git a/Blog.Core.Api/Controllers/WeChatController.cs b/Blog.Core.Api/Controllers/WeChatController.cs index 4c4877f6..9927eb78 100644 --- a/Blog.Core.Api/Controllers/WeChatController.cs +++ b/Blog.Core.Api/Controllers/WeChatController.cs @@ -1,11 +1,8 @@ -using System.IO; -using System.Threading.Tasks; -using Blog.Core.IServices; +using Blog.Core.IServices; using Blog.Core.Model; using Blog.Core.Model.ViewModels; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; namespace Blog.Core.Controllers { diff --git a/Blog.Core.Api/Controllers/WeChatPushLogController.cs b/Blog.Core.Api/Controllers/WeChatPushLogController.cs index 1fe1603d..af168091 100644 --- a/Blog.Core.Api/Controllers/WeChatPushLogController.cs +++ b/Blog.Core.Api/Controllers/WeChatPushLogController.cs @@ -1,4 +1,3 @@ -using System.Threading.Tasks; using Blog.Core.IServices; using Blog.Core.Model; using Blog.Core.Model.Models; diff --git a/Blog.Core.Api/Controllers/WeChatSubController.cs b/Blog.Core.Api/Controllers/WeChatSubController.cs index bd8d1759..94f982d2 100644 --- a/Blog.Core.Api/Controllers/WeChatSubController.cs +++ b/Blog.Core.Api/Controllers/WeChatSubController.cs @@ -1,4 +1,3 @@ -using System.Threading.Tasks; using Blog.Core.IServices; using Blog.Core.Model; using Blog.Core.Model.Models; diff --git a/Blog.Core.Services/TasksLogServices.cs b/Blog.Core.Services/TasksLogServices.cs index eac9a739..49393cd4 100644 --- a/Blog.Core.Services/TasksLogServices.cs +++ b/Blog.Core.Services/TasksLogServices.cs @@ -5,12 +5,10 @@ using Blog.Core.Model.Models; using Blog.Core.Services.BASE; using Blog.Core.Common.Extensions; -using System.Drawing.Printing; using SqlSugar; using Blog.Core.Model; using System.Collections.Generic; using System.Linq; -using OfficeOpenXml.FormulaParsing.Excel.Functions.DateTime; namespace Blog.Core.Services { @@ -43,9 +41,8 @@ public async Task> GetTaskLogs(int jobId, int page, int intP .ToPageListAsync(page, intPageSize, totalCount); return new PageModel(page, totalCount, intPageSize, data); } - public async Task GetTaskOverview(int jobId, DateTime? runTime, DateTime? endTime,string type) + public async Task GetTaskOverview(int jobId, DateTime? runTime, DateTime? endTime, string type) { - //按年 if ("year".Equals(type)) { @@ -54,7 +51,7 @@ public async Task GetTaskOverview(int jobId, DateTime? runTime, DateTime var dayArray = new List(); while (days >= 0) { - dayArray.Add(new DateTime(runTime.Value.Year + days,1,1)); + dayArray.Add(new DateTime(runTime.Value.Year + days, 1, 1)); days--; } var queryableLeft = this.Db.Reportable(dayArray).ToQueryable(); @@ -66,10 +63,11 @@ public async Task GetTaskOverview(int jobId, DateTime? runTime, DateTime .Select((x1, x2) => new { 执行次数 = SqlFunc.AggregateSum(SqlFunc.IIF(x2.Id > 0, 1, 0)), - date = x1.ColumnName.Year.ToString()+"年" + date = x1.ColumnName.Year.ToString() + "年" }).ToList().OrderBy(t => t.date); return list; - }else if ("month".Equals(type)) + } + else if ("month".Equals(type)) { //按月 var queryableLeft = this.Db.Reportable(ReportableDateType.MonthsInLast1years).ToQueryable(); //生成月份 //ReportableDateType.MonthsInLast1yea 表式近一年月份 并且queryable之后还能在where过滤 @@ -78,7 +76,7 @@ public async Task GetTaskOverview(int jobId, DateTime? runTime, DateTime //月份和表JOIN var list = queryableLeft .LeftJoin(queryableRight, (x1, x2) => x2.RunTime.ToString("MM月") == x1.ColumnName.ToString("MM月")) - + .GroupBy((x1, x2) => x1.ColumnName) .Select((x1, x2) => new { @@ -87,8 +85,10 @@ public async Task GetTaskOverview(int jobId, DateTime? runTime, DateTime date = x1.ColumnName.ToString("MM月") } ).ToList().OrderBy(t => t.date); + await Task.CompletedTask; return list; - }else if ("day".Equals(type)) + } + else if ("day".Equals(type)) { //按日 var time = runTime.Value; @@ -97,7 +97,7 @@ public async Task GetTaskOverview(int jobId, DateTime? runTime, DateTime var queryableLeft = this.Db.Reportable(dayArray).ToQueryable(); var star = Convert.ToDateTime(runTime.Value.ToString("yyyy-MM-01 00:00:00")); var end = Convert.ToDateTime(runTime.Value.ToString($"yyyy-MM-{days} 23:59:59")); - var queryableRight = this.Db.Queryable().Where((x) => x.RunTime >= star && x.RunTime <= end); ;; //声名表 + var queryableRight = this.Db.Queryable().Where((x) => x.RunTime >= star && x.RunTime <= end); ; ; //声名表 var list = this.Db.Queryable(queryableLeft, queryableRight, JoinType.Left, (x1, x2) => x1.ColumnName.Date == x2.RunTime.Date) @@ -107,26 +107,30 @@ public async Task GetTaskOverview(int jobId, DateTime? runTime, DateTime 执行次数 = SqlFunc.AggregateSum(SqlFunc.IIF(x2.Id > 0, 1, 0)), date = x1.ColumnName.Day }).ToList().OrderBy(t => t.date); + await Task.CompletedTask; return list; - }else if ("hour".Equals(type)) + } + else if ("hour".Equals(type)) { //按小时 var time = runTime.Value; var days = 24; var dayArray = Enumerable.Range(0, days).Select(it => Convert.ToDateTime(time.ToString($"yyyy-MM-dd {it.ToString().PadLeft(2, '0')}:00:00"))).ToList();//转成时间数组 var queryableLeft = this.Db.Reportable(dayArray).ToQueryable(); - var queryableRight = this.Db.Queryable().Where((x) => x.RunTime >= runTime.Value.Date && x.RunTime<= runTime.Value.Date.AddDays(1).AddMilliseconds(-1)); //声名表 + var queryableRight = this.Db.Queryable().Where((x) => x.RunTime >= runTime.Value.Date && x.RunTime <= runTime.Value.Date.AddDays(1).AddMilliseconds(-1)); //声名表 var list = this.Db.Queryable(queryableLeft, queryableRight, JoinType.Left, - (x1, x2) => x1.ColumnName.Hour == x2.RunTime.Hour) + (x1, x2) => x1.ColumnName.Hour == x2.RunTime.Hour) .GroupBy((x1, x2) => x1.ColumnName) .Select((x1, x2) => new { 执行次数 = SqlFunc.AggregateSum(SqlFunc.IIF(x2.Id > 0, 1, 0)), date = x1.ColumnName.Hour }).ToList().OrderBy(t => t.date); + await Task.CompletedTask; return list; } + await Task.CompletedTask; return null; } } From d1288b103ecf582d5491712f63e5dbd8c05048dc Mon Sep 17 00:00:00 2001 From: ansonzhang <3143422472@qq.com> Date: Thu, 23 Mar 2023 16:39:57 +0800 Subject: [PATCH 04/84] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 29d427ec..ee36dc2c 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,8 @@ Blog.Core 开箱即用的企业级前后端分离【 .NET Core6.0 Api + Vue 2.x #### 框架模块: - [x] 采用`仓储+服务+接口`的形式封装框架; -- [x] 异步 async/await 开发; +- [x] 自定义项目模板 `CreateYourProject.bat` ,可以一键生成自己的项目;🎶 +- [x] 异步 async/await 开发; - [x] 接入国产数据库ORM组件 —— SqlSugar,封装数据库操作,支持级联操作; - [x] 支持自由切换多种数据库,MySql/SqlServer/Sqlite/Oracle/Postgresql/达梦/人大金仓; - [x] 实现项目启动,自动生成种子数据 ✨; From 798a7f64228960d181fb296aace484a915a2b919 Mon Sep 17 00:00:00 2001 From: hudingwen <765472804@qq.com> Date: Fri, 24 Mar 2023 21:24:24 +0800 Subject: [PATCH 05/84] =?UTF-8?q?=E8=AE=A2=E9=98=85=E9=93=BE=E6=8E=A5?= =?UTF-8?q?=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Api/Controllers/TrojanController.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Blog.Core.Api/Controllers/TrojanController.cs b/Blog.Core.Api/Controllers/TrojanController.cs index 8ec64c8a..cdb010e8 100644 --- a/Blog.Core.Api/Controllers/TrojanController.cs +++ b/Blog.Core.Api/Controllers/TrojanController.cs @@ -15,6 +15,7 @@ using Blog.Core.Model.ViewModels; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using OfficeOpenXml.FormulaParsing.Excel.Functions.Math; namespace Blog.Core.Controllers { @@ -357,9 +358,12 @@ public async Task>> AddUrlServers(TrojanUrlS private string GetSplice(TrojanServers item,string passwordshow) { if ("0".Equals(item.servertype)) - return $"trojan://{passwordshow}@{item.serveraddress}:{item.serverport}?allowinsecure=0&tfo=0&peer={(string.IsNullOrEmpty(item.serverpeer) ? item.serverpeer : item.serveraddress)}#{item.servername}"; + return $"trojan://{passwordshow}@{item.serveraddress}:{item.serverport}?allowinsecure=0&tfo=0&fp=chrome&peer={(string.IsNullOrEmpty(item.serverpeer) ? item.serverpeer : item.serveraddress)}#{item.servername}"; else if ("1".Equals(item.servertype)) - return $"trojan://{passwordshow}@{item.serveraddress}:{item.serverport}?wspath={item.serverpath}&ws=1&peer={(string.IsNullOrEmpty(item.serverpeer) ? item.serverpeer : item.serveraddress)}#{item.servername}"; + { + var sni = string.IsNullOrEmpty(item.serverpeer) ? item.serverpeer : item.serveraddress; + return $"trojan://{passwordshow}@{item.serveraddress}:{item.serverport}?wspath={item.serverpath}&ws=1&peer={sni}&path={item.serverpath}&host={sni}&fp=chrome&type=ws&sni={sni}#{item.servername}"; + } else return $"servertype:({item.servertype})错误"; } From 042c4a6c4497437ac09f93d4c9ec8bbb178a92c1 Mon Sep 17 00:00:00 2001 From: hudingwen <765472804@qq.com> Date: Sun, 26 Mar 2023 15:36:45 +0800 Subject: [PATCH 06/84] fix bug --- Blog.Core.Api/Controllers/TrojanController.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Blog.Core.Api/Controllers/TrojanController.cs b/Blog.Core.Api/Controllers/TrojanController.cs index cdb010e8..d8d005dc 100644 --- a/Blog.Core.Api/Controllers/TrojanController.cs +++ b/Blog.Core.Api/Controllers/TrojanController.cs @@ -357,11 +357,12 @@ public async Task>> AddUrlServers(TrojanUrlS } private string GetSplice(TrojanServers item,string passwordshow) { + var sni = string.IsNullOrEmpty(item.serverpeer) ? item.serveraddress : item.serverpeer; if ("0".Equals(item.servertype)) - return $"trojan://{passwordshow}@{item.serveraddress}:{item.serverport}?allowinsecure=0&tfo=0&fp=chrome&peer={(string.IsNullOrEmpty(item.serverpeer) ? item.serverpeer : item.serveraddress)}#{item.servername}"; + return $"trojan://{passwordshow}@{item.serveraddress}:{item.serverport}?allowinsecure=0&tfo=0&fp=chrome&peer={sni}&host={sni}&sni={sni}#{item.servername}"; else if ("1".Equals(item.servertype)) { - var sni = string.IsNullOrEmpty(item.serverpeer) ? item.serverpeer : item.serveraddress; + return $"trojan://{passwordshow}@{item.serveraddress}:{item.serverport}?wspath={item.serverpath}&ws=1&peer={sni}&path={item.serverpath}&host={sni}&fp=chrome&type=ws&sni={sni}#{item.servername}"; } else From 67efee3ee1bd7d8337ee3e4af2667c1139e93b11 Mon Sep 17 00:00:00 2001 From: Nine Date: Thu, 30 Mar 2023 15:38:17 +0800 Subject: [PATCH 07/84] =?UTF-8?q?Sqlsugar=20=E5=88=86=E8=A1=A8=20CRUD=20de?= =?UTF-8?q?mo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Api/Blog.Core.xml | 44 ++++ .../Controllers/SplitDemoController.cs | 199 ++++++++++++++++++ Blog.Core.Api/Program.cs | 4 + Blog.Core.Api/Startup.cs | 4 + Blog.Core.Common/Helper/NumberConverter.cs | 174 +++++++++++++++ Blog.Core.Common/Seed/DBSeed.cs | 2 +- Blog.Core.IServices/BASE/IBaseServices.cs | 10 +- Blog.Core.IServices/ISplitDemoServices.cs | 15 ++ Blog.Core.Model/Models/SplitDemo.cs | 27 +++ Blog.Core.Repository/BASE/BaseRepository.cs | 72 ++++++- Blog.Core.Repository/BASE/IBaseRepository.cs | 40 ++++ Blog.Core.Services/BASE/BaseServices.cs | 31 +++ Blog.Core.Services/SplitDemoServices.cs | 23 ++ 13 files changed, 642 insertions(+), 3 deletions(-) create mode 100644 Blog.Core.Api/Controllers/SplitDemoController.cs create mode 100644 Blog.Core.Common/Helper/NumberConverter.cs create mode 100644 Blog.Core.IServices/ISplitDemoServices.cs create mode 100644 Blog.Core.Model/Models/SplitDemo.cs create mode 100644 Blog.Core.Services/SplitDemoServices.cs diff --git a/Blog.Core.Api/Blog.Core.xml b/Blog.Core.Api/Blog.Core.xml index 89cb3213..9891d5d9 100644 --- a/Blog.Core.Api/Blog.Core.xml +++ b/Blog.Core.Api/Blog.Core.xml @@ -1249,6 +1249,50 @@ + + + 分表demo + + + + + 分页获取数据 + + + + + + + + + + + 根据ID获取信息 + + + + + + + 添加一条测试数据 + + + + + + + 修改一条测试数据 + + + + + + + 根据id删除数据 + + + + 多租户-多库方案 测试 diff --git a/Blog.Core.Api/Controllers/SplitDemoController.cs b/Blog.Core.Api/Controllers/SplitDemoController.cs new file mode 100644 index 00000000..fb3c03c6 --- /dev/null +++ b/Blog.Core.Api/Controllers/SplitDemoController.cs @@ -0,0 +1,199 @@ +using Blog.Core.IServices; +using Blog.Core.Model; +using Blog.Core.Model.Models; +using Blog.Core.Repository.UnitOfWorks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Linq.Expressions; + +namespace Blog.Core.Api.Controllers +{ + /// + /// 分表demo + /// + [Route("api/[controller]/[action]")] + [ApiController] + [Authorize(Permissions.Name)] + public class SplitDemoController : ControllerBase + { + readonly ISplitDemoServices splitDemoServices; + readonly IUnitOfWorkManage unitOfWorkManage; + public SplitDemoController(ISplitDemoServices _splitDemoServices, IUnitOfWorkManage _unitOfWorkManage) + { + splitDemoServices = _splitDemoServices; + unitOfWorkManage = _unitOfWorkManage; + } + + /// + /// 分页获取数据 + /// + /// + /// + /// + /// + /// + /// + [HttpGet] + [AllowAnonymous] + public async Task>> Get(DateTime beginTime, DateTime endTime, int page = 1, string key = "", int pageSize = 10) + { + if (string.IsNullOrEmpty(key) || string.IsNullOrWhiteSpace(key)) + { + key = ""; + } + Expression> whereExpression = a => (a.Name != null && a.Name.Contains(key)); + var data = await splitDemoServices.QueryPageSplit(whereExpression, beginTime, endTime, page, pageSize, " Id desc "); + return MessageModel>.Message(data.dataCount >= 0, "获取成功", data); + } + + /// + /// 根据ID获取信息 + /// + /// + /// + [HttpGet] + [AllowAnonymous] + public async Task> GetById(long id) + { + var data = new MessageModel(); + var model = await splitDemoServices.QueryByIdSplit(id); + if (model != null) + { + return MessageModel.Success("获取成功", model); + } + else + { + return MessageModel.Fail("获取失败"); + } + } + + /// + /// 添加一条测试数据 + /// + /// + /// + [HttpPost] + [AllowAnonymous] + public async Task> Post([FromBody] SplitDemo splitDemo) + { + var data = new MessageModel(); + unitOfWorkManage.BeginTran(); + var id = (await splitDemoServices.AddSplit(splitDemo)); + data.success = (id == null ? false : true); + try + { + if (data.success) + { + data.response = id.FirstOrDefault().ToString(); + data.msg = "添加成功"; + } + else + { + data.msg = "添加失败"; + } + } + catch (Exception) + { + throw; + } + finally + { + if (data.success) + unitOfWorkManage.CommitTran(); + else + unitOfWorkManage.RollbackTran(); + } + return data; + } + + /// + /// 修改一条测试数据 + /// + /// + /// + [HttpPut] + [AllowAnonymous] + public async Task> Put([FromBody] SplitDemo splitDemo) + { + var data = new MessageModel(); + if (splitDemo != null && splitDemo.Id > 0) + { + unitOfWorkManage.BeginTran(); + data.success = await splitDemoServices.UpdateSplit(splitDemo, splitDemo.CreateTime); + try + { + if (data.success) + { + data.msg = "修改成功"; + data.response = splitDemo?.Id.ObjToString(); + } + else + { + data.msg = "修改失败"; + } + } + catch (Exception) + { + throw; + } + finally + { + if (data.success) + unitOfWorkManage.CommitTran(); + else + unitOfWorkManage.RollbackTran(); + } + } + return data; + } + + /// + /// 根据id删除数据 + /// + /// + /// + [HttpDelete] + [AllowAnonymous] + public async Task> Delete(long id) + { + var data = new MessageModel(); + + var model = await splitDemoServices.QueryByIdSplit(id); + if (model != null) + { + unitOfWorkManage.BeginTran(); + data.success = await splitDemoServices.DeleteSplit(model,model.CreateTime); + try + { + data.response = id.ObjToString(); + if (data.success) + { + data.msg = "删除成功"; + } + else + { + data.msg = "删除失败"; + } + + } + catch (Exception) + { + throw; + } + finally + { + if (data.success) + unitOfWorkManage.CommitTran(); + else + unitOfWorkManage.RollbackTran(); + } + } + else + { + data.msg = "不存在"; + } + return data; + + } + } +} diff --git a/Blog.Core.Api/Program.cs b/Blog.Core.Api/Program.cs index f5223beb..2a75498e 100644 --- a/Blog.Core.Api/Program.cs +++ b/Blog.Core.Api/Program.cs @@ -7,6 +7,7 @@ using Blog.Core; using Blog.Core.Common; using Blog.Core.Common.Core; +using Blog.Core.Common.Helper; using Blog.Core.Common.LogHelper; using Blog.Core.Extensions; using Blog.Core.Extensions.Apollo; @@ -14,6 +15,7 @@ using Blog.Core.Filter; using Blog.Core.Hubs; using Blog.Core.IServices; +using Blog.Core.Model; using Blog.Core.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Controllers; @@ -111,6 +113,8 @@ //options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local; options.SerializerSettings.Converters.Add(new StringEnumConverter()); + //将long类型转为string + options.SerializerSettings.Converters.Add(new NumberConverter(NumberConverterShip.Int64)); }) //.AddFluentValidation(config => //{ diff --git a/Blog.Core.Api/Startup.cs b/Blog.Core.Api/Startup.cs index bc1630f1..4f99b621 100644 --- a/Blog.Core.Api/Startup.cs +++ b/Blog.Core.Api/Startup.cs @@ -3,6 +3,7 @@ using System.Text; using Autofac; using Blog.Core.Common; +using Blog.Core.Common.Helper; using Blog.Core.Common.LogHelper; using Blog.Core.Common.Seed; using Blog.Core.Extensions; @@ -10,6 +11,7 @@ using Blog.Core.Filter; using Blog.Core.Hubs; using Blog.Core.IServices; +using Blog.Core.Model; using Blog.Core.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Controllers; @@ -123,6 +125,8 @@ public void ConfigureServices(IServiceCollection services) options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local; //添加Enum转string options.SerializerSettings.Converters.Add(new StringEnumConverter()); + //将long类型转为string + options.SerializerSettings.Converters.Add(new NumberConverter(NumberConverterShip.Int64)); }); services.Replace(ServiceDescriptor.Transient()); diff --git a/Blog.Core.Common/Helper/NumberConverter.cs b/Blog.Core.Common/Helper/NumberConverter.cs new file mode 100644 index 00000000..27890faf --- /dev/null +++ b/Blog.Core.Common/Helper/NumberConverter.cs @@ -0,0 +1,174 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Blog.Core.Common.Helper +{ + /// + /// + /// 大数据json序列化重写 + /// + public sealed class NumberConverter : JsonConverter + { + /// + /// 转换成字符串的类型 + /// + private readonly NumberConverterShip _ship; + + /// + /// 大数据json序列化重写实例化 + /// + public NumberConverter() + { + _ship = (NumberConverterShip)0xFF; + } + + /// + /// 大数据json序列化重写实例化 + /// + /// 转换成字符串的类型 + public NumberConverter(NumberConverterShip ship) + { + _ship = ship; + } + + /// + /// + /// 确定此实例是否可以转换指定的对象类型。 + /// + /// 对象的类型。 + /// 如果此实例可以转换指定的对象类型,则为:true,否则为:false + public override bool CanConvert(Type objectType) + { + var typecode = Type.GetTypeCode(objectType.Name.Equals("Nullable`1") ? objectType.GetGenericArguments().First() : objectType); + switch (typecode) + { + case TypeCode.Decimal: + return (_ship & NumberConverterShip.Decimal) == NumberConverterShip.Decimal; + case TypeCode.Double: + return (_ship & NumberConverterShip.Double) == NumberConverterShip.Double; + case TypeCode.Int64: + return (_ship & NumberConverterShip.Int64) == NumberConverterShip.Int64; + case TypeCode.UInt64: + return (_ship & NumberConverterShip.UInt64) == NumberConverterShip.UInt64; + case TypeCode.Single: + return (_ship & NumberConverterShip.Single) == NumberConverterShip.Single; + default: return false; + } + } + + /// + /// + /// 读取对象的JSON表示。 + /// + /// 中读取。 + /// 对象的类型。 + /// 正在读取的对象的现有值。 + /// 调用的序列化器实例。 + /// 对象值。 + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + return AsType(reader.Value.ToString(), objectType); + } + + /// + /// 字符串格式数据转其他类型数据 + /// + /// 输入的字符串 + /// 目标格式 + /// 转换结果 + public static object AsType(string input, Type destinationType) + { + try + { + var converter = TypeDescriptor.GetConverter(destinationType); + if (converter.CanConvertFrom(typeof(string))) + { + return converter.ConvertFrom(null, null, input); + } + + converter = TypeDescriptor.GetConverter(typeof(string)); + if (converter.CanConvertTo(destinationType)) + { + return converter.ConvertTo(null, null, input, destinationType); + } + } + catch + { + return null; + } + return null; + } + + /// + /// + /// 写入对象的JSON表示形式。 + /// + /// 要写入的 。 + /// 要写入对象值 + /// 调用的序列化器实例。 + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value == null) + { + writer.WriteNull(); + } + else + { + var objectType = value.GetType(); + var typeCode = Type.GetTypeCode(objectType.Name.Equals("Nullable`1") ? objectType.GetGenericArguments().First() : objectType); + switch (typeCode) + { + case TypeCode.Decimal: + writer.WriteValue(((decimal)value).ToString("f6")); + break; + case TypeCode.Double: + writer.WriteValue(((double)value).ToString("f4")); + break; + case TypeCode.Single: + writer.WriteValue(((float)value).ToString("f2")); + break; + default: + writer.WriteValue(value.ToString()); + break; + } + } + } + } + + /// + /// 转换成字符串的类型 + /// + [Flags] + public enum NumberConverterShip + { + /// + /// 长整数 + /// + Int64 = 1, + + /// + /// 无符号长整数 + /// + UInt64 = 2, + + /// + /// 浮点数 + /// + Single = 4, + + /// + /// 双精度浮点数 + /// + Double = 8, + + /// + /// 大数字 + /// + Decimal =16 + } +} diff --git a/Blog.Core.Common/Seed/DBSeed.cs b/Blog.Core.Common/Seed/DBSeed.cs index 7b594109..fb13768a 100644 --- a/Blog.Core.Common/Seed/DBSeed.cs +++ b/Blog.Core.Common/Seed/DBSeed.cs @@ -109,7 +109,7 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) if (!myContext.Db.DbMaintenance.IsAnyTable(t.Name)) { Console.WriteLine(t.Name); - myContext.Db.CodeFirst.InitTables(t); + myContext.Db.CodeFirst.SplitTables().InitTables(t); } }); ConsoleHelper.WriteSuccessLine($"Tables created successfully!"); diff --git a/Blog.Core.IServices/BASE/IBaseServices.cs b/Blog.Core.IServices/BASE/IBaseServices.cs index 7856f8bf..4091b978 100644 --- a/Blog.Core.IServices/BASE/IBaseServices.cs +++ b/Blog.Core.IServices/BASE/IBaseServices.cs @@ -23,7 +23,7 @@ public interface IBaseServices where TEntity : class Task DeleteById(object id); Task Delete(TEntity model); - + Task DeleteByIds(object[] ids); Task Update(TEntity model); @@ -59,6 +59,14 @@ Task> QueryMuch( Expression> selectExpression, Expression> whereLambda = null) where T : class, new(); Task> QueryPage(PaginationModel pagination); + + #region 分表 + Task QueryByIdSplit(object objId); + Task> AddSplit(TEntity entity); + Task DeleteSplit(TEntity entity, DateTime dateTime); + Task UpdateSplit(TEntity entity, DateTime dateTime); + Task> QueryPageSplit(Expression> whereExpression, DateTime beginTime, DateTime endTime, int pageIndex = 1, int pageSize = 20, string orderByFields = null); + #endregion } } diff --git a/Blog.Core.IServices/ISplitDemoServices.cs b/Blog.Core.IServices/ISplitDemoServices.cs new file mode 100644 index 00000000..55215761 --- /dev/null +++ b/Blog.Core.IServices/ISplitDemoServices.cs @@ -0,0 +1,15 @@ + + +using Blog.Core.IServices.BASE; +using Blog.Core.Model.Models; +using System.Threading.Tasks; + +namespace Blog.Core.IServices +{ + /// + /// sysUserInfoServices + /// + public interface ISplitDemoServices : IBaseServices + { + } +} diff --git a/Blog.Core.Model/Models/SplitDemo.cs b/Blog.Core.Model/Models/SplitDemo.cs new file mode 100644 index 00000000..26329935 --- /dev/null +++ b/Blog.Core.Model/Models/SplitDemo.cs @@ -0,0 +1,27 @@ +using Newtonsoft.Json; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace Blog.Core.Model.Models +{ + [SplitTable(SplitType.Day)]//按年分表 (自带分表支持 年、季、月、周、日) + [SugarTable("SplitDemo_{year}{month}{day}")]//3个变量必须要有,这么设计为了兼容开始按年,后面改成按月、按日 + public class SplitDemo + { + [SugarColumn(IsPrimaryKey = true)] + public long Id { get; set; } + + public string Name { get; set; } + + [SugarColumn(IsNullable = true)]//设置为可空字段 (更多用法看文档 迁移) + public DateTime UpdateTime { get; set; } + + [SplitField] //分表字段 在插入的时候会根据这个字段插入哪个表,在更新删除的时候用这个字段找出相关表 + public DateTime CreateTime { get; set; } + } +} diff --git a/Blog.Core.Repository/BASE/BaseRepository.cs b/Blog.Core.Repository/BASE/BaseRepository.cs index 828f9506..3048baa8 100644 --- a/Blog.Core.Repository/BASE/BaseRepository.cs +++ b/Blog.Core.Repository/BASE/BaseRepository.cs @@ -5,6 +5,7 @@ using Blog.Core.Model.Models; using Blog.Core.Model.Tenants; using Blog.Core.Repository.UnitOfWorks; +using OfficeOpenXml.FormulaParsing.Excel.Functions.DateTime; using SqlSugar; using System; using System.Collections.Generic; @@ -127,7 +128,6 @@ public async Task Add(TEntity entity) return await insert.ExecuteReturnIdentityAsync(); } - /// /// 写入实体数据 /// @@ -557,5 +557,75 @@ public async Task> QueryTabsPage( // groupName = s.groupName, // jobName = s.jobName // }, exp, s => new { s.uID, s.uRealName, s.groupName, s.jobName }, model.currentPage, model.pageSize, model.orderField + " " + model.orderType); + #region Split分表基础接口 (基础CRUD) + /// + /// 分页查询[使用版本,其他分页未测试] + /// + /// 条件表达式 + /// 页码(下标0) + /// 页大小 + /// 排序字段,如name asc,age desc + /// + public async Task> QueryPageSplit(Expression> whereExpression, DateTime beginTime, DateTime endTime, int pageIndex = 1, int pageSize = 20, string orderByFields = null) + { + RefAsync totalCount = 0; + var list = await _db.Queryable().SplitTable(beginTime, endTime) + .OrderByIF(!string.IsNullOrEmpty(orderByFields), orderByFields) + .WhereIF(whereExpression != null, whereExpression) + .ToPageListAsync(pageIndex, pageSize, totalCount); + var data= new PageModel(pageIndex, totalCount, pageSize, list); + return data; + } + /// + /// 写入实体数据 + /// + /// 数据实体 + /// + public async Task> AddSplit(TEntity entity) + { + var insert = _db.Insertable(entity).SplitTable(); + //插入并返回雪花ID并且自动赋值ID  + return await insert.ExecuteReturnSnowflakeIdListAsync(); + } + + /// + /// 更新实体数据 + /// + /// 数据实体 + /// + public async Task UpdateSplit(TEntity entity, DateTime dateTime) + { + //直接根据实体集合更新 (全自动 找表更新) + //return await _db.Updateable(entity).SplitTable().ExecuteCommandAsync();//,SplitTable不能少 + + //精准找单个表 + var tableName = _db.SplitHelper().GetTableName(dateTime);//根据时间获取表名 + return await _db.Updateable(entity).AS(tableName).ExecuteCommandHasChangeAsync(); + } + /// + /// 删除数据 + /// + /// + /// + /// + public async Task DeleteSplit(TEntity entity,DateTime dateTime) + { + ////直接根据实体集合删除 (全自动 找表插入),返回受影响数 + //return await _db.Deleteable(entity).SplitTable().ExecuteCommandAsync();//,SplitTable不能少 + + //精准找单个表 + var tableName = _db.SplitHelper().GetTableName(dateTime);//根据时间获取表名 + return await _db.Deleteable().AS(tableName).Where(entity).ExecuteCommandHasChangeAsync(); + } + /// + /// 根据ID查找数据 + /// + /// + /// + public async Task QueryByIdSplit(object objId) + { + return await _db.Queryable().In(objId).SplitTable(tabs => tabs).SingleAsync(); + } + #endregion } } \ No newline at end of file diff --git a/Blog.Core.Repository/BASE/IBaseRepository.cs b/Blog.Core.Repository/BASE/IBaseRepository.cs index 29783505..5f70a4be 100644 --- a/Blog.Core.Repository/BASE/IBaseRepository.cs +++ b/Blog.Core.Repository/BASE/IBaseRepository.cs @@ -208,5 +208,45 @@ Task> QueryTabsPage( int pageIndex = 1, int pageSize = 20, string orderByFields = null); + + #region 分表 + /// + /// 通过ID查询 + /// + /// + /// + Task QueryByIdSplit(object objId); + /// + /// 自动分表插入 + /// + /// + /// + Task> AddSplit(TEntity entity); + /// + /// 删除 + /// + /// + /// + /// + Task DeleteSplit(TEntity entity, DateTime dateTime); + /// + /// 更新 + /// + /// + /// + /// + Task UpdateSplit(TEntity entity, DateTime dateTime); + /// + /// 分页查询 + /// + /// + /// + /// + /// + /// + /// + /// + Task> QueryPageSplit(Expression> whereExpression, DateTime beginTime, DateTime endTime, int pageIndex = 1, int pageSize = 20, string orderByFields = null); + #endregion } } diff --git a/Blog.Core.Services/BASE/BaseServices.cs b/Blog.Core.Services/BASE/BaseServices.cs index f8105165..7ee55eb1 100644 --- a/Blog.Core.Services/BASE/BaseServices.cs +++ b/Blog.Core.Services/BASE/BaseServices.cs @@ -332,5 +332,36 @@ public async Task> QueryPage(PaginationModel pagination) var express = DynamicLinqFactory.CreateLambda(pagination.Conditions); return await QueryPage(express, pagination.PageIndex, pagination.PageSize, pagination.OrderByFileds); } + #region 分表 + public async Task> AddSplit(TEntity entity) + { + return await BaseDal.AddSplit(entity); + } + public async Task UpdateSplit(TEntity entity, DateTime dateTime) + { + return await BaseDal.UpdateSplit(entity, dateTime); + } + + /// + /// 根据实体删除一条数据 + /// + /// 博文实体类 + /// + public async Task DeleteSplit(TEntity entity, DateTime dateTime) + { + return await BaseDal.DeleteSplit(entity, dateTime); + } + + public async Task QueryByIdSplit(object objId) + { + return await BaseDal.QueryByIdSplit(objId); + } + public async Task> QueryPageSplit(Expression> whereExpression, DateTime beginTime, DateTime endTime, + int pageIndex = 1, int pageSize = 20, string orderByFields = null) + { + return await BaseDal.QueryPageSplit(whereExpression, beginTime, endTime, + pageIndex, pageSize, orderByFields); + } + #endregion } } \ No newline at end of file diff --git a/Blog.Core.Services/SplitDemoServices.cs b/Blog.Core.Services/SplitDemoServices.cs new file mode 100644 index 00000000..cf8e2cc1 --- /dev/null +++ b/Blog.Core.Services/SplitDemoServices.cs @@ -0,0 +1,23 @@ +using Blog.Core.IRepository.Base; +using Blog.Core.IServices; +using Blog.Core.Model.Models; +using Blog.Core.Services.BASE; +using System.Linq; +using System.Threading.Tasks; + +namespace Blog.Core.FrameWork.Services +{ + /// + /// sysUserInfoServices + /// + public class SplitDemoServices : BaseServices, ISplitDemoServices + { + private readonly IBaseRepository _splitDemoRepository; + public SplitDemoServices(IBaseRepository splitDemoRepository) + { + _splitDemoRepository = splitDemoRepository; + } + + + } +} From 7b1f3a4c740f6cc7302219a12bb1033ab583018c Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Thu, 30 Mar 2023 15:49:30 +0800 Subject: [PATCH 08/84] feat: :airplane: change id to long --- Blog.Core.Api/Blog.Core.Model.xml | 5 --- Blog.Core.Api/Blog.Core.xml | 4 +- .../Controllers/DbFirst/MigrateController.cs | 30 ++++++------- .../Controllers/DepartmentController.cs | 2 +- .../Controllers/PermissionController.cs | 24 +++++----- Blog.Core.Api/Controllers/UserController.cs | 6 +-- .../wwwroot/BlogCore.Data.json/Permission.tsv | 44 +++++++++---------- .../RoleModulePermission.tsv | 12 ++--- Blog.Core.Common/DB/Aop/SqlsugarAop.cs | 10 ++++- Blog.Core.Common/Helper/RecursionHelper.cs | 12 ++--- Blog.Core.Common/Seed/DBSeed.cs | 30 +++++++------ .../IRoleModulePermissionServices.cs | 2 +- Blog.Core.Model/Models/AccessTrendLog.cs | 2 +- Blog.Core.Model/Models/Advertisement.cs | 2 +- Blog.Core.Model/Models/BlogArticle.cs | 4 +- Blog.Core.Model/Models/BlogArticleComment.cs | 2 +- Blog.Core.Model/Models/Department.cs | 2 +- Blog.Core.Model/Models/GblLogAudit.cs | 4 +- Blog.Core.Model/Models/Guestbook.cs | 6 +-- Blog.Core.Model/Models/Modules.cs | 2 +- Blog.Core.Model/Models/OperateLog.cs | 2 +- Blog.Core.Model/Models/PasswordLib.cs | 6 +-- Blog.Core.Model/Models/Permission.cs | 2 +- Blog.Core.Model/Models/Role.cs | 2 +- .../Models/RoleModulePermission.cs | 2 +- Blog.Core.Model/Models/RootEntity.cs | 15 ------- Blog.Core.Model/Models/TasksLog.cs | 4 +- Blog.Core.Model/Models/TasksQz.cs | 2 +- Blog.Core.Model/Models/TestModels.cs | 6 +-- Blog.Core.Model/Models/Topic.cs | 2 +- Blog.Core.Model/Models/TopicDetail.cs | 2 +- Blog.Core.Model/Models/UserRole.cs | 6 +-- Blog.Core.Model/Models/sysUserInfo.cs | 4 +- Blog.Core.Model/ViewModels/BlogViewModels.cs | 4 +- .../IRoleModulePermissionRepository.cs | 2 +- .../RoleModulePermissionRepository.cs | 2 +- .../RoleModulePermissionServices.cs | 2 +- 37 files changed, 130 insertions(+), 140 deletions(-) delete mode 100644 Blog.Core.Model/Models/RootEntity.cs diff --git a/Blog.Core.Api/Blog.Core.Model.xml b/Blog.Core.Api/Blog.Core.Model.xml index 6bbfd4fe..fe62d8e2 100644 --- a/Blog.Core.Api/Blog.Core.Model.xml +++ b/Blog.Core.Api/Blog.Core.Model.xml @@ -1795,11 +1795,6 @@ 修改时间 - - - ID - - 部门表 diff --git a/Blog.Core.Api/Blog.Core.xml b/Blog.Core.Api/Blog.Core.xml index 89cb3213..faf9d522 100644 --- a/Blog.Core.Api/Blog.Core.xml +++ b/Blog.Core.Api/Blog.Core.xml @@ -413,14 +413,14 @@ - + 获取路由树 - + 获取路由树 diff --git a/Blog.Core.Api/Controllers/DbFirst/MigrateController.cs b/Blog.Core.Api/Controllers/DbFirst/MigrateController.cs index 8006d1fb..7865cc69 100644 --- a/Blog.Core.Api/Controllers/DbFirst/MigrateController.cs +++ b/Blog.Core.Api/Controllers/DbFirst/MigrateController.cs @@ -81,7 +81,7 @@ public async Task> DataMigrateFromOld2New() InitPermissionTree(permissions, permissionsAllList, apiList); var actionPermissionIds = permissionsAllList.Where(d => d.Id >= filterPermissionId).Select(d => d.Id).ToList(); - List filterPermissionIds = new(); + List filterPermissionIds = new(); FilterPermissionTree(permissionsAllList, actionPermissionIds, filterPermissionIds); permissions = permissions.Where(d => filterPermissionIds.Contains(d.Id)).ToList(); @@ -93,10 +93,10 @@ public async Task> DataMigrateFromOld2New() // 1、保持菜单和接口 await SavePermissionTreeAsync(permissions, pms); - var rid = 0; - var pid = 0; - var mid = 0; - var rpmid = 0; + long rid = 0; + long pid = 0; + long mid = 0; + long rpmid = 0; // 2、保存关系表 foreach (var item in rmps) @@ -116,8 +116,8 @@ public async Task> DataMigrateFromOld2New() } } - pid = (pms.FirstOrDefault(d => d.PidOld == item.PermissionId)?.PidNew).ObjToInt(); - mid = (pms.FirstOrDefault(d => d.MidOld == item.ModuleId)?.MidNew).ObjToInt(); + pid = (pms.FirstOrDefault(d => d.PidOld == item.PermissionId)?.PidNew).ObjToLong(); + mid = (pms.FirstOrDefault(d => d.MidOld == item.ModuleId)?.MidNew).ObjToLong(); // 关系 if (rid > 0 && pid > 0) { @@ -282,7 +282,7 @@ private void InitPermissionTree(List permissionsTree, List permissionsAll, List actionPermissionId, List filterPermissionIds) + private void FilterPermissionTree(List permissionsAll, List actionPermissionId, List filterPermissionIds) { actionPermissionId = actionPermissionId.Distinct().ToList(); var doneIds = permissionsAll.Where(d => actionPermissionId.Contains(d.Id) && d.Pid == 0).Select(d => d.Id).ToList(); @@ -295,7 +295,7 @@ private void FilterPermissionTree(List permissionsAll, List act } } - private async Task SavePermissionTreeAsync(List permissionsTree, List pms, int permissionId = 0) + private async Task SavePermissionTreeAsync(List permissionsTree, List pms, long permissionId = 0) { var parendId = permissionId; @@ -304,9 +304,9 @@ private async Task SavePermissionTreeAsync(List permissionsTree, Lis PM pm = new PM(); // 保留原始主键id pm.PidOld = item.Id; - pm.MidOld = (item.Module?.Id).ObjToInt(); + pm.MidOld = (item.Module?.Id).ObjToLong(); - var mid = 0; + long mid = 0; // 接口 if (item.Module != null) { @@ -351,9 +351,9 @@ private async Task SavePermissionTreeAsync(List permissionsTree, Lis public class PM { - public int PidOld { get; set; } - public int MidOld { get; set; } - public int PidNew { get; set; } - public int MidNew { get; set; } + public long PidOld { get; set; } + public long MidOld { get; set; } + public long PidNew { get; set; } + public long MidNew { get; set; } } } diff --git a/Blog.Core.Api/Controllers/DepartmentController.cs b/Blog.Core.Api/Controllers/DepartmentController.cs index bca7bf6d..b8174e90 100644 --- a/Blog.Core.Api/Controllers/DepartmentController.cs +++ b/Blog.Core.Api/Controllers/DepartmentController.cs @@ -83,7 +83,7 @@ public async Task>> GetTreeTable(long f = 0, strin foreach (var item in departments) { - List pidarr = new() { }; + List pidarr = new() { }; var parent = departmentList.FirstOrDefault(d => d.Id == item.Pid); while (parent != null) diff --git a/Blog.Core.Api/Controllers/PermissionController.cs b/Blog.Core.Api/Controllers/PermissionController.cs index 70f8f689..9277a36b 100644 --- a/Blog.Core.Api/Controllers/PermissionController.cs +++ b/Blog.Core.Api/Controllers/PermissionController.cs @@ -99,7 +99,7 @@ public async Task>> Get(int page = 1, string var permissionAll = await _permissionServices.Query(d => d.IsDeleted != true); foreach (var item in permissionsView) { - List pidarr = new List + List pidarr = new() { item.Pid }; @@ -177,7 +177,7 @@ public async Task>> GetTreeTable(int f = 0, string foreach (var item in permissions) { - List pidarr = new List { }; + List pidarr = new() { }; var parent = permissionsList.FirstOrDefault(d => d.Id == item.Pid); while (parent != null) @@ -353,13 +353,13 @@ orderby child.Id /// /// [HttpGet] - public async Task> GetNavigationBar(int uid) + public async Task> GetNavigationBar(long uid) { var data = new MessageModel(); var uidInHttpcontext1 = 0; - var roleIds = new List(); + var roleIds = new List(); // ids4和jwt切换 if (Permissions.IsUseIds4) { @@ -369,13 +369,13 @@ public async Task> GetNavigationBar(int uid) select item.Value).FirstOrDefault().ObjToInt(); roleIds = (from item in _httpContext.HttpContext.User.Claims where item.Type == "role" - select item.Value.ObjToInt()).ToList(); + select item.Value.ObjToLong()).ToList(); } else { // jwt uidInHttpcontext1 = ((JwtHelper.SerializeJwt(_httpContext.HttpContext.Request.Headers["Authorization"].ObjToString().Replace("Bearer ", "")))?.Uid).ObjToInt(); - roleIds = (await _userRoleServices.Query(d => d.IsDeleted == false && d.UserId == uid)).Select(d => d.RoleId.ObjToInt()).Distinct().ToList(); + roleIds = (await _userRoleServices.Query(d => d.IsDeleted == false && d.UserId == uid)).Select(d => d.RoleId.ObjToLong()).Distinct().ToList(); } @@ -383,7 +383,7 @@ public async Task> GetNavigationBar(int uid) { if (roleIds.Any()) { - var pids = (await _roleModulePermissionServices.Query(d => d.IsDeleted == false && roleIds.Contains(d.RoleId))).Select(d => d.PermissionId.ObjToInt()).Distinct(); + var pids = (await _roleModulePermissionServices.Query(d => d.IsDeleted == false && roleIds.Contains(d.RoleId))).Select(d => d.PermissionId.ObjToLong()).Distinct(); if (pids.Any()) { var rolePermissionMoudles = (await _permissionServices.Query(d => pids.Contains(d.Id))).OrderBy(c => c.OrderSort); @@ -445,12 +445,12 @@ orderby child.Id /// /// [HttpGet] - public async Task>> GetNavigationBarPro(int uid) + public async Task>> GetNavigationBarPro(long uid) { var data = new MessageModel>(); var uidInHttpcontext1 = 0; - var roleIds = new List(); + var roleIds = new List(); // ids4和jwt切换 if (Permissions.IsUseIds4) { @@ -460,13 +460,13 @@ public async Task>> GetNavigationBarPro(int select item.Value).FirstOrDefault().ObjToInt(); roleIds = (from item in _httpContext.HttpContext.User.Claims where item.Type == "role" - select item.Value.ObjToInt()).ToList(); + select item.Value.ObjToLong()).ToList(); } else { // jwt uidInHttpcontext1 = ((JwtHelper.SerializeJwt(_httpContext.HttpContext.Request.Headers["Authorization"].ObjToString().Replace("Bearer ", "")))?.Uid).ObjToInt(); - roleIds = (await _userRoleServices.Query(d => d.IsDeleted == false && d.UserId == uid)).Select(d => d.RoleId.ObjToInt()).Distinct().ToList(); + roleIds = (await _userRoleServices.Query(d => d.IsDeleted == false && d.UserId == uid)).Select(d => d.RoleId.ObjToLong()).Distinct().ToList(); } if (uid > 0 && uid == uidInHttpcontext1) @@ -474,7 +474,7 @@ public async Task>> GetNavigationBarPro(int if (roleIds.Any()) { var pids = (await _roleModulePermissionServices.Query(d => d.IsDeleted == false && roleIds.Contains(d.RoleId))) - .Select(d => d.PermissionId.ObjToInt()).Distinct(); + .Select(d => d.PermissionId.ObjToLong()).Distinct(); if (pids.Any()) { var rolePermissionMoudles = (await _permissionServices.Query(d => pids.Contains(d.Id) && d.IsButton == false)).OrderBy(c => c.OrderSort); diff --git a/Blog.Core.Api/Controllers/UserController.cs b/Blog.Core.Api/Controllers/UserController.cs index 43f7f610..27989821 100644 --- a/Blog.Core.Api/Controllers/UserController.cs +++ b/Blog.Core.Api/Controllers/UserController.cs @@ -103,15 +103,15 @@ public async Task>> Get(int page = 1, str return Success(data.ConvertTo(_mapper)); } - private (string, List) GetFullDepartmentName(List departments, int departmentId) + private (string, List) GetFullDepartmentName(List departments, int departmentId) { var departmentModel = departments.FirstOrDefault(d => d.Id == departmentId); if (departmentModel == null) { - return ("", new List()); + return ("", new List()); } - var pids = departmentModel.CodeRelationship?.TrimEnd(',').Split(',').Select(d => d.ObjToInt()).ToList(); + var pids = departmentModel.CodeRelationship?.TrimEnd(',').Split(',').Select(d => d.ObjToLong()).ToList(); pids.Add(departmentModel.Id); var pnams = departments.Where(d => pids.Contains(d.Id)).ToList().Select(d => d.Name).ToArray(); var fullName = string.Join("/", pnams); diff --git a/Blog.Core.Api/wwwroot/BlogCore.Data.json/Permission.tsv b/Blog.Core.Api/wwwroot/BlogCore.Data.json/Permission.tsv index d1a5c3f8..c026f01e 100644 --- a/Blog.Core.Api/wwwroot/BlogCore.Data.json/Permission.tsv +++ b/Blog.Core.Api/wwwroot/BlogCore.Data.json/Permission.tsv @@ -2323,28 +2323,28 @@ "Pid": 94, "Mid": 63 }, - { - "Id": 114, - "Code": " ", - "Name": "推送文字消息", - "IsButton": 1, - "IsHide": 0, - "IskeepAlive": 0, - "Func": null, - "OrderSort": 0, - "Icon": null, - "Description": null, - "Enabled": 1, - "CreateId": 8, - "CreateBy": "test", - "CreateTime": "2020-04-23 16:22:11", - "ModifyId": null, - "ModifyBy": null, - "ModifyTime": "2021-09-29 00:00:00", - "IsDeleted": 0, - "Pid": 95, - "Mid": 0 - }, + //{ + // "Id": 114, + // "Code": " ", + // "Name": "推送文字消息", + // "IsButton": 1, + // "IsHide": 0, + // "IskeepAlive": 0, + // "Func": null, + // "OrderSort": 0, + // "Icon": null, + // "Description": null, + // "Enabled": 1, + // "CreateId": 8, + // "CreateBy": "test", + // "CreateTime": "2020-04-23 16:22:11", + // "ModifyId": null, + // "ModifyBy": null, + // "ModifyTime": "2021-09-29 00:00:00", + // "IsDeleted": 0, + // "Pid": 95, + // "Mid": 0 + //}, { "Code": "-", "Name": "部门权限管理", diff --git a/Blog.Core.Api/wwwroot/BlogCore.Data.json/RoleModulePermission.tsv b/Blog.Core.Api/wwwroot/BlogCore.Data.json/RoleModulePermission.tsv index b7923ab4..eb727383 100644 --- a/Blog.Core.Api/wwwroot/BlogCore.Data.json/RoleModulePermission.tsv +++ b/Blog.Core.Api/wwwroot/BlogCore.Data.json/RoleModulePermission.tsv @@ -1647,7 +1647,7 @@ "ModifyId": null, "ModifyBy": null, "ModifyTime": "\/Date(1546272000000+0800)\/", - "Id": 128 + "Id": 228 }, { "IsDeleted": false, @@ -1658,7 +1658,7 @@ "RoleId": 6, "ModuleId": 0, "PermissionId": 114, - "Id": 129 + "Id": 229 }, { "IsDeleted": false, @@ -1669,7 +1669,7 @@ "RoleId": 6, "ModuleId": 66, "PermissionId": 115, - "Id": 130 + "Id": 230 }, { "IsDeleted": false, @@ -1680,7 +1680,7 @@ "RoleId": 6, "ModuleId": 70, "PermissionId": 120, - "Id": 131 + "Id": 231 }, { "IsDeleted": false, @@ -1691,7 +1691,7 @@ "RoleId": 6, "ModuleId": 66, "PermissionId": 116, - "Id": 132 + "Id": 232 }, { "IsDeleted": false, @@ -1702,6 +1702,6 @@ "RoleId": 4, "ModuleId": 72, "PermissionId": 122, - "Id": 133 + "Id": 233 } ] diff --git a/Blog.Core.Common/DB/Aop/SqlsugarAop.cs b/Blog.Core.Common/DB/Aop/SqlsugarAop.cs index 3d83b002..c1417a5a 100644 --- a/Blog.Core.Common/DB/Aop/SqlsugarAop.cs +++ b/Blog.Core.Common/DB/Aop/SqlsugarAop.cs @@ -1,4 +1,5 @@ -using Blog.Core.Model.Models.RootTkey; +using Blog.Core.Model; +using Blog.Core.Model.Models.RootTkey; using Blog.Core.Model.Tenants; using SqlSugar; using System; @@ -16,6 +17,13 @@ public static void DataExecuting(object oldValue, DataFilterModel entityInfo) root.Id = SnowFlakeSingle.Instance.NextId(); } } + if (entityInfo.EntityValue is RootEntityTkey rootEntity) + { + if (rootEntity.Id == 0) + { + rootEntity.Id = SnowFlakeSingle.Instance.NextId(); + } + } if (entityInfo.EntityValue is BaseEntity baseEntity) { diff --git a/Blog.Core.Common/Helper/RecursionHelper.cs b/Blog.Core.Common/Helper/RecursionHelper.cs index 9b27a37d..f6f21a38 100644 --- a/Blog.Core.Common/Helper/RecursionHelper.cs +++ b/Blog.Core.Common/Helper/RecursionHelper.cs @@ -117,8 +117,8 @@ public static void LoopToAppendChildrenT(List all, T curItem, string paren public class PermissionTree { - public int value { get; set; } - public int Pid { get; set; } + public long value { get; set; } + public long Pid { get; set; } public string label { get; set; } public int order { get; set; } public bool isbtn { get; set; } @@ -139,8 +139,8 @@ public class DepartmentTree public class NavigationBar { - public int id { get; set; } - public int pid { get; set; } + public long id { get; set; } + public long pid { get; set; } public int order { get; set; } public string name { get; set; } public bool IsHide { get; set; } = false; @@ -165,8 +165,8 @@ public class NavigationBarMeta public class NavigationBarPro { - public int id { get; set; } - public int parentId { get; set; } + public long id { get; set; } + public long parentId { get; set; } public int order { get; set; } public string name { get; set; } public bool IsHide { get; set; } = false; diff --git a/Blog.Core.Common/Seed/DBSeed.cs b/Blog.Core.Common/Seed/DBSeed.cs index 7b594109..94893c38 100644 --- a/Blog.Core.Common/Seed/DBSeed.cs +++ b/Blog.Core.Common/Seed/DBSeed.cs @@ -175,7 +175,11 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) { var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "Permission"), Encoding.UTF8), setting); - myContext.GetEntityDB().InsertRange(data); + foreach (var item in data) + { + Console.WriteLine($"{item.Name}:{item.Id}"); + myContext.GetEntityDB().Insert(item); + } Console.WriteLine("Table:Permission created success!"); } else @@ -190,10 +194,10 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) if (!await myContext.Db.Queryable().AnyAsync()) { - //var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "Role"), Encoding.UTF8), setting); - using var stream = new FileStream(Path.Combine(WebRootPath, "BlogCore.Data.excel", "Role.xlsx"), FileMode.Open); - var result = await importer.Import(stream); - var data = result.Data.ToList(); + var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "Role"), Encoding.UTF8), setting); + //using var stream = new FileStream(Path.Combine(WebRootPath, "BlogCore.Data.excel", "Role.xlsx"), FileMode.Open); + //var result = await importer.Import(stream); + //var data = result.Data.ToList(); myContext.GetEntityDB().InsertRange(data); Console.WriteLine("Table:Role created success!"); @@ -212,7 +216,11 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) { var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "RoleModulePermission"), Encoding.UTF8), setting); - myContext.GetEntityDB().InsertRange(data); + foreach (var item in data) + { + Console.WriteLine($"{item.Id}"); + myContext.GetEntityDB().Insert(item); + } Console.WriteLine("Table:RoleModulePermission created success!"); } else @@ -261,10 +269,7 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) if (!await myContext.Db.Queryable().AnyAsync()) { - //var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "UserRole"), Encoding.UTF8), setting); - using var stream = new FileStream(Path.Combine(WebRootPath, "BlogCore.Data.excel", "UserRole.xlsx"), FileMode.Open); - var result = await importer.Import(stream); - var data = result.Data.ToList(); + var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "UserRole"), Encoding.UTF8), setting); myContext.GetEntityDB().InsertRange(data); Console.WriteLine("Table:UserRole created success!"); @@ -281,10 +286,7 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) if (!await myContext.Db.Queryable().AnyAsync()) { - //var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "sysUserInfo"), Encoding.UTF8), setting); - using var stream = new FileStream(Path.Combine(WebRootPath, "BlogCore.Data.excel", "SysUserInfo.xlsx"), FileMode.Open); - var result = await importer.Import(stream); - var data = result.Data.ToList(); + var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "sysUserInfo"), Encoding.UTF8), setting); myContext.GetEntityDB().InsertRange(data); Console.WriteLine("Table:sysUserInfo created success!"); diff --git a/Blog.Core.IServices/IRoleModulePermissionServices.cs b/Blog.Core.IServices/IRoleModulePermissionServices.cs index 22532479..2a5c7345 100644 --- a/Blog.Core.IServices/IRoleModulePermissionServices.cs +++ b/Blog.Core.IServices/IRoleModulePermissionServices.cs @@ -21,6 +21,6 @@ public interface IRoleModulePermissionServices :IBaseServices˵ /// ӿ /// - Task UpdateModuleId(int permissionId, int moduleId); + Task UpdateModuleId(long permissionId, long moduleId); } } diff --git a/Blog.Core.Model/Models/AccessTrendLog.cs b/Blog.Core.Model/Models/AccessTrendLog.cs index 4a87b13e..fd6dbae7 100644 --- a/Blog.Core.Model/Models/AccessTrendLog.cs +++ b/Blog.Core.Model/Models/AccessTrendLog.cs @@ -6,7 +6,7 @@ namespace Blog.Core.Model.Models /// /// 用户访问趋势日志 /// - public class AccessTrendLog : RootEntityTkey + public class AccessTrendLog : RootEntityTkey { /// /// 用户 diff --git a/Blog.Core.Model/Models/Advertisement.cs b/Blog.Core.Model/Models/Advertisement.cs index c2babd74..3b11b21f 100644 --- a/Blog.Core.Model/Models/Advertisement.cs +++ b/Blog.Core.Model/Models/Advertisement.cs @@ -3,7 +3,7 @@ namespace Blog.Core.Model.Models { - public class Advertisement : RootEntityTkey + public class Advertisement : RootEntityTkey { /// diff --git a/Blog.Core.Model/Models/BlogArticle.cs b/Blog.Core.Model/Models/BlogArticle.cs index 66f05bdf..8b75c8df 100644 --- a/Blog.Core.Model/Models/BlogArticle.cs +++ b/Blog.Core.Model/Models/BlogArticle.cs @@ -13,8 +13,8 @@ public class BlogArticle /// 主键 /// /// 这里之所以没用RootEntity,是想保持和之前的数据库一致,主键是bID,不是Id - [SugarColumn(IsNullable = false, IsPrimaryKey = true, IsIdentity = true)] - public int bID { get; set; } + [SugarColumn(IsNullable = false, IsPrimaryKey = true, IsIdentity = false)] + public long bID { get; set; } /// /// 创建人 diff --git a/Blog.Core.Model/Models/BlogArticleComment.cs b/Blog.Core.Model/Models/BlogArticleComment.cs index 08010863..519fb003 100644 --- a/Blog.Core.Model/Models/BlogArticleComment.cs +++ b/Blog.Core.Model/Models/BlogArticleComment.cs @@ -7,7 +7,7 @@ namespace Blog.Core.Model.Models; /// public class BlogArticleComment : RootEntityTkey { - public int bID { get; set; } + public long bID { get; set; } public string Comment { get; set; } diff --git a/Blog.Core.Model/Models/Department.cs b/Blog.Core.Model/Models/Department.cs index 3583bcff..424bcf44 100644 --- a/Blog.Core.Model/Models/Department.cs +++ b/Blog.Core.Model/Models/Department.cs @@ -7,7 +7,7 @@ namespace Blog.Core.Model.Models /// /// 部门表 /// - public class Department : DepartmentRoot + public class Department : DepartmentRoot { /// /// Desc:部门关系编码 diff --git a/Blog.Core.Model/Models/GblLogAudit.cs b/Blog.Core.Model/Models/GblLogAudit.cs index 4b1bd9cd..2cecce8b 100644 --- a/Blog.Core.Model/Models/GblLogAudit.cs +++ b/Blog.Core.Model/Models/GblLogAudit.cs @@ -12,8 +12,8 @@ public class GblLogAudit /// ///ID /// - [SugarColumn(ColumnDescription = "ID", IsNullable = false, IsPrimaryKey = true, IsIdentity = true)] - public int Id { get; set; } + [SugarColumn(ColumnDescription = "ID", IsNullable = false, IsPrimaryKey = true, IsIdentity = false)] + public long Id { get; set; } /// ///HttpContext.TraceIdentifier 事件链路ID(获取或设置一个唯一标识符,用于在跟踪日志中表示此请求。) diff --git a/Blog.Core.Model/Models/Guestbook.cs b/Blog.Core.Model/Models/Guestbook.cs index d1f671c0..0cd5dcef 100644 --- a/Blog.Core.Model/Models/Guestbook.cs +++ b/Blog.Core.Model/Models/Guestbook.cs @@ -3,13 +3,13 @@ namespace Blog.Core.Model.Models { - public class Guestbook:RootEntityTkey + public class Guestbook : RootEntityTkey { - + /// 博客ID /// /// - public int? blogId { get; set; } + public long? blogId { get; set; } /// 创建时间 /// /// diff --git a/Blog.Core.Model/Models/Modules.cs b/Blog.Core.Model/Models/Modules.cs index b62c0a47..6e41aaac 100644 --- a/Blog.Core.Model/Models/Modules.cs +++ b/Blog.Core.Model/Models/Modules.cs @@ -6,7 +6,7 @@ namespace Blog.Core.Model.Models /// /// 接口API地址信息表 /// - public class Modules : ModulesRoot + public class Modules : ModulesRoot { public Modules() { diff --git a/Blog.Core.Model/Models/OperateLog.cs b/Blog.Core.Model/Models/OperateLog.cs index 4086781c..3c2fb54c 100644 --- a/Blog.Core.Model/Models/OperateLog.cs +++ b/Blog.Core.Model/Models/OperateLog.cs @@ -6,7 +6,7 @@ namespace Blog.Core.Model.Models /// /// 日志记录 /// - public class OperateLog : RootEntityTkey + public class OperateLog : RootEntityTkey { /// diff --git a/Blog.Core.Model/Models/PasswordLib.cs b/Blog.Core.Model/Models/PasswordLib.cs index 2b43c265..b8b633d6 100644 --- a/Blog.Core.Model/Models/PasswordLib.cs +++ b/Blog.Core.Model/Models/PasswordLib.cs @@ -7,11 +7,11 @@ namespace Blog.Core.Model.Models /// 密码库表 /// [SugarTable("PasswordLib", "密码库表")]//('数据库表名','数据库表备注') - [TenantAttribute("WMBLOG_MYSQL_2")] //('代表是哪个数据库,名字是appsettings.json 的 ConnId') + [Tenant("WMBLOG_MYSQL_2")] //('代表是哪个数据库,名字是appsettings.json 的 ConnId') public class PasswordLib { - [SugarColumn(IsNullable = false, IsPrimaryKey = true, IsIdentity = true)] - public int PLID { get; set; } + [SugarColumn(IsNullable = false, IsPrimaryKey = true, IsIdentity = false)] + public long PLID { get; set; } /// ///获取或设置是否禁用,逻辑上的删除,非物理删除 diff --git a/Blog.Core.Model/Models/Permission.cs b/Blog.Core.Model/Models/Permission.cs index c650cb5e..deece0c0 100644 --- a/Blog.Core.Model/Models/Permission.cs +++ b/Blog.Core.Model/Models/Permission.cs @@ -7,7 +7,7 @@ namespace Blog.Core.Model.Models /// /// 路由菜单表 /// - public class Permission : PermissionRoot + public class Permission : PermissionRoot { public Permission() { diff --git a/Blog.Core.Model/Models/Role.cs b/Blog.Core.Model/Models/Role.cs index e34ccdd9..1357afb0 100644 --- a/Blog.Core.Model/Models/Role.cs +++ b/Blog.Core.Model/Models/Role.cs @@ -6,7 +6,7 @@ namespace Blog.Core.Model.Models /// /// 角色表 /// - public class Role : RootEntityTkey + public class Role : RootEntityTkey { public Role() { diff --git a/Blog.Core.Model/Models/RoleModulePermission.cs b/Blog.Core.Model/Models/RoleModulePermission.cs index 13d82a80..482b9b4e 100644 --- a/Blog.Core.Model/Models/RoleModulePermission.cs +++ b/Blog.Core.Model/Models/RoleModulePermission.cs @@ -6,7 +6,7 @@ namespace Blog.Core.Model.Models /// /// 按钮跟权限关联表 /// - public class RoleModulePermission : RoleModulePermissionRoot + public class RoleModulePermission : RoleModulePermissionRoot { public RoleModulePermission() { diff --git a/Blog.Core.Model/Models/RootEntity.cs b/Blog.Core.Model/Models/RootEntity.cs deleted file mode 100644 index d3874bb4..00000000 --- a/Blog.Core.Model/Models/RootEntity.cs +++ /dev/null @@ -1,15 +0,0 @@ -using SqlSugar; - -namespace Blog.Core.Model -{ - public class RootEntity - { - /// - /// ID - /// - [SugarColumn(IsNullable = false, IsPrimaryKey = true)] - public int Id { get; set; } - - - } -} \ No newline at end of file diff --git a/Blog.Core.Model/Models/TasksLog.cs b/Blog.Core.Model/Models/TasksLog.cs index b4317c95..c79e8077 100644 --- a/Blog.Core.Model/Models/TasksLog.cs +++ b/Blog.Core.Model/Models/TasksLog.cs @@ -6,12 +6,12 @@ namespace Blog.Core.Model.Models /// /// 任务日志表 /// - public class TasksLog : RootEntityTkey + public class TasksLog : RootEntityTkey { /// /// 任务ID /// - public int JobId { get; set; } + public long JobId { get; set; } /// /// 任务耗时 /// diff --git a/Blog.Core.Model/Models/TasksQz.cs b/Blog.Core.Model/Models/TasksQz.cs index 1c07b60e..b029a995 100644 --- a/Blog.Core.Model/Models/TasksQz.cs +++ b/Blog.Core.Model/Models/TasksQz.cs @@ -8,7 +8,7 @@ namespace Blog.Core.Model.Models /// /// 任务计划表 /// - public class TasksQz : RootEntityTkey + public class TasksQz : RootEntityTkey { /// /// 任务名称 diff --git a/Blog.Core.Model/Models/TestModels.cs b/Blog.Core.Model/Models/TestModels.cs index f5fa7dc7..8a8d123c 100644 --- a/Blog.Core.Model/Models/TestModels.cs +++ b/Blog.Core.Model/Models/TestModels.cs @@ -5,9 +5,9 @@ public class TestMuchTableResult { public string moduleName { get; set; } public string permName { get; set; } - public int rid { get; set; } - public int mid { get; set; } - public int? pid { get; set; } + public long rid { get; set; } + public long mid { get; set; } + public long? pid { get; set; } } } diff --git a/Blog.Core.Model/Models/Topic.cs b/Blog.Core.Model/Models/Topic.cs index 16bf7dad..e57bd561 100644 --- a/Blog.Core.Model/Models/Topic.cs +++ b/Blog.Core.Model/Models/Topic.cs @@ -7,7 +7,7 @@ namespace Blog.Core.Model.Models /// /// Tibug 类别 /// - public class Topic : RootEntityTkey + public class Topic : RootEntityTkey { public Topic() { diff --git a/Blog.Core.Model/Models/TopicDetail.cs b/Blog.Core.Model/Models/TopicDetail.cs index 87e16ebf..1a98f3af 100644 --- a/Blog.Core.Model/Models/TopicDetail.cs +++ b/Blog.Core.Model/Models/TopicDetail.cs @@ -6,7 +6,7 @@ namespace Blog.Core.Model.Models /// /// Tibug 博文 /// - public class TopicDetail : TopicDetailRoot + public class TopicDetail : TopicDetailRoot { public TopicDetail() { diff --git a/Blog.Core.Model/Models/UserRole.cs b/Blog.Core.Model/Models/UserRole.cs index 996eea2c..7ed9c6be 100644 --- a/Blog.Core.Model/Models/UserRole.cs +++ b/Blog.Core.Model/Models/UserRole.cs @@ -6,11 +6,11 @@ namespace Blog.Core.Model.Models /// /// 用户跟角色关联表 /// - public class UserRole : UserRoleRoot + public class UserRole : UserRoleRoot { public UserRole() { } - public UserRole(int uid, int rid) + public UserRole(long uid, long rid) { UserId = uid; RoleId = rid; @@ -31,7 +31,7 @@ public UserRole(int uid, int rid) /// 创建ID /// [SugarColumn(IsNullable = true)] - public int? CreateId { get; set; } + public long? CreateId { get; set; } /// /// 创建者 /// diff --git a/Blog.Core.Model/Models/sysUserInfo.cs b/Blog.Core.Model/Models/sysUserInfo.cs index 366b2291..2a417a16 100644 --- a/Blog.Core.Model/Models/sysUserInfo.cs +++ b/Blog.Core.Model/Models/sysUserInfo.cs @@ -9,7 +9,7 @@ namespace Blog.Core.Model.Models /// //[SugarTable("SysUserInfo")] [SugarTable("SysUserInfo", "用户表")] //('数据库表名','数据库表备注') - public class SysUserInfo : SysUserInfoRoot + public class SysUserInfo : SysUserInfoRoot { public SysUserInfo() { @@ -133,7 +133,7 @@ public SysUserInfo(string loginName, string loginPWD) public List RoleNames { get; set; } [SugarColumn(IsIgnore = true)] - public List Dids { get; set; } + public List Dids { get; set; } [SugarColumn(IsIgnore = true)] public string DepartmentName { get; set; } diff --git a/Blog.Core.Model/ViewModels/BlogViewModels.cs b/Blog.Core.Model/ViewModels/BlogViewModels.cs index f959270c..411a8ef7 100644 --- a/Blog.Core.Model/ViewModels/BlogViewModels.cs +++ b/Blog.Core.Model/ViewModels/BlogViewModels.cs @@ -34,7 +34,7 @@ public class BlogViewModels /// /// 上一篇id /// - public int previousID { get; set; } + public long previousID { get; set; } /// /// 下一篇 @@ -44,7 +44,7 @@ public class BlogViewModels /// /// 下一篇id /// - public int nextID { get; set; } + public long nextID { get; set; } /// 类别 /// diff --git a/Blog.Core.Repository/IRoleModulePermissionRepository.cs b/Blog.Core.Repository/IRoleModulePermissionRepository.cs index c66448f0..9ba3d4ed 100644 --- a/Blog.Core.Repository/IRoleModulePermissionRepository.cs +++ b/Blog.Core.Repository/IRoleModulePermissionRepository.cs @@ -19,6 +19,6 @@ public interface IRoleModulePermissionRepository : IBaseRepository菜单主键 /// 接口主键 /// - Task UpdateModuleId(int permissionId, int moduleId); + Task UpdateModuleId(long permissionId, long moduleId); } } diff --git a/Blog.Core.Repository/RoleModulePermissionRepository.cs b/Blog.Core.Repository/RoleModulePermissionRepository.cs index 1cb21ebf..9438ff50 100644 --- a/Blog.Core.Repository/RoleModulePermissionRepository.cs +++ b/Blog.Core.Repository/RoleModulePermissionRepository.cs @@ -99,7 +99,7 @@ public async Task> GetRMPMapsPage() /// 菜单主键 /// 接口主键 /// - public async Task UpdateModuleId(int permissionId, int moduleId) + public async Task UpdateModuleId(long permissionId, long moduleId) { await Db.Updateable(it => it.ModuleId == moduleId).Where( it => it.PermissionId == permissionId).ExecuteCommandAsync(); diff --git a/Blog.Core.Services/RoleModulePermissionServices.cs b/Blog.Core.Services/RoleModulePermissionServices.cs index c0248e17..d3834f89 100644 --- a/Blog.Core.Services/RoleModulePermissionServices.cs +++ b/Blog.Core.Services/RoleModulePermissionServices.cs @@ -83,7 +83,7 @@ public async Task> GetRMPMaps() /// 菜单主键 /// 接口主键 /// - public async Task UpdateModuleId(int permissionId, int moduleId) + public async Task UpdateModuleId(long permissionId, long moduleId) { await _dal.UpdateModuleId(permissionId, moduleId); } From 0ed67675a6bbab5a1b997ed9ea002f60aa3fde32 Mon Sep 17 00:00:00 2001 From: hudingwen <765472804@qq.com> Date: Fri, 31 Mar 2023 22:02:30 +0800 Subject: [PATCH 09/84] =?UTF-8?q?=E6=9D=83=E9=99=90=E5=88=86=E9=85=8D?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E9=80=BB=E8=BE=91=E4=BC=98=E5=8C=96=20sqlsug?= =?UTF-8?q?arAop=E9=80=BB=E8=BE=91=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Api/Blog.Core.xml | 3 +- .../Controllers/PermissionController.cs | 79 +++++++++---------- Blog.Core.Common/DB/Aop/SqlsugarAop.cs | 41 ++++++++++ 3 files changed, 81 insertions(+), 42 deletions(-) diff --git a/Blog.Core.Api/Blog.Core.xml b/Blog.Core.Api/Blog.Core.xml index c0b52222..79325e37 100644 --- a/Blog.Core.Api/Blog.Core.xml +++ b/Blog.Core.Api/Blog.Core.xml @@ -361,7 +361,7 @@ 菜单管理 - + 构造函数 @@ -369,6 +369,7 @@ + diff --git a/Blog.Core.Api/Controllers/PermissionController.cs b/Blog.Core.Api/Controllers/PermissionController.cs index 9277a36b..f4889c73 100644 --- a/Blog.Core.Api/Controllers/PermissionController.cs +++ b/Blog.Core.Api/Controllers/PermissionController.cs @@ -6,6 +6,8 @@ using Blog.Core.IServices; using Blog.Core.Model; using Blog.Core.Model.Models; +using Blog.Core.Repository.UnitOfWorks; +using Blog.Core.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; @@ -21,6 +23,7 @@ namespace Blog.Core.Controllers [Authorize(Permissions.Name)] public class PermissionController : BaseApiController { + readonly IUnitOfWorkManage _unitOfWorkManage; readonly IPermissionServices _permissionServices; readonly IModuleServices _moduleServices; readonly IRoleModulePermissionServices _roleModulePermissionServices; @@ -37,16 +40,19 @@ public class PermissionController : BaseApiController /// /// /// + /// /// /// /// /// public PermissionController(IPermissionServices permissionServices, IModuleServices moduleServices, IRoleModulePermissionServices roleModulePermissionServices, IUserRoleServices userRoleServices, + IUnitOfWorkManage unitOfWorkManage, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContext, IUser user, PermissionRequirement requirement) { _permissionServices = permissionServices; + _unitOfWorkManage = unitOfWorkManage; _moduleServices = moduleServices; _roleModulePermissionServices = roleModulePermissionServices; _userRoleServices = userRoleServices; @@ -239,25 +245,24 @@ public async Task> Post([FromBody] Permission permission) /// [HttpPost] public async Task> Assign([FromBody] AssignView assignView) - { - var data = new MessageModel(); - - + { if (assignView.rid > 0) { - data.success = true; - - var roleModulePermissions = await _roleModulePermissionServices.Query(d => d.RoleId == assignView.rid); + //开启事务 + try + { + var old_rmps = await _roleModulePermissionServices.Query(d => d.RoleId == assignView.rid); - var remove = roleModulePermissions.Where(d => !assignView.pids.Contains(d.PermissionId.ObjToInt())).Select(c => (object)c.Id); - data.success &= remove.Any() ? await _roleModulePermissionServices.DeleteByIds(remove.ToArray()) : true; + _unitOfWorkManage.BeginTran(); + await _permissionServices.Db.Deleteable(t => t.RoleId == assignView.rid).ExecuteCommandAsync(); + var permissions = await _permissionServices.Query(d => d.IsDeleted == false); - foreach (var item in assignView.pids) - { - var rmpitem = roleModulePermissions.Where(d => d.PermissionId == item); - var moduleid = (await _permissionServices.Query(p => p.Id == item)).FirstOrDefault()?.Mid; - if (!rmpitem.Any()) + List new_rmps = new List(); + var nowTime = _permissionServices.Db.GetDate(); + foreach (var item in assignView.pids) { + var moduleid = permissions.Find(p => p.Id == item)?.Mid; + var find_old_rmps = old_rmps.Find(p => p.PermissionId == item); RoleModulePermission roleModulePermission = new RoleModulePermission() { @@ -265,39 +270,31 @@ public async Task> Assign([FromBody] AssignView assignView) RoleId = assignView.rid, ModuleId = moduleid.ObjToInt(), PermissionId = item, - }; - + CreateId = find_old_rmps == null ? _user.ID : find_old_rmps.CreateId, + CreateBy = find_old_rmps == null ? _user.Name : find_old_rmps.CreateBy, + CreateTime = find_old_rmps == null ? nowTime : find_old_rmps.CreateTime, + ModifyId = _user.ID, + ModifyBy = _user.Name, + ModifyTime = nowTime - roleModulePermission.CreateId = _user.ID; - roleModulePermission.CreateBy = _user.Name; - - data.success &= (await _roleModulePermissionServices.Add(roleModulePermission)) > 0; - - } - else - { - foreach (var role in rmpitem) - { - if (!role.ModuleId.Equals(moduleid)) - { - role.ModuleId = moduleid.Value; - await _roleModulePermissionServices.Update(role, new List { "ModuleId" }); - } - } + }; + new_rmps.Add(roleModulePermission); } + if(new_rmps.Count>0) await _roleModulePermissionServices.Add(new_rmps); + _unitOfWorkManage.CommitTran(); } - - if (data.success) + catch (Exception) { - _requirement.Permissions.Clear(); - data.response = ""; - data.msg = "保存成功"; + _unitOfWorkManage.RollbackTran(); + throw; } - + _requirement.Permissions.Clear(); + return Success("保存成功"); } - - - return data; + else + { + return Failed("请选择要操作的角色"); + } } diff --git a/Blog.Core.Common/DB/Aop/SqlsugarAop.cs b/Blog.Core.Common/DB/Aop/SqlsugarAop.cs index c1417a5a..c70d9283 100644 --- a/Blog.Core.Common/DB/Aop/SqlsugarAop.cs +++ b/Blog.Core.Common/DB/Aop/SqlsugarAop.cs @@ -1,6 +1,7 @@ using Blog.Core.Model; using Blog.Core.Model.Models.RootTkey; using Blog.Core.Model.Tenants; +using NetTaste; using SqlSugar; using System; @@ -69,6 +70,46 @@ public static void DataExecuting(object oldValue, DataFilterModel entityInfo) } } } + else + { + //兼容以前的表 + var getType = entityInfo.EntityValue.GetType(); + + + switch (entityInfo.OperationType) + { + + case DataFilterType.InsertByObject: + var dyCreateBy = getType.GetProperty("CreateBy"); + var dyCreateId = getType.GetProperty("CreateId"); + var dyCreateTime = getType.GetProperty("CreateTime"); + + if (App.User?.ID > 0 && dyCreateBy != null && dyCreateBy.GetValue(entityInfo.EntityValue) == null) + dyCreateBy.SetValue(entityInfo.EntityValue, App.User.Name); + + if (App.User?.ID > 0 && dyCreateId != null && dyCreateId.GetValue(entityInfo.EntityValue) == null) + dyCreateId.SetValue(entityInfo.EntityValue, App.User.ID); + + if (dyCreateTime != null && (DateTime)dyCreateTime.GetValue(entityInfo.EntityValue) == DateTime.MinValue) + dyCreateTime.SetValue(entityInfo.EntityValue, DateTime.Now); + + break; + case DataFilterType.UpdateByObject: + var dyModifyBy = getType.GetProperty("ModifyBy"); + var dyModifyId = getType.GetProperty("ModifyId"); + var dyModifyTime = getType.GetProperty("ModifyTime"); + + if (App.User?.ID > 0 && dyModifyBy != null) + dyModifyBy.SetValue(entityInfo.EntityValue, App.User.Name); + + if (App.User?.ID > 0 && dyModifyId != null) + dyModifyId.SetValue(entityInfo.EntityValue, App.User.ID); + + if (dyModifyTime != null) + dyModifyTime.SetValue(entityInfo.EntityValue, DateTime.Now); + break; + } + } } private static string GetWholeSql(SugarParameter[] paramArr, string sql) From 3c0d9f977cc03da275ba7d32a4f27e083b947ce3 Mon Sep 17 00:00:00 2001 From: hudingwen <765472804@qq.com> Date: Sat, 1 Apr 2023 21:09:47 +0800 Subject: [PATCH 10/84] =?UTF-8?q?=E6=B7=BB=E5=8A=A0update=20list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.IServices/BASE/IBaseServices.cs | 1 + Blog.Core.Repository/BASE/BaseRepository.cs | 9 +++++++++ Blog.Core.Repository/BASE/IBaseRepository.cs | 6 ++++++ Blog.Core.Services/BASE/BaseServices.cs | 9 +++++++++ 4 files changed, 25 insertions(+) diff --git a/Blog.Core.IServices/BASE/IBaseServices.cs b/Blog.Core.IServices/BASE/IBaseServices.cs index 4091b978..b491614f 100644 --- a/Blog.Core.IServices/BASE/IBaseServices.cs +++ b/Blog.Core.IServices/BASE/IBaseServices.cs @@ -27,6 +27,7 @@ public interface IBaseServices where TEntity : class Task DeleteByIds(object[] ids); Task Update(TEntity model); + Task Update(List model); Task Update(TEntity entity, string where); Task Update(object operateAnonymousObjects); diff --git a/Blog.Core.Repository/BASE/BaseRepository.cs b/Blog.Core.Repository/BASE/BaseRepository.cs index 3048baa8..9ef11ecd 100644 --- a/Blog.Core.Repository/BASE/BaseRepository.cs +++ b/Blog.Core.Repository/BASE/BaseRepository.cs @@ -170,6 +170,15 @@ public async Task Update(TEntity entity) //这种方式会以主键为条件 return await _db.Updateable(entity).ExecuteCommandHasChangeAsync(); } + /// + /// 更新实体数据 + /// + /// 博文实体类 + /// + public async Task Update(List entity) + { + return await _db.Updateable(entity).ExecuteCommandHasChangeAsync(); + } public async Task Update(TEntity entity, string where) { diff --git a/Blog.Core.Repository/BASE/IBaseRepository.cs b/Blog.Core.Repository/BASE/IBaseRepository.cs index 5f70a4be..8ef05c1e 100644 --- a/Blog.Core.Repository/BASE/IBaseRepository.cs +++ b/Blog.Core.Repository/BASE/IBaseRepository.cs @@ -69,6 +69,12 @@ public interface IBaseRepository where TEntity : class /// /// Task Update(TEntity model); + /// + /// 更新model + /// + /// + /// + Task Update(List model); /// /// 根据model,更新,带where条件 diff --git a/Blog.Core.Services/BASE/BaseServices.cs b/Blog.Core.Services/BASE/BaseServices.cs index 7ee55eb1..14f69636 100644 --- a/Blog.Core.Services/BASE/BaseServices.cs +++ b/Blog.Core.Services/BASE/BaseServices.cs @@ -80,6 +80,15 @@ public async Task Update(TEntity entity) { return await BaseDal.Update(entity); } + /// + /// 更新实体数据 + /// + /// 博文实体类 + /// + public async Task Update(List entity) + { + return await BaseDal.Update(entity); + } public async Task Update(TEntity entity, string where) { From f785d507704275c104a61e8793548644084983ed Mon Sep 17 00:00:00 2001 From: "Lemon.NoCry" <773596523@qq.com> Date: Sat, 1 Apr 2023 21:29:34 +0800 Subject: [PATCH 11/84] =?UTF-8?q?=F0=9F=8E=A8=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E9=9B=AA=E8=8A=B1id=E4=BD=BF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Api/Blog.Core.Model.xml | 5 ---- .../Controllers/PermissionController.cs | 4 +-- Blog.Core.Common/DB/Aop/SqlsugarAop.cs | 15 ++++------ Blog.Core.IServices/BASE/IBaseServices.cs | 4 +-- Blog.Core.Model/Models/RootTkey/BaseEntity.cs | 8 +----- Blog.Core.Repository/BASE/BaseRepository.cs | 28 +++++++++++-------- Blog.Core.Repository/BASE/IBaseRepository.cs | 4 +-- Blog.Core.Services/BASE/BaseServices.cs | 11 ++++++-- 8 files changed, 37 insertions(+), 42 deletions(-) diff --git a/Blog.Core.Api/Blog.Core.Model.xml b/Blog.Core.Api/Blog.Core.Model.xml index fe62d8e2..79666d28 100644 --- a/Blog.Core.Api/Blog.Core.Model.xml +++ b/Blog.Core.Api/Blog.Core.Model.xml @@ -832,11 +832,6 @@ 修改时间 - - - 雪花Id - - 状态
diff --git a/Blog.Core.Api/Controllers/PermissionController.cs b/Blog.Core.Api/Controllers/PermissionController.cs index f4889c73..059e9b18 100644 --- a/Blog.Core.Api/Controllers/PermissionController.cs +++ b/Blog.Core.Api/Controllers/PermissionController.cs @@ -754,11 +754,11 @@ public async Task>> MigratePermission(string actio List modules = await _moduleServices.Query(d => d.LinkUrl != null && d.LinkUrl.ToLower() == item.Module.LinkUrl); if (!modules.Any()) { - int mid = await _moduleServices.Add(item.Module); + var mid = await _moduleServices.Add(item.Module); if (mid > 0) { item.Mid = mid; - int permissionid = await _permissionServices.Add(item); + var permissionid = await _permissionServices.Add(item); } } diff --git a/Blog.Core.Common/DB/Aop/SqlsugarAop.cs b/Blog.Core.Common/DB/Aop/SqlsugarAop.cs index c70d9283..9ab494a4 100644 --- a/Blog.Core.Common/DB/Aop/SqlsugarAop.cs +++ b/Blog.Core.Common/DB/Aop/SqlsugarAop.cs @@ -1,7 +1,6 @@ using Blog.Core.Model; using Blog.Core.Model.Models.RootTkey; using Blog.Core.Model.Tenants; -using NetTaste; using SqlSugar; using System; @@ -11,13 +10,6 @@ public static class SqlSugarAop { public static void DataExecuting(object oldValue, DataFilterModel entityInfo) { - if (entityInfo.EntityValue is BaseEntity root) - { - if (root.Id == 0) - { - root.Id = SnowFlakeSingle.Instance.NextId(); - } - } if (entityInfo.EntityValue is RootEntityTkey rootEntity) { if (rootEntity.Id == 0) @@ -73,12 +65,15 @@ public static void DataExecuting(object oldValue, DataFilterModel entityInfo) else { //兼容以前的表 - var getType = entityInfo.EntityValue.GetType(); + //这里要小心 在AOP里用反射 数据量多性能就会有问题 + //要么都统一使用基类 + //要么考虑老的表没必要兼容老的表 + // + var getType = entityInfo.EntityValue.GetType(); switch (entityInfo.OperationType) { - case DataFilterType.InsertByObject: var dyCreateBy = getType.GetProperty("CreateBy"); var dyCreateId = getType.GetProperty("CreateId"); diff --git a/Blog.Core.IServices/BASE/IBaseServices.cs b/Blog.Core.IServices/BASE/IBaseServices.cs index 4091b978..21f1afb4 100644 --- a/Blog.Core.IServices/BASE/IBaseServices.cs +++ b/Blog.Core.IServices/BASE/IBaseServices.cs @@ -16,9 +16,9 @@ public interface IBaseServices where TEntity : class Task QueryById(object objId, bool blnUseCache = false); Task> QueryByIDs(object[] lstIds); - Task Add(TEntity model); + Task Add(TEntity model); - Task Add(List listEntity); + Task> Add(List listEntity); Task DeleteById(object id); diff --git a/Blog.Core.Model/Models/RootTkey/BaseEntity.cs b/Blog.Core.Model/Models/RootTkey/BaseEntity.cs index efbde8fd..b6dabe54 100644 --- a/Blog.Core.Model/Models/RootTkey/BaseEntity.cs +++ b/Blog.Core.Model/Models/RootTkey/BaseEntity.cs @@ -4,14 +4,8 @@ namespace Blog.Core.Model.Models.RootTkey; -public class BaseEntity : IDeleteFilter +public class BaseEntity : RootEntityTkey, IDeleteFilter { - /// - /// 雪花Id - /// - [SugarColumn(IsNullable = false, IsPrimaryKey = true, IsIdentity = false)] - public long Id { get; set; } - #region 数据状态管理 /// diff --git a/Blog.Core.Repository/BASE/BaseRepository.cs b/Blog.Core.Repository/BASE/BaseRepository.cs index 3048baa8..e808690d 100644 --- a/Blog.Core.Repository/BASE/BaseRepository.cs +++ b/Blog.Core.Repository/BASE/BaseRepository.cs @@ -114,7 +114,7 @@ public async Task> QueryByIDs(object[] lstIds) /// /// 博文实体类 /// - public async Task Add(TEntity entity) + public async Task Add(TEntity entity) { //var i = await Task.Run(() => _db.Insertable(entity).ExecuteReturnBigIdentity()); ////返回的i是long类型,这里你可以根据你的业务需要进行处理 @@ -125,7 +125,7 @@ public async Task Add(TEntity entity) //这里你可以返回TEntity,这样的话就可以获取id值,无论主键是什么类型 //var return3 = await insert.ExecuteReturnEntityAsync(); - return await insert.ExecuteReturnIdentityAsync(); + return await insert.ExecuteReturnSnowflakeIdAsync(); } /// @@ -134,16 +134,16 @@ public async Task Add(TEntity entity) /// 实体类 /// 指定只插入列 /// 返回自增量列 - public async Task Add(TEntity entity, Expression> insertColumns = null) + public async Task Add(TEntity entity, Expression> insertColumns = null) { var insert = _db.Insertable(entity); if (insertColumns == null) { - return await insert.ExecuteReturnIdentityAsync(); + return await insert.ExecuteReturnSnowflakeIdAsync(); } else { - return await insert.InsertColumns(insertColumns).ExecuteReturnIdentityAsync(); + return await insert.InsertColumns(insertColumns).ExecuteReturnSnowflakeIdAsync(); } } @@ -152,9 +152,9 @@ public async Task Add(TEntity entity, Expression> ins /// /// 实体集合 /// 影响行数 - public async Task Add(List listEntity) + public async Task> Add(List listEntity) { - return await _db.Insertable(listEntity.ToArray()).ExecuteCommandAsync(); + return await _db.Insertable(listEntity.ToArray()).ExecuteReturnSnowflakeIdListAsync(); } /// @@ -557,7 +557,9 @@ public async Task> QueryTabsPage( // groupName = s.groupName, // jobName = s.jobName // }, exp, s => new { s.uID, s.uRealName, s.groupName, s.jobName }, model.currentPage, model.pageSize, model.orderField + " " + model.orderType); + #region Split分表基础接口 (基础CRUD) + /// /// 分页查询[使用版本,其他分页未测试] /// @@ -573,9 +575,10 @@ public async Task> QueryPageSplit(Expression(pageIndex, totalCount, pageSize, list); + var data = new PageModel(pageIndex, totalCount, pageSize, list); return data; } + /// /// 写入实体数据 /// @@ -599,24 +602,26 @@ public async Task UpdateSplit(TEntity entity, DateTime dateTime) //return await _db.Updateable(entity).SplitTable().ExecuteCommandAsync();//,SplitTable不能少 //精准找单个表 - var tableName = _db.SplitHelper().GetTableName(dateTime);//根据时间获取表名 + var tableName = _db.SplitHelper().GetTableName(dateTime); //根据时间获取表名 return await _db.Updateable(entity).AS(tableName).ExecuteCommandHasChangeAsync(); } + /// /// 删除数据 /// /// /// /// - public async Task DeleteSplit(TEntity entity,DateTime dateTime) + public async Task DeleteSplit(TEntity entity, DateTime dateTime) { ////直接根据实体集合删除 (全自动 找表插入),返回受影响数 //return await _db.Deleteable(entity).SplitTable().ExecuteCommandAsync();//,SplitTable不能少 //精准找单个表 - var tableName = _db.SplitHelper().GetTableName(dateTime);//根据时间获取表名 + var tableName = _db.SplitHelper().GetTableName(dateTime); //根据时间获取表名 return await _db.Deleteable().AS(tableName).Where(entity).ExecuteCommandHasChangeAsync(); } + /// /// 根据ID查找数据 /// @@ -626,6 +631,7 @@ public async Task QueryByIdSplit(object objId) { return await _db.Queryable().In(objId).SplitTable(tabs => tabs).SingleAsync(); } + #endregion } } \ No newline at end of file diff --git a/Blog.Core.Repository/BASE/IBaseRepository.cs b/Blog.Core.Repository/BASE/IBaseRepository.cs index 5f70a4be..e3e6553b 100644 --- a/Blog.Core.Repository/BASE/IBaseRepository.cs +++ b/Blog.Core.Repository/BASE/IBaseRepository.cs @@ -33,14 +33,14 @@ public interface IBaseRepository where TEntity : class /// /// /// - Task Add(TEntity model); + Task Add(TEntity model); /// /// 批量添加 /// /// /// - Task Add(List listEntity); + Task> Add(List listEntity); /// /// 根据id 删除某一实体 diff --git a/Blog.Core.Services/BASE/BaseServices.cs b/Blog.Core.Services/BASE/BaseServices.cs index 7ee55eb1..809890fb 100644 --- a/Blog.Core.Services/BASE/BaseServices.cs +++ b/Blog.Core.Services/BASE/BaseServices.cs @@ -56,7 +56,7 @@ public async Task> QueryByIDs(object[] lstIds) /// /// 博文实体类 /// - public async Task Add(TEntity entity) + public async Task Add(TEntity entity) { return await BaseDal.Add(entity); } @@ -66,7 +66,7 @@ public async Task Add(TEntity entity) ///
/// 实体集合 /// 影响行数 - public async Task Add(List listEntity) + public async Task> Add(List listEntity) { return await BaseDal.Add(listEntity); } @@ -332,11 +332,14 @@ public async Task> QueryPage(PaginationModel pagination) var express = DynamicLinqFactory.CreateLambda(pagination.Conditions); return await QueryPage(express, pagination.PageIndex, pagination.PageSize, pagination.OrderByFileds); } + #region 分表 + public async Task> AddSplit(TEntity entity) { return await BaseDal.AddSplit(entity); } + public async Task UpdateSplit(TEntity entity, DateTime dateTime) { return await BaseDal.UpdateSplit(entity, dateTime); @@ -356,12 +359,14 @@ public async Task QueryByIdSplit(object objId) { return await BaseDal.QueryByIdSplit(objId); } - public async Task> QueryPageSplit(Expression> whereExpression, DateTime beginTime, DateTime endTime, + + public async Task> QueryPageSplit(Expression> whereExpression, DateTime beginTime, DateTime endTime, int pageIndex = 1, int pageSize = 20, string orderByFields = null) { return await BaseDal.QueryPageSplit(whereExpression, beginTime, endTime, pageIndex, pageSize, orderByFields); } + #endregion } } \ No newline at end of file From 0d2a95e0e94553140f550fc4606ef88c8efab824 Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Sat, 1 Apr 2023 23:25:56 +0800 Subject: [PATCH 12/84] =?UTF-8?q?=E2=9C=A8=20=E5=88=9D=E6=AD=A5=E8=B0=83?= =?UTF-8?q?=E6=95=B4Serilog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + Blog.Core.Api/Blog.Core.Api.csproj | 6 +- Blog.Core.Api/Blog.Core.xml | 2 +- Blog.Core.Api/Controllers/ValuesController.cs | 2 +- Blog.Core.Api/Filter/GlobalExceptionFilter.cs | 6 +- Blog.Core.Api/Log4net.config | 364 ----------- Blog.Core.Api/Program.cs | 112 ++-- Blog.Core.Api/Startup.cs | 1 - Blog.Core.Api/appsettings.json | 601 +++++++++--------- Blog.Core.Api/skyapm.json | 2 +- Blog.Core.Common/App.cs | 63 +- Blog.Core.Common/Blog.Core.Common.csproj | 14 +- Blog.Core.Common/Const/SqlSugarConst.cs | 9 + Blog.Core.Common/Core/InternalApp.cs | 25 +- Blog.Core.Common/DB/Aop/SqlsugarAop.cs | 30 +- Blog.Core.Common/DB/BaseDBConfig.cs | 3 +- Blog.Core.Common/Helper/RecursionHelper.cs | 67 +- .../{ => Https}/HttpPolly/HttpPollyHelper.cs | 20 +- .../{ => Https}/HttpPolly/IHttpPollyHelper.cs | 2 +- Blog.Core.Common/Https/RequestIpUtility.cs | 83 +++ Blog.Core.Common/Hubs/ChatHub.cs | 3 +- .../LogHelper/LogContextExtension.cs | 42 ++ .../LogHelper/LogContextStatic.cs | 42 ++ Blog.Core.Common/LogHelper/LogLock.cs | 170 ++--- .../Blog.Core.Extensions.csproj | 3 +- .../Middlewares/ExceptionHandlerMiddleware.cs | 9 +- .../Middlewares/IpLimitMiddleware.cs | 7 +- .../Middlewares/IpLogMiddleware.cs | 7 +- .../Middlewares/MiniProfilerMiddleware.cs | 7 +- .../Middlewares/SignalRSendMiddleware.cs | 1 + .../Middlewares/SwaggerMiddleware.cs | 21 +- .../ServiceExtensions/AppConfigSetup.cs | 38 +- .../AutofacModuleRegister.cs | 20 +- .../ServiceExtensions/HttpPollySetup.cs | 2 +- .../ServiceExtensions/SerilogSetup.cs | 37 ++ .../ServiceExtensions/SqlsugarSetup.cs | 45 +- .../ServiceExtensions/SwaggerSetup.cs | 26 +- .../Blog.Core.Serilog.Es.csproj | 2 +- Blog.Core.Serilog/Blog.Core.Serilog.csproj | 13 + .../LoggerConfigurationExtensions.cs | 121 ++++ .../Utility/SerilogRequestUtility.cs | 34 + .../Jobs/Job_AccessTrendLog_Quartz.cs | 4 +- .../QuartzNet/Jobs/Job_OperateLog_Quartz.cs | 17 +- .../DependencyInjection/DI_Test.cs | 1 - Blog.Core.sln | 6 + 45 files changed, 1137 insertions(+), 955 deletions(-) delete mode 100644 Blog.Core.Api/Log4net.config create mode 100644 Blog.Core.Common/Const/SqlSugarConst.cs rename Blog.Core.Common/{ => Https}/HttpPolly/HttpPollyHelper.cs (98%) rename Blog.Core.Common/{ => Https}/HttpPolly/IHttpPollyHelper.cs (96%) create mode 100644 Blog.Core.Common/Https/RequestIpUtility.cs create mode 100644 Blog.Core.Common/LogHelper/LogContextExtension.cs create mode 100644 Blog.Core.Common/LogHelper/LogContextStatic.cs create mode 100644 Blog.Core.Extensions/ServiceExtensions/SerilogSetup.cs create mode 100644 Blog.Core.Serilog/Blog.Core.Serilog.csproj create mode 100644 Blog.Core.Serilog/Extensions/LoggerConfigurationExtensions.cs create mode 100644 Blog.Core.Serilog/Utility/SerilogRequestUtility.cs diff --git a/.gitignore b/.gitignore index b7645c45..4f554bea 100644 --- a/.gitignore +++ b/.gitignore @@ -356,3 +356,5 @@ Blog.Core/Blog.Core*.xml Blog.Core.Api/WMBlog.db Blog.Core.Api/wwwroot/ui/ *.db +/Blog.Core.Api/WMBlog.db-journal +Logs diff --git a/Blog.Core.Api/Blog.Core.Api.csproj b/Blog.Core.Api/Blog.Core.Api.csproj index 77d681f1..3bf64399 100644 --- a/Blog.Core.Api/Blog.Core.Api.csproj +++ b/Blog.Core.Api/Blog.Core.Api.csproj @@ -26,21 +26,25 @@ + + + + @@ -51,8 +55,6 @@ - - diff --git a/Blog.Core.Api/Blog.Core.xml b/Blog.Core.Api/Blog.Core.xml index 89cb3213..488903a0 100644 --- a/Blog.Core.Api/Blog.Core.xml +++ b/Blog.Core.Api/Blog.Core.xml @@ -760,7 +760,7 @@ Values控制器
- + ValuesController diff --git a/Blog.Core.Api/Controllers/ValuesController.cs b/Blog.Core.Api/Controllers/ValuesController.cs index 1347ca16..677eb138 100644 --- a/Blog.Core.Api/Controllers/ValuesController.cs +++ b/Blog.Core.Api/Controllers/ValuesController.cs @@ -1,7 +1,7 @@ using AutoMapper; using Blog.Core.Common; using Blog.Core.Common.HttpContextUser; -using Blog.Core.Common.HttpPolly; +using Blog.Core.Common.Https.HttpPolly; using Blog.Core.Common.WebApiClients.HttpApis; using Blog.Core.EventBus; using Blog.Core.EventBus.EventHandling; diff --git a/Blog.Core.Api/Filter/GlobalExceptionFilter.cs b/Blog.Core.Api/Filter/GlobalExceptionFilter.cs index da119a6e..44c6124c 100644 --- a/Blog.Core.Api/Filter/GlobalExceptionFilter.cs +++ b/Blog.Core.Api/Filter/GlobalExceptionFilter.cs @@ -3,14 +3,10 @@ using Blog.Core.Common.LogHelper; using Blog.Core.Hubs; using Blog.Core.Model; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.SignalR; -using Microsoft.Extensions.Logging; using StackExchange.Profiling; -using System; namespace Blog.Core.Filter { @@ -54,7 +50,7 @@ public void OnException(ExceptionContext context) MiniProfiler.Current.CustomTiming("Errors:", json.msg); - //采用log4net 进行错误日志记录 + //进行错误日志记录 _loggerHelper.LogError(json.msg + WriteLog(json.msg, context.Exception)); if (AppSettings.app(new string[] { "Middleware", "SignalRSendLog", "Enabled" }).ObjToBool()) { diff --git a/Blog.Core.Api/Log4net.config b/Blog.Core.Api/Log4net.config deleted file mode 100644 index 61bd3739..00000000 --- a/Blog.Core.Api/Log4net.config +++ /dev/null @@ -1,364 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Blog.Core.Api/Program.cs b/Blog.Core.Api/Program.cs index f5223beb..f7790a8c 100644 --- a/Blog.Core.Api/Program.cs +++ b/Blog.Core.Api/Program.cs @@ -1,20 +1,16 @@ // 以下为asp.net 6.0的写法,如果用5.0,请看Program.five.cs文件 -using System.IdentityModel.Tokens.Jwt; -using System.Reflection; -using System.Text; + using Autofac; using Autofac.Extensions.DependencyInjection; using Blog.Core; using Blog.Core.Common; using Blog.Core.Common.Core; -using Blog.Core.Common.LogHelper; using Blog.Core.Extensions; using Blog.Core.Extensions.Apollo; using Blog.Core.Extensions.Middlewares; +using Blog.Core.Extensions.ServiceExtensions; using Blog.Core.Filter; using Blog.Core.Hubs; -using Blog.Core.IServices; -using Blog.Core.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Server.Kestrel.Core; @@ -22,34 +18,38 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; +using Serilog; +using System.IdentityModel.Tokens.Jwt; +using System.Reflection; +using System.Text; +using Blog.Core.Common.Https; +using Blog.Core.Serilog.Utility; var builder = WebApplication.CreateBuilder(args); + + // 1、配置host与容器 builder.Host -.UseServiceProviderFactory(new AutofacServiceProviderFactory()) -.ConfigureContainer(builder => -{ - builder.RegisterModule(new AutofacModuleRegister()); - builder.RegisterModule(); -}) -.ConfigureLogging((hostingContext, builder) => -{ - builder.AddFilter("System", LogLevel.Error); - builder.AddFilter("Microsoft", LogLevel.Error); - builder.SetMinimumLevel(LogLevel.Error); - builder.AddLog4Net(Path.Combine(Directory.GetCurrentDirectory(), "Log4net.config")); -}) -.ConfigureAppConfiguration((hostingContext, config) => -{ - config.Sources.Clear(); - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false); - config.AddConfigurationApollo("appsettings.apollo.json"); -}); + .UseServiceProviderFactory(new AutofacServiceProviderFactory()) + .ConfigureContainer(builder => + { + builder.RegisterModule(new AutofacModuleRegister()); + builder.RegisterModule(); + }) + .ConfigureAppConfiguration((hostingContext, config) => + { + config.Sources.Clear(); + config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false); + config.AddConfigurationApollo("appsettings.apollo.json"); + }); +builder.ConfigureApplication(); // 2、配置服务 builder.Services.AddSingleton(new AppSettings(builder.Configuration)); -builder.Services.AddSingleton(new LogLock(builder.Environment.ContentRootPath)); + + + builder.Services.AddUiFilesZipSetup(builder.Environment); Permissions.IsUseIds4 = AppSettings.app(new string[] { "Startup", "IdentityServer4", "Enabled" }).ObjToBool(); @@ -62,6 +62,9 @@ builder.Services.AddRedisCacheSetup(); builder.Services.AddSqlsugarSetup(); builder.Services.AddDbSetup(); + +builder.Host.AddSerilogSetup(); + builder.Services.AddAutoMapperSetup(); builder.Services.AddCorsSetup(); builder.Services.AddMiniProfilerSetup(); @@ -92,34 +95,34 @@ builder.Services.AddSignalR().AddNewtonsoftJsonProtocol(); builder.Services.AddScoped(); builder.Services.Configure(x => x.AllowSynchronousIO = true) - .Configure(x => x.AllowSynchronousIO = true); + .Configure(x => x.AllowSynchronousIO = true); builder.Services.AddDistributedMemoryCache(); builder.Services.AddSession(); builder.Services.AddHttpPollySetup(); builder.Services.AddControllers(o => -{ - o.Filters.Add(typeof(GlobalExceptionsFilter)); - //o.Conventions.Insert(0, new GlobalRouteAuthorizeConvention()); - o.Conventions.Insert(0, new GlobalRoutePrefixFilter(new RouteAttribute(RoutePrefix.Name))); -}) -.AddNewtonsoftJson(options => -{ - options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; - options.SerializerSettings.ContractResolver = new DefaultContractResolver(); - options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; - //options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; - options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local; - options.SerializerSettings.Converters.Add(new StringEnumConverter()); -}) -//.AddFluentValidation(config => -//{ -// //程序集方式添加验证 -// config.RegisterValidatorsFromAssemblyContaining(typeof(UserRegisterVoValidator)); -// //是否与MvcValidation共存 -// config.DisableDataAnnotationsValidation = true; -//}) -; + { + o.Filters.Add(typeof(GlobalExceptionsFilter)); + //o.Conventions.Insert(0, new GlobalRouteAuthorizeConvention()); + o.Conventions.Insert(0, new GlobalRoutePrefixFilter(new RouteAttribute(RoutePrefix.Name))); + }) + .AddNewtonsoftJson(options => + { + options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + options.SerializerSettings.ContractResolver = new DefaultContractResolver(); + options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; + //options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; + options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local; + options.SerializerSettings.Converters.Add(new StringEnumConverter()); + }) + //.AddFluentValidation(config => + //{ + // //程序集方式添加验证 + // config.RegisterValidatorsFromAssemblyContaining(typeof(UserRegisterVoValidator)); + // //是否与MvcValidation共存 + // config.DisableDataAnnotationsValidation = true; + //}) + ; builder.Services.AddEndpointsApiExplorer(); @@ -159,12 +162,23 @@ app.UseStaticFiles(); app.UseCookiePolicy(); app.UseStatusCodePages(); +app.UseSerilogRequestLogging(options => +{ + options.GetLevel = SerilogRequestUtility.GetRequestLevel; + options.EnrichDiagnosticContext = (diagnosticContext, httpContext) => + { + diagnosticContext.Set("RequestHost", httpContext.Request.Host.Value); + diagnosticContext.Set("RequestScheme", httpContext.Request.Scheme); + diagnosticContext.Set("RequestIp", httpContext.GetRequestIp()); + }; +}); app.UseRouting(); if (builder.Configuration.GetValue("AppSettings:UseLoadTest")) { app.UseMiddleware(); } + app.UseAuthentication(); app.UseAuthorization(); app.UseMiniProfilerMiddleware(); diff --git a/Blog.Core.Api/Startup.cs b/Blog.Core.Api/Startup.cs index bc1630f1..1364d6a0 100644 --- a/Blog.Core.Api/Startup.cs +++ b/Blog.Core.Api/Startup.cs @@ -39,7 +39,6 @@ public void ConfigureServices(IServiceCollection services) { // 以下code可能与文章中不一样,对代码做了封装,具体查看右侧 Extensions 文件夹. services.AddSingleton(new AppSettings(Configuration)); - services.AddSingleton(new LogLock(Env.ContentRootPath)); services.AddUiFilesZipSetup(Env); Permissions.IsUseIds4 = AppSettings.app(new string[] { "Startup", "IdentityServer4", "Enabled" }).ObjToBool(); diff --git a/Blog.Core.Api/appsettings.json b/Blog.Core.Api/appsettings.json index 3c117375..c712054c 100644 --- a/Blog.Core.Api/appsettings.json +++ b/Blog.Core.Api/appsettings.json @@ -1,98 +1,92 @@ { - "urls": "http://*:9291", //web服务端口,如果用IIS部署,把这个去掉 - "Logging": { - "LogLevel": { - "Default": "Information", //加入Default否则log4net本地写入不了日志 - "Blog.Core.AuthHelper.ApiResponseHandler": "Error" - }, - "Debug": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Warning" - } - }, - "Console": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Warning", - "Microsoft.Hosting.Lifetime": "Debug" - } - }, - "Log4Net": { - "Name": "Blog.Core" - } - }, - "AllowedHosts": "*", - "Redis": { - "ConnectionString": "127.0.0.1:6319,password=admin" + "urls": "http://*:9291", //web服务端口,如果用IIS部署,把这个去掉 + "Serilog": { + "MinimumLevel": { + "Default": "Debug", + "Override": { + "Microsoft": "Information", + "Microsoft.AspNetCore": "Warning", + "System": "Warning", + "System.Net.Http.HttpClient": "Warning", + "Hangfire": "Information", + "Magicodes": "Warning", + "DotNetCore.CAP": "Information", + "Savorboard.CAP": "Information", + "Quartz": "Information" + } + } + }, + "AllowedHosts": "*", + "Redis": { + "ConnectionString": "127.0.0.1:6319,password=admin" + }, + "RabbitMQ": { + "Enabled": false, + "Connection": "118.25.251.13", + "UserName": "", + "Password": "!", + "RetryCount": 3 + }, + "Kafka": { + "Enabled": false, + "Servers": "localhost:9092", + "Topic": "blog", + "GroupId": "blog-consumer", + "NumPartitions": 3 //主题分区数量 + }, + "EventBus": { + "Enabled": false, + "SubscriptionClientName": "Blog.Core" + }, + "AppSettings": { + "RedisCachingAOP": { + "Enabled": false }, - "RabbitMQ": { - "Enabled": false, - "Connection": "118.25.251.13", - "UserName": "", - "Password": "!", - "RetryCount": 3 + "MemoryCachingAOP": { + "Enabled": true }, - "Kafka": { - "Enabled": false, - "Servers": "localhost:9092", - "Topic": "blog", - "GroupId": "blog-consumer", - "NumPartitions": 3 //主题分区数量 + "LogAOP": { + "Enabled": true, + "LogToFile": { + "Enabled": false + }, + "LogToDB": { + "Enabled": true + } }, - "EventBus": { - "Enabled": false, - "SubscriptionClientName": "Blog.Core" + "TranAOP": { + "Enabled": true }, - "AppSettings": { - "RedisCachingAOP": { - "Enabled": false - }, - "MemoryCachingAOP": { - "Enabled": true - }, - "LogAOP": { - "Enabled": true, - "LogToFile": { - "Enabled": false - }, - "LogToDB": { - "Enabled": true - } - }, - "TranAOP": { - "Enabled": true - }, - "SqlAOP": { - "Enabled": true, - "LogToFile": { - "Enabled": false - }, - "LogToDB": { - "Enabled": false - }, - "LogToConsole": { - "Enabled": true - } - }, - "Date": "2018-08-28", - "SeedDBEnabled": true, //只生成表结构 - "SeedDBDataEnabled": true, //生成表,并初始化数据 - "Author": "Blog.Core", - "SvcName": "", // /svc/blog - "UseLoadTest": false + "SqlAOP": { + "Enabled": true, + "LogToFile": { + "Enabled": true + }, + "LogToDB": { + "Enabled": false + }, + "LogToConsole": { + "Enabled": true + } }, + "Date": "2018-08-28", + "SeedDBEnabled": true, //只生成表结构 + "SeedDBDataEnabled": true, //生成表,并初始化数据 + "Author": "Blog.Core", + "SvcName": "", // /svc/blog + "UseLoadTest": false + }, - // 请配置MainDB为你想要的主库的ConnId值,并设置对应的Enabled为true; - // *** 单库操作,把 MutiDBEnabled 设为false ***; - // *** 多库操作,把 MutiDBEnabled 设为true,其他的从库Enabled也为true **; - // 具体配置看视频:https://www.bilibili.com/video/BV1BJ411B7mn?p=6 - - "MainDB": "WMBLOG_SQLITE", //当前项目的主库,所对应的连接字符串的Enabled必须为true - "MutiDBEnabled": false, //是否开启多库模式 - "CQRSEnabled": false, //是否开启读写分离模式,必须是单库模式,且数据库类型一致,比如都是SqlServer - "DBS": [ - /* + // 请配置MainDB为你想要的主库的ConnId值,并设置对应的Enabled为true; + // *** 单库操作,把 MutiDBEnabled 设为false ***; + // *** 多库操作,把 MutiDBEnabled 设为true,其他的从库Enabled也为true **; + // 具体配置看视频:https://www.bilibili.com/video/BV1BJ411B7mn?p=6 + //Log:日志库; + "MainDB": "WMBLOG_SQLITE", //当前项目的主库,所对应的连接字符串的Enabled必须为true + "MutiDBEnabled": true, //是否开启多库模式 + "CQRSEnabled": false, //是否开启读写分离模式,必须是单库模式,且数据库类型一致,比如都是SqlServer + "DBS": [ + /* 对应下边的 DBType MySql = 0, SqlServer = 1, @@ -102,225 +96,232 @@ Dm = 5,//达梦 Kdbndp = 6,//人大金仓 */ - { - "ConnId": "WMBLOG_SQLITE", - "DBType": 2, - "Enabled": true, - "HitRate": 50, // 值越大,优先级越高 - "Connection": "WMBlog.db" //sqlite只写数据库名就行 - }, - { - "ConnId": "WMBLOG_MSSQL_1", - "DBType": 1, - "Enabled": false, - "HitRate": 40, - "Connection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=WMBLOG_MSSQL_1;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", - "ProviderName": "System.Data.SqlClient" - }, - { - "ConnId": "WMBLOG_MSSQL_2", - "DBType": 1, - "Enabled": false, - "HitRate": 30, - "Connection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=WMBLOG_MSSQL_2;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", - "ProviderName": "System.Data.SqlClient" - }, - { - "ConnId": "WMBLOG_MYSQL", - "DBType": 0, - "Enabled": false, - "HitRate": 20, - "Connection": "server=localhost;Database=blog;Uid=root;Pwd=root;Port=3306;Allow User Variables=True;" - }, - { - "ConnId": "WMBLOG_MYSQL_2", - "DBType": 0, - "Enabled": false, - "HitRate": 20, - "Connection": "server=localhost;Database=blogcore001;Uid=root;Pwd=root;Port=3306;Allow User Variables=True;" - }, - { - "ConnId": "WMBLOG_ORACLE", - "DBType": 3, - "Enabled": false, - "HitRate": 10, - "Connection": "Data Source=127.0.0.1/ops;User ID=OPS;Password=123456;Persist Security Info=True;Connection Timeout=60;" - }, - { - "ConnId": "WMBLOG_DM", - "DBType": 5, - "Enabled": false, - "HitRate": 10, - "Connection": "PORT=5236;DATABASE=DAMENG;HOST=localhost;PASSWORD=SYSDBA;USER ID=SYSDBA;" - }, - { - "ConnId": "WMBLOG_KDBNDP", - "DBType": 6, - "Enabled": false, - "HitRate": 10, - "Connection": "Server=127.0.0.1;Port=54321;UID=SYSTEM;PWD=system;database=SQLSUGAR4XTEST1;" - } - ], - "Audience": { - "Secret": "sdfsdfsrty45634kkhllghtdgdfss345t678fs", //不要太短,16位+ - "SecretFile": "C:\\my-file\\blog.core.audience.secret.txt", //安全。内容就是Secret - "Issuer": "Blog.Core", - "Audience": "wr" + { + "ConnId": "WMBLOG_SQLITE", + "DBType": 2, + "Enabled": true, + "HitRate": 50, // 值越大,优先级越高 + "Connection": "WMBlog.db" //sqlite只写数据库名就行 }, - "Mongo": { - "ConnectionString": "mongodb://nosql.data", - "Database": "BlogCoreDb" + { + "ConnId": "Log", + "DBType": 2, + "Enabled": true, + "HitRate": 50, // 值越大,优先级越高 + "Connection": "WMBlogLog.db" //sqlite只写数据库名就行 }, - "Startup": { - "Domain": "http://localhost:9291", - "Cors": { - "PolicyName": "CorsIpAccess", //策略名称 - "EnableAllIPs": false, //当为true时,开放所有IP均可访问。 - // 支持多个域名端口,注意端口号后不要带/斜杆:比如localhost:8000/,是错的 - // 注意,http://127.0.0.1:1818 和 http://localhost:1818 是不一样的 - "IPs": "http://127.0.0.1:2364,http://localhost:2364,http://127.0.0.1:6688,http://localhost:6688" - }, - "AppConfigAlert": { - "Enabled": true - }, - "ApiName": "Blog.Core", - "IdentityServer4": { - "Enabled": false, // 这里默认是false,表示使用jwt,如果设置为true,则表示系统使用Ids4模式 - "AuthorizationUrl": "http://localhost:5004", // 认证中心域名 - "ApiName": "blog.core.api" // 资源服务器 - }, - "Authing": { - "Enabled": false, - "Issuer": "https://uldr24esx31h-demo.authing.cn/oidc", - "Audience": "63d51c4205c2849803be5178", - "JwksUri": "https://uldr24esx31h-demo.authing.cn/oidc/.well-known/jwks.json" - }, - "RedisMq": { - "Enabled": false //redis 消息队列 - }, - "MiniProfiler": { - "Enabled": false //性能分析开启 - }, - "Nacos": { - "Enabled": false //Nacos注册中心 - } + { + "ConnId": "WMBLOG_MSSQL_1", + "DBType": 1, + "Enabled": false, + "HitRate": 40, + "Connection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=WMBLOG_MSSQL_1;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", + "ProviderName": "System.Data.SqlClient" }, - "Middleware": { - "RequestResponseLog": { - "Enabled": true, - "LogToFile": { - "Enabled": false - }, - "LogToDB": { - "Enabled": true - } - }, - "IPLog": { - "Enabled": true, - "LogToFile": { - "Enabled": false - }, - "LogToDB": { - "Enabled": true - } - }, - "RecordAccessLogs": { - "Enabled": true, - "LogToFile": { - "Enabled": false - }, - "LogToDB": { - "Enabled": true - }, - "IgnoreApis": "/api/permission/getnavigationbar,/api/monitor/getids4users,/api/monitor/getaccesslogs,/api/monitor/server,/api/monitor/getactiveusers,/api/monitor/server," - }, - "SignalR": { - "Enabled": false - }, - "SignalRSendLog": { - "Enabled": false - }, - "QuartzNetJob": { - "Enabled": true - }, - "Consul": { - "Enabled": false - }, - "IpRateLimit": { - "Enabled": true - } + { + "ConnId": "WMBLOG_MSSQL_2", + "DBType": 1, + "Enabled": false, + "HitRate": 30, + "Connection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=WMBLOG_MSSQL_2;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", + "ProviderName": "System.Data.SqlClient" }, - "IpRateLimiting": { - "EnableEndpointRateLimiting": true, //False: globally executed, true: executed for each - "StackBlockedRequests": false, //False: Number of rejections should be recorded on another counter - "RealIpHeader": "X-Real-IP", - "ClientIdHeader": "X-ClientId", - "IpWhitelist": [], //白名单 - "EndpointWhitelist": [ "get:/api/xxx", "*:/api/yyy" ], - "ClientWhitelist": [ "dev-client-1", "dev-client-2" ], - "QuotaExceededResponse": { - "Content": "{{\"status\":429,\"msg\":\"访问过于频繁,请稍后重试\",\"success\":false}}", - "ContentType": "application/json", - "StatusCode": 429 - }, - "HttpStatusCode": 429, //返回状态码 - "GeneralRules": [ //api规则,结尾一定要带* - { - "Endpoint": "*:/api/blog*", - "Period": "1m", - "Limit": 20 - }, - { - "Endpoint": "*/api/*", - "Period": "1s", - "Limit": 3 - }, - { - "Endpoint": "*/api/*", - "Period": "1m", - "Limit": 30 - }, - { - "Endpoint": "*/api/*", - "Period": "12h", - "Limit": 500 - } - ] - + { + "ConnId": "WMBLOG_MYSQL", + "DBType": 0, + "Enabled": false, + "HitRate": 20, + "Connection": "server=localhost;Database=blog;Uid=root;Pwd=root;Port=3306;Allow User Variables=True;" + }, + { + "ConnId": "WMBLOG_MYSQL_2", + "DBType": 0, + "Enabled": false, + "HitRate": 20, + "Connection": "server=localhost;Database=blogcore001;Uid=root;Pwd=root;Port=3306;Allow User Variables=True;" + }, + { + "ConnId": "WMBLOG_ORACLE", + "DBType": 3, + "Enabled": false, + "HitRate": 10, + "Connection": "Data Source=127.0.0.1/ops;User ID=OPS;Password=123456;Persist Security Info=True;Connection Timeout=60;" + }, + { + "ConnId": "WMBLOG_DM", + "DBType": 5, + "Enabled": false, + "HitRate": 10, + "Connection": "PORT=5236;DATABASE=DAMENG;HOST=localhost;PASSWORD=SYSDBA;USER ID=SYSDBA;" + }, + { + "ConnId": "WMBLOG_KDBNDP", + "DBType": 6, + "Enabled": false, + "HitRate": 10, + "Connection": "Server=127.0.0.1;Port=54321;UID=SYSTEM;PWD=system;database=SQLSUGAR4XTEST1;" + } + ], + "Audience": { + "Secret": "sdfsdfsrty45634kkhllghtdgdfss345t678fs", //不要太短,16位+ + "SecretFile": "C:\\my-file\\blog.core.audience.secret.txt", //安全。内容就是Secret + "Issuer": "Blog.Core", + "Audience": "wr" + }, + "Mongo": { + "ConnectionString": "mongodb://nosql.data", + "Database": "BlogCoreDb" + }, + "Startup": { + "Domain": "http://localhost:9291", + "Cors": { + "PolicyName": "CorsIpAccess", //策略名称 + "EnableAllIPs": false, //当为true时,开放所有IP均可访问。 + // 支持多个域名端口,注意端口号后不要带/斜杆:比如localhost:8000/,是错的 + // 注意,http://127.0.0.1:1818 和 http://localhost:1818 是不一样的 + "IPs": "http://127.0.0.1:2364,http://localhost:2364,http://127.0.0.1:6688,http://localhost:6688" + }, + "AppConfigAlert": { + "Enabled": true + }, + "ApiName": "Blog.Core", + "IdentityServer4": { + "Enabled": false, // 这里默认是false,表示使用jwt,如果设置为true,则表示系统使用Ids4模式 + "AuthorizationUrl": "http://localhost:5004", // 认证中心域名 + "ApiName": "blog.core.api" // 资源服务器 }, - "ConsulSetting": { - "ServiceName": "BlogCoreService", - "ServiceIP": "localhost", - "ServicePort": "9291", - "ServiceHealthCheck": "/healthcheck", - "ConsulAddress": "http://localhost:8500" + "Authing": { + "Enabled": false, + "Issuer": "https://uldr24esx31h-demo.authing.cn/oidc", + "Audience": "63d51c4205c2849803be5178", + "JwksUri": "https://uldr24esx31h-demo.authing.cn/oidc/.well-known/jwks.json" }, - "PayInfo": { //建行聚合支付信息 - "MERCHANTID": "", //商户号 - "POSID": "", //柜台号 - "BRANCHID": "", //分行号 - "pubKey": "", //公钥 - "USER_ID": "", //操作员号 - "PASSWORD": "", //密码 - "OutAddress": "http://127.0.0.1:12345" //外联地址 + "RedisMq": { + "Enabled": false //redis 消息队列 }, - "nacos": { - "ServerAddresses": [ "http://localhost:8848" ], // nacos 连接地址 - "DefaultTimeOut": 15000, // 默认超时时间 - "Namespace": "public", // 命名空间 - "ListenInterval": 10000, // 监听的频率 - "ServiceName": "blog.Core.Api", // 服务名 - "Port": "9291", // 服务端口号 - "RegisterEnabled": true // 是否直接注册nacos + "MiniProfiler": { + "Enabled": false //性能分析开启 }, - "LogFiedOutPutConfigs": { - "tcpAddressHost": "", // 输出elk的tcp连接地址 - "tcpAddressPort": 0, // 输出elk的tcp端口号 - "ConfigsInfo": [ // 配置的输出elk节点内容 常用语动态标识 - { - "FiedName": "applicationName", - "FiedValue": "Blog.Core.Api" - } - ] + "Nacos": { + "Enabled": false //Nacos注册中心 } + }, + "Middleware": { + "RequestResponseLog": { + "Enabled": true, + "LogToFile": { + "Enabled": false + }, + "LogToDB": { + "Enabled": true + } + }, + "IPLog": { + "Enabled": true, + "LogToFile": { + "Enabled": false + }, + "LogToDB": { + "Enabled": true + } + }, + "RecordAccessLogs": { + "Enabled": true, + "LogToFile": { + "Enabled": false + }, + "LogToDB": { + "Enabled": true + }, + "IgnoreApis": "/api/permission/getnavigationbar,/api/monitor/getids4users,/api/monitor/getaccesslogs,/api/monitor/server,/api/monitor/getactiveusers,/api/monitor/server," + }, + "SignalR": { + "Enabled": false + }, + "SignalRSendLog": { + "Enabled": false + }, + "QuartzNetJob": { + "Enabled": true + }, + "Consul": { + "Enabled": false + }, + "IpRateLimit": { + "Enabled": true + } + }, + "IpRateLimiting": { + "EnableEndpointRateLimiting": true, //False: globally executed, true: executed for each + "StackBlockedRequests": false, //False: Number of rejections should be recorded on another counter + "RealIpHeader": "X-Real-IP", + "ClientIdHeader": "X-ClientId", + "IpWhitelist": [], //白名单 + "EndpointWhitelist": [ "get:/api/xxx", "*:/api/yyy" ], + "ClientWhitelist": [ "dev-client-1", "dev-client-2" ], + "QuotaExceededResponse": { + "Content": "{{\"status\":429,\"msg\":\"访问过于频繁,请稍后重试\",\"success\":false}}", + "ContentType": "application/json", + "StatusCode": 429 + }, + "HttpStatusCode": 429, //返回状态码 + "GeneralRules": [ //api规则,结尾一定要带* + { + "Endpoint": "*:/api/blog*", + "Period": "1m", + "Limit": 20 + }, + { + "Endpoint": "*/api/*", + "Period": "1s", + "Limit": 3 + }, + { + "Endpoint": "*/api/*", + "Period": "1m", + "Limit": 30 + }, + { + "Endpoint": "*/api/*", + "Period": "12h", + "Limit": 500 + } + ] + + }, + "ConsulSetting": { + "ServiceName": "BlogCoreService", + "ServiceIP": "localhost", + "ServicePort": "9291", + "ServiceHealthCheck": "/healthcheck", + "ConsulAddress": "http://localhost:8500" + }, + "PayInfo": { //建行聚合支付信息 + "MERCHANTID": "", //商户号 + "POSID": "", //柜台号 + "BRANCHID": "", //分行号 + "pubKey": "", //公钥 + "USER_ID": "", //操作员号 + "PASSWORD": "", //密码 + "OutAddress": "http://127.0.0.1:12345" //外联地址 + }, + "nacos": { + "ServerAddresses": [ "http://localhost:8848" ], // nacos 连接地址 + "DefaultTimeOut": 15000, // 默认超时时间 + "Namespace": "public", // 命名空间 + "ListenInterval": 10000, // 监听的频率 + "ServiceName": "blog.Core.Api", // 服务名 + "Port": "9291", // 服务端口号 + "RegisterEnabled": true // 是否直接注册nacos + }, + "LogFiedOutPutConfigs": { + "tcpAddressHost": "", // 输出elk的tcp连接地址 + "tcpAddressPort": 0, // 输出elk的tcp端口号 + "ConfigsInfo": [ // 配置的输出elk节点内容 常用语动态标识 + { + "FiedName": "applicationName", + "FiedValue": "Blog.Core.Api" + } + ] + } } diff --git a/Blog.Core.Api/skyapm.json b/Blog.Core.Api/skyapm.json index cd5ed0ee..cdb0e606 100644 --- a/Blog.Core.Api/skyapm.json +++ b/Blog.Core.Api/skyapm.json @@ -11,7 +11,7 @@ }, "Logging": { "Level": "Information", - "FilePath": "Log/skyapm-{Date}.log" + "FilePath": "Logs/Skyapm/skyapm-{Date}.log" }, "Transport": { "Interval": 3000, diff --git a/Blog.Core.Common/App.cs b/Blog.Core.Common/App.cs index 008aea5d..c2e2e706 100644 --- a/Blog.Core.Common/App.cs +++ b/Blog.Core.Common/App.cs @@ -1,14 +1,24 @@ using Blog.Core.Common.Core; using Blog.Core.Common.HttpContextUser; +using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; using System; +using System.Linq; namespace Blog.Core.Common; public class App { - public static IServiceProvider RootServices => InternalApp.RootServices ; + public static IServiceProvider RootServices => InternalApp.RootServices; + + /// 获取Web主机环境,如,是否是开发环境,生产环境等 + public static IWebHostEnvironment WebHostEnvironment => InternalApp.WebHostEnvironment; + + /// 获取泛型主机环境,如,是否是开发环境,生产环境等 + public static IHostEnvironment HostEnvironment => InternalApp.HostEnvironment; /// /// 获取请求上下文 @@ -16,4 +26,55 @@ public class App public static HttpContext HttpContext => RootServices?.GetService()?.HttpContext; public static IUser User => HttpContext == null ? null : RootServices?.GetService(); + + /// 解析服务提供器 + /// + /// + public static IServiceProvider GetServiceProvider(Type serviceType, bool mustBuild = false) + { + if (App.HostEnvironment == null || App.RootServices != null && + InternalApp.InternalServices + .Where((u => u.ServiceType == (serviceType.IsGenericType ? serviceType.GetGenericTypeDefinition() : serviceType))) + .Any((u => u.Lifetime == ServiceLifetime.Singleton))) + return App.RootServices; + HttpContext httpContext = App.HttpContext; + if (httpContext?.RequestServices != null) + return httpContext.RequestServices; + if (App.RootServices != null) + { + IServiceScope scope = App.RootServices.CreateScope(); + return scope.ServiceProvider; + } + + if (mustBuild) + { + throw new ApplicationException("当前不可用,必须要等到 WebApplication Build后"); + } + + ServiceProvider serviceProvider = InternalApp.InternalServices.BuildServiceProvider(); + return serviceProvider; + } + + + public static TService GetService(bool mustBuild = true) where TService : class => App.GetService(typeof(TService), null, mustBuild) as TService; + + /// 获取请求生存周期的服务 + /// + /// + /// + /// + public static TService GetService(IServiceProvider serviceProvider, bool mustBuild = true) where TService : class => App.GetService(typeof(TService), serviceProvider, mustBuild) as TService; + + /// 获取请求生存周期的服务 + /// + /// + /// + /// + public static object GetService(Type type, IServiceProvider serviceProvider = null, bool mustBuild = true) => (serviceProvider ?? App.GetServiceProvider(type, mustBuild)).GetService(type); + + public static TOptions GetOptions(IServiceProvider serviceProvider = null) where TOptions : class, new() + { + IOptions service = App.GetService>(serviceProvider ?? App.RootServices, false); + return service?.Value; + } } \ 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 fc0abb9d..0662bac5 100644 --- a/Blog.Core.Common/Blog.Core.Common.csproj +++ b/Blog.Core.Common/Blog.Core.Common.csproj @@ -18,20 +18,23 @@ - + - + + + + + - - - + + @@ -45,6 +48,7 @@ + diff --git a/Blog.Core.Common/Const/SqlSugarConst.cs b/Blog.Core.Common/Const/SqlSugarConst.cs new file mode 100644 index 00000000..f5efd7e2 --- /dev/null +++ b/Blog.Core.Common/Const/SqlSugarConst.cs @@ -0,0 +1,9 @@ +namespace Blog.Core.Common.Const; + +public class SqlSugarConst +{ + /// + /// 默认Log数据库标识 + /// + public const string LogConfigId = "Log"; +} \ No newline at end of file diff --git a/Blog.Core.Common/Core/InternalApp.cs b/Blog.Core.Common/Core/InternalApp.cs index c1ae8dcd..62e04724 100644 --- a/Blog.Core.Common/Core/InternalApp.cs +++ b/Blog.Core.Common/Core/InternalApp.cs @@ -1,17 +1,34 @@ -using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; namespace Blog.Core.Common.Core; public static class InternalApp { + public static IServiceCollection InternalServices; + /// 根服务 public static IServiceProvider RootServices; - public static void ConfigureApplication(this WebApplication app) + /// 获取Web主机环境 + public static IWebHostEnvironment WebHostEnvironment; + + /// 获取泛型主机环境 + public static IHostEnvironment HostEnvironment; + + public static void ConfigureApplication(this WebApplicationBuilder wab) { - app.Lifetime.ApplicationStarted.Register(() => { InternalApp.RootServices = app.Services; }); + HostEnvironment = wab.Environment; + WebHostEnvironment = wab.Environment; + InternalServices = wab.Services; + } + - app.Lifetime.ApplicationStopped.Register(() => { InternalApp.RootServices = null; }); + public static void ConfigureApplication(this IHost app) + { + RootServices = app.Services; } } \ No newline at end of file diff --git a/Blog.Core.Common/DB/Aop/SqlsugarAop.cs b/Blog.Core.Common/DB/Aop/SqlsugarAop.cs index 3d83b002..c3d374ae 100644 --- a/Blog.Core.Common/DB/Aop/SqlsugarAop.cs +++ b/Blog.Core.Common/DB/Aop/SqlsugarAop.cs @@ -1,12 +1,40 @@ -using Blog.Core.Model.Models.RootTkey; +using Blog.Core.Common.LogHelper; +using Blog.Core.Model.Models.RootTkey; using Blog.Core.Model.Tenants; using SqlSugar; +using StackExchange.Profiling; using System; +using Serilog; namespace Blog.Core.Common.DB.Aop; public static class SqlSugarAop { + public static void OnLogExecuting(ISqlSugarClient sqlSugarScopeProvider, string sql, SugarParameter[] p, ConnectionConfig config) + { + try + { + MiniProfiler.Current.CustomTiming($"ConnId:[{config.ConfigId}] SQL:", GetParas(p) + "【SQL语句】:" + sql); + + if (!AppSettings.app(new string[] { "AppSettings", "SqlAOP", "Enabled" }).ObjToBool()) return; + + if (AppSettings.app(new string[] { "AppSettings", "SqlAOP", "LogToConsole", "Enabled" }).ObjToBool() || + AppSettings.app(new string[] { "AppSettings", "SqlAOP", "LogToFile", "Enabled" }).ObjToBool() || + AppSettings.app(new string[] { "AppSettings", "SqlAOP", "LogToDB", "Enabled" }).ObjToBool()) + { + using (LogContextExtension.Create.SqlAopPushProperty(sqlSugarScopeProvider)) + { + Log.Information("------------------ \r\n ConnId:[{ConnId}]【SQL语句】: \r\n {Sql}", + config.ConfigId, UtilMethods.GetSqlString(config.DbType, sql, p)); + } + } + } + catch (Exception e) + { + Log.Error("Error occured OnLogExcuting:" + e); + } + } + public static void DataExecuting(object oldValue, DataFilterModel entityInfo) { if (entityInfo.EntityValue is BaseEntity root) diff --git a/Blog.Core.Common/DB/BaseDBConfig.cs b/Blog.Core.Common/DB/BaseDBConfig.cs index 1d86369a..d8c3ee50 100644 --- a/Blog.Core.Common/DB/BaseDBConfig.cs +++ b/Blog.Core.Common/DB/BaseDBConfig.cs @@ -12,6 +12,7 @@ public class BaseDBConfig * 目前是多库操作,默认加载的是appsettings.json设置为true的第一个db连接。 */ public static (List allDbs, List slaveDbs) MutiConnectionString => MutiInitConn(); + public static ConnectionConfig LogConfig; //日志库 private static string DifDBConnOfSecurity(params string[] conn) { @@ -107,8 +108,6 @@ private static MutiDBOperate SpecialDbString(MutiDBOperate mutiDBOperate) return mutiDBOperate; } - - } diff --git a/Blog.Core.Common/Helper/RecursionHelper.cs b/Blog.Core.Common/Helper/RecursionHelper.cs index 9b27a37d..b4cbd682 100644 --- a/Blog.Core.Common/Helper/RecursionHelper.cs +++ b/Blog.Core.Common/Helper/RecursionHelper.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; namespace Blog.Core.Common.Helper @@ -8,9 +9,8 @@ namespace Blog.Core.Common.Helper /// public static class RecursionHelper { - public static void LoopToAppendChildren(List all, PermissionTree curItem, int pid, bool needbtn) + public static void LoopToAppendChildren(List all, PermissionTree curItem, long pid, bool needbtn) { - var subItems = all.Where(ee => ee.Pid == curItem.value).ToList(); var btnItems = subItems.Where(ss => ss.isbtn == true).ToList(); @@ -28,6 +28,7 @@ public static void LoopToAppendChildren(List all, PermissionTree { subItems = subItems.Where(ss => ss.isbtn == false).ToList(); } + if (subItems.Count > 0) { curItem.children = new List(); @@ -49,14 +50,15 @@ public static void LoopToAppendChildren(List all, PermissionTree { //subItem.disabled = true;//禁用当前节点 } + LoopToAppendChildren(all, subItem, pid, needbtn); } } + public static void LoopToAppendChildren(List all, DepartmentTree curItem, int pid) { - var subItems = all.Where(ee => ee.Pid == curItem.value).ToList(); - + if (subItems.Count > 0) { curItem.children = new List(); @@ -73,15 +75,14 @@ public static void LoopToAppendChildren(List all, DepartmentTree { //subItem.disabled = true;//禁用当前节点 } + LoopToAppendChildren(all, subItem, pid); } } - public static void LoopNaviBarAppendChildren(List all, NavigationBar curItem) { - var subItems = all.Where(ee => ee.pid == curItem.id).ToList(); if (subItems.Count > 0) @@ -102,7 +103,6 @@ public static void LoopNaviBarAppendChildren(List all, Navigation } - public static void LoopToAppendChildrenT(List all, T curItem, string parentIdName = "Pid", string idName = "value", string childrenName = "children") { var subItems = all.Where(ee => ee.GetType().GetProperty(parentIdName).GetValue(ee, null).ToString() == curItem.GetType().GetProperty(idName).GetValue(curItem, null).ToString()).ToList(); @@ -113,12 +113,47 @@ public static void LoopToAppendChildrenT(List all, T curItem, string paren LoopToAppendChildrenT(all, subItem); } } + + /// + /// 将父子级数据结构转换为普通list + /// + /// + /// + public static List TreeToList(List list, Action> action = null) + { + List results = new List(); + foreach (var item in list) + { + results.Add(item); + OperationChildData(results, item, action); + } + + return results; + } + + /// + /// 递归子级数据 + /// + /// 树形列表数据 + /// Item + public static void OperationChildData(List allList, T item, Action> action) + { + dynamic dynItem = item; + if (dynItem.Children == null) return; + if (dynItem.Children.Count <= 0) return; + allList.AddRange(dynItem.Children); + foreach (var subItem in dynItem.Children) + { + action?.Invoke(item, subItem, allList); + OperationChildData(allList, subItem, action); + } + } } public class PermissionTree { - public int value { get; set; } - public int Pid { get; set; } + public long value { get; set; } + public long Pid { get; set; } public string label { get; set; } public int order { get; set; } public bool isbtn { get; set; } @@ -139,8 +174,8 @@ public class DepartmentTree public class NavigationBar { - public int id { get; set; } - public int pid { get; set; } + public long id { get; set; } + public long pid { get; set; } public int order { get; set; } public string name { get; set; } public bool IsHide { get; set; } = false; @@ -158,15 +193,13 @@ public class NavigationBarMeta public bool requireAuth { get; set; } = true; public bool NoTabPage { get; set; } = false; public bool keepAlive { get; set; } = false; - - } public class NavigationBarPro { - public int id { get; set; } - public int parentId { get; set; } + public long id { get; set; } + public long parentId { get; set; } public int order { get; set; } public string name { get; set; } public bool IsHide { get; set; } = false; @@ -184,4 +217,4 @@ public class NavigationBarMetaPro public string icon { get; set; } public bool show { get; set; } = false; } -} +} \ No newline at end of file diff --git a/Blog.Core.Common/HttpPolly/HttpPollyHelper.cs b/Blog.Core.Common/Https/HttpPolly/HttpPollyHelper.cs similarity index 98% rename from Blog.Core.Common/HttpPolly/HttpPollyHelper.cs rename to Blog.Core.Common/Https/HttpPolly/HttpPollyHelper.cs index f1a1e84c..1187d711 100644 --- a/Blog.Core.Common/HttpPolly/HttpPollyHelper.cs +++ b/Blog.Core.Common/Https/HttpPolly/HttpPollyHelper.cs @@ -6,7 +6,7 @@ using System.Text; using System.Threading.Tasks; -namespace Blog.Core.Common.HttpPolly +namespace Blog.Core.Common.Https.HttpPolly { public class HttpPollyHelper : IHttpPollyHelper { @@ -35,7 +35,7 @@ public async Task PostAsync(HttpEnum httpEnum, string url, R request, D var stringContent = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json"); var response = await client.PostAsync(url, stringContent); - + if (response.StatusCode == System.Net.HttpStatusCode.OK) { string result = await response.Content.ReadAsStringAsync(); @@ -72,7 +72,7 @@ public async Task PostAsync(HttpEnum httpEnum, string url, string request, var stringContent = new StringContent(request, Encoding.UTF8, "application/json"); var response = await client.PostAsync(url, stringContent); - + if (response.StatusCode == System.Net.HttpStatusCode.OK) { string result = await response.Content.ReadAsStringAsync(); @@ -110,7 +110,7 @@ public async Task PostAsync(HttpEnum httpEnum, string url, R request, var stringContent = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json"); var response = await client.PostAsync(url, stringContent); - + if (response.StatusCode == System.Net.HttpStatusCode.OK) { return await response.Content.ReadAsStringAsync(); @@ -146,7 +146,7 @@ public async Task PostAsync(HttpEnum httpEnum, string url, string reques var stringContent = new StringContent(request, Encoding.UTF8, "application/json"); var response = await client.PostAsync(url, stringContent); - + if (response.StatusCode == System.Net.HttpStatusCode.OK) { return await response.Content.ReadAsStringAsync(); @@ -182,7 +182,7 @@ public async Task GetAsync(HttpEnum httpEnum, string url, Dictionary GetAsync(HttpEnum httpEnum, string url, Dictionary PutAsync(HttpEnum httpEnum, string url, R request, Di var stringContent = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json"); var response = await client.PutAsync(url, stringContent); - + if (response.StatusCode == System.Net.HttpStatusCode.OK) { string result = await response.Content.ReadAsStringAsync(); @@ -294,7 +294,7 @@ public async Task PutAsync(HttpEnum httpEnum, string url, string request, var stringContent = new StringContent(request, Encoding.UTF8, "application/json"); var response = await client.PutAsync(url, stringContent); - + if (response.StatusCode == System.Net.HttpStatusCode.OK) { string result = await response.Content.ReadAsStringAsync(); @@ -331,7 +331,7 @@ public async Task DeleteAsync(HttpEnum httpEnum, string url, Dictionary(context, "X-Forwarded-For")).FirstOrDefault(); + + if (string.IsNullOrWhiteSpace(ip)) + ip = SplitCsv(GetHeaderValueAs(context, "X-Real-IP")).FirstOrDefault(); + + if (string.IsNullOrWhiteSpace(ip) && context.Connection?.RemoteIpAddress != null) + ip = context.Connection.RemoteIpAddress.ToString(); + + if (string.IsNullOrWhiteSpace(ip)) + ip = GetHeaderValueAs(context, "REMOTE_ADDR"); + + return ip; + } + + public static bool IsLocal(this HttpContext context) + { + return GetRequestIp(context) is "127.0.0.1" or "::1" || context.Request?.IsLocal() == true; + } + + + public static bool IsLocal(this HttpRequest req) + { + var connection = req.HttpContext.Connection; + if (connection.RemoteIpAddress != null) + { + if (connection.LocalIpAddress != null) + { + return connection.RemoteIpAddress.Equals(connection.LocalIpAddress); + } + else + { + return IPAddress.IsLoopback(connection.RemoteIpAddress); + } + } + + // for in memory TestServer or when dealing with default connection info + if (connection.RemoteIpAddress == null && connection.LocalIpAddress == null) + { + return true; + } + + return false; + } + + + private static T GetHeaderValueAs(HttpContext context, string headerName) + { + if (context.Request?.Headers?.TryGetValue(headerName, out var values) ?? false) + { + string rawValues = values.ToString(); + + if (!string.IsNullOrWhiteSpace(rawValues)) + return (T) Convert.ChangeType(values.ToString(), typeof(T)); + } + + return default; + } + + private static List SplitCsv(string csvList) + { + if (string.IsNullOrWhiteSpace(csvList)) + return new List(); + + return csvList + .TrimEnd(',') + .Split(',') + .AsEnumerable() + .Select(s => s.Trim()) + .ToList(); + } +} \ No newline at end of file diff --git a/Blog.Core.Common/Hubs/ChatHub.cs b/Blog.Core.Common/Hubs/ChatHub.cs index ff15c97f..1c58c8a0 100644 --- a/Blog.Core.Common/Hubs/ChatHub.cs +++ b/Blog.Core.Common/Hubs/ChatHub.cs @@ -83,7 +83,8 @@ public async Task GetLatestCount(string random) //2、服务端主动向客户端发送数据,名字千万不能错 if (AppSettings.app(new string[] { "Middleware", "SignalRSendLog", "Enabled" }).ObjToBool()) { - await Clients.All.ReceiveUpdate(LogLock.GetLogData()); + //TODO 主动发送错误消息 + //await Clients.All.ReceiveUpdate(LogLock.GetLogData()); } diff --git a/Blog.Core.Common/LogHelper/LogContextExtension.cs b/Blog.Core.Common/LogHelper/LogContextExtension.cs new file mode 100644 index 00000000..bce80cbb --- /dev/null +++ b/Blog.Core.Common/LogHelper/LogContextExtension.cs @@ -0,0 +1,42 @@ +using Serilog.Context; +using SqlSugar; +using System; +using System.Collections.Generic; + +namespace Blog.Core.Common.LogHelper; + +public class LogContextExtension : IDisposable +{ + private readonly Stack _disposableStack = new Stack(); + + public static LogContextExtension Create => new(); + + public void AddStock(IDisposable disposable) + { + _disposableStack.Push(disposable); + } + + public IDisposable SqlAopPushProperty(ISqlSugarClient db) + { + AddStock(LogContext.PushProperty(LogContextStatic.LogSource, LogContextStatic.AopSql)); + AddStock(LogContext.PushProperty(LogContextStatic.SqlOutToConsole, + AppSettings.app(new string[] { "AppSettings", "SqlAOP", "LogToConsole", "Enabled" }).ObjToBool())); + AddStock(LogContext.PushProperty(LogContextStatic.SqlOutToFile, + AppSettings.app(new string[] { "AppSettings", "SqlAOP", "LogToFile", "Enabled" }).ObjToBool())); + AddStock(LogContext.PushProperty(LogContextStatic.OutToDb, + AppSettings.app(new string[] { "AppSettings", "SqlAOP", "LogToDb", "Enabled" }).ObjToBool())); + + AddStock(LogContext.PushProperty(LogContextStatic.SugarActionType, db.SugarActionType)); + + return this; + } + + + public void Dispose() + { + while (_disposableStack.Count > 0) + { + _disposableStack.Pop().Dispose(); + } + } +} \ No newline at end of file diff --git a/Blog.Core.Common/LogHelper/LogContextStatic.cs b/Blog.Core.Common/LogHelper/LogContextStatic.cs new file mode 100644 index 00000000..52a8167d --- /dev/null +++ b/Blog.Core.Common/LogHelper/LogContextStatic.cs @@ -0,0 +1,42 @@ +using System.IO; + +namespace Blog.Core.Common.LogHelper; + +public class LogContextStatic +{ + static LogContextStatic() + { + if (!Directory.Exists(BaseLogs)) + { + Directory.CreateDirectory(BaseLogs); + } + } + + public static readonly string BaseLogs = "Logs"; + public static readonly string BasePathLogs = @"Logs"; + + public static readonly string LogSource = "LogSource"; + public static readonly string AopSql = "AopSql"; + public static readonly string SqlOutToConsole = "OutToConsole"; + public static readonly string SqlOutToFile = "SqlOutToFile"; + public static readonly string OutToDb = "OutToDb"; + public static readonly string SugarActionType = "SugarActionType"; + + public static readonly string FileMessageTemplate = "{NewLine}Date:{Timestamp:yyyy-MM-dd HH:mm:ss.fff}{NewLine}LogLevel:{Level}{NewLine}Message:{Message}{NewLine}{Exception}" + new string('-', 100); + + + public static string Combine(string path1) + { + return Path.Combine(BaseLogs, path1); + } + + public static string Combine(string path1, string path2) + { + return Path.Combine(BaseLogs, path1, path2); + } + + public static string Combine(string path1, string path2, string path3) + { + return Path.Combine(BaseLogs, path1, path2, path3); + } +} \ No newline at end of file diff --git a/Blog.Core.Common/LogHelper/LogLock.cs b/Blog.Core.Common/LogHelper/LogLock.cs index 70c5f7f2..2c9be9a9 100644 --- a/Blog.Core.Common/LogHelper/LogLock.cs +++ b/Blog.Core.Common/LogHelper/LogLock.cs @@ -1,5 +1,4 @@ using Blog.Core.Common.Helper; -using log4net; using Newtonsoft.Json; using System; using System.Collections.Generic; @@ -7,12 +6,12 @@ using System.Linq; using System.Text; using System.Threading; +using Serilog; namespace Blog.Core.Common.LogHelper { public class LogLock { - private static readonly ILog log = LogManager.GetLogger(typeof(LogLock)); static ReaderWriterLockSlim LogWriteLock = new ReaderWriterLockSlim(); static int WritedCount = 0; static int FailedCount = 0; @@ -53,12 +52,14 @@ public static void OutLogAOP(string prefix, string traceId, string[] dataParas, default: break; } + if (AppSettings.app(new string[] { AppSetingNodeName, AppSetingName, "Enabled" }).ObjToBool()) { if (AppSettings.app(new string[] { AppSetingNodeName, AppSetingName, "LogToDB", "Enabled" }).ObjToBool()) { OutSql2LogToDB(prefix, traceId, dataParas, IsHeader); } + if (AppSettings.app(new string[] { AppSetingNodeName, AppSetingName, "LogToFile", "Enabled" }).ObjToBool()) { OutSql2LogToFile(prefix, traceId, dataParas, IsHeader); @@ -90,6 +91,7 @@ public static void OutSql2LogToFile(string prefix, string traceId, string[] data { Directory.CreateDirectory(folderPath); } + //string logFilePath = Path.Combine(path, $@"{filename}.log"); var logFilePath = FileHelper.GetAvailableFileWithPrefixOrderSize(folderPath, prefix); switch (prefix) @@ -98,47 +100,48 @@ public static void OutSql2LogToFile(string prefix, string traceId, string[] data AOPLogInfo apiLogAopInfo = JsonConvert.DeserializeObject(dataParas[1]); //记录被拦截方法信息的日志信息 var dataIntercept = "" + - $"【操作时间】:{apiLogAopInfo.RequestTime}\r\n" + - $"【当前操作用户】:{ apiLogAopInfo.OpUserName} \r\n" + - $"【当前执行方法】:{ apiLogAopInfo.RequestMethodName} \r\n" + - $"【携带的参数有】: {apiLogAopInfo.RequestParamsName} \r\n" + - $"【携带的参数JSON】: {apiLogAopInfo.RequestParamsData} \r\n" + - $"【响应时间】:{apiLogAopInfo.ResponseIntervalTime}\r\n" + - $"【执行完成时间】:{apiLogAopInfo.ResponseTime}\r\n" + - $"【执行完成结果】:{apiLogAopInfo.ResponseJsonData}\r\n"; + $"【操作时间】:{apiLogAopInfo.RequestTime}\r\n" + + $"【当前操作用户】:{apiLogAopInfo.OpUserName} \r\n" + + $"【当前执行方法】:{apiLogAopInfo.RequestMethodName} \r\n" + + $"【携带的参数有】: {apiLogAopInfo.RequestParamsName} \r\n" + + $"【携带的参数JSON】: {apiLogAopInfo.RequestParamsData} \r\n" + + $"【响应时间】:{apiLogAopInfo.ResponseIntervalTime}\r\n" + + $"【执行完成时间】:{apiLogAopInfo.ResponseTime}\r\n" + + $"【执行完成结果】:{apiLogAopInfo.ResponseJsonData}\r\n"; dataParas = new string[] { dataIntercept }; break; case "AOPLogEx": AOPLogExInfo apiLogAopExInfo = JsonConvert.DeserializeObject(dataParas[1]); var dataInterceptEx = "" + - $"【操作时间】:{apiLogAopExInfo.ApiLogAopInfo.RequestTime}\r\n" + - $"【当前操作用户】:{ apiLogAopExInfo.ApiLogAopInfo.OpUserName} \r\n" + - $"【当前执行方法】:{ apiLogAopExInfo.ApiLogAopInfo.RequestMethodName} \r\n" + - $"【携带的参数有】: {apiLogAopExInfo.ApiLogAopInfo.RequestParamsName} \r\n" + - $"【携带的参数JSON】: {apiLogAopExInfo.ApiLogAopInfo.RequestParamsData} \r\n" + - $"【响应时间】:{apiLogAopExInfo.ApiLogAopInfo.ResponseIntervalTime}\r\n" + - $"【执行完成时间】:{apiLogAopExInfo.ApiLogAopInfo.ResponseTime}\r\n" + - $"【执行完成结果】:{apiLogAopExInfo.ApiLogAopInfo.ResponseJsonData}\r\n" + - $"【执行完成异常信息】:方法中出现异常:{apiLogAopExInfo.ExMessage}\r\n" + - $"【执行完成异常】:方法中出现异常:{apiLogAopExInfo.InnerException}\r\n"; + $"【操作时间】:{apiLogAopExInfo.ApiLogAopInfo.RequestTime}\r\n" + + $"【当前操作用户】:{apiLogAopExInfo.ApiLogAopInfo.OpUserName} \r\n" + + $"【当前执行方法】:{apiLogAopExInfo.ApiLogAopInfo.RequestMethodName} \r\n" + + $"【携带的参数有】: {apiLogAopExInfo.ApiLogAopInfo.RequestParamsName} \r\n" + + $"【携带的参数JSON】: {apiLogAopExInfo.ApiLogAopInfo.RequestParamsData} \r\n" + + $"【响应时间】:{apiLogAopExInfo.ApiLogAopInfo.ResponseIntervalTime}\r\n" + + $"【执行完成时间】:{apiLogAopExInfo.ApiLogAopInfo.ResponseTime}\r\n" + + $"【执行完成结果】:{apiLogAopExInfo.ApiLogAopInfo.ResponseJsonData}\r\n" + + $"【执行完成异常信息】:方法中出现异常:{apiLogAopExInfo.ExMessage}\r\n" + + $"【执行完成异常】:方法中出现异常:{apiLogAopExInfo.InnerException}\r\n"; dataParas = new string[] { dataInterceptEx }; break; } + var now = DateTime.Now; string logContent = String.Join("\r\n", dataParas); if (IsHeader) { logContent = ( - "--------------------------------\r\n" + - DateTime.Now + "|\r\n" + - String.Join("\r\n", dataParas) + "\r\n" - ); + "--------------------------------\r\n" + + DateTime.Now + "|\r\n" + + String.Join("\r\n", dataParas) + "\r\n" + ); } else { logContent = ( - dataParas[1] + ",\r\n" - ); + dataParas[1] + ",\r\n" + ); } //if (logContent.IsNotEmptyOrNull() && logContent.Length > 500) @@ -148,12 +151,12 @@ public static void OutSql2LogToFile(string prefix, string traceId, string[] data if (isWrt) { File.WriteAllText(logFilePath, logContent); - } else { File.AppendAllText(logFilePath, logContent); } + WritedCount++; } catch (Exception e) @@ -170,14 +173,15 @@ public static void OutSql2LogToFile(string prefix, string traceId, string[] data LogWriteLock.ExitWriteLock(); } } + public static void OutSql2LogToDB(string prefix, string traceId, string[] dataParas, bool IsHeader = true) { - log4net.LogicalThreadContext.Properties["LogType"] = prefix; - log4net.LogicalThreadContext.Properties["TraceId"] = traceId; - if (dataParas.Length >= 2) - { - log4net.LogicalThreadContext.Properties["DataType"] = dataParas[0]; - } + //log4net.LogicalThreadContext.Properties["LogType"] = prefix; + //log4net.LogicalThreadContext.Properties["TraceId"] = traceId; + //if (dataParas.Length >= 2) + //{ + // log4net.LogicalThreadContext.Properties["DataType"] = dataParas[0]; + //} dataParas = dataParas.Skip(1).ToArray(); @@ -186,32 +190,37 @@ public static void OutSql2LogToDB(string prefix, string traceId, string[] dataPa { logContent = (String.Join("", dataParas)); } + switch (prefix) { //DEBUG | INFO | WARN | ERROR | FATAL case "AOPLog": - log.Info(logContent); + //TODO 是否需要输出? + //Log.Information(logContent); break; case "AOPLogEx": - log.Error(logContent); + Log.Error(logContent); break; case "RequestIpInfoLog": - log.Debug(logContent); + //TODO 是否需要Debug输出? + //Log.Debug(logContent); break; case "RecordAccessLogs": - log.Debug(logContent); + //TODO 是否需要Debug输出? + //Log.Debug(logContent); break; case "SqlLog": - log.Info(logContent); + Log.Information(logContent); break; case "RequestResponseLog": - log.Debug(logContent); + //TODO 是否需要Debug输出? + //Log.Debug(logContent); break; default: break; } - } + /// /// 读取文件内容 /// @@ -287,6 +296,7 @@ public static string ReadLog(string folderPath, string fileName, Encoding encode { LogWriteLock.ExitReadLock(); } + return s; } @@ -315,7 +325,6 @@ private static List GetRequestInfo(ReadType readType) } } } - } return requestInfos; @@ -336,16 +345,18 @@ public static List GetLogData() if (!string.IsNullOrEmpty(aoplogContent)) { aopLogs = aoplogContent.Split("--------------------------------") - .Where(d => !string.IsNullOrEmpty(d) && d != "\n" && d != "\r\n") - .Select(d => new LogInfo - { - Datetime = d.Split("|")[0].ObjToDate(), - Content = d.Split("|")[1]?.Replace("\r\n", "
"), - LogColor = "AOP", - }).ToList(); + .Where(d => !string.IsNullOrEmpty(d) && d != "\n" && d != "\r\n") + .Select(d => new LogInfo + { + Datetime = d.Split("|")[0].ObjToDate(), + Content = d.Split("|")[1]?.Replace("\r\n", "
"), + LogColor = "AOP", + }).ToList(); } } - catch (Exception) { } + catch (Exception) + { + } try { @@ -354,17 +365,19 @@ public static List GetLogData() if (!string.IsNullOrEmpty(exclogContent)) { excLogs = exclogContent.Split("--------------------------------") - .Where(d => !string.IsNullOrEmpty(d) && d != "\n" && d != "\r\n") - .Select(d => new LogInfo - { - Datetime = (d.Split("|")[0]).Split(',')[0].ObjToDate(), - Content = d.Split("|")[1]?.Replace("\r\n", "
"), - LogColor = "EXC", - Import = 9, - }).ToList(); + .Where(d => !string.IsNullOrEmpty(d) && d != "\n" && d != "\r\n") + .Select(d => new LogInfo + { + Datetime = (d.Split("|")[0]).Split(',')[0].ObjToDate(), + Content = d.Split("|")[1]?.Replace("\r\n", "
"), + LogColor = "EXC", + Import = 9, + }).ToList(); } } - catch (Exception) { } + catch (Exception) + { + } try @@ -374,16 +387,18 @@ public static List GetLogData() if (!string.IsNullOrEmpty(sqllogContent)) { sqlLogs = sqllogContent.Split("--------------------------------") - .Where(d => !string.IsNullOrEmpty(d) && d != "\n" && d != "\r\n") - .Select(d => new LogInfo - { - Datetime = d.Split("|")[0].ObjToDate(), - Content = d.Split("|")[1]?.Replace("\r\n", "
"), - LogColor = "SQL", - }).ToList(); + .Where(d => !string.IsNullOrEmpty(d) && d != "\n" && d != "\r\n") + .Select(d => new LogInfo + { + Datetime = d.Split("|")[0].ObjToDate(), + Content = d.Split("|")[1]?.Replace("\r\n", "
"), + LogColor = "SQL", + }).ToList(); } } - catch (Exception) { } + catch (Exception) + { + } //try //{ @@ -422,14 +437,17 @@ public static List GetLogData() { aopLogs.AddRange(excLogs); } + if (sqlLogs != null) { aopLogs.AddRange(sqlLogs); } + if (reqresLogs != null) { aopLogs.AddRange(reqresLogs); } + aopLogs = aopLogs.OrderByDescending(d => d.Import).ThenByDescending(d => d.Datetime).Take(100).ToList(); return aopLogs; @@ -450,7 +468,8 @@ public static RequestApiWeekView RequestApiinfoByWeek() Logs = GetRequestInfo(ReadType.Prefix); apiWeeks = (from n in Logs - group n by new { n.Week, n.Url } into g + group n by new { n.Week, n.Url } + into g select new ApiWeek { week = g.Key.Week, @@ -459,7 +478,6 @@ public static RequestApiWeekView RequestApiinfoByWeek() }).ToList(); //apiWeeks = apiWeeks.OrderByDescending(d => d.count).Take(8).ToList(); - } catch (Exception) { @@ -489,10 +507,12 @@ public static RequestApiWeekView RequestApiinfoByWeek() jsonBuilder.Append(item.count); jsonBuilder.Append("\","); } + if (apiweeksCurrentWeek.Count > 0) { jsonBuilder.Remove(jsonBuilder.Length - 1, 1); } + jsonBuilder.Append("},"); } @@ -500,6 +520,7 @@ public static RequestApiWeekView RequestApiinfoByWeek() { jsonBuilder.Remove(jsonBuilder.Length - 1, 1); } + jsonBuilder.Append("]"); //columns.AddRange(apiWeeks.OrderByDescending(d => d.count).Take(8).Select(d => d.url).ToList()); @@ -521,7 +542,8 @@ public static AccessApiDateView AccessApiByDate() Logs = GetRequestInfo(ReadType.Prefix); apiDates = (from n in Logs - group n by new { n.Date } into g + group n by new { n.Date } + into g select new ApiDate { date = g.Key.Date, @@ -529,7 +551,6 @@ public static AccessApiDateView AccessApiByDate() }).ToList(); apiDates = apiDates.OrderByDescending(d => d.date).Take(7).ToList(); - } catch (Exception) { @@ -552,7 +573,8 @@ public static AccessApiDateView AccessApiByHour() apiDates = (from n in Logs where n.Datetime.ObjToDate() >= DateTime.Today - group n by new { hour = n.Datetime.ObjToDate().Hour } into g + group n by new { hour = n.Datetime.ObjToDate().Hour } + into g select new ApiDate { date = g.Key.hour.ToString("00"), @@ -560,7 +582,6 @@ where n.Datetime.ObjToDate() >= DateTime.Today }).ToList(); apiDates = apiDates.OrderBy(d => d.date).Take(24).ToList(); - } catch (Exception) { @@ -580,14 +601,15 @@ public enum ReadType /// 精确查找一个 ///
Accurate, + /// /// 指定前缀,模糊查找全部 /// Prefix, + /// /// 指定前缀,最新一个文件 /// PrefixLatest } - -} +} \ No newline at end of file diff --git a/Blog.Core.Extensions/Blog.Core.Extensions.csproj b/Blog.Core.Extensions/Blog.Core.Extensions.csproj index 451d24f1..9eae3d92 100644 --- a/Blog.Core.Extensions/Blog.Core.Extensions.csproj +++ b/Blog.Core.Extensions/Blog.Core.Extensions.csproj @@ -18,10 +18,10 @@ - + @@ -35,6 +35,7 @@ + diff --git a/Blog.Core.Extensions/Middlewares/ExceptionHandlerMiddleware.cs b/Blog.Core.Extensions/Middlewares/ExceptionHandlerMiddleware.cs index 85d96e9b..aed57769 100644 --- a/Blog.Core.Extensions/Middlewares/ExceptionHandlerMiddleware.cs +++ b/Blog.Core.Extensions/Middlewares/ExceptionHandlerMiddleware.cs @@ -4,14 +4,13 @@ using Blog.Core.Model; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; +using Serilog; namespace Blog.Core.Extensions.Middlewares { public class ExceptionHandlerMiddleware { private readonly RequestDelegate _next; - private static readonly log4net.ILog Log = - log4net.LogManager.GetLogger(typeof(ExceptionHandlerMiddleware)); public ExceptionHandlerMiddleware(RequestDelegate next) { @@ -48,7 +47,9 @@ private static async Task WriteExceptionAsync(HttpContext context, Exception e) context.Response.ContentType = "application/json"; - await context.Response.WriteAsync(JsonConvert.SerializeObject((new ApiResponse(StatusCode.CODE500, e.Message)).MessageModel)).ConfigureAwait(false); + await context.Response + .WriteAsync(JsonConvert.SerializeObject(new ApiResponse(StatusCode.CODE500, e.Message).MessageModel)) + .ConfigureAwait(false); } } -} +} \ No newline at end of file diff --git a/Blog.Core.Extensions/Middlewares/IpLimitMiddleware.cs b/Blog.Core.Extensions/Middlewares/IpLimitMiddleware.cs index 958d6ff3..7fe68fc4 100644 --- a/Blog.Core.Extensions/Middlewares/IpLimitMiddleware.cs +++ b/Blog.Core.Extensions/Middlewares/IpLimitMiddleware.cs @@ -1,8 +1,8 @@ -using System; -using AspNetCoreRateLimit; +using AspNetCoreRateLimit; using Blog.Core.Common; -using log4net; using Microsoft.AspNetCore.Builder; +using System; +using Serilog; namespace Blog.Core.Extensions.Middlewares { @@ -11,7 +11,6 @@ namespace Blog.Core.Extensions.Middlewares ///
public static class IpLimitMiddleware { - private static readonly ILog Log = LogManager.GetLogger(typeof(IpLimitMiddleware)); public static void UseIpLimitMiddle(this IApplicationBuilder app) { if (app == null) throw new ArgumentNullException(nameof(app)); diff --git a/Blog.Core.Extensions/Middlewares/IpLogMiddleware.cs b/Blog.Core.Extensions/Middlewares/IpLogMiddleware.cs index b6b91bd5..ccd0d7af 100644 --- a/Blog.Core.Extensions/Middlewares/IpLogMiddleware.cs +++ b/Blog.Core.Extensions/Middlewares/IpLogMiddleware.cs @@ -1,10 +1,10 @@ -using System; -using System.Threading.Tasks; -using Blog.Core.Common; +using Blog.Core.Common; using Blog.Core.Common.LogHelper; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; +using System; +using System.Threading.Tasks; namespace Blog.Core.Extensions.Middlewares { @@ -19,7 +19,6 @@ public class IpLogMiddleware ///
private readonly RequestDelegate _next; private readonly IWebHostEnvironment _environment; - private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(IpLogMiddleware)); /// /// diff --git a/Blog.Core.Extensions/Middlewares/MiniProfilerMiddleware.cs b/Blog.Core.Extensions/Middlewares/MiniProfilerMiddleware.cs index 068c4a0d..49e3b70d 100644 --- a/Blog.Core.Extensions/Middlewares/MiniProfilerMiddleware.cs +++ b/Blog.Core.Extensions/Middlewares/MiniProfilerMiddleware.cs @@ -1,7 +1,7 @@ -using System; -using Blog.Core.Common; -using log4net; +using Blog.Core.Common; using Microsoft.AspNetCore.Builder; +using System; +using Serilog; namespace Blog.Core.Extensions.Middlewares { @@ -10,7 +10,6 @@ namespace Blog.Core.Extensions.Middlewares /// public static class MiniProfilerMiddleware { - private static readonly ILog Log = LogManager.GetLogger(typeof(MiniProfilerMiddleware)); public static void UseMiniProfilerMiddleware(this IApplicationBuilder app) { if (app == null) throw new ArgumentNullException(nameof(app)); diff --git a/Blog.Core.Extensions/Middlewares/SignalRSendMiddleware.cs b/Blog.Core.Extensions/Middlewares/SignalRSendMiddleware.cs index 1909c08f..f15df7b4 100644 --- a/Blog.Core.Extensions/Middlewares/SignalRSendMiddleware.cs +++ b/Blog.Core.Extensions/Middlewares/SignalRSendMiddleware.cs @@ -36,6 +36,7 @@ public async Task InvokeAsync(HttpContext context) { if (AppSettings.app("Middleware", "SignalR", "Enabled").ObjToBool()) { + //TODO 主动发送错误消息 await _hubContext.Clients.All.SendAsync("ReceiveUpdate", LogLock.GetLogData()); } await _next(context); diff --git a/Blog.Core.Extensions/Middlewares/SwaggerMiddleware.cs b/Blog.Core.Extensions/Middlewares/SwaggerMiddleware.cs index 099b2576..73af77d2 100644 --- a/Blog.Core.Extensions/Middlewares/SwaggerMiddleware.cs +++ b/Blog.Core.Extensions/Middlewares/SwaggerMiddleware.cs @@ -1,10 +1,10 @@ -using System; -using System.IO; -using System.Linq; -using Blog.Core.Common; -using log4net; +using Blog.Core.Common; using Microsoft.AspNetCore.Builder; using Swashbuckle.AspNetCore.SwaggerUI; +using System; +using System.IO; +using System.Linq; +using Serilog; using static Blog.Core.Extensions.CustomApiVersion; namespace Blog.Core.Extensions.Middlewares @@ -14,7 +14,6 @@ namespace Blog.Core.Extensions.Middlewares ///
public static class SwaggerMiddleware { - private static readonly ILog Log = LogManager.GetLogger(typeof(SwaggerMiddleware)); public static void UseSwaggerMiddle(this IApplicationBuilder app, Func streamHtml) { if (app == null) throw new ArgumentNullException(nameof(app)); @@ -24,10 +23,7 @@ public static void UseSwaggerMiddle(this IApplicationBuilder app, Func s { //根据版本名称倒序 遍历展示 var apiName = AppSettings.app(new string[] { "Startup", "ApiName" }); - typeof(ApiVersions).GetEnumNames().OrderByDescending(e => e).ToList().ForEach(version => - { - c.SwaggerEndpoint($"/swagger/{version}/swagger.json", $"{apiName} {version}"); - }); + typeof(ApiVersions).GetEnumNames().OrderByDescending(e => e).ToList().ForEach(version => { c.SwaggerEndpoint($"/swagger/{version}/swagger.json", $"{apiName} {version}"); }); c.SwaggerEndpoint($"https://petstore.swagger.io/v2/swagger.json", $"{apiName} pet"); @@ -38,12 +34,13 @@ public static void UseSwaggerMiddle(this IApplicationBuilder app, Func s Log.Error(msg); throw new Exception(msg); } + c.IndexStream = streamHtml; c.DocExpansion(DocExpansion.None); //->修改界面打开时自动折叠 if (Permissions.IsUseIds4) { - c.OAuthClientId("blogadminjs"); + c.OAuthClientId("blogadminjs"); } @@ -52,4 +49,4 @@ public static void UseSwaggerMiddle(this IApplicationBuilder app, Func s }); } } -} +} \ No newline at end of file diff --git a/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs b/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs index 0622e766..0336567f 100644 --- a/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs @@ -76,10 +76,10 @@ public static void AddAppConfigSetup(this IServiceCollection services, IHostEnvi var ipLogOpen = AppSettings.app(new string[] { "Middleware", "IPLog", "Enabled" }).ObjToBool(); var recordAccessLogsOpen = AppSettings.app(new string[] { "Middleware", "RecordAccessLogs", "Enabled" }).ObjToBool(); ConsoleHelper.WriteSuccessLine($"OPEN Log: " + - (requestResponseLogOpen ? "RequestResponseLog √," : "") + - (ipLogOpen ? "IPLog √," : "") + - (recordAccessLogsOpen ? "RecordAccessLogs √," : "") - ); + (requestResponseLogOpen ? "RequestResponseLog √," : "") + + (ipLogOpen ? "IPLog √," : "") + + (recordAccessLogsOpen ? "RecordAccessLogs √," : "") + ); // 事务AOP if (!AppSettings.app(new string[] { "AppSettings", "TranAOP", "Enabled" }).ObjToBool()) @@ -213,7 +213,6 @@ public static void AddAppConfigSetup(this IServiceCollection services, IHostEnvi Console.WriteLine(); } - } public static void AddAppTableConfigSetup(this IServiceCollection services, IHostEnvironment env) @@ -222,7 +221,6 @@ public static void AddAppTableConfigSetup(this IServiceCollection services, IHos if (AppSettings.app(new string[] { "Startup", "AppConfigAlert", "Enabled" }).ObjToBool()) { - if (env.IsDevelopment()) { Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); @@ -230,6 +228,7 @@ public static void AddAppTableConfigSetup(this IServiceCollection services, IHos } #region 程序配置 + List configInfos = new() { new string[] { "当前环境", Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") }, @@ -238,7 +237,7 @@ public static void AddAppTableConfigSetup(this IServiceCollection services, IHos new string[] { "RabbitMQ消息列队", AppSettings.app("RabbitMQ", "Enabled") }, new string[] { "事件总线(必须开启消息列队)", AppSettings.app("EventBus", "Enabled") }, new string[] { "redis消息队列", AppSettings.app("Startup", "RedisMq", "Enabled") }, - new string[] { "是否多库", AppSettings.app("MutiDBEnabled" ) }, + new string[] { "是否多库", AppSettings.app("MutiDBEnabled") }, new string[] { "读写分离", AppSettings.app("CQRSEnabled") }, }; @@ -253,17 +252,19 @@ public static void AddAppTableConfigSetup(this IServiceCollection services, IHos TableStyle = TableStyle.Alternative }.Writer(ConsoleColor.Blue); Console.WriteLine(); + #endregion 程序配置 #region AOP + List aopInfos = new() -{ + { new string[] { "Redis缓存AOP", AppSettings.app("AppSettings", "RedisCachingAOP", "Enabled") }, new string[] { "内存缓存AOP", AppSettings.app("AppSettings", "MemoryCachingAOP", "Enabled") }, - new string[] { "服务日志AOP", AppSettings.app("AppSettings", "LogAOP", "Enabled" ) }, - new string[] { "事务AOP", AppSettings.app("AppSettings", "TranAOP", "Enabled" ) }, - new string[] { "Sql执行AOP", AppSettings.app("AppSettings", "SqlAOP", "OutToLogFile", "Enabled" ) }, - new string[] { "Sql执行AOP控制台输出", AppSettings.app("AppSettings", "SqlAOP", "OutToConsole", "Enabled" ) }, + new string[] { "服务日志AOP", AppSettings.app("AppSettings", "LogAOP", "Enabled") }, + new string[] { "事务AOP", AppSettings.app("AppSettings", "TranAOP", "Enabled") }, + new string[] { "Sql执行AOP", AppSettings.app("AppSettings", "SqlAOP", "Enabled") }, + new string[] { "Sql执行AOP控制台输出", AppSettings.app("AppSettings", "SqlAOP", "LogToConsole", "Enabled") }, }; new ConsoleTable @@ -277,15 +278,17 @@ public static void AddAppTableConfigSetup(this IServiceCollection services, IHos TableStyle = TableStyle.Alternative }.Writer(ConsoleColor.Blue); Console.WriteLine(); + #endregion AOP #region 中间件 + List MiddlewareInfos = new() { new string[] { "请求纪录中间件", AppSettings.app("Middleware", "RecordAccessLogs", "Enabled") }, - new string[] { "IP记录中间件", AppSettings.app("Middleware", "IPLog", "Enabled" ) }, - new string[] { "请求响应日志中间件", AppSettings.app("Middleware", "RequestResponseLog", "Enabled" ) }, - new string[] { "SingnalR实时发送请求数据中间件", AppSettings.app("Middleware", "SignalR", "Enabled" ) }, + new string[] { "IP记录中间件", AppSettings.app("Middleware", "IPLog", "Enabled") }, + new string[] { "请求响应日志中间件", AppSettings.app("Middleware", "RequestResponseLog", "Enabled") }, + new string[] { "SingnalR实时发送请求数据中间件", AppSettings.app("Middleware", "SignalR", "Enabled") }, new string[] { "IP限流中间件", AppSettings.app("Middleware", "IpRateLimit", "Enabled") }, new string[] { "性能分析中间件", AppSettings.app("Startup", "MiniProfiler", "Enabled") }, new string[] { "Consul注册服务", AppSettings.app("Middleware", "Consul", "Enabled") }, @@ -302,10 +305,9 @@ public static void AddAppTableConfigSetup(this IServiceCollection services, IHos TableStyle = TableStyle.Alternative }.Writer(ConsoleColor.Blue); Console.WriteLine(); - #endregion 中间件 + #endregion 中间件 } - } } -} +} \ No newline at end of file diff --git a/Blog.Core.Extensions/ServiceExtensions/AutofacModuleRegister.cs b/Blog.Core.Extensions/ServiceExtensions/AutofacModuleRegister.cs index 7f2997bd..4351962b 100644 --- a/Blog.Core.Extensions/ServiceExtensions/AutofacModuleRegister.cs +++ b/Blog.Core.Extensions/ServiceExtensions/AutofacModuleRegister.cs @@ -6,20 +6,18 @@ using Blog.Core.IServices.BASE; using Blog.Core.Model; using Blog.Core.Repository.Base; +using Blog.Core.Repository.UnitOfWorks; using Blog.Core.Services.BASE; -using log4net; using System; using System.Collections.Generic; using System.IO; using System.Reflection; -using Blog.Core.Repository.UnitOfWorks; +using Serilog; namespace Blog.Core.Extensions { public class AutofacModuleRegister : Autofac.Module { - private static readonly ILog log = LogManager.GetLogger(typeof(AutofacModuleRegister)); - protected override void Load(ContainerBuilder builder) { var basePath = AppContext.BaseDirectory; @@ -34,39 +32,39 @@ protected override void Load(ContainerBuilder builder) if (!(File.Exists(servicesDllFile) && File.Exists(repositoryDllFile))) { var msg = "Repository.dll和service.dll 丢失,因为项目解耦了,所以需要先F6编译,再F5运行,请检查 bin 文件夹,并拷贝。"; - log.Error(msg); + Log.Error(msg); throw new Exception(msg); } // AOP 开关,如果想要打开指定的功能,只需要在 appsettigns.json 对应对应 true 就行。 var cacheType = new List(); - if (AppSettings.app(new string[] {"AppSettings", "RedisCachingAOP", "Enabled"}).ObjToBool()) + if (AppSettings.app(new string[] { "AppSettings", "RedisCachingAOP", "Enabled" }).ObjToBool()) { builder.RegisterType(); cacheType.Add(typeof(BlogRedisCacheAOP)); } - if (AppSettings.app(new string[] {"AppSettings", "MemoryCachingAOP", "Enabled"}).ObjToBool()) + if (AppSettings.app(new string[] { "AppSettings", "MemoryCachingAOP", "Enabled" }).ObjToBool()) { builder.RegisterType(); cacheType.Add(typeof(BlogCacheAOP)); } - if (AppSettings.app(new string[] {"AppSettings", "TranAOP", "Enabled"}).ObjToBool()) + if (AppSettings.app(new string[] { "AppSettings", "TranAOP", "Enabled" }).ObjToBool()) { builder.RegisterType(); cacheType.Add(typeof(BlogTranAOP)); } - if (AppSettings.app(new string[] {"AppSettings", "LogAOP", "Enabled"}).ObjToBool()) + if (AppSettings.app(new string[] { "AppSettings", "LogAOP", "Enabled" }).ObjToBool()) { builder.RegisterType(); cacheType.Add(typeof(BlogLogAOP)); } - builder.RegisterGeneric(typeof(BaseRepository<>)).As(typeof(IBaseRepository<>)).InstancePerDependency();//注册仓储 - builder.RegisterGeneric(typeof(BaseServices<>)).As(typeof(IBaseServices<>)).InstancePerDependency();//注册服务 + builder.RegisterGeneric(typeof(BaseRepository<>)).As(typeof(IBaseRepository<>)).InstancePerDependency(); //注册仓储 + builder.RegisterGeneric(typeof(BaseServices<>)).As(typeof(IBaseServices<>)).InstancePerDependency(); //注册服务 // 获取 Service.dll 程序集服务,并注册 var assemblysServices = Assembly.LoadFrom(servicesDllFile); diff --git a/Blog.Core.Extensions/ServiceExtensions/HttpPollySetup.cs b/Blog.Core.Extensions/ServiceExtensions/HttpPollySetup.cs index e8e3929f..b3147ca8 100644 --- a/Blog.Core.Extensions/ServiceExtensions/HttpPollySetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/HttpPollySetup.cs @@ -1,4 +1,4 @@ -using Blog.Core.Common.HttpPolly; +using Blog.Core.Common.Https.HttpPolly; using Blog.Core.Model; using Microsoft.Extensions.DependencyInjection; using Polly; diff --git a/Blog.Core.Extensions/ServiceExtensions/SerilogSetup.cs b/Blog.Core.Extensions/ServiceExtensions/SerilogSetup.cs new file mode 100644 index 00000000..a112409c --- /dev/null +++ b/Blog.Core.Extensions/ServiceExtensions/SerilogSetup.cs @@ -0,0 +1,37 @@ +using Blog.Core.Common; +using Blog.Core.Common.LogHelper; +using Blog.Core.Serilog.Extensions; +using Microsoft.Extensions.Hosting; +using Serilog; +using Serilog.Debugging; +using System; +using System.IO; + +namespace Blog.Core.Extensions.ServiceExtensions; + +public static class SerilogSetup +{ + public static IHostBuilder AddSerilogSetup(this IHostBuilder host) + { + if (host == null) throw new ArgumentNullException(nameof(host)); + + var loggerConfiguration = new LoggerConfiguration() + .ReadFrom.Configuration(AppSettings.Configuration) + .Enrich.FromLogContext() + //输出到控制台 + .WriteToConsole() + //将日志保存到文件中 + .WriteToFile(); + //配置日志库 + //.WriteToLogBatching(); + + Log.Logger = loggerConfiguration.CreateLogger(); + + //Serilog 内部日志 + var file = File.CreateText(LogContextStatic.Combine($"SerilogDebug{DateTime.Now:yyyyMMdd}.txt")); + SelfLog.Enable(TextWriter.Synchronized(file)); + + host.UseSerilog(); + return host; + } +} \ No newline at end of file diff --git a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs index 3ecf224a..3440b8af 100644 --- a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs @@ -1,6 +1,7 @@ using Blog.Core.Common; +using Blog.Core.Common.Const; using Blog.Core.Common.DB; -using Blog.Core.Common.Helper; +using Blog.Core.Common.DB.Aop; using Blog.Core.Common.LogHelper; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.DependencyInjection; @@ -9,7 +10,6 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Blog.Core.Common.DB.Aop; namespace Blog.Core.Extensions { @@ -48,7 +48,7 @@ public static void AddSqlsugarSetup(this IServiceCollection services) BaseDBConfig.MutiConnectionString.allDbs.ForEach(m => { - listConfig.Add(new ConnectionConfig() + var config = new ConnectionConfig() { ConfigId = m.ConnId.ObjToString().ToLower(), ConnectionString = m.Connection, @@ -56,29 +56,6 @@ public static void AddSqlsugarSetup(this IServiceCollection services) IsAutoCloseConnection = true, // Check out more information: https://github.com/anjoy8/Blog.Core/issues/122 //IsShardSameThread = false, - AopEvents = new AopEvents - { - OnLogExecuting = (sql, p) => - { - if (AppSettings.app(new string[] { "AppSettings", "SqlAOP", "Enabled" }).ObjToBool()) - { - if (AppSettings.app(new string[] { "AppSettings", "SqlAOP", "LogToFile", "Enabled" }).ObjToBool()) - { - Parallel.For(0, 1, e => - { - MiniProfiler.Current.CustomTiming("SQL:", GetParas(p) + "【SQL语句】:" + sql); - //LogLock.OutSql2Log("SqlLog", new string[] { GetParas(p), "【SQL语句】:" + sql }); - LogLock.OutLogAOP("SqlLog", "", new string[] { sql.GetType().ToString(), GetParas(p), "【SQL语句】:" + sql }); - - }); - } - if (AppSettings.app(new string[] { "AppSettings", "SqlAOP", "LogToConsole", "Enabled" }).ObjToBool()) - { - ConsoleHelper.WriteColorLine(string.Join("\r\n", new string[] { "--------", $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} :" + GetWholeSql(p, sql) }), ConsoleColor.DarkCyan); - } - } - }, - }, MoreSettings = new ConnMoreSettings() { //IsWithNoLockQuery = true, @@ -99,15 +76,29 @@ public static void AddSqlsugarSetup(this IServiceCollection services) } }, InitKeyType = InitKeyType.Attribute + }; + if (SqlSugarConst.LogConfigId.Equals(m.ConnId)) + { + BaseDBConfig.LogConfig = config; } - ); + + listConfig.Add(config); }); + + if (BaseDBConfig.LogConfig is null) + { + throw new ApplicationException("未配置Log库连接"); + } + return new SqlSugarScope(listConfig, db => { listConfig.ForEach(config => { var dbProvider = db.GetConnectionScope((string)config.ConfigId); + // 打印SQL语句 + dbProvider.Aop.OnLogExecuting = (s, parameters) => SqlSugarAop.OnLogExecuting(dbProvider,s, parameters, config); + // 数据审计 dbProvider.Aop.DataExecuting = SqlSugarAop.DataExecuting; diff --git a/Blog.Core.Extensions/ServiceExtensions/SwaggerSetup.cs b/Blog.Core.Extensions/ServiceExtensions/SwaggerSetup.cs index 449b6a4d..85dff620 100644 --- a/Blog.Core.Extensions/ServiceExtensions/SwaggerSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/SwaggerSetup.cs @@ -1,7 +1,7 @@ using Blog.Core.Common; -using log4net; using Microsoft.Extensions.DependencyInjection; using Microsoft.OpenApi.Models; +using Serilog; using Swashbuckle.AspNetCore.Filters; using System; using System.Collections.Generic; @@ -17,10 +17,6 @@ namespace Blog.Core.Extensions /// public static class SwaggerSetup { - - private static readonly ILog log = - LogManager.GetLogger(typeof(SwaggerSetup)); - public static void AddSwaggerSetup(this IServiceCollection services) { if (services == null) throw new ArgumentNullException(nameof(services)); @@ -59,7 +55,7 @@ public static void AddSwaggerSetup(this IServiceCollection services) } catch (Exception ex) { - log.Error("Blog.Core.xml和Blog.Core.Model.xml 丢失,请检查并拷贝。\n" + ex.Message); + Log.Error("Blog.Core.xml和Blog.Core.Model.xml 丢失,请检查并拷贝。\n" + ex.Message); } // 开启加权小锁 @@ -82,12 +78,13 @@ public static void AddSwaggerSetup(this IServiceCollection services) Implicit = new OpenApiOAuthFlow { AuthorizationUrl = new Uri($"{AppSettings.app(new string[] { "Startup", "IdentityServer4", "AuthorizationUrl" })}/connect/authorize"), - Scopes = new Dictionary { + Scopes = new Dictionary { - "blog.core.api","ApiResource id" + { + "blog.core.api", "ApiResource id" + } } } - } } }); } @@ -97,14 +94,11 @@ public static void AddSwaggerSetup(this IServiceCollection services) c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme { Description = "JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}(注意两者之间是一个空格)\"", - Name = "Authorization",//jwt默认的参数名称 - In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中) + Name = "Authorization", //jwt默认的参数名称 + In = ParameterLocation.Header, //jwt默认存放Authorization信息的位置(请求头中) Type = SecuritySchemeType.ApiKey }); } - - - }); services.AddSwaggerGenNewtonsoftSupport(); } @@ -124,11 +118,11 @@ public enum ApiVersions /// V1 版本 /// V1 = 1, + /// /// V2 版本 /// V2 = 2, } } - -} +} \ No newline at end of file diff --git a/Blog.Core.Serilog.Es/Blog.Core.Serilog.Es.csproj b/Blog.Core.Serilog.Es/Blog.Core.Serilog.Es.csproj index 5cf82020..398aab48 100644 --- a/Blog.Core.Serilog.Es/Blog.Core.Serilog.Es.csproj +++ b/Blog.Core.Serilog.Es/Blog.Core.Serilog.Es.csproj @@ -11,7 +11,7 @@ - + diff --git a/Blog.Core.Serilog/Blog.Core.Serilog.csproj b/Blog.Core.Serilog/Blog.Core.Serilog.csproj new file mode 100644 index 00000000..07fa26f3 --- /dev/null +++ b/Blog.Core.Serilog/Blog.Core.Serilog.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + enable + enable + + + + + + + diff --git a/Blog.Core.Serilog/Extensions/LoggerConfigurationExtensions.cs b/Blog.Core.Serilog/Extensions/LoggerConfigurationExtensions.cs new file mode 100644 index 00000000..2736aa1f --- /dev/null +++ b/Blog.Core.Serilog/Extensions/LoggerConfigurationExtensions.cs @@ -0,0 +1,121 @@ +using Blog.Core.Common; +using Blog.Core.Common.LogHelper; +using Serilog; +using Serilog.Events; +using Serilog.Filters; +using SqlSugar; + +namespace Blog.Core.Serilog.Extensions; + +public static class LoggerConfigurationExtensions +{ + public static LoggerConfiguration WriteToSqlServer(this LoggerConfiguration loggerConfiguration) + { + var logConnectionStrings = AppSettings.app("LogConnectionStrings"); + if (logConnectionStrings.IsNullOrEmpty()) return loggerConfiguration; + + //输出SQL + //loggerConfiguration = loggerConfiguration.WriteTo.Logger(lg => + // lg.FilterSqlLog().WriteTo.MSSqlServer(logConnectionStrings, new MSSqlServerSinkOptions() + // { + // TableName = "SqlLog", + // AutoCreateSqlTable = true + // })); + + //输出普通日志 + //loggerConfiguration = loggerConfiguration.WriteTo.Logger(lg => + // lg.FilterRemoveSqlLog().Filter.ByIncludingOnly(p => p.Level >= LogEventLevel.Error) + // .WriteTo.MSSqlServer(logConnectionStrings, new MSSqlServerSinkOptions() + // { + // TableName = "ErrorLog", + // AutoCreateSqlTable = true + // })); + //loggerConfiguration = loggerConfiguration.WriteTo.Logger(lg => + // lg.FilterRemoveSqlLog().Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Warning) + // .WriteTo.MSSqlServer(logConnectionStrings, new MSSqlServerSinkOptions() + // { + // TableName = "WarningLog", + // AutoCreateSqlTable = true + // })); + //loggerConfiguration = loggerConfiguration.WriteTo.Logger(lg => + // lg.FilterRemoveSqlLog().Filter.ByIncludingOnly(p => p.Level <= LogEventLevel.Information) + // .WriteTo.MSSqlServer(logConnectionStrings, new MSSqlServerSinkOptions() + // { + // TableName = "InformationLog", + // AutoCreateSqlTable = true + // })); + + return loggerConfiguration; + } + + public static LoggerConfiguration WriteToConsole(this LoggerConfiguration loggerConfiguration) + { + //输出普通日志 + loggerConfiguration = loggerConfiguration.WriteTo.Logger(lg => + lg.FilterRemoveSqlLog().WriteTo.Console()); + + //输出SQL + loggerConfiguration = loggerConfiguration.WriteTo.Logger(lg => + lg.FilterSqlLog().Filter.ByIncludingOnly(Matching.WithProperty(LogContextStatic.SqlOutToConsole, s => s)) + .WriteTo.Console()); + + return loggerConfiguration; + } + + public static LoggerConfiguration WriteToFile(this LoggerConfiguration loggerConfiguration) + { + //输出SQL + loggerConfiguration = loggerConfiguration.WriteTo.Logger(lg => + lg.FilterSqlLog().Filter.ByIncludingOnly(Matching.WithProperty(LogContextStatic.SqlOutToFile, s => s)) + .WriteTo.Async(s => s.File(LogContextStatic.Combine(LogContextStatic.AopSql, @"AopSql.txt"), rollingInterval: RollingInterval.Day, + outputTemplate: LogContextStatic.FileMessageTemplate, retainedFileCountLimit: 31))); + //输出普通日志 + loggerConfiguration = loggerConfiguration.WriteTo.Logger(lg => + lg.FilterRemoveSqlLog().WriteTo.Async(s => s.File(LogContextStatic.Combine(LogContextStatic.BasePathLogs, @"Log.txt"), rollingInterval: RollingInterval.Day, + outputTemplate: LogContextStatic.FileMessageTemplate, retainedFileCountLimit: 31))); + return loggerConfiguration; + } + + public static LoggerConfiguration FilterSqlLog(this LoggerConfiguration lc) + { + lc = lc.Filter.ByIncludingOnly(Matching.WithProperty(LogContextStatic.LogSource, s => LogContextStatic.AopSql.Equals(s))); + return lc; + } + + public static IEnumerable FilterSqlLog(this IEnumerable batch) + { + return batch.Where(s => s.WithProperty(LogContextStatic.LogSource, q => LogContextStatic.AopSql.Equals(q))) + .Where(s => s.WithProperty(LogContextStatic.SugarActionType, + q => !new[] { SugarActionType.UnKnown, SugarActionType.Query }.Contains(q))); + } + + public static LoggerConfiguration FilterRemoveSqlLog(this LoggerConfiguration lc) + { + lc = lc.Filter.ByIncludingOnly(WithProperty(LogContextStatic.LogSource, s => !LogContextStatic.AopSql.Equals(s))); + return lc; + } + + public static IEnumerable FilterRemoveOtherLog(this IEnumerable batch) + { + return batch.Where(s => WithProperty(LogContextStatic.LogSource, + q => !LogContextStatic.AopSql.Equals(q))(s)); + } + + public static Func WithProperty(string propertyName, Func predicate) + { + //如果不包含属性 也认为是true + return e => + { + if (!e.Properties.TryGetValue(propertyName, out var propertyValue)) return true; + + return propertyValue is ScalarValue { Value: T value } && predicate(value); + }; + } + + public static bool WithProperty(this LogEvent e, string key, Func predicate) + { + if (!e.Properties.TryGetValue(key, out var propertyValue)) return false; + + return propertyValue is ScalarValue { Value: T value } && predicate(value); + } +} \ No newline at end of file diff --git a/Blog.Core.Serilog/Utility/SerilogRequestUtility.cs b/Blog.Core.Serilog/Utility/SerilogRequestUtility.cs new file mode 100644 index 00000000..cab7ae55 --- /dev/null +++ b/Blog.Core.Serilog/Utility/SerilogRequestUtility.cs @@ -0,0 +1,34 @@ +using Microsoft.AspNetCore.Http; +using Serilog.Events; + +namespace Blog.Core.Serilog.Utility; + +public class SerilogRequestUtility +{ + private static readonly List _ignoreUrl = new() + { + "/job", + }; + + private static LogEventLevel DefaultGetLevel( + HttpContext ctx, + double _, + Exception? ex) + { + return ex is null && ctx.Response.StatusCode <= 499 ? LogEventLevel.Information : LogEventLevel.Error; + } + + public static LogEventLevel GetRequestLevel(HttpContext ctx, double _, Exception? ex) => + ex is null && ctx.Response.StatusCode <= 499 ? IgnoreRequest(ctx) : LogEventLevel.Error; + + private static LogEventLevel IgnoreRequest(HttpContext ctx) + { + var path = ctx.Request.Path.Value; + if (path.IsNullOrEmpty()) + { + return LogEventLevel.Information; + } + + return _ignoreUrl.Any(s => path.StartsWith(s)) ? LogEventLevel.Verbose : LogEventLevel.Information; + } +} \ No newline at end of file diff --git a/Blog.Core.Tasks/QuartzNet/Jobs/Job_AccessTrendLog_Quartz.cs b/Blog.Core.Tasks/QuartzNet/Jobs/Job_AccessTrendLog_Quartz.cs index 1dcc57ed..1d501c34 100644 --- a/Blog.Core.Tasks/QuartzNet/Jobs/Job_AccessTrendLog_Quartz.cs +++ b/Blog.Core.Tasks/QuartzNet/Jobs/Job_AccessTrendLog_Quartz.cs @@ -34,7 +34,7 @@ public async Task Execute(IJobExecutionContext context) } public async Task Run(IJobExecutionContext context) { - + // 可以直接获取 JobDetail 的值 var jobKey = context.JobDetail.Key; var jobId = jobKey.Name; @@ -94,7 +94,7 @@ await _accessTrendLogServices.Add(new AccessTrendLog() Parallel.For(0, 1, e => { - LogLock.OutLogAOP("ACCESSTRENDLOG","",new string[] { activeUserVMs.GetType().ToString(), JsonConvert.SerializeObject(activeUserVMs) }, false); + LogLock.OutLogAOP("ACCESSTRENDLOG", "", new string[] { activeUserVMs.GetType().ToString(), JsonConvert.SerializeObject(activeUserVMs) }, false); }); } diff --git a/Blog.Core.Tasks/QuartzNet/Jobs/Job_OperateLog_Quartz.cs b/Blog.Core.Tasks/QuartzNet/Jobs/Job_OperateLog_Quartz.cs index 18c4c298..abcd81ea 100644 --- a/Blog.Core.Tasks/QuartzNet/Jobs/Job_OperateLog_Quartz.cs +++ b/Blog.Core.Tasks/QuartzNet/Jobs/Job_OperateLog_Quartz.cs @@ -17,19 +17,21 @@ namespace Blog.Core.Tasks { public class Job_OperateLog_Quartz : JobBase, IJob { - private readonly IOperateLogServices _operateLogServices; + private readonly IOperateLogServices _operateLogServices; private readonly IWebHostEnvironment _environment; - public Job_OperateLog_Quartz(IOperateLogServices operateLogServices,IWebHostEnvironment environment, ITasksQzServices tasksQzServices,ITasksLogServices tasksLogServices) - :base(tasksQzServices, tasksLogServices) + public Job_OperateLog_Quartz(IOperateLogServices operateLogServices, IWebHostEnvironment environment, ITasksQzServices tasksQzServices, ITasksLogServices tasksLogServices) + : base(tasksQzServices, tasksLogServices) { - _operateLogServices = operateLogServices; - _environment = environment; + _operateLogServices = operateLogServices; + _environment = environment; } + public async Task Execute(IJobExecutionContext context) { var executeLog = await ExecuteJob(context, async () => await Run(context)); } + public async Task Run(IJobExecutionContext context) { @@ -78,7 +80,4 @@ public async Task Run(IJobExecutionContext context) } } } - - - -} +} \ No newline at end of file diff --git a/Blog.Core.Tests/DependencyInjection/DI_Test.cs b/Blog.Core.Tests/DependencyInjection/DI_Test.cs index ff2c74cd..d425fa01 100644 --- a/Blog.Core.Tests/DependencyInjection/DI_Test.cs +++ b/Blog.Core.Tests/DependencyInjection/DI_Test.cs @@ -59,7 +59,6 @@ public IContainer DICollections() services.AddAutoMapper(typeof(Startup)); services.AddSingleton(new AppSettings(basePath)); - services.AddSingleton(new LogLock(basePath)); services.AddScoped(); services.AddScoped(); diff --git a/Blog.Core.sln b/Blog.Core.sln index c8f61505..bf4f65cb 100644 --- a/Blog.Core.sln +++ b/Blog.Core.sln @@ -57,6 +57,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blog.Core.Serilog.Es", "Blo EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Nacos", "Ocelot.Provider.Nacos\Ocelot.Provider.Nacos.csproj", "{6463FB13-5F01-4A1D-8B62-A454FB3812EB}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blog.Core.Serilog", "Blog.Core.Serilog\Blog.Core.Serilog.csproj", "{7F9057F0-ED8D-4694-B590-7D75C012DF00}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -119,6 +121,10 @@ Global {6463FB13-5F01-4A1D-8B62-A454FB3812EB}.Debug|Any CPU.Build.0 = Debug|Any CPU {6463FB13-5F01-4A1D-8B62-A454FB3812EB}.Release|Any CPU.ActiveCfg = Release|Any CPU {6463FB13-5F01-4A1D-8B62-A454FB3812EB}.Release|Any CPU.Build.0 = Release|Any CPU + {7F9057F0-ED8D-4694-B590-7D75C012DF00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F9057F0-ED8D-4694-B590-7D75C012DF00}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F9057F0-ED8D-4694-B590-7D75C012DF00}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F9057F0-ED8D-4694-B590-7D75C012DF00}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 070e441bc02ef0a1e410d176cf953e6549824721 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Sun, 2 Apr 2023 15:16:37 +0800 Subject: [PATCH 13/84] feat: :accept: change api param --- Blog.Core.Api/Blog.Core.xml | 50 ++++++++--------- Blog.Core.Api/Controllers/BlogController.cs | 10 ++-- .../Controllers/DepartmentController.cs | 4 +- Blog.Core.Api/Controllers/ModuleController.cs | 2 +- .../Controllers/PermissionController.cs | 55 +++++++------------ Blog.Core.Api/Controllers/RoleController.cs | 2 +- .../Controllers/TasksQzController.cs | 18 +++--- Blog.Core.Api/Controllers/TopicController.cs | 6 +- .../Controllers/TopicDetailController.cs | 8 +-- .../Controllers/TransactionController.cs | 6 +- Blog.Core.Api/Controllers/UserController.cs | 4 +- .../Controllers/UserRoleController.cs | 2 +- Blog.Core.Common/Helper/RecursionHelper.cs | 4 +- Blog.Core.IServices/IBlogArticleServices.cs | 2 +- Blog.Core.IServices/ITasksLogServices.cs | 4 +- Blog.Core.IServices/IUserRoleServices.cs | 4 +- Blog.Core.Model/ViewModels/BlogViewModels.cs | 2 +- Blog.Core.Model/ViewModels/SysUserInfoDto.cs | 6 +- Blog.Core.Services/BlogArticleServices.cs | 2 +- Blog.Core.Services/TasksLogServices.cs | 4 +- Blog.Core.Services/UserRoleServices.cs | 4 +- .../Controller_Test/BlogController_Should.cs | 2 +- 22 files changed, 93 insertions(+), 108 deletions(-) diff --git a/Blog.Core.Api/Blog.Core.xml b/Blog.Core.Api/Blog.Core.xml index 79325e37..19f0ad64 100644 --- a/Blog.Core.Api/Blog.Core.xml +++ b/Blog.Core.Api/Blog.Core.xml @@ -26,14 +26,14 @@
- + 获取博客详情 - + 获取详情【无权限】 @@ -67,7 +67,7 @@ - + 删除博客 @@ -276,7 +276,7 @@ - + 删除一条接口 @@ -384,7 +384,7 @@ - + 查询树形 Table @@ -406,7 +406,7 @@ - + 获取菜单树 @@ -428,7 +428,7 @@ - + 通过角色获取菜单 @@ -442,7 +442,7 @@ - + 删除菜单 @@ -456,7 +456,7 @@ - + 系统接口菜单同步接口 @@ -493,7 +493,7 @@ - + 删除角色 @@ -522,42 +522,42 @@ - + 删除一个任务 - + 启动计划任务 - + 停止一个计划任务 - + 暂停一个计划任务 - + 恢复一个计划任务 - + 重启一个计划任务 @@ -570,20 +570,20 @@ - + 立即执行任务 - + 获取任务运行日志 - + 任务概况 @@ -629,7 +629,7 @@ - + 获取详情【无权限】 @@ -650,14 +650,14 @@ - + 删除 bug - + 测试事务在AOP中的使用 @@ -712,7 +712,7 @@ - + 删除用户 @@ -748,7 +748,7 @@ - + 新建用户角色关系 @@ -1203,7 +1203,7 @@ 关键字 - + 获取部门树 diff --git a/Blog.Core.Api/Controllers/BlogController.cs b/Blog.Core.Api/Controllers/BlogController.cs index 83fad967..fbc67e12 100644 --- a/Blog.Core.Api/Controllers/BlogController.cs +++ b/Blog.Core.Api/Controllers/BlogController.cs @@ -83,7 +83,7 @@ public async Task>> Get(int id, int page = 1 [HttpGet("{id}")] //[Authorize(Policy = "Scope_BlogModule_Policy")] [Authorize] - public async Task> Get(int id) + public async Task> Get(long id) { return Success(await _blogArticleServices.GetBlogDetails(id)); } @@ -96,7 +96,7 @@ public async Task> Get(int id) /// [HttpGet] [Route("DetailNuxtNoPer")] - public async Task> DetailNuxtNoPer(int id) + public async Task> DetailNuxtNoPer(long id) { _logger.LogInformation("xxxxxxxxxxxxxxxxxxx"); return Success(await _blogArticleServices.GetBlogDetails(id)); @@ -104,7 +104,7 @@ public async Task> DetailNuxtNoPer(int id) [HttpGet] [Route("GoUrl")] - public async Task GoUrl(int id = 0) + public async Task GoUrl(long id = 0) { var response = await _blogArticleServices.QueryById(id); if (response != null && response.bsubmitter.IsNotEmptyOrNull()) @@ -136,7 +136,7 @@ public async Task>> GetBlogsByTypesForMVP(string [HttpGet] [Route("GetBlogByIdForMVP")] - public async Task> GetBlogByIdForMVP(int id = 0) + public async Task> GetBlogByIdForMVP(long id = 0) { if (id > 0) { @@ -247,7 +247,7 @@ public async Task> Put([FromBody] BlogArticle BlogArticle) [HttpDelete] [Authorize(Permissions.Name)] [Route("Delete")] - public async Task> Delete(int id) + public async Task> Delete(long id) { if (id > 0) { diff --git a/Blog.Core.Api/Controllers/DepartmentController.cs b/Blog.Core.Api/Controllers/DepartmentController.cs index b8174e90..faf1f850 100644 --- a/Blog.Core.Api/Controllers/DepartmentController.cs +++ b/Blog.Core.Api/Controllers/DepartmentController.cs @@ -109,7 +109,7 @@ public async Task>> GetTreeTable(long f = 0, strin /// /// [HttpGet] - public async Task> GetDepartmentTree(int pid = 0) + public async Task> GetDepartmentTree(long pid = 0) { var departments = await _departmentServices.Query(d => d.IsDeleted == false); var departmentTrees = (from child in departments @@ -168,7 +168,7 @@ public async Task> Put([FromBody] Department request) } [HttpDelete] - public async Task> Delete(int id) + public async Task> Delete(long id) { var data = new MessageModel(); var model = await _departmentServices.QueryById(id); diff --git a/Blog.Core.Api/Controllers/ModuleController.cs b/Blog.Core.Api/Controllers/ModuleController.cs index 2da284ba..27e6f4db 100644 --- a/Blog.Core.Api/Controllers/ModuleController.cs +++ b/Blog.Core.Api/Controllers/ModuleController.cs @@ -119,7 +119,7 @@ public async Task> Put([FromBody] Modules module) /// // DELETE: api/ApiWithActions/5 [HttpDelete] - public async Task> Delete(int id) + public async Task> Delete(long id) { if (id <= 0) return Failed("缺少参数"); diff --git a/Blog.Core.Api/Controllers/PermissionController.cs b/Blog.Core.Api/Controllers/PermissionController.cs index 059e9b18..7346cc21 100644 --- a/Blog.Core.Api/Controllers/PermissionController.cs +++ b/Blog.Core.Api/Controllers/PermissionController.cs @@ -79,21 +79,6 @@ public async Task>> Get(int page = 1, string key = ""; } - #region 舍弃 - //var permissions = await _permissionServices.Query(a => a.IsDeleted != true); - //if (!string.IsNullOrEmpty(key)) - //{ - // permissions = permissions.Where(t => (t.Name != null && t.Name.Contains(key))).ToList(); - //} - ////筛选后的数据总数 - //totalCount = permissions.Count; - ////筛选后的总页数 - //pageCount = (Math.Ceiling(totalCount.ObjToDecimal() / intTotalCount.ObjToDecimal())).ObjToInt(); - //permissions = permissions.OrderByDescending(d => d.Id).Skip((page - 1) * intTotalCount).Take(intTotalCount).ToList(); - #endregion - - - permissions = await _permissionServices.QueryPage(a => a.IsDeleted != true && (a.Name != null && a.Name.Contains(key)), page, pageSize, " Id desc "); @@ -162,7 +147,7 @@ public async Task>> Get(int page = 1, string /// [HttpGet] [AllowAnonymous] - public async Task>> GetTreeTable(int f = 0, string key = "") + public async Task>> GetTreeTable(long f = 0, string key = "") { List permissions = new List(); var apiList = await _moduleServices.Query(d => d.IsDeleted == false); @@ -245,7 +230,7 @@ public async Task> Post([FromBody] Permission permission) /// [HttpPost] public async Task> Assign([FromBody] AssignView assignView) - { + { if (assignView.rid > 0) { //开启事务 @@ -258,7 +243,7 @@ public async Task> Assign([FromBody] AssignView assignView) var permissions = await _permissionServices.Query(d => d.IsDeleted == false); List new_rmps = new List(); - var nowTime = _permissionServices.Db.GetDate(); + var nowTime = _permissionServices.Db.GetDate(); foreach (var item in assignView.pids) { var moduleid = permissions.Find(p => p.Id == item)?.Mid; @@ -268,7 +253,7 @@ public async Task> Assign([FromBody] AssignView assignView) { IsDeleted = false, RoleId = assignView.rid, - ModuleId = moduleid.ObjToInt(), + ModuleId = moduleid.ObjToLong(), PermissionId = item, CreateId = find_old_rmps == null ? _user.ID : find_old_rmps.CreateId, CreateBy = find_old_rmps == null ? _user.Name : find_old_rmps.CreateBy, @@ -280,7 +265,7 @@ public async Task> Assign([FromBody] AssignView assignView) }; new_rmps.Add(roleModulePermission); } - if(new_rmps.Count>0) await _roleModulePermissionServices.Add(new_rmps); + if (new_rmps.Count > 0) await _roleModulePermissionServices.Add(new_rmps); _unitOfWorkManage.CommitTran(); } catch (Exception) @@ -294,7 +279,7 @@ public async Task> Assign([FromBody] AssignView assignView) else { return Failed("请选择要操作的角色"); - } + } } @@ -305,7 +290,7 @@ public async Task> Assign([FromBody] AssignView assignView) /// /// [HttpGet] - public async Task> GetPermissionTree(int pid = 0, bool needbtn = false) + public async Task> GetPermissionTree(long pid = 0, bool needbtn = false) { //var data = new MessageModel(); @@ -355,7 +340,7 @@ public async Task> GetNavigationBar(long uid) var data = new MessageModel(); - var uidInHttpcontext1 = 0; + long uidInHttpcontext1 = 0; var roleIds = new List(); // ids4和jwt切换 if (Permissions.IsUseIds4) @@ -363,7 +348,7 @@ public async Task> GetNavigationBar(long uid) // ids4 uidInHttpcontext1 = (from item in _httpContext.HttpContext.User.Claims where item.Type == "sub" - select item.Value).FirstOrDefault().ObjToInt(); + select item.Value).FirstOrDefault().ObjToLong(); roleIds = (from item in _httpContext.HttpContext.User.Claims where item.Type == "role" select item.Value.ObjToLong()).ToList(); @@ -371,7 +356,7 @@ public async Task> GetNavigationBar(long uid) else { // jwt - uidInHttpcontext1 = ((JwtHelper.SerializeJwt(_httpContext.HttpContext.Request.Headers["Authorization"].ObjToString().Replace("Bearer ", "")))?.Uid).ObjToInt(); + uidInHttpcontext1 = ((JwtHelper.SerializeJwt(_httpContext.HttpContext.Request.Headers["Authorization"].ObjToString().Replace("Bearer ", "")))?.Uid).ObjToLong(); roleIds = (await _userRoleServices.Query(d => d.IsDeleted == false && d.UserId == uid)).Select(d => d.RoleId.ObjToLong()).Distinct().ToList(); } @@ -446,7 +431,7 @@ public async Task>> GetNavigationBarPro(long { var data = new MessageModel>(); - var uidInHttpcontext1 = 0; + long uidInHttpcontext1 = 0; var roleIds = new List(); // ids4和jwt切换 if (Permissions.IsUseIds4) @@ -454,7 +439,7 @@ public async Task>> GetNavigationBarPro(long // ids4 uidInHttpcontext1 = (from item in _httpContext.HttpContext.User.Claims where item.Type == "sub" - select item.Value).FirstOrDefault().ObjToInt(); + select item.Value).FirstOrDefault().ObjToLong(); roleIds = (from item in _httpContext.HttpContext.User.Claims where item.Type == "role" select item.Value.ObjToLong()).ToList(); @@ -462,7 +447,7 @@ public async Task>> GetNavigationBarPro(long else { // jwt - uidInHttpcontext1 = ((JwtHelper.SerializeJwt(_httpContext.HttpContext.Request.Headers["Authorization"].ObjToString().Replace("Bearer ", "")))?.Uid).ObjToInt(); + uidInHttpcontext1 = ((JwtHelper.SerializeJwt(_httpContext.HttpContext.Request.Headers["Authorization"].ObjToString().Replace("Bearer ", "")))?.Uid).ObjToLong(); roleIds = (await _userRoleServices.Query(d => d.IsDeleted == false && d.UserId == uid)).Select(d => d.RoleId.ObjToLong()).Distinct().ToList(); } @@ -519,14 +504,14 @@ orderby item.Id /// [HttpGet] [AllowAnonymous] - public async Task> GetPermissionIdByRoleId(int rid = 0) + public async Task> GetPermissionIdByRoleId(long rid = 0) { //var data = new MessageModel(); var rmps = await _roleModulePermissionServices.Query(d => d.IsDeleted == false && d.RoleId == rid); var permissionTrees = (from child in rmps orderby child.Id - select child.PermissionId.ObjToInt()).ToList(); + select child.PermissionId.ObjToLong()).ToList(); var permissions = await _permissionServices.Query(d => d.IsDeleted == false); List assignbtns = new List(); @@ -592,7 +577,7 @@ public async Task> Put([FromBody] Permission permission) /// // DELETE: api/ApiWithActions/5 [HttpDelete] - public async Task> Delete(int id) + public async Task> Delete(long id) { var data = new MessageModel(); if (id > 0) @@ -654,7 +639,7 @@ public async Task> BatchPost([FromBody] List pe /// 是否执行迁移到数据 /// [HttpGet] - public async Task>> MigratePermission(string action = "", string controllerName = "", int pid = 0, bool isAction = false) + public async Task>> MigratePermission(string action = "", string controllerName = "", long pid = 0, bool isAction = false) { var data = new MessageModel>(); if (controllerName.IsNullOrEmpty()) @@ -776,12 +761,12 @@ public async Task>> MigratePermission(string actio public class AssignView { - public List pids { get; set; } - public int rid { get; set; } + public List pids { get; set; } + public long rid { get; set; } } public class AssignShow { - public List permissionids { get; set; } + public List permissionids { get; set; } public List assignbtns { get; set; } } diff --git a/Blog.Core.Api/Controllers/RoleController.cs b/Blog.Core.Api/Controllers/RoleController.cs index c96502c8..0b93e943 100644 --- a/Blog.Core.Api/Controllers/RoleController.cs +++ b/Blog.Core.Api/Controllers/RoleController.cs @@ -112,7 +112,7 @@ public async Task> Put([FromBody] Role role) /// // DELETE: api/ApiWithActions/5 [HttpDelete] - public async Task> Delete(int id) + public async Task> Delete(long id) { var data = new MessageModel(); diff --git a/Blog.Core.Api/Controllers/TasksQzController.cs b/Blog.Core.Api/Controllers/TasksQzController.cs index ca9d37bc..887cfcfc 100644 --- a/Blog.Core.Api/Controllers/TasksQzController.cs +++ b/Blog.Core.Api/Controllers/TasksQzController.cs @@ -172,7 +172,7 @@ public async Task> Put([FromBody] TasksQz tasksQz) /// /// [HttpDelete] - public async Task> Delete(int jobId) + public async Task> Delete(long jobId) { var data = new MessageModel(); @@ -221,7 +221,7 @@ public async Task> Delete(int jobId) /// /// [HttpGet] - public async Task> StartJob(int jobId) + public async Task> StartJob(long jobId) { var data = new MessageModel(); @@ -278,7 +278,7 @@ public async Task> StartJob(int jobId) /// /// [HttpGet] - public async Task> StopJob(int jobId) + public async Task> StopJob(long jobId) { var data = new MessageModel(); @@ -318,7 +318,7 @@ public async Task> StopJob(int jobId) /// /// [HttpGet] - public async Task> PauseJob(int jobId) + public async Task> PauseJob(long jobId) { var data = new MessageModel(); var model = await _tasksQzServices.QueryById(jobId); @@ -372,7 +372,7 @@ public async Task> PauseJob(int jobId) /// /// [HttpGet] - public async Task> ResumeJob(int jobId) + public async Task> ResumeJob(long jobId) { var data = new MessageModel(); @@ -428,7 +428,7 @@ public async Task> ResumeJob(int jobId) /// /// [HttpGet] - public async Task> ReCovery(int jobId) + public async Task> ReCovery(long jobId) { var data = new MessageModel(); var model = await _tasksQzServices.QueryById(jobId); @@ -507,7 +507,7 @@ public MessageModel> GetTaskNameSpace() /// /// [HttpGet] - public async Task> ExecuteJob(int jobId) + public async Task> ExecuteJob(long jobId) { var data = new MessageModel(); @@ -527,7 +527,7 @@ public async Task> ExecuteJob(int jobId) /// /// [HttpGet] - public async Task>> GetTaskLogs(int jobId, int page = 1, int pageSize = 10, DateTime? runTimeStart = null, DateTime? runTimeEnd = null) + public async Task>> GetTaskLogs(long jobId, int page = 1, int pageSize = 10, DateTime? runTimeStart = null, DateTime? runTimeEnd = null) { var model = await _tasksLogServices.GetTaskLogs(jobId, page, pageSize, runTimeStart, runTimeEnd); return MessageModel>.Message(model.dataCount >= 0, "获取成功", model); @@ -537,7 +537,7 @@ public async Task>> GetTaskLogs(int jobId, int /// /// [HttpGet] - public async Task> GetTaskOverview(int jobId, int page = 1, int pageSize = 10, DateTime? runTimeStart = null, DateTime? runTimeEnd = null, string type = "month") + public async Task> GetTaskOverview(long jobId, int page = 1, int pageSize = 10, DateTime? runTimeStart = null, DateTime? runTimeEnd = null, string type = "month") { var model = await _tasksLogServices.GetTaskOverview(jobId, runTimeStart, runTimeEnd, type); return MessageModel.Message(true, "获取成功", model); diff --git a/Blog.Core.Api/Controllers/TopicController.cs b/Blog.Core.Api/Controllers/TopicController.cs index 308ad082..253f54ff 100644 --- a/Blog.Core.Api/Controllers/TopicController.cs +++ b/Blog.Core.Api/Controllers/TopicController.cs @@ -44,7 +44,7 @@ public async Task>> Get() // GET: api/Topic/5 [HttpGet("{id}")] - public string Get(int id) + public string Get(long id) { return "value"; } @@ -57,13 +57,13 @@ public void Post([FromBody] string value) // PUT: api/Topic/5 [HttpPut("{id}")] - public void Put(int id, [FromBody] string value) + public void Put(long id, [FromBody] string value) { } // DELETE: api/ApiWithActions/5 [HttpDelete("{id}")] - public void Delete(int id) + public void Delete(long id) { } } diff --git a/Blog.Core.Api/Controllers/TopicDetailController.cs b/Blog.Core.Api/Controllers/TopicDetailController.cs index 55af1a75..374aca24 100644 --- a/Blog.Core.Api/Controllers/TopicDetailController.cs +++ b/Blog.Core.Api/Controllers/TopicDetailController.cs @@ -42,7 +42,7 @@ public TopicDetailController(ITopicServices topicServices, ITopicDetailServices [AllowAnonymous] public async Task>> Get(int page = 1, string tname = "", string key = "", int intPageSize = 12) { - int tid = 0; + long tid = 0; if (string.IsNullOrEmpty(key) || string.IsNullOrWhiteSpace(key)) { @@ -56,7 +56,7 @@ public async Task>> Get(int page = 1, string if (!string.IsNullOrEmpty(tname)) { - tid = ((await _topicServices.Query(ts => ts.tName == tname)).FirstOrDefault()?.Id).ObjToInt(); + tid = ((await _topicServices.Query(ts => ts.tName == tname)).FirstOrDefault()?.Id).ObjToLong(); } @@ -81,7 +81,7 @@ public async Task>> Get(int page = 1, string // GET: api/TopicDetail/5 [HttpGet("{id}")] [AllowAnonymous] - public async Task> Get(int id) + public async Task> Get(long id) { var data = new MessageModel(); var response = id > 0 ? await _topicDetailServices.QueryById(id) : new TopicDetail(); @@ -154,7 +154,7 @@ public async Task> Update([FromBody] TopicDetail topicDetai /// // DELETE: api/ApiWithActions/5 [HttpDelete] - public async Task> Delete(int id) + public async Task> Delete(long id) { var data = new MessageModel(); if (id > 0) diff --git a/Blog.Core.Api/Controllers/TransactionController.cs b/Blog.Core.Api/Controllers/TransactionController.cs index 67ab576e..9853d985 100644 --- a/Blog.Core.Api/Controllers/TransactionController.cs +++ b/Blog.Core.Api/Controllers/TransactionController.cs @@ -95,7 +95,7 @@ public async Task>> Get() // GET: api/Transaction/5 [HttpGet("{id}")] - public async Task> Get(int id) + public async Task> Get(long id) { return await _guestbookServices.TestTranInRepository(); } @@ -126,7 +126,7 @@ public void Post([FromBody] string value) // PUT: api/Transaction/5 [HttpPut("{id}")] - public void Put(int id, [FromBody] string value) + public void Put(long id, [FromBody] string value) { } @@ -136,7 +136,7 @@ public void Put(int id, [FromBody] string value) /// /// [HttpDelete("{id}")] - public async Task Delete(int id) + public async Task Delete(long id) { return await _guestbookServices.TestTranInRepositoryAOP(); } diff --git a/Blog.Core.Api/Controllers/UserController.cs b/Blog.Core.Api/Controllers/UserController.cs index 27989821..95137c8e 100644 --- a/Blog.Core.Api/Controllers/UserController.cs +++ b/Blog.Core.Api/Controllers/UserController.cs @@ -103,7 +103,7 @@ public async Task>> Get(int page = 1, str return Success(data.ConvertTo(_mapper)); } - private (string, List) GetFullDepartmentName(List departments, int departmentId) + private (string, List) GetFullDepartmentName(List departments, long departmentId) { var departmentModel = departments.FirstOrDefault(d => d.Id == departmentId); if (departmentModel == null) @@ -265,7 +265,7 @@ public async Task> Put([FromBody] SysUserInfoDto sysUserInf /// // DELETE: api/ApiWithActions/5 [HttpDelete] - public async Task> Delete(int id) + public async Task> Delete(long id) { var data = new MessageModel(); if (id > 0) diff --git a/Blog.Core.Api/Controllers/UserRoleController.cs b/Blog.Core.Api/Controllers/UserRoleController.cs index c21ec6f4..693a68b8 100644 --- a/Blog.Core.Api/Controllers/UserRoleController.cs +++ b/Blog.Core.Api/Controllers/UserRoleController.cs @@ -80,7 +80,7 @@ public async Task> AddRole(string roleName) /// /// [HttpGet] - public async Task> AddUserRole(int uid, int rid) + public async Task> AddUserRole(long uid, long rid) { return new MessageModel() { diff --git a/Blog.Core.Common/Helper/RecursionHelper.cs b/Blog.Core.Common/Helper/RecursionHelper.cs index f6f21a38..092a0678 100644 --- a/Blog.Core.Common/Helper/RecursionHelper.cs +++ b/Blog.Core.Common/Helper/RecursionHelper.cs @@ -8,7 +8,7 @@ namespace Blog.Core.Common.Helper /// public static class RecursionHelper { - public static void LoopToAppendChildren(List all, PermissionTree curItem, int pid, bool needbtn) + public static void LoopToAppendChildren(List all, PermissionTree curItem, long pid, bool needbtn) { var subItems = all.Where(ee => ee.Pid == curItem.value).ToList(); @@ -52,7 +52,7 @@ public static void LoopToAppendChildren(List all, PermissionTree LoopToAppendChildren(all, subItem, pid, needbtn); } } - public static void LoopToAppendChildren(List all, DepartmentTree curItem, int pid) + public static void LoopToAppendChildren(List all, DepartmentTree curItem, long pid) { var subItems = all.Where(ee => ee.Pid == curItem.value).ToList(); diff --git a/Blog.Core.IServices/IBlogArticleServices.cs b/Blog.Core.IServices/IBlogArticleServices.cs index 23e6081b..a38826fb 100644 --- a/Blog.Core.IServices/IBlogArticleServices.cs +++ b/Blog.Core.IServices/IBlogArticleServices.cs @@ -9,7 +9,7 @@ namespace Blog.Core.IServices public interface IBlogArticleServices :IBaseServices { Task> GetBlogs(); - Task GetBlogDetails(int id); + Task GetBlogDetails(long id); } diff --git a/Blog.Core.IServices/ITasksLogServices.cs b/Blog.Core.IServices/ITasksLogServices.cs index fb15c8dd..fb6ad8a7 100644 --- a/Blog.Core.IServices/ITasksLogServices.cs +++ b/Blog.Core.IServices/ITasksLogServices.cs @@ -12,8 +12,8 @@ namespace Blog.Core.IServices /// public interface ITasksLogServices :IBaseServices { - public Task> GetTaskLogs(int jobId, int page, int intPageSize,DateTime? runTime,DateTime? endTime); - public Task GetTaskOverview(int jobId, DateTime? runTime, DateTime? endTime, string type); + public Task> GetTaskLogs(long jobId, int page, int intPageSize,DateTime? runTime,DateTime? endTime); + public Task GetTaskOverview(long jobId, DateTime? runTime, DateTime? endTime, string type); } } \ No newline at end of file diff --git a/Blog.Core.IServices/IUserRoleServices.cs b/Blog.Core.IServices/IUserRoleServices.cs index 9e7d3d29..91272a09 100644 --- a/Blog.Core.IServices/IUserRoleServices.cs +++ b/Blog.Core.IServices/IUserRoleServices.cs @@ -10,8 +10,8 @@ namespace Blog.Core.IServices public interface IUserRoleServices :IBaseServices { - Task SaveUserRole(int uid, int rid); - Task GetRoleIdByUid(int uid); + Task SaveUserRole(long uid, long rid); + Task GetRoleIdByUid(long uid); } } diff --git a/Blog.Core.Model/ViewModels/BlogViewModels.cs b/Blog.Core.Model/ViewModels/BlogViewModels.cs index 411a8ef7..86c16618 100644 --- a/Blog.Core.Model/ViewModels/BlogViewModels.cs +++ b/Blog.Core.Model/ViewModels/BlogViewModels.cs @@ -10,7 +10,7 @@ public class BlogViewModels /// /// /// - public int bID { get; set; } + public long bID { get; set; } /// 创建人 /// /// diff --git a/Blog.Core.Model/ViewModels/SysUserInfoDto.cs b/Blog.Core.Model/ViewModels/SysUserInfoDto.cs index 33d84161..3b5451f0 100644 --- a/Blog.Core.Model/ViewModels/SysUserInfoDto.cs +++ b/Blog.Core.Model/ViewModels/SysUserInfoDto.cs @@ -3,13 +3,13 @@ namespace Blog.Core.Model.ViewModels { - public class SysUserInfoDto : SysUserInfoDtoRoot + public class SysUserInfoDto : SysUserInfoDtoRoot { public string uLoginName { get; set; } public string uLoginPWD { get; set; } public string uRealName { get; set; } public int uStatus { get; set; } - public int DepartmentId { get; set; } + public long DepartmentId { get; set; } public string uRemark { get; set; } public System.DateTime uCreateTime { get; set; } = DateTime.Now; public System.DateTime uUpdateTime { get; set; } = DateTime.Now; @@ -22,7 +22,7 @@ public class SysUserInfoDto : SysUserInfoDtoRoot public string addr { get; set; } public bool tdIsDelete { get; set; } public List RoleNames { get; set; } - public List Dids { get; set; } + public List Dids { get; set; } public string DepartmentName { get; set; } } } diff --git a/Blog.Core.Services/BlogArticleServices.cs b/Blog.Core.Services/BlogArticleServices.cs index eeff04e3..67ea2b9e 100644 --- a/Blog.Core.Services/BlogArticleServices.cs +++ b/Blog.Core.Services/BlogArticleServices.cs @@ -23,7 +23,7 @@ public BlogArticleServices(IMapper mapper) /// /// /// - public async Task GetBlogDetails(int id) + public async Task GetBlogDetails(long id) { // 此处想获取上一条下一条数据,因此将全部数据list出来,有好的想法请提出 //var bloglist = await base.Query(a => a.IsDeleted==false, a => a.bID); diff --git a/Blog.Core.Services/TasksLogServices.cs b/Blog.Core.Services/TasksLogServices.cs index 49393cd4..07d95f1f 100644 --- a/Blog.Core.Services/TasksLogServices.cs +++ b/Blog.Core.Services/TasksLogServices.cs @@ -14,7 +14,7 @@ namespace Blog.Core.Services { public partial class TasksLogServices : BaseServices, ITasksLogServices { - public async Task> GetTaskLogs(int jobId, int page, int intPageSize, DateTime? runTime, DateTime? endTime) + public async Task> GetTaskLogs(long jobId, int page, int intPageSize, DateTime? runTime, DateTime? endTime) { RefAsync totalCount = 0; Expression> whereExpression = log => true; @@ -41,7 +41,7 @@ public async Task> GetTaskLogs(int jobId, int page, int intP .ToPageListAsync(page, intPageSize, totalCount); return new PageModel(page, totalCount, intPageSize, data); } - public async Task GetTaskOverview(int jobId, DateTime? runTime, DateTime? endTime, string type) + public async Task GetTaskOverview(long jobId, DateTime? runTime, DateTime? endTime, string type) { //按年 if ("year".Equals(type)) diff --git a/Blog.Core.Services/UserRoleServices.cs b/Blog.Core.Services/UserRoleServices.cs index 38f524da..26194837 100644 --- a/Blog.Core.Services/UserRoleServices.cs +++ b/Blog.Core.Services/UserRoleServices.cs @@ -19,7 +19,7 @@ public class UserRoleServices : BaseServices, IUserRoleServices /// /// /// - public async Task SaveUserRole(int uid, int rid) + public async Task SaveUserRole(long uid, long rid) { UserRole userRole = new UserRole(uid, rid); @@ -42,7 +42,7 @@ public async Task SaveUserRole(int uid, int rid) [Caching(AbsoluteExpiration = 30)] - public async Task GetRoleIdByUid(int uid) + public async Task GetRoleIdByUid(long uid) { return ((await base.Query(d => d.UserId == uid)).OrderByDescending(d => d.Id).LastOrDefault()?.RoleId).ObjToInt(); } diff --git a/Blog.Core.Tests/Controller_Test/BlogController_Should.cs b/Blog.Core.Tests/Controller_Test/BlogController_Should.cs index 3d1e3178..59d42ae0 100644 --- a/Blog.Core.Tests/Controller_Test/BlogController_Should.cs +++ b/Blog.Core.Tests/Controller_Test/BlogController_Should.cs @@ -53,7 +53,7 @@ public async void Get_Blog_Page_Test() [Fact] public async void Get_Blog_Test() { - MessageModel blogVo = await blogController.Get(1); + MessageModel blogVo = await blogController.Get(1.ObjToLong()); Assert.NotNull(blogVo); } From 5e84e11ca84f3bf245ef4307e17b48cd2c20b265 Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Sun, 2 Apr 2023 15:56:20 +0800 Subject: [PATCH 14/84] =?UTF-8?q?=E2=9C=A8=20=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + Blog.Core.Api/Blog.Core.Api.csproj | 1 + Blog.Core.Model/Models/PasswordLib.cs | 2 +- Blog.Core.Tests/WMBlog.db | Bin 200704 -> 0 bytes 4 files changed, 3 insertions(+), 1 deletion(-) delete mode 100644 Blog.Core.Tests/WMBlog.db diff --git a/.gitignore b/.gitignore index 4f554bea..fa4a3448 100644 --- a/.gitignore +++ b/.gitignore @@ -358,3 +358,4 @@ Blog.Core.Api/wwwroot/ui/ *.db /Blog.Core.Api/WMBlog.db-journal Logs +*.db diff --git a/Blog.Core.Api/Blog.Core.Api.csproj b/Blog.Core.Api/Blog.Core.Api.csproj index 3bf64399..0d32998e 100644 --- a/Blog.Core.Api/Blog.Core.Api.csproj +++ b/Blog.Core.Api/Blog.Core.Api.csproj @@ -52,6 +52,7 @@ + diff --git a/Blog.Core.Model/Models/PasswordLib.cs b/Blog.Core.Model/Models/PasswordLib.cs index 2b43c265..9eb9292f 100644 --- a/Blog.Core.Model/Models/PasswordLib.cs +++ b/Blog.Core.Model/Models/PasswordLib.cs @@ -7,7 +7,7 @@ namespace Blog.Core.Model.Models /// 密码库表 /// [SugarTable("PasswordLib", "密码库表")]//('数据库表名','数据库表备注') - [TenantAttribute("WMBLOG_MYSQL_2")] //('代表是哪个数据库,名字是appsettings.json 的 ConnId') + //[TenantAttribute("WMBLOG_MYSQL_2")] //('代表是哪个数据库,名字是appsettings.json 的 ConnId') public class PasswordLib { [SugarColumn(IsNullable = false, IsPrimaryKey = true, IsIdentity = true)] diff --git a/Blog.Core.Tests/WMBlog.db b/Blog.Core.Tests/WMBlog.db deleted file mode 100644 index 4931b9674dbdfa61661ba06a34e3c55254029529..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 200704 zcmeIbc~o0hnlCDW&;ST|h%Jvn#&K+dF-Xita*D|g6%QEWOqIkX;22aEk|GHYsjez8 z1_L(6PCO6s5Mw*B4UXdk4A{K)x>mog*WK&Z>Z-S@*Q#D`z1O94ByM+Cs{iP^w^!ZQ z^}c~*?ZtX@N&Y@SRmmwm@z zkjZ2j@P8xxKlE7wUlgGq@LxISd#tY+vJLBNlHhKl?%z2BO=tLJ=8uBfdltE)29m6dO; zGGuV>WUSLBXVh%1uFNpl9A2u4avQeqs55Ney>+XhrnY)pS?$|~*Q?$(lRxBc&CTT9t$7o$LU)bA zyJZax&Q^zayS0Uiq>9q9%WL(vdN?K{bA#wrZnx82fs}~fe+}|ka{$Z^*$|~2rTCzg z@_21d#}vsb+?3Tz)!C4xjquG2-*`FQ?LwEQNU+uF@q)QAQ+UA!Q3DK_9cgNne$^^@ zXA|r89WKfZSX-SUse{wPzdUXbsj?Jn1S2sy9Q`81EJ?;XIFrD;j*S{7{5Z zKH<~U4iEyiy@$kA<9l4yg3RCqF(bLg>ZTmtXx)a)!|y3o`t)>pr-lvEEvS;KcRKfl zzo__u8ooZmP_4H+n|OBc=xtytP*g{9D^OmNRw!`}VOzk94p+0&!3iCx%z3wW?u32No-No=j$g=^(X%4KzXTfiBSZ%RJHksC9l7?qq_ z%5Fs!pQo8;bU^{MygWMjHH0ZT!B)x&g^iPOHc2?>r!9)1hMDaW@SuhL1>Vi*!h~%L z13yz~;+7bJ{B$M8Ce|84t=_Ro!S=4YD%b*34)I*(_Szck6z6La-=&;8tWWFX?_AY; zTI*YEUN6@bh5W#-_u9O6Zr~Rba{F2J4N$C_obD+CydElKFT^It+k(PV?{!=E?zJ_D z^c2{N7c{h>=-%Gi5;KW-Cy3|$dU5aa)6`NeR`sv)x` zL8;HmlE2MT;`zW19a4^6NtmycJLDizg@JFkIKdBlm z{Dnb3`49#O1B3y>0AYYIKo}ql5C#YXgaN_;VSq63XUqT^|EK8Qmg#<{`;G1wx}WI= zbvJdVbzQnvoeeIN4`F~XKo}ql5C#YXgaN_;VSq3|7$6J~1_%RFF_4y$xJn+G|D;Jy zPF$|3vwHSy&wuVZ|Yqiy)-GYAOWR;89y)qvxaiFz+^3$8zpN~D-svU(XEwfiGUorbtokvak(4|c($a; zK{sa(4|)GTl@rN5!T@1_FhCd}3=jqg1B3y>0AYYIKo}ql{CO~d#{cq^@50AYYIKo}ql5C#YX&&)tio;*#aKU}h=zf`Wt%af_CjV(3@^VPAy&>7~;1Ez2A zoAHa))w>K1>VU!8*l0LF)qBw1Ti=Wy_c&p5oda&CqiJJZGi7k2)yf{&eaPUlHc^HK ztHa^+8tN&7m0fEL*XyYUE9@R&@Up34e+0M&3*PtInp$C}9C#s!_Uy5uZN1netQwmK zw#3Lf0nzP8a-z zEvtt2zi96@@EIoAwF$qgHK15|ld3mVINcOWziZQWr`HBM7GMwWcI*FiUb8wH zVHY9;BrzPKyoN^Dy$aqAJE+EB0t|;X)G*{8EY8g>g6$CUg7;t-BM6vHx}m>=jH^{)-)gTKql>m8mqTCcnucXLr`y{yv@$W*m=aM(2MlgX@Zs* z>fF`_o1@84X>)_wu$P3PV&l6=6z2flMmtWS9D3g6^R&y^>TaMy?kaP54p8pUFC!G^ zy*5W9TkcT#Uh6SfT`qgbYc8kL?g^?ht6QE7Nf{M)bZisp|yh2}pV#+m^Wak>;pCLDQBl}O} z5*q(&Qs0y5KGtQY{Z-l??SI$yX){v)H@HMTgaN_;VSq3|7$6J~1_%R$0m1-b;Mo~y zfbGQ=8Rx4t>a0>(sXTXm@%pM=%<-f2(D*mw-A4b|k#EL(C{LiRogTmDzj%cH_#u7& zmMPa{%FfM40~cfAMq~cQ{GtsoRux<>fFTXKtbSjumCesu7z9!0u76{96=?B2z7}Xd z?Z0$&e=9W|fXOHVkiQXMwyCtTr6rt97}w{or%#^n-yNbK^s{;BHu<04gsfy|Pk~gh zv7mUPF;9rZxva1$qoDHE(>;A$DC4t188_yOL4__SQc2*NoTWk5G`Z`I>&q+NsL7@u zPte1k(A`~=9e42esk4;3(IkeGyD>i><&;v0!}vyenXiU14E{bPOn%8mQ&bQXYz$wf z62)QYAg}Mxb5rlUq)}_M(lZ0uiv#WitQ&*{d^> zk}yCRAPf)&2m^!x!T@1_FhCd}3=jqg1K(B#(Aj(my5A%7*P^re(pG67Y2QfwZR*j~ z#zW2sD5s*Y@k0CRDIbED2apvOk7E*o4iz*DEmb9elM z*O?QybKqJ|L?EY$@}|ObH@g1;(>4Z4{3pBUQ|CqIG~mY_QL`V=<)+n(WST{5g=V3n z$XGb`%WMF(r(^)q_cDb@Wlb=t!;p=$o^7Bf?oPH{WroM--fPT>0lNQF=1Rxpg_Eo? zY}#4L_Q6%)4P&;@n81l)y8kTQc?W1`&JEBL=UCirPKOO9#LNn^+W^RGqLAtCYm*ml z!UQyW@CJ*Cwxq~uWY64_?Bspv$jLdv+=iXZ!7@n)d-0jUjFQ3H6ILQRw+XYhh4R8P z^+5OM%$4gbop4}YaA%MlxVPQ9-`2!V8Y{QD^-{~w!4=4c5}^&gi7WK|^XOS?XP?l9 zGT5Y~$srk;T8@y@BgkvmN+lS^{9szZ^q;J6Z3;ZPMfVTKS4p%i1DNyzaTw6a=8m&J z3!+#8+z*zbTybg&oGV-@1a5y6=y}K@!ZCwXjteHsNx-r#L0Fvn4`cZz2AR{3qp(_~ zK-u*Oic|726fXHfC|vTTWNfi$#&Bo|(V&8t0M#yA1Xx)+#N#8-f0^z(!(2NhECT=< zDV1I~MM*Hh^#aOmRD=dYhP6NlFEMbO3lSoxJ?90EIHIKRaW6oCoLYt#KB9nrc1egPv3j92(2Ejj+1b{QQwmTUD52imtA;EYanDc#qY@aBpiv2q4VPR`=cH6IPn#_6rcY05{eTg2j;bK0PwZ=)W0JK^=qQk!%`kt zCB)HK$rnE|36XpZ|HXxn*Xo+H|+tf zJhd}bo^mE7H~H_9-_!hy<}Wm<>PzZZRHLfbl)q9^N&lYolO&zu6Ge97&l2BF_^$~c zCMe}Mw??CZnO`tdCAb7YE;rD_w|q};;~C46Ck3lkQZT6>VAMhw zJR=!TsFw_rv7!}b^c&Zx1u*KI$WePGfrjj94i_k26 z-BMt1xmvb{<0$6pKw#(wOo1wIZSvqBc*>7t%oWQVFiW4sOlL9i#6T1r?9zZ{c01Eb z%8q|xR0%V`Rw!LCQJ=+IrIxML$isO4@w0)ar!Zbsqs@zd2WK`(K}^~Qll65tU@{^6 zF``;q1BW42QQTs_RnU#$;EXDE9)b!a|V4)R}}4gN|%cx9Or z=HG{0#-&i)WjH`30IC)^VJ3daXAqa6Oc}&|CLM601Lop~a>xZ_V3E>|PjHZwH8$d( z((zvK!hC!dFOwgX;er>bTtekfI_i7}OwAX|rKlhA1TzV4RHnILa(>8-+_({Uqd_{N z&IwcWLpkIkiu!RD0Oz3@`C?fNJ2H3xZ?F=To<*$#X5@#m$PHCdC*lVVn!Ea1u*g%M2_4HrXpYO)kY7o*Aep$u{v74;yV zoh$)Qk>Z5O_|NV^>42$dD!y0-MN6S{yksj(#b32l*aoU|Hp33{2EQpF`tJ~|LFFgeuDIjR(eX!%OmfLY)dIvyrxZ_}l-QBYr5T*pMb~#MFUx@mC z5cSoOHkLVeEnIi8yP^jsF8D@1$6lJe{uP?m%=QkM<2{of4N9;;q52S}IvGe~m z30I-IN&AKNY0Cebyk7mc%C(BK30D)&K?3q24E)bzpu?+C=~u6oAI+MvH)#E`U z<*l}Q&e!CM+N!d;Dnnga`PM2!2Io%3I&E@B&DQG53^@A6OEsZ$L3Y#`w(s7$)lgGg zy{)YFZNuwTZyU;X*X^j@4r;blZLeDoGSwb<^Z+NQG-ep;ZC)&3KsQ`=IKL#ru;1!N z4evTrZf+*;Zq1v36}oE_-YvtYc5Jt{P?1zoI(B)j-c~pWBy2J=H;B%j;!=T*{t&V+% z+K)lh07GU+np&k_wMyRE#JYV4ESv@t;53%VHx1wEksGYftAT+qebPK$21M}8cZ16v7zAJ)PXWI{a;|qAEZ?!r0?RMKE{0U0lpTLK3 z+EGk+bm$WICb!Di;cledyPR%rc@70ETjoVwFUViDXB*{c4c1jW=_&`RH$&AJ?-nlM zJdcMf8ho$(P{e9J;nUO(5CXQnhs0ImdtB9m%-{quBe}-vhAniWbsI7dzo%5`)6?ah z8a7C`pfl9!oz8vXFDibZhOf^sRMDYlJUe*wHn0^asyew9C@%@>Av%O@0WYv!%}xg= zbf7Zl-QKw~;vnI@dS@fYeZspoS8Pd}$J6XQAdDucmA5CcwQ?7(l_Og-MZPKd3`cIv zkYZGFYAHD14k}PjGtcOP0w_j2I{7t(DLTPcIMR$)QL{@V~O$xSm)m6b37_=`^ncHh?uv46`Nqm=b?yx?s zkH2$O?`f@Xv3cQqx=0Kb@&mgb4nDJU1HYh$dKN)AU4o3T(v-8d_RF!}ivem`TJtK|Jr*i+h)!rWS_V?tQUs6~-4EI#A;+NL1<9 zu9bH*unvU7a()sZ?lwmKCr{Hj6EK7{2-jOC-|AjqeXCY9jcnwgJfp=TXK~?GgE$e zAEuc-g|iNtp+@LdL7yJFZlPZZJdGLrn=@n@b<8phaC9dga~Ms{~AJTZYUdt~9m- z-BX0KZT0N)?EJ6}|7X4+d-&=MqeYN2e!co#XZ;qpvz6cY^P&k3p@nKF^`QKxdc9uW z1*gb@W7thZ*gp#eraE0vfud1%wOAW7Q@X`~DWEGxkkf$9V;V zZ(hmv;-Vch4TRhtE!(>HB(Xk(hw1DH56+wAe4WSlC+E%#y$Vhh8EA63XH)Q=lL%M` zUhPwQKhzJ48sP^AOfGmOf|@{HOon?~&(^>Q#~(cMr>Ry^bOfJ%IHR6_@DzzY&f%q) znh5G3Cji4agpEVRE|=Tf-e&G#BqAX4{DJGTP%(grjXVy6{$ok1N?%YQ?^?u07<$$Y zUC~x}P8Z&OQtX~_#U@={T#=-eAUx*+z~T8n|YCesg8X= z;sjorVArvuDMh7Uv`Bu`81@~4ON;i+Z2N*A(R@Ol54u4LwGSR6wX;S9|tRl&j2c+aGmnbE^Z!cHv$#KMbY~qUxz0=sioAejCDw z@0n?8qnMRzjV)$RW}Eme1KL&a#n>-)+Pyjz86*Tp2Xl6iApQ;v=0H;|_4Y&jaxUoN zRoLN8-jpeXy)<}8%&W(1TOFv2#(ho3YaPoe=!Z_(iK}unPI)mm z8XhsZVcR_zO*kDf3qi#pc%n9)z11G*)uOim(KR0o+TfwLwT08Di9FnlSv+z7Mwr!L zHvp~wU#U7M(+%m~)2Y&Kr|r~EYJZ|Nr~XIk*QuV=+?0P!`8=g5<)!5RC;5})?0AYYIKp1#N2C7%;WSaR4H8Qzcu1A|t7og7{ z*S>B&&}@5_Eqvkl>);942UqB1>ZLnCRdD52GiA3sjrfQe13fYVvl9)lhRcBV=r+I( zTVU7GEmO@EZU;C8PP)zxc6khnn7 zaq5zQ6x=4@^_t-v{V|?t>bXV(+rS0exc3!^r!{KS#-*d?a#yR1aX7WD?z?^F*}UR9y;zbQwQhm;$W{*d%EsV%8I zNul^F#Zg7IB02GI68jRjC+ZS@o^UFmHesRsm+~|6H{^QRKgceKbeb%$rlMT~kgw%( zd3?Mp7p<~vnz^`Q#cGRX=Hkj_28&sO`1~c7QVHVP7c3hkh%cUJDUl$q(pZXTF0KH_ z6-f|ZkzpyExwvxK3QK_m@%buCz65ct(wr9&SH~OkNfuK?I_?s)SYa_r5LYFdb0gw$ z+n!*_iAcw7`|_3M4H5CU!BfrI5~Nei>m^7ho3kWHt4m+uNNeJ(BrTZ{;dnV$KpnSE zg81^4mbDVZmFbq3C5R`zXjvmceAx=~OA+yS<4uujUM)d7#hf8QI@!ERg0$LVh)BmB zG7FYl(j|y5TxNMug7^zdEh{C6L!-Pxinz+MT!MI--m*-BxNfm!sRZ$Pi!4hdh(m`& zFGXBwUK|mRyR;>hF5*bXSw|`?FGPgn4tV`S%R&j_OBPrbNDyB--!fl!e2UJZl^~v)W=WMGo~*T`NDzk}bg~rjM2lwT;)&1&R!a~E<5d#G zp5Kns1oWv1V$JZfBP?Rdd(i(Z3(u&f=nF}YDCd^zoVWT`Eta&E0VZ5FFBlxra zRW$yW>;8ZS;6~jaw5_SXPAyCMB>A_=b(&F)LG4sMRlTI_P5NEZ8;Ws7Mq+ouZxia| zqjH0+3yI9ur(M5vfo%S^rE0mXw6qlV@l%(|GzSlw%BnV3x-+oQ6|TvJ|tv8lwATTot-o0n%SDJ(~= z=H{ZZ;v!Qyeewj|{{X-GG12|QalR^#(TJwTxK1BG=Nr98j~s5_kh(yYzRjqXFJv2f zjl5K*4!^U>+gw($*;H6qTvS({A9H>vF;?^$RH%Hf36*aR^o!N(TgmcrV?}O( zv2b(l=KTD^l7jpaFuts$pg1qDtf;uEfayO8`~C(V-I_IHW0|N|&Q)jYljf>r%M}Y` zOZmecH6F~&g&|LRA}S=I!5C}}2|a^RYGm%X+I~K;WZM}SfY-O0eES5ch{SY>fZko=#KwCRKer*@Q-{51B3y>0AYYIKo}ql5C#YXgaN_;VSq3|7?8?9`wQ?~U`51mj}mfXmsj4?nz+ydvGzv*pXbrD>+8^!~XA8wCm=E3a6Xe!0rYCI|A%MmpprR zw!)?XA4U)Eb4|G>Q+BQq{)HD(7)6qy@xN@I)XWii!T@1_FhCd}3=jqg1B3y>0AYYI zKo}ql5C-Nl1Ni-aolI9Zm#rkW5e5hYgaN_;VSq3|7$6J~1_%R$0m1-bVD2%{o+;PL z4p?@qgmvs{c-fz`+huoJ8*||GdvR`|sW1nY>*N5JB!c@3=DnZKYi7J?*)1f!#&^lwZPCLv=sF5 zBj4DkX4j^v?&i2QK?-K5A9|R~gHz18{>hFf%;lcI(a-7OFF=cb;FG}66|D3(ZHMWb zx0zdq>5=2iwd3$z`l`@6vy=J*gjaE$K!I+*2$Pk|ql z_^Z#E@w>R+PCxh}aQ!&SQTJUqBZIke4Qcq|I&-z3xpHUn@TXu5{h-S?a-Y8R1Y~_9 zVDnMm=xKW73L^i?0QdrDneOglJ~;{ED9b}f0)wO2a)j7(n>jtRu`c)<7?_|RjzCiX1|LF<3>oV-PPLCjyA*=$Oy@B@Abk`;Hv#%Xun7;IgIXwm$rJo{F zePeyh<i`(iDMu; z`Pm4#4&ns`=Yw(h!kh*F-11*(gH`YT?ynKtkqP?#N&o#G=F9_xb@>iG@FgmlV~^-N zmmxjN;2_=m8H5JYeUk3IN?(33+19~j1h~Lh{%)|U14RK}iErctee^mSqMsfHI52zi z99z+{=*SFVdMJwUufu=&7_v50rDAIXH}2vn!ww5_7l|Ka&iM&!5jYX56qK{>F5lzx zP(woP6zG18Ty|@W9&dwu1U=RD5F85GjSzYD1o9L#Z#ml3f0d~eW?7m!`JSdwHBbEq zg<1J4gyV+?;afXH!V&XLVaRr-}H<=uPOLw#|AowcF=uH+x+8@kLM=ex7kYirm?!BLZ; zvvKj^oS~B~Yig^vmDRp&c)jXv_B{2Swe?^SoM-Q{Iu2D=M#SJqPB>XPB*sY#%Tt~; z-F7fVs4;vj`Oew~x7AAtkFFFTx?1b)wuZ8X24|~-lPOW_Y@Xd7Ds+muKo4wRB1Ga? z0(W<~C?4VfiNl=^LJ zr;fpzR!{R*XHzH+gFjbvE?K0~uURAS@o-|W!s*y+Yr+ACzb_SwMSP2m$7k}NFetGQ zKYI9K%*NvZwhTQ>Y~YmBDzv&>(ILPKBuL?*8rm(st&qrP*!Z{?CeG;GhR3w*RVYtn{KMb>gE(Q!EwB^c2>0@^8TkqbP(?P*CGv%Q~7Ds`P8u%8%-JG0oO}p`d1e=*6R&y~l?2v=yc>q=N$dQ;|hN zC#1MW7(TqH2&NJqqbyQJf+@>tsv`4B0<9XcgbPT0=D(-EMcPr z+7x%K(@ur{yO)y(t!jH6KH|QTvQyr0vlTAX@ijR7 zv)*U5Wb#!{(cGZyAT~rvU@L4yqxfAz=8;YFRp`w4?h2OmI;YFlP)T{MHaq9*3w-W5 zcVV=LIROZ}{38dhiWbSRmnczVPQosS@phPSo^uRc_ zgbo20J4W2wSW8)B9#W1BZD|3F3Y6P=vG2^)~Ky zg&L)tn}PUj~B-g4I`a zXFMD-!Gv{Awpdn9hge6oxa`oqU^RA>%~R?1dU=;MQXX0U7xLL)ug$ddOK3+H(imNvcx@0ft#kh88XrQ{}k;hneL~$qq^_vQqz8(b|&qu zv}M|V)Q-Sq@*xZm1_%R$0m1-bfG|K9APf)&2m^$H=f%L9loXiYgU&sd$>nQBW^8YO z$46^dWF$i}HJeNw>&Q2=CZr0EP+t;5RgBA8m08V^UKk@;jLT|esVX9^(Z)y?<8nfY z5`;DE+3%V-Pwd>76cLT{1nmT~B4V>#AH%p9ms8>VKadU`SRW%*jLRxG|8K^^iE#d3 zL^w{m6lDLu=T(r2UcvxjfG|K9APf)&2m^!x!T@1_FhCd}49pz{l94H7|Npt;5Ml{o zfG|K9APf)&2m^!x!T@1_FhCd}3=jsM0|Vs!|8pRfs3Z&!1_%R$0m1-bfG|K9APf)& z2m^!x!oVD3fV}^oV-6wa5C#YXgaN_;VSq3|7$6J~1_%R$0m1-b;5jfr-v2)bQi)2! z0AYYIKo}ql5C#YXgaN_;VSq3|7$6MHF$T!{|2gImVh&+|FhCd}3=jqg1B3y>0AYYI zKo}ql5C)zD1LXbxb0C$dBn%J+2m^!x!T@1_FhCd}3=jqg1B3y>z#Lz4q<>WKo}ql5C#YXgaN_;VSq3|7$6J~2A%^0TIFBLG>TO+ z?Te|uNxqnDQ~kB-jPh?3tCWYI0~k?B7$6LMyBIj~{);MocDB6R>9y9|sW+*LW~+C% z%kH!`Znj1LUR_aJRaRGJs4FYqT4l%(CeK)>P0ol&S65~j_FLTz%~tn1V{UGyVf&6c z!}i@TSb%p3HV@3l)(v@uUPvciBFKq&A0_YNFf`-GWpVPDtSJ z)*W(j^cEmvkRgJ^RR^2BBv)^*ta{5Z zLv4l~+YMoj>%>f0A2wlq$b?Me3Z=d(eQHV|afl1}YUq4%xk{g&F7Hj@6jQhn22U3& zn2>-ikv52#P&qWQaS^K=fDDUv$!NPzRIMQcc3x3}v6It(Mv=Oct$a$^u&L+3hHY;LI@8+q{BOzSZjS2JI2g zU>NJ$SttMqHv%6osriNWAa9Eiam%f+HGcrVTxHYkN^S)r@L&I+cfu8E}J_d^~tZs)hNaFF-^->%^pv6e7E7$6J~1_%R$0m1-bfG|K9 zAPf)&2m>==fUN(Y32Q`#FhCd}3=jqg1B3y>0AYYIKo}ql5C#YX-!2Bo`~Pp3zlgPj z0m1-bfG|K9APf)&2m^!x!T@1_FhCfX2?OZ;|4XtrWxAWXsx&(7WZEk2BkdchzfC=w zx;*86N_q0%C-2dGqxrE$tG=LKr}|ozr~GT>J4xRpbtJu@xUa}d984@r_+`TP<&69- z**BmFf7%zlv`D6=^4MQ$nOY+^mda$M>PpIGb$eT2xve4Fzm3LbJS-$IHU8UuFZSJtYH>zLzOPDr;i%jF#Tj7jDvh7wN$pEM~dg*_6{r*(on2*~$CTk&|x86|_YC#*ztZWCs03+1JU#sl4-Ggq#&bh;>ai_L@9spr7G?biLaCM#NtUT$^k zrIw+CE07H(LK}P&SLpla>F%z{&OV_HWnfB^Lozb893iJikk_!4N-&K1!L)$sKUv?} z6nJ!t?jMd(8^pNWvJ7C-3&deSC!4!Er_0t*NqMa{JEB+u+z*zbTybg&oGV-@1a5y6 z=y}K@x}A0^$K~`$!LltuSe*I~WBDcqnbVJ>uv(=++4TsDQ}QtsF8M+zT=FH5Xj?3r zF&r8~G^pSuK()&j0an%y@%RYzU#9!cFxO5A%K(5zN~PCLQ4&mWy?}BX6`{e9VJ#5C zOAH+6LWIa^&v}6(jwmU7+zSvOrl5-vaW<`||>__PS z;4T5{$`)s1tMu~k&>_GqVd&!WFAgZliyx#RJfjc~R|2AVlAY+#ie?czQ4~^|4{fP{ z7pqG3pT5I%53>%0<@N3ymn1grDF`YmTritLD9J?rAQ_>>XqSX&5~~+V1HCAbmYr?= zIHdr!ffDM?y=usk5%&x=Fe-r|2^y8)*uYr|4BcWnIwKx~;K+HmF)Kz_A;#r*Rgsv8 zfx|Y}dEi+Xk-RVXlp{>_+;uXc$xn{n|RT zPuxt+aF>A<#weDftq{5v>gkzcwtfdNi!70xFJc3UemdxzxK4N8^NpW_F(js*g~eOH zz`k*NZJ~!2YV>pg0xFjcsC;2S!R7qy+(O=E`>QCcoW2o;4K_w&r|7|poK`lxkr#R_ z!$HeO;cG11Xv*D~Uxc50AtTt!+y`Q=O$dz>P+T0Ru%e=AVh7hr9N1v&KzERb zX?mu>@oYqxQN#-374s~D2~QAa0D7PVc@y^o*y@27KVNq&%r2y89ed4;);! zUI^|R!~ur#@JwO!K`{WMr^oP#0uBt`m^|MZRZI|Ma5FUz4>DX<@^XV^MKp4Y0J4Hp z332lpt`K4cFJR9wa@~c%fpIvC0OP7jdVXIK&F|#V zKKkU42y87?;oMKT4@m~zpNW7uL!DrL+b=WsZ-@Zz@;Y6!!SjK2q8X;g+r%>rJN3D} zvjOa0i-0*#x^Ra5<7Wd;Pl;q0ngPiqdtOGEQLhbg-%SJ)_IH*J`r|bKYKh7yw+;zB z=s<62;l0AYYI z@Mpz9yLt-@yR&w&nc_@O-@=(kTjO@Jqiy~RPTo{nNFsSo>yI`gOE{AWMP9oGClhjB zWW&%uGBX~%be-wDPc;62N%o#hcSW}$?Jv{b(oSj*XyvJ$sq&OFDY?mim;9dQUo?NA zNmXA`zoHsdy{7z?l1lpbq@N_|6rU)v6MvTYZo+>}_%J~!zah_({S*{&Kh#T$WV$sP z4KNHdRf0Ubt7U6Aj$*D31cq)vu$8wq zdGHTB{uoiM&c@3g@Cuf_*6h7-j9&Hur@JxR zDFLn?X63Kehodu;y_`BUA1nv?ug(U4B_O=A%n9@FLoVY|DDE;Gpb`L83!E?$Kjbrr z%TT5a;y#lOxX=M}@k2S}0-{AqH$K5ZQr6gre@e%D!3*>8S-ebsP=*U$q;d(BKk2CR z9WXUtESI8w#1qUUxKWwrg30+IH*(`f+>Hk5h&m@s(GTU2izw>HSpb}eX5@=yG3-b< z0}*sC#48{qI5AQ0gbDef9CGo*ohTh}37U-`%HZ@qln%Jm z0kiR0z?CryBbqE4@}P9Q#b`2qD1%%^MLme;xJdAzBE<=l@t@s;(g9P^RD7`vik3p@ zc*$0nioa^9unkn_Y<9X^*gr5a2^Qx_AThZCrr@uM1p!Cny1gFy(o?{Z0)l4V2RjaH zxg7_lcK|epI}Uc6gUUu{=So`5B!{W@3sK(>qP{xP#xm!wh3hUfE7}v7xZoT49D51g zM51ZUZ10dc-ZS~ppoHuyR3E~$`~tS4BwrcMuUwt8>yXDwwd9mrJ+_8jl-Fx>G=T}e z(NR2OFbO(O9(dzykN{rkg{k@_kzReMi^1*HN7`WA_F@M}08N_jf+_r*v4j{ZZZL2| zAstbR=JD%{Y}r^9<`_DoI7@iQ5{@i5xEPKac1g&hO#$6e5n53w<*@ODBS~`35>z&r z-5(Q*t+H`lEgfoc3rzG6lDKkysH^~qI4@in2u+yl;`SJg5=gYd#D5)I*~pi3;BqE! zorMy33ox3UEmU5f1PC<#UnBcmrt8x!PP>|B(oSgWQYTa0sS8rhr4%IJOJ1${nZ~JE zq#jdS)hg9})h^|KD6cB3lKwgAR8o;*OtCfb*NJ-*ew*-r(B?DBw^h0(N$%9G|!=du~<(Pl$Dy|?Xd$z5?>B#nEJK@nEyJ}uC zum@IiWUo;R9)ziL*qsQ1i^wPax2e)b8bC3?0M$gogXCQ zm91)lwHv{13u`e=oHWBqiRYvlmO?xy&9LI( zIcbJ94bMq4EL?a_nqfi0bJ7e851x}|SYPmbG`A~ZA;Fug!o%utBV9{1w7S_nX3%<< zJ;ogTAI_IOCUhy=2>&xlt{HRMm9V&AhK3l!h8U$A@_krjpj#P5NOlNAGD7wtSXxjP zjgS+I@(|C`Sg#t1IL-zxB+QMjdtD~va+C)i)+y`p{R;Nb=e@?Zr*pXv% zG*SoYOHU>*bkfJqO2*1;g>?WqYoo#HV72Y8X6Jz%^rh~A6Yr-6gW}06H>8(=HLb7| zU|QH64vGsay z)K^jlQZke8B(Kq2)fA}zT3xI9d)0f&e^KsF`UAZCU!nM46pad1;-`su2|rKpCd`vR zlD{eYPf+uGK5yDlfr9=u9)IK2m}q#+E+xiKu%=n7CbrD;9m|{h#1-S^7c*B}^t~T| zV)U>AUN(a#!xjr)mP}){*n-Ws^WU)rmBX8Ju5!FmCH#_dCc5+Ln}dw37Unyg93=v;9CnHf{rXO z$36%yEysw@yujR8#9K4_mbt!hrY;d%fXfyHAx6RX8iKSvd%S}3sMYGC$~V4KA5<=k zSN=pGJG2*t z(X_F^#9d!6g!aC-NS1}Ojh+-ODusV)cYyR8x4Rd6rkH*9=-{`MuI z4Kak7_P!lvIZGtd6;8J1<$$GY6sWy!Hm4RH&zy#--L>x2=z+k>0vN5v5UWB4DPBSf)7x(gEq&rKpFXa z5w$120Z>Mm{ijvSETLzr(DmUi5QP=y{@V|tuguc>kF6WpWxInlVU}9PebkEAn1@=s z;IeY=!t>cOA^RtFK?`7_6wd;7tvR+Jw7i_*tUsSE1J-}ID`)^TVq60R4GsQ@R}0S; zc2;{rZ4f+%4~|!!hxo^f&0+a9cR~7W8S%=GcLr@(%eMiR`oki0Y(sF}IleTzBxL@i zMmz%WV)PI`UVqMApgvm!toz}PpaIZF;zoe5Pmh1kZtmB9xIG9i2tPrepSxInwle08Im?6T=czAvYyR!uTHM`kfsaK{dShSQrK^#~)?iz2lC7LWy6d{~wBMv1NqZUg z|1V4Zzfyk$`~Q!o?15Y4Ll__o5C#YXgaN_;VSq3|82ENG(0mAb+*#2}D!J=YXJc)z zYRlUP0SPbO)M_vlbx_Z$cg8`9N0)q)2SGX|*m{{A4qDn5X4 zygOrr$~~>(SrDubnK3|>)f)s@&CdcJtKw${J6q8XeKXt?+*AI6C%k@u>jv(@Wsw=X z*zNea;7lRiJbnxY6?=df#`wM9$?8lo-~9j~3SYpgVc{IwHyU0| z(D%>b=hvKPoT0Y?Ceucvk^444uut@!_grGyL(e$5?;mEM{ry+pb55n6`;tLSy>Vkf zUeI{4ZMEMke$O$LdfwZF$R6RaovHXM1OYeiZhEg}O7)zLmwDDdoBHpSpz;5!vI{cZ zPjyFi-_>c;{tKJ|xHD~m_7~dI+TGg4slQ4+pZX3w3;4&BD=F`%q$mGV^37x_`DKk? zb4O#-yrTYR^*wcqdV}hBs)s7Cs!;jglq1SR%8f8j;Av7@QhAa>@mGqYifTo2;@>3p zC2mjD!Hj`Z3AG6e<-e4lk-s6=%l-l2qd%5cQ_&5 zLqt4o@KkfQ1nCs>dI{3W<}3-)>e5#@(wZ2xVT{X4OJ+nkUd|Oz$E}kfzI>%+tpssp zy5(gF;z=)B)<_Uvw!-{UL_FSjQ>2<#OOQ@6XGoAvHm{N(t+p5<(s75(g5{QU3E~Tv zSzeSN{=!nrN(ti7D6fzruCgqbAfBeTER!IvTWnb>L44jK%MuCV&>_)F5m%ZQN5tbU zZAqnzIMQ*}kqXNT5#hK4Ucb;L_BV~8U*Od~)lU_6@ zam3XzdOk5OCn!o4VQGy#PH9DH;>?8;OA}@;oUlNF{~uB>roNl{V#@!WawDZN<>ll+^5@C+Ut~69z_Rm~g z0phJQ7hkdRC5u;rI6TPkNDzlM#4SM_+WrqDh(j&zk{}LU2d4yamBQkXAig-!+!7Iw zx971Uq14WijXz ze?KA~FSxSI@?JzbUT}rsJLY#I;&FrLn!hJOI>-Eu1nCXtwG);Zum0AYYIKo}ql5C#YXgaN{U%+fBCFxuX7Xv7i) z%?G#qcvciu96(OJe1{(R(m(Ktf9w%`=Q4A42<`KJ{3tzs#y4{8oAKk!SDyza9@Cc} zz&7m6SHtMa~1ar*Kd=14z%^ETc6Szus-emLU4FgAIi>o;wO=_emUde8%VLHbV| z^ItyZ8|n3pc1@n2@QwG<560mO0MU=XW_o)AM?a^Bzd(rxzNC*#(0AMYV;3N?Z|p3n zg1>ZsH*Fu*r*0A@a)@FLPYa*Vk$0C&wfu1#jwCV^SZ{V~WMzyyY`PG0N>q{+4p zr0B{3vJyFx(~{{!Fn5cBZMb#PJ}<<3N=jf~J^qpX>2u>U>E;?)zr(Tnth)AaD8 z$)^_r6Sv?BK%z5Qo=yNa{wL>v8@Pz<>*))O-UabU{_t&da0*b}f75?y#5Zw0ECcd% z=RLZ!10;*`j9_&>bGh4Ognip#FIGqvIB^&DWu-qlP9OUSzOSowwzS$?Jq9?V&j9-X zwt6zbUbJJ>#Use5w&UzsF*m_>`aYZ(bCvEqLXTes7~j|lf6rs)(Ip51pp-s33L*mX zQ4a2(r0-tcC|C$6@_Q^M&ES z$tNg#5aOX6f?vQpzLB&3(a({KVg1s%&mggH^cdzWj71MT34C!K#puK9%;g?>=rQDr z8T`us*{J`*6Sx8!g7}`C@ps%}+Q&f^gbLGh43@`1zI-F#x`)2;Yk{Fha0{Y*^6;lv z5Q3Y&em8J^0K?J!ea!t&aZ1G7=&?ZGNA&5FpuvCmF&GBBT+;pLn0q$@6UgWOv3>}5 zkc9;FwJzr5C%%zBW~4LlpdE4we?bHlIruwvm3cS@=!oYN53mS2P+_cXa+EEWKy~Lm z-v}xv!IFS~gD~BH+TZ;(5a%DdPTxe)hyxu5v~L7mnLPgxw*CoshifLH~}=u^Pm=O}Rq9<?u^jhCt5{J@_?!>u8uE`sQaC7}@vm`s8V-5@374QQ!DQkoVs`Mt6@v z(S>AC7$+|v=Ag#JjzY!_o$-yHn!I!ejAQx+Aw+}Ofc*5fF_(v+5J9d$3X23L&Z8m# z96dcp_xDbAeFUDQ2af|Cbl*`xp-+rKIDj@>V1p5H`YH}Ny8n2fM=3Gi@IsYaVoA)lBahx^n=o`+IJn>(D;11uTrjoPNrN z?jS_t}C!>gkxEd!jV1*T8I&dVFZNrKlvDv;RFyta7I$N zMxY12hLEApqQ)JXX3&6&eNP{A<`fo(k_gpAgarTq%OD-oHU`w9X6e{C-8lwrJT3yv zmk`o7Uv|`cT&48WPLvY32PgD*pj6J_$S;5J_w)0%5EgaXE6!H`WL75EvYR-MGWp zkvxS#CeKWuP82j^NFVAWV0kP92*~7{s2Kt6s2q3SMGj(f6TEx58-f^J?D?2Edk*?( zKp1oOKI&-nd`5q9Cc=F01l@ZnaJ+-Q^axC$SwTPtT*O&NC!8^dDxU{Xd7z#WT=b81 z`$kWo+7EJd^D}fMs`%oz7a7m?<=FnmF_Ekyy%0TD+MuC?o(?w~^r>HXM$ z?GdPp?)4xmub{3JDiUnx60U%cf~^y{I}`>e=GcB@F!KXvp+N(ius0F(P@iuU+URlL z;}i7oLny?k;|COjm)WWX$}~#<1Zo}Bfk9!-*0*dLVRsDi^`sw+!TJM((Eou(QkV?d zF^Ek1i!Z=8kT`JRiT}vg%=wS$!EV&UJk7S(eUDHZ*8LgXe+rz3{M3650yS&~3T&u0 zLiHKs1$P^9B*9O_Qcs(2Yyef%5V6pYzY>&BUVvJ*OT;7n-53|K%~mly ze?fP!>oWAMK|`?Wh9%(sc4%6GuP^A6$5HAt&`6BpK10}gxDItAoVkRy>AQW5Y5yAf zaG-^4U`J7FggOAIgMqugDE+A?&}^Vwp+-1HnyV-1D{aUDY&|SU2F)yVV{nupLPH)6 zRukP_bPq&8sKa#f2;B#ax1o9nTy-2Z;}8e_Q5aD;TfKV|@*Q*SC>%2(whlmK z;RAaAzf{&G)9p*HTBo2WhtK||2DZ!GpaGDovNp*mz2Fpze{>UF|No+?1q#6 z>g1zx1BlGxr(M5vfo%S^rE0mXw6qkq`B#_9GzSlw%BnV3#m1TEauH=fJw`XzJLGX0#jZ1J46Y|)&x z?1edN*}^$%*@8K1+59 zb%88>n^7%a$TsvEd8tet-g_%=b6Le^Q(<9oQDs?qk*OfRIKQ~0sL)hUSXEhGP`Ej0 zbQ}9@@MIT#>ii$Q#)24_@m)@ik-S}>GFN@CPoA@uY38bB+3E$dMd9+2&W0b_l`FTT zcynQSMP+`W$yk|Jlvhz!S(RT^oM$RD6_w|2F60&$M~Uk{B!c3@TK<*M!Qq z2KvQnuMJL)D=#-zhNPTc0#nEnBWwAX~~G?x^u#W-bhQ(i2f32@S^J6f+1qbmY%;wxKk3+w8O?^y3No zRBx=9Tbh81g`QU|>{h4UHl2xVqS>)9mTl|h3uG(9>}9d+0qlpc+ho&p=E2KIdi>h# z*oSqYrx71Nr0?H~VJ;f~r>PFfbbHhOHqE3xpZcGvjVb>}N^bI5&HvEU!^wY{aFcup z1B3y>0AYYIKp2=E2HLaNqCQL(nhuB_d9y>2*R2PdZ6$@9tE%!gR~8xb^9qU!3yRA( zmlqcs%kry?h54rPDmIY;j~&nuI6Qog(Zt7hc|nZi?JHh}-t_D9L-xwiBl?n(vI-a> zmBC=7qP()a&{S1XQdm`#Yua428El0o4<)l~UaY=RyME1F?d Date: Sun, 2 Apr 2023 16:01:03 +0800 Subject: [PATCH 15/84] Update SplitDemoController.cs --- Blog.Core.Api/Controllers/SplitDemoController.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Blog.Core.Api/Controllers/SplitDemoController.cs b/Blog.Core.Api/Controllers/SplitDemoController.cs index fb3c03c6..f625b202 100644 --- a/Blog.Core.Api/Controllers/SplitDemoController.cs +++ b/Blog.Core.Api/Controllers/SplitDemoController.cs @@ -77,7 +77,7 @@ public async Task> GetById(long id) public async Task> Post([FromBody] SplitDemo splitDemo) { var data = new MessageModel(); - unitOfWorkManage.BeginTran(); + //unitOfWorkManage.BeginTran(); var id = (await splitDemoServices.AddSplit(splitDemo)); data.success = (id == null ? false : true); try @@ -98,10 +98,10 @@ public async Task> Post([FromBody] SplitDemo splitDemo) } finally { - if (data.success) - unitOfWorkManage.CommitTran(); - else - unitOfWorkManage.RollbackTran(); + //if (data.success) + // unitOfWorkManage.CommitTran(); + //else + // unitOfWorkManage.RollbackTran(); } return data; } From 45d538010be8dc826b128a38bad9ff9c91d429b3 Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Sun, 2 Apr 2023 17:33:59 +0800 Subject: [PATCH 16/84] =?UTF-8?q?=E2=9C=A8=20=E6=9A=82=E6=97=B6=E5=85=88?= =?UTF-8?q?=E5=8F=96=E6=B6=88Log?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Api/appsettings.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Blog.Core.Api/appsettings.json b/Blog.Core.Api/appsettings.json index 3c117375..22b8e84d 100644 --- a/Blog.Core.Api/appsettings.json +++ b/Blog.Core.Api/appsettings.json @@ -57,7 +57,7 @@ "Enabled": false }, "LogToDB": { - "Enabled": true + "Enabled": false } }, "TranAOP": { @@ -212,7 +212,7 @@ "Enabled": false }, "LogToDB": { - "Enabled": true + "Enabled": false } }, "IPLog": { @@ -221,7 +221,7 @@ "Enabled": false }, "LogToDB": { - "Enabled": true + "Enabled": false } }, "RecordAccessLogs": { @@ -230,7 +230,7 @@ "Enabled": false }, "LogToDB": { - "Enabled": true + "Enabled": false }, "IgnoreApis": "/api/permission/getnavigationbar,/api/monitor/getids4users,/api/monitor/getaccesslogs,/api/monitor/server,/api/monitor/getactiveusers,/api/monitor/server," }, From 8cb4df9ac0a7a8ec43b1e2d059a26b57edb3f018 Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Thu, 6 Apr 2023 21:43:18 +0800 Subject: [PATCH 17/84] =?UTF-8?q?=E2=9C=A8=20git=20ignore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index fa4a3448..de419c90 100644 --- a/.gitignore +++ b/.gitignore @@ -355,7 +355,6 @@ Blog.Core/WMBlog.db Blog.Core/Blog.Core*.xml Blog.Core.Api/WMBlog.db Blog.Core.Api/wwwroot/ui/ +Blog.Core.Api/Logs *.db /Blog.Core.Api/WMBlog.db-journal -Logs -*.db From 7c4b76aeea437eec708ff7083c78cd9ab53669ae Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Thu, 6 Apr 2023 22:40:05 +0800 Subject: [PATCH 18/84] =?UTF-8?q?=E2=9C=A8=20Serilog=20log=20to=20db?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Api/appsettings.json | 5 +- Blog.Core.Common/DB/BaseDBConfig.cs | 4 +- .../LogHelper/LogContextExtension.cs | 2 +- Blog.Core.Common/Seed/DBSeed.cs | 56 ++++++++ .../HostedService/SeedDataHostedService.cs | 3 + .../ServiceExtensions/SerilogSetup.cs | 5 +- .../ServiceExtensions/SqlsugarSetup.cs | 16 ++- Blog.Core.Model/Base/BaseLog.cs | 22 +++ Blog.Core.Model/Logs/AuditSqlLog.cs | 12 ++ Blog.Core.Model/Logs/GlobalErrorLog.cs | 13 ++ Blog.Core.Model/Logs/GlobalInformationLog.cs | 12 ++ Blog.Core.Model/Logs/GlobalWarningLog.cs | 12 ++ Blog.Core.Serilog/Blog.Core.Serilog.csproj | 4 + .../LogBatchingSinkConfiguration.cs | 31 ++++ .../LoggerConfigurationExtensions.cs | 40 +----- Blog.Core.Serilog/Sink/LogBatchingSink.cs | 135 ++++++++++++++++++ 16 files changed, 320 insertions(+), 52 deletions(-) create mode 100644 Blog.Core.Model/Base/BaseLog.cs create mode 100644 Blog.Core.Model/Logs/AuditSqlLog.cs create mode 100644 Blog.Core.Model/Logs/GlobalErrorLog.cs create mode 100644 Blog.Core.Model/Logs/GlobalInformationLog.cs create mode 100644 Blog.Core.Model/Logs/GlobalWarningLog.cs create mode 100644 Blog.Core.Serilog/Configuration/LogBatchingSinkConfiguration.cs create mode 100644 Blog.Core.Serilog/Sink/LogBatchingSink.cs diff --git a/Blog.Core.Api/appsettings.json b/Blog.Core.Api/appsettings.json index c712054c..98f6d4b0 100644 --- a/Blog.Core.Api/appsettings.json +++ b/Blog.Core.Api/appsettings.json @@ -45,10 +45,11 @@ "MemoryCachingAOP": { "Enabled": true }, + "LogToDb": true, "LogAOP": { "Enabled": true, "LogToFile": { - "Enabled": false + "Enabled": true }, "LogToDB": { "Enabled": true @@ -63,7 +64,7 @@ "Enabled": true }, "LogToDB": { - "Enabled": false + "Enabled": true }, "LogToConsole": { "Enabled": true diff --git a/Blog.Core.Common/DB/BaseDBConfig.cs b/Blog.Core.Common/DB/BaseDBConfig.cs index d8c3ee50..5761d91d 100644 --- a/Blog.Core.Common/DB/BaseDBConfig.cs +++ b/Blog.Core.Common/DB/BaseDBConfig.cs @@ -12,7 +12,9 @@ public class BaseDBConfig * 目前是多库操作,默认加载的是appsettings.json设置为true的第一个db连接。 */ public static (List allDbs, List slaveDbs) MutiConnectionString => MutiInitConn(); - public static ConnectionConfig LogConfig; //日志库 + public static List AllConfig=new(); //所有的库连接 + public static List ValidConfig=new(); //有效的库连接(除去Log库) + public static ConnectionConfig LogConfig; //日志库 private static string DifDBConnOfSecurity(params string[] conn) { diff --git a/Blog.Core.Common/LogHelper/LogContextExtension.cs b/Blog.Core.Common/LogHelper/LogContextExtension.cs index bce80cbb..573ba057 100644 --- a/Blog.Core.Common/LogHelper/LogContextExtension.cs +++ b/Blog.Core.Common/LogHelper/LogContextExtension.cs @@ -24,7 +24,7 @@ public IDisposable SqlAopPushProperty(ISqlSugarClient db) AddStock(LogContext.PushProperty(LogContextStatic.SqlOutToFile, AppSettings.app(new string[] { "AppSettings", "SqlAOP", "LogToFile", "Enabled" }).ObjToBool())); AddStock(LogContext.PushProperty(LogContextStatic.OutToDb, - AppSettings.app(new string[] { "AppSettings", "SqlAOP", "LogToDb", "Enabled" }).ObjToBool())); + AppSettings.app(new string[] { "AppSettings", "SqlAOP", "LogToDB", "Enabled" }).ObjToBool())); AddStock(LogContext.PushProperty(LogContextStatic.SugarActionType, db.SugarActionType)); diff --git a/Blog.Core.Common/Seed/DBSeed.cs b/Blog.Core.Common/Seed/DBSeed.cs index 7b594109..5a86f933 100644 --- a/Blog.Core.Common/Seed/DBSeed.cs +++ b/Blog.Core.Common/Seed/DBSeed.cs @@ -8,11 +8,13 @@ using SqlSugar; using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; +using Blog.Core.Common.Const; namespace Blog.Core.Common.Seed { @@ -426,6 +428,56 @@ private static async Task SeedDataAsync(ISqlSugarClient db) } } + /// + /// 迁移日志数据库 + /// + /// + public static void MigrationLogs(MyContext myContext) + { + // 创建数据库表,遍历指定命名空间下的class, + // 注意不要把其他命名空间下的也添加进来。 + Console.WriteLine("Create Log Tables..."); + if (!myContext.Db.IsAnyConnection(SqlSugarConst.LogConfigId.ToLower())) + { + throw new ApplicationException("未配置日志数据库,请在appsettings.json中DBS节点中配置"); + } + + var logDb = myContext.Db.GetConnection(SqlSugarConst.LogConfigId.ToLower()); + + var path = AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory; + var referencedAssemblies = System.IO.Directory.GetFiles(path, "Blog.Core.Model.dll").Select(Assembly.LoadFrom).ToArray(); + var modelTypes = referencedAssemblies + .SelectMany(a => a.DefinedTypes) + .Select(type => type.AsType()) + .Where(x => x.IsClass && x.Namespace != null && x.Namespace.StartsWith("Blog.Core.Model.Logs")).ToList(); + Stopwatch sw = Stopwatch.StartNew(); + + var tables = logDb.DbMaintenance.GetTableInfoList(); + + modelTypes.ForEach(t => + { + // 这里只支持添加修改表,不支持删除 + // 如果想要删除,数据库直接右键删除,或者联系SqlSugar作者; + if (!tables.Any(s => s.Name.Contains(t.Name))) + { + Console.WriteLine(t.Name); + if (t.GetCustomAttribute() != null) + { + logDb.CodeFirst.SplitTables().InitTables(t); + } + else + { + logDb.CodeFirst.InitTables(t); + } + } + }); + + sw.Stop(); + + $"Log Tables created successfully! {sw.ElapsedMilliseconds}ms".WriteSuccessLine(); + Console.WriteLine(); + } + /// /// 初始化 多租户 @@ -523,6 +575,8 @@ public static async Task InitTenantSeedAsync(ITenant itenant, ConnectionConfig c #endregion + #region 多租户 种子数据 初始化 + private static async Task TenantSeedDataAsync(ISqlSugarClient db, TenantTypeEnum tenantType) { // 获取所有种子配置-初始化数据 @@ -578,5 +632,7 @@ private static async Task TenantSeedDataAsync(ISqlSugarClient db, TenantTypeEnum } } } + + #endregion } } \ No newline at end of file diff --git a/Blog.Core.Extensions/HostedService/SeedDataHostedService.cs b/Blog.Core.Extensions/HostedService/SeedDataHostedService.cs index 868d750e..e1da3d69 100644 --- a/Blog.Core.Extensions/HostedService/SeedDataHostedService.cs +++ b/Blog.Core.Extensions/HostedService/SeedDataHostedService.cs @@ -39,6 +39,9 @@ private async Task DoWork() { await DBSeed.SeedAsync(_myContext, _webRootPath); + //日志 + DBSeed.MigrationLogs(_myContext); + //多租户 同步 await DBSeed.TenantSeedAsync(_myContext); } diff --git a/Blog.Core.Extensions/ServiceExtensions/SerilogSetup.cs b/Blog.Core.Extensions/ServiceExtensions/SerilogSetup.cs index a112409c..0d8a0768 100644 --- a/Blog.Core.Extensions/ServiceExtensions/SerilogSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/SerilogSetup.cs @@ -1,5 +1,6 @@ using Blog.Core.Common; using Blog.Core.Common.LogHelper; +using Blog.Core.Serilog.Configuration; using Blog.Core.Serilog.Extensions; using Microsoft.Extensions.Hosting; using Serilog; @@ -21,9 +22,9 @@ public static IHostBuilder AddSerilogSetup(this IHostBuilder host) //输出到控制台 .WriteToConsole() //将日志保存到文件中 - .WriteToFile(); + .WriteToFile() //配置日志库 - //.WriteToLogBatching(); + .WriteToLogBatching(); Log.Logger = loggerConfiguration.CreateLogger(); diff --git a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs index 3440b8af..98dd6e24 100644 --- a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs @@ -33,8 +33,6 @@ public static void AddSqlsugarSetup(this IServiceCollection services) { var memoryCache = o.GetRequiredService(); - // 连接字符串 - var listConfig = new List(); // 从库 var listConfig_Slave = new List(); BaseDBConfig.MutiConnectionString.slaveDbs.ForEach(s => @@ -77,12 +75,16 @@ public static void AddSqlsugarSetup(this IServiceCollection services) }, InitKeyType = InitKeyType.Attribute }; - if (SqlSugarConst.LogConfigId.Equals(m.ConnId)) + if (SqlSugarConst.LogConfigId.ToLower().Equals(m.ConnId.ToLower())) { BaseDBConfig.LogConfig = config; } + else + { + BaseDBConfig.ValidConfig.Add(config); + } - listConfig.Add(config); + BaseDBConfig.AllConfig.Add(config); }); if (BaseDBConfig.LogConfig is null) @@ -90,14 +92,14 @@ public static void AddSqlsugarSetup(this IServiceCollection services) throw new ApplicationException("未配置Log库连接"); } - return new SqlSugarScope(listConfig, db => + return new SqlSugarScope(BaseDBConfig.AllConfig, db => { - listConfig.ForEach(config => + BaseDBConfig.ValidConfig.ForEach(config => { var dbProvider = db.GetConnectionScope((string)config.ConfigId); // 打印SQL语句 - dbProvider.Aop.OnLogExecuting = (s, parameters) => SqlSugarAop.OnLogExecuting(dbProvider,s, parameters, config); + dbProvider.Aop.OnLogExecuting = (s, parameters) => SqlSugarAop.OnLogExecuting(dbProvider, s, parameters, config); // 数据审计 dbProvider.Aop.DataExecuting = SqlSugarAop.DataExecuting; diff --git a/Blog.Core.Model/Base/BaseLog.cs b/Blog.Core.Model/Base/BaseLog.cs new file mode 100644 index 00000000..829ff09e --- /dev/null +++ b/Blog.Core.Model/Base/BaseLog.cs @@ -0,0 +1,22 @@ +using SqlSugar; +using System; + +namespace Blog.Core.Model.Base; + +public abstract class BaseLog : RootEntityTkey +{ + [SplitField] + public DateTime? DateTime { get; set; } + + [SugarColumn(IsNullable = true)] + public string Level { get; set; } + + [SugarColumn(IsNullable = true, ColumnDataType = "longtext,text,clob")] + public string Message { get; set; } + + [SugarColumn(IsNullable = true, ColumnDataType = "longtext,text,clob")] + public string MessageTemplate { get; set; } + + [SugarColumn(IsNullable = true, ColumnDataType = "longtext,text,clob")] + public string Properties { get; set; } +} \ No newline at end of file diff --git a/Blog.Core.Model/Logs/AuditSqlLog.cs b/Blog.Core.Model/Logs/AuditSqlLog.cs new file mode 100644 index 00000000..a6bd4e23 --- /dev/null +++ b/Blog.Core.Model/Logs/AuditSqlLog.cs @@ -0,0 +1,12 @@ +using Blog.Core.Model.Base; +using SqlSugar; + +namespace Blog.Core.Model.Logs; + +[Tenant("log")] +[SplitTable(SplitType.Month)] //按年分表 (自带分表支持 年、季、月、周、日) +[SugarTable($@"{nameof(AuditSqlLog)}_{{year}}{{month}}{{day}}")] +public class AuditSqlLog: BaseLog +{ + +} \ No newline at end of file diff --git a/Blog.Core.Model/Logs/GlobalErrorLog.cs b/Blog.Core.Model/Logs/GlobalErrorLog.cs new file mode 100644 index 00000000..08172eae --- /dev/null +++ b/Blog.Core.Model/Logs/GlobalErrorLog.cs @@ -0,0 +1,13 @@ +using Blog.Core.Model.Base; +using SqlSugar; + +namespace Blog.Core.Model.Logs; + +[Tenant("log")] +[SplitTable(SplitType.Month)] //按年分表 (自带分表支持 年、季、月、周、日) +[SugarTable($@"{nameof(GlobalErrorLog)}_{{year}}{{month}}{{day}}")] +public class GlobalErrorLog : BaseLog +{ + [SugarColumn(IsNullable = true, ColumnDataType = "longtext,text,clob")] + public string Exception { get; set; } +} \ No newline at end of file diff --git a/Blog.Core.Model/Logs/GlobalInformationLog.cs b/Blog.Core.Model/Logs/GlobalInformationLog.cs new file mode 100644 index 00000000..14873eed --- /dev/null +++ b/Blog.Core.Model/Logs/GlobalInformationLog.cs @@ -0,0 +1,12 @@ +using Blog.Core.Model.Base; +using SqlSugar; + +namespace Blog.Core.Model.Logs; + +[Tenant("log")] +[SplitTable(SplitType.Month)] //按年分表 (自带分表支持 年、季、月、周、日) +[SugarTable($@"{nameof(GlobalInformationLog)}_{{year}}{{month}}{{day}}")] +public class GlobalInformationLog : BaseLog +{ + +} \ No newline at end of file diff --git a/Blog.Core.Model/Logs/GlobalWarningLog.cs b/Blog.Core.Model/Logs/GlobalWarningLog.cs new file mode 100644 index 00000000..7f4fb945 --- /dev/null +++ b/Blog.Core.Model/Logs/GlobalWarningLog.cs @@ -0,0 +1,12 @@ +using Blog.Core.Model.Base; +using SqlSugar; + +namespace Blog.Core.Model.Logs; + +[Tenant("log")] +[SplitTable(SplitType.Month)] //按年分表 (自带分表支持 年、季、月、周、日) +[SugarTable($@"{nameof(GlobalWarningLog)}_{{year}}{{month}}{{day}}")] +public class GlobalWarningLog: BaseLog +{ + +} \ No newline at end of file diff --git a/Blog.Core.Serilog/Blog.Core.Serilog.csproj b/Blog.Core.Serilog/Blog.Core.Serilog.csproj index 07fa26f3..8df8f16f 100644 --- a/Blog.Core.Serilog/Blog.Core.Serilog.csproj +++ b/Blog.Core.Serilog/Blog.Core.Serilog.csproj @@ -6,6 +6,10 @@ enable + + + + diff --git a/Blog.Core.Serilog/Configuration/LogBatchingSinkConfiguration.cs b/Blog.Core.Serilog/Configuration/LogBatchingSinkConfiguration.cs new file mode 100644 index 00000000..65aacde7 --- /dev/null +++ b/Blog.Core.Serilog/Configuration/LogBatchingSinkConfiguration.cs @@ -0,0 +1,31 @@ +using Blog.Core.Common; +using Blog.Core.Serilog.Sink; +using Serilog; +using Serilog.Sinks.PeriodicBatching; + +namespace Blog.Core.Serilog.Configuration; + +public static class LogBatchingSinkConfiguration +{ + public static LoggerConfiguration WriteToLogBatching(this LoggerConfiguration loggerConfiguration) + { + if (!AppSettings.app("AppSettings", "LogToDb").ObjToBool()) + { + return loggerConfiguration; + } + + var exampleSink = new LogBatchingSink(); + + var batchingOptions = new PeriodicBatchingSinkOptions + { + BatchSizeLimit = 500, + Period = TimeSpan.FromSeconds(1), + EagerlyEmitFirstEvent = true, + QueueLimit = 10000 + }; + + var batchingSink = new PeriodicBatchingSink(exampleSink, batchingOptions); + + return loggerConfiguration.WriteTo.Sink(batchingSink); + } +} \ No newline at end of file diff --git a/Blog.Core.Serilog/Extensions/LoggerConfigurationExtensions.cs b/Blog.Core.Serilog/Extensions/LoggerConfigurationExtensions.cs index 2736aa1f..d70616b4 100644 --- a/Blog.Core.Serilog/Extensions/LoggerConfigurationExtensions.cs +++ b/Blog.Core.Serilog/Extensions/LoggerConfigurationExtensions.cs @@ -9,45 +9,6 @@ namespace Blog.Core.Serilog.Extensions; public static class LoggerConfigurationExtensions { - public static LoggerConfiguration WriteToSqlServer(this LoggerConfiguration loggerConfiguration) - { - var logConnectionStrings = AppSettings.app("LogConnectionStrings"); - if (logConnectionStrings.IsNullOrEmpty()) return loggerConfiguration; - - //输出SQL - //loggerConfiguration = loggerConfiguration.WriteTo.Logger(lg => - // lg.FilterSqlLog().WriteTo.MSSqlServer(logConnectionStrings, new MSSqlServerSinkOptions() - // { - // TableName = "SqlLog", - // AutoCreateSqlTable = true - // })); - - //输出普通日志 - //loggerConfiguration = loggerConfiguration.WriteTo.Logger(lg => - // lg.FilterRemoveSqlLog().Filter.ByIncludingOnly(p => p.Level >= LogEventLevel.Error) - // .WriteTo.MSSqlServer(logConnectionStrings, new MSSqlServerSinkOptions() - // { - // TableName = "ErrorLog", - // AutoCreateSqlTable = true - // })); - //loggerConfiguration = loggerConfiguration.WriteTo.Logger(lg => - // lg.FilterRemoveSqlLog().Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Warning) - // .WriteTo.MSSqlServer(logConnectionStrings, new MSSqlServerSinkOptions() - // { - // TableName = "WarningLog", - // AutoCreateSqlTable = true - // })); - //loggerConfiguration = loggerConfiguration.WriteTo.Logger(lg => - // lg.FilterRemoveSqlLog().Filter.ByIncludingOnly(p => p.Level <= LogEventLevel.Information) - // .WriteTo.MSSqlServer(logConnectionStrings, new MSSqlServerSinkOptions() - // { - // TableName = "InformationLog", - // AutoCreateSqlTable = true - // })); - - return loggerConfiguration; - } - public static LoggerConfiguration WriteToConsole(this LoggerConfiguration loggerConfiguration) { //输出普通日志 @@ -84,6 +45,7 @@ public static LoggerConfiguration FilterSqlLog(this LoggerConfiguration lc) public static IEnumerable FilterSqlLog(this IEnumerable batch) { + //只记录 Insert、Update、Delete语句 return batch.Where(s => s.WithProperty(LogContextStatic.LogSource, q => LogContextStatic.AopSql.Equals(q))) .Where(s => s.WithProperty(LogContextStatic.SugarActionType, q => !new[] { SugarActionType.UnKnown, SugarActionType.Query }.Contains(q))); diff --git a/Blog.Core.Serilog/Sink/LogBatchingSink.cs b/Blog.Core.Serilog/Sink/LogBatchingSink.cs new file mode 100644 index 00000000..0a6e1223 --- /dev/null +++ b/Blog.Core.Serilog/Sink/LogBatchingSink.cs @@ -0,0 +1,135 @@ +using Blog.Core.Common; +using Blog.Core.Model.Logs; +using Blog.Core.Serilog.Extensions; +using Mapster; +using Serilog.Events; +using Serilog.Sinks.PeriodicBatching; +using SqlSugar; + +namespace Blog.Core.Serilog.Sink; + +public class LogBatchingSink : IBatchedLogEventSink +{ + public async Task EmitBatchAsync(IEnumerable batch) + { + var sugar = App.GetService(false); + + await WriteSqlLog(sugar, batch.FilterSqlLog()); + await WriteLogs(sugar, batch.FilterRemoveOtherLog()); + } + + public Task OnEmptyBatchAsync() + { + return Task.CompletedTask; + } + + #region Write Log + + private async Task WriteLogs(ISqlSugarClient db, IEnumerable batch) + { + if (!batch.Any()) + { + return; + } + + var group = batch.GroupBy(s => s.Level); + foreach (var v in group) + { + switch (v.Key) + { + case LogEventLevel.Information: + await WriteInformationLog(db, v); + break; + case LogEventLevel.Warning: + await WriteWarningLog(db, v); + break; + case LogEventLevel.Error: + case LogEventLevel.Fatal: + await WriteErrorLog(db, v); + break; + } + } + } + + private async Task WriteInformationLog(ISqlSugarClient db, IEnumerable batch) + { + if (!batch.Any()) + { + return; + } + + var logs = new List(); + foreach (var logEvent in batch) + { + var log = logEvent.Adapt(); + log.Message = logEvent.RenderMessage(); + log.Properties = logEvent.Properties.ToJson(); + log.DateTime = logEvent.Timestamp.DateTime; + logs.Add(log); + } + + await db.AsTenant().InsertableWithAttr(logs).SplitTable().ExecuteReturnSnowflakeIdAsync(); + } + + private async Task WriteWarningLog(ISqlSugarClient db, IEnumerable batch) + { + if (!batch.Any()) + { + return; + } + + var logs = new List(); + foreach (var logEvent in batch) + { + var log = logEvent.Adapt(); + log.Message = logEvent.RenderMessage(); + log.Properties = logEvent.Properties.ToJson(); + log.DateTime = logEvent.Timestamp.DateTime; + logs.Add(log); + } + + await db.AsTenant().InsertableWithAttr(logs).SplitTable().ExecuteReturnSnowflakeIdAsync(); + } + + private async Task WriteErrorLog(ISqlSugarClient db, IEnumerable batch) + { + if (!batch.Any()) + { + return; + } + + var logs = new List(); + foreach (var logEvent in batch) + { + var log = logEvent.Adapt(); + log.Message = logEvent.RenderMessage(); + log.Properties = logEvent.Properties.ToJson(); + log.DateTime = logEvent.Timestamp.DateTime; + logs.Add(log); + } + + await db.AsTenant().InsertableWithAttr(logs).SplitTable().ExecuteReturnSnowflakeIdAsync(); + } + + private async Task WriteSqlLog(ISqlSugarClient db, IEnumerable batch) + { + if (!batch.Any()) + { + return; + } + + var logs = new List(); + foreach (var logEvent in batch) + { + var log = logEvent.Adapt(); + log.Message = logEvent.RenderMessage(); + log.Properties = logEvent.Properties.ToJson(); + log.DateTime = logEvent.Timestamp.DateTime; + logs.Add(log); + } + + await db.AsTenant().InsertableWithAttr(logs).SplitTable().ExecuteReturnSnowflakeIdAsync(); + } + + #endregion +} \ No newline at end of file From 36a38694b5396c43f143202175889e78678bef43 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Sat, 8 Apr 2023 19:07:00 +0800 Subject: [PATCH 19/84] feat: change readme --- Blog.Core.Api/Blog.Core.Api.csproj | 1 - README.md | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Blog.Core.Api/Blog.Core.Api.csproj b/Blog.Core.Api/Blog.Core.Api.csproj index 0d32998e..3bf64399 100644 --- a/Blog.Core.Api/Blog.Core.Api.csproj +++ b/Blog.Core.Api/Blog.Core.Api.csproj @@ -52,7 +52,6 @@ - diff --git a/README.md b/README.md index ee36dc2c..a62a2eb6 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ Blog.Core 开箱即用的企业级前后端分离【 .NET Core6.0 Api + Vue 2.x #### 商业授权付费版下🎁🎁🎁 -- [x] 包含下边框架模块中的所有功能; +- [x] 包含开源版 `框架模块/组件模块` 中的所有功能; - [x] 全部表结构主键底层架构改成`string`类型(默认雪花,支持guid),更方便迁移; - [x] 完善部门数据权限,可以基于策略配置查看数据范围; - [x] 优化权限处理器,解决多实例分布式下,权限不同步问题(必须配置Redis); @@ -82,7 +82,7 @@ Blog.Core 开箱即用的企业级前后端分离【 .NET Core6.0 Api + Vue 2.x - [x] 五种日志记录,审计/异常/请求响应/服务操作/Sql记录等,并自动持久化到数据库表🎶; - [x] 支持项目事务处理(若要分布式,用cap即可)✨; - [x] 设计4种 AOP 切面编程,功能涵盖:日志、缓存、审计、事务 ✨; -- [x] Log4net 多种日志自动生成到数据库中,目前支持MySql/SqlServer/Sqlite/Oracle/Postgresql🎉; +- [x] 全局统一封装 Serilog 生成多种日志,并自动生成到数据库中,目前支持MySql/SqlServer/Sqlite/Oracle/Postgresql🎉; - [x] 设计并支持按钮级别的RBAC权限控制,同时支持一键同步接口和菜单 🎶; - [x] 支持 T4 代码模板,自动生成每层代码; - [x] 或使用 DbFirst 一键创建自己项目的四层文件(支持多库); @@ -100,7 +100,7 @@ Blog.Core 开箱即用的企业级前后端分离【 .NET Core6.0 Api + Vue 2.x - [x] 使用 AutoFac 做依赖注入容器,并提供批量服务注入 ✨; - [x] 支持 CORS 跨域; - [x] 封装 JWT 自定义策略授权; -- [x] 使用 Log4Net 日志框架,集成原生 ILogger 接口做日志记录; +- [x] 使用 Serilog 日志框架,集成原生 ILogger 接口做日志记录; - [x] 使用 SignalR 双工通讯 ✨; - [x] 添加 IpRateLimiting 做 API 限流处理; - [x] 使用 Quartz.net 做任务调度(目前单机多任务,集群调度暂不支持); @@ -115,7 +115,7 @@ Blog.Core 开箱即用的企业级前后端分离【 .NET Core6.0 Api + Vue 2.x - [x] 新增 Kafka 消息队列,并配合实现EventBus ✨; - [x] 新增 微信公众号管理,并集成到Blog.Admin后台 ✨; - [x] 新增 - 数据部门权限; -- [x] 新增 - Log4net集成日志数据持久化到数据库; +- [x] 新增 - Serilog 集成日志数据持久化到数据库; - [x] 新增 - 多租户模式(单表,多表,多库三种模式); From cf97167537a431376c5e55990c4a049f1761a109 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Sat, 8 Apr 2023 20:33:42 +0800 Subject: [PATCH 20/84] Update DBSeed.cs --- Blog.Core.Common/Seed/DBSeed.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Blog.Core.Common/Seed/DBSeed.cs b/Blog.Core.Common/Seed/DBSeed.cs index f2795b08..000ff976 100644 --- a/Blog.Core.Common/Seed/DBSeed.cs +++ b/Blog.Core.Common/Seed/DBSeed.cs @@ -445,7 +445,9 @@ public static void MigrationLogs(MyContext myContext) } var logDb = myContext.Db.GetConnection(SqlSugarConst.LogConfigId.ToLower()); - + Console.WriteLine($"Create log Database(The Db Id:{SqlSugarConst.LogConfigId.ToLower()})..."); + logDb.DbMaintenance.CreateDatabase(); + ConsoleHelper.WriteSuccessLine($"Log Database created successfully!"); var path = AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory; var referencedAssemblies = System.IO.Directory.GetFiles(path, "Blog.Core.Model.dll").Select(Assembly.LoadFrom).ToArray(); var modelTypes = referencedAssemblies From b595ac4a1e36be0de394a853340a881a0a49553c Mon Sep 17 00:00:00 2001 From: hudingwen <765472804@qq.com> Date: Sun, 9 Apr 2023 22:46:53 +0800 Subject: [PATCH 21/84] =?UTF-8?q?=E5=90=8C=E6=AD=A5=E4=B8=BB=E5=88=86?= =?UTF-8?q?=E6=94=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 + Blog.Core.Api/Blog.Core.Api.csproj | 5 +- Blog.Core.Api/Blog.Core.Model.xml | 315 +++++++++ Blog.Core.Api/Blog.Core.xml | 201 +++++- Blog.Core.Api/Controllers/BlogController.cs | 16 +- .../Controllers/DbFirst/MigrateController.cs | 30 +- .../Controllers/DepartmentController.cs | 13 +- Blog.Core.Api/Controllers/LoginController.cs | 37 +- Blog.Core.Api/Controllers/ModuleController.cs | 7 +- .../Controllers/MonitorController.cs | 7 - Blog.Core.Api/Controllers/NacosController.cs | 1 - Blog.Core.Api/Controllers/PayController.cs | 4 +- .../Controllers/PermissionController.cs | 158 ++--- Blog.Core.Api/Controllers/RoleController.cs | 5 +- .../Controllers/SplitDemoController.cs | 199 ++++++ .../Controllers/TasksQzController.cs | 85 ++- .../Tenant/TenantByDbController.cs | 50 ++ .../Tenant/TenantByIdController.cs | 49 ++ .../Tenant/TenantByTableController.cs | 57 ++ .../Tenant/TenantManagerController.cs | 87 +++ Blog.Core.Api/Controllers/TopicController.cs | 10 +- .../Controllers/TopicDetailController.cs | 13 +- .../Controllers/TransactionController.cs | 11 +- Blog.Core.Api/Controllers/UserController.cs | 15 +- .../Controllers/UserRoleController.cs | 5 +- Blog.Core.Api/Controllers/ValuesController.cs | 1 - .../Controllers/WeChatCompanyController.cs | 1 - .../Controllers/WeChatConfigController.cs | 1 - Blog.Core.Api/Controllers/WeChatController.cs | 5 +- .../Controllers/WeChatPushLogController.cs | 1 - .../Controllers/WeChatSubController.cs | 1 - Blog.Core.Api/Program.cs | 34 +- Blog.Core.Api/Startup.cs | 43 +- Blog.Core.Api/appsettings.json | 599 +++++++++--------- .../BlogCore.Data.excel/SysUserInfo.xlsx | Bin 11177 -> 11197 bytes .../wwwroot/BlogCore.Data.json/Modules.tsv | 44 ++ .../wwwroot/BlogCore.Data.json/Permission.tsv | 86 ++- .../RoleModulePermission.tsv | 12 +- .../wwwroot/BlogCore.Data.json/TasksQz.tsv | 8 +- Blog.Core.Build.bat | 1 - Blog.Core.Common/App.cs | 19 + Blog.Core.Common/Blog.Core.Common.csproj | 4 + Blog.Core.Common/Core/InternalApp.cs | 17 + Blog.Core.Common/DB/Aop/SqlsugarAop.cs | 130 ++++ Blog.Core.Common/DB/BaseDBConfig.cs | 22 +- Blog.Core.Common/DB/RepositorySetting.cs | 48 ++ Blog.Core.Common/DB/TenantUtil.cs | 102 +++ .../Extensions/AssemblysExtensions.cs | 24 + .../Extensions/ExpressionExtensions.cs | 13 +- .../Extensions/GenericTypeExtensions.cs | 28 +- .../Extensions/UntilExtensions.cs | 18 + Blog.Core.Common/Helper/DynamicLinqFactory.cs | 100 ++- .../Helper/GenericTypeExtensions.cs | 56 ++ Blog.Core.Common/Helper/NumberConverter.cs | 174 +++++ Blog.Core.Common/Helper/RecursionHelper.cs | 16 +- Blog.Core.Common/Helper/SM/SM4.cs | 3 +- Blog.Core.Common/Helper/UtilConvert.cs | 12 + .../HttpContextUser/AspNetUser.cs | 6 +- Blog.Core.Common/HttpContextUser/IUser.cs | 3 +- Blog.Core.Common/Seed/DBSeed.cs | 305 ++++++++- Blog.Core.Common/Seed/IEntitySeedData.cs | 36 ++ .../Seed/SeedData/BusinessDataSeedData.cs | 79 +++ .../SeedData/MultiBusinessDataSeedData.cs | 38 ++ .../SeedData/MultiBusinessSubDataSeedData.cs | 38 ++ .../Seed/SeedData/SubBusinessDataSeedData.cs | 70 ++ .../Seed/SeedData/TenantSeedData.cs | 72 +++ .../Seed/SeedData/UserInfoSeedData.cs | 80 +++ Blog.Core.EventBus/Blog.Core.EventBus.csproj | 2 +- .../Blog.Core.Extensions.csproj | 2 +- .../HostedService/ConsulHostedService.cs | 71 +++ .../HostedService/EventBusHostedService.cs | 45 ++ .../HostedService/QuartzJobHostedService.cs | 67 ++ .../HostedService/SeedDataHostedService.cs | 58 ++ .../Middlewares/RequRespLogMiddleware.cs | 1 - .../ServiceExtensions/EventBusSetup.cs | 21 +- .../InitializationHostServiceSetup.cs | 24 + .../ServiceExtensions/SqlsugarSetup.cs | 17 +- Blog.Core.Gateway/Blog.Core.Gateway.csproj | 6 +- Blog.Core.Gateway/Program.cs | 4 +- .../appsettings.gw.Development.json | 9 + Blog.Core.Gateway/appsettings.gw.json | 103 +++ Blog.Core.IServices/BASE/IBaseServices.cs | 15 +- Blog.Core.IServices/IBlogArticleServices.cs | 2 +- .../IRoleModulePermissionServices.cs | 2 +- Blog.Core.IServices/ISplitDemoServices.cs | 15 + Blog.Core.IServices/ITasksLogServices.cs | 19 + Blog.Core.IServices/ITenantService.cs | 12 + Blog.Core.IServices/IUserRoleServices.cs | 4 +- Blog.Core.Model/Blog.Core.Model.csproj | 6 +- Blog.Core.Model/Models/AccessTrendLog.cs | 2 +- Blog.Core.Model/Models/Advertisement.cs | 2 +- Blog.Core.Model/Models/BlogArticle.cs | 18 +- Blog.Core.Model/Models/BlogArticleComment.cs | 19 + Blog.Core.Model/Models/Department.cs | 2 +- Blog.Core.Model/Models/GblLogAudit.cs | 4 +- Blog.Core.Model/Models/Guestbook.cs | 6 +- Blog.Core.Model/Models/Modules.cs | 2 +- Blog.Core.Model/Models/OperateLog.cs | 2 +- Blog.Core.Model/Models/PasswordLib.cs | 6 +- Blog.Core.Model/Models/Permission.cs | 2 +- Blog.Core.Model/Models/Role.cs | 2 +- .../Models/RoleModulePermission.cs | 2 +- Blog.Core.Model/Models/RootTkey/BaseEntity.cs | 80 +++ .../RootTkey/Interface/IDeleteFilter.cs | 9 + .../Models/RootTkey/RootEntityTkey.cs | 2 - Blog.Core.Model/Models/SplitDemo.cs | 27 + Blog.Core.Model/Models/SysTenant.cs | 67 ++ Blog.Core.Model/Models/TasksLog.cs | 87 +++ Blog.Core.Model/Models/TasksQz.cs | 6 +- .../Models/Tenant/BusinessTable.cs | 27 + .../Models/Tenant/MultiBusinessSubTable.cs | 14 + .../Models/Tenant/MultiBusinessTable.cs | 26 + .../Models/Tenant/SubLibraryBusinessTable.cs | 22 + Blog.Core.Model/Models/TestModels.cs | 6 +- Blog.Core.Model/Models/Topic.cs | 2 +- Blog.Core.Model/Models/TopicDetail.cs | 2 +- Blog.Core.Model/Models/UserRole.cs | 6 +- Blog.Core.Model/Models/WeChatConfig.cs | 4 +- Blog.Core.Model/Models/WeChatPushLog.cs | 2 +- Blog.Core.Model/Models/sysUserInfo.cs | 14 +- Blog.Core.Model/Tenants/ITenantEntity.cs | 15 + .../Tenants/MultiTenantAttribute.cs | 24 + Blog.Core.Model/Tenants/TenantTypeEnum.cs | 29 + Blog.Core.Model/ViewModels/BlogViewModels.cs | 6 +- Blog.Core.Model/ViewModels/SysUserInfoDto.cs | 6 +- Blog.Core.Publish.Linux.sh | 3 +- Blog.Core.Repository/BASE/BaseRepository.cs | 180 ++++-- Blog.Core.Repository/BASE/IBaseRepository.cs | 44 +- .../Blog.Core.Repository.csproj | 6 +- .../IRoleModulePermissionRepository.cs | 2 +- .../RoleModulePermissionRepository.cs | 2 +- Blog.Core.Services/BASE/BaseServices.cs | 96 ++- Blog.Core.Services/BlogArticleServices.cs | 2 +- .../RoleModulePermissionServices.cs | 2 +- Blog.Core.Services/SplitDemoServices.cs | 23 + Blog.Core.Services/TasksLogServices.cs | 137 ++++ Blog.Core.Services/TenantService.cs | 57 ++ Blog.Core.Services/UserRoleServices.cs | 4 +- Blog.Core.Tasks/QuartzNet/Jobs/JobBase.cs | 43 +- .../Jobs/Job_AccessTrendLog_Quartz.cs | 3 +- .../QuartzNet/Jobs/Job_Blogs_Quartz.cs | 6 +- .../QuartzNet/Jobs/Job_OperateLog_Quartz.cs | 6 +- .../QuartzNet/Jobs/Job_Trojan_Quartz.cs | 6 +- .../QuartzNet/Jobs/Job_URL_Quartz.cs | 8 +- .../QuartzNet/SchedulerCenterServer.cs | 6 + .../Common_Test/DynamicLambdaTest.cs | 54 +- .../Controller_Test/BlogController_Should.cs | 2 +- .../DependencyInjection/DI_Test.cs | 35 +- Blog.Core.Tests/Repository_Test/OrmTest.cs | 73 +++ README.md | 17 +- 150 files changed, 4694 insertions(+), 914 deletions(-) create mode 100644 Blog.Core.Api/Controllers/SplitDemoController.cs create mode 100644 Blog.Core.Api/Controllers/Tenant/TenantByDbController.cs create mode 100644 Blog.Core.Api/Controllers/Tenant/TenantByIdController.cs create mode 100644 Blog.Core.Api/Controllers/Tenant/TenantByTableController.cs create mode 100644 Blog.Core.Api/Controllers/Tenant/TenantManagerController.cs create mode 100644 Blog.Core.Common/App.cs create mode 100644 Blog.Core.Common/Core/InternalApp.cs create mode 100644 Blog.Core.Common/DB/Aop/SqlsugarAop.cs create mode 100644 Blog.Core.Common/DB/RepositorySetting.cs create mode 100644 Blog.Core.Common/DB/TenantUtil.cs create mode 100644 Blog.Core.Common/Extensions/AssemblysExtensions.cs create mode 100644 Blog.Core.Common/Extensions/UntilExtensions.cs create mode 100644 Blog.Core.Common/Helper/GenericTypeExtensions.cs create mode 100644 Blog.Core.Common/Helper/NumberConverter.cs create mode 100644 Blog.Core.Common/Seed/IEntitySeedData.cs create mode 100644 Blog.Core.Common/Seed/SeedData/BusinessDataSeedData.cs create mode 100644 Blog.Core.Common/Seed/SeedData/MultiBusinessDataSeedData.cs create mode 100644 Blog.Core.Common/Seed/SeedData/MultiBusinessSubDataSeedData.cs create mode 100644 Blog.Core.Common/Seed/SeedData/SubBusinessDataSeedData.cs create mode 100644 Blog.Core.Common/Seed/SeedData/TenantSeedData.cs create mode 100644 Blog.Core.Common/Seed/SeedData/UserInfoSeedData.cs create mode 100644 Blog.Core.Extensions/HostedService/ConsulHostedService.cs create mode 100644 Blog.Core.Extensions/HostedService/EventBusHostedService.cs create mode 100644 Blog.Core.Extensions/HostedService/QuartzJobHostedService.cs create mode 100644 Blog.Core.Extensions/HostedService/SeedDataHostedService.cs create mode 100644 Blog.Core.Extensions/ServiceExtensions/InitializationHostServiceSetup.cs create mode 100644 Blog.Core.Gateway/appsettings.gw.Development.json create mode 100644 Blog.Core.Gateway/appsettings.gw.json create mode 100644 Blog.Core.IServices/ISplitDemoServices.cs create mode 100644 Blog.Core.IServices/ITasksLogServices.cs create mode 100644 Blog.Core.IServices/ITenantService.cs create mode 100644 Blog.Core.Model/Models/BlogArticleComment.cs create mode 100644 Blog.Core.Model/Models/RootTkey/BaseEntity.cs create mode 100644 Blog.Core.Model/Models/RootTkey/Interface/IDeleteFilter.cs create mode 100644 Blog.Core.Model/Models/SplitDemo.cs create mode 100644 Blog.Core.Model/Models/SysTenant.cs create mode 100644 Blog.Core.Model/Models/TasksLog.cs create mode 100644 Blog.Core.Model/Models/Tenant/BusinessTable.cs create mode 100644 Blog.Core.Model/Models/Tenant/MultiBusinessSubTable.cs create mode 100644 Blog.Core.Model/Models/Tenant/MultiBusinessTable.cs create mode 100644 Blog.Core.Model/Models/Tenant/SubLibraryBusinessTable.cs create mode 100644 Blog.Core.Model/Tenants/ITenantEntity.cs create mode 100644 Blog.Core.Model/Tenants/MultiTenantAttribute.cs create mode 100644 Blog.Core.Model/Tenants/TenantTypeEnum.cs create mode 100644 Blog.Core.Services/SplitDemoServices.cs create mode 100644 Blog.Core.Services/TasksLogServices.cs create mode 100644 Blog.Core.Services/TenantService.cs create mode 100644 Blog.Core.Tests/Repository_Test/OrmTest.cs diff --git a/.gitignore b/.gitignore index b98046c6..b7645c45 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,9 @@ bld/ # Visual Studio 2017 auto generated files Generated\ Files/ +# Visual Studio Code +.vscode + # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* @@ -352,3 +355,4 @@ Blog.Core/WMBlog.db Blog.Core/Blog.Core*.xml Blog.Core.Api/WMBlog.db Blog.Core.Api/wwwroot/ui/ +*.db diff --git a/Blog.Core.Api/Blog.Core.Api.csproj b/Blog.Core.Api/Blog.Core.Api.csproj index 38575a55..77d681f1 100644 --- a/Blog.Core.Api/Blog.Core.Api.csproj +++ b/Blog.Core.Api/Blog.Core.Api.csproj @@ -26,18 +26,22 @@ + + + + @@ -94,7 +98,6 @@ - diff --git a/Blog.Core.Api/Blog.Core.Model.xml b/Blog.Core.Api/Blog.Core.Model.xml index ea9857bc..8423cb6b 100644 --- a/Blog.Core.Api/Blog.Core.Model.xml +++ b/Blog.Core.Api/Blog.Core.Model.xml @@ -301,6 +301,16 @@ 逻辑删除 + + + 评论 + + + + + 博客文章 评论 + + 部门表 @@ -822,6 +832,122 @@ 修改时间 + + + 状态
+ 中立字段,某些表可使用某些表不使用 +
+
+ + + 中立字段,某些表可使用某些表不使用
+ 逻辑上的删除,非物理删除
+ 例如:单据删除并非直接删除 +
+
+ + + 中立字段
+ 是否内置数据 +
+
+ + + 创建ID + + + + + 创建者 + + + + + 创建时间 + + + + + 修改ID + + + + + 更新者 + + + + + 修改日期 + + + + + 数据版本 + + + + + 软删除 过滤器 + + + + + 系统租户表
+ 根据TenantType 分为两种方案:
+ 1.按租户字段区分
+ 2.按租户分库
+ +
+ + 注意:
+ 使用租户Id方案,无需配置分库的连接 +
+
+ + + 名称 + + + + + 租户类型 + + + + + 数据库/租户标识 不可重复
+ 使用Id方案,可无需配置 +
+
+ + + 主机
+ 使用Id方案,可无需配置 +
+
+ + + 数据库类型
+ 使用Id方案,可无需配置 +
+
+ + + 数据库连接
+ 使用Id方案,可无需配置 +
+
+ + + 状态 + + + + + 备注 + + 用户信息表 @@ -887,6 +1013,96 @@ 登录账号 + + + 租户Id + + + + + 任务日志表 + + + + + 任务ID + + + + + 任务耗时 + + + + + 执行结果(0-失败 1-成功) + + + + + 运行时间 + + + + + 结束时间 + + + + + 执行参数 + + + + + 异常信息 + + + + + 异常堆栈 + + + + + 创建ID + + + + + 创建者 + + + + + 创建时间 + + + + + 修改ID + + + + + 修改者 + + + + + 修改时间 + + + + + 任务名称 + + + + + 任务分组 + + 任务计划表 @@ -952,6 +1168,11 @@ 循环执行次数 + + + 已循环次数 + + 是否启动 @@ -972,6 +1193,63 @@ 任务内存中的状态 + + + 业务数据
+ 多租户 (Id 隔离) +
+
+ + + 无需手动赋值 + + + + + 名称 + + + + + 金额 + + + + + 多租户-多表方案 业务表 子表
+
+
+ + + 多租户-多表方案 业务表
+
+
+ + + 名称 + + + + + 金额 + + + + + 多租户-多库方案 业务表
+ 公共库无需标记[MultiTenant]特性 +
+
+ + + 名称 + + + + + 金额 + + Tibug 类别 @@ -1779,6 +2057,43 @@ 返回数据集 + + + 租户模型接口 + + + + + 租户Id + + + + + 标识 多租户 的业务表
+ 默认设置是多库
+ 公共表无需区分 直接使用主库 各自业务在各自库中
+
+
+ + + 租户隔离方案 + + + + + Id隔离 + + + + + 库隔离 + + + + + 表隔离 + + 广告类 diff --git a/Blog.Core.Api/Blog.Core.xml b/Blog.Core.Api/Blog.Core.xml index a14a9006..c6b33db9 100644 --- a/Blog.Core.Api/Blog.Core.xml +++ b/Blog.Core.Api/Blog.Core.xml @@ -26,14 +26,14 @@ - + 获取博客详情 - + 获取详情【无权限】 @@ -67,7 +67,7 @@ - + 删除博客 @@ -276,7 +276,7 @@ - + 删除一条接口 @@ -361,7 +361,7 @@ 菜单管理 - + 构造函数 @@ -369,20 +369,22 @@ + - + 获取菜单 + - + 查询树形 Table @@ -404,7 +406,7 @@ - + 获取菜单树 @@ -412,21 +414,21 @@ - + 获取路由树 - + 获取路由树 - + 通过角色获取菜单 @@ -440,7 +442,7 @@ - + 删除菜单 @@ -454,7 +456,7 @@ - + 系统接口菜单同步接口 @@ -491,7 +493,7 @@ - + 删除角色 @@ -520,42 +522,42 @@ - + 删除一个任务 - + 启动计划任务 - + 停止一个计划任务 - + 暂停一个计划任务 - + 恢复一个计划任务 - + 重启一个计划任务 @@ -568,13 +570,25 @@ - + 立即执行任务 + + + 获取任务运行日志 + + + + + + 任务概况 + + + 类别管理【无权限】 @@ -615,7 +629,7 @@ - + 获取详情【无权限】 @@ -636,14 +650,14 @@ - + 删除 bug - + 测试事务在AOP中的使用 @@ -851,7 +865,7 @@ - + 删除用户 @@ -887,7 +901,7 @@ - + 新建用户角色关系 @@ -1349,7 +1363,7 @@ 关键字 - + 获取部门树 @@ -1396,6 +1410,139 @@ + + + 分表demo + + + + + 分页获取数据 + + + + + + + + + + + 根据ID获取信息 + + + + + + + 添加一条测试数据 + + + + + + + 修改一条测试数据 + + + + + + + 根据id删除数据 + + + + + + + 多租户-多库方案 测试 + + + + + 获取租户下全部业务数据
+
+ +
+ + + 新增数据 + + + + + + 多租户-Id方案 测试 + + + + + 获取租户下全部业务数据
+
+ +
+ + + 新增业务数据 + + + + + + 多租户-多表方案 测试 + + + + + 获取租户下全部业务数据
+
+ +
+ + + 新增数据 + + + + + + 租户管理 + + + + + 获取全部租户 + + + + + + 获取租户信息 + + + + + + 新增租户信息
+ 此处只做演示,具体要以实际业务为准 +
+ +
+ + + 修改租户信息
+ 此处只做演示,具体要以实际业务为准 +
+ +
+ + + 删除租户
+ 此处只做演示,具体要以实际业务为准 +
+ +
自定义路由 /api/{version}/[controler]/[action] diff --git a/Blog.Core.Api/Controllers/BlogController.cs b/Blog.Core.Api/Controllers/BlogController.cs index 6525448e..fbc67e12 100644 --- a/Blog.Core.Api/Controllers/BlogController.cs +++ b/Blog.Core.Api/Controllers/BlogController.cs @@ -1,8 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq.Expressions; +using System.Linq.Expressions; using System.Text.RegularExpressions; -using System.Threading.Tasks; using Blog.Core.Common.Helper; using Blog.Core.IServices; using Blog.Core.Model; @@ -11,7 +8,6 @@ using Blog.Core.SwaggerHelper; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; using StackExchange.Profiling; using static Blog.Core.Extensions.CustomApiVersion; @@ -87,7 +83,7 @@ public async Task>> Get(int id, int page = 1 [HttpGet("{id}")] //[Authorize(Policy = "Scope_BlogModule_Policy")] [Authorize] - public async Task> Get(int id) + public async Task> Get(long id) { return Success(await _blogArticleServices.GetBlogDetails(id)); } @@ -100,7 +96,7 @@ public async Task> Get(int id) /// [HttpGet] [Route("DetailNuxtNoPer")] - public async Task> DetailNuxtNoPer(int id) + public async Task> DetailNuxtNoPer(long id) { _logger.LogInformation("xxxxxxxxxxxxxxxxxxx"); return Success(await _blogArticleServices.GetBlogDetails(id)); @@ -108,7 +104,7 @@ public async Task> DetailNuxtNoPer(int id) [HttpGet] [Route("GoUrl")] - public async Task GoUrl(int id = 0) + public async Task GoUrl(long id = 0) { var response = await _blogArticleServices.QueryById(id); if (response != null && response.bsubmitter.IsNotEmptyOrNull()) @@ -140,7 +136,7 @@ public async Task>> GetBlogsByTypesForMVP(string [HttpGet] [Route("GetBlogByIdForMVP")] - public async Task> GetBlogByIdForMVP(int id = 0) + public async Task> GetBlogByIdForMVP(long id = 0) { if (id > 0) { @@ -251,7 +247,7 @@ public async Task> Put([FromBody] BlogArticle BlogArticle) [HttpDelete] [Authorize(Permissions.Name)] [Route("Delete")] - public async Task> Delete(int id) + public async Task> Delete(long id) { if (id > 0) { diff --git a/Blog.Core.Api/Controllers/DbFirst/MigrateController.cs b/Blog.Core.Api/Controllers/DbFirst/MigrateController.cs index 8006d1fb..7865cc69 100644 --- a/Blog.Core.Api/Controllers/DbFirst/MigrateController.cs +++ b/Blog.Core.Api/Controllers/DbFirst/MigrateController.cs @@ -81,7 +81,7 @@ public async Task> DataMigrateFromOld2New() InitPermissionTree(permissions, permissionsAllList, apiList); var actionPermissionIds = permissionsAllList.Where(d => d.Id >= filterPermissionId).Select(d => d.Id).ToList(); - List filterPermissionIds = new(); + List filterPermissionIds = new(); FilterPermissionTree(permissionsAllList, actionPermissionIds, filterPermissionIds); permissions = permissions.Where(d => filterPermissionIds.Contains(d.Id)).ToList(); @@ -93,10 +93,10 @@ public async Task> DataMigrateFromOld2New() // 1、保持菜单和接口 await SavePermissionTreeAsync(permissions, pms); - var rid = 0; - var pid = 0; - var mid = 0; - var rpmid = 0; + long rid = 0; + long pid = 0; + long mid = 0; + long rpmid = 0; // 2、保存关系表 foreach (var item in rmps) @@ -116,8 +116,8 @@ public async Task> DataMigrateFromOld2New() } } - pid = (pms.FirstOrDefault(d => d.PidOld == item.PermissionId)?.PidNew).ObjToInt(); - mid = (pms.FirstOrDefault(d => d.MidOld == item.ModuleId)?.MidNew).ObjToInt(); + pid = (pms.FirstOrDefault(d => d.PidOld == item.PermissionId)?.PidNew).ObjToLong(); + mid = (pms.FirstOrDefault(d => d.MidOld == item.ModuleId)?.MidNew).ObjToLong(); // 关系 if (rid > 0 && pid > 0) { @@ -282,7 +282,7 @@ private void InitPermissionTree(List permissionsTree, List permissionsAll, List actionPermissionId, List filterPermissionIds) + private void FilterPermissionTree(List permissionsAll, List actionPermissionId, List filterPermissionIds) { actionPermissionId = actionPermissionId.Distinct().ToList(); var doneIds = permissionsAll.Where(d => actionPermissionId.Contains(d.Id) && d.Pid == 0).Select(d => d.Id).ToList(); @@ -295,7 +295,7 @@ private void FilterPermissionTree(List permissionsAll, List act } } - private async Task SavePermissionTreeAsync(List permissionsTree, List pms, int permissionId = 0) + private async Task SavePermissionTreeAsync(List permissionsTree, List pms, long permissionId = 0) { var parendId = permissionId; @@ -304,9 +304,9 @@ private async Task SavePermissionTreeAsync(List permissionsTree, Lis PM pm = new PM(); // 保留原始主键id pm.PidOld = item.Id; - pm.MidOld = (item.Module?.Id).ObjToInt(); + pm.MidOld = (item.Module?.Id).ObjToLong(); - var mid = 0; + long mid = 0; // 接口 if (item.Module != null) { @@ -351,9 +351,9 @@ private async Task SavePermissionTreeAsync(List permissionsTree, Lis public class PM { - public int PidOld { get; set; } - public int MidOld { get; set; } - public int PidNew { get; set; } - public int MidNew { get; set; } + public long PidOld { get; set; } + public long MidOld { get; set; } + public long PidNew { get; set; } + public long MidNew { get; set; } } } diff --git a/Blog.Core.Api/Controllers/DepartmentController.cs b/Blog.Core.Api/Controllers/DepartmentController.cs index 1674883f..faf1f850 100644 --- a/Blog.Core.Api/Controllers/DepartmentController.cs +++ b/Blog.Core.Api/Controllers/DepartmentController.cs @@ -4,17 +4,10 @@ using Blog.Core.Model; using Blog.Core.Model.Models; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Hosting; using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Linq.Expressions; using System.Text; -using System.Threading.Tasks; namespace Blog.Core.Api.Controllers { @@ -90,7 +83,7 @@ public async Task>> GetTreeTable(long f = 0, strin foreach (var item in departments) { - List pidarr = new() { }; + List pidarr = new() { }; var parent = departmentList.FirstOrDefault(d => d.Id == item.Pid); while (parent != null) @@ -116,7 +109,7 @@ public async Task>> GetTreeTable(long f = 0, strin /// /// [HttpGet] - public async Task> GetDepartmentTree(int pid = 0) + public async Task> GetDepartmentTree(long pid = 0) { var departments = await _departmentServices.Query(d => d.IsDeleted == false); var departmentTrees = (from child in departments @@ -175,7 +168,7 @@ public async Task> Put([FromBody] Department request) } [HttpDelete] - public async Task> Delete(int id) + public async Task> Delete(long id) { var data = new MessageModel(); var model = await _departmentServices.QueryById(id); diff --git a/Blog.Core.Api/Controllers/LoginController.cs b/Blog.Core.Api/Controllers/LoginController.cs index 25818c13..87f5c0c9 100644 --- a/Blog.Core.Api/Controllers/LoginController.cs +++ b/Blog.Core.Api/Controllers/LoginController.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.IdentityModel.Tokens.Jwt; -using System.Linq; -using System.Security.Claims; -using System.Threading.Tasks; -using Blog.Core.AuthHelper; +using Blog.Core.AuthHelper; using Blog.Core.AuthHelper.OverWrite; using Blog.Core.Common.Helper; using Blog.Core.IServices; @@ -12,8 +6,9 @@ using Blog.Core.Model.ViewModels; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; namespace Blog.Core.Controllers @@ -52,6 +47,7 @@ public LoginController(ISysUserInfoServices sysUserInfoServices, IUserRoleServic #region 获取token的第1种方法 + /// /// 获取JWT的方法1 /// @@ -62,7 +58,6 @@ public LoginController(ISysUserInfoServices sysUserInfoServices, IUserRoleServic [Route("Token")] public async Task> GetJwtStr(string name, string pass) { - string jwtStr = string.Empty; bool suc = false; //这里就是用户登陆以后,通过数据库去调取数据,分配权限的操作 @@ -70,7 +65,6 @@ public async Task> GetJwtStr(string name, string pass) var user = await _sysUserInfoServices.GetUserRoleNameStr(name, MD5Helper.MD5Encrypt32(pass)); if (user != null) { - TokenModelJwt tokenModel = new TokenModelJwt { Uid = 1, Role = user }; jwtStr = JwtHelper.IssueJwt(tokenModel); @@ -119,6 +113,7 @@ public MessageModel GetJwtStrForNuxt(string name, string pass) { jwtStr = "login fail!!!"; } + var result = new { data = new { success = suc, token = jwtStr } @@ -131,8 +126,8 @@ public MessageModel GetJwtStrForNuxt(string name, string pass) response = jwtStr }; } - #endregion + #endregion /// @@ -157,11 +152,14 @@ public async Task> GetJwtToken3(string name = " { var userRoles = await _sysUserInfoServices.GetUserRoleNameStr(name, pass); //如果是基于用户的授权策略,这里要添加用户;如果是基于角色的授权策略,这里要添加角色 - var claims = new List { + var claims = new List + { new Claim(ClaimTypes.Name, name), new Claim(JwtRegisteredClaimNames.Jti, user.FirstOrDefault().Id.ToString()), + new Claim("TenantId", user.FirstOrDefault().TenantId.ToString()), new Claim(JwtRegisteredClaimNames.Iat, DateTime.Now.ToString()), - new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(_requirement.Expiration.TotalSeconds).ToString()) }; + new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(_requirement.Expiration.TotalSeconds).ToString()) + }; claims.AddRange(userRoles.Split(',').Select(s => new Claim(ClaimTypes.Role, s))); @@ -218,11 +216,13 @@ public async Task> RefreshToken(string token = { var userRoles = await _sysUserInfoServices.GetUserRoleNameStr(user.LoginName, user.LoginPWD); //如果是基于用户的授权策略,这里要添加用户;如果是基于角色的授权策略,这里要添加角色 - var claims = new List { - new Claim(ClaimTypes.Name, user.LoginName), - new Claim(JwtRegisteredClaimNames.Jti, tokenModel.Uid.ObjToString()), - new Claim(JwtRegisteredClaimNames.Iat, DateTime.Now.ToString()), - new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(_requirement.Expiration.TotalSeconds).ToString()) }; + var claims = new List + { + new Claim(ClaimTypes.Name, user.LoginName), + new Claim(JwtRegisteredClaimNames.Jti, tokenModel.Uid.ObjToString()), + new Claim(JwtRegisteredClaimNames.Iat, DateTime.Now.ToString()), + new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(_requirement.Expiration.TotalSeconds).ToString()) + }; claims.AddRange(userRoles.Split(',').Select(s => new Claim(ClaimTypes.Role, s))); //用户标识 @@ -233,6 +233,7 @@ public async Task> RefreshToken(string token = return Success(refreshToken, "获取成功"); } } + return Failed("认证失败!"); } diff --git a/Blog.Core.Api/Controllers/ModuleController.cs b/Blog.Core.Api/Controllers/ModuleController.cs index 334a8ea4..27e6f4db 100644 --- a/Blog.Core.Api/Controllers/ModuleController.cs +++ b/Blog.Core.Api/Controllers/ModuleController.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq.Expressions; -using System.Threading.Tasks; +using System.Linq.Expressions; using Blog.Core.Common.HttpContextUser; using Blog.Core.IServices; using Blog.Core.Model; @@ -122,7 +119,7 @@ public async Task> Put([FromBody] Modules module) /// // DELETE: api/ApiWithActions/5 [HttpDelete] - public async Task> Delete(int id) + public async Task> Delete(long id) { if (id <= 0) return Failed("缺少参数"); diff --git a/Blog.Core.Api/Controllers/MonitorController.cs b/Blog.Core.Api/Controllers/MonitorController.cs index c0dbed52..77b4e5bf 100644 --- a/Blog.Core.Api/Controllers/MonitorController.cs +++ b/Blog.Core.Api/Controllers/MonitorController.cs @@ -6,19 +6,12 @@ using Blog.Core.Model; using Blog.Core.Model.ViewModels; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.SignalR; -using Microsoft.Extensions.Logging; using Newtonsoft.Json; -using System; -using System.Collections.Generic; using System.Diagnostics; -using System.IO; -using System.Linq; using System.Runtime.InteropServices; using System.Text; -using System.Threading.Tasks; using Blog.Core.Extensions.Middlewares; namespace Blog.Core.Controllers diff --git a/Blog.Core.Api/Controllers/NacosController.cs b/Blog.Core.Api/Controllers/NacosController.cs index a8701b39..e5223851 100644 --- a/Blog.Core.Api/Controllers/NacosController.cs +++ b/Blog.Core.Api/Controllers/NacosController.cs @@ -4,7 +4,6 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Nacos.V2; -using System.Threading.Tasks; namespace Blog.Core.Api.Controllers { diff --git a/Blog.Core.Api/Controllers/PayController.cs b/Blog.Core.Api/Controllers/PayController.cs index 0cbe0541..6c05c249 100644 --- a/Blog.Core.Api/Controllers/PayController.cs +++ b/Blog.Core.Api/Controllers/PayController.cs @@ -1,10 +1,8 @@ -using System.Threading.Tasks; -using Blog.Core.IServices; +using Blog.Core.IServices; using Blog.Core.Model; using Blog.Core.Model.ViewModels; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; namespace Blog.Core.Controllers { diff --git a/Blog.Core.Api/Controllers/PermissionController.cs b/Blog.Core.Api/Controllers/PermissionController.cs index d3000766..7346cc21 100644 --- a/Blog.Core.Api/Controllers/PermissionController.cs +++ b/Blog.Core.Api/Controllers/PermissionController.cs @@ -6,6 +6,8 @@ using Blog.Core.IServices; using Blog.Core.Model; using Blog.Core.Model.Models; +using Blog.Core.Repository.UnitOfWorks; +using Blog.Core.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; @@ -21,6 +23,7 @@ namespace Blog.Core.Controllers [Authorize(Permissions.Name)] public class PermissionController : BaseApiController { + readonly IUnitOfWorkManage _unitOfWorkManage; readonly IPermissionServices _permissionServices; readonly IModuleServices _moduleServices; readonly IRoleModulePermissionServices _roleModulePermissionServices; @@ -37,16 +40,19 @@ public class PermissionController : BaseApiController /// /// /// + /// /// /// /// /// public PermissionController(IPermissionServices permissionServices, IModuleServices moduleServices, IRoleModulePermissionServices roleModulePermissionServices, IUserRoleServices userRoleServices, + IUnitOfWorkManage unitOfWorkManage, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContext, IUser user, PermissionRequirement requirement) { _permissionServices = permissionServices; + _unitOfWorkManage = unitOfWorkManage; _moduleServices = moduleServices; _roleModulePermissionServices = roleModulePermissionServices; _userRoleServices = userRoleServices; @@ -61,34 +67,19 @@ public PermissionController(IPermissionServices permissionServices, IModuleServi /// /// /// + /// /// // GET: api/User [HttpGet] - public async Task>> Get(int page = 1, string key = "") + public async Task>> Get(int page = 1, string key = "", int pageSize = 50) { PageModel permissions = new PageModel(); - int intPageSize = 50; if (string.IsNullOrEmpty(key) || string.IsNullOrWhiteSpace(key)) { key = ""; } - #region 舍弃 - //var permissions = await _permissionServices.Query(a => a.IsDeleted != true); - //if (!string.IsNullOrEmpty(key)) - //{ - // permissions = permissions.Where(t => (t.Name != null && t.Name.Contains(key))).ToList(); - //} - ////筛选后的数据总数 - //totalCount = permissions.Count; - ////筛选后的总页数 - //pageCount = (Math.Ceiling(totalCount.ObjToDecimal() / intTotalCount.ObjToDecimal())).ObjToInt(); - //permissions = permissions.OrderByDescending(d => d.Id).Skip((page - 1) * intTotalCount).Take(intTotalCount).ToList(); - #endregion - - - - permissions = await _permissionServices.QueryPage(a => a.IsDeleted != true && (a.Name != null && a.Name.Contains(key)), page, intPageSize, " Id desc "); + permissions = await _permissionServices.QueryPage(a => a.IsDeleted != true && (a.Name != null && a.Name.Contains(key)), page, pageSize, " Id desc "); #region 单独处理 @@ -99,7 +90,7 @@ public async Task>> Get(int page = 1, string var permissionAll = await _permissionServices.Query(d => d.IsDeleted != true); foreach (var item in permissionsView) { - List pidarr = new List + List pidarr = new() { item.Pid }; @@ -156,7 +147,7 @@ public async Task>> Get(int page = 1, string /// [HttpGet] [AllowAnonymous] - public async Task>> GetTreeTable(int f = 0, string key = "") + public async Task>> GetTreeTable(long f = 0, string key = "") { List permissions = new List(); var apiList = await _moduleServices.Query(d => d.IsDeleted == false); @@ -177,7 +168,7 @@ public async Task>> GetTreeTable(int f = 0, string foreach (var item in permissions) { - List pidarr = new List { }; + List pidarr = new() { }; var parent = permissionsList.FirstOrDefault(d => d.Id == item.Pid); while (parent != null) @@ -240,64 +231,55 @@ public async Task> Post([FromBody] Permission permission) [HttpPost] public async Task> Assign([FromBody] AssignView assignView) { - var data = new MessageModel(); - - if (assignView.rid > 0) { - data.success = true; - - var roleModulePermissions = await _roleModulePermissionServices.Query(d => d.RoleId == assignView.rid); + //开启事务 + try + { + var old_rmps = await _roleModulePermissionServices.Query(d => d.RoleId == assignView.rid); - var remove = roleModulePermissions.Where(d => !assignView.pids.Contains(d.PermissionId.ObjToInt())).Select(c => (object)c.Id); - data.success &= remove.Any() ? await _roleModulePermissionServices.DeleteByIds(remove.ToArray()) : true; + _unitOfWorkManage.BeginTran(); + await _permissionServices.Db.Deleteable(t => t.RoleId == assignView.rid).ExecuteCommandAsync(); + var permissions = await _permissionServices.Query(d => d.IsDeleted == false); - foreach (var item in assignView.pids) - { - var rmpitem = roleModulePermissions.Where(d => d.PermissionId == item); - var moduleid = (await _permissionServices.Query(p => p.Id == item)).FirstOrDefault()?.Mid; - if (!rmpitem.Any()) + List new_rmps = new List(); + var nowTime = _permissionServices.Db.GetDate(); + foreach (var item in assignView.pids) { + var moduleid = permissions.Find(p => p.Id == item)?.Mid; + var find_old_rmps = old_rmps.Find(p => p.PermissionId == item); RoleModulePermission roleModulePermission = new RoleModulePermission() { IsDeleted = false, RoleId = assignView.rid, - ModuleId = moduleid.ObjToInt(), + ModuleId = moduleid.ObjToLong(), PermissionId = item, - }; - - - roleModulePermission.CreateId = _user.ID; - roleModulePermission.CreateBy = _user.Name; + CreateId = find_old_rmps == null ? _user.ID : find_old_rmps.CreateId, + CreateBy = find_old_rmps == null ? _user.Name : find_old_rmps.CreateBy, + CreateTime = find_old_rmps == null ? nowTime : find_old_rmps.CreateTime, + ModifyId = _user.ID, + ModifyBy = _user.Name, + ModifyTime = nowTime - data.success &= (await _roleModulePermissionServices.Add(roleModulePermission)) > 0; - - } - else - { - foreach (var role in rmpitem) - { - if (!role.ModuleId.Equals(moduleid)) - { - role.ModuleId = moduleid.Value; - await _roleModulePermissionServices.Update(role, new List { "ModuleId" }); - } - } + }; + new_rmps.Add(roleModulePermission); } + if (new_rmps.Count > 0) await _roleModulePermissionServices.Add(new_rmps); + _unitOfWorkManage.CommitTran(); } - - if (data.success) + catch (Exception) { - _requirement.Permissions.Clear(); - data.response = ""; - data.msg = "保存成功"; + _unitOfWorkManage.RollbackTran(); + throw; } - + _requirement.Permissions.Clear(); + return Success("保存成功"); + } + else + { + return Failed("请选择要操作的角色"); } - - - return data; } @@ -308,7 +290,7 @@ public async Task> Assign([FromBody] AssignView assignView) /// /// [HttpGet] - public async Task> GetPermissionTree(int pid = 0, bool needbtn = false) + public async Task> GetPermissionTree(long pid = 0, bool needbtn = false) { //var data = new MessageModel(); @@ -353,29 +335,29 @@ orderby child.Id /// /// [HttpGet] - public async Task> GetNavigationBar(int uid) + public async Task> GetNavigationBar(long uid) { var data = new MessageModel(); - var uidInHttpcontext1 = 0; - var roleIds = new List(); + long uidInHttpcontext1 = 0; + var roleIds = new List(); // ids4和jwt切换 if (Permissions.IsUseIds4) { // ids4 uidInHttpcontext1 = (from item in _httpContext.HttpContext.User.Claims where item.Type == "sub" - select item.Value).FirstOrDefault().ObjToInt(); + select item.Value).FirstOrDefault().ObjToLong(); roleIds = (from item in _httpContext.HttpContext.User.Claims where item.Type == "role" - select item.Value.ObjToInt()).ToList(); + select item.Value.ObjToLong()).ToList(); } else { // jwt - uidInHttpcontext1 = ((JwtHelper.SerializeJwt(_httpContext.HttpContext.Request.Headers["Authorization"].ObjToString().Replace("Bearer ", "")))?.Uid).ObjToInt(); - roleIds = (await _userRoleServices.Query(d => d.IsDeleted == false && d.UserId == uid)).Select(d => d.RoleId.ObjToInt()).Distinct().ToList(); + uidInHttpcontext1 = ((JwtHelper.SerializeJwt(_httpContext.HttpContext.Request.Headers["Authorization"].ObjToString().Replace("Bearer ", "")))?.Uid).ObjToLong(); + roleIds = (await _userRoleServices.Query(d => d.IsDeleted == false && d.UserId == uid)).Select(d => d.RoleId.ObjToLong()).Distinct().ToList(); } @@ -383,7 +365,7 @@ public async Task> GetNavigationBar(int uid) { if (roleIds.Any()) { - var pids = (await _roleModulePermissionServices.Query(d => d.IsDeleted == false && roleIds.Contains(d.RoleId))).Select(d => d.PermissionId.ObjToInt()).Distinct(); + var pids = (await _roleModulePermissionServices.Query(d => d.IsDeleted == false && roleIds.Contains(d.RoleId))).Select(d => d.PermissionId.ObjToLong()).Distinct(); if (pids.Any()) { var rolePermissionMoudles = (await _permissionServices.Query(d => pids.Contains(d.Id))).OrderBy(c => c.OrderSort); @@ -445,28 +427,28 @@ orderby child.Id /// /// [HttpGet] - public async Task>> GetNavigationBarPro(int uid) + public async Task>> GetNavigationBarPro(long uid) { var data = new MessageModel>(); - var uidInHttpcontext1 = 0; - var roleIds = new List(); + long uidInHttpcontext1 = 0; + var roleIds = new List(); // ids4和jwt切换 if (Permissions.IsUseIds4) { // ids4 uidInHttpcontext1 = (from item in _httpContext.HttpContext.User.Claims where item.Type == "sub" - select item.Value).FirstOrDefault().ObjToInt(); + select item.Value).FirstOrDefault().ObjToLong(); roleIds = (from item in _httpContext.HttpContext.User.Claims where item.Type == "role" - select item.Value.ObjToInt()).ToList(); + select item.Value.ObjToLong()).ToList(); } else { // jwt - uidInHttpcontext1 = ((JwtHelper.SerializeJwt(_httpContext.HttpContext.Request.Headers["Authorization"].ObjToString().Replace("Bearer ", "")))?.Uid).ObjToInt(); - roleIds = (await _userRoleServices.Query(d => d.IsDeleted == false && d.UserId == uid)).Select(d => d.RoleId.ObjToInt()).Distinct().ToList(); + uidInHttpcontext1 = ((JwtHelper.SerializeJwt(_httpContext.HttpContext.Request.Headers["Authorization"].ObjToString().Replace("Bearer ", "")))?.Uid).ObjToLong(); + roleIds = (await _userRoleServices.Query(d => d.IsDeleted == false && d.UserId == uid)).Select(d => d.RoleId.ObjToLong()).Distinct().ToList(); } if (uid > 0 && uid == uidInHttpcontext1) @@ -474,7 +456,7 @@ public async Task>> GetNavigationBarPro(int if (roleIds.Any()) { var pids = (await _roleModulePermissionServices.Query(d => d.IsDeleted == false && roleIds.Contains(d.RoleId))) - .Select(d => d.PermissionId.ObjToInt()).Distinct(); + .Select(d => d.PermissionId.ObjToLong()).Distinct(); if (pids.Any()) { var rolePermissionMoudles = (await _permissionServices.Query(d => pids.Contains(d.Id) && d.IsButton == false)).OrderBy(c => c.OrderSort); @@ -522,14 +504,14 @@ orderby item.Id /// [HttpGet] [AllowAnonymous] - public async Task> GetPermissionIdByRoleId(int rid = 0) + public async Task> GetPermissionIdByRoleId(long rid = 0) { //var data = new MessageModel(); var rmps = await _roleModulePermissionServices.Query(d => d.IsDeleted == false && d.RoleId == rid); var permissionTrees = (from child in rmps orderby child.Id - select child.PermissionId.ObjToInt()).ToList(); + select child.PermissionId.ObjToLong()).ToList(); var permissions = await _permissionServices.Query(d => d.IsDeleted == false); List assignbtns = new List(); @@ -595,7 +577,7 @@ public async Task> Put([FromBody] Permission permission) /// // DELETE: api/ApiWithActions/5 [HttpDelete] - public async Task> Delete(int id) + public async Task> Delete(long id) { var data = new MessageModel(); if (id > 0) @@ -657,7 +639,7 @@ public async Task> BatchPost([FromBody] List pe /// 是否执行迁移到数据 /// [HttpGet] - public async Task>> MigratePermission(string action = "", string controllerName = "", int pid = 0, bool isAction = false) + public async Task>> MigratePermission(string action = "", string controllerName = "", long pid = 0, bool isAction = false) { var data = new MessageModel>(); if (controllerName.IsNullOrEmpty()) @@ -757,11 +739,11 @@ public async Task>> MigratePermission(string actio List modules = await _moduleServices.Query(d => d.LinkUrl != null && d.LinkUrl.ToLower() == item.Module.LinkUrl); if (!modules.Any()) { - int mid = await _moduleServices.Add(item.Module); + var mid = await _moduleServices.Add(item.Module); if (mid > 0) { item.Mid = mid; - int permissionid = await _permissionServices.Add(item); + var permissionid = await _permissionServices.Add(item); } } @@ -779,12 +761,12 @@ public async Task>> MigratePermission(string actio public class AssignView { - public List pids { get; set; } - public int rid { get; set; } + public List pids { get; set; } + public long rid { get; set; } } public class AssignShow { - public List permissionids { get; set; } + public List permissionids { get; set; } public List assignbtns { get; set; } } diff --git a/Blog.Core.Api/Controllers/RoleController.cs b/Blog.Core.Api/Controllers/RoleController.cs index 36df73a4..0b93e943 100644 --- a/Blog.Core.Api/Controllers/RoleController.cs +++ b/Blog.Core.Api/Controllers/RoleController.cs @@ -1,5 +1,4 @@ -using System.Threading.Tasks; -using Blog.Core.Common.HttpContextUser; +using Blog.Core.Common.HttpContextUser; using Blog.Core.IServices; using Blog.Core.Model; using Blog.Core.Model.Models; @@ -113,7 +112,7 @@ public async Task> Put([FromBody] Role role) /// // DELETE: api/ApiWithActions/5 [HttpDelete] - public async Task> Delete(int id) + public async Task> Delete(long id) { var data = new MessageModel(); diff --git a/Blog.Core.Api/Controllers/SplitDemoController.cs b/Blog.Core.Api/Controllers/SplitDemoController.cs new file mode 100644 index 00000000..f625b202 --- /dev/null +++ b/Blog.Core.Api/Controllers/SplitDemoController.cs @@ -0,0 +1,199 @@ +using Blog.Core.IServices; +using Blog.Core.Model; +using Blog.Core.Model.Models; +using Blog.Core.Repository.UnitOfWorks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Linq.Expressions; + +namespace Blog.Core.Api.Controllers +{ + /// + /// 分表demo + /// + [Route("api/[controller]/[action]")] + [ApiController] + [Authorize(Permissions.Name)] + public class SplitDemoController : ControllerBase + { + readonly ISplitDemoServices splitDemoServices; + readonly IUnitOfWorkManage unitOfWorkManage; + public SplitDemoController(ISplitDemoServices _splitDemoServices, IUnitOfWorkManage _unitOfWorkManage) + { + splitDemoServices = _splitDemoServices; + unitOfWorkManage = _unitOfWorkManage; + } + + /// + /// 分页获取数据 + /// + /// + /// + /// + /// + /// + /// + [HttpGet] + [AllowAnonymous] + public async Task>> Get(DateTime beginTime, DateTime endTime, int page = 1, string key = "", int pageSize = 10) + { + if (string.IsNullOrEmpty(key) || string.IsNullOrWhiteSpace(key)) + { + key = ""; + } + Expression> whereExpression = a => (a.Name != null && a.Name.Contains(key)); + var data = await splitDemoServices.QueryPageSplit(whereExpression, beginTime, endTime, page, pageSize, " Id desc "); + return MessageModel>.Message(data.dataCount >= 0, "获取成功", data); + } + + /// + /// 根据ID获取信息 + /// + /// + /// + [HttpGet] + [AllowAnonymous] + public async Task> GetById(long id) + { + var data = new MessageModel(); + var model = await splitDemoServices.QueryByIdSplit(id); + if (model != null) + { + return MessageModel.Success("获取成功", model); + } + else + { + return MessageModel.Fail("获取失败"); + } + } + + /// + /// 添加一条测试数据 + /// + /// + /// + [HttpPost] + [AllowAnonymous] + public async Task> Post([FromBody] SplitDemo splitDemo) + { + var data = new MessageModel(); + //unitOfWorkManage.BeginTran(); + var id = (await splitDemoServices.AddSplit(splitDemo)); + data.success = (id == null ? false : true); + try + { + if (data.success) + { + data.response = id.FirstOrDefault().ToString(); + data.msg = "添加成功"; + } + else + { + data.msg = "添加失败"; + } + } + catch (Exception) + { + throw; + } + finally + { + //if (data.success) + // unitOfWorkManage.CommitTran(); + //else + // unitOfWorkManage.RollbackTran(); + } + return data; + } + + /// + /// 修改一条测试数据 + /// + /// + /// + [HttpPut] + [AllowAnonymous] + public async Task> Put([FromBody] SplitDemo splitDemo) + { + var data = new MessageModel(); + if (splitDemo != null && splitDemo.Id > 0) + { + unitOfWorkManage.BeginTran(); + data.success = await splitDemoServices.UpdateSplit(splitDemo, splitDemo.CreateTime); + try + { + if (data.success) + { + data.msg = "修改成功"; + data.response = splitDemo?.Id.ObjToString(); + } + else + { + data.msg = "修改失败"; + } + } + catch (Exception) + { + throw; + } + finally + { + if (data.success) + unitOfWorkManage.CommitTran(); + else + unitOfWorkManage.RollbackTran(); + } + } + return data; + } + + /// + /// 根据id删除数据 + /// + /// + /// + [HttpDelete] + [AllowAnonymous] + public async Task> Delete(long id) + { + var data = new MessageModel(); + + var model = await splitDemoServices.QueryByIdSplit(id); + if (model != null) + { + unitOfWorkManage.BeginTran(); + data.success = await splitDemoServices.DeleteSplit(model,model.CreateTime); + try + { + data.response = id.ObjToString(); + if (data.success) + { + data.msg = "删除成功"; + } + else + { + data.msg = "删除失败"; + } + + } + catch (Exception) + { + throw; + } + finally + { + if (data.success) + unitOfWorkManage.CommitTran(); + else + unitOfWorkManage.RollbackTran(); + } + } + else + { + data.msg = "不存在"; + } + return data; + + } + } +} diff --git a/Blog.Core.Api/Controllers/TasksQzController.cs b/Blog.Core.Api/Controllers/TasksQzController.cs index 264fa195..887cfcfc 100644 --- a/Blog.Core.Api/Controllers/TasksQzController.cs +++ b/Blog.Core.Api/Controllers/TasksQzController.cs @@ -1,9 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; +using System.Linq.Expressions; using System.Reflection; -using System.Threading.Tasks; using Blog.Core.IServices; using Blog.Core.Model; using Blog.Core.Model.Models; @@ -22,14 +18,16 @@ namespace Blog.Core.Controllers public class TasksQzController : ControllerBase { private readonly ITasksQzServices _tasksQzServices; + private readonly ITasksLogServices _tasksLogServices; private readonly ISchedulerCenter _schedulerCenter; private readonly IUnitOfWorkManage _unitOfWorkManage; - public TasksQzController(ITasksQzServices tasksQzServices, ISchedulerCenter schedulerCenter, IUnitOfWorkManage unitOfWorkManage) + public TasksQzController(ITasksQzServices tasksQzServices, ISchedulerCenter schedulerCenter, IUnitOfWorkManage unitOfWorkManage, ITasksLogServices tasksLogServices) { _unitOfWorkManage = unitOfWorkManage; _tasksQzServices = tasksQzServices; _schedulerCenter = schedulerCenter; + _tasksLogServices = tasksLogServices; } /// @@ -58,7 +56,7 @@ public async Task>> Get(int page = 1, string key item.Triggers = await _schedulerCenter.GetTaskStaus(item); } } - return MessageModel>.Message(data.dataCount >= 0, "获取成功", data); + return MessageModel>.Message(data.dataCount >= 0, "获取成功", data); } /// @@ -86,32 +84,33 @@ public async Task> Post([FromBody] TasksQz tasksQz) var ResuleModel = await _schedulerCenter.AddScheduleJobAsync(tasksQz); data.success = ResuleModel.success; if (ResuleModel.success) - { + { data.msg = $"{data.msg}=>启动成功=>{ResuleModel.msg}"; } else - { + { data.msg = $"{data.msg}=>启动失败=>{ResuleModel.msg}"; } } } else - { + { data.msg = "添加失败"; - } + } } catch (Exception) { throw; } finally - { if(data.success) + { + if (data.success) _unitOfWorkManage.CommitTran(); else _unitOfWorkManage.RollbackTran(); } - return data; + return data; } @@ -135,7 +134,7 @@ public async Task> Put([FromBody] TasksQz tasksQz) data.msg = "修改成功"; data.response = tasksQz?.Id.ObjToString(); if (tasksQz.IsStart) - { + { var ResuleModelStop = await _schedulerCenter.StopScheduleJobAsync(tasksQz); data.msg = $"{data.msg}=>停止:{ResuleModelStop.msg}"; var ResuleModelStar = await _schedulerCenter.AddScheduleJobAsync(tasksQz); @@ -163,7 +162,7 @@ public async Task> Put([FromBody] TasksQz tasksQz) _unitOfWorkManage.CommitTran(); else _unitOfWorkManage.RollbackTran(); - } + } } return data; } @@ -173,7 +172,7 @@ public async Task> Put([FromBody] TasksQz tasksQz) /// /// [HttpDelete] - public async Task> Delete(int jobId) + public async Task> Delete(long jobId) { var data = new MessageModel(); @@ -207,7 +206,7 @@ public async Task> Delete(int jobId) _unitOfWorkManage.CommitTran(); else _unitOfWorkManage.RollbackTran(); - } + } } else { @@ -222,14 +221,14 @@ public async Task> Delete(int jobId) /// /// [HttpGet] - public async Task> StartJob(int jobId) + public async Task> StartJob(long jobId) { var data = new MessageModel(); var model = await _tasksQzServices.QueryById(jobId); if (model != null) { - _unitOfWorkManage.BeginTran(); + _unitOfWorkManage.BeginTran(); try { model.IsStart = true; @@ -265,7 +264,7 @@ public async Task> StartJob(int jobId) _unitOfWorkManage.CommitTran(); else _unitOfWorkManage.RollbackTran(); - } + } } else { @@ -279,7 +278,7 @@ public async Task> StartJob(int jobId) /// /// [HttpGet] - public async Task> StopJob(int jobId) + public async Task> StopJob(long jobId) { var data = new MessageModel(); @@ -319,12 +318,12 @@ public async Task> StopJob(int jobId) /// /// [HttpGet] - public async Task> PauseJob(int jobId) + public async Task> PauseJob(long jobId) { - var data = new MessageModel(); + var data = new MessageModel(); var model = await _tasksQzServices.QueryById(jobId); if (model != null) - { + { _unitOfWorkManage.BeginTran(); try { @@ -359,7 +358,7 @@ public async Task> PauseJob(int jobId) _unitOfWorkManage.CommitTran(); else _unitOfWorkManage.RollbackTran(); - } + } } else { @@ -373,13 +372,13 @@ public async Task> PauseJob(int jobId) /// /// [HttpGet] - public async Task> ResumeJob(int jobId) + public async Task> ResumeJob(long jobId) { var data = new MessageModel(); var model = await _tasksQzServices.QueryById(jobId); if (model != null) - { + { _unitOfWorkManage.BeginTran(); try { @@ -415,7 +414,7 @@ public async Task> ResumeJob(int jobId) _unitOfWorkManage.CommitTran(); else _unitOfWorkManage.RollbackTran(); - } + } } else { @@ -429,7 +428,7 @@ public async Task> ResumeJob(int jobId) /// /// [HttpGet] - public async Task> ReCovery(int jobId) + public async Task> ReCovery(long jobId) { var data = new MessageModel(); var model = await _tasksQzServices.QueryById(jobId); @@ -475,7 +474,7 @@ public async Task> ReCovery(int jobId) _unitOfWorkManage.CommitTran(); else _unitOfWorkManage.RollbackTran(); - } + } } else { @@ -488,7 +487,7 @@ public async Task> ReCovery(int jobId) /// 获取任务命名空间 /// /// - [HttpGet] + [HttpGet] public MessageModel> GetTaskNameSpace() { var baseType = typeof(IJob); @@ -501,14 +500,14 @@ public MessageModel> GetTaskNameSpace() var implementTypes = types.Where(x => x.IsClass).Select(item => new QuartzReflectionViewModel { nameSpace = item.Namespace, nameClass = item.Name, remark = "" }).ToList(); return MessageModel>.Success("获取成功", implementTypes); } - + /// /// 立即执行任务 /// /// /// [HttpGet] - public async Task> ExecuteJob(int jobId) + public async Task> ExecuteJob(long jobId) { var data = new MessageModel(); @@ -523,6 +522,26 @@ public async Task> ExecuteJob(int jobId) } return data; } + /// + /// 获取任务运行日志 + /// + /// + [HttpGet] + public async Task>> GetTaskLogs(long jobId, int page = 1, int pageSize = 10, DateTime? runTimeStart = null, DateTime? runTimeEnd = null) + { + var model = await _tasksLogServices.GetTaskLogs(jobId, page, pageSize, runTimeStart, runTimeEnd); + return MessageModel>.Message(model.dataCount >= 0, "获取成功", model); + } + /// + /// 任务概况 + /// + /// + [HttpGet] + public async Task> GetTaskOverview(long jobId, int page = 1, int pageSize = 10, DateTime? runTimeStart = null, DateTime? runTimeEnd = null, string type = "month") + { + var model = await _tasksLogServices.GetTaskOverview(jobId, runTimeStart, runTimeEnd, type); + return MessageModel.Message(true, "获取成功", model); + } } } diff --git a/Blog.Core.Api/Controllers/Tenant/TenantByDbController.cs b/Blog.Core.Api/Controllers/Tenant/TenantByDbController.cs new file mode 100644 index 00000000..046f7f7b --- /dev/null +++ b/Blog.Core.Api/Controllers/Tenant/TenantByDbController.cs @@ -0,0 +1,50 @@ +using Blog.Core.Common.HttpContextUser; +using Blog.Core.Controllers; +using Blog.Core.IServices.BASE; +using Blog.Core.Model; +using Blog.Core.Model.Models; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Blog.Core.Api.Controllers.Tenant; + +/// +/// 多租户-多库方案 测试 +/// +[Produces("application/json")] +[Route("api/Tenant/ByDb")] +[Authorize] +public class TenantByDbController : BaseApiController +{ + private readonly IBaseServices _services; + private readonly IUser _user; + + public TenantByDbController(IUser user, IBaseServices services) + { + _user = user; + _services = services; + } + + /// + /// 获取租户下全部业务数据
+ ///
+ /// + [HttpGet] + public async Task>> GetAll() + { + var data = await _services.Query(); + return Success(data); + } + + /// + /// 新增数据 + /// + /// + [HttpPost] + public async Task Post(SubLibraryBusinessTable data) + { + await _services.Db.Insertable(data).ExecuteReturnSnowflakeIdAsync(); + + return Success(); + } +} \ No newline at end of file diff --git a/Blog.Core.Api/Controllers/Tenant/TenantByIdController.cs b/Blog.Core.Api/Controllers/Tenant/TenantByIdController.cs new file mode 100644 index 00000000..b015bc6d --- /dev/null +++ b/Blog.Core.Api/Controllers/Tenant/TenantByIdController.cs @@ -0,0 +1,49 @@ +using Blog.Core.Common.HttpContextUser; +using Blog.Core.Controllers; +using Blog.Core.IServices.BASE; +using Blog.Core.Model; +using Blog.Core.Model.Models; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Blog.Core.Api.Controllers.Tenant; + +/// +/// 多租户-Id方案 测试 +/// +[Produces("application/json")] +[Route("api/Tenant/ById")] +[Authorize] +public class TenantByIdController : BaseApiController +{ + private readonly IBaseServices _services; + private readonly IUser _user; + + public TenantByIdController(IUser user, IBaseServices services) + { + _user = user; + _services = services; + } + + /// + /// 获取租户下全部业务数据
+ ///
+ /// + [HttpGet] + public async Task>> GetAll() + { + var data = await _services.Query(); + return Success(data); + } + + /// + /// 新增业务数据 + /// + /// + [HttpPost] + public async Task Post([FromBody] BusinessTable data) + { + await _services.Db.Insertable(data).ExecuteReturnSnowflakeIdAsync(); + return Success(); + } +} \ No newline at end of file diff --git a/Blog.Core.Api/Controllers/Tenant/TenantByTableController.cs b/Blog.Core.Api/Controllers/Tenant/TenantByTableController.cs new file mode 100644 index 00000000..6c0b110e --- /dev/null +++ b/Blog.Core.Api/Controllers/Tenant/TenantByTableController.cs @@ -0,0 +1,57 @@ +using Blog.Core.Common.HttpContextUser; +using Blog.Core.Controllers; +using Blog.Core.IServices.BASE; +using Blog.Core.Model; +using Blog.Core.Model.Models; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Blog.Core.Api.Controllers.Tenant; + +/// +/// 多租户-多表方案 测试 +/// +[Produces("application/json")] +[Route("api/Tenant/ByTable")] +[Authorize] +public class TenantByTableController : BaseApiController +{ + private readonly IBaseServices _services; + private readonly IUser _user; + + public TenantByTableController(IUser user, IBaseServices services) + { + _user = user; + _services = services; + } + + /// + /// 获取租户下全部业务数据
+ ///
+ /// + [HttpGet] + public async Task>> GetAll() + { + //查询 + // var data = await _services.Query(); + + //关联查询 + var data = await _services.Db + .Queryable() + .Includes(s => s.Child) + .ToListAsync(); + return Success(data); + } + + /// + /// 新增数据 + /// + /// + [HttpPost] + public async Task Post(MultiBusinessTable data) + { + await _services.Db.Insertable(data).ExecuteReturnSnowflakeIdAsync(); + + return Success(); + } +} \ No newline at end of file diff --git a/Blog.Core.Api/Controllers/Tenant/TenantManagerController.cs b/Blog.Core.Api/Controllers/Tenant/TenantManagerController.cs new file mode 100644 index 00000000..90133fdb --- /dev/null +++ b/Blog.Core.Api/Controllers/Tenant/TenantManagerController.cs @@ -0,0 +1,87 @@ +using Blog.Core.Controllers; +using Blog.Core.IServices; +using Blog.Core.Model; +using Blog.Core.Model.Models; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Blog.Core.Api.Controllers.Tenant; + +/// +/// 租户管理 +/// +[Produces("application/json")] +[Route("api/TenantManager")] +[Authorize] +public class TenantManagerController : BaseApiController +{ + private readonly ITenantService _services; + + public TenantManagerController(ITenantService services) + { + _services = services; + } + + + /// + /// 获取全部租户 + /// + /// + [HttpGet] + public async Task>> GetAll() + { + var data = await _services.Query(); + return Success(data); + } + + + /// + /// 获取租户信息 + /// + /// + [HttpGet("{id}")] + public async Task> GetInfo(long id) + { + var data = await _services.QueryById(id); + return Success(data); + } + + /// + /// 新增租户信息
+ /// 此处只做演示,具体要以实际业务为准 + ///
+ /// + [HttpPost] + public async Task Post(SysTenant tenant) + { + await _services.SaveTenant(tenant); + return Success(); + } + + /// + /// 修改租户信息
+ /// 此处只做演示,具体要以实际业务为准 + ///
+ /// + [HttpPut] + public async Task Put(SysTenant tenant) + { + await _services.SaveTenant(tenant); + return Success(); + } + + /// + /// 删除租户
+ /// 此处只做演示,具体要以实际业务为准 + ///
+ /// + [HttpDelete] + public async Task Delete(long id) + { + //是否删除租户库? + //要根据实际情况而定 + //例如直接删除租户库、备份租户库到xx + await _services.DeleteById(id); + return Success(); + } +} \ No newline at end of file diff --git a/Blog.Core.Api/Controllers/TopicController.cs b/Blog.Core.Api/Controllers/TopicController.cs index 1fe2d4a3..253f54ff 100644 --- a/Blog.Core.Api/Controllers/TopicController.cs +++ b/Blog.Core.Api/Controllers/TopicController.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using Blog.Core.IServices; +using Blog.Core.IServices; using Blog.Core.Model; using Blog.Core.Model.Models; using Microsoft.AspNetCore.Authorization; @@ -46,7 +44,7 @@ public async Task>> Get() // GET: api/Topic/5 [HttpGet("{id}")] - public string Get(int id) + public string Get(long id) { return "value"; } @@ -59,13 +57,13 @@ public void Post([FromBody] string value) // PUT: api/Topic/5 [HttpPut("{id}")] - public void Put(int id, [FromBody] string value) + public void Put(long id, [FromBody] string value) { } // DELETE: api/ApiWithActions/5 [HttpDelete("{id}")] - public void Delete(int id) + public void Delete(long id) { } } diff --git a/Blog.Core.Api/Controllers/TopicDetailController.cs b/Blog.Core.Api/Controllers/TopicDetailController.cs index 264fe2df..374aca24 100644 --- a/Blog.Core.Api/Controllers/TopicDetailController.cs +++ b/Blog.Core.Api/Controllers/TopicDetailController.cs @@ -1,7 +1,4 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using Blog.Core.Common.Helper; +using Blog.Core.Common.Helper; using Blog.Core.IServices; using Blog.Core.Model; using Blog.Core.Model.Models; @@ -45,7 +42,7 @@ public TopicDetailController(ITopicServices topicServices, ITopicDetailServices [AllowAnonymous] public async Task>> Get(int page = 1, string tname = "", string key = "", int intPageSize = 12) { - int tid = 0; + long tid = 0; if (string.IsNullOrEmpty(key) || string.IsNullOrWhiteSpace(key)) { @@ -59,7 +56,7 @@ public async Task>> Get(int page = 1, string if (!string.IsNullOrEmpty(tname)) { - tid = ((await _topicServices.Query(ts => ts.tName == tname)).FirstOrDefault()?.Id).ObjToInt(); + tid = ((await _topicServices.Query(ts => ts.tName == tname)).FirstOrDefault()?.Id).ObjToLong(); } @@ -84,7 +81,7 @@ public async Task>> Get(int page = 1, string // GET: api/TopicDetail/5 [HttpGet("{id}")] [AllowAnonymous] - public async Task> Get(int id) + public async Task> Get(long id) { var data = new MessageModel(); var response = id > 0 ? await _topicDetailServices.QueryById(id) : new TopicDetail(); @@ -157,7 +154,7 @@ public async Task> Update([FromBody] TopicDetail topicDetai /// // DELETE: api/ApiWithActions/5 [HttpDelete] - public async Task> Delete(int id) + public async Task> Delete(long id) { var data = new MessageModel(); if (id > 0) diff --git a/Blog.Core.Api/Controllers/TransactionController.cs b/Blog.Core.Api/Controllers/TransactionController.cs index dd6b0384..9853d985 100644 --- a/Blog.Core.Api/Controllers/TransactionController.cs +++ b/Blog.Core.Api/Controllers/TransactionController.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Blog.Core.IServices; +using Blog.Core.IServices; using Blog.Core.Model; using Blog.Core.Model.Models; using Blog.Core.Repository.UnitOfWorks; @@ -98,7 +95,7 @@ public async Task>> Get() // GET: api/Transaction/5 [HttpGet("{id}")] - public async Task> Get(int id) + public async Task> Get(long id) { return await _guestbookServices.TestTranInRepository(); } @@ -129,7 +126,7 @@ public void Post([FromBody] string value) // PUT: api/Transaction/5 [HttpPut("{id}")] - public void Put(int id, [FromBody] string value) + public void Put(long id, [FromBody] string value) { } @@ -139,7 +136,7 @@ public void Put(int id, [FromBody] string value) /// /// [HttpDelete("{id}")] - public async Task Delete(int id) + public async Task Delete(long id) { return await _guestbookServices.TestTranInRepositoryAOP(); } diff --git a/Blog.Core.Api/Controllers/UserController.cs b/Blog.Core.Api/Controllers/UserController.cs index 6a2a18fa..95137c8e 100644 --- a/Blog.Core.Api/Controllers/UserController.cs +++ b/Blog.Core.Api/Controllers/UserController.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using AutoMapper; +using AutoMapper; using Blog.Core.AuthHelper.OverWrite; using Blog.Core.Common.Helper; using Blog.Core.Common.HttpContextUser; @@ -13,7 +9,6 @@ using Blog.Core.Repository.UnitOfWorks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; namespace Blog.Core.Controllers { @@ -108,15 +103,15 @@ public async Task>> Get(int page = 1, str return Success(data.ConvertTo(_mapper)); } - private (string, List) GetFullDepartmentName(List departments, int departmentId) + private (string, List) GetFullDepartmentName(List departments, long departmentId) { var departmentModel = departments.FirstOrDefault(d => d.Id == departmentId); if (departmentModel == null) { - return ("", new List()); + return ("", new List()); } - var pids = departmentModel.CodeRelationship?.TrimEnd(',').Split(',').Select(d => d.ObjToInt()).ToList(); + var pids = departmentModel.CodeRelationship?.TrimEnd(',').Split(',').Select(d => d.ObjToLong()).ToList(); pids.Add(departmentModel.Id); var pnams = departments.Where(d => pids.Contains(d.Id)).ToList().Select(d => d.Name).ToArray(); var fullName = string.Join("/", pnams); @@ -270,7 +265,7 @@ public async Task> Put([FromBody] SysUserInfoDto sysUserInf /// // DELETE: api/ApiWithActions/5 [HttpDelete] - public async Task> Delete(int id) + public async Task> Delete(long id) { var data = new MessageModel(); if (id > 0) diff --git a/Blog.Core.Api/Controllers/UserRoleController.cs b/Blog.Core.Api/Controllers/UserRoleController.cs index d14d6a73..693a68b8 100644 --- a/Blog.Core.Api/Controllers/UserRoleController.cs +++ b/Blog.Core.Api/Controllers/UserRoleController.cs @@ -1,5 +1,4 @@ -using System.Threading.Tasks; -using AutoMapper; +using AutoMapper; using Blog.Core.IServices; using Blog.Core.Model; using Blog.Core.Model.Models; @@ -81,7 +80,7 @@ public async Task> AddRole(string roleName) /// /// [HttpGet] - public async Task> AddUserRole(int uid, int rid) + public async Task> AddUserRole(long uid, long rid) { return new MessageModel() { diff --git a/Blog.Core.Api/Controllers/ValuesController.cs b/Blog.Core.Api/Controllers/ValuesController.cs index 52eb0ea1..1347ca16 100644 --- a/Blog.Core.Api/Controllers/ValuesController.cs +++ b/Blog.Core.Api/Controllers/ValuesController.cs @@ -298,7 +298,6 @@ public object Post([FromBody] BlogArticle blogArticle, int id) /// /// [HttpPost] - [Route("TestPostPara")] [AllowAnonymous] public object TestPostPara(string name) { diff --git a/Blog.Core.Api/Controllers/WeChatCompanyController.cs b/Blog.Core.Api/Controllers/WeChatCompanyController.cs index 4fa6eea5..dc12930b 100644 --- a/Blog.Core.Api/Controllers/WeChatCompanyController.cs +++ b/Blog.Core.Api/Controllers/WeChatCompanyController.cs @@ -1,4 +1,3 @@ -using System.Threading.Tasks; using Blog.Core.IServices; using Blog.Core.Model; using Blog.Core.Model.Models; diff --git a/Blog.Core.Api/Controllers/WeChatConfigController.cs b/Blog.Core.Api/Controllers/WeChatConfigController.cs index c597cb3f..1f3b705d 100644 --- a/Blog.Core.Api/Controllers/WeChatConfigController.cs +++ b/Blog.Core.Api/Controllers/WeChatConfigController.cs @@ -1,4 +1,3 @@ -using System.Threading.Tasks; using Blog.Core.IServices; using Blog.Core.Model; using Blog.Core.Model.Models; diff --git a/Blog.Core.Api/Controllers/WeChatController.cs b/Blog.Core.Api/Controllers/WeChatController.cs index c215f563..a27762e8 100644 --- a/Blog.Core.Api/Controllers/WeChatController.cs +++ b/Blog.Core.Api/Controllers/WeChatController.cs @@ -1,11 +1,8 @@ -using System.IO; -using System.Threading.Tasks; -using Blog.Core.IServices; +using Blog.Core.IServices; using Blog.Core.Model; using Blog.Core.Model.ViewModels; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; namespace Blog.Core.Controllers { diff --git a/Blog.Core.Api/Controllers/WeChatPushLogController.cs b/Blog.Core.Api/Controllers/WeChatPushLogController.cs index 1fe1603d..af168091 100644 --- a/Blog.Core.Api/Controllers/WeChatPushLogController.cs +++ b/Blog.Core.Api/Controllers/WeChatPushLogController.cs @@ -1,4 +1,3 @@ -using System.Threading.Tasks; using Blog.Core.IServices; using Blog.Core.Model; using Blog.Core.Model.Models; diff --git a/Blog.Core.Api/Controllers/WeChatSubController.cs b/Blog.Core.Api/Controllers/WeChatSubController.cs index bd8d1759..94f982d2 100644 --- a/Blog.Core.Api/Controllers/WeChatSubController.cs +++ b/Blog.Core.Api/Controllers/WeChatSubController.cs @@ -1,4 +1,3 @@ -using System.Threading.Tasks; using Blog.Core.IServices; using Blog.Core.Model; using Blog.Core.Model.Models; diff --git a/Blog.Core.Api/Program.cs b/Blog.Core.Api/Program.cs index 8ab4b93d..2a75498e 100644 --- a/Blog.Core.Api/Program.cs +++ b/Blog.Core.Api/Program.cs @@ -1,19 +1,22 @@ - -// 以下为asp.net 6.0的写法,如果用5.0,请看Program.five.cs文件 +// 以下为asp.net 6.0的写法,如果用5.0,请看Program.five.cs文件 +using System.IdentityModel.Tokens.Jwt; +using System.Reflection; +using System.Text; using Autofac; using Autofac.Extensions.DependencyInjection; using Blog.Core; using Blog.Core.Common; +using Blog.Core.Common.Core; +using Blog.Core.Common.Helper; using Blog.Core.Common.LogHelper; -using Blog.Core.Common.Seed; using Blog.Core.Extensions; using Blog.Core.Extensions.Apollo; using Blog.Core.Extensions.Middlewares; using Blog.Core.Filter; using Blog.Core.Hubs; using Blog.Core.IServices; +using Blog.Core.Model; using Blog.Core.Tasks; -using FluentValidation.AspNetCore; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Server.Kestrel.Core; @@ -21,9 +24,6 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; -using System.IdentityModel.Tokens.Jwt; -using System.Reflection; -using System.Text; var builder = WebApplication.CreateBuilder(args); @@ -49,7 +49,6 @@ config.AddConfigurationApollo("appsettings.apollo.json"); }); - // 2、配置服务 builder.Services.AddSingleton(new AppSettings(builder.Configuration)); builder.Services.AddSingleton(new LogLock(builder.Environment.ContentRootPath)); @@ -79,7 +78,7 @@ builder.Services.AddKafkaSetup(builder.Configuration); builder.Services.AddEventBusSetup(); builder.Services.AddNacosSetup(builder.Configuration); - +builder.Services.AddInitializationHostServiceSetup(); builder.Services.AddAuthorizationSetup(); if (Permissions.IsUseIds4 || Permissions.IsUseAuthing) { @@ -114,6 +113,8 @@ //options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local; options.SerializerSettings.Converters.Add(new StringEnumConverter()); + //将long类型转为string + options.SerializerSettings.Converters.Add(new NumberConverter(NumberConverterShip.Int64)); }) //.AddFluentValidation(config => //{ @@ -129,9 +130,9 @@ builder.Services.Replace(ServiceDescriptor.Transient()); Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); - // 3、配置中间件 var app = builder.Build(); +app.ConfigureApplication(); if (app.Environment.IsDevelopment()) { @@ -182,16 +183,5 @@ endpoints.MapHub("/api2/chatHub"); }); - -var scope = app.Services.GetRequiredService().CreateScope(); -var myContext = scope.ServiceProvider.GetRequiredService(); -var tasksQzServices = scope.ServiceProvider.GetRequiredService(); -var schedulerCenter = scope.ServiceProvider.GetRequiredService(); -var lifetime = scope.ServiceProvider.GetRequiredService(); -app.UseSeedDataMiddle(myContext, builder.Environment.WebRootPath); -app.UseQuartzJobMiddleware(tasksQzServices, schedulerCenter); -app.UseConsulMiddle(builder.Configuration, lifetime); -app.ConfigureEventBus(); - // 4、运行 -app.Run(); +app.Run(); \ No newline at end of file diff --git a/Blog.Core.Api/Startup.cs b/Blog.Core.Api/Startup.cs index b341819e..4f99b621 100644 --- a/Blog.Core.Api/Startup.cs +++ b/Blog.Core.Api/Startup.cs @@ -1,34 +1,30 @@ -using Autofac; +using System.IdentityModel.Tokens.Jwt; +using System.Reflection; +using System.Text; +using Autofac; using Blog.Core.Common; +using Blog.Core.Common.Helper; using Blog.Core.Common.LogHelper; using Blog.Core.Common.Seed; using Blog.Core.Extensions; +using Blog.Core.Extensions.Middlewares; using Blog.Core.Filter; using Blog.Core.Hubs; using Blog.Core.IServices; +using Blog.Core.Model; using Blog.Core.Tasks; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Server.Kestrel.Core; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Hosting; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; -using System.IdentityModel.Tokens.Jwt; -using System.Reflection; -using System.Text; -using Blog.Core.Extensions.Middlewares; namespace Blog.Core { public class Startup { - private IServiceCollection _services; public Startup(IConfiguration configuration, IWebHostEnvironment env) @@ -75,7 +71,7 @@ public void ConfigureServices(IServiceCollection services) services.AddEventBusSetup(); services.AddNacosSetup(Configuration); - + services.AddInitializationHostServiceSetup(); // 授权+认证 (jwt or ids4) services.AddAuthorizationSetup(); if (Permissions.IsUseIds4) @@ -95,7 +91,7 @@ public void ConfigureServices(IServiceCollection services) services.Configure(x => x.AllowSynchronousIO = true) .Configure(x => x.AllowSynchronousIO = true); - + services.AddDistributedMemoryCache(); services.AddSession(); services.AddHttpPollySetup(); @@ -129,12 +125,14 @@ public void ConfigureServices(IServiceCollection services) options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local; //添加Enum转string options.SerializerSettings.Converters.Add(new StringEnumConverter()); + //将long类型转为string + options.SerializerSettings.Converters.Add(new NumberConverter(NumberConverterShip.Int64)); }); services.Replace(ServiceDescriptor.Transient()); _services = services; - //支持编码大全 例如:支持 System.Text.Encoding.GetEncoding("GB2312") System.Text.Encoding.GetEncoding("GB18030") + //支持编码大全 例如:支持 System.Text.Encoding.GetEncoding("GB2312") System.Text.Encoding.GetEncoding("GB18030") Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); } @@ -150,11 +148,11 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, MyContex { // Ip限流,尽量放管道外层 app.UseIpLimitMiddle(); - // 记录请求与返回数据 + // 记录请求与返回数据 app.UseRequestResponseLogMiddle(); // 用户访问记录(必须放到外层,不然如果遇到异常,会报错,因为不能返回流) app.UseRecordAccessLogsMiddle(); - // signalr + // signalr app.UseSignalRSendMiddle(); // 记录ip请求 app.UseIpLogMiddle(); @@ -214,7 +212,6 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, MyContex // 开启异常中间件,要放到最后 //app.UseExceptionHandlerMidd(); - app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( @@ -225,15 +222,13 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, MyContex }); // 生成种子数据 - app.UseSeedDataMiddle(myContext, Env.WebRootPath); + //app.UseSeedDataMiddle(myContext, Env.WebRootPath); // 开启QuartzNetJob调度服务 - app.UseQuartzJobMiddleware(tasksQzServices, schedulerCenter); + //app.UseQuartzJobMiddleware(tasksQzServices, schedulerCenter); // 服务注册 - app.UseConsulMiddle(Configuration, lifetime); + //app.UseConsulMiddle(Configuration, lifetime); // 事件总线,订阅服务 - app.ConfigureEventBus(); - + //app.ConfigureEventBus(); } - } -} +} \ No newline at end of file diff --git a/Blog.Core.Api/appsettings.json b/Blog.Core.Api/appsettings.json index ea678174..dd2f2990 100644 --- a/Blog.Core.Api/appsettings.json +++ b/Blog.Core.Api/appsettings.json @@ -1,98 +1,98 @@ { - "urls": "http://*:9291", //web服务端口,如果用IIS部署,把这个去掉 - "Logging": { - "LogLevel": { - "Default": "Information", //加入Default否则log4net本地写入不了日志 - "Blog.Core.AuthHelper.ApiResponseHandler": "Error" + "urls": "http://*:9291", //web服务端口,如果用IIS部署,把这个去掉 + "Logging": { + "LogLevel": { + "Default": "Information", //加入Default否则log4net本地写入不了日志 + "Blog.Core.AuthHelper.ApiResponseHandler": "Error" + }, + "Debug": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Warning" + } + }, + "Console": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Warning", + "Microsoft.Hosting.Lifetime": "Debug" + } + }, + "Log4Net": { + "Name": "Blog.Core" + } }, - "Debug": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Warning" - } + "AllowedHosts": "*", + "Redis": { + "ConnectionString": "127.0.0.1:6319,password=admin" }, - "Console": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Warning", - "Microsoft.Hosting.Lifetime": "Debug" - } + "RabbitMQ": { + "Enabled": false, + "Connection": "118.25.251.13", + "UserName": "", + "Password": "!", + "RetryCount": 3 }, - "Log4Net": { - "Name": "Blog.Core" - } - }, - "AllowedHosts": "*", - "Redis": { - "ConnectionString": "127.0.0.1:6319,password=admin" - }, - "RabbitMQ": { - "Enabled": false, - "Connection": "118.25.251.13", - "UserName": "", - "Password": "!", - "RetryCount": 3 - }, - "Kafka": { - "Enabled": false, - "Servers": "localhost:9092", - "Topic": "blog", - "GroupId": "blog-consumer", - "NumPartitions": 3 //主题分区数量 - }, - "EventBus": { - "Enabled": false, - "SubscriptionClientName": "Blog.Core" - }, - "AppSettings": { - "RedisCachingAOP": { - "Enabled": false - }, - "MemoryCachingAOP": { - "Enabled": true - }, - "LogAOP": { - "Enabled": true, - "LogToFile": { - "Enabled": false - }, - "LogToDB": { - "Enabled": true - } + "Kafka": { + "Enabled": false, + "Servers": "localhost:9092", + "Topic": "blog", + "GroupId": "blog-consumer", + "NumPartitions": 3 //主题分区数量 }, - "TranAOP": { - "Enabled": true + "EventBus": { + "Enabled": false, + "SubscriptionClientName": "Blog.Core" }, - "SqlAOP": { - "Enabled": true, - "LogToFile": { - "Enabled": false - }, - "LogToDB": { - "Enabled": false - }, - "LogToConsole": { - "Enabled": true - } + "AppSettings": { + "RedisCachingAOP": { + "Enabled": false + }, + "MemoryCachingAOP": { + "Enabled": true + }, + "LogAOP": { + "Enabled": true, + "LogToFile": { + "Enabled": false + }, + "LogToDB": { + "Enabled": false + } + }, + "TranAOP": { + "Enabled": true + }, + "SqlAOP": { + "Enabled": true, + "LogToFile": { + "Enabled": false + }, + "LogToDB": { + "Enabled": false + }, + "LogToConsole": { + "Enabled": true + } + }, + "Date": "2018-08-28", + "SeedDBEnabled": true, //只生成表结构 + "SeedDBDataEnabled": true, //生成表,并初始化数据 + "Author": "Blog.Core", + "SvcName": "", // /svc/blog + "UseLoadTest": false }, - "Date": "2018-08-28", - "SeedDBEnabled": true, //只生成表结构 - "SeedDBDataEnabled": true, //生成表,并初始化数据 - "Author": "Blog.Core", - "SvcName": "", // /svc/blog - "UseLoadTest": false - }, - // 请配置MainDB为你想要的主库的ConnId值,并设置对应的Enabled为true; - // *** 单库操作,把 MutiDBEnabled 设为false ***; - // *** 多库操作,把 MutiDBEnabled 设为true,其他的从库Enabled也为true **; - // 具体配置看视频:https://www.bilibili.com/video/BV1BJ411B7mn?p=6 + // 请配置MainDB为你想要的主库的ConnId值,并设置对应的Enabled为true; + // *** 单库操作,把 MutiDBEnabled 设为false ***; + // *** 多库操作,把 MutiDBEnabled 设为true,其他的从库Enabled也为true **; + // 具体配置看视频:https://www.bilibili.com/video/BV1BJ411B7mn?p=6 - "MainDB": "WMBLOG_SQLITE", //当前项目的主库,所对应的连接字符串的Enabled必须为true - "MutiDBEnabled": false, //是否开启多库模式 - "CQRSEnabled": false, //是否开启读写分离模式,必须是单库模式,且数据库类型一致,比如都是SqlServer - "DBS": [ - /* + "MainDB": "WMBLOG_MYSQL", //当前项目的主库,所对应的连接字符串的Enabled必须为true + "MutiDBEnabled": true, //是否开启多库模式 + "CQRSEnabled": false, //是否开启读写分离模式,必须是单库模式,且数据库类型一致,比如都是SqlServer + "DBS": [ + /* 对应下边的 DBType MySql = 0, SqlServer = 1, @@ -102,225 +102,230 @@ Dm = 5,//达梦 Kdbndp = 6,//人大金仓 */ - { - "ConnId": "WMBLOG_SQLITE", - "DBType": 2, - "Enabled": true, - "HitRate": 50, // 值越大,优先级越高 - "Connection": "WMBlog.db" //sqlite只写数据库名就行 - }, - { - "ConnId": "WMBLOG_MSSQL_1", - "DBType": 1, - "Enabled": false, - "HitRate": 40, - "Connection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=WMBLOG_MSSQL_1;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", - "ProviderName": "System.Data.SqlClient" - }, - { - "ConnId": "WMBLOG_MSSQL_2", - "DBType": 1, - "Enabled": false, - "HitRate": 30, - "Connection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=WMBLOG_MSSQL_2;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", - "ProviderName": "System.Data.SqlClient" + { + "ConnId": "WMBLOG_SQLITE", + "DBType": 2, + "Enabled": false, + "HitRate": 50, // 值越大,优先级越高 + "Connection": "WMBlog.db" //sqlite只写数据库名就行 + }, + { + "ConnId": "WMBLOG_MSSQL_1", + "DBType": 1, + "Enabled": false, + "HitRate": 40, + "Connection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=WMBLOG_MSSQL_1;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", + "ProviderName": "System.Data.SqlClient" + }, + { + "ConnId": "WMBLOG_MSSQL_2", + "DBType": 1, + "Enabled": false, + "HitRate": 30, + "Connection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=WMBLOG_MSSQL_2;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", + "ProviderName": "System.Data.SqlClient" + }, + { + "ConnId": "WMBLOG_MYSQL", + "DBType": 0, + "Enabled": true, + "HitRate": 20, + "Connection": "server=localhost;Database=blog;Uid=root;Pwd=root;Port=3306;Allow User Variables=True;" + }, + { + "ConnId": "WMBLOG_MYSQL_2", + "DBType": 0, + "Enabled": true, + "HitRate": 20, + "Connection": "server=localhost;Database=trojan;Uid=root;Pwd=root;Port=3306;Allow User Variables=True;" + }, + { + "ConnId": "WMBLOG_ORACLE", + "DBType": 3, + "Enabled": false, + "HitRate": 10, + "Connection": "Data Source=127.0.0.1/ops;User ID=OPS;Password=123456;Persist Security Info=True;Connection Timeout=60;" + }, + { + "ConnId": "WMBLOG_DM", + "DBType": 5, + "Enabled": false, + "HitRate": 10, + "Connection": "PORT=5236;DATABASE=DAMENG;HOST=localhost;PASSWORD=SYSDBA;USER ID=SYSDBA;" + }, + { + "ConnId": "WMBLOG_KDBNDP", + "DBType": 6, + "Enabled": false, + "HitRate": 10, + "Connection": "Server=127.0.0.1;Port=54321;UID=SYSTEM;PWD=system;database=SQLSUGAR4XTEST1;" + } + ], + "Audience": { + "Secret": "sdfsdfsrty45634kkhllghtdgdfss345t678fs", //不要太短,16位+ + "SecretFile": "C:\\my-file\\blog.core.audience.secret.txt", //安全。内容就是Secret + "Issuer": "Blog.Core", + "Audience": "wr" }, - { - "ConnId": "WMBLOG_MYSQL", - "DBType": 0, - "Enabled": false, - "HitRate": 20, - "Connection": "server=.;Database=ddd;Uid=root;Pwd=123456;Port=10060;Allow User Variables=True;" - }, - { - "ConnId": "WMBLOG_MYSQL_2", - "DBType": 0, - "Enabled": true, - "HitRate": 20, - "Connection": "server=localhost;Database=blogcore001;Uid=root;Pwd=root;Port=3306;Allow User Variables=True;" - }, - { - "ConnId": "WMBLOG_ORACLE", - "DBType": 3, - "Enabled": false, - "HitRate": 10, - "Connection": "Data Source=127.0.0.1/ops;User ID=OPS;Password=123456;Persist Security Info=True;Connection Timeout=60;" - }, - { - "ConnId": "WMBLOG_DM", - "DBType": 5, - "Enabled": false, - "HitRate": 10, - "Connection": "PORT=5236;DATABASE=DAMENG;HOST=localhost;PASSWORD=SYSDBA;USER ID=SYSDBA;" - }, - { - "ConnId": "WMBLOG_KDBNDP", - "DBType": 6, - "Enabled": true, - "HitRate": 10, - "Connection": "Server=127.0.0.1;Port=54321;UID=SYSTEM;PWD=system;database=SQLSUGAR4XTEST1;" - } - ], - "Audience": { - "Secret": "sdfsdfsrty45634kkhllghtdgdfss345t678fs", //不要太短,16位+ - "SecretFile": "C:\\my-file\\blog.core.audience.secret.txt", //安全。内容就是Secret - "Issuer": "Blog.Core", - "Audience": "wr" - }, - "Mongo": { - "ConnectionString": "mongodb://nosql.data", - "Database": "BlogCoreDb" - }, - "Startup": { - "Domain": "http://localhost:9291", - "Cors": { - "PolicyName": "CorsIpAccess", //策略名称 - "EnableAllIPs": false, //当为true时,开放所有IP均可访问。 - // 支持多个域名端口,注意端口号后不要带/斜杆:比如localhost:8000/,是错的 - // 注意,http://127.0.0.1:1818 和 http://localhost:1818 是不一样的 - "IPs": "http://127.0.0.1:2364,http://localhost:2364,http://127.0.0.1:6688,http://localhost:6688" + "Mongo": { + "ConnectionString": "mongodb://nosql.data", + "Database": "BlogCoreDb" }, - "AppConfigAlert": { - "Enabled": true + "Startup": { + "Domain": "http://localhost:9291", + "Cors": { + "PolicyName": "CorsIpAccess", //策略名称 + "EnableAllIPs": false, //当为true时,开放所有IP均可访问。 + // 支持多个域名端口,注意端口号后不要带/斜杆:比如localhost:8000/,是错的 + // 注意,http://127.0.0.1:1818 和 http://localhost:1818 是不一样的 + "IPs": "http://127.0.0.1:2364,http://localhost:2364,http://127.0.0.1:6688,http://localhost:6688" + }, + "AppConfigAlert": { + "Enabled": true + }, + "ApiName": "Blog.Core", + "IdentityServer4": { + "Enabled": false, // 这里默认是false,表示使用jwt,如果设置为true,则表示系统使用Ids4模式 + "AuthorizationUrl": "http://localhost:5004", // 认证中心域名 + "ApiName": "blog.core.api" // 资源服务器 + }, + "Authing": { + "Enabled": false, + "Issuer": "https://uldr24esx31h-demo.authing.cn/oidc", + "Audience": "63d51c4205c2849803be5178", + "JwksUri": "https://uldr24esx31h-demo.authing.cn/oidc/.well-known/jwks.json" + }, + "RedisMq": { + "Enabled": false //redis 消息队列 + }, + "MiniProfiler": { + "Enabled": false //性能分析开启 + }, + "Nacos": { + "Enabled": false //Nacos注册中心 + } }, - "ApiName": "Blog.Core", - "IdentityServer4": { - "Enabled": false, // 这里默认是false,表示使用jwt,如果设置为true,则表示系统使用Ids4模式 - "AuthorizationUrl": "http://localhost:5004", // 认证中心域名 - "ApiName": "blog.core.api" // 资源服务器 + "Middleware": { + "RequestResponseLog": { + "Enabled": true, + "LogToFile": { + "Enabled": false + }, + "LogToDB": { + "Enabled": false + } + }, + "IPLog": { + "Enabled": true, + "LogToFile": { + "Enabled": false + }, + "LogToDB": { + "Enabled": false + } + }, + "RecordAccessLogs": { + "Enabled": true, + "LogToFile": { + "Enabled": false + }, + "LogToDB": { + "Enabled": false + }, + "IgnoreApis": "/api/permission/getnavigationbar,/api/monitor/getids4users,/api/monitor/getaccesslogs,/api/monitor/server,/api/monitor/getactiveusers,/api/monitor/server," + }, + "SignalR": { + "Enabled": false + }, + "SignalRSendLog": { + "Enabled": false + }, + "QuartzNetJob": { + "Enabled": true + }, + "Consul": { + "Enabled": false + }, + "IpRateLimit": { + "Enabled": true + } }, - "Authing": { - "Enabled": false, - "Issuer": "https://uldr24esx31h-demo.authing.cn/oidc", - "Audience": "63d51c4205c2849803be5178", - "JwksUri": "https://uldr24esx31h-demo.authing.cn/oidc/.well-known/jwks.json" - }, - "RedisMq": { - "Enabled": false //redis 消息队列 - }, - "MiniProfiler": { - "Enabled": false //性能分析开启 - }, - "Nacos": { - "Enabled": false //Nacos注册中心 - } - }, - "Middleware": { - "RequestResponseLog": { - "Enabled": true, - "LogToFile": { - "Enabled": false - }, - "LogToDB": { - "Enabled": true - } - }, - "IPLog": { - "Enabled": true, - "LogToFile": { - "Enabled": false - }, - "LogToDB": { - "Enabled": true - } - }, - "RecordAccessLogs": { - "Enabled": true, - "LogToFile": { - "Enabled": false - }, - "LogToDB": { - "Enabled": true - }, - "IgnoreApis": "/api/permission/getnavigationbar,/api/monitor/getids4users,/api/monitor/getaccesslogs,/api/monitor/server,/api/monitor/getactiveusers,/api/monitor/server," + "IpRateLimiting": { + "EnableEndpointRateLimiting": true, //False: globally executed, true: executed for each + "StackBlockedRequests": false, //False: Number of rejections should be recorded on another counter + "RealIpHeader": "X-Real-IP", + "ClientIdHeader": "X-ClientId", + "IpWhitelist": [], //白名单 + "EndpointWhitelist": [ "get:/api/xxx", "*:/api/yyy" ], + "ClientWhitelist": [ "dev-client-1", "dev-client-2" ], + "QuotaExceededResponse": { + "Content": "{{\"status\":429,\"msg\":\"访问过于频繁,请稍后重试\",\"success\":false}}", + "ContentType": "application/json", + "StatusCode": 429 + }, + "HttpStatusCode": 429, //返回状态码 + "GeneralRules": [ //api规则,结尾一定要带* + { + "Endpoint": "*:/api/blog*", + "Period": "1m", + "Limit": 20 + }, + { + "Endpoint": "*/api/*", + "Period": "1s", + "Limit": 3 + }, + { + "Endpoint": "*/api/*", + "Period": "1m", + "Limit": 30 + }, + { + "Endpoint": "*/api/*", + "Period": "12h", + "Limit": 500 + } + ] + }, - "SignalR": { - "Enabled": false + "ConsulSetting": { + "ServiceName": "BlogCoreService", + "ServiceIP": "localhost", + "ServicePort": "9291", + "ServiceHealthCheck": "/healthcheck", + "ConsulAddress": "http://localhost:8500" }, - "SignalRSendLog": { - "Enabled": false + "PayInfo": { //建行聚合支付信息 + "MERCHANTID": "", //商户号 + "POSID": "", //柜台号 + "BRANCHID": "", //分行号 + "pubKey": "", //公钥 + "USER_ID": "", //操作员号 + "PASSWORD": "", //密码 + "OutAddress": "http://127.0.0.1:12345" //外联地址 }, - "QuartzNetJob": { - "Enabled": true + "nacos": { + "ServerAddresses": [ "http://localhost:8848" ], // nacos 连接地址 + "DefaultTimeOut": 15000, // 默认超时时间 + "Namespace": "public", // 命名空间 + "ListenInterval": 10000, // 监听的频率 + "ServiceName": "blog.Core.Api", // 服务名 + "Port": "9291", // 服务端口号 + "RegisterEnabled": true // 是否直接注册nacos }, - "Consul": { - "Enabled": false + "LogFiedOutPutConfigs": { + "tcpAddressHost": "", // 输出elk的tcp连接地址 + "tcpAddressPort": 0, // 输出elk的tcp端口号 + "ConfigsInfo": [ // 配置的输出elk节点内容 常用语动态标识 + { + "FiedName": "applicationName", + "FiedValue": "Blog.Core.Api" + } + ] }, - "IpRateLimit": { - "Enabled": true + "trojan": { //科学上网订阅 + "normalApi": "https://apiurl/api/Trojan/RSS?id=", + "clashApi": "https://clashurl/sub?target=clash&insert=false&url=", + "clashApiBackup": "https://clashurl/sub?target=clash&insert=false&url=" } - }, - "IpRateLimiting": { - "EnableEndpointRateLimiting": true, //False: globally executed, true: executed for each - "StackBlockedRequests": false, //False: Number of rejections should be recorded on another counter - "RealIpHeader": "X-Real-IP", - "ClientIdHeader": "X-ClientId", - "IpWhitelist": [], //白名单 - "EndpointWhitelist": [ "get:/api/xxx", "*:/api/yyy" ], - "ClientWhitelist": [ "dev-client-1", "dev-client-2" ], - "QuotaExceededResponse": { - "Content": "{{\"status\":429,\"msg\":\"访问过于频繁,请稍后重试\",\"success\":false}}", - "ContentType": "application/json", - "StatusCode": 429 - }, - "HttpStatusCode": 429, //返回状态码 - "GeneralRules": [ //api规则,结尾一定要带* - { - "Endpoint": "*:/api/blog*", - "Period": "1m", - "Limit": 20 - }, - { - "Endpoint": "*/api/*", - "Period": "1s", - "Limit": 3 - }, - { - "Endpoint": "*/api/*", - "Period": "1m", - "Limit": 30 - }, - { - "Endpoint": "*/api/*", - "Period": "12h", - "Limit": 500 - } - ] - - }, - "ConsulSetting": { - "ServiceName": "BlogCoreService", - "ServiceIP": "localhost", - "ServicePort": "9291", - "ServiceHealthCheck": "/healthcheck", - "ConsulAddress": "http://localhost:8500" - }, - "PayInfo": { //建行聚合支付信息 - "MERCHANTID": "", //商户号 - "POSID": "", //柜台号 - "BRANCHID": "", //分行号 - "pubKey": "", //公钥 - "USER_ID": "", //操作员号 - "PASSWORD": "", //密码 - "OutAddress": "http://127.0.0.1:12345" //外联地址 - }, - "nacos": { - "ServerAddresses": [ "http://localhost:8848" ], // nacos 连接地址 - "DefaultTimeOut": 15000, // 默认超时时间 - "Namespace": "public", // 命名空间 - "ListenInterval": 10000, // 监听的频率 - "ServiceName": "blog.Core.Api", // 服务名 - "Port": "9291", // 服务端口号 - "RegisterEnabled": true // 是否直接注册nacos - }, - "LogFiedOutPutConfigs": { - "tcpAddressHost": "", // 输出elk的tcp连接地址 - "tcpAddressPort": 0, // 输出elk的tcp端口号 - "ConfigsInfo": [ // 配置的输出elk节点内容 常用语动态标识 - { - "FiedName": "applicationName", - "FiedValue": "Blog.Core.Api" - } - ] - } } diff --git a/Blog.Core.Api/wwwroot/BlogCore.Data.excel/SysUserInfo.xlsx b/Blog.Core.Api/wwwroot/BlogCore.Data.excel/SysUserInfo.xlsx index 51c0e125ec7a2b31b7d3e932bee4a0cde662b4d0..6a1e4ca6b01b92e1ab3095a7090a48ed41119a21 100644 GIT binary patch delta 4254 zcmZ9PcTf{*v&MsT1OkMjbV89XMF|2*=+a9lqJZ>XgiwTtL8>4<^p1cS2{rU8geKB! zM0yF)0|L@T;Ck+zIp==g{3V;aVpwD&a#8&_Dprn@P^Y&S)1<9e8GrYw9}3m2*N9{? zi;LqVvpfMTvUaGRcvYj0*|~-?Km)mV7)$xkDLl)=?Zqd+3^a63kHyNFM5yr%<7e>A z15d^kLl>k&MT~K=8!L=YmUV^0d?lj$E95-g)IwIIngQUb5eBM+FZ;#`+lfecW@BlU z;AoQ%c4$_E6fwz9#fhO_<>Cgk*yEJc)I@Uz#=2)NYidyPs>n|3ZWDF_XflRQS<&f8mXurGhUp_X^F;%1WmjmYa8y-QBTH zy}gGiY2Un;f5&9GIm&E6`(pwXw-LCSxo=5}id34xF9qDts0IaE|1d?~ zD6Tm~tEW=$kuJCLcyqv9rc?i|cl(RkVI%jRk6M~35eLO5eulf$Vt6AHjoq&Fv7RUA zH5m6T@}z)ks@q2kBW(`-Y}8UhHo!O^dJdfU$0zYUEG5!b8PZSU^R&{wgV^5B@DF{L zY`NM$zx+Jb)zx`H>oMw6^-(5~|5yHj(bSZKk>c`4@5!g}QE-v&%EkpQsilu*46;{B}GVudef&G+7H zFB;eRt`mh=luo)UkzvDzsSsYhdoOxs1kJnzg5`V`Rl@^Q21G^SHajwh2LsMBS_N{Y zL$S#$pU1P^{IQp(b^&SMjdyR#a!Z*is39D_J2AMnx(H5mVl2rGY0yX=`<04$@>cWs z4Z8QtD$LQ?B7t^Q37kp7YO+SR$T|31v17WMHZ!OfdKEI}v4hu9h^`X&zHemsUfiPy z@7F!j!lz-Qo!Qi;BU;B&vkOY3J~E(LVtnzgN)LWzR+b7AY!HtJ4y$}U_-p5OxCYd9 z>@ei2#Pu1c@NsWt7%9^1uruS?BwT~^%VEAFwqQ6>R|2RsHojbg(C4*BP{+JM90MyY z5)6M#hX?{S_u>{slBOHvmvgbUE0Fl`BT=HoCMT_3%!-J)BN1eC*JR*(PxpMoU zIuCJrRn?uj2^onETin3#QrdNXUbpBELXd_Euo{$wnXi6NUntP70pGj3KM2Rh(4K3v z8~CR2CUMuNY^t3UMKnQOD@atg!=|Q_N(Az|qR3j<1=KBM_F~Vspb^^uZ|5~px5Ph*Z*zKNQ8C`s}MU}_?JlP@bSF;6a>XKZ< z4L0$_)n)X3&Sa|~g}ojXzLtIr(T_ghSobRSsZ#v;e@>)t?)D zbrqSl=sgc*hz0z_#7M>C$rQhV4*%hnb!ehM&Y_3pvjG>sQc|r}LWW5#rnxLN zAY;M!vkl*Xu|iRb=9eHS+(i82yY!Axz-UA&Inh6kd{0S~b>%>#7En|jM}B;)UHg&n z5++{Lwwzyv)FsqZD`(vZ^#7h{f-JDVP-=OGEQIuz^r|aq==G8^duYK8CxFS{R-SxS z7kH=L`VnSJEGxE%wy7B5{_<;u+=IyYXi!^!ntfBzoXTeEmUwSg>qPO$;HrA;YT0P^ zYrTO2ai(+;4RYA>{0*4eZMqJb!^d8Y#lrTmj!NTV2SgNJ*h!v+HaB;Ry>T@wX_a;Z zsi*F#7kLIdZG$bOBI0N~I~$}uqixtjoQMtc+bNa6Xcx64Uf5k@kF?!Ke2zLII00L| zEd>}G{W^YMYf5l)KO)E^xebBaWS>(8E1~WAtojNJo#G2^Ey}V^%Fc( zJ4^O!eAerxHPt>%<5we1a>(eBab2})+79zvFX6ay{i8sT@#oVpIuxN-K2HAG2K8v6NPv$awl7sQInV$yXVL1!6K^Br}Ae~U)ldR>09AT zChr6$peNRLBS6gnExS(5_5`+sE~>XwCdZfF_`J>UFunDuXCgpP;75f z)?AWF17{T1(Kd7z5m@^j&Xs9FRSiS_p8RARPv)iAV}IWHee(o}ZDUlULKg6Vtn^3$ zfH^7vfbRdC3fw=~(+U2!Pr;aXC9SJ7|42FI(Ufc+`irL)u3Zxf&Ta6Y8rnC3`lpC7|*)-i~xXuC5sfK9y_+GhJfwH_9lJc~fyqA!n1eQZvkeRki{te!XM z+h|8i%o~0tUS)cb*Y}l`K82QZ$1(@UEf|Rvq6FWr-h;5S)G<l~louj^Hm8cfxR z<4>V_{YWbVNwgqR$l5aEzg>e*CX7j!cxXI{_jW$9!eJ;eXsL3~WtFHPXh_(Z+wj+? zHh;AFWENucpf=I|O?vd?k7k3nSGk#U-axW<@pOJsftepu1dYB#3#Z@!T-z0)bdRa! zBe^mIm+oUBG`kT}+!A~9VU~3nA0pM1CDTUJ1>;ST6oJr*k=f1!%xX(U|HtmZ)YP>E z<}Ay~a~4W9Tq9$@q?g0#TZQf=vyy9(tPa?w23P%u8@7SZ7eB5gO(<+LdeY_|UDmi- z(4Fo+=4wYx*RZd)I-hga)x^*?7ua$c;6p_^qmJEhbLyL3ZS8DF-JHvsp+EvE<_E4r z`mrEVrOo(NUfKZfojN+c0Pp4Yv%j`{ipRKnRsV{XQR`Dqqp;0rbErKtr*$|-K8aRi z7Oqbqtl)seS{S#+?4MSTmiG@g4+198_9M`l>j*-7g{Wz_+Xu!kcPPI`rEk8NhGb?=!tr+^OO;AA z`Ye~NTzc|&^6pQ8RM~edDJ6dq9u0j8M)7frBM0xiQ|gj-nvh;0fscdEif&P|RTz)n zo=QCuzelu`-6N}gE@8ee7SC$^t0Hv>Z8RgDJN`H${&SRGkf>XzwML2D8%N#X0d>>C z(yEHqH%j2dHFR`7uI{cGYhpc1a(jk@ex}o1(BpA(Mr^qDyK)ST9EZwN{et|usw%Xi zIkK6-gPXk4J4u8G9T6d=%};Ec<0pH?(IfoRA)ITihd~h^^WpN@y1~JY1|($fx6Q$Z z^+AY5S*`?~`<95G8H5G%qsQOr;0%GifX+-}hFe&z31Tn_2%mu?f?j85I>30PMTNP| zZpDsDjptT@dq-ExOe<&SvX`9lhdf7B{;Ly4u|U~HyBV%%)ZFKf3%`woRH{OL8gZjCa1DfrjG?Gv9-zXG42^Lfq-RKS+?tc zb?0VMJ~_$V;gTpXsdQrHa^+F;BqiE20KW3SqALdR%Cs4vdF*+Tr|KdSw?|5lF3O$l z?hh8tfHp-<{quq}MmYjf384c~+?w1%BHeiE#ap7`n6bp`F(6tJFsrxa>^;zcYY>+;IuHz z{2X`VH`(8Qh!kL?OG?HJpa7t%Nhz?Jyw^yn6TZGYzeNH7>|>4jrAUz1)zrSeLf!WQ z%ln?PrnX7qUnHxyU7k2EVh+}&^Weyn1(f?sBkP|eqq-#?X)a`fMJ2{#05BZj;Zj`<$N)ovk-hFvC#OPXa3Y8eWv z`ZD$*v)3f4vhB!UQL`d%Jh%o^WSqDuavE8`Tg5>V?m&dTqUYYSOzi3)$@_UlA3qcY z`y_>bK6USh;FCR+d<)RVpuck$+sWzB`}^Kfm4)Ft8a+KwC0|6q8xFj9G$0zp)Vny2 zf!@5ZW%Yi)_q2iO#jw^)FgRB1o5)J$p^4-Vi0(9VsW8l>)UlE2lS|sdI~UJ3tQ`X46}`-1=g delta 4220 zcmZWsbyO2<-yR?_T0$izDH|P94kRQb2GSwjATbc7VM-~2)aVVVQ3FOx2?zosq+~Ec zBoq*7Bt+`-dEWCr&-uRZ{_8&HzJCAwuKQfkZeDI)GfRQkpa=*VCj|iXC;$Kk000o= zB_0TW?CA)Hdx`~ldtpq6eP&e{JIsG5ScJ-5kkHmT76LS06U+=z0@pF_mf}IELtL9J zJ|D8Cq*85aS8)8c8hIoG^>m_IN)@>F@gm{m2$c=b_)z3BSmlX*ET9qnE+bw<=w(8- zh5GleGl-sTK4GFoPnNDKUqj&?DEqrGKMS3wGRXZwQb5Xm$T*Y+1O@~3{Jx~Ju`Xuo zraRK&%1czD@6d5O2pdn(P7>H(Kn^R{4FmKt9EPZi-{@}YB1Mf&oukdR>*om{2KuDV$lh46G_@OJ&UVzOCrI%Ddwdc$@s zDv9ESf8Tnrs11$Vi~>4G$IsahywbZ?Q()6VQWGB+4J+k#4_ew3oQY$W=^sf}kq`vZTtNNOtH&kb0^y3k+ z*~+QXFJ26|G7%ctcRK%*E48oZ+rV>#`eYa3=L_wqER*T}BNJhoRq$6igI0!L?9{-= zam4Tl()9h=%*e8_ws-4v3?X0br5lpkM|!rK&)?4Pkax_(xembL3uF^5G4>{mOmQ^1 zl`@X`d;}8J&6Y3AE?@UP*Vyl6^u2D2@Vc!e@p@P+1Ajli|$>2)WPU1*gTSzYfeo+eJ5 zG1FdZT>Ig(inneY=@Otu9X4h4AMvf)_cBfaq`=X7svY381L7p{2EuQU80 zHE`S)t{)s&XF3*7vF5vS7)bIHzs{;O0Z)=OrKPm7Ma&ZFJ$(6t^znjvD*BNNp)dA5 zXXBmDyBCT%_oI(s@KNPg6s0My;-7)?2Di8nxDowqyYEva%_ILn!mLDpmgHXM6TLnO=w?B?%L!k;HUB#Yjg#y!}V(5njzpaW9NXx&iYAL-`A|J07S z<2*~^u=(sKW9i<3PKnBFPt-VG7r$770MTnE#ou4aKF#T7#;FN-$Pd*$f%@N<9x)@2 z9IVQ&4gBokUpv)oJ}^_7DVXgd-xd(E8S|JqDL49SjjhnxNNUt1)QV2ya+E1ZW|tce zIrOWhfQ8J_I$GDr$6hy@lqqBy6<|G)S?Rt0HeMJTkG2Z!gOy;RB8XgmESFe087j@(dKTowJ z66h&stt4(Qi=D(de>*%`+t?_Ux6z+FoZIOvY8noL-psv3N94}inaS#%C&j z9C8f{_;GsVb2oSa2Nox)-vpN+ptbSLar!KNypwUK@+s>*YUr%>9$TjMpeLvyYjB4S z@5ZgV^u#p&IzoyNu6`^Iy3N`r-ve~V@NCe#C-Ree%{CJS-v{#-0df=70i(=ufIK)q z>9Y_V4F*8t;NZY z>2a_ZZ$yj+O$aZ`;odLHmoj7R*dp{$-s&1&(sS&r&_=VDda<-^_Z<97r9Mn4S29nN zwMbn>NiDGmO1;t}P@xcTZz|M($L^M$bX}e9=j%_SC$O(5m{MgACNJ?Dz}(Q~$Pate z$F?|_GxWe&vIwT}I_vjTY!-Wj$FKAyV#7=<_CEHlW{iFnt0R&p3R^RLM;5$LQye5L z#9S-L4DY?uEAS|KxJfp8cvtsBHEn0fux#P7PtnX2qHOGtD5UBo<=ouP@|GOG`t2DV zZqqv3z`GRIEEP}w|K@HI&K7+%DzI@FxsKUMoyrQ$&@By8{#C}Ymnw1r^P zZ=MpGU?`7y$!+(dmy1?l3EYSP)DK)$qe5tVy*bk-0|26c007;80*e2WU@sT{zv4-c z+1;c$*&AQT4wM<$bF@ntbQ8C9sF!~Nsgfy3wyB>q$7DcI5M`EBXun|VQ^=U4S|(2Q ztv={e!DWg0f!jf`^9=%AVTvS#`saB=7S4Yj`_^o~+ zgLft|hLXj&kA9unI}Om!ROY3Y*6;BAXulU1YYlYo{*vLdpb!9O`9kiX2wi+iUGFSv z3e-~;OS?J9q$xy(xN7jEz7fvn8H6jA9J^s@Wb`hD^fqc^smFGVQMP-!rTs<+&d0ZD zvJ9~Fs`ks=!vN(UCCUs?(bh=8Vd?FAr9Bz7_SgAS$3`^@frQkQFsU-q7IU zkwcUmqW$8gYqx>LMy!-tuX38CZ^l-wWSAAKZv=~Tp?{b!sAIGgn;riFPq;H^mzpZ8 zsJI!TS&dnT7_C8+YtK1gRrH^7!0g+$K}DM5gp(t$6Z;P6kXqoXxSNkOPpv{yrm$S1 zerAQK(y^N7`E%odC0YBxb%qQG~udVI%AN@-7pFEDt&l{Wi732T`C%ob@SBvd~@B=e0 zm!svJyA>`%2QMi83P->&1tpc-KZQUQnE>KIq?rzT5>z&nG zvwZjXwRP844w!(atJ&Ne>`O}4IbO#-Oy(QHD@a7-WrD}4Tb8%R512^M$56O!#18&j zv0SIwwLWjN_;<_U6WR58QF{*QD`5n=%*;qtlo7|&~Z<*a+!ET zwDo{^E8VyN>m*yyD8iOWlf7;7>37L1P48<18T*NGWy#t$=1;E&!#MHAbP6wAJh392 z9>UzgqWB!Kp+^D@`82$)u-sCS4e2*A^T2|Akuk8h3P=k^YPXB)zcxLgWcZq)%DZW;Ws*RbL@l><8N3WA&dE# z^}*GlB-gb9Y_Vmwzi8`6^OA=?_t5=EoN2Ry(U4tx#$E|p#m&}&B<2T=O4GAGHi=4` zGFEve`cgay(~tDNue3Jeo-wy|aK!kLGH;Sz2_rSeRDaj5KippYT2yrIrjJ+9&x)E4 zKU~x?e4XfD5siv9q-N}n;AG+oVpQY&MHK{zi+k}20joLL+TE)7uthmMg3Fi~pOT8F zZ%E>K`e*Jx`v4oTigQ4&k#dg`x0?$kXa~ee(hqr1w8qJi)qq+y>GD39XBW< zQG6(yd7ZT>jf3)d#KKoK@&T`Q=%DxO03M51wQC9nAKk_aikfBgYZ>!!y%~GvQ>0>4 z*-qrof!7c>lmNyGSI2>ZzoHx9I1Z9Xr|&Ay>2I!CCU&-yp!YB769%)52PCG0CIq+d zSnU?cw;bE*_jT;lcW^rOoZX4AUAUW2T4Kc2y8J9hO&Rpl0aWo>hmolEp+OFMGA%i8 z_N&?&s6>JJA)VTjdyuuO8zKbvkrO7cU?4_yS_8~sYPj!}nBL9e?oN{7=G8TEnY_Pu z)ppB(4ERrFN3sa&lMEuA1VuUiZ88ABjXy5=?>R?S2x^cxAom0X1pcp={)^gW`@8!8 z?W{ InternalApp.RootServices ; + + /// + /// 获取请求上下文 + /// + public static HttpContext HttpContext => RootServices?.GetService()?.HttpContext; + + public static IUser User => HttpContext == null ? null : RootServices?.GetService(); +} \ 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 43f65ab9..fc0abb9d 100644 --- a/Blog.Core.Common/Blog.Core.Common.csproj +++ b/Blog.Core.Common/Blog.Core.Common.csproj @@ -43,4 +43,8 @@ + + + + diff --git a/Blog.Core.Common/Core/InternalApp.cs b/Blog.Core.Common/Core/InternalApp.cs new file mode 100644 index 00000000..c1ae8dcd --- /dev/null +++ b/Blog.Core.Common/Core/InternalApp.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore.Builder; +using System; + +namespace Blog.Core.Common.Core; + +public static class InternalApp +{ + /// 根服务 + public static IServiceProvider RootServices; + + public static void ConfigureApplication(this WebApplication app) + { + app.Lifetime.ApplicationStarted.Register(() => { InternalApp.RootServices = app.Services; }); + + app.Lifetime.ApplicationStopped.Register(() => { InternalApp.RootServices = null; }); + } +} \ No newline at end of file diff --git a/Blog.Core.Common/DB/Aop/SqlsugarAop.cs b/Blog.Core.Common/DB/Aop/SqlsugarAop.cs new file mode 100644 index 00000000..9ab494a4 --- /dev/null +++ b/Blog.Core.Common/DB/Aop/SqlsugarAop.cs @@ -0,0 +1,130 @@ +using Blog.Core.Model; +using Blog.Core.Model.Models.RootTkey; +using Blog.Core.Model.Tenants; +using SqlSugar; +using System; + +namespace Blog.Core.Common.DB.Aop; + +public static class SqlSugarAop +{ + public static void DataExecuting(object oldValue, DataFilterModel entityInfo) + { + if (entityInfo.EntityValue is RootEntityTkey rootEntity) + { + if (rootEntity.Id == 0) + { + rootEntity.Id = SnowFlakeSingle.Instance.NextId(); + } + } + + if (entityInfo.EntityValue is BaseEntity baseEntity) + { + // 新增操作 + if (entityInfo.OperationType == DataFilterType.InsertByObject) + { + if (baseEntity.CreateTime == DateTime.MinValue) + { + baseEntity.CreateTime = DateTime.Now; + } + } + + if (entityInfo.OperationType == DataFilterType.UpdateByObject) + { + baseEntity.ModifyTime = DateTime.Now; + } + + + if (App.User?.ID > 0) + { + if (baseEntity is ITenantEntity tenant && App.User.TenantId > 0) + { + if (tenant.TenantId == 0) + { + tenant.TenantId = App.User.TenantId; + } + } + + switch (entityInfo.OperationType) + { + case DataFilterType.UpdateByObject: + baseEntity.ModifyId = App.User.ID; + baseEntity.ModifyBy = App.User.Name; + break; + case DataFilterType.InsertByObject: + if (baseEntity.CreateBy.IsNullOrEmpty() || baseEntity.CreateId is null or <= 0) + { + baseEntity.CreateId = App.User.ID; + baseEntity.CreateBy = App.User.Name; + } + + break; + } + } + } + else + { + //兼容以前的表 + //这里要小心 在AOP里用反射 数据量多性能就会有问题 + //要么都统一使用基类 + //要么考虑老的表没必要兼容老的表 + // + + var getType = entityInfo.EntityValue.GetType(); + + switch (entityInfo.OperationType) + { + case DataFilterType.InsertByObject: + var dyCreateBy = getType.GetProperty("CreateBy"); + var dyCreateId = getType.GetProperty("CreateId"); + var dyCreateTime = getType.GetProperty("CreateTime"); + + if (App.User?.ID > 0 && dyCreateBy != null && dyCreateBy.GetValue(entityInfo.EntityValue) == null) + dyCreateBy.SetValue(entityInfo.EntityValue, App.User.Name); + + if (App.User?.ID > 0 && dyCreateId != null && dyCreateId.GetValue(entityInfo.EntityValue) == null) + dyCreateId.SetValue(entityInfo.EntityValue, App.User.ID); + + if (dyCreateTime != null && (DateTime)dyCreateTime.GetValue(entityInfo.EntityValue) == DateTime.MinValue) + dyCreateTime.SetValue(entityInfo.EntityValue, DateTime.Now); + + break; + case DataFilterType.UpdateByObject: + var dyModifyBy = getType.GetProperty("ModifyBy"); + var dyModifyId = getType.GetProperty("ModifyId"); + var dyModifyTime = getType.GetProperty("ModifyTime"); + + if (App.User?.ID > 0 && dyModifyBy != null) + dyModifyBy.SetValue(entityInfo.EntityValue, App.User.Name); + + if (App.User?.ID > 0 && dyModifyId != null) + dyModifyId.SetValue(entityInfo.EntityValue, App.User.ID); + + if (dyModifyTime != null) + dyModifyTime.SetValue(entityInfo.EntityValue, DateTime.Now); + break; + } + } + } + + private static string GetWholeSql(SugarParameter[] paramArr, string sql) + { + foreach (var param in paramArr) + { + sql = sql.Replace(param.ParameterName, $@"'{param.Value.ObjToString()}'"); + } + + 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.Common/DB/BaseDBConfig.cs b/Blog.Core.Common/DB/BaseDBConfig.cs index dc7fee4e..1d86369a 100644 --- a/Blog.Core.Common/DB/BaseDBConfig.cs +++ b/Blog.Core.Common/DB/BaseDBConfig.cs @@ -1,4 +1,5 @@ -using System; +using SqlSugar; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -11,6 +12,7 @@ public class BaseDBConfig * 目前是多库操作,默认加载的是appsettings.json设置为true的第一个db连接。 */ public static (List allDbs, List slaveDbs) MutiConnectionString => MutiInitConn(); + private static string DifDBConnOfSecurity(params string[] conn) { foreach (var item in conn) @@ -22,7 +24,9 @@ private static string DifDBConnOfSecurity(params string[] conn) return File.ReadAllText(item).Trim(); } } - catch (System.Exception) { } + catch (System.Exception) + { + } } return conn[conn.Length - 1]; @@ -37,8 +41,9 @@ public static (List, List) MutiInitConn() { SpecialDbString(i); } - List listdatabaseSimpleDB = new List();//单库 - List listdatabaseSlaveDB = new List();//从库 + + List listdatabaseSimpleDB = new List(); //单库 + List listdatabaseSlaveDB = new List(); //从库 // 单库,且不开启读写分离,只保留一个 if (!AppSettings.app(new string[] { "CQRSEnabled" }).ObjToBool() && !AppSettings.app(new string[] { "MutiDBEnabled" }).ObjToBool()) @@ -54,6 +59,7 @@ public static (List, List) MutiInitConn() { dbFirst = listdatabase.FirstOrDefault(); } + listdatabaseSimpleDB.Add(dbFirst); return (listdatabaseSimpleDB, listdatabaseSlaveDB); } @@ -70,7 +76,6 @@ public static (List, List) MutiInitConn() } - return (listdatabase, listdatabaseSlaveDB); //} } @@ -102,6 +107,8 @@ private static MutiDBOperate SpecialDbString(MutiDBOperate mutiDBOperate) return mutiDBOperate; } + + } @@ -115,24 +122,29 @@ public enum DataBaseType Dm = 5, Kdbndp = 6, } + public class MutiDBOperate { /// /// 连接启用开关 /// public bool Enabled { get; set; } + /// /// 连接ID /// public string ConnId { get; set; } + /// /// 从库执行级别,越大越先执行 /// public int HitRate { get; set; } + /// /// 连接字符串 /// public string Connection { get; set; } + /// /// 数据库类型 /// diff --git a/Blog.Core.Common/DB/RepositorySetting.cs b/Blog.Core.Common/DB/RepositorySetting.cs new file mode 100644 index 00000000..bfca7174 --- /dev/null +++ b/Blog.Core.Common/DB/RepositorySetting.cs @@ -0,0 +1,48 @@ +using Blog.Core.Model.Models.RootTkey; +using Blog.Core.Model.Models.RootTkey.Interface; +using Blog.Core.Model.Tenants; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Blog.Core.Common.DB; + +public class RepositorySetting +{ + private static readonly Lazy> AllEntitys = new(() => + { + return typeof(BaseEntity).Assembly + .GetTypes() + .Where(t => t.IsClass && !t.IsAbstract && t.IsSubclassOf(typeof(BaseEntity))) + .Where(it => it.FullName != null && it.FullName.StartsWith("Blog.Core.Model.Models")); + }); + + public static IEnumerable Entitys => AllEntitys.Value; + + /// + /// 配置实体软删除过滤器
+ /// 统一过滤 软删除 无需自己写条件 + ///
+ public static void SetDeletedEntityFilter(SqlSugarScopeProvider db) + { + db.QueryFilter.AddTableFilter(it => it.IsDeleted == false); + } + + /// + /// 配置租户 + /// + public static void SetTenantEntityFilter(SqlSugarScopeProvider db) + { + if (App.User is not { ID: > 0, TenantId: > 0 }) + { + return; + } + + //多租户 单表 + db.QueryFilter.AddTableFilter(it => it.TenantId == App.User.TenantId || it.TenantId == 0); + + //多租户 多表 + db.SetTenantTable(App.User.TenantId.ToString()); + } +} \ No newline at end of file diff --git a/Blog.Core.Common/DB/TenantUtil.cs b/Blog.Core.Common/DB/TenantUtil.cs new file mode 100644 index 00000000..8d57189b --- /dev/null +++ b/Blog.Core.Common/DB/TenantUtil.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using Blog.Core.Model.Models; +using Blog.Core.Model.Tenants; +using SqlSugar; + +namespace Blog.Core.Common.DB; + +public static class TenantUtil +{ + public static SysTenant DefaultTenantConfig(this SysTenant tenant) + { + tenant.DbType ??= DbType.Sqlite; + + //如果没有配置连接 + if (tenant.Connection.IsNullOrEmpty()) + { + //此处默认配置 Sqlite 地址 + //实际业务中 也会有运维、系统管理员等来维护 + switch (tenant.DbType.Value) + { + case DbType.Sqlite: + tenant.Connection = $"DataSource={Path.Combine(Environment.CurrentDirectory, tenant.ConfigId)}.db"; + break; + } + } + + return tenant; + } + + public static ConnectionConfig GetConnectionConfig(this SysTenant tenant) + { + if (tenant.DbType is null) + { + throw new ArgumentException("Tenant DbType Must"); + } + + + return new ConnectionConfig() + { + ConfigId = tenant.ConfigId, + DbType = tenant.DbType.Value, + ConnectionString = tenant.Connection, + IsAutoCloseConnection = true, + MoreSettings = new ConnMoreSettings() + { + IsAutoRemoveDataCache = true + }, + }; + } + + public static List GetTenantEntityTypes(TenantTypeEnum? tenantType = null) + { + return RepositorySetting.Entitys + .Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass) + .Where(s => IsTenantEntity(s, tenantType)) + .ToList(); + } + + public static bool IsTenantEntity(this Type u, TenantTypeEnum? tenantType = null) + { + var mta = u.GetCustomAttribute(); + if (mta is null) + { + return false; + } + + if (tenantType != null) + { + if (mta.TenantType != tenantType) + { + return false; + } + } + + return true; + } + + public static string GetTenantTableName(this Type type, ISqlSugarClient db, string id) + { + var entityInfo = db.EntityMaintenance.GetEntityInfo(type); + return $@"{entityInfo.DbTableName}_{id}"; + } + + public static string GetTenantTableName(this Type type, ISqlSugarClient db, SysTenant tenant) + { + return GetTenantTableName(type, db, tenant.Id.ToString()); + } + + public static void SetTenantTable(this ISqlSugarClient db, string id) + { + var types = GetTenantEntityTypes(TenantTypeEnum.Tables); + + foreach (var type in types) + { + db.MappingTables.Add(type.Name, type.GetTenantTableName(db, id)); + } + } +} \ No newline at end of file diff --git a/Blog.Core.Common/Extensions/AssemblysExtensions.cs b/Blog.Core.Common/Extensions/AssemblysExtensions.cs new file mode 100644 index 00000000..5e5d4349 --- /dev/null +++ b/Blog.Core.Common/Extensions/AssemblysExtensions.cs @@ -0,0 +1,24 @@ +using Microsoft.Extensions.DependencyModel; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.Loader; + +namespace Blog.Core.Common.Extensions; + +public static class AssemblysExtensions +{ + public static List GetAllAssemblies() + { + var list = new List(); + var deps = DependencyContext.Default; + var libs = deps.CompileLibraries.Where(lib => !lib.Serviceable && lib.Type != "package" ); + foreach (var lib in libs) + { + var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name)); + list.Add(assembly); + } + + return list; + } +} \ No newline at end of file diff --git a/Blog.Core.Common/Extensions/ExpressionExtensions.cs b/Blog.Core.Common/Extensions/ExpressionExtensions.cs index 7f9e69e5..4058b95a 100644 --- a/Blog.Core.Common/Extensions/ExpressionExtensions.cs +++ b/Blog.Core.Common/Extensions/ExpressionExtensions.cs @@ -151,6 +151,16 @@ public static Expression Contains(this Expression left, Expression right) return left.Call("Contains", right); } + public static Expression StartContains(this Expression left, Expression right) + { + return left.Call("StartsWith", right); + } + + public static Expression EndContains(this Expression left, Expression right) + { + return left.Call("EndsWith", right); + } + /// /// > /// @@ -201,5 +211,4 @@ public static Expression NotEqual(this Expression left, Expression right) #endregion } - -} +} \ No newline at end of file diff --git a/Blog.Core.Common/Extensions/GenericTypeExtensions.cs b/Blog.Core.Common/Extensions/GenericTypeExtensions.cs index 368b6676..6c067773 100644 --- a/Blog.Core.Common/Extensions/GenericTypeExtensions.cs +++ b/Blog.Core.Common/Extensions/GenericTypeExtensions.cs @@ -26,5 +26,31 @@ public static string GetGenericTypeName(this object @object) { return @object.GetType().GetGenericTypeName(); } + + /// + /// 判断类型是否实现某个泛型 + /// + /// 类型 + /// 泛型类型 + /// bool + // public static bool HasImplementedRawGeneric(this Type type, Type generic) + // { + // // 检查接口类型 + // var isTheRawGenericType = type.GetInterfaces().Any(IsTheRawGenericType); + // if (isTheRawGenericType) return true; + + // // 检查类型 + // while (type != null && type != typeof(object)) + // { + // isTheRawGenericType = IsTheRawGenericType(type); + // if (isTheRawGenericType) return true; + // type = type.BaseType; + // } + + // return false; + + // // 判断逻辑 + // bool IsTheRawGenericType(Type type) => generic == (type.IsGenericType ? type.GetGenericTypeDefinition() : type); + // } } -} +} \ No newline at end of file diff --git a/Blog.Core.Common/Extensions/UntilExtensions.cs b/Blog.Core.Common/Extensions/UntilExtensions.cs new file mode 100644 index 00000000..1ae503b2 --- /dev/null +++ b/Blog.Core.Common/Extensions/UntilExtensions.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace Blog.Core.Common.Extensions; + +public static class UntilExtensions +{ + public static void AddOrModify(this IDictionary dic, TKey key, TValue value) + { + if (dic.TryGetValue(key, out _)) + { + dic[key] = value; + } + else + { + dic.Add(key, value); + } + } +} \ No newline at end of file diff --git a/Blog.Core.Common/Helper/DynamicLinqFactory.cs b/Blog.Core.Common/Helper/DynamicLinqFactory.cs index 43fdd7e3..7d45bbd2 100644 --- a/Blog.Core.Common/Helper/DynamicLinqFactory.cs +++ b/Blog.Core.Common/Helper/DynamicLinqFactory.cs @@ -1,15 +1,11 @@ -using Microsoft.AspNetCore.Http; -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Linq.Expressions; -using System.Net.Http; -using System.Net.Http.Headers; using System.Reflection; using System.Text; using System.Text.RegularExpressions; -using System.Threading.Tasks; namespace Blog.Core.Common.Helper { @@ -20,10 +16,10 @@ namespace Blog.Core.Common.Helper /// public static class DynamicLinqFactory { - private static readonly Dictionary _operatingSystems = new(); + private static readonly Dictionary _operatingSystems = new Dictionary(); public static Dictionary OperatingSystems => GetOperationSymbol(); - private static readonly Dictionary _linkSymbols = new(); + private static readonly Dictionary _linkSymbols = new Dictionary(); public static Dictionary LinkSymbols => GetLinkSymbol(); /// @@ -70,11 +66,16 @@ public static Expression ExpressionStudio(Expression left, DynamicLinqHelper Dyn var properties = DynamicLinq.Left.Split('.'); - // 从1开始,是不想用自定义种子,外层种子已经定义好了 - // 暂时也不会有多个自定义种子,先这样 - for (var i = 0; i < properties.Length; i++) + int index = 0; + foreach (var t in properties) { - mainExpression = mainExpression.Property(properties[i]); + if (mainExpression.Type.HasImplementedRawGeneric(typeof(IEnumerable<>))) + { + return ExpressionStudioEnumerable(left, mainExpression, DynamicLinq.Clone(), properties.Skip(index).ToArray()); + } + + mainExpression = mainExpression.Property(t); + index++; } left = left == null @@ -85,6 +86,32 @@ public static Expression ExpressionStudio(Expression left, DynamicLinqHelper Dyn return left; } + public static Expression ExpressionStudioEnumerable(Expression left, Expression property, DynamicLinqHelper dynamicLinq, string[] properties) + { + var realType = property.Type.GenericTypeArguments[0]; + + var parameter = Expression.Parameter(realType, "z"); + Expression mainExpression = property; + if (!properties.Any()) + { + throw new ApplicationException("条件表达式错误,属性为集合时,需要明确具体属性"); + } + + dynamicLinq.Left = string.Join(".", properties); + mainExpression = ExpressionStudio(null, dynamicLinq, parameter); + + var lambda = Expression.Lambda(mainExpression, parameter); + + mainExpression = Expression.Call(typeof(Enumerable), "Any", new[] { realType }, property, lambda); + + left = left == null + ? mainExpression + : ChangeLinkSymbol(dynamicLinq.LinkSymbol, left, mainExpression); + + return left; + } + + /// /// 将字符串装换成动态帮助类(内含递归) /// @@ -132,6 +159,7 @@ public static List SplitOperationSymbol(string str) { var outList = new List(); var tokens = Regex.Matches(FormatString(str), _pattern, RegexOptions.Compiled) + .Cast() .Select(m => m.Groups[1].Value.Trim()) .ToList(); @@ -139,7 +167,7 @@ public static List SplitOperationSymbol(string str) int lastOperatingSymbolIndex = -1; for (int i = tokens.Count - 1; i >= 0; i--) { - var token = tokens[i]; + var token = tokens[i].ToLower(); if (OperatingSystems.ContainsKey(token)) { @@ -189,6 +217,7 @@ public static List SplitOperationSymbol(string str) } } + outList.Reverse(); return outList; } @@ -259,7 +288,7 @@ public static Dictionary GetOperationSymbol() { foreach (var name in attr.Name.Split(';')) { - _operatingSystems.Add(name, (OperationSymbol)item.GetValue(null)); + _operatingSystems.Add(name.ToLower(), (OperationSymbol)item.GetValue(null)); } } } @@ -353,7 +382,14 @@ public static string FormatString(string str) public static readonly string _pattern = @"\s*(" + string.Join("|", new string[] { // operators and punctuation that are longer than one char: longest first - string.Join("|", new[] { "||", "&&", "==", "!=", "<=", ">=", "like", "contains" }.Select(Regex.Escape)), + string.Join("|", new[] + { + "||", "&&", "==", "!=", "<=", ">=", + "in", + "like", "contains", "%=", + "startslike", "startscontains", "%>", + "endlike", "endcontains", "%<", + }.Select(Regex.Escape)), @"""(?:\\.|[^""])*""", // string @"\d+(?:\.\d+)?", // number with optional decimal part @"\w+", // word @@ -365,7 +401,7 @@ public static string FormatString(string str) /// public static OperationSymbol ChangeOperationSymbol(string str) { - switch (str) + switch (str.ToLower()) { case "<": return OperationSymbol.LessThan; @@ -382,7 +418,16 @@ public static OperationSymbol ChangeOperationSymbol(string str) return OperationSymbol.NotEqual; case "contains": case "like": + case "%=": return OperationSymbol.Contains; + case "startslike": + case "startscontains": + case "%>": + return OperationSymbol.StartsContains; + case "endlike": + case "endcontains": + case "%<": + return OperationSymbol.EndContains; } throw new Exception("OperationSymbol IS NULL"); @@ -451,6 +496,10 @@ public static Expression ChangeOperationSymbol(OperationSymbol symbol, Expressio return key.NotEqual(Expression.Constant(newTypeRight)); case OperationSymbol.Contains: return key.Contains(Expression.Constant(newTypeRight)); + case OperationSymbol.StartsContains: + return key.StartContains(Expression.Constant(newTypeRight)); + case OperationSymbol.EndContains: + return key.EndContains(Expression.Constant(newTypeRight)); case OperationSymbol.In: var contains = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public) .Single(x => x.Name == "Contains" && x.GetParameters().Length == 2) @@ -480,6 +529,17 @@ public class DynamicLinqHelper [Display(Name = "连接符")] public LinkSymbol LinkSymbol { get; set; } + + public DynamicLinqHelper Clone() + { + return new DynamicLinqHelper() + { + Left = this.Left, + Right = this.Right, + OperationSymbol = this.OperationSymbol, + LinkSymbol = this.LinkSymbol, + }; + } } /// @@ -504,9 +564,15 @@ public enum OperationSymbol [Display(Name = "in")] In, - [Display(Name = "like;contains")] + [Display(Name = "like;contains;%=")] Contains, + [Display(Name = "StartsLike;StartsContains;%>")] + StartsContains, + + [Display(Name = "EndLike;EndContains;%<")] + EndContains, + [Display(Name = ">")] GreaterThan, @@ -528,7 +594,7 @@ public enum OperationSymbol #endregion - + /// /// Queryable扩展 /// diff --git a/Blog.Core.Common/Helper/GenericTypeExtensions.cs b/Blog.Core.Common/Helper/GenericTypeExtensions.cs new file mode 100644 index 00000000..aa095e2a --- /dev/null +++ b/Blog.Core.Common/Helper/GenericTypeExtensions.cs @@ -0,0 +1,56 @@ +using System; +using System.Linq; + +namespace Blog.Core.Common.Helper +{ + public static class GenericTypeExtensions + { + /// + /// 判断类型是否实现某个泛型 + /// + /// 类型 + /// 泛型类型 + /// bool + public static bool HasImplementedRawGeneric(this Type type, Type generic) + { + // 检查接口类型 + var isTheRawGenericType = type.GetInterfaces().Any(IsTheRawGenericType); + if (isTheRawGenericType) return true; + + // 检查类型 + while (type != null && type != typeof(object)) + { + isTheRawGenericType = IsTheRawGenericType(type); + if (isTheRawGenericType) return true; + type = type.BaseType; + } + + return false; + + // 判断逻辑 + bool IsTheRawGenericType(Type t) => generic == (t.IsGenericType ? t.GetGenericTypeDefinition() : t); + } + + public static string GetGenericTypeName(this Type type) + { + var typeName = string.Empty; + + if (type.IsGenericType) + { + var genericTypes = string.Join(",", type.GetGenericArguments().Select(t => t.Name).ToArray()); + typeName = $"{type.Name.Remove(type.Name.IndexOf('`'))}<{genericTypes}>"; + } + else + { + typeName = type.Name; + } + + return typeName; + } + + public static string GetGenericTypeName(this object @object) + { + return @object.GetType().GetGenericTypeName(); + } + } +} \ No newline at end of file diff --git a/Blog.Core.Common/Helper/NumberConverter.cs b/Blog.Core.Common/Helper/NumberConverter.cs new file mode 100644 index 00000000..27890faf --- /dev/null +++ b/Blog.Core.Common/Helper/NumberConverter.cs @@ -0,0 +1,174 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Blog.Core.Common.Helper +{ + /// + /// + /// 大数据json序列化重写 + /// + public sealed class NumberConverter : JsonConverter + { + /// + /// 转换成字符串的类型 + /// + private readonly NumberConverterShip _ship; + + /// + /// 大数据json序列化重写实例化 + /// + public NumberConverter() + { + _ship = (NumberConverterShip)0xFF; + } + + /// + /// 大数据json序列化重写实例化 + /// + /// 转换成字符串的类型 + public NumberConverter(NumberConverterShip ship) + { + _ship = ship; + } + + /// + /// + /// 确定此实例是否可以转换指定的对象类型。 + /// + /// 对象的类型。 + /// 如果此实例可以转换指定的对象类型,则为:true,否则为:false + public override bool CanConvert(Type objectType) + { + var typecode = Type.GetTypeCode(objectType.Name.Equals("Nullable`1") ? objectType.GetGenericArguments().First() : objectType); + switch (typecode) + { + case TypeCode.Decimal: + return (_ship & NumberConverterShip.Decimal) == NumberConverterShip.Decimal; + case TypeCode.Double: + return (_ship & NumberConverterShip.Double) == NumberConverterShip.Double; + case TypeCode.Int64: + return (_ship & NumberConverterShip.Int64) == NumberConverterShip.Int64; + case TypeCode.UInt64: + return (_ship & NumberConverterShip.UInt64) == NumberConverterShip.UInt64; + case TypeCode.Single: + return (_ship & NumberConverterShip.Single) == NumberConverterShip.Single; + default: return false; + } + } + + /// + /// + /// 读取对象的JSON表示。 + /// + /// 中读取。 + /// 对象的类型。 + /// 正在读取的对象的现有值。 + /// 调用的序列化器实例。 + /// 对象值。 + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + return AsType(reader.Value.ToString(), objectType); + } + + /// + /// 字符串格式数据转其他类型数据 + /// + /// 输入的字符串 + /// 目标格式 + /// 转换结果 + public static object AsType(string input, Type destinationType) + { + try + { + var converter = TypeDescriptor.GetConverter(destinationType); + if (converter.CanConvertFrom(typeof(string))) + { + return converter.ConvertFrom(null, null, input); + } + + converter = TypeDescriptor.GetConverter(typeof(string)); + if (converter.CanConvertTo(destinationType)) + { + return converter.ConvertTo(null, null, input, destinationType); + } + } + catch + { + return null; + } + return null; + } + + /// + /// + /// 写入对象的JSON表示形式。 + /// + /// 要写入的 。 + /// 要写入对象值 + /// 调用的序列化器实例。 + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value == null) + { + writer.WriteNull(); + } + else + { + var objectType = value.GetType(); + var typeCode = Type.GetTypeCode(objectType.Name.Equals("Nullable`1") ? objectType.GetGenericArguments().First() : objectType); + switch (typeCode) + { + case TypeCode.Decimal: + writer.WriteValue(((decimal)value).ToString("f6")); + break; + case TypeCode.Double: + writer.WriteValue(((double)value).ToString("f4")); + break; + case TypeCode.Single: + writer.WriteValue(((float)value).ToString("f2")); + break; + default: + writer.WriteValue(value.ToString()); + break; + } + } + } + } + + /// + /// 转换成字符串的类型 + /// + [Flags] + public enum NumberConverterShip + { + /// + /// 长整数 + /// + Int64 = 1, + + /// + /// 无符号长整数 + /// + UInt64 = 2, + + /// + /// 浮点数 + /// + Single = 4, + + /// + /// 双精度浮点数 + /// + Double = 8, + + /// + /// 大数字 + /// + Decimal =16 + } +} diff --git a/Blog.Core.Common/Helper/RecursionHelper.cs b/Blog.Core.Common/Helper/RecursionHelper.cs index 9b27a37d..092a0678 100644 --- a/Blog.Core.Common/Helper/RecursionHelper.cs +++ b/Blog.Core.Common/Helper/RecursionHelper.cs @@ -8,7 +8,7 @@ namespace Blog.Core.Common.Helper /// public static class RecursionHelper { - public static void LoopToAppendChildren(List all, PermissionTree curItem, int pid, bool needbtn) + public static void LoopToAppendChildren(List all, PermissionTree curItem, long pid, bool needbtn) { var subItems = all.Where(ee => ee.Pid == curItem.value).ToList(); @@ -52,7 +52,7 @@ public static void LoopToAppendChildren(List all, PermissionTree LoopToAppendChildren(all, subItem, pid, needbtn); } } - public static void LoopToAppendChildren(List all, DepartmentTree curItem, int pid) + public static void LoopToAppendChildren(List all, DepartmentTree curItem, long pid) { var subItems = all.Where(ee => ee.Pid == curItem.value).ToList(); @@ -117,8 +117,8 @@ public static void LoopToAppendChildrenT(List all, T curItem, string paren public class PermissionTree { - public int value { get; set; } - public int Pid { get; set; } + public long value { get; set; } + public long Pid { get; set; } public string label { get; set; } public int order { get; set; } public bool isbtn { get; set; } @@ -139,8 +139,8 @@ public class DepartmentTree public class NavigationBar { - public int id { get; set; } - public int pid { get; set; } + public long id { get; set; } + public long pid { get; set; } public int order { get; set; } public string name { get; set; } public bool IsHide { get; set; } = false; @@ -165,8 +165,8 @@ public class NavigationBarMeta public class NavigationBarPro { - public int id { get; set; } - public int parentId { get; set; } + public long id { get; set; } + public long parentId { get; set; } public int order { get; set; } public string name { get; set; } public bool IsHide { get; set; } = false; diff --git a/Blog.Core.Common/Helper/SM/SM4.cs b/Blog.Core.Common/Helper/SM/SM4.cs index 4b1f1996..b4aa3ebf 100644 --- a/Blog.Core.Common/Helper/SM/SM4.cs +++ b/Blog.Core.Common/Helper/SM/SM4.cs @@ -10,7 +10,9 @@ public class SM4 private long GET_ULONG_BE(SByte[] b, int i) { +#pragma warning disable CS0675 // 对进行了带符号扩展的操作数使用了按位或运算符 long n2 = (b[i] & 0xFF) << 24 | (b[(i + 1)] & 0xFF) << 16 | (b[(i + 2)] & 0xFF) << 8 | b[(i + 3)] & 0xFF & 0xFFFFFFFF; +#pragma warning restore CS0675 // 对进行了带符号扩展的操作数使用了按位或运算符 return n2; } @@ -207,7 +209,6 @@ public SByte[] sm4_crypt_ecb(SM4_Context ctx, SByte[] input) int length = input.Length; SByte[] bins = new SByte[length]; SByte[] bous = new SByte[length]; - SByte[] output = null; Array.Copy(input, 0, bins, 0, length); diff --git a/Blog.Core.Common/Helper/UtilConvert.cs b/Blog.Core.Common/Helper/UtilConvert.cs index 282989e3..ea3b7c05 100644 --- a/Blog.Core.Common/Helper/UtilConvert.cs +++ b/Blog.Core.Common/Helper/UtilConvert.cs @@ -43,6 +43,18 @@ public static int ObjToInt(this object thisValue, int errorValue) return errorValue; } + public static long ObjToLong(this object thisValue) + { + long reval = 0; + if (thisValue == null) return 0; + if (thisValue != DBNull.Value && long.TryParse(thisValue.ToString(), out reval)) + { + return reval; + } + + return reval; + } + /// /// /// diff --git a/Blog.Core.Common/HttpContextUser/AspNetUser.cs b/Blog.Core.Common/HttpContextUser/AspNetUser.cs index e4e27d87..b37e4a24 100644 --- a/Blog.Core.Common/HttpContextUser/AspNetUser.cs +++ b/Blog.Core.Common/HttpContextUser/AspNetUser.cs @@ -5,6 +5,7 @@ using Blog.Core.Model; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; +using SqlSugar.Extensions; namespace Blog.Core.Common.HttpContextUser { @@ -40,6 +41,7 @@ private string GetName() } public int ID => GetClaimValueByType("jti").FirstOrDefault().ObjToInt(); + public long TenantId => GetClaimValueByType("TenantId").FirstOrDefault().ObjToLong(); public bool IsAuthenticated() { @@ -87,11 +89,9 @@ public IEnumerable GetClaimsIdentity() public List GetClaimValueByType(string ClaimType) { - return (from item in GetClaimsIdentity() where item.Type == ClaimType select item.Value).ToList(); - } } -} +} \ No newline at end of file diff --git a/Blog.Core.Common/HttpContextUser/IUser.cs b/Blog.Core.Common/HttpContextUser/IUser.cs index beef3b7d..3849bd38 100644 --- a/Blog.Core.Common/HttpContextUser/IUser.cs +++ b/Blog.Core.Common/HttpContextUser/IUser.cs @@ -8,6 +8,7 @@ public interface IUser { string Name { get; } int ID { get; } + long TenantId { get; } bool IsAuthenticated(); IEnumerable GetClaimsIdentity(); List GetClaimValueByType(string ClaimType); @@ -17,4 +18,4 @@ public interface IUser MessageModel MessageModel { get; set; } } -} +} \ No newline at end of file diff --git a/Blog.Core.Common/Seed/DBSeed.cs b/Blog.Core.Common/Seed/DBSeed.cs index da313e3c..cb6acc59 100644 --- a/Blog.Core.Common/Seed/DBSeed.cs +++ b/Blog.Core.Common/Seed/DBSeed.cs @@ -1,8 +1,11 @@ using Blog.Core.Common.DB; +using Blog.Core.Common.Extensions; using Blog.Core.Common.Helper; using Blog.Core.Model.Models; +using Blog.Core.Model.Tenants; using Magicodes.ExporterAndImporter.Excel; using Newtonsoft.Json; +using SqlSugar; using System; using System.Collections.Generic; using System.IO; @@ -96,7 +99,9 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) var modelTypes = referencedAssemblies .SelectMany(a => a.DefinedTypes) .Select(type => type.AsType()) - .Where(x => x.IsClass && x.Namespace != null && x.Namespace.Equals("Blog.Core.Model.Models")).ToList(); + .Where(x => x.IsClass && x.Namespace is "Blog.Core.Model.Models") + .Where(s => !s.IsDefined(typeof(MultiTenantAttribute), false)) + .ToList(); modelTypes.ForEach(t => { // 这里只支持添加表,不支持删除 @@ -104,14 +109,12 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) if (!myContext.Db.DbMaintenance.IsAnyTable(t.Name)) { Console.WriteLine(t.Name); - myContext.Db.CodeFirst.InitTables(t); + myContext.Db.CodeFirst.SplitTables().InitTables(t); } }); ConsoleHelper.WriteSuccessLine($"Tables created successfully!"); Console.WriteLine(); - - if (AppSettings.app(new string[] { "AppSettings", "SeedDBDataEnabled" }).ObjToBool()) { JsonSerializerSettings setting = new JsonSerializerSettings(); @@ -135,6 +138,7 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) var importer = new ExcelImporter(); #region BlogArticle + if (!await myContext.Db.Queryable().AnyAsync()) { myContext.GetEntityDB().InsertRange(JsonHelper.ParseFormByJson>(FileHelper.ReadFile(string.Format(SeedDataFolder, "BlogArticle"), Encoding.UTF8))); @@ -144,15 +148,14 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) { Console.WriteLine("Table:BlogArticle already exists..."); } + #endregion #region Modules + if (!await myContext.Db.Queryable().AnyAsync()) { - - - var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "Modules"), Encoding.UTF8), setting); myContext.GetEntityDB().InsertRange(data); @@ -162,31 +165,39 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) { Console.WriteLine("Table:Modules already exists..."); } + #endregion #region Permission + if (!await myContext.Db.Queryable().AnyAsync()) { var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "Permission"), Encoding.UTF8), setting); - myContext.GetEntityDB().InsertRange(data); + foreach (var item in data) + { + Console.WriteLine($"{item.Name}:{item.Id}"); + myContext.GetEntityDB().Insert(item); + } Console.WriteLine("Table:Permission created success!"); } else { Console.WriteLine("Table:Permission already exists..."); } + #endregion #region Role + if (!await myContext.Db.Queryable().AnyAsync()) { - //var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "Role"), Encoding.UTF8), setting); - using var stream = new FileStream(Path.Combine(WebRootPath, "BlogCore.Data.excel", "Role.xlsx"), FileMode.Open); - var result = await importer.Import(stream); - var data = result.Data.ToList(); + var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "Role"), Encoding.UTF8), setting); + //using var stream = new FileStream(Path.Combine(WebRootPath, "BlogCore.Data.excel", "Role.xlsx"), FileMode.Open); + //var result = await importer.Import(stream); + //var data = result.Data.ToList(); myContext.GetEntityDB().InsertRange(data); Console.WriteLine("Table:Role created success!"); @@ -195,25 +206,33 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) { Console.WriteLine("Table:Role already exists..."); } + #endregion #region RoleModulePermission + if (!await myContext.Db.Queryable().AnyAsync()) { var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "RoleModulePermission"), Encoding.UTF8), setting); - myContext.GetEntityDB().InsertRange(data); + foreach (var item in data) + { + Console.WriteLine($"{item.Id}"); + myContext.GetEntityDB().Insert(item); + } Console.WriteLine("Table:RoleModulePermission created success!"); } else { Console.WriteLine("Table:RoleModulePermission already exists..."); } + #endregion #region Topic + if (!await myContext.Db.Queryable().AnyAsync()) { var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "Topic"), Encoding.UTF8), setting); @@ -225,10 +244,12 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) { Console.WriteLine("Table:Topic already exists..."); } + #endregion #region TopicDetail + if (!await myContext.Db.Queryable().AnyAsync()) { var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "TopicDetail"), Encoding.UTF8), setting); @@ -240,16 +261,15 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) { Console.WriteLine("Table:TopicDetail already exists..."); } + #endregion #region UserRole + if (!await myContext.Db.Queryable().AnyAsync()) { - //var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "UserRole"), Encoding.UTF8), setting); - using var stream = new FileStream(Path.Combine(WebRootPath, "BlogCore.Data.excel", "UserRole.xlsx"), FileMode.Open); - var result = await importer.Import(stream); - var data = result.Data.ToList(); + var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "UserRole"), Encoding.UTF8), setting); myContext.GetEntityDB().InsertRange(data); Console.WriteLine("Table:UserRole created success!"); @@ -258,16 +278,15 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) { Console.WriteLine("Table:UserRole already exists..."); } + #endregion #region sysUserInfo + if (!await myContext.Db.Queryable().AnyAsync()) { - //var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "sysUserInfo"), Encoding.UTF8), setting); - using var stream = new FileStream(Path.Combine(WebRootPath, "BlogCore.Data.excel", "SysUserInfo.xlsx"), FileMode.Open); - var result = await importer.Import(stream); - var data = result.Data.ToList(); + var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "sysUserInfo"), Encoding.UTF8), setting); myContext.GetEntityDB().InsertRange(data); Console.WriteLine("Table:sysUserInfo created success!"); @@ -276,10 +295,12 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) { Console.WriteLine("Table:sysUserInfo already exists..."); } + #endregion #region TasksQz + if (!await myContext.Db.Queryable().AnyAsync()) { var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "TasksQz"), Encoding.UTF8), setting); @@ -291,9 +312,24 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) { Console.WriteLine("Table:TasksQz already exists..."); } + + #endregion + + #region TasksLog + + if (!await myContext.Db.Queryable().AnyAsync()) + { + Console.WriteLine("Table:TasksLog created success!"); + } + else + { + Console.WriteLine("Table:TasksLog already exists..."); + } + #endregion #region Department + if (!await myContext.Db.Queryable().AnyAsync()) { var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "Department"), Encoding.UTF8), setting); @@ -305,13 +341,16 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) { Console.WriteLine("Table:Department already exists..."); } + #endregion + //种子初始化 + await SeedDataAsync(myContext.Db); + ConsoleHelper.WriteSuccessLine($"Done seeding database!"); } Console.WriteLine(); - } catch (Exception ex) { @@ -321,5 +360,225 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) "3、其他错误:" + ex.Message); } } + + /// + /// 种子初始化数据 + /// + /// + /// + private static async Task SeedDataAsync(ISqlSugarClient db) + { + // 获取所有种子配置-初始化数据 + var seedDataTypes = AssemblysExtensions.GetAllAssemblies().SelectMany(s => s.DefinedTypes) + .Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass) + .Where(u => + { + var esd = u.GetInterfaces().FirstOrDefault(i => i.HasImplementedRawGeneric(typeof(IEntitySeedData<>))); + if (esd is null) + { + return false; + } + + var eType = esd.GenericTypeArguments[0]; + if (eType.GetCustomAttribute() is null) + { + return true; + } + + return false; + }); + + if (!seedDataTypes.Any()) return; + foreach (var seedType in seedDataTypes) + { + dynamic instance = Activator.CreateInstance(seedType); + //初始化数据 + { + var seedData = instance.InitSeedData(); + if (seedData != null && Enumerable.Any(seedData)) + { + var entityType = seedType.GetInterfaces().First().GetGenericArguments().First(); + var entity = db.EntityMaintenance.GetEntityInfo(entityType); + + if (!await db.Queryable(entity.DbTableName, "").AnyAsync()) + { + await db.Insertable(Enumerable.ToList(seedData)).ExecuteCommandAsync(); + Console.WriteLine($"Table:{entity.DbTableName} init success!"); + } + } + } + + //种子数据 + { + var seedData = instance.SeedData(); + if (seedData != null && Enumerable.Any(seedData)) + { + var entityType = seedType.GetInterfaces().First().GetGenericArguments().First(); + var entity = db.EntityMaintenance.GetEntityInfo(entityType); + + await db.Storageable(Enumerable.ToList(seedData)).ExecuteCommandAsync(); + Console.WriteLine($"Table:{entity.DbTableName} seedData success!"); + } + } + + //自定义处理 + { + await instance.CustomizeSeedData(db); + } + } + } + + + /// + /// 初始化 多租户 + /// + /// + /// + public static async Task TenantSeedAsync(MyContext myContext) + { + var tenants = await myContext.Db.Queryable().Where(s => s.TenantType == TenantTypeEnum.Db).ToListAsync(); + if (tenants.Any()) + { + Console.WriteLine($@"Init Multi Tenant Db"); + foreach (var tenant in tenants) + { + Console.WriteLine($@"Init Multi Tenant Db : {tenant.ConfigId}/{tenant.Name}"); + await InitTenantSeedAsync(myContext.Db.AsTenant(), tenant.GetConnectionConfig()); + } + } + + tenants = await myContext.Db.Queryable().Where(s => s.TenantType == TenantTypeEnum.Tables).ToListAsync(); + if (tenants.Any()) + { + await InitTenantSeedAsync(myContext, tenants); + } + } + + #region 多租户 多表 初始化 + + private static async Task InitTenantSeedAsync(MyContext myContext, List tenants) + { + ConsoleHelper.WriteInfoLine($"Init Multi Tenant Tables : {myContext.Db.CurrentConnectionConfig.ConfigId}"); + + // 获取所有实体表-初始化租户业务表 + var entityTypes = TenantUtil.GetTenantEntityTypes(TenantTypeEnum.Tables); + if (!entityTypes.Any()) return; + + foreach (var sysTenant in tenants) + { + foreach (var entityType in entityTypes) + { + myContext.Db.CodeFirst + .As(entityType, entityType.GetTenantTableName(myContext.Db, sysTenant)) + .InitTables(entityType); + + Console.WriteLine($@"Init Tables:{entityType.GetTenantTableName(myContext.Db, sysTenant)}"); + } + + myContext.Db.SetTenantTable(sysTenant.Id.ToString()); + //多租户初始化种子数据 + await TenantSeedDataAsync(myContext.Db, TenantTypeEnum.Tables); + } + + ConsoleHelper.WriteSuccessLine($"Init Multi Tenant Tables : {myContext.Db.CurrentConnectionConfig.ConfigId} created successfully!"); + } + + #endregion + + #region 多租户 多库 初始化 + + /// + /// 初始化多库 + /// + /// + /// + /// + public static async Task InitTenantSeedAsync(ITenant itenant, ConnectionConfig config) + { + itenant.RemoveConnection(config.ConfigId); + itenant.AddConnection(config); + + var db = itenant.GetConnectionScope(config.ConfigId); + + db.DbMaintenance.CreateDatabase(); + ConsoleHelper.WriteSuccessLine($"Init Multi Tenant Db : {config.ConfigId} Database created successfully!"); + + Console.WriteLine($@"Init Multi Tenant Db : {config.ConfigId} Create Tables"); + + // 获取所有实体表-初始化租户业务表 + var entityTypes = TenantUtil.GetTenantEntityTypes(TenantTypeEnum.Db); + if (!entityTypes.Any()) return; + foreach (var entityType in entityTypes) + { + var splitTable = entityType.GetCustomAttribute(); + if (splitTable == null) + db.CodeFirst.InitTables(entityType); + else + db.CodeFirst.SplitTables().InitTables(entityType); + + Console.WriteLine(entityType.Name); + } + + //多租户初始化种子数据 + await TenantSeedDataAsync(db, TenantTypeEnum.Db); + } + + #endregion + + private static async Task TenantSeedDataAsync(ISqlSugarClient db, TenantTypeEnum tenantType) + { + // 获取所有种子配置-初始化数据 + var seedDataTypes = AssemblysExtensions.GetAllAssemblies().SelectMany(s => s.DefinedTypes) + .Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass) + .Where(u => + { + var esd = u.GetInterfaces().FirstOrDefault(i => i.HasImplementedRawGeneric(typeof(IEntitySeedData<>))); + if (esd is null) + { + return false; + } + + var eType = esd.GenericTypeArguments[0]; + return eType.IsTenantEntity(tenantType); + }); + if (!seedDataTypes.Any()) return; + foreach (var seedType in seedDataTypes) + { + dynamic instance = Activator.CreateInstance(seedType); + //初始化数据 + { + var seedData = instance.InitSeedData(); + if (seedData != null && Enumerable.Any(seedData)) + { + var entityType = seedType.GetInterfaces().First().GetGenericArguments().First(); + var entity = db.EntityMaintenance.GetEntityInfo(entityType); + + if (!await db.Queryable(entity.DbTableName, "").AnyAsync()) + { + await db.Insertable(Enumerable.ToList(seedData)).ExecuteCommandAsync(); + Console.WriteLine($"Table:{entity.DbTableName} init success!"); + } + } + } + + //种子数据 + { + var seedData = instance.SeedData(); + if (seedData != null && Enumerable.Any(seedData)) + { + var entityType = seedType.GetInterfaces().First().GetGenericArguments().First(); + var entity = db.EntityMaintenance.GetEntityInfo(entityType); + + await db.Storageable(Enumerable.ToList(seedData)).ExecuteCommandAsync(); + Console.WriteLine($"Table:{entity.DbTableName} seedData success!"); + } + } + + //自定义处理 + { + await instance.CustomizeSeedData(db); + } + } + } } -} +} \ No newline at end of file diff --git a/Blog.Core.Common/Seed/IEntitySeedData.cs b/Blog.Core.Common/Seed/IEntitySeedData.cs new file mode 100644 index 00000000..3e2f4859 --- /dev/null +++ b/Blog.Core.Common/Seed/IEntitySeedData.cs @@ -0,0 +1,36 @@ +using SqlSugar; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Blog.Core.Common.Seed; + +/// +/// 种子数据 接口 +/// +/// +public interface IEntitySeedData + where T : class, new() +{ + /// + /// 初始化种子数据
+ /// 只要表不存在数据,程序启动就会自动初始化 + ///
+ /// + IEnumerable InitSeedData(); + + /// + /// 种子数据
+ /// 存在不操作、不存在Insert
+ /// 适合系统内置数据,项目开发后续增加内置数据 + ///
+ /// + IEnumerable SeedData(); + + /// + /// 自定义操作
+ /// 以上满不足了,可以自己编写 + ///
+ /// + /// + Task CustomizeSeedData(ISqlSugarClient db); +} \ No newline at end of file diff --git a/Blog.Core.Common/Seed/SeedData/BusinessDataSeedData.cs b/Blog.Core.Common/Seed/SeedData/BusinessDataSeedData.cs new file mode 100644 index 00000000..361cd725 --- /dev/null +++ b/Blog.Core.Common/Seed/SeedData/BusinessDataSeedData.cs @@ -0,0 +1,79 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Blog.Core.Model.Models; +using SqlSugar; + +namespace Blog.Core.Common.Seed.SeedData; + +/// +/// 初始化 业务数据 +/// +public class BusinessDataSeedData : IEntitySeedData +{ + public IEnumerable InitSeedData() + { + return new[] + { + new BusinessTable() + { + Id = 1, + TenantId = 1000001, + Name = "张三的数据01", + Amount = 150, + IsDeleted = true, + }, + new BusinessTable() + { + Id = 2, + TenantId = 1000001, + Name = "张三的数据02", + Amount = 200, + }, + new BusinessTable() + { + Id = 3, + TenantId = 1000001, + Name = "张三的数据03", + Amount = 250, + }, + new BusinessTable() + { + Id = 4, + TenantId = 1000002, + Name = "李四的数据01", + Amount = 300, + }, + new BusinessTable() + { + Id = 5, + TenantId = 1000002, + Name = "李四的数据02", + Amount = 500, + }, + new BusinessTable() + { + Id = 6, + TenantId = 0, + Name = "公共数据01", + Amount = 16600, + }, + new BusinessTable() + { + Id = 7, + TenantId = 0, + Name = "公共数据02", + Amount = 19800, + }, + }; + } + + public IEnumerable SeedData() + { + return default; + } + + public Task CustomizeSeedData(ISqlSugarClient db) + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Blog.Core.Common/Seed/SeedData/MultiBusinessDataSeedData.cs b/Blog.Core.Common/Seed/SeedData/MultiBusinessDataSeedData.cs new file mode 100644 index 00000000..4ca1a7dd --- /dev/null +++ b/Blog.Core.Common/Seed/SeedData/MultiBusinessDataSeedData.cs @@ -0,0 +1,38 @@ +using Blog.Core.Model.Models; +using SqlSugar; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Blog.Core.Common.Seed.SeedData; + +public class MultiBusinessDataSeedData : IEntitySeedData +{ + public IEnumerable InitSeedData() + { + return new List() + { + new() + { + Id = 1001, + Name = "业务数据1", + Amount = 100, + }, + new() + { + Id = 1002, + Name = "业务数据2", + Amount = 1000, + }, + }; + } + + public IEnumerable SeedData() + { + return default; + } + + public Task CustomizeSeedData(ISqlSugarClient db) + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Blog.Core.Common/Seed/SeedData/MultiBusinessSubDataSeedData.cs b/Blog.Core.Common/Seed/SeedData/MultiBusinessSubDataSeedData.cs new file mode 100644 index 00000000..e73d4603 --- /dev/null +++ b/Blog.Core.Common/Seed/SeedData/MultiBusinessSubDataSeedData.cs @@ -0,0 +1,38 @@ +using Blog.Core.Model.Models; +using SqlSugar; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Blog.Core.Common.Seed.SeedData; + +public class MultiBusinessSubDataSeedData : IEntitySeedData +{ + public IEnumerable InitSeedData() + { + return new List() + { + new() + { + Id = 100, + MainId = 1001, + Memo = "子数据", + }, + new() + { + Id = 1001, + MainId = 1001, + Memo = "子数据2", + }, + }; + } + + public IEnumerable SeedData() + { + return default; + } + + public Task CustomizeSeedData(ISqlSugarClient db) + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Blog.Core.Common/Seed/SeedData/SubBusinessDataSeedData.cs b/Blog.Core.Common/Seed/SeedData/SubBusinessDataSeedData.cs new file mode 100644 index 00000000..be8462e8 --- /dev/null +++ b/Blog.Core.Common/Seed/SeedData/SubBusinessDataSeedData.cs @@ -0,0 +1,70 @@ +using Blog.Core.Model.Models; +using SqlSugar; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Blog.Core.Common.Seed.SeedData; + +public class SubBusinessDataSeedData : IEntitySeedData +{ + public IEnumerable InitSeedData() + { + return default; + } + + public IEnumerable SeedData() + { + return default; + } + + public async Task CustomizeSeedData(ISqlSugarClient db) + { + //初始化分库数据 + //只是用于测试 + if (db.CurrentConnectionConfig.ConfigId == "Tenant_3") + { + if (!await db.Queryable().AnyAsync()) + { + await db.Insertable(new List() + { + new() + { + Id = SnowFlakeSingle.Instance.NextId(), + Name = "王五业务数据1", + Amount = 100, + }, + new() + { + Id = SnowFlakeSingle.Instance.NextId(), + Name = "王五业务数据2", + Amount = 1000, + }, + }).ExecuteReturnSnowflakeIdListAsync(); + } + } + else if (db.CurrentConnectionConfig.ConfigId == "Tenant_4") + { + if (!await db.Queryable().AnyAsync()) + { + await db.Insertable(new List() + { + new() + { + Id = SnowFlakeSingle.Instance.NextId(), + Name = "赵六业务数据1", + Amount = 50, + }, + new() + { + Id = SnowFlakeSingle.Instance.NextId(), + Name = "赵六业务数据2", + Amount = 60, + }, + }).ExecuteReturnSnowflakeIdListAsync(); + } + } + + + await Task.Delay(1); + } +} \ No newline at end of file diff --git a/Blog.Core.Common/Seed/SeedData/TenantSeedData.cs b/Blog.Core.Common/Seed/SeedData/TenantSeedData.cs new file mode 100644 index 00000000..f33f83b2 --- /dev/null +++ b/Blog.Core.Common/Seed/SeedData/TenantSeedData.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Blog.Core.Common.DB; +using Blog.Core.Model.Models; +using Blog.Core.Model.Tenants; +using SqlSugar; + +namespace Blog.Core.Common.Seed.SeedData; + +/// +/// 租户 种子数据 +/// +public class TenantSeedData : IEntitySeedData +{ + public IEnumerable InitSeedData() + { + return new[] + { + new SysTenant() + { + Id = 1000001, + ConfigId = "Tenant_1", + Name = "张三", + TenantType = TenantTypeEnum.Id + }, + new SysTenant() + { + Id = 1000002, + ConfigId = "Tenant_2", + Name = "李四", + TenantType = TenantTypeEnum.Id + }, + new SysTenant() + { + Id = 1000003, + ConfigId = "Tenant_3", + Name = "王五", + TenantType = TenantTypeEnum.Db, + DbType = DbType.Sqlite, + Connection = $"DataSource=" + Path.Combine(Environment.CurrentDirectory, "WangWu.db"), + }, + new SysTenant() + { + Id = 1000004, + ConfigId = "Tenant_4", + Name = "赵六", + TenantType = TenantTypeEnum.Db, + DbType = DbType.Sqlite, + Connection = $"DataSource=" + Path.Combine(Environment.CurrentDirectory, "ZhaoLiu.db"), + }, + new SysTenant() + { + Id = 1000005, + ConfigId = "Tenant_5", + Name = "孙七", + TenantType = TenantTypeEnum.Tables, + }, + }; + } + + public IEnumerable SeedData() + { + return default; + } + + public Task CustomizeSeedData(ISqlSugarClient db) + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Blog.Core.Common/Seed/SeedData/UserInfoSeedData.cs b/Blog.Core.Common/Seed/SeedData/UserInfoSeedData.cs new file mode 100644 index 00000000..414a506b --- /dev/null +++ b/Blog.Core.Common/Seed/SeedData/UserInfoSeedData.cs @@ -0,0 +1,80 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Blog.Core.Model.Models; +using SqlSugar; + +namespace Blog.Core.Common.Seed.SeedData; + +public class UserInfoSeedData : IEntitySeedData +{ + public IEnumerable InitSeedData() + { + return default; + } + + public IEnumerable SeedData() + { + return default; + } + + public async Task CustomizeSeedData(ISqlSugarClient db) + { + var data = new List() + { + new SysUserInfo() + { + Id = 10001, + LoginName = "zhangsan", + LoginPWD = "E10ADC3949BA59ABBE56E057F20F883E", + Name = "张三", + TenantId = 1000001, //租户Id + }, + new SysUserInfo() + { + Id = 10002, + LoginName = "lisi", + LoginPWD = "E10ADC3949BA59ABBE56E057F20F883E", + Name = "李四", + TenantId = 1000002, //租户Id + }, + new SysUserInfo() + { + Id = 10003, + LoginName = "wangwu", + LoginPWD = "E10ADC3949BA59ABBE56E057F20F883E", + Name = "王五", + TenantId = 1000003, //租户Id + }, + new SysUserInfo() + { + Id = 10004, + LoginName = "zhaoliu", + LoginPWD = "E10ADC3949BA59ABBE56E057F20F883E", + Name = "赵六", + TenantId = 1000004, //租户Id + }, + new SysUserInfo() + { + Id = 10005, + LoginName = "sunqi", + LoginPWD = "E10ADC3949BA59ABBE56E057F20F883E", + Name = "孙七", + TenantId = 1000005, //租户Id + }, + }; + + var names = data.Select(s => s.LoginName).ToList(); + names = await db.Queryable() + .Where(s => names.Contains(s.LoginName)) + .Select(s => s.LoginName).ToListAsync(); + + var sysUserInfos = data.Where(s => !names.Contains(s.LoginName)).ToList(); + if (sysUserInfos.Any()) + { + await db.Insertable(sysUserInfos).ExecuteReturnIdentityAsync(); + } + + await Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Blog.Core.EventBus/Blog.Core.EventBus.csproj b/Blog.Core.EventBus/Blog.Core.EventBus.csproj index 070c0a48..09d4e6a2 100644 --- a/Blog.Core.EventBus/Blog.Core.EventBus.csproj +++ b/Blog.Core.EventBus/Blog.Core.EventBus.csproj @@ -12,7 +12,7 @@ - + diff --git a/Blog.Core.Extensions/Blog.Core.Extensions.csproj b/Blog.Core.Extensions/Blog.Core.Extensions.csproj index 50969b05..451d24f1 100644 --- a/Blog.Core.Extensions/Blog.Core.Extensions.csproj +++ b/Blog.Core.Extensions/Blog.Core.Extensions.csproj @@ -21,7 +21,7 @@ - + diff --git a/Blog.Core.Extensions/HostedService/ConsulHostedService.cs b/Blog.Core.Extensions/HostedService/ConsulHostedService.cs new file mode 100644 index 00000000..df866e6a --- /dev/null +++ b/Blog.Core.Extensions/HostedService/ConsulHostedService.cs @@ -0,0 +1,71 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Consul; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace Blog.Core.Extensions.HostedService; + +public class ConsulHostedService : IHostedService +{ + private readonly IConfiguration _configuration; + private readonly IHostApplicationLifetime _hostApplicationLifetime; + private readonly ILogger _logger; + + public ConsulHostedService(IConfiguration configuration, IHostApplicationLifetime hostApplicationLifetime, ILogger logger) + { + _configuration = configuration; + _hostApplicationLifetime = hostApplicationLifetime; + _logger = logger; + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("Start Consul Service!"); + await DoWork(); + } + + public async Task DoWork() + { + if (_configuration["Middleware:Consul:Enabled"].ObjToBool()) + { + var consulClient = new ConsulClient(c => + { + //consul地址 + c.Address = new Uri(_configuration["ConsulSetting:ConsulAddress"]); + }); + + var registration = new AgentServiceRegistration() + { + ID = Guid.NewGuid().ToString(),//服务实例唯一标识 + Name = _configuration["ConsulSetting:ServiceName"],//服务名 + Address = _configuration["ConsulSetting:ServiceIP"], //服务IP + Port = int.Parse(_configuration["ConsulSetting:ServicePort"]),//服务端口 + Check = new AgentServiceCheck() + { + DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服务启动多久后注册 + Interval = TimeSpan.FromSeconds(10),//健康检查时间间隔 + HTTP = $"http://{_configuration["ConsulSetting:ServiceIP"]}:{_configuration["ConsulSetting:ServicePort"]}{_configuration["ConsulSetting:ServiceHealthCheck"]}",//健康检查地址 + Timeout = TimeSpan.FromSeconds(5)//超时时间 + } + }; + + //服务注册 + await consulClient.Agent.ServiceRegister(registration); + + //应用程序终止时,取消注册 + _hostApplicationLifetime.ApplicationStopping.Register(async () => + { + await consulClient.Agent.ServiceDeregister(registration.ID); + }); + } + } + + public Task StopAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("Stop Consul Service!"); + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Blog.Core.Extensions/HostedService/EventBusHostedService.cs b/Blog.Core.Extensions/HostedService/EventBusHostedService.cs new file mode 100644 index 00000000..7f18ed19 --- /dev/null +++ b/Blog.Core.Extensions/HostedService/EventBusHostedService.cs @@ -0,0 +1,45 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Blog.Core.Common; +using Blog.Core.EventBus; +using Blog.Core.EventBus.EventHandling; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace Blog.Core.Extensions.HostedService; + +public class EventBusHostedService : IHostedService +{ + private readonly IServiceProvider _serviceProvider; + private readonly ILogger _logger; + + public EventBusHostedService(IServiceProvider serviceProvider, ILogger logger) + { + _serviceProvider = serviceProvider; + _logger = logger; + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("Start EventBus Service!"); + await DoWork(); + } + + private Task DoWork() + { + if (AppSettings.app(new string[] { "EventBus", "Enabled" }).ObjToBool()) + { + var eventBus = _serviceProvider.GetRequiredService(); + eventBus.Subscribe(); + } + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("Stop EventBus Service!"); + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Blog.Core.Extensions/HostedService/QuartzJobHostedService.cs b/Blog.Core.Extensions/HostedService/QuartzJobHostedService.cs new file mode 100644 index 00000000..d8f4d602 --- /dev/null +++ b/Blog.Core.Extensions/HostedService/QuartzJobHostedService.cs @@ -0,0 +1,67 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Blog.Core.Common; +using Blog.Core.IServices; +using Blog.Core.Tasks; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace Blog.Core.Extensions.HostedService; + +public class QuartzJobHostedService : IHostedService +{ + private readonly ITasksQzServices _tasksQzServices; + private readonly ISchedulerCenter _schedulerCenter; + private readonly ILogger _logger; + + public QuartzJobHostedService(ITasksQzServices tasksQzServices, ISchedulerCenter schedulerCenter, ILogger logger) + { + _tasksQzServices = tasksQzServices; + _schedulerCenter = schedulerCenter; + _logger = logger; + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("Start QuartzJob Service!"); + await DoWork(); + } + + private async Task DoWork() + { + try + { + if (AppSettings.app("Middleware", "QuartzNetJob", "Enabled").ObjToBool()) + { + var allQzServices = await _tasksQzServices.Query(); + foreach (var item in allQzServices) + { + if (item.IsStart) + { + var result = await _schedulerCenter.AddScheduleJobAsync(item); + if (result.success) + { + Console.WriteLine($"QuartzNetJob{item.Name}启动成功!"); + } + else + { + Console.WriteLine($"QuartzNetJob{item.Name}启动失败!错误信息:{result.msg}"); + } + } + } + } + } + catch (Exception e) + { + _logger.LogError(e, "An error was reported when starting the job service."); + throw; + } + } + + public Task StopAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("Stop QuartzJob Service!"); + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Blog.Core.Extensions/HostedService/SeedDataHostedService.cs b/Blog.Core.Extensions/HostedService/SeedDataHostedService.cs new file mode 100644 index 00000000..868d750e --- /dev/null +++ b/Blog.Core.Extensions/HostedService/SeedDataHostedService.cs @@ -0,0 +1,58 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Blog.Core.Common; +using Blog.Core.Common.Seed; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace Blog.Core.Extensions; + +public sealed class SeedDataHostedService : IHostedService +{ + private readonly MyContext _myContext; + private readonly ILogger _logger; + private readonly string _webRootPath; + + public SeedDataHostedService( + MyContext myContext, + IWebHostEnvironment webHostEnvironment, + ILogger logger) + { + _myContext = myContext; + _logger = logger; + _webRootPath = webHostEnvironment.WebRootPath; + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("Start Initialization Db Seed Service!"); + await DoWork(); + } + + private async Task DoWork() + { + try + { + if (AppSettings.app("AppSettings", "SeedDBEnabled").ObjToBool() || AppSettings.app("AppSettings", "SeedDBDataEnabled").ObjToBool()) + { + await DBSeed.SeedAsync(_myContext, _webRootPath); + + //多租户 同步 + await DBSeed.TenantSeedAsync(_myContext); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Error occured seeding the Database."); + throw; + } + } + + public Task StopAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("Stop Initialization Db Seed Service!"); + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Blog.Core.Extensions/Middlewares/RequRespLogMiddleware.cs b/Blog.Core.Extensions/Middlewares/RequRespLogMiddleware.cs index 6ff8d044..4c61ed3b 100644 --- a/Blog.Core.Extensions/Middlewares/RequRespLogMiddleware.cs +++ b/Blog.Core.Extensions/Middlewares/RequRespLogMiddleware.cs @@ -7,7 +7,6 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Newtonsoft.Json; -using Ubiety.Dns.Core.Common; namespace Blog.Core.Extensions.Middlewares { diff --git a/Blog.Core.Extensions/ServiceExtensions/EventBusSetup.cs b/Blog.Core.Extensions/ServiceExtensions/EventBusSetup.cs index 1aae5ed5..4ae98830 100644 --- a/Blog.Core.Extensions/ServiceExtensions/EventBusSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/EventBusSetup.cs @@ -1,11 +1,9 @@ -using Autofac; +using System; +using Autofac; using Blog.Core.Common; using Blog.Core.EventBus; -using Blog.Core.EventBus.EventHandling; -using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using System; namespace Blog.Core.Extensions { @@ -43,23 +41,12 @@ public static void AddEventBusSetup(this IServiceCollection services) return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); }); } - if(AppSettings.app(new string[] { "Kafka", "Enabled" }).ObjToBool()) + if (AppSettings.app(new string[] { "Kafka", "Enabled" }).ObjToBool()) { services.AddHostedService(); services.AddSingleton(); } } } - - - public static void ConfigureEventBus(this IApplicationBuilder app) - { - if (AppSettings.app(new string[] { "EventBus", "Enabled" }).ObjToBool()) - { - var eventBus = app.ApplicationServices.GetRequiredService(); - - eventBus.Subscribe(); - } - } } -} +} \ No newline at end of file diff --git a/Blog.Core.Extensions/ServiceExtensions/InitializationHostServiceSetup.cs b/Blog.Core.Extensions/ServiceExtensions/InitializationHostServiceSetup.cs new file mode 100644 index 00000000..fea6a5ad --- /dev/null +++ b/Blog.Core.Extensions/ServiceExtensions/InitializationHostServiceSetup.cs @@ -0,0 +1,24 @@ +using System; +using Blog.Core.Extensions.HostedService; +using Microsoft.Extensions.DependencyInjection; + +namespace Blog.Core.Extensions; + +public static class InitializationHostServiceSetup +{ + /// + /// 应用初始化服务注入 + /// + /// + public static void AddInitializationHostServiceSetup(this IServiceCollection services) + { + if (services is null) + { + ArgumentNullException.ThrowIfNull(nameof(services)); + } + services.AddHostedService(); + services.AddHostedService(); + services.AddHostedService(); + services.AddHostedService(); + } +} \ No newline at end of file diff --git a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs index b3eb4bdc..3ecf224a 100644 --- a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs @@ -9,6 +9,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Blog.Core.Common.DB.Aop; namespace Blog.Core.Extensions { @@ -101,7 +102,21 @@ public static void AddSqlsugarSetup(this IServiceCollection services) } ); }); - return new SqlSugarScope(listConfig); + return new SqlSugarScope(listConfig, db => + { + listConfig.ForEach(config => + { + var dbProvider = db.GetConnectionScope((string)config.ConfigId); + + // 数据审计 + dbProvider.Aop.DataExecuting = SqlSugarAop.DataExecuting; + + // 配置实体假删除过滤器 + RepositorySetting.SetDeletedEntityFilter(dbProvider); + // 配置实体数据权限 + RepositorySetting.SetTenantEntityFilter(dbProvider); + }); + }); }); } diff --git a/Blog.Core.Gateway/Blog.Core.Gateway.csproj b/Blog.Core.Gateway/Blog.Core.Gateway.csproj index 47398520..f784da5c 100644 --- a/Blog.Core.Gateway/Blog.Core.Gateway.csproj +++ b/Blog.Core.Gateway/Blog.Core.Gateway.csproj @@ -15,9 +15,9 @@ - - - + + + diff --git a/Blog.Core.Gateway/Program.cs b/Blog.Core.Gateway/Program.cs index 1ba46816..9c1ba1ce 100644 --- a/Blog.Core.Gateway/Program.cs +++ b/Blog.Core.Gateway/Program.cs @@ -15,7 +15,9 @@ public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) => { - config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: true) + config.AddJsonFile("appsettings.gw.json", optional: true, reloadOnChange: false) + .AddJsonFile($"appsettings.gw.{hostingContext.HostingEnvironment.EnvironmentName}.json", optional: true, reloadOnChange: false) + .AddJsonFile("ocelot.json", optional: true, reloadOnChange: true) .AddJsonFile($"ocelot.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true); }) .ConfigureWebHostDefaults(webBuilder => diff --git a/Blog.Core.Gateway/appsettings.gw.Development.json b/Blog.Core.Gateway/appsettings.gw.Development.json new file mode 100644 index 00000000..8983e0fc --- /dev/null +++ b/Blog.Core.Gateway/appsettings.gw.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/Blog.Core.Gateway/appsettings.gw.json b/Blog.Core.Gateway/appsettings.gw.json new file mode 100644 index 00000000..33b99ee8 --- /dev/null +++ b/Blog.Core.Gateway/appsettings.gw.json @@ -0,0 +1,103 @@ +{ + "Logging": { + "IncludeScopes": false, + "Debug": { + "LogLevel": { + "Default": "Warning" + } + }, + "Console": { + "LogLevel": { + "Default": "Warning", + "Microsoft.Hosting.Lifetime": "Debug" + } + } + }, + "AllowedHosts": "*", + "Startup": { + "Cors": { + "PolicyName": "CorsIpAccess", + "EnableAllIPs": false, + "IPs": "http://127.0.0.1:2364,http://localhost:2364" + } + }, + "Audience": { + "Secret": "sdfsdfsrty45634kkhllghtdgdfss345t678fs", + "SecretFile": "C:\\my-file\\blog.core.audience.secret.txt", + "Issuer": "Blog.Core", + "Audience": "wr" + }, + "WhiteList": [ + { "url": "/" }, + { "url": "/illagal/****" }, + { "url": "/api3/****" }, + { "url": "/baseapi/swagger.json" } + ], + "BlackList": [ + { "url": "/favicon.ico" } + ], + "ApiGateWay": { + "OcelotConfig": "OcelotConfig.json", + "OcelotConfigGroup": "DEFAULT_GROUP", + "AppConfig": "****.****.Gateway.json", + "AppConfigGroup": "DEFAULT_GROUP", + "PermissionServName": "****.****.Api", + "PermissionServGroup": "DEFAULT_GROUP", + "PermissionServUrl": "/api/Permission/GetPermissionlist" + }, + "Influxdb": { + "Endpoint": "http://*******:9328", + "uid": "root", + "pwd": "*****", + "dbname": "mndata" + }, + "nacos": { + "ServerAddresses": [ "http://******:8848/" ], + "ServiceName": "*****.****.Gateway", + "DefaultTimeOut": 15000, + "Namespace": "****", + "ListenInterval": 1000, + "GroupName": "DEFAULT_GROUP", + "ClusterName": "DEFAULT", + "Ip": "", + "PreferredNetworks": "", + "Port": 8090, + "Weight": 100, + "RegisterEnabled": true, + "InstanceEnabled": true, + "Ephemeral": true, + "Secure": false, + "AccessKey": "", + "SecretKey": "", + "UserName": "****", + "Password": "*****", + "NamingUseRpc": true, + "NamingLoadCacheAtStart": "", + "LBStrategy": "WeightRandom", + "Metadata": { + "aa": "bb", + "cc": "dd", + "endpoint33": "******:8090" + } + }, + "nacosConfig": { + "ServiceName": "*****.*****.Gateway", + "Optional": false, + "DataId": "options1", + "Tenant": "******", + "Group": "DEFAULT_GROUP", + "Namespace": "*****", + "ServerAddresses": [ "http://******:8848/" ], + "UserName": "****", + "Password": "*****", + "AccessKey": "", + "SecretKey": "", + "EndPoint": "", + "ConfigUseRpc": true, + "ConfigFilterAssemblies": [ "apigateway" ], + "ConfigFilterExtInfo": "{\"JsonPaths\":[\"ConnectionStrings.Default\"],\"Other\":\"xxxxxx\"}" + } + + + +} diff --git a/Blog.Core.IServices/BASE/IBaseServices.cs b/Blog.Core.IServices/BASE/IBaseServices.cs index de4a7a06..a496b59f 100644 --- a/Blog.Core.IServices/BASE/IBaseServices.cs +++ b/Blog.Core.IServices/BASE/IBaseServices.cs @@ -10,19 +10,20 @@ namespace Blog.Core.IServices.BASE { public interface IBaseServices where TEntity : class { + ISqlSugarClient Db { get; } Task QueryById(object objId); Task QueryById(object objId, bool blnUseCache = false); Task> QueryByIDs(object[] lstIds); - Task Add(TEntity model); + Task Add(TEntity model); - Task Add(List listEntity); + Task> Add(List listEntity); Task DeleteById(object id); Task Delete(TEntity model); - + Task DeleteByIds(object[] ids); Task Update(TEntity model); @@ -59,6 +60,14 @@ Task> QueryMuch( Expression> selectExpression, Expression> whereLambda = null) where T : class, new(); Task> QueryPage(PaginationModel pagination); + + #region 分表 + Task QueryByIdSplit(object objId); + Task> AddSplit(TEntity entity); + Task DeleteSplit(TEntity entity, DateTime dateTime); + Task UpdateSplit(TEntity entity, DateTime dateTime); + Task> QueryPageSplit(Expression> whereExpression, DateTime beginTime, DateTime endTime, int pageIndex = 1, int pageSize = 20, string orderByFields = null); + #endregion } } diff --git a/Blog.Core.IServices/IBlogArticleServices.cs b/Blog.Core.IServices/IBlogArticleServices.cs index 23e6081b..a38826fb 100644 --- a/Blog.Core.IServices/IBlogArticleServices.cs +++ b/Blog.Core.IServices/IBlogArticleServices.cs @@ -9,7 +9,7 @@ namespace Blog.Core.IServices public interface IBlogArticleServices :IBaseServices { Task> GetBlogs(); - Task GetBlogDetails(int id); + Task GetBlogDetails(long id); } diff --git a/Blog.Core.IServices/IRoleModulePermissionServices.cs b/Blog.Core.IServices/IRoleModulePermissionServices.cs index 22532479..2a5c7345 100644 --- a/Blog.Core.IServices/IRoleModulePermissionServices.cs +++ b/Blog.Core.IServices/IRoleModulePermissionServices.cs @@ -21,6 +21,6 @@ public interface IRoleModulePermissionServices :IBaseServices˵ /// ӿ /// - Task UpdateModuleId(int permissionId, int moduleId); + Task UpdateModuleId(long permissionId, long moduleId); } } diff --git a/Blog.Core.IServices/ISplitDemoServices.cs b/Blog.Core.IServices/ISplitDemoServices.cs new file mode 100644 index 00000000..55215761 --- /dev/null +++ b/Blog.Core.IServices/ISplitDemoServices.cs @@ -0,0 +1,15 @@ + + +using Blog.Core.IServices.BASE; +using Blog.Core.Model.Models; +using System.Threading.Tasks; + +namespace Blog.Core.IServices +{ + /// + /// sysUserInfoServices + /// + public interface ISplitDemoServices : IBaseServices + { + } +} diff --git a/Blog.Core.IServices/ITasksLogServices.cs b/Blog.Core.IServices/ITasksLogServices.cs new file mode 100644 index 00000000..fb6ad8a7 --- /dev/null +++ b/Blog.Core.IServices/ITasksLogServices.cs @@ -0,0 +1,19 @@ + +using System; +using System.Threading.Tasks; +using Blog.Core.IServices.BASE; +using Blog.Core.Model; +using Blog.Core.Model.Models; + +namespace Blog.Core.IServices +{ + /// + /// ITasksLogServices + /// + public interface ITasksLogServices :IBaseServices + { + public Task> GetTaskLogs(long jobId, int page, int intPageSize,DateTime? runTime,DateTime? endTime); + public Task GetTaskOverview(long jobId, DateTime? runTime, DateTime? endTime, string type); + } +} + \ No newline at end of file diff --git a/Blog.Core.IServices/ITenantService.cs b/Blog.Core.IServices/ITenantService.cs new file mode 100644 index 00000000..0927b793 --- /dev/null +++ b/Blog.Core.IServices/ITenantService.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; +using Blog.Core.IServices.BASE; +using Blog.Core.Model.Models; + +namespace Blog.Core.IServices; + +public interface ITenantService : IBaseServices +{ + public Task SaveTenant(SysTenant tenant); + + public Task InitTenantDb(SysTenant tenant); +} \ No newline at end of file diff --git a/Blog.Core.IServices/IUserRoleServices.cs b/Blog.Core.IServices/IUserRoleServices.cs index 9e7d3d29..91272a09 100644 --- a/Blog.Core.IServices/IUserRoleServices.cs +++ b/Blog.Core.IServices/IUserRoleServices.cs @@ -10,8 +10,8 @@ namespace Blog.Core.IServices public interface IUserRoleServices :IBaseServices { - Task SaveUserRole(int uid, int rid); - Task GetRoleIdByUid(int uid); + Task SaveUserRole(long uid, long rid); + Task GetRoleIdByUid(long uid); } } diff --git a/Blog.Core.Model/Blog.Core.Model.csproj b/Blog.Core.Model/Blog.Core.Model.csproj index de7e6467..e66c5c5b 100644 --- a/Blog.Core.Model/Blog.Core.Model.csproj +++ b/Blog.Core.Model/Blog.Core.Model.csproj @@ -16,9 +16,13 @@ - + + + + + diff --git a/Blog.Core.Model/Models/AccessTrendLog.cs b/Blog.Core.Model/Models/AccessTrendLog.cs index 4a87b13e..fd6dbae7 100644 --- a/Blog.Core.Model/Models/AccessTrendLog.cs +++ b/Blog.Core.Model/Models/AccessTrendLog.cs @@ -6,7 +6,7 @@ namespace Blog.Core.Model.Models /// /// 用户访问趋势日志 /// - public class AccessTrendLog : RootEntityTkey + public class AccessTrendLog : RootEntityTkey { /// /// 用户 diff --git a/Blog.Core.Model/Models/Advertisement.cs b/Blog.Core.Model/Models/Advertisement.cs index c2babd74..3b11b21f 100644 --- a/Blog.Core.Model/Models/Advertisement.cs +++ b/Blog.Core.Model/Models/Advertisement.cs @@ -3,7 +3,7 @@ namespace Blog.Core.Model.Models { - public class Advertisement : RootEntityTkey + public class Advertisement : RootEntityTkey { /// diff --git a/Blog.Core.Model/Models/BlogArticle.cs b/Blog.Core.Model/Models/BlogArticle.cs index 52176190..8b75c8df 100644 --- a/Blog.Core.Model/Models/BlogArticle.cs +++ b/Blog.Core.Model/Models/BlogArticle.cs @@ -1,5 +1,6 @@ using SqlSugar; using System; +using System.Collections.Generic; namespace Blog.Core.Model.Models { @@ -12,14 +13,18 @@ public class BlogArticle /// 主键 /// /// 这里之所以没用RootEntity,是想保持和之前的数据库一致,主键是bID,不是Id - [SugarColumn(IsNullable = false, IsPrimaryKey = true, IsIdentity = true)] - public int bID { get; set; } + [SugarColumn(IsNullable = false, IsPrimaryKey = true, IsIdentity = false)] + public long bID { get; set; } + /// /// 创建人 /// [SugarColumn(Length = 600, IsNullable = true)] public string bsubmitter { get; set; } + [Navigate(NavigateType.OneToOne, nameof(bsubmitter))] + public SysUserInfo User { get; set; } + /// /// 标题blog /// @@ -57,6 +62,7 @@ public class BlogArticle /// 创建时间 /// public System.DateTime bCreateTime { get; set; } + /// /// 备注 /// @@ -69,5 +75,11 @@ public class BlogArticle [SugarColumn(IsNullable = true)] public bool? IsDeleted { get; set; } + + /// + /// 评论 + /// + [Navigate(NavigateType.OneToMany, nameof(BlogArticleComment.bID))] + public List Comments { get; set; } } -} +} \ No newline at end of file diff --git a/Blog.Core.Model/Models/BlogArticleComment.cs b/Blog.Core.Model/Models/BlogArticleComment.cs new file mode 100644 index 00000000..519fb003 --- /dev/null +++ b/Blog.Core.Model/Models/BlogArticleComment.cs @@ -0,0 +1,19 @@ +using SqlSugar; + +namespace Blog.Core.Model.Models; + +/// +/// 博客文章 评论 +/// +public class BlogArticleComment : RootEntityTkey +{ + public long bID { get; set; } + + public string Comment { get; set; } + + + public string UserId { get; set; } + + [Navigate(NavigateType.OneToOne, nameof(UserId))] + public SysUserInfo User { get; set; } +} \ No newline at end of file diff --git a/Blog.Core.Model/Models/Department.cs b/Blog.Core.Model/Models/Department.cs index 3583bcff..424bcf44 100644 --- a/Blog.Core.Model/Models/Department.cs +++ b/Blog.Core.Model/Models/Department.cs @@ -7,7 +7,7 @@ namespace Blog.Core.Model.Models /// /// 部门表 /// - public class Department : DepartmentRoot + public class Department : DepartmentRoot { /// /// Desc:部门关系编码 diff --git a/Blog.Core.Model/Models/GblLogAudit.cs b/Blog.Core.Model/Models/GblLogAudit.cs index 4b1bd9cd..2cecce8b 100644 --- a/Blog.Core.Model/Models/GblLogAudit.cs +++ b/Blog.Core.Model/Models/GblLogAudit.cs @@ -12,8 +12,8 @@ public class GblLogAudit /// ///ID /// - [SugarColumn(ColumnDescription = "ID", IsNullable = false, IsPrimaryKey = true, IsIdentity = true)] - public int Id { get; set; } + [SugarColumn(ColumnDescription = "ID", IsNullable = false, IsPrimaryKey = true, IsIdentity = false)] + public long Id { get; set; } /// ///HttpContext.TraceIdentifier 事件链路ID(获取或设置一个唯一标识符,用于在跟踪日志中表示此请求。) diff --git a/Blog.Core.Model/Models/Guestbook.cs b/Blog.Core.Model/Models/Guestbook.cs index d1f671c0..0cd5dcef 100644 --- a/Blog.Core.Model/Models/Guestbook.cs +++ b/Blog.Core.Model/Models/Guestbook.cs @@ -3,13 +3,13 @@ namespace Blog.Core.Model.Models { - public class Guestbook:RootEntityTkey + public class Guestbook : RootEntityTkey { - + /// 博客ID /// /// - public int? blogId { get; set; } + public long? blogId { get; set; } /// 创建时间 /// /// diff --git a/Blog.Core.Model/Models/Modules.cs b/Blog.Core.Model/Models/Modules.cs index b62c0a47..6e41aaac 100644 --- a/Blog.Core.Model/Models/Modules.cs +++ b/Blog.Core.Model/Models/Modules.cs @@ -6,7 +6,7 @@ namespace Blog.Core.Model.Models /// /// 接口API地址信息表 /// - public class Modules : ModulesRoot + public class Modules : ModulesRoot { public Modules() { diff --git a/Blog.Core.Model/Models/OperateLog.cs b/Blog.Core.Model/Models/OperateLog.cs index 4086781c..3c2fb54c 100644 --- a/Blog.Core.Model/Models/OperateLog.cs +++ b/Blog.Core.Model/Models/OperateLog.cs @@ -6,7 +6,7 @@ namespace Blog.Core.Model.Models /// /// 日志记录 /// - public class OperateLog : RootEntityTkey + public class OperateLog : RootEntityTkey { /// diff --git a/Blog.Core.Model/Models/PasswordLib.cs b/Blog.Core.Model/Models/PasswordLib.cs index 2b43c265..b8b633d6 100644 --- a/Blog.Core.Model/Models/PasswordLib.cs +++ b/Blog.Core.Model/Models/PasswordLib.cs @@ -7,11 +7,11 @@ namespace Blog.Core.Model.Models /// 密码库表 /// [SugarTable("PasswordLib", "密码库表")]//('数据库表名','数据库表备注') - [TenantAttribute("WMBLOG_MYSQL_2")] //('代表是哪个数据库,名字是appsettings.json 的 ConnId') + [Tenant("WMBLOG_MYSQL_2")] //('代表是哪个数据库,名字是appsettings.json 的 ConnId') public class PasswordLib { - [SugarColumn(IsNullable = false, IsPrimaryKey = true, IsIdentity = true)] - public int PLID { get; set; } + [SugarColumn(IsNullable = false, IsPrimaryKey = true, IsIdentity = false)] + public long PLID { get; set; } /// ///获取或设置是否禁用,逻辑上的删除,非物理删除 diff --git a/Blog.Core.Model/Models/Permission.cs b/Blog.Core.Model/Models/Permission.cs index c650cb5e..deece0c0 100644 --- a/Blog.Core.Model/Models/Permission.cs +++ b/Blog.Core.Model/Models/Permission.cs @@ -7,7 +7,7 @@ namespace Blog.Core.Model.Models /// /// 路由菜单表 /// - public class Permission : PermissionRoot + public class Permission : PermissionRoot { public Permission() { diff --git a/Blog.Core.Model/Models/Role.cs b/Blog.Core.Model/Models/Role.cs index e34ccdd9..1357afb0 100644 --- a/Blog.Core.Model/Models/Role.cs +++ b/Blog.Core.Model/Models/Role.cs @@ -6,7 +6,7 @@ namespace Blog.Core.Model.Models /// /// 角色表 /// - public class Role : RootEntityTkey + public class Role : RootEntityTkey { public Role() { diff --git a/Blog.Core.Model/Models/RoleModulePermission.cs b/Blog.Core.Model/Models/RoleModulePermission.cs index 13d82a80..482b9b4e 100644 --- a/Blog.Core.Model/Models/RoleModulePermission.cs +++ b/Blog.Core.Model/Models/RoleModulePermission.cs @@ -6,7 +6,7 @@ namespace Blog.Core.Model.Models /// /// 按钮跟权限关联表 /// - public class RoleModulePermission : RoleModulePermissionRoot + public class RoleModulePermission : RoleModulePermissionRoot { public RoleModulePermission() { diff --git a/Blog.Core.Model/Models/RootTkey/BaseEntity.cs b/Blog.Core.Model/Models/RootTkey/BaseEntity.cs new file mode 100644 index 00000000..b6dabe54 --- /dev/null +++ b/Blog.Core.Model/Models/RootTkey/BaseEntity.cs @@ -0,0 +1,80 @@ +using Blog.Core.Model.Models.RootTkey.Interface; +using SqlSugar; +using System; + +namespace Blog.Core.Model.Models.RootTkey; + +public class BaseEntity : RootEntityTkey, IDeleteFilter +{ + #region 数据状态管理 + + /// + /// 状态
+ /// 中立字段,某些表可使用某些表不使用 + ///
+ public bool Enabled { get; set; } = true; + + /// + /// 中立字段,某些表可使用某些表不使用
+ /// 逻辑上的删除,非物理删除
+ /// 例如:单据删除并非直接删除 + ///
+ public bool IsDeleted { get; set; } + + /// + /// 中立字段
+ /// 是否内置数据 + ///
+ public bool IsInternal { get; set; } + + #endregion + + #region 创建 + + /// + /// 创建ID + /// + [SugarColumn(IsNullable = true, IsOnlyIgnoreUpdate = true)] + public long? CreateId { get; set; } + + /// + /// 创建者 + /// + [SugarColumn(IsNullable = true, IsOnlyIgnoreUpdate = true)] + public string CreateBy { get; set; } + + /// + /// 创建时间 + /// + [SugarColumn(IsOnlyIgnoreUpdate = true)] + public DateTime CreateTime { get; set; } = DateTime.Now; + + #endregion + + #region 修改 + + /// + /// 修改ID + /// + [SugarColumn(IsNullable = true)] + public long? ModifyId { get; set; } + + /// + /// 更新者 + /// + [SugarColumn(IsNullable = true)] + public string ModifyBy { get; set; } + + /// + /// 修改日期 + /// + public DateTime? ModifyTime { get; set; } = DateTime.Now; + + /// + /// 数据版本 + /// + [SugarColumn(DefaultValue = "0", IsEnableUpdateVersionValidation = true)] //标识版本字段 + public long Version { get; set; } + + #endregion +} \ No newline at end of file diff --git a/Blog.Core.Model/Models/RootTkey/Interface/IDeleteFilter.cs b/Blog.Core.Model/Models/RootTkey/Interface/IDeleteFilter.cs new file mode 100644 index 00000000..57d421e9 --- /dev/null +++ b/Blog.Core.Model/Models/RootTkey/Interface/IDeleteFilter.cs @@ -0,0 +1,9 @@ +namespace Blog.Core.Model.Models.RootTkey.Interface; + +/// +/// 软删除 过滤器 +/// +public interface IDeleteFilter +{ + public bool IsDeleted { get; set; } +} \ No newline at end of file diff --git a/Blog.Core.Model/Models/RootTkey/RootEntityTkey.cs b/Blog.Core.Model/Models/RootTkey/RootEntityTkey.cs index afecdec9..20bdda0d 100644 --- a/Blog.Core.Model/Models/RootTkey/RootEntityTkey.cs +++ b/Blog.Core.Model/Models/RootTkey/RootEntityTkey.cs @@ -11,7 +11,5 @@ public class RootEntityTkey where Tkey : IEquatable ///
[SugarColumn(IsNullable = false, IsPrimaryKey = true)] public Tkey Id { get; set; } - - } } \ No newline at end of file diff --git a/Blog.Core.Model/Models/SplitDemo.cs b/Blog.Core.Model/Models/SplitDemo.cs new file mode 100644 index 00000000..26329935 --- /dev/null +++ b/Blog.Core.Model/Models/SplitDemo.cs @@ -0,0 +1,27 @@ +using Newtonsoft.Json; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace Blog.Core.Model.Models +{ + [SplitTable(SplitType.Day)]//按年分表 (自带分表支持 年、季、月、周、日) + [SugarTable("SplitDemo_{year}{month}{day}")]//3个变量必须要有,这么设计为了兼容开始按年,后面改成按月、按日 + public class SplitDemo + { + [SugarColumn(IsPrimaryKey = true)] + public long Id { get; set; } + + public string Name { get; set; } + + [SugarColumn(IsNullable = true)]//设置为可空字段 (更多用法看文档 迁移) + public DateTime UpdateTime { get; set; } + + [SplitField] //分表字段 在插入的时候会根据这个字段插入哪个表,在更新删除的时候用这个字段找出相关表 + public DateTime CreateTime { get; set; } + } +} diff --git a/Blog.Core.Model/Models/SysTenant.cs b/Blog.Core.Model/Models/SysTenant.cs new file mode 100644 index 00000000..61d03866 --- /dev/null +++ b/Blog.Core.Model/Models/SysTenant.cs @@ -0,0 +1,67 @@ +using Blog.Core.Model.Tenants; +using SqlSugar; + +namespace Blog.Core.Model.Models; + +/// +/// 系统租户表
+/// 根据TenantType 分为两种方案:
+/// 1.按租户字段区分
+/// 2.按租户分库
+/// +///
+/// +/// 注意:
+/// 使用租户Id方案,无需配置分库的连接 +///
+public class SysTenant : RootEntityTkey +{ + /// + /// 名称 + /// + public string Name { get; set; } + + /// + /// 租户类型 + /// + public TenantTypeEnum TenantType { get; set; } + + /// + /// 数据库/租户标识 不可重复
+ /// 使用Id方案,可无需配置 + ///
+ [SugarColumn(Length = 64)] + public string ConfigId { get; set; } + + /// + /// 主机
+ /// 使用Id方案,可无需配置 + ///
+ [SugarColumn(IsNullable = true)] + public string Host { get; set; } + + /// + /// 数据库类型
+ /// 使用Id方案,可无需配置 + ///
+ [SugarColumn(IsNullable = true)] + public SqlSugar.DbType? DbType { get; set; } + + /// + /// 数据库连接
+ /// 使用Id方案,可无需配置 + ///
+ [SugarColumn(IsNullable = true)] + public string Connection { get; set; } + + /// + /// 状态 + /// + public bool Status { get; set; } = true; + + /// + /// 备注 + /// + [SugarColumn(IsNullable = true)] + public string Remark { get; set; } +} \ No newline at end of file diff --git a/Blog.Core.Model/Models/TasksLog.cs b/Blog.Core.Model/Models/TasksLog.cs new file mode 100644 index 00000000..c79e8077 --- /dev/null +++ b/Blog.Core.Model/Models/TasksLog.cs @@ -0,0 +1,87 @@ +using SqlSugar; +using System; + +namespace Blog.Core.Model.Models +{ + /// + /// 任务日志表 + /// + public class TasksLog : RootEntityTkey + { + /// + /// 任务ID + /// + public long JobId { get; set; } + /// + /// 任务耗时 + /// + public double TotalTime { get; set; } + /// + /// 执行结果(0-失败 1-成功) + /// + public bool RunResult { get; set; } + /// + /// 运行时间 + /// + public DateTime RunTime { get; set; } + /// + /// 结束时间 + /// + public DateTime EndTime { get; set; } + /// + /// 执行参数 + /// + [SugarColumn(Length = 500, IsNullable = true)] + public string RunPars { get; set; } + /// + /// 异常信息 + /// + [SugarColumn(Length = 500, IsNullable = true)] + public string ErrMessage { get; set; } + /// + /// 异常堆栈 + /// + [SugarColumn(Length = 2000, IsNullable = true)] + public string ErrStackTrace { get; set; } + /// + /// 创建ID + /// + [SugarColumn(IsNullable = true)] + public int? CreateId { get; set; } + /// + /// 创建者 + /// + [SugarColumn(Length = 50, IsNullable = true)] + public string CreateBy { get; set; } + /// + /// 创建时间 + /// + [SugarColumn(IsNullable = true)] + public DateTime CreateTime { get; set; } = DateTime.Now; + /// + /// 修改ID + /// + [SugarColumn(IsNullable = true)] + public int? ModifyId { get; set; } + /// + /// 修改者 + /// + [SugarColumn(Length = 100, IsNullable = true)] + public string ModifyBy { get; set; } + /// + /// 修改时间 + /// + [SugarColumn(IsNullable = true)] + public DateTime? ModifyTime { get; set; } = DateTime.Now; + /// + /// 任务名称 + /// + [SugarColumn(IsIgnore = true)] + public string Name { get; set; } + /// + /// 任务分组 + /// + [SugarColumn(IsIgnore = true)] + public string JobGroup { get; set; } + } +} diff --git a/Blog.Core.Model/Models/TasksQz.cs b/Blog.Core.Model/Models/TasksQz.cs index 5c812f79..b029a995 100644 --- a/Blog.Core.Model/Models/TasksQz.cs +++ b/Blog.Core.Model/Models/TasksQz.cs @@ -8,7 +8,7 @@ namespace Blog.Core.Model.Models /// /// 任务计划表 /// - public class TasksQz : RootEntityTkey + public class TasksQz : RootEntityTkey { /// /// 任务名称 @@ -65,6 +65,10 @@ public class TasksQz : RootEntityTkey /// public int CycleRunTimes { get; set; } /// + /// 已循环次数 + /// + public int CycleHasRunTimes { get; set; } + /// /// 是否启动 /// public bool IsStart { get; set; } = false; diff --git a/Blog.Core.Model/Models/Tenant/BusinessTable.cs b/Blog.Core.Model/Models/Tenant/BusinessTable.cs new file mode 100644 index 00000000..b3b0140a --- /dev/null +++ b/Blog.Core.Model/Models/Tenant/BusinessTable.cs @@ -0,0 +1,27 @@ +using Blog.Core.Model.Models.RootTkey; +using Blog.Core.Model.Tenants; + +namespace Blog.Core.Model.Models; + +/// +/// 业务数据
+/// 多租户 (Id 隔离) +///
+public class BusinessTable : BaseEntity, ITenantEntity +{ + /// + /// 无需手动赋值 + /// + public long TenantId { get; set; } + + + /// + /// 名称 + /// + public string Name { get; set; } + + /// + /// 金额 + /// + public decimal Amount { get; set; } +} \ No newline at end of file diff --git a/Blog.Core.Model/Models/Tenant/MultiBusinessSubTable.cs b/Blog.Core.Model/Models/Tenant/MultiBusinessSubTable.cs new file mode 100644 index 00000000..1ada394d --- /dev/null +++ b/Blog.Core.Model/Models/Tenant/MultiBusinessSubTable.cs @@ -0,0 +1,14 @@ +using Blog.Core.Model.Models.RootTkey; +using Blog.Core.Model.Tenants; + +namespace Blog.Core.Model.Models; + +/// +/// 多租户-多表方案 业务表 子表
+///
+[MultiTenant(TenantTypeEnum.Tables)] +public class MultiBusinessSubTable : BaseEntity +{ + public long MainId { get; set; } + public string Memo { get; set; } +} \ No newline at end of file diff --git a/Blog.Core.Model/Models/Tenant/MultiBusinessTable.cs b/Blog.Core.Model/Models/Tenant/MultiBusinessTable.cs new file mode 100644 index 00000000..619bdaaf --- /dev/null +++ b/Blog.Core.Model/Models/Tenant/MultiBusinessTable.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using Blog.Core.Model.Models.RootTkey; +using Blog.Core.Model.Tenants; +using SqlSugar; + +namespace Blog.Core.Model.Models; + +/// +/// 多租户-多表方案 业务表
+///
+[MultiTenant(TenantTypeEnum.Tables)] +public class MultiBusinessTable : BaseEntity +{ + /// + /// 名称 + /// + public string Name { get; set; } + + /// + /// 金额 + /// + public decimal Amount { get; set; } + + [Navigate(NavigateType.OneToMany, nameof(MultiBusinessSubTable.MainId))] + public List Child { get; set; } +} \ No newline at end of file diff --git a/Blog.Core.Model/Models/Tenant/SubLibraryBusinessTable.cs b/Blog.Core.Model/Models/Tenant/SubLibraryBusinessTable.cs new file mode 100644 index 00000000..446fb436 --- /dev/null +++ b/Blog.Core.Model/Models/Tenant/SubLibraryBusinessTable.cs @@ -0,0 +1,22 @@ +using Blog.Core.Model.Models.RootTkey; +using Blog.Core.Model.Tenants; + +namespace Blog.Core.Model.Models; + +/// +/// 多租户-多库方案 业务表
+/// 公共库无需标记[MultiTenant]特性 +///
+[MultiTenant] +public class SubLibraryBusinessTable : BaseEntity +{ + /// + /// 名称 + /// + public string Name { get; set; } + + /// + /// 金额 + /// + public decimal Amount { get; set; } +} \ No newline at end of file diff --git a/Blog.Core.Model/Models/TestModels.cs b/Blog.Core.Model/Models/TestModels.cs index f5fa7dc7..8a8d123c 100644 --- a/Blog.Core.Model/Models/TestModels.cs +++ b/Blog.Core.Model/Models/TestModels.cs @@ -5,9 +5,9 @@ public class TestMuchTableResult { public string moduleName { get; set; } public string permName { get; set; } - public int rid { get; set; } - public int mid { get; set; } - public int? pid { get; set; } + public long rid { get; set; } + public long mid { get; set; } + public long? pid { get; set; } } } diff --git a/Blog.Core.Model/Models/Topic.cs b/Blog.Core.Model/Models/Topic.cs index 16bf7dad..e57bd561 100644 --- a/Blog.Core.Model/Models/Topic.cs +++ b/Blog.Core.Model/Models/Topic.cs @@ -7,7 +7,7 @@ namespace Blog.Core.Model.Models /// /// Tibug 类别 /// - public class Topic : RootEntityTkey + public class Topic : RootEntityTkey { public Topic() { diff --git a/Blog.Core.Model/Models/TopicDetail.cs b/Blog.Core.Model/Models/TopicDetail.cs index 87e16ebf..1a98f3af 100644 --- a/Blog.Core.Model/Models/TopicDetail.cs +++ b/Blog.Core.Model/Models/TopicDetail.cs @@ -6,7 +6,7 @@ namespace Blog.Core.Model.Models /// /// Tibug 博文 /// - public class TopicDetail : TopicDetailRoot + public class TopicDetail : TopicDetailRoot { public TopicDetail() { diff --git a/Blog.Core.Model/Models/UserRole.cs b/Blog.Core.Model/Models/UserRole.cs index 996eea2c..7ed9c6be 100644 --- a/Blog.Core.Model/Models/UserRole.cs +++ b/Blog.Core.Model/Models/UserRole.cs @@ -6,11 +6,11 @@ namespace Blog.Core.Model.Models /// /// 用户跟角色关联表 /// - public class UserRole : UserRoleRoot + public class UserRole : UserRoleRoot { public UserRole() { } - public UserRole(int uid, int rid) + public UserRole(long uid, long rid) { UserId = uid; RoleId = rid; @@ -31,7 +31,7 @@ public UserRole(int uid, int rid) /// 创建ID ///
[SugarColumn(IsNullable = true)] - public int? CreateId { get; set; } + public long? CreateId { get; set; } /// /// 创建者 /// diff --git a/Blog.Core.Model/Models/WeChatConfig.cs b/Blog.Core.Model/Models/WeChatConfig.cs index f0aa97c4..16910487 100644 --- a/Blog.Core.Model/Models/WeChatConfig.cs +++ b/Blog.Core.Model/Models/WeChatConfig.cs @@ -51,13 +51,13 @@ public class WeChatConfig /// /// 公众号推送token /// - [SugarColumn(Length = 100, IsNullable = true)] + [SugarColumn(Length = 500, IsNullable = true)] public string token { get; set; } /// /// 验证秘钥(验证消息是否真实) /// - [SugarColumn(Length = 100, IsNullable = false)] + [SugarColumn(Length = 500, IsNullable = false)] public string interactiveToken { get; set; } /// diff --git a/Blog.Core.Model/Models/WeChatPushLog.cs b/Blog.Core.Model/Models/WeChatPushLog.cs index b2b6752e..5368c806 100644 --- a/Blog.Core.Model/Models/WeChatPushLog.cs +++ b/Blog.Core.Model/Models/WeChatPushLog.cs @@ -50,7 +50,7 @@ public partial class WeChatPushLog /// /// 推送内容 /// - [SugarColumn(IsNullable = true)] + [SugarColumn(Length = 2000, IsNullable = true)] public string PushLogContent { get; set; } /// diff --git a/Blog.Core.Model/Models/sysUserInfo.cs b/Blog.Core.Model/Models/sysUserInfo.cs index c60e4bce..2a417a16 100644 --- a/Blog.Core.Model/Models/sysUserInfo.cs +++ b/Blog.Core.Model/Models/sysUserInfo.cs @@ -8,8 +8,8 @@ namespace Blog.Core.Model.Models /// 用户信息表 /// //[SugarTable("SysUserInfo")] - [SugarTable("SysUserInfo", "用户表")]//('数据库表名','数据库表备注') - public class SysUserInfo : SysUserInfoRoot + [SugarTable("SysUserInfo", "用户表")] //('数据库表名','数据库表备注') + public class SysUserInfo : SysUserInfoRoot { public SysUserInfo() { @@ -120,12 +120,20 @@ public SysUserInfo(string loginName, string loginPWD) [SugarColumn(IsNullable = true)] public bool IsDeleted { get; set; } + /// + /// 租户Id + /// + [SugarColumn(IsNullable = false,DefaultValue = "0")] + public long TenantId { get; set; } + + [Navigate(NavigateType.OneToOne, nameof(TenantId))] + public SysTenant Tenant { get; set; } [SugarColumn(IsIgnore = true)] public List RoleNames { get; set; } [SugarColumn(IsIgnore = true)] - public List Dids { get; set; } + public List Dids { get; set; } [SugarColumn(IsIgnore = true)] public string DepartmentName { get; set; } diff --git a/Blog.Core.Model/Tenants/ITenantEntity.cs b/Blog.Core.Model/Tenants/ITenantEntity.cs new file mode 100644 index 00000000..2d0c5dc9 --- /dev/null +++ b/Blog.Core.Model/Tenants/ITenantEntity.cs @@ -0,0 +1,15 @@ +using SqlSugar; + +namespace Blog.Core.Model.Tenants; + +/// +/// 租户模型接口 +/// +public interface ITenantEntity +{ + /// + /// 租户Id + /// + [SugarColumn(DefaultValue = "0")] + public long TenantId { get; set; } +} \ No newline at end of file diff --git a/Blog.Core.Model/Tenants/MultiTenantAttribute.cs b/Blog.Core.Model/Tenants/MultiTenantAttribute.cs new file mode 100644 index 00000000..443745bf --- /dev/null +++ b/Blog.Core.Model/Tenants/MultiTenantAttribute.cs @@ -0,0 +1,24 @@ +using System; + +namespace Blog.Core.Model.Tenants; + +/// +/// 标识 多租户 的业务表
+/// 默认设置是多库
+/// 公共表无需区分 直接使用主库 各自业务在各自库中
+///
+[AttributeUsage(AttributeTargets.Class)] +public class MultiTenantAttribute : Attribute +{ + public MultiTenantAttribute() + { + } + + public MultiTenantAttribute(TenantTypeEnum tenantType) + { + TenantType = tenantType; + } + + + public TenantTypeEnum TenantType { get; set; } = TenantTypeEnum.Db; +} \ No newline at end of file diff --git a/Blog.Core.Model/Tenants/TenantTypeEnum.cs b/Blog.Core.Model/Tenants/TenantTypeEnum.cs new file mode 100644 index 00000000..f4af3bda --- /dev/null +++ b/Blog.Core.Model/Tenants/TenantTypeEnum.cs @@ -0,0 +1,29 @@ +using System.ComponentModel; + +namespace Blog.Core.Model.Tenants; + +/// +/// 租户隔离方案 +/// +public enum TenantTypeEnum +{ + None = 0, + + /// + /// Id隔离 + /// + [Description("Id隔离")] + Id = 1, + + /// + /// 库隔离 + /// + [Description("库隔离")] + Db = 2, + + /// + /// 表隔离 + /// + [Description("表隔离")] + Tables = 3, +} \ No newline at end of file diff --git a/Blog.Core.Model/ViewModels/BlogViewModels.cs b/Blog.Core.Model/ViewModels/BlogViewModels.cs index f959270c..86c16618 100644 --- a/Blog.Core.Model/ViewModels/BlogViewModels.cs +++ b/Blog.Core.Model/ViewModels/BlogViewModels.cs @@ -10,7 +10,7 @@ public class BlogViewModels /// /// /// - public int bID { get; set; } + public long bID { get; set; } /// 创建人 /// /// @@ -34,7 +34,7 @@ public class BlogViewModels /// /// 上一篇id /// - public int previousID { get; set; } + public long previousID { get; set; } /// /// 下一篇 @@ -44,7 +44,7 @@ public class BlogViewModels /// /// 下一篇id /// - public int nextID { get; set; } + public long nextID { get; set; } /// 类别 /// diff --git a/Blog.Core.Model/ViewModels/SysUserInfoDto.cs b/Blog.Core.Model/ViewModels/SysUserInfoDto.cs index 33d84161..3b5451f0 100644 --- a/Blog.Core.Model/ViewModels/SysUserInfoDto.cs +++ b/Blog.Core.Model/ViewModels/SysUserInfoDto.cs @@ -3,13 +3,13 @@ namespace Blog.Core.Model.ViewModels { - public class SysUserInfoDto : SysUserInfoDtoRoot + public class SysUserInfoDto : SysUserInfoDtoRoot { public string uLoginName { get; set; } public string uLoginPWD { get; set; } public string uRealName { get; set; } public int uStatus { get; set; } - public int DepartmentId { get; set; } + public long DepartmentId { get; set; } public string uRemark { get; set; } public System.DateTime uCreateTime { get; set; } = DateTime.Now; public System.DateTime uUpdateTime { get; set; } = DateTime.Now; @@ -22,7 +22,7 @@ public class SysUserInfoDto : SysUserInfoDtoRoot public string addr { get; set; } public bool tdIsDelete { get; set; } public List RoleNames { get; set; } - public List Dids { get; set; } + public List Dids { get; set; } public string DepartmentName { get; set; } } } diff --git a/Blog.Core.Publish.Linux.sh b/Blog.Core.Publish.Linux.sh index 05eb8103..7599f204 100644 --- a/Blog.Core.Publish.Linux.sh +++ b/Blog.Core.Publish.Linux.sh @@ -1,8 +1,9 @@ -git pull; + find .PublishFiles/ -type f -and ! -path '*/wwwroot/images/*' ! -name 'appsettings.*' |xargs rm -rf dotnet build; rm -rf /home/Blog.Core/Blog.Core.Api/bin/Debug/.PublishFiles; dotnet publish -o /home/Blog.Core/Blog.Core.Api/bin/Debug/.PublishFiles; +rm -rf /home/Blog.Core/Blog.Core.Api/bin/Debug/.PublishFiles/WMBlog.db; # cp -r /home/Blog.Core/Blog.Core.Api/bin/Debug/.PublishFiles ./; awk 'BEGIN { cmd="cp -ri /home/Blog.Core/Blog.Core.Api/bin/Debug/.PublishFiles ./"; print "n" |cmd; }' echo "Successfully!!!! ^ please see the file .PublishFiles"; \ No newline at end of file diff --git a/Blog.Core.Repository/BASE/BaseRepository.cs b/Blog.Core.Repository/BASE/BaseRepository.cs index ead9f9af..9d926ee4 100644 --- a/Blog.Core.Repository/BASE/BaseRepository.cs +++ b/Blog.Core.Repository/BASE/BaseRepository.cs @@ -1,6 +1,11 @@ using Blog.Core.Common; +using Blog.Core.Common.DB; using Blog.Core.IRepository.Base; using Blog.Core.Model; +using Blog.Core.Model.Models; +using Blog.Core.Model.Tenants; +using Blog.Core.Repository.UnitOfWorks; +using OfficeOpenXml.FormulaParsing.Excel.Functions.DateTime; using SqlSugar; using System; using System.Collections.Generic; @@ -8,7 +13,6 @@ using System.Linq.Expressions; using System.Reflection; using System.Threading.Tasks; -using Blog.Core.Repository.UnitOfWorks; namespace Blog.Core.Repository.Base { @@ -22,11 +26,12 @@ private ISqlSugarClient _db get { ISqlSugarClient db = _dbBase; + /* 如果要开启多库支持, * 1、在appsettings.json 中开启MutiDBEnabled节点为true,必填 * 2、设置一个主连接的数据库ID,节点MainDB,对应的连接字符串的Enabled也必须true,必填 */ - if (AppSettings.app(new[] {"MutiDBEnabled"}).ObjToBool()) + if (AppSettings.app(new[] { "MutiDBEnabled" }).ObjToBool()) { //修改使用 model备注字段作为切换数据库条件,使用sqlsugar TenantAttribute存放数据库ConnId //参考 https://www.donet5.com/Home/Doc?typeId=2246 @@ -35,6 +40,28 @@ private ISqlSugarClient _db { //统一处理 configId 小写 db = _dbBase.GetConnectionScope(tenantAttr.configId.ToString().ToLower()); + return db; + } + } + + //多租户 + var mta = typeof(TEntity).GetCustomAttribute(); + if (mta is { TenantType: TenantTypeEnum.Db }) + { + //获取租户信息 租户信息可以提前缓存下来 + if (App.User is { TenantId: > 0 }) + { + var tenant = db.Queryable().WithCache().Where(s => s.Id == App.User.TenantId).First(); + if (tenant != null) + { + var iTenant = db.AsTenant(); + if (!iTenant.IsAnyConnection(tenant.ConfigId)) + { + iTenant.AddConnection(tenant.GetConnectionConfig()); + } + + return iTenant.GetConnectionScope(tenant.ConfigId); + } } } @@ -51,12 +78,12 @@ public BaseRepository(IUnitOfWorkManage unitOfWorkManage) } - public async Task QueryById(object objId) { //return await Task.Run(() => _db.Queryable().InSingle(objId)); return await _db.Queryable().In(objId).SingleAsync(); } + /// /// 功能描述:根据ID查询一条数据 /// 作  者:Blog.Core @@ -87,7 +114,7 @@ public async Task> QueryByIDs(object[] lstIds) /// /// 博文实体类 /// - public async Task Add(TEntity entity) + public async Task Add(TEntity entity) { //var i = await Task.Run(() => _db.Insertable(entity).ExecuteReturnBigIdentity()); ////返回的i是long类型,这里你可以根据你的业务需要进行处理 @@ -98,26 +125,25 @@ public async Task Add(TEntity entity) //这里你可以返回TEntity,这样的话就可以获取id值,无论主键是什么类型 //var return3 = await insert.ExecuteReturnEntityAsync(); - return await insert.ExecuteReturnIdentityAsync(); + return await insert.ExecuteReturnSnowflakeIdAsync(); } - /// /// 写入实体数据 /// /// 实体类 /// 指定只插入列 /// 返回自增量列 - public async Task Add(TEntity entity, Expression> insertColumns = null) + public async Task Add(TEntity entity, Expression> insertColumns = null) { var insert = _db.Insertable(entity); if (insertColumns == null) { - return await insert.ExecuteReturnIdentityAsync(); + return await insert.ExecuteReturnSnowflakeIdAsync(); } else { - return await insert.InsertColumns(insertColumns).ExecuteReturnIdentityAsync(); + return await insert.InsertColumns(insertColumns).ExecuteReturnSnowflakeIdAsync(); } } @@ -126,9 +152,9 @@ public async Task Add(TEntity entity, Expression> ins /// /// 实体集合 /// 影响行数 - public async Task Add(List listEntity) + public async Task> Add(List listEntity) { - return await _db.Insertable(listEntity.ToArray()).ExecuteCommandAsync(); + return await _db.Insertable(listEntity.ToArray()).ExecuteReturnSnowflakeIdListAsync(); } /// @@ -151,10 +177,6 @@ public async Task Update(TEntity entity) /// public async Task Update(List entity) { - ////这种方式会以主键为条件 - //var i = await Task.Run(() => _db.Updateable(entity).ExecuteCommand()); - //return i > 0; - //这种方式会以主键为条件 return await _db.Updateable(entity).ExecuteCommandHasChangeAsync(); } @@ -174,25 +196,28 @@ public async Task Update(object operateAnonymousObjects) } public async Task Update( - TEntity entity, - List lstColumns = null, - List lstIgnoreColumns = null, - string where = "" - ) + TEntity entity, + List lstColumns = null, + List lstIgnoreColumns = null, + string where = "" + ) { IUpdateable up = _db.Updateable(entity); if (lstIgnoreColumns != null && lstIgnoreColumns.Count > 0) { up = up.IgnoreColumns(lstIgnoreColumns.ToArray()); } + if (lstColumns != null && lstColumns.Count > 0) { up = up.UpdateColumns(lstColumns.ToArray()); } + if (!string.IsNullOrEmpty(where)) { up = up.Where(where); } + return await up.ExecuteCommandHasChangeAsync(); } @@ -227,7 +252,6 @@ public async Task DeleteByIds(object[] ids) } - /// /// 功能描述:查询所有数据 /// 作  者:Blog.Core @@ -297,6 +321,7 @@ public async Task> Query(Expression> whereExpr { return await _db.Queryable().WhereIF(whereExpression != null, whereExpression).OrderByIF(orderByFields != null, orderByFields).ToListAsync(); } + /// /// 功能描述:查询一个列表 /// @@ -406,18 +431,16 @@ public async Task> Query( /// 排序字段,如name asc,age desc /// 数据列表 public async Task> Query( - string where, - int pageIndex, - int pageSize, - - string orderByFields) + string where, + int pageIndex, + int pageSize, + string orderByFields) { return await _db.Queryable().OrderByIF(!string.IsNullOrEmpty(orderByFields), orderByFields) .WhereIF(!string.IsNullOrEmpty(where), where).ToPageListAsync(pageIndex, pageSize); } - /// /// 分页查询[使用版本,其他分页未测试] /// @@ -428,12 +451,11 @@ public async Task> Query( /// public async Task> QueryPage(Expression> whereExpression, int pageIndex = 1, int pageSize = 20, string orderByFields = null) { - RefAsync totalCount = 0; var list = await _db.Queryable() - .OrderByIF(!string.IsNullOrEmpty(orderByFields), orderByFields) - .WhereIF(whereExpression != null, whereExpression) - .ToPageListAsync(pageIndex, pageSize, totalCount); + .OrderByIF(!string.IsNullOrEmpty(orderByFields), orderByFields) + .WhereIF(whereExpression != null, whereExpression) + .ToPageListAsync(pageIndex, pageSize, totalCount); return new PageModel(pageIndex, totalCount, pageSize, list); } @@ -459,6 +481,7 @@ public async Task> QueryMuch( { return await _db.Queryable(joinExpression).Select(selectExpression).ToListAsync(); } + return await _db.Queryable(joinExpression).Where(whereLambda).Select(selectExpression).ToListAsync(); } @@ -484,13 +507,12 @@ public async Task> QueryTabsPage( int pageSize = 20, string orderByFields = null) { - RefAsync totalCount = 0; var list = await _db.Queryable(joinExpression) - .Select(selectExpression) - .OrderByIF(!string.IsNullOrEmpty(orderByFields), orderByFields) - .WhereIF(whereExpression != null, whereExpression) - .ToPageListAsync(pageIndex, pageSize, totalCount); + .Select(selectExpression) + .OrderByIF(!string.IsNullOrEmpty(orderByFields), orderByFields) + .WhereIF(whereExpression != null, whereExpression) + .ToPageListAsync(pageIndex, pageSize, totalCount); return new PageModel(pageIndex, totalCount, pageSize, list); } @@ -519,10 +541,10 @@ public async Task> QueryTabsPage( { RefAsync totalCount = 0; var list = await _db.Queryable(joinExpression).GroupBy(groupExpression) - .Select(selectExpression) - .OrderByIF(!string.IsNullOrEmpty(orderByFields), orderByFields) - .WhereIF(whereExpression != null, whereExpression) - .ToPageListAsync(pageIndex, pageSize, totalCount); + .Select(selectExpression) + .OrderByIF(!string.IsNullOrEmpty(orderByFields), orderByFields) + .WhereIF(whereExpression != null, whereExpression) + .ToPageListAsync(pageIndex, pageSize, totalCount); return new PageModel(pageIndex, totalCount, pageSize, list); } @@ -545,6 +567,80 @@ public async Task> QueryTabsPage( // jobName = s.jobName // }, exp, s => new { s.uID, s.uRealName, s.groupName, s.jobName }, model.currentPage, model.pageSize, model.orderField + " " + model.orderType); - } + #region Split分表基础接口 (基础CRUD) -} + /// + /// 分页查询[使用版本,其他分页未测试] + /// + /// 条件表达式 + /// 页码(下标0) + /// 页大小 + /// 排序字段,如name asc,age desc + /// + public async Task> QueryPageSplit(Expression> whereExpression, DateTime beginTime, DateTime endTime, int pageIndex = 1, int pageSize = 20, string orderByFields = null) + { + RefAsync totalCount = 0; + var list = await _db.Queryable().SplitTable(beginTime, endTime) + .OrderByIF(!string.IsNullOrEmpty(orderByFields), orderByFields) + .WhereIF(whereExpression != null, whereExpression) + .ToPageListAsync(pageIndex, pageSize, totalCount); + var data = new PageModel(pageIndex, totalCount, pageSize, list); + return data; + } + + /// + /// 写入实体数据 + /// + /// 数据实体 + /// + public async Task> AddSplit(TEntity entity) + { + var insert = _db.Insertable(entity).SplitTable(); + //插入并返回雪花ID并且自动赋值ID  + return await insert.ExecuteReturnSnowflakeIdListAsync(); + } + + /// + /// 更新实体数据 + /// + /// 数据实体 + /// + public async Task UpdateSplit(TEntity entity, DateTime dateTime) + { + //直接根据实体集合更新 (全自动 找表更新) + //return await _db.Updateable(entity).SplitTable().ExecuteCommandAsync();//,SplitTable不能少 + + //精准找单个表 + var tableName = _db.SplitHelper().GetTableName(dateTime); //根据时间获取表名 + return await _db.Updateable(entity).AS(tableName).ExecuteCommandHasChangeAsync(); + } + + /// + /// 删除数据 + /// + /// + /// + /// + public async Task DeleteSplit(TEntity entity, DateTime dateTime) + { + ////直接根据实体集合删除 (全自动 找表插入),返回受影响数 + //return await _db.Deleteable(entity).SplitTable().ExecuteCommandAsync();//,SplitTable不能少 + + //精准找单个表 + var tableName = _db.SplitHelper().GetTableName(dateTime); //根据时间获取表名 + return await _db.Deleteable().AS(tableName).Where(entity).ExecuteCommandHasChangeAsync(); + } + + /// + /// 根据ID查找数据 + /// + /// + /// + public async Task QueryByIdSplit(object objId) + { + return await _db.Queryable().In(objId).SplitTable(tabs => tabs).SingleAsync(); + } + + #endregion + } +} \ No newline at end of file diff --git a/Blog.Core.Repository/BASE/IBaseRepository.cs b/Blog.Core.Repository/BASE/IBaseRepository.cs index 9b72a00d..77e9a808 100644 --- a/Blog.Core.Repository/BASE/IBaseRepository.cs +++ b/Blog.Core.Repository/BASE/IBaseRepository.cs @@ -33,14 +33,14 @@ public interface IBaseRepository where TEntity : class /// /// /// - Task Add(TEntity model); + Task Add(TEntity model); /// /// 批量添加 /// /// /// - Task Add(List listEntity); + Task> Add(List listEntity); /// /// 根据id 删除某一实体 @@ -214,5 +214,45 @@ Task> QueryTabsPage( int pageIndex = 1, int pageSize = 20, string orderByFields = null); + + #region 分表 + /// + /// 通过ID查询 + /// + /// + /// + Task QueryByIdSplit(object objId); + /// + /// 自动分表插入 + /// + /// + /// + Task> AddSplit(TEntity entity); + /// + /// 删除 + /// + /// + /// + /// + Task DeleteSplit(TEntity entity, DateTime dateTime); + /// + /// 更新 + /// + /// + /// + /// + Task UpdateSplit(TEntity entity, DateTime dateTime); + /// + /// 分页查询 + /// + /// + /// + /// + /// + /// + /// + /// + Task> QueryPageSplit(Expression> whereExpression, DateTime beginTime, DateTime endTime, int pageIndex = 1, int pageSize = 20, string orderByFields = null); + #endregion } } diff --git a/Blog.Core.Repository/Blog.Core.Repository.csproj b/Blog.Core.Repository/Blog.Core.Repository.csproj index 5c9764b0..df05250e 100644 --- a/Blog.Core.Repository/Blog.Core.Repository.csproj +++ b/Blog.Core.Repository/Blog.Core.Repository.csproj @@ -6,9 +6,9 @@ - - - + + + diff --git a/Blog.Core.Repository/IRoleModulePermissionRepository.cs b/Blog.Core.Repository/IRoleModulePermissionRepository.cs index c66448f0..9ba3d4ed 100644 --- a/Blog.Core.Repository/IRoleModulePermissionRepository.cs +++ b/Blog.Core.Repository/IRoleModulePermissionRepository.cs @@ -19,6 +19,6 @@ public interface IRoleModulePermissionRepository : IBaseRepository菜单主键 /// 接口主键 /// - Task UpdateModuleId(int permissionId, int moduleId); + Task UpdateModuleId(long permissionId, long moduleId); } } diff --git a/Blog.Core.Repository/RoleModulePermissionRepository.cs b/Blog.Core.Repository/RoleModulePermissionRepository.cs index 1cb21ebf..9438ff50 100644 --- a/Blog.Core.Repository/RoleModulePermissionRepository.cs +++ b/Blog.Core.Repository/RoleModulePermissionRepository.cs @@ -99,7 +99,7 @@ public async Task> GetRMPMapsPage() /// 菜单主键 /// 接口主键 /// - public async Task UpdateModuleId(int permissionId, int moduleId) + public async Task UpdateModuleId(long permissionId, long moduleId) { await Db.Updateable(it => it.ModuleId == moduleId).Where( it => it.PermissionId == permissionId).ExecuteCommandAsync(); diff --git a/Blog.Core.Services/BASE/BaseServices.cs b/Blog.Core.Services/BASE/BaseServices.cs index 232c6fae..44042382 100644 --- a/Blog.Core.Services/BASE/BaseServices.cs +++ b/Blog.Core.Services/BASE/BaseServices.cs @@ -17,13 +17,17 @@ public BaseServices(IBaseRepository BaseDal = null) { this.BaseDal = BaseDal; } + //public IBaseRepository baseDal = new BaseRepository(); - public IBaseRepository BaseDal { get; set; }//通过在子类的构造函数中注入,这里是基类,不用构造函数 + public IBaseRepository BaseDal { get; set; } //通过在子类的构造函数中注入,这里是基类,不用构造函数 + + public ISqlSugarClient Db => BaseDal.Db; public async Task QueryById(object objId) { return await BaseDal.QueryById(objId); } + /// /// 功能描述:根据ID查询一条数据 /// 作  者:AZLinli.Blog.Core @@ -52,7 +56,7 @@ public async Task> QueryByIDs(object[] lstIds) /// /// 博文实体类 /// - public async Task Add(TEntity entity) + public async Task Add(TEntity entity) { return await BaseDal.Add(entity); } @@ -62,7 +66,7 @@ public async Task Add(TEntity entity) /// /// 实体集合 /// 影响行数 - public async Task Add(List listEntity) + public async Task> Add(List listEntity) { return await BaseDal.Add(listEntity); } @@ -85,21 +89,23 @@ public async Task Update(List entity) { return await BaseDal.Update(entity); } + public async Task Update(TEntity entity, string where) { return await BaseDal.Update(entity, where); } + public async Task Update(object operateAnonymousObjects) { return await BaseDal.Update(operateAnonymousObjects); } public async Task Update( - TEntity entity, - List lstColumns = null, - List lstIgnoreColumns = null, - string where = "" - ) + TEntity entity, + List lstColumns = null, + List lstIgnoreColumns = null, + string where = "" + ) { return await BaseDal.Update(entity, lstColumns, lstIgnoreColumns, where); } @@ -136,7 +142,6 @@ public async Task DeleteByIds(object[] ids) } - /// /// 功能描述:查询所有数据 /// 作  者:AZLinli.Blog.Core @@ -190,7 +195,7 @@ public async Task> Query(Expression查询实体条件 /// 排序条件 /// - public async Task> Query(Expression> expression, Expression> whereExpression,string orderByFileds) + public async Task> Query(Expression> expression, Expression> whereExpression, string orderByFileds) { return await BaseDal.Query(expression, whereExpression, orderByFileds); } @@ -233,7 +238,6 @@ public async Task> Query(string where, string orderByFileds) public async Task> QuerySql(string sql, SugarParameter[] parameters = null) { return await BaseDal.QuerySql(sql, parameters); - } /// @@ -245,8 +249,8 @@ public async Task> QuerySql(string sql, SugarParameter[] parameter public async Task QueryTable(string sql, SugarParameter[] parameters = null) { return await BaseDal.QueryTable(sql, parameters); - } + /// /// 功能描述:查询前N条数据 /// 作  者:AZLinli.Blog.Core @@ -292,10 +296,10 @@ public async Task> Query( string orderByFileds) { return await BaseDal.Query( - whereExpression, - pageIndex, - pageSize, - orderByFileds); + whereExpression, + pageIndex, + pageSize, + orderByFileds); } /// @@ -308,34 +312,70 @@ public async Task> Query( /// 排序字段,如name asc,age desc /// 数据列表 public async Task> Query( - string where, - int pageIndex, - int pageSize, - string orderByFileds) + string where, + int pageIndex, + int pageSize, + string orderByFileds) { return await BaseDal.Query( - where, - pageIndex, - pageSize, - orderByFileds); + where, + pageIndex, + pageSize, + orderByFileds); } public async Task> QueryPage(Expression> whereExpression, - int pageIndex = 1, int pageSize = 20, string orderByFileds = null) + int pageIndex = 1, int pageSize = 20, string orderByFileds = null) { return await BaseDal.QueryPage(whereExpression, - pageIndex, pageSize, orderByFileds); + pageIndex, pageSize, orderByFileds); } public async Task> QueryMuch(Expression> joinExpression, Expression> selectExpression, Expression> whereLambda = null) where T : class, new() { return await BaseDal.QueryMuch(joinExpression, selectExpression, whereLambda); } + public async Task> QueryPage(PaginationModel pagination) { var express = DynamicLinqFactory.CreateLambda(pagination.Conditions); return await QueryPage(express, pagination.PageIndex, pagination.PageSize, pagination.OrderByFileds); } - } -} + #region 分表 + + public async Task> AddSplit(TEntity entity) + { + return await BaseDal.AddSplit(entity); + } + + public async Task UpdateSplit(TEntity entity, DateTime dateTime) + { + return await BaseDal.UpdateSplit(entity, dateTime); + } + + /// + /// 根据实体删除一条数据 + /// + /// 博文实体类 + /// + public async Task DeleteSplit(TEntity entity, DateTime dateTime) + { + return await BaseDal.DeleteSplit(entity, dateTime); + } + + public async Task QueryByIdSplit(object objId) + { + return await BaseDal.QueryByIdSplit(objId); + } + + public async Task> QueryPageSplit(Expression> whereExpression, DateTime beginTime, DateTime endTime, + int pageIndex = 1, int pageSize = 20, string orderByFields = null) + { + return await BaseDal.QueryPageSplit(whereExpression, beginTime, endTime, + pageIndex, pageSize, orderByFields); + } + + #endregion + } +} \ No newline at end of file diff --git a/Blog.Core.Services/BlogArticleServices.cs b/Blog.Core.Services/BlogArticleServices.cs index eeff04e3..67ea2b9e 100644 --- a/Blog.Core.Services/BlogArticleServices.cs +++ b/Blog.Core.Services/BlogArticleServices.cs @@ -23,7 +23,7 @@ public BlogArticleServices(IMapper mapper) /// /// /// - public async Task GetBlogDetails(int id) + public async Task GetBlogDetails(long id) { // 此处想获取上一条下一条数据,因此将全部数据list出来,有好的想法请提出 //var bloglist = await base.Query(a => a.IsDeleted==false, a => a.bID); diff --git a/Blog.Core.Services/RoleModulePermissionServices.cs b/Blog.Core.Services/RoleModulePermissionServices.cs index c0248e17..d3834f89 100644 --- a/Blog.Core.Services/RoleModulePermissionServices.cs +++ b/Blog.Core.Services/RoleModulePermissionServices.cs @@ -83,7 +83,7 @@ public async Task> GetRMPMaps() /// 菜单主键 /// 接口主键 /// - public async Task UpdateModuleId(int permissionId, int moduleId) + public async Task UpdateModuleId(long permissionId, long moduleId) { await _dal.UpdateModuleId(permissionId, moduleId); } diff --git a/Blog.Core.Services/SplitDemoServices.cs b/Blog.Core.Services/SplitDemoServices.cs new file mode 100644 index 00000000..cf8e2cc1 --- /dev/null +++ b/Blog.Core.Services/SplitDemoServices.cs @@ -0,0 +1,23 @@ +using Blog.Core.IRepository.Base; +using Blog.Core.IServices; +using Blog.Core.Model.Models; +using Blog.Core.Services.BASE; +using System.Linq; +using System.Threading.Tasks; + +namespace Blog.Core.FrameWork.Services +{ + /// + /// sysUserInfoServices + /// + public class SplitDemoServices : BaseServices, ISplitDemoServices + { + private readonly IBaseRepository _splitDemoRepository; + public SplitDemoServices(IBaseRepository splitDemoRepository) + { + _splitDemoRepository = splitDemoRepository; + } + + + } +} diff --git a/Blog.Core.Services/TasksLogServices.cs b/Blog.Core.Services/TasksLogServices.cs new file mode 100644 index 00000000..07d95f1f --- /dev/null +++ b/Blog.Core.Services/TasksLogServices.cs @@ -0,0 +1,137 @@ +using System.Linq.Expressions; +using System; +using System.Threading.Tasks; +using Blog.Core.IServices; +using Blog.Core.Model.Models; +using Blog.Core.Services.BASE; +using Blog.Core.Common.Extensions; +using SqlSugar; +using Blog.Core.Model; +using System.Collections.Generic; +using System.Linq; + +namespace Blog.Core.Services +{ + public partial class TasksLogServices : BaseServices, ITasksLogServices + { + public async Task> GetTaskLogs(long jobId, int page, int intPageSize, DateTime? runTime, DateTime? endTime) + { + RefAsync totalCount = 0; + Expression> whereExpression = log => true; + if (jobId > 0) whereExpression = whereExpression.And(log => log.JobId == jobId); + var data = await this.Db.Queryable() + .LeftJoin((log, qz) => log.JobId == qz.Id) + .OrderByDescending((log) => log.RunTime) + .WhereIF(jobId > 0, (log) => log.JobId == jobId) + .WhereIF(runTime != null, (log) => log.RunTime >= runTime.Value) + .WhereIF(endTime != null, (log) => log.RunTime <= endTime.Value) + .Select((log, qz) => new TasksLog + { + RunPars = log.RunPars, + RunResult = log.RunResult, + RunTime = log.RunTime, + EndTime = log.EndTime, + ErrMessage = log.ErrMessage, + ErrStackTrace = log.ErrStackTrace, + TotalTime = log.TotalTime, + Name = qz.Name, + JobGroup = qz.JobGroup + + }) + .ToPageListAsync(page, intPageSize, totalCount); + return new PageModel(page, totalCount, intPageSize, data); + } + public async Task GetTaskOverview(long jobId, DateTime? runTime, DateTime? endTime, string type) + { + //按年 + if ("year".Equals(type)) + { + + var days = endTime.Value.Year - runTime.Value.Year; + var dayArray = new List(); + while (days >= 0) + { + dayArray.Add(new DateTime(runTime.Value.Year + days, 1, 1)); + days--; + } + var queryableLeft = this.Db.Reportable(dayArray).ToQueryable(); + var queryableRight = this.Db.Queryable().Where((x) => x.RunTime.Year >= runTime.Value.Year && x.RunTime.Year <= endTime.Value.Year); ; ; //声名表 + + var list = this.Db.Queryable(queryableLeft, queryableRight, JoinType.Left, + (x1, x2) => x1.ColumnName.Year == x2.RunTime.Year) + .GroupBy((x1, x2) => x1.ColumnName) + .Select((x1, x2) => new + { + 执行次数 = SqlFunc.AggregateSum(SqlFunc.IIF(x2.Id > 0, 1, 0)), + date = x1.ColumnName.Year.ToString() + "年" + }).ToList().OrderBy(t => t.date); + return list; + } + else if ("month".Equals(type)) + { + //按月 + var queryableLeft = this.Db.Reportable(ReportableDateType.MonthsInLast1years).ToQueryable(); //生成月份 //ReportableDateType.MonthsInLast1yea 表式近一年月份 并且queryable之后还能在where过滤 + var queryableRight = this.Db.Queryable().Where((x) => x.RunTime.Year == runTime.Value.Year); //声名表 + + //月份和表JOIN + var list = queryableLeft + .LeftJoin(queryableRight, (x1, x2) => x2.RunTime.ToString("MM月") == x1.ColumnName.ToString("MM月")) + + .GroupBy((x1, x2) => x1.ColumnName) + .Select((x1, x2) => new + { + //null的数据要为0所以不能用count + 执行次数 = SqlFunc.AggregateSum(SqlFunc.IIF(x2.Id > 0, 1, 0)), + date = x1.ColumnName.ToString("MM月") + } + ).ToList().OrderBy(t => t.date); + await Task.CompletedTask; + return list; + } + else if ("day".Equals(type)) + { + //按日 + var time = runTime.Value; + var days = DateTime.DaysInMonth(time.Year, time.Month); + var dayArray = Enumerable.Range(1, days).Select(it => Convert.ToDateTime(time.ToString("yyyy-MM-" + it))).ToList();//转成时间数组 + var queryableLeft = this.Db.Reportable(dayArray).ToQueryable(); + var star = Convert.ToDateTime(runTime.Value.ToString("yyyy-MM-01 00:00:00")); + var end = Convert.ToDateTime(runTime.Value.ToString($"yyyy-MM-{days} 23:59:59")); + var queryableRight = this.Db.Queryable().Where((x) => x.RunTime >= star && x.RunTime <= end); ; ; //声名表 + + var list = this.Db.Queryable(queryableLeft, queryableRight, JoinType.Left, + (x1, x2) => x1.ColumnName.Date == x2.RunTime.Date) + .GroupBy((x1, x2) => x1.ColumnName) + .Select((x1, x2) => new + { + 执行次数 = SqlFunc.AggregateSum(SqlFunc.IIF(x2.Id > 0, 1, 0)), + date = x1.ColumnName.Day + }).ToList().OrderBy(t => t.date); + await Task.CompletedTask; + return list; + } + else if ("hour".Equals(type)) + { + //按小时 + var time = runTime.Value; + var days = 24; + var dayArray = Enumerable.Range(0, days).Select(it => Convert.ToDateTime(time.ToString($"yyyy-MM-dd {it.ToString().PadLeft(2, '0')}:00:00"))).ToList();//转成时间数组 + var queryableLeft = this.Db.Reportable(dayArray).ToQueryable(); + var queryableRight = this.Db.Queryable().Where((x) => x.RunTime >= runTime.Value.Date && x.RunTime <= runTime.Value.Date.AddDays(1).AddMilliseconds(-1)); //声名表 + + var list = this.Db.Queryable(queryableLeft, queryableRight, JoinType.Left, + (x1, x2) => x1.ColumnName.Hour == x2.RunTime.Hour) + .GroupBy((x1, x2) => x1.ColumnName) + .Select((x1, x2) => new + { + 执行次数 = SqlFunc.AggregateSum(SqlFunc.IIF(x2.Id > 0, 1, 0)), + date = x1.ColumnName.Hour + }).ToList().OrderBy(t => t.date); + await Task.CompletedTask; + return list; + } + await Task.CompletedTask; + return null; + } + } +} diff --git a/Blog.Core.Services/TenantService.cs b/Blog.Core.Services/TenantService.cs new file mode 100644 index 00000000..a552442b --- /dev/null +++ b/Blog.Core.Services/TenantService.cs @@ -0,0 +1,57 @@ +using Blog.Core.Common.DB; +using Blog.Core.Common.Seed; +using Blog.Core.IServices; +using Blog.Core.Model.Models; +using Blog.Core.Repository.UnitOfWorks; +using Blog.Core.Services.BASE; +using System.Threading.Tasks; + +namespace Blog.Core.Services; + +public class TenantService : BaseServices, ITenantService +{ + private readonly IUnitOfWorkManage _uowManager; + + public TenantService(IUnitOfWorkManage uowManage) + { + this._uowManager = uowManage; + } + + + public async Task SaveTenant(SysTenant tenant) + { + bool initDb = tenant.Id == 0; + using (var uow = _uowManager.CreateUnitOfWork()) + { + + tenant.DefaultTenantConfig(); + + if (tenant.Id == 0) + { + await Db.Insertable(tenant).ExecuteReturnSnowflakeIdAsync(); + } + else + { + var oldTenant = await QueryById(tenant.Id); + if (oldTenant.Connection != tenant.Connection) + { + initDb = true; + } + + await Db.Updateable(tenant).ExecuteCommandAsync(); + } + + uow.Commit(); + } + + if (initDb) + { + await InitTenantDb(tenant); + } + } + + public async Task InitTenantDb(SysTenant tenant) + { + await DBSeed.InitTenantSeedAsync(Db.AsTenant(), tenant.GetConnectionConfig()); + } +} \ No newline at end of file diff --git a/Blog.Core.Services/UserRoleServices.cs b/Blog.Core.Services/UserRoleServices.cs index 38f524da..26194837 100644 --- a/Blog.Core.Services/UserRoleServices.cs +++ b/Blog.Core.Services/UserRoleServices.cs @@ -19,7 +19,7 @@ public class UserRoleServices : BaseServices, IUserRoleServices /// /// /// - public async Task SaveUserRole(int uid, int rid) + public async Task SaveUserRole(long uid, long rid) { UserRole userRole = new UserRole(uid, rid); @@ -42,7 +42,7 @@ public async Task SaveUserRole(int uid, int rid) [Caching(AbsoluteExpiration = 30)] - public async Task GetRoleIdByUid(int uid) + public async Task GetRoleIdByUid(long uid) { return ((await base.Query(d => d.UserId == uid)).OrderByDescending(d => d.Id).LastOrDefault()?.RoleId).ObjToInt(); } diff --git a/Blog.Core.Tasks/QuartzNet/Jobs/JobBase.cs b/Blog.Core.Tasks/QuartzNet/Jobs/JobBase.cs index 4a71fbf5..b4486dfc 100644 --- a/Blog.Core.Tasks/QuartzNet/Jobs/JobBase.cs +++ b/Blog.Core.Tasks/QuartzNet/Jobs/JobBase.cs @@ -1,5 +1,6 @@ using Blog.Core.Common.Helper; using Blog.Core.IServices; +using Blog.Core.Model.Models; using Quartz; using System; using System.Diagnostics; @@ -10,6 +11,12 @@ namespace Blog.Core.Tasks public class JobBase { public ITasksQzServices _tasksQzServices; + public ITasksLogServices _tasksLogServices; + public JobBase(ITasksQzServices tasksQzServices, ITasksLogServices tasksLogServices) + { + _tasksQzServices = tasksQzServices; + _tasksLogServices = tasksLogServices; + } /// /// 执行指定任务 /// @@ -17,40 +24,50 @@ public class JobBase /// public async Task ExecuteJob(IJobExecutionContext context, Func func) { - //记录Job时间 - Stopwatch stopwatch = new Stopwatch(); + //记录Job + TasksLog tasksLog = new TasksLog(); //JOBID int jobid = context.JobDetail.Key.Name.ObjToInt(); //JOB组名 string groupName = context.JobDetail.Key.Group; //日志 - string jobHistory = $"【{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}】【执行开始】【Id:{jobid},组别:{groupName}】"; - //耗时 - double taskSeconds = 0; + tasksLog.JobId = jobid; + tasksLog.RunTime = DateTime.Now; + string jobHistory = $"【{tasksLog.RunTime.ToString("yyyy-MM-dd HH:mm:ss")}】【执行开始】【Id:{jobid},组别:{groupName}】"; try { - stopwatch.Start(); await func();//执行任务 - stopwatch.Stop(); - jobHistory += $",【{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}】【执行成功】"; + tasksLog.EndTime = DateTime.Now; + tasksLog.RunResult = true; + jobHistory += $",【{tasksLog.EndTime.ToString("yyyy-MM-dd HH:mm:ss")}】【执行成功】"; + + JobDataMap jobPars = context.JobDetail.JobDataMap; + tasksLog.RunPars = jobPars.GetString("JobParam"); } catch (Exception ex) { - JobExecutionException e2 = new JobExecutionException(ex); + tasksLog.EndTime = DateTime.Now; + tasksLog.RunResult = false; + //JobExecutionException e2 = new JobExecutionException(ex); //true 是立即重新执行任务 - e2.RefireImmediately = true; - jobHistory += $",【{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}】【执行失败:{ex.Message}】"; + //e2.RefireImmediately = true; + tasksLog.ErrMessage = ex.Message; + tasksLog.ErrStackTrace = ex.StackTrace; + jobHistory += $",【{tasksLog.EndTime.ToString("yyyy-MM-dd HH:mm:ss")}】【执行失败:{ex.Message}】"; } finally { - taskSeconds = Math.Round(stopwatch.Elapsed.TotalSeconds, 3); - jobHistory += $",【{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}】【执行结束】(耗时:{taskSeconds}秒)"; + tasksLog.TotalTime = Math.Round((tasksLog.EndTime - tasksLog.RunTime).TotalSeconds,3); + jobHistory += $"(耗时:{tasksLog.TotalTime}秒)"; if (_tasksQzServices != null) { var model = await _tasksQzServices.QueryById(jobid); if (model != null) { + if(_tasksLogServices != null) await _tasksLogServices.Add(tasksLog); model.RunTimes += 1; + if (model.TriggerType == 0) model.CycleHasRunTimes += 1; + if (model.TriggerType == 0 && model.CycleRunTimes != 0 && model.CycleHasRunTimes >= model.CycleRunTimes) model.IsStart = false;//循环完善,当循环任务完成后,停止该任务,防止下次启动再次执行 var separator = "
"; // 这里注意数据库字段的长度问题,超过限制,会造成数据库remark不更新问题。 model.Remark = diff --git a/Blog.Core.Tasks/QuartzNet/Jobs/Job_AccessTrendLog_Quartz.cs b/Blog.Core.Tasks/QuartzNet/Jobs/Job_AccessTrendLog_Quartz.cs index 743180c3..1dcc57ed 100644 --- a/Blog.Core.Tasks/QuartzNet/Jobs/Job_AccessTrendLog_Quartz.cs +++ b/Blog.Core.Tasks/QuartzNet/Jobs/Job_AccessTrendLog_Quartz.cs @@ -21,7 +21,8 @@ public class Job_AccessTrendLog_Quartz : JobBase, IJob private readonly IAccessTrendLogServices _accessTrendLogServices; private readonly IWebHostEnvironment _environment; - public Job_AccessTrendLog_Quartz(IAccessTrendLogServices accessTrendLogServices, ITasksQzServices tasksQzServices, IWebHostEnvironment environment) + public Job_AccessTrendLog_Quartz(IAccessTrendLogServices accessTrendLogServices, IWebHostEnvironment environment, ITasksQzServices tasksQzServices, ITasksLogServices tasksLogServices) + : base(tasksQzServices, tasksLogServices) { _accessTrendLogServices = accessTrendLogServices; _environment = environment; diff --git a/Blog.Core.Tasks/QuartzNet/Jobs/Job_Blogs_Quartz.cs b/Blog.Core.Tasks/QuartzNet/Jobs/Job_Blogs_Quartz.cs index 173e996e..f116fe05 100644 --- a/Blog.Core.Tasks/QuartzNet/Jobs/Job_Blogs_Quartz.cs +++ b/Blog.Core.Tasks/QuartzNet/Jobs/Job_Blogs_Quartz.cs @@ -1,5 +1,6 @@ using Blog.Core.IServices; using Quartz; +using System; using System.Threading.Tasks; /// @@ -11,10 +12,10 @@ public class Job_Blogs_Quartz : JobBase, IJob { private readonly IBlogArticleServices _blogArticleServices; - public Job_Blogs_Quartz(IBlogArticleServices blogArticleServices, ITasksQzServices tasksQzServices) + public Job_Blogs_Quartz(IBlogArticleServices blogArticleServices, ITasksQzServices tasksQzServices, ITasksLogServices tasksLogServices) + : base(tasksQzServices, tasksLogServices) { _blogArticleServices = blogArticleServices; - _tasksQzServices = tasksQzServices; } public async Task Execute(IJobExecutionContext context) { @@ -22,6 +23,7 @@ public async Task Execute(IJobExecutionContext context) } public async Task Run(IJobExecutionContext context) { + System.Console.WriteLine($"Job_Blogs_Quartz 执行 {DateTime.Now.ToShortTimeString()}"); var list = await _blogArticleServices.Query(); // 也可以通过数据库配置,获取传递过来的参数 JobDataMap data = context.JobDetail.JobDataMap; diff --git a/Blog.Core.Tasks/QuartzNet/Jobs/Job_OperateLog_Quartz.cs b/Blog.Core.Tasks/QuartzNet/Jobs/Job_OperateLog_Quartz.cs index 8e9b3847..18c4c298 100644 --- a/Blog.Core.Tasks/QuartzNet/Jobs/Job_OperateLog_Quartz.cs +++ b/Blog.Core.Tasks/QuartzNet/Jobs/Job_OperateLog_Quartz.cs @@ -20,11 +20,11 @@ public class Job_OperateLog_Quartz : JobBase, IJob private readonly IOperateLogServices _operateLogServices; private readonly IWebHostEnvironment _environment; - public Job_OperateLog_Quartz(IOperateLogServices operateLogServices, ITasksQzServices tasksQzServices, IWebHostEnvironment environment) + public Job_OperateLog_Quartz(IOperateLogServices operateLogServices,IWebHostEnvironment environment, ITasksQzServices tasksQzServices,ITasksLogServices tasksLogServices) + :base(tasksQzServices, tasksLogServices) { _operateLogServices = operateLogServices; - _environment = environment; - _tasksQzServices = tasksQzServices; + _environment = environment; } public async Task Execute(IJobExecutionContext context) { diff --git a/Blog.Core.Tasks/QuartzNet/Jobs/Job_Trojan_Quartz.cs b/Blog.Core.Tasks/QuartzNet/Jobs/Job_Trojan_Quartz.cs index f9640749..9d2d4cea 100644 --- a/Blog.Core.Tasks/QuartzNet/Jobs/Job_Trojan_Quartz.cs +++ b/Blog.Core.Tasks/QuartzNet/Jobs/Job_Trojan_Quartz.cs @@ -1,7 +1,8 @@ -using Blog.Core.Repository.UnitOfWorks; + using Blog.Core.IServices; using Blog.Core.IServices.BASE; using Blog.Core.Model.Models; +using Blog.Core.Repository.UnitOfWorks; using Microsoft.Extensions.Logging; using Quartz; using System; @@ -20,7 +21,8 @@ public class Job_Trojan_Quartz : JobBase, IJob private readonly ITrojanUsersServices _TrojanUsers; private readonly ILogger _logger; - public Job_Trojan_Quartz(IUnitOfWorkManage unitOfWorkManage, IBaseServices iusers_DetailServices, ITrojanUsersServices trojanUsers, ITasksQzServices tasksQzServices, ILogger logger) + public Job_Trojan_Quartz(IUnitOfWorkManage unitOfWorkManage, IBaseServices iusers_DetailServices, ITrojanUsersServices trojanUsers, ILogger logger, ITasksQzServices tasksQzServices, ITasksLogServices tasksLogServices) + : base(tasksQzServices, tasksLogServices) { _tasksQzServices = tasksQzServices; _unitOfWorkManage = unitOfWorkManage; diff --git a/Blog.Core.Tasks/QuartzNet/Jobs/Job_URL_Quartz.cs b/Blog.Core.Tasks/QuartzNet/Jobs/Job_URL_Quartz.cs index 666a8d41..1ec225c3 100644 --- a/Blog.Core.Tasks/QuartzNet/Jobs/Job_URL_Quartz.cs +++ b/Blog.Core.Tasks/QuartzNet/Jobs/Job_URL_Quartz.cs @@ -1,12 +1,7 @@ using Blog.Core.Common.Helper; -using Blog.Core.Repository.UnitOfWorks; using Blog.Core.IServices; -using Blog.Core.IServices.BASE; -using Blog.Core.Model.Models; using Microsoft.Extensions.Logging; using Quartz; -using System; -using System.Collections.Generic; using System.Threading.Tasks; /// @@ -18,7 +13,8 @@ public class Job_URL_Quartz : JobBase, IJob { private readonly ILogger _logger; - public Job_URL_Quartz(ITasksQzServices tasksQzServices, ILogger logger) + public Job_URL_Quartz(ILogger logger, ITasksQzServices tasksQzServices, ITasksLogServices tasksLogServices) + : base(tasksQzServices, tasksLogServices) { _tasksQzServices = tasksQzServices; _logger = logger; diff --git a/Blog.Core.Tasks/QuartzNet/SchedulerCenterServer.cs b/Blog.Core.Tasks/QuartzNet/SchedulerCenterServer.cs index 8c21115c..474a2a9a 100644 --- a/Blog.Core.Tasks/QuartzNet/SchedulerCenterServer.cs +++ b/Blog.Core.Tasks/QuartzNet/SchedulerCenterServer.cs @@ -125,6 +125,12 @@ public async Task> AddScheduleJobAsync(TasksQz tasksQz) result.msg = $"该任务计划已经在执行:【{tasksQz.Name}】,请勿重复启动!"; return result; } + if(tasksQz.TriggerType == 0 && tasksQz.CycleRunTimes != 0 && tasksQz.CycleHasRunTimes>=tasksQz.CycleRunTimes) + { + result.success = false; + result.msg = $"该任务计划已完成:【{tasksQz.Name}】,无需重复启动,如需启动请修改已循环次数再提交"; + return result; + } #region 设置开始时间和结束时间 if (tasksQz.BeginTime == null) diff --git a/Blog.Core.Tests/Common_Test/DynamicLambdaTest.cs b/Blog.Core.Tests/Common_Test/DynamicLambdaTest.cs index 99757e4f..8f6ad096 100644 --- a/Blog.Core.Tests/Common_Test/DynamicLambdaTest.cs +++ b/Blog.Core.Tests/Common_Test/DynamicLambdaTest.cs @@ -3,6 +3,7 @@ using Blog.Core.Common.Helper; using Blog.Core.IRepository.Base; using Blog.Core.Model.Models; +using SqlSugar; using Xunit; using Xunit.Abstractions; @@ -21,13 +22,42 @@ public DynamicLambdaTest(ITestOutputHelper testOutputHelper) var container = dI_Test.DICollections(); _baseRepository = container.Resolve>(); - + _baseRepository.Db.Aop.OnLogExecuting = (sql, p) => + { + _testOutputHelper.WriteLine(""); + _testOutputHelper.WriteLine("==================FullSql=====================", "", new string[] { sql.GetType().ToString(), GetParas(p), "【SQL语句】:" + sql }); + _testOutputHelper.WriteLine("【SQL语句】:" + sql); + _testOutputHelper.WriteLine(GetParas(p)); + _testOutputHelper.WriteLine("=============================================="); + _testOutputHelper.WriteLine(""); + }; //DbContext.Init(BaseDBConfig.ConnectionString,(DbType)BaseDBConfig.DbType); + + Init(); + } + + private static string GetParas(SugarParameter[] pars) + { + string key = "【SQL参数】:"; + foreach (var param in pars) + { + key += $"{param.ParameterName}:{param.Value}\n"; + } + + return key; + } + + private void Init() + { + _baseRepository.Db.CodeFirst.InitTables(); + _baseRepository.Db.CodeFirst.InitTables(); } [Fact] public async void Get_Blogs_DynamicTest() { + //方便前端自定义条件查询 + //语法更舒服 var data = await _baseRepository.Query(); _testOutputHelper.WriteLine(data.ToJson()); @@ -39,12 +69,26 @@ public async void Get_Blogs_DynamicTest() await TestConditions("btitle like \"测试数据\" && bId>0"); await TestConditions("btitle like \"测试!@#$%^&*()_+|}{\":<>?LP\"数据\" && bId>0"); await TestConditions("btitle like \"测试!@+)(*()_&%^&^$^%$IUYWIQOJVLXKZM>?Z<>??LP\"数据\" && bId>0"); - - //比如文章下 过滤创建人 - //await TestConditions("btitle.user.name like \"老张\""); - await TestConditions("IsDeleted == false"); await TestConditions("IsDeleted == true"); + + //导航属性 + + //一对一 + + //查询 老张的文章 + await TestConditions("User.RealName like \"老张\""); + //查询 2019年后的老张文章 + await TestConditions("User.RealName like \"老张\" && bUpdateTime>=\"2019-01-01 01:01:01\""); + + //一对多 + + //查询 评论中有"写的不错"的文章 + await TestConditions("Comments.Comment like \"写的不错\""); + //查询 2019后的 评论中有"写的不错"的文章 + await TestConditions("Comments.Comment like \"写的不错\" && bUpdateTime>=\"2019-01-01 01:01:01\""); + //查询 有老张评论的文章 + await TestConditions("Comments.User.LoginName like \"老张\""); } private async Task TestConditions(string conditions) diff --git a/Blog.Core.Tests/Controller_Test/BlogController_Should.cs b/Blog.Core.Tests/Controller_Test/BlogController_Should.cs index 3d1e3178..59d42ae0 100644 --- a/Blog.Core.Tests/Controller_Test/BlogController_Should.cs +++ b/Blog.Core.Tests/Controller_Test/BlogController_Should.cs @@ -53,7 +53,7 @@ public async void Get_Blog_Page_Test() [Fact] public async void Get_Blog_Test() { - MessageModel blogVo = await blogController.Get(1); + MessageModel blogVo = await blogController.Get(1.ObjToLong()); Assert.NotNull(blogVo); } diff --git a/Blog.Core.Tests/DependencyInjection/DI_Test.cs b/Blog.Core.Tests/DependencyInjection/DI_Test.cs index 6fad1f45..ff2c74cd 100644 --- a/Blog.Core.Tests/DependencyInjection/DI_Test.cs +++ b/Blog.Core.Tests/DependencyInjection/DI_Test.cs @@ -74,13 +74,13 @@ public IContainer DICollections() var permission = new List(); var permissionRequirement = new PermissionRequirement( - "/api/denied", - permission, - ClaimTypes.Role, - AppSettings.app(new string[] { "Audience", "Issuer" }), - AppSettings.app(new string[] { "Audience", "Audience" }), - signingCredentials,//签名凭据 - expiration: TimeSpan.FromSeconds(60 * 60)//接口的过期时间 + "/api/denied", + permission, + ClaimTypes.Role, + AppSettings.app(new string[] { "Audience", "Issuer" }), + AppSettings.app(new string[] { "Audience", "Audience" }), + signingCredentials, //签名凭据 + expiration: TimeSpan.FromSeconds(60 * 60) //接口的过期时间 ); services.AddSingleton(permissionRequirement); @@ -88,17 +88,16 @@ public IContainer DICollections() services.AddAuthorization(options => { options.AddPolicy(Permissions.Name, - policy => policy.Requirements.Add(permissionRequirement)); + policy => policy.Requirements.Add(permissionRequirement)); }); services.AddScoped(o => { return new SqlSugar.SqlSugarScope(new SqlSugar.ConnectionConfig() { - ConnectionString = GetMainConnectionDb().Connection,//必填, 数据库连接字符串 - DbType = (SqlSugar.DbType)GetMainConnectionDb().DbType,//必填, 数据库类型 - IsAutoCloseConnection = true,//默认false, 时候知道关闭数据库连接, 设置为true无需使用using或者Close操作 - InitKeyType = SqlSugar.InitKeyType.SystemTable//默认SystemTable, 字段信息读取, 如:该属性是不是主键,标识列等等信息 + ConnectionString = GetMainConnectionDb().Connection, //必填, 数据库连接字符串 + DbType = (SqlSugar.DbType)GetMainConnectionDb().DbType, //必填, 数据库类型 + IsAutoCloseConnection = true, //默认false, 时候知道关闭数据库连接, 设置为true无需使用using或者Close操作 }); }); @@ -121,19 +120,19 @@ public IContainer DICollections() builder.RegisterAssemblyTypes(typeof(Startup).Assembly) .Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType) .PropertiesAutowired(); - + var servicesDllFile = Path.Combine(basePath, "Blog.Core.Services.dll"); var assemblysServices = Assembly.LoadFrom(servicesDllFile); builder.RegisterAssemblyTypes(assemblysServices) - .AsImplementedInterfaces() - .InstancePerLifetimeScope() - .PropertiesAutowired() - .EnableInterfaceInterceptors(); + .AsImplementedInterfaces() + .InstancePerLifetimeScope() + .PropertiesAutowired() + .EnableInterfaceInterceptors(); var repositoryDllFile = Path.Combine(basePath, "Blog.Core.Repository.dll"); var assemblysRepository = Assembly.LoadFrom(repositoryDllFile); builder.RegisterAssemblyTypes(assemblysRepository) - .PropertiesAutowired().AsImplementedInterfaces(); + .PropertiesAutowired().AsImplementedInterfaces(); services.Replace(ServiceDescriptor.Transient()); diff --git a/Blog.Core.Tests/Repository_Test/OrmTest.cs b/Blog.Core.Tests/Repository_Test/OrmTest.cs new file mode 100644 index 00000000..fa4629f7 --- /dev/null +++ b/Blog.Core.Tests/Repository_Test/OrmTest.cs @@ -0,0 +1,73 @@ +using System; +using Autofac; +using Blog.Core.Common.Extensions; +using Blog.Core.IRepository.Base; +using Blog.Core.Model.Models; +using OfficeOpenXml.FormulaParsing.Excel.Functions.Math; +using SqlSugar; +using Xunit; +using Xunit.Abstractions; + +namespace Blog.Core.Tests; + +public class OrmTest +{ + private readonly ITestOutputHelper _testOutputHelper; + private readonly IBaseRepository _baseRepository; + DI_Test dI_Test = new DI_Test(); + + public OrmTest(ITestOutputHelper testOutputHelper) + { + _testOutputHelper = testOutputHelper; + + var container = dI_Test.DICollections(); + + _baseRepository = container.Resolve>(); + _baseRepository.Db.Aop.OnLogExecuting = (sql, p) => + { + _testOutputHelper.WriteLine(""); + _testOutputHelper.WriteLine("==================FullSql=====================", "", new string[] { sql.GetType().ToString(), GetParas(p), "【SQL语句】:" + sql }); + _testOutputHelper.WriteLine("【SQL语句】:" + sql); + _testOutputHelper.WriteLine(GetParas(p)); + _testOutputHelper.WriteLine("=============================================="); + _testOutputHelper.WriteLine(""); + }; + } + + private static string GetParas(SugarParameter[] pars) + { + string key = "【SQL参数】:"; + foreach (var param in pars) + { + key += $"{param.ParameterName}:{param.Value}\n"; + } + + return key; + } + + [Fact] + public void MultiTables() + { + var sql = _baseRepository.Db.Queryable() + .AS($@"{nameof(BlogArticle)}_TenantA") + .ToSqlString(); + //_testOutputHelper.WriteLine(sql); + + _baseRepository.Db.MappingTables.Add(nameof(BlogArticle), $@"{nameof(BlogArticle)}_TenantA"); + + var query = _baseRepository.Db.Queryable() + .LeftJoin((a, c) => a.bID == c.bID); + // query.QueryBuilder.AsTables.AddOrModify(nameof(BlogArticle), $@"{nameof(BlogArticle)}_TenantA"); + //query.QueryBuilder.AsTables.AddOrModify(nameof(BlogArticleComment), $@"{nameof(BlogArticleComment)}_TenantA"); + // query.QueryBuilder.AsTables.AddOrModify(nameof(BlogArticleComment), $@"{nameof(BlogArticleComment)}_TenantA"); + // query.QueryBuilder.AsTables.AddOrModify(nameof(SysUserInfo), $@"{nameof(SysUserInfo)}_TenantA"); + + + sql = query.ToSqlString(); + + _testOutputHelper.WriteLine(sql); + + sql = _baseRepository.Db.Deleteable().ToSqlString(); + _testOutputHelper.WriteLine(sql); + } +} \ No newline at end of file diff --git a/README.md b/README.md index 7f37ce66..ee36dc2c 100644 --- a/README.md +++ b/README.md @@ -73,20 +73,24 @@ Blog.Core 开箱即用的企业级前后端分离【 .NET Core6.0 Api + Vue 2.x #### 框架模块: - [x] 采用`仓储+服务+接口`的形式封装框架; -- [x] 异步 async/await 开发; -- [x] 接入国产数据库ORM组件 —— SqlSugar,封装数据库操作; +- [x] 自定义项目模板 `CreateYourProject.bat` ,可以一键生成自己的项目;🎶 +- [x] 异步 async/await 开发; +- [x] 接入国产数据库ORM组件 —— SqlSugar,封装数据库操作,支持级联操作; - [x] 支持自由切换多种数据库,MySql/SqlServer/Sqlite/Oracle/Postgresql/达梦/人大金仓; - [x] 实现项目启动,自动生成种子数据 ✨; - [x] 实现数据库主键类型配置化,什么类型都可以自定义 ✨; - [x] 五种日志记录,审计/异常/请求响应/服务操作/Sql记录等,并自动持久化到数据库表🎶; - [x] 支持项目事务处理(若要分布式,用cap即可)✨; - [x] 设计4种 AOP 切面编程,功能涵盖:日志、缓存、审计、事务 ✨; +- [x] Log4net 多种日志自动生成到数据库中,目前支持MySql/SqlServer/Sqlite/Oracle/Postgresql🎉; - [x] 设计并支持按钮级别的RBAC权限控制,同时支持一键同步接口和菜单 🎶; - [x] 支持 T4 代码模板,自动生成每层代码; - [x] 或使用 DbFirst 一键创建自己项目的四层文件(支持多库); - [x] 封装`Blog.Core.Webapi.Template`项目模板,一键重建自己的项目 ✨; - [x] 搭配多个前端案例供参考和借鉴:Blog.Vue、Blog.Admin、Nuxt.tbug、Blog.Mvp.Blazor ✨; - [x] 统一集成 IdentityServer4 认证 ✨; +- [x] 统一实现多租户; + 组件模块: - [x] 提供 Redis 做缓存处理; @@ -111,6 +115,9 @@ Blog.Core 开箱即用的企业级前后端分离【 .NET Core6.0 Api + Vue 2.x - [x] 新增 Kafka 消息队列,并配合实现EventBus ✨; - [x] 新增 微信公众号管理,并集成到Blog.Admin后台 ✨; - [x] 新增 - 数据部门权限; +- [x] 新增 - Log4net集成日志数据持久化到数据库; +- [x] 新增 - 多租户模式(单表,多表,多库三种模式); + 微服务模块: - [x] 可配合 Docker 实现容器化; @@ -229,9 +236,9 @@ Contributions of any kind are welcome! ## 售后服务与支持 -鼓励作者,简单打赏,入微信群,随时随地解答我框架中(NetCore、Vue、DDD、IdentityServer4等)的疑难杂症。 -注意主要是帮忙解决bug和思路,不会远程授课,但是可以适当发我代码,我帮忙调试, -打赏的时候,备注自己的微信号,我拉你进群,两天内没回应,QQ私聊我(3143422472); +鼓励作者,简单打赏~~ +打赏的时候,备注自己的微信号,加个微信,交个朋友,两天内没回应,QQ私聊我(3143422472); +目前精力有限,主要针对企业级用户答疑,或者购买授权版的个人用户。 [赞赏列表](http://apk.neters.club/.doc/Contribution/) From 7b8d60d9d07ce02cc80102fa1fb3795278a2a0e8 Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Fri, 14 Apr 2023 11:20:10 +0800 Subject: [PATCH 22/84] =?UTF-8?q?=E2=9C=A8=20=E4=BC=98=E5=8C=96Http?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E6=A8=A1=E7=89=88=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E4=BA=BF=E4=BA=9B=E9=99=84=E5=8A=A0=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Api/Program.cs | 58 +++++++++---------- .../Extensions/HttpRequestExtension.cs | 28 +++++++++ .../Policys/PermissionHandler.cs | 4 +- .../Utility/SerilogRequestUtility.cs | 37 +++++++++++- 4 files changed, 90 insertions(+), 37 deletions(-) create mode 100644 Blog.Core.Common/Extensions/HttpRequestExtension.cs diff --git a/Blog.Core.Api/Program.cs b/Blog.Core.Api/Program.cs index 6f326a7c..b36a02ac 100644 --- a/Blog.Core.Api/Program.cs +++ b/Blog.Core.Api/Program.cs @@ -29,7 +29,6 @@ var builder = WebApplication.CreateBuilder(args); - // 1、配置host与容器 builder.Host .UseServiceProviderFactory(new AutofacServiceProviderFactory()) @@ -50,7 +49,6 @@ builder.Services.AddSingleton(new AppSettings(builder.Configuration)); - builder.Services.AddUiFilesZipSetup(builder.Environment); Permissions.IsUseIds4 = AppSettings.app(new string[] { "Startup", "IdentityServer4", "Enabled" }).ObjToBool(); @@ -102,30 +100,30 @@ builder.Services.AddSession(); builder.Services.AddHttpPollySetup(); builder.Services.AddControllers(o => -{ - o.Filters.Add(typeof(GlobalExceptionsFilter)); - //o.Conventions.Insert(0, new GlobalRouteAuthorizeConvention()); - o.Conventions.Insert(0, new GlobalRoutePrefixFilter(new RouteAttribute(RoutePrefix.Name))); -}) -.AddNewtonsoftJson(options => -{ - options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; - options.SerializerSettings.ContractResolver = new DefaultContractResolver(); - options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; - //options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; - options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local; - options.SerializerSettings.Converters.Add(new StringEnumConverter()); - //将long类型转为string - options.SerializerSettings.Converters.Add(new NumberConverter(NumberConverterShip.Int64)); -}) -//.AddFluentValidation(config => -//{ -// //程序集方式添加验证 -// config.RegisterValidatorsFromAssemblyContaining(typeof(UserRegisterVoValidator)); -// //是否与MvcValidation共存 -// config.DisableDataAnnotationsValidation = true; -//}) -; + { + o.Filters.Add(typeof(GlobalExceptionsFilter)); + //o.Conventions.Insert(0, new GlobalRouteAuthorizeConvention()); + o.Conventions.Insert(0, new GlobalRoutePrefixFilter(new RouteAttribute(RoutePrefix.Name))); + }) + .AddNewtonsoftJson(options => + { + options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + options.SerializerSettings.ContractResolver = new DefaultContractResolver(); + options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; + //options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; + options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local; + options.SerializerSettings.Converters.Add(new StringEnumConverter()); + //将long类型转为string + options.SerializerSettings.Converters.Add(new NumberConverter(NumberConverterShip.Int64)); + }) + //.AddFluentValidation(config => + //{ + // //程序集方式添加验证 + // config.RegisterValidatorsFromAssemblyContaining(typeof(UserRegisterVoValidator)); + // //是否与MvcValidation共存 + // config.DisableDataAnnotationsValidation = true; + //}) + ; builder.Services.AddEndpointsApiExplorer(); @@ -167,13 +165,9 @@ app.UseStatusCodePages(); app.UseSerilogRequestLogging(options => { + options.MessageTemplate = SerilogRequestUtility.HttpMessageTemplate; options.GetLevel = SerilogRequestUtility.GetRequestLevel; - options.EnrichDiagnosticContext = (diagnosticContext, httpContext) => - { - diagnosticContext.Set("RequestHost", httpContext.Request.Host.Value); - diagnosticContext.Set("RequestScheme", httpContext.Request.Scheme); - diagnosticContext.Set("RequestIp", httpContext.GetRequestIp()); - }; + options.EnrichDiagnosticContext = SerilogRequestUtility.EnrichFromRequest; }); app.UseRouting(); diff --git a/Blog.Core.Common/Extensions/HttpRequestExtension.cs b/Blog.Core.Common/Extensions/HttpRequestExtension.cs new file mode 100644 index 00000000..7e87fd28 --- /dev/null +++ b/Blog.Core.Common/Extensions/HttpRequestExtension.cs @@ -0,0 +1,28 @@ +using Microsoft.AspNetCore.Http; +using System.IO; +using System.Text; + +namespace Blog.Core.Common.Extensions; + +public static class HttpRequestExtension +{ + public static string GetRequestBody(this HttpRequest request) + { + if (request.Body.Length < 1) + { + return default; + } + + var bodyStr = ""; + // 启用倒带功能,就可以让 Request.Body 可以再次读取 + request.Body.Seek(0, SeekOrigin.Begin); + using (StreamReader reader + = new StreamReader(request.Body, Encoding.UTF8, true, 1024, true)) + { + bodyStr = reader.ReadToEnd(); + } + + request.Body.Position = 0; + return bodyStr; + } +} \ 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 4ed55525..121b9b10 100644 --- a/Blog.Core.Extensions/Authorizations/Policys/PermissionHandler.cs +++ b/Blog.Core.Extensions/Authorizations/Policys/PermissionHandler.cs @@ -1,6 +1,8 @@ using Blog.Core.Common; using Blog.Core.Common.Helper; +using Blog.Core.Common.HttpContextUser; using Blog.Core.IServices; +using Blog.Core.Model; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; @@ -12,8 +14,6 @@ using System.Security.Claims; using System.Text.RegularExpressions; using System.Threading.Tasks; -using Blog.Core.Common.HttpContextUser; -using Blog.Core.Model; namespace Blog.Core.AuthHelper { diff --git a/Blog.Core.Serilog/Utility/SerilogRequestUtility.cs b/Blog.Core.Serilog/Utility/SerilogRequestUtility.cs index cab7ae55..61b652dd 100644 --- a/Blog.Core.Serilog/Utility/SerilogRequestUtility.cs +++ b/Blog.Core.Serilog/Utility/SerilogRequestUtility.cs @@ -1,17 +1,22 @@ -using Microsoft.AspNetCore.Http; +using Blog.Core.Common.Extensions; +using Blog.Core.Common.Https; +using Microsoft.AspNetCore.Http; +using Serilog; using Serilog.Events; namespace Blog.Core.Serilog.Utility; public class SerilogRequestUtility { + public const string HttpMessageTemplate = + "HTTP {RequestMethod} {RequestPath} QueryString:{QueryString} Body:{Body} responded {StatusCode} in {Elapsed:0.0000} ms"; + private static readonly List _ignoreUrl = new() { "/job", }; - private static LogEventLevel DefaultGetLevel( - HttpContext ctx, + private static LogEventLevel DefaultGetLevel(HttpContext ctx, double _, Exception? ex) { @@ -31,4 +36,30 @@ private static LogEventLevel IgnoreRequest(HttpContext ctx) return _ignoreUrl.Any(s => path.StartsWith(s)) ? LogEventLevel.Verbose : LogEventLevel.Information; } + + /// + /// 从Request中增加附属属性 + /// + /// + /// + public static void EnrichFromRequest(IDiagnosticContext diagnosticContext, HttpContext httpContext) + { + var request = httpContext.Request; + + diagnosticContext.Set("RequestHost", request.Host); + diagnosticContext.Set("RequestScheme", request.Scheme); + diagnosticContext.Set("Protocol", request.Protocol); + diagnosticContext.Set("RequestIp", httpContext.GetRequestIp()); + + diagnosticContext.Set("QueryString", request.QueryString.HasValue ? request.QueryString.Value : string.Empty); + diagnosticContext.Set("Body", request.ContentLength > 0 ? request.GetRequestBody() : string.Empty); + + diagnosticContext.Set("ContentType", httpContext.Response.ContentType); + + var endpoint = httpContext.GetEndpoint(); + if (endpoint != null) + { + diagnosticContext.Set("EndpointName", endpoint.DisplayName); + } + } } \ No newline at end of file From 90157e470ded0a785bb7dee34de32bc780d26265 Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Fri, 14 Apr 2023 11:46:44 +0800 Subject: [PATCH 23/84] =?UTF-8?q?=E2=9C=A8=20=E6=B7=BB=E5=8A=A0Seq?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Api/Blog.Core.Api.csproj | 5 +++++ Blog.Core.Api/appsettings.json | 5 +++++ Blog.Core.Extensions/Blog.Core.Extensions.csproj | 1 + .../ServiceExtensions/SerilogSetup.cs | 16 +++++++++++++++- 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/Blog.Core.Api/Blog.Core.Api.csproj b/Blog.Core.Api/Blog.Core.Api.csproj index 3bf64399..5ff619d1 100644 --- a/Blog.Core.Api/Blog.Core.Api.csproj +++ b/Blog.Core.Api/Blog.Core.Api.csproj @@ -52,6 +52,11 @@ + + + + + diff --git a/Blog.Core.Api/appsettings.json b/Blog.Core.Api/appsettings.json index 78ede19b..0523d64d 100644 --- a/Blog.Core.Api/appsettings.json +++ b/Blog.Core.Api/appsettings.json @@ -324,5 +324,10 @@ "FiedValue": "Blog.Core.Api" } ] + }, + "Seq": { + "Enabled": true, + "Address": "http://localhost:5341/", + "ApiKey": "" } } \ No newline at end of file diff --git a/Blog.Core.Extensions/Blog.Core.Extensions.csproj b/Blog.Core.Extensions/Blog.Core.Extensions.csproj index 9eae3d92..1dfbbc48 100644 --- a/Blog.Core.Extensions/Blog.Core.Extensions.csproj +++ b/Blog.Core.Extensions/Blog.Core.Extensions.csproj @@ -22,6 +22,7 @@ + diff --git a/Blog.Core.Extensions/ServiceExtensions/SerilogSetup.cs b/Blog.Core.Extensions/ServiceExtensions/SerilogSetup.cs index 0d8a0768..94975918 100644 --- a/Blog.Core.Extensions/ServiceExtensions/SerilogSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/SerilogSetup.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.Hosting; using Serilog; using Serilog.Debugging; +using Serilog.Events; using System; using System.IO; @@ -26,8 +27,21 @@ public static IHostBuilder AddSerilogSetup(this IHostBuilder host) //配置日志库 .WriteToLogBatching(); + //配置Seq日志中心 + if (AppSettings.app("Seq", "Enabled").ObjToBool()) + { + var address = AppSettings.app("Seq", "Address"); + var apiKey = AppSettings.app("Seq", "ApiKey"); + if (!address.IsNullOrEmpty()) + { + loggerConfiguration = + loggerConfiguration.WriteTo.Seq(address, restrictedToMinimumLevel: LogEventLevel.Verbose, + apiKey: apiKey, eventBodyLimitBytes: 10485760); + } + } + Log.Logger = loggerConfiguration.CreateLogger(); - + //Serilog 内部日志 var file = File.CreateText(LogContextStatic.Combine($"SerilogDebug{DateTime.Now:yyyyMMdd}.txt")); SelfLog.Enable(TextWriter.Synchronized(file)); From 86e5d6f845d9bc98c5c850759b8c886c7a21f628 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Fri, 21 Apr 2023 22:13:41 +0800 Subject: [PATCH 24/84] Update swg-login.html --- Blog.Core.Api/wwwroot/swg-login.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Blog.Core.Api/wwwroot/swg-login.html b/Blog.Core.Api/wwwroot/swg-login.html index 6e7c6b09..022fec49 100644 --- a/Blog.Core.Api/wwwroot/swg-login.html +++ b/Blog.Core.Api/wwwroot/swg-login.html @@ -3,7 +3,7 @@ 登录 - 接口文档 - + From 720ef2472ada7c5f8bb88a3c3fa39a7f03a9ec04 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Fri, 21 Apr 2023 22:15:45 +0800 Subject: [PATCH 25/84] Update swg-login.html --- Blog.Core.Api/wwwroot/swg-login.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Blog.Core.Api/wwwroot/swg-login.html b/Blog.Core.Api/wwwroot/swg-login.html index 6e7c6b09..022fec49 100644 --- a/Blog.Core.Api/wwwroot/swg-login.html +++ b/Blog.Core.Api/wwwroot/swg-login.html @@ -3,7 +3,7 @@ 登录 - 接口文档 - + From 50a936bdebfa752b92784192a0aed2b311d0e7c7 Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Sat, 22 Apr 2023 11:10:30 +0800 Subject: [PATCH 26/84] =?UTF-8?q?=E2=9C=A8=20=E5=A2=9E=E5=8A=A0IOption?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原有AppSetting 手写字符串去读取,极容易出错,扩展性也低:如果要修改或者增加值 就要到处都写 保留AppSetting基础上增加IOption方便读取配置,可反序列对象 增加IConfigurableOptions接口 可参考SeqOptions配置 配置文件名为Option或Options结尾,匹配时会忽略Option或Options SeqOptions->对应 AppSetting中Seq节点 可直接在控制器中注入使用 参考ValuesController private readonly SeqOptions _seqOptions; cto(IOptions seqOptions){ _seqOptions = seqOptions.Value; } --- Blog.Core.Api/Blog.Core.Api.csproj | 9 +- Blog.Core.Api/Blog.Core.xml | 2 +- Blog.Core.Api/Controllers/ValuesController.cs | 39 ++++-- Blog.Core.Api/Program.cs | 7 +- Blog.Core.Common/App.cs | 114 +++++++++++++++++- Blog.Core.Common/Core/InternalApp.cs | 8 ++ .../Extensions/RuntimeExtension.cs | 84 +++++++++++++ Blog.Core.Common/Helper/Appsettings.cs | 21 ++-- .../Option/Core/ConfigurableOptions.cs | 61 ++++++++++ .../Option/Core/IConfigurableOptions.cs | 11 ++ Blog.Core.Common/Option/SeqOptions.cs | 18 +++ .../ServiceExtensions/AllOptionRegister.cs | 21 ++++ .../ServiceExtensions/ApplicationSetup.cs | 24 ++++ .../ServiceExtensions/SerilogSetup.cs | 8 +- 14 files changed, 396 insertions(+), 31 deletions(-) create mode 100644 Blog.Core.Common/Extensions/RuntimeExtension.cs create mode 100644 Blog.Core.Common/Option/Core/ConfigurableOptions.cs create mode 100644 Blog.Core.Common/Option/Core/IConfigurableOptions.cs create mode 100644 Blog.Core.Common/Option/SeqOptions.cs create mode 100644 Blog.Core.Extensions/ServiceExtensions/AllOptionRegister.cs create mode 100644 Blog.Core.Extensions/ServiceExtensions/ApplicationSetup.cs diff --git a/Blog.Core.Api/Blog.Core.Api.csproj b/Blog.Core.Api/Blog.Core.Api.csproj index 5ff619d1..f3f9befa 100644 --- a/Blog.Core.Api/Blog.Core.Api.csproj +++ b/Blog.Core.Api/Blog.Core.Api.csproj @@ -5,10 +5,11 @@ Exe net6.0 - enable + enable Linux true + @@ -65,8 +66,8 @@ - - + + @@ -114,4 +115,4 @@ - + \ No newline at end of file diff --git a/Blog.Core.Api/Blog.Core.xml b/Blog.Core.Api/Blog.Core.xml index 3cf56019..e664ed17 100644 --- a/Blog.Core.Api/Blog.Core.xml +++ b/Blog.Core.Api/Blog.Core.xml @@ -761,7 +761,7 @@ Values控制器 - + ValuesController diff --git a/Blog.Core.Api/Controllers/ValuesController.cs b/Blog.Core.Api/Controllers/ValuesController.cs index 677eb138..03b37556 100644 --- a/Blog.Core.Api/Controllers/ValuesController.cs +++ b/Blog.Core.Api/Controllers/ValuesController.cs @@ -2,6 +2,7 @@ using Blog.Core.Common; using Blog.Core.Common.HttpContextUser; using Blog.Core.Common.Https.HttpPolly; +using Blog.Core.Common.Option; using Blog.Core.Common.WebApiClients.HttpApis; using Blog.Core.EventBus; using Blog.Core.EventBus.EventHandling; @@ -13,6 +14,7 @@ using Blog.Core.Model.ViewModels; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; using System.ComponentModel.DataAnnotations; using System.Linq.Expressions; @@ -40,6 +42,7 @@ public class ValuesController : ControllerBase private readonly IDoubanApi _doubanApi; readonly IBlogArticleServices _blogArticleServices; private readonly IHttpPollyHelper _httpPollyHelper; + private readonly SeqOptions _seqOptions; /// /// ValuesController @@ -62,7 +65,8 @@ public ValuesController(IBlogArticleServices blogArticleServices , IUser user, IPasswordLibServices passwordLibServices , IBlogApi blogApi , IDoubanApi doubanApi - , IHttpPollyHelper httpPollyHelper) + , IHttpPollyHelper httpPollyHelper + , IOptions seqOptions) { // 测试 Authorize 和 mapper _mapper = mapper; @@ -82,6 +86,7 @@ public ValuesController(IBlogArticleServices blogArticleServices _blogArticleServices = blogArticleServices; // httpPolly _httpPollyHelper = httpPollyHelper; + _seqOptions = seqOptions.Value; } [HttpGet] @@ -127,7 +132,9 @@ public async Task> Get() /* * 测试 sql 查询 */ - var queryBySql = await _blogArticleServices.QuerySql("SELECT bsubmitter,btitle,bcontent,bCreateTime FROM BlogArticle WHERE bID>5"); + var queryBySql = + await _blogArticleServices.QuerySql( + "SELECT bsubmitter,btitle,bcontent,bCreateTime FROM BlogArticle WHERE bID>5"); /* * 测试按照指定列查询 @@ -151,7 +158,8 @@ public async Task> Get() * 【SQL语句】:UPDATE `BlogArticle` SET * `bsubmitter`=@bsubmitter,`IsDeleted`=@IsDeleted WHERE `bID`=@bID */ - var updateSql = await _blogArticleServices.Update(new { bsubmitter = $"laozhang{DateTime.Now.Millisecond}", IsDeleted = false, bID = 5 }); + var updateSql = await _blogArticleServices.Update(new + { bsubmitter = $"laozhang{DateTime.Now.Millisecond}", IsDeleted = false, bID = 5 }); // 测试模拟异常,全局异常过滤器拦截 @@ -364,6 +372,7 @@ public async Task FluentVaTest([FromBody] UserRegisterVo param) public void Put(int id, [FromBody] string value) { } + /// /// Delete方法 /// @@ -375,15 +384,18 @@ public void Delete(int id) } #region Apollo 配置 + /// /// 测试接入Apollo获取配置信息 /// [HttpGet("/apollo")] [AllowAnonymous] - public async Task>> GetAllConfigByAppllo([FromServices] IConfiguration configuration) + public async Task>> GetAllConfigByAppllo( + [FromServices] IConfiguration configuration) { return await Task.FromResult(configuration.AsEnumerable()); } + /// /// 通过此处的key格式为 xx:xx:x /// @@ -393,14 +405,17 @@ public async Task GetConfigByAppllo(string key) { return await Task.FromResult(AppSettings.app(key)); } + #endregion #region HttpPolly + [HttpPost] [AllowAnonymous] public async Task HttpPollyPost() { - var response = await _httpPollyHelper.PostAsync(HttpEnum.LocalHost, "/api/ElasticDemo/EsSearchTest", "{\"from\": 0,\"size\": 10,\"word\": \"非那雄安\"}"); + var response = await _httpPollyHelper.PostAsync(HttpEnum.LocalHost, "/api/ElasticDemo/EsSearchTest", + "{\"from\": 0,\"size\": 10,\"word\": \"非那雄安\"}"); return response; } @@ -409,17 +424,27 @@ public async Task HttpPollyPost() [AllowAnonymous] public async Task HttpPollyGet() { - return await _httpPollyHelper.GetAsync(HttpEnum.LocalHost, "/api/ElasticDemo/GetDetailInfo?esid=3130&esindex=chinacodex"); + return await _httpPollyHelper.GetAsync(HttpEnum.LocalHost, + "/api/ElasticDemo/GetDetailInfo?esid=3130&esindex=chinacodex"); } + #endregion [HttpPost] [AllowAnonymous] public string TestEnum(EnumDemoDto dto) => dto.Type.ToString(); + + [HttpGet] + [AllowAnonymous] + public string TestOption() + { + return _seqOptions.ToJson(); + } } + public class ClaimDto { public string Type { get; set; } public string Value { get; set; } } -} +} \ No newline at end of file diff --git a/Blog.Core.Api/Program.cs b/Blog.Core.Api/Program.cs index b36a02ac..b4929cc7 100644 --- a/Blog.Core.Api/Program.cs +++ b/Blog.Core.Api/Program.cs @@ -12,6 +12,7 @@ using Blog.Core.Extensions.ServiceExtensions; using Blog.Core.Filter; using Blog.Core.Hubs; +using Blog.Core.Serilog.Utility; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Server.Kestrel.Core; @@ -23,8 +24,6 @@ using System.IdentityModel.Tokens.Jwt; using System.Reflection; using System.Text; -using Blog.Core.Common.Https; -using Blog.Core.Serilog.Utility; var builder = WebApplication.CreateBuilder(args); @@ -39,6 +38,7 @@ }) .ConfigureAppConfiguration((hostingContext, config) => { + hostingContext.Configuration.ConfigureApplication(); config.Sources.Clear(); config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false); config.AddConfigurationApollo("appsettings.apollo.json"); @@ -47,7 +47,7 @@ // 2、配置服务 builder.Services.AddSingleton(new AppSettings(builder.Configuration)); - +builder.Services.AddAllOptionRegister(); builder.Services.AddUiFilesZipSetup(builder.Environment); @@ -133,6 +133,7 @@ // 3、配置中间件 var app = builder.Build(); app.ConfigureApplication(); +app.UseApplicationSetup(); if (app.Environment.IsDevelopment()) { diff --git a/Blog.Core.Common/App.cs b/Blog.Core.Common/App.cs index c2e2e706..07abee9f 100644 --- a/Blog.Core.Common/App.cs +++ b/Blog.Core.Common/App.cs @@ -1,18 +1,45 @@ using Blog.Core.Common.Core; +using Blog.Core.Common.Extensions; using Blog.Core.Common.HttpContextUser; +using Blog.Core.Common.Option.Core; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; using System; +using System.Collections.Generic; using System.Linq; +using System.Reflection; namespace Blog.Core.Common; public class App { - public static IServiceProvider RootServices => InternalApp.RootServices; + static App() + { + EffectiveTypes = Assemblies.SelectMany(GetTypes); + } + + private static bool _isRun; + + /// 是否正在运行 + public static bool IsBuild { get; set; } + + public static bool IsRun + { + get => _isRun; + set => _isRun = IsBuild = value; + } + + /// 应用有效程序集 + public static readonly IEnumerable Assemblies = RuntimeExtension.GetAllAssemblies(); + + /// 有效程序集类型 + public static readonly IEnumerable EffectiveTypes; + + public static IServiceProvider RootServices => IsRun || IsBuild ? InternalApp.RootServices : null; /// 获取Web主机环境,如,是否是开发环境,生产环境等 public static IWebHostEnvironment WebHostEnvironment => InternalApp.WebHostEnvironment; @@ -20,6 +47,9 @@ public class App /// 获取泛型主机环境,如,是否是开发环境,生产环境等 public static IHostEnvironment HostEnvironment => InternalApp.HostEnvironment; + /// 全局配置选项 + public static IConfiguration Configuration => InternalApp.Configuration; + /// /// 获取请求上下文 /// @@ -27,6 +57,8 @@ public class App public static IUser User => HttpContext == null ? null : RootServices?.GetService(); + #region Service + /// 解析服务提供器 /// /// @@ -34,7 +66,9 @@ public static IServiceProvider GetServiceProvider(Type serviceType, bool mustBui { if (App.HostEnvironment == null || App.RootServices != null && InternalApp.InternalServices - .Where((u => u.ServiceType == (serviceType.IsGenericType ? serviceType.GetGenericTypeDefinition() : serviceType))) + .Where((u => + u.ServiceType == + (serviceType.IsGenericType ? serviceType.GetGenericTypeDefinition() : serviceType))) .Any((u => u.Lifetime == ServiceLifetime.Singleton))) return App.RootServices; HttpContext httpContext = App.HttpContext; @@ -56,25 +90,95 @@ public static IServiceProvider GetServiceProvider(Type serviceType, bool mustBui } - public static TService GetService(bool mustBuild = true) where TService : class => App.GetService(typeof(TService), null, mustBuild) as TService; + public static TService GetService(bool mustBuild = true) where TService : class => + App.GetService(typeof(TService), null, mustBuild) as TService; /// 获取请求生存周期的服务 /// /// /// /// - public static TService GetService(IServiceProvider serviceProvider, bool mustBuild = true) where TService : class => App.GetService(typeof(TService), serviceProvider, mustBuild) as TService; + public static TService GetService(IServiceProvider serviceProvider, bool mustBuild = true) + where TService : class => App.GetService(typeof(TService), serviceProvider, mustBuild) as TService; /// 获取请求生存周期的服务 /// /// /// /// - public static object GetService(Type type, IServiceProvider serviceProvider = null, bool mustBuild = true) => (serviceProvider ?? App.GetServiceProvider(type, mustBuild)).GetService(type); + public static object GetService(Type type, IServiceProvider serviceProvider = null, bool mustBuild = true) => + (serviceProvider ?? App.GetServiceProvider(type, mustBuild)).GetService(type); + + #endregion + #region private + + /// 加载程序集中的所有类型 + /// + /// + private static IEnumerable GetTypes(Assembly ass) + { + Type[] source = Array.Empty(); + try + { + source = ass.GetTypes(); + } + catch + { + $@"Error load `{ass.FullName}` assembly.".WriteErrorLine(); + } + + return source.Where(u => u.IsPublic); + } + + #endregion + + #region Options + + /// 获取配置 + /// 强类型选项类 + /// TOptions + public static TOptions GetConfig() + where TOptions : class, IConfigurableOptions + { + TOptions instance = App.Configuration + .GetSection(ConfigurableOptions.GetConfigurationPath(typeof(TOptions))) + .Get(); + return instance; + } + + /// 获取选项 + /// 强类型选项类 + /// + /// TOptions public static TOptions GetOptions(IServiceProvider serviceProvider = null) where TOptions : class, new() { IOptions service = App.GetService>(serviceProvider ?? App.RootServices, false); return service?.Value; } + + /// 获取选项 + /// 强类型选项类 + /// + /// TOptions + public static TOptions GetOptionsMonitor(IServiceProvider serviceProvider = null) + where TOptions : class, new() + { + IOptionsMonitor service = + App.GetService>(serviceProvider ?? App.RootServices, false); + return service?.CurrentValue; + } + + /// 获取选项 + /// 强类型选项类 + /// + /// TOptions + public static TOptions GetOptionsSnapshot(IServiceProvider serviceProvider = null) + where TOptions : class, new() + { + IOptionsSnapshot service = App.GetService>(serviceProvider, false); + return service?.Value; + } + + #endregion } \ No newline at end of file diff --git a/Blog.Core.Common/Core/InternalApp.cs b/Blog.Core.Common/Core/InternalApp.cs index 62e04724..df16c911 100644 --- a/Blog.Core.Common/Core/InternalApp.cs +++ b/Blog.Core.Common/Core/InternalApp.cs @@ -3,6 +3,7 @@ using System; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; namespace Blog.Core.Common.Core; @@ -19,6 +20,9 @@ public static class InternalApp /// 获取泛型主机环境 public static IHostEnvironment HostEnvironment; + /// 配置对象 + public static IConfiguration Configuration; + public static void ConfigureApplication(this WebApplicationBuilder wab) { HostEnvironment = wab.Environment; @@ -26,6 +30,10 @@ public static void ConfigureApplication(this WebApplicationBuilder wab) InternalServices = wab.Services; } + public static void ConfigureApplication(this IConfiguration configuration) + { + Configuration = configuration; + } public static void ConfigureApplication(this IHost app) { diff --git a/Blog.Core.Common/Extensions/RuntimeExtension.cs b/Blog.Core.Common/Extensions/RuntimeExtension.cs new file mode 100644 index 00000000..c4ddb0c8 --- /dev/null +++ b/Blog.Core.Common/Extensions/RuntimeExtension.cs @@ -0,0 +1,84 @@ +using Microsoft.Extensions.DependencyModel; +using Serilog; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.Loader; + +namespace Blog.Core.Common.Extensions; + +public static class RuntimeExtension +{ + /// + /// 获取项目程序集,排除所有的系统程序集(Microsoft.***、System.***等)、Nuget下载包 + /// + /// + public static IList GetAllAssemblies() + { + var list = new List(); + var deps = DependencyContext.Default; + var libs = deps.CompileLibraries.Where(lib => !lib.Serviceable && lib.Type != "package"); //排除所有的系统程序集、Nuget下载包 + foreach (var lib in libs) + { + try + { + var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name)); + list.Add(assembly); + } + catch (Exception e) + { + Log.Debug(e, "GetAllAssemblies Exception:{ex}", e.Message); + } + } + return list; + } + + public static Assembly GetAssembly(string assemblyName) + { + return GetAllAssemblies().FirstOrDefault(assembly => assembly.FullName.Contains(assemblyName)); + } + + public static IList GetAllTypes() + { + var list = new List(); + foreach (var assembly in GetAllAssemblies()) + { + var typeInfos = assembly.DefinedTypes; + foreach (var typeInfo in typeInfos) + { + list.Add(typeInfo.AsType()); + } + } + + return list; + } + + public static IList GetTypesByAssembly(string assemblyName) + { + var list = new List(); + var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(assemblyName)); + var typeInfos = assembly.DefinedTypes; + foreach (var typeInfo in typeInfos) + { + list.Add(typeInfo.AsType()); + } + + return list; + } + + public static Type GetImplementType(string typeName, Type baseInterfaceType) + { + return GetAllTypes().FirstOrDefault(t => + { + if (t.Name == typeName && + t.GetTypeInfo().GetInterfaces().Any(b => b.Name == baseInterfaceType.Name)) + { + var typeInfo = t.GetTypeInfo(); + return typeInfo.IsClass && !typeInfo.IsAbstract && !typeInfo.IsGenericType; + } + + return false; + }); + } +} \ No newline at end of file diff --git a/Blog.Core.Common/Helper/Appsettings.cs b/Blog.Core.Common/Helper/Appsettings.cs index d6d06651..83e3d7e7 100644 --- a/Blog.Core.Common/Helper/Appsettings.cs +++ b/Blog.Core.Common/Helper/Appsettings.cs @@ -22,9 +22,12 @@ public AppSettings(string contentPath) //Path = $"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json"; Configuration = new ConfigurationBuilder() - .SetBasePath(contentPath) - .Add(new JsonConfigurationSource { Path = Path, Optional = false, ReloadOnChange = true })//这样的话,可以直接读目录里的json文件,而不是 bin 文件夹下的,所以不用修改复制属性 - .Build(); + .SetBasePath(contentPath) + .Add(new JsonConfigurationSource + { + Path = Path, Optional = false, ReloadOnChange = true + }) //这样的话,可以直接读目录里的json文件,而不是 bin 文件夹下的,所以不用修改复制属性 + .Build(); } public AppSettings(IConfiguration configuration) @@ -41,13 +44,14 @@ public static string app(params string[] sections) { try { - if (sections.Any()) { return Configuration[string.Join(":", sections)]; } } - catch (Exception) { } + catch (Exception) + { + } return ""; } @@ -78,10 +82,11 @@ public static string GetValue(string sectionsPath) { return Configuration[sectionsPath]; } - catch (Exception) { } + catch (Exception) + { + } return ""; - } } -} +} \ No newline at end of file diff --git a/Blog.Core.Common/Option/Core/ConfigurableOptions.cs b/Blog.Core.Common/Option/Core/ConfigurableOptions.cs new file mode 100644 index 00000000..adeea98a --- /dev/null +++ b/Blog.Core.Common/Option/Core/ConfigurableOptions.cs @@ -0,0 +1,61 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using System; + +namespace Blog.Core.Common.Option.Core; + +public static class ConfigurableOptions +{ + /// 添加选项配置 + /// 选项类型 + /// 服务集合 + /// 服务集合 + public static IServiceCollection AddConfigurableOptions(this IServiceCollection services) + where TOptions : class, IConfigurableOptions + { + Type optionsType = typeof(TOptions); + string path = GetConfigurationPath(optionsType); + services.Configure(App.Configuration.GetSection(path)); + + return services; + } + + public static IServiceCollection AddConfigurableOptions(this IServiceCollection services, Type type) + { + string path = GetConfigurationPath(type); + var config = App.Configuration.GetSection(path); + + Type iOptionsChangeTokenSource = typeof(IOptionsChangeTokenSource<>); + Type iConfigureOptions = typeof(IConfigureOptions<>); + Type configurationChangeTokenSource = typeof(ConfigurationChangeTokenSource<>); + Type namedConfigureFromConfigurationOptions = typeof(NamedConfigureFromConfigurationOptions<>); + iOptionsChangeTokenSource = iOptionsChangeTokenSource.MakeGenericType(type); + iConfigureOptions = iConfigureOptions.MakeGenericType(type); + configurationChangeTokenSource = configurationChangeTokenSource.MakeGenericType(type); + namedConfigureFromConfigurationOptions = namedConfigureFromConfigurationOptions.MakeGenericType(type); + + services.AddOptions(); + services.AddSingleton(iOptionsChangeTokenSource, + Activator.CreateInstance(configurationChangeTokenSource, Options.DefaultName, config) ?? throw new InvalidOperationException()); + return services.AddSingleton(iConfigureOptions, + Activator.CreateInstance(namedConfigureFromConfigurationOptions, Options.DefaultName, config) ?? throw new InvalidOperationException()); + } + + /// 获取配置路径 + /// 选项类型 + /// + public static string GetConfigurationPath(Type optionsType) + { + var endPath = new[] {"Option", "Options"}; + var configurationPath = optionsType.Name; + foreach (var s in endPath) + { + if (configurationPath.EndsWith(s)) + { + return configurationPath[..^s.Length]; + } + } + + return configurationPath; + } +} \ No newline at end of file diff --git a/Blog.Core.Common/Option/Core/IConfigurableOptions.cs b/Blog.Core.Common/Option/Core/IConfigurableOptions.cs new file mode 100644 index 00000000..71568268 --- /dev/null +++ b/Blog.Core.Common/Option/Core/IConfigurableOptions.cs @@ -0,0 +1,11 @@ +namespace Blog.Core.Common.Option.Core; + +/// +/// 应用选项依赖接口
+/// 自动注入配置文件
+/// 文件名为Option或Options结尾 +///
+public interface IConfigurableOptions +{ + +} \ No newline at end of file diff --git a/Blog.Core.Common/Option/SeqOptions.cs b/Blog.Core.Common/Option/SeqOptions.cs new file mode 100644 index 00000000..944aa7bc --- /dev/null +++ b/Blog.Core.Common/Option/SeqOptions.cs @@ -0,0 +1,18 @@ +using Blog.Core.Common.Option.Core; + +namespace Blog.Core.Common.Option; + +public class SeqOptions : IConfigurableOptions +{ + /// + /// 是否启用 + /// + public bool Enabled { get; set; } + + /// + /// 地址 + /// + public string Address { get; set; } + + public string ApiKey { get; set; } +} \ No newline at end of file diff --git a/Blog.Core.Extensions/ServiceExtensions/AllOptionRegister.cs b/Blog.Core.Extensions/ServiceExtensions/AllOptionRegister.cs new file mode 100644 index 00000000..28e1739b --- /dev/null +++ b/Blog.Core.Extensions/ServiceExtensions/AllOptionRegister.cs @@ -0,0 +1,21 @@ +using Blog.Core.Common; +using Blog.Core.Common.Option.Core; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Linq; + +namespace Blog.Core.Extensions.ServiceExtensions; + +public static class AllOptionRegister +{ + public static void AddAllOptionRegister(this IServiceCollection services) + { + if (services == null) throw new ArgumentNullException(nameof(services)); + + foreach (var optionType in App.EffectiveTypes.Where(s => + !s.IsInterface && typeof(IConfigurableOptions).IsAssignableFrom(s))) + { + services.AddConfigurableOptions(optionType); + } + } +} \ No newline at end of file diff --git a/Blog.Core.Extensions/ServiceExtensions/ApplicationSetup.cs b/Blog.Core.Extensions/ServiceExtensions/ApplicationSetup.cs new file mode 100644 index 00000000..793df2c8 --- /dev/null +++ b/Blog.Core.Extensions/ServiceExtensions/ApplicationSetup.cs @@ -0,0 +1,24 @@ +using Blog.Core.Common; +using Microsoft.AspNetCore.Builder; +using Serilog; + +namespace Blog.Core.Extensions.ServiceExtensions; + +public static class ApplicationSetup +{ + public static void UseApplicationSetup(this WebApplication app) + { + app.Lifetime.ApplicationStarted.Register(() => + { + App.IsRun = true; + }); + + app.Lifetime.ApplicationStopped.Register(() => + { + App.IsRun = false; + + //清除日志 + Log.CloseAndFlush(); + }); + } +} \ No newline at end of file diff --git a/Blog.Core.Extensions/ServiceExtensions/SerilogSetup.cs b/Blog.Core.Extensions/ServiceExtensions/SerilogSetup.cs index 94975918..30ab29f8 100644 --- a/Blog.Core.Extensions/ServiceExtensions/SerilogSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/SerilogSetup.cs @@ -8,6 +8,7 @@ using Serilog.Events; using System; using System.IO; +using Blog.Core.Common.Option; namespace Blog.Core.Extensions.ServiceExtensions; @@ -27,11 +28,12 @@ public static IHostBuilder AddSerilogSetup(this IHostBuilder host) //配置日志库 .WriteToLogBatching(); + var option = App.GetOptions(); //配置Seq日志中心 - if (AppSettings.app("Seq", "Enabled").ObjToBool()) + if (option.Enabled) { - var address = AppSettings.app("Seq", "Address"); - var apiKey = AppSettings.app("Seq", "ApiKey"); + var address = option.Address; + var apiKey = option.ApiKey; if (!address.IsNullOrEmpty()) { loggerConfiguration = From 014ab8de749b3b1c62408ec91727cabbd2379018 Mon Sep 17 00:00:00 2001 From: hudingwen <765472804@qq.com> Date: Sat, 29 Apr 2023 12:03:12 +0800 Subject: [PATCH 27/84] remove extra code --- Blog.Core.Api/Blog.Core.xml | 2 -- Blog.Core.Api/Controllers/WeChatController.cs | 2 -- 2 files changed, 4 deletions(-) diff --git a/Blog.Core.Api/Blog.Core.xml b/Blog.Core.Api/Blog.Core.xml index c6b33db9..45e38404 100644 --- a/Blog.Core.Api/Blog.Core.xml +++ b/Blog.Core.Api/Blog.Core.xml @@ -1147,8 +1147,6 @@ 微信公众号管理 - 作者:胡丁文 - 时间:2020-3-29 21:24:12 diff --git a/Blog.Core.Api/Controllers/WeChatController.cs b/Blog.Core.Api/Controllers/WeChatController.cs index a27762e8..5c7e5c6f 100644 --- a/Blog.Core.Api/Controllers/WeChatController.cs +++ b/Blog.Core.Api/Controllers/WeChatController.cs @@ -8,8 +8,6 @@ namespace Blog.Core.Controllers { /// /// 微信公众号管理 - /// 作者:胡丁文 - /// 时间:2020-3-29 21:24:12 /// [Route("api/[controller]/[action]")] [ApiController] From 7ddec93479903b713b63b0fdd63eead3c15dcf70 Mon Sep 17 00:00:00 2001 From: hudingwen <765472804@qq.com> Date: Sat, 29 Apr 2023 12:03:12 +0800 Subject: [PATCH 28/84] remove extra code --- Blog.Core.Api/Blog.Core.Model.xml | 137 ------ Blog.Core.Api/Blog.Core.xml | 155 ------ Blog.Core.Api/Controllers/TrojanController.cs | 456 ------------------ Blog.Core.Api/Controllers/WeChatController.cs | 2 - Blog.Core.IServices/ITrojanUsersServices.cs | 14 - Blog.Core.Model/Models/TrojanCusServers.cs | 26 - Blog.Core.Model/Models/TrojanDetails.cs | 63 --- Blog.Core.Model/Models/TrojanServers.cs | 31 -- Blog.Core.Model/Models/TrojanUrlServers.cs | 26 - Blog.Core.Model/Models/TrojanUsers.cs | 39 -- .../ViewModels/TrojanLimitFlowDto.cs | 23 - Blog.Core.Model/ViewModels/TrojanServerDto.cs | 14 - .../ViewModels/TrojanServerSpliceDto.cs | 28 -- .../ViewModels/TrojanUseDetailDto.cs | 35 -- Blog.Core.Services/TrojanUsersServices.cs | 18 - .../QuartzNet/Jobs/Job_Trojan_Quartz.cs | 81 ---- 16 files changed, 1148 deletions(-) delete mode 100644 Blog.Core.Api/Controllers/TrojanController.cs delete mode 100644 Blog.Core.IServices/ITrojanUsersServices.cs delete mode 100644 Blog.Core.Model/Models/TrojanCusServers.cs delete mode 100644 Blog.Core.Model/Models/TrojanDetails.cs delete mode 100644 Blog.Core.Model/Models/TrojanServers.cs delete mode 100644 Blog.Core.Model/Models/TrojanUrlServers.cs delete mode 100644 Blog.Core.Model/Models/TrojanUsers.cs delete mode 100644 Blog.Core.Model/ViewModels/TrojanLimitFlowDto.cs delete mode 100644 Blog.Core.Model/ViewModels/TrojanServerDto.cs delete mode 100644 Blog.Core.Model/ViewModels/TrojanServerSpliceDto.cs delete mode 100644 Blog.Core.Model/ViewModels/TrojanUseDetailDto.cs delete mode 100644 Blog.Core.Services/TrojanUsersServices.cs delete mode 100644 Blog.Core.Tasks/QuartzNet/Jobs/Job_Trojan_Quartz.cs diff --git a/Blog.Core.Api/Blog.Core.Model.xml b/Blog.Core.Api/Blog.Core.Model.xml index bf3d350d..79666d28 100644 --- a/Blog.Core.Api/Blog.Core.Model.xml +++ b/Blog.Core.Api/Blog.Core.Model.xml @@ -1260,76 +1260,6 @@ Tibug 博文
- - - users自定义服务器 - - - - - 用户流量每月汇总表 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Trojan服务器 - - - - - users自定义URL服务器 - - - - - Trojan用户 - - - - - 历史流量记录 - - 用户跟角色关联表 @@ -2787,73 +2717,6 @@ - - - 限制流量dto - 作者:胡丁文 - 时间:2020-4-27 16:57:07 - - - - - 用户 - - - - - 流量(-1为无限,单位为最小单位byte) - - - - - Trojan服务器拼接服务器和订阅地址 - - - - - 普通订阅连接 - - - - - clash订阅连接 - - - - - 备用clash订阅连接 - - - - - Trojan用户流量统计分组 - - - - - 用户ID - - - - - 月度 - - - - - 上传流量 - - - - - 下载流量 - - - - - 下载流量 - - 微信接口消息DTO diff --git a/Blog.Core.Api/Blog.Core.xml b/Blog.Core.Api/Blog.Core.xml index c6b33db9..05783a69 100644 --- a/Blog.Core.Api/Blog.Core.xml +++ b/Blog.Core.Api/Blog.Core.xml @@ -664,159 +664,6 @@ - - - 获取Trojan用户 - - - - - - - - - 获取Trojan用户-下拉列表用 - - - - - - 添加Trojan用户 - - - - - - - 更新Trojan用户 - - - - - - - 删除用户 - - - - - - - 重置流量 - - - - - - - 限制流量 - - - - - - - 重置链接密码 - - - - - - - 获取Trojan服务器 - - - - - - 获取拼接后的Trojan服务器 - - passwordshow - - - - - 删除Trojan服务器 - - - - - - - 更新Trojan服务器 - - - - - - - 添加Trojan服务器 - - - - - - - 获取Cus服务器 - - - - - - 删除Cus服务器 - - - - - - - 更新Cus服务器 - - - - - - - 添加Cus服务器 - - - - - - - 获取Url服务器 - - - - - - 删除Url服务器 - - - - - - - 更新Url服务器 - - - - - - - 添加Url服务器 - - - - - - - 获取订阅数据 - - 链接密码 - 是否使用base64加密 - - 用户管理 @@ -1147,8 +994,6 @@ 微信公众号管理 - 作者:胡丁文 - 时间:2020-3-29 21:24:12 diff --git a/Blog.Core.Api/Controllers/TrojanController.cs b/Blog.Core.Api/Controllers/TrojanController.cs deleted file mode 100644 index d8d005dc..00000000 --- a/Blog.Core.Api/Controllers/TrojanController.cs +++ /dev/null @@ -1,456 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Blog.Core.Common; -using Blog.Core.Common.Extensions; -using Blog.Core.Common.Helper; -using Blog.Core.Common.HttpContextUser; -using Blog.Core.IServices; -using Blog.Core.IServices.BASE; -using Blog.Core.Model; -using Blog.Core.Model.Models; -using Blog.Core.Model.ViewModels; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Math; - -namespace Blog.Core.Controllers -{ - [Route("api/[controller]/[action]")] - [ApiController] - [Authorize(Permissions.Name)] - public class TrojanController : ControllerBase - { - private ITrojanUsersServices _trojanUsersServices; - public IBaseServices _baseServicesServers; - public IBaseServices _baseServicesDetails; - public IBaseServices _baseServicesCusServers; - public IBaseServices _baseServicesUrlServers; - private IUser _user; - public TrojanController(ITrojanUsersServices trojanUsersServices,IUser user - , IBaseServices baseServicesServers - , IBaseServices baseServicesDetails - , IBaseServices baseServicesCusServers - , IBaseServices baseServicesUrlServers) - { - _baseServicesDetails = baseServicesDetails; - _baseServicesServers = baseServicesServers; - _trojanUsersServices = trojanUsersServices; - _baseServicesCusServers = baseServicesCusServers; - _baseServicesUrlServers = baseServicesUrlServers; - _user = user; - } - /// - /// 获取Trojan用户 - /// - /// - /// - /// - /// - [HttpGet] - public async Task>> GetUser([FromQuery]PaginationModel pagination, [FromQuery] string name, [FromQuery] bool isuse) - { - var whereFind = LinqHelper.True(); - if (!string.IsNullOrEmpty(name)) - whereFind = whereFind.And(t=>t.username.Contains(name)); - if (isuse) - whereFind = whereFind.And(t => t.upload > 0 || t.download > 0); - var data = await _trojanUsersServices.QueryPage(whereFind, pagination.PageIndex, pagination.PageSize); - if (data.data.Count > 0) - { - var ids = data.data.Select(t => t.id).ToList(); - var where = LinqHelper.True(); - where = where.And(t => ids.Contains(t.userId));//.And(t => t.calDate < DateTime.Now).And(t => t.calDate > DateTime.Now.AddMonths(-12)); - var userDetails = await _baseServicesDetails.Query(where); - foreach (var trojanUser in data.data) - { - var ls = from t in userDetails - where t.userId == trojanUser.id - group t by new { moth = t.calDate.ToString("yyyy-MM"), id = t.userId } into g - orderby g.Key.moth descending - select new TrojanUseDetailDto { userId = g.Key.id, moth = g.Key.moth, up = g.Sum(t => Convert.ToDecimal(t.upload)), down = g.Sum(t => Convert.ToDecimal(t.download)) }; - var lsData = ls.ToList(); - trojanUser.useList = lsData; - } - } - return MessageModel>.Success("获取成功", data); - } - - /// - /// 获取Trojan用户-下拉列表用 - /// - /// - [HttpGet] - public async Task> GetAllTrojanUser() - { - var data = await _trojanUsersServices.QueryTable("select id,username from users"); - return MessageModel.Success("获取成功", data); - } - /// - /// 添加Trojan用户 - /// - /// - /// - [HttpPost] - public async Task> AddUser([FromBody]TrojanUsers user) - { - var find = await _trojanUsersServices.Query(t => t.username == user.username); - if(find!=null && find.Count>0) return MessageModel.Fail("用户名已存在"); - var pass = StringHelper.GetGUID(); - var passEcrypt = ShaHelper.Sha224(pass); - //user.quota = 0; - user.upload = 0; - user.download = 0; - user.password = passEcrypt; - user.passwordshow = pass; - var data = await _trojanUsersServices.Add(user); - return MessageModel.Success("添加成功", data); - } - /// - /// 更新Trojan用户 - /// - /// - /// - [HttpPut] - public async Task> UpdateUser([FromBody]TrojanUsers user) - { - var find = await _trojanUsersServices.QueryById(user.id); - if (find == null) return MessageModel.Fail("用户名不存在"); - find.username = user.username; - var data = await _trojanUsersServices.Update(find, new List { "username" }); - return MessageModel.Success("更新成功", data); - } - - /// - /// 删除用户 - /// - /// - /// - [HttpPut] - public async Task> DelUser([FromBody]int[] users) - { - var data = await _trojanUsersServices.Query(t => users.Contains(t.id)); - var list = data.Select(t => t.id.ToString()).ToArray(); - await _trojanUsersServices.DeleteByIds(list); - return MessageModel.Success("删除成功"); - } - /// - /// 重置流量 - /// - /// - /// - [HttpPut] - public async Task> ResetFlow([FromBody]int[] users) - { - var data = await _trojanUsersServices.Query(t => users.Contains(t.id)); - foreach (var item in data) - { - item.upload = 0; - item.download = 0; - await _trojanUsersServices.Update(item, new List { "upload", "download" }); - } - return MessageModel.Success("重置流量成功"); - } - /// - /// 限制流量 - /// - /// - /// - [HttpPut] - public async Task> LimitFlow([FromBody] TrojanLimitFlowDto limit) - { - var data = await _trojanUsersServices.Query(t => limit.users.Contains(t.id)); - foreach (var item in data) - { - item.quota = limit.quota; - await _trojanUsersServices.Update(item, new List { "quota" }); - } - return MessageModel.Success("限制流量成功"); - } - /// - /// 重置链接密码 - /// - /// - /// - [HttpPut] - public async Task> ResetPass([FromBody]int[] users) - { - var data = await _trojanUsersServices.Query(t => users.Contains(t.id)); - var pass = StringHelper.GetGUID(); - var passEcrypt = ShaHelper.Sha224(pass); - foreach (var item in data) - { - item.password = passEcrypt; - item.passwordshow = pass; - await _trojanUsersServices.Update(item, new List { "password" , "passwordshow" }); - } - return MessageModel.Success("重置链接密码成功"); - } - /// - /// 获取Trojan服务器 - /// - /// - [HttpGet] - public async Task>> GetServers() - { - var data = await _baseServicesServers.Query(); - data = data.OrderBy(t => t.servername).ToList(); - return MessageModel>.Success("获取成功", data); - } - /// - /// 获取拼接后的Trojan服务器 - /// - /// passwordshow - /// - [HttpGet] - public async Task> GetSpliceServers(string id) - { - var data = await _baseServicesServers.Query(); - data = data.OrderBy(t => t.servername).ToList(); - var res = new TrojanServerSpliceDto(); - res.normalApi = AppSettings.app(new string[] { "trojan", "normalApi" }).ObjToString(); - res.clashApi = AppSettings.app(new string[] { "trojan", "clashApi" }).ObjToString(); - res.clashApiBackup = AppSettings.app(new string[] { "trojan", "clashApiBackup" }).ObjToString(); - foreach (var item in data) - { - var serverSplice = GetSplice(item, id); - res.list.Add(new TrojanServerDto { name = item.servername, value = serverSplice }); - } - return MessageModel.Success("获取成功", res); ; - - } - /// - /// 删除Trojan服务器 - /// - /// - /// - [HttpPut] - public async Task>> DelServers([FromBody]int[] servers) - { - var data = await _baseServicesServers.DeleteByIds(servers.Select(t=>t.ToString()).ToArray()); - if (data) - return MessageModel>.Success("删除成功"); - else - return MessageModel>.Fail("删除失败"); - } - /// - /// 更新Trojan服务器 - /// - /// - /// - [HttpPut] - public async Task>> UpdateServers(TrojanServers server) - { - var data = await _baseServicesServers.Update(server); - return MessageModel>.Success("更新成功"); - } - /// - /// 添加Trojan服务器 - /// - /// - /// - [HttpPost] - public async Task>> AddServers(TrojanServers server) - { - var data = await _baseServicesServers.Add(server); - return MessageModel>.Success("添加成功"); - } - - /// - /// 获取Cus服务器 - /// - /// - [HttpGet] - public async Task>> GetCusServers() - { - var data = await _baseServicesCusServers.Query(); - data = data.OrderBy(t => t.servername).ToList(); - return MessageModel>.Success("获取成功", data); - } - /// - /// 删除Cus服务器 - /// - /// - /// - [HttpPut] - public async Task>> DelCusServers([FromBody] int[] servers) - { - var data = await _baseServicesCusServers.DeleteByIds(servers.Select(t => t.ToString()).ToArray()); - if (data) - return MessageModel>.Success("删除成功"); - else - return MessageModel>.Fail("删除失败"); - } - /// - /// 更新Cus服务器 - /// - /// - /// - [HttpPut] - public async Task>> UpdateCusServers(TrojanCusServers server) - { - var data = await _baseServicesCusServers.Update(server); - return MessageModel>.Success("更新成功"); - } - /// - /// 添加Cus服务器 - /// - /// - /// - [HttpPost] - public async Task>> AddCusServers(TrojanCusServers server) - { - var data = await _baseServicesCusServers.Add(server); - return MessageModel>.Success("添加成功"); - } - - - /// - /// 获取Url服务器 - /// - /// - [HttpGet] - public async Task>> GetUrlServers() - { - var data = await _baseServicesUrlServers.Query(); - data = data.OrderBy(t => t.servername).ToList(); - return MessageModel>.Success("获取成功", data); - } - /// - /// 删除Url服务器 - /// - /// - /// - [HttpPut] - public async Task>> DelUrlServers([FromBody] int[] servers) - { - var data = await _baseServicesUrlServers.DeleteByIds(servers.Select(t => t.ToString()).ToArray()); - if (data) - return MessageModel>.Success("删除成功"); - else - return MessageModel>.Fail("删除失败"); - } - /// - /// 更新Url服务器 - /// - /// - /// - [HttpPut] - public async Task>> UpdateUrlServers(TrojanUrlServers server) - { - var data = await _baseServicesUrlServers.Update(server); - return MessageModel>.Success("更新成功"); - } - /// - /// 添加Url服务器 - /// - /// - /// - [HttpPost] - public async Task>> AddUrlServers(TrojanUrlServers server) - { - var data = await _baseServicesUrlServers.Add(server); - return MessageModel>.Success("添加成功"); - } - private string GetSplice(TrojanServers item,string passwordshow) - { - var sni = string.IsNullOrEmpty(item.serverpeer) ? item.serveraddress : item.serverpeer; - if ("0".Equals(item.servertype)) - return $"trojan://{passwordshow}@{item.serveraddress}:{item.serverport}?allowinsecure=0&tfo=0&fp=chrome&peer={sni}&host={sni}&sni={sni}#{item.servername}"; - else if ("1".Equals(item.servertype)) - { - - return $"trojan://{passwordshow}@{item.serveraddress}:{item.serverport}?wspath={item.serverpath}&ws=1&peer={sni}&path={item.serverpath}&host={sni}&fp=chrome&type=ws&sni={sni}#{item.servername}"; - } - else - return $"servertype:({item.servertype})错误"; - } - private List GetSplice(List items, string passwordshow) - { - List ls = new List(); - foreach (var item in items) - { - ls.Add(GetSplice(item, passwordshow)); - } - return ls; - } - /// - /// 获取订阅数据 - /// - /// 链接密码 - /// 是否使用base64加密 - /// - [HttpGet] - [AllowAnonymous] - public async Task RSS(string id,bool isUseBase64=true) - { - StringBuilder sb = new StringBuilder(); - try - { - var user = (await _trojanUsersServices.Query(t => t.passwordshow == id)).FirstOrDefault(); - if (user == null) throw new Exception("用户不存在"); - var data = await _baseServicesServers.Query(t => (t.userid == user.id || t.userid <= 0) && t.serverenable); - if (data != null) - { - data = data.OrderBy(t => t.servername).ToList(); - foreach (var item in data) - { - sb.AppendLine(GetSplice(item, user.passwordshow)); - } - } - var cusData = await _baseServicesCusServers.Query(t=> (t.userid == user.id || t.userid <=0) && t.serverenable); - if (cusData != null) - { - cusData = cusData.OrderBy(t => t.servername).ToList(); - foreach (var item in cusData) - { - sb.AppendLine(item.serveraddress); - } - } - var urlData = await _baseServicesUrlServers.Query(t => (t.userid == user.id || t.userid <= 0) && t.serverenable); - if (urlData != null) - { - urlData = urlData.OrderBy(t => t.servername).ToList(); - foreach (var item in urlData) - { - try - { - var urlStrObj = await HttpHelper.GetAsync(item.serveraddress); - var lines = ""; - try - { - lines = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(urlStrObj)); - } - catch (Exception) - { - lines = urlStrObj; - } - finally - { - sb.AppendLine(lines); - } - } - catch (Exception ex) - { - sb.AppendLine($"trojan://xxxxxx@xxxxxx.xx:443?allowinsecure=0&tfo=0#{ex.Message}"); - } - } - } - } - catch (Exception ex) - { - sb.AppendLine($"trojan://xxxxxx@xxxxxx.xx:443?allowinsecure=0&tfo=0#{ex.Message}"); - } - if (isUseBase64) - { - return Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(sb.ToString())); - } - else{ - return sb.ToString(); - } - } - } -} \ No newline at end of file diff --git a/Blog.Core.Api/Controllers/WeChatController.cs b/Blog.Core.Api/Controllers/WeChatController.cs index a27762e8..5c7e5c6f 100644 --- a/Blog.Core.Api/Controllers/WeChatController.cs +++ b/Blog.Core.Api/Controllers/WeChatController.cs @@ -8,8 +8,6 @@ namespace Blog.Core.Controllers { /// /// 微信公众号管理 - /// 作者:胡丁文 - /// 时间:2020-3-29 21:24:12 /// [Route("api/[controller]/[action]")] [ApiController] diff --git a/Blog.Core.IServices/ITrojanUsersServices.cs b/Blog.Core.IServices/ITrojanUsersServices.cs deleted file mode 100644 index 92bac1d8..00000000 --- a/Blog.Core.IServices/ITrojanUsersServices.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Blog.Core.IServices.BASE; -using Blog.Core.Model.Models; - -namespace Blog.Core.IServices -{ - /// - /// TrojanUsersServices - /// - public interface ITrojanUsersServices : IBaseServices - { - - } -} - diff --git a/Blog.Core.Model/Models/TrojanCusServers.cs b/Blog.Core.Model/Models/TrojanCusServers.cs deleted file mode 100644 index 03f02539..00000000 --- a/Blog.Core.Model/Models/TrojanCusServers.cs +++ /dev/null @@ -1,26 +0,0 @@ - -using System; -using System.Linq; -using System.Text; -using SqlSugar; - -namespace Blog.Core.Model.Models -{ - /// - ///users自定义服务器 - /// - [SugarTable("users_cus", "users自定义服务器")] - [TenantAttribute("WMBLOG_MYSQL_2")] //('代表是哪个数据库,名字是appsettings.json 的 ConnId') - public partial class TrojanCusServers - { - - [SugarColumn(IsNullable = false, IsPrimaryKey = true, IsIdentity = true)] - public int id { set; get; } - public int userid { get; set; } - public string servername { set; get; } - public string serveraddress { set; get; } - [SugarColumn(IsNullable = true)] - public string serverremark { get; set; } - public bool serverenable { get; set; } - } -} diff --git a/Blog.Core.Model/Models/TrojanDetails.cs b/Blog.Core.Model/Models/TrojanDetails.cs deleted file mode 100644 index dda53d9b..00000000 --- a/Blog.Core.Model/Models/TrojanDetails.cs +++ /dev/null @@ -1,63 +0,0 @@ - -//模板自动生成(请勿修改) -//作者:胡丁文 -using System; -using System.Linq; -using System.Text; -using SqlSugar; - -namespace Blog.Core.Model.Models -{ - /// - ///用户流量每月汇总表 - /// - [SugarTable("users_detail", "用户流量每月汇总表")] - [TenantAttribute("WMBLOG_MYSQL_2")] //('代表是哪个数据库,名字是appsettings.json 的 ConnId') - public partial class TrojanDetails - { - - /// - /// - /// - [SugarColumn(IsPrimaryKey = true, IsIdentity = true)] - public int id { get; set; } - - /// - /// - /// - public int userId { get; set; } - - /// - /// - /// - public DateTime calDate { get; set; } - - /// - /// - /// - public ulong download { get; set; } - - /// - /// - /// - public ulong upload { get; set; } - - /// - /// - /// - [SugarColumn(IsNullable = true)] - public int? CreateId { get; set; } - - /// - /// - /// - [SugarColumn(IsNullable = true)] - public string CreateBy { get; set; } - - /// - /// - /// - [SugarColumn(IsNullable = true)] - public DateTime? CreateTime { get; set; } - } -} diff --git a/Blog.Core.Model/Models/TrojanServers.cs b/Blog.Core.Model/Models/TrojanServers.cs deleted file mode 100644 index d9d8275f..00000000 --- a/Blog.Core.Model/Models/TrojanServers.cs +++ /dev/null @@ -1,31 +0,0 @@ - -using System; -using System.Linq; -using System.Text; -using SqlSugar; - -namespace Blog.Core.Model.Models -{ - /// - ///Trojan服务器 - /// - [SugarTable("servers", "Trojan服务器")] - [TenantAttribute("WMBLOG_MYSQL_2")] //('代表是哪个数据库,名字是appsettings.json 的 ConnId') - public partial class TrojanServers - { - - [SugarColumn(IsNullable = false, IsPrimaryKey = true, IsIdentity = true)] - public int id { set; get; } - public int userid { get; set; } - public string servername { set; get; } - public string serveraddress { set; get; } - public int serverport { get; set; } - [SugarColumn(IsNullable = true)] - public string serverremark { get; set; } - public bool serverenable { get; set; } - public string serverpeer { get; set; } - [SugarColumn(IsNullable = true)] - public string serverpath { get; set; } - public string servertype { get; set; } - } -} diff --git a/Blog.Core.Model/Models/TrojanUrlServers.cs b/Blog.Core.Model/Models/TrojanUrlServers.cs deleted file mode 100644 index db48343a..00000000 --- a/Blog.Core.Model/Models/TrojanUrlServers.cs +++ /dev/null @@ -1,26 +0,0 @@ - -using System; -using System.Linq; -using System.Text; -using SqlSugar; - -namespace Blog.Core.Model.Models -{ - /// - ///users自定义URL服务器 - /// - [SugarTable("users_url", "users自定义URL服务器")] - [TenantAttribute("WMBLOG_MYSQL_2")] //('代表是哪个数据库,名字是appsettings.json 的 ConnId') - public partial class TrojanUrlServers - { - - [SugarColumn(IsNullable = false, IsPrimaryKey = true, IsIdentity = true)] - public int id { set; get; } - public int userid { get; set; } - public string servername { set; get; } - public string serveraddress { set; get; } - [SugarColumn(IsNullable = true)] - public string serverremark { get; set; } - public bool serverenable { get; set; } - } -} diff --git a/Blog.Core.Model/Models/TrojanUsers.cs b/Blog.Core.Model/Models/TrojanUsers.cs deleted file mode 100644 index 796c044e..00000000 --- a/Blog.Core.Model/Models/TrojanUsers.cs +++ /dev/null @@ -1,39 +0,0 @@ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Blog.Core.Model.ViewModels; -using SqlSugar; - -namespace Blog.Core.Model.Models -{ - /// - ///Trojan用户 - /// - [SugarTable("users", "Trojan用户表")] - [TenantAttribute("WMBLOG_MYSQL_2")] //('代表是哪个数据库,名字是appsettings.json 的 ConnId') - public partial class TrojanUsers - { - - [SugarColumn(IsNullable = false, IsPrimaryKey = true, IsIdentity = true)] - public int id { set; get; } - public string username { set; get; } - public string password { set; get; } - public Int64 quota { set; get; } - public UInt64 download { set; get; } - public UInt64 upload { set; get; } - public string passwordshow { set; get; } - [SugarColumn(IsNullable = true)] - public int CreateId { get; set; } - [SugarColumn(IsNullable = true)] - public string CreateBy { get; set; } - [SugarColumn(IsNullable = true)] - public DateTime? CreateTime { get; set; } - /// - /// 历史流量记录 - /// - [SugarColumn(IsIgnore = true)] - public List useList { get; set; } - } -} diff --git a/Blog.Core.Model/ViewModels/TrojanLimitFlowDto.cs b/Blog.Core.Model/ViewModels/TrojanLimitFlowDto.cs deleted file mode 100644 index c0efd347..00000000 --- a/Blog.Core.Model/ViewModels/TrojanLimitFlowDto.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Blog.Core.Model.ViewModels -{ - /// - /// 限制流量dto - /// 作者:胡丁文 - /// 时间:2020-4-27 16:57:07 - /// - public class TrojanLimitFlowDto - { - /// - /// 用户 - /// - public int[] users { get; set; } - /// - /// 流量(-1为无限,单位为最小单位byte) - /// - public Int64 quota { get; set; } - } -} diff --git a/Blog.Core.Model/ViewModels/TrojanServerDto.cs b/Blog.Core.Model/ViewModels/TrojanServerDto.cs deleted file mode 100644 index 89cb87e3..00000000 --- a/Blog.Core.Model/ViewModels/TrojanServerDto.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Blog.Core.Model.ViewModels -{ - public class TrojanServerDto - { - public string name { get; set; } - public string value { get; set; } - } -} diff --git a/Blog.Core.Model/ViewModels/TrojanServerSpliceDto.cs b/Blog.Core.Model/ViewModels/TrojanServerSpliceDto.cs deleted file mode 100644 index e83adfd5..00000000 --- a/Blog.Core.Model/ViewModels/TrojanServerSpliceDto.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Blog.Core.Model.ViewModels -{ - /// - /// Trojan服务器拼接服务器和订阅地址 - /// - public class TrojanServerSpliceDto - { - /// - /// 普通订阅连接 - /// - public string normalApi { get; set; } - /// - /// clash订阅连接 - /// - public string clashApi { get; set; } - /// - /// 备用clash订阅连接 - /// - public string clashApiBackup { get; set; } - public List list { get; set; } = new List(); - } -} diff --git a/Blog.Core.Model/ViewModels/TrojanUseDetailDto.cs b/Blog.Core.Model/ViewModels/TrojanUseDetailDto.cs deleted file mode 100644 index 7746b105..00000000 --- a/Blog.Core.Model/ViewModels/TrojanUseDetailDto.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Blog.Core.Model.ViewModels -{ - /// - /// Trojan用户流量统计分组 - /// - public class TrojanUseDetailDto - { - /// - /// 用户ID - /// - public int userId { get; set; } - /// - /// 月度 - /// - public string moth { get; set; } - /// - /// 上传流量 - /// - public decimal up { get; set; } - /// - /// 下载流量 - /// - public decimal down { get; set; } - /// - /// 下载流量 - /// - public decimal total { get { return up + down; } } - } -} diff --git a/Blog.Core.Services/TrojanUsersServices.cs b/Blog.Core.Services/TrojanUsersServices.cs deleted file mode 100644 index f42f51d9..00000000 --- a/Blog.Core.Services/TrojanUsersServices.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Blog.Core.Common; -using Blog.Core.IRepository.Base; -using Blog.Core.IServices; -using Blog.Core.Model.Models; -using Blog.Core.Services.BASE; -using System.Linq; -using System.Threading.Tasks; - -namespace Blog.Core.Services -{ - /// - /// TrojanUsersServices - /// - public class TrojanUsersServices : BaseServices, ITrojanUsersServices - { - - } -} diff --git a/Blog.Core.Tasks/QuartzNet/Jobs/Job_Trojan_Quartz.cs b/Blog.Core.Tasks/QuartzNet/Jobs/Job_Trojan_Quartz.cs deleted file mode 100644 index 9d2d4cea..00000000 --- a/Blog.Core.Tasks/QuartzNet/Jobs/Job_Trojan_Quartz.cs +++ /dev/null @@ -1,81 +0,0 @@ - -using Blog.Core.IServices; -using Blog.Core.IServices.BASE; -using Blog.Core.Model.Models; -using Blog.Core.Repository.UnitOfWorks; -using Microsoft.Extensions.Logging; -using Quartz; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -/// -/// 这里要注意下,命名空间和程序集是一样的,不然反射不到(任务类要去JobSetup添加注入) -/// -namespace Blog.Core.Tasks -{ - public class Job_Trojan_Quartz : JobBase, IJob - { - private readonly IUnitOfWorkManage _unitOfWorkManage; - public IBaseServices_DetailServices; - private readonly ITrojanUsersServices _TrojanUsers; - private readonly ILogger _logger; - - public Job_Trojan_Quartz(IUnitOfWorkManage unitOfWorkManage, IBaseServices iusers_DetailServices, ITrojanUsersServices trojanUsers, ILogger logger, ITasksQzServices tasksQzServices, ITasksLogServices tasksLogServices) - : base(tasksQzServices, tasksLogServices) - { - _tasksQzServices = tasksQzServices; - _unitOfWorkManage = unitOfWorkManage; - _DetailServices = iusers_DetailServices; - _TrojanUsers = trojanUsers; - _logger = logger; - } - public async Task Execute(IJobExecutionContext context) - { - //var param = context.MergedJobDataMap; - // 可以直接获取 JobDetail 的值 - var jobKey = context.JobDetail.Key; - var jobId = jobKey.Name; - var executeLog = await ExecuteJob(context, async () => await Run(context, jobId.ObjToInt())); - - } - public async Task Run(IJobExecutionContext context, int jobid) - { - if (jobid > 0) - { - try - { - //获取每月用户的数据 - _unitOfWorkManage.BeginTran(); - var now = DateTime.Now.AddMonths(-1); - - var list = await _TrojanUsers.Query(); - List ls = new List(); - foreach (var us in list) - { - TrojanDetails u = new TrojanDetails(); - u.calDate = now; - u.userId = us.id; - u.download = us.download; - u.upload = us.upload; - //清零 - us.download = 0; - us.upload = 0; - ls.Add(u); - } - await _TrojanUsers.Update(list); - await _DetailServices.Add(ls); - _unitOfWorkManage.CommitTran(); - } - catch (Exception) - { - _unitOfWorkManage.RollbackTran(); - throw; - } - } - } - } - - - -} From f062eb09831838fec80b069fbd7157d976c5db9b Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Wed, 3 May 2023 17:57:02 +0800 Subject: [PATCH 29/84] =?UTF-8?q?=E2=9C=A8=20=E4=BC=98=E5=8C=96Http?= =?UTF-8?q?=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Serilog/Utility/SerilogRequestUtility.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Blog.Core.Serilog/Utility/SerilogRequestUtility.cs b/Blog.Core.Serilog/Utility/SerilogRequestUtility.cs index 61b652dd..289f0aae 100644 --- a/Blog.Core.Serilog/Utility/SerilogRequestUtility.cs +++ b/Blog.Core.Serilog/Utility/SerilogRequestUtility.cs @@ -51,9 +51,15 @@ public static void EnrichFromRequest(IDiagnosticContext diagnosticContext, HttpC diagnosticContext.Set("Protocol", request.Protocol); diagnosticContext.Set("RequestIp", httpContext.GetRequestIp()); - diagnosticContext.Set("QueryString", request.QueryString.HasValue ? request.QueryString.Value : string.Empty); - diagnosticContext.Set("Body", request.ContentLength > 0 ? request.GetRequestBody() : string.Empty); - + if (request.Method == HttpMethods.Get) + { + diagnosticContext.Set("QueryString", request.QueryString.HasValue ? request.QueryString.Value : string.Empty); + } + else + { + diagnosticContext.Set("QueryString", request.QueryString.HasValue ? request.QueryString.Value : string.Empty); + diagnosticContext.Set("Body", request.ContentLength > 0 ? request.GetRequestBody() : string.Empty); + } diagnosticContext.Set("ContentType", httpContext.Response.ContentType); var endpoint = httpContext.GetEndpoint(); From 8662047671c771f5717e8c44aa65f11678c70f54 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Fri, 5 May 2023 18:11:24 +0800 Subject: [PATCH 30/84] feat: new user login --- Blog.Core.Common/HttpContextUser/AspNetUser.cs | 2 +- Blog.Core.Common/HttpContextUser/IUser.cs | 2 +- Blog.Core.Extensions/Authorizations/Helpers/JwtHelper.cs | 2 +- Blog.Core.Model/Models/Modules.cs | 4 ++-- Blog.Core.Model/Models/Permission.cs | 4 ++-- Blog.Core.Model/Models/Role.cs | 4 ++-- Blog.Core.Model/Models/RoleModulePermission.cs | 4 ++-- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Blog.Core.Common/HttpContextUser/AspNetUser.cs b/Blog.Core.Common/HttpContextUser/AspNetUser.cs index b37e4a24..590fda59 100644 --- a/Blog.Core.Common/HttpContextUser/AspNetUser.cs +++ b/Blog.Core.Common/HttpContextUser/AspNetUser.cs @@ -40,7 +40,7 @@ private string GetName() return ""; } - public int ID => GetClaimValueByType("jti").FirstOrDefault().ObjToInt(); + public long ID => GetClaimValueByType("jti").FirstOrDefault().ObjToLong(); public long TenantId => GetClaimValueByType("TenantId").FirstOrDefault().ObjToLong(); public bool IsAuthenticated() diff --git a/Blog.Core.Common/HttpContextUser/IUser.cs b/Blog.Core.Common/HttpContextUser/IUser.cs index 3849bd38..aa6094b1 100644 --- a/Blog.Core.Common/HttpContextUser/IUser.cs +++ b/Blog.Core.Common/HttpContextUser/IUser.cs @@ -7,7 +7,7 @@ namespace Blog.Core.Common.HttpContextUser public interface IUser { string Name { get; } - int ID { get; } + long ID { get; } long TenantId { get; } bool IsAuthenticated(); IEnumerable GetClaimsIdentity(); diff --git a/Blog.Core.Extensions/Authorizations/Helpers/JwtHelper.cs b/Blog.Core.Extensions/Authorizations/Helpers/JwtHelper.cs index 018f4c39..b9659029 100644 --- a/Blog.Core.Extensions/Authorizations/Helpers/JwtHelper.cs +++ b/Blog.Core.Extensions/Authorizations/Helpers/JwtHelper.cs @@ -90,7 +90,7 @@ public static TokenModelJwt SerializeJwt(string jwtStr) tokenModelJwt = new TokenModelJwt { - Uid = (jwtToken.Id).ObjToInt(), + Uid = (jwtToken.Id).ObjToLong(), Role = role != null ? role.ObjToString() : "", }; } diff --git a/Blog.Core.Model/Models/Modules.cs b/Blog.Core.Model/Models/Modules.cs index 6e41aaac..684cfcd0 100644 --- a/Blog.Core.Model/Models/Modules.cs +++ b/Blog.Core.Model/Models/Modules.cs @@ -78,7 +78,7 @@ public Modules() /// 创建ID /// [SugarColumn(IsNullable = true)] - public int? CreateId { get; set; } + public long? CreateId { get; set; } /// /// 创建者 /// @@ -93,7 +93,7 @@ public Modules() /// 修改ID /// [SugarColumn(IsNullable = true)] - public int? ModifyId { get; set; } + public long? ModifyId { get; set; } /// /// 修改者 /// diff --git a/Blog.Core.Model/Models/Permission.cs b/Blog.Core.Model/Models/Permission.cs index deece0c0..9dd6238d 100644 --- a/Blog.Core.Model/Models/Permission.cs +++ b/Blog.Core.Model/Models/Permission.cs @@ -69,7 +69,7 @@ public Permission() /// 创建ID /// [SugarColumn(IsNullable = true)] - public int? CreateId { get; set; } + public long? CreateId { get; set; } /// /// 创建者 /// @@ -84,7 +84,7 @@ public Permission() /// 修改ID /// [SugarColumn(IsNullable = true)] - public int? ModifyId { get; set; } + public long? ModifyId { get; set; } /// /// 修改者 /// diff --git a/Blog.Core.Model/Models/Role.cs b/Blog.Core.Model/Models/Role.cs index 1357afb0..0e65bcaf 100644 --- a/Blog.Core.Model/Models/Role.cs +++ b/Blog.Core.Model/Models/Role.cs @@ -64,7 +64,7 @@ public Role(string name) /// 创建ID /// [SugarColumn(IsNullable = true)] - public int? CreateId { get; set; } + public long? CreateId { get; set; } /// /// 创建者 /// @@ -79,7 +79,7 @@ public Role(string name) /// 修改ID /// [SugarColumn(IsNullable = true)] - public int? ModifyId { get; set; } + public long? ModifyId { get; set; } /// /// 修改者 /// diff --git a/Blog.Core.Model/Models/RoleModulePermission.cs b/Blog.Core.Model/Models/RoleModulePermission.cs index 482b9b4e..f33c1080 100644 --- a/Blog.Core.Model/Models/RoleModulePermission.cs +++ b/Blog.Core.Model/Models/RoleModulePermission.cs @@ -26,7 +26,7 @@ public RoleModulePermission() /// 创建ID /// [SugarColumn(IsNullable = true)] - public int? CreateId { get; set; } + public long? CreateId { get; set; } /// /// 创建者 /// @@ -41,7 +41,7 @@ public RoleModulePermission() /// 修改ID /// [SugarColumn(IsNullable = true)] - public int? ModifyId { get; set; } + public long? ModifyId { get; set; } /// /// 修改者 /// From 48a4adcd371864b3c533bdc981177e6909cfe0aa Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Sat, 6 May 2023 09:47:20 +0800 Subject: [PATCH 31/84] =?UTF-8?q?=E2=9C=A8=20=E4=BC=98=E5=8C=96Http?= =?UTF-8?q?=E6=97=A5=E5=BF=97=EF=BC=8C=E8=AE=B0=E5=BD=95RequestBody?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Common/Extensions/HttpRequestExtension.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Blog.Core.Common/Extensions/HttpRequestExtension.cs b/Blog.Core.Common/Extensions/HttpRequestExtension.cs index 7e87fd28..285a4a9e 100644 --- a/Blog.Core.Common/Extensions/HttpRequestExtension.cs +++ b/Blog.Core.Common/Extensions/HttpRequestExtension.cs @@ -8,6 +8,16 @@ public static class HttpRequestExtension { public static string GetRequestBody(this HttpRequest request) { + if (!request.Body.CanRead) + { + return default; + } + + if (!request.Body.CanSeek) + { + return default; + } + if (request.Body.Length < 1) { return default; From ddb326763b26ff5b540c310c8d83cb251b73dbb4 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Sat, 6 May 2023 10:58:18 +0800 Subject: [PATCH 32/84] feat: change file upload api --- Blog.Core.Api/Controllers/ImgController.cs | 6 +++--- Blog.Core.Model/ViewModels/UploadFileDto.cs | 7 +------ 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Blog.Core.Api/Controllers/ImgController.cs b/Blog.Core.Api/Controllers/ImgController.cs index 11d9d209..5ba85388 100644 --- a/Blog.Core.Api/Controllers/ImgController.cs +++ b/Blog.Core.Api/Controllers/ImgController.cs @@ -54,11 +54,11 @@ public FileStreamResult DownImg() public async Task> InsertPicture([FromForm]UploadFileDto dto) { - if (dto.Files == null || !dto.Files.Any()) return Failed("请选择上传的文件。"); + if (dto.file == null || !dto.file.Any()) return Failed("请选择上传的文件。"); //格式限制 var allowType = new string[] { "image/jpg", "image/png", "image/jpeg" }; - var allowedFile = dto.Files.Where(c => allowType.Contains(c.ContentType)); + var allowedFile = dto.file.Where(c => allowType.Contains(c.ContentType)); if (!allowedFile.Any()) return Failed("图片格式错误"); if (allowedFile.Sum(c => c.Length) > 1024 * 1024 * 4) return Failed("图片过大"); @@ -79,7 +79,7 @@ public async Task> InsertPicture([FromForm]UploadFileDto dt } } - var excludeFiles = dto.Files.Except(allowedFile); + var excludeFiles = dto.file.Except(allowedFile); if (excludeFiles.Any()) { diff --git a/Blog.Core.Model/ViewModels/UploadFileDto.cs b/Blog.Core.Model/ViewModels/UploadFileDto.cs index fc2b3cf7..dd0f51bb 100644 --- a/Blog.Core.Model/ViewModels/UploadFileDto.cs +++ b/Blog.Core.Model/ViewModels/UploadFileDto.cs @@ -1,10 +1,5 @@ using Microsoft.AspNetCore.Http; -using System; -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Blog.Core.Model.ViewModels { @@ -12,7 +7,7 @@ public class UploadFileDto { //多文件 [Required] - public IFormFileCollection Files { get; set; } + public IFormFileCollection file { get; set; } //单文件 //public IFormFile File { get; set; } From 8183be9d5888a8cd07f300e6e8d4a44e1dda117e Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Mon, 8 May 2023 16:55:41 +0800 Subject: [PATCH 33/84] feat: change maindb sort --- Blog.Core.Common/DB/BaseDBConfig.cs | 9 +++++++-- Blog.Core.Common/DB/TenantUtil.cs | 3 ++- Blog.Core.Common/Seed/DBSeed.cs | 6 +++--- Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs | 3 ++- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Blog.Core.Common/DB/BaseDBConfig.cs b/Blog.Core.Common/DB/BaseDBConfig.cs index 5761d91d..313b8a00 100644 --- a/Blog.Core.Common/DB/BaseDBConfig.cs +++ b/Blog.Core.Common/DB/BaseDBConfig.cs @@ -12,8 +12,8 @@ public class BaseDBConfig * 目前是多库操作,默认加载的是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 List AllConfig = new(); //所有的库连接 + public static List ValidConfig = new(); //有效的库连接(除去Log库) public static ConnectionConfig LogConfig; //日志库 private static string DifDBConnOfSecurity(params string[] conn) @@ -40,6 +40,11 @@ public static (List, List) MutiInitConn() { List listdatabase = AppSettings.app("DBS") .Where(i => i.Enabled).ToList(); + var mainDbId = AppSettings.app(new string[] { "MainDB" }).ObjToString(); + var mainDbModel = listdatabase.Single(d => d.ConnId == mainDbId); + listdatabase.Remove(mainDbModel); + listdatabase.Insert(0, mainDbModel); + foreach (var i in listdatabase) { SpecialDbString(i); diff --git a/Blog.Core.Common/DB/TenantUtil.cs b/Blog.Core.Common/DB/TenantUtil.cs index 8d57189b..8395c271 100644 --- a/Blog.Core.Common/DB/TenantUtil.cs +++ b/Blog.Core.Common/DB/TenantUtil.cs @@ -47,7 +47,8 @@ public static ConnectionConfig GetConnectionConfig(this SysTenant tenant) IsAutoCloseConnection = true, MoreSettings = new ConnMoreSettings() { - IsAutoRemoveDataCache = true + IsAutoRemoveDataCache = true, + SqlServerCodeFirstNvarchar = true, }, }; } diff --git a/Blog.Core.Common/Seed/DBSeed.cs b/Blog.Core.Common/Seed/DBSeed.cs index 000ff976..e7eb4e27 100644 --- a/Blog.Core.Common/Seed/DBSeed.cs +++ b/Blog.Core.Common/Seed/DBSeed.cs @@ -44,9 +44,9 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) Console.WriteLine($"Is multi-DataBase: {AppSettings.app(new string[] { "MutiDBEnabled" })}"); Console.WriteLine($"Is CQRS: {AppSettings.app(new string[] { "CQRSEnabled" })}"); Console.WriteLine(); - Console.WriteLine($"Master DB ConId: {MyContext.ConnId}"); - Console.WriteLine($"Master DB Type: {MyContext.DbType}"); - Console.WriteLine($"Master DB ConnectString: {MyContext.ConnectionString}"); + Console.WriteLine($"Master DB ConId: {myContext.Db.CurrentConnectionConfig.ConfigId}"); + Console.WriteLine($"Master DB Type: {myContext.Db.CurrentConnectionConfig.DbType}"); + Console.WriteLine($"Master DB ConnectString: {myContext.Db.CurrentConnectionConfig.ConnectionString}"); Console.WriteLine(); if (AppSettings.app(new string[] { "MutiDBEnabled" }).ObjToBool()) { diff --git a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs index 98dd6e24..83dff774 100644 --- a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs @@ -57,7 +57,8 @@ public static void AddSqlsugarSetup(this IServiceCollection services) MoreSettings = new ConnMoreSettings() { //IsWithNoLockQuery = true, - IsAutoRemoveDataCache = true + IsAutoRemoveDataCache = true, + SqlServerCodeFirstNvarchar = true, }, // 从库 SlaveConnectionConfigs = listConfig_Slave, 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 34/84] =?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 | 427 +++++++++--------- .../ServiceExtensions/SqlsugarSetup.cs | 248 +++++----- .../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, 822 insertions(+), 410 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 f3f9befa..2f8c41e9 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 79666d28..1eb3423b 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 b47bbf53..51ffc1f2 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 5a374ded..97a938ea 100644 --- a/Blog.Core.Api/Controllers/BaseApiController.cs +++ b/Blog.Core.Api/Controllers/BaseApiController.cs @@ -4,82 +4,85 @@ 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 00000000..434bd195 --- /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 0662bac5..aeba8476 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 313b8a00..495414fb 100644 --- a/Blog.Core.Common/DB/BaseDBConfig.cs +++ b/Blog.Core.Common/DB/BaseDBConfig.cs @@ -3,18 +3,23 @@ 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 @@ public static (List, List) MutiInitConn() 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 @@ public static (List, List) MutiInitConn() } 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 @@ public static (List, List) MutiInitConn() // 读写分离,且必须是单库模式,获取从库 - 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 00000000..f997a1eb --- /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 00000000..ffcf910c --- /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 121b9b10..23627ccd 100644 --- a/Blog.Core.Extensions/Authorizations/Policys/PermissionHandler.cs +++ b/Blog.Core.Extensions/Authorizations/Policys/PermissionHandler.cs @@ -14,212 +14,229 @@ 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; } - - 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; - } - - // 重写异步处理程序 - 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(); - } - - requirement.Permissions = list; - } - - 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 - }); - - // 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(); - - //result?.Principal不为空即登录成功 - if (result?.Principal != null || isTestCurrent) - { - if (!isTestCurrent) httpContext.User = result.Principal; - - // 获取当前用户的角色信息 - 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(); - } - - 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 (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; - } - - if (!isExp) - { - context.Fail(new AuthorizationFailureReason(this, "授权已过期,请重新授权")); - 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); - } - } + /// + /// 权限授权处理器 + /// + 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; + + /// + /// 构造函数注入 + /// + /// + /// + /// + /// + /// + 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; + + // 获取系统中所有的角色和菜单的关系集合 + 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; + } + + 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 + }); + + // 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(); + + //result?.Principal不为空即登录成功 + if (result?.Principal != null || isTestCurrent) + { + if (!isTestCurrent) httpContext.User = result.Principal; + + // 获取当前用户的角色信息 + 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.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 + } + } + + //验证权限 + 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; + } + + if (!isExp) + { + context.Fail(new AuthorizationFailureReason(this, "授权已过期,请重新授权")); + 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); + } + } } \ No newline at end of file diff --git a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs index 83dff774..4ac2d2e8 100644 --- a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs @@ -13,126 +13,130 @@ namespace Blog.Core.Extensions { - /// - /// 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)); - - // 默认添加主数据库连接 - MainDb.CurrentDbConnId = AppSettings.app(new string[] { "MainDB" }); - - // SqlSugarScope是线程安全,可使用单例注入 - // 参考:https://www.donet5.com/Home/Doc?typeId=1181 - services.AddSingleton(o => - { - var memoryCache = o.GetRequiredService(); - - // 从库 - 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 = 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.AllConfig.Add(config); - }); - - 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); - - // 打印SQL语句 - dbProvider.Aop.OnLogExecuting = (s, parameters) => SqlSugarAop.OnLogExecuting(dbProvider, s, parameters, config); - - // 数据审计 - dbProvider.Aop.DataExecuting = SqlSugarAop.DataExecuting; - - // 配置实体假删除过滤器 - RepositorySetting.SetDeletedEntityFilter(dbProvider); - // 配置实体数据权限 - RepositorySetting.SetTenantEntityFilter(dbProvider); - }); - }); - }); - } - - private static string GetWholeSql(SugarParameter[] paramArr, string sql) - { - foreach (var param in paramArr) - { - sql.Replace(param.ParameterName, param.Value.ObjToString()); - } - - return sql; - } - - private static string GetParas(SugarParameter[] pars) - { - string key = "【SQL参数】:"; - foreach (var param in pars) - { - key += $"{param.ParameterName}:{param.Value}\n"; - } - - return key; - } - } + /// + /// 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)); + + // 默认添加主数据库连接 + MainDb.CurrentDbConnId = AppSettings.app(new string[] {"MainDB"}); + + BaseDBConfig.MutiConnectionString.slaveDbs.ForEach(s => + { + BaseDBConfig.AllSlaveConfigs.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.AllConfigs.Add(config); + }); + + if (BaseDBConfig.LogConfig is null) + { + throw new ApplicationException("未配置Log库连接"); + } + + + // SqlSugarScope是线程安全,可使用单例注入 + // 参考:https://www.donet5.com/Home/Doc?typeId=1181 + services.AddSingleton(o => + { + var memoryCache = o.GetRequiredService(); + + foreach (var config in BaseDBConfig.AllConfigs) + { + config.ConfigureExternalServices.DataInfoCacheService = new SqlSugarMemoryCacheService(memoryCache); + } + + return new SqlSugarScope(BaseDBConfig.AllConfigs, db => + { + BaseDBConfig.ValidConfig.ForEach(config => + { + var dbProvider = db.GetConnectionScope((string) config.ConfigId); + + // 打印SQL语句 + dbProvider.Aop.OnLogExecuting = (s, parameters) => + SqlSugarAop.OnLogExecuting(dbProvider, s, parameters, config); + + // 数据审计 + dbProvider.Aop.DataExecuting = SqlSugarAop.DataExecuting; + + // 配置实体假删除过滤器 + RepositorySetting.SetDeletedEntityFilter(dbProvider); + // 配置实体数据权限 + RepositorySetting.SetTenantEntityFilter(dbProvider); + }); + }); + }); + } + + private static string GetWholeSql(SugarParameter[] paramArr, string sql) + { + foreach (var param in paramArr) + { + sql.Replace(param.ParameterName, param.Value.ObjToString()); + } + + 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 00000000..f2ae1ed0 --- /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 00000000..8cefaeb6 --- /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 00000000..6cada2ff --- /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 00000000..88c66973 --- /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 00000000..496a6dd2 --- /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 From a17d6eeb358317e77ed5219e6a0477131111919b Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Sat, 13 May 2023 12:20:47 +0800 Subject: [PATCH 35/84] Update DataBaseController.cs --- Blog.Core.Api/Controllers/Systems/DataBaseController.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Blog.Core.Api/Controllers/Systems/DataBaseController.cs b/Blog.Core.Api/Controllers/Systems/DataBaseController.cs index 434bd195..1f7b3089 100644 --- a/Blog.Core.Api/Controllers/Systems/DataBaseController.cs +++ b/Blog.Core.Api/Controllers/Systems/DataBaseController.cs @@ -29,7 +29,7 @@ public DataBaseController(ISqlSugarClient db) } [return: NotNull] - public ISqlSugarClient GetTenantDb(string configId) + private ISqlSugarClient GetTenantDb(string configId) { if (!_db.AsTenant().IsAnyConnection(configId)) { @@ -88,6 +88,8 @@ public MessageModel> GetTableInfoList(string configId, configId = MainDb.CurrentDbConnId; } + configId = configId.ToLower(); + var provider = GetTenantDb(configId); List data = null; switch (readType) @@ -129,7 +131,9 @@ public MessageModel> GetColumnInfosByTableName(string t configId = MainDb.CurrentDbConnId; } - List data = null; + configId = configId.ToLower(); + + List data = null; var provider = GetTenantDb(configId); switch (readType) { From ca0dda2d0471ab5e0e2d47cbb1b1f93012aaae9a Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Mon, 15 May 2023 14:48:20 +0800 Subject: [PATCH 36/84] Update appsettings.json --- Blog.Core.Api/appsettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Blog.Core.Api/appsettings.json b/Blog.Core.Api/appsettings.json index 0523d64d..cabddf53 100644 --- a/Blog.Core.Api/appsettings.json +++ b/Blog.Core.Api/appsettings.json @@ -105,7 +105,7 @@ "Connection": "WMBlog.db" //sqlite只写数据库名就行 }, { - "ConnId": "Log", + "ConnId": "Log", //日志库连接固定名称,不要改,其他的可以改 "DBType": 2, "Enabled": true, "HitRate": 50, // 值越大,优先级越高 From 42146e2a04999a37fab456c91f6c35965e81254a Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Mon, 15 May 2023 15:03:14 +0800 Subject: [PATCH 37/84] Update launchSettings.json --- Blog.Core.Api/Properties/launchSettings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Blog.Core.Api/Properties/launchSettings.json b/Blog.Core.Api/Properties/launchSettings.json index 425ec45f..e3113d39 100644 --- a/Blog.Core.Api/Properties/launchSettings.json +++ b/Blog.Core.Api/Properties/launchSettings.json @@ -13,8 +13,8 @@ "commandName": "Project", "launchBrowser": true, "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development", - "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "SkyAPM.Agent.AspNetCore" + "ASPNETCORE_ENVIRONMENT": "Development" + //"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "SkyAPM.Agent.AspNetCore"// 如果要开始skywalking,请取消此行注释 }, "applicationUrl": "http://localhost:9291" }, From 1e4e3ee52df856808a313f6a9acfa88503261f1b Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Fri, 19 May 2023 14:08:33 +0800 Subject: [PATCH 38/84] Update RoleModulePermission.tsv --- .../RoleModulePermission.tsv | 1391 +++++++++++++++++ 1 file changed, 1391 insertions(+) diff --git a/Blog.Core.Api/wwwroot/BlogCore.Data.json/RoleModulePermission.tsv b/Blog.Core.Api/wwwroot/BlogCore.Data.json/RoleModulePermission.tsv index eb727383..5626b81f 100644 --- a/Blog.Core.Api/wwwroot/BlogCore.Data.json/RoleModulePermission.tsv +++ b/Blog.Core.Api/wwwroot/BlogCore.Data.json/RoleModulePermission.tsv @@ -1703,5 +1703,1396 @@ "ModuleId": 72, "PermissionId": 122, "Id": 233 + }, + { + "Id": 1658115520798527489, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 1 + }, + { + "Id": 1658115520798527490, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2022-03-23 19:21:58", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 114 + }, + { + "Id": 1658115520798527491, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2022-03-23 19:21:58", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 66, + "PermissionId": 115 + }, + { + "Id": 1658115520798527492, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 2 + }, + { + "Id": 1658115520798527493, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 22, + "PermissionId": 3 + }, + { + "Id": 1658115520798527494, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 7, + "PermissionId": 4 + }, + { + "Id": 1658115520798527495, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 5 + }, + { + "Id": 1658115520798527496, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 13, + "PermissionId": 6 + }, + { + "Id": 1658115520798527497, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 17, + "PermissionId": 7 + }, + { + "Id": 1658115520798527498, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 34 + }, + { + "Id": 1658115520798527499, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 10 + }, + { + "Id": 1658115520798527500, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 11 + }, + { + "Id": 1658115520798527501, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 12 + }, + { + "Id": 1658115520798527502, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 29 + }, + { + "Id": 1658115520798527503, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 27, + "PermissionId": 43 + }, + { + "Id": 1658115520798527504, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 67 + }, + { + "Id": 1658115520798527505, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 37 + }, + { + "Id": 1658115520798527506, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 38 + }, + { + "Id": 1658115520798527507, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 39 + }, + { + "Id": 1658115520798527508, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 41 + }, + { + "Id": 1658115520798527509, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 40 + }, + { + "Id": 1658115520798527510, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 42 + }, + { + "Id": 1658115520798527511, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 26, + "PermissionId": 28 + }, + { + "Id": 1658115520798527512, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 44 + }, + { + "Id": 1658115520798527513, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 45 + }, + { + "Id": 1658115520798527514, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 46 + }, + { + "Id": 1658115520798527515, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 47 + }, + { + "Id": 1658115520798527516, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 48 + }, + { + "Id": 1658115520798527517, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 49 + }, + { + "Id": 1658115520798527518, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 65 + }, + { + "Id": 1658115520798527519, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 33, + "PermissionId": 66 + }, + { + "Id": 1658115520798527520, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 68 + }, + { + "Id": 1658115520798527521, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 69 + }, + { + "Id": 1658115520798527522, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 8 + }, + { + "Id": 1658115520798527523, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 75 + }, + { + "Id": 1658115520798527524, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 37, + "PermissionId": 76 + }, + { + "Id": 1658115520798527525, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 87 + }, + { + "Id": 1658115520798527526, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 88 + }, + { + "Id": 1658115520798527527, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 89 + }, + { + "Id": 1658115520798527528, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 90 + }, + { + "Id": 1658115520798527529, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 91 + }, + { + "Id": 1658115520798527530, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 95 + }, + { + "Id": 1658115520798527531, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 93 + }, + { + "Id": 1658115520798527532, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 94 + }, + { + "Id": 1658115520798527533, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 0, + "PermissionId": 92 + }, + { + "Id": 1658115520798527534, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 7, + "PermissionId": 9 + }, + { + "Id": 1658115520798527535, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 10, + "PermissionId": 13 + }, + { + "Id": 1658115520798527536, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 12, + "PermissionId": 14 + }, + { + "Id": 1658115520798527537, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 11, + "PermissionId": 15 + }, + { + "Id": 1658115520798527538, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 22, + "PermissionId": 16 + }, + { + "Id": 1658115520798527539, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 25, + "PermissionId": 17 + }, + { + "Id": 1658115520798527540, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 24, + "PermissionId": 18 + }, + { + "Id": 1658115520798527541, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 23, + "PermissionId": 19 + }, + { + "Id": 1658115520798527542, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 13, + "PermissionId": 20 + }, + { + "Id": 1658115520798527543, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 16, + "PermissionId": 21 + }, + { + "Id": 1658115520798527544, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 15, + "PermissionId": 22 + }, + { + "Id": 1658115520798527545, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 14, + "PermissionId": 23 + }, + { + "Id": 1658115520798527546, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 17, + "PermissionId": 24 + }, + { + "Id": 1658115520798527547, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 20, + "PermissionId": 25 + }, + { + "Id": 1658115520798527548, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 19, + "PermissionId": 26 + }, + { + "Id": 1658115520798527549, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 18, + "PermissionId": 27 + }, + { + "Id": 1658115520798527550, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 5, + "PermissionId": 30 + }, + { + "Id": 1658115520798527551, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 6, + "PermissionId": 31 + }, + { + "Id": 1658115520798527552, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 26, + "PermissionId": 32 + }, + { + "Id": 1658115520798527553, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 21, + "PermissionId": 33 + }, + { + "Id": 1658115520798527554, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 28, + "PermissionId": 35 + }, + { + "Id": 1658115520798527555, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 29, + "PermissionId": 36 + }, + { + "Id": 1658115520798527556, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2022-03-23 19:21:58", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 66, + "PermissionId": 116 + }, + { + "Id": 1658115520798527557, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2022-03-23 19:21:58", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 69, + "PermissionId": 117 + }, + { + "Id": 1658115520798527558, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2022-03-23 19:21:58", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 68, + "PermissionId": 118 + }, + { + "Id": 1658115520798527559, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2022-03-23 19:21:58", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 67, + "PermissionId": 119 + }, + { + "Id": 1658115520798527560, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2022-03-23 19:21:58", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 70, + "PermissionId": 120 + }, + { + "Id": 1658115520798527561, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 32, + "PermissionId": 64 + }, + { + "Id": 1658115520798527562, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 36, + "PermissionId": 72 + }, + { + "Id": 1658115520798527563, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 27, + "PermissionId": 73 + }, + { + "Id": 1658115520798527564, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 27, + "PermissionId": 74 + }, + { + "Id": 1658115520798527565, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2019-01-01 00:00:00", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 71, + "PermissionId": 121 + }, + { + "Id": 1658115520798527566, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2022-04-11 16:08:49", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 72, + "PermissionId": 122 + }, + { + "Id": 1658115520798527567, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 37, + "PermissionId": 77 + }, + { + "Id": 1658115520798527568, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 38, + "PermissionId": 78 + }, + { + "Id": 1658115520798527569, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 39, + "PermissionId": 79 + }, + { + "Id": 1658115520798527570, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 40, + "PermissionId": 80 + }, + { + "Id": 1658115520798527571, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 41, + "PermissionId": 81 + }, + { + "Id": 1658115520798527572, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 42, + "PermissionId": 82 + }, + { + "Id": 1658115520798527573, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 43, + "PermissionId": 83 + }, + { + "Id": 1658115520798527574, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 44, + "PermissionId": 84 + }, + { + "Id": 1658115520798527575, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 45, + "PermissionId": 85 + }, + { + "Id": 1658115520798527576, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 46, + "PermissionId": 86 + }, + { + "Id": 1658115520798527577, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 73, + "PermissionId": 123 + }, + { + "Id": 1658115520798527578, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 74, + "PermissionId": 124 + }, + { + "Id": 1658115520798527579, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 55, + "PermissionId": 108 + }, + { + "Id": 1658115520798527580, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 61, + "PermissionId": 109 + }, + { + "Id": 1658115520798527581, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 50, + "PermissionId": 103 + }, + { + "Id": 1658115520798527582, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 49, + "PermissionId": 104 + }, + { + "Id": 1658115520798527583, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 48, + "PermissionId": 105 + }, + { + "Id": 1658115520798527584, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 51, + "PermissionId": 106 + }, + { + "Id": 1658115520798527585, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 52, + "PermissionId": 107 + }, + { + "Id": 1658115520798527586, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 54, + "PermissionId": 96 + }, + { + "Id": 1658115520798527587, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 64, + "PermissionId": 98 + }, + { + "Id": 1658115520798527588, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 59, + "PermissionId": 99 + }, + { + "Id": 1658115520798527589, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 58, + "PermissionId": 100 + }, + { + "Id": 1658115520798527590, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 53, + "PermissionId": 101 + }, + { + "Id": 1658115520798527591, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 60, + "PermissionId": 102 + }, + { + "Id": 1658115520798527592, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 57, + "PermissionId": 110 + }, + { + "Id": 1658115520798527593, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 56, + "PermissionId": 112 + }, + { + "Id": 1658115520798527594, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 62, + "PermissionId": 111 + }, + { + "Id": 1658115520798527595, + "IsDeleted": 0, + "CreateId": 12, + "CreateBy": "blogadmin", + "CreateTime": "2023-05-15 14:20:12", + "ModifyId": 12, + "ModifyBy": "blogadmin", + "ModifyTime": "2023-05-15 14:20:12", + "RoleId": 4, + "ModuleId": 63, + "PermissionId": 113 } ] From f0bbf06c4725bd29abdf047ce199aab03367c574 Mon Sep 17 00:00:00 2001 From: ansonzhang <3143422472@qq.com> Date: Sun, 21 May 2023 18:22:28 +0800 Subject: [PATCH 39/84] Update SqlsugarAop.cs --- Blog.Core.Common/DB/Aop/SqlsugarAop.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Blog.Core.Common/DB/Aop/SqlsugarAop.cs b/Blog.Core.Common/DB/Aop/SqlsugarAop.cs index 369fc482..3a595e67 100644 --- a/Blog.Core.Common/DB/Aop/SqlsugarAop.cs +++ b/Blog.Core.Common/DB/Aop/SqlsugarAop.cs @@ -113,7 +113,7 @@ public static void DataExecuting(object oldValue, DataFilterModel entityInfo) if (App.User?.ID > 0 && dyCreateId != null && dyCreateId.GetValue(entityInfo.EntityValue) == null) dyCreateId.SetValue(entityInfo.EntityValue, App.User.ID); - if (dyCreateTime != null && (DateTime)dyCreateTime.GetValue(entityInfo.EntityValue) == DateTime.MinValue) + if (dyCreateTime != null && dyCreateTime.GetValue(entityInfo.EntityValue) != null && (DateTime)dyCreateTime.GetValue(entityInfo.EntityValue) == DateTime.MinValue) dyCreateTime.SetValue(entityInfo.EntityValue, DateTime.Now); break; @@ -155,4 +155,4 @@ private static string GetParas(SugarParameter[] pars) return key; } -} \ No newline at end of file +} From a979d3646129fd2009517f6f98c7da85e11c3994 Mon Sep 17 00:00:00 2001 From: ansonzhang <3143422472@qq.com> Date: Sun, 21 May 2023 19:23:01 +0800 Subject: [PATCH 40/84] Update NumberConverter.cs --- Blog.Core.Common/Helper/NumberConverter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Blog.Core.Common/Helper/NumberConverter.cs b/Blog.Core.Common/Helper/NumberConverter.cs index 27890faf..4232a75f 100644 --- a/Blog.Core.Common/Helper/NumberConverter.cs +++ b/Blog.Core.Common/Helper/NumberConverter.cs @@ -72,7 +72,7 @@ public override bool CanConvert(Type objectType) /// 对象值。 public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { - return AsType(reader.Value.ToString(), objectType); + return AsType(reader.Value.ObjToString(), objectType); } /// From 0cea9672b5f1ed8ae8ac1bbca221a127be35d2cd Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Wed, 24 May 2023 11:19:36 +0800 Subject: [PATCH 41/84] =?UTF-8?q?=E2=9C=A8=20=E4=BC=98=E5=8C=96Swagger?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.swagger登录可以用用户账号登录,如果登录成功 token存在session中 之前默认admin感觉没什么用 当然也可以扩展User 加个字段是否开发者帐户等类似的 2.优化权限校验 优先读取Header->没有读取Session 中token解析用户 --- Blog.Core.Api/Blog.Core.xml | 2 +- Blog.Core.Api/Controllers/LoginController.cs | 621 +++++++++--------- Blog.Core.Api/appsettings.json | 2 +- Blog.Core.Api/wwwroot/swg-login.html | 225 ++++--- .../Extensions/HttpContextExtension.cs | 19 + .../HttpContextUser/AspNetUser.cs | 41 +- .../Swagger/SwaggerContextExtension.cs | 48 ++ .../Policys/PermissionHandler.cs | 120 ++-- .../Middlewares/SwaggerAuthMiddleware.cs | 5 +- Blog.Core.Model/Models/sysUserInfo.cs | 271 ++++---- .../Controller_Test/LoginController_Should.cs | 136 ++-- 11 files changed, 833 insertions(+), 657 deletions(-) create mode 100644 Blog.Core.Common/Extensions/HttpContextExtension.cs create mode 100644 Blog.Core.Common/Swagger/SwaggerContextExtension.cs diff --git a/Blog.Core.Api/Blog.Core.xml b/Blog.Core.Api/Blog.Core.xml index 51ffc1f2..672ea21f 100644 --- a/Blog.Core.Api/Blog.Core.xml +++ b/Blog.Core.Api/Blog.Core.xml @@ -177,7 +177,7 @@ 登录管理【无权限】 - + 构造函数注入 diff --git a/Blog.Core.Api/Controllers/LoginController.cs b/Blog.Core.Api/Controllers/LoginController.cs index 87f5c0c9..8313507d 100644 --- a/Blog.Core.Api/Controllers/LoginController.cs +++ b/Blog.Core.Api/Controllers/LoginController.cs @@ -9,307 +9,330 @@ using Microsoft.AspNetCore.Mvc; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; +using Blog.Core.Common.Swagger; +using Serilog; namespace Blog.Core.Controllers { - /// - /// 登录管理【无权限】 - /// - [Produces("application/json")] - [Route("api/Login")] - [AllowAnonymous] - public class LoginController : BaseApiController - { - readonly ISysUserInfoServices _sysUserInfoServices; - readonly IUserRoleServices _userRoleServices; - readonly IRoleServices _roleServices; - readonly PermissionRequirement _requirement; - private readonly IRoleModulePermissionServices _roleModulePermissionServices; - - - /// - /// 构造函数注入 - /// - /// - /// - /// - /// - /// - public LoginController(ISysUserInfoServices sysUserInfoServices, IUserRoleServices userRoleServices, IRoleServices roleServices, PermissionRequirement requirement, IRoleModulePermissionServices roleModulePermissionServices) - { - this._sysUserInfoServices = sysUserInfoServices; - this._userRoleServices = userRoleServices; - this._roleServices = roleServices; - _requirement = requirement; - _roleModulePermissionServices = roleModulePermissionServices; - } - - - #region 获取token的第1种方法 - - /// - /// 获取JWT的方法1 - /// - /// - /// - /// - [HttpGet] - [Route("Token")] - public async Task> GetJwtStr(string name, string pass) - { - string jwtStr = string.Empty; - bool suc = false; - //这里就是用户登陆以后,通过数据库去调取数据,分配权限的操作 - - var user = await _sysUserInfoServices.GetUserRoleNameStr(name, MD5Helper.MD5Encrypt32(pass)); - if (user != null) - { - TokenModelJwt tokenModel = new TokenModelJwt { Uid = 1, Role = user }; - - jwtStr = JwtHelper.IssueJwt(tokenModel); - suc = true; - } - else - { - jwtStr = "login fail!!!"; - } - - return new MessageModel() - { - success = suc, - msg = suc ? "获取成功" : "获取失败", - response = jwtStr - }; - } - - - /// - /// 获取JWT的方法2:给Nuxt提供 - /// - /// - /// - /// - [HttpGet] - [Route("GetTokenNuxt")] - public MessageModel GetJwtStrForNuxt(string name, string pass) - { - string jwtStr = string.Empty; - bool suc = false; - //这里就是用户登陆以后,通过数据库去调取数据,分配权限的操作 - //这里直接写死了 - if (name == "admins" && pass == "admins") - { - TokenModelJwt tokenModel = new TokenModelJwt - { - Uid = 1, - Role = "Admin" - }; - - jwtStr = JwtHelper.IssueJwt(tokenModel); - suc = true; - } - else - { - jwtStr = "login fail!!!"; - } - - var result = new - { - data = new { success = suc, token = jwtStr } - }; - - return new MessageModel() - { - success = suc, - msg = suc ? "获取成功" : "获取失败", - response = jwtStr - }; - } - - #endregion - - - /// - /// 获取JWT的方法3:整个系统主要方法 - /// - /// - /// - /// - [HttpGet] - [Route("JWTToken3.0")] - public async Task> GetJwtToken3(string name = "", string pass = "") - { - string jwtStr = string.Empty; - - if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(pass)) - return Failed("用户名或密码不能为空"); - - pass = MD5Helper.MD5Encrypt32(pass); - - var user = await _sysUserInfoServices.Query(d => d.LoginName == name && d.LoginPWD == pass && d.IsDeleted == false); - if (user.Count > 0) - { - var userRoles = await _sysUserInfoServices.GetUserRoleNameStr(name, pass); - //如果是基于用户的授权策略,这里要添加用户;如果是基于角色的授权策略,这里要添加角色 - var claims = new List - { - new Claim(ClaimTypes.Name, name), - new Claim(JwtRegisteredClaimNames.Jti, user.FirstOrDefault().Id.ToString()), - new Claim("TenantId", user.FirstOrDefault().TenantId.ToString()), - new Claim(JwtRegisteredClaimNames.Iat, DateTime.Now.ToString()), - new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(_requirement.Expiration.TotalSeconds).ToString()) - }; - claims.AddRange(userRoles.Split(',').Select(s => new Claim(ClaimTypes.Role, s))); - - - // ids4和jwt切换 - // jwt - if (!Permissions.IsUseIds4) - { - var data = await _roleModulePermissionServices.RoleModuleMaps(); - var 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; - } - - var token = JwtToken.BuildJwtToken(claims.ToArray(), _requirement); - return Success(token, "获取成功"); - } - else - { - return Failed("认证失败"); - } - } - - /// - /// 请求刷新Token(以旧换新) - /// - /// - /// - [HttpGet] - [Route("RefreshToken")] - public async Task> RefreshToken(string token = "") - { - string jwtStr = string.Empty; - - if (string.IsNullOrEmpty(token)) - return Failed("token无效,请重新登录!"); - var tokenModel = JwtHelper.SerializeJwt(token); - if (tokenModel != null && JwtHelper.customSafeVerify(token) && tokenModel.Uid > 0) - { - var user = await _sysUserInfoServices.QueryById(tokenModel.Uid); - var value = User.Claims.SingleOrDefault(s => s.Type == JwtRegisteredClaimNames.Iat)?.Value; - if (value != null && user.CriticalModifyTime > value.ObjToDate()) - { - return Failed("很抱歉,授权已失效,请重新授权!"); - } - - if (user != null && !(value != null && user.CriticalModifyTime > value.ObjToDate())) - { - var userRoles = await _sysUserInfoServices.GetUserRoleNameStr(user.LoginName, user.LoginPWD); - //如果是基于用户的授权策略,这里要添加用户;如果是基于角色的授权策略,这里要添加角色 - var claims = new List - { - new Claim(ClaimTypes.Name, user.LoginName), - new Claim(JwtRegisteredClaimNames.Jti, tokenModel.Uid.ObjToString()), - new Claim(JwtRegisteredClaimNames.Iat, DateTime.Now.ToString()), - new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(_requirement.Expiration.TotalSeconds).ToString()) - }; - claims.AddRange(userRoles.Split(',').Select(s => new Claim(ClaimTypes.Role, s))); - - //用户标识 - var identity = new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme); - identity.AddClaims(claims); - - var refreshToken = JwtToken.BuildJwtToken(claims.ToArray(), _requirement); - return Success(refreshToken, "获取成功"); - } - } - - return Failed("认证失败!"); - } - - /// - /// 获取JWT的方法4:给 JSONP 测试 - /// - /// - /// - /// - /// - /// - /// - [HttpGet] - [Route("jsonp")] - public void Getjsonp(string callBack, long id = 1, string sub = "Admin", int expiresSliding = 30, int expiresAbsoulute = 30) - { - TokenModelJwt tokenModel = new TokenModelJwt - { - Uid = id, - Role = sub - }; - - string jwtStr = JwtHelper.IssueJwt(tokenModel); - - string response = string.Format("\"value\":\"{0}\"", jwtStr); - string call = callBack + "({" + response + "})"; - Response.WriteAsync(call); - } - - - /// - /// 测试 MD5 加密字符串 - /// - /// - /// - [HttpGet] - [Route("Md5Password")] - public string Md5Password(string password = "") - { - return MD5Helper.MD5Encrypt32(password); - } - - /// - /// swagger登录 - /// - /// - /// - [HttpPost] - [Route("/api/Login/swgLogin")] - public dynamic SwgLogin([FromBody] SwaggerLoginRequest loginRequest) - { - // 这里可以查询数据库等各种校验 - if (loginRequest?.name == "admin" && loginRequest?.pwd == "admin") - { - HttpContext.Session.SetString("swagger-code", "success"); - return new { result = true }; - } - - return new { result = false }; - } - - /// - /// weixin登录 - /// - /// - [HttpGet] - [Route("wxLogin")] - public dynamic WxLogin(string g = "", string token = "") - { - return new { g, token }; - } - } - - public class SwaggerLoginRequest - { - public string name { get; set; } - public string pwd { get; set; } - } + /// + /// 登录管理【无权限】 + /// + [Produces("application/json")] + [Route("api/Login")] + [AllowAnonymous] + public class LoginController : BaseApiController + { + readonly ISysUserInfoServices _sysUserInfoServices; + readonly IUserRoleServices _userRoleServices; + readonly IRoleServices _roleServices; + readonly PermissionRequirement _requirement; + private readonly IRoleModulePermissionServices _roleModulePermissionServices; + private readonly ILogger _logger; + + /// + /// 构造函数注入 + /// + /// + /// + /// + /// + /// + public LoginController(ISysUserInfoServices sysUserInfoServices, IUserRoleServices userRoleServices, + IRoleServices roleServices, PermissionRequirement requirement, + IRoleModulePermissionServices roleModulePermissionServices, ILogger logger) + { + this._sysUserInfoServices = sysUserInfoServices; + this._userRoleServices = userRoleServices; + this._roleServices = roleServices; + _requirement = requirement; + _roleModulePermissionServices = roleModulePermissionServices; + _logger = logger; + } + + + #region 获取token的第1种方法 + + /// + /// 获取JWT的方法1 + /// + /// + /// + /// + [HttpGet] + [Route("Token")] + public async Task> GetJwtStr(string name, string pass) + { + string jwtStr = string.Empty; + bool suc = false; + //这里就是用户登陆以后,通过数据库去调取数据,分配权限的操作 + + var user = await _sysUserInfoServices.GetUserRoleNameStr(name, MD5Helper.MD5Encrypt32(pass)); + if (user != null) + { + TokenModelJwt tokenModel = new TokenModelJwt {Uid = 1, Role = user}; + + jwtStr = JwtHelper.IssueJwt(tokenModel); + suc = true; + } + else + { + jwtStr = "login fail!!!"; + } + + return new MessageModel() + { + success = suc, + msg = suc ? "获取成功" : "获取失败", + response = jwtStr + }; + } + + + /// + /// 获取JWT的方法2:给Nuxt提供 + /// + /// + /// + /// + [HttpGet] + [Route("GetTokenNuxt")] + public MessageModel GetJwtStrForNuxt(string name, string pass) + { + string jwtStr = string.Empty; + bool suc = false; + //这里就是用户登陆以后,通过数据库去调取数据,分配权限的操作 + //这里直接写死了 + if (name == "admins" && pass == "admins") + { + TokenModelJwt tokenModel = new TokenModelJwt + { + Uid = 1, + Role = "Admin" + }; + + jwtStr = JwtHelper.IssueJwt(tokenModel); + suc = true; + } + else + { + jwtStr = "login fail!!!"; + } + + var result = new + { + data = new {success = suc, token = jwtStr} + }; + + return new MessageModel() + { + success = suc, + msg = suc ? "获取成功" : "获取失败", + response = jwtStr + }; + } + + #endregion + + + /// + /// 获取JWT的方法3:整个系统主要方法 + /// + /// + /// + /// + [HttpGet] + [Route("JWTToken3.0")] + public async Task> GetJwtToken3(string name = "", string pass = "") + + { + string jwtStr = string.Empty; + + if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(pass)) + return Failed("用户名或密码不能为空"); + + pass = MD5Helper.MD5Encrypt32(pass); + + var user = await _sysUserInfoServices.Query(d => + d.LoginName == name && d.LoginPWD == pass && d.IsDeleted == false); + if (user.Count > 0) + { + var userRoles = await _sysUserInfoServices.GetUserRoleNameStr(name, pass); + //如果是基于用户的授权策略,这里要添加用户;如果是基于角色的授权策略,这里要添加角色 + var claims = new List + { + new Claim(ClaimTypes.Name, name), + new Claim(JwtRegisteredClaimNames.Jti, user.FirstOrDefault().Id.ToString()), + new Claim("TenantId", user.FirstOrDefault().TenantId.ToString()), + new Claim(JwtRegisteredClaimNames.Iat, DateTime.Now.ToString()), + new Claim(ClaimTypes.Expiration, + DateTime.Now.AddSeconds(_requirement.Expiration.TotalSeconds).ToString()) + }; + claims.AddRange(userRoles.Split(',').Select(s => new Claim(ClaimTypes.Role, s))); + + + // ids4和jwt切换 + // jwt + if (!Permissions.IsUseIds4) + { + var data = await _roleModulePermissionServices.RoleModuleMaps(); + var 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; + } + + var token = JwtToken.BuildJwtToken(claims.ToArray(), _requirement); + return Success(token, "获取成功"); + } + else + { + return Failed("认证失败"); + } + } + + /// + /// 请求刷新Token(以旧换新) + /// + /// + /// + [HttpGet] + [Route("RefreshToken")] + public async Task> RefreshToken(string token = "") + { + string jwtStr = string.Empty; + + if (string.IsNullOrEmpty(token)) + return Failed("token无效,请重新登录!"); + var tokenModel = JwtHelper.SerializeJwt(token); + if (tokenModel != null && JwtHelper.customSafeVerify(token) && tokenModel.Uid > 0) + { + var user = await _sysUserInfoServices.QueryById(tokenModel.Uid); + var value = User.Claims.SingleOrDefault(s => s.Type == JwtRegisteredClaimNames.Iat)?.Value; + if (value != null && user.CriticalModifyTime > value.ObjToDate()) + { + return Failed("很抱歉,授权已失效,请重新授权!"); + } + + if (user != null && !(value != null && user.CriticalModifyTime > value.ObjToDate())) + { + var userRoles = await _sysUserInfoServices.GetUserRoleNameStr(user.LoginName, user.LoginPWD); + //如果是基于用户的授权策略,这里要添加用户;如果是基于角色的授权策略,这里要添加角色 + var claims = new List + { + new Claim(ClaimTypes.Name, user.LoginName), + new Claim(JwtRegisteredClaimNames.Jti, tokenModel.Uid.ObjToString()), + new Claim(JwtRegisteredClaimNames.Iat, DateTime.Now.ToString()), + new Claim(ClaimTypes.Expiration, + DateTime.Now.AddSeconds(_requirement.Expiration.TotalSeconds).ToString()) + }; + claims.AddRange(userRoles.Split(',').Select(s => new Claim(ClaimTypes.Role, s))); + + //用户标识 + var identity = new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme); + identity.AddClaims(claims); + + var refreshToken = JwtToken.BuildJwtToken(claims.ToArray(), _requirement); + return Success(refreshToken, "获取成功"); + } + } + + return Failed("认证失败!"); + } + + /// + /// 获取JWT的方法4:给 JSONP 测试 + /// + /// + /// + /// + /// + /// + /// + [HttpGet] + [Route("jsonp")] + public void Getjsonp(string callBack, long id = 1, string sub = "Admin", int expiresSliding = 30, + int expiresAbsoulute = 30) + { + TokenModelJwt tokenModel = new TokenModelJwt + { + Uid = id, + Role = sub + }; + + string jwtStr = JwtHelper.IssueJwt(tokenModel); + + string response = string.Format("\"value\":\"{0}\"", jwtStr); + string call = callBack + "({" + response + "})"; + Response.WriteAsync(call); + } + + + /// + /// 测试 MD5 加密字符串 + /// + /// + /// + [HttpGet] + [Route("Md5Password")] + public string Md5Password(string password = "") + { + return MD5Helper.MD5Encrypt32(password); + } + + /// + /// swagger登录 + /// + /// + /// + [HttpPost] + [Route("/api/Login/swgLogin")] + public async Task SwgLogin([FromBody] SwaggerLoginRequest loginRequest) + { + if (loginRequest is null) + { + return new {result = false}; + } + + try + { + var result = await GetJwtToken3(loginRequest.name, loginRequest.pwd); + if (result.success) + { + HttpContext.SuccessSwagger(); + HttpContext.SuccessSwaggerJwt(result.response.token); + return new {result = true}; + } + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Swagger登录异常"); + } + + return new {result = false}; + } + + /// + /// weixin登录 + /// + /// + [HttpGet] + [Route("wxLogin")] + public dynamic WxLogin(string g = "", string token = "") + { + return new {g, token}; + } + } + + public class SwaggerLoginRequest + { + public string name { get; set; } + public string pwd { get; set; } + } } \ No newline at end of file diff --git a/Blog.Core.Api/appsettings.json b/Blog.Core.Api/appsettings.json index cabddf53..cef87bed 100644 --- a/Blog.Core.Api/appsettings.json +++ b/Blog.Core.Api/appsettings.json @@ -47,7 +47,7 @@ }, "LogToDb": true, "LogAOP": { - "Enabled": true, + "Enabled": false, "LogToFile": { "Enabled": true }, diff --git a/Blog.Core.Api/wwwroot/swg-login.html b/Blog.Core.Api/wwwroot/swg-login.html index 022fec49..93653957 100644 --- a/Blog.Core.Api/wwwroot/swg-login.html +++ b/Blog.Core.Api/wwwroot/swg-login.html @@ -1,128 +1,141 @@  - + 登录 - 接口文档 - + -
-
-
-
+
+
+
+
- -
欢迎使用!
-
用户名:admin,密码:admin
-
-
- - - - - - - - - -
- - - - - -
+ +
欢迎使用!
+
使用用户账号登录
+
+
+ + + + + + + + + +
+ + + + +
+
- + } + }); + } + \ No newline at end of file diff --git a/Blog.Core.Common/Extensions/HttpContextExtension.cs b/Blog.Core.Common/Extensions/HttpContextExtension.cs new file mode 100644 index 00000000..0de018a0 --- /dev/null +++ b/Blog.Core.Common/Extensions/HttpContextExtension.cs @@ -0,0 +1,19 @@ +using System; +using Microsoft.AspNetCore.Http; + +namespace Blog.Core.Common.Extensions; + +public static class HttpContextExtension +{ + public static ISession GetSession(this HttpContext context) + { + try + { + return context.Session; + } + catch (Exception) + { + return default; + } + } +} \ No newline at end of file diff --git a/Blog.Core.Common/HttpContextUser/AspNetUser.cs b/Blog.Core.Common/HttpContextUser/AspNetUser.cs index 590fda59..ba21889e 100644 --- a/Blog.Core.Common/HttpContextUser/AspNetUser.cs +++ b/Blog.Core.Common/HttpContextUser/AspNetUser.cs @@ -1,7 +1,9 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; +using Blog.Core.Common.Swagger; using Blog.Core.Model; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; @@ -51,7 +53,25 @@ public bool IsAuthenticated() public string GetToken() { - return _accessor.HttpContext?.Request?.Headers["Authorization"].ObjToString().Replace("Bearer ", ""); + var token = _accessor.HttpContext?.Request?.Headers["Authorization"].ObjToString().Replace("Bearer ", ""); + if (!token.IsNullOrEmpty()) + { + return token; + } + + if (_accessor.HttpContext?.IsSuccessSwagger() == true) + { + token = _accessor.HttpContext.GetSuccessSwaggerJwt(); + if (token.IsNotEmptyOrNull()) + { + var claims = new ClaimsIdentity(GetClaimsIdentity(token)); + _accessor.HttpContext.User.AddIdentity(claims); + + return token; + } + } + + return token; } public List GetUserInfoFromToken(string ClaimType) @@ -77,6 +97,10 @@ public List GetUserInfoFromToken(string ClaimType) public IEnumerable GetClaimsIdentity() { + if (_accessor.HttpContext == null) return ArraySegment.Empty; + + if (!IsAuthenticated()) return GetClaimsIdentity(GetToken()); + var claims = _accessor.HttpContext.User.Claims.ToList(); var headers = _accessor.HttpContext.Request.Headers; foreach (var header in headers) @@ -86,6 +110,19 @@ public IEnumerable GetClaimsIdentity() return claims; } + public IEnumerable GetClaimsIdentity(string token) + { + var jwtHandler = new JwtSecurityTokenHandler(); + // token校验 + if (token.IsNotEmptyOrNull() && jwtHandler.CanReadToken(token)) + { + var jwtToken = jwtHandler.ReadJwtToken(token); + + return jwtToken.Claims; + } + + return new List(); + } public List GetClaimValueByType(string ClaimType) { diff --git a/Blog.Core.Common/Swagger/SwaggerContextExtension.cs b/Blog.Core.Common/Swagger/SwaggerContextExtension.cs new file mode 100644 index 00000000..ad89344d --- /dev/null +++ b/Blog.Core.Common/Swagger/SwaggerContextExtension.cs @@ -0,0 +1,48 @@ +using Blog.Core.Common.Extensions; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Extensions; + +namespace Blog.Core.Common.Swagger; + +public static class SwaggerContextExtension +{ + public const string SwaggerCodeKey = "swagger-code"; + public const string SwaggerJwt = "swagger-jwt"; + + public static bool IsSuccessSwagger() + { + return App.HttpContext?.GetSession()?.GetString(SwaggerCodeKey) == "success"; + } + + public static bool IsSuccessSwagger(this HttpContext context) + { + return context.GetSession()?.GetString(SwaggerCodeKey) == "success"; + } + + public static void SuccessSwagger() + { + App.HttpContext?.GetSession()?.SetString(SwaggerCodeKey, "success"); + } + + public static void SuccessSwagger(this HttpContext context) + { + context.GetSession()?.SetString(SwaggerCodeKey, "success"); + } + + public static void SuccessSwaggerJwt(this HttpContext context, string token) + { + context.GetSession()?.SetString(SwaggerJwt, token); + } + + public static string GetSuccessSwaggerJwt(this HttpContext context) + { + return context.GetSession()?.GetString(SwaggerJwt); + } + + + public static void RedirectSwaggerLogin(this HttpContext context) + { + var returnUrl = context.Request.GetDisplayUrl(); //获取当前url地址 + context.Response.Redirect("/swg-login.html?returnUrl=" + returnUrl); + } +} \ 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 23627ccd..d2dbfdf7 100644 --- a/Blog.Core.Extensions/Authorizations/Policys/PermissionHandler.cs +++ b/Blog.Core.Extensions/Authorizations/Policys/PermissionHandler.cs @@ -14,6 +14,7 @@ using System.Security.Claims; using System.Text.RegularExpressions; using System.Threading.Tasks; +using Blog.Core.Common.Swagger; using Blog.Core.Model.Models; namespace Blog.Core.AuthHelper @@ -116,7 +117,7 @@ orderby item.Id return; } } - + //判断请求是否拥有凭据,即有没有登录 var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync(); if (defaultAuthenticate != null) @@ -127,10 +128,79 @@ orderby item.Id var isTestCurrent = AppSettings.app(new string[] {"AppSettings", "UseLoadTest"}).ObjToBool(); //result?.Principal不为空即登录成功 - if (result?.Principal != null || isTestCurrent) + if (result?.Principal != null || isTestCurrent || httpContext.IsSuccessSwagger()) { if (!isTestCurrent) httpContext.User = result.Principal; + //应该要先校验用户的信息 再校验菜单权限相关的 + + //校验用户 + var user = await _userServices.QueryById(_user.ID, true); + if (user == null) + { + _user.MessageModel = new ApiResponse(StatusCode.CODE401, "用户不存在或已被删除").MessageModel; + context.Fail(new AuthorizationFailureReason(this, _user.MessageModel.msg)); + return; + } + + if (user.IsDeleted) + { + _user.MessageModel = new ApiResponse(StatusCode.CODE401, "用户已被删除,禁止登陆!").MessageModel; + context.Fail(new AuthorizationFailureReason(this, _user.MessageModel.msg)); + return; + } + + if (!user.Enable) + { + _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.FirstOrDefault(s => s.Type == "exp")?.Value) != null && + DateHelper.StampToDateTime(httpContext.User.Claims + .FirstOrDefault(s => s.Type == "exp")?.Value) >= DateTime.Now; + } + else + { + // jwt + isExp = + (httpContext.User.Claims.FirstOrDefault(s => s.Type == ClaimTypes.Expiration) + ?.Value) != null && + DateTime.Parse(httpContext.User.Claims + .FirstOrDefault(s => s.Type == ClaimTypes.Expiration)?.Value) >= DateTime.Now; + } + + if (!isExp) + { + context.Fail(new AuthorizationFailureReason(this, "授权已过期,请重新授权")); + return; + } + + + //校验签发时间 + if (!Permissions.IsUseIds4) + { + var value = httpContext.User.Claims + .FirstOrDefault(s => s.Type == JwtRegisteredClaimNames.Iat)?.Value; + if (value != null) + { + if (user.CriticalModifyTime > value.ObjToDate()) + { + _user.MessageModel = new ApiResponse(StatusCode.CODE401, "很抱歉,授权已失效,请重新授权") + .MessageModel; + context.Fail(new AuthorizationFailureReason(this, _user.MessageModel.msg)); + return; + } + } + } + // 获取当前用户的角色信息 var currentUserRoles = new List(); // ids4和jwt切换 @@ -153,7 +223,8 @@ orderby item.Id if (currentUserRoles.All(s => s != "SuperAdmin")) { var isMatchRole = false; - var permisssionRoles = requirement.Permissions.Where(w => currentUserRoles.Contains(w.Role)); + var permisssionRoles = + requirement.Permissions.Where(w => currentUserRoles.Contains(w.Role)); foreach (var item in permisssionRoles) { try @@ -177,50 +248,7 @@ orderby item.Id 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; - } - if (!isExp) - { - context.Fail(new AuthorizationFailureReason(this, "授权已过期,请重新授权")); - 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; diff --git a/Blog.Core.Extensions/Middlewares/SwaggerAuthMiddleware.cs b/Blog.Core.Extensions/Middlewares/SwaggerAuthMiddleware.cs index 1438588c..3cc284a1 100644 --- a/Blog.Core.Extensions/Middlewares/SwaggerAuthMiddleware.cs +++ b/Blog.Core.Extensions/Middlewares/SwaggerAuthMiddleware.cs @@ -1,5 +1,6 @@ using System.Net; using System.Threading.Tasks; +using Blog.Core.Common.Swagger; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; @@ -28,7 +29,7 @@ public async Task InvokeAsync(HttpContext context) } // 无权限,跳转swagger登录页 - context.Response.Redirect("/swg-login.html"); + context.RedirectSwaggerLogin(); } else { @@ -40,7 +41,7 @@ public bool IsAuthorized(HttpContext context) { // 使用session模式 // 可以使用其他的 - return context.Session.GetString("swagger-code") == "success"; + return context.IsSuccessSwagger(); } /// diff --git a/Blog.Core.Model/Models/sysUserInfo.cs b/Blog.Core.Model/Models/sysUserInfo.cs index 2a417a16..574ea311 100644 --- a/Blog.Core.Model/Models/sysUserInfo.cs +++ b/Blog.Core.Model/Models/sysUserInfo.cs @@ -4,138 +4,141 @@ namespace Blog.Core.Model.Models { - /// - /// 用户信息表 - /// - //[SugarTable("SysUserInfo")] - [SugarTable("SysUserInfo", "用户表")] //('数据库表名','数据库表备注') - public class SysUserInfo : SysUserInfoRoot - { - public SysUserInfo() - { - } - - public SysUserInfo(string loginName, string loginPWD) - { - LoginName = loginName; - LoginPWD = loginPWD; - RealName = LoginName; - Status = 0; - CreateTime = DateTime.Now; - UpdateTime = DateTime.Now; - LastErrorTime = DateTime.Now; - ErrorCount = 0; - Name = ""; - } - - /// - /// 登录账号 - /// - [SugarColumn(Length = 200, IsNullable = true, ColumnDescription = "登录账号")] - //:eg model 根据sqlsugar的完整定义可以如下定义,ColumnDescription可定义表字段备注 - //[SugarColumn(IsNullable = false, ColumnDescription = "登录账号", IsPrimaryKey = false, IsIdentity = false, Length = 50)] - //ColumnDescription 表字段备注, 已在MSSQL测试,配合 [SugarTable("SysUserInfo", "用户表")]//('数据库表名','数据库表备注') - //可以完整生成 表备注和各个字段的中文备注 - //2022/10/11 - //测试mssql 发现 不写ColumnDescription,写好注释在mssql下也能生成表字段备注 - public string LoginName { get; set; } - - /// - /// 登录密码 - /// - [SugarColumn(Length = 200, IsNullable = true)] - public string LoginPWD { get; set; } - - /// - /// 真实姓名 - /// - [SugarColumn(Length = 200, IsNullable = true)] - public string RealName { get; set; } - - /// - /// 状态 - /// - public int Status { get; set; } - - /// - /// 部门 - /// - [SugarColumn(IsNullable = true)] - public int DepartmentId { get; set; } = -1; - - /// - /// 备注 - /// - [SugarColumn(Length = 2000, IsNullable = true)] - public string Remark { get; set; } - - /// - /// 创建时间 - /// - public DateTime CreateTime { get; set; } = DateTime.Now; - - /// - /// 更新时间 - /// - public DateTime UpdateTime { get; set; } = DateTime.Now; - - /// - /// 关键业务修改时间 - /// - public DateTime CriticalModifyTime { get; set; } = DateTime.Now; - - /// - ///最后异常时间 - /// - public DateTime LastErrorTime { get; set; } = DateTime.Now; - - /// - ///错误次数 - /// - public int ErrorCount { get; set; } - - - /// - /// 登录账号 - /// - [SugarColumn(Length = 200, IsNullable = true)] - public string Name { get; set; } - - // 性别 - [SugarColumn(IsNullable = true)] - public int Sex { get; set; } = 0; - - // 年龄 - [SugarColumn(IsNullable = true)] - public int Age { get; set; } - - // 生日 - [SugarColumn(IsNullable = true)] - public DateTime Birth { get; set; } = DateTime.Now; - - // 地址 - [SugarColumn(Length = 200, IsNullable = true)] - public string Address { get; set; } - - [SugarColumn(IsNullable = true)] - public bool IsDeleted { get; set; } - - /// - /// 租户Id - /// - [SugarColumn(IsNullable = false,DefaultValue = "0")] - public long TenantId { get; set; } - - [Navigate(NavigateType.OneToOne, nameof(TenantId))] - public SysTenant Tenant { get; set; } - - [SugarColumn(IsIgnore = true)] - public List RoleNames { get; set; } - - [SugarColumn(IsIgnore = true)] - public List Dids { get; set; } - - [SugarColumn(IsIgnore = true)] - public string DepartmentName { get; set; } - } + /// + /// 用户信息表 + /// + //[SugarTable("SysUserInfo")] + [SugarTable("SysUserInfo", "用户表")] //('数据库表名','数据库表备注') + public class SysUserInfo : SysUserInfoRoot + { + public SysUserInfo() + { + } + + public SysUserInfo(string loginName, string loginPWD) + { + LoginName = loginName; + LoginPWD = loginPWD; + RealName = LoginName; + Status = 0; + CreateTime = DateTime.Now; + UpdateTime = DateTime.Now; + LastErrorTime = DateTime.Now; + ErrorCount = 0; + Name = ""; + } + + /// + /// 登录账号 + /// + [SugarColumn(Length = 200, IsNullable = true, ColumnDescription = "登录账号")] + //:eg model 根据sqlsugar的完整定义可以如下定义,ColumnDescription可定义表字段备注 + //[SugarColumn(IsNullable = false, ColumnDescription = "登录账号", IsPrimaryKey = false, IsIdentity = false, Length = 50)] + //ColumnDescription 表字段备注, 已在MSSQL测试,配合 [SugarTable("SysUserInfo", "用户表")]//('数据库表名','数据库表备注') + //可以完整生成 表备注和各个字段的中文备注 + //2022/10/11 + //测试mssql 发现 不写ColumnDescription,写好注释在mssql下也能生成表字段备注 + public string LoginName { get; set; } + + /// + /// 登录密码 + /// + [SugarColumn(Length = 200, IsNullable = true)] + public string LoginPWD { get; set; } + + /// + /// 真实姓名 + /// + [SugarColumn(Length = 200, IsNullable = true)] + public string RealName { get; set; } + + /// + /// 状态 + /// + public int Status { get; set; } + + /// + /// 部门 + /// + [SugarColumn(IsNullable = true)] + public int DepartmentId { get; set; } = -1; + + /// + /// 备注 + /// + [SugarColumn(Length = 2000, IsNullable = true)] + public string Remark { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } = DateTime.Now; + + /// + /// 更新时间 + /// + public DateTime UpdateTime { get; set; } = DateTime.Now; + + /// + /// 关键业务修改时间 + /// + public DateTime CriticalModifyTime { get; set; } = DateTime.Now; + + /// + ///最后异常时间 + /// + public DateTime LastErrorTime { get; set; } = DateTime.Now; + + /// + ///错误次数 + /// + public int ErrorCount { get; set; } + + + /// + /// 登录账号 + /// + [SugarColumn(Length = 200, IsNullable = true)] + public string Name { get; set; } + + // 性别 + [SugarColumn(IsNullable = true)] + public int Sex { get; set; } = 0; + + // 年龄 + [SugarColumn(IsNullable = true)] + public int Age { get; set; } + + // 生日 + [SugarColumn(IsNullable = true)] + public DateTime Birth { get; set; } = DateTime.Now; + + // 地址 + [SugarColumn(Length = 200, IsNullable = true)] + public string Address { get; set; } + + [SugarColumn(DefaultValue = "1")] + public bool Enable { get; set; } = true; + + [SugarColumn(IsNullable = true)] + public bool IsDeleted { get; set; } + + /// + /// 租户Id + /// + [SugarColumn(IsNullable = false, DefaultValue = "0")] + public long TenantId { get; set; } + + [Navigate(NavigateType.OneToOne, nameof(TenantId))] + public SysTenant Tenant { get; set; } + + [SugarColumn(IsIgnore = true)] + public List RoleNames { get; set; } + + [SugarColumn(IsIgnore = true)] + public List Dids { get; set; } + + [SugarColumn(IsIgnore = true)] + public string DepartmentName { get; set; } + } } \ No newline at end of file diff --git a/Blog.Core.Tests/Controller_Test/LoginController_Should.cs b/Blog.Core.Tests/Controller_Test/LoginController_Should.cs index 7c0cb1bb..c9387f72 100644 --- a/Blog.Core.Tests/Controller_Test/LoginController_Should.cs +++ b/Blog.Core.Tests/Controller_Test/LoginController_Should.cs @@ -3,72 +3,76 @@ using Xunit; using Autofac; using Blog.Core.AuthHelper; +using Microsoft.Extensions.Logging; namespace Blog.Core.Tests { - public class LoginController_Should - { - LoginController loginController; - - private readonly ISysUserInfoServices _sysUserInfoServices; - private readonly IUserRoleServices _userRoleServices; - private readonly IRoleServices _roleServices; - private readonly PermissionRequirement _requirement; - private readonly IRoleModulePermissionServices _roleModulePermissionServices; - - DI_Test dI_Test = new DI_Test(); - - - - public LoginController_Should() - { - var container = dI_Test.DICollections(); - _sysUserInfoServices = container.Resolve(); - _userRoleServices = container.Resolve(); - _roleServices = container.Resolve(); - _requirement = container.Resolve(); - _roleModulePermissionServices = container.Resolve(); - loginController = new LoginController(_sysUserInfoServices,_userRoleServices,_roleServices,_requirement, _roleModulePermissionServices); - } - - [Fact] - public void GetJwtStrTest() - { - var data = loginController.GetJwtStr("test", "test"); - - Assert.NotNull(data); - } - [Fact] - public void GetJwtStrForNuxtTest() - { - object blogs = loginController.GetJwtStrForNuxt("test", "test"); - - Assert.NotNull(blogs); - } - - [Fact] - public async void GetJwtToken3Test() - { - - var res = await loginController.GetJwtToken3("test", "test"); - - Assert.NotNull(res); - } - - [Fact] - public async void RefreshTokenTest() - { - var res = await loginController.RefreshToken("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoidGVzdCIsImp0aSI6IjgiLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL2V4cGlyYXRpb24iOiIyMDE5LzEwLzE4IDIzOjI2OjQ5IiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjoiQWRtaW5UZXN0IiwibmJmIjoxNTcxNDA4ODA5LCJleHAiOjE1NzE0MTI0MDksImlzcyI6IkJsb2cuQ29yZSIsImF1ZCI6IndyIn0.oz-SPz6UCL78fM09bUecw5rmjcNYEY9dWGtuPs2gdBg"); - - Assert.NotNull(res); - } - - [Fact] - public void Md5PasswordTest() - { - var res = loginController.Md5Password("test"); - - Assert.NotNull(res); - } - } -} + public class LoginController_Should + { + LoginController loginController; + + private readonly ISysUserInfoServices _sysUserInfoServices; + private readonly IUserRoleServices _userRoleServices; + private readonly IRoleServices _roleServices; + private readonly PermissionRequirement _requirement; + private readonly IRoleModulePermissionServices _roleModulePermissionServices; + private readonly ILogger _logger; + + DI_Test dI_Test = new DI_Test(); + + + public LoginController_Should() + { + var container = dI_Test.DICollections(); + _sysUserInfoServices = container.Resolve(); + _userRoleServices = container.Resolve(); + _roleServices = container.Resolve(); + _requirement = container.Resolve(); + _roleModulePermissionServices = container.Resolve(); + _logger = container.Resolve>(); + loginController = new LoginController(_sysUserInfoServices, _userRoleServices, _roleServices, _requirement, + _roleModulePermissionServices, _logger); + } + + [Fact] + public void GetJwtStrTest() + { + var data = loginController.GetJwtStr("test", "test"); + + Assert.NotNull(data); + } + + [Fact] + public void GetJwtStrForNuxtTest() + { + object blogs = loginController.GetJwtStrForNuxt("test", "test"); + + Assert.NotNull(blogs); + } + + [Fact] + public async void GetJwtToken3Test() + { + var res = await loginController.GetJwtToken3("test", "test"); + + Assert.NotNull(res); + } + + [Fact] + public async void RefreshTokenTest() + { + var res = await loginController.RefreshToken( + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoidGVzdCIsImp0aSI6IjgiLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL2V4cGlyYXRpb24iOiIyMDE5LzEwLzE4IDIzOjI2OjQ5IiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjoiQWRtaW5UZXN0IiwibmJmIjoxNTcxNDA4ODA5LCJleHAiOjE1NzE0MTI0MDksImlzcyI6IkJsb2cuQ29yZSIsImF1ZCI6IndyIn0.oz-SPz6UCL78fM09bUecw5rmjcNYEY9dWGtuPs2gdBg"); + + Assert.NotNull(res); + } + + [Fact] + public void Md5PasswordTest() + { + var res = loginController.Md5Password("test"); + + Assert.NotNull(res); + } + } +} \ No newline at end of file From 1c700fd90d8a2032114c5390a6681dc3d8689e9e Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Fri, 26 May 2023 15:20:26 +0800 Subject: [PATCH 42/84] =?UTF-8?q?=E2=9C=A8=20=E4=BC=98=E5=8C=96=E5=A4=9A?= =?UTF-8?q?=E6=AC=A1=E8=A7=A3=E6=9E=90Token=E9=87=8D=E5=A4=8D=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0Claims?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../HttpContextUser/AspNetUser.cs | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/Blog.Core.Common/HttpContextUser/AspNetUser.cs b/Blog.Core.Common/HttpContextUser/AspNetUser.cs index ba21889e..1ceaa45f 100644 --- a/Blog.Core.Common/HttpContextUser/AspNetUser.cs +++ b/Blog.Core.Common/HttpContextUser/AspNetUser.cs @@ -1,13 +1,12 @@ -using System; +using Blog.Core.Common.Swagger; +using Blog.Core.Model; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; -using Blog.Core.Common.Swagger; -using Blog.Core.Model; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; -using SqlSugar.Extensions; namespace Blog.Core.Common.HttpContextUser { @@ -34,7 +33,9 @@ private string GetName() { if (!string.IsNullOrEmpty(GetToken())) { - var getNameType = Permissions.IsUseIds4 ? "name" : "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"; + var getNameType = Permissions.IsUseIds4 + ? "name" + : "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"; return GetUserInfoFromToken(getNameType).FirstOrDefault().ObjToString(); } } @@ -58,15 +59,19 @@ public string GetToken() { return token; } - + if (_accessor.HttpContext?.IsSuccessSwagger() == true) { token = _accessor.HttpContext.GetSuccessSwaggerJwt(); if (token.IsNotEmptyOrNull()) { + if (_accessor.HttpContext.User.Claims.Any(s => s.Type == JwtRegisteredClaimNames.Jti)) + { + return token; + } + var claims = new ClaimsIdentity(GetClaimsIdentity(token)); _accessor.HttpContext.User.AddIdentity(claims); - return token; } } @@ -86,8 +91,8 @@ public List GetUserInfoFromToken(string ClaimType) JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(token); return (from item in jwtToken.Claims - where item.Type == ClaimType - select item.Value).ToList(); + where item.Type == ClaimType + select item.Value).ToList(); } return new List() { }; @@ -100,7 +105,7 @@ public IEnumerable GetClaimsIdentity() if (_accessor.HttpContext == null) return ArraySegment.Empty; if (!IsAuthenticated()) return GetClaimsIdentity(GetToken()); - + var claims = _accessor.HttpContext.User.Claims.ToList(); var headers = _accessor.HttpContext.Request.Headers; foreach (var header in headers) @@ -110,6 +115,7 @@ public IEnumerable GetClaimsIdentity() return claims; } + public IEnumerable GetClaimsIdentity(string token) { var jwtHandler = new JwtSecurityTokenHandler(); @@ -127,8 +133,8 @@ public IEnumerable GetClaimsIdentity(string token) public List GetClaimValueByType(string ClaimType) { return (from item in GetClaimsIdentity() - where item.Type == ClaimType - select item.Value).ToList(); + where item.Type == ClaimType + select item.Value).ToList(); } } } \ No newline at end of file From 3f24902521fe64385198bbd4f4f5effb662727c3 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Tue, 30 May 2023 10:27:43 +0800 Subject: [PATCH 43/84] Update PermissionHandler.cs --- .../Policys/PermissionHandler.cs | 451 +++++++++--------- 1 file changed, 228 insertions(+), 223 deletions(-) diff --git a/Blog.Core.Extensions/Authorizations/Policys/PermissionHandler.cs b/Blog.Core.Extensions/Authorizations/Policys/PermissionHandler.cs index d2dbfdf7..27099b7b 100644 --- a/Blog.Core.Extensions/Authorizations/Policys/PermissionHandler.cs +++ b/Blog.Core.Extensions/Authorizations/Policys/PermissionHandler.cs @@ -19,252 +19,257 @@ 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 defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync(); + if (defaultAuthenticate != null) + { + var result = await httpContext.AuthenticateAsync(defaultAuthenticate.Name); - // 是否开启测试环境 - var isTestCurrent = AppSettings.app(new string[] {"AppSettings", "UseLoadTest"}).ObjToBool(); + // 是否开启测试环境 + var isTestCurrent = AppSettings.app(new string[] { "AppSettings", "UseLoadTest" }).ObjToBool(); - //result?.Principal不为空即登录成功 - if (result?.Principal != null || isTestCurrent || httpContext.IsSuccessSwagger()) - { - if (!isTestCurrent) httpContext.User = result.Principal; + //result?.Principal不为空即登录成功 + if (result?.Principal != null || isTestCurrent || httpContext.IsSuccessSwagger()) + { + if (!isTestCurrent) httpContext.User = result.Principal; - //应该要先校验用户的信息 再校验菜单权限相关的 + //应该要先校验用户的信息 再校验菜单权限相关的 + // JWT模式下校验当前用户状态 + // IDS4也可以校验,可以通过服务或者接口形式 + SysUserInfo user = new(); + if (!Permissions.IsUseIds4) + { + //校验用户 + user = await _userServices.QueryById(_user.ID, true); + if (user == null) + { + _user.MessageModel = new ApiResponse(StatusCode.CODE401, "用户不存在或已被删除").MessageModel; + context.Fail(new AuthorizationFailureReason(this, _user.MessageModel.msg)); + return; + } - //校验用户 - var user = await _userServices.QueryById(_user.ID, true); - if (user == null) - { - _user.MessageModel = new ApiResponse(StatusCode.CODE401, "用户不存在或已被删除").MessageModel; - context.Fail(new AuthorizationFailureReason(this, _user.MessageModel.msg)); - return; - } + if (user.IsDeleted) + { + _user.MessageModel = new ApiResponse(StatusCode.CODE401, "用户已被删除,禁止登陆!").MessageModel; + context.Fail(new AuthorizationFailureReason(this, _user.MessageModel.msg)); + return; + } - if (user.IsDeleted) - { - _user.MessageModel = new ApiResponse(StatusCode.CODE401, "用户已被删除,禁止登陆!").MessageModel; - context.Fail(new AuthorizationFailureReason(this, _user.MessageModel.msg)); - return; - } + if (!user.Enable) + { + _user.MessageModel = new ApiResponse(StatusCode.CODE401, "用户已被禁用!禁止登陆!").MessageModel; + context.Fail(new AuthorizationFailureReason(this, _user.MessageModel.msg)); + return; + } + } - if (!user.Enable) - { - _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.FirstOrDefault(s => s.Type == "exp")?.Value) != null && + DateHelper.StampToDateTime(httpContext.User.Claims + .FirstOrDefault(s => s.Type == "exp")?.Value) >= DateTime.Now; + } + else + { + // jwt + isExp = + (httpContext.User.Claims.FirstOrDefault(s => s.Type == ClaimTypes.Expiration) + ?.Value) != null && + DateTime.Parse(httpContext.User.Claims + .FirstOrDefault(s => s.Type == ClaimTypes.Expiration)?.Value) >= DateTime.Now; + } - // 判断token是否过期,过期则重新登录 - var isExp = false; - // ids4和jwt切换 - // ids4 - if (Permissions.IsUseIds4) - { - isExp = (httpContext.User.Claims.FirstOrDefault(s => s.Type == "exp")?.Value) != null && - DateHelper.StampToDateTime(httpContext.User.Claims - .FirstOrDefault(s => s.Type == "exp")?.Value) >= DateTime.Now; - } - else - { - // jwt - isExp = - (httpContext.User.Claims.FirstOrDefault(s => s.Type == ClaimTypes.Expiration) - ?.Value) != null && - DateTime.Parse(httpContext.User.Claims - .FirstOrDefault(s => s.Type == ClaimTypes.Expiration)?.Value) >= DateTime.Now; - } + if (!isExp) + { + context.Fail(new AuthorizationFailureReason(this, "授权已过期,请重新授权")); + return; + } - if (!isExp) - { - context.Fail(new AuthorizationFailureReason(this, "授权已过期,请重新授权")); - return; - } + //校验签发时间 + if (!Permissions.IsUseIds4) + { + var value = httpContext.User.Claims + .FirstOrDefault(s => s.Type == JwtRegisteredClaimNames.Iat)?.Value; + if (value != null) + { + if (user.CriticalModifyTime > value.ObjToDate()) + { + _user.MessageModel = new ApiResponse(StatusCode.CODE401, "很抱歉,授权已失效,请重新授权") + .MessageModel; + context.Fail(new AuthorizationFailureReason(this, _user.MessageModel.msg)); + return; + } + } + } - //校验签发时间 - if (!Permissions.IsUseIds4) - { - var value = httpContext.User.Claims - .FirstOrDefault(s => s.Type == JwtRegisteredClaimNames.Iat)?.Value; - if (value != null) - { - if (user.CriticalModifyTime > value.ObjToDate()) - { - _user.MessageModel = new ApiResponse(StatusCode.CODE401, "很抱歉,授权已失效,请重新授权") - .MessageModel; - context.Fail(new AuthorizationFailureReason(this, _user.MessageModel.msg)); - return; - } - } - } + // 获取当前用户的角色信息 + 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(); + } - // 获取当前用户的角色信息 - 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.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 + } + } - //超级管理员 默认拥有所有权限 - 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 - } - } + //验证权限 + if (currentUserRoles.Count <= 0 || !isMatchRole) + { + context.Fail(); + return; + } + } - //验证权限 - if (currentUserRoles.Count <= 0 || !isMatchRole) - { - context.Fail(); - return; - } - } + context.Succeed(requirement); + 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; + } + } - //判断没有登录时,是否访问登录的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 From 7629527ee987b3d2d081a7423f9a2873cd48d2ee Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Thu, 1 Jun 2023 17:54:54 +0800 Subject: [PATCH 44/84] =?UTF-8?q?=E2=9C=A8=F0=9F=8E=A8=20=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E7=9A=84=E5=AE=8C=E5=96=84=E4=BC=98=E5=8C=96=201.?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E5=B0=81=E8=A3=85=E5=9F=BA=E4=BA=8E=E5=BE=AE?= =?UTF-8?q?=E8=BD=AF=E5=88=86=E5=B8=83=E5=BC=8F=E7=BC=93=E5=AD=98=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3IDistributedCache=E4=BD=BF=E7=94=A8=202.IDistributedCa?= =?UTF-8?q?che=E5=8F=AA=E9=80=82=E5=90=88=E6=99=AE=E9=80=9A=E7=9A=84?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E4=BD=BF=E7=94=A8,=E5=A6=82=E6=9E=9C?= =?UTF-8?q?=E8=A6=81=E4=BD=BF=E7=94=A8redis=E9=98=9F=E5=88=97=E3=80=81?= =?UTF-8?q?=E8=AE=A2=E9=98=85redis=E6=B6=88=E6=81=AF=E7=AD=89,=E5=B0=B1?= =?UTF-8?q?=E8=A6=81=E4=BD=BF=E7=94=A8redis=E5=8E=9F=E7=94=9F=E5=BA=93=203?= =?UTF-8?q?.=E5=A2=9E=E5=8A=A0=E7=BC=93=E5=AD=98=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3[Systems/CacheManageController]=204.=E7=9B=AE?= =?UTF-8?q?=E5=89=8D=E6=94=AF=E6=8C=81=E5=86=85=E5=AD=98=E3=80=81redis?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E5=AE=9E=E7=8E=B0,=E7=90=86=E8=AE=BA?= =?UTF-8?q?=E5=8F=AF=E9=9A=8F=E6=84=8F=E6=89=A9=E5=B1=95=E7=94=9A=E8=87=B3?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E5=AE=9E=E7=8E=B0=205.=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E4=BD=BF=E7=94=A8=E5=86=85=E5=AD=98=E7=BC=93=E5=AD=98?= =?UTF-8?q?,=E5=8F=AF=E5=9C=A8appsetting.json=E4=B8=AD=E9=85=8D=E7=BD=AERe?= =?UTF-8?q?dis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 切换到IDistributedCache好处如下 默认session使用IDistributedCache进行存储,如果你搭配使用IDistributedCache+外部缓存(如Redis),可实现应用程序重启session不丢失 更直观就是,调试的时候登录swagger后即使重启调试也无需在登陆 --- Blog.Core.Api/Blog.Core.Api.csproj | 1 - Blog.Core.Api/Blog.Core.xml | 35 ++ .../Systems/CacheManageController.cs | 81 ++++ Blog.Core.Api/Program.cs | 3 +- Blog.Core.Api/Startup.cs | 5 +- Blog.Core.Api/appsettings.json | 4 +- Blog.Core.Common/Blog.Core.Common.csproj | 1 - Blog.Core.Common/Caches/Caching.cs | 328 ++++++++++++++ Blog.Core.Common/Caches/ICaching.cs | 53 +++ .../Caches/SqlSugarCacheService.cs | 62 +++ Blog.Core.Common/Const/CacheConst.cs | 87 ++++ Blog.Core.Common/DB/BaseDBConfig.cs | 1 - .../Extensions/ExpressionExtensions.cs | 410 +++++++++--------- .../MemoryCache/ICachingProvider.cs | 12 - Blog.Core.Common/MemoryCache/MemoryCaching.cs | 30 -- Blog.Core.Common/Option/RedisOptions.cs | 24 + Blog.Core.Extensions/AOP/BlogCacheAOP.cs | 8 +- .../Blog.Core.Extensions.csproj | 1 + .../Redis/IRedisBasketRepository.cs | 1 + .../Redis/RedisBasketRepository.cs | 1 + .../ServiceExtensions/CacheSetup.cs | 45 ++ .../ServiceExtensions/HttpRuntimeCache.cs | 67 --- .../ServiceExtensions/MemoryCacheSetup.cs | 27 -- .../ServiceExtensions/RedisCacheSetup.cs | 34 -- .../ServiceExtensions/SqlsugarSetup.cs | 12 +- .../Helper/CustomJwtTokenAuthMiddleware.cs | 1 + .../Utility/SerilogRequestUtility.cs | 104 ++--- 27 files changed, 992 insertions(+), 446 deletions(-) create mode 100644 Blog.Core.Api/Controllers/Systems/CacheManageController.cs create mode 100644 Blog.Core.Common/Caches/Caching.cs create mode 100644 Blog.Core.Common/Caches/ICaching.cs create mode 100644 Blog.Core.Common/Caches/SqlSugarCacheService.cs create mode 100644 Blog.Core.Common/Const/CacheConst.cs delete mode 100644 Blog.Core.Common/MemoryCache/ICachingProvider.cs delete mode 100644 Blog.Core.Common/MemoryCache/MemoryCaching.cs create mode 100644 Blog.Core.Common/Option/RedisOptions.cs create mode 100644 Blog.Core.Extensions/ServiceExtensions/CacheSetup.cs delete mode 100644 Blog.Core.Extensions/ServiceExtensions/HttpRuntimeCache.cs delete mode 100644 Blog.Core.Extensions/ServiceExtensions/MemoryCacheSetup.cs delete mode 100644 Blog.Core.Extensions/ServiceExtensions/RedisCacheSetup.cs diff --git a/Blog.Core.Api/Blog.Core.Api.csproj b/Blog.Core.Api/Blog.Core.Api.csproj index 2f8c41e9..f3f9befa 100644 --- a/Blog.Core.Api/Blog.Core.Api.csproj +++ b/Blog.Core.Api/Blog.Core.Api.csproj @@ -106,7 +106,6 @@ - diff --git a/Blog.Core.Api/Blog.Core.xml b/Blog.Core.Api/Blog.Core.xml index 672ea21f..69d3f9b2 100644 --- a/Blog.Core.Api/Blog.Core.xml +++ b/Blog.Core.Api/Blog.Core.xml @@ -1299,6 +1299,41 @@ + + + 缓存管理 + + + + + 获取全部缓存 + + + + + + 获取缓存 + + + + + + 新增 + + + + + + 删除全部缓存 + + + + + + 删除缓存 + + + 数据库管理 diff --git a/Blog.Core.Api/Controllers/Systems/CacheManageController.cs b/Blog.Core.Api/Controllers/Systems/CacheManageController.cs new file mode 100644 index 00000000..4f400e8f --- /dev/null +++ b/Blog.Core.Api/Controllers/Systems/CacheManageController.cs @@ -0,0 +1,81 @@ +using Blog.Core.Common.Caches; +using Blog.Core.Controllers; +using Blog.Core.Model; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Blog.Core.Api.Controllers.Systems; + +/// +/// 缓存管理 +/// +[Route("api/Systems/[controller]")] +[ApiController] +[Authorize(Permissions.Name)] +public class CacheManageController : BaseApiController +{ + private readonly ICaching _caching; + + public CacheManageController(ICaching caching) + { + _caching = caching; + } + + /// + /// 获取全部缓存 + /// + /// + [HttpGet] + public async Task>> Get() + { + return Success(await _caching.GetAllCacheKeysAsync()); + } + + /// + /// 获取缓存 + /// + /// + [HttpGet("{key}")] + public async Task> Get(string key) + { + return Success(await _caching.GetStringAsync(key)); + } + + /// + /// 新增 + /// + /// + [HttpPost] + public async Task Post([FromQuery] string key, [FromQuery] string value, [FromQuery] int? expire) + { + if (expire.HasValue) + await _caching.SetStringAsync(key, value, TimeSpan.FromMilliseconds(expire.Value)); + else + await _caching.SetStringAsync(key, value); + + return Success(); + } + + /// + /// 删除全部缓存 + /// + /// + [HttpDelete] + public async Task Delete() + { + await _caching.RemoveAllAsync(); + return Success(); + } + + /// + /// 删除缓存 + /// + /// + [Route("{key}")] + [HttpDelete] + public async Task Delete(string key) + { + await _caching.RemoveAsync(key); + return Success(); + } +} \ No newline at end of file diff --git a/Blog.Core.Api/Program.cs b/Blog.Core.Api/Program.cs index b4929cc7..4311c97c 100644 --- a/Blog.Core.Api/Program.cs +++ b/Blog.Core.Api/Program.cs @@ -57,8 +57,7 @@ JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); -builder.Services.AddMemoryCacheSetup(); -builder.Services.AddRedisCacheSetup(); +builder.Services.AddCacheSetup(); builder.Services.AddSqlsugarSetup(); builder.Services.AddDbSetup(); diff --git a/Blog.Core.Api/Startup.cs b/Blog.Core.Api/Startup.cs index 64e5ee78..2911625d 100644 --- a/Blog.Core.Api/Startup.cs +++ b/Blog.Core.Api/Startup.cs @@ -8,6 +8,7 @@ using Blog.Core.Common.Seed; using Blog.Core.Extensions; using Blog.Core.Extensions.Middlewares; +using Blog.Core.Extensions.ServiceExtensions; using Blog.Core.Filter; using Blog.Core.Hubs; using Blog.Core.IServices; @@ -49,9 +50,7 @@ public void ConfigureServices(IServiceCollection services) // 确保从认证中心返回的ClaimType不被更改,不使用Map映射 JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); - services.AddMemoryCacheSetup(); - services.AddRedisCacheSetup(); - + services.AddCacheSetup(); services.AddSqlsugarSetup(); services.AddDbSetup(); services.AddAutoMapperSetup(); diff --git a/Blog.Core.Api/appsettings.json b/Blog.Core.Api/appsettings.json index cef87bed..d220a034 100644 --- a/Blog.Core.Api/appsettings.json +++ b/Blog.Core.Api/appsettings.json @@ -18,7 +18,9 @@ }, "AllowedHosts": "*", "Redis": { - "ConnectionString": "127.0.0.1:6319,password=admin" + "Enable": true, + "ConnectionString": "127.0.0.1:6379", + "InstanceName": "" //前缀 }, "RabbitMQ": { "Enabled": false, diff --git a/Blog.Core.Common/Blog.Core.Common.csproj b/Blog.Core.Common/Blog.Core.Common.csproj index aeba8476..5f249870 100644 --- a/Blog.Core.Common/Blog.Core.Common.csproj +++ b/Blog.Core.Common/Blog.Core.Common.csproj @@ -51,7 +51,6 @@ - diff --git a/Blog.Core.Common/Caches/Caching.cs b/Blog.Core.Common/Caches/Caching.cs new file mode 100644 index 00000000..e7b4ab07 --- /dev/null +++ b/Blog.Core.Common/Caches/Caching.cs @@ -0,0 +1,328 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Blog.Core.Common.Const; +using Microsoft.Extensions.Caching.Distributed; +using Newtonsoft.Json; + +namespace Blog.Core.Common.Caches; + +public class Caching : ICaching +{ + private readonly IDistributedCache _cache; + + public Caching(IDistributedCache cache) + { + _cache = cache; + } + + private byte[] GetBytes(T source) + { + return Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(source)); + } + + public IDistributedCache Cache => _cache; + + public void AddCacheKey(string cacheKey) + { + var res = _cache.GetString(CacheConst.KeyAll); + var allkeys = string.IsNullOrWhiteSpace(res) ? new List() : JsonConvert.DeserializeObject>(res); + if (!allkeys.Any(m => m == cacheKey)) + { + allkeys.Add(cacheKey); + _cache.SetString(CacheConst.KeyAll, JsonConvert.SerializeObject(allkeys)); + } + } + + /// + /// 增加缓存Key + /// + /// + /// + public async Task AddCacheKeyAsync(string cacheKey) + { + var res = await _cache.GetStringAsync(CacheConst.KeyAll); + var allkeys = string.IsNullOrWhiteSpace(res) ? new List() : JsonConvert.DeserializeObject>(res); + if (!allkeys.Any(m => m == cacheKey)) + { + allkeys.Add(cacheKey); + await _cache.SetStringAsync(CacheConst.KeyAll, JsonConvert.SerializeObject(allkeys)); + } + } + + public void DelByPattern(string key) + { + var allkeys = GetAllCacheKeys(); + if (allkeys == null) return; + + var delAllkeys = allkeys.Where(u => u.Contains(key)).ToList(); + delAllkeys.ForEach(u => { _cache.Remove(u); }); + + // 更新所有缓存键 + allkeys = allkeys.Where(u => !u.Contains(key)).ToList(); + _cache.SetString(CacheConst.KeyAll, JsonConvert.SerializeObject(allkeys)); + } + + /// + /// 删除某特征关键字缓存 + /// + /// + /// + public async Task DelByPatternAsync(string key) + { + var allkeys = await GetAllCacheKeysAsync(); + if (allkeys == null) return; + + var delAllkeys = allkeys.Where(u => u.Contains(key)).ToList(); + delAllkeys.ForEach(u => { _cache.Remove(u); }); + + // 更新所有缓存键 + allkeys = allkeys.Where(u => !u.Contains(key)).ToList(); + await _cache.SetStringAsync(CacheConst.KeyAll, JsonConvert.SerializeObject(allkeys)); + } + + public void DelCacheKey(string cacheKey) + { + var res = _cache.GetString(CacheConst.KeyAll); + var allkeys = string.IsNullOrWhiteSpace(res) ? new List() : JsonConvert.DeserializeObject>(res); + if (allkeys.Any(m => m == cacheKey)) + { + allkeys.Remove(cacheKey); + _cache.SetString(CacheConst.KeyAll, JsonConvert.SerializeObject(allkeys)); + } + } + + /// + /// 删除缓存 + /// + /// + /// + public async Task DelCacheKeyAsync(string cacheKey) + { + var res = await _cache.GetStringAsync(CacheConst.KeyAll); + var allkeys = string.IsNullOrWhiteSpace(res) ? new List() : JsonConvert.DeserializeObject>(res); + if (allkeys.Any(m => m == cacheKey)) + { + allkeys.Remove(cacheKey); + await _cache.SetStringAsync(CacheConst.KeyAll, JsonConvert.SerializeObject(allkeys)); + } + } + + public bool Exists(string cacheKey) + { + var res = _cache.Get(cacheKey); + return res != null; + } + + /// + /// 检查给定 key 是否存在 + /// + /// 键 + /// + public async Task ExistsAsync(string cacheKey) + { + var res = await _cache.GetAsync(cacheKey); + return res != null; + } + + public List GetAllCacheKeys() + { + var res = _cache.GetString(CacheConst.KeyAll); + return string.IsNullOrWhiteSpace(res) ? null : JsonConvert.DeserializeObject>(res); + } + + /// + /// 获取所有缓存列表 + /// + /// + public async Task> GetAllCacheKeysAsync() + { + var res = await _cache.GetStringAsync(CacheConst.KeyAll); + return string.IsNullOrWhiteSpace(res) ? null : JsonConvert.DeserializeObject>(res); + } + + public T Get(string cacheKey) + { + var res = _cache.Get(cacheKey); + return res == null ? default : JsonConvert.DeserializeObject(Encoding.UTF8.GetString(res)); + } + + /// + /// 获取缓存 + /// + /// + /// + /// + public async Task GetAsync(string cacheKey) + { + var res = await _cache.GetAsync(cacheKey); + return res == null ? default : JsonConvert.DeserializeObject(Encoding.UTF8.GetString(res)); + } + + public string GetString(string cacheKey) + { + return _cache.GetString(cacheKey); + } + + /// + /// 获取缓存 + /// + /// + /// + public async Task GetStringAsync(string cacheKey) + { + return await _cache.GetStringAsync(cacheKey); + } + + public void Remove(string key) + { + _cache.Remove(key); + DelCacheKey(key); + } + + /// + /// 删除缓存 + /// + /// + /// + public async Task RemoveAsync(string key) + { + await _cache.RemoveAsync(key); + await DelCacheKeyAsync(key); + } + + public void RemoveAll() + { + var catches = GetAllCacheKeys(); + foreach (var @catch in catches) Remove(@catch); + + catches.Clear(); + _cache.SetString(CacheConst.KeyAll, JsonConvert.SerializeObject(catches)); + } + + public async Task RemoveAllAsync() + { + var catches = await GetAllCacheKeysAsync(); + foreach (var @catch in catches) await RemoveAsync(@catch); + + catches.Clear(); + await _cache.SetStringAsync(CacheConst.KeyAll, JsonConvert.SerializeObject(catches)); + } + + + public void Set(string cacheKey, T value, TimeSpan? expire = null) + { + _cache.Set(cacheKey, GetBytes(value), expire == null ? new DistributedCacheEntryOptions() {AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(6)} : new DistributedCacheEntryOptions() {AbsoluteExpirationRelativeToNow = expire}); + + AddCacheKey(cacheKey); + } + + /// + /// 增加对象缓存 + /// + /// + /// + /// + public async Task SetAsync(string cacheKey, T value) + { + await _cache.SetAsync(cacheKey, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(value)), new DistributedCacheEntryOptions() {AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(6)}); + + await AddCacheKeyAsync(cacheKey); + } + + /// + /// 增加对象缓存,并设置过期时间 + /// + /// + /// + /// + /// + public async Task SetAsync(string cacheKey, T value, TimeSpan expire) + { + await _cache.SetAsync(cacheKey, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(value)), new DistributedCacheEntryOptions() {AbsoluteExpirationRelativeToNow = expire}); + + await AddCacheKeyAsync(cacheKey); + } + + public void SetPermanent(string cacheKey, T value) + { + _cache.Set(cacheKey, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(value))); + AddCacheKey(cacheKey); + } + public async Task SetPermanentAsync(string cacheKey, T value) + { + await _cache.SetAsync(cacheKey, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(value))); + await AddCacheKeyAsync(cacheKey); + } + + public void SetString(string cacheKey, string value, TimeSpan? expire = null) + { + if (expire == null) + _cache.SetString(cacheKey, value, new DistributedCacheEntryOptions() {AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(6)}); + else + _cache.SetString(cacheKey, value, new DistributedCacheEntryOptions() {AbsoluteExpirationRelativeToNow = expire}); + + AddCacheKey(cacheKey); + } + + /// + /// 增加字符串缓存 + /// + /// + /// + /// + public async Task SetStringAsync(string cacheKey, string value) + { + await _cache.SetStringAsync(cacheKey, value, new DistributedCacheEntryOptions() {AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(6)}); + + await AddCacheKeyAsync(cacheKey); + } + + /// + /// 增加字符串缓存,并设置过期时间 + /// + /// + /// + /// + /// + public async Task SetStringAsync(string cacheKey, string value, TimeSpan expire) + { + await _cache.SetStringAsync(cacheKey, value, new DistributedCacheEntryOptions() {AbsoluteExpirationRelativeToNow = expire}); + + await AddCacheKeyAsync(cacheKey); + } + + + /// + /// 缓存最大角色数据范围 + /// + /// + /// + /// + public async Task SetMaxDataScopeType(long userId, int dataScopeType) + { + var cacheKey = CacheConst.KeyMaxDataScopeType + userId; + await SetStringAsync(cacheKey, dataScopeType.ToString()); + + await AddCacheKeyAsync(cacheKey); + } + + /// + /// 根据父键清空 + /// + /// + /// + public async Task DelByParentKeyAsync(string key) + { + var allkeys = await GetAllCacheKeysAsync(); + if (allkeys == null) return; + + var delAllkeys = allkeys.Where(u => u.StartsWith(key)).ToList(); + delAllkeys.ForEach(Remove); + // 更新所有缓存键 + allkeys = allkeys.Where(u => !u.StartsWith(key)).ToList(); + await SetStringAsync(CacheConst.KeyAll, JsonConvert.SerializeObject(allkeys)); + } +} \ No newline at end of file diff --git a/Blog.Core.Common/Caches/ICaching.cs b/Blog.Core.Common/Caches/ICaching.cs new file mode 100644 index 00000000..f856aeb6 --- /dev/null +++ b/Blog.Core.Common/Caches/ICaching.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.Extensions.Caching.Distributed; + +namespace Blog.Core.Common.Caches; + +/// +/// 缓存抽象接口,基于IDistributedCache封装 +/// +public interface ICaching +{ + public IDistributedCache Cache { get; } + void AddCacheKey(string cacheKey); + Task AddCacheKeyAsync(string cacheKey); + + void DelByPattern(string key); + Task DelByPatternAsync(string key); + + void DelCacheKey(string cacheKey); + Task DelCacheKeyAsync(string cacheKey); + + bool Exists(string cacheKey); + Task ExistsAsync(string cacheKey); + + List GetAllCacheKeys(); + Task> GetAllCacheKeysAsync(); + + T Get(string cacheKey); + Task GetAsync(string cacheKey); + + string GetString(string cacheKey); + Task GetStringAsync(string cacheKey); + + void Remove(string key); + Task RemoveAsync(string key); + + void RemoveAll(); + Task RemoveAllAsync(); + + void Set(string cacheKey, T value, TimeSpan? expire = null); + Task SetAsync(string cacheKey, T value); + Task SetAsync(string cacheKey, T value, TimeSpan expire); + + void SetPermanent(string cacheKey, T value); + Task SetPermanentAsync(string cacheKey, T value); + + void SetString(string cacheKey, string value, TimeSpan? expire = null); + Task SetStringAsync(string cacheKey, string value); + Task SetStringAsync(string cacheKey, string value, TimeSpan expire); + + Task DelByParentKeyAsync(string key); +} \ No newline at end of file diff --git a/Blog.Core.Common/Caches/SqlSugarCacheService.cs b/Blog.Core.Common/Caches/SqlSugarCacheService.cs new file mode 100644 index 00000000..c26a164d --- /dev/null +++ b/Blog.Core.Common/Caches/SqlSugarCacheService.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using SqlSugar; + +namespace Blog.Core.Common.Caches; + +/// +/// 实现SqlSugar的ICacheService接口 +/// +public class SqlSugarCacheService : ICacheService +{ + private readonly Lazy _caching = new(() => App.GetService(false)); + private ICaching Caching => _caching.Value; + + public void Add(string key, V value) + { + Caching.Set(key, value); + } + + public void Add(string key, V value, int cacheDurationInSeconds) + { + Caching.Set(key, value, TimeSpan.FromSeconds(cacheDurationInSeconds)); + } + + public bool ContainsKey(string key) + { + return Caching.Exists(key); + } + + public V Get(string key) + { + return Caching.Get(key); + } + + public IEnumerable GetAllKey() + { + return Caching.GetAllCacheKeys(); + } + + public V GetOrCreate(string cacheKey, Func create, int cacheDurationInSeconds = int.MaxValue) + { + if (!ContainsKey(cacheKey)) + { + var value = create(); + Caching.Set(cacheKey, value, TimeSpan.FromSeconds(cacheDurationInSeconds)); + return value; + } + + return Caching.Get(cacheKey); + } + + public void Remove(string key) + { + Caching.Remove(key); + } + + public bool RemoveAll() + { + Caching.RemoveAll(); + return true; + } +} \ No newline at end of file diff --git a/Blog.Core.Common/Const/CacheConst.cs b/Blog.Core.Common/Const/CacheConst.cs new file mode 100644 index 00000000..92f0e633 --- /dev/null +++ b/Blog.Core.Common/Const/CacheConst.cs @@ -0,0 +1,87 @@ +namespace Blog.Core.Common.Const; + +/// +/// 缓存相关常量 +/// +public class CacheConst +{ + /// + /// 用户缓存 + /// + public const string KeyUser = "user:"; + + /// + /// 用户部门缓存 + /// + public const string KeyUserDepart = "userDepart:"; + + /// + /// 菜单缓存 + /// + public const string KeyMenu = "menu:"; + + /// + /// 菜单 + /// + public const string KeyPermissions = "permissions"; + + /// + /// 权限缓存 + /// + public const string KeyPermission = "permission:"; + + /// + /// 接口路由 + /// + public const string KeyModules = "modules"; + + /// + /// 系统配置 + /// + public const string KeySystemConfig = "sysConfig"; + + /// + /// 查询过滤器缓存 + /// + public const string KeyQueryFilter = "queryFilter:"; + + /// + /// 机构Id集合缓存 + /// + public const string KeyOrgIdList = "org:"; + + /// + /// 最大角色数据范围缓存 + /// + public const string KeyMaxDataScopeType = "maxDataScopeType:"; + + /// + /// 验证码缓存 + /// + public const string KeyVerCode = "verCode:"; + + /// + /// 所有缓存关键字集合 + /// + public const string KeyAll = "keys"; + + /// + /// 定时任务缓存 + /// + public const string KeyTimer = "timer:"; + + /// + /// 在线用户缓存 + /// + public const string KeyOnlineUser = "onlineuser:"; + + /// + /// 常量下拉框 + /// + public const string KeyConstSelector = "selector:"; + + /// + /// swagger登录缓存 + /// + public const string SwaggerLogin = "swaggerLogin:"; +} \ No newline at end of file diff --git a/Blog.Core.Common/DB/BaseDBConfig.cs b/Blog.Core.Common/DB/BaseDBConfig.cs index 495414fb..b7153766 100644 --- a/Blog.Core.Common/DB/BaseDBConfig.cs +++ b/Blog.Core.Common/DB/BaseDBConfig.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using SqlSugar; namespace Blog.Core.Common.DB { diff --git a/Blog.Core.Common/Extensions/ExpressionExtensions.cs b/Blog.Core.Common/Extensions/ExpressionExtensions.cs index 4058b95a..6591923f 100644 --- a/Blog.Core.Common/Extensions/ExpressionExtensions.cs +++ b/Blog.Core.Common/Extensions/ExpressionExtensions.cs @@ -4,211 +4,213 @@ using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; +using Blog.Core.Common.Caches; namespace Blog.Core.Common.Helper { - /// - /// Linq扩展 - /// - public static class ExpressionExtensions - { - #region HttpContext - - /// - /// 返回请求上下文 - /// - /// - /// - /// - /// - /// - public static async Task Cof_SendResponse(this HttpContext context, System.Net.HttpStatusCode code, string message, string ContentType = "text/html;charset=utf-8") - { - context.Response.StatusCode = (int)code; - context.Response.ContentType = ContentType; - await context.Response.WriteAsync(message); - } - - #endregion - - #region ICaching - - /// - /// 从缓存里取数据,如果不存在则执行查询方法, - /// - /// 类型 - /// ICaching - /// 键值 - /// 查询方法 - /// 有效期 单位分钟/param> - /// - public static T Cof_GetICaching(this ICaching cache, string key, Func GetFun, int timeSpanMin) where T : class - { - var obj = cache.Get(key); - obj = GetFun(); - if (obj == null) - { - obj = GetFun(); - cache.Set(key, obj, timeSpanMin); - } - - return obj as T; - } - - /// - /// 异步从缓存里取数据,如果不存在则执行查询方法 - /// - /// 类型 - /// ICaching - /// 键值 - /// 查询方法 - /// 有效期 单位分钟/param> - /// - public static async Task Cof_AsyncGetICaching(this ICaching cache, string key, Func> GetFun, int timeSpanMin) where T : class - { - var obj = cache.Get(key); - if (obj == null) - { - obj = await GetFun(); - cache.Set(key, obj, timeSpanMin); - } - - return obj as T; - } - - #endregion - - #region 常用扩展方法 - - public static bool Cof_CheckAvailable(this IEnumerable Tlist) - { - return Tlist != null && Tlist.Count() > 0; - } - - /// - /// 调用内部方法 - /// - public static Expression Call(this Expression instance, string methodName, params Expression[] arguments) - { - if (instance.Type == typeof(string)) - return Expression.Call(instance, instance.Type.GetMethod(methodName, new Type[] { typeof(string) }), arguments); //修复string contains 出现的问题 Ambiguous match found. - else - return Expression.Call(instance, instance.Type.GetMethod(methodName), arguments); - } - - /// - /// 获取内部成员 - /// - public static Expression Property(this Expression expression, string propertyName) - { - // Todo:左边条件如果是dynamic, - // 则Expression.Property无法获取子内容 - // 报错在这里,由于expression内的对象为Object,所以无法解析到 - // var x = (expression as IQueryable).ElementType; - var exp = Expression.Property(expression, propertyName); - if (exp.Type.IsGenericType && exp.Type.GetGenericTypeDefinition() == typeof(Nullable<>)) - { - return Expression.Convert(exp, exp.Type.GetGenericArguments()[0]); - } - - return exp; - } - - /// - /// 转Lambda - /// - public static Expression ToLambda(this Expression body, - params ParameterExpression[] parameters) - { - return Expression.Lambda(body, parameters); - } - - #endregion - - #region 常用运算符 [ > , >= , == , < , <= , != , || , && ] - - /// - /// && - /// - public static Expression AndAlso(this Expression left, Expression right) - { - return Expression.AndAlso(left, right); - } - - /// - /// || - /// - public static Expression OrElse(this Expression left, Expression right) - { - return Expression.OrElse(left, right); - } - - /// - /// Contains - /// - public static Expression Contains(this Expression left, Expression right) - { - return left.Call("Contains", right); - } - - public static Expression StartContains(this Expression left, Expression right) - { - return left.Call("StartsWith", right); - } - - public static Expression EndContains(this Expression left, Expression right) - { - return left.Call("EndsWith", right); - } - - /// - /// > - /// - public static Expression GreaterThan(this Expression left, Expression right) - { - return Expression.GreaterThan(left, right); - } - - /// - /// >= - /// - public static Expression GreaterThanOrEqual(this Expression left, Expression right) - { - return Expression.GreaterThanOrEqual(left, right); - } - - /// - /// < - /// - public static Expression LessThan(this Expression left, Expression right) - { - return Expression.LessThan(left, right); - } - - /// - /// <= - /// - public static Expression LessThanOrEqual(this Expression left, Expression right) - { - return Expression.LessThanOrEqual(left, right); - } - - /// - /// == - /// - public static Expression Equal(this Expression left, Expression right) - { - return Expression.Equal(left, right); - } - - /// - /// != - /// - public static Expression NotEqual(this Expression left, Expression right) - { - return Expression.NotEqual(left, right); - } - - #endregion - } + /// + /// Linq扩展 + /// + public static class ExpressionExtensions + { + #region HttpContext + + /// + /// 返回请求上下文 + /// + /// + /// + /// + /// + /// + public static async Task Cof_SendResponse(this HttpContext context, System.Net.HttpStatusCode code, string message, + string ContentType = "text/html;charset=utf-8") + { + context.Response.StatusCode = (int) code; + context.Response.ContentType = ContentType; + await context.Response.WriteAsync(message); + } + + #endregion + + #region ICaching + + /// + /// 从缓存里取数据,如果不存在则执行查询方法, + /// + /// 类型 + /// ICaching + /// 键值 + /// 查询方法 + /// 有效期 单位分钟/param> + /// + public static T Cof_GetICaching(this ICaching cache, string key, Func GetFun, int timeSpanMin) where T : class + { + var obj = cache.Get(key); + if (obj == null) + { + obj = GetFun(); + cache.Set(key, obj, TimeSpan.FromMinutes(timeSpanMin)); + } + + return obj; + } + + /// + /// 异步从缓存里取数据,如果不存在则执行查询方法 + /// + /// 类型 + /// ICaching + /// 键值 + /// 查询方法 + /// 有效期 单位分钟/param> + /// + public static async Task Cof_AsyncGetICaching(this ICaching cache, string key, Func> GetFun, int timeSpanMin) where T : class + { + var obj = await cache.GetAsync(key); + if (obj == null) + { + obj = await GetFun(); + cache.Set(key, obj, TimeSpan.FromMinutes(timeSpanMin)); + } + + return obj; + } + + #endregion + + #region 常用扩展方法 + + public static bool Cof_CheckAvailable(this IEnumerable Tlist) + { + return Tlist != null && Tlist.Count() > 0; + } + + /// + /// 调用内部方法 + /// + public static Expression Call(this Expression instance, string methodName, params Expression[] arguments) + { + if (instance.Type == typeof(string)) + return Expression.Call(instance, instance.Type.GetMethod(methodName, new Type[] {typeof(string)}), + arguments); //修复string contains 出现的问题 Ambiguous match found. + else + return Expression.Call(instance, instance.Type.GetMethod(methodName), arguments); + } + + /// + /// 获取内部成员 + /// + public static Expression Property(this Expression expression, string propertyName) + { + // Todo:左边条件如果是dynamic, + // 则Expression.Property无法获取子内容 + // 报错在这里,由于expression内的对象为Object,所以无法解析到 + // var x = (expression as IQueryable).ElementType; + var exp = Expression.Property(expression, propertyName); + if (exp.Type.IsGenericType && exp.Type.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + return Expression.Convert(exp, exp.Type.GetGenericArguments()[0]); + } + + return exp; + } + + /// + /// 转Lambda + /// + public static Expression ToLambda(this Expression body, + params ParameterExpression[] parameters) + { + return Expression.Lambda(body, parameters); + } + + #endregion + + #region 常用运算符 [ > , >= , == , < , <= , != , || , && ] + + /// + /// && + /// + public static Expression AndAlso(this Expression left, Expression right) + { + return Expression.AndAlso(left, right); + } + + /// + /// || + /// + public static Expression OrElse(this Expression left, Expression right) + { + return Expression.OrElse(left, right); + } + + /// + /// Contains + /// + public static Expression Contains(this Expression left, Expression right) + { + return left.Call("Contains", right); + } + + public static Expression StartContains(this Expression left, Expression right) + { + return left.Call("StartsWith", right); + } + + public static Expression EndContains(this Expression left, Expression right) + { + return left.Call("EndsWith", right); + } + + /// + /// > + /// + public static Expression GreaterThan(this Expression left, Expression right) + { + return Expression.GreaterThan(left, right); + } + + /// + /// >= + /// + public static Expression GreaterThanOrEqual(this Expression left, Expression right) + { + return Expression.GreaterThanOrEqual(left, right); + } + + /// + /// < + /// + public static Expression LessThan(this Expression left, Expression right) + { + return Expression.LessThan(left, right); + } + + /// + /// <= + /// + public static Expression LessThanOrEqual(this Expression left, Expression right) + { + return Expression.LessThanOrEqual(left, right); + } + + /// + /// == + /// + public static Expression Equal(this Expression left, Expression right) + { + return Expression.Equal(left, right); + } + + /// + /// != + /// + public static Expression NotEqual(this Expression left, Expression right) + { + return Expression.NotEqual(left, right); + } + + #endregion + } } \ No newline at end of file diff --git a/Blog.Core.Common/MemoryCache/ICachingProvider.cs b/Blog.Core.Common/MemoryCache/ICachingProvider.cs deleted file mode 100644 index e6a06529..00000000 --- a/Blog.Core.Common/MemoryCache/ICachingProvider.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Blog.Core.Common -{ - /// - /// 简单的缓存接口,只有查询和添加,以后会进行扩展 - /// - public interface ICaching - { - object Get(string cacheKey); - - void Set(string cacheKey, object cacheValue, int timeSpan); - } -} diff --git a/Blog.Core.Common/MemoryCache/MemoryCaching.cs b/Blog.Core.Common/MemoryCache/MemoryCaching.cs deleted file mode 100644 index fc9a60c9..00000000 --- a/Blog.Core.Common/MemoryCache/MemoryCaching.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Microsoft.Extensions.Caching.Memory; -using System; - -namespace Blog.Core.Common -{ - /// - /// 实例化缓存接口ICaching - /// - public class MemoryCaching : ICaching - { - //引用Microsoft.Extensions.Caching.Memory;这个和.net 还是不一样,没有了Httpruntime了 - private readonly IMemoryCache _cache; - //还是通过构造函数的方法,获取 - public MemoryCaching(IMemoryCache cache) - { - _cache = cache; - } - - public object Get(string cacheKey) - { - return _cache.Get(cacheKey); - } - - public void Set(string cacheKey, object cacheValue,int timeSpan) - { - _cache.Set(cacheKey, cacheValue, TimeSpan.FromSeconds(timeSpan * 60)); - } - } - -} diff --git a/Blog.Core.Common/Option/RedisOptions.cs b/Blog.Core.Common/Option/RedisOptions.cs new file mode 100644 index 00000000..27326595 --- /dev/null +++ b/Blog.Core.Common/Option/RedisOptions.cs @@ -0,0 +1,24 @@ +using Blog.Core.Common.Option.Core; + +namespace Blog.Core.Common.Option; + +/// +/// 缓存配置选项 +/// +public sealed class RedisOptions : IConfigurableOptions +{ + /// + /// 是否启用 + /// + public bool Enable { get; set; } + + /// + /// Redis连接 + /// + public string ConnectionString { get; set; } + + /// + /// 键值前缀 + /// + public string InstanceName { get; set; } +} \ No newline at end of file diff --git a/Blog.Core.Extensions/AOP/BlogCacheAOP.cs b/Blog.Core.Extensions/AOP/BlogCacheAOP.cs index a3a52f58..d872bab5 100644 --- a/Blog.Core.Extensions/AOP/BlogCacheAOP.cs +++ b/Blog.Core.Extensions/AOP/BlogCacheAOP.cs @@ -1,6 +1,8 @@ -using Blog.Core.Common; +using System; +using Blog.Core.Common; using Castle.DynamicProxy; using System.Linq; +using Blog.Core.Common.Caches; namespace Blog.Core.AOP { @@ -28,7 +30,7 @@ public override void Intercept(IInvocation invocation) //获取自定义缓存键 var cacheKey = CustomCacheKey(invocation); //根据key获取相应的缓存值 - var cacheValue = _cache.Get(cacheKey); + var cacheValue = _cache.Get(cacheKey); if (cacheValue != null) { //将当前获取到的缓存值,赋值给当前执行方法 @@ -40,7 +42,7 @@ public override void Intercept(IInvocation invocation) //存入缓存 if (!string.IsNullOrWhiteSpace(cacheKey)) { - _cache.Set(cacheKey, invocation.ReturnValue, qCachingAttribute.AbsoluteExpiration); + _cache.Set(cacheKey, invocation.ReturnValue, TimeSpan.FromMinutes(qCachingAttribute.AbsoluteExpiration)); } } else diff --git a/Blog.Core.Extensions/Blog.Core.Extensions.csproj b/Blog.Core.Extensions/Blog.Core.Extensions.csproj index 1dfbbc48..b0c2408d 100644 --- a/Blog.Core.Extensions/Blog.Core.Extensions.csproj +++ b/Blog.Core.Extensions/Blog.Core.Extensions.csproj @@ -15,6 +15,7 @@ + diff --git a/Blog.Core.Extensions/Redis/IRedisBasketRepository.cs b/Blog.Core.Extensions/Redis/IRedisBasketRepository.cs index ca1827fd..08effd1b 100644 --- a/Blog.Core.Extensions/Redis/IRedisBasketRepository.cs +++ b/Blog.Core.Extensions/Redis/IRedisBasketRepository.cs @@ -8,6 +8,7 @@ namespace Blog.Core.Extensions /// /// Redis缓存接口 /// + [Obsolete("普通缓存考虑直接使用ICaching,如果要使用Redis队列等还是使用此类")] public interface IRedisBasketRepository { diff --git a/Blog.Core.Extensions/Redis/RedisBasketRepository.cs b/Blog.Core.Extensions/Redis/RedisBasketRepository.cs index 31c7a030..7f1356e2 100644 --- a/Blog.Core.Extensions/Redis/RedisBasketRepository.cs +++ b/Blog.Core.Extensions/Redis/RedisBasketRepository.cs @@ -9,6 +9,7 @@ namespace Blog.Core.Extensions { + [Obsolete("普通缓存考虑直接使用ICaching,如果要使用Redis队列等还是使用此类")] public class RedisBasketRepository : IRedisBasketRepository { private readonly ILogger _logger; diff --git a/Blog.Core.Extensions/ServiceExtensions/CacheSetup.cs b/Blog.Core.Extensions/ServiceExtensions/CacheSetup.cs new file mode 100644 index 00000000..3744acd9 --- /dev/null +++ b/Blog.Core.Extensions/ServiceExtensions/CacheSetup.cs @@ -0,0 +1,45 @@ +using Blog.Core.Common; +using Blog.Core.Common.Caches; +using Blog.Core.Common.Option; +using Microsoft.Extensions.DependencyInjection; +using StackExchange.Redis; + +namespace Blog.Core.Extensions.ServiceExtensions; + +public static class CacheSetup +{ + /// + /// 统一注册缓存 + /// + /// + public static void AddCacheSetup(this IServiceCollection services) + { + var cacheOptions = App.GetOptions(); + if (cacheOptions.Enable) + { + //使用Redis + services.AddStackExchangeRedisCache(options => + { + options.Configuration = cacheOptions.ConnectionString; + if (!cacheOptions.InstanceName.IsNullOrEmpty()) options.InstanceName = cacheOptions.InstanceName; + }); + + services.AddTransient(); + // 配置启动Redis服务,虽然可能影响项目启动速度,但是不能在运行的时候报错,所以是合理的 + services.AddSingleton(sp => + { + //获取连接字符串 + var configuration = ConfigurationOptions.Parse(cacheOptions.ConnectionString, true); + configuration.ResolveDns = true; + return ConnectionMultiplexer.Connect(configuration); + }); + } + else + { + //使用内存 + services.AddDistributedMemoryCache(); + } + + services.AddSingleton(); + } +} \ No newline at end of file diff --git a/Blog.Core.Extensions/ServiceExtensions/HttpRuntimeCache.cs b/Blog.Core.Extensions/ServiceExtensions/HttpRuntimeCache.cs deleted file mode 100644 index b9b4d7b3..00000000 --- a/Blog.Core.Extensions/ServiceExtensions/HttpRuntimeCache.cs +++ /dev/null @@ -1,67 +0,0 @@ -using Microsoft.Extensions.Caching.Memory; -using SqlSugar; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Reflection; - -namespace Blog.Core.Extensions -{ - /// - /// 实现SqlSugar的ICacheService接口 - /// - public class SqlSugarMemoryCacheService : ICacheService - { - protected IMemoryCache _memoryCache; - public SqlSugarMemoryCacheService(IMemoryCache memoryCache) - { - _memoryCache = memoryCache; - } - public void Add(string key, V value) - { - _memoryCache.Set(key, value); - } - public void Add(string key, V value, int cacheDurationInSeconds) - { - _memoryCache.Set(key, value, DateTimeOffset.Now.AddSeconds(cacheDurationInSeconds)); - } - public bool ContainsKey(string key) - { - return _memoryCache.TryGetValue(key, out _); - } - - public V Get(string key) - { - return _memoryCache.Get(key); - } - - public IEnumerable GetAllKey() - { - const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; - var entries = _memoryCache.GetType().GetField("_entries", flags).GetValue(_memoryCache); - var cacheItems = entries as IDictionary; - var keys = new List(); - if (cacheItems == null) return keys; - foreach (DictionaryEntry cacheItem in cacheItems) - { - keys.Add(cacheItem.Key.ToString()); - } - return keys; - } - - public V GetOrCreate(string cacheKey, Func create, int cacheDurationInSeconds = int.MaxValue) - { - if (!_memoryCache.TryGetValue(cacheKey, out V value)) - { - value = create(); - _memoryCache.Set(cacheKey, value, DateTime.Now.AddSeconds(cacheDurationInSeconds)); - } - return value; - } - - public void Remove(string key) - { - _memoryCache.Remove(key); - } - } -} diff --git a/Blog.Core.Extensions/ServiceExtensions/MemoryCacheSetup.cs b/Blog.Core.Extensions/ServiceExtensions/MemoryCacheSetup.cs deleted file mode 100644 index 0e489681..00000000 --- a/Blog.Core.Extensions/ServiceExtensions/MemoryCacheSetup.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Blog.Core.Common; -using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using System; - -namespace Blog.Core.Extensions -{ - /// - /// Memory缓存 启动服务 - /// - public static class MemoryCacheSetup - { - public static void AddMemoryCacheSetup(this IServiceCollection services) - { - if (services == null) throw new ArgumentNullException(nameof(services)); - - services.AddScoped(); - services.AddSingleton(factory => - { - var value = factory.GetRequiredService>(); - var cache = new MemoryCache(value); - return cache; - }); - } - } -} diff --git a/Blog.Core.Extensions/ServiceExtensions/RedisCacheSetup.cs b/Blog.Core.Extensions/ServiceExtensions/RedisCacheSetup.cs deleted file mode 100644 index e070cbd6..00000000 --- a/Blog.Core.Extensions/ServiceExtensions/RedisCacheSetup.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Blog.Core.Common; -using Microsoft.Extensions.DependencyInjection; -using StackExchange.Redis; -using System; - -namespace Blog.Core.Extensions -{ - /// - /// Redis缓存 启动服务 - /// - public static class RedisCacheSetup - { - public static void AddRedisCacheSetup(this IServiceCollection services) - { - if (services == null) throw new ArgumentNullException(nameof(services)); - - services.AddTransient(); - - // 配置启动Redis服务,虽然可能影响项目启动速度,但是不能在运行的时候报错,所以是合理的 - services.AddSingleton(sp => - { - //获取连接字符串 - string redisConfiguration = AppSettings.app(new string[] { "Redis", "ConnectionString" }); - - var configuration = ConfigurationOptions.Parse(redisConfiguration, true); - - configuration.ResolveDns = true; - - return ConnectionMultiplexer.Connect(configuration); - }); - - } - } -} diff --git a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs index 4ac2d2e8..dd5c02b3 100644 --- a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs @@ -10,6 +10,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Blog.Core.Common.Caches; namespace Blog.Core.Extensions { @@ -57,6 +58,7 @@ public static void AddSqlsugarSetup(this IServiceCollection services) // 自定义特性 ConfigureExternalServices = new ConfigureExternalServices() { + DataInfoCacheService = new SqlSugarCacheService(), EntityService = (property, column) => { if (column.IsPrimarykey && property.PropertyType == typeof(int)) @@ -83,19 +85,11 @@ public static void AddSqlsugarSetup(this IServiceCollection services) { throw new ApplicationException("未配置Log库连接"); } - - + // SqlSugarScope是线程安全,可使用单例注入 // 参考:https://www.donet5.com/Home/Doc?typeId=1181 services.AddSingleton(o => { - var memoryCache = o.GetRequiredService(); - - foreach (var config in BaseDBConfig.AllConfigs) - { - config.ConfigureExternalServices.DataInfoCacheService = new SqlSugarMemoryCacheService(memoryCache); - } - return new SqlSugarScope(BaseDBConfig.AllConfigs, db => { BaseDBConfig.ValidConfig.ForEach(config => diff --git a/Blog.Core.Gateway/Helper/CustomJwtTokenAuthMiddleware.cs b/Blog.Core.Gateway/Helper/CustomJwtTokenAuthMiddleware.cs index 88181bc3..b00db95d 100644 --- a/Blog.Core.Gateway/Helper/CustomJwtTokenAuthMiddleware.cs +++ b/Blog.Core.Gateway/Helper/CustomJwtTokenAuthMiddleware.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Blog.Core.Common; +using Blog.Core.Common.Caches; using Blog.Core.Common.Helper; using Nacos.V2; using Newtonsoft.Json.Linq; diff --git a/Blog.Core.Serilog/Utility/SerilogRequestUtility.cs b/Blog.Core.Serilog/Utility/SerilogRequestUtility.cs index 289f0aae..888d2dc7 100644 --- a/Blog.Core.Serilog/Utility/SerilogRequestUtility.cs +++ b/Blog.Core.Serilog/Utility/SerilogRequestUtility.cs @@ -8,64 +8,66 @@ namespace Blog.Core.Serilog.Utility; public class SerilogRequestUtility { - public const string HttpMessageTemplate = - "HTTP {RequestMethod} {RequestPath} QueryString:{QueryString} Body:{Body} responded {StatusCode} in {Elapsed:0.0000} ms"; + public const string HttpMessageTemplate = + "HTTP {RequestMethod} {RequestPath} QueryString:{QueryString} Body:{Body} responded {StatusCode} in {Elapsed:0.0000} ms"; - private static readonly List _ignoreUrl = new() - { - "/job", - }; + private static readonly List _ignoreUrl = new() + { + "/job", + }; - private static LogEventLevel DefaultGetLevel(HttpContext ctx, - double _, - Exception? ex) - { - return ex is null && ctx.Response.StatusCode <= 499 ? LogEventLevel.Information : LogEventLevel.Error; - } + private static LogEventLevel DefaultGetLevel(HttpContext ctx, + double _, + Exception? ex) + { + return ex is null && ctx.Response.StatusCode <= 499 ? LogEventLevel.Information : LogEventLevel.Error; + } - public static LogEventLevel GetRequestLevel(HttpContext ctx, double _, Exception? ex) => - ex is null && ctx.Response.StatusCode <= 499 ? IgnoreRequest(ctx) : LogEventLevel.Error; + public static LogEventLevel GetRequestLevel(HttpContext ctx, double _, Exception? ex) => + ex is null && ctx.Response.StatusCode <= 499 ? IgnoreRequest(ctx) : LogEventLevel.Error; - private static LogEventLevel IgnoreRequest(HttpContext ctx) - { - var path = ctx.Request.Path.Value; - if (path.IsNullOrEmpty()) - { - return LogEventLevel.Information; - } + private static LogEventLevel IgnoreRequest(HttpContext ctx) + { + var path = ctx.Request.Path.Value; + if (path.IsNullOrEmpty()) + { + return LogEventLevel.Information; + } - return _ignoreUrl.Any(s => path.StartsWith(s)) ? LogEventLevel.Verbose : LogEventLevel.Information; - } + return _ignoreUrl.Any(s => path.StartsWith(s)) ? LogEventLevel.Verbose : LogEventLevel.Information; + } - /// - /// 从Request中增加附属属性 - /// - /// - /// - public static void EnrichFromRequest(IDiagnosticContext diagnosticContext, HttpContext httpContext) - { - var request = httpContext.Request; + /// + /// 从Request中增加附属属性 + /// + /// + /// + public static void EnrichFromRequest(IDiagnosticContext diagnosticContext, HttpContext httpContext) + { + var request = httpContext.Request; - diagnosticContext.Set("RequestHost", request.Host); - diagnosticContext.Set("RequestScheme", request.Scheme); - diagnosticContext.Set("Protocol", request.Protocol); - diagnosticContext.Set("RequestIp", httpContext.GetRequestIp()); + diagnosticContext.Set("RequestHost", request.Host); + diagnosticContext.Set("RequestScheme", request.Scheme); + diagnosticContext.Set("Protocol", request.Protocol); + diagnosticContext.Set("RequestIp", httpContext.GetRequestIp()); - if (request.Method == HttpMethods.Get) - { - diagnosticContext.Set("QueryString", request.QueryString.HasValue ? request.QueryString.Value : string.Empty); - } - else - { - diagnosticContext.Set("QueryString", request.QueryString.HasValue ? request.QueryString.Value : string.Empty); - diagnosticContext.Set("Body", request.ContentLength > 0 ? request.GetRequestBody() : string.Empty); - } - diagnosticContext.Set("ContentType", httpContext.Response.ContentType); + if (request.Method == HttpMethods.Get) + { + diagnosticContext.Set("QueryString", request.QueryString.HasValue ? request.QueryString.Value : string.Empty); + diagnosticContext.Set("Body", string.Empty); + } + else + { + diagnosticContext.Set("QueryString", request.QueryString.HasValue ? request.QueryString.Value : string.Empty); + diagnosticContext.Set("Body", request.ContentLength > 0 ? request.GetRequestBody() : string.Empty); + } - var endpoint = httpContext.GetEndpoint(); - if (endpoint != null) - { - diagnosticContext.Set("EndpointName", endpoint.DisplayName); - } - } + diagnosticContext.Set("ContentType", httpContext.Response.ContentType); + + var endpoint = httpContext.GetEndpoint(); + if (endpoint != null) + { + diagnosticContext.Set("EndpointName", endpoint.DisplayName); + } + } } \ No newline at end of file From 1f35642400667046aaca2b9ef8ae9ac552996712 Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Thu, 1 Jun 2023 17:58:46 +0800 Subject: [PATCH 45/84] =?UTF-8?q?=F0=9F=8E=A8=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E5=A4=9A=E4=BD=99=E7=9A=84=E6=B3=A8=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Api/Program.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Blog.Core.Api/Program.cs b/Blog.Core.Api/Program.cs index 4311c97c..74bcd311 100644 --- a/Blog.Core.Api/Program.cs +++ b/Blog.Core.Api/Program.cs @@ -95,7 +95,6 @@ builder.Services.Configure(x => x.AllowSynchronousIO = true) .Configure(x => x.AllowSynchronousIO = true); -builder.Services.AddDistributedMemoryCache(); builder.Services.AddSession(); builder.Services.AddHttpPollySetup(); builder.Services.AddControllers(o => From 4e23f086542b3dbe29e02e8b0036119de28d91db Mon Sep 17 00:00:00 2001 From: Nine Date: Fri, 2 Jun 2023 14:11:09 +0800 Subject: [PATCH 46/84] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dmini-profiler=20?= =?UTF-8?q?=E4=B8=8D=E6=98=BE=E7=A4=BA=E7=9A=84=E9=97=AE=E9=A2=98=EF=BC=8C?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E4=B8=8D=E4=B8=80=E8=87=B4=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=20=E4=BF=AE=E6=94=B9=E4=B8=80=E4=BA=9Bswagger=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F=EF=BC=8C=E6=9D=83=E9=99=90=E5=B0=8F=E9=94=81=EF=BC=8C?= =?UTF-8?q?=E6=9D=83=E9=99=90=E8=AE=A4=E8=AF=81=E6=8C=89=E9=92=AE..?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Api/index.html | 103 ++++++++++++++++++++++++++++++++++----- 1 file changed, 92 insertions(+), 11 deletions(-) diff --git a/Blog.Core.Api/index.html b/Blog.Core.Api/index.html index b0092a62..609a60cf 100644 --- a/Blog.Core.Api/index.html +++ b/Blog.Core.Api/index.html @@ -1,8 +1,6 @@  - + @@ -10,14 +8,25 @@ - + %(DocumentTitle) + %(HeadContent) @@ -95,10 +123,16 @@ From e9b4531ff8bfa0393dd4247376177c6cdea9d930 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Fri, 2 Jun 2023 16:34:07 +0800 Subject: [PATCH 47/84] Update index.html --- Blog.Core.Api/index.html | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/Blog.Core.Api/index.html b/Blog.Core.Api/index.html index 609a60cf..388d79ad 100644 --- a/Blog.Core.Api/index.html +++ b/Blog.Core.Api/index.html @@ -178,25 +178,27 @@ //修改权限小锁样式,由于这个单页面没去获取token,采取直接修改样式的方法 $(document).ready(function () { - document.querySelector('.auth-wrapper svg use').href.baseVal = '#locked' - let divList = document.querySelectorAll('.opblock-tag') - for (let div of divList) { - //debugger - div.addEventListener('click', function () { - setTimeout(() => { - //修改锁状态 locked 为已认证token unlocked为未认证 - let list = document.querySelectorAll(' .unlocked svg use') - for (let item of list) { - item.href.baseVal = '#locked' - } - //修改锁颜色 locked 黑色 unlocked灰色 - let btnlist = document.querySelectorAll('.authorization__btn') - for (let item of btnlist) { - item.classList = 'authorization__btn locked' - } - }, 0) - }) - } + setTimeout(() => { + document.querySelector('.auth-wrapper svg use').href.baseVal = '#locked' + let divList = document.querySelectorAll('.opblock-tag') + for (let div of divList) { + //debugger + div.addEventListener('click', function () { + setTimeout(() => { + //修改锁状态 locked 为已认证token unlocked为未认证 + let list = document.querySelectorAll(' .unlocked svg use') + for (let item of list) { + item.href.baseVal = '#locked' + } + //修改锁颜色 locked 黑色 unlocked灰色 + let btnlist = document.querySelectorAll('.authorization__btn') + for (let item of btnlist) { + item.classList = 'authorization__btn locked' + } + }, 0) + }) + } + }, 500) }); }, 1000) // 文档logo From 2c74a716ca342f20359e181a27ce0f2f4bbfcd11 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Mon, 5 Jun 2023 11:55:23 +0800 Subject: [PATCH 48/84] Update swg-login.html --- Blog.Core.Api/wwwroot/swg-login.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Blog.Core.Api/wwwroot/swg-login.html b/Blog.Core.Api/wwwroot/swg-login.html index 93653957..d361cdef 100644 --- a/Blog.Core.Api/wwwroot/swg-login.html +++ b/Blog.Core.Api/wwwroot/swg-login.html @@ -15,7 +15,7 @@
欢迎使用!
-
使用用户账号登录
+
使用真实用户账号登录,测试账号: blogadmin/blogadmin
From 96e21474bcb66c7ffc1379f39c0da06081841788 Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Wed, 7 Jun 2023 15:25:13 +0800 Subject: [PATCH 49/84] =?UTF-8?q?=F0=9F=8E=A8=E4=BC=98=E5=8C=96=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E6=B3=A8=E5=85=A5=E3=80=81=E7=A7=BB=E9=99=A4=E5=A4=9A?= =?UTF-8?q?=E4=BD=99=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Redis/IRedisCacheManager.cs | 29 --- .../Redis/RedisCacheManager.cs | 165 ------------------ .../ServiceExtensions/CacheSetup.cs | 73 ++++---- 3 files changed, 38 insertions(+), 229 deletions(-) delete mode 100644 Blog.Core.Extensions/Redis/IRedisCacheManager.cs delete mode 100644 Blog.Core.Extensions/Redis/RedisCacheManager.cs diff --git a/Blog.Core.Extensions/Redis/IRedisCacheManager.cs b/Blog.Core.Extensions/Redis/IRedisCacheManager.cs deleted file mode 100644 index 56cd4f84..00000000 --- a/Blog.Core.Extensions/Redis/IRedisCacheManager.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; - -namespace Blog.Core.Extensions -{ - /// - /// Redis缓存接口 - /// - public interface IRedisCacheManager - { - - //获取 Reids 缓存值 - string GetValue(string key); - - //获取值,并序列化 - TEntity Get(string key); - - //保存 - void Set(string key, object value, TimeSpan cacheTime); - - //判断是否存在 - bool Get(string key); - - //移除某一个缓存值 - void Remove(string key); - - //全部清除 - void Clear(); - } -} diff --git a/Blog.Core.Extensions/Redis/RedisCacheManager.cs b/Blog.Core.Extensions/Redis/RedisCacheManager.cs deleted file mode 100644 index 0e5d7819..00000000 --- a/Blog.Core.Extensions/Redis/RedisCacheManager.cs +++ /dev/null @@ -1,165 +0,0 @@ -using Blog.Core.Common; -using StackExchange.Redis; -using System; - -namespace Blog.Core.Extensions -{ - public class RedisCacheManager : IRedisCacheManager - { - - private readonly string redisConnenctionString; - - public volatile ConnectionMultiplexer redisConnection; - - private readonly object redisConnectionLock = new object(); - - public RedisCacheManager() - { - string redisConfiguration = AppSettings.app(new string[] { "AppSettings", "RedisCachingAOP", "ConnectionString" });//获取连接字符串 - - if (string.IsNullOrWhiteSpace(redisConfiguration)) - { - throw new ArgumentException("redis config is empty", nameof(redisConfiguration)); - } - this.redisConnenctionString = redisConfiguration; - this.redisConnection = GetRedisConnection(); - } - - /// - /// 核心代码,获取连接实例 - /// 通过双if 夹lock的方式,实现单例模式 - /// - /// - private ConnectionMultiplexer GetRedisConnection() - { - //如果已经连接实例,直接返回 - if (this.redisConnection != null && this.redisConnection.IsConnected) - { - return this.redisConnection; - } - //加锁,防止异步编程中,出现单例无效的问题 - lock (redisConnectionLock) - { - if (this.redisConnection != null) - { - //释放redis连接 - this.redisConnection.Dispose(); - } - try - { - var config = new ConfigurationOptions - { - AbortOnConnectFail = false, - AllowAdmin = true, - ConnectTimeout = 15000,//改成15s - SyncTimeout = 5000, - //Password = "Pwd",//Redis数据库密码 - EndPoints = { redisConnenctionString }// connectionString 为IP:Port 如”192.168.2.110:6379” - }; - this.redisConnection = ConnectionMultiplexer.Connect(config); - } - catch (Exception) - { - throw new Exception("Redis服务未启用,请开启该服务,并且请注意端口号,本项目使用的的6319,而且我的是没有设置密码。"); - } - } - return this.redisConnection; - } - /// - /// 清除 - /// - public void Clear() - { - foreach (var endPoint in this.GetRedisConnection().GetEndPoints()) - { - var server = this.GetRedisConnection().GetServer(endPoint); - foreach (var key in server.Keys()) - { - redisConnection.GetDatabase().KeyDelete(key); - } - } - } - /// - /// 判断是否存在 - /// - /// - /// - public bool Get(string key) - { - return redisConnection.GetDatabase().KeyExists(key); - } - - /// - /// 查询 - /// - /// - /// - public string GetValue(string key) - { - return redisConnection.GetDatabase().StringGet(key); - } - - /// - /// 获取 - /// - /// - /// - /// - public TEntity Get(string key) - { - var value = redisConnection.GetDatabase().StringGet(key); - if (value.HasValue) - { - //需要用的反序列化,将Redis存储的Byte[],进行反序列化 - return SerializeHelper.Deserialize(value); - } - else - { - return default(TEntity); - } - } - - /// - /// 移除 - /// - /// - public void Remove(string key) - { - redisConnection.GetDatabase().KeyDelete(key); - } - /// - /// 设置 - /// - /// - /// - /// - public void Set(string key, object value, TimeSpan cacheTime) - { - if (value != null) - { - if (value is string cacheValue) - { - // 字符串无需序列化 - redisConnection.GetDatabase().StringSet(key, cacheValue, cacheTime); - } - else - { - //序列化,将object值生成RedisValue - redisConnection.GetDatabase().StringSet(key, SerializeHelper.Serialize(value), cacheTime); - } - } - } - - /// - /// 增加/修改 - /// - /// - /// - /// - public bool SetValue(string key, byte[] value) - { - return redisConnection.GetDatabase().StringSet(key, value, TimeSpan.FromSeconds(120)); - } - - } -} diff --git a/Blog.Core.Extensions/ServiceExtensions/CacheSetup.cs b/Blog.Core.Extensions/ServiceExtensions/CacheSetup.cs index 3744acd9..579da785 100644 --- a/Blog.Core.Extensions/ServiceExtensions/CacheSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/CacheSetup.cs @@ -1,4 +1,5 @@ -using Blog.Core.Common; +using System.Threading.Tasks; +using Blog.Core.Common; using Blog.Core.Common.Caches; using Blog.Core.Common.Option; using Microsoft.Extensions.DependencyInjection; @@ -8,38 +9,40 @@ namespace Blog.Core.Extensions.ServiceExtensions; public static class CacheSetup { - /// - /// 统一注册缓存 - /// - /// - public static void AddCacheSetup(this IServiceCollection services) - { - var cacheOptions = App.GetOptions(); - if (cacheOptions.Enable) - { - //使用Redis - services.AddStackExchangeRedisCache(options => - { - options.Configuration = cacheOptions.ConnectionString; - if (!cacheOptions.InstanceName.IsNullOrEmpty()) options.InstanceName = cacheOptions.InstanceName; - }); - - services.AddTransient(); - // 配置启动Redis服务,虽然可能影响项目启动速度,但是不能在运行的时候报错,所以是合理的 - services.AddSingleton(sp => - { - //获取连接字符串 - var configuration = ConfigurationOptions.Parse(cacheOptions.ConnectionString, true); - configuration.ResolveDns = true; - return ConnectionMultiplexer.Connect(configuration); - }); - } - else - { - //使用内存 - services.AddDistributedMemoryCache(); - } - - services.AddSingleton(); - } + /// + /// 统一注册缓存 + /// + /// + public static void AddCacheSetup(this IServiceCollection services) + { + var cacheOptions = App.GetOptions(); + if (cacheOptions.Enable) + { + // 配置启动Redis服务,虽然可能影响项目启动速度,但是不能在运行的时候报错,所以是合理的 + services.AddSingleton(sp => + { + //获取连接字符串 + var configuration = ConfigurationOptions.Parse(cacheOptions.ConnectionString, true); + configuration.ResolveDns = true; + return ConnectionMultiplexer.Connect(configuration); + }); + services.AddSingleton(p => p.GetService() as ConnectionMultiplexer); + //使用Redis + services.AddStackExchangeRedisCache(options => + { + options.ConnectionMultiplexerFactory = () => Task.FromResult(App.GetService()); + if (!cacheOptions.InstanceName.IsNullOrEmpty()) options.InstanceName = cacheOptions.InstanceName; + }); + + services.AddTransient(); + } + else + { + //使用内存 + services.AddMemoryCache(); + services.AddDistributedMemoryCache(); + } + + services.AddSingleton(); + } } \ No newline at end of file From da6a7546aa56b49c90b19ef23f10b136d82f3213 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BD=95=E6=8B=BE=E7=8E=96?= Date: Fri, 9 Jun 2023 17:55:20 +0800 Subject: [PATCH 50/84] =?UTF-8?q?Update=20index.html=20=E6=9B=B4=E6=94=B9?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=90=8D=E7=A7=B0=E4=B8=8D=E8=83=BD=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E5=A4=8D=E5=88=B6=E7=9A=84=E9=97=AE=E9=A2=98=EF=BC=8C?= =?UTF-8?q?=E5=8E=BB=E9=99=A4=E6=8E=A5=E5=8F=A3border=E9=80=89=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E9=BB=91=E8=89=B2=E8=BE=B9=E6=A1=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Api/index.html | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Blog.Core.Api/index.html b/Blog.Core.Api/index.html index 388d79ad..eca0b424 100644 --- a/Blog.Core.Api/index.html +++ b/Blog.Core.Api/index.html @@ -17,6 +17,9 @@ %(HeadContent) - + From 3a25de3fc30f91fde762353f072cc5d1b995bc45 Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Sat, 10 Jun 2023 11:23:54 +0800 Subject: [PATCH 51/84] =?UTF-8?q?=E2=9C=A8=20=E4=BC=98=E5=8C=96Redis?= =?UTF-8?q?=E6=B3=A8=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Extensions/ServiceExtensions/CacheSetup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Blog.Core.Extensions/ServiceExtensions/CacheSetup.cs b/Blog.Core.Extensions/ServiceExtensions/CacheSetup.cs index 579da785..eb810f52 100644 --- a/Blog.Core.Extensions/ServiceExtensions/CacheSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/CacheSetup.cs @@ -30,7 +30,7 @@ public static void AddCacheSetup(this IServiceCollection services) //使用Redis services.AddStackExchangeRedisCache(options => { - options.ConnectionMultiplexerFactory = () => Task.FromResult(App.GetService()); + options.ConnectionMultiplexerFactory = () => Task.FromResult(App.GetService(false)); if (!cacheOptions.InstanceName.IsNullOrEmpty()) options.InstanceName = cacheOptions.InstanceName; }); From 7ea8d3565d8888ca8a66d716ab890a753cdd013f Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Sat, 10 Jun 2023 11:27:27 +0800 Subject: [PATCH 52/84] Update appsettings.json --- Blog.Core.Api/appsettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Blog.Core.Api/appsettings.json b/Blog.Core.Api/appsettings.json index d220a034..73637e19 100644 --- a/Blog.Core.Api/appsettings.json +++ b/Blog.Core.Api/appsettings.json @@ -18,7 +18,7 @@ }, "AllowedHosts": "*", "Redis": { - "Enable": true, + "Enable": false, "ConnectionString": "127.0.0.1:6379", "InstanceName": "" //前缀 }, From 70134714669c1bcf0fedf179f034d09b1859aaa6 Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Sat, 10 Jun 2023 11:53:12 +0800 Subject: [PATCH 53/84] =?UTF-8?q?=F0=9F=90=9B=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E5=B7=B2=E7=9F=A5=E7=9A=84DynamicLambda?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在双引号后又空格会解析错误 --- Blog.Core.Common/Helper/DynamicLinqFactory.cs | 1 + Blog.Core.Tests/Common_Test/DynamicLambdaTest.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/Blog.Core.Common/Helper/DynamicLinqFactory.cs b/Blog.Core.Common/Helper/DynamicLinqFactory.cs index 7d45bbd2..248b18ff 100644 --- a/Blog.Core.Common/Helper/DynamicLinqFactory.cs +++ b/Blog.Core.Common/Helper/DynamicLinqFactory.cs @@ -348,6 +348,7 @@ public static string FormatString(string str) } else { + if (i == firstIndex) continue; if (i == str.Length - 1 || str[i + 1].IsNullOrEmpty()) { lastIndex = i; diff --git a/Blog.Core.Tests/Common_Test/DynamicLambdaTest.cs b/Blog.Core.Tests/Common_Test/DynamicLambdaTest.cs index 8f6ad096..d67a67e0 100644 --- a/Blog.Core.Tests/Common_Test/DynamicLambdaTest.cs +++ b/Blog.Core.Tests/Common_Test/DynamicLambdaTest.cs @@ -66,6 +66,7 @@ public async void Get_Blogs_DynamicTest() await TestConditions("bId=2"); await TestConditions("bId in (1,2,3,4,5)"); await TestConditions("bId in (1,2,3,4,5)|| bUpdateTime>=\"2019-01-01 01:01:01\""); + await TestConditions("btitle like \" 测试数据\""); await TestConditions("btitle like \"测试数据\" && bId>0"); await TestConditions("btitle like \"测试!@#$%^&*()_+|}{\":<>?LP\"数据\" && bId>0"); await TestConditions("btitle like \"测试!@+)(*()_&%^&^$^%$IUYWIQOJVLXKZM>?Z<>??LP\"数据\" && bId>0"); From 04d3cd4865e2c018202cc07c99ba6ab3d2ec7c9b Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Sat, 10 Jun 2023 11:59:11 +0800 Subject: [PATCH 54/84] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20=E5=8D=87=E7=BA=A7Sq?= =?UTF-8?q?lSugar=E4=BE=9D=E8=B5=96=205.1.3.49=20->=205.1.4.83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 优化Aop Sql日志记录 原有的UtilMethods.GetSqlString 有较大的性能影响,参数越多影响越大 使用最新的建议的 UtilMethods.GetNativeSql 参考 https://www.donet5.com/home/doc?masterId=1&typeId=1204 --- Blog.Core.Common/DB/Aop/SqlsugarAop.cs | 2 +- Blog.Core.Model/Blog.Core.Model.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Blog.Core.Common/DB/Aop/SqlsugarAop.cs b/Blog.Core.Common/DB/Aop/SqlsugarAop.cs index 3a595e67..f165ef43 100644 --- a/Blog.Core.Common/DB/Aop/SqlsugarAop.cs +++ b/Blog.Core.Common/DB/Aop/SqlsugarAop.cs @@ -26,7 +26,7 @@ public static void OnLogExecuting(ISqlSugarClient sqlSugarScopeProvider, string using (LogContextExtension.Create.SqlAopPushProperty(sqlSugarScopeProvider)) { Log.Information("------------------ \r\n ConnId:[{ConnId}]【SQL语句】: \r\n {Sql}", - config.ConfigId, UtilMethods.GetSqlString(config.DbType, sql, p)); + config.ConfigId, UtilMethods.GetNativeSql( sql, p)); } } } diff --git a/Blog.Core.Model/Blog.Core.Model.csproj b/Blog.Core.Model/Blog.Core.Model.csproj index e66c5c5b..ac851de2 100644 --- a/Blog.Core.Model/Blog.Core.Model.csproj +++ b/Blog.Core.Model/Blog.Core.Model.csproj @@ -16,7 +16,7 @@ - + From 0ddaa35bed9ba0536688f356c1766972380c8cf4 Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Mon, 12 Jun 2023 10:52:11 +0800 Subject: [PATCH 55/84] =?UTF-8?q?=F0=9F=8E=A8=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E9=9B=86=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Extensions/RuntimeExtension.cs | 130 +++++++++--------- 1 file changed, 66 insertions(+), 64 deletions(-) diff --git a/Blog.Core.Common/Extensions/RuntimeExtension.cs b/Blog.Core.Common/Extensions/RuntimeExtension.cs index c4ddb0c8..d66f1183 100644 --- a/Blog.Core.Common/Extensions/RuntimeExtension.cs +++ b/Blog.Core.Common/Extensions/RuntimeExtension.cs @@ -10,75 +10,77 @@ namespace Blog.Core.Common.Extensions; public static class RuntimeExtension { - /// - /// 获取项目程序集,排除所有的系统程序集(Microsoft.***、System.***等)、Nuget下载包 - /// - /// - public static IList GetAllAssemblies() - { - var list = new List(); - var deps = DependencyContext.Default; - var libs = deps.CompileLibraries.Where(lib => !lib.Serviceable && lib.Type != "package"); //排除所有的系统程序集、Nuget下载包 - foreach (var lib in libs) - { - try - { - var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name)); - list.Add(assembly); - } - catch (Exception e) - { - Log.Debug(e, "GetAllAssemblies Exception:{ex}", e.Message); - } - } - return list; - } + /// + /// 获取项目程序集,排除所有的系统程序集(Microsoft.***、System.***等)、Nuget下载包 + /// + /// + public static IList GetAllAssemblies() + { + var list = new List(); + var deps = DependencyContext.Default; + //只加载项目中的程序集 + var libs = deps.CompileLibraries.Where(lib => !lib.Serviceable && lib.Type == "project"); //排除所有的系统程序集、Nuget下载包 + foreach (var lib in libs) + { + try + { + var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name)); + list.Add(assembly); + } + catch (Exception e) + { + Log.Debug(e, "GetAllAssemblies Exception:{ex}", e.Message); + } + } - public static Assembly GetAssembly(string assemblyName) - { - return GetAllAssemblies().FirstOrDefault(assembly => assembly.FullName.Contains(assemblyName)); - } + return list; + } - public static IList GetAllTypes() - { - var list = new List(); - foreach (var assembly in GetAllAssemblies()) - { - var typeInfos = assembly.DefinedTypes; - foreach (var typeInfo in typeInfos) - { - list.Add(typeInfo.AsType()); - } - } + public static Assembly GetAssembly(string assemblyName) + { + return GetAllAssemblies().FirstOrDefault(assembly => assembly.FullName.Contains(assemblyName)); + } - return list; - } + public static IList GetAllTypes() + { + var list = new List(); + foreach (var assembly in GetAllAssemblies()) + { + var typeInfos = assembly.DefinedTypes; + foreach (var typeInfo in typeInfos) + { + list.Add(typeInfo.AsType()); + } + } - public static IList GetTypesByAssembly(string assemblyName) - { - var list = new List(); - var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(assemblyName)); - var typeInfos = assembly.DefinedTypes; - foreach (var typeInfo in typeInfos) - { - list.Add(typeInfo.AsType()); - } + return list; + } - return list; - } + public static IList GetTypesByAssembly(string assemblyName) + { + var list = new List(); + var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(assemblyName)); + var typeInfos = assembly.DefinedTypes; + foreach (var typeInfo in typeInfos) + { + list.Add(typeInfo.AsType()); + } - public static Type GetImplementType(string typeName, Type baseInterfaceType) - { - return GetAllTypes().FirstOrDefault(t => - { - if (t.Name == typeName && - t.GetTypeInfo().GetInterfaces().Any(b => b.Name == baseInterfaceType.Name)) - { - var typeInfo = t.GetTypeInfo(); - return typeInfo.IsClass && !typeInfo.IsAbstract && !typeInfo.IsGenericType; - } + return list; + } - return false; - }); - } + public static Type GetImplementType(string typeName, Type baseInterfaceType) + { + return GetAllTypes().FirstOrDefault(t => + { + if (t.Name == typeName && + t.GetTypeInfo().GetInterfaces().Any(b => b.Name == baseInterfaceType.Name)) + { + var typeInfo = t.GetTypeInfo(); + return typeInfo.IsClass && !typeInfo.IsAbstract && !typeInfo.IsGenericType; + } + + return false; + }); + } } \ No newline at end of file From 60cade83d4da87642e70720ad42be85de4cc85e6 Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Mon, 12 Jun 2023 11:16:45 +0800 Subject: [PATCH 56/84] =?UTF-8?q?=F0=9F=8E=A8=20=E4=BC=98=E5=8C=96AOP?= =?UTF-8?q?=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Common/Caches/Caching.cs | 32 ++++-- Blog.Core.Common/Caches/ICaching.cs | 5 +- Blog.Core.Extensions/AOP/BlogCacheAOP.cs | 122 ++++++++++++++--------- 3 files changed, 104 insertions(+), 55 deletions(-) diff --git a/Blog.Core.Common/Caches/Caching.cs b/Blog.Core.Common/Caches/Caching.cs index e7b4ab07..36e4ce3a 100644 --- a/Blog.Core.Common/Caches/Caching.cs +++ b/Blog.Core.Common/Caches/Caching.cs @@ -142,13 +142,13 @@ public async Task> GetAllCacheKeysAsync() var res = await _cache.GetStringAsync(CacheConst.KeyAll); return string.IsNullOrWhiteSpace(res) ? null : JsonConvert.DeserializeObject>(res); } - + public T Get(string cacheKey) { var res = _cache.Get(cacheKey); return res == null ? default : JsonConvert.DeserializeObject(Encoding.UTF8.GetString(res)); } - + /// /// 获取缓存 /// @@ -161,6 +161,18 @@ public async Task GetAsync(string cacheKey) return res == null ? default : JsonConvert.DeserializeObject(Encoding.UTF8.GetString(res)); } + public object Get(Type type, string cacheKey) + { + var res = _cache.Get(cacheKey); + return res == null ? default : JsonConvert.DeserializeObject(Encoding.UTF8.GetString(res), type); + } + + public async Task GetAsync(Type type, string cacheKey) + { + var res = await _cache.GetAsync(cacheKey); + return res == null ? default : JsonConvert.DeserializeObject(Encoding.UTF8.GetString(res), type); + } + public string GetString(string cacheKey) { return _cache.GetString(cacheKey); @@ -175,7 +187,7 @@ public async Task GetStringAsync(string cacheKey) { return await _cache.GetStringAsync(cacheKey); } - + public void Remove(string key) { _cache.Remove(key); @@ -214,7 +226,10 @@ public async Task RemoveAllAsync() public void Set(string cacheKey, T value, TimeSpan? expire = null) { - _cache.Set(cacheKey, GetBytes(value), expire == null ? new DistributedCacheEntryOptions() {AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(6)} : new DistributedCacheEntryOptions() {AbsoluteExpirationRelativeToNow = expire}); + _cache.Set(cacheKey, GetBytes(value), + expire == null + ? new DistributedCacheEntryOptions() {AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(6)} + : new DistributedCacheEntryOptions() {AbsoluteExpirationRelativeToNow = expire}); AddCacheKey(cacheKey); } @@ -227,7 +242,8 @@ public void Set(string cacheKey, T value, TimeSpan? expire = null) /// public async Task SetAsync(string cacheKey, T value) { - await _cache.SetAsync(cacheKey, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(value)), new DistributedCacheEntryOptions() {AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(6)}); + await _cache.SetAsync(cacheKey, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(value)), + new DistributedCacheEntryOptions() {AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(6)}); await AddCacheKeyAsync(cacheKey); } @@ -241,7 +257,8 @@ public async Task SetAsync(string cacheKey, T value) /// public async Task SetAsync(string cacheKey, T value, TimeSpan expire) { - await _cache.SetAsync(cacheKey, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(value)), new DistributedCacheEntryOptions() {AbsoluteExpirationRelativeToNow = expire}); + await _cache.SetAsync(cacheKey, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(value)), + new DistributedCacheEntryOptions() {AbsoluteExpirationRelativeToNow = expire}); await AddCacheKeyAsync(cacheKey); } @@ -251,6 +268,7 @@ public void SetPermanent(string cacheKey, T value) _cache.Set(cacheKey, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(value))); AddCacheKey(cacheKey); } + public async Task SetPermanentAsync(string cacheKey, T value) { await _cache.SetAsync(cacheKey, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(value))); @@ -293,7 +311,7 @@ public async Task SetStringAsync(string cacheKey, string value, TimeSpan expire) await AddCacheKeyAsync(cacheKey); } - + /// /// 缓存最大角色数据范围 diff --git a/Blog.Core.Common/Caches/ICaching.cs b/Blog.Core.Common/Caches/ICaching.cs index f856aeb6..9ce92632 100644 --- a/Blog.Core.Common/Caches/ICaching.cs +++ b/Blog.Core.Common/Caches/ICaching.cs @@ -25,10 +25,13 @@ public interface ICaching List GetAllCacheKeys(); Task> GetAllCacheKeysAsync(); - + T Get(string cacheKey); Task GetAsync(string cacheKey); + object Get(Type type, string cacheKey); + Task GetAsync(Type type, string cacheKey); + string GetString(string cacheKey); Task GetStringAsync(string cacheKey); diff --git a/Blog.Core.Extensions/AOP/BlogCacheAOP.cs b/Blog.Core.Extensions/AOP/BlogCacheAOP.cs index d872bab5..d3d91f2a 100644 --- a/Blog.Core.Extensions/AOP/BlogCacheAOP.cs +++ b/Blog.Core.Extensions/AOP/BlogCacheAOP.cs @@ -2,54 +2,82 @@ using Blog.Core.Common; using Castle.DynamicProxy; using System.Linq; +using System.Threading.Tasks; using Blog.Core.Common.Caches; namespace Blog.Core.AOP { - /// - /// 面向切面的缓存使用 - /// - public class BlogCacheAOP : CacheAOPbase - { - //通过注入的方式,把缓存操作接口通过构造函数注入 - private readonly ICaching _cache; - public BlogCacheAOP(ICaching cache) - { - _cache = cache; - } - - //Intercept方法是拦截的关键所在,也是IInterceptor接口中的唯一定义 - public override void Intercept(IInvocation invocation) - { - var method = invocation.MethodInvocationTarget ?? invocation.Method; - //对当前方法的特性验证 - //如果需要验证 - var CachingAttribute = method.GetCustomAttributes(true).FirstOrDefault(x => x.GetType() == typeof(CachingAttribute)); - if (CachingAttribute is CachingAttribute qCachingAttribute) - { - //获取自定义缓存键 - var cacheKey = CustomCacheKey(invocation); - //根据key获取相应的缓存值 - var cacheValue = _cache.Get(cacheKey); - if (cacheValue != null) - { - //将当前获取到的缓存值,赋值给当前执行方法 - invocation.ReturnValue = cacheValue; - return; - } - //去执行当前的方法 - invocation.Proceed(); - //存入缓存 - if (!string.IsNullOrWhiteSpace(cacheKey)) - { - _cache.Set(cacheKey, invocation.ReturnValue, TimeSpan.FromMinutes(qCachingAttribute.AbsoluteExpiration)); - } - } - else - { - invocation.Proceed();//直接执行被拦截方法 - } - } - } - -} + /// + /// 面向切面的缓存使用 + /// + public class BlogCacheAOP : CacheAOPbase + { + //通过注入的方式,把缓存操作接口通过构造函数注入 + private readonly ICaching _cache; + + public BlogCacheAOP(ICaching cache) + { + _cache = cache; + } + + //Intercept方法是拦截的关键所在,也是IInterceptor接口中的唯一定义 + public override void Intercept(IInvocation invocation) + { + var method = invocation.MethodInvocationTarget ?? invocation.Method; + //对当前方法的特性验证 + //如果需要验证 + var CachingAttribute = method.GetCustomAttributes(true).FirstOrDefault(x => x.GetType() == typeof(CachingAttribute)); + if (CachingAttribute is CachingAttribute qCachingAttribute) + { + //获取自定义缓存键 + var cacheKey = CustomCacheKey(invocation); + if (_cache.Exists(cacheKey)) + { + //将当前获取到的缓存值,赋值给当前执行方法 + Type returnType; + if (typeof(Task).IsAssignableFrom(method.ReturnType)) + { + returnType = method.ReturnType.GenericTypeArguments.FirstOrDefault(); + } + else + { + returnType = method.ReturnType; + } + + //根据key获取相应的缓存值 + dynamic cacheValue = _cache.Get(returnType, cacheKey); + invocation.ReturnValue = (typeof(Task).IsAssignableFrom(method.ReturnType)) ? Task.FromResult(cacheValue) : cacheValue; + return; + } + + //去执行当前的方法 + invocation.Proceed(); + //存入缓存 + if (!string.IsNullOrWhiteSpace(cacheKey)) + { + object response; + + //Type type = invocation.ReturnValue?.GetType(); + var type = invocation.Method.ReturnType; + if (typeof(Task).IsAssignableFrom(type)) + { + var resultProperty = type.GetProperty("Result"); + response = resultProperty?.GetValue(invocation.ReturnValue); + } + else + { + response = invocation.ReturnValue; + } + + if (response == null) response = string.Empty; + + _cache.Set(cacheKey, response, TimeSpan.FromMinutes(qCachingAttribute.AbsoluteExpiration)); + } + } + else + { + invocation.Proceed(); //直接执行被拦截方法 + } + } + } +} \ No newline at end of file From c1ce9e59728b8f0592fa7bd100466fd2c393ad1b Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Mon, 12 Jun 2023 11:34:39 +0800 Subject: [PATCH 57/84] =?UTF-8?q?=F0=9F=8E=A8=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E5=8E=9F=E6=9C=89=E7=9A=84RedisCacheAop=20=E4=B8=8ECacheAop?= =?UTF-8?q?=E6=9C=89=E4=BA=9B=E9=87=8D=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Api/Controllers/ValuesController.cs | 2 +- Blog.Core.Api/appsettings.json | 5 +- Blog.Core.Extensions/AOP/BlogCacheAOP.cs | 4 +- Blog.Core.Extensions/AOP/BlogRedisCacheAOP.cs | 89 ------------------- .../Redis/IRedisBasketRepository.cs | 3 +- .../ServiceExtensions/AppConfigSetup.cs | 22 ++--- .../AutofacModuleRegister.cs | 8 +- Blog.Core.Services/AdvertisementServices.cs | 2 +- Blog.Core.Tests/appsettings.json | 5 +- 9 files changed, 14 insertions(+), 126 deletions(-) delete mode 100644 Blog.Core.Extensions/AOP/BlogRedisCacheAOP.cs diff --git a/Blog.Core.Api/Controllers/ValuesController.cs b/Blog.Core.Api/Controllers/ValuesController.cs index 03b37556..ae5cc81f 100644 --- a/Blog.Core.Api/Controllers/ValuesController.cs +++ b/Blog.Core.Api/Controllers/ValuesController.cs @@ -164,7 +164,7 @@ await _blogArticleServices.QuerySql( // 测试模拟异常,全局异常过滤器拦截 var i = 0; - var d = 3 / i; + // var d = 3 / i; // 测试 AOP 缓存 diff --git a/Blog.Core.Api/appsettings.json b/Blog.Core.Api/appsettings.json index 73637e19..def53cfc 100644 --- a/Blog.Core.Api/appsettings.json +++ b/Blog.Core.Api/appsettings.json @@ -41,10 +41,7 @@ "SubscriptionClientName": "Blog.Core" }, "AppSettings": { - "RedisCachingAOP": { - "Enabled": false - }, - "MemoryCachingAOP": { + "CachingAOP": { "Enabled": true }, "LogToDb": true, diff --git a/Blog.Core.Extensions/AOP/BlogCacheAOP.cs b/Blog.Core.Extensions/AOP/BlogCacheAOP.cs index d3d91f2a..1d52a2de 100644 --- a/Blog.Core.Extensions/AOP/BlogCacheAOP.cs +++ b/Blog.Core.Extensions/AOP/BlogCacheAOP.cs @@ -61,8 +61,8 @@ public override void Intercept(IInvocation invocation) var type = invocation.Method.ReturnType; if (typeof(Task).IsAssignableFrom(type)) { - var resultProperty = type.GetProperty("Result"); - response = resultProperty?.GetValue(invocation.ReturnValue); + dynamic result = invocation.ReturnValue; + response = result.Result; } else { diff --git a/Blog.Core.Extensions/AOP/BlogRedisCacheAOP.cs b/Blog.Core.Extensions/AOP/BlogRedisCacheAOP.cs deleted file mode 100644 index 9bc7f7f7..00000000 --- a/Blog.Core.Extensions/AOP/BlogRedisCacheAOP.cs +++ /dev/null @@ -1,89 +0,0 @@ -using Blog.Core.Common; -using Blog.Core.Extensions; -using Castle.DynamicProxy; -using System; -using System.Linq; -using System.Threading.Tasks; - -namespace Blog.Core.AOP -{ - /// - /// 面向切面的缓存使用 - /// - public class BlogRedisCacheAOP : CacheAOPbase - { - //通过注入的方式,把缓存操作接口通过构造函数注入 - private readonly IRedisBasketRepository _cache; - public BlogRedisCacheAOP(IRedisBasketRepository cache) - { - _cache = cache; - } - - //Intercept方法是拦截的关键所在,也是IInterceptor接口中的唯一定义 - //代码已经合并 ,学习pr流程 - public override void Intercept(IInvocation invocation) - { - var method = invocation.MethodInvocationTarget ?? invocation.Method; - if (method.ReturnType == typeof(void) || method.ReturnType == typeof(Task)) - { - invocation.Proceed(); - return; - } - //对当前方法的特性验证 - var qCachingAttribute = method.GetCustomAttributes(true).FirstOrDefault(x => x.GetType() == typeof(CachingAttribute)) as CachingAttribute; - - if (qCachingAttribute != null) - { - //获取自定义缓存键 - var cacheKey = CustomCacheKey(invocation); - //注意是 string 类型,方法GetValue - var cacheValue = _cache.GetValue(cacheKey).Result; - if (cacheValue != null) - { - //将当前获取到的缓存值,赋值给当前执行方法 - Type returnType; - if (typeof(Task).IsAssignableFrom(method.ReturnType)) - { - returnType = method.ReturnType.GenericTypeArguments.FirstOrDefault(); - } - else - { - returnType = method.ReturnType; - } - - dynamic _result = Newtonsoft.Json.JsonConvert.DeserializeObject(cacheValue, returnType); - invocation.ReturnValue = (typeof(Task).IsAssignableFrom(method.ReturnType)) ? Task.FromResult(_result) : _result; - return; - } - //去执行当前的方法 - invocation.Proceed(); - - //存入缓存 - if (!string.IsNullOrWhiteSpace(cacheKey)) - { - object response; - - //Type type = invocation.ReturnValue?.GetType(); - var type = invocation.Method.ReturnType; - if (typeof(Task).IsAssignableFrom(type)) - { - var resultProperty = type.GetProperty("Result"); - response = resultProperty.GetValue(invocation.ReturnValue); - } - else - { - response = invocation.ReturnValue; - } - if (response == null) response = string.Empty; - - _cache.Set(cacheKey, response, TimeSpan.FromMinutes(qCachingAttribute.AbsoluteExpiration)).Wait(); - } - } - else - { - invocation.Proceed();//直接执行被拦截方法 - } - } - } - -} diff --git a/Blog.Core.Extensions/Redis/IRedisBasketRepository.cs b/Blog.Core.Extensions/Redis/IRedisBasketRepository.cs index 08effd1b..85408160 100644 --- a/Blog.Core.Extensions/Redis/IRedisBasketRepository.cs +++ b/Blog.Core.Extensions/Redis/IRedisBasketRepository.cs @@ -1,6 +1,7 @@ using StackExchange.Redis; using System; using System.Collections.Generic; +using System.ComponentModel; using System.Threading.Tasks; namespace Blog.Core.Extensions @@ -8,7 +9,7 @@ namespace Blog.Core.Extensions /// /// Redis缓存接口 /// - [Obsolete("普通缓存考虑直接使用ICaching,如果要使用Redis队列等还是使用此类")] + [Description("普通缓存考虑直接使用ICaching,如果要使用Redis队列等还是使用此类")] public interface IRedisBasketRepository { diff --git a/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs b/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs index 0336567f..d7a1f90d 100644 --- a/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs @@ -40,25 +40,14 @@ public static void AddAppConfigSetup(this IServiceCollection services, IHostEnvi { Console.WriteLine($"Current authorization scheme: " + (Permissions.IsUseIds4 ? "Ids4" : "JWT")); } - - // Redis缓存AOP - if (!AppSettings.app(new string[] { "AppSettings", "RedisCachingAOP", "Enabled" }).ObjToBool()) - { - Console.WriteLine($"Redis Caching AOP: False"); - } - else - { - ConsoleHelper.WriteSuccessLine($"Redis Caching AOP: True"); - } - - // 内存缓存AOP - if (!AppSettings.app(new string[] { "AppSettings", "MemoryCachingAOP", "Enabled" }).ObjToBool()) + // 缓存AOP + if (!AppSettings.app(new string[] { "AppSettings", "CachingAOP", "Enabled" }).ObjToBool()) { - Console.WriteLine($"Memory Caching AOP: False"); + Console.WriteLine($"Caching AOP: False"); } else { - ConsoleHelper.WriteSuccessLine($"Memory Caching AOP: True"); + ConsoleHelper.WriteSuccessLine($"Caching AOP: True"); } // 服务日志AOP @@ -259,8 +248,7 @@ public static void AddAppTableConfigSetup(this IServiceCollection services, IHos List aopInfos = new() { - new string[] { "Redis缓存AOP", AppSettings.app("AppSettings", "RedisCachingAOP", "Enabled") }, - new string[] { "内存缓存AOP", AppSettings.app("AppSettings", "MemoryCachingAOP", "Enabled") }, + new string[] { "缓存AOP", AppSettings.app("AppSettings", "CachingAOP", "Enabled") }, new string[] { "服务日志AOP", AppSettings.app("AppSettings", "LogAOP", "Enabled") }, new string[] { "事务AOP", AppSettings.app("AppSettings", "TranAOP", "Enabled") }, new string[] { "Sql执行AOP", AppSettings.app("AppSettings", "SqlAOP", "Enabled") }, diff --git a/Blog.Core.Extensions/ServiceExtensions/AutofacModuleRegister.cs b/Blog.Core.Extensions/ServiceExtensions/AutofacModuleRegister.cs index 4351962b..4836c402 100644 --- a/Blog.Core.Extensions/ServiceExtensions/AutofacModuleRegister.cs +++ b/Blog.Core.Extensions/ServiceExtensions/AutofacModuleRegister.cs @@ -39,13 +39,7 @@ protected override void Load(ContainerBuilder builder) // AOP 开关,如果想要打开指定的功能,只需要在 appsettigns.json 对应对应 true 就行。 var cacheType = new List(); - if (AppSettings.app(new string[] { "AppSettings", "RedisCachingAOP", "Enabled" }).ObjToBool()) - { - builder.RegisterType(); - cacheType.Add(typeof(BlogRedisCacheAOP)); - } - - if (AppSettings.app(new string[] { "AppSettings", "MemoryCachingAOP", "Enabled" }).ObjToBool()) + if (AppSettings.app(new string[] { "AppSettings", "CachingAOP", "Enabled" }).ObjToBool()) { builder.RegisterType(); cacheType.Add(typeof(BlogCacheAOP)); diff --git a/Blog.Core.Services/AdvertisementServices.cs b/Blog.Core.Services/AdvertisementServices.cs index c95f4142..348048e8 100644 --- a/Blog.Core.Services/AdvertisementServices.cs +++ b/Blog.Core.Services/AdvertisementServices.cs @@ -13,7 +13,7 @@ public void ReturnExp() int a = 1; int b = 0; - int c = a / b; + // int c = a / b; } //public IAdvertisementRepository dal = new AdvertisementRepository(); diff --git a/Blog.Core.Tests/appsettings.json b/Blog.Core.Tests/appsettings.json index 23ab9c65..8c0305c7 100644 --- a/Blog.Core.Tests/appsettings.json +++ b/Blog.Core.Tests/appsettings.json @@ -45,10 +45,7 @@ "SubscriptionClientName": "Blog.Core" }, "AppSettings": { - "RedisCachingAOP": { - "Enabled": false - }, - "MemoryCachingAOP": { + "CachingAOP": { "Enabled": true }, "LogAOP": { From a338c56e7ae43d657c23bc3374aba4332ea86c56 Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Mon, 12 Jun 2023 17:45:15 +0800 Subject: [PATCH 58/84] =?UTF-8?q?=E2=9C=A8=F0=9F=8E=A8=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E5=90=9E=E5=BC=82=E5=B8=B8=E9=97=AE=E9=A2=98=E3=80=81?= =?UTF-8?q?=E4=BC=98=E5=8C=96HttpResponse=20Body=E8=AF=BB=E5=8F=96?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.处理掉中间件过度try catch 吞掉异常,建议直接往上抛不要过度try 2.优雅处理HttpResponse读取问题,原生[HttpResponseStream]实际上只是个包装类,内部包装了[HttpResponsePipeWriter]来进行写入响应数据,由此封装一个[FluentHttpResponseStream],内部扩展使用[MemoryStream]来读取 --- Blog.Core.Api/Program.cs | 1 + .../Extensions/HttpResponseExceptions.cs | 34 ++ .../Https/FluentHttpResponseStream.cs | 75 +++ Blog.Core.Extensions/AOP/BlogLogAOP.cs | 486 +++++++++--------- .../Middlewares/ExceptionHandlerMiddleware.cs | 89 ++-- .../FluentResponseBodyMiddleware.cs | 21 + .../Middlewares/IpLogMiddleware.cs | 241 +++++---- .../Middlewares/RecordAccessLogsMiddleware.cs | 262 +++++----- .../Middlewares/RequRespLogMiddleware.cs | 245 +++++---- .../Redis/RedisBasketRepository.cs | 3 +- 10 files changed, 784 insertions(+), 673 deletions(-) create mode 100644 Blog.Core.Common/Extensions/HttpResponseExceptions.cs create mode 100644 Blog.Core.Common/Https/FluentHttpResponseStream.cs create mode 100644 Blog.Core.Extensions/Middlewares/FluentResponseBodyMiddleware.cs diff --git a/Blog.Core.Api/Program.cs b/Blog.Core.Api/Program.cs index 74bcd311..67d4524e 100644 --- a/Blog.Core.Api/Program.cs +++ b/Blog.Core.Api/Program.cs @@ -132,6 +132,7 @@ var app = builder.Build(); app.ConfigureApplication(); app.UseApplicationSetup(); +app.UseResponseBodyRead(); if (app.Environment.IsDevelopment()) { diff --git a/Blog.Core.Common/Extensions/HttpResponseExceptions.cs b/Blog.Core.Common/Extensions/HttpResponseExceptions.cs new file mode 100644 index 00000000..b9d25f81 --- /dev/null +++ b/Blog.Core.Common/Extensions/HttpResponseExceptions.cs @@ -0,0 +1,34 @@ +using System; +using System.IO; +using Blog.Core.Common.Https; +using Microsoft.AspNetCore.Http; + +namespace Blog.Core.Common.Extensions; + +public static class HttpResponseExceptions +{ + public static string GetResponseBody(this HttpResponse response) + { + if (response is null) + { + return default; + } + + if (response.Body is FluentHttpResponseStream responseBody) + { + response.Body.Position = 0; + using StreamReader stream = new StreamReader(responseBody); + string body = stream.ReadToEnd(); + response.Body.Position = 0; + return body; + } + else + { + //原始HttpResponseStream 无法读取 + //实际上只是个包装类,内部使用了HttpResponsePipeWriter write + throw new ApplicationException("The response body is not a FluentHttpResponseStream"); + } + + return default; + } +} \ No newline at end of file diff --git a/Blog.Core.Common/Https/FluentHttpResponseStream.cs b/Blog.Core.Common/Https/FluentHttpResponseStream.cs new file mode 100644 index 00000000..c668f47d --- /dev/null +++ b/Blog.Core.Common/Https/FluentHttpResponseStream.cs @@ -0,0 +1,75 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http.Features; + +namespace Blog.Core.Common.Https; + +/// +/// 扩展 HttpResponseStream
+/// 原始[HttpResponseStream]实际上只是个包装类,内部包装了[HttpResponsePipeWriter]来进行写入响应数据 +/// +public class FluentHttpResponseStream : Stream +{ + private readonly IHttpBodyControlFeature _bodyControl; + private readonly IHttpResponseBodyFeature _pipeWriter; + private readonly MemoryStream _stream = new(); + + public FluentHttpResponseStream(IHttpResponseBodyFeature pipeWriter, IHttpBodyControlFeature bodyControl) + { + _pipeWriter = pipeWriter; + _bodyControl = bodyControl; + } + + public override bool CanRead => _stream.CanRead; + + public override bool CanSeek => _stream.CanSeek; + + public override bool CanWrite => _stream.CanWrite; + + public override long Length => _stream.Length; + + public override long Position { get => _stream.Position; set => _stream.Position = value; } + + public override void Flush() + { + if (!_bodyControl.AllowSynchronousIO) + { + throw new InvalidOperationException("SynchronousWritesDisallowed "); + } + _stream.Flush(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + return _stream.Read(buffer, offset, count); + } + + public override long Seek(long offset, SeekOrigin origin) + { + return _stream.Seek(offset, origin); + } + + public override void SetLength(long value) + { + _stream.SetLength(value); + } + + public override void Write(byte[] buffer, int offset, int count) + { + WriteAsync(buffer, offset, count, default).GetAwaiter().GetResult(); + } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + _stream.Write(buffer, offset, count); + return _pipeWriter.Writer.WriteAsync(new ReadOnlyMemory(buffer, offset, count), cancellationToken).AsTask(); + } + + protected override void Dispose(bool disposing) + { + _stream.Dispose(); + base.Dispose(disposing); + } +} \ No newline at end of file diff --git a/Blog.Core.Extensions/AOP/BlogLogAOP.cs b/Blog.Core.Extensions/AOP/BlogLogAOP.cs index 29861578..09d798bf 100644 --- a/Blog.Core.Extensions/AOP/BlogLogAOP.cs +++ b/Blog.Core.Extensions/AOP/BlogLogAOP.cs @@ -13,271 +13,279 @@ namespace Blog.Core.AOP { - /// - /// 拦截器BlogLogAOP 继承IInterceptor接口 - /// - public class BlogLogAOP : IInterceptor - { - private readonly IHubContext _hubContext; - private readonly IHttpContextAccessor _accessor; + /// + /// 拦截器BlogLogAOP 继承IInterceptor接口 + /// + public class BlogLogAOP : IInterceptor + { + private readonly IHubContext _hubContext; + private readonly IHttpContextAccessor _accessor; - public BlogLogAOP(IHubContext hubContext, IHttpContextAccessor accessor) - { - _hubContext = hubContext; - _accessor = accessor; - } + public BlogLogAOP(IHubContext hubContext, IHttpContextAccessor accessor) + { + _hubContext = hubContext; + _accessor = accessor; + } - /// - /// 实例化IInterceptor唯一方法 - /// - /// 包含被拦截方法的信息 - public void Intercept(IInvocation invocation) - { - string UserName = _accessor.HttpContext?.User?.Identity?.Name; - string json; - try - { - json = JsonConvert.SerializeObject(invocation.Arguments); - } - catch (Exception ex) - { - json = "无法序列化,可能是兰姆达表达式等原因造成,按照框架优化代码" + ex.ToString(); - } - DateTime startTime = DateTime.Now; - AOPLogInfo apiLogAopInfo = new AOPLogInfo - { - RequestTime = startTime.ToString("yyyy-MM-dd hh:mm:ss fff"), - OpUserName = UserName, - RequestMethodName = invocation.Method.Name, - RequestParamsName = string.Join(", ", invocation.Arguments.Select(a => (a ?? "").ToString()).ToArray()), - ResponseJsonData = json - }; + /// + /// 实例化IInterceptor唯一方法 + /// + /// 包含被拦截方法的信息 + public void Intercept(IInvocation invocation) + { + string UserName = _accessor.HttpContext?.User?.Identity?.Name; + string json; + try + { + json = JsonConvert.SerializeObject(invocation.Arguments); + } + catch (Exception ex) + { + json = "无法序列化,可能是兰姆达表达式等原因造成,按照框架优化代码" + ex.ToString(); + } - //测试异常记录 - //Convert.ToDateTime(DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss fff")); + DateTime startTime = DateTime.Now; + AOPLogInfo apiLogAopInfo = new AOPLogInfo + { + RequestTime = startTime.ToString("yyyy-MM-dd hh:mm:ss fff"), + OpUserName = UserName, + RequestMethodName = invocation.Method.Name, + RequestParamsName = string.Join(", ", invocation.Arguments.Select(a => (a ?? "").ToString()).ToArray()), + ResponseJsonData = json + }; - //记录被拦截方法信息的日志信息 - //var dataIntercept = "" + - // $"【当前操作用户】:{ UserName} \r\n" + - // $"【当前执行方法】:{ invocation.Method.Name} \r\n" + - // $"【携带的参数有】: {string.Join(", ", invocation.Arguments.Select(a => (a ?? "").ToString()).ToArray())} \r\n"; + //测试异常记录 + //Convert.ToDateTime(DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss fff")); - try - { - MiniProfiler.Current.Step($"执行Service方法:{invocation.Method.Name}() -> "); - //在被拦截的方法执行完毕后 继续执行当前方法,注意是被拦截的是异步的 - invocation.Proceed(); + //记录被拦截方法信息的日志信息 + //var dataIntercept = "" + + // $"【当前操作用户】:{ UserName} \r\n" + + // $"【当前执行方法】:{ invocation.Method.Name} \r\n" + + // $"【携带的参数有】: {string.Join(", ", invocation.Arguments.Select(a => (a ?? "").ToString()).ToArray())} \r\n"; + try + { + MiniProfiler.Current.Step($"执行Service方法:{invocation.Method.Name}() -> "); + //在被拦截的方法执行完毕后 继续执行当前方法,注意是被拦截的是异步的 + invocation.Proceed(); - // 异步获取异常,先执行 - if (IsAsyncMethod(invocation.Method)) - { - #region 方案一 - //Wait task execution and modify return value - if (invocation.Method.ReturnType == typeof(Task)) - { - invocation.ReturnValue = InternalAsyncHelper.AwaitTaskWithPostActionAndFinally( - (Task)invocation.ReturnValue, - async () => await SuccessAction(invocation, apiLogAopInfo, startTime),/*成功时执行*/ - ex => - { - LogEx(ex, apiLogAopInfo); - }); - } - //Task - else - { - invocation.ReturnValue = InternalAsyncHelper.CallAwaitTaskWithPostActionAndFinallyAndGetResult( - invocation.Method.ReturnType.GenericTypeArguments[0], - invocation.ReturnValue, - //async () => await SuccessAction(invocation, dataIntercept),/*成功时执行*/ - async (o) => await SuccessAction(invocation, apiLogAopInfo, startTime, o),/*成功时执行*/ - ex => - { - LogEx(ex, apiLogAopInfo); - }); - } - #endregion + // 异步获取异常,先执行 + if (IsAsyncMethod(invocation.Method)) + { + #region 方案一 + //Wait task execution and modify return value + if (invocation.Method.ReturnType == typeof(Task)) + { + invocation.ReturnValue = InternalAsyncHelper.AwaitTaskWithPostActionAndFinally( + (Task) invocation.ReturnValue, + async () => await SuccessAction(invocation, apiLogAopInfo, startTime), /*成功时执行*/ + ex => + { + LogEx(ex, apiLogAopInfo); + }); + } + //Task + else + { + invocation.ReturnValue = InternalAsyncHelper.CallAwaitTaskWithPostActionAndFinallyAndGetResult( + invocation.Method.ReturnType.GenericTypeArguments[0], + invocation.ReturnValue, + //async () => await SuccessAction(invocation, dataIntercept),/*成功时执行*/ + async (o) => await SuccessAction(invocation, apiLogAopInfo, startTime, o), /*成功时执行*/ + ex => + { + LogEx(ex, apiLogAopInfo); + }); + } - // 如果方案一不行,试试这个方案 - //#region 方案二 + #endregion - //var type = invocation.Method.ReturnType; - //var resultProperty = type.GetProperty("Result"); - //DateTime endTime = DateTime.Now; - //string ResponseTime = (endTime - startTime).Milliseconds.ToString(); - //apiLogAopInfo.ResponseTime = endTime.ToString("yyyy-MM-dd hh:mm:ss fff"); - //apiLogAopInfo.ResponseIntervalTime = ResponseTime + "ms"; - //apiLogAopInfo.ResponseJsonData = JsonConvert.SerializeObject(resultProperty.GetValue(invocation.ReturnValue)); - ////dataIntercept += ($"【响应时间】:{ResponseTime}ms\r\n"); - ////dataIntercept += ($"【执行完成时间】:{endTime.ToString("yyyy-MM-dd hh:mm:ss fff")}\r\n"); - ////dataIntercept += ($"【执行完成结果】:{JsonConvert.SerializeObject(resultProperty.GetValue(invocation.ReturnValue))}\r\n"); + // 如果方案一不行,试试这个方案 + //#region 方案二 - //Parallel.For(0, 1, e => - //{ - // //LogLock.OutLogAOP("AOPLog", new string[] { dataIntercept }); - // LogLock.OutLogAOP("AOPLog", new string[] { apiLogAopInfo.GetType().ToString() + " - ResponseJsonDataType:" + type, JsonConvert.SerializeObject(apiLogAopInfo) }); - //}); + //var type = invocation.Method.ReturnType; + //var resultProperty = type.GetProperty("Result"); + //DateTime endTime = DateTime.Now; + //string ResponseTime = (endTime - startTime).Milliseconds.ToString(); + //apiLogAopInfo.ResponseTime = endTime.ToString("yyyy-MM-dd hh:mm:ss fff"); + //apiLogAopInfo.ResponseIntervalTime = ResponseTime + "ms"; + //apiLogAopInfo.ResponseJsonData = JsonConvert.SerializeObject(resultProperty.GetValue(invocation.ReturnValue)); - //#endregion - } - else - { - // 同步1 - string jsonResult; - try - { - jsonResult = JsonConvert.SerializeObject(invocation.ReturnValue); - } - catch (Exception ex) - { - jsonResult = "无法序列化,可能是兰姆达表达式等原因造成,按照框架优化代码" + ex.ToString(); - } - var type = invocation.Method.ReturnType; - var resultProperty = type.GetProperty("Result"); - DateTime endTime = DateTime.Now; - string ResponseTime = (endTime - startTime).Milliseconds.ToString(); - apiLogAopInfo.ResponseTime = endTime.ToString("yyyy-MM-dd hh:mm:ss fff"); - apiLogAopInfo.ResponseIntervalTime = ResponseTime + "ms"; - //apiLogAopInfo.ResponseJsonData = JsonConvert.SerializeObject(resultProperty.GetValue(invocation.ReturnValue)); - apiLogAopInfo.ResponseJsonData = jsonResult; - //dataIntercept += ($"【执行完成结果】:{jsonResult}"); - Parallel.For(0, 1, e => - { - //LogLock.OutLogAOP("AOPLog", new string[] { dataIntercept }); - LogLock.OutLogAOP("AOPLog", _accessor.HttpContext?.TraceIdentifier, new string[] { apiLogAopInfo.GetType().ToString(), JsonConvert.SerializeObject(apiLogAopInfo) }); - }); - } - } - catch (Exception ex)// 同步2 - { - LogEx(ex, apiLogAopInfo); - } - if (AppSettings.app(new string[] { "Middleware", "SignalRSendLog", "Enabled" }).ObjToBool()) - { - _hubContext.Clients.All.SendAsync("ReceiveUpdate", LogLock.GetLogData()).Wait(); - } - } + ////dataIntercept += ($"【响应时间】:{ResponseTime}ms\r\n"); + ////dataIntercept += ($"【执行完成时间】:{endTime.ToString("yyyy-MM-dd hh:mm:ss fff")}\r\n"); + ////dataIntercept += ($"【执行完成结果】:{JsonConvert.SerializeObject(resultProperty.GetValue(invocation.ReturnValue))}\r\n"); - private async Task SuccessAction(IInvocation invocation, AOPLogInfo apiLogAopInfo, DateTime startTime, object o = null) - { - //invocation.ReturnValue = o; - //var type = invocation.Method.ReturnType; - //if (typeof(Task).IsAssignableFrom(type)) - //{ - // //var resultProperty = type.GetProperty("Result"); - // //类型错误 都可以不要invocation参数,直接将o系列化保存到日记中 - // dataIntercept += ($"【执行完成结果】:{JsonConvert.SerializeObject(invocation.ReturnValue)}"); - //} - //else - //{ - // dataIntercept += ($"【执行完成结果】:{invocation.ReturnValue}"); - //} - DateTime endTime = DateTime.Now; - string ResponseTime = (endTime - startTime).Milliseconds.ToString(); - apiLogAopInfo.ResponseTime = endTime.ToString("yyyy-MM-dd hh:mm:ss fff"); - apiLogAopInfo.ResponseIntervalTime = ResponseTime + "ms"; - apiLogAopInfo.ResponseJsonData = JsonConvert.SerializeObject(o); + //Parallel.For(0, 1, e => + //{ + // //LogLock.OutLogAOP("AOPLog", new string[] { dataIntercept }); + // LogLock.OutLogAOP("AOPLog", new string[] { apiLogAopInfo.GetType().ToString() + " - ResponseJsonDataType:" + type, JsonConvert.SerializeObject(apiLogAopInfo) }); + //}); + //#endregion + } + else + { + // 同步1 + string jsonResult; + try + { + jsonResult = JsonConvert.SerializeObject(invocation.ReturnValue); + } + catch (Exception ex) + { + jsonResult = "无法序列化,可能是兰姆达表达式等原因造成,按照框架优化代码" + ex.ToString(); + } - await Task.Run(() => - { - Parallel.For(0, 1, e => - { - //LogLock.OutSql2Log("AOPLog", new string[] { JsonConvert.SerializeObject(apiLogAopInfo) }); - LogLock.OutLogAOP("AOPLog", _accessor.HttpContext?.TraceIdentifier, new string[] { apiLogAopInfo.GetType().ToString(), JsonConvert.SerializeObject(apiLogAopInfo) }); - }); - }); - } + var type = invocation.Method.ReturnType; + var resultProperty = type.GetProperty("Result"); + DateTime endTime = DateTime.Now; + string ResponseTime = (endTime - startTime).Milliseconds.ToString(); + apiLogAopInfo.ResponseTime = endTime.ToString("yyyy-MM-dd hh:mm:ss fff"); + apiLogAopInfo.ResponseIntervalTime = ResponseTime + "ms"; + //apiLogAopInfo.ResponseJsonData = JsonConvert.SerializeObject(resultProperty.GetValue(invocation.ReturnValue)); + apiLogAopInfo.ResponseJsonData = jsonResult; + //dataIntercept += ($"【执行完成结果】:{jsonResult}"); + Parallel.For(0, 1, e => + { + //LogLock.OutLogAOP("AOPLog", new string[] { dataIntercept }); + LogLock.OutLogAOP("AOPLog", _accessor.HttpContext?.TraceIdentifier, + new string[] {apiLogAopInfo.GetType().ToString(), JsonConvert.SerializeObject(apiLogAopInfo)}); + }); + } + } + catch (Exception ex) // 同步2 + { + LogEx(ex, apiLogAopInfo); + throw; + } - private void LogEx(Exception ex, AOPLogInfo dataIntercept) - { - if (ex != null) - { - //执行的 service 中,收录异常 - MiniProfiler.Current.CustomTiming("Errors:", ex.Message); - //执行的 service 中,捕获异常 - //dataIntercept += ($"【执行完成结果】:方法中出现异常:{ex.Message + ex.InnerException}\r\n"); - AOPLogExInfo apiLogAopExInfo = new AOPLogExInfo - { - ExMessage = ex.Message, - InnerException = "InnerException-内部异常:\r\n" + (ex.InnerException == null ? "" : ex.InnerException.InnerException.ToString()) + ("\r\nStackTrace-堆栈跟踪:\r\n") + (ex.StackTrace == null ? "" : ex.StackTrace.ToString()), - ApiLogAopInfo = dataIntercept - }; - // 异常日志里有详细的堆栈信息 - Parallel.For(0, 1, e => - { - //LogLock.OutLogAOP("AOPLogEx", new string[] { dataIntercept }); - LogLock.OutLogAOP("AOPLogEx", _accessor.HttpContext?.TraceIdentifier, new string[] { apiLogAopExInfo.GetType().ToString(), JsonConvert.SerializeObject(apiLogAopExInfo) }); + if (AppSettings.app(new string[] {"Middleware", "SignalRSendLog", "Enabled"}).ObjToBool()) + { + _hubContext.Clients.All.SendAsync("ReceiveUpdate", LogLock.GetLogData()).Wait(); + } + } - }); - } - } + private async Task SuccessAction(IInvocation invocation, AOPLogInfo apiLogAopInfo, DateTime startTime, object o = null) + { + //invocation.ReturnValue = o; + //var type = invocation.Method.ReturnType; + //if (typeof(Task).IsAssignableFrom(type)) + //{ + // //var resultProperty = type.GetProperty("Result"); + // //类型错误 都可以不要invocation参数,直接将o系列化保存到日记中 + // dataIntercept += ($"【执行完成结果】:{JsonConvert.SerializeObject(invocation.ReturnValue)}"); + //} + //else + //{ + // dataIntercept += ($"【执行完成结果】:{invocation.ReturnValue}"); + //} + DateTime endTime = DateTime.Now; + string ResponseTime = (endTime - startTime).Milliseconds.ToString(); + apiLogAopInfo.ResponseTime = endTime.ToString("yyyy-MM-dd hh:mm:ss fff"); + apiLogAopInfo.ResponseIntervalTime = ResponseTime + "ms"; + apiLogAopInfo.ResponseJsonData = JsonConvert.SerializeObject(o); - public static bool IsAsyncMethod(MethodInfo method) - { - return ( - method.ReturnType == typeof(Task) || - (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)) - ); - } + await Task.Run(() => + { + Parallel.For(0, 1, e => + { + //LogLock.OutSql2Log("AOPLog", new string[] { JsonConvert.SerializeObject(apiLogAopInfo) }); + LogLock.OutLogAOP("AOPLog", _accessor.HttpContext?.TraceIdentifier, + new string[] {apiLogAopInfo.GetType().ToString(), JsonConvert.SerializeObject(apiLogAopInfo)}); + }); + }); + } - } + private void LogEx(Exception ex, AOPLogInfo dataIntercept) + { + if (ex != null) + { + //执行的 service 中,收录异常 + MiniProfiler.Current.CustomTiming("Errors:", ex.Message); + //执行的 service 中,捕获异常 + //dataIntercept += ($"【执行完成结果】:方法中出现异常:{ex.Message + ex.InnerException}\r\n"); + AOPLogExInfo apiLogAopExInfo = new AOPLogExInfo + { + ExMessage = ex.Message, + InnerException = "InnerException-内部异常:\r\n" + (ex.InnerException == null ? "" : ex.InnerException.InnerException.ToString()) + + ("\r\nStackTrace-堆栈跟踪:\r\n") + (ex.StackTrace == null ? "" : ex.StackTrace.ToString()), + ApiLogAopInfo = dataIntercept + }; + // 异常日志里有详细的堆栈信息 + Parallel.For(0, 1, e => + { + //LogLock.OutLogAOP("AOPLogEx", new string[] { dataIntercept }); + LogLock.OutLogAOP("AOPLogEx", _accessor.HttpContext?.TraceIdentifier, + new string[] {apiLogAopExInfo.GetType().ToString(), JsonConvert.SerializeObject(apiLogAopExInfo)}); + }); + } + } - internal static class InternalAsyncHelper - { - public static async Task AwaitTaskWithPostActionAndFinally(Task actualReturnValue, Func postAction, Action finalAction) - { - Exception exception = null; + public static bool IsAsyncMethod(MethodInfo method) + { + return ( + method.ReturnType == typeof(Task) || + (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)) + ); + } + } - try - { - await actualReturnValue; - await postAction(); - } - catch (Exception ex) - { - exception = ex; - } - finally - { - finalAction(exception); - } - } - public static async Task AwaitTaskWithPostActionAndFinallyAndGetResult(Task actualReturnValue, Func postAction, Action finalAction) - { - Exception exception = null; - try - { - var result = await actualReturnValue; - await postAction(result); - return result; - } - catch (Exception ex) - { - exception = ex; - throw; - } - finally - { - finalAction(exception); - } - } + internal static class InternalAsyncHelper + { + public static async Task AwaitTaskWithPostActionAndFinally(Task actualReturnValue, Func postAction, Action finalAction) + { + Exception exception = null; - public static object CallAwaitTaskWithPostActionAndFinallyAndGetResult(Type taskReturnType, object actualReturnValue, Func action, Action finalAction) - { - return typeof(InternalAsyncHelper) - .GetMethod("AwaitTaskWithPostActionAndFinallyAndGetResult", BindingFlags.Public | BindingFlags.Static) - .MakeGenericMethod(taskReturnType) - .Invoke(null, new object[] { actualReturnValue, action, finalAction }); - } - } + try + { + await actualReturnValue; + await postAction(); + } + catch (Exception ex) + { + exception = ex; + } + finally + { + finalAction(exception); + } + } -} + public static async Task AwaitTaskWithPostActionAndFinallyAndGetResult(Task actualReturnValue, Func postAction, + Action finalAction) + { + Exception exception = null; + try + { + var result = await actualReturnValue; + await postAction(result); + return result; + } + catch (Exception ex) + { + exception = ex; + throw; + } + finally + { + finalAction(exception); + } + } + + public static object CallAwaitTaskWithPostActionAndFinallyAndGetResult(Type taskReturnType, object actualReturnValue, + Func action, Action finalAction) + { + return typeof(InternalAsyncHelper) + .GetMethod("AwaitTaskWithPostActionAndFinallyAndGetResult", BindingFlags.Public | BindingFlags.Static) + .MakeGenericMethod(taskReturnType) + .Invoke(null, new object[] {actualReturnValue, action, finalAction}); + } + } +} \ No newline at end of file diff --git a/Blog.Core.Extensions/Middlewares/ExceptionHandlerMiddleware.cs b/Blog.Core.Extensions/Middlewares/ExceptionHandlerMiddleware.cs index aed57769..7a7e1fb6 100644 --- a/Blog.Core.Extensions/Middlewares/ExceptionHandlerMiddleware.cs +++ b/Blog.Core.Extensions/Middlewares/ExceptionHandlerMiddleware.cs @@ -8,48 +8,49 @@ namespace Blog.Core.Extensions.Middlewares { - public class ExceptionHandlerMiddleware - { - private readonly RequestDelegate _next; - - public ExceptionHandlerMiddleware(RequestDelegate next) - { - _next = next; - } - - public async Task Invoke(HttpContext context) - { - try - { - await _next(context); - } - catch (Exception ex) - { - await HandleExceptionAsync(context, ex); - } - } - - private async Task HandleExceptionAsync(HttpContext context, Exception e) - { - if (e == null) return; - - Log.Error(e.GetBaseException().ToString()); - - await WriteExceptionAsync(context, e).ConfigureAwait(false); - } - - private static async Task WriteExceptionAsync(HttpContext context, Exception e) - { - if (e is UnauthorizedAccessException) - context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; - else if (e is Exception) - context.Response.StatusCode = (int)HttpStatusCode.BadRequest; - - context.Response.ContentType = "application/json"; - - await context.Response - .WriteAsync(JsonConvert.SerializeObject(new ApiResponse(StatusCode.CODE500, e.Message).MessageModel)) - .ConfigureAwait(false); - } - } + public class ExceptionHandlerMiddleware + { + private readonly RequestDelegate _next; + + public ExceptionHandlerMiddleware(RequestDelegate next) + { + _next = next; + } + + public async Task Invoke(HttpContext context) + { + try + { + await _next(context); + } + catch (Exception ex) + { + await HandleExceptionAsync(context, ex); + throw; + } + } + + private async Task HandleExceptionAsync(HttpContext context, Exception e) + { + if (e == null) return; + + Log.Error(e.GetBaseException().ToString()); + + await WriteExceptionAsync(context, e).ConfigureAwait(false); + } + + private static async Task WriteExceptionAsync(HttpContext context, Exception e) + { + if (e is UnauthorizedAccessException) + context.Response.StatusCode = (int) HttpStatusCode.Unauthorized; + else if (e is Exception) + context.Response.StatusCode = (int) HttpStatusCode.BadRequest; + + context.Response.ContentType = "application/json"; + + await context.Response + .WriteAsync(JsonConvert.SerializeObject(new ApiResponse(StatusCode.CODE500, e.Message).MessageModel)) + .ConfigureAwait(false); + } + } } \ No newline at end of file diff --git a/Blog.Core.Extensions/Middlewares/FluentResponseBodyMiddleware.cs b/Blog.Core.Extensions/Middlewares/FluentResponseBodyMiddleware.cs new file mode 100644 index 00000000..dfb5d19e --- /dev/null +++ b/Blog.Core.Extensions/Middlewares/FluentResponseBodyMiddleware.cs @@ -0,0 +1,21 @@ +using System.IO; +using Blog.Core.Common.Https; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http.Features; + +namespace Blog.Core.Extensions.Middlewares; + +public static class FluentResponseBodyMiddleware +{ + public static IApplicationBuilder UseResponseBodyRead(this IApplicationBuilder app) + { + return app.Use(async (context, next) => + { + await using var swapStream = new FluentHttpResponseStream(context!.Features!.Get()!, + context!.Features!.Get()!); + context.Response.Body = swapStream; + await next(context); + context.Response.Body.Seek(0, SeekOrigin.Begin); + }); + } +} \ No newline at end of file diff --git a/Blog.Core.Extensions/Middlewares/IpLogMiddleware.cs b/Blog.Core.Extensions/Middlewares/IpLogMiddleware.cs index ccd0d7af..61e9861c 100644 --- a/Blog.Core.Extensions/Middlewares/IpLogMiddleware.cs +++ b/Blog.Core.Extensions/Middlewares/IpLogMiddleware.cs @@ -8,139 +8,134 @@ namespace Blog.Core.Extensions.Middlewares { - /// - /// 中间件 - /// 记录IP请求数据 - /// - public class IpLogMiddleware - { - /// - /// - /// - private readonly RequestDelegate _next; - private readonly IWebHostEnvironment _environment; + /// + /// 中间件 + /// 记录IP请求数据 + /// + public class IpLogMiddleware + { + /// + /// + /// + private readonly RequestDelegate _next; - /// - /// - /// - /// - public IpLogMiddleware(RequestDelegate next, IWebHostEnvironment environment) - { - _next = next; - _environment = environment; - } + private readonly IWebHostEnvironment _environment; - public async Task InvokeAsync(HttpContext context) - { - if (AppSettings.app("Middleware", "IPLog", "Enabled").ObjToBool()) - { - // 过滤,只有接口 - if (context.Request.Path.Value.Contains("api")) - { - context.Request.EnableBuffering(); + /// + /// + /// + /// + public IpLogMiddleware(RequestDelegate next, IWebHostEnvironment environment) + { + _next = next; + _environment = environment; + } - try - { - // 存储请求数据 - var request = context.Request; + public async Task InvokeAsync(HttpContext context) + { + if (AppSettings.app("Middleware", "IPLog", "Enabled").ObjToBool()) + { + // 过滤,只有接口 + if (context.Request.Path.Value.Contains("api")) + { + context.Request.EnableBuffering(); - var requestInfo = JsonConvert.SerializeObject(new RequestInfo() - { - Ip = GetClientIP(context), - Url = request.Path.ObjToString().TrimEnd('/').ToLower(), - Datetime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), - Date = DateTime.Now.ToString("yyyy-MM-dd"), - Week = GetWeek(), - }); - if (!string.IsNullOrEmpty(requestInfo)) - { - // 自定义log输出 - Parallel.For(0, 1, e => - { - //LogLock.OutSql2Log("RequestIpInfoLog", new string[] { requestInfo + "," }, false); - LogLock.OutLogAOP("RequestIpInfoLog", context.TraceIdentifier, new string[] { requestInfo.GetType().ToString(), requestInfo }, false); - }); + // 存储请求数据 + var request = context.Request; - //try - //{ - // var testLogMatchRequestInfo = JsonConvert.DeserializeObject(requestInfo); - // if (testLogMatchRequestInfo != null) - // { - // var logFileName = FileHelper.GetAvailableFileNameWithPrefixOrderSize(_environment.ContentRootPath, "RequestIpInfoLog"); - // SerilogServer.WriteLog(logFileName, new string[] { requestInfo + "," }, false, "", true); - // } - //} - //catch (Exception e) - //{ - // log.Error(requestInfo + "\r\n" + e.GetBaseException().ToString()); - //} + var requestInfo = JsonConvert.SerializeObject(new RequestInfo() + { + Ip = GetClientIP(context), + Url = request.Path.ObjToString().TrimEnd('/').ToLower(), + Datetime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), + Date = DateTime.Now.ToString("yyyy-MM-dd"), + Week = GetWeek(), + }); + if (!string.IsNullOrEmpty(requestInfo)) + { + // 自定义log输出 + Parallel.For(0, 1, e => + { + //LogLock.OutSql2Log("RequestIpInfoLog", new string[] { requestInfo + "," }, false); + LogLock.OutLogAOP("RequestIpInfoLog", context.TraceIdentifier, + new string[] {requestInfo.GetType().ToString(), requestInfo}, false); + }); - request.Body.Position = 0; - } + //try + //{ + // var testLogMatchRequestInfo = JsonConvert.DeserializeObject(requestInfo); + // if (testLogMatchRequestInfo != null) + // { + // var logFileName = FileHelper.GetAvailableFileNameWithPrefixOrderSize(_environment.ContentRootPath, "RequestIpInfoLog"); + // SerilogServer.WriteLog(logFileName, new string[] { requestInfo + "," }, false, "", true); + // } + //} + //catch (Exception e) + //{ + // log.Error(requestInfo + "\r\n" + e.GetBaseException().ToString()); + //} + + request.Body.Position = 0; + } - await _next(context); - } - catch (Exception) - { - // ignored - } - } - else - { - await _next(context); - } - } - else - { - await _next(context); - } - } + await _next(context); + } + else + { + await _next(context); + } + } + else + { + await _next(context); + } + } - private string GetWeek() - { - string week = string.Empty; - switch (DateTime.Now.DayOfWeek) - { - case DayOfWeek.Monday: - week = "周一"; - break; - case DayOfWeek.Tuesday: - week = "周二"; - break; - case DayOfWeek.Wednesday: - week = "周三"; - break; - case DayOfWeek.Thursday: - week = "周四"; - break; - case DayOfWeek.Friday: - week = "周五"; - break; - case DayOfWeek.Saturday: - week = "周六"; - break; - case DayOfWeek.Sunday: - week = "周日"; - break; - default: - week = "N/A"; - break; - } - return week; - } + private string GetWeek() + { + string week = string.Empty; + switch (DateTime.Now.DayOfWeek) + { + case DayOfWeek.Monday: + week = "周一"; + break; + case DayOfWeek.Tuesday: + week = "周二"; + break; + case DayOfWeek.Wednesday: + week = "周三"; + break; + case DayOfWeek.Thursday: + week = "周四"; + break; + case DayOfWeek.Friday: + week = "周五"; + break; + case DayOfWeek.Saturday: + week = "周六"; + break; + case DayOfWeek.Sunday: + week = "周日"; + break; + default: + week = "N/A"; + break; + } - public static string GetClientIP(HttpContext context) - { - var ip = context.Request.Headers["X-Forwarded-For"].ObjToString(); - if (string.IsNullOrEmpty(ip)) - { - ip = context.Connection.RemoteIpAddress.ObjToString(); - } - return ip; - } + return week; + } - } -} + public static string GetClientIP(HttpContext context) + { + var ip = context.Request.Headers["X-Forwarded-For"].ObjToString(); + if (string.IsNullOrEmpty(ip)) + { + ip = context.Connection.RemoteIpAddress.ObjToString(); + } + return ip; + } + } +} \ No newline at end of file diff --git a/Blog.Core.Extensions/Middlewares/RecordAccessLogsMiddleware.cs b/Blog.Core.Extensions/Middlewares/RecordAccessLogsMiddleware.cs index 4e12dd93..5c203fb3 100644 --- a/Blog.Core.Extensions/Middlewares/RecordAccessLogsMiddleware.cs +++ b/Blog.Core.Extensions/Middlewares/RecordAccessLogsMiddleware.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using System.Web; using Blog.Core.Common; +using Blog.Core.Common.Extensions; using Blog.Core.Common.Helper; using Blog.Core.Common.HttpContextUser; using Blog.Core.Common.LogHelper; @@ -15,146 +16,121 @@ namespace Blog.Core.Extensions.Middlewares { - /// - /// 中间件 - /// 记录用户方访问数据 - /// - public class RecordAccessLogsMiddleware - { - /// - /// - /// - private readonly RequestDelegate _next; - private readonly IUser _user; - private readonly ILogger _logger; - private readonly IWebHostEnvironment _environment; - private Stopwatch _stopwatch; - - /// - /// - /// - /// - public RecordAccessLogsMiddleware(RequestDelegate next, IUser user, ILogger logger, IWebHostEnvironment environment) - { - _next = next; - _user = user; - _logger = logger; - _environment = environment; - _stopwatch = new Stopwatch(); - } - - public async Task InvokeAsync(HttpContext context) - { - if (AppSettings.app("Middleware", "RecordAccessLogs", "Enabled").ObjToBool()) - { - var api = context.Request.Path.ObjToString().TrimEnd('/').ToLower(); - var ignoreApis = AppSettings.app("Middleware", "RecordAccessLogs", "IgnoreApis"); - - // 过滤,只有接口 - if (api.Contains("api") && !ignoreApis.Contains(api)) - { - _stopwatch.Restart(); - var userAccessModel = new UserAccessModel(); - - HttpRequest request = context.Request; - - userAccessModel.API = api; - userAccessModel.User = _user.Name; - userAccessModel.IP = IpLogMiddleware.GetClientIP(context); - userAccessModel.BeginTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); - userAccessModel.RequestMethod = request.Method; - userAccessModel.Agent = request.Headers["User-Agent"].ObjToString(); - - - // 获取请求body内容 - if (request.Method.ToLower().Equals("post") || request.Method.ToLower().Equals("put")) - { - // 启用倒带功能,就可以让 Request.Body 可以再次读取 - request.EnableBuffering(); - - Stream stream = request.Body; - byte[] buffer = new byte[request.ContentLength.Value]; - stream.Read(buffer, 0, buffer.Length); - userAccessModel.RequestData = Encoding.UTF8.GetString(buffer); - - request.Body.Position = 0; - } - else if (request.Method.ToLower().Equals("get") || request.Method.ToLower().Equals("delete")) - { - userAccessModel.RequestData = HttpUtility.UrlDecode(request.QueryString.ObjToString(), Encoding.UTF8); - } - - // 获取Response.Body内容 - var originalBodyStream = context.Response.Body; - using (var responseBody = new MemoryStream()) - { - context.Response.Body = responseBody; - - await _next(context); - - var responseBodyData = await GetResponse(context.Response); - - await responseBody.CopyToAsync(originalBodyStream); - } - - // 响应完成记录时间和存入日志 - context.Response.OnCompleted(() => - { - _stopwatch.Stop(); - - userAccessModel.OPTime = _stopwatch.ElapsedMilliseconds + "ms"; - - // 自定义log输出 - var requestInfo = JsonConvert.SerializeObject(userAccessModel); - Parallel.For(0, 1, e => - { - //LogLock.OutSql2Log("RecordAccessLogs", new string[] { requestInfo + "," }, false); - LogLock.OutLogAOP("RecordAccessLogs", context.TraceIdentifier, new string[] { userAccessModel.GetType().ToString(), requestInfo }, false); - }); - //var logFileName = FileHelper.GetAvailableFileNameWithPrefixOrderSize(_environment.ContentRootPath, "RecordAccessLogs"); - //SerilogServer.WriteLog(logFileName, new string[] { requestInfo + "," }, false); - return Task.CompletedTask; - }); - - } - else - { - await _next(context); - } - } - else - { - await _next(context); - } - } - - - /// - /// 获取响应内容 - /// - /// - /// - public async Task GetResponse(HttpResponse response) - { - response.Body.Seek(0, SeekOrigin.Begin); - var text = await new StreamReader(response.Body).ReadToEndAsync(); - response.Body.Seek(0, SeekOrigin.Begin); - return text; - } - } - - public class UserAccessModel - { - public string User { get; set; } - public string IP { get; set; } - public string API { get; set; } - public string BeginTime { get; set; } - public string OPTime { get; set; } - public string RequestMethod { get; set; } - public string RequestData { get; set; } - public string Agent { get; set; } - - } - -} - + /// + /// 中间件 + /// 记录用户方访问数据 + /// + public class RecordAccessLogsMiddleware + { + /// + /// + /// + private readonly RequestDelegate _next; + + private readonly IUser _user; + private readonly ILogger _logger; + private readonly IWebHostEnvironment _environment; + private Stopwatch _stopwatch; + + /// + /// + /// + /// + public RecordAccessLogsMiddleware(RequestDelegate next, IUser user, ILogger logger, + IWebHostEnvironment environment) + { + _next = next; + _user = user; + _logger = logger; + _environment = environment; + _stopwatch = new Stopwatch(); + } + + public async Task InvokeAsync(HttpContext context) + { + if (AppSettings.app("Middleware", "RecordAccessLogs", "Enabled").ObjToBool()) + { + var api = context.Request.Path.ObjToString().TrimEnd('/').ToLower(); + var ignoreApis = AppSettings.app("Middleware", "RecordAccessLogs", "IgnoreApis"); + + // 过滤,只有接口 + if (api.Contains("api") && !ignoreApis.Contains(api)) + { + _stopwatch.Restart(); + var userAccessModel = new UserAccessModel(); + + HttpRequest request = context.Request; + + userAccessModel.API = api; + userAccessModel.User = _user.Name; + userAccessModel.IP = IpLogMiddleware.GetClientIP(context); + userAccessModel.BeginTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); + userAccessModel.RequestMethod = request.Method; + userAccessModel.Agent = request.Headers["User-Agent"].ObjToString(); + + + // 获取请求body内容 + if (request.Method.ToLower().Equals("post") || request.Method.ToLower().Equals("put")) + { + // 启用倒带功能,就可以让 Request.Body 可以再次读取 + request.EnableBuffering(); + + Stream stream = request.Body; + byte[] buffer = new byte[request.ContentLength.Value]; + stream.Read(buffer, 0, buffer.Length); + userAccessModel.RequestData = Encoding.UTF8.GetString(buffer); + + request.Body.Position = 0; + } + else if (request.Method.ToLower().Equals("get") || request.Method.ToLower().Equals("delete")) + { + userAccessModel.RequestData = HttpUtility.UrlDecode(request.QueryString.ObjToString(), Encoding.UTF8); + } + + await _next(context); + + // 响应完成记录时间和存入日志 + context.Response.OnCompleted(() => + { + _stopwatch.Stop(); + + userAccessModel.OPTime = _stopwatch.ElapsedMilliseconds + "ms"; + + // 自定义log输出 + var requestInfo = JsonConvert.SerializeObject(userAccessModel); + Parallel.For(0, 1, e => + { + //LogLock.OutSql2Log("RecordAccessLogs", new string[] { requestInfo + "," }, false); + LogLock.OutLogAOP("RecordAccessLogs", context.TraceIdentifier, + new string[] {userAccessModel.GetType().ToString(), requestInfo}, false); + }); + //var logFileName = FileHelper.GetAvailableFileNameWithPrefixOrderSize(_environment.ContentRootPath, "RecordAccessLogs"); + //SerilogServer.WriteLog(logFileName, new string[] { requestInfo + "," }, false); + return Task.CompletedTask; + }); + } + else + { + await _next(context); + } + } + else + { + await _next(context); + } + } + + } + + public class UserAccessModel + { + public string User { get; set; } + public string IP { get; set; } + public string API { get; set; } + public string BeginTime { get; set; } + public string OPTime { get; set; } + public string RequestMethod { get; set; } + public string RequestData { get; set; } + public string Agent { get; set; } + } +} \ No newline at end of file diff --git a/Blog.Core.Extensions/Middlewares/RequRespLogMiddleware.cs b/Blog.Core.Extensions/Middlewares/RequRespLogMiddleware.cs index 4c61ed3b..26971a3a 100644 --- a/Blog.Core.Extensions/Middlewares/RequRespLogMiddleware.cs +++ b/Blog.Core.Extensions/Middlewares/RequRespLogMiddleware.cs @@ -3,6 +3,7 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using Blog.Core.Common; +using Blog.Core.Common.Extensions; using Blog.Core.Common.LogHelper; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; @@ -10,126 +11,124 @@ namespace Blog.Core.Extensions.Middlewares { - /// - /// 中间件 - /// 记录请求和响应数据 - /// - public class RequRespLogMiddleware - { - /// - /// - /// - private readonly RequestDelegate _next; - private readonly ILogger _logger; - - /// - /// - /// - /// - public RequRespLogMiddleware(RequestDelegate next, ILogger logger) - { - _next = next; - _logger = logger; - } - - - - public async Task InvokeAsync(HttpContext context) - { - if (AppSettings.app("Middleware", "RequestResponseLog", "Enabled").ObjToBool()) - { - // 过滤,只有接口 - if (context.Request.Path.Value.Contains("api")) - { - context.Request.EnableBuffering(); - Stream originalBody = context.Response.Body; - - try - { - // 存储请求数据 - await RequestDataLog(context); - - using (var ms = new MemoryStream()) - { - context.Response.Body = ms; - - await _next(context); - - // 存储响应数据 - ResponseDataLog(context.Response, ms); - - ms.Position = 0; - await ms.CopyToAsync(originalBody); - } - } - catch (Exception ex) - { - // 记录异常 - _logger.LogError(ex.Message + "" + ex.InnerException); - } - finally - { - context.Response.Body = originalBody; - } - } - else - { - await _next(context); - } - } - else - { - await _next(context); - } - } - - private async Task RequestDataLog(HttpContext context) - { - var request = context.Request; - var sr = new StreamReader(request.Body); - RequestLogInfo requestResponse = new RequestLogInfo() - { - Path = request.Path, - QueryString = request.QueryString.ToString(), - BodyData = await sr.ReadToEndAsync() - }; - var content = JsonConvert.SerializeObject(requestResponse); - //var content = $" QueryData:{request.Path + request.QueryString}\r\n BodyData:{await sr.ReadToEndAsync()}"; - - if (!string.IsNullOrEmpty(content)) - { - Parallel.For(0, 1, e => - { - //LogLock.OutSql2Log("RequestResponseLog", new string[] { "Request Data:", content }); - LogLock.OutLogAOP("RequestResponseLog", context.TraceIdentifier, new string[] { "Request Data - RequestJsonDataType:" + requestResponse.GetType().ToString(), content }); - - }); - //SerilogServer.WriteLog("RequestResponseLog", new string[] { "Request Data:", content }); - - request.Body.Position = 0; - } - } - - private void ResponseDataLog(HttpResponse response, MemoryStream ms) - { - ms.Position = 0; - var responseBody = new StreamReader(ms).ReadToEnd(); - - // 去除 Html - var reg = "<[^>]+>"; - var isHtml = Regex.IsMatch(responseBody, reg); - - if (!string.IsNullOrEmpty(responseBody)) - { - Parallel.For(0, 1, e => - { - //LogLock.OutSql2Log("RequestResponseLog", new string[] { "Response Data:", ResponseBody }); - LogLock.OutLogAOP("RequestResponseLog", response.HttpContext.TraceIdentifier, new string[] { "Response Data - ResponseJsonDataType:" + responseBody.GetType().ToString(), responseBody }); - - }); - //SerilogServer.WriteLog("RequestResponseLog", new string[] { "Response Data:", responseBody }); - } - } - } -} - + /// + /// 中间件 + /// 记录请求和响应数据 + /// + public class RequRespLogMiddleware + { + /// + /// + /// + private readonly RequestDelegate _next; + + private readonly ILogger _logger; + + /// + /// + /// + /// + public RequRespLogMiddleware(RequestDelegate next, ILogger logger) + { + _next = next; + _logger = logger; + } + + + public async Task InvokeAsync(HttpContext context) + { + if (AppSettings.app("Middleware", "RequestResponseLog", "Enabled").ObjToBool()) + { + // 过滤,只有接口 + if (context.Request.Path.Value.Contains("api")) + { + context.Request.EnableBuffering(); + + // 存储请求数据 + await RequestDataLog(context); + + await _next(context); + + // 存储响应数据 + ResponseDataLog(context.Response); + } + else + { + await _next(context); + } + } + else + { + await _next(context); + } + } + + private async Task RequestDataLog(HttpContext context) + { + var request = context.Request; + var sr = new StreamReader(request.Body); + RequestLogInfo requestResponse = new RequestLogInfo() + { + Path = request.Path, + QueryString = request.QueryString.ToString(), + BodyData = await sr.ReadToEndAsync() + }; + var content = JsonConvert.SerializeObject(requestResponse); + //var content = $" QueryData:{request.Path + request.QueryString}\r\n BodyData:{await sr.ReadToEndAsync()}"; + + if (!string.IsNullOrEmpty(content)) + { + Parallel.For(0, 1, e => + { + //LogLock.OutSql2Log("RequestResponseLog", new string[] { "Request Data:", content }); + LogLock.OutLogAOP("RequestResponseLog", context.TraceIdentifier, + new string[] {"Request Data - RequestJsonDataType:" + requestResponse.GetType().ToString(), content}); + }); + //SerilogServer.WriteLog("RequestResponseLog", new string[] { "Request Data:", content }); + + request.Body.Position = 0; + } + } + + private void ResponseDataLog(HttpResponse response) + { + var responseBody = response.GetResponseBody(); + + // 去除 Html + var reg = "<[^>]+>"; + var isHtml = Regex.IsMatch(responseBody, reg); + + if (!string.IsNullOrEmpty(responseBody)) + { + Parallel.For(0, 1, e => + { + //LogLock.OutSql2Log("RequestResponseLog", new string[] { "Response Data:", ResponseBody }); + LogLock.OutLogAOP("RequestResponseLog", response.HttpContext.TraceIdentifier, + new string[] {"Response Data - ResponseJsonDataType:" + responseBody.GetType().ToString(), responseBody}); + }); + //SerilogServer.WriteLog("RequestResponseLog", new string[] { "Response Data:", responseBody }); + } + } + + private void ResponseDataLog(HttpResponse response, MemoryStream ms) + { + ms.Position = 0; + var responseBody = new StreamReader(ms).ReadToEnd(); + + // 去除 Html + var reg = "<[^>]+>"; + var isHtml = Regex.IsMatch(responseBody, reg); + + if (!string.IsNullOrEmpty(responseBody)) + { + Parallel.For(0, 1, e => + { + //LogLock.OutSql2Log("RequestResponseLog", new string[] { "Response Data:", ResponseBody }); + LogLock.OutLogAOP("RequestResponseLog", response.HttpContext.TraceIdentifier, + new string[] {"Response Data - ResponseJsonDataType:" + responseBody.GetType().ToString(), responseBody}); + }); + //SerilogServer.WriteLog("RequestResponseLog", new string[] { "Response Data:", responseBody }); + } + } + } +} \ No newline at end of file diff --git a/Blog.Core.Extensions/Redis/RedisBasketRepository.cs b/Blog.Core.Extensions/Redis/RedisBasketRepository.cs index 7f1356e2..822f9253 100644 --- a/Blog.Core.Extensions/Redis/RedisBasketRepository.cs +++ b/Blog.Core.Extensions/Redis/RedisBasketRepository.cs @@ -4,12 +4,13 @@ using StackExchange.Redis; using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Threading.Tasks; namespace Blog.Core.Extensions { - [Obsolete("普通缓存考虑直接使用ICaching,如果要使用Redis队列等还是使用此类")] + [Description("普通缓存考虑直接使用ICaching,如果要使用Redis队列等还是使用此类")] public class RedisBasketRepository : IRedisBasketRepository { private readonly ILogger _logger; From 45116fe5afbd480d85c1bec233ac67293b6a04fd Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Tue, 13 Jun 2023 17:29:41 +0800 Subject: [PATCH 59/84] =?UTF-8?q?=F0=9F=8E=A8=20=E5=90=AF=E7=94=A8?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E5=A4=84=E7=90=86=E4=B8=AD=E9=97=B4=E4=BB=B6?= =?UTF-8?q?,=E7=BB=9F=E4=B8=80=E6=8B=A6=E6=88=AA=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Api/Program.cs | 1 + .../Middlewares/ExceptionHandlerMiddleware.cs | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Blog.Core.Api/Program.cs b/Blog.Core.Api/Program.cs index 67d4524e..eee48a6e 100644 --- a/Blog.Core.Api/Program.cs +++ b/Blog.Core.Api/Program.cs @@ -144,6 +144,7 @@ //app.UseHsts(); } +app.UseExceptionHandlerMiddle(); app.UseIpLimitMiddle(); app.UseRequestResponseLogMiddle(); app.UseRecordAccessLogsMiddle(); diff --git a/Blog.Core.Extensions/Middlewares/ExceptionHandlerMiddleware.cs b/Blog.Core.Extensions/Middlewares/ExceptionHandlerMiddleware.cs index 7a7e1fb6..03168882 100644 --- a/Blog.Core.Extensions/Middlewares/ExceptionHandlerMiddleware.cs +++ b/Blog.Core.Extensions/Middlewares/ExceptionHandlerMiddleware.cs @@ -26,7 +26,6 @@ public async Task Invoke(HttpContext context) catch (Exception ex) { await HandleExceptionAsync(context, ex); - throw; } } @@ -34,22 +33,26 @@ private async Task HandleExceptionAsync(HttpContext context, Exception e) { if (e == null) return; - Log.Error(e.GetBaseException().ToString()); - await WriteExceptionAsync(context, e).ConfigureAwait(false); } private static async Task WriteExceptionAsync(HttpContext context, Exception e) { - if (e is UnauthorizedAccessException) - context.Response.StatusCode = (int) HttpStatusCode.Unauthorized; - else if (e is Exception) - context.Response.StatusCode = (int) HttpStatusCode.BadRequest; + var message = e.Message; + switch (e) + { + case UnauthorizedAccessException: + context.Response.StatusCode = (int) HttpStatusCode.Unauthorized; + break; + default: + context.Response.StatusCode = (int) HttpStatusCode.BadRequest; + break; + } context.Response.ContentType = "application/json"; await context.Response - .WriteAsync(JsonConvert.SerializeObject(new ApiResponse(StatusCode.CODE500, e.Message).MessageModel)) + .WriteAsync(JsonConvert.SerializeObject(new ApiResponse(StatusCode.CODE500, message).MessageModel)) .ConfigureAwait(false); } } From 0c837cd50e9c4b0f86e81ca75a11da0f0a929a23 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Tue, 13 Jun 2023 17:58:57 +0800 Subject: [PATCH 60/84] :a: change MigratePermission api --- Blog.Core.Api/Blog.Core.xml | 6 +--- .../Controllers/PermissionController.cs | 36 +++++++++---------- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/Blog.Core.Api/Blog.Core.xml b/Blog.Core.Api/Blog.Core.xml index 69d3f9b2..ad812e58 100644 --- a/Blog.Core.Api/Blog.Core.xml +++ b/Blog.Core.Api/Blog.Core.xml @@ -456,14 +456,10 @@ - + 系统接口菜单同步接口 - - 接口module的控制器名称 - 菜单permission的父id - 是否执行迁移到数据 diff --git a/Blog.Core.Api/Controllers/PermissionController.cs b/Blog.Core.Api/Controllers/PermissionController.cs index 7346cc21..a3429163 100644 --- a/Blog.Core.Api/Controllers/PermissionController.cs +++ b/Blog.Core.Api/Controllers/PermissionController.cs @@ -7,7 +7,6 @@ using Blog.Core.Model; using Blog.Core.Model.Models; using Blog.Core.Repository.UnitOfWorks; -using Blog.Core.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; @@ -633,13 +632,9 @@ public async Task> BatchPost([FromBody] List pe /// /// 系统接口菜单同步接口 /// - /// - /// 接口module的控制器名称 - /// 菜单permission的父id - /// 是否执行迁移到数据 /// [HttpGet] - public async Task>> MigratePermission(string action = "", string controllerName = "", long pid = 0, bool isAction = false) + public async Task>> MigratePermission(string action = "", string token = "", string gatewayPrefix = "", string swaggerDomain = "", string controllerName = "", long pid = 0, bool isAction = false) { var data = new MessageModel>(); if (controllerName.IsNullOrEmpty()) @@ -648,18 +643,23 @@ public async Task>> MigratePermission(string actio return data; } - controllerName = controllerName.ToLower(); + controllerName = controllerName.TrimEnd('/').ToLower(); - using var client = _httpClientFactory.CreateClient(); - var jsonFileDomain = AppSettings.GetValue("Startup:Domain"); + gatewayPrefix = gatewayPrefix.Trim(); + swaggerDomain = swaggerDomain.Trim(); + controllerName = controllerName.Trim(); - if (jsonFileDomain.IsNullOrEmpty()) + using var client = _httpClientFactory.CreateClient(); + var Configuration = swaggerDomain.IsNotEmptyOrNull() ? swaggerDomain : AppSettings.GetValue("SystemCfg:Domain"); + var url = $"{Configuration}/swagger/V2/swagger.json"; + if (Configuration.IsNullOrEmpty()) { data.msg = "Swagger.json在线文件域名不能为空"; return data; } - - var url = $"{jsonFileDomain}/swagger/V2/swagger.json"; + if (token.IsNullOrEmpty()) token = Request.Headers.Authorization; + token = token.Trim(); + client.DefaultRequestHeaders.Add("Authorization", $"{token}"); var response = await client.GetAsync(url); var body = await response.Content.ReadAsStringAsync(); @@ -671,9 +671,10 @@ public async Task>> MigratePermission(string actio List permissions = new List(); foreach (JProperty jProperty in pathsJObj.Properties()) { - var apiPath = jProperty.Name.ToLower(); + var apiPath = gatewayPrefix + jProperty.Name.ToLower(); if (action.IsNotEmptyOrNull()) { + action = action.Trim(); if (!apiPath.Contains(action.ToLower())) { continue; @@ -697,12 +698,12 @@ public async Task>> MigratePermission(string actio httpmethod = "delete"; } - var summary = jProperty.Value.SelectToken($"{httpmethod}.summary").ObjToString(); + var summary = jProperty.Value?.SelectToken($"{httpmethod}.summary")?.ObjToString() ?? ""; var subIx = summary.IndexOf("(Auth"); - if (subIx > 0) + if (subIx >= 0) { - summary = summary.Substring(0, subIx - 1); + summary = summary.Substring(0, subIx); } permissions.Add(new Permission() @@ -715,7 +716,6 @@ public async Task>> MigratePermission(string actio CreateTime = DateTime.Now, IsDeleted = false, Pid = pid, - MName = apiPath ?? "", Module = new Modules() { LinkUrl = apiPath ?? "", @@ -748,6 +748,7 @@ public async Task>> MigratePermission(string actio } } + data.msg = "同步完成"; } data.response = permissions; @@ -756,7 +757,6 @@ public async Task>> MigratePermission(string actio return data; } - } public class AssignView From bb0150e96a1d605382a917b173cccb91fb903c59 Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Mon, 19 Jun 2023 16:43:29 +0800 Subject: [PATCH 61/84] =?UTF-8?q?=F0=9F=8E=A8=20=E4=B8=8D=E5=85=B3?= =?UTF-8?q?=E9=97=AD=E5=BA=95=E5=B1=82=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Common/Extensions/HttpResponseExceptions.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Blog.Core.Common/Extensions/HttpResponseExceptions.cs b/Blog.Core.Common/Extensions/HttpResponseExceptions.cs index b9d25f81..67deee45 100644 --- a/Blog.Core.Common/Extensions/HttpResponseExceptions.cs +++ b/Blog.Core.Common/Extensions/HttpResponseExceptions.cs @@ -17,7 +17,8 @@ public static string GetResponseBody(this HttpResponse response) if (response.Body is FluentHttpResponseStream responseBody) { response.Body.Position = 0; - using StreamReader stream = new StreamReader(responseBody); + //不关闭底层流 + using StreamReader stream = new StreamReader(responseBody, leaveOpen: true); string body = stream.ReadToEnd(); response.Body.Position = 0; return body; From 0d967086d9d3742328bd07dd4f293735340ec689 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Wed, 21 Jun 2023 17:35:13 +0800 Subject: [PATCH 62/84] :accept: feat: add request access etc. log --- Blog.Core.Api/Controllers/ValuesController.cs | 22 ++++++++++++++++--- Blog.Core.Api/appsettings.json | 6 ++--- Blog.Core.Common/LogHelper/LogLock.cs | 9 ++++---- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/Blog.Core.Api/Controllers/ValuesController.cs b/Blog.Core.Api/Controllers/ValuesController.cs index ae5cc81f..072ab39e 100644 --- a/Blog.Core.Api/Controllers/ValuesController.cs +++ b/Blog.Core.Api/Controllers/ValuesController.cs @@ -30,7 +30,7 @@ namespace Blog.Core.Controllers //[Authorize(Policy = "SystemOrAdmin")] //[Authorize(PermissionNames.Permission)] [Authorize] - public class ValuesController : ControllerBase + public class ValuesController : BaseApiController { private IMapper _mapper; private readonly IAdvertisementServices _advertisementServices; @@ -159,12 +159,12 @@ await _blogArticleServices.QuerySql( * `bsubmitter`=@bsubmitter,`IsDeleted`=@IsDeleted WHERE `bID`=@bID */ var updateSql = await _blogArticleServices.Update(new - { bsubmitter = $"laozhang{DateTime.Now.Millisecond}", IsDeleted = false, bID = 5 }); + { bsubmitter = $"laozhang{DateTime.Now.Millisecond}", IsDeleted = false, bID = 5 }); // 测试模拟异常,全局异常过滤器拦截 var i = 0; - // var d = 3 / i; + // var d = 3 / i; // 测试 AOP 缓存 @@ -188,6 +188,22 @@ await _blogArticleServices.QuerySql( return data; } + + [HttpGet] + [AllowAnonymous] + public async Task>> Test_Aop_Cache() + { + // 测试 AOP 缓存 + var blogArticles = await _blogArticleServices.GetBlogs(); + + if (blogArticles.Any()) + { + return Success(blogArticles); + } + + return Failed>(); + } + /// /// 测试Redis消息队列 /// diff --git a/Blog.Core.Api/appsettings.json b/Blog.Core.Api/appsettings.json index def53cfc..941d0125 100644 --- a/Blog.Core.Api/appsettings.json +++ b/Blog.Core.Api/appsettings.json @@ -210,7 +210,7 @@ "RequestResponseLog": { "Enabled": true, "LogToFile": { - "Enabled": false + "Enabled": true }, "LogToDB": { "Enabled": true @@ -219,7 +219,7 @@ "IPLog": { "Enabled": true, "LogToFile": { - "Enabled": false + "Enabled": true }, "LogToDB": { "Enabled": true @@ -228,7 +228,7 @@ "RecordAccessLogs": { "Enabled": true, "LogToFile": { - "Enabled": false + "Enabled": true }, "LogToDB": { "Enabled": true diff --git a/Blog.Core.Common/LogHelper/LogLock.cs b/Blog.Core.Common/LogHelper/LogLock.cs index 2c9be9a9..e03c6366 100644 --- a/Blog.Core.Common/LogHelper/LogLock.cs +++ b/Blog.Core.Common/LogHelper/LogLock.cs @@ -195,26 +195,25 @@ public static void OutSql2LogToDB(string prefix, string traceId, string[] dataPa { //DEBUG | INFO | WARN | ERROR | FATAL case "AOPLog": - //TODO 是否需要输出? - //Log.Information(logContent); + Log.Information(logContent); break; case "AOPLogEx": Log.Error(logContent); break; case "RequestIpInfoLog": //TODO 是否需要Debug输出? - //Log.Debug(logContent); + Log.Information(logContent); break; case "RecordAccessLogs": //TODO 是否需要Debug输出? - //Log.Debug(logContent); + Log.Information(logContent); break; case "SqlLog": Log.Information(logContent); break; case "RequestResponseLog": //TODO 是否需要Debug输出? - //Log.Debug(logContent); + Log.Information(logContent); break; default: break; From 274f3c1760cf80532c76fafc38cdb5c32ff7f813 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Sun, 25 Jun 2023 16:44:16 +0800 Subject: [PATCH 63/84] Update Program.cs --- Blog.Core.Api/Program.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Blog.Core.Api/Program.cs b/Blog.Core.Api/Program.cs index eee48a6e..876a83a8 100644 --- a/Blog.Core.Api/Program.cs +++ b/Blog.Core.Api/Program.cs @@ -180,7 +180,6 @@ app.UseAuthentication(); app.UseAuthorization(); app.UseMiniProfilerMiddleware(); -//app.UseExceptionHandlerMidd(); app.UseEndpoints(endpoints => { From 990dcfd756d520600501ca9deba0878d36c4fd72 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Sun, 25 Jun 2023 17:01:14 +0800 Subject: [PATCH 64/84] Update ExceptionHandlerMiddleware.cs --- .../Middlewares/ExceptionHandlerMiddleware.cs | 97 +++++++++---------- 1 file changed, 48 insertions(+), 49 deletions(-) diff --git a/Blog.Core.Extensions/Middlewares/ExceptionHandlerMiddleware.cs b/Blog.Core.Extensions/Middlewares/ExceptionHandlerMiddleware.cs index 03168882..dc2cd17d 100644 --- a/Blog.Core.Extensions/Middlewares/ExceptionHandlerMiddleware.cs +++ b/Blog.Core.Extensions/Middlewares/ExceptionHandlerMiddleware.cs @@ -4,56 +4,55 @@ using Blog.Core.Model; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; -using Serilog; namespace Blog.Core.Extensions.Middlewares { - public class ExceptionHandlerMiddleware - { - private readonly RequestDelegate _next; - - public ExceptionHandlerMiddleware(RequestDelegate next) - { - _next = next; - } - - public async Task Invoke(HttpContext context) - { - try - { - await _next(context); - } - catch (Exception ex) - { - await HandleExceptionAsync(context, ex); - } - } - - private async Task HandleExceptionAsync(HttpContext context, Exception e) - { - if (e == null) return; - - await WriteExceptionAsync(context, e).ConfigureAwait(false); - } - - private static async Task WriteExceptionAsync(HttpContext context, Exception e) - { - var message = e.Message; - switch (e) - { - case UnauthorizedAccessException: - context.Response.StatusCode = (int) HttpStatusCode.Unauthorized; - break; - default: - context.Response.StatusCode = (int) HttpStatusCode.BadRequest; - break; - } - - context.Response.ContentType = "application/json"; - - await context.Response - .WriteAsync(JsonConvert.SerializeObject(new ApiResponse(StatusCode.CODE500, message).MessageModel)) - .ConfigureAwait(false); - } - } + public class ExceptionHandlerMiddleware + { + private readonly RequestDelegate _next; + + public ExceptionHandlerMiddleware(RequestDelegate next) + { + _next = next; + } + + public async Task Invoke(HttpContext context) + { + try + { + await _next(context); + } + catch (Exception ex) + { + await HandleExceptionAsync(context, ex); + } + } + + private async Task HandleExceptionAsync(HttpContext context, Exception e) + { + if (e == null) return; + + await WriteExceptionAsync(context, e).ConfigureAwait(false); + } + + private static async Task WriteExceptionAsync(HttpContext context, Exception e) + { + var message = e.Message; + switch (e) + { + case UnauthorizedAccessException: + context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; + break; + default: + context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; + break; + } + + context.Response.ContentType = "application/json"; + + await context.Response + .WriteAsync(JsonConvert.SerializeObject(new ApiResponse(StatusCode.CODE500, message).MessageModel)) + .ConfigureAwait(false); + } + } } \ No newline at end of file From f5a2631d7ef31f004234563743f805c73ebbdfab Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Wed, 5 Jul 2023 17:34:40 +0800 Subject: [PATCH 65/84] =?UTF-8?q?=E2=9C=A8=20SignalR=20Demo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 优化SignalR,通过传递token确认身份 --- Blog.Core.Api/Blog.Core.xml | 21 ++++++++ .../Controllers/SignalRTestController.cs | 49 +++++++++++++++++++ Blog.Core.Api/appsettings.json | 4 +- Blog.Core.Common/Hubs/ChatHub.cs | 31 ++++++++---- .../Authentication_JWTSetup.cs | 14 ++++++ 5 files changed, 108 insertions(+), 11 deletions(-) create mode 100644 Blog.Core.Api/Controllers/SignalRTestController.cs diff --git a/Blog.Core.Api/Blog.Core.xml b/Blog.Core.Api/Blog.Core.xml index ad812e58..6dd7f16b 100644 --- a/Blog.Core.Api/Blog.Core.xml +++ b/Blog.Core.Api/Blog.Core.xml @@ -1251,6 +1251,27 @@ + + + SignalR测试 + + + + + 向指定用户发送消息 + + + + + + + + 向指定角色发送消息 + + + + + 分表demo diff --git a/Blog.Core.Api/Controllers/SignalRTestController.cs b/Blog.Core.Api/Controllers/SignalRTestController.cs new file mode 100644 index 00000000..16ba47f0 --- /dev/null +++ b/Blog.Core.Api/Controllers/SignalRTestController.cs @@ -0,0 +1,49 @@ +using Blog.Core.Controllers; +using Blog.Core.Hubs; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.SignalR; + +namespace Blog.Core.Api.Controllers; + +/// +/// SignalR测试 +/// +[Route("api/[controller]/[action]")] +[ApiController] +[Authorize] +public class SignalRTestController : BaseApiController +{ + private readonly IHubContext _hubContext; + + public SignalRTestController(IHubContext hubContext) + { + _hubContext = hubContext; + } + + /// + /// 向指定用户发送消息 + /// + /// + /// + /// + [HttpPost] + public async Task SendMessageToUser(string user, string message) + { + await _hubContext.Clients.Group(user).ReceiveMessage(user, message); + return Ok(); + } + + /// + /// 向指定角色发送消息 + /// + /// + /// + /// + [HttpPost] + public async Task SendMessageToRole(string role, string message) + { + await _hubContext.Clients.Group(role).ReceiveMessage(role, message); + return Ok(); + } +} \ No newline at end of file diff --git a/Blog.Core.Api/appsettings.json b/Blog.Core.Api/appsettings.json index 941d0125..5fdf0000 100644 --- a/Blog.Core.Api/appsettings.json +++ b/Blog.Core.Api/appsettings.json @@ -236,10 +236,10 @@ "IgnoreApis": "/api/permission/getnavigationbar,/api/monitor/getids4users,/api/monitor/getaccesslogs,/api/monitor/server,/api/monitor/getactiveusers,/api/monitor/server," }, "SignalR": { - "Enabled": false + "Enabled": true }, "SignalRSendLog": { - "Enabled": false + "Enabled": true }, "QuartzNetJob": { "Enabled": true diff --git a/Blog.Core.Common/Hubs/ChatHub.cs b/Blog.Core.Common/Hubs/ChatHub.cs index 1c58c8a0..f30e6b7b 100644 --- a/Blog.Core.Common/Hubs/ChatHub.cs +++ b/Blog.Core.Common/Hubs/ChatHub.cs @@ -1,4 +1,6 @@ -using System.Threading.Tasks; +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; using Blog.Core.Common; using Blog.Core.Common.LogHelper; using Microsoft.AspNetCore.SignalR; @@ -53,10 +55,23 @@ public async Task SendPrivateMessage(string user, string message) /// 当连接建立时运行 /// /// - public override Task OnConnectedAsync() + public override async Task OnConnectedAsync() { - //TODO.. - return base.OnConnectedAsync(); + await base.OnConnectedAsync(); + if (Context.User?.Identity?.IsAuthenticated == true) + { + //按用户分组 + //是有必要的 例如多个浏览器、多个标签页使用同个用户登录 应当归属于一组 + await AddToGroup(Context.User.Identity.Name); + + //加入角色组 + //根据角色分组 例如管理员分组发送管理员的消息 + var roles = Context.User.Claims.Where(s => s.Type == ClaimTypes.Role).ToList(); + foreach (var role in roles) + { + await AddToGroup(role.Value); + } + } } /// @@ -81,16 +96,14 @@ public async Task SendMessage(string user, string message) public async Task GetLatestCount(string random) { //2、服务端主动向客户端发送数据,名字千万不能错 - if (AppSettings.app(new string[] { "Middleware", "SignalRSendLog", "Enabled" }).ObjToBool()) + if (AppSettings.app(new string[] {"Middleware", "SignalRSendLog", "Enabled"}).ObjToBool()) { //TODO 主动发送错误消息 - //await Clients.All.ReceiveUpdate(LogLock.GetLogData()); + await Clients.All.ReceiveUpdate(LogLock.GetLogData()); } - //3、客户端再通过 ReceiveUpdate ,来接收 - } } -} +} \ No newline at end of file diff --git a/Blog.Core.Extensions/ServiceExtensions/Authentication_JWTSetup.cs b/Blog.Core.Extensions/ServiceExtensions/Authentication_JWTSetup.cs index 930bfb6e..98fa0b82 100644 --- a/Blog.Core.Extensions/ServiceExtensions/Authentication_JWTSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/Authentication_JWTSetup.cs @@ -58,6 +58,20 @@ public static void AddAuthentication_JWTSetup(this IServiceCollection services) o.TokenValidationParameters = tokenValidationParameters; o.Events = new JwtBearerEvents { + OnMessageReceived = context => + { + var accessToken = context.Request.Query["access_token"]; + + // If the request is for our hub... + var path = context.HttpContext.Request.Path; + if (!string.IsNullOrEmpty(accessToken) && + (path.StartsWithSegments("/api2/chathub"))) + { + // Read the token out of the query string + context.Token = accessToken; + } + return Task.CompletedTask; + }, OnChallenge = context => { context.Response.Headers.Add("Token-Error", context.ErrorDescription); From b9e24a84e652f217a28b98ea1beaf899ac3b9be3 Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Tue, 11 Jul 2023 12:14:16 +0800 Subject: [PATCH 66/84] =?UTF-8?q?=E2=9C=A8=E2=9C=A8=E2=9C=A8=20=E5=8F=B2?= =?UTF-8?q?=E8=AF=97=E7=BA=A7=E6=9B=B4=E6=96=B0,=E5=AE=8C=E7=BE=8E?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=90=84=E7=A7=8D=E5=A4=8D=E6=9D=82=E5=8A=A8?= =?UTF-8?q?=E6=80=81=E6=9D=A1=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 例子看单元测试DynamicLambdaTest 支持复杂链表 动态条件拼接 从此后端可只定义一种接口,条件交给前端拼接 后端接口就不在需要定义一堆参数等 --- Blog.Core.Common/Helper/DynamicLinqFactory.cs | 367 +++++++----------- .../Common_Test/DynamicLambdaTest.cs | 108 ++++-- 2 files changed, 227 insertions(+), 248 deletions(-) diff --git a/Blog.Core.Common/Helper/DynamicLinqFactory.cs b/Blog.Core.Common/Helper/DynamicLinqFactory.cs index 248b18ff..adb57074 100644 --- a/Blog.Core.Common/Helper/DynamicLinqFactory.cs +++ b/Blog.Core.Common/Helper/DynamicLinqFactory.cs @@ -6,6 +6,7 @@ using System.Reflection; using System.Text; using System.Text.RegularExpressions; +using Mapster; namespace Blog.Core.Common.Helper { @@ -40,10 +41,9 @@ public static Expression> CreateLambda(string prope // 第一个判断条件,固定一个判断条件作为最左边 - Expression mainExpressin = ExpressionStudio(null, strArr.FirstOrDefault(x => x.LinkSymbol == LinkSymbol.Empty), parameter); - + Expression mainExpressin = ExpressionStudio(null, strArr[0], parameter); // 将需要放置在最左边的判断条件从列表中去除,因为已经合成到表达式最左边了 - strArr.Remove(strArr.FirstOrDefault(x => x.LinkSymbol == LinkSymbol.Empty)); + strArr.RemoveAt(0); foreach (var x in strArr) { @@ -57,32 +57,50 @@ public static Expression> CreateLambda(string prope /// 组合条件判断表达式 /// /// 左边的表达式 - /// + /// /// /// - public static Expression ExpressionStudio(Expression left, DynamicLinqHelper DynamicLinq, ParameterExpression key) + public static Expression ExpressionStudio(Expression left, DynamicLinqHelper dynamicLinq, ParameterExpression key) { Expression mainExpression = key; - var properties = DynamicLinq.Left.Split('.'); - - int index = 0; - foreach (var t in properties) + if (!dynamicLinq.Left.IsNullOrEmpty()) { - if (mainExpression.Type.HasImplementedRawGeneric(typeof(IEnumerable<>))) + var properties = dynamicLinq.Left.Split('.'); + + int index = 0; + foreach (var t in properties) { - return ExpressionStudioEnumerable(left, mainExpression, DynamicLinq.Clone(), properties.Skip(index).ToArray()); + if (mainExpression.Type.HasImplementedRawGeneric(typeof(IEnumerable<>))) + { + return ExpressionStudioEnumerable(left, mainExpression, dynamicLinq.Adapt(), + properties.Skip(index).ToArray()); + } + + mainExpression = mainExpression.Property(t); + index++; } + } - mainExpression = mainExpression.Property(t); - index++; + Expression right = null; + if (dynamicLinq.IsMerge && dynamicLinq.Child.Any()) + { + right = ExpressionStudio(null, dynamicLinq.Child[0], key); + for (var i = 1; i < dynamicLinq.Child.Count; i++) + { + right = ChangeLinkSymbol(dynamicLinq.Child[i].LinkSymbol, right, ExpressionStudio(null, dynamicLinq.Child[i], key)); + } + } + else + { + right = ChangeOperationSymbol(dynamicLinq.OperationSymbol, mainExpression, dynamicLinq.Right); } left = left == null // 如果左边表达式为空,则当前的表达式就为最左边 - ? ChangeOperationSymbol(DynamicLinq.OperationSymbol, mainExpression, DynamicLinq.Right) + ? right // 如果不为空,则将当前的表达式连接到左边 - : ChangeLinkSymbol(DynamicLinq.LinkSymbol, left, ChangeOperationSymbol(DynamicLinq.OperationSymbol, mainExpression, DynamicLinq.Right)); + : ChangeLinkSymbol(dynamicLinq.LinkSymbol, left, right); return left; } @@ -102,7 +120,7 @@ public static Expression ExpressionStudioEnumerable(Expression left, Expression var lambda = Expression.Lambda(mainExpression, parameter); - mainExpression = Expression.Call(typeof(Enumerable), "Any", new[] { realType }, property, lambda); + mainExpression = Expression.Call(typeof(Enumerable), "Any", new[] {realType}, property, lambda); left = left == null ? mainExpression @@ -112,155 +130,112 @@ public static Expression ExpressionStudioEnumerable(Expression left, Expression } - /// - /// 将字符串装换成动态帮助类(内含递归) - /// - public static List SpiltStrings(string propertyStr) - { - // 定义返回用List - var outList = new List(); - - // 当最后已经没有连接运算符的时候,进入该条件 - if (!propertyStr.Contains("&") & !propertyStr.Contains("|")) - { - // 当前的条件是不具备连接符号的 - var lastStr = propertyStr.Trim().Split(' '); - outList.Add(new DynamicLinqHelper - { - LinkSymbol = LinkSymbol.Empty, - Left = lastStr[0], - Right = lastStr[2], - OperationSymbol = ChangeOperationSymbol(lastStr[1]) - }); - return outList; - } - - // 判断当前 & | 哪个符号在最后一个判断逻辑内 - var key = propertyStr.LastIndexOf('&') > propertyStr.LastIndexOf('|') ? '&' : '|'; - - var nowStrArr = propertyStr.Substring(propertyStr.LastIndexOf(key)).Trim().Split(' '); - - outList.Add(new DynamicLinqHelper - { - LinkSymbol = ChangeLinkSymbol(nowStrArr[0]), - Left = nowStrArr[1], - OperationSymbol = ChangeOperationSymbol(nowStrArr[2]), - Right = nowStrArr[3] - }); - // 将剩余部分继续切割 - propertyStr = propertyStr.Substring(0, propertyStr.LastIndexOf(key)).Trim(); - // 递归 由后彺前 - outList.AddRange(SpiltStrings(propertyStr)); - - return outList; - } - public static List SplitOperationSymbol(string str) { var outList = new List(); var tokens = Regex.Matches(FormatString(str), _pattern, RegexOptions.Compiled) - .Cast() .Select(m => m.Groups[1].Value.Trim()) .ToList(); + SplitOperationSymbol(tokens, outList); + return outList; + } - int lastIndex = tokens.Count - 1; - int lastOperatingSymbolIndex = -1; - for (int i = tokens.Count - 1; i >= 0; i--) + private static void SplitOperationSymbol(List tokens, List outList, int start = 0, int end = 0) + { + var dys = new Stack(); + var dynamicLinqHelper = new DynamicLinqHelper(); + if (end == 0) { - var token = tokens[i].ToLower(); + end = tokens.Count - 1; + } - if (OperatingSystems.ContainsKey(token)) - { - //比较运算符 - lastOperatingSymbolIndex = i; - } - else if (LinkSymbols.ContainsKey(token)) + for (int i = start; i <= end; i++) + { + var token = tokens[i]; + + if (LinkSymbols.TryGetValue(token, out var symbol)) { - var left = ""; - for (int j = i + 1; j < lastOperatingSymbolIndex; j++) + if (dys.Count > 0) { - left += tokens[j]; + var linqHelper = dys.Peek(); + linqHelper.Child.Add(dynamicLinqHelper); } - - var right = ""; - for (int j = lastOperatingSymbolIndex + 1; j <= lastIndex; j++) + else { - right += tokens[j]; + outList.Add(dynamicLinqHelper); } - outList.Add(GetDynamicLinqHelper(LinkSymbols[token], - OperatingSystems[tokens[lastOperatingSymbolIndex]], - left, - right)); - lastIndex = i - 1; - lastOperatingSymbolIndex = -1; + dynamicLinqHelper = new DynamicLinqHelper() + { + LinkSymbol = symbol, + }; + continue; } - else if (i == 0 && lastOperatingSymbolIndex != -1) + + if (OperatingSystems.TryGetValue(token.ToLower(), out var system)) { - var left = ""; - for (int j = i; j < lastOperatingSymbolIndex; j++) - { - left += tokens[j]; - } + dynamicLinqHelper!.OperationSymbol = system; + continue; + } + - var right = ""; - for (int j = lastOperatingSymbolIndex + 1; j <= lastIndex; j++) + if (dynamicLinqHelper!.OperationSymbol != OperationSymbol.In) + { + if (string.Equals(token.Trim(), "(")) { - right += tokens[j]; + dynamicLinqHelper!.IsMerge = true; + dynamicLinqHelper.Child = new List(); + dys.Push(dynamicLinqHelper); + dynamicLinqHelper = new DynamicLinqHelper(); + continue; } + if (string.Equals(token.Trim(), ")")) + { + if (dys.Count > 1) + { + var dya = dys.Pop(); + dya.Child.Add(dynamicLinqHelper); - outList.Add(GetDynamicLinqHelper(LinkSymbol.Empty, - OperatingSystems[tokens[lastOperatingSymbolIndex]], - left, - right)); + dynamicLinqHelper = dya; + continue; + } + else + { + var dya = dys.Pop(); + dya.Child.Add(dynamicLinqHelper); + outList.Add(dya); + dynamicLinqHelper = null; + continue; + } + } } - } - outList.Reverse(); - return outList; - } - public static DynamicLinqHelper GetDynamicLinqHelper(LinkSymbol linkSymbol, OperationSymbol operationSymbol, string left, string right) - { - var dynamic = new DynamicLinqHelper - { - LinkSymbol = linkSymbol, - OperationSymbol = operationSymbol, - Left = left, - Right = right - }; + if (dynamicLinqHelper!.OperationSymbol is null) + { + dynamicLinqHelper.Left += token; + } + else + { + dynamicLinqHelper.Right += FormatValue(token); + } - if (dynamic.Right.StartsWith("\"") && dynamic.Right.EndsWith("\"")) - { - dynamic.Right = dynamic.Right.Remove(0, 1) - .Remove(dynamic.Right.Length - 2, 1) - .Replace(@"\""", @""""); + if (i == end) + { + outList.Add(dynamicLinqHelper); + dynamicLinqHelper = null; + } } - - return dynamic; } - - /// - /// 将字符串符号转成运算枚举符号 - /// - public static LinkSymbol ChangeLinkSymbol(string str) + public static string FormatValue(string str) { - // 这里判断链接符号 - // 当链接符号为Empty,则说明当前对象为表达式的最左边 - // 如果一个表达式出现两次链接符号为空,则说明输入的字符串格式有问题 - switch (str) - { - case "|": - return LinkSymbol.OrElse; - case "&": - return LinkSymbol.AndAlso; - default: - return LinkSymbol.Empty; - } + return str.TrimStart('"').TrimEnd('"'); + // return str.TrimStart('"').TrimEnd('"').Replace(@"\""", @""""); } + /// /// 将运算枚举符号转成具体使用方法 /// @@ -288,7 +263,7 @@ public static Dictionary GetOperationSymbol() { foreach (var name in attr.Name.Split(';')) { - _operatingSystems.Add(name.ToLower(), (OperationSymbol)item.GetValue(null)); + _operatingSystems.Add(name.ToLower(), (OperationSymbol) item.GetValue(null)); } } } @@ -307,7 +282,7 @@ public static Dictionary GetLinkSymbol() { foreach (var name in attr.Name.Split(';')) { - _linkSymbols.Add(name, (LinkSymbol)item.GetValue(null)); + _linkSymbols.Add(name, (LinkSymbol) item.GetValue(null)); } } } @@ -318,22 +293,18 @@ public static Dictionary GetLinkSymbol() public static string FormatString(string str) { - StringBuilder sb = new StringBuilder(); - int firstIndex = -1; - int lastIndex = -1; - for (int i = 0; i < str.Length; i++) + var sb = new StringBuilder(); + var firstIndex = -1; + var lastIndex = -1; + for (var i = 0; i < str.Length; i++) { var character = str[i]; if (firstIndex == -1) { if (character.IsNullOrEmpty() && i < str.Length - 2) - { if ('"'.Equals(str[i + 1])) - { firstIndex = i + 1; - } - } } else { @@ -341,7 +312,9 @@ public static string FormatString(string str) { var andIndex = str.IndexOf("\" &", firstIndex); var orIndex = str.IndexOf("\" |", firstIndex); - var andOrIndex = andIndex > 0 ? andIndex : orIndex; + var andOrIndex = Math.Min(andIndex, orIndex); + andOrIndex = andOrIndex == -1 ? Math.Max(andOrIndex, orIndex) : andOrIndex; + if (andOrIndex != -1) { lastIndex = andOrIndex; @@ -349,10 +322,7 @@ public static string FormatString(string str) else { if (i == firstIndex) continue; - if (i == str.Length - 1 || str[i + 1].IsNullOrEmpty()) - { - lastIndex = i; - } + if (i == str.Length - 1 || str[i + 1].IsNullOrEmpty()) lastIndex = i; } } @@ -368,10 +338,7 @@ public static string FormatString(string str) } } - if (firstIndex != -1) - { - continue; - } + if (firstIndex != -1) continue; sb.Append(character); } @@ -388,8 +355,8 @@ public static string FormatString(string str) "||", "&&", "==", "!=", "<=", ">=", "in", "like", "contains", "%=", - "startslike", "startscontains", "%>", - "endlike", "endcontains", "%<", + "startslike", "StartsLike", "startscontains", "StartsContains", "%>", + "endlike", "EndLike", "endcontains", "EndContains", "%<", }.Select(Regex.Escape)), @"""(?:\\.|[^""])*""", // string @"\d+(?:\.\d+)?", // number with optional decimal part @@ -397,47 +364,11 @@ public static string FormatString(string str) @"\S", // other 1-char tokens (or eat up one character in case of an error) }) + @")\s*"; - /// - /// 将字符串符号转成运算枚举符号 - /// - public static OperationSymbol ChangeOperationSymbol(string str) - { - switch (str.ToLower()) - { - case "<": - return OperationSymbol.LessThan; - case "<=": - return OperationSymbol.LessThanOrEqual; - case ">": - return OperationSymbol.GreaterThan; - case ">=": - return OperationSymbol.GreaterThanOrEqual; - case "==": - case "=": - return OperationSymbol.Equal; - case "!=": - return OperationSymbol.NotEqual; - case "contains": - case "like": - case "%=": - return OperationSymbol.Contains; - case "startslike": - case "startscontains": - case "%>": - return OperationSymbol.StartsContains; - case "endlike": - case "endcontains": - case "%<": - return OperationSymbol.EndContains; - } - - throw new Exception("OperationSymbol IS NULL"); - } /// /// 将运算枚举符号转成具体使用方法 /// - public static Expression ChangeOperationSymbol(OperationSymbol symbol, Expression key, object right) + public static Expression ChangeOperationSymbol(OperationSymbol? symbol, Expression key, object right) { // 将右边数据类型强行转换成左边一样的类型 // 两者如果Type不匹配则无法接下去的运算操作,抛出异常 @@ -468,30 +399,26 @@ public static Expression ChangeOperationSymbol(OperationSymbol symbol, Expressio { if (key.Type == typeof(string)) return key.Contains(Expression.Constant(newTypeRight)); //对string 特殊处理 由于string - else - return key.GreaterThan(Expression.Constant((newTypeRight))); + return key.GreaterThan(Expression.Constant((newTypeRight))); } case OperationSymbol.GreaterThanOrEqual: { if (key.Type == typeof(string)) return key.Contains(Expression.Constant(newTypeRight, typeof(string))); - else - return key.GreaterThanOrEqual(Expression.Constant(newTypeRight)); + return key.GreaterThanOrEqual(Expression.Constant(newTypeRight)); } case OperationSymbol.LessThan: { if (key.Type == typeof(string)) return key.Contains(Expression.Constant(newTypeRight, typeof(string))); - else - return key.LessThan(Expression.Constant((newTypeRight))); + return key.LessThan(Expression.Constant((newTypeRight))); } case OperationSymbol.LessThanOrEqual: { if (key.Type == typeof(string)) return key.Contains(Expression.Constant(newTypeRight, typeof(string))); - else - return key.LessThanOrEqual(Expression.Constant((newTypeRight))); + return key.LessThanOrEqual(Expression.Constant((newTypeRight))); } case OperationSymbol.NotEqual: return key.NotEqual(Expression.Constant(newTypeRight)); @@ -502,15 +429,10 @@ public static Expression ChangeOperationSymbol(OperationSymbol symbol, Expressio case OperationSymbol.EndContains: return key.EndContains(Expression.Constant(newTypeRight)); case OperationSymbol.In: - var contains = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public) - .Single(x => x.Name == "Contains" && x.GetParameters().Length == 2) - .MakeGenericMethod(key.Type); return Expression.Constant(newTypeRight).Contains(key); - - //return Expression.Call(contains, , key); + default: + throw new ArgumentException("OperationSymbol IS NULL"); } - - throw new NotImplementedException("OperationSymbol IS NULL"); } } @@ -526,21 +448,20 @@ public class DynamicLinqHelper public string Right { get; set; } [Display(Name = "运算符")] - public OperationSymbol OperationSymbol { get; set; } + public OperationSymbol? OperationSymbol { get; set; } [Display(Name = "连接符")] public LinkSymbol LinkSymbol { get; set; } - public DynamicLinqHelper Clone() - { - return new DynamicLinqHelper() - { - Left = this.Left, - Right = this.Right, - OperationSymbol = this.OperationSymbol, - LinkSymbol = this.LinkSymbol, - }; - } + /// + /// 是否是合并 用于括号 + /// + public bool IsMerge { get; set; } = false; + + /// + /// 再有括号时候使用 + /// + public List Child { get; set; } } /// @@ -638,8 +559,9 @@ public static IOrderedQueryable ISort(this IQueryable var parameter = Expression.Parameter(type, "p"); var propertyAccess = Expression.MakeMemberAccess(parameter, property); var orderByExpression = Expression.Lambda(propertyAccess, parameter); - var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExpression)); - return (IOrderedQueryable)source.Provider.CreateQuery(resultExpression); + var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] {type, property.PropertyType}, source.Expression, + Expression.Quote(orderByExpression)); + return (IOrderedQueryable) source.Provider.CreateQuery(resultExpression); } /// @@ -666,7 +588,7 @@ public static IQueryable ISkip(this IQueryable source // 调用的方法 "Skip", // 元素类别 - new Type[] { source.ElementType }, + new Type[] {source.ElementType}, // 调用的表达树 source.Expression, // 参数 @@ -684,7 +606,7 @@ public static IQueryable ITake(this IQueryable source // 调用的方法 "Take", // 元素类别 - new Type[] { source.ElementType }, + new Type[] {source.ElementType}, // 调用的表达树 source.Expression, // 参数 @@ -716,7 +638,8 @@ public static IEnumerable IDistinctBy(this IEnumerable>(); _baseRepository.Db.Aop.OnLogExecuting = (sql, p) => { - _testOutputHelper.WriteLine(""); - _testOutputHelper.WriteLine("==================FullSql=====================", "", new string[] { sql.GetType().ToString(), GetParas(p), "【SQL语句】:" + sql }); - _testOutputHelper.WriteLine("【SQL语句】:" + sql); - _testOutputHelper.WriteLine(GetParas(p)); - _testOutputHelper.WriteLine("=============================================="); - _testOutputHelper.WriteLine(""); + _testOutputHelper.WriteLine(UtilMethods.GetNativeSql(sql, p)); }; - //DbContext.Init(BaseDBConfig.ConnectionString,(DbType)BaseDBConfig.DbType); Init(); } - - private static string GetParas(SugarParameter[] pars) - { - string key = "【SQL参数】:"; - foreach (var param in pars) - { - key += $"{param.ParameterName}:{param.Value}\n"; - } - - return key; - } - + private void Init() { _baseRepository.Db.CodeFirst.InitTables(); _baseRepository.Db.CodeFirst.InitTables(); + _baseRepository.Db.CodeFirst.InitTables(); } + /// + /// 普通查询 例子
+ /// 没有复杂链表 主要使用导航属性
+ /// 推荐将条件拼接交给前端 后端只定义个接口就很方便 维护也很简单
+ ///
[Fact] public async void Get_Blogs_DynamicTest() { @@ -69,34 +59,100 @@ public async void Get_Blogs_DynamicTest() await TestConditions("btitle like \" 测试数据\""); await TestConditions("btitle like \"测试数据\" && bId>0"); await TestConditions("btitle like \"测试!@#$%^&*()_+|}{\":<>?LP\"数据\" && bId>0"); - await TestConditions("btitle like \"测试!@+)(*()_&%^&^$^%$IUYWIQOJVLXKZM>?Z<>??LP\"数据\" && bId>0"); + await TestConditions( + "btitle like \"测试!@+)(*()_&%^&^$^%$IUYWIQOJVLXKZM>?Z<>??LP\"数据\" && bId>0"); await TestConditions("IsDeleted == false"); await TestConditions("IsDeleted == true"); + await TestConditions("IsDeleted == true && ( btitle like \"张三\" || btitle like \"李四\" )"); + await TestConditions( + "IsDeleted == true && ( btitle like \"张三\" || btitle like \"李四\" || ( btitle StartsLike \"王五\" && btitle EndLike \"赵六\" ) )"); //导航属性 //一对一 //查询 老张的文章 - await TestConditions("User.RealName like \"老张\""); + await TestConditions("User.RealName like \"老张\""); //查询 2019年后的老张文章 - await TestConditions("User.RealName like \"老张\" && bUpdateTime>=\"2019-01-01 01:01:01\""); + await TestConditions("User.RealName like \"老张\" && bUpdateTime>=\"2019-01-01 01:01:01\""); //一对多 //查询 评论中有"写的不错"的文章 - await TestConditions("Comments.Comment like \"写的不错\""); + await TestConditions("Comments.Comment like \"写的不错\""); //查询 2019后的 评论中有"写的不错"的文章 - await TestConditions("Comments.Comment like \"写的不错\" && bUpdateTime>=\"2019-01-01 01:01:01\""); + await TestConditions("Comments.Comment like \"写的不错\" && bUpdateTime>=\"2019-01-01 01:01:01\""); //查询 有老张评论的文章 await TestConditions("Comments.User.LoginName like \"老张\""); } + /// + /// 复杂链表 也能使用动态条件
+ /// 存在复杂的链表 left join等 + ///
+ [Fact] + public async void Get_Blogs_DynamicJoinTest() + { + //方便前端自定义条件查询 + //语法更舒服 + var data = await _baseRepository.Query(); + _testOutputHelper.WriteLine(data.ToJson()); + + await TestJoinConditions(""); + await TestJoinConditions("bId=1"); + await TestJoinConditions("bId=2"); + await TestJoinConditions("bId in (1,2,3,4,5)"); + await TestJoinConditions("bId in (1,2,3,4,5)|| bUpdateTime>=\"2019-01-01 01:01:01\""); + await TestJoinConditions("btitle like \" 测试数据\""); + await TestJoinConditions("btitle like \"测试数据\" && bId>0"); + await TestJoinConditions("btitle like \"测试!@#$%^&*()_+|}{\":<>?LP\"数据\" && bId>0"); + await TestJoinConditions( + "btitle like \"测试!@+)(*()_&%^&^$^%$IUYWIQOJVLXKZM>?Z<>??LP\"数据\" && bId>0"); + await TestJoinConditions("IsDeleted == false"); + await TestJoinConditions("IsDeleted == true"); + await TestJoinConditions("IsDeleted == true && ( btitle like \"张三\" || btitle like \"李四\" )"); + await TestJoinConditions( + "IsDeleted == true && ( btitle like \"张三\" || btitle like \"李四\" || ( btitle StartsLike \"王五\" && btitle EndLike \"赵六\" ) )"); + + //导航属性 + + //一对一 + + //查询 老张的文章 + await TestJoinConditions("User.RealName like \"老张\""); + //查询 2019年后的老张文章 + await TestJoinConditions("User.RealName like \"老张\" && bUpdateTime>=\"2019-01-01 01:01:01\""); + + //一对多 + + //查询 评论中有"写的不错"的文章 + await TestJoinConditions("Comments.Comment like \"写的不错\""); + //查询 2019后的 评论中有"写的不错"的文章 + await TestJoinConditions("Comments.Comment like \"写的不错\" && bUpdateTime>=\"2019-01-01 01:01:01\""); + //查询 有老张评论的文章 + await TestJoinConditions("Comments.User.LoginName like \"老张\""); + } + + private async Task TestConditions(string conditions) { var express = DynamicLinqFactory.CreateLambda(conditions); + _testOutputHelper.WriteLine(new string('=', 100)); var product = await _baseRepository.Query(express); + _testOutputHelper.WriteLine($"条件:{DynamicLinqFactory.FormatString(conditions)}\r\nLambda:{express}\r\n结果:{product.Count}"); + _testOutputHelper.WriteLine(new string('=', 100)); + } + + private async Task TestJoinConditions(string conditions) + { + var express = DynamicLinqFactory.CreateLambda(conditions); + _testOutputHelper.WriteLine(new string('=', 100)); + var product = await _baseRepository.Db.Queryable() + .LeftJoin((b, u) => Convert.ToInt64(b.bsubmitter) == u.Id) + .MergeTable() + .Where(express) + .ToListAsync(); + _testOutputHelper.WriteLine($"条件:{DynamicLinqFactory.FormatString(conditions)}\r\nLambda:{express}\r\n结果:{product.Count}"); _testOutputHelper.WriteLine(new string('=', 100)); - _testOutputHelper.WriteLine($"原条件:{conditions}\r\nLambda:{express}\r\n结果:{product.Count}"); } } \ No newline at end of file From 8b4e378a4931bb17849bb21a66698afe651b5c04 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Thu, 20 Jul 2023 12:48:49 +0800 Subject: [PATCH 67/84] feat: dm database --- Blog.Core.Api/wwwroot/BlogCore.Data.json/TopicDetail.tsv | 2 +- Blog.Core.Common/Seed/DBSeed.cs | 4 ++-- Blog.Core.Model/Models/GblLogAudit.cs | 4 ++-- Blog.Core.Model/Models/TopicDetail.cs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Blog.Core.Api/wwwroot/BlogCore.Data.json/TopicDetail.tsv b/Blog.Core.Api/wwwroot/BlogCore.Data.json/TopicDetail.tsv index 2cee8073..ff5f8d68 100644 --- a/Blog.Core.Api/wwwroot/BlogCore.Data.json/TopicDetail.tsv +++ b/Blog.Core.Api/wwwroot/BlogCore.Data.json/TopicDetail.tsv @@ -3,7 +3,7 @@ "TopicId": 1, "tdLogo": null, "tdName": "第一章 罗马的诞生 第一节 传说的年代", - "tdContent": "

第一节 传说的年代<\/p>

每个民族都有自己的神话传说。大概希望知道本民族的来源是个很自然的愿望吧。但这是一个难题,因为这几乎不可能用科学的方法来解释清楚。不过所有的民族都没有这样的奢求。他们只要有一个具有一定的条理性,而又能振奋其民族精神的浪漫故事就行,别抬杠,象柏杨那样将中国的三皇五帝都来个科学分析,来评论他们的执政之优劣是大可不必的。<\/p>

对於罗马人,他们有一个和特洛伊城的陷落相关的传说。<\/p>

位於小亚细亚西岸的繁荣的城市特洛伊,在遭受了阿加美农统帅的希腊联军的十年围攻之後,仍未陷落。希腊联军於是留下一个巨大的木马後假装撤兵。特洛伊人以为那是希腊联军留给自己的礼物,就将它拉入城内。<\/p>

当庆祝胜利的狂欢结束,特洛伊人满怀对明日的和平生活的希望熟睡後,藏在木马内的希腊士兵一个又一个地爬了出来。就在这天夜里,特洛伊城便在火光和叫喊中陷落了。全城遭到大屠杀 ,幸免於死的人全都沦为奴隶。混乱之中只有特洛伊国王的驸马阿伊尼阿斯带着老父,儿子等数人在女神维娜斯的帮助下成功地逃了出来。这驸马爷乃是女神维娜斯与凡人男子之间的儿子,女神维娜斯不忍心看着自己的儿子被希腊士兵屠杀 。<\/p>

这阿驸马一行人分乘几条船,离开了火光冲天的特洛伊城。在女神维娜斯的指引下,浪迹地中海,最後在意大利西岸登陆。当地的国王看上了阿伊尼阿斯并把自己的女儿嫁给了他。他又是驸马了,与他的新妻过起了幸福的生活。难民们也安定了下来。<\/p>

阿伊尼阿斯死後,跟随他逃难来的儿子继承了王位。新王在位三十年後,离开了这块地方,到台伯河(Tiber)下游建了一个新城亚尔巴龙迦城。这便是罗马城的前身了。<\/p>

罗马人自古相信罗马城是公元前731年4月21日由罗莫路和勒莫(Romulus and Remus)建设的。而这两个孪生兄弟是从特洛伊逃出的阿伊尼阿斯的子孙。後来,罗马人接触了希腊文化後才知道特洛伊的陷落是在公元前十三世纪,老早的事了。罗马人好象并没有对这段空白有任何烦恼,随手编出一串传说,把那空白给填补了。反正传说这事荒唐一点的更受欢迎。经过了一堆搞不清谁是谁的王的统治,出现了一个什麽王的公主。<\/p>

公主的叔父在篡夺了王位後,为了防止公主结婚生子威胁自己的王位,便任命未婚的公主为巫女。这是主管祭神的职位,象修女一样不得结婚。<\/p>

不巧一日这美丽的公主在祭事的空余,来到小河边午睡。也是合当有事,被过往的战神玛尔斯(Mars)一见钟情。这玛尔斯本是靠挑起战争混饭吃的,但也常勾引 良家妇女。这天战神也没错过机会,立刻由天而降,与公主一试云雨。据说战神的技术特神,公主还没来得及醒便完事升天去了。後来公主生了一双胞胎,起名罗莫路和勒莫。<\/p>

叔父闻知此事大怒,将公主投入大牢,又把那双胞胎放在篮子里抛入台伯河,指望那篮子漂入大海将那双胞胎淹死。类似的故事在旧约圣经里也有,那是关於摩西的事,好象这类传说在当地十分流行。<\/p>

再说那兄弟俩的篮子被河口附近茂密的灌木丛钩住而停了下来,俩人哭声引来的一只过路的母狼。意大利的狼都带点慈悲心,不但没吃了俩人当点心,还用自己的奶去喂他们,这才救了俩小命。<\/p>

不过,总是由狼养活也没法交 待,於是又一日一放羊的在这地盘上溜哒,发现了兄弟俩,将他们抱了回去扶养成人 。据说现在这一带仍有许多放羊的。<\/p>

兄弟俩长大後成了这一带放羊人的头,在与别的放羊人的圈子的打斗中不断地扩展自己的势力范围。圈子大了,情报也就多了,终于有一天,罗莫路和勒莫知道了自己身事。<\/p>

兄弟俩就带着手下的放羊人呼啸着去打破了亚尔巴龙迦城,杀了那国王,将王位又交 还给了自己祖父。他们的母亲似乎已经死在了大牢里。但兄弟俩也没在亚尔巴龙迦城多住,他们认为亚尔巴龙迦城位於山地,虽然易守难攻,却不利发展。加上兄弟俩是在台伯河的下游长大的,所以便回到原地,建了个新城。除了手下的放羊人又加上了附近的放羊人和农民。<\/p>

消灭了共同的敌人後,兄弟俩的关系开始恶化。有人说是为了新城的命名,有人说是为了新城的城址,也有人说是为了争夺王位。兄弟俩於是分割统治,各占一小山包。但纷争又开始了,勒莫跳过了罗莫路为表示势力范围而挖的沟。对於这种侵犯他人权力的行为,罗莫路大义灭亲地在自己兄弟的後脑上重重地来了一锄头,勒莫便被灭了。<\/p>

<\/p>

於是这城便以罗莫路的名字命名为罗马,这就是公元前731年4月21日的事了,到现在这天仍是意大利的节日,罗马人会欢天喜地的庆祝罗莫路杀了自己的…不,是庆祝罗马建城。王位当然也得由罗莫路来坐,一切问题都没了。这时四年一度的奥林匹克运动会在希腊已经开了六回,罗马也从传说的时代走出,近入了历史时代。<\/p>


<\/p>", + "tdContent": "

第一节 传说的年代<\/p>

每个民族都有自己的神话传说。大概希望知道本民族的来源是个很自然的愿望吧。但这是一个难题,因为这几乎不可能用科学的方法来解释清楚。", "tdDetail": "标题", "tdSectendDetail": null, "tdIsDelete": 0, diff --git a/Blog.Core.Common/Seed/DBSeed.cs b/Blog.Core.Common/Seed/DBSeed.cs index e7eb4e27..6fa8f901 100644 --- a/Blog.Core.Common/Seed/DBSeed.cs +++ b/Blog.Core.Common/Seed/DBSeed.cs @@ -81,7 +81,7 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) // 创建数据库 Console.WriteLine($"Create Database(The Db Id:{MyContext.ConnId})..."); - if (MyContext.DbType != SqlSugar.DbType.Oracle) + if (MyContext.DbType != SqlSugar.DbType.Oracle && MyContext.DbType != SqlSugar.DbType.Dm) { myContext.Db.DbMaintenance.CreateDatabase(); ConsoleHelper.WriteSuccessLine($"Database created successfully!"); @@ -89,7 +89,7 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) else { //Oracle 数据库不支持该操作 - ConsoleHelper.WriteSuccessLine($"Oracle 数据库不支持该操作,可手动创建Oracle数据库!"); + ConsoleHelper.WriteSuccessLine($"Oracle 数据库不支持该操作,可手动创建Oracle/Dm数据库!"); } // 创建数据库表,遍历指定命名空间下的class, diff --git a/Blog.Core.Model/Models/GblLogAudit.cs b/Blog.Core.Model/Models/GblLogAudit.cs index 2cecce8b..d4a85411 100644 --- a/Blog.Core.Model/Models/GblLogAudit.cs +++ b/Blog.Core.Model/Models/GblLogAudit.cs @@ -57,13 +57,13 @@ public class GblLogAudit ///

///错误信息 /// - [SugarColumn(ColumnDescription = "错误信息", IsNullable = false, IsPrimaryKey = false, IsIdentity = false, Length = 5000)] + [SugarColumn(ColumnDescription = "错误信息", IsNullable = false, IsPrimaryKey = false, IsIdentity = false, Length = 2000)] public string Message { get; set; } /// ///异常 /// - [SugarColumn(ColumnDescription = "异常", IsNullable = true, IsPrimaryKey = false, IsIdentity = false, Length = 5000)] + [SugarColumn(ColumnDescription = "异常", IsNullable = true, IsPrimaryKey = false, IsIdentity = false, Length = 2000)] public string Exception { get; set; } } diff --git a/Blog.Core.Model/Models/TopicDetail.cs b/Blog.Core.Model/Models/TopicDetail.cs index 1a98f3af..6cb69c67 100644 --- a/Blog.Core.Model/Models/TopicDetail.cs +++ b/Blog.Core.Model/Models/TopicDetail.cs @@ -19,7 +19,7 @@ public TopicDetail() [SugarColumn(Length = 200, IsNullable = true)] public string tdName { get; set; } - [SugarColumn(Length = 6000, IsNullable = true)] + [SugarColumn(Length = 2000, IsNullable = true)] public string tdContent { get; set; } [SugarColumn(Length = 2000, IsNullable = true)] From e360a4bbe747942c236daa1e50e9b98c93daac0f Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Thu, 20 Jul 2023 17:13:08 +0800 Subject: [PATCH 68/84] =?UTF-8?q?feat=EF=BC=9Achange=20access=20trend=20lo?= =?UTF-8?q?g?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Api/Blog.Core.Api.csproj | 2 -- Blog.Core.Api/Blog.Core.Model.xml | 2 +- Blog.Core.Model/Blog.Core.Model.csproj | 2 +- Blog.Core.Model/Models/AccessTrendLog.cs | 2 +- .../QuartzNet/Jobs/Job_AccessTrendLog_Quartz.cs | 8 ++++---- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Blog.Core.Api/Blog.Core.Api.csproj b/Blog.Core.Api/Blog.Core.Api.csproj index f3f9befa..dc7680ca 100644 --- a/Blog.Core.Api/Blog.Core.Api.csproj +++ b/Blog.Core.Api/Blog.Core.Api.csproj @@ -61,8 +61,6 @@ - - diff --git a/Blog.Core.Api/Blog.Core.Model.xml b/Blog.Core.Api/Blog.Core.Model.xml index 1eb3423b..8de49a7b 100644 --- a/Blog.Core.Api/Blog.Core.Model.xml +++ b/Blog.Core.Api/Blog.Core.Model.xml @@ -200,7 +200,7 @@ 用户访问趋势日志
- + 用户 diff --git a/Blog.Core.Model/Blog.Core.Model.csproj b/Blog.Core.Model/Blog.Core.Model.csproj index ac851de2..f1e8764d 100644 --- a/Blog.Core.Model/Blog.Core.Model.csproj +++ b/Blog.Core.Model/Blog.Core.Model.csproj @@ -16,7 +16,7 @@ - + diff --git a/Blog.Core.Model/Models/AccessTrendLog.cs b/Blog.Core.Model/Models/AccessTrendLog.cs index fd6dbae7..bc4848cf 100644 --- a/Blog.Core.Model/Models/AccessTrendLog.cs +++ b/Blog.Core.Model/Models/AccessTrendLog.cs @@ -12,7 +12,7 @@ public class AccessTrendLog : RootEntityTkey /// 用户 ///
[SugarColumn(Length = 128, IsNullable = true)] - public string User { get; set; } + public string UserInfo { get; set; } /// /// 次数 diff --git a/Blog.Core.Tasks/QuartzNet/Jobs/Job_AccessTrendLog_Quartz.cs b/Blog.Core.Tasks/QuartzNet/Jobs/Job_AccessTrendLog_Quartz.cs index 1d501c34..68a9aa53 100644 --- a/Blog.Core.Tasks/QuartzNet/Jobs/Job_AccessTrendLog_Quartz.cs +++ b/Blog.Core.Tasks/QuartzNet/Jobs/Job_AccessTrendLog_Quartz.cs @@ -60,7 +60,7 @@ public async Task Run(IJobExecutionContext context) foreach (var item in activeUsers) { - var user = (await _accessTrendLogServices.Query(d => d.User != "" && d.User == item.user)).FirstOrDefault(); + var user = (await _accessTrendLogServices.Query(d => d.UserInfo != "" && d.UserInfo == item.user)).FirstOrDefault(); if (user != null) { user.Count += item.count; @@ -73,13 +73,13 @@ await _accessTrendLogServices.Add(new AccessTrendLog() { Count = item.count, UpdateTime = logUpdate, - User = item.user + UserInfo = item.user }); } } // 重新拉取 - var actUsers = await _accessTrendLogServices.Query(d => d.User != "", d => d.Count, false); + var actUsers = await _accessTrendLogServices.Query(d => d.UserInfo != "", d => d.Count, false); actUsers = actUsers.Take(15).ToList(); List activeUserVMs = new(); @@ -87,7 +87,7 @@ await _accessTrendLogServices.Add(new AccessTrendLog() { activeUserVMs.Add(new ActiveUserVM() { - user = item.User, + user = item.UserInfo, count = item.Count }); } From 3396b6b34d09ab097fd56d4aaee34cf4a1cbc363 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Fri, 21 Jul 2023 10:53:32 +0800 Subject: [PATCH 69/84] Update appsettings.json --- Blog.Core.Api/appsettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Blog.Core.Api/appsettings.json b/Blog.Core.Api/appsettings.json index 5fdf0000..52229055 100644 --- a/Blog.Core.Api/appsettings.json +++ b/Blog.Core.Api/appsettings.json @@ -152,7 +152,7 @@ "DBType": 5, "Enabled": false, "HitRate": 10, - "Connection": "PORT=5236;DATABASE=DAMENG;HOST=localhost;PASSWORD=SYSDBA;USER ID=SYSDBA;" + "Connection": "Server=49.232.247.202:5236;User Id=TESTDBA;PWD=TESTDBA123654;SCHEMA=TESTDBA;" }, { "ConnId": "WMBLOG_KDBNDP", From b5ea86faa3717b91e02dc4b365f3f69077119529 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Fri, 21 Jul 2023 11:13:44 +0800 Subject: [PATCH 70/84] Update appsettings.json --- Blog.Core.Api/appsettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Blog.Core.Api/appsettings.json b/Blog.Core.Api/appsettings.json index 52229055..d94a7606 100644 --- a/Blog.Core.Api/appsettings.json +++ b/Blog.Core.Api/appsettings.json @@ -152,7 +152,7 @@ "DBType": 5, "Enabled": false, "HitRate": 10, - "Connection": "Server=49.232.247.202:5236;User Id=TESTDBA;PWD=TESTDBA123654;SCHEMA=TESTDBA;" + "Connection": "Server=xxxxx:5236;User Id=xxxxx;PWD=xxxxx;SCHEMA=TESTDBA;" }, { "ConnId": "WMBLOG_KDBNDP", From 7cf94998c294291e676dbbb85f25726fd43b0341 Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Fri, 21 Jul 2023 18:03:09 +0800 Subject: [PATCH 71/84] test --- .../Systems/DynamicCodeFirstController.cs | 35 +++++++++++++ .../DB/Extension/DynamicBuildException.cs | 50 +++++++++++++++++++ Blog.Core.Model/Models/RootTkey/BaseEntity.cs | 2 + 3 files changed, 87 insertions(+) create mode 100644 Blog.Core.Api/Controllers/Systems/DynamicCodeFirstController.cs create mode 100644 Blog.Core.Common/DB/Extension/DynamicBuildException.cs diff --git a/Blog.Core.Api/Controllers/Systems/DynamicCodeFirstController.cs b/Blog.Core.Api/Controllers/Systems/DynamicCodeFirstController.cs new file mode 100644 index 00000000..e2d56377 --- /dev/null +++ b/Blog.Core.Api/Controllers/Systems/DynamicCodeFirstController.cs @@ -0,0 +1,35 @@ +using Blog.Core.Controllers; +using Blog.Core.Model; +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 DynamicCodeFirstController : BaseApiController +{ + private readonly ISqlSugarClient _db; + + public DynamicCodeFirstController(ISqlSugarClient db) + { + _db = db; + } + + + /// + /// 测试建表 + /// + /// + [HttpPost] + public MessageModel TestCreateTable() + { + _db.DynamicBuilder(); + return Success(); + } +} \ No newline at end of file diff --git a/Blog.Core.Common/DB/Extension/DynamicBuildException.cs b/Blog.Core.Common/DB/Extension/DynamicBuildException.cs new file mode 100644 index 00000000..6f3a1bad --- /dev/null +++ b/Blog.Core.Common/DB/Extension/DynamicBuildException.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using Blog.Core.Common.Extensions; +using SqlSugar; + +namespace Blog.Core.Common.DB.Extension; + +public static class DynamicBuildException +{ + private static List GetEntityAttr(this DynamicBuilder builder) + { + FieldInfo fieldInfo = builder.GetType().GetField("entityAttr", BindingFlags.Instance | BindingFlags.NonPublic); + List entityAttr = (List) fieldInfo.GetValue(builder); + return entityAttr; + } + + private static CustomAttributeBuilder CreateIndex(SugarIndexAttribute indexAttribute) + { + Type type = typeof(SugarIndexAttribute); + return new CustomAttributeBuilder(type.GetConstructor(new[] + { + typeof(string), typeof(string), typeof(OrderByType), typeof(bool) + })!, + new object[] + { + indexAttribute.IndexName, indexAttribute.IndexFields.First().Key, indexAttribute.IndexFields.First().Value, indexAttribute.IsUnique + }, + new PropertyInfo[] + { + type.GetProperty("IndexName"), + type.GetProperty("IndexFields"), + type.GetProperty("IsUnique"), + }, + new object[] + { + indexAttribute.IndexName, indexAttribute.IndexFields, indexAttribute.IsUnique + }); + } + + public static DynamicProperyBuilder CreateIndex(this DynamicProperyBuilder builder, SugarIndexAttribute indexAttribute) + { + var classBuilder = builder.baseBuilder; + var entityAttr = classBuilder.GetEntityAttr(); + entityAttr.Add(CreateIndex(indexAttribute)); + return builder; + } +} \ No newline at end of file diff --git a/Blog.Core.Model/Models/RootTkey/BaseEntity.cs b/Blog.Core.Model/Models/RootTkey/BaseEntity.cs index b6dabe54..5d5d4414 100644 --- a/Blog.Core.Model/Models/RootTkey/BaseEntity.cs +++ b/Blog.Core.Model/Models/RootTkey/BaseEntity.cs @@ -4,6 +4,8 @@ namespace Blog.Core.Model.Models.RootTkey; +[SugarIndex("index_{table}_Enabled", nameof(Enabled), OrderByType.Asc)] +[SugarIndex("index_{table}_IsDeleted", nameof(IsDeleted), OrderByType.Asc)] public class BaseEntity : RootEntityTkey, IDeleteFilter { #region 数据状态管理 From 667cc8fafcfe350709235cb17401c537452a7b19 Mon Sep 17 00:00:00 2001 From: LemonNoCry Date: Fri, 21 Jul 2023 20:18:51 +0800 Subject: [PATCH 72/84] test --- Blog.Core.Api/Blog.Core.xml | 23 +++++++ .../Systems/DynamicCodeFirstController.cs | 63 ++++++++++++++++++- Blog.Core.Api/appsettings.json | 30 ++++----- .../DB/Extension/DynamicBuildException.cs | 34 +++++----- 4 files changed, 117 insertions(+), 33 deletions(-) diff --git a/Blog.Core.Api/Blog.Core.xml b/Blog.Core.Api/Blog.Core.xml index 6dd7f16b..566d345a 100644 --- a/Blog.Core.Api/Blog.Core.xml +++ b/Blog.Core.Api/Blog.Core.xml @@ -1391,6 +1391,29 @@
+ + + 缓存管理 + + + + + 测试建表 + + + + + + 测试查询 + + + + + + 测试写入 + + + 多租户-多库方案 测试 diff --git a/Blog.Core.Api/Controllers/Systems/DynamicCodeFirstController.cs b/Blog.Core.Api/Controllers/Systems/DynamicCodeFirstController.cs index e2d56377..29260f03 100644 --- a/Blog.Core.Api/Controllers/Systems/DynamicCodeFirstController.cs +++ b/Blog.Core.Api/Controllers/Systems/DynamicCodeFirstController.cs @@ -1,7 +1,11 @@ +using Blog.Core.Common.DB.Extension; using Blog.Core.Controllers; using Blog.Core.Model; +using Blog.Core.Model.Models.RootTkey; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using NetTaste; +using OfficeOpenXml.FormulaParsing.Excel.Functions.Math; using SqlSugar; namespace Blog.Core.Api.Controllers.Systems; @@ -21,6 +25,36 @@ public DynamicCodeFirstController(ISqlSugarClient db) _db = db; } + /// + /// 动态type + /// + /// + private Type GetDynamicType() + { + return _db.DynamicBuilder().CreateClass("DynamicTestTable") + //{table} 占位符会自动替换成表名 + .CreateIndex(new SugarIndexAttribute("idx_{table}_Code", "Code", OrderByType.Desc)) + .CreateProperty("Id", typeof(int), new SugarColumn() {IsPrimaryKey = true, IsIdentity = true}) + .CreateProperty("Code", typeof(string), new SugarColumn() {Length = 50}) + .CreateProperty("Name", typeof(string), new SugarColumn() {Length = 50}) + .WithCache() + .BuilderType(); + } + + /// + /// 动态type 继承BaseEntity + /// + /// + private Type GetDynamicType2() + { + return _db.DynamicBuilder().CreateClass("DynamicTestTable2", null, typeof(BaseEntity)) + //{table} 占位符会自动替换成表名 + .CreateIndex(new SugarIndexAttribute("idx_{table}_Code", "Code", OrderByType.Desc)) + .CreateProperty("Code", typeof(string), new SugarColumn() {Length = 50}) + .CreateProperty("Name", typeof(string), new SugarColumn() {Length = 50}) + .WithCache() + .BuilderType(); + } /// /// 测试建表 @@ -29,7 +63,34 @@ public DynamicCodeFirstController(ISqlSugarClient db) [HttpPost] public MessageModel TestCreateTable() { - _db.DynamicBuilder(); + var type = GetDynamicType(); + _db.CodeFirst.InitTables(type); + return Success(); + } + + /// + /// 测试查询 + /// + /// + [HttpGet] + public MessageModel TestQuery() + { + var type = GetDynamicType(); + return Success(_db.QueryableByObject(type).ToList()); + } + + /// + /// 测试写入 + /// + /// + [HttpPost] + public MessageModel TestInsert(string code, string name) + { + var type = GetDynamicType(); + var entity = Activator.CreateInstance(type); + type.GetProperty("Code")!.SetValue(entity, code); + type.GetProperty("Name")!.SetValue(entity, name); + _db.InsertableByObject(entity).ExecuteCommand(); return Success(); } } \ No newline at end of file diff --git a/Blog.Core.Api/appsettings.json b/Blog.Core.Api/appsettings.json index d94a7606..3b3b4041 100644 --- a/Blog.Core.Api/appsettings.json +++ b/Blog.Core.Api/appsettings.json @@ -76,13 +76,12 @@ "SvcName": "", // /svc/blog "UseLoadTest": false }, - // 请配置MainDB为你想要的主库的ConnId值,并设置对应的Enabled为true; // *** 单库操作,把 MutiDBEnabled 设为false ***; // *** 多库操作,把 MutiDBEnabled 设为true,其他的从库Enabled也为true **; // 具体配置看视频:https://www.bilibili.com/video/BV1BJ411B7mn?p=6 //Log:日志库; - "MainDB": "WMBLOG_SQLITE", //当前项目的主库,所对应的连接字符串的Enabled必须为true + "MainDB": "WMBLOG_MSSQL_1", //当前项目的主库,所对应的连接字符串的Enabled必须为true "MutiDBEnabled": true, //是否开启多库模式 "CQRSEnabled": false, //是否开启读写分离模式,必须是单库模式,且数据库类型一致,比如都是SqlServer "DBS": [ @@ -99,23 +98,24 @@ { "ConnId": "WMBLOG_SQLITE", "DBType": 2, - "Enabled": true, + "Enabled": false, "HitRate": 50, // 值越大,优先级越高 "Connection": "WMBlog.db" //sqlite只写数据库名就行 }, { "ConnId": "Log", //日志库连接固定名称,不要改,其他的可以改 - "DBType": 2, + "DBType": 1, "Enabled": true, "HitRate": 50, // 值越大,优先级越高 - "Connection": "WMBlogLog.db" //sqlite只写数据库名就行 + "Connection": "Server=localhost;Database=BlogCoreLog;Trusted_Connection=True;", + "ProviderName": "System.Data.SqlClient" }, { "ConnId": "WMBLOG_MSSQL_1", "DBType": 1, - "Enabled": false, + "Enabled": true, "HitRate": 40, - "Connection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=WMBLOG_MSSQL_1;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", + "Connection": "Server=localhost;Database=BlogCore;Trusted_Connection=True;", "ProviderName": "System.Data.SqlClient" }, { @@ -257,15 +257,16 @@ "RealIpHeader": "X-Real-IP", "ClientIdHeader": "X-ClientId", "IpWhitelist": [], //白名单 - "EndpointWhitelist": [ "get:/api/xxx", "*:/api/yyy" ], - "ClientWhitelist": [ "dev-client-1", "dev-client-2" ], + "EndpointWhitelist": ["get:/api/xxx", "*:/api/yyy"], + "ClientWhitelist": ["dev-client-1", "dev-client-2"], "QuotaExceededResponse": { "Content": "{{\"status\":429,\"msg\":\"访问过于频繁,请稍后重试\",\"success\":false}}", "ContentType": "application/json", "StatusCode": 429 }, "HttpStatusCode": 429, //返回状态码 - "GeneralRules": [ //api规则,结尾一定要带* + "GeneralRules": [ + //api规则,结尾一定要带* { "Endpoint": "*:/api/blog*", "Period": "1m", @@ -287,7 +288,6 @@ "Limit": 500 } ] - }, "ConsulSetting": { "ServiceName": "BlogCoreService", @@ -296,7 +296,8 @@ "ServiceHealthCheck": "/healthcheck", "ConsulAddress": "http://localhost:8500" }, - "PayInfo": { //建行聚合支付信息 + "PayInfo": { + //建行聚合支付信息 "MERCHANTID": "", //商户号 "POSID": "", //柜台号 "BRANCHID": "", //分行号 @@ -306,7 +307,7 @@ "OutAddress": "http://127.0.0.1:12345" //外联地址 }, "nacos": { - "ServerAddresses": [ "http://localhost:8848" ], // nacos 连接地址 + "ServerAddresses": ["http://localhost:8848"], // nacos 连接地址 "DefaultTimeOut": 15000, // 默认超时时间 "Namespace": "public", // 命名空间 "ListenInterval": 10000, // 监听的频率 @@ -317,7 +318,8 @@ "LogFiedOutPutConfigs": { "tcpAddressHost": "", // 输出elk的tcp连接地址 "tcpAddressPort": 0, // 输出elk的tcp端口号 - "ConfigsInfo": [ // 配置的输出elk节点内容 常用语动态标识 + "ConfigsInfo": [ + // 配置的输出elk节点内容 常用语动态标识 { "FiedName": "applicationName", "FiedValue": "Blog.Core.Api" diff --git a/Blog.Core.Common/DB/Extension/DynamicBuildException.cs b/Blog.Core.Common/DB/Extension/DynamicBuildException.cs index 6f3a1bad..15e638a4 100644 --- a/Blog.Core.Common/DB/Extension/DynamicBuildException.cs +++ b/Blog.Core.Common/DB/Extension/DynamicBuildException.cs @@ -20,24 +20,22 @@ private static List GetEntityAttr(this DynamicBuilder bu private static CustomAttributeBuilder CreateIndex(SugarIndexAttribute indexAttribute) { Type type = typeof(SugarIndexAttribute); - return new CustomAttributeBuilder(type.GetConstructor(new[] - { - typeof(string), typeof(string), typeof(OrderByType), typeof(bool) - })!, - new object[] - { - indexAttribute.IndexName, indexAttribute.IndexFields.First().Key, indexAttribute.IndexFields.First().Value, indexAttribute.IsUnique - }, - new PropertyInfo[] - { - type.GetProperty("IndexName"), - type.GetProperty("IndexFields"), - type.GetProperty("IsUnique"), - }, - new object[] - { - indexAttribute.IndexName, indexAttribute.IndexFields, indexAttribute.IsUnique - }); + var constructorTypes = new List() {typeof(string)}; + for (int i = 0; i < indexAttribute.IndexFields.Count; i++) + { + constructorTypes.AddRange(new[] {typeof(string), typeof(OrderByType)}); + } + + constructorTypes.Add(typeof(bool)); + + var values = new List() {indexAttribute.IndexName}; + foreach (var indexField in indexAttribute.IndexFields) + { + values.AddRange(new object[] {indexField.Key, indexField.Value}); + } + + values.Add(indexAttribute.IsUnique); + return new CustomAttributeBuilder(type.GetConstructor(constructorTypes.ToArray())!, values.ToArray()); } public static DynamicProperyBuilder CreateIndex(this DynamicProperyBuilder builder, SugarIndexAttribute indexAttribute) From 6e136fe861c01dcdf392fefbf5cd55010bf29a90 Mon Sep 17 00:00:00 2001 From: LemonNoCry Date: Fri, 21 Jul 2023 20:23:33 +0800 Subject: [PATCH 73/84] =?UTF-8?q?=E2=9C=A8=20=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Systems/DynamicCodeFirstController.cs | 2 +- Blog.Core.Api/appsettings.json | 30 +++++++++---------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/Blog.Core.Api/Controllers/Systems/DynamicCodeFirstController.cs b/Blog.Core.Api/Controllers/Systems/DynamicCodeFirstController.cs index 29260f03..37c84791 100644 --- a/Blog.Core.Api/Controllers/Systems/DynamicCodeFirstController.cs +++ b/Blog.Core.Api/Controllers/Systems/DynamicCodeFirstController.cs @@ -11,7 +11,7 @@ namespace Blog.Core.Api.Controllers.Systems; /// -/// 缓存管理 +/// 动态建表 CURD /// [Route("api/Systems/[controller]/[action]")] [ApiController] diff --git a/Blog.Core.Api/appsettings.json b/Blog.Core.Api/appsettings.json index 3b3b4041..d94a7606 100644 --- a/Blog.Core.Api/appsettings.json +++ b/Blog.Core.Api/appsettings.json @@ -76,12 +76,13 @@ "SvcName": "", // /svc/blog "UseLoadTest": false }, + // 请配置MainDB为你想要的主库的ConnId值,并设置对应的Enabled为true; // *** 单库操作,把 MutiDBEnabled 设为false ***; // *** 多库操作,把 MutiDBEnabled 设为true,其他的从库Enabled也为true **; // 具体配置看视频:https://www.bilibili.com/video/BV1BJ411B7mn?p=6 //Log:日志库; - "MainDB": "WMBLOG_MSSQL_1", //当前项目的主库,所对应的连接字符串的Enabled必须为true + "MainDB": "WMBLOG_SQLITE", //当前项目的主库,所对应的连接字符串的Enabled必须为true "MutiDBEnabled": true, //是否开启多库模式 "CQRSEnabled": false, //是否开启读写分离模式,必须是单库模式,且数据库类型一致,比如都是SqlServer "DBS": [ @@ -98,24 +99,23 @@ { "ConnId": "WMBLOG_SQLITE", "DBType": 2, - "Enabled": false, + "Enabled": true, "HitRate": 50, // 值越大,优先级越高 "Connection": "WMBlog.db" //sqlite只写数据库名就行 }, { "ConnId": "Log", //日志库连接固定名称,不要改,其他的可以改 - "DBType": 1, + "DBType": 2, "Enabled": true, "HitRate": 50, // 值越大,优先级越高 - "Connection": "Server=localhost;Database=BlogCoreLog;Trusted_Connection=True;", - "ProviderName": "System.Data.SqlClient" + "Connection": "WMBlogLog.db" //sqlite只写数据库名就行 }, { "ConnId": "WMBLOG_MSSQL_1", "DBType": 1, - "Enabled": true, + "Enabled": false, "HitRate": 40, - "Connection": "Server=localhost;Database=BlogCore;Trusted_Connection=True;", + "Connection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=WMBLOG_MSSQL_1;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", "ProviderName": "System.Data.SqlClient" }, { @@ -257,16 +257,15 @@ "RealIpHeader": "X-Real-IP", "ClientIdHeader": "X-ClientId", "IpWhitelist": [], //白名单 - "EndpointWhitelist": ["get:/api/xxx", "*:/api/yyy"], - "ClientWhitelist": ["dev-client-1", "dev-client-2"], + "EndpointWhitelist": [ "get:/api/xxx", "*:/api/yyy" ], + "ClientWhitelist": [ "dev-client-1", "dev-client-2" ], "QuotaExceededResponse": { "Content": "{{\"status\":429,\"msg\":\"访问过于频繁,请稍后重试\",\"success\":false}}", "ContentType": "application/json", "StatusCode": 429 }, "HttpStatusCode": 429, //返回状态码 - "GeneralRules": [ - //api规则,结尾一定要带* + "GeneralRules": [ //api规则,结尾一定要带* { "Endpoint": "*:/api/blog*", "Period": "1m", @@ -288,6 +287,7 @@ "Limit": 500 } ] + }, "ConsulSetting": { "ServiceName": "BlogCoreService", @@ -296,8 +296,7 @@ "ServiceHealthCheck": "/healthcheck", "ConsulAddress": "http://localhost:8500" }, - "PayInfo": { - //建行聚合支付信息 + "PayInfo": { //建行聚合支付信息 "MERCHANTID": "", //商户号 "POSID": "", //柜台号 "BRANCHID": "", //分行号 @@ -307,7 +306,7 @@ "OutAddress": "http://127.0.0.1:12345" //外联地址 }, "nacos": { - "ServerAddresses": ["http://localhost:8848"], // nacos 连接地址 + "ServerAddresses": [ "http://localhost:8848" ], // nacos 连接地址 "DefaultTimeOut": 15000, // 默认超时时间 "Namespace": "public", // 命名空间 "ListenInterval": 10000, // 监听的频率 @@ -318,8 +317,7 @@ "LogFiedOutPutConfigs": { "tcpAddressHost": "", // 输出elk的tcp连接地址 "tcpAddressPort": 0, // 输出elk的tcp端口号 - "ConfigsInfo": [ - // 配置的输出elk节点内容 常用语动态标识 + "ConfigsInfo": [ // 配置的输出elk节点内容 常用语动态标识 { "FiedName": "applicationName", "FiedValue": "Blog.Core.Api" From 4638fc7633963980da056525c909facf1e19b37d Mon Sep 17 00:00:00 2001 From: ansonzhang <3143422472@qq.com> Date: Wed, 26 Jul 2023 18:15:31 +0800 Subject: [PATCH 74/84] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a62a2eb6..66ce9e7f 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,9 @@ Blog.Core 开箱即用的企业级前后端分离【 .NET Core6.0 Api + Vue 2.x - [x] 封装`Blog.Core.Webapi.Template`项目模板,一键重建自己的项目 ✨; - [x] 搭配多个前端案例供参考和借鉴:Blog.Vue、Blog.Admin、Nuxt.tbug、Blog.Mvp.Blazor ✨; - [x] 统一集成 IdentityServer4 认证 ✨; -- [x] 统一实现多租户; +- [x] 统一实现多租户; +- [x] 实现分表案例,支持分表的增删改查哈分页查询,具体查看SplitDemoController.cs; +- [x] 支持signalR对指定用户通讯; 组件模块: From a3759986993de51344e3b6431d6f627241eb2dbc Mon Sep 17 00:00:00 2001 From: ansonzhang <3143422472@qq.com> Date: Sat, 5 Aug 2023 12:56:46 +0800 Subject: [PATCH 75/84] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 66ce9e7f..2ab20732 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ Blog.Core 开箱即用的企业级前后端分离【 .NET Core6.0 Api + Vue 2.x - [x] 可配合 Jenkins 实现CI / CD; - [x] 可配合 Consul 实现服务发现; - [x] 可配合 Nacos 实现服务发现; -- [x] 可配合 Ocelot 实现网关处理; +- [x] 可配合 apisix/Ocelot 实现网关处理; - [x] 可配合 Nginx 实现负载均衡; - [x] 可配合 Ids4 实现认证中心; From 8372a3a0d4d0c618dec3c0abd38b83a16d8f8024 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Wed, 23 Aug 2023 16:13:42 +0800 Subject: [PATCH 76/84] feat: :tada: test log sql operate log --- Blog.Core.Api/Blog.Core.xml | 14 +- Blog.Core.Api/appsettings.json | 3 + Blog.Core.Common/DB/Aop/SqlsugarAop.cs | 6 +- .../HttpContextUser/AspNetUser.cs | 2 +- .../ServiceExtensions/AppConfigSetup.cs | 10 + .../AutofacModuleRegister.cs | 6 + .../ServiceExtensions/SqlsugarSetup.cs | 265 ++++++++++-------- 7 files changed, 182 insertions(+), 124 deletions(-) diff --git a/Blog.Core.Api/Blog.Core.xml b/Blog.Core.Api/Blog.Core.xml index 566d345a..667dcfdc 100644 --- a/Blog.Core.Api/Blog.Core.xml +++ b/Blog.Core.Api/Blog.Core.xml @@ -1393,9 +1393,21 @@ - 缓存管理 + 动态建表 CURD + + + 动态type + + + + + + 动态type 继承BaseEntity + + + 测试建表 diff --git a/Blog.Core.Api/appsettings.json b/Blog.Core.Api/appsettings.json index d94a7606..82fc206a 100644 --- a/Blog.Core.Api/appsettings.json +++ b/Blog.Core.Api/appsettings.json @@ -57,6 +57,9 @@ "TranAOP": { "Enabled": true }, + "UserAuditAOP": { + "Enabled": false + }, "SqlAOP": { "Enabled": true, "LogToFile": { diff --git a/Blog.Core.Common/DB/Aop/SqlsugarAop.cs b/Blog.Core.Common/DB/Aop/SqlsugarAop.cs index f165ef43..826984ed 100644 --- a/Blog.Core.Common/DB/Aop/SqlsugarAop.cs +++ b/Blog.Core.Common/DB/Aop/SqlsugarAop.cs @@ -11,7 +11,7 @@ namespace Blog.Core.Common.DB.Aop; public static class SqlSugarAop { - public static void OnLogExecuting(ISqlSugarClient sqlSugarScopeProvider, string sql, SugarParameter[] p, ConnectionConfig config) + public static void OnLogExecuting(ISqlSugarClient sqlSugarScopeProvider, string user, string table, string operate, string sql, SugarParameter[] p, ConnectionConfig config) { try { @@ -25,8 +25,8 @@ public static void OnLogExecuting(ISqlSugarClient sqlSugarScopeProvider, string { using (LogContextExtension.Create.SqlAopPushProperty(sqlSugarScopeProvider)) { - Log.Information("------------------ \r\n ConnId:[{ConnId}]【SQL语句】: \r\n {Sql}", - config.ConfigId, UtilMethods.GetNativeSql( sql, p)); + Log.Information("------------------ \r\n User:[{User}] Table:[{Table}] Operate:[{Operate}] ConnId:[{ConnId}]【SQL语句】: \r\n {Sql}", + user, table, operate, config.ConfigId, UtilMethods.GetNativeSql(sql, p)); } } } diff --git a/Blog.Core.Common/HttpContextUser/AspNetUser.cs b/Blog.Core.Common/HttpContextUser/AspNetUser.cs index 1ceaa45f..dfa87949 100644 --- a/Blog.Core.Common/HttpContextUser/AspNetUser.cs +++ b/Blog.Core.Common/HttpContextUser/AspNetUser.cs @@ -48,7 +48,7 @@ private string GetName() public bool IsAuthenticated() { - return _accessor.HttpContext.User.Identity.IsAuthenticated; + return _accessor.HttpContext?.User?.Identity?.IsAuthenticated ?? false; } diff --git a/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs b/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs index d7a1f90d..680a1912 100644 --- a/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs @@ -79,6 +79,15 @@ public static void AddAppConfigSetup(this IServiceCollection services, IHostEnvi { ConsoleHelper.WriteSuccessLine($"Transaction AOP: True"); } + // 审计AOP + if (!AppSettings.app(new string[] { "AppSettings", "UserAuditAOP", "Enabled" }).ObjToBool()) + { + Console.WriteLine($"UserAudit AOP: False"); + } + else + { + ConsoleHelper.WriteSuccessLine($"UserAudit AOP: True"); + } // 数据库Sql执行AOP if (!AppSettings.app(new string[] { "AppSettings", "SqlAOP", "OutToLogFile", "Enabled" }).ObjToBool()) @@ -251,6 +260,7 @@ public static void AddAppTableConfigSetup(this IServiceCollection services, IHos new string[] { "缓存AOP", AppSettings.app("AppSettings", "CachingAOP", "Enabled") }, new string[] { "服务日志AOP", AppSettings.app("AppSettings", "LogAOP", "Enabled") }, new string[] { "事务AOP", AppSettings.app("AppSettings", "TranAOP", "Enabled") }, + new string[] { "服务审计AOP", AppSettings.app("AppSettings", "UserAuditAOP", "Enabled") }, new string[] { "Sql执行AOP", AppSettings.app("AppSettings", "SqlAOP", "Enabled") }, new string[] { "Sql执行AOP控制台输出", AppSettings.app("AppSettings", "SqlAOP", "LogToConsole", "Enabled") }, }; diff --git a/Blog.Core.Extensions/ServiceExtensions/AutofacModuleRegister.cs b/Blog.Core.Extensions/ServiceExtensions/AutofacModuleRegister.cs index 4836c402..4236bfb7 100644 --- a/Blog.Core.Extensions/ServiceExtensions/AutofacModuleRegister.cs +++ b/Blog.Core.Extensions/ServiceExtensions/AutofacModuleRegister.cs @@ -57,6 +57,12 @@ protected override void Load(ContainerBuilder builder) cacheType.Add(typeof(BlogLogAOP)); } + if (AppSettings.app(new string[] { "AppSettings", "UserAuditAOP", "Enabled" }).ObjToBool()) + { + builder.RegisterType(); + cacheType.Add(typeof(BlogUserAuditAOP)); + } + builder.RegisterGeneric(typeof(BaseRepository<>)).As(typeof(IBaseRepository<>)).InstancePerDependency(); //注册仓储 builder.RegisterGeneric(typeof(BaseServices<>)).As(typeof(IBaseServices<>)).InstancePerDependency(); //注册服务 diff --git a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs index dd5c02b3..9cf99036 100644 --- a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs @@ -11,126 +11,153 @@ using System.Collections.Generic; using System.Threading.Tasks; using Blog.Core.Common.Caches; +using Blog.Core.Common.Core; +using Blog.Core.Common.HttpContextUser; +using static Grpc.Core.ChannelOption; +using System.Text.RegularExpressions; namespace Blog.Core.Extensions { - /// - /// 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)); - - // 默认添加主数据库连接 - MainDb.CurrentDbConnId = AppSettings.app(new string[] {"MainDB"}); - - BaseDBConfig.MutiConnectionString.slaveDbs.ForEach(s => - { - BaseDBConfig.AllSlaveConfigs.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() - { - DataInfoCacheService = new SqlSugarCacheService(), - 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); - }); - - if (BaseDBConfig.LogConfig is null) - { - throw new ApplicationException("未配置Log库连接"); - } - - // SqlSugarScope是线程安全,可使用单例注入 - // 参考:https://www.donet5.com/Home/Doc?typeId=1181 - services.AddSingleton(o => - { - return new SqlSugarScope(BaseDBConfig.AllConfigs, db => - { - BaseDBConfig.ValidConfig.ForEach(config => - { - var dbProvider = db.GetConnectionScope((string) config.ConfigId); - - // 打印SQL语句 - dbProvider.Aop.OnLogExecuting = (s, parameters) => - SqlSugarAop.OnLogExecuting(dbProvider, s, parameters, config); - - // 数据审计 - dbProvider.Aop.DataExecuting = SqlSugarAop.DataExecuting; - - // 配置实体假删除过滤器 - RepositorySetting.SetDeletedEntityFilter(dbProvider); - // 配置实体数据权限 - RepositorySetting.SetTenantEntityFilter(dbProvider); - }); - }); - }); - } - - private static string GetWholeSql(SugarParameter[] paramArr, string sql) - { - foreach (var param in paramArr) - { - sql.Replace(param.ParameterName, param.Value.ObjToString()); - } - - return sql; - } - - private static string GetParas(SugarParameter[] pars) - { - string key = "【SQL参数】:"; - foreach (var param in pars) - { - key += $"{param.ParameterName}:{param.Value}\n"; - } - - return key; - } - } + /// + /// 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)); + + // 默认添加主数据库连接 + MainDb.CurrentDbConnId = AppSettings.app(new string[] { "MainDB" }); + + BaseDBConfig.MutiConnectionString.slaveDbs.ForEach(s => + { + BaseDBConfig.AllSlaveConfigs.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() + { + DataInfoCacheService = new SqlSugarCacheService(), + 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); + }); + + if (BaseDBConfig.LogConfig is null) + { + throw new ApplicationException("未配置Log库连接"); + } + + // SqlSugarScope是线程安全,可使用单例注入 + // 参考:https://www.donet5.com/Home/Doc?typeId=1181 + services.AddSingleton(o => + { + return new SqlSugarScope(BaseDBConfig.AllConfigs, db => + { + BaseDBConfig.ValidConfig.ForEach(config => + { + var dbProvider = db.GetConnectionScope((string)config.ConfigId); + + // 打印SQL语句 + dbProvider.Aop.OnLogExecuting = (s, parameters) => + { + var user = InternalApp.RootServices.GetService(); + SqlSugarAop.OnLogExecuting(dbProvider, user?.Name.ObjToString(), ExtractTableName(s), Enum.GetName(typeof(SugarActionType), dbProvider.SugarActionType), s, parameters, config); + }; + + // 数据审计 + dbProvider.Aop.DataExecuting = SqlSugarAop.DataExecuting; + + // 配置实体假删除过滤器 + RepositorySetting.SetDeletedEntityFilter(dbProvider); + // 配置实体数据权限 + RepositorySetting.SetTenantEntityFilter(dbProvider); + }); + }); + }); + } + + private static string GetWholeSql(SugarParameter[] paramArr, string sql) + { + foreach (var param in paramArr) + { + sql.Replace(param.ParameterName, param.Value.ObjToString()); + } + + return sql; + } + + private static string GetParas(SugarParameter[] pars) + { + string key = "【SQL参数】:"; + foreach (var param in pars) + { + key += $"{param.ParameterName}:{param.Value}\n"; + } + + return key; + } + + private static string ExtractTableName(string sql) + { + // 匹配 SQL 语句中的表名的正则表达式 + //string regexPattern = @"\s*(?:UPDATE|DELETE\s+FROM|SELECT\s+\*\s+FROM)\s+(\w+)"; + string regexPattern = @"(?i)(?:FROM|UPDATE|DELETE\s+FROM)\s+`(.+?)`"; + Regex regex = new Regex(regexPattern, RegexOptions.IgnoreCase); + Match match = regex.Match(sql); + + if (match.Success) + { + // 提取匹配到的表名 + return match.Groups[1].Value; + } + else + { + // 如果没有匹配到表名,则返回空字符串或者抛出异常等处理 + return string.Empty; + } + } + } } \ No newline at end of file From 6d0a3dfc092307fcb0273a0de319076dd6dcc00c Mon Sep 17 00:00:00 2001 From: LemonNoCry Date: Wed, 30 Aug 2023 19:40:54 +0800 Subject: [PATCH 77/84] =?UTF-8?q?=F0=9F=90=9B=F0=9F=90=9B=F0=9F=90=9B=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8DRootService=E5=86=85=E5=AD=98=E6=BA=A2?= =?UTF-8?q?=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Common/App.cs | 29 ++++++++++++------- Blog.Core.Common/Core/InternalApp.cs | 13 +++++---- .../ServiceExtensions/SqlsugarSetup.cs | 3 +- 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/Blog.Core.Common/App.cs b/Blog.Core.Common/App.cs index 07abee9f..ecdb1b49 100644 --- a/Blog.Core.Common/App.cs +++ b/Blog.Core.Common/App.cs @@ -39,6 +39,7 @@ public static bool IsRun /// 有效程序集类型 public static readonly IEnumerable EffectiveTypes; + /// 优先使用App.GetService()手动获取服务 public static IServiceProvider RootServices => IsRun || IsBuild ? InternalApp.RootServices : null; /// 获取Web主机环境,如,是否是开发环境,生产环境等 @@ -55,14 +56,16 @@ public static bool IsRun /// public static HttpContext HttpContext => RootServices?.GetService()?.HttpContext; - public static IUser User => HttpContext == null ? null : RootServices?.GetService(); + public static IUser User => GetService(); #region Service /// 解析服务提供器 /// + /// + /// /// - public static IServiceProvider GetServiceProvider(Type serviceType, bool mustBuild = false) + public static IServiceProvider GetServiceProvider(Type serviceType, bool mustBuild = false, bool throwException = true) { if (App.HostEnvironment == null || App.RootServices != null && InternalApp.InternalServices @@ -71,25 +74,31 @@ public static IServiceProvider GetServiceProvider(Type serviceType, bool mustBui (serviceType.IsGenericType ? serviceType.GetGenericTypeDefinition() : serviceType))) .Any((u => u.Lifetime == ServiceLifetime.Singleton))) return App.RootServices; - HttpContext httpContext = App.HttpContext; - if (httpContext?.RequestServices != null) - return httpContext.RequestServices; + + //获取请求生存周期的服务 + if (HttpContext?.RequestServices != null) + return HttpContext.RequestServices; + if (App.RootServices != null) { - IServiceScope scope = App.RootServices.CreateScope(); + IServiceScope scope = RootServices.CreateScope(); return scope.ServiceProvider; } if (mustBuild) { - throw new ApplicationException("当前不可用,必须要等到 WebApplication Build后"); + if (throwException) + { + throw new ApplicationException("当前不可用,必须要等到 WebApplication Build后"); + } + + return default; } ServiceProvider serviceProvider = InternalApp.InternalServices.BuildServiceProvider(); return serviceProvider; } - public static TService GetService(bool mustBuild = true) where TService : class => App.GetService(typeof(TService), null, mustBuild) as TService; @@ -99,7 +108,7 @@ public static TService GetService(bool mustBuild = true) where TServic /// /// public static TService GetService(IServiceProvider serviceProvider, bool mustBuild = true) - where TService : class => App.GetService(typeof(TService), serviceProvider, mustBuild) as TService; + where TService : class => (serviceProvider ?? App.GetServiceProvider(typeof(TService), mustBuild, false))?.GetService(); /// 获取请求生存周期的服务 /// @@ -107,7 +116,7 @@ public static TService GetService(IServiceProvider serviceProvider, bo /// /// public static object GetService(Type type, IServiceProvider serviceProvider = null, bool mustBuild = true) => - (serviceProvider ?? App.GetServiceProvider(type, mustBuild)).GetService(type); + (serviceProvider ?? App.GetServiceProvider(type, mustBuild, false))?.GetService(type); #endregion diff --git a/Blog.Core.Common/Core/InternalApp.cs b/Blog.Core.Common/Core/InternalApp.cs index df16c911..b8a7736a 100644 --- a/Blog.Core.Common/Core/InternalApp.cs +++ b/Blog.Core.Common/Core/InternalApp.cs @@ -7,21 +7,24 @@ namespace Blog.Core.Common.Core; +/// +/// 内部只用于初始化使用 +/// public static class InternalApp { - public static IServiceCollection InternalServices; + internal static IServiceCollection InternalServices; /// 根服务 - public static IServiceProvider RootServices; + internal static IServiceProvider RootServices; /// 获取Web主机环境 - public static IWebHostEnvironment WebHostEnvironment; + internal static IWebHostEnvironment WebHostEnvironment; /// 获取泛型主机环境 - public static IHostEnvironment HostEnvironment; + internal static IHostEnvironment HostEnvironment; /// 配置对象 - public static IConfiguration Configuration; + internal static IConfiguration Configuration; public static void ConfigureApplication(this WebApplicationBuilder wab) { diff --git a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs index 9cf99036..6574e70e 100644 --- a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs @@ -103,8 +103,7 @@ public static void AddSqlsugarSetup(this IServiceCollection services) // 打印SQL语句 dbProvider.Aop.OnLogExecuting = (s, parameters) => { - var user = InternalApp.RootServices.GetService(); - SqlSugarAop.OnLogExecuting(dbProvider, user?.Name.ObjToString(), ExtractTableName(s), Enum.GetName(typeof(SugarActionType), dbProvider.SugarActionType), s, parameters, config); + SqlSugarAop.OnLogExecuting(dbProvider, App.User?.Name.ObjToString(), ExtractTableName(s), Enum.GetName(typeof(SugarActionType), dbProvider.SugarActionType), s, parameters, config); }; // 数据审计 From 39fc0ab4e8699e4285f75c3774949c7f1cb5fd4c Mon Sep 17 00:00:00 2001 From: LemonNoCry Date: Wed, 30 Aug 2023 19:42:56 +0800 Subject: [PATCH 78/84] =?UTF-8?q?=F0=9F=8E=A8=20=E8=B0=83=E6=95=B4DbSeed?= =?UTF-8?q?=E6=89=B9=E9=87=8F=E5=86=99=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Common/Seed/DBSeed.cs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/Blog.Core.Common/Seed/DBSeed.cs b/Blog.Core.Common/Seed/DBSeed.cs index 6fa8f901..a70bf847 100644 --- a/Blog.Core.Common/Seed/DBSeed.cs +++ b/Blog.Core.Common/Seed/DBSeed.cs @@ -177,11 +177,7 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) { var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "Permission"), Encoding.UTF8), setting); - foreach (var item in data) - { - Console.WriteLine($"{item.Name}:{item.Id}"); - myContext.GetEntityDB().Insert(item); - } + myContext.GetEntityDB().InsertRange(data); Console.WriteLine("Table:Permission created success!"); } else @@ -218,11 +214,7 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) { var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "RoleModulePermission"), Encoding.UTF8), setting); - foreach (var item in data) - { - Console.WriteLine($"{item.Id}"); - myContext.GetEntityDB().Insert(item); - } + myContext.GetEntityDB().InsertRange(data); Console.WriteLine("Table:RoleModulePermission created success!"); } else From dfa067d21446409a83abd9a71f76afb0cd0757bd Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Wed, 18 Oct 2023 09:55:54 +0800 Subject: [PATCH 79/84] =?UTF-8?q?=F0=9F=90=9B=20HttpClient=20=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E5=8D=95=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Extensions/ExpressionExtensions_Nacos.cs | 38 ++++++++++--------- Blog.Core.Common/Helper/HttpHelper.cs | 20 +++++----- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/Blog.Core.Common/Extensions/ExpressionExtensions_Nacos.cs b/Blog.Core.Common/Extensions/ExpressionExtensions_Nacos.cs index c187ee6a..705d4c15 100644 --- a/Blog.Core.Common/Extensions/ExpressionExtensions_Nacos.cs +++ b/Blog.Core.Common/Extensions/ExpressionExtensions_Nacos.cs @@ -15,9 +15,8 @@ public static class ExpressionExtensions_Nacos { #region Nacos NamingService - private static readonly HttpClient httpclient = new HttpClient(); - - private static string GetServiceUrl(Nacos.V2.INacosNamingService serv, string ServiceName, string Group, string apiurl) + private static string GetServiceUrl(Nacos.V2.INacosNamingService serv, string ServiceName, string Group, + string apiurl) { try { @@ -45,7 +44,8 @@ private static string GetServiceUrl(Nacos.V2.INacosNamingService serv, string Se return ""; } - public static async Task Cof_NaoceGet(this Nacos.V2.INacosNamingService serv, string ServiceName, string Group, string apiurl, Dictionary Parameters = null) + public static async Task Cof_NaoceGet(this Nacos.V2.INacosNamingService serv, string ServiceName, + string Group, string apiurl, Dictionary Parameters = null) { try { @@ -62,8 +62,9 @@ public static async Task Cof_NaoceGet(this Nacos.V2.INacosNamingService url = $"{url}?{sb.ToString().Trim('&')}"; } - httpclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - var result = await httpclient.GetAsync(url); + HttpHelper.Httpclient.DefaultRequestHeaders.Accept.Add( + new MediaTypeWithQualityHeaderValue("application/json")); + var result = await HttpHelper.Httpclient.GetAsync(url); return await result.Content.ReadAsStringAsync(); } catch (Exception e) @@ -74,7 +75,8 @@ public static async Task Cof_NaoceGet(this Nacos.V2.INacosNamingService return ""; } - public static async Task Cof_NaocePostForm(this Nacos.V2.INacosNamingService serv, string ServiceName, string Group, string apiurl, Dictionary Parameters) + public static async Task Cof_NaocePostForm(this Nacos.V2.INacosNamingService serv, string ServiceName, + string Group, string apiurl, Dictionary Parameters) { try { @@ -82,8 +84,8 @@ public static async Task Cof_NaocePostForm(this Nacos.V2.INacosNamingSer if (string.IsNullOrEmpty(url)) return ""; var content = (Parameters != null && Parameters.Any()) ? new FormUrlEncodedContent(Parameters) : null; - httpclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - var result = await httpclient.PostAsync(url, content); + HttpHelper.Httpclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + var result = await HttpHelper.Httpclient.PostAsync(url, content); return await result.Content.ReadAsStringAsync(); //.GetAwaiter().GetResult(); } catch (Exception e) @@ -94,14 +96,16 @@ public static async Task Cof_NaocePostForm(this Nacos.V2.INacosNamingSer return ""; } - public static async Task Cof_NaocePostJson(this Nacos.V2.INacosNamingService serv, string ServiceName, string Group, string apiurl, string jSonData) + public static async Task Cof_NaocePostJson(this Nacos.V2.INacosNamingService serv, string ServiceName, + string Group, string apiurl, string jSonData) { try { var url = GetServiceUrl(serv, ServiceName, Group, apiurl); if (string.IsNullOrEmpty(url)) return ""; - httpclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - var result = await httpclient.PostAsync(url, new StringContent(jSonData, Encoding.UTF8, "application/json")); + HttpHelper.Httpclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + var result = + await HttpHelper.Httpclient.PostAsync(url, new StringContent(jSonData, Encoding.UTF8, "application/json")); return await result.Content.ReadAsStringAsync(); //.GetAwaiter().GetResult(); //httpClient.BaseAddress = new Uri("https://www.testapi.com"); @@ -116,7 +120,8 @@ public static async Task Cof_NaocePostJson(this Nacos.V2.INacosNamingSer return ""; } - public static async Task Cof_NaocePostFile(this Nacos.V2.INacosNamingService serv, string ServiceName, string Group, string apiurl, Dictionary Parameters) + public static async Task Cof_NaocePostFile(this Nacos.V2.INacosNamingService serv, string ServiceName, + string Group, string apiurl, Dictionary Parameters) { try { @@ -129,8 +134,8 @@ public static async Task Cof_NaocePostFile(this Nacos.V2.INacosNamingSer content.Add(new ByteArrayContent(pitem.Value), "files", pitem.Key); } - httpclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - var result = await httpclient.PostAsync(url, content); + HttpHelper.Httpclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + var result = await HttpHelper.Httpclient.PostAsync(url, content); return await result.Content.ReadAsStringAsync(); //.GetAwaiter().GetResult(); } catch (Exception e) @@ -144,5 +149,4 @@ public static async Task Cof_NaocePostFile(this Nacos.V2.INacosNamingSer #endregion } - -} +} \ No newline at end of file diff --git a/Blog.Core.Common/Helper/HttpHelper.cs b/Blog.Core.Common/Helper/HttpHelper.cs index 4bd14883..82f9c32f 100644 --- a/Blog.Core.Common/Helper/HttpHelper.cs +++ b/Blog.Core.Common/Helper/HttpHelper.cs @@ -10,21 +10,23 @@ namespace Blog.Core.Common.Helper /// public class HttpHelper { + public static readonly HttpClient Httpclient = new HttpClient(); + public static async Task GetAsync(string serviceAddress) { try { string result = string.Empty; Uri getUrl = new Uri(serviceAddress); - using var httpClient = new HttpClient(); - httpClient.Timeout = new TimeSpan(0, 0, 60); - result = await httpClient.GetAsync(serviceAddress).Result.Content.ReadAsStringAsync(); + Httpclient.Timeout = new TimeSpan(0, 0, 60); + result = await Httpclient.GetAsync(serviceAddress).Result.Content.ReadAsStringAsync(); return result; } catch (Exception e) { Console.WriteLine(e.Message); } + return null; } @@ -38,19 +40,19 @@ public static async Task PostAsync(string serviceAddress, string request using (HttpContent httpContent = new StringContent(requestJson)) { httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); - using var httpClient = new HttpClient(); - httpClient.Timeout = new TimeSpan(0, 0, 60); - result = await httpClient.PostAsync(serviceAddress, httpContent).Result.Content.ReadAsStringAsync(); + + Httpclient.Timeout = new TimeSpan(0, 0, 60); + result = await Httpclient.PostAsync(serviceAddress, httpContent).Result.Content.ReadAsStringAsync(); } + return result; } catch (Exception e) { Console.WriteLine(e.Message); } + return null; } } - - -} +} \ No newline at end of file From 0901de2fbfd8d8128a4ecf3f8b27daf2054c35c2 Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Thu, 19 Oct 2023 16:28:20 +0800 Subject: [PATCH 80/84] =?UTF-8?q?=F0=9F=8E=A8=E2=9C=A8=F0=9F=8E=89=20?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=8E=9F=E6=9C=89=E7=9A=84DBS=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E3=80=81=E6=96=B0=E5=A2=9E=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=E6=95=85=E9=9A=9C=E8=BD=AC=E7=A7=BB=E6=96=B9=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.优化原有的DBS配置,破坏性修改,原有的DBS配置在多库和读写分离无法兼容,配置写法不是合适,故此优化 2.新增数据库故障转移方案,例如主库挂了自动切换到备用库,备用库不会由程序维护,需要运维、dba去做数据库同步方案,比如Sqlserver事务日志传输等 故障转移方案兼容多种方式 1.数据库主从方案 在配置主从之后,需要将从库配置为备用链接就行了 一般就是:修改、写入、删除走主库,查询操作走从库,在主库挂了后则所有操作走从库 2.数据库主备方案 日常使用主数据库操作,备用库只是备份,只有主库挂了才会用备用库 从库和备库都属于slave库功能 --- .../Controllers/DbFirst/DbFirstController.cs | 26 +-- .../Controllers/MonitorController.cs | 2 +- Blog.Core.Api/appsettings.json | 51 ++++-- Blog.Core.Common/DB/Aop/SqlSugarReuse.cs | 23 +++ Blog.Core.Common/DB/BaseDBConfig.cs | 102 +++++------ .../DB/Extension/DbEntityException.cs | 14 ++ Blog.Core.Common/DB/MainDb.cs | 2 +- Blog.Core.Common/Helper/UtilConvert.cs | 8 + Blog.Core.Common/Seed/DBSeed.cs | 94 +++++----- .../ServiceExtensions/AppConfigSetup.cs | 16 +- .../ServiceExtensions/SqlsugarSetup.cs | 39 ++-- .../IDS4Db/IApplicationUserServices.cs | 4 +- Blog.Core.Repository/BASE/BaseRepository.cs | 21 +-- .../IDS4Db/ApplicationUserServices.cs | 11 +- Blog.Core.Tests/Blog.Core.Tests.csproj | 15 +- Blog.Core.Tests/appsettings.json | 169 ++++++++++++------ 16 files changed, 363 insertions(+), 234 deletions(-) create mode 100644 Blog.Core.Common/DB/Aop/SqlSugarReuse.cs create mode 100644 Blog.Core.Common/DB/Extension/DbEntityException.cs diff --git a/Blog.Core.Api/Controllers/DbFirst/DbFirstController.cs b/Blog.Core.Api/Controllers/DbFirst/DbFirstController.cs index 4f51856d..83b0beb5 100644 --- a/Blog.Core.Api/Controllers/DbFirst/DbFirstController.cs +++ b/Blog.Core.Api/Controllers/DbFirst/DbFirstController.cs @@ -36,19 +36,19 @@ public MessageModel GetFrameFiles() { var data = new MessageModel() { success = true, msg = "" }; data.response += @"file path is:C:\my-file\}"; - var isMuti = AppSettings.app(new string[] { "MutiDBEnabled" }).ObjToBool(); + var isMuti = BaseDBConfig.IsMulti; if (Env.IsDevelopment()) { data.response += $"Controller层生成:{FrameSeed.CreateControllers(_sqlSugarClient)} || "; - BaseDBConfig.MutiConnectionString.allDbs.ToList().ForEach(m => + BaseDBConfig.ValidConfig.ForEach(m => { - _sqlSugarClient.ChangeDatabase(m.ConnId.ToLower()); - data.response += $"库{m.ConnId}-Model层生成:{FrameSeed.CreateModels(_sqlSugarClient, m.ConnId, isMuti)} || "; - data.response += $"库{m.ConnId}-IRepositorys层生成:{FrameSeed.CreateIRepositorys(_sqlSugarClient, m.ConnId, isMuti)} || "; - data.response += $"库{m.ConnId}-IServices层生成:{FrameSeed.CreateIServices(_sqlSugarClient, m.ConnId, isMuti)} || "; - data.response += $"库{m.ConnId}-Repository层生成:{FrameSeed.CreateRepository(_sqlSugarClient, m.ConnId, isMuti)} || "; - data.response += $"库{m.ConnId}-Services层生成:{FrameSeed.CreateServices(_sqlSugarClient, m.ConnId, isMuti)} || "; + _sqlSugarClient.ChangeDatabase(m.ConfigId.ToLower()); + data.response += $"库{m.ConfigId}-Model层生成:{FrameSeed.CreateModels(_sqlSugarClient, m.ConfigId, isMuti)} || "; + data.response += $"库{m.ConfigId}-IRepositorys层生成:{FrameSeed.CreateIRepositorys(_sqlSugarClient, m.ConfigId, isMuti)} || "; + data.response += $"库{m.ConfigId}-IServices层生成:{FrameSeed.CreateIServices(_sqlSugarClient, m.ConfigId, isMuti)} || "; + data.response += $"库{m.ConfigId}-Repository层生成:{FrameSeed.CreateRepository(_sqlSugarClient, m.ConfigId, isMuti)} || "; + data.response += $"库{m.ConfigId}-Services层生成:{FrameSeed.CreateServices(_sqlSugarClient, m.ConfigId, isMuti)} || "; }); // 切回主库 @@ -74,7 +74,7 @@ public MessageModel GetFrameFilesByTableNames([FromBody]string[] tableNa { ConnID = ConnID == null ? MainDb.CurrentDbConnId.ToLower() : ConnID; - var isMuti = AppSettings.app(new string[] { "MutiDBEnabled" }).ObjToBool(); + var isMuti = BaseDBConfig.IsMulti; var data = new MessageModel() { success = true, msg = "" }; if (Env.IsDevelopment()) { @@ -102,7 +102,7 @@ public MessageModel GetFrameFilesByTableNamesForEntity([FromBody] string { ConnID = ConnID == null ? MainDb.CurrentDbConnId.ToLower() : ConnID; - var isMuti = AppSettings.app(new string[] { "MutiDBEnabled" }).ObjToBool(); + var isMuti = BaseDBConfig.IsMulti; var data = new MessageModel() { success = true, msg = "" }; if (Env.IsDevelopment()) { @@ -112,7 +112,7 @@ public MessageModel GetFrameFilesByTableNamesForEntity([FromBody] string { data.success = false; data.msg = "当前不处于开发模式,代码生成不可用!"; - } + } return data; } /// @@ -126,7 +126,7 @@ public MessageModel GetFrameFilesByTableNamesForController([FromBody] st { ConnID = ConnID == null ? MainDb.CurrentDbConnId.ToLower() : ConnID; - var isMuti = AppSettings.app(new string[] { "MutiDBEnabled" }).ObjToBool(); + var isMuti = BaseDBConfig.IsMulti; var data = new MessageModel() { success = true, msg = "" }; if (Env.IsDevelopment()) { @@ -151,7 +151,7 @@ public MessageModel GetAllFrameFilesByTableNames([FromBody]string[] tabl { ConnID = ConnID == null ? MainDb.CurrentDbConnId.ToLower() : ConnID; - var isMuti = AppSettings.app(new string[] { "MutiDBEnabled" }).ObjToBool(); + var isMuti = BaseDBConfig.IsMulti; var data = new MessageModel() { success = true, msg = "" }; if (Env.IsDevelopment()) { diff --git a/Blog.Core.Api/Controllers/MonitorController.cs b/Blog.Core.Api/Controllers/MonitorController.cs index 77b4e5bf..9ed96672 100644 --- a/Blog.Core.Api/Controllers/MonitorController.cs +++ b/Blog.Core.Api/Controllers/MonitorController.cs @@ -219,7 +219,7 @@ public async Task> GetIds4Users() { List apiDates = new List(); - if (AppSettings.app(new string[] { "MutiDBEnabled" }).ObjToBool()) + if (_applicationUserServices.IsEnable()) { var users = await _applicationUserServices.Query(d => d.tdIsDelete == false); diff --git a/Blog.Core.Api/appsettings.json b/Blog.Core.Api/appsettings.json index 82fc206a..f91f6df8 100644 --- a/Blog.Core.Api/appsettings.json +++ b/Blog.Core.Api/appsettings.json @@ -80,14 +80,17 @@ "UseLoadTest": false }, - // 请配置MainDB为你想要的主库的ConnId值,并设置对应的Enabled为true; - // *** 单库操作,把 MutiDBEnabled 设为false ***; - // *** 多库操作,把 MutiDBEnabled 设为true,其他的从库Enabled也为true **; - // 具体配置看视频:https://www.bilibili.com/video/BV1BJ411B7mn?p=6 - //Log:日志库; - "MainDB": "WMBLOG_SQLITE", //当前项目的主库,所对应的连接字符串的Enabled必须为true - "MutiDBEnabled": true, //是否开启多库模式 - "CQRSEnabled": false, //是否开启读写分离模式,必须是单库模式,且数据库类型一致,比如都是SqlServer + //优化DB配置、不会再区分单库多库 + //MainDb:标识当前项目的主库,所对应的连接字符串的Enabled必须为true + //Log:标识日志库,所对应的连接字符串的Enabled必须为true + //从库只需配置Slaves数组,要求数据库类型一致!,比如都是SqlServer + // + //新增,故障转移方案 + //如果主库挂了,会自动切换到备用连接(比如说主库+备用库) + //备用连接的ConnId配置为主库的ConnId+数字即可,比如主库的ConnId为Main,那么备用连接的ConnId为Mian1 + //主库、备用库无需数据库类型一致! + //备用库不会有程序维护,需要手动维护 + "MainDB": "Main", //当前项目的主库,所对应的连接字符串的Enabled必须为true "DBS": [ /* 对应下边的 DBType @@ -100,24 +103,40 @@ Kdbndp = 6,//人大金仓 */ { - "ConnId": "WMBLOG_SQLITE", + "ConnId": "Main", "DBType": 2, "Enabled": true, - "HitRate": 50, // 值越大,优先级越高 - "Connection": "WMBlog.db" //sqlite只写数据库名就行 + "Connection": "WMBlog.db", //sqlite只写数据库名就行 + "Slaves": [ + { + "HitRate": 0,// 值越大,优先级越高 0不使用 + "Connection": "WMBlog2.db" + } + ] + }, + { + "ConnId": "Main2", + "DBType": 2, + "Enabled": true, + "Connection": "WMBlog3.db", //sqlite只写数据库名就行 + "Slaves": [ + { + "HitRate": 0,// 值越大,优先级越高 0不使用 + "Connection": "WMBlog4.db" + } + ] }, { "ConnId": "Log", //日志库连接固定名称,不要改,其他的可以改 "DBType": 2, "Enabled": true, - "HitRate": 50, // 值越大,优先级越高 + "HitRate": 50, "Connection": "WMBlogLog.db" //sqlite只写数据库名就行 }, { "ConnId": "WMBLOG_MSSQL_1", "DBType": 1, "Enabled": false, - "HitRate": 40, "Connection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=WMBLOG_MSSQL_1;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", "ProviderName": "System.Data.SqlClient" }, @@ -125,7 +144,6 @@ "ConnId": "WMBLOG_MSSQL_2", "DBType": 1, "Enabled": false, - "HitRate": 30, "Connection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=WMBLOG_MSSQL_2;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", "ProviderName": "System.Data.SqlClient" }, @@ -133,35 +151,30 @@ "ConnId": "WMBLOG_MYSQL", "DBType": 0, "Enabled": false, - "HitRate": 20, "Connection": "server=localhost;Database=blog;Uid=root;Pwd=root;Port=3306;Allow User Variables=True;" }, { "ConnId": "WMBLOG_MYSQL_2", "DBType": 0, "Enabled": false, - "HitRate": 20, "Connection": "server=localhost;Database=blogcore001;Uid=root;Pwd=root;Port=3306;Allow User Variables=True;" }, { "ConnId": "WMBLOG_ORACLE", "DBType": 3, "Enabled": false, - "HitRate": 10, "Connection": "Data Source=127.0.0.1/ops;User ID=OPS;Password=123456;Persist Security Info=True;Connection Timeout=60;" }, { "ConnId": "WMBLOG_DM", "DBType": 5, "Enabled": false, - "HitRate": 10, "Connection": "Server=xxxxx:5236;User Id=xxxxx;PWD=xxxxx;SCHEMA=TESTDBA;" }, { "ConnId": "WMBLOG_KDBNDP", "DBType": 6, "Enabled": false, - "HitRate": 10, "Connection": "Server=127.0.0.1;Port=54321;UID=SYSTEM;PWD=system;database=SQLSUGAR4XTEST1;" } ], diff --git a/Blog.Core.Common/DB/Aop/SqlSugarReuse.cs b/Blog.Core.Common/DB/Aop/SqlSugarReuse.cs new file mode 100644 index 00000000..949af640 --- /dev/null +++ b/Blog.Core.Common/DB/Aop/SqlSugarReuse.cs @@ -0,0 +1,23 @@ +using System.Linq; +using SqlSugar; + +namespace Blog.Core.Common.DB.Aop; + +public class SqlSugarReuse +{ + public static void AutoChangeAvailableConnect(SqlSugarClient db) + { + if (db == null) return; + if (db.Ado.IsValidConnection()) return; + if (!BaseDBConfig.ReuseConfigs.Any()) return; + + foreach (var connectionConfig in BaseDBConfig.ReuseConfigs) + { + var config = db.CurrentConnectionConfig.ConfigId; + db.ChangeDatabase(connectionConfig.ConfigId); + //移除旧的连接,只会在本次上下文移除,因为主库已经故障会导致多库事务无法使用 + db.RemoveConnection(config); + if (db.Ado.IsValidConnection()) return; + } + } +} \ No newline at end of file diff --git a/Blog.Core.Common/DB/BaseDBConfig.cs b/Blog.Core.Common/DB/BaseDBConfig.cs index b7153766..7eb74fb8 100644 --- a/Blog.Core.Common/DB/BaseDBConfig.cs +++ b/Blog.Core.Common/DB/BaseDBConfig.cs @@ -1,24 +1,46 @@ -using SqlSugar; -using System; +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; //日志库 - + /// + /// 所有库配置 + /// + public static readonly List AllConfigs = new(); + + /// + /// 主库的备用连接配置 + /// + public static readonly List ReuseConfigs = new(); + + /// + /// 有效的库连接(除去Log库) + /// + public static List ValidConfig = new(); + + public static ConnectionConfig MainConfig; + public static ConnectionConfig LogConfig; //日志库 + + public static bool IsMulti => ValidConfig.Count > 1; + /* 之前的单库操作已经删除,如果想要之前的代码,可以查看我的GitHub的历史记录 * 目前是多库操作,默认加载的是appsettings.json设置为true的第一个db连接。 + * + * 优化配置连接 + * 老的配置方式,再多库和从库中有些冲突 + * 直接在单个配置中可以配置从库 + * + * 新增故障转移方案 + * 增加主库备用连接,配置方式为ConfigId为主库的ConfigId+随便数字 只要不重复就好 + * + * 主库在无法连接后会自动切换到备用链接 */ public static (List allDbs, List slaveDbs) MutiConnectionString => MutiInitConn(); - - private static string DifDBConnOfSecurity(params string[] conn) { @@ -44,52 +66,13 @@ public static (List, List) MutiInitConn() { List listdatabase = AppSettings.app("DBS") .Where(i => i.Enabled).ToList(); - var mainDbId = AppSettings.app(new string[] { "MainDB" }).ObjToString(); + var mainDbId = AppSettings.app(new string[] {"MainDB"}).ObjToString(); var mainDbModel = listdatabase.Single(d => d.ConnId == mainDbId); listdatabase.Remove(mainDbModel); listdatabase.Insert(0, mainDbModel); - foreach (var i in listdatabase) - { - SpecialDbString(i); - } - - List listdatabaseSimpleDB = new List(); //单库 - List listdatabaseSlaveDB = new List(); //从库 - - // 单库,且不开启读写分离,只保留一个 - if (!AppSettings.app(new string[] {"CQRSEnabled"}).ObjToBool() && !AppSettings.app(new string[] {"MutiDBEnabled"}).ObjToBool()) - { - if (listdatabase.Count == 1) - { - return (listdatabase, listdatabaseSlaveDB); - } - else - { - var dbFirst = listdatabase.FirstOrDefault(d => d.ConnId == AppSettings.app(new string[] {"MainDB"}).ObjToString()); - if (dbFirst == null) - { - dbFirst = listdatabase.FirstOrDefault(); - } - - listdatabaseSimpleDB.Add(dbFirst); - return (listdatabaseSimpleDB, listdatabaseSlaveDB); - } - } - - - // 读写分离,且必须是单库模式,获取从库 - 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(); - } - } - - - return (listdatabase, listdatabaseSlaveDB); - //} + foreach (var i in listdatabase) SpecialDbString(i); + return (listdatabase, mainDbModel.Slaves); } /// @@ -102,19 +85,23 @@ private static MutiDBOperate SpecialDbString(MutiDBOperate mutiDBOperate) { if (mutiDBOperate.DbType == DataBaseType.Sqlite) { - mutiDBOperate.Connection = $"DataSource=" + Path.Combine(Environment.CurrentDirectory, mutiDBOperate.Connection); + mutiDBOperate.Connection = + $"DataSource=" + Path.Combine(Environment.CurrentDirectory, mutiDBOperate.Connection); } else if (mutiDBOperate.DbType == DataBaseType.SqlServer) { - mutiDBOperate.Connection = DifDBConnOfSecurity(@"D:\my-file\dbCountPsw1_SqlserverConn.txt", mutiDBOperate.Connection); + mutiDBOperate.Connection = DifDBConnOfSecurity(@"D:\my-file\dbCountPsw1_SqlserverConn.txt", + mutiDBOperate.Connection); } else if (mutiDBOperate.DbType == DataBaseType.MySql) { - mutiDBOperate.Connection = DifDBConnOfSecurity(@"D:\my-file\dbCountPsw1_MySqlConn.txt", mutiDBOperate.Connection); + mutiDBOperate.Connection = + DifDBConnOfSecurity(@"D:\my-file\dbCountPsw1_MySqlConn.txt", mutiDBOperate.Connection); } else if (mutiDBOperate.DbType == DataBaseType.Oracle) { - mutiDBOperate.Connection = DifDBConnOfSecurity(@"D:\my-file\dbCountPsw1_OracleConn.txt", mutiDBOperate.Connection); + mutiDBOperate.Connection = + DifDBConnOfSecurity(@"D:\my-file\dbCountPsw1_OracleConn.txt", mutiDBOperate.Connection); } return mutiDBOperate; @@ -159,5 +146,10 @@ public class MutiDBOperate /// 数据库类型 /// public DataBaseType DbType { get; set; } + + /// + /// 从库 + /// + public List Slaves { get; set; } } } \ No newline at end of file diff --git a/Blog.Core.Common/DB/Extension/DbEntityException.cs b/Blog.Core.Common/DB/Extension/DbEntityException.cs new file mode 100644 index 00000000..6d06f291 --- /dev/null +++ b/Blog.Core.Common/DB/Extension/DbEntityException.cs @@ -0,0 +1,14 @@ +using System; +using System.Reflection; +using SqlSugar; + +namespace Blog.Core.Common.DB.Extension; + +public static class DbEntityException +{ + public static object GetEntityTenant(this Type type) + { + var tenant = type.GetCustomAttribute(); + return tenant?.configId; + } +} \ No newline at end of file diff --git a/Blog.Core.Common/DB/MainDb.cs b/Blog.Core.Common/DB/MainDb.cs index f132b710..3de863f3 100644 --- a/Blog.Core.Common/DB/MainDb.cs +++ b/Blog.Core.Common/DB/MainDb.cs @@ -2,6 +2,6 @@ { public static class MainDb { - public static string CurrentDbConnId = "1"; + public static string CurrentDbConnId = "Main"; } } diff --git a/Blog.Core.Common/Helper/UtilConvert.cs b/Blog.Core.Common/Helper/UtilConvert.cs index ea3b7c05..530c4fd2 100644 --- a/Blog.Core.Common/Helper/UtilConvert.cs +++ b/Blog.Core.Common/Helper/UtilConvert.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using Newtonsoft.Json; namespace Blog.Core @@ -288,5 +289,12 @@ public static string ToJson(this object value) { return JsonConvert.SerializeObject(value); } + + public static bool AnyNoException(this ICollection source) + { + if (source == null) return false; + + return source.Any() && source.All(s => s != null); + } } } \ No newline at end of file diff --git a/Blog.Core.Common/Seed/DBSeed.cs b/Blog.Core.Common/Seed/DBSeed.cs index a70bf847..082b9b20 100644 --- a/Blog.Core.Common/Seed/DBSeed.cs +++ b/Blog.Core.Common/Seed/DBSeed.cs @@ -41,40 +41,33 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) SeedDataFolder = Path.Combine(WebRootPath, SeedDataFolder); Console.WriteLine("************ Blog.Core DataBase Set *****************"); - Console.WriteLine($"Is multi-DataBase: {AppSettings.app(new string[] { "MutiDBEnabled" })}"); - Console.WriteLine($"Is CQRS: {AppSettings.app(new string[] { "CQRSEnabled" })}"); - Console.WriteLine(); Console.WriteLine($"Master DB ConId: {myContext.Db.CurrentConnectionConfig.ConfigId}"); Console.WriteLine($"Master DB Type: {myContext.Db.CurrentConnectionConfig.DbType}"); Console.WriteLine($"Master DB ConnectString: {myContext.Db.CurrentConnectionConfig.ConnectionString}"); Console.WriteLine(); - if (AppSettings.app(new string[] { "MutiDBEnabled" }).ObjToBool()) + if (BaseDBConfig.MainConfig.SlaveConnectionConfigs.AnyNoException()) { - var slaveIndex = 0; - BaseDBConfig.MutiConnectionString.allDbs.Where(x => x.ConnId != MainDb.CurrentDbConnId).ToList().ForEach(m => + var index = 0; + BaseDBConfig.MainConfig.SlaveConnectionConfigs.ForEach(m => { - slaveIndex++; - Console.WriteLine($"Slave{slaveIndex} DB ID: {m.ConnId}"); - Console.WriteLine($"Slave{slaveIndex} DB Type: {m.DbType}"); - Console.WriteLine($"Slave{slaveIndex} DB ConnectString: {m.Connection}"); + index++; + Console.WriteLine($"Slave{index} DB HitRate: {m.HitRate}"); + Console.WriteLine($"Slave{index} DB ConnectString: {m.ConnectionString}"); Console.WriteLine($"--------------------------------------"); }); } - else if (AppSettings.app(new string[] { "CQRSEnabled" }).ObjToBool()) + else if (BaseDBConfig.ReuseConfigs.AnyNoException()) { - var slaveIndex = 0; - BaseDBConfig.MutiConnectionString.slaveDbs.Where(x => x.ConnId != MainDb.CurrentDbConnId).ToList().ForEach(m => + var index = 0; + BaseDBConfig.ReuseConfigs.ForEach(m => { - slaveIndex++; - Console.WriteLine($"Slave{slaveIndex} DB ID: {m.ConnId}"); - Console.WriteLine($"Slave{slaveIndex} DB Type: {m.DbType}"); - Console.WriteLine($"Slave{slaveIndex} DB ConnectString: {m.Connection}"); + index++; + Console.WriteLine($"Reuse{index} DB ID: {m.ConfigId}"); + Console.WriteLine($"Reuse{index} DB Type: {m.DbType}"); + Console.WriteLine($"Reuse{index} DB ConnectString: {m.ConnectionString}"); Console.WriteLine($"--------------------------------------"); }); } - else - { - } Console.WriteLine(); @@ -97,7 +90,8 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) Console.WriteLine("Create Tables..."); var path = AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory; - var referencedAssemblies = System.IO.Directory.GetFiles(path, "Blog.Core.Model.dll").Select(Assembly.LoadFrom).ToArray(); + var referencedAssemblies = System.IO.Directory.GetFiles(path, "Blog.Core.Model.dll") + .Select(Assembly.LoadFrom).ToArray(); var modelTypes = referencedAssemblies .SelectMany(a => a.DefinedTypes) .Select(type => type.AsType()) @@ -117,7 +111,7 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) ConsoleHelper.WriteSuccessLine($"Tables created successfully!"); Console.WriteLine(); - if (AppSettings.app(new string[] { "AppSettings", "SeedDBDataEnabled" }).ObjToBool()) + if (AppSettings.app(new string[] {"AppSettings", "SeedDBDataEnabled"}).ObjToBool()) { JsonSerializerSettings setting = new JsonSerializerSettings(); JsonConvert.DefaultSettings = new Func(() => @@ -143,7 +137,9 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) if (!await myContext.Db.Queryable().AnyAsync()) { - myContext.GetEntityDB().InsertRange(JsonHelper.ParseFormByJson>(FileHelper.ReadFile(string.Format(SeedDataFolder, "BlogArticle"), Encoding.UTF8))); + myContext.GetEntityDB().InsertRange( + JsonHelper.ParseFormByJson>( + FileHelper.ReadFile(string.Format(SeedDataFolder, "BlogArticle"), Encoding.UTF8))); Console.WriteLine("Table:BlogArticle created success!"); } else @@ -158,7 +154,8 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) if (!await myContext.Db.Queryable().AnyAsync()) { - var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "Modules"), Encoding.UTF8), setting); + var data = JsonConvert.DeserializeObject>( + FileHelper.ReadFile(string.Format(SeedDataFolder, "Modules"), Encoding.UTF8), setting); myContext.GetEntityDB().InsertRange(data); Console.WriteLine("Table:Modules created success!"); @@ -175,7 +172,8 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) if (!await myContext.Db.Queryable().AnyAsync()) { - var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "Permission"), Encoding.UTF8), setting); + var data = JsonConvert.DeserializeObject>( + FileHelper.ReadFile(string.Format(SeedDataFolder, "Permission"), Encoding.UTF8), setting); myContext.GetEntityDB().InsertRange(data); Console.WriteLine("Table:Permission created success!"); @@ -192,7 +190,8 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) if (!await myContext.Db.Queryable().AnyAsync()) { - var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "Role"), Encoding.UTF8), setting); + var data = JsonConvert.DeserializeObject>( + FileHelper.ReadFile(string.Format(SeedDataFolder, "Role"), Encoding.UTF8), setting); //using var stream = new FileStream(Path.Combine(WebRootPath, "BlogCore.Data.excel", "Role.xlsx"), FileMode.Open); //var result = await importer.Import(stream); //var data = result.Data.ToList(); @@ -212,7 +211,9 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) if (!await myContext.Db.Queryable().AnyAsync()) { - var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "RoleModulePermission"), Encoding.UTF8), setting); + var data = JsonConvert.DeserializeObject>( + FileHelper.ReadFile(string.Format(SeedDataFolder, "RoleModulePermission"), Encoding.UTF8), + setting); myContext.GetEntityDB().InsertRange(data); Console.WriteLine("Table:RoleModulePermission created success!"); @@ -229,7 +230,8 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) if (!await myContext.Db.Queryable().AnyAsync()) { - var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "Topic"), Encoding.UTF8), setting); + var data = JsonConvert.DeserializeObject>( + FileHelper.ReadFile(string.Format(SeedDataFolder, "Topic"), Encoding.UTF8), setting); myContext.GetEntityDB().InsertRange(data); Console.WriteLine("Table:Topic created success!"); @@ -246,7 +248,8 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) if (!await myContext.Db.Queryable().AnyAsync()) { - var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "TopicDetail"), Encoding.UTF8), setting); + var data = JsonConvert.DeserializeObject>( + FileHelper.ReadFile(string.Format(SeedDataFolder, "TopicDetail"), Encoding.UTF8), setting); myContext.GetEntityDB().InsertRange(data); Console.WriteLine("Table:TopicDetail created success!"); @@ -263,7 +266,8 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) if (!await myContext.Db.Queryable().AnyAsync()) { - var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "UserRole"), Encoding.UTF8), setting); + var data = JsonConvert.DeserializeObject>( + FileHelper.ReadFile(string.Format(SeedDataFolder, "UserRole"), Encoding.UTF8), setting); myContext.GetEntityDB().InsertRange(data); Console.WriteLine("Table:UserRole created success!"); @@ -280,7 +284,8 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) if (!await myContext.Db.Queryable().AnyAsync()) { - var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "sysUserInfo"), Encoding.UTF8), setting); + var data = JsonConvert.DeserializeObject>( + FileHelper.ReadFile(string.Format(SeedDataFolder, "sysUserInfo"), Encoding.UTF8), setting); myContext.GetEntityDB().InsertRange(data); Console.WriteLine("Table:sysUserInfo created success!"); @@ -297,7 +302,8 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) if (!await myContext.Db.Queryable().AnyAsync()) { - var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "TasksQz"), Encoding.UTF8), setting); + var data = JsonConvert.DeserializeObject>( + FileHelper.ReadFile(string.Format(SeedDataFolder, "TasksQz"), Encoding.UTF8), setting); myContext.GetEntityDB().InsertRange(data); Console.WriteLine("Table:TasksQz created success!"); @@ -326,7 +332,8 @@ public static async Task SeedAsync(MyContext myContext, string WebRootPath) if (!await myContext.Db.Queryable().AnyAsync()) { - var data = JsonConvert.DeserializeObject>(FileHelper.ReadFile(string.Format(SeedDataFolder, "Department"), Encoding.UTF8), setting); + var data = JsonConvert.DeserializeObject>( + FileHelper.ReadFile(string.Format(SeedDataFolder, "Department"), Encoding.UTF8), setting); myContext.GetEntityDB().InsertRange(data); Console.WriteLine("Table:Department created success!"); @@ -367,7 +374,8 @@ private static async Task SeedDataAsync(ISqlSugarClient db) .Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass) .Where(u => { - var esd = u.GetInterfaces().FirstOrDefault(i => i.HasImplementedRawGeneric(typeof(IEntitySeedData<>))); + var esd = u.GetInterfaces() + .FirstOrDefault(i => i.HasImplementedRawGeneric(typeof(IEntitySeedData<>))); if (esd is null) { return false; @@ -441,11 +449,13 @@ public static void MigrationLogs(MyContext myContext) logDb.DbMaintenance.CreateDatabase(); ConsoleHelper.WriteSuccessLine($"Log Database created successfully!"); var path = AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory; - var referencedAssemblies = System.IO.Directory.GetFiles(path, "Blog.Core.Model.dll").Select(Assembly.LoadFrom).ToArray(); + var referencedAssemblies = System.IO.Directory.GetFiles(path, "Blog.Core.Model.dll") + .Select(Assembly.LoadFrom).ToArray(); var modelTypes = referencedAssemblies .SelectMany(a => a.DefinedTypes) .Select(type => type.AsType()) - .Where(x => x.IsClass && x.Namespace != null && x.Namespace.StartsWith("Blog.Core.Model.Logs")).ToList(); + .Where(x => x.IsClass && x.Namespace != null && x.Namespace.StartsWith("Blog.Core.Model.Logs")) + .ToList(); Stopwatch sw = Stopwatch.StartNew(); var tables = logDb.DbMaintenance.GetTableInfoList(); @@ -482,7 +492,8 @@ public static void MigrationLogs(MyContext myContext) /// public static async Task TenantSeedAsync(MyContext myContext) { - var tenants = await myContext.Db.Queryable().Where(s => s.TenantType == TenantTypeEnum.Db).ToListAsync(); + var tenants = await myContext.Db.Queryable().Where(s => s.TenantType == TenantTypeEnum.Db) + .ToListAsync(); if (tenants.Any()) { Console.WriteLine($@"Init Multi Tenant Db"); @@ -493,7 +504,8 @@ public static async Task TenantSeedAsync(MyContext myContext) } } - tenants = await myContext.Db.Queryable().Where(s => s.TenantType == TenantTypeEnum.Tables).ToListAsync(); + tenants = await myContext.Db.Queryable().Where(s => s.TenantType == TenantTypeEnum.Tables) + .ToListAsync(); if (tenants.Any()) { await InitTenantSeedAsync(myContext, tenants); @@ -526,7 +538,8 @@ private static async Task InitTenantSeedAsync(MyContext myContext, List !u.IsInterface && !u.IsAbstract && u.IsClass) .Where(u => { - var esd = u.GetInterfaces().FirstOrDefault(i => i.HasImplementedRawGeneric(typeof(IEntitySeedData<>))); + var esd = u.GetInterfaces() + .FirstOrDefault(i => i.HasImplementedRawGeneric(typeof(IEntitySeedData<>))); if (esd is null) { return false; diff --git a/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs b/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs index 680a1912..e5e9f339 100644 --- a/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Text; +using Blog.Core.Common.DB; namespace Blog.Core.Extensions { @@ -189,18 +190,8 @@ public static void AddAppConfigSetup(this IServiceCollection services, IHostEnvi ConsoleHelper.WriteSuccessLine($"EventBus: True"); } - // 多库 - if (!AppSettings.app(new string[] { "MutiDBEnabled" }).ObjToBool()) - { - Console.WriteLine($"Is multi-DataBase: False"); - } - else - { - ConsoleHelper.WriteSuccessLine($"Is multi-DataBase: True"); - } - // 读写分离 - if (!AppSettings.app(new string[] { "CQRSEnabled" }).ObjToBool()) + if (!BaseDBConfig.MainConfig.SlaveConnectionConfigs.AnyNoException()) { Console.WriteLine($"Is CQRS: False"); } @@ -235,8 +226,7 @@ public static void AddAppTableConfigSetup(this IServiceCollection services, IHos new string[] { "RabbitMQ消息列队", AppSettings.app("RabbitMQ", "Enabled") }, new string[] { "事件总线(必须开启消息列队)", AppSettings.app("EventBus", "Enabled") }, new string[] { "redis消息队列", AppSettings.app("Startup", "RedisMq", "Enabled") }, - new string[] { "是否多库", AppSettings.app("MutiDBEnabled") }, - new string[] { "读写分离", AppSettings.app("CQRSEnabled") }, + new string[] { "读写分离", BaseDBConfig.MainConfig.SlaveConnectionConfigs.AnyNoException()? "True" : "False" }, }; new ConsoleTable() diff --git a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs index 6574e70e..82f74378 100644 --- a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs @@ -9,6 +9,7 @@ using StackExchange.Profiling; using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Blog.Core.Common.Caches; using Blog.Core.Common.Core; @@ -30,16 +31,10 @@ public static void AddSqlsugarSetup(this IServiceCollection services) if (services == null) throw new ArgumentNullException(nameof(services)); // 默认添加主数据库连接 - MainDb.CurrentDbConnId = AppSettings.app(new string[] { "MainDB" }); - - BaseDBConfig.MutiConnectionString.slaveDbs.ForEach(s => + if (!AppSettings.app("MainDB").IsNullOrEmpty()) { - BaseDBConfig.AllSlaveConfigs.Add(new SlaveConnectionConfig() - { - HitRate = s.HitRate, - ConnectionString = s.Connection - }); - }); + MainDb.CurrentDbConnId = AppSettings.app("MainDB"); + } BaseDBConfig.MutiConnectionString.allDbs.ForEach(m => { @@ -47,7 +42,7 @@ public static void AddSqlsugarSetup(this IServiceCollection services) { ConfigId = m.ConnId.ObjToString().ToLower(), ConnectionString = m.Connection, - DbType = (DbType)m.DbType, + DbType = (DbType) m.DbType, IsAutoCloseConnection = true, // Check out more information: https://github.com/anjoy8/Blog.Core/issues/122 //IsShardSameThread = false, @@ -58,7 +53,11 @@ public static void AddSqlsugarSetup(this IServiceCollection services) SqlServerCodeFirstNvarchar = true, }, // 从库 - SlaveConnectionConfigs = BaseDBConfig.AllSlaveConfigs, + SlaveConnectionConfigs = m.Slaves?.Where(s => s.HitRate > 0).Select(s => new SlaveConnectionConfig + { + ConnectionString = s.Connection, + HitRate = s.HitRate + }).ToList(), // 自定义特性 ConfigureExternalServices = new ConfigureExternalServices() { @@ -79,6 +78,16 @@ public static void AddSqlsugarSetup(this IServiceCollection services) } else { + if (string.Equals(SqlSugarConst.LogConfigId, MainDb.CurrentDbConnId, + StringComparison.CurrentCultureIgnoreCase)) + { + BaseDBConfig.MainConfig = config; + } + + //复用连接 + if (m.ConnId.ToLower().StartsWith(SqlSugarConst.LogConfigId.ToLower())) + BaseDBConfig.ReuseConfigs.Add(config); + BaseDBConfig.ValidConfig.Add(config); } @@ -98,12 +107,14 @@ public static void AddSqlsugarSetup(this IServiceCollection services) { BaseDBConfig.ValidConfig.ForEach(config => { - var dbProvider = db.GetConnectionScope((string)config.ConfigId); + var dbProvider = db.GetConnectionScope((string) config.ConfigId); // 打印SQL语句 dbProvider.Aop.OnLogExecuting = (s, parameters) => { - SqlSugarAop.OnLogExecuting(dbProvider, App.User?.Name.ObjToString(), ExtractTableName(s), Enum.GetName(typeof(SugarActionType), dbProvider.SugarActionType), s, parameters, config); + SqlSugarAop.OnLogExecuting(dbProvider, App.User?.Name.ObjToString(), ExtractTableName(s), + Enum.GetName(typeof(SugarActionType), dbProvider.SugarActionType), s, parameters, + config); }; // 数据审计 @@ -114,6 +125,8 @@ public static void AddSqlsugarSetup(this IServiceCollection services) // 配置实体数据权限 RepositorySetting.SetTenantEntityFilter(dbProvider); }); + //故障转移,检查主库链接自动切换备用连接 + SqlSugarReuse.AutoChangeAvailableConnect(db); }); }); } diff --git a/Blog.Core.IServices/IDS4Db/IApplicationUserServices.cs b/Blog.Core.IServices/IDS4Db/IApplicationUserServices.cs index c6d784ff..4be6a876 100644 --- a/Blog.Core.IServices/IDS4Db/IApplicationUserServices.cs +++ b/Blog.Core.IServices/IDS4Db/IApplicationUserServices.cs @@ -1,9 +1,11 @@ -using Blog.Core.IServices.BASE; +using System.Threading.Tasks; +using Blog.Core.IServices.BASE; using Blog.Core.Model.IDS4DbModels; namespace Blog.Core.IServices { public partial interface IApplicationUserServices : IBaseServices { + bool IsEnable(); } } \ No newline at end of file diff --git a/Blog.Core.Repository/BASE/BaseRepository.cs b/Blog.Core.Repository/BASE/BaseRepository.cs index 9d926ee4..3a1c1ee8 100644 --- a/Blog.Core.Repository/BASE/BaseRepository.cs +++ b/Blog.Core.Repository/BASE/BaseRepository.cs @@ -27,21 +27,14 @@ private ISqlSugarClient _db { ISqlSugarClient db = _dbBase; - /* 如果要开启多库支持, - * 1、在appsettings.json 中开启MutiDBEnabled节点为true,必填 - * 2、设置一个主连接的数据库ID,节点MainDB,对应的连接字符串的Enabled也必须true,必填 - */ - if (AppSettings.app(new[] { "MutiDBEnabled" }).ObjToBool()) + //修改使用 model备注字段作为切换数据库条件,使用sqlsugar TenantAttribute存放数据库ConnId + //参考 https://www.donet5.com/Home/Doc?typeId=2246 + var tenantAttr = typeof(TEntity).GetCustomAttribute(); + if (tenantAttr != null) { - //修改使用 model备注字段作为切换数据库条件,使用sqlsugar TenantAttribute存放数据库ConnId - //参考 https://www.donet5.com/Home/Doc?typeId=2246 - var tenantAttr = typeof(TEntity).GetCustomAttribute(); - if (tenantAttr != null) - { - //统一处理 configId 小写 - db = _dbBase.GetConnectionScope(tenantAttr.configId.ToString().ToLower()); - return db; - } + //统一处理 configId 小写 + db = _dbBase.GetConnectionScope(tenantAttr.configId.ToString().ToLower()); + return db; } //多租户 diff --git a/Blog.Core.Services/IDS4Db/ApplicationUserServices.cs b/Blog.Core.Services/IDS4Db/ApplicationUserServices.cs index 1cda7d36..bfaf5dfc 100644 --- a/Blog.Core.Services/IDS4Db/ApplicationUserServices.cs +++ b/Blog.Core.Services/IDS4Db/ApplicationUserServices.cs @@ -1,4 +1,7 @@ -using Blog.Core.IRepository.Base; +using System.Threading.Tasks; +using Blog.Core.Common.DB; +using Blog.Core.Common.DB.Extension; +using Blog.Core.IRepository.Base; using Blog.Core.Model.IDS4DbModels; using Blog.Core.Services.BASE; @@ -6,6 +9,10 @@ namespace Blog.Core.IServices { public class ApplicationUserServices : BaseServices, IApplicationUserServices { - + public bool IsEnable() + { + var configId = typeof(ApplicationUser).GetEntityTenant(); + return Db.AsTenant().IsAnyConnection(configId); + } } } \ No newline at end of file diff --git a/Blog.Core.Tests/Blog.Core.Tests.csproj b/Blog.Core.Tests/Blog.Core.Tests.csproj index 2ceef163..5619bea8 100644 --- a/Blog.Core.Tests/Blog.Core.Tests.csproj +++ b/Blog.Core.Tests/Blog.Core.Tests.csproj @@ -11,13 +11,6 @@ - - - Always - PreserveNewest - - - @@ -38,6 +31,14 @@ + + + true + Always + PreserveNewest + + + diff --git a/Blog.Core.Tests/appsettings.json b/Blog.Core.Tests/appsettings.json index 8c0305c7..f91f6df8 100644 --- a/Blog.Core.Tests/appsettings.json +++ b/Blog.Core.Tests/appsettings.json @@ -1,30 +1,26 @@ { "urls": "http://*:9291", //web服务端口,如果用IIS部署,把这个去掉 - "Logging": { - "LogLevel": { - "Default": "Information", //加入Default否则log4net本地写入不了日志 - "Blog.Core.AuthHelper.ApiResponseHandler": "Error" - }, - "Debug": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Warning" - } - }, - "Console": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Warning", - "Microsoft.Hosting.Lifetime": "Debug" + "Serilog": { + "MinimumLevel": { + "Default": "Debug", + "Override": { + "Microsoft": "Information", + "Microsoft.AspNetCore": "Warning", + "System": "Warning", + "System.Net.Http.HttpClient": "Warning", + "Hangfire": "Information", + "Magicodes": "Warning", + "DotNetCore.CAP": "Information", + "Savorboard.CAP": "Information", + "Quartz": "Information" } - }, - "Log4Net": { - "Name": "Blog.Core" } }, "AllowedHosts": "*", "Redis": { - "ConnectionString": "127.0.0.1:6319,password=admin" + "Enable": false, + "ConnectionString": "127.0.0.1:6379", + "InstanceName": "" //前缀 }, "RabbitMQ": { "Enabled": false, @@ -48,24 +44,34 @@ "CachingAOP": { "Enabled": true }, + "LogToDb": true, "LogAOP": { - "Enabled": false + "Enabled": false, + "LogToFile": { + "Enabled": true + }, + "LogToDB": { + "Enabled": true + } }, "TranAOP": { + "Enabled": true + }, + "UserAuditAOP": { "Enabled": false }, "SqlAOP": { "Enabled": true, - "OutToLogFile": { - "Enabled": false + "LogToFile": { + "Enabled": true }, - "OutToConsole": { + "LogToDB": { + "Enabled": true + }, + "LogToConsole": { "Enabled": true } }, - "LogToDb": { - "Enabled": true - }, "Date": "2018-08-28", "SeedDBEnabled": true, //只生成表结构 "SeedDBDataEnabled": true, //生成表,并初始化数据 @@ -74,14 +80,17 @@ "UseLoadTest": false }, - // 请配置MainDB为你想要的主库的ConnId值,并设置对应的Enabled为true; - // *** 单库操作,把 MutiDBEnabled 设为false ***; - // *** 多库操作,把 MutiDBEnabled 设为true,其他的从库Enabled也为true **; - // 具体配置看视频:https://www.bilibili.com/video/BV1BJ411B7mn?p=6 - - "MainDB": "WMBLOG_SQLITE", //当前项目的主库,所对应的连接字符串的Enabled必须为true - "MutiDBEnabled": false, //是否开启多库模式 - "CQRSEnabled": false, //是否开启读写分离模式,必须是单库模式,且数据库类型一致,比如都是SqlServer + //优化DB配置、不会再区分单库多库 + //MainDb:标识当前项目的主库,所对应的连接字符串的Enabled必须为true + //Log:标识日志库,所对应的连接字符串的Enabled必须为true + //从库只需配置Slaves数组,要求数据库类型一致!,比如都是SqlServer + // + //新增,故障转移方案 + //如果主库挂了,会自动切换到备用连接(比如说主库+备用库) + //备用连接的ConnId配置为主库的ConnId+数字即可,比如主库的ConnId为Main,那么备用连接的ConnId为Mian1 + //主库、备用库无需数据库类型一致! + //备用库不会有程序维护,需要手动维护 + "MainDB": "Main", //当前项目的主库,所对应的连接字符串的Enabled必须为true "DBS": [ /* 对应下边的 DBType @@ -94,17 +103,40 @@ Kdbndp = 6,//人大金仓 */ { - "ConnId": "WMBLOG_SQLITE", + "ConnId": "Main", "DBType": 2, "Enabled": true, - "HitRate": 50, // 值越大,优先级越高 - "Connection": "WMBlog.db" //sqlite只写数据库名就行 + "Connection": "WMBlog.db", //sqlite只写数据库名就行 + "Slaves": [ + { + "HitRate": 0,// 值越大,优先级越高 0不使用 + "Connection": "WMBlog2.db" + } + ] + }, + { + "ConnId": "Main2", + "DBType": 2, + "Enabled": true, + "Connection": "WMBlog3.db", //sqlite只写数据库名就行 + "Slaves": [ + { + "HitRate": 0,// 值越大,优先级越高 0不使用 + "Connection": "WMBlog4.db" + } + ] + }, + { + "ConnId": "Log", //日志库连接固定名称,不要改,其他的可以改 + "DBType": 2, + "Enabled": true, + "HitRate": 50, + "Connection": "WMBlogLog.db" //sqlite只写数据库名就行 }, { "ConnId": "WMBLOG_MSSQL_1", "DBType": 1, "Enabled": false, - "HitRate": 40, "Connection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=WMBLOG_MSSQL_1;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", "ProviderName": "System.Data.SqlClient" }, @@ -112,7 +144,6 @@ "ConnId": "WMBLOG_MSSQL_2", "DBType": 1, "Enabled": false, - "HitRate": 30, "Connection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=WMBLOG_MSSQL_2;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False", "ProviderName": "System.Data.SqlClient" }, @@ -120,35 +151,30 @@ "ConnId": "WMBLOG_MYSQL", "DBType": 0, "Enabled": false, - "HitRate": 20, - "Connection": "server=.;Database=ddd;Uid=root;Pwd=123456;Port=10060;Allow User Variables=True;" + "Connection": "server=localhost;Database=blog;Uid=root;Pwd=root;Port=3306;Allow User Variables=True;" }, { "ConnId": "WMBLOG_MYSQL_2", "DBType": 0, - "Enabled": true, - "HitRate": 20, - "Connection": "server=.;Database=blogcore001;Uid=root;Pwd=123456;Port=3096;Allow User Variables=True;" + "Enabled": false, + "Connection": "server=localhost;Database=blogcore001;Uid=root;Pwd=root;Port=3306;Allow User Variables=True;" }, { "ConnId": "WMBLOG_ORACLE", "DBType": 3, "Enabled": false, - "HitRate": 10, "Connection": "Data Source=127.0.0.1/ops;User ID=OPS;Password=123456;Persist Security Info=True;Connection Timeout=60;" }, { "ConnId": "WMBLOG_DM", "DBType": 5, "Enabled": false, - "HitRate": 10, - "Connection": "PORT=5236;DATABASE=DAMENG;HOST=localhost;PASSWORD=SYSDBA;USER ID=SYSDBA;" + "Connection": "Server=xxxxx:5236;User Id=xxxxx;PWD=xxxxx;SCHEMA=TESTDBA;" }, { "ConnId": "WMBLOG_KDBNDP", "DBType": 6, - "Enabled": true, - "HitRate": 10, + "Enabled": false, "Connection": "Server=127.0.0.1;Port=54321;UID=SYSTEM;PWD=system;database=SQLSUGAR4XTEST1;" } ], @@ -163,12 +189,13 @@ "Database": "BlogCoreDb" }, "Startup": { + "Domain": "http://localhost:9291", "Cors": { "PolicyName": "CorsIpAccess", //策略名称 "EnableAllIPs": false, //当为true时,开放所有IP均可访问。 // 支持多个域名端口,注意端口号后不要带/斜杆:比如localhost:8000/,是错的 // 注意,http://127.0.0.1:1818 和 http://localhost:1818 是不一样的 - "IPs": "http://127.0.0.1:2364,http://localhost:2364" + "IPs": "http://127.0.0.1:2364,http://localhost:2364,http://127.0.0.1:6688,http://localhost:6688" }, "AppConfigAlert": { "Enabled": true @@ -179,6 +206,12 @@ "AuthorizationUrl": "http://localhost:5004", // 认证中心域名 "ApiName": "blog.core.api" // 资源服务器 }, + "Authing": { + "Enabled": false, + "Issuer": "https://uldr24esx31h-demo.authing.cn/oidc", + "Audience": "63d51c4205c2849803be5178", + "JwksUri": "https://uldr24esx31h-demo.authing.cn/oidc/.well-known/jwks.json" + }, "RedisMq": { "Enabled": false //redis 消息队列 }, @@ -191,17 +224,38 @@ }, "Middleware": { "RequestResponseLog": { - "Enabled": false + "Enabled": true, + "LogToFile": { + "Enabled": true + }, + "LogToDB": { + "Enabled": true + } }, "IPLog": { - "Enabled": true + "Enabled": true, + "LogToFile": { + "Enabled": true + }, + "LogToDB": { + "Enabled": true + } }, "RecordAccessLogs": { "Enabled": true, + "LogToFile": { + "Enabled": true + }, + "LogToDB": { + "Enabled": true + }, "IgnoreApis": "/api/permission/getnavigationbar,/api/monitor/getids4users,/api/monitor/getaccesslogs,/api/monitor/server,/api/monitor/getactiveusers,/api/monitor/server," }, "SignalR": { - "Enabled": false + "Enabled": true + }, + "SignalRSendLog": { + "Enabled": true }, "QuartzNetJob": { "Enabled": true @@ -285,5 +339,10 @@ "FiedValue": "Blog.Core.Api" } ] + }, + "Seq": { + "Enabled": true, + "Address": "http://localhost:5341/", + "ApiKey": "" } -} +} \ No newline at end of file From f87b33a4a80ecc817eba410f79e52c563bcb7fc2 Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Wed, 25 Oct 2023 11:20:51 +0800 Subject: [PATCH 81/84] =?UTF-8?q?=F0=9F=90=9B=20=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs index 82f74378..eb012394 100644 --- a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs @@ -78,14 +78,14 @@ public static void AddSqlsugarSetup(this IServiceCollection services) } else { - if (string.Equals(SqlSugarConst.LogConfigId, MainDb.CurrentDbConnId, + if (string.Equals(config.ConfigId, MainDb.CurrentDbConnId, StringComparison.CurrentCultureIgnoreCase)) { BaseDBConfig.MainConfig = config; } //复用连接 - if (m.ConnId.ToLower().StartsWith(SqlSugarConst.LogConfigId.ToLower())) + if (m.ConnId.ToLower().StartsWith(MainDb.CurrentDbConnId.ToLower())) BaseDBConfig.ReuseConfigs.Add(config); BaseDBConfig.ValidConfig.Add(config); From f6cee97f899852f34008424b4c50d7b762d62d55 Mon Sep 17 00:00:00 2001 From: ansonzhang <3143422472@qq.com> Date: Fri, 27 Oct 2023 15:25:37 +0800 Subject: [PATCH 82/84] Update sysUserInfo.cs --- Blog.Core.Model/Models/sysUserInfo.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Blog.Core.Model/Models/sysUserInfo.cs b/Blog.Core.Model/Models/sysUserInfo.cs index 574ea311..1137d91c 100644 --- a/Blog.Core.Model/Models/sysUserInfo.cs +++ b/Blog.Core.Model/Models/sysUserInfo.cs @@ -61,7 +61,7 @@ public SysUserInfo(string loginName, string loginPWD) /// 部门 /// [SugarColumn(IsNullable = true)] - public int DepartmentId { get; set; } = -1; + public long DepartmentId { get; set; } = -1; /// /// 备注 @@ -141,4 +141,4 @@ public SysUserInfo(string loginName, string loginPWD) [SugarColumn(IsIgnore = true)] public string DepartmentName { get; set; } } -} \ No newline at end of file +} From eed5d5cfab71d7baf4f270ee52cfb007954e315c Mon Sep 17 00:00:00 2001 From: ansonzhang <3143422472@qq.com> Date: Fri, 27 Oct 2023 15:26:17 +0800 Subject: [PATCH 83/84] Update sysUserInfo.cs --- Blog.Core.Model/Models/sysUserInfo.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Blog.Core.Model/Models/sysUserInfo.cs b/Blog.Core.Model/Models/sysUserInfo.cs index 574ea311..1137d91c 100644 --- a/Blog.Core.Model/Models/sysUserInfo.cs +++ b/Blog.Core.Model/Models/sysUserInfo.cs @@ -61,7 +61,7 @@ public SysUserInfo(string loginName, string loginPWD) /// 部门 /// [SugarColumn(IsNullable = true)] - public int DepartmentId { get; set; } = -1; + public long DepartmentId { get; set; } = -1; /// /// 备注 @@ -141,4 +141,4 @@ public SysUserInfo(string loginName, string loginPWD) [SugarColumn(IsIgnore = true)] public string DepartmentName { get; set; } } -} \ No newline at end of file +} From 1b7d13aa6db48929c413244d2c555be36ef15ee2 Mon Sep 17 00:00:00 2001 From: hudingwen <765472804@qq.com> Date: Sat, 28 Oct 2023 18:21:49 +0800 Subject: [PATCH 84/84] =?UTF-8?q?xml=E5=BA=8F=E5=88=97=E5=8C=96=E5=86=85?= =?UTF-8?q?=E5=AD=98=E6=B3=84=E6=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Common/Helper/XmlHelper.cs | 52 +++++++++++++++++----------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/Blog.Core.Common/Helper/XmlHelper.cs b/Blog.Core.Common/Helper/XmlHelper.cs index 510c84a1..f81d6817 100644 --- a/Blog.Core.Common/Helper/XmlHelper.cs +++ b/Blog.Core.Common/Helper/XmlHelper.cs @@ -1,33 +1,40 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Xml.Serialization; namespace Blog.Core.Common.Helper { + /// + /// xml序列化帮助类 + /// public class XmlHelper { + /// + /// 存储序列类型,防止内存泄漏 + /// + private static ConcurrentDictionary hasTypes = new ConcurrentDictionary(); /// /// 转换对象为JSON格式数据 /// /// /// 对象 /// 字符格式的JSON数据 - public static string GetXML(object obj) + public static string GetXML(object obj, string rootName = "root") { - try + XmlSerializer xs; + var xsType = typeof(T); + hasTypes.TryGetValue(xsType, out xs); + if(xs == null) { - XmlSerializer xs = new XmlSerializer(typeof(T)); - - using (TextWriter tw = new StringWriter()) - { - xs.Serialize(tw, obj); - return tw.ToString(); - } + xs = new XmlSerializer(typeof(T)); + hasTypes.TryAdd(xsType, xs); } - catch (Exception) + using (TextWriter tw = new StringWriter()) { - return string.Empty; + xs.Serialize(tw, obj); + return tw.ObjToString(); } } @@ -37,15 +44,20 @@ public static string GetXML(object obj) /// /// /// - public static T ParseFormByXml(string xml,string rootName="root") + public static T ParseFormByXml(string xml, string rootName = "root") { - XmlSerializer serializer = new XmlSerializer(typeof(T), new XmlRootAttribute(rootName)); - StringReader reader = new StringReader(xml); - - T res = (T)serializer.Deserialize(reader); - reader.Close(); - reader.Dispose(); - return res; - } + XmlSerializer xs; + var xsType = typeof(T); + hasTypes.TryGetValue(xsType, out xs); + if (xs == null) + { + xs = new XmlSerializer(xsType, new XmlRootAttribute(rootName)); + hasTypes.TryAdd(xsType, xs); + } + using (StringReader reader = new StringReader(xml)) + { + return (T)xs.Deserialize(reader); + } + } } }