Skip to content

feat: expose spot roles and settlement status#123

Open
seoJing wants to merge 2 commits into
developfrom
feat/spot-member-roles-settlement-lookup-122
Open

feat: expose spot roles and settlement status#123
seoJing wants to merge 2 commits into
developfrom
feat/spot-member-roles-settlement-lookup-122

Conversation

@seoJing
Copy link
Copy Markdown
Member

@seoJing seoJing commented Jun 1, 2026

Summary

  • expose OWNER/SUPPORTER/PARTNER roles on chat members and spot participants
  • preserve accepted feed application role when converting a feed to a spot
  • return fundingGoal/fundedAmount/remainingAmount/remainingParticipantCount on feed responses
  • return spotConverted/spotId from feed application acceptance when conversion happens
  • add latest settlement to spot detail and GET /api/spots/{spotId}/settlement
  • add migration for spot_participants.application_role

Closes #122

Verification

  • JAVA_HOME=/opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home ./gradlew compileJava testClasses :capstone-api:test --tests backend.feed.service.FeedItemServiceTest ✅
  • JAVA_HOME=/opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home ./gradlew bootJar ✅
  • JAVA_HOME=/opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home ./gradlew checkstyleMain ✅ (warnings remain from existing files; build is configured ignoreFailures=true)
  • JAVA_HOME=/opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home ./gradlew test ⚠️ fails at BackendApplicationTests.contextLoads because local PostgreSQL is not running/available (PSQLException connection refused), unrelated to this change

Backend API note

This PR covers the frontend visibility/control flow with existing APIs plus small response additions. One backend gap remains only if product requires settlement approval to move points automatically: current settlement approval records agreement state only, and the point domain has balance/transaction primitives but no clear settlement transfer/escrow policy yet.

구현한 것:

피드에서 “얼마나 더 모아야 스팟이 되는지” 피드백 추가
FeedItemResponse에 아래 필드 추가:
fundingGoal
fundedAmount
remainingAmount
remainingParticipantCount
프론트에서 “1명 더 필요”, “5,000P 더 필요” 같은 안내 가능.

피드가 스팟으로 전환됐는지 확인 가능하게 추가
신청 수락 API 응답 FeedApplicationResponse에:
spotConverted
spotId
전환 시 원본 FeedItem에도 spotId 보존 + MATCHED 상태 기록 후 soft delete.

오너 / 서포터 / 파트너 구분 추가
새 enum: SpotMemberRole
OWNER
SUPPORTER
PARTNER
ChatMemberResponse.role에 채팅방 멤버별 역할 노출.
SpotParticipantResponse.memberRole에 스팟 참여자별 역할 노출.
피드 신청 당시 SUPPORTER/PARTNER 역할을 스팟 전환 후에도 보존하도록 spot_participants.application_role 추가.

정산 상태 확인 API 추가
GET /api/spots/{spotId}/settlement
스팟 상세 SpotDetailResponse에도 최신 정산 상태 settlement 포함.
기존 정산 요청/승인 API는 유지:
POST /api/spots/{spotId}/settlement
POST /api/spots/{spotId}/settlement/approve

DB 마이그레이션 문서 추가
docs/migrations/2026-06-02_spot_participant_application_role.sql
spot_participants.application_role 컬럼 추가용.

검증 결과:

./gradlew compileJava testClasses :capstone-api:test --tests backend.feed.service.FeedItemServiceTest 성공
./gradlew bootJar 성공
./gradlew checkstyleMain 성공
다만 기존 코드의 checkstyle warning들은 남아 있음. 현재 프로젝트 설정상 ignoreFailures=true라 빌드는 성공.
./gradlew test 전체는 실패
원인: BackendApplicationTests.contextLoads()가 로컬 PostgreSQL 연결 실패로 깨짐.
에러: PSQLException / ConnectException
이번 변경 로직 문제는 아니고, 로컬 DB 미기동/미설정 이슈로 보입니다.

백엔드적으로 “없어서 못 만드는 것”은 하나만 있습니다:

만약 “정산 승인”이 단순 승인 기록이 아니라 실제 포인트 차감/지급/환불까지 의미한다면, 그건 아직 백엔드 정책/API가 부족합니다.
현재 settlement 코드는 합의 상태 기록만 하고, 포인트 이동은 하지 않습니다.
PointTransaction, PayService 같은 포인트 도메인 기본 요소는 있지만, 정산용 escrow/transfer/refund 정책이 명확히 없습니다.
그래서 프론트에서 “정산 요청/승인 상태 표시”는 이번 PR로 가능하지만, “승인 시 실제 포인트 이동”까지 하려면 별도 백엔드 설계가 필요합니다.

Summary by CodeRabbit

New Features

  • Chat members now display their assigned role information
  • Feed applications track spot conversion status and associated spot ID details
  • Feed items now show complete financing information: funding goal, funded amount, remaining amount, and remaining participant capacity
  • Spot settlement status information is now accessible via dedicated API endpoint
  • Spot participants display their assigned role information

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 1, 2026

Review Change Stack

Warning

Review limit reached

@seoJing, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 23 minutes and 59 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c7da217e-4dfd-48d5-9b4c-cb5b92277c00

📥 Commits

Reviewing files that changed from the base of the PR and between d3b29ff and f6da9c9.

📒 Files selected for processing (5)
  • capstone-api/src/main/java/backend/chat/service/ChatService.java
  • capstone-api/src/main/java/backend/feed/dto/FeedItemResponse.java
  • capstone-api/src/main/java/backend/spot/dto/SpotParticipantResponse.java
  • capstone-api/src/main/java/backend/spot/service/SpotService.java
  • capstone-domain/src/main/java/backend/spot/entity/SpotMemberRole.java
📝 Walkthrough

Walkthrough

PR adds role preservation across feed-to-spot conversions, exposing member roles in chat and spot participant API responses, and enables settlement state lookup. Role origins (application role from feed or spot participant role) are resolved and mapped to a new SpotMemberRole contract for consistent front-end display.

Changes

Spot member roles and settlement lookup

Layer / File(s) Summary
Role enum definition and persistence schema
capstone-domain/src/main/java/backend/spot/entity/SpotMemberRole.java, capstone-domain/src/main/java/backend/spot/entity/SpotParticipant.java, docs/migrations/2026-06-02_spot_participant_application_role.sql
New SpotMemberRole enum (OWNER, SUPPORTER, PARTNER) defines the role contract. SpotParticipant gains applicationRole field persisted to application_role column to retain original feed application role.
Feed-to-spot conversion flow
capstone-domain/src/main/java/backend/feed/entity/FeedItem.java, capstone-api/src/main/java/backend/feed/service/FeedItemService.java
FeedItem.convertToSpot(spotId) updates item status to MATCHED and marks as deleted. FeedItemService.acceptApplication persists the new Spot, converts the feed item, and registers participants with their application role preserved via SpotParticipant.applicationRole.
Spot participant role exposure in API
capstone-api/src/main/java/backend/spot/dto/SpotParticipantResponse.java
SpotParticipantResponse includes memberRole resolved by mapping AUTHOR→OWNER or converting applicationRole enum to SpotMemberRole.
Chat member role resolution
capstone-api/src/main/java/backend/chat/dto/ChatMemberResponse.java, capstone-api/src/main/java/backend/chat/service/ChatService.java
ChatService.getMembers enriches group chat member responses with SpotMemberRole by querying SpotParticipant for spot-based rooms or FeedItem authors and accepted applications for feed-based rooms. ChatMemberResponse includes nullable role field.
Feed application and item response enrichment
capstone-api/src/main/java/backend/feed/dto/FeedApplicationResponse.java, capstone-api/src/main/java/backend/feed/dto/FeedItemResponse.java
FeedApplicationResponse adds spotConverted flag and spotId to track conversion outcome. FeedItemResponse adds financing fields (fundingGoal, fundedAmount, remainingAmount, remainingParticipantCount) with helper methods for remaining calculations.
Settlement lookup API
capstone-api/src/main/java/backend/spot/controller/SpotController.java, capstone-api/src/main/java/backend/spot/service/SpotService.java, capstone-api/src/main/java/backend/spot/dto/SpotDetailResponse.java
New GET /api/v1/spots/{spotId}/settlement endpoint and SpotService.getSettlement(spotId, userId) method retrieve latest settlement with participant authorization. SpotDetailResponse includes settlement field populated in detail view.
Test validation
capstone-api/src/test/java/backend/feed/service/FeedItemServiceTest.java
Tests capture and verify spot participant records, assert feed item status/spotId transitions post-conversion, validate application role preservation on saved participants, and confirm spot conversion metadata in response.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • spot-platform/backend#101: Extends spot settlement feature with SpotSettlementResponse wiring in SpotController/SpotService/SpotDetailResponse, building on similar settlement infrastructure.
  • spot-platform/backend#21: Introduced ChatRoomMember membership infrastructure that this PR builds on to attach SpotMemberRole to chat members.
  • spot-platform/backend#32: Related SpotController API expansion with new spot endpoints; this PR adds /{spotId}/settlement to the same routing layer.

Suggested labels

feature

Suggested reviewers

  • hoTan35
  • ca5tlechan

🐰 A feed becomes a spot, roles bloom with care,
OWNER, SUPPORTER, PARTNER—each name to share,
Chat rooms now know who's who from the start,
Settlement shows the balance of heart,
Hop toward frontend clarity! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.71% 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
Title check ✅ Passed The PR title 'feat: expose spot roles and settlement status' clearly summarizes the main changes: exposing roles and settlement status as core additions.
Description check ✅ Passed The PR description is comprehensive and includes all required template sections: Summary, Describe your changes (with detailed Korean notes), Verification steps, and Issue number (#122).
Linked Issues check ✅ Passed All objectives from issue #122 are met: application role is preserved on spot participants [FeedItemService, SpotParticipant]; OWNER/SUPPORTER/PARTNER roles are exposed [SpotMemberRole enum, ChatMemberResponse.role, SpotParticipantResponse.memberRole]; settlement lookup is provided [SpotService.getSettlement, SpotController endpoint, SpotDetailResponse]; and verification checks are performed [tests pass, compilation and checkstyle succeed].
Out of Scope Changes check ✅ Passed All changes are scoped to objectives in #122: enum/DTO additions for roles, settlement endpoints/responses, FeedItem conversion logic, and feed funding metadata—all directly supporting role exposure and settlement lookup requirements.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/spot-member-roles-settlement-lookup-122

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 and usage tips.

@seoJing
Copy link
Copy Markdown
Member Author

seoJing commented Jun 1, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 1, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (3)
capstone-api/src/main/java/backend/chat/service/ChatService.java (2)

286-294: 💤 Low value

Role-resolution logic duplicated across modules.

resolveSpotMemberRole is identical to SpotParticipantResponse.resolveMemberRole. Consider extracting a single resolver (e.g. a static method on SpotMemberRole or SpotParticipant) so both call sites stay in sync. Optional given module boundaries.

🤖 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 `@capstone-api/src/main/java/backend/chat/service/ChatService.java` around
lines 286 - 294, The role-resolution logic in resolveSpotMemberRole duplicates
SpotParticipantResponse.resolveMemberRole; extract a single shared resolver
(e.g., a static method on SpotMemberRole like
SpotMemberRole.fromParticipant(SpotParticipant) or a static helper on
SpotParticipant) and replace both resolveSpotMemberRole and
SpotParticipantResponse.resolveMemberRole to call that new method; ensure it
handles ParticipantRole.AUTHOR -> OWNER, null applicationRole -> null, and
mapping via valueOf(applicationRole.name()) so both call sites remain in sync.

296-305: 💤 Low value

Duplicate parse helper — reuse existing parseSpotId.

parseLongOrNull is functionally identical to parseSpotId (Lines 984-993): both null/blank-guard and swallow NumberFormatException returning null. Drop one to avoid divergence.

♻️ Reuse existing helper
-	private Long parseLongOrNull(String value) {
-		if (value == null || value.isBlank()) {
-			return null;
-		}
-		try {
-			return Long.valueOf(value);
-		} catch (NumberFormatException e) {
-			return null;
-		}
-	}
-

Then call parseSpotId(...) at Lines 258 and 270.

🤖 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 `@capstone-api/src/main/java/backend/chat/service/ChatService.java` around
lines 296 - 305, Remove the duplicate private helper parseLongOrNull and instead
call the existing parseSpotId helper; delete the parseLongOrNull method
definition and replace every call site that used parseLongOrNull with
parseSpotId (ensure the argument types match), keeping the null/blank guard and
NumberFormatException swallowing behavior provided by parseSpotId. Update
ChatService so all former parseLongOrNull usages now reference parseSpotId and
remove the unused method to avoid divergence.
capstone-api/src/main/java/backend/spot/dto/SpotParticipantResponse.java (1)

68-76: ⚡ Quick win

Brittle enum coupling via valueOf can throw at runtime.

SpotMemberRole.valueOf(participant.getApplicationRole().name()) will throw IllegalArgumentException if the application-role enum ever holds a constant name (e.g. a future role) that has no matching SpotMemberRole. It's safe only while both enums share identical names. Consider an explicit mapping that falls back to null/PARTNER instead of relying on name identity.

The same pattern is used in ChatService (Lines 282, 293).

Please confirm the applicationRole type and that all its constant names exist in SpotMemberRole:

#!/bin/bash
# Resolve SpotParticipant.applicationRole type and its enum constants
ast-grep --pattern 'private $TYPE applicationRole;'
fd -e java | rg -l 'SpotParticipant' | head
# FeedApplicationRole constants
fd -i 'FeedApplicationRole.java' --exec cat {}
# SpotMemberRole constants
fd -i 'SpotMemberRole.java' --exec cat {}
🤖 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 `@capstone-api/src/main/java/backend/spot/dto/SpotParticipantResponse.java`
around lines 68 - 76, The resolveMemberRole method is brittle because
SpotMemberRole.valueOf(participant.getApplicationRole().name()) can throw
IllegalArgumentException if enum names diverge; replace the name-based
conversion with an explicit mapping (e.g., switch or map) from the
applicationRole enum values to SpotMemberRole values and return a safe fallback
(null or SpotMemberRole.PARTNER) for unmapped cases; update the same fragile
conversions in ChatService (the occurrences around lines 282 and 293) to use the
same explicit mapping method or a shared helper to avoid runtime exceptions.
🤖 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 `@capstone-api/src/main/java/backend/feed/dto/FeedItemResponse.java`:
- Around line 239-246: In calculateRemainingParticipantCount, avoid possible
integer overflow in the ceiling division by performing the computation in long
like calculateProgressPercent does: convert remainingAmount and price to long,
compute (remaining + price - 1) / price using long arithmetic, then cast the
result back to Integer (or return null/handle if result exceeds
Integer.MAX_VALUE) before returning; update the logic in
calculateRemainingParticipantCount to use these long temporaries to ensure
correctness for very large Integer inputs.

In `@capstone-api/src/main/java/backend/spot/service/SpotService.java`:
- Around line 805-811: In getSettlement, validate spot existence before checking
participant membership: call validateSpotExists(spotId,
ErrorCode.SPOT_NOT_FOUND) at the start of SpotService.getSettlement (before
calling validateParticipant) so a missing spot yields SPOT_NOT_FOUND (404)
instead of NOT_SPOT_PARTICIPANT; mirror the order used in approveSettlement to
ensure consistent error semantics.
- Around line 253-257: getSpot currently always loads the latest settlement and
exposes it to unauthenticated callers; change it to only include settlement when
the caller is an owner/participant by reusing the existing access checks: call
validateSpotExists(spotId) if not already done, then call
validateParticipant(spotId, currentUserId) or delegate to getSettlement(spotId,
currentUserId) to obtain the SpotSettlementResponse so both paths share the same
authorization logic; additionally, update getSettlement to first call
validateSpotExists(spotId) so missing spots return SPOT_NOT_FOUND consistently.

---

Nitpick comments:
In `@capstone-api/src/main/java/backend/chat/service/ChatService.java`:
- Around line 286-294: The role-resolution logic in resolveSpotMemberRole
duplicates SpotParticipantResponse.resolveMemberRole; extract a single shared
resolver (e.g., a static method on SpotMemberRole like
SpotMemberRole.fromParticipant(SpotParticipant) or a static helper on
SpotParticipant) and replace both resolveSpotMemberRole and
SpotParticipantResponse.resolveMemberRole to call that new method; ensure it
handles ParticipantRole.AUTHOR -> OWNER, null applicationRole -> null, and
mapping via valueOf(applicationRole.name()) so both call sites remain in sync.
- Around line 296-305: Remove the duplicate private helper parseLongOrNull and
instead call the existing parseSpotId helper; delete the parseLongOrNull method
definition and replace every call site that used parseLongOrNull with
parseSpotId (ensure the argument types match), keeping the null/blank guard and
NumberFormatException swallowing behavior provided by parseSpotId. Update
ChatService so all former parseLongOrNull usages now reference parseSpotId and
remove the unused method to avoid divergence.

In `@capstone-api/src/main/java/backend/spot/dto/SpotParticipantResponse.java`:
- Around line 68-76: The resolveMemberRole method is brittle because
SpotMemberRole.valueOf(participant.getApplicationRole().name()) can throw
IllegalArgumentException if enum names diverge; replace the name-based
conversion with an explicit mapping (e.g., switch or map) from the
applicationRole enum values to SpotMemberRole values and return a safe fallback
(null or SpotMemberRole.PARTNER) for unmapped cases; update the same fragile
conversions in ChatService (the occurrences around lines 282 and 293) to use the
same explicit mapping method or a shared helper to avoid runtime exceptions.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: a2a20510-a651-45d2-8248-ebc5cc542585

📥 Commits

Reviewing files that changed from the base of the PR and between 1d0463b and d3b29ff.

📒 Files selected for processing (15)
  • capstone-api/src/main/java/backend/chat/dto/ChatMemberResponse.java
  • capstone-api/src/main/java/backend/chat/service/ChatService.java
  • capstone-api/src/main/java/backend/feed/dto/FeedApplicationResponse.java
  • capstone-api/src/main/java/backend/feed/dto/FeedItemResponse.java
  • capstone-api/src/main/java/backend/feed/service/FeedItemService.java
  • capstone-api/src/main/java/backend/spot/controller/SpotController.java
  • capstone-api/src/main/java/backend/spot/dto/SpotDetailResponse.java
  • capstone-api/src/main/java/backend/spot/dto/SpotParticipantResponse.java
  • capstone-api/src/main/java/backend/spot/service/SpotService.java
  • capstone-api/src/test/java/backend/feed/service/FeedItemServiceTest.java
  • capstone-domain/src/main/java/backend/feed/entity/FeedItem.java
  • capstone-domain/src/main/java/backend/spot/entity/Spot.java
  • capstone-domain/src/main/java/backend/spot/entity/SpotMemberRole.java
  • capstone-domain/src/main/java/backend/spot/entity/SpotParticipant.java
  • docs/migrations/2026-06-02_spot_participant_application_role.sql

Comment thread capstone-api/src/main/java/backend/feed/dto/FeedItemResponse.java
Comment thread capstone-api/src/main/java/backend/spot/service/SpotService.java Outdated
Comment thread capstone-api/src/main/java/backend/spot/service/SpotService.java
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.

feat: expose spot member roles and settlement lookup

1 participant