qihang-ecom-erp-open/docs/yunxi-pdd-goods-listing-gat...

274 lines
12 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.

# 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`/`WEI`/`KWAI`/`XHS`**(后续平台新增再扩展枚举)。
- AK/SK**一套即可**(外部系统作为单一调用方)。
- `X-Timestamp`**毫秒**ms
- `X-Signature`**Base64** 编码HMAC-SHA256 输出 bytes → Base64
- 签名 body使用**原始请求体 raw bytes** 计算 SHA-256不做 JSON 重排/字段排序)。
- 参数形态:**统一走 body**(尽量避免 queryGET 若必须使用 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-256hex
拼接方式:
```
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 + 回调/轮询)。首期建议同步,但需要设定超时时间与重试策略。