JavaRush /จาวาบล็อก /Random-TH /เราลบการสมัครสมาชิกบทความออกจากกลุ่ม - "โครงการ Java จาก ...
Roman Beekeeper
ระดับ

เราลบการสมัครสมาชิกบทความออกจากกลุ่ม - "โครงการ Java จาก A ถึง Z"

เผยแพร่ในกลุ่ม
สวัสดีทุกคน เพื่อนรัก วิศวกรซอฟต์แวร์อาวุโสในอนาคต เรายังคงพัฒนาบอทโทรเลขต่อไป ในขั้นตอนนี้ของโครงการ เราจะพิจารณางานสามงานที่มีคุณค่าที่มองเห็นได้มากกว่ามูลค่าโปรแกรม เราจำเป็นต้องเรียนรู้วิธีลบการสมัครรับบทความใหม่จากกลุ่มเฉพาะ: ใช้ คำสั่ง /stopเพื่อปิดการใช้งานบอท และใช้ คำสั่ง /startเพื่อเปิดใช้งาน นอกจากนี้ คำขอและการอัปเดตทั้งหมดเกี่ยวข้องกับผู้ใช้บอทที่ใช้งานอยู่เท่านั้น ตามปกติ เราจะอัปเดต สาขา หลักเพื่อรับการเปลี่ยนแปลงทั้งหมดและสร้างสาขาใหม่: STEP_7_JRTB-7 ในส่วนนี้เราจะพูดถึงการลบการสมัครสมาชิกและพิจารณา 5 ตัวเลือกสำหรับกิจกรรม - มันจะน่าสนใจ

JRTB-7: ลบการสมัครสมาชิกบทความใหม่ออกจากกลุ่ม

เป็นที่ชัดเจนว่าผู้ใช้ทุกคนจะต้องการลบการสมัครสมาชิกของตนเพื่อไม่ให้ได้รับการแจ้งเตือนเกี่ยวกับบทความใหม่ ตรรกะของมันจะคล้ายกับตรรกะของการเพิ่มการสมัครสมาชิกมาก หากเราส่งเพียงคำสั่งเดียว เพื่อเป็นการตอบสนองเราจะได้รับรายชื่อกลุ่มและ ID ของกลุ่มที่ผู้ใช้ได้สมัครรับข้อมูลไว้แล้ว เพื่อที่เราจะได้เข้าใจได้ว่าจำเป็นต้องลบสิ่งใดอย่างแน่นอน และหากผู้ใช้ส่ง ID กลุ่มพร้อมกับทีมงานเราจะลบการสมัครสมาชิก ดังนั้นเรามาพัฒนาคำสั่งนี้จากฝั่งบอทโทรเลขกันดีกว่า
  1. มาเพิ่มชื่อของคำสั่งใหม่ - /deleteGroupSubและในCommandName - บรรทัด:

    DELETE_GROUP_SUB("/deleteGroupSub")

  2. ต่อไปเรามาสร้าง คำสั่ง 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));
       }
    }

ในการดำเนินการนี้ เราต้องเพิ่มอีกสองวิธีในการทำงานกับเอนทิตี GroupSub - การดึงข้อมูลจากฐานข้อมูลด้วย ID และการบันทึกเอนทิตีเอง วิธีการทั้งหมดนี้เรียกวิธีการเก็บข้อมูลสำเร็จรูป ฉันจะบอกคุณแยกต่างหากเกี่ยวกับการลบการสมัครสมาชิก ใน Schema ฐานข้อมูล นี่คือตารางที่รับผิดชอบกระบวนการแบบกลุ่มต่อกลุ่ม และหากต้องการลบความสัมพันธ์นี้ คุณจะต้องลบเรกคอร์ดในนั้น คือถ้าเราใช้ความเข้าใจทั่วไปในส่วนของฐานข้อมูล แต่เราใช้ Spring Data และมี Hibernate เป็นค่าเริ่มต้น ซึ่งสามารถดำเนินการแตกต่างออกไปได้ เราได้รับเอนทิตี GroupSub ซึ่งผู้ใช้ทั้งหมดที่เกี่ยวข้องกับเอนทิตีจะถูกดึงออกมา จากกลุ่มผู้ใช้นี้ เราจะลบกลุ่มที่เราต้องการและบันทึก groupSub กลับเข้าไปในฐานข้อมูล แต่ไม่มีผู้ใช้รายนี้ วิธีนี้ Spring Data จะเข้าใจสิ่งที่เราต้องการและลบบันทึก "โครงการ Java จาก A ถึง Z": การลบการสมัครสมาชิกบทความออกจากกลุ่ม - 1"โครงการ Java จาก A ถึง Z": การลบการสมัครสมาชิกบทความจากกลุ่ม - 2หากต้องการลบผู้ใช้ออกอย่างรวดเร็ว ฉันได้เพิ่มคำอธิบายประกอบ EqualsAndHashCode ให้กับ TelegramUser โดยไม่รวมรายการ GroupSub เพื่อไม่ให้เกิดปัญหา และเรียกวิธีการลบในการรวบรวมผู้ใช้กับผู้ใช้ที่เราต้องการ นี่คือลักษณะของ 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;
}
เป็นผลให้ทุกอย่างดำเนินไปอย่างที่เราต้องการ มีเหตุการณ์ที่เป็นไปได้หลายอย่างในทีม ดังนั้น การเขียนแบบทดสอบที่ดีสำหรับแต่ละเหตุการณ์จึงเป็นความคิดที่ดี เมื่อพูดถึงการทดสอบ: ขณะที่ฉันกำลังเขียน ฉันพบข้อบกพร่องในตรรกะและแก้ไขก่อนที่จะเผยแพร่สู่การใช้งานจริง หากไม่มีการทดสอบ ก็ไม่มีความชัดเจนว่าจะตรวจพบได้เร็วแค่ไหน ลบ GroupSubCommandTest:
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);
   }
}
ที่นี่ การทดสอบแต่ละครั้งจะตรวจสอบสถานการณ์แยกต่างหาก และฉันขอเตือนคุณว่ามีเพียงห้าสถานการณ์เท่านั้น:
  • เมื่อคุณส่ง คำสั่ง /deleteGroupSubและไม่มีการสมัครสมาชิกกลุ่ม
  • เมื่อคุณเพียงแค่ส่ง คำสั่ง /deleteGroupSubและมีการสมัครสมาชิกกลุ่ม
  • เมื่อมีการส่ง ID กลุ่มที่ไม่ถูกต้อง เช่น/deleteGroupSub abc ;
  • สถานการณ์ที่ทุกอย่างถูกลบอย่างถูกต้องตามที่คาดไว้
  • สถานการณ์เมื่อ ID กลุ่มถูกต้อง แต่กลุ่มดังกล่าวไม่อยู่ในฐานข้อมูล
อย่างที่คุณเห็น สถานการณ์ทั้งหมดนี้จำเป็นต้องได้รับการทดสอบ ขณะที่ฉันกำลังเขียนอยู่ ฉันตระหนักว่าเพื่อที่จะเขียนแบบทดสอบได้ดีขึ้น คุ้มค่าที่จะเรียนหลักสูตรการทดสอบบ้าง ฉันคิดว่านี่จะช่วยค้นหาตัวเลือกต่างๆ ได้อย่างถูกต้อง ถูกต้องความคิดสำหรับอนาคต ถัดไป คุณต้องเพิ่มคำอธิบายให้กับ คำสั่ง /helpซึ่งขณะนี้คุณสามารถลบการสมัครสมาชิกได้ มาวางไว้ในส่วนสำหรับการทำงานกับการสมัครสมาชิก "โครงการ Java จาก A ถึง Z": การลบการสมัครสมาชิกบทความออกจากกลุ่ม - 3แน่นอนว่าเพื่อให้คำสั่งนี้ใช้งานได้ คุณต้องเพิ่มการกำหนดค่าเริ่มต้นให้กับCommandContainer :
.put(DELETE_GROUP_SUB.getCommandName(),
       new DeleteGroupSubCommand(sendBotMessageService, groupSubService, telegramUserService))
ตอนนี้คุณสามารถทดสอบฟังก์ชันการทำงานบนบอททดสอบได้แล้ว เราเปิดตัวฐานข้อมูลของเราโดยใช้ docker-compose-test.yml: docker-compose -f docker-compose-test.yml up และเปิด SpringBoot ผ่าน IDEA ฉันจะล้างการติดต่อกับบอทให้หมดและเริ่มต้นใหม่อีกครั้ง ฉันจะพิจารณาตัวเลือกทั้งหมดที่อาจเกิดขึ้นเมื่อทำงานร่วมกับทีมนี้ "โครงการ Java จาก A ถึง Z": การลบการสมัครสมาชิกบทความจากกลุ่ม - 4อย่างที่คุณเห็นจากภาพหน้าจอ ตัวเลือกทั้งหมดผ่านไปและประสบความสำเร็จ
เพื่อน! คุณต้องการทราบทันทีเมื่อมีการออกโค้ดใหม่สำหรับโปรเจ็กต์หรือไม่? เมื่อไหร่จะมีบทความใหม่ออกมา? เข้าร่วมช่องโทรเลข ของ ฉัน ที่นั่นฉันรวบรวมบทความ ความคิด และการพัฒนาโอเพ่นซอร์สของฉันไว้ด้วยกัน
เราอัปเดตเวอร์ชันของโครงการของเราเป็น 0.6.0-SNAPSHOT เราอัปเดต RELEASE_NOTES.md โดยเพิ่มคำอธิบายของสิ่งที่ได้ทำไปแล้วในเวอร์ชันใหม่:
## 0.6.0-SNAPSHOT * JRTB-7: เพิ่มความสามารถในการลบการสมัครสมาชิกกลุ่ม
รหัสใช้งานได้ มีการเขียนการทดสอบแล้ว: ถึงเวลาที่จะผลักดันงานเข้าไปในพื้นที่เก็บข้อมูลและสร้างคำขอดึง"โครงการ Java จาก A ถึง Z": การลบการสมัครสมาชิกบทความจากกลุ่ม - 5

แทนที่จะจบ.

เราไม่ได้ดูบอร์ดโครงการของเรามานานแล้ว แต่มีการเปลี่ยนแปลงครั้งใหญ่: "โครงการ Java จาก A ถึง Z": การลบการสมัครสมาชิกบทความจากกลุ่ม - 6เหลือเพียง 5 งานเท่านั้น นั่นคือคุณและฉันอยู่สุดถนนแล้ว เหลือนิดหน่อย. เป็นเรื่องที่น่าสนใจอย่างยิ่งที่ทราบว่าบทความชุดนี้เผยแพร่ตั้งแต่กลางเดือนกันยายนนั่นคือเป็นเวลา 7 เดือน!!! เมื่อฉันคิดไอเดียนี้ขึ้นมา ฉันไม่ได้คาดหวังว่ามันจะใช้เวลานานนัก ในขณะเดียวกัน ฉันก็พอใจกับผลลัพธ์ที่ได้มาก! เพื่อน ๆ หากยังไม่ชัดเจนว่าเกิดอะไรขึ้นในบทความให้ถามคำถามในความคิดเห็น ด้วยวิธีนี้ฉันจะรู้ว่าบางสิ่งจำเป็นต้องอธิบายให้ดีขึ้น และบางสิ่งจำเป็นต้องอธิบายเพิ่มเติม ตามปกติเช่น - สมัครสมาชิก - กดกริ่งให้ ดาว โครงการของเราเขียนความคิดเห็นและให้คะแนนบทความ! ขอบคุณทุกคน. จนกระทั่งถึงภาคต่อไป เราจะพูดถึงวิธีเพิ่มการปิดใช้งานและการเปิดใช้งานบอทผ่าน คำสั่ง /stop & /startและวิธีใช้งานให้ดีที่สุด เร็วๆ นี้ แล้วพบกันใหม่!

รายการเนื้อหาทั้งหมดในซีรีส์นี้อยู่ที่ตอนต้นของบทความนี้

ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION