From 29ccdd6ea0d63d73ec3f4ed4110a821c054f6d1f Mon Sep 17 00:00:00 2001 From: huangyujie <27665451@qq.com> Date: Fri, 10 Apr 2026 19:21:33 +0800 Subject: [PATCH] =?UTF-8?q?feat(pdd):=20=E5=A2=9E=E5=8A=A0=20latest-commit?= =?UTF-8?q?-status=20=E5=A4=96=E9=83=A8=E6=8E=A5=E5=8F=A3=E8=BD=AC?= =?UTF-8?q?=E5=8F=91=20POP=20=E5=AE=A1=E6=A0=B8=E7=8A=B6=E6=80=81=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 POST /external/pdd/goods/latest-commit-status,调用 pdd.goods.latest.commit.status.get; 请求体含 pddGoodsIds 与 pddPopAuth,与 detail 接口鉴权形态一致。 Made-with: Cursor --- .../ExternalPddGoodsController.java | 28 +++++ ...rnalPddGoodsLatestCommitStatusRequest.java | 18 +++ ...lPddGoodsLatestCommitStatusAppService.java | 12 ++ ...GoodsLatestCommitStatusAppServiceImpl.java | 110 ++++++++++++++++++ .../external/pdd/PddOpenApiSupport.java | 21 ++++ 5 files changed, 189 insertions(+) create mode 100644 model/src/main/java/cn/qihangerp/model/request/ExternalPddGoodsLatestCommitStatusRequest.java create mode 100644 service/src/main/java/cn/qihangerp/service/external/ExternalPddGoodsLatestCommitStatusAppService.java create mode 100644 service/src/main/java/cn/qihangerp/service/external/impl/ExternalPddGoodsLatestCommitStatusAppServiceImpl.java diff --git a/api/erp-api/src/main/java/cn/qihangerp/erp/controller/ExternalPddGoodsController.java b/api/erp-api/src/main/java/cn/qihangerp/erp/controller/ExternalPddGoodsController.java index 28caa501..0b175add 100644 --- a/api/erp-api/src/main/java/cn/qihangerp/erp/controller/ExternalPddGoodsController.java +++ b/api/erp-api/src/main/java/cn/qihangerp/erp/controller/ExternalPddGoodsController.java @@ -2,11 +2,13 @@ package cn.qihangerp.erp.controller; import cn.qihangerp.common.AjaxResult; import cn.qihangerp.model.request.ExternalPddGoodsDetailRequest; +import cn.qihangerp.model.request.ExternalPddGoodsLatestCommitStatusRequest; import cn.qihangerp.model.vo.ExternalPddGoodsDetailResultVo; import cn.qihangerp.security.common.BaseController; import cn.qihangerp.erp.config.ExternalGoodsApiLogProperties; import cn.qihangerp.erp.support.ExternalGoodsRequestLogSupport; import cn.qihangerp.service.external.ExternalPddGoodsDetailAppService; +import cn.qihangerp.service.external.ExternalPddGoodsLatestCommitStatusAppService; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; @@ -19,6 +21,7 @@ import org.springframework.web.bind.annotation.RestController; * 拼多多专用外部接口(与 {@code /external/goods/upsert} 编排解耦)。 * *

改库存请使用 {@code POST /external/goods/pdd/quantity/update}(与 upsert 相同 out 键)。

*/ @@ -29,6 +32,7 @@ import org.springframework.web.bind.annotation.RestController; public class ExternalPddGoodsController extends BaseController { private final ExternalPddGoodsDetailAppService externalPddGoodsDetailAppService; + private final ExternalPddGoodsLatestCommitStatusAppService externalPddGoodsLatestCommitStatusAppService; private final ExternalGoodsApiLogProperties goodsApiLogProperties; @PostMapping("/detail") @@ -53,4 +57,28 @@ public class ExternalPddGoodsController extends BaseController { } return AjaxResult.success(vo); } + + @PostMapping("/latest-commit-status") + public AjaxResult latestCommitStatus(@RequestBody ExternalPddGoodsLatestCommitStatusRequest req) { + if (goodsApiLogProperties.isLogFullRequest()) { + log.info("[external/pdd/goods/latest-commit-status] goodsIdCount={}", + req != null && req.getPddGoodsIds() != null ? req.getPddGoodsIds().size() : 0); + } + if (req == null || req.getPddGoodsIds() == null || req.getPddGoodsIds().isEmpty()) { + return AjaxResult.error("参数错误:pddGoodsIds 不能为空"); + } + if (req.getPddPopAuth() == null) { + return AjaxResult.error("参数错误:pddPopAuth 不能为空"); + } + var auth = req.getPddPopAuth(); + if (!StringUtils.hasText(auth.getAppKey()) || !StringUtils.hasText(auth.getAppSecret()) + || !StringUtils.hasText(auth.getAccessToken())) { + return AjaxResult.error("参数错误:pddPopAuth 需提供 appKey、appSecret、accessToken"); + } + ExternalPddGoodsDetailResultVo vo = externalPddGoodsLatestCommitStatusAppService.fetchLatestCommitStatus(req); + if (!Boolean.TRUE.equals(vo.getPopBizSuccess())) { + return AjaxResult.error(StringUtils.hasText(vo.getMessage()) ? vo.getMessage() : "pdd.goods.latest.commit.status.get 失败"); + } + return AjaxResult.success(vo); + } } diff --git a/model/src/main/java/cn/qihangerp/model/request/ExternalPddGoodsLatestCommitStatusRequest.java b/model/src/main/java/cn/qihangerp/model/request/ExternalPddGoodsLatestCommitStatusRequest.java new file mode 100644 index 00000000..074152fb --- /dev/null +++ b/model/src/main/java/cn/qihangerp/model/request/ExternalPddGoodsLatestCommitStatusRequest.java @@ -0,0 +1,18 @@ +package cn.qihangerp.model.request; + +import lombok.Data; + +import java.util.List; + +/** + * {@code POST /external/pdd/goods/latest-commit-status}:转发 {@code pdd.goods.latest.commit.status.get}。 + *

凭证形态与 {@link ExternalPddGoodsDetailRequest#getPddPopAuth()} 一致。

+ */ +@Data +public class ExternalPddGoodsLatestCommitStatusRequest { + + /** 待查询的拼多多 {@code goods_id} 列表(将序列化为 POP 顶层 {@code goods_id_list}) */ + private List pddGoodsIds; + + private ExternalGoodsUpsertRequest.PddPopAuth pddPopAuth; +} diff --git a/service/src/main/java/cn/qihangerp/service/external/ExternalPddGoodsLatestCommitStatusAppService.java b/service/src/main/java/cn/qihangerp/service/external/ExternalPddGoodsLatestCommitStatusAppService.java new file mode 100644 index 00000000..1f6077a8 --- /dev/null +++ b/service/src/main/java/cn/qihangerp/service/external/ExternalPddGoodsLatestCommitStatusAppService.java @@ -0,0 +1,12 @@ +package cn.qihangerp.service.external; + +import cn.qihangerp.model.request.ExternalPddGoodsLatestCommitStatusRequest; +import cn.qihangerp.model.vo.ExternalPddGoodsDetailResultVo; + +/** + * 拼多多商品最新提交审核状态({@code pdd.goods.latest.commit.status.get}),薄封装,不做业务状态机。 + */ +public interface ExternalPddGoodsLatestCommitStatusAppService { + + ExternalPddGoodsDetailResultVo fetchLatestCommitStatus(ExternalPddGoodsLatestCommitStatusRequest req); +} diff --git a/service/src/main/java/cn/qihangerp/service/external/impl/ExternalPddGoodsLatestCommitStatusAppServiceImpl.java b/service/src/main/java/cn/qihangerp/service/external/impl/ExternalPddGoodsLatestCommitStatusAppServiceImpl.java new file mode 100644 index 00000000..092c2606 --- /dev/null +++ b/service/src/main/java/cn/qihangerp/service/external/impl/ExternalPddGoodsLatestCommitStatusAppServiceImpl.java @@ -0,0 +1,110 @@ +package cn.qihangerp.service.external.impl; + +import cn.qihangerp.model.request.ExternalGoodsUpsertRequest; +import cn.qihangerp.model.request.ExternalPddGoodsLatestCommitStatusRequest; +import cn.qihangerp.model.vo.ExternalPddGoodsDetailResultVo; +import cn.qihangerp.service.external.ExternalPddGoodsLatestCommitStatusAppService; +import cn.qihangerp.service.external.pdd.ExternalPddProperties; +import cn.qihangerp.service.external.pdd.PddOpenApiSupport; +import cn.qihangerp.service.external.pdd.PddPopClient; +import cn.qihangerp.service.external.shop.PddShopCredential; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import java.util.List; + +/** + * 仅调用 POP,返回原始 JSON 供主数据解析 {@code goods_latest_commit_status_get_response}。 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class ExternalPddGoodsLatestCommitStatusAppServiceImpl implements ExternalPddGoodsLatestCommitStatusAppService { + + private final ExternalPddProperties props; + private final PddPopClient pddPopClient; + + @Override + public ExternalPddGoodsDetailResultVo fetchLatestCommitStatus(ExternalPddGoodsLatestCommitStatusRequest req) { + if (req == null || CollectionUtils.isEmpty(req.getPddGoodsIds())) { + return ExternalPddGoodsDetailResultVo.builder() + .popBizSuccess(false) + .message("pddGoodsIds 不能为空") + .popResponseBody(null) + .build(); + } + PddShopCredential cred = resolveCredential(req); + if (cred == null || !StringUtils.hasText(cred.getAppKey()) || !StringUtils.hasText(cred.getAppSecret()) + || !StringUtils.hasText(cred.getAccessToken())) { + return ExternalPddGoodsDetailResultVo.builder() + .popBizSuccess(false) + .message("pddPopAuth 不完整:需 appKey、appSecret、accessToken") + .popResponseBody(null) + .build(); + } + String gateway = StringUtils.hasText(cred.getGatewayUrl()) ? cred.getGatewayUrl() : props.getGatewayUrl(); + if (!StringUtils.hasText(gateway)) { + return ExternalPddGoodsDetailResultVo.builder() + .popBizSuccess(false) + .message("未配置 POP gatewayUrl(请求体或 external.pdd.gateway-url)") + .popResponseBody(null) + .build(); + } + List ids = req.getPddGoodsIds().stream().filter(id -> id != null && id > 0).toList(); + if (ids.isEmpty()) { + return ExternalPddGoodsDetailResultVo.builder() + .popBizSuccess(false) + .message("pddGoodsIds 无有效正数") + .popResponseBody(null) + .build(); + } + try { + String raw = pddPopClient.invokeTopLevelBiz( + gateway, + cred.getAppKey(), + cred.getAppSecret(), + cred.getAccessToken(), + "pdd.goods.latest.commit.status.get", + PddOpenApiSupport.goodsLatestCommitStatusGetTopLevelParams(ids)); + boolean ok = !PddOpenApiSupport.isError(raw); + return ExternalPddGoodsDetailResultVo.builder() + .popBizSuccess(ok) + .message(ok ? "pdd.goods.latest.commit.status.get 调用成功" : ("pdd.goods.latest.commit.status.get 失败: " + PddOpenApiSupport.formatError(raw))) + .popResponseBody(raw) + .build(); + } catch (Exception e) { + log.warn("[PDD] pdd.goods.latest.commit.status.get exception err={}", e.getMessage(), e); + return ExternalPddGoodsDetailResultVo.builder() + .popBizSuccess(false) + .message(e.getMessage()) + .popResponseBody(null) + .build(); + } + } + + private static PddShopCredential resolveCredential(ExternalPddGoodsLatestCommitStatusRequest req) { + if (req == null || req.getPddPopAuth() == null) { + return null; + } + ExternalGoodsUpsertRequest.PddPopAuth a = req.getPddPopAuth(); + PddShopCredential c = new PddShopCredential(); + c.setAppKey(trimToNull(a.getAppKey())); + c.setAppSecret(trimToNull(a.getAppSecret())); + c.setAccessToken(trimToNull(a.getAccessToken())); + if (StringUtils.hasText(a.getGatewayUrl())) { + c.setGatewayUrl(a.getGatewayUrl().trim()); + } + c.setSource("REQUEST"); + return c; + } + + private static String trimToNull(String s) { + if (!StringUtils.hasText(s)) { + return null; + } + return s.trim(); + } +} diff --git a/service/src/main/java/cn/qihangerp/service/external/pdd/PddOpenApiSupport.java b/service/src/main/java/cn/qihangerp/service/external/pdd/PddOpenApiSupport.java index b7e20430..19f1d1e0 100644 --- a/service/src/main/java/cn/qihangerp/service/external/pdd/PddOpenApiSupport.java +++ b/service/src/main/java/cn/qihangerp/service/external/pdd/PddOpenApiSupport.java @@ -51,6 +51,27 @@ public final class PddOpenApiSupport { return m; } + /** + * {@code pdd.goods.latest.commit.status.get}:与官方 POP 一致,业务参数在表单顶层;{@code goods_id_list} 为 JSON 数组字符串(如 {@code [123,456]})。 + */ + public static Map goodsLatestCommitStatusGetTopLevelParams(List goodsIds) { + if (goodsIds == null || goodsIds.isEmpty()) { + throw new IllegalArgumentException("goods_id_list 不能为空"); + } + List sanitized = new ArrayList<>(); + for (Long id : goodsIds) { + if (id != null && id > 0) { + sanitized.add(id); + } + } + if (sanitized.isEmpty()) { + throw new IllegalArgumentException("goods_id_list 无有效 goods_id"); + } + Map m = new LinkedHashMap<>(); + m.put("goods_id_list", JSON.toJSONString(sanitized)); + return m; + } + /** * {@code pdd.goods.quantity.update} 表单顶层参数(与 POP 官方 curl / Java SDK 一致:{@code goods_id}、{@code sku_id}、{@code quantity}、 * {@code update_type}、{@code force_update}、可选 {@code outer_id})。