mirror of
https://github.com/nagisa77/OpenIsle.git
synced 2026-06-03 08:27:35 +08:00
fix: 完善提案通知流程,防止重复通知,提案成功自动插入分类
This commit is contained in:
@@ -46,6 +46,10 @@ public enum NotificationType {
|
|||||||
POLL_RESULT_OWNER,
|
POLL_RESULT_OWNER,
|
||||||
/** A poll you participated in has concluded */
|
/** A poll you participated in has concluded */
|
||||||
POLL_RESULT_PARTICIPANT,
|
POLL_RESULT_PARTICIPANT,
|
||||||
|
/** Your category proposal has concluded */
|
||||||
|
CATEGORY_PROPOSAL_RESULT_OWNER,
|
||||||
|
/** A category proposal you participated in has concluded */
|
||||||
|
CATEGORY_PROPOSAL_RESULT_PARTICIPANT,
|
||||||
/** Your post was featured */
|
/** Your post was featured */
|
||||||
POST_FEATURED,
|
POST_FEATURED,
|
||||||
/** Someone donated to your post */
|
/** Someone donated to your post */
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ public class PostService {
|
|||||||
private final PointService pointService;
|
private final PointService pointService;
|
||||||
private final PostChangeLogService postChangeLogService;
|
private final PostChangeLogService postChangeLogService;
|
||||||
private final PointHistoryRepository pointHistoryRepository;
|
private final PointHistoryRepository pointHistoryRepository;
|
||||||
|
private final CategoryService categoryService;
|
||||||
private final ConcurrentMap<Long, ScheduledFuture<?>> scheduledFinalizations =
|
private final ConcurrentMap<Long, ScheduledFuture<?>> scheduledFinalizations =
|
||||||
new ConcurrentHashMap<>();
|
new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@@ -112,7 +113,8 @@ public class PostService {
|
|||||||
PointHistoryRepository pointHistoryRepository,
|
PointHistoryRepository pointHistoryRepository,
|
||||||
@Value("${app.post.publish-mode:DIRECT}") PublishMode publishMode,
|
@Value("${app.post.publish-mode:DIRECT}") PublishMode publishMode,
|
||||||
RedisTemplate redisTemplate,
|
RedisTemplate redisTemplate,
|
||||||
SearchIndexEventPublisher searchIndexEventPublisher
|
SearchIndexEventPublisher searchIndexEventPublisher,
|
||||||
|
CategoryService categoryService
|
||||||
) {
|
) {
|
||||||
this.postRepository = postRepository;
|
this.postRepository = postRepository;
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
@@ -141,6 +143,7 @@ public class PostService {
|
|||||||
|
|
||||||
this.redisTemplate = redisTemplate;
|
this.redisTemplate = redisTemplate;
|
||||||
this.searchIndexEventPublisher = searchIndexEventPublisher;
|
this.searchIndexEventPublisher = searchIndexEventPublisher;
|
||||||
|
this.categoryService = categoryService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventListener(ApplicationReadyEvent.class)
|
@EventListener(ApplicationReadyEvent.class)
|
||||||
@@ -429,8 +432,11 @@ public class PostService {
|
|||||||
boolean quorumMet = totalParticipants >= cp.getQuorum();
|
boolean quorumMet = totalParticipants >= cp.getQuorum();
|
||||||
int approvePercent = totalParticipants > 0 ? (approveVotes * 100) / totalParticipants : 0;
|
int approvePercent = totalParticipants > 0 ? (approveVotes * 100) / totalParticipants : 0;
|
||||||
boolean thresholdMet = approvePercent >= cp.getApproveThreshold();
|
boolean thresholdMet = approvePercent >= cp.getApproveThreshold();
|
||||||
|
boolean approved = false;
|
||||||
|
String rejectReason = null;
|
||||||
if (quorumMet && thresholdMet) {
|
if (quorumMet && thresholdMet) {
|
||||||
cp.setProposalStatus(CategoryProposalStatus.APPROVED);
|
cp.setProposalStatus(CategoryProposalStatus.APPROVED);
|
||||||
|
approved = true;
|
||||||
} else {
|
} else {
|
||||||
cp.setProposalStatus(CategoryProposalStatus.REJECTED);
|
cp.setProposalStatus(CategoryProposalStatus.REJECTED);
|
||||||
String reason;
|
String reason;
|
||||||
@@ -442,6 +448,7 @@ public class PostService {
|
|||||||
reason = "赞成率不足";
|
reason = "赞成率不足";
|
||||||
}
|
}
|
||||||
cp.setRejectReason(reason);
|
cp.setRejectReason(reason);
|
||||||
|
rejectReason = reason;
|
||||||
}
|
}
|
||||||
cp.setResultSnapshot(
|
cp.setResultSnapshot(
|
||||||
"approveVotes=" +
|
"approveVotes=" +
|
||||||
@@ -452,28 +459,37 @@ public class PostService {
|
|||||||
approvePercent
|
approvePercent
|
||||||
);
|
);
|
||||||
categoryProposalPostRepository.save(cp);
|
categoryProposalPostRepository.save(cp);
|
||||||
|
if (approved) {
|
||||||
|
categoryService.createCategory(cp.getProposedName(), cp.getDescription(), "star", null);
|
||||||
|
}
|
||||||
if (cp.getAuthor() != null) {
|
if (cp.getAuthor() != null) {
|
||||||
notificationService.createNotification(
|
notificationService.createNotification(
|
||||||
cp.getAuthor(),
|
cp.getAuthor(),
|
||||||
NotificationType.POLL_RESULT_OWNER,
|
NotificationType.CATEGORY_PROPOSAL_RESULT_OWNER,
|
||||||
cp,
|
cp,
|
||||||
null,
|
null,
|
||||||
|
approved,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
approved ? null : rejectReason
|
||||||
null
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for (User participant : cp.getParticipants()) {
|
for (User participant : cp.getParticipants()) {
|
||||||
|
if (
|
||||||
|
cp.getAuthor() != null &&
|
||||||
|
java.util.Objects.equals(participant.getId(), cp.getAuthor().getId())
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
notificationService.createNotification(
|
notificationService.createNotification(
|
||||||
participant,
|
participant,
|
||||||
NotificationType.POLL_RESULT_PARTICIPANT,
|
NotificationType.CATEGORY_PROPOSAL_RESULT_PARTICIPANT,
|
||||||
cp,
|
cp,
|
||||||
null,
|
null,
|
||||||
|
approved,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
approved ? null : rejectReason
|
||||||
null
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
postChangeLogService.recordVoteResult(cp);
|
postChangeLogService.recordVoteResult(cp);
|
||||||
@@ -576,6 +592,9 @@ public class PostService {
|
|||||||
pollPostRepository
|
pollPostRepository
|
||||||
.findById(postId)
|
.findById(postId)
|
||||||
.ifPresent(pp -> {
|
.ifPresent(pp -> {
|
||||||
|
if (pp instanceof CategoryProposalPost) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (pp.isResultAnnounced()) {
|
if (pp.isResultAnnounced()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="home-page">
|
<div class="home-page">
|
||||||
<!-- <div v-if="!isMobile" class="search-container">
|
|
||||||
<div class="search-title">一切可能,从此刻启航,在此遇见灵感与共鸣</div>
|
|
||||||
<SearchDropdown />
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<div class="topic-container">
|
<div class="topic-container">
|
||||||
<div class="topic-item-container">
|
<div class="topic-item-container">
|
||||||
<div
|
<div
|
||||||
@@ -117,7 +112,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-else class="placeholder-container">分类浏览功能开发中,敬请期待。</div>
|
<div v-else class="placeholder-container">分类浏览功能开发中,敬请期待。</div>
|
||||||
|
|
||||||
<!-- ✅ 通用“底部加载更多”组件(自管 loading/observer/并发) -->
|
<!-- 通用“底部加载更多”组件(自管 loading/observer/并发) -->
|
||||||
<InfiniteLoadMore
|
<InfiniteLoadMore
|
||||||
v-if="articles.length > 0"
|
v-if="articles.length > 0"
|
||||||
:key="ioKey"
|
:key="ioKey"
|
||||||
|
|||||||
@@ -75,7 +75,9 @@
|
|||||||
@click="markRead(item.id)"
|
@click="markRead(item.id)"
|
||||||
:to="`/posts/${item.post.id}#comment-${item.parentComment.id}`"
|
:to="`/posts/${item.post.id}#comment-${item.parentComment.id}`"
|
||||||
>
|
>
|
||||||
<span v-html="stripMarkdownWithTiebaMoji(item.parentComment.content, 500)"></span>
|
<span
|
||||||
|
v-html="stripMarkdownWithTiebaMoji(item.parentComment.content, 500)"
|
||||||
|
></span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</span>
|
</span>
|
||||||
回复了
|
回复了
|
||||||
@@ -85,7 +87,9 @@
|
|||||||
@click="markRead(item.id)"
|
@click="markRead(item.id)"
|
||||||
:to="`/posts/${item.post.id}#comment-${item.comment.id}`"
|
:to="`/posts/${item.post.id}#comment-${item.comment.id}`"
|
||||||
>
|
>
|
||||||
<span v-html="stripMarkdownWithTiebaMoji(item.comment.content, 500)"></span>
|
<span
|
||||||
|
v-html="stripMarkdownWithTiebaMoji(item.comment.content, 500)"
|
||||||
|
></span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</span>
|
</span>
|
||||||
</NotificationContainer>
|
</NotificationContainer>
|
||||||
@@ -115,7 +119,9 @@
|
|||||||
@click="markRead(item.id)"
|
@click="markRead(item.id)"
|
||||||
:to="`/posts/${item.post.id}#comment-${item.comment.id}`"
|
:to="`/posts/${item.post.id}#comment-${item.comment.id}`"
|
||||||
>
|
>
|
||||||
<span v-html="stripMarkdownWithTiebaMoji(item.comment.content, 500)"></span>
|
<span
|
||||||
|
v-html="stripMarkdownWithTiebaMoji(item.comment.content, 500)"
|
||||||
|
></span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</span>
|
</span>
|
||||||
</NotificationContainer>
|
</NotificationContainer>
|
||||||
@@ -162,7 +168,9 @@
|
|||||||
@click="markRead(item.id)"
|
@click="markRead(item.id)"
|
||||||
:to="`/posts/${item.post.id}#comment-${item.comment.id}`"
|
:to="`/posts/${item.post.id}#comment-${item.comment.id}`"
|
||||||
>
|
>
|
||||||
<span v-html="stripMarkdownWithTiebaMoji(item.comment.content, 500)"></span>
|
<span
|
||||||
|
v-html="stripMarkdownWithTiebaMoji(item.comment.content, 500)"
|
||||||
|
></span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</span>
|
</span>
|
||||||
进行了表态
|
进行了表态
|
||||||
@@ -251,6 +259,38 @@
|
|||||||
已出结果
|
已出结果
|
||||||
</NotificationContainer>
|
</NotificationContainer>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-else-if="item.type === 'CATEGORY_PROPOSAL_RESULT_OWNER'">
|
||||||
|
<NotificationContainer :item="item" :markRead="markRead">
|
||||||
|
你的分类提案
|
||||||
|
<NuxtLink
|
||||||
|
class="notif-content-text"
|
||||||
|
@click="markRead(item.id)"
|
||||||
|
:to="`/posts/${item.post.id}`"
|
||||||
|
>
|
||||||
|
{{ stripMarkdownLength(item.post.title, 100) }}
|
||||||
|
</NuxtLink>
|
||||||
|
<span v-if="item.approved">已通过</span>
|
||||||
|
<span v-else>
|
||||||
|
未通过<span v-if="item.content">,原因:{{ item.content }}</span>
|
||||||
|
</span>
|
||||||
|
</NotificationContainer>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="item.type === 'CATEGORY_PROPOSAL_RESULT_PARTICIPANT'">
|
||||||
|
<NotificationContainer :item="item" :markRead="markRead">
|
||||||
|
你参与的分类提案
|
||||||
|
<NuxtLink
|
||||||
|
class="notif-content-text"
|
||||||
|
@click="markRead(item.id)"
|
||||||
|
:to="`/posts/${item.post.id}`"
|
||||||
|
>
|
||||||
|
{{ stripMarkdownLength(item.post.title, 100) }}
|
||||||
|
</NuxtLink>
|
||||||
|
<span v-if="item.approved">已通过</span>
|
||||||
|
<span v-else>
|
||||||
|
未通过<span v-if="item.content">,原因:{{ item.content }}</span>
|
||||||
|
</span>
|
||||||
|
</NotificationContainer>
|
||||||
|
</template>
|
||||||
<template v-else-if="item.type === 'POST_UPDATED'">
|
<template v-else-if="item.type === 'POST_UPDATED'">
|
||||||
<NotificationContainer :item="item" :markRead="markRead">
|
<NotificationContainer :item="item" :markRead="markRead">
|
||||||
您关注的帖子
|
您关注的帖子
|
||||||
@@ -287,7 +327,9 @@
|
|||||||
@click="markRead(item.id)"
|
@click="markRead(item.id)"
|
||||||
:to="`/posts/${item.post.id}#comment-${item.parentComment.id}`"
|
:to="`/posts/${item.post.id}#comment-${item.parentComment.id}`"
|
||||||
>
|
>
|
||||||
<span v-html="stripMarkdownWithTiebaMoji(item.parentComment.content, 500)"></span>
|
<span
|
||||||
|
v-html="stripMarkdownWithTiebaMoji(item.parentComment.content, 500)"
|
||||||
|
></span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
回复了
|
回复了
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
@@ -775,6 +817,10 @@ const formatType = (t) => {
|
|||||||
return '发布的投票结果已公布'
|
return '发布的投票结果已公布'
|
||||||
case 'POLL_RESULT_PARTICIPANT':
|
case 'POLL_RESULT_PARTICIPANT':
|
||||||
return '参与的投票结果已公布'
|
return '参与的投票结果已公布'
|
||||||
|
case 'CATEGORY_PROPOSAL_RESULT_OWNER':
|
||||||
|
return '分类提案结果已公布'
|
||||||
|
case 'CATEGORY_PROPOSAL_RESULT_PARTICIPANT':
|
||||||
|
return '参与的分类提案结果已公布'
|
||||||
default:
|
default:
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ const iconMap = {
|
|||||||
POLL_VOTE: 'ChartHistogram',
|
POLL_VOTE: 'ChartHistogram',
|
||||||
POLL_RESULT_OWNER: 'RankingList',
|
POLL_RESULT_OWNER: 'RankingList',
|
||||||
POLL_RESULT_PARTICIPANT: 'ChartLine',
|
POLL_RESULT_PARTICIPANT: 'ChartLine',
|
||||||
|
CATEGORY_PROPOSAL_RESULT_OWNER: 'TagOne',
|
||||||
|
CATEGORY_PROPOSAL_RESULT_PARTICIPANT: 'TagOne',
|
||||||
MENTION: 'HashtagKey',
|
MENTION: 'HashtagKey',
|
||||||
POST_DELETED: 'ClearIcon',
|
POST_DELETED: 'ClearIcon',
|
||||||
POST_FEATURED: 'Star',
|
POST_FEATURED: 'Star',
|
||||||
@@ -254,7 +256,9 @@ function createFetchNotifications() {
|
|||||||
} else if (
|
} else if (
|
||||||
n.type === 'POLL_VOTE' ||
|
n.type === 'POLL_VOTE' ||
|
||||||
n.type === 'POLL_RESULT_OWNER' ||
|
n.type === 'POLL_RESULT_OWNER' ||
|
||||||
n.type === 'POLL_RESULT_PARTICIPANT'
|
n.type === 'POLL_RESULT_PARTICIPANT' ||
|
||||||
|
n.type === 'CATEGORY_PROPOSAL_RESULT_OWNER' ||
|
||||||
|
n.type === 'CATEGORY_PROPOSAL_RESULT_PARTICIPANT'
|
||||||
) {
|
) {
|
||||||
arr.push({
|
arr.push({
|
||||||
...n,
|
...n,
|
||||||
|
|||||||
Reference in New Issue
Block a user