feat:(add comment blacklist) (#465)

* feat:(add comment blacklist)

* update enum name

* update enum

* fix:(update pom)

* 优化相关的细节
pull/499/head
Lei XinXin 2020-01-15 22:43:55 +08:00 committed by John Niang
parent d867f4cca3
commit 11c32ffdca
11 changed files with 266 additions and 1 deletions

View File

@ -130,6 +130,7 @@ public class PostController {
@ApiOperation("Comments a post") @ApiOperation("Comments a post")
@CacheLock(autoDelete = false, traceRequest = true) @CacheLock(autoDelete = false, traceRequest = true)
public BaseCommentDTO comment(@RequestBody PostCommentParam postCommentParam) { public BaseCommentDTO comment(@RequestBody PostCommentParam postCommentParam) {
postCommentService.validateCommentBlackListStatus();
return postCommentService.convertTo(postCommentService.createBy(postCommentParam)); return postCommentService.convertTo(postCommentService.createBy(postCommentParam));
} }

View File

@ -0,0 +1,35 @@
package run.halo.app.model.entity;
import lombok.*;
import javax.persistence.*;
import java.util.Date;
/**
* comment_black_list
*
* @author Lei XinXin
* @date 2020/1/3
*/
@Data
@Entity
@Table(name = "comment_black_list")
@EqualsAndHashCode(callSuper = true)
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class CommentBlackList extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "ip_address", columnDefinition = "VARCHAR(127) NOT NULL")
private String ipAddress;
/**
*
*/
@Column(name = "ban_time")
@Temporal(TemporalType.TIMESTAMP)
private Date banTime;
}

View File

@ -0,0 +1,21 @@
package run.halo.app.model.enums;
import lombok.Getter;
/**
*
* @author Lei XinXin
* @date 2020/1/5
*/
@Getter
public enum BanStatusEnum {
/**
*
*/
NORMAL(0),;
private int status;
BanStatusEnum(int status) {
this.status = status;
}
}

View File

@ -0,0 +1,27 @@
package run.halo.app.model.enums;
import lombok.Getter;
/**
*
*
* @author Lei XinXin
* @date 2020/1/4
*/
@Getter
public enum CommentViolationTypeEnum {
/**
*
*/
NORMAL(0),
/**
*
*/
FREQUENTLY(1),
;
private int type;
CommentViolationTypeEnum(int type) {
this.type = type;
}
}

View File

@ -25,7 +25,13 @@ public enum CommentProperties implements PropertyEnum {
CONTENT_PLACEHOLDER("comment_content_placeholder", String.class, ""), CONTENT_PLACEHOLDER("comment_content_placeholder", String.class, ""),
INTERNAL_PLUGIN_JS("comment_internal_plugin_js", String.class, "//cdn.jsdelivr.net/gh/halo-dev/halo-comment@latest/dist/halo-comment.min.js"); INTERNAL_PLUGIN_JS("comment_internal_plugin_js", String.class, "//cdn.jsdelivr.net/gh/halo-dev/halo-comment@latest/dist/halo-comment.min.js"),
COMMENT_BAN_TIME("comment_ban_time", Integer.class, "10"),
COMMENT_RANGE("comment_range", Integer.class, "30");
private final String value; private final String value;

View File

@ -0,0 +1,36 @@
package run.halo.app.repository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import run.halo.app.model.entity.CommentBlackList;
import run.halo.app.repository.base.BaseRepository;
import java.util.Optional;
/**
* Repository
*
* @author Lei XinXin
* @date 2020/1/3
*/
public interface CommentBlackListRepository extends BaseRepository<CommentBlackList, Long> {
/**
* IP
*
* @param ipAddress
* @return
*/
Optional<CommentBlackList> findByIpAddress(String ipAddress);
/**
* Update Comment BlackList By IPAddress
*
* @param commentBlackList
* @return result
*/
@Modifying
@Query("UPDATE CommentBlackList SET banTime=:#{#commentBlackList.banTime} WHERE ipAddress=:#{#commentBlackList.ipAddress}")
int updateByIpAddress(@Param("commentBlackList") CommentBlackList commentBlackList);
}

View File

@ -7,7 +7,9 @@ import run.halo.app.model.projection.CommentChildrenCountProjection;
import run.halo.app.model.projection.CommentCountProjection; import run.halo.app.model.projection.CommentCountProjection;
import run.halo.app.repository.base.BaseCommentRepository; import run.halo.app.repository.base.BaseCommentRepository;
import java.time.LocalDateTime;
import java.util.Collection; import java.util.Collection;
import java.util.Date;
import java.util.List; import java.util.List;
/** /**
@ -45,4 +47,15 @@ public interface PostCommentRepository extends BaseCommentRepository<PostComment
@NonNull @NonNull
@Override @Override
List<CommentChildrenCountProjection> findDirectChildrenCount(@NonNull Collection<Long> commentIds); List<CommentChildrenCountProjection> findDirectChildrenCount(@NonNull Collection<Long> commentIds);
/**
* IP
*
* @param ipAddress IP
* @param startTime
* @param endTime
* @return
*/
@Query("SELECT COUNT(id) FROM PostComment WHERE ipAddress=?1 AND updateTime BETWEEN ?2 AND ?3 AND status <> 2")
int countByIpAndTime(String ipAddress, Date startTime, Date endTime);
} }

View File

@ -0,0 +1,18 @@
package run.halo.app.service;
import run.halo.app.model.enums.CommentViolationTypeEnum;
/**
* Comment BlackList Service
* @author Lei XinXin
* @date 2020/1/3
*/
public interface CommentBlackListService {
/**
*
*
* @param ipAddress ip
* @return boolean
*/
CommentViolationTypeEnum commentsBanStatus(String ipAddress);
}

View File

@ -49,4 +49,9 @@ public interface PostCommentService extends BaseCommentService<PostComment> {
@NonNull @NonNull
Page<PostCommentWithPostVO> pageTreeBy(@NonNull CommentQuery commentQuery, @NonNull Pageable pageable); Page<PostCommentWithPostVO> pageTreeBy(@NonNull CommentQuery commentQuery, @NonNull Pageable pageable);
/**
* Validate CommentBlackList Status
*/
void validateCommentBlackListStatus();
} }

View File

@ -0,0 +1,84 @@
package run.halo.app.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import run.halo.app.model.entity.CommentBlackList;
import run.halo.app.model.enums.CommentViolationTypeEnum;
import run.halo.app.model.properties.CommentProperties;
import run.halo.app.repository.CommentBlackListRepository;
import run.halo.app.repository.PostCommentRepository;
import run.halo.app.service.CommentBlackListService;
import run.halo.app.service.OptionService;
import run.halo.app.service.base.AbstractCrudService;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.Optional;
/**
* Comment BlackList Service Implements
*
* @author Lei XinXin
* @date 2020/1/3
*/
@Service
@Slf4j
public class CommentBlackListServiceImpl extends AbstractCrudService<CommentBlackList, Long> implements CommentBlackListService {
private static ZoneId zoneId = ZoneId.of("Asia/Shanghai");
private final CommentBlackListRepository commentBlackListRepository;
private final PostCommentRepository postCommentRepository;
private final OptionService optionService;
public CommentBlackListServiceImpl(CommentBlackListRepository commentBlackListRepository, PostCommentRepository postCommentRepository, OptionService optionService) {
super(commentBlackListRepository);
this.commentBlackListRepository = commentBlackListRepository;
this.postCommentRepository = postCommentRepository;
this.optionService = optionService;
}
@Override
public CommentViolationTypeEnum commentsBanStatus(String ipAddress) {
/*
N=
1.
2. NN
3. N
4. N
*/
Optional<CommentBlackList> blackList = commentBlackListRepository.findByIpAddress(ipAddress);
LocalDateTime now = LocalDateTime.now();
Date endTime = new Date(now.atZone(zoneId).toInstant().toEpochMilli());
Integer banTime = optionService.getByPropertyOrDefault(CommentProperties.COMMENT_BAN_TIME, Integer.class, 10);
Date startTime = new Date(now.minusMinutes(banTime)
.atZone(zoneId).toInstant().toEpochMilli());
Integer range = optionService.getByPropertyOrDefault(CommentProperties.COMMENT_RANGE, Integer.class, 30);
boolean isPresent = postCommentRepository.countByIpAndTime(ipAddress, startTime, endTime) >= range;
if (isPresent && blackList.isPresent()) {
update(now, blackList.get(), banTime);
return CommentViolationTypeEnum.FREQUENTLY;
} else if (isPresent) {
CommentBlackList commentBlackList = CommentBlackList
.builder()
.banTime(getBanTime(now, banTime))
.ipAddress(ipAddress)
.build();
super.create(commentBlackList);
return CommentViolationTypeEnum.FREQUENTLY;
}
return CommentViolationTypeEnum.NORMAL;
}
private void update(LocalDateTime localDateTime, CommentBlackList blackList, Integer banTime) {
blackList.setBanTime(getBanTime(localDateTime, banTime));
int updateResult = commentBlackListRepository.updateByIpAddress(blackList);
Optional.of(updateResult)
.filter(result -> result <= 0).ifPresent(result -> log.error("更新评论封禁时间失败"));
}
private Date getBanTime(LocalDateTime localDateTime, Integer banTime) {
return new Date(localDateTime.plusMinutes(banTime).atZone(zoneId).toInstant().toEpochMilli());
}
}

View File

@ -9,18 +9,23 @@ import org.springframework.stereotype.Service;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import run.halo.app.exception.BadRequestException; import run.halo.app.exception.BadRequestException;
import run.halo.app.exception.ForbiddenException;
import run.halo.app.exception.NotFoundException; import run.halo.app.exception.NotFoundException;
import run.halo.app.model.dto.post.BasePostMinimalDTO; import run.halo.app.model.dto.post.BasePostMinimalDTO;
import run.halo.app.model.entity.Post; import run.halo.app.model.entity.Post;
import run.halo.app.model.entity.PostComment; import run.halo.app.model.entity.PostComment;
import run.halo.app.model.enums.CommentViolationTypeEnum;
import run.halo.app.model.params.CommentQuery; import run.halo.app.model.params.CommentQuery;
import run.halo.app.model.properties.CommentProperties;
import run.halo.app.model.vo.PostCommentWithPostVO; import run.halo.app.model.vo.PostCommentWithPostVO;
import run.halo.app.repository.PostCommentRepository; import run.halo.app.repository.PostCommentRepository;
import run.halo.app.repository.PostRepository; import run.halo.app.repository.PostRepository;
import run.halo.app.service.CommentBlackListService;
import run.halo.app.service.OptionService; import run.halo.app.service.OptionService;
import run.halo.app.service.PostCommentService; import run.halo.app.service.PostCommentService;
import run.halo.app.service.UserService; import run.halo.app.service.UserService;
import run.halo.app.utils.ServiceUtils; import run.halo.app.utils.ServiceUtils;
import run.halo.app.utils.ServletUtils;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -43,14 +48,18 @@ public class PostCommentServiceImpl extends BaseCommentServiceImpl<PostComment>
private final PostRepository postRepository; private final PostRepository postRepository;
private final CommentBlackListService commentBlackListService;
public PostCommentServiceImpl(PostCommentRepository postCommentRepository, public PostCommentServiceImpl(PostCommentRepository postCommentRepository,
PostRepository postRepository, PostRepository postRepository,
UserService userService, UserService userService,
OptionService optionService, OptionService optionService,
CommentBlackListService commentBlackListService,
ApplicationEventPublisher eventPublisher) { ApplicationEventPublisher eventPublisher) {
super(postCommentRepository, optionService, userService, eventPublisher); super(postCommentRepository, optionService, userService, eventPublisher);
this.postCommentRepository = postCommentRepository; this.postCommentRepository = postCommentRepository;
this.postRepository = postRepository; this.postRepository = postRepository;
this.commentBlackListService = commentBlackListService;
} }
@Override @Override
@ -110,4 +119,14 @@ public class PostCommentServiceImpl extends BaseCommentServiceImpl<PostComment>
throw new BadRequestException("该文章已经被禁止评论").setErrorData(postId); throw new BadRequestException("该文章已经被禁止评论").setErrorData(postId);
} }
} }
@Override
public void validateCommentBlackListStatus() {
CommentViolationTypeEnum banStatus = commentBlackListService.commentsBanStatus(ServletUtils.getRequestIp());
Integer banTime = optionService.getByPropertyOrDefault(CommentProperties.COMMENT_BAN_TIME, Integer.class, 10);
if (banStatus == CommentViolationTypeEnum.FREQUENTLY) {
throw new ForbiddenException(String.format("您的评论过于频繁,请%s分钟之后再试。", banTime));
}
}
} }