Skip to content

Commit 061f4c0

Browse files
authored
[feat] 식당 좋아요 관련 api (#43)
* [feat] create Heart object * [feat] add hearted store * [fix] add transactional annotation * [refac] change to Lazy loading * [refac] rename class name * [refac] delete cascade * [feat] logic for heart additional * [feat] add global exception handler * [refac] delete unused import * [feat] add logic for delete heart * [feat] add logic for delete heart * [feat] add logic for update total heart count * [refac] renaming dto * [refac] apply code reviews * [refac] change to wrapper class * [feat] add logic for heart service * [refac] renaming api endpoint * [refac] delete response dto * [refac] apply code reviews * [refac] rename path variable * [refac] change variable name * [refac] return response dto * [refac] return response dto and change http code * [refac] add final * [refac] divided method responsibility * [refac] change method name * [refac] divide method responsibility * [refac] apply code reviews * [refac] add missing final keyword
1 parent 7a2aeea commit 061f4c0

25 files changed

+272
-18
lines changed

src/main/java/org/hankki/hankkiserver/api/advice/GlobalExceptionHandler.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import org.hankki.hankkiserver.api.dto.HankkiResponse;
55
import org.hankki.hankkiserver.common.code.BusinessErrorCode;
66
import org.hankki.hankkiserver.common.exception.BadRequestException;
7+
import org.hankki.hankkiserver.common.exception.ConflictException;
78
import org.hankki.hankkiserver.common.exception.NotFoundException;
89
import org.hankki.hankkiserver.common.exception.UnauthorizedException;
910

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

36+
@ExceptionHandler(ConflictException.class)
37+
public HankkiResponse<Void> handleConflictException(ConflictException e) {
38+
log.error("handleConflictException() in GlobalExceptionHandler throw ConflictException : {}", e.getMessage());
39+
return HankkiResponse.fail(e.getErrorCode());
40+
}
41+
3542
@ExceptionHandler(Exception.class)
3643
public HankkiResponse<Void> handleException(Exception e) {
3744
log.error("handleException() in GlobalExceptionHandler throw Exception : {}", e.getMessage());

src/main/java/org/hankki/hankkiserver/api/auth/service/AuthService.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ public class AuthService {
3636
private String adminKey;
3737

3838
private final UserFinder userFinder;
39-
private final UserSaver userSaver;
39+
private final UserUpdater userUpdater;
4040
private final UserInfoFinder userInfoFinder;
41-
private final UserInfoSaver userInfoSaver;
41+
private final UserInfoUpdater userInfoUpdater;
4242
private final UserInfoDeleter userInfoDeleter;
4343
private final JwtProvider jwtProvider;
4444
private final JwtValidator jwtValidator;
@@ -131,9 +131,9 @@ private String getRefreshToken(final Long userId) {
131131
}
132132

133133
private void saveUserAndUserInfo(final User user) {
134-
userSaver.saveUser(user);
134+
userUpdater.saveUser(user);
135135
UserInfo userInfo = UserInfo.createMemberInfo(user, null);
136-
userInfoSaver.saveUserInfo(userInfo);
136+
userInfoUpdater.saveUserInfo(userInfo);
137137
}
138138

139139
private void validateRefreshToken(final String refreshToken, final Long userId) {

src/main/java/org/hankki/hankkiserver/api/auth/service/UserFinder.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,15 @@ public class UserFinder {
1919

2020
private final UserRepository userRepository;
2121

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

27+
public User getUserReference(final Long id) {
28+
return userRepository.getReferenceById(id);
29+
}
30+
2731
public boolean isRegisteredUser(final Platform platform, final SocialInfoDto socialInfo) {
2832
return userRepository.existsByPlatformAndSerialIdAndMemberStatus(
2933
platform,

src/main/java/org/hankki/hankkiserver/api/auth/service/UserInfoDeleter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public class UserInfoDeleter {
1010

1111
private final UserInfoRepository userInfoRepository;
1212

13-
public void softDelete(final long userId) {
13+
public void softDelete(final Long userId) {
1414
userInfoRepository.softDeleteByUserId(userId);
1515
}
1616
}

src/main/java/org/hankki/hankkiserver/api/auth/service/UserInfoFinder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class UserInfoFinder {
1313

1414
private final UserInfoRepository userInfoRepository;
1515

16-
public UserInfo getUserInfo(final long userId) {
16+
public UserInfo getUserInfo(final Long userId) {
1717
return userInfoRepository.findByUserId(userId)
1818
.orElseThrow(() -> new NotFoundException(UserErrorCode.USER_INFO_NOT_FOUND));
1919
}

src/main/java/org/hankki/hankkiserver/api/auth/service/UserInfoSaver.java renamed to src/main/java/org/hankki/hankkiserver/api/auth/service/UserInfoUpdater.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
@Component
99
@RequiredArgsConstructor
10-
public class UserInfoSaver {
10+
public class UserInfoUpdater {
1111

1212
private final UserInfoRepository userInfoRepository;
1313

src/main/java/org/hankki/hankkiserver/api/auth/service/UserSaver.java renamed to src/main/java/org/hankki/hankkiserver/api/auth/service/UserUpdater.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
@Component
99
@RequiredArgsConstructor
10-
public class UserSaver {
10+
public class UserUpdater {
1111

1212
private final UserRepository userRepository;
1313

src/main/java/org/hankki/hankkiserver/api/store/controller/StoreController.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,27 @@
33
import lombok.RequiredArgsConstructor;
44
import org.hankki.hankkiserver.api.dto.HankkiResponse;
55
import org.hankki.hankkiserver.api.store.service.StoreQueryService;
6-
import org.hankki.hankkiserver.api.store.service.response.CategoriesResponse;
7-
import org.hankki.hankkiserver.api.store.service.response.PriceCategoriesResponse;
8-
import org.hankki.hankkiserver.api.store.service.response.SortOptionsResponse;
9-
import org.hankki.hankkiserver.api.store.service.response.StoreThumbnailResponse;
6+
import org.hankki.hankkiserver.api.store.service.response.*;
107
import org.hankki.hankkiserver.common.code.CommonSuccessCode;
118
import org.springframework.web.bind.annotation.GetMapping;
129
import org.springframework.web.bind.annotation.PathVariable;
10+
import org.hankki.hankkiserver.auth.UserId;
11+
import org.hankki.hankkiserver.api.store.service.HeartCommandService;
12+
import org.hankki.hankkiserver.api.store.service.command.StoreDeleteCommand;
13+
import org.hankki.hankkiserver.api.store.service.command.StorePostCommand;
14+
import org.springframework.web.bind.annotation.PostMapping;
1315
import org.springframework.web.bind.annotation.RequestMapping;
1416
import org.springframework.web.bind.annotation.RestController;
17+
import org.springframework.web.bind.annotation.*;
18+
1519

1620
@RestController
1721
@RequiredArgsConstructor
1822
@RequestMapping("/api/v1")
1923
public class StoreController {
2024

2125
private final StoreQueryService storeQueryService;
26+
private final HeartCommandService heartCommandService;
2227

2328
@GetMapping("/stores/{id}/thumbnail")
2429
public HankkiResponse<StoreThumbnailResponse> getStoreThumbnail(@PathVariable Long id) {
@@ -39,4 +44,14 @@ public HankkiResponse<SortOptionsResponse> getSortOptions() {
3944
public HankkiResponse<PriceCategoriesResponse> getPrices() {
4045
return HankkiResponse.success(CommonSuccessCode.OK, storeQueryService.getPriceCategories());
4146
}
47+
48+
@PostMapping("/stores/{id}/hearts")
49+
public HankkiResponse<HeartCreateResponse> createHeartStore(@UserId final Long userId, @PathVariable final Long id) {
50+
return HankkiResponse.success(CommonSuccessCode.CREATED, heartCommandService.createHeart(StorePostCommand.of(userId, id)));
51+
}
52+
53+
@DeleteMapping("/stores/{id}/hearts")
54+
public HankkiResponse<HeartDeleteResponse> deleteHeartStore(@UserId final Long userId, @PathVariable final Long id) {
55+
return HankkiResponse.success(CommonSuccessCode.OK, heartCommandService.deleteHeart(StoreDeleteCommand.of(userId, id)));
56+
}
4257
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package org.hankki.hankkiserver.api.store.service;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import org.hankki.hankkiserver.api.auth.service.UserFinder;
5+
import org.hankki.hankkiserver.api.store.service.command.StoreDeleteCommand;
6+
import org.hankki.hankkiserver.api.store.service.command.StorePostCommand;
7+
import org.hankki.hankkiserver.api.store.service.response.HeartCreateResponse;
8+
import org.hankki.hankkiserver.api.store.service.response.HeartDeleteResponse;
9+
import org.hankki.hankkiserver.common.code.HeartErrorCode;
10+
import org.hankki.hankkiserver.common.exception.ConflictException;
11+
import org.hankki.hankkiserver.domain.heart.model.Heart;
12+
import org.hankki.hankkiserver.domain.store.model.Store;
13+
import org.hankki.hankkiserver.domain.user.model.User;
14+
import org.springframework.stereotype.Service;
15+
import org.springframework.transaction.annotation.Transactional;
16+
17+
@Service
18+
@RequiredArgsConstructor
19+
@Transactional
20+
public class HeartCommandService {
21+
22+
private final HeartUpdater heartUpdater;
23+
private final HeartFinder heartFinder;
24+
private final HeartDeleter heartDeleter;
25+
private final UserFinder userFinder;
26+
private final StoreFinder storeFinder;
27+
28+
public HeartCreateResponse createHeart(final StorePostCommand storePostCommand) {
29+
User user = userFinder.getUserReference(storePostCommand.userId());
30+
Store store = storeFinder.getStoreReference(storePostCommand.storeId());
31+
validateStoreHeartCreation(user, store);
32+
saveStoreHeart(user, store);
33+
increaseStoreHeartCount(store);
34+
return HeartCreateResponse.of(store.getId(), true);
35+
}
36+
37+
public HeartDeleteResponse deleteHeart(final StoreDeleteCommand storeDeleteCommand) {
38+
User user = userFinder.getUserReference(storeDeleteCommand.userId());
39+
Store store = storeFinder.getStoreReference(storeDeleteCommand.storeId());
40+
validateStoreHeartRemoval(user, store);
41+
heartDeleter.deleteHeart(user,store);
42+
decreaseStoreHeartCount(store);
43+
return HeartDeleteResponse.of(store.getId(), false);
44+
}
45+
46+
private void validateStoreHeartCreation(final User user, final Store store) {
47+
if(heartFinder.existsByUserAndStore(user, store)){
48+
throw new ConflictException(HeartErrorCode.ALREADY_EXISTED_HEART);
49+
}
50+
}
51+
52+
private void validateStoreHeartRemoval(final User user, final Store store) {
53+
if(!heartFinder.existsByUserAndStore(user, store)){
54+
throw new ConflictException(HeartErrorCode.ALREADY_NO_HEART);
55+
}
56+
}
57+
58+
private void saveStoreHeart(final User user, final Store store) {
59+
heartUpdater.saveHeart(Heart.createHeart(user, store));
60+
}
61+
62+
private void increaseStoreHeartCount(final Store store) {
63+
store.increaseHeartCount();
64+
}
65+
66+
private void decreaseStoreHeartCount(final Store store) {
67+
store.decreaseHeartCount();
68+
}
69+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.hankki.hankkiserver.api.store.service;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import org.hankki.hankkiserver.domain.heart.repository.HeartRepository;
5+
import org.hankki.hankkiserver.domain.store.model.Store;
6+
import org.hankki.hankkiserver.domain.user.model.User;
7+
import org.springframework.stereotype.Component;
8+
9+
@Component
10+
@RequiredArgsConstructor
11+
public class HeartDeleter {
12+
13+
private final HeartRepository heartRepository;
14+
15+
public void deleteHeart(final User user, final Store store) {
16+
heartRepository.deleteByUserAndStore(user, store);
17+
}
18+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package org.hankki.hankkiserver.api.store.service;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import org.hankki.hankkiserver.domain.heart.model.Heart;
5+
import org.hankki.hankkiserver.domain.heart.repository.HeartRepository;
6+
import org.hankki.hankkiserver.domain.store.model.Store;
7+
import org.hankki.hankkiserver.domain.user.model.User;
8+
import org.springframework.stereotype.Component;
9+
10+
import java.util.Optional;
11+
12+
@Component
13+
@RequiredArgsConstructor
14+
public class HeartFinder {
15+
16+
private final HeartRepository heartRepository;
17+
18+
public boolean existsByUserAndStore(final User user, final Store store) {
19+
return heartRepository.existsByUserAndStore(user, store);
20+
}
21+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.hankki.hankkiserver.api.store.service;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import org.hankki.hankkiserver.domain.heart.model.Heart;
5+
import org.hankki.hankkiserver.domain.heart.repository.HeartRepository;
6+
import org.springframework.stereotype.Component;
7+
8+
@Component
9+
@RequiredArgsConstructor
10+
public class HeartUpdater {
11+
12+
private final HeartRepository heartRepository;
13+
14+
public void saveHeart(final Heart heart) {
15+
heartRepository.save(heart);
16+
}
17+
}

src/main/java/org/hankki/hankkiserver/api/store/service/StoreFinder.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,8 @@ protected Store findByIdWhereDeletedIsFalse(Long id) {
1717
return storeRepository.findByIdAndIsDeletedIsFalse(id)
1818
.orElseThrow(() -> new NotFoundException(StoreErrorCode.STORE_NOT_FOUND));
1919
}
20+
21+
public Store getStoreReference(final Long id) {
22+
return storeRepository.getReferenceById(id);
23+
}
2024
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.hankki.hankkiserver.api.store.service.command;
2+
3+
public record StoreDeleteCommand(
4+
Long userId,
5+
Long storeId
6+
) {
7+
public static StoreDeleteCommand of(final Long userId, final Long storeId) {
8+
return new StoreDeleteCommand(userId, storeId);
9+
}
10+
}
11+
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.hankki.hankkiserver.api.store.service.command;
2+
3+
public record StorePostCommand(
4+
Long userId,
5+
Long storeId
6+
) {
7+
public static StorePostCommand of(final Long userId, final Long storeId) {
8+
return new StorePostCommand(userId, storeId);
9+
}
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.hankki.hankkiserver.api.store.service.response;
2+
3+
public record HeartCreateResponse(
4+
Long storeId,
5+
boolean isHearted
6+
) {
7+
public static HeartCreateResponse of(final Long storeId, final boolean isHearted) {
8+
return new HeartCreateResponse(storeId, isHearted);
9+
}
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.hankki.hankkiserver.api.store.service.response;
2+
3+
public record HeartDeleteResponse(
4+
Long storeId,
5+
boolean isHearted
6+
) {
7+
public static HeartDeleteResponse of(final Long storeId, final boolean isHearted) {
8+
return new HeartDeleteResponse(storeId, isHearted);
9+
}
10+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.hankki.hankkiserver.common.code;
2+
3+
import lombok.Getter;
4+
import lombok.RequiredArgsConstructor;
5+
import org.springframework.http.HttpStatus;
6+
7+
@Getter
8+
@RequiredArgsConstructor
9+
public enum HeartErrorCode implements ErrorCode {
10+
11+
ALREADY_EXISTED_HEART(HttpStatus.CONFLICT, "이미 좋아요 한 가게입니다."),
12+
ALREADY_NO_HEART(HttpStatus.CONFLICT, "이미 좋아요를 취소한 가게입니다.");
13+
14+
private final HttpStatus httpStatus;
15+
private final String message;
16+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package org.hankki.hankkiserver.common.exception;
2+
3+
import lombok.Getter;
4+
import org.hankki.hankkiserver.common.code.ErrorCode;
5+
6+
@Getter
7+
public class ConflictException extends RuntimeException {
8+
private final ErrorCode errorCode;
9+
10+
public ConflictException(ErrorCode errorCode) {
11+
super(errorCode.getMessage());
12+
this.errorCode = errorCode;
13+
}
14+
}

src/main/java/org/hankki/hankkiserver/domain/favoritestore/model/FavoriteStore.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ public class FavoriteStore {
1919
@Column(name = "favorite_store_id")
2020
private Long id;
2121

22-
@ManyToOne
22+
@ManyToOne(fetch = FetchType.LAZY)
2323
@JoinColumn(name = "store_id")
2424
private Store store;
2525

26-
@ManyToOne
26+
@ManyToOne(fetch = FetchType.LAZY)
2727
@JoinColumn(name = "favorite_id")
2828
private Favorite favorite;
2929

0 commit comments

Comments
 (0)