博主介绍: ✌博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家✌
Java知识图谱点击链接:体系化学习Java(Java面试专题)
💕💕 感兴趣的同学可以收藏关注下 ,不然下次找不到哟💕💕
文章目录
- 1、什么是秒杀
- 2、秒杀需要注意哪些问题
- 3、秒杀有几种实现方式
- 4、秒杀实现的逻辑步骤
- 5、基于 Redis 实现一个简单的秒杀
- 6、“秒杀”生产代码部分展示
1、什么是秒杀
秒杀是一种促销方式,通常指在限定的时间内,以极低的价格或折扣销售商品。秒杀通常会吸引大量的消费者,因为消费者可以在短时间内以非常低的价格购买到自己需要的商品,而商家也可以通过秒杀活动促进销售,提高品牌知名度。秒杀活动通常需要考虑库存、价格、时间等多个因素,因此需要进行精细的规划和执行。
2、秒杀需要注意哪些问题
秒杀活动需要注意以下几个问题:
- 库存控制:秒杀活动需要预估销售量,并控制库存,避免出现卖断货或者库存积压的情况。
- 网站流量控制:秒杀活动容易引起大量用户访问,需要考虑网站的并发访问量,以避免网站崩溃或者访问延迟等问题。
- 价格设置:秒杀活动的价格需要具有吸引力,但是也需要考虑到成本和利润,以避免亏本或者影响品牌形象。
- 订单处理:秒杀活动的订单处理需要快速、准确,以满足用户的需求。
- 客服支持:秒杀活动期间需要加强客服支持,及时解答用户的问题和投诉,保障用户的购物体验。
- 安全防范:秒杀活动容易引起黑客攻击、恶意刷单等问题,需要加强安全防范措施,保障用户的账户和交易安全。
秒杀活动还需要注意超卖问题。由于秒杀活动的商品价格通常比平时低很多,会吸引大量用户参与,但是库存有限,容易出现超卖的情况。为了避免超卖问题,可以采取以下措施:
- 合理设置商品数量:在活动策划阶段,需要根据预估的销售量和库存情况,合理设置商品数量,以避免出现超卖的情况。
- 实时更新库存信息:在秒杀活动进行期间,需要实时更新库存信息,避免超卖的情况。可以通过技术手段,比如使用分布式锁、队列等方式实现库存的实时更新。
- 限制用户购买数量:可以限制每个用户购买的数量,避免某些用户恶意抢购导致超卖的情况。
- 及时通知用户:如果出现了超卖的情况,需要及时通知用户,并及时退款或者提供其他的补偿措施,保障用户的权益和满意度。
3、秒杀有几种实现方式
秒杀活动的实现方式有多种,以下是其中几种常用的方式,以及使用的技术实现:
- 队列方式:将用户的请求放入队列中,由队列进行处理,避免并发请求对系统造成压力,同时也可以保证请求的顺序。使用技术:消息队列(如RabbitMQ、Kafka)。
- 分布式锁方式:使用分布式锁来控制并发请求的访问,避免重复的请求对系统造成压力。使用技术:分布式锁(如Redis、Zookeeper)。
- 缓存方式:将商品信息预先缓存到缓存服务器中,当用户请求时从缓存中获取,避免频繁地访问数据库。使用技术:缓存(如Redis、Memcached)。
- 限流方式:通过限制每个用户的请求次数来控制并发请求的数量,避免系统崩溃。使用技术:限流算法(如令牌桶算法、漏桶算法)。
- 异步方式:使用异步处理的方式来处理请求,通过将请求放入消息队列中异步处理,避免系统压力过大。使用技术:消息队列(如RabbitMQ、Kafka)。
以上是常用的几种秒杀活动的实现方式,不同的方式适用于不同的场景和需求,需要根据实际情况进行选择。同时,还需要注意系统的稳定性和安全性,避免出现系统崩溃或者数据泄露等问题。
4、秒杀实现的逻辑步骤
秒杀活动的实现逻辑步骤如下:
-
预热阶段:在秒杀活动开始前,提前进行预热,向用户宣传活动信息,吸引用户的关注。
-
商品准备阶段:在秒杀活动开始前,需要准备好秒杀商品的信息,包括商品名称、价格、库存等信息。
-
用户抢购阶段:秒杀活动开始后,用户可以进入抢购页面进行抢购。在这个阶段,需要进行以下处理:
a. 验证用户身份:首先需要验证用户的身份,确保只有注册用户才能参与抢购活动。
b. 验证商品库存:当用户提交抢购请求时,需要验证商品的库存是否充足,如果库存不足,则返回抢购失败的信息。
c. 生成订单:当用户抢购成功时,需要生成订单,并将订单信息保存到数据库中。
d. 扣减库存:当用户抢购成功时,需要扣减商品的库存数量。
e. 支付订单:当用户抢购成功时,需要进行支付操作,将订单的金额从用户的账户中扣除。 -
结束阶段:当秒杀活动结束后,需要进行以下处理:
a. 结算订单:对于已经生成的订单,需要进行结算操作,将订单的金额结算给商家。
b. 处理退款:对于用户取消订单或者支付失败的情况,需要进行退款操作,将金额返还给用户。
c. 统计数据:需要对秒杀活动的数据进行统计,包括参与人数、抢购成功率、销售额等信息。
以上是秒杀活动的实现逻辑步骤,不同的实现方式可能会有所不同,需要根据实际情况进行选择。同时,还需要注意系统的稳定性和安全性,避免出现系统崩溃或者数据泄露等问题。
5、基于 Redis 实现一个简单的秒杀
实现代码如下:
package com.pany.camp.redis;import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;/**** @description: 秒杀样例* @copyright: @Copyright (c) 2022 * @company: Aiocloud* @author: pany* @version: 1.0.0 * @createTime: 2023-06-26 12:37*/
public class SeckillDemo {private static final String REDIS_HOST = "localhost";private static final int REDIS_PORT = 6379;private static final String REDIS_PASSWORD = null;private static final int REDIS_TIMEOUT = 10000;private static final int REDIS_MAX_TOTAL = 100;private static final int REDIS_MAX_IDLE = 10;private static final int REDIS_MAX_WAIT_MILLIS = 10000;private static final String STOCK_KEY = "stock";private static final String ORDER_QUEUE_KEY = "order_queue";private static final String LOCK_KEY = "lock";private static JedisPool jedisPool;static {JedisPoolConfig config = new JedisPoolConfig();config.setMaxTotal(REDIS_MAX_TOTAL);config.setMaxIdle(REDIS_MAX_IDLE);config.setMaxWaitMillis(REDIS_MAX_WAIT_MILLIS);jedisPool = new JedisPool(config, REDIS_HOST, REDIS_PORT, REDIS_TIMEOUT, REDIS_PASSWORD);}public static void main(String[] args) {Jedis jedis = jedisPool.getResource();try {// 初始化库存数量jedis.set(STOCK_KEY, "100");// 模拟多个用户发起秒杀请求for (int i = 0; i < 200; i++) {Thread thread = new Thread(new UserThread());thread.start();}} finally {if (jedis != null) {jedis.close();}}}static class UserThread implements Runnable {public UserThread() {}@Overridepublic void run() {Jedis jedis = jedisPool.getResource();try {handleSecKill(jedis);} catch (Exception e) {} finally {jedis.close();}}/*** 处理秒杀* * @since 1.0.0* @param * @param jedis* @return: void * @author: pany * @version: 1.0.0 * @createTime: 2023-06-26 12:13 */ private void handleSecKill(Jedis jedis) {String userId = Thread.currentThread().getName();// 检查库存数量是否大于0String stock = jedis.get(STOCK_KEY);int stockNum = StringUtils.isBlank(stock) ? 0:Integer.parseInt(stock);if (stockNum <= 0) {System.out.println(userId + " 秒杀失败,库存不足");return;}// 将用户的请求加入到队列中jedis.lpush(ORDER_QUEUE_KEY, userId);// 获取分布式锁String lockValue = System.currentTimeMillis() + "";Long result = jedis.setnx(LOCK_KEY, lockValue);while (result == 0) {// 如果获取锁失败,则等待一段时间后重新尝试获取锁try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}result = jedis.setnx(LOCK_KEY, lockValue);}try {// 再次检查库存数量是否大于0stock = jedis.get(STOCK_KEY);if (Integer.parseInt(stock) <= 0) {System.out.println(userId + " 秒杀失败,库存不足");return;}// 减少库存数量,并将用户的请求从队列中移除jedis.decr(STOCK_KEY);jedis.lrem(ORDER_QUEUE_KEY, 0, userId);System.out.println(userId + " 秒杀成功,库存剩余:" + jedis.get(STOCK_KEY));} finally {// 释放锁if (lockValue.equals(jedis.get(LOCK_KEY))) {jedis.del(LOCK_KEY);}}}}
}
输出结果如下:
Thread-30 秒杀成功,库存剩余:99
Thread-60 秒杀成功,库存剩余:98
Thread-65 秒杀成功,库存剩余:97
Thread-110 秒杀成功,库存剩余:96
Thread-77 秒杀成功,库存剩余:95
Thread-107 秒杀成功,库存剩余:94
Thread-140 秒杀成功,库存剩余:93
Thread-109 秒杀成功,库存剩余:92
Thread-103 秒杀成功,库存剩余:91
Thread-104 秒杀成功,库存剩余:90
Thread-67 秒杀成功,库存剩余:89
Thread-111 秒杀成功,库存剩余:88
Thread-112 秒杀成功,库存剩余:87
Thread-73 秒杀成功,库存剩余:86
Thread-114 秒杀成功,库存剩余:85
Thread-115 秒杀成功,库存剩余:84
Thread-116 秒杀成功,库存剩余:83
Thread-117 秒杀成功,库存剩余:82
Thread-118 秒杀成功,库存剩余:81
Thread-82 秒杀成功,库存剩余:80
Thread-120 秒杀成功,库存剩余:79
Thread-121 秒杀成功,库存剩余:78
Thread-137 秒杀成功,库存剩余:77
Thread-123 秒杀成功,库存剩余:76
Thread-138 秒杀成功,库存剩余:75
Thread-124 秒杀成功,库存剩余:74
Thread-108 秒杀成功,库存剩余:73
Thread-127 秒杀成功,库存剩余:72
Thread-141 秒杀成功,库存剩余:71
Thread-93 秒杀成功,库存剩余:70
Thread-142 秒杀成功,库存剩余:69
Thread-159 秒杀成功,库存剩余:68
Thread-143 秒杀成功,库存剩余:67
Thread-174 秒杀成功,库存剩余:66
Thread-126 秒杀成功,库存剩余:65
Thread-162 秒杀成功,库存剩余:64
Thread-145 秒杀成功,库存剩余:63
Thread-175 秒杀成功,库存剩余:62
Thread-147 秒杀成功,库存剩余:61
Thread-131 秒杀成功,库存剩余:60
Thread-148 秒杀成功,库存剩余:59
Thread-165 秒杀成功,库存剩余:58
Thread-133 秒杀成功,库存剩余:57
Thread-149 秒杀成功,库存剩余:56
Thread-150 秒杀成功,库存剩余:55
Thread-166 秒杀成功,库存剩余:54
Thread-179 秒杀成功,库存剩余:53
Thread-96 秒杀成功,库存剩余:52
Thread-136 秒杀成功,库存剩余:51
Thread-152 秒杀成功,库存剩余:50
Thread-153 秒杀成功,库存剩余:49
Thread-102 秒杀成功,库存剩余:48
Thread-163 秒杀成功,库存剩余:47
Thread-154 秒杀成功,库存剩余:46
Thread-155 秒杀成功,库存剩余:45
Thread-156 秒杀成功,库存剩余:44
Thread-157 秒杀成功,库存剩余:43
Thread-158 秒杀成功,库存剩余:42
Thread-128 秒杀成功,库存剩余:41
Thread-160 秒杀成功,库存剩余:40
Thread-161 秒杀成功,库存剩余:39
Thread-92 秒杀成功,库存剩余:38
Thread-130 秒杀成功,库存剩余:37
Thread-192 秒杀成功,库存剩余:36
Thread-193 秒杀成功,库存剩余:35
Thread-194 秒杀成功,库存剩余:34
Thread-167 秒杀成功,库存剩余:33
Thread-168 秒杀成功,库存剩余:32
Thread-169 秒杀成功,库存剩余:31
Thread-170 秒杀成功,库存剩余:30
Thread-171 秒杀成功,库存剩余:29
Thread-139 秒杀成功,库存剩余:28
Thread-172 秒杀成功,库存剩余:27
Thread-173 秒杀成功,库存剩余:26
Thread-144 秒杀成功,库存剩余:25
Thread-146 秒杀成功,库存剩余:24
Thread-177 秒杀成功,库存剩余:23
Thread-178 秒杀成功,库存剩余:22
Thread-151 秒杀成功,库存剩余:21
Thread-180 秒杀成功,库存剩余:20
Thread-181 秒杀成功,库存剩余:19
Thread-182 秒杀成功,库存剩余:18
Thread-183 秒杀成功,库存剩余:17
Thread-184 秒杀成功,库存剩余:16
Thread-185 秒杀成功,库存剩余:15
Thread-186 秒杀成功,库存剩余:14
Thread-187 秒杀成功,库存剩余:13
Thread-188 秒杀成功,库存剩余:12
Thread-189 秒杀成功,库存剩余:11
Thread-190 秒杀成功,库存剩余:10
Thread-191 秒杀成功,库存剩余:9
Thread-164 秒杀成功,库存剩余:8
Thread-132 秒杀成功,库存剩余:7
Thread-134 秒杀成功,库存剩余:6
Thread-195 秒杀成功,库存剩余:5
Thread-196 秒杀成功,库存剩余:4
Thread-197 秒杀成功,库存剩余:3
Thread-198 秒杀成功,库存剩余:2
Thread-199 秒杀成功,库存剩余:1
Thread-200 秒杀成功,库存剩余:0
Thread-176 秒杀失败,库存不足
Thread-44 秒杀失败,库存不足
Thread-75 秒杀失败,库存不足
Thread-40 秒杀失败,库存不足
Thread-99 秒杀失败,库存不足
Thread-42 秒杀失败,库存不足
Thread-90 秒杀失败,库存不足
Thread-50 秒杀失败,库存不足
Thread-135 秒杀失败,库存不足
Thread-70 秒杀失败,库存不足
Thread-74 秒杀失败,库存不足
Thread-66 秒杀失败,库存不足
Thread-94 秒杀失败,库存不足
Thread-24 秒杀失败,库存不足
Thread-95 秒杀失败,库存不足
Thread-54 秒杀失败,库存不足
Thread-129 秒杀失败,库存不足
Thread-3 秒杀失败,库存不足
Thread-86 秒杀失败,库存不足
Thread-28 秒杀失败,库存不足
Thread-113 秒杀失败,库存不足
Thread-9 秒杀失败,库存不足
Thread-88 秒杀失败,库存不足
Thread-98 秒杀失败,库存不足
Thread-41 秒杀失败,库存不足
Thread-87 秒杀失败,库存不足
Thread-58 秒杀失败,库存不足
Thread-72 秒杀失败,库存不足
Thread-4 秒杀失败,库存不足
Thread-85 秒杀失败,库存不足
Thread-63 秒杀失败,库存不足
Thread-100 秒杀失败,库存不足
Thread-10 秒杀失败,库存不足
Thread-80 秒杀失败,库存不足
Thread-33 秒杀失败,库存不足
Thread-106 秒杀失败,库存不足
Thread-43 秒杀失败,库存不足
Thread-83 秒杀失败,库存不足
Thread-17 秒杀失败,库存不足
Thread-76 秒杀失败,库存不足
Thread-35 秒杀失败,库存不足
Thread-84 秒杀失败,库存不足
Thread-78 秒杀失败,库存不足
Thread-125 秒杀失败,库存不足
Thread-27 秒杀失败,库存不足
Thread-119 秒杀失败,库存不足
Thread-15 秒杀失败,库存不足
Thread-89 秒杀失败,库存不足
Thread-39 秒杀失败,库存不足
Thread-68 秒杀失败,库存不足
Thread-71 秒杀失败,库存不足
Thread-91 秒杀失败,库存不足
Thread-32 秒杀失败,库存不足
Thread-62 秒杀失败,库存不足
Thread-34 秒杀失败,库存不足
Thread-81 秒杀失败,库存不足
Thread-49 秒杀失败,库存不足
Thread-97 秒杀失败,库存不足
Thread-7 秒杀失败,库存不足
Thread-79 秒杀失败,库存不足
Thread-22 秒杀失败,库存不足
Thread-23 秒杀失败,库存不足
Thread-25 秒杀失败,库存不足
Thread-53 秒杀失败,库存不足
Thread-38 秒杀失败,库存不足
Thread-37 秒杀失败,库存不足
Thread-29 秒杀失败,库存不足
Thread-6 秒杀失败,库存不足
Thread-19 秒杀失败,库存不足
Thread-14 秒杀失败,库存不足
Thread-48 秒杀失败,库存不足
Thread-31 秒杀失败,库存不足
Thread-51 秒杀失败,库存不足
Thread-64 秒杀失败,库存不足
Thread-122 秒杀失败,库存不足
Thread-61 秒杀失败,库存不足
Thread-55 秒杀失败,库存不足
Thread-36 秒杀失败,库存不足
Thread-52 秒杀失败,库存不足
Thread-69 秒杀失败,库存不足
Thread-2 秒杀失败,库存不足
Thread-26 秒杀失败,库存不足
Thread-12 秒杀失败,库存不足
Thread-20 秒杀失败,库存不足
Thread-57 秒杀失败,库存不足
Thread-8 秒杀失败,库存不足
Thread-59 秒杀失败,库存不足
Thread-13 秒杀失败,库存不足
Thread-47 秒杀失败,库存不足
Thread-5 秒杀失败,库存不足
Thread-46 秒杀失败,库存不足
Thread-21 秒杀失败,库存不足
Thread-101 秒杀失败,库存不足
Thread-45 秒杀失败,库存不足
Thread-1 秒杀失败,库存不足
Thread-18 秒杀失败,库存不足
Thread-16 秒杀失败,库存不足
Thread-56 秒杀失败,库存不足
Thread-105 秒杀失败,库存不足
Thread-11 秒杀失败,库存不足
需要引入依赖:
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.7.0</version>
</dependency>
6、“秒杀”生产代码部分展示
下面是一个基于微信支付的秒杀程序:
package com.aiocloud.group.camp.controller;import com.aiocloud.camp.common.bean.BeanConvertor;
import com.aiocloud.camp.common.domain.AiocloudPageResult;
import com.aiocloud.camp.common.domain.AiocloudResult;
import com.aiocloud.camp.common.enums.*;
import com.aiocloud.camp.common.utils.DateUtil;
import com.aiocloud.camp.dao.group.vo.GpOrderVO;
import com.aiocloud.group.camp.ai.IGpOrderApp;
import com.aiocloud.group.camp.ai.dto.GpOrderDTO;
import com.aiocloud.group.camp.config.RedisLockConfig;
import com.aiocloud.group.camp.config.WxConfig;
import com.aiocloud.group.camp.utils.CommonUtil;
import com.aiocloud.group.camp.utils.MD5Util;
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.*;import static com.aiocloud.group.camp.config.WxConfig.appid;@RestController
@RequestMapping("/v1/seckill")
public class GpSeckillController {private static Logger logger = LoggerFactory.getLogger(GpSeckillController.class);/*** 超时时间*/private static final int TIMEOUT = 10000;@Resourceprivate IGpOrderApp gpOrderApp;@Autowiredprivate RedisTemplate redisTemplate;@Autowiredprivate RedisLockConfig redisLock;@PostMapping("/get")public AiocloudResult<String> getCommodity(@RequestParam(value = "seckillId", required = true) String seckillId,@RequestParam(value = "userId", required = true) String userId) {AiocloudResult<String> result = new AiocloudResult<>();try{long time = System.currentTimeMillis() + TIMEOUT;//加锁String lockKey = "aiocloud:seckill:" + seckillId;if (!redisLock.lock(lockKey, String.valueOf(time))){result.setCode(200);result.setMsg("抱歉,没抢到,换个姿式再来一遍");result.setData("fail");return result;}Integer stockNum = (Integer) redisTemplate.opsForValue().get(seckillId);if (stockNum == 0) {//库存不足result.setCode(200);result.setMsg("抱歉,商品已经被抢光了,请留意下次活动");result.setData("fail");return result;} else {String userKey = seckillId + "-" + userId;Boolean hasKey = redisTemplate.hasKey(userKey);if(hasKey) {result.setCode(200);result.setMsg("您已经抢购到了一份商品,不能重复抢购");result.setData("fail");return result;}else {stockNum--;redisTemplate.opsForValue().set(seckillId, stockNum);redisTemplate.opsForValue().set(userKey, stockNum);}}//解锁redisLock.unlock(lockKey, String.valueOf(time));result.setData("success");}catch(Exception e){result.setCode(AiocloudPageResult.ERROR);result.setMsg("秒杀商品失败");}return result;}@PostMapping("/stock")public AiocloudResult<Integer> getStockNum(@RequestParam(value = "seckillId", required = true) String seckillId) {AiocloudResult<Integer> result = new AiocloudResult<>();try{Integer stockNum = (Integer) redisTemplate.opsForValue().get(seckillId);result.setData(stockNum);} catch(Exception e) {logger.error("查询库存异常, cause by: " + e.getMessage());result.setCode(AiocloudPageResult.ERROR);result.setMsg("查询库存异常");}return result;}/*** @param [openid, outTradeNo, totalAmount, subject, body, request]* @return com.aiocloud.camp.common.domain.AiocloudResult<java.lang.Object>* @Title: pay* @Description: 秒杀微信支付* @author panyong* @version 1.0* @createtime 2021-04-07 13:42*/@PostMapping("/pay")public AiocloudResult<Object> pay(@RequestParam(value = "commodityId", required = true) String commodityId,@RequestParam(value = "seckillId", required = true) String seckillId,@RequestParam(value = "addressId", required = true) String addressId,@RequestParam(value = "remark", required = true) String remark,@RequestParam(value = "openid", required = true) String openid,@RequestParam(value = "outTradeNo", required = true) String outTradeNo,@RequestParam(value = "totalAmount", required = true) String totalAmount,@RequestParam(value = "subject", required = true) String subject,@RequestParam(value = "body", required = true) String body,HttpServletRequest request) {AiocloudResult<Object> result = new AiocloudResult<>();try {// 1、参数校验if (StringUtils.isBlank(commodityId)|| StringUtils.isBlank(addressId) || StringUtils.isBlank(openid)|| StringUtils.isBlank(outTradeNo) || StringUtils.isBlank(totalAmount)|| StringUtils.isBlank(subject) || StringUtils.isBlank(body) || StringUtils.isBlank(seckillId)) {logger.error("参数提供错误");result.setMsg("参数提供错误");return result;}// 生成订单GpOrderDTO orderDTO = new GpOrderDTO();orderDTO.setOutTradeNo(outTradeNo);orderDTO.setPayStatus(PayStatusEnum.Unpaid.getCode());orderDTO.setOrderStatus(OrderStatusEnum.OrderHasBeenPlaced.getCode());orderDTO.setPayWay(PayWayEnum.WeChat.getCode());orderDTO.setSubject(subject);orderDTO.setDescription(body);orderDTO.setOpenid(openid);orderDTO.setCreateTime(new Date());BigDecimal bigDecimal = new BigDecimal(totalAmount);orderDTO.setTotalAmount(bigDecimal);orderDTO.setCommodityId(commodityId);orderDTO.setNum(1);orderDTO.setType(OrderTypeEnum.seckill.getCode());orderDTO.setRemark(remark);orderDTO.setAddressId(addressId);orderDTO.setRelateId(seckillId);boolean isSuccess = gpOrderApp.add(orderDTO);if (isSuccess) {Map<String, String> resultMap = getWxPayMap(outTradeNo, totalAmount, openid, request);resultMap.put("signType", "MD5");resultMap.put("total_fee", totalAmount);String timestamp = String.valueOf(DateUtil.getSecondTimestamp(new Date()));resultMap.put("timestamp", timestamp);String stringSignTemp = "appId=" + resultMap.get("appid") +"&nonceStr=" + resultMap.get("nonce_str") +"&package=prepay_id=" + resultMap.get("prepay_id") +"&signType=MD5" +"&timeStamp=" + resultMap.get("timestamp") +"&key=" + WxConfig.apiKey;String finalsign = MD5Util.getMD5Str(stringSignTemp).toUpperCase();resultMap.put("finalsign", finalsign);result.setData(resultMap);}} catch (Exception e) {result.setCode(AiocloudResult.ERROR);result.setMsg("支付失败");}return result;}@Transactionalpublic Map<String, String> getWxPayMap(String outTradeNo, String totalAmount, String openId, HttpServletRequest request) throws Exception {String orderExpireTime = DateUtil.getOrderExpireTime(6 * 60 * 1000L);SortedMap<String, String> req = new TreeMap<String, String>();//公众号req.put("appid", appid);// 商户号req.put("mch_id", WxConfig.mchId);// 32位随机字符串req.put("nonce_str", WXPayUtil.generateNonceStr());// 商品描述req.put("body", "aiocloud开发微信支付测试");// 商户订单号req.put("out_trade_no", outTradeNo);DecimalFormat decimalFormat = new DecimalFormat("###################.###########");// 标价金额(分)String total_fee = decimalFormat.format(Double.parseDouble(totalAmount) * 100);req.put("total_fee", "1");// 终端IPreq.put("spbill_create_ip", CommonUtil.getIp(request));// 回调地址req.put("notify_url", WxConfig.notifyUrl);// 交易类型req.put("trade_type", WxConfig.tradeType);// 超时时间req.put("time_expire", orderExpireTime);req.put("openid", openId);req.put("sign", WXPayUtil.generateSignature(req, WxConfig.apiKey, WXPayConstants.SignType.MD5)); // 签名// 生成要发送的 xmlString xmlBody = WXPayUtil.generateSignedXml(req, WxConfig.apiKey);System.err.println(String.format("微信支付预下单请求 xml 格式:\n%s", xmlBody));//发送 POST 请求 统一下单 API 并携带 xmlBody 内容,然后获得返回接口结果String res = CommonUtil.httpsRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST", xmlBody);System.err.println(String.format("%s", res));Map<String, String> resultMap = WXPayUtil.xmlToMap(res);//将返回结果从 xml 格式转换为 map 格式return resultMap;}/*** 接收微信官方返回觉得支付结果通知** @return* @throws Exception*/@ApiOperation("接收支付结果通知的接口")@PostMapping("/getNotifyUrl")@ResponseBodypublic String weiXinPayCallBack(HttpServletRequest request) throws Exception {Map<String, String> rMap = new HashMap<String, String>();//接收微信官方返回的支付结果InputStream inputStream = request.getInputStream();BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));String temp;StringBuilder stringBuilder = new StringBuilder();while ((temp = bufferedReader.readLine()) != null) {stringBuilder.append(temp);}//关闭流,先打开后关,后打开先关bufferedReader.close();inputStream.close();Map<String, String> resultMap = WXPayUtil.xmlToMap(stringBuilder.toString());//判断是不是来自微信官方,进行签名验证boolean flag = WXPayUtil.isSignatureValid(resultMap, WxConfig.apiKey, WXPayConstants.SignType.MD5);if (flag) {//获取支付结果中的result_code,根据此值判断是否进行自身业务实现String resultCode = resultMap.get("result_code");if (resultCode.equals("SUCCESS")) {//获取微信返回的交易号String tradeNo = resultMap.get("transaction_id");//商户订单号String outTradeNo = resultMap.get("out_trade_no");//支付状态判断和自身业务实现GpOrderVO order = gpOrderApp.getOrderByOutTradeNo(outTradeNo);if (order != null) {//格式化成所需的商品金额BigDecimal totalAmount = order.getTotalAmount();double TotalFeeDouble = totalAmount.doubleValue() * 100;//回调接口和实际订单进行比较if (TotalFeeDouble == Double.parseDouble(resultMap.get("total_fee"))) {//修改订单支付状态order.setPayTime(new Date());order.setPayStatus(PayStatusEnum.PaymentSuccessful.getCode());order.setOrderStatus(OrderStatusEnum.CompleteOrder.getCode());order.setConfirm(OrderConfirmEnum.Delivered.getCode());order.setTradeNo(tradeNo);gpOrderApp.update(BeanConvertor.getCopyObject(GpOrderDTO.class, order));rMap.put("return_code", "SUCCESS");rMap.put("return_msg", "已收到");}} else {rMap.put("return_code", "FAIL");rMap.put("return_msg", "已收到");}} else {rMap.put("return_code", "FAIL");rMap.put("return_msg", "已收到");}} else {rMap.put("return_code", "FAIL");rMap.put("return_msg", "已收到");}//以xml格式给微信官方返回是否成功的应答return WXPayUtil.mapToXml(rMap);}@PostMapping("/wait/pay")public AiocloudResult<Object> pay(@RequestParam(value = "outTradeNo", required = true) String outTradeNo,@RequestParam(value = "openid", required = true) String openid,HttpServletRequest request) {AiocloudResult<Object> result = new AiocloudResult<>();try {// 1、参数校验if (StringUtils.isBlank(outTradeNo)) {logger.error("参数提供错误");result.setMsg("参数提供错误");return result;}GpOrderVO order = gpOrderApp.getOrderByOutTradeNo(outTradeNo);String totalAmount = order.getTotalAmount().toString();Map<String, String> resultMap = getWxPayMap(outTradeNo, totalAmount, openid, request);resultMap.put("signType", "MD5");resultMap.put("total_fee", totalAmount);String timestamp = String.valueOf(DateUtil.getSecondTimestamp(new Date()));resultMap.put("timestamp", timestamp);String stringSignTemp = "appId=" + resultMap.get("appid") +"&nonceStr=" + resultMap.get("nonce_str") +"&package=prepay_id=" + resultMap.get("prepay_id") +"&signType=MD5" +"&timeStamp=" + resultMap.get("timestamp") +"&key=" + WxConfig.apiKey;String finalsign = MD5Util.getMD5Str(stringSignTemp).toUpperCase();resultMap.put("finalsign", finalsign);result.setData(resultMap);} catch (Exception e) {result.setCode(AiocloudResult.ERROR);result.setMsg("支付失败");}return result;}
}
💕💕 本文由激流原创,首发于CSDN博客,博客主页 https://blog.csdn.net/qq_37967783?spm=1010.2135.3001.5421
💕💕喜欢的话记得点赞收藏啊