Java Niuke project course_ Discussion area of imitation cattle customer network_ Chapter III

Chapter III

3.1. Trie tree filtering sensitive words

Sensitive words file: sensitive words txt

gambling
 go whoring
 Drug
 Billing

Filter sensitive words tool class sensitivefilter java
Note: the Bean is initialized when the service is started, and then the method annotated by @ PostConstruct is called. So the init() method in the SensitiveFilter class is called as soon as the program starts.

package com.nowcoder.community.util;

import org.apache.commons.lang3.CharUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

@Component
public class SensitiveFilter {

    private static final Logger logger = LoggerFactory.getLogger(SensitiveFilter.class);

    // Substitution character
    private static final String REPLACEMENT = "***";

    // Root node
    private TrieNode rootNode = new TrieNode();

    @PostConstruct
    public void init() {
        try (
                InputStream is = this.getClass().getClassLoader().getResourceAsStream("sensitive-words.txt");
                BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        ) {
            String keyword;
            while ((keyword = reader.readLine()) != null) {
                // Add to prefix tree
                this.addKeyword(keyword);
            }
        } catch (IOException e) {
            logger.error("Failed to load sensitive word file: " + e.getMessage());
        }
    }

    // Add a sensitive word to the prefix tree
    private void addKeyword(String keyword) {
        TrieNode tempNode = rootNode;
        for (int i = 0; i < keyword.length(); i++) {
            char c = keyword.charAt(i);
            TrieNode subNode = tempNode.getSubNode(c);

            if (subNode == null) {
                // Initialize child nodes
                subNode = new TrieNode();
                tempNode.addSubNode(c, subNode);
            }

            // Point to the child node and enter the next cycle
            tempNode = subNode;

            // Set end ID
            if (i == keyword.length() - 1) {
                tempNode.setKeywordEnd(true);
            }
        }
    }

    /**
     * Filter sensitive words
     *
     * @param text Text to be filtered
     * @return Filtered text
     */
    public String filter(String text) {
        if (StringUtils.isBlank(text)) {
            return null;
        }

        // Pointer 1
        TrieNode tempNode = rootNode;
        // Pointer 2
        int begin = 0;
        // Pointer 3
        int position = 0;
        // result
        StringBuilder sb = new StringBuilder();

        while (position < text.length()) {
            char c = text.charAt(position);

            // Skip symbol
            if (isSymbol(c)) {
                // If the pointer is at 1, the result will be included in this step
                if (tempNode == rootNode) {
                    sb.append(c);
                    begin++;
                }
                // Pointer 3 goes one step down regardless of whether the symbol is at the beginning or in the middle
                position++;
                continue;
            }

            // Check child nodes
            tempNode = tempNode.getSubNode(c);
            if (tempNode == null) {
                // String starting with begin is not a sensitive word
                sb.append(text.charAt(begin));
                // Go to the next position
                position = ++begin;
                // Re point to the root node
                tempNode = rootNode;
            } else if (tempNode.isKeywordEnd()) {
                // If sensitive words are found, replace the begin~position string
                sb.append(REPLACEMENT);
                // Go to the next position
                begin = ++position;
                // Re point to the root node
                tempNode = rootNode;
            } else {
                // Check next character
                position++;
            }
        }

        // Count the last batch of characters into the result
        sb.append(text.substring(begin));

        return sb.toString();
    }

    // Judge whether it is a symbol
    private boolean isSymbol(Character c) {
        // 0x2E80~0x9FFF are East Asian characters
        return !CharUtils.isAsciiAlphanumeric(c) && (c < 0x2E80 || c > 0x9FFF);
    }

    // trie 
    private class TrieNode {

        // Keyword end identifier
        private boolean isKeywordEnd = false;

        // Child node (key is a child character and value is a child node)
        private Map<Character, TrieNode> subNodes = new HashMap<>();

        public boolean isKeywordEnd() {
            return isKeywordEnd;
        }

        public void setKeywordEnd(boolean keywordEnd) {
            isKeywordEnd = keywordEnd;
        }

        // Add child node
        public void addSubNode(Character c, TrieNode node) {
            subNodes.put(c, node);
        }

        // Get child nodes
        public TrieNode getSubNode(Character c) {
            return subNodes.get(c);
        }

    }
}

Test, sensitivetests java

package com.nowcoder.community;

import com.nowcoder.community.util.SensitiveFilter;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class SensitiveTests {

    @Autowired
    private SensitiveFilter sensitiveFilter;

    @Test
    public void testSensitiveFilter() {
        String text = "You can gamble here,You can whore,Can take drugs,Can be invoiced,Ha ha ha!";
        text = sensitiveFilter.filter(text);
        System.out.println(text);

        text = "Here you can☆bet☆Bo☆,sure☆Whoring☆Whore☆,sure☆absorb☆poison☆,sure☆open☆ticket☆,Ha ha ha!";
        text = sensitiveFilter.filter(text);
        System.out.println(text);
    }

}

3.6. AJAX post

pom.xml

		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.58</version>
		</dependency>

CommunityUtil.java :

public class CommunityUtil {
    public static String getJSONString(int code, String msg, Map<String, Object> map) {
        JSONObject json = new JSONObject();
        json.put("code", code);
        json.put("msg", msg);
        if (map != null) {
            for (String key : map.keySet()) {
                json.put(key, map.get(key));
            }
        }
        return json.toJSONString();
    }

    public static String getJSONString(int code, String msg) {
        return getJSONString(code, msg, null);
    }

    public static String getJSONString(int code) {
        return getJSONString(code, null, null);
    }

    public static void main(String[] args) {
        Map<String, Object> map = new HashMap<>();
        map.put("name", "zhangsan");
        map.put("age", 25);
        System.out.println(getJSONString(0, "ok", map));
    }
}

Test part:
AlphaController.java test

@Controller
@RequestMapping("/alpha")
public class AlphaController {
    // ajax example
    @RequestMapping(path = "/ajax", method = RequestMethod.POST)
    @ResponseBody
    public String testAjax(String name, int age) {
        System.out.println(name);
        System.out.println(age);
        return CommunityUtil.getJSONString(0, "Operation successful!");
    }
}

Test page, / HTML / Ajax demo html
Note: $ ajax(); It can be simplified to $ post(); And $ get();.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>AJAX</title>
</head>
<body>
    <p>
        <input type="button" value="send out" onclick="send();">
    </p>

    <script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous"></script>
    <script>
        function send() {
            $.post(
                "/community/alpha/ajax",
                {"name":"Zhang San","age":23},
                function(data) {
                    console.log(typeof(data));
                    console.log(data);

                    data = $.parseJSON(data);
                    console.log(typeof(data));
                    console.log(data.code);
                    console.log(data.msg);
                }
            );
        }
    </script>
</body>
</html>

Test results:

AJAX request is adopted to realize the function of publishing posts

discusspost-mapper.xml

