fix: unapproved replies are included in the reply count of comments (#3578)

#### What type of PR is this?
/kind improvement
/area core
/milestone 2.4.x
/kind api-change

#### What this PR does / why we need it:
修复未审核过的回复包含在了评论的回复数量中的问题

此改动需要评论组件修改回复数量取值为 `status.visibleReplyCount`

how to test it?
1. 创建评论,并在评论下回复
2. 评论的所有回复被计数在 `status.replyCount` 中
3. 而 `status.visibleReplyCount` 数量不包含 `spec.hiden=true` 或 `spec.approved = false` 的

#### Which issue(s) this PR fixes:
Fixes #3165

#### Does this PR introduce a user-facing change?

```release-note
修复未审核过的回复包含在了评论的回复数量中的问题
```
pull/3587/head^2
guqing 2023-03-27 17:08:04 +08:00 committed by GitHub
parent da6a458d71
commit 2b73a56b6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 29 additions and 13 deletions

View File

@ -123,6 +123,8 @@ public class Comment extends AbstractExtension {
private Integer replyCount; private Integer replyCount;
private Integer visibleReplyCount;
private Integer unreadReplyCount; private Integer unreadReplyCount;
private Boolean hasNewReply; private Boolean hasNewReply;

View File

@ -6,7 +6,7 @@ import lombok.AllArgsConstructor;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import run.halo.app.core.extension.content.Reply; import run.halo.app.core.extension.content.Reply;
import run.halo.app.event.post.ReplyCreatedEvent; import run.halo.app.event.post.ReplyChangedEvent;
import run.halo.app.event.post.ReplyDeletedEvent; import run.halo.app.event.post.ReplyDeletedEvent;
import run.halo.app.extension.ExtensionClient; import run.halo.app.extension.ExtensionClient;
import run.halo.app.extension.controller.Controller; import run.halo.app.extension.controller.Controller;
@ -36,10 +36,9 @@ public class ReplyReconciler implements Reconciler<Reconciler.Request> {
return; return;
} }
if (addFinalizerIfNecessary(reply)) { addFinalizerIfNecessary(reply);
// on reply created // on reply created
eventPublisher.publishEvent(new ReplyCreatedEvent(this, reply)); eventPublisher.publishEvent(new ReplyChangedEvent(this, reply));
}
}); });
return new Result(false, null); return new Result(false, null);
} }
@ -56,10 +55,10 @@ public class ReplyReconciler implements Reconciler<Reconciler.Request> {
}); });
} }
private boolean addFinalizerIfNecessary(Reply oldReply) { private void addFinalizerIfNecessary(Reply oldReply) {
Set<String> finalizers = oldReply.getMetadata().getFinalizers(); Set<String> finalizers = oldReply.getMetadata().getFinalizers();
if (finalizers != null && finalizers.contains(FINALIZER_NAME)) { if (finalizers != null && finalizers.contains(FINALIZER_NAME)) {
return false; return;
} }
client.fetch(Reply.class, oldReply.getMetadata().getName()) client.fetch(Reply.class, oldReply.getMetadata().getName())
.ifPresent(reply -> { .ifPresent(reply -> {
@ -71,7 +70,6 @@ public class ReplyReconciler implements Reconciler<Reconciler.Request> {
newFinalizers.add(FINALIZER_NAME); newFinalizers.add(FINALIZER_NAME);
client.update(reply); client.update(reply);
}); });
return true;
} }
@Override @Override

View File

@ -6,9 +6,9 @@ import run.halo.app.core.extension.content.Reply;
* @author guqing * @author guqing
* @since 2.0.0 * @since 2.0.0
*/ */
public class ReplyCreatedEvent extends ReplyEvent { public class ReplyChangedEvent extends ReplyEvent {
public ReplyCreatedEvent(Object source, Reply reply) { public ReplyChangedEvent(Object source, Reply reply) {
super(source, reply); super(source, reply);
} }
} }

View File

@ -1,5 +1,7 @@
package run.halo.app.metrics; package run.halo.app.metrics;
import static org.apache.commons.lang3.BooleanUtils.isFalse;
import static org.apache.commons.lang3.BooleanUtils.isTrue;
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
import java.time.Duration; import java.time.Duration;
@ -8,6 +10,7 @@ import java.util.List;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.context.SmartLifecycle; import org.springframework.context.SmartLifecycle;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import run.halo.app.content.comment.ReplyService; import run.halo.app.content.comment.ReplyService;
import run.halo.app.core.extension.content.Comment; import run.halo.app.core.extension.content.Comment;
@ -62,6 +65,13 @@ public class ReplyEventReconciler implements Reconciler<ReplyEvent>, SmartLifecy
// total reply count // total reply count
status.setReplyCount(replies.size()); status.setReplyCount(replies.size());
long visibleReplyCount = replies.stream()
.filter(reply -> isTrue(reply.getSpec().getApproved())
&& isFalse(reply.getSpec().getHidden())
)
.count();
status.setVisibleReplyCount((int) visibleReplyCount);
// calculate last reply time // calculate last reply time
Instant lastReplyTime = replies.stream() Instant lastReplyTime = replies.stream()
.findFirst() .findFirst()
@ -73,6 +83,7 @@ public class ReplyEventReconciler implements Reconciler<ReplyEvent>, SmartLifecy
Instant lastReadTime = comment.getSpec().getLastReadTime(); Instant lastReadTime = comment.getSpec().getLastReadTime();
status.setUnreadReplyCount(Comment.getUnreadReplyCount(replies, lastReadTime)); status.setUnreadReplyCount(Comment.getUnreadReplyCount(replies, lastReadTime));
status.setHasNewReply(defaultIfNull(status.getUnreadReplyCount(), 0) > 0);
client.update(comment); client.update(comment);
}); });
@ -107,8 +118,13 @@ public class ReplyEventReconciler implements Reconciler<ReplyEvent>, SmartLifecy
return this.running; return this.running;
} }
@EventListener(ReplyEvent.class) @Component
public void onReplyAdded(ReplyEvent replyEvent) { public class ReplyEventListener {
replyEventQueue.addImmediately(replyEvent);
@Async
@EventListener(ReplyEvent.class)
public void onReplyEvent(ReplyEvent replyEvent) {
replyEventQueue.addImmediately(replyEvent);
}
} }
} }