Skip to content

TypeScript SDK 封装

小程序与后端的通信分为两路:ztmp RPC 网关(批量调用)和 hapi REST API。以下封装基于小程序通信模块整理。

ZtmpClient — ztmp RPC 网关

小程序所有 ztmp 调用都走 POST /api/gateway,body 是 RPC 数组,支持批量调用。

typescript
const ZTMP_BASE = 'https://ztmp.chintiot.com';

class ZtmpClient {
  private token = '';

  setToken(t: string) {
    this.token = t;
  }

  async call(...calls: Array<[string, ...any[]]>): Promise<any[]> {
    const headers: Record<string, string> = { 'Content-Type': 'application/json' };
    if (this.token) headers['token'] = this.token;

    const res = await fetch(`${ZTMP_BASE}/api/gateway`, {
      method: 'POST',
      headers,
      credentials: 'include',
      body: JSON.stringify(calls),
    });
    if (!res.ok) throw new Error(await res.text());
    return res.json();
  }

  async callOne(method: string, ...args: any[]): Promise<any> {
    return (await this.call([method, ...args]))[0];
  }
}

批量调用示例

typescript
const [loginResult, memberList] = await ztmp.call(
  ['account.login', { type: 'wxmp_phonenumber', code: 'xxx', wx_login_code: 'yyy' }],
  ['ztmp.project.member_list', { currentPage: 1, pageSize: 10 }]
);

小程序原始实现

javascript
// 小程序通信模块
exports.execute = async function(...t) {
  const url = 'https://ztmp.chintiot.com/api/gateway';
  if (e._.isArray(t[0])) {
    return (await request(url, t)).data;      // 批量: [[method, ...args], ...]
  }
  return (await request(url, [t])).data[0];   // 单个: [method, ...args] → 包装成数组
};

HapiClient — hapi REST API

hapi 的接口是标准 REST,每个接口有独立路径。

typescript
const HAPI_BASE = 'https://hotel.chintiot.com';

class HapiClient {
  constructor(private token: string = '') {}

  async post(path: string, body: any): Promise<any> {
    const headers: Record<string, string> = { 'Content-Type': 'application/json' };
    if (this.token) headers['Authorization'] = `Bearer ${this.token}`;

    const res = await fetch(`${HAPI_BASE}${path}`, {
      method: 'POST',
      headers,
      body: JSON.stringify(body),
    });
    return res.json();
  }

  async get(path: string): Promise<any> {
    const headers: Record<string, string> = {};
    if (this.token) headers['Authorization'] = `Bearer ${this.token}`;

    const res = await fetch(`${HAPI_BASE}${path}`, { headers });
    return res.json();
  }
}

调用示例

typescript
await hapi.post('/client/room/control', {
  ci: { room_id: 123 },
  action: 'turn_on',
  mac: '001122334455',
  payload: { index: 0 }
});

const status = await hapi.post('/client/room/status', {
  ci: { room_id: 123 }
});

导出

typescript
export const ztmp = new ZtmpClient();
export const hapi = new HapiClient();

错误处理

ztmp 错误

ztmp 区分业务错误和服务器错误,通过 HTTP 状态码区分:

业务错误(HTTP 4xx)——不应重试,应引导用户:

json
HTTP 401
{ "code": "USER_AUTH_FAILED", "message": "请登录" }

服务器错误(HTTP 500)——可重试:

json
HTTP 500
{ "code": "SERVER_ERROR", "message": "..." }

小程序通信模块会自动解析响应体,Error.message 为中文错误描述,Error.code 为错误键名,Error.statusCode 为 HTTP 状态码:

javascript
try {
  await execute('account.login', { type: 'wxmp_login', code: 'xxx' });
} catch (e) {
  console.log(e.code);        // "USER_NOT_FOUND"
  console.log(e.message);     // "找不到会员"
  console.log(e.statusCode);  // 404
}

完整错误码列表参见 错误码

hapi 错误

hapi 所有响应均返回 HTTP 200,通过 code 字段区分成功/失败:

json
{ "code": 0, "message": "成功", "data": ... }       // 成功
{ "code": 21001, "message": "手机号码或密码错误" }   // 失败

HTTP 状态码对照(ztmp)

状态码含义
200成功
400参数错误 / 业务规则拒绝
401未登录 / Token 过期 / 密码错误
403无权操作
404资源不存在
409冲突(已被绑定 / 编号重复)
500服务器内部错误