牛客网后端项目实战(二十九):关注列表、粉丝列表

本文详细介绍了如何利用Redis的有序集合进行用户关注和粉丝的分页查询,包括业务层的实现逻辑,如查询关注的人和粉丝,以及表现层的处理,如处理HTTP请求、构建模板。同时,文章还展示了如何处理关注列表和分页显示,并实现了关注状态的判断功能。
摘要由CSDN通过智能技术生成
  • 业务层
    • 查询某个用户关注的人,支持分页。
    • 查询某个用户的粉丝,支持分页。
  • 表现层
    • 处理“查询关注的人”、“查询粉丝”请求。
    • 编写“查询关注的人”、“查询粉丝”模板。

查询某个用户关注的人,支持分页

业务层

    // 查询某用户关注的人
    public List<Map<String, Object>> findFollowees(int userId, int offset, int limit) {
        String followeeKey = RedisKeyUtil.getFolloweeKey(userId, ENTITY_TYPE_USER);
        Set<Integer> targetIds = redisTemplate.opsForZSet().reverseRange(followeeKey, offset, offset + limit - 1);

        if (targetIds == null) {
            return null;
        }

        List<Map<String, Object>> list = new ArrayList<>();
        for (Integer targetId : targetIds) {
            Map<String, Object> map = new HashMap<>();
            User user = userService.findUserById(targetId);
            map.put("user", user);
            Double score = redisTemplate.opsForZSet().score(followeeKey, targetId);
            map.put("followTime", new Date(score.longValue()));
            list.add(map);
        }

        return list;
    }

1、获得Key
2、使用 redisTemplate.opsForZSet().reverseRange() 方法,第二第三个参数:[start end],获得指定范围内从大到小的 Id集合
3、当id集合不为空时,遍历id集合放在 List<Map<String, Object>>里,Map是用来存放 关注的用户、关注时间(“user”,“followTime”)
4、关注时间作为分数存放在followeeKey,所以使用方法 redisTemplate.opsForZSet().score() 取出,new Date(score.longValue()) 可以重新转换成日期格式

    @RequestMapping(path = "/followees/{userId}", method = RequestMethod.GET)
    public String getFollowees(@PathVariable("userId") int userId, Page page, Model model) {
        User user = userService.findUserById(userId);
        if (user == null) {
            throw new RuntimeException("该用户不存在!");
        }
        model.addAttribute("user", user);

        page.setLimit(5);
        page.setPath("/followees/" + userId);
        page.setRows((int) followService.findFolloweeCount(userId, ENTITY_TYPE_USER));

        List<Map<String, Object>> userList = followService.findFollowees(userId, page.getOffset(), page.getLimit());
        if (userList != null) {
            for (Map<String, Object> map : userList) {
                User u = (User) map.get("user");
                map.put("hasFollowed", hasFollowed(u.getId()));
            }
        }
        model.addAttribute("users", userList);

        return "/site/followee";
    }

    private boolean hasFollowed(int userId) {
        if (hostHolder.getUser() == null) { // 当前用户未登录,那么肯定不显示/没有关注
            return false;
        }
		// 否则,查询当前登录用户是否关注了 userId;ENTITY_TYPE_USER 表示实体是用户类型
        return followService.hasFollowed(hostHolder.getUser().getId(), ENTITY_TYPE_USER, userId);
    }

1、userId 由路径传入
2、分页显示,使用Page类。page.setRows((int) followService.findFolloweeCount(userId, ENTITY_TYPE_USER)); 计算总共需要显示多少行数据
3、List里面的用户需要判断是否被登录用户关注(用于判断是否显示 “关注” 按钮),重写hasFollowed()。

处理模板

followee.html
1、处理超链接

				<div class="position-relative">
					<!-- 选项 -->
					<ul class="nav nav-tabs mb-3">
						<li class="nav-item">
							<a class="nav-link position-relative active" th:href="@{|/followees/${user.id}|}">
								<i class="text-info" th:utext="${user.username}">Nowcoder</i> 关注的人
							</a>
						</li>
						<li class="nav-item">
							<a class="nav-link position-relative" th:href="@{|/followers/${user.id}|}">
								关注 <i class="text-info" th:utext="${user.username}">Nowcoder</i> 的人
							</a>
						</li>
					</ul>
					<a th:href="@{|/user/profile/${user.id}|}" class="text-muted position-absolute rt-0">返回个人主页&gt;</a>
				</div>

1)th:href="@{|/followees/${user.id}|}" 是 user.id 关注的人的访问路径

