秒杀剩余部分+支付
parent
399e9bb1c6
commit
d6f55e9bf1
|
@ -3,14 +3,17 @@ package com.group.common.domin.pojo;
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 秒杀订单表
|
* 秒杀订单表
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
|
@Builder
|
||||||
@TableName("t_kill_order")
|
@TableName("t_kill_order")
|
||||||
public class KillOrderEntity {
|
public class KillOrderEntity {
|
||||||
/**
|
/**
|
||||||
|
@ -38,5 +41,9 @@ public class KillOrderEntity {
|
||||||
* 秒杀订单创建时间
|
* 秒杀订单创建时间
|
||||||
*/
|
*/
|
||||||
private Date orderCreate ;
|
private Date orderCreate ;
|
||||||
|
/**
|
||||||
|
* 秒杀商品价格
|
||||||
|
*/
|
||||||
|
private BigDecimal killPrice;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package com.group.common.domin.pojo;
|
package com.group.common.domin.pojo;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
@ -17,38 +18,44 @@ import java.util.Date;
|
||||||
@Builder
|
@Builder
|
||||||
public class PayDetail {
|
public class PayDetail {
|
||||||
/**
|
/**
|
||||||
*
|
* 订单详情主键
|
||||||
*/
|
*/
|
||||||
@TableId
|
@TableId(type = IdType.AUTO)
|
||||||
private Integer payId ;
|
private Integer payId;
|
||||||
/**
|
/**
|
||||||
* 订单支付金额
|
* 订单支付金额
|
||||||
*/
|
*/
|
||||||
@TableField("pay_title")
|
@TableField("pay_price")
|
||||||
private BigDecimal payTitle ;
|
private BigDecimal payPrice;
|
||||||
/**
|
/**
|
||||||
* 订单id
|
* 订单id
|
||||||
*/
|
*/
|
||||||
@TableField("pay_order")
|
@TableField("pay_order")
|
||||||
private Integer payOrder ;
|
private Integer payOrder;
|
||||||
/**
|
/**
|
||||||
* 支付宝编号
|
* 支付宝编号
|
||||||
*/
|
*/
|
||||||
@TableField("pay_pay")
|
@TableField("pay_pay")
|
||||||
private String payPay ;
|
private String payPay;
|
||||||
/**
|
/**
|
||||||
* 明细状态 1-待支付 2-已支付 3-支付失败
|
* 明细状态 1-待支付 2-支付成功 3-支付失败
|
||||||
*/
|
*/
|
||||||
@TableField("pay_state")
|
@TableField("pay_state")
|
||||||
private Integer payState ;
|
private Integer payState;
|
||||||
/**
|
/**
|
||||||
* 创建时间
|
* 创建时间
|
||||||
*/
|
*/
|
||||||
@TableField("pay_create")
|
@TableField("pay_create")
|
||||||
private Date payCreate ;
|
private Date payCreate;
|
||||||
/**
|
/**
|
||||||
* 支付时间
|
* 支付时间
|
||||||
*/
|
*/
|
||||||
@TableField("pay_payed")
|
@TableField("pay_payed")
|
||||||
private Date payPayed ;
|
private String payPayed;
|
||||||
|
/**
|
||||||
|
* 订单支付流水编号
|
||||||
|
*/
|
||||||
|
@TableField("pay_detail")
|
||||||
|
private String payDetail;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,5 +23,10 @@
|
||||||
<groupId>com.song</groupId>
|
<groupId>com.song</groupId>
|
||||||
<artifactId>group-common</artifactId>
|
<artifactId>group-common</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-amqp</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,18 +19,11 @@ public class SecKillController {
|
||||||
private SecKillService secKillService;
|
private SecKillService secKillService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 秒杀商品 - 采用最简单方式进行实现
|
* 秒杀商品 - 采用最简单方式进行实现 -使用了redisson框架
|
||||||
*/
|
*/
|
||||||
@GetMapping("/buyGoodsByOne/{id}")
|
@GetMapping("/buyGoodsByOne/{id}")
|
||||||
public R buyGoodsByOne(@PathVariable("id")Integer id){
|
public R buyGoodsByOne(@PathVariable("id")Integer id){
|
||||||
return secKillService.buyGoodsByOne(id);
|
return secKillService.buyGoodsByOne(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 秒杀方式 -- 采用降低锁粒度的方式实现
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 秒杀商品 -- 采用redisson信号量的方式进行解决这个问题
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
package com.group.goods.feign;
|
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.cloud.openfeign.FeignClient;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
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;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -11,5 +15,11 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||||
public interface KillOrderService {
|
public interface KillOrderService {
|
||||||
|
|
||||||
@GetMapping("/killOrder/exitResult")
|
@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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -9,4 +9,5 @@ import com.group.common.result.R;
|
||||||
*/
|
*/
|
||||||
public interface SecKillService extends IService<SeckillEntity> {
|
public interface SecKillService extends IService<SeckillEntity> {
|
||||||
R buyGoodsByOne(Integer id);
|
R buyGoodsByOne(Integer id);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package com.group.goods.service.impl;
|
package com.group.goods.service.impl;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
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.domin.pojo.SessionEntity;
|
||||||
import com.group.common.redis.RedisCache;
|
import com.group.common.redis.RedisCache;
|
||||||
import com.group.common.result.R;
|
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.feign.KillOrderService;
|
||||||
import com.group.goods.mapper.SecKillMapper;
|
import com.group.goods.mapper.SecKillMapper;
|
||||||
import com.group.common.domin.pojo.SeckillEntity;
|
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.SecKillService;
|
||||||
import com.group.goods.service.SessionService;
|
import com.group.goods.service.SessionService;
|
||||||
import org.redisson.Redisson;
|
|
||||||
import org.redisson.api.RLock;
|
import org.redisson.api.RLock;
|
||||||
|
import org.redisson.api.RedissonClient;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
@ -43,7 +44,10 @@ public class SecKillServiceImpl extends ServiceImpl<SecKillMapper, SeckillEntity
|
||||||
private RedisCache redisCache;
|
private RedisCache redisCache;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private Redisson redisson;
|
private RedissonClient redissonClient;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private Product productor;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public R buyGoodsByOne(Integer id) {
|
public R buyGoodsByOne(Integer id) {
|
||||||
|
@ -62,26 +66,33 @@ public class SecKillServiceImpl extends ServiceImpl<SecKillMapper, SeckillEntity
|
||||||
if (session.getSessionEnd().getTime()<time){
|
if (session.getSessionEnd().getTime()<time){
|
||||||
throw new RuntimeException("该场次已经结束");
|
throw new RuntimeException("该场次已经结束");
|
||||||
}
|
}
|
||||||
RLock lock = redisson.getLock("productId:" + id);
|
RLock lock = redissonClient.getLock("productId:" + id);
|
||||||
|
try {
|
||||||
|
lock.tryLock(1,3L,TimeUnit.MINUTES);
|
||||||
|
//校验用户是否购买过
|
||||||
|
//获取用户id
|
||||||
|
Integer userId = UserUtil.getUserId(request);
|
||||||
|
//通过用户id和秒杀商品id,查询该用户是否第一次购买该商品
|
||||||
|
if (killOrderService.exitResult(id,userId)){
|
||||||
|
throw new RuntimeException("用户已购买该商品");
|
||||||
|
}
|
||||||
|
//根据id查询秒杀商品信息,判断库存是否足够
|
||||||
|
//库存信息存储在redis中,通过键"product_id:"+goods_id 来存储,通过键来获取商品库存
|
||||||
|
|
||||||
//校验用户是否购买过
|
Integer stock = redisCache.getCacheObject("product_id:" + id);
|
||||||
//获取用户id
|
if (stock<=0){
|
||||||
Integer userId = UserUtil.getUserId(request);
|
throw new RuntimeException("库存不足,请不要再次点击");
|
||||||
//通过用户id和秒杀商品id,查询该用户是否第一次购买该商品
|
}
|
||||||
if (killOrderService.exitResult(id,userId)){
|
//减少库存,把数据同步redis中
|
||||||
throw new RuntimeException("用户已购买该商品");
|
redisCache.decreaseKey("product_id:"+id);
|
||||||
|
//异步生成订单
|
||||||
|
productor.addOrder(ReqKill.builder().goodsId(id).userId(userId).build());
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
//根据id查询秒杀商品信息,判断库存是否足够
|
return R.ok();
|
||||||
//库存信息存储在redis中,通过键"product_id:"+goods_id 来存储,通过键来获取商品库存
|
|
||||||
|
|
||||||
Integer stock = redisCache.getCacheObject("product_id:" + id);
|
|
||||||
if (stock<=0){
|
|
||||||
throw new RuntimeException("库存不足,请不要再次点击");
|
|
||||||
}
|
|
||||||
//减少库存,把数据同步到数据库和redis中
|
|
||||||
redisCache.decreaseKey("product_id:"+id);
|
|
||||||
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,5 +23,18 @@
|
||||||
<groupId>com.song</groupId>
|
<groupId>com.song</groupId>
|
||||||
<artifactId>group-common</artifactId>
|
<artifactId>group-common</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alipay.sdk</groupId>
|
||||||
|
<artifactId>alipay-sdk-java</artifactId>
|
||||||
|
<version>4.35.79.ALL</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alipay.sdk</groupId>
|
||||||
|
<artifactId>alipay-easysdk</artifactId>
|
||||||
|
<version>2.2.0</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,22 @@
|
||||||
package com.group.order.controller;
|
package com.group.order.controller;
|
||||||
|
|
||||||
|
import com.alipay.api.internal.util.AlipaySignature;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.group.common.domin.pojo.KillOrderEntity;
|
import com.group.common.domin.pojo.KillOrderEntity;
|
||||||
import com.group.common.domin.pojo.PayDetail;
|
import com.group.common.domin.pojo.PayDetail;
|
||||||
import com.group.common.result.R;
|
import com.group.common.result.R;
|
||||||
import com.group.order.payMethod.PayContext;
|
import com.group.order.config.AliPayConfig;
|
||||||
import com.group.order.payMethod.PayType;
|
|
||||||
import com.group.order.pojo.ReqPay;
|
import com.group.order.pojo.ReqPay;
|
||||||
import com.group.order.service.KillOrderService;
|
import com.group.order.service.KillOrderService;
|
||||||
|
import com.group.order.service.PayDetailService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.*;
|
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,证明该用户已经购买过该商品
|
* 如果为true,证明该用户已经购买过该商品
|
||||||
*/
|
*/
|
||||||
@GetMapping("/exitResult")
|
@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<KillOrderEntity>()
|
KillOrderEntity one = killOrderService.getOne(new LambdaQueryWrapper<KillOrderEntity>()
|
||||||
.eq(KillOrderEntity::getGoodsId, goodsId)
|
.eq(KillOrderEntity::getGoodsId, goodsId)
|
||||||
.eq(KillOrderEntity::getUserId, userId));
|
.eq(KillOrderEntity::getUserId, userId));
|
||||||
return one != null;
|
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")
|
@PostMapping("/payKill")
|
||||||
public R payKill(@RequestBody ReqPay pay){
|
public R payKill(@RequestBody ReqPay pay, HttpServletResponse response) {
|
||||||
return killOrderService.payKill(pay);
|
return killOrderService.payKill(pay, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private PayContext payContext;
|
private AliPayConfig aliPayConfig;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PayDetailService payDetailService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 测试支付模式
|
* 支付宝回调
|
||||||
*/
|
*/
|
||||||
@GetMapping("/test")
|
@PostMapping("/notify") // 注意这里必须是POST接口
|
||||||
public R test(@RequestParam Integer type){
|
public void payNotify(HttpServletRequest request) throws Exception {
|
||||||
// PayType pay = payContext.getPay(type);
|
System.out.println("支付宝触发回调");
|
||||||
// pay.pay(PayDetail.builder().build());
|
if (request.getParameter("trade_status").equals("TRADE_SUCCESS")) {
|
||||||
return R.ok("支付成功");
|
System.out.println("=========支付宝异步回调========");
|
||||||
|
Map<String, String> params = new HashMap<>();
|
||||||
|
Map<String, String[]> 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<PayDetail>()
|
||||||
|
.eq(PayDetail::getPayDetail, tradeNo));
|
||||||
|
pay.setPayPayed(gmtPayment);
|
||||||
|
pay.setPayState(2);
|
||||||
|
pay.setPayPay(alipayTradeNo);
|
||||||
|
payDetailService.updateById(pay);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,49 @@
|
||||||
package com.group.order.payMethod;
|
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.common.domin.pojo.PayDetail;
|
||||||
|
import com.group.order.config.AliPayConfig;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 支付宝支付
|
* 支付宝支付
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class AliPay implements PayType {
|
public class AliPay implements PayType {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AlipayClient alipayClient;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AliPayConfig aliPayConfig;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@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("支付宝支付");
|
System.out.println("支付宝支付");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,4 +51,17 @@ public class AliPay implements PayType {
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return PayEnum.BYALI.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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,15 @@ package com.group.order.payMethod;
|
||||||
import com.group.common.domin.pojo.PayDetail;
|
import com.group.common.domin.pojo.PayDetail;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 银行卡支付
|
* 银行卡支付
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class CardPay implements PayType{
|
public class CardPay implements PayType{
|
||||||
@Override
|
@Override
|
||||||
public void pay(PayDetail payDetail) {
|
public void pay(PayDetail payDetail, HttpServletResponse response) {
|
||||||
System.out.println("银行卡支付");
|
System.out.println("银行卡支付");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ public class PayContext implements InitializingBean, ApplicationContextAware {
|
||||||
*/
|
*/
|
||||||
public PayType getPay(Integer type){
|
public PayType getPay(Integer type){
|
||||||
String payType=null;
|
String payType=null;
|
||||||
|
|
||||||
if (type==1){
|
if (type==1){
|
||||||
payType=PayEnum.BYALI.getName();
|
payType=PayEnum.BYALI.getName();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,14 @@ package com.group.order.payMethod;
|
||||||
import com.group.common.domin.pojo.PayDetail;
|
import com.group.common.domin.pojo.PayDetail;
|
||||||
import com.group.common.result.R;
|
import com.group.common.result.R;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 抽象接口
|
* 抽象接口
|
||||||
*/
|
*/
|
||||||
public interface PayType {
|
public interface PayType {
|
||||||
|
|
||||||
public void pay(PayDetail payDetail);
|
public void pay(PayDetail payDetail,HttpServletResponse response);
|
||||||
|
|
||||||
public String getName();
|
public String getName();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,15 @@ package com.group.order.payMethod;
|
||||||
import com.group.common.domin.pojo.PayDetail;
|
import com.group.common.domin.pojo.PayDetail;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*微信支付
|
*微信支付
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class WeChat implements PayType{
|
public class WeChat implements PayType{
|
||||||
@Override
|
@Override
|
||||||
public void pay(PayDetail payDetail) {
|
public void pay(PayDetail payDetail, HttpServletResponse response) {
|
||||||
System.out.println("微信支付");
|
System.out.println("微信支付");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,11 @@ import com.group.common.domin.pojo.KillOrderEntity;
|
||||||
import com.group.common.result.R;
|
import com.group.common.result.R;
|
||||||
import com.group.order.pojo.ReqPay;
|
import com.group.order.pojo.ReqPay;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 秒杀订单服务层
|
* 秒杀订单服务层
|
||||||
*/
|
*/
|
||||||
public interface KillOrderService extends IService<KillOrderEntity> {
|
public interface KillOrderService extends IService<KillOrderEntity> {
|
||||||
R payKill(ReqPay pay);
|
R payKill(ReqPay pay, HttpServletResponse response);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<PayDetail> {
|
||||||
|
}
|
|
@ -12,10 +12,14 @@ import com.group.order.payMethod.PayContext;
|
||||||
import com.group.order.payMethod.PayType;
|
import com.group.order.payMethod.PayType;
|
||||||
import com.group.order.pojo.ReqPay;
|
import com.group.order.pojo.ReqPay;
|
||||||
import com.group.order.service.KillOrderService;
|
import com.group.order.service.KillOrderService;
|
||||||
|
import com.group.order.service.PayDetailService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.util.Date;
|
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<KillOrderMapper, KillOrderEntity>
|
public class KillOrderServiceImpl extends ServiceImpl<KillOrderMapper, KillOrderEntity>
|
||||||
implements KillOrderService {
|
implements KillOrderService {
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private PayDetailMapper payDetailMapper;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private PayContext payContext;
|
private PayContext payContext;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PayDetailService payDetailService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public R payKill(ReqPay pay) {
|
public R payKill(ReqPay pay, HttpServletResponse response) {
|
||||||
//安全校验
|
//安全校验
|
||||||
//防止幂等性
|
//防止幂等性,避免已经成功或者失败的订单重新支付
|
||||||
KillOrderEntity order = getById(pay.getOrderId());
|
KillOrderEntity order = getById(pay.getOrderId());
|
||||||
if (order.getKillState()!=1){
|
if (order.getKillState()==2){
|
||||||
return R.error("订单已经支付,请不要重复支付");
|
return R.error("订单已经支付,请不要重复支付");
|
||||||
}
|
}
|
||||||
|
if (order.getKillState()==3){
|
||||||
|
return R.error("订单已经过期,请重新下单");
|
||||||
|
}
|
||||||
|
//判断是否有支付流水正在处理中
|
||||||
|
List<PayDetail> 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()
|
PayDetail payDetail = PayDetail.builder()
|
||||||
.payPay(IdUtil.getSnowflakeNextIdStr())
|
.payDetail(IdUtil.getSnowflakeNextIdStr())
|
||||||
.payOrder(pay.getOrderId())
|
.payOrder(pay.getOrderId())
|
||||||
.payCreate(new Date())
|
.payCreate(new Date())
|
||||||
.payTitle(pay.getPrice())
|
.payPrice(pay.getPrice())
|
||||||
.build();
|
.build();
|
||||||
payDetailMapper.insert(payDetail);
|
payDetailService.save(payDetail);
|
||||||
PayType pay1 = payContext.getPay(pay.getType());
|
PayType type = payContext.getPay(pay.getType());
|
||||||
pay1.pay(payDetail);
|
type.pay(payDetail,response);
|
||||||
|
return R.ok();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<PayDetailMapper, PayDetail>
|
||||||
|
implements PayDetailService {
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue