Files
tasteby/docs/design/359a-duplicate-place-id-view
joungmin d73947444f 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>
2026-06-15 20:32:40 +09:00
..

설계서: google_place_id 중복 조회 API (#359 1단계)

상태: Approved 작성: [AI] Architect · 최종수정: 2026-06-15 추적성 — Redmine: #359 · 1단계(조회 전용, 위험 0). 2단계(자동 병합) / 3단계(UNIQUE)는 별도 PR. · 구현 파일: backend-java/src/main/resources/mybatis/mapper/RestaurantMapper.xml, backend-java/src/main/java/com/tasteby/mapper/RestaurantMapper.java, backend-java/src/main/java/com/tasteby/service/RestaurantService.java, backend-java/src/main/java/com/tasteby/controller/AdminRestaurantController.java · 테스트: 본 범위 밖 (수동 — admin token으로 호출).

1. 목적 (Why)

같은 google_place_id에 다중 식당이 매핑된 경우 운영자가 어떤 것을 유지/병합할지 결정 필요. 본 단계는 조회만 — 그룹과 후보 식당을 메타데이터(연결된 영상/리뷰/메모 수)와 함께 보여줘 의사결정 자료 제공.

2. 범위

  • 포함: GET /api/admin/restaurants/duplicates/place-id — 운영자만, 그룹별 식당 + 카운트 동봉.
  • 제외 (별도 PR): 병합/삭제, UNIQUE constraint.

3. 인수조건

  • requireAdmin 보호.
  • 응답 구조: [{ google_place_id, items: [{ id, name, address, created_at, video_count, review_count, memo_count, hidden }] }].
  • 그룹은 COUNT(*) > 1 만 반환.

4. SQL

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 reviews rv WHERE rv.restaurant_id = r.id) AS review_count,
       (SELECT COUNT(*) FROM 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

Service 계층에서 google_place_id로 그룹핑하여 응답 구조 변환.

5. 엣지케이스

  • 중복 0건 → 빈 배열.
  • 누군가가 google_place_id를 동시에 변경 중 → 다음 호출에서 반영 (캐시 X).