11第十一章 app端评论系统开发
目标
- 能够完成对mongodb的环境搭建
- 能够掌握mongodb的基本使用
- 能够完成app端评论的查询、发表、点赞等功能
- 能够完成app端评论回复的查询,发表、点赞功能
1 Mongodb
1.1Mongodb简介
MongoDB是一个开源、高性能、无模式的文档型数据库
应用场景:
- 支持文本查询
- 不需要支持事务,不存在复杂的多表查询
- 存储数据可持久化
- 需要TB甚至 PB 级别数据存储
- 需求变化较快,数据模型无法确认,预计使用快速迭代开发形式
- 需要至少2000以上的读写QPS【高性能】
- 能支持快速水平扩展【高扩展】
- 99.999%高可用【高可用】
1.2 Mongodb安装
1.2.1 拉取镜像
docker pull mongo
1.2.2 创建容器
docker run -di --name mongo-service -p 27017:27017 -v ~/data/mongodata:/data mongo
1.2.3 可视化工具
studio3t是mongodb优秀的客户端工具。官方地址在https://studio3t.com/
下载studio3t
同学不用下载,可以使用资料文件夹中提供好的软件安装即可。
安装并启动
创建连接
连接成功:
1.3 Mongodb快速入门案例
1.3.1 创建项目
创建项目:mongodb-demo
pom文件:
<groupId>com.itheima</groupId>
<artifactId>mongodb-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 继承Spring boot -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
</dependencies>
applicaton.yml
server:
port: 9001
spring:
application:
name: mongo-demo
引导类:
package com.itheima.mongo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MongoAppliction {
public static void main(String[] args) {
SpringApplication.run(MongoAppliction.class,args);
}
}
1.3.2 集成mongodb
创建配置文件mongo.properties
#主机地址
mongo.host=192.168.200.130
#端口号
mongo.port=27017
#数据库
mongo.dbname=leadnews-comment
添加配置,创建类MongoDBconfigure
package com.itheima.mongo.config;
import com.mongodb.MongoClient;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
@Data
@Configuration
@PropertySource("classpath:mongo.properties")
@ConfigurationProperties(prefix="mongo")
public class MongoDBconfigure {
private String host;
private Integer port;
private String dbname;
/**
* 自己构建MongoTemplate
* @return
*/
@Bean
public MongoTemplate getMongoTemplate() {
return new MongoTemplate(getSimpleMongoDbFactory());
}
/**
* 如果有用户名和密码也可以通过MongoClient其他构造函数传参
* @return
*/
public SimpleMongoDbFactory getSimpleMongoDbFactory() {
return new SimpleMongoDbFactory(new MongoClient(host, port), dbname);
}
}
1.3.3 实体类准备
实体类准备,添加注解@Document
与mongodb中的集合对应上
package com.itheima.mongo.pojo;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import java.math.BigDecimal;
import java.util.Date;
/**
* APP评论信息
*/
@Data
@Document("ap_comment")
public class ApComment {
/**
* id
*/
private String id;
/**
* 用户ID
*/
private Integer authorId;
/**
* 用户昵称
*/
private String authorName;
/**
* 文章id或动态id
*/
private Long entryId;
/**
* 频道ID
*/
private Integer channelId;
/**
* 评论内容类型
* 0 文章
* 1 动态
*/
private Short type;
/**
* 评论内容
*/
private String content;
/**
* 作者头像
*/
private String image;
/**
* 点赞数
*/
private Integer likes;
/**
* 回复数
*/
private Integer reply;
/**
* 文章标记
* 0 普通评论
* 1 热点评论
* 2 推荐评论
* 3 置顶评论
* 4 精品评论
* 5 大V 评论
*/
private Short flag;
/**
* 经度
*/
private BigDecimal longitude;
/**
* 维度
*/
private BigDecimal latitude;
/**
* 地理位置
*/
private String address;
/**
* 评论排列序号
*/
private Integer ord;
/**
* 创建时间
*/
private Date createdTime;
/**
* 更新时间
*/
private Date updatedTime;
}
1.3.4 基本增删改查
创建测试类:
package com.itheima.mongo.test;
import com.itheima.mongo.MongoApplication;
import com.itheima.mongo.pojos.ApComment;
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.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
@SpringBootTest(classes = MongoApplication.class)
@RunWith(SpringRunner.class)
public class MongoTest {
@Autowired
private MongoTemplate mongoTemplate;
//保存
@Test
public void saveTest(){
for (int i = 0; i <20 ; i++) {
ApComment apComment = new ApComment();
apComment.setAuthorName("小王");
apComment.setContent("上海是不错的城市");
apComment.setLikes(i);
mongoTemplate.save(apComment);
}
}
//查询一个
@Test
public void saveFindOne(){
ApComment apComment = mongoTemplate.findById("5fc2fc3fb60c9a039c44556e", ApComment.class);
apComment.setLikes(55);
mongoTemplate.save(apComment);
System.out.println(apComment);
}
//条件查询
@Test
public void testQuery(){
Query query = Query.query(Criteria.where("authorName").is("小王").and("likes").lt(10)).limit(5)
.with(Sort.by(Sort.Direction.DESC,"likes"));
List<ApComment> apComments = mongoTemplate.find(query, ApComment.class);
System.out.println(apComments);
}
@Test
public void testDel(){
mongoTemplate.remove(Query.query(Criteria.where("authorName").is("小王").and("likes").lt(10)),ApComment.class);
}
}
2 app端评论-发表评论
2.1 需求分析
- 文章详情页下方可以查看评论信息,按照点赞数量倒序排列,展示评论内容、评论的作者、点赞数、回复数、时间,默认查看20条评论,如果想查看更多,可以点击加载更多进行分页
- 可以针对当前文章发布评论
- 可以针对于某一条评论进行点赞操作
2.2 思路分析
(1)数据映射
对应实体及注解配置
package com.heima.model.comment.pojos;
import lombok.Data;
import org.springframework.data.mongodb.core.mapping.Document;
import java.math.BigDecimal;
import java.util.Date;
/**
* APP评论信息
*/
@Data
@Document("ap_comment")
public class ApComment {
/**
* id
*/
private String id;
/**
* 用户ID
*/
private Integer authorId;
/**
* 用户昵称
*/
private String authorName;
/**
* 文章id或动态id
*/
private Long entryId;
/**
* 频道ID
*/
private Integer channelId;
/**
* 评论内容类型
* 0 文章
* 1 动态
*/
private Short type;
/**
* 评论内容
*/
private String content;
/**
* 作者头像
*/
private String image;
/**
* 点赞数
*/
private Integer likes;
/**
* 回复数
*/
private Integer reply;
/**
* 文章标记
* 0 普通评论
* 1 热点评论
* 2 推荐评论
* 3 置顶评论
* 4 精品评论
* 5 大V 评论
*/
private Short flag;
/**
* 经度
*/
private BigDecimal longitude;
/**
* 维度
*/
private BigDecimal latitude;
/**
* 地理位置
*/
private String address;
/**
* 评论排列序号
*/
private Integer ord;
/**
* 创建时间
*/
private Date createdTime;
/**
* 更新时间
*/
private Date updatedTime;
}
APP评论信息点赞
package com.heima.model.comment.pojos;
import lombok.Data;
import org.springframework.data.mongodb.core.mapping.Document;
/**
* APP评论信息点赞
*/
@Data
@Document("ap_comment_like")
public class ApCommentLike {
/**
* id
*/
private String id;
/**
* 用户ID
*/
private Integer authorId;
/**
* 评论id
*/
private String commentId;
/**
* 0:点赞
* 1:取消点赞
*/
private Short operation;
}
leadnews-common和leadnews-model中引入
<!--mongoDB-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
评论与评论点赞表是一对多关系,一条评论可以让多个app用户点赞
(2)思路分析
根据文章id发表评论,输入内容发表评论,评论内容不能超过140字,评论内容需要做文本垃圾检测
2.3 功能实现
2.3.1 搭建评论微服务
(1)创建项目heima-leadnews-comment
(2)pom依赖
<dependencies>
<!-- 引入依赖模块 -->
<dependency>
<groupId>com.heima</groupId>
<artifactId>heima-leadnews-model</artifactId>
</dependency>
<dependency>
<groupId>com.heima</groupId>
<artifactId>heima-leadnews-common</artifactId>
</dependency>
<dependency>
<groupId>com.heima</groupId>
<artifactId>heima-leadnews-apis</artifactId>
</dependency>
<!-- Spring boot starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
(3)application.yml
其中自动配置项去除了关于数据源的配置,因为这个项目不需要查询数据库,查询的mongodb
server:
port: 9006
spring:
autoconfigure:
exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration
application:
name: leadnews-comment
cloud:
nacos:
discovery:
server-addr: 192.168.200.130:8848
(4)集成mongoodb
按照入门案例把mongodb集成到leadnews-common模块中,后面还有其他微服务也会用到
(5)config配置
leadnews-common中定义通用的配置
mongo.properties
mongo.host=192.168.200.130
mongo.port=27017
mongo.dbname=leadnews-comment
配置类:
package com.heima.common.mongo;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
@Data
@Configuration
@PropertySource("classpath:mongo.properties")
@ConfigurationProperties(prefix="mongo")
public class MongoDBconfigure {
private String host;
private Integer port;
private String dbname;
@Bean
public MongoTemplate getMongoTemplate() {
return new MongoTemplate(getSimpleMongoDbFactory());
}
public SimpleMongoDbFactory getSimpleMongoDbFactory() {
return new SimpleMongoDbFactory(new MongoClient(host, port), dbname);
}
}
(6)引导类
在新创建的leadnews-comment中引入mongo的配置
package com.heima.comment.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.heima.common.mongo")// 这样就能使在common模块中配置的MongoDBconfigure生效
public class MongoDBConfig {
}
2.3.2 其他配置准备
(1)需要使用的jackson的序列化、阿里云安全、接口knife4j、通用异常
如下配置:
package com.heima.commnet.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
// fixme 因我们comment模块需要使用的jackson的序列化、阿里云安全、接口knife4j、通用异常,所以需要添加如下
@ComponentScan({"com.heima.common.jackson","com.heima.common.aliyun","com.heima.common.exception"})
public class InitConfig {
}
(2)获取当前登录用户信息
评论必须在登录的情况下才能发布,所以需要验证用户获取用户信息,添加过滤类:
package com.heima.comment.filter;
import com.heima.model.user.pojos.ApUser;
import com.heima.utils.threadlocal.AppThreadLocalUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Order(1)
@WebFilter(filterName = "appTokenFilter", urlPatterns = "/*")
public class AppTokenFilter extends GenericFilterBean {
Logger logger = LoggerFactory.getLogger(AppTokenFilter.class);
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
// 测试和开发环境不过滤
String userId = request.getHeader("userId");
//如果userId为0,说明当前设备没有登录
if(userId!=null && Integer.valueOf(userId).intValue()!=0){
ApUser apUser = new ApUser();
apUser.setId(Integer.valueOf(userId));
AppThreadLocalUtils.setUser(apUser);
}
chain.doFilter(req,res);
}
}
需要在引导类中添加注解@ServletComponentScan
生效
2.3.3 接口定义
package com.heima.apis.comment;
import com.heima.model.comment.dtos.CommentSaveDto;
import com.heima.model.common.dtos.ResponseResult;
public interface CommentControllerApi {
/**
* 保存评论
* @param dto
* @return
*/
public ResponseResult saveComment(CommentSaveDto dto);
}
发表评论:CommentSaveDto
package com.heima.model.comment.dtos;
import com.heima.model.common.annotation.IdEncrypt;
import lombok.Data;
@Data
public class CommentSaveDto {
/**
* 文章id
*/
@IdEncrypt
private Long articleId;
/**
* 评论内容
*/
private String content;
}
2.3.4 用户微服务远程接口准备
评论中需要查询登录的用户信息,所以需要定义远程feign接口根据用户id获取用户信息
定义apis接口
package com.heima.apis.user;
import com.heima.model.user.pojos.ApUser;
public interface ApUserControllerApi {
/**
* 根据id查询app端用户信息
* @param id
* @return
*/
ApUser findUserById(Integer id);
}
在user的微服务中新增业务层层接口,mapper在之前已经定义完成
package com.heima.user.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.heima.model.user.pojos.ApUser;
public interface ApUserService extends IService<ApUser>{
}
实现类:
package com.heima.user.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.model.user.pojos.ApUser;
import com.heima.user.mapper.ApUserMapper;
import com.heima.user.service.ApUserService;
import org.springframework.stereotype.Service;
@Service
public class ApUserServiceImpl extends ServiceImpl<ApUserMapper, ApUser> implements ApUserService {
}
控制层:
package com.heima.user.controller.v1;
import com.heima.apis.user.ApUserControllerApi;
import com.heima.model.user.pojos.ApUser;
import com.heima.user.service.ApUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/v1/user")
public class ApUserController implements ApUserControllerApi {
@Autowired
private ApUserService apUserService;
@GetMapping("/{id}")
@Override
public ApUser findUserById(@PathVariable("id") Integer id) {
return apUserService.getById(id);
}
}
在评论微服务中增加feign远程接口
package com.heima.comment.feign;
import com.heima.model.user.pojos.ApUser;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient("leadnews-user")
public interface UserFeign {
@GetMapping("/api/v1/user/{id}")
ApUser findUserById(@PathVariable("id") Long id);
}
2.3.5 业务层
业务层接口:
package com.heima.comment.service;
import com.heima.model.comment.dtos.CommentSaveDto;
import com.heima.model.common.dtos.ResponseResult;
public interface CommentService {
/**
* 保存评论
* @return
*/
public ResponseResult saveComment(CommentSaveDto dto);
}
实现类:
package com.heima.comment.service.impl;
import com.heima.comment.feign.UserFeign;
import com.heima.comment.service.CommentService;
import com.heima.model.comment.dtos.CommentSaveDto;
import com.heima.model.comment.pojos.ApComment;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.user.pojos.ApUser;
import com.heima.utils.threadlocal.AppThreadLocalUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;
import java.util.*;
@Service
public class CommentServiceImpl implements CommentService {
@Autowired
private MongoTemplate mongoTemplate;
@Autowired
private UserFeign userFeign;
@Override
public ResponseResult saveComment(CommentSaveDto dto) {
//1.检查参数
if (dto.getArticleId() == null) {
return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
}
if (dto.getContent() != null && dto.getContent().length() > 140) {
return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE, "评论内容不能超过140字");
}
//2.判断是否登录
ApUser user = AppThreadLocalUtils.getUser();
if (user == null) {
return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
}
//3.安全过滤,自行实现
//4.保存评论
ApUser apUser = userFeign.findUserById(user.getId().longValue());
if (apUser == null) {
return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE, "当前登录信息有误");
}
ApComment apComment = new ApComment();
apComment.setAuthorId(apUser.getId());
apComment.setAuthorName(apUser.getName());
apComment.setContent(dto.getContent());
apComment.setEntryId(dto.getArticleId());
apComment.setCreatedTime(new Date());
apComment.setUpdatedTime(new Date());
apComment.setImage(apUser.getImage());
apComment.setLikes(0);
apComment.setReply(0);
apComment.setType((short) 0);
apComment.setFlag((short) 0);
mongoTemplate.insert(apComment);
return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
}
}
2.3.6 控制层
评论控制器
package com.heima.comment.controller.v1;
import com.heima.apis.comment.CommentControllerApi;
import com.heima.comment.service.CommentService;
import com.heima.model.comment.dtos.CommentSaveDto;
import com.heima.model.common.dtos.ResponseResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/v1/comment")
public class CommentController implements CommentControllerApi {
@Autowired
private CommentService commentService;
@PostMapping("/save")
@Override
public ResponseResult saveComment(@RequestBody CommentSaveDto dto){
return commentService.saveComment(dto);
}
}
2.3.7 配置网关
修改heima-leadnews-app-gateway网关微服务中的application.yml,新增评论微服务路由配置
#行为微服务
- id: leadnews-comment
uri: lb://leadnews-comment
predicates:
- Path=/comment/**
filters:
- StripPrefix= 1
2.3.8 测试
使用postman来测试
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)