1. 帖子作者发布帖子,帖子标题可以为空,帖子内容不能为空,且不少于16个汉字;

2. 帖子可以加入不多于五个话题,且话题不能重复加入;
3. 帖子标题和内容通过内容过滤后方能发布,如果未能通过内容过滤,则需要经过运营审核之后才能发布。
This commit is contained in:
xieYj 2021-05-11 18:14:32 +08:00
parent a18783fca4
commit 6d4d53054d
14 changed files with 504 additions and 0 deletions

View File

@ -0,0 +1,99 @@
package cn.allms.community.domain.model.post;
import cn.allms.community.domain.model.topic.TopicPost;
import cn.allms.community.infrastructure.constant.CommonConstants;
import cn.allms.community.infrastructure.constant.ExceptionCodeConstant;
import cn.allms.community.infrastructure.exception.BusinessException;
import lombok.Data;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
/**
* 帖子对象
* @author xieYj
* @date 2021/5/11 11:50
*/
@Data
public class Post {
/**
* 帖子ID
*/
private Long id;
/**
* 帖子作者ID
*/
private Long authorId;
/**
* 帖子标题
*/
private String title;
/**
* 帖子内容
*/
private String sourceContent;
/**
* 发帖时间戳
*/
private Long postingTime;
/**
* 最后修改时间
*/
private Long lastModifyTime;
/**
* 加入的话题
*/
private Set<TopicPost> topics = new HashSet<>();
/**
* 最多加入的主题数
*/
public static final Integer MAX_JOINED_TOPICS_NUM = 5;
/**
* 自定义的setter优先会调用此set方法而不是lombok自带的
* @param authorId 作者Id
*/
public void setAuthorId(Long authorId) {
Assert.isTrue(authorId > 0, "帖子作者Id必须大于0");
this.authorId = authorId;
}
public void setSourceContent(String sourceContent) {
Assert.isTrue(Objects.nonNull(sourceContent), "帖子内容不能为空");
Assert.isTrue(sourceContent.length() >= 16, "帖子内容必须大于16");
this.sourceContent = sourceContent;
}
/**
* 将帖子关联话题 | 加入话题
*
* @param topicIds 话题集合
*/
public void joinTopics(String topicIds) throws BusinessException {
if (Objects.isNull(topicIds)) {
return;
}
String[] topicIdArray = topicIds.split(CommonConstants.TOPIC_SPLIT_COMMA);
for (String s : topicIdArray) {
TopicPost topicPost = new TopicPost(Long.valueOf(s), this.getId());
this.topics.add(topicPost);
if (topicSize() > MAX_JOINED_TOPICS_NUM) {
throw new BusinessException(ExceptionCodeConstant.ONE_POST_MOST_JOIN_INTO_FIVE_TOPICS);
}
}
}
public int topicSize(){
return this.topics.size();
}
}

View File

@ -0,0 +1,52 @@
package cn.allms.community.domain.model.post;
import cn.allms.community.infrastructure.constant.ExceptionCodeConstant;
import cn.allms.community.infrastructure.exception.BusinessException;
import lombok.Data;
/**
* 帖子作者对象
* @author xieYj
* @date 2021/5/11 11:51
*/
@Data
public class PostAuthor {
/**
* 帖子内容最小长度
*/
private final static int MIN_LENGTH_POST_SOURCE_CONTENT = 16;
/**
* 作者ID
*/
private Long id;
/**
* 作者账号
*/
private String account;
/**
* 作者昵称
*/
private String nickname;
/**
* 发布帖子
*
* @param title 帖子标题
* @param sourceContent 帖子内容
* @return
*/
public Post posting(String title, String sourceContent) throws BusinessException {
long currentTimeMillis = System.currentTimeMillis();
if (sourceContent.length() < MIN_LENGTH_POST_SOURCE_CONTENT) {
throw new BusinessException(ExceptionCodeConstant.POST_SOURCE_CONTENT_AT_LEAST_SIXTEEN_WORDS);
}
Post post = new Post();
post.setAuthorId(this.id);
post.setSourceContent(sourceContent);
post.setTitle(title);
post.setPostingTime(currentTimeMillis);
post.setLastModifyTime(currentTimeMillis);
return post;
}
}

