feat: 订单
parent
c81c175925
commit
dd42483eae
|
|
@ -6,7 +6,7 @@ import com.ruoyi.common.core.domain.R;
|
|||
import com.ruoyi.web.controller.xkt.vo.express.ExpressRegionTreeNodeVO;
|
||||
import com.ruoyi.xkt.dto.express.ExpressRegionTreeNodeDTO;
|
||||
import com.ruoyi.xkt.manager.ExpressManager;
|
||||
import com.ruoyi.xkt.manager.impl.ZtExpressManagerImpl;
|
||||
import com.ruoyi.xkt.manager.impl.ZtoExpressManagerImpl;
|
||||
import com.ruoyi.xkt.service.IExpressService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
|
|
@ -32,7 +32,7 @@ public class ExpressController extends XktBaseController {
|
|||
@Autowired
|
||||
private List<ExpressManager> expressManagers;
|
||||
@Autowired
|
||||
private ZtExpressManagerImpl ztExpressManager;
|
||||
private ZtoExpressManagerImpl ztoExpressManager;
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('system:express:query')")
|
||||
@ApiOperation("获取行政规划树")
|
||||
|
|
|
|||
|
|
@ -171,10 +171,12 @@ express:
|
|||
county: 510114
|
||||
|
||||
# 中通配置
|
||||
zt:
|
||||
zto:
|
||||
appKey: 95dc4ecf72fce1a2fbe79
|
||||
appSecret: 694ea7e3ea89e3b6aa21aea1a0285d25
|
||||
gatewayUrl: https://japi-test.zto.com/
|
||||
accountId: test
|
||||
accountPassword: ZTO123
|
||||
|
||||
# 支付宝配置
|
||||
alipay:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
package com.ruoyi.xkt.dto.express;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author liangyq
|
||||
* @date 2025-04-16 15:24
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ExpressShipReqDTO {
|
||||
/**
|
||||
* 请求号
|
||||
*/
|
||||
private String expressReqNo;
|
||||
/**
|
||||
* 发货人-名称
|
||||
*/
|
||||
private String originContactName;
|
||||
/**
|
||||
* 发货人-电话
|
||||
*/
|
||||
private String originContactPhoneNumber;
|
||||
/**
|
||||
* 发货人-省编码
|
||||
*/
|
||||
private String originProvinceCode;
|
||||
private String originProvinceName;
|
||||
/**
|
||||
* 发货人-市编码
|
||||
*/
|
||||
private String originCityCode;
|
||||
private String originCityName;
|
||||
/**
|
||||
* 发货人-区县编码
|
||||
*/
|
||||
private String originCountyCode;
|
||||
private String originCountyName;
|
||||
/**
|
||||
* 发货人-详细地址
|
||||
*/
|
||||
private String originDetailAddress;
|
||||
/**
|
||||
* 收货人-名称
|
||||
*/
|
||||
private String destinationContactName;
|
||||
/**
|
||||
* 收货人-电话
|
||||
*/
|
||||
private String destinationContactPhoneNumber;
|
||||
/**
|
||||
* 收货人-省编码
|
||||
*/
|
||||
private String destinationProvinceCode;
|
||||
private String destinationProvinceName;
|
||||
/**
|
||||
* 收货人-市编码
|
||||
*/
|
||||
private String destinationCityCode;
|
||||
private String destinationCityName;
|
||||
/**
|
||||
* 收货人-区县编码
|
||||
*/
|
||||
private String destinationCountyCode;
|
||||
private String destinationCountyName;
|
||||
/**
|
||||
* 收货人-详细地址
|
||||
*/
|
||||
private String destinationDetailAddress;
|
||||
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@ import lombok.Getter;
|
|||
@AllArgsConstructor
|
||||
public enum EExpressChannel {
|
||||
|
||||
ZT(1, "中通", 1L, "ZT");
|
||||
ZTO(1, "中通", 1L, "ZTO");
|
||||
|
||||
private final Integer value;
|
||||
private final String label;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.ruoyi.xkt.manager;
|
||||
|
||||
import com.ruoyi.xkt.dto.express.ExpressShipReqDTO;
|
||||
import com.ruoyi.xkt.enums.EExpressChannel;
|
||||
|
||||
/**
|
||||
|
|
@ -14,4 +15,12 @@ public interface ExpressManager {
|
|||
*/
|
||||
EExpressChannel channel();
|
||||
|
||||
/**
|
||||
* 订单发货
|
||||
*
|
||||
* @param shipReqDTO
|
||||
* @return 运单号
|
||||
*/
|
||||
String shipStoreOrder(ExpressShipReqDTO shipReqDTO);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
package com.ruoyi.xkt.manager.impl;
|
||||
|
||||
import com.ruoyi.xkt.enums.EExpressChannel;
|
||||
import com.ruoyi.xkt.manager.ExpressManager;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author liangyq
|
||||
* @date 2025-04-15 15:45
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ZtExpressManagerImpl implements ExpressManager {
|
||||
|
||||
private static final String CREATE_ORDER_URI = "zto.open.createOrder";
|
||||
|
||||
private static final String STRUCTURE_ADDRESS_URI = "zto.innovate.structureNamePhoneAddress";
|
||||
|
||||
@Value("${zt.appKey:}")
|
||||
private String appKey;
|
||||
|
||||
@Value("${zt.appSecret:}")
|
||||
private String appSecret;
|
||||
|
||||
@Value("${zt.gatewayUrl:}")
|
||||
private String gatewayUrl;
|
||||
|
||||
@Override
|
||||
public EExpressChannel channel() {
|
||||
return EExpressChannel.ZT;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
package com.ruoyi.xkt.manager.impl;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.xkt.dto.express.ExpressShipReqDTO;
|
||||
import com.ruoyi.xkt.enums.EExpressChannel;
|
||||
import com.ruoyi.xkt.manager.ExpressManager;
|
||||
import com.ruoyi.xkt.thirdpart.zto.EncryptionType;
|
||||
import com.ruoyi.xkt.thirdpart.zto.ZopClient;
|
||||
import com.ruoyi.xkt.thirdpart.zto.ZopPublicRequest;
|
||||
import com.ruoyi.xkt.thirdpart.zto.ZtoCreateOrderReqDTO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author liangyq
|
||||
* @date 2025-04-15 15:45
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ZtoExpressManagerImpl implements ExpressManager {
|
||||
|
||||
private static final String CREATE_ORDER_URI = "zto.open.createOrder";
|
||||
|
||||
private static final String STRUCTURE_ADDRESS_URI = "zto.innovate.structureNamePhoneAddress";
|
||||
|
||||
@Value("${zto.appKey:}")
|
||||
private String appKey;
|
||||
|
||||
@Value("${zto.appSecret:}")
|
||||
private String appSecret;
|
||||
|
||||
@Value("${zto.gatewayUrl:}")
|
||||
private String gatewayUrl;
|
||||
|
||||
@Value("${zto.accountId:}")
|
||||
private String accountId;
|
||||
|
||||
@Value("${zto.accountPassword:}")
|
||||
private String accountPassword;
|
||||
|
||||
@Override
|
||||
public EExpressChannel channel() {
|
||||
return EExpressChannel.ZTO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String shipStoreOrder(ExpressShipReqDTO shipReqDTO) {
|
||||
Assert.notNull(shipReqDTO);
|
||||
Assert.notEmpty(shipReqDTO.getExpressReqNo());
|
||||
ZtoCreateOrderReqDTO createOrderReq = trans2CreateOrderReq(shipReqDTO);
|
||||
ZopClient client = new ZopClient(appKey, appSecret);
|
||||
ZopPublicRequest request = new ZopPublicRequest();
|
||||
request.setBody(JSONUtil.toJsonStr(createOrderReq));
|
||||
request.setUrl(gatewayUrl + CREATE_ORDER_URI);
|
||||
request.setEncryptionType(EncryptionType.MD5);
|
||||
try {
|
||||
String bodyStr = client.execute(request);
|
||||
log.info("中通订单创建返回信息: {}", bodyStr);
|
||||
JSONObject bodyJson = JSONUtil.parseObj(bodyStr);
|
||||
boolean success = bodyJson.getBool("status");
|
||||
if (success) {
|
||||
return bodyJson.getJSONObject("result").getStr("billCode");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("中通订单创建异常", e);
|
||||
}
|
||||
throw new ServiceException("中通订单创建失败");
|
||||
}
|
||||
|
||||
private ZtoCreateOrderReqDTO trans2CreateOrderReq(ExpressShipReqDTO expressShipReqDTO) {
|
||||
ZtoCreateOrderReqDTO reqDTO = new ZtoCreateOrderReqDTO();
|
||||
//合作模式 ,1:集团客户;2:非集团客户
|
||||
reqDTO.setPartnerType("2");
|
||||
//partnerType为1时,orderType:1:全网件 2:预约件。 partnerType为2时,orderType:1:全网件 2:预约件(返回运单号) 3:预约件(不返回运单号) 4:星联全网件
|
||||
reqDTO.setOrderType("1");
|
||||
//合作商订单号
|
||||
reqDTO.setPartnerOrderCode(expressShipReqDTO.getExpressReqNo());
|
||||
|
||||
//账号信息
|
||||
ZtoCreateOrderReqDTO.AccountInfo accountInfo = new ZtoCreateOrderReqDTO.AccountInfo();
|
||||
reqDTO.setAccountInfo(accountInfo);
|
||||
//电子面单账号(partnerType为2,orderType传1,2,4时必传)
|
||||
accountInfo.setAccountId(accountId);
|
||||
//电子面单密码(测试环境传ZTO123)
|
||||
accountInfo.setAccountPassword(accountPassword);
|
||||
//单号类型:1.普通电子面单;74.星联电子面单;默认是1
|
||||
accountInfo.setType(1);
|
||||
//集团客户编码(partnerType传1时必传)
|
||||
accountInfo.setCustomerId(null);
|
||||
|
||||
//发件人信息
|
||||
ZtoCreateOrderReqDTO.SenderInfo senderInfo = new ZtoCreateOrderReqDTO.SenderInfo();
|
||||
reqDTO.setSenderInfo(senderInfo);
|
||||
senderInfo.setSenderName(expressShipReqDTO.getOriginCountyName());
|
||||
senderInfo.setSenderPhone(expressShipReqDTO.getOriginContactPhoneNumber());
|
||||
senderInfo.setSenderProvince(expressShipReqDTO.getOriginProvinceName());
|
||||
senderInfo.setSenderCity(expressShipReqDTO.getOriginCityName());
|
||||
senderInfo.setSenderDistrict(expressShipReqDTO.getOriginCountyName());
|
||||
senderInfo.setSenderAddress(expressShipReqDTO.getOriginDetailAddress());
|
||||
|
||||
//收件人信息
|
||||
ZtoCreateOrderReqDTO.ReceiveInfo receiveInfo = new ZtoCreateOrderReqDTO.ReceiveInfo();
|
||||
reqDTO.setReceiveInfo(receiveInfo);
|
||||
receiveInfo.setReceiverName(expressShipReqDTO.getDestinationContactName());
|
||||
receiveInfo.setReceiverPhone(expressShipReqDTO.getDestinationContactPhoneNumber());
|
||||
receiveInfo.setReceiverProvince(expressShipReqDTO.getDestinationProvinceName());
|
||||
receiveInfo.setReceiverCity(expressShipReqDTO.getDestinationCityName());
|
||||
receiveInfo.setReceiverDistrict(expressShipReqDTO.getDestinationCountyName());
|
||||
receiveInfo.setReceiverAddress(expressShipReqDTO.getDestinationDetailAddress());
|
||||
|
||||
return reqDTO;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -93,8 +93,8 @@ public interface IStoreOrderService {
|
|||
* @param operatorId
|
||||
* @return
|
||||
*/
|
||||
StoreOrderExt prepareShipOrderByPlatform(Long storeOrderId, List<Long> storeOrderDetailIds, Long expressId,
|
||||
Long operatorId);
|
||||
StoreOrderExt shipOrderByPlatform(Long storeOrderId, List<Long> storeOrderDetailIds, Long expressId,
|
||||
Long operatorId);
|
||||
|
||||
/**
|
||||
* 发货(档口物流)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ 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.ExpressRegionDTO;
|
||||
import com.ruoyi.xkt.dto.express.ExpressShipReqDTO;
|
||||
import com.ruoyi.xkt.dto.order.*;
|
||||
import com.ruoyi.xkt.dto.storeProductFile.StoreProdMainPicDTO;
|
||||
import com.ruoyi.xkt.enums.*;
|
||||
|
|
@ -564,10 +565,10 @@ public class StoreOrderServiceImpl implements IStoreOrderService {
|
|||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public StoreOrderExt prepareShipOrderByPlatform(Long storeOrderId, List<Long> storeOrderDetailIds, Long expressId,
|
||||
Long operatorId) {
|
||||
public StoreOrderExt shipOrderByPlatform(Long storeOrderId, List<Long> storeOrderDetailIds, Long expressId,
|
||||
Long operatorId) {
|
||||
Assert.notEmpty(storeOrderDetailIds);
|
||||
// ExpressManager expressManager = getExpressManager(expressId);
|
||||
ExpressManager expressManager = getExpressManager(expressId);
|
||||
Express express = expressService.getById(expressId);
|
||||
if (!BeanValidators.exists(express) || !express.getSystemDeliverAccess()) {
|
||||
throw new ServiceException("快递[" + expressId + "]不可用");
|
||||
|
|
@ -600,10 +601,8 @@ public class StoreOrderServiceImpl implements IStoreOrderService {
|
|||
if (orderSuccess == 0) {
|
||||
throw new ServiceException(Constants.VERSION_LOCK_ERROR_COMMON_MSG);
|
||||
}
|
||||
//生成请求号
|
||||
String expressReqNo = IdUtil.simpleUUID();
|
||||
List<Long> orderDetailIdList = new ArrayList<>(orderDetails.size());
|
||||
for (StoreOrderDetail orderDetail : orderDetails) {
|
||||
//校验明细状态
|
||||
if (!BeanValidators.exists(orderDetail)) {
|
||||
throw new ServiceException("订单明细[" + orderDetail.getId() + "]不存在");
|
||||
}
|
||||
|
|
@ -613,12 +612,20 @@ public class StoreOrderServiceImpl implements IStoreOrderService {
|
|||
if (!EOrderStatus.PENDING_SHIPMENT.getValue().equals(orderDetail.getDetailStatus())) {
|
||||
throw new ServiceException("订单明细[" + order.getId() + "]当前状态无法发货");
|
||||
}
|
||||
}
|
||||
//发货
|
||||
ExpressShipReqDTO shipReq = trans2ShipReq(order, orderDetails);
|
||||
String expressWaybillNo = expressManager.shipStoreOrder(shipReq);
|
||||
|
||||
List<Long> orderDetailIdList = new ArrayList<>(orderDetails.size());
|
||||
for (StoreOrderDetail orderDetail : orderDetails) {
|
||||
//明细->已发货
|
||||
orderDetail.setDetailStatus(EOrderStatus.SHIPPED.getValue());
|
||||
orderDetail.setExpressId(expressId);
|
||||
orderDetail.setExpressType(EExpressType.PLATFORM.getValue());
|
||||
orderDetail.setExpressStatus(EExpressStatus.PLACING.getValue());
|
||||
orderDetail.setExpressReqNo(expressReqNo);
|
||||
orderDetail.setExpressStatus(EExpressStatus.PLACED.getValue());
|
||||
orderDetail.setExpressReqNo(shipReq.getExpressReqNo());
|
||||
orderDetail.setExpressWaybillNo(expressWaybillNo);
|
||||
int orderDetailSuccess = storeOrderDetailMapper.updateById(orderDetail);
|
||||
if (orderDetailSuccess == 0) {
|
||||
throw new ServiceException(Constants.VERSION_LOCK_ERROR_COMMON_MSG);
|
||||
|
|
@ -897,6 +904,27 @@ public class StoreOrderServiceImpl implements IStoreOrderService {
|
|||
throw new ServiceException("未知物流渠道");
|
||||
}
|
||||
|
||||
private ExpressShipReqDTO trans2ShipReq(StoreOrder order, List<StoreOrderDetail> orderDetails) {
|
||||
ExpressShipReqDTO reqDTO = BeanUtil.toBean(order, ExpressShipReqDTO.class);
|
||||
//生成请求号
|
||||
reqDTO.setExpressReqNo(IdUtil.simpleUUID());
|
||||
//行政区划信息
|
||||
Map<String, ExpressRegionDTO> regionMap = expressService.getRegionMapCache();
|
||||
reqDTO.setDestinationProvinceName(Optional.ofNullable(regionMap.get(order.getDestinationProvinceCode()))
|
||||
.map(ExpressRegionDTO::getParentRegionName).orElse(null));
|
||||
reqDTO.setDestinationCityName(Optional.ofNullable(regionMap.get(order.getDestinationCityCode()))
|
||||
.map(ExpressRegionDTO::getParentRegionName).orElse(null));
|
||||
reqDTO.setDestinationCountyName(Optional.ofNullable(regionMap.get(order.getDestinationCountyCode()))
|
||||
.map(ExpressRegionDTO::getParentRegionName).orElse(null));
|
||||
reqDTO.setOriginProvinceName(Optional.ofNullable(regionMap.get(order.getOriginProvinceCode()))
|
||||
.map(ExpressRegionDTO::getParentRegionName).orElse(null));
|
||||
reqDTO.setOriginCityName(Optional.ofNullable(regionMap.get(order.getOriginCityCode()))
|
||||
.map(ExpressRegionDTO::getParentRegionName).orElse(null));
|
||||
reqDTO.setOriginCountyName(Optional.ofNullable(regionMap.get(order.getOriginCountyCode()))
|
||||
.map(ExpressRegionDTO::getParentRegionName).orElse(null));
|
||||
return reqDTO;
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
package com.ruoyi.xkt.thirdpart.zto;
|
||||
|
||||
/**
|
||||
* Created By WangWei on 2022/1/18 9:27
|
||||
*/
|
||||
|
||||
public enum EncryptionType {
|
||||
MD5,
|
||||
SHA256,
|
||||
HmacSHA256
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
package com.ruoyi.xkt.thirdpart.zto;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ZopClient {
|
||||
private final ZopProperties properties;
|
||||
|
||||
public ZopClient(ZopProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public ZopClient(String appKey, String appSecret) {
|
||||
this.properties = new ZopProperties(appKey, appSecret);
|
||||
}
|
||||
|
||||
public String execute(ZopPublicRequest request) throws IOException {
|
||||
String jsonBody = request.getBody();
|
||||
if (jsonBody == null) {
|
||||
Map<String, String> params = request.getParams();
|
||||
StringBuilder queryBuilder = new StringBuilder();
|
||||
StringBuilder strToDigestBuilder = new StringBuilder();
|
||||
for (Map.Entry<String, String> e : params.entrySet()) {
|
||||
strToDigestBuilder.append(e.getKey()).append("=").append(e.getValue()).append("&");
|
||||
queryBuilder.append(urlEncode(e.getKey())).append("=").append(urlEncode(e.getValue())).append("&");
|
||||
}
|
||||
String queryString = queryBuilder.substring(0, queryBuilder.length() - 1);
|
||||
String strToDigest = strToDigestBuilder.substring(0, strToDigestBuilder.length() - 1);
|
||||
strToDigest = strToDigest + properties.getKey();
|
||||
Map<String, String> headers = new HashMap<String, String>();
|
||||
headers.put("x-companyid", properties.getCompanyId());
|
||||
headers.put("x-datadigest", ZopDigestUtil.digest(strToDigest, request.getBase64(), request.getEncryptionType(), request.getTimestamp(), request.getSecretKey()));
|
||||
if (request.getTimestamp() != null) {
|
||||
headers.put("x-timestamp", String.valueOf(request.getTimestamp()));
|
||||
}
|
||||
return ZtoHttpUtil.post(request.getUrl(), headers, queryString);
|
||||
} else {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
String strToDigest = jsonBody + properties.getKey();
|
||||
headers.put("x-companyid", properties.getCompanyId());
|
||||
headers.put("x-datadigest", ZopDigestUtil.digest(strToDigest, request.getBase64(), request.getEncryptionType(), request.getTimestamp(), request.getSecretKey()));
|
||||
if (request.getTimestamp() != null) {
|
||||
headers.put("x-timestamp", String.valueOf(request.getTimestamp()));
|
||||
}
|
||||
return ZtoHttpUtil.postJson(request.getUrl(), headers, jsonBody);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String urlEncode(String str) {
|
||||
try {
|
||||
return URLEncoder.encode(str, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package com.ruoyi.xkt.thirdpart.zto;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class ZopDigestUtil {
|
||||
|
||||
private static final String HMAC_SHA_256 = "HmacSHA256";
|
||||
|
||||
private static final Map<String, Mac> MAC_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
public static String digest(String str, Boolean isBase64, EncryptionType encryptionType, Long timestamp, String secretKey) {
|
||||
if (timestamp != null) {
|
||||
str = timestamp + str;
|
||||
}
|
||||
boolean base64 = isBase64 == null || isBase64;
|
||||
switch (encryptionType) {
|
||||
case SHA256:
|
||||
return base64 ? Base64.encodeBase64String(DigestUtils.sha256(str)) : DigestUtils.sha256Hex(str);
|
||||
case HmacSHA256:
|
||||
return base64 ? Base64.encodeBase64String(hmacSha256(secretKey, str)) : hmacSha256Str(secretKey, str);
|
||||
default:
|
||||
return base64 ? Base64.encodeBase64String(DigestUtils.md5(str)) : DigestUtils.md5Hex(str);
|
||||
}
|
||||
}
|
||||
|
||||
public static String hmacSha256Str(String key, String body) {
|
||||
return Hex.encodeHexString(hmacSha256(key, body));
|
||||
}
|
||||
|
||||
public static byte[] hmacSha256(String key, String body) {
|
||||
if (Objects.isNull(key)) {
|
||||
key = "";
|
||||
}
|
||||
Mac mac = MAC_MAP.get(key);
|
||||
if (mac == null) {
|
||||
try {
|
||||
mac = Mac.getInstance(HMAC_SHA_256);
|
||||
SecretKey secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), HMAC_SHA_256);
|
||||
mac.init(secretKey);
|
||||
MAC_MAP.put(key, mac);
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return mac.doFinal(body.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package com.ruoyi.xkt.thirdpart.zto;
|
||||
|
||||
public class ZopProperties {
|
||||
private String companyId;
|
||||
private String key;
|
||||
|
||||
public ZopProperties() {
|
||||
}
|
||||
|
||||
public ZopProperties(String appKey, String appSecret) {
|
||||
this.companyId = appKey;
|
||||
this.key = appSecret;
|
||||
}
|
||||
|
||||
public String getCompanyId() {
|
||||
return companyId;
|
||||
}
|
||||
|
||||
public void setCompanyId(String companyId) {
|
||||
this.companyId = companyId;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
package com.ruoyi.xkt.thirdpart.zto;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ZopPublicRequest {
|
||||
|
||||
private String url;
|
||||
|
||||
private Map<String, String> params = new HashMap<String, String>();
|
||||
|
||||
// 如果body有值,表示使用application/json的方式传值
|
||||
private String body;
|
||||
|
||||
/**
|
||||
* 签名是否需要base64
|
||||
*/
|
||||
private Boolean isBase64;
|
||||
|
||||
/**
|
||||
* 加密方式,MD5或者SHA256
|
||||
*/
|
||||
private EncryptionType encryptionType;
|
||||
|
||||
/**
|
||||
* HmacSHA256加密key
|
||||
*/
|
||||
private String secretKey;
|
||||
|
||||
/**
|
||||
* 时间戳,如果接口文档未标识使用时间戳请不要传值,否则会导致签名错误
|
||||
*/
|
||||
private Long timestamp;
|
||||
|
||||
public ZopPublicRequest() {
|
||||
}
|
||||
|
||||
public void setBody(String body){
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
|
||||
public String getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
public void addParam(String k, String v) {
|
||||
params.put(k, v);
|
||||
}
|
||||
|
||||
public void setData(String data) {
|
||||
try {
|
||||
JSONObject jsonObject = JSON.parseObject(data);
|
||||
for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
|
||||
params.put(entry.getKey(), entry.getValue().toString());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("JSON格式不对,请检查数据", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setDataObj(Map<String, String> data) {
|
||||
params.putAll(data);
|
||||
}
|
||||
|
||||
public void addParam(Map<String, String> p) {
|
||||
for (Map.Entry<String, String> entry : p.entrySet()) {
|
||||
params.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public Map<String, String> getParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
public void setParams(Map<String, String> params) {
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
public Boolean getBase64() {
|
||||
return isBase64;
|
||||
}
|
||||
|
||||
public void setBase64(Boolean base64) {
|
||||
isBase64 = base64;
|
||||
}
|
||||
|
||||
public EncryptionType getEncryptionType() {
|
||||
return encryptionType;
|
||||
}
|
||||
|
||||
public void setEncryptionType(EncryptionType encryptionType) {
|
||||
this.encryptionType = encryptionType;
|
||||
}
|
||||
|
||||
public String getSecretKey() {
|
||||
return secretKey;
|
||||
}
|
||||
|
||||
public void setSecretKey(String secretKey) {
|
||||
this.secretKey = secretKey;
|
||||
}
|
||||
|
||||
public Long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(Long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.ruoyi.xkt.dto.express.zt;
|
||||
package com.ruoyi.xkt.thirdpart.zto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
|
|
@ -11,7 +11,7 @@ import java.util.List;
|
|||
* @date 2025-04-15 19:20
|
||||
*/
|
||||
@Data
|
||||
public class CreateOrderReqDTO {
|
||||
public class ZtoCreateOrderReqDTO {
|
||||
/**
|
||||
* 合作模式 ,1:集团客户;2:非集团客户
|
||||
*/
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
package com.ruoyi.xkt.thirdpart.zto;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Map;
|
||||
|
||||
public class ZtoHttpUtil {
|
||||
private static final int DEFAULT_TIMEOUT = 3000;
|
||||
|
||||
|
||||
public static String post(String interfaceUrl, Map<String, String> headers, String queryString) throws IOException {
|
||||
URL url = new URL(interfaceUrl);
|
||||
HttpURLConnection con = (HttpURLConnection) url.openConnection();
|
||||
con.setRequestMethod("POST");
|
||||
con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
|
||||
con.setDoOutput(true);
|
||||
con.setConnectTimeout(DEFAULT_TIMEOUT);
|
||||
con.setReadTimeout(DEFAULT_TIMEOUT);
|
||||
for (Map.Entry<String, String> e : headers.entrySet()) {
|
||||
con.setRequestProperty(e.getKey(), e.getValue());
|
||||
}
|
||||
DataOutputStream out = null;
|
||||
|
||||
BufferedReader in = null;
|
||||
try {
|
||||
out = new DataOutputStream(con.getOutputStream());
|
||||
out.write(queryString.getBytes(Charset.forName("UTF-8")));
|
||||
out.flush();
|
||||
in = new BufferedReader(
|
||||
new InputStreamReader(con.getInputStream(), "UTF-8"));
|
||||
String inputLine;
|
||||
StringBuilder content = new StringBuilder();
|
||||
while ((inputLine = in.readLine()) != null) {
|
||||
content.append(inputLine);
|
||||
}
|
||||
return content.toString();
|
||||
} finally {
|
||||
if (out != null) {
|
||||
try {
|
||||
out.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static String postJson(String interfaceUrl, Map<String, String> headers, String json) throws IOException {
|
||||
URL url = new URL(interfaceUrl);
|
||||
HttpURLConnection con = (HttpURLConnection) url.openConnection();
|
||||
con.setRequestMethod("POST");
|
||||
con.setRequestProperty("Content-Type", "application/json; charset=utf-8");
|
||||
con.setDoOutput(true);
|
||||
con.setConnectTimeout(DEFAULT_TIMEOUT);
|
||||
con.setReadTimeout(DEFAULT_TIMEOUT);
|
||||
for (Map.Entry<String, String> e : headers.entrySet()) {
|
||||
con.setRequestProperty(e.getKey(), e.getValue());
|
||||
}
|
||||
DataOutputStream out = null;
|
||||
|
||||
BufferedReader in = null;
|
||||
try {
|
||||
out = new DataOutputStream(con.getOutputStream());
|
||||
out.write(json.getBytes(Charset.forName("UTF-8")));
|
||||
out.flush();
|
||||
in = new BufferedReader(
|
||||
new InputStreamReader(con.getInputStream(), "UTF-8"));
|
||||
String inputLine;
|
||||
StringBuilder content = new StringBuilder();
|
||||
while ((inputLine = in.readLine()) != null) {
|
||||
content.append(inputLine);
|
||||
}
|
||||
return content.toString();
|
||||
} finally {
|
||||
if (out != null) {
|
||||
try {
|
||||
out.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue