Public account: MarkerHub (pay attention to get more project resources)

Eblog codebase: github.com/markerhub/e…

Eblog project video: www.bilibili.com/video/BV1ri…


Development Document Catalog:

(Eblog) 1. Set up the project architecture and initialized the home page

(EBLOG) 2. Integrate Redis and project elegant exception handling and return result encapsulation

(eblog) 3, using Redis zset ordered set to achieve a hot feature of the week

(EBLOG) 4, customize Freemaker label to achieve blog homepage data filling

(Eblog) 5, blog classification filling, login registration logic

(EBLOG) 6. Set up blog publishing collection and user center

(eblog) 7, message asynchronous notification, details adjustment

8. Blog search engine development and background selection

(Eblog) 9. Instant group chat development, chat history, etc


Front and back end separation project vueblog please click here: super detailed! 4 hours to develop a SpringBoot+ Vue front and back separated blog project!!


1. User center – 1

In the last article, we completed user login and registration. Now let’s do the following things that can be done after login, such as entering the user center

My home page

First let’s take a look at my home page:

This is the display effect after clicking the user’s home page, it is the basic information of the user, the left side is the recently published article, the right side is the recent operation, etc. (comment, publication, etc.), we will not do the recent operation part for the moment, you can finish by yourself after class.

So this page is very simple to complete, just need to upload the user’s basic information, and recent articles to the page on the line:

  • com.example.controller.UserController
@Controller
public class UserController extends BaseController{
    @RequestMapping("/user/{id:\\d*}") public String home(@PathVariable Long id) { User user = userService.getById(id); user.setPassword(null); Date date30Before = dateutil.offsetDay (new Date(), -30).tojdkDate (); List<Post> posts = postService.list(new QueryWrapper<Post>() .eq("user_id", id)
                .ge("created", date30Before)
                .orderByDesc("created"));
        req.setAttribute("user", user);
        req.setAttribute("posts", posts);
        return "user/home"; }}Copy the code

As for the page, it is a direct presentation of data, not to say more! ~

The user center

When YOU click on the user center, there’s a TAB that divides my posts and my favorites

I sent you a post

Let’s take a look at the post I posted. This is done by installing the userId query post directly

  • com.example.controller.CenterController
@GetMapping("")
public String index() {
    Page page = getPage();
    log.info("--------------> Access to personal Center");
    QueryWrapper<Post> wrapper = new QueryWrapper<Post>().eq("user_id", getProfileId())
            .orderByDesc("created");
    IPage<Post> pageData = postService.page(page, wrapper);
    req.setAttribute("pageData", pageData);
    return "center/index";
}

Copy the code

There seems to be no knowledge point to say ~

My collection of stickers

I collect stickers as they relate to tables

  • UserCollection

  • Post

Therefore, associated query is required

@GetMapping("/collection")
public String collection() {
    Page page = getPage();
    QueryWrapper queryWrapper = new QueryWrapper<>().eq("u.user_id", getProfileId()).orderByAsc("u.created");
    IPage<Post> pageData = collectionService.paging(page, queryWrapper);
    req.setAttribute("pageData", pageData);
    return "center/collection"; } Collectionservice.paging's final mapper looks like this: <select id="selectPosts" resultType="com.example.entity.Post">
  select *
    from user_collection u
    left join post p on u.post_id = p.id
    ${ew.customSqlSegment}
</select>

Copy the code

So our condition is U.user id. Use u. To distinguish which table userID is used. Then render the other page numbers directly into a macro:

<div style="text-align: center">
    <@page pageData></@page>
</div>

Copy the code

Basic setup

My profile

Ok, it’s all fairly simple, but let’s take a look at the basic Settings, which involve form submission and avatar changes.

