-
Notifications
You must be signed in to change notification settings - Fork 0
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
스탬프 추가 기능 구현 #65
스탬프 추가 기능 구현 #65
Changes from 30 commits
8e5634c
4e81f5d
50e338b
0aba065
32caa71
3faacee
9b26fac
ab7ee97
873c22a
db80635
66f10f8
4ef124a
b7b9506
f8b5696
1251c78
8a16b11
21c33ff
4026c19
045fe05
2c14f2c
402d92b
d70aea1
b6a9a36
838a006
8d43251
7ea18cd
bde459f
8d5662a
3d0bf15
3bbc1fb
e136ca5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
=== 새로운 스탬프 추가 | ||
==== 요청 | ||
operation::stamp-controller-test/스탬프_생성을_요청하면_새로운_스탬프를_생성한다[snippets='http-request,request-headers,request-fields'] | ||
==== 응답 | ||
operation::stamp-controller-test/스탬프_생성을_요청하면_새로운_스탬프를_생성한다[snippets='http-response,response-fields'] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package com.backend.blooming.stamp.application; | ||
|
||
import com.backend.blooming.goal.application.exception.NotFoundGoalException; | ||
import com.backend.blooming.goal.domain.Goal; | ||
import com.backend.blooming.goal.infrastructure.repository.GoalRepository; | ||
import com.backend.blooming.stamp.application.dto.CreateStampDto; | ||
import com.backend.blooming.stamp.application.dto.ReadStampDto; | ||
import com.backend.blooming.stamp.application.exception.CreateStampForbiddenException; | ||
import com.backend.blooming.stamp.domain.Day; | ||
import com.backend.blooming.stamp.domain.Message; | ||
import com.backend.blooming.stamp.domain.Stamp; | ||
import com.backend.blooming.stamp.domain.exception.InvalidStampException; | ||
import com.backend.blooming.stamp.infrastructure.repository.StampRepository; | ||
import com.backend.blooming.user.application.exception.NotFoundUserException; | ||
import com.backend.blooming.user.domain.User; | ||
import com.backend.blooming.user.infrastructure.repository.UserRepository; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
import java.util.List; | ||
|
||
@Service | ||
@Transactional | ||
@RequiredArgsConstructor | ||
public class StampService { | ||
|
||
private final GoalRepository goalRepository; | ||
private final UserRepository userRepository; | ||
private final StampRepository stampRepository; | ||
|
||
public ReadStampDto createStamp(final CreateStampDto createStampDto) { | ||
final Goal goal = getGoal(createStampDto.goalId()); | ||
final User user = getUser(createStampDto.userId()); | ||
validateUserInGoalTeams(goal, user.getId()); | ||
validateExistStamp(user.getId(), createStampDto.day()); | ||
final Stamp stamp = persistStamp(createStampDto, goal, user); | ||
|
||
return ReadStampDto.from(stamp); | ||
} | ||
|
||
private Goal getGoal(final Long goalId) { | ||
return goalRepository.findByIdWithUserAndDeletedIsFalse(goalId) | ||
.orElseThrow(NotFoundGoalException::new); | ||
} | ||
|
||
private User getUser(final Long userId) { | ||
return userRepository.findByIdAndDeletedIsFalse(userId) | ||
.orElseThrow(NotFoundUserException::new); | ||
} | ||
|
||
private void validateUserInGoalTeams(final Goal goal, final Long userId) { | ||
final List<Long> teamUserIds = goal.getTeams() | ||
.getGoalTeams() | ||
.stream() | ||
.map(goalTeam -> goalTeam.getUser().getId()) | ||
.toList(); | ||
if (!teamUserIds.contains(userId)) { | ||
throw new CreateStampForbiddenException(); | ||
} | ||
} | ||
|
||
private void validateExistStamp(final Long userId, final int day) { | ||
final boolean isExistsStamp = stampRepository.existsByUserIdAndDayAndDeletedIsFalse(userId, day); | ||
if (isExistsStamp) { | ||
throw new InvalidStampException.InvalidStampToCreate(); | ||
} | ||
} | ||
|
||
private Stamp persistStamp(final CreateStampDto createStampDto, final Goal goal, final User user) { | ||
final Stamp stamp = Stamp.builder() | ||
.goal(goal) | ||
.user(user) | ||
.day(new Day(goal, createStampDto.day())) | ||
.message(new Message(createStampDto.message())) | ||
.build(); | ||
|
||
return stampRepository.save(stamp); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package com.backend.blooming.stamp.application.dto; | ||
|
||
import com.backend.blooming.stamp.presentation.dto.request.CreateStampRequest; | ||
|
||
public record CreateStampDto( | ||
Long goalId, | ||
Long userId, | ||
int day, | ||
String message | ||
) { | ||
|
||
public static CreateStampDto of(final CreateStampRequest request, final Long userId) { | ||
return new CreateStampDto( | ||
request.goalId(), | ||
userId, | ||
request.day(), | ||
request.message() | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package com.backend.blooming.stamp.application.dto; | ||
|
||
import com.backend.blooming.stamp.domain.Stamp; | ||
import com.backend.blooming.themecolor.domain.ThemeColor; | ||
|
||
public record ReadStampDto( | ||
Long id, | ||
String userName, | ||
ThemeColor userColor, | ||
int day, | ||
String message | ||
) { | ||
|
||
public static ReadStampDto from(final Stamp stamp) { | ||
return new ReadStampDto( | ||
stamp.getGoal().getId(), | ||
stamp.getUser().getName(), | ||
stamp.getUser().getColor(), | ||
stamp.getDay().getDay(), | ||
stamp.getMessage().getMessage() | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.backend.blooming.stamp.application.exception; | ||
|
||
import com.backend.blooming.exception.BloomingException; | ||
import com.backend.blooming.exception.ExceptionMessage; | ||
|
||
public class CreateStampForbiddenException extends BloomingException { | ||
|
||
public CreateStampForbiddenException() { | ||
super(ExceptionMessage.CREATE_STAMP_FORBIDDEN); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package com.backend.blooming.stamp.domain; | ||
|
||
import com.backend.blooming.goal.domain.Goal; | ||
import com.backend.blooming.stamp.domain.exception.InvalidStampException; | ||
import jakarta.persistence.Column; | ||
import jakarta.persistence.Embeddable; | ||
import lombok.AccessLevel; | ||
import lombok.EqualsAndHashCode; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import lombok.ToString; | ||
|
||
import java.time.LocalDate; | ||
import java.time.temporal.ChronoUnit; | ||
|
||
@Embeddable | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
@Getter | ||
@EqualsAndHashCode | ||
@ToString | ||
public class Day { | ||
|
||
@Column(name = "stamp_day", nullable = false) | ||
private int day; | ||
|
||
public Day(final Goal goal, final int day) { | ||
this.day = validateDay(goal, day); | ||
} | ||
|
||
private int validateDay(final Goal goal, final int day) { | ||
final long nowStampDay = ChronoUnit.DAYS.between(goal.getGoalTerm().getStartDate(), LocalDate.now()) + 1; | ||
|
||
if (day > nowStampDay) { | ||
throw new InvalidStampException.InvalidStampDayFuture(); | ||
} | ||
if (day > goal.getGoalTerm().getDays()) { | ||
throw new InvalidStampException.InvalidStampDay(); | ||
} | ||
if (goal.getGoalTerm().getStartDate().isAfter(LocalDate.now())) { | ||
throw new InvalidStampException.InvalidStampDay(); | ||
} | ||
|
||
return day; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 별건 아니고, GoalTerm만 사용할 거면, goal이 아닌 goalTerm을 넘겨줬어도 좋을 것 같아요! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 그렇네요! 수정해서 올리도록 하겠습니다! |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package com.backend.blooming.stamp.domain; | ||
|
||
import com.backend.blooming.stamp.domain.exception.InvalidStampException; | ||
import jakarta.persistence.Column; | ||
import jakarta.persistence.Embeddable; | ||
import lombok.AccessLevel; | ||
import lombok.EqualsAndHashCode; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import lombok.ToString; | ||
|
||
@Embeddable | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
@Getter | ||
@EqualsAndHashCode | ||
@ToString | ||
public class Message { | ||
|
||
private static final int STAMP_MESSAGE_MAXIMUM = 30; | ||
|
||
@Column(columnDefinition = "text", nullable = false, length = STAMP_MESSAGE_MAXIMUM) | ||
private String message; | ||
|
||
public Message(final String message) { | ||
this.message = validateMessage(message); | ||
} | ||
|
||
private String validateMessage(final String message) { | ||
if (message == null || message.isEmpty()) { | ||
throw new InvalidStampException.InvalidStampMessage(); | ||
} | ||
if (message.length() > STAMP_MESSAGE_MAXIMUM) { | ||
throw new InvalidStampException.InvalidStampMessage(); | ||
} | ||
|
||
return message; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package com.backend.blooming.stamp.domain; | ||
|
||
import com.backend.blooming.common.entity.BaseTimeEntity; | ||
import com.backend.blooming.goal.domain.Goal; | ||
import com.backend.blooming.user.domain.User; | ||
import jakarta.persistence.Column; | ||
import jakarta.persistence.Embedded; | ||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.FetchType; | ||
import jakarta.persistence.ForeignKey; | ||
import jakarta.persistence.GeneratedValue; | ||
import jakarta.persistence.GenerationType; | ||
import jakarta.persistence.Id; | ||
import jakarta.persistence.JoinColumn; | ||
import jakarta.persistence.ManyToOne; | ||
import lombok.AccessLevel; | ||
import lombok.Builder; | ||
import lombok.EqualsAndHashCode; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import lombok.ToString; | ||
|
||
@Entity | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
@Getter | ||
@EqualsAndHashCode(of = "id", callSuper = false) | ||
@ToString(exclude = {"goal", "user"}) | ||
public class Stamp extends BaseTimeEntity { | ||
|
||
private static final int STAMP_DAY_MINIMUM = 1; | ||
private static final int STAMP_MESSAGE_MAXIMUM = 30; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 클래스에서는 더 이상 사용하지 않으니 없어져도 괜찮겠네요! |
||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Long id; | ||
|
||
@ManyToOne(fetch = FetchType.LAZY) | ||
@JoinColumn(name = "goal_id", foreignKey = @ForeignKey(name = "fk_stamp_goal"), nullable = false) | ||
private Goal goal; | ||
|
||
@ManyToOne(fetch = FetchType.LAZY) | ||
@JoinColumn(name = "user_id", foreignKey = @ForeignKey(name = "fk_stamp_user"), nullable = false) | ||
private User user; | ||
|
||
@Embedded | ||
private Day day; | ||
|
||
@Embedded | ||
private Message message; | ||
|
||
@Column(name = "is_deleted", nullable = false) | ||
private boolean deleted = false; | ||
|
||
@Builder | ||
private Stamp( | ||
final Goal goal, | ||
final User user, | ||
final Day day, | ||
final Message message | ||
) { | ||
this.goal = goal; | ||
this.user = user; | ||
this.day = day; | ||
this.message = message; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