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
+11 -53
View File
@@ -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<T>/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<T> {
public var _list: Array<T> = Array<T>()
@@ -89,11 +90,12 @@ public class PageResponse<T> {
/**
* 带数据的动态响应。
* @param T 数据类型。
* 说明:泛型类手写实现 ISerialization<SimApiResponse<T>>serialize + deserialize),
* data 内嵌为对象(对齐 C# SimApiBaseResponse<T>.Data 是 T? 而非字符串)。
* 序列化支持动态结构(HashMap<String,Any> 等经 SimApiJson 内嵌);反序列化要求 T <: ISerialization<T>。
* 序列化/反序列化由 simapi_serialization 反射处理:
* - data 为对象/数组/标量,反射递归
* - data 支持任意 T(免约束)
* 注:泛型嵌套(SimApiResponse<T>)的反射反序列化已验证可用。
*/
public class SimApiResponse<T> <: SimApiBaseResponse & ISerialization<SimApiResponse<T>> where T <: ISerialization<T> {
public class SimApiResponse<T> <: SimApiBaseResponse {
public var _data: ?T = None
public init() {
@@ -113,48 +115,4 @@ public class SimApiResponse<T> <: SimApiBaseResponse & ISerialization<SimApiResp
super(code, message)
this._data = Some(data)
}
/**
* 序列化为 DataModelcode + message + datadata 为对象)。
*/
public override func serializeObject(options: JsonSerializerOptions): DataModel {
let dms = DataModelStruct()
dms.add(Field("code", DataModelInt(_code)))
dms.add(Field("message", DataModelString(_message)))
if (let Some(data) <- _data) {
if (let ser: ISerializable <- data) {
dms.add(Field("data", ser.serializeObject(options)))
} else {
// 动态结构(HashMap<String, Any> 等):经 SimApiJson 序列化后解析内嵌为对象
let json = SimApiJson.json(Some(data))
dms.add(Field("data", DataModel.fromJson(JsonValue.fromStr(json))))
}
}
dms
}
/**
* 从 DataModel 反序列化:code + message + data(对齐 .NET JsonSerializer.Deserialize<T>)。
*/
public static func deserializeObject(dm: DataModel, options: JsonSerializerOptions): SimApiResponse<T> {
let resp = SimApiResponse<T>()
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
}
}
+4 -54
View File
@@ -5,23 +5,17 @@
package simapi.communications
import std.collection.*
import stdx.encoding.json.*
import soulsoft_serialization.*
/**
* 带任意对象数据的响应(非泛型版,供响应自动封装使用)。
*
* 与 SimApiResponse<T> 的区别:data 为 Any(运行时类型不定),用于
* SimApiRequestDelegateFactory 派发结果时统一包装 DTO/数组/动态结构。
*
* 序列化规则(对齐 SimApiResponse<T>
* - data 实现了 ISerializable@Serialization DTO、Array<T> 等)→ data 内嵌为对象
* - data 为动态结构(HashMap<String, Any>,不满足泛型 ISerialization 约束)
* → 经 SimApiJson 序列化后解析内嵌为对象(而非字符串)
* 因此 data 在 JSON 中始终是对象/数组/标量,不会是"JSON 字符串"。
* 序列化/反序列化由 simapi_serialization 反射处理
* - data 为对象/数组/标量,反射递归
* - data 为动态结构(HashMap<String, Any>)同样反射支持
*/
public class SimApiDataResponse <: SimApiBaseResponse & ISerialization<SimApiDataResponse> {
public class SimApiDataResponse <: SimApiBaseResponse {
public var _data: ?Any = None
public init() {
@@ -41,48 +35,4 @@ public class SimApiDataResponse <: SimApiBaseResponse & ISerialization<SimApiDat
super(code, message)
this._data = Some(data)
}
/**
* 序列化为 DataModelcode + message + datadata 为对象)。
*/
public override func serializeObject(options: JsonSerializerOptions): DataModel {
let dms = DataModelStruct()
dms.add(Field("code", DataModelInt(_code)))
dms.add(Field("message", DataModelString(_message)))
if (let Some(data) <- _data) {
if (let ser: ISerializable <- data) {
dms.add(Field("data", ser.serializeObject(options)))
} else {
// 动态结构(HashMap<String, Any> 等):经 SimApiJson 序列化后解析内嵌为对象
let json = SimApiJson.json(Some(data))
dms.add(Field("data", DataModel.fromJson(JsonValue.fromStr(json))))
}
}
dms
}
/**
* 从 DataModel 反序列化:code + message + datadata 保留原始 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
}
}
+4 -53
View File
@@ -7,25 +7,24 @@
package simapi.communications
import std.collection.*
import simapi_serialization.*
/**
* JSON 序列化静态工具类。
*
* 说明:序列化核心放在依赖图最底层的 simapi.communications 包
* SimApiUtil.jsonsimapi.helpers)委托本类实现,避免循环依赖;
* 全框架 JSON 输出统一走此处,保证转义与格式一致。
* 说明:序列化核心委托给 simapi_serializationJsonSerializer.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<Any> <- 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<String, Any> <- 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"
}
}
+4 -132
View File
@@ -7,18 +7,15 @@
package simapi.communications
import std.collection.*
import soulsoft_serialization.*
/**
* 登录信息项:Token 认证通过后注入请求上下文。
* 对齐 C# SimApiLoginItemId / Type / Meta / ExtracamelCase 输出 id/type/meta/extra)。
* 对齐 C# SimApiLoginItemId / Type / Meta / Extra)。
*
* 说明:因 _extra 为 HashMap<String, Any>Any 不满足 soulsoft 的 ISerialization<V> 约束,
* 无法用 @Serialization 宏),故手动实现 ISerialization<SimApiLoginItem>serialize + deserialize);
* 调用方统一通过 JsonSerializer.serializeObject<T>() / deserializeObject<T>() 使用
* (对齐 .NET JsonSerializer.Serialize / Deserialize)。
* 序列化/反序列化由 simapi_serialization 反射处理(免标注、免约束
* 字段 _id/_types/_meta/_extra 全部为受支持类型(String/Array/HashMap)。
*/
public class SimApiLoginItem <: ISerialization<SimApiLoginItem> {
public class SimApiLoginItem {
public var _id: String = ""
public var _types: Array<String> = ["user"]
public var _meta: HashMap<String, String> = HashMap<String, String>()
@@ -39,129 +36,4 @@ public class SimApiLoginItem <: ISerialization<SimApiLoginItem> {
this._meta = HashMap<String, String>()
this._extra = HashMap<String, Any>()
}
/**
* 序列化为 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<string,string>
let metaStruct = DataModelStruct()
for ((k, v) in _meta) {
metaStruct.add(Field(k, DataModelString(v)))
}
dms.add(Field("meta", metaStruct))
// extra: object<string,any>
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<String>()
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<String, Any>()
for (field in dms.getFields()) {
map[field.getName()] = dataModelToAny(field.getData())
}
return map
}
if (let seq: DataModelSeq <- dm) {
let list = ArrayList<Any>()
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()
}
}