Рефакторинг уведомлений треда

This commit is contained in:
Struchkov Mark 2023-02-24 22:12:43 +03:00
parent c835837156
commit ace251118a
Signed by: upagge
GPG Key ID: D3018BE7BA428CA6
8 changed files with 136 additions and 82 deletions

View File

@ -85,6 +85,13 @@ public class Discussion {
return this.notes.get(0); return this.notes.get(0);
} }
public Optional<Note> getLastNote() {
if (this.notes.size() > 1) {
return Optional.ofNullable(this.notes.get(this.notes.size() - 1));
}
return Optional.empty();
}
public Optional<Note> getPrevLastNote() { public Optional<Note> getPrevLastNote() {
final int size = notes.size(); final int size = notes.size();
if (size > 2) { if (size > 2) {

View File

@ -15,26 +15,24 @@ import static dev.struchkov.bot.gitlab.context.domain.notify.task.DiscussionNewN
*/ */
@Getter @Getter
@FieldNames @FieldNames
public class DiscussionNewNotify extends TaskNotify { public class DiscussionNewNotify extends ThreadNotify {
public static final String TYPE = CLASS_NAME; public static final String TYPE = CLASS_NAME;
private final String threadId; private final String threadId;
private final String mrName;
private final List<Pair<String, String>> notes; private final List<Pair<String, String>> notes;
@Builder @Builder
public DiscussionNewNotify( public DiscussionNewNotify(
String threadId, String threadId,
String mrName, String mergeRequestName,
String authorName, String authorName,
String url, String url,
String discussionMessage, String discussionMessage,
@Singular List<Pair<String, String>> notes @Singular List<Pair<String, String>> notes
) { ) {
super(authorName, url, discussionMessage); super(mergeRequestName, authorName, url, discussionMessage);
this.threadId = threadId; this.threadId = threadId;
this.mrName = mrName;
this.notes = notes; this.notes = notes;
} }

View File

@ -11,24 +11,31 @@ import static dev.struchkov.bot.gitlab.context.domain.notify.task.TaskCloseNotif
*/ */
@Getter @Getter
@FieldNames @FieldNames
public class TaskCloseNotify extends TaskNotify { public class ThreadCloseNotify extends ThreadNotify {
public static final String TYPE = CLASS_NAME; public static final String TYPE = CLASS_NAME;
private final Long personTasks; private final Long personTasks;
private final Long personResolvedTasks; private final Long personResolvedTasks;
private final String authorLastNote;
private final String messageLastNote;
@Builder @Builder
protected TaskCloseNotify( protected ThreadCloseNotify(
String mergeRequestName,
String authorName, String authorName,
String url, String url,
String messageTask, String messageTask,
Long personTasks, Long personTasks,
Long personResolvedTasks Long personResolvedTasks,
String authorLastNote,
String messageLastNote
) { ) {
super(authorName, url, messageTask); super(mergeRequestName, authorName, url, messageTask);
this.personTasks = personTasks; this.personTasks = personTasks;
this.personResolvedTasks = personResolvedTasks; this.personResolvedTasks = personResolvedTasks;
this.authorLastNote = authorLastNote;
this.messageLastNote = messageLastNote;
} }
@Override @Override

View File

@ -4,17 +4,20 @@ import dev.struchkov.bot.gitlab.context.domain.notify.Notify;
import lombok.Getter; import lombok.Getter;
@Getter @Getter
public abstract class TaskNotify implements Notify { public abstract class ThreadNotify implements Notify {
protected final String mergeRequestName;
protected final String authorName; protected final String authorName;
protected final String url; protected final String url;
protected final String messageTask; protected final String messageTask;
protected TaskNotify( protected ThreadNotify(
String mergeRequestName,
String authorName, String authorName,
String url, String url,
String messageTask String messageTask
) { ) {
this.mergeRequestName = mergeRequestName;
this.authorName = authorName; this.authorName = authorName;
this.url = url; this.url = url;
this.messageTask = messageTask; this.messageTask = messageTask;

View File

@ -9,7 +9,7 @@ import dev.struchkov.bot.gitlab.context.domain.entity.Person;
import dev.struchkov.bot.gitlab.context.domain.notify.comment.NewCommentNotify; import dev.struchkov.bot.gitlab.context.domain.notify.comment.NewCommentNotify;
import dev.struchkov.bot.gitlab.context.domain.notify.level.DiscussionLevel; import dev.struchkov.bot.gitlab.context.domain.notify.level.DiscussionLevel;
import dev.struchkov.bot.gitlab.context.domain.notify.task.DiscussionNewNotify; import dev.struchkov.bot.gitlab.context.domain.notify.task.DiscussionNewNotify;
import dev.struchkov.bot.gitlab.context.domain.notify.task.TaskCloseNotify; import dev.struchkov.bot.gitlab.context.domain.notify.task.ThreadCloseNotify;
import dev.struchkov.bot.gitlab.context.repository.DiscussionRepository; import dev.struchkov.bot.gitlab.context.repository.DiscussionRepository;
import dev.struchkov.bot.gitlab.context.service.AppSettingService; import dev.struchkov.bot.gitlab.context.service.AppSettingService;
import dev.struchkov.bot.gitlab.context.service.DiscussionService; import dev.struchkov.bot.gitlab.context.service.DiscussionService;
@ -44,7 +44,9 @@ import static dev.struchkov.bot.gitlab.context.domain.notify.level.DiscussionLev
import static dev.struchkov.bot.gitlab.context.domain.notify.level.DiscussionLevel.WITHOUT_NOTIFY; import static dev.struchkov.bot.gitlab.context.domain.notify.level.DiscussionLevel.WITHOUT_NOTIFY;
import static dev.struchkov.haiti.context.exception.NotFoundException.notFoundException; import static dev.struchkov.haiti.context.exception.NotFoundException.notFoundException;
import static dev.struchkov.haiti.utils.Checker.checkNotNull; import static dev.struchkov.haiti.utils.Checker.checkNotNull;
import static dev.struchkov.haiti.utils.Checker.checkNull;
import static java.lang.Boolean.FALSE; import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
/** /**
* Сервис для работы с дискуссиями. * Сервис для работы с дискуссиями.
@ -80,7 +82,7 @@ public class DiscussionServiceImpl implements DiscussionService {
if (isNeedNotifyNewNote(discussion)) { if (isNeedNotifyNewNote(discussion)) {
notifyNewThread(discussion); notifyNewThread(discussion);
} else { } else {
notes.forEach(note -> notificationPersonal(discussion, note)); notes.forEach(note -> notifyAboutPersonalAnswer(discussion, note));
} }
} else { } else {
discussion.setNotification(false); discussion.setNotification(false);
@ -119,14 +121,14 @@ public class DiscussionServiceImpl implements DiscussionService {
} }
} }
final boolean resolved = discussion.getNotes().stream()
.allMatch(note -> note.isResolvable() && note.getResolved());
discussion.setResolved(resolved);
if (oldDiscussion.isNotification()) { if (oldDiscussion.isNotification()) {
notifyUpdateNote(oldDiscussion, discussion); notifyUpdateNote(oldDiscussion, discussion);
} }
final boolean resolved = discussion.getNotes().stream()
.allMatch(note -> note.isResolvable() && note.getResolved());
discussion.setResolved(resolved);
return repository.save(discussion); return repository.save(discussion);
} }
@ -148,7 +150,7 @@ public class DiscussionServiceImpl implements DiscussionService {
} }
private void notifyUpdateNote(Discussion oldDiscussion, Discussion discussion) { private void notifyUpdateNote(Discussion oldDiscussion, Discussion discussion) {
final Map<Long, Note> noteMap = oldDiscussion final Map<Long, Note> oldNoteMap = oldDiscussion
.getNotes().stream() .getNotes().stream()
.collect(Collectors.toMap(Note::getId, n -> n)); .collect(Collectors.toMap(Note::getId, n -> n));
@ -156,56 +158,73 @@ public class DiscussionServiceImpl implements DiscussionService {
final boolean userParticipatedInDiscussion = oldDiscussion.getNotes().stream() final boolean userParticipatedInDiscussion = oldDiscussion.getNotes().stream()
.anyMatch(note -> personInformation.getId().equals(note.getAuthor().getId())); .anyMatch(note -> personInformation.getId().equals(note.getAuthor().getId()));
final Note threadFirstNote = discussion.getFirstNote();
if (TRUE.equals(discussion.getResolved())) {
notifyAboutCloseThread(threadFirstNote, oldNoteMap.get(threadFirstNote.getId()), discussion.getLastNote());
}
for (Note newNote : discussion.getNotes()) { for (Note newNote : discussion.getNotes()) {
final Long newNoteId = newNote.getId(); final Long newNoteId = newNote.getId();
if (noteMap.containsKey(newNoteId)) { if (!oldNoteMap.containsKey(newNoteId)) {
final Note oldNote = noteMap.get(newNoteId);
if (newNote.isResolvable()) {
updateTask(newNote, oldNote);
}
} else {
if (userParticipatedInDiscussion) { if (userParticipatedInDiscussion) {
notifyNewAnswer(discussion, newNote); notifyAboutNewAnswer(discussion, newNote);
} else { } else {
notificationPersonal(discussion, newNote); notifyAboutPersonalAnswer(discussion, newNote);
} }
} }
} }
} }
private void updateTask(Note note, Note oldNote) { private void notifyAboutCloseThread(Note newNote, Note oldNote, Optional<Note> lastNote) {
if (isResolved(note, oldNote)) { final DiscussionLevel level = settingService.getLevelDiscussionNotify();
final MergeRequestForDiscussion mergeRequest = oldNote.getDiscussion().getMergeRequest(); if (!WITHOUT_NOTIFY.equals(level)) {
final List<Discussion> discussions = getAllByMergeRequestId(mergeRequest.getId())
.stream() if (isResolved(newNote, oldNote)) {
.filter(discussion -> Objects.nonNull(discussion.getResponsible())) final MergeRequestForDiscussion mergeRequest = oldNote.getDiscussion().getMergeRequest();
.toList();
final long allYouTasks = discussions.stream() final List<Discussion> discussions = getAllByMergeRequestId(mergeRequest.getId())
.filter(discussion -> personInformation.getId().equals(discussion.getFirstNote().getAuthor().getId())) .stream()
.count(); .filter(discussion -> Objects.nonNull(discussion.getResponsible()))
final long resolvedYouTask = discussions.stream() .toList();
.filter(discussion -> personInformation.getId().equals(discussion.getFirstNote().getAuthor().getId()) && discussion.getResolved()) final long allYouTasks = discussions.stream()
.count(); .filter(discussion -> personInformation.getId().equals(discussion.getFirstNote().getAuthor().getId()))
notifyService.send( .count();
TaskCloseNotify.builder() final long resolvedYouTask = discussions.stream()
.filter(discussion -> personInformation.getId().equals(discussion.getFirstNote().getAuthor().getId()) && discussion.getResolved())
.count();
final ThreadCloseNotify.ThreadCloseNotifyBuilder notifyBuilder = ThreadCloseNotify.builder()
.mergeRequestName(mergeRequest.getTitle())
.url(oldNote.getWebUrl())
.personTasks(allYouTasks)
.personResolvedTasks(resolvedYouTask);
if (NOTIFY_WITH_CONTEXT.equals(level)) {
notifyBuilder
.authorName(oldNote.getAuthor().getName()) .authorName(oldNote.getAuthor().getName())
.messageTask(oldNote.getBody()) .messageTask(oldNote.getBody());
.url(oldNote.getWebUrl())
.personTasks(allYouTasks)
.personResolvedTasks(resolvedYouTask) lastNote.ifPresent(
.build() note -> {
); notifyBuilder.authorLastNote(note.getAuthor().getName());
notifyBuilder.messageLastNote(note.getBody());
}
);
}
notifyService.send(notifyBuilder.build());
}
} }
} }
private boolean isResolved(Note note, Note oldNote) { private boolean isResolved(Note note, Note oldNote) {
return oldNote.getResolvedBy() == null return checkNull(oldNote.getResolvedBy()) // В старом комментарии не было отметки о решении
&& note.getResolvedBy() != null && checkNotNull(note.getResolvedBy()) // А в новом есть отметка
&& personInformation.getId().equals(oldNote.getAuthor().getId()) && personInformation.getId().equals(oldNote.getAuthor().getId()) // и решающий не является пользователем бота
&& !note.getResolvedBy().getId().equals(oldNote.getAuthor().getId()); && !note.getResolvedBy().getId().equals(oldNote.getAuthor().getId()); // и решающий не является автором треда
} }
@ -288,7 +307,7 @@ public class DiscussionServiceImpl implements DiscussionService {
repository.notification(enable, discussionId); repository.notification(enable, discussionId);
} }
private void notifyNewAnswer(Discussion discussion, Note note) { private void notifyAboutNewAnswer(Discussion discussion, Note note) {
final DiscussionLevel discussionLevel = settingService.getLevelDiscussionNotify(); final DiscussionLevel discussionLevel = settingService.getLevelDiscussionNotify();
if (!WITHOUT_NOTIFY.equals(discussionLevel) if (!WITHOUT_NOTIFY.equals(discussionLevel)
@ -322,7 +341,7 @@ public class DiscussionServiceImpl implements DiscussionService {
/** /**
* Уведомляет пользователя, если его никнейм упоминается в комментарии. * Уведомляет пользователя, если его никнейм упоминается в комментарии.
*/ */
private void notificationPersonal(Discussion discussion, Note note) { private void notifyAboutPersonalAnswer(Discussion discussion, Note note) {
final DiscussionLevel discussionLevel = settingService.getLevelDiscussionNotify(); final DiscussionLevel discussionLevel = settingService.getLevelDiscussionNotify();
if (!WITHOUT_NOTIFY.equals(discussionLevel)) { if (!WITHOUT_NOTIFY.equals(discussionLevel)) {
final Matcher matcher = PATTERN.matcher(note.getBody()); final Matcher matcher = PATTERN.matcher(note.getBody());
@ -372,10 +391,10 @@ public class DiscussionServiceImpl implements DiscussionService {
final Note firstNote = discussion.getFirstNote(); final Note firstNote = discussion.getFirstNote();
final MergeRequestForDiscussion mergeRequest = discussion.getMergeRequest(); final MergeRequestForDiscussion mergeRequest = discussion.getMergeRequest();
DiscussionNewNotify.DiscussionNewNotifyBuilder messageBuilder = DiscussionNewNotify.builder() final DiscussionNewNotify.DiscussionNewNotifyBuilder messageBuilder = DiscussionNewNotify.builder()
.url(firstNote.getWebUrl()) .url(firstNote.getWebUrl())
.threadId(discussion.getId()) .threadId(discussion.getId())
.mrName(mergeRequest.getTitle()) .mergeRequestName(mergeRequest.getTitle())
.authorName(firstNote.getAuthor().getName()); .authorName(firstNote.getAuthor().getName());
if (NOTIFY_WITH_CONTEXT.equals(discussionLevel)) { if (NOTIFY_WITH_CONTEXT.equals(discussionLevel)) {

View File

@ -24,26 +24,22 @@ public class NewCommentNotifyGenerator implements NotifyBoxAnswerGenerator<NewCo
@Override @Override
public BoxAnswer generate(NewCommentNotify notify) { public BoxAnswer generate(NewCommentNotify notify) {
final StringBuilder builder = new StringBuilder(Icons.COMMENT).append(" *New answer in Thread*") final StringBuilder builder = new StringBuilder(Icons.COMMENT).append(" *New answer in Thread*")
.append(Icons.HR) .append("\n-- -- -- merge request -- -- --\n")
.append(Icons.link(escapeMarkdown(notify.getMergeRequestName()), notify.getUrl())) .append(Icons.link(escapeMarkdown(notify.getMergeRequestName()), notify.getUrl()));
.append("\n");
if (checkNotNull(notify.getDiscussionMessage())) { if (checkNotNull(notify.getDiscussionMessage())) {
builder.append("\n-- -- thread first message -- --\n") builder.append("\n\n-- -- thread first message -- --\n")
.append("*").append(notify.getDiscussionAuthor()).append("*: ").append(escapeMarkdown(notify.getDiscussionMessage())) .append("*").append(notify.getDiscussionAuthor()).append("*: ").append(escapeMarkdown(notify.getDiscussionMessage()));
.append("\n");
} }
if (checkNotNull(notify.getPreviousMessage())) { if (checkNotNull(notify.getPreviousMessage())) {
builder.append("\n-- -- -- previous message -- -- --\n") builder.append("\n\n-- -- -- previous message -- -- --\n")
.append("*").append(notify.getPreviousAuthor()).append("*: ").append(escapeMarkdown(notify.getPreviousMessage())) .append("*").append(notify.getPreviousAuthor()).append("*: ").append(escapeMarkdown(notify.getPreviousMessage()));
.append("\n");
} }
if (checkNotNull(notify.getMessage())) { if (checkNotNull(notify.getMessage())) {
builder.append("\n-- -- -- --- new answer --- -- -- --\n") builder.append("\n\n-- -- -- --- new answer --- -- -- --\n")
.append("*").append(notify.getAuthorName()).append("*: ").append(escapeMarkdown(notify.getMessage())) .append("*").append(notify.getAuthorName()).append("*: ").append(escapeMarkdown(notify.getMessage()));
.append("\n");
} }
final String messageNotify = builder.toString(); final String messageNotify = builder.toString();

View File

@ -26,14 +26,14 @@ public class NewThreadNotifyGenerator implements NotifyBoxAnswerGenerator<Discus
@Override @Override
public BoxAnswer generate(DiscussionNewNotify notify) { public BoxAnswer generate(DiscussionNewNotify notify) {
final StringBuilder builder = new StringBuilder(Icons.THREAD).append(" *New Thread in your MR*") final StringBuilder builder = new StringBuilder(Icons.THREAD).append(" *New Thread in your MR*")
.append(Icons.HR) .append("\n -- -- -- merge request -- -- --\n")
.append(Icons.link(escapeMarkdown(notify.getMrName()), notify.getUrl())) .append(Icons.link(escapeMarkdown(notify.getMergeRequestName()), notify.getUrl()))
.append(Icons.HR) .append("\n\n -- -- -- thread message -- -- --\n")
.append("*").append(notify.getAuthorName()).append("*: ").append(escapeMarkdown(notify.getMessageTask())); .append("*").append(notify.getAuthorName()).append("*: ").append(escapeMarkdown(notify.getMessageTask()));
final List<Pair<String, String>> notes = notify.getNotes(); final List<Pair<String, String>> notes = notify.getNotes();
if (checkNotEmpty(notes)) { if (checkNotEmpty(notes)) {
builder.append("\n-- -- -- -- comments -- -- -- --\n") builder.append("\n\n-- -- -- -- comments -- -- -- --\n")
.append(convertNotes(notes)); .append(convertNotes(notes));
} }

View File

@ -1,31 +1,55 @@
package dev.struchkov.bot.gitlab.telegram.service.notify; package dev.struchkov.bot.gitlab.telegram.service.notify;
import dev.struchkov.bot.gitlab.context.domain.notify.task.TaskCloseNotify; import dev.struchkov.bot.gitlab.context.domain.notify.task.ThreadCloseNotify;
import dev.struchkov.bot.gitlab.context.utils.Icons; import dev.struchkov.bot.gitlab.context.utils.Icons;
import dev.struchkov.godfather.simple.domain.BoxAnswer; import dev.struchkov.godfather.simple.domain.BoxAnswer;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import static dev.struchkov.bot.gitlab.context.utils.Icons.link; import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.DELETE_MESSAGE;
import static dev.struchkov.godfather.main.domain.keyboard.button.SimpleButton.simpleButton;
import static dev.struchkov.godfather.simple.domain.BoxAnswer.boxAnswer; import static dev.struchkov.godfather.simple.domain.BoxAnswer.boxAnswer;
import static dev.struchkov.godfather.telegram.domain.keyboard.InlineKeyBoard.inlineKeyBoard;
import static dev.struchkov.godfather.telegram.domain.keyboard.button.UrlButton.urlButton;
import static dev.struchkov.haiti.utils.Checker.checkNotBlank;
import static dev.struchkov.haiti.utils.Strings.escapeMarkdown; import static dev.struchkov.haiti.utils.Strings.escapeMarkdown;
@Component @Component
public class ThreadCloseNotifyGenerate implements NotifyBoxAnswerGenerator<TaskCloseNotify> { public class ThreadCloseNotifyGenerate implements NotifyBoxAnswerGenerator<ThreadCloseNotify> {
@Override @Override
public BoxAnswer generate(TaskCloseNotify notify) { public BoxAnswer generate(ThreadCloseNotify notify) {
final StringBuilder builder = new StringBuilder(Icons.THREAD).append(" *Closed ").append(link("task", notify.getUrl())) final StringBuilder builder = new StringBuilder(Icons.THREAD).append(" *Closed thread*")
.append(Icons.HR) .append("\n-- -- -- merge request -- -- --\n")
.append("*").append(notify.getAuthorName()).append("*: ").append(escapeMarkdown(notify.getMessageTask())); .append(escapeMarkdown(notify.getMergeRequestName()))
.append("\n");
if (checkNotBlank(notify.getAuthorName())) {
builder
.append("\n-- -- -- thread message -- -- --\n")
.append("*").append(escapeMarkdown(notify.getAuthorName())).append("*: ").append(escapeMarkdown(notify.getMessageTask()))
.append("\n");
}
if (checkNotBlank(notify.getAuthorLastNote())) {
builder
.append("\n-- -- -- last message -- -- -- --\n")
.append("*").append(escapeMarkdown(notify.getAuthorLastNote())).append("*: ").append(escapeMarkdown(notify.getMessageLastNote()));
}
final String notifyMessage = builder.toString(); final String notifyMessage = builder.toString();
return boxAnswer(notifyMessage); return boxAnswer(
notifyMessage,
inlineKeyBoard(
simpleButton(Icons.VIEW, DELETE_MESSAGE),
urlButton(Icons.LINK, notify.getUrl())
)
);
} }
@Override @Override
public String getNotifyType() { public String getNotifyType() {
return TaskCloseNotify.TYPE; return ThreadCloseNotify.TYPE;
} }
} }