JavaRush /Blogue Java /Random-PT /Adicionando um cliente aos artigos - "Projeto Java de A a...
Roman Beekeeper
Nível 35

Adicionando um cliente aos artigos - "Projeto Java de A a Z"

Publicado no grupo Random-PT
Olá a todos, meus queridos amigos. Passo a passo estamos nos aproximando do nosso objetivo - nos tornarmos o MVP do nosso projeto - JavaRush Telegram Bot. Como disse no último artigo, restam apenas 5 tarefas. Hoje vamos cobrir dois deles. "Projeto Java de A a Z": Adicionando um cliente aos artigos - 1Quero repetir que o projeto não terminará aqui. Ainda tenho muitas ideias e visões de como este projeto deve se desenvolver, que coisas novas podem ser acrescentadas a ele, o que pode ser feito melhor. Antes do MVP, faremos um artigo separado sobre refatoração - ou seja, sobre como melhorar a qualidade do código sem alterar sua funcionalidade. Nesse momento, todo o projeto estará visível e ficará claro o que e onde pode ser melhorado. No nosso caso, estaremos protegidos ao máximo contra quebras de funcionalidade, pois muitos testes foram escritos. Também escreveremos uma retrospectiva sobre o que queríamos e o que conseguimos no final. Isso é uma coisa muito útil: vamos ver como tudo foi visto corretamente há seis meses. Pelo menos isso é muito interessante para mim. Se alguém quiser tentar ser um testador manual, escreva para nós e colaboraremos. Vamos tornar este projeto melhor juntos! Então, aqui estão elas: duas tarefas descritas há seis meses: JRTB-8 e JRTB-9 . Comecei a olhar o que precisava ser implementado para essas tarefas e percebi que em termos de lançamento de comandos já estava tudo pronto. Acontece...) Aqui, você pode ver o StartCommand , o método execute :
@Override
public void execute(Update update) {
   String chatId = update.getMessage().getChatId().toString();

   telegramUserService.findByChatId(chatId).ifPresentOrElse(
           user -> {
               user.setActive(true);
               telegramUserService.save(user);
           },
           () -> {
               TelegramUser telegramUser = new TelegramUser();
               telegramUser.setActive(true);
               telegramUser.setChatId(chatId);
               telegramUserService.save(telegramUser);
           });

   sendBotMessageService.sendMessage(chatId, START_MESSAGE);
}
A lógica funciona aqui: se nosso banco de dados já possui tal usuário por chatId, simplesmente configuramos o campo active=true para ele. E se não existir tal usuário, criamos um novo. O mesmo para o comando /stop em StopCommand :
@Override
public void execute(Update update) {
   telegramUserService.findByChatId(update.getMessage().getChatId().toString())
           .ifPresent(it -> {
               it.setActive(false);
               telegramUserService.save(it);
           });
   sendBotMessageService.sendMessage(update.getMessage().getChatId().toString(), STOP_MESSAGE);
}
Pode-se observar que ao chamar este comando, apenas o campo ativo = falso é definido para o usuário. E isso é tudo: suas assinaturas permanecerão vivas e aguardarão nos bastidores quando o usuário decidir ativar novamente o chat com o bot. E parece que a tarefa já foi concluída e pode ser encerrada. Mas não estava lá. A tarefa mais importante é criar um alerta sobre novos artigos na assinatura. É aqui que essas tarefas serão completamente atualizadas e concluídas. Ou seja, até que tenhamos implementado a notificação de novos artigos, ela não poderá ser encerrada. Portanto, vamos cuidar da tarefa JRTB-4 - criar uma verificação a cada 20 minutos e notificações sobre novos artigos. Amigos! Quer saber imediatamente quando o novo código do projeto for lançado? Quando sai um novo artigo? Entre no meu canal tg . Lá eu reúno meus artigos, meus pensamentos, meu desenvolvimento de código aberto.

Implementamos JRTB-4

O que precisamos fazer como parte desta tarefa:
  1. Crie um job que irá visitar periodicamente todos os grupos para os quais temos assinaturas na base de dados, ordenar os artigos por data de publicação e verificar se o ID da última publicação corresponde ao valor em GroupSub. Se não corresponder, você precisa entender exatamente quantos artigos foram publicados desde a última vez. Atualizamos last_article_id em GroupSub7 para o estado atual.

  2. Quando encontramos uma lista de artigos publicados, encontramos todos os usuários ATIVOS desses grupos e enviamos notificações sobre novos artigos.

Para fazer isso, usaremos algo como Spring Scheduler. Este é um mecanismo do Spring Framework, com ele você pode criar tarefas que serão executadas em um horário específico. A cada 15-20-40 minutos, ou todas as quintas-feiras às 15h30 ou alguma outra opção. Eles também são chamados de papel vegetal do inglês - joba. Enquanto realizamos esta tarefa, deixarei deliberadamente um defeito na busca por novos artigos. É bastante raro e só apareceu numa situação em que testei manualmente o funcionamento desta tarefa. Para fazer isso, você precisa escrever um cliente para pesquisar artigos. Para fazer isso, usaremos a API Swagger que já conhecemos . Existe um pós-controlador. Estamos interessados ​​apenas em pesquisar uma coleção de artigos usando determinados filtros:
/api/1.0/rest/posts Obtenha postagens por filtros
Trabalharemos com esta solicitação. O que precisamos nisso? Obtenha uma lista de artigos que pertencem a um grupo específico e devem ser ordenados por data de publicação. Desta forma podemos pegar os últimos 15 artigos e verificar se novas publicações foram publicadas com base no lastArticleId da nossa base de dados. Caso existam, iremos repassá-los para processamento e envio ao utilizador. Então precisamos escrever JavaRushPostClient .

Escrevemos JavaRushPostClient

Aqui não tentaremos cobrir todas as solicitações que nos foram enviadas na API e criaremos apenas aquela que necessitamos. Ao fazer isso, alcançamos dois objetivos ao mesmo tempo:
  1. Agilizamos o processo de redação de nossa inscrição.

  2. Deixamos esse trabalho para quem quer ajudar nossa comunidade e decide tentar ser desenvolvedor. Farei tarefas para isso que poderão ser concluídas após o MVP.

Então, vamos fazê-lo. Para consultar a seção Modelos na UI do Swagger, criaremos os seguintes DTOs:"Projeto Java de A a Z": Adicionando um cliente aos artigos - 2

BaseUserInfo:

package com.github.javarushcommunity.jrtb.javarushclient.dto;

import lombok.Data;

/**
* DTO, which represents base user information.
*/
@Data
public class BaseUserInfo {
   private String city;
   private String country;
   private String displayName;
   private Integer id;
   private String job;
   private String key;
   private Integer level;
   private String pictureUrl;
   private String position;
   private UserPublicStatus publicStatus;
   private String publicStatusMessage;
   private Integer rating;
   private Integer userId;
}

Linguagem:

package com.github.javarushcommunity.jrtb.javarushclient.dto;

/**
* DTO, which represents languages.
*/
public enum Language {
   UNKNOWN,
   ENGLISH,
   GERMAN,
   SPANISH,
   HINDI,
   FRENCH,
   PORTUGUESE,
   POLISH,
   BENGALI,
   PUNJABI,
   CHINESE,
   ITALIAN,
   INDONESIAN,
   MARATHI,
   TAMIL,
   TELUGU,
   JAPANESE,
   KOREAN,
   URDU,
   TAIWANESE,
   NETHERLANDS,
   RUSSIAN,
   UKRAINIAN
}

CurtidasInformações:

package com.github.javarushcommunity.jrtb.javarushclient.dto;

/**
* DTO, which represents like's information.
*/
public class LikesInfo {

   private Integer count;
   private LikeStatus status;
}

Curtir Status:

package com.github.javarushcommunity.jrtb.javarushclient.dto;

/**
* DTO, which represents like's status.
*/
public enum LikeStatus {

   UNKNOWN,
   LIKE,
   HOT,
   FOLLOW,
   FAVORITE,
   SOLUTION,
   HELPFUL,
   ARTICLE,
   OSCAR,
   DISLIKE,
   WRONG,
   SPAM,
   ABUSE,
   FOUL,
   TROLLING,
   OFFTOPIC,
   DUPLICATE,
   DIRTY,
   OUTDATED,
   BORING,
   UNCLEAR,
   HARD,
   EASY,
   FAKE,
   SHAM,
   AWFUL
}

Tipo de postagem:

package com.github.javarushcommunity.jrtb.javarushclient.dto;

/**
* DTO, which represents post types.
*/
public enum PostType {
   UNKNOWN, USUAL, INNER_LINK, OUTER_LINK
}

Status público do usuário:

package com.github.javarushcommunity.jrtb.javarushclient.dto;

/**
* DTO, which represents user public status.
*/
public enum UserPublicStatus {
   UNKNOWN,
   BEGINNER,
   ACTIVE,
   STRONG,
   GRADUATED,
   INTERNSHIP_IN_PROGRESS,
   INTERNSHIP_COMPLETED,
   RESUME_COMPLETED,
   LOOKING_FOR_JOB,
   HAVE_JOB;
}

VisibilityStatus:
package com.github.javarushcommunity.jrtb.javarushclient.dto;

/**
* DTO, which represents visibility status.
*/
public enum VisibilityStatus {
   UNKNOWN,
   RESTRICTED,
   PUBLIC,
   PROTECTED,
   PRIVATE,
   DISABLED,
   DELETED
}
Com base em todos esses DTOs, vamos escrever uma classe principal para receber artigos:

Informações da postagem:

package com.github.javarushcommunity.jrtb.javarushclient.dto;

import lombok.Data;

/**
* DTO, which represents post information.
*/
@Data
public class PostInfo {

