JavaRush /Java Blog /Random-JA /記事へのクライアントの追加 - 「Java プロジェクトの A to Z」
Roman Beekeeper
レベル 35

記事へのクライアントの追加 - 「Java プロジェクトの A to Z」

Random-JA グループに公開済み
親愛なる皆さん、こんにちは。私たちは、プロジェクトの MVP である JavaRush Telegram Bot になるという目標に一歩ずつ近づいています。前回の記事で述べたように、残っているタスクは 5 つだけです。今日はそのうちの 2 つを取り上げます。 「Java プロジェクトの A to Z」: 記事へのクライアントの追加 - 1繰り返しになりますが、このプロジェクトはここで終わるわけではありません。このプロジェクトをどのように発展させるべきか、どのような新しいことを追加できるか、何が改善できるかについて、私にはまだたくさんのアイデアやビジョンがあります。MVP の前に、リファクタリングのトピック、つまり機能を変更せずにコードの品質を向上させることについて別の記事を作成する予定です。その頃にはプロジェクト全体が見えてきて、どこを改善できるかが明確になるでしょう。私たちの場合、多くのテストが記述されているため、機能が破壊されることは最大限に保護されます。また、私たちが望んでいたものと最終的に得たものについての振り返りも書きます。これは非常に便利です。6 か月前にすべてがどの程度正しく認識されていたかを見てみましょう。少なくともこれは私にとって非常に興味深いです。手動テスターとして試してみたい人は、私たちに手紙を書いてください。協力します。一緒にこのプロジェクトをより良いものにしていきましょう!6 か月前に説明した 2 つのタスク、 JRTB-8JRTB-9です。これらのタスクのために何を実装する必要があるかを検討し始めたところ、コマンドの起動に関してはすべてがすでに準備が整っていることに気付きました。それは起こります...) ここで、 StartCommand実行メソッドを確認できます。
@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);
}
ここでロジックが機能します。データベースに chatId によるそのようなユーザーがすでに存在する場合、そのユーザーに対して active = true フィールドを設定するだけです。そのようなユーザーが存在しない場合は、新しいユーザーを作成します。StopCommand/stopコマンドも同様です。
@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);
}
このコマンドを呼び出すと、ユーザーに対して active = false フィールドのみが設定されることがわかります。これですべてです。ユーザーのサブスクリプションは存続し、ユーザーが再びボットとのチャットをアクティブ化することを決定したときに待機します。そして、タスクはすでに完了しており、閉じることができるようです。しかし、そこにはありませんでした。最も重要なタスクは、サブスクリプションの新しい記事に関するアラートを作成することです。ここで、これらのタスクが完全に更新され、完了します。つまり、新しい記事の通知を実装するまでは閉じることができません。したがって、タスク JRTB-4 (20 分ごとのチェックと新しい記事に関する通知の作成) を処理しましょう。 友達!プロジェクトの新しいコードがいつリリースされるかをすぐに知りたいですか? 新しい記事はいつ公開されますか? 私のTGチャンネルに参加してください。そこでは、私の記事、考え、オープンソース開発をまとめています。

JRTB-4を実装します

このタスクの一環として行う必要があることは次のとおりです。
  1. データベース内でサブスクリプションを持っているすべてのグループに定期的にアクセスし、記事を出版日順に並べ替えて、最後の出版物の ID が GroupSub の値と一致するかどうかを確認するジョブを作成します。一致しない場合は、前回以降に公開された記事の数を正確に把握する必要があります。GroupSub7 の last_article_id を現在の状態に更新します。

  2. 公開された記事のリストを見つけると、これらのグループのアクティブなユーザーをすべて見つけて、新しい記事に関する通知を送信します。

