Files
tasteby/backend-java/src/main/java/com/tasteby/service/ReviewService.java

81 lines
3.1 KiB
Java

package com.tasteby.service;
import com.tasteby.domain.Restaurant;
import com.tasteby.domain.Review;
import com.tasteby.mapper.ReviewMapper;
import com.tasteby.util.IdGenerator;
import com.tasteby.util.JsonUtil;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate;
import java.util.*;
@Service
public class ReviewService {
private final ReviewMapper mapper;
public ReviewService(ReviewMapper mapper) {
this.mapper = mapper;
}
public List<Review> findByRestaurant(String restaurantId, int limit, int offset) {
return mapper.findByRestaurant(restaurantId, limit, offset);
}
public Map<String, Object> getAvgRating(String restaurantId) {
Map<String, Object> result = mapper.getAvgRating(restaurantId);
return result != null ? JsonUtil.lowerKeys(result) : Map.of("avg_rating", 0.0, "review_count", 0);
}
@Transactional
public Review create(String userId, String restaurantId, double rating, String reviewText, LocalDate visitedAt) {
String id = IdGenerator.newId();
String visitedStr = visitedAt != null ? visitedAt.toString() : null;
mapper.insertReview(id, userId, restaurantId, rating, reviewText, visitedStr);
return mapper.findById(id);
}
@Transactional // #334 — 단일 SQL이지만 어노테이션 일관성
public boolean update(String reviewId, String userId, Double rating, String reviewText, LocalDate visitedAt) {
String visitedStr = visitedAt != null ? visitedAt.toString() : null;
return mapper.updateReview(reviewId, userId, rating, reviewText, visitedStr) > 0;
}
@Transactional // #334 — 단일 SQL이지만 어노테이션 일관성
public boolean delete(String reviewId, String userId) {
return mapper.deleteReview(reviewId, userId) > 0;
}
public List<Review> findByUser(String userId, int limit, int offset) {
return mapper.findByUser(userId, limit, offset);
}
public boolean isFavorited(String userId, String restaurantId) {
return mapper.countFavorite(userId, restaurantId) > 0;
}
@Transactional
public boolean toggleFavorite(String userId, String restaurantId) {
String existingId = mapper.findFavoriteId(userId, restaurantId);
if (existingId != null) {
mapper.deleteFavorite(userId, restaurantId);
return false;
}
// #294 — 동시성 가드: 동시 INSERT 시 UNIQUE 충돌 → 한 쪽 500.
// INSERT 시도 후 DuplicateKeyException은 "이미 추가됨"으로 간주 (토글 의도는 ON).
try {
mapper.insertFavorite(IdGenerator.newId(), userId, restaurantId);
} catch (DuplicateKeyException ignored) {
// 다른 트랜잭션이 먼저 INSERT 함 — 결과는 어쨌든 즐겨찾기 ON.
}
return true;
}
public List<Restaurant> getUserFavorites(String userId) {
return mapper.getUserFavorites(userId);
}
}