JavaRush /בלוג Java /Random-HE /הוספת לקוח למאמרים - "פרויקט ג'אווה מא' עד ת'"
Roman Beekeeper
רָמָה

הוספת לקוח למאמרים - "פרויקט ג'אווה מא' עד ת'"

פורסם בקבוצה
שלום לכולם, חברים יקרים שלי. צעד אחר צעד אנחנו מתקרבים למטרה שלנו - להפוך ל-MVP של הפרויקט שלנו - JavaRush Telegram Bot. כפי שאמרתי במאמר האחרון, נותרו רק 5 משימות. היום נעסוק בשניים מהם. "פרויקט ג'אווה מא' עד ת': הוספת לקוח למאמרים - 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.

אז בואו נעשה את זה. כדי לשאול את קטע המודלים בממשק המשתמש של Swagger, ניצור את ה-DTOs הבאים:"פרויקט ג'אווה מא' עד ת': הוספת לקוח למאמרים - 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
}
בהתבסס על כל ה-DTOs הללו, בואו נכתוב מחלקה ראשית כדי לקבל מאמרים:

PostInfo:

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;
   }
}
אנו מוסיפים מספר מסננים לבקשה:
  • order = NEW - כך שהרשימה תכיל תחילה חדשים;
  • groupKid = groupId - חפש רק קבוצות מסוימות;
  • limit = 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 מאמרים חדשים בקבוצת הפרויקטים של Java, כי אני נותן לו את המזהה של המאמר הראשון בקבוצה הזו, וכבר יש יותר מ-15 מהם... יש כבר 22 מהם! אפילו לא חשבתי שיהיו כל כך הרבה כאלה. איך גיליתי מהר? אתה חושב שהוא הלך לספור אותם? לא) השתמשתי בסוויטר והסתכלתי על מספר המאמרים עבור קבוצה מסוימת. אגב, אתה יכול להיראות כך אצל אחרים... וכמה מאמרים יש בקבוצת RANDOM?... אני אגיד לך עכשיו: יש 1062 כאלה! כמות רצינית.

סוף החלק הראשון

כאן הוספנו עבודה עם הלקוח לפי מאמר. כבר עשינו הכל, הפעם אני חושב שהכל צריך להיות פשוט ומהיר. במאמר הבא נוסיף את Spring Scheduler ונכתוב FindNewArticleService . ובכן, כרגיל, עשו לייק - הירשמו - צלצלו בפעמון , תנו כוכב לפרויקט שלנו , כתבו תגובות ודרגו את המאמר! תודה לכולכם שקראתם - נתראה בקרוב!

רשימה של כל החומרים בסדרה נמצאת בתחילת מאמר זה.

הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION