Przeglądaj źródła

feat: 补货单ERP同步功能实现 (foonsu.erp.orders.save)

- 实现 syncToErp() 方法,对接企得宝ERP订单上传接口
- 收件人信息从 t_shop 获取(省市区地址、联系人、电话)
- platformGoodsCode 通过 t_product.erp_goods_id → t_erp_goods.goods_code 映射
- 固定参数: warehouseName=充电桩餐饮项目, platformLogisticsCompanyCode=001

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
skyline 3 dni temu
rodzic
commit
a405b5ca69

+ 132 - 11
haha-service/src/main/java/com/haha/service/impl/ReplenishmentOrderServiceImpl.java

@@ -18,14 +18,13 @@ import com.haha.entity.dto.ReplenishmentSuggestionVO;
 import com.haha.entity.dto.ReplenishmentSuggestionVO.SuggestionItem;
 import com.haha.mapper.*;
 import com.haha.service.ReplenishmentOrderService;
-// TODO: 企得宝 ERP SDK - 暂未对接,后续接入时取消注释
-// import com.qdb.sdk.QdbClient;
-// import com.qdb.sdk.QdbException;
-// import com.qdb.sdk.model.request.OrderGoods;
-// import com.qdb.sdk.model.request.OrderPay;
-// import com.qdb.sdk.model.request.OrderSaveRequest;
+import com.qdb.sdk.QdbClient;
+import com.qdb.sdk.QdbException;
+import com.qdb.sdk.model.request.OrderGoods;
+import com.qdb.sdk.model.request.OrderSaveRequest;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.StringUtils;
@@ -53,12 +52,12 @@ public class ReplenishmentOrderServiceImpl extends ServiceImpl<ReplenishmentOrde
     private final LayerTemplateMapper layerTemplateMapper;
     private final DeviceInventoryMapper deviceInventoryMapper;
     private final ProductMapper productMapper;
+    private final ErpGoodsMapper erpGoodsMapper;
 
     private final ObjectMapper objectMapper = new ObjectMapper();
 
