78. Remove the comment – persistence layer

(a) Plan the SQL statements to be executed

SQL statements that need to be executed are as follows:

delete from comment where id=?
Copy the code

Before adding, deleting, or modifying data, you need to check whether the data being operated exists, whether you have permission to operate on the data, and other service rules that may exist.

The answer to whether the data exists can be found by querying:

select * from comment where id=?
Copy the code

With respect to permissions to manipulate data, you can temporarily design the business rule so that “the comment author, or any teacher, can delete the comment.” You can judge whether you are the comment publisher by the user_id in the query result and user_id in the login status. You can judge whether you are the teacher by the type attribute in the login status.

Usually, the conditions are very simple data operations can be directly completed by MyBatis Plus functions, you can not develop special related functions!

(b) Abstract methods of interfaces

There is no

(c) Configure SQL statements

There is no

(d) Unit testing

@SpringBootTest
@Slf4j
public class CommentsMapperTests {

    @Autowired
    CommentMapper mapper;

    @Test
    void deleteById(a) {
        Integer id = 4;
        int rows = mapper.deleteById(id);
        log.debug("delete ok, rows={}", rows);
    }

    @Test
    void selectById(a) {
        Integer id = 4;
        Comment comment = mapper.selectById(id);
        log.debug("query ok, result={}", comment); }}Copy the code

79. Delete the comment – business layer

(a) Create an exception

Create exception “Comment data does not exist” :

public class CommentNotFoundException extends ServiceException {}
Copy the code

Create “insufficient permissions” exception:

public class PermissionDeniedException extends ServiceException {}
Copy the code

Failed to delete data exception:

public class DeleteException extends ServiceException {}
Copy the code

(b) Abstract methods of interfaces

/** * delete comments **@paramCommentId Indicates the ID * of the comment data@paramUserId Id of the current login user *@paramUserType Type of the current login user *@returnSuccessfully deleted comment data */
Comment delete(Integer commentId, Integer userId, Integer userType);
Copy the code

(c) Business realization

About business implementation steps:

public Comment delete(Integer commentId, Integer userId, Integer userType) {
    // Call mapper.selectByid () with the commentId argument to query for information about deleted comments
    // Check whether the query result is null
    // Yes: This "comment" does not exist. Throw CommentNotFoundException
    
    // Based on the userId in the query result and the parameter userId, determine whether the query result data belongs to the current login user.
    // Based on the userType parameter, determine the identity of the current login user is "teacher",
    / / does not meet the, if the two conditions are not allowed to delete, throw PermissionDeniedException
    
    Mapper.deletebyid () is called with the commentId argument to perform the deletion and get the number of affected rows returned
    // Check whether the return value is not 1
    // Yes: throws DeleteException
    
    // Return the query result
}
Copy the code

Specific implementation business:

@Override
public Comment delete(Integer commentId, Integer userId, Integer userType) {
    // Call mapper.selectByid () with the commentId argument to query for information about deleted comments
    Comment comment = commentMapper.selectById(commentId);
    // Check whether the query result is null
    if (comment == null) {
        // Yes: This "comment" does not exist. Throw CommentNotFoundException
        throw new CommentNotFoundException("Failed to delete comment! Trying to access comment data does not exist!");
    }

    // Based on the userId in the query result and the parameter userId, determine whether the query result data belongs to the current login user.
    // Based on the userType parameter, determine the identity of the current login user is "teacher",
    // Note: To determine the userId, you must use equals()
    // Use == or! = to determine data of type Integer, provided that the value range must be between -128 and 127
    // For example, Integer i1 = 128; Integer i2 = 128;
    // Use == to determine the above i1 and i2, the result will be false
    if(! comment.getUserId().equals(userId) && userType ! = User.TYPE_TEACHER) {/ / does not meet the, if the two conditions are not allowed to delete, throw PermissionDeniedException
        throw new PermissionDeniedException("Failed to delete comment! Only the publisher and teacher can delete this comment!");
    }

    Mapper.deletebyid () is called with the commentId argument to perform the deletion and get the number of affected rows returned
    int rows = commentMapper.deleteById(commentId);
    // Check whether the return value is not 1
    if(rows ! =1) {
        // Yes: throws DeleteException
        throw new DeleteException("Failed to delete comment! Unknown error occurred while deleting, please contact system administrator!");
    }

    // Return the query result
    return comment;
}
Copy the code

(d) Unit testing

@Test
void delete(a) {
    try {
        Integer commentId = 10;
        Integer userId = 5;
        Integer userType = User.TYPE_TEACHER;
        Comment comment = service.delete(commentId, userId, userType);
        log.debug("OK, comment >>> {}", comment);
    } catch (ServiceException e) {
        log.debug([Failed to delete comment]);
        log.debug(Error type: {}, e.getClass().getName());
        log.debug("Error: {}", e.getMessage()); }}Copy the code

80. Remove the comment – Controller layer

(a) Handling exceptions

You need to add the corresponding status code for each exception in R. state, and then handle the three exceptions created by the business layer in GloableExceptionHandler.

(b) Design request

Request path: / API /v1/comments/{commentId}/delete

Request parameters: @pathVariable (“commentId”) Integer commentId, @authenticationPriciple UserInfo UserInfo

Request type: POST

R

(c) Processing of requests

// http://localhost:8080/api/v1/comments/11/delete
@RequestMapping("/{commentId}/delete")
public R<Comment> delete(@PathVariable("commentId") Integer commentId, 
    @AuthenticationPrincipal UserInfo userInfo) {
    Comment comment = commentService.delete(commentId, userInfo.getId(), userInfo.getType());
    return R.ok(comment);
}
Copy the code

(d) Tests

http://localhost:8080/api/v1/comments/14/delete

81. Remove comments – front page

In Vue, if you need to traverse an array and need to get the index of each array element during traverse, you can:

v-for="(comment, index) in comments"
Copy the code

Comment in the above code is the data of the array element traversed, while index is the index of the array element. As specified in Vue 2.x, during traversal, the array element name and array index can be enclosed in parentheses on the left side of IN. The last name in parentheses is the array index, and the name can be customized!

After using the above code, you can use index to represent the subscript in the code area being traversed!

So, about traversing the entire comment list:

In the TAB of “Delete Comments” configure:

Then, in theanswers.jsDefine a new function in:

After completing the above content, you can test the effect of clicking “delete” on the page, and if it is correct, you can continue to add the code for sending subsequent requests and processing results in the above function.

The complete code is as follows:

82. Modify the comment – persistence layer

(a) Plan the SQL statements to be executed

The SQL statement to modify a comment looks like this:

update comment set content=? where id=?
Copy the code

The above functions can be directly implemented using the updateById() method of MyBatis Plus.

In addition, before performing the modification, you should also perform a check on the modified data. The check logic can be the same as in delete.

(b) Abstract methods of interfaces

There is no

(c) Configure SQL statements

There is no

(d) Unit testing

@Test
void updateById(a) {
    Comment comment = new Comment();
    comment.setId(15);
    comment.setContent("The functionality of the project is almost finished!!");
    int rows = mapper.updateById(comment);
    log.debug("update over. rows={}", rows);
}
Copy the code

83. Modify comments – Business layer

(a) Create an exception

To create a failed to modify data exception:

public class UpdateException extends ServiceException {}Copy the code

(b) Abstract methods of interfaces

/** * Modify comments **@paramCommentId Indicates the ID * of the comment data@paramContent The body of the comment@paramUserId Id of the current login user *@paramUserType Type of the current login user *@returnSuccessfully modified comment data */
Comment update(Integer commentId, String content, Integer userId, Integer userType);
Copy the code

(c) Business realization

About business implementation steps:

public Comment update(Integer commentId, String content, Integer userId, Integer type) {
    // Call mapper.selectByid () with the commentId argument to query for information about edited comments
    // Check whether the query result is null
    // Yes: This "comment" does not exist. Throw CommentNotFoundException
    
    // Based on the userId in the query result and the parameter userId, determine whether the query result data belongs to the current login user.
    // Based on the userType parameter, determine the identity of the current login user is "teacher",
    / / does not meet the, if the two conditions are not allowed to delete, throw PermissionDeniedException
    
    // Create a new Comment object
    // Encapsulate commentId and Content in comment
    // Call mapper.updateByid () based on comment to make the change and get the number of affected rows returned
    // Check whether the return value is not 1
    // If yes, UpdateException is thrown
    
    // Encapsulate content in result
    // Return the query result
}
Copy the code

Concrete implementation:

@Override
public Comment update(Integer commentId, String content, Integer userId, Integer userType) {
    // Call mapper.selectByid () with the commentId argument to query for information about the modified comment
    Comment result = commentMapper.selectById(commentId);
    // Check whether the query result is null
    if (result == null) {
        // Yes: This "comment" does not exist. Throw CommentNotFoundException
        throw new CommentNotFoundException("Failed to modify comment! Trying to access comment data does not exist!");
    }

    // Based on the userId in the query result and the parameter userId, determine whether the query result data belongs to the current login user.
    // Based on the userType parameter, determine the identity of the current login user is "teacher",
    if(! result.getUserId().equals(userId) && userType ! = User.TYPE_TEACHER) {/ / does not meet the, if the two conditions are not allowed to modify, sell PermissionDeniedException
        throw new PermissionDeniedException("Failed to modify comment! Only the publisher and teacher can modify this comment!");
    }

    // Create a new Comment object
    Comment comment = new Comment();
    // Encapsulate commentId and Content in comment
    comment.setId(commentId);
    comment.setContent(content);
    // Call mapper.updateByid () based on comment to make the change and get the number of affected rows returned
    int rows = commentMapper.updateById(comment);
    // Check whether the return value is not 1
    if(rows ! =1) {
        // If yes, UpdateException is thrown
        throw new UpdateException("Failed to modify comment! Server busy, please try again later!");
    }

    // Encapsulate content in result
    result.setContent(content);
    // Return the query result
    return result;
}
Copy the code

(d) Unit testing

