JavaRush /Blog Java /Random-VI /Chúng tôi đang thêm khả năng đăng ký một nhóm bài viết. (...
Roman Beekeeper
Mức độ

Chúng tôi đang thêm khả năng đăng ký một nhóm bài viết. (Phần 2) - "Dự án Java từ A đến Z"

Xuất bản trong nhóm
Chào mọi người! Chúng tôi tiếp tục thực hiện nhiệm vụ mà chúng tôi đã bắt đầu vào tuần trước ."Dự án Java từ A đến Z": Thêm tính năng đăng ký theo dõi một nhóm bài viết.  Phần 2 - 1

Chúng tôi triển khai JRTB-5

Bây giờ chúng ta cần thêm một lệnh để có thể đăng ký một số nhóm bài viết từ JavaRush. Làm thế nào để làm nó? Chúng ta sẽ làm theo kịch bản đơn giản nhất mà tôi nghĩ ra. Vì chúng tôi có quyền truy cập theo ID nhóm nên chúng tôi cần người dùng chuyển nó. Để thực hiện việc này, người dùng sẽ nhập lệnh /addGroupSub GROUP_ID, lệnh này sẽ hoạt động theo một trong hai cách: nếu chỉ có lệnh đó xuất hiện: /addGroupSub , danh sách tất cả các nhóm và ID của họ sẽ được gửi để phản hồi. Sau đó, người dùng sẽ có thể chọn ID nhóm mình cần và tạo phiên bản thứ hai của yêu cầu trong lệnh này: /addGroupSub GROUP_ID - và sau đó sẽ có một bản ghi của nhóm này với người dùng này. Tôi nghĩ chúng ta có thể làm tốt hơn trong tương lai. Mục tiêu của chúng tôi là thể hiện sự phát triển chứ không phải trải nghiệm người dùng cực kỳ thú vị (tôi rất xấu hổ khi phải nói điều này, nhưng tôi không biết thuật ngữ trong tiếng Nga có nghĩa này). Để thêm đúng chức năng xuyên suốt toàn bộ ứng dụng (trong trường hợp của chúng tôi, từ ứng dụng khách bot telegram đến cơ sở dữ liệu), bạn cần phải bắt đầu từ đầu nào đó. Chúng tôi sẽ làm điều này từ phía cơ sở dữ liệu.

Thêm một di chuyển mới vào cơ sở dữ liệu

Điều đầu tiên cần làm là thêm tính năng di chuyển cơ sở dữ liệu mới và khả năng lưu dữ liệu đăng ký nhóm người dùng trong JR. Để nhớ nó phải như thế nào, hãy quay lại bài viết “ Lập kế hoạch dự án: đo lường bảy lần .” Trong bức ảnh thứ hai có một sơ đồ gần đúng của cơ sở dữ liệu. Chúng ta cần thêm một bảng để lưu trữ thông tin nhóm:
  • ID nhóm trong JavaRush cũng sẽ là ID của chúng tôi. Chúng tôi tin tưởng họ và tin rằng những ID này là duy nhất;
  • tiêu đề - trong ảnh của chúng tôi, đó là tên - tên không chính thức của nhóm; tức là những gì chúng ta thấy trên trang web JavaRush;
  • Last_article_id - và đây là một lĩnh vực thú vị. Nó sẽ lưu trữ ID cuối cùng của bài viết trong nhóm này mà bot đã gửi cho những người đăng ký. Sử dụng trường này, cơ chế tìm kiếm bài viết mới sẽ hoạt động. Những người đăng ký mới sẽ không nhận được các bài viết được xuất bản trước khi người dùng đăng ký: chỉ những bài được xuất bản sau khi đăng ký vào nhóm.
Chúng ta cũng sẽ có mối quan hệ nhiều-nhiều giữa các nhóm và bảng người dùng, bởi vì mỗi người dùng có thể có nhiều đăng ký nhóm (một-nhiều) và mỗi đăng ký nhóm có thể có nhiều người dùng (một-nhiều, chỉ Mặt khác). Hóa ra đây sẽ là nhiều-nhiều của chúng tôi. Đối với những người có thắc mắc, hãy xem lại các bài viết trong cơ sở dữ liệu. Có, tôi dự định sẽ sớm tạo một bài đăng trên kênh Telegram, nơi tôi sẽ tổng hợp tất cả các bài viết trên cơ sở dữ liệu. Đây là giao diện di chuyển cơ sở dữ liệu thứ hai của chúng tôi.
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)
);
Điều quan trọng cần lưu ý là trước tiên tôi thay đổi bảng cũ - tôi thêm khóa chính vào đó. Bằng cách nào đó, tôi đã bỏ lỡ điều này vào thời điểm đó, nhưng bây giờ MySQL không cho tôi cơ hội thêm KHÓA NGOẠI TỆ cho bảng gorup_x_user và là một phần của quá trình di chuyển này, tôi đã cập nhật cơ sở dữ liệu. Xin lưu ý một khía cạnh quan trọng. Việc thay đổi cơ sở dữ liệu phải được thực hiện chính xác theo cách này - mọi thứ cần thiết đều có trong quá trình di chuyển mới, nhưng không phải bằng cách cập nhật quá trình di chuyển đã được phát hành. Có, trong trường hợp của chúng tôi sẽ không có gì xảy ra vì đây là một dự án thử nghiệm và chúng tôi biết rằng nó chỉ được triển khai ở một nơi, nhưng đây sẽ là một cách tiếp cận sai lầm. Nhưng chúng tôi muốn mọi thứ đều đúng. Tiếp theo là xóa bảng trước khi tạo chúng. Tại sao lại thế này? Vì vậy, nếu tình cờ có những bảng có tên như vậy trong cơ sở dữ liệu, quá trình di chuyển sẽ không thất bại và sẽ hoạt động chính xác như mong đợi. Và sau đó chúng tôi thêm hai bảng. Mọi thứ đều như chúng tôi mong muốn. Bây giờ chúng ta cần khởi chạy ứng dụng của mình. Nếu mọi thứ bắt đầu và không bị hỏng thì quá trình di chuyển sẽ được ghi lại. Và để kiểm tra kỹ điều này, chúng tôi vào cơ sở dữ liệu để đảm bảo rằng: a) các bảng như vậy đã xuất hiện; b) có mục mới trong bảng kỹ thuật đường bay. Điều này hoàn thành công việc di chuyển, hãy chuyển sang kho lưu trữ.

