[the transfer of external chain pictures fails, and the source station may have anti-theft chain mechanism. It is recommended to save the pictures and upload them directly (img-xwZ8AhZC-1646567007779)(D:\TyporaNotes \ Niuke forum project \ Chapter III pictures \ image-20220306104936732.png)]
Design the key and Value of Redis
- Query the attention list, query the entity concerned by a user, pass in the userId of the user to be found, and then pass in the type of attention list; Use an ordered set to put in the id and current time of the entity, so as to facilitate the subsequent display of attention time and other functions.
- Query the fan list, query the fans owned by an entity, and enter the type and id of the entity; Use an ordered collection to put the id and current time of the follower, so as to facilitate the subsequent display of the time of interest and other functions.
private static final String PREFIX_FOLLOWEE = "followee:"; private static final String PREFIX_FOLLOWER = "follower:";
//Entity concerned by a user //followee: userid:entityType -->zset(entityid,now) public static String getFolloweeKey(int userId,int entityType) { return PREFIX_FOLLOWEE+userId+SPLIT+entityType; } //Query the fan list of an entity //follower:entityType:entityId -->zset(userId,now) public static String getFollowerKey(int entityType,int entityId) { return PREFIX_FOLLOWER+entityType+SPLIT+entityId; }
Develop business of concern and customs clearance
Because two key s need to be set, Redis enabled business operations use ms of the current time
@Service public class FollowService { @Autowired private RedisTemplate redisTemplate; //Attention needs to set up attention and fan use transactions public void follow(int userId,int entityType,int entityId) { redisTemplate.execute(new SessionCallback() { @Override public Object execute(RedisOperations operations) throws DataAccessException { String followeeKey= RedisKeyUtil.getFolloweeKey(userId,entityType); String followerKey=RedisKeyUtil.getFollowerKey(entityType,entityId); //Start Redis transaction operations.multi(); operations.opsForZSet().add(followeeKey,entityId,System.currentTimeMillis()); operations.opsForZSet().add(followerKey,userId,System.currentTimeMillis()); //Commit transaction return operations.exec(); } }); } //To close the business, you need to set the transaction of canceling attention and reducing fans by one public void unfollow(int userId,int entityType,int entityId) { redisTemplate.execute(new SessionCallback() { @Override public Object execute(RedisOperations operations) throws DataAccessException { String followeeKey= RedisKeyUtil.getFolloweeKey(userId,entityType); String followerKey=RedisKeyUtil.getFollowerKey(entityType,entityId); //Start Redis transaction operations.multi(); operations.opsForZSet().remove(followeeKey,entityId); operations.opsForZSet().remove(followerKey,userId); //Commit transaction return operations.exec(); } }); } }
Develop the Controller and accept the close request
The current user pays attention to an entity
@Controller public class FollowController { @Autowired FollowService followService; //Get User information from logged in users @Autowired HostHolder hostHolder; //follow @RequestMapping(value = "/follow",method = RequestMethod.POST) @ResponseBody public String follow(int entityType,int entityId){ User user = hostHolder.getUser(); followService.follow(user.getId(),entityType,entityId); //Return JSON string return CommunityUtil.getJSONString(0,"Paid attention!"); } //Cancel attention @RequestMapping(value = "/unfollow",method = RequestMethod.POST) @ResponseBody public String unfollow(int entityType,int entityId){ User user = hostHolder.getUser(); followService.unfollow(user.getId(),entityType,entityId); //Return JSON string return CommunityUtil.getJSONString(0,"Attention cancelled!"); } }
Modify the js of the home page
Get the userId, use the hidden box, and then get the value of the previous node of the button
$(function(){ $(".follow-btn").click(follow); }); function follow() { var btn = this; if($(btn).hasClass("btn-info")) { // Focus on TA $.post( CONTEXT_PATH+"/follow", {"entityType":3,"entityId":$(btn).prev().val()}, function (data) { data=$.parseJSON(data); if(data.code==0) { window.location.reload(); }else { alert(data.msg); } } ) $(btn).text("Paid attention to").removeClass("btn-info").addClass("btn-secondary"); } else { // Cancel attention $.post( CONTEXT_PATH+"/unFollow", {"entityType":3,"entityId":$(btn).prev().val()}, function (data) { data=$.parseJSON(data); if(data.code==0) { window.location.reload(); }else { alert(data.msg); } } ) $(btn).text("follow TA").removeClass("btn-secondary").addClass("btn-info"); } }
Increase the number of followers and businesses concerned
//Query the number of entities concerned public long findFolloweeCount(int userId,int entityType) { String followeeKey=RedisKeyUtil.getFolloweeKey(userId,entityType); return redisTemplate.opsForZSet().zCard(followeeKey); } //Query the number of fans public long findFollowerCount(int entityType,int entityId) { String followerKey=RedisKeyUtil.getFollowerKey(entityType,entityId); return redisTemplate.opsForZSet().zCard(followerKey); } //Query whether the current user has paid attention to the entity public boolean hasFollowed(int userId,int entityType,int entityId) { String followeeKey=RedisKeyUtil.getFolloweeKey(userId,entityType); //Check whether the current key has a score to know whether it has been followed return redisTemplate.opsForZSet().score(followeeKey,entityId)!=null; }
Controller for home page
//Number of concerns long followeeCount = followService.findFolloweeCount(userId, ENTITY_TYPE_USER); model.addAttribute("followeeCount", followeeCount); //Number of fans long followerCount = followService.findFollowerCount(ENTITY_TYPE_USER, userId); model.addAttribute("followerCount", followerCount); //Have you paid attention boolean hasFollowed=false; if(hostHolder.getUser()!=null) { hasFollowed=followService.hasFollowed(hostHolder.getUser().getId(),ENTITY_TYPE_USER,userId); } model.addAttribute("hasFollowed", hasFollowed);
Modify page
<!-- personal information --> <div class="media mt-5"> <img th:src="${user.headerUrl}" class="align-self-start mr-4 rounded-circle" alt="User Avatar" style="width:50px;"> <div class="media-body"> <h5 class="mt-0 text-warning"> <span th:utext="${user.username}">nowcoder</span> <input type="hidden" id="entityId" th:value="${user.id}"> <button type="button" th:class="|btn ${hasFollowed?'btn-secondary':'btn-info'} btn-sm float-right mr-5 follow-btn|" th:text="${hasFollowed?'Paid attention to':'follow TA'}" th:if="${loginUser!=null&&loginUser.id!=user.id}">follow TA</button> </h5> <div class="text-muted mt-3"> <span>Registered in <i class="text-muted" th:text="${#dates.format(user.createTime,'yyyy-MM-dd HH:mm:ss')}">2015-06-12 15:20:12</i></span> </div> <div class="text-muted mt-3 mb-5"> <span>Attention <a class="text-primary" href="followee.html" th:text="${followeeCount}">5</a> people</span> <span class="ml-4">Followers <a class="text-primary" href="follower.html"th:text="${followerCount}">123</a> people</span> <span class="ml-4">Got <i class="text-danger" th:text="${likeCount}">87</i> A praise</span> </div> </div> </div>
List of followers
Business layer
-
Paging is required, so you need to pass in paging parameters;
-
Find out the target id by using the time flashback of the key (i.e. the collection of people concerned by the user);
-
Traverse the id of user and find the corresponding information through ids;
-
Find out the corresponding score through the key and id, that is, the creation time, and put it into the map;
-
// Query the person concerned by a user 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.queryUserById(targetId); map.put("user", user); Double score = redisTemplate.opsForZSet().score(followeeKey, targetId); map.put("followTime", new Date(score.longValue())); list.add(map); } return list; } // Query the fans of a user 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.queryUserById(targetId); map.put("user", user); Double score = redisTemplate.opsForZSet().score(followerKey, targetId); map.put("followTime", new Date(score.longValue())); list.add(map); } return list; }
Presentation layer
- First, save the user information of the user in the template to facilitate modification;
- Set paging parameters, starting page and number of entries per page;
- Call Service to query the user's attention list;
- Traverse the userList to obtain the information of each user in the attention list;
- It is necessary to judge whether the logged in user pays attention to other users in the list to display not paid attention and paid attention;
//Find the following list of this user @RequestMapping(path = "/followees/{userId}", method = RequestMethod.GET) public String getFollowees(@PathVariable("userId") int userId, Page page, Model model) { //Query current user information User user = userService.queryUserById(userId); if (user == null) { throw new RuntimeException("The user does not exist!"); } //Save the current user's information to the template model.addAttribute("user", user); //Set paging parameters page.setLimit(5); page.setPath("/followees/" + userId); page.setRows((int) followService.findFolloweeCount(userId, ENTITY_TYPE_USER)); //Call Service to query the user's attention list List<Map<String, Object>> userList = followService.findFollowees(userId, page.getOffset(), page.getLimit()); if (userList != null) { //Traverse the userList to get the information of each user in the following list 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"; } //View the user's fan list @RequestMapping(path = "/followers/{userId}", method = RequestMethod.GET) public String getFollowers(@PathVariable("userId") int userId, Page page, Model model) { User user = userService.queryUserById(userId); if (user == null) { throw new RuntimeException("The user does not exist!"); } 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"); //Judge whether the currently logged in user has paid attention to other people in the fan list map.put("hasFollowed", hasFollowed(u.getId())); } } model.addAttribute("users", userList); return "/site/follower"; } //Judge whether the logged in user pays attention to other users in the list private boolean hasFollowed(int userId) { if (hostHolder.getUser() == null) { return false; } return followService.hasFollowed(hostHolder.getUser().getId(), ENTITY_TYPE_USER, userId); }
Modify page
1. Modify the following and follower request, change it to the corresponding request, and spell the id of the current user
<div class="text-muted mt-3 mb-5"> <span>Attention <a class="text-primary" th:href="@{|/followees/${user.id}|}" th:text="${followeeCount}">5</a> people</span> <span class="ml-4">Followers <a class="text-primary" th:href="@{|/followers/${user.id}|}" th:text="${followerCount}">123</a> people</span> <span class="ml-4">Got <i class="text-danger" th:text="${likeCount}">87</i> A praise</span> </div>
2. Traverse the usersList collection and modify the information of each user
<!-- Follow list --> <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="User Avatar" > </a> <div class="media-body"> <h6 class="mt-0 mb-3"> <span class="text-success" th:utext="${map.user.username}">Idlers in the Rockies</span> <span class="float-right text-muted font-size-12"> Focus on <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?'Paid attention to':'follow TA'}">follow TA</button> </div> </div> </li> </ul>
```</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?'Paid attention to':'follow TA'}">follow TA</button> </div> </div>