refactor: 全框架 JSON 操作迁移到 simapi_serialization

- 依赖:新增 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<HashMap<String,Any>> + 重序列化
- SimApiAuthDto JsonValue 字段 → HashMap<String,Any>;SimApiCache.get<T> 去约束
- webdemo:AuthController readFromJson 换 Deserialize;cjpm.toml 补依赖
- 验证:webdemo 启动正常,GET/POST 接口响应格式统一,S3 存储全链路 OK
This commit is contained in:
2026-08-18 00:07:00 +08:00
parent 8b0a8f783c
commit 73f60f6f15
17 changed files with 108 additions and 380 deletions
+3 -5
View File
@@ -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<string>)。
*/
@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<XxxRequest>(jsonStr)
* let request = JsonSerializer.Deserialize<XxxRequest>(jsonStr)
*
* 流程(与 C# 一致):
* 1. 读取 body 并反序列化为 {"data": "密文"}
@@ -75,7 +73,7 @@ public class SimApiAesBodyChecker {
}
// 2. 反序列化 {"data": "密文"}
let req = JsonSerializer.deserializeObject<AesBodyRequest>(body)
let req = JsonSerializer.Deserialize<AesBodyRequest>(body)
if (req._data.isEmpty()) {
SimApiError.error(code: 400, message: "请求体缺少密文Data字段")
}
+3 -4
View File
@@ -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<SimApiLoginItem>(item)
JsonSerializer.Serialize(item)
}
private static func parseLoginItem(json: String): SimApiLoginItem {
// 统一 JSON 反序列化:对齐 .NET JsonSerializer.Deserialize<SimApiLoginItem>(json)
JsonSerializer.deserializeObject<SimApiLoginItem>(json)
JsonSerializer.Deserialize<SimApiLoginItem>(json)
}
}
+4 -4
View File
@@ -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<T>:从 JSON 反序列化)。
* 获取特定类型缓存(对齐 C# Get<T>:从 JSON 反序列化,使用 simapi_serialization)。
*/
public func get<T>(key: String): ?T where T <: ISerialization<T> {
public func get<T>(key: String): ?T {
match (getString(key)) {
case Some(json) => Some(JsonSerializer.deserializeObject<T>(json))
case Some(json) => Some(JsonSerializer.Deserialize<T>(json))
case None => None
}
}
+11 -9
View File
@@ -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<T>,如 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<T>(url: String, body!: String = "", queries!: HashMap<String, String> = HashMap<String, String>()): T where T <: ISerialization<T> {
public func signQuery<T>(url: String, body!: String = "", queries!: HashMap<String, String> = HashMap<String, String>()): 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<T>(url: String, body: String): T where T <: ISerialization<T> {
public func aesQuery<T>(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<T>(url: String, body: String, queries!: HashMap<String, String> = HashMap<String, String>()): T where T <: ISerialization<T> {
public func aesSignQuery<T>(url: String, body: String, queries!: HashMap<String, String> = HashMap<String, String>()): T {
let encrypted = aesEncrypt(body)
let req = "{\"data\":\"${encrypted}\"}"
return signQuery<T>(url, body: req, queries: queries)
}
/**
* 发起 POST 请求并反序列化 SimApiBaseResponse<T>,返回 data 字段。
* 发起 POST 请求并反序列化 SimApiResponse<T>,返回 data 字段。
* 对齐 C# Query<T>
* ErrorWhenFalse(IsSuccessStatusCode) → ReadFromJsonAsync<SimApiBaseResponse<T>> → ErrorWhen(Code != 200) → return Data。
* ErrorWhenFalse(IsSuccessStatusCode) → ReadFromJsonAsync<SimApiResponse<T>> → ErrorWhen(Code != 200) → return Data。
* 注意:必须 noProxy(),否则会走系统代理(192.168.0.250:8118)导致连接被拒。
* 反序列化使用 simapi_serializationDeserialize<T> 免约束)。
*/
private func query<T>(url: String, body: String): T where T <: ISerialization<T> {
private func query<T>(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<SimApiResponse<T>>()
let json = response.content.readAsString()
let result = JsonSerializer.Deserialize<SimApiResponse<T>>(json)
SimApiError.errorWhen(result._code != 200, code: result._code, message: result._message)
return result._data.getOrThrow()
} finally {
+13 -5
View File
@@ -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<String>data 为字符串)
* - Unitvoid)→ SimApiBaseResponse(){code:200, message:成功}
* - 其他对象(DTO/数组/动态结构)→ SimApiDataResponsedata 内嵌为对象)
*
* 输出使用 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<Any>(result).invoke(context)
writeJson(context, result)
} else if (let result: String <- actionResult) {
// String → SimApiResponse<String>data 为字符串)
ObjectResult<Any>(SimApiResponse<String>(result)).invoke(context)
writeJson(context, SimApiResponse<String>(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<Any>(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))
}
}
+7 -12
View File
@@ -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<T>)。
* @param T 目标类型(需实现 ISerialization<T>,如 @Serialization DTO、基础类型等)。
* @param T 目标类型(任意类,无需接口/宏约束)。
* @param jsonString JSON 字符串。
* @return 反序列化结果。
*/
public static func fromJson<T>(jsonString: String): T where T <: ISerialization<T> {
JsonSerializer.deserializeObject<T>(jsonString)
public static func fromJson<T>(jsonString: String): T {
JsonSerializer.Deserialize<T>(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<T>)。
* @param T 目标类型(需实现 ISerialization<T>)。
* @param T 目标类型(任意类,无需接口/宏约束)。
* @param base64Str Base64 字符串。
* @return 反序列化结果。
*/
public static func base64DecodeTo<T>(base64Str: String): T where T <: ISerialization<T> {
public static func base64DecodeTo<T>(base64Str: String): T {
fromJson<T>(base64Decode(base64Str))
}