17 KiB
17 KiB
관리자 에이전트 정산 상세 조회 설계
문서 목적
- 관리자 페이지에서 에이전트 목록을 보고, 특정 에이전트 상세 화면에서 소속 크리에이터와 에이전트별 정산 현황을 조회할 수 있도록 백엔드 read API 설계를 고정한다.
- 기존
partner/agent/calculate계산 로직은 최대한 재사용하고,ADMIN전용 조회 진입점만 별도로 추가한다.
요구사항 정리
- 관리자 화면에는 에이전트 리스트가 필요하다.
- 에이전트 닉네임
- 에이전트에 속한 크리에이터 수
- 관리자 화면에는 크리에이터 검색이 필요하다.
- 특정 에이전트에 크리에이터를 소속시키기 위한 검색
- 관리자 화면에는 특정 에이전트에 현재 소속된 크리에이터 목록이 필요하다.
- 에이전트 소속 해제를 위한 목록
- 관리자 화면에는 특정 에이전트 기준 정산 상세가 필요하다.
- 라이브 정산 현황
- 콘텐츠 판매 정산 현황
- 커뮤니티 정산 현황
- 채널 후원 정산 현황
- 콘텐츠 후원 정산 현황
범위와 해석
- 이번 설계는 B안: 에이전트 목록 → 에이전트 상세 흐름을 기준으로 한다.
- 에이전트 목록 화면에서는 요약 정보만 제공한다.
- 실제 정산 데이터는 에이전트 상세 화면에서 조회한다.
- 기존
assignment,ratio,settlement/finalize쓰기 기능은 유지하고, 이번 범위에서는 관리자용 read API만 추가한다.
현재 코드 기준 확인 사항
- 관리자 전용 에이전트 관련 컨트롤러는 이미 존재한다.
src/main/kotlin/kr/co/vividnext/sodalive/admin/partner/agent/assignment/AdminAgentCreatorController.ktsrc/main/kotlin/kr/co/vividnext/sodalive/admin/partner/agent/ratio/AdminAgentSettlementRatioController.ktsrc/main/kotlin/kr/co/vividnext/sodalive/admin/partner/agent/settlement/AdminAgentSettlementSnapshotController.kt
- 에이전트 본인 전용 정산 조회 컨트롤러도 이미 존재한다.
src/main/kotlin/kr/co/vividnext/sodalive/partner/agent/calculate/AgentCalculateController.kt
- 따라서 현재 부족한 것은 정산 계산 로직이 아니라,
ADMIN이 특정agentId를 지정해 같은 데이터를 읽는 관리자 전용 read API 레이어다.
권장 아키텍처
1. 관리자 전용 read 진입점 추가
- 신규 패키지:
kr.co.vividnext.sodalive.admin.partner.agent.read - 신규 구성
AdminAgentReadControllerAdminAgentReadServiceAdminAgentReadQueryRepository
- 역할
- 에이전트 목록 조회
- 크리에이터 검색 조회
- 특정 에이전트 소속 크리에이터 목록 조회
- 특정 에이전트 기준 5종 정산 조회
2. 기존 도메인 계산 로직 재사용
- 기존 계산/집계 로직은 계속
kr.co.vividnext.sodalive.partner.agent.calculate아래에 둔다. - 관리자용 read 서비스는 기존
AgentCalculateService,AgentCalculateQueryRepository, snapshot 조회 경로를 재사용한다. - 현재
AgentCalculateService의 정산 조회 메서드는 이미agentId를 직접 받으므로, 관리자 read 서비스는 이 메서드를 그대로 호출한다. - 최종안은 정산 계산 로직은
partner.agent.calculate에 유지하고, ADMIN은 별도 read 서비스에서 기존 agentId 기반 조회 메서드를 재사용하는 구조다.
3. 쓰기와 읽기 축 분리
- 쓰기 축
admin.partner.agent.assignmentadmin.partner.agent.ratioadmin.partner.agent.settlement
- 읽기 축
admin.partner.agent.readpartner.agent.calculate
- 이렇게 나누면 “관리자 권한으로 읽는다”와 “정산 계산 규칙을 제공한다”의 책임이 섞이지 않는다.
화면 흐름 기준 API 설계
1. 에이전트 목록 API
- 목적: 관리자 화면의 첫 진입 리스트
- 권장 경로:
GET /admin/partner/agent/list - 권한:
ADMIN - 요청 파라미터
pageable
- 응답 필드
agentIdagentNicknameassignedCreatorCountliveAgentSettlementAmountcontentAgentSettlementAmountcommunityAgentSettlementAmountcontentDonationAgentSettlementAmountchannelDonationAgentSettlementAmounttotalCount
- 조회 기준
Member.role == AGENT- 현재 활성 소속 크리에이터 수만 집계
- 활성 소속 판정은 현재 코드의 assignment window 규칙과 동일하게 현재 시각 기준
assignedAt <= now < unassignedAt(or null)를 따른다. - 정산 합계는 별도 날짜 입력 없이 현재 월 기준으로 계산한다.
- 기준 시간대:
Asia/Seoul - 시작 시각: 현재 월 1일
00:00:00 - 종료 시각: 다음 달 1일
00:00:00직전까지 포함되는 배타 상한 방식 - 실제 조회 구간은 위 KST 월 경계를 UTC
LocalDateTime으로 변환해 DB의 UTC 저장 시각과 비교한다.
- 기준 시간대:
- 각 합계 값의 의미는 해당 기간의 상세 조회 응답
total.agentSettlementAmount와 동일하다. - 해당 월에 정산 내역이 없더라도 에이전트 목록에서는 제외하지 않고, 5종 합계를 모두
0으로 내려준다.
2. 크리에이터 검색 API
- 목적: 특정 에이전트에 크리에이터를 소속시키기 전 검색
- 권장 경로:
GET /admin/partner/agent/creator/search - 권한:
ADMIN - 요청 파라미터
search_wordpageable
- 응답 필드
creatorIdcreatorNicknamecurrentAgentIdnullablecurrentAgentNicknamenullable
- 동작 원칙
- 기존
AdminMemberController.searchCreator()검색 관례를 따른다. - 검색 결과에 현재 활성 소속 agent 정보를 붙여서, 이미 다른 agent 소속인지 운영자가 바로 판단할 수 있게 한다.
- 기존
3. 특정 에이전트 소속 크리에이터 목록 API
- 목적: 상세 화면의 소속 크리에이터 탭
- 권장 경로:
GET /admin/partner/agent/{agentId}/creator/list - 권한:
ADMIN - 응답 필드
creatorIdcreatorNicknameassignedAt
- 참고
- 현재
GetAgentAssignedCreatorResponse는creatorId,creatorNickname만 제공한다. - 관리자 상세 화면에서는 운영 판단을 위해
assignedAt이 같이 내려가는 편이 자연스럽다.
- 현재
4. 특정 에이전트 정산 상세 API 5종
- 목적: 에이전트 상세 화면의 정산 탭
- 권한:
ADMIN - 권장 경로
GET /admin/partner/agent/{agentId}/calculate/live-by-creatorGET /admin/partner/agent/{agentId}/calculate/content-by-creatorGET /admin/partner/agent/{agentId}/calculate/community-by-creatorGET /admin/partner/agent/{agentId}/calculate/channel-donation-by-creatorGET /admin/partner/agent/{agentId}/calculate/content-donation-by-creator
- 공통 요청 파라미터
startDateStrendDateStrpageable
- 응답 원칙
- 기존 AGENT 전용 응답 계약을 가능한 그대로 유지한다.
totalCount,total,items구조 유지- 각 item의 집계 필드 유지
counttotalCankrwfeesettlementAmountagentSettlementAmount
- 차이점
- 기존 AGENT API는 로그인 principal에서
agentId를 얻는다. - 관리자 API는 path variable
agentId를 받는다.
- 기존 AGENT API는 로그인 principal에서
데이터 모델/응답 설계
1. 에이전트 목록 응답 DTO
- 신규 DTO 필요
- DTO 명
GetAdminAgentListResponseGetAdminAgentListItem
- 필수 필드
- response:
totalCount: Int,items: List<GetAdminAgentListItem> - item:
agentId: LongagentNickname: StringassignedCreatorCount: IntliveAgentSettlementAmount: IntcontentAgentSettlementAmount: IntcommunityAgentSettlementAmount: IntcontentDonationAgentSettlementAmount: IntchannelDonationAgentSettlementAmount: Int
- response:
2. 크리에이터 검색 응답 DTO
- 신규 DTO 필요
- 기존
admin/member검색 응답을 그대로 쓰기보다, 현재 활성 agent 정보를 함께 주는 전용 DTO가 필요하다. - DTO 명
SearchAdminAgentAssignableCreatorResponseSearchAdminAgentAssignableCreatorItem
- 필수 필드
- response:
totalCount: Int,items: List<SearchAdminAgentAssignableCreatorItem> - item:
creatorId: Long,creatorNickname: String,currentAgentId: Long?,currentAgentNickname: String?
- response:
3. 관리자용 소속 크리에이터 목록 DTO
- 기존
GetAgentAssignedCreatorResponse를 그대로 재사용하기보다는 관리자용 항목에assignedAt을 포함한 전용 DTO가 적합하다. - DTO 명
GetAdminAgentAssignedCreatorResponseGetAdminAgentAssignedCreatorItem
- 필수 필드
- response:
totalCount: Int,items: List<GetAdminAgentAssignedCreatorItem> - item:
creatorId: Long,creatorNickname: String,assignedAt: LocalDateTime
- response:
4. 관리자용 정산 상세 DTO
- 가능하면 기존 아래 DTO를 그대로 재사용한다.
GetAgentSettlementByCreatorResponseGetAgentChannelDonationSettlementByCreatorResponse
- 이유
- 화면 주체만 ADMIN으로 바뀌고 데이터 shape는 동일하기 때문이다.
- DTO까지 갈라지면 정산 계약이 중복될 가능성이 높다.
서비스 설계
1. AdminAgentReadService
- 책임
- 에이전트 목록 조회
- 크리에이터 검색 조회
- 에이전트 소속 크리에이터 목록 조회
- 에이전트 정산 상세 조회 진입
- 내부 동작
- 목록/검색/소속 목록은 전용 read query를 사용한다.
- 에이전트 목록 조회 시 현재 월 기준 시작/종료 시각을 서비스에서 계산해 전용 read query에 전달한다.
- 정산 상세는 기존
AgentCalculateService의 agentId 기반 public 조회 메서드를 사용한다.
2. 공통 정산 read 메서드 분리
- 현재
AgentCalculateService의 핵심 정산 조회 메서드는 이미agentId파라미터 중심 public 메서드다. - 따라서 아래 방향으로 정리한다.
- controller는 AGENT/ADMIN 별도로 유지
- ADMIN read 서비스는 기존
AgentCalculateServicepublic 메서드를 직접 호출한다.
- 기대 효과
- 정산 계산 규칙 중복 제거
- snapshot 우선 조회 / live 계산 fallback 규칙 재사용
Repository / Query 방향
1. 에이전트 목록용 조회 추가
- 필요 기능
- AGENT role member 목록
- 각 agent의 현재 활성 creator count
- 각 agent의 현재 월 기준 5종 정산 합계
- 구현 방식
AdminAgentReadQueryRepository에서 전용 projection query를 제공한다.- 기본 에이전트 목록과 활성 creator count는 기존 Querydsl projection query로 조회한다.
- 현재 월 5종 정산 합계는 각 목록 item마다 기존
AgentCalculateQueryRepositorytotal 조회 메서드를 재사용해 채운다. - 정산 row가 없는 agent도 목록에 남겨야 하므로 기존 total 조회 응답의
0기본값을 그대로 사용한다.
2. 크리에이터 검색용 조회 추가
- 필요 기능
- creator nickname 기준 검색
- 현재 활성 소속 agent nullable join
- 구현 방식
AdminAgentReadQueryRepository에서 전용 검색 query를 제공한다.
3. 소속 크리에이터 목록 조회 추가
- 기존
AgentCalculateQueryRepository.getAssignedCreators()를 재사용할 수 있다. - 다만
assignedAt을 응답에 내려야 하면 projection을 확장하거나 관리자 전용 projection을 추가해야 한다.
인증/예외 처리 원칙
- 새 관리자 read 엔드포인트는 모두
@PreAuthorize("hasRole('ADMIN')")를 사용한다. agentId가 존재하지 않거나 role이AGENT가 아니면SodaException(messageKey = ...)로 실패한다.creator/search는 기존admin/member/search관례처럼 최소 검색어 길이 검증을 적용한다.- 정산 계산식, snapshot 우선 전략, assignment/ratio history 적용 규칙은 기존
partner.agent.calculate동작을 그대로 사용한다.
테스트 설계
1. 컨트롤러 테스트
ADMIN접근 성공- 익명 사용자 접근 실패
AGENT또는 일반 사용자 접근 실패
2. 서비스/리포지토리 테스트
- 에이전트 목록에서 닉네임과 현재 활성 creator count가 맞는지 검증
- 크리에이터 검색 결과에 현재 agent 소속 정보가 올바르게 붙는지 검증
- 특정 agent 소속 크리에이터 목록이 현재 활성 구간 기준으로만 내려오는지 검증
3. 정산 parity 테스트
- 동일 기간, 동일
agentId에 대해- AGENT 전용 조회 응답
- ADMIN 전용 조회 응답
- 두 결과의
totalCount,total,items가 동일한지 검증
- 대상 5종
- live
- content
- community
- channel donation
- content donation
4. 설계 대비 구현 계획 누락 체크리스트
- 관리자 컨트롤러 테스트에
ADMIN접근 성공, 익명 접근 실패,AGENT또는 일반 사용자 접근 실패 시나리오가 모두 포함되어 있는지 확인한다. - 동일 기간, 동일
agentId기준으로 AGENT 전용 응답과 ADMIN 전용 응답의totalCount,total,itemsparity를 5종 모두 검증하는 테스트가 구현 계획에 포함되어 있는지 확인한다. - 구현 계획의 검증 기록 단계에
무엇을/왜/어떻게, 실제 실행 명령과 결과, 후속 수정 시 누적 기록 및정정추가 원칙이 명시되어 있는지 확인한다. /admin/partner/agent/list가 별도 날짜 입력 없이 현재 월 기준 5종 정산 합계를 계산하도록 구현 계획에 반영되어 있는지 확인한다.- 에이전트 목록 응답 DTO와 리스트 조회 테스트에
live/content/community/contentDonation/channelDonation5종agentSettlementAmountsummary 필드가 모두 반영되어 있는지 확인한다. - 해당 월에 정산 내역이 없는 에이전트도 목록에서 제외하지 않고 5종 합계를
0으로 표기하는 쿼리/응답/테스트 계획이 포함되어 있는지 확인한다. - 리스트 summary 값이 같은 월 기준 상세 조회 응답의
total.agentSettlementAmount와 일치하는지 검증하는 회귀 테스트가 구현 계획에 포함되어 있는지 확인한다.
구현 시 유의사항
- 관리자용 정산 API를 새로 만든다고 해서 계산 query를 복제하지 않는다.
- 현재 assignment 활성 판정은 시간창 규칙을 반드시 동일하게 사용한다.
- 정산 응답 계약은 기존 agent 응답과 불필요하게 분기하지 않는다.
- 목록 화면은 요약 정보만 제공하고, 상세 정산은 상세 화면에서만 조회한다.
- 목록의 5종 합계는 상세 API의 기간 필터와 별개로, 현재 월 기준 요약값이라는 의미를 문서와 코드에서 동일하게 유지한다.
- 해당 월에 정산 내역이 없더라도 에이전트 행은 유지하고 금액은
0으로 표기한다.
최종 설계 결론
- 이번 기능은 “새 정산 엔진 추가”가 아니라 “기존 agent 정산 계산을 ADMIN에서 조회할 수 있게 read API를 보강”하는 작업으로 본다.
- 관리자 페이지 흐름은 아래로 고정한다.
/admin/partner/agent/list로 에이전트 목록과 현재 월 기준 5종 정산 합계 조회- 상세 화면에서
/admin/partner/agent/{agentId}/creator/list로 현재 소속 크리에이터 조회 - 필요 시
/admin/partner/agent/creator/search로 크리에이터 검색 후 기존 assignment API로 소속 지정 - 상세 화면에서
/admin/partner/agent/{agentId}/calculate/*5종으로 정산 현황 조회
- 목록의 5종 합계는 상세 페이지 이동 포인트용 현재 월 summary이며, 상세 화면의 날짜 범위 조회와 역할을 구분한다.
- 현재 월에 정산 내역이 없는 에이전트도 목록에 포함되며, 합계는 0원으로 표시한다.
- 이 설계를 기준으로 다음 단계에서는 구현 계획 문서를 작성한다.
검증 기록
- 1차 설계
- 무엇을: 관리자용 에이전트 목록/상세 read API 구조와 기존 agent 계산 로직 재사용 범위를 문서로 고정했다.
- 왜: 현재 코드에는 관리자용 assignment/ratio/finalize는 있지만, 관리자용 agent 정산 상세 조회 API는 없어 read 레이어 설계가 먼저 필요했기 때문이다.
- 어떻게:
src/main/kotlin/kr/co/vividnext/sodalive/admin/partner/agent/assignment/AdminAgentCreatorController.kt확인src/main/kotlin/kr/co/vividnext/sodalive/admin/partner/agent/ratio/AdminAgentSettlementRatioController.kt확인src/main/kotlin/kr/co/vividnext/sodalive/admin/partner/agent/settlement/AdminAgentSettlementSnapshotController.kt확인src/main/kotlin/kr/co/vividnext/sodalive/partner/agent/calculate/AgentCalculateController.kt확인src/main/kotlin/kr/co/vividnext/sodalive/partner/agent/calculate/AgentCalculateService.kt확인src/main/kotlin/kr/co/vividnext/sodalive/admin/member/AdminMemberController.kt확인- 결과: 관리자용 read API 부재와 기존 계산 로직 재사용 가능성을 문서에 반영했다.