3. Follow, cancel, follow and fan list

#Follow and cancel

[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>
```

Keywords: Java IntelliJ IDEA Redis Spring Boot Back-end

Added by wyvern on Sun, 06 Mar 2022 14:01:22 +0200