Delete comments, modify comments and structure of the project--Introduction to Kafka (14)

78. Delete comments-Persistence layer

(A) Planning the SQL statements that need to be executed

The SQL statement that needs to be executed is roughly:

delete from comment where id=?

Generally, there are related checks before processing additions, deletions, and modifications, especially before the operations of deletion and modification, you should check whether the data being operated exists, whether you have permission to operate on the data, and other possible existence. Business Rules.

Regarding whether the data exists, you can get the answer by querying:

select * from comment where id=?

Regarding the authority to manipulate data, the business rule can be temporarily designed as "the publisher of the comment, or any teacher, can delete the comment". Regarding "whether it is the publisher of the comment", it can be judged by user_idcombining the log-in status in the above query results, and the user_id"whether it is a teacher" can be judged directly by the typeattribute in the log-in status .

Usually, data operations with very simple conditions can be directly completed by the functions in MyBatis Plus, and there is no need to specifically develop related functions!

(B) Abstract method of interface

no

(C) Configure SQL statements

no

(D) Unit testing

@SpringBootTest
@Slf4j
public class CommentsMapperTests {
    
    

    @Autowired
    CommentMapper mapper;

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

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

}

79. Delete comment-business layer

(A) Create an exception

Create a "comment data does not exist" exception:

public class CommentNotFoundException extends ServiceException {
    
    }

Create "insufficient permissions" exception:

public class PermissionDeniedException extends ServiceException {
    
    }

Create "Failed to delete data" exception:

public class DeleteException extends ServiceException {
    
    }

(B) Abstract method of interface

/**
 * 删除评论
 *
 * @param commentId 评论数据的id
 * @param userId    当前登录的用户的id
 * @param userType  当前登录的用户的类型
 * @return 成功删除的评论数据
 */
Comment delete(Integer commentId, Integer userId, Integer userType);

(C) Realize business

About the business realization steps:

public Comment delete(Integer commentId, Integer userId, Integer userType) {
    
    
    // 根据参数commentId调用mapper.selectById()查询被删除的“评论”的信息
    // 判断查询结果是否为null
    // 是:该“评论”不存在,抛出CommentNotFoundException异常
    
    // 基于查询结果中的userId,结合参数userId,判断查询结果数据是否是当前登录用户的,
    // 或基于参数userType,判断当前登录的用户的身份是“老师”,
    // 如果这2个条件都不符合,则不允许删除,抛出PermissionDeniedException
    
    // 根据参数commentId调用mapper.deleteById()执行删除,并获取返回的受影响行数
    // 判断返回值是否不为1
    // 是:抛出DeleteException
    
    // 返回查询结果
}

Specific business:

@Override
public Comment delete(Integer commentId, Integer userId, Integer userType) {
    
    
    // 根据参数commentId调用mapper.selectById()查询被删除的“评论”的信息
    Comment comment = commentMapper.selectById(commentId);
    // 判断查询结果是否为null
    if (comment == null) {
    
    
        // 是:该“评论”不存在,抛出CommentNotFoundException异常
        throw new CommentNotFoundException("删除评论失败!尝试访问的评论数据不存在!");
    }

    // 基于查询结果中的userId,结合参数userId,判断查询结果数据是否是当前登录用户的,
    // 或基于参数userType,判断当前登录的用户的身份是“老师”,
    // 注意:关于userId的判断,必须使用equals()方法
    // 使用 == 或 != 来判断Integer类型的数据,前提必须是数值范围一定在 -128至127 之间
    // 例如:Integer i1 = 128; Integer i2 = 128;
    // 使用 == 来判断以上 i1 和 i2 时,结果将是false
    if (!comment.getUserId().equals(userId) && userType != User.TYPE_TEACHER) {
    
    
        // 如果这2个条件都不符合,则不允许删除,抛出PermissionDeniedException
        throw new PermissionDeniedException("删除评论失败!仅发布者和老师可以删除此条评论!");
    }

    // 根据参数commentId调用mapper.deleteById()执行删除,并获取返回的受影响行数
    int rows = commentMapper.deleteById(commentId);
    // 判断返回值是否不为1
    if (rows != 1) {
    
    
        // 是:抛出DeleteException
        throw new DeleteException("删除评论失败!删除时出现未知错误,请联系系统管理员!");
    }

    // 返回查询结果
    return comment;
}

(D) Unit testing

@Test
void delete() {
    
    
    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("【删除评论失败】");
        log.debug("错误类型:{}", e.getClass().getName());
        log.debug("错误原因:{}", e.getMessage());
    }
}

80. Delete comment-controller layer

(A) Handling exceptions

You need R.Stateto add the status code corresponding to each exception, and then GloableExceptionHandlerhandle the three exceptions created by the business layer in it.

(B) Design request

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

Request parameters: @PathVariable("commentId") Integer commentId,@AuthenticationPriciple UserInfo userInfo

Request type:POST

Response result:R<Comment>

(C) Processing request

// 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);
}

(D) Test

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

81. Delete comments-frontend page

In Vue, if you need to traverse an array, and you need to get the subscript of each array element when traversing, you can:

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

The above code commentis the data of the array element to be traversed, and it indexis the subscript of the array element. It is stipulated in Vue 2.x that when traversing, you can inuse parentheses to frame the name of the array element and the array subscript on the left side , The last name in the brackets means the array subscript, and the name can be customized!

After using the above code, in the code area being traversed, you can pass indexthe subscript!

So, about traversing the entire list of comments:

Insert picture description here

<a>Configure in the "Delete Comment" tab:

Insert picture description here

Then, answers.jsdefine a new function in:
Insert picture description here

After completing the above content, you can test the click effect of "Delete" on the page. If it is correct, continue to add the code for subsequent requests and processing results in the above function.

The complete code is as follows:

Insert picture description here

82. Modified comments-persistence layer

(A) Planning the SQL statements that need to be executed

The SQL statement that needs to be executed to modify the comment is roughly:

update comment set content=? where id=?

The above functions can be realized updateById()directly by the method of MyBatis Plus .

In addition, before performing the modification, a check should also be performed on the modified data, and the check logic can be the same as in the "delete".

(B) Abstract method of interface

no

(C) Configure SQL statements

no

(D) Unit testing

@Test
void updateById() {
    
    
    Comment comment = new Comment();
    comment.setId(15);
    comment.setContent("项目的功能就快开发完了!!!");
    int rows = mapper.updateById(comment);
    log.debug("update over. rows={}", rows);
}

83. Modified comments-business layer

(A) Create an exception

Need to create "Failed to modify data" exception:

public class UpdateException extends ServiceException {
    
     }

(B) Abstract method of interface

/**
 * 修改评论
 *
 * @param commentId 评论数据的id
 * @param content   评论的正文
 * @param userId    当前登录的用户的id
 * @param userType  当前登录的用户的类型
 * @return 成功修改的评论数据
 */
Comment update(Integer commentId, String content, Integer userId, Integer userType);

(C) Realize business

About the business realization steps:

public Comment update(Integer commentId, String content, Integer userId, Integer type) {
    
    
    // 根据参数commentId调用mapper.selectById()查询被编辑的“评论”的信息
    // 判断查询结果(result)是否为null
    // 是:该“评论”不存在,抛出CommentNotFoundException异常
    
    // 基于查询结果中的userId,结合参数userId,判断查询结果数据是否是当前登录用户的,
    // 或基于参数userType,判断当前登录的用户的身份是“老师”,
    // 如果这2个条件都不符合,则不允许删除,抛出PermissionDeniedException
    
    // 创建新的Comment comment对象
    // 将commentId, content封装到comment中
    // 根据comment调用mapper.updateById()执行修改,并获取返回的受影响行数
    // 判断返回值是否不为1
    // 是:抛出UpdateException
    
    // 将content封装到result中
    // 返回查询结果
}

Implementation:

@Override
public Comment update(Integer commentId, String content, Integer userId, Integer userType) {
    
    
    // 根据参数commentId调用mapper.selectById()查询被修改的“评论”的信息
    Comment result = commentMapper.selectById(commentId);
    // 判断查询结果是否为null
    if (result == null) {
    
    
        // 是:该“评论”不存在,抛出CommentNotFoundException异常
        throw new CommentNotFoundException("修改评论失败!尝试访问的评论数据不存在!");
    }

    // 基于查询结果中的userId,结合参数userId,判断查询结果数据是否是当前登录用户的,
    // 或基于参数userType,判断当前登录的用户的身份是“老师”,
    if (!result.getUserId().equals(userId) && userType != User.TYPE_TEACHER) {
    
    
        // 如果这2个条件都不符合,则不允许修改,抛出PermissionDeniedException
        throw new PermissionDeniedException("修改评论失败!仅发布者和老师可以修改此条评论!");
    }

    // 创建新的Comment comment对象
    Comment comment = new Comment();
    // 将commentId, content封装到comment中
    comment.setId(commentId);
    comment.setContent(content);
    // 根据comment调用mapper.updateById()执行修改,并获取返回的受影响行数
    int rows = commentMapper.updateById(comment);
    // 判断返回值是否不为1
    if (rows != 1) {
    
    
        // 是:抛出UpdateException
        throw new UpdateException("修改评论失败!服务器忙,请稍后再次尝试!");
    }

    // 将content封装到result中
    result.setContent(content);
    // 返回查询结果
    return result;
}

(D) Unit testing

@Test
void update() {
    
    
    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评论失败】");
        log.debug("错误类型:{}", e.getClass().getName());
        log.debug("错误原因:{}", e.getMessage());
    }
}

80. Modification comments-controller layer

(A) Handling exceptions

Need to deal withUpdateException

(B) Design request

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

Request parameters: @PathVariable("commentId") Integer commentId, String content,@AuthenticationPriciple UserInfo userInfo

Request type:POST

Response result:R<Comment>

(C) Processing request

// 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);
}

(D) Test

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

81. Modify comments-front-end page

In the comment list, each comment has a dedicated form for modifying the comment. By default, all comments are collapsed. When you click "Edit", it will be expanded, and click again to collapse it!

Since each item in the comment list is generated by traversal, the target of these "edit" links and the ID of the field where each form is located are all the same, which will cause all forms to be expanded by clicking on any "edit", again Click to collapse all forms! You must first adjust the ID of each "edit" link directory and the field where the form is located.

Regarding the adjustment of the area where the form is located:

[External link image transfer failed. The source site may have an anti-leech link mechanism. It is recommended to save the image and upload it directly (img-TBqL1C8p-1596299955578)(image-20200731152343997.png)]

Then adjust the target position of the "Edit" link:

Insert picture description here

At this point, after clicking "Edit" on the page, the expansion and collapse of the modification comment form should be normal!

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

Insert picture description here

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

Insert picture description here

Finally, you need to bind the response function of the submission event to the form:

Insert picture description here

In answers.js, add a new function, first test and use:

Insert picture description here

After the commentIdsum has been obtained content, just add $.ajax()processing:

Insert picture description here

If the modification is successful, the form needs to be stowed, and it can be added after the publication is successful (because the ID has been bound to the form area before):

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

82. About "adopting answers"

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

public static interface Status {
    
    
    String[] TEXTS = ["未回复", "待解决", "已解决"];
    int UNRESOLVED = 0;
    int TO_BE_SOLVED = 1;
    int RESOLVED = 2;
}

Do the same in the Answerclass:

public static interface Status {
    
    
    String[] TEXTS = {
    
    "未采纳", "已采纳"};
	int TO_BE_ACCEPT = 0;
    int ACCEPTED = 1;
}

When "adopt the answer", you need to update (UPDATE) the values ​​of 2 data tables:

  • The questiontable statusfield value is updated Question.Status.RESOLVED;
  • The answertable status_of_acceptfield value is updated Answer.Status.ACCEPTED.

In the business layer, because two tables need to be updated at a time, they need to be used @Transactional.

If you develop a simple version, only update the values ​​of these 2 fields in the above 2 tables!

If you develop a more difficult version, you can add the rule "Each question can only accept 1 answer, and once adopted, everyone will not be allowed to add answers or comments, nor to 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 multi-threaded methods to process the requests of different clients! However, if the number of clients is very large, and each client's request takes a long time to be processed, it will result in a large number of threads running on the server side at the same time, all of which are processing data. Obviously the amount of data in memory It will be very big too!

In fact, not all requests are very urgent and need to be processed! For some requests, it is possible to use "synchronization" to make these requests "queued" to be processed, which can reduce the pressure on the server!

The most basic function of Kafka is to send out messages and receive messages. When Kafka is used, when a client request is received in the controller, Kafka can be directly called to send a message. Subsequently, Kafka will receive the sent message, and then process it. In the meantime, between sending and receiving, it may be There is a queue! Based on this mechanism, some "notification" and "push" effects can also be achieved through Kafka!

Before learning Kafka, you should have a general understanding of some related concepts and terms:

  • Producer: Producer: Produce messages and send out messages;
  • Consumer: Consumer: receive messages and process them;
  • Topic: Subject: used to classify messages;
  • Record: Record: The message record processed by queue transmission;
  • Broker: Intermediary: A host server used to implement data storage. When used in a cluster, it is also responsible for copying messages.

In terms of specific performance, Kafka is a bit like Tomcat. You only need to turn on its service. The program in the project can send messages to the Kafka server. After the Kafka server receives the message, it can process the message queue. The program in the project processes the messages in the order in the queue.

Kafka asynchronous queue and similar products are: RabbitMQ, ZeroMQ, RocketMQand so on.

Huawei Cloud download address:

  • 2.5.0:https://mirrors.huaweicloud.com/apache/kafka/2.5.0/kafka_2.13-2.5.0.tgz
  • 2.4.1:https://mirrors.huaweicloud.com/apache/kafka/2.4.1/kafka_2.13-2.4.1.tgz

Guess you like

Origin blog.csdn.net/qq_44273429/article/details/107739656