JavaRush /Java Blog /Random-TL /Inalis namin ang subscription sa mga artikulo mula sa pan...

Inalis namin ang subscription sa mga artikulo mula sa pangkat - "Java project from A to Z"

Nai-publish sa grupo
Kumusta sa lahat, mahal kong mga kaibigan, sa hinaharap na mga Senior Software Engineer. Patuloy kaming bumuo ng telegram bot. Sa hakbang na ito ng aming proyekto, isasaalang-alang namin ang tatlong gawain na may mas nakikitang halaga kaysa sa halaga ng programa. Kailangan nating matutunan kung paano mag-alis ng subscription sa mga bagong artikulo mula sa isang partikular na grupo: gamitin ang /stop command para i-deactivate ang bot, at gamitin ang /start command para i-activate ito. Higit pa rito, ang lahat ng mga kahilingan at update ay tungkol lamang sa mga aktibong user ng bot. Gaya ng dati, ia-update namin ang pangunahing sangay para makuha ang lahat ng pagbabago at gumawa ng bago: STEP_7_JRTB-7. Sa bahaging ito, pag-uusapan natin ang tungkol sa pagtanggal ng subscription at isaalang-alang ang 5 opsyon para sa mga kaganapan - magiging kawili-wili ito.

JRTB-7: pag-alis ng subscription sa mga bagong artikulo mula sa isang grupo

Malinaw na gugustuhin ng lahat ng user na ma-delete ang kanilang subscription para hindi makatanggap ng mga notification tungkol sa mga bagong artikulo. Ang lohika nito ay magiging halos kapareho sa lohika ng pagdaragdag ng isang subscription. Kung magpapadala lamang kami ng isang utos, bilang tugon makakatanggap kami ng isang listahan ng mga grupo at kanilang mga ID kung saan naka-subscribe na ang user, upang maunawaan namin kung ano ang eksaktong kailangang tanggalin. At kung ipapadala ng user ang group ID kasama ang team, tatanggalin namin ang subscription. Samakatuwid, gawin natin ang utos na ito mula sa gilid ng telegram bot.
  1. Idagdag natin ang pangalan ng bagong command - /deleteGroupSub , at sa CommandName - ang linya:

    DELETE_GROUP_SUB("/deleteGroupSub")

  2. Susunod, gawin natin ang DeleteGroupSubCommand command :

    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));
       }
    }

Upang gawin ito, kailangan naming magdagdag ng dalawa pang pamamaraan para sa pagtatrabaho sa GroupSub entity - pagkuha mula sa database sa pamamagitan ng ID at pag-save ng entity mismo. Ang lahat ng mga pamamaraan na ito ay tumawag lamang ng mga yari na pamamaraan ng imbakan. Sasabihin ko sa iyo nang hiwalay ang tungkol sa pagtanggal ng subscription. Sa schema ng database, ito ang talahanayan na may pananagutan para sa prosesong marami-sa-maraming, at para tanggalin ang kaugnayang ito, kailangan mong tanggalin ang rekord dito. Ito ay kung gagamitin natin ang pangkalahatang pag-unawa sa bahagi ng database. Ngunit ginagamit namin ang Spring Data at mayroong Hibernate bilang default, na maaaring gawin ito sa ibang paraan. Nakukuha namin ang GroupSub entity, kung saan ang lahat ng user na nauugnay dito ay iguguhit. Mula sa koleksyong ito ng mga user, aalisin namin ang kailangan namin at i-save ang groupSub pabalik sa database, ngunit wala ang user na ito. Sa ganitong paraan mauunawaan ng Spring Data kung ano ang gusto namin at tatanggalin ang record. "Proyekto ng Java mula A hanggang Z": Pag-alis ng subscription sa mga artikulo mula sa pangkat - 1"Proyekto ng Java mula A hanggang Z": Pag-alis ng subscription sa mga artikulo mula sa pangkat - 2Upang mabilis na maalis ang isang user, idinagdag ko ang EqualsAndHashCode annotation sa TelegramUser, hindi kasama ang listahan ng GroupSub upang walang mga problema. At tinatawag na paraan ng pag-alis sa koleksyon ng mga user sa user na kailangan namin. Ito ang hitsura para sa 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;
}
Bilang isang resulta, ang lahat ay umayos ayon sa gusto namin. Mayroong ilang mga posibleng kaganapan sa isang koponan, kaya ang pagsulat ng isang mahusay na pagsubok para sa bawat isa sa kanila ay isang magandang ideya. Sa pagsasalita ng mga pagsubok: habang sinusulat ko ang mga ito, nakakita ako ng depekto sa lohika at itinama ito bago ito ilabas sa produksyon. Kung walang pagsubok, hindi malinaw kung gaano kabilis ito natukoy. 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);
   }
}
Dito, sinusuri ng bawat pagsubok ang isang hiwalay na senaryo, at hayaan mong ipaalala ko sa iyo, lima lang sa kanila:
  • kapag ipinasa mo lang ang utos na /deleteGroupSub at walang mga subscription sa grupo;
  • kapag ipinasa mo lang ang utos na /deleteGroupSub at may mga subscription sa mga grupo;
  • kapag naipasa ang isang di-wastong ID ng grupo, halimbawa, /deleteGroupSub abc ;
  • isang senaryo kung saan ang lahat ay tinanggal nang tama, tulad ng inaasahan;
  • isang senaryo kapag valid ang group ID, ngunit wala sa database ang naturang grupo.
Tulad ng nakikita mo, ang lahat ng mga sitwasyong ito ay kailangang saklawin ng mga pagsubok. Habang nagsusulat ako, napagtanto ko na para makapagsulat ng mas mahuhusay na pagsusulit, sulit na kumuha ng ilang kurso sa pagsubok. Sa tingin ko ito ay makakatulong upang maayos na maghanap ng iba't ibang mga opsyon. Tama, mga saloobin para sa hinaharap. Susunod, kailangan mong magdagdag ng paglalarawan sa command na /help na maaari mo na ngayong tanggalin ang subscription. Ilagay natin ito sa seksyon para sa pagtatrabaho sa mga subscription. "Proyekto ng Java mula A hanggang Z": Pag-alis ng subscription sa mga artikulo mula sa pangkat - 3Siyempre, para gumana ang command na ito, kailangan mong idagdag ang initialization nito sa CommandContainer :
.put(DELETE_GROUP_SUB.getCommandName(),
       new DeleteGroupSubCommand(sendBotMessageService, groupSubService, telegramUserService))
Ngayon ay maaari mong subukan ang functionality sa isang pansubok na bot. Inilunsad namin ang aming database gamit ang docker-compose-test.yml: docker-compose -f docker-compose-test.yml up At ilunsad ang SpringBoot sa pamamagitan ng IDEA. Ganap kong aalisin ang mga sulat sa bot at magsimulang muli. Tatakbo ako sa lahat ng mga opsyon na maaaring lumabas kapag nagtatrabaho sa team na ito. "Proyekto ng Java mula A hanggang Z": Pag-alis ng subscription sa mga artikulo mula sa pangkat - 4Tulad ng nakikita mo mula sa screenshot, lahat ng mga opsyon ay dumaan at naging matagumpay.
Kaibigan! Gusto mo bang malaman kaagad kung kailan inilabas ang bagong code para sa isang proyekto? Kailan lalabas ang isang bagong artikulo? Sumali sa aking Telegram channel . Doon ko kinokolekta ang aking mga artikulo, kaisipan at open source development nang magkasama.
Ina-update namin ang bersyon ng aming proyekto sa 0.6.0-SNAPSHOT Ina-update namin ang RELEASE_NOTES.md, nagdaragdag ng paglalarawan ng kung ano ang nagawa sa bagong bersyon:
## 0.6.0-SNAPSHOT * JRTB-7: nagdagdag ng kakayahang magtanggal ng subscription ng grupo.
Gumagana ang code, isinulat ang mga pagsubok para dito: oras na upang itulak ang gawain sa repository at lumikha ng kahilingan sa paghila."Proyekto ng Java mula A hanggang Z": Pag-alis ng subscription sa mga artikulo mula sa grupo - 5

Sa halip na tapusin

Matagal na kaming hindi tumitingin sa aming project board, ngunit may malalaking pagbabago: "Java project from A to Z": Pag-alis ng subscription sa mga artikulo mula sa grupo - 65 na lang ang natitira. Ibig sabihin, ikaw at ako ay nasa pinakadulo na ng daan. Umalis ng kaunti. Ito ay lalong kawili-wiling tandaan na ang seryeng ito ng mga artikulo ay tumatakbo mula noong kalagitnaan ng Setyembre, iyon ay, sa loob ng 7 buwan!!! Nang magkaroon ako ng ideyang ito, hindi ko inaasahan na magtatagal ito. Kasabay nito, higit na nasisiyahan ako sa resulta! Mga kaibigan, kung hindi malinaw kung ano ang nangyayari sa artikulo, magtanong sa mga komento. Sa ganitong paraan malalaman ko na may kailangang mas mailarawan, at may nangangailangan ng karagdagang paliwanag. Buweno, gaya ng dati, i-like - mag-subscribe - i-ring ang kampana, bigyan ng bituin ang aming proyekto , magsulat ng mga komento at i-rate ang artikulo! Salamat sa lahat. Hanggang sa susunod na bahagi. Pag-uusapan natin sa lalong madaling panahon kung paano magdagdag ng pag-deactivate at pag-activate ng bot sa pamamagitan ng mga command na /stop & /start at kung paano pinakamahusay na gamitin ang mga ito. See you later!

Ang isang listahan ng lahat ng mga materyales sa serye ay nasa simula ng artikulong ito.

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