Thêm một lớp kho lưu trữ

Nhờ có Spring Boot Data, mọi thứ ở đây rất đơn giản: chúng ta cần thêm thực thể GroupSub, cập nhật một chút TelegramUser và thêm một GroupSubRepository gần như trống: Chúng ta thêm thực thể GroupSub vào cùng gói với 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);
   }
}
Một điều đáng chú ý là chúng tôi có một trường người dùng bổ sung sẽ chứa tập hợp tất cả người dùng đã đăng ký vào nhóm. Và hai chú thích - ManyToMany và JoinTable - chính xác là những gì chúng ta cần cho việc này. Trường tương tự cần được thêm cho TelegramUser:
@ManyToMany(mappedBy = "users", fetch = FetchType.EAGER)
private List<GroupSub> groupSubs;
Trường này sử dụng các phép nối được viết trong thực thể GroupSub. Và trên thực tế, lớp kho lưu trữ của chúng tôi dành cho GroupSub là 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> {
}
Ở giai đoạn này, chúng ta không cần các phương thức bổ sung: những phương thức được triển khai trong tổ tiên JpaRepository là đủ đối với chúng ta. Hãy viết một bài kiểm tra trong TelegramUserRepositoryIT để kiểm tra xem tính năng nhiều-nhiều của chúng tôi có hoạt động không. Ý tưởng của thử nghiệm là chúng tôi sẽ thêm 5 nhóm đăng ký cho mỗi người dùng vào cơ sở dữ liệu thông qua tập lệnh sql, lấy ID của người dùng này và kiểm tra xem chúng tôi đã nhận được chính xác các nhóm đó và có cùng giá trị hay chưa. Làm thế nào để làm nó? Bạn có thể nhúng bộ đếm vào dữ liệu để chúng tôi có thể xem xét và kiểm tra. Đây là tập lệnh 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);
Và bản thân bài kiểm tra:
@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());
   }
}
Bây giờ hãy thêm một bài kiểm tra có ý nghĩa tương tự cho thực thể GroupSub. Để làm điều này, hãy tạo một lớp thử nghiệm groupSubRepositoryIT trong cùng gói với 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());
       }
   }
}
Và tập lệnh fiveUsersForGroupSub.sql bị thiếu:
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);
Tại thời điểm này, một phần công việc với kho lưu trữ có thể được coi là đã hoàn thành. Bây giờ hãy viết một lớp dịch vụ.

Chúng tôi viết GroupSubService

Ở giai đoạn này, để làm việc với các nhóm đăng ký, chúng tôi chỉ cần lưu chúng, vì vậy không có vấn đề gì: chúng tôi tạo dịch vụ GroupSubService và triển khai GroupSubServiceImpl trong gói chứa các dịch vụ khác - dịch vụ:
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);
}
Và việc thực hiện nó:
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 hoạt động chính xác và tạo bản ghi nhiều-nhiều, chúng ta cần lấy người dùng từ cơ sở dữ liệu của mình cho nhóm đăng ký mà chúng ta đang tạo và thêm nó vào đối tượng GroupSub. Như vậy, khi chúng ta chuyển đăng ký này sang lưu, một kết nối cũng sẽ được tạo thông qua bảng group_x_user. Có thể xảy ra trường hợp một nhóm đăng ký như vậy đã được tạo và bạn chỉ cần thêm một người dùng khác vào đó. Để làm điều này, trước tiên chúng tôi lấy ID nhóm từ cơ sở dữ liệu và nếu có bản ghi, chúng tôi sẽ xử lý nó, nếu không, chúng tôi sẽ tạo một bản ghi mới. Điều quan trọng cần lưu ý là để làm việc với TelegramUser, chúng tôi sử dụng TelegramUserService để tuân theo nguyên tắc RẮN cuối cùng. Hiện tại, nếu chúng tôi không tìm thấy bản ghi theo ID, tôi sẽ đưa ra một ngoại lệ. Hiện tại nó không được xử lý theo bất kỳ cách nào: chúng tôi sẽ thực hiện việc này vào cuối cùng, trước MVP. Hãy viết hai bài kiểm tra đơn vị cho lớp GroupSubServiceTest . Chúng ta cần những cái nào? Tôi muốn chắc chắn rằng phương thức lưu sẽ được gọi trong GroupSubRepository và một thực thể có một người dùng sẽ được chuyển đến GroupSub - thực thể sẽ trả lại TelegramUserService cho chúng tôi bằng ID được cung cấp. Và tùy chọn thứ hai, khi một nhóm có cùng ID đã có trong cơ sở dữ liệu và nhóm này đã có một người dùng và bạn cần kiểm tra xem người dùng khác sẽ được thêm vào nhóm này và đối tượng này sẽ được lưu hay không. Đây là cách thực hiện:
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);
   }

}
Tôi cũng đã thêm phương thức init() với chú thích BeforeEach. Bằng cách này, bạn thường tạo một phương thức sẽ được thực thi trước mỗi lần chạy thử nghiệm và bạn có thể đưa logic chung vào đó cho tất cả các thử nghiệm. Trong trường hợp của chúng tôi, chúng tôi cần khóa TelegramUserService theo cách tương tự cho tất cả các thử nghiệm của lớp này, do đó, việc chuyển logic này sang một phương thức chung là hợp lý. Có hai thiết kế mokito được sử dụng ở đây:
  • Mockito.when(o1.m1(a1)).thenReturn(o2) - trong đó chúng ta nói rằng khi phương thức m1 được gọi trên đối tượng o1 với đối số a1 , phương thức sẽ trả về đối tượng o2 . Đây gần như là chức năng quan trọng nhất của mockito - buộc đối tượng giả trả về chính xác những gì chúng ta cần;

  • Mockito.verify(o1).m1(a1) - xác minh rằng phương thức m1 đã được gọi trên đối tượng o1 với đối số a1 . Tất nhiên, có thể sử dụng đối tượng được trả về của phương thức lưu, nhưng tôi quyết định làm cho nó phức tạp hơn một chút bằng cách hiển thị một phương thức khả thi khác. Khi nào nó có thể hữu ích? Trong trường hợp các phương thức của lớp mô phỏng trả về void. Thế thì không có Mockito.verify thì sẽ không có việc gì)))

Chúng tôi tiếp tục tuân thủ ý tưởng rằng các bài kiểm tra cần phải được viết và rất nhiều bài kiểm tra trong số đó cần phải được viết. Giai đoạn tiếp theo là làm việc với nhóm bot telegram.

Tạo lệnh /addGroupSub

Ở đây, chúng ta cần thực hiện logic sau: nếu chúng ta chỉ nhận được một lệnh, không có bất kỳ ngữ cảnh nào, chúng ta sẽ trợ giúp người dùng và cung cấp cho anh ta danh sách tất cả các nhóm có ID của họ để anh ta có thể chuyển thông tin cần thiết cho bot. Và nếu người dùng gửi lệnh tới bot bằng (các) từ khác - hãy tìm một nhóm có ID đó hoặc viết rằng không có nhóm nào như vậy. Hãy thêm một giá trị mới vào ename của chúng ta - CommandName:
ADD_GROUP_SUB("/addgroupsub")
Hãy chuyển xa hơn từ cơ sở dữ liệu sang bot telegram - tạo lớp AddGroupSubCommand trong gói lệnh:
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));
   }
}
Lớp này sử dụng phương thức isNumeric từ thư viện apache-commons, vì vậy hãy thêm nó vào bộ nhớ của chúng ta:
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-lang3</artifactId>
  <version>${apache.commons.version}</version>
</dependency>
Và trong khối thuộc tính:
<apache.commons.version>3.11</apache.commons.version>
Tất cả logic này đều có trong lớp. Đọc nó cẩn thận. Nếu bạn có bất kỳ câu hỏi/đề xuất nào, hãy viết chúng trong phần bình luận. Sau này, chúng ta cần thêm lệnh vào CommandContainer trong bản đồ lệnh của mình:
.put(ADD_GROUP_SUB.getCommandName(), new AddGroupSubCommand(sendBotMessageService, javaRushGroupClient, groupSubService))
Và mọi thứ cho đội này. Tôi muốn bằng cách nào đó kiểm tra chức năng này, nhưng cho đến nay tôi thực sự chỉ có thể xem xét nó trong cơ sở dữ liệu. Trong phần ba, tôi sẽ thêm các thay đổi từ JRTB-6 để chúng ta có thể xem danh sách các nhóm mà người dùng đã đăng ký. Bây giờ sẽ tốt hơn nếu kiểm tra tất cả điều này. Để thực hiện việc này, chúng tôi sẽ thực hiện tất cả các hành động trong Telegram và kiểm tra cơ sở dữ liệu. Vì chúng tôi đã có bài kiểm tra viết nên mọi thứ sẽ ổn. Bài viết đã khá dài nên chúng ta sẽ viết test cho AddGroupSubCommand sau, đồng thời thêm TODO vào code để không quên.

kết luận

Trong bài viết này, chúng ta đã xem xét công việc bổ sung chức năng thông qua toàn bộ ứng dụng, bắt đầu từ cơ sở dữ liệu và kết thúc bằng việc làm việc với khách hàng sử dụng bot. Thông thường những nhiệm vụ như vậy giúp hiểu dự án và hiểu bản chất của nó. Hiểu cách nó hoạt động. Ngày nay, các chủ đề không hề dễ dàng, vì vậy đừng ngại: hãy viết câu hỏi của bạn trong phần bình luận và tôi sẽ cố gắng trả lời chúng. Bạn có thích dự án này không? Hãy tặng nó một ngôi sao trên Github : bằng cách này, rõ ràng là họ quan tâm đến dự án và tôi sẽ rất vui. Như người ta nói, một người chủ luôn hài lòng khi tác phẩm của mình được đánh giá cao. Mã sẽ chứa cả ba phần của STEP_6 và sẽ có sẵn trước bài viết này. Làm thế nào để tìm hiểu về nó? Thật dễ dàng - hãy tham gia kênh telegram , nơi tôi đăng tất cả thông tin về các bài viết của mình về bot telegram. Cảm ơn vì đã đọc! Phần 3 đã có ở đây rồi .

Danh sách tất cả các tài liệu trong loạt bài này nằm ở đầu bài viết này.

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION