Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: 스터디/투두 생성 시에 잘못된 값으로 입력이 넘어와도 정상적으로 생성하는 버그를 수정한다 #245

Merged
merged 5 commits into from
Aug 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@
import com.yigongil.backend.domain.studymember.StudyMember;
import com.yigongil.backend.domain.studymember.StudyMemberRepository;
import com.yigongil.backend.exception.MemberNotFoundException;
import com.yigongil.backend.request.MemberJoinRequest;
import com.yigongil.backend.request.ProfileUpdateRequest;
import com.yigongil.backend.response.FinishedStudyResponse;
import com.yigongil.backend.response.ProfileResponse;
import com.yigongil.backend.utils.DateConverter;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand Down Expand Up @@ -50,7 +48,7 @@ public ProfileResponse findById(Long id) {
member.getNickname(),
member.getGithubId(),
member.getProfileImageUrl(),
(double) studyService.calculateSuccessRate(member),
studyService.calculateSuccessRate(member),
calculateNumberOfSuccessRounds(member),
99, // TODO: 2023/07/27 티어 진행률은 추후 티어 진행 알고리즘 회의 후 추가
member.getTier(),
Expand All @@ -65,7 +63,7 @@ private FinishedStudyResponse createFinishedStudyResponse(StudyMember studyMembe
study.getId(),
study.getName(),
study.calculateAverageTier(),
DateConverter.toStringFormat(study.getStartAt()),
study.getStartAt().toLocalDate(),
study.getTotalRoundCount(),
study.getPeriodUnit().toStringFormat(study.getPeriodOfRound()),
study.sizeOfCurrentMembers(),
Expand All @@ -91,15 +89,4 @@ private int calculateNumberOfSuccessRounds(Member member) {
public void update(Member member, ProfileUpdateRequest request) {
member.updateProfile(request.nickname(), request.introduction());
}

@Transactional
public Long join(MemberJoinRequest request) {
Member member = memberRepository.save(
Member.builder()
.githubId(request.githubId())
.build()
);

return member.getId();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import com.yigongil.backend.response.RoundResponse;
import com.yigongil.backend.response.TodoResponse;
import com.yigongil.backend.response.UpcomingStudyResponse;
import com.yigongil.backend.utils.DateConverter;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
Expand Down Expand Up @@ -90,7 +89,7 @@ public HomeResponse findCurrentRoundOfStudies(Member member) {
currentRound.getId(),
TodoResponse.fromNecessaryTodo(currentRoundOfMemberOwn, currentRound),
leftDays,
DateConverter.toStringFormat(endAt),
endAt.toLocalDate(),
currentRoundOfMembers.calculateMembersProgress(),
TodoResponse.fromOptionalTodo(currentRoundOfMemberOwn.getOptionalTodos())
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import com.yigongil.backend.response.RecruitingStudyResponse;
import com.yigongil.backend.response.StudyDetailResponse;
import com.yigongil.backend.response.StudyMemberResponse;
import com.yigongil.backend.utils.DateConverter;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -55,7 +54,7 @@ public Long create(Member member, StudyCreateRequest request) {
request.name(),
request.introduction(),
request.numberOfMaximumMembers(),
request.startAt(),
request.startAt().atStartOfDay(),
request.totalRoundCount(),
request.periodOfRound(),
member
Expand Down Expand Up @@ -189,7 +188,7 @@ public List<MyStudyResponse> findMyStudies(Member member) {
.getCode(),
study.getName(),
study.calculateAverageTier(),
DateConverter.toStringFormat(study.getStartAt()),
study.getStartAt().toLocalDate(),
study.getTotalRoundCount(),
study.getPeriodUnit()
.toStringFormat(study.getPeriodOfRound()),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.yigongil.backend.config;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JacksonConfig {

private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy.MM.dd");

@Bean
public ObjectMapper serializingObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer());
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer());
objectMapper.registerModule(javaTimeModule);
return objectMapper;
}

static class LocalDateSerializer extends JsonSerializer<LocalDate> {
@Override
public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(value.format(FORMATTER));
}
}

static class LocalDateDeserializer extends JsonDeserializer<LocalDate> {
@Override
public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
return LocalDate.parse(p.getValueAsString(), FORMATTER);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ public class Nickname {
private String nickname;

public Nickname(String nickname) {
if (Objects.isNull(nickname)) {
nickname = "guest" + (int) (Math.random() * 999);
if (nickname == null) {
nickname = "게스트" + (int) (Math.random() * 99999);
}
validate(nickname);
this.nickname = nickname;
}

public Nickname() {
protected Nickname() {

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
@Entity
public class Round extends BaseEntity {

private static final int MAX_CONTENT_LENGTH = 20;
private static final int MAX_TODO_CONTENT_LENGTH = 20;
private static final int MIN_TODO_CONTENT_LENGTH = 1;

@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
Expand All @@ -41,7 +42,7 @@ public class Round extends BaseEntity {
@Column(nullable = false)
private Integer roundNumber;

@Column(length = MAX_CONTENT_LENGTH)
@Column(length = MAX_TODO_CONTENT_LENGTH)
private String necessaryToDoContent;

@ManyToOne(fetch = FetchType.LAZY)
Expand Down Expand Up @@ -96,7 +97,7 @@ public static List<Round> of(Integer totalRoundCount, Member master) {
}

public Long createNecessaryTodo(Member author, String content) {
validateLength(content);
validateTodoLength(content);
validateMaster(author);
if (Objects.nonNull(necessaryToDoContent)) {
throw new NecessaryTodoAlreadyExistException("필수 투두가 이미 존재합니다.", necessaryToDoContent);
Expand All @@ -113,14 +114,21 @@ public void validateMaster(Member member) {
}

public OptionalTodo createOptionalTodo(Member author, String content) {
validateLength(content);
validateTodoLength(content);
RoundOfMember targetRoundOfMember = findRoundOfMemberBy(author);
return targetRoundOfMember.createOptionalTodo(content);
}

private void validateLength(String content) {
if (content.length() > MAX_CONTENT_LENGTH) {
throw new InvalidTodoLengthException("투두 길이는 20자까지 가능합니다.", content);
private void validateTodoLength(String content) {
int contentLength = content.length();
if (contentLength > MAX_TODO_CONTENT_LENGTH || contentLength < MIN_TODO_CONTENT_LENGTH) {
throw new InvalidTodoLengthException(
String.format(
"투두 길이는 %d자 이상 %d자 이하로 작성 가능합니다.",
MIN_TODO_CONTENT_LENGTH,
MAX_TODO_CONTENT_LENGTH
), content
);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
import com.yigongil.backend.domain.roundofmember.RoundOfMembers;
import com.yigongil.backend.exception.CannotStartException;
import com.yigongil.backend.exception.InvalidMemberSizeException;
import com.yigongil.backend.exception.InvalidNumberOfMaximumStudyMember;
import com.yigongil.backend.exception.InvalidProcessingStatusException;
import com.yigongil.backend.exception.InvalidStudyNameLengthException;
import com.yigongil.backend.exception.RoundNotFoundException;
import com.yigongil.backend.utils.DateConverter;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
Expand All @@ -36,6 +37,12 @@
public class Study extends BaseEntity {

private static final int ONE_MEMBER = 1;
private static final int MIN_NAME_LENGTH = 1;
private static final int MAX_NAME_LENGTH = 30;
private static final int MIN_MEMBER_SIZE = 2;
private static final int MAX_MEMBER_SIZE = 8;


@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
private Long id;
Expand Down Expand Up @@ -94,6 +101,9 @@ public Study(
List<Round> rounds,
PeriodUnit periodUnit
) {
name = name.strip();
validateNumberOfMaximumMembers(numberOfMaximumMembers);
validateName(name);
this.id = id;
this.name = name;
this.introduction = introduction;
Expand All @@ -108,19 +118,44 @@ public Study(
this.rounds = rounds == null ? new ArrayList<>() : rounds;
}

private void validateNumberOfMaximumMembers(Integer numberOfMaximumMembers) {
if (numberOfMaximumMembers < MIN_MEMBER_SIZE || numberOfMaximumMembers > MAX_MEMBER_SIZE) {
throw new InvalidNumberOfMaximumStudyMember(
String.format(
"스터디의 정원은 최소 %d명 최대 %d명까지 설정 가능합니다",
MIN_MEMBER_SIZE,
MAX_MEMBER_SIZE
), numberOfMaximumMembers
);
}
}

private void validateName(String name) {
int nameLength = name.length();
if (nameLength < MIN_NAME_LENGTH || nameLength > MAX_NAME_LENGTH) {
throw new InvalidStudyNameLengthException(
String.format(
"스터디 이름의 길이는 %d자 이상 %d자 이하로 작성 가능합니다.",
MIN_NAME_LENGTH,
MAX_NAME_LENGTH
), nameLength
);
}
}

public static Study initializeStudyOf(
String name,
String introduction,
Integer numberOfMaximumMembers,
String startAt,
LocalDateTime startAt,
Integer totalRoundCount,
String periodOfRound,
Member master
) {
Study study = Study.builder()
.name(name)
.numberOfMaximumMembers(numberOfMaximumMembers)
.startAt(DateConverter.toLocalDateTime(startAt))
.startAt(startAt)
.totalRoundCount(totalRoundCount)
.periodOfRound(PeriodUnit.getPeriodNumber(periodOfRound))
.periodUnit(PeriodUnit.getPeriodUnit(periodOfRound))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.yigongil.backend.exception;

import org.springframework.http.HttpStatus;

public class InvalidNumberOfMaximumStudyMember extends HttpException {

public InvalidNumberOfMaximumStudyMember(String message, Integer numberOfMaximumMembers) {
super(HttpStatus.BAD_REQUEST, message, String.valueOf(numberOfMaximumMembers));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.yigongil.backend.exception;

import org.springframework.http.HttpStatus;

public class InvalidStudyNameLengthException extends HttpException {

public InvalidStudyNameLengthException(String message, int inputLength) {
super(HttpStatus.BAD_REQUEST, message, String.valueOf(inputLength));
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package com.yigongil.backend.request;

import javax.validation.constraints.NotBlank;

public record ProfileUpdateRequest(
@NotBlank(message = "회원 닉네임이 공백입니다.")
String nickname,
@NotBlank(message = "회원 소개가 공백입니다.")
String introduction
) {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
package com.yigongil.backend.request;

import java.time.LocalDate;
import javax.validation.constraints.FutureOrPresent;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Positive;

public record StudyCreateRequest(
@NotBlank(message = "스터디 이름이 공백입니다.")
String name,
@Positive(message = "스터디 멤버 정원은 음수일 수 없습니다.")
Integer numberOfMaximumMembers,
String startAt,
@FutureOrPresent(message = "예상시작일은 과거로 설정할 수 없습니다.")
LocalDate startAt,
@Positive(message = "스터디 총 회차가 입력되지 않았습니다.")
Integer totalRoundCount,
@NotBlank(message = "스터디 주기가 입력되지 않았습니다.")
String periodOfRound,
@NotBlank(message = "스터디 소개가 공백입니다.")
String introduction
) {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
package com.yigongil.backend.request;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Positive;

public record TodoCreateRequest(
@NotNull(message = "필수/선택 투두 여부는 필수로 입력되어야 합니다.")
Boolean isNecessary,
@Positive(message = "라운드 식별자는 음수일 수 없습니다.")
Long roundId,
@NotBlank(message = "투두 내용이 비었습니다.")
String content
) {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.yigongil.backend.response;

import java.time.LocalDate;

public record FinishedStudyResponse(
Long id,
String name,
Integer averageTier,
String startAt,
LocalDate startAt,
Integer totalRoundCount,
String periodOfRound,
Integer numberOfCurrentMembers,
Expand Down
Loading