点击关注/粉丝数时,可以显示出关注/粉丝列表
// 查询某用户关注的“人”
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);//通过ID查询用户
map.put("user", user);
//关注的时间,把key,传入,看取那个id的分数
Double score = redisTemplate.opsForZSet().score(followeeKey, targetId);
map.put("followTime", new Date(score.longValue()));//小数转成long类型
list.add(map);
}
return list;//查询某个用户所关注的人
}
// 查询某用户的粉丝
//逻辑同上述方法类似
public List<Map<String, Object>> findFollowers(int userId, int offset, int limit) {
String followerKey = RedisKeyUtil.getFollowerKey(ENTITY_TYPE_USER, userId);
//redis的range方法,返回的set是有序的,虽然JAVA jdk中的set是无序的,但它有自己的实现方法
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;
}
//将某用户的id传入,因为是查询,所以请求方法是get
@RequestMapping(path = "/followees/{userId}", method = RequestMethod.GET)
public String getFollowees(@PathVariable("userId") int userId, Page page, Model model) {//Page支持分页,Model,传入模板
User user = userService.findUserById(userId);// userService需要注入
if (user == null) {//保证ID是正确的
throw new RuntimeException("该用户不存在!");
}
model.addAttribute("user", user);//如果有用户,要将用户传给页面,因为前端页面也需要用到
page.setLimit(5);
page.setPath("/followees/" + userId);
//查询一共有多少条数据;ENTITY_TYPE_USER:注意要实现接口CommunityConstant
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";//返回到模板处理
}
@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";//返回到模板处理
}
private boolean hasFollowed(int userId) {//封装方法,判断当前用户对某人的关注状态
if (hostHolder.getUser() == null) {//当前无用户登录
return false;
}
return followService.hasFollowed(hostHolder.getUser().getId(), ENTITY_TYPE_USER, userId);
}
}
doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="icon" href="https://static.nowcoder.com/images/logo_87_87.png"/>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" crossorigin="anonymous">
<link rel="stylesheet" th:href="@{/css/global.css}" />
<title>牛客网-关注title>
head>
<body>
<div class="nk-container">
<header class="bg-dark sticky-top" th:replace="index::header">
<div class="container">
<nav class="navbar navbar-expand-lg navbar-dark">
<a class="navbar-brand" href="#">a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon">span>
button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item ml-3 btn-group-vertical">
<a class="nav-link" href="../index.html">首页a>
li>
<li class="nav-item ml-3 btn-group-vertical">
<a class="nav-link position-relative" href="letter.html">消息<span class="badge badge-danger">12span>a>
li>
<li class="nav-item ml-3 btn-group-vertical">
<a class="nav-link" href="register.html">注册a>
li>
<li class="nav-item ml-3 btn-group-vertical">
<a class="nav-link" href="login.html">登录a>
li>
<li class="nav-item ml-3 btn-group-vertical dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<img src="http://images.nowcoder.com/head/1t.png" class="rounded-circle" style="width:30px;"/>
a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item text-center" href="profile.html">个人主页a>
<a class="dropdown-item text-center" href="setting.html">账号设置a>
<a class="dropdown-item text-center" href="login.html">退出登录a>
<div class="dropdown-divider">div>
<span class="dropdown-item text-center text-secondary">nowcoderspan>
div>
li>
ul>
<form class="form-inline my-2 my-lg-0" action="search.html">
<input class="form-control mr-sm-2" type="search" aria-label="Search" />
<button class="btn btn-outline-light my-2 my-sm-0" type="submit">搜索button>
form>
div>
nav>
div>
header>
<div class="main">
<div class="container">
<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}">Nowcoderi> 关注的人
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}">Nowcoderi> 的人
a>
li>
ul>
<a th:href="@{|/user/profile/${user.id}|}" class="text-muted position-absolute rt-0">返回个人主页>a>
div>
<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:25i>
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'}">关注TAbutton>
div>
div>
li>
ul>
<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="#">1a>li>
<li class="page-item"><a class="page-link" href="#">2a>li>
<li class="page-item"><a class="page-link" href="#">3a>li>
<li class="page-item"><a class="page-link" href="#">4a>li>
<li class="page-item"><a class="page-link" href="#">5a>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>
div>
div>
<footer class="bg-dark">
<div class="container">
<div class="row">
<div class="col-4 qrcode">
<img src="https://uploadfiles.nowcoder.com/app/app_download.png" class="img-thumbnail" style="width:136px;" />
div>
<div class="col-8 detail-info">
<div class="row">
<div class="col">
<ul class="nav">
<li class="nav-item">
<a class="nav-link text-light" href="#">关于我们a>
li>
<li class="nav-item">
<a class="nav-link text-light" href="#">加入我们a>
li>
<li class="nav-item">
<a class="nav-link text-light" href="#">意见反馈a>
li>
<li class="nav-item">
<a class="nav-link text-light" href="#">企业服务a>
li>
<li class="nav-item">
<a class="nav-link text-light" href="#">联系我们a>
li>
<li class="nav-item">
<a class="nav-link text-light" href="#">免责声明a>
li>
<li class="nav-item">
<a class="nav-link text-light" href="#">友情链接a>
li>
ul>
div>
div>
<div class="row">
<div class="col">
<ul class="nav btn-group-vertical company-info">
<li class="nav-item text-white-50">
公司地址:北京市朝阳区大屯路东金泉时代3-2708北京牛客科技有限公司
li>
<li class="nav-item text-white-50">
联系方式:010-60728802(电话) admin@nowcoder.com
li>
<li class="nav-item text-white-50">
牛客科技©2018 All rights reserved
li>
<li class="nav-item text-white-50">
京ICP备14055008号-4
<img src="http://static.nowcoder.com/company/images/res/ghs.png" style="width:18px;" />
京公网安备 11010502036488号
li>
ul>
div>
div>
div>
div>
div>
footer>
div>
<script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous">script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" crossorigin="anonymous">script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" crossorigin="anonymous">script>
<script th:src="@{/js/global.js}">script>
<script th:src="@{/js/profile.js}">script>
body>
html>
doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="icon" href="https://static.nowcoder.com/images/logo_87_87.png"/>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" crossorigin="anonymous">
<link rel="stylesheet" th:href="@{/css/global.css}" />
<title>牛客网-关注title>
head>
<body>
<div class="nk-container">
<header class="bg-dark sticky-top" th:replace="index::header">
<div class="container">
<nav class="navbar navbar-expand-lg navbar-dark">
<a class="navbar-brand" href="#">a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon">span>
button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item ml-3 btn-group-vertical">
<a class="nav-link" href="../index.html">首页a>
li>
<li class="nav-item ml-3 btn-group-vertical">
<a class="nav-link position-relative" href="letter.html">消息<span class="badge badge-danger">12span>a>
li>
<li class="nav-item ml-3 btn-group-vertical">
<a class="nav-link" href="register.html">注册a>
li>
<li class="nav-item ml-3 btn-group-vertical">
<a class="nav-link" href="login.html">登录a>
li>
<li class="nav-item ml-3 btn-group-vertical dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<img src="http://images.nowcoder.com/head/1t.png" class="rounded-circle" style="width:30px;"/>
a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item text-center" href="profile.html">个人主页a>
<a class="dropdown-item text-center" href="setting.html">账号设置a>
<a class="dropdown-item text-center" href="login.html">退出登录a>
<div class="dropdown-divider">div>
<span class="dropdown-item text-center text-secondary">nowcoderspan>
div>
li>
ul>
<form class="form-inline my-2 my-lg-0" action="search.html">
<input class="form-control mr-sm-2" type="search" aria-label="Search" />
<button class="btn btn-outline-light my-2 my-sm-0" type="submit">搜索button>
form>
div>
nav>
div>
header>
<div class="main">
<div class="container">
<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}">Nowcoderi> 关注的人
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}">Nowcoderi> 的人
a>
li>
ul>
<a th:href="@{|/user/profile/${user.id}|}" class="text-muted position-absolute rt-0">返回个人主页>a>
div>
<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:25i>
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'}">关注TAbutton>
div>
div>
li>
ul>
<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="#">1a>li>
<li class="page-item"><a class="page-link" href="#">2a>li>
<li class="page-item"><a class="page-link" href="#">3a>li>
<li class="page-item"><a class="page-link" href="#">4a>li>
<li class="page-item"><a class="page-link" href="#">5a>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>
div>
div>
<footer class="bg-dark">
<div class="container">
<div class="row">
<div class="col-4 qrcode">
<img src="https://uploadfiles.nowcoder.com/app/app_download.png" class="img-thumbnail" style="width:136px;" />
div>
<div class="col-8 detail-info">
<div class="row">
<div class="col">
<ul class="nav">
<li class="nav-item">
<a class="nav-link text-light" href="#">关于我们a>
li>
<li class="nav-item">
<a class="nav-link text-light" href="#">加入我们a>
li>
<li class="nav-item">
<a class="nav-link text-light" href="#">意见反馈a>
li>
<li class="nav-item">
<a class="nav-link text-light" href="#">企业服务a>
li>
<li class="nav-item">
<a class="nav-link text-light" href="#">联系我们a>
li>
<li class="nav-item">
<a class="nav-link text-light" href="#">免责声明a>
li>
<li class="nav-item">
<a class="nav-link text-light" href="#">友情链接a>
li>
ul>
div>
div>
<div class="row">
<div class="col">
<ul class="nav btn-group-vertical company-info">
<li class="nav-item text-white-50">
公司地址:北京市朝阳区大屯路东金泉时代3-2708北京牛客科技有限公司
li>
<li class="nav-item text-white-50">
联系方式:010-60728802(电话) admin@nowcoder.com
li>
<li class="nav-item text-white-50">
牛客科技©2018 All rights reserved
li>
<li class="nav-item text-white-50">
京ICP备14055008号-4
<img src="http://static.nowcoder.com/company/images/res/ghs.png" style="width:18px;" />
京公网安备 11010502036488号
li>
ul>
div>
div>
div>
div>
div>
footer>
div>
<script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous">script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" crossorigin="anonymous">script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" crossorigin="anonymous">script>
<script th:src="@{/js/global.js}">script>
<script th:src="@{/js/profile.js}">script>
body>
html>
关键点