JavaRush /Blog Java /Random-FR /Ajout d'un client aux articles - "Projet Java de A à Z"
Roman Beekeeper
Niveau 35

Ajout d'un client aux articles - "Projet Java de A à Z"

Publié dans le groupe Random-FR
Bonjour à tous, mes chers amis. Pas à pas, nous nous rapprochons de notre objectif : devenir le MVP de notre projet - JavaRush Telegram Bot. Comme je l'ai dit dans le dernier article, il ne reste plus que 5 tâches. Aujourd’hui, nous en couvrirons deux. "Projet Java de A à Z" : Ajouter un client aux articles - 1Je tiens à répéter que le projet ne s'arrêtera pas là. J'ai encore une tonne d'idées et de visions sur la façon dont ce projet devrait se développer, quelles nouvelles choses peuvent y être ajoutées, ce qui peut être amélioré. Avant MVP, nous ferons un article séparé sur le thème du refactoring, c'est-à-dire sur l'amélioration de la qualité du code sans modifier ses fonctionnalités. À ce moment-là, l’ensemble du projet sera visible et il sera clair ce qui peut être amélioré et où. Dans notre cas, nous serons protégés au maximum contre la rupture de fonctionnalité, car de nombreux tests ont été écrits. Nous rédigerons également une rétrospective sur ce que nous voulions et ce que nous avons obtenu au final. C’est une chose très utile : voyons à quel point tout a été vu correctement il y a six mois. Au moins, c'est très intéressant pour moi. Si quelqu’un souhaite s’essayer en tant que testeur manuel, écrivez-nous et nous collaborerons. Améliorons ce projet ensemble ! Les voici donc : deux tâches décrites il y a six mois : JRTB-8 et JRTB-9 . J'ai commencé à regarder ce qu'il fallait implémenter pour ces tâches, et j'ai réalisé qu'en termes de lancement de commandes, tout était déjà prêt. Cela arrive...) Ici, vous pouvez regarder la StartCommand , la méthode d'exécution :
@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);
}
La logique fonctionne ici : si notre base de données a déjà un tel utilisateur par chatId, nous définissons simplement le champ active = true pour lui. Et s'il n'y a pas un tel utilisateur, nous en créons un nouveau. Idem pour la commande /stop dans 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);
}
On peut voir que lors de l'appel de cette commande, seul le champ active = false est défini pour l'utilisateur. Et c’est tout : ses abonnements vivront et attendront en coulisses lorsque l’utilisateur décidera à nouveau d’activer le chat avec le bot. Et il semblerait que la tâche soit déjà terminée et puisse être clôturée. Mais ce n'était pas là. La tâche la plus importante est de créer une alerte sur les nouveaux articles de l'abonnement. C'est ici que ces tâches seront complètement mises à jour et complétées. Autrement dit, jusqu'à ce que nous ayons mis en place la notification des nouveaux articles, il ne peut pas être fermé. Par conséquent, occupons-nous de la tâche JRTB-4 - créer un contrôle toutes les 20 minutes et des notifications sur les nouveaux articles. Amis! Voulez-vous savoir immédiatement quand le nouveau code du projet est publié ? Quand sort un nouvel article ? Rejoignez ma chaîne tg . Là, je rassemble mes articles, mes réflexions, mes développements open source.

Nous implémentons JRTB-4

Ce que nous devons faire dans le cadre de cette tâche :
  1. Créez un travail qui ira périodiquement à tous les groupes pour lesquels nous avons des abonnements dans la base de données, triera les articles par date de publication et vérifiera si l'ID de la dernière publication correspond à la valeur dans GroupSub. Si cela ne correspond pas, vous devez alors comprendre exactement combien d’articles ont été publiés depuis la dernière fois. Nous mettons à jour last_article_id dans GroupSub7 vers l'état actuel.

  2. Lorsque nous avons trouvé une liste d'articles publiés, nous trouvons tous les utilisateurs ACTIFS de ces groupes et leur envoyons des notifications sur les nouveaux articles.

Pour ce faire, nous utiliserons un outil tel que Spring Scheduler. Il s'agit d'un mécanisme du Spring Framework, avec lequel vous pouvez créer des tâches qui seront exécutées à un moment précis. Soit toutes les 15-20-40 minutes, soit tous les jeudis à 15h30 ou autre option. On les appelle aussi papier calque de l'anglais - joba. Pendant que nous accomplissons cette tâche, je laisserai délibérément un défaut dans la recherche de nouveaux articles. C'est assez rare et n'est apparu que dans une situation où j'ai testé manuellement le fonctionnement de cette tâche. Pour ce faire, vous devez écrire un client pour rechercher des articles. Pour ce faire, nous utiliserons l'API Swagger qui nous est déjà familière . Il y a un post-contrôleur. Nous souhaitons uniquement rechercher une collection d'articles en utilisant certains filtres :
/api/1.0/rest/posts Obtenir les publications par filtres
Nous travaillerons avec cette demande. De quoi avons-nous besoin? Obtenez une liste d’articles appartenant à un groupe spécifique et ils doivent être triés par date de publication. De cette façon, nous pouvons prendre les 15 derniers articles et vérifier si de nouvelles publications ont été publiées sur la base du lastArticleId de notre base de données. S'il y en a, nous les transmettrons pour traitement et envoi à l'utilisateur. Nous devons donc écrire JavaRushPostClient .

Nous écrivons JavaRushPostClient

Ici, nous n'essaierons pas de couvrir toutes les requêtes qui nous ont été envoyées dans l'API et créerons uniquement celle dont nous avons besoin. En faisant cela, nous atteignons deux objectifs à la fois :
  1. Nous accélérons le processus de rédaction de notre candidature.

  2. Nous laissons ce travail à ceux qui veulent aider notre communauté et décidons de s'essayer en tant que développeur. Je ferai des tâches pour cela qui pourront être accomplies après le MVP.

Alors faisons-le. Pour interroger la section Modèles dans Swagger UI, nous allons créer les DTO suivants :"Projet Java de A à Z" : Ajouter un client aux articles - 2

Informations utilisateur de base :

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;
}

Langue:

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
}

J'aimeInfo :

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

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

   private Integer count;
   private LikeStatus status;
}

J'aimeStatut :

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
}

Type de poste:

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

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

Statut public utilisateur :

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
}
Sur la base de tous ces DTO, écrivons une classe principale pour recevoir des articles :

Informations sur le message :

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;

}
Créons maintenant une interface avec laquelle travailler et son implémentation. Nous n'aurons besoin que d'une seule méthode pour travailler avec des articles :

JavaRushPostClient :

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 prend deux arguments : l'ID du groupe et le dernier ID de l'article que le bot a déjà publié. Par conséquent, tous les articles publiés après l'article avec lastPostId seront transmis . Et sa mise en œuvre :
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;
   }
}
Nous ajoutons plusieurs filtres à la requête :
  • order = NEW - pour que la liste contienne d'abord les nouveaux ;
  • groupKid = groupId - recherche uniquement certains groupes ;
  • limit = 15 — nous limitons le nombre d'articles par demande. Notre fréquence est de 15 à 20 minutes et nous prévoyons que pendant cette période, pas PLUS de 15 (!) seront écrits.
Ensuite, lorsque nous avons trouvé des articles, nous parcourons la liste et en recherchons de nouveaux. L'algorithme est simple et intuitif. Si vous souhaitez l'améliorer, écrivez). Écrivons un test simple pour ce client :
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());
   }
}
Il s'agit d'un test très simple qui vérifie s'il y a ou non une communication avec le client. Il trouve 15 nouveaux articles dans le groupe des projets Java, car je lui donne l'identifiant du premier article de ce groupe, et il y en a déjà plus de 15... Il y en a déjà 22 ! Je ne pensais même pas qu'il y en aurait autant. Comment l’ai-je su rapidement ? Pensez-vous qu'il est allé les compter ? Non) J'ai utilisé un swager et j'ai regardé le nombre d'articles pour un certain groupe. D'ailleurs, vous pouvez regarder de cette façon chez les autres... Et combien d'articles y a-t-il dans le groupe RANDOM ?... Je vais vous le dire maintenant : il y en a 1062 ! Montant sérieux.

Fin de la première partie

Ici, nous avons ajouté le travail avec le client par article. Nous avons déjà tout fait, cette fois je pense que tout doit être simple et rapide. Dans le prochain article, nous ajouterons Spring Scheduler et écrirons FindNewArticleService . Eh bien, comme d'habitude, aimez - abonnez-vous - sonnez la cloche , donnez une étoile à notre projet , écrivez des commentaires et notez l'article ! Merci à tous d'avoir lu - à bientôt !

Une liste de tous les matériaux de la série se trouve au début de cet article.

Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION