feat(external): 拼多多 POP 与 upsert 全链路日志(成功/失败均记录)
Made-with: Cursor
This commit is contained in:
parent
dbc5f96768
commit
d1cf874ab8
|
|
@ -92,6 +92,12 @@ public class ExternalGoodsController extends BaseController {
|
|||
}
|
||||
|
||||
ExternalGoodsUpsertResultVo vo = externalGoodsAppService.upsertGoods(req);
|
||||
log.info("[external/goods/upsert] response shopId={} outGoodsId={} erpGoodsId={} platform={}",
|
||||
req.getShopId(), req.getOutGoodsId(), vo.getGoodsId(), req.getPlatform());
|
||||
if (EnumShopType.PDD.equals(platform)) {
|
||||
log.info("[external/goods/upsert] pddPublish attempted={} success={}",
|
||||
vo.getPddPublishAttempted(), vo.getPddPublishSuccess());
|
||||
}
|
||||
return AjaxResult.success(vo);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import com.alibaba.fastjson2.JSON;
|
|||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
|
@ -32,6 +33,7 @@ import java.util.Objects;
|
|||
/**
|
||||
* @author guochengyu
|
||||
*/
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
@Service
|
||||
public class ExternalGoodsAppServiceImpl implements ExternalGoodsAppService {
|
||||
|
|
@ -142,7 +144,30 @@ public class ExternalGoodsAppServiceImpl implements ExternalGoodsAppService {
|
|||
out.pddAutoResolveDetail(lane.getAutoResolveDetail());
|
||||
}
|
||||
|
||||
return out.build();
|
||||
ExternalGoodsUpsertResultVo vo = out.build();
|
||||
if ("PDD".equalsIgnoreCase(req.getPlatform())) {
|
||||
log.info("[external/upsert] PDD summary shopId={} outGoodsId={} erpGoodsId={} attempted={} success={} credentialSource={} message={} goodsAddSnippet={} catRuleFetched={} catRuleSnippet={} specAutoResolved={} autoResolveDetail={}",
|
||||
req.getShopId(),
|
||||
req.getOutGoodsId(),
|
||||
vo.getGoodsId(),
|
||||
vo.getPddPublishAttempted(),
|
||||
vo.getPddPublishSuccess(),
|
||||
vo.getShopCredentialSource(),
|
||||
truncateForLog(vo.getPddPublishMessage(), 600),
|
||||
truncateForLog(vo.getPddResponseSnippet(), 500),
|
||||
vo.getPddCatRuleFetched(),
|
||||
truncateForLog(vo.getPddCatRuleSnippet(), 300),
|
||||
vo.getPddSpecAutoResolved(),
|
||||
truncateForLog(vo.getPddAutoResolveDetail(), 300));
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
|
||||
private static String truncateForLog(String s, int max) {
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
return s.length() <= max ? s : s.substring(0, max) + "...";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -36,6 +36,9 @@ public class ExternalPddPublishService {
|
|||
*/
|
||||
public PddPublishLaneResultVo publish(OGoods goods, List<OGoodsSku> skus, ExternalGoodsUpsertRequest req) {
|
||||
if (!props.isPublishEnabled()) {
|
||||
log.info("[PDD] publish skipped shopId={} outGoodsId={} reason=publish-disabled",
|
||||
req != null ? req.getShopId() : null,
|
||||
req != null ? req.getOutGoodsId() : null);
|
||||
return PddPublishLaneResultVo.builder()
|
||||
.attempted(false)
|
||||
.success(null)
|
||||
|
|
@ -44,6 +47,7 @@ public class ExternalPddPublishService {
|
|||
}
|
||||
|
||||
if (req == null) {
|
||||
log.info("[PDD] publish skipped shopId=null outGoodsId=null reason=req-null");
|
||||
return PddPublishLaneResultVo.builder()
|
||||
.attempted(false)
|
||||
.success(false)
|
||||
|
|
@ -55,6 +59,8 @@ public class ExternalPddPublishService {
|
|||
PddShopCredential cred = resolveCredentialFromRequest(req);
|
||||
if (cred == null || !StringUtils.hasText(cred.getAppKey()) || !StringUtils.hasText(cred.getAppSecret())
|
||||
|| !StringUtils.hasText(cred.getAccessToken())) {
|
||||
log.info("[PDD] publish skipped shopId={} outGoodsId={} reason=pddPopAuth-incomplete",
|
||||
req.getShopId(), req.getOutGoodsId());
|
||||
return PddPublishLaneResultVo.builder()
|
||||
.attempted(false)
|
||||
.success(false)
|
||||
|
|
@ -75,6 +81,8 @@ public class ExternalPddPublishService {
|
|||
|
||||
Long catId = paramBuilder.resolveCatIdForPublish(req.getCategoryCode(), props);
|
||||
if (catId == null || catId <= 0) {
|
||||
log.info("[PDD] publish skipped shopId={} outGoodsId={} reason=category-map-missing categoryCode={}",
|
||||
req.getShopId(), req.getOutGoodsId(), req.getCategoryCode());
|
||||
return PddPublishLaneResultVo.builder()
|
||||
.attempted(false)
|
||||
.success(false)
|
||||
|
|
@ -100,6 +108,8 @@ public class ExternalPddPublishService {
|
|||
effectiveOverrides = ar.getOverrides();
|
||||
specAuto = true;
|
||||
} else {
|
||||
log.info("[PDD] publish skipped shopId={} outGoodsId={} reason=spec-auto-resolve-fail detail={}",
|
||||
req.getShopId(), req.getOutGoodsId(), autoDetail);
|
||||
return PddPublishLaneResultVo.builder()
|
||||
.attempted(false)
|
||||
.success(false)
|
||||
|
|
@ -122,18 +132,29 @@ public class ExternalPddPublishService {
|
|||
catRuleSnippet = PddOpenApiSupport.snippet(raw, 2000);
|
||||
catFetched = true;
|
||||
} catch (Exception e) {
|
||||
log.warn("pdd.goods.cat.rule.get 失败: {}", e.getMessage());
|
||||
log.warn("pdd.goods.cat.rule.get shopId={} outGoodsId={} failed: {}",
|
||||
req.getShopId(), req.getOutGoodsId(), e.getMessage());
|
||||
catRuleSnippet = "ERROR: " + e.getMessage();
|
||||
catFetched = true;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("[PDD] pdd.goods.add begin shopId={} outGoodsId={} catId={} skuCount={}",
|
||||
req.getShopId(), req.getOutGoodsId(), catId, skuRows.size());
|
||||
String paramJson = paramBuilder.buildParamJson(goods, skus, req, props, effectiveOverrides);
|
||||
String raw = pddPopClient.invoke(gateway, cred.getAppKey(), cred.getAppSecret(), cred.getAccessToken(),
|
||||
"pdd.goods.add", paramJson);
|
||||
boolean ok = !PddOpenApiSupport.isError(raw);
|
||||
String errMsg = ok ? null : PddOpenApiSupport.formatError(raw);
|
||||
if (ok) {
|
||||
log.info("[PDD] pdd.goods.add end shopId={} outGoodsId={} success=true msg={} snippet={}",
|
||||
req.getShopId(), req.getOutGoodsId(), "pdd.goods.add 调用成功",
|
||||
PddOpenApiSupport.snippet(raw, 800));
|
||||
} else {
|
||||
log.warn("[PDD] pdd.goods.add end shopId={} outGoodsId={} success=false err={} snippet={}",
|
||||
req.getShopId(), req.getOutGoodsId(), errMsg, PddOpenApiSupport.snippet(raw, 800));
|
||||
}
|
||||
return PddPublishLaneResultVo.builder()
|
||||
.attempted(true)
|
||||
.success(ok)
|
||||
|
|
@ -146,7 +167,7 @@ public class ExternalPddPublishService {
|
|||
.autoResolveDetail(autoDetail)
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
log.warn("拼多多发布失败: {}", e.getMessage());
|
||||
log.warn("拼多多发布异常 shopId={} outGoodsId={} err={}", req.getShopId(), req.getOutGoodsId(), e.getMessage(), e);
|
||||
return PddPublishLaneResultVo.builder()
|
||||
.attempted(true)
|
||||
.success(false)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package cn.qihangerp.service.external.pdd;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
|
@ -15,9 +16,11 @@ import java.util.Map;
|
|||
|
||||
/**
|
||||
* 拼多多 POP HTTP 调用(application/x-www-form-urlencoded)。
|
||||
* <p>每次请求无论成功失败均打日志(含 HTTP 状态、耗时、响应片段;client_id 脱敏)。</p>
|
||||
*
|
||||
* @author guochengyu
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class PddPopClient {
|
||||
|
||||
|
|
@ -57,8 +60,45 @@ public class PddPopClient {
|
|||
.header("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8")
|
||||
.POST(HttpRequest.BodyPublishers.ofString(body, StandardCharsets.UTF_8))
|
||||
.build();
|
||||
HttpResponse<String> resp = httpClient.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
|
||||
return resp.body();
|
||||
String host = safeHostForLog(gatewayUrl);
|
||||
String clientMasked = PddSensitiveLogUtil.maskForLog(clientId);
|
||||
long t0 = System.nanoTime();
|
||||
try {
|
||||
HttpResponse<String> resp = httpClient.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
|
||||
long durationMs = (System.nanoTime() - t0) / 1_000_000L;
|
||||
int httpStatus = resp.statusCode();
|
||||
String raw = resp.body();
|
||||
boolean httpOk = httpStatus >= 200 && httpStatus < 300;
|
||||
boolean popBizError = PddOpenApiSupport.isError(raw);
|
||||
String snippet = PddOpenApiSupport.snippet(raw, 600);
|
||||
if (!httpOk || popBizError) {
|
||||
String errSummary = popBizError ? PddOpenApiSupport.formatError(raw) : "";
|
||||
log.warn("PDD_POP api={} host={} clientId={} httpStatus={} durationMs={} popBizError={} errSummary={} bodySnippet={}",
|
||||
type, host, clientMasked, httpStatus, durationMs, popBizError, errSummary, snippet);
|
||||
} else {
|
||||
log.info("PDD_POP api={} host={} clientId={} httpStatus={} durationMs={} bodySnippet={}",
|
||||
type, host, clientMasked, httpStatus, durationMs, snippet);
|
||||
}
|
||||
return raw;
|
||||
} catch (Exception e) {
|
||||
long durationMs = (System.nanoTime() - t0) / 1_000_000L;
|
||||
log.warn("PDD_POP api={} host={} clientId={} durationMs={} invokeFailed={}",
|
||||
type, host, clientMasked, durationMs, e.toString(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private static String safeHostForLog(String gatewayUrl) {
|
||||
if (!StringUtils.hasText(gatewayUrl)) {
|
||||
return "";
|
||||
}
|
||||
try {
|
||||
URI u = URI.create(gatewayUrl.trim());
|
||||
String h = u.getHost();
|
||||
return h != null ? h : gatewayUrl.trim();
|
||||
} catch (Exception e) {
|
||||
return gatewayUrl.trim();
|
||||
}
|
||||
}
|
||||
|
||||
private static String formEncode(Map<String, String> params) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue