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

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

Random-KO 그룹에 게시되었습니다
다시 안녕. 이것은 JRTB-6 작업 에 기능을 추가하는 방법에 대해 설명하는 STEP_6의 마지막 기사입니다 . 이전 두 기사( 1부 , 2부 )에서 우리는 이미 필요한 거의 모든 것을 준비했습니다. 이 부분이 프로세스의 정점입니다. 처음부터 지금까지 이 시리즈의 기사를 읽어주신 모든 분들께 큰 존경을 표합니다. 이는 당신이 훌륭한 직업을 찾을 만큼 충분한 동기를 갖고 있다는 것을 의미합니다. 이제 사업을 시작하겠습니다."A부터 Z까지의 Java 프로젝트": 기사 그룹을 구독하는 기능을 추가합니다.  파트 3 - 1

JRTB-6을 구현합니다

이번에는 텔레그램 봇 측에서 작업을 수행하겠습니다. 데이터베이스 업데이트 작업이 모두 완료되고 데이터베이스 엔터티가 구성되어 작업 준비가 되었기 때문입니다. CommandName - LIST_GROUP_SUB 에 새 값을 추가해 보겠습니다 .
LIST_GROUP_SUB("/listGroupSub");
ListGroupSubCommand 명령을 만들어 보겠습니다 .
package com.github.javarushcommunity.jrtb.command;

import com.github.javarushcommunity.jrtb.repository.entity.GroupSub;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import com.github.javarushcommunity.jrtb.service.TelegramUserService;
import org.telegram.telegrambots.meta.api.objects.Update;

import javax.ws.rs.NotFoundException;
import java.util.stream.Collectors;

import static com.github.javarushcommunity.jrtb.command.CommandUtils.getChatId;

/**
* {@link Command} for getting list of {@link GroupSub}.
*/
public class ListGroupSubCommand implements Command {

   private final SendBotMessageService sendBotMessageService;
   private final TelegramUserService telegramUserService;

   public ListGroupSubCommand(SendBotMessageService sendBotMessageService, TelegramUserService telegramUserService) {
       this.sendBotMessageService = sendBotMessageService;
       this.telegramUserService = telegramUserService;
   }

   @Override
   public void execute(Update update) {
       //todo add exception handling
       TelegramUser telegramUser = telegramUserService.findByChatId(getChatId(update))
               .orElseThrow(NotFoundException::new);

       String message = "Я нашел все подписки на группы: \n\n";
       String collectedGroups = telegramUser.getGroupSubs().stream()
               .map(it -> "Группа: " + it.getTitle() + " , ID = " + it.getId() + " \n")
               .collect(Collectors.joining());

       sendBotMessageService.sendMessage(telegramUser.getChatId(), message + collectedGroups);
   }
}
여기에서는 모든 것이 가능한 한 간단합니다. 기존 chat_id를 사용하여 사용자를 확보하고 그룹에 대한 모든 구독이 그의 개체에 수집됩니다. 우리는 이것을 두 번째 부분에서 설정했습니다. 작업 중에 나타날 수 있는 예외 처리를 추가하는 것을 잊지 않도록 //todo를 다시 추가했습니다. 다음 단계는 새 명령을 추가하여 CommandContainer를 업데이트하는 것입니다.
put(LIST_GROUP_SUB.getCommandName(), new GroupSubListCommand(sendBotMessageService, telegramUserService))
기본적으로는 그게 전부입니다. 이제 더 많은 테스트를 작성하고 /help 명령을 업데이트하고(새 명령에 대한 설명 추가) Telegram을 통해 새 기능을 테스트해야 합니다. ListGroupSubCommand 에 대한 테스트를 작성해 보겠습니다 . 명령의 논리가 일반적이지 않기 때문에 이전처럼 AbstractCommandTest 클래스 에 연결되지 않고 테스트를 작성합니다 .
package com.github.javarushcommunity.jrtb.command;

import com.github.javarushcommunity.jrtb.repository.entity.GroupSub;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import com.github.javarushcommunity.jrtb.service.TelegramUserService;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.telegram.telegrambots.meta.api.objects.Message;
import org.telegram.telegrambots.meta.api.objects.Update;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import static com.github.javarushcommunity.jrtb.command.CommandName.LIST_GROUP_SUB;

@DisplayName("Unit-level testing for ListGroupSubCommand")
public class ListGroupSubCommandTest {

   @Test
   public void shouldProperlyShowsListGroupSub() {
       //given
       TelegramUser telegramUser = new TelegramUser();
       telegramUser.setActive(true);
       telegramUser.setChatId("1");

       List<GroupSub> groupSubList = new ArrayList<>();
       groupSubList.add(populateGroupSub(1, "gs1"));
       groupSubList.add(populateGroupSub(2, "gs2"));
       groupSubList.add(populateGroupSub(3, "gs3"));
       groupSubList.add(populateGroupSub(4, "gs4"));

       telegramUser.setGroupSubs(groupSubList);

       SendBotMessageService sendBotMessageService = Mockito.mock(SendBotMessageService.class);
       TelegramUserService telegramUserService = Mockito.mock(TelegramUserService.class);

       Mockito.when(telegramUserService.findByChatId(telegramUser.getChatId())).thenReturn(Optional.of(telegramUser));

       ListGroupSubCommand command = new ListGroupSubCommand(sendBotMessageService, telegramUserService);

       Update update = new Update();
       Message message = Mockito.mock(Message.class);
       Mockito.when(message.getChatId()).thenReturn(Long.valueOf(telegramUser.getChatId()));
       Mockito.when(message.getText()).thenReturn(LIST_GROUP_SUB.getCommandName());
       update.setMessage(message);

       String collectedGroups = "Я нашел все подписки на группы: \n\n" +
               telegramUser.getGroupSubs().stream()
                       .map(it -> "Группа: " + it.getTitle() + " , ID = " + it.getId() + " \n")
                       .collect(Collectors.joining());

       //when
       command.execute(update);

       //then
       Mockito.verify(sendBotMessageService).sendMessage(telegramUser.getChatId(), collectedGroups);
   }

   private GroupSub populateGroupSub(Integer id, String title) {
       GroupSub gs = new GroupSub();
       gs.setId(id);
       gs.setTitle(title);
       return gs;
   }
}

/help 명령을 업데이트해 봅시다

우리의 경우 /help 명령은 봇 작업을 위한 문서 역할을 하므로 사용자가 사용할 수 있도록 업데이트해야 합니다. 두 개의 명령을 추가했으므로 앞으로 나올 텍스트를 업데이트해 보겠습니다.
public static final String HELP_MESSAGE = String.format("✨Дотупные команды✨\n\n"

               + "Начать\\закончить работу с ботом:\n"
               + "%s - начать работу со мной\n"
               + "%s - приостановить работу со мной\n\n"

               + "Работа с подписками на группы:\n"
               + "%s - подписаться на группу статей\n"
               + "%s - получить список групп, на которые подписан\n\n"

               + "%s - получить помощь в работе со мной\n"
               + "%s - получить мою статистику использования\n",
       START.getCommandName(), STOP.getCommandName(), ADD_GROUP_SUB.getCommandName(),
       LIST_GROUP_SUB.getCommandName(), HELP.getCommandName(), STAT.getCommandName());
또한 봇의 응답 텍스트도 업데이트했습니다. 사용자와 항상 이름을 사용하도록 만들었습니다. 그렇지 않으면 "당신"과 "당신"이 모두 있을 것입니다... 이제 최소한 봇 작업에 일종의 연결이 있습니다.

업데이트된 봇 테스트

우리는 로컬에서 봇을 시작하고 다음을 수행합니다.
  1. 테스트 케이스의 사용자가 데이터베이스에 추가되었는지 확인하기 위해 /start 명령을 실행합니다 .
  2. /help 명령을 실행합니다 . 원하는 대로 모든 것이 정상인지 확인합니다.
  3. 다음으로 /addGroupSub 명령을 실행합니다 .
  4. 제안된 그룹 ID 목록에서 몇 가지를 혼합에 추가합니다.
  5. /listGroupSub 명령을 실행하여 그룹이 사용자에게 등록되었는지 확인합니다.
가다! docker-compose-test.yml을 통해 데이터베이스를 시작하고 SpringBoot를 시작합니다. 그런 다음 테스트 봇으로 이동하여 /start 명령을 실행하고 /help 를 실행합니다 . 다음으로 /addGroupSub"A부터 Z까지의 Java 프로젝트": 기사 그룹을 구독하는 기능을 추가합니다.  파트 3 - 2 명령을 입력합니다 . 드롭다운 목록에는 Java 클라이언트가 정상적으로 작동하고 있다고 표시됩니다. ID, 명령 설명은 다음에 필요한 것이 무엇인지 이해하는 데 도움이 되므로 구독에 여러 그룹을 추가합니다. 이제 5개의 구독이 있으므로 /listGroupSub 명령을 실행할 수 있습니다 . 그러면 이상한 일이 발생합니다. ... 왜 제목이 아무 문제 없이 표시되었는지 는 확실하지 않지만 여기서는 표시되지 않습니다. 데이터베이스에 무엇이 있는지 살펴보겠습니다. 동일한 질문이 데이터베이스에 기록되어 있지만 키릴 문자를 사용하는 질문에만 해당됩니다. 이는 인코딩에 문제가 있음을 의미합니다. 분명히 데이터베이스에 연결하기 위해 데이터베이스와 드라이버를 구성해야 합니다. UTF-8이 필요합니다. 그런데 어떻게 추가하나요? 몇 분 동안 인터넷에서 검색한 후 드라이버가 url 변수를 업데이트해야 한다는 사실을 발견했습니다 . 그리고 docker-compose에서 docker 이미지를 설정하는 경우 가장 첫 번째 링크 이지만 대답은 첫 번째가 아닙니다.) 따라서 이를 알고 속성과 docker-compose 파일을 업데이트하겠습니다. "A부터 Z까지의 Java 프로젝트": 기사 그룹을 구독하는 기능을 추가합니다.  파트 3 - 3"A부터 Z까지의 Java 프로젝트": 기사 그룹을 구독하는 기능을 추가합니다.  파트 3 - 4"A부터 Z까지의 Java 프로젝트": 기사 그룹을 구독하는 기능을 추가합니다.  파트 3 - 5"A부터 Z까지의 Java 프로젝트": 기사 그룹을 구독하는 기능을 추가합니다.  파트 3 - 6
application.properties:
spring.datasource.url=jdbc:mysql://jrtb-db:3306/jrtb_db?characterEncoding=UTF-8

