diff --git a/api/erp-api/src/main/java/cn/qihangerp/erp/controller/ExternalGoodsController.java b/api/erp-api/src/main/java/cn/qihangerp/erp/controller/ExternalGoodsController.java index 892f627b..2e05d540 100644 --- a/api/erp-api/src/main/java/cn/qihangerp/erp/controller/ExternalGoodsController.java +++ b/api/erp-api/src/main/java/cn/qihangerp/erp/controller/ExternalGoodsController.java @@ -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); } diff --git a/service/src/main/java/cn/qihangerp/service/external/impl/ExternalGoodsAppServiceImpl.java b/service/src/main/java/cn/qihangerp/service/external/impl/ExternalGoodsAppServiceImpl.java index 8efb363f..ade225a9 100644 --- a/service/src/main/java/cn/qihangerp/service/external/impl/ExternalGoodsAppServiceImpl.java +++ b/service/src/main/java/cn/qihangerp/service/external/impl/ExternalGoodsAppServiceImpl.java @@ -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 diff --git a/service/src/main/java/cn/qihangerp/service/external/pdd/ExternalPddPublishService.java b/service/src/main/java/cn/qihangerp/service/external/pdd/ExternalPddPublishService.java index 3e18a6c0..88088755 100644 --- a/service/src/main/java/cn/qihangerp/service/external/pdd/ExternalPddPublishService.java +++ b/service/src/main/java/cn/qihangerp/service/external/pdd/ExternalPddPublishService.java @@ -36,6 +36,9 @@ public class ExternalPddPublishService { */ public PddPublishLaneResultVo publish(OGoods goods, List 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) diff --git a/service/src/main/java/cn/qihangerp/service/external/pdd/PddPopClient.java b/service/src/main/java/cn/qihangerp/service/external/pdd/PddPopClient.java index f0b01f5b..55533a15 100644 --- a/service/src/main/java/cn/qihangerp/service/external/pdd/PddPopClient.java +++ b/service/src/main/java/cn/qihangerp/service/external/pdd/PddPopClient.java @@ -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)。 + *

每次请求无论成功失败均打日志(含 HTTP 状态、耗时、响应片段;client_id 脱敏)。

* * @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 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 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 params) {