From 510b5167e4471b0c24f675fb556c80a84c4a87fb Mon Sep 17 00:00:00 2001 From: Jungwoo Kim Date: Mon, 5 Feb 2024 00:26:02 +0900 Subject: [PATCH 01/11] =?UTF-8?q?feat:=20nhn=20=ED=81=B4=EB=9D=BC=EC=9A=B0?= =?UTF-8?q?=EB=93=9C=EC=97=90=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=97=85?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=20=EB=B0=8F=20=EC=A1=B0=ED=9A=8C=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infra/nhn/s3/model/ChatUploadedImage.java | 26 +++++ .../nhn/s3/service/ChatFileUploadService.java | 64 +++++++++++++ .../s3/service/ChatImageUploadService.java | 76 +++++++++++++++ .../nhn/s3/service/ObjectStorageService.java | 96 +++++++++++++++++++ .../nhn/s3/service/ObjectUploadContext.java | 8 ++ 5 files changed, 270 insertions(+) create mode 100644 src/main/java/com/dku/council/infra/nhn/s3/model/ChatUploadedImage.java create mode 100644 src/main/java/com/dku/council/infra/nhn/s3/service/ChatFileUploadService.java create mode 100644 src/main/java/com/dku/council/infra/nhn/s3/service/ChatImageUploadService.java diff --git a/src/main/java/com/dku/council/infra/nhn/s3/model/ChatUploadedImage.java b/src/main/java/com/dku/council/infra/nhn/s3/model/ChatUploadedImage.java new file mode 100644 index 00000000..12c121a2 --- /dev/null +++ b/src/main/java/com/dku/council/infra/nhn/s3/model/ChatUploadedImage.java @@ -0,0 +1,26 @@ +package com.dku.council.infra.nhn.s3.model; + +import com.dku.council.domain.chat.model.FileType; +import lombok.Getter; + +@Getter +public class ChatUploadedImage { + + private final String roomId; + + private final Long userId; + + private final String fileName; + + private final String fileUrl; + + private final String fileType; + + public ChatUploadedImage(String roomId, Long userId, String fileUrl, ImageRequest image) { + this.roomId = roomId; + this.userId = userId; + this.fileName = image.getOriginalFilename(); + this.fileUrl = fileUrl; + this.fileType = FileType.IMAGE.toString(); + } +} diff --git a/src/main/java/com/dku/council/infra/nhn/s3/service/ChatFileUploadService.java b/src/main/java/com/dku/council/infra/nhn/s3/service/ChatFileUploadService.java new file mode 100644 index 00000000..b60bacfd --- /dev/null +++ b/src/main/java/com/dku/council/infra/nhn/s3/service/ChatFileUploadService.java @@ -0,0 +1,64 @@ +package com.dku.council.infra.nhn.s3.service; + +import com.dku.council.infra.nhn.global.exception.InvalidAccessObjectStorageException; +import com.dku.council.infra.nhn.global.service.service.NHNAuthService; +import com.dku.council.infra.nhn.s3.model.FileRequest; +import com.dku.council.infra.nhn.s3.model.UploadedFile; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class ChatFileUploadService { + + private final NHNAuthService nhnAuthService; + private final ObjectStorageService s3service; + private final ObjectUploadContext uploadContext; + + public Context newContext() { + String token = nhnAuthService.requestToken(); + return new Context(token); + } + + public class Context { + private final String token; + + private Context(String token) { this.token = token; } + + public ArrayList uploadChatFiles(List files, String roomId, String prefix) { + ArrayList chatFiles = new ArrayList<>(); + for (FileRequest req : files) { + chatFiles.add(uploadChatFile(req, roomId, prefix)); + } + return chatFiles; + } + + public UploadedFile uploadChatFile(FileRequest file, String roomId, String prefix) { + String originName = file.getOriginalFilename(); + if (originName == null) originName = ""; + + String ext = originName.substring(originName.lastIndexOf(".") + 1); + String fileId; + do { + fileId = uploadContext.makeFileId(prefix, ext); + } while (s3service.isInChatFilePath(roomId, fileId)); + return upload(file, roomId, fileId); + } + + private UploadedFile upload(FileRequest file, String roomId, String objectName) { + try { + s3service.uploadChatFile(token, roomId, objectName, file.getInputStream(), file.getContentType()); + return new UploadedFile(objectName, file); + } catch (Throwable e) { + throw new InvalidAccessObjectStorageException(e); + } + } + + public void deleteChatFile(String roomId, String fileUrl) { + s3service.deleteChatFileByDirectUrl(token, fileUrl); + } + } +} diff --git a/src/main/java/com/dku/council/infra/nhn/s3/service/ChatImageUploadService.java b/src/main/java/com/dku/council/infra/nhn/s3/service/ChatImageUploadService.java new file mode 100644 index 00000000..986a3387 --- /dev/null +++ b/src/main/java/com/dku/council/infra/nhn/s3/service/ChatImageUploadService.java @@ -0,0 +1,76 @@ +package com.dku.council.infra.nhn.s3.service; + +import com.dku.council.infra.nhn.global.exception.AlreadyInStorageException; +import com.dku.council.infra.nhn.global.service.service.NHNAuthService; +import com.dku.council.infra.nhn.s3.model.ImageRequest; +import com.dku.council.infra.nhn.s3.model.ChatUploadedImage; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class ChatImageUploadService { + + private final NHNAuthService nhnAuthService; + private final ObjectUploadContext uploadContext; + private final ObjectStorageService s3service; + + public Context newContext() { + String token = nhnAuthService.requestToken(); + return new Context(token); + } + + public class Context { + private final String token; + + private Context(String token) { this.token = token; } + + public ArrayList uploadChatImages(List images, String roomId, Long userId) { + ArrayList chatImages = new ArrayList<>(); + for (ImageRequest req : images) { + chatImages.add(uploadChatImage(req, roomId, userId)); + } + return chatImages; + } + + public ChatUploadedImage uploadChatImage(ImageRequest image, String roomId, Long userId) { + String originName = image.getOriginalFilename(); + if (originName == null) originName = ""; + + String prefix = originName.substring(0, originName.lastIndexOf(".")); + String ext = originName.substring(originName.lastIndexOf(".") + 1); + String imageId; + do { + imageId = uploadContext.makeImageId(prefix, ext); + } while (s3service.isInChatImagePath(roomId, imageId)); + return upload(image, roomId, userId, imageId); + } + + public ChatUploadedImage uploadChatImageWithName(ImageRequest image, String roomId, Long userId, String objectName) { + if (s3service.isInChatImagePath(roomId, objectName)) { + throw new AlreadyInStorageException(); + } + return upload(image, roomId, userId, objectName); + } + + private ChatUploadedImage upload(ImageRequest image, String roomId, Long userId, String objectName) { + try { + s3service.uploadChatImage(token, roomId, objectName, image.getInputStream(), image.getContentType()); + String chatImageUrl = uploadContext.getChatImageUrl(roomId, objectName); + + // TODO : 여기다가 ChatFile에 담는 서비스 로직 추가해주면될듯? + + return new ChatUploadedImage(roomId, userId, chatImageUrl, image); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + public void deleteChatImage(String fileUrl) { + s3service.deleteChatFileByDirectUrl(token, fileUrl); + } + } +} diff --git a/src/main/java/com/dku/council/infra/nhn/s3/service/ObjectStorageService.java b/src/main/java/com/dku/council/infra/nhn/s3/service/ObjectStorageService.java index 198a5d54..355da145 100644 --- a/src/main/java/com/dku/council/infra/nhn/s3/service/ObjectStorageService.java +++ b/src/main/java/com/dku/council/infra/nhn/s3/service/ObjectStorageService.java @@ -72,6 +72,43 @@ public boolean isInFilePath(String objectName) { return true; } + /** + * ~/chatroom/roomId/image/objectName 경로에 파일이 있는지 확인합니다. + */ + public boolean isInChatImagePath(String roomId, String objectName) { + try { + webClient.get() + .uri(uploadContext.getChatImageUrl(roomId, objectName)) + .retrieve() + .bodyToMono(byte[].class) + .block(); + } catch (WebClientResponseException e) { + if (e.getStatusCode() == HttpStatus.NOT_FOUND) { + return false; + } + } + return true; + } + + /** + * ~/chatroom/roomId/file/objectName 경로에 파일이 있는지 확인합니다. + */ + public boolean isInChatFilePath(String roomId, String objectName) { + try { + webClient.get() + .uri(uploadContext.getChatFileUrl(roomId, objectName)) + .retrieve() + .bodyToMono(byte[].class) + .block(); + } catch (WebClientResponseException e) { + if (e.getStatusCode() == HttpStatus.NOT_FOUND) { + return false; + } + } + return true; + } + + //TODO 기존 코드 삭제 예정 public void uploadObject(String tokenId, String objectName, final InputStream inputStream, @Nullable MediaType contentType) { try { @@ -136,6 +173,50 @@ public void uploadFile(String tokenId, String objectName, final InputStream inpu } } + /** + * ~/chatroom/roomId/image/objectName 경로에 파일을 업로드합니다. + */ + public void uploadChatImage(String tokenId, String roomId, String objectName, final InputStream inputStream, @Nullable MediaType contentType) { + try { + WebClient.RequestBodySpec spec = webClient.put() + .uri(uploadContext.getChatImageUrl(roomId, objectName)) + .header("X-Auth-Token", tokenId); + + if (contentType != null) { + spec = spec.header("Content-Type", contentType.toString()); + } + + spec.body(BodyInserters.fromResource(new InputStreamResource(inputStream))) + .retrieve() + .bodyToMono(Void.class) + .block(); + } catch (Throwable e) { + throw new InvalidAccessObjectStorageException(e); + } + } + + /** + * ~/chatroom/roomId/file/objectName 경로에 파일을 업로드합니다. + */ + public void uploadChatFile(String tokenId, String roomId, String objectName, final InputStream inputStream, @Nullable MediaType contentType) { + try { + WebClient.RequestBodySpec spec = webClient.put() + .uri(uploadContext.getChatFileUrl(roomId, objectName)) + .header("X-Auth-Token", tokenId); + + if (contentType != null) { + spec = spec.header("Content-Type", contentType.toString()); + } + + spec.body(BodyInserters.fromResource(new InputStreamResource(inputStream))) + .retrieve() + .bodyToMono(Void.class) + .block(); + } catch (Throwable e) { + throw new InvalidAccessObjectStorageException(e); + } + } + //TODO 기존 코드 삭제 예정 public void deleteObject(String tokenId, String objectName) { try { @@ -182,4 +263,19 @@ public void deleteFile(String tokenId, String objectName) { } } + /** + * 특정 채팅방에 존재하는 특정 파일을 삭제합니다. + */ + public void deleteChatFileByDirectUrl(String tokenId, String fileUrl) { + try { + webClient.delete() + .uri(fileUrl) + .header("X-Auth-Token", tokenId) + .retrieve() + .bodyToMono(Void.class) + .block(); + } catch (Throwable e) { + throw new InvalidAccessObjectStorageException(e); + } + } } diff --git a/src/main/java/com/dku/council/infra/nhn/s3/service/ObjectUploadContext.java b/src/main/java/com/dku/council/infra/nhn/s3/service/ObjectUploadContext.java index bc285907..ed2838c4 100644 --- a/src/main/java/com/dku/council/infra/nhn/s3/service/ObjectUploadContext.java +++ b/src/main/java/com/dku/council/infra/nhn/s3/service/ObjectUploadContext.java @@ -23,6 +23,11 @@ public class ObjectUploadContext { @Value("${app.post.thumbnail.default}") private final String defaultThumbnailId; + @Value("${nhn.os.api-chat-image-path}") + private final String apiChatImagePath; + + @Value("${nhn.os.api-chat-file-path}") + private final String apiChatFilePath; public String getThumbnailUrl(String thumbnailId) { if (thumbnailId == null || thumbnailId.isBlank()) { @@ -44,6 +49,9 @@ public String getFileUrl(String objectName) { return String.format(apiFilePath, objectName); } + public String getChatImageUrl(String roomId, String objectName) { return String.format(apiChatImagePath, roomId, objectName); } + public String getChatFileUrl(String roomId, String objectName) { return String.format(apiChatFilePath, roomId, objectName); } + public String makeObjectId(String prefix, String extension) { return makeObjName(prefix, UUID.randomUUID() + "." + extension); } From e9a0bf5893e580f97ff5b9f8c045d203d484db35 Mon Sep 17 00:00:00 2001 From: Jungwoo Kim Date: Mon, 5 Feb 2024 00:29:32 +0900 Subject: [PATCH 02/11] =?UTF-8?q?feat:=20=EA=B0=9D=EC=B2=B4=20=EB=8B=A4?= =?UTF-8?q?=EC=9A=B4=EB=A1=9C=EB=93=9C=20=EC=84=9C=EB=B9=84=EC=8A=A4=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nhn/s3/service/ObjectDownloadService.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/main/java/com/dku/council/infra/nhn/s3/service/ObjectDownloadService.java diff --git a/src/main/java/com/dku/council/infra/nhn/s3/service/ObjectDownloadService.java b/src/main/java/com/dku/council/infra/nhn/s3/service/ObjectDownloadService.java new file mode 100644 index 00000000..98998f50 --- /dev/null +++ b/src/main/java/com/dku/council/infra/nhn/s3/service/ObjectDownloadService.java @@ -0,0 +1,39 @@ +package com.dku.council.infra.nhn.s3.service; + +import com.dku.council.infra.nhn.global.service.service.NHNAuthService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.*; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.List; +import java.util.Objects; + +@Service +@RequiredArgsConstructor +public class ObjectDownloadService { + + private final NHNAuthService nhnAuthService; + + public ResponseEntity downloadObject(String fileName, String fileUrl) { + + // RestTemplate 생성 + RestTemplate restTemplate = new RestTemplate(); + + // 헤더 생성 + HttpHeaders headers = new HttpHeaders(); + headers.add("X-Auth-Token", nhnAuthService.requestToken()); + headers.setAccept(List.of(MediaType.APPLICATION_OCTET_STREAM)); + headers.setContentDispositionFormData("attachment", fileName); + + HttpEntity requestHttpEntity = new HttpEntity(null, headers); + + // API 호출, 데이터를 바이트 배열로 받음 + ResponseEntity response = restTemplate.exchange(fileUrl, HttpMethod.GET, requestHttpEntity, byte[].class); + + return response; + } + +} From c73581469a2b631b6bd556e48d6d2fd8e6896b9f Mon Sep 17 00:00:00 2001 From: Jungwoo Kim Date: Mon, 5 Feb 2024 00:30:20 +0900 Subject: [PATCH 03/11] =?UTF-8?q?feat:=20=EC=B1=84=ED=8C=85=EB=B0=A9?= =?UTF-8?q?=EC=97=90=20=EC=B0=B8=EC=97=AC=EC=A4=91=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EC=9D=80=20=EC=9C=A0=EC=A0=80=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=9C=20=EC=97=90=EB=9F=AC=20=EC=B2=98=EB=A6=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/exception/InvalidChatRoomUserException.java | 8 ++++++++ src/main/resources/errors.properties | 1 + src/main/resources/errors_en_US.properties | 1 + 3 files changed, 10 insertions(+) create mode 100644 src/main/java/com/dku/council/domain/chat/exception/InvalidChatRoomUserException.java diff --git a/src/main/java/com/dku/council/domain/chat/exception/InvalidChatRoomUserException.java b/src/main/java/com/dku/council/domain/chat/exception/InvalidChatRoomUserException.java new file mode 100644 index 00000000..ae2122b9 --- /dev/null +++ b/src/main/java/com/dku/council/domain/chat/exception/InvalidChatRoomUserException.java @@ -0,0 +1,8 @@ +package com.dku.council.domain.chat.exception; + +import com.dku.council.global.error.exception.LocalizedMessageException; +import org.springframework.http.HttpStatus; + +public class InvalidChatRoomUserException extends LocalizedMessageException { + public InvalidChatRoomUserException() { super(HttpStatus.BAD_REQUEST, "notfound.chat-room-user"); } +} diff --git a/src/main/resources/errors.properties b/src/main/resources/errors.properties index ea04901a..9ff0406a 100644 --- a/src/main/resources/errors.properties +++ b/src/main/resources/errors.properties @@ -52,6 +52,7 @@ invalid.student-id=\uCD5C\uC18C \uD559\uBC88\uC5D0 \uD574\uB2F9\uD558\uC9C0 \uC5 invalid.status=\uCC38\uC5EC\uAC00 \uBD88\uAC00\uB2A5\uD569\uB2C8\uB2E4. invalid.create-review-to-myself=\uC790\uC2E0\uC5D0\uAC8C\uB294 \uB9AC\uBDF0 \uC791\uC131\uC744 \uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. invalid.mbti=MBTI\uAC00 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. +invalid.chat-room-user=\uD574\uB2F9 \uCC44\uD305\uBC29\uC5D0\uC11C \uCC38\uC5EC\uC911\uC778 \uC720\uC800\uAC00 \uC544\uB2D9\uB2C8\uB2E4. notfound.user=\uC720\uC800\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. notfound.post=\uD574\uB2F9 \uAC8C\uC2DC\uAE00\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. diff --git a/src/main/resources/errors_en_US.properties b/src/main/resources/errors_en_US.properties index a9852bc0..9bcdfdad 100644 --- a/src/main/resources/errors_en_US.properties +++ b/src/main/resources/errors_en_US.properties @@ -52,6 +52,7 @@ invalid.student-id=It does not correspond to the minimum number of students. invalid.status=You can't enter because of your status. invalid.create-review-to-myself=You can't write a review to yourself. invalid.mbti=Invalid MBTI. +invalid.chat-room-user=You are not a participating user in this chat room. notfound.user=Cannot find that user. notfound.post=No such post was found. From 1fb8e9a255a3cec10a2dc42d3e7cb982b06256c8 Mon Sep 17 00:00:00 2001 From: Jungwoo Kim Date: Mon, 5 Feb 2024 00:33:51 +0900 Subject: [PATCH 04/11] =?UTF-8?q?feat:=20=ED=8C=8C=EC=9D=BC=20=EC=97=85?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=20=EA=B8=B0=EB=8A=A5=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B8=ED=95=9C=20DynamoDB=EC=97=90=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=ED=95=84=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../council/domain/chat/model/FileType.java | 18 ++++++++++++++++++ .../model/entity/ChatRoomMessage.java | 9 +++++++++ .../service/ChatRoomMessageService.java | 8 +++++++- 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/dku/council/domain/chat/model/FileType.java diff --git a/src/main/java/com/dku/council/domain/chat/model/FileType.java b/src/main/java/com/dku/council/domain/chat/model/FileType.java new file mode 100644 index 00000000..caeac3dd --- /dev/null +++ b/src/main/java/com/dku/council/domain/chat/model/FileType.java @@ -0,0 +1,18 @@ +package com.dku.council.domain.chat.model; + +public enum FileType { + /** + * 이미지 + */ + IMAGE, + + /** + * 파일 + */ + FILE, + + /** + * 일반 메시지 형태일 경우 + */ + NONE +} diff --git a/src/main/java/com/dku/council/domain/chatmessage/model/entity/ChatRoomMessage.java b/src/main/java/com/dku/council/domain/chatmessage/model/entity/ChatRoomMessage.java index e6f18d2c..69d0b4ba 100644 --- a/src/main/java/com/dku/council/domain/chatmessage/model/entity/ChatRoomMessage.java +++ b/src/main/java/com/dku/council/domain/chatmessage/model/entity/ChatRoomMessage.java @@ -58,4 +58,13 @@ public void setCreatedAt(LocalDateTime createdAt) { @DynamoDBAttribute private String content; + + @DynamoDBAttribute + private String fileName; + + @DynamoDBAttribute + private String fileUrl; + + @DynamoDBAttribute + private String fileType; } \ No newline at end of file diff --git a/src/main/java/com/dku/council/domain/chatmessage/service/ChatRoomMessageService.java b/src/main/java/com/dku/council/domain/chatmessage/service/ChatRoomMessageService.java index eb553566..b2b85b26 100644 --- a/src/main/java/com/dku/council/domain/chatmessage/service/ChatRoomMessageService.java +++ b/src/main/java/com/dku/council/domain/chatmessage/service/ChatRoomMessageService.java @@ -24,7 +24,10 @@ public void create(String roomId, Long userId, String userNickname, String content, - LocalDateTime messageTime) { + LocalDateTime messageTime, + String fileName, + String fileUrl, + String fileType) { ChatRoomMessage chatRoomMessage = new ChatRoomMessage(); chatRoomMessage.setRoomId(roomId); @@ -33,6 +36,9 @@ public void create(String roomId, chatRoomMessage.setUserNickname(userNickname); chatRoomMessage.setContent(content); chatRoomMessage.setCreatedAt(messageTime.atZone(seoulZoneId).toLocalDateTime()); + chatRoomMessage.setFileName(fileName); + chatRoomMessage.setFileUrl(fileUrl); + chatRoomMessage.setFileType(fileType); chatRoomMessageRepository.save(chatRoomMessage); } From 70268d8ee0782b15285c5daf52ca5c0ee28015d9 Mon Sep 17 00:00:00 2001 From: Jungwoo Kim Date: Mon, 5 Feb 2024 00:34:46 +0900 Subject: [PATCH 05/11] =?UTF-8?q?feat:=20=ED=8C=8C=EC=9D=BC=20=EC=97=85?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=20=EA=B8=B0=EB=8A=A5=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B8=ED=95=9C=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=EC=97=90=20=EA=B4=80=EB=A0=A8=20=EB=82=B4=EC=9A=A9=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../council/domain/chat/model/dto/Message.java | 15 ++++++++++++++- .../chat/model/dto/request/RequestChatDto.java | 10 ++++------ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/dku/council/domain/chat/model/dto/Message.java b/src/main/java/com/dku/council/domain/chat/model/dto/Message.java index dba682af..184b1839 100644 --- a/src/main/java/com/dku/council/domain/chat/model/dto/Message.java +++ b/src/main/java/com/dku/council/domain/chat/model/dto/Message.java @@ -1,5 +1,6 @@ package com.dku.council.domain.chat.model.dto; +import com.dku.council.domain.chat.model.FileType; import com.dku.council.domain.chat.model.MessageType; import lombok.Builder; import lombok.Getter; @@ -28,16 +29,28 @@ public class Message { @NotNull private LocalDateTime messageTime; + private String fileName; + + private String fileUrl; + + private FileType fileType; + @Builder private Message(MessageType type, String roomId, String sender, String message, - LocalDateTime messageTime) { + LocalDateTime messageTime, + String fileName, + String fileUrl, + FileType fileType) { this.type = type; this.roomId = roomId; this.sender = sender; this.message = message; this.messageTime = messageTime; + this.fileName = fileName; + this.fileUrl = fileUrl; + this.fileType = fileType; } } diff --git a/src/main/java/com/dku/council/domain/chat/model/dto/request/RequestChatDto.java b/src/main/java/com/dku/council/domain/chat/model/dto/request/RequestChatDto.java index 641e3998..b2e6bfcf 100644 --- a/src/main/java/com/dku/council/domain/chat/model/dto/request/RequestChatDto.java +++ b/src/main/java/com/dku/council/domain/chat/model/dto/request/RequestChatDto.java @@ -1,5 +1,6 @@ package com.dku.council.domain.chat.model.dto.request; +import com.dku.council.domain.chat.model.FileType; import com.dku.council.domain.chat.model.MessageType; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -18,10 +19,7 @@ public class RequestChatDto { private final String message; -// private String time; -// -// /* 파일 업로드 관련 변수 (일단 보류) */ -// private String s3DataUrl; // 파일 업로드 url -// private String fileName; // 파일이름 -// private String fileDir; // s3 파일 경로 + private final String fileName; // 파일이름 + private final String fileUrl; // s3에 업로드 된 위치 + private final FileType fileType; // 이미지인지 파일인지 } From 40bc5301240225b87ac8b15e8a36b93dd221b931 Mon Sep 17 00:00:00 2001 From: Jungwoo Kim Date: Mon, 5 Feb 2024 00:35:31 +0900 Subject: [PATCH 06/11] =?UTF-8?q?fix:=20=EC=B1=84=ED=8C=85=EB=B0=A9=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=ED=9B=84,=20=EB=B0=94=EB=A1=9C=20?= =?UTF-8?q?=EC=9E=85=EC=9E=A5=EC=8B=9C=20=EC=9C=A0=EC=A0=80=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EC=97=90=20=EC=A4=91=EB=B3=B5=20=ED=91=9C?= =?UTF-8?q?=EC=8B=9C=EB=90=98=EB=8A=94=20=ED=98=84=EC=83=81=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/chat/service/ChatService.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/dku/council/domain/chat/service/ChatService.java b/src/main/java/com/dku/council/domain/chat/service/ChatService.java index babca038..e63365c5 100644 --- a/src/main/java/com/dku/council/domain/chat/service/ChatService.java +++ b/src/main/java/com/dku/council/domain/chat/service/ChatService.java @@ -94,12 +94,6 @@ public ResponseChatRoomDto createChatRoom(String roomName, String roomPwd, boole .build(); chatRoomRepository.save(chatRoom); - ChatRoomUser chatRoomUser = ChatRoomUser.builder() - .chatRoom(chatRoom) - .user(user) - .build(); - chatRoomUserRepository.save(chatRoomUser); - return new ResponseChatRoomDto(chatRoom.getRoomId(), chatRoom.getRoomName(), chatRoom.getUserCount(), @@ -153,6 +147,14 @@ public String addUser(String roomId, String userName){ return user.getNickname(); } + /** + * 특정 채팅방에 참여중인 유저인지 확인 + */ + public boolean alreadyInRoom(String roomId, Long userId) { + long chatRoomId = chatRoomRepository.findChatRoomByRoomId(roomId).orElseThrow(ChatRoomNotFoundException::new).getId(); + return chatRoomUserRepository.existsUserByRoomIdAndUserId(chatRoomId, userId).isPresent(); + } + /** * 채팅방 유저 리스트 삭제 */ @@ -204,9 +206,4 @@ public void delChatRoom(Long userId, String roomId, boolean isAdmin) { } log.info("삭제 완료 roomId : {}", roomId); } - - public boolean alreadyInRoom(String roomId, Long userId) { - long chatRoomId = chatRoomRepository.findChatRoomByRoomId(roomId).orElseThrow(ChatRoomNotFoundException::new).getId(); - return chatRoomUserRepository.existsUserByRoomIdAndUserId(chatRoomId, userId).isPresent(); - } } From f987153fb163db79db48ea76ae7e38271e430426 Mon Sep 17 00:00:00 2001 From: Jungwoo Kim Date: Mon, 5 Feb 2024 00:40:01 +0900 Subject: [PATCH 07/11] =?UTF-8?q?feat:=20=EC=B1=84=ED=8C=85=20=EC=BB=A8?= =?UTF-8?q?=ED=8A=B8=EB=A1=A4=EB=9F=AC=EC=97=90=20=EC=97=85=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=ED=95=9C=20=ED=8C=8C=EC=9D=BC=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EB=82=B4=EC=9A=A9=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/chat/controller/ChatController.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/dku/council/domain/chat/controller/ChatController.java b/src/main/java/com/dku/council/domain/chat/controller/ChatController.java index 6b4e2ecc..9c60148c 100644 --- a/src/main/java/com/dku/council/domain/chat/controller/ChatController.java +++ b/src/main/java/com/dku/council/domain/chat/controller/ChatController.java @@ -1,5 +1,6 @@ package com.dku.council.domain.chat.controller; +import com.dku.council.domain.chat.model.FileType; import com.dku.council.domain.chat.model.MessageType; import com.dku.council.domain.chat.model.dto.Message; import com.dku.council.domain.chat.model.dto.request.RequestChatDto; @@ -87,7 +88,7 @@ public void enterUser(@Payload RequestChatDto chat, LocalDateTime messageTime = LocalDateTime.now(); // 입장 메시지 저장 - chatRoomMessageService.create(chat.getRoomId(), chat.getType().toString(), chat.getUserId(), chat.getSender(), enterMessage, messageTime); + chatRoomMessageService.create(chat.getRoomId(), chat.getType().toString(), chat.getUserId(), chat.getSender(), enterMessage, messageTime, "", "", chat.getFileType().toString()); Message message = Message.builder() .type(chat.getType()) @@ -95,6 +96,7 @@ public void enterUser(@Payload RequestChatDto chat, .sender(chat.getSender()) .message(enterMessage) .messageTime(messageTime) + .fileType(chat.getFileType()) .build(); sender.send(topic, message); @@ -110,7 +112,7 @@ public void enterUser(@Payload RequestChatDto chat, public void sendMessage(@Payload RequestChatDto chat) { LocalDateTime messageTime = LocalDateTime.now(); - chatRoomMessageService.create(chat.getRoomId(), chat.getType().toString(), chat.getUserId(), chat.getSender(), chat.getMessage(), messageTime); + chatRoomMessageService.create(chat.getRoomId(), chat.getType().toString(), chat.getUserId(), chat.getSender(), chat.getMessage(), messageTime, chat.getFileName(), chat.getFileUrl(), chat.getFileType().toString()); Message message = Message.builder() .type(chat.getType()) @@ -118,6 +120,9 @@ public void sendMessage(@Payload RequestChatDto chat) { .sender(chat.getSender()) .message(chat.getMessage()) .messageTime(messageTime) + .fileName(chat.getFileName()) + .fileUrl(chat.getFileUrl()) + .fileType(chat.getFileType()) .build(); sender.send(topic, message); @@ -182,7 +187,11 @@ public List userList(String roomId) { return chatService.getUserList(roomId); } - + /** + * 채팅방 별, 이전에 나눈 채팅 메시지 리스트 반환 + * + * @param roomId 채팅방 id + */ @GetMapping("/chat/message/list") @UserAuth @ResponseBody From e59df6b27dddc0d27455e0995fdfad6cb2407a76 Mon Sep 17 00:00:00 2001 From: Jungwoo Kim Date: Mon, 5 Feb 2024 00:42:22 +0900 Subject: [PATCH 08/11] =?UTF-8?q?feat:=20=EC=B1=84=ED=8C=85=EB=B0=A9=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=97=85=EB=A1=9C=EB=93=9C=20?= =?UTF-8?q?=EB=B0=8F=20=EB=8B=A4=EC=9A=B4=EB=A1=9C=EB=93=9C=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=ED=95=9C=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/controller/ChatFileController.java | 79 +++++++++++++++++++ .../model/dto/request/RequestChatFileDto.java | 26 ++++++ 2 files changed, 105 insertions(+) create mode 100644 src/main/java/com/dku/council/domain/chat/controller/ChatFileController.java create mode 100644 src/main/java/com/dku/council/domain/chat/model/dto/request/RequestChatFileDto.java diff --git a/src/main/java/com/dku/council/domain/chat/controller/ChatFileController.java b/src/main/java/com/dku/council/domain/chat/controller/ChatFileController.java new file mode 100644 index 00000000..f30f7539 --- /dev/null +++ b/src/main/java/com/dku/council/domain/chat/controller/ChatFileController.java @@ -0,0 +1,79 @@ +package com.dku.council.domain.chat.controller; + +import com.dku.council.domain.chat.exception.InvalidChatRoomUserException; +import com.dku.council.domain.chat.model.dto.request.RequestChatFileDto; +import com.dku.council.domain.chat.service.ChatService; +import com.dku.council.global.auth.jwt.AppAuthentication; +import com.dku.council.global.auth.role.UserAuth; +import com.dku.council.infra.nhn.global.service.service.NHNAuthService; +import com.dku.council.infra.nhn.s3.model.ChatUploadedImage; +import com.dku.council.infra.nhn.s3.model.ImageRequest; +import com.dku.council.infra.nhn.s3.service.ChatImageUploadService; +import com.dku.council.infra.nhn.s3.service.ObjectDownloadService; +import com.dku.council.infra.nhn.s3.service.ObjectStorageService; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.parameters.P; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.List; + +@Tag(name = "채팅방 파일", description = "채팅방 파일/이미지 업로드 및 다운로드 관련 api") +@RestController +@RequestMapping("/chat") +@RequiredArgsConstructor +public class ChatFileController { + + private final ChatService chatService; + private final ChatImageUploadService chatImageUploadService; + private final ObjectDownloadService objectDownloadService; + + private final NHNAuthService nhnAuthService; + private final ObjectStorageService objectStorageService; + +// @PostMapping("/file/upload") + + /** + * 이미지 업로드 기능 + * + * @param request roomId와 전송할 이미지 파일에 대한 dto + */ + @PostMapping(value = "/image/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @UserAuth + public List uploadImage(AppAuthentication auth, + @Valid @ModelAttribute RequestChatFileDto request) { + // 채팅방에 현재 참여중인 유저가 아니면, 해당 채팅방에 이미지 업로드를 할 수 없게 + if (chatService.alreadyInRoom(request.getRoomId(), auth.getUserId())) { + return chatImageUploadService.newContext().uploadChatImages( + ImageRequest.ofList(request.getFiles()), + request.getRoomId(), + auth.getUserId()); + } else { + throw new InvalidChatRoomUserException(); + } + } + + @GetMapping("/download/{fileName}") + @UserAuth + public ResponseEntity download(AppAuthentication auth, + @PathVariable String fileName, + @RequestParam("roomId") String roomId, + @RequestParam("fileUrl") String fileUrl) { + if (chatService.alreadyInRoom(roomId, auth.getUserId())) { + return objectDownloadService.downloadObject(fileName, fileUrl); + } else { + throw new InvalidChatRoomUserException(); + } + } + + // TODO : 파일 삭제시, chatRoomMessage에도 삭제된게 반영 되어야함 +// @DeleteMapping("/file/delete") +// public void deleteFile(@RequestParam("roomId") String roomId, +// @RequestParam("fileUrl") String fileUrl) { +// objectStorageService.deleteChatFileByDirectUrl(nhnAuthService.requestToken(), fileUrl); +// } + +} diff --git a/src/main/java/com/dku/council/domain/chat/model/dto/request/RequestChatFileDto.java b/src/main/java/com/dku/council/domain/chat/model/dto/request/RequestChatFileDto.java new file mode 100644 index 00000000..54ff1fda --- /dev/null +++ b/src/main/java/com/dku/council/domain/chat/model/dto/request/RequestChatFileDto.java @@ -0,0 +1,26 @@ +package com.dku.council.domain.chat.model.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import org.springframework.web.multipart.MultipartFile; + +import javax.validation.constraints.NotBlank; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +@Getter +public class RequestChatFileDto { + + @NotBlank + @Schema(description = "채팅방 번호", example = "d118101z-c737-4253-9911-ea2579405f42") + private final String roomId; + + @Schema(description = "첨부 파일 목록") + private final List files; + + public RequestChatFileDto(String roomId, List files) { + this.roomId = roomId; + this.files = Objects.requireNonNullElseGet(files, ArrayList::new); + } +} From baf9893afec6c78c925883db4e76bfbb9bf8f7c1 Mon Sep 17 00:00:00 2001 From: Jungwoo Kim Date: Mon, 5 Feb 2024 00:44:00 +0900 Subject: [PATCH 09/11] =?UTF-8?q?feat:=20=EC=B1=84=ED=8C=85=EB=B0=A9=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=97=85=EB=A1=9C=EB=93=9C,=20?= =?UTF-8?q?=EB=8B=A4=EC=9A=B4=EB=A1=9C=EB=93=9C=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=9C=20socket.js=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/static/js/chatroom/socket.js | 145 +++++++++--------- 1 file changed, 73 insertions(+), 72 deletions(-) diff --git a/src/main/resources/static/js/chatroom/socket.js b/src/main/resources/static/js/chatroom/socket.js index 75d764c9..d4d0ad9d 100644 --- a/src/main/resources/static/js/chatroom/socket.js +++ b/src/main/resources/static/js/chatroom/socket.js @@ -60,7 +60,8 @@ function onConnected() { "roomId": roomId, "userId": userId, sender: username, - type: 'ENTER' + type: 'ENTER', + fileType: 'NONE' }) ) @@ -107,7 +108,10 @@ function getPreviousMessageList() { sender: data[i]["userNickname"], message: data[i]["content"], type: data[i]["messageType"], - messageTime: data[i]["createdAt"] + messageTime: data[i]["createdAt"], + "fileName": data[i]["fileName"], + fileUrl: data[i]["fileUrl"], + fileType: data[i]["fileType"] }; console.log(previousChatMessage); previousMessageReceived(JSON.stringify(previousChatMessage)); @@ -132,7 +136,8 @@ function sendMessage(event) { "userId": userId, sender: username, message: messageInput.value, - type: 'TALK' + type: 'TALK', + fileType: 'NONE' }; stompClient.send("/pub/chat/sendMessage", {}, JSON.stringify(chatMessage)); @@ -189,9 +194,9 @@ function addMessageToTheChatRoom(chat, messageElement) { // 만약 s3DataUrl 의 값이 null 이 아니라면 => chat 내용이 파일 업로드와 관련된 내용이라면 // img 를 채팅에 보여주는 작업 - if(chat.s3DataUrl != null){ + if(chat.fileUrl !== "" && chat.fileType === 'IMAGE'){ let imgElement = document.createElement('img'); - imgElement.setAttribute("src", chat.s3DataUrl); + imgElement.setAttribute("src", chat.fileUrl); imgElement.setAttribute("width", "300"); imgElement.setAttribute("height", "300"); @@ -199,8 +204,7 @@ function addMessageToTheChatRoom(chat, messageElement) { downBtnElement.setAttribute("class", "btn fa fa-download"); downBtnElement.setAttribute("id", "downBtn"); downBtnElement.setAttribute("name", chat.fileName); - downBtnElement.setAttribute("onclick", `downloadFile('${chat.fileName}', '${chat.fileDir}')`); - + downBtnElement.setAttribute("onclick", `downloadFile('${chat.roomId}', '${chat.fileName}', '${chat.fileUrl}')`); contentElement.appendChild(imgElement); contentElement.appendChild(downBtnElement); @@ -255,75 +259,72 @@ document.addEventListener('DOMContentLoaded', function () { messageForm.addEventListener('submit', sendMessage, true) -// TODO: 아래 파일 관련 내용들은 일단 보류 - /// 파일 업로드 부분 //// function uploadFile(){ - // var file = $("#file")[0].files[0]; - // var formData = new FormData(); - // formData.append("file",file); - // formData.append("roomId", roomId); - // - // // ajax 로 multipart/form-data 를 넘겨줄 때는 - // // processData: false, - // // contentType: false - // // 처럼 설정해주어야 한다. - // - // // 동작 순서 - // // post 로 rest 요청한다. - // // 1. 먼저 upload 로 파일 업로드를 요청한다. - // // 2. upload 가 성공적으로 완료되면 data 에 upload 객체를 받고, - // // 이를 이용해 chatMessage 를 작성한다. - // $.ajax({ - // type : 'POST', - // url : '/s3/upload', - // data : formData, - // processData: false, - // contentType: false - // }).done(function (data){ - // // console.log("업로드 성공") - // - // var chatMessage = { - // "roomId": roomId, - // sender: username, - // message: username+"님의 파일 업로드", - // type: 'TALK', - // s3DataUrl : data.s3DataUrl, // Dataurl - // "fileName": file.name, // 원본 파일 이름 - // "fileDir": data.fileDir // 업로드 된 위치 - // }; - // - // // 해당 내용을 발신한다. - // stompClient.send("/pub/chat/sendMessage", {}, JSON.stringify(chatMessage)); - // }).fail(function (error){ - // alert(error); - // }) + var files = $("#file")[0].files; + var formData = new FormData(); + + for (let i = 0; i < files.length; i++) { + formData.append("files", files[i]); + } + formData.append("roomId", roomId); + + // ajax 로 multipart/form-data 를 넘겨줄 때는 + // processData: false, + // contentType: false + // 처럼 설정해주어야 한다. + + // 동작 순서 + // post 로 rest 요청한다. + // 1. 먼저 upload 로 파일 업로드를 요청한다. + // 2. upload 가 성공적으로 완료되면 data 에 upload 객체를 받고, + // 이를 이용해 chatMessage 를 작성한다. + $.ajax({ + type : 'POST', + url : '/chat/image/upload', + data : formData, + processData: false, + contentType: false + }).done(function (data){ + for (let i = 0; data.length; i++) { + var chatMessage = { + "roomId": roomId, + "userId": data[i]["userId"], + sender: username, + type: 'TALK', + "fileName": data[i]["fileName"], // 원본 파일 이름 + fileUrl: data[i]["fileUrl"], // 저장된 파일 경로 + fileType: data[i]["fileType"] + }; + + // 해당 내용을 발신한다. + stompClient.send("/pub/chat/sendMessage", {}, JSON.stringify(chatMessage)); + } + }).fail(function (error){ + alert(error); + }) } // 파일 다운로드 부분 // // 버튼을 누르면 downloadFile 메서드가 실행됨 -// 다운로드 url 은 /s3/download+원본파일이름 -function downloadFile(name, dir){ - // // console.log("파일 이름 : "+name); - // // console.log("파일 경로 : " + dir); - // let url = "/s3/download/"+name; - // - // // get 으로 rest 요청한다. - // $.ajax({ - // url: "/s3/download/"+name, // 요청 url 은 download/{name} - // data: { - // "fileDir" : dir // 파일의 경로를 파라미터로 넣는다. - // }, - // dataType: 'binary', // 파일 다운로드를 위해서는 binary 타입으로 받아야한다. - // xhrFields: { - // 'responseType': 'blob' // 여기도 마찬가지 - // }, - // success: function(data) { - // - // var link = document.createElement('a'); - // link.href = URL.createObjectURL(data); - // link.download = name; - // link.click(); - // } - // }); +// 다운로드 url 은 /chat/download+원본파일이름 +function downloadFile(roomId, fileName, url){ + // get 으로 rest 요청한다. + $.ajax({ + url: "/chat/download/"+fileName, // 요청 url 은 download/{name} + data: { + "roomId": roomId, + "fileUrl" : url + }, + dataType: 'binary', // 파일 다운로드를 위해서는 binary 타입으로 받아야한다. + xhrFields: { + 'responseType': 'blob' // 여기도 마찬가지 + }, + success: function(data) { + var link = document.createElement('a'); + link.href = URL.createObjectURL(data); + link.download = fileName; + link.click(); + } + }); } \ No newline at end of file From e3afb55219aa59aa8101923a6e6a87b237cd31d6 Mon Sep 17 00:00:00 2001 From: Jungwoo Kim Date: Mon, 5 Feb 2024 00:45:19 +0900 Subject: [PATCH 10/11] =?UTF-8?q?feat:=20=EC=B1=84=ED=8C=85=EB=B0=A9=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EC=8B=9C,=20=ED=95=B4=EB=8B=B9=20?= =?UTF-8?q?=EC=B1=84=ED=8C=85=EB=B0=A9=EC=97=90=20=EC=A1=B4=EC=9E=AC?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=ED=8C=8C=EC=9D=BC=EB=93=A4=EC=9D=84=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=ED=95=98=EB=8A=94=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/controller/ChatRoomController.java | 13 +++++-- .../domain/chat/service/ChatFileService.java | 39 +++++++++++++++++++ .../repository/ChatRoomMessageRepository.java | 2 + .../templates/page/chatting/roomlist.html | 10 ++--- 4 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 src/main/java/com/dku/council/domain/chat/service/ChatFileService.java diff --git a/src/main/java/com/dku/council/domain/chat/controller/ChatRoomController.java b/src/main/java/com/dku/council/domain/chat/controller/ChatRoomController.java index c46901a8..12a1837b 100644 --- a/src/main/java/com/dku/council/domain/chat/controller/ChatRoomController.java +++ b/src/main/java/com/dku/council/domain/chat/controller/ChatRoomController.java @@ -1,6 +1,7 @@ package com.dku.council.domain.chat.controller; import com.dku.council.domain.chat.model.dto.response.ResponseChatRoomDto; +import com.dku.council.domain.chat.service.ChatFileService; import com.dku.council.domain.chat.service.ChatService; import com.dku.council.domain.chatmessage.service.ChatRoomMessageService; import com.dku.council.domain.user.model.dto.response.ResponseUserInfoForChattingDto; @@ -10,7 +11,6 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; @@ -22,10 +22,11 @@ @RequiredArgsConstructor @Slf4j public class ChatRoomController { - private final ChatService chatService; private final UserService userService; + private final ChatService chatService; private final ChatRoomMessageService chatRoomMessageService; + private final ChatFileService chatFileService; /** * 채팅방 리스트 화면 @@ -109,10 +110,14 @@ public boolean confirmPwd(@PathVariable String roomId, * * @param roomId 채팅방 id */ - @DeleteMapping("/delete/{roomId}") + @DeleteMapping @UserAuth - public String delChatRoom(@PathVariable String roomId, AppAuthentication auth){ + public String delChatRoom(@RequestParam String roomId, AppAuthentication auth){ + // 해당 채팅방에 존재하는 파일들 삭제 + chatFileService.deleteAllFilesInChatRoom(roomId); + + // 해당 채팅방에 존재하는 채팅 메시지들 삭제 chatRoomMessageService.deleteChatRoomMessages(roomId); // roomId(UUID 값) 기준으로 채팅방 삭제 diff --git a/src/main/java/com/dku/council/domain/chat/service/ChatFileService.java b/src/main/java/com/dku/council/domain/chat/service/ChatFileService.java new file mode 100644 index 00000000..dcce2d16 --- /dev/null +++ b/src/main/java/com/dku/council/domain/chat/service/ChatFileService.java @@ -0,0 +1,39 @@ +package com.dku.council.domain.chat.service; + +import com.dku.council.domain.chatmessage.model.entity.ChatRoomMessage; +import com.dku.council.domain.chatmessage.repository.ChatRoomMessageRepository; +import com.dku.council.infra.nhn.global.service.service.NHNAuthService; +import com.dku.council.infra.nhn.s3.service.ObjectStorageService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +@Slf4j +public class ChatFileService { + + private final NHNAuthService nhnAuthService; + private final ObjectStorageService objectStorageService; + + private final ChatRoomMessageRepository chatRoomMessageRepository; + + public void deleteAllFilesInChatRoom(String roomId) { + List imageMessageList = chatRoomMessageRepository.findAllByRoomIdAndFileType(roomId, "IMAGE"); + List fileMessageList = chatRoomMessageRepository.findAllByRoomIdAndFileType(roomId, "FILE"); + + if (!imageMessageList.isEmpty()) { + for (ChatRoomMessage chatRoomMessage : imageMessageList) { + objectStorageService.deleteChatFileByDirectUrl(nhnAuthService.requestToken(), chatRoomMessage.getFileUrl()); + } + } + + if (!fileMessageList.isEmpty()) { + for (ChatRoomMessage chatRoomMessage : fileMessageList) { + objectStorageService.deleteChatFileByDirectUrl(nhnAuthService.requestToken(), chatRoomMessage.getFileUrl()); + } + } + } +} diff --git a/src/main/java/com/dku/council/domain/chatmessage/repository/ChatRoomMessageRepository.java b/src/main/java/com/dku/council/domain/chatmessage/repository/ChatRoomMessageRepository.java index 17d253ef..b86427fd 100644 --- a/src/main/java/com/dku/council/domain/chatmessage/repository/ChatRoomMessageRepository.java +++ b/src/main/java/com/dku/council/domain/chatmessage/repository/ChatRoomMessageRepository.java @@ -14,4 +14,6 @@ public interface ChatRoomMessageRepository extends CrudRepository findAllByRoomIdOrderByCreatedAtAsc(String roomId); void deleteAllByRoomId(String roomId); + + List findAllByRoomIdAndFileType(String roomId, String fileType); } diff --git a/src/main/resources/templates/page/chatting/roomlist.html b/src/main/resources/templates/page/chatting/roomlist.html index 49e91571..43102386 100644 --- a/src/main/resources/templates/page/chatting/roomlist.html +++ b/src/main/resources/templates/page/chatting/roomlist.html @@ -163,16 +163,14 @@ }) } - // 채팅방 삭제 - // function delRoom(){ - // location.href = "/chatRoom/delete/"+roomId; - // } - function delRoom(){ $.ajax({ type : "DELETE", - url : "/chatRoom/delete/"+roomId, + url : "/chatRoom", async : false, + data: { + "roomId": roomId + }, success : function(result){ if (result) { From d612bf16f35eb61e1a74129bf5e4c2eca89e9db6 Mon Sep 17 00:00:00 2001 From: Jungwoo Kim Date: Mon, 5 Feb 2024 01:11:57 +0900 Subject: [PATCH 11/11] =?UTF-8?q?fix:=20ObjectUploadContext=EC=97=90=20?= =?UTF-8?q?=ED=95=84=EB=93=9C=EA=B0=92=20=EC=B6=94=EA=B0=80=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B8=ED=95=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=EB=93=A4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../council/domain/post/service/PetitionServiceTest.java | 2 +- .../council/infra/nhn/service/ObjectStorageServiceTest.java | 6 +++++- .../nhn/service/actual/ActualObjectStorageServiceTest.java | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/dku/council/domain/post/service/PetitionServiceTest.java b/src/test/java/com/dku/council/domain/post/service/PetitionServiceTest.java index 651739b9..80b7cb45 100644 --- a/src/test/java/com/dku/council/domain/post/service/PetitionServiceTest.java +++ b/src/test/java/com/dku/council/domain/post/service/PetitionServiceTest.java @@ -47,7 +47,7 @@ public class PetitionServiceTest { private final Clock clock = ClockUtil.create(); private final Duration writeCooltime = Duration.ofDays(1); - private final ObjectUploadContext uploadContext = new ObjectUploadContext("", "", "", ""); + private final ObjectUploadContext uploadContext = new ObjectUploadContext("", "", "", "", "", ""); @Mock private PetitionStatisticService petitionStatisticService; diff --git a/src/test/java/com/dku/council/infra/nhn/service/ObjectStorageServiceTest.java b/src/test/java/com/dku/council/infra/nhn/service/ObjectStorageServiceTest.java index 968dfca9..2917c03e 100644 --- a/src/test/java/com/dku/council/infra/nhn/service/ObjectStorageServiceTest.java +++ b/src/test/java/com/dku/council/infra/nhn/service/ObjectStorageServiceTest.java @@ -26,13 +26,17 @@ class ObjectStorageServiceTest extends AbstractMockServerTest { private String apiFilePath; + private String apiChatImagePath; + + private String apiChatFilePath; + @BeforeEach public void beforeEach() { WebClient webClient = WebClient.create(); this.apiPath = "http://localhost:" + mockServer.getPort(); - this.uploadContext = new ObjectUploadContext(apiPath, apiImagePath, apiFilePath ,"default"); + this.uploadContext = new ObjectUploadContext(apiPath, apiImagePath, apiFilePath ,"default", apiChatImagePath, apiChatFilePath); this.service = new ObjectStorageService(webClient, uploadContext); } diff --git a/src/test/java/com/dku/council/infra/nhn/service/actual/ActualObjectStorageServiceTest.java b/src/test/java/com/dku/council/infra/nhn/service/actual/ActualObjectStorageServiceTest.java index 72e8f040..0b6533c3 100644 --- a/src/test/java/com/dku/council/infra/nhn/service/actual/ActualObjectStorageServiceTest.java +++ b/src/test/java/com/dku/council/infra/nhn/service/actual/ActualObjectStorageServiceTest.java @@ -36,12 +36,14 @@ public void beforeEach() throws NoSuchMethodException, InvocationTargetException String osApiPath = properties.get("nhn.os.api-path"); String osApiImagePath = properties.get("nhn.os.api-image-path"); String osApiFilePath = properties.get("nhn.os.api-file-path"); + String osApiChatImagePath = properties.get("nhn.os.api-chat-image-path"); + String osApiChatFilePath = properties.get("nhn.os.api-chat-file-path"); String authApiPath = properties.get("nhn.auth.api-path"); String tenantId = properties.get("nhn.auth.tenant-id"); String username = properties.get("nhn.auth.username"); String password = properties.get("nhn.auth.password"); - ObjectUploadContext uploadContext = new ObjectUploadContext(osApiPath, osApiImagePath, osApiFilePath ,defaultThumbnail); + ObjectUploadContext uploadContext = new ObjectUploadContext(osApiPath, osApiImagePath, osApiFilePath ,defaultThumbnail, osApiChatImagePath, osApiChatFilePath); this.storageService = new ObjectStorageService(webClient, uploadContext); this.authService = new NHNAuthService(webClient, authApiPath, tenantId, username, password);