|
|
@@ -1,23 +1,20 @@
|
|
|
package com.fs.his.controller;
|
|
|
|
|
|
import com.baidu.dev2.thirdparty.commons.codec.digest.DigestUtils;
|
|
|
-import com.fasterxml.jackson.core.JsonProcessingException;
|
|
|
-import com.fs.erp.dto.OrderItemDTO;
|
|
|
-import com.fs.erp.dto.PaymentDTO;
|
|
|
-import com.fs.erp.dto.ShopOrderDTO;
|
|
|
-import com.fs.erp.dto.tl.*;
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
+import com.fs.erp.dto.tl.dto.*;
|
|
|
import com.fs.erp.service.TlErpOrderService;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.http.ResponseEntity;
|
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
-
|
|
|
-import java.math.BigDecimal;
|
|
|
import java.time.Instant;
|
|
|
+import java.util.Arrays;
|
|
|
import java.util.Collections;
|
|
|
+import java.util.List;
|
|
|
|
|
|
/**
|
|
|
- * 聚水潭订单同步接口
|
|
|
+ * 聚水潭[兔灵]订单同步接口
|
|
|
*/
|
|
|
@Slf4j
|
|
|
@RestController
|
|
|
@@ -28,157 +25,81 @@ public class JstOrderSyncController {
|
|
|
@Autowired
|
|
|
private TlErpOrderService jstErpHttpService;
|
|
|
|
|
|
+ @Autowired
|
|
|
+ private ObjectMapper objectMapper;
|
|
|
|
|
|
|
|
|
private static final String partnerKey="erp";
|
|
|
|
|
|
|
|
|
+ private static final List<String> SUPPORTED_METHODS =
|
|
|
+ Collections.unmodifiableList(Arrays.asList(
|
|
|
+ "logistics.upload",
|
|
|
+ "cancel.order",
|
|
|
+ "inventory.upload",
|
|
|
+ "refund.goods"
|
|
|
+ ));
|
|
|
/**
|
|
|
* 聚水潭erp物流回调接口
|
|
|
* URL: POST /open/callback?partnerid=erp&method=logistics.upload&ts=xxx&sign=xxx
|
|
|
+ * 其中取消订单回调传入了token,其他接口没有,暂时还不知道这个token的作用
|
|
|
*/
|
|
|
@PostMapping("/open/callback")
|
|
|
- public ResponseEntity<PushResponse> handleLogisticsPush(
|
|
|
+ public ResponseEntity<PushResponse> handleCallback(
|
|
|
@RequestParam String partnerid,
|
|
|
@RequestParam String method,
|
|
|
@RequestParam Long ts,
|
|
|
@RequestParam String sign,
|
|
|
- @RequestBody JstLogisticsPushRequest request) {
|
|
|
+ // 注意:订单取消接口还有 token,但其他没有。聚水潭文档说 cancel.order 有 token
|
|
|
+ @RequestParam(required = false) String token,
|
|
|
+ @RequestBody String rawBody) {
|
|
|
|
|
|
// 1. 校验 partnerid
|
|
|
if (!"erp".equals(partnerid)) {
|
|
|
return ResponseEntity.ok(new PushResponse("1", "invalid partnerid"));
|
|
|
}
|
|
|
|
|
|
- // 2. 校验时间戳(±10分钟)
|
|
|
+ // 2. 校验 method
|
|
|
+ if (!SUPPORTED_METHODS.contains(method)) {
|
|
|
+ log.warn("不支持的 method: {}", method);
|
|
|
+ return ResponseEntity.ok(new PushResponse("1", "unsupported method"));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 校验时间戳
|
|
|
long now = Instant.now().getEpochSecond();
|
|
|
if (Math.abs(now - ts) > 600) {
|
|
|
return ResponseEntity.ok(new PushResponse("1", "ts expired"));
|
|
|
}
|
|
|
|
|
|
- // 3. 验签
|
|
|
+ // 4. 验签(注意:sign 拼接规则不含 token!)
|
|
|
String signSource = method + partnerid + "ts" + ts + partnerKey;
|
|
|
String expectedSign = DigestUtils.md5Hex(signSource).toLowerCase();
|
|
|
if (!expectedSign.equals(sign)) {
|
|
|
- log.warn("聚水潭签名校验失败: method={}, ts={}, sign={}", method, ts, sign);
|
|
|
+ log.warn("签名校验失败: method={}, ts={}", method, ts);
|
|
|
return ResponseEntity.ok(new PushResponse("1", "invalid sign"));
|
|
|
}
|
|
|
|
|
|
- // 4. 处理业务
|
|
|
+ // 5. 根据 method 反序列化并处理
|
|
|
try {
|
|
|
- jstErpHttpService.jSTanErpLogisticsCallback(request);
|
|
|
+ if ("logistics.upload".equals(method)) {
|
|
|
+ JstLogisticsPushDTO req = objectMapper.readValue(rawBody, JstLogisticsPushDTO.class);
|
|
|
+ jstErpHttpService.jSTanErpLogisticsCallback(req);
|
|
|
+ } else if ("inventory.upload".equals(method)) {
|
|
|
+ JstInventoryPushDTO req = objectMapper.readValue(rawBody, JstInventoryPushDTO.class);
|
|
|
+ jstErpHttpService.jSTanErpInventoryCallback(req);
|
|
|
+ } else if ("cancel.order".equals(method)) {
|
|
|
+ JstCancelOrderDTO req = objectMapper.readValue(rawBody, JstCancelOrderDTO.class);
|
|
|
+ jstErpHttpService.jSTanErpCancelOrderCallback(req);
|
|
|
+ } else if ("refund.goods".equals(method)) {
|
|
|
+ JstRefundGoodsDTO req = objectMapper.readValue(rawBody, JstRefundGoodsDTO.class);
|
|
|
+ jstErpHttpService.jSTanErpRefundGoodsCallback(req);
|
|
|
+ } else {
|
|
|
+ return ResponseEntity.ok(new PushResponse("1", "unknown method"));
|
|
|
+ }
|
|
|
return ResponseEntity.ok(new PushResponse("0", "执行成功"));
|
|
|
} catch (Exception e) {
|
|
|
- log.error("处理聚水潭物流回调异常", e);
|
|
|
+ log.error("处理聚水潭回调异常, method={}", method, e);
|
|
|
return ResponseEntity.ok(new PushResponse("1", "system error"));
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- // 模拟兔灵erp订单同步(推送)
|
|
|
- @PostMapping("/create")
|
|
|
- public Object syncOrderToJst(){
|
|
|
- TlCreateOrderRequest request=fillRequest();
|
|
|
- log.info("同步订单请求,corp_id={}, so_id={}",
|
|
|
- request.getCorpId(),
|
|
|
- request.getData() != null && !request.getData().isEmpty()
|
|
|
- ? request.getData().get(0).getSoId() : "N/A");
|
|
|
- return jstErpHttpService.syncOrderToJst(request);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 辅助方法 填充入参对象JstCreateOrderRequest
|
|
|
- * */
|
|
|
-
|
|
|
- private TlCreateOrderRequest fillRequest() {
|
|
|
- TlCreateOrderRequest request = new TlCreateOrderRequest();
|
|
|
- request.setCorpId("LwMAMdlBIH7agxn2t9N4Zv40"); // 正式环境企业ID
|
|
|
-
|
|
|
-
|
|
|
- //ShopOrderDTO order = buildOrderItem();
|
|
|
- TlOrderItem order = buildOrderItem();
|
|
|
- request.setData(Collections.singletonList(order));
|
|
|
-
|
|
|
- TlOptions options = buildOptions();
|
|
|
- request.setOptions(options);
|
|
|
-
|
|
|
- return request;
|
|
|
- }
|
|
|
-
|
|
|
- // 构建订单主体
|
|
|
- private TlOrderItem buildOrderItem() {
|
|
|
- //ShopOrderDTO order = new ShopOrderDTO();
|
|
|
- TlOrderItem order = new TlOrderItem();
|
|
|
- //order.setShopId(18150182);//测试店铺
|
|
|
- order.setShopId(19415819);
|
|
|
- order.setSoId("SO_20251110_001");
|
|
|
- order.setOrderDate("2025-11-10T10:30:00Z");
|
|
|
- order.setShopStatus("paid");
|
|
|
- order.setShopBuyerId("buyer_123");
|
|
|
-
|
|
|
- order.setReceiverState("广东省");
|
|
|
- order.setReceiverCity("深圳市");
|
|
|
- order.setReceiverDistrict("南山区");
|
|
|
- order.setReceiverAddress("科技园A座101");
|
|
|
- order.setReceiverName("张三");
|
|
|
- order.setReceiverMobile("13800138000");
|
|
|
-// order.setPayAmount(10000.0);
|
|
|
-// order.setFreight(0.0);
|
|
|
- order.setPayAmount(10000);
|
|
|
- order.setFreight(0);
|
|
|
-
|
|
|
- order.setRemark("卖家备注");
|
|
|
- order.setBuyerMessage("请发顺丰");
|
|
|
- order.setLabels("测试,同步");
|
|
|
- order.setShopModified("2025-11-10T10:30:00Z");
|
|
|
- order.setLId("cs12345678912");
|
|
|
- order.setIsCod(false);
|
|
|
-
|
|
|
- // 设置商品明细和支付信息
|
|
|
- order.setItems(Collections.singletonList(buildOrderItemDetail()));
|
|
|
- order.setPay(buildPayDetail());
|
|
|
-
|
|
|
- return order;
|
|
|
- }
|
|
|
-
|
|
|
- // 构建商品明细
|
|
|
- private TlOrderItemDetail buildOrderItemDetail() {
|
|
|
- TlOrderItemDetail item = new TlOrderItemDetail();
|
|
|
- //OrderItemDTO item = new OrderItemDTO();
|
|
|
- item.setSkuId("SKU123");
|
|
|
- item.setShopSkuId("SHOP_SKU_001");
|
|
|
-// item.setAmount(BigDecimal.valueOf(10000)); // 实付金额(分)
|
|
|
-// item.setPrice(BigDecimal.valueOf(10000)); // 单价(分)
|
|
|
-// item.setBasePrice(12000.0); // 原价(分)
|
|
|
-
|
|
|
- item.setAmount(10000); // 实付金额(分)
|
|
|
- item.setPrice(10000); // 单价(分)
|
|
|
- item.setBasePrice(12000); // 原价(分)
|
|
|
- item.setQty(1);
|
|
|
- item.setName("测试商品");
|
|
|
- item.setOuterOiId("OI_20251110_001");
|
|
|
- item.setProducedDate("2025-11-10T00:00:00Z");
|
|
|
- item.setBatchId(null);
|
|
|
- return item;
|
|
|
- }
|
|
|
-
|
|
|
- // 构建支付明细
|
|
|
- private TlPayDetail buildPayDetail() {
|
|
|
- //PaymentDTO pay = new PaymentDTO();
|
|
|
- TlPayDetail pay = new TlPayDetail();
|
|
|
- pay.setOuterPayId("PAY_20251110_001"); // 必填
|
|
|
- //pay.setPayDate("2025-11-10T10:30:00Z"); // 必填,RFC3339
|
|
|
- pay.setPayDate("2025-11-11 10:39:03"); // 必填,RFC3339
|
|
|
- pay.setPayment("online");
|
|
|
- pay.setSellerAccount("alipay@company.com"); // 必填
|
|
|
- pay.setBuyerAccount("buyer@example.com"); // 必填
|
|
|
- pay.setAmount(10000);
|
|
|
- return pay;
|
|
|
- }
|
|
|
-
|
|
|
- // 构建选项
|
|
|
- private TlOptions buildOptions() {
|
|
|
- TlOptions options = new TlOptions();
|
|
|
- options.setJstServerId(1);
|
|
|
- return options;
|
|
|
- }
|
|
|
}
|