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 01/91] 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 02/91] 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 03/91] 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 04/91] =?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 05/91] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=9C=A8docker?= =?UTF-8?q?=E4=B8=AD=E6=97=A0=E6=B3=95=E8=BF=98=E5=8E=9F=E7=9A=84=E9=97=AE?= =?UTF-8?q?=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 8b4e378a4931bb17849bb21a66698afe651b5c04 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Thu, 20 Jul 2023 12:48:49 +0800 Subject: [PATCH 06/91] 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 07/91] =?UTF-8?q?feat=EF=BC=9Achange=20access=20trend=20lo?= =?UTF-8?q?g?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Blog.Core.Api/Blog.Core.Api.csproj | 2 -- Blog.Core.Api/Blog.Core.Model.xml | 2 +- Blog.Core.Model/Blog.Core.Model.csproj | 2 +- Blog.Core.Model/Models/AccessTrendLog.cs | 2 +- .../QuartzNet/Jobs/Job_AccessTrendLog_Quartz.cs | 8 ++++---- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Blog.Core.Api/Blog.Core.Api.csproj b/Blog.Core.Api/Blog.Core.Api.csproj index f3f9befa..dc7680ca 100644 --- a/Blog.Core.Api/Blog.Core.Api.csproj +++ b/Blog.Core.Api/Blog.Core.Api.csproj @@ -61,8 +61,6 @@
- - diff --git a/Blog.Core.Api/Blog.Core.Model.xml b/Blog.Core.Api/Blog.Core.Model.xml index 1eb3423b..8de49a7b 100644 --- a/Blog.Core.Api/Blog.Core.Model.xml +++ b/Blog.Core.Api/Blog.Core.Model.xml @@ -200,7 +200,7 @@ 用户访问趋势日志 - + 用户 diff --git a/Blog.Core.Model/Blog.Core.Model.csproj b/Blog.Core.Model/Blog.Core.Model.csproj index ac851de2..f1e8764d 100644 --- a/Blog.Core.Model/Blog.Core.Model.csproj +++ b/Blog.Core.Model/Blog.Core.Model.csproj @@ -16,7 +16,7 @@ - + diff --git a/Blog.Core.Model/Models/AccessTrendLog.cs b/Blog.Core.Model/Models/AccessTrendLog.cs index fd6dbae7..bc4848cf 100644 --- a/Blog.Core.Model/Models/AccessTrendLog.cs +++ b/Blog.Core.Model/Models/AccessTrendLog.cs @@ -12,7 +12,7 @@ public class AccessTrendLog : RootEntityTkey /// 用户 /// [SugarColumn(Length = 128, IsNullable = true)] - public string User { get; set; } + public string UserInfo { get; set; } /// /// 次数 diff --git a/Blog.Core.Tasks/QuartzNet/Jobs/Job_AccessTrendLog_Quartz.cs b/Blog.Core.Tasks/QuartzNet/Jobs/Job_AccessTrendLog_Quartz.cs index 1d501c34..68a9aa53 100644 --- a/Blog.Core.Tasks/QuartzNet/Jobs/Job_AccessTrendLog_Quartz.cs +++ b/Blog.Core.Tasks/QuartzNet/Jobs/Job_AccessTrendLog_Quartz.cs @@ -60,7 +60,7 @@ public async Task Run(IJobExecutionContext context) foreach (var item in activeUsers) { - var user = (await _accessTrendLogServices.Query(d => d.User != "" && d.User == item.user)).FirstOrDefault(); + var user = (await _accessTrendLogServices.Query(d => d.UserInfo != "" && d.UserInfo == item.user)).FirstOrDefault(); if (user != null) { user.Count += item.count; @@ -73,13 +73,13 @@ await _accessTrendLogServices.Add(new AccessTrendLog() { Count = item.count, UpdateTime = logUpdate, - User = item.user + UserInfo = item.user }); } } // 重新拉取 - var actUsers = await _accessTrendLogServices.Query(d => d.User != "", d => d.Count, false); + var actUsers = await _accessTrendLogServices.Query(d => d.UserInfo != "", d => d.Count, false); actUsers = actUsers.Take(15).ToList(); List activeUserVMs = new(); @@ -87,7 +87,7 @@ await _accessTrendLogServices.Add(new AccessTrendLog() { activeUserVMs.Add(new ActiveUserVM() { - user = item.User, + user = item.UserInfo, count = item.Count }); } From 3396b6b34d09ab097fd56d4aaee34cf4a1cbc363 Mon Sep 17 00:00:00 2001 From: anjoy8 <3143422472@qq.com> Date: Fri, 21 Jul 2023 10:53:32 +0800 Subject: [PATCH 08/91] 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 09/91] 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 10/91] 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 11/91] 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 12/91] =?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 13/91] 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 14/91] 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 15/91] 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 16/91] =?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 17/91] =?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 18/91] =?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 19/91] =?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 20/91] =?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 21/91] =?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 22/91] 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 23/91] 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 24/91] =?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 25/91] =?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 26/91] 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 27/91] =?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 28/91] =?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 29/91] =?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 30/91] 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 31/91] 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 32/91] =?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 33/91] 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 34/91] 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 35/91] =?UTF-8?q?feat=EF=BC=9A=E5=B0=86=E5=AE=98=E6=96=B9?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E8=BF=81=E7=A7=BB=E5=88=B0=E6=96=B0=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 36/91] =?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 37/91] 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 38/91] 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 39/91] =?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 40/91] 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 41/91] 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 42/91] 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 43/91] =?UTF-8?q?feat=EF=BC=9A=20:aerial=5Ftramway:=20=20R?= =?UTF-8?q?abbitMQ?= 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 44/91] =?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 45/91] 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 @@