From 73f60f6f1567d8fe3271e0d4e2232eeaa9b08029 Mon Sep 17 00:00:00 2001 From: xRain Date: Tue, 18 Aug 2026 00:07:00 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E5=85=A8=E6=A1=86=E6=9E=B6=20JSON?= =?UTF-8?q?=20=E6=93=8D=E4=BD=9C=E8=BF=81=E7=A7=BB=E5=88=B0=20simapi=5Fser?= =?UTF-8?q?ialization?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 依赖:新增 simapi_serialization path 依赖(保留 soulsoft_serialization 供 soulsoft web 栈传递使用) - SimApiJson.json / SimApiUtil.json → JsonSerializer.Serialize - SimApiUtil.fromJson / base64DecodeTo → JsonSerializer.Deserialize(去 ISerialization 约束) - SimApiHttpClient signQuery/aesQuery/aesSignQuery/query → 去约束,反序列化换新库 - SimApiBaseResponse/SimApiDataResponse/SimApiLoginItem/AesBodyRequest/AuthSDK DTO → 去 @Serialization 宏/去 soulsoft 接口,改纯 POCO(simapi_serialization 反射) - SimApiBaseResponse 标 @SerializerParent:子类响应序列化包含 _code/_message - SimApiResultWriter / SimApiExceptionMiddleware → 响应输出走 simapi_serialization(统一 _code/_message 格式) - SimApiRequestLogMiddleware 截断 → Deserialize> + 重序列化 - SimApiAuthDto JsonValue 字段 → HashMap;SimApiCache.get 去约束 - webdemo:AuthController readFromJson 换 Deserialize;cjpm.toml 补依赖 - 验证:webdemo 启动正常,GET/POST 接口响应格式统一,S3 存储全链路 OK --- cjpm.lock | 24 ++-- cjpm.toml | 1 + src/SimApiExtensions.cj | 2 - src/authsdk/SimApiAuthCenter.cj | 4 +- src/authsdk/SimApiAuthDto.cj | 18 +-- src/communications/SimApiBaseResponse.cj | 64 ++------- src/communications/SimApiDataResponse.cj | 58 +------- src/communications/SimApiJson.cj | 57 +------- src/communications/SimApiLoginItem.cj | 136 +----------------- src/helpers/SimApiAesBodyChecker.cj | 8 +- src/helpers/SimApiAuth.cj | 7 +- src/helpers/SimApiCache.cj | 8 +- src/helpers/SimApiHttpClient.cj | 20 +-- src/helpers/SimApiResultWriter.cj | 18 ++- src/helpers/SimApiUtil.cj | 19 +-- src/middlewares/SimApiExceptionMiddleware.cj | 5 +- src/middlewares/SimApiRequestLogMiddleware.cj | 39 ++--- 17 files changed, 108 insertions(+), 380 deletions(-) diff --git a/cjpm.lock b/cjpm.lock index 2acd944..4e15e44 100644 --- a/cjpm.lock +++ b/cjpm.lock @@ -2,19 +2,19 @@ version = 0 [requires] soulsoft_extensions_hosting = {version = "1.0.20260528"} - soulsoft_extensions_options_configuration = {version = "1.0.20260528"} - soulsoft_net_http = {version = "1.0.20260528"} - redis = {version = "1.0.20260627"} soulsoft_web_http = {version = "1.0.20260528"} - soulsoft_identity_claims = {version = "1.0.20260528"} - soulsoft_web_routing = {version = "1.0.20260528"} soulsoft_web_hosting = {version = "1.0.20260528"} - soulsoft_extensions_logging = {version = "1.0.20260528"} - soulsoft_extensions_configuration = {version = "1.0.20260528"} + soulsoft_web_routing = {version = "1.0.20260528"} + soulsoft_extensions_options_configuration = {version = "1.0.20260528"} soulsoft_web_mvc = {version = "1.0.20260528"} - soulsoft_extensions_logging_console = {version = "1.0.20260528"} - soulsoft_web_cors = {version = "1.0.20260528"} - soulsoft_extensions_logging_configuration = {version = "1.0.20260528"} - soulsoft_extensions_injection = {version = "1.0.20260528"} - soulsoft_serialization = {version = "1.0.20260528"} soulsoft_extensions_options = {version = "1.0.20260528"} + redis = {version = "1.0.20260627"} + soulsoft_web_cors = {version = "1.0.20260528"} + soulsoft_identity_claims = {version = "1.0.20260528"} + soulsoft_extensions_logging_console = {version = "1.0.20260528"} + soulsoft_extensions_logging = {version = "1.0.20260528"} + soulsoft_extensions_logging_configuration = {version = "1.0.20260528"} + soulsoft_serialization = {version = "1.0.20260528"} + soulsoft_extensions_configuration = {version = "1.0.20260528"} + soulsoft_net_http = {version = "1.0.20260528"} + soulsoft_extensions_injection = {version = "1.0.20260528"} diff --git a/cjpm.toml b/cjpm.toml index 5c36e65..9b32497 100644 --- a/cjpm.toml +++ b/cjpm.toml @@ -24,6 +24,7 @@ soulsoft_serialization = "1.0.20260528" soulsoft_net_http = "1.0.20260528" redis = "1.0.20260627" + simapi_serialization = { path = "../simapi-serialization" } [target] [target.x86_64-w64-mingw32] diff --git a/src/SimApiExtensions.cj b/src/SimApiExtensions.cj index fb68638..f4c6c32 100644 --- a/src/SimApiExtensions.cj +++ b/src/SimApiExtensions.cj @@ -25,8 +25,6 @@ import std.collection.* import std.convert.* import std.reflect.* import std.time.* -import soulsoft_serialization.* -import soulsoft_serialization.macros.* import soulsoft_web_http.* import soulsoft_web_hosting.* import soulsoft_web_mvc.* diff --git a/src/authsdk/SimApiAuthCenter.cj b/src/authsdk/SimApiAuthCenter.cj index 27d18e5..26301e1 100644 --- a/src/authsdk/SimApiAuthCenter.cj +++ b/src/authsdk/SimApiAuthCenter.cj @@ -11,6 +11,7 @@ import stdx.net.tls.* import stdx.net.tls.common.* import soulsoft_net_http.{HttpClient, HttpRequestMessage, JsonContent} import soulsoft_net_http.{HttpMethod as NetHttpMethod} +import simapi_serialization.* import simapi.communications.* import simapi.helpers.* @@ -62,7 +63,8 @@ public class SimApiAuthCenter { let response = http.send(request) try { response.ensureSuccessStatusCode() - let resp = response.content.readFromJson() + let json = response.content.readAsString() + let resp = JsonSerializer.Deserialize(json) SimApiError.errorWhen(resp._code != 200, code: 400, message: "签名验证失败") } finally { response.close() diff --git a/src/authsdk/SimApiAuthDto.cj b/src/authsdk/SimApiAuthDto.cj index 395d4db..83b1ac5 100644 --- a/src/authsdk/SimApiAuthDto.cj +++ b/src/authsdk/SimApiAuthDto.cj @@ -6,19 +6,17 @@ * 说明:C# 中这些 DTO 是 SimApiAuthCenterDto / SimApiAuthIamDto 的嵌套类; * 仓颉不支持在类体内声明嵌套类(unexpected class declaration in class body), * 故拍平为顶层类,语义与字段保持一致。 + * + * 序列化/反序列化由 simapi_serialization 反射处理(免标注、免约束)。 */ package simapi.authsdk import std.collection.* -import stdx.encoding.json.* -import soulsoft_serialization.* -import soulsoft_serialization.macros.* /** * 应用/Profile 通用项(对齐 C# AppAndProfileItem)。 */ -@Serialization public class AppAndProfileItem { public var _id: String = "" public var _name: String = "" @@ -30,14 +28,13 @@ public class AppAndProfileItem { /** * 安全确认响应(对齐 C# ConfirmResponse)。 - * _data 用 ?JsonValue 对齐 C# Dictionary?(任意 JSON 对象)。 + * _data 用 ?HashMap 对齐 C# Dictionary?(任意 JSON 对象)。 */ -@Serialization public class ConfirmResponse { public var _applicationId: String = "" public var _profileId: String = "" public var _scene: ?String = None - public var _data: ?JsonValue = None + public var _data: ?HashMap = None public init() {} } @@ -45,10 +42,9 @@ public class ConfirmResponse { /** * 登录信息响应(对齐 C# LoginInfoResponse)。 */ -@Serialization public class LoginInfoResponse { public var _scene: ?String = None - public var _data: ?JsonValue = None + public var _data: ?HashMap = None public var _profileId: String = "" public var _name: String = "" public var _image: ?String = None @@ -60,7 +56,6 @@ public class LoginInfoResponse { /** * 获取授权码响应(对齐 C# GetCodeResponse)。 */ -@Serialization public class GetCodeResponse { public var _code: String = "" public var _server: String = "" @@ -78,7 +73,6 @@ public class GetCodeResponse { /** * 群组关联项(对齐 C# GroupRelatedItem)。 */ -@Serialization public class GroupRelatedItem { public var _id: String = "" public var _name: String = "" @@ -94,7 +88,6 @@ public class GroupRelatedItem { /** * 群组详情树节点(对齐 C# GroupDetailTreeNode,children 递归)。 */ -@Serialization public class GroupDetailTreeNode { public var _id: String = "" public var _name: String = "" @@ -109,7 +102,6 @@ public class GroupDetailTreeNode { /** * 权限项(对齐 C# PermissionItem)。 */ -@Serialization public class PermissionItem { public var _identifier: String = "" public var _name: String = "" diff --git a/src/communications/SimApiBaseResponse.cj b/src/communications/SimApiBaseResponse.cj index e07ee9c..393e3c9 100644 --- a/src/communications/SimApiBaseResponse.cj +++ b/src/communications/SimApiBaseResponse.cj @@ -6,15 +6,16 @@ package simapi.communications import std.collection.* -import stdx.encoding.json.* -import soulsoft_serialization.* -import soulsoft_serialization.macros.* +import simapi_serialization.* +import simapi_serialization.macros.* /** * 基础响应体:所有接口统一返回该结构。 * HTTP 状态码始终 200,业务错误通过 code 字段表达。 + * 序列化/反序列化由 simapi_serialization 反射处理(无需接口/宏); + * @SerializerParent 使子类(SimApiResponse/SimApiDataResponse)序列化时包含 _code/_message。 */ -@Serialization +@SerializerParent public open class SimApiBaseResponse { public var _code: Int64 = 200 public var _message: String = "成功" @@ -68,7 +69,7 @@ public open class SimApiBaseResponse { /** * 分页内容返回。 * @param T 列表元素类型。 - * 说明:泛型类暂不通过 @Serialization 宏序列化,可手动转换为 SimApiResponse。 + * 说明:泛型类暂不通过反射序列化,可手动转换为 SimApiResponse。 */ public class PageResponse { public var _list: Array = Array() @@ -89,11 +90,12 @@ public class PageResponse { /** * 带数据的动态响应。 * @param T 数据类型。 - * 说明:泛型类手写实现 ISerialization>(serialize + deserialize), - * data 内嵌为对象(对齐 C# SimApiBaseResponse.Data 是 T? 而非字符串)。 - * 序列化支持动态结构(HashMap 等经 SimApiJson 内嵌);反序列化要求 T <: ISerialization。 + * 序列化/反序列化由 simapi_serialization 反射处理: + * - data 为对象/数组/标量,反射递归 + * - data 支持任意 T(免约束) + * 注:泛型嵌套(SimApiResponse)的反射反序列化已验证可用。 */ -public class SimApiResponse <: SimApiBaseResponse & ISerialization> where T <: ISerialization { +public class SimApiResponse <: SimApiBaseResponse { public var _data: ?T = None public init() { @@ -113,48 +115,4 @@ public class SimApiResponse <: SimApiBaseResponse & ISerialization 等):经 SimApiJson 序列化后解析内嵌为对象 - let json = SimApiJson.json(Some(data)) - dms.add(Field("data", DataModel.fromJson(JsonValue.fromStr(json)))) - } - } - dms - } - - /** - * 从 DataModel 反序列化:code + message + data(对齐 .NET JsonSerializer.Deserialize)。 - */ - public static func deserializeObject(dm: DataModel, options: JsonSerializerOptions): SimApiResponse { - let resp = SimApiResponse() - if (let dms: DataModelStruct <- dm) { - for (field in dms.getFields()) { - match (field.getName()) { - case "code" => - if (let v: DataModelInt <- field.getData()) { - resp._code = v.getValue() - } - case "message" => - if (let v: DataModelString <- field.getData()) { - resp._message = v.getValue() - } - case "data" => - resp._data = Some(T.deserializeObject(field.getData(), options)) - case _ => () - } - } - } - resp - } } diff --git a/src/communications/SimApiDataResponse.cj b/src/communications/SimApiDataResponse.cj index 67acc85..3f1f7b2 100644 --- a/src/communications/SimApiDataResponse.cj +++ b/src/communications/SimApiDataResponse.cj @@ -5,23 +5,17 @@ package simapi.communications -import std.collection.* -import stdx.encoding.json.* -import soulsoft_serialization.* - /** * 带任意对象数据的响应(非泛型版,供响应自动封装使用)。 * * 与 SimApiResponse 的区别:data 为 Any(运行时类型不定),用于 * SimApiRequestDelegateFactory 派发结果时统一包装 DTO/数组/动态结构。 * - * 序列化规则(对齐 SimApiResponse): - * - data 实现了 ISerializable(@Serialization DTO、Array 等)→ data 内嵌为对象 - * - data 为动态结构(如 HashMap,不满足泛型 ISerialization 约束) - * → 经 SimApiJson 序列化后解析内嵌为对象(而非字符串) - * 因此 data 在 JSON 中始终是对象/数组/标量,不会是"JSON 字符串"。 + * 序列化/反序列化由 simapi_serialization 反射处理: + * - data 为对象/数组/标量,反射递归 + * - data 为动态结构(HashMap)同样反射支持 */ -public class SimApiDataResponse <: SimApiBaseResponse & ISerialization { +public class SimApiDataResponse <: SimApiBaseResponse { public var _data: ?Any = None public init() { @@ -41,48 +35,4 @@ public class SimApiDataResponse <: SimApiBaseResponse & ISerialization 等):经 SimApiJson 序列化后解析内嵌为对象 - let json = SimApiJson.json(Some(data)) - dms.add(Field("data", DataModel.fromJson(JsonValue.fromStr(json)))) - } - } - dms - } - - /** - * 从 DataModel 反序列化:code + message + data(data 保留原始 DataModel)。 - */ - public static func deserializeObject(dm: DataModel, options: JsonSerializerOptions): SimApiDataResponse { - let resp = SimApiDataResponse() - if (let dms: DataModelStruct <- dm) { - for (field in dms.getFields()) { - match (field.getName()) { - case "code" => - if (let v: DataModelInt <- field.getData()) { - resp._code = v.getValue() - } - case "message" => - if (let v: DataModelString <- field.getData()) { - resp._message = v.getValue() - } - case "data" => - resp._data = Some(field.getData()) - case _ => () - } - } - } - resp - } } diff --git a/src/communications/SimApiJson.cj b/src/communications/SimApiJson.cj index c649dd9..6ac4d7a 100644 --- a/src/communications/SimApiJson.cj +++ b/src/communications/SimApiJson.cj @@ -7,25 +7,24 @@ package simapi.communications import std.collection.* +import simapi_serialization.* /** * JSON 序列化静态工具类。 * - * 说明:序列化核心放在依赖图最底层的 simapi.communications 包, - * SimApiUtil.json(simapi.helpers)委托本类实现,避免循环依赖; - * 全框架 JSON 输出统一走此处,保证转义与格式一致。 + * 说明:序列化核心委托给 simapi_serialization(JsonSerializer.Serialize), + * 全框架 JSON 输出统一走此处,保证与序列化库行为一致。 */ public class SimApiJson { private init() {} /** * 对象序列化为 JSON 字符串(统一入口)。 - * 支持 String/Int64/Bool/Float64/Array/HashMap,其他类型退化为字符串。 * @param obj 任意对象(None 输出 null)。 */ public static func json(obj: ?Any): String { if (let Some(obj) <- obj) { - return jsonValue(obj) + return JsonSerializer.Serialize(obj) } "null" } @@ -49,52 +48,4 @@ public class SimApiJson { } sb.toString() } - - private static func jsonValue(obj: Any): String { - if (let s: String <- obj) { - return "\"${escapeJson(s)}\"" - } - if (let i: Int64 <- obj) { - return "${i}" - } - if (let b: Bool <- obj) { - return "${b}" - } - if (let f: Float64 <- obj) { - return "${f}" - } - if (let arr: Array <- obj) { - var sb = StringBuilder() - sb.append("[") - var first = true - for (item in arr) { - if (!first) { sb.append(",") } - sb.append(jsonValue(item)) - first = false - } - sb.append("]") - return sb.toString() - } - if (let map: HashMap <- obj) { - var sb = StringBuilder() - sb.append("{") - var first = true - for ((key, value) in map) { - if (!first) { sb.append(",") } - sb.append("\"${escapeJson(key)}\":${jsonValue(value)}") - first = false - } - sb.append("}") - return sb.toString() - } - // 其他类型退化为字符串 - return "\"${escapeJson(describe(obj))}\"" - } - - private static func describe(obj: Any): String { - if (let s: ToString <- obj) { - return s.toString() - } - "null" - } } diff --git a/src/communications/SimApiLoginItem.cj b/src/communications/SimApiLoginItem.cj index 2bd1fed..873a263 100644 --- a/src/communications/SimApiLoginItem.cj +++ b/src/communications/SimApiLoginItem.cj @@ -7,18 +7,15 @@ package simapi.communications import std.collection.* -import soulsoft_serialization.* /** * 登录信息项:Token 认证通过后注入请求上下文。 - * 对齐 C# SimApiLoginItem(Id / Type / Meta / Extra,camelCase 输出 id/type/meta/extra)。 + * 对齐 C# SimApiLoginItem(Id / Type / Meta / Extra)。 * - * 说明:因 _extra 为 HashMap(Any 不满足 soulsoft 的 ISerialization 约束, - * 无法用 @Serialization 宏),故手动实现 ISerialization(serialize + deserialize); - * 调用方统一通过 JsonSerializer.serializeObject() / deserializeObject() 使用 - * (对齐 .NET JsonSerializer.Serialize / Deserialize)。 + * 序列化/反序列化由 simapi_serialization 反射处理(免标注、免约束), + * 字段 _id/_types/_meta/_extra 全部为受支持类型(String/Array/HashMap)。 */ -public class SimApiLoginItem <: ISerialization { +public class SimApiLoginItem { public var _id: String = "" public var _types: Array = ["user"] public var _meta: HashMap = HashMap() @@ -39,129 +36,4 @@ public class SimApiLoginItem <: ISerialization { this._meta = HashMap() this._extra = HashMap() } - - /** - * 序列化为 DataModel:{"id","type","meta","extra"}。 - */ - public func serializeObject(options: JsonSerializerOptions): DataModel { - let dms = DataModelStruct() - dms.add(Field("id", DataModelString(_id))) - // type: string[] - let typesSeq = DataModelSeq() - for (t in _types) { - typesSeq.add(DataModelString(t)) - } - dms.add(Field("type", typesSeq)) - // meta: object - let metaStruct = DataModelStruct() - for ((k, v) in _meta) { - metaStruct.add(Field(k, DataModelString(v))) - } - dms.add(Field("meta", metaStruct)) - // extra: object - let extraStruct = DataModelStruct() - for ((k, v) in _extra) { - extraStruct.add(Field(k, anyToDataModel(v))) - } - dms.add(Field("extra", extraStruct)) - dms - } - - /** - * 从 DataModel 反序列化:{"id","type","meta","extra"}。 - */ - public static func deserializeObject(dm: DataModel, options: JsonSerializerOptions): SimApiLoginItem { - let item = SimApiLoginItem() - if (let dms: DataModelStruct <- dm) { - for (field in dms.getFields()) { - match (field.getName()) { - case "id" => item._id = field.getData() |> dataModelToString - case "type" => - let list = ArrayList() - if (let seq: DataModelSeq <- field.getData()) { - for (sub in seq.getItems()) { - list.add(dataModelToString(sub)) - } - } - item._types = list.toArray() - case "meta" => - if (let metaStruct: DataModelStruct <- field.getData()) { - for (sub in metaStruct.getFields()) { - item._meta[sub.getName()] = dataModelToString(sub.getData()) - } - } - case "extra" => - if (let extraStruct: DataModelStruct <- field.getData()) { - for (sub in extraStruct.getFields()) { - item._extra[sub.getName()] = dataModelToAny(sub.getData()) - } - } - case _ => () - } - } - } - item - } - - private static func dataModelToString(dm: DataModel): String { - if (let s: DataModelString <- dm) { - return s.getValue() - } - if (let i: DataModelInt <- dm) { - return "${i.getValue()}" - } - if (let b: DataModelBool <- dm) { - return "${b.getValue()}" - } - "" - } - - private static func dataModelToAny(dm: DataModel): Any { - if (let s: DataModelString <- dm) { - return s.getValue() - } - if (let i: DataModelInt <- dm) { - return i.getValue() - } - if (let b: DataModelBool <- dm) { - return b.getValue() - } - if (let f: DataModelFloat <- dm) { - return f.getValue() - } - if (let dms: DataModelStruct <- dm) { - var map = HashMap() - for (field in dms.getFields()) { - map[field.getName()] = dataModelToAny(field.getData()) - } - return map - } - if (let seq: DataModelSeq <- dm) { - let list = ArrayList() - for (item in seq.getItems()) { - list.add(dataModelToAny(item)) - } - return list.toArray() - } - "" - } - - private static func anyToDataModel(v: Any): DataModel { - if (let s: String <- v) { - return DataModelString(s) - } - if (let i: Int64 <- v) { - return DataModelInt(i) - } - if (let b: Bool <- v) { - return DataModelBool(b) - } - if (let f: Float64 <- v) { - return DataModelFloat(f) - } - if (let s: ToString <- v) { - return DataModelString(s.toString()) - } - DataModelNull() - } } diff --git a/src/helpers/SimApiAesBodyChecker.cj b/src/helpers/SimApiAesBodyChecker.cj index c1568d0..9508a7f 100644 --- a/src/helpers/SimApiAesBodyChecker.cj +++ b/src/helpers/SimApiAesBodyChecker.cj @@ -7,8 +7,7 @@ package simapi.helpers import std.collection.* import std.io.* -import soulsoft_serialization.* -import soulsoft_serialization.macros.* +import simapi_serialization.* import soulsoft_web_http.* import simapi.communications.* import simapi.exceptions.* @@ -16,7 +15,6 @@ import simapi.exceptions.* /** * AES body 请求({"data": "密文"},对齐 C# SimApiOneFieldRequest)。 */ -@Serialization public class AesBodyRequest { public var _data: String = "" @@ -48,7 +46,7 @@ public open class AesBodyProviderBase { * * 仓颉无 ModelBinder 机制,按项目惯例由控制器在方法开头调用: * let jsonStr = SimApiAesBodyChecker.decryptBody(context, provider) - * let request = JsonSerializer.deserializeObject(jsonStr) + * let request = JsonSerializer.Deserialize(jsonStr) * * 流程(与 C# 一致): * 1. 读取 body 并反序列化为 {"data": "密文"} @@ -75,7 +73,7 @@ public class SimApiAesBodyChecker { } // 2. 反序列化 {"data": "密文"} - let req = JsonSerializer.deserializeObject(body) + let req = JsonSerializer.Deserialize(body) if (req._data.isEmpty()) { SimApiError.error(code: 400, message: "请求体缺少密文Data字段") } diff --git a/src/helpers/SimApiAuth.cj b/src/helpers/SimApiAuth.cj index ac14666..dc0aee7 100644 --- a/src/helpers/SimApiAuth.cj +++ b/src/helpers/SimApiAuth.cj @@ -9,8 +9,7 @@ import std.collection.* import std.collection.concurrent.* import std.convert.* import std.time.* -import stdx.encoding.json.* -import soulsoft_serialization.* +import simapi_serialization.* import redis.client.* import simapi.communications.* import simapi.configurations.* @@ -287,11 +286,11 @@ public class SimApiAuth { private static func loginItemJson(item: SimApiLoginItem): String { // 统一 JSON 序列化:对齐 .NET JsonSerializer.Serialize(item) - JsonSerializer.serializeObject(item) + JsonSerializer.Serialize(item) } private static func parseLoginItem(json: String): SimApiLoginItem { // 统一 JSON 反序列化:对齐 .NET JsonSerializer.Deserialize(json) - JsonSerializer.deserializeObject(json) + JsonSerializer.Deserialize(json) } } diff --git a/src/helpers/SimApiCache.cj b/src/helpers/SimApiCache.cj index 8ab915c..6790cdc 100644 --- a/src/helpers/SimApiCache.cj +++ b/src/helpers/SimApiCache.cj @@ -10,7 +10,7 @@ import std.collection.concurrent.* import std.convert.* import std.time.* import redis.client.* -import soulsoft_serialization.* +import simapi_serialization.* import simapi.configurations.* import simapi.exceptions.* @@ -100,11 +100,11 @@ public class SimApiCache { } /** - * 获取特定类型缓存(对齐 C# Get:从 JSON 反序列化)。 + * 获取特定类型缓存(对齐 C# Get:从 JSON 反序列化,使用 simapi_serialization)。 */ - public func get(key: String): ?T where T <: ISerialization { + public func get(key: String): ?T { match (getString(key)) { - case Some(json) => Some(JsonSerializer.deserializeObject(json)) + case Some(json) => Some(JsonSerializer.Deserialize(json)) case None => None } } diff --git a/src/helpers/SimApiHttpClient.cj b/src/helpers/SimApiHttpClient.cj index 8876fdf..5126a25 100644 --- a/src/helpers/SimApiHttpClient.cj +++ b/src/helpers/SimApiHttpClient.cj @@ -11,7 +11,7 @@ import stdx.net.tls.* import stdx.net.tls.common.* import soulsoft_net_http.{HttpClient, HttpRequestMessage, JsonContent} import soulsoft_net_http.{HttpMethod as NetHttpMethod} -import soulsoft_serialization.* +import simapi_serialization.* import simapi.communications.* import simapi.configurations.* import simapi.exceptions.* @@ -21,7 +21,7 @@ import simapi.exceptions.* * 对齐 C# 的 SimApi.Helpers.SimApiHttpClient: * - 内部使用 soulsoft_net_http 的 HttpClient(等价 .NET 的 System.Net.Http.HttpClient) * - 返回泛型 T(反序列化响应 body 的 data 字段),不再返回 String - * @param T 响应 data 的数据类型(需实现 ISerialization,如 SimApiLoginItem、String、Int64 等)。 + * @param T 响应 data 的数据类型(任意类,simapi_serialization 反射反序列化)。 */ public open class SimApiHttpClient { public var server: String @@ -48,7 +48,7 @@ public open class SimApiHttpClient { * @param queries 额外查询参数(可选)。 * @return 响应 data 字段反序列化后的 T。 */ - public func signQuery(url: String, body!: String = "", queries!: HashMap = HashMap()): T where T <: ISerialization { + public func signQuery(url: String, body!: String = "", queries!: HashMap = HashMap()): T { var queryUrl = StringBuilder() for (field in signFields) { queryUrl.append("${field}=") @@ -80,7 +80,7 @@ public open class SimApiHttpClient { * @param body 请求体 JSON 字符串。 * @return 响应 data 字段反序列化后的 T。 */ - public func aesQuery(url: String, body: String): T where T <: ISerialization { + public func aesQuery(url: String, body: String): T { var target = "${server}${url}" if (let Some(name) <- appIdName) { target = "${target}?${name}=${appId}" @@ -98,19 +98,20 @@ public open class SimApiHttpClient { * @param queries 额外查询参数(可选)。 * @return 响应 data 字段反序列化后的 T。 */ - public func aesSignQuery(url: String, body: String, queries!: HashMap = HashMap()): T where T <: ISerialization { + public func aesSignQuery(url: String, body: String, queries!: HashMap = HashMap()): T { let encrypted = aesEncrypt(body) let req = "{\"data\":\"${encrypted}\"}" return signQuery(url, body: req, queries: queries) } /** - * 发起 POST 请求并反序列化 SimApiBaseResponse,返回 data 字段。 + * 发起 POST 请求并反序列化 SimApiResponse,返回 data 字段。 * 对齐 C# Query: - * ErrorWhenFalse(IsSuccessStatusCode) → ReadFromJsonAsync> → ErrorWhen(Code != 200) → return Data。 + * ErrorWhenFalse(IsSuccessStatusCode) → ReadFromJsonAsync> → ErrorWhen(Code != 200) → return Data。 * 注意:必须 noProxy(),否则会走系统代理(192.168.0.250:8118)导致连接被拒。 + * 反序列化使用 simapi_serialization(Deserialize 免约束)。 */ - private func query(url: String, body: String): T where T <: ISerialization { + private func query(url: String, body: String): T { let client = HttpClient.create { builder => builder.noProxy() // 支持 https:配置 TLS(信任所有证书 + SNI 域名) @@ -128,7 +129,8 @@ public open class SimApiHttpClient { let response = client.send(request) try { SimApiError.errorWhenFalse(response.isSuccessStatusCode, code: response.statusCode, message: "HTTP ERROR: ${response.statusCode}") - let result = response.content.readFromJson>() + let json = response.content.readAsString() + let result = JsonSerializer.Deserialize>(json) SimApiError.errorWhen(result._code != 200, code: result._code, message: result._message) return result._data.getOrThrow() } finally { diff --git a/src/helpers/SimApiResultWriter.cj b/src/helpers/SimApiResultWriter.cj index a9cdf45..b627131 100644 --- a/src/helpers/SimApiResultWriter.cj +++ b/src/helpers/SimApiResultWriter.cj @@ -7,6 +7,7 @@ package simapi.helpers import soulsoft_web_http.* import soulsoft_web_mvc.core.* +import simapi_serialization.* import simapi.communications.* /** @@ -16,6 +17,8 @@ import simapi.communications.* * - String → SimApiResponse(data 为字符串) * - Unit(void)→ SimApiBaseResponse()({code:200, message:成功}) * - 其他对象(DTO/数组/动态结构)→ SimApiDataResponse(data 内嵌为对象) + * + * 输出使用 simapi_serialization 序列化后直接写响应体(不走 soulsoft formatter)。 */ public class SimApiResultWriter { private init() {} @@ -26,17 +29,22 @@ public class SimApiResultWriter { result.invoke(context) } else if (let result: SimApiBaseResponse <- actionResult) { // 已是 SimApiBaseResponse(含子类)→ 原样输出 - ObjectResult(result).invoke(context) + writeJson(context, result) } else if (let result: String <- actionResult) { // String → SimApiResponse(data 为字符串) - ObjectResult(SimApiResponse(result)).invoke(context) + writeJson(context, SimApiResponse(result)) } else if (let result: Unit <- actionResult) { // void/无返回 → SimApiBaseResponse()({code:200, message:成功}) - context.response.writeAsJson(SimApiBaseResponse()) + writeJson(context, SimApiBaseResponse()) } else { // 其他对象(DTO/数组/动态结构)→ SimApiDataResponse - // data 由 SimApiDataResponse.serializeObject 内嵌为对象 - ObjectResult(SimApiDataResponse(actionResult)).invoke(context) + writeJson(context, SimApiDataResponse(actionResult)) } } + + /// simapi_serialization 序列化后直接写响应体 + private static func writeJson(context: HttpContext, obj: Any): Unit { + context.response.contentType = "application/json; charset=utf-8" + context.response.write(JsonSerializer.Serialize(obj)) + } } diff --git a/src/helpers/SimApiUtil.cj b/src/helpers/SimApiUtil.cj index 63aece0..db1b234 100644 --- a/src/helpers/SimApiUtil.cj +++ b/src/helpers/SimApiUtil.cj @@ -11,9 +11,8 @@ import std.random.* import stdx.crypto.digest.* import stdx.encoding.hex.* import stdx.encoding.base64.* -import stdx.encoding.json.* import std.regex.* -import soulsoft_serialization.* +import simapi_serialization.* import simapi.communications.* import simapi.macros.* @@ -181,12 +180,12 @@ public class SimApiUtil { /** * 从 JSON 字符串反序列化为 T(对齐 C# SimApiUtil.FromJson)。 - * @param T 目标类型(需实现 ISerialization,如 @Serialization DTO、基础类型等)。 + * @param T 目标类型(任意类,无需接口/宏约束)。 * @param jsonString JSON 字符串。 * @return 反序列化结果。 */ - public static func fromJson(jsonString: String): T where T <: ISerialization { - JsonSerializer.deserializeObject(jsonString) + public static func fromJson(jsonString: String): T { + JsonSerializer.Deserialize(jsonString) } /** @@ -195,21 +194,17 @@ public class SimApiUtil { * @return Base64 字符串。 */ public static func base64Encode(obj: Any): String { - let json = if (let ser: ISerializable <- obj) { - ser.serializeObject().toJson().toString() - } else { - SimApiJson.json(Some(obj)) - } + let json = SimApiJson.json(Some(obj)) base64Encode(json) } /** * Base64 → JSON → T 反序列化(对齐 C# Base64Decode)。 - * @param T 目标类型(需实现 ISerialization)。 + * @param T 目标类型(任意类,无需接口/宏约束)。 * @param base64Str Base64 字符串。 * @return 反序列化结果。 */ - public static func base64DecodeTo(base64Str: String): T where T <: ISerialization { + public static func base64DecodeTo(base64Str: String): T { fromJson(base64Decode(base64Str)) } diff --git a/src/middlewares/SimApiExceptionMiddleware.cj b/src/middlewares/SimApiExceptionMiddleware.cj index faf8bc0..4c2bd06 100644 --- a/src/middlewares/SimApiExceptionMiddleware.cj +++ b/src/middlewares/SimApiExceptionMiddleware.cj @@ -7,6 +7,7 @@ package simapi.middlewares import soulsoft_web_http.* import soulsoft_extensions_logging.* +import simapi_serialization.* import simapi.communications.* import simapi.exceptions.* import simapi.configurations.* @@ -60,7 +61,7 @@ public class SimApiExceptionMiddleware <: IMiddleware { } var response = SimApiBaseResponse(simEx.code, message) if (context.response.statusCode == 404) { - response.message = "接口不存在" + response._message = "接口不存在" } return response } @@ -69,6 +70,6 @@ public class SimApiExceptionMiddleware <: IMiddleware { } private func responseJson(response: SimApiBaseResponse): String { - response.toJsonString() + JsonSerializer.Serialize(response) } } diff --git a/src/middlewares/SimApiRequestLogMiddleware.cj b/src/middlewares/SimApiRequestLogMiddleware.cj index 2feb55f..4c4b876 100644 --- a/src/middlewares/SimApiRequestLogMiddleware.cj +++ b/src/middlewares/SimApiRequestLogMiddleware.cj @@ -8,9 +8,9 @@ package simapi.middlewares import std.collection.* import std.io.* import std.time.* -import stdx.encoding.json.* import soulsoft_web_http.* import soulsoft_extensions_logging.* +import simapi_serialization.* import simapi.communications.* import simapi.configurations.* @@ -139,26 +139,27 @@ public class SimApiRequestLogMiddleware <: IMiddleware { return body + "\n" } try { - let jv = JsonValue.fromStr(body) - match (jv.kind()) { - case JsObject => - let obj = jv.asObject() - let newObj = JsonObject() - for ((k, v) in obj.getFields()) { - match (v.kind()) { - case JsString => - let str = v.asString().getValue() - if (str.size > maxLen) { - newObj.put(k, JsonString(str[0..maxLen] + "...(${str.size})")) - } else { - newObj.put(k, v) - } - case _ => newObj.put(k, v) - } + // 用 simapi_serialization 解析为动态结构,仅截断超长字符串字段(保持结构) + let map = JsonSerializer.Deserialize>(body) + var sb = StringBuilder() + sb.append("{") + var first = true + for ((k, v) in map) { + if (!first) { sb.append(",") } + sb.append("\"${SimApiJson.escapeJson(k)}\":") + if (let s: String <- v) { + if (s.size > maxLen) { + sb.append("\"${SimApiJson.escapeJson(s[0..maxLen])}...(${s.size})\"") + } else { + sb.append("\"${SimApiJson.escapeJson(s)}\"") } - return newObj.toString() + "\n" - case _ => () + } else { + sb.append(SimApiJson.json(Some(v))) + } + first = false } + sb.append("}") + return sb.toString() + "\n" } catch (_: Exception) { } // 非 JSON 或解析失败:整串按长度截断