diff --git a/pom.xml b/pom.xml index 53b9a3a..8c93ac0 100644 --- a/pom.xml +++ b/pom.xml @@ -164,18 +164,27 @@ 1.2.62 + + + org.springframework.boot spring-boot-configuration-processor true - + redis.clients jedis 5.2.0 + + + com.google.code.gson + gson + 2.8.8 + diff --git a/src/main/java/com/cultural/heritage/config/WxAccessToken.java b/src/main/java/com/cultural/heritage/config/WxAccessToken.java new file mode 100644 index 0000000..e8a3b22 --- /dev/null +++ b/src/main/java/com/cultural/heritage/config/WxAccessToken.java @@ -0,0 +1,31 @@ +package com.cultural.heritage.config; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.context.annotation.Configuration; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 微信小程序获取token响应体 + */ +@Configuration +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxAccessToken implements Serializable { + + @Schema(description = "获取到的凭证") + private String access_token; + + @Schema(description = "凭证有效时间,单位:秒。目前是7200秒之内的值。") + private String expires_in; + + @Serial + private static final long serialVersionUID = 1L; +} diff --git a/src/main/java/com/cultural/heritage/config/WxWaybillToken.java b/src/main/java/com/cultural/heritage/config/WxWaybillToken.java new file mode 100644 index 0000000..a4da28d --- /dev/null +++ b/src/main/java/com/cultural/heritage/config/WxWaybillToken.java @@ -0,0 +1,35 @@ +package com.cultural.heritage.config; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.context.annotation.Configuration; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 微信小程序获取waybillToken响应体 + */ +@Configuration +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxWaybillToken implements Serializable { + + @Schema(description = "返回码") + private Integer errcode; + + @Schema(description = "错误信息") + private String errmsg; + + @Schema(description = "物流查询id") + private String waybill_token; + + @Serial + private static final long serialVersionUID = 1L; +} diff --git a/src/main/java/com/cultural/heritage/controller/order/OrderController.java b/src/main/java/com/cultural/heritage/controller/order/OrderController.java index b27961b..dab5b67 100644 --- a/src/main/java/com/cultural/heritage/controller/order/OrderController.java +++ b/src/main/java/com/cultural/heritage/controller/order/OrderController.java @@ -67,6 +67,8 @@ public class OrderController { private CartRecordService cartRecordService; + + @Resource private GoodService goodService; diff --git a/src/main/java/com/cultural/heritage/controller/wx/WeChatLogisticsController.java b/src/main/java/com/cultural/heritage/controller/wx/WeChatLogisticsController.java new file mode 100644 index 0000000..9fe6f18 --- /dev/null +++ b/src/main/java/com/cultural/heritage/controller/wx/WeChatLogisticsController.java @@ -0,0 +1,148 @@ +package com.cultural.heritage.controller.wx; + + +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.cultural.heritage.common.BaseResponse; +import com.cultural.heritage.common.ErrorCode; +import com.cultural.heritage.common.ResultUtils; +import com.cultural.heritage.config.WxAccessToken; +import com.cultural.heritage.config.WxWaybillToken; +import com.cultural.heritage.exception.BusinessException; +import com.cultural.heritage.exception.ThrowUtils; +import com.cultural.heritage.model.dto.CommonRequest; +import com.cultural.heritage.model.dto.snapshot.AddressSnapshot; +import com.cultural.heritage.model.dto.snapshot.GoodSnapshot; +import com.cultural.heritage.model.entity.Order; +import com.cultural.heritage.model.entity.OrderItems; +import com.cultural.heritage.model.entity.User; +import com.cultural.heritage.model.vo.good.GoodLogisticsInfoVO; +import com.cultural.heritage.model.vo.good.GoodsInfo; +import com.cultural.heritage.service.order.OrderItemService; +import com.cultural.heritage.service.order.OrderService; +import com.cultural.heritage.service.user.UserService; +import com.cultural.heritage.service.wx.WeChatLogisticsService; +import com.google.gson.Gson; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 微信小程序查看物流相关接口 + **/ +@Slf4j +@RestController +@Tag(name = "微信物流接口") +@RequestMapping("/logistics") +public class WeChatLogisticsController { + + + private final static String ACCESS_TOKEN_KEY = "accessToken"; + + + @Resource + private UserService userService; + + + @Resource + private WeChatLogisticsService weChatLogisticsService; + + + @Resource + private RedisTemplate redisTemplate; + + + @Resource + private Gson gson; + + + @Resource + private OrderService orderService; + + + @Resource + private OrderItemService orderItemService; + + + /** + * (小程序端)获取接口调用凭据 + * + * @param request + */ + @GetMapping("/get/token") + @Operation(summary = "(小程序端)微信小程序获取接口调用凭据", description = "参数:无, 权限:所有人, 方法名:getAccessToken") + public BaseResponse getAccessToken(HttpServletRequest request) { + userService.getLoginUser(request); + String accessToken = (String) redisTemplate.opsForValue().get(ACCESS_TOKEN_KEY); + if (accessToken == null) { + weChatLogisticsService.addAccessToken(); + accessToken = (String) redisTemplate.opsForValue().get(ACCESS_TOKEN_KEY); + } + WxAccessToken wxAccessToken = WxAccessToken.builder() + .access_token(accessToken) + .expires_in("7200").build(); + return ResultUtils.success(wxAccessToken); + } + + + /** + * (小程序端)根据订单id查询物流信息 + */ + @PostMapping("/get/info") + @Operation(summary = "(小程序端)微信小程序获取接口调用凭据", description = "参数:无, 权限:所有人, 方法名:getAccessToken") + public BaseResponse getWaybillToken(@RequestBody CommonRequest commonRequest, HttpServletRequest request) throws IOException { + if (commonRequest == null || commonRequest.getId() <= 0) { + throw new BusinessException(ErrorCode.PARAMS_ERROR); + } + User loginUser = userService.getLoginUser(request); + String miniOpenId = loginUser.getMiniOpenId(); + String accessToken = (String) redisTemplate.opsForValue().get(ACCESS_TOKEN_KEY); + if (accessToken == null) { + weChatLogisticsService.addAccessToken(); + accessToken = (String) redisTemplate.opsForValue().get(ACCESS_TOKEN_KEY); + } + Long id = commonRequest.getId(); + Order order = orderService.getById(id); + ThrowUtils.throwIf(order == null, ErrorCode.SYSTEM_ERROR, "订单不存在"); + String trackingNumber = order.getTrackingNumber(); + AddressSnapshot addressSnapshot = order.getAddressSnapshot(); + String phone = addressSnapshot.getPhone(); + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("orderId", id); + List orderItemsList = orderItemService.list(queryWrapper); + List goodLogisticsInfoVOS = orderItemsList.stream().map(orderItems -> { + GoodSnapshot goodSnapshot = orderItems.getGoodSnapshot(); + String name = goodSnapshot.getName(); + String goodImg = goodSnapshot.getGoodImg(); + return GoodLogisticsInfoVO.builder().goods_name(name).goods_img_url(goodImg).build(); + }).toList(); + GoodsInfo goodsInfo = GoodsInfo.builder().detail_list(goodLogisticsInfoVOS).build(); + Map param = new HashMap<>(); + param.put("openid", miniOpenId); + param.put("waybill_id", trackingNumber); + param.put("receiver_phone", phone); + param.put("goods_info", goodsInfo); + String url = "https://api.weixin.qq.com/cgi-bin/express/delivery/open_msg/trace_waybill?access_token=" + accessToken; + + String jsonParams = JSONUtil.toJsonStr(param); + String response = HttpUtil.createPost(url) + .header("Content-Type", "application/json") + .body(jsonParams) + .execute() + .body(); + + WxWaybillToken wxWaybillToken = gson.fromJson(response, WxWaybillToken.class); + return ResultUtils.success(wxWaybillToken); + } + +} diff --git a/src/main/java/com/cultural/heritage/controller/wx/WeChatPayController.java b/src/main/java/com/cultural/heritage/controller/wx/WeChatPayController.java index 1856879..ae26dfe 100644 --- a/src/main/java/com/cultural/heritage/controller/wx/WeChatPayController.java +++ b/src/main/java/com/cultural/heritage/controller/wx/WeChatPayController.java @@ -14,7 +14,7 @@ import com.cultural.heritage.model.entity.Order; import com.cultural.heritage.model.entity.User; import com.cultural.heritage.service.order.OrderService; import com.cultural.heritage.service.user.UserService; -import com.cultural.heritage.service.wxpay.WeChatService; +import com.cultural.heritage.service.wx.WeChatService; import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse; import com.wechat.pay.java.service.payments.model.Transaction; import com.wechat.pay.java.service.refund.model.Refund; diff --git a/src/main/java/com/cultural/heritage/model/vo/good/GoodLogisticsInfoVO.java b/src/main/java/com/cultural/heritage/model/vo/good/GoodLogisticsInfoVO.java new file mode 100644 index 0000000..79a05d7 --- /dev/null +++ b/src/main/java/com/cultural/heritage/model/vo/good/GoodLogisticsInfoVO.java @@ -0,0 +1,32 @@ +package com.cultural.heritage.model.vo.good; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class GoodLogisticsInfoVO implements Serializable { + + /** + * 商品名 + */ + private String goods_name; + + + /** + * 商品图片 + */ + private String goods_img_url; + + + @Serial + private static final long serialVersionUID = 1L; +} diff --git a/src/main/java/com/cultural/heritage/model/vo/good/GoodsInfo.java b/src/main/java/com/cultural/heritage/model/vo/good/GoodsInfo.java new file mode 100644 index 0000000..8f93b8e --- /dev/null +++ b/src/main/java/com/cultural/heritage/model/vo/good/GoodsInfo.java @@ -0,0 +1,27 @@ +package com.cultural.heritage.model.vo.good; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class GoodsInfo implements Serializable { + + + /** + * 物流信息商品列表 + */ + private List detail_list; + + + @Serial + private static final long serialVersionUID = 1L; +} diff --git a/src/main/java/com/cultural/heritage/service/wx/WeChatLogisticsService.java b/src/main/java/com/cultural/heritage/service/wx/WeChatLogisticsService.java new file mode 100644 index 0000000..1f14be3 --- /dev/null +++ b/src/main/java/com/cultural/heritage/service/wx/WeChatLogisticsService.java @@ -0,0 +1,19 @@ +package com.cultural.heritage.service.wx; + +import com.cultural.heritage.config.WxAccessToken; + +public interface WeChatLogisticsService { + + /** + * 获取微信token + */ + WxAccessToken getAccessToken(); + + + /** + * 两小时内重新获取token + */ + void addAccessToken(); + + +} diff --git a/src/main/java/com/cultural/heritage/service/wxpay/WeChatService.java b/src/main/java/com/cultural/heritage/service/wx/WeChatService.java similarity index 96% rename from src/main/java/com/cultural/heritage/service/wxpay/WeChatService.java rename to src/main/java/com/cultural/heritage/service/wx/WeChatService.java index b163dad..fb4a2b4 100644 --- a/src/main/java/com/cultural/heritage/service/wxpay/WeChatService.java +++ b/src/main/java/com/cultural/heritage/service/wx/WeChatService.java @@ -1,4 +1,4 @@ -package com.cultural.heritage.service.wxpay; +package com.cultural.heritage.service.wx; import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse; diff --git a/src/main/java/com/cultural/heritage/service/wx/impl/WeChatLogisticsServiceImpl.java b/src/main/java/com/cultural/heritage/service/wx/impl/WeChatLogisticsServiceImpl.java new file mode 100644 index 0000000..5628abe --- /dev/null +++ b/src/main/java/com/cultural/heritage/service/wx/impl/WeChatLogisticsServiceImpl.java @@ -0,0 +1,71 @@ +package com.cultural.heritage.service.wx.impl; + +import cn.hutool.http.HttpUtil; +import com.cultural.heritage.config.WxAccessToken; +import com.cultural.heritage.service.wx.WeChatLogisticsService; +import com.google.gson.Gson; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +import java.util.concurrent.TimeUnit; + +@Slf4j +@Service +public class WeChatLogisticsServiceImpl implements WeChatLogisticsService { + + + private final static String ACCESS_TOKEN_KEY = "accessToken"; + + Logger logger = LoggerFactory.getLogger(WeChatLogisticsService.class); + + @Resource + private RedisTemplate redisTemplate; + + @Resource + private Gson gson; + + + @Schema(description = "小程序 appId") + @Value("${wx.mini.appId}") + private String appId; + + @Schema(description = "小程序 appSecret") + @Value("${wx.mini.appSecret}") + private String appSecret; + + + /** + * 获取微信token + */ + @Override + public WxAccessToken getAccessToken() { + String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appId + "&secret=" + appSecret; + String jsonString = null; + try { + jsonString = HttpUtil.get(url); + } catch (Exception e) { + e.printStackTrace(); + } + return gson.fromJson(jsonString, WxAccessToken.class); + } + + + /** + * 两小时内重新获取token + */ + @Scheduled(fixedDelay = 7080000) + @Override + public void addAccessToken() { + String accessToken = getAccessToken().getAccess_token(); + logger.info("定时任务启用===========" + accessToken); + //微信token2小时过期,每2小时重新获得一次 + redisTemplate.opsForValue().set(ACCESS_TOKEN_KEY, accessToken, 7200, TimeUnit.SECONDS); + } +} diff --git a/src/main/java/com/cultural/heritage/service/wxpay/impl/WeChatServiceImpl.java b/src/main/java/com/cultural/heritage/service/wx/impl/WeChatServiceImpl.java similarity index 99% rename from src/main/java/com/cultural/heritage/service/wxpay/impl/WeChatServiceImpl.java rename to src/main/java/com/cultural/heritage/service/wx/impl/WeChatServiceImpl.java index a7cbe13..9fce07d 100644 --- a/src/main/java/com/cultural/heritage/service/wxpay/impl/WeChatServiceImpl.java +++ b/src/main/java/com/cultural/heritage/service/wx/impl/WeChatServiceImpl.java @@ -1,4 +1,4 @@ -package com.cultural.heritage.service.wxpay.impl; +package com.cultural.heritage.service.wx.impl; import cn.binarywang.wx.miniapp.api.WxMaMsgService; @@ -19,7 +19,7 @@ import com.cultural.heritage.model.entity.OrderItems; import com.cultural.heritage.service.good.GoodService; import com.cultural.heritage.service.order.OrderItemService; import com.cultural.heritage.service.order.OrderService; -import com.cultural.heritage.service.wxpay.WeChatService; +import com.cultural.heritage.service.wx.WeChatService; import com.wechat.pay.java.core.notification.NotificationParser; import com.wechat.pay.java.core.notification.RequestParam; import com.wechat.pay.java.service.payments.jsapi.model.Amount;