商城项目秒杀业务和订单业务秒杀订单整合----商城项目

package com.alatus.mall.seckill.service.impl;

import com.alatus.common.to.mq.SeckillOrderTo;
import com.alatus.common.utils.R;
import com.alatus.common.vo.MemberRespVo;
import com.alatus.mall.seckill.constant.SecKillConstants;
import com.alatus.mall.seckill.feign.CouponFeignService;
import com.alatus.mall.seckill.feign.ProductFeignService;
import com.alatus.mall.seckill.interceptor.LoginUserInterceptor;
import com.alatus.mall.seckill.service.SecKillService;
import com.alatus.mall.seckill.to.SecKillSkuRedisTo;
import com.alatus.mall.seckill.vo.SeckillSessionEntityVo;
import com.alatus.mall.seckill.vo.SkuInfoVo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.apache.commons.lang.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Service
public class SecKillServiceImpl implements SecKillService {
    @Autowired
    private CouponFeignService couponFeignService;
    @Autowired
    private ProductFeignService productFeignService;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Override
    public void uploadSecKillSkuLatest3Days() {
//        扫描需要参与秒杀的活动
        R daySession = couponFeignService.Latest3DaySession();
        if(daySession.getCode() == 0){
            List<SeckillSessionEntityVo> seckillSessionVos = daySession.getData(new TypeReference<List<SeckillSessionEntityVo>>() {});
//            缓存活动信息
            saveSessionInfos(seckillSessionVos);
//            缓存活动关联的商品信息
            saveSkuInfos(seckillSessionVos);
        }
    }

//    返回当前事件可以参与的秒杀商品信息
    @Override
    public List<SecKillSkuRedisTo> getCurrentSeckillSkus() {
        long time = new Date().getTime();
        Set<String> keys = redisTemplate.keys(SecKillConstants.SESSION_CACHE_PREFIX + "*");
        List<Object> currentSeckillSkus = new ArrayList<>();
        if (keys != null) {
            for (String key : keys) {
                String replace = key.replace(SecKillConstants.SESSION_CACHE_PREFIX, "");
                String[] tempString = replace.split(":");
                String timeString = tempString[1];
                String[] timeSplit = timeString.split("-");
                long start = Long.parseLong(timeSplit[0]);
                long end = Long.parseLong(timeSplit[1]);
                if(time >= start && time <= end){
                    List<String> range = redisTemplate.opsForList().range(key, -100, 100);
                    BoundHashOperations<String, String, Object> operations = redisTemplate.boundHashOps(SecKillConstants.SKU_CACHE_PREFIX);
                    List<Object> strings = null;
                    if (range != null) {
                        strings = operations.multiGet(range);
                    }
                    if(strings != null && !strings.isEmpty()){
                        currentSeckillSkus.addAll(strings);
                    }
                }
            }
        }
        if(!currentSeckillSkus.isEmpty()){
//            遍历获取到的JSON集合并转换为对象,并完整添加进去方便展示出来
            return currentSeckillSkus.stream().map(item -> {
                SecKillSkuRedisTo secKillSkuRedisTo = JSON.parseObject(item.toString(), SecKillSkuRedisTo.class);
//                屏蔽掉随机码
                secKillSkuRedisTo.setRandomCode(null);
                return secKillSkuRedisTo;
            }).collect(Collectors.toList());
        }
        else{
            return null;
        }
    }

    @Override
    public SecKillSkuRedisTo getSkuSeckillInfo(Long skuId) {
//        找到所有需要参与秒杀的SKU的Key信息
        BoundHashOperations<String, String, String> operations = redisTemplate.boundHashOps(SecKillConstants.SKU_CACHE_PREFIX);
        Set<String> keys = operations.keys();
        if (keys != null && !keys.isEmpty()) {
            String regx = "\\d_" +skuId;
            for (String key : keys) {
                if(Pattern.matches(regx,key)){
                    String json = operations.get(key);
                    SecKillSkuRedisTo secKillSkuRedisTo = JSON.parseObject(json, SecKillSkuRedisTo.class);
                    if(secKillSkuRedisTo.getEndTime() > new Date().getTime()){
                        if(secKillSkuRedisTo.getStartTime() > new Date().getTime()){
                            secKillSkuRedisTo.setRandomCode(null);
                        }
                        return secKillSkuRedisTo;
                    }
                }
            }
        }
        return null;
    }

    @Override
    public String kill(String killId, String key, Integer num) {
        MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get();
//        获取当前秒杀商品的详细信息
        BoundHashOperations<String, String, String> operations = redisTemplate.boundHashOps(SecKillConstants.SKU_CACHE_PREFIX);
        String skuInfo = operations.get(killId);
//        没找到这么一个秒杀商品
        if(!StringUtils.isEmpty(skuInfo)){
            SecKillSkuRedisTo secKillSkuRedisTo = JSON.parseObject(skuInfo, SecKillSkuRedisTo.class);
            Long startTime = secKillSkuRedisTo.getStartTime();
            Long endTime = secKillSkuRedisTo.getEndTime();
            long time = new Date().getTime();
//            在活动时间范围内
            if(time >= startTime && time <= endTime){
//                拼接商品ID
                String skuId = secKillSkuRedisTo.getPromotionSessionId()+"_"+secKillSkuRedisTo.getSkuId();
//                商品随机码
                String randomCode = secKillSkuRedisTo.getRandomCode();
//                校验随机码和商品ID
                if(key.equals(randomCode) && killId.equals(skuId)){
//                    验证购物数量是否合理
                    if(secKillSkuRedisTo.getSeckillLimit().intValue() >= num){
                        String redisKey = memberRespVo.getId() + "_" + skuId;
//                        验证这个人是否已经购买过,幂等性处理,如果秒杀成功,就去redis占位
//                        而且这个占位必须让它自动过期,不然会一直占用redis的资源
                        long ttl = endTime - time;
                        Boolean check = redisTemplate.opsForValue().setIfAbsent(redisKey, num.toString(), ttl, TimeUnit.MILLISECONDS);
//                        如果这里可以占位成功说明,当前用户没有在该场次购买过该商品
                        if(check){
                            RSemaphore semaphore = redissonClient.getSemaphore(SecKillConstants.SKU_STOCK_SEMAPHORE + randomCode);
                            try {
                                boolean acquire = semaphore.tryAcquire(num, 100, TimeUnit.MILLISECONDS);
//                                秒杀成功以后,我们就可以做一个快速下单,发送一个MQ消息
                                if(acquire){
                                    String orderSn = IdWorker.getTimeId();
                                    SeckillOrderTo secKillOrderTo = new SeckillOrderTo();
                                    BeanUtils.copyProperties(secKillSkuRedisTo,secKillOrderTo);
                                    secKillOrderTo.setOrderSn(orderSn);
                                    secKillOrderTo.setMemberId(memberRespVo.getId());
                                    secKillOrderTo.setNum(num);
                                    rabbitTemplate.convertAndSend("order-event-exchange","order.seckill.order",secKillOrderTo);
//                                    TODO 发送MQ消息
                                    return orderSn;
                                }
                            } catch (InterruptedException e) {
                                return null;
                            }
                        }
                    }
                }
            }
        }
        return null;
    }

    private void saveSessionInfos(List<SeckillSessionEntityVo> seckillSessionVos) {
        seckillSessionVos.stream().forEach(session -> {
            //获取秒杀活动开始和结束时间
            long start = session.getStartTime().getTime();
            long end = session.getEndTime().getTime();
            String key = SecKillConstants.SESSION_CACHE_PREFIX+session.getName()+":"+start+"-"+end;
//            保存活动的信息
            if (!redisTemplate.hasKey(key)) {
                List<String> ids = session.getRelationSkus().stream().map(item->{
                    return item.getPromotionSessionId()+"_"+item.getSkuId().toString();
                }).collect(Collectors.toList());
                redisTemplate.opsForList().leftPushAll(key,ids);
            }
        });
    }
    private void saveSkuInfos(List<SeckillSessionEntityVo> seckillSessionVos){
        seckillSessionVos.forEach(session -> {
//            准备哈希操作
            BoundHashOperations ops = redisTemplate.boundHashOps(SecKillConstants.SKU_CACHE_PREFIX);
            session.getRelationSkus().forEach(relationSku -> {
//                生成随机码
                String token = UUID.randomUUID().toString().replace("-", "");
//                拼接Key保存商品
                String key = relationSku.getPromotionSessionId().toString()+"_"+relationSku.getSkuId().toString();
                if(!ops.hasKey(key)){
                    //                缓存商品
                    SecKillSkuRedisTo secKillSkuRedisTo = new SecKillSkuRedisTo();
//                sku的基本信息
                    R info = productFeignService.getSkuInfo(relationSku.getSkuId());
                    if(info.getCode() == 0){
                        SkuInfoVo skuInfoVo = info.get("skuInfo",new TypeReference<SkuInfoVo>(){});
                        secKillSkuRedisTo.setSkuInfo(skuInfoVo);
                    }
//                sku的秒杀信息
                    BeanUtils.copyProperties(relationSku,secKillSkuRedisTo);
//                    设置上秒杀数量啊!!
                    secKillSkuRedisTo.setSeckillCount(relationSku.getSeckillCount());
//                设置秒杀的时间
                    secKillSkuRedisTo.setStartTime(session.getStartTime().getTime());
                    secKillSkuRedisTo.setEndTime(session.getEndTime().getTime());
                    secKillSkuRedisTo.setRandomCode(token);
//                    保存商品
                    String skuJson = JSON.toJSONString(secKillSkuRedisTo);
                    ops.put(key,skuJson);
//                    如果当前这个场次的商品的库存信息已经上架,就不需要再次上架了
//                设置库存为分布式秒杀的信号量,限流
                    RSemaphore semaphore = redissonClient.getSemaphore(SecKillConstants.SKU_STOCK_SEMAPHORE + token);
//                商品可以秒杀的数量作为信号量
                    semaphore.trySetPermits(relationSku.getSeckillCount().intValue());
                }
            });
        });
    }
}
package com.alatus.mall.seckill.service.impl;

