diff --git a/.idea/compiler.xml b/.idea/compiler.xml index d78a3d4c..bedddf14 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -2,13 +2,7 @@ - - - - - - - + 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 9355123e..4934b07a 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/DiscodeitApplication.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/DiscodeitApplication.java @@ -19,7 +19,7 @@ public class DiscodeitApplication { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(DiscodeitApplication.class, args); - UserService userService = context.getBean(UserService.class); + /* UserService userService = context.getBean(UserService.class); ChannelService channelService = context.getBean(ChannelService.class); MessageService messageService = context.getBean(MessageService.class); ReadStatusService readStatusService = context.getBean(ReadStatusService.class); @@ -30,10 +30,7 @@ public static void main(String[] args) { Channel channel = setupChannel(channelService); messageCreateTest(messageService, channel, user); - /* 유저의 회원가입 로그인, 채널생성, 메세지 발송, 마지막 접속상태까지의 전체 시나리오를 검증하는 과정 - 메인 메서드 내에 변수명이 겹쳐서 항목별로 변수명을 명확히 분리하여 가독성 높임 - 데이터를 주고 받을때 엔티티를 직접 노출하지않고 ex)UserStatusResponse같은 DTO(Record)를 활용하여 필요한 정보만 안전하게전달 - */ + System.out.println("\n=== Discodeit 테스트 시작 ==="); User scenarioUser = userService.create(new UserCreateRequest("haha", "haha@co.com", "ha1234", null)); @@ -102,6 +99,7 @@ private static void messageCreateTest(MessageService messageService, Channel cha null ); Message message = messageService.create(request); - System.out.println("메시지 생성: " + message.getId()); + System.out.println("메시지 생성: " + message.getId()); */ } -} \ No newline at end of file +} + diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/JavaApplication2.java b/discodeit/src/main/java/com/sprint/mission/discodeit/JavaApplication2.java index d2e27553..39f5ba3c 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/JavaApplication2.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/JavaApplication2.java @@ -1,86 +1,88 @@ -package com.sprint.mission.discodeit; - -import com.sprint.mission.discodeit.dto.ChannelCreateRequest; -import com.sprint.mission.discodeit.dto.MessageCreateRequest; -import com.sprint.mission.discodeit.dto.ReadStatusCreateRequest; -import com.sprint.mission.discodeit.dto.UserCreateRequest; -import com.sprint.mission.discodeit.entity.Channel; -import com.sprint.mission.discodeit.entity.ChannelType; -import com.sprint.mission.discodeit.entity.Message; -import com.sprint.mission.discodeit.entity.User; -import com.sprint.mission.discodeit.repository.*; -import com.sprint.mission.discodeit.repository.file.FileBinaryContentRepository; -import com.sprint.mission.discodeit.repository.file.FileChannelRepository; -import com.sprint.mission.discodeit.repository.file.FileMessageRepository; -import com.sprint.mission.discodeit.repository.file.FileUserRepository; -import com.sprint.mission.discodeit.repository.jcf.JcfReadStatusRepository; -import com.sprint.mission.discodeit.repository.jcf.JcfUserStatusRepository; -import com.sprint.mission.discodeit.service.ChannelService; -import com.sprint.mission.discodeit.service.MessageService; -import com.sprint.mission.discodeit.service.ReadStatusService; -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.BasicReadStatusService; -import com.sprint.mission.discodeit.service.basic.BasicUserService; - -public class JavaApplication2 { - static User setupUser(UserService userService) { - UserCreateRequest request = new UserCreateRequest( - "woody", - "woody@codeit.com", - "woody1234", - null - ); - return userService.create(request); - } - private static Channel setupChannel(ChannelService channelService) { - ChannelCreateRequest request = new ChannelCreateRequest( - "공지", - "공지 채널입니다.", - null - ); - return channelService.createPublic(request); - } - - private static void messageCreateTest(MessageService messageService, Channel channel, User author) { - MessageCreateRequest request = new MessageCreateRequest( - "안녕하세요.", - channel.getId(), - author.getId(), - null // 첨부파일(지금은 없으므로 null 전달) - ); - - Message message = messageService.create(request); - System.out.println("메시지 생성: " + message.getId()); - } - public static void main(String[] args) { - // 레포지토리 초기화 - UserRepository userRepository = new FileUserRepository(); - ChannelRepository channelRepository = new FileChannelRepository(); - MessageRepository messageRepository = new FileMessageRepository(); - ReadStatusRepository readStatusRepository = new JcfReadStatusRepository(); - BinaryContentRepository binaryContentRepository = new FileBinaryContentRepository(); - - // 서비스 초기화 - UserStatusRepository userStatusRepository = new JcfUserStatusRepository(); - UserService userService = new BasicUserService(userRepository, userStatusRepository); - ChannelService channelService = new BasicChannelService(channelRepository, readStatusRepository, messageRepository); - MessageService messageService = new BasicMessageService(messageRepository, channelRepository, userRepository, binaryContentRepository); - ReadStatusService readStatusService = new BasicReadStatusService(readStatusRepository, userRepository, channelRepository); - - // 셋업 - User user = setupUser(userService); - Channel channel = setupChannel(channelService); - // 테스트 - messageCreateTest(messageService, channel, user); - ReadStatusCreateRequest rsRequest = new ReadStatusCreateRequest( - user.getId(), - channel.getId(), - null // 처음에는 읽은 메시지가 없으므로 null을 넣습니다. - ); - - readStatusService.create(rsRequest); - System.out.println("읽음 상태 생성 완료!"); - } -} +//package com.sprint.mission.discodeit; +// +//import com.sprint.mission.discodeit.dto.ChannelCreateRequest; +//import com.sprint.mission.discodeit.dto.MessageCreateRequest; +//import com.sprint.mission.discodeit.dto.ReadStatusCreateRequest; +//import com.sprint.mission.discodeit.dto.UserCreateRequest; +//import com.sprint.mission.discodeit.entity.Channel; +//import com.sprint.mission.discodeit.entity.ChannelType; +//import com.sprint.mission.discodeit.entity.Message; +//import com.sprint.mission.discodeit.entity.User; +//import com.sprint.mission.discodeit.repository.*; +//import com.sprint.mission.discodeit.repository.file.FileBinaryContentRepository; +//import com.sprint.mission.discodeit.repository.file.FileChannelRepository; +//import com.sprint.mission.discodeit.repository.file.FileMessageRepository; +//import com.sprint.mission.discodeit.repository.file.FileUserRepository; +//import com.sprint.mission.discodeit.repository.jcf.JcfReadStatusRepository; +//import com.sprint.mission.discodeit.repository.jcf.JcfUserStatusRepository; +//import com.sprint.mission.discodeit.service.ChannelService; +//import com.sprint.mission.discodeit.service.MessageService; +//import com.sprint.mission.discodeit.service.ReadStatusService; +//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.BasicReadStatusService; +//import com.sprint.mission.discodeit.service.basic.BasicUserService; +// +//import java.util.Optional; +// +//public class JavaApplication2 { +// static User setupUser(UserService userService) { +// UserCreateRequest request = new UserCreateRequest( +// "woody", +// "woody@codeit.com", +// "woody1234", +// null +// ); +// return userService.create(request, Optional.empty()); +// } +// private static Channel setupChannel(ChannelService channelService) { +// ChannelCreateRequest request = new ChannelCreateRequest( +// "공지", +// "공지 채널입니다.", +// null +// ); +// return channelService.createPublic(request); +// } +// +// private static void messageCreateTest(MessageService messageService, Channel channel, User author) { +// MessageCreateRequest request = new MessageCreateRequest( +// "안녕하세요.", +// channel.getId(), +// author.getId(), +// null // 첨부파일(지금은 없으므로 null 전달) +// ); +// +// Message message = messageService.create(request); +// System.out.println("메시지 생성: " + message.getId()); +// } +// public static void main(String[] args) { +// // 레포지토리 초기화 +// UserRepository userRepository = new FileUserRepository(); +// ChannelRepository channelRepository = new FileChannelRepository(); +// MessageRepository messageRepository = new FileMessageRepository(); +// ReadStatusRepository readStatusRepository = new JcfReadStatusRepository(); +// BinaryContentRepository binaryContentRepository = new FileBinaryContentRepository(); +// +// // 서비스 초기화 +// UserStatusRepository userStatusRepository = new JcfUserStatusRepository(); +// UserService userService = new BasicUserService(userRepository, userStatusRepository); +// ChannelService channelService = new BasicChannelService(channelRepository, readStatusRepository, messageRepository); +// MessageService messageService = new BasicMessageService(messageRepository, channelRepository, userRepository, binaryContentRepository); +// ReadStatusService readStatusService = new BasicReadStatusService(readStatusRepository, userRepository, channelRepository); +// +// // 셋업 +// User user = setupUser(userService); +// Channel channel = setupChannel(channelService); +// // 테스트 +// messageCreateTest(messageService, channel, user); +// ReadStatusCreateRequest rsRequest = new ReadStatusCreateRequest( +// user.getId(), +// channel.getId(), +// null // 처음에는 읽은 메시지가 없으므로 null을 넣습니다. +// ); +// +// readStatusService.create(rsRequest); +// System.out.println("읽음 상태 생성 완료!"); +// } +//} 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..a7e89ce3 --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/AuthController.java @@ -0,0 +1,25 @@ +package com.sprint.mission.discodeit.controller; + +import com.sprint.mission.discodeit.dto.LoginRequest; +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.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/auth") +@RequiredArgsConstructor +public class AuthController { + private final AuthService authService; + + @PostMapping("/login") + public ResponseEntity login(@RequestBody LoginRequest request) { + User user = authService.login(request); + return ResponseEntity.ok(user); + } + +} \ No newline at end of file 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..84111579 --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/BinaryContentController.java @@ -0,0 +1,46 @@ +package com.sprint.mission.discodeit.controller; + +import com.sprint.mission.discodeit.dto.BinaryContentCreateRequest; +import com.sprint.mission.discodeit.entity.BinaryContent; +import com.sprint.mission.discodeit.service.BinaryContentService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.UUID; + +@RestController +@RequestMapping("/api/binary-contents") +@RequiredArgsConstructor +public class BinaryContentController { + + private final BinaryContentService binaryContentService; + + @GetMapping("/{id}") + public ResponseEntity download(@PathVariable UUID id) { + BinaryContent content = binaryContentService.find(id); + + String contentDisposition = "attachment; filename=\"file_" + id + "\""; + + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition) // 다운로드 파일명 설정 + .contentType(MediaType.APPLICATION_OCTET_STREAM) // 이진 데이터 타입 설정 + .body(content.getBytes()); // 실제 바이트 데이터 + } + + @PostMapping + public ResponseEntity create(@RequestBody BinaryContentCreateRequest request) { + BinaryContent savedContent = binaryContentService.create(request); + return ResponseEntity.status(HttpStatus.CREATED).body(savedContent); + } + + @GetMapping + public ResponseEntity> findAll(@RequestParam List ids) { + List contents = binaryContentService.findAllByIds(ids); + return ResponseEntity.ok(contents); + } +} \ No newline at end of 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 new file mode 100644 index 00000000..c0988e1d --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/ChannelController.java @@ -0,0 +1,72 @@ +package com.sprint.mission.discodeit.controller; + +import com.sprint.mission.discodeit.dto.ChannelCreateRequest; +import com.sprint.mission.discodeit.dto.ChannelDto; +import com.sprint.mission.discodeit.dto.ChannelUpdateRequest; +import com.sprint.mission.discodeit.entity.Channel; +import com.sprint.mission.discodeit.service.ChannelService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@RestController +@RequestMapping("/api/channel") +@RequiredArgsConstructor +public class ChannelController { + + private final ChannelService channelService; + + @PostMapping("/public") + public ResponseEntity createPublic(@RequestBody ChannelCreateRequest request) { + Channel channel = channelService.createPublic(request); + return ResponseEntity.status(HttpStatus.CREATED).body(channel); + } + + @PostMapping("/private") + public ResponseEntity createPrivate(@RequestBody ChannelCreateRequest request) { + Channel channel = channelService.createPrivate(request); + return ResponseEntity.status(HttpStatus.CREATED).body(channel); + } + + @GetMapping("/{userId}") + public ResponseEntity> findAllByUserId(@PathVariable UUID userId) { + List channels = channelService.findAllByUserId(userId); + return ResponseEntity.ok(channels); + } + + @PatchMapping("/{id}") + public ResponseEntity update( + @PathVariable UUID id, + @RequestBody ChannelUpdateRequest request) { + Channel updatedChannel = channelService.update(id, request); + return ResponseEntity.ok(toDto(updatedChannel)); + } + + @DeleteMapping("/{id}") + public ResponseEntity delete(@PathVariable UUID id) { + channelService.delete(id); + return ResponseEntity.noContent().build(); + } + + private ChannelDto toDto(Channel channel) { + return new ChannelDto( + channel.getId(), + channel.getType(), + channel.getName(), + channel.getDescription(), + new ArrayList<>(), // 참여자 명단은 일단 빈 리스트로 처리 + Instant.now() + ); + } + + @GetMapping + public ResponseEntity> findAll() { + return ResponseEntity.ok(null); + } +} \ No newline at end of file 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..619a0742 --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/MessageController.java @@ -0,0 +1,48 @@ +package com.sprint.mission.discodeit.controller; + + +import com.sprint.mission.discodeit.dto.MessageCreateRequest; +import com.sprint.mission.discodeit.dto.MessageUpdateRequest; +import com.sprint.mission.discodeit.entity.Message; +import com.sprint.mission.discodeit.service.MessageService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.UUID; + +@RestController +@RequestMapping("/api/messages") +@RequiredArgsConstructor +public class MessageController { + + private final MessageService messageService; + + @PostMapping + public ResponseEntity send(@RequestBody MessageCreateRequest request) { + Message message = messageService.create(request); + return ResponseEntity.status(HttpStatus.CREATED).body(message); + } + + @PatchMapping("/{id}") + public ResponseEntity update( + @PathVariable UUID id, + @RequestBody MessageUpdateRequest request) { + Message updatedMessage = messageService.update(id, request); + return ResponseEntity.ok(updatedMessage); + } + + @DeleteMapping("/{id}") + public ResponseEntity delete(@PathVariable UUID id) { + messageService.delete(id); + return ResponseEntity.noContent().build(); + } + + @GetMapping("/channel/{channelId}") + public ResponseEntity> findAllByChannelId(@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..259ea4b4 --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/ReadStatusController.java @@ -0,0 +1,36 @@ +package com.sprint.mission.discodeit.controller; + +import com.sprint.mission.discodeit.dto.ReadStatusCreateRequest; +import com.sprint.mission.discodeit.dto.ReadStatusUpdateRequest; +import com.sprint.mission.discodeit.entity.ReadStatus; +import com.sprint.mission.discodeit.service.ReadStatusService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.UUID; + +@RestController +@RequestMapping("/api/read-statuses") +@RequiredArgsConstructor +public class ReadStatusController { + private final ReadStatusService readStatusService; + + @PostMapping + public ResponseEntity create(@RequestBody ReadStatusCreateRequest request) { + return ResponseEntity.status(HttpStatus.CREATED).body(readStatusService.create(request)); + } + + @PatchMapping("/{id}") + public ResponseEntity update(@PathVariable UUID id, @RequestBody ReadStatusUpdateRequest request) { + return ResponseEntity.ok(readStatusService.update(id, request)); + } + + @GetMapping("/user/{userId}") + public ResponseEntity> findAllByUserId(@PathVariable UUID userId) { + return ResponseEntity.ok(readStatusService.findAllByUserId(userId)); + } + +} 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..951ef2fc --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/controller/UserController.java @@ -0,0 +1,101 @@ +package com.sprint.mission.discodeit.controller; + +import com.sprint.mission.discodeit.dto.*; +import com.sprint.mission.discodeit.entity.User; +import com.sprint.mission.discodeit.service.UserService; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +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; + +@RestController +@RequestMapping("/api/user") +public class UserController { + + private final UserService userService; + + public UserController(UserService userService) { + this.userService = userService; + } + + @RequestMapping( + path = "create", + method = RequestMethod.POST, + consumes = {MediaType.MULTIPART_FORM_DATA_VALUE} + ) + public ResponseEntity create( + @RequestPart("userCreateRequest") UserCreateRequest userCreateRequest, + @RequestPart(value = "profile", required = false) MultipartFile profile + ) { + Optional profileRequest = Optional.ofNullable(profile) + .flatMap(this::resolveProfileRequest); + + User createdUser = userService.create(userCreateRequest, profileRequest); + + return ResponseEntity + .status(HttpStatus.CREATED) + .body(createdUser); + } + + private Optional resolveProfileRequest(MultipartFile profile) { + if (profile == null || profile.isEmpty()) return Optional.empty(); // null 체크 추가 + try { + return Optional.of(new BinaryContentCreateRequest( + profile.getOriginalFilename(), + profile.getContentType(), + profile.getBytes() + )); + } catch (IOException e) { + throw new RuntimeException("프로필 파일 처리 중 오류 발생", e); + } + } + + @RequestMapping( + path = "/{userId}", + method = RequestMethod.PATCH + ) + public ResponseEntity update( + @PathVariable UUID userId, + @RequestBody UserUpdateRequest request + ) { + + User updatedUser = userService.update(userId, request); + + return ResponseEntity + .status(HttpStatus.OK) + .body(updatedUser); + } + + @RequestMapping( + path = "/{userId}", + method = RequestMethod.DELETE + ) + public ResponseEntity delete(@PathVariable UUID userId) { + userService.delete(userId); + return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); + } + + @RequestMapping(method = RequestMethod.GET) + public ResponseEntity> findAll() { + List users = userService.findAll(); + + return ResponseEntity + .status(HttpStatus.OK) + .body(users); + } + + + + + + + + + +} \ No newline at end of file diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/BinaryContentCreateRequest.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/BinaryContentCreateRequest.java new file mode 100644 index 00000000..be9ae304 --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/BinaryContentCreateRequest.java @@ -0,0 +1,10 @@ +package com.sprint.mission.discodeit.dto; + +import java.io.InputStream; + +public record BinaryContentCreateRequest( + String fileName, + String contentType, + byte[] bytes +) { +} \ No newline at end of file diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/ChannelDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/ChannelDto.java new file mode 100644 index 00000000..9f7abc0b --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/ChannelDto.java @@ -0,0 +1,16 @@ +package com.sprint.mission.discodeit.dto; + +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/UserDto.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserDto.java new file mode 100644 index 00000000..697743a8 --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserDto.java @@ -0,0 +1,15 @@ +package com.sprint.mission.discodeit.dto; + +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 +) { +} \ No newline at end of file diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserStatusResponse.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserStatusResponse.java index 15ad1bbd..d0983da5 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserStatusResponse.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserStatusResponse.java @@ -3,6 +3,7 @@ import com.sprint.mission.discodeit.entity.UserStatus; public record UserStatusResponse( + String userId, String nickname, boolean isOnline, String lastActive @@ -10,15 +11,9 @@ public record UserStatusResponse( public UserStatusResponse(UserStatus userStatus) { this( userStatus.getUserId().toString(), + "haha", userStatus.isOnline(), - userStatus.getUpdatedAt().toString() + userStatus.getLastActiveAt() != null ? userStatus.getLastActiveAt().toString() : "기록 없음" ); } } - -/* 상태 주인 유저의 식별 정보, 5분이내 활동여부 계산결과, 마지막으로 활동했던 시각을 문자열로 표현 - UserStatus 엔티티를 통째로 입력받아 DTO 체우고 레코드가 기본으로 가지고 있는 생성자를 다시 호출하여 - 아래 값을 순서대로 집어넣음. - 유저의 고유 ID를 꺼내서 문자열로 변경해서 담기, 엔티티가 계산한 isOnline의 결과값을 true/false에 담기 - 수정된 시각(Instant)을 문자열로 변환 - */ \ No newline at end of file diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserUpdateRequest.java b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserUpdateRequest.java index 3260b75f..35f8a65d 100644 --- a/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserUpdateRequest.java +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/dto/UserUpdateRequest.java @@ -3,7 +3,7 @@ import java.util.UUID; public record UserUpdateRequest( - String name, + String username, String email, String password, UUID profileImageId 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 f9fcf314..08265e04 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 @@ -2,26 +2,31 @@ import lombok.Getter; +import java.io.Serializable; import java.time.Instant; import java.util.UUID; @Getter -public class BinaryContent { +public class BinaryContent implements Serializable { + private static final long serialVersionUID = 1L; + private final UUID id; private final Instant createdAt; private String fileName; private String contentType; private Long size; + private byte[] bytes; private UUID userId; private UUID messageId; - public BinaryContent(String fileName, String contentType, Long size) { + public BinaryContent(String fileName, String contentType, Long size, byte[] bytes) { this.id = UUID.randomUUID(); this.createdAt = Instant.now(); this.fileName = fileName; this.contentType = contentType; this.size = size; -// this.userId = userId; + this.bytes = bytes; + } } 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 8775e434..6e7bd1fb 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 @@ -16,15 +16,26 @@ public class User implements Serializable { private String email; private String password; private UUID profileId; - //기존 Long 타입을 Instant로 변경하면서 기존 데이터와 충돌하는 상황이 발생했는데 이를 해결하기 위해 기존 데이터를 초기화하고 - //타입을 통일하는 과정을 거침 + private boolean online; // + private Instant lastActiveAt; // - 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 online () { + this.online = true; + this.lastActiveAt = Instant.now(); + } + + public void offline () { + this.online = false; } public void update(String newUsername, String newEmail, String newPassword) { @@ -44,6 +55,8 @@ public void update(String newUsername, String newEmail, String newPassword) { if (anyValueUpdated) { this.updatedAt = Instant.now(); } + } + } // 이름,이메일,비밀번호가 변경되었다면 true updatedAt을 현재시점으로 기록 \ No newline at end of file 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 88e946b5..edaef440 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 @@ -14,7 +14,7 @@ public class UserStatus { private Instant updatedAt; private UUID userId; private String type; - private Instant lastActiveAt; //마지막 활동 시간 필드 + private Instant lastActiveAt; public UserStatus(UUID userId, String type) { this.id = UUID.randomUUID(); diff --git a/discodeit/src/main/java/com/sprint/mission/discodeit/exception/GlobalExceptionHandler.java b/discodeit/src/main/java/com/sprint/mission/discodeit/exception/GlobalExceptionHandler.java new file mode 100644 index 00000000..0744d622 --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/exception/GlobalExceptionHandler.java @@ -0,0 +1,35 @@ +package com.sprint.mission.discodeit.exception; + + +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()); + } +} 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 8ceec6e8..5050f455 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 @@ -7,7 +7,7 @@ import java.util.UUID; public interface ReadStatusRepository { - + List findAllByChannelId(UUID channelId); ReadStatus save(ReadStatus readStatus); Optional findById(UUID id); List findAllByUserId(UUID userId); 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 a973ba1a..a750df7f 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,10 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Repository; +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Optional; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -13,33 +17,84 @@ @Repository @ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "file") public class FileBinaryContentRepository implements BinaryContentRepository { - private final Map data = new ConcurrentHashMap<>(); + 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()); + if (Files.notExists(DIRECTORY)) { + try { + Files.createDirectories(DIRECTORY); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + private Path resolvePath(UUID id) { + return DIRECTORY.resolve(id + EXTENSION); + } @Override public BinaryContent save(BinaryContent binaryContent) { - data.put(binaryContent.getId(), binaryContent); + Path path = resolvePath(binaryContent.getId()); + try ( + FileOutputStream fos = new FileOutputStream(path.toFile()); + ObjectOutputStream oos = new ObjectOutputStream(fos) + ) { + oos.writeObject(binaryContent); + } catch (IOException e) { + throw new RuntimeException(e); + } return binaryContent; } -//입력받은 매개변수 binaryContent id와 값을 data에 입력하고 리턴값으로 binaryContent 받는다 + @Override public Optional findById(UUID id) { - return Optional.ofNullable(data.get(id)); + BinaryContent binaryContentNullable = null; + Path path = resolvePath(id); + if (Files.exists(path)) { + try ( + FileInputStream fis = new FileInputStream(path.toFile()); + ObjectInputStream ois = new ObjectInputStream(fis) + ) { + binaryContentNullable = (BinaryContent) ois.readObject(); + } catch (IOException | ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + return Optional.ofNullable(binaryContentNullable); } - //안에 아무것도 없으면 null반환한다. - //data.get(id) map에서 매개변수로 받은 id를 사용해 data에서 꺼낸다 - @Override public Optional findByUserId(UUID userId) { - return data.values().stream() - .filter(content -> content.getUserId().equals(userId)) - .findFirst(); + try { + return Files.list(DIRECTORY) + .filter(path -> path.toString().endsWith(EXTENSION)) + .map(path -> { + try ( + FileInputStream fis = new FileInputStream(path.toFile()); + ObjectInputStream ois = new ObjectInputStream(fis) + ) { + return (BinaryContent) ois.readObject(); + } catch (IOException | ClassNotFoundException e) { + throw new RuntimeException(e); + } + }) + .filter(content -> content.getUserId().equals(userId)) + .findFirst(); + } catch (IOException e) { + throw new RuntimeException(e); + } } -//data에 저장된 모든 values(객체)를 하나씩 꺼내서 스트림 방식으로 변환한다. -//BinaryContent 객체에서 getUserId(메소드)호출하고 추출한 id가 매개변수로 받은 userid와 값이 같은 가장 첫번째 값을 반환한다 + @Override public void deleteById(UUID id) { - data.remove(id); + Path path = resolvePath(id); + try { + Files.deleteIfExists(path); + } catch (IOException e) { + throw new RuntimeException(e); + } } -} -// data에서 매개변수로 받은 id와 일치하는 key가 있다면 삭제한다 \ No newline at end of file +} \ No newline at end of file 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 7fdc6ae0..5ba24496 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 @@ -61,17 +61,18 @@ public Channel save(Channel channel) { public Optional findById(UUID id) { Channel channelNullable = null; Path path = resolvePath(id); - if (Files.exists(path)) { + if (Files.notExists(path)) { + return Optional.ofNullable(channelNullable); + } else { try ( FileInputStream fis = new FileInputStream(path.toFile()); ObjectInputStream ois = new ObjectInputStream(fis) ) { - channelNullable = (Channel) ois.readObject(); + return Optional.ofNullable((Channel) ois.readObject()); } catch (IOException | ClassNotFoundException e) { throw new RuntimeException(e); } } - return Optional.ofNullable(channelNullable); } /* Channel 타입의 channelNullable에 null을 대입하고 resolvePath메서드의 매개변수로 채널 id를 넣는다 만약 저 경로가 존재할경우 FileOutputStream은 이 프로젝트와 컴퓨터를 연결해서 path를 파일 형태로 바꾸고 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 e6f0c6f0..3ead0a02 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 @@ -5,40 +5,122 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Repository; +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @Repository @ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "file") public class FileReadStatusRepository implements ReadStatusRepository { + private final Path DIRECTORY; + private final String EXTENSION = ".ser"; - private final Map database = new ConcurrentHashMap<>(); + public FileReadStatusRepository() { + this.DIRECTORY = Paths.get(System.getProperty("user.dir"), "file-data-map", ReadStatus.class.getSimpleName()); + if (Files.notExists(DIRECTORY)) { + try { + Files.createDirectories(DIRECTORY); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + private Path resolvePath(UUID id) { + return DIRECTORY.resolve(id + EXTENSION); + } @Override public ReadStatus save(ReadStatus readStatus) { - database.put(readStatus.getId(), readStatus); + Path path = resolvePath(readStatus.getId()); + try ( + FileOutputStream fos = new FileOutputStream(path.toFile()); + ObjectOutputStream oos = new ObjectOutputStream(fos) + ) { + oos.writeObject(readStatus); + } catch (IOException e) { + throw new RuntimeException(e); + } return readStatus; } @Override public List findAllByUserId(UUID userId) { - return database.values().stream() - .filter(status -> status.getUserId().equals(userId)) - .toList(); + try { + return Files.list(DIRECTORY) + .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(status -> status.getUserId().equals(userId)) + .toList(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public List findAllByChannelId(UUID channelId) { + try { + return Files.list(DIRECTORY) + .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(status -> status.getChannelId().equals(channelId)) + .toList(); + } catch (IOException e) { + throw new RuntimeException(e); + } } @Override public Optional findById(UUID id) { - return Optional.ofNullable(database.get(id)); + ReadStatus readStatusNullable = null; + Path path = resolvePath(id); + if (Files.exists(path)) { + try ( + FileInputStream fis = new FileInputStream(path.toFile()); + ObjectInputStream ois = new ObjectInputStream(fis) + ) { + readStatusNullable = (ReadStatus) ois.readObject(); + } catch (IOException | ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + return Optional.ofNullable(readStatusNullable); } @Override public boolean existsById(UUID id) { - return database.containsKey(id); + Path path = resolvePath(id); + return Files.exists(path); } @Override public void deleteById(UUID id) { - database.remove(id); + Path path = resolvePath(id); + try { + Files.deleteIfExists(path); + } catch (IOException e) { + throw new RuntimeException(e); + } } } \ No newline at end of file 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 similarity index 91% rename from discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JcfReadStatusRepository.java rename to discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFReadStatusRepository.java index c84bda25..649348ec 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 @@ -9,9 +9,14 @@ @Repository @ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "jcf", matchIfMissing = true) -public class JcfReadStatusRepository implements ReadStatusRepository { +public class JCFReadStatusRepository implements ReadStatusRepository { private final Map database = new HashMap<>(); + @Override + public List findAllByChannelId(UUID channelId) { + return List.of(); + } + @Override public ReadStatus save(ReadStatus readStatus) { database.put(readStatus.getId(), readStatus); 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 similarity index 97% rename from discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JcfUserStatusRepository.java rename to discodeit/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserStatusRepository.java index 07e49548..0c31bc3a 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 @@ -12,7 +12,7 @@ @Repository @ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "jcf", matchIfMissing = true) -public class JcfUserStatusRepository implements UserStatusRepository { +public class JCFUserStatusRepository implements UserStatusRepository { private final List userStatuses = new ArrayList<>(); @Override 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..7efad1e5 --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/BinaryContentService.java @@ -0,0 +1,13 @@ +package com.sprint.mission.discodeit.service; + +import com.sprint.mission.discodeit.dto.BinaryContentCreateRequest; +import com.sprint.mission.discodeit.entity.BinaryContent; + +import java.util.List; +import java.util.UUID; + +public interface BinaryContentService { + BinaryContent create(BinaryContentCreateRequest request); + BinaryContent find(UUID id); + List findAllByIds(List ids); +} \ No newline at end of file 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 e47ca48f..87996bef 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,6 +1,7 @@ package com.sprint.mission.discodeit.service; import com.sprint.mission.discodeit.dto.ChannelCreateRequest; +import com.sprint.mission.discodeit.dto.ChannelDto; import com.sprint.mission.discodeit.dto.ChannelUpdateRequest; import com.sprint.mission.discodeit.entity.Channel; import com.sprint.mission.discodeit.entity.ChannelType; @@ -12,7 +13,7 @@ public interface ChannelService { Channel createPublic(ChannelCreateRequest request); Channel createPrivate(ChannelCreateRequest request); Channel find(UUID id); - List findAllByUserId(UUID userId); + List findAllByUserId(UUID userId); Channel update(UUID id, ChannelUpdateRequest request); void delete(UUID id); } 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 76c471cc..81bda645 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,18 +1,18 @@ package com.sprint.mission.discodeit.service; -import com.sprint.mission.discodeit.dto.UserCreateRequest; -import com.sprint.mission.discodeit.dto.UserStatusResponse; -import com.sprint.mission.discodeit.dto.UserUpdateRequest; +import com.sprint.mission.discodeit.dto.*; import com.sprint.mission.discodeit.entity.User; import java.util.List; +import java.util.Optional; import java.util.UUID; public interface UserService { - User create(UserCreateRequest request); + User create(UserCreateRequest request, Optional profileRequest); UserStatusResponse find(UUID userId); List findAll(); User update(UUID userId, UserUpdateRequest request); void delete(UUID userId); + } 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 9202cf70..62dc0811 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,8 +2,11 @@ import com.sprint.mission.discodeit.dto.LoginRequest; 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.UserService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -14,16 +17,20 @@ @RequiredArgsConstructor public class BasicAuthService implements AuthService { private final UserRepository userRepository; + private final UserStatusRepository userStatusRepository; @Override public User login(LoginRequest request) { - - return userRepository.findByUsername(request.username()) - .filter(user -> user.getPassword().equals(request.password())) + User user = userRepository.findByUsername(request.username()) + .filter(u -> u.getPassword().equals(request.password())) .orElseThrow(() -> new NoSuchElementException("아이디 또는 비밀번호가 일치하지 않습니다.")); + + UserStatus status = userStatusRepository.findByUserId(user.getId()) + .orElseGet(() -> new UserStatus(user.getId())); + + status.update("ONLINE"); + userStatusRepository.save(status); + + return user; } } - -/*findByUsername 메소드를 호출해서 매개변수에 request.username을 넣고 반환된 user를 필터링을 통해 해당 유저의 비밀번호와 -매개변수로 받은 request의 비밀번호가 같을시 true 아니면 오류를 반환한다 - */ \ 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..56012a1c --- /dev/null +++ b/discodeit/src/main/java/com/sprint/mission/discodeit/service/basic/BasicBinaryContentService.java @@ -0,0 +1,48 @@ +package com.sprint.mission.discodeit.service.basic; + +import com.sprint.mission.discodeit.dto.BinaryContentCreateRequest; +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; + +import java.util.Base64; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.UUID; + +@Service +@RequiredArgsConstructor +public class BasicBinaryContentService implements BinaryContentService { + private final BinaryContentRepository binaryContentRepository; + + @Override + public BinaryContent create(BinaryContentCreateRequest request) { + byte[] originalBytes = request.bytes(); + BinaryContent content = new BinaryContent( + request.fileName(), + request.contentType(), + (long) originalBytes.length, + originalBytes + ); + return binaryContentRepository.save(content); + } + + @Override + public BinaryContent find(UUID id) { + return getBinaryContent(id); + } + + @Override + public List findAllByIds(List ids) { + return ids.stream() + .map(this::find) // 각 ID를 find 메서드에 넣어 BinaryContent로 변환 + .toList(); + } + + public BinaryContent getBinaryContent(UUID id) { + return binaryContentRepository.findById(id) + .orElseThrow(() -> new NoSuchElementException("파일을 찾을 수 없습니다.")); + } +} \ No newline at end of file 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 c4d10d6b..3efc8468 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,9 +1,11 @@ package com.sprint.mission.discodeit.service.basic; import com.sprint.mission.discodeit.dto.ChannelCreateRequest; +import com.sprint.mission.discodeit.dto.ChannelDto; import com.sprint.mission.discodeit.dto.ChannelUpdateRequest; import com.sprint.mission.discodeit.entity.Channel; import com.sprint.mission.discodeit.entity.ChannelType; +import com.sprint.mission.discodeit.entity.Message; import com.sprint.mission.discodeit.entity.ReadStatus; import com.sprint.mission.discodeit.repository.ChannelRepository; import com.sprint.mission.discodeit.repository.MessageRepository; @@ -12,9 +14,9 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.UUID; +import java.time.Instant; +import java.util.*; + @Service @RequiredArgsConstructor public class BasicChannelService implements ChannelService { @@ -44,18 +46,18 @@ public Channel createPrivate(ChannelCreateRequest request) { } return savedChannel; } -/*매개변수로 ChannelCreateRequest를 받아온다 -channeltype이 private이고 이름과 설명은 null인 새 채널을 만든다 -channelRepository.save 메소드에 channel을 넣고 리턴값으로 받아온 반환값을 savechannel에 담는다 -만약에 매개변수로 받은 request의 memverIds가 null이 아닐때 for문을 실행한다 -for문을 돌면서 각 객체의 UUID, savedChannel.getid(),null로 readStatus를 새로 생성해서 readStatusRepository.save -메서드로 저장한다. 리턴값으로 savedChannel 반환한다 - */ + @Override - public List findAllByUserId(UUID userId) { + public List findAllByUserId(UUID userId) { return channelRepository.findAll().stream() - .filter(channel -> channel.containsUser(userId)) + .filter(channel -> { + if (channel.getType() == ChannelType.PUBLIC) return true; + + return readStatusRepository.findAllByChannelId(channel.getId()).stream() + .anyMatch(rs -> rs.getUserId().equals(userId)); + }) + .map(this::toDto) .toList(); } /*매개변수로 userId를 받아온다. 매개변수로 받아온 userId로 Chnnel에 있는 모든 데이터를 가져와서 스트림 형식으로 바꾼다 @@ -70,12 +72,7 @@ public Channel update(UUID channelId, ChannelUpdateRequest request) { channel.update(request.name(), request.description()); return channelRepository.save(channel); } -/* 매개변수로 channelId와 ChannelUpdaterequest를 받아온다 -channelRepository의 findById메소드에 channelId를 넣고 리턴값으로 반환된 채널객체를 channel에 넣는다 -만약 매개변수로 입력한 ChannelId로 반환된 리턴값이 없으면 오류를 던진다 -channel의 이름과 설명을 request로 받아온 이름과 설명으로 update 메소드를 통해 변경한다 -channelRepository.save메서드의 리턴값을 반환한다 - */ + @Override public void delete(UUID channelId) { if (!channelRepository.existsById(channelId)) { @@ -91,6 +88,31 @@ public Channel find(UUID channelId) { return channelRepository.findById(channelId) .orElseThrow(() -> new NoSuchElementException("Channel with id " + channelId + " not found")); } + + private ChannelDto toDto(Channel channel) { + // 참여자 명단 가져오기 (비공개 채널일 때만 ReadStatus를 뒤져서 가져옴) + List participantIds = new ArrayList<>(); + if (channel.getType().equals(ChannelType.PRIVATE)) { + readStatusRepository.findAllByChannelId(channel.getId()).stream() + .map(ReadStatus::getUserId) + .forEach(participantIds::add); + } + + // 마지막 메시지 시간 찾기 (메시지가 없으면 아주 옛날 시간으로 설정) + Instant lastMessageAt = messageRepository.findAllByChannelId(channel.getId()) + .stream() + .sorted(Comparator.comparing(Message::getCreatedAt).reversed()) + .map(Message::getCreatedAt) + .findFirst() + .orElse(Instant.MIN); + + return new ChannelDto( + channel.getId(), + channel.getType(), + channel.getName(), + channel.getDescription(), + participantIds, + lastMessageAt + ); + } } -//매개변수로 ChannelId를 받아서 channelRepository.findById메서드를 호출하고 리턴값으로 반환된 -//채널이 있으면 그 채널을 반환하고 없을시 오류를 생성한다 \ No newline at end of file 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 06720909..c828e1c8 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 @@ -62,7 +62,6 @@ public List findAllByChannelId(UUID channelId) { public Message update(UUID messageId, MessageUpdateRequest request) { Message message = messageRepository.findById(messageId) .orElseThrow(() -> new NoSuchElementException("Message not found")); - message.update(request.content()); return messageRepository.save(message); } 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 bb07570d..9a7ada5a 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 @@ -23,6 +23,12 @@ public class BasicReadStatusService implements ReadStatusService { @Override public ReadStatus create(ReadStatusCreateRequest request) { + if (!userRepository.existsById(request.userId())) { + throw new NoSuchElementException("해당 유저를 찾을 수 없습니다. ID: " + request.userId()); + } + if (!channelRepository.existsById(request.channelId())) { + throw new NoSuchElementException("해당 채널을 찾을 수 없습니다. ID: " + request.channelId()); + } boolean isDuplicate = readStatusRepository.findAllByUserId(request.userId()).stream() .anyMatch(rs -> rs.getChannelId().equals(request.channelId())); @@ -36,11 +42,7 @@ public ReadStatus create(ReadStatusCreateRequest request) { ); return readStatusRepository.save(readStatus); } -/* readStatusRepository에 findAllByUserId의 매개변수로 request.userId로 넣어서 스트림 방식으로 변환하고 -만약에 매개변수로 받은 request.channelid와 스트림방식으로 변환했던 데이터의 Channelid가 같으면 -오류를 발생시킨다 아닐시 new ReadStatus를 생성한다 -readStatusRepository.save의 리턴값을 반환한다 - */ + @Override public ReadStatus find(UUID id) { return readStatusRepository.findById(id) 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 10435218..10bdf469 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,18 +1,19 @@ package com.sprint.mission.discodeit.service.basic; -import com.sprint.mission.discodeit.dto.UserCreateRequest; -import com.sprint.mission.discodeit.dto.UserStatusResponse; -import com.sprint.mission.discodeit.dto.UserUpdateRequest; +import com.sprint.mission.discodeit.dto.*; +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.UserRepository; import com.sprint.mission.discodeit.repository.UserStatusRepository; +import com.sprint.mission.discodeit.service.BinaryContentService; import com.sprint.mission.discodeit.service.UserService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import java.util.List; import java.util.NoSuchElementException; +import java.util.Optional; import java.util.UUID; import com.sprint.mission.discodeit.dto.UserCreateRequest; @Service @@ -20,42 +21,49 @@ public class BasicUserService implements UserService { private final UserRepository userRepository; private final UserStatusRepository userStatusRepository; + private final BinaryContentService binaryContentService; @Override - public User create(UserCreateRequest request) { - User user = new User(request.username(), request.email(), request.password()); + public User create(UserCreateRequest request, Optional profileRequest) { + UUID profileId = profileRequest.map(req -> { + BinaryContent savedContent = binaryContentService.create(req); + return savedContent.getId(); + }).orElse(null); + User user = new User( + request.username(), + request.email(), + request.password(), + profileId + ); + User savedUser = userRepository.save(user); - UserStatus status = new UserStatus(savedUser.getId()); - userStatusRepository.save(status); + userStatusRepository.save(new UserStatus(savedUser.getId())); return savedUser; } -/* 매개변수로 UserCreateRequest를 받아서 request에 이름,이메일,비밀번호로 새유저를 만들고 -userRepository.save메서드의 매개변수로 user를 넣어서 리턴값으로 반환된 user를 saveduser로 저장한다 -savedUser의 id로 new UserStatus로 만들어서 status로 저장하고 userStatusRepository.save의 메서드 매개변수로 -입력해서 호출한다 return savedUser반환한다 -*/ + @Override public UserStatusResponse find(UUID userId) { User user = userRepository.findById(userId) .orElseThrow(() -> new NoSuchElementException("User not found")); + UserStatus status = userStatusRepository.findByUserId(userId) - .orElseThrow(() -> new NoSuchElementException("User status not found")); + .orElseGet(() -> { + return new UserStatus(userId); + }); return new UserStatusResponse( + user.getId().toString(), user.getUsername(), status.isOnline(), - status.getUpdatedAt().toString() + status.getUpdatedAt() != null ? status.getUpdatedAt().toString() : "N/A" ); } -/*매개변수로 userId를 받는다 userRepository.findById메서드에 userId를 매개변수로 입력해서 리턴값으로 반환된 User객체를 user에넣는다 -만약에 반환된 user가 없으면 에러를 던진다 userStatusRepository.findById메서드에 userId를 매개변수로 입력해서 리턴값으로 반환된 UserStatus객체를 status에넣는다 -만약에 반환된 UserStatus가 없으면 에러를 던진다 리턴값으로 new UserStatusResponse객체를 반환한다 UserStatusResponse의 값은 위에서 찾은 user의 이름과 -status의 온라인상태,업데이트 시간이 들어간다 - */ + @Override public List findAll() { return userRepository.findAll().stream() + //.limit(2) .map(user -> find(user.getId())) .toList(); } @@ -67,16 +75,12 @@ public User update(UUID userId, UserUpdateRequest request) { User user = userRepository.findById(userId) .orElseThrow(() -> new NoSuchElementException("User not found")); user.update( - request.name(), + request.username(), request.email(), request.password() ); return userRepository.save(user); } -/* 매개변수로 userid와 UserUpdateRequest를 받는다 userRepository.findById메서드에 useerid를 매개변수로 입력해서 리턴값으로 반환된 User객체를 -user에 넣는다 반환된 user가 없으면 에러를 던진다. user.update메서드를 통해 request에 담긴 이름,이메일,비밀번호로 변경한다. -리턴값으로 userRepository.save 메서드의 반환값을 보낸다 - */ @Override public void delete(UUID userId) { @@ -86,8 +90,5 @@ public void delete(UUID userId) { userStatusRepository.deleteByUserId(userId); userRepository.deleteById(userId); } + } -/* 매개변수로 userid를 받아서 userRepository.existsById메서드로 userId가 있는지 확인하고 없으면 에러를 던진다 -userStatusRepository.deleteByUserId메서드와 -userRepository.deleteById메서드를 통해서 userStatusRepository와 userRepository값을 삭제한다 - */ \ No newline at end of file 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 index b37ce685..22138e82 100644 --- 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 @@ -33,22 +33,17 @@ public String create(UserStatusCreateRequest request) { return savedStatus.getId().toString(); } -/* 매개변수로 UserStatusCreateRequest를 받아서 만약에 request.userId가 userRepository.existsById 메서드로 못찾았을때 -에러를 반환한다 userRepository.existbyuserId 메서드에 매개변수로 request.userId를 입력해서 상태정보가 존재하는지 확인하고 이미 존재하면 에러 던진다 - - */ @Override public UserStatusResponse find(UUID userId) { UserStatus userStatus = userStatusRepository.findByUserId(userId) - .orElseThrow(() -> new RuntimeException("해당 유저의 상태 정보를 찾을 수 없습니다.")); + .orElseGet(() -> { + return new UserStatus(userId); + }); return new UserStatusResponse(userStatus); } -/* 매개변수로 userId를 받는다 userStatusRepository.findByUserId 메서드에 userId를 매개변수로 입력해서 반환된 UserStatus객체를 -userStatus에 넣는다 반환된 userStatus가 있으면 그 userStatus를 반환하고 없을시 오류를 생성한다 -userStatus를 UserStatusResponse로 변환하여 리턴값으로 반환한다 - */ + @Override public List findAll() { return userStatusRepository.findAll().stream()