前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >项目之删除评论、修改评论及架构--Kafka简介(14)

项目之删除评论、修改评论及架构--Kafka简介(14)

作者头像
海拥
发布2021-08-23 16:23:08
6370
发布2021-08-23 16:23:08
举报
文章被收录于专栏:全栈技术

78. 删除评论-持久层

(a)规划需要执行的SQL语句

需要执行的SQL语句大致是:

代码语言:javascript
复制
delete from comment where id=?

通常,在处理增、删、改之前,还存在相关的检查,特别是删、改的操作之前,都应该检查被操作的数据是否存在、是否具有权限对该数据进行操作,及可能存在的其它业务规则。

关于数据是否存在,可以通过查询来得到答案:

代码语言:javascript
复制
select * from comment where id=?

关于操作数据的权限,可以暂将业务规则设计为“评论的发表者,或任何老师,都可以删除评论”。关于“是否是评论的发表者”,通过以上查询结果中的user_id结合登录状态中的user_id即可进行判断,关于“是否是老师”直接通过登录状态中的type属性即可判断。

通常,条件非常简单的数据操作都可以直接由MyBatis Plus中的功能直接完成,可以不必专门的开发相关功能!

(b)接口的抽象方法

(c)配置SQL语句

(d)单元测试
代码语言:javascript
复制
@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. 删除评论-业务层

(a)创建异常

创建“评论数据不存在”的异常:

代码语言:javascript
复制
public class CommentNotFoundException extends ServiceException {}

创建“权限不足”的异常:

代码语言:javascript
复制
public class PermissionDeniedException extends ServiceException {}

创建“删除数据失败”的异常:

代码语言:javascript
复制
public class DeleteException extends ServiceException {}
(b)接口的抽象方法
代码语言:javascript
复制
/**
 * 删除评论
 *
 * @param commentId 评论数据的id
 * @param userId    当前登录的用户的id
 * @param userType  当前登录的用户的类型
 * @return 成功删除的评论数据
 */
Comment delete(Integer commentId, Integer userId, Integer userType);
(c)实现业务

关于业务的实现步骤:

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

具体的实现业务:

代码语言:javascript
复制
@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)单元测试
代码语言:javascript
复制
@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. 删除评论-控制器层

(a)处理异常

需要在R.State中添加各异常对应的状态码,然后在GloableExceptionHandler中处理业务层创建的3种异常。

(b)设计请求

请求路径:/api/v1/comments/{commentId}/delete

请求参数:@PathVariable("commentId") Integer commentId, @AuthenticationPriciple UserInfo userInfo

请求类型:POST

响应结果:R<Comment>

(c)处理请求
代码语言:javascript
复制
// 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)测试

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

81. 删除评论-前端页面

在Vue中,如果需要遍历某个数组,并且,在遍历时需要获取每个数组元素的下标,在遍历时,可以:

代码语言:javascript
复制
v-for="(comment, index) in comments"

以上代码中的comment就是被遍历到的数组元素的数据,而index就是数组元素的下标,在Vue 2.x中规定,在遍历时,可以在in的左侧使用括号框住数组元素名称和数组下标,在括号中的最后一个名称即表示数组下标,名称是可以自定义的!

使用以上代码后,在被遍历的代码区域内,就可以通过index表示下标!

所以,关于遍历整个评论列表:

在“删除评论”的<a>标签中配置:

然后,在answers.js中定义新的函数:

完成以上内容后,即可测试页面的“删除”的点击效果,如果无误,则在以上函数中继续补充后续发请求并处理结果的代码。

完整的代码如下:

82. 修改评论-持久层

(a)规划需要执行的SQL语句

修改评论需要执行的SQL语句大致是:

代码语言:javascript
复制
update comment set content=? where id=?

以上功能可以通过MyBatis Plus的updateById()方法直接实现。

另外,在执行修改之前,也应该对被修改的数据执行检查,检查逻辑与“删除”时可以相同。

(b)接口的抽象方法

(c)配置SQL语句

(d)单元测试
代码语言:javascript
复制
@Test
void updateById() {
    Comment comment = new Comment();
    comment.setId(15);
    comment.setContent("项目的功能就快开发完了!!!");
    int rows = mapper.updateById(comment);
    log.debug("update over. rows={}", rows);
}

83. 修改评论-业务层

(a)创建异常

需要创建“修改数据失败”的异常:

代码语言:javascript
复制
public class UpdateException extends ServiceException { }
(b)接口的抽象方法
代码语言:javascript
复制
/**
 * 修改评论
 *
 * @param commentId 评论数据的id
 * @param content   评论的正文
 * @param userId    当前登录的用户的id
 * @param userType  当前登录的用户的类型
 * @return 成功修改的评论数据
 */
Comment update(Integer commentId, String content, Integer userId, Integer userType);
(c)实现业务

关于业务的实现步骤:

代码语言:javascript
复制
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中
    // 返回查询结果
}

具体实现:

代码语言:javascript
复制
@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)单元测试
代码语言:javascript
复制
@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. 修改评论-控制器层

(a)处理异常

需要处理UpdateException

(b)设计请求

请求路径:/api/v1/comments/{commentId}/update

请求参数:@PathVariable("commentId") Integer commentId, String content, @AuthenticationPriciple UserInfo userInfo

