优惠券系统:优惠券分发微服务功能编码实现
程序员文章站
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;
}
上一篇: 3-2.4 出栈序列的合法性:判断一个序列是否可由入栈出栈得到
下一篇: python 方差分析