新增顺丰同城的业务代码
This commit is contained in:
parent
9da192c6a3
commit
7279e39aa2
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (c) 2024. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
* Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan.
|
||||
* Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna.
|
||||
* Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus.
|
||||
* Vestibulum commodo. Ut rhoncus gravida arcu.
|
||||
*/
|
||||
|
||||
package com.suisung.mall.shop.sfexpress.service;
|
||||
|
||||
import com.suisung.mall.common.pojo.res.SFExpressApiRes;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface SFExpressApiService {
|
||||
|
||||
/**
|
||||
* (店铺)创建顺丰同城订单
|
||||
*
|
||||
* @param shopOrderId 商家订单号
|
||||
* @return
|
||||
*/
|
||||
SFExpressApiRes createOrder(String shopOrderId);
|
||||
|
||||
|
||||
/**
|
||||
* 取消订单,当商家处发生异常需要取消配送时,可调用此接口对订单进行取消操作,同步返回结果。
|
||||
*
|
||||
* @param sfOrderId 顺丰订单号
|
||||
* @param cancelCode 取消码,参考:https://openic.sf-express.com/open/api/docs/index/#/apidoc
|
||||
* @param cancelReason 取消原因
|
||||
* @return
|
||||
*/
|
||||
SFExpressApiRes cancelOrder(String sfOrderId, Integer cancelCode, String cancelReason);
|
||||
|
||||
|
||||
/**
|
||||
* 取消订单,当商家处发生异常需要取消配送时,可调用此接口对订单进行取消操作,同步返回结果。
|
||||
*
|
||||
* @param params 综合参数,顺丰订单号必填项,请参考:https://openic.sf-express.com/open/api/docs/index/#/apidoc
|
||||
* @return
|
||||
*/
|
||||
SFExpressApiRes cancelOrder(Map<String, Object> params);
|
||||
|
||||
/**
|
||||
* 订单加小费,订单创建后,骑士未接单的情况下通过该接口对订单进行加小费,促进订单接单,截止订单完成前,都可以对订单加小费
|
||||
*
|
||||
* @param params 综合参数,顺丰订单号order_id,订单小费 gratuity_fee,必填项,请参考:https://openic.sf-express.com/open/api/docs/index/#/apidoc
|
||||
* @return
|
||||
*/
|
||||
SFExpressApiRes addOrderGratuityFee(Map<String, Object> params);
|
||||
|
||||
/**
|
||||
* 催单,当订单为配送状态中,可通过该接口发起催单
|
||||
*
|
||||
* @param params 综合参数,顺丰订单号order_id必填项,请参考:https://openic.sf-express.com/open/api/docs/index/#/apidoc
|
||||
* @return
|
||||
*/
|
||||
SFExpressApiRes reminderOrder(Map<String, Object> params);
|
||||
|
||||
/**
|
||||
* 获取配送员实时坐标接口,此接口用于获取订单配送员的实时经纬度坐标,一般情况下骑士经纬度30s更新一次。
|
||||
*
|
||||
* @param params 综合参数,顺丰订单号order_id必填项,请参考:https://openic.sf-express.com/open/api/docs/index/#/apidoc
|
||||
* @return
|
||||
*/
|
||||
SFExpressApiRes riderLatestPosition(Map<String, Object> params);
|
||||
|
||||
/**
|
||||
* 获取配送员轨迹H5,此接口可获取一个订单的骑士位置H5链接,可进行内嵌或发送给用户(内嵌时无法保证界面的兼容性,如发现兼容性问题可使用获取配送员坐标接口自行开发轨迹H5)。
|
||||
*
|
||||
* @param params 综合参数,顺丰订单号order_id必填项,请参考:https://openic.sf-express.com/open/api/docs/index/#/apidoc
|
||||
* @return
|
||||
*/
|
||||
SFExpressApiRes riderViewV2(Map<String, Object> params);
|
||||
|
||||
/**
|
||||
* 改单,支持店铺和企业客户改单,当订单生成后,可通过该接口修改收件人信息,可修改字段:收件地址、物品重量等,详情可参考请求参数列表
|
||||
*
|
||||
* @param params 综合参数,请参考:https://openic.sf-express.com/open/api/docs/index/#/apidoc
|
||||
* @return
|
||||
*/
|
||||
SFExpressApiRes changeOrder(Map<String, Object> params);
|
||||
|
||||
// 顺丰同城回调相关业务
|
||||
|
||||
/**
|
||||
* 接收顺丰原因订单取消回调
|
||||
* @param jsonData
|
||||
* @param sign
|
||||
* @return
|
||||
*/
|
||||
SFExpressApiRes receiveCancelOrderNotify(String jsonData, String sign);
|
||||
|
||||
/**
|
||||
* 接收顺丰配送状态更改回调
|
||||
* @param jsonData
|
||||
* @param sign
|
||||
* @return
|
||||
*/
|
||||
SFExpressApiRes receiveRiderOrderStatusNotify(String jsonData, String sign);
|
||||
|
||||
/**
|
||||
* 接收顺丰订单完成回调
|
||||
* @param jsonData
|
||||
* @param sign
|
||||
* @return
|
||||
*/
|
||||
SFExpressApiRes receiveOrderCompleteNotify(String jsonData, String sign);
|
||||
}
|
||||
@ -0,0 +1,418 @@
|
||||
/*
|
||||
* Copyright (c) 2024. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
* Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan.
|
||||
* Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna.
|
||||
* Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus.
|
||||
* Vestibulum commodo. Ut rhoncus gravida arcu.
|
||||
*/
|
||||
|
||||
package com.suisung.mall.shop.sfexpress.service.impl;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.suisung.mall.common.pojo.req.*;
|
||||
import com.suisung.mall.common.pojo.res.SFExpressApiRes;
|
||||
import com.suisung.mall.common.utils.JsonUtil;
|
||||
import com.suisung.mall.shop.sfexpress.service.SFExpressApiService;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class SFExpressApiServiceImpl implements SFExpressApiService {
|
||||
|
||||
private final static String sfExpressApiDomain = "https://openic.sf-express.com/open/api/external/";
|
||||
private static final Logger logger = LoggerFactory.getLogger(SFExpressApiServiceImpl.class);
|
||||
@Value("${sf-express.appid}")
|
||||
private Long appId;
|
||||
@Value("${sf-express.appkey}")
|
||||
private String appKey;
|
||||
@Value("${sf-express.dev_id}")
|
||||
private String devId;
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public SFExpressApiRes createOrder(String shopOrderId) {
|
||||
// 组织请求参数
|
||||
// Map<String, Object> params = buildCommonParams();
|
||||
// params.put("app_id", appId);
|
||||
// params.put("app_key", appKey);
|
||||
// params.put("device_id", devId);
|
||||
|
||||
Long now = DateUtil.currentSeconds();
|
||||
|
||||
SFCreateOrderReq param = new SFCreateOrderReq();
|
||||
param.setDev_id(1711573316);
|
||||
param.setVersion(19);
|
||||
param.setOrder_time(now);
|
||||
param.setPush_time(now);
|
||||
param.setRemark("测试顺丰同城发单,请不要通知骑手接单!");
|
||||
param.setOrder_sequence("000000123");
|
||||
param.setShop_id("3243279847393");
|
||||
param.setShop_order_id(shopOrderId); //"DD-20241118-00001"
|
||||
param.setReturn_flag(511);
|
||||
|
||||
SFOrderDetailReq orderDetail = new SFOrderDetailReq();
|
||||
orderDetail.setTotal_price(2000);
|
||||
orderDetail.setProduct_type(6);
|
||||
orderDetail.setWeight_gram(0); // 重量一律传 0kg 先,谢总本地协商好的
|
||||
orderDetail.setProduct_num(2);
|
||||
orderDetail.setProduct_type_num(1);
|
||||
|
||||
// 产品详情
|
||||
SFOrderProductDetailReq orderProductDetail = new SFOrderProductDetailReq();
|
||||
orderProductDetail.setProduct_id(1L);
|
||||
orderProductDetail.setProduct_name("猪腿肉500g");
|
||||
orderProductDetail.setProduct_num(2);
|
||||
|
||||
List<SFOrderProductDetailReq> orderProductList = new ArrayList<>();
|
||||
orderProductList.add(orderProductDetail);
|
||||
|
||||
orderDetail.setProduct_detail(orderProductList);
|
||||
|
||||
param.setOrder_detail(orderDetail);
|
||||
|
||||
SFOrderShopReq shop = new SFOrderShopReq();
|
||||
shop.setShop_name("顺丰同城开放平台");
|
||||
shop.setShop_address("蜂巢工场西区");
|
||||
shop.setShop_phone("13203559287");
|
||||
shop.setShop_lng("116.327914");
|
||||
shop.setShop_lat("40.045488");
|
||||
param.setShop(shop);
|
||||
|
||||
|
||||
SFOrderReceiveReq receive = new SFOrderReceiveReq();
|
||||
receive.setUser_name("顺丰同城");
|
||||
receive.setUser_phone("13881979410");
|
||||
receive.setUser_address("北京市海淀区学清嘉创大厦A座15层");
|
||||
receive.setUser_lng("116.352843");
|
||||
receive.setUser_lat("40.015028");
|
||||
param.setReceive(receive);
|
||||
|
||||
|
||||
// 转换 json 字符串参数
|
||||
String paramJSON = JsonUtil.toJSONString(param);
|
||||
|
||||
// 根据参数生成请求签名
|
||||
String send_url = buildUrl("createorder", paramJSON);
|
||||
String retRespStr = HttpUtil.post(send_url, paramJSON);
|
||||
if (StrUtil.isEmpty(retRespStr)) {
|
||||
logger.error("顺丰同城:创建订单异常,无返回值!");
|
||||
return null;
|
||||
}
|
||||
|
||||
return JsonUtil.json2object(retRespStr, SFExpressApiRes.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消订单
|
||||
*
|
||||
* @param sfOrderId 顺丰订单号
|
||||
* @param cancelCode 取消码,参考:https://openic.sf-express.com/open/api/docs/index/#/apidoc
|
||||
* @param cancelReason 取消原因
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public SFExpressApiRes cancelOrder(String sfOrderId, Integer cancelCode, String cancelReason) {
|
||||
Map<String, Object> params = buildCommonParams();
|
||||
return cancelOrder(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消订单
|
||||
*
|
||||
* @param params 综合参数,请参考:https://openic.sf-express.com/open/api/docs/index/#/apidoc
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public SFExpressApiRes cancelOrder(Map<String, Object> params) {
|
||||
if (params == null || params.get("order_id") == null) {
|
||||
return new SFExpressApiRes().fail(1003,"请求参数有误!");
|
||||
}
|
||||
|
||||
// 转换 json 字符串参数
|
||||
String paramJSON = JsonUtil.toJSONString(params);
|
||||
|
||||
// 根据参数生成请求签名
|
||||
String send_url = buildUrl("cancelorder", paramJSON);
|
||||
String retRespStr = HttpUtil.post(send_url, paramJSON);
|
||||
if (StrUtil.isEmpty(retRespStr)) {
|
||||
logger.error("顺丰同城:取消订单异常,无返回值!");
|
||||
return new SFExpressApiRes().fail(-1,"顺丰同城:无返回值!");
|
||||
}
|
||||
|
||||
return JsonUtil.json2object(retRespStr, SFExpressApiRes.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单加小费,订单创建后,骑士未接单的情况下通过该接口对订单进行加小费,促进订单接单,截止订单完成前,都可以对订单加小费
|
||||
*
|
||||
* @param params 综合参数,顺丰订单号order_id,订单小费 gratuity_fee,必填项,请参考:https://openic.sf-express.com/open/api/docs/index/#/apidoc
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public SFExpressApiRes addOrderGratuityFee(Map<String, Object> params) {
|
||||
if (params == null || params.get("order_id") == null || params.get("gratuity_fee") == null) {
|
||||
return new SFExpressApiRes().fail(1003,"请求参数有误!");
|
||||
}
|
||||
|
||||
params.putAll(buildCommonParams());
|
||||
|
||||
// 转换 json 字符串参数
|
||||
String paramJSON = JsonUtil.toJSONString(params);
|
||||
|
||||
// 根据参数生成请求签名
|
||||
String send_url = buildUrl("addordergratuityfee", paramJSON);
|
||||
String retRespStr = HttpUtil.post(send_url, paramJSON);
|
||||
if (StrUtil.isEmpty(retRespStr)) {
|
||||
logger.error("顺丰同城:订单加小费,无返回值!");
|
||||
return new SFExpressApiRes().fail(-1,"顺丰同城:无返回值!");
|
||||
}
|
||||
|
||||
return JsonUtil.json2object(retRespStr, SFExpressApiRes.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 催单,当订单为配送状态中,可通过该接口发起催单
|
||||
*
|
||||
* @param params 综合参数,顺丰订单号order_id必填项,请参考:https://openic.sf-express.com/open/api/docs/index/#/apidoc
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public SFExpressApiRes reminderOrder(Map<String, Object> params) {
|
||||
if (params == null || params.get("order_id") == null) {
|
||||
return new SFExpressApiRes().fail(1003,"请求参数有误!");
|
||||
}
|
||||
|
||||
params.putAll(buildCommonParams());
|
||||
|
||||
// 转换 json 字符串参数
|
||||
String paramJSON = JsonUtil.toJSONString(params);
|
||||
|
||||
// 根据参数生成请求签名
|
||||
String send_url = buildUrl("reminderorder", paramJSON);
|
||||
String retRespStr = HttpUtil.post(send_url, paramJSON);
|
||||
if (StrUtil.isEmpty(retRespStr)) {
|
||||
logger.error("顺丰同城:催单异常,无返回值!");
|
||||
return new SFExpressApiRes().fail(-1,"顺丰同城:无返回值!");
|
||||
}
|
||||
|
||||
return JsonUtil.json2object(retRespStr, SFExpressApiRes.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配送员实时坐标接口,此接口用于获取订单配送员的实时经纬度坐标,一般情况下骑士经纬度30s更新一次。
|
||||
*
|
||||
* @param params 综合参数,顺丰订单号order_id必填项,请参考:https://openic.sf-express.com/open/api/docs/index/#/apidoc
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public SFExpressApiRes riderLatestPosition(Map<String, Object> params) {
|
||||
if (params == null || params.get("order_id") == null) {
|
||||
return new SFExpressApiRes().fail(1003,"请求参数有误!");
|
||||
}
|
||||
|
||||
params.putAll(buildCommonParams());
|
||||
|
||||
// 转换 json 字符串参数
|
||||
String paramJSON = JsonUtil.toJSONString(params);
|
||||
|
||||
// 根据参数生成请求签名
|
||||
String send_url = buildUrl("riderlatestposition", paramJSON);
|
||||
String retRespStr = HttpUtil.post(send_url, paramJSON);
|
||||
if (StrUtil.isEmpty(retRespStr)) {
|
||||
logger.error("顺丰同城:获取配送员实时坐标异常,无返回值!");
|
||||
return new SFExpressApiRes().fail(-1,"顺丰同城:无返回值!");
|
||||
}
|
||||
|
||||
return JsonUtil.json2object(retRespStr, SFExpressApiRes.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配送员轨迹H5,此接口可获取一个订单的骑士位置H5链接,可进行内嵌或发送给用户(内嵌时无法保证界面的兼容性,如发现兼容性问题可使用获取配送员坐标接口自行开发轨迹H5)。
|
||||
*
|
||||
* @param params 综合参数,顺丰订单号order_id必填项,请参考:https://openic.sf-express.com/open/api/docs/index/#/apidoc
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public SFExpressApiRes riderViewV2(Map<String, Object> params) {
|
||||
if (params == null || params.get("order_id") == null) {
|
||||
return new SFExpressApiRes().fail(1003,"请求参数有误!");
|
||||
}
|
||||
|
||||
params.putAll(buildCommonParams());
|
||||
|
||||
// 转换 json 字符串参数
|
||||
String paramJSON = JsonUtil.toJSONString(params);
|
||||
|
||||
// 根据参数生成请求签名
|
||||
String send_url = buildUrl("riderviewv2", paramJSON);
|
||||
String retRespStr = HttpUtil.post(send_url, paramJSON);
|
||||
if (StrUtil.isEmpty(retRespStr)) {
|
||||
logger.error("顺丰同城:获取配送员轨迹H5异常,无返回值!");
|
||||
return new SFExpressApiRes().fail(-1,"顺丰同城:无返回值!");
|
||||
}
|
||||
|
||||
return JsonUtil.json2object(retRespStr, SFExpressApiRes.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 改单,支持店铺和企业客户改单,当订单生成后,可通过该接口修改收件人信息,可修改字段:收件地址、物品重量等,详情可参考请求参数列表
|
||||
*
|
||||
* @param params 综合参数,请参考:https://openic.sf-express.com/open/api/docs/index/#/apidoc
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public SFExpressApiRes changeOrder(Map<String, Object> params) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 接收顺丰原因订单取消回调
|
||||
*
|
||||
* @param jsonData
|
||||
* @param sign
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public SFExpressApiRes receiveCancelOrderNotify(String jsonData, String sign) {
|
||||
if (StrUtil.isBlank(jsonData) || StrUtil.isBlank(sign)) {
|
||||
return new SFExpressApiRes().fail(1003, "缺少必要参数!");
|
||||
}
|
||||
|
||||
if (!checkOpenSign(sign, jsonData)) {
|
||||
return new SFExpressApiRes().fail(2002, "请求签名sign校验失败!");
|
||||
}
|
||||
|
||||
logger.info("接收顺丰原因订单取消回调返回的 JSON 数据:{}", jsonData);
|
||||
|
||||
return new SFExpressApiRes().success("success");
|
||||
}
|
||||
|
||||
/**
|
||||
* 接收顺丰配送状态更改回调
|
||||
*
|
||||
* @param jsonData
|
||||
* @param sign
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public SFExpressApiRes receiveRiderOrderStatusNotify(String jsonData, String sign) {
|
||||
if (StrUtil.isBlank(jsonData) || StrUtil.isBlank(sign)) {
|
||||
return new SFExpressApiRes().fail(1003, "缺少必要参数!");
|
||||
}
|
||||
|
||||
if (!checkOpenSign(sign, jsonData)) {
|
||||
return new SFExpressApiRes().fail(2002, "请求签名sign校验失败!");
|
||||
}
|
||||
|
||||
logger.info("接收顺丰原因订单取消回调返回的 JSON 数据:{}", jsonData);
|
||||
|
||||
return new SFExpressApiRes().success("success");
|
||||
}
|
||||
|
||||
/**
|
||||
* 接收顺丰订单完成回调
|
||||
*
|
||||
* @param jsonData
|
||||
* @param sign
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public SFExpressApiRes receiveOrderCompleteNotify(String jsonData, String sign) {
|
||||
if (StrUtil.isBlank(jsonData) || StrUtil.isBlank(sign)) {
|
||||
return new SFExpressApiRes().fail(1003, "缺少必要参数!");
|
||||
}
|
||||
|
||||
if (!checkOpenSign(sign, jsonData)) {
|
||||
return new SFExpressApiRes().fail(2002, "请求签名sign校验失败!");
|
||||
}
|
||||
|
||||
logger.info("接收顺丰原因订单取消回调返回的 JSON 数据:{}", jsonData);
|
||||
|
||||
return new SFExpressApiRes().success("success");
|
||||
}
|
||||
|
||||
|
||||
// 私有方法
|
||||
|
||||
/**
|
||||
* 生成顺丰同城请求签名,参考官网:https://commit-openic.sf-express.com/#/apidoc
|
||||
*
|
||||
* @param postData
|
||||
* @return
|
||||
*/
|
||||
private String generateOpenSign(String postData) {
|
||||
try {
|
||||
String sb = postData +
|
||||
"&" + appId + "&" + appKey;
|
||||
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
byte[] md5 = md.digest(sb.getBytes(StandardCharsets.UTF_8));
|
||||
int i;
|
||||
StringBuffer buf = new StringBuffer();
|
||||
for (byte b : md5) {
|
||||
i = b;
|
||||
if (i < 0) {
|
||||
i += 256;
|
||||
}
|
||||
if (i < 16) {
|
||||
buf.append("0");
|
||||
}
|
||||
buf.append(Integer.toHexString(i));
|
||||
}
|
||||
return Base64.encodeBase64String(buf.toString().getBytes(StandardCharsets.UTF_8));
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage());
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证签名是否一致
|
||||
*
|
||||
* @param sign
|
||||
* @param postData
|
||||
* @return
|
||||
*/
|
||||
private boolean checkOpenSign(String sign, String postData) {
|
||||
if (StrUtil.isBlank(sign) || StrUtil.isBlank(postData)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String newSign = generateOpenSign(postData);
|
||||
return sign.equals(newSign);
|
||||
}
|
||||
|
||||
private Map<String, Object> buildCommonParams() {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("dev_id", devId);
|
||||
params.put("push_time", DateUtil.currentSeconds());
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装请求地址
|
||||
*
|
||||
* @param urlPath
|
||||
* @param postData
|
||||
* @return
|
||||
*/
|
||||
private String buildUrl(String urlPath, String postData) {
|
||||
String sb = sfExpressApiDomain + urlPath +
|
||||
"?sign=" +
|
||||
generateOpenSign(postData);
|
||||
return sb;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user