Refector/130 email imgcnt#131
Conversation
complaint_petition 목록·단건 조회 엔드포인트를 추가해
There was a problem hiding this comment.
Code Review
This pull request introduces admin endpoints and services for reviewing complaint petitions, including listing with pagination/filtering and retrieving detailed petition information. It also enforces a requirement that issue pins must have at least one image when created or updated, adding corresponding validation and unit tests. Feedback highlights a potential ValidationError in ComplaintPetitionService if database fields are None, with a suggestion to use default empty strings. Additionally, suggestions were made to improve SQLAlchemy query construction in ComplaintPetitionRepo by chaining nested relationship options more cleanly and applying filters before limit/offset for better readability.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| if location_department is not None: | ||
| location_department_email = location_department.location_department_email | ||
| if location_department.department is not None: | ||
| department_name = location_department.department.department_name |
There was a problem hiding this comment.
데이터베이스에서 location_department_email 또는 department_name이 None일 경우, ComplaintPetitionReviewItem 및 ComplaintPetitionApplyResponse 스키마에서 해당 필드들을 필수 문자열(str)로 정의하고 있기 때문에 Pydantic 검증 에러(ValidationError)가 발생할 수 있습니다. 안전한 방어적 프로그래밍을 위해 or ""를 사용하여 None인 경우 빈 문자열로 대체하는 것을 권장합니다.
| if location_department is not None: | |
| location_department_email = location_department.location_department_email | |
| if location_department.department is not None: | |
| department_name = location_department.department.department_name | |
| if location_department is not None: | |
| location_department_email = location_department.location_department_email or "" | |
| if location_department.department is not None: | |
| department_name = location_department.department.department_name or "" |
| selectinload(ComplaintPetition.location_department).selectinload(LocationDepartment.department), | ||
| selectinload(ComplaintPetition.location_department).selectinload(LocationDepartment.location), |
There was a problem hiding this comment.
동일한 관계 경로(ComplaintPetition.location_department)에 대해 여러 번 selectinload를 호출하는 대신, options() 메서드를 사용하여 하위 관계들을 한 번에 체이닝하는 것이 더 깔끔하고 SQLAlchemy에서 권장하는 방식입니다.
selectinload(ComplaintPetition.location_department).options(
selectinload(LocationDepartment.department),
selectinload(LocationDepartment.location),
),| list_stmt = ( | ||
| select(ComplaintPetition) | ||
| .options(*self._review_load_options()) | ||
| .order_by(ComplaintPetition.created_at.desc()) | ||
| .limit(limit) | ||
| .offset(offset) | ||
| ) | ||
| if filters: | ||
| list_stmt = list_stmt.where(*filters) |
There was a problem hiding this comment.
SQLAlchemy에서 where 필터를 limit 및 offset 메서드 호출 이후에 적용하는 것은 문법적으로는 허용되지만, 실제 생성되는 SQL의 구조와 일치하지 않아 가독성을 떨어뜨리고 혼란을 줄 수 있습니다. 필터(where)를 먼저 적용한 후 정렬(order_by) 및 페이징(limit, offset)을 적용하는 것이 더 직관적이고 올바른 관례입니다.
list_stmt = select(ComplaintPetition).options(*self._review_load_options())
if filters:
list_stmt = list_stmt.where(*filters)
list_stmt = (
list_stmt.order_by(ComplaintPetition.created_at.desc())
.limit(limit)
.offset(offset)
)
🔗 Related Issue
✨ 작업 개요
주요 작업 내용
photos와pinImages가 최소 1장 이상 필요하도록 검증 추가PIN_IMAGE_400_4추가GET /complaint-admin/petitionsstatus,limit,offset지원GET /complaint-admin/petitions/{petition_id}created_at,updated_at,location_region,pin_title필드 추가tests/test_issue_pin_image_required.pytests/test_complaint_petition_review.py체크리스트
📷 이미지 첨부 (선택)
🧐 집중 리뷰 요청
status필터,limit/offset페이지네이션 동작 방식이 기대한 정책과 맞는지 확인 부탁드립니다.Test plan