欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

优惠券系统:优惠券分发微服务功能编码实现

程序员文章站 2024-01-19 15:31:46
...

保存无效的优惠券数据到缓存中:

package com.zcw.coupon.service.impl;

import com.alibaba.fastjson.JSON;
import com.zcw.coupon.constant.Constant;
import com.zcw.coupon.constant.CouponStatus;
import com.zcw.coupon.entity.Coupon;
import com.zcw.coupon.exception.CouponException;
import com.zcw.coupon.service.IRedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Classname RedisServiceImpl
 * @Description Redis 相关的操作服务接口实现
 * @Date 2020/3/10 17:09
 * @Created by zhaocunwei
 */
@Slf4j
@Service
public class RedisServiceImpl implements IRedisService {

    private final StringRedisTemplate redisTemplate;
    @Autowired
    public RedisServiceImpl(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public List<Coupon> getCachedCoupons(Long userId, Integer status) {
        log.info("Get Coupons From Cache:{},{}",userId,status);
        String redisKey = status2RedosKey(status,userId);
        List<String> couponStrs = redisTemplate.opsForHash().values(redisKey)
                .stream().map(
                    o -> Objects.toString(o,null))
                .collect(Collectors.toList());
        if(CollectionUtils.isEmpty(couponStrs)){
            saveEmptyCouponListToCache(userId,
                    Collections.singletonList(status));
            return Collections.emptyList();
        }
        return couponStrs.stream()
                .map(cs -> JSON.parseObject(cs,Coupon.class))
                .collect(Collectors.toList());
    }
    /**目的:避免缓存穿透*/
    @Override
    public void saveEmptyCouponListToCache(Long userId, List<Integer> status) {
        log.info("Save Empty List To Cache For User:{},Status:{}",
                userId, JSON.toJSONString(status));
        //Key 是 coupon_id,value 是序列化的 coupon
        Map<String,String> invalidCouponMap = new HashMap<>();
        invalidCouponMap.put("-1",JSON.toJSONString(Coupon.invalidCoupon()));
        //用户优惠券缓存信息
        //KV
        // K: status -> redisKey
        // V: {coupon_id :序列化的Coupon}
        //使用redis SessionCallback 把数据命令放到Redis 的pipeline
        SessionCallback sessionCallback = new SessionCallback() {
            @Override
            public Object execute(RedisOperations redisOperations)
                    throws DataAccessException {
                status.forEach(s ->{
                    String redisKey =status2RedosKey(s,userId);
                    redisOperations.opsForHash().putAll(
                            redisKey,invalidCouponMap
                    );
                });
                return null;
            }
        };
    log.info("Pipeline Exe Result:{}",
            JSON.toJSONString(redisTemplate.executePipelined(sessionCallback)));
    }

    @Override
    public String tryToAcquireCouponCodeFromCache(Integer templateId) {
        return null;
    }

    @Override
    public Integer addCouponToCache(Long userId, List<Coupon> coupons, Integer status)
            throws CouponException {
        return null;
    }
    /**根据status 获取到对应的 Redis key*/
    private String status2RedosKey(Integer status,Long userId){
        String redisKey = null;
        CouponStatus couponStatus = CouponStatus.of(status);
        switch (couponStatus){
            case USABLE:
                redisKey =String.format("%s%s",
                        Constant.RedisPrefix.USER_COUPON_USABLE,userId);
                break;
            case USED:
                redisKey =String.format("%s%s",
                        Constant.RedisPrefix.USER_COUPON_USED,userId);
                break;
            case EXPIRED:
                redisKey =String.format("%s%s",
                        Constant.RedisPrefix.USER_COUPON_EXPIRED,userId);
                break;
        }
        return redisKey;
    }
}

尝试从缓存中获取优惠券码:

优惠券系统:优惠券分发微服务功能编码实现

缓存雪崩:

 /**
     * <h2>获取一个随机的过期时间</h2>
     * 缓存雪崩: key在同一个时间失效
     * @param min 最小的小时数
     * @param max 最大的小时数
     * @return 返回[minx,max]之间的随机秒数
     */
    private Long getRandomExpirationTime(Integer min ,Integer max){
        return RandomUtils.nextLong(
                min * 60 *60,
                max *60 *60
        );
    }

优惠券系统:优惠券分发微服务功能编码实现
优惠券系统:优惠券分发微服务功能编码实现

新增加优惠券到缓存中:

  /**
     * <h2>新增加优惠券到cache中</h2>
     * @param userId
     * @param coupons
     * @return
     */
    private Integer addCouponTocacheForUsable(Long userId,List<Coupon> coupons){
        /**
         * 如果 status 是 USABLE,代表是新增加的优惠劵
         * 只会影响一个,Cache: USER_COUPON_USABLE
         */
        log.debug("Add Coupon To Cache For Usable");
        Map<String,String> needCachedObject = new HashMap<>();
        coupons.forEach(c ->
                needCachedObject.put(
                        c.getId().toString(),
                        JSON.toJSONString(c)
                ));
        String redisKey =status2RedosKey(
                CouponStatus.USABLE.getCode(),userId);

        redisTemplate.opsForHash().putAll(redisKey,needCachedObject);
        log.info("Add {} Coupons To Cache:{},{}",
                needCachedObject.size(),userId,redisKey);
        redisTemplate.expire(
                redisKey,
                getRandomExpirationTime(1,2),
                TimeUnit.SECONDS
        );
        return  needCachedObject.size();
    }
    /**根据status 获取到对应的 Redis key*/
    private String status2RedosKey(Integer status,Long userId){
        String redisKey = null;
        CouponStatus couponStatus = CouponStatus.of(status);
        switch (couponStatus){
            case USABLE:
                redisKey =String.format("%s%s",
                        Constant.RedisPrefix.USER_COUPON_USABLE,userId);
                break;
            case USED:
                redisKey =String.format("%s%s",
                        Constant.RedisPrefix.USER_COUPON_USED,userId);
                break;
            case EXPIRED:
                redisKey =String.format("%s%s",
                        Constant.RedisPrefix.USER_COUPON_EXPIRED,userId);
                break;
        }
        return redisKey;
    }

优惠券系统:优惠券分发微服务功能编码实现
优惠券系统:优惠券分发微服务功能编码实现

相关标签: 项目