Healnet.Infrastructure 4.0.38

dotnet add package Healnet.Infrastructure --version 4.0.38
                    
NuGet\Install-Package Healnet.Infrastructure -Version 4.0.38
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Healnet.Infrastructure" Version="4.0.38" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Healnet.Infrastructure" Version="4.0.38" />
                    
Directory.Packages.props
<PackageReference Include="Healnet.Infrastructure" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Healnet.Infrastructure --version 4.0.38
                    
#r "nuget: Healnet.Infrastructure, 4.0.38"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Healnet.Infrastructure@4.0.38
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Healnet.Infrastructure&version=4.0.38
                    
Install as a Cake Addin
#tool nuget:?package=Healnet.Infrastructure&version=4.0.38
                    
Install as a Cake Tool

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=workbuddy claim;其 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(原无任何锁保护,并发订阅/声明存在竞态)。
    • 新增 DiCompletenessTests 16 项守卫(这条链路此前在 146 单测 + 42 E2E 中零覆盖):ValidateOnBuild 全 provider 容器校验、NullCopilotClient 降级语义、根容器单例断言、跨调用历史保留 / ClearContext 生效 / 单会话裁剪 / 容量淘汰、同 contextId 并发串行化、RabbitMQ 并发容器反射断言。QA 变异证伪:改回 AddScoped 被 8 项抓住、移除 Null 兜底被 4 项抓住。
    • 兼容性:DI 行为变化仅 ICopilotClient Singleton 化与未配置降级;直接 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。

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] 的模型校验失败返回 RFC7807 ProblemDetails(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.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 成对取路径与 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 实例字段保存跨请求状态的缺陷(改为 Singleton InMemoryProductStore 注入)。
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 框架时按 trailers x-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(原 EventingBasicConsumer async void 事件异常崩进程);失败路径 republish 携带 x-retry-count=原值+1 header 的新消息并 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:MemoryOrchestrator L2 持久化失败如实返回 l2Persisted=false;TurnNo 改 COUNT 聚合查询(原拉 10 万条仅为计数);Milvus domain 白名单校验(字母数字下划线中划线,防表达式注入);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,兼容性不变。
    • 安全:ApiKeyAuthenticationHandler API Key 比对改 CryptographicOperations.FixedTimeEquals 恒定时间比较(防时序侧信道)。
    • Consul:ConsulService 实现 IDisposable 释放 ConsulClient(单例下原 HTTP 连接永不释放)。
    • 已知限制:全部契约接口补 CancellationToken 归 5.0;RedisShortTermMemoryStore 读改写改 Redis List 需数据迁移。

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-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 泄漏:此前 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.Client 2.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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
Loading failed

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&lt;T&gt;(同类型重复映射会抛端点重复异常)。模板案例见 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 设计。