import { HttpManager, HttpMethod } from "../transport/HttpManager"; import { JsonCodec } from "../codec/JsonCodec"; import { ProtoCodec } from "../codec/ProtoCodec"; import type { Endpoint } from "./endpoints"; import { ApiCode, type ApiResponse } from "./types"; import { DataManager, DataId } from "db://assets/Scripts/chat18x/data/DataManager"; import { LoginData } from "db://assets/Scripts/chat18x/data/LoginData"; import { AppConfig } from "db://assets/Scripts/chat18x/config/env/appConfig"; export class ApiClient { private static _I: ApiClient | null = null; public static get I(): ApiClient { if (!ApiClient._I) ApiClient._I = new ApiClient(); return ApiClient._I; } private constructor(private http = HttpManager.I) {} private get baseUrl() { return AppConfig.I.getHttpBase() } private get token() { return DataManager.I.getDataById(DataId.Login).getToken();} public async call(ep: Endpoint, req: Req): Promise> { const url = `${this.baseUrl}/${ep.path.replace(/^\/+/, "")}`; const headers: Record = {}; let responseType: XMLHttpRequestResponseType | undefined; let rawBody: XMLHttpRequestBodyInit | null | undefined; let jsonData: any; if (ep.needsAuth && this.token) { headers["Authorization"] = `Bearer ${this.token}`; } // 编码 if (ep.codec === "json") { jsonData = (ep.method === "GET") ? req : JsonCodec.encode(req); if (ep.method !== "GET") headers["Content-Type"] = "application/json"; } else if (ep.codec === "proto") { if (!ep.reqType || !ep.resType) { return { code: ApiCode.UNKNOWN, msg: "proto types missing" }; } rawBody = ProtoCodec.encode(ep.reqType, req); headers["Content-Type"] = "application/x-protobuf"; headers["Accept"] = "application/x-protobuf"; responseType = "arraybuffer"; } // 发送 const r = await this.http.request(url, jsonData, ep.method, { headers, responseType, rawBody }); // HTTP/网络错误 → 统一返回 if (!r.ok) { if (r.status === 401) { return { code: ApiCode.AUTH_EXPIRED, msg: "auth expired" }; } if (r.error === "timeout") { return { code: ApiCode.TIMEOUT, msg: "request timeout" }; } if (r.error === "network") { return { code: ApiCode.NETWORK_ERROR, msg: "network error" }; } return { code: ApiCode.HTTP_ERROR, msg: `http error: ${r.status}` }; } // 解码 + 统一化 try { if (ep.codec === "json") { const parsed = JsonCodec.decode(r.body); // 如果后端本来就返回 {code,msg,data},直接透传(并确保有默认 msg) if (parsed && typeof parsed === "object" && typeof parsed.code === "number") { return { code: parsed.code, msg: parsed.msg ?? (parsed.code === 1 ? "ok" : "error"), data: parsed.data as Res }; } // 否则我们包一层 return { code: ApiCode.OK, msg: "ok", data: parsed as Res }; } else if (ep.codec === "proto") { const data = ProtoCodec.decode(ep.resType!, r.body); // proto 场景一般没有 {code,msg} 包装,我们统一包一层 return { code: ApiCode.OK, msg: "ok", data }; } } catch (e) { return { code: ApiCode.DECODE_ERROR, msg: "decode error" }; } } }