application-test.properties:
spring.datasource.url=jdbc:mysql://localhost:3306/dev_jrtb_db?characterEncoding=UTF-8

docker-compose.yml (добавил последнюю строку):
jrtb-db:
 image: mysql:5.7
 restart: always
 environment:
   MYSQL_USER: ${BOT_DB_USERNAME}
   MYSQL_PASSWORD: ${BOT_DB_PASSWORD}
   MYSQL_DATABASE: 'jrtb_db'
   MYSQL_ROOT_PASSWORD: 'root'
 ports:
   - '3306:3306'
 expose:
   - '3306'
 command: --character-set-server=utf8 --collation-server=utf8_general_ci

docker-compose-test.yml (добавил последнюю строку)
jrtb-db-dev:
 image: mysql:5.7
 restart: always
 environment:
   MYSQL_DATABASE: 'dev_jrtb_db'
   # So you don't have to use root, but you can if you like
   MYSQL_USER: 'dev_jrtb_db_user'
   # You can use whatever password you like
   MYSQL_PASSWORD: 'dev_jrtb_db_password'
   # Password for root access
   MYSQL_ROOT_PASSWORD: 'root'
 ports:
   # <Port exposed> : < MySQL Port running inside container>
   - '3306:3306'
 expose:
   # Opens port 3306 on the container
     - '3306'
 command: --character-set-server=utf8 --collation-server=utf8_general_ci
이러한 업데이트 후에는 데이터베이스의 모든 데이터를 지우고 다시 시작해야 합니다. 삭제는 매우 간단합니다. docker-compose -f docker-compose-test.yml down 명령을 실행 한 후 모든 데이터와 데이터베이스를 삭제 해야 합니다 . 그리고 업데이트된 인코딩을 사용하여 다시 실행합니다. docker-compose -f docker-compose-test.uml up 데이터베이스가 준비되었습니다. 업데이트된 애플리케이션을 실행하고 살펴보겠습니다. 신속하게 검토하고 결과를 보여 드리겠습니다. "A부터 Z까지의 Java 프로젝트": 기사 그룹을 구독하는 기능을 추가합니다.  파트 3 - 7이제 우리가 원하는 것을 정확하게 얻었습니다. 이제 이것이 진실처럼 보입니다.

종결

이제 이 단계를 완료할 수 있을 것 같습니다. 정말 많은 일이 이루어졌습니다. 애플리케이션 버전을 0.5.0-SNAPSHOT 및 RELEASE_NOTES 로 업데이트하겠습니다 .
# 릴리스 노트 ## 0.5.0-SNAPSHOT * JRTB-5: 그룹 구독 기능이 추가되었습니다. * JRTB-6: 그룹 구독 목록을 가져오는 기능이 추가되었습니다.
그런 다음 모든 것이 평소와 같습니다. 모든 변경 사항이 포함된 새 커밋을 만듭니다. 가장 중요한 것은 보고 목적으로 이 단계에서 완료한 두 가지 작업에 대한 설명을 추가하는 것입니다. 댓글은 다음과 같습니다.
STEP_6 JRTB-5: 그룹 구독 기능 추가 JRTB-6: 그룹 구독 목록을 볼 수 있는 기능 추가.
이로 인해 47개의 파일이 변경되었습니다. 이는 큰 변화입니다. 기능 설명으로는 알 수 없지만. 결국, 전체 내용을 이해하려면 API용 Java 클라이언트를 작성하고 본질적으로 전체 애플리케이션을 업데이트해야 한다는 점을 알아야 합니다. 서버에서 작업하는 방법은 다음과 같습니다. 작업은 많지만 클라이언트 측에서의 가시성은 작습니다...)) 친구 여러분, 저는 전통적으로 제 작업에 관심을 보일 수 있는 방법을 제공합니다. Github를 구독하세요 . 계정 , 텔레그램 채널에 가입 하고 명확하지 않은 내용이 있으면 기사에 대한 질문을 작성하세요! 다음은 STEP_6 에 대한 변경 사항이 포함된 풀 요청 링크입니다 . 읽어주신 모든 분들께 감사드립니다. 앞으로 더 많은 정보를 제공하겠습니다. 구독 삭제, 프로필 비활성화 등에 대해 이야기해 보겠습니다. 전환하지 마세요))

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

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