import com.alatus.common.to.mq.SeckillOrderTo;
import com.alatus.common.utils.R;
import com.alatus.common.vo.MemberRespVo;
import com.alatus.mall.seckill.constant.SecKillConstants;
import com.alatus.mall.seckill.feign.CouponFeignService;
import com.alatus.mall.seckill.feign.ProductFeignService;
import com.alatus.mall.seckill.interceptor.LoginUserInterceptor;
import com.alatus.mall.seckill.service.SecKillService;
import com.alatus.mall.seckill.to.SecKillSkuRedisTo;
import com.alatus.mall.seckill.vo.SeckillSessionEntityVo;
import com.alatus.mall.seckill.vo.SkuInfoVo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.apache.commons.lang.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Service
public class SecKillServiceImpl implements SecKillService {
    @Autowired
    private CouponFeignService couponFeignService;
    @Autowired
    private ProductFeignService productFeignService;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Override
    public void uploadSecKillSkuLatest3Days() {
//        扫描需要参与秒杀的活动
        R daySession = couponFeignService.Latest3DaySession();
        if(daySession.getCode() == 0){
            List<SeckillSessionEntityVo> seckillSessionVos = daySession.getData(new TypeReference<List<SeckillSessionEntityVo>>() {});
//            缓存活动信息
            saveSessionInfos(seckillSessionVos);
//            缓存活动关联的商品信息
            saveSkuInfos(seckillSessionVos);
        }
    }

//    返回当前事件可以参与的秒杀商品信息
    @Override
    public List<SecKillSkuRedisTo> getCurrentSeckillSkus() {
        long time = new Date().getTime();
        Set<String> keys = redisTemplate.keys(SecKillConstants.SESSION_CACHE_PREFIX + "*");
        List<Object> currentSeckillSkus = new ArrayList<>();
        if (keys != null) {
            for (String key : keys) {
                String replace = key.replace(SecKillConstants.SESSION_CACHE_PREFIX, "");
                String[] tempString = replace.split(":");
                String timeString = tempString[1];
                String[] timeSplit = timeString.split("-");
                long start = Long.parseLong(timeSplit[0]);
                long end = Long.parseLong(timeSplit[1]);
                if(time >= start && time <= end){
                    List<String> range = redisTemplate.opsForList().range(key, -100, 100);
                    BoundHashOperations<String, String, Object> operations = redisTemplate.boundHashOps(SecKillConstants.SKU_CACHE_PREFIX);
                    List<Object> strings = null;
                    if (range != null) {
                        strings = operations.multiGet(range);
                    }
                    if(strings != null && !strings.isEmpty()){
                        currentSeckillSkus.addAll(strings);
                    }
                }
            }
        }
        if(!currentSeckillSkus.isEmpty()){
//            遍历获取到的JSON集合并转换为对象,并完整添加进去方便展示出来
            return currentSeckillSkus.stream().map(item -> {
                SecKillSkuRedisTo secKillSkuRedisTo = JSON.parseObject(item.toString(), SecKillSkuRedisTo.class);
//                屏蔽掉随机码
                secKillSkuRedisTo.setRandomCode(null);
                return secKillSkuRedisTo;
            }).collect(Collectors.toList());
        }
        else{
            return null;
        }
    }

    @Override
    public SecKillSkuRedisTo getSkuSeckillInfo(Long skuId) {
//        找到所有需要参与秒杀的SKU的Key信息
        BoundHashOperations<String, String, String> operations = redisTemplate.boundHashOps(SecKillConstants.SKU_CACHE_PREFIX);
        Set<String> keys = operations.keys();
        if (keys != null && !keys.isEmpty()) {
            String regx = "\\d_" +skuId;
            for (String key : keys) {
                if(Pattern.matches(regx,key)){
                    String json = operations.get(key);
                    SecKillSkuRedisTo secKillSkuRedisTo = JSON.parseObject(json, SecKillSkuRedisTo.class);
                    if(secKillSkuRedisTo.getEndTime() > new Date().getTime()){
                        if(secKillSkuRedisTo.getStartTime() > new Date().getTime()){
                            secKillSkuRedisTo.setRandomCode(null);
                        }
                        return secKillSkuRedisTo;
                    }
                }
            }
        }
        return null;
    }

    @Override
    public String kill(String killId, String key, Integer num) {
        MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get();
//        获取当前秒杀商品的详细信息
        BoundHashOperations<String, String, String> operations = redisTemplate.boundHashOps(SecKillConstants.SKU_CACHE_PREFIX);
        String skuInfo = operations.get(killId);
//        没找到这么一个秒杀商品
        if(!StringUtils.isEmpty(skuInfo)){
            SecKillSkuRedisTo secKillSkuRedisTo = JSON.parseObject(skuInfo, SecKillSkuRedisTo.class);
            Long startTime = secKillSkuRedisTo.getStartTime();
            Long endTime = secKillSkuRedisTo.getEndTime();
            long time = new Date().getTime();
//            在活动时间范围内
            if(time >= startTime && time <= endTime){
//                拼接商品ID
                String skuId = secKillSkuRedisTo.getPromotionSessionId()+"_"+secKillSkuRedisTo.getSkuId();
//                商品随机码
                String randomCode = secKillSkuRedisTo.getRandomCode();
//                校验随机码和商品ID
                if(key.equals(randomCode) && killId.equals(skuId)){
//                    验证购物数量是否合理
                    if(secKillSkuRedisTo.getSeckillLimit().intValue() >= num){
                        String redisKey = memberRespVo.getId() + "_" + skuId;
//                        验证这个人是否已经购买过,幂等性处理,如果秒杀成功,就去redis占位
//                        而且这个占位必须让它自动过期,不然会一直占用redis的资源
                        long ttl = endTime - time;
                        Boolean check = redisTemplate.opsForValue().setIfAbsent(redisKey, num.toString(), ttl, TimeUnit.MILLISECONDS);
//                        如果这里可以占位成功说明,当前用户没有在该场次购买过该商品
                        if(check){
                            RSemaphore semaphore = redissonClient.getSemaphore(SecKillConstants.SKU_STOCK_SEMAPHORE + randomCode);
                            try {
                                boolean acquire = semaphore.tryAcquire(num, 100, TimeUnit.MILLISECONDS);
//                                秒杀成功以后,我们就可以做一个快速下单,发送一个MQ消息
                                if(acquire){
                                    String orderSn = IdWorker.getTimeId();
                                    SeckillOrderTo secKillOrderTo = new SeckillOrderTo();
                                    BeanUtils.copyProperties(secKillSkuRedisTo,secKillOrderTo);
                                    secKillOrderTo.setOrderSn(orderSn);
                                    secKillOrderTo.setMemberId(memberRespVo.getId());
                                    secKillOrderTo.setNum(num);
                                    rabbitTemplate.convertAndSend("order-event-exchange","order.seckill.order",secKillOrderTo);
//                                    TODO 发送MQ消息
                                    return orderSn;
                                }
                            } catch (InterruptedException e) {
                                return null;
                            }
                        }
                    }
                }
            }
        }
        return null;
    }

