JavaRush /Blog Java /Random-FR /Nous ajoutons la possibilité de s'abonner à un groupe d'a...
Roman Beekeeper
Niveau 35

Nous ajoutons la possibilité de s'abonner à un groupe d'articles. (Partie 2) - "Projet Java de A à Z"

Publié dans le groupe Random-FR
Salut tout le monde! Nous continuons à travailler sur la tâche que nous avons commencée la semaine dernière ."Projet Java de A à Z" : Ajout de la possibilité de s'abonner à un groupe d'articles.  Partie 2 - 1

Nous implémentons JRTB-5

Nous devons maintenant ajouter une commande pour pouvoir nous abonner à un groupe d'articles de JavaRush. Comment faire? Nous suivrons le scénario le plus simple que j'ai proposé. Puisque nous avons accès par ID de groupe, nous avons besoin que l'utilisateur le transfère. Pour ce faire, l'utilisateur entrera la commande /addGroupSub GROUP_ID, qui fonctionnera de deux manières : si seule la commande elle-même arrive : /addGroupSub , une liste de tous les groupes et leurs identifiants est envoyée en réponse. Ensuite, l'utilisateur pourra sélectionner l'ID de groupe dont il a besoin et créer la deuxième version de la requête dans cette commande : /addGroupSub GROUP_ID - et il y aura alors une entrée pour ce groupe avec cet utilisateur. Je pense que nous pouvons faire mieux à l'avenir. Notre objectif est de montrer le développement, et non l’expérience utilisateur super cool (j’ai honte de le dire, mais je ne connais pas le terme russe qui signifierait cela). Pour ajouter correctement des fonctionnalités qui traversent l'ensemble de l'application (dans notre cas, du client du robot Telegram à la base de données), vous devez commencer par une extrémité. Nous ferons cela du côté de la base de données.

Ajout d'une nouvelle migration à la base de données

La première chose à faire est d'ajouter une nouvelle migration de base de données et la possibilité de sauvegarder les données d'abonnement aux groupes d'utilisateurs dans JR. Pour vous rappeler comment cela devrait être, revenez à l'article « Planification de projet : mesurer sept fois ». Sur la deuxième photo, il y a un schéma approximatif de la base de données. Nous devons ajouter un tableau pour stocker les informations sur le groupe :
  • L'ID de groupe dans JavaRush sera également notre identifiant. Nous leur faisons confiance et pensons que ces identifiants sont uniques ;
  • titre - sur nos photos, c'était nom - le nom informel du groupe ; c'est-à-dire ce que nous voyons sur le site Web JavaRush ;
  • last_article_id - et c'est un domaine intéressant. Il stockera le dernier identifiant de l'article dans ce groupe, que le bot a déjà envoyé à ses abonnés. Grâce à ce champ, le mécanisme de recherche de nouveaux articles fonctionnera. Les nouveaux abonnés ne recevront pas les articles publiés avant l'abonnement de l'utilisateur : uniquement ceux qui ont été publiés après l'inscription au groupe.
Nous aurons également une relation plusieurs-à-plusieurs entre les tables groupes et utilisateurs, car chaque utilisateur peut avoir plusieurs abonnements de groupe (un-à-plusieurs), et chaque abonnement de groupe peut avoir plusieurs utilisateurs (un-à-plusieurs, uniquement). d'un autre côté). Il s’avère que ce sera notre plusieurs-à-plusieurs. Pour ceux qui ont des questions, consultez les articles de la base de données. Oui, je prévois prochainement de créer un post sur la chaîne Telegram, où je rassemblerai tous les articles de la base de données. Voici à quoi ressemblera notre deuxième migration de base de données.
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)
);
Il est important de noter que je modifie d'abord l'ancienne table - j'y ajoute une clé primaire. Cela m'a manqué d'une manière ou d'une autre à l'époque, mais maintenant MySQL ne m'a pas donné la possibilité d'ajouter une CLÉ ÉTRANGÈRE pour la table gorup_x_user, et dans le cadre de cette migration, j'ai mis à jour la base de données. Veuillez noter un aspect important. La modification de la base de données doit être effectuée exactement de cette manière : tout ce qui est nécessaire se trouve dans la nouvelle migration, mais pas dans la mise à jour d'une migration déjà publiée. Oui, dans notre cas, rien ne se passerait, puisqu'il s'agit d'un projet test et nous savons qu'il est déployé à un seul endroit, mais ce serait une mauvaise approche. Mais nous voulons que tout se passe bien. Vient ensuite la suppression des tables avant de les créer. Pourquoi est-ce? Ainsi, si par hasard il y avait des tables avec de tels noms dans la base de données, la migration n'échouerait pas et fonctionnerait exactement comme prévu. Et puis nous ajoutons deux tables. Tout était comme nous le souhaitions. Nous devons maintenant lancer notre application. Si tout démarre et ne casse pas, alors la migration est enregistrée. Et pour vérifier cela, nous allons dans la base de données pour nous assurer que : a) de telles tables sont apparues ; b) il y a une nouvelle entrée dans le tableau technique des voies de migration. Ceci termine le travail de migration, passons aux référentiels.

Ajout d'une couche de référentiel

Grâce à Spring Boot Data, tout est très simple ici : il faut ajouter l'entité GroupSub, mettre à jour légèrement TelegramUser et ajouter un GroupSubRepository presque vide : On ajoute l'entité GroupSub au même package que TelegramUser :
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);
   }
}
Une chose à noter est que nous avons un champ d'utilisateurs supplémentaires qui contiendra une collection de tous les utilisateurs abonnés au groupe. Et deux annotations - ManyToMany et JoinTable - sont exactement ce dont nous avons besoin pour cela. Le même champ doit être ajouté pour TelegramUser :
@ManyToMany(mappedBy = "users", fetch = FetchType.EAGER)
private List<GroupSub> groupSubs;
Ce champ utilise des jointures écrites dans l'entité GroupSub. Et, en fait, notre classe de référentiel pour GroupSub est 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> {
}
A ce stade, nous n'avons pas besoin de méthodes supplémentaires : celles implémentées dans l'ancêtre JpaRepository nous suffisent. Écrivons un test dans TelegramUserRepositoryIT qui vérifiera que notre plusieurs à plusieurs fonctionne. L'idée du test est que nous allons ajouter 5 groupes d'abonnements par utilisateur à la base de données via un script sql, obtenir cet utilisateur par son identifiant et vérifier que nous avons reçu exactement ces groupes et avec exactement les mêmes valeurs. Comment faire? Vous pouvez intégrer un compteur dans les données, que nous pouvons ensuite parcourir et vérifier. Voici le script 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);
Et le test lui-même :
@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());
   }
}
Ajoutons maintenant un test de même signification pour l'entité GroupSub. Pour ce faire, créons une classe de test groupSubRepositoryIT dans le même package que 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());
       }
   }
}
Et le script fiveUsersForGroupSub.sql manquant :
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);
À ce stade, une partie du travail avec le référentiel peut être considérée comme terminée. Écrivons maintenant une couche de service.

Nous écrivons GroupSubService

A ce stade, pour travailler avec des groupes d'abonnements, il suffit de pouvoir les sauvegarder, donc pas de problème : on crée le service GroupSubService et son implémentation de GroupSubServiceImpl dans un package qui contient d'autres services - service :
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);
}
Et sa mise en œuvre :
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);
   }
}
Pour que Spring Data fonctionne correctement et qu'un enregistrement plusieurs-à-plusieurs soit créé, nous devons extraire l'utilisateur de notre base de données pour le groupe d'abonnement que nous créons et l'ajouter à l'objet GroupSub. Ainsi, lorsque nous transférons cet abonnement pour sauvegarde, une connexion sera également créée via la table group_x_user. Il peut arriver qu'un tel groupe d'abonnement ait déjà été créé et qu'il vous suffit d'y ajouter un autre utilisateur. Pour ce faire, nous obtenons d'abord l'ID de groupe de la base de données, et s'il existe un enregistrement, nous travaillons avec, sinon nous en créons un nouveau. Il est important de noter que pour travailler avec TelegramUser, nous utilisons TelegramUserService pour suivre le dernier des principes SOLID. Pour le moment, si nous ne trouvons pas d'enregistrement par ID, je lance simplement une exception. Il n'est en aucun cas traité actuellement : nous le ferons à la toute fin, avant le MVP. Écrivons deux tests unitaires pour la classe GroupSubServiceTest . De quoi avons-nous besoin ? Je veux être sûr que la méthode de sauvegarde sera appelée dans le GroupSubRepository et qu'une entité avec un seul utilisateur sera transmise à GroupSub - celle qui nous renverra TelegramUserService en utilisant l'ID fourni. Et la deuxième option, lorsqu'un groupe avec le même ID est déjà dans la base de données et que ce groupe a déjà un utilisateur, et vous devez vérifier qu'un autre utilisateur sera ajouté à ce groupe et que cet objet sera enregistré. Voici la mise en œuvre :
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);
   }

}
J'ai également ajouté la méthode init() avec l'annotation BeforeEach. De cette façon, ils créent généralement une méthode qui sera exécutée avant chaque exécution de test, et il est possible d'y insérer une logique commune pour tous les tests. Dans notre cas, nous devons verrouiller TelegramUserService de la même manière pour tous les tests de cette classe, il est donc logique de transférer cette logique vers une méthode commune. Deux modèles de mokito sont utilisés ici :
  • Mockito.when(o1.m1(a1)).thenReturn(o2) - nous y disons que lorsque la méthode m1 est appelée sur l'objet o1 avec l'argument a1 , la méthode renverra l'objet o2 . C'est presque la fonctionnalité la plus importante de mockito - forcer l'objet fictif à renvoyer exactement ce dont nous avons besoin ;

  • Mockito.verify(o1).m1(a1) - qui vérifie que la méthode m1 a été appelée sur l'objet o1 avec l'argument a1 . Il était bien sûr possible d'utiliser l'objet renvoyé par la méthode save, mais j'ai décidé de rendre les choses un peu plus compliquées en montrant une autre méthode possible. Quand peut-il être utile ? Dans les cas où les méthodes des classes fictives renvoient void. Ensuite, sans Mockito.verify, il n'y aura pas de travail)))

Nous continuons d’adhérer à l’idée selon laquelle les tests doivent être écrits, et beaucoup d’entre eux doivent l’être. La prochaine étape consiste à travailler avec l’équipe du robot Telegram.

Créez la commande /addGroupSub

Ici, nous devons effectuer la logique suivante : si nous recevons juste une commande, sans aucun contexte, nous aidons l'utilisateur et lui donnons une liste de tous les groupes avec leurs identifiants afin qu'il puisse transmettre les informations nécessaires au bot. Et si l'utilisateur envoie une commande au bot avec un ou plusieurs autres mots, recherchez un groupe avec cet identifiant ou écrivez qu'un tel groupe n'existe pas. Ajoutons une nouvelle valeur dans notre nom - CommandName :
ADD_GROUP_SUB("/addgroupsub")
Passons plus loin de la base de données au bot télégramme - créons la classe AddGroupSubCommand dans le package de commandes :
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 = "Whatбы подписаться на группу - передай комадну вместе с ID группы. \n" +
               "Например: /addGroupSub 16. \n\n" +
               "я подготовил список всех групп - выберай Howую хочешь :) \n\n" +
               "Name группы - ID группы \n\n" +
               "%s";

       sendBotMessageService.sendMessage(chatId, String.format(message, groupIds));
   }
}
Cette classe utilise la méthode isNumeric de la bibliothèque apache-commons, ajoutons-la donc à notre mémoire :
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-lang3</artifactId>
  <version>${apache.commons.version}</version>
</dependency>
Et dans le bloc propriétés :
<apache.commons.version>3.11</apache.commons.version>
Toute cette logique est dans la classe. Lisez-le attentivement. Si vous avez des questions/suggestions, écrivez-les dans les commentaires. Après cela, nous devons ajouter la commande à CommandContainer dans notre mappe de commandes :
.put(ADD_GROUP_SUB.getCommandName(), new AddGroupSubCommand(sendBotMessageService, javaRushGroupClient, groupSubService))
Et tout pour cette équipe. J'aimerais tester cette fonctionnalité d'une manière ou d'une autre, mais jusqu'à présent, je ne peux vraiment la regarder que dans la base de données. Dans la troisième partie, j'ajouterai les modifications de JRTB-6 afin que nous puissions afficher la liste des groupes auxquels un utilisateur est abonné. Maintenant, ce serait bien de vérifier tout cela. Pour ce faire, nous effectuerons toutes les actions dans Telegram et vérifierons dans la base de données. Puisque nous avons des tests écrits, tout devrait bien se passer. L'article est déjà assez long, nous écrirons donc un test pour AddGroupSubCommand plus tard, et ajouterons TODO dans le code pour ne pas oublier.

conclusions

Dans cet article, nous avons examiné le travail d'ajout de fonctionnalités dans l'ensemble de l'application, en commençant par la base de données et en terminant par le travail avec le client qui utilise le bot. Habituellement, de telles tâches aident à comprendre le projet et à comprendre son essence. Comprenez comment cela fonctionne. De nos jours, les sujets ne sont pas faciles, alors ne soyez pas timide : écrivez vos questions dans les commentaires, et j'essaierai d'y répondre. Aimez-vous le projet? Donnez-lui une étoile sur Github : de cette façon, il sera clair qu'ils sont intéressés par le projet, et j'en serai heureux. Comme on dit, un maître est toujours heureux lorsque son travail est apprécié. Le code contiendra les trois parties de STEP_6 et sera disponible avant cet article. Comment s'en informer ? C'est simple : rejoignez la chaîne Telegram , où je publie toutes les informations sur mes articles sur le robot Telegram. Merci d'avoir lu! La partie 3 est déjà .

Une liste de tous les matériaux de la série se trouve au début de cet article.

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