JavaRush /Java Blog /Random-KO /기사 그룹을 구독하는 기능을 추가하고 있습니다. (2부) - "Java 프로젝트 A부터 Z까지"
Roman Beekeeper
레벨 35

기사 그룹을 구독하는 기능을 추가하고 있습니다. (2부) - "Java 프로젝트 A부터 Z까지"

Random-KO 그룹에 게시되었습니다
안녕하세요 여러분! 우리는 지난 주 에 시작한 작업을 계속해서 수행하고 있습니다 ."A부터 Z까지의 Java 프로젝트": 기사 그룹을 구독하는 기능을 추가합니다.  파트 2 - 1

JRTB-5를 구현합니다

이제 JavaRush의 일부 기사 그룹을 구독할 수 있도록 명령을 추가해야 합니다. 어떻게 하나요? 제가 생각해낸 가장 간단한 시나리오를 따르겠습니다. 그룹 ID별로 접근이 가능하기 때문에 사용자의 이동이 필요합니다. 이를 위해 사용자는 /addGroupSub GROUP_ID 명령을 입력합니다. 이 명령은 두 가지 방법 중 하나로 작동합니다. 명령 자체만 오는 경우: /addGroupSub 모든 그룹 및 해당 ID 목록이 응답으로 전송됩니다. 그런 다음 사용자는 필요한 그룹 ID를 선택하고 다음 명령에서 요청의 두 번째 버전을 생성할 수 있습니다. /addGroupSub GROUP_ID - 그러면 이 사용자와 함께 이 그룹에 대한 항목이 있게 됩니다. 앞으로는 더 잘할 수 있을 것 같아요. 우리의 목표는 멋진 사용자 경험이 아닌 개발 과정을 보여주는 것입니다(말하기 부끄럽지만 러시아어로 이를 의미하는 용어는 모르겠습니다). 전체 애플리케이션(우리의 경우 텔레그램 봇 클라이언트에서 데이터베이스까지)을 통과하는 기능을 적절하게 추가하려면 어느 쪽 끝에서 시작해야 합니다. 우리는 데이터베이스 측에서 이 작업을 수행할 것입니다.

데이터베이스에 새 마이그레이션 추가

가장 먼저 해야 할 일은 새로운 데이터베이스 마이그레이션과 JR에 사용자 그룹 구독 데이터를 저장하는 기능을 추가하는 것입니다. 어떻게 해야 하는지 기억하려면 " 프로젝트 계획: 일곱 번 측정 " 기사로 돌아가세요. 두 번째 사진에는 데이터베이스의 대략적인 다이어그램이 있습니다. 그룹 정보를 저장하려면 테이블을 추가해야 합니다.
  • JavaRush의 그룹 ID는 우리의 ID이기도 합니다. 우리는 이를 신뢰하고 이러한 ID가 고유하다고 믿습니다.
  • 제목 - 우리 사진에서는 이름이었습니다 - 그룹의 비공식 이름입니다. 즉, JavaRush 웹사이트에서 볼 수 있는 것입니다.
  • last_article_id - 이것은 흥미로운 필드입니다. 봇이 이미 구독자에게 보낸 기사의 마지막 ID를 이 그룹에 저장합니다. 이 필드를 사용하면 새 기사를 검색하는 메커니즘이 작동합니다. 신규 구독자는 사용자가 구독하기 전에 게시된 기사를 받을 수 없으며, 그룹을 구독한 후에 게시된 기사만 받을 수 있습니다.
