diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 6931629..059632e 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -13,6 +13,7 @@ + @@ -26,6 +27,7 @@ + @@ -44,6 +46,8 @@ + + diff --git a/.idea/encodings.xml b/.idea/encodings.xml index 3afc4a8..d60ad9c 100644 --- a/.idea/encodings.xml +++ b/.idea/encodings.xml @@ -9,6 +9,7 @@ + @@ -20,6 +21,7 @@ + diff --git a/atguigu-tuan/common/http.api.js b/atguigu-tuan/common/http.api.js index 965ed62..050c8f7 100644 --- a/atguigu-tuan/common/http.api.js +++ b/atguigu-tuan/common/http.api.js @@ -24,7 +24,7 @@ const post_submit_order = '/order/auth/submitOrder' // 生成订单 const get_order_info = '/order/auth/getOrderInfoById' // 订单详情 const get_wx_login = '/user/weixin/wxLogin' // 微信用户登陆 const post_update_user = '/user/weixin/auth/updateUser' // 更新用户信息 -const get_weixin_payment = '/payment/weixin/createJsapi/' // 获取微信支付信息 +const get_weixin_payment = '/payment/weixin/createJsapi' // 获取微信支付信息 const get_find_user_order = '/order/auth/findUserOrderPage' // 获取用户订单信息 const get_order_status = '/payment/weixin/queryPayStatus' // 获取订单状态 diff --git a/atguigu-tuan/unpackage/dist/dev/mp-weixin/common/vendor.js b/atguigu-tuan/unpackage/dist/dev/mp-weixin/common/vendor.js index aa9b9a6..74a915d 100644 --- a/atguigu-tuan/unpackage/dist/dev/mp-weixin/common/vendor.js +++ b/atguigu-tuan/unpackage/dist/dev/mp-weixin/common/vendor.js @@ -25932,7 +25932,7 @@ var get_order_info = '/order/auth/getOrderInfoById'; // 订单详情 var get_wx_login = '/user/weixin/wxLogin'; // 微信用户登陆 var post_update_user = '/user/weixin/auth/updateUser'; // 更新用户信息 - var get_weixin_payment = '/payment/weixin/createJsapi/'; // 获取微信支付信息 + var get_weixin_payment = '/payment/weixin/createJsapi'; // 获取微信支付信息 var get_find_user_order = '/order/auth/findUserOrderPage'; // 获取用户订单信息 var get_order_status = '/payment/weixin/queryPayStatus'; // 获取订单状态 diff --git a/guigu-ssyx-parent/common/service-util/src/main/java/com/atguigu/ssyx/common/result/Result.java b/guigu-ssyx-parent/common/service-util/src/main/java/com/atguigu/ssyx/common/result/Result.java index 255f02c..8d31ed5 100644 --- a/guigu-ssyx-parent/common/service-util/src/main/java/com/atguigu/ssyx/common/result/Result.java +++ b/guigu-ssyx-parent/common/service-util/src/main/java/com/atguigu/ssyx/common/result/Result.java @@ -26,7 +26,7 @@ public class Result { //设置数据,返回对象的方法 public static Result build(T data, Integer code, String message) { - //创建Result对象,设置值,返回对象 + //创建Resullt对象,设置值,返回对象 Result result = new Result<>(); //判断返回结果中是否需要数据 if (data != null) { @@ -40,9 +40,10 @@ public class Result { return result; } + //设置数据,返回对象的方法 public static Result build(T data, ResultCodeEnum resultCodeEnum) { - //创建Result对象,设置值,返回对象 + //创建Resullt对象,设置值,返回对象 Result result = new Result<>(); //判断返回结果中是否需要数据 if (data != null) { @@ -58,11 +59,13 @@ public class Result { //成功的方法 public static Result ok(T data) { - return build(data, ResultCodeEnum.SUCCESS); + Result result = build(data, ResultCodeEnum.SUCCESS); + return result; } //失败的方法 public static Result fail(T data) { return build(data, ResultCodeEnum.FAIL); } + } diff --git a/guigu-ssyx-parent/common/service-util/src/main/java/com/atguigu/ssyx/common/result/ResultCodeEnum.java b/guigu-ssyx-parent/common/service-util/src/main/java/com/atguigu/ssyx/common/result/ResultCodeEnum.java index 9d852c8..b99243e 100644 --- a/guigu-ssyx-parent/common/service-util/src/main/java/com/atguigu/ssyx/common/result/ResultCodeEnum.java +++ b/guigu-ssyx-parent/common/service-util/src/main/java/com/atguigu/ssyx/common/result/ResultCodeEnum.java @@ -13,18 +13,36 @@ import lombok.Getter; @Getter public enum ResultCodeEnum { - SUCCESS(200, "成功"), FAIL(201, "失败"), SERVICE_ERROR(2012, "服务异常"), DATA_ERROR(204, "数据异常"), ILLEGAL_REQUEST(205, "非法请求"), REPEAT_SUBMIT(206, "重复提交"), + SUCCESS(200, "成功"), + FAIL(201, "失败"), - LOGIN_AUTH(208, "未登陆"), PERMISSION(209, "没有权限"), + SERVICE_ERROR(2012, "服务异常"), + DATA_ERROR(204, "数据异常"), + ILLEGAL_REQUEST(205, "非法请求"), + REPEAT_SUBMIT(206, "重复提交"), - ORDER_PRICE_ERROR(210, "订单商品价格变化"), ORDER_STOCK_FALL(204, "订单库存锁定失败"), CREATE_ORDER_FAIL(210, "创建订单失败"), + LOGIN_AUTH(208, "未登陆"), + PERMISSION(209, "没有权限"), - COUPON_GET(220, "优惠券已经领取"), COUPON_LIMIT_GET(221, "优惠券已发放完毕"), + ORDER_PRICE_ERROR(210, "订单商品价格变化"), + ORDER_STOCK_FALL(204, "订单库存锁定失败"), + CREATE_ORDER_FAIL(210, "创建订单失败"), - URL_ENCODE_ERROR(216, "URL编码失败"), ILLEGAL_CALLBACK_REQUEST_ERROR(217, "非法回调请求"), FETCH_ACCESSTOKEN_FAILD(218, "获取accessToken失败"), FETCH_USERINFO_ERROR(219, "获取用户信息失败"), + COUPON_GET(220, "优惠券已经领取"), + COUPON_LIMIT_GET(221, "优惠券已发放完毕"), + + URL_ENCODE_ERROR(216, "URL编码失败"), + ILLEGAL_CALLBACK_REQUEST_ERROR(217, "非法回调请求"), + FETCH_ACCESSTOKEN_FAILD(218, "获取accessToken失败"), + FETCH_USERINFO_ERROR(219, "获取用户信息失败"), - SKU_LIMIT_ERROR(230, "购买个数不能大于限购个数"), REGION_OPEN(240, "该区域已开通"), REGION_NO_OPEN(240, "该区域未开通"), + SKU_LIMIT_ERROR(230, "购买个数不能大于限购个数"), + REGION_OPEN(240, "该区域已开通"), + REGION_NO_OPEN(240, "该区域未开通"), + PAYMENT_WAITING(242, "订单支付中"), + PAYMENT_SUCCESS(241, "订单支付成功"), + PAYMENT_FAIL(243, "订单支付失败"), ; private final Integer code; diff --git a/guigu-ssyx-parent/service-client/pom.xml b/guigu-ssyx-parent/service-client/pom.xml index 2cf6840..c061650 100644 --- a/guigu-ssyx-parent/service-client/pom.xml +++ b/guigu-ssyx-parent/service-client/pom.xml @@ -17,6 +17,7 @@ service-search-client service-activity-client service-cart-client + service-order-client diff --git a/guigu-ssyx-parent/service-client/service-order-client/pom.xml b/guigu-ssyx-parent/service-client/service-order-client/pom.xml new file mode 100644 index 0000000..a58059d --- /dev/null +++ b/guigu-ssyx-parent/service-client/service-order-client/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + com.atguigu + service-client + 1.0-SNAPSHOT + + + service-order-client + + + 8 + 8 + UTF-8 + + + diff --git a/guigu-ssyx-parent/service-client/service-order-client/src/main/java/com/atguigu/ssyx/client/order/OrderFeignClient.java b/guigu-ssyx-parent/service-client/service-order-client/src/main/java/com/atguigu/ssyx/client/order/OrderFeignClient.java new file mode 100644 index 0000000..d43fb2f --- /dev/null +++ b/guigu-ssyx-parent/service-client/service-order-client/src/main/java/com/atguigu/ssyx/client/order/OrderFeignClient.java @@ -0,0 +1,22 @@ +package com.atguigu.ssyx.client.order; + +import com.atguigu.ssyx.model.order.OrderInfo; +import io.swagger.annotations.ApiOperation; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; + +/** + * ClassName: OrderFeignClient + * Package: com.atguigu.ssyx.client.order + * + * @author yovinchen + * @Create 2023/10/13 11:03 + */ +@FeignClient(value = "service-order") +public interface OrderFeignClient { + + @ApiOperation(value = "根据orderNo查询订单信息") + @GetMapping("/api/order/inner/getOrderInfo/{orderNo}") + OrderInfo getOrderInfo(@PathVariable("orderNo") String orderNo); +} diff --git a/guigu-ssyx-parent/service/pom.xml b/guigu-ssyx-parent/service/pom.xml index 9c16709..3b3e859 100644 --- a/guigu-ssyx-parent/service/pom.xml +++ b/guigu-ssyx-parent/service/pom.xml @@ -21,6 +21,7 @@ service-home service-cart service-order + service-payment diff --git a/guigu-ssyx-parent/service/service-cart/src/main/resources/application-dev.yml b/guigu-ssyx-parent/service/service-cart/src/main/resources/application-dev.yml index d685efb..bd43581 100644 --- a/guigu-ssyx-parent/service/service-cart/src/main/resources/application-dev.yml +++ b/guigu-ssyx-parent/service/service-cart/src/main/resources/application-dev.yml @@ -12,6 +12,11 @@ feign: spring: main: allow-bean-definition-overriding: true #当遇到同样名字的时候,是否允许覆盖注册 + rabbitmq: + host: 43.143.164.194 + port: 5672 + username: guest + password: guest redis: host: 82.157.68.223 port: 6379 diff --git a/guigu-ssyx-parent/service/service-order/src/main/java/com/atguigu/ssyx/order/controller/OrderInfoController.java b/guigu-ssyx-parent/service/service-order/src/main/java/com/atguigu/ssyx/order/controller/OrderInfoController.java index a41b27f..293f687 100644 --- a/guigu-ssyx-parent/service/service-order/src/main/java/com/atguigu/ssyx/order/controller/OrderInfoController.java +++ b/guigu-ssyx-parent/service/service-order/src/main/java/com/atguigu/ssyx/order/controller/OrderInfoController.java @@ -1,12 +1,17 @@ package com.atguigu.ssyx.order.controller; +import com.atguigu.ssyx.common.auth.AuthContextHolder; import com.atguigu.ssyx.common.result.Result; import com.atguigu.ssyx.model.order.OrderInfo; import com.atguigu.ssyx.order.service.OrderInfoService; import com.atguigu.ssyx.vo.order.OrderConfirmVo; import com.atguigu.ssyx.vo.order.OrderSubmitVo; +import com.atguigu.ssyx.vo.order.OrderUserQueryVo; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -22,10 +27,28 @@ import org.springframework.web.bind.annotation.*; @RequestMapping("/api/order") public class OrderInfoController { - @Autowired private OrderInfoService orderInfoService; + @ApiOperation(value = "订单查询") + @GetMapping("auth/findUserOrderPage/{page}/{limit}") + public Result findUserOrderPage( + @ApiParam(name = "page", value = "当前页码", required = true) + @PathVariable Long page, + @ApiParam(name = "limit", value = "每页记录数", required = true) + @PathVariable Long limit, + @ApiParam(name = "orderVo", value = "查询对象", required = false) + OrderUserQueryVo orderUserQueryVo) { + //获取userId + Long userId = AuthContextHolder.getUserId(); + orderUserQueryVo.setUserId(userId); + + //分页查询条件 + Page pageParam = new Page<>(page, limit); + IPage pageModel = orderInfoService.getOrderInfoByUserIdPage(pageParam, orderUserQueryVo); + return Result.ok(pageModel); + } + @ApiOperation("确认订单") @GetMapping("auth/confirmOrder") public Result confirm() { diff --git a/guigu-ssyx-parent/service/service-order/src/main/java/com/atguigu/ssyx/order/receiver/OrderReceiver.java b/guigu-ssyx-parent/service/service-order/src/main/java/com/atguigu/ssyx/order/receiver/OrderReceiver.java new file mode 100644 index 0000000..e049321 --- /dev/null +++ b/guigu-ssyx-parent/service/service-order/src/main/java/com/atguigu/ssyx/order/receiver/OrderReceiver.java @@ -0,0 +1,44 @@ +package com.atguigu.ssyx.order.receiver; + +import com.atguigu.ssyx.mq.constant.MqConst; +import com.atguigu.ssyx.order.service.OrderInfoService; +import com.rabbitmq.client.Channel; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.rabbit.annotation.Exchange; +import org.springframework.amqp.rabbit.annotation.Queue; +import org.springframework.amqp.rabbit.annotation.QueueBinding; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import java.io.IOException; + +/** + * ClassName: OrderReceiver + * Package: com.atguigu.ssyx.order.receiver + * + * @author yovinchen + * @Create 2023/10/13 14:15 + */ +@Component +public class OrderReceiver { + @Autowired + private OrderInfoService orderInfoService; + + //订单支付成功,更新订单状态,扣减库存 + @RabbitListener(bindings = @QueueBinding( + value = @Queue(value = MqConst.QUEUE_ORDER_PAY, durable = "true"), + exchange = @Exchange(value = MqConst.EXCHANGE_PAY_DIRECT), + key = {MqConst.ROUTING_PAY_SUCCESS} + )) + public void orderPay(String orderNo, + Message message, + Channel channel) throws IOException { + if (!StringUtils.isEmpty(orderNo)) { + orderInfoService.orderPay(orderNo); + } + channel.basicAck(message.getMessageProperties().getDeliveryTag(), + false); + } +} diff --git a/guigu-ssyx-parent/service/service-order/src/main/java/com/atguigu/ssyx/order/service/OrderInfoService.java b/guigu-ssyx-parent/service/service-order/src/main/java/com/atguigu/ssyx/order/service/OrderInfoService.java index f84316c..9121965 100644 --- a/guigu-ssyx-parent/service/service-order/src/main/java/com/atguigu/ssyx/order/service/OrderInfoService.java +++ b/guigu-ssyx-parent/service/service-order/src/main/java/com/atguigu/ssyx/order/service/OrderInfoService.java @@ -3,6 +3,9 @@ package com.atguigu.ssyx.order.service; import com.atguigu.ssyx.model.order.OrderInfo; import com.atguigu.ssyx.vo.order.OrderConfirmVo; import com.atguigu.ssyx.vo.order.OrderSubmitVo; +import com.atguigu.ssyx.vo.order.OrderUserQueryVo; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; /** @@ -45,4 +48,20 @@ public interface OrderInfoService extends IService { * @return */ OrderInfo getOrderInfoByOrderNo(String orderNo); + + /** + * 订单支付成功,更新订单状态,扣减库存 + * + * @param orderNo + */ + void orderPay(String orderNo); + + /** + * 订单查询 + * + * @param pageParam + * @param orderUserQueryVo + * @return + */ + IPage getOrderInfoByUserIdPage(Page pageParam, OrderUserQueryVo orderUserQueryVo); } diff --git a/guigu-ssyx-parent/service/service-order/src/main/java/com/atguigu/ssyx/order/service/impl/OrderInfoServiceImpl.java b/guigu-ssyx-parent/service/service-order/src/main/java/com/atguigu/ssyx/order/service/impl/OrderInfoServiceImpl.java index 72316f1..5574ab1 100644 --- a/guigu-ssyx-parent/service/service-order/src/main/java/com/atguigu/ssyx/order/service/impl/OrderInfoServiceImpl.java +++ b/guigu-ssyx-parent/service/service-order/src/main/java/com/atguigu/ssyx/order/service/impl/OrderInfoServiceImpl.java @@ -24,9 +24,12 @@ import com.atguigu.ssyx.order.service.OrderItemService; import com.atguigu.ssyx.vo.order.CartInfoVo; import com.atguigu.ssyx.vo.order.OrderConfirmVo; import com.atguigu.ssyx.vo.order.OrderSubmitVo; +import com.atguigu.ssyx.vo.order.OrderUserQueryVo; import com.atguigu.ssyx.vo.product.SkuStockLockVo; import com.atguigu.ssyx.vo.user.LeaderAddressVo; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.BoundHashOperations; @@ -328,7 +331,61 @@ public class OrderInfoServiceImpl extends ServiceImpl().eq(OrderInfo::getOrderNo, orderNo)); + } + + /** + * 订单支付成功,更新订单状态,扣减库存 + * + * @param orderNo + */ + @Override + public void orderPay(String orderNo) { + //查询订单状态是否已经修改完成了支付状态 + OrderInfo orderInfo = this.getOrderInfoByOrderNo(orderNo); + if (orderInfo == null || orderInfo.getOrderStatus() != OrderStatus.UNPAID) { + return; + } + //更新状态 + this.updateOrderStatus(orderInfo.getId()); + + //扣减库存 + rabbitService.sendMessage(MqConst.EXCHANGE_ORDER_DIRECT, MqConst.ROUTING_MINUS_STOCK, orderNo); + } + + /** + * 订单查询 + * + * @param pageParam + * @param orderUserQueryVo + * @return + */ + @Override + public IPage getOrderInfoByUserIdPage(Page pageParam, OrderUserQueryVo orderUserQueryVo) { + IPage pageModel = baseMapper.selectPage(pageParam, new LambdaQueryWrapper() + .eq(OrderInfo::getUserId, orderUserQueryVo.getUserId()) + .eq(OrderInfo::getOrderStatus, orderUserQueryVo.getOrderStatus())); + + //获取每个订单,把每个订单里面订单项查询封装 + List orderInfoList = pageModel.getRecords(); + for (OrderInfo orderInfo : orderInfoList) { + //根据订单id查询里面所有订单项列表 + List orderItemList = orderItemMapper.selectList(new LambdaQueryWrapper() + .eq(OrderItem::getOrderId, orderInfo.getId())); + //把订单项集合封装到每个订单里面 + orderInfo.setOrderItemList(orderItemList); + //封装订单状态名称 + orderInfo.getParam().put("orderStatusName", orderInfo.getOrderStatus().getComment()); + } + return pageModel; + } + + //更新状态 + private void updateOrderStatus(Long id) { + OrderInfo orderInfo = baseMapper.selectById(id); + orderInfo.setOrderStatus(OrderStatus.WAITING_DELEVER); + orderInfo.setProcessStatus(ProcessStatus.WAITING_DELEVER); + baseMapper.updateById(orderInfo); } //计算总金额 diff --git a/guigu-ssyx-parent/service/service-payment/Dockerfile b/guigu-ssyx-parent/service/service-payment/Dockerfile new file mode 100644 index 0000000..b04938c --- /dev/null +++ b/guigu-ssyx-parent/service/service-payment/Dockerfile @@ -0,0 +1,5 @@ +FROM openjdk:8-jdk-alpine +LABEL authors="yovinchen" +VOLUME /tmp +ADD ./target/service-payment.jar service-order.jar +ENTRYPOINT ["java","-jar","/service-payment.jar", "&"] diff --git a/guigu-ssyx-parent/service/service-payment/pom.xml b/guigu-ssyx-parent/service/service-payment/pom.xml new file mode 100644 index 0000000..00f322c --- /dev/null +++ b/guigu-ssyx-parent/service/service-payment/pom.xml @@ -0,0 +1,39 @@ + + + 4.0.0 + + com.atguigu + service + 1.0-SNAPSHOT + + + service-payment + + + + com.github.wxpay + wxpay-sdk + 0.0.3 + + + com.atguigu + service-order-client + 1.0-SNAPSHOT + compile + + + com.atguigu + rabbit_util + 1.0-SNAPSHOT + compile + + + + 8 + 8 + UTF-8 + + + diff --git a/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/ServicePaymentApplication.java b/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/ServicePaymentApplication.java new file mode 100644 index 0000000..2a6b66e --- /dev/null +++ b/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/ServicePaymentApplication.java @@ -0,0 +1,22 @@ +package com.atguigu.ssyx; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.openfeign.EnableFeignClients; + +/** + * ClassName: ServicePaymentApplication + * Package: vom.atguigu.ssyx + * + * @author yovinchen + * @Create 2023/10/13 10:29 + */ +@SpringBootApplication +@EnableDiscoveryClient +@EnableFeignClients +public class ServicePaymentApplication { + public static void main(String[] args) { + SpringApplication.run(ServicePaymentApplication.class, args); + } +} diff --git a/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/payment/controller/WeiXinController.java b/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/payment/controller/WeiXinController.java new file mode 100644 index 0000000..409908e --- /dev/null +++ b/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/payment/controller/WeiXinController.java @@ -0,0 +1,66 @@ +package com.atguigu.ssyx.payment.controller; + +import com.atguigu.ssyx.common.result.Result; +import com.atguigu.ssyx.common.result.ResultCodeEnum; +import com.atguigu.ssyx.payment.service.PaymentInfoService; +import com.atguigu.ssyx.payment.service.WeixinService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Map; + +/** + * ClassName: WeiXinController + * Package: com.atguigu.ssyx.payment.controller + * 微信支付API + * + * @author yovinchen + * @Create 2023/10/13 10:32 + */ +@Api(tags = "微信支付接口") +@RestController +@RequestMapping("/api/payment/weixin") +@Slf4j +public class WeiXinController { + + @Autowired + private WeixinService weixinService; + + @Autowired + private PaymentInfoService paymentInfoService; + + @ApiOperation(value = "调用微信支付系统生成预付单") + @GetMapping("/createJsapi/{orderNo}") + public Result createJsapi(@PathVariable("orderNo") String orderNo) { + Map map = weixinService.createJsapi(orderNo); + return Result.ok(map); + } + + @ApiOperation(value = "查询订单支付状态") + @GetMapping("/queryPayStatus/{orderNo}") + public Result queryPayStatus(@PathVariable("orderNo") String orderNo) { + //1 调用微信支付系统接口查询订单支付状态 + Map resultMap = weixinService.queryPayStatus(orderNo); + + //2 微信支付系统返回值为null,支付失败 + if (resultMap == null) { + return Result.build(null, ResultCodeEnum.PAYMENT_FAIL); + } + + //3 如果微信支付系统返回值,判断支付成功 + if ("SUCCESS".equals(resultMap.get("trade_state"))) { + String out_trade_no = resultMap.get("out_trade_no"); + paymentInfoService.paySuccess(out_trade_no, resultMap); + return Result.ok(null); + } + + //4 支付中,等待 + return Result.build(null, ResultCodeEnum.PAYMENT_WAITING); + } +} diff --git a/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/payment/mapper/PaymentInfoMapper.java b/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/payment/mapper/PaymentInfoMapper.java new file mode 100644 index 0000000..6706c07 --- /dev/null +++ b/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/payment/mapper/PaymentInfoMapper.java @@ -0,0 +1,16 @@ +package com.atguigu.ssyx.payment.mapper; + +import com.atguigu.ssyx.model.order.PaymentInfo; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + *

+ * 支付信息表 Mapper 接口 + *

+ * + * @author atguigu + * @since 2023-10-12 + */ +public interface PaymentInfoMapper extends BaseMapper { + +} diff --git a/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/payment/mapper/xml/PaymentInfoMapper.xml b/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/payment/mapper/xml/PaymentInfoMapper.xml new file mode 100644 index 0000000..06b9d25 --- /dev/null +++ b/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/payment/mapper/xml/PaymentInfoMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/payment/service/PaymentInfoService.java b/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/payment/service/PaymentInfoService.java new file mode 100644 index 0000000..5a56094 --- /dev/null +++ b/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/payment/service/PaymentInfoService.java @@ -0,0 +1,40 @@ +package com.atguigu.ssyx.payment.service; + +import com.atguigu.ssyx.model.order.PaymentInfo; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.Map; + +/** + * ClassName: PaymentInfoService + * Package: com.atguigu.ssyx.payment.service + * + * @author yovinchen + * @Create 2023/10/13 10:33 + */ + +public interface PaymentInfoService extends IService { + /** + * 根据orderNo查询支付订单 + * + * @param orderNo + * @return + */ + PaymentInfo getPaymentInfoByOrderNo(String orderNo); + + /** + * 保存支付订单 + * + * @param orderNo + * @return + */ + PaymentInfo savePaymentInfo(String orderNo); + + /** + * 支付成功 + * + * @param orderNo + * @param resultMap + */ + void paySuccess(String orderNo, Map resultMap); +} diff --git a/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/payment/service/WeixinService.java b/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/payment/service/WeixinService.java new file mode 100644 index 0000000..530f535 --- /dev/null +++ b/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/payment/service/WeixinService.java @@ -0,0 +1,28 @@ +package com.atguigu.ssyx.payment.service; + +import java.util.Map; + +/** + * ClassName: WeixinService + * Package: com.atguigu.ssyx.payment.controller.payment.service + * + * @author yovinchen + * @Create 2023/10/13 10:32 + */ +public interface WeixinService { + /** + * 调用微信支付系统生成预付单 + * + * @param orderNo + * @return + */ + Map createJsapi(String orderNo); + + /** + * 调用微信支付系统接口查询订单支付状态 + * + * @param orderNo + * @return + */ + Map queryPayStatus(String orderNo); +} diff --git a/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/payment/service/impl/PaymentInfoServiceImpl.java b/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/payment/service/impl/PaymentInfoServiceImpl.java new file mode 100644 index 0000000..bf53c69 --- /dev/null +++ b/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/payment/service/impl/PaymentInfoServiceImpl.java @@ -0,0 +1,111 @@ +package com.atguigu.ssyx.payment.service.impl; + +import com.atguigu.ssyx.client.order.OrderFeignClient; +import com.atguigu.ssyx.common.exception.SsyxException; +import com.atguigu.ssyx.common.result.ResultCodeEnum; +import com.atguigu.ssyx.enums.PaymentStatus; +import com.atguigu.ssyx.enums.PaymentType; +import com.atguigu.ssyx.model.order.OrderInfo; +import com.atguigu.ssyx.model.order.PaymentInfo; +import com.atguigu.ssyx.mq.constant.MqConst; +import com.atguigu.ssyx.mq.service.RabbitService; +import com.atguigu.ssyx.payment.mapper.PaymentInfoMapper; +import com.atguigu.ssyx.payment.service.PaymentInfoService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.Map; + +/** + *

+ * 支付信息表 服务实现类 + *

+ * + * @author atguigu + * @since 2023-10-12 + */ +@Service +public class PaymentInfoServiceImpl extends ServiceImpl implements PaymentInfoService { + + + @Autowired + private OrderFeignClient orderFeignClient; + + @Autowired + private RabbitService rabbitService; + + /** + * 根据orderNo查询支付订单 + * + * @param orderNo + * @return + */ + @Override + public PaymentInfo getPaymentInfoByOrderNo(String orderNo) { + return baseMapper.selectOne(new LambdaQueryWrapper().eq(PaymentInfo::getOrderNo, orderNo)); + } + + /** + * 保存支付订单 + * + * @param orderNo + * @return + */ + @Override + public PaymentInfo savePaymentInfo(String orderNo) { + //远程调用调用,根据orderNo查询订单信息 + OrderInfo orderInfo = orderFeignClient.getOrderInfo(orderNo); + if (orderInfo == null) { + throw new SsyxException(ResultCodeEnum.DATA_ERROR); + } + //封装到PaymentInfo对象 + PaymentInfo paymentInfo = new PaymentInfo(); + paymentInfo.setCreateTime(new Date()); + paymentInfo.setOrderId(orderInfo.getId()); + paymentInfo.setPaymentType(PaymentType.WEIXIN); + paymentInfo.setUserId(orderInfo.getUserId()); + paymentInfo.setOrderNo(orderInfo.getOrderNo()); + paymentInfo.setPaymentStatus(PaymentStatus.UNPAID); + String subject = "userID:" + orderInfo.getUserId() + "下订单"; + paymentInfo.setSubject(subject); + //paymentInfo.setTotalAmount(orderInfo.getTotalAmount()); + //TODO 为了测试 + paymentInfo.setTotalAmount(new BigDecimal("0.01")); + + //调用方法实现添加 + baseMapper.insert(paymentInfo); + return paymentInfo; + } + + /** + * 支付成功 + * + * @param orderNo + * @param resultMap + */ + @Override + public void paySuccess(String orderNo, Map resultMap) { + //1 查询当前订单支付记录表状态是否是已经支付 + PaymentInfo paymentInfo = baseMapper.selectOne( + new LambdaQueryWrapper() + .eq(PaymentInfo::getOrderNo, orderNo) + ); + if (paymentInfo.getPaymentStatus() != PaymentStatus.UNPAID) { + return; + } + + //2 如果支付记录表支付状态没有支付,更新 + paymentInfo.setPaymentStatus(PaymentStatus.PAID); + paymentInfo.setTradeNo(resultMap.get("transaction_id")); + paymentInfo.setCallbackContent(resultMap.toString()); + baseMapper.updateById(paymentInfo); + + //3 整合RabbitMQ实现 修改订单记录已经支付,库存扣减 + rabbitService.sendMessage(MqConst.EXCHANGE_PAY_DIRECT, + MqConst.ROUTING_PAY_SUCCESS, orderNo); + } +} diff --git a/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/payment/service/impl/WeXinServiceImpl.java b/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/payment/service/impl/WeXinServiceImpl.java new file mode 100644 index 0000000..7fbc428 --- /dev/null +++ b/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/payment/service/impl/WeXinServiceImpl.java @@ -0,0 +1,140 @@ +package com.atguigu.ssyx.payment.service.impl; + +import com.atguigu.ssyx.common.constant.RedisConst; +import com.atguigu.ssyx.model.order.PaymentInfo; +import com.atguigu.ssyx.payment.service.PaymentInfoService; +import com.atguigu.ssyx.payment.service.WeixinService; +import com.atguigu.ssyx.payment.utils.ConstantPropertiesUtils; +import com.atguigu.ssyx.payment.utils.HttpClient; +import com.atguigu.ssyx.vo.user.UserLoginVo; +import com.github.wxpay.sdk.WXPayUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * ClassName: WeXinServiceImpl + * Package: com.atguigu.ssyx.payment.service.impl + * + * @author yovinchen + * @Create 2023/10/13 10:33 + */ +@Service +public class WeXinServiceImpl implements WeixinService { + + + @Autowired + private PaymentInfoService paymentInfoService; + + @Autowired + private RedisTemplate redisTemplate; + + /** + * 调用微信支付系统生成预付单 + * + * @param orderNo + * @return + */ + @Override + public Map createJsapi(String orderNo) { + //1 向payment_info支付记录表添加记录,目前支付状态:正在支付中 + PaymentInfo paymentInfo = paymentInfoService.getPaymentInfoByOrderNo(orderNo); + if (paymentInfo == null) { + paymentInfo = paymentInfoService.savePaymentInfo(orderNo); + } + + //2 封装微信支付系统接口需要参数 + Map paramMap = new HashMap<>(); + paramMap.put("appid", ConstantPropertiesUtils.APPID); + paramMap.put("mch_id", ConstantPropertiesUtils.PARTNER); + paramMap.put("nonce_str", WXPayUtil.generateNonceStr()); + paramMap.put("body", paymentInfo.getSubject()); + paramMap.put("out_trade_no", paymentInfo.getOrderNo()); + int totalFee = paymentInfo.getTotalAmount().multiply(new BigDecimal(100)).intValue(); + paramMap.put("total_fee", String.valueOf(totalFee)); + paramMap.put("spbill_create_ip", "127.0.0.1"); + paramMap.put("notify_url", ConstantPropertiesUtils.NOTIFYURL); + paramMap.put("trade_type", "JSAPI"); + + //openid + UserLoginVo userLoginVo = (UserLoginVo) redisTemplate.opsForValue().get(RedisConst.USER_LOGIN_KEY_PREFIX + paymentInfo.getUserId()); + if (null != userLoginVo && !StringUtils.isEmpty(userLoginVo.getOpenId())) { + paramMap.put("openid", userLoginVo.getOpenId()); + } else { + paramMap.put("openid", "odo3j4q2KskkbbW-krfE-cAxUnzU1"); + } + //3 使用HttpClient调用微信支付系统接口 + HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder"); + //设置参数,xml格式 + try { + client.setXmlParam(WXPayUtil.generateSignedXml(paramMap, ConstantPropertiesUtils.PARTNERKEY)); + client.setHttps(true); + client.post(); + + //4 调用微信支付系统接口之后,返回结果 prepay_id + String xml = client.getContent(); + Map resultMap = WXPayUtil.xmlToMap(xml); + + //5 封装需要数据-包含预付单标识 prepay_id + Map parameterMap = new HashMap<>(); + String prepayId = String.valueOf(resultMap.get("prepay_id")); + String packages = "prepay_id=" + prepayId; + parameterMap.put("appId", ConstantPropertiesUtils.APPID); + parameterMap.put("nonceStr", resultMap.get("nonce_str")); + parameterMap.put("package", packages); + parameterMap.put("signType", "MD5"); + parameterMap.put("timeStamp", String.valueOf(new Date().getTime())); + String sign = WXPayUtil.generateSignature(parameterMap, ConstantPropertiesUtils.PARTNERKEY); + + //返回结果 + Map result = new HashMap(); + result.put("timeStamp", parameterMap.get("timeStamp")); + result.put("nonceStr", parameterMap.get("nonceStr")); + result.put("signType", "MD5"); + result.put("paySign", sign); + result.put("package", packages); + + //6 返回结果 + return result; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * 调用微信支付系统接口查询订单支付状态 + * + * @param orderNo + * @return + */ + @Override + public Map queryPayStatus(String orderNo) { + //封装数据、 + Map paramMap = new HashMap(); + paramMap.put("appid", ConstantPropertiesUtils.APPID); + paramMap.put("mch_id", ConstantPropertiesUtils.PARTNER); + paramMap.put("out_trade_no", orderNo); + paramMap.put("nonce_str", WXPayUtil.generateNonceStr()); + + //2、设置请求 + HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery"); + try { + client.setXmlParam(WXPayUtil.generateSignedXml(paramMap, ConstantPropertiesUtils.PARTNERKEY)); + client.setHttps(true); + client.post(); + + //3 得到返回结果 + String xml = client.getContent(); + Map stringMap = WXPayUtil.xmlToMap(xml); + return stringMap; + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/payment/utils/ConstantPropertiesUtils.java b/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/payment/utils/ConstantPropertiesUtils.java new file mode 100644 index 0000000..b464eca --- /dev/null +++ b/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/payment/utils/ConstantPropertiesUtils.java @@ -0,0 +1,42 @@ +package com.atguigu.ssyx.payment.utils; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * ClassName: ConstantPropertiesUtils + * Package: com.atguigu.ssyx.payment.utils + * + * @author yovinchen + * @Create 2023/10/13 10:48 + */ +@Component +public class ConstantPropertiesUtils implements InitializingBean { + + public static String APPID; + public static String PARTNER; + public static String PARTNERKEY; + public static String NOTIFYURL; + public static String CERT; + @Value("${weixin.appid}") + private String appid; + @Value("${weixin.partner}") + private String partner; + @Value("${weixin.partnerkey}") + private String partnerkey; + @Value("${weixin.notifyurl}") + private String notifyurl; + @Value("${weixin.cert}") + private String cert; + + @Override + public void afterPropertiesSet() throws Exception { + APPID = appid; + PARTNER = partner; + PARTNERKEY = partnerkey; + NOTIFYURL = notifyurl; + CERT = cert; + } +} + diff --git a/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/payment/utils/HttpClient.java b/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/payment/utils/HttpClient.java new file mode 100644 index 0000000..4bfe322 --- /dev/null +++ b/guigu-ssyx-parent/service/service-payment/src/main/java/com/atguigu/ssyx/payment/utils/HttpClient.java @@ -0,0 +1,207 @@ +package com.atguigu.ssyx.payment.utils; + +import org.apache.http.Consts; +import org.apache.http.HttpEntity; +import org.apache.http.NameValuePair; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.*; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLContextBuilder; +import org.apache.http.conn.ssl.TrustStrategy; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.ssl.SSLContexts; +import org.apache.http.util.EntityUtils; + +import javax.net.ssl.SSLContext; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.security.KeyStore; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.text.ParseException; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * ClassName: HttpClient + * Package: com.atguigu.ssyx.payment.utils + * + * @author yovinchen + * @Create 2023/10/13 10:47 + */ +public class HttpClient { + + private String url; + private Map param; + private int statusCode; + private String content; + private String xmlParam; + private boolean isHttps; + private boolean isCert = false; + //证书密码 微信商户号(mch_id) + private String certPassword; + + public HttpClient(String url, Map param) { + this.url = url; + this.param = param; + } + + public HttpClient(String url) { + this.url = url; + } + + public boolean isHttps() { + return isHttps; + } + + public void setHttps(boolean isHttps) { + this.isHttps = isHttps; + } + + public boolean isCert() { + return isCert; + } + + public void setCert(boolean cert) { + isCert = cert; + } + + public String getXmlParam() { + return xmlParam; + } + + public void setXmlParam(String xmlParam) { + this.xmlParam = xmlParam; + } + + public String getCertPassword() { + return certPassword; + } + + public void setCertPassword(String certPassword) { + this.certPassword = certPassword; + } + + public void setParameter(Map map) { + param = map; + } + + public void addParameter(String key, String value) { + if (param == null) + param = new HashMap(); + param.put(key, value); + } + + public void post() throws IOException { + HttpPost http = new HttpPost(url); + setEntity(http); + execute(http); + } + + public void put() throws IOException { + HttpPut http = new HttpPut(url); + setEntity(http); + execute(http); + } + + public void get() throws IOException { + if (param != null) { + StringBuilder url = new StringBuilder(this.url); + boolean isFirst = true; + for (String key : param.keySet()) { + if (isFirst) + url.append("?"); + else + url.append("&"); + url.append(key).append("=").append(param.get(key)); + } + this.url = url.toString(); + } + HttpGet http = new HttpGet(url); + execute(http); + } + + /** + * set http post,put param + */ + private void setEntity(HttpEntityEnclosingRequestBase http) { + if (param != null) { + List nvps = new LinkedList(); + for (String key : param.keySet()) + nvps.add(new BasicNameValuePair(key, param.get(key))); // 参数 + http.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8)); // 设置参数 + } + if (xmlParam != null) { + http.setEntity(new StringEntity(xmlParam, Consts.UTF_8)); + } + } + + private void execute(HttpUriRequest http) throws + IOException { + CloseableHttpClient httpClient = null; + try { + if (isHttps) { + if (isCert) { + FileInputStream inputStream = new FileInputStream(new File(ConstantPropertiesUtils.CERT)); + KeyStore keystore = KeyStore.getInstance("PKCS12"); + char[] partnerId2charArray = certPassword.toCharArray(); + keystore.load(inputStream, partnerId2charArray); + SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(keystore, partnerId2charArray).build(); + SSLConnectionSocketFactory sslsf = + new SSLConnectionSocketFactory(sslContext, + new String[]{"TLSv1"}, + null, + SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); + httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build(); + } else { + SSLContext sslContext = new SSLContextBuilder() + .loadTrustMaterial(null, new TrustStrategy() { + // 信任所有 + public boolean isTrusted(X509Certificate[] chain, + String authType) + throws CertificateException { + return true; + } + }).build(); + SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( + sslContext); + httpClient = HttpClients.custom().setSSLSocketFactory(sslsf) + .build(); + } + } else { + httpClient = HttpClients.createDefault(); + } + CloseableHttpResponse response = httpClient.execute(http); + try { + if (response != null) { + if (response.getStatusLine() != null) + statusCode = response.getStatusLine().getStatusCode(); + HttpEntity entity = response.getEntity(); + // 响应内容 + content = EntityUtils.toString(entity, Consts.UTF_8); + } + } finally { + response.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + httpClient.close(); + } + } + + public int getStatusCode() { + return statusCode; + } + + public String getContent() throws ParseException, IOException { + return content; + } +} diff --git a/guigu-ssyx-parent/service/service-payment/src/main/resources/application-dev.yml b/guigu-ssyx-parent/service/service-payment/src/main/resources/application-dev.yml new file mode 100644 index 0000000..d91db2f --- /dev/null +++ b/guigu-ssyx-parent/service/service-payment/src/main/resources/application-dev.yml @@ -0,0 +1,60 @@ +server: + port: 8210 +mybatis-plus: + type-enums-package: com.atguigu.ssyx.enums + configuration: + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + mapper-locations: classpath:mapper/*.xml +feign: + sentinel: + enabled: true + client: + config: + default: #配置全局的feign的调用超时时间 如果 有指定的服务配置 默认的配置不会生效 + connectTimeout: 30000 # 指定的是 消费者 连接服务提供者的连接超时时间 是否能连接 单位是毫秒 + readTimeout: 50000 # 指定的是调用服务提供者的 服务 的超时时间() 单位是毫秒 +spring: + main: + allow-bean-definition-overriding: true #当遇到同样名字的时候,是否允许覆盖注册 + rabbitmq: + host: 43.143.164.194 + port: 5672 + username: guest + password: guest + redis: + host: 82.157.68.223 + port: 6379 + database: 0 + timeout: 1800000 + password: + lettuce: + pool: + max-active: 20 #最大连接数 + max-wait: -1 #最大阻塞等待时间(负数表示没限制) + max-idle: 5 #最大空闲 + min-idle: 0 #最小空闲 + datasource: + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://82.157.68.223:3306/shequ-order?characterEncoding=utf-8&useSSL=false + username: shequ-order + password: shequ-order + hikari: + connection-test-query: SELECT 1 + connection-timeout: 60000 + idle-timeout: 500000 + max-lifetime: 540000 + maximum-pool-size: 5 + minimum-idle: 3 + pool-name: GuliHikariPool + jackson: + date-format: yyyy-MM-dd HH:mm:ss + time-zone: GMT+8 +# 微信 +weixin: + #小程序微信公众平台appId + appid: wxcc651fcbab275e33 + partner: 1481962542 + partnerkey: MXb72b9RfshXZD4FRGV5KLqmv5bx9LT9 + notifyurl: http://gmall-prod.atguigu.cn/api/payment/weixin/notify + cert: /Users/yovinchen/project/Java/ssyx/guigu-ssyx-parent/service/service-payment/src/main/resources/cert/apiclient_cert.p12 diff --git a/guigu-ssyx-parent/service/service-payment/src/main/resources/application.yml b/guigu-ssyx-parent/service/service-payment/src/main/resources/application.yml new file mode 100644 index 0000000..ec33dce --- /dev/null +++ b/guigu-ssyx-parent/service/service-payment/src/main/resources/application.yml @@ -0,0 +1,11 @@ +spring: + application: + name: service-payment + profiles: + active: dev + cloud: + nacos: + discovery: + server-addr: 82.157.68.223:8848 + username: nacos + password: nacos diff --git a/guigu-ssyx-parent/service/service-payment/src/main/resources/cert/apiclient_cert.p12 b/guigu-ssyx-parent/service/service-payment/src/main/resources/cert/apiclient_cert.p12 new file mode 100644 index 0000000..3413e3b Binary files /dev/null and b/guigu-ssyx-parent/service/service-payment/src/main/resources/cert/apiclient_cert.p12 differ diff --git a/guigu-ssyx-parent/service/service-product/src/main/java/com/atguigu/ssyx/product/mapper/SkuInfoMapper.java b/guigu-ssyx-parent/service/service-product/src/main/java/com/atguigu/ssyx/product/mapper/SkuInfoMapper.java index ad68b9c..4064870 100644 --- a/guigu-ssyx-parent/service/service-product/src/main/java/com/atguigu/ssyx/product/mapper/SkuInfoMapper.java +++ b/guigu-ssyx-parent/service/service-product/src/main/java/com/atguigu/ssyx/product/mapper/SkuInfoMapper.java @@ -39,4 +39,12 @@ public interface SkuInfoMapper extends BaseMapper { * @param skuNum */ void unlockStock(@Param("skuId") Long skuId, @Param("skuNum") Integer skuNum); + + /** + * 减库存 + * + * @param skuId + * @param skuNum + */ + void minusStock(@Param("skuId") Long skuId, @Param("skuNum") Integer skuNum); } diff --git a/guigu-ssyx-parent/service/service-product/src/main/java/com/atguigu/ssyx/product/receiver/StockReceiver.java b/guigu-ssyx-parent/service/service-product/src/main/java/com/atguigu/ssyx/product/receiver/StockReceiver.java new file mode 100644 index 0000000..b6cd405 --- /dev/null +++ b/guigu-ssyx-parent/service/service-product/src/main/java/com/atguigu/ssyx/product/receiver/StockReceiver.java @@ -0,0 +1,43 @@ +package com.atguigu.ssyx.product.receiver; + +import com.atguigu.ssyx.mq.constant.MqConst; +import com.atguigu.ssyx.product.service.SkuInfoService; +import com.rabbitmq.client.Channel; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.rabbit.annotation.Exchange; +import org.springframework.amqp.rabbit.annotation.Queue; +import org.springframework.amqp.rabbit.annotation.QueueBinding; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import java.io.IOException; + +/** + * ClassName: StockReceiver + * Package: com.atguigu.ssyx.product.receiver + * + * @author yovinchen + * @Create 2023/10/13 15:05 + */ +@Component +public class StockReceiver { + + @Autowired + private SkuInfoService skuInfoService; + + /** + * 扣减库存成功,更新订单状态 + * + * @param orderNo + * @throws IOException + */ + @RabbitListener(bindings = @QueueBinding(value = @Queue(value = MqConst.QUEUE_MINUS_STOCK, durable = "true"), exchange = @Exchange(value = MqConst.EXCHANGE_ORDER_DIRECT), key = {MqConst.ROUTING_MINUS_STOCK})) + public void minusStock(String orderNo, Message message, Channel channel) throws IOException { + if (!StringUtils.isEmpty(orderNo)) { + skuInfoService.minusStock(orderNo); + } + channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); + } +} diff --git a/guigu-ssyx-parent/service/service-product/src/main/java/com/atguigu/ssyx/product/service/SkuInfoService.java b/guigu-ssyx-parent/service/service-product/src/main/java/com/atguigu/ssyx/product/service/SkuInfoService.java index 2758a49..3597e46 100644 --- a/guigu-ssyx-parent/service/service-product/src/main/java/com/atguigu/ssyx/product/service/SkuInfoService.java +++ b/guigu-ssyx-parent/service/service-product/src/main/java/com/atguigu/ssyx/product/service/SkuInfoService.java @@ -115,4 +115,11 @@ public interface SkuInfoService extends IService { * @return */ Boolean checkAndLock(List skuStockLockVoList, String orderNo); + + /** + * 扣减库存成功,更新订单状态 + * + * @param orderNo + */ + void minusStock(String orderNo); } diff --git a/guigu-ssyx-parent/service/service-product/src/main/java/com/atguigu/ssyx/product/service/impl/SkuInfoServiceImpl.java b/guigu-ssyx-parent/service/service-product/src/main/java/com/atguigu/ssyx/product/service/impl/SkuInfoServiceImpl.java index 7d01ca2..db3a6fe 100644 --- a/guigu-ssyx-parent/service/service-product/src/main/java/com/atguigu/ssyx/product/service/impl/SkuInfoServiceImpl.java +++ b/guigu-ssyx-parent/service/service-product/src/main/java/com/atguigu/ssyx/product/service/impl/SkuInfoServiceImpl.java @@ -312,9 +312,7 @@ public class SkuInfoServiceImpl extends ServiceImpl impl //获取第一页数据,每页显示三条记录 Page pageParam = new Page<>(1, 3); //调用方法查询 - IPage skuInfoPage = baseMapper.selectPage(pageParam, new LambdaQueryWrapper().eq(SkuInfo::getIsNewPerson, 1) - .eq(SkuInfo::getPublishStatus, 1) - .orderByDesc(SkuInfo::getStock)); + IPage skuInfoPage = baseMapper.selectPage(pageParam, new LambdaQueryWrapper().eq(SkuInfo::getIsNewPerson, 1).eq(SkuInfo::getPublishStatus, 1).orderByDesc(SkuInfo::getStock)); return skuInfoPage.getRecords(); } @@ -333,31 +331,47 @@ public class SkuInfoServiceImpl extends ServiceImpl impl } //2 遍历skuStockLockVoList得到每个商品,验证库存并锁定库存,具备原子性 - skuStockLockVoList.stream() - .forEach(skuStockLockVo -> { - this.checkLock(skuStockLockVo); - }); + skuStockLockVoList.stream().forEach(skuStockLockVo -> { + this.checkLock(skuStockLockVo); + }); //3 只要有一个商品锁定失败,所有锁定成功的商品都解锁 - boolean flag = skuStockLockVoList.stream() - .anyMatch(skuStockLockVo -> !skuStockLockVo.getIsLock()); + boolean flag = skuStockLockVoList.stream().anyMatch(skuStockLockVo -> !skuStockLockVo.getIsLock()); if (flag) { //所有锁定成功的商品都解锁 - skuStockLockVoList.stream() - .filter(SkuStockLockVo::getIsLock) - .forEach(skuStockLockVo -> { - baseMapper.unlockStock(skuStockLockVo.getSkuId(), skuStockLockVo.getSkuNum()); - }); + skuStockLockVoList.stream().filter(SkuStockLockVo::getIsLock).forEach(skuStockLockVo -> { + baseMapper.unlockStock(skuStockLockVo.getSkuId(), skuStockLockVo.getSkuNum()); + }); //返回失败的状态 return false; } //4 如果所有商品都锁定成功了,redis缓存相关数据,为了方便后面解锁和减库存 - redisTemplate.opsForValue() - .set(RedisConst.SROCK_INFO + orderNo, skuStockLockVoList); + redisTemplate.opsForValue().set(RedisConst.SROCK_INFO + orderNo, skuStockLockVoList); return true; } + /** + * 扣减库存成功,更新订单状态 + * + * @param orderNo + */ + @Override + public void minusStock(String orderNo) { + //从redis获取锁定库存信息 + List skuStockLockVoList = (List) redisTemplate.opsForValue().get(RedisConst.SROCK_INFO + orderNo); + if (CollectionUtils.isEmpty(skuStockLockVoList)) { + return; + } + //遍历集合,得到每个对象,减库存 + skuStockLockVoList.forEach(skuStockLockVo -> { + baseMapper.minusStock(skuStockLockVo.getSkuId(), skuStockLockVo.getSkuNum()); + }); + + //删除redis数据 + redisTemplate.delete(RedisConst.SROCK_INFO + orderNo); + } + //2 遍历skuStockLockVoList得到每个商品,验证库存并锁定库存,具备原子性 private void checkLock(SkuStockLockVo skuStockLockVo) { //获取锁