2、处理分页(重用之前代码)

				<!-- 分页 -->
				<nav class="mt-5" th:replace="index::pagination">
					<ul class="pagination justify-content-center">
						<li class="page-item"><a class="page-link" href="#">首页</a></li>
						<li class="page-item disabled"><a class="page-link" href="#">上一页</a></li>
						<li class="page-item active"><a class="page-link" href="#">1</a></li>
						<li class="page-item"><a class="page-link" href="#">2</a></li>
						<li class="page-item"><a class="page-link" href="#">3</a></li>
						<li class="page-item"><a class="page-link" href="#">4</a></li>
						<li class="page-item"><a class="page-link" href="#">5</a></li>
						<li class="page-item"><a class="page-link" href="#">下一页</a></li>
						<li class="page-item"><a class="page-link" href="#">末页</a></li>
					</ul>
				</nav>

3、处理关注列表

				<!-- 关注列表 -->
				<ul class="list-unstyled">
					<li class="media pb-3 pt-3 mb-3 border-bottom position-relative" th:each="map:${users}">
						<a th:href="@{|/user/profile/${map.user.id}|}">
							<img th:src="${map.user.headerUrl}" class="mr-4 rounded-circle user-header" alt="用户头像" >
						</a>
						<div class="media-body">
							<h6 class="mt-0 mb-3">
								<span class="text-success" th:utext="${map.user.username}">落基山脉下的闲人</span>
								<span class="float-right text-muted font-size-12">
									关注于 <i th:text="${#dates.format(map.followTime,'yyyy-MM-dd HH:mm:ss')}">2019-04-28 14:13:25</i>
								</span>
							</h6>
							<div>
								<input type="hidden" id="entityId" th:value="${map.user.id}">
								<button type="button" th:class="|btn ${map.hasFollowed?'btn-secondary':'btn-info'} btn-sm float-right follow-btn|"
									th:if="${loginUser!=null && loginUser.id!=map.user.id}" th:text="${map.hasFollowed?'已关注':'关注TA'}">关注TA</button>
							</div>
						</div>
					</li>
				</ul>
1)th:each="map:${users}" 遍历 关注的用户 users

2)关注的用户信息:
th:href="@{|/user/profile/${map.user.id}|}"
th:src="${map.user.headerUrl}"
th:utext="${map.user.username}"
th:text="${#dates.format(map.followTime,'yyyy-MM-dd HH:mm:ss')}" 关注的时间信息

3)”关注“ 按钮处理,与上一节逻辑相同,即要实现:异步关注/取关操作;按钮样式文字·转换;是否显示按钮
<div>
	<input type="hidden" id="entityId" th:value="${map.user.id}">
	<button type="button" th:class="|btn ${map.hasFollowed?'btn-secondary':'btn-info'} btn-sm float-right follow-btn|"
		th:if="${loginUser!=null && loginUser.id!=map.user.id}" th:text="${map.hasFollowed?'已关注':'关注TA'}">关注TA</button>
</div>

查询某个用户的粉丝

与查询某个用户的关注逻辑差不多,仅给出代码

    // 查询某用户的粉丝
    public List<Map<String, Object>> findFollowers(int userId, int offset, int limit) {
        String followerKey = RedisKeyUtil.getFollowerKey(ENTITY_TYPE_USER, userId);
        Set<Integer> targetIds = redisTemplate.opsForZSet().reverseRange(followerKey, offset, offset + limit - 1);

        if (targetIds == null) {
            return null;
        }

        List<Map<String, Object>> list = new ArrayList<>();
        for (Integer targetId : targetIds) {
            Map<String, Object> map = new HashMap<>();
            User user = userService.findUserById(targetId);
            map.put("user", user);
            Double score = redisTemplate.opsForZSet().score(followerKey, targetId);
            map.put("followTime", new Date(score.longValue()));
            list.add(map);
        }

        return list;
    }


    @RequestMapping(path = "/followers/{userId}", method = RequestMethod.GET)
    public String getFollowers(@PathVariable("userId") int userId, Page page, Model model) {
        User user = userService.findUserById(userId);
        if (user == null) {
            throw new RuntimeException("该用户不存在!");
        }
        model.addAttribute("user", user);

        page.setLimit(5);
        page.setPath("/followers/" + userId);
        page.setRows((int) followService.findFollowerCount(ENTITY_TYPE_USER, userId));

        List<Map<String, Object>> userList = followService.findFollowers(userId, page.getOffset(), page.getLimit());
        if (userList != null) {
            for (Map<String, Object> map : userList) {
                User u = (User) map.get("user");
                map.put("hasFollowed", hasFollowed(u.getId()));
            }
        }
        model.addAttribute("users", userList);

        return "/site/follower";
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值