仿牛客网论坛项目 第四章 Redis的学习

第一节:redis基础(暂时未整理)

第二节:spring整合redis(暂时未整理)

第三节:点赞功能实现 (通过ajax, 异步刷新)

实现功能预览:

第一步:生成redisKey, 注意此时key对应的数据结构为集合(set)

    private static final String SPLIT = ":";
    //点赞的实体
    private static final String PREFIX_ENTITY_LIKE = "like:entity";

    // 生成某个实体的赞key 实体通过entityType和entityId 来得到唯一的实体(帖子discussPost/评论comment/评论的评论)
    //value数据结构为集合(set) -- 存一个集合set,放赞的userId
    public static String getEntityLikeKey(int entityType, int entityId){
        return PREFIX_ENTITY_LIKE + SPLIT + entityType+SPLIT + entityId;
    }

第二步:点赞逻辑(service层

@Service
public class LikeService {
    @Autowired
    private RedisTemplate redisTemplate;

    //点赞
    public void like(int userId, int entityType, int entityId){
        String entityLikeKey = RedisKeyUtil.getEntityLikeKey(entityType, entityId);
        //点过赞 则取消赞
        Boolean isMember = redisTemplate.opsForSet().isMember(entityLikeKey, userId);
        if(isMember){
            redisTemplate.opsForSet().remove(entityLikeKey, userId);
        }else {
            redisTemplate.opsForSet().add(entityLikeKey, userId);
        }
    }

    //查询某实体点赞的数量
    public long findEntityLikeCount(int entityType, int entityId){
        String entityLikeKey = RedisKeyUtil.getEntityLikeKey(entityType, entityId);
        return redisTemplate.opsForSet().size(entityLikeKey);
    }
    //查询某人对某实体的点赞状态
    public int findEntityLikeStatus(int userId, int entityType, int entityId){
        String entityLikeKey = RedisKeyUtil.getEntityLikeKey(entityType, entityId);
        //查询某人是否点过赞
        return redisTemplate.opsForSet().isMember(entityLikeKey,userId)?1:0;
    }
}

第三步:展示赞的数量

在通过controller访问页面时,将赞的数量保存到model内,通过themleaf展示

代码示例(论坛主页):访问/index路径的代码

    @Autowired
    private LikeService likeService;

@RequestMapping(path = {"/index","/"}, method = RequestMethod.GET){
    //分页设置 略
        List<Map<String,Object>> discussPosts = new ArrayList<>();
        if(discussPostList != null){
            //将数据库查询出的一页的帖子信息保存到  discussPosts中
            for (DiscussPost dp : discussPostList){
                Map<String, Object> map = new HashMap<>();
                map.put("post", dp);
                //获取redis中的点赞数量
                long likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_POST, dp.getId());
                map.put("likeCount", likeCount);
                map.put("user", userService.findUserById(dp.getUserId()));
                discussPosts.add(map);
            }
        }
//  将discussPosts 抛给 view (thymeleaf)中
        model.addAttribute("discussPosts",discussPosts);
        return "/index";
}

themleaf模板展示

<li class="d-inline ml-2">
赞 <span th:text="${map.likeCount}"> 11</span>
</li>

第四步:在帖子内点赞/取消赞

首先是异步访问 /like 路径,在controller内调用点赞service

@Controller
public class LikeController {
    @Autowired
    private LikeService likeService;
    @Autowired
    private HostHolder hostHolder;

    @RequestMapping(path = "/like", method = RequestMethod.POST)
    @ResponseBody
    public String like(int entityType, int entityId){
        User user = hostHolder.getUser();

        likeService.like(user.getId(),entityType, entityId);

        long entityLikeCount = likeService.findEntityLikeCount(entityType, entityId);
        int entityLikeStatus = likeService.findEntityLikeStatus(user.getId(), entityType, entityId);

        Map<String,Object> map = new HashMap<>();
        map.put("likeCount", entityLikeCount);
        map.put("likeStatus", entityLikeStatus);

        return CommunityUtil.getJSONString(0,null, map);
    }

在themleaf内通过JavaScript(jQuery)进行Ajax访问

<a href="javascript:;" th:onclick="|like(this,1,${post.id});|" class="text-primary">
<b>赞</b>  <i>11</i>
</a>
function like(btn, entityType, entityId){
    $.post(
        // path
        CONTEXT_PATH+"/like",
        // data
        {"entityType":entityType, "entityId":entityId},
        // 回调函数,data参数为服务器返回的数据
        function (data){
            data = $.parseJSON(data)
            if(data.code == 0){
                $(btn).children("i").text(data.likeCount);
                $(btn).children("b").text(data.likeStatus==1?'已赞':'赞')
            }else {
                alert(data.msg)
            }
        }
    )
}

第四节 我收到的赞(暂时未整理)

第五节 关注、取消关注(暂时未整理)

第六节 关注列表、粉丝列表(暂时未整理)

最后一节 优化登陆模块

1.优化登陆验证码

第一步:生成redisKey
 //存入验证码 前缀
    private static final String PREFIX_KAPTCHA = "kaptcha";
// 给用户发了一个临时凭证 owner 给cookie
    public static String getKaptchaKey(String owner){

        return PREFIX_KAPTCHA+SPLIT+owner;
    }
第二步:获取验证码时将验证码存入redis (数据结构 k-v 为 String - String)
@RequestMapping(path = "/kaptcha", method = RequestMethod.GET)
    public void getKaptcha(HttpServletResponse response/*HttpSession session*/){
        String text = kaptchaProducer.createText();
        BufferedImage image = kaptchaProducer.createImage(text);

        //存储验证码(简洁版 存入session)
//        session.setAttribute("kaptcha", text);
        //存储验证码 存入redis
        //用户临时凭证
        String kaptchaOwner = CommunityUtil.generateUUID();
        Cookie cookie = new Cookie("kaptchaOwner", kaptchaOwner);
        cookie.setMaxAge(60);
        cookie.setPath(contextPath);
        response.addCookie(cookie);
        //将验证码存入redis
        String redisKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
        redisTemplate.opsForValue().set(redisKey, text, 60, TimeUnit.SECONDS);
第三步:登陆时判断用户输入验证码是否正确
    @RequestMapping(path = "/login", method = RequestMethod.POST)
    public String login(String username, String password, String code, boolean remeberme,
                        Model model, /* HttpSession session */ HttpServletResponse response , 
@CookieValue(value = "kaptchaOwner",required = false/*当取不到cookie时也能正常运行 */) String kaptchaOwner){
//        读取验证码
        String kaptcha =null;
//        String kaptcha = (String) session.getAttribute("kaptcha");
        //通过redis获取验证码
        if(StringUtils.isNotBlank(kaptchaOwner)){
            String kaptchaKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
            kaptcha = (String) redisTemplate.opsForValue().get(kaptchaKey);
        }

2.优化登陆凭证

数据结构为String - Object(redis配置了value的序列化方式为json,object以json格式存储)故为String - String。

第一步:生成redisKey
//用户登陆凭证
    private static final String PREFIX_TICKET = "ticket";
    public static String getTicketKey(String ticket){
        return PREFIX_TICKET+SPLIT+ticket;
    }
第二步:用户登陆时,将登陆凭证存入redis (在service层,ticket将不会存入mysql
String loginTicketKey = RedisKeyUtil.getTicketKey(loginTicket.getTicket());

redisTemplate.opsForValue().set(loginTicketKey, loginTicket);
第三步:用户访问时,判断ticket是否有效时,获取ticket
    public LoginTicket getTicket(String ticket){
        String ticketKey = RedisKeyUtil.getTicketKey(ticket);
        LoginTicket loginTicket = (LoginTicket) redisTemplate.opsForValue().get(ticketKey);
        return loginTicket;
    }
第四步:用户登出时废除ticket
    public void logout(String ticket){
//        int rows = loginTicketMapper.updateStatus(ticket, 1);
        String ticketKey = RedisKeyUtil.getTicketKey(ticket);
        LoginTicket loginTicket = (LoginTicket )redisTemplate.opsForValue().get(ticketKey);
        loginTicket.setStatus(1);//作废
        redisTemplate.opsForValue().set(ticketKey, loginTicket);

    }

3.缓存用户信息

第一步:设置rediskey
    private static final String PREFIX_USER = "user";
    public static String getUserKey(int userid){
        return PREFIX_USER+SPLIT+userid;
    }
第二步:在获取用户的地方,通过redis获取用户。

如果获取不到,则从mysql中获取到用户信息存放到redis中。当更新用户信息时,删除掉redis的缓存信息。

先定义三个方法,getRedisCache,initCache,clearCache

private User getCache(int userId){
        String redisKey = RedisKeyUtil.getUserKey(userId);
        return (User) redisTemplate.opsForValue().get(redisKey);
    }
    private User initCache(int userId){
        User user = userMapper.selectById(userId);
        String redisKey = RedisKeyUtil.getUserKey(userId);
        redisTemplate.opsForValue().set(redisKey, user, 3600, TimeUnit.SECONDS);
        return user;
    }

    private void clearCache(int userId){
        String redisKey = RedisKeyUtil.getUserKey(userId);
        redisTemplate.delete(redisKey);
    }

然后替换获取用户的代码   在service层: 

    public User findUserById(int id){
//        User user = userMapper.selectById(id);
        //增加redis缓存用户信息
        //1.从redis取用户信息
        //2.redis没有则更新redis
        //3.用户更新删除 reids缓存
        User user = getCache(id);
        if(user == null){
            user = initCache(id);
        }
        return user;
    }

在update用户数据的后面添加clearCache

    public int updateHeader(User user, String headUrl){
//        return userMapper.updateHeader(user.getId(), headUrl);
        int rows = userMapper.updateHeader(user.getId(), headUrl);
        clearCache(user.getId());
        return rows;
    }

ps:redis的持久化

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值