또한 각 사용자는 많은 그룹 구독(일대다)을 가질 수 있고 각 그룹 구독은 많은 사용자(일대다, 오직 반대편). 이것이 우리의 다대다(many-to-many)가 될 것으로 밝혀졌습니다. 질문이 있는 경우 데이터베이스에 있는 기사를 검토하세요. 예, 곧 텔레그램 채널에 게시물을 작성하여 데이터베이스의 모든 기사를 정리할 계획입니다. 이것이 두 번째 데이터베이스 마이그레이션의 모습입니다.
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 테이블에 FOREIGN KEY를 추가할 기회를 주지 않았고 이 마이그레이션의 일부로 데이터베이스를 업데이트했습니다. 중요한 점을 참고하시기 바랍니다. 데이터베이스 변경은 정확히 이 방법으로 수행되어야 합니다. 필요한 모든 것은 새 마이그레이션에 있지만 이미 릴리스된 마이그레이션을 업데이트하는 것은 아닙니다. 예, 우리의 경우에는 아무 일도 일어나지 않을 것입니다. 왜냐하면 이것은 테스트 프로젝트이고 한 곳에만 배포된다는 것을 알고 있기 때문입니다. 그러나 이는 잘못된 접근 방식입니다. 그러나 우리는 모든 것이 옳기를 원합니다. 다음은 테이블을 생성하기 전에 테이블을 삭제하는 것입니다. 왜 이런거야? 따라서 만약 데이터베이스에 그러한 이름을 가진 테이블이 있는 경우 마이그레이션이 실패하지 않고 예상대로 정확하게 작동할 것입니다. 그런 다음 두 개의 테이블을 추가합니다. 모든 것이 우리가 원하는 대로였습니다. 이제 애플리케이션을 실행해야 합니다. 모든 것이 시작되고 중단되지 않으면 마이그레이션이 기록됩니다. 그리고 이를 다시 확인하기 위해 데이터베이스로 이동하여 다음 사항을 확인합니다. a) 그러한 테이블이 나타났습니다. b) 이동경로 기술 테이블에 새로운 항목이 있습니다. 이것으로 마이그레이션 작업이 완료되었습니다. 리포지토리로 이동하겠습니다.

저장소 계층 추가

Spring Boot Data 덕분에 여기에서는 모든 것이 매우 간단합니다. GroupSub 엔터티를 추가하고 TelegramUser를 약간 업데이트한 다음 거의 비어 있는 GroupSubRepository를 추가해야 합니다. TelegramUser와 동일한 패키지에 GroupSub 엔터티를 추가합니다.
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에 작성해 보겠습니다. 테스트의 아이디어는 SQL 스크립트를 통해 사용자당 5개의 구독 그룹을 데이터베이스에 추가하고, 이 사용자의 ID를 가져와서 정확히 동일한 값으로 해당 그룹을 받았는지 확인하는 것입니다. 어떻게 하나요? 데이터에 카운터를 삽입하면 이를 검토하고 확인할 수 있습니다. 다음은 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 테이블을 통해서도 연결이 생성됩니다. 그러한 구독 그룹이 이미 생성되어 있고 여기에 다른 사용자를 추가하기만 하면 되는 상황이 있을 수 있습니다. 이를 위해 먼저 데이터베이스에서 그룹 ID를 가져와서 레코드가 있으면 작업하고, 없으면 새 레코드를 만듭니다. TelegramUser와 작업하려면 TelegramUserService를 사용하여 마지막 SOLID 원칙을 따른다는 점에 유의하는 것이 중요합니다. 현재로서는 ID별로 레코드를 찾지 못하면 간단히 예외를 발생시킵니다. 지금은 어떤 식으로든 처리되지 않습니다. MVP 이전에 이 작업을 마지막에 처리할 것입니다. GroupSubServiceTest 클래스 에 대한 두 개의 단위 테스트를 작성해 보겠습니다 . 어떤 것이 필요합니까? GroupSubRepository에서 save 메소드가 호출되고 단일 사용자가 있는 엔터티가 제공된 ID를 사용하여 TelegramUserService를 우리에게 반환하는 GroupSub에 전달되는지 확인하고 싶습니다. 두 번째 옵션은 동일한 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);
   }

}
또한 BeforeEach 주석과 함께 init() 메서드를 추가했습니다 . 이러한 방식으로 그들은 일반적으로 각 테스트가 실행되기 전에 실행될 메서드를 생성하고 모든 테스트에 대해 공통 논리를 넣는 것이 가능합니다. 우리의 경우 이 클래스의 모든 테스트에 대해 동일한 방식으로 TelegramUserService를 잠가야 하므로 이 논리를 공통 메서드로 전송하는 것이 합리적입니다. 여기에는 두 가지 mokito 디자인이 사용됩니다.
  • Mockito.when(o1.m1(a1)).thenReturn(o2) - 여기서는 m1 메서드가 객체 o1 에서 인수 a1 을 사용 하여 호출 될 때 해당 메서드가 객체 o2를 반환한다고 말합니다 . 이것은 mockito의 가장 중요한 기능입니다. 즉, 모의 객체가 우리에게 필요한 것을 정확히 반환하도록 강제하는 것입니다.

  • Mockito.verify(o1).m1(a1) - 메소드 m1이 인수 a1 을 사용하여 객체 o1 에서 호출되었는지 확인합니다 . 물론 save 메소드의 반환된 객체를 사용하는 것도 가능하지만, 다른 가능한 메소드를 보여줌으로써 좀 더 복잡하게 만들기로 했습니다. 언제 유용할 수 있나요? 모의 클래스의 메서드가 void를 반환하는 경우. 그런 다음 Mockito.verify가 없으면 작업이 없습니다.)))