これを行うには、Spring Scheduler などを使用します。これは Spring Framework のメカニズムで、これを使用すると、特定の時間に実行されるタスクを作成できます。15、20、40 分ごと、または毎週木曜日の 15:30 またはその他のオプションのいずれかです。英語のトレーシングペーパー「ジョバ」とも呼ばれます。この作業を行っている間、新しい記事を検索する際に、意図的に欠陥を 1 つ残しておきます。これは非常にまれで、このタスクの動作を手動でテストした状況でのみ発生しました。これを行うには、記事を検索するためのクライアントを作成する必要があります。これを行うには、すでによく知られているSwagger APIを使用します。ポストコントローラーあります。特定のフィルターを使用して記事のコレクションを検索することだけに興味があります。
/api/1.0/rest/posts フィルターで投稿を取得する
このご要望に沿って対応させていただきます。その中で何が必要なのでしょうか?特定のグループに属する記事のリストを取得します。公開日順に並べ替える必要があります。このようにして、過去 15 件の記事を取得し、データベースのlastArticleIdに基づいて新しい出版物が発行されたかどうかを確認できます。存在する場合は、処理してユーザーに送信するためにそれらを渡します。したがって、 JavaRushPostClientを記述する必要があります。

JavaRushPostClientを書きます

ここでは、API で渡されたすべてのリクエストをカバーしようとはせず、必要なリクエストのみを作成します。これにより、次の 2 つの目標を同時に達成できます。
  1. アプリケーションの作成プロセスを高速化します。

  2. この作業は、コミュニティを支援したいと考え、開発者として自分自身を試してみようと決心した人たちに任せます。MVP の後に完了できるタスクを作成します。

それでは、やってみましょう。Swagger UIModelsセクションをクエリするには、次の DTO を作成します。「Java プロジェクトの A to Z」: 記事へのクライアントの追加 - 2

基本ユーザー情報:

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

言語:

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
}

好きな情報:

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

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

   private Integer count;
   private LikeStatus 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
}

投稿タイプ:

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

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

ユーザーパブリックステータス:

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
}
これらすべての DTO に基づいて、記事を受け取るメイン クラスを作成しましょう。

投稿情報:

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;

}
次に、操作するインターフェイスとその実装を作成しましょう。記事を操作するために必要なメソッドは 1 つだけです。

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 は2 つの引数を取ります。グループ ID と、ボットがすでに投稿した記事の最後の ID です。したがって、 lastPostIdの記事より後に公開された記事はすべて送信されます。そしてその実装:
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;
   }
}
リクエストにいくつかのフィルターを追加します。
  • order = NEW - リストに新しいものが最初に含まれるようにします。
  • groupKid = groupId - 特定のグループのみを検索します。
  • 制限 = 15 — リクエストごとの記事数を制限します。頻度は 15 ~ 20 分で、この時間内に書き込まれるのは 15 (!) 未満になると予想されます。
次に、記事を見つけたら、リストを調べて新しい記事を探します。アルゴリズムはシンプルで直感的です。改善したい場合は書き込んでください)。このクライアント用に簡単なテストを書いてみましょう。
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());
   }
}
これは、クライアントとの通信があるかどうかを確認する非常に簡単なテストです。彼は、Java プロジェクト グループで 15 件の新しい記事を見つけました。私がこのグループの最初の記事の ID を彼に与えたので、すでに 15 件以上の記事が存在しています...すでに 22 件あります。こんなにたくさんあるとは思いもしませんでした。どうすればすぐにわかったのでしょうか?彼は数を数えに行ったと思いますか? いや)私はあるグループの記事数を調べてみました。ちなみに、他の記事でもこのように見ることができます...そして、RANDOM グループには記事が何件ありますか?...今言っておきますが、1062 件あります。深刻な量。

最初の部分の終わり

ここではクライアントとの作業を記事ごとに追加していきます。すでにすべての作業を行っていますが、今回はすべてをシンプルかつ迅速に行う必要があると思います。次の記事では、Spring Scheduler を追加し、FindNewArticleServiceを記述します。 さて、いつものように、「購読」、「ベルを鳴らして」 、プロジェクトに星を付け、コメントを書いて記事を評価してください。 読んでくださった皆様、ありがとうございました。またお会いしましょう!

シリーズのすべてのマテリアルのリストは、この記事の冒頭にあります。

コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION