no message
This commit is contained in:
parent
d59b15c569
commit
ae2f59d183
|
|
@ -18,6 +18,7 @@ import com.ghy.order.request.AppOrderAssignRequest;
|
|||
import com.ghy.order.request.AppOrderRequest;
|
||||
import com.ghy.order.request.SysOrderAssignRequest;
|
||||
import com.ghy.order.request.SysOrderGoodsStandards;
|
||||
import com.ghy.order.request.LogisticsQueryRequest;
|
||||
import com.ghy.order.service.*;
|
||||
import com.ghy.payment.domain.FinancialChangeRecord;
|
||||
import com.ghy.payment.domain.FinancialDetail;
|
||||
|
|
@ -111,6 +112,8 @@ public class OrderController extends BaseController {
|
|||
@Resource
|
||||
private IInsuranceManagerService insuranceManagerService;
|
||||
|
||||
@Resource
|
||||
private LogisticsService logisticsService;
|
||||
|
||||
@GetMapping("/imgs")
|
||||
public String orderImgs(Long orderId, ModelMap mmap) {
|
||||
|
|
@ -2154,4 +2157,58 @@ public class OrderController extends BaseController {
|
|||
return AjaxResult.error("生成服务订单失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据快递单号查询物流信息
|
||||
*
|
||||
* @param request 物流查询请求
|
||||
* @return 物流信息
|
||||
*/
|
||||
@PostMapping("/logistics/query")
|
||||
@ResponseBody
|
||||
public AjaxResult queryLogistics(@RequestBody LogisticsQueryRequest request) {
|
||||
try {
|
||||
LogisticsInfo logisticsInfo = logisticsService.queryLogistics(request);
|
||||
return AjaxResult.success("查询成功", logisticsInfo);
|
||||
} catch (Exception e) {
|
||||
logger.error("查询物流信息失败: {}", e.getMessage(), e);
|
||||
return AjaxResult.error("查询物流信息失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据快递单号查询物流信息(简化版)
|
||||
*
|
||||
* @param trackingNumber 快递单号
|
||||
* @return 物流信息
|
||||
*/
|
||||
@GetMapping("/logistics/query/{trackingNumber}")
|
||||
@ResponseBody
|
||||
public AjaxResult queryLogisticsByNumber(@PathVariable String trackingNumber) {
|
||||
try {
|
||||
LogisticsInfo logisticsInfo = logisticsService.queryLogistics(trackingNumber);
|
||||
return AjaxResult.success("查询成功", logisticsInfo);
|
||||
} catch (Exception e) {
|
||||
logger.error("查询物流信息失败: {}", e.getMessage(), e);
|
||||
return AjaxResult.error("查询物流信息失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据订单ID查询物流信息
|
||||
*
|
||||
* @param orderId 订单ID
|
||||
* @return 物流信息
|
||||
*/
|
||||
@GetMapping("/logistics/query/order/{orderId}")
|
||||
@ResponseBody
|
||||
public AjaxResult queryLogisticsByOrderId(@PathVariable Long orderId) {
|
||||
try {
|
||||
LogisticsInfo logisticsInfo = logisticsService.queryLogisticsByOrderId(orderId);
|
||||
return AjaxResult.success("查询成功", logisticsInfo);
|
||||
} catch (Exception e) {
|
||||
logger.error("查询物流信息失败: {}", e.getMessage(), e);
|
||||
return AjaxResult.error("查询物流信息失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import com.ghy.order.domain.*;
|
|||
import com.ghy.order.request.OrderChangePriceReq;
|
||||
import com.ghy.order.request.SysOrderAssignRequest;
|
||||
import com.ghy.order.request.SysOrderGoodsStandards;
|
||||
import com.ghy.order.request.TransferOrderRequest;
|
||||
import com.ghy.order.service.*;
|
||||
import com.ghy.payment.domain.FinancialChangeRecord;
|
||||
import com.ghy.payment.domain.FinancialDetail;
|
||||
|
|
@ -242,86 +243,149 @@ public class OrderMasterController extends BaseController {
|
|||
/**
|
||||
* 订单转单功能
|
||||
*
|
||||
* @param orderMaster 订单对象,包含id、workerId(新师傅)、originalWorkerId等字段
|
||||
* @param request 转单请求对象
|
||||
* @return 操作结果
|
||||
*/
|
||||
@PostMapping("/transferOrder")
|
||||
@ResponseBody
|
||||
@Log(title = "订单转单管理", businessType = BusinessType.UPDATE)
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public AjaxResult transferOrder(@RequestBody OrderMaster orderMaster)
|
||||
public AjaxResult transferOrder(@RequestBody TransferOrderRequest request)
|
||||
{
|
||||
try {
|
||||
// 参数校验
|
||||
if (orderMaster.getId() == null) {
|
||||
if (request.getOrderId() == null) {
|
||||
return AjaxResult.error("订单ID不能为空");
|
||||
}
|
||||
if (orderMaster.getWorkerId() == null) {
|
||||
return AjaxResult.error("新师傅ID不能为空");
|
||||
}
|
||||
|
||||
// 查询原订单信息
|
||||
OrderMaster originalOrder = orderMasterService.selectById(orderMaster.getId());
|
||||
OrderMaster originalOrder = orderMasterService.selectById(request.getOrderId());
|
||||
if (originalOrder == null) {
|
||||
return AjaxResult.error("订单不存在");
|
||||
}
|
||||
|
||||
// // 检查订单状态是否允许转单
|
||||
// if (originalOrder.getOrderStatus() == null || originalOrder.getOrderStatus() > OrderStatus.RECEIVE.code()) {
|
||||
// return AjaxResult.error("订单状态不允许转单,只有待接单、已接单、待上门、进行中状态的订单可以转单");
|
||||
// }
|
||||
|
||||
// 检查新师傅是否存在
|
||||
Worker newWorker = workerService.selectById(orderMaster.getWorkerId());
|
||||
if (newWorker == null) {
|
||||
return AjaxResult.error("新师傅不存在");
|
||||
// 检查订单状态是否允许转单
|
||||
if (originalOrder.getOrderStatus() == null || originalOrder.getOrderStatus() > OrderStatus.RECEIVE.code()) {
|
||||
return AjaxResult.error("订单状态不允许转单,只有待接单、已接单、待上门、进行中状态的订单可以转单");
|
||||
}
|
||||
|
||||
// 如果原师傅和新师傅相同,则不允许转单
|
||||
if (originalOrder.getWorkerId() != null && originalOrder.getWorkerId().equals(orderMaster.getWorkerId())) {
|
||||
return AjaxResult.error("新师傅不能与原师傅相同");
|
||||
// 如果指定了新师傅,检查新师傅是否存在
|
||||
Worker newWorker = null;
|
||||
if (request.getNewWorkerId() != null) {
|
||||
newWorker = workerService.selectById(request.getNewWorkerId());
|
||||
if (newWorker == null) {
|
||||
return AjaxResult.error("新师傅不存在");
|
||||
}
|
||||
|
||||
// 如果原师傅和新师傅相同,则不允许转单
|
||||
if (originalOrder.getWorkerId() != null && originalOrder.getWorkerId().equals(request.getNewWorkerId())) {
|
||||
return AjaxResult.error("新师傅不能与原师傅相同");
|
||||
}
|
||||
}
|
||||
|
||||
// 查询原财务主单
|
||||
FinancialMaster financialMaster = financialMasterService.selectByOrderId(request.getOrderId());
|
||||
if (financialMaster == null) {
|
||||
return AjaxResult.error("订单财务信息不存在");
|
||||
}
|
||||
|
||||
// 处理转单金额
|
||||
if (request.getTransferAmount() != null) {
|
||||
BigDecimal originalAmount = financialMaster.getServerMoney();
|
||||
BigDecimal transferAmount = request.getTransferAmount();
|
||||
|
||||
// 验证转单金额
|
||||
if (transferAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
return AjaxResult.error("转单金额必须大于0");
|
||||
}
|
||||
if (transferAmount.compareTo(originalAmount) > 0) {
|
||||
return AjaxResult.error("转单金额不能超过原订单金额");
|
||||
}
|
||||
|
||||
// 计算差价
|
||||
BigDecimal differenceAmount = originalAmount.subtract(transferAmount);
|
||||
|
||||
// 更新主单金额
|
||||
financialMaster.setServerMoney(transferAmount);
|
||||
financialMaster.setMoney(transferAmount);
|
||||
financialMaster.setPayMoney(transferAmount);
|
||||
financialMaster.setRemark("订单转单,原金额:" + originalAmount + ",新金额:" + transferAmount);
|
||||
financialMasterService.updateFinancialMaster(financialMaster);
|
||||
|
||||
// 如果有差价,创建差额子单记录
|
||||
if (differenceAmount.compareTo(BigDecimal.ZERO) > 0 && originalOrder.getWorkerId() != null) {
|
||||
FinancialDetail differenceDetail = new FinancialDetail();
|
||||
differenceDetail.setFinancialMasterId(financialMaster.getId());
|
||||
differenceDetail.setWorkerId(originalOrder.getWorkerId()); // 原师傅
|
||||
differenceDetail.setMoney(differenceAmount);
|
||||
differenceDetail.setFinancialDetailType(FinancialDetailType.TRANSFER_DIFFERENCE_FEE.getCode());
|
||||
differenceDetail.setRemark("转单差价,原师傅获得");
|
||||
financialDetailService.insertFinancialDetail(differenceDetail);
|
||||
|
||||
logger.info("创建转单差价记录:订单ID={}, 原师傅ID={}, 差价金额={}",
|
||||
request.getOrderId(), originalOrder.getWorkerId(), differenceAmount);
|
||||
}
|
||||
}
|
||||
|
||||
// 执行转单操作
|
||||
OrderMaster updateOrder = new OrderMaster();
|
||||
updateOrder.setId(orderMaster.getId());
|
||||
updateOrder.setId(request.getOrderId());
|
||||
|
||||
// 保存原师傅ID到originalWorkerId字段
|
||||
if (originalOrder.getWorkerId() != null) {
|
||||
updateOrder.setOriginalWorkerId(originalOrder.getWorkerId());
|
||||
}
|
||||
|
||||
// 设置新师傅ID
|
||||
updateOrder.setWorkerId(orderMaster.getWorkerId());
|
||||
// 设置新师傅ID(可以为null,表示进入接单大厅)
|
||||
updateOrder.setWorkerId(request.getNewWorkerId());
|
||||
updateOrder.setUpdateBy(getLoginName());
|
||||
|
||||
// 如果有转单备注,添加到原有备注中
|
||||
// 构建转单备注
|
||||
String newRemark = originalOrder.getRemark() != null ? originalOrder.getRemark() : "";
|
||||
if (StringUtils.isNotEmpty(orderMaster.getRemark())) {
|
||||
newRemark += "【转单记录:" + getLoginName() +
|
||||
"于" + DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss", new Date()) +
|
||||
"转单给师傅ID:" + orderMaster.getWorkerId() +
|
||||
",备注:" + orderMaster.getRemark() + "】";
|
||||
String transferInfo = "【转单记录:" + getLoginName() +
|
||||
"于" + DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss", new Date());
|
||||
|
||||
if (request.getNewWorkerId() != null) {
|
||||
transferInfo += "转单给师傅ID:" + request.getNewWorkerId();
|
||||
} else {
|
||||
newRemark += "【转单记录:" + getLoginName() +
|
||||
"于" + DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss", new Date()) +
|
||||
"转单给师傅ID:" + orderMaster.getWorkerId() + "】";
|
||||
transferInfo += "转单到接单大厅";
|
||||
}
|
||||
updateOrder.setRemark(newRemark);
|
||||
|
||||
if (request.getTransferAmount() != null) {
|
||||
transferInfo += ",转单金额:" + request.getTransferAmount();
|
||||
}
|
||||
|
||||
if (StringUtils.isNotEmpty(request.getTransferReason())) {
|
||||
transferInfo += ",原因:" + request.getTransferReason();
|
||||
}
|
||||
|
||||
transferInfo += "】";
|
||||
updateOrder.setRemark(newRemark + transferInfo);
|
||||
|
||||
int result = orderMasterService.updateOrderMaster(updateOrder);
|
||||
|
||||
if (result > 0) {
|
||||
// 记录转单日志
|
||||
logger.info("订单转单成功:订单ID={}, 原师傅ID={}, 新师傅ID={}, 操作人={}",
|
||||
orderMaster.getId(),
|
||||
logger.info("订单转单成功:订单ID={}, 原师傅ID={}, 新师傅ID={}, 转单金额={}, 操作人={}",
|
||||
request.getOrderId(),
|
||||
originalOrder.getWorkerId(),
|
||||
orderMaster.getWorkerId(),
|
||||
request.getNewWorkerId(),
|
||||
request.getTransferAmount(),
|
||||
getLoginName());
|
||||
|
||||
String message = String.format("转单成功!订单已从师傅[%s]转给师傅[%s]",
|
||||
originalOrder.getWorkerId() != null ? originalOrder.getWorkerId() : "无",
|
||||
newWorker.getName());
|
||||
String message;
|
||||
if (request.getNewWorkerId() != null) {
|
||||
message = String.format("转单成功!订单已从师傅[%s]转给师傅[%s]",
|
||||
originalOrder.getWorkerId() != null ? originalOrder.getWorkerId() : "无",
|
||||
newWorker.getName());
|
||||
} else {
|
||||
message = "转单成功!订单已转入接单大厅";
|
||||
}
|
||||
|
||||
if (request.getTransferAmount() != null) {
|
||||
message += ",转单金额:" + request.getTransferAmount();
|
||||
}
|
||||
|
||||
return AjaxResult.success(message);
|
||||
} else {
|
||||
return AjaxResult.error("转单失败,请重试");
|
||||
|
|
|
|||
|
|
@ -145,3 +145,14 @@ aliyun:
|
|||
accessSecret: EV4dzWRfKTQaPRjf3tFziMuVBCsThU
|
||||
endpoint: dytnsapi.aliyuncs.com
|
||||
authCode: od2FgE9a9g
|
||||
|
||||
# 物流API配置
|
||||
logistics:
|
||||
kdniao:
|
||||
# 快递鸟 API配置
|
||||
appId: '1889454' # 快递鸟应用ID,需要到快递鸟官网申请
|
||||
appKey: 'b2483529-807d-49af-b0e1-1ed218baa4db' # 快递鸟API密钥,需要到快递鸟官网申请
|
||||
url: 'https://api.kdniao.com/api/dist' # 快递鸟即时查询接口地址
|
||||
# 快递鸟支持两种接口:
|
||||
# 1. 即时查询接口:https://api.kdniao.com/api/dist
|
||||
# 2. 物流跟踪接口:https://api.kdniao.com/Ebusiness/EbusinessOrderHandle.aspx
|
||||
|
|
@ -13,7 +13,8 @@ public enum FinancialDetailType {
|
|||
PLACE_FEE(3, "分销金额,可能存在多级"),
|
||||
RETURN_FEE(4, "退款金额"),
|
||||
FINE_FEE(5, "超时罚金"),
|
||||
COMMISSION_FEE(6,"手续费");
|
||||
COMMISSION_FEE(6,"手续费"),
|
||||
TRANSFER_DIFFERENCE_FEE(7, "转单差价");
|
||||
|
||||
private final Integer code;
|
||||
private final String desc;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,90 @@
|
|||
package com.ghy.order.domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 物流信息实体类
|
||||
*
|
||||
* @author clunt
|
||||
*/
|
||||
@Data
|
||||
public class LogisticsInfo {
|
||||
|
||||
/**
|
||||
* 快递单号
|
||||
*/
|
||||
private String trackingNumber;
|
||||
|
||||
/**
|
||||
* 快递公司编码
|
||||
*/
|
||||
private String expressCode;
|
||||
|
||||
/**
|
||||
* 快递公司名称
|
||||
*/
|
||||
private String expressName;
|
||||
|
||||
/**
|
||||
* 物流状态:0=在途,1=揽收,2=疑难,3=签收,4=退签,5=派件,6=退回
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 物流状态描述
|
||||
*/
|
||||
private String statusDesc;
|
||||
|
||||
/**
|
||||
* 物流轨迹信息
|
||||
*/
|
||||
private List<LogisticsTrace> traces;
|
||||
|
||||
/**
|
||||
* 查询时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date queryTime;
|
||||
|
||||
/**
|
||||
* 是否查询成功
|
||||
*/
|
||||
private Boolean success;
|
||||
|
||||
/**
|
||||
* 错误信息
|
||||
*/
|
||||
private String errorMsg;
|
||||
|
||||
/**
|
||||
* 物流轨迹详情
|
||||
*/
|
||||
@Data
|
||||
public static class LogisticsTrace {
|
||||
|
||||
/**
|
||||
* 时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date time;
|
||||
|
||||
/**
|
||||
* 地点
|
||||
*/
|
||||
private String location;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private String status;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package com.ghy.order.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 物流查询请求类
|
||||
*
|
||||
* @author clunt
|
||||
*/
|
||||
@Data
|
||||
public class LogisticsQueryRequest {
|
||||
|
||||
/**
|
||||
* 快递单号
|
||||
*/
|
||||
@NotBlank(message = "快递单号不能为空")
|
||||
private String trackingNumber;
|
||||
|
||||
/**
|
||||
* 快递公司编码(可选,如果不传会自动识别)
|
||||
*/
|
||||
private String expressCode;
|
||||
|
||||
/**
|
||||
* 快递公司名称(可选,如果不传会自动识别)
|
||||
*/
|
||||
private String expressName;
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package com.ghy.order.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 转单请求类
|
||||
*
|
||||
* @author clunt
|
||||
*/
|
||||
@Data
|
||||
public class TransferOrderRequest {
|
||||
|
||||
/**
|
||||
* 订单ID
|
||||
*/
|
||||
@NotNull(message = "订单ID不能为空")
|
||||
private Long orderId;
|
||||
|
||||
/**
|
||||
* 新师傅ID(可以为null,表示进入接单大厅)
|
||||
*/
|
||||
private Long newWorkerId;
|
||||
|
||||
/**
|
||||
* 转单金额(新师傅承担,可以为null表示无转单金额)
|
||||
*/
|
||||
private BigDecimal transferAmount;
|
||||
|
||||
/**
|
||||
* 转单原因
|
||||
*/
|
||||
private String transferReason;
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package com.ghy.order.service;
|
||||
|
||||
import com.ghy.order.domain.LogisticsInfo;
|
||||
import com.ghy.order.request.LogisticsQueryRequest;
|
||||
|
||||
/**
|
||||
* 物流服务接口
|
||||
*
|
||||
* @author clunt
|
||||
*/
|
||||
public interface LogisticsService {
|
||||
|
||||
/**
|
||||
* 根据快递单号查询物流信息
|
||||
*
|
||||
* @param request 物流查询请求
|
||||
* @return 物流信息
|
||||
*/
|
||||
LogisticsInfo queryLogistics(LogisticsQueryRequest request);
|
||||
|
||||
/**
|
||||
* 根据快递单号查询物流信息
|
||||
*
|
||||
* @param trackingNumber 快递单号
|
||||
* @return 物流信息
|
||||
*/
|
||||
LogisticsInfo queryLogistics(String trackingNumber);
|
||||
|
||||
/**
|
||||
* 根据快递单号和快递公司编码查询物流信息
|
||||
*
|
||||
* @param trackingNumber 快递单号
|
||||
* @param expressCode 快递公司编码
|
||||
* @return 物流信息
|
||||
*/
|
||||
LogisticsInfo queryLogistics(String trackingNumber, String expressCode);
|
||||
|
||||
/**
|
||||
* 根据订单ID查询物流信息
|
||||
*
|
||||
* @param orderId 订单ID
|
||||
* @return 物流信息
|
||||
*/
|
||||
LogisticsInfo queryLogisticsByOrderId(Long orderId);
|
||||
}
|
||||
|
|
@ -0,0 +1,246 @@
|
|||
package com.ghy.order.service.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.ghy.order.domain.LogisticsInfo;
|
||||
import com.ghy.order.domain.OrderMaster;
|
||||
import com.ghy.order.request.LogisticsQueryRequest;
|
||||
import com.ghy.order.service.LogisticsService;
|
||||
import com.ghy.order.service.OrderMasterService;
|
||||
import com.ghy.order.utils.LogisticsUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 物流服务实现类
|
||||
*
|
||||
* @author clunt
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class LogisticsServiceImpl implements LogisticsService {
|
||||
|
||||
@Autowired
|
||||
private OrderMasterService orderMasterService;
|
||||
|
||||
/**
|
||||
* 快递鸟 API配置
|
||||
*/
|
||||
@Value("${logistics.kdniao.appId:}")
|
||||
private String appId;
|
||||
|
||||
@Value("${logistics.kdniao.appKey:}")
|
||||
private String appKey;
|
||||
|
||||
@Value("${logistics.kdniao.url:https://api.kdniao.com/api/dist}")
|
||||
private String apiUrl;
|
||||
|
||||
@Override
|
||||
public LogisticsInfo queryLogistics(LogisticsQueryRequest request) {
|
||||
return queryLogistics(request.getTrackingNumber(), request.getExpressCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public LogisticsInfo queryLogistics(String trackingNumber) {
|
||||
return queryLogistics(trackingNumber, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LogisticsInfo queryLogistics(String trackingNumber, String expressCode) {
|
||||
LogisticsInfo logisticsInfo = new LogisticsInfo();
|
||||
logisticsInfo.setTrackingNumber(trackingNumber);
|
||||
logisticsInfo.setQueryTime(new Date());
|
||||
|
||||
try {
|
||||
// 验证快递单号格式
|
||||
if (!LogisticsUtils.isValidTrackingNumber(trackingNumber)) {
|
||||
logisticsInfo.setSuccess(false);
|
||||
logisticsInfo.setErrorMsg("快递单号格式不正确");
|
||||
return logisticsInfo;
|
||||
}
|
||||
|
||||
// 如果没有指定快递公司,先识别快递公司
|
||||
if (StrUtil.isBlank(expressCode)) {
|
||||
expressCode = LogisticsUtils.identifyExpressCompany(trackingNumber);
|
||||
}
|
||||
|
||||
// 调用快递鸟 API查询物流信息
|
||||
JSONObject result = callKdniaoAPI(trackingNumber, expressCode);
|
||||
|
||||
if (result.getBool("Success", false)) {
|
||||
// 查询成功
|
||||
logisticsInfo.setSuccess(true);
|
||||
logisticsInfo.setExpressCode(expressCode);
|
||||
logisticsInfo.setExpressName(result.getStr("ShipperName"));
|
||||
logisticsInfo.setStatus(getKdniaoStatus(result.getStr("State")));
|
||||
logisticsInfo.setStatusDesc(result.getStr("State"));
|
||||
|
||||
// 解析物流轨迹
|
||||
List<LogisticsInfo.LogisticsTrace> traces = new ArrayList<>();
|
||||
JSONArray tracesArray = result.getJSONArray("Traces");
|
||||
if (tracesArray != null) {
|
||||
for (int i = 0; i < tracesArray.size(); i++) {
|
||||
JSONObject trace = tracesArray.getJSONObject(i);
|
||||
LogisticsInfo.LogisticsTrace logisticsTrace = new LogisticsInfo.LogisticsTrace();
|
||||
logisticsTrace.setTime(trace.getDate("AcceptTime"));
|
||||
logisticsTrace.setLocation(trace.getStr("AcceptStation"));
|
||||
logisticsTrace.setDescription(trace.getStr("AcceptStation"));
|
||||
logisticsTrace.setStatus(trace.getStr("Remark"));
|
||||
traces.add(logisticsTrace);
|
||||
}
|
||||
}
|
||||
logisticsInfo.setTraces(traces);
|
||||
} else {
|
||||
// 查询失败
|
||||
logisticsInfo.setSuccess(false);
|
||||
logisticsInfo.setErrorMsg(result.getStr("Reason", "查询失败"));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("查询物流信息失败,快递单号:{},错误:{}", trackingNumber, e.getMessage(), e);
|
||||
logisticsInfo.setSuccess(false);
|
||||
logisticsInfo.setErrorMsg("查询物流信息失败:" + e.getMessage());
|
||||
}
|
||||
|
||||
return logisticsInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LogisticsInfo queryLogisticsByOrderId(Long orderId) {
|
||||
OrderMaster orderMaster = orderMasterService.selectById(orderId);
|
||||
if (orderMaster == null || StrUtil.isBlank(orderMaster.getTrackingNumber())) {
|
||||
LogisticsInfo logisticsInfo = new LogisticsInfo();
|
||||
logisticsInfo.setSuccess(false);
|
||||
logisticsInfo.setErrorMsg("订单不存在或没有快递单号");
|
||||
return logisticsInfo;
|
||||
}
|
||||
|
||||
return queryLogistics(orderMaster.getTrackingNumber());
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用快递鸟 API
|
||||
*
|
||||
* @param trackingNumber 快递单号
|
||||
* @param expressCode 快递公司编码
|
||||
* @return API返回结果
|
||||
*/
|
||||
private JSONObject callKdniaoAPI(String trackingNumber, String expressCode) {
|
||||
try {
|
||||
// 构建请求数据
|
||||
JSONObject requestData = new JSONObject();
|
||||
requestData.set("ShipperCode", expressCode);
|
||||
requestData.set("LogisticCode", trackingNumber);
|
||||
|
||||
String requestDataStr = requestData.toString();
|
||||
|
||||
// 计算数据签名 - 按照快递鸟官方文档
|
||||
// S1: RequestData(未编码) + ApiKey
|
||||
String s1 = requestDataStr + appKey;
|
||||
log.info("S1: {}", s1);
|
||||
|
||||
// S2: MD5加密(32位小写)
|
||||
String s2 = cn.hutool.crypto.digest.DigestUtil.md5Hex(s1);
|
||||
log.info("S2: {}", s2);
|
||||
|
||||
// S3: Base64编码
|
||||
String s3 = java.util.Base64.getEncoder().encodeToString(s2.getBytes("UTF-8"));
|
||||
log.info("S3: {}", s3);
|
||||
|
||||
// S4: URL编码(仅请求接口需要)
|
||||
String s4 = java.net.URLEncoder.encode(s3, "UTF-8");
|
||||
log.info("S4: {}", s4);
|
||||
|
||||
// 构建请求参数 - 即时查询接口使用GET请求
|
||||
Map<String, Object> requestParam = new HashMap<>();
|
||||
requestParam.put("RequestData", requestDataStr);
|
||||
requestParam.put("EBusinessID", appId);
|
||||
requestParam.put("RequestType", "8002"); // 即时查询接口
|
||||
requestParam.put("DataSign", s4); // 使用URL编码后的签名
|
||||
requestParam.put("DataType", "2"); // JSON格式
|
||||
|
||||
log.info("快递鸟API请求参数: {}", requestParam.toString());
|
||||
|
||||
// 使用GET请求调用即时查询接口
|
||||
String result = HttpUtil.get(apiUrl, requestParam);
|
||||
log.info("快递鸟API响应结果: {}", result);
|
||||
|
||||
return JSONUtil.parseObj(result);
|
||||
} catch (Exception e) {
|
||||
log.error("调用快递鸟API失败: {}", e.getMessage(), e);
|
||||
JSONObject errorResult = new JSONObject();
|
||||
errorResult.set("Success", false);
|
||||
errorResult.set("Reason", "API调用失败: " + e.getMessage());
|
||||
return errorResult;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将快递鸟状态转换为系统状态码
|
||||
*
|
||||
* @param kdniaoStatus 快递鸟状态
|
||||
* @return 系统状态码
|
||||
*/
|
||||
private Integer getKdniaoStatus(String kdniaoStatus) {
|
||||
if (StrUtil.isBlank(kdniaoStatus)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (kdniaoStatus) {
|
||||
case "在途":
|
||||
return 0;
|
||||
case "揽收":
|
||||
return 1;
|
||||
case "疑难":
|
||||
return 2;
|
||||
case "签收":
|
||||
return 3;
|
||||
case "退签":
|
||||
return 4;
|
||||
case "派件":
|
||||
return 5;
|
||||
case "退回":
|
||||
return 6;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取状态描述
|
||||
*
|
||||
* @param status 状态码
|
||||
* @return 状态描述
|
||||
*/
|
||||
private String getStatusDesc(Integer status) {
|
||||
switch (status) {
|
||||
case 0:
|
||||
return "在途";
|
||||
case 1:
|
||||
return "揽收";
|
||||
case 2:
|
||||
return "疑难";
|
||||
case 3:
|
||||
return "签收";
|
||||
case 4:
|
||||
return "退签";
|
||||
case 5:
|
||||
return "派件";
|
||||
case 6:
|
||||
return "退回";
|
||||
default:
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,188 @@
|
|||
package com.ghy.order.utils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 物流查询工具类
|
||||
*
|
||||
* @author clunt
|
||||
*/
|
||||
public class LogisticsUtils {
|
||||
|
||||
/**
|
||||
* 快递公司编码映射(快递鸟格式)
|
||||
*/
|
||||
private static final Map<String, String> EXPRESS_COMPANY_MAP = new HashMap<>();
|
||||
|
||||
static {
|
||||
// 顺丰速运
|
||||
EXPRESS_COMPANY_MAP.put("SF", "SF");
|
||||
EXPRESS_COMPANY_MAP.put("顺丰", "SF");
|
||||
EXPRESS_COMPANY_MAP.put("顺丰速运", "SF");
|
||||
|
||||
// 圆通速递
|
||||
EXPRESS_COMPANY_MAP.put("YTO", "YTO");
|
||||
EXPRESS_COMPANY_MAP.put("圆通", "YTO");
|
||||
EXPRESS_COMPANY_MAP.put("圆通速递", "YTO");
|
||||
|
||||
// 中通快递
|
||||
EXPRESS_COMPANY_MAP.put("ZTO", "ZTO");
|
||||
EXPRESS_COMPANY_MAP.put("中通", "ZTO");
|
||||
EXPRESS_COMPANY_MAP.put("中通快递", "ZTO");
|
||||
|
||||
// 申通快递
|
||||
EXPRESS_COMPANY_MAP.put("STO", "STO");
|
||||
EXPRESS_COMPANY_MAP.put("申通", "STO");
|
||||
EXPRESS_COMPANY_MAP.put("申通快递", "STO");
|
||||
|
||||
// 韵达速递
|
||||
EXPRESS_COMPANY_MAP.put("YD", "YD");
|
||||
EXPRESS_COMPANY_MAP.put("韵达", "YD");
|
||||
EXPRESS_COMPANY_MAP.put("韵达速递", "YD");
|
||||
|
||||
// 百世快递
|
||||
EXPRESS_COMPANY_MAP.put("HTKY", "HTKY");
|
||||
EXPRESS_COMPANY_MAP.put("百世", "HTKY");
|
||||
EXPRESS_COMPANY_MAP.put("百世快递", "HTKY");
|
||||
|
||||
// 德邦快递
|
||||
EXPRESS_COMPANY_MAP.put("DBL", "DBL");
|
||||
EXPRESS_COMPANY_MAP.put("德邦", "DBL");
|
||||
EXPRESS_COMPANY_MAP.put("德邦快递", "DBL");
|
||||
|
||||
// 京东物流
|
||||
EXPRESS_COMPANY_MAP.put("JD", "JD");
|
||||
EXPRESS_COMPANY_MAP.put("京东", "JD");
|
||||
EXPRESS_COMPANY_MAP.put("京东物流", "JD");
|
||||
|
||||
// EMS
|
||||
EXPRESS_COMPANY_MAP.put("EMS", "EMS");
|
||||
EXPRESS_COMPANY_MAP.put("中国邮政", "EMS");
|
||||
EXPRESS_COMPANY_MAP.put("邮政", "EMS");
|
||||
|
||||
// 天天快递
|
||||
EXPRESS_COMPANY_MAP.put("TTKDEX", "TTKDEX");
|
||||
EXPRESS_COMPANY_MAP.put("天天", "TTKDEX");
|
||||
EXPRESS_COMPANY_MAP.put("天天快递", "TTKDEX");
|
||||
|
||||
// 优速快递
|
||||
EXPRESS_COMPANY_MAP.put("UC", "UC");
|
||||
EXPRESS_COMPANY_MAP.put("优速", "UC");
|
||||
EXPRESS_COMPANY_MAP.put("优速快递", "UC");
|
||||
|
||||
// 全峰快递
|
||||
EXPRESS_COMPANY_MAP.put("QFKD", "QFKD");
|
||||
EXPRESS_COMPANY_MAP.put("全峰", "QFKD");
|
||||
EXPRESS_COMPANY_MAP.put("全峰快递", "QFKD");
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据快递单号识别快递公司(快递鸟格式)
|
||||
*
|
||||
* @param trackingNumber 快递单号
|
||||
* @return 快递公司编码
|
||||
*/
|
||||
public static String identifyExpressCompany(String trackingNumber) {
|
||||
if (trackingNumber == null || trackingNumber.trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String number = trackingNumber.trim().toUpperCase();
|
||||
|
||||
// 顺丰速运:以SF开头,13位数字
|
||||
if (Pattern.matches("^SF\\d{13}$", number)) {
|
||||
return "SF";
|
||||
}
|
||||
|
||||
// 圆通速递:以YT开头,10位数字
|
||||
if (Pattern.matches("^YT\\d{10}$", number)) {
|
||||
return "YTO";
|
||||
}
|
||||
|
||||
// 中通快递:以ZTO开头,10位数字
|
||||
if (Pattern.matches("^ZTO\\d{10}$", number)) {
|
||||
return "ZTO";
|
||||
}
|
||||
|
||||
// 申通快递:以STO开头,10位数字
|
||||
if (Pattern.matches("^STO\\d{10}$", number)) {
|
||||
return "STO";
|
||||
}
|
||||
|
||||
// 韵达速递:以YD开头,10位数字
|
||||
if (Pattern.matches("^YD\\d{10}$", number)) {
|
||||
return "YD";
|
||||
}
|
||||
|
||||
// 百世快递:以HTKY开头,10位数字
|
||||
if (Pattern.matches("^HTKY\\d{10}$", number)) {
|
||||
return "HTKY";
|
||||
}
|
||||
|
||||
// 德邦快递:以DBL开头,10位数字
|
||||
if (Pattern.matches("^DBL\\d{10}$", number)) {
|
||||
return "DBL";
|
||||
}
|
||||
|
||||
// 京东物流:以JD开头,10位数字
|
||||
if (Pattern.matches("^JD\\d{13}$", number)) {
|
||||
return "JD";
|
||||
}
|
||||
|
||||
// EMS:以EMS开头,13位数字
|
||||
if (Pattern.matches("^EMS\\d{13}$", number)) {
|
||||
return "EMS";
|
||||
}
|
||||
|
||||
// 天天快递:以TT开头,10位数字
|
||||
if (Pattern.matches("^TT\\d{10}$", number)) {
|
||||
return "TTKDEX";
|
||||
}
|
||||
|
||||
// 优速快递:以UC开头,10位数字
|
||||
if (Pattern.matches("^UC\\d{10}$", number)) {
|
||||
return "UC";
|
||||
}
|
||||
|
||||
// 纯数字单号,可能是其他快递公司
|
||||
if (Pattern.matches("^\\d{10,15}$", number)) {
|
||||
// 这里可以根据数字规则进一步识别,暂时返回null让API自动识别
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据快递公司名称获取编码(快递鸟格式)
|
||||
*
|
||||
* @param companyName 快递公司名称
|
||||
* @return 快递公司编码
|
||||
*/
|
||||
public static String getExpressCode(String companyName) {
|
||||
if (companyName == null || companyName.trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return EXPRESS_COMPANY_MAP.get(companyName.trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证快递单号格式
|
||||
*
|
||||
* @param trackingNumber 快递单号
|
||||
* @return 是否有效
|
||||
*/
|
||||
public static boolean isValidTrackingNumber(String trackingNumber) {
|
||||
if (trackingNumber == null || trackingNumber.trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String number = trackingNumber.trim();
|
||||
|
||||
// 基本格式验证:长度在10-20位之间,包含字母和数字
|
||||
return Pattern.matches("^[A-Za-z0-9]{10,20}$", number);
|
||||
}
|
||||
}
|
||||
|
|
@ -1460,7 +1460,7 @@ public class OrderServiceImpl implements OrderService {
|
|||
}
|
||||
|
||||
// 检查是否到达目标时间
|
||||
if (now.after(targetTime)) {
|
||||
if (now.after(targetTime)&&orderMaster.getPayStatus()==1) {
|
||||
log.info("订单[{}]确认截止时间已到,开始转为确认中状态", orderDetail.getCode());
|
||||
orderDetail.setWorkFinishTime(new Date());
|
||||
orderDetailService.updateOrderDetail(orderDetail);
|
||||
|
|
|
|||
Loading…
Reference in New Issue