JavaRush /مدونة جافا /Random-AR /إضافة جدولة الربيع - "مشروع جافا من الألف إلى الياء"
Roman Beekeeper
مستوى

إضافة جدولة الربيع - "مشروع جافا من الألف إلى الياء"

نشرت في المجموعة
مرحبا بالجميع، أصدقائي الأعزاء. في المقالة السابقة ، قمنا بإعداد عميل للعمل مع JavaRush API للمقالات. الآن يمكننا كتابة المنطق لعملنا، والذي سيتم تنفيذه كل 15 دقيقة. تمامًا كما هو موضح في هذا الرسم البياني: "مشروع جافا من الألف إلى الياء": إضافة جدولة الربيع - 1كل 15 دقيقة سيتم إطلاق مهمة (في رأينا، مجرد طريقة في فئة معينة)، والتي يتم تنفيذها في خلفية التطبيق الرئيسي وتقوم بما يلي:
  1. يجد في جميع المجموعات الموجودة في قاعدة بياناتنا المقالات الجديدة المنشورة بعد التنفيذ السابق.

    يحدد هذا المخطط عددًا أقل من المجموعات - فقط تلك التي تضم مستخدمين نشطين. في ذلك الوقت بدا الأمر منطقيًا بالنسبة لي، لكنني الآن أفهم أنه بغض النظر عما إذا كان هناك مستخدمون نشطون مشتركون في مجموعة معينة أم لا، فأنت لا تزال بحاجة إلى تحديث آخر مقالة قام الروبوت بمعالجتها. قد ينشأ موقف عندما يتلقى مستخدم جديد على الفور العدد الكامل للمقالات المنشورة منذ إلغاء تنشيط هذه المجموعة. هذا ليس سلوكًا متوقعًا، ولتجنبه، نحتاج إلى إبقاء تلك المجموعات من قاعدة بياناتنا التي لا تحتوي حاليًا على مستخدمين نشطين محدثة.
  2. إذا كانت هناك مقالات جديدة، فقم بإنشاء رسائل لجميع المستخدمين المشتركين بشكل نشط في هذه المجموعة. إذا لم تكن هناك مقالات جديدة، فإننا ببساطة نكمل العمل.

بالمناسبة، لقد ذكرت بالفعل في قناة TG الخاصة بي أن الروبوت يعمل بالفعل ويرسل مقالات جديدة بناءً على الاشتراكات. لنبدأ في كتابة FindNewArtcileService . كل أعمال البحث وإرسال الرسائل ستتم هناك، والمهمة ستقتصر فقط على إطلاق طريقة هذه الخدمة:

خدمة البحث عن مقالة جديدة:

package com.github.javarushcommunity.jrtb.service;

/**
* Service for finding new articles.
*/
public interface FindNewArticleService {

   /**
    * Find new articles and notify subscribers about it.
    */
   void findNewArticles();
}
بسيط جدا، أليس كذلك؟ وهذا هو جوهرها، وكل الصعوبة ستكون في التنفيذ:
package com.github.javarushcommunity.jrtb.service;

import com.github.javarushcommunity.jrtb.javarushclient.JavaRushPostClient;
import com.github.javarushcommunity.jrtb.javarushclient.dto.PostInfo;
import com.github.javarushcommunity.jrtb.repository.entity.GroupSub;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class FindNewArticleServiceImpl implements FindNewArticleService {

   public static final String JAVARUSH_WEB_POST_FORMAT = "https://javarush.com/groups/posts/%s";

   private final GroupSubService groupSubService;
   private final JavaRushPostClient javaRushPostClient;
   private final SendBotMessageService sendMessageService;

   @Autowired
   public FindNewArticleServiceImpl(GroupSubService groupSubService,
                                    JavaRushPostClient javaRushPostClient,
                                    SendBotMessageService sendMessageService) {
       this.groupSubService = groupSubService;
       this.javaRushPostClient = javaRushPostClient;
       this.sendMessageService = sendMessageService;
   }


   @Override
   public void findNewArticles() {
       groupSubService.findAll().forEach(gSub -> {
           List<PostInfo> newPosts = javaRushPostClient.findNewPosts(gSub.getId(), gSub.getLastArticleId());

           setNewLastArticleId(gSub, newPosts);

           notifySubscribersAboutNewArticles(gSub, newPosts);
       });
   }

   private void notifySubscribersAboutNewArticles(GroupSub gSub, List<PostInfo> newPosts) {
       Collections.reverse(newPosts);
       List<String> messagesWithNewArticles = newPosts.stream()
               .map(post -> String.format("✨Вышла новая статья <b>%s</b> в группе <b>%s</b>.✨\n\n" +
                               "<b>Описание:</b> %s\n\n" +
                               "<b>Ссылка:</b> %s\n",
                       post.getTitle(), gSub.getTitle(), post.getDescription(), getPostUrl(post.getKey())))
               .collect(Collectors.toList());

       gSub.getUsers().stream()
               .filter(TelegramUser::isActive)
               .forEach(it -> sendMessageService.sendMessage(it.getChatId(), messagesWithNewArticles));
   }

   private void setNewLastArticleId(GroupSub gSub, List<PostInfo> newPosts) {
       newPosts.stream().mapToInt(PostInfo::getId).max()
               .ifPresent(id -> {
                   gSub.setLastArticleId(id);
                   groupSubService.save(gSub);
               });
   }

   private String getPostUrl(String key) {
       return String.format(JAVARUSH_WEB_POST_FORMAT, key);
   }
}
هنا سنتعامل مع كل شيء بالترتيب:
  1. باستخدام groupService نجد جميع المجموعات الموجودة في قاعدة البيانات.

  2. ثم نتفرق إلى جميع المجموعات ونسمي كل منها العميل الذي تم إنشاؤه في المقالة الأخيرة - javaRushPostClient.findNewPosts .

  3. بعد ذلك، باستخدام طريقة setNewArticleId ، نقوم بتحديث معرف المقالة لآخر مقالة جديدة لدينا حتى تعلم قاعدة بياناتنا أننا قمنا بالفعل بمعالجة مقالات جديدة.

  4. وباستخدام حقيقة أن GroupSub لديه مجموعة من المستخدمين، فإننا نتصفح المستخدمين النشطين ونرسل إشعارات حول المقالات الجديدة.

لن نناقش ماهية الرسالة الآن، فهي ليست مهمة جدًا بالنسبة لنا. الشيء الرئيسي هو أن الطريقة تعمل. منطق البحث عن مقالات جديدة وإرسال الإشعارات جاهز، حتى تتمكن من الانتقال إلى إنشاء وظيفة.

إنشاء FindNewArticleJob

لقد تحدثنا بالفعل عن ماهية SpringScheduler، ولكن دعونا نكررها بسرعة: إنها آلية في إطار عمل Spring لإنشاء عملية خلفية سيتم تشغيلها في وقت محدد نحدده. ماذا تحتاج لهذا؟ الخطوة الأولى هي إضافة التعليق التوضيحي @EnableScheduling إلى فئة الإدخال الربيعية الخاصة بنا:
package com.github.javarushcommunity.jrtb;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@EnableScheduling
@SpringBootApplication
public class JavarushTelegramBotApplication {

   public static void main(String[] args) {
       SpringApplication.run(JavarushTelegramBotApplication.class, args);
   }

}
الخطوة الثانية هي إنشاء فئة وإضافتها إلى ApplicationContext وإنشاء طريقة فيها سيتم تشغيلها بشكل دوري. نقوم بإنشاء حزمة وظائف على نفس مستوى المستودع والخدمة وما إلى ذلك، وهناك نقوم بإنشاء فئة FindNewArticleJob :
package com.github.javarushcommunity.jrtb.job;

import com.github.javarushcommunity.jrtb.service.FindNewArticleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.ZoneOffset;

/**
* Job for finding new articles.
*/
@Slf4j
@Component
public class FindNewArticlesJob {

   private final FindNewArticleService findNewArticleService;

   @Autowired
   public FindNewArticlesJob(FindNewArticleService findNewArticleService) {
       this.findNewArticleService = findNewArticleService;
   }

   @Scheduled(fixedRateString = "${bot.recountNewArticleFixedRate}")
   public void findNewArticles() {
       LocalDateTime start = LocalDateTime.now();

       log.info("Find new article job started.");

       findNewArticleService.findNewArticles();

       LocalDateTime end = LocalDateTime.now();

       log.info("Find new articles job finished. Took seconds: {}",
               end.toEpochSecond(ZoneOffset.UTC) - start.toEpochSecond(ZoneOffset.UTC));
   }
}
لإضافة هذه الفئة إلى سياق التطبيق، استخدمت التعليق التوضيحي @Component . ولكي تعرف الطريقة الموجودة داخل الفصل أنها تحتاج إلى التشغيل بشكل دوري، أضفت تعليقًا توضيحيًا للطريقة: @Scheduled(fixedRateString = "${bot.recountNewArticleFixedRate}") . ولكن ما هي القيمة التي ستكون عليها - لقد قمنا بتعيينها بالفعل في ملف application.properties:
bot.recountNewArticleFixedRate = 900000
هنا القيمة بالمللي ثانية. سيكون 15 دقيقة. في هذه الطريقة، كل شيء بسيط: لقد أضفت مقياسًا بسيطًا للغاية لنفسي في السجلات لحساب البحث عن مقالات جديدة، وذلك لفهم مدى سرعة عمله تقريبًا على الأقل.

اختبار وظائف جديدة

الآن سنقوم بالاختبار على روبوت الاختبار الخاص بنا. ولكن كيف؟ لن أحذف المقالات في كل مرة لإظهار وصول الإشعارات؟ بالطبع لا. سنقوم ببساطة بتحرير البيانات الموجودة في قاعدة البيانات وتشغيل التطبيق. سأختبره على خادم الاختبار الخاص بي. للقيام بذلك، دعونا الاشتراك في بعض المجموعة. عند اكتمال الاشتراك، سيتم منح المجموعة المعرف الحالي لآخر مقالة. دعنا نذهب إلى قاعدة البيانات ونغير القيمة إلى مقالتين مرة أخرى. ونتيجة لذلك، نتوقع أن يكون هناك عدد من المقالات بنفس القدر الذي قمنا بتعيين lastArticleId عليه سابقًا . "مشروع جافا من الألف إلى الياء": إضافة جدولة الربيع - 2بعد ذلك نذهب إلى الموقع ونفرز المقالات في مجموعة مشاريع Java - الجديدة أولاً - وننتقل إلى المقالة الثالثة من القائمة: "Java-проект от А до Я": Добавляем Spring Scheduler - 3دعنا نذهب إلى المقالة السفلية ومن شريط العناوين نحصل على معرف المقالة - 3313: "Java-проект от А до Я": Добавляем Spring Scheduler - 4التالي ، انتقل إلى MySQL Workbench وقم بتغيير قيمة lastArticleId إلى 3313. دعونا نرى أن هذه المجموعة موجودة في قاعدة البيانات: "Java-проект от А до Я": Добавляем Spring Scheduler - 5ولهذا سنقوم بتنفيذ الأمر: "Java-проект от А до Я": Добавляем Spring Scheduler - 6وهذا كل شيء، الآن عليك الانتظار حتى الإطلاق التالي للمهمة البحث عن مقالات جديدة. نتوقع أن نتلقى رسالتين حول مقال جديد من مجموعة مشاريع جافا. كما يقولون، النتيجة لم تستغرق وقتا طويلا: "Java-проект от А до Я": Добавляем Spring Scheduler - 7اتضح أن الروبوت يعمل كما توقعنا.

إنهاء

كما هو الحال دائمًا، نقوم بتحديث الإصدار في pom.xml ونضيف إدخالاً إلى RELEASE_NOTES حتى يتم حفظ سجل العمل ويمكنك دائمًا العودة وفهم ما تغير. لذلك، نقوم بزيادة الإصدار بوحدة واحدة:
<version>0.7.0-SNAPSHOT</version>
وقم بتحديث RELEASE_NOTES:
## 0.7.0-SNAPSHOT * JRTB-4: تمت إضافة إمكانية إرسال إشعارات حول المقالات الجديدة * JRTB-8: تمت إضافة إمكانية تعيين مستخدم برقية غير نشط * JRTB-9: تمت إضافة إمكانية تعيين مستخدم نشط و/أو البدء في استخدامه.
يمكنك الآن إنشاء طلب سحب وتحميل التغييرات الجديدة. إليك طلب السحب مع كافة التغييرات في جزأين: STEP_8 . ماذا بعد؟ يبدو أن كل شيء جاهز، وكما نقول، يمكن أن يدخل مرحلة الإنتاج، ولكن لا تزال هناك بعض الأشياء التي أريد القيام بها. على سبيل المثال، قم بتكوين عمل المسؤولين للبوت، وإضافتهم وإضافة القدرة على تعيينهم. إنها لفكرة جيدة أيضًا مراجعة الكود قبل الانتهاء ومعرفة ما إذا كانت هناك أشياء يمكن إعادة هيكلتها. أستطيع بالفعل رؤية عدم التزامن في تسمية المقالة/المنشور. وفي النهاية، سنقوم بأثر رجعي لما خططنا له وما تلقيناه. وماذا تريد أن تفعل في المستقبل؟ الآن سوف أشارككم فكرة بدائية إلى حد ما والتي يمكن أن ترى النور وسترى النور: إنشاء بداية Springboot التي من شأنها أن تحتوي على جميع الوظائف اللازمة للعمل مع روبوت Telegram والبحث عن المقالات. وهذا سيجعل من الممكن توحيد النهج واستخدامه لروبوتات التلغراف الأخرى. وهذا سيجعل هذا المشروع في متناول الآخرين ويمكن أن يفيد المزيد من الناس. هذه واحدة من الأفكار. فكرة أخرى هي التعمق في تطوير الإشعارات. لكننا سنتحدث عن هذا بعد قليل. شكرًا لكم جميعًا على اهتمامكم، كالعادة: إعجاب - اشتراك - جرس ، نجمة لمشروعنا ، تعليق وتقييم المقال! شكرا للجميع على القراءة.

توجد قائمة بجميع المواد الموجودة في السلسلة في بداية هذه المقالة.

تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION