俊安交接
陈俊安-shopee交接
shopee-业务代码
shopee-coding
陈俊安-temu交接
temu 刊登方案设计
temu coding处理
陈俊安-amazon交接
亚马逊跟卖流程设计
陈俊安-mercadolibre交接
陈俊安-ozon交接
ozon 刊登方案设计
ozon coding处理
陈俊安-tiktok交接
陈俊安-pib交接
陈俊安-monitor交接
刊登监控设计
其他平台coding处理
joom
速卖通
shopify
shoplazza
shopline
本文档使用 MrDoc 发布
-
+
首页
shopee-业务代码
### # 官方文档地址 ``` v1:https://open.shopee.com/documents/v1/Introduction?module=63&type=2 v2:https://open.shopee.com/documents/v2/Introduction?module=87&type=2 ``` ### # 刊登商品业务方式 > shopee 刊登分为 ```刊登CNSC``` 、```发布到子站点```、```本土店刊登```三种。 > ```cnsc```:China Seller Center(中国卖家中心),只支持中国和韩国卖家,一般一个用户会有一个 cnsc 店铺,发布到 cnsc 上的商品我们称为主商品或者 Global 商品,它是无法直接被买家可见和下单的,只能便于卖家对商品的管理。 > ```发布到子站点```:用户可以将 Global 商品发布到 ```相应国家站点的店铺```上,这样不同国家站点的买家就能看到这个商品并下单。 > ```本土店刊登```:一些非中国和韩国的卖家。以及部分未升级为 cnsc 店铺的卖家,只有相应的国家站点店铺,没有 cnsc 店铺管理商品,就只能对单站点的本土店刊登。 ### # 调度中心架构方式  ### # 关于 mdc-open-api-publish > ```【重要】```我们对外只暴露 open-api 的接口,无论是异步还是直调任务功能,我们的入口都是 open-api ,此方式架构便于管理全平台接口和核心服务的安全性,以及功能的解耦合。 > ```【重要】```我们所有的对外接口只提供 POST 方式的调用。这是由于各平台业务的复杂性导致的,我们很难使用 REST 方式或常规方式来因为业务迭代导致入参的可变性,因此强制所有入参使用 Body 中的 Json 对象方式传递。 > ```【重要】```我们所有的入参需要使用 ```@Validated``` 来校验入参参数的合理性和规范性。以此强制性校验方法减少入参数据因格式问题带来的刊登失败概率。 > ```【重要】```我们所有的对外接口都使用 ```RestResultVo``` 对象 Json 格式返回。 > 如下为 cnsc 刊登的接口代码: ```java @PostMapping("/addProduct") @ShopTokenAop @ApiOperation("刊登") public RestResultVo<ShopeeGlobalPublishSubmitVo> addProduct(@Validated @RequestBody ShopeeGlobalProductBo shopeeProductBo) { return RestResultVo.ok( shopeeGlobalPublishService.addProduct(shopeeProductBo) ); } ``` ### # 关于 mdc-product-shopee > ```【重要】```所有刊登的架构都是分平台的,每个平台将存在 mdc-product-平台 的名称的项目来处理目标平台的刊登业务。 > mdc-product-shopee 分为如下四个模块: ``` product-shopee-entity```:存放实体类 ``` product-shopee-feign```:存放相关的 feign 调用(如果有的话) ``` product-shopee-service```:存放相关 service 及其实现 ``` product-shopee-startup```:项目启动入口以及队列消息入口和请求接口入口 ### # mdc-product-shopee 中 cnsc 刊登流程 > ```【1、刊登消息入口】```:product-shopee-startup/consumer/ShopeePublishGlobalConsumer 为 cnsc 刊登消息任务消费入口,目前我们使用的 rocketmq 队列。 > ```【2、处理消息重复消费】```:product-shopee-service/ShopeeGlobalConsumerServiceImpl/addProduct() 方法为整个 cnsc 刊登流程服务方法。如下为该方法中判断消息重复的方式: ```java // 判断是否重复消费 if (!checkRepeatConsume(consumerBaseBo.getId())) { log.error("刊登重复消费:" + JSONObject.toJSONString(consumerBaseBo)); return; } ``` > ```【3、将刊登数据查询出来,并判断是否已经刊登或刊登任务不存在】```: ```java ShopeeGlobalTask shopeeGlobalTask = this.shopeeGlobalTaskMapper.selectById(consumerBaseBo.getId()); if (shopeeGlobalTask == null) { log.warn("刊登任务不存在。"); return; } if (shopeeGlobalTask.getStatus().intValue() == 9 || shopeeGlobalTask.getStatus().intValue() == 6 || shopeeGlobalTask.getStatus().intValue() == 7) { log.warn("刊登任务已完成。"); return; } ``` > ```【4、组装 cnsc 刊登数据,并刊登 cnsc 商品】```: ```java // 组装数据,组装过程中会上传图片 AddProductGlobalBo addProductV2Bo = packagePublishParams(shopeeGlobalTask); // 刊登 cnsc 商品 shopeeGlobalService.addProduct(addProductV2Bo, addProductGlobalResult -> { ... }); ``` > ```【5、cnsc 刊登完成后,上传子商品】```: ```java //刊登变体 if (StringUtils.hasText(shopeeGlobalTask.getVariations())) { initVariationGlobal(shopeeGlobalTask, addProductGlobalResult.getGlobal_item_id()); } ``` > ```【6、查询需要发布的数据,并发布到站点】```: ```java //先查询需要发布的站点数据信息 LambdaQueryWrapper<ShopeeCreateTask> queryWrapper = new QueryWrapper<ShopeeCreateTask>() .lambda() .eq(ShopeeCreateTask::getGlobal_task_id, shopeeGlobalTask.getId()); List<ShopeeCreateTask> shopeeCreateTasks = shopeeCreateTaskMapper.selectList(queryWrapper); if (shopeeCreateTasks.size() > 0 || shopeeCreateTasks != null) { // 发布到站点 strategyCreatePublishTasks(shopeeCreateTasks, addProductGlobalResult.getGlobal_item_id(), shopeeCreateTaskResults); } ``` > ```【7、如果 cnsc 刊登成功,上传尺码图】```:以下代码在 finally 代码块中。 ```java try { if (shopeeTaskResult.getStatus().equals(9) && StringUtils.hasText(shopeeGlobalTask.getSizeChart())) { // 上传尺码图 sizeChartResult = UpdateSizeChart(shopeeTaskResult.getProductId(), shopeeGlobalTask.getSizeChart()); } else { sizeChartResult = new SizeChartResult() .setSuccess(false) .setMessage(!shopeeTaskResult.getStatus().equals(9) ?"刊登失败,取消上传尺码图。" : "用户取消上传尺码图。"); } } finally { imageIdsCache.remove(); } ``` > ```【8、保存刊登结果、记录到成功失败表、推送刊登结果】```: ```java // 保存刊登结果 shopeeTaskResult.updateById(); // 记录刊登成功失败 recordPublishResult(shopeeTaskResult, null); CreateProductGlobalResult createProductResult = new CreateProductGlobalResult() .setShopeeGlobalTask(shopeeTaskResult) .setPublishProductResults(shopeeCreateTaskResults) .setSizeChartResult(sizeChartResult); // 推送刊登结果 pushPublishResult(createProductResult); ``` ### # mdc-product-shopee 中 ```发布到子站点``` 流程 > ```【1、发布到子站点消息入口】```:product-shopee-startup/consumer/ShopeeCreatePublishGlobalConsumer 为发布到子站点消息任务消费入口,目前我们使用的 rocketmq 队列。 > ```【2、查询任务详情】```:product-shopee-service/ShopeeGlobalConsumerServiceImpl/createPublish() 方法为整个发布到子站点流程服务方法。如下为该方法中查询任务详情的方式: ```java ShopeeCreateTask shopeeCreateTask = this.shopeeCreateTaskMapper.selectById(consumerBaseBo.getId()); ``` > ```【3、查询任务详情】```:product-shopee-service/ShopeeGlobalConsumerServiceImpl/createPublish() 方法为整个发布到子站点流程服务方法。先查询了任务,然后初始化了图片缓存,缓存图片如下为该方法中查询任务详情的方式: ```java // 查询任务 ShopeeCreateTask shopeeCreateTask = this.shopeeCreateTaskMapper.selectById(consumerBaseBo.getId()); // 初始化图片ID缓存 imageIdsCache.set(new HashMap<>()); ``` > ```【4、提交数据到平台】```:product-shopee-service/ShopeeGlobalConsumerServiceImpl/createPublishTask() 方法为提交发布数据到平台的功能,这部分和 CNSC 刊登并发布功能复用: ```java /** * 应用到站点 * * @param addProductGlobalResult * @param shopeeCreateTask */ private ShopeeCreateTask createPublishTask(ShopeeCreateTask shopeeCreateTask) { ShopeeCreateTask shopeeCreateTaskResult = new ShopeeCreateTask(); shopeeCreateTaskResult.setSite(shopeeCreateTask.getSite()); shopeeCreateTaskResult.setId(shopeeCreateTask.getId()); //调发布接口 try { // 组装数据 ShopeeCreateProductBo shopeeCreateProductBo = packagePushSiteParams(shopeeCreateTask); PushSiteResult pushSiteResult = shopeeGlobalService.createPublish(shopeeCreateProductBo); //发布完获取状态 GetPushSiteStatusResult getpushSiteStatusResult = shopeeGlobalService.getPushSiteStatus(pushSiteResult.getPublish_task_id().toString(), 1); if (getpushSiteStatusResult.getPublish_status().equals("success")) { shopeeCreateTaskResult.setStatus(9); shopeeCreateTaskResult.setResult("发布成功!"); shopeeCreateTaskResult.setItemId(getpushSiteStatusResult.getSuccess().getItem_id().toString()); } else { shopeeCreateTaskResult.setStatus(6); if (getpushSiteStatusResult.getFailed() != null) { shopeeCreateTaskResult.setResult("发布失败:" + getpushSiteStatusResult.getFailed().getFailed_reason()); } else { shopeeCreateTaskResult.setResult("发布失败:" + JSONObject.toJSONString(getpushSiteStatusResult)); } } } catch (Exception e) { String message = e.getMessage(); boolean flag = false; if (StringUtils.hasText(message) && (message.contains("you have published this global item to shop in the same region, and published " + "item is not deleted or unlisted.") || message.contains("you have published this global item to the same shop already, and " + "published item is not deleted."))) { handleSIPShopePublish(shopeeCreateTask, shopeeCreateTaskResult, message); flag = true; } if (!flag) { log.error("发布商品异常:" + ExceptionUtils.getStackTrace(e) + " : " + message); shopeeCreateTaskResult.setStatus(6); shopeeCreateTaskResult.setResult("出现异常,请联系管理员。=>" + message); } } shopeeCreateTaskResult.updateById(); return shopeeCreateTaskResult; } ``` > ```【5、推送发布结果】```:发布执行完成后推送结果和清除图片的缓存: ```java MdcScBackendMessage msbMessage = new MdcScBackendMessage(); msbMessage.setAppId(String.valueOf(shopTokenInfo.getAppId())); msbMessage.setPlatformId(String.valueOf(shopTokenInfo.getPlatformId())); msbMessage.setDevPartnerId(String.valueOf(shopTokenInfo.getPartnerId())); msbMessage.setShopId(String.valueOf(shopTokenInfo.getShopId())); msbMessage.setMsgType(MsgTypeEnum.CREATE_PUBLISH_PRODUCT_RESULT_GLOBAL_SHOPEE); msbMessage.setMqType(MsgMqKeyConstants.MSG_RABBITMQ_KANDENG); msbMessage.setActionType("update"); msbMessage.setData(JSONObject.toJSONString(publishTask).toString()); // 推送结果 rocketMqProducer.asyncSend( TopicConstants.MDC_SC_PUSHMESSAGE_TOPIC, MessageBuilder.withPayload(msbMessage).build() ); log.info("推送:" + JSONObject.toJSONString(msbMessage)); imageIdsCache.remove(); ``` ### # mdc-product-shopee 中 ```本土店``` 刊登流程 > 参考 mdc-product-shopee 中 cnsc 刊登流程类似。入口为 product-shopee-startup/consumer/ShopeePublishV2Consumer ### # mdc-product-shopee 中同步店铺在线商品功能 > ```【重要】```目前 shopee 同步店铺商品采用增量同步的方式,防止大量用户存在大量商品的情况将队列消息积压。 > ```【1、消息消费入口】```:product-shopee-startup/consumer/ShopeeSyncShopProductGlobalConsumer > ```【2、业务处理 service 方法】```:product-shopee-service/ShopeeGlobalConsumerServiceImpl/ShopeeSyncProductGlobalConsumer/syncShopProduct() > ```【3、分页拉取商品】```: ```java try { syncShopProductByPage(lastProduct, offset, is, productMap, 0, updateDate); } catch (Exception e) { ... } finally { ... } ``` > ```【3、将每页的每个商品 ID 推送的拉取详情的队列】```: ```java pushSyncProductGlobal(globalItem, null); ``` > ```【4、每次拉取第一页时推送本次同步的商品总数(如果有的话)】```: ```java // 推送商品总数,只推一次 if ("".equals(offset.get())) { pushGlobalShopStatus(2, "拉取中", productListGlobalResult.getTotal_count().longValue()); } ``` > ```【5、更新店铺同步状态】```: ```java //同步完店铺修改店铺中间表状态及同步信息 updateShopItemStatus(shopTokenInfo.getShopId(), shopStatus, resultMessage); ``` > ```【6、将店铺同步状态绑定在最后一个商品上,防止商品详情还未同步完成时,推送了店铺状态】```: ```java // 推送最后一个商品,并携带店铺状态 if (lastProduct.get() != null) { SyncProductConsumerBaseBo.ShopStatus shopStatus1 = new SyncProductConsumerBaseBo.ShopStatus(); shopStatus1.setShopStatus(shopStatus); shopStatus1.setResultMessage(resultMessage); pushSyncProductGlobal(lastProduct.get(), shopStatus1); } else { // 最后一个商品不存在 就直接推状态 pushGlobalShopStatus(shopStatus, resultMessage, null); } ``` ### # mdc-product-shopee 中直调接口功能 > ```【重要】```所有的直调接口的入口在 ```product-shopee-startup``` 下的 ```com.mabang.product.controller``` 包下,以下是每个 controller 的业务功能: ```ShopeeCommonProductController```:公共业务服务接口(上传图片、活动操作) ```ShopeeDataAnalysisController```:数据统计相关(之前运营需要刊登的数据统计,所以相关接口开在这里) ```ShopeeGlobalProductController```:shopee 全球商品相关接口 ```ShopeeV2ProductController```:本土商品相关接口
chenjunan
2023年8月22日 17:39
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码