From ff03c4a462459b4523cae744b5c3a58004486355 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Tue, 17 Jan 2023 17:00:31 +0800 Subject: [PATCH 001/155] feat:update to net 7.0 --- Blog.Core.Api/Blog.Core.Api.csproj | 14 ++++----- Blog.Core.Api/Program.cs | 12 ++------ Blog.Core.Common/Blog.Core.Common.csproj | 20 ++++++------- Blog.Core.Common/Helper/SM/SM4.cs | 1 - Blog.Core.EventBus/Blog.Core.EventBus.csproj | 10 +++---- .../Blog.Core.Extensions.csproj | 30 +++++++++---------- .../Middlewares/RequRespLogMiddleware.cs | 1 - .../ServiceExtensions/HttpRuntimeCache.cs | 3 +- .../Blog.Core.FrameWork.csproj | 2 +- Blog.Core.Gateway/Blog.Core.Gateway.csproj | 10 +++---- .../Blog.Core.IServices.csproj | 2 +- Blog.Core.Model/Blog.Core.Model.csproj | 8 ++--- Blog.Core.Publish.Docker.Jenkins.sh | 2 +- Blog.Core.Publish.bat | 4 +-- .../Blog.Core.Repository.csproj | 8 ++--- .../Blog.Core.Serilog.Es.csproj | 14 ++++----- Blog.Core.Services/Blog.Core.Services.csproj | 2 +- Blog.Core.Tasks/Blog.Core.Tasks.csproj | 4 +-- Blog.Core.Tests/Blog.Core.Tests.csproj | 6 ++-- .../Ocelot.Provider.Nacos.csproj | 12 ++++---- 20 files changed, 78 insertions(+), 87 deletions(-) diff --git a/Blog.Core.Api/Blog.Core.Api.csproj b/Blog.Core.Api/Blog.Core.Api.csproj index 38575a55..9016c427 100644 --- a/Blog.Core.Api/Blog.Core.Api.csproj +++ b/Blog.Core.Api/Blog.Core.Api.csproj @@ -4,7 +4,7 @@ Exe - net6.0 + net7.0 enable Linux @@ -49,13 +49,13 @@ - - + + - - - - + + + + diff --git a/Blog.Core.Api/Program.cs b/Blog.Core.Api/Program.cs index b3bf2387..6f4cf041 100644 --- a/Blog.Core.Api/Program.cs +++ b/Blog.Core.Api/Program.cs @@ -13,7 +13,6 @@ using Blog.Core.Hubs; using Blog.Core.IServices; using Blog.Core.Tasks; -using FluentValidation.AspNetCore; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Server.Kestrel.Core; @@ -170,15 +169,8 @@ app.UseMiniProfilerMiddleware(); //app.UseExceptionHandlerMidd(); -app.UseEndpoints(endpoints => -{ - endpoints.MapControllerRoute( - name: "default", - pattern: "{controller=Home}/{action=Index}/{id?}"); - - endpoints.MapHub("/api2/chatHub"); -}); - +app.MapControllers(); +app.MapHub("/api2/chatHub"); var scope = app.Services.GetRequiredService().CreateScope(); var myContext = scope.ServiceProvider.GetRequiredService(); diff --git a/Blog.Core.Common/Blog.Core.Common.csproj b/Blog.Core.Common/Blog.Core.Common.csproj index 43f65ab9..b3d20137 100644 --- a/Blog.Core.Common/Blog.Core.Common.csproj +++ b/Blog.Core.Common/Blog.Core.Common.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 @@ -16,21 +16,21 @@ - - + + - - + + - + - - + + - - + + diff --git a/Blog.Core.Common/Helper/SM/SM4.cs b/Blog.Core.Common/Helper/SM/SM4.cs index 4b1f1996..e0984558 100644 --- a/Blog.Core.Common/Helper/SM/SM4.cs +++ b/Blog.Core.Common/Helper/SM/SM4.cs @@ -207,7 +207,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.EventBus/Blog.Core.EventBus.csproj b/Blog.Core.EventBus/Blog.Core.EventBus.csproj index 070c0a48..bb75b1b0 100644 --- a/Blog.Core.EventBus/Blog.Core.EventBus.csproj +++ b/Blog.Core.EventBus/Blog.Core.EventBus.csproj @@ -1,20 +1,20 @@ - net6.0 + net7.0 - + - + - + - + diff --git a/Blog.Core.Extensions/Blog.Core.Extensions.csproj b/Blog.Core.Extensions/Blog.Core.Extensions.csproj index aca15b55..3323fd2b 100644 --- a/Blog.Core.Extensions/Blog.Core.Extensions.csproj +++ b/Blog.Core.Extensions/Blog.Core.Extensions.csproj @@ -1,35 +1,35 @@  - net6.0 + net7.0 - + - - - - - + + + + + - - - - + + + + - - - + + + - + 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/HttpRuntimeCache.cs b/Blog.Core.Extensions/ServiceExtensions/HttpRuntimeCache.cs index b9b4d7b3..ac7713f6 100644 --- a/Blog.Core.Extensions/ServiceExtensions/HttpRuntimeCache.cs +++ b/Blog.Core.Extensions/ServiceExtensions/HttpRuntimeCache.cs @@ -38,7 +38,8 @@ public V Get(string key) public IEnumerable GetAllKey() { const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; - var entries = _memoryCache.GetType().GetField("_entries", flags).GetValue(_memoryCache); + var coherentState = _memoryCache.GetType().GetField("_coherentState", flags).GetValue(_memoryCache); + var entries = coherentState.GetType().GetField("_entries", flags).GetValue(coherentState); var cacheItems = entries as IDictionary; var keys = new List(); if (cacheItems == null) return keys; diff --git a/Blog.Core.FrameWork/Blog.Core.FrameWork.csproj b/Blog.Core.FrameWork/Blog.Core.FrameWork.csproj index b167521c..4f60a46d 100644 --- a/Blog.Core.FrameWork/Blog.Core.FrameWork.csproj +++ b/Blog.Core.FrameWork/Blog.Core.FrameWork.csproj @@ -1,7 +1,7 @@ - net6.0 + net7.0 diff --git a/Blog.Core.Gateway/Blog.Core.Gateway.csproj b/Blog.Core.Gateway/Blog.Core.Gateway.csproj index 47398520..819eaa36 100644 --- a/Blog.Core.Gateway/Blog.Core.Gateway.csproj +++ b/Blog.Core.Gateway/Blog.Core.Gateway.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 @@ -15,13 +15,13 @@ - - - + + + - + diff --git a/Blog.Core.IServices/Blog.Core.IServices.csproj b/Blog.Core.IServices/Blog.Core.IServices.csproj index 820e1511..8ccfcc2f 100644 --- a/Blog.Core.IServices/Blog.Core.IServices.csproj +++ b/Blog.Core.IServices/Blog.Core.IServices.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 diff --git a/Blog.Core.Model/Blog.Core.Model.csproj b/Blog.Core.Model/Blog.Core.Model.csproj index de7e6467..120f3222 100644 --- a/Blog.Core.Model/Blog.Core.Model.csproj +++ b/Blog.Core.Model/Blog.Core.Model.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 @@ -16,9 +16,9 @@ - - - + + + diff --git a/Blog.Core.Publish.Docker.Jenkins.sh b/Blog.Core.Publish.Docker.Jenkins.sh index 3c561c67..d82c2d2f 100644 --- a/Blog.Core.Publish.Docker.Jenkins.sh +++ b/Blog.Core.Publish.Docker.Jenkins.sh @@ -4,7 +4,7 @@ cd Blog.Core.Api dotnet publish echo "Successfully!!!! ^ please see the file ." -cd bin/Debug/net6.0/publish/ +cd bin/Debug/net7.0/publish/ #rm -f appsettings.json #\cp -rf /var/jenkins_home/workspace/SecurityConfig/Blog.Core/appsettings.json appsettings.json diff --git a/Blog.Core.Publish.bat b/Blog.Core.Publish.bat index f022508d..cebbbf58 100644 --- a/Blog.Core.Publish.bat +++ b/Blog.Core.Publish.bat @@ -8,11 +8,11 @@ dotnet build cd Blog.Core.Api -dotnet publish -o ..\Blog.Core.Api\bin\Debug\net6.0\ +dotnet publish -o ..\Blog.Core.Api\bin\Debug\net7.0\ md ..\.PublishFiles -xcopy ..\Blog.Core.Api\bin\Debug\net6.0\*.* ..\.PublishFiles\ /s /e +xcopy ..\Blog.Core.Api\bin\Debug\net7.0\*.* ..\.PublishFiles\ /s /e echo "Successfully!!!! ^ please see the file .PublishFiles" diff --git a/Blog.Core.Repository/Blog.Core.Repository.csproj b/Blog.Core.Repository/Blog.Core.Repository.csproj index 5c9764b0..3547007f 100644 --- a/Blog.Core.Repository/Blog.Core.Repository.csproj +++ b/Blog.Core.Repository/Blog.Core.Repository.csproj @@ -1,14 +1,14 @@  - net6.0 + net7.0 - - - + + + diff --git a/Blog.Core.Serilog.Es/Blog.Core.Serilog.Es.csproj b/Blog.Core.Serilog.Es/Blog.Core.Serilog.Es.csproj index 5cf82020..d98cfafb 100644 --- a/Blog.Core.Serilog.Es/Blog.Core.Serilog.Es.csproj +++ b/Blog.Core.Serilog.Es/Blog.Core.Serilog.Es.csproj @@ -1,17 +1,17 @@ - net6.0 + net7.0 - - - - - - + + + + + + diff --git a/Blog.Core.Services/Blog.Core.Services.csproj b/Blog.Core.Services/Blog.Core.Services.csproj index ae5e969a..fbfa7bbc 100644 --- a/Blog.Core.Services/Blog.Core.Services.csproj +++ b/Blog.Core.Services/Blog.Core.Services.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 diff --git a/Blog.Core.Tasks/Blog.Core.Tasks.csproj b/Blog.Core.Tasks/Blog.Core.Tasks.csproj index 5e173559..968fb795 100644 --- a/Blog.Core.Tasks/Blog.Core.Tasks.csproj +++ b/Blog.Core.Tasks/Blog.Core.Tasks.csproj @@ -1,11 +1,11 @@  - net6.0 + net7.0 - + diff --git a/Blog.Core.Tests/Blog.Core.Tests.csproj b/Blog.Core.Tests/Blog.Core.Tests.csproj index 2ceef163..fd488226 100644 --- a/Blog.Core.Tests/Blog.Core.Tests.csproj +++ b/Blog.Core.Tests/Blog.Core.Tests.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 false false @@ -19,8 +19,8 @@ - - + + all diff --git a/Ocelot.Provider.Nacos/Ocelot.Provider.Nacos.csproj b/Ocelot.Provider.Nacos/Ocelot.Provider.Nacos.csproj index c713416a..23fd3e3d 100644 --- a/Ocelot.Provider.Nacos/Ocelot.Provider.Nacos.csproj +++ b/Ocelot.Provider.Nacos/Ocelot.Provider.Nacos.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 softlgl softlgl https://github.com/softlgl/Ocelot.Provider.Nacos @@ -11,10 +11,10 @@ - - - - - + + + + + From 8d1c0bde1480da0ffe1b1565990bb18c679b86ad Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Tue, 17 Jan 2023 19:04:36 +0800 Subject: [PATCH 002/155] Update Dockerfile --- Dockerfile | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index 95bdaf92..69a3d132 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,28 +5,32 @@ #如果你想先手动dotnet build成可执行的二进制文件,然后再构建镜像,请看.Api层下的dockerfile。 -FROM mcr.microsoft.com/dotnet/aspnet:6.0-bullseye-slim AS base +#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base WORKDIR /app EXPOSE 80 -FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim AS build +FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build WORKDIR /src COPY ["Blog.Core.Api/Blog.Core.Api.csproj", "Blog.Core.Api/"] COPY ["Blog.Core.Extensions/Blog.Core.Extensions.csproj", "Blog.Core.Extensions/"] -COPY ["Blog.Core.Tasks/Blog.Core.Tasks.csproj", "Blog.Core.Tasks/"] -COPY ["Blog.Core.IServices/Blog.Core.IServices.csproj", "Blog.Core.IServices/"] -COPY ["Blog.Core.Model/Blog.Core.Model.csproj", "Blog.Core.Model/"] +COPY ["Blog.Core.EventBus/Blog.Core.EventBus.csproj", "Blog.Core.EventBus/"] COPY ["Blog.Core.Common/Blog.Core.Common.csproj", "Blog.Core.Common/"] +COPY ["Blog.Core.Model/Blog.Core.Model.csproj", "Blog.Core.Model/"] +COPY ["Blog.Core.Serilog.Es/Blog.Core.Serilog.Es.csproj", "Blog.Core.Serilog.Es/"] +COPY ["Ocelot.Provider.Nacos/Ocelot.Provider.Nacos.csproj", "Ocelot.Provider.Nacos/"] COPY ["Blog.Core.Services/Blog.Core.Services.csproj", "Blog.Core.Services/"] +COPY ["Blog.Core.IServices/Blog.Core.IServices.csproj", "Blog.Core.IServices/"] COPY ["Blog.Core.Repository/Blog.Core.Repository.csproj", "Blog.Core.Repository/"] -COPY ["Blog.Core.EventBus/Blog.Core.EventBus.csproj", "Blog.Core.EventBus/"] +COPY ["Blog.Core.Tasks/Blog.Core.Tasks.csproj", "Blog.Core.Tasks/"] RUN dotnet restore "Blog.Core.Api/Blog.Core.Api.csproj" COPY . . WORKDIR "/src/Blog.Core.Api" RUN dotnet build "Blog.Core.Api.csproj" -c Release -o /app/build FROM build AS publish -RUN dotnet publish "Blog.Core.Api.csproj" -c Release -o /app/publish +RUN dotnet publish "Blog.Core.Api.csproj" -c Release -o /app/publish /p:UseAppHost=false FROM base AS final WORKDIR /app From 2dd3ed1e02bbe34c07e714780f4045a4dc37d8bb Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Tue, 17 Jan 2023 19:06:07 +0800 Subject: [PATCH 003/155] Update dotnetcore.yml --- .github/workflows/dotnetcore.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml index faa57fc4..2dd1ed9c 100644 --- a/.github/workflows/dotnetcore.yml +++ b/.github/workflows/dotnetcore.yml @@ -12,7 +12,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v1 with: - dotnet-version: 6.0.x + dotnet-version: 7.0.x - name: Build with dotnet run: dotnet build --configuration Release - name: Build image From d0e4ff7757aed635dd240426cc3160e5a68a65f5 Mon Sep 17 00:00:00 2001 From: Geralt_Zhang <40553940+HuiJiOnGit@users.noreply.github.com> Date: Thu, 2 Feb 2023 20:42:46 +0800 Subject: [PATCH 004/155] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=85=AC=E5=85=B1?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E5=B1=9E=E6=80=A7=E9=85=8D=E7=BD=AE=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将`TargetFramework`属性迁移到`common.targets`作为方便管理 --- Blog.Core.Api/Blog.Core.Api.csproj | 4 +--- Blog.Core.Common/Blog.Core.Common.csproj | 4 +--- Blog.Core.EventBus/Blog.Core.EventBus.csproj | 4 +--- Blog.Core.Extensions/Blog.Core.Extensions.csproj | 4 +--- Blog.Core.FrameWork/Blog.Core.FrameWork.csproj | 4 +--- Blog.Core.Gateway/Blog.Core.Gateway.csproj | 4 +--- Blog.Core.IServices/Blog.Core.IServices.csproj | 4 +--- Blog.Core.Model/Blog.Core.Model.csproj | 4 +--- Blog.Core.Repository/Blog.Core.Repository.csproj | 4 +--- Blog.Core.Serilog.Es/Blog.Core.Serilog.Es.csproj | 4 +--- Blog.Core.Services/Blog.Core.Services.csproj | 4 +--- Blog.Core.Tasks/Blog.Core.Tasks.csproj | 4 +--- Blog.Core.Tests/Blog.Core.Tests.csproj | 3 +-- Ocelot.Provider.Nacos/Ocelot.Provider.Nacos.csproj | 4 ++-- build/common.targets | 5 +++++ 15 files changed, 20 insertions(+), 40 deletions(-) create mode 100644 build/common.targets diff --git a/Blog.Core.Api/Blog.Core.Api.csproj b/Blog.Core.Api/Blog.Core.Api.csproj index 9016c427..fcaef62a 100644 --- a/Blog.Core.Api/Blog.Core.Api.csproj +++ b/Blog.Core.Api/Blog.Core.Api.csproj @@ -1,10 +1,8 @@  + - Exe - - net7.0 enable Linux diff --git a/Blog.Core.Common/Blog.Core.Common.csproj b/Blog.Core.Common/Blog.Core.Common.csproj index b3d20137..30816b97 100644 --- a/Blog.Core.Common/Blog.Core.Common.csproj +++ b/Blog.Core.Common/Blog.Core.Common.csproj @@ -1,8 +1,6 @@  - - net7.0 - + diff --git a/Blog.Core.EventBus/Blog.Core.EventBus.csproj b/Blog.Core.EventBus/Blog.Core.EventBus.csproj index bb75b1b0..8c50f8e2 100644 --- a/Blog.Core.EventBus/Blog.Core.EventBus.csproj +++ b/Blog.Core.EventBus/Blog.Core.EventBus.csproj @@ -1,8 +1,6 @@ - - net7.0 - + diff --git a/Blog.Core.Extensions/Blog.Core.Extensions.csproj b/Blog.Core.Extensions/Blog.Core.Extensions.csproj index 3323fd2b..437841b7 100644 --- a/Blog.Core.Extensions/Blog.Core.Extensions.csproj +++ b/Blog.Core.Extensions/Blog.Core.Extensions.csproj @@ -1,8 +1,6 @@  - - net7.0 - + diff --git a/Blog.Core.FrameWork/Blog.Core.FrameWork.csproj b/Blog.Core.FrameWork/Blog.Core.FrameWork.csproj index 4f60a46d..51022b3c 100644 --- a/Blog.Core.FrameWork/Blog.Core.FrameWork.csproj +++ b/Blog.Core.FrameWork/Blog.Core.FrameWork.csproj @@ -1,8 +1,6 @@ - - net7.0 - + diff --git a/Blog.Core.Gateway/Blog.Core.Gateway.csproj b/Blog.Core.Gateway/Blog.Core.Gateway.csproj index 819eaa36..ca3a7f72 100644 --- a/Blog.Core.Gateway/Blog.Core.Gateway.csproj +++ b/Blog.Core.Gateway/Blog.Core.Gateway.csproj @@ -1,8 +1,6 @@  - - net7.0 - + ..\Blog.Core.Gateway\Blog.Core.Gateway.xml diff --git a/Blog.Core.IServices/Blog.Core.IServices.csproj b/Blog.Core.IServices/Blog.Core.IServices.csproj index 8ccfcc2f..20e8e658 100644 --- a/Blog.Core.IServices/Blog.Core.IServices.csproj +++ b/Blog.Core.IServices/Blog.Core.IServices.csproj @@ -1,8 +1,6 @@  - - net7.0 - + diff --git a/Blog.Core.Model/Blog.Core.Model.csproj b/Blog.Core.Model/Blog.Core.Model.csproj index 120f3222..3c2ee04a 100644 --- a/Blog.Core.Model/Blog.Core.Model.csproj +++ b/Blog.Core.Model/Blog.Core.Model.csproj @@ -1,8 +1,6 @@  - - net7.0 - + ..\Blog.Core.Api\Blog.Core.Model.xml diff --git a/Blog.Core.Repository/Blog.Core.Repository.csproj b/Blog.Core.Repository/Blog.Core.Repository.csproj index 3547007f..783014e0 100644 --- a/Blog.Core.Repository/Blog.Core.Repository.csproj +++ b/Blog.Core.Repository/Blog.Core.Repository.csproj @@ -1,8 +1,6 @@  - - net7.0 - + diff --git a/Blog.Core.Serilog.Es/Blog.Core.Serilog.Es.csproj b/Blog.Core.Serilog.Es/Blog.Core.Serilog.Es.csproj index d98cfafb..cd71cf26 100644 --- a/Blog.Core.Serilog.Es/Blog.Core.Serilog.Es.csproj +++ b/Blog.Core.Serilog.Es/Blog.Core.Serilog.Es.csproj @@ -1,8 +1,6 @@ - - net7.0 - + diff --git a/Blog.Core.Services/Blog.Core.Services.csproj b/Blog.Core.Services/Blog.Core.Services.csproj index fbfa7bbc..04437f0c 100644 --- a/Blog.Core.Services/Blog.Core.Services.csproj +++ b/Blog.Core.Services/Blog.Core.Services.csproj @@ -1,8 +1,6 @@  - - net7.0 - + ..\Blog.Core.Api\bin\Debug\ diff --git a/Blog.Core.Tasks/Blog.Core.Tasks.csproj b/Blog.Core.Tasks/Blog.Core.Tasks.csproj index 968fb795..408f379c 100644 --- a/Blog.Core.Tasks/Blog.Core.Tasks.csproj +++ b/Blog.Core.Tasks/Blog.Core.Tasks.csproj @@ -1,8 +1,6 @@  - - net7.0 - + diff --git a/Blog.Core.Tests/Blog.Core.Tests.csproj b/Blog.Core.Tests/Blog.Core.Tests.csproj index fd488226..98eff6fc 100644 --- a/Blog.Core.Tests/Blog.Core.Tests.csproj +++ b/Blog.Core.Tests/Blog.Core.Tests.csproj @@ -1,8 +1,7 @@  + - net7.0 - false false diff --git a/Ocelot.Provider.Nacos/Ocelot.Provider.Nacos.csproj b/Ocelot.Provider.Nacos/Ocelot.Provider.Nacos.csproj index 23fd3e3d..6bdeb841 100644 --- a/Ocelot.Provider.Nacos/Ocelot.Provider.Nacos.csproj +++ b/Ocelot.Provider.Nacos/Ocelot.Provider.Nacos.csproj @@ -1,7 +1,7 @@  - + + - net7.0 softlgl softlgl https://github.com/softlgl/Ocelot.Provider.Nacos diff --git a/build/common.targets b/build/common.targets new file mode 100644 index 00000000..bfd5899f --- /dev/null +++ b/build/common.targets @@ -0,0 +1,5 @@ + + + net7.0 + + \ No newline at end of file From 657e67d164b64996d2ee2ca790f81c3800304b8f Mon Sep 17 00:00:00 2001 From: HuiJiOnGit <40553940+HuiJiOnGit@users.noreply.github.com> Date: Tue, 7 Feb 2023 08:30:39 +0800 Subject: [PATCH 005/155] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=9C=A8docke?= =?UTF-8?q?r=E4=B8=AD=E6=97=A0=E6=B3=95=E8=BF=98=E5=8E=9F=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.sln | 1 + Dockerfile | 1 + 2 files changed, 2 insertions(+) diff --git a/Blog.Core.sln b/Blog.Core.sln index c8f61505..c8989b24 100644 --- a/Blog.Core.sln +++ b/Blog.Core.sln @@ -30,6 +30,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionIt Blog.Core.Publish.Docker.sh = Blog.Core.Publish.Docker.sh Blog.Core.Publish.Linux.sh = Blog.Core.Publish.Linux.sh codecov.yml = codecov.yml + build\common.targets = build\common.targets CreateYourProject.bat = CreateYourProject.bat DockerBuild.bat = DockerBuild.bat Dockerfile = Dockerfile diff --git a/Dockerfile b/Dockerfile index 69a3d132..c80e6896 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,6 +24,7 @@ COPY ["Blog.Core.Services/Blog.Core.Services.csproj", "Blog.Core.Services/"] COPY ["Blog.Core.IServices/Blog.Core.IServices.csproj", "Blog.Core.IServices/"] COPY ["Blog.Core.Repository/Blog.Core.Repository.csproj", "Blog.Core.Repository/"] COPY ["Blog.Core.Tasks/Blog.Core.Tasks.csproj", "Blog.Core.Tasks/"] +COPY ["build", "build/"] RUN dotnet restore "Blog.Core.Api/Blog.Core.Api.csproj" COPY . . WORKDIR "/src/Blog.Core.Api" 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 006/155] =?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 d1288b103ecf582d5491712f63e5dbd8c05048dc Mon Sep 17 00:00:00 2001 From: ansonzhang <3143422472@qq.com> Date: Thu, 23 Mar 2023 16:39:57 +0800 Subject: [PATCH 007/155] 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 008/155] =?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 009/155] 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 010/155] =?UTF-8?q?Sqlsugar=20=E5=88=86=E8=A1=A8=20CRUD=20?= =?UTF-8?q?demo?= 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 011/155] 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 012/155] =?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 013/155] =?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 014/155] =?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 015/155] =?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 016/155] 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 017/155] =?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 018/155] 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 019/155] =?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 020/155] =?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 021/155] =?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 022/155] 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 023/155] 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 024/155] =?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 025/155] =?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 026/155] =?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 027/155] 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 028/155] 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 029/155] =?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 030/155] 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 031/155] 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 032/155] =?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 033/155] 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 034/155] =?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 035/155] 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 036/155] 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 037/155] =?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 038/155] 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 039/155] 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 040/155] 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 041/155] 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 042/155] 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 043/155] 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 044/155] =?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 045/155] =?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 046/155] 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 047/155] =?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 048/155] =?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 049/155] =?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 050/155] 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 051/155] 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 052/155] =?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 053/155] =?UTF-8?q?Update=20index.html=20=E6=9B=B4?= =?UTF-8?q?=E6=94=B9=E6=8E=A5=E5=8F=A3=E5=90=8D=E7=A7=B0=E4=B8=8D=E8=83=BD?= =?UTF-8?q?=E7=9B=B4=E6=8E=A5=E5=A4=8D=E5=88=B6=E7=9A=84=E9=97=AE=E9=A2=98?= =?UTF-8?q?=EF=BC=8C=E5=8E=BB=E9=99=A4=E6=8E=A5=E5=8F=A3border=E9=80=89?= =?UTF-8?q?=E4=B8=AD=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 054/155] =?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 055/155] 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 056/155] =?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 057/155] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20=E5=8D=87=E7=BA=A7?= =?UTF-8?q?SqlSugar=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 058/155] =?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 059/155] =?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 060/155] =?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 061/155] =?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 062/155] =?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 063/155] :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 064/155] =?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 065/155] :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 066/155] 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 067/155] 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 068/155] =?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 069/155] =?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 070/155] 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 071/155] =?UTF-8?q?feat=EF=BC=9Achange=20access=20trend=20?= =?UTF-8?q?log?= 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 072/155] 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 073/155] 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 074/155] 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 075/155] 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 076/155] =?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 077/155] 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 078/155] 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 079/155] 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 080/155] =?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 081/155] =?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 1e9865cfef1adf5db59a3be4980e1bf36cec91a0 Mon Sep 17 00:00:00 2001 From: LemonNoCry Date: Thu, 31 Aug 2023 21:59:39 +0800 Subject: [PATCH 082/155] =?UTF-8?q?=F0=9F=8E=A8=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Test/SqlsugarTestController.cs | 29 +++++++++++++++++++ .../ServiceExtensions/SqlsugarSetup.cs | 1 + 2 files changed, 30 insertions(+) create mode 100644 Blog.Core.Api/Controllers/Test/SqlsugarTestController.cs diff --git a/Blog.Core.Api/Controllers/Test/SqlsugarTestController.cs b/Blog.Core.Api/Controllers/Test/SqlsugarTestController.cs new file mode 100644 index 00000000..774a9b12 --- /dev/null +++ b/Blog.Core.Api/Controllers/Test/SqlsugarTestController.cs @@ -0,0 +1,29 @@ +using Blog.Core.Common; +using Blog.Core.Controllers; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using SqlSugar; + +namespace Blog.Core.Api.Controllers.Test; + +[Route("api/[Controller]/[Action]")] +[AllowAnonymous] +public class SqlsugarTestController : BaseApiController +{ + private readonly SqlSugarScope _db; + + public SqlsugarTestController(SqlSugarScope db) + { + _db = db; + } + + [HttpGet] + public async Task Get() + { + Console.WriteLine(App.HttpContext.Request.Path); + Console.WriteLine(App.HttpContext.RequestServices.ToString()); + Console.WriteLine(App.User?.ID); + await Task.CompletedTask; + return Ok(); + } +} \ No newline at end of file diff --git a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs index 6574e70e..6e8a148c 100644 --- a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs @@ -116,6 +116,7 @@ public static void AddSqlsugarSetup(this IServiceCollection services) }); }); }); + services.AddTransient(s => s.GetService() as SqlSugarScope); } private static string GetWholeSql(SugarParameter[] paramArr, string sql) 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 083/155] =?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 084/155] =?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 085/155] =?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 086/155] 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 087/155] 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 088/155] =?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); + } + } } } From 164946d0bd3c24f479f9615b09508cefe6d8db8e Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Wed, 1 Nov 2023 11:05:28 +0800 Subject: [PATCH 089/155] =?UTF-8?q?feat=EF=BC=9Aupdate=20common.targets?= 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.Serilog/Blog.Core.Serilog.csproj | 20 ++++++++------------ build/common.targets | 4 +++- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/Blog.Core.Api/Blog.Core.Api.csproj b/Blog.Core.Api/Blog.Core.Api.csproj index 3b913630..e022f1a2 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.Serilog/Blog.Core.Serilog.csproj b/Blog.Core.Serilog/Blog.Core.Serilog.csproj index 8df8f16f..70fe14fd 100644 --- a/Blog.Core.Serilog/Blog.Core.Serilog.csproj +++ b/Blog.Core.Serilog/Blog.Core.Serilog.csproj @@ -1,17 +1,13 @@ - + - - net6.0 - enable - enable - + - - - + + + - - - + + + diff --git a/build/common.targets b/build/common.targets index bfd5899f..68825d9d 100644 --- a/build/common.targets +++ b/build/common.targets @@ -1,5 +1,7 @@ - net7.0 + net7.0 + enable + enable \ No newline at end of file From 6655c65924e3408e2f5c0a9ab44bf12507769559 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Wed, 1 Nov 2023 15:06:32 +0800 Subject: [PATCH 090/155] Update common.targets --- build/common.targets | 1 - 1 file changed, 1 deletion(-) diff --git a/build/common.targets b/build/common.targets index 68825d9d..5f035173 100644 --- a/build/common.targets +++ b/build/common.targets @@ -2,6 +2,5 @@ net7.0 enable - enable \ No newline at end of file From afb9a0d9f5da0fdfe1d1478a5c7ec23bd39f4bff Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Fri, 3 Nov 2023 09:22:02 +0800 Subject: [PATCH 091/155] =?UTF-8?q?=F0=9F=90=9B=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E6=B7=BB=E5=8A=A0=E5=A4=87=E7=94=A8=E9=93=BE?= =?UTF-8?q?=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs index eb012394..99133fe1 100644 --- a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs @@ -83,10 +83,12 @@ public static void AddSqlsugarSetup(this IServiceCollection services) { BaseDBConfig.MainConfig = config; } - - //复用连接 - if (m.ConnId.ToLower().StartsWith(MainDb.CurrentDbConnId.ToLower())) + else if (m.ConnId.ToLower().StartsWith(MainDb.CurrentDbConnId.ToLower())) + { + //复用连接 BaseDBConfig.ReuseConfigs.Add(config); + } + BaseDBConfig.ValidConfig.Add(config); } From c85f51a9013323f82b4e3a9a635d6c79c4730788 Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Sat, 11 Nov 2023 15:57:56 +0800 Subject: [PATCH 092/155] =?UTF-8?q?=F0=9F=8E=A8=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=93=8D=E5=BA=94=E6=B5=81=E8=AF=BB=E5=8F=96,=E5=85=BC?= =?UTF-8?q?=E5=AE=B9MemoryStream?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Extensions/HttpResponseExceptions.cs | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/Blog.Core.Common/Extensions/HttpResponseExceptions.cs b/Blog.Core.Common/Extensions/HttpResponseExceptions.cs index 67deee45..34c3baae 100644 --- a/Blog.Core.Common/Extensions/HttpResponseExceptions.cs +++ b/Blog.Core.Common/Extensions/HttpResponseExceptions.cs @@ -7,29 +7,29 @@ namespace Blog.Core.Common.Extensions; public static class HttpResponseExceptions { - public static string GetResponseBody(this HttpResponse response) - { - if (response is null) - { - return default; - } + public static string GetResponseBody(this HttpResponse response) + { + if (response is null) + { + return string.Empty; + } - if (response.Body is FluentHttpResponseStream responseBody) - { - response.Body.Position = 0; - //不关闭底层流 - using StreamReader stream = new StreamReader(responseBody, leaveOpen: true); - 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; - } + //原始HttpResponseStream 无法读取 + //实际上只是个包装类,内部使用了HttpResponsePipeWriter write + switch (response.Body) + { + case FluentHttpResponseStream: + case MemoryStream: + { + response.Body.Position = 0; + using var stream = new StreamReader(response.Body, leaveOpen: true); + var body = stream.ReadToEnd(); + response.Body.Position = 0; + return body; + } + default: + // throw new ApplicationException("The response body is not a FluentHttpResponseStream"); + return string.Empty; + } + } } \ No newline at end of file From 908e170a792538d3614dae433154674009069926 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Sat, 11 Nov 2023 16:57:32 +0800 Subject: [PATCH 093/155] =?UTF-8?q?feat:=20=E8=87=AA=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E8=AF=B7=E6=B1=82=E5=8F=82=E6=95=B0=E8=A7=A3=E5=AF=86=E5=92=8C?= =?UTF-8?q?=E5=93=8D=E5=BA=94=E5=8A=A0=E5=AF=86=E4=B8=AD=E9=97=B4=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Api/Controllers/LoginController.cs | 648 +++++++++--------- Blog.Core.Api/Program.cs | 3 + Blog.Core.Api/appsettings.json | 14 + .../EncryptionRequestMiddleware.cs | 116 ++++ .../EncryptionResponseMiddleware.cs | 114 +++ .../Middlewares/RequRespLogMiddleware.cs | 4 +- 6 files changed, 577 insertions(+), 322 deletions(-) create mode 100644 Blog.Core.Extensions/Middlewares/EncryptionRequestMiddleware.cs create mode 100644 Blog.Core.Extensions/Middlewares/EncryptionResponseMiddleware.cs diff --git a/Blog.Core.Api/Controllers/LoginController.cs b/Blog.Core.Api/Controllers/LoginController.cs index 8313507d..4838c3c9 100644 --- a/Blog.Core.Api/Controllers/LoginController.cs +++ b/Blog.Core.Api/Controllers/LoginController.cs @@ -15,324 +15,332 @@ 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; - 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; } - } + /// + /// 登录管理【无权限】 + /// + [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("认证失败"); + } + } + + [HttpGet] + [Route("GetJwtTokenSecret")] + public async Task> GetJwtTokenSecret(string name = "", string pass = "") + { + var rlt = await GetJwtToken3(name, pass); + return rlt; + } + + /// + /// 请求刷新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/Program.cs b/Blog.Core.Api/Program.cs index 876a83a8..4c2274ee 100644 --- a/Blog.Core.Api/Program.cs +++ b/Blog.Core.Api/Program.cs @@ -144,6 +144,9 @@ //app.UseHsts(); } +app.UseEncryptionRequest(); +app.UseEncryptionResponse(); + app.UseExceptionHandlerMiddle(); app.UseIpLimitMiddle(); app.UseRequestResponseLogMiddle(); diff --git a/Blog.Core.Api/appsettings.json b/Blog.Core.Api/appsettings.json index f91f6df8..ff2c178a 100644 --- a/Blog.Core.Api/appsettings.json +++ b/Blog.Core.Api/appsettings.json @@ -265,6 +265,20 @@ }, "IpRateLimit": { "Enabled": true + }, + "EncryptionResponse": { + "Enabled": true, + "AllApis": false, + "LimitApis": [ + "/api/Login/GetJwtTokenSecret" + ] + }, + "EncryptionRequest": { + "Enabled": true, + "AllApis": false, + "LimitApis": [ + "/api/Login/GetJwtTokenSecret" + ] } }, "IpRateLimiting": { diff --git a/Blog.Core.Extensions/Middlewares/EncryptionRequestMiddleware.cs b/Blog.Core.Extensions/Middlewares/EncryptionRequestMiddleware.cs new file mode 100644 index 00000000..9cb78a30 --- /dev/null +++ b/Blog.Core.Extensions/Middlewares/EncryptionRequestMiddleware.cs @@ -0,0 +1,116 @@ +using Blog.Core.Common; +using Blog.Core.Common.Extensions; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; +using System; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Blog.Core.Extensions +{ + /// + /// 自定义中间件 + /// 通过配置,对指定接口返回数据进行加密返回 + /// 可过滤文件流 + /// + public class EncryptionRequestMiddleware + { + private readonly RequestDelegate _next; + + public EncryptionRequestMiddleware(RequestDelegate next) + { + _next = next; + } + + public async Task InvokeAsync(HttpContext context) + { + // 配置开关,过滤接口 + if (AppSettings.app("Middleware", "EncryptionRequest", "Enabled").ObjToBool()) + { + var isAllApis = AppSettings.app("Middleware", "EncryptionRequest", "AllApis").ObjToBool(); + var needEnApis = AppSettings.app("Middleware", "EncryptionRequest", "LimitApis"); + var path = context.Request.Path.Value.ToLower(); + if (isAllApis || (path.Length > 5 && needEnApis.Any(d => d.ToLower().Contains(path)))) + { + Console.WriteLine($"{isAllApis} -- {path}"); + + if (context.Request.Method.ToLower() == "post") + { + // 读取请求主体 + using StreamReader reader = new(context.Request.Body, Encoding.UTF8); + string requestBody = await reader.ReadToEndAsync(); + + // 检查是否有要解密的数据 + if (!string.IsNullOrEmpty(requestBody) && context.Request.Headers.ContainsKey("Content-Type") && + context.Request.Headers["Content-Type"].ToString().ToLower().Contains("application/json")) + { + // 解密数据 + string decryptedString = DecryptData(requestBody); + + // 更新请求主体中的数据 + context.Request.Body = GenerateStreamFromString(decryptedString); + } + } + else if (context.Request.Method.ToLower() == "get") + { + // 获取url参数 + string param = context.Request.Query["param"]; + + // 检查是否有要解密的数据 + if (!string.IsNullOrEmpty(param)) + { + // 解密数据 + string decryptedString = DecryptData(param); + + // 更新url参数值 + context.Request.QueryString = new QueryString($"?{decryptedString}"); + } + } + + await _next(context); + } + else + { + await _next(context); + } + } + else + { + await _next(context); + } + } + private string DecryptData(string encryptedData) + { + // 解密逻辑实现,可以根据你使用的加密算法和密钥进行自定义 + byte[] bytes = Convert.FromBase64String(encryptedData); + string originalString = Encoding.UTF8.GetString(bytes); + Console.WriteLine(originalString); + return originalString; + } + private static Stream GenerateStreamFromString(string s) + { + var stream = new MemoryStream(); + var writer = new StreamWriter(stream); + writer.Write(s); + writer.Flush(); + stream.Position = 0; + return stream; + } + } + + public static class EncryptionRequestExtensions + { + /// + /// 自定义中间件 + /// 通过配置,对指定接口入参进行解密操作 + /// 注意:放到管道最外层 + /// + public static IApplicationBuilder UseEncryptionRequest(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } + } +} diff --git a/Blog.Core.Extensions/Middlewares/EncryptionResponseMiddleware.cs b/Blog.Core.Extensions/Middlewares/EncryptionResponseMiddleware.cs new file mode 100644 index 00000000..cb701b1f --- /dev/null +++ b/Blog.Core.Extensions/Middlewares/EncryptionResponseMiddleware.cs @@ -0,0 +1,114 @@ +using Blog.Core.Common; +using Blog.Core.Common.Extensions; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; +using System; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Blog.Core.Extensions +{ + /// + /// 自定义中间件 + /// 通过配置,对指定接口返回数据进行加密返回 + /// 可过滤文件流 + /// + public class EncryptionResponseMiddleware + { + private readonly RequestDelegate _next; + + public EncryptionResponseMiddleware(RequestDelegate next) + { + _next = next; + } + + public async Task InvokeAsync(HttpContext context) + { + // 配置开关,过滤接口 + if (AppSettings.app("Middleware", "EncryptionResponse", "Enabled").ObjToBool()) + { + var isAllApis = AppSettings.app("Middleware", "EncryptionResponse", "AllApis").ObjToBool(); + var needEnApis = AppSettings.app("Middleware", "EncryptionResponse", "LimitApis"); + var path = context.Request.Path.Value.ToLower(); + if (isAllApis || (path.Length > 5 && needEnApis.Any(d => d.ToLower().Contains(path)))) + { + Console.WriteLine($"{isAllApis} -- {path}"); + var responseCxt = context.Response; + var originalBodyStream = responseCxt.Body; + + // 创建一个新的内存流用于存储加密后的数据 + using var encryptedBodyStream = new MemoryStream(); + // 用新的内存流替换 responseCxt.Body + responseCxt.Body = encryptedBodyStream; + + // 执行下一个中间件请求管道 + await _next(context); + + //encryptedBodyStream.Seek(0, SeekOrigin.Begin); + //encryptedBodyStream.Position = 0; + + // 可以去掉某些流接口 + if (!context.Response.ContentType.ToLower().Contains("application/json")) + { + Console.WriteLine($"非json返回格式 {context.Response.ContentType}"); + //await encryptedBodyStream.CopyToAsync(originalBodyStream); + context.Response.Body = originalBodyStream; + return; + } + + // 读取加密后的数据 + //var encryptedBody = await new StreamReader(encryptedBodyStream).ReadToEndAsync(); + var encryptedBody = responseCxt.GetResponseBody(); + + if (encryptedBody.IsNotEmptyOrNull()) + { + dynamic jsonObject = JsonConvert.DeserializeObject(encryptedBody); + string statusCont = jsonObject.status; + var status = statusCont.ObjToInt(); + string msg = jsonObject.msg; + string successCont = jsonObject.success; + var success = successCont.ObjToBool(); + dynamic responseCnt = success ? jsonObject.response : ""; + string s = "1"; + // 这里换成自己的任意加密方式 + var response = responseCnt.ToString() != "" ? Convert.ToBase64String(Encoding.UTF8.GetBytes(responseCnt.ToString())) : ""; + string resJson = JsonConvert.SerializeObject(new { response, msg, status, s, success }); + + context.Response.Clear(); + await using var streamlriter = new StreamWriter(originalBodyStream, leaveOpen: true); + await streamlriter.WriteAsync(resJson); + + //var encryptedData = Encoding.UTF8.GetBytes(resJson); + //responseCxt.ContentLength = encryptedData.Length; + //await originalBodyStream.WriteAsync(encryptedData, 0, encryptedData.Length); + } + } + else + { + await _next(context); + } + } + else + { + await _next(context); + } + } + } + + public static class EncryptionResponseExtensions + { + /// + /// 自定义中间件 + /// 通过配置,对指定接口返回数据进行加密返回 + /// 可过滤文件流 + /// 注意:放到管道最外层 + /// + public static IApplicationBuilder UseEncryptionResponse(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } + } +} diff --git a/Blog.Core.Extensions/Middlewares/RequRespLogMiddleware.cs b/Blog.Core.Extensions/Middlewares/RequRespLogMiddleware.cs index 26971a3a..510b87ef 100644 --- a/Blog.Core.Extensions/Middlewares/RequRespLogMiddleware.cs +++ b/Blog.Core.Extensions/Middlewares/RequRespLogMiddleware.cs @@ -96,11 +96,11 @@ private void ResponseDataLog(HttpResponse response) // 去除 Html var reg = "<[^>]+>"; - var isHtml = Regex.IsMatch(responseBody, reg); if (!string.IsNullOrEmpty(responseBody)) { - Parallel.For(0, 1, e => + var isHtml = Regex.IsMatch(responseBody, reg); + Parallel.For(0, 1, e => { //LogLock.OutSql2Log("RequestResponseLog", new string[] { "Response Data:", ResponseBody }); LogLock.OutLogAOP("RequestResponseLog", response.HttpContext.TraceIdentifier, From d0fe732331368ff71191d3d89d4a28f3d17f99d5 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Sat, 11 Nov 2023 17:56:31 +0800 Subject: [PATCH 094/155] Update EncryptionResponseMiddleware.cs --- .../Middlewares/EncryptionResponseMiddleware.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Blog.Core.Extensions/Middlewares/EncryptionResponseMiddleware.cs b/Blog.Core.Extensions/Middlewares/EncryptionResponseMiddleware.cs index cb701b1f..188c6f8d 100644 --- a/Blog.Core.Extensions/Middlewares/EncryptionResponseMiddleware.cs +++ b/Blog.Core.Extensions/Middlewares/EncryptionResponseMiddleware.cs @@ -78,12 +78,16 @@ public async Task InvokeAsync(HttpContext context) string resJson = JsonConvert.SerializeObject(new { response, msg, status, s, success }); context.Response.Clear(); - await using var streamlriter = new StreamWriter(originalBodyStream, leaveOpen: true); - await streamlriter.WriteAsync(resJson); + responseCxt.ContentType = "application/json"; - //var encryptedData = Encoding.UTF8.GetBytes(resJson); - //responseCxt.ContentLength = encryptedData.Length; - //await originalBodyStream.WriteAsync(encryptedData, 0, encryptedData.Length); + //await using var streamlriter = new StreamWriter(originalBodyStream, leaveOpen: true); + //await streamlriter.WriteAsync(resJson); + + var encryptedData = Encoding.UTF8.GetBytes(resJson); + responseCxt.ContentLength = encryptedData.Length; + await originalBodyStream.WriteAsync(encryptedData, 0, encryptedData.Length); + + responseCxt.Body = originalBodyStream; } } else From 7ca3e1ec22dcf3812c2114a502f977264cba1535 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Wed, 15 Nov 2023 16:15:21 +0800 Subject: [PATCH 095/155] feat: update to 8.0 --- .github/workflows/dotnetcore.yml | 2 +- Blog.Core.Api/Blog.Core.Api.csproj | 8 ++-- Blog.Core.Api/Program.cs | 3 ++ Blog.Core.Common/Blog.Core.Common.csproj | 32 ++++++++-------- Blog.Core.EventBus/Blog.Core.EventBus.csproj | 14 +++---- .../Blog.Core.Extensions.csproj | 38 +++++++++---------- Blog.Core.Gateway/Blog.Core.Gateway.csproj | 12 +++--- Blog.Core.Model/Blog.Core.Model.csproj | 4 +- .../Blog.Core.Repository.csproj | 8 ++-- .../Blog.Core.Serilog.Es.csproj | 12 +++--- Blog.Core.Serilog/Blog.Core.Serilog.csproj | 2 +- Blog.Core.Tasks/Blog.Core.Tasks.csproj | 2 +- Blog.Core.Tests/Blog.Core.Tests.csproj | 8 ++-- Dockerfile | 4 +- Ocelot.Provider.Nacos/Nacos.cs | 11 +++--- .../Ocelot.Provider.Nacos.csproj | 10 ++--- build/common.targets | 2 +- 17 files changed, 88 insertions(+), 84 deletions(-) diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml index 2dd1ed9c..f11fee1f 100644 --- a/.github/workflows/dotnetcore.yml +++ b/.github/workflows/dotnetcore.yml @@ -12,7 +12,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v1 with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Build with dotnet run: dotnet build --configuration Release - name: Build image diff --git a/Blog.Core.Api/Blog.Core.Api.csproj b/Blog.Core.Api/Blog.Core.Api.csproj index e022f1a2..92a680da 100644 --- a/Blog.Core.Api/Blog.Core.Api.csproj +++ b/Blog.Core.Api/Blog.Core.Api.csproj @@ -61,11 +61,11 @@ - + - - - + + + diff --git a/Blog.Core.Api/Program.cs b/Blog.Core.Api/Program.cs index 875b4f2e..13d7cea2 100644 --- a/Blog.Core.Api/Program.cs +++ b/Blog.Core.Api/Program.cs @@ -17,6 +17,7 @@ using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.IdentityModel.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; @@ -130,6 +131,8 @@ // 3、配置中间件 var app = builder.Build(); +IdentityModelEventSource.ShowPII = true; + app.ConfigureApplication(); app.UseApplicationSetup(); app.UseResponseBodyRead(); diff --git a/Blog.Core.Common/Blog.Core.Common.csproj b/Blog.Core.Common/Blog.Core.Common.csproj index 457e5722..ce966e59 100644 --- a/Blog.Core.Common/Blog.Core.Common.csproj +++ b/Blog.Core.Common/Blog.Core.Common.csproj @@ -14,29 +14,29 @@ - - + + - - + + - - + + - - - - + + + + - - + + - - + + - - + + diff --git a/Blog.Core.EventBus/Blog.Core.EventBus.csproj b/Blog.Core.EventBus/Blog.Core.EventBus.csproj index 8c50f8e2..17bebf20 100644 --- a/Blog.Core.EventBus/Blog.Core.EventBus.csproj +++ b/Blog.Core.EventBus/Blog.Core.EventBus.csproj @@ -5,15 +5,15 @@ - - + + - + - - - - + + + + diff --git a/Blog.Core.Extensions/Blog.Core.Extensions.csproj b/Blog.Core.Extensions/Blog.Core.Extensions.csproj index a68f7f12..96183e46 100644 --- a/Blog.Core.Extensions/Blog.Core.Extensions.csproj +++ b/Blog.Core.Extensions/Blog.Core.Extensions.csproj @@ -5,32 +5,32 @@ - - - - - - + + + + + + - - - - - - - - - + + + + + + + + + - - - + + + - + diff --git a/Blog.Core.Gateway/Blog.Core.Gateway.csproj b/Blog.Core.Gateway/Blog.Core.Gateway.csproj index ca3a7f72..8af7a7e3 100644 --- a/Blog.Core.Gateway/Blog.Core.Gateway.csproj +++ b/Blog.Core.Gateway/Blog.Core.Gateway.csproj @@ -13,12 +13,12 @@ - - - - - - + + + + + + diff --git a/Blog.Core.Model/Blog.Core.Model.csproj b/Blog.Core.Model/Blog.Core.Model.csproj index 6762bbcd..841a4665 100644 --- a/Blog.Core.Model/Blog.Core.Model.csproj +++ b/Blog.Core.Model/Blog.Core.Model.csproj @@ -14,9 +14,9 @@ - + - + diff --git a/Blog.Core.Repository/Blog.Core.Repository.csproj b/Blog.Core.Repository/Blog.Core.Repository.csproj index 45931d9f..84b5dfad 100644 --- a/Blog.Core.Repository/Blog.Core.Repository.csproj +++ b/Blog.Core.Repository/Blog.Core.Repository.csproj @@ -4,9 +4,9 @@ - - - + + + @@ -19,7 +19,7 @@ - + diff --git a/Blog.Core.Serilog.Es/Blog.Core.Serilog.Es.csproj b/Blog.Core.Serilog.Es/Blog.Core.Serilog.Es.csproj index cd71cf26..a53e98f5 100644 --- a/Blog.Core.Serilog.Es/Blog.Core.Serilog.Es.csproj +++ b/Blog.Core.Serilog.Es/Blog.Core.Serilog.Es.csproj @@ -4,12 +4,12 @@ - - - - - - + + + + + + diff --git a/Blog.Core.Serilog/Blog.Core.Serilog.csproj b/Blog.Core.Serilog/Blog.Core.Serilog.csproj index 70fe14fd..9994dce2 100644 --- a/Blog.Core.Serilog/Blog.Core.Serilog.csproj +++ b/Blog.Core.Serilog/Blog.Core.Serilog.csproj @@ -3,7 +3,7 @@ - + diff --git a/Blog.Core.Tasks/Blog.Core.Tasks.csproj b/Blog.Core.Tasks/Blog.Core.Tasks.csproj index 3292ff0d..abc0d331 100644 --- a/Blog.Core.Tasks/Blog.Core.Tasks.csproj +++ b/Blog.Core.Tasks/Blog.Core.Tasks.csproj @@ -3,7 +3,7 @@ - + diff --git a/Blog.Core.Tests/Blog.Core.Tests.csproj b/Blog.Core.Tests/Blog.Core.Tests.csproj index 4db51c4f..22c227e0 100644 --- a/Blog.Core.Tests/Blog.Core.Tests.csproj +++ b/Blog.Core.Tests/Blog.Core.Tests.csproj @@ -18,10 +18,10 @@ - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Dockerfile b/Dockerfile index c80e6896..15ba36e5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,11 +7,11 @@ #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. -FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base WORKDIR /app EXPOSE 80 -FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /src COPY ["Blog.Core.Api/Blog.Core.Api.csproj", "Blog.Core.Api/"] COPY ["Blog.Core.Extensions/Blog.Core.Extensions.csproj", "Blog.Core.Extensions/"] diff --git a/Ocelot.Provider.Nacos/Nacos.cs b/Ocelot.Provider.Nacos/Nacos.cs index d8357920..b1d4cd4f 100644 --- a/Ocelot.Provider.Nacos/Nacos.cs +++ b/Ocelot.Provider.Nacos/Nacos.cs @@ -1,8 +1,4 @@ -using System; -using System.Linq; -using System.Collections.Generic; -using System.Threading.Tasks; -using Ocelot.ServiceDiscovery.Providers; +using Ocelot.ServiceDiscovery.Providers; using Ocelot.Values; using Nacos.V2; using Microsoft.Extensions.Options; @@ -52,5 +48,10 @@ public async Task> Get() return await Task.FromResult(services); } + + public Task> GetAsync() + { + throw new NotImplementedException(); + } } } diff --git a/Ocelot.Provider.Nacos/Ocelot.Provider.Nacos.csproj b/Ocelot.Provider.Nacos/Ocelot.Provider.Nacos.csproj index 6bdeb841..541ee1e9 100644 --- a/Ocelot.Provider.Nacos/Ocelot.Provider.Nacos.csproj +++ b/Ocelot.Provider.Nacos/Ocelot.Provider.Nacos.csproj @@ -11,10 +11,10 @@ - - - - - + + + + + diff --git a/build/common.targets b/build/common.targets index 5f035173..eeb37544 100644 --- a/build/common.targets +++ b/build/common.targets @@ -1,6 +1,6 @@ - net7.0 + net8.0 enable \ No newline at end of file From 6331e2082021440aa3e675ba687f7e20bf50e7b5 Mon Sep 17 00:00:00 2001 From: LemonNoCry <773596523@qq.com> Date: Wed, 15 Nov 2023 18:09:54 +0800 Subject: [PATCH 096/155] =?UTF-8?q?=F0=9F=90=9B=20=E4=BC=98=E5=8C=96JWT?= =?UTF-8?q?=E7=AD=BE=E5=8F=91=E5=B1=9E=E6=80=A7=20iat=20=E4=B8=BA=E7=AD=BE?= =?UTF-8?q?=E5=8F=91=E6=97=B6=E9=97=B4=E6=88=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Api/Controllers/LoginController.cs | 16 ++++++++-------- Blog.Core.Common/Helper/UtilConvert.cs | 20 ++++++++++++++++---- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/Blog.Core.Api/Controllers/LoginController.cs b/Blog.Core.Api/Controllers/LoginController.cs index 4838c3c9..e6b4ee4f 100644 --- a/Blog.Core.Api/Controllers/LoginController.cs +++ b/Blog.Core.Api/Controllers/LoginController.cs @@ -70,7 +70,7 @@ 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 }; + TokenModelJwt tokenModel = new TokenModelJwt {Uid = 1, Role = user}; jwtStr = JwtHelper.IssueJwt(tokenModel); suc = true; @@ -121,7 +121,7 @@ public MessageModel GetJwtStrForNuxt(string name, string pass) var result = new { - data = new { success = suc, token = jwtStr } + data = new {success = suc, token = jwtStr} }; return new MessageModel() @@ -164,7 +164,7 @@ public async Task> GetJwtToken3(string name = " 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(JwtRegisteredClaimNames.Iat, DateTimeOffset.Now.ToUnixTimeSeconds().ToString()), new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(_requirement.Expiration.TotalSeconds).ToString()) }; @@ -236,7 +236,7 @@ public async Task> RefreshToken(string token = { new Claim(ClaimTypes.Name, user.LoginName), new Claim(JwtRegisteredClaimNames.Jti, tokenModel.Uid.ObjToString()), - new Claim(JwtRegisteredClaimNames.Iat, DateTime.Now.ToString()), + new Claim(JwtRegisteredClaimNames.Iat, DateTimeOffset.Now.ToUnixTimeSeconds().ToString()), new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(_requirement.Expiration.TotalSeconds).ToString()) }; @@ -305,7 +305,7 @@ public async Task SwgLogin([FromBody] SwaggerLoginRequest loginRequest) { if (loginRequest is null) { - return new { result = false }; + return new {result = false}; } try @@ -315,7 +315,7 @@ public async Task SwgLogin([FromBody] SwaggerLoginRequest loginRequest) { HttpContext.SuccessSwagger(); HttpContext.SuccessSwaggerJwt(result.response.token); - return new { result = true }; + return new {result = true}; } } catch (Exception ex) @@ -323,7 +323,7 @@ public async Task SwgLogin([FromBody] SwaggerLoginRequest loginRequest) _logger.LogWarning(ex, "Swagger登录异常"); } - return new { result = false }; + return new {result = false}; } /// @@ -334,7 +334,7 @@ public async Task SwgLogin([FromBody] SwaggerLoginRequest loginRequest) [Route("wxLogin")] public dynamic WxLogin(string g = "", string token = "") { - return new { g, token }; + return new {g, token}; } } diff --git a/Blog.Core.Common/Helper/UtilConvert.cs b/Blog.Core.Common/Helper/UtilConvert.cs index 530c4fd2..a75005ac 100644 --- a/Blog.Core.Common/Helper/UtilConvert.cs +++ b/Blog.Core.Common/Helper/UtilConvert.cs @@ -107,7 +107,8 @@ public static string ObjToString(this object thisValue) /// public static bool IsNotEmptyOrNull(this object thisValue) { - return ObjToString(thisValue) != "" && ObjToString(thisValue) != "undefined" && ObjToString(thisValue) != "null"; + return ObjToString(thisValue) != "" && ObjToString(thisValue) != "undefined" && + ObjToString(thisValue) != "null"; } /// @@ -122,7 +123,8 @@ public static string ObjToString(this object thisValue, string errorValue) return errorValue; } - public static bool IsNullOrEmpty(this object thisValue) => thisValue == null || thisValue == DBNull.Value || string.IsNullOrWhiteSpace(thisValue.ToString()); + public static bool IsNullOrEmpty(this object thisValue) => thisValue == null || thisValue == DBNull.Value || + string.IsNullOrWhiteSpace(thisValue.ToString()); /// /// @@ -169,6 +171,16 @@ public static DateTime ObjToDate(this object thisValue) { reval = Convert.ToDateTime(thisValue); } + else + { + //时间戳转为时间 + var seconds = ObjToLong(thisValue); + if (seconds > 0) + { + var startTime = TimeZoneInfo.ConvertTime(new DateTime(1970, 1, 1), TimeZoneInfo.Local); + reval = startTime.AddSeconds(Convert.ToDouble(thisValue)); + } + } return reval; } @@ -235,7 +247,7 @@ public static object ChangeType(this object value, Type type) { Type innerType = type.GetGenericArguments()[0]; object innerValue = ChangeType(value, innerType); - return Activator.CreateInstance(type, new object[] { innerValue }); + return Activator.CreateInstance(type, new object[] {innerValue}); } if (value is string && type == typeof(Guid)) return new Guid(value as string); @@ -278,7 +290,7 @@ public static object ChangeTypeList(this object value, Type type) .Remove(split.Length - 2, 1); } - addMethod.Invoke(lis, new object[] { ChangeType(str, type) }); + addMethod.Invoke(lis, new object[] {ChangeType(str, type)}); } } From b3e7fbca5436f7390dc4eb3bd26b4794932a2689 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Thu, 16 Nov 2023 10:31:21 +0800 Subject: [PATCH 097/155] feat: :apple: change iat value --- Blog.Core.Api/Controllers/LoginController.cs | 4 ++-- Blog.Core.Extensions/Authorizations/Helpers/JwtHelper.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Blog.Core.Api/Controllers/LoginController.cs b/Blog.Core.Api/Controllers/LoginController.cs index e6b4ee4f..abb495c9 100644 --- a/Blog.Core.Api/Controllers/LoginController.cs +++ b/Blog.Core.Api/Controllers/LoginController.cs @@ -164,7 +164,7 @@ public async Task> GetJwtToken3(string name = " new Claim(ClaimTypes.Name, name), new Claim(JwtRegisteredClaimNames.Jti, user.FirstOrDefault().Id.ToString()), new Claim("TenantId", user.FirstOrDefault().TenantId.ToString()), - new Claim(JwtRegisteredClaimNames.Iat, DateTimeOffset.Now.ToUnixTimeSeconds().ToString()), + new Claim(JwtRegisteredClaimNames.Iat, DateTime.Now.DateToTimeStamp()), new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(_requirement.Expiration.TotalSeconds).ToString()) }; @@ -236,7 +236,7 @@ public async Task> RefreshToken(string token = { new Claim(ClaimTypes.Name, user.LoginName), new Claim(JwtRegisteredClaimNames.Jti, tokenModel.Uid.ObjToString()), - new Claim(JwtRegisteredClaimNames.Iat, DateTimeOffset.Now.ToUnixTimeSeconds().ToString()), + new Claim(JwtRegisteredClaimNames.Iat, DateTime.Now.DateToTimeStamp()), new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(_requirement.Expiration.TotalSeconds).ToString()) }; diff --git a/Blog.Core.Extensions/Authorizations/Helpers/JwtHelper.cs b/Blog.Core.Extensions/Authorizations/Helpers/JwtHelper.cs index b9659029..6fa9c9b6 100644 --- a/Blog.Core.Extensions/Authorizations/Helpers/JwtHelper.cs +++ b/Blog.Core.Extensions/Authorizations/Helpers/JwtHelper.cs @@ -36,8 +36,8 @@ public static string IssueJwt(TokenModelJwt tokenModel) new Claim(JwtRegisteredClaimNames.Jti, tokenModel.Uid.ToString()), - new Claim(JwtRegisteredClaimNames.Iat, $"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}"), - new Claim(JwtRegisteredClaimNames.Nbf,$"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}") , + new Claim(JwtRegisteredClaimNames.Iat, $"{DateTime.Now.DateToTimeStamp()}"), + new Claim(JwtRegisteredClaimNames.Nbf,$"{DateTime.Now.DateToTimeStamp()}") , //这个就是过期时间,目前是过期1000秒,可自定义,注意JWT有自己的缓冲过期时间 new Claim (JwtRegisteredClaimNames.Exp,$"{new DateTimeOffset(DateTime.Now.AddSeconds(1000)).ToUnixTimeSeconds()}"), new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(1000).ToString()), From c93c3eb44b57b7414a486f86aba336dcb02d7e5a Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Sat, 18 Nov 2023 08:53:47 +0800 Subject: [PATCH 098/155] Update Authentication_JWTSetup.cs --- .../ServiceExtensions/Authentication_JWTSetup.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Blog.Core.Extensions/ServiceExtensions/Authentication_JWTSetup.cs b/Blog.Core.Extensions/ServiceExtensions/Authentication_JWTSetup.cs index 98fa0b82..d9048c6e 100644 --- a/Blog.Core.Extensions/ServiceExtensions/Authentication_JWTSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/Authentication_JWTSetup.cs @@ -5,11 +5,8 @@ using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; -using System; using System.IdentityModel.Tokens.Jwt; -using System.Linq; using System.Text; -using System.Threading.Tasks; namespace Blog.Core.Extensions { @@ -74,7 +71,7 @@ public static void AddAuthentication_JWTSetup(this IServiceCollection services) }, OnChallenge = context => { - context.Response.Headers.Add("Token-Error", context.ErrorDescription); + context.Response.Headers["Token-Error"] = context.ErrorDescription; return Task.CompletedTask; }, OnAuthenticationFailed = context => @@ -88,12 +85,12 @@ public static void AddAuthentication_JWTSetup(this IServiceCollection services) if (jwtToken.Issuer != Issuer) { - context.Response.Headers.Add("Token-Error-Iss", "issuer is wrong!"); + context.Response.Headers["Token-Error-Iss"] = "issuer is wrong!"; } if (jwtToken.Audiences.FirstOrDefault() != Audience) { - context.Response.Headers.Add("Token-Error-Aud", "Audience is wrong!"); + context.Response.Headers["Token-Error-Aud"] = "Audience is wrong!"; } } @@ -101,7 +98,7 @@ public static void AddAuthentication_JWTSetup(this IServiceCollection services) // 如果过期,则把<是否过期>添加到,返回头信息中 if (context.Exception.GetType() == typeof(SecurityTokenExpiredException)) { - context.Response.Headers.Add("Token-Expired", "true"); + context.Response.Headers["Token-Expired"] = "true"; } return Task.CompletedTask; } From 1b3ab297f36ea6906a6a13518531c1e7532d4f46 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Mon, 20 Nov 2023 16:38:36 +0800 Subject: [PATCH 099/155] =?UTF-8?q?feat=EF=BC=9A=E5=B0=86=E5=AE=98?= =?UTF-8?q?=E6=96=B9=E6=96=87=E6=A1=A3=E8=BF=81=E7=A7=BB=E5=88=B0=E6=96=B0?= =?UTF-8?q?=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .docs/README.md | 2 + .docs/contents/.vuepress/config.js | 46 -- .../.vuepress/public/bcvphomelogo.png | Bin 5644 -> 0 bytes .docs/contents/.vuepress/public/favicon.ico | Bin 1556 -> 0 bytes .docs/contents/Contribution/README.md | 143 ----- .docs/contents/PressureTest/README.md | 80 --- .docs/contents/QQ/README.md | 18 - .docs/contents/README.md | 14 - .docs/contents/Update/README.md | 198 ------ .docs/contents/guide/README.md | 121 ---- .docs/contents/guide/cheat-sheet.md | 580 ------------------ .docs/contents/guide/function-sheet.md | 471 -------------- .docs/contents/guide/getting-started.md | 132 ---- .docs/package.json | 12 - .gitignore | 1 + 15 files changed, 3 insertions(+), 1815 deletions(-) create mode 100644 .docs/README.md delete mode 100644 .docs/contents/.vuepress/config.js delete mode 100644 .docs/contents/.vuepress/public/bcvphomelogo.png delete mode 100644 .docs/contents/.vuepress/public/favicon.ico delete mode 100644 .docs/contents/Contribution/README.md delete mode 100644 .docs/contents/PressureTest/README.md delete mode 100644 .docs/contents/QQ/README.md delete mode 100644 .docs/contents/README.md delete mode 100644 .docs/contents/Update/README.md delete mode 100644 .docs/contents/guide/README.md delete mode 100644 .docs/contents/guide/cheat-sheet.md delete mode 100644 .docs/contents/guide/function-sheet.md delete mode 100644 .docs/contents/guide/getting-started.md delete mode 100644 .docs/package.json diff --git a/.docs/README.md b/.docs/README.md new file mode 100644 index 00000000..c7aaa452 --- /dev/null +++ b/.docs/README.md @@ -0,0 +1,2 @@ +BlogCore官方文档仓库地址已经迁移到: +https://gitee.com/laozhangIsPhi/Blog.Core.E-Book \ No newline at end of file diff --git a/.docs/contents/.vuepress/config.js b/.docs/contents/.vuepress/config.js deleted file mode 100644 index 6cb23754..00000000 --- a/.docs/contents/.vuepress/config.js +++ /dev/null @@ -1,46 +0,0 @@ -module.exports = { - title: 'Blog.Core', - description: 'Hello, 欢迎使用前后端分离之 ASP.NET Core 后端全家桶框架!', - base : '/.doc/', - head: [ - ['link', { - rel: 'icon', - href: `/favicon.ico` - }] - ], - dest: './contents/.vuepress/dist', - ga: '', - evergreen: true, - themeConfig: { - nav: [ - { text: '首页', link: '/' }, - { text: '指南', link: '/guide/' }, - { text: '更新日志', link: '/Update/' }, - { text: '压测', link: '/PressureTest/' }, - { text: '参与贡献', link: '/Contribution/' }, - { text: 'BCVP社区', link: '/QQ/' }, - { text: '接口API', link: 'http://apk.neters.club' }, - { text: '管理后台', link: 'http://vueadmin.neters.club' }, - { text: 'Github', link: 'https://github.com/anjoy8/Blog.Core' }, - ], - sidebarDepth: 2, - sidebar: { - '/guide/': getGuideSidebar('Guide'), - } - } -} - -function getGuideSidebar (groupA) { - return [ - { - title: groupA, - collapsable: false, - children: [ - '', - 'getting-started', - 'function-sheet', - 'cheat-sheet' - ] - } - ] - } \ No newline at end of file diff --git a/.docs/contents/.vuepress/public/bcvphomelogo.png b/.docs/contents/.vuepress/public/bcvphomelogo.png deleted file mode 100644 index e1bf0f79d16b27c9f911c995cdd0f15a849c1d14..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5644 zcma)=^;Z5zsc1nH9QrF#KU1O%j%kZxF3Wa&~6L29K1 z>810_^AEh|{bBAsb8gHz_sq|uakN~&3tiiDAE53Zngj=3l0v$M-3Gvqd@E3 z55x$DzK#I}Q2<1SghZ2;JAnxY3G5S%d|S-8v{Ly}rL2p9@C2SjbqIk5?nf2VLq&)I z`c>pqHXk`Hdjyr}Rp^cMln~tPstnw^ztB0zSkOwj`aVrj1KNCECzl$ zXMh+Db_(Q_+x1%##ygECEq3TtEsIA|@ypw?Qh!Uok1miA)LrhF%@=#>v6X`JbaCJy1t_rgoX7S-sN zs4b}it=1&m=fu~YK(RB(kO0sA6y~KzsB1f{@w@-*5~?s&9GB}B?lEtjq#QH&oJ)Bw zEzF}LP<|FIZil(z&UQB(W~YCY6a*h!&ce^Dm)FoCBB%EEnbUAV4ZiE(9y1*7*F%{+I-=}#F4L8E8$+iT9|D%@$vF3sX9c^hz3l0| zl(JfTsJrkyNp&xoXeEY{O)7=!?=tN0B10V+RhO9>cOL9c(05KRVI&eyq<7nEM1=b+a3P7x7fMsK7sC!L0S%g-RWb>-T5!>j2E!V1?1(W3=&7V=j+V$PUuVy1a9A%y?qIW829aUulUra6Pwu8Do@-(SqvEDx3s_pz_#^&Wjfn0&Eu4RwcdC0wg3O?>QsjVDuaOA9N;f-10aRBRqnr2OHt%L?8r=r<54 zL$1!`7?*x$uvle_tby9FA>>J7LUxjx-TMuZC;r+2^jh&cvb*8}AFrRjs7;f<{QIoR z28}N*=)|WM2ByFOyn;fx=AT?%J2To%-iTWnx)XxFuoUmHS-NLvcb*1+l@* zD173%I;Q3mJ1$8A!wV=d5BDpNGrE1JVPvP$I7g7kSQzgIKrp7-w4QT<&n#h){oj}r zCIw8tJX4}h#qz+Y)zXUMST=Gf+I<^K>gjdS#98W;;~L9$Q3C)6u`l>nc5U~i zOVH)kr(F*^M)1@p-BzqEP-bnQoFH065qw=Yw=-Fm+}wq(Ur3Cg@6lW;3^GyFx1xNN ze;~1#&?l~u95^O1Gf1cV)ONoY*81w^^5li8=WLzQQt&?^=Y_WV*ZWIW<}Gf{tpKY? zaV?$D#o#F4 z^g-d(1j8?tgSOKuX|52T=~&L@w1cV00PS3q|H(eS**j1@JCm_SY>Vs6xVd*svR15= z%TK7J$HJ@Lz>TClDbIrAe>0CBIVw?8IY*o?$A4MFKIp*Vp5ys)esdq$2A_0TefI?v z>~i+3-UXtUVFEbX^@9<-k`B(;By>#TZv2v(##?9-G>>n-e^Uty=5yD1-5X-3@3<`7FDQFS5Ifmw& z5U6Q;b{{N`smmNJqxmw19&O7+A#6nEcw}1Tmd73&5)GlLgZN@9r%ahuMu)~ilv`j~ zqf#YSn_%~Pj&Vzk_)?67+1jaV=+@%PNviD$i|d2e9RIFhtbfTt;)ArCJ}`_tmt+vE zFKPN=3pt0mF(mD(KrX~wAYI0H!?Zyh+-4+WJNiX`9{NOUSHwWdqkIr)IM#W_lK91~ zO-$w4Mxwha@10?3xO<^#gX1D$;zrWJjGX4$J}`kikN;>}mJ_LaRwh-;Z+NeYlS!WtPoDrDV$pc7oT-@i1IDRbPxzXp(ghkHrX>=9tr9nTn ziFJMX2QTHNS=rfmL>Cm{-_N|u(#!WxuPh1?Wde}FH%<`KY%o!H0EXYkC&b2wO6|>U zlf^)CzAL@jzSyL$@Sx|g8AhC`?*{+MU!X?fb#QozTX6pKqNu2FOQ8 zNa{`h@>A&v7?CGaY~DUtuyca7f|0ftHhxlJ;%{uIL?5leSTJ7t(L zECA2P6|@z-pv;ifzztj@NyCY8UveR_W;Yl@{8_<08SN0|t{9PABk@<9-WYD)(4!5Xp-^h^G z6js?^p!vqs3JP|^K?vM9JNsw*pCq7vhKQ0IZygDZM!fC$AjpmEGRWIyCEs_vzEhuR zqY>(}91EQD^OlqpS$%2sbSg97n4k(GP(cJ zC3#a#<~H2=L~Sjq=S}?C#u+lwhhRSnL>hXU4IOd3r58BfZsIB-W9%P#*BHEB+{(O@{Q>{Q7AKbTG7ND={MZz&{D@{OxO}hfrYxKkc6n6cHgd zDqJ`*Q%rxSoW_A$5#0($;{>8=Uer5{L}fZk>%Mj54^t~cCB~v)V25_a+qHO7%C|Y> z;dObxZjsV|zeD!>ag$JCpK8aGvsh97ho5e@XMZnVi}wktRp<#5h|LxkTfw4PGfNcB zp}cb2G5`b=TrRZVeN`i|{sY&trA1r;{Us$ni{x|3Qub@~L<%n{yjj+lq~Z*sXUb(z}~F*jSE z3g@^!Rt`r8ZOI~*uJ$^0%eCHT2B!soB2($hCo}w2?%3o?N=DNQBX9i8;HurO&E(Dg zCM429tgzrR|4z$P9XmxLtfa3Qn?kEaD5eTvL%&d@G^mK%6juB>z!s4YQfyb0&p{u3 z7``un)%#_c_?0M1boTG!b&tZ;Bghhac@qTBTS=u9l1-GJ;@7plOT5OS!i*JcbD$-` zoL2K!&Z4SbYQQJ#sTYraWeVdZMmzXCyUTnoTt8u}$Y@iy+Qvt+p&VMuK-85W)L1_ZB)*a5J5^7A;J%t|BoS|$tnK5= z!}1^?=LZ%dR%q4w!=wkr;@ro^X(4_{fqB14q-4%*3DnWlFQw(+BESN`*<&*va9AB7!6>n(Q@!q=Jw5<*b> zj3o3&KOG(dh7^0HB~{@_?r{>_?n{ukDHXg4y)Mlk136F*zIZ)3n89VZ{~B2(&Ms?- zSa_-<^f$uU1f$y#l3?&*v870Xfc(UA9AbrDIGkR$(ldN4y}9@1j5-@YvQ(U9FHx=~=! zDq!*DnBDmkO4o5WJ$?=)1f#->7E`co+LtO=8#_PWzGIS_O_~q`*1vktFGd_F!GCqPfFLn!I`YgFFQj9RCUv+rqqGE_bk5i28BD73TK8KBuw zTofc|TKBakM#+(>)Cn;FW^tQu(e`LxXS{M3o~*)8p%DRoOZWrZ7uJ1Kk@RagVNT9K zWPiF1U}Z;sg+CZ{|Euf-G_ofT3+WJM0cD?~dD82+z@*b%IUa}ExQoqzx3T+C>_h*x zI*lp6;i5dIJyMdzBt<7;aLPx;me%ow8m&WL>*CooVj9fXy zvCUN8hpY3J=v5h(3>vsu$S(k;^0b8>ATSQb%@4RYG$FksX zg}!_9Z2Jro;R$q1vWU|v86|ZLrl0Pp5VtBIe@-%)tnJ;l61X8CGi#c$WPB)0o!<0$ z;j{Q_vd>oTuOkIw;zvFfT`eaE_X(+&>+=#23s`Sf2~)>sJfu)u^(_-+ezm3W?qSR44xK*U$PFe5EbvGK~LCYd=b>Y;QibU>_ z+8k*d_eKWwD3w!DjQ#!#)?`{~wa-v4ti#G`o4TAB+d3NWMkws@WZ8|Aeri8qw!FL# zV{;U)S;wvRQWx<6Y0u{XnzPG)wYp_PnmKG4k~wU-0q^7yPTwnRG+;y;1zRCoVB74f{*{&t`bhYWSNBYY`OK7y9EVY=nX-(LK5!oDUAA_BM0&o zK-r)my>R2VQ=#AZd(x8mz>{f{U9qj_=H)3plld@qv$iE<2r%=RA9SnIxXK`N&}K!* zvhBOrjce4M(=(SK-bdnihmEX>e5M+*hiE!71OxXM?L0|I%F6t60h5}h@EBLnio`vG zVUY`NYIUENoF`d{RH3V$bR`NeVE!vZ+JEw2M-;m`c8@16Ii^9V@SDq}1R5?k=~P8* zD!!ujV z*0NiGQgH5)vHr%(ewu9w)Icv68nM647HvM(%bf0GPUJBiT6PmLc~|^RH=x>bSwpdy zhY5BOWfb=@b0`vETr;7eYLE9s$*=G#}rC4;-o|KlFwlF zVHKgNdEc=aMECh9$B`f2gNZlq3~dp$8CsrSBXLiI z_Lq=L_c?m!=4%u_(yG%xD3wL3W#+$1K7{c`plRpO*FrG z1fDwd1T%@?zcgcIt>VTjeVZXcDL|Vxc3{&Z{0gKae8j3WZH9=`O^sBVL8G(DsP7o>3jHkOVE z^N-DzphYrTG3sSD&B066F1&>v{l_$EWvRw{{{OJ^2la)5?ylDkmP7+}PjGQGRCQEp Ip26V%1Gkl@sQ>@~ diff --git a/.docs/contents/.vuepress/public/favicon.ico b/.docs/contents/.vuepress/public/favicon.ico deleted file mode 100644 index 68062fe0577bebf8065cfc59d83f29f40a8bd7f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1556 zcmV+v2J88WP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!~vBn4jTXf1*u6yK~z{r?N(WA zR8<)MX6~J(v$Qi4x`7m0T3QN4s8R%i5L{Ri6D5e4078sDXynC>5EB)BktcjIp@s+= zqL8R5Dq&GE2&GtB#ZsUP6l6MWXz4PY&fTy7IdiA9(qaq5^ucd(=H7GubI$*rf4j$K z5-?6+x1Qqy|C1RBN?e1}CBPqYBP-vJ$;Cd{?f36IBakss zeW(@9HC<2@9fD|s%}&im5}IuqCUo6^U>7jGEP&GGlSpRcV~iYozZ={}rZW=5(cMkZ zGy|eT-~>hjTWaVgC;HfiX~@X&!7vhp?gm51M6UGW2oZ6*M|V1=m`!7ZRLR7Om-CS| zHO-Qa9?1g?kcsum*#FTvI8(?P^cw(4nUxIfmI28-mepvU&9q!cn?lFx*Ji^TbXqe< zhs2>sYm&@2AJ-w;B|#8wnOLVu8lAYHG#$CKL+}S(i1o;5sq4nYs&+U%cA8JjES@aB zGIRY{zIl3LV2mI#x_P}JibJ1XfZHcp!$gFv;dDB%=8f6pHd07XWSwVI^8MSL^^heQ zf+KMNL$$#d6!G+y65eNE^O&6-*19`^Tjq!Ii)g#r%}vi>P_~%54Vyol$NQ%bUc>p5 z5h#iVQ548h8Cbe5kIVMHR}EEDV5f|eq6U_4oPoRtL+EOc149=>uDKkjQFR|;6XCJ!rLEaF6ZZb;a_ zqn3!+D9~-zo-F-AF$Ns}s);LRIQU6Dc5OY0-P@{oSlE@gPGmG*$V>`Yg9?}1lgNfh zOD}C`ks#UN#?rsxY=kyDh40Jj@XLWFe7U0UVn&m`4R!V_x;m|#ctIY=D%RN59{7~mT z#U+ZBL{u|K{VL`x&c)KT`Mlr!M+Z8?eMn1BLE*eiKK5%xBhDOdgv(9q=%lKwA*Ub< ztGCSM@^1RlH`jLX_uP^YLRnt^J`y+?_OkQUW0WP7-V|g>FhW_AgH#_1k(rZfjRGCv zD30yBh}M=)xG1xdU=|Hb@MGgU3$2l{VSr41RRrIa*TNfc4b&SZUKq7H`$(Pf9O zm^_A9Ad7_dwdhxgW@=I1N$raFhXY&h^*LQj^;GZ#(7 zs*Q7af5=yV47iO9LqknF4wl#O6lNbdRu2Z>Z`cy%K6b6}r@Qgw`dP>=${__?-%Zg3^VHeH0(HX6Yw|3()tChQ+DtG0000 - - - -## 2、测试准备 -因为 `JMeter` 是使用 `JAVA` 写的,所以使用 `JMeter` 之前,先安装 `JAVA` 环境。 -安装好后,在 `bin` 文件夹下,点击 `jmeter.bat` 启动程序。 -启动之后会有两个窗口,一个`cmd`窗口,一个`JMeter`的 `GUI`。前面不要忽略`CMD`窗口的提示信息,不要关闭它。 -注意:使用`API`模式,不要使用`GUI`模式。 - - -## 3、测试配置 -本地发布后的 `windows` 环境,直接用 `kestrel` 启动。 -线程数:100 -循环数:1000 -HTTP默认值:协议:`http`;服务器或IP:`localhost`;端口号:`9291`; -HTTP请求:方法:GET;路径:`/api/blog/ApacheTestUpdate` -HTTP信息请求管理器:无 -响应断言:无 - - - -## 4、项目初始化 -目前采用 `Blog.Core` 默认的配置, -只开启了内存 `AOP` , -其他的都是默认的,然后也把任务调度也关闭了, -最后注意要把 `IP限流`给关闭,不然压测没效果,因为限流了: - - - -## 5、压测过程 - -##### 第一阶段 - - - - -##### 第二阶段 - - - - -##### 第三阶段 - - - - -##### 第四阶段(压测后,检测内存是否降低,20m后) - - - -##### 第五阶段(停止压测1h后) - - - -## 6、测试结果 -内存方面,`100*1000` 的 **压测过程中** (写操作),项目保证所占内存在 `350m~500m` 之间 -然后停止一个小时后,内存将为`150m~200m`: - - - - - - -## 7、压测配置文件下载 - [配置文件](https://img.neters.club/doc/blogcore_blog_ApacheTestUpdate.jmx) - 下载后,导入到工具里,可以直接测试,察看结果树。 - -## 8、Docker 镜像 - 已经提交到 `docker hub` 自行拉取操作即可: - ``` - docker pull laozhangisphi/apkimg:latest - ``` diff --git a/.docs/contents/QQ/README.md b/.docs/contents/QQ/README.md deleted file mode 100644 index 31b7f909..00000000 --- a/.docs/contents/QQ/README.md +++ /dev/null @@ -1,18 +0,0 @@ -## 开源社区 - -bcvp - -[https://github.com/BaseCoreVueProject/Home](https://github.com/BaseCoreVueProject/Home) - -Base netCore (Vue) Project Team, -基于Net/Core 和Vue(react/ng),快速搭建 MVC & SPA 及微服务应用 -如果你有关于dotNet/core 的,不错的,可以正常运行,且一年内维护的,均可以加入。 -唯一宗旨:我们来自社区,服务社区,反哺社区。 - - - -## 微信公众号 - -公众号 - - diff --git a/.docs/contents/README.md b/.docs/contents/README.md deleted file mode 100644 index 597f16d9..00000000 --- a/.docs/contents/README.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -home: true -heroImage: /bcvphomelogo.png -actionText: 快速上手 → -actionLink: /guide/ -features: -- title: 详尽的文档 - details: 通过详细的文章和视频讲解,将知识点各个击破,入门ASP.Net Core不再难 -- title: 强大的社区 - details: 通过 QQ 群,和数千位同业大佬一起切磋交流。 -- title: 丰富的内容 - details: 框架涵盖ASP.Net Core开发中常见的基本知识点,不仅适合初学者入门,同时也适用于企业级别的开发。 -footer: MIT Licensed | Copyright © 2018-2020-老张的哲学 Powered by VUEPRESS on CentOS 7.6 ---- \ No newline at end of file diff --git a/.docs/contents/Update/README.md b/.docs/contents/Update/README.md deleted file mode 100644 index 870f98ec..00000000 --- a/.docs/contents/Update/README.md +++ /dev/null @@ -1,198 +0,0 @@ - -## 更新日志 - - -### 2021-08-21 - -重要功能增加:项目增加 `Apollo` 配置中心; - -### 2021-08-03 - -重要功能增加:项目增加 `ES` 搜索,增加 `Serilog` 使用 `tcp` 的方式自定义格式化,写入 `elk` 的实现; - -### 2021-06-28 - -功能增加:项目增加 `nacos` 配置,支持将项目注册到 `nacos` 服务中心,搭建微服务之子服务; - -### 2021-06-04 - -小功能更新:执行的时候,将 `Sql` 日志输出到控制台,方便查看,支持配置文件关闭; - -### 2021-05-01 - -组件更新:多项日志中间件,由自写组件,转为使用`serilog`组件记录日志; - -### 2021-03-03 - -项目目录调整:新增测试文件夹和模板文件夹; - -### 2021-02-09 - -重大项目更新:新增建行聚合支付; - -### 2021-01-11 - -更新:优化任务调度功能,新增暂停和停止; - -### 2020-12-02 - -更新:新增调用`MongoDB`功能,功能可用待完善中; - - -### 2020-11-19 -> 重大内容更新:更新项目模板 `Update Blog.Core.Webapi.Template.2.5.2.nupkg` -> 主要内容:1、泛型主键;2、通过测试中间件;3、`RabbitMQ`消息队列 - - -### 2020-11-18 - -项目更新:新增`RabbitMQ`消息队列和`EventBus`事件总线,功能可用待完善。 - - -### 2020-11-11 - -项目重大更新:更新至`.NET 5.0`。 - - -### 2020-11-05 - -项目更新:增加`测试用户`中间件,通过一键操作可以跳过权限限制,方便调试,文章[使用测试用户中间件](http://apk.neters.club/api/Blog/GoUrl?id=156)。 - - - -### 2020-10-11 - -项目更新:设计泛型主键功能,可以在项目初始化的时候设计主键类型。 - - - -### 2020-09-18 - -项目更新:更新项目模板 `Update Blog.Core.Webapi.Template.2.2.3.nupkg` 。 -> 1、增加 `Redis` 消息队列功能; - - - -### 2020-09-04 - -项目更新:增加 `Redis` 消息队列功能; - - -### 2020-08-06 - -项目更新:更新项目模板 `Update Blog.Core.Webapi.Template.2.2.0.nupkg` 。 -> 1、根据解决方案名,来自动导入model; -> 2、单独封装服务扩展层 `Blog.Core.Extensions` ; -> 3、代码生成器,支持控制器文件的生成; -> 4、弱化仓储层,用泛型仓储基类注入服务; - - - - -### 2020-08-01 - -> 重大结构更新:弱化仓储层,通过泛型仓储基类,来实现仓储服务注入,并去掉`Blog.Core.IRepository` 接口层; - -### 2020-07-03 - -> 更新:`DbFirstController` 生成四层文件,目前新增支持 `控制器Controller` 文件的输出; - - -### 2020-06-22 - -> 项目更新:将服务扩展和自定义中间件,单独封装一层 `Blog.Core.Extensions` ,更解耦。 - - - -### 2020-06-08 - -> 简单项目更新:生成数据库表结构的时候,利用反射机制,自动生成固定命名空间 `Blog.Core.Model.Models` 下的全部实体. -> 同时判断表是否存在,如果存在下次不再重复生成。 - - -### 2020-06-06 - -项目更新:更新项目模板 `Update Blog.Core.Webapi.Template.2.1.0.nupkg` [1a726f8](https://github.com/anjoy8/Blog.Core/commit/1a726f890e527c978982071462e82db4478632f0),更新项目即可 。 -> 1、配置内容展示到控制台; -> 2、简化封装 `Startup.cs` 类文件; -> 3、`DbFirst` 模式支持多库模式; -> 4、`Log4net` 讲异常和 `Info` 分开; -> 5、修复 `BlogLogAop` 偶尔卡顿问题; -> 6、将生成种子数据和任务调度功能,封装到中间件; -> 7、获取当前项目在服务器中的运行信息; -> 8、删除所有的不需要的 `using` 指令; - - - - -### 2020-05-29 -项目启动开启 `QuzrtzNet` 调度任务,并且在 `Admin` 后台管理中配置操作界面; -> 内容更新:封装生成种子数据的入口方法; - - - -### 2020-05-12 -修复:支持多库模式下,生成项目模板代码 `DbFirstController` [102c6d6](https://github.com/anjoy8/Blog.Core/commit/102c6d6bfcafd06bf5241844759dea5e7a6815da) -> 注意:`T4` 模板不能此功能,一次只能一个数据库,且只能 `SqlServer` - - -### 2020-05-07 -> 重大内容更新:更新项目模板 `Update Blog.Core.Webapi.Template.2.1.0.nupkg` [7f64fde](https://github.com/anjoy8/Blog.Core/commit/7f64fde5507f7a8572372dcadb6af5110bd37d68) - - -### 2020-05-06 -> 重大内容更新:优化Log4Net使用方案,完美配合 `NetCore` 官方的 `ILogger`, [ecaffb6](https://github.com/anjoy8/Blog.Core/commit/ecaffb66bdf10a90c087d01e6e817e54f23a97d4) - - -### 2020-05-01 - -> 重要内容更新:配合Admin全部完成按钮级别权限,更新初始化种子数据 - -### 2020-04-27 - -增加功能:配合前端Admin,增加页面 `KeepAlive` 功能; -增加功能:增加 `Sql` 语句查询Demo,支持返回 `DataTable`; - - -### 2020-04-25 - -增加功能:`Http api` 接口调用,满足微服务需求 -> 重要内容更新:优化 `Appsettings.app()` 方法,通过官方 `IConfiguration` 接口来获取DBS连接字符串; -> 优化 `BlogLogAOP.cs` - - -### 2020-04-15 - -> 重大内容更新:更新项目模板 `Update Blog.Core.Webapi.Template.1.11.30.nupkg` - - -### 2020-04-14 -> 重大内容更新:主分支,可以通过配置,一键切换JWT和Ids4认证授权模式 - - -### 2020-03-30 -> 重大内容更新:统一所有接口返回格式 - - -### 2020-03-25 -增加功能:支持读写分离(目前是三种模式:单库、多库、读写分离) -> 重大BUG更新:系统登录接口,未对用户软删除进行判断,现已修复 -> API: /api/login/GetJwtToken3 -> Code: await _sysUserInfoServices.Query(d => d.uLoginName == name && d.uLoginPWD == pass && d.tdIsDelete == false); - - - -### 2020-03-18 -增加功能:创建 Quartz.net 任务调度服务 - - -### 2020-01-09 -增加功能:项目迁移到IdentityServer4,统一授权认证中心 - - -### 2020-01-05 -增加功能:设计一个简单的中间件,可以查看所有已经注入的服务 - - -### 2020-01-04 -增加功能:Ip限流,防止过多刷数据 diff --git a/.docs/contents/guide/README.md b/.docs/contents/guide/README.md deleted file mode 100644 index 30950e0c..00000000 --- a/.docs/contents/guide/README.md +++ /dev/null @@ -1,121 +0,0 @@ -# W 文档指南 -## 亮点与优势 - -Blog.Core 是一个开箱即用的企业级权限管理应用框架。 -采用最新的前后端完全分离技术【 ASP.NET Core Api 5.0 + Vue 2.x 】。 -并结合 `IdentityServer4` ,可快速解决多客户端和多资源服务的统一认证与鉴权的问题。 - -## 其他资料 - -博客园,早期架构搭建:[博客园](https://www.cnblogs.com/laozhang-is-phi/p/9495618.html) -公众号,后期调整:[文章](https://mvp.neters.club/MVP_aspnetcore_2020/2020) -视频:[B站](https://www.bilibili.com/video/BV1vC4y1p7Za) - - -## 配套站点 - -本资源服务器,配合多个项目,构建前后端权限一体化平台,前端用 `VUE` 框架。 -前端-客户端:[预览](https://vueadmin.neters.club/)、[源码](https://github.com/anjoy8/Blog.Admin) -前端-管理后台:[预览](http://vueblog.neters.club/)、[源码](https://github.com/anjoy8/Blog.Vue) -认证平台:[预览](https://ids.neters.club/)、[源码](https://github.com/anjoy8/Blog.IdentityServer) - - -### 为什么选择 ASPNET.Core -1、【开源】`ASPNET.NET Core` 是由 `Microsoft` 和 `.NET` 社区在 `GitHub` 上开源并维护的一个跨平台(支持 Windows、macOS 和 Linux)的新一代高性能框架, -拥有十分广泛的社区与支持者,可用于构建web应用、物联网IOT应用和移动端应用。 -2、【高效】Asp.net core(.net core)来源于.net,很容易迁移,而且也很容易上手, -但是又是不同的一个框架,除了上述对.net开发者十分友好以外,相对于之前的.net项目,速度上有巨大的改进, -相比与原来的`Web(.net framework 4.6)`程序性能提升了`2300%`。跟`python`、`java`等相同环境比较,性能都要优越, -参考[www.techempower.com](https://www.techempower.com/benchmarks/)。 -3、【跨平台】可以在`Windows`、`Mac`和`Linux`构建和运行跨平台的`Asp.Net Core`应用。 -4、【云原生】在云原生领域拥有天然的优势,搭配Azure云服务,配合K8s,更好的实现分布式应用,以及微服务应用。 -5、【微服务】`ASP.NET Core`尤其适用于微服务架构,也就是说ASP.NET Core不仅适合于中小型项目而且还特别适合于大型,超大型项目。 -6、【大公司】目前国内采用`ASP.NET Core`的大公司比如腾讯、网易,国际的有Bing,GoDaddy,Stackoverflow,Adobe,Microsoft -7、【总结来说】,`java`支持的,`ASPNET.Core`都支持,而且更轻量级、更高效跨,并且对.net开发者十分友好,微服务案例成熟。 - - - -### 框架功能点 -1、丰富完整的接口文档,在查看的基础上,可以模拟前端调用,更方便。 -2、采用多层开发,隔离性更好,封装更完善。 -3、基于项目模板,可以一键创建自己的项目。 -4、搭配代码生成器,实现快速开发,节省成本。 -5、项目集成多库模式以及读写分离模式,可以同时处理多个数据库的不同模块,更快更安全。 -6、集成统一认证平台 `IdentityServer4` ,实现多个项目的统一认证管理,解决了之前一个项目, -一套用户的弊端,更适用微服务的开发。 -7、丰富的审计日志处理,方便线上项目快速定位异常点。 -8、支持自由切换多种数据库,Sqlite/SqlServer/MySql/PostgreSQL/Oracle; -9、支持 `Docker` 容器化开发,可以搭配 k8s 更好的实现微服务。 - - -### 应用领域 -1、【对接第三方api】项目通过`webapi`,可以快速对接第三方`api`服务,实现业务逻辑。 -2、【前后端分离】 采用的是`API`+前端的完全分离的开发模式,满足平时开发的所有需求, -你可以对接任何的自定义前端项目:无论是微信小程序,还是授权APP,无论是PC网页, -还是手机H5。 -3、【多项目】同时框架还集成了一套鉴权平台,采用IdentityServer4,可以快速的实现多个客户端的认证与授权服务, -从而大大的减少了平时的工作量,可以快速的进行产品迭代。 -4、【微服务】当然,因为采用的是API模式,所以同样适用于微服务项目,实现高并发的产品需求。 - - - -### 市场前景 -1、前后端分离模式已经是目前的主流开发模式,框架已经是一套可行的方案,开箱即用。 -2、拥有几十篇技术文档和3000人的技术社区,方便快捷的解决问题。 -3、目前已经有超过20多家公司在生产环境中使用,当然实际中更多,具体查看 [点击查看使用的情况](https://github.com/anjoy8/Blog.Core/issues/75)。 -4、同时可以搭配自己的业务,实现微服务的开发,在大数据高并发中,占有更好的优势。 -5、本项目直接作者由微软MVP“老张的哲学”出品,并长久维护,不会断更,有保障。 - - - -## 功能与进度 - -框架模块: -- [√] 采用`仓储+服务+接口`的形式封装框架; -- [√] 异步 async/await 开发; -- [√] 接入国产数据库ORM组件 —— SqlSugar,封装数据库操作; -- [√] 支持自由切换多种数据库,MySql/SqlServer/Sqlite/Oracle/Postgresql/达梦/人大金仓; -- [√] 实现项目启动,自动生成种子数据 ✨; -- [√] 五种日志记录,审计/异常/请求响应/服务操作/Sql记录等; -- [√] 支持项目事务处理(若要分布式,用cap即可)✨; -- [√] 设计4种 AOP 切面编程,功能涵盖:日志、缓存、审计、事务 ✨; -- [√] 支持 T4 代码模板,自动生成每层代码; -- [√] 或使用 DbFirst 一键创建自己项目的四层文件(支持多库); -- [√] 封装`Blog.Core.Webapi.Template`项目模板,一键重建自己的项目 ✨; -- [√] 搭配多个前端案例供参考和借鉴:Blog.Vue、Blog.Admin、Nuxt.tbug、Blog.Mvp.Blazor ✨; -- [√] 统一集成 IdentityServer4 认证 ✨; - -组件模块: -- [√] 提供 Redis 做缓存处理; -- [√] 使用 Swagger 做api文档; -- [√] 使用 MiniProfiler 做接口性能分析 ✨; -- [√] 使用 Automapper 处理对象映射; -- [√] 使用 AutoFac 做依赖注入容器,并提供批量服务注入 ✨; -- [√] 支持 CORS 跨域; -- [√] 封装 JWT 自定义策略授权; -- [√] 使用 Log4Net 日志框架,集成原生 ILogger 接口做日志记录; -- [√] 使用 SignalR 双工通讯 ✨; -- [√] 添加 IpRateLimiting 做 API 限流处理; -- [√] 使用 Quartz.net 做任务调度(目前单机多任务,集群调度暂不支持); -- [√] 支持 数据库`读写分离`和多库操作 ✨; -- [√] 新增 Redis 消息队列 ✨; -- [√] 新增 RabbitMQ 消息队列 ✨; -- [√] 新增 EventBus 事件总线 ✨; -- [√] 新增 实现聚合支付; -- [ ] 计划 - 数据部门权限; -- [ ] 计划 - ES 搜索; - -微服务模块: -- [√] 可配合 Docker 实现容器化; -- [√] 可配合 Jenkins 实现CI / CD; -- [√] 可配合 Consul 实现服务发现; -- [√] 可配合 Ocelot 实现网关处理; -- [√] 可配合 Nginx 实现负载均衡; -- [√] 可配合 Ids4 实现认证中心; - - -  - - - - diff --git a/.docs/contents/guide/cheat-sheet.md b/.docs/contents/guide/cheat-sheet.md deleted file mode 100644 index 36f95e36..00000000 --- a/.docs/contents/guide/cheat-sheet.md +++ /dev/null @@ -1,580 +0,0 @@ -# Z 主要知识点 - - - -## AOP - -本项目多处采用面向切面编程思想——AOP,除了广义上的过滤器和中间件以外,主要通过动态代理的形式来实现AOP编程思想,主要的案例共有四个,分别是: -1、服务日志AOP; -2、服务InMemory缓存AOP; -3、服务Redis缓存AOP; -4、服务事务AOP; - - -具体的代码可以在 `Blog.Core\Blog.Core\AOP` 文件夹下查看。 - -与此同时,多个AOP也设置了阀门来控制是否开启,具体的可以查看 `appsettings.json` 中的: - -``` - "AppSettings": { - "RedisCachingAOP": { - "Enabled": false, - "ConnectionString": "127.0.0.1:6319" - }, - "MemoryCachingAOP": { - "Enabled": true - }, - "LogAOP": { - "Enabled": false - }, - "TranAOP": { - "Enabled": false - }, - "SqlAOP": { - "Enabled": false - } - }, - -``` - -## Appsettings - -整个系统通过一个封装的操作类 `Appsettings.cs` 来控制配置文件 `appsettings.json` 文件, -操作类地址在:`\Blog.Core.Common\Helper` 文件夹下。 -具体的使用方法是: - -``` -Appsettings.app(new string[] { "AppSettings", "RedisCachingAOP", "Enabled" }) - -// 里边的参数,按照 appsettings.json 中设置的层级顺序来写,可以获取到指定的任意内容。 - -``` - - - -## AspNetCoreRateLimit - -系统使用 `AspNetCoreRateLimit` 组件来实现ip限流: -1、添加 `nuget` 包: -``` - -``` - -2、注入服务 `IpPolicyRateLimitSetup.cs` -``` -services.AddIpPolicyRateLimitSetup(Configuration); -``` - -3、配置中间件 -``` - // Ip限流,尽量放管道外层 - app.UseIpRateLimiting(); -``` - -4、配置数据 - -具体的内容,自行百度即可 -``` - "IpRateLimiting": { - "EnableEndpointRateLimiting": true, - "StackBlockedRequests": false, - "RealIpHeader": "X-Real-IP", - "ClientIdHeader": "X-ClientId", - "HttpStatusCode": 429,//返回状态码 - "GeneralRules": [//规则,结尾一定要带* - { - "Endpoint": "*", - "Period": "1m", - "Limit": 120 - }, - { - "Endpoint": "*:/api/blog*", - "Period": "1m", - "Limit": 30 - } - ] - - } -``` - - - -## Async-Await - -整个系统采用 async/await 异步编程,符合主流的开发模式, -特别是对多线程开发很友好。 - - - -## Authorization-Ids4 - -本系统 v2.0 版本(目前的系统已经集成 `ids4` 和 `jwt`,并且可以自由切换),已经支持了统一授权认证,和 `blog` 项目、`Admin` 项目、`DDD` 项目等一起,使用一个统一的认证中心。 - -具体的代码参考:`.\Blog.Core\Extensions` 文件夹下的 `Authorization_Ids4Setup.cs` ,注意需要引用指定的 `nuget` 包,核心代码如下: - -``` - //【认证】 - services.AddAuthentication(o => - { - o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; - o.DefaultChallengeScheme = nameof(ApiResponseHandler); - o.DefaultForbidScheme = nameof(ApiResponseHandler); - }) - // 2.添加Identityserver4认证 - .AddIdentityServerAuthentication(options => - { - options.Authority = Appsettings.app(new string[] { "Startup", "IdentityServer4", "AuthorizationUrl" }); - options.RequireHttpsMetadata = false; - options.ApiName = Appsettings.app(new string[] { "Startup", "IdentityServer4", "ApiName" }); - options.SupportedTokens = IdentityServer4.AccessTokenValidation.SupportedTokens.Jwt; - options.ApiSecret = "api_secret"; - - }) - - -``` - -### 如何在Swagger中配置Ids4? -很简单,直接在 `SwaggerSetup.cs` 中直接接入 `oauth、Implicit` 即可: - -``` - //接入identityserver4 - c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme - { - Type = SecuritySchemeType.OAuth2, - Flows = new OpenApiOAuthFlows - { - Implicit = new OpenApiOAuthFlow - { - AuthorizationUrl = new Uri($"{Appsettings.app(new string[] { "Startup", "IdentityServer4", "AuthorizationUrl" })}/connect/authorize"), - Scopes = new Dictionary { - { - "blog.core.api","ApiResource id" - } - } - } - } - }); - -``` - -然后在 `IdentityServer4` 项目中,做指定的修改,配置 `9291` 的回调地址: - -``` - new Client { - ClientId = "blogadminjs", - ClientName = "Blog.Admin JavaScript Client", - AllowedGrantTypes = GrantTypes.Implicit, - AllowAccessTokensViaBrowser = true, - - RedirectUris = - { - "http://vueadmin.neters.club/callback", - // 这里要配置回调地址 - "http://localhost:9291/oauth2-redirect.html" - }, - PostLogoutRedirectUris = { "http://vueadmin.neters.club" }, - AllowedCorsOrigins = { "http://vueadmin.neters.club" }, - - AllowedScopes = { - IdentityServerConstants.StandardScopes.OpenId, - IdentityServerConstants.StandardScopes.Profile, - "roles", - "blog.core.api" - } - }, - -``` - -然后再 `Swagger` 中,配置登录授权: - -swagger - - -## Authorization-JWT - -如果你不想使用 `IdentityServer4` 的话,也可以使用 `JWT` 认证,同样是是`Blog.Core\Blog.Core\Extensions` 文件夹下的 `AuthorizationSetup.cs` 中有关认证的部分: - -``` - 1.添加JwtBearer认证服务 -.AddJwtBearer(o => -{ - o.TokenValidationParameters = tokenValidationParameters; - o.Events = new JwtBearerEvents - { - OnAuthenticationFailed = context => - { - // 如果过期,则把<是否过期>添加到,返回头信息中 - if (context.Exception.GetType() == typeof(SecurityTokenExpiredException)) - { - context.Response.Headers.Add("Token-Expired", "true"); - } - return Task.CompletedTask; - } - }; -}) - -``` - - -## AutoMapper - -使用 `AutoMapper` 组件来实现 `Dto` 模型的传输转换,具体的用法,可以查看: -`Blog.Core\Blog.Core\Extensions` 文件夹下的 `AutoMapperSetup.cs` 扩展类, -通过引用 `AutoMapper` 和 `AutoMapper.Extensions.Microsoft.DependencyInjection` 两个 `nuget` 包,并设置指定的 `profile` 文件,来实现模型转换控制。 - -``` -// 比如如何定义: - public class CustomProfile : Profile - { - /// - /// 配置构造函数,用来创建关系映射 - /// - public CustomProfile() - { - CreateMap(); - CreateMap(); - } - } - - -// 比如如何使用 -models = _mapper.Map(blogArticle); - -``` - -具体的查看项目中代码即可。 - - - - -## CORS - -在线项目使用的是 `nginx` 跨域代理,但是同时也是支持 `CORS` 代理: -1、注入服务 `services.AddCorsSetup();` 具体代码 `Blog.Core\Blog.Core\Extensions` 文件夹下的 `CorsSetup.cs` 扩展类; -2、配置中间件 `app.UseCors("LimitRequests");` ,要注意中间件顺序; -3、配置自己项目的前端端口,通过在 `appsettings.json` 文件中配置自己的前端项目 `ip:端口` ,来实现跨域: - -``` - "Startup": { - "Cors": { - "IPs": "http://127.0.0.1:2364,http://localhost:2364,http://localhost:8080,http://localhost:8021,http://localhost:1818" - } - }, - -``` - - -## DI-AutoFac - -项目使用了依赖注入,除了原生的依赖注入以外,更多的使用的是第三方组件 `Autofac` : -1、引用依赖包: -``` - - - -``` -主要是第一个 `nuget` 包,下边的是为了实现动态代理 `AOP` 操作; - -2、项目之间采用引用解耦的方式,通过反射来注入服务层和仓储层的程序集 `dll` 来实现批量注入,更方便,以后每次新增和修改 `Service` 层和 `Repository` 层,只需要 `F6` 编译一下即可,具体代码查看 `Startup.cs`: - -``` - - - // 注意在CreateDefaultBuilder中,添加Autofac服务工厂 - public void ConfigureContainer(ContainerBuilder builder) - { - var basePath = Microsoft.DotNet.PlatformAbstractions.ApplicationEnvironment.ApplicationBasePath; - //builder.RegisterType().As(); - - - #region 带有接口层的服务注入 - - - var servicesDllFile = Path.Combine(basePath, "Blog.Core.Services.dll"); - var repositoryDllFile = Path.Combine(basePath, "Blog.Core.Repository.dll"); - - if (!(File.Exists(servicesDllFile) && File.Exists(repositoryDllFile))) - { - throw new Exception("Repository.dll和service.dll 丢失,因为项目解耦了,所以需要先F6编译,再F5运行,请检查 bin 文件夹,并拷贝。"); - } - - - - // 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()) - { - builder.RegisterType(); - cacheType.Add(typeof(BlogCacheAOP)); - } - if (Appsettings.app(new string[] { "AppSettings", "TranAOP", "Enabled" }).ObjToBool()) - { - builder.RegisterType(); - cacheType.Add(typeof(BlogTranAOP)); - } - if (Appsettings.app(new string[] { "AppSettings", "LogAOP", "Enabled" }).ObjToBool()) - { - builder.RegisterType(); - cacheType.Add(typeof(BlogLogAOP)); - } - - // 获取 Service.dll 程序集服务,并注册 - var assemblysServices = Assembly.LoadFrom(servicesDllFile); - builder.RegisterAssemblyTypes(assemblysServices) - .AsImplementedInterfaces() - .InstancePerDependency() - .EnableInterfaceInterceptors()//引用Autofac.Extras.DynamicProxy; - .InterceptedBy(cacheType.ToArray());//允许将拦截器服务的列表分配给注册。 - - // 获取 Repository.dll 程序集服务,并注册 - var assemblysRepository = Assembly.LoadFrom(repositoryDllFile); - builder.RegisterAssemblyTypes(assemblysRepository) - .AsImplementedInterfaces() - .InstancePerDependency(); - - #endregion - - #region 没有接口层的服务层注入 - - //因为没有接口层,所以不能实现解耦,只能用 Load 方法。 - //注意如果使用没有接口的服务,并想对其使用 AOP 拦截,就必须设置为虚方法 - //var assemblysServicesNoInterfaces = Assembly.Load("Blog.Core.Services"); - //builder.RegisterAssemblyTypes(assemblysServicesNoInterfaces); - - #endregion - - #region 没有接口的单独类 class 注入 - - //只能注入该类中的虚方法 - builder.RegisterAssemblyTypes(Assembly.GetAssembly(typeof(Love))) - .EnableClassInterceptors() - .InterceptedBy(cacheType.ToArray()); - - #endregion - - - // 这里和注入没关系,只是获取注册列表,请忽略 - tsDIAutofac.AddRange(assemblysServices.GetTypes().ToList()); - tsDIAutofac.AddRange(assemblysRepository.GetTypes().ToList()); - } - -``` - -3、然后 `Program.cs` 中也要加一句话:` .UseServiceProviderFactory(new AutofacServiceProviderFactory()) //<--NOTE THIS ` - - - -## DI-NetCore - -除了主要的 `Autofac` 依赖注入以外,也减少的使用了原生的依赖注入方式,很简单,比如这样的: -``` - - services.AddSingleton(); - // 注入权限处理器 - services.AddScoped(); - services.AddSingleton(permissionRequirement); -``` - - -## Filter - -项目中一共有四个过滤器 -``` -1、GlobalAuthorizeFilter.cs —— 全局授权配置,添加后,就可以不用在每一个控制器上添加 [Authorize] 特性,但是3.1版本好像有些问题,【暂时放弃使用】; -2、GlobalExceptionFilter.cs —— 全局异常处理,实现 actionContext 级别的异常日志收集; -3、GlobalRoutePrefixFilter.cs —— 全局路由前缀公约,统计在路由上加上前缀; -4、UseServiceDIAttribute.cs —— 测试注入,【暂时无用】; -``` -文件地址在 `.\Blog.Core\Filter` 文件夹下,其中核心的是 `2` 个,重点使用的是 `1` 个 —— 全局异常错误日志 `GlobalExceptionsFilter`: -通过注册在 `MVC` 服务 `services.AddControllers()` 中,实现全局异常过滤: -``` - services.AddControllers(o => - { - // 全局异常过滤 - o.Filters.Add(typeof(GlobalExceptionsFilter)); - // 全局路由权限公约 - //o.Conventions.Insert(0, new GlobalRouteAuthorizeConvention()); - // 全局路由前缀,统一修改路由 - o.Conventions.Insert(0, new GlobalRoutePrefixFilter(new RouteAttribute(RoutePrefix.Name))); - }) -``` - - - -## Framework - -项目采用 `服务+仓储+接口` 的多层结构,使用依赖注入,并且通过解耦项目,较完整的实现了 `DIP` 原则: -高层模块不应该依赖于底层模块,二者都应该依赖于抽象。 -抽象不应该依赖于细节,细节应该依赖于抽象。 - -同时项目也封装了: -`CodeFirst` 初始化数据库以及数据; -`DbFirst` 根据数据库(支持多库),生成多层代码,算是简单代码生成器; -其他功能,[核心功能与进度](http://apk.neters.club/.doc/guide/#%E5%8A%9F%E8%83%BD%E4%B8%8E%E8%BF%9B%E5%BA%A6) - - - - -## Log - -通过集成 `Log4Net` 组件,完美配合 `NetCore` 官方的 `ILogger` 接口,实现对日志的管控,引用 `nuget` 包 `Microsoft.Extensions.Logging.Log4Net.AspNetCore`: -Program.cs -``` - webBuilder - .UseStartup() - .ConfigureLogging((hostingContext, builder) => - { - //该方法需要引入Microsoft.Extensions.Logging名称空间 - builder.AddFilter("System", LogLevel.Error); //过滤掉系统默认的一些日志 - builder.AddFilter("Microsoft", LogLevel.Error);//过滤掉系统默认的一些日志 - - //添加Log4Net - //var path = Directory.GetCurrentDirectory() + "\\log4net.config"; - //不带参数:表示log4net.config的配置文件就在应用程序根目录下,也可以指定配置文件的路径 - //需要添加nuget包:Microsoft.Extensions.Logging.Log4Net.AspNetCore - builder.AddLog4Net(); - }); - -``` - -然后直接在需要的地方注入使用,比如在控制器中 -` public UserController(ILogger logger)` - -然后就可以使用了。 - -> 注意:日志 其实是分为两部分的: -> netcore输出(控制台、输出窗口等) 和 `ILogger` 持久化 -> 两者对应配置也不一样,就比如上边的过滤,是针对日志持久化的,如果想要对控制台进行控制,需要配置 `appsettings.json` 中的 `Logging` 节点 - - -## MemoryCache - -精力有限,还是更新中... -如果你愿意帮忙,可以直接在GitHub中,提交pull request, -我会在后边的贡献者页面里,列出你的名字和项目地址做推广 - -## Middleware - -精力有限,还是更新中... -如果你愿意帮忙,可以直接在GitHub中,提交pull request, -我会在后边的贡献者页面里,列出你的名字和项目地址做推广 -## MiniProfiler - -精力有限,还是更新中... -如果你愿意帮忙,可以直接在GitHub中,提交pull request, -我会在后边的贡献者页面里,列出你的名字和项目地址做推广 - -## publish -精力有限,还是更新中... -如果你愿意帮忙,可以直接在GitHub中,提交pull request, -我会在后边的贡献者页面里,列出你的名字和项目地址做推广 - - -## Redis - -精力有限,还是更新中... -如果你愿意帮忙,可以直接在GitHub中,提交pull request, -我会在后边的贡献者页面里,列出你的名字和项目地址做推广 -## Repository -精力有限,还是更新中... -如果你愿意帮忙,可以直接在GitHub中,提交pull request, -我会在后边的贡献者页面里,列出你的名字和项目地址做推广 -## SeedData - -精力有限,还是更新中... -如果你愿意帮忙,可以直接在GitHub中,提交pull request, -我会在后边的贡献者页面里,列出你的名字和项目地址做推广 -## SignalR - -精力有限,还是更新中... -如果你愿意帮忙,可以直接在GitHub中,提交pull request, -我会在后边的贡献者页面里,列出你的名字和项目地址做推广 -## SqlSugar - -精力有限,还是更新中... -如果你愿意帮忙,可以直接在GitHub中,提交pull request, -我会在后边的贡献者页面里,列出你的名字和项目地址做推广 -## SqlSugar-Codefirst&DataSeed - -精力有限,还是更新中... -如果你愿意帮忙,可以直接在GitHub中,提交pull request, -我会在后边的贡献者页面里,列出你的名字和项目地址做推广 -## SqlSugar-SqlAOP - -精力有限,还是更新中... -如果你愿意帮忙,可以直接在GitHub中,提交pull request, -我会在后边的贡献者页面里,列出你的名字和项目地址做推广 -## Swagger - -精力有限,还是更新中... -如果你愿意帮忙,可以直接在GitHub中,提交pull request, -我会在后边的贡献者页面里,列出你的名字和项目地址做推广 -## T4 - -项目集成 `T4` 模板 `.\Blog.Core.FrameWork` 层,目的是可以一键生成项目模板代码。 -1、需要在 `DbHelper.ttinclude` 中配置连接数据库连接字符串; -2、针对每一层的代码,就去指定的 `.tt` 模板,直接 `CTRL+S` 保存即可; - -> 注意,目前的代码是 `SqlServer` 版本的,其他数据库版本的,可以去群文件查看。 - - -## Test-xUnit - -项目简单使用了单元测试,通过 `xUnit` 组件,具体的可以查看 `Blog.Core.Tests` 层相关代码。 -目前单元测试用例还比较少,大家可以自行添加。 - - -## Temple-Nuget - -本项目封装了 `Nuget` 自定义模板,你可以根据这个模板,一键创建自己的项目名,具体的操作,可以双击项目根目录下的 `CreateYourProject.bat` ,可以参考 [#如何项目重命名](http://apk.neters.club/.doc/guide/getting-started.html#%E5%A6%82%E4%BD%95%E9%A1%B9%E7%9B%AE%E9%87%8D%E5%91%BD%E5%90%8D) - -同时,你也可以再 `Nuget` 管理器中,搜索到: -nuget - - - -## UserInfo - - -项目中封装了获取用户信息的代码: -在 `.\Blog.Core.Common\HttpContextUser` 文件夹下 `AspNetUser.cs` 实现类和 `IUser.cs` 接口。 - -如果使用,首先需要注册相应的服务,参见:`.\Blog.Core\Extensions` 文件夹下的 `HttpContextSetup.cs`; -然后,就直接在控制器构造函数中,注入接口 `IUser` 即可; - -> `注意`: -> 1、如果要想获取指定的服务,必须登录,也就是必须要在 `Header` 中传递有效 `Token` ,这是肯定的。 -> 2、如果要获取用户信息,一定要在中间件 `app.UseAuthentication()` 之后(不要问为什么),控制器肯定在它之后,所以能获取到; -> 3、`【并不是】`一定需要添加 `[Authorize]` 特性,如果你加了这个特性,可以直接获取,但是如果不加,可以从我的 `AspNetUser.cs` 方法中,有一个直接从 `Header` 中解析的方法 `List GetUserInfoFromToken(string ClaimType);`: - -``` - public string GetToken() - { - return _accessor.HttpContext.Request.Headers["Authorization"].ObjToString().Replace("Bearer ", ""); - } - - public List GetUserInfoFromToken(string ClaimType) - { - - var jwtHandler = new JwtSecurityTokenHandler(); - if (!string.IsNullOrEmpty(GetToken())) - { - JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(GetToken()); - - return (from item in jwtToken.Claims - where item.Type == ClaimType - select item.Value).ToList(); - } - else - { - return new List() { }; - } - } - -``` diff --git a/.docs/contents/guide/function-sheet.md b/.docs/contents/guide/function-sheet.md deleted file mode 100644 index d81ad13f..00000000 --- a/.docs/contents/guide/function-sheet.md +++ /dev/null @@ -1,471 +0,0 @@ -# H 核心功能一览表 - -## 一、表结构解析 - -`Blog.Core` 项目共包含四部分的数据库表结构,分别是:用户角色管理部分、接口菜单权限管理部分、博客文章管理部分、以及其他不重要部分。 -> 注意:目前不提供与维护数据库数据,直接通过 `SeedData` 生成种子数据; - -### 1、用户角色管理部分[必须] -主要是三个表:分别对应用户表(sysUserInfo)、角色表(Role)、用户角色关系表(UserRole)。 - -usermanager - - - -### 2、接口菜单权限管理部分[必须] - -主要是四个表:分别对应接口表(Module)、菜单表(Permission)、接口菜单关系表(ModulePermission)暂时没用到、角色接口菜单关系表(RoleModulePermission)。 - -permissionmanager - - - - -### 3、博客文章管理部分[可选] -主要是三个表:分别对应博客表(BlogArticle)、Bug专题表(Topic)、Bug内容表(TopicDetail)。 - -blogmanager - - - - -### 4、其他不重要部分 - -主要是三个表:分别对应Job调度表(TasksQz)、密码库表(PasswordLib)、操作日志表(OperateLog)、广告表(Advertisement)、公告表(Guestbook)。 - -othersmanager - - - - - - -## 二、日志记录 - -本框架涵盖了不同领域的日志记录,共五个,分别是: - -1、全局异常日志 - - 开启方式:无需操作。 - 文件路径:web目录下,Log/GlobalExcepLogs_{日期}.log。 - 功能描述:记录项目启动后出现的所有异常日志,不包括中间件中异常。 - - -2、IP 请求日志 - - 开启方式:无需操作。 - 文件路径:web目录下,Log/RequestIpInfoLog.log。 - 功能描述:记录项目启动后客户端请求的ip和接口信息。 - 举例来说: - {"Ip":"xxx.xx.xx.x","Url":"/api/values","Datetime":"2020-01-06 18:02:19","Date":"2020-01-06","Week":"周一"} - - -3、用户API访问日志 - - 开启方式:appsettings.json -> Middlewar -> RecordAccessLogs 节点为true。 - 文件路径:web目录下,Log/RecordAccessLogs_{日期}.log。 - 功能描述:记录项目启动后客户端所有的API访问日志,包括参数、body以及用户信息。 - - -4、服务层请求响应AOP日志 - - 开启方式:appsettings.json -> AppSettings -> LogAOP 节点为true。 - 文件路径:web目录下,Log/AOPLog.log。 - 功能描述:记录项目启动请求api后,所有的service层日志,包括方法名、参数、响应结果或用户(非必须)。 - - -5、数据库操作日志 - - 开启方式:appsettings.json -> AppSettings -> SqlAOP 节点为true。 - 文件路径:web目录下,Log/SqlLog.log。 - 功能描述:记录项目启动请求api并访问service后,所有的db操作日志,包括Sql参数与Sql语句。 - 举例来说: - -------------------------------- - 1/6/2020 6:13:04 PM| - 【SQL参数】:@bID0:1 - 【SQL语句】:SELECT `bID`,`bsubmitter`,`btitle`,`bcategory`,`bcontent`,`btraffic`,`bcommentNum`,`bUpdateTime`,`bCreateTime`,`bRemark`,`IsDeleted` FROM `BlogArticle` WHERE ( `bID` = @bID0 ) - - - ## 三、控制台信息展示 - - 配置 - - - - ## 四、Nginx一览表 - - - -``` -#user nobody; -worker_processes 1; - -#error_log logs/error.log; -#error_log logs/error.log notice; -#error_log logs/error.log info; - -#pid logs/nginx.pid; -events { - worker_connections 1024; -} - -http { - include mime.types; - default_type application/octet-stream; - server_names_hash_bucket_size 64; - - #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' - # '$status $body_bytes_sent "$http_referer" ' - # '"$http_user_agent" "$http_x_forwarded_for"'; - - #access_log logs/access.log main; - sendfile on; - #tcp_nopush on; - - #keepalive_timeout 0; - keepalive_timeout 600; - proxy_read_timeout 600; - proxy_send_timeout 600; - - proxy_buffer_size 128k; - proxy_buffers 32 32k; - proxy_busy_buffers_size 128k; - - #gzip on; - - ###################################################################### - server { - listen 80; - server_name www.neters.club; - - #charset koi8-r; - - #access_log logs/host.access.log main; - location / { - root C:\code\Code\Neters\home; - index index.html index.htm; - } - } - - server { - listen 80; - server_name neters.club; - - #charset koi8-r; - - #access_log logs/host.access.log main; - location / { - root C:\code\Code\Neters\home; - - index index.html index.htm; - } - } - - server { - listen 80; - server_name ids.neters.club; - rewrite ^(.*)$ https://$host$1 permanent;#把http的域名请求转成https,第二种写法在此节的末端 - - #charset koi8-r; - - #access_log logs/host.access.log main; - location / { - #proxy_pass http://localhost:5004; - root html; - index index.html index.htm; - } - } - - server { - listen 443 ssl; - server_name ids.neters.club; #网站域名,和80端口保持一致 - ssl on; - ssl_certificate 1_ids.neters.club_bundle.crt; #证书公钥 - ssl_certificate_key 2_ids.neters.club.key; #证书私钥 - ssl_session_cache shared:SSL:1m; - ssl_session_timeout 5m; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - ssl_ciphers ECDH:AESGCM:HIGH:!RC4:!DH:!MD5:!3DES:!aNULL:!eNULL; - ssl_prefer_server_ciphers on; - - error_page 497 https://$host$uri?$args; - - location / { - proxy_pass http://localhost:5004; - proxy_redirect off; - proxy_set_header Host $http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - - proxy_set_header Cookie $http_cookie; - #proxy_cookie_path - chunked_transfer_encoding off; - } - } - - server { - listen 80; - server_name apk.neters.club; - - #charset koi8-r; - - #access_log logs/host.access.log main; - location / { - root html; - proxy_pass http://localhost:9291; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection keep-alive; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_cache_bypass $http_upgrade; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - - index index.html index.htm; - } - - location /.doc/ { - proxy_pass http://docs.neters.club/; - } - } - - server { - listen 80; - server_name docs.neters.club; - - location / { - root C:\code\Code\Blog.Core\.docs\contents\.vuepress\dist; - index index.html index.htm; - } - } - - server { - listen 80; - server_name vueadmin.neters.club; - - location / { - try_files $uri $uri/ /index.html; - root C:\code\Code\Blog.Admin\distis; - #proxy_pass http://localhost:2364; - index index.html index.htm; - } - - location /api/ { - rewrite ^.+apb/?(.*)$ /$1 break; - include uwsgi_params; - proxy_pass http://localhost:9291; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - #proxy_set_header Connection "upgrade"; - #proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - } - - location /api2/ { - rewrite ^.+apb/?(.*)$ /$1 break; - include uwsgi_params; - proxy_pass http://localhost:9291; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - } - - location /images/ { - include uwsgi_params; - proxy_pass http://localhost:9291; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - #proxy_set_header Connection "upgrade"; - #proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - } - location /.doc/ { - proxy_pass http://docsadmin.neters.club/; - } - - error_page 404 /404.html; - - # redirect server error pages to the static page /50x.html - # - error_page 500 502 503 504 /50x.html; - location = /50x.html { - root html; - } - } - - server { - listen 80; - server_name docsadmin.neters.club; - - location / { - root C:\code\Code\Blog.Admin\.doc\contents\.vuepress\dist; - index index.html index.htm; - } - } - - - server { - listen 80; - server_name ddd.neters.club; - location / { - proxy_pass http://localhost:4773; - index index.php index.html index.htm; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection keep-alive; - proxy_set_header Host $host; - proxy_cache_bypass $http_upgrade; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - - } - } - - - server { - listen 80; - server_name ask.neters.club; - - #charset koi8-r; - - #access_log logs/host.access.log main; - location / { - root html; - proxy_pass http://localhost:5020; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - #proxy_set_header Connection "upgrade"; - #proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - index index.html index.htm; - } - } - - - server { - listen 80; - server_name vueblog.neters.club; - - location / { - try_files $uri $uri/ /index.html; - root C:\code\Code\Blog.Vue\dist; - index index.html index.htm; - } - - - location /api { - rewrite ^.+apb/?(.*)$ /$1 break; - include uwsgi_params; - proxy_pass http://localhost:9291; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - } - - - location /images { - include uwsgi_params; - proxy_pass http://localhost:9291; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - } - - error_page 404 /404.html; - - # redirect server error pages to the static page /50x.html - # - error_page 500 502 503 504 /50x.html; - location = /50x.html { - root html; - } - } - - upstream nodenuxt { - server 127.0.0.1:3089; # nuxt 项目监听PC端端口 - keepalive 64; - } - server { - listen 80; - server_name tibug.neters.club; - - location / { - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_set_header Host $host; - proxy_set_header X-Nginx-Proxy true; - proxy_cache_bypass $http_upgrade; - proxy_pass http://nodenuxt; - } - - error_page 500 502 503 504 /50x.html; - location = /50x.html { - root html; - } - } - - server { - listen 80; - server_name jwt.neters.club; - - location / { - root C:\code\Code\jwttoken; - index index.html index.htm; - } - - error_page 404 /404.html; - - # redirect server error pages to the static page /50x.html - # - error_page 500 502 503 504 /50x.html; - location = /50x.html { - root html; - } - } -} - -``` -> 这里说明下,我的 `Nginx` 文件中,`Ids4` 项目强制使用 `Https` ,采用的是直接跳转,这也是一个办法,当然还有第二种办法(感谢 `tibos`): -``` -server { - listen 80; - server_name admin.wmowm.com; - location / { - proxy_pass http://localhost:9002; - index index.php index.html index.htm; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection keep-alive; - proxy_set_header Host $host; - proxy_cache_bypass $http_upgrade; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - - } -} - -server { - listen 443 ssl;#监听443端口(https默认端口) - server_name admin.wmowm.com; #填写绑定证书的域名 - ssl_certificate /etc/nginx/conf.d/key/admin.wm.crt;#填写你的证书所在的位置 - ssl_certificate_key /etc/nginx/conf.d/key/admin.wm.key;#填写你的key所在的位置 - ssl_session_timeout 5m; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #按照这个协议配置 - ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;#按照这个套件配置 - ssl_prefer_server_ciphers on; - location / { - proxy_pass http://localhost:9002; - index index.php index.html index.htm; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection keep-alive; - proxy_set_header Host $host; - proxy_cache_bypass $http_upgrade; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - -} -``` \ No newline at end of file diff --git a/.docs/contents/guide/getting-started.md b/.docs/contents/guide/getting-started.md deleted file mode 100644 index 9c4f6b0d..00000000 --- a/.docs/contents/guide/getting-started.md +++ /dev/null @@ -1,132 +0,0 @@ -# K 快速上手 -注意 - -请确保你的 `Visual Studio 2019` 版本 >= `16.8.2`。 -并安装 `.NET 5.0 SDK` - - -## 下载 -Github(国际) 下载 [https://github.com/anjoy8/Blog.Core](https://github.com/anjoy8/Blog.Core) - -Gitee(国内) 下载 [https://gitee.com/laozhangIsPhi/Blog.Core](https://gitee.com/laozhangIsPhi/Blog.Core) - - -## 编译与运行 -1、拿到项目后,双击 `Blog.Core.sln` 解决方案; -2、首先 `F6` 编译,看是否有错误; -3、然后 `F5` 运行,调起 `9291` 端口,浏览器查看效果; -4、因为系统默认的是 `sqlite` 数据库,如果你想换其他数据库,请看下边; -5、注意:本系统是直接自动生成数据库和数据的,不用手动创建数据库; - - - - -## CodeFirst 与 DbFirst -1、项目同时支持两个常见开发模式:`CodeFirst` 和 `DbFirst`; -2、首先 如果你是第一次下载我的项目,肯定是想要浏览效果和直接使用对应的权限相关的内容,这个时候肯定需要用到数据库表结构,那就肯定需要 `CodeFirst` ,只需要在`appsettings.json` 里配置好数据库连接字符串(下文会说到如何配置),就能正确运行; -3、浏览器查看效果,或者配合 `Admin` 项目查看效果后,如果感觉项目可行,并打算在此基础上二次开发,那肯定会在你刚刚创建的数据库种去创建新的表结构,这个时候就需要使用 `DbFirst` 模式,来生成四层项目问题:Model+Service+Repository等; -4、你可以使用T4模板,但是我更建议使用 `/api/DbFirst/GetFrameFiles` 接口来生成,不仅支持多种类型的数据库,还支持同时多库模式的输出; -5、如果你不想用我的表结构和实体类,在项目启动的时候,把配置文件的 `SeedDBEnabled`节点设置成False即可,然后配置对应的你自己的数据库连接字符串,比如是商城的,然后使用 `/api/DbFirst/GetFrameFiles` 接口来生成你的数据库四层类文件; - - - -## 如何配置数据库连接字符串 - -1、打开 `Blog.Core` 项目下的 `appsettings.json` 文件; -2、修改 `DBS` 字节内容,配置对应的连接字符串,注意`DBType`对应不同的数据库类型; -3、把你想要运行的数据库 `Enabled` 为 `true` 即可,其他都要设置 `false`; -4、然后 `MainDB` 设置为下边你使用的指定 `ConnId`: - -``` - "MainDB": "WMBLOG_MSSQL", //当前项目的主库,所对应的连接字符串的Enabled必须为true - "MutiDBEnabled": false, //是否开启多库 - "DBS": [ - { - "ConnId": "WMBLOG_SQLITE", - "DBType": 2,// sqlite数据库 - "Enabled": true,// 设置为true,启用1 - "Connection": "WMBlog.db" //只写数据库名就行 - }, - { - "ConnId": "WMBLOG_MSSQL", - "DBType": 1,// sqlserver数据库 - "Enabled": true,// 设置为true,启用2 - "Connection": "Server=.;Database=WMBlogDB;User ID=sa;Password=123;", - "ProviderName": "System.Data.SqlClient" - }, - { - "ConnId": "WMBLOG_MYSQL", - "DBType": 0,// mysql - "Enabled": false,// false 不启用 - "Connection": "Server=localhost; Port=3306;Stmt=; Database=wmblogdb; Uid=root; Pwd=456;" - }, - { - "ConnId": "WMBLOG_ORACLE", - "DBType": 3,// Oracle - "Enabled": false,// 不启用 - "Connection": "Provider=OraOLEDB.Oracle; Data Source=WMBlogDB; User Id=sss; Password=789;" - } - ], -``` - - -5、如果你想多库操作,需要配置 -``` - a:MainDB 设置为主库的 ConnId; - b:MutiDBEnabled设置为true, - c:把下边想要连接的多个连接字符串都设置为true -``` - -## 如何配置项目端口号 -1、在 `Blog.Core` 层下的 `program.cs` 文件中,将 `9291`端口,修改为自己想要的端口号; -2、或者在 `launchSettings.json` 中设置(`注意,如果仅仅修改这里,publish后,端口访问无效`); - -## 如何项目重命名 -1、双击项目根目录下的 `CreateYourProject.bat` 批处理文件; -2、根据提示,`在Dos窗口内`输入自己想要的项目名称即可; -3、在根目录会有一个 `.1YourProject` 文件夹,里边即你的项目; - - -## 新增实体模块后如何迁移到数据库 -1、在 `Blog.Core.Model` 项目目录下的 `Seed` 文件夹下,找到 `DBSeed` 类; -2、根据提示,找到生成table的地方 `myContext.CreateTableByEntity`; -3、添加进去你新增的实体类,当然也可以用下边的单独写法; -4、编译项目,没错后,运行,则数据库更新完毕; - - -## 新增实体,如何进行增删改查CURD操作 -1、随便找一个含有业务逻辑的 `controller` 参考一下即可; -2、主要 `api` 是通过 `Service` 服务层提供业务逻辑; -3、然后服务层通过 `Repository` 仓储层封装持久化操作; -4、每一个表基本上对应一个仓储类,基本的操作都封装到了 `BaseRepository.cs` 基类仓储中; -5、添加完业务逻辑,记得要 `F6` 重新编译一下,因为项目间引用解耦了; -6、项目已经自动注入了,直接在控制器使用对应的服务层接口就行: `IxxxxService` ; - - -## 新增数据库表,如何反向生成四层文件 -1、可以通过 `T4` 模板来生成,在 `Blog.Core.FrameWork` 层,使用方法: [9757999.html](https://www.cnblogs.com/laozhang-is-phi/p/9757999.html#autoid-4-3-0) ; -> 注意:这种方案,目前默认的只能是 `SqlServer` ,其他类型的数据库,可以看上边文章中的代码,或者群文件里对应的代码。 - -> 1、修改`DbHelper.ttinclude`文件中的连接字符串,注意是`SqlServer`的: public static readonly string ConnectionString; -> 2、然后去各个层模板文件,点击`Ctrl+S`; -> 3、就会在对应的层内,看到新文件,比如:Blog.Core.Model/Model_NEW - - - -2、也可以通过 `Sqlsugar` 所带的方法来实现 `DbFirst`,具体查看 `Controller` 层下的 `DbFirstController.cs`; - -3、总体操作过程,可以参考我的视频:[av77612407](https://www.bilibili.com/video/av77612407?p=2) ; - - -## 发布与部署 -1、双击项目根目录下的 `Blog.Core.Publish.bat`批处理文件; -2、执行完成后,根目录会有一个`.PublishFiles` 文件夹,就是发布后的项目; - - -## 如何更新项目模板 -1、着急的话自己打包,不着急就提 `issue`,等我更新; -2、我的开源项目中,有个模板项目 `BlogCoreTempl` [地址](https://github.com/anjoy8/BlogCoreTempl),下载下来; -3、下载最新的 `Blog.Core` 源代码; -4、将源代码拷贝到模板项目的 `content` 文件夹下; -5、双击 `Package.bat` 文件,就生成了最新的模板了; - diff --git a/.docs/package.json b/.docs/package.json deleted file mode 100644 index 3f0483bf..00000000 --- a/.docs/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "BCVP", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "keywords": [], - "author": "", - "license": "ISC" -} diff --git a/.gitignore b/.gitignore index de419c90..97072da3 100644 --- a/.gitignore +++ b/.gitignore @@ -358,3 +358,4 @@ Blog.Core.Api/wwwroot/ui/ Blog.Core.Api/Logs *.db /Blog.Core.Api/WMBlog.db-journal +.docs/.vuepress/dist/ From 2ce3e6b7ffa79c8a05cdd155a1a589c6a19c560c Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Fri, 24 Nov 2023 20:12:02 +0800 Subject: [PATCH 100/155] =?UTF-8?q?feat=EF=BC=9Aremove=20startup.cs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Api/Program.cs | 4 +- Blog.Core.Api/Startup.cs | 232 ------------------ .../DependencyInjection/DI_Test.cs | 13 +- 3 files changed, 6 insertions(+), 243 deletions(-) delete mode 100644 Blog.Core.Api/Startup.cs diff --git a/Blog.Core.Api/Program.cs b/Blog.Core.Api/Program.cs index 13d7cea2..3138e5ad 100644 --- a/Blog.Core.Api/Program.cs +++ b/Blog.Core.Api/Program.cs @@ -1,4 +1,5 @@ -// 以下为asp.net 6.0的写法,如果用5.0,请看Program.five.cs文件 +// 以下为asp.net 6.0的写法,如果用5.0,请看Program.five.cs文件, +// 或者参考github上的.net6.0分支相关代码 using Autofac; using Autofac.Extensions.DependencyInjection; @@ -69,7 +70,6 @@ builder.Services.AddMiniProfilerSetup(); builder.Services.AddSwaggerSetup(); builder.Services.AddJobSetup(); -//builder.Services.AddJobSetup_HostedService(); builder.Services.AddHttpContextSetup(); builder.Services.AddAppTableConfigSetup(builder.Environment); builder.Services.AddHttpApi(); diff --git a/Blog.Core.Api/Startup.cs b/Blog.Core.Api/Startup.cs deleted file mode 100644 index 2911625d..00000000 --- a/Blog.Core.Api/Startup.cs +++ /dev/null @@ -1,232 +0,0 @@ -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.Extensions.ServiceExtensions; -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; -using Microsoft.AspNetCore.Server.Kestrel.Core; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using Newtonsoft.Json.Serialization; - -namespace Blog.Core -{ - public class Startup - { - private IServiceCollection _services; - - public Startup(IConfiguration configuration, IWebHostEnvironment env) - { - Configuration = configuration; - Env = env; - } - - public IConfiguration Configuration { get; } - public IWebHostEnvironment Env { get; } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - // 以下code可能与文章中不一样,对代码做了封装,具体查看右侧 Extensions 文件夹. - services.AddSingleton(new AppSettings(Configuration)); - services.AddUiFilesZipSetup(Env); - - Permissions.IsUseIds4 = AppSettings.app(new string[] { "Startup", "IdentityServer4", "Enabled" }).ObjToBool(); - RoutePrefix.Name = AppSettings.app(new string[] { "AppSettings", "SvcName" }).ObjToString(); - - // 确保从认证中心返回的ClaimType不被更改,不使用Map映射 - JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); - - services.AddCacheSetup(); - services.AddSqlsugarSetup(); - services.AddDbSetup(); - services.AddAutoMapperSetup(); - services.AddCorsSetup(); - services.AddMiniProfilerSetup(); - services.AddSwaggerSetup(); - services.AddJobSetup(); - services.AddHttpContextSetup(); - //services.AddAppConfigSetup(Env); - services.AddAppTableConfigSetup(Env);//表格打印配置 - services.AddHttpApi(); - services.AddRedisInitMqSetup(); - - services.AddRabbitMQSetup(); - services.AddKafkaSetup(Configuration); - services.AddEventBusSetup(); - - services.AddNacosSetup(Configuration); - services.AddInitializationHostServiceSetup(); - // 授权+认证 (jwt or ids4) - services.AddAuthorizationSetup(); - if (Permissions.IsUseIds4) - { - services.AddAuthentication_Ids4Setup(); - } - else - { - services.AddAuthentication_JWTSetup(); - } - - services.AddIpPolicyRateLimitSetup(Configuration); - - services.AddSignalR().AddNewtonsoftJsonProtocol(); - - services.AddScoped(); - - services.Configure(x => x.AllowSynchronousIO = true) - .Configure(x => x.AllowSynchronousIO = true); - - services.AddDistributedMemoryCache(); - services.AddSession(); - services.AddHttpPollySetup(); - - services.AddControllers(o => - { - // 全局异常过滤 - o.Filters.Add(typeof(GlobalExceptionsFilter)); - // 全局路由权限公约 - //o.Conventions.Insert(0, new GlobalRouteAuthorizeConvention()); - // 全局路由前缀,统一修改路由 - o.Conventions.Insert(0, new GlobalRoutePrefixFilter(new RouteAttribute(RoutePrefix.Name))); - }) - // 这种写法也可以 - //.AddJsonOptions(options => - //{ - // options.JsonSerializerOptions.PropertyNamingPolicy = null; - //}) - //MVC全局配置Json序列化处理 - .AddNewtonsoftJson(options => - { - //忽略循环引用 - options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; - //不使用驼峰样式的key - options.SerializerSettings.ContractResolver = new DefaultContractResolver(); - //设置时间格式 - options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; - //忽略Model中为null的属性 - //options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; - //设置本地时间而非UTC时间 - 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") - Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); - } - - // 注意在Program.CreateHostBuilder,添加Autofac服务工厂 - public void ConfigureContainer(ContainerBuilder builder) - { - builder.RegisterModule(new AutofacModuleRegister()); - builder.RegisterModule(); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env, MyContext myContext, ITasksQzServices tasksQzServices, ISchedulerCenter schedulerCenter, IHostApplicationLifetime lifetime) - { - // Ip限流,尽量放管道外层 - app.UseIpLimitMiddle(); - // 记录请求与返回数据 - app.UseRequestResponseLogMiddle(); - // 用户访问记录(必须放到外层,不然如果遇到异常,会报错,因为不能返回流) - app.UseRecordAccessLogsMiddle(); - // signalr - app.UseSignalRSendMiddle(); - // 记录ip请求 - app.UseIpLogMiddle(); - // 查看注入的所有服务 - app.UseAllServicesMiddle(_services); - - if (env.IsDevelopment()) - { - // 在开发环境中,使用异常页面,这样可以暴露错误堆栈信息,所以不要放在生产环境。 - app.UseDeveloperExceptionPage(); - } - else - { - app.UseExceptionHandler("/Error"); - // 在非开发环境中,使用HTTP严格安全传输(or HSTS) 对于保护web安全是非常重要的。 - // 强制实施 HTTPS 在 ASP.NET Core,配合 app.UseHttpsRedirection - //app.UseHsts(); - } - - app.UseSession(); - app.UseSwaggerAuthorized(); - // 封装Swagger展示 - app.UseSwaggerMiddle(() => GetType().GetTypeInfo().Assembly.GetManifestResourceStream("Blog.Core.Api.index.html")); - - // ↓↓↓↓↓↓ 注意下边这些中间件的顺序,很重要 ↓↓↓↓↓↓ - - // CORS跨域 - app.UseCors(AppSettings.app(new string[] { "Startup", "Cors", "PolicyName" })); - // 跳转https - //app.UseHttpsRedirection(); - // 使用静态文件 - DefaultFilesOptions defaultFilesOptions = new DefaultFilesOptions(); - defaultFilesOptions.DefaultFileNames.Clear(); - defaultFilesOptions.DefaultFileNames.Add("index.html"); - app.UseDefaultFiles(defaultFilesOptions); - app.UseStaticFiles(); - // 使用cookie - app.UseCookiePolicy(); - // 返回错误码 - app.UseStatusCodePages(); - // Routing - app.UseRouting(); - // 这种自定义授权中间件,可以尝试,但不推荐 - // app.UseJwtTokenAuth(); - - // 测试用户,用来通过鉴权 - if (Configuration.GetValue("AppSettings:UseLoadTest")) - { - app.UseMiddleware(); - } - // 先开启认证 - app.UseAuthentication(); - // 然后是授权中间件 - app.UseAuthorization(); - //开启性能分析 - app.UseMiniProfilerMiddleware(); - // 开启异常中间件,要放到最后 - //app.UseExceptionHandlerMidd(); - - app.UseEndpoints(endpoints => - { - endpoints.MapControllerRoute( - name: "default", - pattern: "{controller=Home}/{action=Index}/{id?}"); - - endpoints.MapHub("/api2/chatHub"); - }); - - // 生成种子数据 - //app.UseSeedDataMiddle(myContext, Env.WebRootPath); - // 开启QuartzNetJob调度服务 - //app.UseQuartzJobMiddleware(tasksQzServices, schedulerCenter); - // 服务注册 - //app.UseConsulMiddle(Configuration, lifetime); - // 事件总线,订阅服务 - //app.ConfigureEventBus(); - } - } -} \ 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 d425fa01..1ac4d5a7 100644 --- a/Blog.Core.Tests/DependencyInjection/DI_Test.cs +++ b/Blog.Core.Tests/DependencyInjection/DI_Test.cs @@ -1,12 +1,10 @@ using Autofac; using Autofac.Extensions.DependencyInjection; using Autofac.Extras.DynamicProxy; -using AutoMapper; using Blog.Core.AuthHelper; using Blog.Core.Common; using Blog.Core.Common.AppConfig; using Blog.Core.Common.DB; -using Blog.Core.Common.LogHelper; using Blog.Core.Common.Seed; using Blog.Core.Extensions; using Blog.Core.IRepository.Base; @@ -17,9 +15,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.IdentityModel.Tokens; -using System; -using System.Collections.Generic; -using System.IO; using System.Reflection; using System.Security.Claims; using System.Text; @@ -56,7 +51,7 @@ public IContainer DICollections() var basePath = AppContext.BaseDirectory; IServiceCollection services = new ServiceCollection(); - services.AddAutoMapper(typeof(Startup)); + services.AddAutoMapperSetup(); services.AddSingleton(new AppSettings(basePath)); services.AddScoped(); @@ -116,9 +111,9 @@ public IContainer DICollections() // 属性注入 var controllerBaseType = typeof(ControllerBase); - builder.RegisterAssemblyTypes(typeof(Startup).Assembly) - .Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType) - .PropertiesAutowired(); + //builder.RegisterAssemblyTypes(typeof(Program).Assembly) + // .Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType) + // .PropertiesAutowired(); var servicesDllFile = Path.Combine(basePath, "Blog.Core.Services.dll"); var assemblysServices = Assembly.LoadFrom(servicesDllFile); From 8188403b60d9b8641ebc0bfee13e3f2590515e11 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Fri, 24 Nov 2023 22:49:56 +0800 Subject: [PATCH 101/155] feat: remove some needless code --- Blog.Core.Api/Blog.Core.xml | 1 + Blog.Core.Api/Controllers/BlogController.cs | 2 + Blog.Core.Api/Controllers/LoginController.cs | 2 +- .../LogHelper/Seri/SerilogServer.cs | 76 ---------------- .../LogHelper/Seri/SerilogServer_Es.cs | 89 ------------------- Blog.Core.Common/Seed/DBSeed.cs | 5 -- .../ServiceExtensions/DbSetup.cs | 1 - .../ServiceExtensions/SerilogSetup.cs | 2 - .../ServiceExtensions/SqlsugarSetup.cs | 9 -- 9 files changed, 4 insertions(+), 183 deletions(-) delete mode 100644 Blog.Core.Common/LogHelper/Seri/SerilogServer.cs delete mode 100644 Blog.Core.Common/LogHelper/Seri/SerilogServer_Es.cs diff --git a/Blog.Core.Api/Blog.Core.xml b/Blog.Core.Api/Blog.Core.xml index 667dcfdc..5b904c4c 100644 --- a/Blog.Core.Api/Blog.Core.xml +++ b/Blog.Core.Api/Blog.Core.xml @@ -186,6 +186,7 @@ + diff --git a/Blog.Core.Api/Controllers/BlogController.cs b/Blog.Core.Api/Controllers/BlogController.cs index fbc67e12..d0e9a235 100644 --- a/Blog.Core.Api/Controllers/BlogController.cs +++ b/Blog.Core.Api/Controllers/BlogController.cs @@ -8,6 +8,7 @@ using Blog.Core.SwaggerHelper; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Serilog; using StackExchange.Profiling; using static Blog.Core.Extensions.CustomApiVersion; @@ -99,6 +100,7 @@ public async Task> Get(long id) public async Task> DetailNuxtNoPer(long id) { _logger.LogInformation("xxxxxxxxxxxxxxxxxxx"); + Log.Information("yyyyyyyyyyyyyyyyy"); return Success(await _blogArticleServices.GetBlogDetails(id)); } diff --git a/Blog.Core.Api/Controllers/LoginController.cs b/Blog.Core.Api/Controllers/LoginController.cs index abb495c9..f3e9accc 100644 --- a/Blog.Core.Api/Controllers/LoginController.cs +++ b/Blog.Core.Api/Controllers/LoginController.cs @@ -10,7 +10,6 @@ using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using Blog.Core.Common.Swagger; -using Serilog; namespace Blog.Core.Controllers @@ -38,6 +37,7 @@ public class LoginController : BaseApiController /// /// /// + /// public LoginController(ISysUserInfoServices sysUserInfoServices, IUserRoleServices userRoleServices, IRoleServices roleServices, PermissionRequirement requirement, IRoleModulePermissionServices roleModulePermissionServices, ILogger logger) diff --git a/Blog.Core.Common/LogHelper/Seri/SerilogServer.cs b/Blog.Core.Common/LogHelper/Seri/SerilogServer.cs deleted file mode 100644 index cb5ec0ff..00000000 --- a/Blog.Core.Common/LogHelper/Seri/SerilogServer.cs +++ /dev/null @@ -1,76 +0,0 @@ -using Blog.Core.Common.Helper; -using Serilog; -using Serilog.Events; -using System; -using System.IO; - -namespace Blog.Core.Common.LogHelper -{ - public class SerilogServer - { - /// - /// 记录日常日志 - /// - /// - /// - /// - public static void WriteLog(string filename, string[] dataParas, bool IsHeader = true, string defaultFolder = "", bool isJudgeJsonFormat = false) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Debug() - .MinimumLevel.Override("Microsoft", LogEventLevel.Error) - //.WriteTo.File(Path.Combine($"log/Serilog/{filename}/", ".log"), rollingInterval: RollingInterval.Day, outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level}] {Message}{NewLine}{Exception}") - .WriteTo.File(Path.Combine("Log", defaultFolder, $"{filename}.log"), - rollingInterval: RollingInterval.Infinite, - outputTemplate: "{Message}{NewLine}{Exception}") - .CreateLogger(); - - var now = DateTime.Now; - string logContent = String.Join("\r\n", dataParas); - var isJsonFormat = true; - if (isJudgeJsonFormat) - { - var judCont = logContent.Substring(0, logContent.LastIndexOf(",")); - isJsonFormat = JsonHelper.IsJson(judCont); - } - - if (isJsonFormat) - { - if (IsHeader) - { - logContent = ( - "--------------------------------\r\n" + - DateTime.Now + "|\r\n" + - String.Join("\r\n", dataParas) + "\r\n" - ); - } - // 展示elk支持输出4种日志级别 - Log.Information(logContent); - //Log.Warning(logContent); - //Log.Error(logContent); - //Log.Debug(logContent); - } - else - { - Console.WriteLine("【JSON格式异常:】"+logContent + now.ObjToString()); - } - Log.CloseAndFlush(); - } - /// - /// 记录异常日志 - /// - /// - /// - /// - public static void WriteErrorLog(string filename, string message, Exception ex) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Debug() - .MinimumLevel.Override("Microsoft", LogEventLevel.Error) - .WriteTo.File(Path.Combine($"log/Error/{filename}/", ".txt"), rollingInterval: RollingInterval.Day) - .CreateLogger(); - Log.Error(ex, message); - Log.CloseAndFlush(); - } - } -} diff --git a/Blog.Core.Common/LogHelper/Seri/SerilogServer_Es.cs b/Blog.Core.Common/LogHelper/Seri/SerilogServer_Es.cs deleted file mode 100644 index 048d5597..00000000 --- a/Blog.Core.Common/LogHelper/Seri/SerilogServer_Es.cs +++ /dev/null @@ -1,89 +0,0 @@ -using Blog.Core.Common.Helper; -using Blog.Core.Serilog.Es; -using Blog.Core.Serilog.Es.Formatters; -using Serilog; -using Serilog.Events; -using System; -using System.IO; - -namespace Blog.Core.Common.LogHelper -{ - public class SerilogServer_Es - { - /// - /// 记录日常日志 - /// - /// - /// - /// - public static void WriteLog(string filename, string[] dataParas, bool IsHeader = true, string defaultFolder = "", bool isJudgeJsonFormat = false) - { - Log.Logger = new LoggerConfiguration() - // TCPSink 集成Serilog 使用tcp的方式向elk 输出log日志 LogstashJsonFormatter 这个是按照自定义格式化输出内容 - .WriteTo.TCPSink(new LogstashJsonFormatter()) - .MinimumLevel.Debug() - .MinimumLevel.Override("Microsoft", LogEventLevel.Error) - //.WriteTo.File(Path.Combine($"log/Serilog/{filename}/", ".log"), rollingInterval: RollingInterval.Day, outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level}] {Message}{NewLine}{Exception}") - .WriteTo.File(Path.Combine("Log", defaultFolder, $"{filename}.log"), - rollingInterval: RollingInterval.Infinite, - outputTemplate: "{Message}{NewLine}{Exception}") - - // 将日志托送到远程ES - // docker run -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -d --name ES01 elasticsearch:7.2.0 - //.Enrich.FromLogContext() - //.WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri("http://x.xxx.xx.xx:9200/")) - //{ - // AutoRegisterTemplate = true, - //}) - - .CreateLogger(); - - var now = DateTime.Now; - string logContent = String.Join("\r\n", dataParas); - var isJsonFormat = true; - if (isJudgeJsonFormat) - { - var judCont = logContent.Substring(0, logContent.LastIndexOf(",")); - isJsonFormat = JsonHelper.IsJson(judCont); - } - - if (isJsonFormat) - { - if (IsHeader) - { - logContent = ( - "--------------------------------\r\n" + - DateTime.Now + "|\r\n" + - String.Join("\r\n", dataParas) + "\r\n" - ); - } - // 展示elk支持输出4种日志级别 - Log.Information(logContent); - //Log.Warning(logContent); - //Log.Error(logContent); - //Log.Debug(logContent); - } - else - { - Console.WriteLine("【JSON格式异常:】"+logContent + now.ObjToString()); - } - Log.CloseAndFlush(); - } - /// - /// 记录异常日志 - /// - /// - /// - /// - public static void WriteErrorLog(string filename, string message, Exception ex) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Debug() - .MinimumLevel.Override("Microsoft", LogEventLevel.Error) - .WriteTo.File(Path.Combine($"log/Error/{filename}/", ".txt"), rollingInterval: RollingInterval.Day) - .CreateLogger(); - Log.Error(ex, message); - Log.CloseAndFlush(); - } - } -} diff --git a/Blog.Core.Common/Seed/DBSeed.cs b/Blog.Core.Common/Seed/DBSeed.cs index 082b9b20..f3f023a0 100644 --- a/Blog.Core.Common/Seed/DBSeed.cs +++ b/Blog.Core.Common/Seed/DBSeed.cs @@ -6,14 +6,9 @@ using Magicodes.ExporterAndImporter.Excel; using Newtonsoft.Json; 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 diff --git a/Blog.Core.Extensions/ServiceExtensions/DbSetup.cs b/Blog.Core.Extensions/ServiceExtensions/DbSetup.cs index b00470ab..1f377bcc 100644 --- a/Blog.Core.Extensions/ServiceExtensions/DbSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/DbSetup.cs @@ -1,6 +1,5 @@ using Blog.Core.Common.Seed; using Microsoft.Extensions.DependencyInjection; -using System; namespace Blog.Core.Extensions { diff --git a/Blog.Core.Extensions/ServiceExtensions/SerilogSetup.cs b/Blog.Core.Extensions/ServiceExtensions/SerilogSetup.cs index 30ab29f8..89cbedd6 100644 --- a/Blog.Core.Extensions/ServiceExtensions/SerilogSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/SerilogSetup.cs @@ -6,8 +6,6 @@ using Serilog; using Serilog.Debugging; using Serilog.Events; -using System; -using System.IO; using Blog.Core.Common.Option; namespace Blog.Core.Extensions.ServiceExtensions; diff --git a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs index 99133fe1..0700a74c 100644 --- a/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/SqlsugarSetup.cs @@ -2,19 +2,10 @@ using Blog.Core.Common.Const; using Blog.Core.Common.DB; using Blog.Core.Common.DB.Aop; -using Blog.Core.Common.LogHelper; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.DependencyInjection; using SqlSugar; -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; -using Blog.Core.Common.HttpContextUser; -using static Grpc.Core.ChannelOption; using System.Text.RegularExpressions; namespace Blog.Core.Extensions From 325baf8e9e2b37485ebb005f80889e5a7467850e Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Sun, 26 Nov 2023 00:13:23 +0800 Subject: [PATCH 102/155] feat: little change --- Blog.Core.Api/Program.cs | 19 +++++++------------ .../ServiceExtensions/JobSetup.cs | 7 ------- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/Blog.Core.Api/Program.cs b/Blog.Core.Api/Program.cs index 3138e5ad..61ebc6e7 100644 --- a/Blog.Core.Api/Program.cs +++ b/Blog.Core.Api/Program.cs @@ -70,15 +70,14 @@ builder.Services.AddMiniProfilerSetup(); builder.Services.AddSwaggerSetup(); builder.Services.AddJobSetup(); + builder.Services.AddHttpContextSetup(); builder.Services.AddAppTableConfigSetup(builder.Environment); builder.Services.AddHttpApi(); builder.Services.AddRedisInitMqSetup(); -builder.Services.AddRabbitMQSetup(); -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,15 +113,11 @@ 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.AddRabbitMQSetup(); +builder.Services.AddKafkaSetup(builder.Configuration); +builder.Services.AddEventBusSetup(); builder.Services.AddEndpointsApiExplorer(); diff --git a/Blog.Core.Extensions/ServiceExtensions/JobSetup.cs b/Blog.Core.Extensions/ServiceExtensions/JobSetup.cs index 318168d2..da881cb3 100644 --- a/Blog.Core.Extensions/ServiceExtensions/JobSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/JobSetup.cs @@ -2,8 +2,6 @@ using Microsoft.Extensions.DependencyInjection; using Quartz; using Quartz.Spi; -using System; -using System.Linq; using System.Reflection; namespace Blog.Core.Extensions @@ -17,12 +15,7 @@ public static void AddJobSetup(this IServiceCollection services) { if (services == null) throw new ArgumentNullException(nameof(services)); - //services.AddHostedService(); - //services.AddHostedService(); - services.AddSingleton(); - //services.AddTransient();//Job使用瞬时依赖注入 - //services.AddTransient();//Job使用瞬时依赖注入 services.AddSingleton(); //任务注入 var baseType = typeof(IJob); From 62cdfb3a5674a453a982f8dde1b7d4b22675d62b Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Sun, 26 Nov 2023 15:50:27 +0800 Subject: [PATCH 103/155] =?UTF-8?q?feat=EF=BC=9A=20some=20change?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Api/Blog.Core.xml | 11 +- Blog.Core.Api/Controllers/ValuesController.cs | 31 +-- Blog.Core.Api/Filter/UseServiceDIAttribute.cs | 9 +- Blog.Core.Api/Program.cs | 12 +- Blog.Core.Common/Blog.Core.Common.csproj | 2 - .../ServiceExtensions/AppConfigSetup.cs | 192 ------------------ .../ServiceExtensions/HttpPollySetup.cs | 2 - .../IpPolicyRateLimitSetup.cs | 1 - .../ServiceExtensions/WebApiClientSetup.cs | 33 --- .../DoubanApis/DoubanViewModel.cs | 96 --------- .../WebApiClients/DoubanApis/IDoubanApi.cs | 22 -- .../WebApiClients/HttpApis/IBlogApi.cs | 84 -------- 12 files changed, 14 insertions(+), 481 deletions(-) delete mode 100644 Blog.Core.Extensions/ServiceExtensions/WebApiClientSetup.cs delete mode 100644 Blog.Core.IServices/WebApiClients/DoubanApis/DoubanViewModel.cs delete mode 100644 Blog.Core.IServices/WebApiClients/DoubanApis/IDoubanApi.cs delete mode 100644 Blog.Core.IServices/WebApiClients/HttpApis/IBlogApi.cs diff --git a/Blog.Core.Api/Blog.Core.xml b/Blog.Core.Api/Blog.Core.xml index 5b904c4c..64ee4586 100644 --- a/Blog.Core.Api/Blog.Core.xml +++ b/Blog.Core.Api/Blog.Core.xml @@ -758,7 +758,7 @@ Values控制器 - + ValuesController @@ -769,9 +769,8 @@ - - + @@ -854,12 +853,6 @@ - - - 测试http请求 WebApiClient Get - - - 测试Fulent做参数校验 diff --git a/Blog.Core.Api/Controllers/ValuesController.cs b/Blog.Core.Api/Controllers/ValuesController.cs index 072ab39e..c9cd8dfc 100644 --- a/Blog.Core.Api/Controllers/ValuesController.cs +++ b/Blog.Core.Api/Controllers/ValuesController.cs @@ -3,7 +3,6 @@ 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; using Blog.Core.Extensions; @@ -38,8 +37,6 @@ public class ValuesController : BaseApiController private readonly IRoleModulePermissionServices _roleModulePermissionServices; private readonly IUser _user; private readonly IPasswordLibServices _passwordLibServices; - private readonly IBlogApi _blogApi; - private readonly IDoubanApi _doubanApi; readonly IBlogArticleServices _blogArticleServices; private readonly IHttpPollyHelper _httpPollyHelper; private readonly SeqOptions _seqOptions; @@ -54,17 +51,14 @@ public class ValuesController : BaseApiController /// /// /// - /// - /// /// + /// public ValuesController(IBlogArticleServices blogArticleServices , IMapper mapper , IAdvertisementServices advertisementServices , Love love , IRoleModulePermissionServices roleModulePermissionServices , IUser user, IPasswordLibServices passwordLibServices - , IBlogApi blogApi - , IDoubanApi doubanApi , IHttpPollyHelper httpPollyHelper , IOptions seqOptions) { @@ -77,9 +71,6 @@ public ValuesController(IBlogArticleServices blogArticleServices _user = user; // 测试多库 _passwordLibServices = passwordLibServices; - // 测试http请求 - _blogApi = blogApi; - _doubanApi = doubanApi; // 测试AOP加载顺序,配合 return _blogArticleServices = blogArticleServices; // 测试redis消息队列 @@ -162,11 +153,6 @@ await _blogArticleServices.QuerySql( { bsubmitter = $"laozhang{DateTime.Now.Millisecond}", IsDeleted = false, bID = 5 }); - // 测试模拟异常,全局异常过滤器拦截 - var i = 0; - // var d = 3 / i; - - // 测试 AOP 缓存 var blogArticles = await _blogArticleServices.GetBlogs(); @@ -240,7 +226,6 @@ public void EventBusTry([FromServices] IEventBus _eventBus, string blogId = "1") // GET api/values/5 [HttpGet("{id}")] [AllowAnonymous] - //[TypeFilter(typeof(DeleteSubscriptionCache),Arguments =new object[] { "1"})] [TypeFilter(typeof(UseServiceDIAttribute), Arguments = new object[] { "laozhang" })] public ActionResult Get(int id) { @@ -351,20 +336,6 @@ public async Task TestMutiDBAPI() }; } - /// - /// 测试http请求 WebApiClient Get - /// - /// - [HttpGet("WebApiClientGetAsync")] - [AllowAnonymous] - public async Task WebApiClientGetAsync() - { - int id = 1; - string isbn = "9787544270878"; - var doubanVideoDetail = await _doubanApi.VideoDetailAsync(isbn); - return await _blogApi.DetailNuxtNoPerAsync(id); - } - /// /// 测试Fulent做参数校验 /// diff --git a/Blog.Core.Api/Filter/UseServiceDIAttribute.cs b/Blog.Core.Api/Filter/UseServiceDIAttribute.cs index 867ca0cd..2c487872 100644 --- a/Blog.Core.Api/Filter/UseServiceDIAttribute.cs +++ b/Blog.Core.Api/Filter/UseServiceDIAttribute.cs @@ -1,6 +1,5 @@ using Blog.Core.IServices; using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.Extensions.Logging; namespace Blog.Core.Filter { @@ -11,7 +10,7 @@ public class UseServiceDIAttribute : ActionFilterAttribute private readonly IBlogArticleServices _blogArticleServices; private readonly string _name; - public UseServiceDIAttribute(ILogger logger, IBlogArticleServices blogArticleServices,string Name="") + public UseServiceDIAttribute(ILogger logger, IBlogArticleServices blogArticleServices, string Name = "") { _logger = logger; _blogArticleServices = blogArticleServices; @@ -21,14 +20,16 @@ public UseServiceDIAttribute(ILogger logger, IBlogArticle public override void OnActionExecuted(ActionExecutedContext context) { - //var dd =await _blogArticleServices.Query(); + var dd = _blogArticleServices.Query().Result; + _logger.LogInformation("测试自定义服务特性"); + Console.WriteLine(_name); base.OnActionExecuted(context); DeleteSubscriptionFiles(); } private void DeleteSubscriptionFiles() { - + } } } diff --git a/Blog.Core.Api/Program.cs b/Blog.Core.Api/Program.cs index 61ebc6e7..0b9dfd3f 100644 --- a/Blog.Core.Api/Program.cs +++ b/Blog.Core.Api/Program.cs @@ -62,6 +62,7 @@ builder.Services.AddCacheSetup(); builder.Services.AddSqlsugarSetup(); builder.Services.AddDbSetup(); +builder.Services.AddInitializationHostServiceSetup(); builder.Host.AddSerilogSetup(); @@ -73,10 +74,12 @@ builder.Services.AddHttpContextSetup(); builder.Services.AddAppTableConfigSetup(builder.Environment); -builder.Services.AddHttpApi(); -builder.Services.AddRedisInitMqSetup(); +builder.Services.AddHttpPollySetup(); builder.Services.AddNacosSetup(builder.Configuration); -builder.Services.AddInitializationHostServiceSetup(); +builder.Services.AddRedisInitMqSetup(); + +builder.Services.AddIpPolicyRateLimitSetup(builder.Configuration); +builder.Services.AddSignalR().AddNewtonsoftJsonProtocol(); builder.Services.AddAuthorizationSetup(); if (Permissions.IsUseIds4 || Permissions.IsUseAuthing) @@ -89,14 +92,11 @@ builder.Services.AddAuthentication_JWTSetup(); } -builder.Services.AddIpPolicyRateLimitSetup(builder.Configuration); -builder.Services.AddSignalR().AddNewtonsoftJsonProtocol(); builder.Services.AddScoped(); builder.Services.Configure(x => x.AllowSynchronousIO = true) .Configure(x => x.AllowSynchronousIO = true); builder.Services.AddSession(); -builder.Services.AddHttpPollySetup(); builder.Services.AddControllers(o => { o.Filters.Add(typeof(GlobalExceptionsFilter)); diff --git a/Blog.Core.Common/Blog.Core.Common.csproj b/Blog.Core.Common/Blog.Core.Common.csproj index ce966e59..7ac6dfb8 100644 --- a/Blog.Core.Common/Blog.Core.Common.csproj +++ b/Blog.Core.Common/Blog.Core.Common.csproj @@ -37,8 +37,6 @@ - - diff --git a/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs b/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs index e5e9f339..7cb098ff 100644 --- a/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/AppConfigSetup.cs @@ -1,11 +1,7 @@ using Blog.Core.Common; -using Blog.Core.Common.Helper; -using Blog.Core.Common.LogHelper; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using System; -using System.Collections.Generic; using System.Text; using Blog.Core.Common.DB; @@ -16,194 +12,6 @@ namespace Blog.Core.Extensions /// public static class AppConfigSetup { - public static void AddAppConfigSetup(this IServiceCollection services, IHostEnvironment env) - { - if (services == null) throw new ArgumentNullException(nameof(services)); - - if (AppSettings.app(new string[] { "Startup", "AppConfigAlert", "Enabled" }).ObjToBool()) - { - if (env.IsDevelopment()) - { - Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); - Console.OutputEncoding = Encoding.GetEncoding("GB2312"); - } - - Console.WriteLine("************ Blog.Core Config Set *****************"); - - ConsoleHelper.WriteSuccessLine("Current environment: " + Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")); - - // 授权策略方案 - if (Permissions.IsUseIds4) - { - ConsoleHelper.WriteSuccessLine($"Current authorization scheme: " + (Permissions.IsUseIds4 ? "Ids4" : "JWT")); - } - else - { - Console.WriteLine($"Current authorization scheme: " + (Permissions.IsUseIds4 ? "Ids4" : "JWT")); - } - // 缓存AOP - if (!AppSettings.app(new string[] { "AppSettings", "CachingAOP", "Enabled" }).ObjToBool()) - { - Console.WriteLine($"Caching AOP: False"); - } - else - { - ConsoleHelper.WriteSuccessLine($"Caching AOP: True"); - } - - // 服务日志AOP - if (!AppSettings.app(new string[] { "AppSettings", "LogAOP", "Enabled" }).ObjToBool()) - { - Console.WriteLine($"Service Log AOP: False"); - } - else - { - ConsoleHelper.WriteSuccessLine($"Service Log AOP: True"); - } - - // 开启的中间件日志 - var requestResponseLogOpen = AppSettings.app(new string[] { "Middleware", "RequestResponseLog", "Enabled" }).ObjToBool(); - 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 √," : "") - ); - - // 事务AOP - if (!AppSettings.app(new string[] { "AppSettings", "TranAOP", "Enabled" }).ObjToBool()) - { - Console.WriteLine($"Transaction AOP: False"); - } - else - { - 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()) - { - Console.WriteLine($"DB Sql AOP To LogFile: False"); - } - else - { - ConsoleHelper.WriteSuccessLine($"DB Sql AOP To LogFile: True"); - } - - // Sql执行日志输出到控制台 - if (!AppSettings.app(new string[] { "AppSettings", "SqlAOP", "OutToConsole", "Enabled" }).ObjToBool()) - { - Console.WriteLine($"DB Sql AOP To Console: False"); - } - else - { - ConsoleHelper.WriteSuccessLine($"DB Sql AOP To Console: True"); - } - - // SingnalR发送数据 - if (!AppSettings.app(new string[] { "Middleware", "SignalR", "Enabled" }).ObjToBool()) - { - Console.WriteLine($"SignalR send data: False"); - } - else - { - ConsoleHelper.WriteSuccessLine($"SignalR send data: True"); - } - - // IP限流 - if (!AppSettings.app("Middleware", "IpRateLimit", "Enabled").ObjToBool()) - { - Console.WriteLine($"IpRateLimiting: False"); - } - else - { - ConsoleHelper.WriteSuccessLine($"IpRateLimiting: True"); - } - - // 性能分析 - if (!AppSettings.app("Startup", "MiniProfiler", "Enabled").ObjToBool()) - { - Console.WriteLine($"MiniProfiler: False"); - } - else - { - ConsoleHelper.WriteSuccessLine($"MiniProfiler: True"); - } - - // CORS跨域 - if (!AppSettings.app("Startup", "Cors", "EnableAllIPs").ObjToBool()) - { - Console.WriteLine($"EnableAllIPs For CORS: False"); - } - else - { - ConsoleHelper.WriteSuccessLine($"EnableAllIPs For CORS: True"); - } - - // redis消息队列 - if (!AppSettings.app("Startup", "RedisMq", "Enabled").ObjToBool()) - { - Console.WriteLine($"Redis MQ: False"); - } - else - { - ConsoleHelper.WriteSuccessLine($"Redis MQ: True"); - } - - // RabbitMQ 消息队列 - if (!AppSettings.app("RabbitMQ", "Enabled").ObjToBool()) - { - Console.WriteLine($"RabbitMQ: False"); - } - else - { - ConsoleHelper.WriteSuccessLine($"RabbitMQ: True"); - } - - // Consul 注册服务 - if (!AppSettings.app("Middleware", "Consul", "Enabled").ObjToBool()) - { - Console.WriteLine($"Consul service: False"); - } - else - { - ConsoleHelper.WriteSuccessLine($"Consul service: True"); - } - - // EventBus 事件总线 - if (!AppSettings.app("EventBus", "Enabled").ObjToBool()) - { - Console.WriteLine($"EventBus: False"); - } - else - { - ConsoleHelper.WriteSuccessLine($"EventBus: True"); - } - - // 读写分离 - if (!BaseDBConfig.MainConfig.SlaveConnectionConfigs.AnyNoException()) - { - Console.WriteLine($"Is CQRS: False"); - } - else - { - ConsoleHelper.WriteSuccessLine($"Is CQRS: True"); - } - - Console.WriteLine(); - } - } - public static void AddAppTableConfigSetup(this IServiceCollection services, IHostEnvironment env) { if (services == null) throw new ArgumentNullException(nameof(services)); diff --git a/Blog.Core.Extensions/ServiceExtensions/HttpPollySetup.cs b/Blog.Core.Extensions/ServiceExtensions/HttpPollySetup.cs index b3147ca8..8c1d637b 100644 --- a/Blog.Core.Extensions/ServiceExtensions/HttpPollySetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/HttpPollySetup.cs @@ -4,8 +4,6 @@ using Polly; using Polly.Extensions.Http; using Polly.Timeout; -using System; -using System.Net.Http; namespace Blog.Core.Extensions { diff --git a/Blog.Core.Extensions/ServiceExtensions/IpPolicyRateLimitSetup.cs b/Blog.Core.Extensions/ServiceExtensions/IpPolicyRateLimitSetup.cs index 61dabc3e..e75e587d 100644 --- a/Blog.Core.Extensions/ServiceExtensions/IpPolicyRateLimitSetup.cs +++ b/Blog.Core.Extensions/ServiceExtensions/IpPolicyRateLimitSetup.cs @@ -2,7 +2,6 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using System; namespace Blog.Core.Extensions { diff --git a/Blog.Core.Extensions/ServiceExtensions/WebApiClientSetup.cs b/Blog.Core.Extensions/ServiceExtensions/WebApiClientSetup.cs deleted file mode 100644 index 7cee91a4..00000000 --- a/Blog.Core.Extensions/ServiceExtensions/WebApiClientSetup.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using System; -using WebApiClient.Extensions.DependencyInjection; -using Blog.Core.Common.WebApiClients.HttpApis; - -namespace Blog.Core.Extensions -{ - /// - /// WebApiClientSetup 启动服务 - /// - public static class WebApiClientSetup - { - /// - /// 注册WebApiClient接口 - /// - /// - public static void AddHttpApi(this IServiceCollection services) - { - if (services == null) throw new ArgumentNullException(nameof(services)); - - services.AddHttpApi().ConfigureHttpApiConfig(c => - { - c.HttpHost = new Uri("http://apk.neters.club/"); - c.FormatOptions.DateTimeFormat = "yyyy-MM-dd HH:mm:ss.fff"; - }); - services.AddHttpApi().ConfigureHttpApiConfig(c => - { - c.HttpHost = new Uri("http://api.xiaomafeixiang.com/"); - c.FormatOptions.DateTimeFormat = "yyyy-MM-dd HH:mm:ss.fff"; - }); - } - } -} diff --git a/Blog.Core.IServices/WebApiClients/DoubanApis/DoubanViewModel.cs b/Blog.Core.IServices/WebApiClients/DoubanApis/DoubanViewModel.cs deleted file mode 100644 index 0ddb402e..00000000 --- a/Blog.Core.IServices/WebApiClients/DoubanApis/DoubanViewModel.cs +++ /dev/null @@ -1,96 +0,0 @@ -namespace Blog.Core.Common.WebApiClients.HttpApis -{ - public class Data - { - /// - /// - /// - public string isbn { get; set; } - /// - /// 解忧杂货店 - /// - public string title { get; set; } - /// - /// ナミヤ雑貨店の奇蹟 - /// - public string origintitle { get; set; } - /// - /// - /// - public string subtitle { get; set; } - /// - /// - /// - public string image { get; set; } - /// - /// [日]东野圭吾 - /// - public string author { get; set; } - /// - /// 李盈春 - /// - public string translator { get; set; } - /// - /// 南海出版公司 - /// - public string publisher { get; set; } - /// - /// - /// - public string pubdate { get; set; } - /// - /// <东野圭吾><治愈><温暖><小说><日本><日本文学><東野圭吾><推理> - /// - public string tags { get; set; } - /// - /// - /// - public string kaiben { get; set; } - /// - /// - /// - public string zhizhang { get; set; } - /// - /// 精装 - /// - public string binding { get; set; } - /// - /// - /// - public string taozhuang { get; set; } - /// - /// 新经典文库·东野圭吾作品 - /// - public string series { get; set; } - /// - /// - /// - public string pages { get; set; } - /// - /// 39.50元 - /// - public string price { get; set; } - - public string author_intro { get; set; } - - public string summary { get; set; } - - public string catalog { get; set; } - } - - public class DoubanViewModel - { - /// - /// - /// - public string status { get; set; } - /// - /// - /// - public Data data { get; set; } - /// - /// 获取图书数据成功 - /// - public string msg { get; set; } - } -} diff --git a/Blog.Core.IServices/WebApiClients/DoubanApis/IDoubanApi.cs b/Blog.Core.IServices/WebApiClients/DoubanApis/IDoubanApi.cs deleted file mode 100644 index c2f1d42d..00000000 --- a/Blog.Core.IServices/WebApiClients/DoubanApis/IDoubanApi.cs +++ /dev/null @@ -1,22 +0,0 @@ -using WebApiClient; -using WebApiClient.Attributes; - -namespace Blog.Core.Common.WebApiClients.HttpApis -{ - /// - /// 豆瓣视频管理 - /// - [TraceFilter] - public interface IDoubanApi : IHttpApi - { - /// - /// 获取电影详情 - /// - /// - [HttpGet("api/bookinfo")] - ITask VideoDetailAsync(string isbn); - - } - - -} diff --git a/Blog.Core.IServices/WebApiClients/HttpApis/IBlogApi.cs b/Blog.Core.IServices/WebApiClients/HttpApis/IBlogApi.cs deleted file mode 100644 index 52ff8aa4..00000000 --- a/Blog.Core.IServices/WebApiClients/HttpApis/IBlogApi.cs +++ /dev/null @@ -1,84 +0,0 @@ -using Blog.Core.Model; -using Blog.Core.Model.Models; -using Blog.Core.Model.ViewModels; -using System.ComponentModel.DataAnnotations; -using System.Threading.Tasks; -using WebApiClient; -using WebApiClient.Attributes; - -namespace Blog.Core.Common.WebApiClients.HttpApis -{ - /// - /// 博客管理 - /// - [TraceFilter] - public interface IBlogApi : IHttpApi - { - /// - /// 获取博客列表【无权限】 - /// - /// - /// - /// - /// - /// Success - [HttpGet("api/Blog")] - Task>> BlogAsync(int? id, int page, string bcategory, string key); - - /// - /// 添加博客【无权限】 - /// - /// - /// Success - [HttpPost("api/Blog")] - Task> Blog2Async([JsonContent] BlogArticle body); - - /// - /// 获取博客详情 (Auth) - /// - /// - /// Success - [HttpGet("api/Blog/{id}")] - Task> Blog3Async([Required] int id); - - /// - /// apache jemeter 压力测试 - /// 更新接口 - /// - /// Success - [HttpGet("api/Blog/ApacheTestUpdate")] - Task> ApacheTestUpdateAsync(); - - /// - /// 删除博客 (Auth policies: Permission) - /// - /// - /// Success - [HttpDelete("api/Blog/Delete")] - Task> DeleteAsync(int? id); - - /// - /// 获取详情【无权限】 - /// - /// - /// Success - [HttpGet("api/Blog/DetailNuxtNoPer")] - Task> DetailNuxtNoPerAsync(int? id); - - /// - /// 更新博客信息 (Auth) - /// - /// - /// Success - [HttpPut("api/Blog/Update")] - Task> UpdateAsync([JsonContent] BlogArticle body); - - /// - /// 获取博客测试信息 v2版本 - /// - /// Success - [HttpGet("api/V2/Blog/Blogtest")] - Task> BlogtestAsync(); - - } -} From 7a07b85b80e367dea5b8c06b4c52f507f70e2b9f Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Sun, 26 Nov 2023 22:17:09 +0800 Subject: [PATCH 104/155] feat: add rabbitmq demo --- Blog.Core.Api/Blog.Core.xml | 13 +++- Blog.Core.Api/Controllers/ValuesController.cs | 66 +++++++++++++++++++ Blog.Core.Api/appsettings.json | 15 +++-- 3 files changed, 86 insertions(+), 8 deletions(-) diff --git a/Blog.Core.Api/Blog.Core.xml b/Blog.Core.Api/Blog.Core.xml index 64ee4586..dfe9950a 100644 --- a/Blog.Core.Api/Blog.Core.xml +++ b/Blog.Core.Api/Blog.Core.xml @@ -758,7 +758,7 @@ Values控制器 - + ValuesController @@ -770,8 +770,19 @@ + + + + 测试Rabbit消息队列发送 + + + + + 测试Rabbit消息队列订阅 + + 测试SqlSugar二级缓存 diff --git a/Blog.Core.Api/Controllers/ValuesController.cs b/Blog.Core.Api/Controllers/ValuesController.cs index c9cd8dfc..1ab4d3c8 100644 --- a/Blog.Core.Api/Controllers/ValuesController.cs +++ b/Blog.Core.Api/Controllers/ValuesController.cs @@ -14,8 +14,11 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; +using RabbitMQ.Client; +using RabbitMQ.Client.Events; using System.ComponentModel.DataAnnotations; using System.Linq.Expressions; +using System.Text; namespace Blog.Core.Controllers { @@ -39,6 +42,7 @@ public class ValuesController : BaseApiController private readonly IPasswordLibServices _passwordLibServices; readonly IBlogArticleServices _blogArticleServices; private readonly IHttpPollyHelper _httpPollyHelper; + private readonly IRabbitMQPersistentConnection _persistentConnection; private readonly SeqOptions _seqOptions; /// @@ -52,6 +56,7 @@ public class ValuesController : BaseApiController /// /// /// + /// /// public ValuesController(IBlogArticleServices blogArticleServices , IMapper mapper @@ -60,6 +65,7 @@ public ValuesController(IBlogArticleServices blogArticleServices , IRoleModulePermissionServices roleModulePermissionServices , IUser user, IPasswordLibServices passwordLibServices , IHttpPollyHelper httpPollyHelper + , IRabbitMQPersistentConnection persistentConnection , IOptions seqOptions) { // 测试 Authorize 和 mapper @@ -77,9 +83,69 @@ public ValuesController(IBlogArticleServices blogArticleServices _blogArticleServices = blogArticleServices; // httpPolly _httpPollyHelper = httpPollyHelper; + _persistentConnection = persistentConnection; _seqOptions = seqOptions.Value; } + /// + /// 测试Rabbit消息队列发送 + /// + [HttpGet] + [AllowAnonymous] + public void TestRabbitMqPublish() + { + if (!_persistentConnection.IsConnected) + { + _persistentConnection.TryConnect(); + } + using var channel = _persistentConnection.CreateModel(); + var message = " < i am a sender! > "; + var body = Encoding.UTF8.GetBytes(message); + var properties = channel.CreateBasicProperties(); + channel.BasicPublish( + exchange: "blogcore", + routingKey: "eventName", + mandatory: true, + basicProperties: properties, + body: body); + } + + /// + /// 测试Rabbit消息队列订阅 + /// + [HttpGet] + [AllowAnonymous] + public void TestRabbitMqSubscribe() + { + if (!_persistentConnection.IsConnected) + { + _persistentConnection.TryConnect(); + } + + string QueueName = "testq"; + using var channel = _persistentConnection.CreateModel(); + var consumer = new AsyncEventingBasicConsumer(channel); + + consumer.Received += new AsyncEventHandler( + async (a, b) => + { + var Headers = b.BasicProperties.Headers; + var msgBody = b.Body.ToArray(); + bool Dealresult = await Dealer(b.Exchange, b.RoutingKey, msgBody, Headers); + if (Dealresult) channel.BasicAck(b.DeliveryTag, false); + else channel.BasicNack(b.DeliveryTag, false, true); + } + ); + channel.BasicConsume(QueueName, false, consumer); + } + + private async Task Dealer(string exchange, string routingKey, byte[] msgBody, IDictionary headers) + { + await Task.CompletedTask; + Console.WriteLine("我是消费者,这里消费了一条信息是:" + Encoding.UTF8.GetString(msgBody)); + return true; + } + [HttpGet] public MessageModel> MyClaims() { diff --git a/Blog.Core.Api/appsettings.json b/Blog.Core.Api/appsettings.json index ff2c178a..9b599af0 100644 --- a/Blog.Core.Api/appsettings.json +++ b/Blog.Core.Api/appsettings.json @@ -23,11 +23,12 @@ "InstanceName": "" //前缀 }, "RabbitMQ": { - "Enabled": false, - "Connection": "118.25.251.13", - "UserName": "", - "Password": "!", - "RetryCount": 3 + "Enabled": true, + "Connection": "101.35.125.157", + "UserName": "admin", + "Password": "admin", + "Port": "5672", + "RetryCount": 2 }, "Kafka": { "Enabled": false, @@ -181,8 +182,8 @@ "Audience": { "Secret": "sdfsdfsrty45634kkhllghtdgdfss345t678fs", //不要太短,16位+ "SecretFile": "C:\\my-file\\blog.core.audience.secret.txt", //安全。内容就是Secret - "Issuer": "Blog.Core", - "Audience": "wr" + "Issuer": "Blog.Core", //这个值一定要在自己的项目里修改!! + "Audience": "wr" //这个值一定要在自己的项目里修改!! }, "Mongo": { "ConnectionString": "mongodb://nosql.data", From e4eb46a644b7df47813e2a0cbdb4c5ec9991114e Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Sun, 26 Nov 2023 22:22:23 +0800 Subject: [PATCH 105/155] Update appsettings.json --- Blog.Core.Api/appsettings.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Blog.Core.Api/appsettings.json b/Blog.Core.Api/appsettings.json index 9b599af0..673340be 100644 --- a/Blog.Core.Api/appsettings.json +++ b/Blog.Core.Api/appsettings.json @@ -24,9 +24,9 @@ }, "RabbitMQ": { "Enabled": true, - "Connection": "101.35.125.157", - "UserName": "admin", - "Password": "admin", + "Connection": "101xxxx57", + "UserName": "xxxx", + "Password": "xxxxx", "Port": "5672", "RetryCount": 2 }, From 59e729fa2c97bc26b3ad0d57f5b200a45ae2e18d Mon Sep 17 00:00:00 2001 From: ansonzhang <3143422472@qq.com> Date: Tue, 28 Nov 2023 09:15:24 +0800 Subject: [PATCH 106/155] Update README.md --- README.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2ab20732..b69a5621 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Blog.Core 开箱即用的企业级前后端分离【 .NET Core6.0 Api + Vue 2.x ### 功能与进度 -#### 商业授权付费版下🎁🎁🎁 +#### 企业使用高级版本 - [x] 包含开源版 `框架模块/组件模块` 中的所有功能; - [x] 全部表结构主键底层架构改成`string`类型(默认雪花,支持guid),更方便迁移; @@ -239,10 +239,7 @@ Contributions of any kind are welcome! ## 售后服务与支持 鼓励作者,简单打赏~~ -打赏的时候,备注自己的微信号,加个微信,交个朋友,两天内没回应,QQ私聊我(3143422472); -目前精力有限,主要针对企业级用户答疑,或者购买授权版的个人用户。 - -[赞赏列表](http://apk.neters.club/.doc/Contribution/) +如果你喜欢,就给作者加个鸡腿吧 赞赏码 From c4a6c84d969bd0ef2c4b1a84078ffe6e396d5824 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Fri, 1 Dec 2023 11:18:21 +0800 Subject: [PATCH 107/155] =?UTF-8?q?feat=EF=BC=9A=20:aerial=5Ftramway:=20?= =?UTF-8?q?=20RabbitMQ?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Api/Controllers/ValuesController.cs | 33 +++----------- .../IRabbitMQPersistentConnection.cs | 25 +++++++++++ .../RabbitMQPersistentConnection.cs | 45 +++++++++++++++++++ 3 files changed, 76 insertions(+), 27 deletions(-) diff --git a/Blog.Core.Api/Controllers/ValuesController.cs b/Blog.Core.Api/Controllers/ValuesController.cs index 1ab4d3c8..9d0c1558 100644 --- a/Blog.Core.Api/Controllers/ValuesController.cs +++ b/Blog.Core.Api/Controllers/ValuesController.cs @@ -92,22 +92,14 @@ public ValuesController(IBlogArticleServices blogArticleServices /// [HttpGet] [AllowAnonymous] - public void TestRabbitMqPublish() + public IActionResult TestRabbitMqPublish() { if (!_persistentConnection.IsConnected) { _persistentConnection.TryConnect(); } - using var channel = _persistentConnection.CreateModel(); - var message = " < i am a sender! > "; - var body = Encoding.UTF8.GetBytes(message); - var properties = channel.CreateBasicProperties(); - channel.BasicPublish( - exchange: "blogcore", - routingKey: "eventName", - mandatory: true, - basicProperties: properties, - body: body); + _persistentConnection.PublishMessage("Hello, RabbitMQ!", exchangeName: "blogcore", routingKey: "myRoutingKey"); + return Ok(); } /// @@ -115,28 +107,15 @@ public void TestRabbitMqPublish() /// [HttpGet] [AllowAnonymous] - public void TestRabbitMqSubscribe() + public IActionResult TestRabbitMqSubscribe() { if (!_persistentConnection.IsConnected) { _persistentConnection.TryConnect(); } - string QueueName = "testq"; - using var channel = _persistentConnection.CreateModel(); - var consumer = new AsyncEventingBasicConsumer(channel); - - consumer.Received += new AsyncEventHandler( - async (a, b) => - { - var Headers = b.BasicProperties.Headers; - var msgBody = b.Body.ToArray(); - bool Dealresult = await Dealer(b.Exchange, b.RoutingKey, msgBody, Headers); - if (Dealresult) channel.BasicAck(b.DeliveryTag, false); - else channel.BasicNack(b.DeliveryTag, false, true); - } - ); - channel.BasicConsume(QueueName, false, consumer); + _persistentConnection.StartConsuming("myQueue"); + return Ok(); } private async Task Dealer(string exchange, string routingKey, byte[] msgBody, IDictionary headers) diff --git a/Blog.Core.EventBus/RabbitMQPersistent/IRabbitMQPersistentConnection.cs b/Blog.Core.EventBus/RabbitMQPersistent/IRabbitMQPersistentConnection.cs index c90b0d4a..bad482ae 100644 --- a/Blog.Core.EventBus/RabbitMQPersistent/IRabbitMQPersistentConnection.cs +++ b/Blog.Core.EventBus/RabbitMQPersistent/IRabbitMQPersistentConnection.cs @@ -10,10 +10,35 @@ namespace Blog.Core.EventBus public interface IRabbitMQPersistentConnection : IDisposable { + /// + /// 是否已经连接 + /// bool IsConnected { get; } + /// + /// 尝试重连 + /// + /// bool TryConnect(); + /// + /// 创建Model + /// + /// IModel CreateModel(); + + /// + /// 发布消息 + /// + /// + /// + /// + void PublishMessage(string message, string exchangeName, string routingKey); + + /// + /// 订阅消息 + /// + /// + void StartConsuming(string queueName); } } diff --git a/Blog.Core.EventBus/RabbitMQPersistent/RabbitMQPersistentConnection.cs b/Blog.Core.EventBus/RabbitMQPersistent/RabbitMQPersistentConnection.cs index be2d8de3..1fa3bd08 100644 --- a/Blog.Core.EventBus/RabbitMQPersistent/RabbitMQPersistentConnection.cs +++ b/Blog.Core.EventBus/RabbitMQPersistent/RabbitMQPersistentConnection.cs @@ -7,6 +7,7 @@ using System; using System.IO; using System.Net.Sockets; +using System.Text; namespace Blog.Core.EventBus { @@ -162,5 +163,49 @@ void OnConnectionShutdown(object sender, ShutdownEventArgs reason) TryConnect(); } + + /// + /// 发布消息 + /// + /// + /// + /// + public void PublishMessage(string message, string exchangeName, string routingKey) + { + using var channel = CreateModel(); + channel.ExchangeDeclare(exchange: exchangeName, type: ExchangeType.Direct, true); + var body = Encoding.UTF8.GetBytes(message); + channel.BasicPublish(exchange: exchangeName, routingKey: routingKey, basicProperties: null, body: body); + } + + /// + /// 订阅消息 + /// + /// + public void StartConsuming(string queueName) + { + using var channel = CreateModel(); + channel.QueueDeclare(queue: queueName, durable: true, exclusive: false, autoDelete: false, arguments: null); + + var consumer = new AsyncEventingBasicConsumer(channel); + consumer.Received += new AsyncEventHandler( + async (a, b) => + { + var Headers = b.BasicProperties.Headers; + var msgBody = b.Body.ToArray(); + var message = Encoding.UTF8.GetString(msgBody); + await Task.CompletedTask; + Console.WriteLine("Received message: {0}", message); + + //bool Dealresult = await Dealer(b.Exchange, b.RoutingKey, msgBody, Headers); + //if (Dealresult) channel.BasicAck(b.DeliveryTag, false); + //else channel.BasicNack(b.DeliveryTag, false, true); + } + ); + + channel.BasicConsume(queue: queueName, autoAck: true, consumer: consumer); + + Console.WriteLine("Consuming messages..."); + } } } From 5f132f03863e1356d22029d5cc4e3840d7826153 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Sun, 3 Dec 2023 18:16:27 +0800 Subject: [PATCH 108/155] =?UTF-8?q?feat=EF=BC=9A=E6=9B=B4=E6=96=B0gateway?= =?UTF-8?q?=EF=BC=8C=E5=8E=BB=E9=99=A4nacos=E7=9B=B8=E5=85=B3=E9=85=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Gateway/Blog.Core.Gateway.csproj | 8 + Blog.Core.Gateway/Blog.Core.Gateway.xml | 45 ------ .../Extensions/CustomOcelotSetup.cs | 9 +- .../Extensions/CustomSwaggerSetup.cs | 30 ++-- .../Helper/CustomJwtTokenAuthMiddleware.cs | 38 +---- .../Helper/OcelotConfigurationTask.cs | 147 ------------------ Blog.Core.Gateway/Startup.cs | 23 +-- Blog.Core.Gateway/appsettings.gw.json | 68 +------- Blog.Core.Gateway/ocelot.Development.json | 7 +- 9 files changed, 54 insertions(+), 321 deletions(-) delete mode 100644 Blog.Core.Gateway/Helper/OcelotConfigurationTask.cs diff --git a/Blog.Core.Gateway/Blog.Core.Gateway.csproj b/Blog.Core.Gateway/Blog.Core.Gateway.csproj index 8af7a7e3..a21feb4b 100644 --- a/Blog.Core.Gateway/Blog.Core.Gateway.csproj +++ b/Blog.Core.Gateway/Blog.Core.Gateway.csproj @@ -12,6 +12,14 @@ + + + + + + + + diff --git a/Blog.Core.Gateway/Blog.Core.Gateway.xml b/Blog.Core.Gateway/Blog.Core.Gateway.xml index 41507d7a..34543342 100644 --- a/Blog.Core.Gateway/Blog.Core.Gateway.xml +++ b/Blog.Core.Gateway/Blog.Core.Gateway.xml @@ -51,50 +51,5 @@ │ 作 者:anson zhang └──────────────────────────────────────────────────────────────┘ - - - Nacos配置文件变更事件 - - - - - Nacos 配置文件监听事件 - - - - - - - - - - - - - - - 执行 - - - - - - - 停止 - - - - - - - 配置监听事件 - - - - - 收到配置文件变更 - - - diff --git a/Blog.Core.Gateway/Extensions/CustomOcelotSetup.cs b/Blog.Core.Gateway/Extensions/CustomOcelotSetup.cs index f04df2a5..b197c824 100644 --- a/Blog.Core.Gateway/Extensions/CustomOcelotSetup.cs +++ b/Blog.Core.Gateway/Extensions/CustomOcelotSetup.cs @@ -16,11 +16,12 @@ public static void AddCustomOcelotSetup(this IServiceCollection services) { if (services == null) throw new ArgumentNullException(nameof(services)); - var basePath = AppContext.BaseDirectory; - services.AddAuthentication_JWTSetup(); - services.AddOcelot().AddDelegatingHandler().AddNacosDiscovery().AddPolly(); - //.AddConsul().AddPolly(); + services.AddOcelot() + .AddDelegatingHandler() + //.AddNacosDiscovery() + //.AddConsul() + .AddPolly(); } public static async Task UseCustomOcelotMildd(this IApplicationBuilder app) diff --git a/Blog.Core.Gateway/Extensions/CustomSwaggerSetup.cs b/Blog.Core.Gateway/Extensions/CustomSwaggerSetup.cs index 92956ff1..033feeb5 100644 --- a/Blog.Core.Gateway/Extensions/CustomSwaggerSetup.cs +++ b/Blog.Core.Gateway/Extensions/CustomSwaggerSetup.cs @@ -1,11 +1,16 @@ -using Microsoft.AspNetCore.Builder; +using Blog.Core.Common; +using Blog.Core.Extensions.Middlewares; +using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.Filters; +using Swashbuckle.AspNetCore.SwaggerUI; using System; using System.Collections.Generic; using System.IO; using System.Reflection; +using static Blog.Core.Extensions.CustomApiVersion; namespace Blog.Core.Gateway.Extensions { public static class CustomSwaggerSetup @@ -44,23 +49,30 @@ public static void AddCustomSwaggerSetup(this IServiceCollection services) }); } - public static void UseCustomSwaggerMildd(this IApplicationBuilder app) + public static void UseCustomSwaggerMildd(this IApplicationBuilder app, Func streamHtml) { if (app == null) throw new ArgumentNullException(nameof(app)); var apis = new List { "blog-svc" }; - app.UseMvc().UseSwagger(); - app.UseSwaggerUI(options => + app.UseSwagger(); + app.UseSwaggerUI(c => { - options.SwaggerEndpoint($"/swagger/v1/swagger.json", $"Blog.Core.Gateway-v1"); - + c.SwaggerEndpoint($"/swagger/v1/swagger.json", "gateway"); apis.ForEach(m => { - options.SwaggerEndpoint($"/swagger/apiswg/{m}/swagger.json", m); - options.IndexStream = () => app.GetType().GetTypeInfo().Assembly.GetManifestResourceStream("Blog.Core.ApiGateway.index.html"); + c.SwaggerEndpoint($"/swagger/apiswg/{m}/swagger.json", m); }); - options.RoutePrefix = ""; + + if (streamHtml.Invoke() == null) + { + var msg = "index.html的属性,必须设置为嵌入的资源"; + throw new Exception(msg); + } + + c.IndexStream = streamHtml; + + c.RoutePrefix = ""; }); } diff --git a/Blog.Core.Gateway/Helper/CustomJwtTokenAuthMiddleware.cs b/Blog.Core.Gateway/Helper/CustomJwtTokenAuthMiddleware.cs index b00db95d..f9120ec0 100644 --- a/Blog.Core.Gateway/Helper/CustomJwtTokenAuthMiddleware.cs +++ b/Blog.Core.Gateway/Helper/CustomJwtTokenAuthMiddleware.cs @@ -1,16 +1,9 @@ -using System; -using System.Net; -using System.Linq; -using System.Collections.Generic; -using System.Threading.Tasks; +using System.Net; using System.Text.RegularExpressions; 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; namespace Blog.Core.AuthHelper { @@ -23,7 +16,6 @@ public class CustomJwtTokenAuthMiddleware { private readonly ICaching _cache; - private readonly INacosNamingService NacosServClient; /// /// 验证方案提供对象 @@ -36,13 +28,11 @@ public class CustomJwtTokenAuthMiddleware private readonly RequestDelegate _next; - public CustomJwtTokenAuthMiddleware(INacosNamingService serv, RequestDelegate next, IAuthenticationSchemeProvider schemes, AppSettings appset,ICaching cache) + public CustomJwtTokenAuthMiddleware(RequestDelegate next, IAuthenticationSchemeProvider schemes, AppSettings appset,ICaching cache) { - NacosServClient = serv; _cache = cache; _next = next; Schemes = schemes; - List Permissions = _cache.Cof_AsyncGetICaching>("Permissions", GetPermitionData, 10).GetAwaiter().GetResult(); } /// @@ -66,7 +56,7 @@ public async Task Invoke(HttpContext httpContext) return; } - List Permissions= await _cache.Cof_AsyncGetICaching>("Permissions", GetPermitionData, 10); + List Permissions= new(); httpContext.Features.Set(new AuthenticationFeature { @@ -126,28 +116,6 @@ public async Task Invoke(HttpContext httpContext) await _next.Invoke(httpContext); } - private async Task> GetPermitionData() - { - try - { - string PermissionServName = AppSettings.GetValue("ApiGateWay:PermissionServName"); - string PermissionServGroup = AppSettings.GetValue("ApiGateWay:PermissionServGroup"); - string PermissionServUrl = AppSettings.GetValue("ApiGateWay:PermissionServUrl"); - - string requestdata = await NacosServClient.Cof_NaoceGet(PermissionServName, PermissionServGroup, PermissionServUrl); - if (string.IsNullOrEmpty(requestdata)) return null; - JToken perJt = JToken.Parse(requestdata); - if(perJt["response"]!=null) return perJt["response"].ToObject>(); - return perJt["data"].ToObject>(); - } - catch (Exception e) - { - Console.WriteLine(e.Message); - } - - return null; - } - /// /// 返回相应 /// diff --git a/Blog.Core.Gateway/Helper/OcelotConfigurationTask.cs b/Blog.Core.Gateway/Helper/OcelotConfigurationTask.cs deleted file mode 100644 index a95df86e..00000000 --- a/Blog.Core.Gateway/Helper/OcelotConfigurationTask.cs +++ /dev/null @@ -1,147 +0,0 @@ - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Hosting; -using Nacos.V2; -using System; -using System.Threading; -using System.Threading.Tasks; -using Blog.Core.Common.Helper; -using Ocelot.Configuration.Repository; -using Ocelot.Configuration.Creator; -using Newtonsoft.Json.Linq; -using Ocelot.Configuration.File; -using Blog.Core.Common; - -namespace ApiGateway.Helper -{ - /// - /// Nacos配置文件变更事件 - /// - public class OcelotConfigurationTask : BackgroundService - { - private readonly INacosConfigService _configClient; - private readonly INacosNamingService _servClient; - /// - /// Nacos 配置文件监听事件 - /// - private OcelotConfigListener nacosConfigListener = new OcelotConfigListener(); - private AppConfigListener AppConfigListener = new AppConfigListener(); - private string OcelotConfig = ""; - private string OcelotConfigGroup = ""; - private string AppConfig = ""; - private string AppConfigGroup = ""; - - - /// - /// - /// - /// - /// - /// - /// - /// - public OcelotConfigurationTask(INacosNamingService serv, INacosConfigService configClient, IServiceProvider serviceProvider, IInternalConfigurationRepository _internalConfigurationRepo, IInternalConfigurationCreator _internalConfigurationCreator) - { - _configClient = configClient; - _servClient = serv; - nacosConfigListener.internalConfigurationRepo = _internalConfigurationRepo; - nacosConfigListener.internalConfigurationCreator = _internalConfigurationCreator; - OcelotConfig = AppSettings.GetValue("ApiGateWay:OcelotConfig"); - OcelotConfigGroup = AppSettings.GetValue("ApiGateWay:OcelotConfigGroup"); - AppConfig = AppSettings.GetValue("ApiGateWay:AppConfig"); - AppConfigGroup = AppSettings.GetValue("ApiGateWay:AppConfigGroup"); - - - - - string OcelotCfg = configClient.GetConfig(OcelotConfig, OcelotConfigGroup, 10000).GetAwaiter().GetResult(); - nacosConfigListener.ReceiveConfigInfo(OcelotCfg); - string AppCfg= configClient.GetConfig(AppConfig, AppConfigGroup, 10000).GetAwaiter().GetResult(); - AppConfigListener.ReceiveConfigInfo(AppCfg); - //string sss = serv.Cof_NaoceGet("fld-cloud-datax", "DEFAULT_GROUP", "/api/base/deviceList?limit=10&page=1").GetAwaiter().GetResult(); - } - - - - /// - /// 执行 - /// - /// - /// - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - try - { - // Add listener OcelotConfig.json" - await _configClient.AddListener(OcelotConfig, OcelotConfigGroup, nacosConfigListener); - await _configClient.AddListener(AppConfig, AppConfigGroup, AppConfigListener); - } - catch (Exception) - { - } - } - - /// - /// 停止 - /// - /// - /// - public override async Task StopAsync(CancellationToken cancellationToken) - { - // Remove listener - await _configClient.RemoveListener(OcelotConfig, OcelotConfigGroup, nacosConfigListener); - await _configClient.RemoveListener(AppConfig, AppConfigGroup, AppConfigListener); - await base.StopAsync(cancellationToken); - } - } - - /// - /// 配置监听事件 - /// - public class OcelotConfigListener : IListener - { - public IInternalConfigurationRepository internalConfigurationRepo { get; set; } - public IInternalConfigurationCreator internalConfigurationCreator { get; set; } - /// - /// 收到配置文件变更 - /// - /// - public void ReceiveConfigInfo(string configInfo) - { - Task.Run(async () => - { - FileConfiguration filecfg = JToken.Parse(configInfo).ToObject(); - var internalConfiguration = await internalConfigurationCreator.Create(filecfg); - if (!internalConfiguration.IsError) - { - - internalConfigurationRepo.AddOrReplace(internalConfiguration.Data); - } - }); - - - } - } - - public class AppConfigListener : IListener - { - public void ReceiveConfigInfo(string configInfo) - { - var _configurationBuilder = new ConfigurationBuilder(); - _configurationBuilder.Sources.Clear(); - var buffer = System.Text.Encoding.Default.GetBytes(configInfo); - System.IO.MemoryStream ms = new System.IO.MemoryStream(buffer); - _configurationBuilder.AddJsonStream(ms); - var configuration = _configurationBuilder.Build(); - ms.Dispose(); - - - - // 读取配置 将nacos配置中心读取到的配置 替换掉.net core 内存中的 configuration - // 当前监听到配置配置 应该重新断开 重连 刷新等一些中间件操作 - // 比如 mq redis 等其他跟配置相关的中间件 - JsonConfigSettings.Configuration = configuration; - AppSettings.Configuration = configuration; - } - } -} diff --git a/Blog.Core.Gateway/Startup.cs b/Blog.Core.Gateway/Startup.cs index 552b4ef1..1a37cc9f 100644 --- a/Blog.Core.Gateway/Startup.cs +++ b/Blog.Core.Gateway/Startup.cs @@ -1,14 +1,10 @@ using Blog.Core.AuthHelper; using Blog.Core.Common; +using Blog.Core.Common.Caches; using Blog.Core.Extensions; using Blog.Core.Gateway.Extensions; using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Nacos.V2.DependencyInjection; +using System.Reflection; namespace Blog.Core.AdminMvc { @@ -34,17 +30,10 @@ public void ConfigureServices(IServiceCollection services) { services.AddSingleton(new AppSettings(Configuration)); - services.AddAuthentication_JWTSetup(); - services.AddAuthentication() .AddScheme(Permissions.GWName, _ => { }); - services.AddNacosV2Config(Configuration, null, "nacosConfig"); - services.AddNacosV2Naming(Configuration, null, "nacos"); - services.AddHostedService(); - - services.AddCustomSwaggerSetup(); services.AddControllers(); @@ -53,6 +42,10 @@ public void ConfigureServices(IServiceCollection services) services.AddCorsSetup(); + services.AddMemoryCache(); + services.AddDistributedMemoryCache(); + services.AddSingleton(); + services.AddCustomOcelotSetup(); } @@ -69,7 +62,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseAuthentication(); app.UseAuthorization(); - app.UseCustomSwaggerMildd(); + app.UseCustomSwaggerMildd(() => Assembly.GetExecutingAssembly().GetManifestResourceStream("Blog.Core.Gateway.index.html")); app.UseCors(AppSettings.app(new string[] { "Startup", "Cors", "PolicyName" })); @@ -79,7 +72,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) }); app.UseMiddleware(); - + app.UseCustomOcelotMildd().Wait(); } } diff --git a/Blog.Core.Gateway/appsettings.gw.json b/Blog.Core.Gateway/appsettings.gw.json index 33b99ee8..93e64636 100644 --- a/Blog.Core.Gateway/appsettings.gw.json +++ b/Blog.Core.Gateway/appsettings.gw.json @@ -1,6 +1,5 @@ { "Logging": { - "IncludeScopes": false, "Debug": { "LogLevel": { "Default": "Warning" @@ -21,6 +20,11 @@ "IPs": "http://127.0.0.1:2364,http://localhost:2364" } }, + "Redis": { + "Enable": false, + "ConnectionString": "127.0.0.1:6379", + "InstanceName": "" //前缀 + }, "Audience": { "Secret": "sdfsdfsrty45634kkhllghtdgdfss345t678fs", "SecretFile": "C:\\my-file\\blog.core.audience.secret.txt", @@ -31,73 +35,17 @@ { "url": "/" }, { "url": "/illagal/****" }, { "url": "/api3/****" }, - { "url": "/baseapi/swagger.json" } + { "url": "/baseapi/swagger.json" }, + { "url": "/swagger/v1/swagger.json" }, + { "url": "/swagger/apiswg/blog-svc/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.Gateway/ocelot.Development.json b/Blog.Core.Gateway/ocelot.Development.json index 6af5171b..589a5a6e 100644 --- a/Blog.Core.Gateway/ocelot.Development.json +++ b/Blog.Core.Gateway/ocelot.Development.json @@ -49,11 +49,6 @@ ], "GlobalConfiguration": { - "BaseUrl": "http://localhost:9000", - "ServiceDiscoveryProvider": { - "Host": "localhost", - "Port": 8500, - "Type": "Consul" - } + "BaseUrl": "http://localhost:9000" } } \ No newline at end of file From 5e4987d2f6921da920c29841d24cea5977f5a465 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Wed, 13 Dec 2023 11:04:04 +0800 Subject: [PATCH 109/155] feat: :100: change ClaimTypes.Role --- .../Controllers/PermissionController.cs | 17 +++++++++++++++-- Blog.Core.Api/index.html | 4 ++-- .../Authorizations/Policys/PermissionHandler.cs | 14 ++++---------- Blog.Core.Model/Logs/AuditSqlLog.cs | 2 +- Blog.Core.Model/Logs/GlobalErrorLog.cs | 2 +- Blog.Core.Model/Logs/GlobalInformationLog.cs | 2 +- Blog.Core.Model/Logs/GlobalWarningLog.cs | 2 +- Blog.Core.Model/Models/SplitDemo.cs | 2 +- 8 files changed, 26 insertions(+), 19 deletions(-) diff --git a/Blog.Core.Api/Controllers/PermissionController.cs b/Blog.Core.Api/Controllers/PermissionController.cs index a3429163..7f412a04 100644 --- a/Blog.Core.Api/Controllers/PermissionController.cs +++ b/Blog.Core.Api/Controllers/PermissionController.cs @@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using System.Security.Claims; namespace Blog.Core.Controllers { @@ -349,8 +350,14 @@ public async Task> GetNavigationBar(long uid) where item.Type == "sub" select item.Value).FirstOrDefault().ObjToLong(); roleIds = (from item in _httpContext.HttpContext.User.Claims - where item.Type == "role" + where item.Type == ClaimTypes.Role select item.Value.ObjToLong()).ToList(); + if (!roleIds.Any()) + { + roleIds = (from item in _httpContext.HttpContext.User.Claims + where item.Type == "role" + select item.Value.ObjToLong()).ToList(); + } } else { @@ -440,8 +447,14 @@ public async Task>> GetNavigationBarPro(long where item.Type == "sub" select item.Value).FirstOrDefault().ObjToLong(); roleIds = (from item in _httpContext.HttpContext.User.Claims - where item.Type == "role" + where item.Type == ClaimTypes.Role select item.Value.ObjToLong()).ToList(); + if (!roleIds.Any()) + { + roleIds = (from item in _httpContext.HttpContext.User.Claims + where item.Type == "role" + select item.Value.ObjToLong()).ToList(); + } } else { diff --git a/Blog.Core.Api/index.html b/Blog.Core.Api/index.html index eca0b424..99b4caa4 100644 --- a/Blog.Core.Api/index.html +++ b/Blog.Core.Api/index.html @@ -111,9 +111,9 @@