请求类型:POST

响应结果:R<Comment>

(c)处理请求
代码语言:javascript
复制
// 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)测试

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

81. 修改评论-前端页面

在评论列表中,每个评论都有一个专属的表单用于修改评论,默认全部是收起的,当点击“编辑”时,会将其展开,再次点击,会收起!

由于评论列表的每一项都是遍历生成的,所以,这些“编辑”链接的目标及各表单所在匹域的ID全部是相同的,则会导致点击任何一个“编辑”会展开所有表单,再次点击会收起所有表单!必须先调各“编辑”链接的目录和表单所在匹域的ID。

关于表单所在区域的调整:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TBqL1C8p-1596299955578)(image-20200731152343997.png)]

然后调整“编辑”链接到的目标位置:

至此,点击页面上的“编辑”后,修改评论的表单的展开和收起应该是正常的!

接下来,在表单控件中设置默认值,使得展开时即显示原来的评论正文:

然后,为文本域控件分配ID,便于后续获取文本域中填写的评论正文:

最后,还需要为表单绑定提交事件的响应函数:

answers.js中,添加新的函数,先测试使用:

在已经获取到commentIdcontent的基础上,添加$.ajax()处理即可:

如果修改成功后,需要将表单收起,可以在发表成功后添加(因为此前已经为表单区域绑定了ID):

代码语言:javascript
复制
$('#updateComment' + commentId).collapse('hide');

82. 关于“采纳答案”

建议事先在Question类中添加静态成员内部接口:

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

Answer类中也作同样的处理:

代码语言:javascript
复制
public static interface Status {
    String[] TEXTS = {"未采纳", "已采纳"};
	int TO_BE_ACCEPT = 0;
    int ACCEPTED = 1;
}

“采纳答案”时,需要修改(UPDATE)2张数据表的值:

  • question表的status字段值更新为Question.Status.RESOLVED
  • answer表的status_of_accept字段值更新为Answer.Status.ACCEPTED

在业务层,由于需要一次性更新2张表,需要使用@Transactional

如果开发简单版,则只更新以上2张表中的这2个字段值即可!

如果开发较难的版本,可以添加规则“每个问题只能采纳1个答案,并且,一旦采纳,所有人将不允许添加答案或评论,也不允许对现有的答案或评论进行编辑、删除”。

83. 架构–Kafka简介

当客户端向服务器端发送请求后,服务器端会使用多线程的方式来处理不同客户端的请求!但是,如果客户端数量非常多,并且每个客户端的请求在被处理时耗时较长,就会导致在服务器端存在大量的线程同时处于运行状态,都 在处理数据,显然内存中的数据量也会非常大!

其实,并不是所有的请求都是非常急迫的需要被处理的!对于部分请求来说,可能使用“同步”的方式使得这些请求“排队”被处理,反而可以减轻服务器的压力!

Kafka的最基本功能就是:发出消息,接收消息。当使用Kafka时,可以在控制器中收到客户端的请求时,直接调用Kafka来发出消息,后续,Kafka就会收到所发出的消息,然后进行处理,其间,发出到接收之间,可能是存在队列的!基于这样的机制,还可以通过Kafka实现一些“通知”、“推送”的效果!

在学习Kafka之前,应该大致了解一些相关的概念与术语:

  • Producer:生产者:生产消息,并发出消息;
  • Consumer:消费者:接收消息,并处理消息;
  • Topic:主题:用于对消息进行分类;
  • Record:记录:被队列传输处理的消息记录;
  • Broker:中间商:用于实现数据存储的主机服务器,在集群中使用时,也负责复制消息。

在具体的表现方面,Kafka有点像是Tomcat,只需要将其服务开启即可,项目中的程序就可以向Kafka服务器发送消息,则Kafka服务器接收到消息后,可以对消息队列进行处理,后续,项目中的程序就按照队列中的顺序来处理消息。

与Kafka类似的异步队列的产品还有:RabbitMQZeroMQRocketMQ等。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/08/02 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 78. 删除评论-持久层
    • (a)规划需要执行的SQL语句
      • (b)接口的抽象方法
        • (c)配置SQL语句
          • (d)单元测试
          • 79. 删除评论-业务层
            • (a)创建异常
              • (b)接口的抽象方法
                • (c)实现业务
                  • (d)单元测试
                  • 80. 删除评论-控制器层
                    • (a)处理异常
                      • (b)设计请求
                        • (c)处理请求
                          • (d)测试
                          • 81. 删除评论-前端页面
                          • 82. 修改评论-持久层
                            • (a)规划需要执行的SQL语句
                              • (b)接口的抽象方法
                                • (c)配置SQL语句
                                  • (d)单元测试
                                  • 83. 修改评论-业务层
                                    • (a)创建异常
                                      • (b)接口的抽象方法
                                        • (c)实现业务
                                          • (d)单元测试
                                          • 80. 修改评论-控制器层
                                            • (a)处理异常
                                              • (b)设计请求
                                                • (c)处理请求
                                                  • (d)测试
                                                  • 81. 修改评论-前端页面
                                                  • 82. 关于“采纳答案”
                                                  • 83. 架构–Kafka简介
                                                  相关产品与服务
                                                  云服务器
                                                  云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
                                                  领券
                                                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档