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

[feat] 식당 좋아요 관련 api #43

Merged
merged 29 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
ffba207
[feat] create Heart object
kgy1008 Jul 10, 2024
78f8e44
[feat] add hearted store
kgy1008 Jul 10, 2024
d301c60
[fix] add transactional annotation
kgy1008 Jul 10, 2024
50d2b95
[refac] change to Lazy loading
kgy1008 Jul 10, 2024
96e8a51
[refac] rename class name
kgy1008 Jul 10, 2024
8ed7d37
[refac] delete cascade
kgy1008 Jul 10, 2024
ba4e651
[feat] logic for heart additional
kgy1008 Jul 10, 2024
0688dcd
[feat] add global exception handler
kgy1008 Jul 10, 2024
0b69897
[refac] delete unused import
kgy1008 Jul 10, 2024
1d5de90
[feat] add logic for delete heart
kgy1008 Jul 11, 2024
0f1f997
[feat] add logic for delete heart
kgy1008 Jul 11, 2024
25d4b61
[feat] add logic for update total heart count
kgy1008 Jul 11, 2024
7b4b3af
[refac] renaming dto
kgy1008 Jul 11, 2024
011176c
[refac] apply code reviews
kgy1008 Jul 11, 2024
c5053ec
[refac] change to wrapper class
kgy1008 Jul 11, 2024
ca7cd7a
[feat] add logic for heart service
kgy1008 Jul 11, 2024
91bcb49
[refac] renaming api endpoint
kgy1008 Jul 11, 2024
ce82be9
[refac] delete response dto
kgy1008 Jul 12, 2024
728fe8b
[refac] apply code reviews
kgy1008 Jul 12, 2024
3590cb2
[refac] rename path variable
kgy1008 Jul 12, 2024
c71081c
[refac] change variable name
kgy1008 Jul 12, 2024
96dcdbd
[refac] return response dto
kgy1008 Jul 12, 2024
d20ca42
[refac] return response dto and change http code
kgy1008 Jul 12, 2024
fd3ff54
[refac] add final
kgy1008 Jul 12, 2024
c9db421
[refac] divided method responsibility
kgy1008 Jul 12, 2024
94c3af0
[refac] change method name
kgy1008 Jul 12, 2024
0a5cad1
[refac] divide method responsibility
kgy1008 Jul 12, 2024
9c0a185
[refac] apply code reviews
kgy1008 Jul 12, 2024
7664272
[refac] add missing final keyword
kgy1008 Jul 12, 2024
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 @@ -4,6 +4,7 @@
import org.hankki.hankkiserver.api.dto.HankkiResponse;
import org.hankki.hankkiserver.common.code.BusinessErrorCode;
import org.hankki.hankkiserver.common.exception.BadRequestException;
import org.hankki.hankkiserver.common.exception.ConflictException;
import org.hankki.hankkiserver.common.exception.NotFoundException;
import org.hankki.hankkiserver.common.exception.UnauthorizedException;

Expand Down Expand Up @@ -32,6 +33,12 @@ public HankkiResponse<Void> handleEntityNotFoundException(NotFoundException e) {
return HankkiResponse.fail(e.getErrorCode());
}

@ExceptionHandler(ConflictException.class)
public HankkiResponse<Void> handleConflictException(ConflictException e) {
log.error("handleConflictException() in GlobalExceptionHandler throw ConflictException : {}", e.getMessage());
return HankkiResponse.fail(e.getErrorCode());
}

@ExceptionHandler(Exception.class)
public HankkiResponse<Void> handleException(Exception e) {
log.error("handleException() in GlobalExceptionHandler throw Exception : {}", e.getMessage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ public class AuthService {
private String adminKey;

private final UserFinder userFinder;
private final UserSaver userSaver;
private final UserUpdater userUpdater;
private final UserInfoFinder userInfoFinder;
private final UserInfoSaver userInfoSaver;
private final UserInfoUpdater userInfoUpdater;
private final UserInfoDeleter userInfoDeleter;
private final JwtProvider jwtProvider;
private final JwtValidator jwtValidator;
Expand Down Expand Up @@ -131,9 +131,9 @@ private String getRefreshToken(final Long userId) {
}

private void saveUserAndUserInfo(final User user) {
userSaver.saveUser(user);
userUpdater.saveUser(user);
UserInfo userInfo = UserInfo.createMemberInfo(user, null);
userInfoSaver.saveUserInfo(userInfo);
userInfoUpdater.saveUserInfo(userInfo);
}

private void validateRefreshToken(final String refreshToken, final Long userId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,15 @@ public class UserFinder {

private final UserRepository userRepository;

public User getUser(final long userId) {
return userRepository.findById(userId)
public User getUser(final Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new NotFoundException(UserErrorCode.USER_NOT_FOUND));
}

public User getUserReference(final Long id) {
return userRepository.getReferenceById(id);
}

public boolean isRegisteredUser(final Platform platform, final SocialInfoDto socialInfo) {
return userRepository.existsByPlatformAndSerialIdAndMemberStatus(
platform,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class UserInfoDeleter {

private final UserInfoRepository userInfoRepository;

public void softDelete(final long userId) {
public void softDelete(final Long userId) {
userInfoRepository.softDeleteByUserId(userId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class UserInfoFinder {

private final UserInfoRepository userInfoRepository;

public UserInfo getUserInfo(final long userId) {
public UserInfo getUserInfo(final Long userId) {
return userInfoRepository.findByUserId(userId)
.orElseThrow(() -> new NotFoundException(UserErrorCode.USER_INFO_NOT_FOUND));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

@Component
@RequiredArgsConstructor
public class UserInfoSaver {
public class UserInfoUpdater {

private final UserInfoRepository userInfoRepository;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

@Component
@RequiredArgsConstructor
public class UserSaver {
public class UserUpdater {

private final UserRepository userRepository;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,27 @@
import lombok.RequiredArgsConstructor;
import org.hankki.hankkiserver.api.dto.HankkiResponse;
import org.hankki.hankkiserver.api.store.service.StoreQueryService;
import org.hankki.hankkiserver.api.store.service.response.CategoriesResponse;
import org.hankki.hankkiserver.api.store.service.response.PriceCategoriesResponse;
import org.hankki.hankkiserver.api.store.service.response.SortOptionsResponse;
import org.hankki.hankkiserver.api.store.service.response.StoreThumbnailResponse;
import org.hankki.hankkiserver.api.store.service.response.*;
import org.hankki.hankkiserver.common.code.CommonSuccessCode;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.hankki.hankkiserver.auth.UserId;
import org.hankki.hankkiserver.api.store.service.HeartCommandService;
import org.hankki.hankkiserver.api.store.service.command.StoreDeleteCommand;
import org.hankki.hankkiserver.api.store.service.command.StorePostCommand;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;


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

private final StoreQueryService storeQueryService;
private final HeartCommandService heartCommandService;

@GetMapping("/stores/{id}/thumbnail")
public HankkiResponse<StoreThumbnailResponse> getStoreThumbnail(@PathVariable Long id) {
Expand All @@ -39,4 +44,14 @@ public HankkiResponse<SortOptionsResponse> getSortOptions() {
public HankkiResponse<PriceCategoriesResponse> getPrices() {
return HankkiResponse.success(CommonSuccessCode.OK, storeQueryService.getPriceCategories());
}

@PostMapping("/stores/{id}/hearts")
public HankkiResponse<HeartCreateResponse> createHeartStore(@UserId Long userId, @PathVariable Long id) {
return HankkiResponse.success(CommonSuccessCode.CREATED, heartCommandService.createHeart(StorePostCommand.of(userId, id)));
}

@DeleteMapping("/stores/{id}/hearts")
public HankkiResponse<HeartDeleteResponse> deleteHeartStore(@UserId Long userId, @PathVariable Long id) {
return HankkiResponse.success(CommonSuccessCode.OK, heartCommandService.deleteHeart(StoreDeleteCommand.of(userId, id)));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

매개변수에 final 추가해주세요

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package org.hankki.hankkiserver.api.store.service;

import lombok.RequiredArgsConstructor;
import org.hankki.hankkiserver.api.auth.service.UserFinder;
import org.hankki.hankkiserver.api.store.service.command.StoreDeleteCommand;
import org.hankki.hankkiserver.api.store.service.command.StorePostCommand;
import org.hankki.hankkiserver.api.store.service.response.HeartCreateResponse;
import org.hankki.hankkiserver.api.store.service.response.HeartDeleteResponse;
import org.hankki.hankkiserver.common.code.HeartErrorCode;
import org.hankki.hankkiserver.common.exception.ConflictException;
import org.hankki.hankkiserver.domain.heart.model.Heart;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional
public class HeartCommandService {

private final HeartUpdater heartUpdater;
private final HeartFinder heartFinder;
private final HeartDeleter heartDeleter;
private final UserFinder userFinder;
private final StoreFinder storeFinder;

public HeartCreateResponse createHeart(final StorePostCommand storePostCommand) {
Long userId = storePostCommand.userId();
Long storeId = storePostCommand.storeId();
validateCreateStoreHeart(userId, storeId);
saveStoreHeart(userId, storeId);
updateStoreHeartCount(storeId, false);
return HeartCreateResponse.of(storeId, true);
}

public HeartDeleteResponse deleteHeart(final StoreDeleteCommand storeDeleteCommand) {
Long userId = storeDeleteCommand.userId();
Long storeId = storeDeleteCommand.storeId();
validateDeleteStoreHeart(userId, storeId);
heartDeleter.deleteHeart(userId,storeId);
updateStoreHeartCount(storeId, true);
return HeartDeleteResponse.of(storeId, false);
}

private void validateCreateStoreHeart(final Long userId, final Long storeId) {
heartFinder.findByUserIdAndStoreId(userId, storeId)
.ifPresent(heart -> {
throw new ConflictException(HeartErrorCode.ALREADY_EXISTED_HEART);
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

existsby로 boolean 값만 받아오는 것이 더 나아 보입니다.
뭔가 ValidateCreate이 중복돼서 의미가 한번에 안 와닿는데,
validateCreateStoreHeart는 어떨까요?


private void validateDeleteStoreHeart(final Long userId, final Long storeId) {
heartFinder.findByUserIdAndStoreId(userId, storeId)
.orElseThrow(() -> new ConflictException(HeartErrorCode.ALREADY_DELETED_HEART));
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기도 existby로 바꿔주시고
사소하지만 애초에 좋아요가 없는 경우도 에러코드 상수의 이름은 ALREADY_DELETED_HEART보다 모든 경우를 나타낼 수 있도록 변경해주세요

private void saveStoreHeart(final Long userId, final Long storeId) {
Heart heart = Heart.createHeart(userFinder.getUserReference(userId), storeFinder.getStoreReference(storeId));
heartUpdater.saveHeart(heart);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

객체를 매개변수로 받고 한줄로 줄여주세요. heart변수 만들 필요 없어 보입니다.

}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

굳이 heart 변수 안 써도 될 거 같습니다. 바로 saveHeart에 넣어주세요

private void updateStoreHeartCount(final Long storeId, final boolean isDeleted) {
storeFinder.getStore(storeId).updateHearCount(isDeleted);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.hankki.hankkiserver.api.store.service;

import lombok.RequiredArgsConstructor;
import org.hankki.hankkiserver.domain.heart.repository.HeartRepository;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class HeartDeleter {

private final HeartRepository heartRepository;

public void deleteHeart(final Long userId, final Long storeId) {
heartRepository.deleteByUserIdAndStoreId(userId, storeId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.hankki.hankkiserver.api.store.service;

import lombok.RequiredArgsConstructor;
import org.hankki.hankkiserver.domain.heart.model.Heart;
import org.hankki.hankkiserver.domain.heart.repository.HeartRepository;
import org.springframework.stereotype.Component;

import java.util.Optional;

@Component
@RequiredArgsConstructor
public class HeartFinder {

private final HeartRepository heartRepository;

public Optional<Heart> findByUserIdAndStoreId(final Long userId, final Long storeId) {
return heartRepository.findByUserIdAndStoreId(userId, storeId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.hankki.hankkiserver.api.store.service;

import lombok.RequiredArgsConstructor;
import org.hankki.hankkiserver.domain.heart.model.Heart;
import org.hankki.hankkiserver.domain.heart.repository.HeartRepository;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class HeartUpdater {

private final HeartRepository heartRepository;

public void saveHeart(final Heart heart) {
heartRepository.save(heart);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,13 @@ protected Store findByIdWhereDeletedIsFalse(Long id) {
return storeRepository.findByIdAndIsDeletedIsFalse(id)
.orElseThrow(() -> new NotFoundException(StoreErrorCode.STORE_NOT_FOUND));
}

public Store getStore(final Long id) {
return storeRepository.findById(id)
.orElseThrow(()-> new NotFoundException(StoreErrorCode.STORE_NOT_FOUND));
}

public Store getStoreReference(Long id) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

final를 달아주세용

return storeRepository.getReferenceById(id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.hankki.hankkiserver.api.store.service.command;

public record StoreDeleteCommand(
Long userId,
Long storeId
) {
public static StoreDeleteCommand of(Long userId, Long storeId) {
return new StoreDeleteCommand(userId, storeId);
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기도 통일성을 위해 final 붙여주세요

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.hankki.hankkiserver.api.store.service.command;

public record StorePostCommand(
Long userId,
Long storeId
) {
public static StorePostCommand of(Long userId, Long storeId) {
return new StorePostCommand(userId, storeId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.hankki.hankkiserver.api.store.service.response;

public record HeartCreateResponse(
Long storeId,
boolean isHearted
) {
public static HeartCreateResponse of(Long storeId, boolean isHearted) {
return new HeartCreateResponse(storeId, isHearted);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.hankki.hankkiserver.api.store.service.response;

public record HeartDeleteResponse(
Long storeId,
boolean isHearted
) {
public static HeartDeleteResponse of(Long storeId, boolean isHearted) {
return new HeartDeleteResponse(storeId, isHearted);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.hankki.hankkiserver.common.code;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;

@Getter
@RequiredArgsConstructor
public enum HeartErrorCode implements ErrorCode {

ALREADY_EXISTED_HEART(HttpStatus.CONFLICT, "이미 좋아요 한 가게입니다."),
ALREADY_DELETED_HEART(HttpStatus.CONFLICT, "이미 좋아요를 취소한 가게입니다.");

private final HttpStatus httpStatus;
private final String message;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.hankki.hankkiserver.common.exception;

import lombok.Getter;
import org.hankki.hankkiserver.common.code.ErrorCode;

@Getter
public class ConflictException extends RuntimeException {
private final ErrorCode errorCode;

public ConflictException(ErrorCode errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ public class FavoriteStore {
@Column(name = "favorite_store_id")
private Long id;

@ManyToOne
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "store_id")
private Store store;

@ManyToOne
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "favorite_id")
private Favorite favorite;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hankki.hankkiserver.domain.store.model.Store;
Expand All @@ -25,4 +26,17 @@ public class Heart {
@JoinColumn(name = "store_id")
private Store store;

@Builder
private Heart(User user, Store store) {
this.user = user;
this.store = store;
}

public static final Heart createHeart(User user, Store store) {
return Heart.builder()
.user(user)
.store(store)
.build();
}

}
Loading
Loading