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);
|
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);
|
return AjaxResult.success(vo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import com.alibaba.fastjson2.JSON;
|
||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
@ -32,6 +33,7 @@ import java.util.Objects;
|
||||||
/**
|
/**
|
||||||
* @author guochengyu
|
* @author guochengyu
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Service
|
@Service
|
||||||
public class ExternalGoodsAppServiceImpl implements ExternalGoodsAppService {
|
public class ExternalGoodsAppServiceImpl implements ExternalGoodsAppService {
|
||||||
|
|
@ -142,7 +144,30 @@ public class ExternalGoodsAppServiceImpl implements ExternalGoodsAppService {
|
||||||
out.pddAutoResolveDetail(lane.getAutoResolveDetail());
|
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
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,9 @@ public class ExternalPddPublishService {
|
||||||
*/
|
*/
|
||||||
public PddPublishLaneResultVo publish(OGoods goods, List<OGoodsSku> skus, ExternalGoodsUpsertRequest req) {
|
public PddPublishLaneResultVo publish(OGoods goods, List<OGoodsSku> skus, ExternalGoodsUpsertRequest req) {
|
||||||
if (!props.isPublishEnabled()) {
|
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()
|
return PddPublishLaneResultVo.builder()
|
||||||
.attempted(false)
|
.attempted(false)
|
||||||
.success(null)
|
.success(null)
|
||||||
|
|
@ -44,6 +47,7 @@ public class ExternalPddPublishService {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req == null) {
|
if (req == null) {
|
||||||
|
log.info("[PDD] publish skipped shopId=null outGoodsId=null reason=req-null");
|
||||||
return PddPublishLaneResultVo.builder()
|
return PddPublishLaneResultVo.builder()
|
||||||
.attempted(false)
|
.attempted(false)
|
||||||
.success(false)
|
.success(false)
|
||||||
|
|
@ -55,6 +59,8 @@ public class ExternalPddPublishService {
|
||||||
PddShopCredential cred = resolveCredentialFromRequest(req);
|
PddShopCredential cred = resolveCredentialFromRequest(req);
|
||||||
if (cred == null || !StringUtils.hasText(cred.getAppKey()) || !StringUtils.hasText(cred.getAppSecret())
|
if (cred == null || !StringUtils.hasText(cred.getAppKey()) || !StringUtils.hasText(cred.getAppSecret())
|
||||||
|| !StringUtils.hasText(cred.getAccessToken())) {
|
|| !StringUtils.hasText(cred.getAccessToken())) {
|
||||||
|
log.info("[PDD] publish skipped shopId={} outGoodsId={} reason=pddPopAuth-incomplete",
|
||||||
|
req.getShopId(), req.getOutGoodsId());
|
||||||
return PddPublishLaneResultVo.builder()
|
return PddPublishLaneResultVo.builder()
|
||||||
.attempted(false)
|
.attempted(false)
|
||||||
.success(false)
|
.success(false)
|
||||||
|
|
@ -75,6 +81,8 @@ public class ExternalPddPublishService {
|
||||||
|
|
||||||
Long catId = paramBuilder.resolveCatIdForPublish(req.getCategoryCode(), props);
|
Long catId = paramBuilder.resolveCatIdForPublish(req.getCategoryCode(), props);
|
||||||
if (catId == null || catId <= 0) {
|
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()
|
return PddPublishLaneResultVo.builder()
|
||||||
.attempted(false)
|
.attempted(false)
|
||||||
.success(false)
|
.success(false)
|
||||||
|
|
@ -100,6 +108,8 @@ public class ExternalPddPublishService {
|
||||||
effectiveOverrides = ar.getOverrides();
|
effectiveOverrides = ar.getOverrides();
|
||||||
specAuto = true;
|
specAuto = true;
|
||||||
} else {
|
} else {
|
||||||
|
log.info("[PDD] publish skipped shopId={} outGoodsId={} reason=spec-auto-resolve-fail detail={}",
|
||||||
|
req.getShopId(), req.getOutGoodsId(), autoDetail);
|
||||||
return PddPublishLaneResultVo.builder()
|
return PddPublishLaneResultVo.builder()
|
||||||
.attempted(false)
|
.attempted(false)
|
||||||
.success(false)
|
.success(false)
|
||||||
|
|
@ -122,18 +132,29 @@ public class ExternalPddPublishService {
|
||||||
catRuleSnippet = PddOpenApiSupport.snippet(raw, 2000);
|
catRuleSnippet = PddOpenApiSupport.snippet(raw, 2000);
|
||||||
catFetched = true;
|
catFetched = true;
|
||||||
} catch (Exception e) {
|
} 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();
|
catRuleSnippet = "ERROR: " + e.getMessage();
|
||||||
catFetched = true;
|
catFetched = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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 paramJson = paramBuilder.buildParamJson(goods, skus, req, props, effectiveOverrides);
|
||||||
String raw = pddPopClient.invoke(gateway, cred.getAppKey(), cred.getAppSecret(), cred.getAccessToken(),
|
String raw = pddPopClient.invoke(gateway, cred.getAppKey(), cred.getAppSecret(), cred.getAccessToken(),
|
||||||
"pdd.goods.add", paramJson);
|
"pdd.goods.add", paramJson);
|
||||||
boolean ok = !PddOpenApiSupport.isError(raw);
|
boolean ok = !PddOpenApiSupport.isError(raw);
|
||||||
String errMsg = ok ? null : PddOpenApiSupport.formatError(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()
|
return PddPublishLaneResultVo.builder()
|
||||||
.attempted(true)
|
.attempted(true)
|
||||||
.success(ok)
|
.success(ok)
|
||||||
|
|
@ -146,7 +167,7 @@ public class ExternalPddPublishService {
|
||||||
.autoResolveDetail(autoDetail)
|
.autoResolveDetail(autoDetail)
|
||||||
.build();
|
.build();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("拼多多发布失败: {}", e.getMessage());
|
log.warn("拼多多发布异常 shopId={} outGoodsId={} err={}", req.getShopId(), req.getOutGoodsId(), e.getMessage(), e);
|
||||||
return PddPublishLaneResultVo.builder()
|
return PddPublishLaneResultVo.builder()
|
||||||
.attempted(true)
|
.attempted(true)
|
||||||
.success(false)
|
.success(false)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package cn.qihangerp.service.external.pdd;
|
package cn.qihangerp.service.external.pdd;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
|
@ -15,9 +16,11 @@ import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 拼多多 POP HTTP 调用(application/x-www-form-urlencoded)。
|
* 拼多多 POP HTTP 调用(application/x-www-form-urlencoded)。
|
||||||
|
* <p>每次请求无论成功失败均打日志(含 HTTP 状态、耗时、响应片段;client_id 脱敏)。</p>
|
||||||
*
|
*
|
||||||
* @author guochengyu
|
* @author guochengyu
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
public class PddPopClient {
|
public class PddPopClient {
|
||||||
|
|
||||||
|
|
@ -57,8 +60,45 @@ public class PddPopClient {
|
||||||
.header("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8")
|
.header("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8")
|
||||||
.POST(HttpRequest.BodyPublishers.ofString(body, StandardCharsets.UTF_8))
|
.POST(HttpRequest.BodyPublishers.ofString(body, StandardCharsets.UTF_8))
|
||||||
.build();
|
.build();
|
||||||
|
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));
|
HttpResponse<String> resp = httpClient.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
|
||||||
return resp.body();
|
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) {
|
private static String formEncode(Map<String, String> params) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue