274 lines
12 KiB
Markdown
274 lines
12 KiB
Markdown
# External 系统 → 启航ERP(拼多多)商品上架网关:裁剪与改造方案
|
||
|
||
> 目标:在 `qihang-ecom-erp-open` 基础上,**仅保留**“商品上架/商品同步(仅商品域)/接口鉴权”能力,作为单体服务提供给外部系统调用。
|
||
> 首期平台:**拼多多**。
|
||
> 调用方:外部系统(传入商品数据 + `shopId` + `platform`)。
|
||
> 店铺授权:由本服务维护(外部系统不传 appKey/appSecret/accessToken)。
|
||
> 鉴权:**AK/SK 签名鉴权**(system-to-system)。
|
||
|
||
## 1. 范围与非目标
|
||
|
||
### 1.1 In Scope(首期必须)
|
||
- **商品数据接入**:外部系统推送商品(SPU + SKU)到本服务(创建/更新)。
|
||
- **拼多多上架全链路**:
|
||
- **A** 创建商品(首次发布)
|
||
- **B** 更新商品信息(标题/主图/详情/属性等)
|
||
- **C** 更新 SKU(规格、价格等)
|
||
- **D** 更新库存
|
||
- **E** 上架/下架
|
||
- **F** 接收“已准备好的商品数据”(图片/详情为可访问 URL 或可上传的素材)并发起平台发布
|
||
- **最小商品同步**:
|
||
- 从拼多多拉取商品(用于对账/状态回写/增量同步),保留 `pull_goods` 能力即可
|
||
- 同步结果落库,供外部系统查询/校验
|
||
- **接口鉴权**:
|
||
- 调用方 → 本服务 API 必须签名验证(AK/SK)
|
||
- 失败拒绝(401/403),并记录审计日志
|
||
|
||
### 1.2 Out of Scope(首期明确不做/可后置)
|
||
- 订单/售后/发货/电子面单/仓库/采购等业务域
|
||
- 多平台(淘宝/京东/抖店/快手/小红书/微信小店等)
|
||
- 前端管理后台(Vue)与多租户/多商户能力
|
||
- AI-agent 相关能力
|
||
|
||
## 2. 现状评估(基于当前开源代码)
|
||
|
||
### 2.1 已具备的基础能力
|
||
- **商品库接入入口**:`erp-api` 下存在 `POST /goods/add`,并且模型存在 `outerErpGoodsId` 等外部标识字段,适合承接调用方商品数据。
|
||
- **拼多多商品拉取(同步)**:`oms-api` 下存在 `POST /pdd/goods/pull_goods`,当前实现是“拉取列表并落库”。
|
||
- **安全模块线索**:`core/security` 已存在并在 `erp-api`/`oms-api` 引用(但对调用方的 S2S 鉴权需要单独设计)。
|
||
|
||
### 2.2 关键缺口(必须改造/补齐)
|
||
- 当前拼多多侧 `open-sdk-2.1.12.jar` 中公开的 `PddGoodsApiHelper` 仅暴露 `pullGoodsList`(拉取),未暴露“创建/更新/上下架/库存更新”等发布能力。
|
||
- 因此,**商品上架全链路需要新增拼多多发布适配层**(可能复用已有签名/HTTP 基础设施,但要补齐 API)。
|
||
|
||
## 3. 目标架构(单体部署)
|
||
|
||
### 3.1 进程形态
|
||
单体 Spring Boot 应用(Java 17),对外提供 REST API:
|
||
- `external-api`:外部系统调用入口(推送商品/触发上架/查询结果)
|
||
- `admin-api`(可选,首期可不暴露):用于运维/店铺授权维护/密钥配置
|
||
|
||
### 3.2 逻辑分层(建议)
|
||
- **API 层**:只做参数校验、鉴权、幂等键处理、调用 Service、统一响应
|
||
- **Service 层**:
|
||
- 统一商品模型校验与映射
|
||
- 上架编排(创建→更新→库存→上架状态)
|
||
- 与拼多多适配层交互
|
||
- 任务化(可选):异步发布/重试/死信
|
||
- **Adapter 层(PDD)**:
|
||
- HTTP + 签名 + 请求/响应模型
|
||
- 平台错误码映射
|
||
|
||
## 4. 对外接口设计(External/外部系统 → 本服务)
|
||
|
||
> 说明:下面是建议接口。落地时以外部系统现有调用规范为准,可在后续命令下达后再对齐路径/DTO 命名。
|
||
|
||
### 4.0 已确认口径(冻结)
|
||
- `shopId`:**全平台唯一**,可直接作为本服务店铺主键,不考虑跨平台撞号。
|
||
- `platform`:由外部系统显式传入,取值采用 **`PDD`/`TAO`/`JD`/`DOU`/`WEIXIN`/`KWAI`/`XHS`**(后续平台新增再扩展枚举)。
|
||
- AK/SK:**一套即可**(外部系统作为单一调用方)。
|
||
- `X-Timestamp`:**毫秒**(ms)。
|
||
- `X-Signature`:**Base64** 编码(HMAC-SHA256 输出 bytes → Base64)。
|
||
- 签名 body:使用**原始请求体 raw bytes** 计算 SHA-256(不做 JSON 重排/字段排序)。
|
||
- 参数形态:**统一走 body**(尽量避免 query;GET 若必须使用 query,则改为 POST 查询接口更一致)。
|
||
- 幂等:**完全由本服务内部实现**,不依赖调用方提供 `X-Request-Id`。
|
||
- 商品素材:首期按**公网可访问 URL**(不做素材代上传)。
|
||
- 接口形态:首期按**同步接口**(直接返回发布结果或明确错误)。
|
||
|
||
### 4.1 统一返回
|
||
- `code`: 0 成功;非 0 失败
|
||
- `msg`: 错误信息
|
||
- `data`: 业务数据
|
||
|
||
### 4.2 鉴权(AK/SK 签名)
|
||
#### 请求头
|
||
- `X-Api-Key`: 分配给调用方的 AK
|
||
- `X-Timestamp`: 毫秒时间戳(例如 `1710000000000`)
|
||
- `X-Nonce`: 随机串(建议 16~32)
|
||
- `X-Signature`: 签名串(Base64)
|
||
- `Content-Type: application/json`
|
||
|
||
#### 签名串构造(推荐)
|
||
canonical string:
|
||
1. `HTTP_METHOD`(大写)
|
||
2. `PATH`(不含域名,不含 query)
|
||
3. `timestamp`
|
||
4. `nonce`
|
||
5. `bodySha256Hex`(请求体 raw bytes 的 SHA-256,hex)
|
||
|
||
拼接方式:
|
||
```
|
||
METHOD + "\n" +
|
||
PATH + "\n" +
|
||
timestamp + "\n" +
|
||
nonce + "\n" +
|
||
bodySha256Hex
|
||
```
|
||
|
||
签名算法:
|
||
- `signature = HMAC-SHA256(secret, canonicalString)`
|
||
- 输出:对 `signature` 的 bytes 做 `Base64`。
|
||
|
||
#### 校验规则
|
||
- `timestamp` 与服务端时间偏差 ≤ 300 秒(按 ms 计算)
|
||
- `nonce` 在时间窗内不可重复(需要缓存,例如 Redis/本地 Caffeine,首期可先用 Redis)
|
||
- `X-Api-Key` 必须存在且有效(数据库/配置中)
|
||
- `X-Signature` 校验失败直接拒绝
|
||
|
||
#### 错误码建议
|
||
- 401:缺少必要头/签名参数
|
||
- 403:签名错误、AK 无效、nonce 重放
|
||
- 429:频控(可选)
|
||
|
||
### 4.3 商品接入与上架接口(建议)
|
||
#### 4.3.1 推送/更新商品(SPU + SKU)
|
||
`POST /external/goods/upsert`
|
||
|
||
入参:
|
||
- `shopId`(必填)
|
||
- `platform`(必填,枚举,例如 `PDD`/`TAO`/`JD`…;首期仅允许 `PDD`)
|
||
- `outGoodsId`:调用方侧商品ID(用于幂等与映射)
|
||
- `title`、`mainImages[]`、`detail`(富文本/图片 URL 列表等)
|
||
- `skus[]`:`outSkuId`、`spec`、`price`、`stock`、`barCode`…
|
||
- `category`、`attrs`、`brand`、`weight`、`shipping`…
|
||
|
||
行为:
|
||
- 本地保存/更新商品库(以 `shopId + outGoodsId` 幂等)
|
||
- 返回本地 `goodsId`(内部ID)与当前状态
|
||
|
||
#### 4.3.2 触发上架(编排)
|
||
`POST /external/goods/listing/submit`
|
||
|
||
入参:
|
||
- `shopId`
|
||
- `platform`(必填,首期仅允许 `PDD`)
|
||
- `outGoodsId`
|
||
- `mode`: `CREATE_OR_UPDATE`(默认)
|
||
|
||
行为(同步/异步二选一,建议首期同步 + 超时控制,后续演进异步):
|
||
- 若平台商品不存在:创建商品 → 创建/更新 SKU → 设置库存 → 上架
|
||
- 若已存在:更新商品 → 更新 SKU → 库存 → 上架/保持
|
||
|
||
返回:
|
||
- `listingTaskId`(若异步)
|
||
- 或直接返回平台侧 `goodsId`/状态
|
||
|
||
#### 4.3.3 上下架
|
||
`POST /external/goods/listing/status`
|
||
|
||
入参:
|
||
- `shopId`
|
||
- `platform`(必填,首期仅允许 `PDD`)
|
||
- `outGoodsId`
|
||
- `status`: `ON`/`OFF`
|
||
|
||
#### 4.3.4 库存更新(可被 submit 内部调用,也可单独暴露)
|
||
`POST /external/goods/stock/update`
|
||
|
||
入参:
|
||
- `shopId`
|
||
- `platform`(必填,首期仅允许 `PDD`)
|
||
- `outGoodsId`
|
||
- `skus[]`: `outSkuId` + `stock`
|
||
|
||
#### 4.3.5 查询发布状态/映射
|
||
`GET /external/goods/listing/status?shopId=...&platform=...&outGoodsId=...`
|
||
|
||
返回:
|
||
- 平台商品ID、上下架状态、最后一次错误、最后同步时间
|
||
|
||
## 5. 领域模型(最小集合)
|
||
|
||
### 5.1 本地核心表(建议最小)
|
||
- `shop`:店铺基础信息(含平台类型=PDD)
|
||
- `shop_auth`:拼多多 appKey/appSecret/accessToken/refreshToken/过期时间
|
||
- `goods`:统一商品(承接调用方)
|
||
- `goods_sku`:统一 SKU(承接调用方)
|
||
- `goods_platform_mapping`:统一商品与平台商品映射(平台 goodsId、skuId 映射)
|
||
- `listing_task`(可选):异步发布任务、重试次数、状态机
|
||
- `api_client`:AK/SK、状态、权限范围、IP 白名单(可选)
|
||
- `api_request_log`:审计(签名校验结果、请求摘要、耗时)
|
||
|
||
### 5.2 幂等策略(必须)
|
||
- `shopId + outGoodsId`:商品幂等键
|
||
- `shopId + outSkuId`:SKU 幂等键
|
||
- **不依赖调用方幂等键**:本服务内部需实现“请求去重/防重复上架”。
|
||
- 推荐:落库 `idempotency_key = sha256Hex(platform + shopId + outGoodsId + operation + normalizedBodyHash)`
|
||
- 同一 `idempotency_key` 在有效期内重复请求:直接返回第一次执行结果
|
||
- 对“提交上架”类接口:至少保证不会重复创建平台商品
|
||
|
||
## 6. 拼多多适配(PDD Adapter)设计要点
|
||
|
||
### 6.1 适配层职责
|
||
- 拼多多 API 签名、请求发送、响应解析
|
||
- 平台错误码归一化
|
||
- “创建/更新/上下架/库存”接口封装为内部方法:
|
||
- `createGoods(...)`
|
||
- `updateGoods(...)`
|
||
- `updateSku(...)`
|
||
- `updateStock(...)`
|
||
- `setGoodsOnSale(...)` / `setGoodsOffSale(...)`
|
||
|
||
### 6.2 依赖策略(重要)
|
||
当前 `oms-api` 通过 `systemPath` 引入 `open-sdk-2.1.12.jar`。
|
||
|
||
首期有两条路径(二选一):
|
||
- **路径 1(优先)**:在本仓库内新增 `pdd-adapter`(源码方式),不再依赖 `open-sdk-2.1.12.jar` 做发布能力(只保留 pull 同步可继续复用或也迁移)。
|
||
- **路径 2(备选)**:升级/替换 `open-sdk`,确保其暴露发布相关 API(需要确认是否可获得源码/维护成本)。
|
||
|
||
> 推荐路径 1:可控、可审计、便于后续扩展多平台。
|
||
|
||
## 7. 裁剪方案(保留/删除建议)
|
||
|
||
> 原项目是微服务结构(gateway/sys-api/erp-api/oms-api 等)。你的目标是单体优先,因此裁剪思路是:
|
||
> 1) **保留商品域 + 拼多多域 + 鉴权**
|
||
> 2) 其它域模块不编译/不启动/不暴露接口
|
||
|
||
### 7.1 建议保留(首期)
|
||
- `core/common`(通用工具、AjaxResult 等)
|
||
- `core/security`(复用登录/权限基础设施的同时,新增 S2S AK/SK 鉴权)
|
||
- `model`(实体/BO/VO)
|
||
- `mapper`(DB 访问)
|
||
- `service`、`serviceImpl` 中与商品/店铺/拼多多相关的最小集合
|
||
- `api/erp-api`(商品库接入:`/goods/add` 等入口可重构为 `/external/goods/*`)
|
||
- `api/oms-api` 中 **拼多多商品拉取**(同步)相关接口(可改为内部任务或保留对外)
|
||
|
||
### 7.2 建议禁用/后置
|
||
- `api/ai-agent`
|
||
- `api/sys-api`(如仅对调用方开放,不需要后台用户体系,可后置)
|
||
- `api/gateway`(若单体直连,不需要网关;若保留网关需把 TokenFilter 改为 AK/SK 校验)
|
||
- `oms-api` 中非拼多多平台(dou/jd/tao/wei/kwai…)
|
||
- 订单/售后/发货/库存出入库/采购等 Controller 与 Service
|
||
|
||
## 8. 安全与审计
|
||
- 所有外部系统接口强制 AK/SK
|
||
- **平台一致性校验(强制)**:
|
||
- 调用方传入 `platform`
|
||
- 服务端根据 `shopId` 查询数据库中的 `shopType/platformType`
|
||
- 若两者不一致:直接拒绝(建议 400 或 403),记录审计日志
|
||
- 目的:避免“串平台/串店铺”导致误上架
|
||
- `shopId` 必须存在且平台类型为 PDD
|
||
- 店铺 accessToken 过期:返回明确错误码(例如 `SHOP_TOKEN_EXPIRED`),并支持后台刷新/重新授权流程(首期可人工)
|
||
- 全链路日志:
|
||
- `requestId`、`shopId`、`outGoodsId`、平台错误码、耗时
|
||
- 敏感信息保护:
|
||
- appSecret/accessToken 加密存储(KMS/本地对称加密;首期可用配置密钥 + AES)
|
||
- 严禁打印明文 token/secret
|
||
|
||
## 9. 验证与验收清单(首期)
|
||
- [ ] 外部系统推送商品 → 本地入库(幂等生效)
|
||
- [ ] 触发上架 → 拼多多创建/更新/库存/上下架成功(全链路)
|
||
- [ ] 上架失败可返回可读错误(含平台原始信息可追踪)
|
||
- [ ] `pull_goods` 同步可用(用于对账/状态回写)
|
||
- [ ] AK/SK:
|
||
- [ ] 缺参拒绝
|
||
- [ ] 签名错误拒绝
|
||
- [ ] 超时拒绝
|
||
- [ ] nonce 重放拒绝
|
||
- [ ] 审计日志完整
|
||
|
||
## 10. 待你确认/后续落地前置
|
||
- 拼多多商品发布所需字段“最小集”(类目、属性、物流、售后承诺、资质等)需要你提供调用方侧现有字段映射或样例 payload。
|
||
- 是否需要“异步上架”(返回 taskId + 回调/轮询)。首期建议同步,但需要设定超时时间与重试策略。
|
||
|