My data and password change are just simple form submission. As we said last time, the form submission has been wrapped for us. We just need to tell what the jump link is after the successful form submission when we return, so my data submission is like this:

  • com.example.controller.CenterController
    @GetMapping("/setting")
    public String setting() {
        User user = userService.getById(getProfileId());
        user.setPassword(null);
        req.setAttribute("user", user);
        return "center/setting";
    }
    @ResponseBody
    @PostMapping("/setting")
    public Result postSetting(User user) {
        if(StringUtils.isEmpty(user.getUsername())){
            return Result.fail("User name cannot be empty");
        }
        User tempUser = userService.getById(getProfileId());
//        tempUser.setEmail(user.getEmail());
        tempUser.setUsername(user.getUsername());
        tempUser.setGender(user.getGender());
        tempUser.setSign(user.getSign());
        boolean isSucc = userService.updateById(tempUser);
        if(isSucc) {// Update shiro's information AccountProfile profile = getProfile(); profile.setUsername(user.getUsername()); profile.setGender(user.getGender()); }return isSucc ? Result.succ("Update successful", null, "/center/setting"): Result.fail("Update failed");
    }

Copy the code

AccountProfile profile = getProfile(); AccountProfile = getProfile(); “, and then reset it. One thing to notice about the front end is that I put alert=”true” on the confirm button

<button
 
class
=
"layui-btn"
 
key
=
"set-mine"
 
lay-filter
=
"*"
 
lay-submit
 
alert
=
"true"</button>Copy the code

Head portrait

From profile design to photo upload,

  • com.example.controller.CenterController
@ResponseBody
@PostMapping("/upload")
public Result upload(@RequestParam(value = "file") MultipartFile file,
                @RequestParam() String type) {
    if(file.isEmpty()) {
        return Result.fail("Upload failed"); String fileName = file.getoriginalfilename (); log.info("Upload file name:"+ fileName); String suffixName = filename.substring (filename.lastIndexof ())"."));
    log.info("Upload with the suffix:"+ suffixName); String filePath = constant.getuploaddir ();if ("avatar".equalsIgnoreCase(type)) {
        fileName = "/avatar/avatar_" + getProfileId() + suffixName;
    } else if ("post".equalsIgnoreCase(type)) {
        fileName = "/post/post_"+ DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_PATTERN) + suffixName; } File dest = new File(filePath + fileName); // Check if a directory existsif(! dest.getParentFile().exists()) { dest.getParentFile().mkdirs(); } try { file.transferTo(dest); log.info("The file path after successful upload is not:" + filePath + fileName);
        String path = filePath + fileName;
        String url = constant.getUploadUrl() + fileName;
        log.info("url ---> {}", url); User current = userService.getById(getProfileId()); current.setAvatar(url); userService.updateById(current); AccountProfile profile = getProfile(); profile.setAvatar(url);return Result.succ(url);
    } catch (IllegalStateException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return Result.succ(null);
}

Copy the code

The above logic is actually very simple, first get the image, rename it, save it to the specified location, then update the user, and update shiro’s profile picture information. File copying is all that matters

file
.
transferTo
(
dest
);
Copy the code

Everything else is more fixed. Also, because our image location is in the root directory:

