秒杀剩余部分+支付

idcard
2024-05-06 19:28:23 +08:00
parent 399e9bb1c6
commit d6f55e9bf1
26 changed files with 667 additions and 75 deletions

View File

@ -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;
}

View File

@ -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;
}

View File

@ -23,5 +23,10 @@
<groupId>com.song</groupId>
<artifactId>group-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -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;
}
}

View File

@ -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());
}
}

View File

@ -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);
}
}

View File

@ -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
*/
}

View File

@ -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);
}

View File

@ -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);
}
}
}

View File

@ -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
);
}
}

View File

@ -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;
}

View File

@ -9,4 +9,5 @@ import com.group.common.result.R;
*/
public interface SecKillService extends IService<SeckillEntity> {
R buyGoodsByOne(Integer id);
}

View File

@ -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 ServiceImpl<SecKillMapper, SeckillEntity
private RedisCache redisCache;
@Autowired
private Redisson redisson;
private RedissonClient redissonClient;
@Autowired
private Product productor;
@Override
public R buyGoodsByOne(Integer id) {
@ -62,26 +66,33 @@ public class SecKillServiceImpl extends ServiceImpl<SecKillMapper, SeckillEntity
if (session.getSessionEnd().getTime()<time){
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 来存储,通过键来获取商品库存
//校验用户是否购买过
//获取用户id
Integer userId = UserUtil.getUserId(request);
//通过用户id和秒杀商品id,查询该用户是否第一次购买该商品
if (killOrderService.exitResult(id,userId)){
throw new RuntimeException("用户已购买该商品");
Integer stock = redisCache.getCacheObject("product_id:" + id);
if (stock<=0){
throw new RuntimeException("库存不足,请不要再次点击");
}
//减少库存,把数据同步redis中
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查询秒杀商品信息,判断库存是否足够
//库存信息存储在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;
return R.ok();
}
}

View File

@ -23,5 +23,18 @@
<groupId>com.song</groupId>
<artifactId>group-common</artifactId>
</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>
</project>

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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<KillOrderEntity>()
.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<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);
}
}
}
}

View File

@ -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;
}
}

View File

@ -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("银行卡支付");
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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("微信支付");
}

View File

@ -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<KillOrderEntity> {
R payKill(ReqPay pay);
R payKill(ReqPay pay, HttpServletResponse response);
}

View File

@ -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> {
}

View File

@ -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<KillOrderMapper, KillOrderEntity>
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<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()
.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();
}
}

View File

@ -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 {
}