JavaRush /Java-Blog /Random-DE /Wir fügen die Möglichkeit hinzu, eine Gruppe von Artikeln...

Wir fügen die Möglichkeit hinzu, eine Gruppe von Artikeln zu abonnieren. (Teil 2) - „Java-Projekt von A bis Z“

Veröffentlicht in der Gruppe Random-DE
Hallo zusammen! Wir arbeiten weiter an der Aufgabe, die wir letzte Woche begonnen haben .„Java-Projekt von A bis Z“: Hinzufügen der Möglichkeit, eine Gruppe von Artikeln zu abonnieren.  Teil 2 - 1

Wir implementieren JRTB-5

Jetzt müssen wir einen Befehl hinzufügen, damit wir einige Artikelgruppen von JavaRush abonnieren können. Wie kann man das machen? Wir werden dem einfachsten Szenario folgen, das ich mir ausgedacht habe. Da wir Zugriff über die Gruppen-ID haben, müssen wir diese vom Benutzer übertragen. Dazu gibt der Benutzer den Befehl /addGroupSub GROUP_ID ein, der auf zwei Arten funktioniert: Wenn nur der Befehl selbst kommt: /addGroupSub , wird als Antwort die Liste aller Gruppen und ihrer IDs gesendet. Dann kann der Benutzer die von ihm benötigte Gruppen-ID auswählen und die zweite Version der Anfrage mit diesem Befehl erstellen: /addGroupSub GROUP_ID – und dann wird es einen Datensatz dieser Gruppe mit diesem Benutzer geben. Ich denke, wir können es in Zukunft besser machen. Unser Ziel ist es, die Entwicklung zu zeigen und nicht das supercoole Benutzererlebnis (ich schäme mich, das zu sagen, aber ich kenne den russischen Begriff nicht, der das bedeuten würde). Um die Funktionalität, die sich durch die gesamte Anwendung zieht (in unserem Fall vom Telegram-Bot-Client bis zur Datenbank), ordnungsgemäß hinzuzufügen, müssen Sie an einem Ende beginnen. Wir werden dies von der Datenbankseite aus tun.

Hinzufügen einer neuen Migration zur Datenbank

Als Erstes müssen Sie eine neue Datenbankmigration und die Möglichkeit hinzufügen, Benutzergruppenabonnementdaten in JR zu speichern. Um sich daran zu erinnern, wie es sein sollte, kehren Sie zum Artikel „ Projektplanung: Sieben Mal messen “ zurück. Auf dem zweiten Foto gibt es ein ungefähres Diagramm der Datenbank. Wir müssen eine Tabelle hinzufügen, um Gruppeninformationen zu speichern:
  • Die Gruppen-ID in JavaRush ist auch unsere ID. Wir vertrauen ihnen und glauben, dass diese IDs einzigartig sind;
  • Titel – auf unseren Bildern war es Name – der informelle Name der Gruppe; das heißt, was wir auf der JavaRush-Website sehen;
  • last_article_id – und das ist ein interessantes Feld. In dieser Gruppe wird die letzte ID des Artikels gespeichert, die der Bot bereits an seine Abonnenten gesendet hat. Mit diesem Feld funktioniert der Mechanismus zur Suche nach neuen Artikeln. Neue Abonnenten erhalten keine Artikel, die vor dem Abonnieren des Benutzers veröffentlicht wurden, sondern nur diejenigen, die nach dem Abonnieren der Gruppe veröffentlicht wurden.
Wir werden auch eine Viele-zu-Viele-Beziehung zwischen der Gruppen- und der Benutzertabelle haben, da jeder Benutzer viele Gruppenabonnements haben kann (eins-zu-viele) und jedes Gruppenabonnement viele Benutzer haben kann (nur eins-zu-viele). auf der anderen Seite). Es stellt sich heraus, dass dies unser Viele-zu-Viele sein wird. Wenn Sie Fragen haben, lesen Sie die Artikel in der Datenbank. Ja, ich habe demnächst vor, einen Beitrag auf dem Telegram-Kanal zu erstellen, in dem ich alle Artikel der Datenbank zusammenstellen werde. So wird unsere zweite Datenbankmigration aussehen.
V00002__created_groupsub_many_to_many.sql:

-- add PRIMARY KEY FOR tg_user
ALTER TABLE tg_user ADD PRIMARY KEY (chat_id);

-- ensure that the tables with these names are removed before creating a new one.
DROP TABLE IF EXISTS group_sub;
DROP TABLE IF EXISTS group_x_user;

CREATE TABLE group_sub (
   id INT,
   title VARCHAR(100),
   last_article_id INT,
   PRIMARY KEY (id)
);

CREATE TABLE group_x_user (
   group_sub_id INT NOT NULL,
   user_id VARCHAR(100) NOT NULL,
   FOREIGN KEY (user_id) REFERENCES tg_user(chat_id),
   FOREIGN KEY (group_sub_id) REFERENCES group_sub(id),
   UNIQUE(user_id, group_sub_id)
);
Es ist wichtig zu beachten, dass ich zuerst die alte Tabelle ändere – ich füge ihr einen Primärschlüssel hinzu. Irgendwie habe ich das damals übersehen, aber jetzt gab mir MySQL nicht die Möglichkeit, einen AUSLÄNDISCHEN SCHLÜSSEL für die Tabelle gorup_x_user hinzuzufügen, und im Rahmen dieser Migration habe ich die Datenbank aktualisiert. Bitte beachten Sie einen wichtigen Aspekt. Das Ändern der Datenbank sollte genau auf diese Weise erfolgen – alles, was benötigt wird, ist in der neuen Migration enthalten, jedoch nicht durch die Aktualisierung einer bereits veröffentlichten Migration. Ja, in unserem Fall würde nichts passieren, da es sich um ein Testprojekt handelt und wir wissen, dass es nur an einer Stelle eingesetzt wird, aber das wäre der falsche Ansatz. Aber wir wollen, dass alles stimmt. Als nächstes müssen Sie Tabellen löschen, bevor Sie sie erstellen. Warum ist das? Wenn also zufällig Tabellen mit solchen Namen in der Datenbank vorhanden wären, würde die Migration nicht fehlschlagen und genau wie erwartet funktionieren. Und dann fügen wir zwei Tabellen hinzu. Alles war so, wie wir es wollten. Jetzt müssen wir unsere Anwendung starten. Wenn alles startet und nicht kaputt geht, wird die Migration aufgezeichnet. Und um dies noch einmal zu überprüfen, gehen wir in die Datenbank, um sicherzustellen, dass: a) solche Tabellen erschienen sind; b) Es gibt einen neuen Eintrag in der technischen Flyway-Tabelle. Damit ist die Migrationsarbeit abgeschlossen. Fahren wir mit den Repositorys fort.

Hinzufügen einer Repository-Ebene

Dank Spring Boot Data ist hier alles sehr einfach: Wir müssen die GroupSub-Entität hinzufügen, TelegramUser leicht aktualisieren und ein fast leeres GroupSubRepository hinzufügen: Wir fügen die GroupSub-Entität demselben Paket wie TelegramUser hinzu:
package com.github.javarushcommunity.jrtb.repository.entity;

import lombok.Data;
import lombok.EqualsAndHashCode;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

import static java.util.Objects.isNull;

@Data
@Entity
@Table(name = "group_sub")
@EqualsAndHashCode
public class GroupSub {

   @Id
   private Integer id;

   @Column(name = "title")
   private String title;

   @Column(name = "last_article_id")
   private Integer lastArticleId;

   @ManyToMany(fetch = FetchType.EAGER)
   @JoinTable(
           name = "group_x_user",
           joinColumns = @JoinColumn(name = "group_sub_id"),
           inverseJoinColumns = @JoinColumn(name = "user_id")
   )
   private List<TelegramUser> users;

   public void addUser(TelegramUser telegramUser) {
       if (isNull(users)) {
           users = new ArrayList<>();
       }
       users.add(telegramUser);
   }
}
Erwähnenswert ist, dass wir über ein zusätzliches Benutzerfeld verfügen, das eine Sammlung aller Benutzer enthält, die der Gruppe beigetreten sind. Und zwei Annotationen – ManyToMany und JoinTable – sind genau das, was wir dafür brauchen. Das gleiche Feld muss für TelegramUser hinzugefügt werden:
@ManyToMany(mappedBy = "users", fetch = FetchType.EAGER)
private List<GroupSub> groupSubs;
Dieses Feld verwendet in der GroupSub-Entität geschriebene Joins. Und tatsächlich ist unsere Repository-Klasse für GroupSub GroupSubRepository :
package com.github.javarushcommunity.jrtb.repository;

import com.github.javarushcommunity.jrtb.repository.entity.GroupSub;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

/**
* {@link Repository} for {@link GroupSub} entity.
*/
@Repository
public interface GroupSubRepository extends JpaRepository<GroupSub, Integer> {
}
Zu diesem Zeitpunkt benötigen wir keine zusätzlichen Methoden: Die im JpaRepository-Vorfahren implementierten Methoden reichen uns aus. Schreiben wir einen Test in TelegramUserRepositoryIT, der prüft, ob unsere Many-to-Many-Methode funktioniert. Die Idee des Tests besteht darin, dass wir über ein SQL-Skript 5 Gruppen von Abonnements pro Benutzer zur Datenbank hinzufügen, diesen Benutzer anhand seiner ID ermitteln und überprüfen, ob wir genau diese Gruppen und mit genau denselben Werten erhalten haben. Wie kann man das machen? Sie können einen Zähler in die Daten einbetten, den wir dann durchgehen und überprüfen können. Hier ist das Skript fiveGroupSubsForUser.sql:
INSERT INTO tg_user VALUES (1, 1);

INSERT INTO group_sub VALUES
(1, 'g1', 1),
(2, 'g2', 2),
(3, 'g3', 3),
(4, 'g4', 4),
(5, 'g5', 5);

INSERT INTO group_x_user VALUES
(1, 1),
(2, 1),
(3, 1),
(4, 1),
(5, 1);
Und der Test selbst:
@Sql(scripts = {"/sql/clearDbs.sql", "/sql/fiveGroupSubsForUser.sql"})
@Test
public void shouldProperlyGetAllGroupSubsForUser() {
   //when
   Optional<TelegramUser> userFromDB = telegramUserRepository.findById("1");

   //then
   Assertions.assertTrue(userFromDB.isPresent());
   List<GroupSub> groupSubs = userFromDB.get().getGroupSubs();
   for (int i = 0; i < groupSubs.size(); i++) {
       Assertions.assertEquals(String.format("g%s", (i + 1)), groupSubs.get(i).getTitle());
       Assertions.assertEquals(i + 1, groupSubs.get(i).getId());
       Assertions.assertEquals(i + 1, groupSubs.get(i).getLastArticleId());
   }
}
Fügen wir nun einen Test mit derselben Bedeutung für die GroupSub-Entität hinzu. Dazu erstellen wir eine Testklasse „groupSubRepositoryIT“ im selben Paket wie „groupSubRepositoryIT“ :
package com.github.javarushcommunity.jrtb.repository;

import com.github.javarushcommunity.jrtb.repository.entity.GroupSub;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.jdbc.Sql;

import java.util.List;
import java.util.Optional;

import static org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace.NONE;

/**
* Integration-level testing for {@link GroupSubRepository}.
*/
@ActiveProfiles("test")
@DataJpaTest
@AutoConfigureTestDatabase(replace = NONE)
public class GroupSubRepositoryIT {

   @Autowired
   private GroupSubRepository groupSubRepository;

   @Sql(scripts = {"/sql/clearDbs.sql", "/sql/fiveUsersForGroupSub.sql"})
   @Test
   public void shouldProperlyGetAllUsersForGroupSub() {
       //when
       Optional<GroupSub> groupSubFromDB = groupSubRepository.findById(1);

       //then
       Assertions.assertTrue(groupSubFromDB.isPresent());
       Assertions.assertEquals(1, groupSubFromDB.get().getId());
       List<TelegramUser> users = groupSubFromDB.get().getUsers();
       for(int i=0; i<users.size(); i++) {
           Assertions.assertEquals(String.valueOf(i + 1), users.get(i).getChatId());
           Assertions.assertTrue(users.get(i).isActive());
       }
   }
}
Und das fehlende Skript fiveUsersForGroupSub.sql:
INSERT INTO tg_user VALUES
(1, 1),
(2, 1),
(3, 1),
(4, 1),
(5, 1);

INSERT INTO group_sub VALUES (1, 'g1', 1);

INSERT INTO group_x_user VALUES
(1, 1),
(1, 2),
(1, 3),
(1, 4),
(1, 5);
An diesem Punkt kann ein Teil der Arbeit mit dem Repository als abgeschlossen betrachtet werden. Schreiben wir nun eine Serviceschicht.

Wir schreiben GroupSubService

Um mit Gruppen von Abonnements zu arbeiten, müssen wir diese in dieser Phase nur speichern können, also kein Problem: Wir erstellen den GroupSubService-Dienst und seine Implementierung von GroupSubServiceImpl in einem Paket, das andere Dienste enthält – Dienst:
package com.github.javarushcommunity.jrtb.service;

import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupDiscussionInfo;
import com.github.javarushcommunity.jrtb.repository.entity.GroupSub;

/**
* Service for manipulating with {@link GroupSub}.
*/
public interface GroupSubService {

   GroupSub save(String chatId, GroupDiscussionInfo groupDiscussionInfo);
}
Und seine Umsetzung:
package com.github.javarushcommunity.jrtb.service;

import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupDiscussionInfo;
import com.github.javarushcommunity.jrtb.repository.GroupSubRepository;
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 javax.ws.rs.NotFoundException;
import java.util.Optional;

@Service
public class GroupSubServiceImpl implements GroupSubService {

   private final GroupSubRepository groupSubRepository;
   private final TelegramUserService telegramUserService;

   @Autowired
   public GroupSubServiceImpl(GroupSubRepository groupSubRepository, TelegramUserService telegramUserService) {
       this.groupSubRepository = groupSubRepository;
       this.telegramUserService = telegramUserService;
   }

   @Override
   public GroupSub save(String chatId, GroupDiscussionInfo groupDiscussionInfo) {
       TelegramUser telegramUser = telegramUserService.findByChatId(chatId).orElseThrow(NotFoundException::new);
       //TODO add exception handling
       GroupSub groupSub;
       Optional<GroupSub> groupSubFromDB = groupSubRepository.findById(groupDiscussionInfo.getId());
       if(groupSubFromDB.isPresent()) {
           groupSub = groupSubFromDB.get();
           Optional<TelegramUser> first = groupSub.getUsers().stream()
                   .filter(it -> it.getChatId().equalsIgnoreCase(chatId))
                   .findFirst();
           if(first.isEmpty()) {
               groupSub.addUser(telegramUser);
           }
       } else {
           groupSub = new GroupSub();
           groupSub.addUser(telegramUser);
           groupSub.setId(groupDiscussionInfo.getId());
           groupSub.setTitle(groupDiscussionInfo.getTitle());
       }
       return groupSubRepository.save(groupSub);
   }
}
Damit Spring Data ordnungsgemäß funktioniert und ein Viele-zu-Viele-Datensatz erstellt werden kann, müssen wir den Benutzer aus unserer Datenbank für die Abonnementgruppe, die wir erstellen, abrufen und ihn dem GroupSub-Objekt hinzufügen. Wenn wir also dieses Abonnement zum Speichern übertragen, wird auch eine Verbindung über die Tabelle „group_x_user“ erstellt. Es kann vorkommen, dass eine solche Abonnementgruppe bereits erstellt wurde und Sie ihr nur noch einen weiteren Benutzer hinzufügen müssen. Dazu holen wir uns zunächst die Gruppen-ID aus der Datenbank, und wenn es einen Datensatz gibt, arbeiten wir damit, wenn nicht, erstellen wir einen neuen. Es ist wichtig zu beachten, dass wir für die Arbeit mit TelegramUser TelegramUserService verwenden, um dem letzten der SOLID-Prinzipien zu folgen. Wenn wir derzeit keinen Datensatz anhand der ID finden, löse ich einfach eine Ausnahme aus. Es wird derzeit in keiner Weise verarbeitet: Wir werden dies ganz am Ende tun, vor dem MVP. Schreiben wir zwei Komponententests für die GroupSubServiceTest- Klasse . Welche brauchen wir? Ich möchte sicherstellen, dass die Speichermethode im GroupSubRepository aufgerufen wird und eine Entität mit einem einzelnen Benutzer an GroupSub übergeben wird – diejenige, die TelegramUserService unter Verwendung der bereitgestellten ID an uns zurückgibt. Und die zweite Option: Wenn sich bereits eine Gruppe mit derselben ID in der Datenbank befindet und diese Gruppe bereits einen Benutzer hat, müssen Sie überprüfen, ob ein weiterer Benutzer zu dieser Gruppe hinzugefügt und dieses Objekt gespeichert wird. Hier ist die Implementierung:
package com.github.javarushcommunity.jrtb.service;

import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupDiscussionInfo;
import com.github.javarushcommunity.jrtb.repository.GroupSubRepository;
import com.github.javarushcommunity.jrtb.repository.entity.GroupSub;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import java.util.Optional;

@DisplayName("Unit-level testing for GroupSubService")
public class GroupSubServiceTest {

   private GroupSubService groupSubService;
   private GroupSubRepository groupSubRepository;
   private TelegramUser newUser;

   private final static String CHAT_ID = "1";

   @BeforeEach
   public void init() {
       TelegramUserService telegramUserService = Mockito.mock(TelegramUserService.class);
       groupSubRepository = Mockito.mock(GroupSubRepository.class);
       groupSubService = new GroupSubServiceImpl(groupSubRepository, telegramUserService);

       newUser = new TelegramUser();
       newUser.setActive(true);
       newUser.setChatId(CHAT_ID);

       Mockito.when(telegramUserService.findByChatId(CHAT_ID)).thenReturn(Optional.of(newUser));
   }

   @Test
   public void shouldProperlySaveGroup() {
       //given

       GroupDiscussionInfo groupDiscussionInfo = new GroupDiscussionInfo();
       groupDiscussionInfo.setId(1);
       groupDiscussionInfo.setTitle("g1");

       GroupSub expectedGroupSub = new GroupSub();
       expectedGroupSub.setId(groupDiscussionInfo.getId());
       expectedGroupSub.setTitle(groupDiscussionInfo.getTitle());
       expectedGroupSub.addUser(newUser);

       //when
       groupSubService.save(CHAT_ID, groupDiscussionInfo);

       //then
       Mockito.verify(groupSubRepository).save(expectedGroupSub);
   }

   @Test
   public void shouldProperlyAddUserToExistingGroup() {
       //given
       TelegramUser oldTelegramUser = new TelegramUser();
       oldTelegramUser.setChatId("2");
       oldTelegramUser.setActive(true);

       GroupDiscussionInfo groupDiscussionInfo = new GroupDiscussionInfo();
       groupDiscussionInfo.setId(1);
       groupDiscussionInfo.setTitle("g1");

       GroupSub groupFromDB = new GroupSub();
       groupFromDB.setId(groupDiscussionInfo.getId());
       groupFromDB.setTitle(groupDiscussionInfo.getTitle());
       groupFromDB.addUser(oldTelegramUser);

       Mockito.when(groupSubRepository.findById(groupDiscussionInfo.getId())).thenReturn(Optional.of(groupFromDB));

       GroupSub expectedGroupSub = new GroupSub();
       expectedGroupSub.setId(groupDiscussionInfo.getId());
       expectedGroupSub.setTitle(groupDiscussionInfo.getTitle());
       expectedGroupSub.addUser(oldTelegramUser);
       expectedGroupSub.addUser(newUser);

       //when
       groupSubService.save(CHAT_ID, groupDiscussionInfo);

       //then
       Mockito.verify(groupSubRepository).findById(groupDiscussionInfo.getId());
       Mockito.verify(groupSubRepository).save(expectedGroupSub);
   }

}
Ich habe auch die init() -Methode mit der BeforeEach-Annotation hinzugefügt. Auf diese Weise erstellen sie normalerweise eine Methode, die vor jedem Testlauf ausgeführt wird, und es ist möglich, für alle Tests eine gemeinsame Logik darin einzubauen. In unserem Fall müssen wir TelegramUserService für alle Tests dieser Klasse auf die gleiche Weise sperren, daher ist es sinnvoll, diese Logik auf eine gemeinsame Methode zu übertragen. Hier werden zwei Mokito-Designs verwendet:
  • Mockito.when(o1.m1(a1)).thenReturn(o2) – darin sagen wir, dass die Methode das Objekt o2 zurückgibt, wenn die Methode m1 für das Objekt o1 mit dem Argument a1 aufgerufen wird . Dies ist fast die wichtigste Funktionalität von Mockito – das Mock-Objekt zu zwingen, genau das zurückzugeben, was wir brauchen;

  • Mockito.verify(o1).m1(a1) – überprüft, ob die Methode m1 für das Objekt o1 mit dem Argument a1 aufgerufen wurde . Es war natürlich möglich, das zurückgegebene Objekt der Methode save zu verwenden, aber ich habe beschlossen, es etwas komplizierter zu machen, indem ich eine andere mögliche Methode gezeigt habe. Wann kann es nützlich sein? In Fällen, in denen Methoden von Scheinklassen „void“ zurückgeben. Dann wird es ohne Mockito.verify keine Arbeit geben)))

Wir halten weiterhin an der Idee fest, dass Tests geschrieben werden müssen, und viele davon müssen geschrieben werden. Der nächste Schritt ist die Zusammenarbeit mit dem Telegram-Bot-Team.

Erstellen Sie den Befehl /addGroupSub

Hier müssen wir die folgende Logik ausführen: Wenn wir nur einen Befehl ohne Kontext erhalten, helfen wir dem Benutzer und geben ihm eine Liste aller Gruppen mit ihren IDs, damit er die notwendigen Informationen an den Bot weitergeben kann. Und wenn der Benutzer einen Befehl mit anderen Wörtern an den Bot sendet, suchen Sie eine Gruppe mit dieser ID oder schreiben Sie, dass es keine solche Gruppe gibt. Fügen wir unserem E-Namen einen neuen Wert hinzu – CommandName:
ADD_GROUP_SUB("/addgroupsub")
Gehen wir weiter von der Datenbank zum Telegram-Bot – erstellen Sie die AddGroupSubCommand- Klasse im Befehlspaket:
package com.github.javarushcommunity.jrtb.command;

import com.github.javarushcommunity.jrtb.javarushclient.JavaRushGroupClient;
import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupDiscussionInfo;
import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupRequestArgs;
import com.github.javarushcommunity.jrtb.repository.entity.GroupSub;
import com.github.javarushcommunity.jrtb.service.GroupSubService;
import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import org.telegram.telegrambots.meta.api.objects.Update;

import java.util.stream.Collectors;

import static com.github.javarushcommunity.jrtb.command.CommandName.ADD_GROUP_SUB;
import static com.github.javarushcommunity.jrtb.command.CommandUtils.getChatId;
import static com.github.javarushcommunity.jrtb.command.CommandUtils.getMessage;
import static java.util.Objects.isNull;
import static org.apache.commons.lang3.StringUtils.SPACE;
import static org.apache.commons.lang3.StringUtils.isNumeric;

/**
* Add Group subscription {@link Command}.
*/
public class AddGroupSubCommand implements Command {

   private final SendBotMessageService sendBotMessageService;
   private final JavaRushGroupClient javaRushGroupClient;
   private final GroupSubService groupSubService;

   public AddGroupSubCommand(SendBotMessageService sendBotMessageService, JavaRushGroupClient javaRushGroupClient,
                             GroupSubService groupSubService) {
       this.sendBotMessageService = sendBotMessageService;
       this.javaRushGroupClient = javaRushGroupClient;
       this.groupSubService = groupSubService;
   }

   @Override
   public void execute(Update update) {
       if (getMessage(update).equalsIgnoreCase(ADD_GROUP_SUB.getCommandName())) {
           sendGroupIdList(getChatId(update));
           return;
       }
       String groupId = getMessage(update).split(SPACE)[1];
       String chatId = getChatId(update);
       if (isNumeric(groupId)) {
           GroupDiscussionInfo groupById = javaRushGroupClient.getGroupById(Integer.parseInt(groupId));
           if (isNull(groupById.getId())) {
               sendGroupNotFound(chatId, groupId);
           }
           GroupSub savedGroupSub = groupSubService.save(chatId, groupById);
           sendBotMessageService.sendMessage(chatId, "Подписал на группу " + savedGroupSub.getTitle());
       } else {
           sendGroupNotFound(chatId, groupId);
       }
   }

   private void sendGroupNotFound(String chatId, String groupId) {
       String groupNotFoundMessage = "Нет группы с ID = \"%s\"";
       sendBotMessageService.sendMessage(chatId, String.format(groupNotFoundMessage, groupId));
   }

   private void sendGroupIdList(String chatId) {
       String groupIds = javaRushGroupClient.getGroupList(GroupRequestArgs.builder().build()).stream()
               .map(group -> String.format("%s - %s \n", group.getTitle(), group.getId()))
               .collect(Collectors.joining());

       String message = "Wasбы подписаться на группу - передай комадну вместе с ID группы. \n" +
               "Например: /addGroupSub 16. \n\n" +
               "я подготовил список всех групп - выберай Wieую хочешь :) \n\n" +
               "Name группы - ID группы \n\n" +
               "%s";

       sendBotMessageService.sendMessage(chatId, String.format(message, groupIds));
   }
}
Diese Klasse verwendet die isNumeric- Methode aus der Apache-Commons-Bibliothek, also fügen wir sie unserem Speicher hinzu:
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-lang3</artifactId>
  <version>${apache.commons.version}</version>
</dependency>
Und im Eigenschaftenblock:
<apache.commons.version>3.11</apache.commons.version>
All diese Logik ist in der Klasse. Lesen Sie es sorgfältig. Wenn Sie Fragen/Anregungen haben, schreiben Sie diese in die Kommentare. Danach müssen wir den Befehl zu CommandContainer in unserer Befehlszuordnung hinzufügen:
.put(ADD_GROUP_SUB.getCommandName(), new AddGroupSubCommand(sendBotMessageService, javaRushGroupClient, groupSubService))
Und alles für dieses Team. Ich würde diese Funktionalität gerne irgendwie testen, kann sie mir aber bisher nur in der Datenbank wirklich anschauen. Im dritten Teil füge ich Änderungen von JRTB-6 hinzu, damit wir die Liste der Gruppen anzeigen können, die ein Benutzer abonniert hat. Jetzt wäre es gut, das alles zu überprüfen. Dazu führen wir alle Aktionen im Telegram durch und checken die Datenbank ein. Da wir Tests geschrieben haben, sollte alles in Ordnung sein. Der Artikel ist schon ziemlich lang, daher werden wir später einen Test für AddGroupSubCommand schreiben und TODO in den Code einfügen, um es nicht zu vergessen.

Schlussfolgerungen

In diesem Artikel haben wir uns die Arbeit des Hinzufügens von Funktionalität in der gesamten Anwendung angesehen, angefangen bei der Datenbank bis hin zur Arbeit mit dem Client, der den Bot verwendet. Normalerweise helfen solche Aufgaben dabei, das Projekt zu verstehen und sein Wesen zu verstehen. Verstehen Sie, wie es funktioniert. Heutzutage sind die Themen nicht einfach, also scheuen Sie sich nicht: Schreiben Sie Ihre Fragen in die Kommentare und ich werde versuchen, sie zu beantworten. Gefällt dir das Projekt? Geben Sie ihm auf Github einen Stern : Auf diese Weise wird klar, dass sie an dem Projekt interessiert sind, und ich werde glücklich sein. Wie man so schön sagt: Ein Meister freut sich immer, wenn seine Arbeit geschätzt wird. Der Code enthält alle drei Teile von STEP_6 und wird vor diesem Artikel verfügbar sein. Wie kann man davon erfahren? Es ist ganz einfach – treten Sie dem Telegram-Kanal bei , wo ich alle Informationen zu meinen Artikeln über den Telegram-Bot veröffentliche. Danke fürs Lesen! Teil 3 ist bereits da .

Eine Liste aller Materialien der Serie finden Sie am Anfang dieses Artikels.

Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION