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:
@@ -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"}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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.*
|
||||
|
||||
@@ -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<SimApiBaseResponse>()
|
||||
let json = response.content.readAsString()
|
||||
let resp = JsonSerializer.Deserialize<SimApiBaseResponse>(json)
|
||||
SimApiError.errorWhen(resp._code != 200, code: 400, message: "签名验证失败")
|
||||
} finally {
|
||||
response.close()
|
||||
|
||||
@@ -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<string,object>?(任意 JSON 对象)。
|
||||
* _data 用 ?HashMap<String, Any> 对齐 C# Dictionary<string,object>?(任意 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<String, Any> = 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<String, Any> = 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 = ""
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化为 DataModel:code + message + data(data 为对象)。
|
||||
*/
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化为 DataModel:code + message + data(data 为对象)。
|
||||
*/
|
||||
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(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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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字段")
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,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_serialization(Deserialize<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 {
|
||||
|
||||
@@ -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 为字符串)
|
||||
* - 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<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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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})"))
|
||||
// 用 simapi_serialization 解析为动态结构,仅截断超长字符串字段(保持结构)
|
||||
let map = JsonSerializer.Deserialize<HashMap<String, Any>>(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 {
|
||||
newObj.put(k, v)
|
||||
sb.append("\"${SimApiJson.escapeJson(s)}\"")
|
||||
}
|
||||
case _ => newObj.put(k, v)
|
||||
} else {
|
||||
sb.append(SimApiJson.json(Some(v)))
|
||||
}
|
||||
first = false
|
||||
}
|
||||
return newObj.toString() + "\n"
|
||||
case _ => ()
|
||||
}
|
||||
sb.append("}")
|
||||
return sb.toString() + "\n"
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
// 非 JSON 或解析失败:整串按长度截断
|
||||
|
||||
Reference in New Issue
Block a user