用Spring Boot + MongoDB,做一个作品评论的功能,用户可以发表评论;其他用户可以对作品现有的评论进行回复;最顶级的评论者,可以对回复评论的人再回复评论。其中,评论的数据结构如下:
t_resource_comment {
_id:,
resource_id: 123,
comment_user_id: "A",
comment_user_name:,
comment_content: "A的评论",
ctime:,
status:,
comment_responses: [
{
response_user_id: "B",
response_user_name:,
response_content:[
{ctime:, content:"B给A的第一个评论"},
{ctime:, content:"B给A的第二个评论"},
{ctime:, content:"B给A的第三个评论"}
]
get_reply:[
{ctime:, content:"这是A给B的某一个评论的回复如果有就对应插入index对应的元素没有就是空串"},
{ctime:, content:"A没有回复B这一条就是空串"},
{ctime:, content:"A个神经病跳着回复了这一条评论,这数组的第三个元素就是A回复的内容"}
]
},
{
response_user_id: "C",
response_user_name:,
response_content:[
{ctime:, content:"C给A的第一个评论"},
{ctime:, content:"C给A的第二个评论"},
{ctime:, content:"C给A的第三个评论"}
]
get_reply:[
{ctime:, content:"这是A给C的某一个评论的回复如果有就对应插入index对应的元素没有就是空串"},
{ctime:, content:"A没有回复C这一条就是空串"},
{ctime:, content:"A个神经病跳着回复了这一条评论,这数组的第三个元素就是A回复的内容"}
]
},
...
]
}
比如,A对文章或作品123发表了一个评论;B看到发表的评论后,给A的评论回复了一个评论;A看到B的回复后,又对B的回复评论做了一个回复。一共三级。
创建一个Spring Boot项目,pom.xml如下:
4.0.0
com.fhbean.springboot
springboot-mongodb
0.0.1-SNAPSHOT
jar
springboot-mongodb
Demo project for Spring Boot
org.springframework.boot
spring-boot-starter-parent
2.0.1.RELEASE
UTF-8
UTF-8
1.8
org.springframework.boot
spring-boot-starter-data-mongodb
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-maven-plugin
评论相关的实体类:
package com.fhbean.springboot.mongodb.entity;
import java.util.Date;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
@Document(collection = "resource_comment")
public class ResourceComment {
@Id
private String id;
/**
* 接收评论的资源ID或作品ID
*/
@Indexed
@Field(value = "resource_id")
private Long resourceId;
/**
* 发表评论的用户ID
*/
@Indexed
@Field(value = "comment_user_id")
private Long commentUserId;
/**
* 发表评论的用户名
*/
@Field(value = "comment_user_name")
private String commentUserName;
/**
* 发表的评论内容
*/
@Field(value = "comment_content")
private String commentContent;
/**
* 发表评论的时间
*/
private Date ctime;
/**
* 此条评论的状态:0为正常....
*/
private String status;
/**
* 对此评论的回复
*/
@Field(value = "comment_responses")
private CommentResponse[] commentResponses;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Long getResourceId() {
return resourceId;
}
public void setResourceId(Long resourceId) {
this.resourceId = resourceId;
}
public Long getCommentUserId() {
return commentUserId;
}
public void setCommentUserId(Long commentUserId) {
this.commentUserId = commentUserId;
}
public String getCommentUserName() {
return commentUserName;
}
public void setCommentUserName(String commentUserName) {
this.commentUserName = commentUserName;
}
public String getCommentContent() {
return commentContent;
}
public void setCommentContent(String commentContent) {
this.commentContent = commentContent;
}
public Date getCtime() {
return ctime;
}
public void setCtime(Date ctime) {
this.ctime = ctime;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public CommentResponse[] getCommentResponses() {
return commentResponses;
}
public void setCommentResponses(CommentResponse[] commentResponses) {
this.commentResponses = commentResponses;
}
}
package com.fhbean.springboot.mongodb.entity;
import org.springframework.data.mongodb.core.mapping.Field;
public class CommentResponse {
/**
* 回复评论的用户ID
*/
@Field(value = "response_user_id")
private Long responseUserId;
/**
* 回复评论的用户名
*/
@Field(value = "response_user_name")
private String responseUserName;
/**
* 回复的评论内容
*/
@Field(value = "response_contents")
private ResponseContent[] responseContents;
/**
* 一楼评论作者 对 回复评论者 的回复
*/
@Field(value = "get_replys")
private ResponseContent[] getReplys;
public Long getResponseUserId() {
return responseUserId;
}
public void setResponseUserId(Long responseUserId) {
this.responseUserId = responseUserId;
}
public String getResponseUserName() {
return responseUserName;
}
public void setResponseUserName(String responseUserName) {
this.responseUserName = responseUserName;
}
public ResponseContent[] getResponseContents() {
return responseContents;
}
public void setResponseContents(ResponseContent[] responseContents) {
this.responseContents = responseContents;
}
public ResponseContent[] getGetReplys() {
return getReplys;
}
public void setGetReplys(ResponseContent[] getReplys) {
this.getReplys = getReplys;
}
}
package com.fhbean.springboot.mongodb.entity;
import java.util.Date;
public class ResponseContent {
private Date ctime;
private String content;
public ResponseContent() {
super();
}
public ResponseContent(Date ctime, String content) {
super();
this.ctime = ctime;
this.content = content;
}
public Date getCtime() {
return ctime;
}
public void setCtime(Date ctime) {
this.ctime = ctime;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
创建Repository接口
package com.fhbean.springboot.mongodb.reposistory;
import org.springframework.data.mongodb.repository.MongoRepository;
import com.fhbean.springboot.mongodb.entity.ResourceComment;
public interface ResourceCommentRepository extends MongoRepository {
}
创建Service
package com.fhbean.springboot.mongodb.service;
import java.util.List;
import com.fhbean.springboot.mongodb.entity.ResourceComment;
public interface ResourceCommentService {
/**
* 查询某资源的所有评论
*
* @param resourceId
* @return
*/
public List findResourceComment(Long resourceId);
/**
* 对指定资源发表评论
*
* @param resourceId 资源ID
* @param commentUserId 用户ID
* @param commentUserName 用户名
* @param commentContent 评论内容
* @return 1成功,0失败
*/
public int saveComment(Long resourceId, Long commentUserId, String commentUserName, String commentContent);
/**
* 回复评论
*
* @param id 原评论的ID
* @param responseUserId 回复评论的用户ID
* @param responseUserName 回复评论的用户名
* @param responseContent 回复的内容
* @return 1成功,0失败
*/
public int saveResponse(String id, Long responseUserId, String responseUserName, String responseContent);
/**
* 一楼评论者 回复二楼评论者 的评论
*
* @param id 原评论的ID
* @param responseUserId 回复评论的用户ID
* @param index 要回复的评论索引值,从0开始
* @param replyContent 一楼评论者 回复的内容
* @return 1成功,0失败
*/
public int saveReply(String id, Long responseUserId, int index, String replyContent);
}
package com.fhbean.springboot.mongodb.service.impl;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import com.fhbean.springboot.mongodb.entity.CommentResponse;
import com.fhbean.springboot.mongodb.entity.ResourceComment;
import com.fhbean.springboot.mongodb.entity.ResponseContent;
import com.fhbean.springboot.mongodb.reposistory.ResourceCommentRepository;
import com.fhbean.springboot.mongodb.service.ResourceCommentService;
@Service(value = "resourceCommentService")
public class ResourceCommentServiceImpl implements ResourceCommentService {
@Autowired
private ResourceCommentRepository resourceCommentRepository;
/**
* 查询某个资源的所有评论
*/
@Override
public List findResourceComment(Long resourceId) {
if (null == resourceId) {
return null;
}
ResourceComment rc = new ResourceComment();
rc.setResourceId(resourceId);
return resourceCommentRepository.findAll(Example.of(rc));
}
/**
* 发表评论
*/
@Override
public int saveComment(Long resourceId, Long commentUserId, String commentUserName, String commentContent) {
//数据合法性校验 begin
if (null == resourceId || null == commentUserId) {
return 0;
}
if (StringUtils.isEmpty(commentUserName) || StringUtils.isEmpty(commentContent)) {
return 0;
}
//数据合法性校验 end
//保存评论 begin
ResourceComment rc = new ResourceComment();
rc.setResourceId(resourceId);
rc.setCommentUserId(commentUserId);
rc.setCommentUserName(commentUserName);
rc.setCommentContent(commentContent);
rc.setCtime(new Date());
rc.setStatus("0"); //此处可采用常量或枚举类型
resourceCommentRepository.save(rc);
//保存评论 end
return 0;
}
/**
* 回复评论
*/
@Override
public int saveResponse(String id, Long responseUserId, String responseUserName, String responseContent) {
//数据合法性校验 begin
if (StringUtils.isEmpty(id)) {
return 0;
}
if (null == responseUserId) {
return 0;
}
if (StringUtils.isEmpty(responseUserName) || StringUtils.isEmpty(responseContent)) {
return 0;
}
//数据合法性校验 end
//查找指定评论 begin
ResourceComment rc = new ResourceComment();
rc.setId(id);
Optional optional = resourceCommentRepository.findOne(Example.of(rc));
if (!optional.isPresent()) {
//未找到指定的评论
return 0;
}
rc = optional.get();
//查找指定评论 end
//得到巳有回复
CommentResponse[] crs = rc.getCommentResponses();
if (null == crs) {
//此评论之前没有任何人回复,当前回复作为第一条回复
CommentResponse cr = getNewResponse(responseUserId, responseUserName, responseContent);
rc.setCommentResponses(new CommentResponse[]{cr});
resourceCommentRepository.save(rc);
return 1;
}
//己有人回复过评论,判断responseUserId是否回复过评论 begin
//responseUserId之前是否回复过此评论 begin
boolean responsed = false;
for (int i = 0; i < crs.length; i++) {
if (crs[i].getResponseUserId().equals(responseUserId)) {
responsed = true; //之前回复过
ResponseContent[] rcsNew = getNewResponseContent(responseContent, crs, crs[i].getResponseContents());
crs[i].setResponseContents(rcsNew);
break; //已经回复,不再继续循环
}
}
//己有人回复过评论,判断responseUserId是否回复过评论 end
if (responsed) {
//之前,responseUserId已经回复过此评论,在现有回复后追加一个回复即可
resourceCommentRepository.save(rc);
return 1;
}
//之前别人回复过此评论,但responseUserId没有回复过此评论 begin
//将之前所有人的回复转储到新的数组
CommentResponse[] crsNew = new CommentResponse[crs.length + 1];
CommentResponse cr = getNewResponse(responseUserId, responseUserName, responseContent);
for (int i = 0; i < crs.length; i++) {
crsNew[i] = crs[i];
}
crsNew[crsNew.length - 1] = cr;
//之前别人回复过此评论,但responseUserId没有回复过此评论 end
rc.setCommentResponses(crsNew);
resourceCommentRepository.save(rc);
return 1;
}
/**
* 回复评论的评论
*/
@Override
public int saveReply(String id, Long responseUserId, int index, String replyContent) {
//数据合法性校验 begin
if (StringUtils.isEmpty(id)) {
return 0;
}
if (null == responseUserId) {
return 0;
}
if (index < 0) {
//索引值不能小于零
return 0;
}
if (StringUtils.isEmpty(replyContent)) {
return 0;
}
//数据合法性校验 end
//查找指定评论 begin
ResourceComment rc = new ResourceComment();
rc.setId(id);
Optional optional = resourceCommentRepository.findOne(Example.of(rc));
if (!optional.isPresent()) {
//未找到指定的评论
return 0;
}
rc = optional.get();
//查找指定评论 end
CommentResponse[] crs = rc.getCommentResponses();
if (null == crs || crs.length < 1) {
//没有人回复过评论
return 0;
}
for (int i = 0; i < crs.length; i++) {
if (responseUserId.equals(crs[i].getResponseUserId())) {
//找到responseUserId回复的评论
ResponseContent[] rcs = crs[i].getResponseContents();//回复的评论数组
if (null == rcs || index >= rcs.length) {
//responseUserId没有对评论回复任何内容或index越界,指定的回复不存在,不再继续
return 0;
}
ResponseContent[] grs = crs[i].getGetReplys();
if (null == grs) {
//之前没有回复过评论的评论
grs = new ResponseContent[index+1];
ResponseContent reply = new ResponseContent(new Date(), replyContent);
grs[index] = reply;
crs[i].setGetReplys(grs);
} else {
//之前回复过评论的评论
if (index >= grs.length) {
//索引值超过现有回复数组的长度,给数组增长
ResponseContent[] grsNew = new ResponseContent[index + 1];
ResponseContent reply = new ResponseContent(new Date(), replyContent);
grsNew[index] = reply;
crs[i].setGetReplys(grsNew);
} else {
//索引值未超过回复数组的长度,reply内容直接覆盖索引值的reply
ResponseContent reply = new ResponseContent(new Date(), replyContent);
grs[index] = reply;
crs[i].setGetReplys(grs);
}
}
resourceCommentRepository.save(rc);
return 1;
}
}
return 0;
}
private ResponseContent[] getNewResponseContent(String responseContent, CommentResponse[] crs, ResponseContent[] rcs) {
ResponseContent[] rcsNew = null;
int rcsSize = 0;
if (null == rcs) {
rcsSize++;
rcsNew = new ResponseContent[rcsSize]; //防止程序异常,没有回复
} else {
rcsSize = rcs.length + 1;
rcsNew = new ResponseContent[rcsSize];
for (int i = 0; i < rcs.length; i++) {
//原先的回复转储到新的数组
rcsNew[i] = rcs[i];
}
}
ResponseContent rcon = new ResponseContent();
rcon.setCtime(new Date());
rcon.setContent(responseContent);
rcsNew[rcsSize - 1] = rcon;
return rcsNew;
}
private CommentResponse getNewResponse(Long responseUserId, String responseUserName, String responseContent) {
CommentResponse cr = new CommentResponse();
cr.setResponseUserId(responseUserId);
cr.setResponseUserName(responseUserName);
ResponseContent rct = new ResponseContent();
rct.setCtime(new Date());
rct.setContent(responseContent);
cr.setResponseContents(new ResponseContent[] {rct});
return cr;
}
}
创建Controller
package com.fhbean.springboot.mongodb.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.fhbean.springboot.mongodb.service.ResourceCommentService;
@RestController
@RequestMapping(value = "/rc")
public class ResourceCommentController {
@Autowired
private ResourceCommentService resourceCommentService;
/**
* 列出某个资源的所有评论
*
* 客户端页面要做到这些权限限制
* 三级按钮:发表评论btn1,回复评论btn2,回复评论的评论btn3
* 当前登录用户是本条评论的第一级评论者,
* 可以看到发表评论按钮btn1
* 不可以看到第二级的回复按钮btn2,即不能给自己的评论回复
* 可以看到第三级的按钮btn3,这个按钮可以给回复评论的人回复评论
*
* 当前登录用户是本条评论的第二级评论者,即回复评论者
* 可以看到发表评论按钮btn1
* 可以看到第二级的回复按钮btn2,即可以给他人的评论回复
* 不可以看到第三级的按钮btn3,即不可以给自己的回复发表回复评论
*
* 当前登录用户不是本条评论的第一级和第二级用户
* 可以看到发表评论按钮btn1
* 可以看到第二级的回复按钮btn2,即可以给他人的评论回复
* 不可以看到第三级的按钮btn3,只有版主才可以回复评论的评论
*
* @param resourceId
* @return
* @throws Exception
*
* example http://localhost:8080/rc/listComments/1
*/
@ResponseBody
@RequestMapping(value = "/listComments/{resourceId}")
public Object listComments(@PathVariable(value = "resourceId", required = true)Long resourceId) throws Exception {
return resourceCommentService.findResourceComment(resourceId);
}
/**
* 发表评论
* @param resourceId
* @param commentUserId
* @param commentUserName
* @param commentContent
* @return
* @throws Exception
*/
@ResponseBody
@RequestMapping(value = "/saveComment")
public Object saveComment(
@RequestParam(value = "resourceId", required = true)Long resourceId,
@RequestParam(value = "commentUserId", required = true)Long commentUserId,
@RequestParam(value = "commentUserName", required = true)String commentUserName,
@RequestParam(value = "commentContent", required = true)String commentContent) throws Exception {
return resourceCommentService.saveComment(resourceId, commentUserId, commentUserName, commentContent);
}
/**
* 回复评论
* @param id
* @param responseUserId
* @param responseUserName
* @param responseContent
* @return
* @throws Exception
*/
@ResponseBody
@RequestMapping(value = "/saveResponse")
public Object saveResponse(
@RequestParam(value = "id", required = true)String id,
@RequestParam(value = "responseUserId", required = true)Long responseUserId,
@RequestParam(value = "responseUserName", required = true)String responseUserName,
@RequestParam(value = "responseContent", required = true)String responseContent) throws Exception {
return resourceCommentService.saveResponse(id, responseUserId, responseUserName, responseContent);
}
/**
* 回复评论的评论
*
* @param id
* @param responseUserId
* @param index
* @param replyContent
* @return
* @throws Exception
*/
@ResponseBody
@RequestMapping(value = "/saveReply")
public Object saveReply(
@RequestParam(value = "id", required = true)String id,
@RequestParam(value = "responseUserId", required = true)Long responseUserId,
@RequestParam(value = "index", required = true)Integer index,
@RequestParam(value = "replyContent", required = true)String replyContent) throws Exception {
if (null == index) {
index = 0;
}
return resourceCommentService.saveReply(id, responseUserId, index, replyContent);
}
}
配置application.yml
spring:
data:
mongodb:
host: 127.0.0.1
port: 27017
database: foobar
编写testcase
package com.fhbean.springboot.mongodb;
import java.util.Optional;
import org.junit.Assert;
import org.junit.Before;
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.Example;
import org.springframework.test.context.junit4.SpringRunner;
import com.fhbean.springboot.mongodb.entity.ResourceComment;
import com.fhbean.springboot.mongodb.reposistory.ResourceCommentRepository;
import com.fhbean.springboot.mongodb.service.ResourceCommentService;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ResourceCommentTests {
@Autowired
private ResourceCommentRepository resourceCommentRepository;
@Autowired
private ResourceCommentService resourceCommentService;
@Before
public void setUp() {
}
/**
* 测试评论
*/
@Test
public void testSaveComment() {
Long resourceId = 1L;
Long commentUserId = 1L;
String commentUserName = "user" + commentUserId;
String commentContent = commentUserName + "'s comment on " + System.currentTimeMillis();
resourceCommentService.saveComment(resourceId, commentUserId, commentUserName, commentContent);
}
/**
* 测试评论回复
*/
@Test
public void testSaveResponse() {
String id = "5acdae25c986d7518c1a7081";
Long responseUserId = 2L;
String responseUserName = "user" + responseUserId;
String responseContent = responseUserName + "'s response on " + System.currentTimeMillis();
resourceCommentService.saveResponse(id, responseUserId, responseUserName, responseContent);
}
/**
* 测试回复评论的评论
*/
@Test
public void testSaveReply() {
String id = "5acdae25c986d7518c1a7081";
Long responseUserId = 2L;
int index = 1;
String replyContent = "user"+responseUserId+" reply index["+index+"] on " + System.currentTimeMillis();
resourceCommentService.saveReply(id, responseUserId, index, replyContent);
}
@Test
public void testFind() {
ResourceComment rc = new ResourceComment();
rc.setResourceId(1L);
rc.setCommentUserId(1L);
Example example = Example.of(rc);
Optional optional = resourceCommentRepository.findOne(example);
Assert.assertTrue(optional.isPresent());
Assert.assertEquals("user1", optional.get().getCommentUserName());
}
}
未编写页面ajax脚本
项目结构:
依次跑一下ResourceCommentTests的三个方法:testSaveComment()、testSaveResponse()、testSaveReply()
然后启动Spring Boot项目,在浏览器地址栏录入http://localhost:8080/rc/listComments/1
项目在github上的源码 https://github.com/cjrjc/springboot-mongodb.git
参考 https://segmentfault.com/a/1190000010391079