diff --git a/build.gradle b/build.gradle index 7c8911023..9b7100fe8 100644 --- a/build.gradle +++ b/build.gradle @@ -25,6 +25,11 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' + //Spring Data JPA와 PostgreSQL을 위한 의존성을 추가하세요. + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' +// implementation 'org.mapstruct:mapstruct:1.4.2.Final' +// annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final' + runtimeOnly 'org.postgresql:postgresql:42.6.0' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..ee33e2c97 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,16 @@ +version: '3' +services: + postgres-db: + image: postgres:15 + container_name: discodeit + environment: + POSTGRES_USER: discodeit_user + POSTGRES_PASSWORD: discodeit1234 + POSTGRES_DB: discodeit + ports: + - "5432:5432" + volumes: + - db_data:/var/lib/postgresql/data + +volumes: + db_data: \ No newline at end of file diff --git a/src/main/java/com/sprint/mission/discodeit/DiscodeitApplication.java b/src/main/java/com/sprint/mission/discodeit/DiscodeitApplication.java index fd82d2302..9383e68de 100644 --- a/src/main/java/com/sprint/mission/discodeit/DiscodeitApplication.java +++ b/src/main/java/com/sprint/mission/discodeit/DiscodeitApplication.java @@ -2,175 +2,14 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +@EnableJpaAuditing @SpringBootApplication public class DiscodeitApplication { - public static void main(String[] args) { - SpringApplication.run(DiscodeitApplication.class, args); -// -// // 서비스 초기화 -// UserService userService = context.getBean(UserService.class); -// ChannelService channelService = context.getBean(ChannelService.class); -// MessageService messageService = context.getBean(MessageService.class); -// -// // User Create Test -// try{ -// UserCreateRequest userCreateRequest = new UserCreateRequest("woody", "woody@codeit.com", "1234", Optional.empty()); -// UserCreateResponse user = userService.create(userCreateRequest); -// System.out.println("SignUp User: " + user); -// }catch(Exception e){ -// System.out.println(e.getMessage()); -// } -// -// // 동일 이름으로 생성 Test -// try { -// UserCreateRequest userCreateRequest = new UserCreateRequest("woody", "buzz@codeit.com", "1234", Optional.empty()); -// UserCreateResponse user = userService.create(userCreateRequest); -// System.out.println("user = " + user); -// }catch(Exception e){ -// System.out.println("중복 이름 에러: " + e.getMessage()); -// } -// -// // 동일 이메일 생성 Test -// try{ -// UserCreateRequest userCreateRequest = new UserCreateRequest("buzz", "woody@codeit.com", "1234", Optional.empty()); -// UserCreateResponse user = userService.create(userCreateRequest); -// System.out.println("user = " + user); -// }catch(Exception e){ -// System.out.println("중복 이메일 에러: " + e.getMessage()); -// } -// -// // User find,findAll Test -// try{ -// UserCreateRequest userCreateRequest = new UserCreateRequest("potato", "potato@codeit.com", "1234", Optional.empty()); -// UserCreateResponse user = userService.create(userCreateRequest); -// -// System.out.println("find user: " + userService.find(user.id())); -// System.out.println("findAll: " + userService.findAll()); -// -// // 저장 안 된 유저 조회 -// System.out.println(userService.find(UUID.randomUUID())); -// }catch(Exception e){ -// System.out.println("없는 유저 조회: " + e.getMessage()); -// } -// -// // User update/delete Test -// try{ -// UserCreateRequest userCreateRequest = new UserCreateRequest("rex", "rex@codeit.com", "1234", Optional.empty()); -// UserCreateResponse user = userService.create(userCreateRequest); -// System.out.println("before update: " + userService.find(user.id())); -// -// userService.update(new UserUpdateRequest(user.id(), Optional.of("T-rex"), Optional.empty(), Optional.of("abcd"), Optional.empty())); -// System.out.println("after update: " + userService.find(user.id())); -// -// userService.delete(user.id()); -// System.out.println("after delete find: " + userService.find(user.id())); -// }catch(Exception e){ -// System.out.println("삭제 후 없는 유저 조회: " + e.getMessage()); -// } -// -// // Test Setting -// User userA = new User("mike", "mike@codeit.com", "1234"); -// User userB = new User("troll", "troll@codeit.com", "1234"); -// List users = List.of(userA, userB); -// -// // Channel(Private/Public) Create,Find Test -// try{ -// // Create -// PrivateChannelCreateRequest privateChannelCreateRequest = new PrivateChannelCreateRequest(users); -// PrivateChannelCreateResponse privateChannel = channelService.createPrivate(privateChannelCreateRequest); -// System.out.println("\nprivateChannel: " + privateChannel); -// -// PublicChannelCreateRequest publicChannelCreateRequest = new PublicChannelCreateRequest("codeit", "codeit 채널입니다."); -// PublicChannelCreateResponse publicChannel = channelService.createPublic(publicChannelCreateRequest); -// System.out.println("publicChannel: " + publicChannel); -// -// // Find -// System.out.println("privateChannelFind: " + channelService.find(privateChannel.id())); -// System.out.println("publicChannelFind: " + channelService.find(publicChannel.id())); -// -// // FindAllByUserId -// List findAllByUserId = channelService.findAllByUserId(userA.getId()); -// System.out.println("FindAllByUserId: " + findAllByUserId); -// -// // 없는 id로 조회 -// System.out.println(channelService.find(UUID.randomUUID())); -// -// }catch(Exception e) { -// System.out.println("없는 id로 조회: " + e.getMessage()); -// } -// -// // Test Setting -// PublicChannelCreateRequest publicChannelCreateRequest = new PublicChannelCreateRequest("sprint", "sprint 채널입니다."); -// PublicChannelCreateResponse publicChannel = channelService.createPublic(publicChannelCreateRequest); -// -// // Update/Delete Test -// try{ -// System.out.println("before update: " + publicChannel); -// -// ChannelUpdateDto updateDto = new ChannelUpdateDto(publicChannel.id(), "aws", "aws 채널입니다."); -// channelService.update(updateDto); -// -// System.out.println("after update: " + channelService.find(publicChannel.id())); -// -// channelService.delete(publicChannel.id()); -// System.out.println(channelService.find(publicChannel.id())); -// -// }catch(Exception e){ -// System.out.println("삭제 후 채널 조회: " + e.getMessage()); -// } -// -// // Test Setting -// PublicChannelCreateRequest request = new PublicChannelCreateRequest("messageTest", "messageTest 채널입니다."); -// PublicChannelCreateResponse channel = channelService.createPublic(request); -// -// UserCreateRequest userCreateRequest = new UserCreateRequest(userA.getUsername(), userA.getEmail(), userA.getPassword(), Optional.empty()); -// UserCreateResponse messageTestUserA = userService.create(userCreateRequest); -// -// userCreateRequest = new UserCreateRequest(userB.getUsername(), userB.getEmail(), userB.getPassword(), Optional.empty()); -// UserCreateResponse messageTestUserB = userService.create(userCreateRequest); -// -// // Message Create, FindAllByChannelId Test -// try{ -// MessageCreateRequest messageCreateRequest = new MessageCreateRequest(channel.id(), messageTestUserA.id(), "messageA Content"); -// MessageCreateResponse messageA = messageService.create(messageCreateRequest); -// System.out.println("\nmessageA: " + messageA); -// -// messageCreateRequest = new MessageCreateRequest(channel.id(), messageTestUserB.id(), "messageB Content"); -// MessageCreateResponse messageB = messageService.create(messageCreateRequest); -// System.out.println("messageB: " + messageB); -// -// List messages = messageService.findAllByChannelId(channel.id()); -// System.out.println("messages: " + messages); -// -// System.out.println(messageService.findAllByChannelId(UUID.randomUUID())); -// -// } catch (Exception e) { -// System.out.println("없는 channelId로 조회: " + e.getMessage()); -// } -// -// // Test setting -// MessageCreateRequest messageCreateRequest = new MessageCreateRequest(channel.id(), messageTestUserA.id(), "TestMessage Content"); -// MessageCreateResponse TestMessage = messageService.create(messageCreateRequest); -// -// // Update/Delete Test -// try{ -// System.out.println("before update: " + TestMessage); -// -// MessageUpdateDto messageUpdateDto = new MessageUpdateDto(TestMessage.id(), "Update Content"); -// messageUpdateDto = messageService.update(messageUpdateDto); -// System.out.println("after update: " + messageUpdateDto); -// -// System.out.println("before delete: " + messageService.findAllByChannelId(channel.id())); -// messageService.delete(messageUpdateDto.id()); -// System.out.println("after delete: " + messageService.findAllByChannelId(channel.id())); -// -// messageUpdateDto = new MessageUpdateDto(UUID.randomUUID(), "Update Content"); -// System.out.println(messageService.update(messageUpdateDto)); -// -// } catch (Exception e) { -// System.out.println("없는 id로 업데이트: " + e.getMessage()); -// } - } + public static void main(String[] args) { + SpringApplication.run(DiscodeitApplication.class, args); + + } } diff --git a/src/main/java/com/sprint/mission/discodeit/JavaApplication.java b/src/main/java/com/sprint/mission/discodeit/JavaApplication.java index 2ef76a899..1d4ebc9d9 100644 --- a/src/main/java/com/sprint/mission/discodeit/JavaApplication.java +++ b/src/main/java/com/sprint/mission/discodeit/JavaApplication.java @@ -11,9 +11,9 @@ //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.service.basic.ChannelService; -//import com.sprint.mission.discodeit.service.basic.MessageService; -//import com.sprint.mission.discodeit.service.basic.UserService; +//import com.sprint.mission.discodeit.service.ChannelService; +//import com.sprint.mission.discodeit.service.MessageService; +//import com.sprint.mission.discodeit.service.UserService; //import com.sprint.mission.discodeit.service.basic.BasicChannelService; //import com.sprint.mission.discodeit.service.basic.BasicMessageService; //import com.sprint.mission.discodeit.service.basic.BasicUserService; diff --git a/src/main/java/com/sprint/mission/discodeit/config/AppConfig.java b/src/main/java/com/sprint/mission/discodeit/config/AppConfig.java new file mode 100644 index 000000000..fc7c6dbdd --- /dev/null +++ b/src/main/java/com/sprint/mission/discodeit/config/AppConfig.java @@ -0,0 +1,10 @@ +package com.sprint.mission.discodeit.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; + +@Configuration +@EnableJpaAuditing +public class AppConfig { + +} diff --git a/src/main/java/com/sprint/mission/discodeit/controller/basic/BasicChannelController.java b/src/main/java/com/sprint/mission/discodeit/controller/basic/BasicChannelController.java index 41283f324..d367d5dd9 100644 --- a/src/main/java/com/sprint/mission/discodeit/controller/basic/BasicChannelController.java +++ b/src/main/java/com/sprint/mission/discodeit/controller/basic/BasicChannelController.java @@ -5,7 +5,7 @@ import com.sprint.mission.discodeit.dto.request.PublicChannelCreateRequest; import com.sprint.mission.discodeit.dto.request.PublicChannelUpdateRequest; import com.sprint.mission.discodeit.entity.Channel; -import com.sprint.mission.discodeit.service.basic.ChannelService; +import com.sprint.mission.discodeit.service.ChannelService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.responses.ApiResponse; diff --git a/src/main/java/com/sprint/mission/discodeit/controller/basic/BasicMessageController.java b/src/main/java/com/sprint/mission/discodeit/controller/basic/BasicMessageController.java index 97f5aa1af..b1ef5827a 100644 --- a/src/main/java/com/sprint/mission/discodeit/controller/basic/BasicMessageController.java +++ b/src/main/java/com/sprint/mission/discodeit/controller/basic/BasicMessageController.java @@ -6,14 +6,10 @@ import com.sprint.mission.discodeit.dto.request.MessageCreateRequest; import com.sprint.mission.discodeit.dto.request.BinaryContentCreateRequest; import com.sprint.mission.discodeit.entity.Message; -import com.sprint.mission.discodeit.service.basic.MessageService; +import com.sprint.mission.discodeit.service.MessageService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.enums.ParameterIn; -import io.swagger.v3.oas.annotations.media.ArraySchema; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.ExampleObject; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; diff --git a/src/main/java/com/sprint/mission/discodeit/controller/basic/BasicUserController.java b/src/main/java/com/sprint/mission/discodeit/controller/basic/BasicUserController.java index 9abebe04b..af4d3ed3c 100644 --- a/src/main/java/com/sprint/mission/discodeit/controller/basic/BasicUserController.java +++ b/src/main/java/com/sprint/mission/discodeit/controller/basic/BasicUserController.java @@ -8,7 +8,7 @@ import com.sprint.mission.discodeit.dto.request.UserUpdateRequest; import com.sprint.mission.discodeit.entity.User; import com.sprint.mission.discodeit.entity.UserStatus; -import com.sprint.mission.discodeit.service.basic.UserService; +import com.sprint.mission.discodeit.service.UserService; import com.sprint.mission.discodeit.service.basic.UserStatusService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; diff --git a/src/main/java/com/sprint/mission/discodeit/dto/data/BinaryContentDto.java b/src/main/java/com/sprint/mission/discodeit/dto/data/BinaryContentDto.java new file mode 100644 index 000000000..d44aee484 --- /dev/null +++ b/src/main/java/com/sprint/mission/discodeit/dto/data/BinaryContentDto.java @@ -0,0 +1,12 @@ +package com.sprint.mission.discodeit.dto.data; + +import java.util.UUID; + +public record BinaryContentDto( + UUID id, + String fileName, + Long size, + String contentType +) { + +} diff --git a/src/main/java/com/sprint/mission/discodeit/dto/data/ChannelDto.java b/src/main/java/com/sprint/mission/discodeit/dto/data/ChannelDto.java index 2005105e1..f3f099dca 100644 --- a/src/main/java/com/sprint/mission/discodeit/dto/data/ChannelDto.java +++ b/src/main/java/com/sprint/mission/discodeit/dto/data/ChannelDto.java @@ -7,11 +7,12 @@ import java.util.UUID; public record ChannelDto( - UUID id, - ChannelType type, - String name, - String description, - List participantIds, - Instant lastMessageAt + UUID id, + ChannelType type, + String name, + String description, + List participantIds, + Instant lastMessageAt ) { + } diff --git a/src/main/java/com/sprint/mission/discodeit/dto/data/MessageDto.java b/src/main/java/com/sprint/mission/discodeit/dto/data/MessageDto.java new file mode 100644 index 000000000..6bcaa0907 --- /dev/null +++ b/src/main/java/com/sprint/mission/discodeit/dto/data/MessageDto.java @@ -0,0 +1,17 @@ +package com.sprint.mission.discodeit.dto.data; + +import java.time.Instant; +import java.util.List; +import java.util.UUID; + +public record MessageDto( + UUID id, + Instant createdAt, + Instant updatedAt, + String content, + UUID channelId, + UserDto author, + List attachments +) { + +} diff --git a/src/main/java/com/sprint/mission/discodeit/dto/data/ReadStatusDto.java b/src/main/java/com/sprint/mission/discodeit/dto/data/ReadStatusDto.java new file mode 100644 index 000000000..1d0bc2c12 --- /dev/null +++ b/src/main/java/com/sprint/mission/discodeit/dto/data/ReadStatusDto.java @@ -0,0 +1,13 @@ +package com.sprint.mission.discodeit.dto.data; + +import java.time.Instant; +import java.util.UUID; + +public record ReadStatusDto( + UUID id, + UUID userId, + UUID channelId, + Instant lastReadAt +) { + +} diff --git a/src/main/java/com/sprint/mission/discodeit/dto/data/UserDto.java b/src/main/java/com/sprint/mission/discodeit/dto/data/UserDto.java index d7911dea3..2f6e837ac 100644 --- a/src/main/java/com/sprint/mission/discodeit/dto/data/UserDto.java +++ b/src/main/java/com/sprint/mission/discodeit/dto/data/UserDto.java @@ -1,15 +1,14 @@ package com.sprint.mission.discodeit.dto.data; +import com.sprint.mission.discodeit.entity.BinaryContent; import java.time.Instant; import java.util.UUID; public record UserDto( UUID id, - Instant createdAt, - Instant updatedAt, String username, String email, - UUID profileId, + BinaryContentDto profile, Boolean online ) { diff --git a/src/main/java/com/sprint/mission/discodeit/dto/data/UserStatusDto.java b/src/main/java/com/sprint/mission/discodeit/dto/data/UserStatusDto.java new file mode 100644 index 000000000..0d7395d9b --- /dev/null +++ b/src/main/java/com/sprint/mission/discodeit/dto/data/UserStatusDto.java @@ -0,0 +1,12 @@ +package com.sprint.mission.discodeit.dto.data; + +import java.time.Instant; +import java.util.UUID; + +public record UserStatusDto( + UUID id, + UUID userId, + Instant lastActiveAt +) { + +} diff --git a/src/main/java/com/sprint/mission/discodeit/dto/request/BinaryContentCreateRequest.java b/src/main/java/com/sprint/mission/discodeit/dto/request/BinaryContentCreateRequest.java index 570451680..dbd0f44ca 100644 --- a/src/main/java/com/sprint/mission/discodeit/dto/request/BinaryContentCreateRequest.java +++ b/src/main/java/com/sprint/mission/discodeit/dto/request/BinaryContentCreateRequest.java @@ -1,7 +1,11 @@ package com.sprint.mission.discodeit.dto.request; +import java.util.UUID; + public record BinaryContentCreateRequest( - String fileName, - String contentType, - byte[] bytes -) {} + String fileName, + String contentType, + byte[] bytes +) { + +} diff --git a/src/main/java/com/sprint/mission/discodeit/entity/BinaryContent.java b/src/main/java/com/sprint/mission/discodeit/entity/BinaryContent.java index fe358044e..d46fc8d3a 100644 --- a/src/main/java/com/sprint/mission/discodeit/entity/BinaryContent.java +++ b/src/main/java/com/sprint/mission/discodeit/entity/BinaryContent.java @@ -1,28 +1,31 @@ package com.sprint.mission.discodeit.entity; +import com.sprint.mission.discodeit.entity.base.BaseEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import lombok.AccessLevel; import lombok.Getter; - -import java.time.Instant; -import java.util.UUID; -import java.io.Serializable; +import lombok.NoArgsConstructor; @Getter -public class BinaryContent implements Serializable { - private static final long serialVersionUID = 1L; +@Table(name = "binary_contents") +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class BinaryContent extends BaseEntity { + + @Column(name = "file_name", nullable = false, length = 255) + private String fileName; + + @Column(name = "size", nullable = false) + private Long size; - private final UUID id; - private final Instant createdAt; - private String fileName; - private Long size; - private String contentType; - private byte[] bytes; + @Column(name = "content_type", nullable = false, length = 100) + private String contentType; - public BinaryContent(String fileName, Long size, String contentType, byte[] bytes) { - this.id = UUID.randomUUID(); - this.createdAt = Instant.now(); - this.fileName = fileName; - this.size = size; - this.contentType = contentType; - this.bytes = bytes; - } + public BinaryContent(String fileName, Long size, String contentType) { + this.fileName = fileName; + this.size = size; + this.contentType = contentType; + } } diff --git a/src/main/java/com/sprint/mission/discodeit/entity/Channel.java b/src/main/java/com/sprint/mission/discodeit/entity/Channel.java index 82b7cfce3..868fd563a 100644 --- a/src/main/java/com/sprint/mission/discodeit/entity/Channel.java +++ b/src/main/java/com/sprint/mission/discodeit/entity/Channel.java @@ -1,49 +1,51 @@ package com.sprint.mission.discodeit.entity; +import com.sprint.mission.discodeit.entity.base.BaseUpdatableEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.Table; +import java.io.Serial; import lombok.Getter; import java.io.Serializable; import java.time.Instant; import java.util.Optional; -import java.util.UUID; +import lombok.NoArgsConstructor; @Getter -public class Channel implements Serializable { - private static final long serialVersionUID = 1L; - private UUID id; - private Instant createdAt; - private Instant updatedAt; - private ChannelType type; - private String name; - private String description; - - public Channel(ChannelType type, String name, String description) { - this.id = UUID.randomUUID(); - this.createdAt = Instant.now(); - this.type = type; - this.name = name; - this.description = description; - } +@Table(name = "channels") +@Entity +@NoArgsConstructor +public class Channel extends BaseUpdatableEntity { - public Channel(ChannelType type) { - this.id = UUID.randomUUID(); - this.createdAt = Instant.now(); - this.type = type; - } + @Enumerated(EnumType.STRING) + @Column(length = 10, nullable = false) + private ChannelType type; + + @Column(name = "name", length = 100) + private String name; + + @Column(name = "description", length = 500) + private String description; - public void update(Optional newName, Optional newDescription) { - boolean anyValueUpdated = false; - if (newName.isPresent() && !newName.get().equals(this.name)) { - this.name = newName.get(); - anyValueUpdated = true; - } - if (newDescription.isPresent() && !newDescription.get().equals(this.description)) { - this.description = newDescription.get(); - anyValueUpdated = true; - } - - if (anyValueUpdated) { - this.updatedAt = Instant.now(); - } + public Channel(ChannelType type, String name, String description) { + this.type = type; + this.name = name; + this.description = description; + } + + public Channel(ChannelType type) { + this.type = type; + } + + public void update(Optional newName, Optional newDescription) { + if (newName.isPresent() && !newName.get().equals(this.name)) { + this.name = newName.get(); + } + if (newDescription.isPresent() && !newDescription.get().equals(this.description)) { + this.description = newDescription.get(); } + } } diff --git a/src/main/java/com/sprint/mission/discodeit/entity/Message.java b/src/main/java/com/sprint/mission/discodeit/entity/Message.java index e0a73fb86..c7014e301 100644 --- a/src/main/java/com/sprint/mission/discodeit/entity/Message.java +++ b/src/main/java/com/sprint/mission/discodeit/entity/Message.java @@ -1,46 +1,57 @@ package com.sprint.mission.discodeit.entity; -import lombok.Getter; - -import java.io.Serializable; +import com.sprint.mission.discodeit.entity.base.BaseUpdatableEntity; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.JoinTable; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; import java.time.Instant; import java.util.List; -import java.util.UUID; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; @Getter -public class Message implements Serializable { - private static final long serialVersionUID = 1L; - - private UUID id; - private Instant createdAt; - private Instant updatedAt; - private String content; - private UUID channelId; - private UUID authorId; - private List attachmentIds; - - public Message(String content, UUID channelId, UUID authorId) { - this.id = UUID.randomUUID(); - this.createdAt = Instant.now(); - this.content = content; - this.channelId = channelId; - this.authorId = authorId; - } - - public void setAttachmentIds(List attachmentIds) { - this.attachmentIds = attachmentIds; - this.updatedAt = Instant.now(); - } - - public void update(String newContent) { - boolean anyValueUpdated = false; - if (newContent != null && !newContent.equals(this.content)) { - this.content = newContent; - anyValueUpdated = true; - } - - if (anyValueUpdated) { - this.updatedAt = Instant.now(); - } +@Entity +@Table(name = "messages") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Message extends BaseUpdatableEntity { + + @Column(name = "content", nullable = false) + private String content; + + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "channel_id", nullable = false) + private Channel channel; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "author_id", nullable = false) + private User author; + + @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true) + @JoinTable( + name = "message_attachments", + joinColumns = @JoinColumn(name = "message_id"), + inverseJoinColumns = @JoinColumn(name = "attachment_id") + ) + private List attachments; + + public Message(String content, Channel channel, User author, List attachments) { + super(); + this.content = content; + this.channel = channel; + this.author = author; + this.attachments = attachments; + } + + public void update(String newContent) { + if (newContent != null && !newContent.equals(this.content)) { + this.content = newContent; } + } } diff --git a/src/main/java/com/sprint/mission/discodeit/entity/ReadStatus.java b/src/main/java/com/sprint/mission/discodeit/entity/ReadStatus.java index 878e0bbaf..293709677 100644 --- a/src/main/java/com/sprint/mission/discodeit/entity/ReadStatus.java +++ b/src/main/java/com/sprint/mission/discodeit/entity/ReadStatus.java @@ -1,35 +1,42 @@ package com.sprint.mission.discodeit.entity; -import lombok.Getter; - -import java.io.Serializable; +import com.sprint.mission.discodeit.entity.base.BaseUpdatableEntity; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; import java.time.Instant; -import java.util.UUID; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +@Setter @Getter -public class ReadStatus implements Serializable{ - private static final long serialVersionUID = 1L; - - private final UUID id; - private final UUID userId; - private final UUID channelId; - private final Instant createdAt; - private Instant updatedAt; - private Instant lastReadTime; - - public ReadStatus(UUID userId, UUID channelId, Instant lastReadTime) { - this.id = UUID.randomUUID(); - this.userId = userId; - this.channelId = channelId; - this.lastReadTime = lastReadTime; - this.createdAt = Instant.now(); - this.updatedAt = Instant.now(); +@Entity +@Table(name = "read_statuses") +@NoArgsConstructor +public class ReadStatus extends BaseUpdatableEntity { + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") + private User user; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "channel_id") + private Channel channel; + + private Instant lastReadAt; + + public ReadStatus(User user, Channel channel, Instant lastReadTime) { + this.user = user; + this.channel = channel; + this.lastReadAt = lastReadTime; + } + + public void updateLastReadTime(Instant lastReadTime) { + if (lastReadTime != null && !lastReadTime.equals(this.lastReadAt)) { + this.lastReadAt = lastReadTime; } - - public void updateLastReadTime(Instant lastReadTime) { - this.lastReadTime = lastReadTime; - this.updatedAt = Instant.now(); - } - - + } } diff --git a/src/main/java/com/sprint/mission/discodeit/entity/User.java b/src/main/java/com/sprint/mission/discodeit/entity/User.java index 5a6224d99..b01b689d0 100644 --- a/src/main/java/com/sprint/mission/discodeit/entity/User.java +++ b/src/main/java/com/sprint/mission/discodeit/entity/User.java @@ -1,55 +1,77 @@ package com.sprint.mission.discodeit.entity; -import lombok.Getter; - -import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonManagedReference; +import com.sprint.mission.discodeit.entity.base.BaseUpdatableEntity; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; import java.time.Instant; -import java.util.Optional; -import java.util.UUID; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; @Getter -public class User implements Serializable { - private static final long serialVersionUID = 1L; - - private UUID id; - private UUID profileId; - private Instant createdAt; - private Instant updatedAt; - private String username; - private String email; - private 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; - } +@Entity +@Table(name = "users") +@NoArgsConstructor +public class User extends BaseUpdatableEntity { + + @Column(name = "username", nullable = false, unique = true, length = 50) + private String username; + + @Column(name = "email", nullable = false, unique = true, length = 100) + private String email; + + @Column(name = "password", nullable = false, length = 60) + private String password; - public UUID setProfileId(UUID BinaryContentId){ - this.profileId = BinaryContentId; - return this.profileId; + @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true) + @JoinColumn(name = "profile_id") + private BinaryContent profile; + + @JsonManagedReference + @Setter(AccessLevel.PROTECTED) + @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) + private UserStatus status; + + public User(String username, String email, String password, BinaryContent profile) { + this.username = username; + this.email = email; + this.password = password; + this.profile = profile; + } + + public void setStatus(UserStatus status) { + this.status = status; + } + + public void setProfile(BinaryContent binaryContent) { + this.profile = binaryContent; + setUpdatedAt(Instant.now()); + } + + public void update(String newUsername, String newEmail, String newPassword) { + boolean anyValueUpdated = false; + if (newUsername != null && !newUsername.equals(this.username)) { + this.username = newUsername; + anyValueUpdated = true; + } + if (newEmail != null && !newEmail.equals(this.email)) { + this.email = newEmail; + anyValueUpdated = true; + } + if (newPassword != null && !newPassword.equals(this.password)) { + this.password = newPassword; + anyValueUpdated = true; } - public void update(String newUsername, String newEmail, String newPassword) { - boolean anyValueUpdated = false; - if (newUsername != null && !newUsername.equals(this.username)) { - this.username = newUsername; - anyValueUpdated = true; - } - if (newEmail != null && !newEmail.equals(this.email)) { - this.email = newEmail; - anyValueUpdated = true; - } - if (newPassword != null && !newPassword.equals(this.password)) { - this.password = newPassword; - anyValueUpdated = true; - } - - if (anyValueUpdated) { - this.updatedAt = Instant.now(); - } + if (anyValueUpdated) { + setUpdatedAt(Instant.now()); } + } } diff --git a/src/main/java/com/sprint/mission/discodeit/entity/UserStatus.java b/src/main/java/com/sprint/mission/discodeit/entity/UserStatus.java index 7cc4c22e2..56e0ec5c3 100644 --- a/src/main/java/com/sprint/mission/discodeit/entity/UserStatus.java +++ b/src/main/java/com/sprint/mission/discodeit/entity/UserStatus.java @@ -1,28 +1,32 @@ package com.sprint.mission.discodeit.entity; -import lombok.Getter; - +import com.sprint.mission.discodeit.entity.base.BaseUpdatableEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; import java.time.Instant; -import java.util.UUID; -import java.io.Serializable; +import lombok.Getter; +import lombok.NoArgsConstructor; @Getter -public class UserStatus implements Serializable { +@Table(name = "user_statuses") +@Entity +@NoArgsConstructor +public class UserStatus extends BaseUpdatableEntity { - private static final long serialVersionUID = 1L; + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") + private User user; - private final UUID id; - private final UUID userId; - private final Instant craetedAt; - private Instant updatedAt; + @Column(nullable = false) private Instant lastActiveAt; private boolean online; - public UserStatus(UUID userId, Instant lastActiveAt) { - this.id = UUID.randomUUID(); - this.userId = userId; - this.craetedAt = Instant.now(); - this.updatedAt = Instant.now(); + public UserStatus(User user, Instant lastActiveAt) { + this.user = user; this.lastActiveAt = lastActiveAt; this.online = isOnline(); } @@ -36,7 +40,7 @@ public void updateLastActiveAt(Instant lastActiveAt) { this.online = isOnline(); if (anyValueUpdated) { - this.updatedAt = Instant.now(); + setUpdatedAt(Instant.now()); } } @@ -45,5 +49,4 @@ public boolean isOnline() { return lastActiveAt.isAfter(fiveMinuteAgo); } - } diff --git a/src/main/java/com/sprint/mission/discodeit/entity/base/BaseEntity.java b/src/main/java/com/sprint/mission/discodeit/entity/base/BaseEntity.java new file mode 100644 index 000000000..d9b405a12 --- /dev/null +++ b/src/main/java/com/sprint/mission/discodeit/entity/base/BaseEntity.java @@ -0,0 +1,32 @@ +package com.sprint.mission.discodeit.entity.base; + +import jakarta.persistence.Column; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; +import java.time.Instant; +import java.util.UUID; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@MappedSuperclass +@EntityListeners(AuditingEntityListener.class) +public abstract class BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.UUID) + @Column(columnDefinition = "uuid", updatable = false, nullable = false) + private UUID id; + + @CreatedDate + @Column(columnDefinition = "timestamp with time zone", updatable = false, nullable = false) + private Instant createdAt; +} diff --git a/src/main/java/com/sprint/mission/discodeit/entity/base/BaseUpdatableEntity.java b/src/main/java/com/sprint/mission/discodeit/entity/base/BaseUpdatableEntity.java new file mode 100644 index 000000000..57d1d3169 --- /dev/null +++ b/src/main/java/com/sprint/mission/discodeit/entity/base/BaseUpdatableEntity.java @@ -0,0 +1,19 @@ +package com.sprint.mission.discodeit.entity.base; + +import jakarta.persistence.Column; +import jakarta.persistence.MappedSuperclass; +import java.time.Instant; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.LastModifiedDate; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@MappedSuperclass +public abstract class BaseUpdatableEntity extends BaseEntity { + + @LastModifiedDate + @Column(columnDefinition = "timestamp with time zone") + private Instant updatedAt; +} diff --git a/src/main/java/com/sprint/mission/discodeit/mapper/BinaryContentMapper.java b/src/main/java/com/sprint/mission/discodeit/mapper/BinaryContentMapper.java new file mode 100644 index 000000000..271a72744 --- /dev/null +++ b/src/main/java/com/sprint/mission/discodeit/mapper/BinaryContentMapper.java @@ -0,0 +1,18 @@ +package com.sprint.mission.discodeit.mapper; + +import com.sprint.mission.discodeit.dto.data.BinaryContentDto; +import com.sprint.mission.discodeit.entity.BinaryContent; +import org.springframework.stereotype.Component; + +@Component +public class BinaryContentMapper { + + public BinaryContentDto toDto(BinaryContent binaryContent) { + return new BinaryContentDto( + binaryContent.getId(), + binaryContent.getFileName(), + binaryContent.getSize(), + binaryContent.getContentType() + ); + } +} diff --git a/src/main/java/com/sprint/mission/discodeit/mapper/ChannelMapper.java b/src/main/java/com/sprint/mission/discodeit/mapper/ChannelMapper.java new file mode 100644 index 000000000..c5bbaf690 --- /dev/null +++ b/src/main/java/com/sprint/mission/discodeit/mapper/ChannelMapper.java @@ -0,0 +1,46 @@ +package com.sprint.mission.discodeit.mapper; + +import com.sprint.mission.discodeit.dto.data.ChannelDto; +import com.sprint.mission.discodeit.dto.data.UserDto; +import com.sprint.mission.discodeit.entity.Channel; +import com.sprint.mission.discodeit.entity.ChannelType; +import com.sprint.mission.discodeit.entity.ReadStatus; +import com.sprint.mission.discodeit.repository.MessageRepository; +import com.sprint.mission.discodeit.repository.ReadStatusRepository; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class ChannelMapper { + + private final MessageRepository messageRepository; + private final ReadStatusRepository readStatusRepository; + private final UserMapper userMapper; + + public ChannelDto toDto(Channel channel) { + Instant lastMessageAt = messageRepository.findLastMessageAtByChannelId(channel.getId()) + .orElse(Instant.MIN); + + List participants = new ArrayList<>(); + if (channel.getType().equals(ChannelType.PRIVATE)) { + readStatusRepository.findAllByChannelId(channel.getId()) + .stream() + .map(ReadStatus::getUser) + .map(userMapper::toDto) + .forEach(participants::add); + } + + return new ChannelDto( + channel.getId(), + channel.getType(), + channel.getName(), + channel.getDescription(), + participants, + lastMessageAt + ); + } +} diff --git a/src/main/java/com/sprint/mission/discodeit/mapper/MessageMapper.java b/src/main/java/com/sprint/mission/discodeit/mapper/MessageMapper.java new file mode 100644 index 000000000..a08206493 --- /dev/null +++ b/src/main/java/com/sprint/mission/discodeit/mapper/MessageMapper.java @@ -0,0 +1,36 @@ +package com.sprint.mission.discodeit.mapper; + +import com.sprint.mission.discodeit.dto.data.BinaryContentDto; +import com.sprint.mission.discodeit.dto.data.MessageDto; +import com.sprint.mission.discodeit.dto.data.UserDto; +import com.sprint.mission.discodeit.entity.Message; +import java.util.List; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class MessageMapper { + + private final UserMapper userMapper; + private final BinaryContentMapper binaryContentMapper; + + public MessageDto toDto(Message message) { + UserDto author = Optional.ofNullable(message.getAuthor()) + .map(userMapper::toDto).orElse(null); + List attachments = message.getAttachments().stream() + .map(binaryContentMapper::toDto) + .toList(); + + return new MessageDto( + message.getId(), + message.getCreatedAt(), + message.getUpdatedAt(), + message.getContent(), + message.getChannel().getId(), + author, + attachments + ); + } +} diff --git a/src/main/java/com/sprint/mission/discodeit/mapper/ReadStatusMapper.java b/src/main/java/com/sprint/mission/discodeit/mapper/ReadStatusMapper.java new file mode 100644 index 000000000..5baf033b1 --- /dev/null +++ b/src/main/java/com/sprint/mission/discodeit/mapper/ReadStatusMapper.java @@ -0,0 +1,18 @@ +package com.sprint.mission.discodeit.mapper; + +import com.sprint.mission.discodeit.dto.data.ReadStatusDto; +import com.sprint.mission.discodeit.entity.ReadStatus; +import org.springframework.stereotype.Component; + +@Component +public class ReadStatusMapper { + + public ReadStatusDto toDto(ReadStatus readStatus) { + return new ReadStatusDto( + readStatus.getId(), + readStatus.getUser().getId(), + readStatus.getChannel().getId(), + readStatus.getLastReadAt() + ); + } +} diff --git a/src/main/java/com/sprint/mission/discodeit/mapper/UserMapper.java b/src/main/java/com/sprint/mission/discodeit/mapper/UserMapper.java new file mode 100644 index 000000000..ed4da2b5d --- /dev/null +++ b/src/main/java/com/sprint/mission/discodeit/mapper/UserMapper.java @@ -0,0 +1,27 @@ +package com.sprint.mission.discodeit.mapper; + +import com.sprint.mission.discodeit.dto.data.BinaryContentDto; +import com.sprint.mission.discodeit.dto.data.UserDto; +import com.sprint.mission.discodeit.entity.User; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class UserMapper { + + private final BinaryContentMapper binaryContentMapper; + + public UserDto toDto(User user) { + boolean online = user.getStatus().isOnline(); + BinaryContentDto binaryContentDto = binaryContentMapper.toDto(user.getProfile()); + + return new UserDto( + user.getId(), + user.getUsername(), + user.getEmail(), + binaryContentDto, + online + ); + } +} diff --git a/src/main/java/com/sprint/mission/discodeit/mapper/UserStatusMapper.java b/src/main/java/com/sprint/mission/discodeit/mapper/UserStatusMapper.java new file mode 100644 index 000000000..22e2916a2 --- /dev/null +++ b/src/main/java/com/sprint/mission/discodeit/mapper/UserStatusMapper.java @@ -0,0 +1,17 @@ +package com.sprint.mission.discodeit.mapper; + +import com.sprint.mission.discodeit.dto.data.UserStatusDto; +import com.sprint.mission.discodeit.entity.UserStatus; +import org.springframework.stereotype.Component; + +@Component +public class UserStatusMapper { + + public UserStatusDto toDto(UserStatus userStatus) { + return new UserStatusDto( + userStatus.getId(), + userStatus.getUser().getId(), + userStatus.getLastActiveAt() + ); + } +} diff --git a/src/main/java/com/sprint/mission/discodeit/repository/BinaryContentRepository.java b/src/main/java/com/sprint/mission/discodeit/repository/BinaryContentRepository.java index bf859f784..0de17a64e 100644 --- a/src/main/java/com/sprint/mission/discodeit/repository/BinaryContentRepository.java +++ b/src/main/java/com/sprint/mission/discodeit/repository/BinaryContentRepository.java @@ -1,15 +1,11 @@ package com.sprint.mission.discodeit.repository; import com.sprint.mission.discodeit.entity.BinaryContent; - import java.util.List; -import java.util.Optional; import java.util.UUID; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface BinaryContentRepository extends JpaRepository { -public interface BinaryContentRepository { - BinaryContent save(BinaryContent binaryContent); - Optional findById(UUID id); - List findAll(); - boolean existsById(UUID id); - void delete(UUID id); + List findAllByIdIn(List ids); } diff --git a/src/main/java/com/sprint/mission/discodeit/repository/ChannelRepository.java b/src/main/java/com/sprint/mission/discodeit/repository/ChannelRepository.java index 326a84f4b..162f38fc4 100644 --- a/src/main/java/com/sprint/mission/discodeit/repository/ChannelRepository.java +++ b/src/main/java/com/sprint/mission/discodeit/repository/ChannelRepository.java @@ -1,15 +1,13 @@ package com.sprint.mission.discodeit.repository; import com.sprint.mission.discodeit.entity.Channel; - +import com.sprint.mission.discodeit.entity.ChannelType; import java.util.List; -import java.util.Optional; import java.util.UUID; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ChannelRepository extends JpaRepository { + + List findAllByTypeOrIdIn(ChannelType type, List ids); -public interface ChannelRepository { - Channel save(Channel channel); - Optional findById(UUID id); - List findAll(); - boolean existsById(UUID id); - void deleteById(UUID id); } diff --git a/src/main/java/com/sprint/mission/discodeit/repository/MessageRepository.java b/src/main/java/com/sprint/mission/discodeit/repository/MessageRepository.java index ad7c1d588..d702b05cb 100644 --- a/src/main/java/com/sprint/mission/discodeit/repository/MessageRepository.java +++ b/src/main/java/com/sprint/mission/discodeit/repository/MessageRepository.java @@ -1,16 +1,25 @@ package com.sprint.mission.discodeit.repository; import com.sprint.mission.discodeit.entity.Message; - +import java.time.Instant; import java.util.List; import java.util.Optional; import java.util.UUID; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +public interface MessageRepository extends JpaRepository { + + Slice findAllByChannelId(UUID channelId, Pageable pageable); + + @Query("SELECT m.createdAt " + + "FROM Message m " + + "WHERE m.channel.id = :channelId " + + "ORDER BY m.createdAt DESC LIMIT 1") + Optional findLastMessageAtByChannelId(@Param("channelId") UUID channelId); -public interface MessageRepository { - Message save(Message message); - Optional findById(UUID id); - List findAllByChannelId(UUID channelId); - List findAll(); - boolean existsById(UUID id); - void deleteById(UUID id); + void deleteAllByChannelId(UUID channelId); } diff --git a/src/main/java/com/sprint/mission/discodeit/repository/ReadStatusRepository.java b/src/main/java/com/sprint/mission/discodeit/repository/ReadStatusRepository.java index fd76cf5dd..c3de25aee 100644 --- a/src/main/java/com/sprint/mission/discodeit/repository/ReadStatusRepository.java +++ b/src/main/java/com/sprint/mission/discodeit/repository/ReadStatusRepository.java @@ -1,16 +1,17 @@ package com.sprint.mission.discodeit.repository; import com.sprint.mission.discodeit.entity.ReadStatus; - import java.util.List; -import java.util.Optional; import java.util.UUID; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ReadStatusRepository extends JpaRepository { + + List findAllByUserId(UUID userId); + + List findAllByChannelId(UUID channelId); + + Boolean existsByUserIdAndChannelId(UUID userId, UUID channelId); -public interface ReadStatusRepository { - ReadStatus save(ReadStatus readStatus); - Optional findById(UUID id); - List findAllByUserId(UUID userId); - List findAllByChannelId(UUID channelId); - List findAll(); - void delete(UUID id); + void deleteAllByChannelId(UUID channelId); } diff --git a/src/main/java/com/sprint/mission/discodeit/repository/UserRepository.java b/src/main/java/com/sprint/mission/discodeit/repository/UserRepository.java index ebdbcb9e4..812e6de4b 100644 --- a/src/main/java/com/sprint/mission/discodeit/repository/UserRepository.java +++ b/src/main/java/com/sprint/mission/discodeit/repository/UserRepository.java @@ -1,15 +1,9 @@ package com.sprint.mission.discodeit.repository; import com.sprint.mission.discodeit.entity.User; - -import java.util.List; -import java.util.Optional; import java.util.UUID; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserRepository extends JpaRepository { -public interface UserRepository { - User save(User user); - Optional findById(UUID id); - List findAll(); - boolean existsById(UUID id); - void deleteById(UUID id); } diff --git a/src/main/java/com/sprint/mission/discodeit/repository/UserStatusRepository.java b/src/main/java/com/sprint/mission/discodeit/repository/UserStatusRepository.java index 3a02b78d6..2028a74d6 100644 --- a/src/main/java/com/sprint/mission/discodeit/repository/UserStatusRepository.java +++ b/src/main/java/com/sprint/mission/discodeit/repository/UserStatusRepository.java @@ -5,11 +5,9 @@ import java.util.List; import java.util.Optional; import java.util.UUID; +import org.springframework.data.jpa.repository.JpaRepository; -public interface UserStatusRepository { - UserStatus save(UserStatus userStatus); - Optional findById(UUID id); - Optional findByUserId(UUID userId); - List findAll(); - void delete(UUID id); +public interface UserStatusRepository extends JpaRepository { + + Optional findByUserId(UUID userId); } diff --git a/src/main/java/com/sprint/mission/discodeit/repository/file/FileBinaryContentRepository.java b/src/main/java/com/sprint/mission/discodeit/repository/file/FileBinaryContentRepository.java deleted file mode 100644 index 450b2967b..000000000 --- a/src/main/java/com/sprint/mission/discodeit/repository/file/FileBinaryContentRepository.java +++ /dev/null @@ -1,113 +0,0 @@ -package com.sprint.mission.discodeit.repository.file; - -import com.sprint.mission.discodeit.entity.BinaryContent; -import com.sprint.mission.discodeit.repository.BinaryContentRepository; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -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.List; -import java.util.Optional; -import java.util.UUID; - -@Repository -@ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "file") -public class FileBinaryContentRepository implements BinaryContentRepository { - private final Path DIRECTORY; - private final String EXTENSION = ".ser"; - - @Autowired - public FileBinaryContentRepository( - @Value("${discodeit.repository.file-directory:data}") String fileDirectory - ) { - this.DIRECTORY = Paths.get(System.getProperty("user.dir"), fileDirectory, 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); - } - - public FileBinaryContentRepository(Path DIRECTORY) { - this.DIRECTORY = DIRECTORY; - } - - @Override - public BinaryContent save(BinaryContent 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; - } - - @Override - public Optional findById(UUID 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); - } - - @Override - public List findAll() { - 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); - } - }) - .toList(); - }catch(IOException e) { - throw new RuntimeException(e); - } - } - - @Override - public boolean existsById(UUID id) { - Path path = resolvePath(id); - return Files.exists(path); - } - - @Override - public void delete(UUID id) { - Path path = resolvePath(id); - try{ - Files.delete(path); - }catch(IOException e) { - throw new RuntimeException(e); - } - } -} diff --git a/src/main/java/com/sprint/mission/discodeit/repository/file/FileChannelRepository.java b/src/main/java/com/sprint/mission/discodeit/repository/file/FileChannelRepository.java deleted file mode 100644 index a0d8686cc..000000000 --- a/src/main/java/com/sprint/mission/discodeit/repository/file/FileChannelRepository.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.sprint.mission.discodeit.repository.file; - -import com.sprint.mission.discodeit.entity.Channel; -import com.sprint.mission.discodeit.repository.ChannelRepository; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.stereotype.Repository; - -import java.io.*; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.List; -import java.util.Optional; -import java.util.UUID; - -@Repository -@ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "file") -public class FileChannelRepository implements ChannelRepository { - private final Path DIRECTORY; - private final String EXTENSION = ".ser"; - - public FileChannelRepository( - @Value("${discodeit.repository.file-directory:data}") String fileDirectory - ) { - this.DIRECTORY = Paths.get(System.getProperty("user.dir"), fileDirectory, Channel.class.getSimpleName()); - if (Files.notExists(DIRECTORY)) { - try { - Files.createDirectories(DIRECTORY); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } - - private Path resolvePath(UUID id) { - return DIRECTORY.resolve(id + EXTENSION); - } - - @Override - public Channel save(Channel channel) { - Path path = resolvePath(channel.getId()); - try ( - FileOutputStream fos = new FileOutputStream(path.toFile()); - ObjectOutputStream oos = new ObjectOutputStream(fos) - ) { - oos.writeObject(channel); - } catch (IOException e) { - throw new RuntimeException(e); - } - return channel; - } - - @Override - public Optional findById(UUID id) { - Channel channelNullable = null; - Path path = resolvePath(id); - if (Files.exists(path)) { - try ( - FileInputStream fis = new FileInputStream(path.toFile()); - ObjectInputStream ois = new ObjectInputStream(fis) - ) { - channelNullable = (Channel) ois.readObject(); - } catch (IOException | ClassNotFoundException e) { - throw new RuntimeException(e); - } - } - return Optional.ofNullable(channelNullable); - } - - @Override - public List findAll() { - 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 (Channel) ois.readObject(); - } catch (IOException | ClassNotFoundException e) { - throw new RuntimeException(e); - } - }) - .toList(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - @Override - public boolean existsById(UUID id) { - Path path = resolvePath(id); - return Files.exists(path); - } - - @Override - public void deleteById(UUID id) { - Path path = resolvePath(id); - try { - Files.delete(path); - } catch (IOException e) { - throw new RuntimeException(e); - } - } -} diff --git a/src/main/java/com/sprint/mission/discodeit/repository/file/FileMessageRepository.java b/src/main/java/com/sprint/mission/discodeit/repository/file/FileMessageRepository.java deleted file mode 100644 index 748e0ef8a..000000000 --- a/src/main/java/com/sprint/mission/discodeit/repository/file/FileMessageRepository.java +++ /dev/null @@ -1,130 +0,0 @@ -package com.sprint.mission.discodeit.repository.file; - -import com.sprint.mission.discodeit.entity.Message; -import com.sprint.mission.discodeit.repository.MessageRepository; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.stereotype.Repository; - -import java.io.*; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.List; -import java.util.Optional; -import java.util.UUID; -import java.util.stream.Stream; - -@Repository -@ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "file") -public class FileMessageRepository implements MessageRepository { - private final Path DIRECTORY; - private final String EXTENSION = ".ser"; - - public FileMessageRepository( - @Value("${discodeit.repository.file-directory:data}") String fileDirectory - ) { - this.DIRECTORY = Paths.get(System.getProperty("user.dir"), fileDirectory, Message.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 Message save(Message message) { - Path path = resolvePath(message.getId()); - try ( - FileOutputStream fos = new FileOutputStream(path.toFile()); - ObjectOutputStream oos = new ObjectOutputStream(fos) - ) { - oos.writeObject(message); - } catch (IOException e) { - throw new RuntimeException(e); - } - return message; - } - - @Override - public Optional findById(UUID id) { - Message messageNullable = null; - Path path = resolvePath(id); - if (Files.exists(path)) { - try ( - FileInputStream fis = new FileInputStream(path.toFile()); - ObjectInputStream ois = new ObjectInputStream(fis) - ) { - messageNullable = (Message) ois.readObject(); - } catch (IOException | ClassNotFoundException e) { - throw new RuntimeException(e); - } - } - return Optional.ofNullable(messageNullable); - } - - @Override - public List findAllByChannelId(UUID channelId) { - try (Stream paths = Files.list(DIRECTORY)) { - return paths - .filter(path -> path.toString().endsWith(EXTENSION)) - .map(path -> { - try ( - FileInputStream fis = new FileInputStream(path.toFile()); - ObjectInputStream ois = new ObjectInputStream(fis) - ) { - return (Message) ois.readObject(); - } catch (IOException | ClassNotFoundException e) { - throw new RuntimeException(e); - } - }) - .filter(message -> message.getChannelId().equals(channelId)) - .toList(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - @Override - public List findAll() { - 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 (Message) ois.readObject(); - } catch (IOException | ClassNotFoundException e) { - throw new RuntimeException(e); - } - }) - .toList(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - @Override - public boolean existsById(UUID id) { - Path path = resolvePath(id); - return Files.exists(path); - } - - @Override - public void deleteById(UUID id) { - Path path = resolvePath(id); - try { - Files.delete(path); - } catch (IOException e) { - throw new RuntimeException(e); - } - } -} diff --git a/src/main/java/com/sprint/mission/discodeit/repository/file/FileReadStatusRepository.java b/src/main/java/com/sprint/mission/discodeit/repository/file/FileReadStatusRepository.java deleted file mode 100644 index a8a01fb52..000000000 --- a/src/main/java/com/sprint/mission/discodeit/repository/file/FileReadStatusRepository.java +++ /dev/null @@ -1,132 +0,0 @@ -package com.sprint.mission.discodeit.repository.file; - -import com.sprint.mission.discodeit.entity.ReadStatus; -import com.sprint.mission.discodeit.repository.ReadStatusRepository; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.stereotype.Repository; - -import java.io.*; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.List; -import java.util.Optional; -import java.util.UUID; -import java.util.stream.Stream; - -@Repository -@ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "file") -public class FileReadStatusRepository implements ReadStatusRepository { - private final Path DIRECTORY; - private final String EXTENSION = ".ser"; - - public FileReadStatusRepository( - @Value("${discodeit.repository.file-directory:data}") String fileDirectory - ) { - this.DIRECTORY = Paths.get(System.getProperty("user.dir"), fileDirectory, 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) { - 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 Optional findById(UUID 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 List findAllByUserId(UUID userId) { - return findAll().stream() - .filter(ReadStatus -> ReadStatus.getUserId().equals(userId)) - .toList(); - } - - @Override - public List findAllByChannelId(UUID channelId) { - try (Stream paths = Files.list(DIRECTORY)) { - return paths - .filter(path -> path.toString().endsWith(EXTENSION)) - .map(path -> { - try ( - FileInputStream fis = new FileInputStream(path.toFile()); - ObjectInputStream ois = new ObjectInputStream(fis) - ) { - return (ReadStatus) ois.readObject(); - } catch (IOException | ClassNotFoundException e) { - throw new RuntimeException(e); - } - }) - .filter(readStatus -> readStatus.getChannelId().equals(channelId)) - .toList(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - @Override - public List findAll() { - 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); - } - }) - .toList(); - }catch (IOException e){ - throw new RuntimeException(e); - } - } - - @Override - public void delete(UUID id) { - Path path = resolvePath(id); - try{ - Files.delete(path); - }catch(IOException e){ - throw new RuntimeException(e); - } - } -} diff --git a/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserRepository.java b/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserRepository.java deleted file mode 100644 index c4d8a09b8..000000000 --- a/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserRepository.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.sprint.mission.discodeit.repository.file; - -import com.sprint.mission.discodeit.entity.User; -import com.sprint.mission.discodeit.repository.UserRepository; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.stereotype.Repository; - -import java.io.*; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.List; -import java.util.Optional; -import java.util.UUID; - -@Repository -@ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "file") -public class FileUserRepository implements UserRepository { - private final Path DIRECTORY; - private final String EXTENSION = ".ser"; - - public FileUserRepository( - @Value("${discodeit.repository.file-directory:data}") String fileDirectory - ) { - this.DIRECTORY = Paths.get(System.getProperty("user.dir"), fileDirectory, User.class.getSimpleName()); - if (Files.notExists(DIRECTORY)) { - try { - Files.createDirectories(DIRECTORY); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } - - private Path resolvePath(UUID id) { - return DIRECTORY.resolve(id + EXTENSION); - } - - @Override - public User save(User user) { - Path path = resolvePath(user.getId()); - try ( - FileOutputStream fos = new FileOutputStream(path.toFile()); - ObjectOutputStream oos = new ObjectOutputStream(fos) - ) { - oos.writeObject(user); - } catch (IOException e) { - throw new RuntimeException(e); - } - return user; - } - - @Override - public Optional findById(UUID id) { - User userNullable = null; - Path path = resolvePath(id); - if (Files.exists(path)) { - try ( - FileInputStream fis = new FileInputStream(path.toFile()); - ObjectInputStream ois = new ObjectInputStream(fis) - ) { - userNullable = (User) ois.readObject(); - } catch (IOException | ClassNotFoundException e) { - throw new RuntimeException(e); - } - } - return Optional.ofNullable(userNullable); - } - - @Override - public List findAll() { - 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 (User) ois.readObject(); - } catch (IOException | ClassNotFoundException e) { - throw new RuntimeException(e); - } - }) - .toList(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - @Override - public boolean existsById(UUID id) { - Path path = resolvePath(id); - return Files.exists(path); - } - - @Override - public void deleteById(UUID id) { - Path path = resolvePath(id); - try { - Files.delete(path); - } catch (IOException e) { - throw new RuntimeException(e); - } - } -} diff --git a/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserStatusRepository.java b/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserStatusRepository.java deleted file mode 100644 index ca2f831ec..000000000 --- a/src/main/java/com/sprint/mission/discodeit/repository/file/FileUserStatusRepository.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.sprint.mission.discodeit.repository.file; - -import com.sprint.mission.discodeit.entity.UserStatus; -import com.sprint.mission.discodeit.repository.UserStatusRepository; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.stereotype.Repository; - -import java.io.*; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.List; -import java.util.Optional; -import java.util.UUID; - -@Repository -@ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "file") -public class FileUserStatusRepository implements UserStatusRepository { - private final Path DIRECTORY; - private final String EXTENSION = ".ser"; - - public FileUserStatusRepository( - @Value("${discodeit.repository.file-directory:data}") String fileDirectory - ) { - this.DIRECTORY = Paths.get(System.getProperty("user.dir"), fileDirectory, UserStatus.class.getSimpleName()); - if (Files.notExists(DIRECTORY)) { - try { - Files.createDirectories(DIRECTORY); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } - - private Path resolvePath(UUID id){ - return DIRECTORY.resolve(id + EXTENSION); - } - - @Override - public UserStatus save(UserStatus userStatus) { - Path path = resolvePath(userStatus.getId()); - try( - FileOutputStream fos = new FileOutputStream(path.toFile()); - ObjectOutputStream oos = new ObjectOutputStream(fos) - ){ - oos.writeObject(userStatus); - }catch (IOException e){ - throw new RuntimeException(e); - } - return userStatus; - } - - @Override - public Optional findById(UUID id) { - UserStatus userStatusNullable = null; - Path path = resolvePath(id); - if(Files.notExists(path)){ - try( - FileInputStream fis = new FileInputStream(path.toFile()); - ObjectInputStream ois = new ObjectInputStream(fis) - ){ - userStatusNullable = (UserStatus) ois.readObject(); - } catch (ClassNotFoundException | IOException e) { - throw new RuntimeException(e); - } - } - return Optional.ofNullable(userStatusNullable); - } - - @Override - public Optional findByUserId(UUID userId) { - return findAll().stream() - .filter(UserStatus -> UserStatus.getUserId().equals(userId)) - .findFirst(); - } - - @Override - public List findAll() { - 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 (UserStatus) ois.readObject(); - }catch(IOException | ClassNotFoundException e){ - throw new RuntimeException(e); - } - }) - .toList(); - } catch(IOException e){ - throw new RuntimeException(e); - } - } - - @Override - public void delete(UUID id) { - Path path = resolvePath(id); - try{ - Files.delete(path); - }catch(IOException e){ - throw new RuntimeException(e); - } - } -} diff --git a/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFBinaryContentRepository.java b/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFBinaryContentRepository.java deleted file mode 100644 index 07fccedf2..000000000 --- a/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFBinaryContentRepository.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.sprint.mission.discodeit.repository.jcf; - -import com.sprint.mission.discodeit.entity.BinaryContent; -import com.sprint.mission.discodeit.repository.BinaryContentRepository; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.stereotype.Repository; - -import java.util.*; - -@Repository -@ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "jcf", matchIfMissing = true) -public class JCFBinaryContentRepository implements BinaryContentRepository { - private final Map data; - - public JCFBinaryContentRepository() { - this.data = new HashMap<>(); - } - - @Override - public BinaryContent save(BinaryContent binaryContent) { - this.data.put(binaryContent.getId(), binaryContent); - return binaryContent; - } - - @Override - public Optional findById(UUID id) { - return Optional.ofNullable(this.data.get(id)); - } - - @Override - public List findAll() { - return this.data.values().stream().toList(); - } - - @Override - public boolean existsById(UUID id) { - return this.data.containsKey(id); - } - - @Override - public void delete(UUID id) { - this.data.remove(id); - } -} diff --git a/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFChannelRepository.java b/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFChannelRepository.java deleted file mode 100644 index cced79e79..000000000 --- a/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFChannelRepository.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.sprint.mission.discodeit.repository.jcf; - -import com.sprint.mission.discodeit.entity.Channel; -import com.sprint.mission.discodeit.repository.ChannelRepository; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.stereotype.Repository; - -import java.util.*; - -@Repository -@ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "jcf", matchIfMissing = true) -public class JCFChannelRepository implements ChannelRepository { - private final Map data; - - public JCFChannelRepository() { - this.data = new HashMap<>(); - } - - @Override - public Channel save(Channel channel) { - this.data.put(channel.getId(), channel); - return channel; - } - - @Override - public Optional findById(UUID id) { - return Optional.ofNullable(this.data.get(id)); - } - - @Override - public List findAll() { - return this.data.values().stream().toList(); - } - - @Override - public boolean existsById(UUID id) { - return this.data.containsKey(id); - } - - @Override - public void deleteById(UUID id) { - this.data.remove(id); - } -} diff --git a/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFMessageRepository.java b/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFMessageRepository.java deleted file mode 100644 index da417b712..000000000 --- a/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFMessageRepository.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.sprint.mission.discodeit.repository.jcf; - -import com.sprint.mission.discodeit.entity.Message; -import com.sprint.mission.discodeit.repository.MessageRepository; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.stereotype.Repository; - -import java.util.*; - -@Repository -@ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "jcf", matchIfMissing = true) -public class JCFMessageRepository implements MessageRepository { - private final Map data; - - public JCFMessageRepository() { - this.data = new HashMap<>(); - } - - @Override - public Message save(Message message) { - this.data.put(message.getId(), message); - return message; - } - - @Override - public Optional findById(UUID id) { - return Optional.ofNullable(this.data.get(id)); - } - - @Override - public List findAll() { - return this.data.values().stream().toList(); - } - - @Override - public List findAllByChannelId(UUID channelId) { - return data.values().stream().filter(message -> message.getChannelId().equals(channelId)).toList(); - } - - @Override - public boolean existsById(UUID id) { - return this.data.containsKey(id); - } - - @Override - public void deleteById(UUID id) { - this.data.remove(id); - } -} diff --git a/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFReadStatusRepository.java b/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFReadStatusRepository.java deleted file mode 100644 index 1bc27108a..000000000 --- a/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFReadStatusRepository.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.sprint.mission.discodeit.repository.jcf; - -import com.sprint.mission.discodeit.entity.ReadStatus; -import com.sprint.mission.discodeit.repository.ReadStatusRepository; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.stereotype.Repository; - -import java.util.*; - -@Repository -@ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "jcf", matchIfMissing = true) -public class JCFReadStatusRepository implements ReadStatusRepository { - private final Map data; - - public JCFReadStatusRepository() { - this.data = new HashMap<>(); - } - - @Override - public ReadStatus save(ReadStatus readStatus) { - this.data.put(readStatus.getId(), readStatus); - return readStatus; - } - - @Override - public Optional findById(UUID id) { - return Optional.ofNullable(data.get(id)); - } - - @Override - public List findAllByUserId(UUID userId) { - return data.values().stream() - .filter(readStatus -> readStatus.getUserId().equals(userId)) - .toList(); - } - - @Override - public List findAllByChannelId(UUID channelId) { - return this.data.values().stream() - .filter(readStatus -> readStatus.getChannelId().equals(channelId)) - .toList(); - } - - @Override - public List findAll() { - return data.values().stream().toList(); - } - - @Override - public void delete(UUID id) { - data.remove(id); - } -} diff --git a/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserRepository.java b/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserRepository.java deleted file mode 100644 index 69fd078e3..000000000 --- a/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserRepository.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.sprint.mission.discodeit.repository.jcf; - -import com.sprint.mission.discodeit.entity.User; -import com.sprint.mission.discodeit.repository.UserRepository; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.stereotype.Repository; - -import java.util.*; - -@Repository -@ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "jcf", matchIfMissing = true) -public class JCFUserRepository implements UserRepository { - private final Map data; - - public JCFUserRepository() { - this.data = new HashMap<>(); - } - - @Override - public User save(User user) { - this.data.put(user.getId(), user); - return user; - } - - @Override - public Optional findById(UUID id) { - return Optional.ofNullable(this.data.get(id)); - } - - @Override - public List findAll() { - return this.data.values().stream().toList(); - } - - @Override - public boolean existsById(UUID id) { - return this.data.containsKey(id); - } - - @Override - public void deleteById(UUID id) { - this.data.remove(id); - } -} diff --git a/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserStatusRepository.java b/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserStatusRepository.java deleted file mode 100644 index 93ee517b2..000000000 --- a/src/main/java/com/sprint/mission/discodeit/repository/jcf/JCFUserStatusRepository.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.sprint.mission.discodeit.repository.jcf; - -import com.sprint.mission.discodeit.entity.UserStatus; -import com.sprint.mission.discodeit.repository.UserStatusRepository; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.stereotype.Repository; - -import java.util.*; - -@Repository -@ConditionalOnProperty(name = "discodeit.repository.type", havingValue = "jcf", matchIfMissing = true) -public class JCFUserStatusRepository implements UserStatusRepository { - private final Map data; - - public JCFUserStatusRepository() { - this.data = new HashMap<>(); - } - - @Override - public UserStatus save(UserStatus userStatus) { - return data.put(userStatus.getId(), userStatus); - } - - @Override - public Optional findById(UUID id) { - return Optional.ofNullable(data.get(id)); - } - - @Override - public Optional findByUserId(UUID userId) { - return data.values().stream() - .filter(userStatus -> userStatus.getUserId().equals(userId)) - .findFirst(); - } - - @Override - public List findAll() { - return data.values().stream().toList(); - } - - @Override - public void delete(UUID id) { - data.remove(id); - } -} diff --git a/src/main/java/com/sprint/mission/discodeit/service/basic/ChannelService.java b/src/main/java/com/sprint/mission/discodeit/service/ChannelService.java similarity index 93% rename from src/main/java/com/sprint/mission/discodeit/service/basic/ChannelService.java rename to src/main/java/com/sprint/mission/discodeit/service/ChannelService.java index 2a1b92be7..277cb2d65 100644 --- a/src/main/java/com/sprint/mission/discodeit/service/basic/ChannelService.java +++ b/src/main/java/com/sprint/mission/discodeit/service/ChannelService.java @@ -1,4 +1,4 @@ -package com.sprint.mission.discodeit.service.basic; +package com.sprint.mission.discodeit.service; import com.sprint.mission.discodeit.dto.data.ChannelDto; import com.sprint.mission.discodeit.dto.request.PrivateChannelCreateRequest; diff --git a/src/main/java/com/sprint/mission/discodeit/service/basic/MessageService.java b/src/main/java/com/sprint/mission/discodeit/service/MessageService.java similarity index 50% rename from src/main/java/com/sprint/mission/discodeit/service/basic/MessageService.java rename to src/main/java/com/sprint/mission/discodeit/service/MessageService.java index a940aa846..3101fca22 100644 --- a/src/main/java/com/sprint/mission/discodeit/service/basic/MessageService.java +++ b/src/main/java/com/sprint/mission/discodeit/service/MessageService.java @@ -1,4 +1,4 @@ -package com.sprint.mission.discodeit.service.basic; +package com.sprint.mission.discodeit.service; import com.sprint.mission.discodeit.dto.request.MessageCreateRequest; import com.sprint.mission.discodeit.dto.request.MessageUpdateRequest; @@ -9,9 +9,15 @@ import java.util.UUID; public interface MessageService { - Message create(MessageCreateRequest request, List binaryContentCreateRequests); - Message find(UUID messageId); - List findAllByChannelId(UUID channelId); - Message update(UUID id, MessageUpdateRequest request); - void delete(UUID messageId); + + Message create(MessageCreateRequest request, + List binaryContentCreateRequests); + + Message find(UUID messageId); + + List findAllByChannelId(UUID channelId); + + Message update(UUID id, MessageUpdateRequest request); + + void delete(UUID messageId); } diff --git a/src/main/java/com/sprint/mission/discodeit/service/basic/UserService.java b/src/main/java/com/sprint/mission/discodeit/service/UserService.java similarity index 93% rename from src/main/java/com/sprint/mission/discodeit/service/basic/UserService.java rename to src/main/java/com/sprint/mission/discodeit/service/UserService.java index c45353f95..a2937150a 100644 --- a/src/main/java/com/sprint/mission/discodeit/service/basic/UserService.java +++ b/src/main/java/com/sprint/mission/discodeit/service/UserService.java @@ -1,4 +1,4 @@ -package com.sprint.mission.discodeit.service.basic; +package com.sprint.mission.discodeit.service; import com.sprint.mission.discodeit.dto.request.BinaryContentCreateRequest; import com.sprint.mission.discodeit.dto.data.UserDto; diff --git a/src/main/java/com/sprint/mission/discodeit/service/basic/BasicChannelService.java b/src/main/java/com/sprint/mission/discodeit/service/basic/BasicChannelService.java index c3874a436..8ef95b746 100644 --- a/src/main/java/com/sprint/mission/discodeit/service/basic/BasicChannelService.java +++ b/src/main/java/com/sprint/mission/discodeit/service/basic/BasicChannelService.java @@ -8,9 +8,12 @@ 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.mapper.ChannelMapper; import com.sprint.mission.discodeit.repository.ChannelRepository; import com.sprint.mission.discodeit.repository.MessageRepository; import com.sprint.mission.discodeit.repository.ReadStatusRepository; +import com.sprint.mission.discodeit.repository.UserRepository; +import com.sprint.mission.discodeit.service.ChannelService; import java.time.Instant; import java.util.ArrayList; import java.util.Comparator; @@ -25,8 +28,10 @@ public class BasicChannelService implements ChannelService { private final ChannelRepository channelRepository; + private final UserRepository userRepository; private final MessageRepository messageRepository; private final ReadStatusRepository readStatusRepository; + private final ChannelMapper channelMapper; @Override public Channel createPrivate(PrivateChannelCreateRequest request) { @@ -36,7 +41,10 @@ public Channel createPrivate(PrivateChannelCreateRequest request) { // 채널에 참여하는 User의 정보를 받아 User 별 ReadStatus 정보를 생성합니다. request.participantIds().stream() - .map(userId -> new ReadStatus(userId, createdChannel.getId(), Instant.MIN)) + .map(userId -> new ReadStatus( + userRepository.findById(userId).orElseThrow(), + createdChannel, + Instant.MIN)) .forEach(readStatusRepository::save); return createdChannel; @@ -53,7 +61,7 @@ public Channel createPublic(PublicChannelCreateRequest channelCreateDTO) { @Override public ChannelDto find(UUID channelId) { return channelRepository.findById(channelId) - .map(this::toDto) + .map(channelMapper::toDto) .orElseThrow( () -> new NoSuchElementException("Channel with id " + channelId + " not found")); } @@ -61,7 +69,7 @@ public ChannelDto find(UUID channelId) { @Override public List findAllByUserId(UUID userId) { List mySubscribedChannelIds = readStatusRepository.findAllByUserId(userId).stream() - .map(ReadStatus::getChannelId) + .map(ReadStatus -> ReadStatus.getChannel().getId()) .toList(); return channelRepository.findAll().stream() @@ -69,7 +77,7 @@ public List findAllByUserId(UUID userId) { channel.getType().equals(ChannelType.PUBLIC) || mySubscribedChannelIds.contains(channel.getId()) ) - .map(this::toDto) + .map(channelMapper::toDto) .toList(); } @@ -95,42 +103,9 @@ public void delete(UUID channelId) { throw new NoSuchElementException("Channel with id " + channelId + " not found"); } // 관련된 도메인도 같이 삭제합니다. - messageRepository.findAllByChannelId(channelId) - .forEach(msg -> { - messageRepository.deleteById(msg.getId()); - }); - readStatusRepository.findAllByChannelId(channelId) - .forEach(status -> { - readStatusRepository.delete(status.getId()); - }); + messageRepository.deleteAllByChannelId(channelId); + readStatusRepository.deleteAllByChannelId(channelId); channelRepository.deleteById(channelId); } - - private ChannelDto toDto(Channel channel) { - Instant lastMessageAt = messageRepository.findAllByChannelId(channel.getId()) - .stream() - .sorted(Comparator.comparing(Message::getCreatedAt).reversed()) - .map(Message::getCreatedAt) - .limit(1) - .findFirst() - .orElse(Instant.MIN); - - List participantIds = new ArrayList<>(); - if (channel.getType().equals(ChannelType.PRIVATE)) { - readStatusRepository.findAllByChannelId(channel.getId()) - .stream() - .map(ReadStatus::getUserId) - .forEach(participantIds::add); - } - - return new ChannelDto( - channel.getId(), - channel.getType(), - channel.getName(), - channel.getDescription(), - participantIds, - lastMessageAt - ); - } } diff --git a/src/main/java/com/sprint/mission/discodeit/service/basic/BasicMessageService.java b/src/main/java/com/sprint/mission/discodeit/service/basic/BasicMessageService.java index f964e60fe..6069ffb00 100644 --- a/src/main/java/com/sprint/mission/discodeit/service/basic/BasicMessageService.java +++ b/src/main/java/com/sprint/mission/discodeit/service/basic/BasicMessageService.java @@ -4,9 +4,12 @@ import com.sprint.mission.discodeit.dto.request.MessageCreateRequest; import com.sprint.mission.discodeit.dto.request.BinaryContentCreateRequest; import com.sprint.mission.discodeit.entity.BinaryContent; +import com.sprint.mission.discodeit.entity.Channel; import com.sprint.mission.discodeit.entity.Message; +import com.sprint.mission.discodeit.entity.User; import com.sprint.mission.discodeit.entity.UserStatus; import com.sprint.mission.discodeit.repository.*; +import com.sprint.mission.discodeit.service.MessageService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -38,21 +41,24 @@ public Message create(MessageCreateRequest request, throw new NoSuchElementException("Author not found with id " + request.channelId()); } - List attachmentIds = binaryContentCreateRequests.stream() + List attachments = binaryContentCreateRequests.stream() .map(attachmentRequest -> { String fileName = attachmentRequest.fileName(); String contentType = attachmentRequest.contentType(); - byte[] bytes = attachmentRequest.bytes(); BinaryContent binaryContent = new BinaryContent(fileName, (long) bytes.length, - contentType, bytes); + contentType); binaryContentRepository.save(binaryContent); - return binaryContent.getId(); + return binaryContent; }).toList(); String content = request.content(); - Message message = new Message(content, channelId, authorId); - message.setAttachmentIds(attachmentIds); + User author = userRepository.findById(authorId) + .orElseThrow(() -> new NoSuchElementException("Author not found with id " + authorId)); + Channel channel = channelRepository.findById(channelId) + .orElseThrow(() -> new NoSuchElementException("Channel not found with id " + channelId)); + Message message = new Message(content, channel, author); + message.setAttachmentIds(attachments); messageRepository.save(message); @@ -78,7 +84,7 @@ public List findAllByChannelId(UUID channelId) { throw new NoSuchElementException("Channel not found with id " + channelId); } return messageRepository.findAll().stream() - .filter(msg -> msg.getChannelId().equals(channelId)) + .filter(msg -> msg.getChannel().getId().equals(channelId)) .toList(); } @@ -102,7 +108,8 @@ public void delete(UUID messageId) { .orElseThrow( () -> new NoSuchElementException("Message with id " + messageId + " not found")); - message.getAttachmentIds().forEach(binaryContentRepository::delete); + message.getAttachments().forEach(binaryContent -> + binaryContentRepository.delete(binaryContent.getId())); messageRepository.deleteById(messageId); } } diff --git a/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserService.java b/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserService.java index 7c8c8d9c6..c60d8f0f8 100644 --- a/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserService.java +++ b/src/main/java/com/sprint/mission/discodeit/service/basic/BasicUserService.java @@ -10,6 +10,7 @@ import com.sprint.mission.discodeit.repository.BinaryContentRepository; import com.sprint.mission.discodeit.repository.UserRepository; import com.sprint.mission.discodeit.repository.UserStatusRepository; +import com.sprint.mission.discodeit.service.UserService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -18,6 +19,7 @@ import java.util.NoSuchElementException; import java.util.Optional; import java.util.UUID; +import org.springframework.transaction.annotation.Transactional; @Service @RequiredArgsConstructor @@ -28,20 +30,21 @@ public class BasicUserService implements UserService { private final UserStatusRepository userStatusRepository; @Override + @Transactional public User create(UserCreateRequest request, Optional optionalBinaryContentCreateRequest) { // username과 email은 다른 유저와 같으면 안됩니다. validationUserCreateRequest(request); - UUID nullableProfileId = optionalBinaryContentCreateRequest + BinaryContent nullableProfile = optionalBinaryContentCreateRequest .map(profileRequest -> { String fileName = profileRequest.fileName(); String contentType = profileRequest.contentType(); byte[] bytes = profileRequest.bytes(); BinaryContent binaryContent = new BinaryContent(fileName, (long) bytes.length, - contentType, bytes); + contentType); binaryContentRepository.save(binaryContent); - return binaryContent.getId(); + return binaryContent; }) .orElse(null); @@ -49,11 +52,12 @@ public User create(UserCreateRequest request, request.username(), request.email(), request.password(), - nullableProfileId + nullableProfile ); // UserStatus 를 같이 생성합니다. - UserStatus userStatus = new UserStatus(user.getId(), Instant.now()); + UserStatus userStatus = new UserStatus(user, Instant.now()); + user.setStatus(userStatus); userStatusRepository.save(userStatus); return userRepository.save(user); @@ -92,16 +96,15 @@ public User update(UUID userId, UserUpdateRequest request, ); // 업데이트 할 바이너리 컨텐츠가 있으면 교체 - optionalBinaryContentRequest.ifPresent(binaryContentRequest -> { + BinaryContent nullableProfile = optionalBinaryContentRequest.ifPresent(binaryContentRequest -> { BinaryContent binaryContent = new BinaryContent( binaryContentRequest.fileName(), (long) binaryContentRequest.bytes().length, - binaryContentRequest.contentType(), - binaryContentRequest.bytes() + binaryContentRequest.contentType() ); binaryContentRepository.save(binaryContent); - user.setProfileId(binaryContent.getId()); - }); + return binaryContent; + }).orElse(null); return userRepository.save(user); } @@ -116,8 +119,8 @@ public void delete(UUID userId) { // BinaryContent(프로필), UserStatus userRepository.findById(userId) .ifPresent(user -> { - if (user.getProfileId() != null) { - binaryContentRepository.delete(user.getProfileId()); + if (user.getProfile() != null) { + binaryContentRepository.delete(user.getProfile().getId()); } }); userStatusRepository.findByUserId(userId) @@ -154,7 +157,7 @@ private UserDto toDto(User user) { user.getUpdatedAt(), user.getUsername(), user.getEmail(), - user.getProfileId(), + user.getProfile(), online ); } diff --git a/src/main/java/com/sprint/mission/discodeit/service/basic/ReadStatusService.java b/src/main/java/com/sprint/mission/discodeit/service/basic/ReadStatusService.java index 22e9be6bc..a674d8feb 100644 --- a/src/main/java/com/sprint/mission/discodeit/service/basic/ReadStatusService.java +++ b/src/main/java/com/sprint/mission/discodeit/service/basic/ReadStatusService.java @@ -34,7 +34,7 @@ public ReadStatus create(ReadStatusCreateRequest request) { boolean exists = readStatusRepository.findAll().stream() .anyMatch(rs -> rs.getId().equals(request.userId()) && - rs.getChannelId().equals(request.channelId()) + rs.getChannel().getId().equals(request.channelId()) ); if (exists) { @@ -42,7 +42,7 @@ public ReadStatus create(ReadStatusCreateRequest request) { "ReadStatus related to the same Channel and User already exists"); } - ReadStatus readStatus = new ReadStatus(user.getId(), channel.getId(), request.lastReadTime()); + ReadStatus readStatus = new ReadStatus(user, channel, request.lastReadTime()); return readStatusRepository.save(readStatus); } @@ -53,7 +53,7 @@ public ReadStatus find(UUID id) { public List findAllByUserId(UUID userId) { return readStatusRepository.findAll().stream() - .filter(readStatus -> readStatus.getUserId().equals(userId)) + .filter(readStatus -> readStatus.getUser().getId().equals(userId)) .toList(); } diff --git a/src/main/java/com/sprint/mission/discodeit/service/basic/UserStatusService.java b/src/main/java/com/sprint/mission/discodeit/service/basic/UserStatusService.java index af9660d53..931810420 100644 --- a/src/main/java/com/sprint/mission/discodeit/service/basic/UserStatusService.java +++ b/src/main/java/com/sprint/mission/discodeit/service/basic/UserStatusService.java @@ -30,7 +30,7 @@ public UserStatus create(UserStatusCreateRequest request) { throw new IllegalArgumentException("User with id " + request.userId() + " already exists"); } - UserStatus userStatus = new UserStatus(user.getId(), request.lastOnlineAt()); + UserStatus userStatus = new UserStatus(user, request.lastOnlineAt()); userStatusRepository.save(userStatus); return userStatus; diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 3523d48af..562c4c1f4 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,12 +1,39 @@ spring: application: newName:discodeit + datasource: #앞서 구성한 데이터베이스에 연결하기 위한 설정값을 application.yaml 파일에 작성하세요. + url: jdbc:postgresql://localhost:5432/discodeit + username: discodeit_user + password: discodeit1234 + driver-class-name: org.postgresql.Driver + hikari: + maximum-pool-size: 10 + minimum-idle: 5 + idle-timeout: 60000 + jpa: + hibernate: + ddl-auto: update -discodeit: - repository: - type: file # jcf | file - file-directory: .discodeit + show-sql: true + properties: + hibernate: + format_sql: true + use_sql_comments: true + dialect: org.hibernate.dialect.PostgreSQLDialect + open-in-view: false + #디버깅을 위해 SQL 로그와 관련된 설정값을 application.yaml 파일에 작성하세요. + logging: + level: + org: + hibernate: + SQL: DEBUG + orm.jdbc.bind: TRACE # Hibernate 6?? ??? ?? + springframework: + data: DEBUG + com: + example: + myapp: DEBUG server: port: 8080 diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql new file mode 100644 index 000000000..1af7acc00 --- /dev/null +++ b/src/main/resources/schema.sql @@ -0,0 +1,66 @@ +CREATE TABLE users +( + id CHAR(36) PRIMARY KEY, + created_at TIMESTAMP WITH TIME ZONE NOT NULL, + updated_at TIMESTAMP WITH TIME ZONE, + username VARCHAR(50) UNIQUE NOT NULL, + email VARCHAR(100) UNIQUE NOT NULL, + password VARCHAR(60) NOT NULL, + profile_id CHAR(36) REFERENCES binary_contents (id) ON DELETE SET NULL +); + +CREATE TABLE binary_contents +( + id UUID PRIMARY KEY, + created_at TIMESTAMPTZ NOT NULL, + file_name VARCHAR(255) NOT NULL, + size BIGINT NOT NULL, + content_type VARCHAR(100) NOT NULL, + bytes BYTEA NOT NULL +); + +CREATE TABLE user_statuses +( + id UUID PRIMARY KEY, + created_at TIMESTAMPTZ NOT NULL, + updated_at TIMESTAMPTZ, + user_id UUID NOT NULL REFERENCES users (id) ON DELETE CASCADE, + last_active_at TIMESTAMPTZ NOT NULL +); + +CREATE TABLE channels +( + id UUID PRIMARY KEY, + created_at TIMESTAMPTZ NOT NULL, + updated_at TIMESTAMPTZ, + name VARCHAR(100), + description VARCHAR(500), + type VARCHAR(10) NOT NULL CHECK (type IN ('PUBLIC', 'PRIVATE')) +); + +CREATE TABLE messages +( + id UUID PRIMARY KEY, + created_at TIMESTAMPTZ NOT NULL, + updated_at TIMESTAMPTZ, + content TEXT, + channel_id UUID NOT NULL REFERENCES channels (id) ON DELETE CASCADE, + author_id UUID REFERENCES users (id) ON DELETE SET NULL +); + +CREATE TABLE read_statuses +( + id UUID PRIMARY KEY, + created_at TIMESTAMPTZ NOT NULL, + updated_at TIMESTAMPTZ, + user_id UUID NOT NULL REFERENCES users (id) ON DELETE CASCADE, + channel_id UUID NOT NULL REFERENCES channels (id) ON DELETE CASCADE, + last_read_at TIMESTAMPTZ NOT NULL, + UNIQUE (user_id, channel_id) +); + +CREATE TABLE message_attachments +( + message_id UUID NOT NULL REFERENCES messages (id) ON DELETE CASCADE, + attachment_id UUID NOT NULL REFERENCES binary_contents (id) ON DELETE CASCADE +);