商城项目秒杀业务秒杀业务完善和网关配置路由转发----商城项目

package com.alatus.mall.seckill.app;

import com.alatus.common.utils.R;
import com.alatus.mall.seckill.service.SecKillService;
import com.alatus.mall.seckill.to.SecKillSkuRedisTo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class SeckillController {
    @Autowired
    private SecKillService secKillService;
    /**
     * 获取当前时间可以参与秒杀的商品信息
     * @return
     */
    @GetMapping("/getCurrentSeckillSkus")
    public R getCurrentSeckillSkus() {
        List secKillSkuRedisToList = secKillService.getCurrentSeckillSkus();
        return R.ok().put("data",secKillSkuRedisToList);
    }
}
package com.alatus.mall.seckill.app;

import com.alatus.common.utils.R;
import com.alatus.mall.seckill.service.SecKillService;
import com.alatus.mall.seckill.to.SecKillSkuRedisTo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class SeckillController {
    @Autowired
    private SecKillService secKillService;
    /**
     * 获取当前时间可以参与秒杀的商品信息
     * @return
     */
    @GetMapping("/getCurrentSeckillSkus")
    public R getCurrentSeckillSkus() {
        List secKillSkuRedisToList = secKillService.getCurrentSeckillSkus();
        return R.ok().put("data",secKillSkuRedisToList);
    }
}
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();
        }
    }
}
server:
  port: 88
spring:
  redis:
    port: 6379
    host: 192.168.56.10
  cloud:
    gateway:
      routes:
        #          前端项目都带API前缀
        - id: product_route
          uri: lb://Mall-product
          predicates:
            - Path=/api/product/**
          filters:
            - RewritePath=/api/(?/?.*),/$\{segment}
        - id: coupon_route
          uri: lb://Mall-coupon
          predicates:
            - Path=/api/coupon/**
          filters:
            - RewritePath=/api/(?/?.*),/$\{segment}
        - id: ware_route
          uri: lb://Mall-ware
          predicates:
            - Path=/api/ware/**
          filters:
            - RewritePath=/api/(?/?.*),/$\{segment}
        - id: member_route
          uri: lb://Mall-member
          predicates:
            - Path=/api/member/**
          filters:
            - RewritePath=/api/(?/?.*),/$\{segment}
        - id: third_party_route
          uri: lb://Mall-third-party
          predicates:
            - Path=/api/thirdparty/**
          filters:
            - RewritePath=/api/thirdparty/(?/?.*), /$\{segment}
        - id: admin_route
          uri: lb://Mall-admin
          predicates:
            - Path=/api/**
          filters:
            - RewritePath=/api/(?/?.*), /Mall-admin/$\{segment}
        - id: alatusmall_host_route
          uri: lb://Mall-product
          predicates:
            - Host=alatusmall.com,item.alatusmall.com
        - id: alatusmall_search_route
          uri: lb://Mall-search
          predicates:
            - Host=search.alatusmall.com
        - id: alatusmall_auth_route
          uri: lb://Mall-auth-server
          predicates:
            - Host=auth.alatusmall.com
        - id: alatusmall_cart_route
          uri: lb://Mall-cart
          predicates:
            - Host=cart.alatusmall.com
        - id: alatusmall_order_route
          uri: lb://Mall-order
          predicates:
            - Host=order.alatusmall.com
        - id: alatusmall_member_route
          uri: lb://Mall-member
          predicates:
            - Host=member.alatusmall.com
        - id: alatusmall_seckill_route
          uri: lb://Mall-seckill
          predicates:
            - Host=seckill.alatusmall.com

 

server:
  port: 88
spring:
  redis:
    port: 6379
    host: 192.168.56.10
  cloud:
    gateway:
      routes:
        #          前端项目都带API前缀
        - id: product_route
          uri: lb://Mall-product
          predicates:
            - Path=/api/product/**
          filters:
            - RewritePath=/api/(?/?.*),/$\{segment}
        - id: coupon_route
          uri: lb://Mall-coupon
          predicates:
            - Path=/api/coupon/**
          filters:
            - RewritePath=/api/(?/?.*),/$\{segment}
        - id: ware_route
          uri: lb://Mall-ware
          predicates:
            - Path=/api/ware/**
          filters:
            - RewritePath=/api/(?/?.*),/$\{segment}
        - id: member_route
          uri: lb://Mall-member
          predicates:
            - Path=/api/member/**
          filters:
            - RewritePath=/api/(?/?.*),/$\{segment}
        - id: third_party_route
          uri: lb://Mall-third-party
          predicates:
            - Path=/api/thirdparty/**
          filters:
            - RewritePath=/api/thirdparty/(?/?.*), /$\{segment}
        - id: admin_route
          uri: lb://Mall-admin
          predicates:
            - Path=/api/**
          filters:
            - RewritePath=/api/(?/?.*), /Mall-admin/$\{segment}
        - id: alatusmall_host_route
          uri: lb://Mall-product
          predicates:
            - Host=alatusmall.com,item.alatusmall.com
        - id: alatusmall_search_route
          uri: lb://Mall-search
          predicates:
            - Host=search.alatusmall.com
        - id: alatusmall_auth_route
          uri: lb://Mall-auth-server
          predicates:
            - Host=auth.alatusmall.com
        - id: alatusmall_cart_route
          uri: lb://Mall-cart
          predicates:
            - Host=cart.alatusmall.com
        - id: alatusmall_order_route
          uri: lb://Mall-order
          predicates:
            - Host=order.alatusmall.com
        - id: alatusmall_member_route
          uri: lb://Mall-member
          predicates:
            - Host=member.alatusmall.com
        - id: alatusmall_seckill_route
          uri: lb://Mall-seckill
          predicates:
            - Host=seckill.alatusmall.com
package com.alatus.mall.seckill.service;


import com.alatus.mall.seckill.to.SecKillSkuRedisTo;

import java.util.List;

public interface SecKillService {
    void uploadSecKillSkuLatest3Days();

    List getCurrentSeckillSkus();
}

 

package com.alatus.mall.seckill.service;


import com.alatus.mall.seckill.to.SecKillSkuRedisTo;

import java.util.List;

public interface SecKillService {
    void uploadSecKillSkuLatest3Days();

    List getCurrentSeckillSkus();
}
package com.alatus.mall.seckill.service.impl;

import com.alatus.common.utils.R;
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.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.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
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.math.BigDecimal;
import java.util.*;
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;
    @Override
    public void uploadSecKillSkuLatest3Days() {
//        扫描需要参与秒杀的活动
        R daySession = couponFeignService.Latest3DaySession();
        if(daySession.getCode() == 0){
            List seckillSessionVos = daySession.getData(new TypeReference>() {});
//            缓存活动信息
            saveSessionInfos(seckillSessionVos);
//            缓存活动关联的商品信息
            saveSkuInfos(seckillSessionVos);
        }
    }

//    返回当前事件可以参与的秒杀商品信息
    @Override
    public List getCurrentSeckillSkus() {
//        确定当前时间属于哪个秒杀场次
        long time = new Date().getTime();
        Set keys = redisTemplate.keys(SecKillConstants.SESSION_CACHE_PREFIX + "*");
        List secKillSkuRedisToList = new ArrayList<>();
        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 range = redisTemplate.opsForList().range(key, 0, -1);
                BoundHashOperations operations = redisTemplate.boundHashOps(SecKillConstants.SKU_CACHE_PREFIX);
                List skus = operations.multiGet(range);
                if(!skus.isEmpty()){
//                    遍历获取到的JSON集合并转换为对象,并完整添加进去方便展示出来
                    secKillSkuRedisToList.addAll(skus.stream().map(obj -> {
                        SecKillSkuRedisTo secKillSkuRedisTo = JSON.parseObject(obj.toString(), SecKillSkuRedisTo.class);
//                        屏蔽掉随机码
                        secKillSkuRedisTo.setRandomCode(null);
                        return secKillSkuRedisTo;
                    }).collect(Collectors.toList()));
                }
            }
        }
//        获取这个秒杀场次所需要的所有商品信息
        return secKillSkuRedisToList;
    }

    private void saveSessionInfos(List 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 ids = session.getRelationSkus().stream().map(item->{
                    return item.getPromotionSessionId()+"_"+item.getSkuId().toString();
                }).collect(Collectors.toList());
                redisTemplate.opsForList().leftPushAll(key,ids);
            }
        });
    }
    private void saveSkuInfos(List 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(){});
                        secKillSkuRedisTo.setSkuInfo(skuInfoVo);
                    }
//                sku的秒杀信息
                    BeanUtils.copyProperties(relationSku,secKillSkuRedisTo);
//                    设置上秒杀数量啊!!
                    secKillSkuRedisTo.setSeckillCount(BigDecimal.valueOf(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());
                }
            });
        });
    }
}
 
   

 

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

import com.alatus.common.utils.R;
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.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.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
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.math.BigDecimal;
import java.util.*;
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;
    @Override
    public void uploadSecKillSkuLatest3Days() {
//        扫描需要参与秒杀的活动
        R daySession = couponFeignService.Latest3DaySession();
        if(daySession.getCode() == 0){
            List seckillSessionVos = daySession.getData(new TypeReference>() {});
//            缓存活动信息
            saveSessionInfos(seckillSessionVos);
//            缓存活动关联的商品信息
            saveSkuInfos(seckillSessionVos);
        }
    }

//    返回当前事件可以参与的秒杀商品信息
    @Override
    public List getCurrentSeckillSkus() {
//        确定当前时间属于哪个秒杀场次
        long time = new Date().getTime();
        Set keys = redisTemplate.keys(SecKillConstants.SESSION_CACHE_PREFIX + "*");
        List secKillSkuRedisToList = new ArrayList<>();
        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 range = redisTemplate.opsForList().range(key, 0, -1);
                BoundHashOperations operations = redisTemplate.boundHashOps(SecKillConstants.SKU_CACHE_PREFIX);
                List skus = operations.multiGet(range);
                if(!skus.isEmpty()){
//                    遍历获取到的JSON集合并转换为对象,并完整添加进去方便展示出来
                    secKillSkuRedisToList.addAll(skus.stream().map(obj -> {
                        SecKillSkuRedisTo secKillSkuRedisTo = JSON.parseObject(obj.toString(), SecKillSkuRedisTo.class);
//                        屏蔽掉随机码
                        secKillSkuRedisTo.setRandomCode(null);
                        return secKillSkuRedisTo;
                    }).collect(Collectors.toList()));
                }
            }
        }
//        获取这个秒杀场次所需要的所有商品信息
        return secKillSkuRedisToList;
    }

    private void saveSessionInfos(List 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 ids = session.getRelationSkus().stream().map(item->{
                    return item.getPromotionSessionId()+"_"+item.getSkuId().toString();
                }).collect(Collectors.toList());
                redisTemplate.opsForList().leftPushAll(key,ids);
            }
        });
    }
    private void saveSkuInfos(List 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(){});
                        secKillSkuRedisTo.setSkuInfo(skuInfoVo);
                    }
//                sku的秒杀信息
                    BeanUtils.copyProperties(relationSku,secKillSkuRedisTo);
//                    设置上秒杀数量啊!!
                    secKillSkuRedisTo.setSeckillCount(BigDecimal.valueOf(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());
                }
            });
        });
    }
}
 
   
  
package com.alatus.mall.seckill.vo;

import lombok.Data;
import java.math.BigDecimal;

@Data
public class SkuInfoVo {
    private Long skuId;
    /**
     * spuId
     */
    private Long spuId;
    /**
     * sku名称
     */
    private String skuName;
    /**
     * sku介绍描述
     */
    private String skuDesc;
    /**
     * 所属分类id
     */
    private Long catalogId;
    /**
     * 品牌id
     */
    private Long brandId;
    /**
     * 默认图片
     */
    private String skuDefaultImg;
    /**
     * 标题
     */
    private String skuTitle;
    /**
     * 副标题
     */
    private String skuSubtitle;
    /**
     * 价格
     */
    private BigDecimal price;
    /**
     * 销量
     */
    private Long saleCount;
}

 

package com.alatus.mall.seckill.vo;

import lombok.Data;
import java.math.BigDecimal;

@Data
public class SkuInfoVo {
    private Long skuId;
    /**
     * spuId
     */
    private Long spuId;
    /**
     * sku名称
     */
    private String skuName;
    /**
     * sku介绍描述
     */
    private String skuDesc;
    /**
     * 所属分类id
     */
    private Long catalogId;
    /**
     * 品牌id
     */
    private Long brandId;
    /**
     * 默认图片
     */
    private String skuDefaultImg;
    /**
     * 标题
     */
    private String skuTitle;
    /**
     * 副标题
     */
    private String skuSubtitle;
    /**
     * 价格
     */
    private BigDecimal price;
    /**
     * 销量
     */
    private Long saleCount;
}

你可能感兴趣的:(电商项目,#,Spring-Cloud框架,#,Spring-Boot框架,spring,boot,分布式,spring,spring,cloud,后端,mybatis,微服务)