This involves loading some static resources, so we need to add the location of this static resource in our MVC configuration,

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Autowired
    Constant constant;
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/upload/avatar/**")
                .addResourceLocations("file:///" + constant.getUploadDir() + "/avatar/");
    }

Copy the code

}

WebMvcConfig overrides the addResourceHandlers method, reads the upload folder, and Constant is a loaded Constant, which I extract into the configuration file

  • com.example.common.lang.Constant
@Data
@Component
public class Constant {
    @Value("${file.upload.dir}")
    private String uploadDir;
    @Value("${file.upload.url}")
    private String uploadUrl;
}

Copy the code

Then the configuration file looks like this:

  • application.yml
file:
  upload:
    dir: ${user.dir}/upload
    url: http://localhost:8080/upload    

Copy the code

User. dir means user root path, of course, now I am directly stored in the local hard disk, students can expand to upload to the seven niuyun cloud disk. Therefore in the pictures above avatar5. PNG, our access path is: http://localhost:8080/upload/avatar/avatar5.png

password

Password change is also a simple form submission

@ResponseBody
@PostMapping("/repass")
public Result repass(String nowpass, String pass, String repass) {
    if(! pass.equals(repass)) {return Result.fail("Two different passwords.");
    }
    User user = userService.getById(getProfileId());
    String nowPassMd5 = SecureUtil.md5(nowpass);
    if(! nowPassMd5.equals(user.getPassword())) {return Result.fail("Incorrect password");
    }
    user.setPassword(SecureUtil.md5(pass));
    userService.updateById(user);
    return Result.succ("Update successful", null, "/center/setting");
}

Copy the code

Doesn’t look like there’s anything to say?

My news

Display a message

My message includes two types, one is system message, the other is others comment on my article, or collect my article and other types.

Here I have two kinds, com. Example. The entity. The UserMessage type set up 2 types:

  • Type Indicates the message type. The value can be 1 comment message or 2 system message

When displaying the page, we need to choose the data style to display according to the type of type. Let’s look at the structure of the entity first

CREATE TABLE `user_message` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `from_user_id` bigint(20) DEFAULT NULL COMMENT 'User ID that sent the message',
  `to_user_id` bigint(20) NOT NULL COMMENT 'User ID receiving message',
  `post_id` bigint(20) DEFAULT NULL COMMENT 'Posts that messages may be associated with',
  `comment_id` bigint(20) DEFAULT NULL COMMENT 'Comments that messages may be associated with',
  `content` text,
  `type` tinyint(2) DEFAULT NULL COMMENT 'Message type, 1 comment message, 2 system message', `created` datetime NOT NULL, `modified` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; This table has a lot of ids in it, so we need to display the details of the comment, the source of the user, the title of the comment, etc., we need to associate a lot of tables, so we used another way to write SQL, let's first look at vo:  @Data public class MessageVo extends UserMessage { private String toUserName; private String fromUserName; private String postTitle; private String commentContent; }Copy the code

SQL > select * from ‘SQL’; SQL > select * from ‘SQL’;

<select id="selectMessages" resultType="com.example.vo.MessageVo">
    SELECT m.*,
        ( SELECT username FROM `user` WHERE id = m.to_user_id ) AS toUserName,
        ( SELECT username FROM `user` WHERE id = m.from_user_id ) AS fromUserName,
        ( SELECT title FROM `post` WHERE id = m.post_id ) AS postTitle,
        ( SELECT content FROM `comment` WHERE id = m.comment_id ) AS commentContent
    FROM `user_message` m
    ${ew.customSqlSegment}
</select>

Copy the code

Select * from touserID; select * from touserID; So it’s easy for us to write. When coding, we can query the result directly through the SQL:

@GetMapping("/message")
public String message() {
    Page<UserMessage> page = getPage();
    QueryWrapper wrapper = new QueryWrapper<UserMessage>()
            .eq("to_user_id", getProfileId())
            .orderByDesc("created");
    IPage<MessageVo> pageData = userMessageService.paging(page, wrapper);
    req.setAttribute("pageData", pageData);
    return "center/message";
}

Copy the code

The page side displays the data according to type

<#if mess.type == 1>
    <a href="/user/${mess.fromUserId}" target="_blank"><cite>${mess.fromUserName}</cite></a> comments on your article <a target="_blank" href="${base}/post/${mess.postId}"><cite>${mess.postTitle}</cite></a>
</#if>
<#if mess.type == 2>System message:${mess.content}
</#if>
</blockquote>

Copy the code

Delete the message

The original JS framework is already done for us, so we just need to fill in the data, especially the data-ID

<li
 
data-id
=
"${mess.id}"
>
Copy the code

The deletion logic is as follows:

@ResponseBody
@PostMapping("/message/remove")
public Result removeMsg(Long id, boolean all) {
    QueryWrapper<UserMessage> warapper = new QueryWrapper<UserMessage>()
            .eq("to_user_id", getProfileId()) .eq(! all,"id", id); / / can delete their own news Boolean res = userMessageService. Remove (warapper);return res ? Result.succ("Operation successful", null, "/center/message") : Result.fail("Delete failed");
}

Copy the code
  • static/res/mods/user.js

Some of the js links above need to be modified, please pay attention to it

2. User center – 2

In the user center above, we have finished displaying some data, now we will create some data, such as Posting articles, comments and so on. This involves some permissions after login, failed to log in the operation error prompt and other problems, we will make details in the process of code adjustment.

Publish edit blog

Let’s start with the comments page

The post is divided into a new publishing and editing, generally we according to whether have the blog id judgment, if you have id, then we will query, then the echo data submission to update, because the page is the same, so, a new publishing, editing, we use a method, from this page you can see, we need the data of blog entity, There’s a list of categories and a captcha.

@GetMapping("/post/edit")
public String edit() {
    String id = req.getParameter("id");
    Post post = new Post();
    if(! StringUtils.isEmpty(id)) { post = postService.getById(Long.valueOf(id)); Assert.notNull(post,"The article has been deleted!");
        Long profileId = getProfileId();
        Assert.isTrue(post.getUserId()==getProfileId(), "No permission to edit this article!");
    }
    List<Category> categories = categoryService.list(new QueryWrapper<Category>()
            .orderByDesc("order_num"));
    req.setAttribute("post", post);
    req.setAttribute("categories", categories);
    return "post/edit";
}

Copy the code

This is the controller that we jump to the edit page, from which we can see that we can judge whether it is an edit by whether the ID is empty. If it is an edit, we also use assertions to determine whether the article has been deleted, or whether it is a self-published article, etc.

Extension: Assertions and exceptions

Here’s a little detail. When we post a new post, the post we pass is a new post (), and the properties are empty, so when we use ${post.title} in the page, Freemaker will report an error, and we need to fix this so that it doesn’t report an error. The solution is simple, we just need to configure Freemaker in our configuration file not to report an error in this case:

spring:
  freemarker:
    cache: false
    settings:
      classic_compatible: true

Copy the code

After adding classic_compatible=true, we will not report an error if the property was empty.

  • Post mandatory field whether the article and meet the requirements

  • If it is an editor, then whether it is their own article

  • Authentication code problem

Ok, next we solve one by one, first look at the verification code problem, the page of “human authentication”, should be similar to the registration page verification code problem, but here I will directly skip, directly write dead 1+1=2 answer, we go back to add verification code, the logic is simple, Generate the verification code and put it into the session, and then compare the authentication code in the session with the submitted verification code when submit, which is the same logic as the registration verification code.

Captcha code I wrote dead:

if(!"2".equals(vercode)) {
    return Result.fail("Human authentication error!");
}

Copy the code

Hibernate Validatior: Hibernate Validatior: Hibernate Validatior: Hibernate Validatior: Hibernate Validatior: Hibernate Validatior: Hibernate Validatior: Hibernate Validatior: Hibernate Validatior: Hibernate Validatior: Hibernate Validatior: Hibernate Validatior: Hibernate Validatior: Hibernate Validatior: Hibernate Validatior

/** ** ** ** @length (min = 4, Max = 32, message ="Headings must be no longer than a minimum of 4 digits and a maximum of 32 digits.")
@NotBlank(message = "The title cannot be empty.") private String title; /** * content */ @notblank (message ="Content cannot be empty.")
private String content;
@NotNull(message = "Classification cannot be empty.")
private Long categoryId;

Copy the code

Then the submit method validates the POST by injecting the @Valid annotation and BindingResult to validate the result. So the overall code looks like this:

@ResponseBody
@Transactional
@PostMapping("/post/submit")
public Result submit(@Valid Post post, BindingResult bindingResult, String vercode) throws JsonProcessingException {
    if(!"2".equals(vercode)) {
        return Result.fail("Human authentication error!");
    }
    if(bindingResult.hasErrors()) {
        return Result.fail(bindingResult.getFieldError().getDefaultMessage());
    }
    if(post.getId() == null) {
        post.setUserId(getProfileId());
        post.setModified(new Date());
        post.setCreated(new Date());
        post.setCommentCount(0);
        post.setEditMode(null);
        post.setLevel(0);
        post.setRecommend(false);
        post.setViewCount(0);
        post.setVoteDown(0);
        post.setVoteUp(0);
    } else {
        Post tempPost = postService.getById(post.getId());
        Assert.isTrue(tempPost.getUserId()==getProfileId(), "No permission to edit this article!");
    }
    postService.saveOrUpdate(post);
    return Result.succ("Successful release", null, "/post/" + post.getId());
}

Copy the code

Submit (@valid Post Post, BindingResult BindingResult, String vercode) Bindingresult.haserrors () is used to determine whether the submitted post complies with validation logic. Compare the post.userId with the current logged-in userId and use the assertion to determine if the post is your own.

So let me extend the question here, the usefulness of assertions. We know that there are a lot of places where we need to do a checksum check to see if the data meets the expected expectations, and if it doesn’t then the assertion throws an exception.

There are two cases, synchronous or asynchronous. When synchronizing the Controller, we jump to the exception page and display the error message for the assertion. So asynchronous, we need to return JSON data, popup with an error message for assertion.

Previously, we had done a global exception handling, but I found that it was not well written and the error handling of our assertion was not good enough, so I adjusted it to determine whether the req header contained x-requested-with or not. The JSON data is then written by getting resp.getwriter ().

  • com.example.common.exception.GlobalExceptionHandler
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(value = Exception.class)
    public ModelAndView defaultErrorHandler(HttpServletRequest req, HttpServletResponse resp, Exception e) {
        log.error("------------------> Global exception caught", e);
        if (req.getHeader("accept").contains("application/json")  || (req.getHeader("X-Requested-With")! = null && req.getHeader("X-Requested-With").contains("XMLHttpRequest") )) {
            try {
                System.out.println(e.getMessage());
                Result result = Result.fail(e.getMessage(), "some error data");
                resp.setCharacterEncoding("utf-8");
                PrintWriter writer = resp.getWriter();
                writer.write(JSONUtil.toJsonStr(result));
                writer.flush();
            } catch (IOException i) {
                i.printStackTrace();
            }
            return null;
        }
        if(e instanceof HwException) {
            //...
        }
        ModelAndView mav = new ModelAndView();
        mav.addObject("exception", e);
        mav.addObject("message", e.getMessage());
        mav.addObject("url", req.getRequestURL());
        mav.setViewName("error");
        returnmav; }}Copy the code

Delete the blog

Above, we can publish and edit articles, and we can see our own articles in my posts in the user center. Next, we will delete the blog. Similarly, we need to make a simple check

  • Whether the post exists

  • Is it your own post

  • Delete messages or favorites related to posts, etc

It’s all pretty simple, so I’m just going to give you the code, and favorites we haven’t done yet, but it’s pretty simple to delete, so we’re just going to put it in there.

@ResponseBody
@Transactional
@PostMapping("/post/delete")
public Result delete(Long id) {
    Post post = postService.getById(id);
    Assert.notNull(post, "The post has since been removed.");
    Assert.isTrue(post.getUserId()==getProfileId(), "No permission to delete this article!"); postService.removeById(id); / / delete the related news, the collection userMessageService removeByMap (MapUtil. Of ("post_id", id));
    userCollectionService.removeByMap(MapUtil.of("post_id", id));
    return Result.succ("Deleted successfully", null, "/center");
}

Copy the code

The url is /post/delete

Post delete comment

Apply colours to a drawing

Next, let’s look at the comment function. Last time, we were able to display the comment function of the article, but there was a bug that some expressions and pictures could not be rendered. We just displayed the content as it was. Let’s do this first and then comment on it. It’s actually quite simple. The template gives us a hint, but if we’re using layui’s built-in editor, we need to add this code:

And since layui is loading the js module, we wrote it on the layout.ftl template, so we wait for the page to load before rendering, so that the previous JS has been rendered. $(function () {}); . Othis.html (fly.content(HTML)); Render code.

<script type="text/javascript">
    $(function () {
        layui.use(['fly'.'face'].function(){ var $ = layui.$,fly = layui.fly; // If you are using the template editor, you need to enable the following statement to parse. $('.detail-body').each(function(){
                var othis = $(this), html = othis.html();
                othis.html(fly.content(html));
            });
        });
    });
</script>

Copy the code

Here’s what we see:

Okay, the div class above is detail-body and has been rendered, including the content of the article and so on.

release

Comment release actually involves a lot of things or, let’s sort it out first

  • A simple logical judgment

  • Increase the number of comments on an article by one

  • The sidebar’s hot topic of the week is reranked

  • Notify the article writer that comments have been received

  • Notify @users that someone has replied to your comment

1, a simple logical judgment, in fact, is to judge whether the content is empty, nothing to say, direct assertion judgment

@ResponseBody
@Transactional
@PostMapping("/post/reply")
public Result reply(Long pid, Long parentId, String content) {
    Assert.notNull(pid, "Can't find the corresponding article!");
    Assert.hasLength(content, "Comments can't be empty!"); Post post = postService.getById(pid); Assert.isTrue(post ! = null,"The article has been deleted."); . }Copy the code

2. Save comments and increase the number of comments by one

Comment comment = new Comment(); comment.setPostId(pid); comment.setContent(content); comment.setUserId(getProfileId()); comment.setCreated(new Date()); comment.setModified(new Date()); comment.setLevel(0); comment.setVoteDown(0); comment.setVoteUp(0); commentService.save(comment); // The number of comments plus post.setCommentCount(post.getCommentCount() + 1); postService.saveOrUpdate(post);Copy the code

The sidebar’s Talk of the Week feature

About this function we do from the start, but seems a little considerate, only add a comment, no comments minus one, because the delete comments need to minus one, so we adjust the postService. IncrZsetValueAndUnionForLastWeekRank this method, Add an argument * Boolean *isIncr to determine whether to increase or decrease.

Notice that so I’m going to change everything where I call this method.

So adding one to the number of sidebars is easy, and we just call this method and we’re done

// Update the number of comments postservice.incrzsetValueAndUnionForLastWeekRank(comment.getPostId(), true);

Copy the code

4. Notify the author

User-centric my messages are for viewing messages, and we need to differentiate message types to show different styles. We’re going to save the message, but it’s not enough to just save it, we learned about Websocket, but when there’s a comment we should notify the author in real time, how do we do that, and we’ll fix that for the next assignment. Let’s save the message.

UserMessage = new UserMessage(); message.setPostId(pid); message.setCommentId(comment.getId()); message.setFromUserId(getProfileId()); message.setToUserId(post.getUserId()); message.setType(1); message.setContent(comment.getContent()); message.setCreated(new Date()); userMessageService.save(message);Copy the code

5. Notify the @user

When we click the reply button of the comment, the user name of @ current comment will appear in the input box of the comment, so we need to inform the user of the comment we publish. According to the characteristics of @, we extract the user name between @ and the space, and then search it out and report an error message.

// Notify @'s personif(content.startsWith("@")) {
    String username = content.substring(1, content.indexOf(""));
    System.out.println(username);
    QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("username", username);
    User user = userService.getOne(wrapper);
    if(user != null) {
        UserMessage message2 = new UserMessage();
        message.setPostId(pid);
        message2.setCommentId(comment.getId());
        message2.setFromUserId(getProfileId());
        message2.setToUserId(user.getId());
        message2.setType(3);
        message2.setContent(comment.getContent());
        message2.setCreated(new Date());
        userMessageService.save(message2);
    }
}

Copy the code

String username = content.substring(1, content.indexOf(” “)); Is to extract the user name, so here involves a problem, is the uniqueness of the user nickname problem, so we need to modify the registration, we also want to add the uniqueness of the nickname check.

  • com.example.service.impl.UserServiceImpl#register
User po = this.getOne(new QueryWrapper<User>().eq("email", user.getEmail()).or().eq("username", user.getUsername()));
if(po ! = null) {return Result.fail("Email address or nickname already registered");
}

Copy the code

Note the synchronization of the code ~, in fact, the user center to modify the user nickname there also need to add uniqueness check, you complete. In addition, I added a unique index to the database to ensure that the fields are unique.

The above method, we will need to adjust later, now I show you to do this first.

delete

Similar logic applies to deleting comments

  • A simple calibration

  • Comments to delete

  • Number of comments minus one

  • Reranking is the talk of the week

Given the code directly, with the added code logic, I believe that the deletion logic should also be familiar with the line.

@ResponseBody
@Transactional
@PostMapping("/post/jieda-delete/")
public Result reply(Long id) {
    Assert.notNull(id, "Comment ID cannot be empty!");
    Comment comment = commentService.getById(id);
    Assert.notNull(comment, "Can't find corresponding comment!");
    if(comment.getUserId() ! = getProfileId()) {return Result.fail("Not your comments!"); } commentService.removeById(id); Post Post = postservice.getById (comment.getPostid ()); post.setCommentCount(post.getCommentCount() - 1); postService.saveOrUpdate(post); // Number of comments minus postservice.incrzsetValueAndUnionForLastWeekRank(comment.getPostId(), false);
    return Result.succ(null);
}

Copy the code

Not logged in prompt what is not logged in prompt, we have done not logged in to the login page, in fact, we rely on shiro framework. Some of the operations that involve using talents require logging in. Reviewing what we have learned about Shiro, there are two ways to do this:

  • Configuration: in com. Example. Config. ShiroConfig# shiroFilter configuration hashMap. Put (“/post/edit “, “auth”);

  • Or the annotation form uses @requiresAuthentication on the corresponding method

Auth is the alias for the filter, which is actually the AuthFilter.

So remember, some of the methods that need to be logged in are labeled with both forms, otherwise you might get an error with getProfileId(). After all, you need to log in to operate the content!