Authored by Lixiaodi

增加缓存模块

package com.yoho.activity.common.cache;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.yoho.activity.common.redis.CacheKeyHelper;
import com.yoho.core.common.utils.MD5;
import com.yoho.core.redis.YHRedisTemplate;
import com.yoho.core.redis.YHValueOperations;
@Aspect
@Component
public class CacheAop {
private static final Logger logger = LoggerFactory.getLogger(CacheAop.class);
private static final String DEFAULT_KEY_PRE = "yh:activity:";
private static final int DEFAULT_CACHE_SECONDS = 300;
@Resource(name = "yhValueOperations")
private YHValueOperations<String, String> yhValueOperations;
@Resource(name = "yhRedisTemplate")
private YHRedisTemplate<String, String> yhRedisTemplate;
@Pointcut("@annotation(com.yoho.activity.common.cache.Cacheable)")
public void cacheAnnotationPointCut() {
}
@Around("cacheAnnotationPointCut() ")
public Object cacheAop(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Method proxyMethod = ((MethodSignature)joinPoint.getSignature()).getMethod();
Method soruceMethod = joinPoint.getTarget().getClass().getMethod(proxyMethod.getName(), proxyMethod.getParameterTypes());
Object[] args = joinPoint.getArgs();
Class<?> returnType = method.getReturnType();
Cacheable cachable = soruceMethod.getAnnotation(Cacheable.class);
String key = getCacheKey(soruceMethod, args);
logger.debug("Enter cache pointcut: key is {}", key);
//从缓存中获取数据
try {
Object result = getFromCache(key, returnType);
if (result != null) {
logger.debug("Cache hit for key:{}.", key);
return result;
}
} catch (Exception e) {
logger.warn("get from cache exception.", e);
}
// cache miss
Object result = joinPoint.proceed();
try {
saveToCache(key, result, cachable.expireTime());
} catch (Exception e) {
logger.warn("save cache exception.", e);
}
return result;
}
private void saveToCache(String key, Object value, long expire) {
if (expire <= 0) {
expire = DEFAULT_CACHE_SECONDS;
}
yhValueOperations.set(key, CacheKeyHelper.value2String(value));
long timeout = 5;
yhRedisTemplate.longExpire(key, timeout, TimeUnit.SECONDS);
}
private Object getFromCache(String key, Class<?> valueType) {
return CacheKeyHelper.string2Value(yhValueOperations.get(key), valueType);
}
public void clearCache(Method method, Object[] args) {
yhRedisTemplate.delete(getCacheKey(method, args));
}
private String getCacheKey(Method method, Object[] args) {
Cacheable cachable = method.getAnnotation(Cacheable.class);
String keyPre = cachable.keyPre();
if (StringUtils.isBlank(keyPre)) {
keyPre = DEFAULT_KEY_PRE + method.getName();
}
int[] excludeParams = cachable.excludeParams();
String cacheValueKey = getValueStrings(args, excludeParams);
String cacheKey = keyPre + cacheValueKey;
// 最大250,超过限制需要MD5
if (cacheKey.length() > 250) {
cacheKey = MD5.md5(cacheKey);
}
return cacheKey;
}
/**
* 获取参数的签名
* public for test
* @param arguments arguments
* @param excludeParams arguments to exclude
* @return params sign
*/
public String getValueStrings(Object arguments[], int[] excludeParams) {
if (ArrayUtils.isEmpty(arguments)) {
return StringUtils.EMPTY;
}
List<String> methodParams = new LinkedList<>();
for (int i = 0; i < arguments.length; i++) {
//排除掉某些参数
if (ArrayUtils.contains(excludeParams, i)) {
continue;
}
Object arg = arguments[i];
String param = this.argToString(arg);
methodParams.add(param);
}
String arg_sign = String.join("-", methodParams).replaceAll("\\s+","");
return arg_sign;
}
/**
* 把参数对象转化为字符串,使用JSON转化
* A key can be up to 250 bytes. It may not contain:
* null (0x00)
* space (0x20)
* tab (0x09)
* newline (0x0a)
* carriage-return (0x0d)
*
* @param arg 参数对象
* @return 字符串
* @auth chunhua.zhang@yoho.cn
*/
private String argToString(Object arg) {
if (arg == null) {
return StringUtils.EMPTY;
}
if(arg instanceof String[]){
return String.join("-", (String[]) arg);
}else{
return String.valueOf(arg);
}
}
}
... ...
package com.yoho.activity.common.cache;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Cacheable {
/**
*
* 功能描述: 缓存key模板
*
* @return
* @see [相关类/方法](可选)
* @since [产品/模块版本](可选)
*/
String keyPre() default "";
/**
*
* 功能描述: 缓存时间,单位秒 〈功能详细描述〉
*
* @return
* @see [相关类/方法](可选)
* @since [产品/模块版本](可选)
*/
long expireTime();
int[] excludeParams() default {};
}
... ...
... ... @@ -90,4 +90,11 @@ public class Prize {
public void setImage(String image) {
this.image = image;
}
@Override
public String toString() {
return "Prize [id=" + id + ", lotteryId=" + lotteryId + ", giftId=" + giftId + ", name=" + name + ", prizeType="
+ prizeType + ", prizeValue=" + prizeValue + ", total=" + total + ", remark=" + remark + ", image="
+ image + "]";
}
}
\ No newline at end of file
... ...
... ... @@ -3,6 +3,7 @@
*/
package com.yoho.activity.service.impl;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
... ... @@ -20,6 +21,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.yoho.activity.common.cache.Cacheable;
import com.yoho.activity.common.helper.SendCouponHelper;
import com.yoho.activity.common.vo.LotteryRespData;
import com.yoho.activity.service.ILotteryService;
... ... @@ -67,6 +69,7 @@ public class LotteryServiceImpl implements ILotteryService {
private SendCouponHelper sendCouponHelper;
@Override
@Cacheable(expireTime = 300)
public LotteryRespData getLotteryInfo(Integer lotteryId) {
LotteryRespData data = new LotteryRespData();
... ... @@ -107,7 +110,7 @@ public class LotteryServiceImpl implements ILotteryService {
LotteryRecord rec = records.get(0);
boolean havePrize;
Prize prize = null;
if(rec.getPrizeId() == null || rec.getPrizeId() <= 0) {
if (rec.getPrizeId() == null || rec.getPrizeId() <= 0) {
havePrize = false;
} else {
prize = prizeMapper.selectByPrimaryKey(rec.getPrizeId());
... ... @@ -129,10 +132,11 @@ public class LotteryServiceImpl implements ILotteryService {
@Override
public synchronized LotteryRespData lucky(Integer lotteryId, Integer userId, String orderCode) {
logger.info("用户{}的订单{}进行抽奖!", userId, orderCode);
// 已抽奖检查
LotteryRespData orderLotteryState = getOrderLotteryState(lotteryId, userId, orderCode);
if (orderLotteryState.getOrderLotteryCode() != STATE_NOT_USE) {
logger.info("用户{}的订单{}不符合抽奖条件!", userId, orderCode);
return orderLotteryState;
}
... ... @@ -140,6 +144,7 @@ public class LotteryServiceImpl implements ILotteryService {
int lotteryCount = lotteryRecordMapper.selectUserLotteryPrizeCount(lotteryId, userId, lotteryId % 10);
// 只能中一次
if (lotteryCount > 0) {
logger.info("用户{}的订单{}已经中过奖!", userId, orderCode);
return savePrizeAndGetResp(lotteryId, userId, orderCode, null);
}
... ... @@ -170,10 +175,11 @@ public class LotteryServiceImpl implements ILotteryService {
// 随机抽取一个
Integer prizeId = selectOne(allMap);
logger.info("用户{}的订单{}抽到奖项{}!", userId, orderCode, prizeId);
// 奖品已经抽完
Integer remain = remainMap.get(prizeId);
if (remain <= 0) {
logger.info("用户{}的订单{}抽到奖项{},该奖项剩余量为0!", userId, orderCode, prizeId);
prizeId = null;
}
... ... @@ -185,6 +191,7 @@ public class LotteryServiceImpl implements ILotteryService {
LotteryRespData data = savePrizeAndGetResp(lotteryId, userId, orderCode, prizeId);
Prize prize = prizeMapper.selectByPrimaryKey(prizeId);
if (!hasRealPrize(prize)) {
logger.info("用户{}的订单{}抽到奖项{},该奖项为nothing!", userId, orderCode, prizeId);
data.setOrderLotteryCode(STATE_USE_NO_PRIZE);
data.setOrderLotteryMessage(MSG_NO_PRIZE);
return data;
... ... @@ -199,6 +206,7 @@ public class LotteryServiceImpl implements ILotteryService {
}
private void givePrize(Integer userId, String orderCode, Prize prize) {
logger.info("用户{}的订单{}抽到奖项{},进行发奖!", userId, orderCode, prize);
executor.execute(() -> {
if ("coupons".equals(prize.getPrizeType())) {
sendCouponHelper.sendCoupon(prize.getPrizeValue(), userId);
... ... @@ -207,7 +215,11 @@ public class LotteryServiceImpl implements ILotteryService {
sendCouponHelper.sendYOHOBi(userId, Integer.parseInt(prize.getPrizeValue()), orderCode);
}
if ("orderyohocoin".equals(prize.getPrizeType())) {
// 订单金额
BigDecimal orderMoney = BigDecimal.ONE;
int yohobi = new BigDecimal(prize.getPrizeValue()).divide(new BigDecimal("100")).multiply(orderMoney)
.intValue();
sendCouponHelper.sendYOHOBi(userId, yohobi, orderCode);
}
});
}
... ... @@ -260,14 +272,14 @@ public class LotteryServiceImpl implements ILotteryService {
for (Iterator<Prize> iterator = prize.iterator(); iterator.hasNext();) {
Prize p = iterator.next();
if(!hasRealPrize(p)) {
if (!hasRealPrize(p)) {
iterator.remove();
} else {
clearPrize(p);
}
}
}
private boolean hasRealPrize(Prize prize) {
return !StringUtils.equals("nothing", prize.getPrizeType());
}
... ...