diff --git a/Dockerfile b/Dockerfile
index d4999c8..a8f5003 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,27 +1,3 @@
-# FROM mcr.microsoft.com/dotnet/aspnet:8.0
-
-# # 创建目录
-# RUN mkdir /app
-
-# COPY publish /app
-
-
-
-# # 设置工作目录
-# WORKDIR /app
-
-# # 暴露80端口
-# EXPOSE 80
-# # 设置时区 .net6 才有这个问题
-# ENV TZ=Asia/Shanghai
-
-# # 设置环境变量
-# ENV ASPNETCORE_ENVIRONMENT=Production
-
-# ENTRYPOINT ["dotnet", "JiShe.IOT.HttpApi.Host.dll"]
-
-
-
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80
@@ -30,8 +6,8 @@ ENV TZ=Asia/Shanghai
ENV ASPNETCORE_ENVIRONMENT=Production
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
-WORKDIR /src
-COPY ["JiShe.CollectBus.sln", "."]
+# WORKDIR /src
+COPY ["JiShe.CollectBus.Main.sln", "."]
COPY ["common.props", "."]
COPY ["NuGet.Config", "."]
COPY ["web/", "web/"]
@@ -41,10 +17,10 @@ COPY ["shared/", "shared/"]
COPY ["protocols/", "protocols/"]
# 恢复项目依赖
-RUN dotnet restore "JiShe.CollectBus.sln"
+RUN dotnet restore "JiShe.CollectBus.Main.sln"
# 构建项目
-WORKDIR "/src/web/JiShe.CollectBus.Host"
+WORKDIR "/web/JiShe.CollectBus.Host"
RUN dotnet build "JiShe.CollectBus.Host.csproj" -c Release -o /app/build
# 发布项目
@@ -53,6 +29,7 @@ RUN dotnet publish "JiShe.CollectBus.Host.csproj" -c Release -o /app/publish /p:
# 创建最终镜像
FROM base AS final
+
WORKDIR /app
# 创建Plugins目录
diff --git a/JiShe.CollectBus.External.sln b/JiShe.CollectBus.External.sln
new file mode 100644
index 0000000..a1344ff
--- /dev/null
+++ b/JiShe.CollectBus.External.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.9.34728.123
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.PluginFileWatcher", "external\JiShe.CollectBus.PluginFileWatcher\JiShe.CollectBus.PluginFileWatcher.csproj", "{0F67A493-A4DF-550E-AB4D-95F55144C706}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {0F67A493-A4DF-550E-AB4D-95F55144C706}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0F67A493-A4DF-550E-AB4D-95F55144C706}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0F67A493-A4DF-550E-AB4D-95F55144C706}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0F67A493-A4DF-550E-AB4D-95F55144C706}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {4324B3B4-B60B-4E3C-91D8-59576B4E26DD}
+ EndGlobalSection
+EndGlobal
diff --git a/JiShe.CollectBus.Main.sln b/JiShe.CollectBus.Main.sln
new file mode 100644
index 0000000..46d2db3
--- /dev/null
+++ b/JiShe.CollectBus.Main.sln
@@ -0,0 +1,188 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.9.34728.123
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.Domain.Shared", "shared\JiShe.CollectBus.Domain.Shared\JiShe.CollectBus.Domain.Shared.csproj", "{D64C1577-4929-4B60-939E-96DE1534891A}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.Domain", "services\JiShe.CollectBus.Domain\JiShe.CollectBus.Domain.csproj", "{F2840BC7-0188-4606-9126-DADD0F5ABF7A}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.Application.Contracts", "services\JiShe.CollectBus.Application.Contracts\JiShe.CollectBus.Application.Contracts.csproj", "{BD65D04F-08D5-40C1-8C24-77CA0BACB877}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.Application", "services\JiShe.CollectBus.Application\JiShe.CollectBus.Application.csproj", "{78040F9E-3501-4A40-82DF-00A597710F35}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.MongoDB", "modules\JiShe.CollectBus.MongoDB\JiShe.CollectBus.MongoDB.csproj", "{F1C58097-4C08-4D88-8976-6B3389391481}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.HttpApi", "web\JiShe.CollectBus.HttpApi\JiShe.CollectBus.HttpApi.csproj", "{077AA5F8-8B61-420C-A6B5-0150A66FDB34}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.Host", "web\JiShe.CollectBus.Host\JiShe.CollectBus.Host.csproj", "{35829A15-4127-4F69-8BDE-9405DEAACA9A}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.Common", "shared\JiShe.CollectBus.Common\JiShe.CollectBus.Common.csproj", "{AD2F1928-4411-4511-B564-5FB996EC08B9}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.DbMigrator", "services\JiShe.CollectBus.DbMigrator\JiShe.CollectBus.DbMigrator.csproj", "{8BA01C3D-297D-42DF-BD63-EF07202A0A67}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.FreeSql", "modules\JiShe.CollectBus.FreeSql\JiShe.CollectBus.FreeSql.csproj", "{FE0457D9-4038-4A17-8808-DCAD06CFC0A0}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.FreeRedis", "modules\JiShe.CollectBus.FreeRedis\JiShe.CollectBus.FreeRedis.csproj", "{C06C4082-638F-2996-5FED-7784475766C1}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.Kafka", "modules\JiShe.CollectBus.Kafka\JiShe.CollectBus.Kafka.csproj", "{F0288175-F0EC-48BD-945F-CF1512850943}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.IoTDB", "modules\JiShe.CollectBus.IoTDB\JiShe.CollectBus.IoTDB.csproj", "{A3F3C092-0A25-450B-BF6A-5983163CBEF5}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.Protocol.Test", "protocols\JiShe.CollectBus.Protocol.Test\JiShe.CollectBus.Protocol.Test.csproj", "{A377955E-7EA1-6F29-8CF7-774569E93925}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.Cassandra", "modules\JiShe.CollectBus.Cassandra\JiShe.CollectBus.Cassandra.csproj", "{443B4549-0AC0-4493-8F3E-49C83225DD76}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "1.Web", "1.Web", "{A02F7D8A-04DC-44D6-94D4-3F65712D6B94}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "4.Modules", "4.Modules", "{2E0FE301-34C3-4561-9CAE-C7A9E65AEE59}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "5.Protocols", "5.Protocols", "{3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2.Services", "2.Services", "{BA4DA3E7-9AD0-47AD-A0E6-A0BB6700DA23}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "3.Shared", "3.Shared", "{EBF7C01F-9B4F-48E6-8418-2CBFDA51EB0B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Protocol.T1882018", "protocols\JiShe.CollectBus.Protocol.T1882018\JiShe.CollectBus.Protocol.T1882018.csproj", "{430D298B-377E-49B8-83AA-ADC7C0EBDB0F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Protocol.T37612012", "protocols\JiShe.CollectBus.Protocol.T37612012\JiShe.CollectBus.Protocol.T37612012.csproj", "{8A61DF78-069B-40B5-8811-614E2960443E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Protocol", "protocols\JiShe.CollectBus.Protocol\JiShe.CollectBus.Protocol.csproj", "{E27377CC-E2D3-4237-060F-96EA214D3129}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Protocol.T6452007", "protocols\JiShe.CollectBus.Protocol.T6452007\JiShe.CollectBus.Protocol.T6452007.csproj", "{75B7D419-C261-577D-58D6-AA3ACED9129F}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "0.Docs", "0.Docs", "{D8346C4C-55B8-43E8-A6B8-E59D56FE6D92}"
+ ProjectSection(SolutionItems) = preProject
+ .gitignore = .gitignore
+ Dockerfile = Dockerfile
+ NuGet.Config = NuGet.Config
+ PackageAndPublish.bat = PackageAndPublish.bat
+ readme.md = readme.md
+ Temp.Dockerfile = Temp.Dockerfile
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Analyzers.Shared", "shared\JiShe.CollectBus.Analyzers.Shared\JiShe.CollectBus.Analyzers.Shared.csproj", "{DD68F314-BC66-5601-B094-B1A7BE93F4E0}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Analyzers", "modules\JiShe.CollectBus.Analyzers\JiShe.CollectBus.Analyzers.csproj", "{EB97C7BB-1E4A-CBA4-04C1-22DBF48A253A}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {D64C1577-4929-4B60-939E-96DE1534891A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D64C1577-4929-4B60-939E-96DE1534891A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D64C1577-4929-4B60-939E-96DE1534891A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D64C1577-4929-4B60-939E-96DE1534891A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F2840BC7-0188-4606-9126-DADD0F5ABF7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F2840BC7-0188-4606-9126-DADD0F5ABF7A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F2840BC7-0188-4606-9126-DADD0F5ABF7A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F2840BC7-0188-4606-9126-DADD0F5ABF7A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BD65D04F-08D5-40C1-8C24-77CA0BACB877}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BD65D04F-08D5-40C1-8C24-77CA0BACB877}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BD65D04F-08D5-40C1-8C24-77CA0BACB877}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BD65D04F-08D5-40C1-8C24-77CA0BACB877}.Release|Any CPU.Build.0 = Release|Any CPU
+ {78040F9E-3501-4A40-82DF-00A597710F35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {78040F9E-3501-4A40-82DF-00A597710F35}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {78040F9E-3501-4A40-82DF-00A597710F35}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {78040F9E-3501-4A40-82DF-00A597710F35}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F1C58097-4C08-4D88-8976-6B3389391481}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F1C58097-4C08-4D88-8976-6B3389391481}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F1C58097-4C08-4D88-8976-6B3389391481}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F1C58097-4C08-4D88-8976-6B3389391481}.Release|Any CPU.Build.0 = Release|Any CPU
+ {077AA5F8-8B61-420C-A6B5-0150A66FDB34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {077AA5F8-8B61-420C-A6B5-0150A66FDB34}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {077AA5F8-8B61-420C-A6B5-0150A66FDB34}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {077AA5F8-8B61-420C-A6B5-0150A66FDB34}.Release|Any CPU.Build.0 = Release|Any CPU
+ {35829A15-4127-4F69-8BDE-9405DEAACA9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {35829A15-4127-4F69-8BDE-9405DEAACA9A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {35829A15-4127-4F69-8BDE-9405DEAACA9A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {35829A15-4127-4F69-8BDE-9405DEAACA9A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AD2F1928-4411-4511-B564-5FB996EC08B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AD2F1928-4411-4511-B564-5FB996EC08B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AD2F1928-4411-4511-B564-5FB996EC08B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AD2F1928-4411-4511-B564-5FB996EC08B9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8BA01C3D-297D-42DF-BD63-EF07202A0A67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8BA01C3D-297D-42DF-BD63-EF07202A0A67}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8BA01C3D-297D-42DF-BD63-EF07202A0A67}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8BA01C3D-297D-42DF-BD63-EF07202A0A67}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FE0457D9-4038-4A17-8808-DCAD06CFC0A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FE0457D9-4038-4A17-8808-DCAD06CFC0A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FE0457D9-4038-4A17-8808-DCAD06CFC0A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FE0457D9-4038-4A17-8808-DCAD06CFC0A0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C06C4082-638F-2996-5FED-7784475766C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C06C4082-638F-2996-5FED-7784475766C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C06C4082-638F-2996-5FED-7784475766C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C06C4082-638F-2996-5FED-7784475766C1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F0288175-F0EC-48BD-945F-CF1512850943}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F0288175-F0EC-48BD-945F-CF1512850943}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F0288175-F0EC-48BD-945F-CF1512850943}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F0288175-F0EC-48BD-945F-CF1512850943}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A3F3C092-0A25-450B-BF6A-5983163CBEF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A3F3C092-0A25-450B-BF6A-5983163CBEF5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A3F3C092-0A25-450B-BF6A-5983163CBEF5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A3F3C092-0A25-450B-BF6A-5983163CBEF5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A377955E-7EA1-6F29-8CF7-774569E93925}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A377955E-7EA1-6F29-8CF7-774569E93925}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A377955E-7EA1-6F29-8CF7-774569E93925}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A377955E-7EA1-6F29-8CF7-774569E93925}.Release|Any CPU.Build.0 = Release|Any CPU
+ {443B4549-0AC0-4493-8F3E-49C83225DD76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {443B4549-0AC0-4493-8F3E-49C83225DD76}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {443B4549-0AC0-4493-8F3E-49C83225DD76}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {443B4549-0AC0-4493-8F3E-49C83225DD76}.Release|Any CPU.Build.0 = Release|Any CPU
+ {430D298B-377E-49B8-83AA-ADC7C0EBDB0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {430D298B-377E-49B8-83AA-ADC7C0EBDB0F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {430D298B-377E-49B8-83AA-ADC7C0EBDB0F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {430D298B-377E-49B8-83AA-ADC7C0EBDB0F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8A61DF78-069B-40B5-8811-614E2960443E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8A61DF78-069B-40B5-8811-614E2960443E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8A61DF78-069B-40B5-8811-614E2960443E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8A61DF78-069B-40B5-8811-614E2960443E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E27377CC-E2D3-4237-060F-96EA214D3129}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E27377CC-E2D3-4237-060F-96EA214D3129}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E27377CC-E2D3-4237-060F-96EA214D3129}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E27377CC-E2D3-4237-060F-96EA214D3129}.Release|Any CPU.Build.0 = Release|Any CPU
+ {75B7D419-C261-577D-58D6-AA3ACED9129F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {75B7D419-C261-577D-58D6-AA3ACED9129F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {75B7D419-C261-577D-58D6-AA3ACED9129F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {75B7D419-C261-577D-58D6-AA3ACED9129F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DD68F314-BC66-5601-B094-B1A7BE93F4E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DD68F314-BC66-5601-B094-B1A7BE93F4E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DD68F314-BC66-5601-B094-B1A7BE93F4E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DD68F314-BC66-5601-B094-B1A7BE93F4E0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EB97C7BB-1E4A-CBA4-04C1-22DBF48A253A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EB97C7BB-1E4A-CBA4-04C1-22DBF48A253A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EB97C7BB-1E4A-CBA4-04C1-22DBF48A253A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EB97C7BB-1E4A-CBA4-04C1-22DBF48A253A}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {D64C1577-4929-4B60-939E-96DE1534891A} = {EBF7C01F-9B4F-48E6-8418-2CBFDA51EB0B}
+ {F2840BC7-0188-4606-9126-DADD0F5ABF7A} = {BA4DA3E7-9AD0-47AD-A0E6-A0BB6700DA23}
+ {BD65D04F-08D5-40C1-8C24-77CA0BACB877} = {BA4DA3E7-9AD0-47AD-A0E6-A0BB6700DA23}
+ {78040F9E-3501-4A40-82DF-00A597710F35} = {BA4DA3E7-9AD0-47AD-A0E6-A0BB6700DA23}
+ {F1C58097-4C08-4D88-8976-6B3389391481} = {2E0FE301-34C3-4561-9CAE-C7A9E65AEE59}
+ {077AA5F8-8B61-420C-A6B5-0150A66FDB34} = {A02F7D8A-04DC-44D6-94D4-3F65712D6B94}
+ {35829A15-4127-4F69-8BDE-9405DEAACA9A} = {A02F7D8A-04DC-44D6-94D4-3F65712D6B94}
+ {AD2F1928-4411-4511-B564-5FB996EC08B9} = {EBF7C01F-9B4F-48E6-8418-2CBFDA51EB0B}
+ {8BA01C3D-297D-42DF-BD63-EF07202A0A67} = {BA4DA3E7-9AD0-47AD-A0E6-A0BB6700DA23}
+ {FE0457D9-4038-4A17-8808-DCAD06CFC0A0} = {2E0FE301-34C3-4561-9CAE-C7A9E65AEE59}
+ {C06C4082-638F-2996-5FED-7784475766C1} = {2E0FE301-34C3-4561-9CAE-C7A9E65AEE59}
+ {F0288175-F0EC-48BD-945F-CF1512850943} = {2E0FE301-34C3-4561-9CAE-C7A9E65AEE59}
+ {A3F3C092-0A25-450B-BF6A-5983163CBEF5} = {2E0FE301-34C3-4561-9CAE-C7A9E65AEE59}
+ {A377955E-7EA1-6F29-8CF7-774569E93925} = {3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC}
+ {443B4549-0AC0-4493-8F3E-49C83225DD76} = {2E0FE301-34C3-4561-9CAE-C7A9E65AEE59}
+ {430D298B-377E-49B8-83AA-ADC7C0EBDB0F} = {3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC}
+ {8A61DF78-069B-40B5-8811-614E2960443E} = {3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC}
+ {E27377CC-E2D3-4237-060F-96EA214D3129} = {3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC}
+ {75B7D419-C261-577D-58D6-AA3ACED9129F} = {3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC}
+ {DD68F314-BC66-5601-B094-B1A7BE93F4E0} = {EBF7C01F-9B4F-48E6-8418-2CBFDA51EB0B}
+ {EB97C7BB-1E4A-CBA4-04C1-22DBF48A253A} = {2E0FE301-34C3-4561-9CAE-C7A9E65AEE59}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {4324B3B4-B60B-4E3C-91D8-59576B4E26DD}
+ EndGlobalSection
+EndGlobal
diff --git a/JiShe.CollectBus.sln b/JiShe.CollectBus.sln
index 03bd207..272514c 100644
--- a/JiShe.CollectBus.sln
+++ b/JiShe.CollectBus.sln
@@ -37,7 +37,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "1.Web", "1.Web", "{A02F7D8A
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "4.Modules", "4.Modules", "{2E0FE301-34C3-4561-9CAE-C7A9E65AEE59}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "6.Protocols", "6.Protocols", "{3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "5.Protocols", "5.Protocols", "{3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2.Services", "2.Services", "{BA4DA3E7-9AD0-47AD-A0E6-A0BB6700DA23}"
EndProject
@@ -45,8 +45,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "3.Shared", "3.Shared", "{EB
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Kafka.Test", "modules\JiShe.CollectBus.Kafka.Test\JiShe.CollectBus.Kafka.Test.csproj", "{6D6A2A58-7406-9C8C-7B23-3E442CCE3E6B}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "5.External", "5.External", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Protocol.T1882018", "protocols\JiShe.CollectBus.Protocol.T1882018\JiShe.CollectBus.Protocol.T1882018.csproj", "{430D298B-377E-49B8-83AA-ADC7C0EBDB0F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Protocol.T37612012", "protocols\JiShe.CollectBus.Protocol.T37612012\JiShe.CollectBus.Protocol.T37612012.csproj", "{8A61DF78-069B-40B5-8811-614E2960443E}"
@@ -55,8 +53,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Protocol",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Protocol.T6452007", "protocols\JiShe.CollectBus.Protocol.T6452007\JiShe.CollectBus.Protocol.T6452007.csproj", "{75B7D419-C261-577D-58D6-AA3ACED9129F}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.PluginFileWatcher", "external\JiShe.CollectBus.PluginFileWatcher\JiShe.CollectBus.PluginFileWatcher.csproj", "{0F67A493-A4DF-550E-AB4D-95F55144C706}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "0.Docs", "0.Docs", "{D8346C4C-55B8-43E8-A6B8-E59D56FE6D92}"
ProjectSection(SolutionItems) = preProject
.gitignore = .gitignore
@@ -66,6 +62,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "0.Docs", "0.Docs", "{D8346C
readme.md = readme.md
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Analyzers.Shared", "shared\JiShe.CollectBus.Analyzers.Shared\JiShe.CollectBus.Analyzers.Shared.csproj", "{DD68F314-BC66-5601-B094-B1A7BE93F4E0}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Analyzers", "modules\JiShe.CollectBus.Analyzers\JiShe.CollectBus.Analyzers.csproj", "{EB97C7BB-1E4A-CBA4-04C1-22DBF48A253A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Migration.Application.Contracts", "services\JiShe.CollectBus.Migration.Application.Contracts\JiShe.CollectBus.Migration.Application.Contracts.csproj", "{E01625B5-B5B7-7A4B-468F-EC12C1BDBA2A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Migration.Application", "services\JiShe.CollectBus.Migration.Application\JiShe.CollectBus.Migration.Application.csproj", "{B955C5DA-3C20-35D2-0770-8FE473C41C44}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Migration.Host", "web\JiShe.CollectBus.Migration.Host\JiShe.CollectBus.Migration.Host.csproj", "{995D3D91-7221-D4A3-A7B2-FEC202328A18}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Migration.HttpApi", "web\JiShe.CollectBus.Migration.HttpApi\JiShe.CollectBus.Migration.HttpApi.csproj", "{8A113DE5-7D50-6E6B-739F-B6EEAD5E13B4}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -152,10 +160,30 @@ Global
{75B7D419-C261-577D-58D6-AA3ACED9129F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{75B7D419-C261-577D-58D6-AA3ACED9129F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{75B7D419-C261-577D-58D6-AA3ACED9129F}.Release|Any CPU.Build.0 = Release|Any CPU
- {0F67A493-A4DF-550E-AB4D-95F55144C706}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {0F67A493-A4DF-550E-AB4D-95F55144C706}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {0F67A493-A4DF-550E-AB4D-95F55144C706}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {0F67A493-A4DF-550E-AB4D-95F55144C706}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DD68F314-BC66-5601-B094-B1A7BE93F4E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DD68F314-BC66-5601-B094-B1A7BE93F4E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DD68F314-BC66-5601-B094-B1A7BE93F4E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DD68F314-BC66-5601-B094-B1A7BE93F4E0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EB97C7BB-1E4A-CBA4-04C1-22DBF48A253A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EB97C7BB-1E4A-CBA4-04C1-22DBF48A253A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EB97C7BB-1E4A-CBA4-04C1-22DBF48A253A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EB97C7BB-1E4A-CBA4-04C1-22DBF48A253A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E01625B5-B5B7-7A4B-468F-EC12C1BDBA2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E01625B5-B5B7-7A4B-468F-EC12C1BDBA2A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E01625B5-B5B7-7A4B-468F-EC12C1BDBA2A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E01625B5-B5B7-7A4B-468F-EC12C1BDBA2A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B955C5DA-3C20-35D2-0770-8FE473C41C44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B955C5DA-3C20-35D2-0770-8FE473C41C44}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B955C5DA-3C20-35D2-0770-8FE473C41C44}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B955C5DA-3C20-35D2-0770-8FE473C41C44}.Release|Any CPU.Build.0 = Release|Any CPU
+ {995D3D91-7221-D4A3-A7B2-FEC202328A18}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {995D3D91-7221-D4A3-A7B2-FEC202328A18}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {995D3D91-7221-D4A3-A7B2-FEC202328A18}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {995D3D91-7221-D4A3-A7B2-FEC202328A18}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8A113DE5-7D50-6E6B-739F-B6EEAD5E13B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8A113DE5-7D50-6E6B-739F-B6EEAD5E13B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8A113DE5-7D50-6E6B-739F-B6EEAD5E13B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8A113DE5-7D50-6E6B-739F-B6EEAD5E13B4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -181,7 +209,12 @@ Global
{8A61DF78-069B-40B5-8811-614E2960443E} = {3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC}
{E27377CC-E2D3-4237-060F-96EA214D3129} = {3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC}
{75B7D419-C261-577D-58D6-AA3ACED9129F} = {3C3F9DB2-EC97-4464-B49F-BF1A0C2B46DC}
- {0F67A493-A4DF-550E-AB4D-95F55144C706} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+ {DD68F314-BC66-5601-B094-B1A7BE93F4E0} = {EBF7C01F-9B4F-48E6-8418-2CBFDA51EB0B}
+ {EB97C7BB-1E4A-CBA4-04C1-22DBF48A253A} = {2E0FE301-34C3-4561-9CAE-C7A9E65AEE59}
+ {E01625B5-B5B7-7A4B-468F-EC12C1BDBA2A} = {BA4DA3E7-9AD0-47AD-A0E6-A0BB6700DA23}
+ {B955C5DA-3C20-35D2-0770-8FE473C41C44} = {BA4DA3E7-9AD0-47AD-A0E6-A0BB6700DA23}
+ {995D3D91-7221-D4A3-A7B2-FEC202328A18} = {A02F7D8A-04DC-44D6-94D4-3F65712D6B94}
+ {8A113DE5-7D50-6E6B-739F-B6EEAD5E13B4} = {A02F7D8A-04DC-44D6-94D4-3F65712D6B94}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4324B3B4-B60B-4E3C-91D8-59576B4E26DD}
diff --git a/PackageAndPublish.bat b/PackageAndPublish.bat
index 0bb73cd..24ce317 100644
--- a/PackageAndPublish.bat
+++ b/PackageAndPublish.bat
@@ -5,6 +5,7 @@ set VERSION=1.0.0
set CONFIGURATION=Release
set OUTPUT_DIR=%~dp0\nupkgs
set API_KEY=your-nuget-api-key
+set SOURCE=https://api.nuget.org/v3/index.json
REM 创建输出目录
if not exist "%OUTPUT_DIR%" mkdir "%OUTPUT_DIR%"
@@ -22,11 +23,10 @@ echo 开始打包项目...
REM 打包 Protocol 项目
echo 打包 Protocol 项目...
-call :PackProject protocols\JiShe.CollectBus.Protocol\JiShe.CollectBus.Protocol.csproj
+call :
+ protocols\JiShe.CollectBus.Protocol\JiShe.CollectBus.Protocol.csproj
call :PackProject protocols\JiShe.CollectBus.Protocol.Contracts\JiShe.CollectBus.Protocol.Contracts.csproj
-call :PackProject protocols\JiShe.CollectBus.Protocol.T1882018\JiShe.CollectBus.Protocol.T1882018.csproj
call :PackProject protocols\JiShe.CollectBus.Protocol.T37612012\JiShe.CollectBus.Protocol.T37612012.csproj
-call :PackProject protocols\JiShe.CollectBus.Protocol.T6452007\JiShe.CollectBus.Protocol.T6452007.csproj
REM 打包 Modules 项目
echo 打包 Modules 项目...
@@ -36,7 +36,6 @@ call :PackProject modules\JiShe.CollectBus.IoTDB\JiShe.CollectBus.IoTDB.csproj
call :PackProject modules\JiShe.CollectBus.MongoDB\JiShe.CollectBus.MongoDB.csproj
call :PackProject modules\JiShe.CollectBus.FreeSql\JiShe.CollectBus.FreeSql.csproj
call :PackProject modules\JiShe.CollectBus.Cassandra\JiShe.CollectBus.Cassandra.csproj
-call :PackProject modules\JiShe.CollectBusMultiProcessing\JiShe.CollectBusMultiProcessing.csproj
REM 打包 Shared 项目
echo 打包 Shared 项目...
@@ -44,11 +43,11 @@ call :PackProject shared\JiShe.CollectBus.Common\JiShe.CollectBus.Common.csproj
call :PackProject shared\JiShe.CollectBus.Domain.Shared\JiShe.CollectBus.Domain.Shared.csproj
REM 打包 Services 项目
-echo 打包 Services 项目...
-call :PackProject services\JiShe.CollectBus.Domain\JiShe.CollectBus.Domain.csproj
-call :PackProject services\JiShe.CollectBus.Application.Contracts\JiShe.CollectBus.Application.Contracts.csproj
-call :PackProject services\JiShe.CollectBus.Application\JiShe.CollectBus.Application.csproj
-call :PackProject services\JiShe.CollectBus.EntityFrameworkCore\JiShe.CollectBus.EntityFrameworkCore.csproj
+@REM echo 打包 Services 项目...
+@REM call :PackProject services\JiShe.CollectBus.Domain\JiShe.CollectBus.Domain.csproj
+@REM call :PackProject services\JiShe.CollectBus.Application.Contracts\JiShe.CollectBus.Application.Contracts.csproj
+@REM call :PackProject services\JiShe.CollectBus.Application\JiShe.CollectBus.Application.csproj
+@REM call :PackProject services\JiShe.CollectBus.EntityFrameworkCore\JiShe.CollectBus.EntityFrameworkCore.csproj
echo.
echo 是否要发布包到 NuGet? (Y/N)
@@ -58,7 +57,7 @@ if /i "%PUBLISH_CHOICE%"=="Y" (
echo 开始发布包...
for %%f in ("%OUTPUT_DIR%\*.nupkg") do (
echo 发布: %%f
- dotnet nuget push "%%f" --api-key %API_KEY% --source https://api.nuget.org/v3/index.json --skip-duplicate
+ dotnet nuget push "%%f" --api-key %API_KEY% --source %SOURCE% --skip-duplicate
)
echo 所有包已发布完成!
) else (
@@ -70,7 +69,7 @@ goto :eof
:PackProject
if exist "%~1" (
echo 打包: %~1
- dotnet pack "%~1" -c %CONFIGURATION% --no-build --include-symbols -p:SymbolPackageFormat=snupkg -p:PackageVersion=%VERSION% -o "%OUTPUT_DIR%"
+ dotnet pack "%~1" -c %CONFIGURATION% --include-symbols -p:SymbolPackageFormat=snupkg -p:PackageVersion=%VERSION% -o "%OUTPUT_DIR%"
) else (
echo 警告: 项目不存在 - %~1
)
diff --git a/Temp.Dockerfile b/Temp.Dockerfile
new file mode 100644
index 0000000..e63b6e0
--- /dev/null
+++ b/Temp.Dockerfile
@@ -0,0 +1,52 @@
+FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
+WORKDIR /app
+EXPOSE 80
+EXPOSE 443
+ENV TZ=Asia/Shanghai
+ENV ASPNETCORE_ENVIRONMENT=Production
+
+FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
+
+COPY ["publish/", "/app/publish"]
+
+# # WORKDIR /src
+# COPY ["JiShe.CollectBus.Main.sln", "."]
+# COPY ["common.props", "."]
+# COPY ["NuGet.Config", "."]
+# COPY ["web/", "web/"]
+# COPY ["modules/", "modules/"]
+# COPY ["services/", "services/"]
+# COPY ["shared/", "shared/"]
+# COPY ["protocols/", "protocols/"]
+
+# # 恢复项目依赖
+# RUN dotnet restore "JiShe.CollectBus.Main.sln"
+
+# # 构建项目
+# WORKDIR "/web/JiShe.CollectBus.Host"
+# RUN dotnet build "JiShe.CollectBus.Host.csproj" -c Release -o /app/build
+
+# 发布项目
+FROM build AS publish
+# RUN dotnet publish "JiShe.CollectBus.Host.csproj" -c Release -o /app/publish /p:UseAppHost=false
+
+
+# 创建最终镜像
+FROM base AS final
+
+WORKDIR /app
+
+# 创建Plugins目录
+RUN mkdir -p /app/Plugins
+
+# 复制发布内容
+COPY --from=publish /app/publish .
+
+# 设置入口点
+ENTRYPOINT ["dotnet", "JiShe.CollectBus.Host.dll"]
+
+# 启动命令
+# 可选:添加命令行参数
+# CMD ["--urls", "http://+:80"]
+
+
diff --git a/modules/JiShe.CollectBus.Analyzers/ComplexTypeSourceAnalyzers.cs b/modules/JiShe.CollectBus.Analyzers/ComplexTypeSourceAnalyzers.cs
new file mode 100644
index 0000000..00a7129
--- /dev/null
+++ b/modules/JiShe.CollectBus.Analyzers/ComplexTypeSourceAnalyzers.cs
@@ -0,0 +1,720 @@
+using JiShe.CollectBus.Analyzers.Shared;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+
+namespace JiShe.CollectBus.IncrementalGenerator
+{
+ ///
+ /// 复杂类型增量源生成器
+ ///
+ [Generator(LanguageNames.CSharp)]
+ public class ComplexTypeSourceAnalyzers : IIncrementalGenerator
+ {
+ private const string AttributeFullName = "JiShe.CollectBus.Analyzers.Shared.SourceAnalyzersAttribute";
+
+ public void Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ //Debugger.Launch();
+
+ // 步骤1:筛选带有 [SourceAnalyzers] 的类
+ var classDeclarations = context.SyntaxProvider
+ .CreateSyntaxProvider(
+ predicate: static (s, _) => IsClassWithAttribute(s),
+ transform: static (ctx, _) => GetClassDeclaration(ctx))
+ .Where(static c => c is not null);
+
+ // 步骤2:合并编译信息
+ var compilationAndClasses = context.CompilationProvider.Combine(classDeclarations.Collect());
+
+ context.RegisterSourceOutput(compilationAndClasses, (spc, source) =>
+ GenerateCode(source.Left, source.Right!, spc));
+ }
+
+ private static bool IsClassWithAttribute(SyntaxNode node) => node is ClassDeclarationSyntax cds && cds.AttributeLists.Count > 0;
+
+ private static ClassDeclarationSyntax GetClassDeclaration(GeneratorSyntaxContext context)
+ {
+ var classDecl = (ClassDeclarationSyntax)context.Node;
+ var semanticModel = context.SemanticModel;
+
+ // 获取类符号
+ var classSymbol = semanticModel.GetDeclaredSymbol(classDecl) as INamedTypeSymbol;
+ if (classSymbol == null) return null;
+
+ // 检查是否包含 SourceAnalyzers 特性
+ var sourceAnalyzerAttr = classSymbol.GetAttributes().FirstOrDefault(attr => attr.AttributeClass?.ToDisplayString() == AttributeFullName);
+
+ // 必须包含 EntityType 参数
+ if (sourceAnalyzerAttr == null ||
+ sourceAnalyzerAttr.ConstructorArguments.Length == 0)
+ {
+ return null;
+ }
+
+ return classDecl;
+
+ //var classDecl = (ClassDeclarationSyntax)context.Node;
+ //var attributeType = context.SemanticModel.Compilation.GetTypeByMetadataName(AttributeFullName);
+
+ //foreach (var attribute in classDecl.AttributeLists.SelectMany(al => al.Attributes))
+ //{
+ // var symbol = context.SemanticModel.GetSymbolInfo(attribute).Symbol;
+ // if (symbol is IMethodSymbol ctor &&
+ // SymbolEqualityComparer.Default.Equals(ctor.ContainingType, attributeType))
+ // {
+ // return classDecl;
+ // }
+ //}
+ //return null;
+ }
+
+ ///
+ /// 递归获取所有层级的属性
+ ///
+ ///
+ ///
+ private static IEnumerable GetAllPropertiesInHierarchy(INamedTypeSymbol classSymbol)
+ {
+ var currentSymbol = classSymbol;
+ while (currentSymbol != null)
+ {
+ foreach (var prop in currentSymbol.GetMembers().OfType())
+ {
+ yield return prop;
+ }
+ currentSymbol = currentSymbol.BaseType; // 向上遍历基类
+ }
+ }
+
+ ///
+ /// 生成代码
+ ///
+ ///
+ ///
+ ///
+ private static void GenerateCode(
+ Compilation compilation,
+ IEnumerable classes,
+ SourceProductionContext context)
+ {
+ var processedTypes = new HashSet(SymbolEqualityComparer.Default);
+
+ if (!classes.Any())
+ {
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor("GEN002", "没有目标类",
+ "没有找到SourceAnalyzers标记的类", "Debug", DiagnosticSeverity.Warning, true),
+ Location.None));
+ }
+
+ foreach (var classDecl in classes.Distinct())
+ {
+ var model = compilation.GetSemanticModel(classDecl.SyntaxTree);
+ var classSymbol = model.GetDeclaredSymbol(classDecl) as INamedTypeSymbol;
+
+ if (classSymbol == null || !processedTypes.Add(classSymbol))
+ {
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor("GEN003", "无效符号",
+ $"类名称为{classDecl.Identifier.Text} 符号为空", "Error", DiagnosticSeverity.Error, true),
+ Location.None));
+ continue;
+ }
+
+ var code3 = BuildAccessorsForSourceEntity(classSymbol, compilation, processedTypes);
+ context.AddSource($"{classSymbol.Name}Accessor.g.cs", code3);
+ }
+
+ // 生成工厂注册代码
+ context.AddSource("SourceEntityAccessorFactory.g.cs", BuildFactoryCode());
+ }
+
+ ///
+ /// 获取泛型参数
+ ///
+ ///
+ ///
+ public static string GetGenericParams(INamedTypeSymbol symbol)
+ {
+ if (!symbol.IsGenericType) return "";
+ var parameters = symbol.TypeParameters.Select(t => t.Name);
+ return $"<{string.Join(", ", parameters)}>";
+ }
+
+
+ ///
+ /// 生成标准属性的访问器
+ ///
+ ///
+ ///
+ ///
+ private static void GenerateStandardAccessors(IPropertySymbol prop, INamedTypeSymbol propType, StringBuilder code)
+ {
+ var parentType = prop.ContainingType.ToDisplayString();
+ code.AppendLine($" public static {propType.ToDisplayString()} Get{prop.Name}({parentType} obj) => obj.{prop.Name};");
+
+ if (prop.SetMethod != null)
+ {
+ code.AppendLine($" public static void Set{prop.Name}({parentType} obj, {propType.ToDisplayString()} value) => obj.{prop.Name} = value;");
+ }
+ }
+
+
+ ///
+ /// 构建实体访问器代码(支持泛型)
+ ///
+ private static string BuildAccessorsForSourceEntity(
+ INamedTypeSymbol classSymbol,
+ Compilation compilation,
+ HashSet processedTypes)
+ {
+ // 获取 SourceAnalyzers 特性的 EntityType 参数
+ var sourceAnalyzerAttr = classSymbol.GetAttributes()
+ .FirstOrDefault(attr =>
+ attr.AttributeClass?.ToDisplayString() == AttributeFullName);
+
+ // 解析 EntityType 枚举值
+ string entityTypeValue = "EntityTypeEnum.Other"; // 默认值
+ if (sourceAnalyzerAttr != null &&
+ sourceAnalyzerAttr.ConstructorArguments.Length > 0)
+ {
+ var arg = sourceAnalyzerAttr.ConstructorArguments[0];
+ if (arg.Kind == TypedConstantKind.Enum &&
+ arg.Type is INamedTypeSymbol enumType)
+ {
+ int enumValue = (int)arg.Value!;
+ entityTypeValue = GetEnumMemberName(enumType, enumValue);
+ }
+ }
+
+ var code = new StringBuilder();
+ code.AppendLine("// ");
+ code.AppendLine("#nullable enable");
+ code.AppendLine("using System;");
+ code.AppendLine("using System.Reflection;");
+ code.AppendLine("using System.Collections.Generic;");
+ code.AppendLine("using JiShe.CollectBus.Analyzers.Shared;");
+ code.AppendLine($"namespace {classSymbol.ContainingNamespace.ToDisplayString()};");
+ code.AppendLine();
+
+ // 处理泛型类型名称
+ var accessibility = classSymbol.DeclaredAccessibility switch
+ {
+ Accessibility.Public => "public",
+ _ => "internal"
+ };
+
+ var genericParams = classSymbol.IsGenericType
+ ? $"<{string.Join(", ", classSymbol.TypeParameters.Select(t => t.Name))}>"
+ : "";
+
+ code.AppendLine(
+ $"{accessibility} sealed class {classSymbol.Name}Accessor{genericParams} " + // 保留泛型参数
+ $": ISourceEntityAccessor<{classSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}>");
+
+ code.AppendLine("{");
+
+ var propList = GetAllPropertiesInHierarchy(classSymbol);
+
+ //类名称
+ code.AppendLine($" public string EntityName {{get;}} = \"{classSymbol.Name}\";");
+ // 添加 EntityType 属性
+ code.AppendLine($" public EntityTypeEnum? EntityType {{ get; }} = {entityTypeValue};");
+
+ foreach (var prop in propList)
+ {
+ // 安全类型转换
+ if (prop.Type is not ITypeSymbol propType) continue;
+
+ if (propType is INamedTypeSymbol namedType)
+ {
+ GenerateStandardAccessors(prop, namedType, code);
+ }
+
+ if (propType is INamedTypeSymbol { IsTupleType: true } tupleType)
+ {
+ GenerateTupleAccessors(prop, tupleType, code);
+ }
+ }
+
+ //生成当前类属性名称集合
+ GeneratePropertyListForSourceEntity(propList, code, compilation, classSymbol);
+
+ //生成当前类属性信息集合
+ GenerateEntityMemberInfoList(propList, code, compilation, classSymbol);
+
+
+ //生成当前类属性访问
+ GetGeneratePropertyValueForSourceEntity(propList, code, compilation, classSymbol);
+
+ //生成当前类属性设置
+ SetGeneratePropertyValueForSourceEntity(propList, code, compilation, classSymbol);
+
+ code.AppendLine("}");
+ return code.ToString();
+ }
+
+ ///
+ /// 生成ValueTuple元组属性访问器
+ ///
+ ///
+ ///
+ ///
+ private static void GenerateTupleAccessors(
+ IPropertySymbol prop,
+ INamedTypeSymbol tupleType,
+ StringBuilder code)
+ {
+ var parentType = prop.ContainingType.ToDisplayString();
+ var tupleElements = tupleType.TupleElements;
+
+ for (int i = 0; i < tupleElements.Length; i++)
+ {
+ var element = tupleElements[i];
+ var elementType = element.Type.ToDisplayString();
+ var elementName = element.Name;
+
+ // Getter
+ code.AppendLine($"public static {elementType} Get{prop.Name}_{elementName}({parentType} obj) => obj.{prop.Name}.{elementName};");
+
+ // Setter
+ if (prop.SetMethod != null)
+ {
+ code.AppendLine($"public static void Set{prop.Name}_{elementName}({parentType} obj, {elementType} value) => obj.{prop.Name} = ({string.Join(", ", GetTupleElements(prop.Name, tupleElements, i))});");
+ }
+ }
+ }
+
+ private static IEnumerable GetTupleElements(
+ string propName,
+ ImmutableArray elements,
+ int targetIndex)
+ {
+ for (int i = 0; i < elements.Length; i++)
+ {
+ yield return i == targetIndex
+ ? "value"
+ : $"obj.{propName}.{elements[i].Name}";
+ }
+ }
+
+ ///
+ /// 处理System.Tuple类型的访问器生成
+ ///
+ private static void GenerateSystemTupleAccessors(
+ IPropertySymbol prop,
+ INamedTypeSymbol tupleType,
+ StringBuilder code,
+ INamedTypeSymbol classSymbol)
+ {
+ var parentType = classSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
+ var elementTypes = tupleType.TypeArguments;
+ var tupleTypeName = tupleType.ToDisplayString();
+
+ for (int i = 0; i < elementTypes.Length; i++)
+ {
+ var elementType = elementTypes[i].ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
+ var elementName = $"Item{i + 1}";
+
+ // Getter
+ code.AppendLine(
+ $" public static {elementType} Get{prop.Name}_{elementName}" +
+ $"({parentType} obj) => obj.{prop.Name}.{elementName};");
+
+ // Setter
+ if (prop.SetMethod != null)
+ {
+ var assignments = elementTypes.Select((_, idx) =>
+ idx == i ? "value" : $"obj.{prop.Name}.Item{idx + 1}"
+ ).ToList();
+
+ code.AppendLine(
+ $" public static void Set{prop.Name}_{elementName}" +
+ $"({parentType} obj, {elementType} value) => " +
+ $"obj.{prop.Name} = new {tupleTypeName}({string.Join(", ", assignments)});");
+ }
+ }
+ }
+
+ ///
+ /// 增强的工厂类实现
+ ///
+ private static string BuildFactoryCode()
+ {
+ return """
+ using System;
+ using System.Collections.Concurrent;
+ using System.Reflection;
+
+ namespace JiShe.CollectBus.Analyzers.Shared;
+
+ public static class SourceEntityAccessorFactory
+ {
+ private static readonly ConcurrentDictionary _accessors = new();
+
+ public static ISourceEntityAccessor GetAccessor()
+ {
+ return (ISourceEntityAccessor)_accessors.GetOrAdd(typeof(T), t =>
+ {
+ // 获取泛型类型定义信息(如果是泛型类型)
+ var isGeneric = t.IsGenericType;
+ var genericTypeDef = isGeneric ? t.GetGenericTypeDefinition() : null;
+ var arity = isGeneric ? genericTypeDef!.GetGenericArguments().Length : 0;
+
+ // 构建访问器类名
+ var typeName = isGeneric
+ ? $"{t.Namespace}.{genericTypeDef!.Name.Split('`')[0]}Accessor`{arity}"
+ : $"{t.Namespace}.{t.Name}Accessor";
+
+ // 尝试从当前程序集加载
+ var accessorType = Assembly.GetAssembly(t)!.GetType(typeName)
+ ?? throw new InvalidOperationException($"Accessor type {typeName} not found");
+
+ // 处理泛型参数
+ if (isGeneric && accessorType.IsGenericTypeDefinition)
+ {
+ accessorType = accessorType.MakeGenericType(t.GetGenericArguments());
+ }
+
+ return Activator.CreateInstance(accessorType)!;
+ });
+ }
+ }
+ """;
+ }
+
+ ///
+ /// 属性访问生成逻辑
+ ///
+ /// 属性集合
+ ///
+ ///
+ ///
+ private static void GetGeneratePropertyValueForSourceEntity(
+ IEnumerable propList,
+ StringBuilder code,
+ Compilation compilation,
+ INamedTypeSymbol classSymbol)
+ {
+ code.AppendLine($" public object GetPropertyValue({classSymbol} targetEntity, string propertyName)");
+ code.AppendLine(" {");
+ code.AppendLine(" return propertyName switch");
+ code.AppendLine(" {");
+
+ foreach (var prop in propList)
+ {
+ code.AppendLine(
+ $" \"{prop.Name}\" => " +
+ $"Get{prop.Name}(targetEntity),");
+
+ if (prop.Type is INamedTypeSymbol { IsTupleType: true } tupleType)
+ {
+ foreach (var element in tupleType.TupleElements)
+ {
+ code.AppendLine(
+ $" \"{prop.Name}.{element.Name}\" => " +
+ $"Get{prop.Name}_{element.Name}(targetEntity),");
+ }
+ }
+
+ }
+
+ code.AppendLine(" _ => throw new ArgumentException($\"Unknown property: {propertyName}\")");
+ code.AppendLine(" };");
+ code.AppendLine(" }");
+ }
+
+ ///
+ /// 属性设置生成逻辑
+ ///
+ /// 属性集合
+ ///
+ ///
+ ///
+ private static void SetGeneratePropertyValueForSourceEntity(
+ IEnumerable propList,
+ StringBuilder code,
+ Compilation compilation,
+ INamedTypeSymbol classSymbol)
+ {
+ code.AppendLine($" public void SetPropertyValue({classSymbol} targetEntity, string propertyName, object value)");
+ code.AppendLine(" {");
+ code.AppendLine(" switch (propertyName)");
+ code.AppendLine(" {");
+
+ foreach (var prop in propList)
+ {
+ var propType = prop.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
+ code.AppendLine($" case \"{prop.Name}\":");
+ code.AppendLine($" Set{prop.Name}(");
+ code.AppendLine($" targetEntity, ({propType})value);");
+ code.AppendLine(" break;");
+
+ if (prop.Type is INamedTypeSymbol { IsTupleType: true } tupleType)
+ {
+ foreach (var element in tupleType.TupleElements)
+ {
+ var elementType = element.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
+ code.AppendLine($" case \"{prop.Name}.{element.Name}\":");
+ code.AppendLine($" Set{prop.Name}_{element.Name}(");
+ code.AppendLine($" targetEntity, ({elementType})value);");
+ code.AppendLine(" break;");
+ }
+ }
+ }
+
+ code.AppendLine(" default:");
+ code.AppendLine(" throw new ArgumentException($\"Unknown property: {propertyName}\");");
+ code.AppendLine(" }");
+ code.AppendLine(" }");
+ }
+
+ ///
+ /// 属性名称集合
+ ///
+ /// 属性集合
+ ///
+ ///
+ ///
+ private static void GeneratePropertyListForSourceEntity(
+ IEnumerable propList,
+ StringBuilder code,
+ Compilation compilation,
+ INamedTypeSymbol classSymbol)
+ {
+ code.AppendLine(" public List PropertyNameList {get;} = new List()");
+ code.AppendLine(" {");
+ List tempPropList = new List();
+ foreach (var prop in propList)
+ {
+ if (prop.Type is INamedTypeSymbol { IsTupleType: true } tupleType)
+ {
+ foreach (var element in tupleType.TupleElements)
+ {
+ tempPropList.Add($"\"{prop.Name}.{element.Name}\"");
+ }
+ }
+ else
+ {
+ tempPropList.Add($"\"{prop.Name}\"");
+ }
+ }
+
+ code.Append(string.Join(",", tempPropList));
+
+ code.AppendLine(" };");
+ }
+
+
+ ///
+ /// 生成当前类属性信息集合
+ ///
+ private static void GenerateEntityMemberInfoList(
+ IEnumerable propList,
+ StringBuilder code,
+ Compilation compilation,
+ INamedTypeSymbol classSymbol)
+ {
+ code.AppendLine(" public List MemberList { get; } = new()");
+ code.AppendLine(" {");
+
+ var initializerLines = new List();
+
+ foreach (var prop in propList)
+ {
+ var entityType = prop.ContainingType.ToDisplayString();//entity 实体类型名称
+ var propType = prop.Type;//实体属性的类型
+ var propTypeName = propType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
+ // var declaredTypeName = propType.Name; // 直接获取类型名称(如 "Int32")
+ // 处理可空类型,获取底层具体类型名称
+ var declaredTypeName = propType switch
+ {
+ INamedTypeSymbol { OriginalDefinition.SpecialType: SpecialType.System_Nullable_T } nullableType =>
+ nullableType.TypeArguments[0].Name, // 提取如 "Int32"
+ _ => propType.Name
+ };
+
+ // 处理主属性
+ var propAttributes = prop.GetAttributes()
+ .Where(a => !IsCompilerGeneratedAttribute(a))
+ .ToList();
+
+ var attributeInitializers = propAttributes
+ .Select(GenerateAttributeInitializer)
+ .Where(s => !string.IsNullOrEmpty(s));
+
+ var mainMember = new StringBuilder();
+ mainMember.Append(
+ $"new EntityMemberInfo(" +
+ $"\"{prop.Name}\", " +
+ $"typeof({propTypeName}), " +
+ $"\"{declaredTypeName}\", " +
+ $"(e) => Get{prop.Name}(({entityType})e), " +
+ $"(e, v) => Set{prop.Name}(({entityType})e, ({propTypeName})v))");
+
+ if (attributeInitializers.Any())
+ {
+ mainMember.AppendLine();
+ mainMember.Append(" { CustomAttributes = new List");
+ mainMember.Append($" {{ {string.Join(", ", attributeInitializers)} }} }}");
+ }
+
+ initializerLines.Add(mainMember.ToString());
+
+ // 处理元组元素,(暂不需要处理元组元素的特性)
+ if (prop.Type is INamedTypeSymbol { IsTupleType: true } tupleType)
+ {
+ foreach (var element in tupleType.TupleElements)
+ {
+ var elementType = element.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);//元组元素的类型
+ var elementName = element.Name;//元组元素名称
+ var elementDeclaredName = element.Type.Name;//元组元素类型名称
+
+ initializerLines.Add(
+ $"new EntityMemberInfo(" +
+ $"\"{prop.Name}.{elementName}\", " +
+ $"typeof({elementType}), " +
+ $"GetValueTupleElementDeclaredTypeName(typeof({elementType})), " +//$"\"{elementDeclaredName}\", " +
+ $"(e) => Get{prop.Name}_{elementName}(({entityType})e), " +
+ $"(e, v) => Set{prop.Name}_{elementName}(({entityType})e, ({elementType})v))");
+ }
+ }
+ }
+
+ code.AppendLine(string.Join(",\n", initializerLines));
+ code.AppendLine(" };");
+
+ code.AppendLine(GetValueTupleElementName());
+ }
+
+ private static string GetValueTupleElementName()
+ {
+ return """
+ public static string GetValueTupleElementDeclaredTypeName(Type declaredType)
+ {
+ string typeName;
+ // 处理可空类型
+ if (declaredType.IsGenericType && declaredType.GetGenericTypeDefinition() == typeof(Nullable<>))
+ {
+ Type underlyingType = Nullable.GetUnderlyingType(declaredType);
+ typeName = underlyingType.Name;
+ }
+ else
+ {
+ typeName = declaredType.Name;
+ }
+
+ return typeName;
+ }
+ """;
+ }
+
+
+ private static string GenerateAttributeInitializer(AttributeData attribute)
+ {
+ if (attribute.AttributeClass == null)
+ return string.Empty;
+
+ var attributeClass = attribute.AttributeClass.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
+ var args = attribute.ConstructorArguments;
+ var namedArgs = attribute.NamedArguments;
+
+ var parameters = new List();
+ foreach (var arg in args)
+ {
+ parameters.Add(ConvertTypedConstantToCode(arg));
+ }
+
+ var constructorArgs = string.Join(", ", parameters);
+
+ var initializer = new StringBuilder();
+ initializer.Append($"new {attributeClass}({constructorArgs})");
+
+ if (namedArgs.Any())
+ {
+ initializer.Append(" { ");
+ var namedArgsList = namedArgs.Select(n => $"{n.Key} = {ConvertTypedConstantToCode(n.Value)}");
+ initializer.Append(string.Join(", ", namedArgsList));
+ initializer.Append(" }");
+ }
+
+ return initializer.ToString();
+ }
+
+ private static string ConvertTypedConstantToCode(TypedConstant constant)
+ {
+ if (constant.IsNull)
+ return "null";
+
+ switch (constant.Kind)
+ {
+ case TypedConstantKind.Array:
+ var elements = constant.Values.Select(ConvertTypedConstantToCode);
+ return $"new[] {{ {string.Join(", ", elements)} }}";
+ case TypedConstantKind.Type:
+ var typeSymbol = (ITypeSymbol)constant.Value!;
+ return $"typeof({typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)})";
+ case TypedConstantKind.Enum:
+ return ConvertEnumTypedConstant(constant);
+ default:
+ return ConvertPrimitiveConstant(constant);
+ }
+ }
+
+ private static string ConvertEnumTypedConstant(TypedConstant constant)
+ {
+ var enumType = constant.Type!;
+ var enumValue = constant.Value!;
+ var enumTypeName = enumType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
+
+ foreach (var member in enumType.GetMembers().OfType())
+ {
+ if (member.ConstantValue != null && member.ConstantValue.Equals(enumValue))
+ return $"{enumTypeName}.{member.Name}";
+ }
+
+ return $"({enumTypeName})({enumValue})";
+ }
+
+ private static string ConvertPrimitiveConstant(TypedConstant constant)
+ {
+ var value = constant.Value!;
+ return value switch
+ {
+ string s => $"\"{s}\"",
+ char c => $"'{c}'",
+ bool b => b ? "true" : "false",
+ _ => value.ToString()
+ };
+ }
+
+ private static bool IsCompilerGeneratedAttribute(AttributeData attribute)
+ {
+ return attribute.AttributeClass?.ToDisplayString() == "System.Runtime.CompilerServices.CompilerGeneratedAttribute";
+ }
+
+ ///
+ /// 获取枚举的参数
+ ///
+ ///
+ ///
+ ///
+ private static string GetEnumMemberName(INamedTypeSymbol enumType, int value)
+ {
+ foreach (var member in enumType.GetMembers().OfType())
+ {
+ if (member.ConstantValue is int intValue && intValue == value)
+ {
+ return $"{enumType.ToDisplayString()}.{member.Name}";
+ }
+ }
+ return $"{enumType.ToDisplayString()}.Other";
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/JiShe.CollectBus.Analyzers/JiShe.CollectBus.Analyzers.csproj b/modules/JiShe.CollectBus.Analyzers/JiShe.CollectBus.Analyzers.csproj
new file mode 100644
index 0000000..66b1fcf
--- /dev/null
+++ b/modules/JiShe.CollectBus.Analyzers/JiShe.CollectBus.Analyzers.csproj
@@ -0,0 +1,21 @@
+
+
+
+ netstandard2.0
+ true
+ true
+ false
+ bin
+ false
+ latest
+ true
+ true
+
+
+
+
+
+
+
+
+
diff --git a/modules/JiShe.CollectBus.FreeRedis/FreeRedisProvider.cs b/modules/JiShe.CollectBus.FreeRedis/FreeRedisProvider.cs
index 4c81d02..b2cf9e5 100644
--- a/modules/JiShe.CollectBus.FreeRedis/FreeRedisProvider.cs
+++ b/modules/JiShe.CollectBus.FreeRedis/FreeRedisProvider.cs
@@ -37,466 +37,6 @@ namespace JiShe.CollectBus.FreeRedis
Instance.Deserialize = (json, type) => BusJsonSerializer.Deserialize(json, type);
Instance.Notice += (s, e) => Trace.WriteLine(e.Log);
return Instance;
- }
-
- /////
- ///// 单个添加数据
- /////
- /////
- ///// 主数据存储Hash缓存Key
- ///// 集中器索引Set缓存Key
- ///// 集中器排序索引ZSET缓存Key
- ///// 集中器采集频率分组全局索引ZSet缓存Key
- ///// 表计信息
- ///// 可选时间戳
- /////
- //public async Task AddMeterCacheData(
- //string redisCacheKey,
- //string redisCacheFocusIndexKey,
- //string redisCacheScoresIndexKey,
- //string redisCacheGlobalIndexKey,
- //T data,
- //DateTimeOffset? timestamp = null) where T : DeviceCacheBasicModel
- //{
- // // 参数校验增强
- // if (data == null || string.IsNullOrWhiteSpace(redisCacheKey)
- // || string.IsNullOrWhiteSpace(redisCacheFocusIndexKey)
- // || string.IsNullOrWhiteSpace(redisCacheScoresIndexKey)
- // || string.IsNullOrWhiteSpace(redisCacheGlobalIndexKey))
- // {
- // throw new ArgumentException($"{nameof(AddMeterCacheData)} 参数异常,-101");
- // }
-
- // // 计算组合score(分类ID + 时间戳)
- // var actualTimestamp = timestamp ?? DateTimeOffset.UtcNow;
-
- // long scoreValue = ((long)data.FocusId << 32) | (uint)actualTimestamp.Ticks;
-
- // //全局索引写入
- // long globalScore = actualTimestamp.ToUnixTimeMilliseconds();
-
- // // 使用事务保证原子性
- // using (var trans = Instance.Multi())
- // {
- // // 主数据存储Hash
- // trans.HSet(redisCacheKey, data.MemberID, data.Serialize());
-
- // // 分类索引
- // trans.SAdd(redisCacheFocusIndexKey, data.MemberID);
-
- // // 排序索引使用ZSET
- // trans.ZAdd(redisCacheScoresIndexKey, scoreValue, data.MemberID);
-
- // //全局索引
- // trans.ZAdd(redisCacheGlobalIndexKey, globalScore, data.MemberID);
-
- // var results = trans.Exec();
-
- // if (results == null || results.Length <= 0)
- // throw new Exception($"{nameof(AddMeterCacheData)} 事务提交失败,-102");
- // }
-
- // await Task.CompletedTask;
- //}
-
- /////
- ///// 批量添加数据
- /////
- /////
- ///// 主数据存储Hash缓存Key
- ///// 集中器索引Set缓存Key
- ///// 集中器排序索引ZSET缓存Key
- ///// 集中器采集频率分组全局索引ZSet缓存Key
- ///// 数据集合
- ///// 可选时间戳
- /////
- //public async Task BatchAddMeterData(
- //string redisCacheKey,
- //string redisCacheFocusIndexKey,
- //string redisCacheScoresIndexKey,
- //string redisCacheGlobalIndexKey,
- //IEnumerable items,
- //DateTimeOffset? timestamp = null) where T : DeviceCacheBasicModel
- //{
- // if (items == null
- // || items.Count() <=0
- // || string.IsNullOrWhiteSpace(redisCacheKey)
- // || string.IsNullOrWhiteSpace(redisCacheFocusIndexKey)
- // || string.IsNullOrWhiteSpace(redisCacheScoresIndexKey)
- // || string.IsNullOrWhiteSpace(redisCacheGlobalIndexKey))
- // {
- // throw new ArgumentException($"{nameof(BatchAddMeterData)} 参数异常,-101");
- // }
-
- // const int BATCH_SIZE = 1000; // 每批1000条
- // var semaphore = new SemaphoreSlim(Environment.ProcessorCount * 2);
-
- // foreach (var batch in items.Batch(BATCH_SIZE))
- // {
- // await semaphore.WaitAsync();
-
- // _ = Task.Run(() =>
- // {
- // using (var pipe = Instance.StartPipe())
- // {
- // foreach (var item in batch)
- // {
- // // 计算组合score(分类ID + 时间戳)
- // var actualTimestamp = timestamp ?? DateTimeOffset.UtcNow;
-
- // long scoreValue = ((long)item.FocusId << 32) | (uint)actualTimestamp.Ticks;
-
- // //全局索引写入
- // long globalScore = actualTimestamp.ToUnixTimeMilliseconds();
-
- // // 主数据存储Hash
- // pipe.HSet(redisCacheKey, item.MemberID, item.Serialize());
-
- // // 分类索引Set
- // pipe.SAdd(redisCacheFocusIndexKey, item.MemberID);
-
- // // 排序索引使用ZSET
- // pipe.ZAdd(redisCacheScoresIndexKey, scoreValue, item.MemberID);
-
- // //全局索引
- // pipe.ZAdd(redisCacheGlobalIndexKey, globalScore, item.MemberID);
- // }
- // pipe.EndPipe();
- // }
- // semaphore.Release();
- // });
- // }
-
- // await Task.CompletedTask;
- //}
-
- /////
- ///// 删除指定redis缓存key的缓存数据
- /////
- /////
- ///// 主数据存储Hash缓存Key
- ///// 集中器索引Set缓存Key
- ///// 集中器排序索引ZSET缓存Key
- ///// 集中器采集频率分组全局索引ZSet缓存Key
- ///// 表计信息
- /////
- //public async Task RemoveMeterData(
- //string redisCacheKey,
- //string redisCacheFocusIndexKey,
- //string redisCacheScoresIndexKey,
- //string redisCacheGlobalIndexKey,
- //T data) where T : DeviceCacheBasicModel
- //{
-
- // if (data == null
- // || string.IsNullOrWhiteSpace(redisCacheKey)
- // || string.IsNullOrWhiteSpace(redisCacheFocusIndexKey)
- // || string.IsNullOrWhiteSpace(redisCacheScoresIndexKey)
- // || string.IsNullOrWhiteSpace(redisCacheGlobalIndexKey))
- // {
- // throw new ArgumentException($"{nameof(RemoveMeterData)} 参数异常,-101");
- // }
-
- // const string luaScript = @"
- // local mainKey = KEYS[1]
- // local focusIndexKey = KEYS[2]
- // local scoresIndexKey = KEYS[3]
- // local globalIndexKey = KEYS[4]
- // local member = ARGV[1]
-
- // local deleted = 0
- // if redis.call('HDEL', mainKey, member) > 0 then
- // deleted = 1
- // end
-
- // redis.call('SREM', focusIndexKey, member)
- // redis.call('ZREM', scoresIndexKey, member)
- // redis.call('ZREM', globalIndexKey, member)
- // return deleted
- // ";
-
- // var keys = new[]
- // {
- // redisCacheKey,
- // redisCacheFocusIndexKey,
- // redisCacheScoresIndexKey,
- // redisCacheGlobalIndexKey
- // };
-
- // var result = await Instance.EvalAsync(luaScript, keys, new[] { data.MemberID });
-
- // if ((int)result == 0)
- // throw new KeyNotFoundException("指定数据不存在");
- //}
-
- /////
- ///// 修改表计缓存信息
- /////
- /////
- ///// 主数据存储Hash缓存Key
- ///// 旧集中器索引Set缓存Key
- ///// 新集中器索引Set缓存Key
- ///// 集中器排序索引ZSET缓存Key
- ///// 集中器采集频率分组全局索引ZSet缓存Key
- ///// 表计信息
- ///// 可选时间戳
- /////
- //public async Task UpdateMeterData(
- //string redisCacheKey,
- //string oldRedisCacheFocusIndexKey,
- //string newRedisCacheFocusIndexKey,
- //string redisCacheScoresIndexKey,
- //string redisCacheGlobalIndexKey,
- //T newData,
- //DateTimeOffset? newTimestamp = null) where T : DeviceCacheBasicModel
- //{
- // if (newData == null
- // || string.IsNullOrWhiteSpace(redisCacheKey)
- // || string.IsNullOrWhiteSpace(oldRedisCacheFocusIndexKey)
- // || string.IsNullOrWhiteSpace(newRedisCacheFocusIndexKey)
- // || string.IsNullOrWhiteSpace(redisCacheScoresIndexKey)
- // || string.IsNullOrWhiteSpace(redisCacheGlobalIndexKey))
- // {
- // throw new ArgumentException($"{nameof(UpdateMeterData)} 参数异常,-101");
- // }
-
- // var luaScript = @"
- // local mainKey = KEYS[1]
- // local oldFocusIndexKey = KEYS[2]
- // local newFocusIndexKey = KEYS[3]
- // local scoresIndexKey = KEYS[4]
- // local globalIndexKey = KEYS[5]
- // local member = ARGV[1]
- // local newData = ARGV[2]
- // local newScore = ARGV[3]
- // local newGlobalScore = ARGV[4]
-
- // -- 校验存在性
- // if redis.call('HEXISTS', mainKey, member) == 0 then
- // return 0
- // end
-
- // -- 更新主数据
- // redis.call('HSET', mainKey, member, newData)
-
- // -- 处理变更
- // if newScore ~= '' then
- // -- 删除旧索引
- // redis.call('SREM', oldFocusIndexKey, member)
- // redis.call('ZREM', scoresIndexKey, member)
-
- // -- 添加新索引
- // redis.call('SADD', newFocusIndexKey, member)
- // redis.call('ZADD', scoresIndexKey, newScore, member)
- // end
-
- // -- 更新全局索引
- // if newGlobalScore ~= '' then
- // -- 删除旧索引
- // redis.call('ZREM', globalIndexKey, member)
-
- // -- 添加新索引
- // redis.call('ZADD', globalIndexKey, newGlobalScore, member)
- // end
-
- // return 1
- // ";
-
- // var actualTimestamp = newTimestamp ?? DateTimeOffset.UtcNow;
- // var newGlobalScore = actualTimestamp.ToUnixTimeMilliseconds();
- // var newScoreValue = ((long)newData.FocusId << 32) | (uint)actualTimestamp.Ticks;
-
- // var result = await Instance.EvalAsync(luaScript,
- // new[]
- // {
- // redisCacheKey,
- // oldRedisCacheFocusIndexKey,
- // newRedisCacheFocusIndexKey,
- // redisCacheScoresIndexKey,
- // redisCacheGlobalIndexKey
- // },
- // new object[]
- // {
- // newData.MemberID,
- // newData.Serialize(),
- // newScoreValue.ToString() ?? "",
- // newGlobalScore.ToString() ?? ""
- // });
-
- // if ((int)result == 0)
- // {
- // throw new KeyNotFoundException($"{nameof(UpdateMeterData)}指定Key{redisCacheKey}的数据不存在");
- // }
- //}
-
- //public async Task> SingleGetMeterPagedData(
- //string redisCacheKey,
- //string redisCacheScoresIndexKey,
- //int focusId,
- //int pageSize = 10,
- //int pageIndex = 1,
- //bool descending = true)
- //{
- // // 计算score范围
- // long minScore = (long)focusId << 32;
- // long maxScore = ((long)focusId + 1) << 32;
-
- // // 分页参数计算
- // int start = (pageIndex - 1) * pageSize;
-
- // // 获取排序后的member列表
- // var members = descending
- // ? await Instance.ZRevRangeByScoreAsync(
- // redisCacheScoresIndexKey,
- // maxScore,
- // minScore,
- // start,
- // pageSize)
- // : await Instance.ZRangeByScoreAsync(
- // redisCacheScoresIndexKey,
- // minScore,
- // maxScore,
- // start,
- // pageSize);
-
- // // 批量获取实际数据
- // var dataTasks = members.Select(m =>
- // Instance.HGetAsync(redisCacheKey, m)).ToArray();
- // await Task.WhenAll(dataTasks);
-
- // // 总数统计优化
- // var total = await Instance.ZCountAsync(
- // redisCacheScoresIndexKey,
- // minScore,
- // maxScore);
-
- // return new BusPagedResult
- // {
- // Items = dataTasks.Select(t => t.Result).ToList(),
- // TotalCount = total,
- // PageIndex = pageIndex,
- // PageSize = pageSize
- // };
- //}
-
-
- //public async Task> GetFocusPagedData(
- //string redisCacheKey,
- //string redisCacheScoresIndexKey,
- //int focusId,
- //int pageSize = 10,
- //long? lastScore = null,
- //string lastMember = null,
- //bool descending = true) where T : DeviceCacheBasicModel
- //{
- // // 计算分数范围
- // long minScore = (long)focusId << 32;
- // long maxScore = ((long)focusId + 1) << 32;
-
- // // 获取成员列表
- // var members = await GetSortedMembers(
- // redisCacheScoresIndexKey,
- // minScore,
- // maxScore,
- // pageSize,
- // lastScore,
- // lastMember,
- // descending);
-
- // // 批量获取数据
- // var dataDict = await Instance.HMGetAsync(redisCacheKey, members.CurrentItems);
-
- // return new BusPagedResult
- // {
- // Items = dataDict,
- // TotalCount = await GetTotalCount(redisCacheScoresIndexKey, minScore, maxScore),
- // HasNext = members.HasNext,
- // NextScore = members.NextScore,
- // NextMember = members.NextMember
- // };
- //}
-
- //private async Task<(string[] CurrentItems, bool HasNext, decimal? NextScore, string NextMember)>
- // GetSortedMembers(
- // string zsetKey,
- // long minScore,
- // long maxScore,
- // int pageSize,
- // long? lastScore,
- // string lastMember,
- // bool descending)
- //{
- // var querySize = pageSize + 1;
- // var (startScore, exclude) = descending
- // ? (lastScore ?? maxScore, lastMember)
- // : (lastScore ?? minScore, lastMember);
-
- // var members = descending
- // ? await Instance.ZRevRangeByScoreAsync(
- // zsetKey,
- // max: startScore,
- // min: minScore,
- // offset: 0,
- // count: querySize)
- // : await Instance.ZRangeByScoreAsync(
- // zsetKey,
- // min: startScore,
- // max: maxScore,
- // offset: 0,
- // count: querySize);
-
- // var hasNext = members.Length > pageSize;
- // var currentItems = members.Take(pageSize).ToArray();
-
- // var nextCursor = currentItems.Any()
- // ? await GetNextCursor(zsetKey, currentItems.Last(), descending)
- // : (null, null);
-
- // return (currentItems, hasNext, nextCursor.score, nextCursor.member);
- //}
-
- //private async Task GetTotalCount(string zsetKey, long min, long max)
- //{
- // // 缓存计数优化
- // var cacheKey = $"{zsetKey}_count_{min}_{max}";
- // var cached = await Instance.GetAsync(cacheKey);
-
- // if (cached.HasValue)
- // return cached.Value;
-
- // var count = await Instance.ZCountAsync(zsetKey, min, max);
- // await Instance.SetExAsync(cacheKey, 60, count); // 缓存60秒
- // return count;
- //}
-
-
- //public async Task>> BatchGetMeterPagedData(
- //string redisCacheKey,
- //string redisCacheScoresIndexKey,
- //IEnumerable focusIds,
- //int pageSizePerFocus = 10) where T : DeviceCacheBasicModel
- //{
- // var results = new ConcurrentDictionary>();
- // var parallelOptions = new ParallelOptions
- // {
- // MaxDegreeOfParallelism = Environment.ProcessorCount * 2
- // };
-
- // await Parallel.ForEachAsync(focusIds, parallelOptions, async (focusId, _) =>
- // {
- // var data = await SingleGetMeterPagedData(
- // redisCacheKey,
- // redisCacheScoresIndexKey,
- // focusId,
- // pageSizePerFocus);
-
- // results.TryAdd(focusId, data);
- // });
-
- // return new Dictionary>(results);
- //}
-
-
-
+ }
}
}
\ No newline at end of file
diff --git a/modules/JiShe.CollectBus.IoTDB/Attribute/EntityTypeAttribute.cs b/modules/JiShe.CollectBus.IoTDB/Attribute/EntityTypeAttribute.cs
deleted file mode 100644
index 3610c00..0000000
--- a/modules/JiShe.CollectBus.IoTDB/Attribute/EntityTypeAttribute.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using JiShe.CollectBus.IoTDB.Enums;
-
-namespace JiShe.CollectBus.IoTDB.Attribute
-{
- ///
- /// IoTDB实体类型特性
- ///
- [AttributeUsage(AttributeTargets.Class)]
- public class EntityTypeAttribute : System.Attribute
- {
- public EntityTypeEnum EntityType { get; }
-
-
- public EntityTypeAttribute(EntityTypeEnum entityType)
- {
- EntityType = entityType;
- }
- }
-}
diff --git a/modules/JiShe.CollectBus.IoTDB/Attribute/ATTRIBUTEColumnAttribute.cs b/modules/JiShe.CollectBus.IoTDB/Attributes/ATTRIBUTEColumnAttribute.cs
similarity index 83%
rename from modules/JiShe.CollectBus.IoTDB/Attribute/ATTRIBUTEColumnAttribute.cs
rename to modules/JiShe.CollectBus.IoTDB/Attributes/ATTRIBUTEColumnAttribute.cs
index d188c36..7ef13f3 100644
--- a/modules/JiShe.CollectBus.IoTDB/Attribute/ATTRIBUTEColumnAttribute.cs
+++ b/modules/JiShe.CollectBus.IoTDB/Attributes/ATTRIBUTEColumnAttribute.cs
@@ -1,4 +1,4 @@
-namespace JiShe.CollectBus.IoTDB.Attribute
+namespace JiShe.CollectBus.IoTDB.Attributes
{
///
/// Column分类标记特性(ATTRIBUTE字段),也就是属性字段
diff --git a/modules/JiShe.CollectBus.IoTDB/Attribute/FIELDColumnAttribute.cs b/modules/JiShe.CollectBus.IoTDB/Attributes/FIELDColumnAttribute.cs
similarity index 82%
rename from modules/JiShe.CollectBus.IoTDB/Attribute/FIELDColumnAttribute.cs
rename to modules/JiShe.CollectBus.IoTDB/Attributes/FIELDColumnAttribute.cs
index 7cabdf4..43d699f 100644
--- a/modules/JiShe.CollectBus.IoTDB/Attribute/FIELDColumnAttribute.cs
+++ b/modules/JiShe.CollectBus.IoTDB/Attributes/FIELDColumnAttribute.cs
@@ -1,4 +1,4 @@
-namespace JiShe.CollectBus.IoTDB.Attribute
+namespace JiShe.CollectBus.IoTDB.Attributes
{
///
/// Column分类标记特性(FIELD字段),数据列字段
diff --git a/modules/JiShe.CollectBus.IoTDB/Attribute/SingleMeasuringAttribute.cs b/modules/JiShe.CollectBus.IoTDB/Attributes/SingleMeasuringAttribute.cs
similarity index 91%
rename from modules/JiShe.CollectBus.IoTDB/Attribute/SingleMeasuringAttribute.cs
rename to modules/JiShe.CollectBus.IoTDB/Attributes/SingleMeasuringAttribute.cs
index 5f0ca07..481bfa2 100644
--- a/modules/JiShe.CollectBus.IoTDB/Attribute/SingleMeasuringAttribute.cs
+++ b/modules/JiShe.CollectBus.IoTDB/Attributes/SingleMeasuringAttribute.cs
@@ -1,4 +1,4 @@
-namespace JiShe.CollectBus.IoTDB.Attribute
+namespace JiShe.CollectBus.IoTDB.Attributes
{
///
/// 用于标识当前实体为单侧点模式,单侧点模式只有一个Filed标识字段,类型是Tuple,Item1=>测点名称,Item2=>测点值,泛型
diff --git a/modules/JiShe.CollectBus.IoTDB/Attribute/TAGColumnAttribute.cs b/modules/JiShe.CollectBus.IoTDB/Attributes/TAGColumnAttribute.cs
similarity index 82%
rename from modules/JiShe.CollectBus.IoTDB/Attribute/TAGColumnAttribute.cs
rename to modules/JiShe.CollectBus.IoTDB/Attributes/TAGColumnAttribute.cs
index 6f40a47..48a3830 100644
--- a/modules/JiShe.CollectBus.IoTDB/Attribute/TAGColumnAttribute.cs
+++ b/modules/JiShe.CollectBus.IoTDB/Attributes/TAGColumnAttribute.cs
@@ -1,4 +1,4 @@
-namespace JiShe.CollectBus.IoTDB.Attribute
+namespace JiShe.CollectBus.IoTDB.Attributes
{
///
/// Column分类标记特性(TAG字段),标签字段
diff --git a/modules/JiShe.CollectBus.IoTDB/Attribute/TableNameOrTreePathAttribute.cs b/modules/JiShe.CollectBus.IoTDB/Attributes/TableNameOrTreePathAttribute.cs
similarity index 85%
rename from modules/JiShe.CollectBus.IoTDB/Attribute/TableNameOrTreePathAttribute.cs
rename to modules/JiShe.CollectBus.IoTDB/Attributes/TableNameOrTreePathAttribute.cs
index 1b4f4f0..5f986b5 100644
--- a/modules/JiShe.CollectBus.IoTDB/Attribute/TableNameOrTreePathAttribute.cs
+++ b/modules/JiShe.CollectBus.IoTDB/Attributes/TableNameOrTreePathAttribute.cs
@@ -1,6 +1,5 @@
-using JiShe.CollectBus.IoTDB.Enums;
-
-namespace JiShe.CollectBus.IoTDB.Attribute
+
+namespace JiShe.CollectBus.IoTDB.Attributes
{
///
/// IoTDB实体存储路径或表名称,一般用于已经明确的存储路径或表名称,例如日志存储
diff --git a/modules/JiShe.CollectBus.IoTDB/Context/IoTDBRuntimeContext.cs b/modules/JiShe.CollectBus.IoTDB/Context/IoTDBRuntimeContext.cs
index ef68325..7384716 100644
--- a/modules/JiShe.CollectBus.IoTDB/Context/IoTDBRuntimeContext.cs
+++ b/modules/JiShe.CollectBus.IoTDB/Context/IoTDBRuntimeContext.cs
@@ -7,7 +7,7 @@ namespace JiShe.CollectBus.IoTDB.Context
///
/// IoTDB SessionPool 运行时上下文
///
- public class IoTDBRuntimeContext: IScopedDependency
+ public class IoTDBRuntimeContext: IScopedDependency//ITransientDependency
{
private readonly bool _defaultValue;
diff --git a/modules/JiShe.CollectBus.IoTDB/Exceptions/IoTException.cs b/modules/JiShe.CollectBus.IoTDB/Exceptions/IoTException.cs
new file mode 100644
index 0000000..93bed4f
--- /dev/null
+++ b/modules/JiShe.CollectBus.IoTDB/Exceptions/IoTException.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace JiShe.CollectBus.IoTDB.Exceptions
+{
+ ///
+ /// IoTDB异常
+ ///
+ public class IoTException : Exception
+ {
+ public int ErrorCode { get; }
+
+ public IoTException(string message, int errorCode)
+ : base($"{message} (Code: {errorCode})")
+ {
+ ErrorCode = errorCode;
+ }
+ }
+}
diff --git a/modules/JiShe.CollectBus.IoTDB/Interface/IIoTDBProvider.cs b/modules/JiShe.CollectBus.IoTDB/Interface/IIoTDBProvider.cs
index 82a0d47..a5d885d 100644
--- a/modules/JiShe.CollectBus.IoTDB/Interface/IIoTDBProvider.cs
+++ b/modules/JiShe.CollectBus.IoTDB/Interface/IIoTDBProvider.cs
@@ -16,6 +16,8 @@ namespace JiShe.CollectBus.IoTDB.Interface
///// 是否使用表模型
//void SwitchSessionPool(bool useTableSession);
+ IIoTDbProvider GetSessionPool(bool sessionpolType);
+
///
/// 插入数据
///
diff --git a/modules/JiShe.CollectBus.IoTDB/JiShe.CollectBus.IoTDB.csproj b/modules/JiShe.CollectBus.IoTDB/JiShe.CollectBus.IoTDB.csproj
index 8cc4b33..78b81e3 100644
--- a/modules/JiShe.CollectBus.IoTDB/JiShe.CollectBus.IoTDB.csproj
+++ b/modules/JiShe.CollectBus.IoTDB/JiShe.CollectBus.IoTDB.csproj
@@ -1,15 +1,19 @@
-
- net8.0
- enable
- enable
-
+
+ net8.0
+ enable
+ enable
+
-
+
+
+
+
+
diff --git a/modules/JiShe.CollectBus.IoTDB/Model/IoTEntity.cs b/modules/JiShe.CollectBus.IoTDB/Model/IoTEntity.cs
index a079bd4..da14d20 100644
--- a/modules/JiShe.CollectBus.IoTDB/Model/IoTEntity.cs
+++ b/modules/JiShe.CollectBus.IoTDB/Model/IoTEntity.cs
@@ -1,9 +1,12 @@
-using JiShe.CollectBus.IoTDB.Attribute;
+using JiShe.CollectBus.Common.Attributes;
+using JiShe.CollectBus.Common.Consts;
+using JiShe.CollectBus.IoTDB.Attributes;
+using Volo.Abp.Domain.Entities;
namespace JiShe.CollectBus.IoTDB.Model
{
///
- /// IoT实体基类,此类适用于多个数据测点记录场景,单个测点请使用子类 SingleMeasuring
+ /// IoT实体基类,此类适用于多个数据测点记录场景,单个测点请使用子类 SingleMeasuring,新增字段只能现有字段末尾添加,否则会导致数据写入失败。
///
public abstract class IoTEntity
{
@@ -19,6 +22,12 @@ namespace JiShe.CollectBus.IoTDB.Model
[TAGColumn]
public string ProjectId { get; set; }
+ ///
+ /// 数据类型
+ ///
+ [TAGColumn]
+ public string DataType { get; set; } = IOTDBDataTypeConst.Data;
+
///
/// 设备类型集中器、电表、水表、流量计、传感器等
///
@@ -35,5 +44,29 @@ namespace JiShe.CollectBus.IoTDB.Model
/// 时标,也就是业务时间戳,单位毫秒,必须通过DateTimeOffset获取
///
public long Timestamps { get; set; } = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+
+ ///
+ /// 设备路径
+ ///
+ private string _devicePath;
+ ///
+ /// 设备路径
+ ///
+ public virtual string DevicePath
+ {
+ get
+ {
+ // 如果未手动设置路径,则自动生成
+ if (string.IsNullOrWhiteSpace(_devicePath))
+ {
+ return $"root.{SystemName.ToLower()}.`{ProjectId}`.`{DeviceType}`.{DataType}.`{DeviceId}`";
+ }
+ return _devicePath;
+ }
+ set
+ {
+ _devicePath = value; // 直接赋值给支持字段,避免递归
+ }
+ }
}
}
diff --git a/modules/JiShe.CollectBus.IoTDB/Model/TableModelSingleMeasuringEntity.cs b/modules/JiShe.CollectBus.IoTDB/Model/TableModelSingleMeasuringEntity.cs
index 4308dae..376b677 100644
--- a/modules/JiShe.CollectBus.IoTDB/Model/TableModelSingleMeasuringEntity.cs
+++ b/modules/JiShe.CollectBus.IoTDB/Model/TableModelSingleMeasuringEntity.cs
@@ -1,24 +1,18 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using JiShe.CollectBus.IoTDB.Attribute;
-using JiShe.CollectBus.IoTDB.Enums;
-using JiShe.CollectBus.IoTDB.Provider;
+using JiShe.CollectBus.Analyzers.Shared;
+using JiShe.CollectBus.IoTDB.Attributes;
namespace JiShe.CollectBus.IoTDB.Model
{
///
/// Table模型单项数据实体
- ///
- [EntityType(EntityTypeEnum.TableModel)]
+ ///
+ [SourceAnalyzers(EntityTypeEnum.TableModel)]
public class TableModelSingleMeasuringEntity : IoTEntity
{
///
/// 单项数据键值对
///
[SingleMeasuring(nameof(SingleColumn))]
- public required Tuple SingleColumn { get; set; }
+ public required ValueTuple SingleColumn { get; set; }
}
}
diff --git a/modules/JiShe.CollectBus.IoTDB/Model/TreeModelSingleMeasuringEntity.cs b/modules/JiShe.CollectBus.IoTDB/Model/TreeModelSingleMeasuringEntity.cs
index 9b3609c..56a6c54 100644
--- a/modules/JiShe.CollectBus.IoTDB/Model/TreeModelSingleMeasuringEntity.cs
+++ b/modules/JiShe.CollectBus.IoTDB/Model/TreeModelSingleMeasuringEntity.cs
@@ -1,24 +1,18 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using JiShe.CollectBus.IoTDB.Attribute;
-using JiShe.CollectBus.IoTDB.Enums;
-using JiShe.CollectBus.IoTDB.Provider;
+using JiShe.CollectBus.Analyzers.Shared;
+using JiShe.CollectBus.IoTDB.Attributes;
namespace JiShe.CollectBus.IoTDB.Model
{
///
/// Tree模型单项数据实体
///
- [EntityType(EntityTypeEnum.TreeModel)]
+ [SourceAnalyzers(EntityTypeEnum.TreeModel)]
public class TreeModelSingleMeasuringEntity : IoTEntity
{
///
/// 单项数据键值对
///
[SingleMeasuring(nameof(SingleMeasuring))]
- public required Tuple SingleMeasuring { get; set; }
+ public required ValueTuple SingleMeasuring { get; set; }
}
}
diff --git a/modules/JiShe.CollectBus.IoTDB/Options/IoTDBOptions.cs b/modules/JiShe.CollectBus.IoTDB/Options/IoTDBOptions.cs
index 251e48b..68b2770 100644
--- a/modules/JiShe.CollectBus.IoTDB/Options/IoTDBOptions.cs
+++ b/modules/JiShe.CollectBus.IoTDB/Options/IoTDBOptions.cs
@@ -26,7 +26,7 @@
///
/// 连接池大小
///
- public int PoolSize { get; set; } = 2;
+ public int PoolSize { get; set; } = 8;
///
/// 查询时,每次查询的数据量,默认1024
diff --git a/modules/JiShe.CollectBus.IoTDB/Options/QueryCondition.cs b/modules/JiShe.CollectBus.IoTDB/Options/QueryCondition.cs
index 40dd443..bccf017 100644
--- a/modules/JiShe.CollectBus.IoTDB/Options/QueryCondition.cs
+++ b/modules/JiShe.CollectBus.IoTDB/Options/QueryCondition.cs
@@ -1,4 +1,7 @@
-namespace JiShe.CollectBus.IoTDB.Options
+using JiShe.CollectBus.Common.Extensions;
+using JiShe.CollectBus.Common.Helpers;
+
+namespace JiShe.CollectBus.IoTDB.Options
{
///
/// 查询条件
@@ -19,10 +22,43 @@
/// 是否数值,如果是数值,则进行数值比较,否则进行字符串比较
///
public bool IsNumber { get; set; } = false;
-
+
+ private object _rawValue;
///
/// 值
///
- public object Value { get; set; }
+ public object Value
+ {
+ get => ApplyValueConversion(_rawValue);
+ set => _rawValue = value;
+ }
+
+ ///
+ /// 值转换
+ ///
+ ///
+ ///
+ private object ApplyValueConversion(object rawValue)
+ {
+ string declaredTypeName = rawValue.GetType().Name;
+
+ Func
public TSDataType DataType { get; }
- public ColumnInfo(string name, ColumnCategory category, TSDataType dataType, bool isSingleMeasuring)
+ public ColumnInfo(string name, ColumnCategory category, TSDataType dataType, bool isSingleMeasuring, string declaredTypeName)
{
Name = name;
Category = category;
DataType = dataType;
IsSingleMeasuring = isSingleMeasuring;
+ DeclaredTypeName = declaredTypeName;
}
}
@@ -759,7 +913,7 @@ namespace JiShe.CollectBus.IoTDB.Provider
["DATETIME"] = TSDataType.TIMESTAMP,
["DATE"] = TSDataType.DATE,
["BLOB"] = TSDataType.BLOB,
- ["DECIMAL"] = TSDataType.STRING,
+ ["DECIMAL"] = TSDataType.DOUBLE,
["STRING"] = TSDataType.STRING
};
@@ -795,7 +949,7 @@ namespace JiShe.CollectBus.IoTDB.Provider
TSDataType.BOOLEAN => Convert.ToBoolean(value),
TSDataType.INT32 => Convert.ToInt32(value),
TSDataType.INT64 => Convert.ToInt64(value),
- TSDataType.FLOAT => Convert.ToDouble(value),
+ TSDataType.FLOAT => Convert.ToSingle(value),
TSDataType.DOUBLE => Convert.ToDouble(value),
TSDataType.TEXT => Convert.ToString(value),
TSDataType.NONE => null,
@@ -805,5 +959,23 @@ namespace JiShe.CollectBus.IoTDB.Provider
TSDataType.STRING => Convert.ToString(value),
_ => Convert.ToString(value)
};
+
+ ///
+ /// 缓存实体属性信息
+ ///
+ ///
+ ///
+ ///
+ private Dictionary BuildMemberCache(ISourceEntityAccessor accessor)
+ {
+ var cache = new Dictionary(StringComparer.Ordinal);
+ foreach (var member in accessor.MemberList)
+ {
+ cache[member.NameOrPath] = member;
+ }
+ return cache;
+ }
+
+ private static readonly Regex _asciiAlphanumericRegex = new Regex(@"^[a-zA-Z0-9]*$", RegexOptions.Compiled);
}
}
diff --git a/modules/JiShe.CollectBus.IoTDB/Provider/SessionPoolAdapter.cs b/modules/JiShe.CollectBus.IoTDB/Provider/SessionPoolAdapter.cs
index ee71cf1..26ea4c7 100644
--- a/modules/JiShe.CollectBus.IoTDB/Provider/SessionPoolAdapter.cs
+++ b/modules/JiShe.CollectBus.IoTDB/Provider/SessionPoolAdapter.cs
@@ -70,7 +70,7 @@ namespace JiShe.CollectBus.IoTDB.Provider
var result = await _sessionPool.InsertAlignedTabletAsync(tablet);
if (result != 0)
{
- throw new Exception($"{nameof(SessionPoolAdapter)} Tree模型数据入库没有成功,返回结果为:{result}");
+ throw new Exception($"{nameof(SessionPoolAdapter)} Tree模型数据入库没有成功,返回结果为:{result},请检查IoTEntity继承子类属性索引是否有变动。");
}
//await CloseAsync();
return result;
diff --git a/modules/JiShe.CollectBus.IoTDB/Provider/TableSessionPoolAdapter.cs b/modules/JiShe.CollectBus.IoTDB/Provider/TableSessionPoolAdapter.cs
index dc4f0ee..8edb112 100644
--- a/modules/JiShe.CollectBus.IoTDB/Provider/TableSessionPoolAdapter.cs
+++ b/modules/JiShe.CollectBus.IoTDB/Provider/TableSessionPoolAdapter.cs
@@ -68,7 +68,7 @@ namespace JiShe.CollectBus.IoTDB.Provider
var result = await _sessionPool.InsertAsync(tablet);
if (result != 0)
{
- throw new Exception($"{nameof(TableSessionPoolAdapter)} table模型数据入库没有成功,返回结果为:{result}");
+ throw new Exception($"{nameof(TableSessionPoolAdapter)} table模型数据入库没有成功,返回结果为:{result},请检查IoTEntity继承子类属性索引是否有变动。");
}
//await CloseAsync();
diff --git a/modules/JiShe.CollectBus.Kafka.Test/JiShe.CollectBus.Kafka.Test.csproj b/modules/JiShe.CollectBus.Kafka.Test/JiShe.CollectBus.Kafka.Test.csproj
index db97b00..c312740 100644
--- a/modules/JiShe.CollectBus.Kafka.Test/JiShe.CollectBus.Kafka.Test.csproj
+++ b/modules/JiShe.CollectBus.Kafka.Test/JiShe.CollectBus.Kafka.Test.csproj
@@ -31,10 +31,14 @@
-
+
+
diff --git a/modules/JiShe.CollectBus.Kafka.Test/KafkaProduceBenchmark.cs b/modules/JiShe.CollectBus.Kafka.Test/KafkaProduceBenchmark.cs
index a8b8d93..4152a33 100644
--- a/modules/JiShe.CollectBus.Kafka.Test/KafkaProduceBenchmark.cs
+++ b/modules/JiShe.CollectBus.Kafka.Test/KafkaProduceBenchmark.cs
@@ -1,8 +1,10 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using Confluent.Kafka;
+using JiShe.CollectBus.Common;
using JiShe.CollectBus.Kafka.AdminClient;
using JiShe.CollectBus.Kafka.Consumer;
+using JiShe.CollectBus.Kafka.Internal;
using JiShe.CollectBus.Kafka.Producer;
using Microsoft.Extensions.Configuration;
@@ -24,11 +26,11 @@ namespace JiShe.CollectBus.Kafka.Test
{
// 每批消息数量
- [Params(1000, 10000, 100000)]
+ [Params(1000, 10000, 100000, 1000000)]
public int N;
public ServiceProvider _serviceProvider;
- public IConsumerService _consumerService;
- public IProducerService _producerService;
+ public IConsumerService _consumerService;
+ public IProducerService _producerService;
public string topic = "test-topic1";
[GlobalSetup]
@@ -40,13 +42,22 @@ namespace JiShe.CollectBus.Kafka.Test
.AddJsonFile("appsettings.json")
.Build();
// 直接读取配置项
- var greeting = config["ServerTagName"];
+ var greeting = config["Kafka:ServerTagName"];
Console.WriteLine(greeting); // 输出: Hello, World!
// 创建服务容器
var services = new ServiceCollection();
// 注册 IConfiguration 实例
services.AddSingleton(config);
+ services.Configure(options =>
+ {
+ config.GetSection("Kafka").Bind(options);
+ });
+ services.Configure(options =>
+ {
+ config.GetSection(nameof(ServerApplicationOptions)).Bind(options);
+ });
+
// 初始化日志
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(config) // 从 appsettings.json 读取配置
@@ -61,6 +72,8 @@ namespace JiShe.CollectBus.Kafka.Test
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
+ services.AddSingleton();
+ services.AddTransient();
// 构建ServiceProvider
_serviceProvider = services.BuildServiceProvider();
@@ -72,10 +85,10 @@ namespace JiShe.CollectBus.Kafka.Test
var adminClientService = _serviceProvider.GetRequiredService();
-
+
//await adminClientService.DeleteTopicAsync(topic);
// 创建 topic
- adminClientService.CreateTopicAsync(topic, 3, 3).ConfigureAwait(false).GetAwaiter();
+ //adminClientService.CreateTopicAsync(topic, 3, 3).ConfigureAwait(false).GetAwaiter();
_consumerService = _serviceProvider.GetRequiredService();
@@ -100,9 +113,9 @@ namespace JiShe.CollectBus.Kafka.Test
List tasks = new();
for (int i = 0; i < N; ++i)
{
- var task = _producerService.ProduceAsync(topic, i.ToString(),null);
+ var task = _producerService.ProduceAsync(topic, i.ToString(), null);
}
await Task.WhenAll(tasks);
}
- }
+ }
}
diff --git a/modules/JiShe.CollectBus.Kafka.Test/Lib/JiShe.CollectBus.Common.dll b/modules/JiShe.CollectBus.Kafka.Test/Lib/JiShe.CollectBus.Common.dll
new file mode 100644
index 0000000..5d9bd8d
Binary files /dev/null and b/modules/JiShe.CollectBus.Kafka.Test/Lib/JiShe.CollectBus.Common.dll differ
diff --git a/modules/JiShe.CollectBus.Kafka.Test/Program.cs b/modules/JiShe.CollectBus.Kafka.Test/Program.cs
index 3c99810..d932806 100644
--- a/modules/JiShe.CollectBus.Kafka.Test/Program.cs
+++ b/modules/JiShe.CollectBus.Kafka.Test/Program.cs
@@ -1,8 +1,5 @@
// See https://aka.ms/new-console-template for more information
-using BenchmarkDotNet.Configs;
-using BenchmarkDotNet.Running;
-using Confluent.Kafka;
-using DeviceDetectorNET.Parser.Device;
+using JiShe.CollectBus.Common;
using JiShe.CollectBus.Common.Consts;
using JiShe.CollectBus.Kafka;
using JiShe.CollectBus.Kafka.AdminClient;
@@ -17,10 +14,6 @@ using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Serilog;
-using System.Diagnostics;
-using System.Reflection;
-using System.Reflection.PortableExecutable;
-using System.Text.Json;
#region 基准测试
//var summary = BenchmarkRunner.Run();
@@ -38,7 +31,7 @@ var host = Host.CreateDefaultBuilder(args)
.AddJsonFile("appsettings.json")
.Build();
// 直接读取配置项
- var greeting = config["Kafka:ServerTagName"];
+ var greeting = config["ServerApplicationOptions:ServerTagName"];
Console.WriteLine(greeting); // 输出: Hello, World!
@@ -58,7 +51,18 @@ var host = Host.CreateDefaultBuilder(args)
logging.ClearProviders();
logging.AddSerilog();
});
- services.Configure(config.GetSection("Kafka"));
+ //services.Configure(config.GetSection("Kafka"));
+ //services.Configure(config.GetSection("ServerApplicationOptions"));
+ var dss = config.GetSection("Kafka");
+
+ services.Configure(options =>
+ {
+ config.GetSection("Kafka").Bind(options);
+ });
+ services.Configure(options =>
+ {
+ config.GetSection(nameof(ServerApplicationOptions)).Bind(options);
+ });
services.AddSingleton();
services.AddSingleton();
@@ -86,8 +90,20 @@ var host = Host.CreateDefaultBuilder(args)
var loggerFactory = host.Services.GetRequiredService();
var logger = loggerFactory.CreateLogger();
logger.LogInformation("程序启动");
+
+
+var _kafkaPollyPipeline = host.Services.GetRequiredService();
+if (_kafkaPollyPipeline == null)
+{
+ logger.LogInformation("KafkaPollyPipeline未注册!");
+}
+
+
var adminClientService = host.Services.GetRequiredService();
var configuration = host.Services.GetRequiredService();
+
+var kafkaOptionConfig=host.Services.GetRequiredService>();
+
string topic = ProtocolConst.TESTTOPIC;
//await adminClientService.DeleteTopicAsync(topic);
// 创建 topic
diff --git a/modules/JiShe.CollectBus.Kafka.Test/Properties/launchSettings.json b/modules/JiShe.CollectBus.Kafka.Test/Properties/launchSettings.json
new file mode 100644
index 0000000..33504c9
--- /dev/null
+++ b/modules/JiShe.CollectBus.Kafka.Test/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "WSL": {
+ "commandName": "WSL2",
+ "distributionName": ""
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/JiShe.CollectBus.Kafka.Test/appsettings.json b/modules/JiShe.CollectBus.Kafka.Test/appsettings.json
index 9767dee..0734e89 100644
--- a/modules/JiShe.CollectBus.Kafka.Test/appsettings.json
+++ b/modules/JiShe.CollectBus.Kafka.Test/appsettings.json
@@ -13,7 +13,8 @@
"DotNetCore.CAP": "Warning",
"Serilog.AspNetCore": "Information",
"Microsoft.EntityFrameworkCore": "Warning",
- "Microsoft.AspNetCore": "Warning"
+ "Microsoft.AspNetCore": "Warning",
+ "Microsoft.AspNetCore.Diagnostics.HealthChecks": "Warning"
}
},
"WriteTo": [
@@ -34,7 +35,7 @@
"CorsOrigins": "http://localhost:4200,http://localhost:3100"
},
"ConnectionStrings": {
- "Default": "mongodb://admin:admin02023@118.190.144.92:37117,118.190.144.92:37119,118.190.144.92:37120/JiSheCollectBus?authSource=admin&maxPoolSize=400&minPoolSize=10&waitQueueTimeoutMS=5000",
+ "Default": "mongodb://mongo_PmEeF3:lixiao1980@192.168.1.9:27017/JiSheCollectBus?authSource=admin&maxPoolSize=400&minPoolSize=10&waitQueueTimeoutMS=5000",
"Kafka": "192.168.1.9:29092,192.168.1.9:39092,192.168.1.9:49092",
"PrepayDB": "server=118.190.144.92;database=jishe.sysdb;uid=sa;pwd=admin@2023;Encrypt=False;Trust Server Certificate=False",
"EnergyDB": "server=118.190.144.92;database=db_energy;uid=sa;pwd=admin@2023;Encrypt=False;Trust Server Certificate=False"
@@ -43,7 +44,7 @@
"Configuration": "192.168.1.9:6380,password=1q2w3e!@#,syncTimeout=30000,abortConnect=false,connectTimeout=30000,allowAdmin=true",
"MaxPoolSize": "50",
"DefaultDB": "14",
- "HangfireDB": "15"
+ "HangfireDB": "13"
},
"Jwt": {
"Audience": "JiShe.CollectBus",
@@ -51,16 +52,11 @@
"Issuer": "JiShe.CollectBus",
"ExpirationTime": 2
},
- "HealthCheck": {
+ "HealthChecks": {
"IsEnable": true,
- "MySql": {
- "IsEnable": true
- },
- "Pings": {
- "IsEnable": true,
- "Host": "https://www.baidu.com/",
- "TimeOut": 5000
- }
+ "HealthCheckDatabaseName": "HealthChecks",
+ "EvaluationTimeInSeconds": 10,
+ "MinimumSecondsBetweenFailureNotifications": 60
},
"SwaggerConfig": [
{
@@ -74,14 +70,6 @@
"Version": "V1"
}
],
- "Cap": {
- "RabbitMq": {
- "HostName": "118.190.144.92",
- "UserName": "collectbus",
- "Password": "123456",
- "Port": 5672
- }
- },
"Kafka": {
"BootstrapServers": "192.168.1.9:29092,192.168.1.9:39092,192.168.1.9:49092",
"EnableFilter": true,
@@ -92,48 +80,17 @@
"SaslPassword": "lixiao1980",
"KafkaReplicationFactor": 3,
"NumPartitions": 30,
- "ServerTagName": "JiSheCollectBus99"
- //"Topic": {
- // "ReplicationFactor": 3,
- // "NumPartitions": 1000
- //}
+ "FirstCollectionTime": "2025-04-22 16:07:00"
},
- //"Kafka": {
- // "Connections": {
- // "Default": {
- // "BootstrapServers": "192.168.1.9:29092,192.168.1.9:39092,192.168.1.9:49092"
- // // "SecurityProtocol": "SASL_PLAINTEXT",
- // // "SaslMechanism": "PLAIN",
- // // "SaslUserName": "lixiao",
- // // "SaslPassword": "lixiao1980",
- // }
- // },
- // "Consumer": {
- // "GroupId": "JiShe.CollectBus"
- // },
- // "Producer": {
- // "MessageTimeoutMs": 6000,
- // "Acks": -1
- // },
- // "Topic": {
- // "ReplicationFactor": 3,
- // "NumPartitions": 1000
- // },
- // "EventBus": {
- // "GroupId": "JiShe.CollectBus",
- // "TopicName": "DefaultTopicName"
- // }
- //},
"IoTDBOptions": {
"UserName": "root",
"Password": "root",
"ClusterList": [ "192.168.1.9:6667" ],
- "PoolSize": 2,
+ "PoolSize": 32,
"DataBaseName": "energy",
"OpenDebugMode": true,
"UseTableSessionPoolByDefault": false
},
- "ServerTagName": "JiSheCollectBus3",
"Cassandra": {
"ReplicationStrategy": {
"Class": "NetworkTopologyStrategy", //策略为NetworkTopologyStrategy时才会有多个数据中心,SimpleStrategy用在只有一个数据中心的情况下
@@ -156,6 +113,12 @@
"Port": 9043,
"DataCenter": "dc1",
"Rack": "RAC2"
+ },
+ {
+ "Host": "192.168.1.9",
+ "Port": 9044,
+ "DataCenter": "dc1",
+ "Rack": "RAC2"
}
],
"Username": "admin",
@@ -176,5 +139,17 @@
"SerialConsistencyLevel": "Serial",
"DefaultIdempotence": true
}
- }
+ },
+ "ServerApplicationOptions": {
+ "ServerTagName": "JiSheCollectBus4",
+ "SystemType": "Energy",
+ "FirstCollectionTime": "2025-04-28 15:07:00",
+ "AutomaticVerificationTime": "16:07:00",
+ "AutomaticTerminalVersionTime": "17:07:00",
+ "AutomaticTelematicsModuleTime": "17:30:00",
+ "AutomaticDayFreezeTime": "02:30:00",
+ "AutomaticMonthFreezeTime": "03:30:00",
+ "DefaultProtocolPlugin": "T37612012ProtocolPlugin"
+ },
+ "PlugInFolder": ""
}
\ No newline at end of file
diff --git a/modules/JiShe.CollectBus.Kafka/AdminClient/AdminClientService.cs b/modules/JiShe.CollectBus.Kafka/AdminClient/AdminClientService.cs
index 66643a5..ac793b2 100644
--- a/modules/JiShe.CollectBus.Kafka/AdminClient/AdminClientService.cs
+++ b/modules/JiShe.CollectBus.Kafka/AdminClient/AdminClientService.cs
@@ -1,7 +1,9 @@
using Confluent.Kafka;
using Confluent.Kafka.Admin;
+using JiShe.CollectBus.Kafka.Internal;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
namespace JiShe.CollectBus.Kafka.AdminClient;
@@ -9,16 +11,17 @@ namespace JiShe.CollectBus.Kafka.AdminClient;
public class AdminClientService : IAdminClientService, IDisposable, ISingletonDependency
{
private readonly ILogger _logger;
-
+ private readonly KafkaOptionConfig _kafkaOptionConfig;
///
/// Initializes a new instance of the class.
///
///
///
- public AdminClientService(IConfiguration configuration, ILogger logger)
+ public AdminClientService(IConfiguration configuration, ILogger logger, IOptions kafkaOptionConfig)
{
_logger = logger;
- Instance = GetInstance(configuration);
+ _kafkaOptionConfig = kafkaOptionConfig.Value;
+ Instance = GetInstance();
}
///
@@ -142,22 +145,19 @@ public class AdminClientService : IAdminClientService, IDisposable, ISingletonDe
///
/// Gets the instance.
///
- /// The configuration.
///
- public IAdminClient GetInstance(IConfiguration configuration)
+ public IAdminClient GetInstance()
{
- ArgumentException.ThrowIfNullOrWhiteSpace(configuration["Kafka:EnableAuthorization"]);
- var enableAuthorization = bool.Parse(configuration["Kafka:EnableAuthorization"]!);
var adminClientConfig = new AdminClientConfig
{
- BootstrapServers = configuration["Kafka:BootstrapServers"]
+ BootstrapServers = _kafkaOptionConfig.BootstrapServers
};
- if (enableAuthorization)
+ if (_kafkaOptionConfig.EnableAuthorization)
{
- adminClientConfig.SecurityProtocol = SecurityProtocol.SaslPlaintext;
- adminClientConfig.SaslMechanism = SaslMechanism.Plain;
- adminClientConfig.SaslUsername = configuration["Kafka:SaslUserName"];
- adminClientConfig.SaslPassword = configuration["Kafka:SaslPassword"];
+ adminClientConfig.SecurityProtocol = _kafkaOptionConfig.SecurityProtocol;
+ adminClientConfig.SaslMechanism = _kafkaOptionConfig.SaslMechanism;
+ adminClientConfig.SaslUsername = _kafkaOptionConfig.SaslUserName;
+ adminClientConfig.SaslPassword = _kafkaOptionConfig.SaslPassword;
}
return new AdminClientBuilder(adminClientConfig).Build();
}
diff --git a/modules/JiShe.CollectBus.Kafka/Consumer/ConsumerService.cs b/modules/JiShe.CollectBus.Kafka/Consumer/ConsumerService.cs
index fc853e9..043af46 100644
--- a/modules/JiShe.CollectBus.Kafka/Consumer/ConsumerService.cs
+++ b/modules/JiShe.CollectBus.Kafka/Consumer/ConsumerService.cs
@@ -1,6 +1,7 @@
using Confluent.Kafka;
using JiShe.CollectBus.Common;
using JiShe.CollectBus.Common.Consts;
+using JiShe.CollectBus.Common.Helpers;
using JiShe.CollectBus.Kafka.Internal;
using JiShe.CollectBus.Kafka.Serialization;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
@@ -27,7 +28,7 @@ namespace JiShe.CollectBus.Kafka.Consumer
///
/// 消费完或者无数据时的延迟时间
///
- private TimeSpan DelayTime => TimeSpan.FromMilliseconds(100);
+ private static TimeSpan DelayTime => TimeSpan.FromMilliseconds(100);
private readonly KafkaOptionConfig _kafkaOptionConfig;
@@ -127,75 +128,88 @@ namespace JiShe.CollectBus.Kafka.Consumer
///
public async Task SubscribeAsync(string[] topics, Func> messageHandler, string? groupId = null) where TKey : notnull where TValue : class
{
- await _kafkaPollyPipeline.KafkaPipeline.ExecuteAsync(async token =>
+ try
{
-
- var consumerKey = $"{groupId}_{string.Join("_", topics)}_{typeof(TKey).Name}_{typeof(TValue).Name}";
- var cts = new CancellationTokenSource();
-
- var consumer = _consumerStore.GetOrAdd(consumerKey, _ =>
- (
- CreateConsumer(groupId),
- cts
- )).Consumer as IConsumer;
-
- consumer!.Subscribe(topics);
-
- _ = Task.Run(async () =>
+ await _kafkaPollyPipeline.KafkaPipeline.ExecuteAsync(async token =>
{
- while (!cts.IsCancellationRequested)
- {
- try
- {
- //_logger.LogInformation($"Kafka消费: {string.Join("", topics)} 开始拉取消息....");
- var result = consumer.Consume(cts.Token);
- if (result == null || result.Message == null || result.Message.Value == null)
+ var consumerKey = $"{groupId}_{string.Join("_", topics)}_{typeof(TKey).Name}_{typeof(TValue).Name}";
+ var cts = new CancellationTokenSource();
+
+ var consumer = _consumerStore.GetOrAdd(consumerKey, _ =>
+ (
+ CreateConsumer(groupId),
+ cts
+ )).Consumer as IConsumer;
+
+ consumer!.Subscribe(topics);
+
+ _ = Task.Run(async () =>
+ {
+ while (!cts.IsCancellationRequested)
+ {
+ try
{
- await Task.Delay(DelayTime, cts.Token);
- continue;
- }
- if (result.IsPartitionEOF)
- {
-#if DEBUG
- _logger.LogInformation("Kafka消费: {Topic} 分区 {Partition} 已消费完", result.Topic, result.Partition);
-#endif
- await Task.Delay(DelayTime, cts.Token);
- continue;
- }
- if (_kafkaOptionConfig.EnableFilter)
- {
- var headersFilter = new HeadersFilter { { "route-key", Encoding.UTF8.GetBytes(_applicationOptions.ServerTagName) } };
- // 检查 Header 是否符合条件
- if (!headersFilter.Match(result.Message.Headers))
+ //_logger.LogInformation($"Kafka消费: {string.Join("", topics)} 开始拉取消息....");
+
+ var result = consumer.Consume(cts.Token);
+ if (result == null || result.Message == null || result.Message.Value == null)
{
- consumer.Commit(result); // 提交偏移量
- // 跳过消息
+ await Task.Delay(DelayTime, cts.Token);
continue;
}
+ if (result.IsPartitionEOF)
+ {
+#if DEBUG
+ _logger.LogInformation("Kafka消费: {Topic} 分区 {Partition} 已消费完", result.Topic, result.Partition);
+#endif
+ await Task.Delay(DelayTime, cts.Token);
+ continue;
+ }
+ if (_kafkaOptionConfig.EnableFilter)
+ {
+ var headersFilter = new HeadersFilter { { "route-key", Encoding.UTF8.GetBytes(_applicationOptions.ServerTagName) } };
+ // 检查 Header 是否符合条件
+ if (!headersFilter.Match(result.Message.Headers))
+ {
+ consumer.Commit(result); // 提交偏移量
+ // 跳过消息
+ continue;
+ }
+ }
+ bool sucess = await messageHandler(result.Message.Key, result.Message.Value);
+ if (sucess)
+ consumer.Commit(result); // 手动提交
+ }
+ catch (ConsumeException ex) when (KafkaPollyPipeline.IsRecoverableError(ex))
+ {
+ _logger.LogError(ex, $"{string.Join("、", topics)}消息消费失败: {ex.Error.Reason}");
+ throw; // 抛出异常,以便重试
+ }
+ catch (KafkaException ex) when (KafkaPollyPipeline.IsRecoverableError(ex))
+ {
+ _logger.LogError(ex, $"{string.Join("、", topics)} 消息消费失败: {ex.Error.Reason}");
+ throw; // 抛出异常,以便重试
+ }
+ catch (OperationCanceledException)
+ {
+ //ignore
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "处理消息时发生未知错误");
}
- bool sucess = await messageHandler(result.Message.Key, result.Message.Value);
- if (sucess)
- consumer.Commit(result); // 手动提交
}
- catch (ConsumeException ex) when (KafkaPollyPipeline.IsRecoverableError(ex))
- {
- _logger.LogError(ex, $"{string.Join("、", topics)}消息消费失败: {ex.Error.Reason}");
- throw; // 抛出异常,以便重试
- }
- catch (OperationCanceledException)
- {
- //ignore
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "处理消息时发生未知错误");
- }
- }
- }, cts.Token);
- await Task.CompletedTask;
- });
+ }, cts.Token);
+ await Task.CompletedTask;
+ });
+ }
+ catch (Exception ex)
+ {
+
+ throw;
+ }
}
@@ -210,76 +224,84 @@ namespace JiShe.CollectBus.Kafka.Consumer
///
public async Task SubscribeAsync(string[] topics, Func> messageHandler, string? groupId) where TValue : class
{
- await _kafkaPollyPipeline.KafkaPipeline.ExecuteAsync(async token =>
+ try
{
- var consumerKey = $"{groupId}_{string.Join("_", topics)}_{typeof(Ignore).Name}_{typeof(TValue).Name}";
- var cts = new CancellationTokenSource();
- var consumer = _consumerStore.GetOrAdd(consumerKey, _ =>
- (
- CreateConsumer(groupId),
- cts
- )).Consumer as IConsumer;
-
- consumer!.Subscribe(topics);
-
- _ = Task.Run(async () =>
+ await _kafkaPollyPipeline.KafkaPipeline.ExecuteAsync(async token =>
{
- int count = 0;
- while (!cts.IsCancellationRequested)
- {
- try
- {
- //_logger.LogInformation($"Kafka消费: {string.Join("", topics)}_{count} 开始拉取消息....");
- count++;
- var result = consumer.Consume(cts.Token);
- if (result == null || result.Message == null || result.Message.Value == null)
- {
- await Task.Delay(DelayTime, cts.Token);
- continue;
- }
+ var consumerKey = $"{groupId}_{string.Join("_", topics)}_{typeof(Ignore).Name}_{typeof(TValue).Name}";
+ var cts = new CancellationTokenSource();
+ var consumer = _consumerStore.GetOrAdd(consumerKey, _ =>
+ (
+ CreateConsumer(groupId),
+ cts
+ )).Consumer as IConsumer;
- if (result.IsPartitionEOF)
+ consumer!.Subscribe(topics);
+
+ _ = Task.Run(async () =>
+ {
+ int count = 0;
+ while (!cts.IsCancellationRequested)
+ {
+ try
{
-#if DEBUG
- _logger.LogInformation("Kafka消费: {Topic} 分区 {Partition} 已消费完", result.Topic, result.Partition);
-#endif
- await Task.Delay(DelayTime, cts.Token);
- continue;
- }
- if (_kafkaOptionConfig.EnableFilter)
- {
- var headersFilter = new HeadersFilter { { "route-key", Encoding.UTF8.GetBytes(_applicationOptions.ServerTagName) } };
- // 检查 Header 是否符合条件
- if (!headersFilter.Match(result.Message.Headers))
+ //_logger.LogInformation($"Kafka消费: {string.Join("", topics)}_{count} 开始拉取消息....");
+ count++;
+ var result = consumer.Consume(cts.Token);
+ if (result == null || result.Message == null || result.Message.Value == null)
{
- consumer.Commit(result); // 提交偏移量
- // 跳过消息
+ await Task.Delay(DelayTime, cts.Token);
continue;
}
+
+ if (result.IsPartitionEOF)
+ {
+#if DEBUG
+ _logger.LogInformation("Kafka消费: {Topic} 分区 {Partition} 已消费完", result.Topic, result.Partition);
+#endif
+ await Task.Delay(DelayTime, cts.Token);
+ continue;
+ }
+ if (_kafkaOptionConfig.EnableFilter)
+ {
+ var headersFilter = new HeadersFilter { { "route-key", Encoding.UTF8.GetBytes(_applicationOptions.ServerTagName) } };
+ // 检查 Header 是否符合条件
+ if (!headersFilter.Match(result.Message.Headers))
+ {
+ consumer.Commit(result); // 提交偏移量
+ // 跳过消息
+ continue;
+ }
+ }
+ bool sucess = await messageHandler(result.Message.Value);
+ if (sucess)
+ consumer.Commit(result); // 手动提交
+ //else
+ // consumer.StoreOffset(result);
+ }
+ catch (ConsumeException ex) when (KafkaPollyPipeline.IsRecoverableError(ex))
+ {
+ _logger.LogError(ex, $"{string.Join("、", topics)}消息消费失败: {ex.Error.Reason}");
+ throw; // 抛出异常,以便重试
+ }
+ catch (OperationCanceledException)
+ {
+ //ignore
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "处理消息时发生未知错误");
}
- bool sucess = await messageHandler(result.Message.Value);
- if (sucess)
- consumer.Commit(result); // 手动提交
- //else
- // consumer.StoreOffset(result);
}
- catch (ConsumeException ex) when (KafkaPollyPipeline.IsRecoverableError(ex))
- {
- _logger.LogError(ex, $"{string.Join("、", topics)}消息消费失败: {ex.Error.Reason}");
- throw; // 抛出异常,以便重试
- }
- catch (OperationCanceledException)
- {
- //ignore
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "处理消息时发生未知错误");
- }
- }
- }, cts.Token);
- await Task.CompletedTask;
- });
+ }, cts.Token);
+ await Task.CompletedTask;
+ });
+ }
+ catch (Exception ex)
+ {
+
+ throw;
+ }
}
@@ -295,7 +317,15 @@ namespace JiShe.CollectBus.Kafka.Consumer
/// 批次超时时间
public async Task SubscribeBatchAsync(string topic, Func, Task> messageBatchHandler, string? groupId = null, int batchSize = 100, TimeSpan? batchTimeout = null) where TKey : notnull where TValue : class
{
- await SubscribeBatchAsync(new[] { topic }, messageBatchHandler, groupId, batchSize, batchTimeout);
+ try
+ {
+ await SubscribeBatchAsync(new[] { topic }, messageBatchHandler, groupId, batchSize, batchTimeout);
+ }
+ catch (Exception ex)
+ {
+
+ throw;
+ }
}
///
@@ -310,112 +340,125 @@ namespace JiShe.CollectBus.Kafka.Consumer
/// 批次超时时间
public async Task SubscribeBatchAsync(string[] topics, Func, Task> messageBatchHandler, string? groupId = null, int batchSize = 100, TimeSpan? batchTimeout = null) where TKey : notnull where TValue : class
{
- await _kafkaPollyPipeline.KafkaPipeline.ExecuteAsync(async token =>
+ try
{
-
- var consumerKey = $"{groupId}_{string.Join("_", topics)}_{typeof(TKey).Name}_{typeof(TValue).Name}";
- var cts = new CancellationTokenSource();
-
- var consumer = _consumerStore.GetOrAdd(consumerKey, _ =>
- (
- CreateConsumer(groupId),
- cts
- )).Consumer as IConsumer;
- consumer!.Subscribe(topics);
-
- var timeout = batchTimeout ?? TimeSpan.FromSeconds(5); // 默认超时时间调整为5秒
-
- _ = Task.Run(async () =>
+ await _kafkaPollyPipeline.KafkaPipeline.ExecuteAsync(async token =>
{
- var messages = new List<(TValue Value, TopicPartitionOffset Offset)>();
- var startTime = DateTime.UtcNow;
- while (!cts.IsCancellationRequested)
+ var consumerKey = $"{groupId}_{string.Join("_", topics)}_{typeof(TKey).Name}_{typeof(TValue).Name}";
+ var cts = new CancellationTokenSource();
+
+ var consumer = _consumerStore.GetOrAdd(consumerKey, _ =>
+ (
+ CreateConsumer(groupId),
+ cts
+ )).Consumer as IConsumer;
+ consumer!.Subscribe(topics);
+
+ var timeout = batchTimeout ?? TimeSpan.FromSeconds(5); // 默认超时时间调整为5秒
+
+ _ = Task.Run(async () =>
{
- try
- {
- // 非阻塞快速累积消息
- while (messages.Count < batchSize && (DateTime.UtcNow - startTime) < timeout)
- {
- var result = consumer.Consume(TimeSpan.Zero); // 非阻塞调用
+ var messages = new List<(TValue Value, TopicPartitionOffset Offset)>();
+ var startTime = DateTime.UtcNow;
- if (result != null)
+ while (!cts.IsCancellationRequested)
+ {
+ try
+ {
+ // 非阻塞快速累积消息
+ while (messages.Count < batchSize && (DateTime.UtcNow - startTime) < timeout)
{
- if (result.IsPartitionEOF)
+ var result = consumer.Consume(TimeSpan.Zero); // 非阻塞调用
+
+ if (result != null)
{
+ if (result.IsPartitionEOF)
+ {
#if DEBUG
- _logger.LogInformation("Kafka消费: {Topic} 分区 {Partition} 已消费完", result.Topic, result.Partition);
+ _logger.LogInformation("Kafka消费: {Topic} 分区 {Partition} 已消费完", result.Topic, result.Partition);
#endif
+ await Task.Delay(DelayTime, cts.Token);
+ }
+ else if (result.Message.Value != null)
+ {
+ if (_kafkaOptionConfig.EnableFilter)
+ {
+ var headersFilter = new HeadersFilter { { "route-key", Encoding.UTF8.GetBytes(_applicationOptions.ServerTagName) } };
+ // 检查 Header 是否符合条件
+ if (!headersFilter.Match(result.Message.Headers))
+ {
+ consumer.Commit(result); // 提交偏移量
+ // 跳过消息
+ continue;
+ }
+ }
+ messages.Add((result.Message.Value, result.TopicPartitionOffset));
+ }
+ }
+ else
+ {
+ // 无消息时短暂等待
await Task.Delay(DelayTime, cts.Token);
}
- else if (result.Message.Value != null)
+ }
+
+ // 处理批次
+ if (messages.Count > 0)
+ {
+ bool success = await messageBatchHandler(messages.Select(m => m.Value).ToList());
+ if (success)
{
- if (_kafkaOptionConfig.EnableFilter)
+ var offsetsByPartition = new Dictionary();
+ foreach (var msg in messages)
{
- var headersFilter = new HeadersFilter { { "route-key", Encoding.UTF8.GetBytes(_applicationOptions.ServerTagName) } };
- // 检查 Header 是否符合条件
- if (!headersFilter.Match(result.Message.Headers))
+ var tp = msg.Offset.TopicPartition;
+ var offset = msg.Offset.Offset;
+ if (!offsetsByPartition.TryGetValue(tp, out var currentMax) || offset > currentMax)
{
- consumer.Commit(result); // 提交偏移量
- // 跳过消息
- continue;
+ offsetsByPartition[tp] = offset;
}
}
- messages.Add((result.Message.Value, result.TopicPartitionOffset));
- }
- }
- else
- {
- // 无消息时短暂等待
- await Task.Delay(DelayTime, cts.Token);
- }
- }
- // 处理批次
- if (messages.Count > 0)
+ var offsetsToCommit = offsetsByPartition
+ .Select(kv => new TopicPartitionOffset(kv.Key, new Offset(kv.Value + 1)))
+ .ToList();
+ consumer.Commit(offsetsToCommit);
+ }
+ messages.Clear();
+ }
+
+ startTime = DateTime.UtcNow;
+ }
+ catch (ConsumeException ex) when (KafkaPollyPipeline.IsRecoverableError(ex))
{
- bool success = await messageBatchHandler(messages.Select(m => m.Value).ToList());
- if (success)
- {
- var offsetsByPartition = new Dictionary();
- foreach (var msg in messages)
- {
- var tp = msg.Offset.TopicPartition;
- var offset = msg.Offset.Offset;
- if (!offsetsByPartition.TryGetValue(tp, out var currentMax) || offset > currentMax)
- {
- offsetsByPartition[tp] = offset;
- }
- }
-
- var offsetsToCommit = offsetsByPartition
- .Select(kv => new TopicPartitionOffset(kv.Key, new Offset(kv.Value + 1)))
- .ToList();
- consumer.Commit(offsetsToCommit);
- }
- messages.Clear();
+ _logger.LogError(ex, $"{string.Join("、", topics)} 消息消费失败: {ex.Error.Reason}");
+ throw; // 抛出异常,以便重试
}
+ catch (KafkaException ex) when (KafkaPollyPipeline.IsRecoverableError(ex))
+ {
+ _logger.LogError(ex, $"{string.Join("、", topics)} 消息消费失败: {ex.Error.Reason}");
+ throw; // 抛出异常,以便重试
+ }
+ catch (OperationCanceledException)
+ {
+ //ignore
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "处理批量消息时发生未知错误");
+ }
+ }
+ }, cts.Token);
- startTime = DateTime.UtcNow;
- }
- catch (ConsumeException ex) when (KafkaPollyPipeline.IsRecoverableError(ex))
- {
- _logger.LogError(ex, $"{string.Join("、", topics)} 消息消费失败: {ex.Error.Reason}");
- throw; // 抛出异常,以便重试
- }
- catch (OperationCanceledException)
- {
- //ignore
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "处理批量消息时发生未知错误");
- }
- }
- }, cts.Token);
+ await Task.CompletedTask;
+ });
+ }
+ catch (Exception ex)
+ {
- await Task.CompletedTask;
- });
+ throw;
+ }
}
@@ -431,7 +474,15 @@ namespace JiShe.CollectBus.Kafka.Consumer
/// 消费等待时间
public async Task SubscribeBatchAsync(string topic, Func, Task> messageBatchHandler, string? groupId = null, int batchSize = 100, TimeSpan? batchTimeout = null, TimeSpan? consumeTimeout = null) where TValue : class
{
- await SubscribeBatchAsync(new[] { topic }, messageBatchHandler, groupId, batchSize, batchTimeout, consumeTimeout);
+ try
+ {
+ await SubscribeBatchAsync(new[] { topic }, messageBatchHandler, groupId, batchSize, batchTimeout, consumeTimeout);
+ }
+ catch (Exception ex)
+ {
+
+ throw;
+ }
}
@@ -448,111 +499,124 @@ namespace JiShe.CollectBus.Kafka.Consumer
/// 消费等待时间
public async Task SubscribeBatchAsync(string[] topics, Func, Task> messageBatchHandler, string? groupId = null, int batchSize = 100, TimeSpan? batchTimeout = null, TimeSpan? consumeTimeout = null) where TValue : class
{
- await _kafkaPollyPipeline.KafkaPipeline.ExecuteAsync(async token =>
+ try
{
-
- var consumerKey = $"{groupId}_{string.Join("_", topics)}_{typeof(Ignore).Name}_{typeof(TValue).Name}";
- var cts = new CancellationTokenSource();
-
- var consumer = _consumerStore.GetOrAdd(consumerKey, _ =>
- (
- CreateConsumer(groupId),
- cts
- )).Consumer as IConsumer;
-
- consumer!.Subscribe(topics);
-
- var timeout = batchTimeout ?? TimeSpan.FromSeconds(5); // 默认超时时间调整为5秒
-
- _ = Task.Run(async () =>
+ await _kafkaPollyPipeline.KafkaPipeline.ExecuteAsync(async token =>
{
- var messages = new List<(TValue Value, TopicPartitionOffset Offset)>();
- var startTime = DateTime.UtcNow;
- while (!cts.IsCancellationRequested)
+ var consumerKey = $"{groupId}_{string.Join("_", topics)}_{typeof(Ignore).Name}_{typeof(TValue).Name}";
+ var cts = new CancellationTokenSource();
+
+ var consumer = _consumerStore.GetOrAdd(consumerKey, _ =>
+ (
+ CreateConsumer(groupId),
+ cts
+ )).Consumer as IConsumer;
+
+ consumer!.Subscribe(topics);
+
+ var timeout = batchTimeout ?? TimeSpan.FromSeconds(5); // 默认超时时间调整为5秒
+
+ _ = Task.Run(async () =>
{
- try
- {
- // 非阻塞快速累积消息
- while (messages.Count < batchSize && (DateTime.UtcNow - startTime) < timeout)
- {
- var result = consumer.Consume(TimeSpan.Zero); // 非阻塞调用
+ var messages = new List<(TValue Value, TopicPartitionOffset Offset)>();
+ var startTime = DateTime.UtcNow;
- if (result != null)
+ while (!cts.IsCancellationRequested)
+ {
+ try
+ {
+ // 非阻塞快速累积消息
+ while (messages.Count < batchSize && (DateTime.UtcNow - startTime) < timeout)
{
- if (result.IsPartitionEOF)
+ var result = consumer.Consume(TimeSpan.Zero); // 非阻塞调用
+
+ if (result != null)
{
- //_logger.LogInformation("Kafka消费: {Topic} 分区 {Partition} 已消费完", result.Topic, result.Partition);
+ if (result.IsPartitionEOF)
+ {
+ //_logger.LogInformation("Kafka消费: {Topic} 分区 {Partition} 已消费完", result.Topic, result.Partition);
+ await Task.Delay(DelayTime, cts.Token);
+ }
+ else if (result.Message.Value != null)
+ {
+ if (_kafkaOptionConfig.EnableFilter)
+ {
+ var headersFilter = new HeadersFilter { { "route-key", Encoding.UTF8.GetBytes(_applicationOptions.ServerTagName) } };
+ // 检查 Header 是否符合条件
+ if (!headersFilter.Match(result.Message.Headers))
+ {
+ consumer.Commit(result); // 提交偏移量
+ // 跳过消息
+ continue;
+ }
+ }
+ messages.Add((result.Message.Value, result.TopicPartitionOffset));
+ }
+ }
+ else
+ {
+ // 无消息时短暂等待
await Task.Delay(DelayTime, cts.Token);
}
- else if (result.Message.Value != null)
+ }
+
+ // 处理批次
+ if (messages.Count > 0)
+ {
+ bool success = await messageBatchHandler(messages.Select(m => m.Value).ToList());
+ if (success)
{
- if (_kafkaOptionConfig.EnableFilter)
+ var offsetsByPartition = new Dictionary();
+ foreach (var msg in messages)
{
- var headersFilter = new HeadersFilter { { "route-key", Encoding.UTF8.GetBytes(_applicationOptions.ServerTagName) } };
- // 检查 Header 是否符合条件
- if (!headersFilter.Match(result.Message.Headers))
+ var tp = msg.Offset.TopicPartition;
+ var offset = msg.Offset.Offset;
+ if (!offsetsByPartition.TryGetValue(tp, out var currentMax) || offset > currentMax)
{
- consumer.Commit(result); // 提交偏移量
- // 跳过消息
- continue;
+ offsetsByPartition[tp] = offset;
}
}
- messages.Add((result.Message.Value, result.TopicPartitionOffset));
- }
- }
- else
- {
- // 无消息时短暂等待
- await Task.Delay(DelayTime, cts.Token);
- }
- }
- // 处理批次
- if (messages.Count > 0)
+ var offsetsToCommit = offsetsByPartition
+ .Select(kv => new TopicPartitionOffset(kv.Key, new Offset(kv.Value + 1)))
+ .ToList();
+ consumer.Commit(offsetsToCommit);
+ }
+ messages.Clear();
+ }
+
+ startTime = DateTime.UtcNow;
+ }
+ catch (ConsumeException ex) when (KafkaPollyPipeline.IsRecoverableError(ex))
{
- bool success = await messageBatchHandler(messages.Select(m => m.Value).ToList());
- if (success)
- {
- var offsetsByPartition = new Dictionary();
- foreach (var msg in messages)
- {
- var tp = msg.Offset.TopicPartition;
- var offset = msg.Offset.Offset;
- if (!offsetsByPartition.TryGetValue(tp, out var currentMax) || offset > currentMax)
- {
- offsetsByPartition[tp] = offset;
- }
- }
-
- var offsetsToCommit = offsetsByPartition
- .Select(kv => new TopicPartitionOffset(kv.Key, new Offset(kv.Value + 1)))
- .ToList();
- consumer.Commit(offsetsToCommit);
- }
- messages.Clear();
+ _logger.LogError(ex, $"消息消费失败: {ex.Error.Reason}");
+ throw; // 抛出异常,以便重试
}
+ catch (KafkaException ex) when (KafkaPollyPipeline.IsRecoverableError(ex))
+ {
+ _logger.LogError(ex, $"{string.Join("、", topics)} 消息消费失败: {ex.Error.Reason}");
+ throw; // 抛出异常,以便重试
+ }
+ catch (OperationCanceledException)
+ {
+ //ignore
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "处理批量消息时发生未知错误");
+ }
+ }
+ }, cts.Token);
- startTime = DateTime.UtcNow;
- }
- catch (ConsumeException ex) when (KafkaPollyPipeline.IsRecoverableError(ex))
- {
- _logger.LogError(ex, $"消息消费失败: {ex.Error.Reason}");
- throw; // 抛出异常,以便重试
- }
- catch (OperationCanceledException)
- {
- //ignore
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "处理批量消息时发生未知错误");
- }
- }
- }, cts.Token);
+ await Task.CompletedTask;
+ });
+ }
+ catch (Exception ex)
+ {
- await Task.CompletedTask;
- });
+ throw;
+ }
}
@@ -563,12 +627,20 @@ namespace JiShe.CollectBus.Kafka.Consumer
///
public void Unsubscribe(string[] topics, string? groupId) where TKey : notnull where TValue : class
{
- var consumerKey = $"{groupId}_{string.Join("_", topics)}_{typeof(TKey).Name}_{typeof(TValue).Name}";
- if (_consumerStore.TryRemove(consumerKey, out var entry))
+ try
{
- entry.CTS.Cancel();
- (entry.Consumer as IDisposable)?.Dispose();
- entry.CTS.Dispose();
+ var consumerKey = $"{groupId}_{string.Join("_", topics)}_{typeof(TKey).Name}_{typeof(TValue).Name}";
+ if (_consumerStore.TryRemove(consumerKey, out var entry))
+ {
+ entry.CTS.Cancel();
+ (entry.Consumer as IDisposable)?.Dispose();
+ entry.CTS.Dispose();
+ }
+ }
+ catch (Exception ex)
+ {
+
+ throw;
}
}
diff --git a/modules/JiShe.CollectBus.Kafka/Internal/KafkaPollyPipeline.cs b/modules/JiShe.CollectBus.Kafka/Internal/KafkaPollyPipeline.cs
index c467921..fd4bb1b 100644
--- a/modules/JiShe.CollectBus.Kafka/Internal/KafkaPollyPipeline.cs
+++ b/modules/JiShe.CollectBus.Kafka/Internal/KafkaPollyPipeline.cs
@@ -1,16 +1,8 @@
using Confluent.Kafka;
+using Microsoft.Extensions.Logging;
+using Polly;
using Polly.CircuitBreaker;
using Polly.Retry;
-using Polly;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Polly.Contrib.WaitAndRetry;
-using Volo.Abp.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using JiShe.CollectBus.Kafka.Producer;
namespace JiShe.CollectBus.Kafka.Internal
{
@@ -41,7 +33,9 @@ namespace JiShe.CollectBus.Kafka.Internal
ErrorCode.RebalanceInProgress,
ErrorCode.NotCoordinatorForGroup,
ErrorCode.NetworkException,
- ErrorCode.GroupCoordinatorNotAvailable
+ ErrorCode.GroupCoordinatorNotAvailable,
+ ErrorCode.InvalidGroupId,
+ ErrorCode.IllegalGeneration
};
return ex switch
{
diff --git a/modules/JiShe.CollectBus.Kafka/KafkaSubscribeExtensions.cs b/modules/JiShe.CollectBus.Kafka/KafkaSubscribeExtensions.cs
index 3c50aae..ccbe540 100644
--- a/modules/JiShe.CollectBus.Kafka/KafkaSubscribeExtensions.cs
+++ b/modules/JiShe.CollectBus.Kafka/KafkaSubscribeExtensions.cs
@@ -21,6 +21,10 @@ namespace JiShe.CollectBus.Kafka
public static class KafkaSubscribeExtensions
{
+ private static long _threadCount = 0;
+ private static long _topicSubscribeCount = 0;
+ private static long _threadStartCount = 0;
+
public static void UseInitKafkaTopic(this IServiceProvider provider)
{
//初始化主题信息
@@ -46,12 +50,12 @@ namespace JiShe.CollectBus.Kafka
lifetime.ApplicationStarted.Register(() =>
{
var logger = provider.GetRequiredService>();
- var threadCount = 0;
- var topicCount = 0;
+ //var threadCount = 0;
+ //var topicCount = 0;
var assemblyPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
if (string.IsNullOrWhiteSpace(assemblyPath))
{
- logger.LogInformation($"kafka订阅未能找到程序路径");
+ logger.LogWarning($"kafka订阅未能找到程序路径");
return;
}
var dllFiles = Directory.GetFiles(assemblyPath, "*.dll");
@@ -69,21 +73,35 @@ namespace JiShe.CollectBus.Kafka
if (subscribeTypes.Count == 0)
continue;
- foreach (var subscribeType in subscribeTypes)
+ // 并行处理
+ Parallel.ForEach(subscribeTypes, subscribeType =>
{
var subscribes = provider.GetServices(subscribeType).ToList();
- subscribes.ForEach(subscribe =>
+ Parallel.ForEach(subscribes,subscribe =>
{
if (subscribe != null)
{
Tuple tuple = BuildKafkaSubscribe(subscribe, provider, logger, kafkaOptions.Value);
- threadCount += tuple.Item1;
- topicCount += tuple.Item2;
+ //threadCount += tuple.Item1;
+ //topicCount += tuple.Item2;
}
});
- }
+ });
+ //foreach (var subscribeType in subscribeTypes)
+ //{
+ // var subscribes = provider.GetServices(subscribeType).ToList();
+ // subscribes.ForEach(subscribe =>
+ // {
+ // if (subscribe != null)
+ // {
+ // Tuple tuple = BuildKafkaSubscribe(subscribe, provider, logger, kafkaOptions.Value);
+ // threadCount += tuple.Item1;
+ // topicCount += tuple.Item2;
+ // }
+ // });
+ //}
}
- logger.LogInformation($"kafka订阅主题:{topicCount}数,共启动:{threadCount}线程");
+ logger.LogWarning($"kafka订阅主题:{_topicSubscribeCount}数,共启动:{_threadCount}线程");
});
}
@@ -135,24 +153,50 @@ namespace JiShe.CollectBus.Kafka
//var configuration = provider.GetRequiredService();
int threadCount = 0;
- foreach (var sub in subscribedMethods)
+ Parallel.ForEach(subscribedMethods, sub =>
{
- int partitionCount = 3;// kafkaOptionConfig.NumPartitions;
-#if DEBUG
+ Interlocked.Increment(ref _topicSubscribeCount);
+ int partitionCount = sub.Attribute!.TaskCount == -1 ? 3 : sub.Attribute!.TaskCount;// kafkaOptionConfig.NumPartitions;
var adminClientService = provider.GetRequiredService();
+
int topicCount = adminClientService.GetTopicPartitionsNum(sub.Attribute!.Topic);
+
+ //int partitionCount = sub.Attribute!.TaskCount == -1 ? topicCount : sub.Attribute!.TaskCount;// kafkaOptionConfig.NumPartitions;
+
partitionCount = partitionCount > topicCount ? topicCount : partitionCount;
-#endif
- //int partitionCount = sub.Attribute!.TaskCount==-1?adminClientService.GetTopicPartitionsNum(sub.Attribute!.Topic) : sub.Attribute!.TaskCount;
+ //partitionCount = sub.Attribute!.TaskCount == -1 ? adminClientService.GetTopicPartitionsNum(sub.Attribute!.Topic) : sub.Attribute!.TaskCount;
if (partitionCount <= 0)
partitionCount = 1;
- for (int i = 0; i < partitionCount; i++)
+ Parallel.For(0,partitionCount, async (partition) =>
{
- //if (sub.Attribute!.Topic == ProtocolConst.SubscriberLoginReceivedEventName)
- Task.Run(() => StartConsumerAsync(provider, sub.Attribute!, sub.Method, subscribe, logger));
- threadCount++;
- }
- }
+ Interlocked.Increment(ref _threadCount);
+ //Task.Run(() => StartConsumerAsync(provider, sub.Attribute!, sub.Method, subscribe, logger));
+ //threadCount++;
+ await StartConsumerAsync(provider, sub.Attribute!, sub.Method, subscribe, logger);
+ });
+
+ });
+
+ //foreach (var sub in subscribedMethods)
+ //{
+ // //int partitionCount = sub.Attribute!.TaskCount==-1?3: sub.Attribute!.TaskCount;// kafkaOptionConfig.NumPartitions;
+ // var adminClientService = provider.GetRequiredService();
+
+ // int topicCount = adminClientService.GetTopicPartitionsNum(sub.Attribute!.Topic);
+
+ // int partitionCount = sub.Attribute!.TaskCount == -1 ? topicCount : sub.Attribute!.TaskCount;// kafkaOptionConfig.NumPartitions;
+
+ // partitionCount = partitionCount > topicCount ? topicCount : partitionCount;
+ // //partitionCount = sub.Attribute!.TaskCount == -1 ? adminClientService.GetTopicPartitionsNum(sub.Attribute!.Topic) : sub.Attribute!.TaskCount;
+ // if (partitionCount <= 0)
+ // partitionCount = 1;
+ // for (int i = 0; i < partitionCount; i++)
+ // {
+ // //if (sub.Attribute!.Topic == ProtocolConst.SubscriberLoginReceivedEventName)
+ // Task.Run(() => StartConsumerAsync(provider, sub.Attribute!, sub.Method, subscribe, logger));
+ // threadCount++;
+ // }
+ //}
return Tuple.Create(threadCount, subscribedMethods.Length);
}
@@ -165,6 +209,8 @@ namespace JiShe.CollectBus.Kafka
if (attr.EnableBatch)
{
+ Interlocked.Increment(ref _threadStartCount);
+ logger.LogInformation($"kafka开启线程消费:{_threadStartCount}");
await consumerService.SubscribeBatchAsync(attr.Topic, async (message) =>
{
try
@@ -180,11 +226,18 @@ namespace JiShe.CollectBus.Kafka
// 处理消费错误
logger.LogError($"kafka批量消费异常:{ex.Message}");
}
+ catch (Exception ex)
+ {
+ // 处理消费错误
+ logger.LogError($"kafka批量消费异常:{ex.Message}");
+ }
return await Task.FromResult(false);
}, attr.GroupId, attr.BatchSize, attr.BatchTimeout);
}
else
{
+ Interlocked.Increment(ref _threadStartCount);
+ logger.LogInformation($"kafka开启线程消费:{_threadStartCount}");
await consumerService.SubscribeAsync(attr.Topic, async (message) =>
{
try
@@ -200,6 +253,11 @@ namespace JiShe.CollectBus.Kafka
// 处理消费错误
logger.LogError($"kafka消费异常:{ex.Message}");
}
+ catch (Exception ex)
+ {
+ // 处理消费错误
+ logger.LogError($"kafka批量消费异常:{ex.Message}");
+ }
return await Task.FromResult(false);
}, attr.GroupId);
}
@@ -212,125 +270,133 @@ namespace JiShe.CollectBus.Kafka
///
private static async Task ProcessMessageAsync(List messages, MethodInfo method, object subscribe)
{
- var parameters = method.GetParameters();
- bool isGenericTask = method.ReturnType.IsGenericType
- && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>);
- bool existParameters = parameters.Length > 0;
- object[]? executeParameters = null;
-
- if (existParameters)
+ try
{
- IList? list = null;
- Tuple tuple = method.GetParameterTypeInfo();
- bool isEnumerable = false;
- if (tuple.Item2 != null)
- {
- Type listType = typeof(List<>).MakeGenericType(tuple.Item2);
- list = (IList)Activator.CreateInstance(listType)!;
- isEnumerable = tuple.Item2.IsConvertType();
- }
- else
- {
- isEnumerable = tuple.Item1.IsConvertType();
- }
- #region 暂时
- //foreach (var msg in messages)
- //{
- // if (tuple.Item2 != null)
- // {
- // if (isEnumerable)
- // {
- // var parameterType = parameters[0].ParameterType;
- // var data=messages?.Serialize().Deserialize(parameterType);
- // messageObj = data!=null? new[] { data }:null;
- // break;
- // }
- // else
- // {
- // // 集合类型
- // var data = msg?.Serialize().Deserialize(tuple.Item2) /*isEnumerable ? Convert.ChangeType(msg, tuple.Item2) : msg?.Serialize().Deserialize(tuple.Item2)*/;
- // if (data != null)
- // list?.Add(data);
- // }
+ var parameters = method.GetParameters();
+ bool isGenericTask = method.ReturnType.IsGenericType
+ && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>);
+ bool existParameters = parameters.Length > 0;
+ object[]? executeParameters = null;
- // }
- // else
- // {
- // // (dynamic)Convert.ChangeType(msg, tuple.Item1)
- // using (var stream = new MemoryStream(msg))
- // {
- // var data1= System.Text.Json.JsonSerializer.Deserialize(stream, tuple.Item1);
- // }
- // var data = isEnumerable ? System.Text.Json.JsonSerializer.Deserialize(msg, tuple.Item1): msg?.ToString()?.Deserialize(tuple.Item1);
- // if (data != null)
- // messageObj = new[] { data };
- // }
- //}
- //if (tuple.Item2 != null && list != null && list.Count > 0)
- //{
- // messageObj = new[] { list };
- //}
- #endregion
- var parameterDescriptors = method.GetParameters();
- executeParameters = new object?[parameterDescriptors.Length];
- for (var i = 0; i < parameterDescriptors.Length; i++)
+ if (existParameters)
{
- foreach (var item in messages)
- {
-
- object? tempParameter=null;
- var parameterDescriptor = parameterDescriptors[i];
- if (KafkaSerialization.IsJsonType(item))
+ IList? list = null;
+ Tuple tuple = method.GetParameterTypeInfo();
+ bool isEnumerable = false;
+ if (tuple.Item2 != null)
+ {
+ Type listType = typeof(List<>).MakeGenericType(tuple.Item2);
+ list = (IList)Activator.CreateInstance(listType)!;
+ isEnumerable = tuple.Item2.IsConvertType();
+ }
+ else
+ {
+ isEnumerable = tuple.Item1.IsConvertType();
+ }
+ #region 暂时
+ //foreach (var msg in messages)
+ //{
+ // if (tuple.Item2 != null)
+ // {
+ // if (isEnumerable)
+ // {
+ // var parameterType = parameters[0].ParameterType;
+ // var data=messages?.Serialize().Deserialize(parameterType);
+ // messageObj = data!=null? new[] { data }:null;
+ // break;
+ // }
+ // else
+ // {
+ // // 集合类型
+ // var data = msg?.Serialize().Deserialize(tuple.Item2) /*isEnumerable ? Convert.ChangeType(msg, tuple.Item2) : msg?.Serialize().Deserialize(tuple.Item2)*/;
+ // if (data != null)
+ // list?.Add(data);
+ // }
+
+ // }
+ // else
+ // {
+ // // (dynamic)Convert.ChangeType(msg, tuple.Item1)
+ // using (var stream = new MemoryStream(msg))
+ // {
+ // var data1= System.Text.Json.JsonSerializer.Deserialize(stream, tuple.Item1);
+ // }
+ // var data = isEnumerable ? System.Text.Json.JsonSerializer.Deserialize(msg, tuple.Item1): msg?.ToString()?.Deserialize(tuple.Item1);
+ // if (data != null)
+ // messageObj = new[] { data };
+ // }
+ //}
+ //if (tuple.Item2 != null && list != null && list.Count > 0)
+ //{
+ // messageObj = new[] { list };
+ //}
+ #endregion
+ var parameterDescriptors = method.GetParameters();
+ executeParameters = new object?[parameterDescriptors.Length];
+ for (var i = 0; i < parameterDescriptors.Length; i++)
+ {
+ foreach (var item in messages)
{
- tempParameter = KafkaSerialization.Deserialize(item, tuple.Item2 != null? tuple.Item2: parameterDescriptor.ParameterType);
- }
- else
- {
-
- var converter = TypeDescriptor.GetConverter(parameterDescriptor.ParameterType);
- if (converter.CanConvertFrom(item.GetType()))
+
+ object? tempParameter = null;
+ var parameterDescriptor = parameterDescriptors[i];
+ if (KafkaSerialization.IsJsonType(item))
{
- tempParameter = converter.ConvertFrom(item);
+ tempParameter = KafkaSerialization.Deserialize(item, tuple.Item2 != null ? tuple.Item2 : parameterDescriptor.ParameterType);
}
else
{
- if (parameterDescriptor.ParameterType.IsInstanceOfType(item))
- tempParameter = item;
- else
- tempParameter =Convert.ChangeType(item, parameterDescriptor.ParameterType);
- }
- }
- if (tuple.Item2 == null)
- {
- executeParameters[i] = tempParameter;
- }
- else
- {
- list.Add(tempParameter);
- }
-
- }
- if(list!=null && list.Count>0)
- executeParameters[i] = list;
- }
- }
- var result = method.Invoke(subscribe, executeParameters);
- if (result is Task genericTask)
- {
- await genericTask.ConfigureAwait(false);
- return genericTask.Result.Ack;
+ var converter = TypeDescriptor.GetConverter(parameterDescriptor.ParameterType);
+ if (converter.CanConvertFrom(item.GetType()))
+ {
+ tempParameter = converter.ConvertFrom(item);
+ }
+ else
+ {
+ if (parameterDescriptor.ParameterType.IsInstanceOfType(item))
+ tempParameter = item;
+ else
+ tempParameter = Convert.ChangeType(item, parameterDescriptor.ParameterType);
+ }
+ }
+ if (tuple.Item2 == null)
+ {
+ executeParameters[i] = tempParameter;
+ }
+ else
+ {
+ list.Add(tempParameter);
+ }
+
+ }
+ if (list != null && list.Count > 0)
+ executeParameters[i] = list;
+ }
+ }
+
+ var result = method.Invoke(subscribe, executeParameters);
+ if (result is Task genericTask)
+ {
+ await genericTask.ConfigureAwait(false);
+ return genericTask.Result.Ack;
+ }
+ else if (result is Task nonGenericTask)
+ {
+ await nonGenericTask.ConfigureAwait(false);
+ return true;
+ }
+ else if (result is ISubscribeAck ackResult)
+ {
+ return ackResult.Ack;
+ }
+ return false;
}
- else if (result is Task nonGenericTask)
+ catch (Exception ex)
{
- await nonGenericTask.ConfigureAwait(false);
- return true;
+
+ throw;
}
- else if (result is ISubscribeAck ackResult)
- {
- return ackResult.Ack;
- }
- return false;
}
}
diff --git a/modules/JiShe.CollectBus.Kafka/Producer/ProducerService.cs b/modules/JiShe.CollectBus.Kafka/Producer/ProducerService.cs
index 50df423..72e9096 100644
--- a/modules/JiShe.CollectBus.Kafka/Producer/ProducerService.cs
+++ b/modules/JiShe.CollectBus.Kafka/Producer/ProducerService.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
@@ -6,6 +7,7 @@ using System.Text;
using System.Threading.Tasks;
using Confluent.Kafka;
using JiShe.CollectBus.Common;
+using JiShe.CollectBus.Common.Helpers;
using JiShe.CollectBus.Kafka.Consumer;
using JiShe.CollectBus.Kafka.Internal;
using JiShe.CollectBus.Kafka.Serialization;
@@ -68,15 +70,16 @@ namespace JiShe.CollectBus.Kafka.Producer
{
BootstrapServers = _kafkaOptionConfig.BootstrapServers,
//AllowAutoCreateTopics = true,
- QueueBufferingMaxKbytes = 2_097_151, // 修改缓冲区最大为2GB,默认为1GB
+ QueueBufferingMaxKbytes = 4_194_304, // 4_194_304 2_097_151 // 修改缓冲区最大为2GB,默认为1GB
+ QueueBufferingMaxMessages = int.MaxValue, // 缓冲区消息条
CompressionType = CompressionType.Lz4, // 配置使用压缩算法LZ4,其他:gzip/snappy/zstd
BatchSize = 32_768, // 修改批次大小为32K
- LingerMs = 20, // 修改等待时间为20ms
+ LingerMs = 20, // 修改等待时间为20ms,默认为5ms
Acks = Acks.All, // 表明只有所有副本Broker都收到消息才算提交成功, 可以 Acks.Leader
MessageSendMaxRetries = 50, // 消息发送失败最大重试50次
MessageTimeoutMs = 120000, // 消息发送超时时间为2分钟,设置值MessageTimeoutMs > LingerMs
};
-
+
if (_kafkaOptionConfig.EnableAuthorization)
{
config.SecurityProtocol = _kafkaOptionConfig.SecurityProtocol;
@@ -114,17 +117,25 @@ namespace JiShe.CollectBus.Kafka.Producer
///
public async Task ProduceAsync(string topic, TKey key, TValue value)where TKey : notnull where TValue : class
{
- var typeKey = typeof(KafkaProducer);
- var producer = GetProducer(typeKey);
- var message = new Message
+ try
{
- Key = key,
- Value = value,
- Headers = new Headers{
+ var typeKey = typeof(KafkaProducer);
+ var producer = GetProducer(typeKey);
+ var message = new Message
+ {
+ Key = key,
+ Value = value,
+ Headers = new Headers{
{ "route-key", Encoding.UTF8.GetBytes(_applicationOptions.ServerTagName) }
}
- };
- await producer.ProduceAsync(topic, message);
+ };
+ await producer.ProduceAsync(topic, message);
+ }
+ catch (Exception ex)
+ {
+
+ throw;
+ }
}
///
@@ -136,17 +147,24 @@ namespace JiShe.CollectBus.Kafka.Producer
///
public async Task ProduceAsync(string topic, TValue value) where TValue : class
{
- var typeKey = typeof(KafkaProducer);
- var producer = GetProducer(typeKey);
- var message = new Message
+ try
{
- //Key= _kafkaOptionConfig.ServerTagName,
- Value = value,
- Headers = new Headers{
+ var typeKey = typeof(KafkaProducer);
+ var producer = GetProducer(typeKey);
+ var message = new Message
+ {
+ Value = value,
+ Headers = new Headers{
{ "route-key", Encoding.UTF8.GetBytes(_applicationOptions.ServerTagName) }
}
- };
- await producer.ProduceAsync(topic, message);
+ };
+ await producer.ProduceAsync(topic, message);
+ }
+ catch (Exception ex)
+ {
+
+ throw;
+ }
}
///
@@ -162,26 +180,34 @@ namespace JiShe.CollectBus.Kafka.Producer
///
public async Task ProduceAsync(string topic,TKey key,TValue value,int? partition=null, Action>? deliveryHandler = null)where TKey : notnull where TValue : class
{
- var message = new Message
+ try
{
- Key = key,
- Value = value,
- Headers = new Headers{
+ var message = new Message
+ {
+ Key = key,
+ Value = value,
+ Headers = new Headers{
{ "route-key", Encoding.UTF8.GetBytes(_applicationOptions.ServerTagName) }
}
- };
- var typeKey = typeof(KafkaProducer);
- var producer = GetProducer(typeKey);
- if (partition.HasValue)
- {
- var topicPartition = new TopicPartition(topic, partition.Value);
- producer.Produce(topicPartition, message, deliveryHandler);
+ };
+ var typeKey = typeof(KafkaProducer);
+ var producer = GetProducer(typeKey);
+ if (partition.HasValue)
+ {
+ var topicPartition = new TopicPartition(topic, new Partition(partition.Value));
+ producer.Produce(topicPartition, message, deliveryHandler);
+ }
+ else
+ {
+ producer.Produce(topic, message, deliveryHandler);
+ }
+ await Task.CompletedTask;
}
- else
+ catch (Exception ex)
{
- producer.Produce(topic, message, deliveryHandler);
+
+ throw;
}
- await Task.CompletedTask;
}
@@ -197,26 +223,34 @@ namespace JiShe.CollectBus.Kafka.Producer
///
public async Task ProduceAsync(string topic, TValue value, int? partition=null, Action>? deliveryHandler = null) where TValue : class
{
- var message = new Message
+ try
{
- //Key = _kafkaOptionConfig.ServerTagName,
- Value = value,
- Headers = new Headers{
+ var message = new Message
+ {
+ Value = value,
+ Headers = new Headers{
{ "route-key", Encoding.UTF8.GetBytes(_applicationOptions.ServerTagName) }
}
- };
- var typeKey = typeof(KafkaProducer);
- var producer = GetProducer(typeKey);
- if (partition.HasValue)
- {
- var topicPartition = new TopicPartition(topic, partition.Value);
- producer.Produce(topicPartition, message, deliveryHandler);
+ };
+ var typeKey = typeof(KafkaProducer);
+ var producer = GetProducer(typeKey);
+ if (partition.HasValue)
+ {
+ var topicPartition = new TopicPartition(topic, new Partition(partition.Value));
+ //_logger.LogError($"push消息:{topic}-{partition.Value}");
+ producer.Produce(topicPartition, message, deliveryHandler);
+ }
+ else
+ {
+ producer.Produce(topic, message, deliveryHandler);
+ }
+ await Task.CompletedTask;
}
- else
+ catch (Exception ex)
{
- producer.Produce(topic, message, deliveryHandler);
+
+ throw;
}
- await Task.CompletedTask;
}
public void Dispose()
diff --git a/modules/JiShe.CollectBus.Kafka/Serialization/JsonSerializer.cs b/modules/JiShe.CollectBus.Kafka/Serialization/JsonSerializer.cs
index 8034954..98fda49 100644
--- a/modules/JiShe.CollectBus.Kafka/Serialization/JsonSerializer.cs
+++ b/modules/JiShe.CollectBus.Kafka/Serialization/JsonSerializer.cs
@@ -19,6 +19,7 @@ namespace JiShe.CollectBus.Kafka.Serialization
{
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
WriteIndented = false,// 设置格式化输出
+ IncludeFields = true,// 允许反序列化到非公共 setter 和字段
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,// 允许特殊字符
IgnoreReadOnlyFields = true,
IgnoreReadOnlyProperties = true,
@@ -53,7 +54,7 @@ namespace JiShe.CollectBus.Kafka.Serialization
{
if (data.IsEmpty)
return default;
- return JsonSerializer.Deserialize(data, _options)!;
+ return JsonSerializer.Deserialize(data, _options)!;
}
catch (Exception ex)
{
@@ -102,24 +103,37 @@ namespace JiShe.CollectBus.Kafka.Serialization
}
public static object? Deserialize(object value, Type valueType)
{
- var _jsonSerializerOptions = new JsonSerializerOptions
+ try
{
- DefaultIgnoreCondition = JsonIgnoreCondition.Never,
- WriteIndented = false,// 设置格式化输出
- Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,// 允许特殊字符
- IgnoreReadOnlyFields = true,
- IgnoreReadOnlyProperties = true,
- NumberHandling = JsonNumberHandling.AllowReadingFromString, // 允许数字字符串
- AllowTrailingCommas = true, // 忽略尾随逗号
- ReadCommentHandling = JsonCommentHandling.Skip, // 忽略注释
- PropertyNameCaseInsensitive = true, // 属性名称大小写不敏感
- PropertyNamingPolicy = JsonNamingPolicy.CamelCase, // 属性名称使用驼峰命名规则
- Converters = { new DateTimeJsonConverter() } // 注册你的自定义转换器,
- };
+ var _jsonSerializerOptions = new JsonSerializerOptions
+ {
+ DefaultIgnoreCondition = JsonIgnoreCondition.Never,
+ WriteIndented = false,// 设置格式化输出
+ IncludeFields = true,// 允许反序列化到非公共 setter 和字段
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,// 允许特殊字符
+ IgnoreReadOnlyFields = true,
+ IgnoreReadOnlyProperties = true,
+ NumberHandling = JsonNumberHandling.AllowReadingFromString, // 允许数字字符串
+ AllowTrailingCommas = true, // 忽略尾随逗号
+ ReadCommentHandling = JsonCommentHandling.Skip, // 忽略注释
+ PropertyNameCaseInsensitive = true, // 属性名称大小写不敏感
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase, // 属性名称使用驼峰命名规则
+ Converters = { new DateTimeJsonConverter() } // 注册你的自定义转换器,
+ };
- if (value is JsonElement jsonElement) return jsonElement.Deserialize(valueType, _jsonSerializerOptions);
+ if (value is JsonElement jsonElement)
+ {
+ //return jsonElement.Deserialize(valueType, _jsonSerializerOptions);
+ return JsonSerializer.Deserialize(jsonElement, valueType, _jsonSerializerOptions);
+ }
- throw new NotSupportedException("Type is not of type JsonElement");
+ return null;
+ }
+ catch (Exception ex)
+ {
+
+ throw;
+ }
}
}
}
diff --git a/modules/JiShe.CollectBus.MongoDB/MongoDB/CollectBusMongoDbContext.cs b/modules/JiShe.CollectBus.MongoDB/MongoDB/CollectBusMongoDbContext.cs
index ebc5ad1..c0ddd77 100644
--- a/modules/JiShe.CollectBus.MongoDB/MongoDB/CollectBusMongoDbContext.cs
+++ b/modules/JiShe.CollectBus.MongoDB/MongoDB/CollectBusMongoDbContext.cs
@@ -13,6 +13,7 @@ using JiShe.CollectBus.IotSystems.MessageIssueds;
using Volo.Abp.Data;
using Volo.Abp.MongoDB;
using Volo.Abp.MultiTenancy;
+using JiShe.CollectBus.IotSystems.LogRecord;
namespace JiShe.CollectBus.MongoDB;
@@ -32,7 +33,6 @@ public class CollectBusMongoDbContext : AbpMongoDbContext, ICollectBusMongoDbCon
public IMongoCollection MessageIssueds => Collection();
-
protected override void CreateModel(IMongoModelBuilder modelBuilder)
{
diff --git a/modules/JiShe.CollectBus.MongoDB/MongoDB/CollectBusMongoDbModule.cs b/modules/JiShe.CollectBus.MongoDB/MongoDB/CollectBusMongoDbModule.cs
index f427d19..89fd079 100644
--- a/modules/JiShe.CollectBus.MongoDB/MongoDB/CollectBusMongoDbModule.cs
+++ b/modules/JiShe.CollectBus.MongoDB/MongoDB/CollectBusMongoDbModule.cs
@@ -1,5 +1,7 @@
-using JiShe.CollectBus.IotSystems.MeterReadingRecords;
+using JiShe.CollectBus.IotSystems.LogRecord;
+using JiShe.CollectBus.IotSystems.MeterReadingRecords;
using JiShe.CollectBus.Repository;
+using JiShe.CollectBus.Repository.LogRecord;
using JiShe.CollectBus.Repository.MeterReadingRecord;
using JiShe.CollectBus.ShardingStrategy;
using Microsoft.Extensions.DependencyInjection;
@@ -35,10 +37,14 @@ public class CollectBusMongoDbModule : AbpModule
typeof(IShardingStrategy<>),
typeof(DayShardingStrategy<>));
+
+ context.Services.AddTransient(typeof(HourShardingStrategy<>));
+
//// 分表策略仓储 替换默认仓储
//options.AddRepository();
- });
+ options.AddRepository();
+ });
context.Services.AddAlwaysDisableUnitOfWorkTransaction();
Configure(options =>
{
diff --git a/modules/JiShe.CollectBus.MongoDB/Repository/LogRecord/ILogRecordRepository.cs b/modules/JiShe.CollectBus.MongoDB/Repository/LogRecord/ILogRecordRepository.cs
new file mode 100644
index 0000000..8d1c203
--- /dev/null
+++ b/modules/JiShe.CollectBus.MongoDB/Repository/LogRecord/ILogRecordRepository.cs
@@ -0,0 +1,57 @@
+using JiShe.CollectBus.IotSystems.LogRecord;
+using JiShe.CollectBus.IotSystems.MeterReadingRecords;
+using MongoDB.Driver;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Volo.Abp.Domain.Repositories;
+
+namespace JiShe.CollectBus.Repository.LogRecord
+{
+ public interface ILogRecordRepository : IRepository
+ {
+ ///
+ /// 批量插入
+ ///
+ ///
+ ///
+ ///
+ Task InsertManyAsync(List entities,
+ DateTime? dateTime);
+
+ ///
+ /// 单个插入
+ ///
+ ///
+ ///
+ ///
+ Task InsertAsync(LogRecords entity, DateTime? dateTime);
+
+ ///
+ /// 单条更新
+ ///
+ /// 过滤条件,示例:Builders.Filter.Eq(x => x.Id, filter.Id)
+ /// 包含待更新的内容,示例:Builders.Update.Set(x => x.SendHexMessage, SendHexMessage).Set(x => x.MessageId, MessageId)
+ /// 数据实体,用于获取对应的分片库
+ ///
+ Task UpdateOneAsync(FilterDefinition filter, UpdateDefinition update, LogRecords entity);
+
+ ///
+ /// 单个获取
+ ///
+ ///
+ ///
+ ///
+ Task FirOrDefaultAsync(LogRecords entity, DateTime dateTime);
+
+ ///
+ /// 多集合数据查询
+ ///
+ ///
+ ///
+ ///
+ Task> ParallelQueryAsync(DateTime startTime, DateTime endTime);
+ }
+}
diff --git a/modules/JiShe.CollectBus.MongoDB/Repository/LogRecord/LogRecordRepository.cs b/modules/JiShe.CollectBus.MongoDB/Repository/LogRecord/LogRecordRepository.cs
new file mode 100644
index 0000000..415ad06
--- /dev/null
+++ b/modules/JiShe.CollectBus.MongoDB/Repository/LogRecord/LogRecordRepository.cs
@@ -0,0 +1,166 @@
+using JiShe.CollectBus.IotSystems.LogRecord;
+using JiShe.CollectBus.IotSystems.MeterReadingRecords;
+using JiShe.CollectBus.MongoDB;
+using JiShe.CollectBus.Repository.MeterReadingRecord;
+using JiShe.CollectBus.ShardingStrategy;
+using MongoDB.Driver;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Domain.Repositories.MongoDB;
+using Volo.Abp.MongoDB;
+
+namespace JiShe.CollectBus.Repository.LogRecord
+{
+ public class LogRecordRepository : MongoDbRepository, ILogRecordRepository
+ {
+
+ private readonly HourShardingStrategy _hourShardingStrategy;
+ private readonly IMongoDbContextProvider _dbContextProvider;
+
+ public LogRecordRepository(
+ IMongoDbContextProvider dbContextProvider,
+ HourShardingStrategy hourShardingStrategy
+ )
+ : base(dbContextProvider)
+ {
+ _dbContextProvider = dbContextProvider;
+ _hourShardingStrategy = hourShardingStrategy;
+ }
+
+ ///
+ /// 批量插入
+ ///
+ ///
+ ///
+ ///
+ public override async Task> InsertManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ var collection = await GetShardedCollection(DateTime.Now);
+ await collection.InsertManyAsync(entities);
+
+ return entities;
+ }
+
+ ///
+ /// 批量插入
+ ///
+ ///
+ ///
+ ///
+ public async Task InsertManyAsync(List entities, DateTime? dateTime)
+ {
+ var collection = await GetShardedCollection(dateTime);
+ await collection.InsertManyAsync(entities);
+ }
+
+
+ ///
+ /// 单条插入
+ ///
+ ///
+ ///
+ ///
+ public override async Task InsertAsync(LogRecords entity, bool autoSave = false, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ var collection = await GetShardedCollection(DateTime.Now);
+ await collection.InsertOneAsync(entity);
+ return entity;
+ }
+
+
+ ///
+ /// 单条插入
+ ///
+ ///
+ ///
+ ///
+ public async Task InsertAsync(LogRecords entity, DateTime? dateTime)
+ {
+ var collection = await GetShardedCollection(dateTime);
+ await collection.InsertOneAsync(entity);
+ return entity;
+ }
+
+ ///
+ /// 单条更新
+ ///
+ /// 过滤条件,示例:Builders.Filter.Eq(x => x.Id, filter.Id)
+ /// 包含待更新的内容,示例:Builders.Update.Set(x => x.SendHexMessage, SendHexMessage).Set(x => x.MessageId, MessageId)
+ /// 数据实体,用于获取对应的分片库
+ ///
+ public async Task UpdateOneAsync(FilterDefinition filter, UpdateDefinition update, LogRecords entity)
+ {
+ var collection = await GetShardedCollection(entity.CreationTime);
+
+ await collection.UpdateOneAsync(filter, update);
+ return entity;
+ }
+
+
+ ///
+ /// 单个获取
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task FirOrDefaultAsync(LogRecords entity, DateTime dateTime)
+ {
+ var collection = await GetShardedCollection(dateTime);
+ var query = await collection.FindAsync(d => d.CreationTime == dateTime && d.AFN == entity.AFN && d.Fn == entity.Fn && d.Code == entity.Code);
+ return await query.FirstOrDefaultAsync();
+ }
+
+ ///
+ /// 多集合数据查询
+ ///
+ ///
+ ///
+ ///
+ public async Task> ParallelQueryAsync(DateTime startTime, DateTime endTime)
+ {
+ var collectionNames = _hourShardingStrategy.GetQueryCollectionNames(startTime, endTime);
+
+ var dbContext = await DbContextProvider.GetDbContextAsync();
+
+ var tasks = collectionNames.Select(async name =>
+ {
+ var collection = dbContext.Database.GetCollection(name);
+ var filter = Builders.Filter.And(
+ Builders.Filter.Gte(x => x.CreationTime, startTime),
+ Builders.Filter.Lte(x => x.CreationTime, endTime)
+ );
+ return await collection.Find(filter).ToListAsync();
+ });
+
+ var results = await Task.WhenAll(tasks);
+ return results.SelectMany(r => r).ToList();
+ }
+
+ ///
+ /// 获得分片集合
+ ///
+ ///
+ private async Task> GetShardedCollection(DateTime? dateTime)
+ {
+ var dbContext = await DbContextProvider.GetDbContextAsync();
+ string collectionName = string.Empty;
+
+ if (dateTime != null)
+ {
+ collectionName = _hourShardingStrategy.GetCollectionName(dateTime.Value);
+ }
+ else
+ {
+ collectionName = _hourShardingStrategy.GetCurrentCollectionName();
+ }
+
+ return dbContext.Database.GetCollection(collectionName);
+ }
+ }
+}
diff --git a/modules/JiShe.CollectBus.MongoDB/ShardingStrategy/DayShardingStrategy.cs b/modules/JiShe.CollectBus.MongoDB/ShardingStrategy/DayShardingStrategy.cs
index f26136d..0c721a5 100644
--- a/modules/JiShe.CollectBus.MongoDB/ShardingStrategy/DayShardingStrategy.cs
+++ b/modules/JiShe.CollectBus.MongoDB/ShardingStrategy/DayShardingStrategy.cs
@@ -1,4 +1,5 @@
-using JiShe.CollectBus.Common.Extensions;
+using JiShe.CollectBus.Common.Enums;
+using JiShe.CollectBus.Common.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -22,7 +23,7 @@ namespace JiShe.CollectBus.ShardingStrategy
public string GetCollectionName(DateTime dateTime)
{
var baseName = typeof(TEntity).Name;
- return $"{baseName}_{dateTime.GetDataTableShardingStrategy()}";
+ return $"{baseName}_{dateTime.GetDataTableShardingStrategy(TableTimeStrategyEnum.DayShardingStrategy)}";
}
///
@@ -32,7 +33,7 @@ namespace JiShe.CollectBus.ShardingStrategy
public string GetCurrentCollectionName()
{
var baseName = typeof(TEntity).Name;
- return $"{baseName}_{DateTime.Now.GetDataTableShardingStrategy()}";
+ return $"{baseName}_{DateTime.Now.GetDataTableShardingStrategy(TableTimeStrategyEnum.DayShardingStrategy)}";
}
///
@@ -50,7 +51,7 @@ namespace JiShe.CollectBus.ShardingStrategy
while (current <= end)
{
- months.Add($"{baseName}_{current.GetDataTableShardingStrategy()}");
+ months.Add($"{baseName}_{current.GetDataTableShardingStrategy(TableTimeStrategyEnum.DayShardingStrategy)}");
current = current.AddMonths(1);
}
diff --git a/modules/JiShe.CollectBus.MongoDB/ShardingStrategy/HourShardingStrategy.cs b/modules/JiShe.CollectBus.MongoDB/ShardingStrategy/HourShardingStrategy.cs
new file mode 100644
index 0000000..20f65de
--- /dev/null
+++ b/modules/JiShe.CollectBus.MongoDB/ShardingStrategy/HourShardingStrategy.cs
@@ -0,0 +1,58 @@
+using JiShe.CollectBus.Common.Enums;
+using JiShe.CollectBus.Common.Extensions;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace JiShe.CollectBus.ShardingStrategy
+{
+ ///
+ /// 按小时分表
+ ///
+ ///
+ public class HourShardingStrategy
+ {
+ ///
+ /// 获取指定时间对应的集合名
+ ///
+ ///
+ ///
+ public string GetCollectionName(DateTime dateTime)
+ {
+ var baseName = typeof(TEntity).Name;
+ return $"{baseName}_{dateTime.GetDataTableShardingStrategy(TableTimeStrategyEnum.HourShardingStrategy)}";
+ }
+
+ ///
+ /// 获取当前时间对应的集合名
+ ///
+ ///
+ public string GetCurrentCollectionName()
+ {
+ var baseName = typeof(TEntity).Name;
+ return $"{baseName}_{DateTime.Now.GetDataTableShardingStrategy(TableTimeStrategyEnum.HourShardingStrategy)}";
+ }
+
+ ///
+ /// 用于查询时确定目标集合
+ ///
+ ///
+ ///
+ ///
+ public IEnumerable GetQueryCollectionNames(DateTime? startTime, DateTime? endTime)
+ {
+ var list = new List();
+ var current = startTime ?? DateTime.MinValue;
+ var end = endTime ?? DateTime.MaxValue;
+ var baseName = typeof(TEntity).Name;
+
+ while (current <= end)
+ {
+ list.Add($"{baseName}_{current.GetDataTableShardingStrategy(TableTimeStrategyEnum.HourShardingStrategy)}");
+ current = current.AddHours(1);
+ }
+
+ return list.Distinct();
+ }
+ }
+}
diff --git a/services/JiShe.CollectBus.Domain/Protocol3761/Dto/AFN12_F188_AnalysisDto.cs b/modules/JiShe.CollectBus.MongoDB/ShardingStrategy/IHourShardingStrategy.cs
similarity index 51%
rename from services/JiShe.CollectBus.Domain/Protocol3761/Dto/AFN12_F188_AnalysisDto.cs
rename to modules/JiShe.CollectBus.MongoDB/ShardingStrategy/IHourShardingStrategy.cs
index 1afb8ff..ad0654c 100644
--- a/services/JiShe.CollectBus.Domain/Protocol3761/Dto/AFN12_F188_AnalysisDto.cs
+++ b/modules/JiShe.CollectBus.MongoDB/ShardingStrategy/IHourShardingStrategy.cs
@@ -4,9 +4,9 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
-namespace JiShe.CollectBus.Protocol.Contracts.Protocol.Dto
+namespace JiShe.CollectBus.ShardingStrategy
{
- public class AFN12_F188_AnalysisDto : AnalysisBaseDto
+ public interface IHourShardingStrategy : IShardingStrategy
{
}
}
diff --git a/protocols/JiShe.CollectBus.Protocol.T1882018/SendData/Telemetry1882018PacketBuilder.cs b/protocols/JiShe.CollectBus.Protocol.T1882018/SendData/Telemetry1882018PacketBuilder.cs
index e5511f2..7dc8e43 100644
--- a/protocols/JiShe.CollectBus.Protocol.T1882018/SendData/Telemetry1882018PacketBuilder.cs
+++ b/protocols/JiShe.CollectBus.Protocol.T1882018/SendData/Telemetry1882018PacketBuilder.cs
@@ -46,7 +46,7 @@ namespace JiShe.CollectBus.Protocol.T1882018.SendData
{
var itemCodeArr = request.ItemCode.Split('_');
var c_data = itemCodeArr[0];//01
- var d_data = itemCodeArr[1];//91 或者 90
+ var d_data = itemCodeArr[2];//91 或者 90
var dataUnit = new List() { "1F", d_data, "00" };
var dataList = Build188SendData.Build188SendCommand(request.MeterAddress, c_data, dataUnit);
@@ -64,7 +64,7 @@ namespace JiShe.CollectBus.Protocol.T1882018.SendData
{
var itemCodeArr = request.ItemCode.Split('_');
var c_data = itemCodeArr[0];//01
- var d_data = itemCodeArr[1];//55 或者 99
+ var d_data = itemCodeArr[2];//55 或者 99
var dataUnit = new List() { "A0", "17", "00", d_data };
var dataList = Build188SendData.Build188SendCommand(request.MeterAddress, c_data, dataUnit);
diff --git a/protocols/JiShe.CollectBus.Protocol.T1882018/T1882018ProtocolPlugin.cs b/protocols/JiShe.CollectBus.Protocol.T1882018/T1882018ProtocolPlugin.cs
index 5aa1b1f..5b3044a 100644
--- a/protocols/JiShe.CollectBus.Protocol.T1882018/T1882018ProtocolPlugin.cs
+++ b/protocols/JiShe.CollectBus.Protocol.T1882018/T1882018ProtocolPlugin.cs
@@ -91,8 +91,10 @@ namespace JiShe.CollectBus.Protocol.T1882018
List dataUnit = new List();
//数据转发场景 10H_F1
- if (aFNStr == "10" && request.SubProtocolRequest != null && string.IsNullOrWhiteSpace(request.SubProtocolRequest.ItemCode) == false)
- {
+ if (request.ItemCode == T37612012PacketItemCodeConst.AFN10HFN01H && request.SubProtocolRequest != null && string.IsNullOrWhiteSpace(request.SubProtocolRequest.ItemCode) == false)
+ {
+ //var subItemCodeArr = request.SubProtocolRequest.ItemCode.Split("_");
+
var t188PacketHandlerName = $"{T1882018PacketItemCodeConst.BasicT1882018}_{request.SubProtocolRequest.ItemCode}_Send";
Telemetry1882018PacketResponse t645PacketResponse = null;
diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_00H/AFN0_F1_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_00H/AFN0_F1_Analysis.cs
index 63ab925..6fd96e1 100644
--- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_00H/AFN0_F1_Analysis.cs
+++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_00H/AFN0_F1_Analysis.cs
@@ -1,4 +1,11 @@
-using JiShe.CollectBus.Protocol.Dto;
+using Apache.IoTDB;
+using JiShe.CollectBus.Common.Consts;
+using JiShe.CollectBus.Common.Enums;
+using JiShe.CollectBus.Common.Extensions;
+using JiShe.CollectBus.IotSystems.Ammeters;
+using JiShe.CollectBus.IotSystems.Devices;
+using JiShe.CollectBus.Protocol.Contracts.Protocol.Dto;
+using JiShe.CollectBus.Protocol.Dto;
using JiShe.CollectBus.Protocol.Interfaces;
using JiShe.CollectBus.Protocol3761;
using Microsoft.Extensions.Logging;
@@ -8,35 +15,63 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_00H
///
/// 5.1.3.1 F1:全部确认:对收到报文中的全部数据单元标识进行确认
///
- public class AFN0_F1_Analysis: IAnalysisStrategy>
+ public class AFN0_F1_Analysis: IAnalysisStrategy
{
private readonly ILogger _logger;
+ private readonly DataStorage _dataStorage;
- public AFN0_F1_Analysis(ILogger logger)
+ public AFN0_F1_Analysis(ILogger logger, DataStorage dataStorage)
{
_logger = logger;
+ _dataStorage= dataStorage;
}
- public Task> ExecuteAsync(TB3761 tB3761)
+ public async Task ExecuteAsync(TB3761 input, Action? result = null)
{
try
{
- ArgumentNullException.ThrowIfNull(nameof(tB3761));
- UnitDataAnalysis dto = new UnitDataAnalysis
+ ArgumentNullException.ThrowIfNull(input);
+ ArgumentNullException.ThrowIfNull(input.A.Code);
+ var data = new AnalysisBaseDto()
{
- Code = tB3761.A.Code,
- AFN = tB3761.AFN_FC.AFN,
- Fn = tB3761.DT.Fn,
- Pn = tB3761.DA.Pn,
- Data= true
+ FiledDesc = "全部确认",
+ DataValue = true,
+ ItemType= $"{input.AFN_FC.AFN.HexToDecStr().PadLeft(2, '0')}_{input.DT.Fn}"
};
- return Task.FromResult(dto);
+ // 查询电表信息
+ DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code);
+ if (deviceInfo != null)
+ {
+ data.ProjectId = deviceInfo.ProjectID;
+ data.DeviceId = deviceInfo.FocusId;
+ data.DatabaseBusiID = deviceInfo.DatabaseBusiID;
+ data.DeviceAddress = deviceInfo.FocusAddress;
+ data.DeviceType = MeterTypeEnum.Focus;
+ data.FocusId= deviceInfo.FocusId;
+ }
+ UnitDataAnalysis> dto = new UnitDataAnalysis>
+ {
+ Code = input.A.Code,
+ AFN = input.AFN_FC.AFN,
+ Fn = input.DT.Fn,
+ Pn = input.DA.Pn,
+ Data = data,
+ ReceivedHexMessage = input.BaseHexMessage.HexMessageString,
+ MessageId = input.MessageId,
+ ReceivedTime = input.ReceivedTime,
+ DensityUnit = DensityUnit.None,
+ TimeDensity = -1,
+ DataType= IOTDBDataTypeConst.Log
+ };
+ result?.Invoke(dto);
+ await _dataStorage.SaveDataToIotDbAsync(dto);
+ return await Task.FromResult(true);
}
catch (Exception ex)
{
- _logger.LogError(ex, $"00_1解析失败:{tB3761.A.Code}-{tB3761.DT.Fn}-{tB3761.BaseHexMessage.HexMessageString},{ex.Message}");
- return null;
+ _logger.LogError(ex, $"00_1解析失败:{input.A.Code}-{input.DT.Fn}-{input.BaseHexMessage.HexMessageString},{ex.Message}");
}
+ return await Task.FromResult(false);
}
}
diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_00H/AFN0_F2_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_00H/AFN0_F2_Analysis.cs
index 5ae0b45..2e465b8 100644
--- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_00H/AFN0_F2_Analysis.cs
+++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_00H/AFN0_F2_Analysis.cs
@@ -1,4 +1,10 @@
-using JiShe.CollectBus.Protocol.Dto;
+using JiShe.CollectBus.Common.Consts;
+using JiShe.CollectBus.Common.Enums;
+using JiShe.CollectBus.Common.Extensions;
+using JiShe.CollectBus.IotSystems.Ammeters;
+using JiShe.CollectBus.IotSystems.Devices;
+using JiShe.CollectBus.Protocol.Contracts.Protocol.Dto;
+using JiShe.CollectBus.Protocol.Dto;
using JiShe.CollectBus.Protocol.Interfaces;
using JiShe.CollectBus.Protocol3761;
using Microsoft.Extensions.Logging;
@@ -8,34 +14,66 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_00H
///
/// 5.1.3.2 F2:全部否认
///
- public class AFN0_F2_Analysis : IAnalysisStrategy>
+ public class AFN0_F2_Analysis : IAnalysisStrategy
{
private readonly ILogger _logger;
-
- public AFN0_F2_Analysis(ILogger logger)
+ private readonly DataStorage _dataStorage;
+ public AFN0_F2_Analysis(ILogger logger, DataStorage dataStorage)
{
_logger = logger;
+ _dataStorage = dataStorage;
}
- public Task> ExecuteAsync(TB3761 input)
+ public async Task ExecuteAsync(TB3761 input, Action? result = null)
{
try
{
ArgumentNullException.ThrowIfNull(input);
- UnitDataAnalysis dto = new UnitDataAnalysis
+ ArgumentNullException.ThrowIfNull(input.A.Code);
+ var data = new AnalysisBaseDto()
+ {
+ FiledDesc = "全部否认",
+ DataValue = false,
+ ItemType = $"{input.AFN_FC.AFN.HexToDecStr().PadLeft(2, '0')}_{input.DT.Fn}"
+ };
+ // 查询电表信息
+ DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code);
+ if (deviceInfo != null)
+ {
+ data.ProjectId = deviceInfo.ProjectID;
+ data.DeviceId = deviceInfo.FocusId;
+ data.DatabaseBusiID = deviceInfo.DatabaseBusiID;
+ data.DeviceAddress = deviceInfo.FocusAddress;
+ data.DeviceType = MeterTypeEnum.Focus;
+ data.FocusId = deviceInfo.FocusId;
+ }
+ UnitDataAnalysis> dto = new UnitDataAnalysis>
{
Code = input.A.Code,
AFN = input.AFN_FC.AFN,
Fn = input.DT.Fn,
Pn = input.DA.Pn,
- Data = false,
+ Data = data,
+ ReceivedHexMessage = input.BaseHexMessage.HexMessageString,
+ MessageId = input.MessageId,
+ ReceivedTime =input.ReceivedTime,
+ DensityUnit = DensityUnit.None,
+ TimeDensity = -1,
+ DataType = IOTDBDataTypeConst.Log
};
- return Task.FromResult(dto);
+ result?.Invoke(dto);
+#if DEBUG
+ _logger.LogWarning($"全部否认:{input.A.Code}-{input.DT.Fn}-{input.BaseHexMessage.HexMessageString}");
+#endif
+ await _dataStorage.SaveDataToIotDbAsync(dto);
+ return await Task.FromResult(true);
}
catch (Exception ex)
{
_logger.LogError(ex, $"00_2解析失败:{input.A.Code}-{input.DT.Fn}-{input.BaseHexMessage.HexMessageString},{ex.Message}");
- return null;
}
+ return await Task.FromResult(false);
}
+
+
}
}
diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_02H/AFN2_F1_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_02H/AFN2_F1_Analysis.cs
new file mode 100644
index 0000000..686d027
--- /dev/null
+++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_02H/AFN2_F1_Analysis.cs
@@ -0,0 +1,77 @@
+using JiShe.CollectBus.Common.Consts;
+using JiShe.CollectBus.Common.Enums;
+using JiShe.CollectBus.Common.Extensions;
+using JiShe.CollectBus.IotSystems.Ammeters;
+using JiShe.CollectBus.IotSystems.Devices;
+using JiShe.CollectBus.Protocol.Contracts.Protocol.Dto;
+using JiShe.CollectBus.Protocol.Dto;
+using JiShe.CollectBus.Protocol.Interfaces;
+using JiShe.CollectBus.Protocol3761;
+using Microsoft.Extensions.Logging;
+
+namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_02H
+{
+ ///
+ /// 5.3.3.1 F1:登录
+ ///
+ public class AFN2_F1_Analysis : IAnalysisStrategy
+ {
+ private readonly ILogger _logger;
+ private readonly DataStorage _dataStorage;
+ public AFN2_F1_Analysis(ILogger logger, DataStorage dataStorage)
+ {
+ _logger = logger;
+ _dataStorage= dataStorage;
+ }
+
+ public async Task ExecuteAsync(TB3761 input, Action? result = null)
+ {
+ try
+ {
+ ArgumentNullException.ThrowIfNull(input);
+ ArgumentNullException.ThrowIfNull(input.A.Code);
+ var data = new AnalysisBaseDto()
+ {
+ FiledDesc = "登录",
+ FiledName = "Type",
+ DataValue = "Login",
+ ItemType = $"{input.AFN_FC.AFN.HexToDecStr().PadLeft(2, '0')}_{input.DT.Fn}"
+ };
+ // 查询设备信息
+ DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code);
+ if (deviceInfo != null)
+ {
+ data.ProjectId = deviceInfo.ProjectID;
+ data.DeviceId = deviceInfo.FocusId;
+ data.DatabaseBusiID = deviceInfo.DatabaseBusiID;
+ data.DeviceAddress = deviceInfo.FocusAddress;
+ data.DeviceType = MeterTypeEnum.Focus;
+ data.FocusId = deviceInfo.FocusId;
+ }
+ UnitDataAnalysis> dto = new UnitDataAnalysis>
+ {
+ Code = input.A.Code,
+ AFN = input.AFN_FC.AFN,
+ Fn = input.DT.Fn,
+ Pn = input.DA.Pn,
+ Data = data,
+ ReceivedHexMessage = input.BaseHexMessage.HexMessageString,
+ MessageId = input.MessageId,
+ ReceivedTime = input.ReceivedTime,
+ DensityUnit = DensityUnit.None,
+ TimeDensity = -1,
+ DataType = IOTDBDataTypeConst.Status
+ };
+ result?.Invoke(dto);
+ await _dataStorage.SaveStatusToIotDbAsync(dto);
+ return await Task.FromResult(true);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, $"02_1解析失败:{input.A.Code}-{input.DT.Fn}-{input.BaseHexMessage.HexMessageString},{ex.Message}");
+ }
+ return await Task.FromResult(false);
+ }
+
+ }
+}
diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_02H/AFN2_F2_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_02H/AFN2_F2_Analysis.cs
new file mode 100644
index 0000000..6428063
--- /dev/null
+++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_02H/AFN2_F2_Analysis.cs
@@ -0,0 +1,76 @@
+using JiShe.CollectBus.Common.Consts;
+using JiShe.CollectBus.Common.Enums;
+using JiShe.CollectBus.Common.Extensions;
+using JiShe.CollectBus.IotSystems.Ammeters;
+using JiShe.CollectBus.IotSystems.Devices;
+using JiShe.CollectBus.Protocol.Contracts.Protocol.Dto;
+using JiShe.CollectBus.Protocol.Dto;
+using JiShe.CollectBus.Protocol.Interfaces;
+using JiShe.CollectBus.Protocol3761;
+using Microsoft.Extensions.Logging;
+
+namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_02H
+{
+ ///
+ /// 5.3.3.2 F2:退出登录
+ ///
+ public class AFN2_F2_Analysis : IAnalysisStrategy
+ {
+ private readonly ILogger _logger;
+ private readonly DataStorage _dataStorage;
+ public AFN2_F2_Analysis(ILogger logger, DataStorage dataStorage)
+ {
+ _logger = logger;
+ _dataStorage = dataStorage;
+ }
+
+ public async Task ExecuteAsync(TB3761 input, Action? result = null)
+ {
+ try
+ {
+ ArgumentNullException.ThrowIfNull(input);
+ ArgumentNullException.ThrowIfNull(input.A.Code);
+ var data = new AnalysisBaseDto()
+ {
+ FiledDesc = "退出登录",
+ FiledName = "Type",
+ DataValue = "LogOut",
+ ItemType = $"{input.AFN_FC.AFN.HexToDecStr().PadLeft(2, '0')}_{input.DT.Fn}"
+ };
+ // 查询设备信息
+ DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code);
+ if (deviceInfo != null)
+ {
+ data.ProjectId = deviceInfo.ProjectID;
+ data.DeviceId = deviceInfo.FocusId;
+ data.DatabaseBusiID = deviceInfo.DatabaseBusiID;
+ data.DeviceAddress = deviceInfo.FocusAddress;
+ data.DeviceType = MeterTypeEnum.Focus;
+ data.FocusId = deviceInfo.FocusId;
+ }
+ UnitDataAnalysis> dto = new UnitDataAnalysis>
+ {
+ Code = input.A.Code,
+ AFN = input.AFN_FC.AFN,
+ Fn = input.DT.Fn,
+ Pn = input.DA.Pn,
+ Data = data,
+ ReceivedHexMessage = input.BaseHexMessage.HexMessageString,
+ MessageId = input.MessageId,
+ ReceivedTime = input.ReceivedTime,
+ DensityUnit = DensityUnit.None,
+ TimeDensity = -1,
+ DataType = IOTDBDataTypeConst.Status
+ };
+ result?.Invoke(dto);
+ await _dataStorage.SaveStatusToIotDbAsync(dto);
+ return await Task.FromResult(true);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, $"02_2解析失败:{input.A.Code}-{input.DT.Fn}-{input.BaseHexMessage.HexMessageString},{ex.Message}");
+ }
+ return await Task.FromResult(false);
+ }
+ }
+}
diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_02H/AFN2_F3_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_02H/AFN2_F3_Analysis.cs
new file mode 100644
index 0000000..1c3027b
--- /dev/null
+++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_02H/AFN2_F3_Analysis.cs
@@ -0,0 +1,76 @@
+using JiShe.CollectBus.Common.Consts;
+using JiShe.CollectBus.Common.Enums;
+using JiShe.CollectBus.Common.Extensions;
+using JiShe.CollectBus.IotSystems.Ammeters;
+using JiShe.CollectBus.IotSystems.Devices;
+using JiShe.CollectBus.Protocol.Contracts.Protocol.Dto;
+using JiShe.CollectBus.Protocol.Dto;
+using JiShe.CollectBus.Protocol.Interfaces;
+using JiShe.CollectBus.Protocol3761;
+using Microsoft.Extensions.Logging;
+
+namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData.AFN_02H
+{
+ ///
+ /// 5.3.3.3 F3:心跳
+ ///
+ public class AFN2_F3_Analysis : IAnalysisStrategy
+ {
+ private readonly ILogger _logger;
+ private readonly DataStorage _dataStorage;
+ public AFN2_F3_Analysis(ILogger logger, DataStorage dataStorage)
+ {
+ _logger = logger;
+ _dataStorage = dataStorage;
+ }
+
+ public async Task ExecuteAsync(TB3761 input, Action? result = null)
+ {
+ try
+ {
+ ArgumentNullException.ThrowIfNull(input);
+ ArgumentNullException.ThrowIfNull(input.A.Code);
+ var data = new AnalysisBaseDto()
+ {
+ FiledDesc = "心跳",
+ FiledName = "Type",
+ DataValue = "Heartbeat",
+ ItemType = $"{input.AFN_FC.AFN.HexToDecStr().PadLeft(2, '0')}_{input.DT.Fn}"
+ };
+ // 查询设备信息
+ DeviceInfo? deviceInfo = await _dataStorage.GetDeviceInfoAsync(input.A.Code);
+ if (deviceInfo != null)
+ {
+ data.ProjectId = deviceInfo.ProjectID;
+ data.DeviceId = deviceInfo.FocusId;
+ data.DatabaseBusiID = deviceInfo.DatabaseBusiID;
+ data.DeviceAddress = deviceInfo.FocusAddress;
+ data.DeviceType = MeterTypeEnum.Focus;
+ data.FocusId = deviceInfo.FocusId;
+ }
+ UnitDataAnalysis> dto = new UnitDataAnalysis>
+ {
+ Code = input.A.Code,
+ AFN = input.AFN_FC.AFN,
+ Fn = input.DT.Fn,
+ Pn = input.DA.Pn,
+ Data = data,
+ ReceivedHexMessage = input.BaseHexMessage.HexMessageString,
+ MessageId = input.MessageId,
+ ReceivedTime = input.ReceivedTime,
+ DensityUnit = DensityUnit.None,
+ TimeDensity = -1,
+ DataType = IOTDBDataTypeConst.Status
+ };
+ result?.Invoke(dto);
+ await _dataStorage.SaveStatusToIotDbAsync(dto);
+ return await Task.FromResult(true);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, $"02_3解析失败:{input.A.Code}-{input.DT.Fn}-{input.BaseHexMessage.HexMessageString},{ex.Message}");
+ }
+ return await Task.FromResult(false);
+ }
+ }
+}
diff --git a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_09H/AFN9_F1_Analysis.cs b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_09H/AFN9_F1_Analysis.cs
index fd93dab..5011b4e 100644
--- a/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_09H/AFN9_F1_Analysis.cs
+++ b/protocols/JiShe.CollectBus.Protocol.T37612012/AnalysisData/AFN_09H/AFN9_F1_Analysis.cs
@@ -1,8 +1,13 @@
using System.Text;
+using JiShe.CollectBus.Common.Consts;
+using JiShe.CollectBus.Common.Enums;
using JiShe.CollectBus.Common.Extensions;
+using JiShe.CollectBus.IotSystems.Ammeters;
+using JiShe.CollectBus.IotSystems.Devices;
using JiShe.CollectBus.Protocol.Contracts.Protocol.Dto;
using JiShe.CollectBus.Protocol.Dto;
using JiShe.CollectBus.Protocol.Interfaces;
+using JiShe.CollectBus.Protocol.T37612012.AnalysisData;
using JiShe.CollectBus.Protocol3761;
using Microsoft.Extensions.Logging;
@@ -11,39 +16,68 @@ namespace JiShe.CollectBus.Protocol.AnalysisData.AFN_09H
///