重构: 源码文件蛇形命名, 接口去I头(BindRequestContext/SimApiAuthChecker), 清理C#相关说明, serialization依赖路径改 serialization-cj
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 声明式 AES body 解密注解(对齐 C# SimApi.Attributes.AesBodyAttribute)。
|
||||
* 遵循 MIT 许可证。
|
||||
* 声明式 AES body 解密注解。
|
||||
*
|
||||
* 标注在控制器方法参数上,请求派发时(SimApiRequestDelegateFactory)自动解密并反序列化:
|
||||
* - 读取请求体 {"data": "密文"}
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
*/
|
||||
|
||||
package simcu::simapi.annotations
|
||||
|
||||
/**
|
||||
* 原样响应注解(对齐 C# SimApi.Attributes.OriginResponseAttribute)。
|
||||
* 原样响应注解。
|
||||
*
|
||||
* 标注在控制器方法或类上,请求派发时 SimApiRequestDelegateFactory 跳过
|
||||
* 统一响应封装(SimApiBaseResponse 包装),接口返回什么就输出什么。
|
||||
@@ -1,25 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
*/
|
||||
|
||||
package simcu::simapi.annotations
|
||||
|
||||
/**
|
||||
* 声明式鉴权注解(对齐 C# SimApi.Attributes.SimApiAuthAttribute)。
|
||||
* 声明式鉴权注解。
|
||||
*
|
||||
* 标注在控制器方法或类上,请求派发时(SimApiRequestDelegateFactory)自动执行鉴权:
|
||||
* - 未登录(无 LoginInfo)→ 401
|
||||
* - type 非空且登录用户类型不匹配 → 403
|
||||
* - 遍历执行所有已注册的 ISimApiAuthChecker
|
||||
* - 遍历执行所有已注册的 SimApiAuthChecker
|
||||
*
|
||||
* 用法:
|
||||
* @SimApiAuth // 任意已登录用户
|
||||
* @SimApiAuth["admin"] // 仅 admin 类型
|
||||
* @SimApiAuth["admin,user"] // admin 或 user 类型(对齐 C# type.Split(","))
|
||||
* @SimApiAuth["admin,user"] // admin 或 user 类型
|
||||
*
|
||||
* 说明:仓颉注解参数须为编译期常量,String 无法作为 const 值数组元素,
|
||||
* 故与 C# 的 string[] 不同,这里用逗号分隔字符串对齐 C# 多类型。
|
||||
* 故用逗号分隔字符串表示多种类型。
|
||||
*/
|
||||
@Annotation[target: [MemberFunction, Type]]
|
||||
public class SimApiAuth {
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 声明式签名校验注解(对齐 C# SimApi.Attributes.SimApiSignAttribute)。
|
||||
* 遵循 MIT 许可证。
|
||||
* 声明式签名校验注解。
|
||||
*
|
||||
* 标注在控制器方法或类上,请求派发时(SimApiRequestDelegateFactory)自动执行验签:
|
||||
* - 提取 appId / timestamp / nonce / sign(Query 优先,其次 Header)
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
* AuthSDK/SimApiAuthCenter:认证中心远程 SDK。
|
||||
*/
|
||||
|
||||
@@ -16,7 +16,7 @@ import simcu::simapi.communications.*
|
||||
import simcu::simapi.helpers.*
|
||||
|
||||
/**
|
||||
* 认证中心远程 SDK(对齐 C# SimApiAuthCenter):
|
||||
* 认证中心远程 SDK:
|
||||
* 群组 / Profile / 内部应用 / 系统登录 / 安全验证 等接口,走签名请求。
|
||||
*/
|
||||
public class SimApiAuthCenter {
|
||||
@@ -35,7 +35,7 @@ public class SimApiAuthCenter {
|
||||
// ===== 公共 =====
|
||||
|
||||
/**
|
||||
* 委托 AuthCenter 进行应用签名验证(对齐 C# VerifySign)。
|
||||
* 委托 AuthCenter 进行应用签名验证。
|
||||
*/
|
||||
public func verifySign(appId: String, timestamp: String, nonce: String, sign: String): Unit {
|
||||
let url = "${_client.server}/api/auth/sign/verify?appId=${appId}×tamp=${timestamp}&nonce=${nonce}&sign=${sign}"
|
||||
@@ -55,7 +55,7 @@ public class SimApiAuthCenter {
|
||||
SimApiError.errorWhenFalse(isSuccess(response.status), code: Int64(response.status),
|
||||
message: "HTTP ERROR: ${response.status}")
|
||||
let json = readBodyText(response.body)
|
||||
let resp = JsonSerializer.Deserialize<SimApiBaseResponse>(json)
|
||||
let resp = JsonSerializer.deserialize<SimApiBaseResponse>(json)
|
||||
SimApiError.errorWhen(resp.code != 200, code: 400, message: "签名验证失败")
|
||||
} finally {
|
||||
response.close()
|
||||
@@ -104,7 +104,7 @@ public class SimApiAuthCenter {
|
||||
// ===== 群组相关 =====
|
||||
|
||||
/**
|
||||
* 根据 profileId 获取群组列表(对齐 C# GroupRelated)。
|
||||
* 根据 profileId 获取群组列表。
|
||||
*/
|
||||
public func groupRelated(profileId: String): Array<GroupRelatedItem> {
|
||||
_client.signQuery<Array<GroupRelatedItem>>("/api/auth/group/related",
|
||||
@@ -112,7 +112,7 @@ public class SimApiAuthCenter {
|
||||
}
|
||||
|
||||
/**
|
||||
* 按关键字搜索群组,输入群组 ID 精准搜索(对齐 C# GroupSearch)。
|
||||
* 按关键字搜索群组,输入群组 ID 精准搜索。
|
||||
*/
|
||||
public func groupSearch(keyword: String, skip!: Int64 = 0, take!: Int64 = 20): Array<AppAndProfileItem> {
|
||||
var body = HashMap<String, Any>()
|
||||
@@ -123,7 +123,7 @@ public class SimApiAuthCenter {
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用组 ID 以及组内成员/管理员 profile 获取组的详细树结构(对齐 C# GroupDetail)。
|
||||
* 使用组 ID 以及组内成员/管理员 profile 获取组的详细树结构。
|
||||
*/
|
||||
public func groupDetail(groupId: String, profileId: String): GroupDetailTreeNode {
|
||||
var body = HashMap<String, Any>()
|
||||
@@ -133,7 +133,7 @@ public class SimApiAuthCenter {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 profile 在本组的所有子组(对齐 C# GroupRelatedIndex)。
|
||||
* 获取 profile 在本组的所有子组。
|
||||
*/
|
||||
public func groupRelatedIndex(groupId: String, profileId: String): Array<String> {
|
||||
var body = HashMap<String, Any>()
|
||||
@@ -146,7 +146,7 @@ public class SimApiAuthCenter {
|
||||
// ===== Profile 相关 =====
|
||||
|
||||
/**
|
||||
* 按关键字搜索用户 Profile(对齐 C# ProfileSearch)。
|
||||
* 按关键字搜索用户 Profile。
|
||||
*/
|
||||
public func profileSearch(keyword: String, skip!: Int64 = 0, take!: Int64 = 20): Array<AppAndProfileItem> {
|
||||
var body = HashMap<String, Any>()
|
||||
@@ -157,7 +157,7 @@ public class SimApiAuthCenter {
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 id 批量获取用户基本信息(对齐 C# ProfileList)。
|
||||
* 通过 id 批量获取用户基本信息。
|
||||
*/
|
||||
public func profileList(ids: Array<String>): Array<AppAndProfileItem> {
|
||||
var body = HashMap<String, Any>()
|
||||
@@ -172,7 +172,7 @@ public class SimApiAuthCenter {
|
||||
// ===== AuthGate 内部应用专用 =====
|
||||
|
||||
/**
|
||||
* 获取是否为 App 的拥有者(对齐 C# CheckIsAppOwner,字段为 PascalCase)。
|
||||
* 获取是否为 App 的拥有者。
|
||||
*/
|
||||
public func checkIsAppOwner(profileId: String, applicationId: String): Bool {
|
||||
var body = HashMap<String, Any>()
|
||||
@@ -182,7 +182,7 @@ public class SimApiAuthCenter {
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户 profileId 和提供的 appIds 获取应用列表(对齐 C# GetAppList,字段为 PascalCase)。
|
||||
* 根据用户 profileId 和提供的 appIds 获取应用列表。
|
||||
*/
|
||||
public func getAppList(profileId: String, appIds: Array<String>): Array<AppAndProfileItem> {
|
||||
var body = HashMap<String, Any>()
|
||||
@@ -199,7 +199,7 @@ public class SimApiAuthCenter {
|
||||
// ===== 系统登录 =====
|
||||
|
||||
/**
|
||||
* 获取登录授权 CODE(对齐 C# GetLoginCode)。
|
||||
* 获取登录授权 CODE。
|
||||
* @param scene 场景标识。
|
||||
* @param data 附加数据。
|
||||
* @param backUrl 回调地址。
|
||||
@@ -217,12 +217,12 @@ public class SimApiAuthCenter {
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 code 获取登录信息(对齐 C# GetLoginInfo,场景不匹配抛 403003)。
|
||||
* 使用 code 获取登录信息。
|
||||
*/
|
||||
public func getLoginInfo(code: String, scene!: ?String = None): LoginInfoResponse {
|
||||
var body = HashMap<String, Any>()
|
||||
body["code"] = code
|
||||
// 说明:C# 的 ErrorWhenNull(resp, 400232, "登录信息获取失败") 对应 signQuery 内部 data.getOrThrow() 的
|
||||
// 说明:ErrorWhenNull(resp, 400232, "登录信息获取失败") 对应 signQuery 内部 data.getOrThrow() 的
|
||||
// None 分支;仓颉版 signQuery 返回非空 T(data 缺失即抛异常),故此处无需重复判空。
|
||||
let resp = _client.signQuery<LoginInfoResponse>("/api/auth/login/get", body: SimApiUtil.json(Some(body)))
|
||||
SimApiError.errorWhen(resp.scene != scene, code: 403003, message: "登录场景不匹配")
|
||||
@@ -232,7 +232,7 @@ public class SimApiAuthCenter {
|
||||
// ===== 安全验证 =====
|
||||
|
||||
/**
|
||||
* 获取安全验证代码(对齐 C# GetConfirmCode)。
|
||||
* 获取安全验证代码。
|
||||
*/
|
||||
public func getConfirmCode(scene: String, userId: String, data!: ?HashMap<String, Any> = None,
|
||||
backUrl!: ?String = None): GetCodeResponse {
|
||||
@@ -247,7 +247,7 @@ public class SimApiAuthCenter {
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用安全验证 code 获取验证结果(对齐 C# Confirm,身份/场景不匹配分别抛 403002/403003)。
|
||||
* 使用安全验证 code 获取验证结果。
|
||||
*/
|
||||
public func confirm(code: String, scene: String, userId!: ?String = None): ConfirmResponse {
|
||||
var body = HashMap<String, Any>()
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
* AuthSDK/SimApiAuthCenterMiddleware:网关透传认证中间件。
|
||||
*/
|
||||
|
||||
@@ -12,7 +12,7 @@ import simcu::simapi.configurations.*
|
||||
import simcu::simapi.helpers.*
|
||||
|
||||
/**
|
||||
* 网关透传认证中间件(对齐 C# SimApiAuthCenterMiddleware):
|
||||
* 网关透传认证中间件:
|
||||
* 当请求带 X-SimApi-Gate-Auth / X-SimApi-Gate-Time / X-SimApi-Gate-Sign 三头时,
|
||||
* 校验 MD5 签名(appId=..&auth=..&time=..&appKey=..),通过则 Base64 解码登录信息写入 LoginInfo。
|
||||
*/
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
* AuthSDK/SimApiAuthClient:认证中心专用签名客户端。
|
||||
*/
|
||||
|
||||
@@ -10,7 +10,7 @@ import simcu::simapi.configurations.*
|
||||
import simcu::simapi.helpers.*
|
||||
|
||||
/**
|
||||
* 认证中心签名客户端(对齐 C# SimApiAuthClient):
|
||||
* 认证中心签名客户端:
|
||||
* SimApiHttpClient 子类,凭证(Server/AppId/AppKey)取自 SimApiAuthCenterOptions。
|
||||
*/
|
||||
public class SimApiAuthClient <: SimApiHttpClient {
|
||||
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* AuthSDK 用到的 DTO(对齐 C# SimApiAuthCenterDto / SimApiAuthIamDto)。
|
||||
* 遵循 MIT 许可证。
|
||||
* AuthSDK 用到的 DTO。
|
||||
*
|
||||
* 说明:C# 中这些 DTO 是 SimApiAuthCenterDto / SimApiAuthIamDto 的嵌套类;
|
||||
* 说明:原版中这些 DTO 是 SimApiAuthCenterDto / SimApiAuthIamDto 的嵌套类;
|
||||
* 仓颉不支持在类体内声明嵌套类(unexpected class declaration in class body),
|
||||
* 故拍平为顶层类,语义与字段保持一致。
|
||||
*
|
||||
@@ -15,7 +15,7 @@ package simcu::simapi.authsdk
|
||||
import std.collection.*
|
||||
|
||||
/**
|
||||
* 应用/Profile 通用项(对齐 C# AppAndProfileItem)。
|
||||
* 应用/Profile 通用项。
|
||||
*/
|
||||
public class AppAndProfileItem {
|
||||
public var id: String = ""
|
||||
@@ -25,8 +25,8 @@ public class AppAndProfileItem {
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全确认响应(对齐 C# ConfirmResponse)。
|
||||
* data 用 ?HashMap<String, Any> 对齐 C# Dictionary<string,object>?(任意 JSON 对象)。
|
||||
* 安全确认响应。
|
||||
* data 用 ?HashMap<String, Any> 表示任意 JSON 对象。
|
||||
*/
|
||||
public class ConfirmResponse {
|
||||
public var applicationId: String = ""
|
||||
@@ -36,7 +36,7 @@ public class ConfirmResponse {
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录信息响应(对齐 C# LoginInfoResponse)。
|
||||
* 登录信息响应。
|
||||
*/
|
||||
public class LoginInfoResponse {
|
||||
public var scene: ?String = None
|
||||
@@ -48,7 +48,7 @@ public class LoginInfoResponse {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取授权码响应(对齐 C# GetCodeResponse)。
|
||||
* 获取授权码响应。
|
||||
*/
|
||||
public class GetCodeResponse {
|
||||
public var code: String = ""
|
||||
@@ -65,7 +65,7 @@ public class GetCodeResponse {
|
||||
}
|
||||
|
||||
/**
|
||||
* 群组关联项(对齐 C# GroupRelatedItem)。
|
||||
* 群组关联项。
|
||||
*/
|
||||
public class GroupRelatedItem {
|
||||
public var id: String = ""
|
||||
@@ -78,7 +78,7 @@ public class GroupRelatedItem {
|
||||
}
|
||||
|
||||
/**
|
||||
* 群组详情树节点(对齐 C# GroupDetailTreeNode,children 递归)。
|
||||
* 群组详情树节点。
|
||||
*/
|
||||
public class GroupDetailTreeNode {
|
||||
public var id: String = ""
|
||||
@@ -90,7 +90,7 @@ public class GroupDetailTreeNode {
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限项(对齐 C# PermissionItem)。
|
||||
* 权限项。
|
||||
*/
|
||||
public class PermissionItem {
|
||||
public var identifier: String = ""
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
* AuthSDK/SimApiAuthIam:权限中心远程 SDK。
|
||||
*/
|
||||
|
||||
@@ -11,7 +11,7 @@ import simcu::simapi.communications.*
|
||||
import simcu::simapi.helpers.*
|
||||
|
||||
/**
|
||||
* 权限中心远程 SDK(对齐 C# SimApiAuthIam):
|
||||
* 权限中心远程 SDK:
|
||||
* 注册权限点 / 获取权限标识 / 校验权限。
|
||||
*/
|
||||
public class SimApiAuthIam {
|
||||
@@ -22,7 +22,7 @@ public class SimApiAuthIam {
|
||||
}
|
||||
|
||||
/**
|
||||
* 向 IAM 注册权限(对齐 C# RegisterPermissions)。
|
||||
* 向 IAM 注册权限。
|
||||
*/
|
||||
public func registerPermissions(permissions: Array<PermissionItem>): Unit {
|
||||
// 请求体:{"permissions":[{"identifier":...,"name":...,"group":...,"description":...},...]}
|
||||
@@ -41,7 +41,7 @@ public class SimApiAuthIam {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取拥有的权限标识数组(对齐 C# GetPermissionOwned)。
|
||||
* 获取拥有的权限标识数组。
|
||||
*/
|
||||
public func getPermissionOwned(profileId: String, groupId!: ?String = None): Array<String> {
|
||||
var body = HashMap<String, Any>()
|
||||
@@ -53,7 +53,7 @@ public class SimApiAuthIam {
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测 profileId 是否有该权限,无权限抛 403(对齐 C# CheckPermission)。
|
||||
* 检测 profileId 是否有该权限,无权限抛 403。
|
||||
*/
|
||||
public func checkPermission(profileId: String, permission: String, groupId!: ?String = None): Unit {
|
||||
var body = HashMap<String, Any>()
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 通用请求 DTO(对齐 C# Communications/SimApiBaseRequest.cs)。
|
||||
* 遵循 MIT 许可证。
|
||||
* 通用请求 DTO。
|
||||
*
|
||||
* 说明:这些类仅作反序列化目标(FromJson<T> / @FromBody),无参构造由编译器自动提供;
|
||||
* 不声明显式构造器。
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
*/
|
||||
|
||||
package simcu::simapi.communications
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
*/
|
||||
|
||||
package simcu::simapi.communications
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
* Communications/SimApiLoginItem:登录信息项。
|
||||
*/
|
||||
|
||||
@@ -10,7 +10,6 @@ import std.collection.*
|
||||
|
||||
/**
|
||||
* 登录信息项:Token 认证通过后注入请求上下文。
|
||||
* 对齐 C# SimApiLoginItem(Id / Type / Meta / Extra)。
|
||||
*
|
||||
* 序列化/反序列化由 simapi_serialization 反射处理(免标注、免约束),
|
||||
* 字段 id/types/meta/extra 全部为受支持类型(String/Array/HashMap)。
|
||||
+3
-3
@@ -1,13 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 对齐 C# 的 Configurations/SimApiAuthCenterOptions.cs。
|
||||
* 遵循 MIT 许可证。
|
||||
* AuthCenter 认证中心选项配置。
|
||||
*/
|
||||
|
||||
package simcu::simapi.configurations
|
||||
|
||||
/**
|
||||
* 认证中心配置(对齐 C# SimApiAuthCenterOptions)。
|
||||
* 认证中心配置。
|
||||
*/
|
||||
public class SimApiAuthCenterOptions {
|
||||
/**
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 对齐 C# 的 Configurations/SimApiDocOptions.cs。
|
||||
* 遵循 MIT 许可证。
|
||||
*/
|
||||
|
||||
package simcu::simapi.configurations
|
||||
@@ -9,7 +8,7 @@ package simcu::simapi.configurations
|
||||
import std.collection.*
|
||||
|
||||
/**
|
||||
* 文档组配置(对齐 C# SimApiDocGroupOption)。
|
||||
* 文档组配置。
|
||||
*/
|
||||
public class SimApiDocGroup {
|
||||
public var id: String = ""
|
||||
@@ -26,13 +25,13 @@ public class SimApiDocGroup {
|
||||
}
|
||||
|
||||
/**
|
||||
* 文档授权配置(对齐 C# SimApiAuthOption)。
|
||||
* 文档授权配置。
|
||||
* Type 支持 "SimApiAuth"、"ClientCredentials"、"Implicit"、"AuthorizationCode"、"Password"。
|
||||
*/
|
||||
public class SimApiAuthOption {
|
||||
/**
|
||||
* 认证方式(默认 ["SimApiAuth"])。
|
||||
* `type` 是仓颉关键字,用反引号转义以对齐 C# 属性名 Type。
|
||||
* `type` 是仓颉关键字,用反引号转义保留属性名。
|
||||
*/
|
||||
public var `type`: Array<String> = ["SimApiAuth"]
|
||||
|
||||
@@ -58,7 +57,7 @@ public class SimApiAuthOption {
|
||||
}
|
||||
|
||||
/**
|
||||
* 文档相关配置(默认值与 C# 一致)。
|
||||
* 文档相关配置(默认值)。
|
||||
*/
|
||||
public class SimApiDocOptions {
|
||||
/**
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 对齐 C# 的 Configurations/SimApiExceptionOptions.cs。
|
||||
* 遵循 MIT 许可证。
|
||||
* 异常处理选项配置。
|
||||
*/
|
||||
|
||||
package simcu::simapi.configurations
|
||||
@@ -9,7 +9,7 @@ package simcu::simapi.configurations
|
||||
import std.collection.*
|
||||
|
||||
/**
|
||||
* 异常处理相关配置(默认值与 C# 一致)。
|
||||
* 异常处理相关配置(默认值)。
|
||||
*/
|
||||
public class SimApiExceptionOptions {
|
||||
/**
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 对齐 C# 的 Configurations/SimApiHttpClientOptions.cs。
|
||||
* 遵循 MIT 许可证。
|
||||
* HTTP 客户端选项配置。
|
||||
*/
|
||||
|
||||
package simcu::simapi.configurations
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 对齐 C# 的 Configurations/SimApiJobOptions.cs。
|
||||
* 遵循 MIT 许可证。
|
||||
* 后台任务选项配置(占位,未实现)。
|
||||
*/
|
||||
|
||||
package simcu::simapi.configurations
|
||||
@@ -9,7 +9,7 @@ package simcu::simapi.configurations
|
||||
import std.collection.*
|
||||
|
||||
/**
|
||||
* 任务调度服务器配置(对齐 C# SimApiJobServerConfig)。
|
||||
* 任务调度服务器配置。
|
||||
*/
|
||||
public class SimApiJobServer {
|
||||
/**
|
||||
@@ -24,7 +24,7 @@ public class SimApiJobServer {
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务调度相关配置(默认值与 C# 一致)。
|
||||
* 任务调度相关配置(默认值)。
|
||||
*/
|
||||
public class SimApiJobOptions {
|
||||
/**
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 对齐 C# 的 Configurations/SimApiOptions.cs。
|
||||
* 遵循 MIT 许可证。
|
||||
*/
|
||||
|
||||
package simcu::simapi.configurations
|
||||
@@ -9,7 +8,7 @@ package simcu::simapi.configurations
|
||||
import std.collection.*
|
||||
|
||||
/**
|
||||
* SimApi 全局配置:对应 C# 的 SimApi.Configurations.SimApiOptions。
|
||||
* SimApi 全局配置。
|
||||
*/
|
||||
public class SimApiOptions {
|
||||
/**
|
||||
@@ -113,7 +112,7 @@ public class SimApiOptions {
|
||||
public var simApiRequestLogOptions = SimApiRequestLogOptions()
|
||||
|
||||
|
||||
// ===== .NET 风格配置回调(对齐 C# ConfigureSimApiXxx(opt => ...)) =====
|
||||
// ===== 配置回调 =====
|
||||
|
||||
/**
|
||||
* 配置路由选项。
|
||||
@@ -172,7 +171,7 @@ public class SimApiOptions {
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置请求日志选项(对齐 C# ConfigureSimApiRequestLog)。
|
||||
* 配置请求日志选项。
|
||||
* @param configure 请求日志配置回调。
|
||||
*/
|
||||
public func configureSimApiRequestLog(configure: (SimApiRequestLogOptions) -> Unit): Unit {
|
||||
@@ -180,7 +179,7 @@ public class SimApiOptions {
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置认证中心选项(对齐 C# ConfigureSimApiAuthCenter)。
|
||||
* 配置认证中心选项。
|
||||
* @param configure 认证中心配置回调。
|
||||
*/
|
||||
public func configureSimApiAuthCenter(configure: (SimApiAuthCenterOptions) -> Unit): Unit {
|
||||
+2
-3
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 对齐 C# 的 Configurations/SimApiRequestLogOptions.cs。
|
||||
* 遵循 MIT 许可证。
|
||||
*/
|
||||
|
||||
package simcu::simapi.configurations
|
||||
@@ -16,7 +15,7 @@ public class SimApiRequestLogOptions {
|
||||
public var showFullHeader: Bool = false
|
||||
|
||||
/**
|
||||
* 是否打印完整的响应体(false 时截断到 200 字符,对齐 C#)。
|
||||
* 是否打印完整的响应体(false 时截断到 200 字符)。
|
||||
*/
|
||||
public var showFullResponse: Bool = false
|
||||
|
||||
+3
-3
@@ -1,13 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 对齐 C# 的 Configurations/SimApiRouteOptions.cs。
|
||||
* 遵循 MIT 许可证。
|
||||
* 内置路由选项配置。
|
||||
*/
|
||||
|
||||
package simcu::simapi.configurations
|
||||
|
||||
/**
|
||||
* 路由相关配置(默认值与 C# 一致)。
|
||||
* 路由相关配置(默认值)。
|
||||
*/
|
||||
public class SimApiRouteOptions {
|
||||
/**
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 对齐 C# 的 Configurations/SimApiStorageOptions.cs。
|
||||
* 遵循 MIT 许可证。
|
||||
* S3/MinIO 存储选项配置。
|
||||
*/
|
||||
|
||||
package simcu::simapi.configurations
|
||||
+3
-3
@@ -1,13 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 对齐 C# 的 Configurations/SimApiSynapseOptions.cs。
|
||||
* 遵循 MIT 许可证。
|
||||
* Synapse 通信选项配置(占位,未实现)。
|
||||
*/
|
||||
|
||||
package simcu::simapi.configurations
|
||||
|
||||
/**
|
||||
* MQTT 通信配置(默认值与 C# 一致)。
|
||||
* MQTT 通信配置(默认值)。
|
||||
*/
|
||||
public class SimApiSynapseOptions {
|
||||
/**
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
* Controllers/SimApiAuthController:认证相关内置路由。
|
||||
*/
|
||||
|
||||
@@ -12,7 +12,6 @@ import simcu::simapi.helpers.*
|
||||
|
||||
/**
|
||||
* 认证控制器:退出登录。
|
||||
* 对齐 C# 的 SimApiAuthController。
|
||||
*/
|
||||
public class SimApiAuthController <: SimApiBaseController {
|
||||
private let _auth: SimApiAuth
|
||||
@@ -23,8 +22,8 @@ public class SimApiAuthController <: SimApiBaseController {
|
||||
|
||||
/**
|
||||
* POST /auth/logout:退出登录(void 自动封装为 SimApiBaseResponse())。
|
||||
* 对齐 C# LogoutRoute 默认值 /auth/logout(SimApiAuthController.Logout,[HttpPost],
|
||||
* 由 MapControllerRoute(pattern=LogoutRoute) 注册)。
|
||||
* 对应路由 [HttpPost],
|
||||
* 由 MapControllerRoute(pattern=LogoutRoute) 注册。
|
||||
*/
|
||||
@HttpPost["/auth/logout"]
|
||||
public func logout(): Unit {
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
* Controllers/SimApiBaseController:基础控制器,所有控制器继承。
|
||||
* 提供当前登录信息访问(对齐 C# 的 LoginInfo / LoginToken)。
|
||||
* 提供当前登录信息访问。
|
||||
*/
|
||||
|
||||
package simcu::simapi.controllers
|
||||
@@ -15,9 +15,8 @@ import simcu::simapi.interfaces.*
|
||||
|
||||
/**
|
||||
* 基础控制器:所有控制器均继承本控制器。
|
||||
* 对齐 C# 的 SimApiBaseController([Consumes]/[Produces] JSON + 登录信息)。
|
||||
*/
|
||||
public open class SimApiBaseController <: Controller & IBindRequestContext {
|
||||
public open class SimApiBaseController <: Controller & BindRequestContext {
|
||||
/**
|
||||
* 当前登录信息(需 EnableSimApiAuth;未登录抛 401)。
|
||||
*/
|
||||
+5
-6
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
* Controllers/SimApiCommonController:通用内置路由。
|
||||
*/
|
||||
|
||||
@@ -14,8 +14,7 @@ import simcu::simapi.helpers.*
|
||||
|
||||
/**
|
||||
* 通用控制器:错误反馈、WebConfig、用户信息。
|
||||
* 对齐 C# 的 SimApiCommonController。
|
||||
* 控制器直接返回 SimApiBaseResponse / SimApiResponse<T>(对齐 C# SimApiBaseResponse<T>)。
|
||||
* 控制器直接返回 SimApiBaseResponse / SimApiResponse<T>。
|
||||
*/
|
||||
public class SimApiCommonController <: SimApiBaseController {
|
||||
private let _options: SimApiOptions
|
||||
@@ -26,7 +25,7 @@ public class SimApiCommonController <: SimApiBaseController {
|
||||
|
||||
/**
|
||||
* GET /exception/{code}:错误反馈页面(始终注册)。
|
||||
* 对齐 C# ExceptionHandler:抛 SimApiException,由异常中间件统一输出。
|
||||
* 抛 SimApiException,由异常中间件统一输出。
|
||||
*/
|
||||
@HttpGet["exception/{code}"]
|
||||
public func exceptionHandler(@FromRoute code: Int64): Unit {
|
||||
@@ -35,8 +34,8 @@ public class SimApiCommonController <: SimApiBaseController {
|
||||
|
||||
/**
|
||||
* POST/GET /config:给前端的自定义信息(含版本)。
|
||||
* 对齐 C# WebConfigRoute 默认值 /config(SimApiCommonController.WebConfig,[HttpPost, HttpGet] 无路径,
|
||||
* 由 MapControllerRoute(pattern=WebConfigRoute) 注册;soulsoft 约定路由不支持 defaults,故用特性路由直接对齐路径)。
|
||||
* 对应路由 [HttpPost, HttpGet] 无路径,
|
||||
* 由 MapControllerRoute(pattern=WebConfigRoute) 注册;soulsoft 约定路由不支持 defaults,故用特性路由直接对齐路径。
|
||||
*/
|
||||
@HttpGet["/config"]
|
||||
public func webConfig(): HashMap<String, Any> {
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
*/
|
||||
|
||||
package simcu::simapi.exceptions
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
*/
|
||||
|
||||
package simcu::simapi.helpers
|
||||
@@ -14,21 +14,21 @@ import simcu::simapi.exceptions.*
|
||||
import simcu::simapi.interfaces.*
|
||||
|
||||
/**
|
||||
* AES body 请求({"data": "密文"},对齐 C# SimApiOneFieldRequest<string>)。
|
||||
* AES body 请求({"data": "密文"})。
|
||||
*/
|
||||
public class AesBodyRequest {
|
||||
public var data: String = ""
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务端 AES body 解密校验器(对齐 C# ModelBinders/AesBodyModelBinder)。
|
||||
* 服务端 AES body 解密校验器。
|
||||
*
|
||||
* 仓颉无 ModelBinder 机制,按项目惯例由控制器在方法开头调用:
|
||||
* let jsonStr = SimApiAesBodyChecker.decryptBody(context, provider)
|
||||
* let request = JsonSerializer.Deserialize<XxxRequest>(jsonStr)
|
||||
* let request = JsonSerializer.deserialize<XxxRequest>(jsonStr)
|
||||
* 或标注 @AesBody 注解自动执行(SimApiRequestDelegateFactory)。
|
||||
*
|
||||
* 流程(与 C# 一致):
|
||||
* 流程:
|
||||
* 1. 读取 body 并反序列化为 {"data": "密文"}
|
||||
* 2. 校验 Data 非空
|
||||
* 3. 提取 appId(Query/Header)
|
||||
@@ -53,7 +53,7 @@ public class SimApiAesBodyChecker {
|
||||
}
|
||||
|
||||
// 2. 反序列化 {"data": "密文"}
|
||||
let req = JsonSerializer.Deserialize<AesBodyRequest>(body)
|
||||
let req = JsonSerializer.deserialize<AesBodyRequest>(body)
|
||||
if (req.data.isEmpty()) {
|
||||
SimApiError.error(code: 400, message: "请求体缺少密文Data字段")
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
*/
|
||||
|
||||
package simcu::simapi.helpers
|
||||
@@ -11,9 +11,9 @@ import stdx.crypto.digest.*
|
||||
import stdx.encoding.base64.*
|
||||
|
||||
/**
|
||||
* AES-256-CBC + PKCS7 加解密工具(对齐 C# SimApiAesUtil)。
|
||||
* AES-256-CBC + PKCS7 加解密工具。
|
||||
*
|
||||
* 约定(与 C# 完全一致):
|
||||
* 约定:
|
||||
* - 密钥:SHA256(key 字符串) → 32 字节
|
||||
* - 模式:AES-256-CBC,PKCS7 填充
|
||||
* - IV:每次加密随机生成 16 字节,前置在密文前
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
*/
|
||||
|
||||
package simcu::simapi.helpers
|
||||
@@ -113,14 +113,14 @@ public class SimApiAuth {
|
||||
let tokenKey = "${tokenCachePrefix}${token}"
|
||||
let json = loginItemJson(loginItem)
|
||||
if (let Some(redis) <- _redis) {
|
||||
// 保留原过期时间(对齐 C# update 不刷新 TTL):先读旧 TTL,SET 后重新续期
|
||||
// 保留原过期时间:先读旧 TTL,SET 后重新续期
|
||||
let ttl = redis.ttl(tokenKey)
|
||||
redis.set(tokenKey, Blob.fromUtf8(json))
|
||||
if (ttl > 0) {
|
||||
redis.expire(tokenKey, ttl)
|
||||
}
|
||||
} else {
|
||||
// 保留原过期时间(对齐 C# update 不刷新 TTL)
|
||||
// 保留原过期时间
|
||||
let expireAt = match (_tokenStore.get(token)) {
|
||||
case Some(entry) => entry.expireAt
|
||||
case None => 0
|
||||
@@ -162,10 +162,10 @@ public class SimApiAuth {
|
||||
}
|
||||
}
|
||||
|
||||
/// 解析登录信息;JSON 无效/非对象时返回 None(视为 token 无效,对齐 C# 返回 null)
|
||||
/// 解析登录信息;JSON 无效/非对象时返回 None(视为 token 无效)
|
||||
private static func parseLoginItemSafe(json: String): ?SimApiLoginItem {
|
||||
try {
|
||||
Some(JsonSerializer.Deserialize<SimApiLoginItem>(json))
|
||||
Some(JsonSerializer.deserialize<SimApiLoginItem>(json))
|
||||
} catch (_: Exception) {
|
||||
None
|
||||
}
|
||||
@@ -253,7 +253,7 @@ public class SimApiAuth {
|
||||
}
|
||||
|
||||
private static func generateToken(): String {
|
||||
// 对齐 C#:token = Guid.NewGuid().ToString()(小写、8-4-4-4-12)
|
||||
// token = Guid.NewGuid().ToString()(小写、8-4-4-4-12)
|
||||
SimApiUtil.newGuid()
|
||||
}
|
||||
|
||||
@@ -294,7 +294,7 @@ public class SimApiAuth {
|
||||
}
|
||||
|
||||
private static func loginItemJson(item: SimApiLoginItem): String {
|
||||
// 统一 JSON 序列化:对齐 .NET JsonSerializer.Serialize(item)
|
||||
JsonSerializer.Serialize(item)
|
||||
// 统一 JSON 序列化:JsonSerializer.Serialize(item)
|
||||
JsonSerializer.serialize(item)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
*/
|
||||
|
||||
package simcu::simapi.helpers
|
||||
@@ -28,7 +28,7 @@ private struct CacheEntry {
|
||||
/**
|
||||
* 缓存助手:Key 自动加前缀 "SimApi:Cache:"。
|
||||
* 存储后端与 SimApiAuth 一致:配置了 Redis 用 Redis(可带密码/DB 索引),否则 InMemory。
|
||||
* InMemory 模式同样支持过期(对齐 C# DistributedCache 的过期语义)。
|
||||
* InMemory 模式同样支持过期。
|
||||
*/
|
||||
public class SimApiCache {
|
||||
private static let prefix = "SimApi:Cache:"
|
||||
@@ -100,17 +100,17 @@ public class SimApiCache {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取特定类型缓存(对齐 C# Get<T>:从 JSON 反序列化,使用 simapi_serialization)。
|
||||
* 获取特定类型缓存(从 JSON 反序列化,使用 simapi_serialization)。
|
||||
*/
|
||||
public func get<T>(key: String): ?T {
|
||||
match (getString(key)) {
|
||||
case Some(json) => Some(JsonSerializer.Deserialize<T>(json))
|
||||
case Some(json) => Some(JsonSerializer.deserialize<T>(json))
|
||||
case None => None
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 string 类型缓存(对应 C# Get(string);Cangjie 不支持按泛型重载,故拆分为 getString/get<T>)。
|
||||
* 获取 string 类型缓存(仓颉不支持按泛型重载,故拆分为 getString/get<T>)。
|
||||
*/
|
||||
public func getString(key: String): ?String {
|
||||
if (let Some(redis) <- _redis) {
|
||||
@@ -1,10 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
* Helpers/SimApiControllerScanner:自动发现调用者包中的 MVC 控制器。
|
||||
*
|
||||
* 对齐 C# 的控制器发现机制:
|
||||
* - C# 通过 StackTrace 获取调用程序集,再 Assembly.GetTypes() 扫描所有类型
|
||||
* 控制器发现机制:
|
||||
* - 通过 StackTrace 获取调用程序集,再 Assembly.GetTypes() 扫描所有类型
|
||||
* - 仓颉版通过 Error.getStackTrace() 获取调用者包名,再 PackageInfo 枚举类型,
|
||||
* 过滤出继承 Controller 的类型(含子包)
|
||||
*/
|
||||
@@ -35,20 +35,19 @@ public class SimApiControllerScanner {
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描调用者包及其所有子包中 ISimApiAuthChecker 的实现类。
|
||||
* 对齐 C# AddSimApi 中遍历调用者程序集 AddScoped 注册 checker 的机制。
|
||||
* 扫描调用者包及其所有子包中 SimApiAuthChecker 的实现类。
|
||||
* @return 找到的 checker 实现类型列表(不含抽象类型与接口本身)。
|
||||
*/
|
||||
public static func scanAuthCheckers(): Array<TypeInfo> {
|
||||
let callerPackage = getCallerPackage()
|
||||
var result = ArrayList<TypeInfo>()
|
||||
collectImplementations(callerPackage, TypeInfo.of<ISimApiAuthChecker>(), result)
|
||||
collectImplementations(callerPackage, TypeInfo.of<SimApiAuthChecker>(), result)
|
||||
result.toArray()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取调用者(应用)包名:遍历栈帧,跳过 simapi/soulsoft/std 等框架包,
|
||||
* 返回第一个应用包的 declaringClass(对齐 C# 通过 StackTrace 找调用程序集)。
|
||||
* 返回第一个应用包的 declaringClass。
|
||||
*/
|
||||
public static func getCallerPackage(): String {
|
||||
try {
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
*
|
||||
* 错误抛出:提供「顶层函数」+「SimApiError 类」两种写法(对齐 C# using static SimApi.Helpers.SimApiError)。
|
||||
* 错误抛出:提供「顶层函数」+「SimApiError 类」两种写法。
|
||||
* - 顶层函数:import simcu::simapi.helpers.* 后可直接 error(400) / errorWhen(...),无需前缀
|
||||
* - SimApiError.error(...):旧写法,保留兼容
|
||||
*/
|
||||
@@ -11,7 +11,7 @@ package simcu::simapi.helpers
|
||||
|
||||
import simcu::simapi.exceptions.*
|
||||
|
||||
// ===== 顶层函数(推荐用法:直接 error(400),对齐 C# using static) =====
|
||||
// ===== 顶层函数(推荐用法:直接 error(400)) =====
|
||||
|
||||
/**
|
||||
* 直接抛错。
|
||||
@@ -47,17 +47,16 @@ public func errorWhenFalse(condition: Bool, code!: Int64 = 400, message!: String
|
||||
}
|
||||
|
||||
/**
|
||||
* 给定的可选值为 None 时抛错。
|
||||
* 给定的可选值:None 时抛错,否则返回解包后的值。
|
||||
*/
|
||||
public func errorWhenNull(condition: ?Any, code!: Int64 = 404, message!: String = ""): Unit {
|
||||
SimApiError.errorWhenNull(condition, code: code, message: message)
|
||||
public func errorWhenNone<T>(value: ?T, code!: Int64 = 404, message!: String = ""): T {
|
||||
SimApiError.errorWhenNone(value, code: code, message: message)
|
||||
}
|
||||
|
||||
// ===== 兼容门面(旧写法 SimApiError.error(...) 仍可用,内部为真实实现) =====
|
||||
|
||||
/**
|
||||
* 错误抛出辅助类:所有业务错误统一通过这里抛出 SimApiException。
|
||||
* 对应 C# 的 SimApi.Helpers.SimApiError。
|
||||
*/
|
||||
public class SimApiError {
|
||||
private init() {}
|
||||
@@ -98,12 +97,12 @@ public class SimApiError {
|
||||
}
|
||||
|
||||
/**
|
||||
* 给定的可选值为 None 时抛错。
|
||||
* 给定的可选值:None 时抛错,否则返回解包后的值。
|
||||
*/
|
||||
public static func errorWhenNull(condition: ?Any, code!: Int64 = 404, message!: String = ""): Unit {
|
||||
match (condition) {
|
||||
case None => error(code: code, message: message)
|
||||
case _ => ()
|
||||
public static func errorWhenNone<T>(value: ?T, code!: Int64 = 404, message!: String = ""): T {
|
||||
match (value) {
|
||||
case Some(v) => v
|
||||
case None => throw SimApiException(code, message: message)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
*/
|
||||
|
||||
package simcu::simapi.helpers
|
||||
@@ -17,8 +17,8 @@ import simcu::simapi.exceptions.*
|
||||
|
||||
/**
|
||||
* HTTP 客户端:用于调用其他带签名/AES 的 SimApi 服务。
|
||||
* 对齐 C# 的 SimApi.Helpers.SimApiHttpClient:
|
||||
* - 内部使用 stdx.net.http 的 HttpClient(等价 .NET 的 System.Net.Http.HttpClient)
|
||||
* 说明:
|
||||
* - 内部使用 stdx.net.http 的 HttpClient(等价 System.Net.Http.HttpClient)
|
||||
* - 返回泛型 T(反序列化响应 body 的 data 字段),不再返回 String
|
||||
* @param T 响应 data 的数据类型(任意类,simapi_serialization 反射反序列化)。
|
||||
*/
|
||||
@@ -41,7 +41,7 @@ public open class SimApiHttpClient {
|
||||
|
||||
/**
|
||||
* 发起签名请求(GET query 签名 + POST body)。
|
||||
* 对齐 C# SignQuery<T>:query 串 = SignFields + AppId + timestamp + nonce,整体拼 AppKey 取 MD5 作为 sign。
|
||||
* SignQuery<T>:query 串 = SignFields + AppId + timestamp + nonce,整体拼 AppKey 取 MD5 作为 sign。
|
||||
* @param url 请求路径(相对路径,自动拼接 server)。
|
||||
* @param body 请求体 JSON 字符串(可选)。
|
||||
* @param queries 额外查询参数(可选)。
|
||||
@@ -74,7 +74,7 @@ public open class SimApiHttpClient {
|
||||
|
||||
/**
|
||||
* 发起 AES 加密请求:body 加密后放入 {"data": "..."} 提交。
|
||||
* 对齐 C# AesQuery<T>(SimApiOneFieldRequest<string> { Data = Encrypt(body, AppKey) })。
|
||||
* AesQuery<T>:对应 SimApiOneFieldRequest<string> { Data = Encrypt(body, AppKey) }。
|
||||
* @param url 请求路径(相对路径,自动拼接 server)。
|
||||
* @param body 请求体 JSON 字符串。
|
||||
* @return 响应 data 字段反序列化后的 T。
|
||||
@@ -91,7 +91,6 @@ public open class SimApiHttpClient {
|
||||
|
||||
/**
|
||||
* 发起 AES 加密 + 签名请求。
|
||||
* 对齐 C# AesSignQuery<T>。
|
||||
* @param url 请求路径(相对路径,自动拼接 server)。
|
||||
* @param body 请求体 JSON 字符串。
|
||||
* @param queries 额外查询参数(可选)。
|
||||
@@ -105,7 +104,7 @@ public open class SimApiHttpClient {
|
||||
|
||||
/**
|
||||
* 发起 POST 请求并反序列化 SimApiResponse<T>,返回 data 字段。
|
||||
* 对齐 C# Query<T>:
|
||||
* Query<T>:
|
||||
* ErrorWhenFalse(IsSuccessStatusCode) → ReadFromJsonAsync<SimApiResponse<T>> → ErrorWhen(Code != 200) → return Data。
|
||||
* 注意:必须 noProxy(),否则会走系统代理(192.168.0.250:8118)导致连接被拒。
|
||||
* 反序列化使用 simapi_serialization(Deserialize<T> 免约束)。
|
||||
@@ -128,7 +127,7 @@ public open class SimApiHttpClient {
|
||||
SimApiError.errorWhenFalse(isSuccess(response.status), code: Int64(response.status),
|
||||
message: "HTTP ERROR: ${response.status}")
|
||||
let json = readBodyText(response.body)
|
||||
let result = JsonSerializer.Deserialize<SimApiResponse<T>>(json)
|
||||
let result = JsonSerializer.deserialize<SimApiResponse<T>>(json)
|
||||
SimApiError.errorWhen(result.code != 200, code: result.code, message: result.message)
|
||||
return result.data.getOrThrow()
|
||||
} finally {
|
||||
@@ -168,12 +167,12 @@ public open class SimApiHttpClient {
|
||||
}
|
||||
|
||||
private func aesEncrypt(plain: String): String {
|
||||
// 对齐 C#:SimApiAesUtil.Encrypt(plain, AppKey)(AES-256-CBC + PKCS7,Base64(IV + 密文))
|
||||
// SimApiAesUtil.Encrypt(plain, AppKey)(AES-256-CBC + PKCS7,Base64(IV + 密文))
|
||||
SimApiAesUtil.encrypt(plain, appKey)
|
||||
}
|
||||
|
||||
private static func generateNonce(): String {
|
||||
// 对齐 C#:nonce 直接用 Guid.NewGuid()
|
||||
// nonce 直接用 Guid.NewGuid()
|
||||
SimApiUtil.newGuid()
|
||||
}
|
||||
|
||||
+18
-18
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
* 自定义 IRequestDelegateFactory:在结果派发时自动封装响应,
|
||||
* 对齐 C# SimApiResponseFilter(IResultFilter)的行为:
|
||||
* 即 SimApiResponseFilter(IResultFilter)的行为:
|
||||
* - 返回 SimApiBaseResponse 或其子类 → 原样输出
|
||||
* - 返回 null/void(Unit)→ SimApiBaseResponse()({code:200, message:成功})
|
||||
* - 返回 String → SimApiResponse<String>(data 为字符串)
|
||||
@@ -35,7 +35,7 @@ import simcu::simapi.interfaces.*
|
||||
|
||||
/**
|
||||
* 自定义请求委托工厂:接管 soulsoft 的 ControllerRequestDelegateFactory,
|
||||
* 在结果派发时自动封装响应(对齐 C# SimApiResponseFilter)。
|
||||
* 在结果派发时自动封装响应。
|
||||
*/
|
||||
public class SimApiRequestDelegateFactory <: IRequestDelegateFactory {
|
||||
private let _mvcOptions: MvcOptions
|
||||
@@ -106,7 +106,7 @@ struct SimApiActionInvoker {
|
||||
// Query/Form/Route/Header/Services → soulsoft
|
||||
bound[index] = soulsoftBound[index]
|
||||
} else if (let Some(aes) <- parameter.findAnnotation<AesBody>()) {
|
||||
// @AesBody → 解密 body 后按参数类型反序列化(对齐 C# AesBodyModelBinder)
|
||||
// @AesBody → 解密 body 后按参数类型反序列化
|
||||
bound[index] = bindAesBody(context, parameter, aes)
|
||||
} else {
|
||||
// FromBody → simapi_serialization 按运行时类型反序列化(免 @Serialization 宏)
|
||||
@@ -135,14 +135,14 @@ struct SimApiActionInvoker {
|
||||
SimApiError.error(code: 400, message: "请求体不能为空")
|
||||
}
|
||||
try {
|
||||
return JsonSerializer.Deserialize(parameter.typeInfo, body)
|
||||
return JsonSerializer.deserialize(parameter.typeInfo, body)
|
||||
} catch (ex: Exception) {
|
||||
SimApiError.error(code: 400, message: "请求体反序列化失败: ${ex.message}")
|
||||
}
|
||||
()
|
||||
}
|
||||
|
||||
/// @AesBody 参数绑定:解密 body 后按参数类型反序列化(对齐 C# AesBodyModelBinder)
|
||||
/// @AesBody 参数绑定:解密 body 后按参数类型反序列化
|
||||
private func bindAesBody(context: ActionBindingContext, parameter: ParameterInfo, aes: AesBody): Any {
|
||||
// 1. 从 DI 解析 keyProvider(AesBodyProviderBase 实现)
|
||||
let provider = resolveAesProvider(aes.keyProvider)
|
||||
@@ -150,7 +150,7 @@ struct SimApiActionInvoker {
|
||||
let plain = SimApiAesBodyChecker.decryptBody(context.httpContext, provider)
|
||||
// 3. 按参数类型反序列化明文 JSON
|
||||
try {
|
||||
return JsonSerializer.Deserialize(parameter.typeInfo, plain)
|
||||
return JsonSerializer.deserialize(parameter.typeInfo, plain)
|
||||
} catch (ex: Exception) {
|
||||
SimApiError.error(code: 400, message: "AES body 反序列化失败: ${ex.message}")
|
||||
}
|
||||
@@ -176,7 +176,7 @@ struct SimApiActionInvoker {
|
||||
AesBodyProviderBase()
|
||||
}
|
||||
|
||||
/// 检查 @SimApiSign 注解并执行验签(对齐 C# SimApiSignAttribute.OnActionExecuting)
|
||||
/// 检查 @SimApiSign 注解并执行验签
|
||||
private func checkSimApiSign() {
|
||||
var sign: ?SimApiSign = None
|
||||
for (item in actionDescriptor.endpointMetadata) {
|
||||
@@ -241,8 +241,8 @@ struct SimApiActionInvoker {
|
||||
""
|
||||
}
|
||||
|
||||
/// 检查 @SimApiAuth 注解并执行鉴权(对齐 C# SimApiAuthAttribute.OnActionExecuting):
|
||||
/// 未登录 401 → 遍历执行 ISimApiAuthChecker → 类型权限 403
|
||||
/// 检查 @SimApiAuth 注解并执行鉴权:
|
||||
/// 未登录 401 → 遍历执行 SimApiAuthChecker → 类型权限 403
|
||||
private func checkSimApiAuth() {
|
||||
var auth: ?SimApiAuthAttribute = None
|
||||
for (item in actionDescriptor.endpointMetadata) {
|
||||
@@ -265,17 +265,17 @@ struct SimApiActionInvoker {
|
||||
SimApiError.error(code: 401, message: "需要登录")
|
||||
}
|
||||
|
||||
// 2. 遍历执行 ISimApiAuthChecker(对齐 C# GetServices<ISimApiAuthChecker>():一次解析全部实现)
|
||||
// 2. 遍历执行 SimApiAuthChecker
|
||||
let token = match (context.items.get("LoginToken")) {
|
||||
case Some(v) => if (let s: String <- v) { s } else { "" }
|
||||
case None => ""
|
||||
}
|
||||
let checkers = context.services.getAll<ISimApiAuthChecker>()
|
||||
let checkers = context.services.getAll<SimApiAuthChecker>()
|
||||
for (checker in checkers) {
|
||||
checker.run(loginItem, token)
|
||||
}
|
||||
|
||||
// 3. 类型权限校验 → 403(对齐 C# Types.Intersect(loginInfo.Type).Any(),支持逗号分隔多类型)
|
||||
// 3. 类型权限校验 → 403
|
||||
if (!auth.`type`.isEmpty()) {
|
||||
let requiredTypes = auth.`type`.split(",")
|
||||
var matched = false
|
||||
@@ -312,9 +312,9 @@ struct SimApiActionInvoker {
|
||||
}
|
||||
}
|
||||
|
||||
/// 结果派发 + 自动封装(对齐 C# SimApiResponseFilter)
|
||||
/// 结果派发 + 自动封装
|
||||
private func dispatchResult(actionResult: Any) {
|
||||
// @OriginResponse:跳过统一封装,原样输出(对齐 C# OnResultExecuting 遇注解直接 return)
|
||||
// @OriginResponse:跳过统一封装,原样输出
|
||||
var originResponse = false
|
||||
for (item in actionDescriptor.endpointMetadata) {
|
||||
if (item is OriginResponse) {
|
||||
@@ -324,7 +324,7 @@ struct SimApiActionInvoker {
|
||||
}
|
||||
if (originResponse) {
|
||||
if (let s: String <- actionResult) {
|
||||
// String 原样输出文本(对齐 C# string 返回直接写入)
|
||||
// String 原样输出文本
|
||||
context.response.contentType = "application/json; charset=utf-8"
|
||||
SimApiResponseWriter.write(context, s)
|
||||
} else {
|
||||
@@ -367,10 +367,10 @@ struct SimApiActionInvoker {
|
||||
}
|
||||
|
||||
/// 通过 DI 容器实例化控制器,并注入当前 HttpContext
|
||||
/// (通过 IBindRequestContext 接口而非 SimApiBaseController,避免 helpers↔controllers 循环依赖)
|
||||
/// (通过 BindRequestContext 接口而非 SimApiBaseController,避免 helpers↔controllers 循环依赖)
|
||||
private func createControllerInstance(): Object {
|
||||
let instance = ActivatorUtilities.createInstance(context.services, actionDescriptor.controllerType)
|
||||
if (let controller: IBindRequestContext <- instance) {
|
||||
if (let controller: BindRequestContext <- instance) {
|
||||
controller.bindRequestContext(context)
|
||||
}
|
||||
return instance
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
*/
|
||||
|
||||
package simcu::simapi.helpers
|
||||
@@ -11,7 +11,7 @@ import simcu::simapi.communications.*
|
||||
|
||||
/**
|
||||
* 响应封装:对写操作(Unit)返回统一成功响应,对已有 SimApiBaseResponse 透传。
|
||||
* 在仓颉版中以中间件形式实现,对应 C# 的 SimApiResponseFilter。
|
||||
* 在仓颉版中以中间件形式实现。
|
||||
*/
|
||||
public class SimApiResponseFilter {
|
||||
|
||||
@@ -26,7 +26,7 @@ public class SimApiResponseFilter {
|
||||
next(context)
|
||||
if (!context.response.hasStarted) {
|
||||
context.response.contentType = "application/json; charset=utf-8"
|
||||
SimApiResponseWriter.write(context, JsonSerializer.Serialize(SimApiBaseResponse()))
|
||||
SimApiResponseWriter.write(context, JsonSerializer.serialize(SimApiBaseResponse()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
*/
|
||||
|
||||
package simcu::simapi.helpers
|
||||
@@ -11,7 +11,7 @@ import soulsoft_web_http.*
|
||||
* 响应写出工具:写出响应体并缓存文本,供请求日志中间件读取。
|
||||
*
|
||||
* 背景:soulsoft 的 HttpResponse.body 为只写流(read 抛 UnsupportedException),
|
||||
* 无法像 C# 那样用 MemoryStream 替换 Body 捕获响应内容;
|
||||
* 无法像原版那样用 MemoryStream 替换 Body 捕获响应内容;
|
||||
* 故在统一写出入口缓存文本,请求日志中间件直接从 context.items 读取。
|
||||
*/
|
||||
public class SimApiResponseWriter {
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
*/
|
||||
|
||||
package simcu::simapi.helpers
|
||||
@@ -11,7 +11,7 @@ import simcu::serialization.*
|
||||
import simcu::simapi.communications.*
|
||||
|
||||
/**
|
||||
* 统一响应封装工具(对齐 C# SimApiResponseFilter 的包装分支)。
|
||||
* 统一响应封装工具。
|
||||
* 供 SimApiRequestDelegateFactory 与内置路由委托复用:
|
||||
* - SimApiBaseResponse(含子类)→ 原样输出
|
||||
* - String → SimApiResponse<String>(data 为字符串)
|
||||
@@ -45,6 +45,6 @@ public class SimApiResultWriter {
|
||||
/// simapi_serialization 序列化后直接写响应体(经 SimApiResponseWriter 缓存,供请求日志读取)
|
||||
private static func writeJson(context: HttpContext, obj: Any): Unit {
|
||||
context.response.contentType = "application/json; charset=utf-8"
|
||||
SimApiResponseWriter.write(context, JsonSerializer.Serialize(obj))
|
||||
SimApiResponseWriter.write(context, JsonSerializer.serialize(obj))
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
*/
|
||||
|
||||
package simcu::simapi.helpers
|
||||
@@ -11,13 +11,13 @@ import simcu::simapi.exceptions.*
|
||||
import simcu::simapi.interfaces.*
|
||||
|
||||
/**
|
||||
* 服务端验签校验器(对齐 C# Attributes/SimApiSignAttribute.OnActionExecuting)。
|
||||
* 服务端验签校验器。
|
||||
*
|
||||
* 仓颉无声明式 ActionFilter 机制,按项目惯例(同 requireLogin)由控制器在需要验签的方法开头调用:
|
||||
* SimApiSignChecker.verify(context, provider, cache)
|
||||
* 或标注 @SimApiSign 注解自动执行(SimApiRequestDelegateFactory)。
|
||||
*
|
||||
* 校验流程(与 C# 完全一致):
|
||||
* 校验流程:
|
||||
* 1. 提取 appId(Query/Header)
|
||||
* 2. provider.getKey(appId) 获取密钥
|
||||
* 3. 提取并解析 timestamp / nonce
|
||||
@@ -111,7 +111,7 @@ public class SimApiSignChecker {
|
||||
}
|
||||
}
|
||||
|
||||
/// 从 Query 或 Header 取参数(Query 优先,对齐 C# FirstOrDefault 语义)
|
||||
/// 从 Query 或 Header 取参数(Query 优先)
|
||||
private static func getParam(context: HttpContext, name: String): ?String {
|
||||
let q = context.request.query.get(name)
|
||||
if (q != None && q != Some("")) {
|
||||
@@ -1,13 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
* Helpers/SimApiStorage:S3/MinIO 兼容存储助手。
|
||||
*
|
||||
* 仓颉生态暂无 Minio SDK,此处自实现 AWS Signature V4(HMAC-SHA256 基于 stdx SHA256):
|
||||
* - 预签名 URL(PUT 上传 / GET 下载)
|
||||
* - 直接 PUT 上传(对象 + 建桶)
|
||||
* - HEAD 检测桶是否存在
|
||||
* 与 C# SimApiStorage(Minio SDK)的公开方法语义对齐:
|
||||
* 公开方法语义对齐:
|
||||
* GetUploadUrl / GetDownloadUrl / UploadFile / FullUrl / GetUrl / GetPath
|
||||
*/
|
||||
|
||||
@@ -26,7 +26,7 @@ import soulsoft_web_http.*
|
||||
import simcu::simapi.configurations.*
|
||||
|
||||
/**
|
||||
* 上传预签名 URL 响应(对齐 C# record GetUploadUrlResponse(UploadUrl, DownloadUrl, Path))。
|
||||
* 上传预签名 URL 响应。
|
||||
*/
|
||||
public class GetUploadUrlResponse {
|
||||
public var uploadUrl: String = ""
|
||||
@@ -43,12 +43,12 @@ public class GetUploadUrlResponse {
|
||||
}
|
||||
|
||||
/**
|
||||
* S3/MinIO 存储助手(对齐 C# Helpers/SimApiStorage)。
|
||||
* S3/MinIO 存储助手。
|
||||
*
|
||||
* 说明:
|
||||
* - 注册为 Scoped 以注入 IHttpContextAccessor(soulsoft DI 禁止 singleton 消费 scoped 服务);
|
||||
* 桶的检测/创建由静态守卫保证整个进程只执行一次(对齐 C# 构造函数中 BucketExists+MakeBucket)。
|
||||
* - fullUrl/getUrl 的 "~/" 分支依赖当前请求上下文(对齐 C# IHttpContextAccessor)。
|
||||
* 桶的检测/创建由静态守卫保证整个进程只执行一次。
|
||||
* - fullUrl/getUrl 的 "~/" 分支依赖当前请求上下文。
|
||||
*/
|
||||
public class SimApiStorage {
|
||||
private static var _bucketEnsured: Bool = false
|
||||
@@ -89,12 +89,12 @@ public class SimApiStorage {
|
||||
_useSsl = useSsl
|
||||
_host = host
|
||||
_httpContextAccessor = httpContextAccessor
|
||||
// 桶不存在则创建(对齐 C# BucketExists + MakeBucket;静态守卫保证只执行一次)
|
||||
// 桶不存在则创建
|
||||
ensureBucketOnce()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上传预签名 URL(对齐 C# GetUploadUrl,默认 7200 秒)。
|
||||
* 获取上传预签名 URL。
|
||||
*/
|
||||
public func getUploadUrl(path: String, expire!: Int64 = 7200): GetUploadUrlResponse {
|
||||
checkPath(path)
|
||||
@@ -104,7 +104,7 @@ public class SimApiStorage {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下载预签名 URL(对齐 C# GetDownloadUrl,默认 600 秒)。
|
||||
* 获取下载预签名 URL。
|
||||
*/
|
||||
public func getDownloadUrl(path: String, expire!: Int64 = 600): String {
|
||||
checkPath(path)
|
||||
@@ -113,7 +113,7 @@ public class SimApiStorage {
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接上传文件(对齐 C# UploadFile;data 为文件字节)。
|
||||
* 直接上传文件。
|
||||
*/
|
||||
public func uploadFile(path: String, data: Array<Byte>, contentType!: String = "image/png"): Unit {
|
||||
checkPath(path)
|
||||
@@ -122,7 +122,7 @@ public class SimApiStorage {
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除对象(对齐 C# Minio RemoveObjectsAsync;S3 原生 DeleteObjects 接口:
|
||||
* 批量删除对象(S3 原生 DeleteObjects 接口:
|
||||
* POST /{bucket}?delete,一次请求删除多个对象,无需逐个删除)。
|
||||
* @param paths 对象路径数组(每个须以 / 开头)。
|
||||
*/
|
||||
@@ -143,7 +143,7 @@ public class SimApiStorage {
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 path 获取完整的访问 URL(对齐 C# FullUrl)。
|
||||
* 使用 path 获取完整的访问 URL。
|
||||
*/
|
||||
public func fullUrl(path: ?String): ?String {
|
||||
if (let Some(p) <- path) {
|
||||
@@ -162,7 +162,7 @@ public class SimApiStorage {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个 Path 的访问 URL(对齐 C# GetUrl)。
|
||||
* 获取一个 Path 的访问 URL。
|
||||
*/
|
||||
public func getUrl(path: ?String): ?String {
|
||||
if (let Some(p) <- path) {
|
||||
@@ -181,7 +181,7 @@ public class SimApiStorage {
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 URL 中获取相对路径(对齐 C# GetPath;去掉 Endpoint/Bucket 或 ServeUrl 前缀)。
|
||||
* 从 URL 中获取相对路径。
|
||||
*/
|
||||
public func getPath(url: ?String): ?String {
|
||||
if (let Some(u) <- url) {
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
*/
|
||||
|
||||
package simcu::simapi.helpers
|
||||
@@ -17,7 +17,7 @@ import simcu::simapi.communications.*
|
||||
import simcu::simapi.macros.*
|
||||
|
||||
/**
|
||||
* 工具类:对应 C# 的 SimApi.Helpers.SimApiUtil。
|
||||
* 工具类。
|
||||
* 提供时间、哈希、Base64、JSON、校验等常用能力。
|
||||
*/
|
||||
public class SimApiUtil {
|
||||
@@ -88,7 +88,7 @@ public class SimApiUtil {
|
||||
toHexString(sha.finish())
|
||||
}
|
||||
|
||||
/// 按 .NET Md5/Sha1 的 mode 格式化:x2/x3/x4 → 每个字节 2/3/4 位十六进制
|
||||
/// 按 Md5/Sha1 的 mode 格式化:x2/x3/x4 → 每个字节 2/3/4 位十六进制
|
||||
private static func formatHex(bytes: Array<Byte>, mode: String): String {
|
||||
if (mode == "x2") {
|
||||
return toHexString(bytes)
|
||||
@@ -134,14 +134,14 @@ public class SimApiUtil {
|
||||
|
||||
/**
|
||||
* 判断是否是 Email 地址。
|
||||
* 说明:.NET 使用 System.Net.Mail.MailAddress 校验,仓颉无等价 API,此处用正则近似。
|
||||
* 说明:原版使用 System.Net.Mail.MailAddress 校验,仓颉无等价 API,此处用正则近似。
|
||||
*/
|
||||
public static func checkEmail(email: String): Bool {
|
||||
Regex("^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$").matches(email)
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 UUID v4 字符串(对齐 C# Guid.NewGuid().ToString():小写、8-4-4-4-12 连字符格式)。
|
||||
* 生成 UUID v4 字符串。
|
||||
* 仓颉标准库没有 GUID 生成器(stdx 的 GUID 是 stdx.net.http 内部类型),
|
||||
* 此处用随机数自行构造:16 字节随机数 + 版本位(4)+ 变体位(10)。
|
||||
*/
|
||||
@@ -172,18 +172,18 @@ public class SimApiUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象序列化为 JSON 字符串(对齐 C# SimApiUtil.Json)。
|
||||
* 将对象序列化为 JSON 字符串。
|
||||
* @param obj 任意对象(None 输出 null)。
|
||||
*/
|
||||
public static func json(obj: ?Any): String {
|
||||
if (let Some(obj) <- obj) {
|
||||
return JsonSerializer.Serialize(obj)
|
||||
return JsonSerializer.serialize(obj)
|
||||
}
|
||||
"null"
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON 字符串转义(对齐 C# 内部转义逻辑)。
|
||||
* JSON 字符串转义。
|
||||
* @param s 原始字符串。
|
||||
* @return 转义后可直接放入 JSON 字符串字面量的内容。
|
||||
*/
|
||||
@@ -203,17 +203,17 @@ public class SimApiUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 JSON 字符串反序列化为 T(对齐 C# SimApiUtil.FromJson<T>)。
|
||||
* 从 JSON 字符串反序列化为 T。
|
||||
* @param T 目标类型(任意类,无需接口/宏约束)。
|
||||
* @param jsonString JSON 字符串。
|
||||
* @return 反序列化结果。
|
||||
*/
|
||||
public static func fromJson<T>(jsonString: String): T {
|
||||
JsonSerializer.Deserialize<T>(jsonString)
|
||||
JsonSerializer.deserialize<T>(jsonString)
|
||||
}
|
||||
|
||||
/**
|
||||
* 对象 Base64 编码(对象 → JSON → Base64,对齐 C# Base64Encode(object))。
|
||||
* 对象 Base64 编码(对象 → JSON → Base64)。
|
||||
* @param obj 任意对象(DTO/基础类型/HashMap 等)。
|
||||
* @return Base64 字符串。
|
||||
*/
|
||||
@@ -223,7 +223,7 @@ public class SimApiUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64 → JSON → T 反序列化(对齐 C# Base64Decode<T>)。
|
||||
* Base64 → JSON → T 反序列化。
|
||||
* @param T 目标类型(任意类,无需接口/宏约束)。
|
||||
* @param base64Str Base64 字符串。
|
||||
* @return 反序列化结果。
|
||||
@@ -233,7 +233,7 @@ public class SimApiUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页(对齐 C# Paginate 扩展;仓颉无 IQueryable,改为对 Array<T> 切片)。
|
||||
* 分页。
|
||||
*/
|
||||
public static func paginate<T>(list: Array<T>, page: Int64, count: Int64): Array<T> {
|
||||
let p = if (page < 1) { 1 } else { page }
|
||||
@@ -247,5 +247,5 @@ public class SimApiUtil {
|
||||
list[skip..end]
|
||||
}
|
||||
|
||||
// 说明:C# 的 XmlDeserialize<T> 依赖 System.Xml.Serialization,仓颉生态无 XML 序列化库,未移植。
|
||||
// 说明:XmlDeserialize<T> 依赖 System.Xml.Serialization,仓颉生态无 XML 序列化库,未移植。
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* AES body 密钥提供器基类(对齐 C# ModelBinders/AesBodyProviderBase)。
|
||||
* 遵循 MIT 许可证。
|
||||
* AES body 密钥提供器基类。
|
||||
*
|
||||
* 说明:本类含配置字段(appIdName),故用 open class 而非 interface
|
||||
* (Cangjie 接口不能声明字段),与 .NET 抽象类对应。应用继承本类并实现 getKey。
|
||||
* (Cangjie 接口不能声明字段),与抽象类对应。应用继承本类并实现 getKey。
|
||||
*/
|
||||
|
||||
package simcu::simapi.interfaces
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* Interfaces/IBindRequestContext:请求上下文绑定接口。
|
||||
* 遵循 MIT 许可证。
|
||||
* Interfaces/BindRequestContext:请求上下文绑定接口。
|
||||
*
|
||||
* 说明:SimApiBaseController 实现本接口,由 SimApiRequestDelegateFactory(simapi.helpers)
|
||||
* 在创建控制器后注入当前 HttpContext。放在 interfaces 包是为了避免
|
||||
@@ -15,7 +15,7 @@ import soulsoft_web_http.*
|
||||
/**
|
||||
* 请求上下文绑定接口:控制器实现后,框架在派发请求时把当前 HttpContext 注入。
|
||||
*/
|
||||
public interface IBindRequestContext {
|
||||
public interface BindRequestContext {
|
||||
/**
|
||||
* 绑定当前请求上下文。
|
||||
* @param context 当前请求的 HttpContext。
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
*/
|
||||
|
||||
package simcu::simapi.interfaces
|
||||
@@ -9,9 +9,8 @@ import simcu::simapi.communications.*
|
||||
|
||||
/**
|
||||
* 认证后处理 Hook:实现后每次认证成功都会调用。
|
||||
* 对应 C# 的 SimApi.Interfaces.ISimApiAuthChecker。
|
||||
*/
|
||||
public interface ISimApiAuthChecker {
|
||||
public interface SimApiAuthChecker {
|
||||
/**
|
||||
* 认证成功后执行。
|
||||
* @param loginItem 登录信息。
|
||||
+3
-3
@@ -1,10 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 签名提供器基类(对齐 C# ModelBinders/SimApiSignProviderBase)。
|
||||
* 遵循 MIT 许可证。
|
||||
* 签名提供器基类。
|
||||
*
|
||||
* 说明:本类含配置字段(appIdName/queryExpires 等),故用 open class 而非 interface
|
||||
* (Cangjie 接口不能声明字段),与 .NET 抽象类对应。应用继承本类并实现 getKey。
|
||||
* (Cangjie 接口不能声明字段),与抽象类对应。应用继承本类并实现 getKey。
|
||||
*/
|
||||
|
||||
package simcu::simapi.interfaces
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
*/
|
||||
|
||||
package simcu::simapi.logger
|
||||
@@ -11,7 +11,7 @@ import std.time.*
|
||||
import soulsoft_extensions_logging.*
|
||||
|
||||
/**
|
||||
* SimApi 日志记录器:输出格式对齐 C# 原版 SimApiLogger。
|
||||
* SimApi 日志记录器。
|
||||
* 格式:`[ 分类 ][ 时间 ][ 级别 ]\n消息\n[异常]`
|
||||
* 按日志级别着色输出。
|
||||
*/
|
||||
@@ -39,7 +39,7 @@ public class SimApiLogger <: ILogger {
|
||||
sb.append("${ex}\n")
|
||||
}
|
||||
sb.append(resetColor())
|
||||
// 对齐 C# Console.WriteLine:message 末尾 \n 之后再补一个 \n,形成空行分隔
|
||||
// message 末尾 \n 之后再补一个 \n,形成空行分隔
|
||||
sb.append("\n")
|
||||
|
||||
let writer = getStdOut()
|
||||
@@ -52,7 +52,7 @@ public class SimApiLogger <: ILogger {
|
||||
}
|
||||
|
||||
/**
|
||||
* 级别名称,对齐 C# 的 LogLevel.ToString()。
|
||||
* 级别名称。
|
||||
*/
|
||||
private static func levelName(logLevel: LogLevel): String {
|
||||
match (logLevel) {
|
||||
@@ -67,7 +67,7 @@ public class SimApiLogger <: ILogger {
|
||||
}
|
||||
|
||||
/**
|
||||
* 级别对应 ANSI 前景色(对齐 C# ConsoleColor):
|
||||
* 级别对应 ANSI 前景色:
|
||||
* Debug→DarkMagenta, Info→DarkCyan, Warn→Yellow, Error→Red, Fatal→DarkRed, 其他→White
|
||||
*/
|
||||
private static func levelColor(logLevel: LogLevel): String {
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
*/
|
||||
|
||||
package simcu::simapi.middlewares
|
||||
@@ -11,7 +11,6 @@ import simcu::simapi.helpers.*
|
||||
|
||||
/**
|
||||
* 认证信息获取中间件:从 Header Token 或 Query token 解析登录信息并注入上下文。
|
||||
* 对应 C# 的 SimApi.Middlewares.SimApiAuthMiddleware。
|
||||
*/
|
||||
public class SimApiAuthMiddleware <: IMiddleware {
|
||||
private let _auth: SimApiAuth
|
||||
@@ -21,7 +20,7 @@ public class SimApiAuthMiddleware <: IMiddleware {
|
||||
}
|
||||
|
||||
/**
|
||||
* 中间件入口(IMiddleware 风格,对齐 C# UseMiddleware<T> 的 InvokeAsync)。
|
||||
* 中间件入口(IMiddleware 风格)。
|
||||
*/
|
||||
public func invoke(context: HttpContext, next: RequestDelegate): Unit {
|
||||
var token = context.request.headers.get("Token")
|
||||
+3
-4
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
*/
|
||||
|
||||
package simcu::simapi.middlewares
|
||||
@@ -15,7 +15,6 @@ import simcu::simapi.helpers.*
|
||||
|
||||
/**
|
||||
* 异常处理中间件:全异常捕获,统一输出 HTTP 200 + JSON 响应。
|
||||
* 对应 C# 的 SimApi.Middlewares.SimApiExceptionMiddleware。
|
||||
*/
|
||||
public class SimApiExceptionMiddleware <: IMiddleware {
|
||||
private let _options: SimApiOptions
|
||||
@@ -27,7 +26,7 @@ public class SimApiExceptionMiddleware <: IMiddleware {
|
||||
}
|
||||
|
||||
/**
|
||||
* 中间件入口(IMiddleware 风格,对齐 C# UseMiddleware<T> 的 InvokeAsync)。
|
||||
* 中间件入口(IMiddleware 风格)。
|
||||
*/
|
||||
public func invoke(context: HttpContext, next: RequestDelegate): Unit {
|
||||
try {
|
||||
@@ -71,6 +70,6 @@ public class SimApiExceptionMiddleware <: IMiddleware {
|
||||
}
|
||||
|
||||
private func responseJson(response: SimApiBaseResponse): String {
|
||||
JsonSerializer.Serialize(response)
|
||||
JsonSerializer.serialize(response)
|
||||
}
|
||||
}
|
||||
+12
-13
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
*/
|
||||
|
||||
package simcu::simapi.middlewares
|
||||
@@ -17,16 +17,15 @@ import simcu::simapi.helpers.*
|
||||
|
||||
/**
|
||||
* 请求日志中间件:记录请求方法、URL、请求头、请求体、响应状态码、响应体、耗时与异常。
|
||||
* 对应 C# 的 SimApi.Middlewares.SimApiRequestLogMiddleware。
|
||||
*
|
||||
* 对齐说明:
|
||||
* - 请求体按 JSON 字段级截断(对齐 C#:仅对超长字符串字段截断,保留结构)
|
||||
* - 捕获下游异常并记录,随后重抛(对齐 C# ExceptionDispatchInfo + edi.Throw)
|
||||
* - 请求体按 JSON 字段级截断
|
||||
* - 捕获下游异常并记录,随后重抛
|
||||
* - 响应体:soulsoft HttpResponse.body 只读且不可读回(read 抛 UnsupportedException),
|
||||
* 无法像 C# 那样用 MemoryStream 替换捕获;改为在各统一写出入口
|
||||
* 无法像原版那样用 MemoryStream 替换捕获;改为在各统一写出入口
|
||||
* (SimApiResponseWriter)缓存响应文本,此处直接读取。
|
||||
* 响应行格式:*( Response [status] ) => [ N bytes ],随后换行输出响应体结构;
|
||||
* ShowFullResponse=false 时截断到 200 字符(对齐 C#,长度仍显示完整字节数)
|
||||
* ShowFullResponse=false 时截断到 200 字符
|
||||
* - ShowFullUrl=false 时仅显示路径+查询串;ShowRunTime=true 时请求行显示 [POST] (xxxms)
|
||||
*/
|
||||
public class SimApiRequestLogMiddleware <: IMiddleware {
|
||||
@@ -39,7 +38,7 @@ public class SimApiRequestLogMiddleware <: IMiddleware {
|
||||
}
|
||||
|
||||
/**
|
||||
* 中间件入口(IMiddleware 风格,对齐 C# UseMiddleware<T> 的 InvokeAsync)。
|
||||
* 中间件入口(IMiddleware 风格)。
|
||||
*/
|
||||
public func invoke(context: HttpContext, next: RequestDelegate): Unit {
|
||||
let start = MonoTime.now()
|
||||
@@ -61,7 +60,7 @@ public class SimApiRequestLogMiddleware <: IMiddleware {
|
||||
sb.append("*( RequestBody ) =>\n")
|
||||
sb.append(readRequestBody(context))
|
||||
|
||||
// 调用下一级,捕获异常以便记录并重抛(对齐 C# ExceptionDispatchInfo)
|
||||
// 调用下一级,捕获异常以便记录并重抛
|
||||
var exception: ?Exception = None
|
||||
try {
|
||||
next(context)
|
||||
@@ -96,7 +95,7 @@ public class SimApiRequestLogMiddleware <: IMiddleware {
|
||||
let display = if (_options.simApiRequestLogOptions.showFullResponse) {
|
||||
responseBody
|
||||
} else if (responseBody.size > 200) {
|
||||
// 对齐 C#:ShowFullResponse=false 时截断到 200 字符(长度仍显示完整字节数)
|
||||
// ShowFullResponse=false 时截断到 200 字符(长度仍显示完整字节数)
|
||||
responseBody[0..200] + "...(${responseBody.size})"
|
||||
} else {
|
||||
responseBody
|
||||
@@ -114,14 +113,14 @@ public class SimApiRequestLogMiddleware <: IMiddleware {
|
||||
|
||||
_logger.info(sbHead.toString() + sb.toString())
|
||||
|
||||
// 重抛原异常(对齐 C# edi?.Throw()),由外层 ExceptionMiddleware 处理
|
||||
// 重抛原异常,由外层 ExceptionMiddleware 处理
|
||||
if (let Some(ex) <- exception) {
|
||||
throw ex
|
||||
}
|
||||
}
|
||||
|
||||
/// 构造完整请求 URL:stdx 服务端请求 URL 只有路径(无 scheme/host),需手动拼接。
|
||||
/// 对齐 C# 的 {Scheme}://{Host}{Path}{QueryString};Host 优先取 Host 请求头。
|
||||
/// {Scheme}://{Host}{Path}{QueryString};Host 优先取 Host 请求头。
|
||||
private func buildDisplayUrl(context: HttpContext): String {
|
||||
var sb = StringBuilder()
|
||||
let scheme = context.request.scheme
|
||||
@@ -196,7 +195,7 @@ public class SimApiRequestLogMiddleware <: IMiddleware {
|
||||
}
|
||||
}
|
||||
|
||||
/// 请求体截断:JSON 字段级截断(对齐 C#:仅对超长字符串字段截断),非 JSON 则整串截断
|
||||
/// 请求体截断:JSON 字段级截断,非 JSON 则整串截断
|
||||
private func truncateBody(body: String): String {
|
||||
let maxLen = _options.simApiRequestLogOptions.requestStringLogLength
|
||||
if (maxLen <= 0) {
|
||||
@@ -204,7 +203,7 @@ public class SimApiRequestLogMiddleware <: IMiddleware {
|
||||
}
|
||||
try {
|
||||
// 用 simapi_serialization 解析为动态结构,仅截断超长字符串字段(保持结构)
|
||||
let map = JsonSerializer.Deserialize<HashMap<String, Any>>(body)
|
||||
let map = JsonSerializer.deserialize<HashMap<String, Any>>(body)
|
||||
var sb = StringBuilder()
|
||||
sb.append("{")
|
||||
var first = true
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
*/
|
||||
|
||||
package simcu::simapi.models
|
||||
@@ -11,7 +11,7 @@ import std.time.*
|
||||
import simcu::simapi.helpers.*
|
||||
|
||||
/**
|
||||
* 实体基类(对齐 C# Models/SimApiBaseModel)。
|
||||
* 实体基类。
|
||||
* 提供:
|
||||
* - Id(默认 Guid)、CreatedAt / UpdatedAt(默认当前时间)
|
||||
* - MapData:反射式字段映射(源 → 目标,同名 + 同类型;可忽略字段/白名单)
|
||||
@@ -30,7 +30,7 @@ public open class SimApiBaseModel {
|
||||
|
||||
|
||||
/**
|
||||
* 反射映射:把 source 的同名同类型非忽略字段赋值到 this(对齐 C# MapData(source, mapAll))。
|
||||
* 反射映射:把 source 的同名同类型非忽略字段赋值到 this。
|
||||
* @param source 源对象。
|
||||
* @param mapAll 为 true 时连忽略字段(Id/CreatedAt/UpdatedAt)也映射。
|
||||
*/
|
||||
@@ -46,7 +46,7 @@ public open class SimApiBaseModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* 反射映射:仅映射白名单字段(对齐 C# MapData(source, mapFields))。
|
||||
* 反射映射:仅映射白名单字段。
|
||||
* @param source 源对象。
|
||||
* @param mapFields 白名单字段名。
|
||||
*/
|
||||
@@ -62,7 +62,7 @@ public open class SimApiBaseModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新 UpdatedAt 为当前时间(对齐 C# UpdateTime)。
|
||||
* 更新 UpdatedAt 为当前时间。
|
||||
*/
|
||||
public func updateTime(): Unit {
|
||||
let targetProps = collectProps(TypeInfo.of(this))
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* 移植自 C# 项目 SimApi(E:\simcu\simapi-net),遵循 MIT 许可证。
|
||||
* 遵循 MIT 许可证。
|
||||
*
|
||||
* 对齐 .NET:根命名空间 SimApi 下的静态类 SimApiExtensions(AddSimApi + UseSimApi)。
|
||||
* 根命名空间 SimApi 下的静态类 SimApiExtensions(AddSimApi + UseSimApi)。
|
||||
* 本文件同时是 simapi 根包的入口锚点:cjpm 要求 src 根目录至少有一个 .cj 文件,
|
||||
* 否则不会扫描 src 子目录(helpers/controllers/...),整个包将编译为空。
|
||||
*
|
||||
@@ -44,7 +44,7 @@ import simcu::simapi.logger.*
|
||||
import simcu::simapi.middlewares.*
|
||||
|
||||
/**
|
||||
* SimApi 扩展入口(对齐 C# 根命名空间 SimApi 的静态类 SimApiExtensions)。
|
||||
* SimApi 扩展入口。
|
||||
*/
|
||||
public class SimApiExtensions {
|
||||
private init() {}
|
||||
@@ -58,10 +58,10 @@ public class SimApiExtensions {
|
||||
public static func addSimApi(builder: WebHostBuilder, configure: (SimApiOptions) -> Unit): WebHostBuilder {
|
||||
// 自动注册路由(对齐 builder.Services.AddRouting())
|
||||
builder.services.addRouting()
|
||||
// 先构造配置,供后续按开关注册服务(对齐 C# AddSimApi 中先读 options 再注册)
|
||||
// 先构造配置,供后续按开关注册服务
|
||||
let options = SimApiOptions()
|
||||
configure(options)
|
||||
// 响应封装(对齐 C# SimApiResponseFilter,受 EnableSimApiResponseFilter 开关控制):
|
||||
// 响应封装:
|
||||
// 启用时注册自定义 IRequestDelegateFactory 自动封装响应。
|
||||
// 必须在 addControllers 之前:soulsoft 用 tryAddSingleton 注册,先到先得,不会被覆盖。
|
||||
// 未启用时使用 soulsoft 默认派发(String→ContentResult / ISerializable→ObjectResult / 其余→204)。
|
||||
@@ -69,7 +69,7 @@ public class SimApiExtensions {
|
||||
builder.services.addSingleton<IRequestDelegateFactory, SimApiRequestDelegateFactory>()
|
||||
}
|
||||
// 自动注册 MVC + 控制器(对齐 builder.Services.AddControllers())
|
||||
// 自动扫描调用者包中的 Controller 子类(对齐 C# 的 Assembly.GetTypes() 扫描)
|
||||
// 自动扫描调用者包中的 Controller 子类
|
||||
let controllers = SimApiControllerScanner.scan()
|
||||
addControllers(builder.services, controllers)
|
||||
// 注册 SimApi 服务
|
||||
@@ -87,16 +87,16 @@ public class SimApiExtensions {
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用 SimApi 中间件与内置路由(日志输出对齐 C# UseSimApi)。
|
||||
* 应用 SimApi 中间件与内置路由(日志输出)。
|
||||
* @param host 构建完成的主机。
|
||||
*/
|
||||
public static func useSimApi(host: WebHost): Unit {
|
||||
let options = host.services.getOrThrow<SimApiOptions>()
|
||||
let loggerFactory = host.services.getOrThrow<ILoggerFactory>()
|
||||
// 对齐 C# ILogger<SimApiOptions>:分类名为 SimApiOptions 的全限定名
|
||||
// 分类名为 SimApiOptions 的全限定名
|
||||
let logger = loggerFactory.createLogger<SimApiOptions>()
|
||||
|
||||
// ===== 基础信息(对齐 C# UseSimApi(IHost) 开头) =====
|
||||
// ===== 基础信息 =====
|
||||
let now = DateTime.now()
|
||||
logger.info("当前时区: ${now.zoneId}")
|
||||
logger.info("主应用版本: ${SimApiUtil.appVersion}\nSimApi版本: ${SimApiUtil.simApiVersion}")
|
||||
@@ -112,7 +112,7 @@ public class SimApiExtensions {
|
||||
}
|
||||
|
||||
// SimApiStorage(已实现:addSimApi 中注册 Scoped,桶初始化惰性执行;
|
||||
// 对齐 C# 的 GetService 预热,但 scoped 服务不能从根解析,故仅输出配置日志)
|
||||
// 但 scoped 服务不能从根解析,故仅输出配置日志)
|
||||
if (options.enableSimApiStorage) {
|
||||
logger.info("开始配置 SimApiStorage...")
|
||||
}
|
||||
@@ -133,35 +133,35 @@ public class SimApiExtensions {
|
||||
logger.info("开始配置 SimApiJob ...")
|
||||
}
|
||||
|
||||
// ===== 中间件与路由(对齐 C# UseSimApi(WebApplication) 的挂载顺序) =====
|
||||
// C# 挂载顺序(先挂载 = 外层):ForwardedHeaders(L419) → CORS(L425) → AuthGate(L454) → Auth(L462)
|
||||
// ===== 中间件与路由 =====
|
||||
// 挂载顺序(先挂载 = 外层):ForwardedHeaders(L419) → CORS(L425) → AuthGate(L454) → Auth(L462)
|
||||
// → 内置路由(L465-496) → Swagger(L502) → RequestLog(L519) → Exception(L525) → LowerUrl → Job
|
||||
|
||||
// ForwardedHeaders(占位:soulsoft 暂无内置,对齐 C# 最先挂载)
|
||||
// ForwardedHeaders(占位:soulsoft 暂无内置)
|
||||
if (options.enableForwardHeaders) {
|
||||
logger.info("开始配置ForwardedHeaders...")
|
||||
}
|
||||
|
||||
// CORS(对齐 C# builder.UseCors("any"))
|
||||
// CORS
|
||||
if (options.enableCors) {
|
||||
logger.info("开始配置 Cors全部允许...")
|
||||
host.useCors()
|
||||
}
|
||||
|
||||
// AuthGate(对齐 C# UseMiddleware<SimApiAuthCenterMiddleware>)
|
||||
// AuthGate
|
||||
if (options.enableSimApiAuthGate) {
|
||||
logger.info("开始配置 SimApiAuthGate...")
|
||||
host.use<SimApiAuthCenterMiddleware>()
|
||||
}
|
||||
|
||||
// 认证中间件(对齐 C# builder.UseMiddleware<SimApiAuthMiddleware>())
|
||||
// 认证中间件
|
||||
if (options.enableSimApiAuth) {
|
||||
logger.info("开始配置 SimApiAuth...")
|
||||
host.use<SimApiAuthMiddleware>()
|
||||
}
|
||||
|
||||
// 内置路由:RouteOptions 自定义路径时真实注册(默认路径已由内置控制器特性路由覆盖,
|
||||
// 对齐 C# MapControllerRoute 语义;soulsoft 无约定路由 defaults,用 mapGet/mapPost 委托实现)
|
||||
// 因 soulsoft 无约定路由 defaults,用 mapGet/mapPost 委托实现)
|
||||
let routeOptions = options.simApiRouteOptions
|
||||
if (let Some(route) <- routeOptions.userInfoRoute) {
|
||||
if (route != "/user/info") {
|
||||
@@ -219,18 +219,18 @@ public class SimApiExtensions {
|
||||
logger.info("注册内置Route: WebConfig => ${route}")
|
||||
}
|
||||
|
||||
// SimApiDoc(占位,对齐 C# UseSwagger/UseSwaggerUI)
|
||||
// SimApiDoc(占位:soulsoft 暂无内置 Swagger 文档页)
|
||||
if (options.enableSimApiDoc) {
|
||||
logger.info("开始配置 SimApiDoc...")
|
||||
}
|
||||
|
||||
// 请求日志中间件(对齐 C# builder.UseMiddleware<SimApiRequestLogMiddleware>())
|
||||
// 请求日志中间件
|
||||
if (options.enableRequestLog) {
|
||||
logger.info("开始配置 SimApiRequestLog...")
|
||||
host.use<SimApiRequestLogMiddleware>()
|
||||
}
|
||||
|
||||
// 异常中间件最后挂载(最内层,对齐 C# builder.UseMiddleware<SimApiExceptionMiddleware>())
|
||||
// 异常中间件最后挂载(最内层)
|
||||
if (options.enableSimApiException) {
|
||||
logger.info("开始配置 SimApiException...")
|
||||
host.use<SimApiExceptionMiddleware>()
|
||||
@@ -247,12 +247,12 @@ public class SimApiExtensions {
|
||||
}
|
||||
|
||||
// 响应封装(已实现:addSimApi 中按开关注册 SimApiRequestDelegateFactory 自动封装,
|
||||
// 对齐 C# SimApiResponseFilter;此处仅输出配置日志)
|
||||
// 此处仅输出配置日志)
|
||||
if (options.enableSimApiResponseFilter) {
|
||||
logger.info("开始配置 SimApiResponseFilter...")
|
||||
}
|
||||
|
||||
// 映射控制器端点(对齐 C# UseSimApi 中的 MapControllers)
|
||||
// 映射控制器端点
|
||||
let callSiteFactory = host.services.getOrThrow<IServiceProviderIsService>()
|
||||
if (callSiteFactory.isService<ApplicationPartManager>()) {
|
||||
host.mapControllers()
|
||||
@@ -262,10 +262,10 @@ public class SimApiExtensions {
|
||||
// ===== 私有辅助 =====
|
||||
|
||||
private static func addSimApiCore(builder: WebHostBuilder, options: SimApiOptions): WebHostBuilder {
|
||||
// 注册单例配置(对齐 C# builder.AddSingleton(simApiOptions))
|
||||
// 注册单例配置
|
||||
builder.services.addSingleton<SimApiOptions>(options)
|
||||
// 子配置不单独注册:中间件统一注入 SimApiOptions 后访问其属性
|
||||
// (对齐 C# SimApiExceptionMiddleware(..., SimApiOptions simApiOptions) 风格)
|
||||
|
||||
|
||||
// 自定义日志格式(替换默认 console provider)
|
||||
if (options.enableLogger) {
|
||||
@@ -277,20 +277,20 @@ public class SimApiExtensions {
|
||||
}
|
||||
|
||||
// 中间件无需注册:挂载时由 ActivatorUtilities 从 DI 解析构造参数创建
|
||||
// (对齐 C# builder.UseMiddleware<T>(),其中间件由 UseMiddleware 创建)
|
||||
|
||||
|
||||
// 认证(DI 自动注入 SimApiOptions)
|
||||
if (options.enableSimApiAuth) {
|
||||
builder.services.addSingleton<SimApiAuth, SimApiAuth>()
|
||||
}
|
||||
|
||||
// ISimApiAuthChecker 自动扫描注册(对齐 C# AddSimApi 中遍历调用者程序集 AddScoped(ISimApiAuthChecker, type))
|
||||
// 扫描调用者包中的实现类,按【接口】注册;执行时用 getAll<ISimApiAuthChecker>() 一次解析全部实现,
|
||||
// SimApiAuthChecker 自动扫描注册
|
||||
// 扫描调用者包中的实现类,按【接口】注册;执行时用 getAll<SimApiAuthChecker>() 一次解析全部实现,
|
||||
// 无需在 SimApiOptions 里维护类型列表。
|
||||
if (options.enableSimApiAuth) {
|
||||
let checkers = SimApiControllerScanner.scanAuthCheckers()
|
||||
for (checkerType in checkers) {
|
||||
builder.services.addScoped(TypeInfo.of<ISimApiAuthChecker>(), checkerType)
|
||||
builder.services.addScoped(TypeInfo.of<SimApiAuthChecker>(), checkerType)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,22 +304,22 @@ public class SimApiExtensions {
|
||||
builder.services.addSingleton<SimApiHttpClient, SimApiHttpClient>()
|
||||
}
|
||||
|
||||
// 存储(S3/MinIO,对齐 C# AddHttpContextAccessor + AddSingleton<SimApiStorage>;
|
||||
// 存储(S3/MinIO):
|
||||
// 仓颉版注册为 Scoped 以便注入 IHttpContextAccessor(DI 禁止 singleton 消费 scoped),
|
||||
// 桶初始化由静态守卫保证只执行一次)
|
||||
// 桶初始化由静态守卫保证只执行一次。
|
||||
if (options.enableSimApiStorage) {
|
||||
builder.services.addHttpContextAccessor()
|
||||
builder.services.addScoped<SimApiStorage, SimApiStorage>()
|
||||
}
|
||||
|
||||
// AuthGate 认证中心 SDK(对齐 C# 注册 SimApiAuthClient/Center/Iam 单例)
|
||||
// AuthGate 认证中心 SDK
|
||||
if (options.enableSimApiAuthGate) {
|
||||
builder.services.addSingleton<SimApiAuthClient, SimApiAuthClient>()
|
||||
builder.services.addSingleton<SimApiAuthCenter, SimApiAuthCenter>()
|
||||
builder.services.addSingleton<SimApiAuthIam, SimApiAuthIam>()
|
||||
}
|
||||
|
||||
// CORS(对齐 C# builder.Services.AddCors(policy => policy.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()))
|
||||
// CORS
|
||||
if (options.enableCors) {
|
||||
builder.services.addCors {
|
||||
cors =>
|
||||
@@ -335,7 +335,7 @@ public class SimApiExtensions {
|
||||
return builder
|
||||
}
|
||||
|
||||
/// 注册 MVC 服务,并注册 SimApi 内置控制器 + 用户控制器到 ApplicationPartManager(对齐 .NET AddControllers())。
|
||||
/// 注册 MVC 服务,并注册 SimApi 内置控制器 + 用户控制器到 ApplicationPartManager。
|
||||
private static func addControllers(services: ServiceCollection, controllerTypes: Array<TypeInfo>): MvcBuilder {
|
||||
// 调用 soulsoft_web_mvc 的无参 addControllers() 注册 MVC 核心服务
|
||||
let mvc = services.addControllers()
|
||||
Reference in New Issue
Block a user