JavaRush /وبلاگ جاوا /Random-FA /افزودن مشتری به مقالات - "پروژه جاوا از A تا Z"
Roman Beekeeper
مرحله

افزودن مشتری به مقالات - "پروژه جاوا از A تا Z"

در گروه منتشر شد
سلام به همه دوستان عزیزم. گام به گام به هدف خود - تبدیل شدن به MVP پروژه خود - JavaRush Telegram Bot نزدیک می شویم. همانطور که در مقاله قبلی گفتم، تنها 5 کار باقی مانده است. امروز به دو مورد از آنها خواهیم پرداخت. "پروژه جاوا از A تا Z": افزودن مشتری به مقالات - 1می خواهم تکرار کنم که پروژه به اینجا ختم نمی شود. من هنوز تعداد زیادی ایده و دیدگاه دارم که چگونه این پروژه باید توسعه یابد، چه چیزهای جدیدی را می توان به آن اضافه کرد، چه کارهایی را می توان بهتر انجام داد. قبل از MVP، ما یک مقاله جداگانه با موضوع refactoring - یعنی در مورد بهبود کیفیت کد بدون تغییر عملکرد آن، خواهیم ساخت. تا آن زمان، کل پروژه قابل مشاهده خواهد بود و مشخص خواهد شد که چه چیزی و کجا می تواند بهبود یابد. در مورد ما، ما از شکستن عملکرد حداکثر محافظت خواهیم کرد، زیرا تست های زیادی نوشته شده است. ما همچنین در مورد آنچه که می‌خواستیم و در پایان به چه چیزی رسیدیم، مروری به گذشته خواهیم نوشت. این یک چیز بسیار مفید است: بیایید ببینیم شش ماه پیش همه چیز به درستی دیده شده است. حداقل این برای من خیلی جالب است. اگر کسی مایل است خود را به عنوان یک آزمایش کننده دستی امتحان کند، برای ما بنویسد و ما همکاری خواهیم کرد. بیایید این پروژه را با هم بهتر کنیم! بنابراین، اینها عبارتند از: دو وظیفه که شش ماه پیش شرح داده شد: JRTB-8 و JRTB-9 . شروع کردم به بررسی مواردی که برای این کارها باید اجرا شود و متوجه شدم که از نظر راه اندازی دستورات، همه چیز از قبل آماده است. این اتفاق می افتد...) در اینجا، می توانید به StartCommand ، متد 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);
}
منطق در اینجا کار می کند: اگر پایگاه داده ما قبلاً چنین کاربری توسط chatId داشته باشد، ما به سادگی فیلد active = true را برای او تنظیم می کنیم. و اگر چنین کاربری وجود نداشته باشد، یک کاربر جدید ایجاد می کنیم. همین امر برای دستور /stop در 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);
}
مشاهده می شود که هنگام فراخوانی این دستور فقط فیلد active = false برای کاربر تنظیم شده است. و این همه است: اشتراک‌های او زنده می‌مانند و زمانی که کاربر دوباره تصمیم به فعال کردن چت با ربات گرفت، در بال خواهند ماند. و به نظر می رسد که کار قبلاً تکمیل شده است و می تواند بسته شود. اما آنجا نبود. مهمترین وظیفه ایجاد هشدار درباره مقالات جدید در اشتراک است. اینجاست که این وظایف به طور کامل به روز و تکمیل خواهند شد. یعنی تا زمانی که اطلاع رسانی مقالات جدید را اجرا نکنیم، نمی توان آن را بست. بنابراین، بیایید به وظیفه JRTB-4 رسیدگی کنیم - هر 20 دقیقه یک چک و اعلان هایی در مورد مقالات جدید ایجاد کنیم. دوستان! آیا می خواهید بدانید چه زمانی کد جدید برای پروژه منتشر می شود؟ چه زمانی مقاله جدید منتشر می شود؟ به کانال tg من بپیوندید . در آنجا مقالاتم، افکارم، توسعه متن بازم را با هم جمع می کنم.

ما JRTB-4 را پیاده سازی می کنیم

کاری که باید به عنوان بخشی از این کار انجام دهیم:
  1. شغلی ایجاد کنید که به صورت دوره‌ای به همه گروه‌هایی که برای آنها اشتراک در پایگاه داده داریم برود، مقالات را بر اساس تاریخ انتشار مرتب کنید و بررسی کنید که آیا شناسه آخرین انتشار با مقدار GroupSub مطابقت دارد یا خیر. اگر مطابقت ندارد، باید بدانید دقیقاً چند مقاله از آخرین بار منتشر شده است. ما last_article_id را در GroupSub7 به وضعیت فعلی به‌روزرسانی می‌کنیم.

  2. وقتی لیستی از مقالات منتشر شده را پیدا کردیم، همه کاربران فعال این گروه ها را پیدا می کنیم و در مورد مقالات جدید اعلان هایی برای آنها ارسال می کنیم.

برای این کار از چیزی به نام Spring Scheduler استفاده می کنیم. این یک مکانیزم در Spring Framework است که با آن می توانید وظایفی را ایجاد کنید که در یک زمان خاص اجرا شوند. یا هر 15-20-40 دقیقه یا هر پنجشنبه ساعت 15:30 یا گزینه دیگری. به آنها کاغذ ردیابی از انگلیسی - joba نیز می گویند. در حالی که ما این کار را انجام می دهیم، من عمداً یک نقص را در جستجوی مقالات جدید باقی می گذارم. بسیار نادر است و فقط در شرایطی ظاهر شد که من به صورت دستی عملکرد این کار را آزمایش کردم. برای این کار باید یک مشتری برای جستجوی مقالات بنویسید. برای انجام این کار، از Swagger API استفاده خواهیم کرد که قبلاً برای ما آشناست . یک پست کنترل وجود دارد. ما فقط علاقه مند به جستجوی مجموعه ای از مقالات با استفاده از فیلترهای خاص هستیم:
/api/1.0/rest/posts دریافت پست ها بر اساس فیلتر
ما با این درخواست کار خواهیم کرد. چه چیزی در آن نیاز داریم؟ فهرستی از مقالاتی که متعلق به یک گروه خاص هستند را دریافت کنید و باید بر اساس تاریخ انتشار مرتب شوند. به این ترتیب می توانیم 15 مقاله آخر را برداریم و بررسی کنیم که آیا انتشارات جدید بر اساس lastArticleId از پایگاه داده ما منتشر شده است یا خیر . در صورت وجود، آنها را برای پردازش و ارسال به کاربر ارسال می کنیم. بنابراین باید JavaRushPostClient را بنویسیم .

ما JavaRushPostClient را می نویسیم

در اینجا ما سعی نمی کنیم تمام درخواست هایی را که در API برای ما ارسال شده اند پوشش دهیم و فقط درخواستی را ایجاد می کنیم که نیاز داریم. با انجام این کار، ما به دو هدف در یک زمان دست پیدا می کنیم:
  1. ما روند نوشتن برنامه خود را تسریع می کنیم.

  2. ما این کار را به کسانی واگذار می کنیم که می خواهند به جامعه ما کمک کنند و تصمیم می گیرند خود را به عنوان یک توسعه دهنده امتحان کنند. من وظایفی را برای این کار خواهم ساخت که بعد از MVP می توان آنها را تکمیل کرد.

خب بیا انجام بدیمش. برای پرس و جو بخش Models در Swagger UI، DTO های زیر را ایجاد می کنیم:"پروژه جاوا از A تا Z": افزودن مشتری به مقالات - 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;
}

زبان:

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
}

UserPublicStatus:

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;

}
حالا بیایید یک رابط برای کار و پیاده سازی آن ایجاد کنیم. برای کار با مقالات فقط به یک روش نیاز داریم:

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 دو آرگومان می گیرد: شناسه گروه و آخرین شناسه مقاله که ربات قبلاً ارسال کرده است. بنابراین، تمام آن دسته از مقالاتی که دیرتر از مقاله با 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;
   }
}
ما چندین فیلتر را به درخواست اضافه می کنیم:
  • سفارش = 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());
   }
}
این یک تست بسیار ساده است که بررسی می کند آیا اصلاً با مشتری ارتباطی وجود دارد یا خیر. او 15 مقاله جدید در گروه پروژه های جاوا پیدا می کند، زیرا من شناسه مقاله اول این گروه را به او می دهم و در حال حاضر بیش از 15 مورد ... قبلاً 22 مورد است! من حتی فکر نمی کردم تعداد آنها زیاد باشد. چطور سریع متوجه شدم؟ فکر می کنی او رفت تا آنها را بشمارد؟ خیر) من از یک سوگند استفاده کردم و به تعداد مقالات یک گروه خاص نگاه کردم. در ضمن، شما می توانید در دیگران به این شکل نگاه کنید... و چند مقاله در گروه RANDOM وجود دارد؟... اکنون به شما می گویم: تعداد آنها 1062 است! مقدار جدی

پایان قسمت اول

در اینجا کار با مشتری را به صورت مقاله اضافه کرده ایم. ما قبلاً همه چیز را انجام داده ایم، این بار فکر می کنم همه چیز باید ساده و سریع باشد. در مقاله بعدی Spring Scheduler را اضافه می کنیم و FindNewArticleService را می نویسیم . خوب، طبق معمول، مانند - مشترک شوید - زنگ را بزنید ، به پروژه ما ستاره بدهید ، نظرات خود را بنویسید و به مقاله امتیاز دهید! از همه شما برای خواندن متشکرم - به زودی شما را می بینیم!

فهرستی از تمام مواد این مجموعه در ابتدای این مقاله است.

نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION