JavaRush /مدونة جافا /Random-AR /نحن نضيف القدرة على الاشتراك في مجموعة من المقالات. (الجز...
Roman Beekeeper
مستوى

نحن نضيف القدرة على الاشتراك في مجموعة من المقالات. (الجزء الثاني) - "مشروع جافا من الألف إلى الياء"

نشرت في المجموعة
أهلاً بكم! نواصل العمل على المهمة التي بدأناها الأسبوع الماضي ."مشروع جافا من الألف إلى الياء": إضافة إمكانية الاشتراك في مجموعة من المقالات.  الجزء 2 - 1

نحن ننفذ JRTB-5

نحتاج الآن إلى إضافة أمر حتى نتمكن من الاشتراك في مجموعة من المقالات من JavaRush. كيف افعلها؟ سوف نتبع أبسط السيناريو الذي توصلت إليه. نظرًا لأن لدينا إمكانية الوصول عن طريق معرف المجموعة، فنحن بحاجة إلى أن يقوم المستخدم بنقله. للقيام بذلك، سيقوم المستخدم بإدخال الأمر /addGroupSub GROUP_ID، والذي سيعمل بإحدى طريقتين: إذا جاء الأمر نفسه فقط: /addGroupSub ، فسيتم إرسال قائمة بجميع المجموعات ومعرفاتها ردًا على ذلك. بعد ذلك سيتمكن المستخدم من تحديد معرف المجموعة الذي يحتاجه وإنشاء الإصدار الثاني من الطلب في هذا الأمر: /addGroupSub GROUP_ID - وبعد ذلك سيكون هناك سجل لهذه المجموعة مع هذا المستخدم. أعتقد أنه يمكننا القيام بعمل أفضل في المستقبل. هدفنا هو إظهار التطور، وليس تجربة المستخدم الرائعة (أخجل من قول ذلك، لكنني لا أعرف المصطلح باللغة الروسية الذي يعني هذا). لإضافة وظيفة تمر عبر التطبيق بأكمله بشكل صحيح (في حالتنا، من عميل Telegram bot إلى قاعدة البيانات)، عليك أن تبدأ من نقطة ما. سنفعل هذا من جانب قاعدة البيانات.

إضافة ترحيل جديد إلى قاعدة البيانات

أول شيء يجب فعله هو إضافة ترحيل قاعدة بيانات جديدة والقدرة على حفظ بيانات اشتراك مجموعة المستخدمين في JR. لتتذكر كيف ينبغي أن يكون، ارجع إلى مقال " تخطيط المشروع: قم بالقياس سبع مرات ". يوجد في الصورة الثانية رسم تخطيطي تقريبي لقاعدة البيانات. نحتاج إلى إضافة جدول لتخزين معلومات المجموعة:
  • سيكون معرف المجموعة في JavaRush هو معرفنا أيضًا. نحن نثق بهم ونعتقد أن هذه المعرفات فريدة من نوعها؛
  • العنوان - في صورنا كان الاسم - الاسم غير الرسمي للمجموعة؛ أي ما نراه على موقع JavaRush؛
  • last_article_id - وهذا مجال مثير للاهتمام. سيقوم بتخزين المعرف الأخير للمقال في هذه المجموعة، والذي أرسله الروبوت بالفعل إلى المشتركين فيه. باستخدام هذا الحقل ستعمل آلية البحث عن المقالات الجديدة. لن يتلقى المشتركون الجدد المقالات المنشورة قبل اشتراك المستخدم: فقط تلك التي تم نشرها بعد الاشتراك في المجموعة.
سيكون لدينا أيضًا علاقة أطراف بأطراف بين جدول المجموعات والمستخدمين، لأن كل مستخدم يمكن أن يكون لديه العديد من اشتراكات المجموعة (واحد لأكثر)، ويمكن أن يحتوي كل اشتراك مجموعة على العديد من المستخدمين (واحد لأكثر، فقط على الجانب الآخر). وتبين أن هذا سيكون لدينا كثير لكثير. لمن لديه أسئلة، قم بمراجعة المقالات الموجودة في قاعدة البيانات. نعم، أخطط قريبًا لإنشاء منشور على قناة Telegram، حيث سأقوم بتجميع جميع المقالات في قاعدة البيانات. هذا هو الشكل الذي ستبدو عليه عملية ترحيل قاعدة البيانات الثانية.
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 الآن لم تمنحني الفرصة لإضافة مفتاح أجنبي لجدول gorup_x_user، وكجزء من عملية الترحيل هذه قمت بتحديث قاعدة البيانات. يرجى ملاحظة جانب مهم. يجب أن يتم تغيير قاعدة البيانات بهذه الطريقة تمامًا - كل ما هو مطلوب موجود في الترحيل الجديد، ولكن ليس عن طريق تحديث الترحيل الذي تم إصداره بالفعل. نعم، في حالتنا لن يحدث شيء، لأن هذا مشروع اختباري ونعلم أنه تم نشره في مكان واحد فقط، لكن هذا سيكون نهجًا خاطئًا. لكننا نريد أن يكون كل شيء على ما يرام. بعد ذلك يأتي حذف الجداول قبل إنشائها. لماذا هذا؟ لذا، إذا كانت هناك جداول تحمل هذه الأسماء في قاعدة البيانات، بالصدفة، فلن تفشل عملية الترحيل وستعمل تمامًا كما هو متوقع. ثم نضيف جدولين. كل شيء كان كما أردنا. الآن نحن بحاجة إلى تشغيل تطبيقنا. إذا بدأ كل شيء ولم ينقطع، فسيتم تسجيل الهجرة. وللتأكد من ذلك، نذهب إلى قاعدة البيانات للتأكد من: أ) ظهور هذه الجداول؛ ب) يوجد إدخال جديد في الجدول الفني لمسار الهجرة. بهذا تنتهي عملية الترحيل، فلننتقل إلى المستودعات.

إضافة طبقة المستودع

بفضل Spring Boot Data، كل شيء بسيط للغاية هنا: نحتاج إلى إضافة كيان GroupSub، وتحديث TelegramUser قليلاً وإضافة GroupSubRepository فارغ تقريبًا: نضيف كيان 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 لاتباع آخر مبادئ SOLID. في الوقت الحالي، إذا لم نتمكن من العثور على سجل حسب المعرف، فأنا أقوم فقط بإجراء استثناء. لا تتم معالجتها بأي شكل من الأشكال الآن: سنفعل ذلك في النهاية، قبل MVP. لنكتب اختبارين للوحدة لفئة GroupSubServiceTest . أي منها نحتاج؟ أريد التأكد من أنه سيتم استدعاء طريقة الحفظ في GroupSubRepository وسيتم تمرير الكيان الذي يحتوي على مستخدم واحد إلى GroupSub - الكيان الذي سيعيد TelegramUserService إلينا باستخدام المعرف المقدم. والخيار الثاني، عندما تكون المجموعة التي لها نفس المعرف موجودة بالفعل في قاعدة البيانات وهذه المجموعة بها مستخدم واحد بالفعل، وتحتاج إلى التحقق من إضافة مستخدم آخر إلى هذه المجموعة وسيتم حفظ هذا الكائن. وهنا التنفيذ:
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. تحقق لن يكون هناك عمل)))

نحن مستمرون في التمسك بفكرة أن الاختبارات يجب أن تكون مكتوبة، وأن الكثير منها يجب أن يكون مكتوبًا. المرحلة التالية هي العمل مع فريق بوت التليجرام.

قم بإنشاء الأمر /addGroupSub

نحتاج هنا إلى تنفيذ المنطق التالي: إذا تلقينا أمرًا فقط، دون أي سياق، فإننا نساعد المستخدم ونمنحه قائمة بجميع المجموعات مع معرفاتها حتى يتمكن من تمرير المعلومات الضرورية إلى الروبوت. وإذا أرسل المستخدم أمرًا إلى الروبوت مع بعض الكلمات (الكلمات) الأخرى، فابحث عن مجموعة بهذا المعرف أو اكتب أنه لا توجد مثل هذه المجموعة. دعونا نضيف قيمة جديدة في اسمنا - 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 حتى نتمكن من عرض قائمة المجموعات التي يشترك فيها المستخدم. الآن سيكون من الجيد التحقق من كل هذا. للقيام بذلك، سنقوم بتنفيذ جميع الإجراءات في Telegram والتحقق من قاعدة البيانات. منذ أن كتبنا الاختبارات، يجب أن يكون كل شيء على ما يرام. المقالة طويلة جدًا بالفعل، لذا سنكتب اختبارًا لـ AddGroupSubCommand لاحقًا، ونضيف TODO في الكود حتى لا ننسى.

الاستنتاجات

في هذه المقالة، نظرنا في عمل إضافة الوظائف من خلال التطبيق بأكمله، بدءًا من قاعدة البيانات وانتهاءً بالعمل مع العميل الذي يستخدم الروبوت. عادة ما تساعد مثل هذه المهام على فهم المشروع وفهم جوهره. افهم كيف يعمل. المواضيع هذه الأيام ليست سهلة، فلا تخجل: اكتب أسئلتك في التعليقات، وسأحاول الإجابة عليها. هل يعجبك المشروع؟ أعطه نجمة على Github : بهذه الطريقة سيكون من الواضح أنهم مهتمون بالمشروع، وسأكون سعيدًا. وكما يقولون، فإن المعلم يكون سعيدًا دائمًا عندما يتم تقدير عمله. سيحتوي الكود على الأجزاء الثلاثة من STEP_6 وسيكون متاحًا قبل هذه المقالة. كيف تعرف ذلك؟ الأمر سهل - انضم إلى قناة التليجرام ، حيث أنشر جميع المعلومات حول مقالاتي حول روبوت التليجرام. شكرا للقراءة! الجزء 3 موجود بالفعل هنا .

توجد قائمة بجميع المواد الموجودة في السلسلة في بداية هذه المقالة.

تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION