mirror of https://github.com/halo-dev/halo
refactor: transition behavior of post state after category updated (#1785)
* refactor: transition behavior of post state after category updated * fix: post status * refactor: category encryption * refactor: modify unit test case * fix: category encryption when post has a passwordpull/1791/head^2
parent
a7825f3f45
commit
2bccfb1abb
|
@ -1,6 +1,7 @@
|
|||
package run.halo.app.event.category;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.lang.Nullable;
|
||||
import run.halo.app.model.entity.Category;
|
||||
|
||||
/**
|
||||
|
@ -11,14 +12,29 @@ import run.halo.app.model.entity.Category;
|
|||
*/
|
||||
public class CategoryUpdatedEvent extends ApplicationEvent {
|
||||
|
||||
private final Category beforeUpdated;
|
||||
private final Category category;
|
||||
private final boolean beforeIsPrivate;
|
||||
|
||||
public CategoryUpdatedEvent(Object source, Category category) {
|
||||
public CategoryUpdatedEvent(Object source, Category category,
|
||||
Category beforeUpdated, boolean beforeIsPrivate) {
|
||||
super(source);
|
||||
this.category = category;
|
||||
this.beforeUpdated = beforeUpdated;
|
||||
this.beforeIsPrivate = beforeIsPrivate;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Category getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Category getBeforeUpdated() {
|
||||
return beforeUpdated;
|
||||
}
|
||||
|
||||
public boolean isBeforeIsPrivate() {
|
||||
return beforeIsPrivate;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package run.halo.app.listener.post;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.event.EventListener;
|
||||
|
@ -48,20 +50,119 @@ public class PostRefreshStatusListener {
|
|||
@EventListener(CategoryUpdatedEvent.class)
|
||||
public void categoryUpdatedListener(CategoryUpdatedEvent event) {
|
||||
Category category = event.getCategory();
|
||||
if (!categoryService.existsById(category.getId())) {
|
||||
Category beforeUpdated = event.getBeforeUpdated();
|
||||
boolean beforeIsPrivate = event.isBeforeIsPrivate();
|
||||
RecordState recordState = determineRecordState(beforeUpdated, category);
|
||||
if (RecordState.DELETED.equals(recordState) || category == null) {
|
||||
return;
|
||||
}
|
||||
boolean isPrivate = categoryService.isPrivate(category.getId());
|
||||
|
||||
// now
|
||||
boolean isPrivate = categoryService.isPrivate(category.getId());
|
||||
List<Post> posts = findPostsByCategoryIdRecursively(category.getId());
|
||||
if (isPrivate) {
|
||||
posts.forEach(post -> post.setStatus(PostStatus.INTIMATE));
|
||||
posts.forEach(post -> {
|
||||
if (post.getStatus() == PostStatus.PUBLISHED) {
|
||||
post.setStatus(PostStatus.INTIMATE);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
posts.forEach(post -> post.setStatus(PostStatus.DRAFT));
|
||||
if (RecordState.UPDATED.equals(recordState)) {
|
||||
Set<Integer> encryptedCategories =
|
||||
pickUpEncryptedFromUpdatedRecord(category.getId());
|
||||
for (Post post : posts) {
|
||||
boolean belongsToEncryptedCategory =
|
||||
postBelongsToEncryptedCategory(post.getId(), encryptedCategories);
|
||||
if (!belongsToEncryptedCategory && StringUtils.isBlank(post.getPassword())
|
||||
&& beforeIsPrivate
|
||||
&& post.getStatus() == PostStatus.INTIMATE) {
|
||||
post.setStatus(PostStatus.PUBLISHED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
postService.updateInBatch(posts);
|
||||
}
|
||||
|
||||
private boolean postBelongsToEncryptedCategory(Integer postId,
|
||||
Set<Integer> encryptedCategories) {
|
||||
Set<Integer> categoryIds =
|
||||
postCategoryService.listCategoryIdsByPostId(postId);
|
||||
|
||||
boolean encrypted = false;
|
||||
for (Integer categoryId : categoryIds) {
|
||||
if (encryptedCategories.contains(categoryId)) {
|
||||
encrypted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
private Set<Integer> pickUpEncryptedFromUpdatedRecord(Integer categoryId) {
|
||||
Set<Integer> privateCategories = new HashSet<>();
|
||||
|
||||
List<Category> categories = categoryService.listAllByParentId(categoryId);
|
||||
Map<Integer, Category> categoryMap =
|
||||
ServiceUtils.convertToMap(categories, Category::getId);
|
||||
categories.forEach(category -> {
|
||||
boolean privateBy = isPrivateBy(category.getId(), categoryMap);
|
||||
if (privateBy) {
|
||||
privateCategories.add(category.getId());
|
||||
}
|
||||
});
|
||||
return privateCategories;
|
||||
}
|
||||
|
||||
private boolean isPrivateBy(Integer categoryId, Map<Integer, Category> categoryMap) {
|
||||
return findFirstEncryptedCategoryBy(categoryMap, categoryId) != null;
|
||||
}
|
||||
|
||||
private Category findFirstEncryptedCategoryBy(Map<Integer, Category> idToCategoryMap,
|
||||
Integer categoryId) {
|
||||
Category category = idToCategoryMap.get(categoryId);
|
||||
|
||||
if (categoryId == 0 || category == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(category.getPassword())) {
|
||||
return category;
|
||||
}
|
||||
|
||||
return findFirstEncryptedCategoryBy(idToCategoryMap, category.getParentId());
|
||||
}
|
||||
|
||||
private RecordState determineRecordState(Category before, Category updated) {
|
||||
if (before == null) {
|
||||
if (updated != null) {
|
||||
// created: null -> record
|
||||
return RecordState.CREATED;
|
||||
} else {
|
||||
// unchanged: null -> null
|
||||
return RecordState.UNCHANGED;
|
||||
}
|
||||
} else {
|
||||
if (updated == null) {
|
||||
// deleted: record -> null
|
||||
return RecordState.DELETED;
|
||||
} else {
|
||||
// updated: record -> record
|
||||
return RecordState.UPDATED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* effective state for database record.
|
||||
*/
|
||||
enum RecordState {
|
||||
CREATED,
|
||||
UPDATED,
|
||||
DELETED,
|
||||
UNCHANGED
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<Post> findPostsByCategoryIdRecursively(Integer categoryId) {
|
||||
Set<Integer> categoryIds =
|
||||
|
@ -84,13 +185,23 @@ public class PostRefreshStatusListener {
|
|||
if (!postService.existsById(post.getId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
PostStatus status = post.getStatus();
|
||||
boolean isPrivate = postCategoryService.listByPostId(post.getId())
|
||||
.stream()
|
||||
.anyMatch(postCategory -> categoryService.isPrivate(postCategory.getCategoryId()));
|
||||
|
||||
if (isPrivate || StringUtils.isNotBlank(post.getPassword())) {
|
||||
post.setStatus(PostStatus.INTIMATE);
|
||||
if (post.getStatus() != PostStatus.DRAFT) {
|
||||
if (StringUtils.isNotEmpty(post.getPassword())) {
|
||||
status = PostStatus.INTIMATE;
|
||||
} else if (isPrivate) {
|
||||
status = PostStatus.INTIMATE;
|
||||
} else {
|
||||
status = PostStatus.PUBLISHED;
|
||||
}
|
||||
} else if (!isPrivate && StringUtils.isBlank(post.getPassword())) {
|
||||
status = PostStatus.DRAFT;
|
||||
}
|
||||
post.setStatus(status);
|
||||
postService.update(post);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,8 +105,14 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
|
|||
|
||||
@Override
|
||||
public Category update(Category category) {
|
||||
Category persisted = getById(category.getId());
|
||||
Category beforeUpdated = new Category();
|
||||
BeanUtils.updateProperties(persisted, beforeUpdated);
|
||||
boolean beforeIsPrivate = isPrivate(category.getId());
|
||||
|
||||
Category updated = super.update(category);
|
||||
applicationContext.publishEvent(new CategoryUpdatedEvent(this, category));
|
||||
applicationContext.publishEvent(
|
||||
new CategoryUpdatedEvent(this, category, beforeUpdated, beforeIsPrivate));
|
||||
return updated;
|
||||
}
|
||||
|
||||
|
@ -192,7 +198,7 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
|
|||
// Remove post categories
|
||||
postCategoryService.removeByCategoryId(categoryId);
|
||||
|
||||
applicationContext.publishEvent(new CategoryUpdatedEvent(this, category));
|
||||
applicationContext.publishEvent(new CategoryUpdatedEvent(this, null, category, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -358,10 +364,15 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
|
|||
return categoryRepository.findAllById(categoryIds)
|
||||
.stream()
|
||||
.map(categoryToUpdate -> {
|
||||
// 将持久化状态的对象转非session管理对象否则数据会被更新
|
||||
Category categoryBefore = BeanUtils.transformFrom(categoryToUpdate, Category.class);
|
||||
boolean beforeIsPrivate = isPrivate(categoryToUpdate.getId());
|
||||
|
||||
Category categoryParam = idCategoryParamMap.get(categoryToUpdate.getId());
|
||||
BeanUtils.updateProperties(categoryParam, categoryToUpdate);
|
||||
Category categoryUpdated = update(categoryToUpdate);
|
||||
applicationContext.publishEvent(new CategoryUpdatedEvent(this, categoryUpdated));
|
||||
applicationContext.publishEvent(new CategoryUpdatedEvent(this,
|
||||
categoryUpdated, categoryBefore, beforeIsPrivate));
|
||||
return categoryUpdated;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
|
|
@ -584,13 +584,6 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
|
|||
Set<Integer> categoryIds, Set<PostMeta> metas) {
|
||||
Assert.notNull(post, "Post param must not be null");
|
||||
|
||||
// if password is not empty
|
||||
if (post.getStatus() != PostStatus.DRAFT
|
||||
&& (StringUtils.isNotEmpty(post.getPassword()))
|
||||
) {
|
||||
post.setStatus(PostStatus.INTIMATE);
|
||||
}
|
||||
|
||||
post = super.createOrUpdateBy(post);
|
||||
|
||||
postTagService.removeByPostId(post.getId());
|
||||
|
|
|
@ -72,7 +72,6 @@ public class PostRefreshStatusListenerTest {
|
|||
PostDetailVO postDetailVO =
|
||||
postService.createBy(post, Set.of(), Set.of(2), Set.of(), false);
|
||||
assertThat(postDetailVO).isNotNull();
|
||||
assertThat(postDetailVO.getStatus()).isEqualTo(PostStatus.INTIMATE);
|
||||
|
||||
category1.setPassword(null);
|
||||
categoryService.update(category1);
|
||||
|
|
Loading…
Reference in New Issue