|
@@ -0,0 +1,106 @@
|
|
|
+package com.caimei.annotation;
|
|
|
+
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+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.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
|
|
+import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
|
|
+import org.springframework.data.redis.core.RedisCallback;
|
|
|
+import org.springframework.data.redis.core.RedisTemplate;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+import redis.clients.jedis.commands.JedisCommands;
|
|
|
+
|
|
|
+import javax.annotation.Resource;
|
|
|
+import java.lang.reflect.Method;
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.Date;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 统计切面
|
|
|
+ *
|
|
|
+ * @author : Charles
|
|
|
+ * @date : 2021/12/29
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@Aspect
|
|
|
+@Component
|
|
|
+@ConditionalOnClass(RedisTemplate.class)
|
|
|
+public class StatisticsAspect {
|
|
|
+ @Resource
|
|
|
+ private RedisTemplate<String,String> redisTemplate;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 切入点,根据自定义Statistics实际路径进行调整
|
|
|
+ */
|
|
|
+ @Pointcut("@annotation(com.caimei.annotation.Statistics)")
|
|
|
+ public void executeStatistics() {}
|
|
|
+
|
|
|
+ @Around("executeStatistics()")
|
|
|
+ public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
|
|
|
+ // 获取方法
|
|
|
+ Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
|
|
|
+ // 获取参数名数组
|
|
|
+ String[] parameters = new LocalVariableTableParameterNameDiscoverer().getParameterNames(method);
|
|
|
+ // 获取参数值列表
|
|
|
+ Object[] args = joinPoint.getArgs();
|
|
|
+ // 获取自定义注解
|
|
|
+ Statistics statistics = method.getAnnotation(Statistics.class);
|
|
|
+ String prefix = statistics.prefix();
|
|
|
+ String field = statistics.field();
|
|
|
+ int expire = statistics.expire();
|
|
|
+ String fieldKey = null;
|
|
|
+ boolean contains = null != parameters && Arrays.asList(parameters).contains(field);
|
|
|
+ if (contains) {
|
|
|
+ for (int i = 0; i < parameters.length; i++) {
|
|
|
+ if (parameters[i] != null && parameters[i].equals(field)) {
|
|
|
+ fieldKey = args[i].toString();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (null != fieldKey) {
|
|
|
+ // 当前日期串
|
|
|
+ String dateStr = new SimpleDateFormat("yyyyMMdd").format(new Date());
|
|
|
+ // 拼接redisKey
|
|
|
+ String redisKey = prefix + ":" + field + ":" + dateStr;
|
|
|
+ // 获取Redis已有的统计值 + 1
|
|
|
+ int count = getHash(redisKey, fieldKey) + 1;
|
|
|
+ // 设置统计数量 + 1, 过期时间默认两天,(后面定时任务每天凌晨把前一天统计量存入数据库(按天存))
|
|
|
+ setHash(redisKey, fieldKey, count, expire);
|
|
|
+ }
|
|
|
+ return joinPoint.proceed();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取Redis中hash对应的统计值
|
|
|
+ */
|
|
|
+ public Integer getHash(String key, String fieldKey) {
|
|
|
+ String result = redisTemplate.execute(
|
|
|
+ (RedisCallback<String>) connection -> (
|
|
|
+ (JedisCommands) connection.getNativeConnection()
|
|
|
+ ).hget(key, fieldKey)
|
|
|
+ );
|
|
|
+ return result == null ? 0 : Integer.parseInt(result);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 添加/更新Redis中hash的值
|
|
|
+ */
|
|
|
+ public void setHash(String key, String fieldKey, int count, int expire) {
|
|
|
+ String value = Integer.toString(count);
|
|
|
+ redisTemplate.execute(
|
|
|
+ (RedisCallback<Object>) connection -> (
|
|
|
+ (JedisCommands) connection.getNativeConnection()
|
|
|
+ ).hset(key, fieldKey, value)
|
|
|
+ );
|
|
|
+ // 设置超时时间 (秒)
|
|
|
+ redisTemplate.execute(
|
|
|
+ (RedisCallback<Object>) connection -> (
|
|
|
+ (JedisCommands) connection.getNativeConnection()
|
|
|
+ ).expire(key, expire)
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|