Authored by Lixiaodi

增加缓存模块

  1 +package com.yoho.activity.common.cache;
  2 +
  3 +import java.lang.reflect.Method;
  4 +import java.util.LinkedList;
  5 +import java.util.List;
  6 +import java.util.concurrent.TimeUnit;
  7 +
  8 +import javax.annotation.Resource;
  9 +
  10 +import org.apache.commons.lang.ArrayUtils;
  11 +import org.apache.commons.lang.StringUtils;
  12 +import org.aspectj.lang.ProceedingJoinPoint;
  13 +import org.aspectj.lang.annotation.Around;
  14 +import org.aspectj.lang.annotation.Aspect;
  15 +import org.aspectj.lang.annotation.Pointcut;
  16 +import org.aspectj.lang.reflect.MethodSignature;
  17 +import org.slf4j.Logger;
  18 +import org.slf4j.LoggerFactory;
  19 +import org.springframework.stereotype.Component;
  20 +
  21 +import com.yoho.activity.common.redis.CacheKeyHelper;
  22 +import com.yoho.core.common.utils.MD5;
  23 +import com.yoho.core.redis.YHRedisTemplate;
  24 +import com.yoho.core.redis.YHValueOperations;
  25 +
  26 +@Aspect
  27 +@Component
  28 +public class CacheAop {
  29 +
  30 +
  31 + private static final Logger logger = LoggerFactory.getLogger(CacheAop.class);
  32 +
  33 + private static final String DEFAULT_KEY_PRE = "yh:activity:";
  34 + private static final int DEFAULT_CACHE_SECONDS = 300;
  35 +
  36 + @Resource(name = "yhValueOperations")
  37 + private YHValueOperations<String, String> yhValueOperations;
  38 +
  39 + @Resource(name = "yhRedisTemplate")
  40 + private YHRedisTemplate<String, String> yhRedisTemplate;
  41 +
  42 + @Pointcut("@annotation(com.yoho.activity.common.cache.Cacheable)")
  43 + public void cacheAnnotationPointCut() {
  44 + }
  45 +
  46 +
  47 +
  48 + @Around("cacheAnnotationPointCut() ")
  49 + public Object cacheAop(ProceedingJoinPoint joinPoint) throws Throwable {
  50 +
  51 + MethodSignature signature = (MethodSignature) joinPoint.getSignature();
  52 +
  53 + Method method = signature.getMethod();
  54 +
  55 + Method proxyMethod = ((MethodSignature)joinPoint.getSignature()).getMethod();
  56 + Method soruceMethod = joinPoint.getTarget().getClass().getMethod(proxyMethod.getName(), proxyMethod.getParameterTypes());
  57 +
  58 +
  59 + Object[] args = joinPoint.getArgs();
  60 + Class<?> returnType = method.getReturnType();
  61 + Cacheable cachable = soruceMethod.getAnnotation(Cacheable.class);
  62 + String key = getCacheKey(soruceMethod, args);
  63 +
  64 + logger.debug("Enter cache pointcut: key is {}", key);
  65 +
  66 + //从缓存中获取数据
  67 + try {
  68 + Object result = getFromCache(key, returnType);
  69 + if (result != null) {
  70 + logger.debug("Cache hit for key:{}.", key);
  71 + return result;
  72 + }
  73 + } catch (Exception e) {
  74 + logger.warn("get from cache exception.", e);
  75 + }
  76 +
  77 + // cache miss
  78 + Object result = joinPoint.proceed();
  79 + try {
  80 + saveToCache(key, result, cachable.expireTime());
  81 + } catch (Exception e) {
  82 + logger.warn("save cache exception.", e);
  83 + }
  84 + return result;
  85 + }
  86 +
  87 + private void saveToCache(String key, Object value, long expire) {
  88 + if (expire <= 0) {
  89 + expire = DEFAULT_CACHE_SECONDS;
  90 + }
  91 + yhValueOperations.set(key, CacheKeyHelper.value2String(value));
  92 + long timeout = 5;
  93 + yhRedisTemplate.longExpire(key, timeout, TimeUnit.SECONDS);
  94 + }
  95 +
  96 + private Object getFromCache(String key, Class<?> valueType) {
  97 + return CacheKeyHelper.string2Value(yhValueOperations.get(key), valueType);
  98 + }
  99 +
  100 + public void clearCache(Method method, Object[] args) {
  101 + yhRedisTemplate.delete(getCacheKey(method, args));
  102 + }
  103 +
  104 + private String getCacheKey(Method method, Object[] args) {
  105 +
  106 + Cacheable cachable = method.getAnnotation(Cacheable.class);
  107 + String keyPre = cachable.keyPre();
  108 + if (StringUtils.isBlank(keyPre)) {
  109 + keyPre = DEFAULT_KEY_PRE + method.getName();
  110 + }
  111 + int[] excludeParams = cachable.excludeParams();
  112 + String cacheValueKey = getValueStrings(args, excludeParams);
  113 +
  114 + String cacheKey = keyPre + cacheValueKey;
  115 +
  116 + // 最大250,超过限制需要MD5
  117 + if (cacheKey.length() > 250) {
  118 + cacheKey = MD5.md5(cacheKey);
  119 + }
  120 +
  121 + return cacheKey;
  122 + }
  123 +
  124 + /**
  125 + * 获取参数的签名
  126 + * public for test
  127 + * @param arguments arguments
  128 + * @param excludeParams arguments to exclude
  129 + * @return params sign
  130 + */
  131 + public String getValueStrings(Object arguments[], int[] excludeParams) {
  132 +
  133 + if (ArrayUtils.isEmpty(arguments)) {
  134 + return StringUtils.EMPTY;
  135 + }
  136 +
  137 + List<String> methodParams = new LinkedList<>();
  138 +
  139 + for (int i = 0; i < arguments.length; i++) {
  140 + //排除掉某些参数
  141 + if (ArrayUtils.contains(excludeParams, i)) {
  142 + continue;
  143 + }
  144 + Object arg = arguments[i];
  145 + String param = this.argToString(arg);
  146 + methodParams.add(param);
  147 + }
  148 +
  149 + String arg_sign = String.join("-", methodParams).replaceAll("\\s+","");
  150 +
  151 + return arg_sign;
  152 + }
  153 +
  154 + /**
  155 + * 把参数对象转化为字符串,使用JSON转化
  156 + * A key can be up to 250 bytes. It may not contain:
  157 + * null (0x00)
  158 + * space (0x20)
  159 + * tab (0x09)
  160 + * newline (0x0a)
  161 + * carriage-return (0x0d)
  162 + *
  163 + * @param arg 参数对象
  164 + * @return 字符串
  165 + * @auth chunhua.zhang@yoho.cn
  166 + */
  167 + private String argToString(Object arg) {
  168 + if (arg == null) {
  169 + return StringUtils.EMPTY;
  170 + }
  171 + if(arg instanceof String[]){
  172 + return String.join("-", (String[]) arg);
  173 + }else{
  174 + return String.valueOf(arg);
  175 + }
  176 + }
  177 +}
  1 +package com.yoho.activity.common.cache;
  2 +
  3 +import java.lang.annotation.ElementType;
  4 +import java.lang.annotation.Retention;
  5 +import java.lang.annotation.RetentionPolicy;
  6 +import java.lang.annotation.Target;
  7 +
  8 +@Retention(RetentionPolicy.RUNTIME)
  9 +@Target(ElementType.METHOD)
  10 +public @interface Cacheable {
  11 +
  12 + /**
  13 + *
  14 + * 功能描述: 缓存key模板
  15 + *
  16 + * @return
  17 + * @see [相关类/方法](可选)
  18 + * @since [产品/模块版本](可选)
  19 + */
  20 + String keyPre() default "";
  21 +
  22 +
  23 + /**
  24 + *
  25 + * 功能描述: 缓存时间,单位秒 〈功能详细描述〉
  26 + *
  27 + * @return
  28 + * @see [相关类/方法](可选)
  29 + * @since [产品/模块版本](可选)
  30 + */
  31 + long expireTime();
  32 +
  33 + int[] excludeParams() default {};
  34 +
  35 +}
@@ -90,4 +90,11 @@ public class Prize { @@ -90,4 +90,11 @@ public class Prize {
90 public void setImage(String image) { 90 public void setImage(String image) {
91 this.image = image; 91 this.image = image;
92 } 92 }
  93 +
  94 + @Override
  95 + public String toString() {
  96 + return "Prize [id=" + id + ", lotteryId=" + lotteryId + ", giftId=" + giftId + ", name=" + name + ", prizeType="
  97 + + prizeType + ", prizeValue=" + prizeValue + ", total=" + total + ", remark=" + remark + ", image="
  98 + + image + "]";
  99 + }
93 } 100 }
@@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
3 */ 3 */
4 package com.yoho.activity.service.impl; 4 package com.yoho.activity.service.impl;
5 5
  6 +import java.math.BigDecimal;
6 import java.util.HashMap; 7 import java.util.HashMap;
7 import java.util.Iterator; 8 import java.util.Iterator;
8 import java.util.List; 9 import java.util.List;
@@ -20,6 +21,7 @@ import org.slf4j.LoggerFactory; @@ -20,6 +21,7 @@ import org.slf4j.LoggerFactory;
20 import org.springframework.beans.factory.annotation.Autowired; 21 import org.springframework.beans.factory.annotation.Autowired;
21 import org.springframework.stereotype.Service; 22 import org.springframework.stereotype.Service;
22 23
  24 +import com.yoho.activity.common.cache.Cacheable;
23 import com.yoho.activity.common.helper.SendCouponHelper; 25 import com.yoho.activity.common.helper.SendCouponHelper;
24 import com.yoho.activity.common.vo.LotteryRespData; 26 import com.yoho.activity.common.vo.LotteryRespData;
25 import com.yoho.activity.service.ILotteryService; 27 import com.yoho.activity.service.ILotteryService;
@@ -67,6 +69,7 @@ public class LotteryServiceImpl implements ILotteryService { @@ -67,6 +69,7 @@ public class LotteryServiceImpl implements ILotteryService {
67 private SendCouponHelper sendCouponHelper; 69 private SendCouponHelper sendCouponHelper;
68 70
69 @Override 71 @Override
  72 + @Cacheable(expireTime = 300)
70 public LotteryRespData getLotteryInfo(Integer lotteryId) { 73 public LotteryRespData getLotteryInfo(Integer lotteryId) {
71 74
72 LotteryRespData data = new LotteryRespData(); 75 LotteryRespData data = new LotteryRespData();
@@ -107,7 +110,7 @@ public class LotteryServiceImpl implements ILotteryService { @@ -107,7 +110,7 @@ public class LotteryServiceImpl implements ILotteryService {
107 LotteryRecord rec = records.get(0); 110 LotteryRecord rec = records.get(0);
108 boolean havePrize; 111 boolean havePrize;
109 Prize prize = null; 112 Prize prize = null;
110 - if(rec.getPrizeId() == null || rec.getPrizeId() <= 0) { 113 + if (rec.getPrizeId() == null || rec.getPrizeId() <= 0) {
111 havePrize = false; 114 havePrize = false;
112 } else { 115 } else {
113 prize = prizeMapper.selectByPrimaryKey(rec.getPrizeId()); 116 prize = prizeMapper.selectByPrimaryKey(rec.getPrizeId());
@@ -129,10 +132,11 @@ public class LotteryServiceImpl implements ILotteryService { @@ -129,10 +132,11 @@ public class LotteryServiceImpl implements ILotteryService {
129 132
130 @Override 133 @Override
131 public synchronized LotteryRespData lucky(Integer lotteryId, Integer userId, String orderCode) { 134 public synchronized LotteryRespData lucky(Integer lotteryId, Integer userId, String orderCode) {
132 - 135 + logger.info("用户{}的订单{}进行抽奖!", userId, orderCode);
133 // 已抽奖检查 136 // 已抽奖检查
134 LotteryRespData orderLotteryState = getOrderLotteryState(lotteryId, userId, orderCode); 137 LotteryRespData orderLotteryState = getOrderLotteryState(lotteryId, userId, orderCode);
135 if (orderLotteryState.getOrderLotteryCode() != STATE_NOT_USE) { 138 if (orderLotteryState.getOrderLotteryCode() != STATE_NOT_USE) {
  139 + logger.info("用户{}的订单{}不符合抽奖条件!", userId, orderCode);
136 return orderLotteryState; 140 return orderLotteryState;
137 } 141 }
138 142
@@ -140,6 +144,7 @@ public class LotteryServiceImpl implements ILotteryService { @@ -140,6 +144,7 @@ public class LotteryServiceImpl implements ILotteryService {
140 int lotteryCount = lotteryRecordMapper.selectUserLotteryPrizeCount(lotteryId, userId, lotteryId % 10); 144 int lotteryCount = lotteryRecordMapper.selectUserLotteryPrizeCount(lotteryId, userId, lotteryId % 10);
141 // 只能中一次 145 // 只能中一次
142 if (lotteryCount > 0) { 146 if (lotteryCount > 0) {
  147 + logger.info("用户{}的订单{}已经中过奖!", userId, orderCode);
143 return savePrizeAndGetResp(lotteryId, userId, orderCode, null); 148 return savePrizeAndGetResp(lotteryId, userId, orderCode, null);
144 } 149 }
145 150
@@ -170,10 +175,11 @@ public class LotteryServiceImpl implements ILotteryService { @@ -170,10 +175,11 @@ public class LotteryServiceImpl implements ILotteryService {
170 175
171 // 随机抽取一个 176 // 随机抽取一个
172 Integer prizeId = selectOne(allMap); 177 Integer prizeId = selectOne(allMap);
173 - 178 + logger.info("用户{}的订单{}抽到奖项{}!", userId, orderCode, prizeId);
174 // 奖品已经抽完 179 // 奖品已经抽完
175 Integer remain = remainMap.get(prizeId); 180 Integer remain = remainMap.get(prizeId);
176 if (remain <= 0) { 181 if (remain <= 0) {
  182 + logger.info("用户{}的订单{}抽到奖项{},该奖项剩余量为0!", userId, orderCode, prizeId);
177 prizeId = null; 183 prizeId = null;
178 } 184 }
179 185
@@ -185,6 +191,7 @@ public class LotteryServiceImpl implements ILotteryService { @@ -185,6 +191,7 @@ public class LotteryServiceImpl implements ILotteryService {
185 LotteryRespData data = savePrizeAndGetResp(lotteryId, userId, orderCode, prizeId); 191 LotteryRespData data = savePrizeAndGetResp(lotteryId, userId, orderCode, prizeId);
186 Prize prize = prizeMapper.selectByPrimaryKey(prizeId); 192 Prize prize = prizeMapper.selectByPrimaryKey(prizeId);
187 if (!hasRealPrize(prize)) { 193 if (!hasRealPrize(prize)) {
  194 + logger.info("用户{}的订单{}抽到奖项{},该奖项为nothing!", userId, orderCode, prizeId);
188 data.setOrderLotteryCode(STATE_USE_NO_PRIZE); 195 data.setOrderLotteryCode(STATE_USE_NO_PRIZE);
189 data.setOrderLotteryMessage(MSG_NO_PRIZE); 196 data.setOrderLotteryMessage(MSG_NO_PRIZE);
190 return data; 197 return data;
@@ -199,6 +206,7 @@ public class LotteryServiceImpl implements ILotteryService { @@ -199,6 +206,7 @@ public class LotteryServiceImpl implements ILotteryService {
199 } 206 }
200 207
201 private void givePrize(Integer userId, String orderCode, Prize prize) { 208 private void givePrize(Integer userId, String orderCode, Prize prize) {
  209 + logger.info("用户{}的订单{}抽到奖项{},进行发奖!", userId, orderCode, prize);
202 executor.execute(() -> { 210 executor.execute(() -> {
203 if ("coupons".equals(prize.getPrizeType())) { 211 if ("coupons".equals(prize.getPrizeType())) {
204 sendCouponHelper.sendCoupon(prize.getPrizeValue(), userId); 212 sendCouponHelper.sendCoupon(prize.getPrizeValue(), userId);
@@ -207,7 +215,11 @@ public class LotteryServiceImpl implements ILotteryService { @@ -207,7 +215,11 @@ public class LotteryServiceImpl implements ILotteryService {
207 sendCouponHelper.sendYOHOBi(userId, Integer.parseInt(prize.getPrizeValue()), orderCode); 215 sendCouponHelper.sendYOHOBi(userId, Integer.parseInt(prize.getPrizeValue()), orderCode);
208 } 216 }
209 if ("orderyohocoin".equals(prize.getPrizeType())) { 217 if ("orderyohocoin".equals(prize.getPrizeType())) {
210 - 218 + // 订单金额
  219 + BigDecimal orderMoney = BigDecimal.ONE;
  220 + int yohobi = new BigDecimal(prize.getPrizeValue()).divide(new BigDecimal("100")).multiply(orderMoney)
  221 + .intValue();
  222 + sendCouponHelper.sendYOHOBi(userId, yohobi, orderCode);
211 } 223 }
212 }); 224 });
213 } 225 }
@@ -260,14 +272,14 @@ public class LotteryServiceImpl implements ILotteryService { @@ -260,14 +272,14 @@ public class LotteryServiceImpl implements ILotteryService {
260 272
261 for (Iterator<Prize> iterator = prize.iterator(); iterator.hasNext();) { 273 for (Iterator<Prize> iterator = prize.iterator(); iterator.hasNext();) {
262 Prize p = iterator.next(); 274 Prize p = iterator.next();
263 - if(!hasRealPrize(p)) { 275 + if (!hasRealPrize(p)) {
264 iterator.remove(); 276 iterator.remove();
265 } else { 277 } else {
266 clearPrize(p); 278 clearPrize(p);
267 } 279 }
268 } 280 }
269 } 281 }
270 - 282 +
271 private boolean hasRealPrize(Prize prize) { 283 private boolean hasRealPrize(Prize prize) {
272 return !StringUtils.equals("nothing", prize.getPrizeType()); 284 return !StringUtils.equals("nothing", prize.getPrizeType());
273 } 285 }