From 77d57e481253d441102ef4711e1105a95d415485 Mon Sep 17 00:00:00 2001 From: Color-Marker Date: Fri, 30 Jan 2026 14:46:43 +0900 Subject: [PATCH 01/20] =?UTF-8?q?feat:=20Bean=EB=93=B1=EB=A1=9D=EA=B3=BC?= =?UTF-8?q?=20=ED=8C=8C=EC=9D=BC=20=EC=A0=80=EC=9E=A5=20=EA=B2=BD=EB=A1=9C?= =?UTF-8?q?=EA=B0=80=20application.yaml=EC=97=90=20=EB=94=B0=EB=9D=BC=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=EB=90=98=EA=B2=8C=20=EC=96=B4=EB=85=B8?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=A7=80=EC=A0=95.=20=EA=B7=B8?= =?UTF-8?q?=EB=A6=AC=EA=B3=A0=20.gitignore=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../6588f710-e9a0-4f74-8295-f032e2e698a6.ser | Bin 0 -> 419 bytes .../9dcfad15-f9c8-4708-ad27-9842f5761504.ser | Bin 0 -> 419 bytes .../795e6167-7678-451a-bca6-2230277b8acb.ser | Bin 505 -> 505 bytes .../d30927f0-c828-4548-a03c-83c06832a0ad.ser | Bin 473 -> 507 bytes .../2921b105-e277-4857-9da3-81e15960dac2.ser | Bin 420 -> 420 bytes .../b695e605-2990-4a4a-89ed-08a404d978e4.ser | Bin 420 -> 420 bytes .../c277c2f8-7ad4-40e2-93fa-0a26adecc0aa.ser | Bin 371 -> 371 bytes .../d27e6d26-233e-49c0-ae6a-6b0198afc89f.ser | Bin 377 -> 377 bytes .../d28b36ee-f8f9-4a0a-a805-ef6d3318703f.ser | Bin 0 -> 377 bytes .../de026036-78cb-420d-bf7b-d11dcbf9b7e2.ser | Bin 334 -> 334 bytes .../f70e582c-b43f-40d3-a8e3-65e63d33f267.ser | Bin 0 -> 334 bytes discodeit/.gitignore | 3 ++ .../buildOutputCleanup.lock | Bin 17 -> 17 bytes discodeit/.gradle/file-system.probe | Bin 8 -> 8 bytes .../compileJava/previous-compilation-data.bin | Bin 28810 -> 29430 bytes .../discodeit/DiscodeitApplication.java | 51 ++++++++++++++---- .../mission/discodeit/JavaApplication.java | 43 ++++++++++----- .../discodeit/dto/PrivateChannelDto.java | 2 + .../file/FileBinaryContentRepository.java | 12 ++++- .../file/FileChannelRepository.java | 10 +++- .../file/FileMessageRepository.java | 10 +++- .../file/FileReadStatusRepository.java | 10 +++- .../repository/file/FileUserRepository.java | 10 +++- .../file/FileUserStatusRepository.java | 10 +++- .../jcf/JCFBinaryContentRepository.java | 5 ++ .../repository/jcf/JCFChannelRepository.java | 7 +++ .../repository/jcf/JCFMessageRepository.java | 7 +++ .../jcf/JCFReadStatusRepository.java | 5 ++ .../repository/jcf/JCFUserRepository.java | 7 +++ .../jcf/JCFUserStatusRepository.java | 5 ++ .../service/basic/BasicChannelService.java | 2 +- discodeit/src/main/resources/application.yaml | 12 ++--- .../1e6fa1ed-bc88-45a9-b704-7c105bcffece.ser | Bin 418 -> 0 bytes 33 files changed, 167 insertions(+), 44 deletions(-) create mode 100644 .discodeit/file-data-map/BinaryContent/6588f710-e9a0-4f74-8295-f032e2e698a6.ser create mode 100644 .discodeit/file-data-map/BinaryContent/9dcfad15-f9c8-4708-ad27-9842f5761504.ser rename file-data-map/Channel/bb4d73bf-b380-4224-9d8a-74ee7696701f.ser => .discodeit/file-data-map/Channel/795e6167-7678-451a-bca6-2230277b8acb.ser (61%) rename file-data-map/Channel/dbc4a16b-e633-4598-a8c2-f94f8a619d1a.ser => .discodeit/file-data-map/Channel/d30927f0-c828-4548-a03c-83c06832a0ad.ser (50%) rename file-data-map/Message/e8ca20e3-41e7-41e6-a6c3-4d80039d226d.ser => .discodeit/file-data-map/Message/2921b105-e277-4857-9da3-81e15960dac2.ser (60%) rename file-data-map/Message/dc6c984f-417e-4c66-8a0e-6634e402f5b2.ser => .discodeit/file-data-map/Message/b695e605-2990-4a4a-89ed-08a404d978e4.ser (60%) rename file-data-map/ReadStatus/4b185178-2527-4329-a613-96466064a7c4.ser => .discodeit/file-data-map/ReadStatus/c277c2f8-7ad4-40e2-93fa-0a26adecc0aa.ser (51%) rename file-data-map/User/4b33ccf0-c98e-4d93-a727-eda586e61b87.ser => .discodeit/file-data-map/User/d27e6d26-233e-49c0-ae6a-6b0198afc89f.ser (50%) create mode 100644 .discodeit/file-data-map/User/d28b36ee-f8f9-4a0a-a805-ef6d3318703f.ser rename file-data-map/UserStatus/69b3a48f-ae63-4570-bf05-f3c025639f6a.ser => .discodeit/file-data-map/UserStatus/de026036-78cb-420d-bf7b-d11dcbf9b7e2.ser (57%) create mode 100644 .discodeit/file-data-map/UserStatus/f70e582c-b43f-40d3-a8e3-65e63d33f267.ser delete mode 100644 file-data-map/BinaryContent/1e6fa1ed-bc88-45a9-b704-7c105bcffece.ser diff --git a/.discodeit/file-data-map/BinaryContent/6588f710-e9a0-4f74-8295-f032e2e698a6.ser b/.discodeit/file-data-map/BinaryContent/6588f710-e9a0-4f74-8295-f032e2e698a6.ser new file mode 100644 index 0000000000000000000000000000000000000000..4022e02b96d342cabf618df3a3e73f9fd99e7f55 GIT binary patch literal 419 zcmX|-u}i~16o+3^14>&dI*N-fP8aOj$=1P^P>0&l;Y`kQl3Zf$wWcBtQbp*XgN3>| zIdyUrCujW|6cNEeaO>(_QoQZn`@Q#lcjF&0Wd!R^;1d}}Tp;nelspi`<dNKx>HsqYNxYG+6!h(H3yVUZiXj@HGJKJR&=A4L8WbOtcET#eE zKC@~&#(O$!PMN6nwn3hG>T}{(`|M#^3N>; zj5_2!MkO|RyTVb{A?FAGw>Uf-{+2$zM)&M;QvAN#)&}V=Ek_c$!RPhM)#{r8nL3zV g>UCHgK`{X)p+?8wW6{NUxSYFrA1K8*oc{3IAL;6hrT_o{ literal 0 HcmV?d00001 diff --git a/.discodeit/file-data-map/BinaryContent/9dcfad15-f9c8-4708-ad27-9842f5761504.ser b/.discodeit/file-data-map/BinaryContent/9dcfad15-f9c8-4708-ad27-9842f5761504.ser new file mode 100644 index 0000000000000000000000000000000000000000..e1a0a2d84263ac93ba6a060d7a2e7c9721fab2ee GIT binary patch literal 419 zcmX|-F;Buk6vrQhCK$jF36pWrL8oh&nFJGsG{giM9nSV7XWDD+J+Khtq%I6PK!Stc z02W8%2XHX)Bk17jXpEcJmhiUs-v9l7|GU{Y=rVwc<+()q0T)PIE+zK_vAMK7n{gye zAjbizaY2JY-4mz=kW@+o-GrQ#437prLs&Di&{PRgax;=$wqGcIZ_crfAPaR|8ks(Y5)KL literal 0 HcmV?d00001 diff --git a/file-data-map/Channel/bb4d73bf-b380-4224-9d8a-74ee7696701f.ser b/.discodeit/file-data-map/Channel/795e6167-7678-451a-bca6-2230277b8acb.ser similarity index 61% rename from file-data-map/Channel/bb4d73bf-b380-4224-9d8a-74ee7696701f.ser rename to .discodeit/file-data-map/Channel/795e6167-7678-451a-bca6-2230277b8acb.ser index 05c2359b6c77b67904b4fd2684b1e5b4bbe4eecd..09fdaa99959fcfec1730620e86e36edd662940d0 100644 GIT binary patch delta 74 zcmey#{F8ZuG^1pVv$Y7nkU~WXgLp_rX0ZYg6_h6BWF}8GVRV(vS|hKyM2rI ZZ*Fi>DPdrP$kr7x=uhO(o_vte0stRM8@B)e diff --git a/file-data-map/Channel/dbc4a16b-e633-4598-a8c2-f94f8a619d1a.ser b/.discodeit/file-data-map/Channel/d30927f0-c828-4548-a03c-83c06832a0ad.ser similarity index 50% rename from file-data-map/Channel/dbc4a16b-e633-4598-a8c2-f94f8a619d1a.ser rename to .discodeit/file-data-map/Channel/d30927f0-c828-4548-a03c-83c06832a0ad.ser index a14917ee0a37bb5ff71f369fd1525ef4eec9fd10..f225730ca51def87110b96d901c6d622bcb63820 100644 GIT binary patch delta 77 zcmcb~{F`}$G^0|Dv$cqUi%vxegLp_rX0ZYg6%=KbC6=TrBxfY%<)!9KR$;V}T42+B bAj4?E+RL2kA5LhvdXzA*LzPV~WXu2n1X&x2 delta 50 zcmV-20L}mV1K9(x8vzM>E7$>NFOYa}lRW`E6{y1bPl{ok8r#I7YvwaWm~eh_056dU IE0e+jIRaA@HUIzs diff --git a/file-data-map/Message/e8ca20e3-41e7-41e6-a6c3-4d80039d226d.ser b/.discodeit/file-data-map/Message/2921b105-e277-4857-9da3-81e15960dac2.ser similarity index 60% rename from file-data-map/Message/e8ca20e3-41e7-41e6-a6c3-4d80039d226d.ser rename to .discodeit/file-data-map/Message/2921b105-e277-4857-9da3-81e15960dac2.ser index ac1cb48075986d5e23fdb4815db1213599732ae2..e792197aabbd6217d6cd5972cecbd4b148eca4b8 100644 GIT binary patch delta 82 zcmZ3&yo7m!Hlx}K*7v!_5(V~`y3OAG`02$}T*y$zz_!4q`9Oxzg0+`9)jyojaP^oR k$Y>&8<7_Q5`4~e5M9JL6jSnLeZXME8+{pT<+#|dI07D@l)&Kwi delta 82 zcmZ3&yo7m!Hlx~d^|wpgo=LZR8=v`bvd?#NaUnw;1KZrLl6Pg(3gmbD7VqEO;G{A+ kkkLfGTKfj$^=C^eAWD`U_HAIEtCaiVl)_`j=Z?<`0LX15U;qFB diff --git a/file-data-map/Message/dc6c984f-417e-4c66-8a0e-6634e402f5b2.ser b/.discodeit/file-data-map/Message/b695e605-2990-4a4a-89ed-08a404d978e4.ser similarity index 60% rename from file-data-map/Message/dc6c984f-417e-4c66-8a0e-6634e402f5b2.ser rename to .discodeit/file-data-map/Message/b695e605-2990-4a4a-89ed-08a404d978e4.ser index f6b4fc32bb841727bbd4a57f2c03216a656edd44..1ad6656d9fae0d6e3ba3ba8b804a9fc98a1afa2a 100644 GIT binary patch delta 82 zcmZ3&yo7m!Hlx}K*7v!_5(V~`y3OAG`02$}T*y$zz_w?Zl7V`4*Xhc*#PqTXSE@nqZ7XRMkNyu1nkAW4@0AWFLU(oCK(ecf~?XNJFHoljZ;0K_OFzW@LL diff --git a/file-data-map/ReadStatus/4b185178-2527-4329-a613-96466064a7c4.ser b/.discodeit/file-data-map/ReadStatus/c277c2f8-7ad4-40e2-93fa-0a26adecc0aa.ser similarity index 51% rename from file-data-map/ReadStatus/4b185178-2527-4329-a613-96466064a7c4.ser rename to .discodeit/file-data-map/ReadStatus/c277c2f8-7ad4-40e2-93fa-0a26adecc0aa.ser index 7dd75b2b94490da4bdd3c9f185004c57fbcffdc8..2fe7c9920d2e886a219fc29a4b9c99ab672e5fac 100644 GIT binary patch delta 102 zcmey&^qFbGXMqJa%?C1!7OcI@ss7=FhO5V9W=1uy8fR+}cdvko;zEWx2A0XcxYX9Z sIk4(b`Jo?GR~#OJMcK-EnHYc|6IJaB*7v!_5(V~`y3OAG`02$307i}{*Z=?k delta 102 zcmey&^qFbGXMq)me)@MM&Xu}-WMTF*W7ipznHkl*sT=bT?K}^ZFmRUV=ciN}8X21u K6qGQqf_VV5^cG0~ delta 57 zcmey#^pk1AANFeP>sGJl_)KPI3{YFH{&s2GGwF73<1-&l_W4dOVc;y!&rhi|G%_|R KC@5iI1@i#y02l-S diff --git a/.discodeit/file-data-map/User/d28b36ee-f8f9-4a0a-a805-ef6d3318703f.ser b/.discodeit/file-data-map/User/d28b36ee-f8f9-4a0a-a805-ef6d3318703f.ser new file mode 100644 index 0000000000000000000000000000000000000000..a4413a63c91a418b93cc2d1f756be9a022c22a67 GIT binary patch literal 377 zcmX|7Jx{|h5WS{~pwJ>j7bG?&CPz@j(t*!`RvD10j0{$7szb43>?=(r!~_!q6B7Ib z1|~+tui$?GBe3Bl2ygeE?|t0UJA}@{+Aveh8H<{jLgz5kOrRTPLLxD#32Dj_CoNR3 z7QjpljiHsCq}U~bc6`duc|@pW)YFc*rfv+OR2)-+rHW0t9z_H4HX~04C$gX_55a{(z4KSs-@*$llmbOAR^)npsDIt7>nMPr w56x6^M*|$~A-O)xtL%Tu&13kfI?HDJ?Plxa`=>ug&?x2CH#(cf5QOFQ3nh|z2mk;8 literal 0 HcmV?d00001 diff --git a/file-data-map/UserStatus/69b3a48f-ae63-4570-bf05-f3c025639f6a.ser b/.discodeit/file-data-map/UserStatus/de026036-78cb-420d-bf7b-d11dcbf9b7e2.ser similarity index 57% rename from file-data-map/UserStatus/69b3a48f-ae63-4570-bf05-f3c025639f6a.ser rename to .discodeit/file-data-map/UserStatus/de026036-78cb-420d-bf7b-d11dcbf9b7e2.ser index 547539d83802ffed6bbe05202886ad63e045b13a..4b85a541647adc9f7e1093f42ab40f6c7adef818 100644 GIT binary patch delta 77 zcmX@dbdG7lIrbW7YvIk4?@YXEXI*_!_Vmx~kM1!gm{pv1;w>&@sAFI$=Vf95f=sBo Z3a}{Kx~y!*8S78Xzf_m2rflbV007`g9>o9v delta 77 zcmX@dbdG7lIreJp>sGJl_)NTNXU+QgfNJvmtjx_z`qw4978DmU)G@G>^D;32K_*mP Z1z41Ax%%6sZO^3Jy^YU&IN9er8369RAZq{s diff --git a/.discodeit/file-data-map/UserStatus/f70e582c-b43f-40d3-a8e3-65e63d33f267.ser b/.discodeit/file-data-map/UserStatus/f70e582c-b43f-40d3-a8e3-65e63d33f267.ser new file mode 100644 index 0000000000000000000000000000000000000000..cd635c41702bc94b9cdb8eb92b761de4c5a12575 GIT binary patch literal 334 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 userIdList) { + PrivateChannelDto privateChannelDto = new PrivateChannelDto(ChannelType.PRIVATE, "private", "This is private channel", userIdList); + return channelService.createPrivateChannel(privateChannelDto); + } + static void messageCreateTest(MessageService messageService, Channel channel, User author) { MessageCreateDto messageCreateDto = new MessageCreateDto("Hello World", channel.getId(), author.getId(), null); Message message = messageService.create(messageCreateDto); - System.out.println("Message created: " + message.getId()); + System.out.println("Message created: " + message.getContent()); + System.out.println("In channel: " + message.getChannelId()); + System.out.println("By user: " + message.getAuthorId()); + System.out.println(); } public static void main(String[] args) { @@ -48,9 +67,21 @@ public static void main(String[] args) { // 셋업 User user = setupUser(userService); - Channel channel = setupChannel(channelService); + System.out.println(); + + List userList = new ArrayList<>(); + userList.add(user); + List userIdList = userList.stream() + .map(User::getId).toList(); + Channel publicChannel = setupPublicChannel(channelService); + System.out.println("Public Channel: " + publicChannel.getName() + "_" + publicChannel.getId()); + Channel privateChannel = setupPrivateChannel(channelService, userIdList); + System.out.println("Private Channel: " + privateChannel.getName() + "_" + privateChannel.getId()); + System.out.println(); + // 테스트 - messageCreateTest(messageService, channel, user); + messageCreateTest(messageService, publicChannel, user); + messageCreateTest(messageService, privateChannel, user); } } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/JavaApplication.java b/discodeit/src/main/java/com/sprint/mission/discodeit/JavaApplication.java index 45ead0ae..2d6a7b9e 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/JavaApplication.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/JavaApplication.java @@ -4,6 +4,7 @@ import com.sprint.mission.discodeit.entity.*; import com.sprint.mission.discodeit.repository.*; import com.sprint.mission.discodeit.repository.file.*; +import com.sprint.mission.discodeit.repository.jcf.*; import com.sprint.mission.discodeit.service.ChannelService; import com.sprint.mission.discodeit.service.MessageService; import com.sprint.mission.discodeit.service.UserService; @@ -17,50 +18,66 @@ public class JavaApplication { static User setupUser(UserService userService) { - BinaryContentCreateDto file = new BinaryContentCreateDto("file","txt",40L); - UseCreaterDto useCreaterDto = new UseCreaterDto("woody", "woody@codeit.com", "woody1234", file); - return userService.create(useCreaterDto); + BinaryContentCreateDto file1 = new BinaryContentCreateDto("file1","txt",40L); + BinaryContentCreateDto file2 = new BinaryContentCreateDto("file2","txt",40L); + UseCreaterDto useCreaterDto1 = new UseCreaterDto("admin", "admin@codeit.com", "admin1234", file1); + UseCreaterDto useCreaterDto2 = new UseCreaterDto("woody", "woody@codeit.com", "woody1234", file2); + User user1 = userService.create(useCreaterDto1); + User user2 = userService.create(useCreaterDto2); + + System.out.println("user1: " + user1.getUsername() + "_" + user1.getId()); + System.out.println("user2: " + user2.getUsername() + "_" + user2.getId()); + + return user1; } static Channel setupPublicChannel(ChannelService channelService) { - PublicChannelDto publicChannelDto = new PublicChannelDto(ChannelType.PUBLIC, "notice", "This is notice channel."); + PublicChannelDto publicChannelDto = new PublicChannelDto(ChannelType.PUBLIC, "public", "This is public channel."); return channelService.createPublicChannel(publicChannelDto); } static Channel setupPrivateChannel(ChannelService channelService, List userIdList) { - PrivateChannelDto privateChannelDto = new PrivateChannelDto(ChannelType.PRIVATE, userIdList); + PrivateChannelDto privateChannelDto = new PrivateChannelDto(ChannelType.PRIVATE, "private", "This is private channel", userIdList); return channelService.createPrivateChannel(privateChannelDto); } static void messageCreateTest(MessageService messageService, Channel channel, User author) { MessageCreateDto messageCreateDto = new MessageCreateDto("Hello World", channel.getId(), author.getId(), null); Message message = messageService.create(messageCreateDto); - System.out.println("Message created: " + message.getId()); + System.out.println("Message created: " + message.getContent()); + System.out.println("In channel: " + message.getChannelId()); + System.out.println("By user: " + message.getAuthorId()); + System.out.println(); } public static void main(String[] args) { // 레포지토리 초기화 - UserRepository userRepository = new FileUserRepository(); - ChannelRepository channelRepository = new FileChannelRepository(); - MessageRepository messageRepository = new FileMessageRepository(); - BinaryContentRepository binaryContentRepository = new FileBinaryContentRepository(); - ReadStatusRepository readStatusRepository = new FileReadStatusRepository(); - UserStatusRepository userStatusRepository = new FileUserStatusRepository(); + UserRepository userRepository = new JCFUserRepository(); + ChannelRepository channelRepository = new JCFChannelRepository(); + MessageRepository messageRepository = new JCFMessageRepository(); + BinaryContentRepository binaryContentRepository = new JCFBinaryContentRepository(); + ReadStatusRepository readStatusRepository = new JCFReadStatusRepository(); + UserStatusRepository userStatusRepository = new JCFUserStatusRepository(); - // 서비스 초기화 + // 서비스 초기화a UserService userService = new BasicUserService(userRepository, userStatusRepository, binaryContentRepository); ChannelService channelService = new BasicChannelService(channelRepository,readStatusRepository, messageRepository); MessageService messageService = new BasicMessageService(messageRepository, channelRepository, userRepository, binaryContentRepository); // 셋업 User user = setupUser(userService); + System.out.println(); List userList = new ArrayList<>(); userList.add(user); List userIdList = userList.stream() .map(User::getId).toList(); Channel publicChannel = setupPublicChannel(channelService); + System.out.println("Public Channel: " + publicChannel.getName() + "_" + publicChannel.getId()); Channel privateChannel = setupPrivateChannel(channelService, userIdList); + System.out.println("Private Channel: " + privateChannel.getName() + "_" + privateChannel.getId()); + System.out.println(); + // 테스트 messageCreateTest(messageService, publicChannel, user); messageCreateTest(messageService, privateChannel, user); diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/PrivateChannelDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/PrivateChannelDto.java index cbb794c1..876331a8 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/PrivateChannelDto.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/PrivateChannelDto.java @@ -7,5 +7,7 @@ public record PrivateChannelDto ( ChannelType type, + String name, + String description, List userIdList ){} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileBinaryContentRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileBinaryContentRepository.java index 8e982ca0..33d088aa 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileBinaryContentRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileBinaryContentRepository.java @@ -5,6 +5,8 @@ import com.sprint.mission.discodeit.entity.User; import com.sprint.mission.discodeit.repository.BinaryContentRepository; import jakarta.websocket.Decoder; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Repository; import java.io.*; @@ -16,12 +18,18 @@ import java.util.UUID; @Repository +@ConditionalOnProperty( + name = "discodeit.repository.type", + havingValue = "file" +) public class FileBinaryContentRepository implements BinaryContentRepository { private final Path DIRECTORY; private final String EXTENSION = ".ser"; - public FileBinaryContentRepository() { - this.DIRECTORY = Paths.get(System.getProperty("user.dir"), "file-data-map", BinaryContent.class.getSimpleName()); + public FileBinaryContentRepository( + @Value("${discodeit.repository.file-directory}") String envPath + ) { + this.DIRECTORY = Paths.get(envPath,"file-data-map", BinaryContent.class.getSimpleName()); if (Files.notExists(DIRECTORY)) { try { Files.createDirectories(DIRECTORY); diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileChannelRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileChannelRepository.java index 48b7de34..7b5455bb 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileChannelRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileChannelRepository.java @@ -2,6 +2,8 @@ import com.sprint.mission.discodeit.entity.Channel; import com.sprint.mission.discodeit.repository.ChannelRepository; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Repository; import java.io.*; @@ -13,12 +15,16 @@ import java.util.UUID; @Repository +@ConditionalOnProperty( + name = "discodeit.repository.type", + havingValue = "file" +) public class FileChannelRepository implements ChannelRepository { private final Path DIRECTORY; private final String EXTENSION = ".ser"; - public FileChannelRepository() { - this.DIRECTORY = Paths.get(System.getProperty("user.dir"), "file-data-map", Channel.class.getSimpleName()); + public FileChannelRepository( @Value("${discodeit.repository.file-directory}") String envPath) { + this.DIRECTORY = Paths.get(envPath, "file-data-map", Channel.class.getSimpleName()); if (Files.notExists(DIRECTORY)) { try { Files.createDirectories(DIRECTORY); diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileMessageRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileMessageRepository.java index bf71910f..688416eb 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileMessageRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileMessageRepository.java @@ -2,6 +2,8 @@ import com.sprint.mission.discodeit.entity.Message; import com.sprint.mission.discodeit.repository.MessageRepository; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Repository; import java.io.*; @@ -13,12 +15,16 @@ import java.util.UUID; @Repository +@ConditionalOnProperty( + name = "discodeit.repository.type", + havingValue = "file" +) public class FileMessageRepository implements MessageRepository { private final Path DIRECTORY; private final String EXTENSION = ".ser"; - public FileMessageRepository() { - this.DIRECTORY = Paths.get(System.getProperty("user.dir"), "file-data-map", Message.class.getSimpleName()); + public FileMessageRepository( @Value("${discodeit.repository.file-directory}") String envPath) { + this.DIRECTORY = Paths.get(envPath, "file-data-map", Message.class.getSimpleName()); if (Files.notExists(DIRECTORY)) { try { Files.createDirectories(DIRECTORY); diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileReadStatusRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileReadStatusRepository.java index 2b2e27c6..18105a64 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileReadStatusRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileReadStatusRepository.java @@ -3,6 +3,8 @@ import com.sprint.mission.discodeit.entity.ReadStatus; import com.sprint.mission.discodeit.entity.User; import com.sprint.mission.discodeit.repository.ReadStatusRepository; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Repository; import java.io.*; @@ -14,12 +16,16 @@ import java.util.UUID; @Repository +@ConditionalOnProperty( + name = "discodeit.repository.type", + havingValue = "file" +) public class FileReadStatusRepository implements ReadStatusRepository { private final Path DIRECTORY; private final String EXTENSION = ".ser"; - public FileReadStatusRepository() { - this.DIRECTORY = Paths.get(System.getProperty("user.dir"), "file-data-map", ReadStatus.class.getSimpleName()); + public FileReadStatusRepository( @Value("${discodeit.repository.file-directory}") String envPath) { + this.DIRECTORY = Paths.get(envPath, "file-data-map", ReadStatus.class.getSimpleName()); if (Files.notExists(DIRECTORY)) { try { Files.createDirectories(DIRECTORY); diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserRepository.java index 51eb115c..b131c4dc 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserRepository.java @@ -2,6 +2,8 @@ import com.sprint.mission.discodeit.entity.User; import com.sprint.mission.discodeit.repository.UserRepository; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Repository; import java.io.*; @@ -13,12 +15,16 @@ import java.util.UUID; @Repository +@ConditionalOnProperty( + name = "discodeit.repository.type", + havingValue = "file" +) public class FileUserRepository implements UserRepository { private final Path DIRECTORY; private final String EXTENSION = ".ser"; - public FileUserRepository() { - this.DIRECTORY = Paths.get(System.getProperty("user.dir"), "file-data-map", User.class.getSimpleName()); + public FileUserRepository( @Value("${discodeit.repository.file-directory}") String envPath) { + this.DIRECTORY = Paths.get(envPath, "file-data-map", User.class.getSimpleName()); if (Files.notExists(DIRECTORY)) { try { Files.createDirectories(DIRECTORY); diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserStatusRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserStatusRepository.java index a7db7ce3..485b73cd 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserStatusRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserStatusRepository.java @@ -3,6 +3,8 @@ import com.sprint.mission.discodeit.entity.User; import com.sprint.mission.discodeit.entity.UserStatus; import com.sprint.mission.discodeit.repository.UserStatusRepository; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Repository; import java.io.*; @@ -15,12 +17,16 @@ import java.util.UUID; @Repository +@ConditionalOnProperty( + name = "discodeit.repository.type", + havingValue = "file" +) public class FileUserStatusRepository implements UserStatusRepository { private final Path DIRECTORY; private final String EXTENSION = ".ser"; - public FileUserStatusRepository() { - this.DIRECTORY = Paths.get(System.getProperty("user.dir"), "file-data-map", UserStatus.class.getSimpleName()); + public FileUserStatusRepository( @Value("${discodeit.repository.file-directory}") String envPath) { + this.DIRECTORY = Paths.get(envPath, "file-data-map", UserStatus.class.getSimpleName()); if (Files.notExists(DIRECTORY)) { try { Files.createDirectories(DIRECTORY); diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFBinaryContentRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFBinaryContentRepository.java index db4a87cc..56daf104 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFBinaryContentRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFBinaryContentRepository.java @@ -3,11 +3,16 @@ import com.sprint.mission.discodeit.dto.BinaryContentCreateDto; import com.sprint.mission.discodeit.entity.BinaryContent; import com.sprint.mission.discodeit.repository.BinaryContentRepository; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Repository; import java.util.*; @Repository +@ConditionalOnProperty( + name = "discodeit.repository.type", + havingValue = "jcf" +) public class JCFBinaryContentRepository implements BinaryContentRepository { private final Map data; diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFChannelRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFChannelRepository.java index e11c92da..ea24fcc4 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFChannelRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFChannelRepository.java @@ -2,9 +2,16 @@ import com.sprint.mission.discodeit.entity.Channel; import com.sprint.mission.discodeit.repository.ChannelRepository; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Repository; import java.util.*; +@Repository +@ConditionalOnProperty( + name = "discodeit.repository.type", + havingValue = "jcf" +) public class JCFChannelRepository implements ChannelRepository { private final Map data; diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFMessageRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFMessageRepository.java index 5ad8b355..949821b2 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFMessageRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFMessageRepository.java @@ -2,9 +2,16 @@ import com.sprint.mission.discodeit.entity.Message; import com.sprint.mission.discodeit.repository.MessageRepository; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Repository; import java.util.*; +@Repository +@ConditionalOnProperty( + name = "discodeit.repository.type", + havingValue = "jcf" +) public class JCFMessageRepository implements MessageRepository { private final Map data; diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFReadStatusRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFReadStatusRepository.java index 9e456432..d1d8febb 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFReadStatusRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFReadStatusRepository.java @@ -2,11 +2,16 @@ import com.sprint.mission.discodeit.entity.ReadStatus; import com.sprint.mission.discodeit.repository.ReadStatusRepository; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Repository; import java.util.*; @Repository +@ConditionalOnProperty( + name = "discodeit.repository.type", + havingValue = "jcf" +) public class JCFReadStatusRepository implements ReadStatusRepository { private final Map data; diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserRepository.java index a60363a3..4d3cf8e4 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserRepository.java @@ -2,9 +2,16 @@ import com.sprint.mission.discodeit.entity.User; import com.sprint.mission.discodeit.repository.UserRepository; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Repository; import java.util.*; +@Repository +@ConditionalOnProperty( + name = "discodeit.repository.type", + havingValue = "jcf" +) public class JCFUserRepository implements UserRepository { private final Map data; diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserStatusRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserStatusRepository.java index 79b1c28f..703ef19b 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserStatusRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserStatusRepository.java @@ -3,11 +3,16 @@ import com.sprint.mission.discodeit.entity.ReadStatus; import com.sprint.mission.discodeit.entity.UserStatus; import com.sprint.mission.discodeit.repository.UserStatusRepository; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Repository; import java.util.*; @Repository +@ConditionalOnProperty( + name = "discodeit.repository.type", + havingValue = "jcf" +) public class JCFUserStatusRepository implements UserStatusRepository { private final Map data; diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicChannelService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicChannelService.java index c67ff1da..0a2b343f 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicChannelService.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicChannelService.java @@ -32,7 +32,7 @@ public Channel createPublicChannel(PublicChannelDto publicChannelDto) { @Override public Channel createPrivateChannel(PrivateChannelDto privateChannelDto) { - Channel channel = new Channel(privateChannelDto.type(), null,null); + Channel channel = new Channel(privateChannelDto.type(), privateChannelDto.name(), privateChannelDto.description()); Channel saved = channelRepository.save(channel); privateChannelDto.userIdList() .forEach(u->{ diff --git a/discodeit/src/main/resources/application.yaml b/discodeit/src/main/resources/application.yaml index cc4d919c..4f20840d 100644 --- a/discodeit/src/main/resources/application.yaml +++ b/discodeit/src/main/resources/application.yaml @@ -1,8 +1,4 @@ -spring: - application: - name: discodeit - -# discodeit: -# repository: -# type: jcf # jcf | file -# file-directory: .discodeit +discodeit: + repository: + type: file # jcf | file + file-directory: .discodeit diff --git a/file-data-map/BinaryContent/1e6fa1ed-bc88-45a9-b704-7c105bcffece.ser b/file-data-map/BinaryContent/1e6fa1ed-bc88-45a9-b704-7c105bcffece.ser deleted file mode 100644 index a4a3d8f038fec4431f5a58a37bcd872b01b50819..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 418 zcmX|-u}i~16vkiE7L>M9bQB#FL=Y}GJ6SE*61vn*;&3KsIY}-t_gb$iPAWnN9W2z* zQRw6%NdE-U)lCQ2&Tir*DSg{}?|a|xyBmxkD??bfeUHc>8DMdoZG!dxOi z?lH3>BvOG|F?GmYR-+y}h6_kpU~mVam`Gd5U9-`sY-?Rc@>5M2Z(@q&YlaFC(x^~B zrw|1okw2-ku(yBtI={5@oCi?BNx_Jc^Ru;2P%C|fkcqhZXeK_2NF*W+AJV^l@5(g5 zs6yUlRAQYsOB`hta-KiwRPd5HFYbN(y)XKYqv1{YY2p=SBXPFw&)wVY@|OXrDrDQ# fZ7~%>F$Tt>TFX0N;q{=mkh}lx>Vzs7fAH!*)@_Rt From 3fec2b62c650737dd631da623f67bbf29cb14a10 Mon Sep 17 00:00:00 2001 From: Color-Marker Date: Fri, 30 Jan 2026 14:49:27 +0900 Subject: [PATCH 02/20] =?UTF-8?q?build:=20.gitignore=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=20=EC=9E=98=20=EB=AA=BB=ED=95=B4=EC=84=9C=20=EB=8B=A4=EC=8B=9C?= =?UTF-8?q?=20=EC=9E=91=EC=97=85=20=EC=A7=84=ED=96=89=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../6588f710-e9a0-4f74-8295-f032e2e698a6.ser | Bin 419 -> 0 bytes .../9dcfad15-f9c8-4708-ad27-9842f5761504.ser | Bin 419 -> 0 bytes .../795e6167-7678-451a-bca6-2230277b8acb.ser | Bin 505 -> 0 bytes .../d30927f0-c828-4548-a03c-83c06832a0ad.ser | Bin 507 -> 0 bytes .../2921b105-e277-4857-9da3-81e15960dac2.ser | Bin 420 -> 0 bytes .../b695e605-2990-4a4a-89ed-08a404d978e4.ser | Bin 420 -> 0 bytes .../c277c2f8-7ad4-40e2-93fa-0a26adecc0aa.ser | Bin 371 -> 0 bytes .../User/d27e6d26-233e-49c0-ae6a-6b0198afc89f.ser | Bin 377 -> 0 bytes .../User/d28b36ee-f8f9-4a0a-a805-ef6d3318703f.ser | Bin 377 -> 0 bytes .../de026036-78cb-420d-bf7b-d11dcbf9b7e2.ser | Bin 334 -> 0 bytes .../f70e582c-b43f-40d3-a8e3-65e63d33f267.ser | Bin 334 -> 0 bytes 11 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .discodeit/file-data-map/BinaryContent/6588f710-e9a0-4f74-8295-f032e2e698a6.ser delete mode 100644 .discodeit/file-data-map/BinaryContent/9dcfad15-f9c8-4708-ad27-9842f5761504.ser delete mode 100644 .discodeit/file-data-map/Channel/795e6167-7678-451a-bca6-2230277b8acb.ser delete mode 100644 .discodeit/file-data-map/Channel/d30927f0-c828-4548-a03c-83c06832a0ad.ser delete mode 100644 .discodeit/file-data-map/Message/2921b105-e277-4857-9da3-81e15960dac2.ser delete mode 100644 .discodeit/file-data-map/Message/b695e605-2990-4a4a-89ed-08a404d978e4.ser delete mode 100644 .discodeit/file-data-map/ReadStatus/c277c2f8-7ad4-40e2-93fa-0a26adecc0aa.ser delete mode 100644 .discodeit/file-data-map/User/d27e6d26-233e-49c0-ae6a-6b0198afc89f.ser delete mode 100644 .discodeit/file-data-map/User/d28b36ee-f8f9-4a0a-a805-ef6d3318703f.ser delete mode 100644 .discodeit/file-data-map/UserStatus/de026036-78cb-420d-bf7b-d11dcbf9b7e2.ser delete mode 100644 .discodeit/file-data-map/UserStatus/f70e582c-b43f-40d3-a8e3-65e63d33f267.ser diff --git a/.discodeit/file-data-map/BinaryContent/6588f710-e9a0-4f74-8295-f032e2e698a6.ser b/.discodeit/file-data-map/BinaryContent/6588f710-e9a0-4f74-8295-f032e2e698a6.ser deleted file mode 100644 index 4022e02b96d342cabf618df3a3e73f9fd99e7f55..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 419 zcmX|-u}i~16o+3^14>&dI*N-fP8aOj$=1P^P>0&l;Y`kQl3Zf$wWcBtQbp*XgN3>| zIdyUrCujW|6cNEeaO>(_QoQZn`@Q#lcjF&0Wd!R^;1d}}Tp;nelspi`<dNKx>HsqYNxYG+6!h(H3yVUZiXj@HGJKJR&=A4L8WbOtcET#eE zKC@~&#(O$!PMN6nwn3hG>T}{(`|M#^3N>; zj5_2!MkO|RyTVb{A?FAGw>Uf-{+2$zM)&M;QvAN#)&}V=Ek_c$!RPhM)#{r8nL3zV g>UCHgK`{X)p+?8wW6{NUxSYFrA1K8*oc{3IAL;6hrT_o{ diff --git a/.discodeit/file-data-map/BinaryContent/9dcfad15-f9c8-4708-ad27-9842f5761504.ser b/.discodeit/file-data-map/BinaryContent/9dcfad15-f9c8-4708-ad27-9842f5761504.ser deleted file mode 100644 index e1a0a2d84263ac93ba6a060d7a2e7c9721fab2ee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 419 zcmX|-F;Buk6vrQhCK$jF36pWrL8oh&nFJGsG{giM9nSV7XWDD+J+Khtq%I6PK!Stc z02W8%2XHX)Bk17jXpEcJmhiUs-v9l7|GU{Y=rVwc<+()q0T)PIE+zK_vAMK7n{gye zAjbizaY2JY-4mz=kW@+o-GrQ#437prLs&Di&{PRgax;=$wqGcIZ_crfAPaR|8ks(Y5)KL diff --git a/.discodeit/file-data-map/Channel/795e6167-7678-451a-bca6-2230277b8acb.ser b/.discodeit/file-data-map/Channel/795e6167-7678-451a-bca6-2230277b8acb.ser deleted file mode 100644 index 09fdaa99959fcfec1730620e86e36edd662940d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 505 zcmb7>ze@u#6vtoBA5hw=R6)?Cba4<8ojciDu;m7!?Whp%!ZmW2#>=~UMd_rAyP*Go zlar(LKk#pHb~Q1zxEeAfc`x7h`M&r12`f5<{a}<3tx_S8BtmO3k|Y#57=>IQ;SvQ- zNNdQXtXU;fL9O2M6q|}kpIVoL`Qb$VXqnCqzfLvWyJnK;0wi;XO)mo0Q-5&~FGh1aBu?T8` zYXmVmYd<;`npo)B^U~K-a!)<*x=@Ze)7TSHQ=slbDH$zpxthJy5B6>zJ|^cZ8t3gD mgjJ_LO@aPn8rI`{B2YE>zeu*jq%E`LZ%~;kr-P>7X{m380ir+v diff --git a/.discodeit/file-data-map/Channel/d30927f0-c828-4548-a03c-83c06832a0ad.ser b/.discodeit/file-data-map/Channel/d30927f0-c828-4548-a03c-83c06832a0ad.ser deleted file mode 100644 index f225730ca51def87110b96d901c6d622bcb63820..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 507 zcmb7>ze@u#6vtoBUzD~YRd8@AE(%Jbu63zxp)EIvZQUHgU1%eBX}rAFD@YghPY`tL zAE1k)I5@idcL;8-Cf+Hoh73vG%lCcW_uaq4oQ`128-zrwNJu21&{_;6@rCvVJ{L&1 zM1f;c?K3HP0LjWYFz-Uvi#S8>R}f*@J!K_X1x+KYrjqlr*VasnoM zw2h`-&!jlQM_5WCLlgn+cAGWxJ}X(s&tL+MIpbJygk9H=(u9yFa5pg!&`Ra s<8`y%Kv+oCClSzpjKemWjs%K^|A)z1OzLtN{tYTq=J2R_Tg2~KYsuJ-PtRoL;$0P=gPtl7)RML}J)(glMKf=+sxp8d0jq97SgW3p%usD%xJ4ng6fl zvNji8Gloq#r;cW>!X`*Mqz#W_q(kURTn^!uQo^X4)!+k4kebsG(47FtT&ieXAmMSI z#C}zos&6K2>~0BtKYD6MTu_b+%5jD3qvp>;cUO@>DniboL|}B3n#KYBNY?K!J<_MT$txx*H*yB@srAJ=&yd1G%FbTw>XPENx)j3pDHhwN}$M zqHE5qQz4k6S^IDdiUAqZ6By|b#uAr9g{@XG>Sp)(fKg=DY6OfY0CJb9nvh6EoTu<# zRj%P%2^+gpq3=ge9f=E)xFCrubglGh6x7RpI9rCXCw}t*)y5}xV z`YSINOCMj~`ml_pL!H~w!tCMsb8DB{&2V!HAzxvR#{10HJ?SRN}wnN9*MY)>{q}<5@R*lX?x`9^3Efxl3L5e*jV0jYNn&0JPz6(FN+ClX10zsvX+a7^El7aLhk>oMIJL+V zB3)6y0CX}R$O1i(1$rP0_RM7dJ|nkPpNWCVi-9L6HLUT|vB)Yz6?(n=nico-Ne3d(tzK%UL4akdt5 z_X?;e204gj@-Hs6wQmlrI#hn>N7WUFM_^Gl>}prAzRxw5D6qfOZT9ZRPcJS2Iski~ diff --git a/.discodeit/file-data-map/User/d27e6d26-233e-49c0-ae6a-6b0198afc89f.ser b/.discodeit/file-data-map/User/d27e6d26-233e-49c0-ae6a-6b0198afc89f.ser deleted file mode 100644 index b45e6ac2918fbd8eab44b1c7891a98e8076a35c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 377 zcmX|7yGjE=6usFHG?<8((oR9FX7SMl#8=^lbW2K$Av1;>cOLHCm<>oO{Q?nU^ZS5~u+d8`|PIu3ldmi`U8)`0vjlNOL*%UP~h0dYTOrYx>gn%Z5cNyRZCST5Ow>p>Wkw;6acKtZrnG9$ty z?De7}kI!4~+}Nbz9HtO>8=IO_Ok`95RS%4;D1GxE6~8g^(KJ`G*vPDJ)Xd|sJPwOx zdV6y9y0Uuw+yKbzxEbUv@ba+Tdb#<^e2B3zVzT#F*WbbmEEWQJE|%wdyQ}`p)O8p@ x(1AuGxuX~d2S~013(EXYnSC6dRc22g_n%X>zP1~^6Vwa&&8_VnYYA$_^b5|$dwBo= diff --git a/.discodeit/file-data-map/User/d28b36ee-f8f9-4a0a-a805-ef6d3318703f.ser b/.discodeit/file-data-map/User/d28b36ee-f8f9-4a0a-a805-ef6d3318703f.ser deleted file mode 100644 index a4413a63c91a418b93cc2d1f756be9a022c22a67..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 377 zcmX|7Jx{|h5WS{~pwJ>j7bG?&CPz@j(t*!`RvD10j0{$7szb43>?=(r!~_!q6B7Ib z1|~+tui$?GBe3Bl2ygeE?|t0UJA}@{+Aveh8H<{jLgz5kOrRTPLLxD#32Dj_CoNR3 z7QjpljiHsCq}U~bc6`duc|@pW)YFc*rfv+OR2)-+rHW0t9z_H4HX~04C$gX_55a{(z4KSs-@*$llmbOAR^)npsDIt7>nMPr w56x6^M*|$~A-O)xtL%Tu&13kfI?HDJ?Plxa`=>ug&?x2CH#(cf5QOFQ3nh|z2mk;8 diff --git a/.discodeit/file-data-map/UserStatus/de026036-78cb-420d-bf7b-d11dcbf9b7e2.ser b/.discodeit/file-data-map/UserStatus/de026036-78cb-420d-bf7b-d11dcbf9b7e2.ser deleted file mode 100644 index 4b85a541647adc9f7e1093f42ab40f6c7adef818..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 334 zcmZ4UmVvdnh(RwoKUc4~peQr1L@zh9xHvOEPcJ32I5|HhHM2x7HLoPIq*5=mIJGFa zB(bElm;nM9nHX537&tPE{qu4%^HO~nIFpM~6H8K497{?VgnhCS%M$fVGILY)J@bl7 z67x!|eHfTBQ%V>FAX23znK}BQp`I>4an90$6o}43hB^jD9|pEkplzNhARbdi0RzyD zydd-RK<4QMrxs0(ZP_KQ)tm3p2Z!yRT bEEqO`MA_D5Wi!rLe`5Znx?DA7JI@0E*i~<& diff --git a/.discodeit/file-data-map/UserStatus/f70e582c-b43f-40d3-a8e3-65e63d33f267.ser b/.discodeit/file-data-map/UserStatus/f70e582c-b43f-40d3-a8e3-65e63d33f267.ser deleted file mode 100644 index cd635c41702bc94b9cdb8eb92b761de4c5a12575..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 334 zcmZ4UmVvdnh(RwoKUc4~peQr1L@zh9xHvOEPcJ32I5|HhHM2x7HLoPIq*5=mIJGFa zB(bElm;nM9nHX537&tPE{qu4%^HO~nIFpM~6H8K497{?VgnhCS%M$fVGILY)J@bl7 z67x!|eHfTBQ%V>FAX23znK}BQp`I>4an90$6o}43hB^jD9|pEkplzNhARbdi0RzyD zydd-RK<4QMrxs0(ZP_KQ Date: Fri, 30 Jan 2026 14:51:42 +0900 Subject: [PATCH 03/20] =?UTF-8?q?build:=20.gitignore=20=EC=9E=91=EB=8F=99?= =?UTF-8?q?=20=ED=99=95=EC=9D=B8=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../44885152-0b8e-4b5e-9ed0-08500b3fb253.ser | Bin 0 -> 419 bytes .../55a030fa-b861-4ab4-842e-0324e8e9d281.ser | Bin 0 -> 419 bytes .../4d3668f1-e086-4637-bd53-fe25d2d5bbfc.ser | Bin 0 -> 505 bytes .../dca0c0e8-4b74-449e-8bef-b122ef9f078e.ser | Bin 0 -> 507 bytes .../cdbb90a3-62fd-4145-83de-d915f3f85c6e.ser | Bin 0 -> 420 bytes .../fd94eaad-9fbc-40ba-a347-a9eb6425cae9.ser | Bin 0 -> 420 bytes .../1a69d3f8-40e8-4a94-9f86-85191a09e5b6.ser | Bin 0 -> 371 bytes .../User/21e9f3b8-c720-41fc-8fac-ae778930aa3d.ser | Bin 0 -> 377 bytes .../User/26956224-a0bd-4928-9afc-9e9d9d546928.ser | Bin 0 -> 377 bytes .../407fffab-9f8a-4402-a2b4-f355d3fedd7a.ser | Bin 0 -> 334 bytes .../67b4a406-f6e2-4d07-bdcb-086fc6ceb865.ser | Bin 0 -> 334 bytes .../buildOutputCleanup/buildOutputCleanup.lock | Bin 17 -> 17 bytes 12 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 .discodeit/file-data-map/BinaryContent/44885152-0b8e-4b5e-9ed0-08500b3fb253.ser create mode 100644 .discodeit/file-data-map/BinaryContent/55a030fa-b861-4ab4-842e-0324e8e9d281.ser create mode 100644 .discodeit/file-data-map/Channel/4d3668f1-e086-4637-bd53-fe25d2d5bbfc.ser create mode 100644 .discodeit/file-data-map/Channel/dca0c0e8-4b74-449e-8bef-b122ef9f078e.ser create mode 100644 .discodeit/file-data-map/Message/cdbb90a3-62fd-4145-83de-d915f3f85c6e.ser create mode 100644 .discodeit/file-data-map/Message/fd94eaad-9fbc-40ba-a347-a9eb6425cae9.ser create mode 100644 .discodeit/file-data-map/ReadStatus/1a69d3f8-40e8-4a94-9f86-85191a09e5b6.ser create mode 100644 .discodeit/file-data-map/User/21e9f3b8-c720-41fc-8fac-ae778930aa3d.ser create mode 100644 .discodeit/file-data-map/User/26956224-a0bd-4928-9afc-9e9d9d546928.ser create mode 100644 .discodeit/file-data-map/UserStatus/407fffab-9f8a-4402-a2b4-f355d3fedd7a.ser create mode 100644 .discodeit/file-data-map/UserStatus/67b4a406-f6e2-4d07-bdcb-086fc6ceb865.ser diff --git a/.discodeit/file-data-map/BinaryContent/44885152-0b8e-4b5e-9ed0-08500b3fb253.ser b/.discodeit/file-data-map/BinaryContent/44885152-0b8e-4b5e-9ed0-08500b3fb253.ser new file mode 100644 index 0000000000000000000000000000000000000000..0e8f04343c22cf636f6fb86105545f7173f5566c GIT binary patch literal 419 zcmX|-F-yZh6vtmv3zf7|bQBj|#NlFR2eB5hH54^=R5+8foFtc+d#$O6lZsH#4iI=gyFq;Grg{onuhzZ?EQPKHpneUHc>&dI*N-rh{Hu)ovb1@gf6w44rg+fljIU}uWc$K2-T&74id+&Yk`+awVAJAn8E4J?u8H8LQ@wk-S7sTPx_8rEN zFo7J~q{Iac+Z(<>HGs4-8t4|}>~y%(4j96ebx51kaH*&pRaBn!H47%}kWpmLIwDM` z0^}Yu%0eO)s1)M|++`K&u_HKzj0GBZ5Q?d^f!sA}weqIYjY)p2D8o%cv3$jd0)#9^ zsGd`Z1CZFCR9V>D>pacOZQthsqM%jK;^h2ftG{&jwi_YnF?Us6O(s_{io~er+wAX+ zcW!8)nUHrGm00EV5=Uu5&htlI6m&>-;p_YLtkz%td!**W$SV#7;l+QSM2nvQ literal 0 HcmV?d00001 diff --git a/.discodeit/file-data-map/Channel/4d3668f1-e086-4637-bd53-fe25d2d5bbfc.ser b/.discodeit/file-data-map/Channel/4d3668f1-e086-4637-bd53-fe25d2d5bbfc.ser new file mode 100644 index 0000000000000000000000000000000000000000..1e4e18957402fb2fdb0226f0beb149d38ae43620 GIT binary patch literal 505 zcmb7>ze@u#6vtmrEta+-b#QPgf{Q|eiw;g&Ew-E>Tsu0PccG2krSbAkuOOY&|3J{q zKfuY+AOD2l?&Rj+AWNb+93@AG}{<_#uP0!u+JCQ2q;Ac?tByeCM=RnQ9= zN5TYh?2}s87lK7#SI&XF3xyzIKC*Bf5oX*2|H!AvV@4Z7p)YXFg<{B5kZ_4w6QN>r zB42cJ>isG3lWd!t(>AHA=Ir|0|6 uot5tU%XxhjA#c?O3DAE`!+0D{1S%T;7s+&(Y>71f8zfS@&6vrRu4?+NAU~q6DtR!umolr1>Hy8mohs#|9HFv#wec+K8CY?;&Of)gd zAK;>+aqtiE4{#xjuD0H3Tx~kEeXrm5dEfUqgC!L~%?$&hWW)uMfGfpAK|HS9&|@44 z6UcE)wg-+7%!g!UELgT7=SIvy=G76QY#%!#hawLcZ3%^rz)c(S9#d|_C2CECQj+64 zqE9=h>-BYt#XW@8Br-(q(_XLD(C>@F3D^ltz|vVNE&#;xv}l*R&Cb*y$SZyfy%u(3k{Vk5F4f!Day~Xl!{#2bmWUt<* tZy$TuxJSsQ>XQiQKgMC2Oh*DG&Huw>EGA7c4E_eCF0+5oI;?k_@(b~`r{Dkp literal 0 HcmV?d00001 diff --git a/.discodeit/file-data-map/Message/cdbb90a3-62fd-4145-83de-d915f3f85c6e.ser b/.discodeit/file-data-map/Message/cdbb90a3-62fd-4145-83de-d915f3f85c6e.ser new file mode 100644 index 0000000000000000000000000000000000000000..bd2400dad5aa5ea7c8aea55b03d01389adc31705 GIT binary patch literal 420 zcmY+Aze~eF6vvDa1tJuXnSxs==yD&^92 zDC4LyfgIP>IVL43Gl-^3ftUd)f=JAY9UwZD2!qB7StD8%xuqFg;)DVHq>2^C)9L@~ z{JfqIu5q(MgkY9VS78$r17fBlFwh|kMJ|hovR1;NTe-t~j3A@A02qz{PVW7?D`wJC`6$_?Anlc-OKy({eCkiFVLz($U4d=ItYbC;tQ>Xl7tEED8>a6 zE>YkzDRQl8g+nq;2Bd7rP(n(<-_uwAlY^xok~E z*R!{dLm-qX3;Ary5J5GAU3Opu<;xTRqr1Ije0B zACC__zFpk(=RR~SZM!ag4c*;eeH2IB*N2V#C_={!_dL}xuDj`skSr-9CP9Kb`J3CN6_-(f*S@ literal 0 HcmV?d00001 diff --git a/.discodeit/file-data-map/ReadStatus/1a69d3f8-40e8-4a94-9f86-85191a09e5b6.ser b/.discodeit/file-data-map/ReadStatus/1a69d3f8-40e8-4a94-9f86-85191a09e5b6.ser new file mode 100644 index 0000000000000000000000000000000000000000..53afdbbade385885ea56b3e871b41d866f62a412 GIT binary patch literal 371 zcmZ4UmVvdnh(RwoKUc4~peQr1L@zh9xHvOEPcJ32I5|HhHM2x7HLoPIq*5;^H8CZ) zB(bElm;nM9nHboj7}zq4K{7rJoXHu9d3mWho+%{^0zO%ZWr_NwC7C(;p`o5G)*$Jk z)Wnk16vvVh24RSFNoH=UzGq%>Nn&0JPz6(FN+ClX10zsvX+a7^El7aLhk>oMIJL+V zB3)6y0CX}R$O1i(1$rP0_RM7dJ|nkPpNWCVi-9L6HLY5S?5I8camAv{5T<_WUXY5sZkKknTunv1HdUa=W+gPRs?wN((D1K|5;+ z7FHr+>u<5JN?~K+-krGJytnf{X7mLWXJNUUDCUesO-!M4m}n-@brT_xnAC(cWF03h z6t5J(L>sDID>+HgAcDE}2|wc@p^{-!JK~zQ+fb2;V@xnxurb%YFd}c$^JIX6V5VUD zgmKvEH1|C|X}EKP#EMh6fWX@rn;c^-n>kSSK;MYMH~UfY8+{*5b0zbQ)c8jAEDp=! zut?h5qs!O%rNieMKxzi{AZvl=2M;Ub_pQ{2;6kC^{j2M5;RU91fh-rxa=qP`zwgvl z7(md1S}eJv2zz@-t_4#n`JYmAGkO|auRiW9evV&078*YU)m(mUePh!Yf=WL90su06 Aod5s; literal 0 HcmV?d00001 diff --git a/.discodeit/file-data-map/User/26956224-a0bd-4928-9afc-9e9d9d546928.ser b/.discodeit/file-data-map/User/26956224-a0bd-4928-9afc-9e9d9d546928.ser new file mode 100644 index 0000000000000000000000000000000000000000..d71b4cdce7c650946bc3053f6f3f4041b63f79ac GIT binary patch literal 377 zcmX|7J4*vW5T0BJ8camAv=fnFv4@Y&CW?p`4lH6yi#4}~k=uv66LSHv(#|%ZLL@)H z%2ML{8j<}+|7-~XtObM1tHsxv%cFEfeJQ-j@uv9Wb!Zhsl zqC=0*8}8gl+vEyvA@DZF>I+OoQ~*^E3{6t{=07TaW9XwPE=94C8Q-Xx$6#Xtw!q($0K{k=Dt${a4rD!V4@G0(mZ$=lXc6em~0lFo2)~ vjZ|<)T^t-BxehEy{Xb>q`R8RiJ;m0>8wPN}OlBRmp literal 0 HcmV?d00001 diff --git a/.discodeit/file-data-map/UserStatus/407fffab-9f8a-4402-a2b4-f355d3fedd7a.ser b/.discodeit/file-data-map/UserStatus/407fffab-9f8a-4402-a2b4-f355d3fedd7a.ser new file mode 100644 index 0000000000000000000000000000000000000000..fd1bb7a3ff8e46d14e14711d3be08342deff5714 GIT binary patch literal 334 zcmZ4UmVvdnh(RwoKUc4~peQr1L@zh9xHvOEPcJ32I5|HhHM2x7HLoPIq*5=mIJGFa zB(bElm;nM9nHX537&tPE{qu4%^HO~nIFpM~6H8K497{?VgnhCS%M$fVGILY)J@bl7 z67x!|eHfTBQ%V>FAX23znK}BQp`I>4an90$6o}43hB^jD9|pEkplzNhARbdi0RzyD zydd-RK<4QMrxs0(ZP_KQXv^o&%m40HIn@7OJ-^F^sTkx0 b77QCeqHMGN%$qwmBvV6eYLd!=y`CBX$&+u0 literal 0 HcmV?d00001 diff --git a/.discodeit/file-data-map/UserStatus/67b4a406-f6e2-4d07-bdcb-086fc6ceb865.ser b/.discodeit/file-data-map/UserStatus/67b4a406-f6e2-4d07-bdcb-086fc6ceb865.ser new file mode 100644 index 0000000000000000000000000000000000000000..7d4650276ddc22606df8b7b3641adf4d792103ac GIT binary patch literal 334 zcmZ4UmVvdnh(RwoKUc4~peQr1L@zh9xHvOEPcJ32I5|HhHM2x7HLoPIq*5=mIJGFa zB(bElm;nM9nHX537&tPE{qu4%^HO~nIFpM~6H8K497{?VgnhCS%M$fVGILY)J@bl7 z67x!|eHfTBQ%V>FAX23znK}BQp`I>4an90$6o}43hB^jD9|pEkplzNhARbdi0RzyD zydd-RK<4QMrxs0(ZP_KQwv86&(Kn018QQoB#j- literal 0 HcmV?d00001 diff --git a/discodeit/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/discodeit/.gradle/buildOutputCleanup/buildOutputCleanup.lock index f7e1036cf734787e369c9610e7d13510599dc389..5a0ef567aa2247d398e22d5d417c61bb1ca9925c 100644 GIT binary patch literal 17 VcmZR!I=1A#?(HL?3}C?e9soM*1)Kl? literal 17 VcmZR!I=1A#?(HL?3}C?e0suPY1(g5* From e54a1becc73faa24b444e479d8daa3d5e9e83a3f Mon Sep 17 00:00:00 2001 From: Color-Marker Date: Fri, 30 Jan 2026 14:52:42 +0900 Subject: [PATCH 04/20] =?UTF-8?q?build:=20.gitignore=EA=B0=80=20=EC=9E=90?= =?UTF-8?q?=EA=BE=B8=20.discodeit=20=EC=A0=9C=EC=99=B8=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EC=95=88=20=ED=95=B4=EC=A4=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../44885152-0b8e-4b5e-9ed0-08500b3fb253.ser | Bin 419 -> 0 bytes .../55a030fa-b861-4ab4-842e-0324e8e9d281.ser | Bin 419 -> 0 bytes .../4d3668f1-e086-4637-bd53-fe25d2d5bbfc.ser | Bin 505 -> 0 bytes .../dca0c0e8-4b74-449e-8bef-b122ef9f078e.ser | Bin 507 -> 0 bytes .../cdbb90a3-62fd-4145-83de-d915f3f85c6e.ser | Bin 420 -> 0 bytes .../fd94eaad-9fbc-40ba-a347-a9eb6425cae9.ser | Bin 420 -> 0 bytes .../1a69d3f8-40e8-4a94-9f86-85191a09e5b6.ser | Bin 371 -> 0 bytes .../User/21e9f3b8-c720-41fc-8fac-ae778930aa3d.ser | Bin 377 -> 0 bytes .../User/26956224-a0bd-4928-9afc-9e9d9d546928.ser | Bin 377 -> 0 bytes .../407fffab-9f8a-4402-a2b4-f355d3fedd7a.ser | Bin 334 -> 0 bytes .../67b4a406-f6e2-4d07-bdcb-086fc6ceb865.ser | Bin 334 -> 0 bytes 11 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .discodeit/file-data-map/BinaryContent/44885152-0b8e-4b5e-9ed0-08500b3fb253.ser delete mode 100644 .discodeit/file-data-map/BinaryContent/55a030fa-b861-4ab4-842e-0324e8e9d281.ser delete mode 100644 .discodeit/file-data-map/Channel/4d3668f1-e086-4637-bd53-fe25d2d5bbfc.ser delete mode 100644 .discodeit/file-data-map/Channel/dca0c0e8-4b74-449e-8bef-b122ef9f078e.ser delete mode 100644 .discodeit/file-data-map/Message/cdbb90a3-62fd-4145-83de-d915f3f85c6e.ser delete mode 100644 .discodeit/file-data-map/Message/fd94eaad-9fbc-40ba-a347-a9eb6425cae9.ser delete mode 100644 .discodeit/file-data-map/ReadStatus/1a69d3f8-40e8-4a94-9f86-85191a09e5b6.ser delete mode 100644 .discodeit/file-data-map/User/21e9f3b8-c720-41fc-8fac-ae778930aa3d.ser delete mode 100644 .discodeit/file-data-map/User/26956224-a0bd-4928-9afc-9e9d9d546928.ser delete mode 100644 .discodeit/file-data-map/UserStatus/407fffab-9f8a-4402-a2b4-f355d3fedd7a.ser delete mode 100644 .discodeit/file-data-map/UserStatus/67b4a406-f6e2-4d07-bdcb-086fc6ceb865.ser diff --git a/.discodeit/file-data-map/BinaryContent/44885152-0b8e-4b5e-9ed0-08500b3fb253.ser b/.discodeit/file-data-map/BinaryContent/44885152-0b8e-4b5e-9ed0-08500b3fb253.ser deleted file mode 100644 index 0e8f04343c22cf636f6fb86105545f7173f5566c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 419 zcmX|-F-yZh6vtmv3zf7|bQBj|#NlFR2eB5hH54^=R5+8foFtc+d#$O6lZsH#4iI=gyFq;Grg{onuhzZ?EQPKHpneUHc>&dI*N-rh{Hu)ovb1@gf6w44rg+fljIU}uWc$K2-T&74id+&Yk`+awVAJAn8E4J?u8H8LQ@wk-S7sTPx_8rEN zFo7J~q{Iac+Z(<>HGs4-8t4|}>~y%(4j96ebx51kaH*&pRaBn!H47%}kWpmLIwDM` z0^}Yu%0eO)s1)M|++`K&u_HKzj0GBZ5Q?d^f!sA}weqIYjY)p2D8o%cv3$jd0)#9^ zsGd`Z1CZFCR9V>D>pacOZQthsqM%jK;^h2ftG{&jwi_YnF?Us6O(s_{io~er+wAX+ zcW!8)nUHrGm00EV5=Uu5&htlI6m&>-;p_YLtkz%td!**W$SV#7;l+QSM2nvQ diff --git a/.discodeit/file-data-map/Channel/4d3668f1-e086-4637-bd53-fe25d2d5bbfc.ser b/.discodeit/file-data-map/Channel/4d3668f1-e086-4637-bd53-fe25d2d5bbfc.ser deleted file mode 100644 index 1e4e18957402fb2fdb0226f0beb149d38ae43620..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 505 zcmb7>ze@u#6vtmrEta+-b#QPgf{Q|eiw;g&Ew-E>Tsu0PccG2krSbAkuOOY&|3J{q zKfuY+AOD2l?&Rj+AWNb+93@AG}{<_#uP0!u+JCQ2q;Ac?tByeCM=RnQ9= zN5TYh?2}s87lK7#SI&XF3xyzIKC*Bf5oX*2|H!AvV@4Z7p)YXFg<{B5kZ_4w6QN>r zB42cJ>isG3lWd!t(>AHA=Ir|0|6 uot5tU%XxhjA#c?O3DAE`!+0D{1S%T;7s+&(Y>71f8zfS@&6vrRu4?+NAU~q6DtR!umolr1>Hy8mohs#|9HFv#wec+K8CY?;&Of)gd zAK;>+aqtiE4{#xjuD0H3Tx~kEeXrm5dEfUqgC!L~%?$&hWW)uMfGfpAK|HS9&|@44 z6UcE)wg-+7%!g!UELgT7=SIvy=G76QY#%!#hawLcZ3%^rz)c(S9#d|_C2CECQj+64 zqE9=h>-BYt#XW@8Br-(q(_XLD(C>@F3D^ltz|vVNE&#;xv}l*R&Cb*y$SZyfy%u(3k{Vk5F4f!Day~Xl!{#2bmWUt<* tZy$TuxJSsQ>XQiQKgMC2Oh*DG&Huw>EGA7c4E_eCF0+5oI;?k_@(b~`r{Dkp diff --git a/.discodeit/file-data-map/Message/cdbb90a3-62fd-4145-83de-d915f3f85c6e.ser b/.discodeit/file-data-map/Message/cdbb90a3-62fd-4145-83de-d915f3f85c6e.ser deleted file mode 100644 index bd2400dad5aa5ea7c8aea55b03d01389adc31705..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 420 zcmY+Aze~eF6vvDa1tJuXnSxs==yD&^92 zDC4LyfgIP>IVL43Gl-^3ftUd)f=JAY9UwZD2!qB7StD8%xuqFg;)DVHq>2^C)9L@~ z{JfqIu5q(MgkY9VS78$r17fBlFwh|kMJ|hovR1;NTe-t~j3A@A02qz{PVW7?D`wJC`6$_?Anlc-OKy({eCkiFVLz($U4d=ItYbC;tQ>Xl7tEED8>a6 zE>YkzDRQl8g+nq;2Bd7rP(n(<-_uwAlY^xok~E z*R!{dLm-qX3;Ary5J5GAU3Opu<;xTRqr1Ije0B zACC__zFpk(=RR~SZM!ag4c*;eeH2IB*N2V#C_={!_dL}xuDj`skSr-9CP9Kb`J3CN6_-(f*S@ diff --git a/.discodeit/file-data-map/ReadStatus/1a69d3f8-40e8-4a94-9f86-85191a09e5b6.ser b/.discodeit/file-data-map/ReadStatus/1a69d3f8-40e8-4a94-9f86-85191a09e5b6.ser deleted file mode 100644 index 53afdbbade385885ea56b3e871b41d866f62a412..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 371 zcmZ4UmVvdnh(RwoKUc4~peQr1L@zh9xHvOEPcJ32I5|HhHM2x7HLoPIq*5;^H8CZ) zB(bElm;nM9nHboj7}zq4K{7rJoXHu9d3mWho+%{^0zO%ZWr_NwC7C(;p`o5G)*$Jk z)Wnk16vvVh24RSFNoH=UzGq%>Nn&0JPz6(FN+ClX10zsvX+a7^El7aLhk>oMIJL+V zB3)6y0CX}R$O1i(1$rP0_RM7dJ|nkPpNWCVi-9L6HLY5S?5I8camAv{5T<_WUXY5sZkKknTunv1HdUa=W+gPRs?wN((D1K|5;+ z7FHr+>u<5JN?~K+-krGJytnf{X7mLWXJNUUDCUesO-!M4m}n-@brT_xnAC(cWF03h z6t5J(L>sDID>+HgAcDE}2|wc@p^{-!JK~zQ+fb2;V@xnxurb%YFd}c$^JIX6V5VUD zgmKvEH1|C|X}EKP#EMh6fWX@rn;c^-n>kSSK;MYMH~UfY8+{*5b0zbQ)c8jAEDp=! zut?h5qs!O%rNieMKxzi{AZvl=2M;Ub_pQ{2;6kC^{j2M5;RU91fh-rxa=qP`zwgvl z7(md1S}eJv2zz@-t_4#n`JYmAGkO|auRiW9evV&078*YU)m(mUePh!Yf=WL90su06 Aod5s; diff --git a/.discodeit/file-data-map/User/26956224-a0bd-4928-9afc-9e9d9d546928.ser b/.discodeit/file-data-map/User/26956224-a0bd-4928-9afc-9e9d9d546928.ser deleted file mode 100644 index d71b4cdce7c650946bc3053f6f3f4041b63f79ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 377 zcmX|7J4*vW5T0BJ8camAv=fnFv4@Y&CW?p`4lH6yi#4}~k=uv66LSHv(#|%ZLL@)H z%2ML{8j<}+|7-~XtObM1tHsxv%cFEfeJQ-j@uv9Wb!Zhsl zqC=0*8}8gl+vEyvA@DZF>I+OoQ~*^E3{6t{=07TaW9XwPE=94C8Q-Xx$6#Xtw!q($0K{k=Dt${a4rD!V4@G0(mZ$=lXc6em~0lFo2)~ vjZ|<)T^t-BxehEy{Xb>q`R8RiJ;m0>8wPN}OlBRmp diff --git a/.discodeit/file-data-map/UserStatus/407fffab-9f8a-4402-a2b4-f355d3fedd7a.ser b/.discodeit/file-data-map/UserStatus/407fffab-9f8a-4402-a2b4-f355d3fedd7a.ser deleted file mode 100644 index fd1bb7a3ff8e46d14e14711d3be08342deff5714..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 334 zcmZ4UmVvdnh(RwoKUc4~peQr1L@zh9xHvOEPcJ32I5|HhHM2x7HLoPIq*5=mIJGFa zB(bElm;nM9nHX537&tPE{qu4%^HO~nIFpM~6H8K497{?VgnhCS%M$fVGILY)J@bl7 z67x!|eHfTBQ%V>FAX23znK}BQp`I>4an90$6o}43hB^jD9|pEkplzNhARbdi0RzyD zydd-RK<4QMrxs0(ZP_KQXv^o&%m40HIn@7OJ-^F^sTkx0 b77QCeqHMGN%$qwmBvV6eYLd!=y`CBX$&+u0 diff --git a/.discodeit/file-data-map/UserStatus/67b4a406-f6e2-4d07-bdcb-086fc6ceb865.ser b/.discodeit/file-data-map/UserStatus/67b4a406-f6e2-4d07-bdcb-086fc6ceb865.ser deleted file mode 100644 index 7d4650276ddc22606df8b7b3641adf4d792103ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 334 zcmZ4UmVvdnh(RwoKUc4~peQr1L@zh9xHvOEPcJ32I5|HhHM2x7HLoPIq*5=mIJGFa zB(bElm;nM9nHX537&tPE{qu4%^HO~nIFpM~6H8K497{?VgnhCS%M$fVGILY)J@bl7 z67x!|eHfTBQ%V>FAX23znK}BQp`I>4an90$6o}43hB^jD9|pEkplzNhARbdi0RzyD zydd-RK<4QMrxs0(ZP_KQwv86&(Kn018QQoB#j- From c8727bb0a58dbba5e92b00231c24ec070d053e0b Mon Sep 17 00:00:00 2001 From: Color-Marker Date: Fri, 30 Jan 2026 15:00:58 +0900 Subject: [PATCH 05/20] =?UTF-8?q?build:=20=EC=B5=9C=EC=83=81=EC=9C=84=20gi?= =?UTF-8?q?tignore=EC=97=90=20=EC=9E=91=EC=84=B1=ED=95=B4=EB=B4=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +++- discodeit/.gitignore | 3 --- .../buildOutputCleanup/buildOutputCleanup.lock | Bin 17 -> 17 bytes 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 0537c224..bd2e6e06 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ - +**/file-data-map/ +**/.discodeit/ +**/.gradle/ .idea diff --git a/discodeit/.gitignore b/discodeit/.gitignore index aad6153b..c2065bc2 100644 --- a/discodeit/.gitignore +++ b/discodeit/.gitignore @@ -1,9 +1,6 @@ HELP.md .gradle -.gradle/ build/ -file-data-map/ -.discodeit/ !gradle/wrapper/gradle-wrapper.jar !**/src/main/**/build/ !**/src/test/**/build/ diff --git a/discodeit/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/discodeit/.gradle/buildOutputCleanup/buildOutputCleanup.lock index 5a0ef567aa2247d398e22d5d417c61bb1ca9925c..3952968967fd46ff8da55325c46e28d0c23c8b9b 100644 GIT binary patch literal 17 VcmZR!I=1A#?(HL?3}C?e4FEdx1)~4} literal 17 VcmZR!I=1A#?(HL?3}C?e9soM*1)Kl? From 13f42b2c537c38cf96a3799e8b8c7e72fd65b349 Mon Sep 17 00:00:00 2001 From: Color-Marker Date: Fri, 30 Jan 2026 15:02:22 +0900 Subject: [PATCH 06/20] =?UTF-8?q?build:=20.gradle=20=EC=A0=9C=EC=99=B8=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- discodeit/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/discodeit/.gitignore b/discodeit/.gitignore index c2065bc2..0005e4f7 100644 --- a/discodeit/.gitignore +++ b/discodeit/.gitignore @@ -1,5 +1,6 @@ HELP.md .gradle +.gradle/ build/ !gradle/wrapper/gradle-wrapper.jar !**/src/main/**/build/ From 759dd26c11bb2e7dc3ce56546fc9a176ac54e949 Mon Sep 17 00:00:00 2001 From: ColorMarker Date: Fri, 30 Jan 2026 15:03:48 +0900 Subject: [PATCH 07/20] =?UTF-8?q?build:=20=EC=9D=BC=EB=8B=A8=20=EA=B9=83?= =?UTF-8?q?=ED=97=88=EB=B8=8C=20=EB=A0=88=ED=8F=AC=EC=97=90=EC=84=9C=20dis?= =?UTF-8?q?codeit/.gradle=20=EB=94=94=EB=A0=89=ED=86=A0=EB=A6=AC=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../.gradle/9.0.0/checksums/checksums.lock | Bin 17 -> 0 bytes .../executionHistory/executionHistory.bin | Bin 112526 -> 0 bytes .../executionHistory/executionHistory.lock | Bin 17 -> 0 bytes .../.gradle/9.0.0/fileChanges/last-build.bin | Bin 1 -> 0 bytes .../.gradle/9.0.0/fileHashes/fileHashes.bin | Bin 25247 -> 0 bytes .../.gradle/9.0.0/fileHashes/fileHashes.lock | Bin 17 -> 0 bytes .../9.0.0/fileHashes/resourceHashesCache.bin | Bin 32369 -> 0 bytes discodeit/.gradle/9.0.0/gc.properties | 0 .../buildOutputCleanup/buildOutputCleanup.lock | Bin 17 -> 0 bytes .../buildOutputCleanup/cache.properties | 2 -- .../.gradle/buildOutputCleanup/outputFiles.bin | Bin 18983 -> 0 bytes discodeit/.gradle/file-system.probe | Bin 8 -> 0 bytes discodeit/.gradle/vcs-1/gc.properties | 0 13 files changed, 2 deletions(-) delete mode 100644 discodeit/.gradle/9.0.0/checksums/checksums.lock delete mode 100644 discodeit/.gradle/9.0.0/executionHistory/executionHistory.bin delete mode 100644 discodeit/.gradle/9.0.0/executionHistory/executionHistory.lock delete mode 100644 discodeit/.gradle/9.0.0/fileChanges/last-build.bin delete mode 100644 discodeit/.gradle/9.0.0/fileHashes/fileHashes.bin delete mode 100644 discodeit/.gradle/9.0.0/fileHashes/fileHashes.lock delete mode 100644 discodeit/.gradle/9.0.0/fileHashes/resourceHashesCache.bin delete mode 100644 discodeit/.gradle/9.0.0/gc.properties delete mode 100644 discodeit/.gradle/buildOutputCleanup/buildOutputCleanup.lock delete mode 100644 discodeit/.gradle/buildOutputCleanup/cache.properties delete mode 100644 discodeit/.gradle/buildOutputCleanup/outputFiles.bin delete mode 100644 discodeit/.gradle/file-system.probe delete mode 100644 discodeit/.gradle/vcs-1/gc.properties diff --git a/discodeit/.gradle/9.0.0/checksums/checksums.lock b/discodeit/.gradle/9.0.0/checksums/checksums.lock deleted file mode 100644 index bb58ad43f7de941725bd10a7da05dd9c4afa8371..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17 TcmZQxwIHtSY_R)M1}FdkH6{cu diff --git a/discodeit/.gradle/9.0.0/executionHistory/executionHistory.bin b/discodeit/.gradle/9.0.0/executionHistory/executionHistory.bin deleted file mode 100644 index 6a65ec2de541d45331414bc1ff25fe2e7d2bc3f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 112526 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 P*adcMtjv}ImFoWijo?m= diff --git a/discodeit/.gradle/9.0.0/fileHashes/fileHashes.lock b/discodeit/.gradle/9.0.0/fileHashes/fileHashes.lock deleted file mode 100644 index 97186b6a38845e4a912a5b63eef0d9f6226b1d56..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17 VcmZSX+pDT~ykv6=0~qis0su0=1VR7+ diff --git a/discodeit/.gradle/9.0.0/fileHashes/resourceHashesCache.bin b/discodeit/.gradle/9.0.0/fileHashes/resourceHashesCache.bin deleted file mode 100644 index fe0cec20c819ba65ece02a3b3959df150bd1b9ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32369 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 z zPmso$E6zFgBcy4MB8C4u51v37$-3;^8TIs7F^Xr~ct`b{Baj)))>7AL_4BF-iP(;Y zt1{(~hSltjx5lQYf-dePPEB;Y7Sd>?e%Yyb--#e>{U%2`MRp_7c;EWIe?qQrD%FI~ zt!ulc7-{s!u40CY4)y*ZRg3x=8>@me+zy)djNekpE&C|;-`y1_MH(WsxF6yrof)Tu zxgIgzW)VXg9T6J?=4Rm)g&JR)(u2iNGE!@A6v~)9n4dm#*O4nastK8qek#><{>SV< z*0X&=vz*i9NWgI!3&hia`iN`53X%(XILNTD8ORgY$P|3GYU zLnWl!=GlR;Z9Xz%H0p%AbpCQ9C5O8#4fpOzq+u(^(SV76!ESUA;}VnO#dV|+Yh%&T zFpPcQwKM#BwgiVG(s<71R3lX;+&U@I_PUB?4Q1XEX_9@t#Ts&${HCZcoDJ_eGGn#j zxOpS1Vs2`+M_`Ho|0k{H;jC zpgxF;gyY>LN00h>E=4A9q!CC)Vv&5nC9B+UI&|iRR439{te;!y!t;IB6)2c)_xAoM z(hznoyBjyRgd>Oj#)j}Jvlr4hXv<{lW!S0cSjOrQ8hVe1G%S5F#G2W!JxL6{6UMkh zgYpiPwGv_|B%9PzrTtKw{$)MN&N1v{nP`*1PFnXjKWfSMJ>tl#(6IdaHA7<{M z6NqJpvfFa_mGN39o+0|`-5*(-QlDCo89Jf7ts0%?G;68COE==t_LjI-oKvG*2h4{P zEaG#WIU2|eV?MIU?~y9HD>YQ_)K|w))|XtO?AbACRzBQ_CB`35I#GIZNt~4B$Ek9h zvz*q^QaAcgPQGWur-TN0!xKITI#pcX>EcG-hqj?4Zi}j3arN0XyU%ZZJ|PWSdJhWX zC~19dH@WskgY@4>gHk)oiU~ui>RNh$l==G`C}+XAse|ehfekgE;~z>ATF#-I!oAKC z(0Ja|YcKJ;obpR+fim(cre7{{*xciHa`hnmB3{;wvdgJ@<+nfJjc<RH01KqX#6;=Y-d$sM zlxKUcy**|jn%(5Tw-Nqv#k%M*nN&34D1@RGWN!q=TOeJUMYS1kml<&kn(i-aDVh0%4!-WfBU}L??@}7 P-^Yn@>Z)~r?&JReee(t{ diff --git a/discodeit/.gradle/9.0.0/gc.properties b/discodeit/.gradle/9.0.0/gc.properties deleted file mode 100644 index e69de29b..00000000 diff --git a/discodeit/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/discodeit/.gradle/buildOutputCleanup/buildOutputCleanup.lock deleted file mode 100644 index 3952968967fd46ff8da55325c46e28d0c23c8b9b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17 VcmZR!I=1A#?(HL?3}C?e4FEdx1)~4} diff --git a/discodeit/.gradle/buildOutputCleanup/cache.properties b/discodeit/.gradle/buildOutputCleanup/cache.properties deleted file mode 100644 index 278d5805..00000000 --- a/discodeit/.gradle/buildOutputCleanup/cache.properties +++ /dev/null @@ -1,2 +0,0 @@ -#Tue Jan 27 16:17:23 KST 2026 -gradle.version=8.14.4 diff --git a/discodeit/.gradle/buildOutputCleanup/outputFiles.bin b/discodeit/.gradle/buildOutputCleanup/outputFiles.bin deleted file mode 100644 index 3d882c42a4addc55d59a339b44e9d81fa76b0926..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18983 zcmeI%Pe_w-9LMqBW{|efoEZK&Xe0z}La7HuqDH099@+x6GKF9;MNpwlLrMxl2Ij>I zAwq$e zf1~ktc@52nxCbuzzYWyC3Q~`950@Q_R3%?eqkW!mj~dr)t@e#U+0{($Uro(EW4>yk z`C;zgUD@&Sz6LjS2lv&h-3zsGB}sP0&pmG5JP}eCi)j8QcSBCZ=pAmkMLj#w^VSL% zV{`O76$|&&DXXVl=bMxBY7+PKYs+8s2L_z-pM#g{cWPJ^fm=Qj*U`3!@V=+JX`VJ-YL7% z&;4FkNwFo6K2GzOx%0aa`loUYuMmI$1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U< z00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##An+%FVr?NsyLEJn)Lmse{~tv==pLQ) K?{@yDpV|V!8gL2# diff --git a/discodeit/.gradle/file-system.probe b/discodeit/.gradle/file-system.probe deleted file mode 100644 index 45e8e7f0579783852223693c9bb3a51ea7609374..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8 PcmZQzV4TCV$-4mn26h4` diff --git a/discodeit/.gradle/vcs-1/gc.properties b/discodeit/.gradle/vcs-1/gc.properties deleted file mode 100644 index e69de29b..00000000 From d904cb84044b17676adb328905675630f5e3b5cc Mon Sep 17 00:00:00 2001 From: Color-Marker Date: Fri, 30 Jan 2026 15:05:33 +0900 Subject: [PATCH 08/20] =?UTF-8?q?build:=20.gradle=20=EC=A0=9C=EC=99=B8=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/PULL_REQUEST_TEMPLATE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index db047f46..d62827a4 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -3,11 +3,11 @@ ### 기본 - [x] 기본 항목 1 -- [x] 기본 항목 2 +- [] 기본 항목 2 ### 심화 - [x] 심화 항목 1 -- [x] 심화 항목 2 +- [] 심화 항목 2 ## 기본 구조 - From 8652dcb49a42d48c65290722afc735f6522d394d Mon Sep 17 00:00:00 2001 From: Color-Marker Date: Fri, 30 Jan 2026 15:09:28 +0900 Subject: [PATCH 09/20] =?UTF-8?q?build:=20.gradle=20=EC=A0=9C=EC=99=B8=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8C=85=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/PULL_REQUEST_TEMPLATE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index d62827a4..db047f46 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -3,11 +3,11 @@ ### 기본 - [x] 기본 항목 1 -- [] 기본 항목 2 +- [x] 기본 항목 2 ### 심화 - [x] 심화 항목 1 -- [] 심화 항목 2 +- [x] 심화 항목 2 ## 기본 구조 - From b66899462f68f176bdda32da2b6409d3208c2938 Mon Sep 17 00:00:00 2001 From: ColorMarker Date: Fri, 30 Jan 2026 15:12:14 +0900 Subject: [PATCH 10/20] =?UTF-8?q?build:=20discodeit/build=20=EB=94=94?= =?UTF-8?q?=EB=A0=89=ED=86=A0=EB=A6=AC=EB=8F=84=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reports/problems/problems-report.html | 663 ------------------ .../compileJava/previous-compilation-data.bin | Bin 29430 -> 0 bytes 2 files changed, 663 deletions(-) delete mode 100644 discodeit/build/reports/problems/problems-report.html delete mode 100644 discodeit/build/tmp/compileJava/previous-compilation-data.bin diff --git a/discodeit/build/reports/problems/problems-report.html b/discodeit/build/reports/problems/problems-report.html deleted file mode 100644 index b723588f..00000000 --- a/discodeit/build/reports/problems/problems-report.html +++ /dev/null @@ -1,663 +0,0 @@ - - - - - - - - - - - - - Gradle Configuration Cache - - - -
- -
- Loading... -
- - - - - - diff --git a/discodeit/build/tmp/compileJava/previous-compilation-data.bin b/discodeit/build/tmp/compileJava/previous-compilation-data.bin deleted file mode 100644 index 95037be4aa1f0815c783e04501c681f587a309b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29430 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 Date: Fri, 30 Jan 2026 15:16:12 +0900 Subject: [PATCH 11/20] =?UTF-8?q?build:=20build=20=ED=8F=B4=EB=8D=94=20?= =?UTF-8?q?=EC=A0=9C=EC=99=B8=20=EC=B2=98=EB=A6=AC=20=EC=9E=91=EC=97=85=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/sprint/mission/discodeit/DiscodeitApplication.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/DiscodeitApplication.java b/discodeit/src/main/java/com/sprint/mission/discodeit/DiscodeitApplication.java index bad02960..31205c13 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/DiscodeitApplication.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/DiscodeitApplication.java @@ -46,6 +46,7 @@ static Channel setupPrivateChannel(ChannelService channelService, List use static void messageCreateTest(MessageService messageService, Channel channel, User author) { MessageCreateDto messageCreateDto = new MessageCreateDto("Hello World", channel.getId(), author.getId(), null); Message message = messageService.create(messageCreateDto); + System.out.println("Message created: " + message.getContent()); System.out.println("In channel: " + message.getChannelId()); System.out.println("By user: " + message.getAuthorId()); @@ -73,6 +74,7 @@ public static void main(String[] args) { userList.add(user); List userIdList = userList.stream() .map(User::getId).toList(); + Channel publicChannel = setupPublicChannel(channelService); System.out.println("Public Channel: " + publicChannel.getName() + "_" + publicChannel.getId()); Channel privateChannel = setupPrivateChannel(channelService, userIdList); From 7c93d221b4bf5f6b457f276781ef5971b4b348f4 Mon Sep 17 00:00:00 2001 From: Color-Marker Date: Wed, 4 Feb 2026 09:37:20 +0900 Subject: [PATCH 12/20] =?UTF-8?q?extra:=20pr=EB=AC=B8=EC=84=9C=20=EB=AF=B8?= =?UTF-8?q?=EB=A6=AC=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/PR_SPRINT_3.md | 34 ++++++++++--------- .../discodeit/DiscodeitApplication.java | 8 ++--- .../mission/discodeit/JavaApplication.java | 9 +++-- ...{UseCreaterDto.java => UserCreateDto.java} | 4 +-- .../discodeit/dto/UserStatusUpdateDto.java | 3 +- .../mission/discodeit/entity/UserStatus.java | 5 +-- .../file/FileBinaryContentRepository.java | 2 +- .../file/FileChannelRepository.java | 2 +- .../file/FileMessageRepository.java | 2 +- .../file/FileReadStatusRepository.java | 2 +- .../repository/file/FileUserRepository.java | 2 +- .../file/FileUserStatusRepository.java | 2 +- .../discodeit/service/UserService.java | 4 +-- .../service/basic/BasicUserService.java | 17 +++++----- .../service/basic/UserStatusService.java | 4 +-- 15 files changed, 49 insertions(+), 51 deletions(-) rename discodeit/src/main/java/com/sprint/mission/discodeit/dto/{UseCreaterDto.java => UserCreateDto.java} (64%) diff --git a/.github/PR_SPRINT_3.md b/.github/PR_SPRINT_3.md index 1eb1c83e..bbdcc559 100644 --- a/.github/PR_SPRINT_3.md +++ b/.github/PR_SPRINT_3.md @@ -10,10 +10,11 @@ - [x] 심화 항목 2 ## 기본 구조 -* +* 미션에서 제공해 준 기본 코드를 이용했습니다. ## 주요 변경사항 -* +* 어노테이션들을 추가하였으며, dto를 통해 파라미터를 받게끔 변경하였습니다. +* 새로운 기능을 위한 레포지토리들이 추가되었습니다. ## Spring 개념 이해 JavaApplication과 DiscodeitApplication에서 @@ -21,18 +22,19 @@ Service를 초기화하는 방식의 차이에 대해 다음 키워드를 중심 **키워드: IoC Container, Dependency Injection, Bean** -### JavaApplication의 Service 초기화 방식 -현재 JavaApplication에서는, 서비스를 생성자로 새롭게 생성하기 전에, +### JavaApplication vs DiscodeitApplication +현재 JavaApplication에서는, 서비스를 생성자로 새롭게 생성하기 전에, +레포지토리들을 먼저 생성자로 모두 생성한 후, 서비스를 new Service()로 직접 호출하는 방식으로 객체를 생성하고 있다. +즉, 개발자가 직접 제어를 하고 있다고 볼 수 있다. +또한, 현재 JavaApplication에서는 그저 평범한 객체를 이용하고 있다. 컨테이너의 관리도 받지 않으며, +Bean을 이용하고 있지도 않다. +의존성 주입은, 현재 JavaApplication에서는 서비스를 생성할 때, +그 이전에 레포지토리를 직접 생성자를 통해 생성한 후 일일이 주입해 주는 방식으로 진행하고 있습니다. + +반면에 DiscodeitApplication에서는, 애플리케이션이 실행될 때 컨테이너가 스스로 필요한 객체들을 생성해준다. +따라서 getBean을 통해 생성되어 있는 서비스 객체를 받아오기만 하면 된다. +DiscodeitApplication에서는 Spring IoC 컨테이너가 관리하는 자바 객체인 Bean의 형태로 클래스를 사용하고 있다. +클래스 위에 @Service들과 같은 어노테이션으로 Bean으로 등록해주고 있다. +의존성 주입은, 컨테이너가 서비스 객체들 내부의 ChannelRepository와 같은 것들에 +이미 생성해둔 빈들을 찾아 알아서 연결해 생성해줍니다. -레포지토리들을 먼저 생성자로 모두 생성한 후, 서비스 생성자에 직접 매개변수로 입력해주는 방식으로 초기화를 진행하고 있다. - - -### DiscodeitApplication의 Service 초기화 방식 - - -## 스크린샷 -![사진설명](사진링크) - -## 멘토에게 -- -- \ No newline at end of file diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/DiscodeitApplication.java b/discodeit/src/main/java/com/sprint/mission/discodeit/DiscodeitApplication.java index 31205c13..d8886175 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/DiscodeitApplication.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/DiscodeitApplication.java @@ -22,10 +22,10 @@ public class DiscodeitApplication { static User setupUser(UserService userService) { BinaryContentCreateDto file1 = new BinaryContentCreateDto("file1","txt",40L); BinaryContentCreateDto file2 = new BinaryContentCreateDto("file2","txt",40L); - UseCreaterDto useCreaterDto1 = new UseCreaterDto("admin", "admin@codeit.com", "admin1234", file1); - UseCreaterDto useCreaterDto2 = new UseCreaterDto("woody", "woody@codeit.com", "woody1234", file2); - User user1 = userService.create(useCreaterDto1); - User user2 = userService.create(useCreaterDto2); + UserCreateDto userCreateDto1 = new UserCreateDto("admin", "admin@codeit.com", "admin1234", file1); + UserCreateDto userCreateDto2 = new UserCreateDto("woody", "woody@codeit.com", "woody1234", file2); + User user1 = userService.create(userCreateDto1); + User user2 = userService.create(userCreateDto2); System.out.println("user1: " + user1.getUsername() + "_" + user1.getId()); System.out.println("user2: " + user2.getUsername() + "_" + user2.getId()); diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/JavaApplication.java b/discodeit/src/main/java/com/sprint/mission/discodeit/JavaApplication.java index 2d6a7b9e..9c6dd8c4 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/JavaApplication.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/JavaApplication.java @@ -3,7 +3,6 @@ import com.sprint.mission.discodeit.dto.*; import com.sprint.mission.discodeit.entity.*; import com.sprint.mission.discodeit.repository.*; -import com.sprint.mission.discodeit.repository.file.*; import com.sprint.mission.discodeit.repository.jcf.*; import com.sprint.mission.discodeit.service.ChannelService; import com.sprint.mission.discodeit.service.MessageService; @@ -20,10 +19,10 @@ public class JavaApplication { static User setupUser(UserService userService) { BinaryContentCreateDto file1 = new BinaryContentCreateDto("file1","txt",40L); BinaryContentCreateDto file2 = new BinaryContentCreateDto("file2","txt",40L); - UseCreaterDto useCreaterDto1 = new UseCreaterDto("admin", "admin@codeit.com", "admin1234", file1); - UseCreaterDto useCreaterDto2 = new UseCreaterDto("woody", "woody@codeit.com", "woody1234", file2); - User user1 = userService.create(useCreaterDto1); - User user2 = userService.create(useCreaterDto2); + UserCreateDto userCreateDto1 = new UserCreateDto("admin", "admin@codeit.com", "admin1234", file1); + UserCreateDto userCreateDto2 = new UserCreateDto("woody", "woody@codeit.com", "woody1234", file2); + User user1 = userService.create(userCreateDto1); + User user2 = userService.create(userCreateDto2); System.out.println("user1: " + user1.getUsername() + "_" + user1.getId()); System.out.println("user2: " + user2.getUsername() + "_" + user2.getId()); diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UseCreaterDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserCreateDto.java similarity index 64% rename from discodeit/src/main/java/com/sprint/mission/discodeit/dto/UseCreaterDto.java rename to discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserCreateDto.java index b3dd6403..3fb5ac36 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UseCreaterDto.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserCreateDto.java @@ -1,8 +1,6 @@ package com.sprint.mission.discodeit.dto; -import com.sprint.mission.discodeit.entity.BinaryContent; - -public record UseCreaterDto( +public record UserCreateDto( String username, String email, String password, diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserStatusUpdateDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserStatusUpdateDto.java index 64e23f0d..81a918fa 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserStatusUpdateDto.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserStatusUpdateDto.java @@ -3,6 +3,5 @@ import java.util.UUID; public record UserStatusUpdateDto ( - UUID id, - boolean isOnline + UUID id ){ } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/entity/UserStatus.java b/discodeit/src/main/java/com/sprint/mission/discodeit/entity/UserStatus.java index e52a7c06..e3dde8f2 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/entity/UserStatus.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/entity/UserStatus.java @@ -24,6 +24,7 @@ public UserStatus(UUID userId) { this.createdAt = Instant.now(); this.updatedAt = Instant.now(); this.userId = userId; + this.isOnline = true; } public boolean stillOnline(){ @@ -36,7 +37,7 @@ public boolean stillOnline(){ return isOnline; } - public void update(boolean online) { - this.isOnline = online; + public void update() { + this.isOnline = true; } } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileBinaryContentRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileBinaryContentRepository.java index 33d088aa..df65f49c 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileBinaryContentRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileBinaryContentRepository.java @@ -29,7 +29,7 @@ public class FileBinaryContentRepository implements BinaryContentRepository { public FileBinaryContentRepository( @Value("${discodeit.repository.file-directory}") String envPath ) { - this.DIRECTORY = Paths.get(envPath,"file-data-map", BinaryContent.class.getSimpleName()); + this.DIRECTORY = Paths.get(envPath, BinaryContent.class.getSimpleName()); if (Files.notExists(DIRECTORY)) { try { Files.createDirectories(DIRECTORY); diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileChannelRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileChannelRepository.java index 7b5455bb..bb1f9653 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileChannelRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileChannelRepository.java @@ -24,7 +24,7 @@ public class FileChannelRepository implements ChannelRepository { private final String EXTENSION = ".ser"; public FileChannelRepository( @Value("${discodeit.repository.file-directory}") String envPath) { - this.DIRECTORY = Paths.get(envPath, "file-data-map", Channel.class.getSimpleName()); + this.DIRECTORY = Paths.get(envPath, Channel.class.getSimpleName()); if (Files.notExists(DIRECTORY)) { try { Files.createDirectories(DIRECTORY); diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileMessageRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileMessageRepository.java index 688416eb..5dc13f00 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileMessageRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileMessageRepository.java @@ -24,7 +24,7 @@ public class FileMessageRepository implements MessageRepository { private final String EXTENSION = ".ser"; public FileMessageRepository( @Value("${discodeit.repository.file-directory}") String envPath) { - this.DIRECTORY = Paths.get(envPath, "file-data-map", Message.class.getSimpleName()); + this.DIRECTORY = Paths.get(envPath, Message.class.getSimpleName()); if (Files.notExists(DIRECTORY)) { try { Files.createDirectories(DIRECTORY); diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileReadStatusRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileReadStatusRepository.java index 18105a64..d77d5dbe 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileReadStatusRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileReadStatusRepository.java @@ -25,7 +25,7 @@ public class FileReadStatusRepository implements ReadStatusRepository { private final String EXTENSION = ".ser"; public FileReadStatusRepository( @Value("${discodeit.repository.file-directory}") String envPath) { - this.DIRECTORY = Paths.get(envPath, "file-data-map", ReadStatus.class.getSimpleName()); + this.DIRECTORY = Paths.get(envPath, ReadStatus.class.getSimpleName()); if (Files.notExists(DIRECTORY)) { try { Files.createDirectories(DIRECTORY); diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserRepository.java index b131c4dc..578ce41d 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserRepository.java @@ -24,7 +24,7 @@ public class FileUserRepository implements UserRepository { private final String EXTENSION = ".ser"; public FileUserRepository( @Value("${discodeit.repository.file-directory}") String envPath) { - this.DIRECTORY = Paths.get(envPath, "file-data-map", User.class.getSimpleName()); + this.DIRECTORY = Paths.get(envPath, User.class.getSimpleName()); if (Files.notExists(DIRECTORY)) { try { Files.createDirectories(DIRECTORY); diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserStatusRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserStatusRepository.java index 485b73cd..d0280406 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserStatusRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserStatusRepository.java @@ -26,7 +26,7 @@ public class FileUserStatusRepository implements UserStatusRepository { private final String EXTENSION = ".ser"; public FileUserStatusRepository( @Value("${discodeit.repository.file-directory}") String envPath) { - this.DIRECTORY = Paths.get(envPath, "file-data-map", UserStatus.class.getSimpleName()); + this.DIRECTORY = Paths.get(envPath, UserStatus.class.getSimpleName()); if (Files.notExists(DIRECTORY)) { try { Files.createDirectories(DIRECTORY); diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/UserService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/UserService.java index e76f25a7..49bdb330 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/service/UserService.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/UserService.java @@ -1,6 +1,6 @@ package com.sprint.mission.discodeit.service; -import com.sprint.mission.discodeit.dto.UseCreaterDto; +import com.sprint.mission.discodeit.dto.UserCreateDto; import com.sprint.mission.discodeit.dto.UserFindResDto; import com.sprint.mission.discodeit.dto.UserUpdateDto; import com.sprint.mission.discodeit.entity.User; @@ -9,7 +9,7 @@ import java.util.UUID; public interface UserService { - User create(UseCreaterDto useCreaterDto); + User create(UserCreateDto userCreateDto); UserFindResDto find(UUID userId); List findAll(); User update(UserUpdateDto userUpdateDto); diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserService.java index da5730c6..dda68d8c 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserService.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserService.java @@ -1,10 +1,9 @@ package com.sprint.mission.discodeit.service.basic; import com.sprint.mission.discodeit.dto.BinaryContentCreateDto; -import com.sprint.mission.discodeit.dto.UseCreaterDto; +import com.sprint.mission.discodeit.dto.UserCreateDto; import com.sprint.mission.discodeit.dto.UserFindResDto; import com.sprint.mission.discodeit.dto.UserUpdateDto; -import com.sprint.mission.discodeit.entity.BinaryContent; import com.sprint.mission.discodeit.entity.User; import com.sprint.mission.discodeit.entity.UserStatus; import com.sprint.mission.discodeit.repository.BinaryContentRepository; @@ -27,23 +26,23 @@ public class BasicUserService implements UserService { private final BinaryContentRepository binaryContentRepository; @Override - public User create(UseCreaterDto useCreaterDto) { + public User create(UserCreateDto userCreateDto) { List allUser = userRepository.findAll(); allUser.stream() - .filter(p -> p.getUsername().equals(useCreaterDto.username())) + .filter(p -> p.getUsername().equals(userCreateDto.username())) .findAny() .ifPresent(p->{ - throw new IllegalArgumentException("Already existing user name: " + useCreaterDto.username()); + throw new IllegalArgumentException("Already existing user name: " + userCreateDto.username()); }); allUser.stream() - .filter(p -> p.getEmail().equals(useCreaterDto.email())) + .filter(p -> p.getEmail().equals(userCreateDto.email())) .findAny() .ifPresent(p->{ - throw new IllegalArgumentException("Already existing email: " + useCreaterDto.email()); + throw new IllegalArgumentException("Already existing email: " + userCreateDto.email()); }); - User user = new User(useCreaterDto.username(), useCreaterDto.email(), useCreaterDto.password()); + User user = new User(userCreateDto.username(), userCreateDto.email(), userCreateDto.password()); UserStatus userStatus = new UserStatus(user.getId()); - BinaryContentCreateDto binaryContentDto = useCreaterDto.profile(); + BinaryContentCreateDto binaryContentDto = userCreateDto.profile(); userStatusRepository.save(userStatus); if(binaryContentDto!=null){ binaryContentRepository.save(binaryContentDto); diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/UserStatusService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/UserStatusService.java index fa2c3e37..4af35e08 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/UserStatusService.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/UserStatusService.java @@ -42,14 +42,14 @@ public List findAll(){ public UserStatus update(UserStatusUpdateDto dto){ UserStatus userStatus = userStatusRepository.findById(dto.id()) .orElseThrow(() -> new NoSuchElementException("User status with id " + dto.id() + " not found")); - userStatus.update(dto.isOnline()); + userStatus.update(); return userStatusRepository.save(userStatus); } public UserStatus updateByUserId(UUID userId, UserStatusUpdateDto dto){ UserStatus userStatus = userStatusRepository.findByUserId(userId) .orElseThrow(() -> new NoSuchElementException("User status with id " + userId + " not found")); - userStatus.update(dto.isOnline()); + userStatus.update(); return userStatusRepository.save(userStatus); } From ca4e8ca60ad71d9df2e1b07f1ea4d84c72830897 Mon Sep 17 00:00:00 2001 From: Color-Marker Date: Wed, 4 Feb 2026 13:14:58 +0900 Subject: [PATCH 13/20] =?UTF-8?q?feat:=20user=EC=9D=98=20profileId=20binar?= =?UTF-8?q?ycontent=20id=EC=99=80=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/sprint/mission/discodeit/entity/User.java | 2 ++ .../mission/discodeit/service/basic/BasicUserService.java | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/entity/User.java b/discodeit/src/main/java/com/sprint/mission/discodeit/entity/User.java index d70fd662..d59d7860 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/entity/User.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/entity/User.java @@ -1,12 +1,14 @@ package com.sprint.mission.discodeit.entity; import lombok.Getter; +import lombok.Setter; import java.io.Serializable; import java.time.Instant; import java.util.UUID; @Getter +@Setter public class User implements Serializable { private static final long serialVersionUID = 1L; diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserService.java index dda68d8c..d0bbd1d1 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserService.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserService.java @@ -4,6 +4,7 @@ import com.sprint.mission.discodeit.dto.UserCreateDto; import com.sprint.mission.discodeit.dto.UserFindResDto; import com.sprint.mission.discodeit.dto.UserUpdateDto; +import com.sprint.mission.discodeit.entity.BinaryContent; import com.sprint.mission.discodeit.entity.User; import com.sprint.mission.discodeit.entity.UserStatus; import com.sprint.mission.discodeit.repository.BinaryContentRepository; @@ -45,7 +46,8 @@ public User create(UserCreateDto userCreateDto) { BinaryContentCreateDto binaryContentDto = userCreateDto.profile(); userStatusRepository.save(userStatus); if(binaryContentDto!=null){ - binaryContentRepository.save(binaryContentDto); + BinaryContent bc = binaryContentRepository.save(binaryContentDto); + user.setProfileId(bc.getId()); } return userRepository.save(user); } From 1463f269eda0bcf1749ce5161c20515453433a57 Mon Sep 17 00:00:00 2001 From: Color-Marker Date: Sat, 7 Feb 2026 12:54:14 +0900 Subject: [PATCH 14/20] =?UTF-8?q?build:=20pr=20md=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/PR_SPRINT_3.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/PR_SPRINT_3.md b/.github/PR_SPRINT_3.md index bbdcc559..bebbe88f 100644 --- a/.github/PR_SPRINT_3.md +++ b/.github/PR_SPRINT_3.md @@ -16,12 +16,6 @@ * 어노테이션들을 추가하였으며, dto를 통해 파라미터를 받게끔 변경하였습니다. * 새로운 기능을 위한 레포지토리들이 추가되었습니다. -## Spring 개념 이해 -JavaApplication과 DiscodeitApplication에서 -Service를 초기화하는 방식의 차이에 대해 다음 키워드를 중심으로 정리. - -**키워드: IoC Container, Dependency Injection, Bean** - ### JavaApplication vs DiscodeitApplication 현재 JavaApplication에서는, 서비스를 생성자로 새롭게 생성하기 전에, 레포지토리들을 먼저 생성자로 모두 생성한 후, 서비스를 new Service()로 직접 호출하는 방식으로 객체를 생성하고 있다. From 440175125a267c6dcda42817bb2ed023f98a3641 Mon Sep 17 00:00:00 2001 From: Color-Marker Date: Mon, 9 Feb 2026 16:33:00 +0900 Subject: [PATCH 15/20] =?UTF-8?q?build:=20base=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=B0=B8=EA=B3=A0=ED=95=98=EC=97=AC=20=EC=88=98=EC=A0=95.=20?= =?UTF-8?q?=EA=B8=B0=EB=B3=B8=20=EC=BD=94=EB=93=9C=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/PR_SPRINT_3.md | 34 ---- .github/PR_SPRINT_4.md | 24 +++ .../discodeit/DiscodeitApplication.java | 52 ------ .../mission/discodeit/JavaApplication.java | 16 +- ...to.java => BinaryContentCreateReqDto.java} | 4 +- .../discodeit/dto/ChannelFindResDto.java | 14 -- ...reateDto.java => MessageCreateReqDto.java} | 7 +- .../discodeit/dto/MessageUpdateDto.java | 8 - .../discodeit/dto/MessageUpdateReqDto.java | 7 + .../dto/PrivateChannelCreateReqDto.java | 8 + .../discodeit/dto/PrivateChannelDto.java | 13 -- .../dto/PublicChannelCreateReqDto.java | 7 + .../discodeit/dto/PublicChannelDto.java | 9 -- ...to.java => PublicChannelUpdateReqDto.java} | 5 +- .../discodeit/dto/ReadStatusCreateDto.java | 12 -- .../discodeit/dto/ReadStatusCreateReqDto.java | 11 ++ .../discodeit/dto/ReadStatusUpdateDto.java | 8 - .../discodeit/dto/ReadStatusUpdateReqDto.java | 7 + .../mission/discodeit/dto/UserCreateDto.java | 8 - ...rFindResDto.java => UserCreateReqDto.java} | 4 +- ...UserLoginDto.java => UserLoginReqDto.java} | 2 +- .../discodeit/dto/UserStatusCreateDto.java | 7 - .../discodeit/dto/UserStatusCreateReqDto.java | 9 ++ .../discodeit/dto/UserStatusUpdateDto.java | 7 - .../discodeit/dto/UserStatusUpdateReqDto.java | 7 + ...erUpdateDto.java => UserUpdateReqDto.java} | 6 +- .../discodeit/dto/data/ChannelDto.java | 17 ++ .../mission/discodeit/dto/data/UserDto.java | 16 ++ .../discodeit/entity/BinaryContent.java | 6 +- .../mission/discodeit/entity/ReadStatus.java | 19 ++- .../sprint/mission/discodeit/entity/User.java | 11 +- .../mission/discodeit/entity/UserStatus.java | 32 ++-- .../repository/BinaryContentRepository.java | 8 +- .../repository/MessageRepository.java | 3 +- .../repository/ReadStatusRepository.java | 10 +- .../discodeit/repository/UserRepository.java | 10 ++ .../file/FileBinaryContentRepository.java | 32 ++-- .../file/FileChannelRepository.java | 16 +- .../file/FileMessageRepository.java | 25 +-- .../file/FileReadStatusRepository.java | 67 ++++---- .../repository/file/FileUserRepository.java | 35 +++- .../file/FileUserStatusRepository.java | 35 ++-- .../jcf/JCFBinaryContentRepository.java | 21 +-- .../repository/jcf/JCFChannelRepository.java | 5 +- .../repository/jcf/JCFMessageRepository.java | 15 +- .../jcf/JCFReadStatusRepository.java | 40 ++--- .../repository/jcf/JCFUserRepository.java | 24 ++- .../jcf/JCFUserStatusRepository.java | 18 +-- .../discodeit/service/AuthService.java | 21 +-- .../service/BinaryContentService.java | 14 ++ .../discodeit/service/ChannelService.java | 22 ++- .../discodeit/service/MessageService.java | 9 +- .../discodeit/service/ReadStatusService.java | 16 ++ .../discodeit/service/UserService.java | 16 +- .../discodeit/service/UserStatusService.java | 17 ++ .../service/basic/BasicAuthService.java | 31 ++++ .../basic/BasicBinaryContentService.java | 49 ++++++ .../service/basic/BasicChannelService.java | 139 ++++++++-------- .../service/basic/BasicMessageService.java | 75 +++++---- .../service/basic/BasicReadStatusService.java | 72 +++++++++ .../service/basic/BasicUserService.java | 150 +++++++++++------- .../service/basic/BasicUserStatusService.java | 78 +++++++++ .../service/basic/BinaryContentService.java | 41 ----- .../service/basic/ReadStatusService.java | 66 -------- .../service/basic/UserStatusService.java | 64 -------- 65 files changed, 875 insertions(+), 766 deletions(-) delete mode 100644 .github/PR_SPRINT_3.md create mode 100644 .github/PR_SPRINT_4.md rename discodeit/src/main/java/com/sprint/mission/discodeit/dto/{BinaryContentCreateDto.java => BinaryContentCreateReqDto.java} (62%) delete mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/dto/ChannelFindResDto.java rename discodeit/src/main/java/com/sprint/mission/discodeit/dto/{MessageCreateDto.java => MessageCreateReqDto.java} (56%) delete mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/dto/MessageUpdateDto.java create mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/dto/MessageUpdateReqDto.java create mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/dto/PrivateChannelCreateReqDto.java delete mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/dto/PrivateChannelDto.java create mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/dto/PublicChannelCreateReqDto.java delete mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/dto/PublicChannelDto.java rename discodeit/src/main/java/com/sprint/mission/discodeit/dto/{ChannelUpdateDto.java => PublicChannelUpdateReqDto.java} (56%) delete mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/dto/ReadStatusCreateDto.java create mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/dto/ReadStatusCreateReqDto.java delete mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/dto/ReadStatusUpdateDto.java create mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/dto/ReadStatusUpdateReqDto.java delete mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserCreateDto.java rename discodeit/src/main/java/com/sprint/mission/discodeit/dto/{UserFindResDto.java => UserCreateReqDto.java} (61%) rename discodeit/src/main/java/com/sprint/mission/discodeit/dto/{UserLoginDto.java => UserLoginReqDto.java} (75%) delete mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserStatusCreateDto.java create mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserStatusCreateReqDto.java delete mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserStatusUpdateDto.java create mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserStatusUpdateReqDto.java rename discodeit/src/main/java/com/sprint/mission/discodeit/dto/{UserUpdateDto.java => UserUpdateReqDto.java} (63%) create mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/dto/data/ChannelDto.java create mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/dto/data/UserDto.java create mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/service/BinaryContentService.java create mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/service/ReadStatusService.java create mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/service/UserStatusService.java create mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicAuthService.java create mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicBinaryContentService.java create mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicReadStatusService.java create mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserStatusService.java delete mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BinaryContentService.java delete mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/ReadStatusService.java delete mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/UserStatusService.java diff --git a/.github/PR_SPRINT_3.md b/.github/PR_SPRINT_3.md deleted file mode 100644 index bebbe88f..00000000 --- a/.github/PR_SPRINT_3.md +++ /dev/null @@ -1,34 +0,0 @@ - -## 요구사항 - -### 기본 -- [x] 기본 항목 1 -- [x] 기본 항목 2 - -### 심화 -- [x] 심화 항목 1 -- [x] 심화 항목 2 - -## 기본 구조 -* 미션에서 제공해 준 기본 코드를 이용했습니다. - -## 주요 변경사항 -* 어노테이션들을 추가하였으며, dto를 통해 파라미터를 받게끔 변경하였습니다. -* 새로운 기능을 위한 레포지토리들이 추가되었습니다. - -### JavaApplication vs DiscodeitApplication -현재 JavaApplication에서는, 서비스를 생성자로 새롭게 생성하기 전에, -레포지토리들을 먼저 생성자로 모두 생성한 후, 서비스를 new Service()로 직접 호출하는 방식으로 객체를 생성하고 있다. -즉, 개발자가 직접 제어를 하고 있다고 볼 수 있다. -또한, 현재 JavaApplication에서는 그저 평범한 객체를 이용하고 있다. 컨테이너의 관리도 받지 않으며, -Bean을 이용하고 있지도 않다. -의존성 주입은, 현재 JavaApplication에서는 서비스를 생성할 때, -그 이전에 레포지토리를 직접 생성자를 통해 생성한 후 일일이 주입해 주는 방식으로 진행하고 있습니다. - -반면에 DiscodeitApplication에서는, 애플리케이션이 실행될 때 컨테이너가 스스로 필요한 객체들을 생성해준다. -따라서 getBean을 통해 생성되어 있는 서비스 객체를 받아오기만 하면 된다. -DiscodeitApplication에서는 Spring IoC 컨테이너가 관리하는 자바 객체인 Bean의 형태로 클래스를 사용하고 있다. -클래스 위에 @Service들과 같은 어노테이션으로 Bean으로 등록해주고 있다. -의존성 주입은, 컨테이너가 서비스 객체들 내부의 ChannelRepository와 같은 것들에 -이미 생성해둔 빈들을 찾아 알아서 연결해 생성해줍니다. - diff --git a/.github/PR_SPRINT_4.md b/.github/PR_SPRINT_4.md new file mode 100644 index 00000000..db047f46 --- /dev/null +++ b/.github/PR_SPRINT_4.md @@ -0,0 +1,24 @@ + +## 요구사항 + +### 기본 +- [x] 기본 항목 1 +- [x] 기본 항목 2 + +### 심화 +- [x] 심화 항목 1 +- [x] 심화 항목 2 + +## 기본 구조 +- +- + +## 주요 변경사항 +- + +## 스크린샷 +![사진설명](사진링크) + +## 멘토에게 +- +- \ No newline at end of file diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/DiscodeitApplication.java b/discodeit/src/main/java/com/sprint/mission/discodeit/DiscodeitApplication.java index d8886175..b8ba8e73 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/DiscodeitApplication.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/DiscodeitApplication.java @@ -19,40 +19,6 @@ @SpringBootApplication public class DiscodeitApplication { - static User setupUser(UserService userService) { - BinaryContentCreateDto file1 = new BinaryContentCreateDto("file1","txt",40L); - BinaryContentCreateDto file2 = new BinaryContentCreateDto("file2","txt",40L); - UserCreateDto userCreateDto1 = new UserCreateDto("admin", "admin@codeit.com", "admin1234", file1); - UserCreateDto userCreateDto2 = new UserCreateDto("woody", "woody@codeit.com", "woody1234", file2); - User user1 = userService.create(userCreateDto1); - User user2 = userService.create(userCreateDto2); - - System.out.println("user1: " + user1.getUsername() + "_" + user1.getId()); - System.out.println("user2: " + user2.getUsername() + "_" + user2.getId()); - - return user1; - } - - static Channel setupPublicChannel(ChannelService channelService) { - PublicChannelDto publicChannelDto = new PublicChannelDto(ChannelType.PUBLIC, "public", "This is public channel."); - return channelService.createPublicChannel(publicChannelDto); - } - - static Channel setupPrivateChannel(ChannelService channelService, List userIdList) { - PrivateChannelDto privateChannelDto = new PrivateChannelDto(ChannelType.PRIVATE, "private", "This is private channel", userIdList); - return channelService.createPrivateChannel(privateChannelDto); - } - - static void messageCreateTest(MessageService messageService, Channel channel, User author) { - MessageCreateDto messageCreateDto = new MessageCreateDto("Hello World", channel.getId(), author.getId(), null); - Message message = messageService.create(messageCreateDto); - - System.out.println("Message created: " + message.getContent()); - System.out.println("In channel: " + message.getChannelId()); - System.out.println("By user: " + message.getAuthorId()); - System.out.println(); - } - public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(DiscodeitApplication.class, args); @@ -66,24 +32,6 @@ public static void main(String[] args) { channelService = context.getBean(ChannelService.class); messageService = context.getBean(MessageService.class); - // 셋업 - User user = setupUser(userService); - System.out.println(); - - List userList = new ArrayList<>(); - userList.add(user); - List userIdList = userList.stream() - .map(User::getId).toList(); - - Channel publicChannel = setupPublicChannel(channelService); - System.out.println("Public Channel: " + publicChannel.getName() + "_" + publicChannel.getId()); - Channel privateChannel = setupPrivateChannel(channelService, userIdList); - System.out.println("Private Channel: " + privateChannel.getName() + "_" + privateChannel.getId()); - System.out.println(); - - // 테스트 - messageCreateTest(messageService, publicChannel, user); - messageCreateTest(messageService, privateChannel, user); } } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/JavaApplication.java b/discodeit/src/main/java/com/sprint/mission/discodeit/JavaApplication.java index 9c6dd8c4..77696511 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/JavaApplication.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/JavaApplication.java @@ -17,12 +17,12 @@ public class JavaApplication { static User setupUser(UserService userService) { - BinaryContentCreateDto file1 = new BinaryContentCreateDto("file1","txt",40L); - BinaryContentCreateDto file2 = new BinaryContentCreateDto("file2","txt",40L); - UserCreateDto userCreateDto1 = new UserCreateDto("admin", "admin@codeit.com", "admin1234", file1); - UserCreateDto userCreateDto2 = new UserCreateDto("woody", "woody@codeit.com", "woody1234", file2); - User user1 = userService.create(userCreateDto1); - User user2 = userService.create(userCreateDto2); + BinaryContentCreateReqDto file1 = new BinaryContentCreateReqDto("file1","txt",40L); + BinaryContentCreateReqDto file2 = new BinaryContentCreateReqDto("file2","txt",40L); + UserCreateReqDto userCreateReqDto1 = new UserCreateReqDto("admin", "admin@codeit.com", "admin1234", file1); + UserCreateReqDto userCreateReqDto2 = new UserCreateReqDto("woody", "woody@codeit.com", "woody1234", file2); + User user1 = userService.create(userCreateReqDto1); + User user2 = userService.create(userCreateReqDto2); System.out.println("user1: " + user1.getUsername() + "_" + user1.getId()); System.out.println("user2: " + user2.getUsername() + "_" + user2.getId()); @@ -41,8 +41,8 @@ static Channel setupPrivateChannel(ChannelService channelService, List use } static void messageCreateTest(MessageService messageService, Channel channel, User author) { - MessageCreateDto messageCreateDto = new MessageCreateDto("Hello World", channel.getId(), author.getId(), null); - Message message = messageService.create(messageCreateDto); + MessageCreateReqDto messageCreateReqDto = new MessageCreateReqDto("Hello World", channel.getId(), author.getId(), null); + Message message = messageService.create(messageCreateReqDto); System.out.println("Message created: " + message.getContent()); System.out.println("In channel: " + message.getChannelId()); System.out.println("By user: " + message.getAuthorId()); diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/BinaryContentCreateDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/BinaryContentCreateReqDto.java similarity index 62% rename from discodeit/src/main/java/com/sprint/mission/discodeit/dto/BinaryContentCreateDto.java rename to discodeit/src/main/java/com/sprint/mission/discodeit/dto/BinaryContentCreateReqDto.java index 580860eb..9f5d4067 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/BinaryContentCreateDto.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/BinaryContentCreateReqDto.java @@ -1,8 +1,8 @@ package com.sprint.mission.discodeit.dto; -public record BinaryContentCreateDto( +public record BinaryContentCreateReqDto( String fileName, String contentType, - Long size + byte[] bytes ) { } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/ChannelFindResDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/ChannelFindResDto.java deleted file mode 100644 index 117f1ca4..00000000 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/ChannelFindResDto.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.sprint.mission.discodeit.dto; - - -import com.sprint.mission.discodeit.entity.Channel; - -import java.time.Instant; -import java.util.List; -import java.util.UUID; - -public record ChannelFindResDto ( - Channel channel, - Instant lastMessageTime, - List userIdList -){} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/MessageCreateDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/MessageCreateReqDto.java similarity index 56% rename from discodeit/src/main/java/com/sprint/mission/discodeit/dto/MessageCreateDto.java rename to discodeit/src/main/java/com/sprint/mission/discodeit/dto/MessageCreateReqDto.java index df86c81a..70dcec95 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/MessageCreateDto.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/MessageCreateReqDto.java @@ -5,8 +5,9 @@ import java.util.List; import java.util.UUID; -public record MessageCreateDto( - String content, UUID channelId, UUID authorId, - List files +public record MessageCreateReqDto( + String content, + UUID channelId, + UUID authorId ) { } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/MessageUpdateDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/MessageUpdateDto.java deleted file mode 100644 index a14c2afc..00000000 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/MessageUpdateDto.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.sprint.mission.discodeit.dto; - -import java.util.UUID; - -public record MessageUpdateDto( - UUID messageId, String newContent -) { -} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/MessageUpdateReqDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/MessageUpdateReqDto.java new file mode 100644 index 00000000..50980081 --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/MessageUpdateReqDto.java @@ -0,0 +1,7 @@ +package com.sprint.mission.discodeit.dto; + + +public record MessageUpdateReqDto( + String newContent +) { +} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/PrivateChannelCreateReqDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/PrivateChannelCreateReqDto.java new file mode 100644 index 00000000..0d368ef1 --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/PrivateChannelCreateReqDto.java @@ -0,0 +1,8 @@ +package com.sprint.mission.discodeit.dto; + +import java.util.List; +import java.util.UUID; + +public record PrivateChannelCreateReqDto( + List participantIds +) {} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/PrivateChannelDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/PrivateChannelDto.java deleted file mode 100644 index 876331a8..00000000 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/PrivateChannelDto.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.sprint.mission.discodeit.dto; - -import com.sprint.mission.discodeit.entity.ChannelType; - -import java.util.List; -import java.util.UUID; - -public record PrivateChannelDto ( - ChannelType type, - String name, - String description, - List userIdList -){} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/PublicChannelCreateReqDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/PublicChannelCreateReqDto.java new file mode 100644 index 00000000..fe874d7b --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/PublicChannelCreateReqDto.java @@ -0,0 +1,7 @@ +package com.sprint.mission.discodeit.dto; + +public record PublicChannelCreateReqDto( + String name, + String description +) { +} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/PublicChannelDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/PublicChannelDto.java deleted file mode 100644 index f29a9d44..00000000 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/PublicChannelDto.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.sprint.mission.discodeit.dto; - -import com.sprint.mission.discodeit.entity.ChannelType; - -public record PublicChannelDto ( - ChannelType type, - String name, - String description -){} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/ChannelUpdateDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/PublicChannelUpdateReqDto.java similarity index 56% rename from discodeit/src/main/java/com/sprint/mission/discodeit/dto/ChannelUpdateDto.java rename to discodeit/src/main/java/com/sprint/mission/discodeit/dto/PublicChannelUpdateReqDto.java index 022d91a3..a2bb4cc0 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/ChannelUpdateDto.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/PublicChannelUpdateReqDto.java @@ -1,9 +1,6 @@ package com.sprint.mission.discodeit.dto; -import java.util.UUID; - -public record ChannelUpdateDto( - UUID channelId, +public record PublicChannelUpdateReqDto( String newName, String newDescription ) { diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/ReadStatusCreateDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/ReadStatusCreateDto.java deleted file mode 100644 index 8c274521..00000000 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/ReadStatusCreateDto.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.sprint.mission.discodeit.dto; - -import com.sprint.mission.discodeit.entity.Channel; -import com.sprint.mission.discodeit.entity.User; - -import java.util.UUID; - -public record ReadStatusCreateDto( - UUID channelId, - UUID userId -) { -} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/ReadStatusCreateReqDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/ReadStatusCreateReqDto.java new file mode 100644 index 00000000..32408259 --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/ReadStatusCreateReqDto.java @@ -0,0 +1,11 @@ +package com.sprint.mission.discodeit.dto; + +import java.time.Instant; +import java.util.UUID; + +public record ReadStatusCreateReqDto( + UUID channelId, + UUID userId, + Instant lastReadAt +) { +} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/ReadStatusUpdateDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/ReadStatusUpdateDto.java deleted file mode 100644 index e3bde4c4..00000000 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/ReadStatusUpdateDto.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.sprint.mission.discodeit.dto; - -import java.util.UUID; - -public record ReadStatusUpdateDto( - UUID id, - boolean isRead -) { } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/ReadStatusUpdateReqDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/ReadStatusUpdateReqDto.java new file mode 100644 index 00000000..e5b4c944 --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/ReadStatusUpdateReqDto.java @@ -0,0 +1,7 @@ +package com.sprint.mission.discodeit.dto; + +import java.time.Instant; + +public record ReadStatusUpdateReqDto( + Instant newLastReadAt +) { } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserCreateDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserCreateDto.java deleted file mode 100644 index 3fb5ac36..00000000 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserCreateDto.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.sprint.mission.discodeit.dto; - -public record UserCreateDto( - String username, - String email, - String password, - BinaryContentCreateDto profile -){} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserFindResDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserCreateReqDto.java similarity index 61% rename from discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserFindResDto.java rename to discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserCreateReqDto.java index c624378c..3e1d6bb5 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserFindResDto.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserCreateReqDto.java @@ -1,7 +1,7 @@ package com.sprint.mission.discodeit.dto; -public record UserFindResDto( +public record UserCreateReqDto( String username, String email, - boolean onlineStatus + String password ){} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserLoginDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserLoginReqDto.java similarity index 75% rename from discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserLoginDto.java rename to discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserLoginReqDto.java index 061962f7..a435de72 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserLoginDto.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserLoginReqDto.java @@ -1,6 +1,6 @@ package com.sprint.mission.discodeit.dto; -public record UserLoginDto( +public record UserLoginReqDto( String username, String password ) { diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserStatusCreateDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserStatusCreateDto.java deleted file mode 100644 index 5daf6adc..00000000 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserStatusCreateDto.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.sprint.mission.discodeit.dto; - -import java.util.UUID; - -public record UserStatusCreateDto( - UUID userId -) { } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserStatusCreateReqDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserStatusCreateReqDto.java new file mode 100644 index 00000000..cd91c75f --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserStatusCreateReqDto.java @@ -0,0 +1,9 @@ +package com.sprint.mission.discodeit.dto; + +import java.time.Instant; +import java.util.UUID; + +public record UserStatusCreateReqDto( + UUID userId, + Instant lastActiveAt +) { } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserStatusUpdateDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserStatusUpdateDto.java deleted file mode 100644 index 81a918fa..00000000 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserStatusUpdateDto.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.sprint.mission.discodeit.dto; - -import java.util.UUID; - -public record UserStatusUpdateDto ( - UUID id -){ } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserStatusUpdateReqDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserStatusUpdateReqDto.java new file mode 100644 index 00000000..ba255af9 --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserStatusUpdateReqDto.java @@ -0,0 +1,7 @@ +package com.sprint.mission.discodeit.dto; + +import java.time.Instant; + +public record UserStatusUpdateReqDto( + Instant newLastActiveAt +){ } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserUpdateDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserUpdateReqDto.java similarity index 63% rename from discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserUpdateDto.java rename to discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserUpdateReqDto.java index 51b0fb31..dd36d539 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserUpdateDto.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserUpdateReqDto.java @@ -4,10 +4,8 @@ import java.util.UUID; -public record UserUpdateDto( - UUID userId, +public record UserUpdateReqDto( String username, String email, - String password, - BinaryContent profile + String password ) { } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/data/ChannelDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/data/ChannelDto.java new file mode 100644 index 00000000..490ed0b4 --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/data/ChannelDto.java @@ -0,0 +1,17 @@ +package com.sprint.mission.discodeit.dto.data; + +import com.sprint.mission.discodeit.entity.ChannelType; + +import java.time.Instant; +import java.util.List; +import java.util.UUID; + +public record ChannelDto( + UUID id, + ChannelType type, + String name, + String description, + List participantIds, + Instant lastMessageAt +) { +} \ No newline at end of file diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/data/UserDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/data/UserDto.java new file mode 100644 index 00000000..6b528c34 --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/data/UserDto.java @@ -0,0 +1,16 @@ +package com.sprint.mission.discodeit.dto.data; + + +import java.time.Instant; +import java.util.UUID; + +public record UserDto( + UUID id, + Instant createdAt, + Instant updatedAt, + String username, + String email, + UUID profileId, + Boolean online +) { +} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/entity/BinaryContent.java b/discodeit/src/main/java/com/sprint/mission/discodeit/entity/BinaryContent.java index 6c1c8d5c..0fb87dd3 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/entity/BinaryContent.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/entity/BinaryContent.java @@ -17,13 +17,13 @@ public class BinaryContent implements Serializable { private String fileName; private String contentType; - private Long size; + private byte[] bytes; - public BinaryContent(String fileName,String contentType, Long size) { + public BinaryContent(String fileName,String contentType, byte[] bytes) { this.id = UUID.randomUUID(); this.createdAt = Instant.now(); this.fileName = fileName; this.contentType = contentType; - this.size = size; + this.bytes = bytes; } } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/entity/ReadStatus.java b/discodeit/src/main/java/com/sprint/mission/discodeit/entity/ReadStatus.java index 1d8e3e05..8325082e 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/entity/ReadStatus.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/entity/ReadStatus.java @@ -18,19 +18,26 @@ public class ReadStatus implements Serializable { private UUID userId; private UUID channelId; - private boolean isRead; + private Instant lastReadAt; - public ReadStatus(UUID userId, UUID channelId) { + public ReadStatus(UUID userId, UUID channelId, Instant lastReadAt) { this.id = UUID.randomUUID(); this.createdAt = Instant.now(); this.updatedAt = Instant.now(); this.userId = userId; this.channelId = channelId; - isRead = false; + this.lastReadAt = lastReadAt; } - public void update(boolean isRead){ - this.isRead = isRead; - updatedAt = Instant.now(); + public void update(Instant newLastReadAt){ + boolean anyValueUpdated = false; + if (newLastReadAt != null && !newLastReadAt.equals(this.lastReadAt)) { + this.lastReadAt = newLastReadAt; + anyValueUpdated = true; + } + + if (anyValueUpdated) { + this.updatedAt = Instant.now(); + } } } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/entity/User.java b/discodeit/src/main/java/com/sprint/mission/discodeit/entity/User.java index d59d7860..77d292d8 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/entity/User.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/entity/User.java @@ -21,16 +21,18 @@ public class User implements Serializable { private String password; private UUID profileId; // 프사 파일 - public User(String username, String email, String password) { + public User(String username, String email, String password, UUID profileId) { this.id = UUID.randomUUID(); this.createdAt = Instant.now(); // this.username = username; this.email = email; this.password = password; + this.profileId = profileId; + } - public void update(String newUsername, String newEmail, String newPassword) { + public void update(String newUsername, String newEmail, String newPassword, UUID newProfileId) { boolean anyValueUpdated = false; if (newUsername != null && !newUsername.equals(this.username)) { this.username = newUsername; @@ -44,9 +46,14 @@ public void update(String newUsername, String newEmail, String newPassword) { this.password = newPassword; anyValueUpdated = true; } + if (newProfileId != null && !newProfileId.equals(this.profileId)) { + this.profileId = newProfileId; + anyValueUpdated = true; + } if (anyValueUpdated) { this.updatedAt = Instant.now(); } + } } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/entity/UserStatus.java b/discodeit/src/main/java/com/sprint/mission/discodeit/entity/UserStatus.java index e3dde8f2..36c20309 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/entity/UserStatus.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/entity/UserStatus.java @@ -4,6 +4,7 @@ import java.io.Serial; import java.io.Serializable; +import java.time.Duration; import java.time.Instant; import java.util.UUID; @@ -17,27 +18,32 @@ public class UserStatus implements Serializable { private Instant updatedAt; private UUID userId; - boolean isOnline; + private Instant lastActiveAt; - public UserStatus(UUID userId) { + + public UserStatus(UUID userId, Instant lastActiveAt) { this.id = UUID.randomUUID(); this.createdAt = Instant.now(); this.updatedAt = Instant.now(); this.userId = userId; - this.isOnline = true; + this.lastActiveAt = lastActiveAt; } - public boolean stillOnline(){ - if(updatedAt.isAfter(Instant.now().minusSeconds(300))){ - isOnline = true; - } - else{ - isOnline = false; - } - return isOnline; + public Boolean isOnline() { + Instant instantFiveMinutesAgo = Instant.now().minus(Duration.ofMinutes(5)); + + return lastActiveAt.isAfter(instantFiveMinutesAgo); } - public void update() { - this.isOnline = true; + public void update(Instant lastActiveAt) { + boolean anyValueUpdated = false; + if (lastActiveAt != null && !lastActiveAt.equals(this.lastActiveAt)) { + this.lastActiveAt = lastActiveAt; + anyValueUpdated = true; + } + + if (anyValueUpdated) { + this.updatedAt = Instant.now(); + } } } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/BinaryContentRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/BinaryContentRepository.java index cfb54a96..b3d4a05a 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/BinaryContentRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/BinaryContentRepository.java @@ -1,6 +1,6 @@ package com.sprint.mission.discodeit.repository; -import com.sprint.mission.discodeit.dto.BinaryContentCreateDto; +import com.sprint.mission.discodeit.dto.BinaryContentCreateReqDto; import com.sprint.mission.discodeit.entity.BinaryContent; import java.util.List; @@ -8,9 +8,9 @@ import java.util.UUID; public interface BinaryContentRepository { - BinaryContent save(BinaryContentCreateDto binaryContentCreateDto); + BinaryContent save(BinaryContent binaryContent); Optional findById(UUID id); - List findAll(); - Boolean existsById(UUID id); + List findAllByIdIn(List ids); + boolean existsById(UUID id); void deleteById(UUID id); } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/MessageRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/MessageRepository.java index 1477de72..69547b22 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/MessageRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/MessageRepository.java @@ -9,7 +9,8 @@ public interface MessageRepository { Message save(Message message); Optional findById(UUID id); - List findAll(); + List findAllByChannelId(UUID channelId); boolean existsById(UUID id); void deleteById(UUID id); + void deleteAllByChannelId(UUID channelId); } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/ReadStatusRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/ReadStatusRepository.java index d84a000c..db512298 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/ReadStatusRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/ReadStatusRepository.java @@ -9,10 +9,8 @@ public interface ReadStatusRepository { ReadStatus save(ReadStatus readStatus); Optional findById(UUID id); - List findUserIdByChannelId(UUID channelId); - List findChannelIdByUserId(UUID userId); - Optional findByChannelAndUser(UUID channelId, UUID userId); - List findAll(); - Boolean existsById(UUID id); + List findAllByUserId(UUID userId); + List findAllByChannelId(UUID channelId); + boolean existsById(UUID id); void deleteById(UUID id); -} + void deleteAllByChannelId(UUID channelId);} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/UserRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/UserRepository.java index f449de1e..5027a343 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/UserRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/UserRepository.java @@ -7,8 +7,18 @@ public interface UserRepository { User save(User user); + Optional findById(UUID id); + + Optional findByUsername(String username); + List findAll(); + boolean existsById(UUID id); + void deleteById(UUID id); + + boolean existsByEmail(String email); + + boolean existsByUsername(String username); } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileBinaryContentRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileBinaryContentRepository.java index df65f49c..42a5861f 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileBinaryContentRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileBinaryContentRepository.java @@ -1,10 +1,7 @@ package com.sprint.mission.discodeit.repository.file; -import com.sprint.mission.discodeit.dto.BinaryContentCreateDto; import com.sprint.mission.discodeit.entity.BinaryContent; -import com.sprint.mission.discodeit.entity.User; import com.sprint.mission.discodeit.repository.BinaryContentRepository; -import jakarta.websocket.Decoder; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Repository; @@ -16,20 +13,18 @@ import java.util.List; import java.util.Optional; import java.util.UUID; +import java.util.stream.Stream; +@ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "file") @Repository -@ConditionalOnProperty( - name = "discodeit.repository.type", - havingValue = "file" -) public class FileBinaryContentRepository implements BinaryContentRepository { private final Path DIRECTORY; private final String EXTENSION = ".ser"; public FileBinaryContentRepository( - @Value("${discodeit.repository.file-directory}") String envPath + @Value("${discodeit.repository.file-directory:data}") String fileDirectory ) { - this.DIRECTORY = Paths.get(envPath, BinaryContent.class.getSimpleName()); + this.DIRECTORY = Paths.get(System.getProperty("user.dir"), fileDirectory, BinaryContent.class.getSimpleName()); if (Files.notExists(DIRECTORY)) { try { Files.createDirectories(DIRECTORY); @@ -39,14 +34,12 @@ public FileBinaryContentRepository( } } - private Path resolvePath(UUID id) { return DIRECTORY.resolve(id + EXTENSION); } @Override - public BinaryContent save(BinaryContentCreateDto dto) { - BinaryContent binaryContent = new BinaryContent(dto.fileName(), dto.contentType(), dto.size()); + public BinaryContent save(BinaryContent binaryContent) { Path path = resolvePath(binaryContent.getId()); try ( FileOutputStream fos = new FileOutputStream(path.toFile()); @@ -61,25 +54,25 @@ public BinaryContent save(BinaryContentCreateDto dto) { @Override public Optional findById(UUID id) { - BinaryContent fileNullable = null; + BinaryContent binaryContentNullable = null; Path path = resolvePath(id); if (Files.exists(path)) { try ( FileInputStream fis = new FileInputStream(path.toFile()); ObjectInputStream ois = new ObjectInputStream(fis) ) { - fileNullable = (BinaryContent) ois.readObject(); + binaryContentNullable = (BinaryContent) ois.readObject(); } catch (IOException | ClassNotFoundException e) { throw new RuntimeException(e); } } - return Optional.ofNullable(fileNullable); + return Optional.ofNullable(binaryContentNullable); } @Override - public List findAll() { - try { - return Files.list(DIRECTORY) + public List findAllByIdIn(List ids) { + try (Stream paths = Files.list(DIRECTORY)) { + return paths .filter(path -> path.toString().endsWith(EXTENSION)) .map(path -> { try ( @@ -91,6 +84,7 @@ public List findAll() { throw new RuntimeException(e); } }) + .filter(content -> ids.contains(content.getId())) .toList(); } catch (IOException e) { throw new RuntimeException(e); @@ -98,7 +92,7 @@ public List findAll() { } @Override - public Boolean existsById(UUID id) { + public boolean existsById(UUID id) { Path path = resolvePath(id); return Files.exists(path); } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileChannelRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileChannelRepository.java index bb1f9653..a345b967 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileChannelRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileChannelRepository.java @@ -13,18 +13,18 @@ import java.util.List; import java.util.Optional; import java.util.UUID; +import java.util.stream.Stream; +@ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "file") @Repository -@ConditionalOnProperty( - name = "discodeit.repository.type", - havingValue = "file" -) public class FileChannelRepository implements ChannelRepository { private final Path DIRECTORY; private final String EXTENSION = ".ser"; - public FileChannelRepository( @Value("${discodeit.repository.file-directory}") String envPath) { - this.DIRECTORY = Paths.get(envPath, Channel.class.getSimpleName()); + public FileChannelRepository( + @Value("${discodeit.repository.file-directory:data}") String fileDirectory + ) { + this.DIRECTORY = Paths.get(System.getProperty("user.dir"), fileDirectory, Channel.class.getSimpleName()); if (Files.notExists(DIRECTORY)) { try { Files.createDirectories(DIRECTORY); @@ -71,8 +71,8 @@ public Optional findById(UUID id) { @Override public List findAll() { - try { - return Files.list(DIRECTORY) + try (Stream paths = Files.list(DIRECTORY)) { + return paths .filter(path -> path.toString().endsWith(EXTENSION)) .map(path -> { try ( diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileMessageRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileMessageRepository.java index 5dc13f00..5705d82a 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileMessageRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileMessageRepository.java @@ -13,18 +13,18 @@ import java.util.List; import java.util.Optional; import java.util.UUID; +import java.util.stream.Stream; +@ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "file") @Repository -@ConditionalOnProperty( - name = "discodeit.repository.type", - havingValue = "file" -) public class FileMessageRepository implements MessageRepository { private final Path DIRECTORY; private final String EXTENSION = ".ser"; - public FileMessageRepository( @Value("${discodeit.repository.file-directory}") String envPath) { - this.DIRECTORY = Paths.get(envPath, Message.class.getSimpleName()); + public FileMessageRepository( + @Value("${discodeit.repository.file-directory:data}") String fileDirectory + ) { + this.DIRECTORY = Paths.get(System.getProperty("user.dir"), fileDirectory, Message.class.getSimpleName()); if (Files.notExists(DIRECTORY)) { try { Files.createDirectories(DIRECTORY); @@ -70,9 +70,9 @@ public Optional findById(UUID id) { } @Override - public List findAll() { - try { - return Files.list(DIRECTORY) + public List findAllByChannelId(UUID channelId) { + try (Stream paths = Files.list(DIRECTORY)) { + return paths .filter(path -> path.toString().endsWith(EXTENSION)) .map(path -> { try ( @@ -84,6 +84,7 @@ public List findAll() { throw new RuntimeException(e); } }) + .filter(message -> message.getChannelId().equals(channelId)) .toList(); } catch (IOException e) { throw new RuntimeException(e); @@ -105,4 +106,10 @@ public void deleteById(UUID id) { throw new RuntimeException(e); } } + + @Override + public void deleteAllByChannelId(UUID channelId) { + this.findAllByChannelId(channelId) + .forEach(message -> this.deleteById(message.getId())); + } } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileReadStatusRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileReadStatusRepository.java index d77d5dbe..f5f96d0d 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileReadStatusRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileReadStatusRepository.java @@ -14,18 +14,18 @@ import java.util.List; import java.util.Optional; import java.util.UUID; +import java.util.stream.Stream; +@ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "file") @Repository -@ConditionalOnProperty( - name = "discodeit.repository.type", - havingValue = "file" -) public class FileReadStatusRepository implements ReadStatusRepository { private final Path DIRECTORY; private final String EXTENSION = ".ser"; - public FileReadStatusRepository( @Value("${discodeit.repository.file-directory}") String envPath) { - this.DIRECTORY = Paths.get(envPath, ReadStatus.class.getSimpleName()); + public FileReadStatusRepository( + @Value("${discodeit.repository.file-directory:data}") String fileDirectory + ) { + this.DIRECTORY = Paths.get(System.getProperty("user.dir"), fileDirectory, ReadStatus.class.getSimpleName()); if (Files.notExists(DIRECTORY)) { try { Files.createDirectories(DIRECTORY); @@ -71,33 +71,31 @@ public Optional findById(UUID id) { } @Override - public List findUserIdByChannelId(UUID channelId) { - return findAll().stream() - .filter(r->r.getChannelId().equals(channelId)) - .map(ReadStatus::getUserId) - .toList(); - } - - @Override - public List findChannelIdByUserId(UUID userId) { - return findAll().stream() - .filter(r->r.getUserId().equals(userId)) - .map(ReadStatus::getChannelId) - .toList(); - } - - @Override - public Optional findByChannelAndUser(UUID channelId, UUID userId) { - return findAll().stream() - .filter(r->r.getChannelId().equals(channelId)) - .filter(r->r.getUserId().equals(userId)) - .findAny(); + public List findAllByUserId(UUID userId) { + try (Stream paths = Files.list(DIRECTORY)) { + return paths + .filter(path -> path.toString().endsWith(EXTENSION)) + .map(path -> { + try ( + FileInputStream fis = new FileInputStream(path.toFile()); + ObjectInputStream ois = new ObjectInputStream(fis) + ) { + return (ReadStatus) ois.readObject(); + } catch (IOException | ClassNotFoundException e) { + throw new RuntimeException(e); + } + }) + .filter(readStatus -> readStatus.getUserId().equals(userId)) + .toList(); + } catch (IOException e) { + throw new RuntimeException(e); + } } @Override - public List findAll() { - try { - return Files.list(DIRECTORY) + public List findAllByChannelId(UUID channelId) { + try (Stream paths = Files.list(DIRECTORY)) { + return paths .filter(path -> path.toString().endsWith(EXTENSION)) .map(path -> { try ( @@ -109,6 +107,7 @@ public List findAll() { throw new RuntimeException(e); } }) + .filter(readStatus -> readStatus.getChannelId().equals(channelId)) .toList(); } catch (IOException e) { throw new RuntimeException(e); @@ -116,7 +115,7 @@ public List findAll() { } @Override - public Boolean existsById(UUID id) { + public boolean existsById(UUID id) { Path path = resolvePath(id); return Files.exists(path); } @@ -130,4 +129,10 @@ public void deleteById(UUID id) { throw new RuntimeException(e); } } + + @Override + public void deleteAllByChannelId(UUID channelId) { + this.findAllByChannelId(channelId) + .forEach(readStatus -> this.deleteById(readStatus.getId())); + } } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserRepository.java index 578ce41d..b7fa1c1b 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserRepository.java @@ -13,18 +13,18 @@ import java.util.List; import java.util.Optional; import java.util.UUID; +import java.util.stream.Stream; +@ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "file") @Repository -@ConditionalOnProperty( - name = "discodeit.repository.type", - havingValue = "file" -) public class FileUserRepository implements UserRepository { private final Path DIRECTORY; private final String EXTENSION = ".ser"; - public FileUserRepository( @Value("${discodeit.repository.file-directory}") String envPath) { - this.DIRECTORY = Paths.get(envPath, User.class.getSimpleName()); + public FileUserRepository( + @Value("${discodeit.repository.file-directory:data}") String fileDirectory + ) { + this.DIRECTORY = Paths.get(System.getProperty("user.dir"), fileDirectory, User.class.getSimpleName()); if (Files.notExists(DIRECTORY)) { try { Files.createDirectories(DIRECTORY); @@ -69,10 +69,17 @@ public Optional findById(UUID id) { return Optional.ofNullable(userNullable); } + @Override + public Optional findByUsername(String username) { + return this.findAll().stream() + .filter(user -> user.getUsername().equals(username)) + .findFirst(); + } + @Override public List findAll() { - try { - return Files.list(DIRECTORY) + try (Stream paths = Files.list(DIRECTORY)) { + return paths .filter(path -> path.toString().endsWith(EXTENSION)) .map(path -> { try ( @@ -105,4 +112,16 @@ public void deleteById(UUID id) { throw new RuntimeException(e); } } + + @Override + public boolean existsByEmail(String email) { + return this.findAll().stream() + .anyMatch(user -> user.getEmail().equals(email)); + } + + @Override + public boolean existsByUsername(String username) { + return this.findAll().stream() + .anyMatch(user -> user.getUsername().equals(username)); + } } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserStatusRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserStatusRepository.java index d0280406..580309d2 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserStatusRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserStatusRepository.java @@ -15,18 +15,18 @@ import java.util.NoSuchElementException; import java.util.Optional; import java.util.UUID; +import java.util.stream.Stream; +@ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "file") @Repository -@ConditionalOnProperty( - name = "discodeit.repository.type", - havingValue = "file" -) public class FileUserStatusRepository implements UserStatusRepository { private final Path DIRECTORY; private final String EXTENSION = ".ser"; - public FileUserStatusRepository( @Value("${discodeit.repository.file-directory}") String envPath) { - this.DIRECTORY = Paths.get(envPath, UserStatus.class.getSimpleName()); + public FileUserStatusRepository( + @Value("${discodeit.repository.file-directory:data}") String fileDirectory + ) { + this.DIRECTORY = Paths.get(System.getProperty("user.dir"), fileDirectory, UserStatus.class.getSimpleName()); if (Files.notExists(DIRECTORY)) { try { Files.createDirectories(DIRECTORY); @@ -36,7 +36,6 @@ public FileUserStatusRepository( @Value("${discodeit.repository.file-directory}" } } - private Path resolvePath(UUID id) { return DIRECTORY.resolve(id + EXTENSION); } @@ -70,20 +69,19 @@ public Optional findById(UUID id) { } } return Optional.ofNullable(userStatusNullable); - } @Override public Optional findByUserId(UUID userId) { return findAll().stream() - .filter(u->u.getUserId().equals(userId)) - .findAny(); + .filter(userStatus -> userStatus.getUserId().equals(userId)) + .findFirst(); } @Override public List findAll() { - try { - return Files.list(DIRECTORY) + try (Stream paths = Files.list(DIRECTORY)) { + return paths .filter(path -> path.toString().endsWith(EXTENSION)) .map(path -> { try ( @@ -119,16 +117,7 @@ public void deleteById(UUID id) { @Override public void deleteByUserId(UUID userId) { - Optional target = findByUserId(userId); - if(target.isEmpty()){ - throw new NoSuchElementException("Can't find user status"); - } - UUID id = target.get().getId(); - Path path = resolvePath(id); - try { - Files.delete(path); - } catch (IOException e) { - throw new RuntimeException(e); - } + this.findByUserId(userId) + .ifPresent(userStatus -> this.deleteById(userStatus.getId())); } } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFBinaryContentRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFBinaryContentRepository.java index 56daf104..b231c8e8 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFBinaryContentRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFBinaryContentRepository.java @@ -1,6 +1,6 @@ package com.sprint.mission.discodeit.repository.jcf; -import com.sprint.mission.discodeit.dto.BinaryContentCreateDto; +import com.sprint.mission.discodeit.dto.BinaryContentCreateReqDto; import com.sprint.mission.discodeit.entity.BinaryContent; import com.sprint.mission.discodeit.repository.BinaryContentRepository; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -8,11 +8,8 @@ import java.util.*; +@ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "jcf", matchIfMissing = true) @Repository -@ConditionalOnProperty( - name = "discodeit.repository.type", - havingValue = "jcf" -) public class JCFBinaryContentRepository implements BinaryContentRepository { private final Map data; @@ -20,10 +17,8 @@ public JCFBinaryContentRepository() { this.data = new HashMap<>(); } - @Override - public BinaryContent save(BinaryContentCreateDto dto) { - BinaryContent binaryContent = new BinaryContent(dto.fileName(), dto.contentType(), dto.size()); + public BinaryContent save(BinaryContent binaryContent) { this.data.put(binaryContent.getId(), binaryContent); return binaryContent; } @@ -31,17 +26,17 @@ public BinaryContent save(BinaryContentCreateDto dto) { @Override public Optional findById(UUID id) { return Optional.ofNullable(this.data.get(id)); - } @Override - public List findAll() { - return this.data.values().stream().toList(); - + public List findAllByIdIn(List ids) { + return this.data.values().stream() + .filter(content -> ids.contains(content.getId())) + .toList(); } @Override - public Boolean existsById(UUID id) { + public boolean existsById(UUID id) { return this.data.containsKey(id); } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFChannelRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFChannelRepository.java index ea24fcc4..5e4ec928 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFChannelRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFChannelRepository.java @@ -7,11 +7,8 @@ import java.util.*; +@ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "jcf", matchIfMissing = true) @Repository -@ConditionalOnProperty( - name = "discodeit.repository.type", - havingValue = "jcf" -) public class JCFChannelRepository implements ChannelRepository { private final Map data; diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFMessageRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFMessageRepository.java index 949821b2..c0039f96 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFMessageRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFMessageRepository.java @@ -7,11 +7,8 @@ import java.util.*; +@ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "jcf", matchIfMissing = true) @Repository -@ConditionalOnProperty( - name = "discodeit.repository.type", - havingValue = "jcf" -) public class JCFMessageRepository implements MessageRepository { private final Map data; @@ -31,8 +28,8 @@ public Optional findById(UUID id) { } @Override - public List findAll() { - return this.data.values().stream().toList(); + public List findAllByChannelId(UUID channelId) { + return this.data.values().stream().filter(message -> message.getChannelId().equals(channelId)).toList(); } @Override @@ -44,4 +41,10 @@ public boolean existsById(UUID id) { public void deleteById(UUID id) { this.data.remove(id); } + + @Override + public void deleteAllByChannelId(UUID channelId) { + this.findAllByChannelId(channelId) + .forEach(message -> this.deleteById(message.getId())); + } } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFReadStatusRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFReadStatusRepository.java index d1d8febb..9c483eeb 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFReadStatusRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFReadStatusRepository.java @@ -7,11 +7,8 @@ import java.util.*; +@ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "jcf", matchIfMissing = true) @Repository -@ConditionalOnProperty( - name = "discodeit.repository.type", - havingValue = "jcf" -) public class JCFReadStatusRepository implements ReadStatusRepository { private final Map data; @@ -31,36 +28,21 @@ public Optional findById(UUID id) { } @Override - public List findUserIdByChannelId(UUID channelId) { - return findAll().stream() - .filter(r->r.getChannelId().equals(channelId)) - .map(ReadStatus::getUserId) + public List findAllByUserId(UUID userId) { + return this.data.values().stream() + .filter(readStatus -> readStatus.getUserId().equals(userId)) .toList(); } @Override - public List findChannelIdByUserId(UUID userId) { - return findAll().stream() - .filter(r->r.getUserId().equals(userId)) - .map(ReadStatus::getChannelId) + public List findAllByChannelId(UUID channelId) { + return this.data.values().stream() + .filter(readStatus -> readStatus.getChannelId().equals(channelId)) .toList(); } @Override - public Optional findByChannelAndUser(UUID channelId, UUID userId) { - return findAll().stream() - .filter(r->r.getUserId().equals(userId)) - .filter(r->r.getChannelId().equals(channelId)) - .findAny(); - } - - @Override - public List findAll() { - return this.data.values().stream().toList(); - } - - @Override - public Boolean existsById(UUID id) { + public boolean existsById(UUID id) { return this.data.containsKey(id); } @@ -68,4 +50,10 @@ public Boolean existsById(UUID id) { public void deleteById(UUID id) { this.data.remove(id); } + + @Override + public void deleteAllByChannelId(UUID channelId) { + this.findAllByChannelId(channelId) + .forEach(readStatus -> this.deleteById(readStatus.getId())); + } } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserRepository.java index 4d3cf8e4..93e203fd 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserRepository.java @@ -7,11 +7,8 @@ import java.util.*; +@ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "jcf", matchIfMissing = true) @Repository -@ConditionalOnProperty( - name = "discodeit.repository.type", - havingValue = "jcf" -) public class JCFUserRepository implements UserRepository { private final Map data; @@ -30,6 +27,13 @@ public Optional findById(UUID id) { return Optional.ofNullable(this.data.get(id)); } + @Override + public Optional findByUsername(String username) { + return this.findAll().stream() + .filter(user -> user.getUsername().equals(username)) + .findFirst(); + } + @Override public List findAll() { return this.data.values().stream().toList(); @@ -44,4 +48,14 @@ public boolean existsById(UUID id) { public void deleteById(UUID id) { this.data.remove(id); } -} + + @Override + public boolean existsByEmail(String email) { + return this.findAll().stream().anyMatch(user -> user.getEmail().equals(email)); + } + + @Override + public boolean existsByUsername(String username) { + return this.findAll().stream().anyMatch(user -> user.getUsername().equals(username)); + } +} \ No newline at end of file diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserStatusRepository.java b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserStatusRepository.java index 703ef19b..2f6e27e2 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserStatusRepository.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserStatusRepository.java @@ -8,11 +8,8 @@ import java.util.*; +@ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "jcf", matchIfMissing = true) @Repository -@ConditionalOnProperty( - name = "discodeit.repository.type", - havingValue = "jcf" -) public class JCFUserStatusRepository implements UserStatusRepository { private final Map data; @@ -33,9 +30,9 @@ public Optional findById(UUID id) { @Override public Optional findByUserId(UUID userId) { - return findAll().stream() - .filter(u->u.getUserId().equals(userId)) - .findAny(); + return this.findAll().stream() + .filter(userStatus -> userStatus.getUserId().equals(userId)) + .findFirst(); } @Override @@ -51,14 +48,11 @@ public boolean existsById(UUID id) { @Override public void deleteById(UUID id) { this.data.remove(id); - } @Override public void deleteByUserId(UUID userId) { - if(findByUserId(userId).isPresent()) { - UUID id = findByUserId(userId).get().getId(); - this.data.remove(id); - } + this.findByUserId(userId) + .ifPresent(userStatus -> this.deleteByUserId(userStatus.getId())); } } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/AuthService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/AuthService.java index 6edb56df..5808cddd 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/service/AuthService.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/AuthService.java @@ -1,23 +1,8 @@ package com.sprint.mission.discodeit.service; -import com.sprint.mission.discodeit.dto.UserLoginDto; +import com.sprint.mission.discodeit.dto.UserLoginReqDto; import com.sprint.mission.discodeit.entity.User; -import com.sprint.mission.discodeit.repository.UserRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import java.util.NoSuchElementException; - -@Service -@RequiredArgsConstructor -public class AuthService { - private final UserRepository userRepository; - - public User login(UserLoginDto userLoginDto){ - return userRepository.findAll().stream() - .filter(u -> u.getUsername().equals(userLoginDto.username())) - .filter(u->u.getPassword().equals(userLoginDto.password())) - .findAny() - .orElseThrow(()->new NoSuchElementException("No user found")); - } +public interface AuthService { + User login(UserLoginReqDto dto); } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/BinaryContentService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/BinaryContentService.java new file mode 100644 index 00000000..a45838f9 --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/BinaryContentService.java @@ -0,0 +1,14 @@ +package com.sprint.mission.discodeit.service; + +import com.sprint.mission.discodeit.dto.BinaryContentCreateReqDto; +import com.sprint.mission.discodeit.entity.BinaryContent; + +import java.util.List; +import java.util.UUID; + +public interface BinaryContentService { + BinaryContent create(BinaryContentCreateReqDto dto); + BinaryContent find(UUID binaryContentId); + List findAllByIdIn(List binaryContentIds); + void delete(UUID binaryContentId); +} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/ChannelService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/ChannelService.java index f07a8785..9bd442f7 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/service/ChannelService.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/ChannelService.java @@ -1,20 +1,18 @@ package com.sprint.mission.discodeit.service; -import com.sprint.mission.discodeit.dto.ChannelFindResDto; -import com.sprint.mission.discodeit.dto.ChannelUpdateDto; -import com.sprint.mission.discodeit.dto.PrivateChannelDto; -import com.sprint.mission.discodeit.dto.PublicChannelDto; +import com.sprint.mission.discodeit.dto.PrivateChannelCreateReqDto; +import com.sprint.mission.discodeit.dto.PublicChannelCreateReqDto; +import com.sprint.mission.discodeit.dto.PublicChannelUpdateReqDto; +import com.sprint.mission.discodeit.dto.data.ChannelDto; import com.sprint.mission.discodeit.entity.Channel; -import com.sprint.mission.discodeit.entity.ChannelType; import java.util.List; import java.util.UUID; public interface ChannelService { - Channel createPublicChannel(PublicChannelDto publicChannelDto); - Channel createPrivateChannel(PrivateChannelDto privateChannelDto); - ChannelFindResDto find(UUID channelId); - List findAllByUserId(UUID userId); - Channel update(ChannelUpdateDto channelUpdateDto); - void delete(UUID channelId); -} + Channel create(PublicChannelCreateReqDto dto); + Channel create(PrivateChannelCreateReqDto dto); + ChannelDto find(UUID channelId); + List findAllByUserId(UUID userId); + Channel update(UUID channelId, PublicChannelUpdateReqDto dto); + void delete(UUID channelId);} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/MessageService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/MessageService.java index b277aff0..ef34e420 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/service/MessageService.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/MessageService.java @@ -1,15 +1,16 @@ package com.sprint.mission.discodeit.service; -import com.sprint.mission.discodeit.dto.MessageCreateDto; -import com.sprint.mission.discodeit.dto.MessageUpdateDto; +import com.sprint.mission.discodeit.dto.BinaryContentCreateReqDto; +import com.sprint.mission.discodeit.dto.MessageCreateReqDto; +import com.sprint.mission.discodeit.dto.MessageUpdateReqDto; import com.sprint.mission.discodeit.entity.Message; import java.util.List; import java.util.UUID; public interface MessageService { - Message create(MessageCreateDto messageCreateDto); + Message create(MessageCreateReqDto messageCreateRequest, List binaryContentCreateRequests); Message find(UUID messageId); List findAllByChannelId(UUID channelId); - Message update(MessageUpdateDto messageUpdateDto); + Message update(UUID messageId, MessageUpdateReqDto request); void delete(UUID messageId); } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/ReadStatusService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/ReadStatusService.java new file mode 100644 index 00000000..0b972710 --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/ReadStatusService.java @@ -0,0 +1,16 @@ +package com.sprint.mission.discodeit.service; + +import com.sprint.mission.discodeit.dto.ReadStatusCreateReqDto; +import com.sprint.mission.discodeit.dto.ReadStatusUpdateReqDto; +import com.sprint.mission.discodeit.entity.ReadStatus; + +import java.util.List; +import java.util.UUID; + +public interface ReadStatusService { + ReadStatus create(ReadStatusCreateReqDto request); + ReadStatus find(UUID readStatusId); + List findAllByUserId(UUID userId); + ReadStatus update(UUID readStatusId, ReadStatusUpdateReqDto request); + void delete(UUID readStatusId); +} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/UserService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/UserService.java index 49bdb330..76372f67 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/service/UserService.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/UserService.java @@ -1,17 +1,19 @@ package com.sprint.mission.discodeit.service; -import com.sprint.mission.discodeit.dto.UserCreateDto; -import com.sprint.mission.discodeit.dto.UserFindResDto; -import com.sprint.mission.discodeit.dto.UserUpdateDto; +import com.sprint.mission.discodeit.dto.BinaryContentCreateReqDto; +import com.sprint.mission.discodeit.dto.UserCreateReqDto; +import com.sprint.mission.discodeit.dto.UserUpdateReqDto; +import com.sprint.mission.discodeit.dto.data.UserDto; import com.sprint.mission.discodeit.entity.User; import java.util.List; +import java.util.Optional; import java.util.UUID; public interface UserService { - User create(UserCreateDto userCreateDto); - UserFindResDto find(UUID userId); - List findAll(); - User update(UserUpdateDto userUpdateDto); + User create(UserCreateReqDto userCreateRequest, Optional profileCreateRequest); + UserDto find(UUID userId); + List findAll(); + User update(UUID userId, UserUpdateReqDto userUpdateRequest, Optional profileCreateRequest); void delete(UUID userId); } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/UserStatusService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/UserStatusService.java new file mode 100644 index 00000000..4e4c8205 --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/UserStatusService.java @@ -0,0 +1,17 @@ +package com.sprint.mission.discodeit.service; + +import com.sprint.mission.discodeit.dto.UserStatusCreateReqDto; +import com.sprint.mission.discodeit.dto.UserStatusUpdateReqDto; +import com.sprint.mission.discodeit.entity.UserStatus; + +import java.util.List; +import java.util.UUID; + +public interface UserStatusService { + UserStatus create(UserStatusCreateReqDto request); + UserStatus find(UUID userStatusId); + List findAll(); + UserStatus update(UUID userStatusId, UserStatusUpdateReqDto request); + UserStatus updateByUserId(UUID userId, UserStatusUpdateReqDto request); + void delete(UUID userStatusId); +} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicAuthService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicAuthService.java new file mode 100644 index 00000000..d664d9fe --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicAuthService.java @@ -0,0 +1,31 @@ +package com.sprint.mission.discodeit.service.basic; + +import com.sprint.mission.discodeit.dto.UserLoginReqDto; +import com.sprint.mission.discodeit.entity.User; +import com.sprint.mission.discodeit.repository.UserRepository; +import com.sprint.mission.discodeit.service.AuthService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.NoSuchElementException; + +@RequiredArgsConstructor +@Service +public class BasicAuthService implements AuthService { + private final UserRepository userRepository; + + @Override + public User login(UserLoginReqDto loginRequest) { + String username = loginRequest.username(); + String password = loginRequest.password(); + + User user = userRepository.findByUsername(username) + .orElseThrow(() -> new NoSuchElementException("User with username " + username + " not found")); + + if (!user.getPassword().equals(password)) { + throw new IllegalArgumentException("Wrong password"); + } + + return user; + } +} \ No newline at end of file diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicBinaryContentService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicBinaryContentService.java new file mode 100644 index 00000000..f8adaa49 --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicBinaryContentService.java @@ -0,0 +1,49 @@ +package com.sprint.mission.discodeit.service.basic; + +import com.sprint.mission.discodeit.entity.BinaryContent; +import com.sprint.mission.discodeit.repository.BinaryContentRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.*; + +@Service +@RequiredArgsConstructor +public class BasicBinaryContentService { + private final BinaryContentRepository binaryContentRepository; + + @Override + public BinaryContent create(BinaryContentCreateRequest request) { + String fileName = request.fileName(); + byte[] bytes = request.bytes(); + String contentType = request.contentType(); + BinaryContent binaryContent = new BinaryContent( + fileName, + (long) bytes.length, + contentType, + bytes + ); + return binaryContentRepository.save(binaryContent); + } + + @Override + public BinaryContent find(UUID binaryContentId) { + return binaryContentRepository.findById(binaryContentId) + .orElseThrow(() -> new NoSuchElementException("BinaryContent with id " + binaryContentId + " not found")); + } + + @Override + public List findAllByIdIn(List binaryContentIds) { + return binaryContentRepository.findAllByIdIn(binaryContentIds).stream() + .toList(); + } + + @Override + public void delete(UUID binaryContentId) { + if (!binaryContentRepository.existsById(binaryContentId)) { + throw new NoSuchElementException("BinaryContent with id " + binaryContentId + " not found"); + } + binaryContentRepository.deleteById(binaryContentId); + } + +} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicChannelService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicChannelService.java index 0a2b343f..51ffe9ba 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicChannelService.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicChannelService.java @@ -1,21 +1,15 @@ package com.sprint.mission.discodeit.service.basic; -import com.sprint.mission.discodeit.dto.ChannelFindResDto; -import com.sprint.mission.discodeit.dto.ChannelUpdateDto; -import com.sprint.mission.discodeit.dto.PrivateChannelDto; -import com.sprint.mission.discodeit.dto.PublicChannelDto; import com.sprint.mission.discodeit.entity.*; import com.sprint.mission.discodeit.repository.ChannelRepository; import com.sprint.mission.discodeit.repository.MessageRepository; import com.sprint.mission.discodeit.repository.ReadStatusRepository; -import com.sprint.mission.discodeit.repository.UserRepository; import com.sprint.mission.discodeit.service.ChannelService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import java.time.Instant; import java.util.*; -import java.util.stream.Collectors; @Service @RequiredArgsConstructor @@ -25,90 +19,97 @@ public class BasicChannelService implements ChannelService { private final MessageRepository messageRepository; @Override - public Channel createPublicChannel(PublicChannelDto publicChannelDto) { - Channel channel = new Channel(publicChannelDto.type(), publicChannelDto.name(), publicChannelDto.description()); + public Channel create(PublicChannelCreateRequest request) { + String name = request.name(); + String description = request.description(); + Channel channel = new Channel(ChannelType.PUBLIC, name, description); + return channelRepository.save(channel); } @Override - public Channel createPrivateChannel(PrivateChannelDto privateChannelDto) { - Channel channel = new Channel(privateChannelDto.type(), privateChannelDto.name(), privateChannelDto.description()); - Channel saved = channelRepository.save(channel); - privateChannelDto.userIdList() - .forEach(u->{ - ReadStatus readStatus = new ReadStatus(u, saved.getId()); - readStatusRepository.save(readStatus); - }); - - return saved; + public Channel create(PrivateChannelCreateRequest request) { + Channel channel = new Channel(ChannelType.PRIVATE, null, null); + Channel createdChannel = channelRepository.save(channel); + + request.participantIds().stream() + .map(userId -> new ReadStatus(userId, createdChannel.getId(), Instant.MIN)) + .forEach(readStatusRepository::save); + + return createdChannel; } @Override - public ChannelFindResDto find(UUID channelId) { - Channel channel = channelRepository.findById(channelId) - .orElseThrow(() -> new IllegalArgumentException("Can't find channel")); - List messages = messageRepository.findAll().stream() - .filter(m->m.getChannelId().equals(channelId)).toList(); - Instant lastMessageTime = messages.stream() - .map(Message::getCreatedAt) - .max(Comparator.naturalOrder()).orElse(null); - if(channel.getType().equals(ChannelType.PRIVATE)){ - List userIdList = readStatusRepository.findUserIdByChannelId(channelId); - return new ChannelFindResDto(channel, lastMessageTime, userIdList); - } - else{ - return new ChannelFindResDto(channel, lastMessageTime, null); - } - + public ChannelDto find(UUID channelId) { + return channelRepository.findById(channelId) + .map(this::toDto) + .orElseThrow(() -> new NoSuchElementException("Channel with id " + channelId + " not found")); } @Override - public List findAllByUserId(UUID userId) { - List publicChannelIdList = channelRepository.findAll().stream() - .filter(c->c.getType().equals(ChannelType.PUBLIC)) - .map(Channel::getId) + public List findAllByUserId(UUID userId) { + List mySubscribedChannelIds = readStatusRepository.findAllByUserId(userId).stream() + .map(ReadStatus::getChannelId) .toList(); - List myPrivateChannelIdList = readStatusRepository.findChannelIdByUserId(userId); - List myPrivateChannelOnlyList = myPrivateChannelIdList.stream() - .filter(i->!publicChannelIdList.contains(i)) + + return channelRepository.findAll().stream() + .filter(channel -> + channel.getType().equals(ChannelType.PUBLIC) + || mySubscribedChannelIds.contains(channel.getId()) + ) + .map(this::toDto) .toList(); - List result = new ArrayList<>(); - for(UUID u:publicChannelIdList){ - result.add(find(u)); - } - for(UUID u:myPrivateChannelOnlyList){ - result.add(find(u)); - } - return result; } @Override - public Channel update(ChannelUpdateDto channelUpdateDto) { - Channel channel = channelRepository.findById(channelUpdateDto.channelId()) - .orElseThrow(() -> new NoSuchElementException("Channel with id " + channelUpdateDto.channelId() + " not found")); - if(channel.getType().equals(ChannelType.PRIVATE)) { - return channelRepository.save(channel); + public Channel update(UUID channelId, PublicChannelUpdateRequest request) { + String newName = request.newName(); + String newDescription = request.newDescription(); + Channel channel = channelRepository.findById(channelId) + .orElseThrow(() -> new NoSuchElementException("Channel with id " + channelId + " not found")); + if (channel.getType().equals(ChannelType.PRIVATE)) { + throw new IllegalArgumentException("Private channel cannot be updated"); } - channel.update(channelUpdateDto.newName(), channelUpdateDto.newDescription()); + channel.update(newName, newDescription); return channelRepository.save(channel); } @Override public void delete(UUID channelId) { - if (!channelRepository.existsById(channelId)) { - throw new NoSuchElementException("Channel with id " + channelId + " not found"); - } - List targetReadStatus = readStatusRepository.findAll().stream() - .filter(r->r.getChannelId().equals(channelId)) - .toList(); - for(ReadStatus r:targetReadStatus){ - readStatusRepository.deleteById(r.getId()); - } - List targetMessage = messageRepository.findAll().stream() - .filter(m->m.getChannelId().equals(channelId)).toList(); - for(Message m:targetMessage){ - messageRepository.deleteById(m.getId()); - } + Channel channel = channelRepository.findById(channelId) + .orElseThrow(() -> new NoSuchElementException("Channel with id " + channelId + " not found")); + + messageRepository.deleteAllByChannelId(channel.getId()); + readStatusRepository.deleteAllByChannelId(channel.getId()); + channelRepository.deleteById(channelId); } + + private ChannelDto toDto(Channel channel) { + Instant lastMessageAt = messageRepository.findAllByChannelId(channel.getId()) + .stream() + .sorted(Comparator.comparing(Message::getCreatedAt).reversed()) + .map(Message::getCreatedAt) + .limit(1) + .findFirst() + .orElse(Instant.MIN); + + List participantIds = new ArrayList<>(); + if (channel.getType().equals(ChannelType.PRIVATE)) { + readStatusRepository.findAllByChannelId(channel.getId()) + .stream() + .map(ReadStatus::getUserId) + .forEach(participantIds::add); + } + + return new ChannelDto( + channel.getId(), + channel.getType(), + channel.getName(), + channel.getDescription(), + participantIds, + lastMessageAt + ); + } + } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicMessageService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicMessageService.java index 3e34947a..04b926ea 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicMessageService.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicMessageService.java @@ -1,8 +1,8 @@ package com.sprint.mission.discodeit.service.basic; -import com.sprint.mission.discodeit.dto.BinaryContentCreateDto; -import com.sprint.mission.discodeit.dto.MessageCreateDto; -import com.sprint.mission.discodeit.dto.MessageUpdateDto; +import com.sprint.mission.discodeit.dto.BinaryContentCreateReqDto; +import com.sprint.mission.discodeit.dto.MessageCreateReqDto; +import com.sprint.mission.discodeit.dto.MessageUpdateReqDto; import com.sprint.mission.discodeit.entity.BinaryContent; import com.sprint.mission.discodeit.entity.Message; import com.sprint.mission.discodeit.repository.BinaryContentRepository; @@ -27,27 +27,36 @@ public class BasicMessageService implements MessageService { private final BinaryContentRepository binaryContentRepository; @Override - public Message create(MessageCreateDto messageCreateDto) { - if (!channelRepository.existsById(messageCreateDto.channelId())) { - throw new NoSuchElementException("Channel not found with id " + messageCreateDto.channelId()); + public Message create(MessageCreateRequest messageCreateRequest, List binaryContentCreateRequests) { + UUID channelId = messageCreateRequest.channelId(); + UUID authorId = messageCreateRequest.authorId(); + + if (!channelRepository.existsById(channelId)) { + throw new NoSuchElementException("Channel with id " + channelId + " does not exist"); } - if (!userRepository.existsById(messageCreateDto.authorId())) { - throw new NoSuchElementException("Author not found with id " + messageCreateDto.authorId()); + if (!userRepository.existsById(authorId)) { + throw new NoSuchElementException("Author with id " + authorId + " does not exist"); } - List binaryContent = messageCreateDto.files(); - if(binaryContent != null){ - for(BinaryContent b:binaryContent){ - BinaryContentCreateDto dto = new BinaryContentCreateDto(b.getFileName(),b.getContentType(),b.getSize()); - binaryContentRepository.save(dto); - } - List binaryContentIdList = binaryContent.stream() - .map(BinaryContent::getId) - .toList(); - Message message = new Message(messageCreateDto.content(), messageCreateDto.channelId(), messageCreateDto.authorId(), binaryContentIdList); - } - Message message = new Message(messageCreateDto.content(), messageCreateDto.channelId(), messageCreateDto.authorId(), null); + List attachmentIds = binaryContentCreateRequests.stream() + .map(attachmentRequest -> { + String fileName = attachmentRequest.fileName(); + String contentType = attachmentRequest.contentType(); + byte[] bytes = attachmentRequest.bytes(); + + BinaryContent binaryContent = new BinaryContent(fileName, (long) bytes.length, contentType, bytes); + BinaryContent createdBinaryContent = binaryContentRepository.save(binaryContent); + return createdBinaryContent.getId(); + }) + .toList(); + String content = messageCreateRequest.content(); + Message message = new Message( + content, + channelId, + authorId, + attachmentIds + ); return messageRepository.save(message); } @@ -59,28 +68,28 @@ public Message find(UUID messageId) { @Override public List findAllByChannelId(UUID channelId) { - return messageRepository.findAll().stream() - .filter(m -> m.getChannelId().equals(channelId)) + return messageRepository.findAllByChannelId(channelId).stream() .toList(); } @Override - public Message update(MessageUpdateDto messageUpdateDto) { - Message message = messageRepository.findById(messageUpdateDto.messageId()) - .orElseThrow(() -> new NoSuchElementException("Message with id " + messageUpdateDto.messageId() + " not found")); - message.update(messageUpdateDto.newContent()); + public Message update(UUID messageId, MessageUpdateRequest request) { + String newContent = request.newContent(); + Message message = messageRepository.findById(messageId) + .orElseThrow(() -> new NoSuchElementException("Message with id " + messageId + " not found")); + message.update(newContent); return messageRepository.save(message); } @Override public void delete(UUID messageId) { - if (!messageRepository.existsById(messageId)) { - throw new NoSuchElementException("Message with id " + messageId + " not found"); - } - List attachmentIdList = messageRepository.findById(messageId).get().getAttachmentIds(); - for(UUID u:attachmentIdList){ - binaryContentRepository.deleteById(u); - } + Message message = messageRepository.findById(messageId) + .orElseThrow(() -> new NoSuchElementException("Message with id " + messageId + " not found")); + + message.getAttachmentIds() + .forEach(binaryContentRepository::deleteById); + messageRepository.deleteById(messageId); } + } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicReadStatusService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicReadStatusService.java new file mode 100644 index 00000000..fe56b0d5 --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicReadStatusService.java @@ -0,0 +1,72 @@ +package com.sprint.mission.discodeit.service.basic; + +import com.sprint.mission.discodeit.entity.ReadStatus; +import com.sprint.mission.discodeit.repository.ChannelRepository; +import com.sprint.mission.discodeit.repository.ReadStatusRepository; +import com.sprint.mission.discodeit.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.NoSuchElementException; +import java.util.UUID; + +@Service +@RequiredArgsConstructor +public class BasicReadStatusService { + private final ReadStatusRepository readStatusRepository; + private final UserRepository userRepository; + private final ChannelRepository channelRepository; + + @Override + public ReadStatus create(ReadStatusCreateRequest request) { + UUID userId = request.userId(); + UUID channelId = request.channelId(); + + if (!userRepository.existsById(userId)) { + throw new NoSuchElementException("User with id " + userId + " does not exist"); + } + if (!channelRepository.existsById(channelId)) { + throw new NoSuchElementException("Channel with id " + channelId + " does not exist"); + } + if (readStatusRepository.findAllByUserId(userId).stream() + .anyMatch(readStatus -> readStatus.getChannelId().equals(channelId))) { + throw new IllegalArgumentException("ReadStatus with userId " + userId + " and channelId " + channelId + " already exists"); + } + + Instant lastReadAt = request.lastReadAt(); + ReadStatus readStatus = new ReadStatus(userId, channelId, lastReadAt); + return readStatusRepository.save(readStatus); + } + + @Override + public ReadStatus find(UUID readStatusId) { + return readStatusRepository.findById(readStatusId) + .orElseThrow(() -> new NoSuchElementException("ReadStatus with id " + readStatusId + " not found")); + } + + @Override + public List findAllByUserId(UUID userId) { + return readStatusRepository.findAllByUserId(userId).stream() + .toList(); + } + + @Override + public ReadStatus update(UUID readStatusId, ReadStatusUpdateRequest request) { + Instant newLastReadAt = request.newLastReadAt(); + ReadStatus readStatus = readStatusRepository.findById(readStatusId) + .orElseThrow(() -> new NoSuchElementException("ReadStatus with id " + readStatusId + " not found")); + readStatus.update(newLastReadAt); + return readStatusRepository.save(readStatus); + } + + @Override + public void delete(UUID readStatusId) { + if (!readStatusRepository.existsById(readStatusId)) { + throw new NoSuchElementException("ReadStatus with id " + readStatusId + " not found"); + } + readStatusRepository.deleteById(readStatusId); + } + + +} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserService.java index d0bbd1d1..98eb44ad 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserService.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserService.java @@ -1,9 +1,8 @@ package com.sprint.mission.discodeit.service.basic; -import com.sprint.mission.discodeit.dto.BinaryContentCreateDto; -import com.sprint.mission.discodeit.dto.UserCreateDto; -import com.sprint.mission.discodeit.dto.UserFindResDto; -import com.sprint.mission.discodeit.dto.UserUpdateDto; +import com.sprint.mission.discodeit.dto.BinaryContentCreateReqDto; +import com.sprint.mission.discodeit.dto.UserCreateReqDto; +import com.sprint.mission.discodeit.dto.UserUpdateReqDto; import com.sprint.mission.discodeit.entity.BinaryContent; import com.sprint.mission.discodeit.entity.User; import com.sprint.mission.discodeit.entity.UserStatus; @@ -27,75 +26,112 @@ public class BasicUserService implements UserService { private final BinaryContentRepository binaryContentRepository; @Override - public User create(UserCreateDto userCreateDto) { - List allUser = userRepository.findAll(); - allUser.stream() - .filter(p -> p.getUsername().equals(userCreateDto.username())) - .findAny() - .ifPresent(p->{ - throw new IllegalArgumentException("Already existing user name: " + userCreateDto.username()); - }); - allUser.stream() - .filter(p -> p.getEmail().equals(userCreateDto.email())) - .findAny() - .ifPresent(p->{ - throw new IllegalArgumentException("Already existing email: " + userCreateDto.email()); - }); - User user = new User(userCreateDto.username(), userCreateDto.email(), userCreateDto.password()); - UserStatus userStatus = new UserStatus(user.getId()); - BinaryContentCreateDto binaryContentDto = userCreateDto.profile(); - userStatusRepository.save(userStatus); - if(binaryContentDto!=null){ - BinaryContent bc = binaryContentRepository.save(binaryContentDto); - user.setProfileId(bc.getId()); + public User create(UserCreateRequest userCreateRequest, Optional optionalProfileCreateRequest) { + String username = userCreateRequest.username(); + String email = userCreateRequest.email(); + + if (userRepository.existsByEmail(email)) { + throw new IllegalArgumentException("User with email " + email + " already exists"); } - return userRepository.save(user); + if (userRepository.existsByUsername(username)) { + throw new IllegalArgumentException("User with username " + username + " already exists"); + } + + UUID nullableProfileId = optionalProfileCreateRequest + .map(profileRequest -> { + String fileName = profileRequest.fileName(); + String contentType = profileRequest.contentType(); + byte[] bytes = profileRequest.bytes(); + BinaryContent binaryContent = new BinaryContent(fileName, (long)bytes.length, contentType, bytes); + return binaryContentRepository.save(binaryContent).getId(); + }) + .orElse(null); + String password = userCreateRequest.password(); + + User user = new User(username, email, password, nullableProfileId); + User createdUser = userRepository.save(user); + + Instant now = Instant.now(); + UserStatus userStatus = new UserStatus(createdUser.getId(), now); + userStatusRepository.save(userStatus); + + return createdUser; } @Override - public UserFindResDto find(UUID userId) { - User user = userRepository.findById(userId) + public UserDto find(UUID userId) { + return userRepository.findById(userId) + .map(this::toDto) .orElseThrow(() -> new NoSuchElementException("User with id " + userId + " not found")); - UserStatus userStatus = userStatusRepository.findByUserId(userId) - .orElseThrow(() -> new NoSuchElementException("User with id " + userId + " not found")); - return new UserFindResDto(user.getUsername(),user.getEmail(), userStatus.isOnline()); } @Override - public List findAll() { - List users = userRepository.findAll(); - List userStatuses = userStatusRepository.findAll(); - List results = new ArrayList<>(); - for(User u: users){ - UserStatus userStatus = userStatuses.stream() - .filter(us -> us.getUserId().equals(u.getId())) - .findAny() - .orElseThrow(() -> new NoSuchElementException("User not found")); - UserFindResDto data = new UserFindResDto(u.getUsername(), u.getEmail(), userStatus.isOnline()); - results.add(data); - } - return results; + public List findAll() { + return userRepository.findAll() + .stream() + .map(this::toDto) + .toList(); } @Override - public User update(UserUpdateDto userDto) { - User user = userRepository.findById(userDto.userId()) - .orElseThrow(() -> new NoSuchElementException("User with id " + userDto.userId() + " not found")); - user.update(userDto.username(), userDto.email(), userDto.password()); + public User update(UUID userId, UserUpdateRequest userUpdateRequest, Optional optionalProfileCreateRequest) { + User user = userRepository.findById(userId) + .orElseThrow(() -> new NoSuchElementException("User with id " + userId + " not found")); + + String newUsername = userUpdateRequest.newUsername(); + String newEmail = userUpdateRequest.newEmail(); + if (userRepository.existsByEmail(newEmail)) { + throw new IllegalArgumentException("User with email " + newEmail + " already exists"); + } + if (userRepository.existsByUsername(newUsername)) { + throw new IllegalArgumentException("User with username " + newUsername + " already exists"); + } + + UUID nullableProfileId = optionalProfileCreateRequest + .map(profileRequest -> { + Optional.ofNullable(user.getProfileId()) + .ifPresent(binaryContentRepository::deleteById); + + String fileName = profileRequest.fileName(); + String contentType = profileRequest.contentType(); + byte[] bytes = profileRequest.bytes(); + BinaryContent binaryContent = new BinaryContent(fileName, (long) bytes.length, contentType, bytes); + return binaryContentRepository.save(binaryContent).getId(); + }) + .orElse(null); + + String newPassword = userUpdateRequest.newPassword(); + user.update(newUsername, newEmail, newPassword, nullableProfileId); + return userRepository.save(user); } @Override public void delete(UUID userId) { - if (!userRepository.existsById(userId)) { - throw new NoSuchElementException("User with id " + userId + " not found"); - } - if(userRepository.findById(userId).isPresent()){ - UUID profileId = userRepository.findById(userId).get().getProfileId(); - binaryContentRepository.deleteById(profileId); - userStatusRepository.deleteByUserId(userId); - userRepository.deleteById(userId); - } + User user = userRepository.findById(userId) + .orElseThrow(() -> new NoSuchElementException("User with id " + userId + " not found")); + Optional.ofNullable(user.getProfileId()) + .ifPresent(binaryContentRepository::deleteById); + userStatusRepository.deleteByUserId(userId); + + userRepository.deleteById(userId); } + + private UserDto toDto(User user) { + Boolean online = userStatusRepository.findByUserId(user.getId()) + .map(UserStatus::isOnline) + .orElse(null); + + return new UserDto( + user.getId(), + user.getCreatedAt(), + user.getUpdatedAt(), + user.getUsername(), + user.getEmail(), + user.getProfileId(), + online + ); + } + } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserStatusService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserStatusService.java new file mode 100644 index 00000000..bd35a8ba --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserStatusService.java @@ -0,0 +1,78 @@ +package com.sprint.mission.discodeit.service.basic; + +import com.sprint.mission.discodeit.entity.UserStatus; +import com.sprint.mission.discodeit.repository.UserRepository; +import com.sprint.mission.discodeit.repository.UserStatusRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.NoSuchElementException; +import java.util.UUID; + +@Service +@RequiredArgsConstructor +public class BasicUserStatusService { + private final UserStatusRepository userStatusRepository; + private final UserRepository userRepository; + + @Override + public UserStatus create(UserStatusCreateRequest request) { + UUID userId = request.userId(); + + if (!userRepository.existsById(userId)) { + throw new NoSuchElementException("User with id " + userId + " does not exist"); + } + if (userStatusRepository.findByUserId(userId).isPresent()) { + throw new IllegalArgumentException("UserStatus with id " + userId + " already exists"); + } + + Instant lastActiveAt = request.lastActiveAt(); + UserStatus userStatus = new UserStatus(userId, lastActiveAt); + return userStatusRepository.save(userStatus); + } + + @Override + public UserStatus find(UUID userStatusId) { + return userStatusRepository.findById(userStatusId) + .orElseThrow(() -> new NoSuchElementException("UserStatus with id " + userStatusId + " not found")); + } + + @Override + public List findAll() { + return userStatusRepository.findAll().stream() + .toList(); + } + + @Override + public UserStatus update(UUID userStatusId, UserStatusUpdateRequest request) { + Instant newLastActiveAt = request.newLastActiveAt(); + + UserStatus userStatus = userStatusRepository.findById(userStatusId) + .orElseThrow(() -> new NoSuchElementException("UserStatus with id " + userStatusId + " not found")); + userStatus.update(newLastActiveAt); + + return userStatusRepository.save(userStatus); + } + + @Override + public UserStatus updateByUserId(UUID userId, UserStatusUpdateRequest request) { + Instant newLastActiveAt = request.newLastActiveAt(); + + UserStatus userStatus = userStatusRepository.findByUserId(userId) + .orElseThrow(() -> new NoSuchElementException("UserStatus with userId " + userId + " not found")); + userStatus.update(newLastActiveAt); + + return userStatusRepository.save(userStatus); + } + + @Override + public void delete(UUID userStatusId) { + if (!userStatusRepository.existsById(userStatusId)) { + throw new NoSuchElementException("UserStatus with id " + userStatusId + " not found"); + } + userStatusRepository.deleteById(userStatusId); + } + + +} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BinaryContentService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BinaryContentService.java deleted file mode 100644 index 5684024b..00000000 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BinaryContentService.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.sprint.mission.discodeit.service.basic; - -import com.sprint.mission.discodeit.dto.BinaryContentCreateDto; -import com.sprint.mission.discodeit.entity.BinaryContent; -import com.sprint.mission.discodeit.repository.BinaryContentRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -import java.util.*; - -@Service -@RequiredArgsConstructor -public class BinaryContentService { - private final BinaryContentRepository binaryContentRepository; - - public BinaryContent create(BinaryContentCreateDto dto){ - return binaryContentRepository.save(dto); - } - - public Optional find(UUID id){ - return binaryContentRepository.findById(id); - } - - public List findAllByIdIn(List idList){ - List fileList = new ArrayList<>(); - for(UUID id : idList){ - if(binaryContentRepository.findById(id).isPresent()) { - fileList.add(binaryContentRepository.findById(id).get()); - } - } - return fileList; - } - - public void delete(UUID id){ - if(!binaryContentRepository.existsById(id)){ - throw new NoSuchElementException("Can't find file"); - } - binaryContentRepository.deleteById(id); - - } -} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/ReadStatusService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/ReadStatusService.java deleted file mode 100644 index 9ba0cfa0..00000000 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/ReadStatusService.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.sprint.mission.discodeit.service.basic; - -import com.sprint.mission.discodeit.dto.ReadStatusCreateDto; -import com.sprint.mission.discodeit.dto.ReadStatusUpdateDto; -import com.sprint.mission.discodeit.entity.ReadStatus; -import com.sprint.mission.discodeit.repository.ChannelRepository; -import com.sprint.mission.discodeit.repository.ReadStatusRepository; -import com.sprint.mission.discodeit.repository.UserRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -import java.util.List; -import java.util.NoSuchElementException; -import java.util.UUID; - -@Service -@RequiredArgsConstructor -public class ReadStatusService { - private final ReadStatusRepository readStatusRepository; - private final UserRepository userRepository; - private final ChannelRepository channelRepository; - - public ReadStatus create(ReadStatusCreateDto dto){ - if(!channelRepository.existsById(dto.channelId())){ - throw new NoSuchElementException("Can't find channel"); - } - if(!userRepository.existsById(dto.userId())){ - throw new NoSuchElementException("Can't find user"); - } - if(readStatusRepository.findByChannelAndUser(dto.channelId(), dto.userId()).isPresent()){ - throw new IllegalArgumentException("Already existing read status"); - } - ReadStatus readStatus = new ReadStatus(dto.userId(), dto.channelId()); - return readStatusRepository.save(readStatus); - } - - public ReadStatus find(UUID id){ - if(readStatusRepository.findById(id).isEmpty()){ - throw new NoSuchElementException("Can't find read status"); - } - return readStatusRepository.findById(id).get(); - } - - public List findAllByUserId(UUID userId){ - return readStatusRepository.findAll().stream() - .filter(r -> r.getUserId().equals(userId)) - .toList(); - } - - public ReadStatus update(ReadStatusUpdateDto dto){ - if(readStatusRepository.findById(dto.id()).isEmpty()){ - throw new NoSuchElementException("Can't find read status"); - } - ReadStatus readStatus = readStatusRepository.findById(dto.id()).get(); - readStatus.update(dto.isRead()); - return readStatusRepository.save(readStatus); - } - - public void delete(UUID id){ - if(!readStatusRepository.existsById(id)){ - throw new NoSuchElementException("Can't find read status"); - } - readStatusRepository.deleteById(id); - } - -} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/UserStatusService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/UserStatusService.java deleted file mode 100644 index 4af35e08..00000000 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/UserStatusService.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.sprint.mission.discodeit.service.basic; - -import com.sprint.mission.discodeit.dto.UserStatusCreateDto; -import com.sprint.mission.discodeit.dto.UserStatusUpdateDto; -import com.sprint.mission.discodeit.entity.ReadStatus; -import com.sprint.mission.discodeit.entity.UserStatus; -import com.sprint.mission.discodeit.repository.UserRepository; -import com.sprint.mission.discodeit.repository.UserStatusRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -import java.util.List; -import java.util.NoSuchElementException; -import java.util.UUID; - -@Service -@RequiredArgsConstructor -public class UserStatusService { - private final UserStatusRepository userStatusRepository; - private final UserRepository userRepository; - - public UserStatus create(UserStatusCreateDto dto){ - if(!userRepository.existsById(dto.userId())){ - throw new NoSuchElementException("Can't find user"); - } - if(userStatusRepository.findByUserId(dto.userId()).isPresent()){ - throw new IllegalArgumentException("Already exist"); - } - UserStatus userStatus = new UserStatus(dto.userId()); - return userStatusRepository.save(userStatus); - } - - public UserStatus find(UUID id){ - return userStatusRepository.findById(id) - .orElseThrow(() -> new NoSuchElementException("User status with id " + id + " not found")); - } - - public List findAll(){ - return userStatusRepository.findAll(); - } - - public UserStatus update(UserStatusUpdateDto dto){ - UserStatus userStatus = userStatusRepository.findById(dto.id()) - .orElseThrow(() -> new NoSuchElementException("User status with id " + dto.id() + " not found")); - userStatus.update(); - return userStatusRepository.save(userStatus); - } - - public UserStatus updateByUserId(UUID userId, UserStatusUpdateDto dto){ - UserStatus userStatus = userStatusRepository.findByUserId(userId) - .orElseThrow(() -> new NoSuchElementException("User status with id " + userId + " not found")); - userStatus.update(); - return userStatusRepository.save(userStatus); - } - - public void delete(UUID id){ - if(!userStatusRepository.existsById(id)){ - throw new NoSuchElementException("Can't find user status"); - } - userStatusRepository.deleteById(id); - - } - -} From 63fcb4acd4aac245d005c2e01f76a8e7ff470318 Mon Sep 17 00:00:00 2001 From: Color-Marker Date: Mon, 9 Feb 2026 18:58:54 +0900 Subject: [PATCH 16/20] =?UTF-8?q?feat:=20=EC=BB=A8=ED=8A=B8=EB=A1=A4?= =?UTF-8?q?=EB=9F=AC=20=EC=B6=94=EA=B0=80=20=EC=99=84=EB=A3=8C=20auth,=20b?= =?UTF-8?q?inarycontent,=20user=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC?= =?UTF-8?q?=EC=97=90=20=EB=8C=80=ED=95=9C=20postman=EC=9D=80=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EB=B0=8F=20=EC=9E=91=EB=8F=99=20=ED=99=95=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../discodeit/DiscodeitApplication.java | 13 +-- .../mission/discodeit/JavaApplication.java | 84 -------------- .../discodeit/controller/AuthController.java | 28 +++++ .../controller/BinaryContentController.java | 28 +++++ .../controller/ChannelController.java | 65 +++++++++++ .../controller/MessageController.java | 72 ++++++++++++ .../controller/ReadStatusController.java | 43 +++++++ .../discodeit/controller/UserController.java | 106 ++++++++++++++++++ .../discodeit/entity/BinaryContent.java | 4 +- .../basic/BasicBinaryContentService.java | 7 +- .../service/basic/BasicChannelService.java | 10 +- .../service/basic/BasicMessageService.java | 4 +- .../service/basic/BasicReadStatusService.java | 10 +- .../service/basic/BasicUserService.java | 17 ++- .../service/basic/BasicUserStatusService.java | 12 +- discodeit/src/main/resources/application.yaml | 7 ++ 16 files changed, 390 insertions(+), 120 deletions(-) delete mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/JavaApplication.java create mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/controller/AuthController.java create mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/controller/BinaryContentController.java create mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/controller/ChannelController.java create mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/controller/MessageController.java create mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/controller/ReadStatusController.java create mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/controller/UserController.java diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/DiscodeitApplication.java b/discodeit/src/main/java/com/sprint/mission/discodeit/DiscodeitApplication.java index b8ba8e73..a0dcddbc 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/DiscodeitApplication.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/DiscodeitApplication.java @@ -20,18 +20,7 @@ public class DiscodeitApplication { public static void main(String[] args) { - ConfigurableApplicationContext context = SpringApplication.run(DiscodeitApplication.class, args); - - // 서비스 초기화 - UserService userService; - ChannelService channelService; - MessageService messageService; - - // TODO context에서 Bean을 조회하여 각 서비스 구현테 할당 코드 작성하기 - userService = context.getBean(UserService.class); - channelService = context.getBean(ChannelService.class); - messageService = context.getBean(MessageService.class); - + SpringApplication.run(DiscodeitApplication.class, args); } } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/JavaApplication.java b/discodeit/src/main/java/com/sprint/mission/discodeit/JavaApplication.java deleted file mode 100644 index 77696511..00000000 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/JavaApplication.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.sprint.mission.discodeit; - -import com.sprint.mission.discodeit.dto.*; -import com.sprint.mission.discodeit.entity.*; -import com.sprint.mission.discodeit.repository.*; -import com.sprint.mission.discodeit.repository.jcf.*; -import com.sprint.mission.discodeit.service.ChannelService; -import com.sprint.mission.discodeit.service.MessageService; -import com.sprint.mission.discodeit.service.UserService; -import com.sprint.mission.discodeit.service.basic.BasicChannelService; -import com.sprint.mission.discodeit.service.basic.BasicMessageService; -import com.sprint.mission.discodeit.service.basic.BasicUserService; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -public class JavaApplication { - static User setupUser(UserService userService) { - BinaryContentCreateReqDto file1 = new BinaryContentCreateReqDto("file1","txt",40L); - BinaryContentCreateReqDto file2 = new BinaryContentCreateReqDto("file2","txt",40L); - UserCreateReqDto userCreateReqDto1 = new UserCreateReqDto("admin", "admin@codeit.com", "admin1234", file1); - UserCreateReqDto userCreateReqDto2 = new UserCreateReqDto("woody", "woody@codeit.com", "woody1234", file2); - User user1 = userService.create(userCreateReqDto1); - User user2 = userService.create(userCreateReqDto2); - - System.out.println("user1: " + user1.getUsername() + "_" + user1.getId()); - System.out.println("user2: " + user2.getUsername() + "_" + user2.getId()); - - return user1; - } - - static Channel setupPublicChannel(ChannelService channelService) { - PublicChannelDto publicChannelDto = new PublicChannelDto(ChannelType.PUBLIC, "public", "This is public channel."); - return channelService.createPublicChannel(publicChannelDto); - } - - static Channel setupPrivateChannel(ChannelService channelService, List userIdList) { - PrivateChannelDto privateChannelDto = new PrivateChannelDto(ChannelType.PRIVATE, "private", "This is private channel", userIdList); - return channelService.createPrivateChannel(privateChannelDto); - } - - static void messageCreateTest(MessageService messageService, Channel channel, User author) { - MessageCreateReqDto messageCreateReqDto = new MessageCreateReqDto("Hello World", channel.getId(), author.getId(), null); - Message message = messageService.create(messageCreateReqDto); - System.out.println("Message created: " + message.getContent()); - System.out.println("In channel: " + message.getChannelId()); - System.out.println("By user: " + message.getAuthorId()); - System.out.println(); - } - - public static void main(String[] args) { - // 레포지토리 초기화 - UserRepository userRepository = new JCFUserRepository(); - ChannelRepository channelRepository = new JCFChannelRepository(); - MessageRepository messageRepository = new JCFMessageRepository(); - BinaryContentRepository binaryContentRepository = new JCFBinaryContentRepository(); - ReadStatusRepository readStatusRepository = new JCFReadStatusRepository(); - UserStatusRepository userStatusRepository = new JCFUserStatusRepository(); - - // 서비스 초기화a - UserService userService = new BasicUserService(userRepository, userStatusRepository, binaryContentRepository); - ChannelService channelService = new BasicChannelService(channelRepository,readStatusRepository, messageRepository); - MessageService messageService = new BasicMessageService(messageRepository, channelRepository, userRepository, binaryContentRepository); - - // 셋업 - User user = setupUser(userService); - System.out.println(); - - List userList = new ArrayList<>(); - userList.add(user); - List userIdList = userList.stream() - .map(User::getId).toList(); - Channel publicChannel = setupPublicChannel(channelService); - System.out.println("Public Channel: " + publicChannel.getName() + "_" + publicChannel.getId()); - Channel privateChannel = setupPrivateChannel(channelService, userIdList); - System.out.println("Private Channel: " + privateChannel.getName() + "_" + privateChannel.getId()); - System.out.println(); - - // 테스트 - messageCreateTest(messageService, publicChannel, user); - messageCreateTest(messageService, privateChannel, user); - } -} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/controller/AuthController.java b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/AuthController.java new file mode 100644 index 00000000..d658efcb --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/AuthController.java @@ -0,0 +1,28 @@ +package com.sprint.mission.discodeit.controller; + +import com.sprint.mission.discodeit.dto.UserLoginReqDto; +import com.sprint.mission.discodeit.entity.User; +import com.sprint.mission.discodeit.service.AuthService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +@Controller +@RequiredArgsConstructor +public class AuthController { + private final AuthService authService; + + @GetMapping("/login") + public ResponseEntity login( + @RequestParam String username, + @RequestParam String password + ){ + UserLoginReqDto dto = new UserLoginReqDto(username, password); + User user = authService.login(dto); + return ResponseEntity.ok(user); + } +} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/controller/BinaryContentController.java b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/BinaryContentController.java new file mode 100644 index 00000000..059727c6 --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/BinaryContentController.java @@ -0,0 +1,28 @@ +package com.sprint.mission.discodeit.controller; + +import com.sprint.mission.discodeit.entity.BinaryContent; +import com.sprint.mission.discodeit.service.BinaryContentService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@Controller +@RequiredArgsConstructor +public class BinaryContentController { + private final BinaryContentService binaryContentService; + + @GetMapping("/files") + public ResponseEntity viewFiles( + @RequestParam List fileIdList + ){ + List fileList = binaryContentService.findAllByIdIn(fileIdList); + return ResponseEntity.ok(fileList); + } +} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/controller/ChannelController.java b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/ChannelController.java new file mode 100644 index 00000000..1a19c7f3 --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/ChannelController.java @@ -0,0 +1,65 @@ +package com.sprint.mission.discodeit.controller; + +import com.sprint.mission.discodeit.dto.PrivateChannelCreateReqDto; +import com.sprint.mission.discodeit.dto.PublicChannelCreateReqDto; +import com.sprint.mission.discodeit.dto.PublicChannelUpdateReqDto; +import com.sprint.mission.discodeit.dto.data.ChannelDto; +import com.sprint.mission.discodeit.entity.Channel; +import com.sprint.mission.discodeit.service.ChannelService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.UUID; + +@Controller +@RequestMapping("/channel") +@RequiredArgsConstructor +public class ChannelController { + private final ChannelService channelService; + + @PostMapping("/public/create") + public ResponseEntity publicCreate(@RequestBody PublicChannelCreateReqDto dto){ + Channel channel = channelService.create(dto); + ChannelDto result = channelService.find(channel.getId()); + return ResponseEntity.ok(result); + } + + @PostMapping("/private/create") + public ResponseEntity privateCreate(@RequestBody PrivateChannelCreateReqDto dto){ + Channel channel = channelService.create(dto); + ChannelDto result = channelService.find(channel.getId()); + return ResponseEntity.ok(result); + } + + @PatchMapping("/public/edit/{channelId}") + public ResponseEntity edit( + @PathVariable UUID channelId, + @RequestBody PublicChannelUpdateReqDto dto + ){ + channelService.update(channelId, dto); + ChannelDto result = channelService.find(channelId); + return ResponseEntity.ok(result); + } + + @DeleteMapping("/delete/{channelId}") + public ResponseEntity delete( + @PathVariable UUID channelId + ){ + channelService.delete(channelId); + return ResponseEntity.ok("channel: " + channelId + "가 삭제되었습니다."); + + } + + @GetMapping("/list/{userId}") + public ResponseEntity channelList( + @PathVariable UUID userId + ){ + List channelList = channelService.findAllByUserId(userId); + return ResponseEntity.ok(channelList); + } + + +} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/controller/MessageController.java b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/MessageController.java new file mode 100644 index 00000000..fef4be6d --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/MessageController.java @@ -0,0 +1,72 @@ +package com.sprint.mission.discodeit.controller; + +import com.sprint.mission.discodeit.dto.BinaryContentCreateReqDto; +import com.sprint.mission.discodeit.dto.MessageCreateReqDto; +import com.sprint.mission.discodeit.dto.MessageUpdateReqDto; +import com.sprint.mission.discodeit.dto.UserCreateReqDto; +import com.sprint.mission.discodeit.entity.Message; +import com.sprint.mission.discodeit.service.MessageService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@Controller +@RequestMapping("/message") +@RequiredArgsConstructor +public class MessageController { + private final MessageService messageService; + + @PostMapping( + path = "/create", + consumes = MediaType.MULTIPART_FORM_DATA_VALUE + ) + public ResponseEntity create( + @RequestPart("messageCreateReqDto") MessageCreateReqDto messageCreateReqDto, + @RequestPart("files") List files + ){ + List binaryDtos = new ArrayList<>(); + try { + for(MultipartFile file: files){ + BinaryContentCreateReqDto data = new BinaryContentCreateReqDto(file.getName(), file.getContentType(), file.getBytes()); + binaryDtos.add(data); + } + } catch (IOException e) { + throw new RuntimeException("파일을 읽을 수 없습니다."); + } + Message message = messageService.create(messageCreateReqDto, binaryDtos); + return ResponseEntity.ok(message); + } + + @PatchMapping("/edit/{messageId}") + public ResponseEntity edit( + @PathVariable UUID messageId, + @RequestBody MessageUpdateReqDto dto + ){ + Message message = messageService.update(messageId,dto); + return ResponseEntity.ok(message); + } + + @DeleteMapping("/delete/{messageId}") + public ResponseEntity delete( + @PathVariable UUID messageId + ){ + messageService.delete(messageId); + return ResponseEntity.ok("message: " + messageId + "가 삭제되었습니다."); + } + + @GetMapping("/list/{channelId}") + public ResponseEntity messageList( + @PathVariable UUID channelId + ){ + List messages = messageService.findAllByChannelId(channelId); + return ResponseEntity.ok(messages); + } +} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/controller/ReadStatusController.java b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/ReadStatusController.java new file mode 100644 index 00000000..ea11fad5 --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/ReadStatusController.java @@ -0,0 +1,43 @@ +package com.sprint.mission.discodeit.controller; + +import com.sprint.mission.discodeit.dto.ReadStatusCreateReqDto; +import com.sprint.mission.discodeit.dto.ReadStatusUpdateReqDto; +import com.sprint.mission.discodeit.entity.ReadStatus; +import com.sprint.mission.discodeit.service.ReadStatusService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.UUID; + +@Controller +@RequestMapping("/readStatus") +@RequiredArgsConstructor +public class ReadStatusController { + private final ReadStatusService readStatusService; + + @PostMapping("/create") + public ResponseEntity create(@RequestBody ReadStatusCreateReqDto dto){ + ReadStatus readStatus = readStatusService.create(dto); + return ResponseEntity.ok(readStatus); + } + + @PatchMapping("/edit/{statusId}") + public ResponseEntity edit( + @PathVariable UUID statusId, + @RequestBody ReadStatusUpdateReqDto dto + ){ + ReadStatus readStatus = readStatusService.update(statusId, dto); + return ResponseEntity.ok(readStatus); + } + + @GetMapping("/list/{userId}") + public ResponseEntity statusList( + @PathVariable UUID userId + ){ + List statusList = readStatusService.findAllByUserId(userId); + return ResponseEntity.ok(statusList); + } +} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/controller/UserController.java b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/UserController.java new file mode 100644 index 00000000..c2d348a5 --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/UserController.java @@ -0,0 +1,106 @@ +package com.sprint.mission.discodeit.controller; + +import com.sprint.mission.discodeit.dto.BinaryContentCreateReqDto; +import com.sprint.mission.discodeit.dto.UserCreateReqDto; +import com.sprint.mission.discodeit.dto.UserStatusUpdateReqDto; +import com.sprint.mission.discodeit.dto.UserUpdateReqDto; +import com.sprint.mission.discodeit.dto.data.UserDto; +import com.sprint.mission.discodeit.entity.User; +import com.sprint.mission.discodeit.entity.UserStatus; +import com.sprint.mission.discodeit.service.UserService; +import com.sprint.mission.discodeit.service.UserStatusService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +@Controller +@RequestMapping("/user") +@RequiredArgsConstructor +public class UserController { + private final UserService userService; + private final UserStatusService userStatusService; + + @PostMapping( + path = "/create", + consumes = MediaType.MULTIPART_FORM_DATA_VALUE + ) + public ResponseEntity create( + @RequestPart("userCreateReqDto") UserCreateReqDto userCreateReqDto, + @RequestPart(value = "profile", required = false) MultipartFile profile + ){ + Optional binaryDto = Optional.ofNullable(profile) + .filter(file -> !file.isEmpty()) + .map(file -> { + try { + return new BinaryContentCreateReqDto( + file.getOriginalFilename(), + file.getContentType(), + file.getBytes() + ); + } catch (IOException e) { + throw new RuntimeException("프로필 파일을 읽을 수 없습니다.", e); + } + }); + User user = userService.create(userCreateReqDto, binaryDto); + UserDto result = userService.find(user.getId()); + return ResponseEntity.ok(result); + } + + @PatchMapping( + path = "/edit/{userId}", + consumes = MediaType.MULTIPART_FORM_DATA_VALUE + ) + public ResponseEntity edit( + @PathVariable UUID userId, + @RequestPart("userUpdateReqDto") UserUpdateReqDto userDto, + @RequestPart(value = "profile", required = false) MultipartFile profile + ){ + Optional binaryDto = Optional.ofNullable(profile) + .filter(file -> !file.isEmpty()) + .map(file -> { + try { + return new BinaryContentCreateReqDto( + file.getOriginalFilename(), + file.getContentType(), + file.getBytes() + ); + } catch (IOException e) { + throw new RuntimeException("프로필 파일을 읽을 수 없습니다.", e); + } + }); + userService.update(userId, userDto, binaryDto); + UserDto result = userService.find(userId); + return ResponseEntity.ok(result); + } + + @DeleteMapping("/delete/{userId}") + public ResponseEntity delete( + @PathVariable UUID userId + ){ + userService.delete(userId); + return ResponseEntity.ok("user: " + userId + "가 삭제되었습니다."); + } + + @GetMapping("/list") + public ResponseEntity userList(){ + List allUsers = userService.findAll(); + return ResponseEntity.ok(allUsers); + } + + @PatchMapping("/update/{userId}") + public ResponseEntity update( + @PathVariable UUID userId, + @RequestBody UserStatusUpdateReqDto dto + ){ + UserStatus userStatus = userStatusService.updateByUserId(userId, dto); + return ResponseEntity.ok(userStatus); + } +} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/entity/BinaryContent.java b/discodeit/src/main/java/com/sprint/mission/discodeit/entity/BinaryContent.java index 0fb87dd3..94e175cf 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/entity/BinaryContent.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/entity/BinaryContent.java @@ -16,13 +16,15 @@ public class BinaryContent implements Serializable { private final Instant createdAt; private String fileName; + private Long size; private String contentType; private byte[] bytes; - public BinaryContent(String fileName,String contentType, byte[] bytes) { + public BinaryContent(String fileName,Long size, String contentType, byte[] bytes) { this.id = UUID.randomUUID(); this.createdAt = Instant.now(); this.fileName = fileName; + this.size = size; this.contentType = contentType; this.bytes = bytes; } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicBinaryContentService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicBinaryContentService.java index f8adaa49..9b034cf7 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicBinaryContentService.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicBinaryContentService.java @@ -1,7 +1,9 @@ package com.sprint.mission.discodeit.service.basic; +import com.sprint.mission.discodeit.dto.BinaryContentCreateReqDto; import com.sprint.mission.discodeit.entity.BinaryContent; import com.sprint.mission.discodeit.repository.BinaryContentRepository; +import com.sprint.mission.discodeit.service.BinaryContentService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -9,11 +11,12 @@ @Service @RequiredArgsConstructor -public class BasicBinaryContentService { +public class BasicBinaryContentService implements BinaryContentService { private final BinaryContentRepository binaryContentRepository; @Override - public BinaryContent create(BinaryContentCreateRequest request) { + public BinaryContent create( + BinaryContentCreateReqDto request) { String fileName = request.fileName(); byte[] bytes = request.bytes(); String contentType = request.contentType(); diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicChannelService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicChannelService.java index 51ffe9ba..beca861d 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicChannelService.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicChannelService.java @@ -1,5 +1,9 @@ package com.sprint.mission.discodeit.service.basic; +import com.sprint.mission.discodeit.dto.PrivateChannelCreateReqDto; +import com.sprint.mission.discodeit.dto.PublicChannelCreateReqDto; +import com.sprint.mission.discodeit.dto.PublicChannelUpdateReqDto; +import com.sprint.mission.discodeit.dto.data.ChannelDto; import com.sprint.mission.discodeit.entity.*; import com.sprint.mission.discodeit.repository.ChannelRepository; import com.sprint.mission.discodeit.repository.MessageRepository; @@ -19,7 +23,7 @@ public class BasicChannelService implements ChannelService { private final MessageRepository messageRepository; @Override - public Channel create(PublicChannelCreateRequest request) { + public Channel create(PublicChannelCreateReqDto request) { String name = request.name(); String description = request.description(); Channel channel = new Channel(ChannelType.PUBLIC, name, description); @@ -28,7 +32,7 @@ public Channel create(PublicChannelCreateRequest request) { } @Override - public Channel create(PrivateChannelCreateRequest request) { + public Channel create(PrivateChannelCreateReqDto request) { Channel channel = new Channel(ChannelType.PRIVATE, null, null); Channel createdChannel = channelRepository.save(channel); @@ -62,7 +66,7 @@ public List findAllByUserId(UUID userId) { } @Override - public Channel update(UUID channelId, PublicChannelUpdateRequest request) { + public Channel update(UUID channelId, PublicChannelUpdateReqDto request) { String newName = request.newName(); String newDescription = request.newDescription(); Channel channel = channelRepository.findById(channelId) diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicMessageService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicMessageService.java index 04b926ea..124fd6e7 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicMessageService.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicMessageService.java @@ -27,7 +27,7 @@ public class BasicMessageService implements MessageService { private final BinaryContentRepository binaryContentRepository; @Override - public Message create(MessageCreateRequest messageCreateRequest, List binaryContentCreateRequests) { + public Message create(MessageCreateReqDto messageCreateRequest, List binaryContentCreateRequests) { UUID channelId = messageCreateRequest.channelId(); UUID authorId = messageCreateRequest.authorId(); @@ -73,7 +73,7 @@ public List findAllByChannelId(UUID channelId) { } @Override - public Message update(UUID messageId, MessageUpdateRequest request) { + public Message update(UUID messageId, MessageUpdateReqDto request) { String newContent = request.newContent(); Message message = messageRepository.findById(messageId) .orElseThrow(() -> new NoSuchElementException("Message with id " + messageId + " not found")); diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicReadStatusService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicReadStatusService.java index fe56b0d5..933ef259 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicReadStatusService.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicReadStatusService.java @@ -1,25 +1,29 @@ package com.sprint.mission.discodeit.service.basic; +import com.sprint.mission.discodeit.dto.ReadStatusCreateReqDto; +import com.sprint.mission.discodeit.dto.ReadStatusUpdateReqDto; import com.sprint.mission.discodeit.entity.ReadStatus; import com.sprint.mission.discodeit.repository.ChannelRepository; import com.sprint.mission.discodeit.repository.ReadStatusRepository; import com.sprint.mission.discodeit.repository.UserRepository; +import com.sprint.mission.discodeit.service.ReadStatusService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.time.Instant; import java.util.List; import java.util.NoSuchElementException; import java.util.UUID; @Service @RequiredArgsConstructor -public class BasicReadStatusService { +public class BasicReadStatusService implements ReadStatusService { private final ReadStatusRepository readStatusRepository; private final UserRepository userRepository; private final ChannelRepository channelRepository; @Override - public ReadStatus create(ReadStatusCreateRequest request) { + public ReadStatus create(ReadStatusCreateReqDto request) { UUID userId = request.userId(); UUID channelId = request.channelId(); @@ -52,7 +56,7 @@ public List findAllByUserId(UUID userId) { } @Override - public ReadStatus update(UUID readStatusId, ReadStatusUpdateRequest request) { + public ReadStatus update(UUID readStatusId, ReadStatusUpdateReqDto request) { Instant newLastReadAt = request.newLastReadAt(); ReadStatus readStatus = readStatusRepository.findById(readStatusId) .orElseThrow(() -> new NoSuchElementException("ReadStatus with id " + readStatusId + " not found")); diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserService.java index 98eb44ad..6c34fb3d 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserService.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserService.java @@ -3,6 +3,7 @@ import com.sprint.mission.discodeit.dto.BinaryContentCreateReqDto; import com.sprint.mission.discodeit.dto.UserCreateReqDto; import com.sprint.mission.discodeit.dto.UserUpdateReqDto; +import com.sprint.mission.discodeit.dto.data.UserDto; import com.sprint.mission.discodeit.entity.BinaryContent; import com.sprint.mission.discodeit.entity.User; import com.sprint.mission.discodeit.entity.UserStatus; @@ -13,10 +14,8 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import java.util.ArrayList; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.UUID; +import java.time.Instant; +import java.util.*; @Service @RequiredArgsConstructor @@ -26,7 +25,7 @@ public class BasicUserService implements UserService { private final BinaryContentRepository binaryContentRepository; @Override - public User create(UserCreateRequest userCreateRequest, Optional optionalProfileCreateRequest) { + public User create(UserCreateReqDto userCreateRequest, Optional optionalProfileCreateRequest) { String username = userCreateRequest.username(); String email = userCreateRequest.email(); @@ -74,12 +73,12 @@ public List findAll() { } @Override - public User update(UUID userId, UserUpdateRequest userUpdateRequest, Optional optionalProfileCreateRequest) { + public User update(UUID userId, UserUpdateReqDto userUpdateRequest, Optional optionalProfileCreateRequest) { User user = userRepository.findById(userId) .orElseThrow(() -> new NoSuchElementException("User with id " + userId + " not found")); - String newUsername = userUpdateRequest.newUsername(); - String newEmail = userUpdateRequest.newEmail(); + String newUsername = userUpdateRequest.username(); + String newEmail = userUpdateRequest.email(); if (userRepository.existsByEmail(newEmail)) { throw new IllegalArgumentException("User with email " + newEmail + " already exists"); } @@ -100,7 +99,7 @@ public User update(UUID userId, UserUpdateRequest userUpdateRequest, Optional findAll() { } @Override - public UserStatus update(UUID userStatusId, UserStatusUpdateRequest request) { + public UserStatus update(UUID userStatusId, UserStatusUpdateReqDto request) { Instant newLastActiveAt = request.newLastActiveAt(); UserStatus userStatus = userStatusRepository.findById(userStatusId) @@ -56,7 +60,7 @@ public UserStatus update(UUID userStatusId, UserStatusUpdateRequest request) { } @Override - public UserStatus updateByUserId(UUID userId, UserStatusUpdateRequest request) { + public UserStatus updateByUserId(UUID userId, UserStatusUpdateReqDto request) { Instant newLastActiveAt = request.newLastActiveAt(); UserStatus userStatus = userStatusRepository.findByUserId(userId) diff --git a/discodeit/src/main/resources/application.yaml b/discodeit/src/main/resources/application.yaml index 4f20840d..c1e5f1d8 100644 --- a/discodeit/src/main/resources/application.yaml +++ b/discodeit/src/main/resources/application.yaml @@ -2,3 +2,10 @@ discodeit: repository: type: file # jcf | file file-directory: .discodeit + + servlet: + multipart: + max-file-size: 10MB # 개별 파일 당 최대 용량 임시 설정 + max-request-size: 10MB + + From aae0a3c86d17c8c1ada866a0e0be1c925ba3953e Mon Sep 17 00:00:00 2001 From: Color-Marker Date: Tue, 10 Feb 2026 11:24:35 +0900 Subject: [PATCH 17/20] =?UTF-8?q?feat:=20postman=EA=B3=BC=20=EC=BB=A8?= =?UTF-8?q?=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=EC=97=B0=EA=B2=B0=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C=20=EB=B0=8F=20=EC=9E=91=EB=8F=99=20=ED=99=95=EC=9D=B8?= =?UTF-8?q?=20=EC=99=84=EB=A3=8C.=20=EC=A0=9C=EA=B3=B5=EB=90=9C=20?= =?UTF-8?q?=ED=94=84=EB=A1=A0=ED=8A=B8=20=EC=BD=94=EB=93=9C=EC=99=80=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0=20=EC=99=84=EB=A3=8C.=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EC=8B=9C=20=EC=98=A8=EB=9D=BC=EC=9D=B8=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20update=EB=90=98=EA=B2=8C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../discodeit/controller/AuthController.java | 6 +- .../controller/BinaryContentController.java | 12 ++- .../controller/ChannelController.java | 2 +- .../controller/MessageController.java | 2 +- .../controller/ReadStatusController.java | 2 +- .../discodeit/controller/UserController.java | 6 +- .../dto/PrivateChannelCreateReqDto.java | 4 +- .../service/basic/BasicAuthService.java | 8 ++ .../service/basic/BasicChannelService.java | 4 +- discodeit/src/main/resources/static/.DS_Store | Bin 0 -> 6148 bytes discodeit/src/main/resources/static/script.js | 67 +++++++++++++++ .../src/main/resources/static/styles.css | 80 ++++++++++++++++++ .../src/main/resources/static/user-list.html | 18 ++++ 13 files changed, 198 insertions(+), 13 deletions(-) create mode 100644 discodeit/src/main/resources/static/.DS_Store create mode 100644 discodeit/src/main/resources/static/script.js create mode 100644 discodeit/src/main/resources/static/styles.css create mode 100644 discodeit/src/main/resources/static/user-list.html diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/controller/AuthController.java b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/AuthController.java index d658efcb..8c09e120 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/controller/AuthController.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/AuthController.java @@ -6,12 +6,10 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.*; @Controller +@RequestMapping("/api/auth") @RequiredArgsConstructor public class AuthController { private final AuthService authService; diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/controller/BinaryContentController.java b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/BinaryContentController.java index 059727c6..ff3b6939 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/controller/BinaryContentController.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/BinaryContentController.java @@ -7,6 +7,7 @@ import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import java.util.ArrayList; @@ -14,15 +15,24 @@ import java.util.UUID; @Controller +@RequestMapping("/api/binaryContent") @RequiredArgsConstructor public class BinaryContentController { private final BinaryContentService binaryContentService; - @GetMapping("/files") + @GetMapping("/findAll") public ResponseEntity viewFiles( @RequestParam List fileIdList ){ List fileList = binaryContentService.findAllByIdIn(fileIdList); return ResponseEntity.ok(fileList); } + + @GetMapping("/find") + public ResponseEntity viewFile( + @RequestParam UUID binaryContentId + ){ + BinaryContent file = binaryContentService.find(binaryContentId); + return ResponseEntity.ok(file); + } } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/controller/ChannelController.java b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/ChannelController.java index 1a19c7f3..b50aa12e 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/controller/ChannelController.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/ChannelController.java @@ -15,7 +15,7 @@ import java.util.UUID; @Controller -@RequestMapping("/channel") +@RequestMapping("/api/channel") @RequiredArgsConstructor public class ChannelController { private final ChannelService channelService; diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/controller/MessageController.java b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/MessageController.java index fef4be6d..f9191810 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/controller/MessageController.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/MessageController.java @@ -19,7 +19,7 @@ import java.util.UUID; @Controller -@RequestMapping("/message") +@RequestMapping("/api/message") @RequiredArgsConstructor public class MessageController { private final MessageService messageService; diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/controller/ReadStatusController.java b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/ReadStatusController.java index ea11fad5..bd9c257c 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/controller/ReadStatusController.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/ReadStatusController.java @@ -13,7 +13,7 @@ import java.util.UUID; @Controller -@RequestMapping("/readStatus") +@RequestMapping("/api/readStatus") @RequiredArgsConstructor public class ReadStatusController { private final ReadStatusService readStatusService; diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/controller/UserController.java b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/UserController.java index c2d348a5..d3aa5677 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/controller/UserController.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/UserController.java @@ -22,7 +22,7 @@ import java.util.UUID; @Controller -@RequestMapping("/user") +@RequestMapping("/api/user") @RequiredArgsConstructor public class UserController { private final UserService userService; @@ -89,8 +89,8 @@ public ResponseEntity delete( return ResponseEntity.ok("user: " + userId + "가 삭제되었습니다."); } - @GetMapping("/list") - public ResponseEntity userList(){ + @GetMapping("/findAll") + public ResponseEntity> userList(){ List allUsers = userService.findAll(); return ResponseEntity.ok(allUsers); } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/PrivateChannelCreateReqDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/PrivateChannelCreateReqDto.java index 0d368ef1..752d8360 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/PrivateChannelCreateReqDto.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/PrivateChannelCreateReqDto.java @@ -4,5 +4,7 @@ import java.util.UUID; public record PrivateChannelCreateReqDto( - List participantIds + List participantIds, + String name, + String description ) {} diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicAuthService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicAuthService.java index d664d9fe..d0dafd16 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicAuthService.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicAuthService.java @@ -2,17 +2,21 @@ import com.sprint.mission.discodeit.dto.UserLoginReqDto; import com.sprint.mission.discodeit.entity.User; +import com.sprint.mission.discodeit.entity.UserStatus; import com.sprint.mission.discodeit.repository.UserRepository; +import com.sprint.mission.discodeit.repository.UserStatusRepository; import com.sprint.mission.discodeit.service.AuthService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.time.Instant; import java.util.NoSuchElementException; @RequiredArgsConstructor @Service public class BasicAuthService implements AuthService { private final UserRepository userRepository; + private final UserStatusRepository userStatusRepository; @Override public User login(UserLoginReqDto loginRequest) { @@ -25,6 +29,10 @@ public User login(UserLoginReqDto loginRequest) { if (!user.getPassword().equals(password)) { throw new IllegalArgumentException("Wrong password"); } + UserStatus userStatus = userStatusRepository.findByUserId(user.getId()) + .orElseThrow(() -> new NoSuchElementException("해당 유저의 상태를 찾을 수 없습니다.")); + userStatus.update(Instant.now()); + userStatusRepository.save(userStatus); return user; } diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicChannelService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicChannelService.java index beca861d..ed175690 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicChannelService.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicChannelService.java @@ -33,7 +33,9 @@ public Channel create(PublicChannelCreateReqDto request) { @Override public Channel create(PrivateChannelCreateReqDto request) { - Channel channel = new Channel(ChannelType.PRIVATE, null, null); + String name = request.name(); + String description = request.description(); + Channel channel = new Channel(ChannelType.PRIVATE, name, description); Channel createdChannel = channelRepository.save(channel); request.participantIds().stream() diff --git a/discodeit/src/main/resources/static/.DS_Store b/discodeit/src/main/resources/static/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 { + fetchAndRenderUsers(); +}); + +// Fetch users from the API +async function fetchAndRenderUsers() { + try { + const response = await fetch(ENDPOINTS.USERS); + if (!response.ok) throw new Error('Failed to fetch users'); + const users = await response.json(); + renderUserList(users); + } catch (error) { + console.error('Error fetching users:', error); + } +} + +// Fetch user profile image +async function fetchUserProfile(profileId) { + try { + const response = await fetch(`${ENDPOINTS.BINARY_CONTENT}?binaryContentId=${profileId}`); + if (!response.ok) throw new Error('Failed to fetch profile'); + const profile = await response.json(); + + // Convert base64 encoded bytes to data URL + return `data:${profile.contentType};base64,${profile.bytes}`; + } catch (error) { + console.error('Error fetching profile:', error); + return '/default-avatar.png'; // Fallback to default avatar + } +} + +// Render user list +async function renderUserList(users) { + const userListElement = document.getElementById('userList'); + userListElement.innerHTML = ''; // Clear existing content + + for (const user of users) { + const userElement = document.createElement('div'); + userElement.className = 'user-item'; + + // Get profile image URL + const profileUrl = user.profileId ? + await fetchUserProfile(user.profileId) : + '/default-avatar.png'; + + userElement.innerHTML = ` + ${user.username} + +
+ ${user.online ? '온라인' : '오프라인'} +
+ `; + + userListElement.appendChild(userElement); + } +} \ No newline at end of file diff --git a/discodeit/src/main/resources/static/styles.css b/discodeit/src/main/resources/static/styles.css new file mode 100644 index 00000000..b45f4e70 --- /dev/null +++ b/discodeit/src/main/resources/static/styles.css @@ -0,0 +1,80 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: Arial, sans-serif; + background-color: #f5f5f5; +} + +.container { + max-width: 800px; + margin: 0 auto; + padding: 20px; +} + +h1 { + text-align: center; + margin-bottom: 30px; + color: #333; +} + +.user-list { + background-color: white; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.user-item { + display: flex; + align-items: center; + padding: 20px; + border-bottom: 1px solid #eee; +} + +.user-item:last-child { + border-bottom: none; +} + +.user-avatar { + width: 60px; + height: 60px; + border-radius: 50%; + margin-right: 20px; + object-fit: cover; +} + +.user-info { + flex-grow: 1; +} + +.user-name { + font-size: 18px; + font-weight: bold; + color: #333; + margin-bottom: 5px; +} + +.user-email { + font-size: 14px; + color: #666; +} + +.status-badge { + padding: 6px 12px; + border-radius: 20px; + font-size: 14px; + font-weight: bold; +} + +.online { + background-color: #4CAF50; + color: white; +} + +.offline { + background-color: #9e9e9e; + color: white; +} \ No newline at end of file diff --git a/discodeit/src/main/resources/static/user-list.html b/discodeit/src/main/resources/static/user-list.html new file mode 100644 index 00000000..f3acfdb5 --- /dev/null +++ b/discodeit/src/main/resources/static/user-list.html @@ -0,0 +1,18 @@ + + + + + + 사용자 목록 + + + +
+

사용자 목록

+
+ +
+
+ + + \ No newline at end of file From d24c3088a62b4f3a978a935e98ce363732d4f95c Mon Sep 17 00:00:00 2001 From: Color-Marker Date: Tue, 10 Feb 2026 11:35:47 +0900 Subject: [PATCH 18/20] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=8B=9C=20=EC=98=A8=EB=9D=BC=EC=9D=B8=20=EC=83=81=ED=83=9C=20?= =?UTF-8?q?=EC=A0=84=ED=99=98=20=EB=B0=A9=EC=8B=9D=EC=9D=84=20userStatusSe?= =?UTF-8?q?rvice=EC=9D=98=20=EB=A9=94=EC=86=8C=EB=93=9C=EB=A5=BC=20?= =?UTF-8?q?=EC=9D=B4=EC=9A=A9=ED=95=98=EB=8A=94=20=EB=B0=A9=EC=8B=9D?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=A0=84=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../discodeit/service/basic/BasicAuthService.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicAuthService.java b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicAuthService.java index d0dafd16..a16d646a 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicAuthService.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicAuthService.java @@ -1,11 +1,13 @@ package com.sprint.mission.discodeit.service.basic; import com.sprint.mission.discodeit.dto.UserLoginReqDto; +import com.sprint.mission.discodeit.dto.UserStatusUpdateReqDto; import com.sprint.mission.discodeit.entity.User; import com.sprint.mission.discodeit.entity.UserStatus; import com.sprint.mission.discodeit.repository.UserRepository; import com.sprint.mission.discodeit.repository.UserStatusRepository; import com.sprint.mission.discodeit.service.AuthService; +import com.sprint.mission.discodeit.service.UserStatusService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -16,7 +18,7 @@ @Service public class BasicAuthService implements AuthService { private final UserRepository userRepository; - private final UserStatusRepository userStatusRepository; + private final UserStatusService userStatusService; @Override public User login(UserLoginReqDto loginRequest) { @@ -29,10 +31,8 @@ public User login(UserLoginReqDto loginRequest) { if (!user.getPassword().equals(password)) { throw new IllegalArgumentException("Wrong password"); } - UserStatus userStatus = userStatusRepository.findByUserId(user.getId()) - .orElseThrow(() -> new NoSuchElementException("해당 유저의 상태를 찾을 수 없습니다.")); - userStatus.update(Instant.now()); - userStatusRepository.save(userStatus); + UserStatusUpdateReqDto dto = new UserStatusUpdateReqDto(Instant.now()); + userStatusService.updateByUserId(user.getId(), dto); return user; } From 4ec08f6440a47eff6e62ac8adf4cc962172bf74a Mon Sep 17 00:00:00 2001 From: Color-Marker Date: Tue, 10 Feb 2026 16:22:53 +0900 Subject: [PATCH 19/20] =?UTF-8?q?feat:=20exceptionHandler=20@controllerAdv?= =?UTF-8?q?ice=20=EC=9D=B4=EC=9A=A9=ED=95=B4=EC=84=9C=20=EC=88=98=EC=97=85?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EB=B0=B0=EC=9A=B4=EB=8C=80=EB=A1=9C=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=ED=95=B4=20=EB=B4=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/GlobalExceptionHandler.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 discodeit/src/main/java/com/sprint/mission/discodeit/controller/GlobalExceptionHandler.java diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/controller/GlobalExceptionHandler.java b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/GlobalExceptionHandler.java new file mode 100644 index 00000000..dbdd6ed3 --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/GlobalExceptionHandler.java @@ -0,0 +1,34 @@ +package com.sprint.mission.discodeit.controller; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.util.NoSuchElementException; + +@ControllerAdvice +@ResponseBody +public class GlobalExceptionHandler { + @ExceptionHandler(IllegalArgumentException.class) + public ResponseEntity handleException(IllegalArgumentException e){ + return ResponseEntity + .status(HttpStatus.BAD_REQUEST) + .body(e.getMessage()); + } + + @ExceptionHandler(NoSuchElementException.class) + public ResponseEntity handleException(NoSuchElementException e){ + return ResponseEntity + .status(HttpStatus.NOT_FOUND) + .body(e.getMessage()); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity handleException(Exception e){ + return ResponseEntity + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(e.getMessage()); + } +} From b3be71e2599c913e7ccbdf338cf3b0290c4c568f Mon Sep 17 00:00:00 2001 From: Color-Marker Date: Sat, 14 Feb 2026 23:08:50 +0900 Subject: [PATCH 20/20] =?UTF-8?q?add:=20postman=20=EC=BB=AC=EB=A0=89?= =?UTF-8?q?=EC=85=98=20=EC=A0=95=EB=B3=B4=20=EC=B6=94=EA=B0=80=20=EC=97=85?= =?UTF-8?q?=EB=A1=9C=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/PR_SPRINT_4.md | 13 +- .../sprint-mission-4.postman_collection.json | 581 ++++++++++++++++++ 2 files changed, 584 insertions(+), 10 deletions(-) create mode 100644 .github/sprint-mission-4.postman_collection.json diff --git a/.github/PR_SPRINT_4.md b/.github/PR_SPRINT_4.md index db047f46..2c9df89d 100644 --- a/.github/PR_SPRINT_4.md +++ b/.github/PR_SPRINT_4.md @@ -9,16 +9,9 @@ - [x] 심화 항목 1 - [x] 심화 항목 2 -## 기본 구조 -- -- ## 주요 변경사항 -- +- controller를 통해 postman과 작동 확인 +- postman의 컬렉션 정보 json형식으로 export하여 .github 폴더에 추가하여 두었습니다. +- 예외를 GlobalExceptionHandler로 처리해보았습니다. -## 스크린샷 -![사진설명](사진링크) - -## 멘토에게 -- -- \ No newline at end of file diff --git a/.github/sprint-mission-4.postman_collection.json b/.github/sprint-mission-4.postman_collection.json new file mode 100644 index 00000000..d858ff97 --- /dev/null +++ b/.github/sprint-mission-4.postman_collection.json @@ -0,0 +1,581 @@ +{ + "info": { + "_postman_id": "50209e81-74b0-4eab-afd7-f7aedc35dd0a", + "name": "sprint-mission-4", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "52123480", + "_collection_link": "https://go.postman.co/collection/52123480-50209e81-74b0-4eab-afd7-f7aedc35dd0a?source=collection_link" + }, + "item": [ + { + "name": "user", + "item": [ + { + "name": "create", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "userCreateReqDto", + "value": "{\"username\": \"kkk\",\n\"email\": \"kkk@codeit.com\",\n\"password\": \"admin\"}", + "type": "text", + "uuid": "a508c30b-d515-41d2-bdb2-6bac10ee0296", + "contentType": "application/json" + }, + { + "key": "profile", + "type": "file", + "uuid": "6f43a7b0-271a-4955-bc21-ec3e5ab5401b", + "src": "/C:/Users/ASUS/Desktop/잡다/그림/짤/HANWZHgaAAEHVa5.jpg" + } + ] + }, + "url": { + "raw": "{{base_url}}/api/user/create", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "user", + "create" + ] + } + }, + "response": [] + }, + { + "name": "edit", + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "userUpdateReqDto", + "value": "{\"username\":\"master\",\n\"email\":\"master@codeit.com\",\n\"password\":\"master\"}", + "type": "text", + "uuid": "0a0a75f0-b33e-4230-95d6-ef4c8bd27a93", + "contentType": "application/json" + } + ] + }, + "url": { + "raw": "{{base_url}}/api/user/edit/a913fde7-2147-4d73-8dba-3fc975e9120e", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "user", + "edit", + "a913fde7-2147-4d73-8dba-3fc975e9120e" + ] + } + }, + "response": [] + }, + { + "name": "delete", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{base_url}}/api/user/delete/68e277c5-22c0-4126-9818-a6841949ab16sdf", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "user", + "delete", + "68e277c5-22c0-4126-9818-a6841949ab16sdf" + ] + } + }, + "response": [] + }, + { + "name": "list", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/user/findAll", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "user", + "findAll" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "channel", + "item": [ + { + "name": "publicCreate", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"name\" : \"publicChannel\",\r\n \"description\": \"This is public channel\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}/api/channel/public/create", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "channel", + "public", + "create" + ] + } + }, + "response": [] + }, + { + "name": "privateCreate", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"participantIds\" : [\"8244f4ee-c584-4980-8427-127806b7fca3\",\"a913fde7-2147-4d73-8dba-3fc975e9120e\"],\r\n \"name\" : \"privateChannel\",\r\n \"description\" : \"This is private channel\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}/api/channel/private/create", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "channel", + "private", + "create" + ] + } + }, + "response": [] + }, + { + "name": "publicEdit", + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"newName\" : \"publicChannel\",\r\n \"newDescription\" : \"This is public channel\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}/api/channel/public/edit/773e89da-b8fa-4dda-8aac-6594bfc0369c", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "channel", + "public", + "edit", + "773e89da-b8fa-4dda-8aac-6594bfc0369c" + ] + } + }, + "response": [] + }, + { + "name": "delete", + "request": { + "method": "DELETE", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "userCreateReqDto", + "value": "{\"username\": \"admin\",\n\"email\": \"admin@codeit.com\",\n\"password\": \"master\"}", + "type": "text", + "uuid": "a508c30b-d515-41d2-bdb2-6bac10ee0296", + "contentType": "application/json" + }, + { + "key": "profile", + "type": "file", + "uuid": "6f43a7b0-271a-4955-bc21-ec3e5ab5401b", + "src": "/C:/Users/ASUS/Desktop/dev/web/codeit_sprint_bootcamp/icon.png" + } + ] + }, + "url": { + "raw": "{{base_url}}/api/channel/delete/f9a91514-74d4-4864-9f88-a18283715bc8", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "channel", + "delete", + "f9a91514-74d4-4864-9f88-a18283715bc8" + ] + } + }, + "response": [] + }, + { + "name": "list", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/channel/list/a913fde7-2147-4d73-8dba-3fc975e9120e", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "channel", + "list", + "a913fde7-2147-4d73-8dba-3fc975e9120e" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "message", + "item": [ + { + "name": "create", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "messageCreateReqDto", + "value": "{\"content\": \"gg\",\n\"channelId\": \"773e89da-b8fa-4dda-8aac-6594bfc0369c\",\n\"authorId\": \"47d171eb-0339-4d27-a10b-0b2c569da996\"}", + "type": "text", + "uuid": "a508c30b-d515-41d2-bdb2-6bac10ee0296", + "contentType": "application/json" + }, + { + "key": "files", + "type": "file", + "uuid": "6f43a7b0-271a-4955-bc21-ec3e5ab5401b", + "value": null + } + ] + }, + "url": { + "raw": "{{base_url}}/api/message/create", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "message", + "create" + ] + } + }, + "response": [] + }, + { + "name": "edit", + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"newContent\" : \"guest\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}/api/message/edit/764dc6bd-dc77-4175-b5db-6f7a19d15b72", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "message", + "edit", + "764dc6bd-dc77-4175-b5db-6f7a19d15b72" + ] + } + }, + "response": [] + }, + { + "name": "delete", + "request": { + "method": "DELETE", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "userCreateReqDto", + "value": "{\"username\": \"admin\",\n\"email\": \"admin@codeit.com\",\n\"password\": \"master\"}", + "type": "text", + "uuid": "a508c30b-d515-41d2-bdb2-6bac10ee0296", + "contentType": "application/json" + }, + { + "key": "profile", + "type": "file", + "uuid": "6f43a7b0-271a-4955-bc21-ec3e5ab5401b", + "src": "/C:/Users/ASUS/Desktop/dev/web/codeit_sprint_bootcamp/icon.png" + } + ] + }, + "url": { + "raw": "{{base_url}}/api/message/delete/21d31209-a335-4773-be70-af6620ac36ff", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "message", + "delete", + "21d31209-a335-4773-be70-af6620ac36ff" + ] + } + }, + "response": [] + }, + { + "name": "list", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/message/list/773e89da-b8fa-4dda-8aac-6594bfc0369c", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "message", + "list", + "773e89da-b8fa-4dda-8aac-6594bfc0369c" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "readStatus", + "item": [ + { + "name": "create", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const currentTime = new Date().toISOString();\r", + "pm.variables.set(\"current_timestamp\", currentTime);" + ], + "type": "text/javascript", + "packages": {}, + "requests": {} + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"channelId\": \"773e89da-b8fa-4dda-8aac-6594bfc0369c\",\r\n \"userId\": \"a913fde7-2147-4d73-8dba-3fc975e9120e\",\r\n \"lastReadAt\": \"{{current_timestamp}}\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}/api/readStatus/create", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "readStatus", + "create" + ] + } + }, + "response": [] + }, + { + "name": "edit", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "const currentTime = new Date().toISOString();\r", + "pm.variables.set(\"current_timestamp\", currentTime);" + ], + "type": "text/javascript", + "packages": {}, + "requests": {} + } + } + ], + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"newLastReadAt\" : \"{{current_timestamp}}\"\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}/api/readStatus/edit/daff9bdb-ded8-4145-b77a-52c36ada4684", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "readStatus", + "edit", + "daff9bdb-ded8-4145-b77a-52c36ada4684" + ] + } + }, + "response": [] + }, + { + "name": "list", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/readStatus/list/a913fde7-2147-4d73-8dba-3fc975e9120e", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "readStatus", + "list", + "a913fde7-2147-4d73-8dba-3fc975e9120e" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "others", + "item": [ + { + "name": "auth", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/auth/login?username=master&password=master", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "auth", + "login" + ], + "query": [ + { + "key": "username", + "value": "master" + }, + { + "key": "password", + "value": "master" + } + ] + } + }, + "response": [] + }, + { + "name": "binaryContent/findAll", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/binaryContent/findAll?fileIdList=b841bbcb-bf86-4807-a01d-89ef8d0e0c35, 13d608a1-01b3-43b7-95b0-8d4dda640ea2", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "binaryContent", + "findAll" + ], + "query": [ + { + "key": "fileIdList", + "value": "b841bbcb-bf86-4807-a01d-89ef8d0e0c35, 13d608a1-01b3-43b7-95b0-8d4dda640ea2" + } + ] + } + }, + "response": [] + }, + { + "name": "binaryContent/find", + "request": { + "method": "GET", + "header": [] + }, + "response": [] + } + ] + } + ] +} \ No newline at end of file