feat(erp-open): add Weixin publish lane and platform rename
Introduce WEIXIN platform handling for external goods upsert with dedicated sphPopAuth validation and result fields, and add a placeholder Sph publish service behind external.sph.publish-enabled. Rename EnumShopType WEI to WEIXIN and update related references to keep internal channel behavior consistent. Made-with: Cursor
This commit is contained in:
parent
3b5ba130d5
commit
a4e3421a10
|
|
@ -13,6 +13,7 @@ import cn.qihangerp.erp.support.ExternalGoodsRequestLogSupport;
|
||||||
import cn.qihangerp.service.external.ExternalGoodsAppService;
|
import cn.qihangerp.service.external.ExternalGoodsAppService;
|
||||||
import cn.qihangerp.service.external.ExternalGoodsPddQuantityUpdateAppService;
|
import cn.qihangerp.service.external.ExternalGoodsPddQuantityUpdateAppService;
|
||||||
import cn.qihangerp.service.external.pdd.ExternalPddProperties;
|
import cn.qihangerp.service.external.pdd.ExternalPddProperties;
|
||||||
|
import cn.qihangerp.service.external.sph.ExternalSphProperties;
|
||||||
import com.alibaba.fastjson2.JSON;
|
import com.alibaba.fastjson2.JSON;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
@ -43,6 +44,7 @@ public class ExternalGoodsController extends BaseController {
|
||||||
private final ExternalGoodsAppService externalGoodsAppService;
|
private final ExternalGoodsAppService externalGoodsAppService;
|
||||||
private final ExternalGoodsPddQuantityUpdateAppService externalGoodsPddQuantityUpdateAppService;
|
private final ExternalGoodsPddQuantityUpdateAppService externalGoodsPddQuantityUpdateAppService;
|
||||||
private final ExternalPddProperties externalPddProperties;
|
private final ExternalPddProperties externalPddProperties;
|
||||||
|
private final ExternalSphProperties externalSphProperties;
|
||||||
private final ExternalGoodsApiLogProperties goodsApiLogProperties;
|
private final ExternalGoodsApiLogProperties goodsApiLogProperties;
|
||||||
|
|
||||||
@PostMapping("/upsert")
|
@PostMapping("/upsert")
|
||||||
|
|
@ -106,6 +108,17 @@ public class ExternalGoodsController extends BaseController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (EnumShopType.WEIXIN.equals(platform) && externalSphProperties.isPublishEnabled()) {
|
||||||
|
if (req.getSphPopAuth() == null) {
|
||||||
|
return AjaxResult.error("参数错误:开启微信视频号小店发布时 sphPopAuth 不能为空");
|
||||||
|
}
|
||||||
|
var sAuth = req.getSphPopAuth();
|
||||||
|
if (!StringUtils.hasText(sAuth.getAppKey()) || !StringUtils.hasText(sAuth.getAppSecret())
|
||||||
|
|| !StringUtils.hasText(sAuth.getAccessToken())) {
|
||||||
|
return AjaxResult.error("参数错误:sphPopAuth 需提供 appKey、appSecret、accessToken");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ExternalGoodsUpsertResultVo vo = externalGoodsAppService.upsertGoods(req);
|
ExternalGoodsUpsertResultVo vo = externalGoodsAppService.upsertGoods(req);
|
||||||
log.info("[external/goods/upsert] response shopId={} outGoodsId={} erpGoodsId={} platform={}",
|
log.info("[external/goods/upsert] response shopId={} outGoodsId={} erpGoodsId={} platform={}",
|
||||||
req.getShopId(), req.getOutGoodsId(), vo.getGoodsId(), req.getPlatform());
|
req.getShopId(), req.getOutGoodsId(), vo.getGoodsId(), req.getPlatform());
|
||||||
|
|
@ -115,6 +128,13 @@ public class ExternalGoodsController extends BaseController {
|
||||||
vo.getPddPublishSuccess(),
|
vo.getPddPublishSuccess(),
|
||||||
truncateLog(vo.getPddPublishMessage(), 800));
|
truncateLog(vo.getPddPublishMessage(), 800));
|
||||||
}
|
}
|
||||||
|
if (EnumShopType.WEIXIN.equals(platform)) {
|
||||||
|
log.info("[external/goods/upsert] sphPublish attempted={} success={} message={} sphProductId={}",
|
||||||
|
vo.getSphPublishAttempted(),
|
||||||
|
vo.getSphPublishSuccess(),
|
||||||
|
truncateLog(vo.getSphPublishMessage(), 800),
|
||||||
|
vo.getSphProductId());
|
||||||
|
}
|
||||||
return AjaxResult.success(vo);
|
return AjaxResult.success(vo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@
|
||||||
// } else if (mqMessage.getShopType().getIndex() == EnumShopType.OFFLINE.getIndex()) {
|
// } else if (mqMessage.getShopType().getIndex() == EnumShopType.OFFLINE.getIndex()) {
|
||||||
// log.info("订单消息OFFLINE");
|
// log.info("订单消息OFFLINE");
|
||||||
// orderService.offlineOrderMessage(mqMessage.getKeyId());
|
// orderService.offlineOrderMessage(mqMessage.getKeyId());
|
||||||
// } else if (mqMessage.getShopType().getIndex() == EnumShopType.WEI.getIndex()) {
|
// } else if (mqMessage.getShopType().getIndex() == EnumShopType.WEIXIN.getIndex()) {
|
||||||
// log.info("订单消息WEI");
|
// log.info("订单消息WEI");
|
||||||
// JSONObject jsonObject = openApiService.getWeiOrderDetail(mqMessage.getKeyId());
|
// JSONObject jsonObject = openApiService.getWeiOrderDetail(mqMessage.getKeyId());
|
||||||
// if (jsonObject.getInteger("code") != 200 || jsonObject.getJSONObject("data") == null) {
|
// if (jsonObject.getInteger("code") != 200 || jsonObject.getJSONObject("data") == null) {
|
||||||
|
|
@ -132,7 +132,7 @@
|
||||||
//
|
//
|
||||||
// JSONObject refundDetail = jsonObject.getJSONObject("data");
|
// JSONObject refundDetail = jsonObject.getJSONObject("data");
|
||||||
// refundService.douRefundMessage(mqMessage.getKeyId(), refundDetail);
|
// refundService.douRefundMessage(mqMessage.getKeyId(), refundDetail);
|
||||||
// } else if (mqMessage.getShopType().getIndex() == EnumShopType.WEI.getIndex()) {
|
// } else if (mqMessage.getShopType().getIndex() == EnumShopType.WEIXIN.getIndex()) {
|
||||||
// log.info("退款消息WEI");
|
// log.info("退款消息WEI");
|
||||||
// JSONObject jsonObject = openApiService.getWeiRefundDetail(mqMessage.getKeyId());
|
// JSONObject jsonObject = openApiService.getWeiRefundDetail(mqMessage.getKeyId());
|
||||||
// if (jsonObject.getInteger("code") != 200 || jsonObject.getJSONObject("data") == null) {
|
// if (jsonObject.getInteger("code") != 200 || jsonObject.getJSONObject("data") == null) {
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@
|
||||||
// } else if(vo.getShopType().getIndex() == EnumShopType.DOU.getIndex()) {
|
// } else if(vo.getShopType().getIndex() == EnumShopType.DOU.getIndex()) {
|
||||||
// logger.info("Kafka订单消息DOU"+vo.getKeyId());
|
// logger.info("Kafka订单消息DOU"+vo.getKeyId());
|
||||||
//// orderService.douOrderMessage(vo.getKeyId());
|
//// orderService.douOrderMessage(vo.getKeyId());
|
||||||
// } else if(vo.getShopType().getIndex() == EnumShopType.WEI.getIndex()) {
|
// } else if(vo.getShopType().getIndex() == EnumShopType.WEIXIN.getIndex()) {
|
||||||
// logger.info("Kafka订单消息WEI"+vo.getKeyId());
|
// logger.info("Kafka订单消息WEI"+vo.getKeyId());
|
||||||
//// orderService.weiOrderMessage(vo.getKeyId());
|
//// orderService.weiOrderMessage(vo.getKeyId());
|
||||||
// }
|
// }
|
||||||
|
|
@ -70,7 +70,7 @@
|
||||||
// } else if(vo.getShopType().getIndex() == EnumShopType.DOU.getIndex()) {
|
// } else if(vo.getShopType().getIndex() == EnumShopType.DOU.getIndex()) {
|
||||||
// logger.info("Kafka售后消息DOU"+vo.getKeyId());
|
// logger.info("Kafka售后消息DOU"+vo.getKeyId());
|
||||||
//// refundService.douRefundMessage(vo.getKeyId());
|
//// refundService.douRefundMessage(vo.getKeyId());
|
||||||
// } else if(vo.getShopType().getIndex() == EnumShopType.WEI.getIndex()) {
|
// } else if(vo.getShopType().getIndex() == EnumShopType.WEIXIN.getIndex()) {
|
||||||
// logger.info("Kafka售后消息WEI"+vo.getKeyId());
|
// logger.info("Kafka售后消息WEI"+vo.getKeyId());
|
||||||
//// refundService.weiRefundMessage(vo.getKeyId());
|
//// refundService.weiRefundMessage(vo.getKeyId());
|
||||||
// }
|
// }
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,10 @@ public final class ExternalGoodsRequestLogSupport {
|
||||||
if (auth != null && !auth.isEmpty()) {
|
if (auth != null && !auth.isEmpty()) {
|
||||||
maskPddPopAuth(auth);
|
maskPddPopAuth(auth);
|
||||||
}
|
}
|
||||||
|
JSONObject sphAuth = root.getJSONObject("sphPopAuth");
|
||||||
|
if (sphAuth != null && !sphAuth.isEmpty()) {
|
||||||
|
maskPddPopAuth(sphAuth);
|
||||||
|
}
|
||||||
return root.toJSONString();
|
return root.toJSONString();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return "{\"_logSanitizeError\":true}";
|
return "{\"_logSanitizeError\":true}";
|
||||||
|
|
|
||||||
|
|
@ -77,4 +77,8 @@ external:
|
||||||
DEFAULT: "303351846671360"
|
DEFAULT: "303351846671360"
|
||||||
sku-overrides: []
|
sku-overrides: []
|
||||||
|
|
||||||
|
# 微信视频号小店(platform=WEIXIN):默认不调用渠道发品;接入 OpenAPI 后再开启
|
||||||
|
sph:
|
||||||
|
publish-enabled: false
|
||||||
|
|
||||||
# 说明:对外商品接口见 temp/yunxi-erp-open-goods-upsert-api.md;Nacos 对齐见 temp/erp-open-erp-api-nacos-yunxi-reference.md
|
# 说明:对外商品接口见 temp/yunxi-erp-open-goods-upsert-api.md;Nacos 对齐见 temp/erp-open-erp-api-nacos-yunxi-reference.md
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ public class WeiApiCommon {
|
||||||
if (shop == null) {
|
if (shop == null) {
|
||||||
return ResultVo.error(HttpStatus.PARAMS_ERROR,"参数错误,没有找到店铺");
|
return ResultVo.error(HttpStatus.PARAMS_ERROR,"参数错误,没有找到店铺");
|
||||||
}
|
}
|
||||||
if (shop.getType() != EnumShopType.WEI.getIndex()) {
|
if (shop.getType() != EnumShopType.WEIXIN.getIndex()) {
|
||||||
return ResultVo.error(HttpStatus.PARAMS_ERROR, "参数错误,店铺不是微信小店店铺");
|
return ResultVo.error(HttpStatus.PARAMS_ERROR, "参数错误,店铺不是微信小店店铺");
|
||||||
}
|
}
|
||||||
if(!StringUtils.hasText(shop.getAppKey())) {
|
if(!StringUtils.hasText(shop.getAppKey())) {
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ public class WeiRefundController extends BaseController {
|
||||||
// TODO:需要优化消息格式
|
// TODO:需要优化消息格式
|
||||||
if(bo!=null && bo.getIds()!=null) {
|
if(bo!=null && bo.getIds()!=null) {
|
||||||
for(String id: bo.getIds()) {
|
for(String id: bo.getIds()) {
|
||||||
mqUtils.sendApiMessage(MqMessage.build(EnumShopType.WEI, MqType.REFUND_MESSAGE, id));
|
mqUtils.sendApiMessage(MqMessage.build(EnumShopType.WEIXIN, MqType.REFUND_MESSAGE, id));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ public enum EnumShopType {
|
||||||
JDVC("京东自营", 280),
|
JDVC("京东自营", 280),
|
||||||
PDD("拼多多", 300),
|
PDD("拼多多", 300),
|
||||||
DOU("抖店", 400),
|
DOU("抖店", 400),
|
||||||
WEI("视频号", 500),
|
WEIXIN("视频号", 500),
|
||||||
KWAI("快手小店", 600),
|
KWAI("快手小店", 600),
|
||||||
XHS("小红书", 700),
|
XHS("小红书", 700),
|
||||||
OFFLINE("拼多多", 999)
|
OFFLINE("拼多多", 999)
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@
|
||||||
|
|
||||||
### 4.0 已确认口径(冻结)
|
### 4.0 已确认口径(冻结)
|
||||||
- `shopId`:**全平台唯一**,可直接作为本服务店铺主键,不考虑跨平台撞号。
|
- `shopId`:**全平台唯一**,可直接作为本服务店铺主键,不考虑跨平台撞号。
|
||||||
- `platform`:由外部系统显式传入,取值采用 **`PDD`/`TAO`/`JD`/`DOU`/`WEI`/`KWAI`/`XHS`**(后续平台新增再扩展枚举)。
|
- `platform`:由外部系统显式传入,取值采用 **`PDD`/`TAO`/`JD`/`DOU`/`WEIXIN`/`KWAI`/`XHS`**(后续平台新增再扩展枚举)。
|
||||||
- AK/SK:**一套即可**(外部系统作为单一调用方)。
|
- AK/SK:**一套即可**(外部系统作为单一调用方)。
|
||||||
- `X-Timestamp`:**毫秒**(ms)。
|
- `X-Timestamp`:**毫秒**(ms)。
|
||||||
- `X-Signature`:**Base64** 编码(HMAC-SHA256 输出 bytes → Base64)。
|
- `X-Signature`:**Base64** 编码(HMAC-SHA256 输出 bytes → Base64)。
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ public class ExternalGoodsUpsertRequest {
|
||||||
private Long shopId;
|
private Long shopId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 目标平台枚举:PDD/TAO/JD/DOU/WEI/KWAI/XHS(与 EnumShopType 命名一致)
|
* 目标平台枚举:PDD/TAO/JD/DOU/WEIXIN/KWAI/XHS(与 EnumShopType 命名一致)
|
||||||
*/
|
*/
|
||||||
private String platform;
|
private String platform;
|
||||||
|
|
||||||
|
|
@ -149,6 +149,12 @@ public class ExternalGoodsUpsertRequest {
|
||||||
*/
|
*/
|
||||||
private PddPopAuth pddPopAuth;
|
private PddPopAuth pddPopAuth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信视频号小店凭证(与 {@link PddPopAuth} 形态一致:appId/secret + 店铺 access_token)。
|
||||||
|
* 当 {@code platform=WEIXIN}({@link cn.qihangerp.common.enums.EnumShopType#WEIXIN})且 {@code external.sph.publish-enabled=true} 时<b>必填</b>。
|
||||||
|
*/
|
||||||
|
private SphPopAuth sphPopAuth;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public static class PddPopAuth {
|
public static class PddPopAuth {
|
||||||
@JsonAlias({"app_key", "clientId", "client_id"})
|
@JsonAlias({"app_key", "clientId", "client_id"})
|
||||||
|
|
@ -165,6 +171,21 @@ public class ExternalGoodsUpsertRequest {
|
||||||
private String gatewayUrl;
|
private String gatewayUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class SphPopAuth {
|
||||||
|
@JsonAlias({"app_key", "clientId", "client_id"})
|
||||||
|
private String appKey;
|
||||||
|
|
||||||
|
@JsonAlias({"app_secret", "clientSecret", "client_secret"})
|
||||||
|
private String appSecret;
|
||||||
|
|
||||||
|
@JsonAlias({"access_token", "token", "sessionKey", "session_key"})
|
||||||
|
private String accessToken;
|
||||||
|
|
||||||
|
@JsonAlias({"gateway_url", "channelsGatewayUrl"})
|
||||||
|
private String gatewayUrl;
|
||||||
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public static class AttributeItem {
|
public static class AttributeItem {
|
||||||
private String code;
|
private String code;
|
||||||
|
|
|
||||||
|
|
@ -52,4 +52,19 @@ public class ExternalGoodsUpsertResultVo implements Serializable {
|
||||||
/** 是否通过 spec.id.get 自动生成了 sku 规格 */
|
/** 是否通过 spec.id.get 自动生成了 sku 规格 */
|
||||||
private Boolean pddSpecAutoResolved;
|
private Boolean pddSpecAutoResolved;
|
||||||
private String pddAutoResolveDetail;
|
private String pddAutoResolveDetail;
|
||||||
|
|
||||||
|
// ---- 微信视频号小店(platform=WEIXIN)----
|
||||||
|
|
||||||
|
/** 是否尝试调用微信侧发品 */
|
||||||
|
private Boolean sphPublishAttempted;
|
||||||
|
|
||||||
|
/** 微信发品是否成功(未尝试时为 null) */
|
||||||
|
private Boolean sphPublishSuccess;
|
||||||
|
|
||||||
|
private String sphPublishMessage;
|
||||||
|
|
||||||
|
private String sphResponseSnippet;
|
||||||
|
|
||||||
|
/** 小店侧商品 ID(字符串) */
|
||||||
|
private String sphProductId;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
package cn.qihangerp.model.vo;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信视频号小店发品链路诊断(与 {@link PddPublishLaneResultVo} 对称,字段独立避免语义混淆)。
|
||||||
|
*
|
||||||
|
* @author guochengyu
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SphPublishLaneResultVo implements Serializable {
|
||||||
|
|
||||||
|
private Boolean attempted;
|
||||||
|
private Boolean success;
|
||||||
|
private String message;
|
||||||
|
/** 渠道网关/适配层响应片段(脱敏前由服务端控制长度) */
|
||||||
|
private String responseSnippet;
|
||||||
|
/** 小店侧商品 ID(字符串,与微信返回形态一致) */
|
||||||
|
private String sphProductId;
|
||||||
|
}
|
||||||
|
|
@ -210,7 +210,7 @@ public class WeiOrderServiceImpl extends ServiceImpl<WeiOrderMapper, WeiOrder>
|
||||||
}
|
}
|
||||||
OOrder order = new OOrder();
|
OOrder order = new OOrder();
|
||||||
order.setOrderNum(weiOrder.getOrderId());
|
order.setOrderNum(weiOrder.getOrderId());
|
||||||
order.setShopType(EnumShopType.WEI.getIndex());
|
order.setShopType(EnumShopType.WEIXIN.getIndex());
|
||||||
order.setShopId(weiOrder.getShopId());
|
order.setShopId(weiOrder.getShopId());
|
||||||
// order.setShipType(confirmBo.getShipType());
|
// order.setShipType(confirmBo.getShipType());
|
||||||
order.setShipType(0);
|
order.setShipType(0);
|
||||||
|
|
@ -273,7 +273,7 @@ public class WeiOrderServiceImpl extends ServiceImpl<WeiOrderMapper, WeiOrder>
|
||||||
oOrderItem.setOrderId(order.getId());
|
oOrderItem.setOrderId(order.getId());
|
||||||
oOrderItem.setOrderNum(order.getOrderNum());
|
oOrderItem.setOrderNum(order.getOrderNum());
|
||||||
oOrderItem.setSubOrderNum(order.getOrderNum()+"-"+item.getSkuId());
|
oOrderItem.setSubOrderNum(order.getOrderNum()+"-"+item.getSkuId());
|
||||||
oOrderItem.setShopType(EnumShopType.WEI.getIndex());
|
oOrderItem.setShopType(EnumShopType.WEIXIN.getIndex());
|
||||||
oOrderItem.setShopId(weiOrder.getShopId());
|
oOrderItem.setShopId(weiOrder.getShopId());
|
||||||
// 商品信息
|
// 商品信息
|
||||||
oOrderItem.setProductId(item.getProductId());
|
oOrderItem.setProductId(item.getProductId());
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,12 @@ import cn.qihangerp.model.request.ExternalGoodsDelistRequest;
|
||||||
import cn.qihangerp.model.request.ExternalGoodsUpsertRequest;
|
import cn.qihangerp.model.request.ExternalGoodsUpsertRequest;
|
||||||
import cn.qihangerp.model.vo.ExternalGoodsUpsertResultVo;
|
import cn.qihangerp.model.vo.ExternalGoodsUpsertResultVo;
|
||||||
import cn.qihangerp.model.vo.PddPublishLaneResultVo;
|
import cn.qihangerp.model.vo.PddPublishLaneResultVo;
|
||||||
|
import cn.qihangerp.model.vo.SphPublishLaneResultVo;
|
||||||
import cn.qihangerp.module.service.OGoodsService;
|
import cn.qihangerp.module.service.OGoodsService;
|
||||||
import cn.qihangerp.module.service.OGoodsSkuService;
|
import cn.qihangerp.module.service.OGoodsSkuService;
|
||||||
import cn.qihangerp.service.external.ExternalGoodsAppService;
|
import cn.qihangerp.service.external.ExternalGoodsAppService;
|
||||||
import cn.qihangerp.service.external.pdd.ExternalPddPublishService;
|
import cn.qihangerp.service.external.pdd.ExternalPddPublishService;
|
||||||
|
import cn.qihangerp.service.external.sph.ExternalSphPublishService;
|
||||||
import cn.qihangerp.service.external.pdd.OGoodsPddMappingPersistence;
|
import cn.qihangerp.service.external.pdd.OGoodsPddMappingPersistence;
|
||||||
import com.alibaba.fastjson2.JSON;
|
import com.alibaba.fastjson2.JSON;
|
||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
|
@ -41,6 +43,7 @@ public class ExternalGoodsAppServiceImpl implements ExternalGoodsAppService {
|
||||||
private final OGoodsService goodsService;
|
private final OGoodsService goodsService;
|
||||||
private final OGoodsSkuService skuService;
|
private final OGoodsSkuService skuService;
|
||||||
private final ExternalPddPublishService externalPddPublishService;
|
private final ExternalPddPublishService externalPddPublishService;
|
||||||
|
private final ExternalSphPublishService externalSphPublishService;
|
||||||
private final OGoodsPddMappingPersistence oGoodsPddMappingPersistence;
|
private final OGoodsPddMappingPersistence oGoodsPddMappingPersistence;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -111,7 +114,12 @@ public class ExternalGoodsAppServiceImpl implements ExternalGoodsAppService {
|
||||||
.pddCatRuleSnippet(null)
|
.pddCatRuleSnippet(null)
|
||||||
.pddSpecAutoResolved(null)
|
.pddSpecAutoResolved(null)
|
||||||
.pddAutoResolveDetail(null)
|
.pddAutoResolveDetail(null)
|
||||||
.pddPublishLane(null);
|
.pddPublishLane(null)
|
||||||
|
.sphPublishAttempted(false)
|
||||||
|
.sphPublishSuccess(null)
|
||||||
|
.sphPublishMessage(null)
|
||||||
|
.sphResponseSnippet(null)
|
||||||
|
.sphProductId(null);
|
||||||
|
|
||||||
if ("PDD".equalsIgnoreCase(req.getPlatform())) {
|
if ("PDD".equalsIgnoreCase(req.getPlatform())) {
|
||||||
OGoods g = goodsService.getById(goodsId);
|
OGoods g = goodsService.getById(goodsId);
|
||||||
|
|
@ -149,6 +157,36 @@ public class ExternalGoodsAppServiceImpl implements ExternalGoodsAppService {
|
||||||
out.pddPublishLane(lane.getPublishLane());
|
out.pddPublishLane(lane.getPublishLane());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ("WEIXIN".equalsIgnoreCase(req.getPlatform())) {
|
||||||
|
OGoods g = goodsService.getById(goodsId);
|
||||||
|
List<OGoodsSku> skuRows = skuService.list(new LambdaQueryWrapper<OGoodsSku>()
|
||||||
|
.eq(OGoodsSku::getGoodsId, goodsId));
|
||||||
|
Map<String, OGoodsSku> byOuter = new LinkedHashMap<>();
|
||||||
|
for (OGoodsSku row : skuRows) {
|
||||||
|
if (row != null && StringUtils.hasText(row.getOuterErpSkuId())) {
|
||||||
|
byOuter.putIfAbsent(row.getOuterErpSkuId(), row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<OGoodsSku> orderedSkus = new ArrayList<>();
|
||||||
|
if (!CollectionUtils.isEmpty(req.getSkus())) {
|
||||||
|
for (ExternalGoodsUpsertRequest.Sku sr : req.getSkus()) {
|
||||||
|
if (sr == null || !StringUtils.hasText(sr.getOutSkuId())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
OGoodsSku row = byOuter.get(sr.getOutSkuId());
|
||||||
|
if (row != null) {
|
||||||
|
orderedSkus.add(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SphPublishLaneResultVo lane = externalSphPublishService.publish(g, orderedSkus, req);
|
||||||
|
out.sphPublishAttempted(Boolean.TRUE.equals(lane.getAttempted()));
|
||||||
|
out.sphPublishSuccess(lane.getSuccess());
|
||||||
|
out.sphPublishMessage(lane.getMessage());
|
||||||
|
out.sphResponseSnippet(lane.getResponseSnippet());
|
||||||
|
out.sphProductId(lane.getSphProductId());
|
||||||
|
}
|
||||||
|
|
||||||
ExternalGoodsUpsertResultVo vo = out.build();
|
ExternalGoodsUpsertResultVo vo = out.build();
|
||||||
if ("PDD".equalsIgnoreCase(req.getPlatform())) {
|
if ("PDD".equalsIgnoreCase(req.getPlatform())) {
|
||||||
oGoodsPddMappingPersistence.persistAfterPddPublishSuccess(goodsId, req, vo);
|
oGoodsPddMappingPersistence.persistAfterPddPublishSuccess(goodsId, req, vo);
|
||||||
|
|
@ -167,6 +205,16 @@ public class ExternalGoodsAppServiceImpl implements ExternalGoodsAppService {
|
||||||
vo.getPddSpecAutoResolved(),
|
vo.getPddSpecAutoResolved(),
|
||||||
truncateForLog(vo.getPddAutoResolveDetail(), 300));
|
truncateForLog(vo.getPddAutoResolveDetail(), 300));
|
||||||
}
|
}
|
||||||
|
if ("WEIXIN".equalsIgnoreCase(req.getPlatform())) {
|
||||||
|
log.info("[external/upsert] WEIXIN summary shopId={} outGoodsId={} erpGoodsId={} attempted={} success={} message={} sphProductId={}",
|
||||||
|
req.getShopId(),
|
||||||
|
req.getOutGoodsId(),
|
||||||
|
vo.getGoodsId(),
|
||||||
|
vo.getSphPublishAttempted(),
|
||||||
|
vo.getSphPublishSuccess(),
|
||||||
|
truncateForLog(vo.getSphPublishMessage(), 600),
|
||||||
|
vo.getSphProductId());
|
||||||
|
}
|
||||||
return vo;
|
return vo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
12
service/src/main/java/cn/qihangerp/service/external/sph/ExternalSphConfiguration.java
vendored
Normal file
12
service/src/main/java/cn/qihangerp/service/external/sph/ExternalSphConfiguration.java
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
package cn.qihangerp.service.external.sph;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author guochengyu
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@EnableConfigurationProperties(ExternalSphProperties.class)
|
||||||
|
public class ExternalSphConfiguration {
|
||||||
|
}
|
||||||
19
service/src/main/java/cn/qihangerp/service/external/sph/ExternalSphProperties.java
vendored
Normal file
19
service/src/main/java/cn/qihangerp/service/external/sph/ExternalSphProperties.java
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
package cn.qihangerp.service.external.sph;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信视频号小店(platform=WEIXIN)发品相关开关;应用级密钥由调用方经 {@code sphPopAuth} 传入,与拼多多一致。
|
||||||
|
*
|
||||||
|
* @author guochengyu
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ConfigurationProperties(prefix = "external.sph")
|
||||||
|
public class ExternalSphProperties {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否在 upsert 落库后尝试调用微信小店发品适配(未接入前请保持 false,避免误报失败)。
|
||||||
|
*/
|
||||||
|
private boolean publishEnabled = false;
|
||||||
|
}
|
||||||
70
service/src/main/java/cn/qihangerp/service/external/sph/ExternalSphPublishService.java
vendored
Normal file
70
service/src/main/java/cn/qihangerp/service/external/sph/ExternalSphPublishService.java
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
package cn.qihangerp.service.external.sph;
|
||||||
|
|
||||||
|
import cn.qihangerp.model.entity.OGoods;
|
||||||
|
import cn.qihangerp.model.entity.OGoodsSku;
|
||||||
|
import cn.qihangerp.model.request.ExternalGoodsUpsertRequest;
|
||||||
|
import cn.qihangerp.model.vo.SphPublishLaneResultVo;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信视频号小店发品:凭证仅来自本次请求的 {@link ExternalGoodsUpsertRequest#getSphPopAuth()}。
|
||||||
|
* <p>具体 HTTP/OpenAPI 接入在 {@code publishEnabled=true} 后实现;关闭开关时不调用渠道,与拼多多 {@link cn.qihangerp.service.external.pdd.ExternalPddPublishService} 行为对称。</p>
|
||||||
|
*
|
||||||
|
* @author guochengyu
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ExternalSphPublishService {
|
||||||
|
|
||||||
|
private final ExternalSphProperties props;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 落库成功后可选调用微信侧发品;未开启或未实现时返回 {@code attempted=false},不阻断 ERP 本地 upsert。
|
||||||
|
*/
|
||||||
|
public SphPublishLaneResultVo publish(OGoods goods, List<OGoodsSku> skus, ExternalGoodsUpsertRequest req) {
|
||||||
|
if (!props.isPublishEnabled()) {
|
||||||
|
log.info("[SPH] publish skipped shopId={} outGoodsId={} reason=publish-disabled",
|
||||||
|
req != null ? req.getShopId() : null,
|
||||||
|
req != null ? req.getOutGoodsId() : null);
|
||||||
|
return SphPublishLaneResultVo.builder()
|
||||||
|
.attempted(false)
|
||||||
|
.success(null)
|
||||||
|
.message("external.sph.publish-enabled=false,未调用微信视频号小店")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
if (req == null) {
|
||||||
|
return SphPublishLaneResultVo.builder()
|
||||||
|
.attempted(false)
|
||||||
|
.success(false)
|
||||||
|
.message("请求参数不能为空")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
ExternalGoodsUpsertRequest.SphPopAuth auth = req.getSphPopAuth();
|
||||||
|
if (auth == null || !StringUtils.hasText(auth.getAppKey()) || !StringUtils.hasText(auth.getAppSecret())
|
||||||
|
|| !StringUtils.hasText(auth.getAccessToken())) {
|
||||||
|
log.info("[SPH] publish skipped shopId={} outGoodsId={} reason=sphPopAuth-incomplete",
|
||||||
|
req.getShopId(), req.getOutGoodsId());
|
||||||
|
return SphPublishLaneResultVo.builder()
|
||||||
|
.attempted(false)
|
||||||
|
.success(false)
|
||||||
|
.message("微信小店凭证不完整:请在请求体 sphPopAuth 中传入 appKey、appSecret、accessToken")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
// TODO: 接入微信视频号小店商品发布 OpenAPI,填充 sphProductId/responseSnippet
|
||||||
|
log.warn("[SPH] publish-enabled=true 但发品适配尚未实现 shopId={} outGoodsId={} skuCount={}",
|
||||||
|
req.getShopId(), req.getOutGoodsId(), skus == null ? 0 : skus.size());
|
||||||
|
return SphPublishLaneResultVo.builder()
|
||||||
|
.attempted(true)
|
||||||
|
.success(false)
|
||||||
|
.message("微信视频号小店发品接口尚未接入(请关闭 external.sph.publish-enabled 或完成适配开发)")
|
||||||
|
.responseSnippet(null)
|
||||||
|
.sphProductId(null)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue