feat: 物流轨迹

pull/1121/head
梁宇奇 2025-05-07 15:23:14 +08:00
parent 73015745f1
commit 59ab724173
17 changed files with 847 additions and 150 deletions

View File

@ -1,17 +1,30 @@
package com.ruoyi.web.controller.xkt;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import cn.hutool.json.JSONUtil;
import com.ruoyi.common.core.controller.XktBaseController;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.web.controller.xkt.vo.express.ZtoPrintOrderReqVO;
import com.ruoyi.web.controller.xkt.vo.express.ZtoPrintOrderRespVO;
import com.ruoyi.xkt.dto.order.StoreOrderExpressTrackAddDTO;
import com.ruoyi.xkt.enums.EExpressChannel;
import com.ruoyi.xkt.enums.EExpressStatus;
import com.ruoyi.xkt.service.IStoreOrderService;
import com.ruoyi.xkt.thirdpart.yto.YtoSignUtil;
import com.ruoyi.xkt.thirdpart.yto.YtoTrackObj;
import com.ruoyi.xkt.thirdpart.zto.ZopDigestUtil;
import com.ruoyi.xkt.thirdpart.zto.ZtoTrackObj;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.w3c.dom.Document;
import java.util.concurrent.TimeUnit;
@ -24,8 +37,15 @@ import java.util.concurrent.TimeUnit;
@RequestMapping("/rest/v1/express-callback")
public class ExpressCallbackController extends XktBaseController {
@Value("${yto.appSecret2:}")
private String ytoAppSecret2;
@Value("${zto.appSecret:}")
private String ztoAppSecret;
@Autowired
private RedisCache redisCache;
@Autowired
private IStoreOrderService storeOrderService;
/**
* -/PDF
@ -43,11 +63,107 @@ public class ExpressCallbackController extends XktBaseController {
if (StrUtil.isNotEmpty(vo.getBillCode())
&& StrUtil.isNotEmpty(vo.getResult())) {
//缓存30分钟
redisCache.setCacheObject("ZTO-"+vo.getBillCode(),vo.getResult(),30, TimeUnit.MINUTES);
redisCache.setCacheObject("ZTO-" + vo.getBillCode(), vo.getResult(), 30, TimeUnit.MINUTES);
return ZtoPrintOrderRespVO.success();
}
return ZtoPrintOrderRespVO.failure();
}
@ApiOperation("圆通-轨迹推送")
@RequestMapping(value = "yto/track")
public String ytoTrack(YtoTrackObj.Request request) {
if (StrUtil.isNotBlank(request.getLogistics_interface()) &&
//验签
YtoSignUtil.verify(request.getData_digest(), request.getLogistics_interface(), ytoAppSecret2)) {
logger.info("圆通-轨迹推送数据处理: {}", request);
Document dom = XmlUtil.parseXml(request.getLogistics_interface());
YtoTrackObj.Info obj = XmlUtil.xmlToBean(dom, YtoTrackObj.class).getUpdateInfo();
StoreOrderExpressTrackAddDTO trackAddDTO = trans2OrderTrack(obj);
storeOrderService.addTrack(trackAddDTO);
return YtoTrackObj.Response.builder()
.success(true)
.logisticProviderID(obj.getLogisticProviderID())
.txLogisticID(obj.getTxLogisticID())
.build()
.toXmlStr();
}
logger.warn("圆通-轨迹推送数据异常: {}", request);
return YtoTrackObj.Response.builder()
.success(false)
.build()
.toXmlStr();
}
@ApiOperation("中通-轨迹推送")
@PostMapping(value = "zto/track")
public String ztoTrack(@RequestBody ZtoTrackObj.Request request) {
if (StrUtil.isNotBlank(request.getData()) &&
//验签
ZopDigestUtil.verify(request.getData_digest(), request.getData(), ztoAppSecret)) {
logger.info("中通-轨迹推送数据处理: {}", request);
ZtoTrackObj.Info obj = JSONUtil.toBean(request.getData(), ZtoTrackObj.Info.class);
StoreOrderExpressTrackAddDTO trackAddDTO = trans2OrderTrack(obj);
storeOrderService.addTrack(trackAddDTO);
return ZtoTrackObj.Response.builder()
.status(true)
.build()
.toJsonStr();
}
logger.warn("中通-轨迹推送数据异常: {}", request);
return ZtoTrackObj.Response.builder()
.status(false)
.build()
.toJsonStr();
}
private StoreOrderExpressTrackAddDTO trans2OrderTrack(ZtoTrackObj.Info ztTrack) {
StoreOrderExpressTrackAddDTO dto = new StoreOrderExpressTrackAddDTO();
dto.setExpressWaybillNo(ztTrack.getBillCode());
dto.setAction(ztTrack.getAction());
dto.setDescription(ztTrack.getActionTime() + " " + ztTrack.getRemark());
dto.setExpressId(EExpressChannel.ZTO.getExpressId());
/**
*
* <p>
* GOT    
*  DEPARTURE    
*  ARRIVAL    
*  DISPATCH    
*  RETURN_SCAN   退   退退returnType
*  RETURN_SIGNED   退   退
*  INBOUND     //
*  HANDOVERSCAN_SIGNED
*  DEPARTURE_SIGNED   //
*  SIGNED    
*  PROBLEM     problemCode
*/
switch (ztTrack.getAction()) {
case "GOT":
dto.setExpressStatus(EExpressStatus.PICKED_UP);
break;
case "SIGNED":
dto.setExpressStatus(EExpressStatus.COMPLETED);
break;
}
return dto;
}
private StoreOrderExpressTrackAddDTO trans2OrderTrack(YtoTrackObj.Info ytTrack) {
StoreOrderExpressTrackAddDTO dto = new StoreOrderExpressTrackAddDTO();
dto.setExpressWaybillNo(ytTrack.getMailNo());
dto.setAction(ytTrack.getInfoContent());
dto.setDescription(DateUtil.formatDateTime(ytTrack.getAcceptTime()) + " " + ytTrack.getRemark());
dto.setExpressId(EExpressChannel.YTO.getExpressId());
switch (ytTrack.getInfoContent()) {
case "GOT":
dto.setExpressStatus(EExpressStatus.PICKED_UP);
break;
case "SIGNED":
dto.setExpressStatus(EExpressStatus.COMPLETED);
break;
}
return dto;
}
}

