springboot+支付宝+redis实现商品购买和订单超时处理
写在前面:做开发尤其是Java开发的朋友肯定都想去阿里这样的大厂(反正作者想去哈哈~),挑战“三高”以及各种极限到不能再极限的业务!那么今天作者就从这类电商企业最基本也是最核心的业务切入,简单实现一个订单支付业务
说人话:就是把交易信息保存起来,不然您的年度账单哪来
前期准备
支付开放平台
首先需要登录支付宝的支付开放平台并生成一个沙箱支付的账号
这里需要配置自己本地的回调地址

这里作者已经注册好了,然后简单了解一下支付的开放api就可以开始了

开发三板斧:约定>配置>编码
项目结构

数据库表结构
CREATE TABLE `product_order` ( `id` int(11) NOT NULL AUTO_INCREMENT, `out_trade_no` varchar(255) DEFAULT NULL COMMENT '订单编号', `subject` varchar(255) DEFAULT NULL COMMENT '订单名', `total_amount` double(10,2) DEFAULT NULL COMMENT '订单金额', `order_status` tinyint(2) DEFAULT '0' COMMENT '订单状态(0:未支付,1:已支付,3:订单超时)', `product_code` varchar(255) DEFAULT NULL COMMENT '产品编号', `create_time` datetime DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
依赖
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.5.4
com.excesys
springboot-pay-service
0.0.1-SNAPSHOT
springboot-pay-service
springboot集成支付
8
org.springframework.boot
spring-boot-starter-web
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
com.belerweb
pinyin4j
2.5.1
com.alibaba
fastjson
1.2.78
com.alipay.sdk
alipay-sdk-java
3.7.110.ALL
com.alibaba
easyexcel
2.2.10
org.springframework.boot
spring-boot-starter-data-redis
redis.clients
jedis
mysql
mysql-connector-java
runtime
com.baomidou
mybatis-plus-boot-starter
3.3.0
org.springframework.boot
spring-boot-starter-thymeleaf
cn.hutool
hutool-all
5.8.16
org.springframework.boot
spring-boot-maven-plugin
org.projectlombok
lombok
配置
server:
port: 8081
spring:
application:
name: springboot-pay-service
main:
allow-bean-definition-overriding: true
datasource:
url: jdbc:mysql://localhost:3306/test?characterEncoding=utf-8&serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
thymeleaf:
cache: false
redis:
host: localhost
password:
database: 15
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: off
type-aliases-package: com.excesys.pojo
# ----------阿里云-支付宝相关配置-----------
alipay:
#应用ID
appId: 你的应用ID
#应用公钥
alipayPublicKey: 你的应用公钥
#应用私钥
privateKey: 你的应用私钥
#异步通知回调地址
notifyUrl: http://127.0.0.1:8081/paySuccess
#同步回调地址 - 成功回调地址
returnUrl: http://127.0.0.1:8081/paySuccess
#编码格式
charset: UTF-8
#签名类型 - 密钥凭证
signType: RSA2
#支付宝的网关地址(注:这里是开发地址,生产和开发不一样)
gatewayUrl: https://openapi-sandbox.dl.alipaydev.com/gateway.do
logging:
level:
org:
springframework: info
file:
name: /logs/${spring.application.name}.log
代码
支付配置实体类
package com.excesys.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Data
@Configuration
//对应配置文件里的前缀
@ConfigurationProperties(prefix = "alipay")
public class AliPayConfig {
private String appId;
private String privateKey;
private String alipayPublicKey;
private String notifyUrl;
private String returnUrl;
private String charset;
private String signType;
private String gatewayUrl;
}
支付客户端请求实体
package com.excesys.bean;
import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.excesys.config.AliPayConfig;
import com.excesys.pojo.ProductOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class AliPay {
@Autowired
private AliPayConfig aliPayConfig;
public String pay(Object obj) throws AlipayApiException {
//支付网关
String serverUrl = aliPayConfig.getGatewayUrl();
// AppId
String appId = aliPayConfig.getAppId();
// 用户密钥(私钥),即PKCS8格式RSA2私钥
String privateKey = aliPayConfig.getPrivateKey();
//格式化为json格式
String format = "json";
//编码
String charset = aliPayConfig.getCharset();
//支付宝公钥,即对应Appid下的支付宝公钥
String alipayPublicKey = aliPayConfig.getAlipayPublicKey();
//签名方式
String signType = aliPayConfig.getSignType();
//页面跳转同步通知页面路径
String returnUrl = aliPayConfig.getReturnUrl();
//服务器异步通知页面路径
String notifyUrl = aliPayConfig.getNotifyUrl();
//1、获取初始化的AlipayClient
AlipayClient alipayClient = new DefaultAlipayClient(serverUrl, appId, privateKey, format, charset, alipayPublicKey, signType);
//2、设置请求参数
AlipayTradePagePayRequest alipayTradePagePayRequest = new AlipayTradePagePayRequest();
// 页面跳转同步通知页面路径
alipayTradePagePayRequest.setReturnUrl(returnUrl);
alipayTradePagePayRequest.setNotifyUrl(notifyUrl);
//封装参数(json格式)
alipayTradePagePayRequest.setBizContent(JSON.toJSONString(obj));
//3、请求支付宝进行付款,并获取支付结果
String body = alipayClient.pageExecute(alipayTradePagePayRequest).getBody();
//放回信息
return body;
}
}
Redis配置
package com.excesys.config;
import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
RedisTemplate template = new RedisTemplate();
template.setConnectionFactory(factory);
//redis序列化--key采用String的序列化方式,value采用json的序列化方式
// template.setKeySerializer(RedisSerializer.string());
// template.setHashKeySerializer(RedisSerializer.string());
// template.setValueSerializer(RedisSerializer.json());
// template.setHashValueSerializer(RedisSerializer.json());
//Jackson序列化--key采用String的序列化方式,value采用Jackson的序列化方式
// Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
// //序列化包括类型描述 否则反向序列化实体会报错,一律都为JsonObject
// ObjectMapper mapper = new ObjectMapper();
// mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
// jackson2JsonRedisSerializer.setObjectMapper(mapper);
// template.setKeySerializer(RedisSerializer.string());
// template.setHashKeySerializer(RedisSerializer.string());
// template.setValueSerializer(jackson2JsonRedisSerializer);
// template.setHashValueSerializer(jackson2JsonRedisSerializer);
//FastJson序列化--key采用String的序列化方式,value采用FastJson的序列化方式
GenericFastJsonRedisSerializer fastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
template.setKeySerializer(RedisSerializer.string());
template.setHashKeySerializer(RedisSerializer.string());
template.setValueSerializer(fastJsonRedisSerializer);
template.setHashValueSerializer(fastJsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
redis工具类
package com.excesys.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* Redis工具类
*/
@Component
public final class RedisUtils {
private final static Logger log = LoggerFactory.getLogger(RedisUtils.class);
@Resource
private RedisTemplate redisTemplate;
// =============================common============================
/**
* 指定缓存失效时间
*
* @param key 键
* @param time 时间(秒)
* @return
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据key 获取过期时间
*
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
*
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete((Collection) CollectionUtils.arrayToList(key));
}
}
}
// ============================String=============================
/**
* 普通缓存获取
*
* @param key 键
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
*
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
*
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
*
* @param key 键
* @param delta 要增加几(大于0)
* @return
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
*
* @param key 键
* @param delta 要减少几(小于0)
* @return
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
// ================================Map=================================
/**
* HashGet
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return 值
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
*
* @param key 键
* @return 对应的多个键值
*/
public Map hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
*
* @param key 键
* @param map 对应多个键值
* @return true 成功 false 失败
*/
public boolean hmset(String key, Map map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet 并设置时间
*
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*;
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除hash表中的值
*
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/**
* 判断hash表中是否有该项的值
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
*
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
* @return
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash递减
*
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
* @return
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
// ============================set=============================
/**
* 根据key获取Set中的所有值
*
* @param key 键
* @return
*/
public Set sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据value从一个set中查询,是否存在
*
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将数据放入set缓存
*
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 将set数据放入缓存
*
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0)
expire(key, time);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取set缓存的长度
*
* @param key 键
* @return
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 移除值为value的
*
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
// ===============================list=================================
/**
* 获取list缓存的内容
*
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
* @return
*/
public List lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取list缓存的长度
*
* @param key 键
* @return
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 通过索引 获取list中的值
*
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
* @return
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, List value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据索引修改list中的某条数据
*
* @param key 键
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 移除N个值为value
*
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* @param prefix 前缀
* @param ids id
*/
public void delByKeys(String prefix, Set ids) {
Set keys = new HashSet();
for (Long id : ids) {
keys.addAll(redisTemplate.keys(new StringBuffer(prefix).append(id).toString()));
}
Boolean count = redisTemplate.delete(String.valueOf(keys));
// 此处提示可自行删除
log.info("--------------------------------------------");
log.info("成功删除缓存:" + keys.toString());
log.info("缓存删除数量:" + count + "个");
log.info("--------------------------------------------");
}
/**
* @param prefix 前缀
*/
public void delByKeys(String prefix) {
Set keys = redisTemplate.keys(prefix + "*");
long count = redisTemplate.delete(keys);
// 此处提示可自行删除
log.info("--------------------------------------------");
log.info("成功删除缓存:" + keys.toString());
log.info("缓存删除数量:" + count + "个");
log.info("--------------------------------------------");
}
}
订单实体类
package com.excesys.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ProductOrder implements Serializable {
@TableId(type = IdType.AUTO)
private Long id;
/**
* 商户订单号
*/
@TableField("out_trade_no")
private String out_trade_no;
/**
* 订单名称
*/
private String subject;
/**
* 付款金额
*/
@TableField("total_amount")
private Double total_amount;
/**
* 订单状态
*/
@TableField("order_status")
private Integer order_status;
/**
* 产品编号
*/
private String product_code = "FAST_INSTANT_TRADE_PAY";
/**
* 创建时间
*/
private LocalDateTime create_time;
}
Dao(数据访问层)
package com.excesys.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.excesys.pojo.ProductOrder;
//这里没有mapper注解是因为启动类已经开启扫描
public interface ProductOrderMapper extends BaseMapper {
}
service及其实现(业务逻辑层)
package com.excesys.service;
import com.alipay.api.AlipayApiException;
import com.excesys.pojo.ProductOrder;
public interface AliPayService {
/**
* 支付宝支付接口
*/
String aliPay(ProductOrder order) throws AlipayApiException;
}
package com.excesys.service.impl;
import com.alipay.api.AlipayApiException;
import com.excesys.bean.AliPay;
import com.excesys.pojo.ProductOrder;
import com.excesys.service.AliPayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AliPayServiceImpl implements AliPayService {
@Autowired
private AliPay aliPay;
@Override
public String aliPay(ProductOrder order) throws AlipayApiException {
System.out.println("order----service--:" + order);
return aliPay.pay(order);
}
}
Controller(控制层)
package com.excesys.controller;
import cn.hutool.core.util.IdUtil;
import com.alipay.api.AlipayApiException;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.excesys.mapper.ProductOrderMapper;
import com.excesys.pojo.ProductOrder;
import com.excesys.service.AliPayService;
import com.excesys.util.RedisUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
@RestController
public class AliPayController {
@Autowired
private AliPayService aliPayService;
@Autowired
private RedisUtils redisUtils;
@Resource
private ProductOrderMapper productOrderMapper;
/**
* 进入支付主页
*
* @return
*/
@GetMapping("/")
public ModelAndView index() {
ModelAndView mav = new ModelAndView();
mav.setViewName("testAliPay");
return mav;
}
/**
* 提交订单
*
* @param subject
* @param totalAmount
* @return
* @throws AlipayApiException
*/
@PostMapping("/order/alipay")
public String aliPay(String subject, Double totalAmount) throws AlipayApiException {
System.out.println("--------进入提交支付----------");
ProductOrder order = new ProductOrder();
order.setOut_trade_no(IdUtil.getSnowflakeNextIdStr());
order.setSubject(subject);
order.setTotal_amount(totalAmount);
order.setCreate_time(LocalDateTime.now());
productOrderMapper.insert(order);
//同时将订单信息存入redis并设置过期时间
redisUtils.set(order.getOut_trade_no(), order, 60);
//如果在过期时间内订单未支付则删除redis中的订单信息
System.out.println("order-----:" + order);
return aliPayService.aliPay(order);
}
/**
* 支付成功
*
* @return
*/
@GetMapping("/paySuccess")
public ModelAndView paySuccess(HttpServletRequest request) {
System.out.println("--------支付成功----------");
//如果支付成功,订单就要新增到数据库
//如果取消支付,也要新增订单到数据库,取消订单-状态
//订单查询能够查出订单状态 信息
String out_trade_no = request.getParameter("out_trade_no");
UpdateWrapper uw = new UpdateWrapper();
uw.eq("out_trade_no", out_trade_no);
uw.set("order_status", 1);
int flag = productOrderMapper.update(new ProductOrder(), uw);
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("out_trade_no", out_trade_no);
modelAndView.setViewName("success");
System.out.println("支付成功" + flag);
return modelAndView;
}
}
mapper(mybatis-plus对单表操作来说xml可以省略)
testAliPay.html
订单支付
| 商品名称: | |
| 商品价格: | |
success.html
支付成功
支付成功
启动类
package com.excesys;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
@EnableAsync
@EnableScheduling
@SpringBootApplication
@MapperScan("com.excesys.mapper")
public class SpringbootPayServiceApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootPayServiceApplication.class, args);
}
}
订单超时处理的定时任务
package com.excesys.task;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.excesys.mapper.ProductOrderMapper;
import com.excesys.pojo.ProductOrder;
import com.excesys.util.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
@Slf4j
@Component
public class MyOrderTask {
@Resource
private ProductOrderMapper productOrderMapper;
@Autowired
private RedisUtils redisUtils;
@Scheduled(cron = "0 0/1 * * * ?")
public void orderSync() {
QueryWrapper qw = new QueryWrapper();
List productOrders = productOrderMapper.selectList(qw);
for (int i = 0; i < productOrders.size(); i++) {
//如果在过期时间内订单未支付则删除redis中的订单信息
long expire = redisUtils.getExpire(productOrders.get(i).getOut_trade_no());
log.info(productOrders.get(i).getOut_trade_no() + "过期时间" + expire);
Integer order_status = productOrders.get(i).getOrder_status();
if (expire <= 0 && order_status == 0) {
redisUtils.del(productOrders.get(i).getOut_trade_no());
UpdateWrapper uw = new UpdateWrapper();
uw.eq("out_trade_no", productOrders.get(i).getOut_trade_no());
uw.set("order_status", 3);
int flag = productOrderMapper.update(new ProductOrder(), uw);
log.info(productOrders.get(i).getOut_trade_no() + "超时订单修改" + flag);
}
}
}
}
效果
启动主启动类访问http://localhost:8081/

填入订单信息,此时数据库生成一条订单数据状态为0(未支付)



输入沙箱买家信息和密码并支付


支付成功商家加钱买家减钱,数据库此时订单状态变成1(已支付)




继续访问http://localhost:8081/并填写订单信息,每条订单信息都会放入Redis缓存起来



超出一段时间不进行支付操作(作者这里是1分钟),则定时任务会扫描到该订单并先删除Redis数据之后再修改数据库订单状态为3(超时)



写在最后:上面就是一个订单和支付业务,但是是不在高并发状态下的简单实现机制,如果是在高并发状态下大厂的选择会更多(核心是锁),难怪12306要请阿里给他们重构系统,年关将近,祝大家阖家欢乐,万事如意,路漫漫其修远兮,吾将上下而求索!~
本文来自网络,不代表协通编程立场,如若转载,请注明出处:https://www.net2asp.com/d27835ef1e.html