<insert id="insertDiscussPost" parameterType="DiscussPost">
    insert into discuss_post(<include refid="insertFields"></include>)
    values(#{userId},#{title},#{content},#{type},#{status},#{createTime},#{commentCount},#{score})
</insert>

Service,DiscussPostService.java:
notes:
HtmlUtils. htmlEscape("<div>"); Get & lt; div>
HtmlUtils. htmlUnescape("<div>"); Get < div >

    public int addDiscussPost(DiscussPost post) {
        if (post == null) {
            throw new IllegalArgumentException("Parameter cannot be empty!");
        }

        // Escape HTML tags
        post.setTitle(HtmlUtils.htmlEscape(post.getTitle()));
        post.setContent(HtmlUtils.htmlEscape(post.getContent()));
        // Filter sensitive words
        post.setTitle(sensitiveFilter.filter(post.getTitle()));
        post.setContent(sensitiveFilter.filter(post.getContent()));

        return discussPostMapper.insertDiscussPost(post);
    }

Controller, 0803 final discusspostcontroller java:

@Controller
@RequestMapping("/discuss")
public class DiscussPostController implements CommunityConstant {

    @Autowired
    private DiscussPostService discussPostService;

    @Autowired
    private HostHolder hostHolder;

    @RequestMapping(path = "/add", method = RequestMethod.POST)
    @ResponseBody
    public String addDiscussPost(String title, String content) {
        User user = hostHolder.getUser();
        if (user == null) {
            return CommunityUtil.getJSONString(403, "You haven't logged in yet!");
        }

        DiscussPost post = new DiscussPost();
        post.setUserId(user.getId());
        post.setTitle(title);
        post.setContent(content);
        post.setCreateTime(new Date());
        discussPostService.addDiscussPost(post);

        // Errors reported will be handled in a unified manner in the future
        return CommunityUtil.getJSONString(0, "Published successfully!");
    }
}

0803 version of index JS is the operation after clicking the Publish button

$(function(){
	$("#publishBtn").click(publish);
});

function publish() {
	$("#publishModal").modal("hide");

    // Before sending the AJAX request, set the CSRF token into the request header
//    var token = $("meta[name='_csrf']").attr("content");
//    var header = $("meta[name='_csrf_header']").attr("content");
//    $(document).ajaxSend(function(e, xhr, options){
//        xhr.setRequestHeader(header, token);
//    });

	// Get title and content
	var title = $("#recipient-name").val();
	var content = $("#message-text").val();
	// Send asynchronous request (POST)
	$.post(
	    CONTEXT_PATH + "/discuss/add",
	    {"title":title,"content":content},
	    function(data) {
	        data = $.parseJSON(data);
	        // Display the return message in the prompt box
	        $("#hintBody").text(data.msg);
	        // Display prompt box
            $("#hintModal").modal("show");
            // After 2 seconds, the prompt box will be hidden automatically
            setTimeout(function(){
                $("#hintModal").modal("hide");
                // Refresh page
                if(data.code == 0) {
                    window.location.reload();
                }
            }, 2000);
	    }
	);
}

3.11 post details (one line of CRUD)

DiscussPostMapper interface
discusspost-mapper.xml
DiscussPostService class

Controller, discusspostcontroller version 0803 java

    @RequestMapping(path = "/detail/{discussPostId}", method = RequestMethod.GET)
    public String getDiscussPost(@PathVariable("discussPostId") int discussPostId, Model model, Page page) {
        // Posts
        DiscussPost post = discussPostService.findDiscussPostById(discussPostId);
        model.addAttribute("post", post);
        // author
        User user = userService.findUserById(post.getUserId());
        model.addAttribute("user", user);
        
        return "/site/discuss-detail";
    }

index.html
notes:
< a th: href = "@ {| / discussions / detail / ${map. Post. ID} |}" > Hello < / a >, there are variables and constants, so add vertical lines on both sides.

<a th:href="@{|/discuss/detail/${map.post.id}|}" th:utext="${map.post.title}">Prepare for spring moves, brush questions in the interview and review with him. It's all done in a month!</a>

discuss-detail.html page

3.13. Transaction management (important and new knowledge. Only test, no project code)

Mainly explain ppt.

Spring transaction management

  • Declarative transaction
    • Declare the transaction characteristics of a method through XML configuration.
    • Declare the transaction characteristics of a method through annotation.
    • For: This is preferred and simple.
  • Programming transaction
    • Manage transactions through TransactionTemplate and perform database operations through it.
    • For: the business logic is complex. We just want to manage a small number of transactions in the middle.

test

AlphaService.java
Note: A calls B. for B, A is the current transaction (external transaction)

@Service
//@Scope("prototype")
public class AlphaService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private DiscussPostMapper discussPostMapper;

    @Autowired
    private TransactionTemplate transactionTemplate;

    // REQUIRED: support the current transaction (external transaction). If it does not exist, create a new transaction
    // REQUIRES_NEW: create a new transaction and suspend the current transaction (external transaction)
    // NESTED: if there is a transaction (external transaction), it will be NESTED in the transaction (independent commit and rollback), otherwise it will be the same as REQUIRED
    @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
    public Object save1() {
        // New user
        User user = new User();
        user.setUsername("alpha");
        user.setSalt(CommunityUtil.generateUUID().substring(0, 5));
        user.setPassword(CommunityUtil.md5("123" + user.getSalt()));
        user.setEmail("alpha@qq.com");
        user.setHeaderUrl("http://image.nowcoder.com/head/99t.png");
        user.setCreateTime(new Date());
        userMapper.insertUser(user);

        // New post
        DiscussPost post = new DiscussPost();
        post.setUserId(user.getId());
        post.setTitle("Hello");
        post.setContent("New reports !");
        post.setCreateTime(new Date());
        discussPostMapper.insertDiscussPost(post);

        Integer.valueOf("abc");

        return "ok";
    }

    public Object save2() {
        transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

        return transactionTemplate.execute(new TransactionCallback<Object>() {
            @Override
            public Object doInTransaction(TransactionStatus status) {
                // New user
                User user = new User();
                user.setUsername("beta");
                user.setSalt(CommunityUtil.generateUUID().substring(0, 5));
                user.setPassword(CommunityUtil.md5("123" + user.getSalt()));
                user.setEmail("beta@qq.com");
                user.setHeaderUrl("http://image.nowcoder.com/head/999t.png");
                user.setCreateTime(new Date());
                userMapper.insertUser(user);

                // New post
                DiscussPost post = new DiscussPost();
                post.setUserId(user.getId());
                post.setTitle("Hello");
                post.setContent("I'm new!");
                post.setCreateTime(new Date());
                discussPostMapper.insertDiscussPost(post);

                Integer.valueOf("abc");

                return "ok";
            }
        });
    }
}

TransactionTests.java

package com.nowcoder.community;

import com.nowcoder.community.service.AlphaService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;

@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class TransactionTests {

    @Autowired
    private AlphaService alphaService;

    @Test
    public void testSave1() {
        Object obj = alphaService.save1();
        System.out.println(obj);
    }

    @Test
    public void testSave2() {
        Object obj = alphaService.save2();
        System.out.println(obj);
    }
}

3.20. Display comments (complex) (CRUD one line)

CommentMapper interface

comment-mapper.xml

    <sql id="selectFields">
        id, user_id, entity_type, entity_id, target_id, content, status, create_time
    </sql>

    <sql id="insertFields">
        user_id, entity_type, entity_id, target_id, content, status, create_time
    </sql>

    <select id="selectCommentsByEntity" resultType="Comment">
        select <include refid="selectFields"></include>
        from comment
        where status = 0
        and entity_type = #{entityType}
        and entity_id = #{entityId}
        order by create_time asc
        limit #{offset}, #{limit}
    </select>

    <select id="selectCountByEntity" resultType="int">
        select count(id)
        from comment
        where status = 0
        and entity_type = #{entityType}
        and entity_id = #{entityId}
    </select>

CommentService.java

Controller, discusspostcontroller version 0803 java

    @RequestMapping(path = "/detail/{discussPostId}", method = RequestMethod.GET)
    public String getDiscussPost(@PathVariable("discussPostId") int discussPostId, Model model, Page page) {
        // Posts
        DiscussPost post = discussPostService.findDiscussPostById(discussPostId);
        model.addAttribute("post", post);
        // author
        User user = userService.findUserById(post.getUserId());
        model.addAttribute("user", user);
        // Number of likes
        long likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_POST, discussPostId);
        model.addAttribute("likeCount", likeCount);
        // Like status
        int likeStatus = hostHolder.getUser() == null ? 0 :
                likeService.findEntityLikeStatus(hostHolder.getUser().getId(), ENTITY_TYPE_POST, discussPostId);
        model.addAttribute("likeStatus", likeStatus);

        // Comment paging information
        page.setLimit(5);
        page.setPath("/discuss/detail/" + discussPostId);
        page.setRows(post.getCommentCount());

        // Comments: comments to posts
        // Re: comments for comments
        // Comment list
        List<Comment> commentList = commentService.findCommentsByEntity(
                ENTITY_TYPE_POST, post.getId(), page.getOffset(), page.getLimit());
        // Comment VO list
        List<Map<String, Object>> commentVoList = new ArrayList<>();
        if (commentList != null) {
            for (Comment comment : commentList) {
                // Comment VO
                Map<String, Object> commentVo = new HashMap<>();
                // comment
                commentVo.put("comment", comment);
                // author
                commentVo.put("user", userService.findUserById(comment.getUserId()));
                // Number of likes
                likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_COMMENT, comment.getId());
                commentVo.put("likeCount", likeCount);
                // Like status
                likeStatus = hostHolder.getUser() == null ? 0 :
                        likeService.findEntityLikeStatus(hostHolder.getUser().getId(), ENTITY_TYPE_COMMENT, comment.getId());
                commentVo.put("likeStatus", likeStatus);

                // Reply list
                List<Comment> replyList = commentService.findCommentsByEntity(
                        ENTITY_TYPE_COMMENT, comment.getId(), 0, Integer.MAX_VALUE);
                // Reply to VO list
                List<Map<String, Object>> replyVoList = new ArrayList<>();
                if (replyList != null) {
                    for (Comment reply : replyList) {
                        Map<String, Object> replyVo = new HashMap<>();
                        // reply
                        replyVo.put("reply", reply);
                        // author
                        replyVo.put("user", userService.findUserById(reply.getUserId()));
                        // Reply target
                        User target = reply.getTargetId() == 0 ? null : userService.findUserById(reply.getTargetId());
                        replyVo.put("target", target);
                        // Number of likes
                        likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_COMMENT, reply.getId());
                        replyVo.put("likeCount", likeCount);
                        // Like status
                        likeStatus = hostHolder.getUser() == null ? 0 :
                                likeService.findEntityLikeStatus(hostHolder.getUser().getId(), ENTITY_TYPE_COMMENT, reply.getId());
                        replyVo.put("likeStatus", likeStatus);

                        replyVoList.add(replyVo);
                    }
                }
                commentVo.put("replys", replyVoList);

                // Number of replies
                int replyCount = commentService.findCommentCount(ENTITY_TYPE_COMMENT, comment.getId());
                commentVo.put("replyCount", replyCount);

                commentVoList.add(commentVo);
            }
        }

        model.addAttribute("comments", commentVoList);

        return "/site/discuss-detail";
    }

discuss-detail.html

3.22. Add comments (CRUD one line)

Sometimes when there is no mistake, IDEA will report a red line. The solution: Ctrl+F9 recompile. If it is not good, cut and paste the class marked with the red line back. There will be no error and no red line.

Transactions are used, because after adding comments, you need to add discussions_ Modify the number of comments in the post table.

CommentMapper interface

comment-mapper.xml

CommentService

-

DiscussPostMapper interface

discusspost-mapper.xml

DiscussPostService class

-

CommentController version 0803

package com.nowcoder.community.controller;

import com.nowcoder.community.entity.Comment;
import com.nowcoder.community.entity.DiscussPost;
import com.nowcoder.community.entity.Event;
import com.nowcoder.community.event.EventProducer;
import com.nowcoder.community.service.CommentService;
import com.nowcoder.community.service.DiscussPostService;
import com.nowcoder.community.util.CommunityConstant;
import com.nowcoder.community.util.HostHolder;
import com.nowcoder.community.util.RedisKeyUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.util.Date;

@Controller
@RequestMapping("/comment")
public class CommentController implements CommunityConstant {

    @Autowired
    private CommentService commentService;

    @Autowired
    private HostHolder hostHolder;

    @Autowired
    private EventProducer eventProducer;

    @Autowired
    private DiscussPostService discussPostService;

    @Autowired
    private RedisTemplate redisTemplate;

    @RequestMapping(path = "/add/{discussPostId}", method = RequestMethod.POST)
    public String addComment(@PathVariable("discussPostId") int discussPostId, Comment comment) {
        comment.setUserId(hostHolder.getUser().getId());
        comment.setStatus(0);
        comment.setCreateTime(new Date());
        commentService.addComment(comment);

        // Trigger comment event
        Event event = new Event()
                .setTopic(TOPIC_COMMENT)
                .setUserId(hostHolder.getUser().getId())
                .setEntityType(comment.getEntityType())
                .setEntityId(comment.getEntityId())
                .setData("postId", discussPostId);
        if (comment.getEntityType() == ENTITY_TYPE_POST) {
            DiscussPost target = discussPostService.findDiscussPostById(comment.getEntityId());
            event.setEntityUserId(target.getUserId());
        } else if (comment.getEntityType() == ENTITY_TYPE_COMMENT) {
            Comment target = commentService.findCommentById(comment.getEntityId());
            event.setEntityUserId(target.getUserId());
        }
        eventProducer.fireEvent(event);

        if (comment.getEntityType() == ENTITY_TYPE_POST) {
            // Trigger posting event
            event = new Event()
                    .setTopic(TOPIC_PUBLISH)
                    .setUserId(comment.getUserId())
                    .setEntityType(ENTITY_TYPE_POST)
                    .setEntityId(discussPostId);
            eventProducer.fireEvent(event);
            // Calculate post score
            String redisKey = RedisKeyUtil.getPostScoreKey();
            redisTemplate.opsForSet().add(redisKey, discussPostId);
        }

        return "redirect:/discuss/detail/" + discussPostId;
    }
}

discuss-detail.html

<!-- Reply to someone's reply input box -->
<div th:id="|huifu-${rvoStat.count}|" class="mt-4 collapse">
	<form method="post" th:action="@{|/comment/add/${post.id}|}">
		<div>
			<input type="text" class="input-size" name="content" th:placeholder="|reply ${rvo.user.username}|"/>
			<input type="hidden" name="entityType" value="2">
			<input type="hidden" name="entityId" th:value="${cvo.comment.id}">
			<input type="hidden" name="targetId" th:value="${rvo.user.id}">
		</div>
		<div class="text-right mt-2">
			<button type="submit" class="btn btn-primary btn-sm" onclick="#"> & nbsp; & nbsp; back to & nbsp; & nbsp; reply to & nbsp; & nbsp; < / button >
		</div>
	</form>
</div>
<!-- Reply input box -->
<li class="pb-3 pt-3">
	<form method="post" th:action="@{|/comment/add/${post.id}|}">
		<div>
			<input type="text" class="input-size" name="content" placeholder="Please enter your opinion"/>
			<input type="hidden" name="entityType" value="2">
			<input type="hidden" name="entityId" th:value="${cvo.comment.id}">
		</div>
		<div class="text-right mt-2">
			<button type="submit" class="btn btn-primary btn-sm" onclick="#"> & nbsp; & nbsp; back to & nbsp; & nbsp; reply to & nbsp; & nbsp; < / button >
		</div>
	</form>
</li>
<!-- Reply input -->
<div class="container mt-3">
	<form class="replyform" method="post" th:action="@{|/comment/add/${post.id}|}">
		<p class="mt-3">
			<a name="replyform"></a>
			<textarea placeholder="Feel free to express your views here!" name="content"></textarea>
			<input type="hidden" name="entityType" value="1">
			<input type="hidden" name="entityId" th:value="${post.id}">
		</p>
		<p class="text-right">
			<button type="submit" class="btn btn-primary btn-sm">&nbsp;&nbsp;return&nbsp;&nbsp;Placard&nbsp;&nbsp;</button>
		</p>
	</form>
</div>

3.24. Private letter list (one line of CRUD)

MessageMapper interface

message-mapper.xml

Test in MapperTests class

MessageService

MessageController

letter.html

letter-detail.html

3.27 sending private messages (one line of CRUD)

The sending here is not the kind of network transmission, but the sender, which puts the private message into the database of the server. Then, when the receiver refreshes the page, it will read the private message from the database.
The problem of the project: the User name should not be the same, because the userservice in the sendLetter() method in the MessageController class Finduserbyname (toname), query the User with the User name. However, the User of the database has only the User name and no nickname. The User name should not be the same, but the nickname can be the same.
There are two places to send private messages:
1. Message list page. Send a private letter here to write the user name of the other party, just like sending an email.

2. A page that sends messages to someone. Sending private messages here is like wechat. Poke it in and chat directly.

  • Send private messages
    • Send private messages asynchronously.
    • Refresh the private message list after sending successfully.
  • Set read
    • When accessing private message details, set the displayed private message to read status.

MessageMapper interface

    // New message
    int insertMessage(Message message);

    // Modify the status of the message
    int updateStatus(List<Integer> ids, int status);

message-mapper.xml

MessageService

MessageController

    @RequestMapping(path = "/letter/send", method = RequestMethod.POST)
    @ResponseBody
    public String sendLetter(String toName, String content) {
        User target = userService.findUserByName(toName);
        if (target == null) {
            return CommunityUtil.getJSONString(1, "Target user does not exist!");
        }

        Message message = new Message();
        message.setFromId(hostHolder.getUser().getId());
        message.setToId(target.getId());
        if (message.getFromId() < message.getToId()) {
            message.setConversationId(message.getFromId() + "_" + message.getToId());
        } else {
            message.setConversationId(message.getToId() + "_" + message.getFromId());
        }
        message.setContent(content);
        message.setCreateTime(new Date());
        messageService.addMessage(message);

        return CommunityUtil.getJSONString(0);
    }

letter.js, when sending a private message, click the send button "#sendBtn" and come here.

$(function(){
	$("#sendBtn").click(send_letter);
	$(".close").click(delete_msg);
});

function send_letter() {
	$("#sendModal").modal("hide");

	var toName = $("#recipient-name").val();
	var content = $("#message-text").val();
	$.post(
	    CONTEXT_PATH + "/letter/send",
	    {"toName":toName,"content":content},
	    function(data) {
	        data = $.parseJSON(data);
	        if(data.code == 0) {
	            $("#hintBody").text(" sent successfully! ");
	        } else {
	            $("#hintBody").text(data.msg);
	        }

	        $("#hintModal").modal("show");
            setTimeout(function(){
                $("#hintModal").modal("hide");
                location.reload();
            }, 2000);
	    }
	);
}

function delete_msg() {
	// TODO delete data
	$(this).parents(".media").remove();
}

Unread messages are set to read.
MessageController

    private List<Integer> getLetterIds(List<Message> letterList) {
        List<Integer> ids = new ArrayList<>();

        if (letterList != null) {
            for (Message message : letterList) {
                if (hostHolder.getUser().getId() == message.getToId() && message.getStatus() == 0) {
                    ids.add(message.getId());
                }
            }
        }
        return ids;
    }

Modify the getLetterDetail method in MessageController.

3.31. Handle exceptions uniformly (@ ControllerAdvice@ExceptionHandler )

  • @ControllerAdvice
    • Used to decorate a class, indicating that this class is the global configuration class of Controller.
    • In this class, three global configurations can be made for the Controller: exception handling scheme, binding data scheme and binding parameter scheme.
  • @ExceptionHandler - used to decorate the method, which will be called after the Controller has an exception and used to handle the caught exception.
  • @ModelAttribute - used to decorate the method, which will be called before the Controller method is executed to bind parameters to the Model object.
  • @DataBinder - used to decorate the method, which will be called before the Controller method is executed, and is used to bind the converter of parameters.

In SpringBoot, 404 HTML and 500 Drag HTML to the template/error / directory, and the two pages will take effect automatically.
404 nothing, 500 errors need to be recorded in a log.
HomeController

    @RequestMapping(path = "/error", method = RequestMethod.GET)
    public String getErrorPage() {
        return "/error/500";
    }

    @RequestMapping(path = "/denied", method = RequestMethod.GET)
    public String getDeniedPage() {
        return "/error/404";
    }

Create a new package advice under the controller package, / controller/advice/ExceptionAdvice
Comments: @ ExceptionHandler({Exception.class}) / / multiple exceptions can be written in braces, indicating which exceptions to handle

package com.nowcoder.community.controller.advice;

import com.nowcoder.community.util.CommunityUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@ControllerAdvice(annotations = Controller.class)
public class ExceptionAdvice {

    private static final Logger logger = LoggerFactory.getLogger(ExceptionAdvice.class);

    @ExceptionHandler({Exception.class})//Multiple exceptions can be written in braces to indicate which exceptions to handle
    public void handleException(Exception e, HttpServletRequest request, HttpServletResponse response) throws IOException {
        logger.error("Server exception: " + e.getMessage());
        for (StackTraceElement element : e.getStackTrace()) {
            logger.error(element.toString());
        }

        String xRequestedWith = request.getHeader("x-requested-with");
        if ("XMLHttpRequest".equals(xRequestedWith)) {
            response.setContentType("application/plain;charset=utf-8");
            PrintWriter writer = response.getWriter();
            writer.write(CommunityUtil.getJSONString(1, "Server exception!"));
        } else {
            response.sendRedirect(request.getContextPath() + "/error");
        }
    }
}

3.33 unified logging (AOP)

You cannot use @ ControllerAdvice because this is for Exception. The interceptor cannot be used, because the interceptor can only be used for the Controller, and we record logs, which may not only be used for the Controller, but also for the Service and data access layer.
AOP and OOP are complementary, not competitive.

New package aspect

test
AlphaAspect
Note: @ pointcut ("execution (* com. Nowcode. Community. Service. * * (..)"), The first * indicates all return values, com nowcoder. community. Service is the package name, the second * represents the class name, and the third * represents the method name, (..) Represents all parameters. Write add * in the method name to indicate the method starting with add.

package com.nowcoder.community.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

//@Component
//@Aspect
public class AlphaAspect {

    @Pointcut("execution(* com.nowcoder.community.service.*.*(..))")
    public void pointcut() {

    }

    @Before("pointcut()")
    public void before() {
        System.out.println("before");
    }

    @After("pointcut()")
    public void after() {
        System.out.println("after");
    }

    @AfterReturning("pointcut()")
    public void afterRetuning() {
        System.out.println("afterRetuning");
    }

    @AfterThrowing("pointcut()")
    public void afterThrowing() {
        System.out.println("afterThrowing");
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("around before");
        Object obj = joinPoint.proceed();
        System.out.println("around after");
        return obj;
    }
}

output

around before
before
around after
after
afterRetuning

ServiceLogAspect
A log should be recorded before accessing the service method every time.

package com.nowcoder.community.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;

//@Component
//@Aspect
public class ServiceLogAspect {

    private static final Logger logger = LoggerFactory.getLogger(ServiceLogAspect.class);

    @Pointcut("execution(* com.nowcoder.community.service.*.*(..))")
    public void pointcut() {

    }

    @Before("pointcut()")
    public void before(JoinPoint joinPoint) {//The method being accessed by joinPoint is
        // The user [1.2.3.4] visited [com.nowcoder.community.service.xxx()] on [xxx]
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            return;
        }
        HttpServletRequest request = attributes.getRequest();
        String ip = request.getRemoteHost();
        String now = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        String target = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();//Class name Method name
        logger.info(String.format("user[%s],stay[%s],Visited[%s].", ip, now, target));
    }
}

Keywords: Java Spring Ajax

Added by guanche on Thu, 10 Feb 2022 09:49:41 +0200