一个关于面经的开发网站全程记录--5

88 阅读11分钟

一个关于面经的开发网站全程记录--5

总览:完成点赞,取消点赞,收藏,取消收藏,浏览数量显示

浏览数量:

@ApiOperation(value = "查看某一篇面经,查看完之后游览量+1")
@GetMapping(value = "getById/{id}")
public R getById(@PathVariable String id){
    AllExper allExper = allExperService.getById(id);
    Map map = new HashMap<>();
    map.put("experence",allExper);
    return R.ok().data(map);
}

上面的接口作用就是根据id查看一篇面经的具体内容,此时一旦调用该接口浏览数量+1

第一种方法直接操作mysql:在这里可以直接在存放面经的表中设置一个浏览数量的字段,每次调用将该字段的值+1 第二种方法使用redis,大致思路如下:每次启动时将每篇面经的浏览数量写入redis中,在redis中进行浏览量的+1,之后通过周期任务的方式定期同步数据在MySQL,最后也要在每次程序关闭是进行数据同步之后才能关闭。

前提准备: 要用到Redis中操作其中数据结构的部分方法,自己对其封装名称为RedisUtil,这玩意网上大把,不用自己写。

@Component
public class RedisUtil {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    private StringRedisTemplate stringRedisTemplate;

    public RedisUtil(RedisTemplate<String, Object> redisTemplate, StringRedisTemplate stringRedisTemplate) {
        this.redisTemplate = redisTemplate;
        this.stringRedisTemplate = stringRedisTemplate;
    }

    /**
     * 指定缓存失效时间
     * @param key 键
     * @param time 时间(秒)
     * @return
     */
    public boolean expire(String key,long time){
        try {
            if(time>0){
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据key 获取过期时间
     * @param key 键 不能为null
     * @return 时间(秒) 返回0代表为永久有效
     */
    public long getExpire(String key){
        return redisTemplate.getExpire(key,TimeUnit.SECONDS);
    }

    /**
     * 判断key是否存在
     * @param key 键
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key){
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除缓存
     * @param key 可以传一个值 或多个
     */
    @SuppressWarnings("unchecked")
    public void del(String ... key){
        if(key!=null&&key.length>0){
            if(key.length==1){
                redisTemplate.delete(key[0]);
            }else{
                //报错    更改
                redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));
            }
        }
    }

    //============================String=============================
    /**
     * 普通缓存获取
     * @param key 键
     * @return 值
     */
    public Object get(String key){
        return key==null?null:redisTemplate.opsForValue().get(key);
    }

    /**
     * 普通缓存放入
     * @param key 键
     * @param value 值
     * @return true成功 false失败
     */
    public boolean set(String key,Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 普通缓存放入并设置时间
     * @param key 键
     * @param value 值
     * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 false 失败
     */
    public boolean set(String key,Object value,long time){
        try {
            if(time>0){
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            }else{
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 递增
     * @param key 键
     * @param delta 要增加几(大于0)
     * @return
     */
    public long incr(String key, long delta){
        if(delta<0){
            throw new RuntimeException("递增因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 递减
     * @param key 键
     * @param delta 要减少几(小于0)
     * @return
     */
    public long decr(String key, long delta){
        if(delta<0){
            throw new RuntimeException("递减因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, -delta);
    }

    //================================Map=================================
    /**
     * HashGet
     * @param key 键 不能为null
     * @param item 项 不能为null
     * @return 值
     */
    public Object hget(String key,String item){
        return redisTemplate.opsForHash().get(key, item);
    }

    /**
     * 获取hashKey对应的所有键值
     * @param key 键
     * @return 对应的多个键值
     */
    public Map<Object,Object> hmget(String key){
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * HashSet
     * @param key 键
     * @param map 对应多个键值
     * @return true 成功 false 失败
     */
    public boolean hmset(String key, Map<String,Object> map){
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * HashSet 并设置时间
     * @param key 键
     * @param map 对应多个键值
     * @param time 时间(秒)
     * @return true成功 false失败
     */
    public boolean hmset(String key, Map<String,Object> map, long time){
        try {
            redisTemplate.opsForHash().putAll(key, map);
            if(time>0){
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     * @param key 键
     * @param item 项
     * @param value 值
     * @return true 成功 false失败
     */
    public boolean hset(String key,String item,Object value) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     * @param key 键
     * @param item 项
     * @param value 值
     * @param time 时间(秒)  注意:如果已存在的hash表有时间,这里将会替换原有的时间
     * @return true 成功 false失败
     */
    public boolean hset(String key,String item,Object value,long time) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            if(time>0){
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除hash表中的值
     * @param key 键 不能为null
     * @param item 项 可以使多个 不能为null
     */
    public void hdel(String key, Object... item){
        redisTemplate.opsForHash().delete(key,item);
    }

    /**
     * 判断hash表中是否有该项的值
     * @param key 键 不能为null
     * @param item 项 不能为null
     * @return true 存在 false不存在
     */
    public boolean hHasKey(String key, String item){
        return redisTemplate.opsForHash().hasKey(key, item);
    }

    /**
     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
     * @param key 键
     * @param item 项
     * @param by 要增加几(大于0)
     * @return
     */
    public double hincr(String key, String item,double by){
        return redisTemplate.opsForHash().increment(key, item, by);
    }

    /**
     * hash递减
     * @param key 键
     * @param item 项
     * @param by 要减少记(小于0)
     * @return
     */
    public double hdecr(String key, String item,double by){
        return redisTemplate.opsForHash().increment(key, item,-by);
    }

    //============================set=============================
    /**
     * 根据key获取Set中的所有值
     * @param key 键
     * @return
     */
    public Set<Object> sGet(String key){
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 根据value从一个set中查询,是否存在
     * @param key 键
     * @param value 值
     * @return true 存在 false不存在
     */
    public boolean sHasKey(String key,Object value){
        try {
            return redisTemplate.opsForSet().isMember(key, value);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将数据放入set缓存
     * @param key 键
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long sSet(String key, Object...values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 将set数据放入缓存
     * @param key 键
     * @param time 时间(秒)
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long sSetAndTime(String key,long time,Object...values) {
        try {
            Long count = redisTemplate.opsForSet().add(key, values);
            if(time>0) {
                expire(key, time);
            }
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 获取set缓存的长度
     * @param key 键
     * @return
     */
    public long sGetSetSize(String key){
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 移除值为value的
     * @param key 键
     * @param values 值 可以是多个
     * @return 移除的个数
     */
    public long setRemove(String key, Object ...values) {
        try {
            Long count = redisTemplate.opsForSet().remove(key, values);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
    //===============================list=================================

    /**
     * 获取list缓存的内容
     * @param key 键
     * @param start 开始
     * @param end 结束  0 到 -1代表所有值
     * @return
     */
    public List<Object> lGet(String key, long start, long end){
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取list缓存的长度
     * @param key 键
     * @return
     */
    public long lGetListSize(String key){
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 通过索引 获取list中的值
     * @param key 键
     * @param index 索引  index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
     * @return
     */
    public Object lGetIndex(String key,long index){
        try {
            return redisTemplate.opsForList().index(key, index);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将list放入缓存
     * @param key 键
     * @param value 值
     * @return
     */
    public boolean lSet(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     * @param key 键
     * @param value 值
     * @param time 时间(秒)
     * @return
     */
    public boolean lSet(String key, Object value, long time) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     * @param key 键
     * @param value 值
     * @return
     */
    public boolean lSet(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     * @param key 键
     * @param value 值
     * @param time 时间(秒)
     * @return
     */
    public boolean lSet(String key, List<Object> value, long time) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据索引修改list中的某条数据
     * @param key 键
     * @param index 索引
     * @param value 值
     * @return
     */
    public boolean lUpdateIndex(String key, long index,Object value) {
        try {
            redisTemplate.opsForList().set(key, index, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 移除N个值为value
     * @param key 键
     * @param count 移除多少个
     * @param value 值
     * @return 移除的个数
     */
    public long lRemove(String key,long count,Object value) {
        try {
            Long remove = redisTemplate.opsForList().remove(key, count, value);
            return remove;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 模糊查询获取key值
     * @param pattern
     * @return
     */
    public Set keys(String pattern){
        return redisTemplate.keys(pattern);
    }

    /**
     * 使用Redis的消息队列
     * @param channel
     * @param message 消息内容
     */
    public void convertAndSend(String channel, Object message){
        redisTemplate.convertAndSend(channel,message);
    }



    /**
     * 根据起始结束序号遍历Redis中的list
     * @param listKey
     * @param start  起始序号
     * @param end  结束序号
     * @return
     */
    public List<Object> rangeList(String listKey, long start, long end) {
        //绑定操作
        BoundListOperations<String, Object> boundValueOperations = redisTemplate.boundListOps(listKey);
        //查询数据
        return boundValueOperations.range(start, end);
    }
    /**
     * 弹出右边的值 --- 并且移除这个值
     * @param listKey
     */
    public Object rifhtPop(String listKey){
        //绑定操作
        BoundListOperations<String, Object> boundValueOperations = redisTemplate.boundListOps(listKey);
        return boundValueOperations.rightPop();
    }

    /**----------------zSet相关操作------------------*/
    /**
     * 添加元素,有序集合是按照元素的score值由小到大排列
     *
     * @param key
     * @param value
     * @param score
     * @return
     */
    public Boolean zAdd(String key, String value, double score) {
        return stringRedisTemplate.opsForZSet().add(key, value, score);
    }

    public Long zAdd(String key, Set<ZSetOperations.TypedTuple<String>> values) {
        return stringRedisTemplate.opsForZSet().add(key, values);
    }

    public Long zRemove(String key, Object... values) {
        return stringRedisTemplate.opsForZSet().remove(key, values);
    }

    /**
     * 增加元素的score值,并返回增加后的值
     * @return
     */
    public Double zIncrementScore(String key, String value, double delta) {
        return stringRedisTemplate.opsForZSet().incrementScore(key, value, delta);
    }

    /**
     * 返回元素在集合的排名,有序集合是按照元素的score值由小到大排列
     * @param key
     * @param value
     * @return
     */
    public Long zRank(String key, Object value) {
        return stringRedisTemplate.opsForZSet().rank(key, value);
    }

    /**
     * 返回元素在集合的排名,按元素的score值由大到小排列
     * @param key
     * @param value
     * @return
     */
    public Long zReverseRank(String key, Object value) {
        return stringRedisTemplate.opsForZSet().reverseRank(key ,value);
    }

    /**
     * 获取集合的元素, 从小到大排序
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<String> zRange(String key, long start, long end) {
        return stringRedisTemplate.opsForZSet().range(key, start, end);
    }

    /**
     * 获取集合元素, 并且把score值也获取
     */
    public Set<ZSetOperations.TypedTuple<String>> zRangeWithScores(String key, long start, long end) {
        return stringRedisTemplate.opsForZSet().rangeWithScores(key, start, end);
    }

    /**
     * 根据Score值查询集合元素
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Set<String> zRangeByScore(String key, double min, double max) {
        return stringRedisTemplate.opsForZSet().rangeByScore(key, min, max);
    }

    /**
     * 根据Score值查询集合元素, 从小到大排序
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Set<ZSetOperations.TypedTuple<String>> zRangeByScoreWithScores(String key, double min, double max) {
        return stringRedisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max);
    }

    public Set<ZSetOperations.TypedTuple<String>> zRangeByScoreWithScores(String key, double min, double max, long start, long end) {
        return stringRedisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max, start, end);
    }

    /**
     * 获取集合的元素, 从大到小排序
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<String> zReverseRange(String key, long start, long end) {
        return stringRedisTemplate.opsForZSet().reverseRange(key, start, end);
    }

    /**
     * 获取集合的元素, 从大到小排序, 并返回score值
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<ZSetOperations.TypedTuple<String>> zReverseRangeWithScores(String key, long start, long end) {
        return stringRedisTemplate.opsForZSet().reverseRangeWithScores(key, start, end);
    }
}

首先写一个aop的切面方法,对前边controll中的public R getById(@PathVariable String id)这个接口进行监听,一旦调用了该接口,浏览量直接+1

//指定为切面类
@Aspect
@Component
public class MyAspect {
    private final AllExperService allExperService;
    private final RedisUtil redisUtil;

    public MyAspect(AllExperService allExperService, RedisUtil redisUtil) {
        this.allExperService = allExperService;
        this.redisUtil = redisUtil;
    }

    //定义一个名为"myPointCut()"的切面,getById()这个方法中
    @Pointcut("execution(public * com.fourpeople.interview.controller.AllExperController.getById(..))")
    public void myScanPointCut(){}

    //打开一个面经之后,将面经的游览量+1
    @After("myScanPointCut()")
    public void doScanAfter(JoinPoint joinPoint) throws Throwable {
        Object[] objs=joinPoint.getArgs();

        String id = (String) objs[0];
        //这个地方是操作Redis中的ZSet类型的scanCount 这个scanCount的数据是从mysql读入到redis读取的
        redisUtil.zIncrementScore("scanCount",id,1);
    }
}

接下来实现,mysql和redis的数据同步问题,即启动将mysql中的数据读入redis,定期同步数据,关闭时同步数据,写一个监听类用来实现该功能。(该类中也包含了点赞和收藏的同步读写)

/**
 * @author zzy
 * ,
 */

@Slf4j
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SyncRMDataListenerHandler {

//    private final ArticleService articleService;
    private final RedisUtil redisUtil;
    private final AllExperService allExperService;
    private final UserGoodService userGoodService;
    private final CollExperService collExperService;


    @Autowired
    public SyncRMDataListenerHandler(AllExperService allExperService, RedisUtil redisUtil,
                                     UserGoodService userGoodService, CollExperService collExperService){
        this.allExperService = allExperService;
        this.redisUtil = redisUtil;
        this.userGoodService = userGoodService;
        this.collExperService = collExperService;
    }

    public static final String SCAN_KEY = "scanCount";
    public static final String COLLECT_KEY = "collectCount";
    public static final String GOOD_KEY = "goodCount";


    @PostConstruct
    public void init() throws Exception {
        log.info("数据初始化开始...");
        //将数据库中的数据写入redis
        List<AllExper> articleLst = allExperService.list();
        articleLst.forEach(article -> {
            //将浏览量、点赞数和收藏数写入redis   操作allexper表
            redisUtil.zAdd(SCAN_KEY, article.getId(), article.getScanCount());
            redisUtil.zAdd(COLLECT_KEY, article.getId(), article.getCollectCount());
            redisUtil.zAdd(GOOD_KEY, article.getId(), article.getGoodCount());
        });

        //将usergood表中的信息写入redis
        List<UserGood> userGoodList = userGoodService.list();
        userGoodList.forEach(userGood -> {
            //写入usergood表中的信息

            String key = RedisKeyUtils.getGoodKey(userGood.getUserId(), userGood.getBeExperId());
            UserGoodVo userGoodVo = new UserGoodVo();
            BeanUtils.copyProperties(userGood,userGoodVo);
            redisUtil.hset(MAP_GOOD_KEY,key,userGoodVo);
        });


        //将collExper表中的信息写入Redis
        List<CollExper> collExperList = collExperService.list();
        collExperList.forEach(collExper -> {
            //写入
            String key = RedisKeyUtils.getGoodKey(collExper.getUserId(), collExper.getExperId());
            CollExperVo collExperVo = new CollExperVo();
            BeanUtils.copyProperties(collExper,collExperVo);  //源——>目标
            redisUtil.hset(MAP_COLL,key,collExperVo);
        });

        log.info("数据已写入redis...");
    }

    /**
     * 关闭时操作
     */
    @PreDestroy
    public void afterDestroy() {
        log.info("开始关闭...");
        //将redis中的数据写入数据库
        Set<ZSetOperations.TypedTuple<String>> scanCount = redisUtil.zReverseRangeWithScores("scanCount", 0, 10);
        Set<ZSetOperations.TypedTuple<String>> collectCount = redisUtil.zReverseRangeWithScores("collectCount", 0, 10);
        Set<ZSetOperations.TypedTuple<String>> goodCount = redisUtil.zReverseRangeWithScores("goodCount", 0, 10);

        writeNum(scanCount, SCAN_KEY);
        writeNum(collectCount, COLLECT_KEY);
        writeNum(goodCount, GOOD_KEY);


        //将redis中的点赞数据写入数据库
        Map<Object, Object> map = redisUtil.hmget(MAP_GOOD_KEY);
        log.info("点赞的map{}",map);
        writeGood(map);


        //将redis中的收藏数据写入数据库中
        Map<Object,Object> mapColl = redisUtil.hmget(MAP_COLL);
        log.info("收藏的map{}",map);
        writeColl(mapColl);

        log.info("redis写入数据库完毕");
    }

    @Scheduled(cron = "*/30 * * * * ?")
    public void updateNum() {
        log.info("周期任务开始执行...");
        Set<ZSetOperations.TypedTuple<String>> scanCount = redisUtil.zReverseRangeWithScores("scanCount", 0, 10);
        writeNum(scanCount, SCAN_KEY);

        Set<ZSetOperations.TypedTuple<String>> collectCount = redisUtil.zReverseRangeWithScores("collectCount", 0, 10);
        writeNum(collectCount, COLLECT_KEY);

        Set<ZSetOperations.TypedTuple<String>> goodCount = redisUtil.zReverseRangeWithScores("goodCount", 0, 10);
        writeNum(goodCount, GOOD_KEY);

        Map<Object, Object> map = redisUtil.hmget(MAP_GOOD_KEY);
        log.info("点赞的map{}",map);
        writeGood(map);

        //将redis中的收藏数据写入数据库中
        Map<Object,Object> mapColl = redisUtil.hmget(MAP_COLL);
        log.info("收藏的map{}",map);
        writeColl(mapColl);

        log.info("周期任务执行完毕,redis写入数据库完毕");
    }


    //将redis数据写入数据库的具体方法
    private void writeNum(Set<ZSetOperations.TypedTuple<String>> set, String fieldName) {
        set.forEach(item -> {
            //人家的这个是转换为整形   我的不用 我的ID本来就是String
//            Long id = Long.valueOf(item.getValue());
            String id = item.getValue();
            log.info("正在写入的id:{}",id);

            //数量
            Integer num = item.getScore().intValue();
            log.info("数量:{}",num);

            AllExper article = allExperService.getById(id);
            switch (fieldName) {
                case SCAN_KEY:
                    article.setScanCount(num);
                    break;
                case COLLECT_KEY:
                    article.setCollectCount(num);
                    break;
                case GOOD_KEY:
                    article.setGoodCount(num);
                    break;
                default:
                    return;
            }

            //更新数据库
            allExperService.updateNumById(article);
            log.info("{} 更新完毕", fieldName);
        });
    }
}

注意:allExperService.updateNumById(article);这个更新数据库的方法,是自己写的sql,当然也可以用MP自带的方法,根据情况而定。

点赞和取消点赞的大致思路:判断此次点击是点赞还是取消点赞,如果是点赞就将goodCount的数量+1,同时将点赞人的id和被点赞文章的id拼接为redis中map的key存储到MAP_GOOD_KEY中,如果时取消点赞,就将点赞数-1.

拼接key的工具类;

@ApiOperation(value = "Redis拼接userID和beExperId,用于生成hash中的key")
public class RedisKeyUtils {

    public static final String MAP_GOOD_KEY = "map_good";
    public static final String MAP_COLL = "map_good";

    public static String getGoodKey(String likedUserId, String likedPostId){
        StringBuilder builder = new StringBuilder();
        builder.append(likedUserId);
        builder.append("::");
        builder.append(likedPostId);
        return builder.toString();
    }
}

vo类:

@Data
public class UserGoodVo {

    //自动生成
    private String id;

    private String userId;

    private String beUserId;

    private String beExperId;

    private Integer state;
}

controller:

@ApiOperation(value = "取消点赞,删除redis中的key,点赞量-1,点赞,判断key是否在redis的map中,是的话就是点赞,存储UserGoodVO类到map中")
@PostMapping(value = "deleteGoodOne")
public R clickAddOrdeleteGoodOne(@RequestBody UserGoodVo userGoodVo){
    userGoodService.clickAddOrdeleteGoodOne(userGoodVo);
    return R.ok();
}

service:

 @Override
    public void clickAddOrdeleteGoodOne(UserGoodVo userGoodVo) {

        String userId = userGoodVo.getUserId();
        String beExperId = userGoodVo.getBeExperId();
        int state = userGoodVo.getState();
        String key = RedisKeyUtils.getGoodKey(userId,beExperId);

        //如果redis中存在对应的key并且state=1标志着已经点赞,要进行取消点赞操作
        if(redisUtil.hHasKey(MAP_GOOD_KEY,key) && state == 1){
            userGoodVo.setState(0);
            redisUtil.hset(MAP_GOOD_KEY,key,userGoodVo);
            redisUtil.zIncrementScore("goodCount",beExperId,-1);

        }else{
            //进行点赞操作
            userGoodVo.setState(1);
            redisUtil.hset(MAP_GOOD_KEY,key,userGoodVo);
            redisUtil.zIncrementScore("goodCount",beExperId,1);
        }

    }

将redis中数据写入mysql中,该方法要写到上面监听类中:

  public void writeGood(Map<Object,Object> map){
        //将redis中map存放数据的value取出来,将UserGoodVo对象转换为UserGood对象存放到数据库中
        Iterator<Map.Entry<Object, Object>> it = map.entrySet().iterator();
        while(it.hasNext()){
            Map.Entry<Object, Object> entry = it.next();
            log.info("map中的一个entry:{}",entry);

            //将object类型  转换为UserGoodVo
            UserGoodVo entryUserGoodVo = new UserGoodVo();
            BeanUtils.copyProperties(entry.getValue(),entryUserGoodVo);
            log.info("entryUserGoodVo:{}",entryUserGoodVo);

            //将UserGoodVo转换诶UserGood
            UserGood entryUserGood = new UserGood();
            BeanUtils.copyProperties(entryUserGoodVo, entryUserGood);
            log.info("entryUserGood:{}",entryUserGood);
            
            //如果state是0,表明是取消点赞,故不用写入mysql中
            QueryWrapper<UserGood> wrapper = new QueryWrapper<>();
            wrapper.ne("state",0);

            //存入数据库  将redis中存在的数据
            userGoodService.saveOrUpdate(entryUserGood,wrapper);
        }
    }

以上是点赞,取消点赞。

下面是收藏和取消收藏。

这个原理跟点赞相同。

controll:

@ApiOperation(value = "收藏和取消收藏,判断state状态,如果已经收藏之后再次点击收藏是取消,否则是收藏")
@PostMapping(value = "addCollExper")
public R addOrDeleCollExper(@RequestBody CollExper collExper){
   collExperService.addOrDeleCollExper(collExper);
    return R.ok();
}

vo类:

@Data
public class CollExperVo {

    private String id;

    private String userId;

    private String experId;

    private Integer collState;

}

service:

@Override
public void addOrDeleCollExper(CollExper collExper) {

    String key = RedisKeyUtils.getGoodKey(collExper.getUserId(),collExper.getExperId());
    int collState = collExper.getCollState();
    if(redisUtil.hHasKey(MAP_COLL,key) && collState == 1){
        //如果collState值为1,并且key值已经在MAP_COLL中存在 则要进行的是取消收藏
        //首先将收藏的数量-1
        redisUtil.zIncrementScore("collectCount",collExper.getExperId(),-1);
        //修改collState状态 将1改为0
        collExper.setCollState(0);
        //将collExper写入redis中数据格式是map
        redisUtil.hset(MAP_COLL,key,collExper);
    }else{
        //进行收藏
        collExper.setCollState(1);
        //将数据加入map
        redisUtil.hset(MAP_COLL,key,collExper);
        //收藏量+1
        redisUtil.zIncrementScore("collectCount",collExper.getExperId(),1);
    }
}

将redis中数据写入mysql中,该方法要写到上面监听类中:

public void writeColl(Map<Object,Object> mapColl){
        //将redis中的收藏数据map.entity依次取出来,转换为CollExper对象存入数据库
        //利用迭代器遍历map
        Iterator<Map.Entry<Object,Object>> it = mapColl.entrySet().iterator();
        while(it.hasNext()){
            Map.Entry<Object, Object> entry  = it.next();
            log.info("redis中存储收藏数据的其中一个map:{}", entry);

            //将object类型对象转换为CollExperVo对象
            CollExperVo collExperVo = new CollExperVo();
            BeanUtils.copyProperties(entry.getValue(),collExperVo);

            //将CollExperVo对象转换为CollExper对象
            CollExper collExper = new CollExper();
            BeanUtils.copyProperties(collExperVo,collExper);
            //如果state是0,表明取消收藏,不用写入mysql
            QueryWrapper<CollExper> wrapper = new QueryWrapper<>();
            wrapper.ne("state",0);

            collExperService.saveOrUpdate(collExper,wrapper);

        }
    }

以上就是总览中的所有功能,本文章中的监听类是刚开始的时候借鉴某位大佬的,但是我现在找不到那个连接了,勿怪,aop切面类是以前没有用过的,这次长见识了,监听类中的部分注解也是以前没有用过的,看不懂的自己查吧。

如有错误,请勿介意,当然也可以评论我进行修改。欢迎指正。