-    // TODO: 企得宝 ERP SDK - 暂未对接,后续接入时取消注释
-    // @Autowired(required = false)
-    // private QdbClient qdbClient;
+    @Autowired(required = false)
+    private QdbClient qdbClient;
 
     @Override
     public IPage<ReplenishmentOrder> getPage(ReplenishmentOrderQueryDTO queryDTO) {
@@ -319,12 +318,134 @@ public class ReplenishmentOrderServiceImpl extends ServiceImpl<ReplenishmentOrde
     @Override
     @Transactional(rollbackFor = Exception.class)
     public ReplenishmentOrder syncToErp(Long id) {
-        // TODO: 企得宝 ERP SDK - 暂未对接,后续接入时实现
-        log.warn("syncToErp 暂未实现,企得宝 ERP SDK 尚未接入: id={}", id);
         ReplenishmentOrder order = getById(id);
         if (order == null) {
             throw new BusinessException(404, "补货单不存在");
         }
+        if (order.getStatus() != ReplenishmentOrderStatusEnum.SUBMITTED.getCode()) {
+            throw new BusinessException(400, "仅已提交状态的补货单可同步ERP");
+        }
+
+        if (qdbClient == null) {
+            throw new BusinessException(500, "ERP SDK 未配置,无法同步");
+        }
+
+        // 查询设备(获取 erpShopId 和 deviceName)
+        Device device = deviceMapper.selectByDeviceId(order.getDeviceId());
+        if (device == null) {
+            throw new BusinessException(400, "设备不存在: " + order.getDeviceId());
+        }
+        if (device.getErpShopId() == null) {
+            throw new BusinessException(400, "设备未绑定ERP店铺");
+        }
+
+        // 查询门店(获取收件人地址和联系方式)
+        Shop shop = null;
+        if (order.getShopId() != null) {
+            shop = shopMapper.selectById(order.getShopId());
+        }
+        if (shop == null) {
+            throw new BusinessException(400, "门店信息不存在");
+        }
+
+        // 查询明细
+        List<ReplenishmentOrderItem> items = orderItemMapper.selectByOrderId(id);
+        if (items == null || items.isEmpty()) {
+            throw new BusinessException(400, "补货单无明细");
+        }
+
+        // 批量查询 Product
+        Set<String> productCodes = items.stream()
+                .map(ReplenishmentOrderItem::getProductCode)
+                .filter(org.springframework.util.StringUtils::hasText)
+                .collect(Collectors.toSet());
+
+        Map<String, Product> productMap = new HashMap<>();
+        if (!productCodes.isEmpty()) {
+            List<Product> products = productMapper.selectList(
+                    new LambdaQueryWrapper<Product>().in(Product::getCode, productCodes));
+            productMap = products.stream().collect(Collectors.toMap(Product::getCode, p -> p, (a, b) -> a));
+        }
+
+        // 批量查询 ERP 商品(t_product.erp_goods_id = t_erp_goods.erp_goods_id)
+        Set<Long> erpGoodsIds = productMap.values().stream()
+                .map(Product::getErpGoodsId).filter(Objects::nonNull).collect(Collectors.toSet());
+        Map<Long, String> erpGoodsCodeMap = new HashMap<>();
+        if (!erpGoodsIds.isEmpty()) {
+            List<ErpGoods> erpGoodsList = erpGoodsMapper.selectList(
+                    new LambdaQueryWrapper<ErpGoods>().in(ErpGoods::getErpGoodsId, erpGoodsIds));
+            erpGoodsCodeMap = erpGoodsList.stream()
+                    .collect(Collectors.toMap(ErpGoods::getErpGoodsId, ErpGoods::getGoodsCode, (a, b) -> a));
+        }
+
+        // 构建订单商品列表
+        List<OrderGoods> ordersGoods = new ArrayList<>();
+        for (ReplenishmentOrderItem item : items) {
+            String productCode = item.getProductCode();
+            Product product = productMap.get(productCode);
+            if (product == null || product.getErpGoodsId() == null) {
+                log.error("syncToErp 商品无ERP关联: name={}, code={}",
+                        item.getProductName(), productCode);
+                throw new BusinessException(400,
+                        "商品「" + item.getProductName() + "」未关联ERP商品,请先在ERP商品管理中绑定");
+            }
+
+            String goodsCode = erpGoodsCodeMap.get(product.getErpGoodsId());
+            if (goodsCode == null) {
+                throw new BusinessException(400,
+                        "商品「" + item.getProductName() + "」ERP商品编码未找到");
+            }
+
+            BigDecimal price = item.getUnitPrice() != null ? item.getUnitPrice() : BigDecimal.ZERO;
+            BigDecimal quantity = BigDecimal.valueOf(item.getPlannedQuantity() != null ? item.getPlannedQuantity() : 0);
+
+            OrderGoods goods = OrderGoods.builder()
+                    .platformGoodsCode(goodsCode)
+                    .goodsPlatformCode(goodsCode)
+                    .platformGoodsName(item.getProductName())
+                    .quantity(quantity)
+                    .price(price)
+                    .realPrice(price)
+                    .build();
+            ordersGoods.add(goods);
+        }
+
+        // 构建请求
+        OrderSaveRequest request = OrderSaveRequest.builder()
+                .shopId(device.getErpShopId())
+                .tradeNo(order.getOrderNo())
+                .platformTradeStatus("02")
+                .platformRefundStatus("0")
+                .orderTypeCode("3")
+                .postAmount(BigDecimal.ZERO)
+                .payAmount(BigDecimal.ZERO)
+                .receiverProvince(shop.getProvince())
+                .receiverCity(shop.getCity())
+                .receiverDistrict(shop.getDistrict())
+                .receiverAddress(shop.getAddress())
+                .receiverName(shop.getContactName())
+                .receiverMobile(shop.getContactPhone())
+                .receiverPhone(shop.getContactPhone())
+                .warehouseName("充电桩餐饮项目")
+                .vipName(device.getName())
+                // 物流公司编码 001 = 深圳市丰渡供应链管理有限公司
+                .platformLogisticsCompanyCode("001")
+                .ordersGoods(ordersGoods)
+                .build();
+
+        try {
+            qdbClient.getOrderApi().saveOrder(request);
+            log.info("补货单同步ERP成功: orderNo={}, items={}", order.getOrderNo(), ordersGoods.size());
+        } catch (QdbException e) {
+            log.error("补货单同步ERP失败: orderNo={}, error={}", order.getOrderNo(), e.getMessage(), e);
+            throw new BusinessException(500, "ERP同步失败: " + e.getMessage());
+        }
+
+        order.setStatus(ReplenishmentOrderStatusEnum.SYNCED_TO_ERP.getCode());
+        order.setErpSyncTime(LocalDateTime.now());
+        order.setUpdateTime(LocalDateTime.now());
+        updateById(order);
+
         fillStatusLabel(order);
         return order;
     }