Skip to content

Commit

Permalink
Merge pull request #31 from wvssm/develop
Browse files Browse the repository at this point in the history
[스프린트 3] 강수민 - 팀 생성 기능 추가
  • Loading branch information
ddglackrp authored Dec 2, 2024
2 parents 9a37b99 + 77e24da commit 61bd825
Show file tree
Hide file tree
Showing 14 changed files with 470 additions and 0 deletions.
4 changes: 4 additions & 0 deletions src/main/java/PNUMEAT/Backend/domain/auth/entity/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import static PNUMEAT.Backend.global.images.ImageConstant.DEFAULT_MEMBER_PROFILE_IMAGE_URL;

import PNUMEAT.Backend.domain.article.entity.Article;
import PNUMEAT.Backend.domain.team_member.entity.TeamMember;
import jakarta.persistence.*;

import java.util.ArrayList;
Expand Down Expand Up @@ -40,6 +41,9 @@ public class Member {
@OneToMany(mappedBy = "member")
private List<Article> articles = new ArrayList<>();

@OneToMany(mappedBy = "member")
private List<TeamMember> teamMembers = new ArrayList<>();

protected Member() {
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package PNUMEAT.Backend.domain.team.controller;

import PNUMEAT.Backend.domain.auth.dto.request.MemberProfileRequest;
import PNUMEAT.Backend.domain.auth.entity.Member;
import PNUMEAT.Backend.domain.auth.service.MemberService;
import PNUMEAT.Backend.domain.team.dto.request.TeamRequest;
import PNUMEAT.Backend.domain.team.service.TeamService;
import PNUMEAT.Backend.global.error.dto.response.ApiResponse;
import PNUMEAT.Backend.global.security.annotation.LoginMember;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import static PNUMEAT.Backend.global.response.ResponseMessageEnum.TEAM_CREATED_SUCCESS;

@RestController
@RequestMapping("/api/v1/teams")
@RequiredArgsConstructor
public class TeamController {

private final TeamService teamService;

@PostMapping
public ResponseEntity<ApiResponse<?>> createTeam(@ModelAttribute @Valid TeamRequest teamRequest,
@RequestPart(value = "teamIcon", required = false) MultipartFile teamIcon,
@LoginMember Member member){
teamService.createTeam(teamRequest, teamIcon, member);

return ResponseEntity.status(TEAM_CREATED_SUCCESS.getStatusCode())
.contentType(MediaType.APPLICATION_JSON)
.body(ApiResponse.createResponseWithMessage(TEAM_CREATED_SUCCESS.getMessage()));
}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package PNUMEAT.Backend.domain.team.dto.request;

import PNUMEAT.Backend.global.validation.annotation.NotNullOrBlank;
import jakarta.validation.constraints.*;

public record TeamRequest(
@NotNullOrBlank
@Pattern(
regexp = "^[가-힣a-zA-Z0-9\\s]*$",
message = "한글, 영어, 숫자, 공백만 입력 가능합니다."
)
@Size(
max = 10,
message = "글자 수를 초과했습니다."
)
String teamName,

@Pattern(
regexp = "^[가-힣a-zA-Z0-9!@#$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>/?`~ ]*$",
message = "팀 설명은 한글, 영어, 숫자, 기본 특수문자만 사용가능합니다."
)
@Size(
max = 50,
message = "글자 수를 초과했습니다."
)
String teamExplain,

@NotNullOrBlank
String topic,

@NotNullOrBlank
@Digits(integer = Integer.MAX_VALUE, fraction = 0, message = "2 ~ 20 사이의 숫자를 입력해주세요.")
@Min(value = 2, message = "2 ~ 20 사이의 숫자를 입력해주세요.")
@Max(value = 20, message = "2 ~ 20 사이의 숫자를 입력해주세요.")
int memberLimit,

@Pattern(
regexp = "^[0-9]{4}$",
message = "숫자 4자리를 입력해주세요."
)
String password
) {
}
66 changes: 66 additions & 0 deletions src/main/java/PNUMEAT/Backend/domain/team/entity/Team.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package PNUMEAT.Backend.domain.team.entity;

import PNUMEAT.Backend.domain.auth.entity.Member;
import PNUMEAT.Backend.domain.team.enums.Topic;
import PNUMEAT.Backend.domain.team_member.entity.TeamMember;
import jakarta.persistence.*;
import lombok.Builder;
import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

import static PNUMEAT.Backend.global.images.ImageConstant.DEFAULT_TEAM_IMAGE_URL;

@Entity
@Getter
@EntityListeners(AuditingEntityListener.class)
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long teamId;

private String teamName;

private String teamIconUrl = DEFAULT_TEAM_IMAGE_URL;

private Topic teamTopic;

private String teamExplain;

private int maxParticipant;

private String teamPassword;

@CreatedDate
private LocalDateTime createdAt;

private int streakDays;

@ManyToOne
@JoinColumn(name = "member_id")
private Member teamManager;

@OneToMany(mappedBy = "team")
private List<TeamMember> teamMembers = new ArrayList<>();

protected Team(){
}

@Builder
public Team(String teamName, Topic teamTopic, String teamExplain, int maxParticipant, String teamPassword, Member teamManager) {
this.teamName = teamName;
this.teamTopic = teamTopic;
this.teamExplain = teamExplain;
this.maxParticipant = maxParticipant;
this.teamPassword = teamPassword;
this.teamManager = teamManager;
}

public void updateTeamIconUrl(String teamIconUrl){
this.teamIconUrl = teamIconUrl;
}
}
36 changes: 36 additions & 0 deletions src/main/java/PNUMEAT/Backend/domain/team/enums/Topic.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package PNUMEAT.Backend.domain.team.enums;

import PNUMEAT.Backend.global.error.ErrorCode;
import PNUMEAT.Backend.global.error.Team24Exception;

import static PNUMEAT.Backend.global.error.ErrorCode.TOPIC_INVALID_ERROR;

public enum Topic {
CODINGTEST(1, "코딩테스트"),
STUDY(2, "스터디");

private final int code;
private final String name;

Topic(int code, String name) {
this.code = code;
this.name = name;
}

public int getCode() {
return code;
}

public String getName() {
return name;
}

public static Topic fromName(String name){
for(Topic topic : Topic.values()){
if(topic.getName().equals(name)){
return topic;
}
}
throw new Team24Exception(TOPIC_INVALID_ERROR);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package PNUMEAT.Backend.domain.team.repository;

import PNUMEAT.Backend.domain.team.entity.Team;
import org.springframework.data.jpa.repository.JpaRepository;

public interface TeamRepository extends JpaRepository<Team, Long> {
}
49 changes: 49 additions & 0 deletions src/main/java/PNUMEAT/Backend/domain/team/service/TeamService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package PNUMEAT.Backend.domain.team.service;

import PNUMEAT.Backend.domain.auth.entity.Member;
import PNUMEAT.Backend.domain.team.dto.request.TeamRequest;
import PNUMEAT.Backend.domain.team.entity.Team;
import PNUMEAT.Backend.domain.team.enums.Topic;
import PNUMEAT.Backend.domain.team.repository.TeamRepository;
import PNUMEAT.Backend.domain.team_member.entity.TeamMember;
import PNUMEAT.Backend.domain.team_member.repository.TeamMemberRepository;
import PNUMEAT.Backend.global.images.ImageService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class TeamService {

private final TeamRepository teamRepository;
private final TeamMemberRepository teamMemberRepository;
private final ImageService imageService;

@Transactional
public Team createTeam(TeamRequest teamRequest,
MultipartFile teamIcon, Member member){

Team team = Team.builder()
.teamName(teamRequest.teamName())
.teamExplain(teamRequest.teamExplain())
.teamTopic(Topic.fromName(teamRequest.topic()))
.maxParticipant(teamRequest.memberLimit())
.teamPassword(teamRequest.password())
.teamManager(member)
.build();

if (teamIcon != null){
String teamIconUrl = imageService.teamImageUpload(teamIcon);
team.updateTeamIconUrl(teamIconUrl);
}

TeamMember teamMember = new TeamMember(team, member);

teamMemberRepository.save(teamMember);

return teamRepository.save(team);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package PNUMEAT.Backend.domain.team_member.entity;

import PNUMEAT.Backend.domain.auth.entity.Member;
import PNUMEAT.Backend.domain.team.entity.Team;
import jakarta.persistence.*;

import java.util.Objects;

@Entity
public class TeamMember {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long teamInfoId;

@ManyToOne
@JoinColumn(name="member_id")
private Member member;

@ManyToOne
@JoinColumn(name="team_id")
private Team team;

protected TeamMember(){}

public TeamMember(Team team, Member member) {
this.member = member;
this.team = team;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TeamMember that = (TeamMember) o;
return Objects.equals(teamInfoId, that.teamInfoId) && Objects.equals(member, that.member) && Objects.equals(team, that.team);
}

@Override
public int hashCode() {
return Objects.hash(teamInfoId, member, team);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package PNUMEAT.Backend.domain.team_member.repository;

import PNUMEAT.Backend.domain.team_member.entity.TeamMember;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface TeamMemberRepository extends JpaRepository<TeamMember, Long> {
}
3 changes: 3 additions & 0 deletions src/main/java/PNUMEAT/Backend/global/error/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ public enum ErrorCode {

ARTICLE_FORBIDDEN_ERROR(HttpStatus.FORBIDDEN, "게시글 권한이 없습니다."),

//TEAM
TOPIC_INVALID_ERROR(HttpStatus.BAD_REQUEST, "주제가 올바른 형식이 아닙니다."),

// ARTICLE
ARTICLE_NOT_FOUND_ERROR(HttpStatus.NOT_FOUND, "존재 하지 않는 게시글 입니다."),

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package PNUMEAT.Backend.global.response;

import lombok.Getter;

@Getter
public enum ResponseMessageEnum {
// TEAM
TEAM_CREATED_SUCCESS("팀이 성공적으로 생성되었습니다.", 201);


private final String message;
private final int statusCode;

ResponseMessageEnum(String message, int statusCode) {
this.message = message;
this.statusCode = statusCode;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package PNUMEAT.Backend.global.validation.annotation;

import PNUMEAT.Backend.global.validation.validator.NotNullOrBlankValidator;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = NotNullOrBlankValidator.class)
public @interface NotNullOrBlank {
String message() default "값은 null이거나 공백만 입력될 수 없습니다.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package PNUMEAT.Backend.global.validation.validator;

import PNUMEAT.Backend.global.validation.annotation.NotNullOrBlank;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

public class NotNullOrBlankValidator implements ConstraintValidator<NotNullOrBlank, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return value != null && !value.trim().isEmpty();
}
}
Loading

0 comments on commit 61bd825

Please sign in to comment.