JavaRush /Blog Java /Random-MS /Kami mengalih keluar langganan artikel daripada kumpulan ...

Kami mengalih keluar langganan artikel daripada kumpulan - "Projek Java dari A hingga Z"

Diterbitkan dalam kumpulan
Halo semua, rakan-rakan yang dikasihi, Jurutera Perisian Kanan masa depan. Kami terus membangunkan bot telegram. Dalam langkah projek kami ini, kami akan melihat tiga tugasan yang mempunyai nilai yang lebih ketara daripada nilai perisian. Kita perlu belajar cara mengalih keluar langganan artikel baharu daripada kumpulan tertentu: gunakan perintah /stop untuk menyahaktifkan bot dan gunakan perintah /start untuk mengaktifkannya. Dan supaya semua permintaan dan kemas kini hanya melibatkan pengguna aktif bot. Seperti biasa, kami akan mengemas kini cawangan utama untuk mendapatkan semua perubahan dan mencipta yang baharu: STEP_7_JRTB-7. Dalam bahagian ini, kita akan bercakap tentang memadamkan langganan dan mempertimbangkan 5 pilihan untuk acara - ia akan menjadi menarik.

JRTB-7: mengalih keluar langganan artikel baharu daripada kumpulan

Jelas sekali bahawa semua pengguna akan mahu dapat memadamkan langganan mereka supaya tidak menerima pemberitahuan tentang artikel baharu. Logiknya akan sangat serupa dengan logik menambah langganan. Jika kami menghantar hanya satu arahan, sebagai tindak balas, kami akan menerima senarai kumpulan dan ID mereka yang telah dilanggan oleh pengguna, supaya kami dapat memahami apa sebenarnya yang perlu dipadamkan. Dan jika pengguna menghantar ID kumpulan bersama pasukan, kami akan memadamkan langganan. Oleh itu, mari kita bangunkan arahan ini dari bahagian bot telegram.
  1. Mari tambahkan nama arahan baharu - /deleteGroupSub , dan dalam CommandName - baris:

    DELETE_GROUP_SUB("/deleteGroupSub")

  2. Seterusnya, mari buat arahan DeleteGroupSubCommand :

    package com.github.javarushcommunity.jrtb.command;
    
    import com.github.javarushcommunity.jrtb.repository.entity.GroupSub;
    import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
    import com.github.javarushcommunity.jrtb.service.GroupSubService;
    import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
    import com.github.javarushcommunity.jrtb.service.TelegramUserService;
    import org.springframework.util.CollectionUtils;
    import org.telegram.telegrambots.meta.api.objects.Update;
    
    import javax.ws.rs.NotFoundException;
    import java.util.List;
    import java.util.Optional;
    import java.util.stream.Collectors;
    
    import static com.github.javarushcommunity.jrtb.command.CommandName.DELETE_GROUP_SUB;
    import static com.github.javarushcommunity.jrtb.command.CommandUtils.getChatId;
    import static com.github.javarushcommunity.jrtb.command.CommandUtils.getMessage;
    import static java.lang.String.format;
    import static org.apache.commons.lang3.StringUtils.SPACE;
    import static org.apache.commons.lang3.StringUtils.isNumeric;
    
    /**
    * Delete Group subscription {@link Command}.
    */
    public class DeleteGroupSubCommand implements Command {
    
       private final SendBotMessageService sendBotMessageService;
       private final TelegramUserService telegramUserService;
       private final GroupSubService groupSubService;
    
       public DeleteGroupSubCommand(SendBotMessageService sendBotMessageService, GroupSubService groupSubService,
                                    TelegramUserService telegramUserService) {
           this.sendBotMessageService = sendBotMessageService;
           this.groupSubService = groupSubService;
           this.telegramUserService = telegramUserService;
       }
    
       @Override
       public void execute(Update update) {
           if (getMessage(update).equalsIgnoreCase(DELETE_GROUP_SUB.getCommandName())) {
               sendGroupIdList(getChatId(update));
               return;
           }
           String groupId = getMessage(update).split(SPACE)[1];
           String chatId = getChatId(update);
           if (isNumeric(groupId)) {
               Optional<GroupSub> optionalGroupSub = groupSubService.findById(Integer.valueOf(groupId));
               if (optionalGroupSub.isPresent()) {
                   GroupSub groupSub = optionalGroupSub.get();
                   TelegramUser telegramUser = telegramUserService.findByChatId(chatId).orElseThrow(NotFoundException::new);
                   groupSub.getUsers().remove(telegramUser);
                   groupSubService.save(groupSub);
                   sendBotMessageService.sendMessage(chatId, format("Удалил подписку на группу: %s", groupSub.getTitle()));
               } else {
                   sendBotMessageService.sendMessage(chatId, "Не нашел такой группы =/");
               }
           } else {
               sendBotMessageService.sendMessage(chatId, "неправильный формат ID группы.\n " +
                       "ID должно быть целым положительным числом");
           }
       }
    
       private void sendGroupIdList(String chatId) {
           String message;
           List<GroupSub> groupSubs = telegramUserService.findByChatId(chatId)
                   .orElseThrow(NotFoundException::new)
                   .getGroupSubs();
           if (CollectionUtils.isEmpty(groupSubs)) {
               message = "Пока нет подписок на группы. Whatбы добавить подписку напиши /addGroupSub";
           } else {
               message = "Whatбы удалить подписку на группу - передай комадну вместе с ID группы. \n" +
                       "Например: /deleteGroupSub 16 \n\n" +
                       "я подготовил список всех групп, на которые ты подписан) \n\n" +
                       "Name группы - ID группы \n\n" +
                       "%s";
    
           }
           String userGroupSubData = groupSubs.stream()
                   .map(group -> format("%s - %s \n", group.getTitle(), group.getId()))
                   .collect(Collectors.joining());
    
           sendBotMessageService.sendMessage(chatId, format(message, userGroupSubData));
       }
    }

Untuk melakukan ini, kami perlu menambah dua lagi kaedah untuk bekerja dengan entiti GroupSub - mendapatkan semula daripada pangkalan data dengan ID dan menyimpan entiti itu sendiri. Semua kaedah ini hanya memanggil kaedah repositori siap sedia. Saya akan memberitahu anda secara berasingan tentang memadamkan langganan. Dalam skema pangkalan data, ini ialah jadual yang bertanggungjawab untuk proses banyak-ke-banyak, dan untuk memadamkan hubungan ini, anda perlu memadamkan rekod di dalamnya. Ini adalah jika kita menggunakan pemahaman umum di bahagian pangkalan data. Tetapi kami menggunakan Spring Data dan terdapat Hibernate secara lalai, yang boleh melakukan ini secara berbeza. Kami mendapat entiti GroupSub, yang mana semua pengguna yang dikaitkan dengannya akan dilukis. Daripada koleksi pengguna ini kami akan mengalih keluar yang kami perlukan dan menyimpan groupSub kembali ke dalam pangkalan data, tetapi tanpa pengguna ini. Dengan cara ini Spring Data akan memahami perkara yang kami mahu dan memadamkan rekod. "Projek Java dari A hingga Z": Mengalih keluar langganan artikel daripada kumpulan - 1"Projek Java dari A hingga Z": Mengalih keluar langganan artikel daripada kumpulan - 2Untuk mengalih keluar pengguna dengan cepat, saya menambahkan anotasi EqualsAndHashCode untuk TelegramUser, tidak termasuk senarai GroupSub supaya tiada masalah. Dan dipanggil kaedah alih keluar pada pengumpulan pengguna dengan pengguna yang kami perlukan. Ini adalah rupa untuk TelegramUser:
@Data
@Entity
@Table(name = "tg_user")
@EqualsAndHashCode(exclude = "groupSubs")
public class TelegramUser {

   @Id
   @Column(name = "chat_id")
   private String chatId;

   @Column(name = "active")
   private boolean active;

   @ManyToMany(mappedBy = "users", fetch = FetchType.EAGER)
   private List<GroupSub> groupSubs;
}
Akibatnya, semuanya berjalan seperti yang kita mahu. Terdapat beberapa kemungkinan acara dalam satu pasukan, jadi menulis ujian yang baik untuk setiap daripada mereka adalah idea yang bagus. Bercakap tentang ujian: semasa saya menulisnya, saya mendapati kecacatan pada logik dan membetulkannya sebelum ia dikeluarkan ke dalam pengeluaran. Sekiranya tiada ujian, tidak jelas berapa cepat ia akan dikesan. DeleteGroupSubCommandTest:
package com.github.javarushcommunity.jrtb.command;

import com.github.javarushcommunity.jrtb.repository.entity.GroupSub;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import com.github.javarushcommunity.jrtb.service.GroupSubService;
import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import com.github.javarushcommunity.jrtb.service.TelegramUserService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.telegram.telegrambots.meta.api.objects.Update;

import java.util.ArrayList;
import java.util.Optional;

import static com.github.javarushcommunity.jrtb.command.AbstractCommandTest.prepareUpdate;
import static com.github.javarushcommunity.jrtb.command.CommandName.DELETE_GROUP_SUB;
import static java.util.Collections.singletonList;

@DisplayName("Unit-level testing for DeleteGroupSubCommand")
class DeleteGroupSubCommandTest {

   private Command command;
   private SendBotMessageService sendBotMessageService;
   GroupSubService groupSubService;
   TelegramUserService telegramUserService;


   @BeforeEach
   public void init() {
       sendBotMessageService = Mockito.mock(SendBotMessageService.class);
       groupSubService = Mockito.mock(GroupSubService.class);
       telegramUserService = Mockito.mock(TelegramUserService.class);

       command = new DeleteGroupSubCommand(sendBotMessageService, groupSubService, telegramUserService);
   }

   @Test
   public void shouldProperlyReturnEmptySubscriptionList() {
       //given
       Long chatId = 23456L;
       Update update = prepareUpdate(chatId, DELETE_GROUP_SUB.getCommandName());

       Mockito.when(telegramUserService.findByChatId(String.valueOf(chatId)))
               .thenReturn(Optional.of(new TelegramUser()));

       String expectedMessage = "Пока нет подписок на группы. Whatбы добавить подписку напиши /addGroupSub";

       //when
       command.execute(update);

       //then
       Mockito.verify(sendBotMessageService).sendMessage(chatId.toString(), expectedMessage);
   }

   @Test
   public void shouldProperlyReturnSubscriptionLit() {
       //given
       Long chatId = 23456L;
       Update update = prepareUpdate(chatId, DELETE_GROUP_SUB.getCommandName());
       TelegramUser telegramUser = new TelegramUser();
       GroupSub gs1 = new GroupSub();
       gs1.setId(123);
       gs1.setTitle("GS1 Title");
       telegramUser.setGroupSubs(singletonList(gs1));
       Mockito.when(telegramUserService.findByChatId(String.valueOf(chatId)))
               .thenReturn(Optional.of(telegramUser));

       String expectedMessage = "Whatбы удалить подписку на группу - передай комадну вместе с ID группы. \n" +
               "Например: /deleteGroupSub 16 \n\n" +
               "я подготовил список всех групп, на которые ты подписан) \n\n" +
               "Name группы - ID группы \n\n" +
               "GS1 Title - 123 \n";

       //when
       command.execute(update);

       //then
       Mockito.verify(sendBotMessageService).sendMessage(chatId.toString(), expectedMessage);
   }

   @Test
   public void shouldRejectByInvalidGroupId() {
       //given
       Long chatId = 23456L;
       Update update = prepareUpdate(chatId, String.format("%s %s", DELETE_GROUP_SUB.getCommandName(), "groupSubId"));
       TelegramUser telegramUser = new TelegramUser();
       GroupSub gs1 = new GroupSub();
       gs1.setId(123);
       gs1.setTitle("GS1 Title");
       telegramUser.setGroupSubs(singletonList(gs1));
       Mockito.when(telegramUserService.findByChatId(String.valueOf(chatId)))
               .thenReturn(Optional.of(telegramUser));

       String expectedMessage = "неправильный формат ID группы.\n " +
               "ID должно быть целым положительным числом";

       //when
       command.execute(update);

       //then
       Mockito.verify(sendBotMessageService).sendMessage(chatId.toString(), expectedMessage);
   }

   @Test
   public void shouldProperlyDeleteByGroupId() {
       //given

       /// prepare update object
       Long chatId = 23456L;
       Integer groupId = 1234;
       Update update = prepareUpdate(chatId, String.format("%s %s", DELETE_GROUP_SUB.getCommandName(), groupId));


       GroupSub gs1 = new GroupSub();
       gs1.setId(123);
       gs1.setTitle("GS1 Title");
       TelegramUser telegramUser = new TelegramUser();
       telegramUser.setChatId(chatId.toString());
       telegramUser.setGroupSubs(singletonList(gs1));
       ArrayList<TelegramUser> users = new ArrayList<>();
       users.add(telegramUser);
       gs1.setUsers(users);
       Mockito.when(groupSubService.findById(groupId)).thenReturn(Optional.of(gs1));
       Mockito.when(telegramUserService.findByChatId(String.valueOf(chatId)))
               .thenReturn(Optional.of(telegramUser));

       String expectedMessage = "Удалил подписку на группу: GS1 Title";

       //when
       command.execute(update);

       //then
       users.remove(telegramUser);
       Mockito.verify(groupSubService).save(gs1);
       Mockito.verify(sendBotMessageService).sendMessage(chatId.toString(), expectedMessage);
   }

