feat(pdd): 增加 latest-commit-status 外部接口转发 POP 审核状态查询

新增 POST /external/pdd/goods/latest-commit-status,调用 pdd.goods.latest.commit.status.get;
请求体含 pddGoodsIds 与 pddPopAuth,与 detail 接口鉴权形态一致。

Made-with: Cursor
This commit is contained in:
huangyujie 2026-04-10 19:21:33 +08:00
parent e8565b4392
commit 29ccdd6ea0
5 changed files with 189 additions and 0 deletions

View File

@ -2,11 +2,13 @@ package cn.qihangerp.erp.controller;
import cn.qihangerp.common.AjaxResult; import cn.qihangerp.common.AjaxResult;
import cn.qihangerp.model.request.ExternalPddGoodsDetailRequest; import cn.qihangerp.model.request.ExternalPddGoodsDetailRequest;
import cn.qihangerp.model.request.ExternalPddGoodsLatestCommitStatusRequest;
import cn.qihangerp.model.vo.ExternalPddGoodsDetailResultVo; import cn.qihangerp.model.vo.ExternalPddGoodsDetailResultVo;
import cn.qihangerp.security.common.BaseController; import cn.qihangerp.security.common.BaseController;
import cn.qihangerp.erp.config.ExternalGoodsApiLogProperties; import cn.qihangerp.erp.config.ExternalGoodsApiLogProperties;
import cn.qihangerp.erp.support.ExternalGoodsRequestLogSupport; import cn.qihangerp.erp.support.ExternalGoodsRequestLogSupport;
import cn.qihangerp.service.external.ExternalPddGoodsDetailAppService; import cn.qihangerp.service.external.ExternalPddGoodsDetailAppService;
import cn.qihangerp.service.external.ExternalPddGoodsLatestCommitStatusAppService;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -19,6 +21,7 @@ import org.springframework.web.bind.annotation.RestController;
* 拼多多专用外部接口 {@code /external/goods/upsert} 编排解耦 * 拼多多专用外部接口 {@code /external/goods/upsert} 编排解耦
* <ul> * <ul>
* <li>{@code POST /external/pdd/goods/detail} {@code pdd.goods.detail.get}</li> * <li>{@code POST /external/pdd/goods/detail} {@code pdd.goods.detail.get}</li>
* <li>{@code POST /external/pdd/goods/latest-commit-status} {@code pdd.goods.latest.commit.status.get}</li>
* </ul> * </ul>
* <p>改库存请使用 {@code POST /external/goods/pdd/quantity/update} upsert 相同 out </p> * <p>改库存请使用 {@code POST /external/goods/pdd/quantity/update} upsert 相同 out </p>
*/ */
@ -29,6 +32,7 @@ import org.springframework.web.bind.annotation.RestController;
public class ExternalPddGoodsController extends BaseController { public class ExternalPddGoodsController extends BaseController {
private final ExternalPddGoodsDetailAppService externalPddGoodsDetailAppService; private final ExternalPddGoodsDetailAppService externalPddGoodsDetailAppService;
private final ExternalPddGoodsLatestCommitStatusAppService externalPddGoodsLatestCommitStatusAppService;
private final ExternalGoodsApiLogProperties goodsApiLogProperties; private final ExternalGoodsApiLogProperties goodsApiLogProperties;
@PostMapping("/detail") @PostMapping("/detail")
@ -53,4 +57,28 @@ public class ExternalPddGoodsController extends BaseController {
} }
return AjaxResult.success(vo); 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);
}
} }

View File

@ -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}
* <p>凭证形态与 {@link ExternalPddGoodsDetailRequest#getPddPopAuth()} 一致</p>
*/
@Data
public class ExternalPddGoodsLatestCommitStatusRequest {
/** 待查询的拼多多 {@code goods_id} 列表(将序列化为 POP 顶层 {@code goods_id_list} */
private List<Long> pddGoodsIds;
private ExternalGoodsUpsertRequest.PddPopAuth pddPopAuth;
}

View File

@ -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);
}

View File

@ -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<Long> 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();
}
}

View File

@ -51,6 +51,27 @@ public final class PddOpenApiSupport {
return m; return m;
} }
/**
* {@code pdd.goods.latest.commit.status.get}与官方 POP 一致业务参数在表单顶层{@code goods_id_list} JSON 数组字符串 {@code [123,456]}
*/
public static Map<String, String> goodsLatestCommitStatusGetTopLevelParams(List<Long> goodsIds) {
if (goodsIds == null || goodsIds.isEmpty()) {
throw new IllegalArgumentException("goods_id_list 不能为空");
}
List<Long> 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<String, String> 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 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} * {@code update_type}{@code force_update}可选 {@code outer_id}