View File

@ -26,8 +26,8 @@ import com.ruoyi.xkt.service.IStoreOrderService;
import io.jsonwebtoken.lang.Assert;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ResponseHeader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@ -53,7 +53,6 @@ public class StoreOrderController extends XktBaseController {
@Autowired
private List<PaymentManager> paymentManagers;
@PreAuthorize("@ss.hasPermi('system:order:add')")
@Log(title = "订单", businessType = BusinessType.INSERT)
@ApiOperation("创建订单")
@PostMapping("create")
@ -69,7 +68,6 @@ public class StoreOrderController extends XktBaseController {
return success(respVO);
}
@PreAuthorize("@ss.hasPermi('system:order:edit')")
@Log(title = "订单", businessType = BusinessType.UPDATE)
@ApiOperation("修改订单")
@PostMapping("edit")
@ -81,7 +79,6 @@ public class StoreOrderController extends XktBaseController {
return success(result.getOrder().getId());
}
@PreAuthorize("@ss.hasPermi('system:order:add')")
@Log(title = "订单", businessType = BusinessType.OTHER)
@ApiOperation("支付订单")
@PostMapping("pay")
@ -96,7 +93,6 @@ public class StoreOrderController extends XktBaseController {
return success(respVO);
}
@PreAuthorize("@ss.hasPermi('system:order:edit')")
@Log(title = "订单", businessType = BusinessType.UPDATE)
@ApiOperation("取消订单")
@PostMapping("cancel")
@ -110,7 +106,6 @@ public class StoreOrderController extends XktBaseController {
return success();
}
@PreAuthorize("@ss.hasPermi('system:order:query')")
@ApiOperation(value = "订单详情")
@GetMapping(value = "/{id}")
public R<StoreOrderInfoVO> getInfo(@PathVariable("id") Long id) {
@ -120,9 +115,9 @@ public class StoreOrderController extends XktBaseController {
}
@PreAuthorize("@ss.hasPermi('system:order:list')")
@ApiOperation(value = "订单分页查询")
@PostMapping("/page")
@ResponseHeader
public R<PageVO<StoreOrderPageItemVO>> page(@Validated @RequestBody StoreOrderQueryVO vo) {
StoreOrderQueryDTO queryDTO = BeanUtil.toBean(vo, StoreOrderQueryDTO.class);
if (1 == vo.getSrcPage()) {
@ -134,7 +129,6 @@ public class StoreOrderController extends XktBaseController {
return success(PageVO.of(pageDTO, StoreOrderPageItemVO.class));
}
@PreAuthorize("@ss.hasPermi('system:order:add')")
@Log(title = "订单", businessType = BusinessType.OTHER)
@ApiOperation("发货-平台物流")
@PostMapping("ship-platform")
@ -151,7 +145,6 @@ public class StoreOrderController extends XktBaseController {
return success(respList);
}
@PreAuthorize("@ss.hasPermi('system:order:add')")
@Log(title = "订单", businessType = BusinessType.OTHER)
@ApiOperation("发货-档口物流")
@PostMapping("ship-store")
@ -168,7 +161,6 @@ public class StoreOrderController extends XktBaseController {
return success(respList);
}
@PreAuthorize("@ss.hasPermi('system:order:add')")
@Log(title = "订单", businessType = BusinessType.OTHER)
@ApiOperation("打印面单")
@PostMapping("print")
@ -184,7 +176,6 @@ public class StoreOrderController extends XktBaseController {
return success(rtnList);
}
@PreAuthorize("@ss.hasPermi('system:order:add')")
@Log(title = "订单", businessType = BusinessType.OTHER)
@ApiOperation("确认收货")
@PostMapping("receipt")
@ -194,7 +185,6 @@ public class StoreOrderController extends XktBaseController {
return success();
}
@PreAuthorize("@ss.hasPermi('system:order:add')")
@Log(title = "订单", businessType = BusinessType.OTHER)
@ApiOperation("申请售后(创建售后订单)")
@PostMapping("refund/apply")
@ -230,7 +220,6 @@ public class StoreOrderController extends XktBaseController {
return success(afterSaleApplyResult.getStoreOrderId());
}
@PreAuthorize("@ss.hasPermi('system:order:add')")
@Log(title = "订单", businessType = BusinessType.OTHER)
@ApiOperation("确认退款")
@PostMapping("refund/confirm")
@ -250,7 +239,6 @@ public class StoreOrderController extends XktBaseController {
return success();
}
@PreAuthorize("@ss.hasPermi('system:order:add')")
@Log(title = "订单", businessType = BusinessType.OTHER)
@ApiOperation("拒绝退款")
@PostMapping("refund/reject")

View File

@ -0,0 +1,48 @@
package com.ruoyi.xkt.dto.express;
import lombok.Data;
import java.util.Date;
import java.util.List;
/**
*
*
* @author liangyq
* @date 2025-05-07 11:57:52.599
**/
@Data
public class ExpressTrackDTO {
/**
*
*/
private String expressWaybillNo;
/**
* ID
*/
private Long expressId;
/**
*
*/
private List<Item> items;
@Data
public static class Item {
/**
*
*/
private String action;
/**
*
*/
private String description;
/**
*
*/
private String remark;
/**
*
*/
private Date createTime;
}
}

View File

@ -0,0 +1,34 @@
package com.ruoyi.xkt.dto.express;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author liangyq
* @date 2025-04-16 15:24
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ExpressTrackSubReqDTO {
/**
*
*/
private String expressReqNo;
/**
*
*/
private String expressWaybillNo;
/**
* -
*/
private String originContactPhoneNumber;
/**
* -
*/
private String destinationContactPhoneNumber;
}

View File

@ -0,0 +1,40 @@
package com.ruoyi.xkt.dto.order;
import com.ruoyi.xkt.enums.EExpressStatus;
import lombok.Data;
import java.util.Date;
/**
*
*
* @author liangyq
* @date 2025-04-01 11:57:52.599
**/
@Data
public class StoreOrderExpressTrackAddDTO {
/**
*
*/
private String expressWaybillNo;
/**
*
*/
private String action;
/**
*
*/
private String description;
/**
*
*/
private String remark;
/**
* ID
*/
private Long expressId;
/**
*
*/
private EExpressStatus expressStatus;
}

View File

@ -1,5 +1,6 @@
package com.ruoyi.xkt.dto.order;
import com.ruoyi.xkt.dto.express.ExpressTrackDTO;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@ -34,4 +35,6 @@ public class StoreOrderInfoDTO extends StoreOrderDTO {
private String destinationCountyName;
private List<StoreOrderDetailInfoDTO> orderDetails;
private List<ExpressTrackDTO> expressTracks;
}

View File

@ -1,9 +1,6 @@
package com.ruoyi.xkt.manager;
import com.ruoyi.xkt.dto.express.ExpressCancelReqDTO;
import com.ruoyi.xkt.dto.express.ExpressInterceptReqDTO;
import com.ruoyi.xkt.dto.express.ExpressPrintDTO;
import com.ruoyi.xkt.dto.express.ExpressShipReqDTO;
import com.ruoyi.xkt.dto.express.*;
import com.ruoyi.xkt.enums.EExpressChannel;
import java.util.Collection;
@ -52,4 +49,11 @@ public interface ExpressManager {
*/
List<ExpressPrintDTO> printOrder(Collection<String> waybillNos);
/**
*
*
* @param trackSubReq
* @return
*/
boolean subscribeTrack(ExpressTrackSubReqDTO trackSubReq);
}

View File

@ -1,15 +1,13 @@
package com.ruoyi.xkt.manager.impl;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.xkt.dto.express.ExpressCancelReqDTO;
import com.ruoyi.xkt.dto.express.ExpressInterceptReqDTO;
import com.ruoyi.xkt.dto.express.ExpressPrintDTO;
import com.ruoyi.xkt.dto.express.ExpressShipReqDTO;
import com.ruoyi.xkt.dto.express.*;
import com.ruoyi.xkt.enums.EExpressChannel;
import com.ruoyi.xkt.manager.ExpressManager;
import com.ruoyi.xkt.thirdpart.yto.*;
@ -166,6 +164,39 @@ public class YtoExpressManagerImpl implements ExpressManager {
return list;
}
@Override
public boolean subscribeTrack(ExpressTrackSubReqDTO trackSubReq) {
Assert.notNull(trackSubReq.getExpressWaybillNo());
YtoSubTrackParam.LogisticsInterface logisticsInterface = new YtoSubTrackParam.LogisticsInterface();
logisticsInterface.setClientId(appKey2);
logisticsInterface.setWaybillNo(trackSubReq.getExpressWaybillNo());
YtoSubTrackParam ytoSubTrackParam = new YtoSubTrackParam();
ytoSubTrackParam.setClient_id(appKey2);
ytoSubTrackParam.setMsg_type("online");
ytoSubTrackParam.setLogistics_interface(JSONUtil.toJsonStr(logisticsInterface));
try {
String param = JSONUtil.toJsonStr(ytoSubTrackParam);
String sign = YtoSignUtil.sign("subscribe_adapter", "v1", param, appSecret2);
YtoPublicRequest request = YtoPublicRequest.builder()
.timestamp(System.currentTimeMillis())
.param(param)
.format(YtoPublicRequest.EFormat.JSON)
.sign(sign).build();
String rtnStr = HttpUtil.post(gatewayUrl + "open/subscribe_adapter/v1/N364gM/" + appKey2,
JSONUtil.toJsonStr(request));
log.info("圆通轨迹订阅返回信息: {}", rtnStr);
JSONObject rtnJson = JSONUtil.parseObj(rtnStr);
Boolean success = rtnJson.getBool("success");
if (BooleanUtil.isTrue(success)) {
return true;
}
} catch (Exception e) {
log.error("圆通轨迹订阅异常", e);
}
log.warn("圆通轨迹订阅失败: {}", trackSubReq);
return false;
}
private YtoCreateOrderParam trans2CreateOrderReq(ExpressShipReqDTO shipReqDTO) {
YtoCreateOrderParam reqDTO = new YtoCreateOrderParam();

View File

@ -2,16 +2,14 @@ package com.ruoyi.xkt.manager.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.xkt.dto.express.ExpressCancelReqDTO;
import com.ruoyi.xkt.dto.express.ExpressInterceptReqDTO;
import com.ruoyi.xkt.dto.express.ExpressPrintDTO;
import com.ruoyi.xkt.dto.express.ExpressShipReqDTO;
import com.ruoyi.xkt.dto.express.*;
import com.ruoyi.xkt.enums.EExpressChannel;
import com.ruoyi.xkt.manager.ExpressManager;
import com.ruoyi.xkt.thirdpart.zto.*;
@ -44,6 +42,8 @@ public class ZtoExpressManagerImpl implements ExpressManager, InitializingBean {
public static final String ORDER_PRINT_URI = "zto.open.order.print";
public static final String TRACK_SUB_URI = "zto.merchant.waybill.track.subsrcibe";
@Value("${zto.appKey:}")
private String appKey;
@ -82,7 +82,7 @@ public class ZtoExpressManagerImpl implements ExpressManager, InitializingBean {
String bodyStr = client.execute(request);
log.info("中通订单创建返回信息: {}", bodyStr);
JSONObject bodyJson = JSONUtil.parseObj(bodyStr);
boolean success = bodyJson.getBool("status");
boolean success = BooleanUtil.isTrue(bodyJson.getBool("status"));
if (success) {
return bodyJson.getJSONObject("result").getStr("billCode");
}
@ -108,7 +108,7 @@ public class ZtoExpressManagerImpl implements ExpressManager, InitializingBean {
String bodyStr = client.execute(request);
log.info("中通订单取消返回信息: {}", bodyStr);
JSONObject bodyJson = JSONUtil.parseObj(bodyStr);
boolean success = bodyJson.getBool("status");
boolean success = BooleanUtil.isTrue(bodyJson.getBool("status"));
if (success) {
return true;
}
@ -137,120 +137,46 @@ public class ZtoExpressManagerImpl implements ExpressManager, InitializingBean {
String bodyStr = client.execute(request);
log.info("中通订单拦截返回信息: {}", bodyStr);
/**
* E23101
*
*
* E23102
*
*
* E23108
* 退
* destinationType
* E23110
* 使
*
* E23111
*
*
* E23112
*
*
* E23113
*
*
* E23114
*
*
* E23115
*
*
* E23116
* /
*
* E23118
* /
* platform
* E23119
*
*
* E23120
*
*
* E23121
* ,
*
* E23122
*
*
* E23123
* 退
*
* E23124
*
*
* E23127
* ,
* destinationType
* E23130
* ,
*
* E23131
* ,
*
* E23135
* ,
*
* E23136
*
*
* E23137
*
*
* E23138
*
*
* E23139
*
*
* E23140
*
*
* E23141
*
*
* E23144
* CRM
*
* E23146
* /退
*
* E23148
*
* receivePhone
* E23149
*
* receivePhone
* E23150
* 16
* receivePhone
* E23151
* ,
* destinationType
* E23153
*
*
* E23154
*
*
* E23157
*
*
* E23160
*
*
* E23101
* E23102
* E23108 退 destinationType
* E23110 使
* E23111
* E23112
* E23113
* E23114
* E23115
* E23116 /
* E23118 / platform
* E23119
* E23120
* E23121 ,
* E23122
* E23123 退
* E23124
* E23127 , destinationType
* E23130 ,
* E23131 ,
* E23135 ,
* E23136
* E23137
* E23138
* E23139
* E23140
* E23141
* E23144 CRM
* E23146 /退
* E23148 receivePhone
* E23149 receivePhone
* E23150 16 receivePhone
* E23151 , destinationType
* E23153
* E23154
* E23157
* E23160
*/
JSONObject bodyJson = JSONUtil.parseObj(bodyStr);
boolean success = bodyJson.getBool("status");
boolean success = BooleanUtil.isTrue(bodyJson.getBool("status"));
if (success) {
return true;
}
@ -274,7 +200,7 @@ public class ZtoExpressManagerImpl implements ExpressManager, InitializingBean {
String bodyStr = client.execute(request);
log.info("中通订单打印返回信息: {}", bodyStr);
JSONObject bodyJson = JSONUtil.parseObj(bodyStr);
boolean success = bodyJson.getBool("status");
boolean success = BooleanUtil.isTrue(bodyJson.getBool("status"));
if (success) {
continue;
}
@ -301,6 +227,34 @@ public class ZtoExpressManagerImpl implements ExpressManager, InitializingBean {
return list;
}
@Override
public boolean subscribeTrack(ExpressTrackSubReqDTO trackSubReq) {
Assert.notNull(trackSubReq);
Assert.notEmpty(trackSubReq.getExpressWaybillNo());
Assert.notEmpty(trackSubReq.getDestinationContactPhoneNumber());
ZtoSubTrackParam ztoSubTrackParam = new ZtoSubTrackParam();
ztoSubTrackParam.setBillCode(trackSubReq.getExpressWaybillNo());
ztoSubTrackParam.setMobilePhone(StrUtil.sub(trackSubReq.getDestinationContactPhoneNumber(),
-1, 4));
ZopPublicRequest request = new ZopPublicRequest();
request.setBody(JSONUtil.toJsonStr(ztoSubTrackParam));
request.setUrl(gatewayUrl + TRACK_SUB_URI);
request.setEncryptionType(EncryptionType.MD5);
try {
String bodyStr = client.execute(request);
log.info("中通轨迹订阅返回信息: {}", bodyStr);
JSONObject bodyJson = JSONUtil.parseObj(bodyStr);
boolean success = BooleanUtil.isTrue(bodyJson.getBool("status"));
if (success) {
return true;
}
} catch (Exception e) {
log.error("中通轨迹订阅异常", e);
}
log.warn("中通轨迹订阅失败: {}", trackSubReq);
return false;
}
/**
*
*
@ -318,7 +272,7 @@ public class ZtoExpressManagerImpl implements ExpressManager, InitializingBean {
String bodyStr = client.execute(request);
log.info("中通智能解析返回信息: {}", bodyStr);
JSONObject bodyJson = JSONUtil.parseObj(bodyStr);
boolean success = bodyJson.getBool("status");
boolean success = BooleanUtil.isTrue(bodyJson.getBool("status"));
if (success) {
return bodyJson.getJSONObject("result");
}

View File

@ -169,5 +169,10 @@ public interface IStoreOrderService {
*/
void refundSuccess(Long storeOrderId, List<Long> storeOrderDetailIds, Long operatorId);
/**
*
*
* @param trackAddDTO
*/
void addTrack(StoreOrderExpressTrackAddDTO trackAddDTO);
}

View File

@ -17,10 +17,7 @@ import com.ruoyi.common.core.domain.XktBaseEntity;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.bean.BeanValidators;
import com.ruoyi.xkt.domain.*;
import com.ruoyi.xkt.dto.express.ExpressContactDTO;
import com.ruoyi.xkt.dto.express.ExpressPrintDTO;
import com.ruoyi.xkt.dto.express.ExpressRegionDTO;
import com.ruoyi.xkt.dto.express.ExpressShipReqDTO;
import com.ruoyi.xkt.dto.express.*;
import com.ruoyi.xkt.dto.order.*;
import com.ruoyi.xkt.dto.storeProductFile.StoreProdMainPicDTO;
import com.ruoyi.xkt.enums.*;
@ -55,6 +52,8 @@ public class StoreOrderServiceImpl implements IStoreOrderService {
@Autowired
private StoreOrderDetailMapper storeOrderDetailMapper;
@Autowired
private StoreOrderExpressTrackMapper storeOrderExpressTrackMapper;
@Autowired
private StoreMapper storeMapper;
@Autowired
private StoreProductMapper storeProductMapper;
@ -627,6 +626,11 @@ public class StoreOrderServiceImpl implements IStoreOrderService {
//发货
ExpressShipReqDTO shipReq = createShipReq(order, orderDetails);
String expressWaybillNo = expressManager.shipStoreOrder(shipReq);
//订阅轨迹
ExpressTrackSubReqDTO trackSubReq = new ExpressTrackSubReqDTO(shipReq.getExpressReqNo(),
expressWaybillNo, shipReq.getOriginContactPhoneNumber(), shipReq.getDestinationContactPhoneNumber());
boolean trackSubSuccess = expressManager.subscribeTrack(trackSubReq);
Assert.isTrue(trackSubSuccess, "物流轨迹订阅失败");
List<Long> orderDetailIdList = new ArrayList<>(orderDetails.size());
for (StoreOrderDetail orderDetail : orderDetails) {
@ -1182,6 +1186,58 @@ public class StoreOrderServiceImpl implements IStoreOrderService {
financeBillService.entryRefundOrderPaymentBill(order.getId());
}
@Transactional(rollbackFor = Exception.class)
@Override
public void addTrack(StoreOrderExpressTrackAddDTO trackAddDTO) {
Assert.notNull(trackAddDTO.getExpressId());
Assert.notEmpty(trackAddDTO.getExpressWaybillNo());
List<StoreOrderDetail> storeOrderDetails = storeOrderDetailMapper.selectList(
Wrappers.lambdaQuery(StoreOrderDetail.class)
.eq(StoreOrderDetail::getExpressId, trackAddDTO.getExpressId())
.eq(StoreOrderDetail::getExpressWaybillNo, trackAddDTO.getExpressWaybillNo())
.eq(SimpleEntity::getDelFlag, Constants.UNDELETED)
);
for (StoreOrderDetail storeOrderDetail : storeOrderDetails) {
if (storeOrderDetail.getDetailStatus() > 20) {
//售后订单
continue;
}
if (EExpressStatus.PICKED_UP == trackAddDTO.getExpressStatus()) {
if (storeOrderDetail.getExpressStatus() == null) {
storeOrderDetail.setExpressStatus(EExpressStatus.PICKED_UP.getValue());
int orderDetailSuccess = storeOrderDetailMapper.updateById(storeOrderDetail);
if (orderDetailSuccess == 0) {
throw new ServiceException(Constants.VERSION_LOCK_ERROR_COMMON_MSG);
}
} else {
if (storeOrderDetail.getExpressStatus() < EExpressStatus.PICKED_UP.getValue()) {
storeOrderDetail.setExpressStatus(EExpressStatus.PICKED_UP.getValue());
int orderDetailSuccess = storeOrderDetailMapper.updateById(storeOrderDetail);
if (orderDetailSuccess == 0) {
throw new ServiceException(Constants.VERSION_LOCK_ERROR_COMMON_MSG);
}
}
}
}
if (EExpressStatus.COMPLETED == trackAddDTO.getExpressStatus()) {
storeOrderDetail.setExpressStatus(EExpressStatus.COMPLETED.getValue());
int orderDetailSuccess = storeOrderDetailMapper.updateById(storeOrderDetail);
if (orderDetailSuccess == 0) {
throw new ServiceException(Constants.VERSION_LOCK_ERROR_COMMON_MSG);
}
}
StoreOrderExpressTrack storeOrderExpressTrack = new StoreOrderExpressTrack();
storeOrderExpressTrack.setStoreOrderId(storeOrderDetail.getStoreOrderId());
storeOrderExpressTrack.setStoreOrderIdDetailId(storeOrderDetail.getId());
storeOrderExpressTrack.setSort(0);
storeOrderExpressTrack.setAction(trackAddDTO.getAction());
storeOrderExpressTrack.setDescription(trackAddDTO.getDescription());
storeOrderExpressTrack.setRemark(trackAddDTO.getRemark());
storeOrderExpressTrack.setDelFlag(Constants.UNDELETED);
storeOrderExpressTrackMapper.insert(storeOrderExpressTrack);
}
}
/**
*
*

View File

@ -1,6 +1,7 @@
package com.ruoyi.xkt.thirdpart.yto;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
/**
@ -22,10 +23,23 @@ public class YtoSignUtil {
}
public static String sign(String data, String secret) {
public static String sign(String data1, String data2) {
//进行md5加密,然后对数组进行base64编码
byte[] bytes = DigestUtil.md5(data + secret);
byte[] bytes = DigestUtil.md5(data1 + data2);
return Base64.encodeStr(bytes, false, false);
}
/**
* xml<order></order> partnerId123456
* <order></order>123456<order></order>123456MD5base64
* md5(16byte)base64LghTkEmsD2tbQ3fsIBRcBg==
*
* @param digest
* @param param
* @param secret
* @return
*/
public static boolean verify(String digest, String param, String secret) {
return StrUtil.equals(digest, sign(StrUtil.emptyIfNull(param), StrUtil.emptyIfNull(secret)));
}
}

View File

@ -0,0 +1,40 @@
package com.ruoyi.xkt.thirdpart.yto;
import lombok.Data;
/**
* @author liangyq
* @date 2025-04-29 18:11
*/
@Data
public class YtoSubTrackParam {
/**
* id,
*/
private String client_id;
/**
*
*/
private String logistics_interface;
/**
* (online)
*/
private String msg_type;
@Data
public static class LogisticsInterface{
/**
* id,
*/
private String clientId;
/**
*
*/
private String waybillNo;
/**
*
*/
private String txLogisticId;
}
}

View File

@ -0,0 +1,164 @@
package com.ruoyi.xkt.thirdpart.yto;
import cn.hutool.core.util.XmlUtil;
import com.ruoyi.common.core.text.CharsetKit;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* @author liangyq
* @date 2025-05-06 23:00
*/
@Data
public class YtoTrackObj {
private Info UpdateInfo;
@Data
public static class Info {
/**
* IDYTO
*/
private String logisticProviderID;
/**
* vip()
*/
private String clientID;
/**
*
*/
private String mailNo;
/**
*
*/
private String txLogisticID;
/**
* STATUS
*/
private String infoType;
/**
* ACCEPT ;ORDER_BOOKING ;GOT ;NOT_SEND ;ARRIVAL ;DEPARTURE ;SENT_SCAN ;INBOUND ;SIGNED ;FAILED ;TMS_RETURN 退;FORWARDING ;AIRSEND ;AIRPICK
*/
private String infoContent;
/**
*
*/
private Date acceptTime;
/**
*
*/
private String remark;
/**
*
*/
private String weight;
/**
*
*/
private String orgName;
/**
*
*/
private String orgCode;
/**
*
*/
private String orgPhone;
/**
*
*/
private String empName;
/**
*
*/
private String empCode;
/**
*
*/
private String city;
/**
*
*/
private String district;
/**
*
*/
private String signedName;
/**
*
*/
private String deliveryName;
/**
*
*/
private String contactInfo;
/**
*
*/
private String questionCause;
/**
*
*/
private Date bookingStartTime;
/**
*
*/
private Date bookingEndTime;
/**
*
*/
private String extendFields;
}
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class Response {
/**
* ID
*/
private String logisticProviderID;
/**
*
*/
private String txLogisticID;
/**
* true-;false-
*/
private Boolean success;
/**
*
*/
private String reason;
public String toXmlStr() {
return XmlUtil.toStr(XmlUtil.beanToXml(this), CharsetKit.UTF_8, false, true);
}
}
@Data
public static class Request {
/**
*
*/
private String logistics_interface;
/**
*
*/
private String data_digest;
/**
*
*/
private String clientId;
/**
* online:线offline:线
*/
private String type;
}
}

View File

@ -1,5 +1,6 @@
package com.ruoyi.xkt.thirdpart.zto;
import cn.hutool.core.util.StrUtil;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
@ -57,4 +58,9 @@ public class ZopDigestUtil {
return mac.doFinal(body.getBytes(StandardCharsets.UTF_8));
}
public static boolean verify(String digest, String param, String secret) {
return StrUtil.equals(digest, Base64.encodeBase64String(DigestUtils.md5(StrUtil.emptyIfNull(param) +
StrUtil.emptyIfNull(secret))));
}
}

View File

@ -0,0 +1,20 @@
package com.ruoyi.xkt.thirdpart.zto;
import lombok.Data;
/**
* @author liangyq
* @date 2025-04-29 18:11
*/
@Data
public class ZtoSubTrackParam {
/**
*
*/
private String billCode;
/**
* 4
*
*/
private String mobilePhone;
}

View File

@ -0,0 +1,174 @@
package com.ruoyi.xkt.thirdpart.zto;
import cn.hutool.json.JSONUtil;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author liangyq
* @date 2025-05-06 23:00
*/
@Data
public class ZtoTrackObj {
@Data
public static class Info {
/**
*
*/
private String billCode;
/**
*
* <p>
* GOT    
*  DEPARTURE    
*  ARRIVAL    
*  DISPATCH    
*  RETURN_SCAN   退   退退returnType
*  RETURN_SIGNED   退   退
*  INBOUND     //
*  HANDOVERSCAN_SIGNED
*  DEPARTURE_SIGNED   //
*  SIGNED    
*  PROBLEM     problemCode
*/
private String action;
/**
*
*/
private String facilityCode;
/**
*
*/
private String facilityName;
/**
*
*/
private String facilityContactPhone;
/**
*
*/
private String city;
/**
*
*/
private String actionTime;
/**
*
*/
private String nextNodeCode;
/**
*
*/
private String nextNodeName;
/**
*
*/
private String nextCity;
/**
*
*/
private String comCode;
/**
*
*/
private String comName;
/**
*
*/
private String desc;
/**
*
*/
private String courier;
/**
*
*/
private String courierPhone;
/**
*
*/
private String expressSigner;
/**
*
*/
private String address;
/**
*
*/
private String problemCode;
/**
* 退
*/
private String returnType;
/**
* 退
*/
private String returnUpdateType;
/**
*
*/
private String remark;
/**
* 1
*/
private String remark1;
/**
* 2
*/
private String remark2;
}
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class Response {
/**
* falsestatus=true
*/
private Boolean status;
/**
*
*/
private String message;
/**
*
*/
private String result;
/**
*
*/
private String statusCode;
public String toJsonStr() {
return JSONUtil.toJsonStr(this);
}
}
@Data
public static class Request {
/**
*
*/
private String data;
/**
*
* <p>
* data_digest=base64(md5(data+AppSecret))
*/
private String data_digest;
/**
* Traces
*/
private String msg_type;
/**
* appKey
*/
private String company_id;
}
}