Files
order_site/docs/cloudtentacles发货平台接入设计.md
T
2026-05-21 19:29:00 +08:00

734 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# cloudtentacles 外部履约平台接入设计
## 1. 背景
当前项目里现有链路大致分两层:
- **订单来源层**
- `agiso`Webhook 推送型;
- `91卡券`:开放接口下单型。
- **履约执行层**
- 当前以内置人工发货、腾讯领取兑换等执行器为主。
现在新增一个发货相关的平台,当前已知入口为:
- `https://123.207.217.176`
- 前端资源来自 `gp.playinjoy.com`
- 前端代码里根实例名显示为 `cloudtentacles`
这个平台**不是订单数据源**,而是**订单创建后用于执行发货/兑换的外部履约平台**。
它和订单来源平台的系统角色完全不同:
- `91卡券`:订单来源 / 上游平台;
- `cloudtentacles`:履约执行 / 下游发货平台。
它的登录链路独立于订单来源:
- 不是图片验证码识别;
- 是“账号 + 密码 + 手机号 + 短信验证码”登录;
- 登录参数不是明文提交,而是前端先做 `MD5 + RSA-OAEP(SHA-256)` 加密;
- 登录成功后不是 Cookie 会话,而是返回 `token`,后续通过 `Authorization` 请求头访问接口。
因此它应当作为**履约执行器能力**接入,不要按“来源平台”建模。
---
## 2. 当前已确认事实
### 2.1 证据来源
- HAR`/Users/yml/Desktop/抓包/123.207.217.176.har`
- 前端代码:`tems/test1.js`
- 打包代码:`tems/index.93fe093c.js`
### 2.2 已确认接口链路
1. 发送短信验证码
`POST https://123.207.217.176/public/verif_code`
2. 提交登录
`POST https://123.207.217.176/public/login`
3. 登录后探活接口
- `POST /user/info`
- `GET /user/get_asset`
- `GET /user/get_permission`
4. 当前 HAR 里还看到的业务接口
- `GET /categories/get`
- `GET /sku/list`
### 2.3 登录态机制
不是依赖 Cookie。
前端在登录成功后会把 `/public/login` 返回的 `data` 当成 token 保存,并在后续请求里自动带上:
```http
Authorization: <token>
deviceid: -
devicetype: 0
```
### 2.4 登录参数的真实结构
根据 `tems/test1.js`,发验证码与登录都不是直接提交原始字段,而是先调用 `encryptWithPublicKey(...)`
#### 发验证码原始参数
```js
{
account: username,
phone: phone
}
```
#### 登录原始参数
```js
{
account: username,
password: MD5(password),
phone: phone,
code: smsCode
}
```
#### 加密前最终明文
```js
{
t: Date.now(),
r: Math.random().toString(16),
s: 'ct-client',
...payload
}
```
然后:
1. `JSON.stringify`
2. 使用 RSA-OAEP + SHA-256 公钥加密
3. Base64 编码,作为 `covert`
最终请求体:
```json
{
"covert": "<base64>",
"t": 1777726021956,
"r": "0.8601863c152d6"
}
```
### 2.5 密码处理
密码不是明文传输。
前端代码中:
```js
password: cu(password)
```
`cu` 实际是:
```js
MD5(password).toString(Hex)
```
即:
-`MD5`
- 再参与 RSA 加密
---
## 3. 设计目标
## 3.1 第一阶段目标
这一版只做“基础登录能力”,不直接做完整发货自动化:
1. 能发送短信验证码;
2. 能使用账号、密码、手机号、短信验证码自动登录;
3. 能持久化保存该平台的履约配置;
4. 能在后台手动测试登录;
5. 能在登录后调用一个轻量探活接口确认登录成功;
6. 输出统一的会话对象,给后续“SKU 查询 / 背包读取 / 发货执行”复用。
## 3.2 第二阶段目标
后续逐步补:
1. token 缓存与自动续登;
2. SKU/分类/背包读取;
3. 发货动作封装;
4. 发货结果记录与截图/证据保存;
5. 接成新的 `executor_key`,纳入现有履约任务执行链路;
6. 由“订单支付成功 / 任务就绪”触发自动发货。
---
## 4. 关键设计决策
## 4.1 领域定位
这里不应该新增 `provider = 'cloudtentacles'` 作为订单来源标识。
更合理的定位是:
- 订单来源 `provider/platform/shopId` 仍然来自 `agiso` / `91卡券`
- `cloudtentacles` 属于**履约执行器 / 外部发货平台**
因此它更适合进入这条链路:
- `fulfillment_profiles.profile_key`
- `fulfillment_profiles.executor_key`
- 任务执行服务
建议后续新增类似:
- `profileKey = 'cloudtentacles_dispatch'`
- `executorKey = 'cloudtentacles_dispatch'`
而不是把它混进订单来源层。
## 4.2 接入模式
这个平台本质上是 **外部履约平台 + token 会话** 模式。
因此结构上可以先放在 `platforms/` 下管理其登录与 API 封装,但业务接入点要落到**履约执行层**:
- 配置服务
- 加密服务
- HTTP 客户端
- 登录/会话服务
- 发货业务服务
- 履约执行适配层
## 4.3 不建议照搬来源平台的原因
历史拉单平台链路核心是:
- 打开登录页
- 拉验证码图片
- OCR
- 表单提交
- Cookie 维持会话
而当前平台核心是:
- 组装明文
- MD5 密码
- RSA 公钥加密
- 请求登录接口
- token 维持会话
所以如果把所有逻辑都塞进一个 `session-service.js`,后面会越来越乱。
更合理的方式是把“加密”、“请求头”、“token 管理”单独拆开。
## 4.4 第一版不引入浏览器自动化
当前证据显示不需要浏览器:
- HAR 中登录接口是纯 XHR
- 参数生成逻辑已经从 JS 中还原;
- token 机制清晰;
- 没看到依赖浏览器页面上下文的动态签名。
因此第一版应坚持 **纯 HTTP 实现**
只有后续遇到这些问题时,才考虑浏览器方案:
- 服务端增加不可还原的前端签名;
- 短信验证码流程强依赖页面态;
- 登录成功但服务端对业务接口做浏览器环境校验。
---
## 5. 推荐目录结构
建议新增目录:
`apps/backend/src/services/platforms/cloudtentacles/`
同时第二阶段补一个执行适配入口,例如:
`apps/backend/src/services/fulfillment/cloudtentacles-dispatch-service.js`
### 5.1 第一阶段实际落地文件
#### `shared.js`
职责:
- 读取运行时配置;
- 规范化 baseUrl / 路径 / timeout
- 构建 URL
- 构建公共请求头;
- 统一设备头默认值。
建议内容:
- `resolveCloudtentaclesConfig(overrides)`
- `buildCloudtentaclesUrl(baseUrl, pathname, searchParams?)`
- `buildCloudtentaclesHeaders({ token?, contentType? })`
#### `crypto-service.js`
职责:
- 密码 MD5
- 公钥 PEM 转 ArrayBuffer / Buffer
- 组装 `t/r/s` 明文;
- 执行 RSA-OAEP(SHA-256) 加密;
- 返回 `{ covert, t, r }`
建议暴露:
- `md5CloudtentaclesPassword(password)`
- `encryptCloudtentaclesPayload(payload, options?)`
这样后续不仅登录能用,发验证码、改密等其它加密接口也都能复用。
#### `http-client.js`
职责:
-`fetch` 做一层轻封装;
- 统一 timeout
- 自动带 `Authorization / deviceid / devicetype`
- 统一解析平台响应格式;
- 统一抛出带平台上下文的错误。
建议暴露:
- `cloudtentaclesRequest(path, options)`
注意:第一版不用做成全局通用 HTTP 框架,只服务这个平台即可。
#### `session-service.js`
职责:
- 发送短信验证码;
- 使用短信验证码登录;
- 登录后调用 `/user/info` 验证会话;
- 返回标准会话对象。
建议暴露:
- `sendCloudtentaclesSmsCode(payload)`
- `loginCloudtentaclesSession(payload)`
- `validateCloudtentaclesSession(payload)`
建议会话对象:
```js
{
baseUrl,
token,
loggedInAt,
username,
phone,
userInfo,
permissions
}
```
#### `source-config-service.js`
职责:
- 读写履约平台配置文件;
- 管理默认账号、密码、手机号;
- 管理后续自动化开关预留字段。
建议文件:
`apps/backend/data/cloudtentacles-sources.json`
建议结构:
```json
{
"enabled": true,
"baseUrl": "https://123.207.217.176",
"username": "",
"password": "",
"phone": "",
"deviceId": "-",
"deviceType": 0
}
```
### 5.2 第二阶段预留文件
这些先不一定创建实现,但目录命名先按这个方向约束:
#### `catalog-service.js`
- 分类
- SKU
- 背包
- 库存类读取
#### `delivery-service.js`
- 发货动作
- CDK / 虚拟物品使用
- 回滚 / 失败重试
#### `record-service.js`
- 交易记录
- 发货记录
- 订单对账
#### `auto-sync-service.js`
- 定时探活
- 自动续登
- 预热会话
#### `apps/backend/src/services/fulfillment/cloudtentacles-dispatch-service.js`
职责:
- 接收履约任务;
- 读取任务绑定的库存 / 凭据;
- 调用 `platforms/cloudtentacles/*` 完成实际发货;
- 回写任务状态、发货结果、证据。
---
## 6. 配置设计
## 6.1 运行时配置
建议在:
- `apps/backend/src/config/defaults.ts`
- `apps/backend/src/types/runtime-config.js`
- `apps/backend/src/config/runtime.js`
增加:
```js
platforms: {
cloudtentacles: {
baseUrl: 'https://123.207.217.176',
timeoutMs: 5000,
sendSmsPath: '/public/verif_code',
loginPath: '/public/login',
userInfoPath: '/user/info',
assetPath: '/user/get_asset',
permissionPath: '/user/get_permission',
publicKeyPem: '-----BEGIN PUBLIC KEY----- ... -----END PUBLIC KEY-----',
clientSource: 'ct-client',
deviceId: '-',
deviceType: 0,
},
}
```
### 设计说明
- `publicKeyPem` 放运行时配置,而不是硬编码到 service;
- `clientSource` 默认是 `ct-client`
- `deviceId / deviceType` 先允许配置,避免后面服务端开始校验时需要改代码。
## 6.2 持久化来源配置
建议先用数据文件落地,不急着进数据库。
建议文件:
`apps/backend/data/cloudtentacles-sources.json`
建议第一版结构:
```json
{
"enabled": true,
"baseUrl": "https://123.207.217.176",
"username": "",
"password": "",
"phone": "",
"deviceId": "-",
"deviceType": 0
}
```
后续如果要支持多套账号,再演进为:
```json
{
"sources": [
{
"sourceKey": "cloudtentacles-main",
"enabled": true,
"baseUrl": "https://123.207.217.176",
"username": "",
"password": "",
"phone": "",
"deviceId": "-",
"deviceType": 0
}
]
}
```
第一版先单来源,能明显降低 UI 和逻辑复杂度。
---
## 7. API 设计建议
建议先在管理后台补一组最小接口:
### 7.1 配置读取
```http
GET /api/v1/admin/platform-config/cloudtentacles-source
```
返回:
- 配置文件路径
- 当前履约平台配置
### 7.2 配置保存
```http
POST /api/v1/admin/platform-config/cloudtentacles-source
```
用于保存:
- 账号
- 密码
- 手机号
- baseUrl
- 设备头默认值
### 7.3 发送短信验证码
```http
POST /api/v1/admin/platforms/cloudtentacles/send-sms-code
```
入参支持:
- 使用已保存配置;
- 或临时覆盖账号/手机号/baseUrl。
### 7.4 手动登录测试
```http
POST /api/v1/admin/platforms/cloudtentacles/login
```
入参:
- `username`
- `password`
- `phone`
- `code`
返回:
- `token`
- `userInfo`
- `permissions`
- `loggedInAt`
### 7.5 会话探活测试
```http
POST /api/v1/admin/platforms/cloudtentacles/validate-session
```
入参:
- `token`
返回:
- 是否有效
- 用户信息
---
## 8. 后台 UI 设计建议
第一版 UI 不需要太复杂,应避免把“订单来源配置”和“履约平台配置”混在一起。
建议在“平台配置”页新增独立区块,归类到“履约平台”分组:
### 8.1 履约平台配置区
字段:
- 启用状态
- baseUrl
- 账号
- 密码
- 手机号
- deviceId
- deviceType
操作:
- 保存配置
### 8.2 登录测试区
字段:
- 短信验证码输入框
操作:
- 发送验证码
- 执行登录
- 验证当前 token
### 8.3 调试结果区
显示:
- token 是否返回
- userInfo
- permissions
- 最近一次错误消息
注意:
- 不要把这个平台和订单来源放在同一张混合表单里;
- 页面上要明确区分:
- 订单来源平台
- 履约执行平台
- 后面平台越来越多时,这个结构才能撑住。
---
## 9. 错误处理与日志建议
建议统一给这个平台加明确日志前缀:
- `[cloudtentacles/crypto]`
- `[cloudtentacles/session]`
- `[cloudtentacles/http]`
第一版重点记录:
1. 发送验证码是否成功;
2. 登录是否成功;
3. `/user/info` 探活是否成功;
4. 平台返回的 `code/message`
5. 不记录明文密码、短信验证码、完整 token。
建议在日志和接口返回里都做脱敏:
- 手机号只显示部分;
- token 只显示前后几位;
- password 永不回显。
---
## 10. 与现有履约架构的关系
当前项目里的履约主链路已经是:
1. 订单进入系统;
2. 根据 `provider/platform/shopId/sku` 命中 `order-fulfillment-bindings.json`
3. 解析到 `fulfillment_profile`
4. 创建 `delivery task`
5. 根据 `executor_key` 决定后续执行方式。
所以 `cloudtentacles` 后续正确的接入位置是:
- 第一阶段:只补平台登录能力;
- 第二阶段:新增 `cloudtentacles_dispatch` 执行器;
- 第三阶段:把某些 SKU 的履约绑定切到这个执行器。
也就是说:
- `agiso/91卡券` 决定“订单从哪里来”
- `cloudtentacles` 决定“订单怎么发出去”
---
## 11. 实施顺序
建议按这个顺序落地:
### 第 1 步:后端基础模块
先实现:
- `shared.js`
- `crypto-service.js`
- `http-client.js`
- `session-service.js`
- `source-config-service.js`
目标:
- 本地可完成发送验证码;
- 收到短信后可手动输入验证码登录;
- 登录后可拉 `/user/info`
### 第 2 步:后台接口
增加:
- 配置读取/保存
- 发送验证码
- 登录测试
- token 探活
### 第 3 步:后台 UI
增加:
- 单独的平台配置区
- 发送验证码与登录测试区
- 调试反馈区
### 第 4 步:后续业务能力
登录能力稳定后,再补:
- SKU/分类/背包读取
- 发货动作
- `cloudtentacles_dispatch` 执行器
- 自动发货链路
---
## 12. 当前阶段结论
这个平台不是新的订单数据源,而是新的**外部履约能力**。
第一版接入重点不是立刻改主流程,而是先把 **可复用的登录能力** 做扎实。
最重要的不是把接口先堆进去,而是先把结构定对:
- 历史拉单型平台:验证码图片 + Cookie
- `cloudtentacles` 型平台:短信码 + RSA 加密 + token
二者都属于“后台登录型平台”,但在系统分层上不同:
- `91卡券`:来源层
- `cloudtentacles`:履约层
因此第一版推荐结论是:
1. 作为独立履约平台能力接入;
2. 先做“配置 + 发验证码 + 登录 + 探活”;
3. 目录结构按“配置 / 加密 / HTTP / 会话 / 业务”拆开;
4. 后续通过新的 `executor_key` 接入履约任务执行,而不是改订单来源模型。