@Test
void update(a) {
    try {
        Integer commentId = 12;
        String content = "New Content!!!";
        Integer userId = 12;
        Integer userType = User.TYPE_STUDENT;
        Comment comment = service.update(commentId, content, userId, userType);
        log.debug("OK, comment >>> {}", comment);
    } catch (ServiceException e) {
        log.debug("Update comment failed");
        log.debug(Error type: {}, e.getClass().getName());
        log.debug("Error: {}", e.getMessage()); }}Copy the code

80. Modify the comment – controller layer

(a) Handling exceptions

UpdateException needs to be handled

(b) Design request

Request path: / API /v1/comments/{commentId}/update

Request parameters: @pathVariable (“commentId”) Integer commentId, String Content, @authenticationPrICIple UserInfo UserInfo

Request type: POST

R

(c) Processing of requests

// http://localhost:8080/api/v1/comments/11/update? content=Hello,Kafka!!!
@RequestMapping("/{commentId}/update")
public R<Comment> update(@PathVariable("commentId") Integer commentId,
                         String content,
                         @AuthenticationPrincipal UserInfo userInfo) {
    Comment comment = commentService.update(commentId, content,
            userInfo.getId(), userInfo.getType());
    return R.ok(comment);
}
Copy the code

(d) Tests

http://localhost:8080/api/v1/comments/10/update?content=NewContent

81. Modify the comments – front page

In the comment list, each comment has its own form for modifying comments. By default, all comments are folded up. When you click “Edit”, it expands.

Since each entry in the comment list is traversed, the target of these edit links and the ID of the field in which each form is located are all the same, causing clicking on any edit to expand all forms and clicking on it again to collapse all forms! You must first adjust the ID of each edit linked directory and the field in which the form resides.

Adjustments to the area of the form:

[img-TBQL1C8P-1596299955578] [image-20200731152343997.png]

Then adjust the target location to which “Edit” links:

At this point, it should be normal to expand and collapse the form for modifying comments after clicking “Edit” on the page!

Next, set the default value in the form control so that the original comment body is displayed when expanded:

Then, assign an ID to the text field control to facilitate subsequent retrieval of the comment body filled in the text field:

Finally, we need a response function that submits the event for the form binding:

In answers.js, add a new function, first testing using:

Add $.ajax() to get commentId and content:

If you need to fold the form after successful modification, you can add it after successful publication (because you already bound the ID to the form area) :

$('#updateComment' + commentId).collapse('hide');
Copy the code

82. On “Adopting the Answer”

It is recommended to add static member internal interfaces to the Question class in advance:

public static interface Status {
    String[] TEXTS = ["No reply"."To be resolved."."Resolved"];
    int UNRESOLVED = 0;
    int TO_BE_SOLVED = 1;
    int RESOLVED = 2;
}
Copy the code

Do the same for the Answer class:

public static interface Status {
    String[] TEXTS = {"Not adopted"."Accepted"};
	int TO_BE_ACCEPT = 0;
    int ACCEPTED = 1;
}
Copy the code

To “accept the answer”, you need to UPDATE the values of two tables:

  • willquestionThe tablestatusThe field value is updated toQuestion.Status.RESOLVED;
  • willanswerThe tablestatus_of_acceptThe field value is updated toAnswer.Status.ACCEPTED.

At the business layer, @Transactional is used because two tables need to be updated at once.

If you are developing a simple version, you can update only the values of these two fields in the above two tables!

If you are developing a harder version, you can add a rule that “only one answer per question can be accepted, and once adopted, no one will be allowed to add answers or comments, edit or delete existing answers or comments.”

83. Architecture – Introduction to Kafka

When the client sends a request to the server, the server will use a multi-threaded way to handle requests from different clients! However, if you have a large number of clients and each client request takes a long time to be processed, you can have a large number of threads running on the server side at the same time, all processing data, and obviously a large amount of data in memory!

In fact, not all requests need to be handled urgently! For some requests, it may be possible to “synchronise” the requests to “queue” them, reducing the strain on the server!

The basic function of Kafka is to send and receive messages. When using Kafka, you can call Kafka directly to send a message when you receive a request from a client in the controller. Then, Kafka will receive the sent message, and then process it. Based on this mechanism, Kafka can also achieve some “notification”, “push” effect!

Before learning about Kafka, you should be familiar with some related concepts and terms:

  • ProducerProducer: Produces and sends messages;
  • Consumer: consumer: receives messages and processes them;
  • Topic: topic: used to classify messages;
  • Record: Record: Record of messages processed by queue transmission;
  • Broker: middleman: The host server that implements the data store and is also responsible for replicating messages when used in a cluster.

In terms of performance, Kafka is a bit like Tomcat. You only need to turn its service on. The program in the project can send messages to the Kafka server.

Examples of asynchronous queues similar to Kafka include RabbitMQ, ZeroMQ, RocketMQ, and more.

Huawei Cloud Download address:

  • 2.5.0:mirrors.huaweicloud.com/apache/kafk…
  • 2.4.1:mirrors.huaweicloud.com/apache/kafk…