JavaRush /Java Blog /Random-IT /Stiamo aggiungendo la possibilità di iscriversi a un grup...
Roman Beekeeper
Livello 35

Stiamo aggiungendo la possibilità di iscriversi a un gruppo di articoli. (Parte 2) - "Progetto Java dalla A alla Z"

Pubblicato nel gruppo Random-IT
Ciao a tutti! Continuiamo a lavorare sul compito che abbiamo iniziato la settimana scorsa ."Progetto Java dalla A alla Z": aggiunta la possibilità di iscriversi a un gruppo di articoli.  Parte 2 - 1

Implementiamo JRTB-5

Ora dobbiamo aggiungere un comando in modo da poterci iscrivere ad un gruppo di articoli di JavaRush. Come farlo? Seguiremo lo scenario più semplice che mi è venuto in mente. Poiché abbiamo accesso tramite ID di gruppo, abbiamo bisogno che l'utente lo trasferisca. Per fare ciò, l'utente inserirà il comando /addGroupSub GROUP_ID, che funzionerà in due modi: se arriva solo il comando stesso: /addGroupSub , l'elenco di tutti i gruppi e i loro ID verrà inviato in risposta. Quindi l'utente sarà in grado di selezionare l'ID del gruppo di cui ha bisogno e creare la seconda versione della richiesta in questo comando: /addGroupSub GROUP_ID - e quindi ci sarà un record di questo gruppo con questo utente. Penso che possiamo fare meglio in futuro. Il nostro obiettivo è mostrare lo sviluppo e non l'esperienza utente fantastica (mi vergogno di dirlo, ma non conosco il termine in russo che significherebbe questo). Per aggiungere correttamente funzionalità che attraversano l'intera applicazione (nel nostro caso, dal client bot di Telegram al database), è necessario iniziare da un punto. Lo faremo dal lato del database.

Aggiunta di una nuova migrazione al database

La prima cosa da fare è aggiungere una nuova migrazione del database e la possibilità di salvare i dati di iscrizione del gruppo utenti in JR. Per ricordare come dovrebbe essere, torniamo all’articolo “ Progettazione: misurare sette volte ”. Lì nella seconda foto c'è un diagramma approssimativo del database. Dobbiamo aggiungere una tabella per memorizzare le informazioni sul gruppo:
  • L'ID del gruppo in JavaRush sarà anche il nostro ID. Ci fidiamo di loro e crediamo che questi ID siano unici;
  • titolo - nelle nostre foto era nome - il nome informale del gruppo; ovvero ciò che vediamo sul sito JavaRush;
  • last_article_id - e questo è un campo interessante. Memorizzerà l'ultimo ID dell'articolo in questo gruppo, che il bot ha già inviato ai suoi abbonati. Utilizzando questo campo funzionerà il meccanismo per la ricerca di nuovi articoli. I nuovi iscritti non riceveranno gli articoli pubblicati prima dell'iscrizione dell'utente: solo quelli pubblicati dopo l'iscrizione al gruppo.
Avremo anche una relazione molti-a-molti tra la tabella dei gruppi e quella degli utenti, poiché ogni utente può avere più iscrizioni a gruppi (uno-a-molti) e ogni iscrizione a gruppi può avere molti utenti (uno-a-molti, solo Dall'altro lato). Si scopre che questo sarà il nostro molti-a-molti. Per coloro che hanno domande, rivedere gli articoli nel database. Sì, ho intenzione di creare presto un post sul canale Telegram, dove metterò insieme tutti gli articoli presenti nel database. Ecco come apparirà la nostra seconda migrazione del database.
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)
);
È importante notare che prima cambio la vecchia tabella, aggiungendovi una chiave primaria. In qualche modo all'epoca mi era mancato, ma ora MySQL non mi ha dato l'opportunità di aggiungere una FOREIGN KEY per la tabella gorup_x_user e come parte di questa migrazione ho aggiornato il database. Si prega di notare un aspetto importante. La modifica del database dovrebbe essere eseguita esattamente in questo modo: tutto ciò che serve è nella nuova migrazione, ma non aggiornando una migrazione già rilasciata. Sì, nel nostro caso non accadrebbe nulla, poiché si tratta di un progetto di prova e sappiamo che verrà implementato in un solo posto, ma questo sarebbe l'approccio sbagliato. Ma vogliamo che tutto vada bene. Poi viene l'eliminazione delle tabelle prima di crearle. Perchè è questo? In modo che se per caso nel database ci fossero tabelle con tali nomi, la migrazione non fallirebbe e funzionerebbe esattamente come previsto. E poi aggiungiamo due tabelle. Tutto era come volevamo. Ora dobbiamo avviare la nostra applicazione. Se tutto si avvia e non si rompe, la migrazione viene registrata. E per ricontrollarlo, andiamo nel database per assicurarci che: a) tali tabelle siano apparse; b) c'è una nuova voce nella tabella tecnica flyway. Questo completa il lavoro di migrazione, passiamo ai repository.

Aggiunta di un livello di repository

Grazie a Spring Boot Data, qui è tutto molto semplice: dobbiamo aggiungere l'entità GroupSub, aggiornare leggermente TelegramUser e aggiungere un GroupSubRepository quasi vuoto: aggiungiamo l'entità GroupSub allo stesso pacchetto di 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);
   }
}
Una cosa degna di nota è che abbiamo un campo utenti aggiuntivo che conterrà una raccolta di tutti gli utenti iscritti al gruppo. E due annotazioni, ManyToMany e JoinTable, sono esattamente ciò di cui abbiamo bisogno. Lo stesso campo deve essere aggiunto per TelegramUser:
@ManyToMany(mappedBy = "users", fetch = FetchType.EAGER)
private List<GroupSub> groupSubs;
Questo campo utilizza join scritti nell'entità GroupSub. E, infatti, la nostra classe repository per 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> {
}
In questa fase non abbiamo bisogno di metodi aggiuntivi: ci bastano quelli implementati nell'antenato JpaRepository. Scriviamo un test in TelegramUserRepositoryIT che controllerà che il nostro molti-a-molti funzioni. L'idea del test è che aggiungeremo 5 gruppi di abbonamenti per utente al database tramite uno script SQL, otterremo questo utente tramite il suo ID e controlleremo di aver ricevuto esattamente quei gruppi e esattamente con gli stessi valori. Come farlo? Puoi incorporare un contatore nei dati, che possiamo poi esaminare e controllare. Ecco lo 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);
E il test stesso:
@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());
   }
}
Ora aggiungiamo un test dello stesso significato per l'entità GroupSub. Per fare ciò, creiamo una classe di test groupSubRepositoryIT nello stesso pacchetto di 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());
       }
   }
}
E lo script fiveUsersForGroupSub.sql mancante:
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);
A questo punto, parte del lavoro con il repository può considerarsi completato. Ora scriviamo un livello di servizio.

Scriviamo GroupSubService

In questa fase, per lavorare con gruppi di abbonamenti, dobbiamo solo poterli salvare, quindi nessun problema: creiamo il servizio GroupSubService e la sua implementazione di GroupSubServiceImpl in un pacchetto che contiene altri servizi - 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);
}
E la sua implementazione:
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);
   }
}
Affinché Spring Data funzioni correttamente e venga creato un record molti-a-molti, dobbiamo ottenere l'utente dal nostro database per il gruppo di sottoscrizione che stiamo creando e aggiungerlo all'oggetto GroupSub. Pertanto, quando trasferiamo questa sottoscrizione per il salvataggio, verrà creata anche una connessione tramite la tabella group_x_user. Potrebbe verificarsi una situazione in cui un gruppo di abbonamento di questo tipo è già stato creato e devi solo aggiungervi un altro utente. Per fare ciò, prima otteniamo l'ID del gruppo dal database e, se è presente un record, lavoriamo con esso, in caso contrario ne creiamo uno nuovo. È importante notare che per lavorare con TelegramUser utilizziamo TelegramUserService per seguire l'ultimo dei principi SOLID. Al momento, se non troviamo un record in base all'ID, lancio semplicemente un'eccezione. Non viene elaborato in alcun modo adesso: lo faremo alla fine, prima dell'MVP. Scriviamo due unit test per la classe GroupSubServiceTest . Di quali abbiamo bisogno? Voglio essere sicuro che il metodo di salvataggio verrà chiamato nel GroupSubRepository e un'entità con un singolo utente verrà passata a GroupSub, quello che ci restituirà TelegramUserService utilizzando l'ID fornito. E la seconda opzione, quando un gruppo con lo stesso ID è già nel database e questo gruppo ha già un utente, ed è necessario verificare che un altro utente verrà aggiunto a questo gruppo e questo oggetto verrà salvato. Ecco l'implementazione:
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);
   }

}
Ho anche aggiunto il metodo init() con l'annotazione BeforeEach. In questo modo, di solito creano un metodo che verrà eseguito prima dell'esecuzione di ogni test ed è possibile inserirvi una logica comune per tutti i test. Nel nostro caso, dobbiamo bloccare TelegramUserService allo stesso modo per tutti i test di questa classe, quindi ha senso trasferire questa logica a un metodo comune. Ci sono due modelli di mokito usati qui:
  • Mockito.when(o1.m1(a1)).thenReturn(o2) - in esso diciamo che quando il metodo m1 viene chiamato sull'oggetto o1 con argomento a1 , il metodo restituirà l'oggetto o2 . Questa è quasi la funzionalità più importante di mockito: forzare l'oggetto mock a restituire esattamente ciò di cui abbiamo bisogno;

  • Mockito.verify(o1).m1(a1) - che verifica che il metodo m1 sia stato chiamato sull'oggetto o1 con argomento a1 . Naturalmente era possibile utilizzare l'oggetto restituito del metodo di salvataggio, ma ho deciso di renderlo un po' più complicato mostrando un altro possibile metodo. Quando può essere utile? Nei casi in cui i metodi delle classi fittizie restituiscono void. Quindi senza Mockito.verify non ci sarà lavoro)))

Continuiamo ad aderire all'idea che i test debbano essere scritti, e molti di essi devono essere scritti. La fase successiva è lavorare con il team dei bot di Telegram.

Crea il comando /addGroupSub

Qui dobbiamo eseguire la seguente logica: se riceviamo solo un comando, senza alcun contesto, aiutiamo l'utente e gli forniamo un elenco di tutti i gruppi con i loro ID in modo che possa passare le informazioni necessarie al bot. E se l'utente invia un comando al bot con qualche altra parola, trova un gruppo con quell'ID o scrivi che non esiste un gruppo del genere. Aggiungiamo un nuovo valore nel nostro nome - CommandName:
ADD_GROUP_SUB("/addgroupsub")
Spostiamoci ulteriormente dal database al bot di Telegram: creiamo la classe AddGroupSubCommand nel pacchetto di comandi:
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));
   }
}
Questa classe utilizza il metodo isNumeric della libreria apache-commons, quindi aggiungiamolo alla nostra memoria:
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-lang3</artifactId>
  <version>${apache.commons.version}</version>
</dependency>
E nel blocco delle proprietà:
<apache.commons.version>3.11</apache.commons.version>
Tutta questa logica è nella classe. Leggilo attentamente. Se avete domande/suggerimenti scriveteli nei commenti. Successivamente, dobbiamo aggiungere il comando a CommandContainer nella nostra mappa dei comandi:
.put(ADD_GROUP_SUB.getCommandName(), new AddGroupSubCommand(sendBotMessageService, javaRushGroupClient, groupSubService))
E tutto per questa squadra. Vorrei in qualche modo testare questa funzionalità, ma finora posso solo guardarla nel database. Nella terza parte aggiungerò le modifiche apportate a JRTB-6 in modo da poter visualizzare l'elenco dei gruppi a cui è iscritto un utente. Adesso sarebbe bene verificare tutto questo. Per fare ciò, eseguiremo tutte le azioni in Telegram e controlleremo il database. Dato che abbiamo dei test scritti, dovrebbe andare tutto bene. L'articolo è già piuttosto lungo, quindi scriveremo un test per AddGroupSubCommand in seguito e aggiungeremo TODO nel codice per non dimenticarlo.

conclusioni

In questo articolo abbiamo esaminato il lavoro di aggiunta di funzionalità all'intera applicazione, a partire dal database e terminando con il lavoro con il client che utilizza il bot. Di solito tali compiti aiutano a comprendere il progetto e a comprenderne l'essenza. Comprendi come funziona. In questi giorni gli argomenti non sono facili, quindi non siate timidi: scrivete le vostre domande nei commenti, e cercherò di rispondervi. Ti piace il progetto? Dategli una stella su Github : così sarà chiaro che sono interessati al progetto, e io ne sarò felice. Come si suol dire, un maestro è sempre contento quando il suo lavoro viene apprezzato. Il codice conterrà tutte e tre le parti di STEP_6 e sarà disponibile prima di questo articolo. Come scoprirlo? È facile: unisciti al canale Telegram , dove pubblico tutte le informazioni sui miei articoli sul bot Telegram. Grazie per aver letto! La terza parte è già qui .

Un elenco di tutti i materiali della serie si trova all'inizio di questo articolo.

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