mirror of https://github.com/halo-dev/halo
Complete event queue publishing
parent
d41c32da43
commit
27a8460ce1
|
@ -113,9 +113,9 @@ public class WebMvcAutoConfiguration implements WebMvcConfigurer {
|
||||||
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
|
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
|
||||||
configurer.setTemplateLoaderPaths(FILE_PROTOCOL + haloProperties.getWorkDir() + "templates/", "classpath:/templates/");
|
configurer.setTemplateLoaderPaths(FILE_PROTOCOL + haloProperties.getWorkDir() + "templates/", "classpath:/templates/");
|
||||||
configurer.setDefaultEncoding("UTF-8");
|
configurer.setDefaultEncoding("UTF-8");
|
||||||
if (haloProperties.isProductionEnv()) {
|
// if (haloProperties.isProductionEnv()) {
|
||||||
configurer.getConfiguration().setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
|
// configurer.getConfiguration().setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
|
||||||
}
|
// }
|
||||||
return configurer;
|
return configurer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
package run.halo.app.event;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.context.ApplicationEvent;
|
||||||
|
import org.springframework.context.ApplicationListener;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.annotation.PreDestroy;
|
||||||
|
import java.util.EventListener;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event queue dispatcher.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
* @date 19-4-20
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class ApplicationEventQueuePublisher {
|
||||||
|
|
||||||
|
private final BlockingQueue<Object> events = new LinkedBlockingQueue<>();
|
||||||
|
|
||||||
|
private final ApplicationListenerManager listenerManager;
|
||||||
|
|
||||||
|
private final ExecutorService executorService;
|
||||||
|
|
||||||
|
public ApplicationEventQueuePublisher(ApplicationListenerManager listenerManager) {
|
||||||
|
this.listenerManager = listenerManager;
|
||||||
|
this.executorService = Executors.newSingleThreadExecutor();
|
||||||
|
|
||||||
|
executorService.execute(new EventQueueConsumer());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void publishEvent(Object event) {
|
||||||
|
try {
|
||||||
|
events.put(event);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
log.warn("Failed to put event to the queue", e);
|
||||||
|
// Ignore this error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreDestroy
|
||||||
|
protected void destroy() {
|
||||||
|
log.info("Shutting down all event queue consumer");
|
||||||
|
this.executorService.shutdownNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private class EventQueueConsumer implements Runnable {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
|
||||||
|
while (!Thread.currentThread().isInterrupted()) {
|
||||||
|
try {
|
||||||
|
// Take an event
|
||||||
|
Object event = events.take();
|
||||||
|
|
||||||
|
// Get listeners
|
||||||
|
List<EventListener> listeners = listenerManager.getListeners(event);
|
||||||
|
|
||||||
|
// Handle the event
|
||||||
|
listeners.forEach(listener -> {
|
||||||
|
if (listener instanceof ApplicationListener && event instanceof ApplicationEvent) {
|
||||||
|
ApplicationEvent applicationEvent = (ApplicationEvent) event;
|
||||||
|
// Fire event
|
||||||
|
((ApplicationListener) listener).onApplicationEvent(applicationEvent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
log.info("Event queue consumer has been shut down");
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
log.warn("Failed to take event", e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to handle event", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
package run.halo.app.event;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Assert;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.ApplicationListener;
|
||||||
|
import org.springframework.lang.NonNull;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import run.halo.app.utils.ReflectionUtils;
|
||||||
|
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Application listener manager.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
* @date 19-4-21
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class ApplicationListenerManager {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener Map.
|
||||||
|
*/
|
||||||
|
private final Map<String, List<EventListener>> listenerMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public ApplicationListenerManager(ApplicationContext applicationContext) {
|
||||||
|
// TODO Need to refactor
|
||||||
|
// Register all listener on starting up
|
||||||
|
applicationContext.getBeansOfType(ApplicationListener.class).values().forEach(this::register);
|
||||||
|
|
||||||
|
log.debug("Initialized event listeners");
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<EventListener> getListeners(@Nullable Object event) {
|
||||||
|
if (event == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get listeners
|
||||||
|
List<EventListener> listeners = listenerMap.get(event.getClass().getTypeName());
|
||||||
|
// Clone the listeners
|
||||||
|
return listeners == null ? Collections.emptyList() : new LinkedList<>(listeners);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void register(@NonNull ApplicationListener<?> listener) {
|
||||||
|
// Get actual generic type
|
||||||
|
Type actualType = resolveActualGenericType(listener);
|
||||||
|
|
||||||
|
if (actualType == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add this listener
|
||||||
|
listenerMap.computeIfAbsent(actualType.getTypeName(), (key) -> new LinkedList<>()).add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void unRegister(@NonNull ApplicationListener<?> listener) {
|
||||||
|
// Get actual generic type
|
||||||
|
Type actualType = resolveActualGenericType(listener);
|
||||||
|
|
||||||
|
if (actualType == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove it from listener map
|
||||||
|
listenerMap.getOrDefault(actualType.getTypeName(), Collections.emptyList()).remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Type resolveActualGenericType(@NonNull ApplicationListener<?> listener) {
|
||||||
|
Assert.notNull(listener, "Application listener must not be null");
|
||||||
|
|
||||||
|
log.debug("Attempting to resolve type of Application listener: [{}]", listener);
|
||||||
|
|
||||||
|
ParameterizedType parameterizedType = ReflectionUtils.getParameterizedType(ApplicationListener.class, ((ApplicationListener) listener).getClass());
|
||||||
|
|
||||||
|
return parameterizedType == null ? null : parameterizedType.getActualTypeArguments()[0];
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,9 @@
|
||||||
package run.halo.app.event;
|
package run.halo.app.event;
|
||||||
|
|
||||||
import org.springframework.context.ApplicationEvent;
|
import org.springframework.context.ApplicationEvent;
|
||||||
|
import run.halo.app.model.enums.LogType;
|
||||||
|
import run.halo.app.model.params.LogParam;
|
||||||
|
import run.halo.app.utils.ValidationUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author johnniang
|
* @author johnniang
|
||||||
|
@ -8,12 +11,28 @@ import org.springframework.context.ApplicationEvent;
|
||||||
*/
|
*/
|
||||||
public class LogEvent extends ApplicationEvent {
|
public class LogEvent extends ApplicationEvent {
|
||||||
|
|
||||||
|
private final LogParam logParam;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new ApplicationEvent.
|
* Create a new ApplicationEvent.
|
||||||
*
|
*
|
||||||
* @param source the object on which the event initially occurred (never {@code null})
|
* @param source the object on which the event initially occurred (never {@code null})
|
||||||
|
* @param logParam
|
||||||
*/
|
*/
|
||||||
public LogEvent(Object source) {
|
public LogEvent(Object source, LogParam logParam) {
|
||||||
super(source);
|
super(source);
|
||||||
|
|
||||||
|
// Validate the log param
|
||||||
|
ValidationUtils.validate(logParam);
|
||||||
|
|
||||||
|
this.logParam = logParam;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LogEvent(Object source, String logKey, LogType logType, String content) {
|
||||||
|
this(source, new LogParam(logKey, logType, content));
|
||||||
|
}
|
||||||
|
|
||||||
|
public LogParam getLogParam() {
|
||||||
|
return logParam;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
package run.halo.app.event;
|
||||||
|
|
||||||
|
import org.springframework.context.ApplicationListener;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import run.halo.app.model.entity.Log;
|
||||||
|
import run.halo.app.service.LogService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log event listener.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
* @date 19-4-21
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class LogEventListener implements ApplicationListener<LogEvent> {
|
||||||
|
|
||||||
|
private final LogService logService;
|
||||||
|
|
||||||
|
public LogEventListener(LogService logService) {
|
||||||
|
this.logService = logService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onApplicationEvent(LogEvent event) {
|
||||||
|
// Convert to log
|
||||||
|
Log logToCreate = event.getLogParam().convertTo();
|
||||||
|
// Create log
|
||||||
|
logService.create(logToCreate);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,13 @@
|
||||||
package run.halo.app.model.entity;
|
package run.halo.app.model.entity;
|
||||||
|
|
||||||
|
|
||||||
import run.halo.app.model.enums.LogType;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
import org.hibernate.annotations.SQLDelete;
|
import org.hibernate.annotations.SQLDelete;
|
||||||
import org.hibernate.annotations.Where;
|
import org.hibernate.annotations.Where;
|
||||||
|
import run.halo.app.model.enums.LogType;
|
||||||
|
import run.halo.app.utils.ServletUtils;
|
||||||
|
|
||||||
import javax.persistence.*;
|
import javax.persistence.*;
|
||||||
|
|
||||||
|
@ -57,5 +58,16 @@ public class Log extends BaseEntity {
|
||||||
public void prePersist() {
|
public void prePersist() {
|
||||||
super.prePersist();
|
super.prePersist();
|
||||||
id = null;
|
id = null;
|
||||||
|
|
||||||
|
if (logKey == null) {
|
||||||
|
logKey = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get ip address
|
||||||
|
ipAddress = ServletUtils.getRequestIp();
|
||||||
|
|
||||||
|
if (ipAddress == null) {
|
||||||
|
logKey = "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,13 @@ package run.halo.app.model.enums;
|
||||||
* @author johnniang
|
* @author johnniang
|
||||||
*/
|
*/
|
||||||
public enum LogType implements ValueEnum<Integer> {
|
public enum LogType implements ValueEnum<Integer> {
|
||||||
|
|
||||||
|
POST_PUBLISHED(0),
|
||||||
|
POST_EDITED(1),
|
||||||
|
POST_DELETED(5),
|
||||||
|
LOGGED_IN(2),
|
||||||
|
LOGGED_OUT(3),
|
||||||
|
LOGIN_FAILED(4),
|
||||||
;
|
;
|
||||||
|
|
||||||
private final Integer value;
|
private final Integer value;
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
package run.halo.app.model.params;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import run.halo.app.model.dto.base.InputConverter;
|
||||||
|
import run.halo.app.model.entity.Log;
|
||||||
|
import run.halo.app.model.enums.LogType;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.constraints.Size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author johnniang
|
||||||
|
* @date 19-4-21
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class LogParam implements InputConverter<Log> {
|
||||||
|
|
||||||
|
@Size(max = 1023, message = "Length of log key must not be more than {max}")
|
||||||
|
private String logKey;
|
||||||
|
|
||||||
|
@NotNull(message = "Log type must not be null")
|
||||||
|
private LogType type;
|
||||||
|
|
||||||
|
@NotBlank(message = "Log content must not be blank")
|
||||||
|
@Size(max = 1023, message = "Log content must not be more than 1023")
|
||||||
|
private String content;
|
||||||
|
}
|
|
@ -13,6 +13,8 @@ import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
import run.halo.app.event.ApplicationEventQueuePublisher;
|
||||||
|
import run.halo.app.event.LogEvent;
|
||||||
import run.halo.app.exception.AlreadyExistsException;
|
import run.halo.app.exception.AlreadyExistsException;
|
||||||
import run.halo.app.exception.NotFoundException;
|
import run.halo.app.exception.NotFoundException;
|
||||||
import run.halo.app.model.dto.CategoryOutputDTO;
|
import run.halo.app.model.dto.CategoryOutputDTO;
|
||||||
|
@ -20,6 +22,7 @@ import run.halo.app.model.dto.TagOutputDTO;
|
||||||
import run.halo.app.model.dto.post.PostMinimalOutputDTO;
|
import run.halo.app.model.dto.post.PostMinimalOutputDTO;
|
||||||
import run.halo.app.model.dto.post.PostSimpleOutputDTO;
|
import run.halo.app.model.dto.post.PostSimpleOutputDTO;
|
||||||
import run.halo.app.model.entity.*;
|
import run.halo.app.model.entity.*;
|
||||||
|
import run.halo.app.model.enums.LogType;
|
||||||
import run.halo.app.model.enums.PostStatus;
|
import run.halo.app.model.enums.PostStatus;
|
||||||
import run.halo.app.model.params.PostQuery;
|
import run.halo.app.model.params.PostQuery;
|
||||||
import run.halo.app.model.vo.ArchiveMonthVO;
|
import run.halo.app.model.vo.ArchiveMonthVO;
|
||||||
|
@ -66,12 +69,15 @@ public class PostServiceImpl extends AbstractCrudService<Post, Integer> implemen
|
||||||
|
|
||||||
private final CommentService commentService;
|
private final CommentService commentService;
|
||||||
|
|
||||||
|
private final ApplicationEventQueuePublisher eventQueuePublisher;
|
||||||
|
|
||||||
public PostServiceImpl(PostRepository postRepository,
|
public PostServiceImpl(PostRepository postRepository,
|
||||||
TagService tagService,
|
TagService tagService,
|
||||||
CategoryService categoryService,
|
CategoryService categoryService,
|
||||||
PostTagService postTagService,
|
PostTagService postTagService,
|
||||||
PostCategoryService postCategoryService,
|
PostCategoryService postCategoryService,
|
||||||
CommentService commentService) {
|
CommentService commentService,
|
||||||
|
ApplicationEventQueuePublisher eventQueuePublisher) {
|
||||||
super(postRepository);
|
super(postRepository);
|
||||||
this.postRepository = postRepository;
|
this.postRepository = postRepository;
|
||||||
this.tagService = tagService;
|
this.tagService = tagService;
|
||||||
|
@ -79,6 +85,7 @@ public class PostServiceImpl extends AbstractCrudService<Post, Integer> implemen
|
||||||
this.postTagService = postTagService;
|
this.postTagService = postTagService;
|
||||||
this.postCategoryService = postCategoryService;
|
this.postCategoryService = postCategoryService;
|
||||||
this.commentService = commentService;
|
this.commentService = commentService;
|
||||||
|
this.eventQueuePublisher = eventQueuePublisher;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -214,13 +221,36 @@ public class PostServiceImpl extends AbstractCrudService<Post, Integer> implemen
|
||||||
return createOrUpdate(postToUpdate, tagIds, categoryIds, this::update);
|
return createOrUpdate(postToUpdate, tagIds, categoryIds, this::update);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Post create(Post post) {
|
||||||
|
Post createdPost = super.create(post);
|
||||||
|
|
||||||
|
// Log the creation
|
||||||
|
LogEvent logEvent = new LogEvent(this, createdPost.getId().toString(), LogType.POST_PUBLISHED, createdPost.getTitle());
|
||||||
|
eventQueuePublisher.publishEvent(logEvent);
|
||||||
|
|
||||||
|
return createdPost;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Post update(Post post) {
|
||||||
|
Post updatedPost = super.update(post);
|
||||||
|
|
||||||
|
// Log the creation
|
||||||
|
LogEvent logEvent = new LogEvent(this, updatedPost.getId().toString(), LogType.POST_EDITED, updatedPost.getTitle());
|
||||||
|
eventQueuePublisher.publishEvent(logEvent);
|
||||||
|
|
||||||
|
return updatedPost;
|
||||||
|
}
|
||||||
|
|
||||||
private PostDetailVO createOrUpdate(@NonNull Post post, Set<Integer> tagIds, Set<Integer> categoryIds, @NonNull Function<Post, Post> postOperation) {
|
private PostDetailVO createOrUpdate(@NonNull Post post, Set<Integer> tagIds, Set<Integer> categoryIds, @NonNull Function<Post, Post> postOperation) {
|
||||||
Assert.notNull(post, "Post param must not be null");
|
Assert.notNull(post, "Post param must not be null");
|
||||||
Assert.notNull(postOperation, "Post operation must not be null");
|
Assert.notNull(postOperation, "Post operation must not be null");
|
||||||
|
|
||||||
// Check url
|
// Check url
|
||||||
long count;
|
long count;
|
||||||
if (post.getId() != null) {
|
boolean isUpdating = post.getId() != null;
|
||||||
|
if (isUpdating) {
|
||||||
// For updating
|
// For updating
|
||||||
count = postRepository.countByIdNotAndUrl(post.getId(), post.getUrl());
|
count = postRepository.countByIdNotAndUrl(post.getId(), post.getUrl());
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,72 +0,0 @@
|
||||||
package run.halo.app.utils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <pre>
|
|
||||||
* 拼装评论
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* @author : RYAN0UP
|
|
||||||
* @date : 2018/7/12
|
|
||||||
*/
|
|
||||||
public class CommentUtil {
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * 获取组装好的评论
|
|
||||||
// *
|
|
||||||
// * @param commentsRoot commentsRoot
|
|
||||||
// * @return List
|
|
||||||
// */
|
|
||||||
// public static List<Comment> getComments(List<Comment> commentsRoot) {
|
|
||||||
// if (CollectionUtils.isEmpty(commentsRoot)) {
|
|
||||||
// return Collections.emptyList();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// final List<Comment> commentsResult = new ArrayList<>();
|
|
||||||
//
|
|
||||||
// for (Comment comment : commentsRoot) {
|
|
||||||
// if (comment.getCommentParent() == 0) {
|
|
||||||
// commentsResult.add(comment);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// for (Comment comment : commentsResult) {
|
|
||||||
// comment.setChildComments(getChild(comment.getCommentId(), commentsRoot));
|
|
||||||
// }
|
|
||||||
// // 集合倒序,最新的评论在最前面
|
|
||||||
// Collections.reverse(commentsResult);
|
|
||||||
// return commentsResult;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 获取评论的子评论
|
|
||||||
// *
|
|
||||||
// * @param id 评论编号
|
|
||||||
// * @param commentsRoot commentsRoot
|
|
||||||
// * @return List
|
|
||||||
// */
|
|
||||||
// private static List<Comment> getChild(Long id, List<Comment> commentsRoot) {
|
|
||||||
// Assert.notNull(id, "comment id must not be null");
|
|
||||||
//
|
|
||||||
// if (CollectionUtils.isEmpty(commentsRoot)) {
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// final List<Comment> commentsChild = new ArrayList<>();
|
|
||||||
// for (Comment comment : commentsRoot) {
|
|
||||||
// if (comment.getCommentParent() != 0) {
|
|
||||||
// if (comment.getCommentParent().equals(id)) {
|
|
||||||
// commentsChild.add(comment);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// for (Comment comment : commentsChild) {
|
|
||||||
// if (comment.getCommentParent() != 0) {
|
|
||||||
// comment.setChildComments(getChild(comment.getCommentId(), commentsRoot));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if (commentsChild.size() == 0) {
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
// return commentsChild;
|
|
||||||
// }
|
|
||||||
}
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
package run.halo.app.utils;
|
||||||
|
|
||||||
|
import cn.hutool.extra.servlet.ServletUtil;
|
||||||
|
import org.springframework.lang.NonNull;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Servlet utilities.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
* @date 19-4-21
|
||||||
|
*/
|
||||||
|
public class ServletUtils {
|
||||||
|
|
||||||
|
private ServletUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets current http servlet request.
|
||||||
|
*
|
||||||
|
* @return an optional http servlet request
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public static Optional<HttpServletRequest> getCurrentRequest() {
|
||||||
|
return Optional.ofNullable(RequestContextHolder.getRequestAttributes())
|
||||||
|
.filter(requestAttributes -> requestAttributes instanceof ServletRequestAttributes)
|
||||||
|
.map(requestAttributes -> ((ServletRequestAttributes) requestAttributes))
|
||||||
|
.map(ServletRequestAttributes::getRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets request ip.
|
||||||
|
*
|
||||||
|
* @return ip address or null
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static String getRequestIp() {
|
||||||
|
return getCurrentRequest().map(ServletUtil::getClientIP).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -99,7 +99,6 @@ public class ContentArchiveController {
|
||||||
*
|
*
|
||||||
* @param url post slug url.
|
* @param url post slug url.
|
||||||
* @param cp comment page number
|
* @param cp comment page number
|
||||||
* @param request request
|
|
||||||
* @param model model
|
* @param model model
|
||||||
* @return template path: theme/{theme}/post.ftl
|
* @return template path: theme/{theme}/post.ftl
|
||||||
*/
|
*/
|
||||||
|
@ -110,14 +109,8 @@ public class ContentArchiveController {
|
||||||
Model model) {
|
Model model) {
|
||||||
Post post = postService.getBy(PostStatus.PUBLISHED, url);
|
Post post = postService.getBy(PostStatus.PUBLISHED, url);
|
||||||
|
|
||||||
postService.getNextPost(post.getCreateTime()).ifPresent(nextPost -> {
|
postService.getNextPost(post.getCreateTime()).ifPresent(nextPost -> model.addAttribute("nextPost", nextPost));
|
||||||
log.debug("Next post: [{}]", nextPost);
|
postService.getPrePost(post.getCreateTime()).ifPresent(prePost -> model.addAttribute("prePost", prePost));
|
||||||
model.addAttribute("nextPost", nextPost);
|
|
||||||
});
|
|
||||||
postService.getPrePost(post.getCreateTime()).ifPresent(prePost -> {
|
|
||||||
log.debug("Pre post: [{}]", prePost);
|
|
||||||
model.addAttribute("prePost", prePost);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
List<Category> categories = postCategoryService.listCategoryBy(post.getId());
|
List<Category> categories = postCategoryService.listCategoryBy(post.getId());
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
package run.halo.app.event;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.context.ApplicationListener;
|
||||||
|
import run.halo.app.utils.ReflectionUtils;
|
||||||
|
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log event listener test.
|
||||||
|
*
|
||||||
|
* @author johnniang
|
||||||
|
* @date 19-4-21
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class LogEventListenerTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getGenericTypeTest() {
|
||||||
|
ParameterizedType parameterizedType = ReflectionUtils.getParameterizedType(ApplicationListener.class, LogEventListener.class);
|
||||||
|
Type[] actualTypeArguments = Objects.requireNonNull(parameterizedType).getActualTypeArguments();
|
||||||
|
Type actualTypeArgument = actualTypeArguments[0];
|
||||||
|
assertThat(actualTypeArgument.getTypeName(), equalTo(LogEvent.class.getTypeName()));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue