JavaRush /Java 博客 /Random-ZH /在文章中添加客户端 - “Java 项目从 A 到 Z”
Roman Beekeeper
第 35 级

在文章中添加客户端 - “Java 项目从 A 到 Z”

已在 Random-ZH 群组中发布
大家好,我亲爱的朋友们。我们正在一步步接近我们的目标——成为我们项目——JavaRush Telegram Bot 的 MVP。正如我在上一篇文章中所说,只剩下 5 个任务了。今天我们将介绍其中两个。 “Java 项目从头到尾”:向文章中添加客户端 - 1我想重申,这个项目不会到此结束。对于这个项目应该如何发展、可以添加哪些新东西、哪些可以做得更好,我仍然有很多想法和愿景。在 MVP 之前,我们将单独撰写一篇关于重构主题的文章——即在不改变代码功能的情况下提高代码质量。到那时,整个项目将是可见的,并且可以清楚哪些地方可以改进。在我们的例子中,我们将最大限度地防止破坏功能,因为已经编写了许多测试。我们还将写一份关于我们想要什么以及最终得到什么的回顾。这是一件非常有用的事情:让我们看看六个月前所看到的一切是否正确。至少这对我来说很有趣。如果有人想尝试自己担任手动测试员,请写信给我们,我们将予以合作。让我们一起把这个项目做得更好!因此,它们是:六个月前描述的两项任务: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框架中的一种机制,用它你可以创建将在特定时间执行的任务。每 15-20-40 分钟一班,或每周四 15:30 或其他选项。它们也被称为英语“描图纸”——joba。当我们做这个任务的时候,我会故意在寻找新文章时留下一个缺陷。这种情况相当罕见,只出现在我手动测试该任务运行情况的情况下。为此,您需要编写一个用于搜索文章的客户端。为此,我们将使用我们已经熟悉的Swagger API。有一个后控制器。我们只对使用某些过滤器搜索文章集合感兴趣:
/api/1.0/rest/posts 通过过滤器获取帖子
我们将处理此请求。我们需要什么?获取属于特定组的文章列表,它们应按发布日期排序。这样我们就可以获取最后 15 篇文章,并根据数据库中的lastArticleId检查是否已发布新出版物。如果有,我们会将其转交处理并发送给用户。所以我们需要编写JavaRushPostClient

我们编写JavaRushPostClient

在这里,我们不会尝试涵盖 API 中发送给我们的所有请求,而只会创建我们需要的请求。通过这样做,我们可以同时实现两个目标:
  1. 我们加快了撰写申请的过程。

  2. 我们将这项工作留给那些想要帮助我们社区并决定尝试自己作为开发人员的人。我会为此制定一些任务,这些任务可以在 MVP 之后完成。

那么让我们开始吧。为了查询Swagger UI中的模型部分,我们将创建以下 DTO:“Java 项目从头到尾”:向文章中添加客户端 - 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;

}
现在让我们创建一个要使用的接口及其实现。我们只需要一种方法来处理文章:

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采用两个参数:组 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 - 仅搜索某些组;
  • 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());
   }
}
这是一个非常简单的测试,用于检查是否与客户端有任何通信。他在Java项目组里找到了15篇新文章,因为我给了他这个组里第一篇文章的ID,已经超过15篇了……已经有22篇了!我什至没有想到会有这么多人。我怎么很快就知道了?你认为他去数数了吗?不)我使用了压印器并查看了某个组的文章数量。对了,你可以在别人身上这样看……那么 RANDOM 组里有多少篇文章?……我现在告诉你:有 1062 篇!数额严重。

第一部分结束

这里我们通过文章添加了与客户的合作。我们已经完成了所有工作,这次我认为一切都应该简单快捷。在下一篇文章中,我们将添加 Spring Scheduler 并编写FindNewArticleService好吧,像往常一样,点赞 - 订阅 - 敲响小铃,给我们的项目一颗星,写评论并评价文章! 感谢大家的阅读 - 很快再见!

该系列所有材料的列表位于本文开头。

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