JavaRush /Blog Jawa /Random-JV /Kita mbusak langganan artikel saka grup - "Proyek Jawa sa...

Kita mbusak langganan artikel saka grup - "Proyek Jawa saka A nganti Z"

Diterbitake ing grup
Halo kabeh, kanca-kanca sing daktresnani, Insinyur Perangkat Lunak Senior ing mangsa ngarep. Kita terus ngembangake bot telegram. Ing langkah proyek iki, kita bakal nimbang telung tugas sing duwe nilai luwih katon tinimbang nilai program. Kita kudu sinau carane mbusak langganan artikel anyar saka grup tartamtu: gunakake perintah / stop kanggo mateni bot, lan gunakake perintah / start kanggo ngaktifake. Kajaba iku, kabeh panjalukan lan nganyari mung ditrapake kanggo pangguna aktif bot. Kaya biasane, kita bakal nganyari cabang utama kanggo entuk kabeh owah-owahan lan nggawe sing anyar: STEP_7_JRTB-7. Ing bagean iki, kita bakal ngomong babagan mbusak langganan lan nimbang opsi 5 kanggo acara - bakal menarik.

JRTB-7: mbusak langganan artikel anyar saka grup

Cetha yen kabeh pangguna pengin bisa mbusak langganan supaya ora nampa kabar babagan artikel anyar. Logika bakal padha banget karo logika nambah langganan. Yen kita ngirim mung siji printah, nanggepi kita bakal nampa dhaptar grup lan ID sing pangguna wis langganan, supaya kita bisa ngerti apa persis sing kudu dibusak. Lan yen pangguna ngirim ID grup bebarengan karo tim, kita bakal mbusak langganan. Mula, ayo gawe perintah iki saka sisih bot telegram.
  1. Ayo nambah jeneng printah anyar - /deleteGroupSub , lan ing CommandName - baris:

    DELETE_GROUP_SUB("/deleteGroupSub")

  2. Sabanjure, ayo gawe perintah 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));
       }
    }

Kanggo nindakake iki, kita kudu nambah rong cara liyane kanggo nggarap entitas GroupSub - njupuk saka database kanthi ID lan nyimpen entitas kasebut dhewe. Kabeh cara kasebut mung nelpon metode repositori sing wis siap. Aku bakal ngandhani kanthi kapisah babagan mbusak langganan. Ing skema database, iki minangka tabel sing tanggung jawab kanggo proses akeh-kanggo-akeh, lan kanggo mbusak hubungan iki, sampeyan kudu mbusak rekaman kasebut. Iki yen kita nggunakake pangerten umum ing bagean saka database. Nanging kita nggunakake Spring Data lan ana Hibernate minangka standar, sing bisa nindakake iki kanthi beda. Kita entuk entitas GroupSub, sing bakal ditarik kabeh pangguna sing ana gandhengane. Saka koleksi pangguna iki, kita bakal mbusak sing dibutuhake lan nyimpen groupSub bali menyang database, nanging tanpa pangguna iki. Kanthi cara iki Spring Data bakal ngerti apa sing dikarepake lan mbusak rekaman kasebut. "Proyek Jawa saka A nganti Z": Mbusak langganan artikel saka grup - 1"Proyek Jawa saka A nganti Z": Mbusak langganan artikel saka grup - 2Kanggo mbusak pangguna kanthi cepet, aku nambahake anotasi EqualsAndHashCode kanggo TelegramUser, ora kalebu dhaptar GroupSub supaya ora ana masalah. Lan diarani cara mbusak ing koleksi pangguna karo pangguna sing kita butuhake. Iki kaya kanggo 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;
}
Akibaté, kabeh njupuk mati kaya kita wanted. Ana sawetara acara sing bisa ditindakake ing tim, mula nulis tes sing apik kanggo saben-saben minangka ide sing apik. Ngomong babagan tes: nalika aku nulis, aku nemokake cacat ing logika lan mbenerake sadurunge dirilis menyang produksi. Yen ora ana tes, ora jelas sepira cepet bisa dideteksi. 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);
   }
}
Ing kene, saben tes mriksa skenario sing kapisah, lan aku ngelingake sampeyan, mung ana lima:
  • nalika sampeyan mung ngliwati printah /deleteGroupSub lan ora ana langganan grup;
  • nalika sampeyan mung ngliwati perintah /deleteGroupSub lan ana langganan grup;
  • nalika ID grup sing ora bener diwarisake, contone, /deleteGroupSub abc ;
  • skenario sing kabeh wis dibusak kanthi bener, kaya sing dikarepake;
  • skenario nalika ID grup bener, nanging grup kuwi ora ing database.
Kaya sing sampeyan ngerteni, kabeh skenario kasebut kudu dites. Nalika aku nulis, aku nyadari yen kanggo nulis tes sing luwih apik, sampeyan kudu njupuk sawetara kursus tes. Aku iki bakal bantuan kanggo mlaku nggoleki opsi beda. Bener, pikiran kanggo masa depan. Sabanjure, sampeyan kudu nambah katrangan menyang /help printah sing saiki bisa mbusak langganan. Ayo diselehake ing bagean kanggo nggarap langganan. "Proyek Jawa saka A nganti Z": Mbusak langganan artikel saka grup - 3Mesthi, supaya printah iki bisa digunakake, sampeyan kudu nambah initialization menyang CommandContainer :
.put(DELETE_GROUP_SUB.getCommandName(),
       new DeleteGroupSubCommand(sendBotMessageService, groupSubService, telegramUserService))
Saiki sampeyan bisa nyoba fungsi ing bot test. Kita miwiti database nggunakake docker-compose-test.yml: docker-compose -f docker-compose-test.yml munggah Lan miwiti SpringBoot liwat IDEA. Aku bakal rampung mbusak Correspondence karo bot lan miwiti maneh. Aku bakal mbukak kabeh opsi sing bisa kedadeyan nalika nggarap tim iki. "Proyek Jawa saka A nganti Z": Mbusak langganan artikel saka grup - 4Minangka sampeyan bisa ndeleng saka gambar, kabeh opsi wis rampung lan sukses.
Kanca-kanca! Apa sampeyan pengin langsung ngerti nalika kode anyar kanggo proyek dirilis? Kapan artikel anyar metu? Gabung saluran Telegramku . Ing kana aku ngumpulake artikel, pikirane lan pangembangan open source bebarengan.
Kita nganyari versi proyek kita menyang 0.6.0-SNAPSHOT Kita nganyari RELEASE_NOTES.md, nambahake katrangan babagan apa sing wis ditindakake ing versi anyar:
## 0.6.0-SNAPSHOT * JRTB-7: nambahake kemampuan kanggo mbusak langganan grup.
Kode bisa digunakake, tes wis ditulis kanggo: wektune kanggo push tugas menyang gudang lan nggawe panjalukan narik."Proyek Jawa saka A nganti Z": Mbusak langganan artikel saka grup - 5

Tinimbang mungkasi

Kita wis ora katon ing Papan project kita kanggo dangu, nanging wis owah-owahan gedhe: "Proyek Jawa saka A nganti Z": Mbusak langganan artikel saka grup - 6Ana mung 5 tugas kiwa. Tegese aku lan kowe wis ana ing pungkasaning dalan. Ditinggal sithik. Iku utamané menarik kanggo dicathet yen seri artikel iki wis mlaku wiwit pertengahan September, yaiku, suwene 7 sasi !!! Nalika aku teka munggah karo idea iki, Aku ora nyana kanggo njupuk sooo dawa. Ing wektu sing padha, aku luwih seneng karo asil! Kanca-kanca, yen ora jelas apa sing kedadeyan ing artikel kasebut, takon pitakonan ing komentar. Kanthi cara iki aku bakal ngerti yen ana sing kudu dijlentrehake kanthi luwih apik, lan ana sing butuh panjelasan luwih lanjut. Inggih, kaya biasane, kaya - langganan - muni lonceng, menehi proyek kita lintang , nulis komentar lan menehi rating artikel! Matur nuwun kanggo kabeh. Nganti bagean sabanjure. Kita bakal ngomong babagan carane nambahake panonaktifan lan aktivasi bot liwat perintah / stop & / start lan cara paling apik nggunakake. Ketemu maneh!

Dhaptar kabeh materi ing seri kasebut ana ing wiwitan artikel iki.

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