[FEAT] 연애관 채팅 프로필 공개 요청 구현#71
Conversation
[Feature] 사용자 사진 관리 기능 구현 및 GCP 배포 전환
[Fix] 배포 시 이미지 환경변수 전달 방식 수정
이미지 계산 방식 개선
[Fix] 배포 단계에서 이미지 주소를 직접 생성
fix: 배포 시 compose 환경변수 로드 보강
fix: compose 실행 시 이미지 변수를 직접 전달
호감/메시지 응답 흐름 및 상대 프로필 과금 로직 개선
관리자 콘솔 및 운영 관리 기능 추가
관리자 회원 사진과 멤버십 표시 수정
운영 환경 SQL 로그 비활성화
수동 배포 실행 버튼 추가
채팅 서비스 구현 및 개선
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
🚧 Files skipped from review as they are similar to previous changes (3)
Walkthrough프로필 사진 요청 생성 전 상태 검사가 추가되었고, 알림 타입은 문자열 enum 저장으로 바뀌었습니다. 관리자 신고 처리와 사용자 정지 만료 시각 반영이 확장되었으며, 만남 인증은 시작·만료 시각과 위치 클러스터 기준으로 재구성되었습니다. Changes프로필 사진 요청 및 알림 타입 저장
관리자 신고 처리와 사용자 정지 상태
만남 매칭과 인증 흐름
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
manabom/src/main/java/mannabom_server/manabom/application/matching/service/PhotoRequestService.java (1)
73-92: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win중복 쿼리 및 도달 불가능한 분기 정리 필요
createPhotoRequest에서getPhotoRequestStatus(roomId, userId)를 호출하면 내부적으로findRoomById,validateLoveViewChatRoom,validateActiveMember,photoRequestRepository.findTopByHistoryIdOrderByIdDesc가 다시 실행됩니다. 이는 이미 73-77번 줄에서 수행한 조회/검증과 중복되어 트랜잭션당 불필요한 DB 조회가 두 배로 늘어납니다.또한
getPhotoRequestStatus의 로직(라인 54-68)을 따라가 보면,currentStatus == LoveViewPhotoStatus.READY가 되는 경우는latestOpt가 비어있거나 상태가REJECTED이면서 메시지 수가 10개 이상인 경우뿐입니다. 즉 READY 검증(79-82번 줄)을 통과한 이후에는 88-92번 줄의latestOpt.get().getStatus() != PhotoRequestStatus.REJECTED조건이 항상 거짓이 되어 해당 분기가 도달 불가능한 죽은 코드가 됩니다.
getPhotoRequestStatus가 사용한latestOpt를 반환하거나 재사용할 수 있도록 리팩터링하여 중복 조회를 없애고, 도달 불가능해진 88-92번 줄의 검증을 정리하는 것을 권장합니다.♻️ 리팩터링 방향 예시
- LoveViewPhotoStatus currentStatus = getPhotoRequestStatus(roomId, userId); - if (currentStatus != LoveViewPhotoStatus.READY) { - throw new IllegalStateException("아직 프로필 사진을 요청할 수 있는 상태가 아닙니다."); - } - - LoveViewRecommendHistory loveView = room.getLoveView(); - Long loveViewId = loveView.getId(); - - Optional<LoveViewPhotoRequest> latestOpt = photoRequestRepository.findTopByHistoryIdOrderByIdDesc(loveViewId); - if(latestOpt.isPresent()){ - if(latestOpt.get().getStatus()!=PhotoRequestStatus.REJECTED){ - throw new IllegalStateException("프로필 요청이 중복되었거나 이미 수락된 상태입니다."); - } - } + LoveViewRecommendHistory loveView = room.getLoveView(); + Long loveViewId = loveView.getId(); + Optional<LoveViewPhotoRequest> latestOpt = photoRequestRepository.findTopByHistoryIdOrderByIdDesc(loveViewId); + LoveViewPhotoStatus currentStatus = resolveStatus(room, userId, latestOpt); + if (currentStatus != LoveViewPhotoStatus.READY) { + throw new IllegalStateException("아직 프로필 사진을 요청할 수 있는 상태가 아닙니다."); + }(위와 같이 상태 판별 로직을
latestOpt재사용이 가능한 private 메서드로 추출하고,getPhotoRequestStatus와createPhotoRequest모두에서 활용하도록 리팩터링을 제안합니다.)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/application/matching/service/PhotoRequestService.java` around lines 73 - 92, `createPhotoRequest` has duplicated room/member/latest-request lookups because `getPhotoRequestStatus(roomId, userId)` re-runs the same queries already done earlier, and the later `latestOpt` status check is dead code once `READY` has been verified. Refactor `PhotoRequestService#createPhotoRequest` and `getPhotoRequestStatus` to share the same fetched `LoveViewPhotoRequest`/status data (for example by extracting a private helper that returns both status and latest request), remove the repeated repository call, and delete the unreachable `latestOpt.get().getStatus() != PhotoRequestStatus.REJECTED` branch.manabom/src/main/resources/db/migration/V18__extend_notification_type_check.sql (1)
1-9: 🗄️ Data Integrity & Integration | 🔵 Trivial | 🏗️ Heavy liftenum 순서 의존성을 줄이세요
Notification.type가 ordinal로 DB에 저장되고 이 CHECK도 그 값에 맞춰져 있습니다. 지금은 맞지만,SseEventName의 순서 변경이나 중간 삽입이 곧바로 스키마 불일치로 이어지므로 고정 코드값이나 STRING 매핑으로 바꾸는 편이 안전합니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/resources/db/migration/V18__extend_notification_type_check.sql` around lines 1 - 9, `notification_type_check` and the `Notification.type` mapping currently depend on `SseEventName` ordinals, so a future enum reorder or insertion will break schema consistency. Update the persistence/migration design used by `Notification.type` and the related enum mapping to use stable explicit codes or STRING-based storage instead of ordinal values, and adjust the `notification_type_check` constraint to validate those stable values rather than a positional range.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@manabom/src/main/resources/db/migration/V18__extend_notification_type_check.sql`:
- Around line 7-9: `notification.type` is still tied to enum ordinal storage,
and the current CHECK constraint only allows a positional range, so enum
reordering or inserts can silently change meaning. Update the `Notification`
mapping to stop using ordinal semantics by switching `type` to
`@Enumerated(EnumType.STRING)` or a fixed `int code` with a converter, then
adjust the `notification_type_check` migration/constraint to match the new
stable representation instead of `0..9`.
---
Nitpick comments:
In
`@manabom/src/main/java/mannabom_server/manabom/application/matching/service/PhotoRequestService.java`:
- Around line 73-92: `createPhotoRequest` has duplicated
room/member/latest-request lookups because `getPhotoRequestStatus(roomId,
userId)` re-runs the same queries already done earlier, and the later
`latestOpt` status check is dead code once `READY` has been verified. Refactor
`PhotoRequestService#createPhotoRequest` and `getPhotoRequestStatus` to share
the same fetched `LoveViewPhotoRequest`/status data (for example by extracting a
private helper that returns both status and latest request), remove the repeated
repository call, and delete the unreachable `latestOpt.get().getStatus() !=
PhotoRequestStatus.REJECTED` branch.
In
`@manabom/src/main/resources/db/migration/V18__extend_notification_type_check.sql`:
- Around line 1-9: `notification_type_check` and the `Notification.type` mapping
currently depend on `SseEventName` ordinals, so a future enum reorder or
insertion will break schema consistency. Update the persistence/migration design
used by `Notification.type` and the related enum mapping to use stable explicit
codes or STRING-based storage instead of ordinal values, and adjust the
`notification_type_check` constraint to validate those stable values rather than
a positional range.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 0166a592-05d5-4144-8f0b-2afc688a5112
📒 Files selected for processing (3)
manabom/src/main/java/mannabom_server/manabom/application/matching/service/PhotoRequestService.javamanabom/src/main/java/mannabom_server/manabom/presentation/matching/controller/PhotoRequestController.javamanabom/src/main/resources/db/migration/V18__extend_notification_type_check.sql
There was a problem hiding this comment.
🧹 Nitpick comments (1)
manabom/src/main/resources/db/migration/V19__change_notification_type_check.sql (1)
1-17: 🚀 Performance & Scalability | 🔵 Trivial대용량 테이블에서
ALTER COLUMN TYPE은 전체 재작성 + 배타적 락을 유발
USING절을 사용한 컬럼 타입 변경은 Postgres에서 테이블 전체를 재작성하며 ACCESS EXCLUSIVE 락을 획득합니다.notification테이블 규모가 커질 경우 배포 시 다운타임을 유발할 수 있으니, 트래픽이 적은 시간대에 실행하거나 테이블 크기를 사전에 확인하는 것을 권장합니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/resources/db/migration/V19__change_notification_type_check.sql` around lines 1 - 17, The V19 notification migration changes notification.type with ALTER COLUMN TYPE and a USING CASE, which triggers a full table rewrite and ACCESS EXCLUSIVE lock on the notification table. Update this migration to account for the heavy lock impact by either gating the deployment to a low-traffic maintenance window or adding an explicit pre-check/operational note based on notification table size before running the change. Reference the V19__change_notification_type_check.sql migration and its ALTER TABLE notification block so it’s easy to locate.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In
`@manabom/src/main/resources/db/migration/V19__change_notification_type_check.sql`:
- Around line 1-17: The V19 notification migration changes notification.type
with ALTER COLUMN TYPE and a USING CASE, which triggers a full table rewrite and
ACCESS EXCLUSIVE lock on the notification table. Update this migration to
account for the heavy lock impact by either gating the deployment to a
low-traffic maintenance window or adding an explicit pre-check/operational note
based on notification table size before running the change. Reference the
V19__change_notification_type_check.sql migration and its ALTER TABLE
notification block so it’s easy to locate.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: df1f8058-4715-4a9a-934a-e246a5f85240
📒 Files selected for processing (2)
manabom/src/main/java/mannabom_server/manabom/domain/notification/entity/Notification.javamanabom/src/main/resources/db/migration/V19__change_notification_type_check.sql
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
manabom/src/main/java/mannabom_server/manabom/application/meeting/service/MeetingVerificationService.java (1)
158-200: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift인증 완료 판정 구간에 락이 없어 최종 위치가 경쟁적으로 덮어써질 수 있음
nearbyCount >= requiredCount판정부터FINAL_LOC_KEY저장까지 별도 동기화 없이 진행됩니다. 여러 사용자가 거의 동시에 마지막 좌표를 제출하면 각자 서로 다른 스냅샷의bestCluster를 계산해verify()/reward()를 중복 호출하고,FINAL_LOC_KEY가 서로 다른 좌표로 연쇄적으로 덮어써질 수 있습니다.reward()/verify()자체는 멱등적이지만, 최종 인증 장소가 뒤늦게 다른 값으로 바뀌면 지각자(processLatecomer)의 거리 검증 기준이 흔들립니다. 같은 파일에서 이미RedissonClient를 사용 중이니chatRoomId기준 분산 락으로 이 임계구간을 보호하는 것을 권장합니다.RLock lock = redissonClient.getLock("meeting:verify:lock:" + chatRoomId); lock.lock(); try { // 기존 nearbyCount 판정 ~ FINAL_LOC_KEY 저장 로직 } finally { lock.unlock(); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/application/meeting/service/MeetingVerificationService.java` around lines 158 - 200, The `processGeneral` flow has a race condition between the `nearbyCount >= requiredCount` check and writing `FINAL_LOC_KEY`, which can let concurrent submissions re-run `verify()`/`reward()` and overwrite the final location. Protect the entire critical section in `MeetingVerificationService.processGeneral` with a `RedissonClient` lock keyed by `chatRoomId`, wrapping the cluster evaluation through the final location save and cleanup in a try/finally. Keep the existing `saveUserLocation`, `findBestCluster`, `verification.verify()`, `reward()`, and Redis writes inside that lock so only one request can complete the verification path at a time.
🧹 Nitpick comments (6)
manabom/src/main/java/mannabom_server/manabom/presentation/meeting/controller/MeetingVerificationController.java (2)
20-37: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win요청 바디 검증 누락
@RequestBody MeetingVerificationRequest request에@Valid가 없습니다.MeetingVerificationRequest에 검증 애노테이션을 추가한 뒤(별도 파일 코멘트 참고) 이 컨트롤러에도@Valid를 적용해야 실제로 검증이 동작합니다.- `@RequestBody` MeetingVerificationRequest request + `@jakarta.validation.Valid` `@RequestBody` MeetingVerificationRequest request🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/presentation/meeting/controller/MeetingVerificationController.java` around lines 20 - 37, The MeetingVerificationController#verifyMeeting endpoint is missing request-body validation, so add `@Valid` to the MeetingVerificationRequest request parameter and ensure the controller method still passes latitude/longitude through to MeetingVerificationService.verifyMeeting; this will activate the validation annotations defined on MeetingVerificationRequest and make the endpoint reject invalid payloads as intended.
4-6: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win서비스 내부 record를 API 응답 타입으로 직접 노출
MeetingVerificationService.MeetingVerificationStatusData(서비스 계층 내부 record)를 그대로 응답 바디로 반환하고 있습니다. 서비스 내부 구현이 바뀌면 API 계약도 함께 깨질 수 있으니, 전용 프레젠테이션 DTO(MeetingVerificationStatusResponse)로 매핑해 반환하는 것을 권장합니다.Also applies to: 39-46
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/presentation/meeting/controller/MeetingVerificationController.java` around lines 4 - 6, The controller is exposing the service-layer record MeetingVerificationService.MeetingVerificationStatusData directly in the API response. Update MeetingVerificationController to return a presentation DTO such as MeetingVerificationStatusResponse instead, and map the result from MeetingVerificationService into that DTO before wrapping it in ApiResponse. Remove the direct dependency on the internal record so the API contract is owned by the presentation layer.manabom/src/main/java/mannabom_server/manabom/presentation/meeting/dto/MeetingVerificationRequest.java (1)
6-9: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win좌표 값 검증 누락
latitude/longitude가 primitivedouble이라 값이 누락되면 조용히0.0으로 바인딩되어 잘못된 위치 기준으로 거리 계산이 수행될 수 있습니다.Double로 변경하고@NotNull및 위경도 범위 제약을 추가하는 것을 권장합니다(컨트롤러의@Valid적용과 함께 동작).🔧 제안
package mannabom_server.manabom.presentation.meeting.dto; +import jakarta.validation.constraints.DecimalMax; +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.NotNull; import lombok.Getter; `@Getter` public class MeetingVerificationRequest { - private double latitude; - private double longitude; + `@NotNull` + `@DecimalMin`("-90.0") + `@DecimalMax`("90.0") + private Double latitude; + + `@NotNull` + `@DecimalMin`("-180.0") + `@DecimalMax`("180.0") + private Double longitude; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/presentation/meeting/dto/MeetingVerificationRequest.java` around lines 6 - 9, MeetingVerificationRequest의 latitude/longitude가 primitive double이라 누락 시 0.0으로 바인딩되는 문제를 수정하세요. 해당 필드를 Double로 바꾸고, MeetingVerificationRequest에 `@NotNull과` 위도/경도 범위 검증 제약을 추가해 값이 없거나 범위를 벗어나면 검증 실패하도록 하세요. 이 변경이 동작하려면 요청을 받는 컨트롤러에서 `@Valid가` 적용되는지 함께 확인하세요.manabom/src/main/java/mannabom_server/manabom/application/admin/service/AdminUserService.java (1)
283-308: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
effectiveStatus,validateSuspension,accountStatusLabel중복 로직 통합을 권장합니다.동일한 헬퍼 메서드들이
AdminUserService와AdminReportService에 중복 정의되어 있으며,accountStatusLabel의 경우AdminReportService버전은 null 체크가 누락되어 있어 구현이 이미 분기하고 있습니다. 이 로직을UserAccountRestriction엔티티의 정적 메서드로 통합하면 일관성을 유지하고 향후 분기로 인한 버그를 방지할 수 있습니다.♻️ 제안: 엔티티 정적 메서드로 통합
+ // UserAccountRestriction.java + public static UserAccountStatus effectiveStatusOrNull(UserAccountRestriction restriction, LocalDateTime now) { + if (restriction == null) { + return UserAccountStatus.ACTIVE; + } + return restriction.effectiveStatus(now); + } + + public static String accountStatusLabel(UserAccountRestriction restriction, LocalDateTime now) { + if (restriction == null) { + return UserAccountStatus.ACTIVE.name(); + } + UserAccountStatus effectiveStatus = restriction.effectiveStatus(now); + if (effectiveStatus == UserAccountStatus.SUSPENDED && restriction.getSuspendedUntil() != null) { + return effectiveStatus.name() + " until " + restriction.getSuspendedUntil(); + } + return effectiveStatus.name(); + } + + public static void validateSuspension(UserAccountStatus status, LocalDateTime suspendedUntil) { + if (status != UserAccountStatus.SUSPENDED) { + return; + } + if (suspendedUntil == null || !suspendedUntil.isAfter(LocalDateTime.now())) { + throw new IllegalArgumentException("정지 만료 시각은 현재 시각 이후여야 합니다."); + } + }이후
AdminUserService와AdminReportService에서는 엔티티 정적 메서드를 호출하도록 변경하면 됩니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/application/admin/service/AdminUserService.java` around lines 283 - 308, `effectiveStatus`, `validateSuspension`, and `accountStatusLabel` 로직이 `AdminUserService`와 `AdminReportService`에 중복되어 있으니, 이 상태 판정과 라벨 생성/검증을 `UserAccountRestriction` 엔티티의 정적 메서드로 옮겨 공통화하세요. `UserAccountRestriction`, `effectiveStatus`, `validateSuspension`, `accountStatusLabel` 같은 식별자를 기준으로 중복 구현을 제거하고, 두 서비스는 엔티티의 공통 메서드를 호출하도록 바꾸어 null 처리와 정지 만료 판단을 한 곳에서 일관되게 유지하세요.manabom/src/main/resources/static/admin/app.js (1)
1129-1136: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value정지 만료 시각 토글 함수 중복.
syncSuspendedUntilInput와syncReportTargetSuspendedUntilInput이 대상 select/label id만 다를 뿐 동일한 로직입니다. 공통 헬퍼로 통합할 수 있습니다.♻️ 제안 리팩터링
-function syncSuspendedUntilInput() { - $("suspendedUntilLabel").classList.toggle("hidden", $("statusSelect").value !== "SUSPENDED"); -} - -function syncReportTargetSuspendedUntilInput() { - $("reportTargetSuspendedUntilLabel").classList.toggle("hidden", $("reportTargetStatus").value !== "SUSPENDED"); -} +function syncSuspendedUntilVisibility(selectId, labelId) { + $(labelId).classList.toggle("hidden", $(selectId).value !== "SUSPENDED"); +}호출부는
syncSuspendedUntilVisibility("statusSelect", "suspendedUntilLabel"),syncSuspendedUntilVisibility("reportTargetStatus", "reportTargetSuspendedUntilLabel")로 변경합니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/resources/static/admin/app.js` around lines 1129 - 1136, The two toggle helpers are duplicated and differ only by the select/label ids. Extract the shared logic into a common helper such as syncSuspendedUntilVisibility that takes the select and label ids, then update syncSuspendedUntilInput and syncReportTargetSuspendedUntilInput to call it with statusSelect/suspendedUntilLabel and reportTargetStatus/reportTargetSuspendedUntilLabel respectively.manabom/src/main/java/mannabom_server/manabom/presentation/admin/controller/AdminReportController.java (1)
60-66: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winX-Forwarded-For 헤더 무검증 신뢰 - 감사 로그 IP 위조 가능.
신뢰할 수 있는 리버스 프록시 검증 없이
X-Forwarded-For의 첫 값을 그대로 사용합니다. 클라이언트가 이 헤더를 임의로 설정해 감사 로그(ipAddress)에 위조된 값을 남길 수 있어, 관리자 조치 추적성(accountability)이 훼손될 수 있습니다. Spring의ForwardedHeaderFilter나 신뢰 프록시 목록 기반 검증을 통해 신뢰 경계를 명확히 하는 것을 권장합니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@manabom/src/main/java/mannabom_server/manabom/presentation/admin/controller/AdminReportController.java` around lines 60 - 66, The clientIp(HttpServletRequest) helper is trusting X-Forwarded-For directly, which allows forged audit IPs. Update AdminReportController.clientIp to only use forwarded headers when the request has passed through a trusted proxy path, or remove this manual parsing and rely on Spring’s ForwardedHeaderFilter/trusted proxy configuration. Keep the fallback to request.getRemoteAddr() for untrusted requests, and make the trust boundary explicit in the controller or surrounding configuration.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@manabom/src/main/java/mannabom_server/manabom/application/admin/service/AdminReportService.java`:
- Around line 136-159: In AdminReportService.processTargetAccount, add a
required validation for request.getTargetAccountReason() before applying
sensitive status changes so the flow matches grantTargetTing’s reason check. Use
the existing request fields and validation style in this service to reject
missing reasons when updating target account status, especially for SUSPENDED or
WITHDRAWN, and keep the audit log call using the validated reason.
- Around line 146-155: The UserAccountRestriction update path in
AdminReportService.processTargetAccount is using findById followed by save,
which can overwrite concurrent admin changes. Update this flow to use a locked
read such as a repository method like findByUserIdForUpdate, or add optimistic
locking with `@Version` on UserAccountRestriction so concurrent updates fail
instead of silently replacing each other. Keep the change aligned with the
existing grantTargetTing locking pattern and preserve the current update(...)
and save(...) flow after the lock is acquired.
In
`@manabom/src/main/java/mannabom_server/manabom/application/meeting/service/MeetingVerificationService.java`:
- Around line 253-276: `toBestCluster` is failing hard on missing profiles and
inactive members because it calls `profileRepository.findByUser(...).get()` and
`getActiveChatMember(...)` without a safe fallback. Update
`MeetingVerificationService.toBestCluster` to avoid throwing for users whose
profile is absent or whose chat membership is no longer ACTIVATE: fetch
members/profiles defensively, skip or ignore invalid entries, and compute
`hasMale`/`hasFemale` only from valid profiles so one departed user does not
break cluster calculation.
- Around line 49-68: The first-time creation path in
MeetingVerificationService.verifyMeeting is racy because
MeetingVerificationRepository.findByRoomId(...).orElseGet(save) can let two
concurrent requests attempt to create the same room record. Fix this by making
the single-record creation in verifyMeeting or the repository access atomic,
such as by rechecking after save or using a lock/serialized lookup around the
MeetingVerification.room unique constraint, so only one MeetingVerification is
ever created per ChatRoom and the other request reuses the existing row.
In
`@manabom/src/main/java/mannabom_server/manabom/infrastructure/redis/meetingmatching/RedisMatchAtomicOps.java`:
- Around line 17-19: The fixed 3-second leaseTime in RedisMatchAtomicOps is too
short for the full match flow and can let another worker reacquire the same
meeting lock before processMatchSuccess or rollbackMatch completes. Update the
locking in RedisMatchAtomicOps to use Redisson’s watchdog-backed tryLock
overload or a much longer leaseTime that safely covers candidate search,
Redis/DB work, and chat room creation, and add idempotency protection around the
matching flow so duplicate execution cannot create duplicate matches.
---
Outside diff comments:
In
`@manabom/src/main/java/mannabom_server/manabom/application/meeting/service/MeetingVerificationService.java`:
- Around line 158-200: The `processGeneral` flow has a race condition between
the `nearbyCount >= requiredCount` check and writing `FINAL_LOC_KEY`, which can
let concurrent submissions re-run `verify()`/`reward()` and overwrite the final
location. Protect the entire critical section in
`MeetingVerificationService.processGeneral` with a `RedissonClient` lock keyed
by `chatRoomId`, wrapping the cluster evaluation through the final location save
and cleanup in a try/finally. Keep the existing `saveUserLocation`,
`findBestCluster`, `verification.verify()`, `reward()`, and Redis writes inside
that lock so only one request can complete the verification path at a time.
---
Nitpick comments:
In
`@manabom/src/main/java/mannabom_server/manabom/application/admin/service/AdminUserService.java`:
- Around line 283-308: `effectiveStatus`, `validateSuspension`, and
`accountStatusLabel` 로직이 `AdminUserService`와 `AdminReportService`에 중복되어 있으니, 이
상태 판정과 라벨 생성/검증을 `UserAccountRestriction` 엔티티의 정적 메서드로 옮겨 공통화하세요.
`UserAccountRestriction`, `effectiveStatus`, `validateSuspension`,
`accountStatusLabel` 같은 식별자를 기준으로 중복 구현을 제거하고, 두 서비스는 엔티티의 공통 메서드를 호출하도록 바꾸어
null 처리와 정지 만료 판단을 한 곳에서 일관되게 유지하세요.
In
`@manabom/src/main/java/mannabom_server/manabom/presentation/admin/controller/AdminReportController.java`:
- Around line 60-66: The clientIp(HttpServletRequest) helper is trusting
X-Forwarded-For directly, which allows forged audit IPs. Update
AdminReportController.clientIp to only use forwarded headers when the request
has passed through a trusted proxy path, or remove this manual parsing and rely
on Spring’s ForwardedHeaderFilter/trusted proxy configuration. Keep the fallback
to request.getRemoteAddr() for untrusted requests, and make the trust boundary
explicit in the controller or surrounding configuration.
In
`@manabom/src/main/java/mannabom_server/manabom/presentation/meeting/controller/MeetingVerificationController.java`:
- Around line 20-37: The MeetingVerificationController#verifyMeeting endpoint is
missing request-body validation, so add `@Valid` to the MeetingVerificationRequest
request parameter and ensure the controller method still passes
latitude/longitude through to MeetingVerificationService.verifyMeeting; this
will activate the validation annotations defined on MeetingVerificationRequest
and make the endpoint reject invalid payloads as intended.
- Around line 4-6: The controller is exposing the service-layer record
MeetingVerificationService.MeetingVerificationStatusData directly in the API
response. Update MeetingVerificationController to return a presentation DTO such
as MeetingVerificationStatusResponse instead, and map the result from
MeetingVerificationService into that DTO before wrapping it in ApiResponse.
Remove the direct dependency on the internal record so the API contract is owned
by the presentation layer.
In
`@manabom/src/main/java/mannabom_server/manabom/presentation/meeting/dto/MeetingVerificationRequest.java`:
- Around line 6-9: MeetingVerificationRequest의 latitude/longitude가 primitive
double이라 누락 시 0.0으로 바인딩되는 문제를 수정하세요. 해당 필드를 Double로 바꾸고,
MeetingVerificationRequest에 `@NotNull과` 위도/경도 범위 검증 제약을 추가해 값이 없거나 범위를 벗어나면 검증
실패하도록 하세요. 이 변경이 동작하려면 요청을 받는 컨트롤러에서 `@Valid가` 적용되는지 함께 확인하세요.
In `@manabom/src/main/resources/static/admin/app.js`:
- Around line 1129-1136: The two toggle helpers are duplicated and differ only
by the select/label ids. Extract the shared logic into a common helper such as
syncSuspendedUntilVisibility that takes the select and label ids, then update
syncSuspendedUntilInput and syncReportTargetSuspendedUntilInput to call it with
statusSelect/suspendedUntilLabel and
reportTargetStatus/reportTargetSuspendedUntilLabel respectively.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 8a6d4c70-1230-42c0-8e21-39127b94e58e
📒 Files selected for processing (34)
manabom/src/main/java/mannabom_server/manabom/application/admin/dto/request/AdminAdjustWalletRequest.javamanabom/src/main/java/mannabom_server/manabom/application/admin/dto/request/AdminProcessReportRequest.javamanabom/src/main/java/mannabom_server/manabom/application/admin/dto/request/AdminUpdateUserStatusRequest.javamanabom/src/main/java/mannabom_server/manabom/application/admin/dto/response/AdminReportDetailResponse.javamanabom/src/main/java/mannabom_server/manabom/application/admin/dto/response/AdminReportListResponse.javamanabom/src/main/java/mannabom_server/manabom/application/admin/dto/response/AdminReportSummaryResponse.javamanabom/src/main/java/mannabom_server/manabom/application/admin/dto/response/AdminUserDetailResponse.javamanabom/src/main/java/mannabom_server/manabom/application/admin/dto/response/AdminUserSummaryResponse.javamanabom/src/main/java/mannabom_server/manabom/application/admin/service/AdminAuditService.javamanabom/src/main/java/mannabom_server/manabom/application/admin/service/AdminReportService.javamanabom/src/main/java/mannabom_server/manabom/application/admin/service/AdminUserService.javamanabom/src/main/java/mannabom_server/manabom/application/meeting/handler/MatchingEventListener.javamanabom/src/main/java/mannabom_server/manabom/application/meeting/service/MeetingMatchingService.javamanabom/src/main/java/mannabom_server/manabom/application/meeting/service/MeetingVerificationService.javamanabom/src/main/java/mannabom_server/manabom/domain/admin/entity/UserAccountRestriction.javamanabom/src/main/java/mannabom_server/manabom/domain/admin/enums/AdminAuditActionType.javamanabom/src/main/java/mannabom_server/manabom/domain/admin/enums/AdminAuditTargetType.javamanabom/src/main/java/mannabom_server/manabom/domain/meeting/entity/MeetingMatch.javamanabom/src/main/java/mannabom_server/manabom/domain/meeting/entity/MeetingVerification.javamanabom/src/main/java/mannabom_server/manabom/domain/report/entity/Report.javamanabom/src/main/java/mannabom_server/manabom/domain/report/repository/ReportRepository.javamanabom/src/main/java/mannabom_server/manabom/infrastructure/redis/meetingmatching/RedisMatchAtomicOps.javamanabom/src/main/java/mannabom_server/manabom/infrastructure/redis/meetingmatching/RedisMatchHistory.javamanabom/src/main/java/mannabom_server/manabom/infrastructure/redis/meetingmatching/RedisMatchQueue.javamanabom/src/main/java/mannabom_server/manabom/infrastructure/security/admin/UserAccountAccessGuardFilter.javamanabom/src/main/java/mannabom_server/manabom/presentation/admin/controller/AdminReportController.javamanabom/src/main/java/mannabom_server/manabom/presentation/meeting/controller/MeetingVerificationController.javamanabom/src/main/java/mannabom_server/manabom/presentation/meeting/dto/MeetingVerificationRequest.javamanabom/src/main/java/mannabom_server/manabom/presentation/meeting/dto/MeetingVerificationResponse.javamanabom/src/main/resources/db/migration/V18__add_admin_report_cs_support.sqlmanabom/src/main/resources/db/migration/V20__add_meeting_verification_valid_window.sqlmanabom/src/main/resources/static/admin/app.jsmanabom/src/main/resources/static/admin/index.htmlmanabom/src/main/resources/static/admin/styles.css
✅ Files skipped from review due to trivial changes (2)
- manabom/src/main/resources/db/migration/V20__add_meeting_verification_valid_window.sql
- manabom/src/main/java/mannabom_server/manabom/application/admin/dto/response/AdminReportSummaryResponse.java
변경 사항
주요 구현
POST /api/loveview/profile/request/{chatRoomId}추가READY인지 검증PHOTO_REQUEST_RECEIVED,PHOTO_REQUEST_ACCEPTED,PHOTO_REQUEST_REJECTED이벤트 저장 가능하도록 DB 제약 수정Summary by CodeRabbit