14 KiB
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 已确认接口链路
-
发送短信验证码
POST https://123.207.217.176/public/verif_code -
提交登录
POST https://123.207.217.176/public/login -
登录后探活接口
POST /user/infoGET /user/get_assetGET /user/get_permission
-
当前 HAR 里还看到的业务接口
GET /categories/getGET /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
}
然后:
JSON.stringify- 使用 RSA-OAEP + SHA-256 公钥加密
- Base64 编码,作为
covert
最终请求体:
{
"covert": "<base64>",
"t": 1777726021956,
"r": "0.8601863c152d6"
}
2.5 密码处理
密码不是明文传输。
前端代码中:
password: cu(password)
而 cu 实际是:
MD5(password).toString(Hex)
即:
- 先
MD5 - 再参与 RSA 加密
3. 设计目标
3.1 第一阶段目标
这一版只做“基础登录能力”,不直接做完整发货自动化:
- 能发送短信验证码;
- 能使用账号、密码、手机号、短信验证码自动登录;
- 能持久化保存该平台的履约配置;
- 能在后台手动测试登录;
- 能在登录后调用一个轻量探活接口确认登录成功;
- 输出统一的会话对象,给后续“SKU 查询 / 背包读取 / 发货执行”复用。
3.2 第二阶段目标
后续逐步补:
- token 缓存与自动续登;
- SKU/分类/背包读取;
- 发货动作封装;
- 发货结果记录与截图/证据保存;
- 接成新的
executor_key,纳入现有履约任务执行链路; - 由“订单支付成功 / 任务就绪”触发自动发货。
4. 关键设计决策
4.1 领域定位
这里不应该新增 provider = 'cloudtentacles' 作为订单来源标识。
更合理的定位是:
- 订单来源
provider/platform/shopId仍然来自agiso/91卡券 cloudtentacles属于履约执行器 / 外部发货平台
因此它更适合进入这条链路:
fulfillment_profiles.profile_keyfulfillment_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/config/default.cjsapps/backend/src/types/runtime-config.jsapps/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
入参:
usernamepasswordphonecode
返回:
tokenuserInfopermissionsloggedInAt
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]
第一版重点记录:
- 发送验证码是否成功;
- 登录是否成功;
/user/info探活是否成功;- 平台返回的
code/message; - 不记录明文密码、短信验证码、完整 token。
建议在日志和接口返回里都做脱敏:
- 手机号只显示部分;
- token 只显示前后几位;
- password 永不回显。
10. 与现有履约架构的关系
当前项目里的履约主链路已经是:
- 订单进入系统;
- 根据
provider/platform/shopId/sku命中order-fulfillment-bindings.json; - 解析到
fulfillment_profile; - 创建
delivery task; - 根据
executor_key决定后续执行方式。
所以 cloudtentacles 后续正确的接入位置是:
- 第一阶段:只补平台登录能力;
- 第二阶段:新增
cloudtentacles_dispatch执行器; - 第三阶段:把某些 SKU 的履约绑定切到这个执行器。
也就是说:
agiso/91卡券决定“订单从哪里来”cloudtentacles决定“订单怎么发出去”
11. 实施顺序
建议按这个顺序落地:
第 1 步:后端基础模块
先实现:
shared.jscrypto-service.jshttp-client.jssession-service.jssource-config-service.js
目标:
- 本地可完成发送验证码;
- 收到短信后可手动输入验证码登录;
- 登录后可拉
/user/info。
第 2 步:后台接口
增加:
- 配置读取/保存
- 发送验证码
- 登录测试
- token 探活
第 3 步:后台 UI
增加:
- 单独的平台配置区
- 发送验证码与登录测试区
- 调试反馈区
第 4 步:后续业务能力
登录能力稳定后,再补:
- SKU/分类/背包读取
- 发货动作
cloudtentacles_dispatch执行器- 自动发货链路
12. 当前阶段结论
这个平台不是新的订单数据源,而是新的外部履约能力。
第一版接入重点不是立刻改主流程,而是先把 可复用的登录能力 做扎实。
最重要的不是把接口先堆进去,而是先把结构定对:
- 历史拉单型平台:验证码图片 + Cookie
cloudtentacles型平台:短信码 + RSA 加密 + token
二者都属于“后台登录型平台”,但在系统分层上不同:
91卡券:来源层cloudtentacles:履约层
因此第一版推荐结论是:
- 作为独立履约平台能力接入;
- 先做“配置 + 发验证码 + 登录 + 探活”;
- 目录结构按“配置 / 加密 / HTTP / 会话 / 业务”拆开;
- 后续通过新的
executor_key接入履约任务执行,而不是改订单来源模型。