View File

@ -0,0 +1,36 @@
package cn.allms.community.domain.model.topic;
import lombok.Data;
import java.sql.Timestamp;
import java.util.Set;
/**
* 话题对象
*
* @author xieYj
* @date 2021/5/11 14:31
*/
@Data
public class Topic {
/**
* 话题id
*/
private Long id;
/**
* 话题标题
*/
private String title;
/**
* 话题描述
*/
private String description;
/**
* 话题创建时间
*/
private Timestamp createTime;
/**
* 话题下的帖子列表
*/
private Set<TopicPost> posts;
}

View File

@ -0,0 +1,17 @@
package cn.allms.community.domain.model.topic;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* 话题文章对象
*
* @author xieYj
* @date 2021/5/11 14:31
*/
@Data
@AllArgsConstructor
public class TopicPost {
private Long topicId;
private Long postId;
}

View File

@ -0,0 +1,25 @@
package cn.allms.community.domain.service.contentfilter;
import lombok.Getter;
import lombok.Setter;
/**
* 内容过滤器
* @author xieYj
* @date 2021/5/11 15:09
*/
@Setter
@Getter
public abstract class ContentFilter {
/**
* 下一个内容过滤器
*/
private ContentFilter nextContentFilter;
/**
* 过滤内容
* @param object 过滤对象
* @return
*/
public abstract boolean filterContent(Object object);
}

View File

@ -0,0 +1,14 @@
package cn.allms.community.domain.service.contentfilter;
/**
* 图片过滤器
* @author xieYj
* @date 2021/5/11 15:17
*/
public class ImageContentFilter extends ContentFilter{
@Override
public boolean filterContent(Object object) {
// TODO 调用第三方接口实现图片过滤
return false;
}
}

View File

@ -0,0 +1,35 @@
package cn.allms.community.domain.service.contentfilter;
import org.springframework.util.Assert;
import java.util.HashSet;
import java.util.Set;
/**
* 本地文本内容过滤器
* @author xieYj
* @date 2021/5/11 15:24
*/
public class LocalTextContentFilter extends TextContentFilter {
/**
* 本地敏感词
*/
private Set<String> sensitiveWords = new HashSet<>();
public LocalTextContentFilter() {
sensitiveWords.add("NND");
sensitiveWords.add("奶奶个熊");
}
@Override
public boolean filterContent(Object content) {
Assert.isTrue(content instanceof String, "本地文本过滤对象必须为 String 类型");
for (String sensitiveWord : sensitiveWords) {
if (((String) content).contains(sensitiveWord)) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,46 @@
package cn.allms.community.domain.service.contentfilter;
import cn.allms.community.domain.model.post.Post;
import java.util.Set;
/**
* 帖子内容过滤包含文本过滤图片过滤
* 责任链模式
*
* @author xieYj
* @date 2021/5/11 15:33
*/
public class PostMainBodyContentFilterChain {
/**
* 内容过滤器
*/
private Set<ContentFilter> contentFilters;
public PostMainBodyContentFilterChain() {
TextContentFilter localTextContentFilter = new LocalTextContentFilter();
TextContentFilter remoteTextContentFilter = new RemoteTextContentFilter();
ImageContentFilter imageContentFilter = new ImageContentFilter();
// 优先校验本地的敏感词
contentFilters.add(localTextContentFilter);
// 第三方文本敏感词过滤
contentFilters.add(remoteTextContentFilter);
// 第三方图片过滤
contentFilters.add(imageContentFilter);
}
/**
* 过滤标题
*
* @param post 帖子信息
* @return true 通过, false 未通过
*/
public boolean filterMainBody(Post post) {
for (ContentFilter contentFilter : contentFilters) {
if (!contentFilter.filterContent(post.getSourceContent())) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,44 @@
package cn.allms.community.domain.service.contentfilter;
import cn.allms.community.domain.model.post.Post;
import java.util.Iterator;
import java.util.Set;
/**
* 帖子标题过滤文本过滤
* 责任链模式
*
* @author xieYj
* @date 2021/5/11 15:40
*/
public class PostTitleContentFilterChain {
/**
* 内容过滤器
*/
private Set<ContentFilter> contentFilters;
public PostTitleContentFilterChain() {
LocalTextContentFilter localTextContentFilter = new LocalTextContentFilter();
RemoteTextContentFilter remoteTextContentFilter = new RemoteTextContentFilter();
// 本地文本敏感词校验
contentFilters.add(localTextContentFilter);
// 第三方文本敏感词校验
contentFilters.add(remoteTextContentFilter);
}
/**
* 过滤标题
*
* @param post 帖子信息
* @return true 通过false 未通过
*/
public boolean filterTitle(Post post) {
for (ContentFilter contentFilter : contentFilters) {
if (!contentFilter.filterContent(post.getTitle())) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,14 @@
package cn.allms.community.domain.service.contentfilter;
/**
* 远程文本内容过滤器
* @author xieYj
* @date 2021/5/11 15:22
*/
public class RemoteTextContentFilter extends TextContentFilter{
@Override
public boolean filterContent(Object object) {
// TODO 需要调用第三方服务才能实现
return false;
}
}

View File

@ -0,0 +1,16 @@
package cn.allms.community.domain.service.contentfilter;
/**
* 文本过滤器
* @author xieYj
* @date 2021/5/11 15:18
*/
public abstract class TextContentFilter extends ContentFilter{
/**
* 文本过滤器抽象
* @param object 过滤对象
* @return
*/
@Override
public abstract boolean filterContent(Object object);
}

View File

@ -0,0 +1,10 @@
package cn.allms.community.infrastructure.constant;
/**
* 公共常量
* @author xieYj
* @date 2021/5/11 14:56
*/
public interface CommonConstants {
String TOPIC_SPLIT_COMMA = ",";
}

View File

@ -0,0 +1,12 @@
package cn.allms.community.infrastructure.constant;
/**
* 异常码状态常量
*
* @author xieYj
* @date 2021/5/11 14:17
*/
public interface ExceptionCodeConstant {
String POST_SOURCE_CONTENT_AT_LEAST_SIXTEEN_WORDS = "2001";
String ONE_POST_MOST_JOIN_INTO_FIVE_TOPICS = "2002";
}

View File

@ -0,0 +1,84 @@
package cn.allms.community.infrastructure.exception;
/**
* @author xieYj
* @date 2021/5/11 13:59
*/
public class BusinessException extends Exception {
/**
* 业务异常码
*/
private String code;
/**
* 业务异常参数
*/
private Object[] arguments;
public BusinessException() {
super();
}
public BusinessException(Throwable cause) {
super(cause);
}
public BusinessException(String message) {
super(message);
}
public BusinessException(String code, String message) {
super(message);
this.code = code;
}
public BusinessException(String message, Object[] arguments) {
super(message);
this.arguments = arguments;
}
public BusinessException(String code, String message, Object[] arguments) {
super(message);
this.code = code;
this.arguments = arguments;
}
public BusinessException(String message, Object[] arguments, Throwable cause) {
super(message, cause);
this.arguments = arguments;
}
public BusinessException(String code, String message, Object[] arguments, Throwable cause) {
super(message, cause);
this.code = code;
this.arguments = arguments;
}
public BusinessException(String message, Throwable cause) {
super(message, cause);
}
public BusinessException(String code, String message, Throwable cause) {
super(message, cause);
this.code = code;
}
public BusinessException(String message,
Throwable cause,
boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public BusinessException(String code,
String message,
Throwable cause,
boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
this.code = code;
}
}