   @Test
   public void shouldDoesNotExistByGroupId() {
       //given
       Long chatId = 23456L;
       Integer groupId = 1234;
       Update update = prepareUpdate(chatId, String.format("%s %s", DELETE_GROUP_SUB.getCommandName(), groupId));


       Mockito.when(groupSubService.findById(groupId)).thenReturn(Optional.empty());

       String expectedMessage = "Не нашел такой группы =/";

       //when
       command.execute(update);

       //then
       Mockito.verify(groupSubService).findById(groupId);
       Mockito.verify(sendBotMessageService).sendMessage(chatId.toString(), expectedMessage);
   }
}
Di sini, setiap ujian menyemak senario yang berasingan, dan izinkan saya mengingatkan anda, hanya terdapat lima daripadanya:
  • apabila anda hanya lulus arahan /deleteGroupSub dan tiada langganan kumpulan;
  • apabila anda hanya lulus arahan /deleteGroupSub dan terdapat langganan kepada kumpulan;
  • apabila ID kumpulan yang tidak sah telah diluluskan, contohnya, /deleteGroupSub abc ;
  • senario di mana segala-galanya dipadamkan dengan betul, seperti yang dijangkakan;
  • senario apabila ID kumpulan adalah sah, tetapi kumpulan sedemikian tiada dalam pangkalan data.
Seperti yang anda lihat, semua senario ini perlu diliputi dengan ujian. Semasa saya menulis, saya menyedari bahawa untuk menulis ujian yang lebih baik, adalah berbaloi untuk mengambil beberapa kursus ujian. Saya fikir ini akan membantu mencari pilihan yang berbeza dengan betul. Betul, pemikiran untuk masa depan. Seterusnya, anda perlu menambah penerangan pada arahan /help yang kini anda boleh memadamkan langganan. Mari letakkannya di bahagian untuk bekerja dengan langganan. "Projek Java dari A hingga Z": Mengalih keluar langganan artikel daripada kumpulan - 3Sudah tentu, untuk arahan ini berfungsi, anda perlu menambah permulaannya pada CommandContainer :
.put(DELETE_GROUP_SUB.getCommandName(),
       new DeleteGroupSubCommand(sendBotMessageService, groupSubService, telegramUserService))
Kini anda boleh menguji kefungsian pada bot ujian. Kami melancarkan pangkalan data kami menggunakan docker-compose-test.yml: docker-compose -f docker-compose-test.yml up Dan melancarkan SpringBoot melalui IDEA. Saya akan mengosongkan surat-menyurat sepenuhnya dengan bot dan mula semula. Saya akan menjalankan semua pilihan yang mungkin timbul apabila bekerja dengan pasukan ini. "Projek Java dari A hingga Z": Mengalih keluar langganan artikel daripada kumpulan - 4Seperti yang anda boleh lihat daripada tangkapan skrin, semua pilihan telah melalui dan berjaya.
Kawan-kawan! Adakah anda ingin tahu dengan segera apabila kod baharu untuk projek dikeluarkan? Bilakah artikel baru keluar? Sertai saluran Telegram saya . Di sana saya mengumpulkan artikel, pemikiran dan pembangunan sumber terbuka saya bersama-sama.
Kami mengemas kini versi projek kami kepada 0.6.0-SNAPSHOT Kami mengemas kini RELEASE_NOTES.md, menambah penerangan tentang perkara yang telah dilakukan dalam versi baharu:
## 0.6.0-SNAPSHOT * JRTB-7: menambahkan keupayaan untuk memadam langganan kumpulan.
Kod berfungsi, ujian telah ditulis untuknya: sudah tiba masanya untuk menolak tugasan ke dalam repositori dan membuat permintaan tarik."Projek Java dari A hingga Z": Mengalih keluar langganan artikel daripada kumpulan - 5

Bukannya berakhir

Kami telah lama tidak melihat papan projek kami, tetapi terdapat perubahan besar: "Projek Java dari A hingga Z": Mengalih keluar langganan artikel daripada kumpulan - 6Hanya ada 5 tugasan lagi. Maksudnya, awak dan saya sudah berada di penghujung jalan. Tinggal sikit. Amat menarik untuk diperhatikan bahawa siri artikel ini telah berjalan sejak pertengahan September, iaitu selama 7 bulan!!! Apabila saya mendapat idea ini, saya tidak menjangka ia akan mengambil masa yang lama. Pada masa yang sama, saya sangat gembira dengan hasilnya! Rakan-rakan, jika tidak jelas apa yang berlaku dalam artikel, tanya soalan dalam komen. Dengan cara ini saya akan tahu bahawa sesuatu perlu diterangkan dengan lebih baik, dan sesuatu memerlukan penjelasan lanjut. Baiklah, seperti biasa, suka - langgan - bunyikan loceng, berikan projek kami bintang , tulis komen dan nilaikan artikel itu! Terima kasih kepada semua. Sehingga bahagian seterusnya. Kami akan bercakap tidak lama lagi tentang cara menambah penyahaktifan dan pengaktifan bot melalui arahan /stop & /start dan cara terbaik menggunakannya. Jumpa lagi!

Senarai semua bahan dalam siri ini adalah pada permulaan artikel ini.

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