Skip to content

feat: 모임방/기록/투표/오늘의 한마디 신고 API 추가 - #376

Merged
hd0rable merged 1 commit into
developfrom
feat/report-room-and-roompost
Aug 7, 2026
Merged

feat: 모임방/기록/투표/오늘의 한마디 신고 API 추가#376
hd0rable merged 1 commit into
developfrom
feat/report-room-and-roompost

Conversation

@hd0rable

@hd0rable hd0rable commented Aug 7, 2026

Copy link
Copy Markdown
Member

Summary

  • Comment/Feed에 이어 Room, Record, Vote, AttendanceCheck 도메인에도 신고 API 추가 (신고 횟수만 증가, 별도 처리 없음)
  • Room, AttendanceCheck: report_count 컬럼이 없어 마이그레이션으로 추가
  • Record, Vote: posts 테이블(단일 테이블 상속)에 Feed가 쓰던 report_count 컬럼이 이미 존재해 엔티티 매핑만 추가
  • posts.report_count의 기존 NULL 값(Record/Vote 로우)을 0으로 백필하고, rooms/attendance_checks와 함께 NOT NULL 제약 추가

Endpoints

  • POST /rooms/{roomId}/report
  • POST /rooms/{roomId}/record/{recordId}/report
  • POST /rooms/{roomId}/vote/{voteId}/report
  • POST /rooms/{roomId}/daily-greeting/{attendanceCheckId}/report

Test plan

  • 4개 도메인 각각 통합 테스트 작성 (성공/누적/404) - RoomReportApiTest, RecordReportApiTest, VoteReportApiTest, AttendanceCheckReportApiTest
  • 전체 테스트 스위트 통과 확인

🤖 Generated with Claude Code

Summary by CodeRabbit

  • 새 기능

    • 모임방, 기록, 투표, 출석 체크를 신고할 수 있는 API가 추가되었습니다.
    • 신고할 때마다 해당 항목의 신고 횟수가 누적되며, 처리 결과로 대상 ID와 최신 신고 횟수를 확인할 수 있습니다.
    • 존재하지 않는 신고 대상에 대해 항목별 오류 응답이 제공됩니다.
  • 데이터 개선

    • 신고 횟수가 기본값 0으로 관리되며, 기존 데이터도 안정적으로 처리됩니다.
  • 테스트

    • 신고 성공, 반복 신고 누적, 잘못된 대상에 대한 오류 응답을 검증하는 테스트가 추가되었습니다.

Comment, Feed에 이어 Room, Record, Vote, AttendanceCheck 도메인에도
신고 API를 추가하고 신고 횟수만 증가시키도록 구현.

- Room, AttendanceCheck: report_count 컬럼이 없어 마이그레이션 추가
- Record, Vote: posts 테이블(단일 테이블 상속)에 Feed가 쓰던 report_count
  컬럼이 이미 있어 엔티티 매핑만 추가
- posts.report_count의 기존 NULL 값(Record/Vote)을 0으로 백필하고
  rooms/attendance_checks와 함께 NOT NULL 제약 추가

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

방, 기록, 투표, 출석 체크에 신고 횟수 필드와 증가 로직을 추가했습니다. 각 신고 API는 갱신된 식별자와 신고 횟수를 반환합니다. 데이터베이스 마이그레이션과 통합 테스트도 추가했습니다.

Changes

신고 횟수 데이터 모델과 영속성

Layer / File(s) Summary
신고 횟수 데이터 모델과 영속성
src/main/java/konkuk/thip/{room,roompost}/domain/*, src/main/java/konkuk/thip/{room,roompost}/adapter/out/{jpa,mapper}/*, src/main/resources/db/migration/*, src/main/java/konkuk/thip/feed/adapter/out/jpa/FeedJpaEntity.java
방, 기록, 투표, 출석 체크에 reportCount와 증가 메서드를 추가했습니다. JPA 매핑과 양방향 변환을 갱신했습니다. 기존 posts.report_count의 NULL 값을 0으로 변경하고 필수 컬럼으로 설정했습니다.

방 신고 처리 흐름

Layer / File(s) Summary
방 신고 처리 흐름
src/main/java/konkuk/thip/room/application/port/in/*, src/main/java/konkuk/thip/room/application/service/RoomReportService.java, src/main/java/konkuk/thip/room/adapter/in/web/*
POST /rooms/{roomId}/report 엔드포인트를 추가했습니다. 서비스는 방을 조회하고 신고 횟수를 증가시킨 뒤 RoomReportResponse를 반환합니다.

기록·투표·출석 체크 신고 처리

Layer / File(s) Summary
기록·투표·출석 체크 신고 처리
src/main/java/konkuk/thip/roompost/application/{port,service}/*, src/main/java/konkuk/thip/roompost/adapter/in/web/*, src/main/java/konkuk/thip/roompost/adapter/out/persistence/*
기록, 투표, 출석 체크 신고 유스케이스와 서비스, 응답 레코드를 추가했습니다. 신고 횟수를 증가시키고 갱신된 결과를 반환합니다.

신고 API 오류 계약과 통합 검증

Layer / File(s) Summary
신고 API 오류 계약과 통합 검증
src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java, src/test/java/konkuk/thip/{room,roompost}/adapter/in/web/*ReportApiTest.java
신고 대상별 조회 실패 오류 코드를 Swagger 설명에 연결했습니다. 성공, 반복 신고 누적, 존재하지 않는 대상의 404 응답을 통합 테스트로 검증합니다.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant RoomPostCommandController
  participant ReportService
  participant JPAEntity

  Client->>RoomPostCommandController: 신고 요청
  RoomPostCommandController->>ReportService: 신고 대상 ID 전달
  ReportService->>JPAEntity: 대상 조회 및 reportCount 증가
  JPAEntity-->>ReportService: 갱신된 신고 횟수
  ReportService-->>RoomPostCommandController: 신고 결과 반환
  RoomPostCommandController-->>Client: 식별자와 reportCount 응답
Loading

Possibly related PRs

Suggested labels: 🛠️ feat

Suggested reviewers: buzz0331, seongjunnoh

Poem

당근처럼 신고 수가 하나씩 올라가요
토끼는 방과 기록을 살펴봐요
투표와 출석도 빠짐없이 세어요
저장소에는 0부터 숫자가 자라요
깡충, API 응답도 함께 달려요

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 모임방, 기록, 투표, 오늘의 한마디 신고 API 추가라는 PR의 주요 변경 사항을 명확하게 요약합니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/report-room-and-roompost

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

Test Results

512 tests   512 ✅  49s ⏱️
153 suites    0 💤
153 files      0 ❌

Results for commit e7b79eb.

@hd0rable
hd0rable merged commit 19ba488 into develop Aug 7, 2026
3 of 4 checks passed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 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
`@src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java`:
- Around line 92-102: 중첩 경로의 방 소속 검증이 누락되어 하위 리소스만으로 처리되는 문제를 수정하십시오.
RoomPostCommandController의 92-102행에서는 roomId와 recordId를 함께 recordReportUseCase에
전달하고, 168-178행과 213-225행에서도 각각 roomId와 voteId, roomId와 attendanceCheckId를 함께
전달하도록 관련 use case 계약과 서비스를 변경해 소속이 다르면 대상 없음 오류를 반환하십시오.
src/test/java/konkuk/thip/roompost/adapter/in/web/RecordReportApiTest.java
106-118행에는 다른 방의 기록 신고 시 404를 반환하고 신고 횟수가 변경되지 않는 테스트를 추가하십시오.

In
`@src/main/java/konkuk/thip/roompost/adapter/out/persistence/AttendanceCheckCommandPersistenceAdapter.java`:
- Around line 50-56: Update AttendanceCheckCommandPersistenceAdapter.update to
avoid the read-then-save lost update by using a database-level atomic increment
of report_count for every reporting path. Check the affected-row count and throw
EntityNotFoundException with ATTENDANCE_CHECK_NOT_FOUND when no row is updated;
alternatively, apply `@Version-based` optimistic locking with retries. Add a
concurrent-report test that verifies all increments are accumulated.

In
`@src/main/java/konkuk/thip/roompost/application/service/AttendanceCheckReportService.java`:
- Around line 20-22: Update reportAttendanceCheck() and the
attendanceCheckCommandPort update flow to preserve concurrent report_count
increments: use a row lock, an atomic database increment, or `@Version-based`
optimistic-lock retry. Ensure concurrent requests cannot overwrite each other’s
increaseReportCount() result.

In
`@src/main/java/konkuk/thip/roompost/application/service/RecordReportService.java`:
- Around line 20-22: Update reportRecord() to make report-count increments
concurrency-safe: replace the current getByIdOrThrow(), increaseReportCount(),
and update() read-modify-write flow with an atomic increment query, or ensure
the lookup uses a PESSIMISTIC_WRITE row lock throughout the transaction.
Preserve the existing behavior of identifying the record by recordId and
propagating the not-found failure.

In
`@src/main/java/konkuk/thip/roompost/application/service/VoteReportService.java`:
- Around line 20-22: VoteReportService.reportVote()의 증가-저장 흐름이 동시 신고에서 갱신을 유실하지
않도록 보호하십시오. VoteJpaEntity와 voteCommandPort.updateVote() 저장 경로를 확인해 `@Version` 기반
낙관적 잠금과 재시도를 적용하거나, report_count를 데이터베이스에서 원자적으로 증가시키는 전용 갱신을 사용하십시오. 동시 요청에서도
신고 횟수가 모두 누적되는 테스트를 추가하십시오.
🪄 Autofix

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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 874904d5-5aa9-4330-83db-3a2a4db2599c

📥 Commits

Reviewing files that changed from the base of the PR and between c835294 and e7b79eb.

📒 Files selected for processing (39)
  • src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java
  • src/main/java/konkuk/thip/feed/adapter/out/jpa/FeedJpaEntity.java
  • src/main/java/konkuk/thip/room/adapter/in/web/RoomCommandController.java
  • src/main/java/konkuk/thip/room/adapter/in/web/response/RoomReportResponse.java
  • src/main/java/konkuk/thip/room/adapter/out/jpa/RoomJpaEntity.java
  • src/main/java/konkuk/thip/room/adapter/out/mapper/RoomMapper.java
  • src/main/java/konkuk/thip/room/application/port/in/RoomReportUseCase.java
  • src/main/java/konkuk/thip/room/application/port/in/dto/RoomReportResult.java
  • src/main/java/konkuk/thip/room/application/service/RoomReportService.java
  • src/main/java/konkuk/thip/room/domain/Room.java
  • src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java
  • src/main/java/konkuk/thip/roompost/adapter/in/web/response/AttendanceCheckReportResponse.java
  • src/main/java/konkuk/thip/roompost/adapter/in/web/response/RecordReportResponse.java
  • src/main/java/konkuk/thip/roompost/adapter/in/web/response/VoteReportResponse.java
  • src/main/java/konkuk/thip/roompost/adapter/out/jpa/AttendanceCheckJpaEntity.java
  • src/main/java/konkuk/thip/roompost/adapter/out/jpa/RecordJpaEntity.java
  • src/main/java/konkuk/thip/roompost/adapter/out/jpa/VoteJpaEntity.java
  • src/main/java/konkuk/thip/roompost/adapter/out/mapper/AttendanceCheckMapper.java
  • src/main/java/konkuk/thip/roompost/adapter/out/mapper/RecordMapper.java
  • src/main/java/konkuk/thip/roompost/adapter/out/mapper/VoteMapper.java
  • src/main/java/konkuk/thip/roompost/adapter/out/persistence/AttendanceCheckCommandPersistenceAdapter.java
  • src/main/java/konkuk/thip/roompost/application/port/in/AttendanceCheckReportUseCase.java
  • src/main/java/konkuk/thip/roompost/application/port/in/RecordReportUseCase.java
  • src/main/java/konkuk/thip/roompost/application/port/in/VoteReportUseCase.java
  • src/main/java/konkuk/thip/roompost/application/port/in/dto/attendancecheck/AttendanceCheckReportResult.java
  • src/main/java/konkuk/thip/roompost/application/port/in/dto/record/RecordReportResult.java
  • src/main/java/konkuk/thip/roompost/application/port/in/dto/vote/VoteReportResult.java
  • src/main/java/konkuk/thip/roompost/application/port/out/AttendanceCheckCommandPort.java
  • src/main/java/konkuk/thip/roompost/application/service/AttendanceCheckReportService.java
  • src/main/java/konkuk/thip/roompost/application/service/RecordReportService.java
  • src/main/java/konkuk/thip/roompost/application/service/VoteReportService.java
  • src/main/java/konkuk/thip/roompost/domain/AttendanceCheck.java
  • src/main/java/konkuk/thip/roompost/domain/Record.java
  • src/main/java/konkuk/thip/roompost/domain/Vote.java
  • src/main/resources/db/migration/V260807__Add_report_count_to_rooms_and_attendance_checks.sql
  • src/test/java/konkuk/thip/room/adapter/in/web/RoomReportApiTest.java
  • src/test/java/konkuk/thip/roompost/adapter/in/web/AttendanceCheckReportApiTest.java
  • src/test/java/konkuk/thip/roompost/adapter/in/web/RecordReportApiTest.java
  • src/test/java/konkuk/thip/roompost/adapter/in/web/VoteReportApiTest.java

Comment on lines +92 to +102
@Operation(
summary = "기록 신고",
description = "사용자가 기록을 신고합니다. 신고 횟수만 증가하며 별도의 처리는 이루어지지 않습니다."
)
@ExceptionDescription(RECORD_REPORT)
@PostMapping("/rooms/{roomId}/record/{recordId}/report")
public BaseResponse<RecordReportResponse> reportRecord(
@Parameter(description = "신고할 방 ID", example = "1") @PathVariable("roomId") final Long roomId,
@Parameter(description = "신고하려는 기록 ID", example = "1") @PathVariable("recordId") final Long recordId) {
return BaseResponse.ok(RecordReportResponse.of(recordReportUseCase.reportRecord(recordId)));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

중첩 경로의 roomId 소속을 검증하십시오.

세 엔드포인트는 {roomId}를 받지만 use case에는 하위 리소스 ID만 전달합니다. 따라서 POST /rooms/A/record/B/report에서 기록 B가 방 A에 속하지 않아도 B의 신고 횟수가 증가합니다. 하위 리소스의 방 ID가 경로의 roomId와 다르면 대상 없음 오류를 반환하도록 use case 계약과 서비스를 변경하십시오.

  • src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java#L92-L102: roomIdrecordId를 함께 use case에 전달하십시오.
  • src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java#L168-L178: roomIdvoteId를 함께 use case에 전달하십시오.
  • src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java#L213-L225: roomIdattendanceCheckId를 함께 use case에 전달하십시오.
  • src/test/java/konkuk/thip/roompost/adapter/in/web/RecordReportApiTest.java#L106-L118: 다른 방에 속한 기록을 신고하면 404를 반환하고 신고 횟수를 변경하지 않는 테스트를 추가하십시오.
📍 Affects 2 files
  • src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java#L92-L102 (this comment)
  • src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java#L168-L178
  • src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java#L213-L225
  • src/test/java/konkuk/thip/roompost/adapter/in/web/RecordReportApiTest.java#L106-L118
🤖 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
`@src/main/java/konkuk/thip/roompost/adapter/in/web/RoomPostCommandController.java`
around lines 92 - 102, 중첩 경로의 방 소속 검증이 누락되어 하위 리소스만으로 처리되는 문제를 수정하십시오.
RoomPostCommandController의 92-102행에서는 roomId와 recordId를 함께 recordReportUseCase에
전달하고, 168-178행과 213-225행에서도 각각 roomId와 voteId, roomId와 attendanceCheckId를 함께
전달하도록 관련 use case 계약과 서비스를 변경해 소속이 다르면 대상 없음 오류를 반환하십시오.
src/test/java/konkuk/thip/roompost/adapter/in/web/RecordReportApiTest.java
106-118행에는 다른 방의 기록 신고 시 404를 반환하고 신고 횟수가 변경되지 않는 테스트를 추가하십시오.

Comment on lines +50 to +56
@Override
public void update(AttendanceCheck attendanceCheck) {
AttendanceCheckJpaEntity attendanceCheckJpaEntity = attendanceCheckJpaRepository.findByAttendanceCheckId(attendanceCheck.getId()).orElseThrow(
() -> new EntityNotFoundException(ATTENDANCE_CHECK_NOT_FOUND)
);

attendanceCheckJpaRepository.save(attendanceCheckJpaEntity.updateFrom(attendanceCheck));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

신고 횟수 증가를 원자 연산으로 변경하십시오.

Line 52의 조회와 Line 56의 저장 사이에 잠금 또는 버전 검사가 없습니다. 두 요청이 reportCount = N을 동시에 읽으면 둘 다 N + 1을 저장합니다. 기대값 N + 2 대신 N + 1이 저장됩니다.

모든 신고 경로에서 report_count = report_count + 1 원자 UPDATE를 사용하십시오. 영향 행 수가 0이면 ATTENDANCE_CHECK_NOT_FOUND를 반환하십시오. 또는 @Version과 재시도를 사용하십시오. 동시 요청 누적 테스트도 추가하십시오.

🤖 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
`@src/main/java/konkuk/thip/roompost/adapter/out/persistence/AttendanceCheckCommandPersistenceAdapter.java`
around lines 50 - 56, Update AttendanceCheckCommandPersistenceAdapter.update to
avoid the read-then-save lost update by using a database-level atomic increment
of report_count for every reporting path. Check the affected-row count and throw
EntityNotFoundException with ATTENDANCE_CHECK_NOT_FOUND when no row is updated;
alternatively, apply `@Version-based` optimistic locking with retries. Add a
concurrent-report test that verifies all increments are accumulated.

Comment on lines +20 to +22
AttendanceCheck attendanceCheck = attendanceCheckCommandPort.getByIdOrThrow(attendanceCheckId);
attendanceCheck.increaseReportCount();
attendanceCheckCommandPort.update(attendanceCheck);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

ast-grep outline src/main/java/konkuk/thip/roompost/application/service/AttendanceCheckReportService.java --items all
ast-grep outline src/main/java/konkuk/thip/roompost/adapter/out/persistence/AttendanceCheckCommandPersistenceAdapter.java --items all

rg -n -C 4 -P '`@Version`|`@Lock`|PESSIMISTIC|report_count|reportCount|update\(AttendanceCheck|findByAttendanceCheckId' \
  src/main/java/konkuk/thip/roompost

Repository: THIP-TextHip/THIP-Server

Length of output: 35771


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,90p' src/main/java/konkuk/thip/roompost/application/service/AttendanceCheckReportService.java
sed -n '44,58p' src/main/java/konkuk/thip/roompost/adapter/out/persistence/AttendanceCheckCommandPersistenceAdapter.java

rg -n -C 3 'reportAttendanceCheck|attendanceCheckCommandPort\.report|reportAttendanceCheck\(' src/main/java/konkuk/thip/roompost

Repository: THIP-TextHip/THIP-Server

Length of output: 5164


동시 신고에서 report_count 증가분을 보존해야 합니다.

reportAttendanceCheck()가 조회 afterward 값에 1을 더한 뒤 update()가 또 다시 엔티티에서 reportCount를 덮어써 저장합니다. 두 트랜잭션이 같은 값을 읽으면 둘 다 같은 값으로 저장하고 1회 증가분이 누락됩니다.

행 잠금, DB 원자적 증가 쿼리, 또는 @Version 기반 재시도를 추가하십시오.

🤖 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
`@src/main/java/konkuk/thip/roompost/application/service/AttendanceCheckReportService.java`
around lines 20 - 22, Update reportAttendanceCheck() and the
attendanceCheckCommandPort update flow to preserve concurrent report_count
increments: use a row lock, an atomic database increment, or `@Version-based`
optimistic-lock retry. Ensure concurrent requests cannot overwrite each other’s
increaseReportCount() result.

Comment on lines +20 to +22
Record record = recordCommandPort.getByIdOrThrow(recordId);
record.increaseReportCount();
recordCommandPort.update(record);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

ast-grep outline src/main/java/konkuk/thip/roompost/application/service/RecordReportService.java --items all

rg -n -C 5 -P 'interface\s+RecordCommandPort|class\s+\w*Record\w*Persistence\w*Adapter|void\s+update\(Record|`@Version`|`@Lock`|PESSIMISTIC|report_count|reportCount' \
  src/main/java/konkuk/thip/roompost

Repository: THIP-TextHip/THIP-Server

Length of output: 37797


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

sed -n '1,140p' src/main/java/konkuk/thip/roompost/application/service/RecordReportService.java
printf '\n--- RecordCommand persistence ---\n'
sed -n '80,105p' src/main/java/konkuk/thip/roompost/adapter/out/persistence/RecordCommandPersistenceAdapter.java
printf '\n--- RecordJpaRepository ---\n'
cat -n src/main/java/konkuk/thip/roompost/adapter/out/persistence/repository/record/RecordJpaRepository.java
printf '\n--- RecordCommandPort ---\n'
cat -n src/main/java/konkuk/thip/roompost/application/port/out/RecordCommandPort.java
printf '\n--- Record domain ---\n'
sed -n '1,95p' src/main/java/konkuk/thip/roompost/domain/Record.java

printf '\n--- all RecordCommandPersistenceAdapter methods ---\n'
rg -n -A6 -B2 'public\s+\w+\s+(save|update|delete|softDelete|increaseReportCount)' src/main/java/konkuk/thip/roompost/adapter/out/persistence/RecordCommandPersistenceAdapter.java

Repository: THIP-TextHip/THIP-Server

Length of output: 9596


동시 신고 증가를 원자적으로 보호하십시오.

reportRecord()의 조회-증가-저장 흐름에서 findById()PESSIMISTIC_WRITE 잠금을 사용하지 않습니다. update()도 별도 조회 후 새 도메인 값을 덮어써서 별도 잠금 없이 저장하므로, 동시 신고 요청이 서로 겹치면 신고 횟수가 손실될 수 있습니다. 증가 문법을 원자 쿼리로 처리하거나 행 잠금을 적용하십시오.

🤖 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
`@src/main/java/konkuk/thip/roompost/application/service/RecordReportService.java`
around lines 20 - 22, Update reportRecord() to make report-count increments
concurrency-safe: replace the current getByIdOrThrow(), increaseReportCount(),
and update() read-modify-write flow with an atomic increment query, or ensure
the lookup uses a PESSIMISTIC_WRITE row lock throughout the transaction.
Preserve the existing behavior of identifying the record by recordId and
propagating the not-found failure.

Comment on lines +20 to +22
Vote vote = voteCommandPort.getByIdOrThrow(voteId);
vote.increaseReportCount();
voteCommandPort.updateVote(vote);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Vote 엔티티의 버전 컬럼 및 신고 횟수 매핑을 확인한다.
ast-grep outline \
  src/main/java/konkuk/thip/roompost/adapter/out/jpa/VoteJpaEntity.java \
  --items all

# Vote 저장 경로가 원자적 UPDATE 또는 낙관적 잠금을 사용하는지 확인한다.
rg -n -C 4 \
  '`@Version`|reportCount|report_count|updateVote\s*\(|`@Modifying`|UPDATE\s+' \
  src/main/java/konkuk/thip/roompost

# 신고 횟수 증가의 동시성 테스트 유무를 확인한다.
rg -n -C 3 \
  'reportVote|ExecutorService|CompletableFuture|CountDownLatch|reportCount' \
  src/test/java/konkuk/thip/roompost

Repository: THIP-TextHip/THIP-Server

Length of output: 50380


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Vote entity/mapper/pertistence relevant sections =="
sed -n '1,120p' src/main/java/konkuk/thip/roompost/domain/Vote.java
echo
sed -n '1,90p' src/main/java/konkuk/thip/roompost/adapter/out/jpa/VoteJpaEntity.java
echo
sed -n '190,210p' src/main/java/konkuk/thip/roompost/adapter/out/persistence/VoteCommandPersistenceAdapter.java
echo
rg -n '`@Version`|Lock(\\.|\\(|Pessimistic|Optimistic)|Pessimistic|Optimistic|PessimisticLock|OptimisticLock|report_count\\s*=.*report_count|reportCount\\s*=.*(reportCount|current|old)' src/main/java/konkuk/thip/roompost

echo
echo "== VoteReport tests mentioning concurrency/reportCount =="
rg -n -C 4 'reportVote|report_count|reportCount|ExecutorService|CompletableFuture|CountDownLatch|Thread|concurr|race|concurrent' src/test/java/konkuk/thip/roompost/application/service src/test/java/konkuk/thip/roompost/adapter/in/web src/test/java/konkuk/thip/roompost/adapter/out || true

Repository: THIP-TextHip/THIP-Server

Length of output: 6548


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== VoteCommandPersistenceAdapter updateVote =="
sed -n '190,210p' src/main/java/konkuk/thip/roompost/adapter/out/persistence/VoteCommandPersistenceAdapter.java

echo
echo "== Lock/version/update counters in vote/package =="
rg -n --no-heading '`@Version`|Lock\.|`@Lock`|Pessimistic|Optimistic|PessimisticLock|OptimisticLock|report_count\s*=.*report_count|reportCount\s*=.*(reportCount|current|old)' src/main/java/konkuk/thip/roompost

echo
echo "== VoteReport test concurrency/reportCount references =="
rg -n -C 3 --no-heading 'reportVote|report_count|reportCount|ExecutorService|CompletableFuture|CountDownLatch|Thread|concurr|race|concurrent' src/test/java || true

Repository: THIP-TextHip/THIP-Server

Length of output: 34605


동시 투표 신고에서 report_count 누적을 보호하십시오.

VoteReportService.reportVote()가 메모리에서 reportCount++save()를 호출하지만, VoteJpaEntity에는 @Version이 없고 저장 경로도 save()로 덮어쓰는 구조입니다. 동일한 report_count 값을 읽은 두 트랜잭션이면 실제 신고가 2번이어도 1번만 저장될 수 있습니다. @Version 낙관적 잠금의 재시도 또는 report_count = report_count + 1의 원자적 갱신을 추가하고, 동시 요청 테스트를 병행하십시오.

🤖 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
`@src/main/java/konkuk/thip/roompost/application/service/VoteReportService.java`
around lines 20 - 22, VoteReportService.reportVote()의 증가-저장 흐름이 동시 신고에서 갱신을 유실하지
않도록 보호하십시오. VoteJpaEntity와 voteCommandPort.updateVote() 저장 경로를 확인해 `@Version` 기반
낙관적 잠금과 재시도를 적용하거나, report_count를 데이터베이스에서 원자적으로 증가시키는 전용 갱신을 사용하십시오. 동시 요청에서도
신고 횟수가 모두 누적되는 테스트를 추가하십시오.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant