# oak-external-sdk

`oak-external-sdk` 是一个面向 Oak 体系项目的外部服务适配库。

它把常见的第三方能力统一收敛到一个 TypeScript SDK 中，包括微信、短信、对象存储、地图服务等，并统一使用 `oak-domain` 的异常类型向上抛错。

## 能力范围

当前代码库主要覆盖以下模块：

- 微信
  - 小程序：`WechatMpInstance`
  - 公众号：`WechatPublicInstance`
  - 网页扫码登录：`WechatWebInstance`
  - 原生应用：`WechatNativeInstance`
- 短信
  - 阿里云
  - 腾讯云
  - 天翼云
- 对象存储 / 上传签名
  - 阿里云 OSS
  - 腾讯云 COS
  - 天翼云对象存储
  - 七牛 Kodo
  - S3 兼容存储
  - Local 自定义签名
- 地图
  - 高德地图
  - 天地图
- 其他
  - 微信公众号文章解析
  - 七牛直播相关接口

## 设计目标

这个库不是简单地把各家官方 SDK 原样透传出来，而是做了一层统一封装：

- 统一的 SDK 入口和实例缓存
- 统一的异常语义
- 常用上传签名和预签名 URL 能力
- 微信 access token 自动刷新
- 多运行环境适配

实例通常通过 `getInstance(...)` 获取，并按 `appId`、`accessKey` 等参数做缓存。

## 运行环境

- Node.js `>= 18`
- 依赖全局 `fetch`

## 安装与开发

在 Oak 工作区中本地开发：

```bash
npm install
```

构建：

```bash
npm run build
```

测试：

```bash
npm test
```

说明：

- `npm test` 当前更偏向手工测试脚本入口，不是完整的自动化单测体系。
- 仓库里还有 `test/testSms.ts`、`test/testWechatMp.ts`、`test/testCos.ts` 等示例脚本，可按需手动调整后执行。

## 导出入口

统一导出在 [src/index.ts](./src/index.ts)。

当前主要导出如下：

- `WechatSDK`
- `SmsSdk`
- `AmapSDK`
- `QiniuSDK`
- `CTYunSDK`
- `ALiYunSDK`
- `TencentYunSDK`
- `MapwordSDK`
- `LocalSDK`
- `S3SDK`

注意：

- 当前对外导出名是 `MapwordSDK`，不是 `MapWorldSDK`。这是代码中的现状，不是 README 笔误。

## 快速示例

### 1. 高德逆地理编码

```ts
import { AmapSDK } from 'oak-external-sdk';

const amap = AmapSDK.getInstance(process.env.AMAP_KEY!);

const result = await amap.regeo({
  longitude: 116.397128,
  latitude: 39.916527,
});

console.log(result);
```

### 2. 阿里云 OSS 上传参数

```ts
import { ALiYunSDK } from 'oak-external-sdk';

const oss = ALiYunSDK.getInstance(
  process.env.OSS_ACCESS_KEY!,
  process.env.OSS_ACCESS_SECRET!,
);

const uploadInfo = oss.getUploadInfo(
  'example-bucket',
  'cn-hangzhou',
  'images/demo.png',
);

console.log(uploadInfo);
```

### 3. S3 预签名上传 URL

```ts
import { S3SDK } from 'oak-external-sdk';

const s3 = S3SDK.getInstance(
  process.env.S3_ACCESS_KEY!,
  process.env.S3_ACCESS_SECRET!,
  process.env.S3_ENDPOINT,
  'us-east-1',
);

const uploadInfo = await s3.getUploadInfo(
  'example-bucket',
  'images/demo.png',
);

console.log(uploadInfo.uploadUrl);
```

### 4. 微信小程序订阅消息

```ts
import { WechatMpInstance, WechatSDK } from 'oak-external-sdk';

const wechat = WechatSDK.getInstance(
  process.env.WECHAT_APP_ID!,
  'wechatMp',
  process.env.WECHAT_APP_SECRET!,
) as WechatMpInstance;

await wechat.sendSubscribedMessage({
  templateId: 'TEMPLATE_ID',
  openId: 'OPEN_ID',
  data: {
    thing1: { value: '测试活动' },
  },
  state: 'formal',
});
```

### 5. 微信公众号 JS-SDK 签名

```ts
import { WechatPublicInstance, WechatSDK } from 'oak-external-sdk';

const wechat = WechatSDK.getInstance(
  process.env.WECHAT_PUBLIC_APP_ID!,
  'wechatPublic',
  process.env.WECHAT_PUBLIC_APP_SECRET!,
) as WechatPublicInstance;

const sign = await wechat.signatureJsSDK({
  url: 'https://example.com/page',
});

console.log(sign);
```

## 模块说明

### WechatSDK

入口文件：[src/WechatSDK.ts](./src/WechatSDK.ts)

支持按不同微信场景创建实例：

- `wechatMp`
- `wechatPublic`
- `web`
- `native`

主要能力包括：

- `code2Session`
- `decryptData`
- access token 自动刷新
- 小程序订阅消息 / 客服消息 / 物流接口
- 公众号模板消息 / 菜单 / 标签 / 素材 / 二维码 / JS-SDK 签名
- 网页授权场景

## SmsSdk

入口文件：[src/SmsSdk.ts](./src/SmsSdk.ts)

支持：

- `ali`
- `tencent`
- `ctyun`

典型方法：

- `sendSms(...)`
- `syncTemplate(...)`

## 对象存储 SDK

相关入口文件：

- [src/ALiYunSDK.ts](./src/ALiYunSDK.ts)
- [src/TencentYunSDK.ts](./src/TencentYunSDK.ts)
- [src/CTYunSDK.ts](./src/CTYunSDK.ts)
- [src/QiniuSDK.ts](./src/QiniuSDK.ts)
- [src/S3SDK.ts](./src/S3SDK.ts)
- [src/LocalSDK.ts](./src/LocalSDK.ts)

常见能力包括：

- `getUploadInfo(...)`
- `presignObjectUrl(...)`
- `isExistObject(...)`
- `removeFile(...)`
- `prepareMultipartUpload(...)`
- `uploadPart(...)`
- `completeMultipartUpload(...)`

补充说明：

- `QiniuSDK` 除文件存储外，还实现了直播流相关方法。
- `LocalSDK` 不是本地文件系统封装，而是一套自定义上传 token / access token 签名工具。

## 地图 SDK

入口文件：

- [src/AmapSDK.ts](./src/AmapSDK.ts)
- [src/MapWorldSDK.ts](./src/MapWorldSDK.ts)

典型能力：

- `regeo(...)`
- `geocode(...)`
- `getDrivingPath(...)`
- `getDistrict(...)`

其中天地图实现会对返回结果做一层转换，例如提取 `poiName`、`areaId`。

## 目录结构

```text
src/
  index.ts               # 统一导出
  *SDK.ts                # 各能力域入口和实例缓存
  service/               # 具体第三方服务实现
  types/                 # 对外类型定义
  utils/                 # fetch / form-data / cheerio 等适配层
test/                    # 手工测试脚本
scripts/                 # 构建辅助脚本
```

## 异常处理

库内部统一使用 `oak-domain` 的异常类型，例如：

- `OakExternalException`
- `OakNetworkException`
- `OakServerProxyException`
- `OakException`

这意味着上层业务可以用统一方式处理第三方服务错误，而不必分别适配各家 SDK 的错误对象。

## 多端适配说明

仓库中存在以下多端变体文件：

- `*.ts`
- `*.web.ts`
- `*.native.ts`
- `*.mp.ts`

主要用于适配：

- `fetch`
- `FormData`
- `cheerio`

但当前多端支持并不完全等价，部分 `mp` 实现仍是占位实现，使用前应按目标运行环境做实际验证。

## 当前限制

- README 之前缺失较多信息，本文件主要依据当前代码实现整理。
- 测试覆盖较弱，更多是联调脚本而不是完备单元测试。
- 一些模块的跨端实现并不完整。
- 某些导出命名存在历史遗留问题，例如 `MapwordSDK`。

## 相关文件

- 统一导出：[src/index.ts](./src/index.ts)
- 微信入口：[src/WechatSDK.ts](./src/WechatSDK.ts)
- 短信入口：[src/SmsSdk.ts](./src/SmsSdk.ts)
- 对象存储入口：[src/QiniuSDK.ts](./src/QiniuSDK.ts)
- 地图入口：[src/AmapSDK.ts](./src/AmapSDK.ts)