우리는 테스트를 작성해야 하고, 많은 테스트를 작성해야 한다는 생각을 계속해서 고수하고 있습니다. 다음 단계는 텔레그램 봇 팀과 협력하는 것입니다.

/addGroupSub 명령을 생성합니다.

여기서는 다음 논리를 수행해야 합니다. 컨텍스트 없이 명령만 받으면 사용자를 돕고 필요한 정보를 봇에 전달할 수 있도록 ID가 포함된 모든 그룹 목록을 사용자에게 제공합니다. 그리고 사용자가 다른 단어와 함께 봇에 명령을 보내는 경우 해당 ID를 가진 그룹을 찾거나 그러한 그룹이 없다고 기록하십시오. 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));
   }
}
이 클래스는 apache-commons 라이브러리의 isNumeric 메소드를 사용하므로 메모리에 추가해 보겠습니다.
<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))
그리고 이 팀을 위한 모든 것. 어떻게든 이 기능을 테스트하고 싶지만 지금까지는 실제로 데이터베이스에서만 볼 수 있습니다. 3부에서는 사용자가 가입한 그룹 목록을 볼 수 있도록 JRTB-6의 변경 사항을 추가하겠습니다. 이제 이 모든 것을 확인하는 것이 좋을 것입니다. 이를 위해 Telegram에서 모든 작업을 수행하고 데이터베이스를 확인합니다. 테스트를 작성했으므로 모든 것이 괜찮을 것입니다. 글이 이미 꽤 길어서 나중에 AddGroupSubCommand에 대한 테스트를 작성하고, 잊지 않도록 코드에 TODO를 추가하겠습니다.

결론

이번 글에서는 데이터베이스부터 시작하여 봇을 사용하는 클라이언트와의 작업까지 전체 애플리케이션을 통해 기능을 추가하는 작업을 살펴보았습니다. 일반적으로 이러한 작업은 프로젝트를 이해하고 그 본질을 이해하는 데 도움이 됩니다. 그것이 어떻게 작동하는지 이해하십시오. 요즘은 주제가 쉽지 않으니 부끄러워하지 마시고 댓글로 궁금한 점을 적어주시면 답변해 드리겠습니다. 프로젝트가 마음에 드시나요? Github에 별표를 주세요 . 이렇게 하면 그들이 프로젝트에 관심이 있다는 것이 분명해지고 저도 기뻐할 것입니다. 그들이 말했듯이, 주인은 자신의 작업이 평가될 때 항상 기뻐합니다. 코드에는 STEP_6의 세 부분이 모두 포함되며 이 기사 이전에 제공될 예정입니다. 그것에 대해 알아내는 방법? 쉽습니다. 텔레그램 봇에 관한 내 기사에 대한 모든 정보를 게시하는 텔레그램 채널 에 가입하세요. 읽어 주셔서 감사합니다! 3부는 이미 나와 있습니다 .

시리즈의 모든 자료 목록은 이 기사의 시작 부분에 있습니다.

코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION