diff --git a/Configurations/SimApiJobOptions.cs b/Configurations/SimApiJobOptions.cs
index 62e0129..a0a47ac 100644
--- a/Configurations/SimApiJobOptions.cs
+++ b/Configurations/SimApiJobOptions.cs
@@ -29,11 +29,12 @@ public class SimApiJobOptions
/// 设置为null 使用默认redis配置
///
public int? Database { get; set; } = null;
+
public SimApiJobServerConfig[] Servers { get; set; } = [new()];
}
public class SimApiJobServerConfig()
{
public string[] Queues { get; set; } = ["default"];
- public int WorkerNum { get; set; } = 50;
+ public int WorkerNum { get; set; } = 5;
}
\ No newline at end of file
diff --git a/README.md b/README.md
index e490033..3f820ee 100644
--- a/README.md
+++ b/README.md
@@ -1,55 +1,1070 @@
-# SimApi 基于.Net的一个基础辅助包
+# SimApi 使用说明书
-[](https://github.com/simcu/simapi-net/actions/workflows/nuget-publish.yml)
-### 包含了以下组件:
+> **NuGet 包名**:`Simcu.SimApi`
+> **目标框架**:`net8.0` / `net9.0` / `net10.0`
+> **作者**:xRain@SimcuTeam
-1. 统一的参数检测,基础认证服务
-所有的控制器需要继承 YYApi.Controllers.BaseController
+---
-```C#
-using Models;
-using SimApi.Controllers;
+## 1. 项目概述
-namespace Controllers
+SimApi 是一个基于 ASP.NET Core 的 **API 开发基础辅助库**,核心理念是**一行注册、一行启用**。通过 `AddSimApi()` + `UseSimApi()` 即可获得统一响应格式、认证、文档、对象存储、任务调度、MQTT 消息通信等全套能力。
+
+### 主要功能特性
+
+| 功能 | 说明 |
+|------|------|
+| **统一响应格式** | 自动封装所有接口响应为 `{code, message, data}` 格式 |
+| **全局异常处理** | 捕获所有异常并以 JSON 格式返回,HTTP 状态码始终为 200 |
+| **Token 认证** | 基于 Redis + Header Token 的简单认证机制 |
+| **在线 API 文档** | 基于 Swagger 的多分组文档,含认证标注、签名参数自动注入 |
+| **S3 对象存储** | MinIO 兼容的文件存储,支持预签名上传/下载 |
+| **任务调度** | 基于 Hangfire + Redis 的后台任务管理,带 Web 控制台 |
+| **MQTT 消息通信** | 基于 MQTTnet v5 的事件发布/订阅与 RPC 调用(Synapse 模块) |
+| **API 签名验证** | MD5 签名 + 时间戳过期 + 防重放攻击 |
+| **AES 加密传输** | 自动解密 AES-256-CBC 加密的请求体 |
+| **Redis 缓存** | 统一封装的分布式缓存操作 |
+| **Coce 统一身份** | 集成 Coce 第三方身份认证平台 |
+| **彩色控制台日志** | 按级别着色的格式化控制台日志 |
+| **CORS** | 开发/生产环境全量跨域支持 |
+| **反向代理支持** | ForwardedHeaders 透传,获取真实 IP |
+
+---
+
+## 2. 安装
+
+```bash
+dotnet add package Simcu.SimApi
+```
+
+---
+
+## 3. 快速开始
+
+`Program.cs` 中两步完成集成:
+
+```csharp
+var builder = WebApplication.CreateBuilder(args);
+
+builder.Services.AddSimApi(options =>
{
- public class BaseController : SimApiBaseController
+ options.RedisConfiguration = "localhost:6379";
+ options.EnableSimApiAuth = true;
+ options.EnableSimApiDoc = true;
+
+ options.ConfigureSimApiDoc(doc =>
{
- ///
- /// 获取登录用户信息
- ///
- protected SimApiLoginInfoItem LoginInfo => (SimApiLoginInfoItem) HttpContext.Items["LoginInfo"];
+ doc.DocumentTitle = "我的API文档";
+ doc.ApiGroups =
+ [
+ new("api", "公共接口"),
+ new("admin", "管理接口", "需要管理员Token")
+ ];
+ });
+});
+
+var app = builder.Build();
+app.UseSimApi(); // 一行启用全部中间件和路由
+app.Run();
+```
+
+---
+
+## 4. 配置选项(SimApiOptions)
+
+所有配置通过 `AddSimApi(options => { ... })` 设置。
+
+### 4.1 功能开关
+
+| 属性 | 类型 | 默认值 | 说明 |
+|------|------|--------|------|
+| `RedisConfiguration` | `string?` | `null` | Redis 连接字符串(多个模块依赖) |
+| `EnableSimApiAuth` | `bool` | `false` | 启用 Token 认证体系 |
+| `EnableSimApiDoc` | `bool` | `false` | 启用 Swagger 文档(`/swagger`) |
+| `EnableSimApiStorage` | `bool` | `false` | 启用 S3 对象存储 |
+| `EnableJob` | `bool` | `false` | 启用 Hangfire 任务调度 |
+| `EnableSynapse` | `bool` | `false` | 启用 MQTT 消息/RPC 通信 |
+| `EnableCoceSdk` | `bool` | `false` | 启用 Coce 统一身份平台 |
+| `EnableCors` | `bool` | **`true`** | 启用 CORS 全部允许 |
+| `EnableSimApiException` | `bool` | **`true`** | 启用全局异常拦截 |
+| `EnableSimApiResponseFilter` | `bool` | **`true`** | 启用响应统一封装 |
+| `EnableForwardHeaders` | `bool` | **`true`** | 启用反向代理 Header 转发 |
+| `EnableLowerUrl` | `bool` | **`true`** | URL 强制小写 |
+| `EnableVersionUrl` | `bool` | **`true`** | 启用 `/versions` 接口 |
+| `EnableLogger` | `bool` | **`true`** | 启用彩色控制台日志 |
+
+### 4.2 子模块配置方法
+
+```csharp
+options.ConfigureSimApiDoc(doc => { ... });
+options.ConfigureSimApiStorage(s => { ... });
+options.ConfigureSimApiJob(job => { ... });
+options.ConfigureSimApiSynapse(s => { ... });
+options.ConfigureCoceSdk(coce => { ... });
+```
+
+---
+
+## 5. 核心模块
+
+### 5.1 统一响应格式
+
+#### 5.1.1 响应结构
+
+所有接口统一返回以下 JSON 格式:
+
+```json
+{
+ "code": 200,
+ "message": "成功",
+ "data": { ... }
+}
+```
+
+#### 5.1.2 状态码映射
+
+| code | 默认 message |
+|------|-------------|
+| 200 | 成功 |
+| 204 | 没有数据 |
+| 400 | 参数错误 |
+| 401 | 需要登录 |
+| 403 | 无权访问 |
+| 404 | 接口不存在 |
+| 500 | 服务器错误 |
+
+#### 5.1.3 响应类
+
+```csharp
+// 无数据响应
+return new SimApiBaseResponse();
+return new SimApiBaseResponse(400, "参数错误");
+return new SimApiBaseResponse(404); // 自动映射消息:接口不存在
+
+// 带数据响应
+return new SimApiBaseResponse(user);
+
+// 分页响应
+return new SimApiBaseResponse>>(new PageResponse>
+{
+ List = users,
+ Page = 1,
+ Count = 20,
+ Total = 100
+});
+```
+
+#### 5.1.4 响应过滤器行为(SimApiResponseFilter)
+
+| 控制器返回值 | 最终 JSON 输出 |
+|------------|--------------|
+| `null` | `{"code":200,"message":"成功"}` |
+| 普通对象 | `{"code":200,"message":"成功","data":对象}` |
+| `SimApiBaseResponse` | 原样输出 |
+| 标注 `[OriginResponse]` | 完全不封装,原始输出 |
+
+#### 5.1.5 跳过封装
+
+```csharp
+[HttpGet]
+[OriginResponse]
+public string GetRaw() => "raw string";
+```
+
+---
+
+### 5.2 基础控制器(SimApiBaseController)
+
+所有业务控制器建议继承 `SimApiBaseController`:
+
+```csharp
+[ApiController]
+[Route("[controller]")]
+public class UserController : SimApiBaseController
+{
+ // 获取当前登录用户(需配合 EnableSimApiAuth)
+ // protected SimApiLoginItem LoginInfo => ...
+}
+```
+
+#### 错误处理方法(均为 `protected static`)
+
+| 方法 | 触发条件 | 默认参数 |
+|------|----------|---------|
+| `Error(code, message)` | 直接抛出 | `500, ""` |
+| `ErrorWhen(condition, code, message)` | condition == `true` | `400, ""` |
+| `ErrorWhenTrue(condition, code, message)` | 同上(别名) | `400, ""` |
+| `ErrorWhenFalse(condition, code, message)` | condition == `false` | `400, ""` |
+| `ErrorWhenNull(obj, code, message)` | obj == `null` | `404, "请求的资源不存在"` |
+
+```csharp
+public SimApiBaseResponse GetUser(int id)
+{
+ var user = _db.Users.Find(id);
+ ErrorWhenNull(user, 404, "用户不存在");
+
+ ErrorWhen(user.Age < 18, 403, "未满18岁");
+
+ return new SimApiBaseResponse(user);
+}
+```
+
+---
+
+### 5.3 认证模块(SimApiAuth)
+
+#### 5.3.1 配置
+
+```csharp
+options.EnableSimApiAuth = true; // 需同时配置 RedisConfiguration
+```
+
+#### 5.3.2 工作原理
+
+1. 客户端请求时在 Header 中携带 `Token: `
+2. `SimApiAuthMiddleware` 从 Redis 读取登录信息存入 `HttpContext.Items["LoginInfo"]`
+3. `[SimApiAuth]` Attribute 检查是否已登录及角色权限
+
+#### 5.3.3 SimApiLoginItem 结构
+
+```csharp
+public class SimApiLoginItem
+{
+ public required string Id { get; set; } // 用户唯一标识
+ public string[] Type { get; set; } = ["user"]; // 用户类型(支持多角色)
+ public Dictionary Meta { get; set; } = []; // 附加元数据
+ public object? Extra { get; set; } // 扩展数据
+}
+```
+
+#### 5.3.4 SimApiAuth 助手方法
+
+```csharp
+// 注入使用
+public MyController(SimApiAuth auth) { ... }
+
+// 登录(token 不传则自动生成 GUID)
+string token = auth.Login(loginItem);
+string token = auth.Login(loginItem, "custom-token");
+
+// 更新登录信息(不改变 token)
+auth.Update(loginItem, token);
+
+// 获取登录信息
+SimApiLoginItem? info = auth.GetLogin(token);
+
+// 退出登录
+auth.Logout(token);
+```
+
+#### 5.3.5 自动注册路由
+
+启用 `EnableSimApiAuth` 后自动生成以下路由:
+
+| 路由 | 方法 | 说明 |
+|------|------|------|
+| `POST /auth/check` | 无需登录 | 检测登录状态,返回用户 ID |
+| `POST /auth/logout` | 无需登录 | 退出登录 |
+| `POST /user/info` | 需要登录 | 获取当前用户完整信息 |
+
+---
+
+### 5.4 特性(Attributes)
+
+#### `[SimApiAuth]` — 身份认证
+
+```csharp
+[SimApiAuth] // 仅检查是否登录
+[SimApiAuth("admin")] // 检查 Type 中是否含 "admin"
+[SimApiAuth("admin,manager")] // 检查 Type 是否含 "admin" 或 "manager"(逗号分隔)
+[SimApiAuth(new[]{"a", "b"})] // 数组形式
+```
+
+#### `[SimApiDoc]` — Swagger 文档注解
+
+```csharp
+[SimApiDoc("用户管理", "获取用户列表")]
+[SimApiDoc("用户管理", "创建用户", "创建一个新用户账号,需要管理员权限")]
+[SimApiDoc(new[]{"用户", "管理"}, "批量操作")]
+```
+
+#### `[SimApiSign]` — API 签名验证
+
+需配合实现 `SimApiSignProviderBase` 并注入:
+
+```csharp
+[SimApiSign(KeyProvider = typeof(MySignProvider))]
+public IActionResult SecureApi(...) { ... }
+```
+
+签名算法:`MD5(field1=v1&...&appId=xxx×tamp=ts&nonce=nnn&密钥)`
+
+#### `[AesBody]` — AES 加密请求体
+
+客户端发送:`{"data": "AES密文"}`,服务端自动解密并反序列化:
+
+```csharp
+[HttpPost]
+public IActionResult Process([AesBody] MyRequest request)
+{
+ // request 已自动解密并反序列化
+}
+```
+
+#### `[OriginResponse]` — 跳过响应封装
+
+```csharp
+[HttpGet]
+[OriginResponse]
+public string GetRawData() => "raw data";
+```
+
+#### `[SynapseEvent]` — MQTT 事件处理方法
+
+```csharp
+// 参数1:事件名;参数2(可选):事件数据
+[SynapseEvent("order/created")]
+public void OnOrderCreated(string eventName) { ... }
+
+[SynapseEvent("order/+/status")] // 支持 MQTT 通配符 + 和 #
+public void OnOrderStatus(string eventName, MyEventData data) { ... }
+```
+
+#### `[SynapseRpc]` — MQTT RPC 方法
+
+```csharp
+[SynapseRpc] // 方法名自动为 "ClassName.MethodName"
+[SynapseRpc("getUserInfo")] // 自定义 RPC 方法名
+
+// 0~2 个参数,第2个参数固定为 Dictionary(headers)
+public UserDto GetUserInfo(GetUserRequest req) { ... }
+public UserDto GetUserInfo(GetUserRequest req, Dictionary headers) { ... }
+```
+
+---
+
+### 5.5 在线 API 文档(Swagger)
+
+#### 5.5.1 配置
+
+```csharp
+options.EnableSimApiDoc = true;
+options.ConfigureSimApiDoc(doc =>
+{
+ doc.DocumentTitle = "接口文档";
+
+ // 接口分组(Id 对应 ApiExplorerSettings.GroupName)
+ doc.ApiGroups =
+ [
+ new("api", "公共接口"),
+ new("admin", "管理接口", "需要管理员Token")
+ ];
+
+ // 配置认证方式(支持 SimApiAuth / ClientCredentials / Implicit / AuthorizationCode / Password)
+ doc.ApiAuth = new SimApiAuthOption
+ {
+ Type = ["SimApiAuth"] // 默认
+ };
+
+ // 支持的调试方法(默认仅 POST)
+ doc.SupportedMethod = [SubmitMethod.Post, SubmitMethod.Get];
+});
+```
+
+#### 5.5.2 接口分组
+
+```csharp
+// 归入 admin 文档
+[ApiExplorerSettings(GroupName = "admin")]
+public class AdminController : SimApiBaseController { ... }
+
+// 不标注则默认归入 Id="api" 的文档
+```
+
+#### 5.5.3 访问文档
+
+启动后访问 `/swagger`。
+
+#### 5.5.4 自动 Swagger 增强
+
+SimApi 内置多个 Swagger 过滤器,无需手动配置:
+
+| 过滤器 | 效果 |
+|--------|------|
+| `SimApiResponseOperationFilter` | 自动将返回类型包装为 `SimApiBaseResponse` 显示 |
+| `SimApiAuthOperationFilter` | 为 `[SimApiAuth]` 接口添加 Token 认证要求 |
+| `SimApiSignOperationFilter` | 为 `[SimApiSign]` 接口自动注入签名参数说明 |
+| `AesBodyOperationFilter` | 为 `[AesBody]` 参数展示加密前的原始数据结构 |
+| `GlobalDynamicObjectSchemaFilter` | 为 `object`/`Dictionary` 类型生成合理 Schema 示例 |
+| `RemoveEmptyTagsFilter` | 清除没有接口的空分组 Tag |
+
+---
+
+### 5.6 S3 对象存储(SimApiStorage)
+
+基于 MinIO SDK(S3 兼容协议)。
+
+#### 5.6.1 配置
+
+```csharp
+options.EnableSimApiStorage = true;
+options.ConfigureSimApiStorage(s =>
+{
+ s.Endpoint = "http://minio.example.com:9000"; // 注意不能以 / 结尾
+ s.AccessKey = "admin";
+ s.SecretKey = "password";
+ s.Bucket = "my-bucket";
+ s.ServeUrl = "http://cdn.example.com/my-bucket"; // 注意不能以 / 结尾
+});
+```
+
+#### 5.6.2 使用
+
+```csharp
+public MyController(SimApiStorage storage) { ... }
+
+// 获取预签名上传 URL(默认 2 小时过期),路径必须以 / 开头
+GetUploadUrlResponse r = storage.GetUploadUrl("/avatars/user1.jpg");
+// r.UploadUrl → 上传用的预签名 URL(供前端直接 PUT 请求)
+// r.DownloadUrl → 下载用的公开 URL
+// r.Path → 相对路径
+
+// 获取预签名下载 URL(默认 10 分钟过期)
+string url = storage.GetDownloadUrl("/files/doc.pdf");
+string url = storage.GetDownloadUrl("/files/doc.pdf", expire: 3600);
+
+// 直接服务端上传
+storage.UploadFile("/avatars/user1.jpg", stream, "image/jpeg");
+
+// 路径转公开访问 URL
+// - 以 http/https 开头:原样返回
+// - 以 ~/ 开头:转当前请求域名的本地路径
+// - 以 / 开头:拼接 ServeUrl
+string? url = storage.FullUrl("/path/to/file");
+string? url = storage.GetUrl("/path/to/file");
+
+// URL 转相对路径
+string? path = storage.GetPath("http://cdn.example.com/my-bucket/path/file");
+
+// 暴露底层 MinIO 客户端
+IMinioClient mc = storage.Client;
+```
+
+---
+
+### 5.7 任务调度(Hangfire)
+
+基于 Hangfire + Redis 存储。
+
+#### 5.7.1 配置
+
+```csharp
+options.EnableJob = true;
+options.ConfigureSimApiJob(job =>
+{
+ job.DashboardUrl = "/jobs"; // null 则不启用 Dashboard
+ job.DashboardAuthUser = "admin";
+ job.DashboardAuthPass = "Admin@123!";
+ job.RedisConfiguration = null; // null 则使用全局 RedisConfiguration
+ job.Database = 1; // Redis DB 编号,null 则使用默认
+ job.Servers =
+ [
+ new SimApiJobServerConfig { Queues = ["default"], WorkerNum = 5 },
+ new SimApiJobServerConfig { Queues = ["email"], WorkerNum = 2 }
+ ];
+});
+```
+
+#### 5.7.2 使用
+
+```csharp
+// 立即执行
+BackgroundJob.Enqueue(() => Console.WriteLine("立即执行"));
+
+// 延迟执行
+BackgroundJob.Schedule(() => Console.WriteLine("延迟执行"), TimeSpan.FromMinutes(5));
+
+// 定时循环执行
+RecurringJob.AddOrUpdate("daily-report",
+ () => myService.GenerateReport(), Cron.Daily);
+
+// 依赖执行(上一个完成后)
+var jobId = BackgroundJob.Enqueue(() => Console.WriteLine("第一步"));
+BackgroundJob.ContinueJobWith(jobId, () => Console.WriteLine("第二步"));
+```
+
+Dashboard 访问 `/jobs`,使用 HTTP Basic Auth(配置的用户名密码)。
+
+---
+
+### 5.8 MQTT 消息通信(Synapse)
+
+基于 MQTTnet v5,通过 WebSocket 连接 MQTT Broker。
+
+#### 5.8.1 配置
+
+```csharp
+options.EnableSynapse = true;
+options.ConfigureSimApiSynapse(s =>
+{
+ s.Websocket = "ws://mqtt.example.com:8083/mqtt";
+ s.Username = "user";
+ s.Password = "pass";
+ s.SysName = "my-system"; // 系统名(Topic 命名空间前缀)
+ s.AppName = "order-service"; // 服务名
+ s.AppId = "instance-001"; // 实例 ID(不填则自动生成 GUID)
+ s.RpcTimeout = 3; // RPC 超时秒数,默认 3
+ s.EventLoadBalancing = false; // 启用事件负载均衡($queue 订阅),默认 false
+ s.EnableConfigStore = true; // 启用分布式配置中心,默认 true
+ s.DisableEventClient = false; // 禁用事件客户端,默认 false
+ s.DisableRpcClient = false; // 禁用 RPC 客户端,默认 false
+});
+```
+
+#### 5.8.2 MQTT Topic 规则
+
+| 用途 | Topic 格式 |
+|------|-----------|
+| 事件发布 | `{SysName}/event/{AppName}/{eventName}` |
+| 事件订阅(负载均衡关闭) | `{SysName}/event/{eventName}` |
+| 事件订阅(负载均衡开启) | `$queue/{SysName}/event/{eventName}` |
+| RPC 请求 | `{SysName}/{targetApp}/rpc/server/{method}`(`$queue` 订阅,天然负载均衡) |
+| RPC 响应 | `{SysName}/{callerApp}/rpc/client/{AppId}/{messageId}` |
+| 配置存储 | `{SysName}/synapse-config-store/{key}`(Retain 消息) |
+
+#### 5.8.3 Synapse API(通过 DI 注入使用)
+
+```csharp
+public MyService(Synapse synapse) { ... }
+
+// 发送事件
+synapse.Event("order/created", new { OrderId = 1 });
+
+// 调用 RPC(同步阻塞,返回 SimApiBaseResponse)
+var res = synapse.Rpc("user-service", "GetUserInfo", new { Id = 1 });
+var res = synapse.Rpc("user-service", "GetUserInfo", param,
+ headers: new Dictionary { { "traceId", "xxx" } });
+
+// 无类型 RPC
+var res = synapse.Rpc("user-service", "GetUserInfo", param);
+
+// 读写分布式配置
+synapse.SetConfig("max_retry", "3");
+string? value = synapse.GetConfig("max_retry");
+
+// 监听配置变化
+synapse.OnConfigChanged += (sender, item) =>
+ Console.WriteLine($"{item.Key} = {item.Value}");
+
+// 在 RPC 方法内部抛出错误
+synapse.RpcError(400, "参数错误");
+synapse.RpcErrorWhen(id <= 0, 400, "ID 无效");
+```
+
+#### 5.8.4 注册事件/RPC 处理器
+
+启用 `EnableSynapse` 后,SimApi 会**自动扫描调用程序集**中所有含 `[SynapseRpc]` 或 `[SynapseEvent]` 方法的类,并自动注册为 Scoped 服务。
+
+```csharp
+// 事件处理类(无需手动注入,自动注册)
+public class OrderEventHandler
+{
+ [SynapseEvent("order/+/status")]
+ public void OnOrderStatus(string eventName, OrderStatusDto data)
+ {
+ // eventName = "order/123/status"
+ // data 已自动反序列化
+ }
+}
+
+// RPC 服务类(无需手动注入,自动注册)
+public class UserRpcService
+{
+ [SynapseRpc] // 方法名为 "UserRpcService.GetUserInfo"
+ public UserDto GetUserInfo(GetUserRequest req)
+ {
+ return new UserDto { ... };
+ }
+
+ [SynapseRpc("customRpcName")]
+ public ResultDto DoSomething(RequestDto req, Dictionary headers)
+ {
+ // headers 包含 RPC 调用方传入的自定义头
}
}
```
+---
-统一配置,现在只需要进行AddSimApi的配置, Configure中直接 UseSimApi即可。
-```C#
-services.AddSimApi(options =>
+### 5.9 API 签名验证(SimApiSign)
+
+#### 5.9.1 实现密钥提供者
+
+```csharp
+public class MySignProvider : SimApiSignProviderBase
{
- options.ConfigureSimApiDoc(options =>
- {
- options.ApiGroups = new[]
- {
- new SimApiDocGroupOption
- {Id = "admin", Name = "后台管理接口", Description = "本接口调用需要Scope:sac.api.admin"},
- new SimApiDocGroupOption
- {Id = "user-v1", Name = "用户中心接口", Description = "本接口调用需要Scope:sac.api.user"}
- };
- options.ApiAuth = new SimApiAuthOption
- {
- Type = new[] {"ClientCredentials", "Implicit", "AuthorizationCode"},
- Scopes = new Dictionary
- {
- {"sac.api.user", "用户信息接口权限"},
- {"sac.api.admin", "后台管理API"}
- },
- AuthorizationUrl = "/connect/authorize",
- TokenUrl = "/connect/token"
- };
- });
- options.EnableSimApiStorage = true;
- options.SimApiStorageOptions = Configuration.GetSection("S3").Get();
+ private readonly IServiceScopeFactory _scopeFactory;
+ public MySignProvider(IServiceScopeFactory sf) => _scopeFactory = sf;
+
+ public override string? AppIdName { get; set; } = "appId";
+ public override string TimestampName { get; set; } = "timestamp";
+ public override string NonceName { get; set; } = "nonce";
+ public override string SignName { get; set; } = "sign";
+ public override int QueryExpires { get; set; } = 5; // 5秒过期
+ public override bool DuplicateRequestProtection { get; set; } = true; // 防重放
+ public override string[] SignFields { get; set; } = ["userId"]; // 额外签名字段
+
+ public override string? GetKey(string? appId)
+ {
+ using var scope = _scopeFactory.CreateScope();
+ var db = scope.ServiceProvider.GetRequiredService();
+ return db.Apps.Find(appId)?.SecretKey;
+ }
+}
+
+// 注册(Scoped 或 Transient)
+services.AddScoped();
+```
+
+#### 5.9.2 使用
+
+```csharp
+[SimApiSign(KeyProvider = typeof(MySignProvider))]
+public IActionResult SecureApi(...) { ... }
+```
+
+#### 5.9.3 签名算法
+
+```
+MD5(field1=v1&field2=v2&...&appId=xxx×tamp=ts&nonce=nnn&密钥)
+```
+
+支持通过 Query 或 Header 传入签名参数。
+
+---
+
+### 5.10 AES 加密传输(AesBody)
+
+算法:**AES-256-CBC + PKCS7 填充**,IV 随机生成并附在密文前(Base64 编码)。
+
+#### 5.10.1 实现密钥提供者
+
+```csharp
+public class MyAesKeyProvider : AesBodyProviderBase
+{
+ private readonly IServiceScopeFactory _scopeFactory;
+ public MyAesKeyProvider(IServiceScopeFactory sf) => _scopeFactory = sf;
+
+ public override string? AppIdName { get; set; } = "appId"; // 从 Query/Header 获取
+
+ public override string? GetKey(string? appId)
+ {
+ using var scope = _scopeFactory.CreateScope();
+ var db = scope.ServiceProvider.GetRequiredService();
+ return db.Apps.Find(appId)?.SecretKey;
+ }
+}
+
+// 注册
+services.AddScoped();
+```
+
+#### 5.10.2 使用
+
+```csharp
+[HttpPost]
+public IActionResult Submit([AesBody(KeyProvider = typeof(MyAesKeyProvider))] MyRequest request)
+{
+ // request 已自动解密并反序列化
+}
+```
+
+客户端请求格式(提交 JSON body):
+```json
+{"data": "Base64(AES加密后的JSON字符串)"}
+```
+
+#### 5.10.3 AES 工具类
+
+```csharp
+string cipher = SimApiAesUtil.Encrypt("明文内容", "任意长度密钥");
+string plain = SimApiAesUtil.Decrypt(cipher, "任意长度密钥");
+```
+
+密钥会经过 SHA256 处理为 32 字节,因此支持任意长度密钥。
+
+---
+
+### 5.11 Redis 缓存(SimApiCache)
+
+统一加前缀 `SimApi:Cache:`,避免 key 冲突。
+
+```csharp
+public MyService(SimApiCache cache) { ... }
+
+// 存储(永不过期)
+cache.Set("userCount", 100);
+
+// 存储(带过期时间)
+cache.Set("userCount", 100, new DistributedCacheEntryOptions
+{
+ AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10)
+});
+
+// 获取原始字符串
+string? raw = cache.Get("userCount");
+
+// 获取并反序列化为指定类型
+int? count = cache.Get("userCount");
+```
+
+> 注意:`SimApiCache` 要求配置 `RedisConfiguration`。
+
+---
+
+### 5.12 HTTP 客户端(SimApiHttpClient)
+
+用于调用其他带签名/AES 加密的 SimApi 服务。
+
+```csharp
+var client = new SimApiHttpClient(appId: "myapp", appKey: "secret")
+{
+ Server = "https://api.example.com",
+ AppIdName = "appId",
+ TimestampName = "timestamp",
+ NonceName = "nonce",
+ SignName = "sign",
+ SignFields = ["field1", "field2"]
+};
+
+// 仅签名请求
+var result = client.SignQuery("/api/user", body, queries);
+
+// 仅 AES 加密请求(body 自动加密为 {"data":"Base64密文"})
+var result = client.AesQuery("/api/user", body);
+
+// AES 加密 + 签名
+var result = client.AesSignQuery("/api/user", body, queries);
+```
+
+---
+
+### 5.13 工具类(SimApiUtil)
+
+全部为静态成员,直接调用,无需注入。
+
+```csharp
+// 时间
+DateTime cst = SimApiUtil.CstNow; // UTC+8 当前时间
+double timestamp = SimApiUtil.TimestampNow; // 秒级 Unix 时间戳
+
+// 版本
+string simApiVer = SimApiUtil.SimApiVersion; // SimApi 包版本
+string appVer = SimApiUtil.AppVersion; // 宿主应用版本
+
+// 哈希加密
+string md5 = SimApiUtil.Md5("source"); // 32位(mode="x2")
+string md5 = SimApiUtil.Md5("source", "x3"); // 48位
+string sha1 = SimApiUtil.Sha1("source"); // SHA1
+
+// 序列化
+string json = SimApiUtil.Json(obj); // camelCase,中文不转义
+T obj = SimApiUtil.XmlDeserialize(xml); // XML → 对象
+JsonSerializerOptions opts = SimApiUtil.JsonOption; // JSON 配置
+
+// 验证
+bool ok = SimApiUtil.CheckCell("13800138000"); // 手机号格式验证
+
+// 分页(IQueryable 扩展方法)
+var paged = dbContext.Users.AsQueryable().Paginate(page: 1, count: 20);
+```
+
+---
+
+### 5.14 数据模型基类(SimApiBaseModel)
+
+ORM 实体基类,提供通用字段和轻量级属性映射能力。
+
+```csharp
+public class UserEntity : SimApiBaseModel
+{
+ public string Name { get; set; }
+ // 自动拥有:Id (GUID, string)、CreatedAt、UpdatedAt
+}
+
+// 从 DTO 映射到实体(自动跳过 Id / CreatedAt / UpdatedAt)
+entity.MapData(dto);
+
+// 强制映射所有字段(包括 Id 等受保护字段)
+entity.MapData(dto, mapAll: true);
+
+// 只映射指定字段
+entity.MapData(dto, new[] { "Name", "Email" });
+
+// 手动更新 UpdatedAt
+entity.UpdateTime();
+```
+
+> **注意**:`MapData` 只映射**同名且同类型**且**源值不为 null** 的属性。
+
+---
+
+### 5.15 Coce 统一身份平台(CoceSdk)
+
+集成 Coce 第三方 OAuth 统一身份认证平台(默认 `api.coce.cc`)。
+
+#### 5.15.1 配置
+
+```csharp
+options.EnableCoceSdk = true; // 需同时启用 EnableSimApiAuth
+options.ConfigureCoceSdk(coce =>
+{
+ coce.ApiEndpoint = "https://api.coce.cc"; // 默认值
+ coce.AuthEndpoint = "https://home.coce.cc"; // 默认值
+ coce.AppId = "your-app-id";
+ coce.AppKey = "your-app-key";
});
```
-基于S3的存储,基于RabbitMq的事件RPC调用,自定义Logger格式,自定义响应格式
+
+#### 5.15.2 自动注册路由
+
+| 路由 | 方法 | 说明 |
+|------|------|------|
+| `POST /auth/login` | 无需登录 | Coce 一键登录(前端传 `{"data":"lv1Token"}`) |
+| `POST /user/groups` | 需要登录 | 获取当前用户群组列表 |
+| `GET /auth/config` | 无需登录 | 获取 AppId 和授权 URL |
+
+#### 5.15.3 CoceApp 可用方法
+
+```csharp
+public MyService(CoceApp coce) { ... }
+
+// 用户
+coce.GetUserInfo(levelToken) // 获取用户基本信息
+coce.GetUserGroups(levelToken) // 获取用户群组
+coce.SearchUserByPhone("13800138000") // 按手机号搜索用户
+coce.SearchUserByIds(new[]{"uid1","uid2"}) // 按 ID 批量获取用户
+
+// 消息
+coce.SendUserMessage(userId, "标题", "内容")
+
+// 支付
+string? tradeNo = coce.TradeCreate("商品名", 100, "扩展数据") // 创建订单
+coce.TradeCheck(tradeNo) // 查询订单状态
+coce.TradeRefund(tradeNo) // 发起退款
+
+// Token 管理
+coce.GetLevelToken(lv1Token, level: 5) // 换取 Level Token
+coce.SaveToken(userId, levelToken) // 保存到 Redis
+coce.GetToken(userId) // 从 Redis 读取
+
+// 代理请求
+coce.ProxyQuery(uri, token)
+coce.ProxyQuery(uri, token, json)
+coce.ProxyQueue(uri, token, data)
+```
+
+#### 5.15.4 自定义登录逻辑
+
+实现 `ICoceLoginProcessor` 接口,在登录时根据群组赋予角色:
+
+```csharp
+public class MyLoginProcessor : ICoceLoginProcessor
+{
+ public SimApiLoginItem Process(SimApiLoginItem item, GroupInfo[] groups)
+ {
+ if (groups.Any(g => g.Role == "owner"))
+ item.Type = ["user", "admin"];
+ return item;
+ }
+}
+
+// 注册
+services.AddScoped();
+```
+
+---
+
+### 5.16 日志(SimApiLogger)
+
+启用后替换默认控制台日志,输出格式如下:
+
+```
+[ Microsoft.AspNetCore.Hosting.Diagnostics ][ 2024-01-01 12:00:00:0000 ][ Information ]
+Request starting HTTP/1.1 POST http://localhost/api/user
+```
+
+| 日志级别 | 控制台颜色 |
+|----------|-----------|
+| Debug | 深紫色 |
+| Information | 深青色 |
+| Warning | 黄色 |
+| Error | 红色 |
+| Critical | 深红色 |
+
+---
+
+## 6. 内置路由汇总
+
+| 路由 | 方法 | 启用条件 | 说明 |
+|------|------|----------|------|
+| `/swagger` | GET | `EnableSimApiDoc` | API 文档页面 |
+| `/versions` | GET/POST | `EnableVersionUrl` | 查看版本信息 |
+| `/auth/check` | POST | `EnableSimApiAuth` | 检测登录状态 |
+| `/auth/logout` | POST | `EnableSimApiAuth` | 退出登录 |
+| `/user/info` | POST | `EnableSimApiAuth` | 获取用户信息(需登录) |
+| `/auth/login` | POST | `EnableCoceSdk` | Coce 登录 |
+| `/user/groups` | POST | `EnableCoceSdk` | 获取用户群组(需登录) |
+| `/auth/config` | POST | `EnableCoceSdk` | 获取 Coce 配置 |
+| `/jobs` | GET | `EnableJob` | Hangfire Dashboard |
+
+---
+
+## 7. 异常处理机制
+
+```
+HTTP 请求
+ ↓
+SimApiExceptionMiddleware(最外层,透传 Query-Id Header)
+ ├── 捕获 SimApiException → 返回 {code: xxx, message: xxx}
+ ├── 捕获 Exception → 记录日志 + 返回 {code: 500, message: 错误信息}
+ └── 响应 404 等非 200/301/302 状态码 → 转换为 SimApiException
+ ↓
+SimApiAuthMiddleware(解析 Token → HttpContext.Items["LoginInfo"])
+ ↓
+[SimApiSign] Filter(签名验证)
+ ↓
+[SimApiAuth] Filter(登录/角色检查)
+ ↓
+Controller.OnActionExecuting(模型验证 → 400)
+ ↓
+Action 方法执行
+ ↓
+SimApiResponseFilter(自动封装响应)
+```
+
+> **关键特性**:所有错误均以 **HTTP 200** 状态码返回,错误信息体现在响应 JSON 的 `code` 字段中。
+
+---
+
+## 8. 完整配置示例
+
+```csharp
+builder.Services.AddSimApi(options =>
+{
+ // Redis(多功能共享)
+ options.RedisConfiguration = "localhost:6379";
+
+ // 认证
+ options.EnableSimApiAuth = true;
+
+ // Swagger 文档
+ options.EnableSimApiDoc = true;
+ options.ConfigureSimApiDoc(doc =>
+ {
+ doc.DocumentTitle = "我的服务接口文档";
+ doc.ApiGroups =
+ [
+ new("api", "公共接口"),
+ new("admin", "管理接口", "需要管理员 Token")
+ ];
+ doc.ApiAuth = new SimApiAuthOption { Type = ["SimApiAuth"] };
+ doc.SupportedMethod = [SubmitMethod.Post];
+ });
+
+ // S3 存储
+ options.EnableSimApiStorage = true;
+ options.ConfigureSimApiStorage(s =>
+ {
+ s.Endpoint = "http://minio:9000";
+ s.AccessKey = "admin";
+ s.SecretKey = "password";
+ s.Bucket = "my-bucket";
+ s.ServeUrl = "http://cdn.example.com/my-bucket";
+ });
+
+ // 任务调度
+ options.EnableJob = true;
+ options.ConfigureSimApiJob(job =>
+ {
+ job.DashboardUrl = "/jobs";
+ job.DashboardAuthUser = "admin";
+ job.DashboardAuthPass = "Admin@123!";
+ job.Servers = [new() { Queues = ["default"], WorkerNum = 5 }];
+ });
+
+ // MQTT 通信
+ options.EnableSynapse = true;
+ options.ConfigureSimApiSynapse(s =>
+ {
+ s.Websocket = "ws://mqtt:8083/mqtt";
+ s.Username = "user";
+ s.Password = "pass";
+ s.SysName = "my-system";
+ s.AppName = "api-service";
+ });
+
+ // Coce 统一身份
+ options.EnableCoceSdk = true;
+ options.ConfigureCoceSdk(coce =>
+ {
+ coce.AppId = "your-app-id";
+ coce.AppKey = "your-app-key";
+ });
+});
+
+var app = builder.Build();
+app.UseSimApi();
+app.Run();
+```
+
+---
+
+## 9. 项目依赖
+
+| 包 | 版本 | 用途 |
+|----|------|------|
+| `Hangfire.AspNetCore` | 1.8.22 | 任务调度框架 |
+| `Hangfire.Console` | 1.4.3 | 任务控制台日志 |
+| `Hangfire.Redis.StackExchange` | 1.12.0 | Hangfire Redis 存储 |
+| `Microsoft.Extensions.Caching.StackExchangeRedis` | 10.0.1 | Redis 分布式缓存 |
+| `Minio` | 7.0.0 | S3 对象存储 |
+| `MQTTnet` | 5.0.1.1416 | MQTT 消息通信 |
+| `Swashbuckle.AspNetCore.Annotations` | 10.1.0 | Swagger 注解 |
+| `Swashbuckle.AspNetCore.SwaggerUI` | 10.1.0 | Swagger UI |
+
+---
+
+## 10. 最佳实践
+
+### 10.1 控制器设计
+
+- 所有控制器继承 `SimApiBaseController`
+- 优先使用 `ErrorWhenNull` / `ErrorWhen` 系列方法进行前置校验,保持主逻辑清晰
+- 需要认证的接口标注 `[SimApiAuth]`,按角色访问控制的传入角色参数
+- 用 `[SimApiDoc]` 为每个接口添加文档注解,分组管理
+
+### 10.2 存储管理
+
+- 路径统一以 `/` 开头,按业务分类规划路径结构(如 `/avatars/{userId}/`)
+- 对公开资源使用 `GetUrl`,对私密资源使用 `GetDownloadUrl`(设合适过期时间)
+- 上传前在业务层验证文件类型和大小
+
+### 10.3 任务调度
+
+- 将长耗时操作异步化,接口立即返回,后台任务处理
+- 根据任务类型设置不同队列,避免低优先级任务阻塞高优先级任务
+- 定期检查 Hangfire Dashboard,监控失败任务
+
+### 10.4 Synapse 消息通信
+
+- 事件名使用层级路径风格(如 `order/created`、`payment/success`)
+- RPC 方法名使用简洁的语义名称
+- 为幂等性操作设计事件处理器(同一事件可能被多次投递)
+- 超时处理:`Rpc` 返回 `code=502` 表示超时
+
+### 10.5 安全
+
+- Token 认证默认不设过期时间,建议在业务层配合定期清理或设置 Redis TTL
+- 签名验证默认开启防重放(5 秒 nonce 缓存),生产环境保持开启
+- AES 密钥通过数据库存储,不硬编码在代码中
diff --git a/SimApi使用说明书.md b/SimApi使用说明书.md
deleted file mode 100644
index 9097cbd..0000000
--- a/SimApi使用说明书.md
+++ /dev/null
@@ -1,796 +0,0 @@
-# SimApi 库使用说明书
-
-## 1. 项目概述
-
-SimApi 是一个基于 .NET 的基础辅助包,提供了一系列实用功能,帮助开发者快速构建和部署 API 服务。
-
-### 主要功能特性:
-
-- **统一的参数检测和错误处理**:自动验证请求参数并返回标准化的错误响应
-- **基础认证服务**:基于 Header Token 的简单认证机制
-- **S3 兼容的存储系统**:支持文件上传、下载和管理
-- **任务调度系统**:基于 Hangfire 的后台任务管理
-- **事件和 RPC 调用**:基于 RabbitMQ 的事件和 RPC 通信
-- **自定义日志格式**:提供格式化的控制台日志
-- **在线 API 文档**:基于 Swagger 的 API 文档生成
-- **统一的响应格式**:标准化的 API 响应结构
-- **CORS 配置**:支持跨域资源共享
-- **版本管理**:提供应用版本和 SimApi 版本查询
-
-## 2. 安装方法
-
-### 通过 NuGet 安装:
-
-```bash
-Install-Package SimApi
-```
-
-### 项目集成
-
-在 `Startup.cs` 或 `Program.cs` 中配置 SimApi:
-
-```csharp
-// 在 ConfigureServices 方法中
-services.AddSimApi(options =>
-{
- // 配置选项
-});
-
-// 在 Configure 方法中
-app.UseSimApi();
-```
-
-## 3. 核心功能模块
-
-### 3.1 基础控制器
-
-所有控制器应继承自 `SimApiBaseController`,以获得统一的参数检测和错误处理功能。
-
-```csharp
-using SimApi.Controllers;
-
-public class BaseController : SimApiBaseController
-{
- ///
- /// 获取登录用户信息
- ///
- protected SimApiLoginItem LoginInfo => (SimApiLoginItem) HttpContext.Items["LoginInfo"];
-}
-```
-
-### 3.2 认证服务
-
-#### 配置认证服务:
-
-```csharp
-services.AddSimApi(options =>
-{
- options.EnableSimApiAuth = true;
-});
-```
-
-#### 使用认证:
-
-1. 在控制器或动作方法上添加 `[SimApiAuth]` 属性
-2. 登录用户信息可通过 `LoginInfo` 属性获取
-
-#### 认证相关接口:
-
-- `POST /auth/check`:检测用户登录状态
-- `POST /auth/logout`:用户退出登录
-- `POST /user/info`:获取用户信息
-
-### 3.3 存储服务
-
-#### 配置存储服务:
-
-```csharp
-services.AddSimApi(options =>
-{
- options.EnableSimApiStorage = true;
- options.SimApiStorageOptions = Configuration.GetSection("S3").Get();
-});
-```
-
-#### 存储配置选项:
-
-```json
-{
- "S3": {
- "Endpoint": "http://localhost:9000",
- "AccessKey": "minioadmin",
- "SecretKey": "minioadmin",
- "Bucket": "mybucket",
- "ServeUrl": "http://localhost:9000/mybucket"
- }
-}
-```
-
-#### 使用存储服务:
-
-```csharp
-private readonly SimApiStorage _storage;
-
-public MyController(SimApiStorage storage)
-{
- _storage = storage;
-}
-
-// 获取上传 URL
-var uploadUrlResponse = _storage.GetUploadUrl("/path/to/file.txt");
-
-// 获取下载 URL
-var downloadUrl = _storage.GetDownloadUrl("/path/to/file.txt");
-
-// 直接上传文件
-using var stream = new MemoryStream();
-_storage.UploadFile("/path/to/file.txt", stream, "text/plain");
-
-// 获取完整访问 URL
-var fullUrl = _storage.FullUrl("/path/to/file.txt");
-```
-
-### 3.4 任务调度系统
-
-#### 配置任务调度:
-
-```csharp
-services.AddSimApi(options =>
-{
- options.EnableJob = true;
- options.SimApiJobOptions = new SimApiJobOptions
- {
- DashboardUrl = "/jobs",
- DashboardAuthUser = "admin",
- DashboardAuthPass = "Admin@123!",
- RedisConfiguration = "localhost:6379",
- Servers = new[]
- {
- new SimApiJobServerConfig
- {
- Queues = new[] { "default" },
- WorkerNum = 50
- }
- }
- };
-});
-```
-
-#### 使用任务调度:
-
-```csharp
-// 立即执行任务
-BackgroundJob.Enqueue(() => Console.WriteLine("Hello, world!"));
-
-// 延迟执行任务
-BackgroundJob.Schedule(() => Console.WriteLine("Delayed job"), TimeSpan.FromMinutes(1));
-
-// 重复执行任务
-RecurringJob.AddOrUpdate("my-recurring-job", () => Console.WriteLine("Recurring job"), Cron.Hourly);
-
-// 连续执行任务
-var id = BackgroundJob.Enqueue(() => Console.WriteLine("First job"));
-BackgroundJob.ContinueWith(id, () => Console.WriteLine("Second job"));
-```
-
-### 3.5 事件和 RPC 调用
-
-#### 配置事件和 RPC:
-
-```csharp
-services.AddSimApi(options =>
-{
- options.EnableSynapse = true;
- options.SimApiSynapseOptions = new SimApiSynapseOptions
- {
- // 配置选项
- };
-});
-```
-
-#### 使用事件:
-
-```csharp
-// 发布事件
-var synapse = serviceProvider.GetRequiredService();
-synapse.PublishEvent("event-name", data);
-
-// 订阅事件
-[SynapseEvent("event-name")]
-public void HandleEvent(dynamic data)
-{
- // 处理事件
-}
-```
-
-#### 使用 RPC:
-
-```csharp
-// 发布 RPC 调用
-var result = await synapse.CallRpcAsync("rpc-method", data);
-
-// 实现 RPC 方法
-[SynapseRpc("rpc-method")]
-public string GetData(dynamic data)
-{
- return "Hello, RPC!";
-}
-```
-
-### 3.6 在线 API 文档
-
-#### 配置 API 文档:
-
-```csharp
-services.AddSimApi(options =>
-{
- options.EnableSimApiDoc = true;
- options.ConfigureSimApiDoc(docOptions =>
- {
- docOptions.ApiGroups = new[]
- {
- new SimApiDocGroupOption
- {
- Id = "admin",
- Name = "后台管理接口",
- Description = "本接口调用需要Scope:sac.api.admin"
- },
- new SimApiDocGroupOption
- {
- Id = "user-v1",
- Name = "用户中心接口",
- Description = "本接口调用需要Scope:sac.api.user"
- }
- };
- docOptions.ApiAuth = new SimApiAuthOption
- {
- Type = new[] { "ClientCredentials", "Implicit", "AuthorizationCode" },
- Scopes = new Dictionary
- {
- { "sac.api.user", "用户信息接口权限" },
- { "sac.api.admin", "后台管理API" }
- },
- AuthorizationUrl = "/connect/authorize",
- TokenUrl = "/connect/token"
- };
- });
-});
-```
-
-#### 访问 API 文档:
-
-启动应用后,访问 `/swagger` 查看 API 文档。
-
-### 3.7 统一响应格式
-
-#### 配置响应过滤器:
-
-```csharp
-services.AddSimApi(options =>
-{
- options.EnableSimApiResponseFilter = true;
-});
-```
-
-#### 响应过滤器实现:
-
-SimApi 提供了 `SimApiResponseFilter` 结果过滤器,用于自动封装 API 响应为统一格式:
-
-- 自动将 `null` 结果封装为 `{"Code": 200, "Message": "成功"}`
-- 自动将普通对象结果封装为 `{"Code": 200, "Message": "成功", "Data": 对象}`
-- 自动将 `EmptyResult` 封装为 `{"Code": 200, "Message": "成功"}`
-- 保持 `SimApiBaseResponse` 类型的结果不变
-
-#### 异常中间件:
-
-SimApi 还提供了 `SimApiExceptionMiddleware` 异常中间件,用于统一处理异常:
-
-- 捕获所有未处理的异常
-- 将异常转换为标准化的错误响应格式
-- 处理 HTTP 状态码,如 404 等
-- 记录错误日志
-
-#### 使用响应格式:
-
-```csharp
-// 无数据响应
-return new SimApiBaseResponse();
-
-// 带数据响应
-return new SimApiBaseResponse(user);
-
-// 直接返回对象,会自动被封装
-return user;
-
-// 错误响应
-Error(400, "参数错误");
-
-// 条件错误检查
-ErrorWhenNull(user, 404, "用户不存在");
-ErrorWhen(user.Age < 18, 403, "未满18岁,无权访问");
-```
-
-#### 原始响应标记:
-
-如果需要返回原始响应格式,不使用统一封装,可以在控制器或动作方法上添加 `[OriginResponse]` 属性:
-
-```csharp
-[HttpGet]
-[OriginResponse] // 返回原始响应格式
-public string GetRawData()
-{
- return "原始字符串响应";
-}
-```
-
-## 4. API 参考
-
-### 4.1 核心类
-
-#### SimApiUtil
-
-**命名空间**:`SimApi.Helpers`
-
-**描述**:提供一系列静态工具方法和属性,用于常见操作。
-
-**主要属性**:
-
-- `CstNow`:获取当前 CST(中国标准时间)
-- `JsonOption`:JSON 序列化常规选项
-- `SimApiVersion`:获取 SimApi 库版本
-- `AppVersion`:获取应用版本
-- `TimestampNow`:获取当前秒级时间戳
-
-**主要方法**:
-
-- `CheckCell(string cell)`:检测手机号是否正确
-- `Md5(string source, string mode = "x2")`:MD5 加密字符串
-- `Sha1(string source, string mode = "x2")`:SHA1 加密字符串
-- `XmlDeserialize(string source)`:将 XML 字符串序列化为对象
-- `Json(object? obj)`:将对象序列化为 JSON 字符串
-- `Paginate(this IQueryable query, int page, int count)`:分页扩展方法
-
-**使用示例**:
-
-```csharp
-// 获取当前时间
-var now = SimApiUtil.CstNow;
-
-// JSON 序列化
-var json = SimApiUtil.Json(new { Name = "Test", Age = 18 });
-
-// MD5 加密
-var md5 = SimApiUtil.Md5("password");
-
-// 分页
-var query = dbContext.Users.AsQueryable();
-var paginatedQuery = query.Paginate(1, 10);
-
-// 获取版本信息
-var simApiVersion = SimApiUtil.SimApiVersion;
-var appVersion = SimApiUtil.AppVersion;
-```
-
-#### SimApiExtensions
-
-**命名空间**:`SimApi`
-
-**描述**:提供一系列扩展方法,用于配置和使用 SimApi。
-
-**主要方法**:
-
-- `AddSimApi(this IServiceCollection builder, Action? options = null)`:向服务集合添加 SimApi 服务和配置
-- `UseSimApi(this IHost builder)`:在主机上使用 SimApi
-- `UseSimApi(this WebApplication builder)`:在 Web 应用上使用 SimApi,配置中间件和路由
-
-**使用示例**:
-
-```csharp
-// 在 ConfigureServices 方法中
-services.AddSimApi(options =>
-{
- // 配置选项
- options.EnableSimApiDoc = true;
- options.EnableSimApiAuth = true;
- // 其他配置...
-});
-
-// 在 Configure 方法中
-app.UseSimApi();
-```
-
-#### SimApiBaseController
-
-**继承自**:`Controller`
-
-**主要方法**:
-
-- `Error(int code = 500, string message = "")`:抛出错误异常
-- `ErrorWhen(bool condition, int code = 400, string message = "")`:当条件为真时抛出错误
-- `ErrorWhenNull(object? condition, int code = 404, string message = "请求的资源不存在")`:当对象为 null 时抛出错误
-- `UploadFile()`:上传文件
-
-**属性**:
-
-- `LoginInfo`:获取当前登录用户信息
-
-#### SimApiAuth
-
-**主要方法**:
-
-- `Login(SimApiLoginItem loginItem, string? token = null)`:登录用户并返回 token
-- `Update(SimApiLoginItem loginItem, string token)`:更新用户登录信息
-- `GetLogin(string token)`:根据 token 获取登录信息
-- `Logout(string uuid)`:退出登录
-
-#### SimApiStorage
-
-**主要方法**:
-
-- `GetUploadUrl(string path, int expire = 7200)`:获取文件上传 URL
-- `GetDownloadUrl(string path, int expire = 600)`:获取文件下载 URL
-- `UploadFile(string path, Stream stream, string contentType = "image/png")`:上传文件
-- `FullUrl(string? path)`:获取完整的文件访问 URL
-- `GetUrl(string? path)`:获取文件访问 URL
-- `GetPath(string? url)`:从 URL 中获取相对路径
-
-#### SimApiBaseResponse
-
-**构造函数**:
-
-- `SimApiBaseResponse(int code = 200, string message = "成功")`:创建响应对象
-
-**属性**:
-
-- `Code`:响应代码
-- `Message`:响应消息
-
-#### SimApiBaseResponse
-
-**继承自**:`SimApiBaseResponse`
-
-**构造函数**:
-
-- `SimApiBaseResponse(T data)`:创建带数据的响应对象
-
-**属性**:
-
-- `Data`:响应数据
-
-### 4.2 配置类
-
-#### SimApiOptions
-
-**主要属性**:
-
-- `RedisConfiguration`:Redis 配置字符串
-- `EnableJob`:是否启用任务调度系统
-- `EnableSimApiAuth`:是否启用认证服务
-- `EnableCoceSdk`:是否启用 CoceSdk
-- `EnableSimApiStorage`:是否启用存储服务
-- `EnableSimApiDoc`:是否启用 API 文档
-- `EnableSynapse`:是否启用事件和 RPC
-- `EnableCors`:是否启用 CORS
-- `EnableSimApiException`:是否启用异常拦截
-- `EnableSimApiResponseFilter`:是否启用响应过滤器
-- `EnableForwardHeaders`:是否启用 Header 转发
-- `EnableLowerUrl`:是否启用小写 URL
-- `EnableVersionUrl`:是否启用版本查询
-- `EnableLogger`:是否启用自定义日志
-
-**配置方法**:
-
-- `ConfigureSimApiDoc(Action? options = null)`:配置 API 文档
-- `ConfigureSimApiStorage(Action? options = null)`:配置存储服务
-- `ConfigureSimApiJob(Action? options = null)`:配置任务调度
-- `ConfigureSimApiSynapse(Action? options = null)`:配置事件和 RPC
-- `ConfigureCoceSdk(Action? options = null)`:配置 CoceSdk
-
-## 5. 配置选项
-
-### 5.1 存储配置 (SimApiStorageOptions)
-
-```csharp
-public class SimApiStorageOptions
-{
- public string? Endpoint { get; set; } // S3 服务端点
- public string? AccessKey { get; set; } // 访问密钥
- public string? SecretKey { get; set; } // 密钥
- public string? Bucket { get; set; } // 存储桶名称
- public string? ServeUrl { get; set; } // 访问 URL
-}
-```
-
-### 5.2 任务调度配置 (SimApiJobOptions)
-
-```csharp
-public class SimApiJobOptions
-{
- public string? DashboardUrl { get; set; } = "/jobs"; // Web UI 地址
- public string DashboardAuthUser { get; set; } = "admin"; // Web UI 用户名
- public string DashboardAuthPass { get; set; } = "Admin@123!"; // Web UI 密码
- public string? RedisConfiguration { get; set; } // Redis 配置
- public int? Database { get; set; } = null; // Redis 数据库
- public SimApiJobServerConfig[] Servers { get; set; } = [new()]; // 服务器配置
-}
-
-public class SimApiJobServerConfig
-{
- public string[] Queues { get; set; } = ["default"]; // 队列名称
- public int WorkerNum { get; set; } = 50; // 工作线程数
-}
-```
-
-### 5.3 API 文档配置 (SimApiDocOptions)
-
-```csharp
-public class SimApiDocOptions
-{
- public string DocumentTitle { get; set; } = "API 文档"; // 文档标题
- public SimApiDocGroupOption[] ApiGroups { get; set; } = []; // API 分组
- public SimApiAuthOption ApiAuth { get; set; } = new(); // 认证配置
- public string[] SupportedMethod { get; set; } = ["GET", "POST", "PUT", "DELETE"]; // 支持的 HTTP 方法
-}
-
-public class SimApiDocGroupOption
-{
- public string Id { get; set; } = "api"; // 分组 ID
- public string Name { get; set; } = "API"; // 分组名称
- public string Description { get; set; } = ""; // 分组描述
-}
-
-public class SimApiAuthOption
-{
- public string[] Type { get; set; } = []; // 认证类型
- public Dictionary Scopes { get; set; } = []; // 权限范围
- public string AuthorizationUrl { get; set; } = "/connect/authorize"; // 授权 URL
- public string TokenUrl { get; set; } = "/connect/token"; // Token URL
- public string Description { get; set; } = ""; // 认证描述
-}
-```
-
-## 6. 使用示例
-
-### 6.1 完整配置示例
-
-```csharp
-services.AddSimApi(options =>
-{
- // 配置 Redis
- options.RedisConfiguration = "localhost:6379";
-
- // 配置 API 文档
- options.EnableSimApiDoc = true;
- options.ConfigureSimApiDoc(docOptions =>
- {
- docOptions.ApiGroups = new[]
- {
- new SimApiDocGroupOption
- {
- Id = "admin",
- Name = "后台管理接口",
- Description = "本接口调用需要Scope:sac.api.admin"
- },
- new SimApiDocGroupOption
- {
- Id = "user-v1",
- Name = "用户中心接口",
- Description = "本接口调用需要Scope:sac.api.user"
- }
- };
- docOptions.ApiAuth = new SimApiAuthOption
- {
- Type = new[] { "ClientCredentials", "Implicit", "AuthorizationCode" },
- Scopes = new Dictionary
- {
- { "sac.api.user", "用户信息接口权限" },
- { "sac.api.admin", "后台管理API" }
- },
- AuthorizationUrl = "/connect/authorize",
- TokenUrl = "/connect/token"
- };
- });
-
- // 配置存储服务
- options.EnableSimApiStorage = true;
- options.SimApiStorageOptions = Configuration.GetSection("S3").Get();
-
- // 配置任务调度
- options.EnableJob = true;
- options.ConfigureSimApiJob(jobOptions =>
- {
- jobOptions.DashboardUrl = "/jobs";
- jobOptions.DashboardAuthUser = "admin";
- jobOptions.DashboardAuthPass = "Admin@123!";
- });
-
- // 配置事件和 RPC
- options.EnableSynapse = true;
-
- // 其他配置
- options.EnableCors = true;
- options.EnableSimApiException = true;
- options.EnableSimApiResponseFilter = true;
- options.EnableVersionUrl = true;
- options.EnableLogger = true;
-});
-
-// 使用 SimApi
-app.UseSimApi();
-```
-
-### 6.2 控制器示例
-
-```csharp
-using Microsoft.AspNetCore.Mvc;
-using SimApi.Controllers;
-using SimApi.Helpers;
-
-[ApiController]
-[Route("[controller]")]
-public class UserController : BaseController
-{
- private readonly SimApiStorage _storage;
-
- public UserController(SimApiStorage storage)
- {
- _storage = storage;
- }
-
- [HttpGet("{id}")]
- public SimApiBaseResponse GetUser(int id)
- {
- var user = GetUserFromDatabase(id);
- ErrorWhenNull(user, 404, "用户不存在");
- return new SimApiBaseResponse(user);
- }
-
- [HttpPost]
- [SimApiAuth] // 需要认证
- public SimApiBaseResponse CreateUser(UserCreateDto dto)
- {
- ErrorWhen(string.IsNullOrEmpty(dto.Name), 400, "用户名不能为空");
- ErrorWhen(dto.Age < 18, 400, "年龄必须大于18岁");
-
- var user = CreateUserInDatabase(dto);
- return new SimApiBaseResponse(user);
- }
-
- [HttpPost("upload-avatar")]
- [SimApiAuth]
- public async Task> UploadAvatar(IFormFile file)
- {
- using var stream = file.OpenReadStream();
- var path = $"/avatars/{LoginInfo.Id}/{Guid.NewGuid()}{Path.GetExtension(file.FileName)}";
- _storage.UploadFile(path, stream, file.ContentType);
- var url = _storage.GetUrl(path);
- return new SimApiBaseResponse(url);
- }
-}
-```
-
-### 6.3 任务调度示例
-
-```csharp
-public class UserService
-{
- public void SendWelcomeEmail(string email)
- {
- // 发送欢迎邮件
- Console.WriteLine($"Sending welcome email to {email}");
- }
-
- public void CleanupInactiveUsers()
- {
- // 清理不活跃用户
- Console.WriteLine("Cleaning up inactive users");
- }
-
- public void GenerateMonthlyReport()
- {
- // 生成月度报告
- Console.WriteLine("Generating monthly report");
- }
-}
-
-// 配置任务
-public void ConfigureJobs(IServiceProvider serviceProvider)
-{
- // 立即发送欢迎邮件
- BackgroundJob.Enqueue(x => x.SendWelcomeEmail("user@example.com"));
-
- // 每天凌晨清理不活跃用户
- RecurringJob.AddOrUpdate("cleanup-inactive-users", x => x.CleanupInactiveUsers(), Cron.Daily);
-
- // 每月1日生成月度报告
- RecurringJob.AddOrUpdate("generate-monthly-report", x => x.GenerateMonthlyReport(), "0 0 1 * *");
-}
-```
-
-## 7. 最佳实践
-
-### 7.1 控制器设计
-
-- 所有控制器应继承自 `SimApiBaseController` 或其派生类
-- 使用 `Error` 和 `ErrorWhen` 系列方法进行错误处理
-- 对需要认证的接口使用 `[SimApiAuth]` 属性
-- 合理使用 API 分组,便于文档管理
-
-### 7.2 存储管理
-
-- 为不同类型的文件使用不同的存储路径结构
-- 合理设置文件 URL 的过期时间
-- 对上传的文件进行验证和处理
-- 考虑使用 CDN 加速文件访问
-
-### 7.3 任务调度
-
-- 合理设置任务的队列和优先级
-- 对长时间运行的任务进行分解
-- 监控任务的执行状态和结果
-- 合理设置任务的重试策略
-
-### 7.4 事件和 RPC
-
-- 为事件和 RPC 方法使用清晰的命名规范
-- 合理设计事件和 RPC 的数据结构
-- 考虑事件处理的幂等性
-- 监控事件和 RPC 的执行情况
-
-### 7.5 配置管理
-
-- 使用配置文件或环境变量管理配置
-- 对敏感配置进行加密处理
-- 不同环境使用不同的配置
-- 定期审查和更新配置
-
-### 7.6 性能优化
-
-- 合理使用缓存减少数据库访问
-- 对高频访问的接口进行优化
-- 考虑使用异步方法提高并发性能
-- 监控系统性能并进行调优
-
-## 8. 故障排查
-
-### 8.1 常见问题
-
-#### 认证失败
-- 检查 Token 是否正确
-- 检查 Redis 是否正常运行
-- 检查认证中间件是否正确配置
-
-#### 存储服务错误
-- 检查 S3 服务是否正常运行
-- 检查存储配置是否正确
-- 检查网络连接是否正常
-
-#### 任务调度错误
-- 检查 Hangfire 仪表盘是否可访问
-- 检查 Redis 是否正常运行
-- 检查任务代码是否有异常
-
-#### API 文档生成错误
-- 检查 Swagger 配置是否正确
-- 检查控制器和方法的注释是否完整
-- 检查模型类是否有循环引用
-
-### 8.2 日志和监控
-
-- 启用 `EnableLogger` 配置查看详细日志
-- 使用应用性能监控工具监控系统状态
-- 定期检查系统日志和错误报告
-- 设置关键指标的告警机制
-
-## 9. 版本管理
-
-- 访问 `/versions` 查看应用版本和 SimApi 版本
-- 定期更新 SimApi 到最新版本
-- 注意版本升级时的兼容性问题
-- 遵循语义化版本规范管理应用版本
-
-## 10. 总结
-
-SimApi 是一个功能丰富的 .NET 基础辅助包,提供了一系列实用功能,帮助开发者快速构建和部署 API 服务。通过合理配置和使用 SimApi,可以显著提高开发效率,减少重复代码,提高系统的可维护性和可靠性。
-
-本说明书提供了 SimApi 的详细使用方法和最佳实践,希望能帮助开发者更好地使用这个库。如果有任何问题或建议,欢迎反馈和贡献。
\ No newline at end of file