diff --git a/build.gradle b/build.gradle index dc48c9c..0797b51 100644 --- a/build.gradle +++ b/build.gradle @@ -2,6 +2,12 @@ plugins { id 'java' id 'org.springframework.boot' version '4.0.1' id 'io.spring.dependency-management' version '1.1.7' + + id 'com.google.protobuf' version '0.9.5' +} + +ext { + springGrpcVersion = "1.0.1" } group = 'flipnote' @@ -24,16 +30,78 @@ repositories { mavenCentral() } +dependencyManagement { + imports { + mavenBom "org.springframework.grpc:spring-grpc-dependencies:$springGrpcVersion" + } +} + dependencies { + implementation 'org.springframework.boot:spring-boot-starter-data-redis' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-validation' + + + //Spring gRPC Starter + implementation 'org.springframework.grpc:spring-grpc-spring-boot-starter' + implementation 'com.google.protobuf:protobuf-java' + implementation 'io.grpc:grpc-stub' + implementation 'org.springframework.boot:spring-boot-starter-webmvc' + compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' runtimeOnly 'com.mysql:mysql-connector-j' annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-data-jpa-test' - testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test' + testImplementation 'org.springframework.boot:spring-boot-starter-data-redis-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + testRuntimeOnly 'com.h2database:h2' + implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' + annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" + annotationProcessor "jakarta.annotation:jakarta.annotation-api" + annotationProcessor "jakarta.persistence:jakarta.persistence-api" +} + +def querydslDir = layout.buildDirectory.dir("generated/querydsl").get().asFile + +tasks.named('compileJava', JavaCompile) { + options.generatedSourceOutputDirectory.set(querydslDir) +} + +/** + * proto 컴파일 설정 + * - .proto 위치: src/main/proto + * - 생성 위치: build/generated/... + */ +protobuf { + protoc { + artifact = "com.google.protobuf:protoc:4.33.2" + } + plugins { + grpc { + artifact = "io.grpc:protoc-gen-grpc-java:1.77.1" + } + } + generateProtoTasks { + all().each { task -> + task.plugins { + grpc {} + } + } + } +} + +sourceSets { + main { + java { + srcDirs += [ + "$buildDir/generated/sources/proto/main/java", + "$buildDir/generated/sources/proto/main/grpc" + ] + } + } } tasks.named('test') { diff --git a/src/main/java/flipnote/group/adapter/in/web/.gitkeep b/src/main/java/flipnote/group/adapter/in/web/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/flipnote/group/adapter/in/web/GroupController.java b/src/main/java/flipnote/group/adapter/in/web/GroupController.java new file mode 100644 index 0000000..4972362 --- /dev/null +++ b/src/main/java/flipnote/group/adapter/in/web/GroupController.java @@ -0,0 +1,50 @@ +package flipnote.group.adapter.in.web; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import flipnote.group.api.dto.request.CreateGroupRequestDto; +import flipnote.group.api.dto.response.CreateGroupResponseDto; +import flipnote.group.application.port.in.CreateGroupUseCase; +import flipnote.group.application.port.in.command.CreateGroupCommand; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@RestController +@RequestMapping("/v1/groups") +public class GroupController { + + private final CreateGroupUseCase createGroupUseCase; + + /** + * 그룹 생성 API + * @param userId + * @param req + * @return + */ + @PostMapping("") + public ResponseEntity createGroup( + @RequestHeader("X-USER-ID") Long userId, + @RequestBody @Valid CreateGroupRequestDto req) { + + CreateGroupCommand cmd = new CreateGroupCommand( + req.name(), + req.category(), + req.description(), + req.joinPolicy(), + req.visibility(), + req.maxMember(), + req.imageRefId() + ); + + var result = createGroupUseCase.create(cmd, userId); + CreateGroupResponseDto res = CreateGroupResponseDto.from(result.groupId()); + return ResponseEntity.ok(res); + } + +} diff --git a/src/main/java/flipnote/group/adapter/out/.gitkeep b/src/main/java/flipnote/group/adapter/out/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/flipnote/group/adapter/out/entity/GroupEntity.java b/src/main/java/flipnote/group/adapter/out/entity/GroupEntity.java new file mode 100644 index 0000000..c3e9968 --- /dev/null +++ b/src/main/java/flipnote/group/adapter/out/entity/GroupEntity.java @@ -0,0 +1,69 @@ +package flipnote.group.adapter.out.entity; + +import flipnote.group.application.port.in.command.CreateGroupCommand; +import flipnote.group.domain.model.BaseEntity; +import flipnote.group.domain.model.group.Category; +import flipnote.group.domain.model.group.JoinPolicy; +import flipnote.group.domain.model.group.Visibility; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Entity +@Table(name = "app_groups") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class GroupEntity extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false, length = 50) + private String name; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private Category category; + + @Column(nullable = false) + private String description; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private JoinPolicy joinPolicy; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private Visibility visibility; + + @Column(nullable = false) + private Integer maxMember; + + private Long imageRefId; + + @Column(nullable = false) + private Integer memberCount; + + @Builder + private GroupEntity(String name, Category category, String description, JoinPolicy joinPolicy, Visibility visibility, + Integer maxMember, Long imageRefId, Integer memberCount) { + this.name = name; + this.category = category; + this.description = description; + this.joinPolicy = joinPolicy; + this.visibility = visibility; + this.maxMember = maxMember; + this.imageRefId = imageRefId; + this.memberCount = memberCount; + } +} diff --git a/src/main/java/flipnote/group/adapter/out/entity/GroupMemberEntity.java b/src/main/java/flipnote/group/adapter/out/entity/GroupMemberEntity.java new file mode 100644 index 0000000..8161510 --- /dev/null +++ b/src/main/java/flipnote/group/adapter/out/entity/GroupMemberEntity.java @@ -0,0 +1,81 @@ +package flipnote.group.adapter.out.entity; + +import flipnote.group.domain.model.BaseEntity; +import flipnote.group.domain.model.member.GroupMemberRole; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Entity +@Table( + name = "group_members", + uniqueConstraints = { + @UniqueConstraint( + name = "uk_group_members_group_user", + columnNames = {"group_id", "user_id"} + ) + } +) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class GroupMemberEntity extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "group_id", nullable = false) + private Long groupId; + + @Column(name = "user_id", nullable = false) + private Long userId; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private GroupMemberRole role; + + @Builder + private GroupMemberEntity(Long groupId, Long userId, GroupMemberRole role) { + this.groupId = groupId; + this.userId = userId; + this.role = (role != null) ? role : GroupMemberRole.MEMBER; + } + + /** + * 오너인 경우 + * @param groupId + * @param userId + * @return + */ + public static GroupMemberEntity createOwner(Long groupId, Long userId) { + return GroupMemberEntity.builder() + .groupId(groupId) + .userId(userId) + .role(GroupMemberRole.OWNER) + .build(); + } + + /** + * 오너가 아닌 경우 + * @param groupId + * @param userId + * @return + */ + public static GroupMemberEntity join(Long groupId, Long userId) { + return GroupMemberEntity.builder() + .groupId(groupId) + .userId(userId) + .role(GroupMemberRole.MEMBER) + .build(); + } +} diff --git a/src/main/java/flipnote/group/adapter/out/persistence/GroupMemberRepositoryAdapter.java b/src/main/java/flipnote/group/adapter/out/persistence/GroupMemberRepositoryAdapter.java new file mode 100644 index 0000000..ad903b3 --- /dev/null +++ b/src/main/java/flipnote/group/adapter/out/persistence/GroupMemberRepositoryAdapter.java @@ -0,0 +1,26 @@ +package flipnote.group.adapter.out.persistence; + +import org.springframework.stereotype.Repository; + +import flipnote.group.adapter.out.persistence.mapper.GroupMemberMapper; +import flipnote.group.application.port.out.GroupMemberRepositoryPort; +import flipnote.group.domain.model.member.GroupMemberRole; +import flipnote.group.infrastructure.persistence.jpa.GroupMemberRepository; +import lombok.RequiredArgsConstructor; + +@Repository +@RequiredArgsConstructor +public class GroupMemberRepositoryAdapter implements GroupMemberRepositoryPort { + + private final GroupMemberRepository groupMemberRepository; + + /** + * 오너일 경우 + * @param groupId + * @param userId + */ + @Override + public void saveOwner(Long groupId, Long userId) { + groupMemberRepository.save(GroupMemberMapper.createOwner(groupId, userId)); + } +} diff --git a/src/main/java/flipnote/group/adapter/out/persistence/GroupRepositoryAdapter.java b/src/main/java/flipnote/group/adapter/out/persistence/GroupRepositoryAdapter.java new file mode 100644 index 0000000..63d3372 --- /dev/null +++ b/src/main/java/flipnote/group/adapter/out/persistence/GroupRepositoryAdapter.java @@ -0,0 +1,23 @@ +package flipnote.group.adapter.out.persistence; + +import org.springframework.stereotype.Repository; + +import flipnote.group.adapter.out.entity.GroupEntity; +import flipnote.group.adapter.out.persistence.mapper.GroupMapper; +import flipnote.group.application.port.out.GroupRepositoryPort; +import flipnote.group.domain.model.group.Group; +import flipnote.group.infrastructure.persistence.jpa.GroupRepository; +import lombok.RequiredArgsConstructor; + +@Repository +@RequiredArgsConstructor +public class GroupRepositoryAdapter implements GroupRepositoryPort { + + private final GroupRepository groupRepository; + + @Override + public Long saveNewGroup(Group group) { + GroupEntity entity = GroupMapper.createNewEntity(group); + return groupRepository.save(entity).getId(); + } +} diff --git a/src/main/java/flipnote/group/adapter/out/persistence/mapper/GroupMapper.java b/src/main/java/flipnote/group/adapter/out/persistence/mapper/GroupMapper.java new file mode 100644 index 0000000..c89f7f0 --- /dev/null +++ b/src/main/java/flipnote/group/adapter/out/persistence/mapper/GroupMapper.java @@ -0,0 +1,31 @@ +package flipnote.group.adapter.out.persistence.mapper; + +import org.springframework.stereotype.Component; + +import flipnote.group.adapter.out.entity.GroupEntity; +import flipnote.group.domain.model.group.Group; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@Component +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class GroupMapper { + + /** + * 새로운 엔티티 생성 + * @param domain + * @return + */ + public static GroupEntity createNewEntity(Group domain) { + return GroupEntity.builder() + .name(domain.getName()) + .category(domain.getCategory()) + .description(domain.getDescription()) + .joinPolicy(domain.getJoinPolicy()) + .visibility(domain.getVisibility()) + .maxMember(domain.getMaxMember()) + .imageRefId(domain.getImageRefId()) + .memberCount(domain.getMemberCount()) + .build(); + } +} diff --git a/src/main/java/flipnote/group/adapter/out/persistence/mapper/GroupMemberMapper.java b/src/main/java/flipnote/group/adapter/out/persistence/mapper/GroupMemberMapper.java new file mode 100644 index 0000000..5d1e4b7 --- /dev/null +++ b/src/main/java/flipnote/group/adapter/out/persistence/mapper/GroupMemberMapper.java @@ -0,0 +1,20 @@ +package flipnote.group.adapter.out.persistence.mapper; + +import org.springframework.stereotype.Component; + +import flipnote.group.adapter.out.entity.GroupMemberEntity; +import flipnote.group.domain.model.member.GroupMemberRole; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@Component +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class GroupMemberMapper { + public static GroupMemberEntity createOwner(Long groupId, Long userId) { + return GroupMemberEntity.builder() + .groupId(groupId) + .userId(userId) + .role(GroupMemberRole.OWNER) + .build(); + } +} diff --git a/src/main/java/flipnote/group/api/dto/request/.gitkeep b/src/main/java/flipnote/group/api/dto/request/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/flipnote/group/api/dto/request/CreateGroupRequestDto.java b/src/main/java/flipnote/group/api/dto/request/CreateGroupRequestDto.java new file mode 100644 index 0000000..833fd01 --- /dev/null +++ b/src/main/java/flipnote/group/api/dto/request/CreateGroupRequestDto.java @@ -0,0 +1,20 @@ +package flipnote.group.api.dto.request; + +import flipnote.group.domain.model.group.Category; +import flipnote.group.domain.model.group.JoinPolicy; +import flipnote.group.domain.model.group.Visibility; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +public record CreateGroupRequestDto( + @NotBlank @Size(max = 50) String name, + @NotNull Category category, + @NotBlank String description, + @NotNull JoinPolicy joinPolicy, + @NotNull Visibility visibility, + @NotNull @Min(1) @Max(100) Integer maxMember, + Long imageRefId +) {} diff --git a/src/main/java/flipnote/group/api/dto/response/.gitkeep b/src/main/java/flipnote/group/api/dto/response/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/flipnote/group/api/dto/response/CreateGroupResponseDto.java b/src/main/java/flipnote/group/api/dto/response/CreateGroupResponseDto.java new file mode 100644 index 0000000..39d2c76 --- /dev/null +++ b/src/main/java/flipnote/group/api/dto/response/CreateGroupResponseDto.java @@ -0,0 +1,9 @@ +package flipnote.group.api.dto.response; + +public record CreateGroupResponseDto( + Long groupId +) { + public static CreateGroupResponseDto from(Long groupId) { + return new CreateGroupResponseDto(groupId); + } +} diff --git a/src/main/java/flipnote/group/application/in/.gitkeep b/src/main/java/flipnote/group/application/in/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/flipnote/group/application/out/.gitkeep b/src/main/java/flipnote/group/application/out/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/flipnote/group/application/port/in/CreateGroupUseCase.java b/src/main/java/flipnote/group/application/port/in/CreateGroupUseCase.java new file mode 100644 index 0000000..d58a135 --- /dev/null +++ b/src/main/java/flipnote/group/application/port/in/CreateGroupUseCase.java @@ -0,0 +1,8 @@ +package flipnote.group.application.port.in; + +import flipnote.group.application.port.in.command.CreateGroupCommand; +import flipnote.group.application.port.in.result.CreateGroupResult; + +public interface CreateGroupUseCase { + CreateGroupResult create(CreateGroupCommand cmd, Long userId); +} diff --git a/src/main/java/flipnote/group/application/port/in/command/CreateGroupCommand.java b/src/main/java/flipnote/group/application/port/in/command/CreateGroupCommand.java new file mode 100644 index 0000000..40a2f58 --- /dev/null +++ b/src/main/java/flipnote/group/application/port/in/command/CreateGroupCommand.java @@ -0,0 +1,16 @@ +package flipnote.group.application.port.in.command; + +import flipnote.group.domain.model.group.Category; +import flipnote.group.domain.model.group.JoinPolicy; +import flipnote.group.domain.model.group.Visibility; + +public record CreateGroupCommand( + String name, + Category category, + String description, + JoinPolicy joinPolicy, + Visibility visibility, + int maxMember, + Long imageRefId +) { +} diff --git a/src/main/java/flipnote/group/application/port/in/result/CreateGroupResult.java b/src/main/java/flipnote/group/application/port/in/result/CreateGroupResult.java new file mode 100644 index 0000000..277caa7 --- /dev/null +++ b/src/main/java/flipnote/group/application/port/in/result/CreateGroupResult.java @@ -0,0 +1,5 @@ +package flipnote.group.application.port.in.result; + +public record CreateGroupResult( + Long groupId +) {} diff --git a/src/main/java/flipnote/group/application/port/out/GroupMemberRepositoryPort.java b/src/main/java/flipnote/group/application/port/out/GroupMemberRepositoryPort.java new file mode 100644 index 0000000..8622b41 --- /dev/null +++ b/src/main/java/flipnote/group/application/port/out/GroupMemberRepositoryPort.java @@ -0,0 +1,7 @@ +package flipnote.group.application.port.out; + +import flipnote.group.domain.model.member.GroupMemberRole; + +public interface GroupMemberRepositoryPort { + void saveOwner(Long groupId, Long userId); +} diff --git a/src/main/java/flipnote/group/application/port/out/GroupRepositoryPort.java b/src/main/java/flipnote/group/application/port/out/GroupRepositoryPort.java new file mode 100644 index 0000000..b1a2f7c --- /dev/null +++ b/src/main/java/flipnote/group/application/port/out/GroupRepositoryPort.java @@ -0,0 +1,7 @@ +package flipnote.group.application.port.out; + +import flipnote.group.domain.model.group.Group; + +public interface GroupRepositoryPort { + Long saveNewGroup(Group group); +} diff --git a/src/main/java/flipnote/group/application/service/.gitkeep b/src/main/java/flipnote/group/application/service/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/flipnote/group/application/service/CreateGroupService.java b/src/main/java/flipnote/group/application/service/CreateGroupService.java new file mode 100644 index 0000000..5ad6e36 --- /dev/null +++ b/src/main/java/flipnote/group/application/service/CreateGroupService.java @@ -0,0 +1,43 @@ +package flipnote.group.application.service; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import flipnote.group.adapter.out.persistence.mapper.GroupMapper; +import flipnote.group.application.port.in.CreateGroupUseCase; +import flipnote.group.application.port.in.command.CreateGroupCommand; +import flipnote.group.application.port.in.result.CreateGroupResult; +import flipnote.group.application.port.out.GroupMemberRepositoryPort; +import flipnote.group.application.port.out.GroupRepositoryPort; +import flipnote.group.domain.model.group.Group; +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class CreateGroupService implements CreateGroupUseCase { + + private final GroupRepositoryPort groupRepository; + private final GroupMemberRepositoryPort groupMemberRepository; + + /** + * 그룹 생성 + * @param cmd + * @param userId + * @return + */ + @Override + @Transactional + public CreateGroupResult create(CreateGroupCommand cmd, Long userId) { + + //도메인 생성 및 검증 + var domainGroup = Group.create(cmd); + + //그룹 도메인 -> 엔티티 변환 후 저장 + Long groupId = groupRepository.saveNewGroup(domainGroup); + + //그룹 멤버 저장 + groupMemberRepository.saveOwner(groupId, userId); + + return new CreateGroupResult(groupId); + } +} diff --git a/src/main/java/flipnote/group/domain/model/BaseEntity.java b/src/main/java/flipnote/group/domain/model/BaseEntity.java index 6db03b9..5541274 100644 --- a/src/main/java/flipnote/group/domain/model/BaseEntity.java +++ b/src/main/java/flipnote/group/domain/model/BaseEntity.java @@ -1,4 +1,50 @@ package flipnote.group.domain.model; +import java.time.LocalDateTime; + +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import jakarta.persistence.Column; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; +import lombok.Getter; + +@Getter +@MappedSuperclass +@EntityListeners(AuditingEntityListener.class) public abstract class BaseEntity { + + @CreatedDate + @Column(updatable = false) + private LocalDateTime createdAt; + + @LastModifiedDate + private LocalDateTime modifiedAt; + + private LocalDateTime deletedAt; + + /** + * 삭제 표시 + * 삭제시간 == 현재 시간 + */ + protected void markDeleted() { + this.deletedAt = LocalDateTime.now(); + } + + /** + * 삭제되었는지 확인하는 메서드 + * @return + */ + public boolean isDeleted() { + return this.deletedAt != null; + } + + /** + * 삭제 복구 + */ + protected void restore() { + this.deletedAt = null; + } } diff --git a/src/main/java/flipnote/group/domain/model/group/.gitkeep b/src/main/java/flipnote/group/domain/model/group/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/flipnote/group/domain/model/group/Category.java b/src/main/java/flipnote/group/domain/model/group/Category.java new file mode 100644 index 0000000..6b9635d --- /dev/null +++ b/src/main/java/flipnote/group/domain/model/group/Category.java @@ -0,0 +1,16 @@ +package flipnote.group.domain.model.group; + +public enum Category { + IT, ENGLISH, MATH, SCIENCE, HISTORY, GEOGRAPHY, KOREAN; + + public static Category from(String category) { + if (category == null || category.isEmpty()) { + return null; + } + try { + return Category.valueOf(category); + } catch (IllegalArgumentException e) { + return null; + } + } +} diff --git a/src/main/java/flipnote/group/domain/model/group/Group.java b/src/main/java/flipnote/group/domain/model/group/Group.java new file mode 100644 index 0000000..e65508d --- /dev/null +++ b/src/main/java/flipnote/group/domain/model/group/Group.java @@ -0,0 +1,113 @@ +package flipnote.group.domain.model.group; + +import flipnote.group.application.port.in.command.CreateGroupCommand; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class Group { + + private Long id; + + private String name; + private Category category; + private String description; + private JoinPolicy joinPolicy; + private Visibility visibility; + + private int maxMember; + private Long imageRefId; + private int memberCount; + + /** + * 신규로 그룹 생성 + * @param cmd + * @return + */ + public static Group create(CreateGroupCommand cmd) { + validate(cmd); + + Group group = new Group(); + group.name = cmd.name(); + group.category = cmd.category(); + group.description = cmd.description(); + group.joinPolicy = cmd.joinPolicy(); + group.visibility = cmd.visibility(); + group.maxMember = cmd.maxMember(); + group.imageRefId = cmd.imageRefId(); + + group.memberCount = 1; + + return group; + } + + /** + * DB 가져오기 + * @param id + * @param name + * @param category + * @param description + * @param joinPolicy + * @param visibility + * @param maxMember + * @param imageRefId + * @param memberCount + * @return + */ + public static Group getGroup( + Long id, + String name, + Category category, + String description, + JoinPolicy joinPolicy, + Visibility visibility, + int maxMember, + Long imageRefId, + int memberCount + ) { + Group g = new Group(); + g.id = id; + g.name = name; + g.category = category; + g.description = description; + g.joinPolicy = joinPolicy; + g.visibility = visibility; + g.maxMember = maxMember; + g.imageRefId = imageRefId; + g.memberCount = memberCount; + return g; + } + + /** + * 파라미터 검증 + * @param cmd + */ + private static void validate(CreateGroupCommand cmd) { + if (cmd.name() == null || cmd.name().isBlank()) { + throw new IllegalArgumentException("name required"); + } + if (cmd.maxMember() < 1 || cmd.maxMember() > 100) { + throw new IllegalArgumentException("maxMember invalid"); + } + if (cmd.category() == null) { + throw new IllegalArgumentException("category required"); + } + if (cmd.joinPolicy() == null) { + throw new IllegalArgumentException("join required"); + } + if (cmd.visibility() == null) { + throw new IllegalArgumentException("visibility required"); + } + if (cmd.description() == null || cmd.description().isBlank()) { + throw new IllegalArgumentException("description required"); + } + if (cmd.name().length() > 50) { + throw new IllegalArgumentException("name too long"); + } + } +} diff --git a/src/main/java/flipnote/group/domain/model/group/GroupId.java b/src/main/java/flipnote/group/domain/model/group/GroupId.java new file mode 100644 index 0000000..7467641 --- /dev/null +++ b/src/main/java/flipnote/group/domain/model/group/GroupId.java @@ -0,0 +1,4 @@ +package flipnote.group.domain.model.group; + +public record GroupId(Long value) { +} diff --git a/src/main/java/flipnote/group/domain/model/group/JoinPolicy.java b/src/main/java/flipnote/group/domain/model/group/JoinPolicy.java new file mode 100644 index 0000000..5cf2481 --- /dev/null +++ b/src/main/java/flipnote/group/domain/model/group/JoinPolicy.java @@ -0,0 +1,6 @@ +package flipnote.group.domain.model.group; + +public enum JoinPolicy { + OPEN, // 바로 가입 가능 + APPROVAL // 가입 승인 필요 +} diff --git a/src/main/java/flipnote/group/domain/model/group/Visibility.java b/src/main/java/flipnote/group/domain/model/group/Visibility.java new file mode 100644 index 0000000..4d0cfab --- /dev/null +++ b/src/main/java/flipnote/group/domain/model/group/Visibility.java @@ -0,0 +1,5 @@ +package flipnote.group.domain.model.group; + +public enum Visibility { + PUBLIC, PRIVATE +} diff --git a/src/main/java/flipnote/group/domain/model/member/.gitkeep b/src/main/java/flipnote/group/domain/model/member/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/flipnote/group/domain/model/member/GroupMember.java b/src/main/java/flipnote/group/domain/model/member/GroupMember.java new file mode 100644 index 0000000..a08555c --- /dev/null +++ b/src/main/java/flipnote/group/domain/model/member/GroupMember.java @@ -0,0 +1,47 @@ +package flipnote.group.domain.model.member; + +import flipnote.group.domain.model.group.GroupId; +import flipnote.group.domain.model.user.UserId; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class GroupMember { + private Long id; + private GroupId groupId; + private UserId userId; + private GroupMemberRole role; + + /** + * 유저가 오너일 경우 + * @param groupId + * @param ownerUserId + * @return + */ + public static GroupMember createOwner(GroupId groupId, UserId ownerUserId) { + GroupMember gm = new GroupMember(); + gm.groupId = groupId; + gm.userId = ownerUserId; + gm.role = GroupMemberRole.OWNER; + return gm; + } + + /** + * 오너가 아닐 경우 + * @param groupId + * @param userId + * @return + */ + public static GroupMember join(GroupId groupId, UserId userId) { + GroupMember gm = new GroupMember(); + gm.groupId = groupId; + gm.userId = userId; + gm.role = GroupMemberRole.MEMBER; + return gm; + } + +} diff --git a/src/main/java/flipnote/group/domain/model/member/GroupMemberRole.java b/src/main/java/flipnote/group/domain/model/member/GroupMemberRole.java new file mode 100644 index 0000000..4ca3784 --- /dev/null +++ b/src/main/java/flipnote/group/domain/model/member/GroupMemberRole.java @@ -0,0 +1,5 @@ +package flipnote.group.domain.model.member; + +public enum GroupMemberRole { + OWNER, HEAD_MANAGER, MANAGER, STAFF, MEMBER +} diff --git a/src/main/java/flipnote/group/domain/model/user/UserId.java b/src/main/java/flipnote/group/domain/model/user/UserId.java new file mode 100644 index 0000000..1545b53 --- /dev/null +++ b/src/main/java/flipnote/group/domain/model/user/UserId.java @@ -0,0 +1,4 @@ +package flipnote.group.domain.model.user; + +public record UserId(Long value) { +} diff --git a/src/main/java/flipnote/group/infrastructure/config/.gitkeep b/src/main/java/flipnote/group/infrastructure/config/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/flipnote/group/infrastructure/config/AuditingConfig.java b/src/main/java/flipnote/group/infrastructure/config/AuditingConfig.java new file mode 100644 index 0000000..57927c5 --- /dev/null +++ b/src/main/java/flipnote/group/infrastructure/config/AuditingConfig.java @@ -0,0 +1,13 @@ +package flipnote.group.infrastructure.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; + +/** + * + */ +@EnableJpaAuditing +@Configuration +public class AuditingConfig { + +} diff --git a/src/main/java/flipnote/group/infrastructure/persistence/.gitkeep b/src/main/java/flipnote/group/infrastructure/persistence/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/flipnote/group/infrastructure/persistence/jpa/GroupMemberRepository.java b/src/main/java/flipnote/group/infrastructure/persistence/jpa/GroupMemberRepository.java new file mode 100644 index 0000000..4da80d7 --- /dev/null +++ b/src/main/java/flipnote/group/infrastructure/persistence/jpa/GroupMemberRepository.java @@ -0,0 +1,8 @@ +package flipnote.group.infrastructure.persistence.jpa; + +import org.springframework.data.jpa.repository.JpaRepository; + +import flipnote.group.adapter.out.entity.GroupMemberEntity; + +public interface GroupMemberRepository extends JpaRepository { +} diff --git a/src/main/java/flipnote/group/infrastructure/persistence/jpa/GroupRepository.java b/src/main/java/flipnote/group/infrastructure/persistence/jpa/GroupRepository.java new file mode 100644 index 0000000..2452a5a --- /dev/null +++ b/src/main/java/flipnote/group/infrastructure/persistence/jpa/GroupRepository.java @@ -0,0 +1,8 @@ +package flipnote.group.infrastructure.persistence.jpa; + +import org.springframework.data.jpa.repository.JpaRepository; + +import flipnote.group.adapter.out.entity.GroupEntity; + +public interface GroupRepository extends JpaRepository { +}