From eed230aab080a9e20baca52791afba0b6d3023d5 Mon Sep 17 00:00:00 2001 From: xRain Date: Tue, 1 Sep 2026 20:31:26 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E4=BA=86=E9=83=A8=E5=88=86li?= =?UTF-8?q?nt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 98 +++++++++---------- cjpm.lock | 22 ++--- cjpm.toml | 2 +- src/controllers/simapi_common_controller.cj | 2 +- src/openapi/OpenApiUIMiddleware.cj | 5 +- .../infrastructure/OpenApiConstants.cj | 6 +- src/openapi/models/OpenApiComponents.cj | 4 +- src/openapi/models/OpenApiDocument.cj | 6 +- src/openapi/models/OpenApiOperation.cj | 10 +- src/openapi/models/OpenApiPathItem.cj | 6 +- src/openapi/models/OpenApiRequestBody.cj | 2 +- src/openapi/models/OpenApiResponse.cj | 2 +- src/openapi/models/OpenApiSchema.cj | 6 +- src/openapi/models/OpenApiSchemaTypes.cj | 6 +- .../services/OpenApiDocumentService.cj | 2 +- 15 files changed, 91 insertions(+), 88 deletions(-) diff --git a/README.md b/README.md index 3ebb75d..12af8bb 100644 --- a/README.md +++ b/README.md @@ -368,25 +368,25 @@ SimApiExtensions.addSimApi(builder) { options => } ``` -| 方法 | 说明 | -|------|------| -| `getUploadUrl(path, expire=7200)` | 上传预签名 URL,返回 `GetUploadUrlResponse(UploadUrl, DownloadUrl, Path)` | -| `getDownloadUrl(path, expire=600)` | 下载预签名 URL | -| `uploadFile(path, data, contentType="image/png")` | 直接 PUT 上传(字节数组) | -| `deleteFiles(paths)` | 批量删除对象(S3 原生 DeleteObjects,一次请求删多个) | -| `fullUrl(path)` / `getUrl(path)` | 补全访问 URL(`~/` 前缀依赖请求上下文) | -| `getPath(url)` | 从 URL 还原相对路径(去掉 Endpoint/Bucket 或 ServeUrl 前缀) | +| 方法 | 说明 | +| ------------------------------------------------- | ------------------------------------------------------------------------- | +| `getUploadUrl(path, expire=7200)` | 上传预签名 URL,返回 `GetUploadUrlResponse(UploadUrl, DownloadUrl, Path)` | +| `getDownloadUrl(path, expire=600)` | 下载预签名 URL | +| `uploadFile(path, data, contentType="image/png")` | 直接 PUT 上传(字节数组) | +| `deleteFiles(paths)` | 批量删除对象(S3 原生 DeleteObjects,一次请求删多个) | +| `fullUrl(path)` / `getUrl(path)` | 补全访问 URL(`~/` 前缀依赖请求上下文) | +| `getPath(url)` | 从 URL 还原相对路径(去掉 Endpoint/Bucket 或 ServeUrl 前缀) | > 说明:桶不存在时自动创建(静态守卫只执行一次); > 预签名与上传使用 AWS SigV4(HMAC-SHA256 基于 stdx SHA256 自实现),已用 AWS 官方测试向量验证签名正确。 ### 6. 内置路由(UseSimApi 自动注册) -| 路由 | 方法 | 条件 | 说明 | -| ----------------- | -------- | ---------------------------- | -------------------------- | -| `/user/info` | POST | `enableSimApiAuth` | 需登录,返回 LoginInfo | -| `/auth/logout` | POST | `enableSimApiAuth` | 退出登录 | -| `/exception/{code}` | GET | 始终 | 错误反馈(抛 SimApiException,不出现在文档中) | +| 路由 | 方法 | 条件 | 说明 | +| ------------------- | ---- | ------------------ | ---------------------------------------------- | +| `/user/info` | POST | `enableSimApiAuth` | 需登录,返回 LoginInfo | +| `/auth/logout` | POST | `enableSimApiAuth` | 退出登录 | +| `/exception/{code}` | GET | 始终 | 错误反馈(抛 SimApiException,不出现在文档中) | 路由路径可自定义(`configureSimApiRoute`): @@ -427,11 +427,11 @@ SimApiExtensions.addSimApi(builder) { options => } ``` -| 类 | 说明 | -|----|------| -| `SimApiAuthClient` | `SimApiHttpClient` 子类,凭证取 AuthCenterOptions | -| `SimApiAuthCenter` | 群组/Profile/内部应用/系统登录/安全验证等 12 个接口 + `VerifySign` | -| `SimApiAuthIam` | 注册权限 / 获取权限标识 / 校验权限(无权限抛 403) | +| 类 | 说明 | +| ---------------------------- | ------------------------------------------------------------------------------ | +| `SimApiAuthClient` | `SimApiHttpClient` 子类,凭证取 AuthCenterOptions | +| `SimApiAuthCenter` | 群组/Profile/内部应用/系统登录/安全验证等 12 个接口 + `VerifySign` | +| `SimApiAuthIam` | 注册权限 / 获取权限标识 / 校验权限(无权限抛 403) | | `SimApiAuthCenterMiddleware` | 网关透传:`X-SimApi-Gate-Auth/Time/Sign` 三头 MD5 校验 → Base64 解码 LoginInfo | ```cangjie @@ -491,12 +491,12 @@ options.configureSimApiDoc { doc => } ``` -| 路由 | 说明 | -|------|------| -| `/{prefix}/all.html` | 多文档切换页(顶部栏下拉选择所有文档) | +| 路由 | 说明 | +| --------------------- | ------------------------------------------ | +| `/{prefix}/all.html` | 多文档切换页(顶部栏下拉选择所有文档) | | `/{prefix}/{id}.html` | 单文档页(无顶部栏,自动加载 `{id}.json`) | -| `/{prefix}/urls` | 文档列表 JSON(供 Swagger UI 下拉) | -| `/{prefix}/{id}.json` | OpenAPI 文档 JSON | +| `/{prefix}/urls` | 文档列表 JSON(供 Swagger UI 下拉) | +| `/{prefix}/{id}.json` | OpenAPI 文档 JSON | > Swagger UI 静态资源(CSS/JS/HTML)以 Base64 内联编译,运行时由 `OpenApiUIMiddleware` 解码输出。资源更新后运行 `pwsh tools/gen-swagger-ui-resources.ps1` 重新生成。 @@ -504,13 +504,13 @@ options.configureSimApiDoc { doc => 接口返回值自动封装为统一响应格式,**文档中 response schema 也体现封装**: -| 返回类型 | `@OriginResponse` | 文档 response schema | -|----------|-------------------|---------------------| -| `SimApiBaseResponse`/`SimApiResponse`/`SimApiDataResponse` | - | 原样不封装 | -| 任意类型 | ✓ | 原样不封装 | -| `Unit` (void) | ✗ | `{code: int64, message: string}` | -| `String` | ✗ | `{code, message, data: {type: string}}` | -| DTO | ✗ | `{code, message, data: {$ref: DTO}}` | +| 返回类型 | `@OriginResponse` | 文档 response schema | +| ------------------------------------------------------------- | ----------------- | --------------------------------------- | +| `SimApiBaseResponse`/`SimApiResponse`/`SimApiDataResponse` | - | 原样不封装 | +| 任意类型 | ✓ | 原样不封装 | +| `Unit` (void) | ✗ | `{code: int64, message: string}` | +| `String` | ✗ | `{code, message, data: {type: string}}` | +| DTO | ✗ | `{code, message, data: {$ref: DTO}}` | ### 认证锁图标 @@ -625,11 +625,11 @@ SimApiExtensions.addSimApi(builder) { options => simapi 提供 Spire MVC 控制器(继承 `SimApiBaseController`),`addSimApi` 自动注册内置控制器 + 自动扫描调用者包中的控制器: -| 控制器 | 路由 | 说明 | -|--------|------|------| -| `SimApiCommonController` | `/exception/{code}`、`/config`、`/user/info` | 通用内置路由 | -| `SimApiAuthController` | `/auth/logout` | 退出登录 | -| `SimApiBaseController` | — | 基类:`loginInfo` / `loginToken`(访问时自动校验登录,未登录抛 401) | +| 控制器 | 路由 | 说明 | +| ------------------------ | -------------------------------------------- | -------------------------------------------------------------------- | +| `SimApiCommonController` | `/exception/{code}`、`/config`、`/user/info` | 通用内置路由 | +| `SimApiAuthController` | `/auth/logout` | 退出登录 | +| `SimApiBaseController` | — | 基类:`loginInfo` / `loginToken`(访问时自动校验登录,未登录抛 401) | ```cangjie import simcu::simapi.controllers.* @@ -656,10 +656,10 @@ public class MyController <: SimApiBaseController { 以下原包功能因仓颉生态暂无对应库(Hangfire/MQTT),**选项保留但未实现**: -| 选项 | 原功能 | 状态 | -|------|--------|------| -| `enableSynapse` | MQTT 通信 | ❌ 未实现 | -| `enableJob` | Hangfire 任务调度 | ❌ 未实现 | +| 选项 | 原功能 | 状态 | +| --------------- | ----------------- | -------- | +| `enableSynapse` | MQTT 通信 | ❌ 未实现 | +| `enableJob` | Hangfire 任务调度 | ❌ 未实现 | > ✅ 已实现(曾为占位):`enableSimApiDoc`(OpenAPI 文档 + Swagger UI)、`enableSimApiStorage`(S3/MinIO,自实现 AWS SigV4)、`enableSimApiAuthGate`(AuthSDK 认证中心)、`SimApiAesUtil`(纯仓颉 AES-256-CBC)、`SimApiAuthChecker`、`@SimApiSign` / `@AesBody` 声明式注解、内置路由自定义路径。 @@ -667,16 +667,16 @@ public class MyController <: SimApiBaseController { ## 依赖 -| 依赖 | 用途 | -|------|------| -| `soulsoft_web_http / routing / hosting` | Web 框架 | -| `soulsoft_extensions_logging` 系列 | 日志 | -| `soulsoft_extensions_injection` | 依赖注入 | -| `soulsoft_extensions_configuration` | 配置 | -| `simcu::serialization`(path 依赖) | JSON 序列化(simapi 自研,反射免标注) | -| `redis`(pkg.cangjie-lang.cn) | Redis 客户端(认证/缓存 Redis 模式) | -| `stdx`(CANGJIE_STDX_PATH) | 标准扩展库(md5/sha1/base64/http/tls) | -| `soulsoft_web_mvc` | MVC 框架(控制器路由、模型绑定) | +| 依赖 | 用途 | +| --------------------------------------- | -------------------------------------- | +| `soulsoft_web_http / routing / hosting` | Web 框架 | +| `soulsoft_extensions_logging` 系列 | 日志 | +| `soulsoft_extensions_injection` | 依赖注入 | +| `soulsoft_extensions_configuration` | 配置 | +| `simcu::serialization`(path 依赖) | JSON 序列化(simapi 自研,反射免标注) | +| `redis`(pkg.cangjie-lang.cn) | Redis 客户端(认证/缓存 Redis 模式) | +| `stdx`(CANGJIE_STDX_PATH) | 标准扩展库(md5/sha1/base64/http/tls) | +| `soulsoft_web_mvc` | MVC 框架(控制器路由、模型绑定) | > 构建前需设置 `CANGJIE_STDX_PATH` 指向本地 stdx 的 `static/stdx` 目录。 > OpenAPI Swagger UI 静态资源内置(Base64 内联),更新资源后运行 `pwsh tools/gen-swagger-ui-resources.ps1`。 diff --git a/cjpm.lock b/cjpm.lock index bd4ef37..2c3c156 100644 --- a/cjpm.lock +++ b/cjpm.lock @@ -4,17 +4,17 @@ version = 0 soulsoft_extensions_hosting = {version = "1.0.20260528"} soulsoft_extensions_options_configuration = {version = "1.0.20260528"} soulsoft_extensions_logging_console = {version = "1.0.20260528"} - redis = {version = "1.0.20260627"} - soulsoft_extensions_logging_configuration = {version = "1.0.20260528"} - soulsoft_identity_claims = {version = "1.0.20260528"} - soulsoft_web_http = {version = "1.0.20260528"} - "simcu::serialization" = {version = "1.2.1"} - soulsoft_extensions_configuration = {version = "1.0.20260528"} - soulsoft_web_routing = {version = "1.0.20260528"} - soulsoft_web_cors = {version = "1.0.20260528"} - soulsoft_extensions_injection = {version = "1.0.20260528"} - soulsoft_web_mvc = {version = "1.0.20260528"} + soulsoft_extensions_logging = {version = "1.0.20260528"} soulsoft_serialization = {version = "1.0.20260528"} soulsoft_extensions_options = {version = "1.0.20260528"} - soulsoft_extensions_logging = {version = "1.0.20260528"} + soulsoft_web_routing = {version = "1.0.20260528"} + soulsoft_identity_claims = {version = "1.0.20260528"} + soulsoft_extensions_logging_configuration = {version = "1.0.20260528"} + soulsoft_web_http = {version = "1.0.20260528"} + soulsoft_extensions_configuration = {version = "1.0.20260528"} + soulsoft_web_cors = {version = "1.0.20260528"} + soulsoft_extensions_injection = {version = "1.0.20260528"} soulsoft_web_hosting = {version = "1.0.20260528"} + soulsoft_web_mvc = {version = "1.0.20260528"} + redis = {version = "1.0.20260627"} + "simcu::serialization" = {version = "1.2.1"} diff --git a/cjpm.toml b/cjpm.toml index 30351f2..b698c96 100644 --- a/cjpm.toml +++ b/cjpm.toml @@ -3,7 +3,7 @@ cjc-version = "1.1.3" name = "simapi" organization = "simcu" description = "SimApi 仓颉版:ASP.NET Core 风格 API 基础框架(统一响应/异常拦截/Token认证/缓存/工具集/HTTP客户端)" -version = "1.1.0" +version = "1.1.2" target-dir = "" output-type = "static" override-compile-option = "" diff --git a/src/controllers/simapi_common_controller.cj b/src/controllers/simapi_common_controller.cj index 2cda814..a479b54 100644 --- a/src/controllers/simapi_common_controller.cj +++ b/src/controllers/simapi_common_controller.cj @@ -31,7 +31,7 @@ public class SimApiCommonController <: SimApiBaseController { */ @HttpGet["exception/{code}"] @SimApiDoc[ignore: true] - public func exceptionHandler(@FromRoute[] code: Int64) { // cjlint-ignore !G.FUN.02 注解绑定参数误报 + public func exceptionHandler(@FromRoute[] code: Int64): Unit { // cjlint-ignore !G.FUN.02 注解绑定参数误报 SimApiError.error(code: code) } diff --git a/src/openapi/OpenApiUIMiddleware.cj b/src/openapi/OpenApiUIMiddleware.cj index 44c88ff..9d8443b 100644 --- a/src/openapi/OpenApiUIMiddleware.cj +++ b/src/openapi/OpenApiUIMiddleware.cj @@ -92,7 +92,10 @@ public class OpenApiUIMiddleware <: IMiddleware { private func serveFromWebRoot(context: HttpContext, relativePath: String): Bool { let openApiRoot = Path(_env.webRootPath).join(_wwwRootSubDir) let path = openApiRoot.join(relativePath).normalize() - if (!exists(path) || !isPathWithinRoot(path)) { + if (!exists(path)) { + return false + } + if (!isPathWithinRoot(path)) { return false } diff --git a/src/openapi/infrastructure/OpenApiConstants.cj b/src/openapi/infrastructure/OpenApiConstants.cj index 184a44c..5aa23e5 100644 --- a/src/openapi/infrastructure/OpenApiConstants.cj +++ b/src/openapi/infrastructure/OpenApiConstants.cj @@ -7,7 +7,7 @@ package simcu::simapi.openapi.infrastructure protected class OpenApiConstants { - public static let DefaultOpenApiName = "default" - public static let DefaultDocumentName = "v1" - public static let DefaultOpenApiRoute = "/openapi/{documentName}.json" + public static let DefaultOpenApiName: String = "default" + public static let DefaultDocumentName: String = "v1" + public static let DefaultOpenApiRoute: String = "/openapi/{documentName}.json" } \ No newline at end of file diff --git a/src/openapi/models/OpenApiComponents.cj b/src/openapi/models/OpenApiComponents.cj index 2095893..ee15396 100644 --- a/src/openapi/models/OpenApiComponents.cj +++ b/src/openapi/models/OpenApiComponents.cj @@ -18,12 +18,12 @@ public class OpenApiComponents <: IOpenApiSerializable { /** * @brief 表示组件中的架构集合。 */ - public var schemas = HashMap() + public var schemas: HashMap = HashMap() /** * @brief 表示组件中的安全方案集合。 */ - public var securitySchemes = HashMap() + public var securitySchemes: HashMap = HashMap() /** * @brief 创建 OpenAPI 组件实例。 diff --git a/src/openapi/models/OpenApiDocument.cj b/src/openapi/models/OpenApiDocument.cj index 53bf4f8..e9b68e6 100644 --- a/src/openapi/models/OpenApiDocument.cj +++ b/src/openapi/models/OpenApiDocument.cj @@ -34,15 +34,15 @@ public class OpenApiDocument <: IOpenApiSerializable { /** * @brief 表示标签集合。 */ - public var tags = ArrayList() + public var tags: ArrayList = ArrayList() /** * @brief 表示服务器集合。 */ - public var servers = ArrayList() + public var servers: ArrayList = ArrayList() /** * @brief 表示安全需求集合。 */ - public var securityRequirements = ArrayList() + public var securityRequirements: ArrayList = ArrayList() /** * @brief 创建 OpenAPI 文档对象。 diff --git a/src/openapi/models/OpenApiOperation.cj b/src/openapi/models/OpenApiOperation.cj index 4545495..e11a5e0 100644 --- a/src/openapi/models/OpenApiOperation.cj +++ b/src/openapi/models/OpenApiOperation.cj @@ -30,15 +30,15 @@ public class OpenApiOperation <: IOpenApiSerializable { /** * @brief 表示操作响应集合。 */ - public var response = OpenApiResponses() + public var response: OpenApiResponses = OpenApiResponses() /** * @brief 表示操作标签集合。 */ - public var tags = ArrayList() + public var tags: ArrayList = ArrayList() /** * @brief 表示操作服务器集合。 */ - public var servers = ArrayList() + public var servers: ArrayList = ArrayList() /** * @brief 表示操作请求体。 */ @@ -46,11 +46,11 @@ public class OpenApiOperation <: IOpenApiSerializable { /** * @brief 表示操作参数集合。 */ - public var parameters = ArrayList() + public var parameters: ArrayList = ArrayList() /** * @brief 表示操作安全需求集合。 */ - public var security = ArrayList() + public var security: ArrayList = ArrayList() /** * @brief 创建 OpenAPI 操作对象。 diff --git a/src/openapi/models/OpenApiPathItem.cj b/src/openapi/models/OpenApiPathItem.cj index 415c375..8cef91c 100644 --- a/src/openapi/models/OpenApiPathItem.cj +++ b/src/openapi/models/OpenApiPathItem.cj @@ -34,15 +34,15 @@ public class OpenApiPathItem <: IOpenApiSerializable { /** * @brief 表示路径项服务器集合。 */ - public var servers = ArrayList() + public var servers: ArrayList = ArrayList() /** * @brief 表示路径项参数集合。 */ - public var parameters = ArrayList() + public var parameters: ArrayList = ArrayList() /** * @brief 表示路径项操作集合。 */ - public var operations = HashMap() + public var operations: HashMap = HashMap() /** * @brief 创建 OpenAPI 路径项对象。 diff --git a/src/openapi/models/OpenApiRequestBody.cj b/src/openapi/models/OpenApiRequestBody.cj index c2905b9..98fba63 100644 --- a/src/openapi/models/OpenApiRequestBody.cj +++ b/src/openapi/models/OpenApiRequestBody.cj @@ -26,7 +26,7 @@ public class OpenApiRequestBody <: IOpenApiSerializable { /** * @brief 表示请求体内容映射。 */ - public var content = HashMap() + public var content: HashMap = HashMap() /** * @brief 创建 OpenAPI 请求体对象。 diff --git a/src/openapi/models/OpenApiResponse.cj b/src/openapi/models/OpenApiResponse.cj index 4bf3b34..c8e2db8 100644 --- a/src/openapi/models/OpenApiResponse.cj +++ b/src/openapi/models/OpenApiResponse.cj @@ -22,7 +22,7 @@ public class OpenApiResponse <: IOpenApiSerializable { /** * @brief 表示响应内容映射。 */ - public var content = HashMap() + public var content: HashMap = HashMap() /** * @brief 创建 OpenAPI 响应对象。 diff --git a/src/openapi/models/OpenApiSchema.cj b/src/openapi/models/OpenApiSchema.cj index 6dd6b9c..ed27fe7 100644 --- a/src/openapi/models/OpenApiSchema.cj +++ b/src/openapi/models/OpenApiSchema.cj @@ -46,11 +46,11 @@ public class OpenApiSchema <: IOpenApiSerializable { /** * @brief 表示对象属性集合。 */ - public var properties = HashMap() + public var properties: HashMap = HashMap() /** * @brief 表示 allOf 组合架构。 */ - public var allOf = ArrayList() + public var allOf: ArrayList = ArrayList() /** * @brief 表示是否允许附加属性。 */ @@ -70,7 +70,7 @@ public class OpenApiSchema <: IOpenApiSerializable { * @param reference 架构引用。 * @param description 架构描述。 */ - public init(title!: ?String = None, nullable!: ?Bool = None, `type`!: ?String = None, items!: ?OpenApiSchema = None, + public init(title!: ?String = None, nullable!: ?Bool = None, `type`!: ?String = None, items!: ?OpenApiSchema = None, // cjlint-ignore !G.FUN.02 format!: ?String = None, reference!: ?OpenApiReference = None, description!: ?String = None) { this.title = title this.format = format diff --git a/src/openapi/models/OpenApiSchemaTypes.cj b/src/openapi/models/OpenApiSchemaTypes.cj index ea47915..b4e1280 100644 --- a/src/openapi/models/OpenApiSchemaTypes.cj +++ b/src/openapi/models/OpenApiSchemaTypes.cj @@ -13,9 +13,9 @@ private let numbers = ["Float16", "Float32", "Float64", "Decimal"] private let integers = ["Int8", "Int16", "Int32", "Int64", "UInt8", "UInt16", "UInt32", "UInt64"] protected class OpenApiSchemaTypes { - public static const OBJECT = "object" - public static const STRING = "string" - public static const ARRAY = "array" + public static const OBJECT: String = "object" + public static const STRING: String = "string" + public static const ARRAY: String = "array" /** * @brief 将类型信息转换为 OpenAPI Schema 类型名称。 diff --git a/src/openapi/services/OpenApiDocumentService.cj b/src/openapi/services/OpenApiDocumentService.cj index d45786a..5243cee 100644 --- a/src/openapi/services/OpenApiDocumentService.cj +++ b/src/openapi/services/OpenApiDocumentService.cj @@ -260,7 +260,7 @@ protected class OpenApiDocumentService { /* 来自请求体的参数 */ - private func createOpenApiOperationRequestBody(endpoint: RouteEndpoint, actionDescriptor: ControllerActionDescriptor): ?OpenApiRequestBody { + private func createOpenApiOperationRequestBody(endpoint: RouteEndpoint, actionDescriptor: ControllerActionDescriptor): ?OpenApiRequestBody { // cjlint-ignore !G.FUN.02 endpoint 未使用 var requestBody = OpenApiRequestBody(required: true) let parameters = HashMap() for (parameter in actionDescriptor.actionFunction.parameters) {