   private BaseUserInfo authorInfo;
   private Integer commentsCount;
   private String content;
   private Long createdTime;
   private String description;
   private GroupInfo groupInfo;
   private Integer id;
   private String key;
   private Language language;
   private LikesInfo likesInfo;
   private GroupInfo originalGroupInfo;
   private String pictureUrl;
   private Double rating;
   private Integer ratingCount;
   private String title;
   private PostType type;
   private Long updatedTime;
   private UserDiscussionInfo userDiscussionInfo;
   private Integer views;
   private VisibilityStatus visibilityStatus;

}
Agora vamos criar uma interface para trabalhar e sua implementação. Precisaremos apenas de um método para trabalhar com artigos:

JavaRushPostCliente:

package com.github.javarushcommunity.jrtb.javarushclient;

import com.github.javarushcommunity.jrtb.javarushclient.dto.PostInfo;

import java.util.List;

/**
* Client for Javarush Open API corresponds to Posts.
*/
public interface JavaRushPostClient {

   /**
    * Find new posts since lastPostId in provided group.
    *
    * @param groupId provided group ID.
    * @param lastPostId provided last post ID.
    * @return the collection of the new {@link PostInfo}.
    */
   List<PostInfo> findNewPosts(Integer groupId, Integer lastPostId);
}
findNewPosts aceita dois argumentos: o ID do grupo e o último ID do artigo que o bot já postou. Portanto, serão transmitidos todos os artigos que foram publicados posteriormente ao artigo com lastPostId . E sua implementação:
package com.github.javarushcommunity.jrtb.javarushclient;

import com.github.javarushcommunity.jrtb.javarushclient.dto.PostInfo;
import kong.unirest.GenericType;
import kong.unirest.Unirest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

@Component
public class JavaRushPostClientImpl implements JavaRushPostClient {

   private final String javarushApiPostPath;

   public JavaRushPostClientImpl(@Value("${javarush.api.path}") String javarushApi) {
       this.javarushApiPostPath = javarushApi + "/posts";
   }

   @Override
   public List<PostInfo> findNewPosts(Integer groupId, Integer lastPostId) {
       List<PostInfo> lastPostsByGroup = Unirest.get(javarushApiPostPath)
               .queryString("order", "NEW")
               .queryString("groupKid", groupId)
               .queryString("limit", 15)
               .asObject(new GenericType<List<PostInfo>>() {
               }).getBody();
       List<PostInfo> newPosts = new ArrayList<>();
       for (PostInfo post : lastPostsByGroup) {
           if (lastPostId.equals(post.getId())) {
               return newPosts;
           }
           newPosts.add(post);
       }
       return newPosts;
   }
}
Adicionamos vários filtros à solicitação:
  • order = NEW - para que a lista contenha os novos primeiro;
  • groupKid = groupId - pesquise apenas determinados grupos;
  • limit = 15 — limitamos o número de artigos por solicitação. Nossa frequência é de 15 a 20 minutos e esperamos que durante esse período não sejam escritos MAIS que 15 (!)
A seguir, quando encontramos artigos, percorremos a lista e procuramos novos. O algoritmo é simples e intuitivo. Se quiser melhorar, escreva). Vamos escrever um teste simples para este cliente:
package com.github.javarushcommunity.jrtb.javarushclient;

import com.github.javarushcommunity.jrtb.javarushclient.dto.PostInfo;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.List;

import static com.github.javarushcommunity.jrtb.javarushclient.JavaRushGroupClientTest.JAVARUSH_API_PATH;

@DisplayName("Integration-level testing for JavaRushPostClient")
class JavaRushPostClientTest {

   private final JavaRushPostClient postClient = new JavaRushPostClientImpl(JAVARUSH_API_PATH);

   @Test
   public void shouldProperlyGetNew15Posts() {
       //when
       List<PostInfo> newPosts = postClient.findNewPosts(30, 2935);

       //then
       Assertions.assertEquals(15, newPosts.size());
   }
}
Este é um teste muito simples que verifica se existe alguma comunicação com o cliente ou não. Ele encontra 15 novos artigos no grupo de projetos Java, porque eu dou a ele o ID do primeiro artigo deste grupo, e já são mais de 15... Já são 22! Eu nem pensei que seriam tantos. Como descobri rapidamente? Você acha que ele foi contá-los? Não) Usei um swager e olhei a quantidade de artigos de um determinado grupo. Aliás, você pode olhar assim em outros... E quantos artigos tem no grupo RANDOM?... Vou te contar agora: são 1062! Quantidade séria.

Fim da primeira parte

Aqui adicionamos trabalho com o cliente por artigo. Já fizemos tudo, desta vez acho que tudo deve ser simples e rápido. No próximo artigo adicionaremos Spring Scheduler e escreveremos FindNewArticleService . Bem, como sempre, curta - inscreva-se - toque a campainha , dê uma estrela ao nosso projeto , escreva comentários e avalie o artigo! Obrigado a todos pela leitura - até breve!

Uma lista de todos os materiais da série está no início deste artigo.

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION