JavaRush /Java Blog /Random-ID /Menambahkan Penjadwal Musim Semi - "Proyek Java dari A hi...

Menambahkan Penjadwal Musim Semi - "Proyek Java dari A hingga Z"

Dipublikasikan di grup Random-ID
Halo semuanya, teman-teman terkasih. Pada artikel sebelumnya , kami menyiapkan klien untuk bekerja dengan JavaRush API untuk artikel. Sekarang kita bisa menulis logika untuk pekerjaan kita, yang akan dieksekusi setiap 15 menit. Persis seperti yang ditunjukkan dalam diagram ini: “Proyek Java dari A hingga Z”: Menambahkan Penjadwal Musim Semi - 1Setiap 15 menit sebuah pekerjaan akan diluncurkan (menurut kami, hanya sebuah metode di kelas tertentu), yang dijalankan di latar belakang aplikasi utama dan melakukan hal berikut:
  1. Temukan di semua grup yang ada di database kami artikel baru yang diterbitkan setelah eksekusi sebelumnya.

    Skema ini menentukan jumlah grup yang lebih kecil - hanya grup yang memiliki pengguna aktif. Pada saat itu tampaknya logis bagi saya, tetapi sekarang saya mengerti bahwa terlepas dari apakah ada pengguna aktif yang berlangganan grup tertentu atau tidak, Anda tetap harus selalu memperbarui artikel terbaru yang diproses bot. Situasi mungkin muncul ketika pengguna baru segera menerima seluruh jumlah artikel yang diterbitkan sejak grup ini dinonaktifkan. Ini bukan perilaku yang diharapkan, dan untuk menghindarinya, kami perlu selalu memperbarui grup-grup tersebut dari database kami yang saat ini tidak memiliki pengguna aktif.
  2. Jika ada artikel baru, buat pesan untuk semua pengguna yang aktif berlangganan grup ini. Jika tidak ada artikel baru, kami cukup menyelesaikan pekerjaannya.

Ngomong-ngomong, saya sudah menyebutkan di saluran TG saya bahwa bot sudah berfungsi dan mengirimkan artikel baru berdasarkan langganan. Mari mulai menulis FindNewArtcileService . Semua pekerjaan pencarian dan pengiriman pesan akan dilakukan di sana, dan pekerjaan tersebut hanya akan meluncurkan metode layanan ini:

TemukanLayananArtikel Baru:

package com.github.javarushcommunity.jrtb.service;

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

   /**
    * Find new articles and notify subscribers about it.
    */
   void findNewArticles();
}
Sangat sederhana, bukan? Inilah esensinya, dan seluruh kesulitannya terletak pada implementasinya:
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);
   }
}
Di sini kita akan menangani semuanya secara berurutan:
  1. Menggunakan groupService kami menemukan semua grup yang ada di database.

  2. Kemudian kami menyebar ke semua grup dan untuk masing-masing grup kami memanggil klien yang dibuat di artikel terakhir - javaRushPostClient.findNewPosts .

  3. Selanjutnya dengan menggunakan metode setNewArticleId kita mengupdate ID artikel dari artikel baru kita yang terbaru agar database kita mengetahui bahwa kita sudah memproses yang baru.

  4. Dan memanfaatkan fakta bahwa GroupSub memiliki kumpulan pengguna, kami menelusuri pengguna yang aktif dan mengirimkan pemberitahuan tentang artikel baru.

Kami tidak akan membahas apa pesannya sekarang, itu tidak terlalu penting bagi kami. Yang penting metodenya berhasil. Logika untuk mencari artikel baru dan mengirimkan notifikasi sudah siap, sehingga Anda dapat melanjutkan ke pembuatan pekerjaan.

Buat TemukanPekerjaanArtikelBaru

Kita sudah membicarakan tentang apa itu SpringScheduler, tapi mari kita ulangi dengan cepat: ini adalah mekanisme dalam kerangka Spring untuk membuat proses latar belakang yang akan berjalan pada waktu tertentu yang kita tetapkan. Apa yang Anda perlukan untuk ini? Langkah pertama adalah menambahkan anotasi @EnableScheduling ke kelas input pegas kita:
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);
   }

}
Langkah kedua adalah membuat kelas, menambahkannya ke ApplicationContext dan membuat metode di dalamnya yang akan dijalankan secara berkala. Kita membuat paket pekerjaan pada level yang sama dengan repositori, layanan, dan sebagainya, dan di sana kita membuat kelas 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));
   }
}
Untuk menambahkan kelas ini ke Konteks Aplikasi saya menggunakan anotasi @Component . Dan agar metode di dalam kelas mengetahui bahwa metode tersebut perlu dijalankan secara berkala, saya menambahkan anotasi ke metode tersebut: @Scheduled(fixedRateString = "${bot.recountNewArticleFixedRate}") . Tapi kami mengaturnya di file application.properties:
bot.recountNewArticleFixedRate = 900000
Di sini nilainya dalam milidetik. Ini akan menjadi 15 menit. Dalam metode ini, semuanya sederhana: Saya menambahkan metrik super sederhana untuk diri saya sendiri di log untuk menghitung pencarian artikel baru, untuk setidaknya memahami secara kasar seberapa cepat kerjanya.

Menguji fungsionalitas baru

Sekarang kami akan menguji pada bot pengujian kami. Tapi bagaimana caranya? Saya tidak akan menghapus artikel setiap kali untuk menunjukkan bahwa pemberitahuan telah tiba? Tentu saja tidak. Kami cukup mengedit data di database dan meluncurkan aplikasi. Saya akan mengujinya di server pengujian saya. Untuk melakukan ini, mari berlangganan beberapa grup. Ketika langganan selesai, grup akan diberikan ID artikel terbaru saat ini. Mari masuk ke database dan ubah kembali nilai dua artikel. Hasilnya, kami berharap akan ada artikel sebanyak yang kami tetapkan lastArticleId sebelumnya . "Proyek Java dari A hingga Z": Menambahkan Penjadwal Musim Semi - 2Selanjutnya, kita pergi ke situsnya, mengurutkan artikel di grup proyek Java - yang baru terlebih dahulu - dan pergi ke artikel ketiga dari daftar: "Proyek Java dari A hingga Z": Menambahkan Penjadwal Musim Semi - 3Mari kita pergi ke artikel paling bawah dan dari bilah alamat kita mendapatkan Id artikel - 3313: "Proyek Java dari A hingga Z": Menambahkan Penjadwal Musim Semi - 4Selanjutnya , buka MySQL Workbench dan ubah nilai lastArticleId menjadi 3313. Mari kita lihat apakah grup tersebut ada di database: "Proyek Java dari A hingga Z": Menambahkan Penjadwal Musim Semi - 5Dan untuk itu kita akan menjalankan perintah: "Proyek Java dari A hingga Z": Menambahkan Penjadwal Musim Semi - 6Dan selesai, sekarang Anda harus menunggu hingga peluncuran pekerjaan berikutnya untuk mencari artikel baru. Kami berharap menerima dua pesan tentang artikel baru dari grup proyek Java. Seperti yang mereka katakan, hasilnya tidak lama lagi: "Proyek Java dari A hingga Z": Menambahkan Penjadwal Musim Semi - 7Ternyata bot tersebut berfungsi seperti yang kami harapkan.

Akhir

Seperti biasa, kami memperbarui versi di pom.xml dan menambahkan entri ke RELEASE_NOTES sehingga riwayat pekerjaan disimpan dan Anda selalu dapat kembali dan memahami apa yang telah berubah. Oleh karena itu, kami menambah versi sebanyak satu unit:
<version>0.7.0-SNAPSHOT</version>
Dan perbarui RELEASE_NOTES:
## 0.7.0-SNAPSHOT * JRTB-4: menambahkan kemampuan untuk mengirim pemberitahuan tentang artikel baru * JRTB-8: menambahkan kemampuan untuk mengatur pengguna telegram yang tidak aktif * JRTB-9: menambahkan kemampuan untuk mengatur pengguna aktif dan/atau mulai menggunakannya.
Sekarang Anda dapat membuat permintaan tarik dan mengunggah perubahan baru. Berikut ini permintaan penarikan dengan semua perubahan dalam dua bagian: STEP_8 . Apa berikutnya? Tampaknya semuanya sudah siap dan, seperti yang kami katakan, bisa mulai diproduksi, tetapi masih ada beberapa hal yang ingin saya lakukan. Misalnya, konfigurasikan pekerjaan admin untuk bot, tambahkan mereka, dan tambahkan kemampuan untuk mengaturnya. Ada baiknya juga untuk membaca kode sebelum menyelesaikannya dan melihat apakah ada hal yang dapat difaktorkan ulang. Saya sudah bisa melihat desinkronisasi pada penamaan artikel/postingan. Pada akhirnya, kami akan melakukan retrospeksi terhadap apa yang kami rencanakan dan apa yang kami terima. Dan apa yang ingin Anda lakukan di masa depan? Sekarang saya akan berbagi dengan Anda ide yang cukup kasar yang dapat dan akan menjadi kenyataan: membuat starter springboot yang memiliki semua fungsi untuk bekerja dengan bot telegram dan mencari artikel. Ini akan memungkinkan untuk menyatukan pendekatan dan menggunakannya untuk bot telegram lainnya. Hal ini akan membuat proyek ini lebih mudah diakses oleh orang lain dan dapat memberi manfaat bagi lebih banyak orang. Ini adalah salah satu idenya. Ide lainnya adalah mempelajari lebih dalam pengembangan notifikasi. Tapi kita akan membicarakannya nanti. Terima kasih atas perhatian Anda, seperti biasa: suka - berlangganan - bel , bintangi proyek kami , komentari, dan beri peringkat artikel! Terima kasih semuanya telah membaca.

Daftar semua materi dalam seri ini ada di awal artikel ini.

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