JavaRush /בלוג Java /Random-HE /אנו מוסיפים את האפשרות להירשם לקבוצת מאמרים. (חלק 2) - "פ...
Roman Beekeeper
רָמָה

אנו מוסיפים את האפשרות להירשם לקבוצת מאמרים. (חלק 2) - "פרויקט ג'אווה מא' עד ת'"

פורסם בקבוצה
שלום לכולם! אנחנו ממשיכים לעבוד על המשימה שהתחלנו בשבוע שעבר ."פרויקט ג'אווה מא' עד ת'": הוספת אפשרות להירשם לקבוצת מאמרים.  חלק 2 - 1

אנו מיישמים JRTB-5

כעת עלינו להוסיף פקודה כדי שנוכל להירשם לקבוצת מאמרים כלשהי מ-JavaRush. איך לעשות את זה? נעקוב אחר התרחיש הפשוט ביותר שהמצאתי. מכיוון שיש לנו גישה באמצעות מזהה קבוצה, אנו צריכים שהמשתמש יעביר אותה. לשם כך, המשתמש יזין את הפקודה /addGroupSub GROUP_ID, שתעבוד באחת משתי דרכים: אם רק הפקודה עצמה מגיעה: /addGroupSub , רשימת כל הקבוצות והמזהים שלהן נשלחת בתגובה. לאחר מכן המשתמש יוכל לבחור את מזהה הקבוצה שהוא צריך וליצור את הגרסה השנייה של הבקשה בפקודה זו: /addGroupSub GROUP_ID - ואז תהיה תיעוד של הקבוצה הזו עם המשתמש הזה. אני חושב שנוכל להשתפר בעתיד. המטרה שלנו היא להראות את הפיתוח, ולא את חווית המשתמש הסופר מגניבה (אני מתבייש לומר, אבל אני לא מכיר את המונח ברוסית שפירושו את זה). כדי להוסיף כראוי פונקציונליות שעוברת דרך האפליקציה כולה (במקרה שלנו, מלקוח הבוט של הטלגרם ועד למסד הנתונים), צריך להתחיל באיזה קצה. נעשה זאת מהצד של מסד הנתונים.

הוספת הגירה חדשה למסד הנתונים

הדבר הראשון שצריך לעשות הוא להוסיף העברת מסד נתונים חדשה ויכולת לשמור נתוני מנוי של קבוצת משתמשים ב-JR. כדי לזכור איך זה צריך להיות, חזור למאמר " תכנון פרויקט: למדוד שבע פעמים ." שם בתמונה השנייה יש תרשים משוער של מסד הנתונים. עלינו להוסיף טבלה כדי לאחסן מידע קבוצתי:
  • מזהה הקבוצה ב-JavaRush יהיה גם המזהה שלנו. אנו סומכים עליהם ומאמינים שתעודות זהות אלו הן ייחודיות;
  • כותרת - בתמונות שלנו זה היה שם - השם הבלתי פורמלי של הקבוצה; כלומר, מה שאנו רואים באתר JavaRush;
  • last_article_id - וזה תחום מעניין. הוא יאחסן את המזהה האחרון של המאמר בקבוצה זו, שהבוט כבר שלח למנויים שלו. באמצעות שדה זה, המנגנון לחיפוש מאמרים חדשים יפעל. מנויים חדשים לא יקבלו מאמרים שפורסמו לפני שהמשתמש נרשם: רק כאלו שפורסמו לאחר ההרשמה לקבוצה.
יהיה לנו גם מערכת יחסים של רבים לרבים בין טבלת הקבוצות והמשתמשים, מכיוון שלכל משתמש יכולים להיות מנויים קבוצתיים רבים (אחד לרבים), ולכל מנוי קבוצה יכולים להיות משתמשים רבים (אחד לרבים, רק בצד השני). מסתבר שזה יהיה הרב-לרבים שלנו. למי שיש שאלות, עיין במאמרים במאגר. כן, בקרוב אני מתכנן ליצור פוסט בערוץ הטלגרם, שבו ארכיב את כל המאמרים במאגר. כך תיראה העברת מסד הנתונים השנייה שלנו.
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)
);
חשוב לציין שקודם כל אני משנה את הטבלה הישנה – אני מוסיף לה מפתח ראשי. איכשהו פספסתי את זה בזמנו, אבל עכשיו MySQL לא נתנה לי את ההזדמנות להוסיף FOREIGN KEY לטבלת gorup_x_user, וכחלק מההגירה הזו עדכנתי את מסד הנתונים. שימו לב להיבט חשוב. שינוי מסד הנתונים צריך להיעשות בדיוק כך - כל מה שצריך נמצא בהגירה החדשה, אך לא על ידי עדכון מיגרציה שכבר שוחררה. כן, במקרה שלנו שום דבר לא היה קורה, מכיוון שמדובר בפרויקט מבחן ואנחנו יודעים שהוא פרוס במקום אחד בלבד, אבל זו תהיה גישה שגויה. אבל אנחנו רוצים שהכל יהיה בסדר. בשלב הבא מחיקת טבלאות לפני יצירתן. למה זה? כך שאם במקרה היו טבלאות עם שמות כאלה במסד הנתונים, ההגירה לא תיכשל ותעבוד בדיוק כמצופה. ואז נוסיף שני טבלאות. הכל היה כמו שרצינו. כעת עלינו להפעיל את האפליקציה שלנו. אם הכל מתחיל ולא נשבר, ההגירה מתועדת. וכדי לבדוק זאת, אנו עוברים למסד הנתונים כדי לוודא ש: א) טבלאות כאלה הופיעו; ב) יש ערך חדש בטבלה הטכנית של המסלול. זה משלים את עבודת ההגירה, בואו נעבור למאגרים.

הוספת שכבת מאגר

הודות ל-Spring Boot Data, הכל מאוד פשוט כאן: אנחנו צריכים להוסיף את הישות GroupSub, לעדכן מעט את TelegramUser ולהוסיף מאגר GroupSub כמעט ריק: אנחנו מוסיפים את הישות GroupSub לאותה חבילה כמו 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);
   }
}
דבר אחד שכדאי לציין הוא שיש לנו שדה משתמשים נוסף שיכיל אוסף של כל המשתמשים הרשומים לקבוצה. ושתי הערות - ManyToMany ו-JoinTable - הם בדיוק מה שאנחנו צריכים בשביל זה. יש להוסיף את אותו שדה עבור TelegramUser:
@ManyToMany(mappedBy = "users", fetch = FetchType.EAGER)
private List<GroupSub> groupSubs;
שדה זה משתמש בהצטרפות שנכתבו בישות GroupSub. ולמעשה, מחלקת המאגר שלנו עבור 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> {
}
בשלב זה, איננו זקוקים לשיטות נוספות: מספיקות לנו אלו המיושמות ב-JpaRepository. בואו נכתוב מבחן ב-TelegramUserRepositoryIT שיבדוק שהרב-לרבים שלנו עובד. הרעיון של הבדיקה הוא שנוסיף 5 קבוצות של מנויים לכל משתמש למסד הנתונים באמצעות סקריפט sql, נקבל את המשתמש הזה לפי תעודת הזהות שלו ונבדוק שקיבלנו בדיוק את הקבוצות האלה ועם אותם ערכים בדיוק. איך לעשות את זה? אתה יכול להטמיע מונה בנתונים, שאותו נוכל לעבור ולבדוק. הנה הסקריפט 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);
והמבחן עצמו:
@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());
   }
}
כעת הבה נוסיף בדיקה בעלת אותה משמעות עבור הישות GroupSub. לשם כך, בואו ניצור קבוצת מבחן groupSubRepositoryIT באותה חבילה כמו 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());
       }
   }
}
וסקריפט 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);
בשלב זה, חלק מהעבודה עם המאגר יכולה להיחשב שהושלמה. כעת נכתוב שכבת שירות.

אנחנו כותבים GroupSubService

בשלב זה, כדי לעבוד עם קבוצות של מנויים, אנחנו צריכים רק להיות מסוגלים לשמור אותם, אז אין בעיה: אנחנו יוצרים את שירות GroupSubService והטמעתו של GroupSubServiceImpl בחבילה המכילה שירותים נוספים - שירות:
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);
}
והיישום שלו:
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);
   }
}
על מנת ש-Spring Data יפעלו בצורה נכונה ותיווצר רשומה של רבים לרבים, עלינו לקבל את המשתמש ממסד הנתונים שלנו עבור קבוצת המנויים שאנו יוצרים ולהוסיף אותו לאובייקט GroupSub. לפיכך, כאשר נעביר מנוי זה לשמירה, יווצר חיבור גם דרך טבלת group_x_user. יכול להיות מצב שבו כבר נוצרה קבוצת מנויים כזו ורק צריך להוסיף לה משתמש נוסף. לשם כך נקבל תחילה את מזהה הקבוצה ממסד הנתונים, ואם יש רשומה, נעבוד איתה, אם לא, יוצרים אחד חדש. חשוב לציין שכדי לעבוד עם TelegramUser אנו משתמשים ב-TelegramUserService כדי לפעול לפי אחרון העקרונות המוצקים. כרגע, אם לא נמצא תיעוד לפי תעודת זהות, אני פשוט זורק חריג. זה לא מעובד בשום צורה עכשיו: אנחנו נעשה את זה ממש בסוף, לפני ה-MVP. בוא נכתוב שני בדיקות יחידה עבור המחלקה GroupSubServiceTest . איזה מהם אנחנו צריכים? אני רוצה להיות בטוח ששיטת השמירה תיקרא ב-GroupSubRepository וישות עם משתמש בודד תועבר ל-GroupSub - זו שתחזיר לנו את TelegramUserService באמצעות המזהה שסופק. והאפשרות השנייה, כאשר קבוצה עם אותו ID כבר נמצאת במסד הנתונים ולקבוצה הזו כבר יש משתמש אחד, וצריך לבדוק שעוד משתמש יתווסף לקבוצה הזו והאובייקט הזה יישמר. הנה היישום:
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);
   }

}
הוספתי גם את השיטה init() עם ההערה BeforeEach. בדרך זו הם יוצרים בדרך כלל שיטה שתתבצע לפני כל ריצת בדיקה, ואפשר להכניס בה היגיון משותף לכל הבדיקות. במקרה שלנו, אנחנו צריכים לנעול את TelegramUserService באותו אופן עבור כל הבדיקות של המחלקה הזו, ולכן הגיוני להעביר את ההיגיון הזה לשיטה נפוצה. יש שני עיצובים של mokito בשימוש כאן:
  • Mockito.when(o1.m1(a1)).thenReturn(o2) - בו אנו אומרים שכאשר מתודה m1 נקראת על אובייקט o1 עם ארגומנט a1 , השיטה תחזיר אובייקט o2 . זוהי כמעט הפונקציונליות החשובה ביותר של mockito - לאלץ את האובייקט המדומה להחזיר בדיוק את מה שאנחנו צריכים;

  • Mockito.verify(o1).m1(a1) - המאמת ששיטה m1 נקראה על אובייקט o1 עם ארגומנט a1 . אפשר היה כמובן להשתמש באובייקט המוחזר של שיטת השמירה, אבל החלטתי לעשות את זה קצת יותר מסובך על ידי הצגת שיטה אפשרית אחרת. מתי זה יכול להיות שימושי? במקרים בהם שיטות של כיתות מדומה חוזרות בטלות. ואז בלי Mockito.verify לא תהיה עבודה)))

אנחנו ממשיכים לדבוק ברעיון שצריך לכתוב מבחנים, והרבה מהם צריך להיכתב. השלב הבא הוא עבודה עם צוות הבוטים של טלגרם.

צור את הפקודה /addGroupSub

כאן אנחנו צריכים לבצע את ההיגיון הבא: אם נקבל רק פקודה, ללא שום הקשר, אנחנו עוזרים למשתמש ונותנים לו רשימה של כל הקבוצות עם המזהים שלהן כדי שיוכל להעביר את המידע הדרוש לבוט. ואם המשתמש שולח פקודה לבוט עם מילים אחרות, מצא קבוצה עם המזהה הזה או כתוב שאין קבוצה כזו. בואו נוסיף ערך חדש ב-ename שלנו - CommandName:
ADD_GROUP_SUB("/addgroupsub")
בואו נעבור הלאה ממסד הנתונים לבוט הטלגרם - צור את המחלקה AddGroupSubCommand בחבילת הפקודה:
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));
   }
}
מחלקה זו משתמשת בשיטת isNumeric מספריית apache-commons, אז בואו נוסיף אותה לזיכרון שלנו:
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-lang3</artifactId>
  <version>${apache.commons.version}</version>
</dependency>
ובגוש המאפיינים:
<apache.commons.version>3.11</apache.commons.version>
כל ההיגיון הזה נמצא בכיתה. קרא אותו בעיון. אם יש לכם שאלות/הצעות, כתבו אותן בתגובות. לאחר מכן, עלינו להוסיף את הפקודה ל-CommandContainer במפת הפקודות שלנו:
.put(ADD_GROUP_SUB.getCommandName(), new AddGroupSubCommand(sendBotMessageService, javaRushGroupClient, groupSubService))
והכל לצוות הזה. הייתי רוצה איכשהו לבדוק את הפונקציונליות הזו, אבל עד כה אני יכול להסתכל עליה באמת רק במסד הנתונים. בחלק השלישי, אוסיף שינויים מ-JRTB-6 כדי שנוכל לראות את רשימת הקבוצות בהן משתמש רשום. עכשיו יהיה טוב לבדוק את כל זה. לשם כך נבצע את כל הפעולות בטלגרם ונבדוק במסד הנתונים. מכיוון שכתבנו מבחנים, הכל אמור להיות בסדר. המאמר כבר די ארוך, אז נכתוב מבחן עבור AddGroupSubCommand בהמשך, ונוסיף TODO בקוד כדי לא לשכוח.

מסקנות

במאמר זה הסתכלנו על עבודת הוספת פונקציונליות דרך האפליקציה כולה, החל ממסד הנתונים וכלה בעבודה עם הלקוח המשתמש בבוט. בדרך כלל משימות כאלה עוזרות להבין את הפרויקט ולהבין את מהותו. להבין איך זה עובד. בימינו הנושאים לא קלים, אז אל תתביישו: כתבו את שאלותיכם בתגובות, ואנסה לענות עליהן. האם אתה אוהב את הפרויקט? תן לזה כוכב ב-Github : כך יהיה ברור שהם מעוניינים בפרויקט, ואני אשמח. כמו שאומרים, מאסטר תמיד מרוצה כשהעבודה שלו זוכה להערכה. הקוד יכיל את כל שלושת החלקים של STEP_6 ויהיה זמין לפני מאמר זה. איך לברר על זה? זה קל - הצטרף לערוץ הטלגרם , שם אני מפרסם את כל המידע על המאמרים שלי על בוט הטלגרם. תודה שקראת! חלק 3 כבר כאן .

רשימה של כל החומרים בסדרה נמצאת בתחילת מאמר זה.

הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION