feat(backend): #359 1단계 — google_place_id 중복 조회 API
- GET /api/admin/restaurants/duplicates/place-id (어드민 전용) - 그룹별 식당 목록 + video/review/memo 카운트 동봉 - Mapper: findDuplicatePlaceIdRows + Service 그룹핑 - 정리/병합 + UNIQUE 제약은 데이터 위험 분리 위해 후속 PR로 Refs: #359 (조회 단계 완료) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -62,6 +62,15 @@ public class AdminRestaurantController {
|
||||
return Map.of("success", true, "id", id);
|
||||
}
|
||||
|
||||
// #359 1단계 — google_place_id 중복 조회 (정리/UNIQUE는 후속).
|
||||
@GetMapping("/duplicates/place-id")
|
||||
public Map<String, Object> duplicatePlaceIds() {
|
||||
var admin = AuthUtil.requireAdmin();
|
||||
var groups = restaurantService.findDuplicatePlaceIdGroups();
|
||||
log.info("[ADMIN] {} duplicate place_id groups: {}", admin.getSubject(), groups.size());
|
||||
return Map.of("groups", groups, "group_count", groups.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* 어드민용 hidden 토글.
|
||||
*/
|
||||
|
||||
@@ -39,6 +39,9 @@ public interface RestaurantMapper {
|
||||
|
||||
int countUnevaluatedLinks();
|
||||
|
||||
// #359 1단계 — google_place_id 중복 조회
|
||||
List<Map<String, Object>> findDuplicatePlaceIdRows();
|
||||
|
||||
Restaurant findById(@Param("id") String id);
|
||||
|
||||
List<Map<String, Object>> findVideoLinks(@Param("restaurantId") String restaurantId,
|
||||
|
||||
@@ -111,6 +111,23 @@ public class RestaurantService {
|
||||
return mapper.countUnevaluatedLinks();
|
||||
}
|
||||
|
||||
// #359 1단계 — google_place_id 중복 그룹 (참조 카운트 동봉)
|
||||
public List<Map<String, Object>> findDuplicatePlaceIdGroups() {
|
||||
var rows = mapper.findDuplicatePlaceIdRows().stream()
|
||||
.map(JsonUtil::lowerKeys)
|
||||
.toList();
|
||||
Map<String, List<Map<String, Object>>> grouped = new LinkedHashMap<>();
|
||||
for (var r : rows) {
|
||||
String key = (String) r.get("google_place_id");
|
||||
grouped.computeIfAbsent(key, k -> new ArrayList<>()).add(r);
|
||||
}
|
||||
List<Map<String, Object>> out = new ArrayList<>(grouped.size());
|
||||
for (var e : grouped.entrySet()) {
|
||||
out.add(Map.of("google_place_id", e.getKey(), "items", e.getValue()));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public void update(String id, Map<String, Object> fields) {
|
||||
mapper.updateFields(id, fields);
|
||||
}
|
||||
|
||||
@@ -353,4 +353,21 @@
|
||||
SELECT COUNT(*) FROM video_restaurants WHERE relevance_evaluated_at IS NULL
|
||||
</select>
|
||||
|
||||
<!-- #359 1단계 — google_place_id 중복 조회 (그룹 식당 + 참조 카운트) -->
|
||||
<select id="findDuplicatePlaceIdRows" resultType="map">
|
||||
SELECT r.id, r.google_place_id, r.name, r.address,
|
||||
TO_CHAR(r.created_at, 'YYYY-MM-DD"T"HH24:MI:SS') AS created_at,
|
||||
r.hidden,
|
||||
(SELECT COUNT(*) FROM video_restaurants vr WHERE vr.restaurant_id = r.id) AS video_count,
|
||||
(SELECT COUNT(*) FROM user_reviews rv WHERE rv.restaurant_id = r.id) AS review_count,
|
||||
(SELECT COUNT(*) FROM user_memos mm WHERE mm.restaurant_id = r.id) AS memo_count
|
||||
FROM restaurants r
|
||||
WHERE r.google_place_id IN (
|
||||
SELECT google_place_id FROM restaurants
|
||||
WHERE google_place_id IS NOT NULL
|
||||
GROUP BY google_place_id HAVING COUNT(*) > 1
|
||||
)
|
||||
ORDER BY r.google_place_id, r.created_at
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
Reference in New Issue
Block a user