From ae2f59d183711291e97beb8c6693d284ef4db1bb Mon Sep 17 00:00:00 2001 From: cb <275647614@qq.com> Date: Tue, 29 Jul 2025 17:39:22 +0800 Subject: [PATCH] no message --- .../web/controller/order/OrderController.java | 57 ++++ .../order/OrderMasterController.java | 140 +++++++--- ghy-admin/src/main/resources/application.yaml | 13 +- .../ghy/common/enums/FinancialDetailType.java | 3 +- .../com/ghy/order/domain/LogisticsInfo.java | 90 +++++++ .../order/request/LogisticsQueryRequest.java | 30 +++ .../order/request/TransferOrderRequest.java | 36 +++ .../ghy/order/service/LogisticsService.java | 45 ++++ .../service/impl/LogisticsServiceImpl.java | 246 ++++++++++++++++++ .../com/ghy/order/utils/LogisticsUtils.java | 188 +++++++++++++ .../quartz/service/impl/OrderServiceImpl.java | 2 +- 11 files changed, 809 insertions(+), 41 deletions(-) create mode 100644 ghy-order/src/main/java/com/ghy/order/domain/LogisticsInfo.java create mode 100644 ghy-order/src/main/java/com/ghy/order/request/LogisticsQueryRequest.java create mode 100644 ghy-order/src/main/java/com/ghy/order/request/TransferOrderRequest.java create mode 100644 ghy-order/src/main/java/com/ghy/order/service/LogisticsService.java create mode 100644 ghy-order/src/main/java/com/ghy/order/service/impl/LogisticsServiceImpl.java create mode 100644 ghy-order/src/main/java/com/ghy/order/utils/LogisticsUtils.java diff --git a/ghy-admin/src/main/java/com/ghy/web/controller/order/OrderController.java b/ghy-admin/src/main/java/com/ghy/web/controller/order/OrderController.java index bffe03a7..72f97103 100644 --- a/ghy-admin/src/main/java/com/ghy/web/controller/order/OrderController.java +++ b/ghy-admin/src/main/java/com/ghy/web/controller/order/OrderController.java @@ -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()); + } + } } diff --git a/ghy-admin/src/main/java/com/ghy/web/controller/order/OrderMasterController.java b/ghy-admin/src/main/java/com/ghy/web/controller/order/OrderMasterController.java index 8e0c46c2..14e1ba31 100644 --- a/ghy-admin/src/main/java/com/ghy/web/controller/order/OrderMasterController.java +++ b/ghy-admin/src/main/java/com/ghy/web/controller/order/OrderMasterController.java @@ -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("转单失败,请重试"); diff --git a/ghy-admin/src/main/resources/application.yaml b/ghy-admin/src/main/resources/application.yaml index 048ff9f7..afb725a3 100644 --- a/ghy-admin/src/main/resources/application.yaml +++ b/ghy-admin/src/main/resources/application.yaml @@ -144,4 +144,15 @@ aliyun: accessKey: LTAI5tDmv3T3Ze1Mt9wi5Be6 accessSecret: EV4dzWRfKTQaPRjf3tFziMuVBCsThU endpoint: dytnsapi.aliyuncs.com - authCode: od2FgE9a9g \ No newline at end of file + 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 \ No newline at end of file diff --git a/ghy-common/src/main/java/com/ghy/common/enums/FinancialDetailType.java b/ghy-common/src/main/java/com/ghy/common/enums/FinancialDetailType.java index ff13cfcb..c97c2a51 100644 --- a/ghy-common/src/main/java/com/ghy/common/enums/FinancialDetailType.java +++ b/ghy-common/src/main/java/com/ghy/common/enums/FinancialDetailType.java @@ -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; diff --git a/ghy-order/src/main/java/com/ghy/order/domain/LogisticsInfo.java b/ghy-order/src/main/java/com/ghy/order/domain/LogisticsInfo.java new file mode 100644 index 00000000..ce378b95 --- /dev/null +++ b/ghy-order/src/main/java/com/ghy/order/domain/LogisticsInfo.java @@ -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 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; + } +} \ No newline at end of file diff --git a/ghy-order/src/main/java/com/ghy/order/request/LogisticsQueryRequest.java b/ghy-order/src/main/java/com/ghy/order/request/LogisticsQueryRequest.java new file mode 100644 index 00000000..40a0fc6b --- /dev/null +++ b/ghy-order/src/main/java/com/ghy/order/request/LogisticsQueryRequest.java @@ -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; +} \ No newline at end of file diff --git a/ghy-order/src/main/java/com/ghy/order/request/TransferOrderRequest.java b/ghy-order/src/main/java/com/ghy/order/request/TransferOrderRequest.java new file mode 100644 index 00000000..47616473 --- /dev/null +++ b/ghy-order/src/main/java/com/ghy/order/request/TransferOrderRequest.java @@ -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; +} \ No newline at end of file diff --git a/ghy-order/src/main/java/com/ghy/order/service/LogisticsService.java b/ghy-order/src/main/java/com/ghy/order/service/LogisticsService.java new file mode 100644 index 00000000..79724147 --- /dev/null +++ b/ghy-order/src/main/java/com/ghy/order/service/LogisticsService.java @@ -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); +} \ No newline at end of file diff --git a/ghy-order/src/main/java/com/ghy/order/service/impl/LogisticsServiceImpl.java b/ghy-order/src/main/java/com/ghy/order/service/impl/LogisticsServiceImpl.java new file mode 100644 index 00000000..9ebc05f2 --- /dev/null +++ b/ghy-order/src/main/java/com/ghy/order/service/impl/LogisticsServiceImpl.java @@ -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 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 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 "未知"; + } + } +} \ No newline at end of file diff --git a/ghy-order/src/main/java/com/ghy/order/utils/LogisticsUtils.java b/ghy-order/src/main/java/com/ghy/order/utils/LogisticsUtils.java new file mode 100644 index 00000000..87ff39f3 --- /dev/null +++ b/ghy-order/src/main/java/com/ghy/order/utils/LogisticsUtils.java @@ -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 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); + } +} \ No newline at end of file diff --git a/ghy-quartz/src/main/java/com/ghy/quartz/service/impl/OrderServiceImpl.java b/ghy-quartz/src/main/java/com/ghy/quartz/service/impl/OrderServiceImpl.java index c09e72db..ac791e5d 100644 --- a/ghy-quartz/src/main/java/com/ghy/quartz/service/impl/OrderServiceImpl.java +++ b/ghy-quartz/src/main/java/com/ghy/quartz/service/impl/OrderServiceImpl.java @@ -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);