-
Findet in allen Gruppen unserer Datenbank neue Artikel, die nach der vorherigen Ausführung veröffentlicht wurden.
Dieses Schema legt eine kleinere Anzahl von Gruppen fest – nur solche mit aktiven Benutzern. Damals schien es mir logisch, aber jetzt verstehe ich, dass Sie den neuesten Artikel, den der Bot verarbeitet hat, immer auf dem neuesten Stand halten müssen, unabhängig davon, ob aktive Benutzer eine bestimmte Gruppe abonniert haben oder nicht. Es kann vorkommen, dass ein neuer Benutzer sofort die gesamte Anzahl der seit der Deaktivierung dieser Gruppe veröffentlichten Artikel erhält. Dies ist kein erwartetes Verhalten, und um es zu vermeiden, müssen wir die Gruppen aus unserer Datenbank, die derzeit keine aktiven Benutzer haben, auf dem neuesten Stand halten. -
Wenn es neue Artikel gibt, generieren Sie Nachrichten für alle Benutzer, die diese Gruppe aktiv abonniert haben. Wenn es keine neuen Artikel gibt, schließen wir die Arbeit einfach ab.
FindNewArticleService:
package com.github.javarushcommunity.jrtb.service;
/**
* Service for finding new articles.
*/
public interface FindNewArticleService {
/**
* Find new articles and notify subscribers about it.
*/
void findNewArticles();
}
Ganz einfach, oder? Das ist das Wesentliche, und die ganze Schwierigkeit wird in der Umsetzung liegen:
package com.github.javarushcommunity.jrtb.service;
import com.github.javarushcommunity.jrtb.javarushclient.JavaRushPostClient;
import com.github.javarushcommunity.jrtb.javarushclient.dto.PostInfo;
import com.github.javarushcommunity.jrtb.repository.entity.GroupSub;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class FindNewArticleServiceImpl implements FindNewArticleService {
public static final String JAVARUSH_WEB_POST_FORMAT = "https://javarush.com/groups/posts/%s";
private final GroupSubService groupSubService;
private final JavaRushPostClient javaRushPostClient;
private final SendBotMessageService sendMessageService;
@Autowired
public FindNewArticleServiceImpl(GroupSubService groupSubService,
JavaRushPostClient javaRushPostClient,
SendBotMessageService sendMessageService) {
this.groupSubService = groupSubService;
this.javaRushPostClient = javaRushPostClient;
this.sendMessageService = sendMessageService;
}
@Override
public void findNewArticles() {
groupSubService.findAll().forEach(gSub -> {
List<PostInfo> newPosts = javaRushPostClient.findNewPosts(gSub.getId(), gSub.getLastArticleId());
setNewLastArticleId(gSub, newPosts);
notifySubscribersAboutNewArticles(gSub, newPosts);
});
}
private void notifySubscribersAboutNewArticles(GroupSub gSub, List<PostInfo> newPosts) {
Collections.reverse(newPosts);
List<String> messagesWithNewArticles = newPosts.stream()
.map(post -> String.format("✨Вышла новая статья <b>%s</b> в группе <b>%s</b>.✨\n\n" +
"<b>Описание:</b> %s\n\n" +
"<b>Ссылка:</b> %s\n",
post.getTitle(), gSub.getTitle(), post.getDescription(), getPostUrl(post.getKey())))
.collect(Collectors.toList());
gSub.getUsers().stream()
.filter(TelegramUser::isActive)
.forEach(it -> sendMessageService.sendMessage(it.getChatId(), messagesWithNewArticles));
}
private void setNewLastArticleId(GroupSub gSub, List<PostInfo> newPosts) {
newPosts.stream().mapToInt(PostInfo::getId).max()
.ifPresent(id -> {
gSub.setLastArticleId(id);
groupSubService.save(gSub);
});
}
private String getPostUrl(String key) {
return String.format(JAVARUSH_WEB_POST_FORMAT, key);
}
}
Hier werden wir alles der Reihe nach behandeln:
-
Mit groupService finden wir alle Gruppen, die in der Datenbank vorhanden sind.
-
Dann verteilen wir uns auf alle Gruppen und rufen für jede den im letzten Artikel erstellten Client auf - javaRushPostClient.findNewPosts .
-
Als nächstes aktualisieren wir mithilfe der setNewArticleId- Methode die Artikel-ID unseres letzten neuen Artikels, sodass unsere Datenbank weiß, dass wir bereits neue Artikel verarbeitet haben.
-
Und indem wir die Tatsache nutzen, dass GroupSub über eine Sammlung von Benutzern verfügt, gehen wir die aktiven durch und senden Benachrichtigungen über neue Artikel.
Erstellen Sie FindNewArticleJob
Wir haben bereits darüber gesprochen, was SpringScheduler ist, aber wiederholen wir es kurz: Es handelt sich um einen Mechanismus im Spring-Framework zum Erstellen eines Hintergrundprozesses, der zu einem bestimmten, von uns festgelegten Zeitpunkt ausgeführt wird. Was brauchen Sie dafür? Der erste Schritt besteht darin, die Annotation @EnableScheduling zu unserer Spring-Eingabeklasse hinzuzufügen:package com.github.javarushcommunity.jrtb;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@EnableScheduling
@SpringBootApplication
public class JavarushTelegramBotApplication {
public static void main(String[] args) {
SpringApplication.run(JavarushTelegramBotApplication.class, args);
}
}
Der zweite Schritt besteht darin, eine Klasse zu erstellen, sie zum ApplicationContext hinzuzufügen und darin eine Methode zu erstellen, die regelmäßig ausgeführt wird. Wir erstellen ein Jobpaket auf derselben Ebene wie Repository, Service usw. und erstellen dort die Klasse FindNewArticleJob :
package com.github.javarushcommunity.jrtb.job;
import com.github.javarushcommunity.jrtb.service.FindNewArticleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
/**
* Job for finding new articles.
*/
@Slf4j
@Component
public class FindNewArticlesJob {
private final FindNewArticleService findNewArticleService;
@Autowired
public FindNewArticlesJob(FindNewArticleService findNewArticleService) {
this.findNewArticleService = findNewArticleService;
}
@Scheduled(fixedRateString = "${bot.recountNewArticleFixedRate}")
public void findNewArticles() {
LocalDateTime start = LocalDateTime.now();
log.info("Find new article job started.");
findNewArticleService.findNewArticles();
LocalDateTime end = LocalDateTime.now();
log.info("Find new articles job finished. Took seconds: {}",
end.toEpochSecond(ZoneOffset.UTC) - start.toEpochSecond(ZoneOffset.UTC));
}
}
Um diese Klasse zum Anwendungskontext hinzuzufügen, habe ich die Annotation @Component verwendet . Und damit die Methode innerhalb der Klasse weiß, dass sie regelmäßig ausgeführt werden muss, habe ich der Methode eine Anmerkung hinzugefügt: @Scheduled(fixedRateString = "${bot.recountNewArticleFixedRate}") . Aber wir legen es in der Datei application.properties fest:
bot.recountNewArticleFixedRate = 900000
Hier ist der Wert in Millisekunden angegeben. Es wird 15 Minuten dauern. Bei dieser Methode ist alles einfach: Ich habe in den Protokollen eine supereinfache Metrik für mich hinzugefügt, um die Suche nach neuen Artikeln zu berechnen, um zumindest grob zu verstehen, wie schnell es funktioniert.
Testen neuer Funktionen
Jetzt werden wir es mit unserem Testbot testen. Aber wie? Ich werde nicht jedes Mal Artikel löschen, um anzuzeigen, dass Benachrichtigungen eingetroffen sind? Nein, natürlich. Wir bearbeiten einfach die Daten in der Datenbank und starten die Anwendung. Ich werde es auf meinem Testserver testen. Um dies zu tun, abonnieren wir eine Gruppe. Wenn das Abonnement abgeschlossen ist, erhält die Gruppe die aktuelle ID des neuesten Artikels. Gehen wir zur Datenbank und ändern den Wert zwei Artikel zurück. Daher gehen wir davon aus, dass es so viele Artikel geben wird, wie wir lastArticleId auf früher gesetzt haben . Als nächstes gehen wir zur Site, sortieren die Artikel in der Gruppe „Java-Projekte“ – neue zuerst – und gehen zum dritten Artikel aus der Liste: Gehen wir zum unteren Artikel und aus der Adressleiste erhalten wir die Artikel-ID – 3313: Weiter , gehen Sie zu MySQL Workbench und ändern Sie den lastArticleId- Wert auf 3313. Sehen wir uns an, dass sich eine solche Gruppe in der Datenbank befindet: Und dafür führen wir den Befehl aus: Und das war’s, jetzt müssen Sie bis zum nächsten Start des Jobs warten Suche nach neuen Artikeln. Wir erwarten zwei Nachrichten zu einem neuen Artikel von der Java-Projektgruppe. Wie es heißt, ließ das Ergebnis nicht lange auf sich warten: Es stellte sich heraus, dass der Bot wie erwartet funktionierte.Ende
Wie immer aktualisieren wir die Version in pom.xml und fügen einen Eintrag zu RELEASE_NOTES hinzu, damit der Arbeitsverlauf gespeichert wird und Sie jederzeit zurückgehen und nachvollziehen können, was sich geändert hat. Daher erhöhen wir die Version um eine Einheit:<version>0.7.0-SNAPSHOT</version>
Und aktualisieren Sie RELEASE_NOTES:
GO TO FULL VERSION