fix: update the posts encryption status synchronously after deleting the category (#1815)

* refactor: update the posts encryption status synchronously after deleting the category

* chore: delete unused code
pull/1820/head
guqing 2022-04-04 11:40:41 +08:00 committed by GitHub
parent 1de5799f82
commit 90d1bce9b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 77 additions and 79 deletions

View File

@ -1,5 +1,6 @@
package run.halo.app.event.category; package run.halo.app.event.category;
import java.util.Set;
import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEvent;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import run.halo.app.model.entity.Category; import run.halo.app.model.entity.Category;
@ -15,13 +16,15 @@ public class CategoryUpdatedEvent extends ApplicationEvent {
private final Category beforeUpdated; private final Category beforeUpdated;
private final Category category; private final Category category;
private final boolean beforeIsPrivate; private final boolean beforeIsPrivate;
private final Set<Integer> postIds;
public CategoryUpdatedEvent(Object source, Category category, public CategoryUpdatedEvent(Object source, Category category,
Category beforeUpdated, boolean beforeIsPrivate) { Category beforeUpdated, boolean beforeIsPrivate, Set<Integer> postIds) {
super(source); super(source);
this.category = category; this.category = category;
this.beforeUpdated = beforeUpdated; this.beforeUpdated = beforeUpdated;
this.beforeIsPrivate = beforeIsPrivate; this.beforeIsPrivate = beforeIsPrivate;
this.postIds = postIds;
} }
@Nullable @Nullable
@ -37,4 +40,8 @@ public class CategoryUpdatedEvent extends ApplicationEvent {
public boolean isBeforeIsPrivate() { public boolean isBeforeIsPrivate() {
return beforeIsPrivate; return beforeIsPrivate;
} }
public Set<Integer> getPostIds() {
return postIds;
}
} }

View File

@ -1,24 +1,19 @@
package run.halo.app.listener.post; package run.halo.app.listener.post;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import run.halo.app.event.category.CategoryUpdatedEvent; import run.halo.app.event.category.CategoryUpdatedEvent;
import run.halo.app.event.post.PostUpdatedEvent; import run.halo.app.event.post.PostUpdatedEvent;
import run.halo.app.model.entity.Category; import run.halo.app.model.entity.Category;
import run.halo.app.model.entity.Post; import run.halo.app.model.entity.Post;
import run.halo.app.model.entity.PostCategory;
import run.halo.app.model.enums.PostStatus; import run.halo.app.model.enums.PostStatus;
import run.halo.app.service.CategoryService; import run.halo.app.service.CategoryService;
import run.halo.app.service.PostCategoryService; import run.halo.app.service.PostCategoryService;
import run.halo.app.service.PostService; import run.halo.app.service.PostService;
import run.halo.app.utils.ServiceUtils;
/** /**
* Post status management. * Post status management.
@ -53,45 +48,55 @@ public class PostRefreshStatusListener {
Category beforeUpdated = event.getBeforeUpdated(); Category beforeUpdated = event.getBeforeUpdated();
boolean beforeIsPrivate = event.isBeforeIsPrivate(); boolean beforeIsPrivate = event.isBeforeIsPrivate();
RecordState recordState = determineRecordState(beforeUpdated, category); RecordState recordState = determineRecordState(beforeUpdated, category);
List<Post> posts = postService.listAllByIds(event.getPostIds());
// handle delete action
if (RecordState.DELETED.equals(recordState) || category == null) { if (RecordState.DELETED.equals(recordState) || category == null) {
if (beforeUpdated == null || !beforeIsPrivate) {
return;
}
// Cancel the encryption status of the posts
changeStatusToPublishIfNecessary(posts, beforeUpdated.getId());
return; return;
} }
// now // now
boolean isPrivate = categoryService.isPrivate(category.getId()); boolean isPrivate = categoryService.isPrivate(category.getId());
List<Post> posts = findPostsByCategoryIdRecursively(category.getId());
if (isPrivate) { if (isPrivate) {
List<Post> postsToUpdate = new ArrayList<>();
posts.forEach(post -> { posts.forEach(post -> {
if (post.getStatus() == PostStatus.PUBLISHED) { if (post.getStatus() == PostStatus.PUBLISHED) {
post.setStatus(PostStatus.INTIMATE); post.setStatus(PostStatus.INTIMATE);
postsToUpdate.add(post);
} }
}); });
} else { postService.updateInBatch(postsToUpdate);
if (RecordState.UPDATED.equals(recordState)) { } else if (beforeIsPrivate && RecordState.UPDATED.equals(recordState)) {
Set<Integer> encryptedCategories = // Cancel the encryption status of the posts
pickUpEncryptedFromUpdatedRecord(category.getId()); changeStatusToPublishIfNecessary(posts, 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, private void changeStatusToPublishIfNecessary(List<Post> posts, Integer categoryId) {
Set<Integer> encryptedCategories) { List<Post> postsToUpdate = new ArrayList<>();
Set<Integer> categoryIds = for (Post post : posts) {
postCategoryService.listCategoryIdsByPostId(postId); boolean belongsToEncryptedCategory = postBelongsToEncryptedCategory(post.getId());
if (!belongsToEncryptedCategory && StringUtils.isBlank(post.getPassword())
&& post.getStatus() == PostStatus.INTIMATE) {
post.setStatus(PostStatus.PUBLISHED);
postsToUpdate.add(post);
}
}
postService.updateInBatch(postsToUpdate);
}
private boolean postBelongsToEncryptedCategory(Integer postId) {
Set<Integer> categoryIds = postCategoryService.listCategoryIdsByPostId(postId);
boolean encrypted = false; boolean encrypted = false;
for (Integer categoryId : categoryIds) { for (Integer categoryId : categoryIds) {
if (encryptedCategories.contains(categoryId)) { if (categoryService.isPrivate(categoryId)) {
encrypted = true; encrypted = true;
break; break;
} }
@ -99,40 +104,6 @@ public class PostRefreshStatusListener {
return encrypted; 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) { private RecordState determineRecordState(Category before, Category updated) {
if (before == null) { if (before == null) {
if (updated != null) { if (updated != null) {
@ -153,27 +124,16 @@ public class PostRefreshStatusListener {
} }
} }
/**
* effective state for database record.
*/
enum RecordState { enum RecordState {
/**
* effective state for database record.
*/
CREATED, CREATED,
UPDATED, UPDATED,
DELETED, DELETED,
UNCHANGED UNCHANGED
} }
@NonNull
private List<Post> findPostsByCategoryIdRecursively(Integer categoryId) {
Set<Integer> categoryIds =
ServiceUtils.fetchProperty(categoryService.listAllByParentId(categoryId),
Category::getId);
List<PostCategory> postCategories =
postCategoryService.listByCategoryIdList(new ArrayList<>(categoryIds));
Set<Integer> postIds = ServiceUtils.fetchProperty(postCategories, PostCategory::getPostId);
return postService.listAllByIds(postIds);
}
/** /**
* If the post belongs to any encryption category, set the status to INTIMATE. * If the post belongs to any encryption category, set the status to INTIMATE.
* *

View File

@ -2,6 +2,7 @@ package run.halo.app.service;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
@ -133,4 +134,13 @@ public interface CategoryService extends CrudService<Category, Integer> {
* @return a tree of category. * @return a tree of category.
*/ */
List<CategoryVO> listToTree(List<Category> categories); List<CategoryVO> listToTree(List<Category> categories);
/**
* Recursively query the associated post ids according to the category id.
*
* @param categoryId category id
* @return a collection of post ids
*/
@NonNull
Set<Integer> listPostIdsByCategoryIdRecursively(@NonNull Integer categoryId);
} }

View File

@ -3,6 +3,7 @@ package run.halo.app.service.impl;
import static run.halo.app.model.support.HaloConst.URL_SEPARATOR; import static run.halo.app.model.support.HaloConst.URL_SEPARATOR;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
@ -28,6 +29,7 @@ import run.halo.app.exception.AlreadyExistsException;
import run.halo.app.exception.NotFoundException; import run.halo.app.exception.NotFoundException;
import run.halo.app.model.dto.CategoryDTO; import run.halo.app.model.dto.CategoryDTO;
import run.halo.app.model.entity.Category; import run.halo.app.model.entity.Category;
import run.halo.app.model.entity.PostCategory;
import run.halo.app.model.vo.CategoryVO; import run.halo.app.model.vo.CategoryVO;
import run.halo.app.repository.CategoryRepository; import run.halo.app.repository.CategoryRepository;
import run.halo.app.service.CategoryService; import run.halo.app.service.CategoryService;
@ -111,8 +113,10 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
boolean beforeIsPrivate = isPrivate(category.getId()); boolean beforeIsPrivate = isPrivate(category.getId());
Category updated = super.update(category); Category updated = super.update(category);
Set<Integer> postIds = listPostIdsByCategoryIdRecursively(category.getId());
applicationContext.publishEvent( applicationContext.publishEvent(
new CategoryUpdatedEvent(this, category, beforeUpdated, beforeIsPrivate)); new CategoryUpdatedEvent(this, category, beforeUpdated, beforeIsPrivate, postIds));
return updated; return updated;
} }
@ -183,8 +187,10 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
} }
@Override @Override
@Transactional @Transactional(rollbackFor = Exception.class)
public void removeCategoryAndPostCategoryBy(Integer categoryId) { public void removeCategoryAndPostCategoryBy(Integer categoryId) {
final boolean beforeIsPrivate = isPrivate(categoryId);
final Set<Integer> postIds = listPostIdsByCategoryIdRecursively(categoryId);
List<Category> categories = listByParentId(categoryId); List<Category> categories = listByParentId(categoryId);
if (null != categories && categories.size() > 0) { if (null != categories && categories.size() > 0) {
categories.forEach(category -> { categories.forEach(category -> {
@ -198,7 +204,8 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
// Remove post categories // Remove post categories
postCategoryService.removeByCategoryId(categoryId); postCategoryService.removeByCategoryId(categoryId);
applicationContext.publishEvent(new CategoryUpdatedEvent(this, null, category, false)); applicationContext.publishEvent(
new CategoryUpdatedEvent(this, null, category, beforeIsPrivate, postIds));
} }
@Override @Override
@ -371,12 +378,26 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
Category categoryParam = idCategoryParamMap.get(categoryToUpdate.getId()); Category categoryParam = idCategoryParamMap.get(categoryToUpdate.getId());
BeanUtils.updateProperties(categoryParam, categoryToUpdate); BeanUtils.updateProperties(categoryParam, categoryToUpdate);
Category categoryUpdated = update(categoryToUpdate); Category categoryUpdated = update(categoryToUpdate);
Set<Integer> postIds = listPostIdsByCategoryIdRecursively(categoryUpdated.getId());
applicationContext.publishEvent(new CategoryUpdatedEvent(this, applicationContext.publishEvent(new CategoryUpdatedEvent(this,
categoryUpdated, categoryBefore, beforeIsPrivate)); categoryUpdated, categoryBefore, beforeIsPrivate, postIds));
return categoryUpdated; return categoryUpdated;
}) })
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@NonNull
@Override
public Set<Integer> listPostIdsByCategoryIdRecursively(@NonNull Integer categoryId) {
Set<Integer> categoryIds = ServiceUtils.fetchProperty(listAllByParentId(categoryId),
Category::getId);
if (CollectionUtils.isEmpty(categoryIds)) {
return Collections.emptySet();
}
List<PostCategory> postCategories =
postCategoryService.listByCategoryIdList(new ArrayList<>(categoryIds));
return ServiceUtils.fetchProperty(postCategories, PostCategory::getPostId);
}
} }