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

14 KiB
Raw Blame History

cloudtentacles 外部履约平台接入设计

1. 背景

当前项目里现有链路大致分两层:

  • 订单来源层
    • agisoWebhook 推送型;
    • 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 保存,并在后续请求里自动带上:

Authorization: <token>
deviceid: -
devicetype: 0

2.4 登录参数的真实结构

根据 tems/test1.js,发验证码与登录都不是直接提交原始字段,而是先调用 encryptWithPublicKey(...)

发验证码原始参数

{
  account: username,
  phone: phone
}

登录原始参数

{
  account: username,
  password: MD5(password),
  phone: phone,
  code: smsCode
}

加密前最终明文

{
  t: Date.now(),
  r: Math.random().toString(16),
  s: 'ct-client',
  ...payload
}

然后:

  1. JSON.stringify
  2. 使用 RSA-OAEP + SHA-256 公钥加密
  3. Base64 编码,作为 covert

最终请求体:

{
  "covert": "<base64>",
  "t": 1777726021956,
  "r": "0.8601863c152d6"
}

2.5 密码处理

密码不是明文传输。

前端代码中:

password: cu(password)

cu 实际是:

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)

建议会话对象:

{
  baseUrl,
  token,
  loggedInAt,
  username,
  phone,
  userInfo,
  permissions
}

source-config-service.js

职责:

  • 读写履约平台配置文件;
  • 管理默认账号、密码、手机号;
  • 管理后续自动化开关预留字段。

建议文件:

apps/backend/data/cloudtentacles-sources.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

增加:

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

建议第一版结构:

{
  "enabled": true,
  "baseUrl": "https://123.207.217.176",
  "username": "",
  "password": "",
  "phone": "",
  "deviceId": "-",
  "deviceType": 0
}

后续如果要支持多套账号,再演进为:

{
  "sources": [
    {
      "sourceKey": "cloudtentacles-main",
      "enabled": true,
      "baseUrl": "https://123.207.217.176",
      "username": "",
      "password": "",
      "phone": "",
      "deviceId": "-",
      "deviceType": 0
    }
  ]
}

第一版先单来源,能明显降低 UI 和逻辑复杂度。


7. API 设计建议

建议先在管理后台补一组最小接口:

7.1 配置读取

GET /api/v1/admin/platform-config/cloudtentacles-source

返回:

  • 配置文件路径
  • 当前履约平台配置

7.2 配置保存

POST /api/v1/admin/platform-config/cloudtentacles-source

用于保存:

  • 账号
  • 密码
  • 手机号
  • baseUrl
  • 设备头默认值

7.3 发送短信验证码

POST /api/v1/admin/platforms/cloudtentacles/send-sms-code

入参支持:

  • 使用已保存配置;
  • 或临时覆盖账号/手机号/baseUrl。

7.4 手动登录测试

POST /api/v1/admin/platforms/cloudtentacles/login

入参:

  • username
  • password
  • phone
  • code

返回:

  • token
  • userInfo
  • permissions
  • loggedInAt

7.5 会话探活测试

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 接入履约任务执行,而不是改订单来源模型。