Skip to content

Commit 20e91cf

Browse files
authored
Merge pull request #80 from Lets-Gu/sql
refactor : 코드 품질 개선 및 버그 수정
2 parents 1083934 + ec2a7df commit 20e91cf

10 files changed

Lines changed: 85 additions & 86 deletions

File tree

src/main/java/avengers/lion/item/service/ItemService.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,13 @@ public void exchangeItem (Long memberId, Long itemId, ExchangeItemRequest exchan
4646
.orElseThrow(()-> new BusinessException(ExceptionType.ITEM_NOT_FOUND));
4747
Member member = memberRepository.findById(memberId)
4848
.orElseThrow(()-> new BusinessException(ExceptionType.MEMBER_NOT_FOUND));
49+
// 총 비용 = 아이템 가격 * 개수
4950
int price = Math.multiplyExact(item.getPrice(), count);
50-
// 구매하고자 하는 수량보다 재고가 적다 -> 에러 발생
51+
// 구매하고자 하는 수량보다 재고가 적을 때
5152
if(item.getStockCount()<count){
5253
throw new BusinessException(ExceptionType.STOCK_NOT_AVAILABLE);
5354
}
54-
member.buyItemByPoint((long) price);
55+
5556
walletService.addPointTransaction(price, member, item.getItemCategory());
5657
item.buyItem(count);
5758

src/main/java/avengers/lion/member/controller/MemberController.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ public class MemberController implements MemberApi {
2626
public ResponseEntity<ResponseBody<MyProfileResponse>> getMyProfile(@AuthenticationPrincipal Long memberId) {
2727
return ResponseEntity.ok(ResponseUtil.createSuccessResponse(memberService.getMyProfile(memberId)));
2828
}
29+
30+
/*
31+
마이페이지 프로필 업데이트
32+
*/
2933
@PutMapping("/my-profile")
3034
@PreAuthorize("hasAuthority('ROLE_USER')")
3135
public ResponseEntity<ResponseBody<Void>> updateMyProfile(@AuthenticationPrincipal Long memberId, @RequestBody UpdateNicknameRequest updateNicknameRequest) {

src/main/java/avengers/lion/member/repository/MemberRepository.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
@Repository
1010
public interface MemberRepository extends JpaRepository<Member, Long> {
1111
Optional<Member> findByEmail(String email);
12-
boolean existsByEmail(String email);
1312

1413
boolean existsByNicknameAndIdNot(String nickname, Long id);
1514
}

src/main/java/avengers/lion/member/service/MemberService.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,14 @@ public Long getMyPoint(Long memberId){
3434
return member.getPoint();
3535
}
3636

37+
38+
/*
39+
프로필 업데이트
40+
*/
3741
@Transactional
3842
public void updateNickname(Long memberId, UpdateNicknameRequest request){
3943
String nickname = request.nickname();
44+
// 닉네임이 이미 존재하는지 조회
4045
if(memberRepository.existsByNicknameAndIdNot(nickname, memberId))
4146
throw new BusinessException(ExceptionType.NICKNAME_ALREADY_EXISTS);
4247
Member member = memberRepository.findById(memberId)

src/main/java/avengers/lion/mission/controller/VerificationController.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ public ResponseEntity<ResponseBody<VerifyImageResponse>> startVerification(
6262
@PathVariable Long missionId,
6363
@Valid @RequestBody VerifyImageRequest request,
6464
@AuthenticationPrincipal Long memberId) {
65-
6665
VerifyImageResponse response = missionVerifyService.startVerification(missionId, request, memberId);
6766
return ResponseEntity.ok(ResponseUtil.createSuccessResponse(response));
6867
}

src/main/java/avengers/lion/review/repository/ReviewQueryRepositoryImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public List<Review> findAllReviewByMissionId(Long missionId, Long cursorId, int
2929
BooleanBuilder where = new BooleanBuilder()
3030
.and(r.completedMission.mission.id.eq(missionId));
3131
if (cursorId != null) {
32-
where.and(sortType.equals(SortType.ASC) ? r.id.lt(cursorId) : r.id.gt(cursorId));
32+
where.and(sortType.equals(SortType.ASC) ? r.id.gt(cursorId) : r.id.lt(cursorId));
3333
}
3434
OrderSpecifier<?> orderSpecifier = sortType.equals(SortType.ASC) ? r.id.asc() : r.id.desc();
3535

src/main/java/avengers/lion/review/service/ReviewService.java

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -103,24 +103,6 @@ public PageResult<WrittenReviewResponse> getWrittenPage(Long memberId,
103103
return new PageResult<>(data, hasNext, null, nextId);
104104
}
105105

106-
/** 작성한 리뷰 상세조회 (ASC/DESC) */
107-
public PageResult<WrittenReviewResponse> getWrittenDetailPage(Long memberId,
108-
Long cursorId,
109-
int limit,
110-
SortType sort) {
111-
112-
List<Review> rows = reviewRepository.findWrittenPage(memberId, cursorId, limit+1, sort);
113-
114-
boolean hasNext = rows.size() > limit;
115-
if (hasNext) rows = rows.subList(0, limit);
116-
117-
List<WrittenReviewResponse> data = WrittenReviewResponse.of(rows);
118-
Long nextId = null;
119-
if (!rows.isEmpty()) { Review last = rows.getLast(); nextId = last.getId(); }
120-
121-
return new PageResult<>(data, hasNext, null, nextId);
122-
}
123-
124106
/** 작성 가능한 리뷰 작성 */
125107
@Transactional
126108
public void writeUnWrittenReview(Long memberId, WriteReviewRequest req) {
@@ -139,9 +121,10 @@ public void writeUnWrittenReview(Long memberId, WriteReviewRequest req) {
139121
.member(cm.getMember())
140122
.completedMission(cm)
141123
.build();
142-
member.addPointByReview();
143124
walletService.addPointTransactionForReview(member);
144125
reviewRepository.save(review);
145126
cm.updateReviewStatus(ReviewStatus.ACTIVE);
146127
}
128+
129+
147130
}

src/main/java/avengers/lion/wallet/controller/WalletController.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ public class WalletController implements WalletApi {
2222

2323
private final WalletService walletService;
2424

25-
2625
/*
2726
내 포인트 조회
2827
*/
@@ -32,6 +31,9 @@ public ResponseEntity<ResponseBody<MyPointResponse>> getMyPoint(@AuthenticationP
3231
return ResponseEntity.ok(ResponseUtil.createSuccessResponse(walletService.getMyPoint(memberId)));
3332
}
3433

34+
/*
35+
내 지갑 조회
36+
*/
3537
@GetMapping("/my-wallet")
3638
@PreAuthorize("hasAuthority('ROLE_USER')")
3739
public ResponseEntity<ResponseBody<MyWalletResponse>> getMyWallet(@AuthenticationPrincipal Long memberId){

src/main/java/avengers/lion/wallet/service/WalletService.java

Lines changed: 24 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@
1818
import org.springframework.transaction.annotation.Transactional;
1919

2020
import java.util.List;
21+
import java.util.function.Function;
2122

2223

2324
@Service
2425
@RequiredArgsConstructor
2526
public class WalletService {
2627

28+
private static final int REVIEW_REWARD_POINTS = 100;
29+
2730
private final PointTransactionRepository pointTransactionRepository;
2831
private final OrderService orderService;
2932
private final MemberService memberService;
@@ -42,56 +45,38 @@ public MyWalletResponse getMyWallet(Long memberId){
4245
Long consumedItemCount = orderService.myConsumedItemCount(memberId);
4346
List<GiftCardResponse> giftCards = getMyGiftCards(memberId);
4447
List<ParentItemResponse> partnerCards = getMyPartnerCards(memberId);
45-
List<ConsumedItemResponse> usedItems = getMyUsedItems(memberId);
48+
List<ConsumedItemResponse> usedItems = getMyConsumedItems(memberId);
4649
return new MyWalletResponse(giftCardCount, partnerCardCount, consumedItemCount, giftCards, partnerCards, usedItems);
4750
}
4851

49-
/*
50-
내 상품권 조회
51-
*/
5252
public List<GiftCardResponse> getMyGiftCards(Long memberId){
5353
List<Orders> orders = orderService.getGiftCards(memberId);
54-
return orders.stream()
55-
.flatMap(order -> order.getOrderItems().stream())
56-
.map(GiftCardResponse::of)
57-
.toList();
54+
return convertOrdersToResponse(orders, GiftCardResponse::of);
5855

5956
}
60-
/*
61-
내 제휴상품 조회
62-
*/
57+
6358
public List<ParentItemResponse> getMyPartnerCards(Long memberId){
6459
List<Orders> orders = orderService.getPartnerCards(memberId);
65-
return orders.stream()
66-
.flatMap(order -> order.getOrderItems().stream())
67-
.map(ParentItemResponse::of)
68-
.toList();
60+
return convertOrdersToResponse(orders, ParentItemResponse::of);
6961
}
70-
/*
71-
사용한 상품 조회
72-
*/
73-
public List<ConsumedItemResponse> getMyUsedItems(Long memberId){
62+
63+
public List<ConsumedItemResponse> getMyConsumedItems(Long memberId){
7464
List<Orders> orders = orderService.getConsumedItem(memberId);
75-
return orders.stream()
76-
.flatMap(order -> order.getOrderItems().stream())
77-
.map(ConsumedItemResponse::of)
78-
.toList();
65+
return convertOrdersToResponse(orders, ConsumedItemResponse::of);
7966
}
8067

81-
/*
82-
리워드 내역 조회
83-
*/
68+
8469
public List<RewardHistoryResponse> getRewardHistory(Long memberId){
8570
List<PointTransaction> pointTransactions = pointTransactionRepository.findAllByMemberId(memberId);
8671
return pointTransactions.stream()
8772
.map(RewardHistoryResponse::of)
8873
.toList();
8974
}
9075

91-
/*
92-
리워드 거래 내역 추가 (아이템 구매용)
93-
*/
76+
77+
@Transactional
9478
public void addPointTransaction(long price, Member member, ItemCategory itemCategory){
79+
member.buyItemByPoint(price);
9580
if(price<=0)
9681
throw new BusinessException(ExceptionType.PRICE_IS_POSITIVE);
9782

@@ -110,19 +95,18 @@ public void addPointTransaction(long price, Member member, ItemCategory itemCate
11095
/*
11196
포인트 적립 거래 내역 추가 (리뷰 작성용)
11297
*/
98+
@Transactional
11399
public void addPointTransactionForReview(Member member) {
100+
member.addPointByReview();
114101
PointTransaction pointTransaction = PointTransaction.builder()
115-
.changeAmount(100) // 리뷰 적립 포인트
102+
.changeAmount(REVIEW_REWARD_POINTS)
116103
.balanceAfter(Math.toIntExact(member.getPoint()))
117104
.pointType(PointType.REVIEW)
118105
.member(member)
119106
.build();
120107
pointTransactionRepository.save(pointTransaction);
121108
}
122109

123-
/*
124-
상품 사용 기능
125-
*/
126110
@Transactional
127111
public void useItem(Long memberId, Long orderItemId){
128112
OrderItem orderItem = orderService.getOrderItem(memberId, orderItemId);
@@ -131,4 +115,11 @@ public void useItem(Long memberId, Long orderItemId){
131115
}
132116
orderItem.useItem();
133117
}
118+
119+
private <T> List<T> convertOrdersToResponse(List<Orders> orders, Function<OrderItem, T> mapper){
120+
return orders.stream()
121+
.flatMap(order -> order.getOrderItems().stream())
122+
.map(mapper)
123+
.toList();
124+
}
134125
}

src/main/java/avengers/lion/weather/service/WeatherService.java

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -45,70 +45,85 @@ public class WeatherService {
4545

4646
private static final ZoneId KST = ZoneId.of("Asia/Seoul");
4747
private static final DateTimeFormatter HOUR_FMT = DateTimeFormatter.ofPattern("a h시", Locale.KOREAN);
48+
private static final String CACHE_KEY_PREFIX = "weather:gumi:basic:";
49+
private static final String API_EXCLUDE_PARAMS = "minutely,alerts";
50+
private static final int SKIP_CURRENT_HOUR = 1;
51+
private static final int HOURLY_FORECAST_COUNT = 6;
52+
private static final int CACHE_BUFFER_SECONDS = 5;
4853

54+
/*
55+
현재 시간을 기반으로 Redis 키 생성
56+
*/
4957
private String cacheKey() {
5058
ZonedDateTime hour = ZonedDateTime.now(KST).truncatedTo(ChronoUnit.HOURS);
51-
return "weather:gumi:basic:" +hour.toEpochSecond();
59+
return CACHE_KEY_PREFIX + hour.toEpochSecond();
5260
}
61+
5362
private Duration ttlUntilNextHour() {
5463
ZonedDateTime now = ZonedDateTime.now(KST);
55-
return Duration.between(now, now.truncatedTo(ChronoUnit.HOURS).plusHours(1)).plusSeconds(5);
64+
return Duration.between(now, now.truncatedTo(ChronoUnit.HOURS).plusHours(1)).plusSeconds(CACHE_BUFFER_SECONDS);
5665
}
5766

5867
public WeatherBasic oneCall() {
5968
String key = cacheKey();
69+
// 캐시된 날씨 데이터가 있으면 바로 가져오기
6070
Object hit = redis.opsForValue().get(key);
61-
if(hit instanceof WeatherBasic cached) {
71+
if (hit instanceof WeatherBasic cached) {
6272
return cached;
6373
}
64-
OpenWeatherDto dto = requestOpenWeather();
74+
// OpenWeatherApi를 호출하여 날씨 데이터 가져오기
75+
OpenWeatherDto dto = requestOpenWeather();
6576
OpenWeatherDto.Current current = dto.current();
6677
List<OpenWeatherDto.Hourly> hourly = dto.hourly();
6778
List<OpenWeatherDto.Daily> daily = dto.daily();
68-
6979
// 현재
70-
WeatherBasic.Now now = new WeatherBasic.Now(
71-
current.temp(),
72-
current.weather().getFirst().icon(),
73-
daily.getFirst().temp().max(),
74-
daily.getFirst().temp().min());
75-
80+
WeatherBasic.Now now = convertToCurrentWeather(current, daily);
7681
// 6시간
77-
List<WeatherBasic.Hour> hourList = hourly.stream()
78-
.skip(1)
79-
.limit(6)
80-
.map(hour -> {
81-
ZonedDateTime kstTime = Instant.ofEpochSecond(hour.dt())
82-
.atZone(ZoneId.of("Asia/Seoul"));
83-
84-
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("a h시", Locale.KOREAN);
85-
String formattedTime = kstTime.format(formatter);
86-
return new WeatherBasic.Hour(formattedTime, hour.temp(), hour.weather().getFirst().icon());
87-
})
88-
.toList();
89-
90-
// 오늘 제외
82+
List<WeatherBasic.Hour> hourList = convertToHourWeather(hourly);
9183
WeatherBasic view = new WeatherBasic(now, hourList);
9284
redis.opsForValue().set(key, view, ttlUntilNextHour());
9385
return view;
9486
}
87+
9588
public OpenWeatherDto requestOpenWeather() {
9689
URI uri = UriComponentsBuilder.fromUriString(baseUrl + "/data/3.0/onecall")
9790
.queryParam("lat", lat)
9891
.queryParam("lon", lon)
9992
.queryParam("appid", apiKey)
10093
.queryParam("lang", lang)
10194
.queryParam("units", units)
102-
.queryParam("exclude", "minutely,alerts")
95+
.queryParam("exclude", API_EXCLUDE_PARAMS)
10396
.build()
10497
.toUri();
10598
log.info("onecall uri = {}", uri);
106-
try{
99+
try {
107100
return rest.getForObject(uri, OpenWeatherDto.class);
108-
} catch(HttpClientErrorException | HttpServerErrorException | UnknownContentTypeException | ResourceAccessException e) {
101+
} catch (HttpClientErrorException | HttpServerErrorException | UnknownContentTypeException |
102+
ResourceAccessException e) {
109103
log.error("OpenWeather API 호출 실패: {}", e.getMessage());
110104
throw new BusinessException(ExceptionType.WEATHER_API_ERROR);
111105
}
106+
}
107+
public WeatherBasic.Now convertToCurrentWeather(OpenWeatherDto.Current current, List<OpenWeatherDto.Daily> daily) {
108+
return new WeatherBasic.Now(
109+
current.temp(),
110+
current.weather().getFirst().icon(),
111+
daily.getFirst().temp().max(),
112+
daily.getFirst().temp().min());
113+
}
114+
115+
public List<WeatherBasic.Hour> convertToHourWeather(List<OpenWeatherDto.Hourly> hourly){
116+
return hourly.stream()
117+
.skip(SKIP_CURRENT_HOUR)
118+
.limit(HOURLY_FORECAST_COUNT)
119+
.map(hour -> {
120+
ZonedDateTime kstTime = Instant.ofEpochSecond(hour.dt())
121+
.atZone(ZoneId.of("Asia/Seoul"));
112122

123+
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("a h시", Locale.KOREAN);
124+
String formattedTime = kstTime.format(formatter);
125+
return new WeatherBasic.Hour(formattedTime, hour.temp(), hour.weather().getFirst().icon());
126+
})
127+
.toList();
113128
}
114129
}

0 commit comments

Comments
 (0)