package com.goalias.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.goalias.dto.Result;
import com.goalias.entity.VoucherOrder;
import com.goalias.mapper.VoucherOrderMapper;
import com.goalias.service.ISeckillVoucherService;
import com.goalias.service.IVoucherOrderService;
import com.goalias.utils.RedisIdWorker;
import com.goalias.utils.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.connection.stream.*;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* <p>
* 服务实现类
* </p>
*
* @author 高文升
* @since 2021-12-22
*/
@Slf4j
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {
@Resource
private ISeckillVoucherService seckillVoucherService;
@Resource
private RedisIdWorker redisIdWorker;
@Resource
private RedissonClient redissonClient;
@Resource
private StringRedisTemplate stringRedisTemplate;
private static final DefaultRedisScript<Long> SECKILL_SCRIPT;
//ExecutorService pool=new ThreadPoolExecutor();
static {
SECKILL_SCRIPT = new DefaultRedisScript<>();
SECKILL_SCRIPT.setLocation(new ClassPathResource("seckill.lua"));
SECKILL_SCRIPT.setResultType(Long.class);
}
private static final ExecutorService SECKILL_ORDER_EXECUTOR = Executors.newSingleThreadExecutor();
@PostConstruct
private void init() {
SECKILL_ORDER_EXECUTOR.submit(new VoucherOrderHandler());
}
private class VoucherOrderHandler implements Runnable {
@Override
public void run() {
while (true) {
try {
// 1.获取消息队列中的订单信息 XREADGROUP GROUP g1 c1 COUNT 1 BLOCK 2000 STREAMS s1 >
List<MapRecord<String, Object, Object>> list = stringRedisTemplate.opsForStream().read(
Consumer.from("g1", "c1"),
StreamReadOptions.empty().count(1).block(Duration.ofSeconds(2)),
StreamOffset.create("stream.orders", ReadOffset.lastConsumed())
);
// 2.判断订单信息是否为空
if (list == null || list.isEmpty()) {
// 如果为null,说明没有消息,继续下一次循环
continue;
}
// 解析数据
MapRecord<String, Object, Object> record = list.get(0);
Map<Object, Object> value = record.getValue();
VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(value, new VoucherOrder(), true);
// 3.创建订单
createVoucherOrder(voucherOrder);
// 4.确认消息 XACK
stringRedisTemplate.opsForStream().acknowledge("s1", "g1", record.getId());
} catch (Exception e) {
log.error("处理订单异常", e);
handlePendingList();
}
}
}
private void handlePendingList() {
while (true) {
try {
// 1.获取pending-list中的订单信息 XREADGROUP GROUP g1 c1 COUNT 1 BLOCK 2000 STREAMS s1 0
List<MapRecord<String, Object, Object>> list = stringRedisTemplate.opsForStream().read(
Consumer.from("g1", "c1"),
StreamReadOptions.empty().count(1),
StreamOffset.create("stream.orders", ReadOffset.from("0"))
);
// 2.判断订单信息是否为空
if (list == null || list.isEmpty()) {
// 如果为null,说明没有异常消息,结束循环
break;
}
// 解析数据
MapRecord<String, Object, Object> record = list.get(0);
Map<Object, Object> value = record.getValue();
VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(value, new VoucherOrder(), true);
// 3.创建订单
createVoucherOrder(voucherOrder);
// 4.确认消息 XACK
stringRedisTemplate.opsForStream().acknowledge("s1", "g1", record.getId());
} catch (Exception e) {
log.error("处理订单异常", e);
}
}
}
}
/*private BlockingQueue<VoucherOrder> orderTasks = new ArrayBlockingQueue<>(1024 * 1024);
private class VoucherOrderHandler implements Runnable{
@Override
public void run() {
while (true){
try {
// 1.获取队列中的订单信息
VoucherOrder voucherOrder = orderTasks.take();
// 2.创建订单
createVoucherOrder(voucherOrder);
} catch (Exception e) {
log.error("处理订单异常", e);
}
}
}
}*/
private void createVoucherOrder(VoucherOrder voucherOrder) {
Long userId = voucherOrder.getUserId();
Long voucherId = voucherOrder.getVoucherId();
// 创建锁对象
RLock redisLock = redissonClient.getLock("lock:order:" + userId);
// 尝试获取锁
boolean isLock = redisLock.tryLock();
// 判断
if (!isLock) {
// 获取锁失败,直接返回失败或者重试
log.error("不允许重复下单!");
return;
}
try {
// 5.1.查询订单
int count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();
// 5.2.判断是否存在
if (count > 0) {
// 用户已经购买过了
log.error("不允许重复下单!");
return;
}
// 6.扣减库存
boolean success = seckillVoucherService.update()
.setSql("stock = stock - 1") // set stock = stock - 1
.eq("voucher_id", voucherId).gt("stock", 0) // where id = ? and stock > 0
.update();
if (!success) {
// 扣减失败
log.error("库存不足!");
return;
}
// 7.创建订单
save(voucherOrder);
} finally {
// 释放锁
redisLock.unlock();
}
}
@Override
public Result seckillVoucher(Long voucherId) {
Long userId = UserHolder.getUser().getId();
long orderId = redisIdWorker.nextId("order");
// 1.执行lua脚本
Long result = stringRedisTemplate.execute(
SECKILL_SCRIPT,
Collections.emptyList(),
voucherId.toString(), userId.toString(), String.valueOf(orderId)
);
int r = result.intValue();
// 2.判断结果是否为0
if (r != 0) {
// 2.1.不为0 ,代表没有购买资格
return Result.fail(r == 1 ? "库存不足" : "不能重复下单");
}
// 3.返回订单id
return Result.ok(orderId);
}
/*@Override
public Result s
没有合适的资源?快使用搜索试试~ 我知道了~
本项目是一个点评类项目,实现了短信登录、探店点评、商品秒杀、每日签到、好友关注等多个模块
共83个文件
java:75个
xml:2个
lua:2个
1.该资源内容由用户上传,如若侵权请联系客服进行举报
2.虚拟产品一经售出概不退款(资源遇到问题,请及时私信上传者)
2.虚拟产品一经售出概不退款(资源遇到问题,请及时私信上传者)
版权申诉
0 下载量 48 浏览量
2024-06-15
13:26:09
上传
评论
收藏 75KB ZIP 举报
温馨提示
本项目是一个点评类项目,实现了短信登录、探店点评、商品秒杀、每日签到、好友关注等多个模块。用户可以浏览首页的推荐内容,搜索附近的商家,查看商家的详情和评价以及发表探店博客,抢购商家发布的限时秒杀商品.zip
资源推荐
资源详情
资源评论
收起资源包目录
本项目是一个点评类项目,实现了短信登录、探店点评、商品秒杀、每日签到、好友关注等多个模块。用户可以浏览首页的推荐内容,搜索附近的商家,查看商家的详情和评价以及发表探店博客,抢购商家发布的限时秒杀商品.zip (83个子文件)
intelligent-life-master
pom.xml 4KB
src
test
java
com
goalias
Thread.java 265B
RedissonTest.java 1KB
NormalTest.java 1KB
GoaliasApplicationTests.java 4KB
main
resources
seckill.lua 974B
mapper
VoucherMapper.xml 659B
Dockerfile.yml 258B
unlock.lua 175B
application.yaml 648B
db
life.sql 26KB
java
com
goalias
mapper
UserInfoMapper.java 274B
ShopMapper.java 262B
BlogMapper.java 262B
BlogCommentsMapper.java 286B
UserMapper.java 262B
VoucherMapper.java 407B
VoucherOrderMapper.java 286B
ShopTypeMapper.java 274B
FollowMapper.java 268B
SeckillVoucherMapper.java 343B
controller
UploadController.java 2KB
BlogCommentsController.java 341B
ShopTypeController.java 795B
UserController.java 3KB
ShopController.java 3KB
VoucherController.java 1KB
BlogController.java 3KB
VoucherOrderController.java 970B
FollowController.java 917B
utils
LoginInterceptor.java 710B
RedisData.java 178B
UserHolder.java 371B
RedisConstants.java 868B
RefreshTokenInterceptor.java 2KB
RegexUtils.java 1KB
CacheClient.java 6KB
RedisIdWorker.java 1KB
SystemConstants.java 322B
PasswordEncoder.java 1KB
SimpleRedisLock.java 2KB
ILock.java 329B
RegexPatterns.java 652B
service
IBlogCommentsService.java 287B
IVoucherService.java 393B
IShopService.java 438B
IBlogService.java 531B
IUserInfoService.java 275B
IFollowService.java 433B
IUserService.java 537B
impl
FollowServiceImpl.java 3KB
SeckillVoucherServiceImpl.java 576B
UserServiceImpl.java 6KB
VoucherOrderServiceImpl.java 15KB
ShopServiceImpl.java 5KB
UserInfoServiceImpl.java 483B
BlogCommentsServiceImpl.java 511B
ShopTypeServiceImpl.java 483B
BlogServiceImpl.java 8KB
VoucherServiceImpl.java 2KB
IShopTypeService.java 275B
ISeckillVoucherService.java 344B
IVoucherOrderService.java 361B
entity
UserInfo.java 1KB
Blog.java 2KB
SeckillVoucher.java 1KB
Voucher.java 2KB
ShopType.java 1KB
Follow.java 913B
VoucherOrder.java 1KB
BlogComments.java 1KB
Shop.java 2KB
User.java 1KB
dto
Result.java 756B
LoginFormDTO.java 163B
UserDTO.java 153B
ScrollResult.java 187B
GoaliasApplication.java 401B
config
WebExceptionAdvice.java 492B
RedissonConfig.java 589B
MybatisConfig.java 665B
MvcConfig.java 1KB
.gitignore 395B
共 83 条
- 1
资源评论
处处清欢
- 粉丝: 810
- 资源: 2770
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功