JavaRush /وبلاگ جاوا /Random-FA /ما در حال اضافه کردن قابلیت اشتراک در گروهی از مقالات هست...
Roman Beekeeper
مرحله

ما در حال اضافه کردن قابلیت اشتراک در گروهی از مقالات هستیم. (قسمت 2) - "پروژه جاوا از A تا Z"

در گروه منتشر شد
سلام به همه! ما بر روی کاری که هفته گذشته شروع کردیم به کار ادامه می دهیم ."پروژه جاوا از A تا Z": افزودن قابلیت اشتراک در گروهی از مقالات.  قسمت 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 این فرصت را به من نداد که یک کلید خارجی برای جدول 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());
       }
   }
}
و اسکریپت پنجUsersForGroupSub.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 را با استفاده از 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 را برای تمام تست های این کلاس به یک شکل قفل کنیم، بنابراین منطقی است که این منطق را به یک متد معمولی منتقل کنیم. دو طرح موکیتو در اینجا استفاده می شود:
  • Mockito.when(o1.m1(a1)).thenReturn(o2) - در آن می گوییم وقتی متد m1 روی شی o1 با آرگومان a1 فراخوانی می شود ، متد شی o2 را برمی گرداند . این تقریباً مهمترین عملکرد mockito است - مجبور کردن شیء ساختگی دقیقاً همان چیزی را که ما نیاز داریم برگرداند.

  • Mockito.verify(o1).m1(a1) - که تأیید می کند که متد m1 در شی o1 با آرگومان a1 فراخوانی شده است . البته امکان استفاده از شی برگردانده شده از متد save وجود داشت، اما تصمیم گرفتم با نشان دادن روش ممکن دیگر، آن را کمی پیچیده تر کنم. چه زمانی می تواند مفید باشد؟ در مواردی که متدهای کلاس های ساختگی به حالت خالی باز می گردند. سپس بدون Mockito.verify هیچ کاری وجود نخواهد داشت)))

ما همچنان به این ایده پایبند هستیم که تست ها باید نوشته شوند و بسیاری از آنها باید نوشته شوند. مرحله بعدی کار با تیم ربات تلگرام است.

دستور /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 اضافه می‌کنم تا بتوانیم لیست گروه‌هایی را که یک کاربر در آنها مشترک است، مشاهده کنیم. حالا خوب است همه اینها را بررسی کنید. برای این کار تمام اعمال را در تلگرام انجام می دهیم و در دیتابیس چک می کنیم. از آنجایی که ما تست های کتبی داریم، همه چیز باید خوب باشد. مقاله در حال حاضر بسیار طولانی است، بنابراین ما بعداً یک آزمایش برای AddGroupSubCommand خواهیم نوشت و TODO را در کد اضافه می کنیم تا فراموش نشود.

نتیجه گیری

در این مقاله، کار اضافه کردن قابلیت از طریق کل برنامه را بررسی کردیم که از پایگاه داده شروع می شود و به کار با مشتری که از ربات استفاده می کند ختم می شود. معمولاً چنین وظایفی به درک پروژه و درک ماهیت آن کمک می کند. درک کنید که چگونه کار می کند. این روزها موضوعات آسان نیستند، بنابراین خجالتی نباشید: سوالات خود را در نظرات بنویسید و من سعی خواهم کرد به آنها پاسخ دهم. آیا پروژه را دوست دارید؟ در Github به آن ستاره بدهید : به این ترتیب مشخص می شود که آنها به پروژه علاقه مند هستند و من خوشحال خواهم شد. همانطور که می گویند استاد همیشه وقتی از کارش قدردانی می شود خوشحال می شود. کد شامل هر سه قسمت STEP_6 خواهد بود و قبل از این مقاله در دسترس خواهد بود. چگونه از آن مطلع شویم؟ آسان است - به کانال تلگرام بپیوندید ، جایی که من تمام اطلاعات مقالات خود را در مورد ربات تلگرام منتشر می کنم. با تشکر برای خواندن! قسمت 3 قبلاً اینجاست .

فهرستی از تمام مواد این مجموعه در ابتدای این مقاله است.

نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION