Commit e4a2c738b27e593f26e232bbdee3ca30c052e6ef

Authored by YunaiV
1 parent 36da5d69

订单:

1. 增加查询物流接口
Showing 22 changed files with 338 additions and 266 deletions
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/jackson/core/databind/LocalDateTimeDeserializer.java
1 1 package cn.iocoder.yudao.framework.jackson.core.databind;
2 2  
3 3 import com.fasterxml.jackson.core.JsonParser;
4   -import com.fasterxml.jackson.core.JsonProcessingException;
5 4 import com.fasterxml.jackson.databind.DeserializationContext;
6 5 import com.fasterxml.jackson.databind.JsonDeserializer;
7 6  
... ... @@ -20,7 +19,7 @@ public class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
20 19 public static final LocalDateTimeDeserializer INSTANCE = new LocalDateTimeDeserializer();
21 20  
22 21 @Override
23   - public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
  22 + public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
24 23 return LocalDateTime.ofInstant(Instant.ofEpochMilli(p.getValueAsLong()), ZoneId.systemDefault());
25 24 }
26 25 }
... ...
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/jackson/core/databind/LocalTimeJson.java deleted
1   -package cn.iocoder.yudao.framework.jackson.core.databind;
2   -
3   -import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
4   -import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
5   -
6   -import java.time.ZoneId;
7   -import java.time.format.DateTimeFormatter;
8   -
9   -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_HOUR_MINUTE_SECOND;
10   -
11   -public class LocalTimeJson {
12   -
13   - public static final LocalTimeSerializer SERIALIZER = new LocalTimeSerializer(DateTimeFormatter
14   - .ofPattern(FORMAT_HOUR_MINUTE_SECOND)
15   - .withZone(ZoneId.systemDefault()));
16   -
17   - public static final LocalTimeDeserializer DESERIALIZABLE = new LocalTimeDeserializer(DateTimeFormatter
18   - .ofPattern(FORMAT_HOUR_MINUTE_SECOND)
19   - .withZone(ZoneId.systemDefault()));
20   -
21   -}
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.http
... ... @@ -35,3 +35,8 @@ tenant-id: {{appTenentId}}
35 35 GET {{appApi}}/trade/order/get-detail?id=21
36 36 Authorization: Bearer {{appToken}}
37 37 tenant-id: {{appTenentId}}
  38 +
  39 +### 获得交易订单的物流轨迹
  40 +GET {{appApi}}/trade/order/get-express-track-list?id=70
  41 +Authorization: Bearer {{appToken}}
  42 +tenant-id: {{appTenentId}}
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java
... ... @@ -16,6 +16,7 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
16 16 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
17 17 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
18 18 import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
  19 +import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
19 20 import cn.iocoder.yudao.module.trade.service.order.TradeOrderService;
20 21 import com.google.common.collect.Maps;
21 22 import io.swagger.v3.oas.annotations.Operation;
... ... @@ -45,6 +46,8 @@ public class AppTradeOrderController {
45 46 @Resource
46 47 private TradeOrderService tradeOrderService;
47 48 @Resource
  49 + private TradeOrderQueryService tradeOrderQueryService;
  50 + @Resource
48 51 private DeliveryExpressService deliveryExpressService;
49 52  
50 53 @Resource
... ... @@ -99,6 +102,14 @@ public class AppTradeOrderController {
99 102 propertyValueDetails, tradeOrderProperties, express));
100 103 }
101 104  
  105 + @GetMapping("/get-express-track-list")
  106 + @Operation(summary = "获得交易订单的物流轨迹")
  107 + @Parameter(name = "id", description = "交易订单编号")
  108 + public CommonResult<List<?>> getOrderExpressTrackList(@RequestParam("id") Long id) {
  109 + return success(TradeOrderConvert.INSTANCE.convertList02(
  110 + tradeOrderQueryService.getExpressTrackList(id, getLoginUserId())));
  111 + }
  112 +
102 113 @GetMapping("/page")
103 114 @Operation(summary = "获得交易订单分页")
104 115 public CommonResult<PageResult<AppTradeOrderPageItemRespVO>> getOrderPage(AppTradeOrderPageReqVO reqVO) {
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppOrderExpressTrackRespDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.controller.app.order.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +/**
  9 + * 快递查询的轨迹 Resp DTO
  10 + *
  11 + * @author jason
  12 + */
  13 +@Schema(description = "用户 App - 快递查询的轨迹 Response VO")
  14 +@Data
  15 +public class AppOrderExpressTrackRespDTO {
  16 +
  17 + @Schema(description = "发生时间", requiredMode = Schema.RequiredMode.REQUIRED)
  18 + private LocalDateTime time;
  19 +
  20 + @Schema(description = "快递状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "已签收")
  21 + private String content;
  22 +
  23 +}
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java
... ... @@ -29,6 +29,7 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
29 29 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
30 30 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
31 31 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;
  32 +import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;
32 33 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
33 34 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
34 35 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
... ... @@ -340,4 +341,6 @@ public interface TradeOrderConvert {
340 341 CombinationRecordCreateReqDTO convert(TradeOrderDO order, TradeOrderItemDO orderItem,
341 342 AppTradeOrderCreateReqVO createReqVO, MemberUserRespDTO user);
342 343  
  344 + List<AppOrderExpressTrackRespDTO> convertList02(List<ExpressTrackRespDTO> list);
  345 +
343 346 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/config/TradeExpressProperties.java
... ... @@ -59,7 +59,7 @@ public class TradeExpressProperties {
59 59 }
60 60  
61 61 /**
62   - * 快递100 配置项
  62 + * 快递 100 配置项
63 63 */
64 64 @Data
65 65 public static class Kd100Config {
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/convert/ExpressQueryConvert.java
... ... @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kd100.Kd
7 7 import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kdniao.KdNiaoExpressQueryReqDTO;
8 8 import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kdniao.KdNiaoExpressQueryRespDTO;
9 9 import org.mapstruct.Mapper;
  10 +import org.mapstruct.Mapping;
10 11 import org.mapstruct.factory.Mappers;
11 12  
12 13 import java.util.List;
... ... @@ -16,9 +17,14 @@ public interface ExpressQueryConvert {
16 17  
17 18 ExpressQueryConvert INSTANCE = Mappers.getMapper(ExpressQueryConvert.class);
18 19  
19   - List<ExpressTrackRespDTO> convertList(List<KdNiaoExpressQueryRespDTO.ExpressTrack> expressTrackList);
  20 + List<ExpressTrackRespDTO> convertList(List<KdNiaoExpressQueryRespDTO.ExpressTrack> list);
  21 + @Mapping(source = "acceptTime", target = "time")
  22 + @Mapping(source = "acceptStation", target = "content")
  23 + ExpressTrackRespDTO convert(KdNiaoExpressQueryRespDTO.ExpressTrack track);
20 24  
21   - List<ExpressTrackRespDTO> convertList2(List<Kd100ExpressQueryRespDTO.ExpressTrack> expressTrackList);
  25 + List<ExpressTrackRespDTO> convertList2(List<Kd100ExpressQueryRespDTO.ExpressTrack> list);
  26 + @Mapping(source = "context", target = "content")
  27 + ExpressTrackRespDTO convert(Kd100ExpressQueryRespDTO.ExpressTrack track);
22 28  
23 29 KdNiaoExpressQueryReqDTO convert(ExpressTrackQueryReqDTO dto);
24 30  
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/dto/ExpressTrackRespDTO.java
... ... @@ -2,23 +2,24 @@ package cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto;
2 2  
3 3 import lombok.Data;
4 4  
  5 +import java.time.LocalDateTime;
  6 +
5 7 /**
6   - * 快递查询 Resp DTO
  8 + * 快递查询的轨迹 Resp DTO
7 9 *
8 10 * @author jason
9 11 */
10 12 @Data
11 13 public class ExpressTrackRespDTO {
12 14  
13   - // TODO @jason:LocalDateTime
14 15 /**
15 16 * 发生时间
16 17 */
17   - private String time;
18   - // TODO @jason:其它字段可能要补充下
  18 + private LocalDateTime time;
  19 +
19 20 /**
20 21 * 快递状态
21 22 */
22   - private String state;
  23 + private String content;
23 24  
24 25 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/dto/kd100/Kd100ExpressQueryReqDTO.java
... ... @@ -29,20 +29,5 @@ public class Kd100ExpressQueryReqDTO {
29 29 * 收、寄件人的电话号码
30 30 */
31 31 private String phone;
32   - /**
33   - * 出发地城市
34   - */
35   - private String from;
36   - /**
37   - * 目的地城市,到达目的地后会加大监控频率
38   - */
39   - private String to;
40   -
41   - /**
42   - * 返回结果排序
43   - *
44   - * desc 降序(默认), asc 升序
45   - */
46   - private String order;
47 32  
48 33 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/dto/kd100/Kd100ExpressQueryRespDTO.java
1 1 package cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kd100;
2 2  
  3 +import com.fasterxml.jackson.annotation.JsonFormat;
3 4 import com.fasterxml.jackson.annotation.JsonProperty;
4 5 import lombok.Data;
5 6  
  7 +import java.time.LocalDateTime;
6 8 import java.util.List;
7 9  
  10 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  11 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;
  12 +
8 13 /**
9   - * 快递 100 实时快递查询 Resp DTO 参见 <a href="https://api.kuaidi100.com/document/5f0ffb5ebc8da837cbd8aefc">快递 100 文档</a>
  14 + * 快递 100 实时快递查询 Resp DTO
  15 + *
  16 + * 参见 <a href="https://api.kuaidi100.com/document/5f0ffb5ebc8da837cbd8aefc">快递 100 文档</a>
10 17 *
11 18 * @author jason
12 19 */
... ... @@ -39,21 +46,26 @@ public class Kd100ExpressQueryRespDTO {
39 46 */
40 47 private String message;
41 48  
  49 + /**
  50 + * 轨迹数组
  51 + */
42 52 @JsonProperty("data")
43 53 private List<ExpressTrack> tracks;
44 54  
45 55 @Data
46 56 public static class ExpressTrack {
  57 +
47 58 /**
48 59 * 轨迹发生时间
49 60 */
50   - @JsonProperty("time")
51   - private String time;
  61 + @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
  62 + private LocalDateTime time;
  63 +
52 64 /**
53 65 * 轨迹描述
54 66 */
55   - @JsonProperty("context")
56   - private String state;
  67 + private String context;
  68 +
57 69 }
58 70  
59 71 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/dto/kdniao/KdNiaoExpressQueryRespDTO.java
1 1 package cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.kdniao;
2 2  
  3 +import com.fasterxml.jackson.annotation.JsonFormat;
3 4 import com.fasterxml.jackson.annotation.JsonProperty;
  5 +import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
  6 +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
4 7 import lombok.Data;
5 8  
  9 +import java.time.LocalDateTime;
6 10 import java.util.List;
7 11  
  12 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  13 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;
  14 +
8 15 /**
9   - * 快递鸟快递查询 Resp DTO 参见 <a href="https://www.yuque.com/kdnjishuzhichi/dfcrg1/wugo6k">快递鸟接口文档</a>
  16 + * 快递鸟快递查询 Resp DTO
  17 + *
  18 + * 参见 <a href="https://www.yuque.com/kdnjishuzhichi/dfcrg1/wugo6k">快递鸟接口文档</a>
10 19 *
11 20 * @author jason
12 21 */
... ... @@ -17,7 +26,7 @@ public class KdNiaoExpressQueryRespDTO {
17 26 * 快递公司编码
18 27 */
19 28 @JsonProperty("ShipperCode")
20   - private String expressCompanyCode;
  29 + private String shipperCode;
21 30  
22 31 /**
23 32 * 快递单号
... ... @@ -31,10 +40,26 @@ public class KdNiaoExpressQueryRespDTO {
31 40 @JsonProperty("OrderCode")
32 41 private String orderNo;
33 42  
  43 + /**
  44 + * 用户 ID
  45 + */
34 46 @JsonProperty("EBusinessID")
35 47 private String businessId;
  48 +
  49 + /**
  50 + * 普通物流状态
  51 + *
  52 + * 0 - 暂无轨迹信息
  53 + * 1 - 已揽收
  54 + * 2 - 在途中
  55 + * 3 - 签收
  56 + * 4 - 问题件
  57 + * 5 - 转寄
  58 + * 6 - 清关
  59 + */
36 60 @JsonProperty("State")
37 61 private String state;
  62 +
38 63 /**
39 64 * 成功与否
40 65 */
... ... @@ -46,30 +71,29 @@ public class KdNiaoExpressQueryRespDTO {
46 71 @JsonProperty("Reason")
47 72 private String reason;
48 73  
  74 + /**
  75 + * 轨迹数组
  76 + */
49 77 @JsonProperty("Traces")
50 78 private List<ExpressTrack> tracks;
51 79  
52 80 @Data
53 81 public static class ExpressTrack {
  82 +
54 83 /**
55   - * 轨迹发生时间
  84 + * 发生时间
56 85 */
57 86 @JsonProperty("AcceptTime")
58   - private String time;
  87 + @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
  88 + @JsonDeserialize(using = LocalDateTimeDeserializer.class)
  89 + private LocalDateTime acceptTime;
  90 +
59 91 /**
60 92 * 轨迹描述
61 93 */
62 94 @JsonProperty("AcceptStation")
63   - private String state;
  95 + private String acceptStation;
  96 +
64 97 }
65 98  
66   -// {
67   -// "EBusinessID": "1237100",
68   -// "Traces": [],
69   -// "State": "0",
70   -// "ShipperCode": "STO",
71   -// "LogisticCode": "638650888018",
72   -// "Success": true,
73   -// "Reason": "暂无轨迹信息"
74   -// }
75 99 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/impl/kd100/Kd100ExpressClient.java
... ... @@ -40,18 +40,24 @@ public class Kd100ExpressClient implements ExpressClient {
40 40 private final RestTemplate restTemplate;
41 41 private final TradeExpressProperties.Kd100Config config;
42 42  
  43 + /**
  44 + * 查询快递轨迹
  45 + *
  46 + * @see <a href="https://api.kuaidi100.com/debug-tool/query/">接口文档</a>
  47 + *
  48 + * @param reqDTO 查询请求参数
  49 + * @return 快递轨迹
  50 + */
43 51 @Override
44 52 public List<ExpressTrackRespDTO> getExpressTrackList(ExpressTrackQueryReqDTO reqDTO) {
45   - // 发起查询
46   - Kd100ExpressQueryReqDTO kd100ReqParam = INSTANCE.convert2(reqDTO);
47   - kd100ReqParam.setExpressCode(kd100ReqParam.getExpressCode().toLowerCase()); // 快递公司编码需要转成小写
48   - Kd100ExpressQueryRespDTO respDTO = requestExpressQuery(REAL_TIME_QUERY_URL, kd100ReqParam,
  53 + // 发起请求
  54 + Kd100ExpressQueryReqDTO requestDTO = INSTANCE.convert2(reqDTO)
  55 + .setExpressCode(reqDTO.getExpressCode().toLowerCase());
  56 + Kd100ExpressQueryRespDTO respDTO = httpRequest(REAL_TIME_QUERY_URL, requestDTO,
49 57 Kd100ExpressQueryRespDTO.class);
50   - log.debug("[getExpressTrackList][快递 100 接口 查询接口返回 {}]", respDTO);
51 58  
52 59 // 处理结果
53 60 if (Objects.equals("false", respDTO.getResult())) {
54   - log.error("[getExpressTrackList][快递 100 接口 返回失败 {}]", respDTO.getMessage());
55 61 throw exception(EXPRESS_API_QUERY_FAILED, respDTO.getMessage());
56 62 }
57 63 if (CollUtil.isEmpty(respDTO.getTracks())) {
... ... @@ -61,7 +67,7 @@ public class Kd100ExpressClient implements ExpressClient {
61 67 }
62 68  
63 69 /**
64   - * 发送快递 100 实时快递查询请求,可以作为通用快递 100 通用请求接口。 目前没有其它场景需要使用。暂时放这里
  70 + * 快递 100 API 请求
65 71 *
66 72 * @param url 请求 url
67 73 * @param req 对应请求的请求参数
... ... @@ -69,24 +75,23 @@ public class Kd100ExpressClient implements ExpressClient {
69 75 * @param <Req> 每个请求的请求结构 Req DTO
70 76 * @param <Resp> 每个请求的响应结构 Resp DTO
71 77 */
72   - private <Req, Resp> Resp requestExpressQuery(String url, Req req, Class<Resp> respClass) {
  78 + private <Req, Resp> Resp httpRequest(String url, Req req, Class<Resp> respClass) {
73 79 // 请求头
74 80 HttpHeaders headers = new HttpHeaders();
75 81 headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
76   - // 生成签名
77   - String param = JsonUtils.toJsonString(req);
78   - String sign = generateReqSign(param, config.getKey(), config.getCustomer());
79 82 // 请求体
  83 + String param = JsonUtils.toJsonString(req);
  84 + String sign = generateReqSign(param, config.getKey(), config.getCustomer()); // 签名
80 85 MultiValueMap<String, String> requestBody = new LinkedMultiValueMap<>();
81 86 requestBody.add("customer", config.getCustomer());
82 87 requestBody.add("sign", sign);
83 88 requestBody.add("param", param);
84   - log.debug("[sendExpressQueryReq][快递 100 接口的请求参数: {}]", requestBody);
  89 + log.debug("[httpRequest][请求参数({})]", requestBody);
  90 +
85 91 // 发送请求
86 92 HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(requestBody, headers);
87 93 ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
88   - log.debug("[sendExpressQueryReq][快递 100 接口响应结果 {}]", responseEntity);
89   -
  94 + log.debug("[httpRequest][的响应结果({})]", responseEntity);
90 95 // 处理响应
91 96 if (!responseEntity.getStatusCode().is2xxSuccessful()) {
92 97 throw exception(EXPRESS_API_QUERY_ERROR);
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/impl/kdniao/KdNiaoExpressClient.java
... ... @@ -41,46 +41,49 @@ public class KdNiaoExpressClient implements ExpressClient {
41 41 * 快递鸟即时查询免费版 RequestType
42 42 */
43 43 private static final String REAL_TIME_FREE_REQ_TYPE = "1002";
  44 +
44 45 private final RestTemplate restTemplate;
45 46 private final TradeExpressProperties.KdNiaoConfig config;
46 47  
47 48 /**
48   - * 快递鸟即时查询免费版本
  49 + * 查询快递轨迹【免费版】
  50 + *
  51 + * 仅支持 3 家:申通快递、圆通速递、百世快递
  52 + *
  53 + * @see <a href="https://www.yuque.com/kdnjishuzhichi/dfcrg1/wugo6k">接口文档</a>
49 54 *
50   - * @see <a href="https://www.yuque.com/kdnjishuzhichi/dfcrg1/wugo6k">快递鸟接口文档</a>
51 55 * @param reqDTO 查询请求参数
  56 + * @return 快递轨迹
52 57 */
53 58 @Override
54 59 public List<ExpressTrackRespDTO> getExpressTrackList(ExpressTrackQueryReqDTO reqDTO) {
55   - KdNiaoExpressQueryReqDTO kdNiaoReqData = INSTANCE.convert(reqDTO);
56   - // 快递公司编码需要转成大写
57   - kdNiaoReqData.setExpressCode(reqDTO.getExpressCode().toUpperCase());
58   - KdNiaoExpressQueryRespDTO respDTO = requestKdNiaoApi(REAL_TIME_QUERY_URL, REAL_TIME_FREE_REQ_TYPE,
59   - kdNiaoReqData, KdNiaoExpressQueryRespDTO.class);
60   - log.debug("[getExpressTrackList][快递鸟即时查询接口返回 {}]", respDTO);
  60 + // 发起请求
  61 + KdNiaoExpressQueryReqDTO requestDTO = INSTANCE.convert(reqDTO)
  62 + .setExpressCode(reqDTO.getExpressCode().toUpperCase());
  63 + KdNiaoExpressQueryRespDTO respDTO = httpRequest(REAL_TIME_QUERY_URL, REAL_TIME_FREE_REQ_TYPE,
  64 + requestDTO, KdNiaoExpressQueryRespDTO.class);
61 65  
62 66 // 处理结果
63 67 if (respDTO == null || !respDTO.getSuccess()) {
64 68 throw exception(EXPRESS_API_QUERY_FAILED, respDTO == null ? "" : respDTO.getReason());
65 69 }
66   - if (CollUtil.isNotEmpty(respDTO.getTracks())) {
  70 + if (CollUtil.isEmpty(respDTO.getTracks())) {
67 71 return Collections.emptyList();
68 72 }
69 73 return INSTANCE.convertList(respDTO.getTracks());
70 74 }
71 75  
72 76 /**
73   - * 快递鸟 通用的 API 请求,暂时没有其他应用场景, 暂时放这里
  77 + * 快递鸟 API 请求
74 78 *
75 79 * @param url 请求 url
76   - * @param requestType 对应的请求指令 (快递鸟的RequestType)
  80 + * @param requestType 对应的请求指令 (快递鸟的 RequestType)
77 81 * @param req 对应请求的请求参数
78 82 * @param respClass 对应请求的响应 class
79 83 * @param <Req> 每个请求的请求结构 Req DTO
80 84 * @param <Resp> 每个请求的响应结构 Resp DTO
81 85 */
82   - private <Req, Resp> Resp requestKdNiaoApi(String url, String requestType, Req req,
83   - Class<Resp> respClass){
  86 + private <Req, Resp> Resp httpRequest(String url, String requestType, Req req, Class<Resp> respClass) {
84 87 // 请求头
85 88 HttpHeaders headers = new HttpHeaders();
86 89 headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
... ... @@ -93,11 +96,12 @@ public class KdNiaoExpressClient implements ExpressClient {
93 96 requestBody.add("EBusinessID", config.getBusinessId());
94 97 requestBody.add("DataSign", dataSign);
95 98 requestBody.add("RequestType", requestType);
96   - log.debug("[requestKdNiaoApi][快递鸟接口 RequestType : {}, 的请求参数 {}]", requestType, requestBody);
  99 + log.debug("[httpRequest][RequestType({}) 的请求参数({})]", requestType, requestBody);
  100 +
97 101 // 发送请求
98 102 HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(requestBody, headers);
99 103 ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
100   - log.debug("快递鸟接口 RequestType : {}, 的响应结果 {}", requestType, responseEntity);
  104 + log.debug("[httpRequest][RequestType({}) 的响应结果({})", requestType, responseEntity);
101 105 // 处理响应
102 106 if (!responseEntity.getStatusCode().is2xxSuccessful()) {
103 107 throw exception(EXPRESS_API_QUERY_ERROR);
... ... @@ -106,7 +110,10 @@ public class KdNiaoExpressClient implements ExpressClient {
106 110 }
107 111  
108 112 /**
109   - * 快递鸟生成请求签名 参见 <a href="https://www.yuque.com/kdnjishuzhichi/dfcrg1/zes04h">签名说明</a>
  113 + * 快递鸟生成请求签名
  114 + *
  115 + * 参见 <a href="https://www.yuque.com/kdnjishuzhichi/dfcrg1/zes04h">签名说明</a>
  116 + *
110 117 * @param reqData 请求实体
111 118 * @param apiKey api Key
112 119 */
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.service.order;
  2 +
  3 +import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;
  4 +
  5 +import java.util.List;
  6 +
  7 +/**
  8 + * 交易订单【读】 Service 接口
  9 + *
  10 + * @author 芋道源码
  11 + */
  12 +public interface TradeOrderQueryService {
  13 +
  14 + /**
  15 + * 获得订单的物流轨迹
  16 + *
  17 + * @param id 订单编号
  18 + * @param userId 用户编号
  19 + * @return 物流轨迹数组
  20 + */
  21 + List<ExpressTrackRespDTO> getExpressTrackList(Long id, Long userId);
  22 +
  23 +}
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.service.order;
  2 +
  3 +import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
  4 +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
  5 +import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
  6 +import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClientFactory;
  7 +import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
  8 +import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;
  9 +import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
  10 +import org.springframework.stereotype.Service;
  11 +
  12 +import javax.annotation.Resource;
  13 +import java.util.Collections;
  14 +import java.util.List;
  15 +
  16 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  17 +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.EXPRESS_NOT_EXISTS;
  18 +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_NOT_FOUND;
  19 +
  20 +/**
  21 + * 交易订单【读】 Service 实现类
  22 + *
  23 + * @author 芋道源码
  24 + */
  25 +@Service
  26 +public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
  27 +
  28 + @Resource
  29 + private ExpressClientFactory expressClientFactory;
  30 +
  31 + @Resource
  32 + private TradeOrderMapper tradeOrderMapper;
  33 +
  34 + @Resource
  35 + private DeliveryExpressService deliveryExpressService;
  36 +
  37 + @Override
  38 + public List<ExpressTrackRespDTO> getExpressTrackList(Long id, Long userId) {
  39 + // 查询订单
  40 + TradeOrderDO order = tradeOrderMapper.selectByIdAndUserId(id, userId);
  41 + if (order == null) {
  42 + throw exception(ORDER_NOT_FOUND);
  43 + }
  44 +
  45 + // 查询物流公司
  46 + if (order.getLogisticsId() == null) {
  47 + return Collections.emptyList();
  48 + }
  49 + DeliveryExpressDO express = deliveryExpressService.getDeliveryExpress(order.getLogisticsId());
  50 + if (express == null) {
  51 + throw exception(EXPRESS_NOT_EXISTS);
  52 + }
  53 +
  54 + // 查询物流轨迹
  55 + return expressClientFactory.getDefaultExpressClient().getExpressTrackList(
  56 + new ExpressTrackQueryReqDTO().setExpressCode(express.getCode()).setLogisticsNo(order.getLogisticsNo())
  57 + .setPhone(order.getReceiverMobile()));
  58 + }
  59 +
  60 +}
... ...
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/impl/Kd100ExpressClientIntegrationTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl;
  2 +
  3 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  4 +import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressProperties;
  5 +import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
  6 +import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;
  7 +import cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl.kd100.Kd100ExpressClient;
  8 +import lombok.extern.slf4j.Slf4j;
  9 +import org.junit.jupiter.api.BeforeEach;
  10 +import org.junit.jupiter.api.Disabled;
  11 +import org.junit.jupiter.api.Test;
  12 +import org.springframework.boot.web.client.RestTemplateBuilder;
  13 +import org.springframework.web.client.RestTemplate;
  14 +
  15 +import java.util.List;
  16 +
  17 +/**
  18 + * {@link Kd100ExpressClient} 的集成测试
  19 + *
  20 + * @author jason
  21 + */
  22 +@Slf4j
  23 +public class Kd100ExpressClientIntegrationTest {
  24 +
  25 + private Kd100ExpressClient client;
  26 +
  27 + @BeforeEach
  28 + public void init() {
  29 + RestTemplate restTemplate = new RestTemplateBuilder().build();
  30 + TradeExpressProperties.Kd100Config config = new TradeExpressProperties.Kd100Config()
  31 + .setKey("pLXUGAwK5305")
  32 + .setCustomer("E77DF18BE109F454A5CD319E44BF5177");
  33 + client = new Kd100ExpressClient(restTemplate, config);
  34 + }
  35 +
  36 + @Test
  37 + @Disabled("集成测试,暂时忽略")
  38 + public void testGetExpressTrackList() {
  39 + ExpressTrackQueryReqDTO reqDTO = new ExpressTrackQueryReqDTO();
  40 + reqDTO.setExpressCode("STO");
  41 + reqDTO.setLogisticsNo("773220402764314");
  42 + List<ExpressTrackRespDTO> tracks = client.getExpressTrackList(reqDTO);
  43 + System.out.println(JsonUtils.toJsonPrettyString(tracks));
  44 + }
  45 +
  46 +}
... ...
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/impl/Kd100ExpressClientTest.java deleted
1   -package cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl;
2   -
3   -import cn.iocoder.yudao.framework.common.exception.ServiceException;
4   -import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressProperties;
5   -import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
6   -import cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl.kd100.Kd100ExpressClient;
7   -import org.junit.jupiter.api.BeforeEach;
8   -import org.junit.jupiter.api.Disabled;
9   -import org.junit.jupiter.api.Test;
10   -import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
11   -import org.springframework.boot.context.properties.EnableConfigurationProperties;
12   -import org.springframework.boot.test.context.SpringBootTest;
13   -import org.springframework.boot.web.client.RestTemplateBuilder;
14   -import org.springframework.context.annotation.Import;
15   -import org.springframework.test.context.ActiveProfiles;
16   -
17   -import javax.annotation.Resource;
18   -
19   -import static org.junit.jupiter.api.Assertions.assertEquals;
20   -import static org.junit.jupiter.api.Assertions.assertThrows;
21   -
22   -// TODO @jason:可以参考 AliyunSmsClientTest 写,纯 mockito,无需启动 spring 容器
23   -/**
24   - * @author jason
25   - */
26   -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = Kd100ExpressClientTest.Application.class)
27   -@ActiveProfiles("unit-test") // 设置使用 trade-delivery-query 配置文件
28   -public class Kd100ExpressClientTest {
29   -
30   - @Resource
31   - private RestTemplateBuilder builder;
32   - @Resource
33   - private TradeExpressProperties expressQueryProperties;
34   -
35   - private Kd100ExpressClient kd100ExpressClient;
36   -
37   - @BeforeEach
38   - public void init(){
39   - kd100ExpressClient = new Kd100ExpressClient(builder.build(),expressQueryProperties.getKd100());
40   - }
41   - @Test
42   - @Disabled("需要 授权 key. 暂时忽略")
43   - void testRealTimeQueryExpressFailed() {
44   - ServiceException t = assertThrows(ServiceException.class, () -> {
45   - ExpressTrackQueryReqDTO reqDTO = new ExpressTrackQueryReqDTO();
46   - reqDTO.setExpressCode("yto");
47   - reqDTO.setLogisticsNo("YT9383342193097");
48   - kd100ExpressClient.getExpressTrackList(reqDTO);
49   - });
50   - assertEquals(1011003005, t.getCode());
51   - }
52   -
53   - @Import({
54   - RestTemplateAutoConfiguration.class
55   - })
56   - @EnableConfigurationProperties(TradeExpressProperties.class)
57   - public static class Application {
58   - }
59   -}
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/impl/KdNiaoExpressClientIntegrationTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl;
  2 +
  3 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  4 +import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressProperties;
  5 +import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
  6 +import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;
  7 +import cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl.kdniao.KdNiaoExpressClient;
  8 +import lombok.extern.slf4j.Slf4j;
  9 +import org.junit.jupiter.api.BeforeEach;
  10 +import org.junit.jupiter.api.Disabled;
  11 +import org.junit.jupiter.api.Test;
  12 +import org.springframework.boot.web.client.RestTemplateBuilder;
  13 +import org.springframework.web.client.RestTemplate;
  14 +
  15 +import java.util.List;
  16 +
  17 +/**
  18 + * {@link KdNiaoExpressClient} 的集成测试
  19 + *
  20 + * @author jason
  21 + */
  22 +@Slf4j
  23 +public class KdNiaoExpressClientIntegrationTest {
  24 +
  25 + private KdNiaoExpressClient client;
  26 +
  27 + @BeforeEach
  28 + public void init() {
  29 + RestTemplate restTemplate = new RestTemplateBuilder().build();
  30 + TradeExpressProperties.KdNiaoConfig config = new TradeExpressProperties.KdNiaoConfig()
  31 + .setApiKey("cb022f1e-48f1-4c4a-a723-9001ac9676b8")
  32 + .setBusinessId("1809751");
  33 + client = new KdNiaoExpressClient(restTemplate, config);
  34 + }
  35 +
  36 + @Test
  37 + @Disabled("集成测试,暂时忽略")
  38 + public void testGetExpressTrackList() {
  39 + ExpressTrackQueryReqDTO reqDTO = new ExpressTrackQueryReqDTO();
  40 + reqDTO.setExpressCode("STO");
  41 + reqDTO.setLogisticsNo("663220402764314");
  42 + List<ExpressTrackRespDTO> tracks = client.getExpressTrackList(reqDTO);
  43 + System.out.println(JsonUtils.toJsonPrettyString(tracks));
  44 + }
  45 +
  46 +}
... ...
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/impl/KdNiaoExpressClientTest.java deleted
1   -package cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl;
2   -
3   -import cn.iocoder.yudao.framework.common.exception.ServiceException;
4   -import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressProperties;
5   -import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackQueryReqDTO;
6   -import cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl.kdniao.KdNiaoExpressClient;
7   -import org.junit.jupiter.api.BeforeEach;
8   -import org.junit.jupiter.api.Disabled;
9   -import org.junit.jupiter.api.Test;
10   -import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
11   -import org.springframework.boot.context.properties.EnableConfigurationProperties;
12   -import org.springframework.boot.test.context.SpringBootTest;
13   -import org.springframework.boot.web.client.RestTemplateBuilder;
14   -import org.springframework.context.annotation.Import;
15   -import org.springframework.test.context.ActiveProfiles;
16   -
17   -import javax.annotation.Resource;
18   -
19   -import static org.junit.jupiter.api.Assertions.assertThrows;
20   -
21   -// TODO @jason:可以参考 AliyunSmsClientTest 写,纯 mockito,无需启动 spring 容器
22   -/**
23   - * {@link KdNiaoExpressClient} 的单元测试
24   - *
25   - * @author jason
26   - */
27   -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = KdNiaoExpressClientTest.Application.class)
28   -@ActiveProfiles("unit-test")
29   -public class KdNiaoExpressClientTest {
30   -
31   - @Resource
32   - private RestTemplateBuilder builder;
33   - @Resource
34   - private TradeExpressProperties expressQueryProperties;
35   -
36   - private KdNiaoExpressClient kdNiaoExpressClient;
37   -
38   - @BeforeEach
39   - public void init(){
40   - kdNiaoExpressClient = new KdNiaoExpressClient(builder.build(),expressQueryProperties.getKdNiao());
41   - }
42   - @Test
43   - @Disabled("需要 授权 key. 暂时忽略")
44   - void testRealTimeQueryExpressFailed() {
45   - assertThrows(ServiceException.class,() ->{
46   - ExpressTrackQueryReqDTO reqDTO = new ExpressTrackQueryReqDTO();
47   - reqDTO.setExpressCode("yy");
48   - reqDTO.setLogisticsNo("YT9383342193097");
49   - kdNiaoExpressClient.getExpressTrackList(reqDTO);
50   - });
51   - }
52   -
53   - @Import({
54   - RestTemplateAutoConfiguration.class
55   - })
56   - @EnableConfigurationProperties(TradeExpressProperties.class)
57   - public static class Application {
58   - }
59   -}
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/impl/NoProvideExpressClientTest.java deleted
1   -package cn.iocoder.yudao.module.trade.framework.delivery.core.client.impl;
2   -
3   -import cn.iocoder.yudao.framework.common.exception.ServiceException;
4   -import cn.iocoder.yudao.module.trade.framework.delivery.config.ExpressClientConfig;
5   -import cn.iocoder.yudao.module.trade.framework.delivery.config.TradeExpressProperties;
6   -import cn.iocoder.yudao.module.trade.framework.delivery.core.client.ExpressClient;
7   -import org.junit.jupiter.api.Test;
8   -import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
9   -import org.springframework.boot.context.properties.EnableConfigurationProperties;
10   -import org.springframework.boot.test.context.SpringBootTest;
11   -import org.springframework.boot.web.client.RestTemplateBuilder;
12   -import org.springframework.context.annotation.Bean;
13   -import org.springframework.context.annotation.Import;
14   -import org.springframework.test.context.ActiveProfiles;
15   -import org.springframework.web.client.RestTemplate;
16   -
17   -import javax.annotation.Resource;
18   -
19   -import static org.junit.jupiter.api.Assertions.assertEquals;
20   -import static org.junit.jupiter.api.Assertions.assertThrows;
21   -
22   -// TODO @jason:可以参考 AliyunSmsClientTest 写,纯 mockito,无需启动 spring 容器
23   -/**
24   - * @author jason
25   - */
26   -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = NoProvideExpressClientTest.Application.class)
27   -@ActiveProfiles("unit-test") // 设置使用 trade-delivery-query 配置文件
28   -@Import({ExpressClientConfig.class})
29   -public class NoProvideExpressClientTest {
30   -
31   - @Resource
32   - private ExpressClient expressClient;
33   -
34   - @Test
35   - void getExpressTrackList() {
36   - ServiceException t = assertThrows(ServiceException.class, () -> {
37   - expressClient.getExpressTrackList(null);
38   - });
39   - assertEquals(1011003006, t.getCode());
40   - }
41   -
42   - @Import({
43   - RestTemplateAutoConfiguration.class,
44   - })
45   - @EnableConfigurationProperties(TradeExpressProperties.class)
46   - public static class Application {
47   -
48   - @Bean
49   - private RestTemplate restTemplate(RestTemplateBuilder builder) {
50   - return builder.build();
51   - }
52   - }
53   -}
yudao-server/src/main/resources/application.yaml
... ... @@ -204,6 +204,14 @@ yudao:
204 204 order:
205 205 app-id: 1 # 商户编号
206 206 expire-time: 2h # 支付的过期时间
  207 + express:
  208 + client: kd_niao
  209 + kd-niao:
  210 + api-key: cb022f1e-48f1-4c4a-a723-9001ac9676b8
  211 + business-id: 1809751
  212 + kd100:
  213 + key: pLXUGAwK5305
  214 + customer: E77DF18BE109F454A5CD319E44BF5177
207 215  
208 216 debug: false
209 217  
... ...