Healnet.Infrastructure
4.0.38
dotnet add package Healnet.Infrastructure --version 4.0.38
NuGet\Install-Package Healnet.Infrastructure -Version 4.0.38
<PackageReference Include="Healnet.Infrastructure" Version="4.0.38" />
<PackageVersion Include="Healnet.Infrastructure" Version="4.0.38" />
<PackageReference Include="Healnet.Infrastructure" />
paket add Healnet.Infrastructure --version 4.0.38
#r "nuget: Healnet.Infrastructure, 4.0.38"
#:package Healnet.Infrastructure@4.0.38
#addin nuget:?package=Healnet.Infrastructure&version=4.0.38
#tool nuget:?package=Healnet.Infrastructure&version=4.0.38
Healnet.Infrastructure 基础设施模块
版本: v4.0.38 | 模块: Healnet.Infrastructure | 依赖: Healnet.Core → Healnet.Common | TFM: net10.0
概述
Healnet.Infrastructure 是 Healnet 微服务框架的基础设施实现层,对 23 个 中间件/基础设施组件进行了统一封装,提供一致的 DI 注册方式与配置约定。它实现 Healnet.Core 定义的抽象契约(如 ICacheService、IUnitOfWork、ITokenService、IHttpClientService、IConsulService、IAIService 等),并集成所有第三方技术组件。
⭐ AI 能力彻底 DIP:AI 抽象契约已上移至
Healnet.Core.AI/Healnet.Core.AI.Memory,本层AI/目录仅含实现类(如UnifiedAIService、AgentService、CopilotClient、MicrosoftAIClient、ChatContextManager、AgentContextManager、RAGService,以及 Memory 三实现ConversationRepository/RedisShortTermMemoryStore/MemoryOrchestrator),并引用 Semantic Kernel / Microsoft.Extensions.AI。
📁 模块范围说明:除以下 23 个带独立 README 的公开集成模块外,本层还包含
Helpers/(如ClientIpExtractor)、Skill/(Skill 加载与渲染,含SkillLoader/OssSkillLoader/SkillParser/SkillTemplateRenderer)与Mvc/(模型校验统一响应AddHealnetUnifiedValidationResponse,4.0.34 新增)三个内部目录,未单独成文,不计入对外集成模块数量。
⚠️ 类型归属澄清:Core 的
PackageReleaseNotes在 3.0.1 条目将ApiKeyAuthenticationHandler与GrpcClientBase归为 Core 新增,但二者实际位于本层 ——ApiKeyAuthenticationHandler属于Infrastructure.Authentication(见下方“认证安全”导航),GrpcClientBase<TClient>属于Infrastructure.gRPC。Core 仅定义认证抽象(ITokenService/JwtOptions)与 Consul 抽象。
快速导航
| 类别 | 模块 | 注册方法 | 配置节点 | 生命周期 |
|---|---|---|---|---|
| 认证安全 | Authentication | AddJwtAuth(...)(另含 ApiKeyAuthenticationHandler:支持 CustomTokenResolver 与 client_source=workbuddy claim) |
Healnet:Jwt |
Scoped |
| 认证安全 | Security | AddSecurityHeaders(...) / AddCorsPolicy(...) |
Healnet:Security / Healnet:Cors |
Singleton |
| 数据存储 | SqlSugar | AddSqlSugar(...) |
Healnet:SqlSugar |
Singleton/Scope |
| 数据存储 | MongoDB | AddMongoDB(...) |
Healnet:MongoDB |
Singleton/Scoped |
| 数据存储 | ElasticSearch | AddElasticSearch(...) |
Healnet:ElasticSearch |
Singleton |
| 数据存储 | Milvus | AddMilvus(...) |
Healnet:Milvus |
Singleton |
| 缓存锁 | Redis | AddRedis(...) |
Healnet:Redis |
Singleton |
| 消息队列 | RabbitMQ | 手动注册 IConnection + IRabbitMQService(4.0.12 起为 Singleton 且实现 IDisposable) |
自定义 | Singleton |
| 服务发现 | Consul | AddConsul(...) + UseConsul(...) |
Healnet:Consul |
Singleton |
| HTTP 客户端 | Http | AddHttpClientService(...) |
Healnet:HttpClient:{name} |
Scoped |
| API 网关 | YARP | AddYarpConfigBuilder() / AddYarpStaticConfig() / AddYarpConsulDiscovery(...) / AddYarpResilience() |
代码配置 | Transient/Singleton |
| RPC | gRPC | AddGrpcService(...)(含泛型基类 GrpcClientBase<TClient>) |
Healnet:gRPC |
Singleton |
| 可观测性 | Logging (Serilog) | UseHealnetSerilog() / AddSerilog(...) |
Healnet:Serilog |
Singleton |
| 可观测性 | OpenTelemetry | AddHealnetOpenTelemetry(...) |
代码配置 | Singleton |
| 可观测性 | HealthChecks | AddHealnetHealthChecks(...) + MapHealnetHealthChecks() |
代码配置 | Transient |
| 中间件 | Middleware | UseExceptionHandling() / UseRequestLogging() / UseCorrelationId() |
代码配置 | Transient |
| 容错 | Resilience | AddResilience(...) |
Healnet:Resilience |
Singleton |
| AOP 拦截器 | AOP | AddAspectCoreWithInterceptors() |
代码配置 | Scoped |
| API 文档 | Swagger | AddSwagger(...) + UseSwaggerWithUI()(4.0.36 起内置鉴权感知 security 与 Controller 默认错误响应两个 operation filter) |
Healnet:Swagger |
Singleton |
| 对象存储 | OSS | AddAliyunOssFileService(...) |
Healnet:FileStorage |
Singleton |
| AI 集成 | AI | AddAIServices(...) / AddMemoryServices(...) |
Healnet:AI |
Scoped/Singleton |
| 工作单元 | UnitOfWork | AddUnitOfWork() |
无 | Scoped |
| 聚合入口 | Extensions | AddHealnetInfrastructure(...) / AddHealnetCommonServices() / AddIdGenerator(...) |
代码配置 | 混合 |
通用入口
对于只需要常用基础服务的场景,提供了快捷注册入口:
// Program.cs
builder.Services.AddHealnetInfrastructure(options =>
{
options.HttpClientName = "default";
options.ConsulAddress = "http://consul.healnet.net:8500";
});
// 或仅基础通用服务(Serilog + 雪花 ID)
builder.Services.AddHealnetCommonServices();
快速上手调用案例
以下案例均以真实公开 API 为准(配套单元测试见 tests/Healnet.Tests)。
1. Swagger + gRPC 转码文档 + 文件上传调试(一个服务端到端)
// Program.cs —— 服务端
builder.Services.AddGrpcJsonTranscodingSupport(); // = AddGrpc().AddJsonTranscoding():proto google.api.http 注解自动映射 REST/JSON
builder.Services.AddSwagger(o =>
{
o.Title = "Healnet.Rpc.Identity";
o.EnableGrpcSupport = true; // gRPC 服务自动生成 Swagger 文档
});
var app = builder.Build();
app.UseSwaggerWithUI(); // UI + 按需聚合下游文档
app.MapGrpcService<MissionService>(); // gRPC 服务
app.MapGrpcFileUploadEndpoint(o => // 文件上传调试端点(4.0.23)
{
o.Pattern = "/file/upload";
o.MaxFileSizeBytes = 20 * 1024 * 1024; // 默认即 20MB
o.AllowedExtensions = [".pdf", ".xlsx"]; // 空集合 = 不限制
o.Handler = async (file, ct) =>
{
await using var fs = File.Create(Path.Combine("./uploads", file.FileName));
await file.CopyToAsync(fs, ct);
return new { saved = file.FileName }; // 放入 ApiResponse.Data
};
});
app.Run();
客户端消费(推荐继承 GrpcClientBase<TClient>,Consul 服务发现 + Channel 缓存 + 重试策略开箱即用):
using Healnet.Infrastructure.gRPC;
public class MissionClient : GrpcClientBase<OsAgentMissionService.OsAgentMissionServiceClient>
{
public MissionClient(IConsulService consul, GrpcChannelCache cache,
GrpcRetryPolicy retry, ILogger logger)
: base(consul, cache, retry, logger, "healnet.rpc.agentos") { } // Consul 服务名
protected override OsAgentMissionService.OsAgentMissionServiceClient CreateClient(GrpcChannel channel)
=> new(channel);
public async Task<MissionInfo?> GetMission(long id)
{
var client = await GetClientAsync(); // 服务不可用返回 null
if (client is null) return null;
var res = await client.getMissionAsync(new MissionRequest { MissionId = id });
return res.Data?.Unpack<MissionInfo>(); // 统一响应 Any 解包
}
}
更多客户端与多层嵌套消息构造案例:gRPC/README.md、Swagger/GRPC_EXAMPLE.md。
2. 缓存(ICacheService,Redis 实现)
using Healnet.Core.Interfaces; // 注入 ICacheService
await cache.SetAsync("product:42", product, TimeSpan.FromMinutes(10));
var product = await cache.GetAsync<Product>("product:42");
// 缓存旁路一步到位:未命中自动执行 factory 并回填
var user = await cache.GetOrSetAsync($"user:{id}",
async () => await db.Users.FindAsync(id), TimeSpan.FromMinutes(30));
await cache.RemoveAsync("product:42");
await cache.RemoveByPatternAsync("product:*"); // 批量清理
var (ok, val) = await cache.GetAndDeleteAsync<string>("once-key"); // 原子 GET+DEL
3. 消息发布(RabbitMQ)
using Healnet.Core.Interfaces; // 注入 IMessagePublisher
await publisher.PublishAsync("order.exchange", "order.created", new
{
OrderId = 1001,
Amount = 199.00m
}); // 交换机 + 路由键
await publisher.PublishAsync("order-queue", message); // 或直发队列
4. 日志配置解析(Healnet:Serilog 双形状兼容)
using Healnet.Infrastructure.Logging;
// 兼容 "MinimumLevel": "Warning"(标量)与 Serilog 原生 {"Default":..,"Override":{..}}(嵌套)两种形状
var options = LoggingExtensions.LoadLoggingOptions(builder.Configuration);
Console.WriteLine($"实际生效日志级别:{options.MinimumLevel}");
5. gRPC Swagger 文档行为(4.0.23 起与 proto3 JSON 对齐)
int64 字段 → Swagger 显示 string(proto3 JSON 64 位整数编码为字符串,"123")
bytes 字段 → Swagger 文本框 base64;大文件上传请用 MapGrpcFileUploadEndpoint(见案例 1)
流式方法 → ClientStreaming/DuplexStreaming 不再生成假端点;ServerStreaming 保留并标注
响应里的 Any → 文档显示 @type 提示(打包类型为运行时信息,静态文档无法展开)
详见 Swagger/README.md 与 Swagger/GRPC_EXAMPLE.md。
模块详细文档
每个模块均有独立的 README,详见:
| 模块 | 文档路径 |
|---|---|
| AI | AI/README.md |
| AOP | AOP/README.md |
| Authentication | Authentication/README.md |
| Consul | Consul/README.md |
| ElasticSearch | ElasticSearch/README.md |
| Milvus | Milvus/README.md |
| Extensions | Extensions/README.md |
| gRPC | gRPC/README.md |
| HealthChecks | HealthChecks/README.md |
| Http | Http/README.md |
| Logging (Serilog) | Logging/README.md |
| Middleware | Middleware/README.md |
| MongoDB | MongoDB/README.md |
| OpenTelemetry | OpenTelemetry/README.md |
| OSS | OSS/README.md |
| RabbitMQ | RabbitMQ/README.md |
| Redis | Redis/README.md |
| Resilience | Resilience/README.md |
| Security | Security/README.md |
| SqlSugar | SqlSugar/README.md |
| Swagger | Swagger/README.md |
| UnitOfWork | UnitOfWork/README.md |
| YARP | YARP/README.md |
架构与 DIP 状态
- 契约在 Core,实现在 Infrastructure:Redis 的
IRedisService : ICacheService(Core)、UnitOfWork 的ISqlSugarUnitOfWork : IUnitOfWork(Core)、认证/Consul/HTTP 的接口均在 Core,实现在本层。 - AI 彻底 DIP:
Core.AI仅含接口 + POCO(无 AI SDK 依赖),Infrastructure.AI提供实现并引用 Semantic Kernel / Microsoft.Extensions.AI。 - OSS 替换 Common 的 IFileService:
AddAliyunOssFileService通过services.Replace将IFileService解析切换为AliyunOssFileService。 - 过时中间件已标记 Obsolete:
ExceptionHandlingMiddleware/HealthCheckMiddleware推荐使用 Core 的ExceptionHandlerMiddleware与 ASP.NET 内置MapHealthChecks。 - API Key 认证:
ApiKeyAuthenticationHandler(位于Infrastructure.Authentication)支持CustomTokenResolver自定义 Token 解析与写入client_source=workbuddyclaim;其CustomTokenResolver委托在 4.0.0 起为异步Func<string,Task<ClaimsPrincipal?>>。
依赖关系(关键技术包)
Healnet.Infrastructure (net10.0)
├── Healnet.Core
├── Healnet.Common
├── StackExchange.Redis
├── MongoDB.Driver
├── SqlSugarCore (+ ClickHouseCore)
├── NEST (ElasticSearch)
├── Milvus.Client (向量数据库)
├── RabbitMQ.Client
├── Consul
├── Yarp.ReverseProxy
├── Grpc.AspNetCore
├── Serilog.AspNetCore
├── OpenTelemetry (Tracing + Metrics + Logging)
├── Polly
├── AspectCore
├── Swashbuckle
├── Microsoft.SemanticKernel
├── Microsoft.Extensions.AI (+ OpenAI)
├── Aliyun.OSS.SDK.NetCore
└── Microsoft.AspNetCore.Authentication.JwtBearer
近期变更
以下变更说明整理自
Healnet.Infrastructure.csproj的PackageReleaseNotes,具体日期以各发布记录为准(本文不杜撰日期)。
v4.0.38
- AI 服务生命周期缺陷修复(P0)+ DI 完整性守卫测试随批落地:
ICopilotClient由 Scoped 改 Singleton:IRAGService(Singleton,4.0.24 起)构造依赖它,两者组合自 4.0.24 起即非法——ValidateScopes/ValidateOnBuild(ASP.NET Core 在 Development 默认开启)下应用启动即抛 "Cannot consume scoped service 'ICopilotClient' from singleton 'IRAGService'"(生产不校验则单例隐式捕获一份永久化)。同时CopilotClient/MicrosoftAIClient是有状态实现(按 contextId 累积会话历史),Scoped 令每次请求丢历史、ClearContext形同虚设。- 新增
ChatSessionStore<THistory>(有界会话存储):会话数上限默认 1024、空闲 30 分钟淘汰、单会话消息数上限默认 100 裁剪最旧、同一 contextId 的并发调用以会话门串行化。淘汰策略:永不淘汰本次请求的会话(否则活跃但恰好最旧的会话会在自己的调用里历史归零);容量淘汰只在严格超过上限时触发(count == max是合法满载,用>=会让第 max 个会话入驻瞬间踢掉最旧者、容量上限形同虚设——评审实测抓到)。 - 新增
NullCopilotClient降级:未配置ApiKey时注册空实现(注册成功、调用 AI 时才抛带Healnet:AI:ApiKey配置指引的异常),容器图始终可解析——业务服务不再需要自行GetService判空 / try-catch 兜底(这正是每个新服务复制同一段防御代码的根因);其ClearContext为无副作用空操作。 RabbitMQService(Singleton)共享状态并发化:_handlers/_declaredExchanges/_declaredQueues由Dictionary/List改ConcurrentDictionary/ConcurrentBag(原无任何锁保护,并发订阅/声明存在竞态)。- 新增
DiCompletenessTests16 项守卫(这条链路此前在 146 单测 + 42 E2E 中零覆盖):ValidateOnBuild全 provider 容器校验、NullCopilotClient 降级语义、根容器单例断言、跨调用历史保留 /ClearContext生效 / 单会话裁剪 / 容量淘汰、同 contextId 并发串行化、RabbitMQ 并发容器反射断言。QA 变异证伪:改回AddScoped被 8 项抓住、移除 Null 兜底被 4 项抓住。 - 兼容性:DI 行为变化仅
ICopilotClientSingleton 化与未配置降级;直接new CopilotClient(...)/new MicrosoftAIClient(...)的使用方签名不变(新增可选参数)。
v4.0.37
- 开启 XML 文档生成 + 修净全部注释格式告警(只动注释与项目属性,IL 无变化):
- 包消费方终于有 IntelliSense 注释:此前本包不产
Healnet.Infrastructure.xml,引用SwaggerOptions、GrpcChannelCache、AddHealnetInfrastructure、IMilvusService等公开类型时编辑器里没有任何说明。现随包提供lib/net10.0/Healnet.Infrastructure.xml(991 个 member 条目),服务端模板的复制 Target 会把 Core 与 Infra 两个 XML 一起带到 build / publish 输出。 - 修净 12 条 CS1570(注释里未转义的泛型尖括号,XML 非法 → 编译器丢弃该成员整段文档)、7 条 CS1573(param 缺项:
AddHealnetHealthChecks两个重载的milvusEnabled、IMilvusService四个方法的cancellationToken、MilvusField.FloatVector的description)、2 条 CS1587、1 条 CS0419(IEntityMapper的cref指向AddSqlSugar重载不明确,改为带完整签名的cref)。 - 一处结构性错位:私有常量
ConsulOperationTimeoutMs的<summary>被夹在AddConsulHttpClient的<example><code>块内部,导致 example 永不闭合、</example>落到另一个私有方法之后(连带 2 条 CS1570)——已把该文档块移回AddConsulHttpClient上方;SqlSugarExtensions把<summary>写在ConfigureExternalServices的 lambda 赋值上(非合法文档目标),改为普通注释。 NoWarn只保留 1591:公开面较大、补全缺注释不在本批范围;其余注释类告警不再抑制。- 顺带补全
SwaggerOptions(类 + 12 个属性)与HeaderParameter的注释——消费方最常查的配置类型,此前只有 4.0.36 新增的两个开关有说明。 - 依赖 Core 4.0.14。
- 包消费方终于有 IntelliSense 注释:此前本包不产
v4.0.36
- Swagger 文档三项 P3 修复(只影响文档生成,运行时行为不变):
- 鉴权声明改为逐操作:移除
c.AddSecurityRequirement的「全局安全要求」——它让文档里每个操作都带security,Swagger UI 给所有端点画锁图标,包括模板中完全匿名的GET /api/products(读者会误以为必须带 token)。改由新增的AuthorizeAwareSecurityOperationFilter : IOperationFilter判定:方法或其 Controller 上存在[Authorize](IAuthorizeData)且任意一处都没有[AllowAnonymous](IAllowAnonymous,优先于 Authorize)时才写 security;判定来源取ActionDescriptor.EndpointMetadata∪ 反射(MethodInfo/ControllerTypeInfo,inherit: true),兼顾 MVC 与 Minimal API/手工描述符。 - Controller 默认错误响应:新增
DefaultResponsesOperationFilter : IOperationFilter,补齐框架实际会返回的 400/401/403/404/500(Swashbuckle 默认只生成 200),错误体经ISchemaGenerator生成$ref指向非泛型ApiResponse(避免悬空引用、兼容CustomSchemaIds);401 只写 description 不挂 body。已声明同名状态码的操作(手写[ProducesResponseType])与 gRPC 转码端点(/api/grpc前缀或GrpcTag分组,错误响应已由GrpcSwaggerDocumentFilter声明)自动跳过。 - 两个开关:
SwaggerOptions.DeclareAuthOnEndpoints/DeclareDefaultResponses,默认true;置false可回退到「全局 security」/「只声明 200」的旧行为。两条配置绑定路径(AddSwagger(IConfiguration)、AddSwaggerWithGrpcSupport(configuration, ...))一并透传,避免配置里关开关静默失效。 - XML 注释文件选取收窄:
GetXmlCommentPaths原把输出目录所有*.xml全喂给IncludeXmlComments,模板开启CopyDocumentationFilesFromPackages后要解析 109 个 / 46MB 三方包文档;现只选Healnet.*.xml(框架契约)+ 入口程序集同名.xml(业务 DTO),实测模板输出目录降为 1 个 / 约 150KB。显式配置XmlCommentPaths时完全采用配置值、不再叠加自动扫描;扫描结果按进程Lazy缓存,避免AddSwagger多次调用重复扫盘。 - 依赖 Core 4.0.13(
ApiResponse语义注释 + XML 文档生成,schema description 才有内容)。详见 Swagger/README.md。
- 鉴权声明改为逐操作:移除
v4.0.35 – v4.0.25(本次补载,此前 README 缺这段历史)
同为本仓库
PackageReleaseNotes的整理,4.0.32 / 4.0.33 在 ReleaseNotes 中无条目(未发布或未记录)。
v4.0.35
- 依赖升级至 Healnet.Core 4.0.12:核查发现 4.0.34 打包时依赖 Core 4.0.11,而 4.0.11 的线上包缺少
ExceptionHandlerMiddleware的「响应已开始写出」保护(修复晚于该版本打包时刻),只升本包会拿到不含修复的 Core;故 Core 补发 4.0.12、本包随之升 4.0.35。本包自身代码与 4.0.34 完全一致。
v4.0.34
- 模型校验统一响应(兼容性接管,默认关闭):此前
[ApiController]的模型校验失败返回 RFC7807ProblemDetails(HTTP 400),而框架其余错误统一走ApiResponse,同一个「字段必填」错误存在两套 body 形状。新增AddHealnetUnifiedValidationResponse扩展与Healnet:Validation配置节(UnifyResponse/HttpStatusCode/ErrorCode/Message/IncludeFieldErrors/ForceJsonContentType)。UnifyResponse=false(默认)时不注册任何 MVC 行为,老调用方零影响;置true后校验失败返回ApiResponse(字段明细放data,结构与ProblemDetails.errors同构)。HttpStatusCode默认仍 400,可配 200 完全对齐「HTTP 恒 200、错误看 body.code」的惯例。 - 另:E2E 用的
ServiceProcess增加extraEnv参数(验证开关两态);模板文档警示不支持给 record 主构造参数加[property: Required](ASP.NET Core 会抛InvalidOperationException,导致该接口所有 POST 变 500)。
v4.0.31
- SqlSugar 多库默认连接名不一致(P1):构造已按「连接配置首个
ConfigId」绑定 Context,但GetClient及全部 CRUD 方法默认参数恒为字面量Default——多库配置首连接名非Default(如Master)时所有增删改查走GetConnectionScope(Default)抛「未找到 ConfigId」。现统一在GetClient入口做别名映射,方法签名保持兼容。 GetSingleAsync/GetFirstAsync改为下推 SQL 的SingleAsync/FirstAsync(原ToListAsync()后内存筛选,会把整表拉进内存)。ServiceDefaults的 Swagger 生产门禁缺失:文档声明生产默认不挂载但实现未校验AllowNonDevelopment,现与网关门禁对齐。
v4.0.30
- gRPC 注解路由解析改强类型实现,新增依赖
Google.Protobuf3.27.2 与Google.Api.CommonProtos2.15.0。4.0.29 的基类链Descriptor查找仍不够:MethodDescriptor根本没有Options属性(旧反射链在此恒断),Extensions.ContainsField的 API 形状也不存在,导致google.api.http注解永远读不到。现改为ServiceDescriptor.Methods→MethodDescriptor.CustomOptions.TryGetMessage(72295728)读取Google.Api.HttpRule,按PatternCase成对取路径与 HTTP 方法,消除全部脆弱反射。
v4.0.29
- 修复 gRPC 转码端点 Swagger 文档路由与真实路由不一致(P1,E2E 实测发现):
GetHttpRouteFromProtoDescriptor的静态Descriptor查找只回溯实现类及其DeclaringType,而 grpc-dotnet 生成结构中Descriptor位于基类链上某一层的DeclaringType,导致注解路由永远解析不到、Swagger 退化为合成回退路由(/grpc/{service}/{method},Try it out 必 404)。现沿整条基类链及其DeclaringType查找。 - 另修模板
ProductsController实例字段保存跨请求状态的缺陷(改为 SingletonInMemoryProductStore注入)。
v4.0.28
- 修复 gRPC 服务类型识别规则在 grpc-dotnet 生成结构下恒不命中(自动探测/自动映射与 Swagger 自动发现均受影响):原判定为「基类链上存在名以
Base结尾且有BindService方法」,但实际结构是服务实现类继承嵌套抽象类,BindService定义在外层静态类上而非基类链上。现改为:基类链上存在名以Base结尾的类型,且该类型自身或其DeclaringType上存在BindService。判定按方法名而非GetMethod精确签名(BindService有两个重载,原写法触发AmbiguousMatchException启动崩溃)。
v4.0.27
- 修复 4.0.26 Service Defaults 三处缺陷(发布后经三模板实机启动验证发现):①
MapAllDiscoveredGrpcServices程序集排除规则误伤业务程序集(原笼统排除Healnet开头的程序集,导致Healnet.Example.*的 gRPC 服务不被自动映射),改为精确排除Healnet.Common/Healnet.Core/Healnet.Infrastructure并补mscorlib/netstandard。②UseHealnetServiceDefaults与AddHealnetServiceDefaults能力开关脱节:Use 侧从配置新建 options(默认全 false),Add 侧的自动探测结果丢失;现 Add 侧把探测后的 options 写入 DI,Use 侧优先取 DI 实例,配置节点仍可覆盖。③ Kestrel 双端口策略修正并显式告警:明文端口的Http1AndHttp2无 TLS 时 HTTP/2 被禁用(连接退化为 HTTP/1.1),此前「HTTP 与 gRPC 同端口共存」的原生 gRPC 调用实际不可用;现EnableGrpc且GrpcPort异于ServicePort时双端口,GrpcPort缺失或同端口则记 Error 提示补配置。
v4.0.26
- 新增服务托管默认值 Service Defaults(
HealnetServiceDefaultsExtensions),框架样板全收编,业务Program.cs只需两行。AddHealnetServiceDefaults(WebApplicationBuilder)自动完成 Serilog、Consul 客户端、gRPC JSON 转码 + 统一异常拦截器、MVC、JWT、健康检查、Swagger(含 gRPC 文档自动发现)、Kestrel 端口策略;能力开关默认自动探测入口程序集(存在 Controller 则启用 MVC、存在 gRPC 服务类则启用 gRPC),可经HealnetServiceDefaultsOptions显式覆盖。UseHealnetServiceDefaults(WebApplication)按生产顺序挂载请求日志(/health降噪)、全局异常、CorrelationId、认证授权、/health、MapControllers、自动发现并映射 gRPC 服务、Swagger UI、Consul 注册。 - 注意:自动映射后不要再手动
MapGrpcService<T>()(重复映射抛端点重复异常)。
v4.0.25
- gRPC 统一封装(业务服务开箱即得):①
AddGrpcJsonTranscodingSupport现已默认注册HealnetGrpcExceptionInterceptor(此前该拦截器为孤儿封装、全库无注册点,业务抛HealnetException/BusinessException会落入Unknown/500);现自动转 gRPC Status(trailers 携带x-error-code/x-error-retryable),转码端点的 HTTP 状态码随之正确映射(NotFound→404、InvalidArgument→400、PermissionDenied→403、ResourceExhausted→429、DeadlineExceeded→504、Unauthenticated→401)。②GrpcClientBase新增统一入口ExecuteAsync:自动构建调用 metadata(x-correlation-id取自当前Activity.TraceId;ForwardAuthorization=true时透传调用方 Authorization),并把RpcException还原为HealnetException(服务端同为 Healnet 框架时按 trailersx-error-code精确还原,否则按 StatusCode 退化映射;Unavailable→503 可重试)。新增GrpcExceptionMapper。③ 构造新增可选参数IHttpContextAccessor(默认 null),既有子类无需改动。
v4.0.24
- 代码审查缺陷批量修复(正确性/安全/资源管理,17 项):
- gRPC:
GrpcChannelCache健康检查失败的坏 Channel 先从缓存移除再重建(原GetOrCreate对已存在 key 直接返回旧坏实例,坏 Channel 永不重建);凭据按 URL scheme 选择(https→SecureSsl,原硬编码 Insecure 导致 https 目标握手失败)。 - RabbitMQ:生产端
BasicPublishBatch/BasicPublish加锁串行化(共享单例 channel 非线程安全);消费端改AsyncEventingBasicConsumer(原EventingBasicConsumerasync void 事件异常崩进程);失败路径 republish 携带x-retry-count=原值+1header 的新消息并 Ack 原消息(原只读不写计数、Nack 重回队列 header 不变,毒消息无限循环),超过MaxRetryCount(默认 3,新公开属性)转BasicNack(requeue:false)交 broker 端 DLX;DLQ 发布成功才 Ack,否则 Nack 交 broker,不再静默丢消息;Subscribe(queue, routingKey, handler)重载标记 Obsolete(该重载错误地将 routingKey 用作 exchange 名称),5.0 移除。 - 分布式锁:
DistributedLockService锁值字典改TryAdd语义(原索引器赋值覆盖同键旧 token);释放时本地无 token 不再KeyDelete兜底(防误删其他实例持有的锁),仅告警。 - AI/Memory:
MemoryOrchestratorL2 持久化失败如实返回l2Persisted=false;TurnNo 改 COUNT 聚合查询(原拉 10 万条仅为计数);Milvusdomain白名单校验(字母数字下划线中划线,防表达式注入);ConversationRepository配合 Core 4.0.11 契约(Save* 失败返回 false +CountTurnsAsync);RAGService注册改 AddSingleton(原 Scoped 每请求拿到空知识库);AgentContextManager加 TTL 惰性清理(1 小时,原只增不减内存泄漏);AgentService.ExecuteAsync成功完成后调用DeleteContextAsync;AiMemoryEntityMapper补ConversationArchive.Id主键/自增映射(原空分支,CodeFirst 建表缺主键)。 - HTTP:
HttpClientService全部方法(POST/PUT/DELETE/PATCH/表单/下载/带状态码变体)统一经BuildRequestUrlAsync走 Consul 服务发现(原仅 GET 走,Consul 模式下其余方法相对路径请求必然失败)。 - SqlSugar:
SqlSugarRepository默认连接名取连接配置首个ConfigId(原硬编码 "Default",多库首连接名非 Default 时绑错库),取不到回退 "Default";OnLogExecuting不再拼接 SQL 参数值(防手机号/金额等敏感数据泄漏进日志)。 - 弹性:
ResiliencePolicyProvider重试策略补TaskCanceledException覆盖(HttpClient 超时表现为该异常,原不重试),并排除外部取消。 - Redis:
GetAsync区分 JsonException(Warning,视为未命中)与连接类异常(Error);仍返回 default,兼容性不变。 - 安全:
ApiKeyAuthenticationHandlerAPI Key 比对改CryptographicOperations.FixedTimeEquals恒定时间比较(防时序侧信道)。 - Consul:
ConsulService实现IDisposable释放ConsulClient(单例下原 HTTP 连接永不释放)。 - 已知限制:全部契约接口补 CancellationToken 归 5.0;RedisShortTermMemoryStore 读改写改 Redis List 需数据迁移。
- gRPC:
v4.0.23
- 修复 Swagger gRPC 封装 7 项缺陷(QA 团队双通道验证后实施):
GetMethodType流式识别失效(原对Task\1泛型定义名做 Contains 恒不命中,流式方法全误判 Unary);跳过 ClientStreaming/DuplexStreaming 假端点(JSON Transcoding 不支持、经 HTTP 调用必然 404),ServerStreaming 标注流式响应;int64/uint64 Schema 对齐 proto3 JSON(→ string+int64/uint64,补 uint→integer/int32);Any/Struct/Value 等 WellKnownTypes 逐一映射(Any 带 @type 提示);嵌套超 10 层截断显式标注;_currentDoc==null` 悬空 $ref 改内联;ByteString 补 format:byte。详见 Swagger/README.md。 - 新增 gRPC 客户端调用文档:多层嵌套消息构造、repeated/map、服务端流式、Swagger 侧 JSON 请求体对照,见 Swagger/GRPC_EXAMPLE.md 客户端调用示例章节。
- 新增文件上传调试端点模板:
MapGrpcFileUploadEndpoint(Swagger/GrpcFileUploadExtensions.cs)在 gRPC 服务同进程映射 multipart/form-data 上传端点(可配路由/大小上限/扩展名白名单/业务 Handler),返回统一 ApiResponse,Swagger UI 独立分组展示并支持文件选择框上传调试。
v4.0.13
- gRPC 服务发现遗留回退:
ResolveAllGrpcAddressesAsync支持遗留回退——实例无grpc-portMeta 但 Tags 含GrpcLegacyFallbackTag(默认server,需Healnet:Consul:GrpcLegacyTagFallback=true开启)时,回退使用Address:Port作为 gRPC 地址,用于与历史 .NET Core 3.1 MicroCloud 框架注册的 gRPC 服务(不带 Meta、主端口即 h2c)互通;fail-fast 报错文案同步更新。默认关闭,行为完全向后兼容。
v4.0.12
- 修复 RabbitMQ channel 泄漏:此前
IRabbitMQService注册为 Scoped,构造中connection.CreateModel()分配 channel 且RabbitMQService未实现IDisposable,长期运行后 channel 只增不减触顶 2047 上限抛出ChannelAllocationException,拖垮依赖 MQ 的服务。现改为AddSingleton(全进程复用 1 个 channel),RabbitMQService实现IDisposable(关闭时回收 channel)并对共享 channel 的发布/确认路径加锁保证并发安全。无公开 API 签名变更,行为对既有调用方兼容。
v4.0.6
- 三库版本重新对齐至统一 4.0.6(消除版本分叉,无新增代码变更/公开 API 变更,源码与 4.0.5 一致)。
v4.0.2
- GrpcChannelCache 的 HttpClient.Timeout 改为可配置:配置键
Grpc:HttpClientTimeoutSeconds,默认 100s(与 .NET 默认一致)。此前未显式设置导致长 deadline(如编排层DownstreamTimeoutSeconds=160)被 100s 硬截断。无公开 API 签名变更。
v4.0.1
- 修复
AddMemoryServices无条件注册 L4 归档服务的缺陷:此前IArchiveRepository/IArchiveService恒被注册,而ArchiveService硬依赖IFileService,导致未注册文件存储的使用方即便EnableArchive=false也会在 DI 解析MemoryOrchestrator时抛异常。现改为仅当MemoryOptions.EnableArchive为true时才注册,并在开启归档却缺失IFileService时抛出带注册指引的InvalidOperationException。无公开 API 签名变更。
v4.0.0
- 破坏性主版本(破坏性变更来自 Healnet.Core):
RedisService实现ICacheService.GetAndDeleteAsync<T>(Core 4.0.0 新增,对实现者破坏性,框架已实现)。
历史背景(2.x)
- v2.0.6:SqlSugar
IEntityMapper可插拔化(AiMemoryEntityMapper框架级默认 +BusinessConfigEntityMapper产品级);BusinessConfig 全套迁出至业务项目Healnet.BusinessConfig;EntityService的PropertyInfo-vs-Type比较 bug 修复;新增 Milvus 向量数据库封装(Milvus/模块,AddMilvus(...),依赖Milvus.Client2.3.0-preview.1)。 - v2.0.5:三库版本对齐为 2.0.5。
- v2.0.4:AI 层彻底 DIP(契约上提
Core.AI,Fluent 映射ConversationSession/ConversationTurn去除[SugarTable],ChatContext去 Semantic Kernel 依赖);代码审查清理修复 12 个编译警告;三库均为 2.0.4。
许可证
MIT License
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Aliyun.OSS.SDK.NetCore (>= 2.14.0)
- AspectCore.Core (>= 2.4.0)
- AspectCore.Extensions.DependencyInjection (>= 2.4.0)
- Azure.AI.OpenAI (>= 2.9.0-beta.1)
- Consul (>= 1.7.14.1)
- Google.Api.CommonProtos (>= 2.15.0)
- Google.Protobuf (>= 3.27.2)
- Grpc.AspNetCore (>= 2.65.0)
- Grpc.Net.Client (>= 2.65.0)
- Healnet.Common (>= 4.0.10)
- Healnet.Core (>= 4.0.14)
- Microsoft.AspNetCore.Authentication.JwtBearer (>= 8.0.0)
- Microsoft.AspNetCore.Grpc.JsonTranscoding (>= 8.0.0)
- Microsoft.Extensions.AI (>= 9.10.0)
- Microsoft.Extensions.AI.OpenAI (>= 10.3.0)
- Microsoft.Extensions.Caching.Memory (>= 10.0.2)
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.2)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.2)
- Microsoft.Extensions.Diagnostics.HealthChecks (>= 10.0.2)
- Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions (>= 10.0.2)
- Microsoft.Extensions.Http.Polly (>= 8.0.0)
- Microsoft.Extensions.Logging (>= 10.0.2)
- Microsoft.SemanticKernel (>= 1.67.0)
- Milvus.Client (>= 2.3.0-preview.1)
- MongoDB.Driver (>= 2.29.0)
- NEST (>= 7.17.0)
- OpenTelemetry.Exporter.Console (>= 1.7.0)
- OpenTelemetry.Exporter.Jaeger (>= 1.6.0-rc.1)
- OpenTelemetry.Exporter.OpenTelemetryProtocol (>= 1.7.0)
- OpenTelemetry.Exporter.Zipkin (>= 1.7.0)
- OpenTelemetry.Extensions.Hosting (>= 1.7.0)
- OpenTelemetry.Instrumentation.AspNetCore (>= 1.7.0)
- OpenTelemetry.Instrumentation.Http (>= 1.7.0)
- Oracle.ManagedDataAccess.Core (>= 23.8.0)
- Polly (>= 8.3.0)
- Polly.RateLimiting (>= 8.0.0)
- RabbitMQ.Client (>= 6.8.0)
- Serilog.AspNetCore (>= 8.0.0)
- Serilog.Extensions.Hosting (>= 8.0.0)
- Serilog.Sinks.Console (>= 6.0.0)
- Serilog.Sinks.File (>= 5.0.0)
- Serilog.Sinks.OpenSearch (>= 1.2.0)
- SqlSugar.ClickHouseCore (>= 5.1.4.1)
- SqlSugarCore (>= 5.1.4.215)
- StackExchange.Redis (>= 2.9.11)
- Swashbuckle.AspNetCore.SwaggerGen (>= 6.5.0)
- Swashbuckle.AspNetCore.SwaggerUI (>= 6.5.0)
- System.DirectoryServices.Protocols (>= 8.0.0)
- System.Drawing.Common (>= 8.0.0)
- System.IdentityModel.Tokens.Jwt (>= 8.0.0)
- System.Interactive.Async (>= 6.0.1)
- Yarp.ReverseProxy (>= 2.2.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 4.0.38 | 4 | 11/3/2026 |
| 4.0.37 | 30 | 11/3/2026 |
| 4.0.36 | 35 | 11/3/2026 |
| 4.0.35 | 37 | 11/3/2026 |
| 4.0.34 | 32 | 11/3/2026 |
| 4.0.33 | 37 | 11/3/2026 |
| 4.0.32 | 32 | 11/3/2026 |
| 4.0.31 | 36 | 11/3/2026 |
| 4.0.30 | 35 | 11/3/2026 |
| 4.0.27 | 33 | 11/3/2026 |
| 4.0.26 | 32 | 11/3/2026 |
| 4.0.25 | 34 | 11/3/2026 |
| 4.0.24 | 36 | 11/3/2026 |
| 4.0.22 | 70 | 10/29/2026 |
| 4.0.21 | 59 | 10/29/2026 |
| 4.0.20 | 56 | 10/29/2026 |
| 4.0.19 | 61 | 10/29/2026 |
| 4.0.18 | 62 | 10/28/2026 |
| 4.0.17 | 73 | 10/14/2026 |
| 4.0.16 | 63 | 10/12/2026 |
v4.0.38: AI 服务生命周期缺陷修复(P0)+ DI 完整性守卫测试随批落地。① ICopilotClient 注册由 Scoped 改为 Singleton:IRAGService(Singleton,4.0.24 起)构造依赖它,两者组合自 4.0.24 起即非法——ValidateScopes/ValidateOnBuild(ASP.NET Core 在 Development 默认开启)下应用启动即抛 "Cannot consume scoped service 'ICopilotClient' from singleton 'IRAGService'"(生产不校验则单例隐式捕获一份永久化);且 CopilotClient/MicrosoftAIClient 是有状态实现(按 contextId 累积会话历史),Scoped 令每次请求丢历史、ClearContext 形同虚设。② 有状态单例必须有界:新增 ChatSessionStore<THistory>(会话数上限默认 1024、空闲 30 分钟淘汰、单会话消息数上限默认 100 裁剪最旧、同一 contextId 并发调用以会话门串行化);淘汰永不踢掉本次请求的会话(否则活跃但恰好最旧的会话会在自己的调用里历史归零);容量淘汰只在严格超过上限时触发(count == max 为合法满载)。③ 未配置 ApiKey 时降级注册 NullCopilotClient:容器图始终可解析、应用可正常启动,真正调用 AI 时才抛带配置指引的 InvalidOperationException(含 Healnet:AI:ApiKey 配置提示)——业务服务不再需要自行 GetService 判空 / try-catch 兜底(这正是每个新服务复制同一段防御代码的根因);其 ClearContext 为无副作用空操作。④ RabbitMQService(Singleton)的可变共享状态 _handlers/_declaredExchanges/_declaredQueues 由 Dictionary/List 改为 ConcurrentDictionary/ConcurrentBag(原无任何锁保护,并发订阅/声明存在竞态)。⑤ 新增 DiCompletenessTests 16 项守卫测试:ValidateOnBuild 全 provider 容器校验、NullCopilotClient 降级语义、根容器单例断言、跨调用历史保留 / ClearContext 生效 / 单会话裁剪 / 容量淘汰、同 contextId 并发串行化、RabbitMQ 并发容器反射断言。QA 变异证伪:改回 AddScoped 被 8 项抓住、移除 Null 兜底被 4 项抓住。兼容性:DI 行为变化仅 ICopilotClient Singleton 化与未配置降级;直接 new CopilotClient/MicrosoftAIClient 的使用方签名不变(新增可选参数)。依赖 Core 4.0.14。 v4.0.37: 开启 XML 注释文档生成 + 修净本包全部 XML 注释格式告警(只动注释与项目属性,IL 无变化)。① 此前本包不产 Healnet.Infrastructure.xml,包消费方引用 SwaggerOptions / GrpcChannelCache / AddHealnetInfrastructure / IMilvusService 等公开类型时【没有任何 IntelliSense 注释】;现启用 GenerateDocumentationFile,随包提供 lib/net10.0/Healnet.Infrastructure.xml(实测 974 个 member 条目),服务端模板的 XML 复制 Target 会一并带上(Swagger 里 gRPC/Consul/SqlSugar 等类型也有了描述)。② 修净 12 条 CS1570(未转义泛型尖括号)、7 条 CS1573(param 缺项:AddHealnetHealthChecks 两个重载的 milvusEnabled、IMilvusService 四个方法的 cancellationToken、MilvusField.FloatVector 的 description)、2 条 CS1587、1 条 CS0419(IEntityMapper 的 cref 指向 AddSqlSugar 重载不明确,改为带完整签名的 cref)。③ 一处结构性错位:私有常量 ConsulOperationTimeoutMs 的 summary 被夹在 AddConsulHttpClient 的 example/code 块内部,使 example 永不闭合、</example> 落到另一个私有方法之后(连带 2 条 CS1570)——已把该文档块移回 AddConsulHttpClient 上方;SqlSugarExtensions 把 summary 写在 ConfigureExternalServices 的 lambda 赋值上(非合法文档目标),改为普通注释。④ NoWarn 只保留 1591(公开面较大,补全缺注释不在本批范围);其余 XML 注释类告警不再抑制,将来格式错误会重新告警而非静默丢文档。依赖 Core 4.0.14。 v4.0.36: Swagger 文档三项 P3 修复(只影响文档生成,运行时行为不变)。① 移除 c.AddSecurityRequirement 的「全局安全要求」:它让文档里每个操作都带 security,Swagger UI 给所有端点画锁图标——包括模板中完全匿名的 GET /api/products(读者会误以为必须带 token)。改由新增的 AuthorizeAwareSecurityOperationFilter 逐操作声明:方法或其 Controller 上存在 [Authorize](IAuthorizeData)且任意一处都没有 [AllowAnonymous](IAllowAnonymous,优先于 Authorize)时才写 security;判定来源取 ActionDescriptor.EndpointMetadata ∪ 反射(MethodInfo/ControllerTypeInfo,inherit: true),兼顾 MVC 与 Minimal API/手工描述符。② 新增 DefaultResponsesOperationFilter,为 Controller 端点补齐框架实际会返回的 400/401/403/404/500(Swashbuckle 默认只生成 200),错误体经 ISchemaGenerator 生成 $ref 指向非泛型 ApiResponse(避免悬空引用、兼容 CustomSchemaIds);已声明同名状态码的操作(手写 [ProducesResponseType])与 gRPC 转码端点(/api/grpc 前缀或 GrpcTag 分组,其错误响应已由 GrpcSwaggerDocumentFilter 声明)自动跳过。③ 两个过滤器各由 SwaggerOptions 开关控制:DeclareAuthOnEndpoints / DeclareDefaultResponses(默认 true;置 false 可回退到「全局 security」/「只声明 200」的旧行为),并已在这两处配置绑定路径(AddSwagger(IConfiguration)、AddSwaggerWithGrpcSupport(configuration, ...))一并透传,避免配置里关开关静默失效。依赖 Core 4.0.13(ApiResponse 语义注释 + XML 文档生成,schema description 才有内容)。 ④ QA 复验修复:GetXmlCommentPaths 原把输出目录【所有】*.xml 全喂给 IncludeXmlComments,模板开启 CopyDocumentationFilesFromPackages 后要解析 100+ 个三方包文档(109 个 / 46MB),swagger.json 首/次耗时 0.354s→1.012s、0.124s→1.196s;现只选 Healnet.*.xml(框架契约)+ 入口程序集同名 .xml(业务 DTO),实测回到 0.159s / 0.035s,且不影响显式配置的 XmlCommentPaths(仍优先)。 ⑤ 同批加固(主理人返工要求):① 显式配置 XmlCommentPaths 时【完全采用配置值】、不再叠加自动扫描(否则「想收窄范围」的意图会被自动扫描打破);② 输出目录扫描结果按进程 Lazy 缓存,避免 AddSwagger 多次调用重复扫盘;③ 被服务端模板改为用 GeneratePathProperty 从 NuGet 包目录定向复制 Healnet.Core.xml(模板输出目录 xml 从 109 个 / 46MB 降到 1 个 / 152KB)。 v4.0.35: 依赖升级至 Healnet.Core 4.0.12。核查发现 4.0.34 打包时依赖 Core 4.0.11,而 4.0.11 的线上包缺少 ExceptionHandlerMiddleware 的「响应已开始写出」保护(该修复晚于 4.0.11 打包时刻),只升本包会拿到不含修复的 Core;故 Core 补发 4.0.12、本包随之升 4.0.35 以保证依赖解析到修复版本。本包自身代码与 4.0.34 完全一致(模型校验统一响应等能力不变)。 v4.0.34: 模型校验统一响应(兼容性接管,默认关闭)。此前 [ApiController] 的模型校验失败返回 RFC7807 ProblemDetails(HTTP 400),而框架其余错误统一走 ApiResponse,同一个「字段必填」错误存在两套 body 形状,调用方要写两份解析逻辑。现提供可选统一:新增 AddHealnetUnifiedValidationResponse 扩展与 Healnet:Validation 配置节(UnifyResponse / HttpStatusCode / ErrorCode / Message / IncludeFieldErrors / ForceJsonContentType)。UnifyResponse=false(默认)时不注册任何 MVC 行为,老调用方零影响;置 true 后校验失败返回 ApiResponse(code=400、success=false、字段明细放 data,结构与 ProblemDetails.errors 同构,前端把 body.errors.Name 改成 body.data.Name 即可迁移)。HttpStatusCode 默认仍 400(既有「看到 400 走失败分支」的判断不用改),可配 200 完全对齐 ApiResponse「HTTP 恒 200、错误看 body.code」的惯例;ForceJsonContentType 默认 true,避免项目注册 XML formatter 后同一响应因 Accept 不同而形状漂移。另在模板文档中警示:不支持给 record 主构造参数加 [property: Required](ASP.NET Core 会抛 InvalidOperationException,导致该接口所有 POST 变 500)。另供 E2E 使用的 ServiceProcess 增加 extraEnv 参数(验证开关两态),withGrpc=false 时显式关闭 GrpcPort 避免撞本机端口。 v4.0.31: 代码审查修复 3 项。① SqlSugarRepository 多库默认连接名不一致(P1):构造已按「连接配置首个 ConfigId」绑定 Context,但 GetClient 及全部 CRUD 方法默认参数恒为字面量 Default——多库配置首连接名非 Default(如 Master)时所有增删改查走 GetConnectionScope(Default) 抛「未找到 ConfigId」。现统一在 GetClient 入口做别名映射(请求 Default 且真实默认名不同时改用真实名),方法签名保持兼容。② GetSingleAsync/GetFirstAsync 性能缺陷:原实现 ToListAsync() 后 SingleOrDefault/FirstOrDefault 会把整表拉进内存,改为下推 SQL 的 SingleAsync/FirstAsync。③ ExceptionHandlerMiddleware 响应已开始写出时(SSE/大文件下载中途失败)再设 ContentType/StatusCode 会抛 InvalidOperationException 导致中间件自身崩溃,补 response.HasStarted 保护。另修复 ServiceDefaults 的 Swagger 生产门禁缺失(文档声明生产默认不挂载但实现未校验 AllowNonDevelopment,与网关门禁对齐,非 Development 环境需显式开启才挂载)。 v4.0.30: 彻底重写 gRPC 注解路由解析为强类型实现并新增依赖 Google.Protobuf 3.27.2 与 Google.Api.CommonProtos 2.15.0。4.0.29 的基类链 Descriptor 查找修复仍不够:MethodDescriptor 根本没有名为 Options 的属性(旧反射链在此恒断),且 Extensions.ContainsField API 形状也不存在,导致 google.api.http 注解永远读不到。现改为强类型:ServiceDescriptor.Methods → MethodDescriptor.CustomOptions.TryGetMessage(72295728) 读取 Google.Api.HttpRule,按 PatternCase(Get/Post/Put/Delete/Patch/Custom)成对取路径与 HTTP 方法,消除全部脆弱反射。 v4.0.29: 修复 gRPC 转码端点 Swagger 文档路由与真实路由不一致(P1,E2E 实测发现)。GetHttpRouteFromProtoDescriptor 的静态 Descriptor 查找只回溯实现类及其 DeclaringType,而 grpc-dotnet 生成结构中 Descriptor 位于基类链上某一层的 DeclaringType(实现类 UserGrpcService → 基类 UserGrpc.UserGrpcBase → 外层容器类 UserGrpc),导致 google.api.http 注解路由永远解析不到,Swagger 退化为合成回退路由(/grpc/{service}/{method},Try it out 必 404)。现沿整条基类链及其 DeclaringType 查找。另修复模板 ProductsController 实例字段保存跨请求状态缺陷(Controller 每请求新建,Create 后 Get 必 404),改为 Singleton InMemoryProductStore 注入。 v4.0.28: 修复 gRPC 服务类型识别规则在 grpc-dotnet 生成结构下恒不命中的缺陷(4.0.26 引入的自动探测/自动映射与 Swagger 自动发现均受影响)。此前 IsGrpcService 判定为'基类链上存在名以 Base 结尾且有 BindService 方法',但 grpc-dotnet 实际生成的结构是:服务实现类继承嵌套抽象类(如 UserGrpc.UserGrpcBase),BindService 定义在外层静态类(UserGrpc)上而非基类链上——导致自动探测 EnableGrpc 恒 false(ServiceDefaults 不启用 gRPC)、Swagger 自动发现也恒为空(此前未暴露是因为业务均显式传入服务类型)。现识别规则改为:基类链上存在名以 Base 结尾的类型,且该类型自身或其 DeclaringType(外层静态类)上存在 BindService 方法。ServiceDefaultsExtensions.IsGrpcServiceType 与 GrpcServiceScanner.IsGrpcService 同步修复。注意 BindService 在外层类上存在两个重载(BindService(base) / BindService(base, builder)),判定按方法名而非 GetMethod 精确签名(原写法触发 AmbiguousMatchException 启动崩溃)。 v4.0.27: 修复 4.0.26 Service Defaults 三处缺陷(发布后经三模板实机启动验证发现)。① MapAllDiscoveredGrpcServices 程序集排除规则误伤业务程序集:原笼统排除 FullName 以 Healnet 开头的程序集,导致 Healnet.Example.* 等业务服务的 gRPC 服务不被自动映射(转码端点 404);现改为精确排除 Healnet.Common/Healnet.Core/Healnet.Infrastructure 三个框架程序集(并补 mscorlib/netstandard)。② UseHealnetServiceDefaults 与 AddHealnetServiceDefaults 能力开关脱节:原 Use 侧从 Healnet:ServiceDefaults 配置新建 options(默认全 false),Add 侧的自动探测结果丢失,导致 Controller 端点不映射;现 Add 侧将探测后的 options 写入 DI(AddSingleton),Use 侧优先取 DI 实例,配置节点仍可覆盖。③ Kestrel 双端口策略修正并显式告警:明文端口的 Http1AndHttp2 无 TLS 时 HTTP/2 被禁用(Kestrel 事件 Http2DisabledWithHttp1AndNoTls,连接退化为 HTTP/1.1),此前'HTTP 与 gRPC 同端口共存'的原生 gRPC 调用实际不可用;现明确——EnableGrpc 且 GrpcPort 配置为独立端口(异于 ServicePort)时双端口(HTTP: Http1AndHttp2 + gRPC: HTTP/2 Only,原生 gRPC 唯一通路);GrpcPort 缺失或与 ServicePort 相同则记 Error 提示补配置(JSON 转码 HTTP 面不受影响)。混合宿主模板已改为双端口(5000 HTTP + 5001 gRPC),Consul 注册 grpc-port Meta 指向 5001,发现/通信全链路正确。 v4.0.26: 新增服务托管默认值 Service Defaults(HealnetServiceDefaultsExtensions),框架样板全收编,业务服务 Program.cs 只需两行调用。① AddHealnetServiceDefaults(WebApplicationBuilder):自动完成 Serilog 日志(UseHealnetSerilog)、Consul 客户端、gRPC JSON 转码 + 统一异常拦截器(按需)、MVC(按需)、JWT、健康检查、Swagger(含 gRPC 文档,服务类型自动发现)、Kestrel 端口策略(读 Healnet:Consul:ServicePort/GrpcPort——GrpcPort 大于 0 且异于 ServicePort 时双端口(HTTP: Http1AndHttp2 + gRPC: HTTP/2 Only),否则单端口 Http1AndHttp2(HTTP 与 gRPC 同端口共存,content-type 协商));能力开关默认自动探测入口程序集(存在 Controller 则启用 MVC、存在 gRPC 服务类则启用 gRPC),可经 HealnetServiceDefaultsOptions 委托显式覆盖。② UseHealnetServiceDefaults(WebApplication):按生产顺序挂载请求日志(/health 降噪)、全局异常(ExceptionHandlerMiddleware)、CorrelationId、认证授权、/health、MapControllers(按需)、自动发现并映射 gRPC 服务(MapAllDiscoveredGrpcServices,扫描非系统程序集中基类链含 BindService 的类型,逐个反射调用 MapGrpcService,业务无需手写 MapGrpcService)、Swagger UI、Consul 注册(Enabled 开关跳过)。③ 业务服务最终形态:builder.AddHealnetServiceDefaults() + app.UseHealnetServiceDefaults() + Run,业务代码只写 proto/gRPC 实现/Controller。④ 注意事项:混合宿主(HTTP 与 gRPC 同端口)必须将 Healnet:Consul:GrpcPort 配为与 ServicePort 相同值,Consul 注册才会写入 grpc-port Meta,其他服务的 gRPC 服务发现才能命中(发现侧跳过无 grpc-port 的实例,4.0.13 同样约束);自动映射 gRPC 服务后不要再手动调用 MapGrpcService<T>(同类型重复映射会抛端点重复异常)。模板案例见 examples/(MixedHost 混合宿主 / GrpcOnly 纯 gRPC 双端口 / ApiOnly 纯 HTTP) v4.0.25: gRPC 统一封装(业务服务开箱即得,无需自行注册)。① 服务端:AddGrpcJsonTranscodingSupport 现已默认注册 HealnetGrpcExceptionInterceptor(此前该拦截器为孤儿封装、全库无注册点,业务代码在 gRPC 服务中抛 HealnetException/BusinessException 会落入 Unknown/500);现自动转换异常为 gRPC Status(trailers 携带 x-error-code/x-error-retryable),JSON Transcoding 端点的 HTTP 状态码随之正确映射(NotFound→404、InvalidArgument→400、FailedPrecondition→400、PermissionDenied→403、ResourceExhausted→429、DeadlineExceeded→504、Unauthenticated→401),业务服务在 gRPC 方法中直接抛 Core 异常体系即可。② 客户端:GrpcClientBase 新增统一调用入口 ExecuteAsync——自动构建调用 metadata(x-correlation-id 链路透传,取自当前 Activity.TraceId;ForwardAuthorization=true 时透传调用方 Authorization header 到下游 gRPC metadata,供内部接口鉴权);并把 RpcException 还原为 HealnetException(服务端为 Healnet 框架时按 trailers x-error-code 精确还原原始错误码,否则按 StatusCode 退化映射:InvalidArgument→400、PermissionDenied→403、NotFound→404、ResourceExhausted→429、Internal→500、DeadlineExceeded→504、FailedPrecondition→400、Unauthenticated→401、Unavailable→503 可重试、Unimplemented→501、Aborted→409),消费方用统一 HTTP 语义错误码处理远程失败,不再感知 gRPC StatusCode/trailers 细节。新增 GrpcExceptionMapper(还原映射器)。③ 兼容性:GrpcClientBase 构造函数新增可选参数 IHttpContextAccessor(默认 null),既有子类无需改动;ExecuteAsync 为新增方法,原 GetClientAsync/GetRequiredClientAsync 用法不变;行为变化仅一处——此前 gRPC 服务中抛 Core 异常返回 Unknown 的场景现在返回精确 Status,属缺陷修复。 v4.0.24: 代码审查缺陷批量修复(正确性/安全/资源管理)。① GrpcChannelCache:健康检查失败的坏 Channel 先从缓存移除再重建(原 GetOrCreate 对已存在 key 直接返回旧坏实例,坏 Channel 永不重建);凭据按 URL scheme 选择(https→SecureSsl,原硬编码 Insecure 导致 https 目标握手失败)。② RabbitMQProducerBase:BasicPublishBatch/BasicPublish 发布操作加锁串行化(共享单例 channel 非线程安全,对照 RabbitMQService._publishLock 模式)。③ RabbitMQConsumerBase:重试耗尽与反序列化失败路径改为 DLQ 发布成功才 Ack,否则 BasicNack(requeue:false) 交给 broker 端 DLX 策略(原 DLQ 未启用或发布失败时无条件 Ack 静默丢消息)。④ DistributedLockService:锁值字典改 TryAdd 语义(原索引器赋值覆盖同键旧 token);释放时本地无 token 不再 KeyDelete 兜底(防误删其他实例持有的锁),仅告警,键由过期时间或真正持有者释放。⑤ MemoryOrchestrator:L2 持久化失败(保存失败或锁未获取)置 l2Persisted=false 如实返回(原静默吞掉仍返回 true);TurnNo 改 COUNT 聚合查询(原 GetRecentTurnsAsync(sessionId, 100000) 拉 10 万条仅为计数);Milvus FilterExpression 的 domain 加白名单校验(字母数字下划线中划线,不合法抛 ArgumentException,防表达式注入)。⑥ ConversationRepository:SaveSessionAsync/SaveTurnAsync 失败返回 false(配合 Core 4.0.11 的 IConversationRepository 契约),新增 CountTurnsAsync。⑦ RAGService 注册改 AddSingleton(其内部知识库字典随实例持久,Scoped 导致每个请求拿到空知识库)。⑧ AgentContextManager 加 TTL 惰性清理(1 小时,Get/Create/Exists/Count 路径触发,原只增不减内存泄漏);AgentService.ExecuteAsync/ExecuteWithToolsAsync 成功完成后调用 DeleteContextAsync。⑨ HttpClientService 全部方法(POST/PUT/DELETE/PATCH/表单 POST/下载字符串与字节数组/带状态码变体)统一经 BuildRequestUrlAsync 走 Consul 服务发现(原仅 GET 走,Consul 模式下其余方法相对路径请求必然失败)。⑩ AiMemoryEntityMapper:补 ConversationArchive.Id 列 IsPrimarykey/IsIdentity 映射(原空分支,CodeFirst 建表缺主键)。⑪ SqlSugarRepository:默认连接名取连接配置中的首个 ConfigId(原硬编码 "Default",多库首连接名非 Default 时仓储绑错库),取不到回退 "Default"。⑫ SqlSugarExtensions.OnLogExecuting 不再拼接 SQL 参数值(防手机号/金额等敏感数据泄漏进日志),仅保留 SQL 文本 Debug。⑬ ResiliencePolicyProvider 重试策略补 TaskCanceledException 覆盖(HttpClient 超时表现为该异常,原不重试),并排除外部取消(CancellationToken 已触发时不重试)。⑭ RabbitMQService 消费者由 EventingBasicConsumer(async void 事件,异常崩进程)改 AsyncEventingBasicConsumer;失败路径由 Nack(requeue:true) 改为 republish 带 x-retry-count=原值+1 header 的新消息并 Ack 原消息(原实现只读不写计数,Nack 重回队列 header 不变,毒消息无限循环),超过 MaxRetryCount(默认 3,新公开属性)转 BasicNack(requeue:false) 交 broker 端 DLX;Subscribe(queue, routingKey, handler) 重载标记 Obsolete(该重载错误地将 routingKey 用作 exchange 名称),5.0 移除。⑮ RedisService.GetAsync 区分 JsonException(Warning 日志,视为未命中)与连接类异常(Error 日志);仍返回 default,兼容性不变。⑯ ApiKeyAuthenticationHandler:API Key 比对由 string.Equals 改 CryptographicOperations.FixedTimeEquals 恒定时间比较(长度不等直接 false),防时序侧信道。⑰ ConsulService 实现 IDisposable 释放 ConsulClient(单例生命周期下原 HTTP 连接永不释放)。已知限制:全部契约接口补 CancellationToken 归 5.0;RedisShortTermMemoryStore 读改写改 Redis List 需数据迁移;MemoryOrchestrator 的 L3 蒸馏 fire-and-forget 需背景队列架构,另批处理。v4.0.23: 修复 Swagger gRPC 封装 4 处缺陷并增强 proto3 JSON 文档准确性(QA 团队双通道验证后实施)。① GetMethodType 流式识别失效:原实现对 Task`1 泛型定义名做 Contains 判断恒不命中,所有流式方法(ServerStreaming/ClientStreaming/DuplexStreaming)被误判为 Unary、x-grpc-type 恒为 Unary;现先解包 Task<T> 再与 Grpc.Core 的 AsyncServerStreamingCall<>/AsyncClientStreamingCall<,>/AsyncDuplexStreamingCall<,> 精确比较。② 流式假端点:GrpcSwaggerDocumentFilter 现跳过 ClientStreaming/DuplexStreaming(JSON Transcoding 官方不支持、经 HTTP 调用必然 404),并记 Information 日志;ServerStreaming 保留但在描述中标注响应为流式 JSON。③ int64 映射对齐 proto3 JSON:long/ulong 的 Schema 从 integer/int64 改为 string/int64、string/uint64(proto3 JSON 将 64 位整数编码为字符串,Transcoding 实际输出 "123");顺带补 uint→integer/int32(原落入默认 string 分支);MapField<long> 值同步修正。④ WellKnownTypes 盲区:Any 从空 object 改为带 @type 键提示与说明的开放对象(Any 打包类型为实例级信息,静态文档原理上无法展开,标注请按 @type 对应消息定义);Struct→任意 JSON 对象;Value→任意 JSON 值(不设 type);ListValue→任意值数组;FieldMask→逗号分隔字段路径字符串;NullValue→NULL_VALUE 枚举;Empty→空对象。⑤ 深度截断显式标注:嵌套超过 10 层降级为字符串时,Schema description 注明「[嵌套截断]」与被截断消息名,避免文档读者误读为真实字符串字段。⑥ 悬空 $ref:_currentDoc 为 null 时(如反射调用场景)CreateSchema 此前返回从未注册进 Components 的 Reference.Id,渲染解析失败;现改为返回已展开的内联 schema。⑦ ByteString Schema 补 format:byte,Swagger UI 可生成合法 base64 示例。⑧ 新增 gRPC 文件上传调试端点模板:MapGrpcFileUploadEndpoint(Swagger/GrpcFileUploadExtensions.cs)在 gRPC 服务同进程映射 multipart/form-data 上传端点(默认 /grpc-file-upload,可配置路由/大小上限/扩展名白名单/业务 Handler),返回统一 ApiResponse(code=200),Swagger UI 独立分组展示并支持文件选择框 Try it out——背景:JSON Transcoding 无 multipart,bytes 仅适合 base64 小内容,真实文件上传需传统 REST 端点。已冒烟实测:上传成功回显元信息/扩展名白名单拒绝 400/空请求 400/swagger.json 含 multipart requestBody 与独立分组。以上均已通过引用真实源码的验证 harness(伪 gRPC 服务 + 私有方法反射调用)回归实测。无公开 API 签名变更,行为向后兼容(文档描述更准确)。v4.0.22: 在 4.0.21 基础上补一处混合形状处理:当标量 MinimumLevel 与嵌套子节点同时存在时(例如环境变量 Healnet__Serilog__MinimumLevel=Error 叠加在 JSON 的嵌套对象之上),级别取标量,但嵌套里的 Override 仍然合并——此前只取标量会丢掉嵌套 Override。4.0.21/4.0.20 未公开该方法且无使用方,请直接用 4.0.22。v4.0.21: 修复 Healnet:Serilog 日志级别配置静默失效(4.0.20 为同一修复的中间发布,未公开解析方法且无使用方,请直接用 4.0.21)。LoggingOptions.MinimumLevel 是 string,而 Serilog.Settings.Configuration 的标准写法是嵌套对象 { "Default": .., "Override": { .. } };配置绑定遇到对象转 string 时会静默跳过该属性且不抛异常,最终 MinimumLevel 保持默认 "Information"、Override 为空字典——等价于"根本没配日志级别"。表现为:生产 Apollo 配 Warning 仍全量输出 Microsoft.AspNetCore 的 INF,本地配 Debug 却看不到任何 LogDebug。现新增公开的 LoggingExtensions.LoadLoggingOptions(IConfiguration) 显式兼容两种形状:标量形状直接采用;嵌套形状取 Default 并合并其 Override,同时通过 SelfLog 提示改回平级写法。Override 合并时平级(框架契约)优先于嵌套,同名键以平级为准,比较器改为 OrdinalIgnoreCase 以匹配 Serilog 的前缀匹配语义。公开该方法也便于服务在启动时直接打印"实际生效的日志配置"做诊断。无破坏性 API 变更,旧配置无需修改即可生效。v4.0.20: 同 4.0.21,中间发布。v4.0.19: IRedisService 新增 GetSubScriber() 方法,返回 StackExchange.Redis 的 ISubscriber,用于直接操作 Redis 发布/订阅(pub/sub)通道;RedisService 基于 _connection.GetSubscriber() 实现。注意:IRedisService 为公开接口,自行实现该接口的外部代码需补齐此方法。v4.0.16: 修复 YARP 服务发现"一缺全废"缺陷:Consul 中任一已配置服务无健康实例时,ConsulServiceDiscoveryConfigProvider.LoadConfigAsync 会在遍历中因 ConsulService.ResolveAllHttpAddressesAsync 抛 InvalidOperationException 而整体中断(异常被循环外层 catch 吞掉),导致此前已生成的路由全部丢弃、且每轮刷新均失败无法自愈——表现为"必须把所有下游服务全部启动,网关路由/Swagger 聚合才可用"。现按服务隔离异常:服务无实例(InvalidOperationException)仅记 Warning 并跳过该服务,其余服务路由照常生成(健康实例出现后 30s 内自动补齐);Consul 不可达等其他异常保留上一份配置,避免瞬时故障清空全部路由。无公开 API 签名变更,行为向后兼容。v4.0.13: gRPC 服务发现(ResolveAllGrpcAddressesAsync)支持遗留回退:实例无 grpc-port Meta 但 Tags 含 GrpcLegacyFallbackTag(默认 server,需 Healnet:Consul:GrpcLegacyTagFallback=true 开启)时,回退使用 Address:Port 作为 gRPC 地址,用于与历史 .NET Core 3.1 MicroCloud 框架注册的 gRPC 服务(不带 Meta、主端口即 h2c)互通;fail-fast 报错文案同步更新。默认关闭,行为完全向后兼容。:实例无 grpc-port Meta 但 Tags 含 GrpcLegacyFallbackTag(默认 server,需 Healnet:Consul:GrpcLegacyTagFallback=true 开启)时,回退使用 Address:Port 作为 gRPC 地址,用于与历史 .NET Core 3.1 MicroCloud 框架注册的 gRPC 服务(不带 Meta、主端口即 h2c)互通;fail-fast 报错文案同步更新。默认关闭,行为完全向后兼容。v4.0.12: 修复 RabbitMQ channel 泄漏导致连接触顶 ChannelAllocationException 的故障。此前 IRabbitMQService 注册为 Scoped,调用方用根 IServiceProvider.GetService 解析时每次新建 RabbitMQService 并在构造中 connection.CreateModel() 分配一个 channel;RabbitMQService 未实现 IDisposable 导致 scope 永不回收 channel,长时间运行后连接上 channel 只增不减,触顶 2047 上限即抛 ChannelAllocationException,进而拖垮依赖 MQ 的服务(如 identity 的 ValidateToken/AccountLogin/RefreshToken 空体 500)。现改为 AddSingleton(全进程复用 1 个 channel),RabbitMQService 实现 IDisposable(关闭时回收 channel),并对共享 channel 的发布/确认路径加锁以保证并发安全。无公开 API 签名变更,行为对既有调用方完全兼容(建议调用方仍用 GetService 懒获取而非构造硬注入)。v4.0.6: 三库版本重新对齐至统一 4.0.6(消除版本分叉,无新增代码变更/公开 API 变更,源码与 4.0.5 一致)。v4.0.2: GrpcChannelCache 创建的 HttpClient.Timeout 改为可配置(配置键 Grpc:HttpClientTimeoutSeconds,默认 100s 与 .NET 默认一致,保持既有行为)。此前 HttpClient 未显式设置 Timeout,恒为 100s 硬上限,且优先级高于 CallOptions.Deadline(grpc-dotnet 中 HttpClient.Timeout 先触发),导致调用方设置的更长 deadline(如编排层 DownstreamTimeoutSeconds=160,覆盖 LLM 生成 + 重试 >100s 的长查询)被 100s 硬截断,表现为客户端收到 "HttpClient.Timeout of 100 seconds elapsing" 的 Cancelled 状态。需要长超时的调用方现可在自身配置中调大该值(如 "gRPC": {"HttpClientTimeoutSeconds": 200})。无公开 API 签名变更,默认行为与 4.0.1 完全兼容。v4.0.1: 修复 AddMemoryServices 无条件注册 L4 归档服务的缺陷。此前 IArchiveRepository/IArchiveService 恒被注册,而 ArchiveService 硬依赖 IFileService,导致未注册文件存储的使用方即便 EnableArchive=false 也会在 DI 解析 MemoryOrchestrator 时抛 "Unable to resolve service for type 'IFileService' while attempting to activate 'ArchiveService'",只能在使用方 RemoveAll<IArchiveService>() 兜底。现改为仅当 MemoryOptions.EnableArchive 为 true 时才注册这两个服务,并在开启归档却缺失 IFileService 时抛出带注册指引的 InvalidOperationException。无公开 API 签名变更,行为对 EnableArchive=true 的使用方完全兼容。v4.0.0: 破坏性主版本(破坏性变更来自 Healnet.Core)。RedisService 实现 ICacheService.GetAndDeleteAsync<T>(Lua 原子 GET+DEL,防并发重复消费);OssFileService 剥离 endpoint 中重复的 bucket 子域前缀;LoggingExtensions 将 ES sink 由 Serilog.Sinks.Elasticsearch 切换为 Serilog.Sinks.OpenSearch 并启用 SelfLog 诊断(公开方法签名未变);AIServiceExtensions 调整 OpenAI HTTP 客户端超时逻辑。v3.1.0: OssFileService 实现 GeneratePresignedUrl(...) 预签名下载 URL(OSS 直连下载,配合 Healnet.Common 3.1.0 的 IFileService 扩展)。v3.0.1: ConsulService gRPC 发现改为 fail-fast 并支持唯一实例 ID 注册与 gRPC 健康检查;SqlSugarExtensions 注册 ITenant 多租户接口、AiMemoryEntityMapper DatabaseName 列标记可空;新增 GrpcClientBase 泛型基类;JwtBearer 跳过非 JWT 格式 Bearer token 避免 IDX14100 噪声、JWT bearer 对 /health 跳过校验;conversation_turn ErrorMessage 列映射标记 IsNullable。v3.0.0: 随 Healnet 统一升版至 3.0.0(破坏性变更来自 Healnet.Common:AESCrypto 移除无参重载、AESDecrypt 改返回元组)。v2.0.7: SqlSugar EntityMapper 可插拔化(IEntityMapper 接口)/ BusinessConfig + BusinessExample 迁出至业务项目 / ConversationArchive 泛化 / EntityService PropertyInfo 类型比较 bug 修复。v2.0.6: 新增 Milvus 向量数据库封装(IMilvusService / MilvusService / MilvusOptions / MilvusExtensions / MilvusHealthCheck),支持集合管理、向量索引、近似检索(ANN)、标量查询、批量写入与健康检查。v2.0.3: 统一三库版本号;升级目标框架至 net10.0;更新所有依赖包至最新稳定版;移除 Infrastructure 项目中多余的 MSBuild 属性。v2.0.1: 依赖包版本升级,修复若干已知问题。v2.0.0: 重大版本更新,目标框架从 net8.0 升级至 net10.0,全面更新依赖包版本,优化 API 设计。