From d6f55e9bf15db55cc82114e893736eb4134a071f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=8B?= <2536636506@qq.com> Date: Mon, 6 May 2024 19:28:23 +0800 Subject: [PATCH] =?UTF-8?q?=E7=A7=92=E6=9D=80=E5=89=A9=E4=BD=99=E9=83=A8?= =?UTF-8?q?=E5=88=86+=E6=94=AF=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/domin/pojo/KillOrderEntity.java | 7 ++ .../group/common/domin/pojo/PayDetail.java | 29 +++--- group-module/group_goods/pom.xml | 5 + .../group/goods/config/RabbitAdminConfig.java | 49 ++++++++++ .../group/goods/config/RabbitMQConfig.java | 86 +++++++++++++++++ .../group/goods/config/RedissonConfig.java | 31 +++++++ .../goods/controller/SecKillController.java | 9 +- .../group/goods/feign/KillOrderService.java | 12 ++- .../java/com/group/goods/mq/Consumer.java | 93 +++++++++++++++++++ .../main/java/com/group/goods/mq/Product.java | 44 +++++++++ .../java/com/group/goods/pojo/ReqKill.java | 14 +++ .../group/goods/service/SecKillService.java | 1 + .../service/impl/SecKillServiceImpl.java | 55 ++++++----- group-module/group_order/pom.xml | 13 +++ .../com/group/order/config/AliPayConfig.java | 40 ++++++++ .../com/group/order/config/CorsConfig.java | 25 +++++ .../order/controller/KillOrderController.java | 87 ++++++++++++++--- .../com/group/order/payMethod/AliPay.java | 49 +++++++++- .../com/group/order/payMethod/CardPay.java | 4 +- .../com/group/order/payMethod/PayContext.java | 1 + .../com/group/order/payMethod/PayType.java | 4 +- .../com/group/order/payMethod/WeChat.java | 4 +- .../group/order/service/KillOrderService.java | 4 +- .../group/order/service/PayDetailService.java | 10 ++ .../service/impl/KillOrderServiceImpl.java | 50 ++++++---- .../service/impl/PayDetailServiceImpl.java | 16 ++++ 26 files changed, 667 insertions(+), 75 deletions(-) create mode 100644 group-module/group_goods/src/main/java/com/group/goods/config/RabbitAdminConfig.java create mode 100644 group-module/group_goods/src/main/java/com/group/goods/config/RabbitMQConfig.java create mode 100644 group-module/group_goods/src/main/java/com/group/goods/config/RedissonConfig.java create mode 100644 group-module/group_goods/src/main/java/com/group/goods/mq/Consumer.java create mode 100644 group-module/group_goods/src/main/java/com/group/goods/mq/Product.java create mode 100644 group-module/group_goods/src/main/java/com/group/goods/pojo/ReqKill.java create mode 100644 group-module/group_order/src/main/java/com/group/order/config/AliPayConfig.java create mode 100644 group-module/group_order/src/main/java/com/group/order/config/CorsConfig.java create mode 100644 group-module/group_order/src/main/java/com/group/order/service/PayDetailService.java create mode 100644 group-module/group_order/src/main/java/com/group/order/service/impl/PayDetailServiceImpl.java diff --git a/group-common/src/main/java/com/group/common/domin/pojo/KillOrderEntity.java b/group-common/src/main/java/com/group/common/domin/pojo/KillOrderEntity.java index a4f5d77..3695ff6 100644 --- a/group-common/src/main/java/com/group/common/domin/pojo/KillOrderEntity.java +++ b/group-common/src/main/java/com/group/common/domin/pojo/KillOrderEntity.java @@ -3,14 +3,17 @@ package com.group.common.domin.pojo; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Builder; import lombok.Data; +import java.math.BigDecimal; import java.util.Date; /** * 秒杀订单表 */ @Data +@Builder @TableName("t_kill_order") public class KillOrderEntity { /** @@ -38,5 +41,9 @@ public class KillOrderEntity { * 秒杀订单创建时间 */ private Date orderCreate ; + /** + * 秒杀商品价格 + */ + private BigDecimal killPrice; } diff --git a/group-common/src/main/java/com/group/common/domin/pojo/PayDetail.java b/group-common/src/main/java/com/group/common/domin/pojo/PayDetail.java index b19ea0f..660b6d3 100644 --- a/group-common/src/main/java/com/group/common/domin/pojo/PayDetail.java +++ b/group-common/src/main/java/com/group/common/domin/pojo/PayDetail.java @@ -1,5 +1,6 @@ package com.group.common.domin.pojo; +import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; @@ -17,38 +18,44 @@ import java.util.Date; @Builder public class PayDetail { /** - * + * 订单详情主键 */ - @TableId - private Integer payId ; + @TableId(type = IdType.AUTO) + private Integer payId; /** * 订单支付金额 */ - @TableField("pay_title") - private BigDecimal payTitle ; + @TableField("pay_price") + private BigDecimal payPrice; /** * 订单id */ @TableField("pay_order") - private Integer payOrder ; + private Integer payOrder; /** * 支付宝编号 */ @TableField("pay_pay") - private String payPay ; + private String payPay; /** - * 明细状态 1-待支付 2-已支付 3-支付失败 + * 明细状态 1-待支付 2-支付成功 3-支付失败 */ @TableField("pay_state") - private Integer payState ; + private Integer payState; /** * 创建时间 */ @TableField("pay_create") - private Date payCreate ; + private Date payCreate; /** * 支付时间 */ @TableField("pay_payed") - private Date payPayed ; + private String payPayed; + /** + * 订单支付流水编号 + */ + @TableField("pay_detail") + private String payDetail; + } diff --git a/group-module/group_goods/pom.xml b/group-module/group_goods/pom.xml index df181ae..1e1bb3e 100644 --- a/group-module/group_goods/pom.xml +++ b/group-module/group_goods/pom.xml @@ -23,5 +23,10 @@ com.song group-common + + + org.springframework.boot + spring-boot-starter-amqp + \ No newline at end of file diff --git a/group-module/group_goods/src/main/java/com/group/goods/config/RabbitAdminConfig.java b/group-module/group_goods/src/main/java/com/group/goods/config/RabbitAdminConfig.java new file mode 100644 index 0000000..4eae44c --- /dev/null +++ b/group-module/group_goods/src/main/java/com/group/goods/config/RabbitAdminConfig.java @@ -0,0 +1,49 @@ +package com.group.goods.config; + +import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.amqp.rabbit.core.RabbitAdmin; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class RabbitAdminConfig { + + @Value("${spring.rabbitmq.host}") + private String host; + @Value("${spring.rabbitmq.username}") + private String username; + @Value("${spring.rabbitmq.password}") + private String password; + @Value("${spring.rabbitmq.virtualhost}") + private String virtualhost; + + /** + * 构建RabbitMQ的连接工厂 + * @return + */ + @Bean + public ConnectionFactory connectionFactory() { + CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); + connectionFactory.setAddresses(host); + connectionFactory.setUsername(username); + connectionFactory.setPassword(password); + connectionFactory.setVirtualHost(virtualhost); + return connectionFactory; + } + + + /** + * 初始化RabbitAdmin + * @param connectionFactory + * @return + */ + @Bean + public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) { + RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory); + rabbitAdmin.setAutoStartup(true); + return rabbitAdmin; + } + +} diff --git a/group-module/group_goods/src/main/java/com/group/goods/config/RabbitMQConfig.java b/group-module/group_goods/src/main/java/com/group/goods/config/RabbitMQConfig.java new file mode 100644 index 0000000..9e36708 --- /dev/null +++ b/group-module/group_goods/src/main/java/com/group/goods/config/RabbitMQConfig.java @@ -0,0 +1,86 @@ +package com.group.goods.config; + + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.amqp.core.*; +import org.springframework.amqp.rabbit.connection.CorrelationData; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; +import org.springframework.amqp.support.converter.MessageConverter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; + +@Configuration +public class RabbitMQConfig implements RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnsCallback{ + + public static final String QUEUE = "order_queue"; + public static final String EXCHANGE = "order_exchange"; + public static final String ROUTINGKEY = "order_routing_key"; + + + + public static final Logger logger = LoggerFactory.getLogger(RabbitMQConfig.class); + + + @Autowired + private RabbitTemplate rabbitTemplate; + + + //创建消息转换器 + @Bean + public MessageConverter messageConverter(){ + return new Jackson2JsonMessageConverter(); + } + + + + @Bean + public Queue queue(){ + return new Queue(QUEUE,true); + } + + @Bean + public DirectExchange directExchange(){ + + return new DirectExchange(EXCHANGE); + + } + + @Bean + public Binding binding(){ + return BindingBuilder.bind(queue()).to(directExchange()).with(ROUTINGKEY); + } + + + @PostConstruct + public void rabbitTemplate(){ + + rabbitTemplate.setConfirmCallback(this); + rabbitTemplate.setReturnsCallback(this); + } + + + + + @Override + public void confirm(CorrelationData correlationData, boolean ack, String s) { + + if (ack){ + logger.info("{}消息到达交换机",correlationData.getId()); + }else { + logger.info("{}消息丢失", correlationData.getId()); + } + + } + + @Override + public void returnedMessage(ReturnedMessage returnedMessage) { + + logger.error("{}消息未到达队列",returnedMessage.getMessage().getMessageProperties().getMessageId()); + + } +} diff --git a/group-module/group_goods/src/main/java/com/group/goods/config/RedissonConfig.java b/group-module/group_goods/src/main/java/com/group/goods/config/RedissonConfig.java new file mode 100644 index 0000000..c9bfa4a --- /dev/null +++ b/group-module/group_goods/src/main/java/com/group/goods/config/RedissonConfig.java @@ -0,0 +1,31 @@ +package com.group.goods.config; + +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.codec.JsonJacksonCodec; +import org.redisson.config.Config; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; + +/** + * redisson配置 + */ +@Component +public class RedissonConfig { + + @Value("${spring.redis.host}") + private String host; + + @Value("${spring.redis.port}") + private String post; + + @Bean + public RedissonClient redissonClient(){ + Config config = new Config(); + config.useSingleServer().setAddress(host+post); + config.setCodec(new JsonJacksonCodec()); + return Redisson.create(config); + } +} diff --git a/group-module/group_goods/src/main/java/com/group/goods/controller/SecKillController.java b/group-module/group_goods/src/main/java/com/group/goods/controller/SecKillController.java index 1e490e9..82de701 100644 --- a/group-module/group_goods/src/main/java/com/group/goods/controller/SecKillController.java +++ b/group-module/group_goods/src/main/java/com/group/goods/controller/SecKillController.java @@ -19,18 +19,11 @@ public class SecKillController { private SecKillService secKillService; /** - * 秒杀商品 - 采用最简单方式进行实现 + * 秒杀商品 - 采用最简单方式进行实现 -使用了redisson框架 */ @GetMapping("/buyGoodsByOne/{id}") public R buyGoodsByOne(@PathVariable("id")Integer id){ return secKillService.buyGoodsByOne(id); } - /** - * 秒杀方式 -- 采用降低锁粒度的方式实现 - */ - - /** - * 秒杀商品 -- 采用redisson信号量的方式进行解决这个问题 - */ } diff --git a/group-module/group_goods/src/main/java/com/group/goods/feign/KillOrderService.java b/group-module/group_goods/src/main/java/com/group/goods/feign/KillOrderService.java index 2a859e1..7c5a866 100644 --- a/group-module/group_goods/src/main/java/com/group/goods/feign/KillOrderService.java +++ b/group-module/group_goods/src/main/java/com/group/goods/feign/KillOrderService.java @@ -1,7 +1,11 @@ package com.group.goods.feign; +import com.group.common.domin.pojo.KillOrderEntity; +import com.group.common.result.R; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; /** @@ -11,5 +15,11 @@ import org.springframework.web.bind.annotation.RequestParam; public interface KillOrderService { @GetMapping("/killOrder/exitResult") - public boolean exitResult(@RequestParam Integer goodsId, Integer userId); + public boolean exitResult(@RequestParam Integer goodsId,@RequestParam Integer userId); + + @PostMapping("/killOrder/addOrder") + public R addOrder(@RequestBody KillOrderEntity killOrderEntity); + + @PostMapping ("/deleteOrder") + public void deleteOrder(@RequestBody KillOrderEntity killOrderEntity); } diff --git a/group-module/group_goods/src/main/java/com/group/goods/mq/Consumer.java b/group-module/group_goods/src/main/java/com/group/goods/mq/Consumer.java new file mode 100644 index 0000000..6250969 --- /dev/null +++ b/group-module/group_goods/src/main/java/com/group/goods/mq/Consumer.java @@ -0,0 +1,93 @@ +package com.group.goods.mq; + +import cn.hutool.core.util.IdUtil; +import com.alibaba.fastjson.JSON; +import com.group.common.domin.pojo.KillOrderEntity; +import com.group.common.domin.pojo.SeckillEntity; +import com.group.common.redis.RedisCache; +import com.group.goods.config.RabbitMQConfig; +import com.group.goods.feign.KillOrderService; +import com.group.goods.pojo.ReqKill; +import com.group.goods.service.SecKillService; +import com.rabbitmq.client.Channel; +import lombok.extern.log4j.Log4j2; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.core.MessageProperties; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.Date; +import java.util.concurrent.TimeUnit; + +/** + * 消费者 + */ +@Log4j2 +@Component +public class Consumer { + + @Autowired + private RedisCache redisCache; + + @Autowired + private RabbitTemplate rabbitTemplate; + + @Autowired + private SecKillService secKillService; + + @Autowired + private KillOrderService killOrderService; + + + @RabbitListener(queues = RabbitMQConfig.QUEUE) + public void addOrder(Message message, Channel channel) { + MessageProperties properties = message.getMessageProperties(); + String messageId = properties.getMessageId(); + //判断消息是否重复消费 + if (redisCache.hasKey(messageId)) { + //当这个messageId存在时,证明已经消费过 + return; + } + //拿到传送来的消息 + String o = (String) rabbitTemplate.getMessageConverter().fromMessage(message); + ReqKill reqKill = JSON.parseObject(o, ReqKill.class); + //执行业务 + //通过商品id获取对应的实体类 + SeckillEntity entity = secKillService.getById(reqKill.getGoodsId()); + //生成秒杀订单编号 + String killId = IdUtil.getSnowflakeNextId()+""; + //生成订单实体类 + //生成订单 商品id 用户id 当前时间 支付金额 商品编号 + KillOrderEntity order = KillOrderEntity + .builder() + .goodsId(reqKill.getGoodsId()) + .userId(reqKill.getUserId()) + .killPrice(entity.getSeckillPrice()) + .killId(killId) + .orderCreate(new Date()) + .build(); + killOrderService.addOrder(order); + //将messageId存入redis缓存中 + redisCache.setCacheObject(messageId, messageId, 30L, TimeUnit.MINUTES); + //手动确认消息 + try { + channel.basicAck(properties.getDeliveryTag(),false); + } catch (IOException e) { + //当消息消费确认失败 + //删除添加的订单 + killOrderService.deleteOrder(order); + //将消息重新放回队列中 + try { + channel.basicReject(properties.getDeliveryTag(),true); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + throw new RuntimeException(e); + } + } + + +} diff --git a/group-module/group_goods/src/main/java/com/group/goods/mq/Product.java b/group-module/group_goods/src/main/java/com/group/goods/mq/Product.java new file mode 100644 index 0000000..59d32fc --- /dev/null +++ b/group-module/group_goods/src/main/java/com/group/goods/mq/Product.java @@ -0,0 +1,44 @@ +package com.group.goods.mq; + +import cn.hutool.core.util.IdUtil; +import com.alibaba.fastjson.JSON; +import com.group.common.redis.RedisCache; +import com.group.goods.config.RabbitMQConfig; +import com.group.goods.pojo.ReqKill; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.core.MessageProperties; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 生产者 + */ +@Component +public class Product { + + @Autowired + private RabbitTemplate rabbitTemplate; + + + /** + * mq异步生成订单 + */ + public void addOrder(ReqKill reqKill) { + long messageId = IdUtil.getSnowflakeNextId(); + MessageProperties messageProperties = new MessageProperties(); + messageProperties.setMessageId( messageId+""); + //将reqKill放到队列中,等待消费 + Message message = rabbitTemplate + .getMessageConverter() + .toMessage(JSON.toJSONString(reqKill) + , messageProperties); + rabbitTemplate.sendAndReceive( + RabbitMQConfig.EXCHANGE, + RabbitMQConfig.ROUTINGKEY, + message + ); + } + + +} diff --git a/group-module/group_goods/src/main/java/com/group/goods/pojo/ReqKill.java b/group-module/group_goods/src/main/java/com/group/goods/pojo/ReqKill.java new file mode 100644 index 0000000..a8affee --- /dev/null +++ b/group-module/group_goods/src/main/java/com/group/goods/pojo/ReqKill.java @@ -0,0 +1,14 @@ +package com.group.goods.pojo; + +import lombok.Builder; +import lombok.Data; + +/** + * mq请求订单生成类 + */ +@Data +@Builder +public class ReqKill { + private Integer userId; + private Integer goodsId; +} diff --git a/group-module/group_goods/src/main/java/com/group/goods/service/SecKillService.java b/group-module/group_goods/src/main/java/com/group/goods/service/SecKillService.java index 5ea586f..cfbb957 100644 --- a/group-module/group_goods/src/main/java/com/group/goods/service/SecKillService.java +++ b/group-module/group_goods/src/main/java/com/group/goods/service/SecKillService.java @@ -9,4 +9,5 @@ import com.group.common.result.R; */ public interface SecKillService extends IService { R buyGoodsByOne(Integer id); + } diff --git a/group-module/group_goods/src/main/java/com/group/goods/service/impl/SecKillServiceImpl.java b/group-module/group_goods/src/main/java/com/group/goods/service/impl/SecKillServiceImpl.java index 0efcb38..4596f8b 100644 --- a/group-module/group_goods/src/main/java/com/group/goods/service/impl/SecKillServiceImpl.java +++ b/group-module/group_goods/src/main/java/com/group/goods/service/impl/SecKillServiceImpl.java @@ -1,7 +1,6 @@ package com.group.goods.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.group.common.domin.pojo.KillOrderEntity; import com.group.common.domin.pojo.SessionEntity; import com.group.common.redis.RedisCache; import com.group.common.result.R; @@ -9,10 +8,12 @@ import com.group.common.util.UserUtil; import com.group.goods.feign.KillOrderService; import com.group.goods.mapper.SecKillMapper; import com.group.common.domin.pojo.SeckillEntity; +import com.group.goods.mq.Product; +import com.group.goods.pojo.ReqKill; import com.group.goods.service.SecKillService; import com.group.goods.service.SessionService; -import org.redisson.Redisson; import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import javax.servlet.http.HttpServletRequest; @@ -43,7 +44,10 @@ public class SecKillServiceImpl extends ServiceImplcom.song group-common + + + + com.alipay.sdk + alipay-sdk-java + 4.35.79.ALL + + + + com.alipay.sdk + alipay-easysdk + 2.2.0 + \ No newline at end of file diff --git a/group-module/group_order/src/main/java/com/group/order/config/AliPayConfig.java b/group-module/group_order/src/main/java/com/group/order/config/AliPayConfig.java new file mode 100644 index 0000000..234fe46 --- /dev/null +++ b/group-module/group_order/src/main/java/com/group/order/config/AliPayConfig.java @@ -0,0 +1,40 @@ +package com.group.order.config; + +import com.alipay.api.AlipayClient; +import com.alipay.api.DefaultAlipayClient; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; + +@Component +@Data +@ConfigurationProperties(prefix = "alipay") +public class AliPayConfig { + + // 支付宝的AppId + private String appId; + // 应用私钥 + private String appPrivateKey; + // 支付宝公钥 + private String alipayPublicKey; + // 支付宝通知本地的接口完整地址 + private String notifyUrl; + //支付宝跳转的地址 + private String gatewayUrl ; + private String format; + private String charset; + //签名方式 + private String signType; + //回调本地地址 + private String returnUrl; + + private String productCode; + + @Bean + public AlipayClient alipayClient(){ + return new DefaultAlipayClient(gatewayUrl,appId,appPrivateKey,format,charset,alipayPublicKey,signType); + } + + +} diff --git a/group-module/group_order/src/main/java/com/group/order/config/CorsConfig.java b/group-module/group_order/src/main/java/com/group/order/config/CorsConfig.java new file mode 100644 index 0000000..e768f4c --- /dev/null +++ b/group-module/group_order/src/main/java/com/group/order/config/CorsConfig.java @@ -0,0 +1,25 @@ +package com.group.order.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; + +/** + * 跨域配置 + */ +@Configuration +public class CorsConfig { + + @Bean + public CorsFilter corsFilter() { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + CorsConfiguration corsConfiguration = new CorsConfiguration(); + corsConfiguration.addAllowedOrigin("*"); // 1 设置访问源地址 + corsConfiguration.addAllowedHeader("*"); // 2 设置访问源请求头 + corsConfiguration.addAllowedMethod("*"); // 3 设置访问源请求方法 + source.registerCorsConfiguration("/**", corsConfiguration); // 4 对接口配置跨域设置 + return new CorsFilter(source); + } +} \ No newline at end of file diff --git a/group-module/group_order/src/main/java/com/group/order/controller/KillOrderController.java b/group-module/group_order/src/main/java/com/group/order/controller/KillOrderController.java index a21c464..0e9caff 100644 --- a/group-module/group_order/src/main/java/com/group/order/controller/KillOrderController.java +++ b/group-module/group_order/src/main/java/com/group/order/controller/KillOrderController.java @@ -1,16 +1,22 @@ package com.group.order.controller; +import com.alipay.api.internal.util.AlipaySignature; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.group.common.domin.pojo.KillOrderEntity; import com.group.common.domin.pojo.PayDetail; import com.group.common.result.R; -import com.group.order.payMethod.PayContext; -import com.group.order.payMethod.PayType; +import com.group.order.config.AliPayConfig; import com.group.order.pojo.ReqPay; import com.group.order.service.KillOrderService; +import com.group.order.service.PayDetailService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.HashMap; +import java.util.Map; + /** * 秒杀订单控制层 */ @@ -27,33 +33,90 @@ public class KillOrderController { * 如果为true,证明该用户已经购买过该商品 */ @GetMapping("/exitResult") - public boolean exitResult(@RequestParam Integer goodsId,Integer userId){ + public boolean exitResult(@RequestParam Integer goodsId, @RequestParam Integer userId) { KillOrderEntity one = killOrderService.getOne(new LambdaQueryWrapper() .eq(KillOrderEntity::getGoodsId, goodsId) .eq(KillOrderEntity::getUserId, userId)); return one != null; } + /** + * 生成秒杀订单 + */ + @PostMapping("/addOrder") + public R addOrder(@RequestBody KillOrderEntity killOrderEntity) { + if (killOrderService.save(killOrderEntity)) { + return R.error(); + } + return R.ok(); + } + + /** + * 删除生成的订单 + */ + @PostMapping("/deleteOrder") + public void deleteOrder(@RequestBody KillOrderEntity killOrderEntity) { + killOrderService.removeById(killOrderEntity); + } /** * 支付秒杀订单 */ @PostMapping("/payKill") - public R payKill(@RequestBody ReqPay pay){ - return killOrderService.payKill(pay); + public R payKill(@RequestBody ReqPay pay, HttpServletResponse response) { + return killOrderService.payKill(pay, response); } @Autowired - private PayContext payContext; + private AliPayConfig aliPayConfig; + + @Autowired + private PayDetailService payDetailService; /** - * 测试支付模式 + * 支付宝回调 */ - @GetMapping("/test") - public R test(@RequestParam Integer type){ -// PayType pay = payContext.getPay(type); -// pay.pay(PayDetail.builder().build()); - return R.ok("支付成功"); + @PostMapping("/notify") // 注意这里必须是POST接口 + public void payNotify(HttpServletRequest request) throws Exception { + System.out.println("支付宝触发回调"); + if (request.getParameter("trade_status").equals("TRADE_SUCCESS")) { + System.out.println("=========支付宝异步回调========"); + Map params = new HashMap<>(); + Map requestParams = request.getParameterMap(); + for (String name : requestParams.keySet()) { + params.put(name, request.getParameter(name)); + } + String sign = params.get("sign"); + String content = AlipaySignature.getSignCheckContentV1(params); + boolean checkSignature = AlipaySignature.rsa256CheckContent(content, sign, aliPayConfig.getAlipayPublicKey(), "UTF-8"); // 验证签名 + // 支付宝验签 + if (checkSignature) { + // 验签通过 + System.out.println("交易名称: " + params.get("subject")); + System.out.println("交易状态: " + params.get("trade_status")); + System.out.println("支付宝交易凭证号: " + params.get("trade_no")); + System.out.println("商户订单号: " + params.get("out_trade_no")); + System.out.println("交易金额: " + params.get("total_amount")); + System.out.println("买家在支付宝唯一id: " + params.get("buyer_id")); + System.out.println("买家付款时间: " + params.get("gmt_payment")); + System.out.println("买家付款金额: " + params.get("buyer_pay_amount")); + String tradeNo = params.get("out_trade_no"); // 订单编号 + String gmtPayment = params.get("gmt_payment"); // 支付时间 + String alipayTradeNo = params.get("trade_no"); // 支付宝交易编号 + // 更新订单状态为已支付,设置支付信息 + PayDetail pay = payDetailService + .getOne(new LambdaQueryWrapper() + .eq(PayDetail::getPayDetail, tradeNo)); + pay.setPayPayed(gmtPayment); + pay.setPayState(2); + pay.setPayPay(alipayTradeNo); + payDetailService.updateById(pay); + } + } } + + + + } diff --git a/group-module/group_order/src/main/java/com/group/order/payMethod/AliPay.java b/group-module/group_order/src/main/java/com/group/order/payMethod/AliPay.java index 3f4dbbc..bd86d2c 100644 --- a/group-module/group_order/src/main/java/com/group/order/payMethod/AliPay.java +++ b/group-module/group_order/src/main/java/com/group/order/payMethod/AliPay.java @@ -1,15 +1,49 @@ package com.group.order.payMethod; +import cn.hutool.json.JSONObject; +import com.alipay.api.AlipayApiException; +import com.alipay.api.AlipayClient; +import com.alipay.api.request.AlipayTradePagePayRequest; import com.group.common.domin.pojo.PayDetail; +import com.group.order.config.AliPayConfig; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + /** * 支付宝支付 */ @Component public class AliPay implements PayType { + + @Autowired + private AlipayClient alipayClient; + + @Autowired + private AliPayConfig aliPayConfig; + + @Override - public void pay(PayDetail payDetail) { + public void pay(PayDetail payDetail, HttpServletResponse response) { + AlipayTradePagePayRequest request = getAlipayTradePagePayRequest(payDetail); + // 执行请求,拿到响应的结果,返回给浏览器 + String form = ""; + try { + form = alipayClient.pageExecute(request).getBody(); // 调用SDK生成表单 + } catch (AlipayApiException e) { + e.printStackTrace(); + } + response.setContentType("text/html;charset=" + aliPayConfig.getCharset()); + try { + response.getWriter().write(form);// 直接将完整的表单html输出到页面 + response.getWriter().flush(); + response.getWriter().close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + System.out.println("支付宝支付"); } @@ -17,4 +51,17 @@ public class AliPay implements PayType { public String getName() { return PayEnum.BYALI.getName(); } + + private AlipayTradePagePayRequest getAlipayTradePagePayRequest(PayDetail payDetail) { + AlipayTradePagePayRequest request = new AlipayTradePagePayRequest(); + request.setNotifyUrl(aliPayConfig.getNotifyUrl()); + JSONObject bizContent = new JSONObject(); + bizContent.set("out_trade_no", payDetail.getPayDetail()); // 订单详情的编号 + bizContent.set("total_amount",payDetail.getPayPrice()); // 订单的总金额 + bizContent.set("subject", payDetail.getPayOrder()); // 支付的名称 + bizContent.set("product_code", aliPayConfig.getProductCode()); + request.setBizContent(bizContent.toString()); + request.setReturnUrl(aliPayConfig.getReturnUrl());// 支付完成后自动跳转到本地页面的路径 + return request; + } } diff --git a/group-module/group_order/src/main/java/com/group/order/payMethod/CardPay.java b/group-module/group_order/src/main/java/com/group/order/payMethod/CardPay.java index 4d931ab..6967910 100644 --- a/group-module/group_order/src/main/java/com/group/order/payMethod/CardPay.java +++ b/group-module/group_order/src/main/java/com/group/order/payMethod/CardPay.java @@ -3,13 +3,15 @@ package com.group.order.payMethod; import com.group.common.domin.pojo.PayDetail; import org.springframework.stereotype.Component; +import javax.servlet.http.HttpServletResponse; + /** * 银行卡支付 */ @Component public class CardPay implements PayType{ @Override - public void pay(PayDetail payDetail) { + public void pay(PayDetail payDetail, HttpServletResponse response) { System.out.println("银行卡支付"); } diff --git a/group-module/group_order/src/main/java/com/group/order/payMethod/PayContext.java b/group-module/group_order/src/main/java/com/group/order/payMethod/PayContext.java index 40932a2..8d33f94 100644 --- a/group-module/group_order/src/main/java/com/group/order/payMethod/PayContext.java +++ b/group-module/group_order/src/main/java/com/group/order/payMethod/PayContext.java @@ -35,6 +35,7 @@ public class PayContext implements InitializingBean, ApplicationContextAware { */ public PayType getPay(Integer type){ String payType=null; + if (type==1){ payType=PayEnum.BYALI.getName(); } diff --git a/group-module/group_order/src/main/java/com/group/order/payMethod/PayType.java b/group-module/group_order/src/main/java/com/group/order/payMethod/PayType.java index 8a9fe09..5fa942e 100644 --- a/group-module/group_order/src/main/java/com/group/order/payMethod/PayType.java +++ b/group-module/group_order/src/main/java/com/group/order/payMethod/PayType.java @@ -3,12 +3,14 @@ package com.group.order.payMethod; import com.group.common.domin.pojo.PayDetail; import com.group.common.result.R; +import javax.servlet.http.HttpServletResponse; + /** * 抽象接口 */ public interface PayType { - public void pay(PayDetail payDetail); + public void pay(PayDetail payDetail,HttpServletResponse response); public String getName(); } diff --git a/group-module/group_order/src/main/java/com/group/order/payMethod/WeChat.java b/group-module/group_order/src/main/java/com/group/order/payMethod/WeChat.java index 6cf39c5..d31e2e4 100644 --- a/group-module/group_order/src/main/java/com/group/order/payMethod/WeChat.java +++ b/group-module/group_order/src/main/java/com/group/order/payMethod/WeChat.java @@ -3,13 +3,15 @@ package com.group.order.payMethod; import com.group.common.domin.pojo.PayDetail; import org.springframework.stereotype.Component; +import javax.servlet.http.HttpServletResponse; + /** *微信支付 */ @Component public class WeChat implements PayType{ @Override - public void pay(PayDetail payDetail) { + public void pay(PayDetail payDetail, HttpServletResponse response) { System.out.println("微信支付"); } diff --git a/group-module/group_order/src/main/java/com/group/order/service/KillOrderService.java b/group-module/group_order/src/main/java/com/group/order/service/KillOrderService.java index 4a6c103..3952b5a 100644 --- a/group-module/group_order/src/main/java/com/group/order/service/KillOrderService.java +++ b/group-module/group_order/src/main/java/com/group/order/service/KillOrderService.java @@ -5,9 +5,11 @@ import com.group.common.domin.pojo.KillOrderEntity; import com.group.common.result.R; import com.group.order.pojo.ReqPay; +import javax.servlet.http.HttpServletResponse; + /** * 秒杀订单服务层 */ public interface KillOrderService extends IService { - R payKill(ReqPay pay); + R payKill(ReqPay pay, HttpServletResponse response); } diff --git a/group-module/group_order/src/main/java/com/group/order/service/PayDetailService.java b/group-module/group_order/src/main/java/com/group/order/service/PayDetailService.java new file mode 100644 index 0000000..0b87d59 --- /dev/null +++ b/group-module/group_order/src/main/java/com/group/order/service/PayDetailService.java @@ -0,0 +1,10 @@ +package com.group.order.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.group.common.domin.pojo.PayDetail; + +/** + * 订单支付详情业务层 + */ +public interface PayDetailService extends IService { +} diff --git a/group-module/group_order/src/main/java/com/group/order/service/impl/KillOrderServiceImpl.java b/group-module/group_order/src/main/java/com/group/order/service/impl/KillOrderServiceImpl.java index dac40f3..6e6f896 100644 --- a/group-module/group_order/src/main/java/com/group/order/service/impl/KillOrderServiceImpl.java +++ b/group-module/group_order/src/main/java/com/group/order/service/impl/KillOrderServiceImpl.java @@ -12,10 +12,14 @@ import com.group.order.payMethod.PayContext; import com.group.order.payMethod.PayType; import com.group.order.pojo.ReqPay; import com.group.order.service.KillOrderService; +import com.group.order.service.PayDetailService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import javax.servlet.http.HttpServletResponse; import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; /** * 秒杀订单实现层 @@ -24,35 +28,49 @@ import java.util.Date; public class KillOrderServiceImpl extends ServiceImpl implements KillOrderService { - @Autowired - private PayDetailMapper payDetailMapper; @Autowired private PayContext payContext; + @Autowired + private PayDetailService payDetailService; + @Override - public R payKill(ReqPay pay) { + public R payKill(ReqPay pay, HttpServletResponse response) { //安全校验 - //防止幂等性 + //防止幂等性,避免已经成功或者失败的订单重新支付 KillOrderEntity order = getById(pay.getOrderId()); - if (order.getKillState()!=1){ + if (order.getKillState()==2){ return R.error("订单已经支付,请不要重复支付"); } + if (order.getKillState()==3){ + return R.error("订单已经过期,请重新下单"); + } + //判断是否有支付流水正在处理中 + List details = payDetailService + .list() + .stream() + .filter(c -> c.getPayOrder().equals(pay.getOrderId())) + .collect(Collectors.toList()); + if (!details.isEmpty()){ + PayDetail detail = details.get(details.size() - 1); + if (detail.getPayState()==1){ + return R.error("订单正在支付中,请不要重复支付"); + } + if (detail.getPayState()==2){ + return R.error("该订单已经支付成功,请不要重复支付"); + } + } //生成详细支付订单 PayDetail payDetail = PayDetail.builder() - .payPay(IdUtil.getSnowflakeNextIdStr()) + .payDetail(IdUtil.getSnowflakeNextIdStr()) .payOrder(pay.getOrderId()) .payCreate(new Date()) - .payTitle(pay.getPrice()) + .payPrice(pay.getPrice()) .build(); - payDetailMapper.insert(payDetail); - PayType pay1 = payContext.getPay(pay.getType()); - pay1.pay(payDetail); - - - - - - return null; + payDetailService.save(payDetail); + PayType type = payContext.getPay(pay.getType()); + type.pay(payDetail,response); + return R.ok(); } } diff --git a/group-module/group_order/src/main/java/com/group/order/service/impl/PayDetailServiceImpl.java b/group-module/group_order/src/main/java/com/group/order/service/impl/PayDetailServiceImpl.java new file mode 100644 index 0000000..87417e0 --- /dev/null +++ b/group-module/group_order/src/main/java/com/group/order/service/impl/PayDetailServiceImpl.java @@ -0,0 +1,16 @@ +package com.group.order.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.group.common.domin.pojo.PayDetail; +import com.group.order.mapper.PayDetailMapper; +import com.group.order.service.PayDetailService; +import org.springframework.stereotype.Service; + +/** + * 订单支付详情实现层 + */ +@Service +public class PayDetailServiceImpl extends ServiceImpl + implements PayDetailService { + +}