    private void saveSessionInfos(List<SeckillSessionEntityVo> seckillSessionVos) {
        seckillSessionVos.stream().forEach(session -> {
            //获取秒杀活动开始和结束时间
            long start = session.getStartTime().getTime();
            long end = session.getEndTime().getTime();
            String key = SecKillConstants.SESSION_CACHE_PREFIX+session.getName()+":"+start+"-"+end;
//            保存活动的信息
            if (!redisTemplate.hasKey(key)) {
                List<String> ids = session.getRelationSkus().stream().map(item->{
                    return item.getPromotionSessionId()+"_"+item.getSkuId().toString();
                }).collect(Collectors.toList());
                redisTemplate.opsForList().leftPushAll(key,ids);
            }
        });
    }
    private void saveSkuInfos(List<SeckillSessionEntityVo> seckillSessionVos){
        seckillSessionVos.forEach(session -> {
//            准备哈希操作
            BoundHashOperations ops = redisTemplate.boundHashOps(SecKillConstants.SKU_CACHE_PREFIX);
            session.getRelationSkus().forEach(relationSku -> {
//                生成随机码
                String token = UUID.randomUUID().toString().replace("-", "");
//                拼接Key保存商品
                String key = relationSku.getPromotionSessionId().toString()+"_"+relationSku.getSkuId().toString();
                if(!ops.hasKey(key)){
                    //                缓存商品
                    SecKillSkuRedisTo secKillSkuRedisTo = new SecKillSkuRedisTo();
//                sku的基本信息
                    R info = productFeignService.getSkuInfo(relationSku.getSkuId());
                    if(info.getCode() == 0){
                        SkuInfoVo skuInfoVo = info.get("skuInfo",new TypeReference<SkuInfoVo>(){});
                        secKillSkuRedisTo.setSkuInfo(skuInfoVo);
                    }
//                sku的秒杀信息
                    BeanUtils.copyProperties(relationSku,secKillSkuRedisTo);
//                    设置上秒杀数量啊!!
                    secKillSkuRedisTo.setSeckillCount(relationSku.getSeckillCount());
//                设置秒杀的时间
                    secKillSkuRedisTo.setStartTime(session.getStartTime().getTime());
                    secKillSkuRedisTo.setEndTime(session.getEndTime().getTime());
                    secKillSkuRedisTo.setRandomCode(token);
//                    保存商品
                    String skuJson = JSON.toJSONString(secKillSkuRedisTo);
                    ops.put(key,skuJson);
//                    如果当前这个场次的商品的库存信息已经上架,就不需要再次上架了
//                设置库存为分布式秒杀的信号量,限流
                    RSemaphore semaphore = redissonClient.getSemaphore(SecKillConstants.SKU_STOCK_SEMAPHORE + token);
//                商品可以秒杀的数量作为信号量
                    semaphore.trySetPermits(relationSku.getSeckillCount().intValue());
                }
            });
        });
    }
}
package com.alatus.mall.member.config;

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.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;

@Configuration
public class SessionConfig {
    @Bean
    public CookieSerializer cookieSerializer(){
        DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
        cookieSerializer.setDomainName("alatusmall.com");
        cookieSerializer.setCookieName("MALLSESSION");
        return cookieSerializer;
    }
    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer(){
        return new GenericJackson2JsonRedisSerializer();
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        template.setDefaultSerializer(springSessionDefaultRedisSerializer());
        return template;
    }
}
package com.alatus.mall.member.config;

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.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;

@Configuration
public class SessionConfig {
    @Bean
    public CookieSerializer cookieSerializer(){
        DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
        cookieSerializer.setDomainName("alatusmall.com");
        cookieSerializer.setCookieName("MALLSESSION");
        return cookieSerializer;
    }
    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer(){
        return new GenericJackson2JsonRedisSerializer();
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        template.setDefaultSerializer(springSessionDefaultRedisSerializer());
        return template;
    }
}
package com.alatus.mall.seckill.scheduled;

import com.alatus.mall.seckill.service.SecKillService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * 秒杀商品的定时上架
 * 每天晚上凌晨4点,上架最新三天秒杀商品
 * 重复上架无需处理
 */
@Service
@Slf4j
public class SecKillSkuScheduled {
    @Autowired
    private SecKillService secKillService;
    @Autowired
    private RedissonClient redissonClient;
    private final String upload_lock = "seckill:upload:lock";
    @Scheduled(cron = "0 0 4 * * ?")
//    TODO 幂等性处理
//    @Scheduled(cron = "*/3 * * * * ?")
    public void uploadSecKillSkuLatest3Days() {
        log.info("上架秒杀的商品信息...");
        RLock rLock = redissonClient.getLock(upload_lock);
        rLock.lock(30, TimeUnit.SECONDS);
        try{
            secKillService.uploadSecKillSkuLatest3Days();
        }finally {
            rLock.unlock();
        }
    }
}

package com.alatus.mall.seckill.scheduled;

import com.alatus.mall.seckill.service.SecKillService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * 秒杀商品的定时上架
 * 每天晚上凌晨4点,上架最新三天秒杀商品
 * 重复上架无需处理
 */
@Service
@Slf4j
public class SecKillSkuScheduled {
    @Autowired
    private SecKillService secKillService;
    @Autowired
    private RedissonClient redissonClient;
    private final String upload_lock = "seckill:upload:lock";
    @Scheduled(cron = "0 0 4 * * ?")
//    TODO 幂等性处理
//    @Scheduled(cron = "*/3 * * * * ?")
    public void uploadSecKillSkuLatest3Days() {
        log.info("上架秒杀的商品信息...");
        RLock rLock = redissonClient.getLock(upload_lock);
        rLock.lock(30, TimeUnit.SECONDS);
        try{
            secKillService.uploadSecKillSkuLatest3Days();
        }finally {
            rLock.unlock();
        }
    }
}
package com.alatus.mall.order.config;

import com.alatus.common.constant.RabbitConstant;
import org.springframework.amqp.core.*;
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 java.util.HashMap;
import java.util.Map;

@Configuration
public class RabbitConfig {
    @Autowired
    private RabbitMQConfigProperties rabbitMQConfigProperties;
    @Bean
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }
//    容器中的Binding,Queue,Exchange都会自动创建,前提是rabbitmq没有
    @Bean
    public Queue orderDelayQueue(){
        Map<String,Object> arguments = new HashMap<>();
        arguments.put(RabbitConstant.X_DEAD_LETTER_EXCHANGE,rabbitMQConfigProperties.getExchange());
        arguments.put(RabbitConstant.X_DEAD_LETTER_ROUTING_KEY,rabbitMQConfigProperties.getReleaseKey());
        arguments.put(RabbitConstant.X_MESSAGE_TTL,rabbitMQConfigProperties.getMessageTTL());
        return new Queue(rabbitMQConfigProperties.getDelayQueue(), true, false, false,arguments);
    }
    @Bean
    public Queue orderReleaseQueue(){
        return new Queue(rabbitMQConfigProperties.getReleaseQueue(), true, false, false);
    }
    @Bean
    public Queue orderSeckillQueue(){
        return new Queue(rabbitMQConfigProperties.getSeckillQueue(), true, false, false);
    }
    @Bean
    public Exchange orderEventExchange(){
        return new TopicExchange(rabbitMQConfigProperties.getExchange(),true,false);
    }
    @Bean
    public Binding orderSeckill(){
        return new Binding(rabbitMQConfigProperties.getSeckillQueue(), Binding.DestinationType.QUEUE,
                rabbitMQConfigProperties.getExchange(), rabbitMQConfigProperties.getSeckillKey(),null);
    }
    @Bean
    public Binding orderCreate(){
        return new Binding(rabbitMQConfigProperties.getDelayQueue(), Binding.DestinationType.QUEUE,
                rabbitMQConfigProperties.getExchange(), rabbitMQConfigProperties.getCreateKey(),null);
    }
    @Bean
    public Binding orderRelease(){
        return new Binding(rabbitMQConfigProperties.getReleaseQueue(), Binding.DestinationType.QUEUE,
                rabbitMQConfigProperties.getExchange(),rabbitMQConfigProperties.getReleaseKey(),null);
    }
//    订单和库存绑定
    @Bean
    public Binding orderReleaseOther(){
        return new Binding(rabbitMQConfigProperties.getStockReleaseQueue(),Binding.DestinationType.QUEUE,
                rabbitMQConfigProperties.getExchange(),rabbitMQConfigProperties.getStockReleaseKey(),null);
    }
}

package com.alatus.mall.order.config;

import com.alatus.common.constant.RabbitConstant;
import org.springframework.amqp.core.*;
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 java.util.HashMap;
import java.util.Map;

@Configuration
public class RabbitConfig {
    @Autowired
    private RabbitMQConfigProperties rabbitMQConfigProperties;
    @Bean
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }
//    容器中的Binding,Queue,Exchange都会自动创建,前提是rabbitmq没有
    @Bean
    public Queue orderDelayQueue(){
        Map<String,Object> arguments = new HashMap<>();
        arguments.put(RabbitConstant.X_DEAD_LETTER_EXCHANGE,rabbitMQConfigProperties.getExchange());
        arguments.put(RabbitConstant.X_DEAD_LETTER_ROUTING_KEY,rabbitMQConfigProperties.getReleaseKey());
        arguments.put(RabbitConstant.X_MESSAGE_TTL,rabbitMQConfigProperties.getMessageTTL());
        return new Queue(rabbitMQConfigProperties.getDelayQueue(), true, false, false,arguments);
    }
    @Bean
    public Queue orderReleaseQueue(){
        return new Queue(rabbitMQConfigProperties.getReleaseQueue(), true, false, false);
    }
    @Bean
    public Queue orderSeckillQueue(){
        return new Queue(rabbitMQConfigProperties.getSeckillQueue(), true, false, false);
    }
    @Bean
    public Exchange orderEventExchange(){
        return new TopicExchange(rabbitMQConfigProperties.getExchange(),true,false);
    }
    @Bean
    public Binding orderSeckill(){
        return new Binding(rabbitMQConfigProperties.getSeckillQueue(), Binding.DestinationType.QUEUE,
                rabbitMQConfigProperties.getExchange(), rabbitMQConfigProperties.getSeckillKey(),null);
    }
    @Bean
    public Binding orderCreate(){
        return new Binding(rabbitMQConfigProperties.getDelayQueue(), Binding.DestinationType.QUEUE,
                rabbitMQConfigProperties.getExchange(), rabbitMQConfigProperties.getCreateKey(),null);
    }
    @Bean
    public Binding orderRelease(){
        return new Binding(rabbitMQConfigProperties.getReleaseQueue(), Binding.DestinationType.QUEUE,
                rabbitMQConfigProperties.getExchange(),rabbitMQConfigProperties.getReleaseKey(),null);
    }
//    订单和库存绑定
    @Bean
    public Binding orderReleaseOther(){
        return new Binding(rabbitMQConfigProperties.getStockReleaseQueue(),Binding.DestinationType.QUEUE,
                rabbitMQConfigProperties.getExchange(),rabbitMQConfigProperties.getStockReleaseKey(),null);
    }
}
/**
 * Copyright (c) 2016-2019 人人开源 All rights reserved.
 *
 * https://www.renren.io
 *
 * 版权所有,侵权必究!
 */

package io.renren.config;

import org.springframework.beans.factory.annotation.Autowired;
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.*;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * Redis配置
 *
 * @author Mark sunlightcs@gmail.com
 */
@Configuration
public class RedisConfig {
    @Autowired
    private RedisConnectionFactory factory;

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();

        // 设置 key 和 value 的序列化器为 JSON
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());

        // 使用 GenericJackson2JsonRedisSerializer 序列化器来将数据以 JSON 格式存储
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        redisTemplate.setConnectionFactory(factory);
        return redisTemplate;
    }

    @Bean
    public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForHash();
    }

    @Bean
    public ValueOperations<String, String> valueOperations(RedisTemplate<String, String> redisTemplate) {
        return redisTemplate.opsForValue();
    }

    @Bean
    public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForList();
    }

    @Bean
    public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForSet();
    }

    @Bean
    public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForZSet();
    }
}

/**
 * Copyright (c) 2016-2019 人人开源 All rights reserved.
 *
 * https://www.renren.io
 *
 * 版权所有,侵权必究!
 */

package io.renren.config;

import org.springframework.beans.factory.annotation.Autowired;
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.*;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * Redis配置
 *
 * @author Mark sunlightcs@gmail.com
 */
@Configuration
public class RedisConfig {
    @Autowired
    private RedisConnectionFactory factory;

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();

        // 设置 key 和 value 的序列化器为 JSON
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());

        // 使用 GenericJackson2JsonRedisSerializer 序列化器来将数据以 JSON 格式存储
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        redisTemplate.setConnectionFactory(factory);
        return redisTemplate;
    }

    @Bean
    public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForHash();
    }

    @Bean
    public ValueOperations<String, String> valueOperations(RedisTemplate<String, String> redisTemplate) {
        return redisTemplate.opsForValue();
    }

    @Bean
    public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForList();
    }

    @Bean
    public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForSet();
    }

    @Bean
    public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForZSet();
    }
}
package com.alatus.mall.order.listener;

import com.alatus.common.to.mq.SeckillOrderTo;
import com.alatus.mall.order.service.OrderService;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
@Slf4j
@RabbitListener(queues = "order.seckill.order.queue")
public class OrderSeckillListener {
    @Autowired
    private OrderService orderService;
    @RabbitHandler
    public void listener(SeckillOrderTo seckillOrderTo, Channel channel, Message message) throws IOException {
        try{
            log.info("收到秒杀单的订单信息,准备创建订单,订单号:{}",seckillOrderTo.getOrderSn());
            orderService.createSeckillOrder(seckillOrderTo);
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        }
        catch (Exception e){
            channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);
        }
    }
}

package com.alatus.mall.order.listener;

import com.alatus.common.to.mq.SeckillOrderTo;
import com.alatus.mall.order.service.OrderService;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
@Slf4j
@RabbitListener(queues = "order.seckill.order.queue")
public class OrderSeckillListener {
    @Autowired
    private OrderService orderService;
    @RabbitHandler
    public void listener(SeckillOrderTo seckillOrderTo, Channel channel, Message message) throws IOException {
        try{
            log.info("收到秒杀单的订单信息,准备创建订单,订单号:{}",seckillOrderTo.getOrderSn());
            orderService.createSeckillOrder(seckillOrderTo);
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        }
        catch (Exception e){
            channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);
        }
    }
}
package com.alatus.mall.coupon.entity;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.math.BigDecimal;
import java.io.Serializable;
import lombok.Data;

/**
 * 秒杀活动商品关联
 * 
 * @author alatus
 * @date 2024-03-12 13:32:54
 */
@Data
@TableName("sms_seckill_sku_relation")
public class SeckillSkuRelationEntity implements Serializable {
	private static final long serialVersionUID = 1L;

	/**
	 * id
	 */
	@TableId
	private Long id;
	/**
	 * 活动id
	 */
	private Long promotionId;
	/**
	 * 活动场次id
	 */
	private Long promotionSessionId;
	/**
	 * 商品id
	 */
	private Long skuId;
	/**
	 * 秒杀价格
	 */
	private BigDecimal seckillPrice;
	/**
	 * 秒杀总量
	 */
	private BigDecimal seckillCount;
	/**
	 * 每人限购数量
	 */
	private BigDecimal seckillLimit;
	/**
	 * 排序
	 */
	private Integer seckillSort;

}

package com.alatus.mall.coupon.entity;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.math.BigDecimal;
import java.io.Serializable;
import lombok.Data;

/**
 * 秒杀活动商品关联
 * 
 * @author alatus
 * @date 2024-03-12 13:32:54
 */
@Data
@TableName("sms_seckill_sku_relation")
public class SeckillSkuRelationEntity implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * id
     */
    @TableId
    private Long id;
    /**
     * 活动id
     */
    private Long promotionId;
    /**
     * 活动场次id
     */
    private Long promotionSessionId;
    /**
     * 商品id
     */
    private Long skuId;
    /**
     * 秒杀价格
     */
    private BigDecimal seckillPrice;
    /**
     * 秒杀总量
     */
    private BigDecimal seckillCount;
    /**
     * 每人限购数量
     */
    private BigDecimal seckillLimit;
    /**
     * 排序
     */
    private Integer seckillSort;

}
package com.alatus.mall.seckill.to;

import com.alatus.mall.seckill.vo.SkuInfoVo;
import lombok.Data;

import java.math.BigDecimal;

@Data
public class SecKillSkuRedisTo {
    /**
     * 活动id
     */
    private Long promotionId;
    /**
     * 活动场次id
     */
    private Long promotionSessionId;
    /**
     * 商品id
     */
    private Long skuId;
    /**
     * 秒杀价格
     */
    private BigDecimal seckillPrice;
    /**
     * 秒杀总量
     */
    private BigDecimal seckillCount;
    /**
     * 每人限购数量
     */
    private BigDecimal seckillLimit;
    /**
     * 排序
     */
    private Integer seckillSort;

    /**
     * 商品详细信息
     */
    private SkuInfoVo skuInfo;
    /**
     * 秒杀开始时间
     */
    private Long startTime;
    /**
     * 秒杀结束时间
     */
    private Long endTime;
    /**
     * 秒杀随机码
     */
    private String randomCode;
}

package com.alatus.mall.seckill.to;

import com.alatus.mall.seckill.vo.SkuInfoVo;
import lombok.Data;

import java.math.BigDecimal;

@Data
public class SecKillSkuRedisTo {
    /**
     * 活动id
     */
    private Long promotionId;
    /**
     * 活动场次id
     */
    private Long promotionSessionId;
    /**
     * 商品id
     */
    private Long skuId;
    /**
     * 秒杀价格
     */
    private BigDecimal seckillPrice;
    /**
     * 秒杀总量
     */
    private BigDecimal seckillCount;
    /**
     * 每人限购数量
     */
    private BigDecimal seckillLimit;
    /**
     * 排序
     */
    private Integer seckillSort;

    /**
     * 商品详细信息
     */
    private SkuInfoVo skuInfo;
    /**
     * 秒杀开始时间
     */
    private Long startTime;
    /**
     * 秒杀结束时间
     */
    private Long endTime;
    /**
     * 秒杀随机码
     */
    private String randomCode;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值