Files
sodalive-backend-spring-boot/docs/20260410_관리자에이전트정산상세조회설계.md

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.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
  • 따라서 현재 부족한 것은 정산 계산 로직이 아니라, ADMIN이 특정 agentId를 지정해 같은 데이터를 읽는 관리자 전용 read API 레이어다.

권장 아키텍처

1. 관리자 전용 read 진입점 추가

  • 신규 패키지: kr.co.vividnext.sodalive.admin.partner.agent.read
  • 신규 구성
    • AdminAgentReadController
    • AdminAgentReadService
    • AdminAgentReadQueryRepository
  • 역할
    • 에이전트 목록 조회
    • 크리에이터 검색 조회
    • 특정 에이전트 소속 크리에이터 목록 조회
    • 특정 에이전트 기준 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.assignment
    • admin.partner.agent.ratio
    • admin.partner.agent.settlement
  • 읽기 축
    • admin.partner.agent.read
    • partner.agent.calculate
  • 이렇게 나누면 “관리자 권한으로 읽는다”와 “정산 계산 규칙을 제공한다”의 책임이 섞이지 않는다.

화면 흐름 기준 API 설계

1. 에이전트 목록 API

  • 목적: 관리자 화면의 첫 진입 리스트
  • 권장 경로: GET /admin/partner/agent/list
  • 권한: ADMIN
  • 요청 파라미터
    • pageable
  • 응답 필드
    • agentId
    • agentNickname
    • assignedCreatorCount
    • liveAgentSettlementAmount
    • contentAgentSettlementAmount
    • communityAgentSettlementAmount
    • contentDonationAgentSettlementAmount
    • channelDonationAgentSettlementAmount
    • totalCount
  • 조회 기준
    • 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_word
    • pageable
  • 응답 필드
    • creatorId
    • creatorNickname
    • currentAgentId nullable
    • currentAgentNickname nullable
  • 동작 원칙
    • 기존 AdminMemberController.searchCreator() 검색 관례를 따른다.
    • 검색 결과에 현재 활성 소속 agent 정보를 붙여서, 이미 다른 agent 소속인지 운영자가 바로 판단할 수 있게 한다.

3. 특정 에이전트 소속 크리에이터 목록 API

  • 목적: 상세 화면의 소속 크리에이터 탭
  • 권장 경로: GET /admin/partner/agent/{agentId}/creator/list
  • 권한: ADMIN
  • 응답 필드
    • creatorId
    • creatorNickname
    • assignedAt
  • 참고
    • 현재 GetAgentAssignedCreatorResponsecreatorId, creatorNickname만 제공한다.
    • 관리자 상세 화면에서는 운영 판단을 위해 assignedAt이 같이 내려가는 편이 자연스럽다.

4. 특정 에이전트 정산 상세 API 5종

  • 목적: 에이전트 상세 화면의 정산 탭
  • 권한: ADMIN
  • 권장 경로
    • GET /admin/partner/agent/{agentId}/calculate/live-by-creator
    • GET /admin/partner/agent/{agentId}/calculate/content-by-creator
    • GET /admin/partner/agent/{agentId}/calculate/community-by-creator
    • GET /admin/partner/agent/{agentId}/calculate/channel-donation-by-creator
    • GET /admin/partner/agent/{agentId}/calculate/content-donation-by-creator
  • 공통 요청 파라미터
    • startDateStr
    • endDateStr
    • pageable
  • 응답 원칙
    • 기존 AGENT 전용 응답 계약을 가능한 그대로 유지한다.
    • totalCount, total, items 구조 유지
    • 각 item의 집계 필드 유지
      • count
      • totalCan
      • krw
      • fee
      • settlementAmount
      • agentSettlementAmount
  • 차이점
    • 기존 AGENT API는 로그인 principal에서 agentId를 얻는다.
    • 관리자 API는 path variable agentId를 받는다.

데이터 모델/응답 설계

1. 에이전트 목록 응답 DTO

  • 신규 DTO 필요
  • DTO 명
    • GetAdminAgentListResponse
    • GetAdminAgentListItem
  • 필수 필드
    • response: totalCount: Int, items: List<GetAdminAgentListItem>
    • item:
      • agentId: Long
      • agentNickname: String
      • assignedCreatorCount: Int
      • liveAgentSettlementAmount: Int
      • contentAgentSettlementAmount: Int
      • communityAgentSettlementAmount: Int
      • contentDonationAgentSettlementAmount: Int
      • channelDonationAgentSettlementAmount: Int

2. 크리에이터 검색 응답 DTO

  • 신규 DTO 필요
  • 기존 admin/member 검색 응답을 그대로 쓰기보다, 현재 활성 agent 정보를 함께 주는 전용 DTO가 필요하다.
  • DTO 명
    • SearchAdminAgentAssignableCreatorResponse
    • SearchAdminAgentAssignableCreatorItem
  • 필수 필드
    • response: totalCount: Int, items: List<SearchAdminAgentAssignableCreatorItem>
    • item: creatorId: Long, creatorNickname: String, currentAgentId: Long?, currentAgentNickname: String?

3. 관리자용 소속 크리에이터 목록 DTO

  • 기존 GetAgentAssignedCreatorResponse를 그대로 재사용하기보다는 관리자용 항목에 assignedAt을 포함한 전용 DTO가 적합하다.
  • DTO 명
    • GetAdminAgentAssignedCreatorResponse
    • GetAdminAgentAssignedCreatorItem
  • 필수 필드
    • response: totalCount: Int, items: List<GetAdminAgentAssignedCreatorItem>
    • item: creatorId: Long, creatorNickname: String, assignedAt: LocalDateTime

4. 관리자용 정산 상세 DTO

  • 가능하면 기존 아래 DTO를 그대로 재사용한다.
    • GetAgentSettlementByCreatorResponse
    • GetAgentChannelDonationSettlementByCreatorResponse
  • 이유
    • 화면 주체만 ADMIN으로 바뀌고 데이터 shape는 동일하기 때문이다.
    • DTO까지 갈라지면 정산 계약이 중복될 가능성이 높다.

서비스 설계

1. AdminAgentReadService

  • 책임
    • 에이전트 목록 조회
    • 크리에이터 검색 조회
    • 에이전트 소속 크리에이터 목록 조회
    • 에이전트 정산 상세 조회 진입
  • 내부 동작
    • 목록/검색/소속 목록은 전용 read query를 사용한다.
    • 에이전트 목록 조회 시 현재 월 기준 시작/종료 시각을 서비스에서 계산해 전용 read query에 전달한다.
    • 정산 상세는 기존 AgentCalculateService의 agentId 기반 public 조회 메서드를 사용한다.

2. 공통 정산 read 메서드 분리

  • 현재 AgentCalculateService의 핵심 정산 조회 메서드는 이미 agentId 파라미터 중심 public 메서드다.
  • 따라서 아래 방향으로 정리한다.
    • controller는 AGENT/ADMIN 별도로 유지
    • ADMIN read 서비스는 기존 AgentCalculateService public 메서드를 직접 호출한다.
  • 기대 효과
    • 정산 계산 규칙 중복 제거
    • snapshot 우선 조회 / live 계산 fallback 규칙 재사용

Repository / Query 방향

1. 에이전트 목록용 조회 추가

  • 필요 기능
    • AGENT role member 목록
    • 각 agent의 현재 활성 creator count
    • 각 agent의 현재 월 기준 5종 정산 합계
  • 구현 방식
    • AdminAgentReadQueryRepository에서 전용 projection query를 제공한다.
    • 기본 에이전트 목록과 활성 creator count는 기존 Querydsl projection query로 조회한다.
    • 현재 월 5종 정산 합계는 각 목록 item마다 기존 AgentCalculateQueryRepository total 조회 메서드를 재사용해 채운다.
    • 정산 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, items parity를 5종 모두 검증하는 테스트가 구현 계획에 포함되어 있는지 확인한다.
  • 구현 계획의 검증 기록 단계에 무엇을/왜/어떻게, 실제 실행 명령과 결과, 후속 수정 시 누적 기록 및 정정 추가 원칙이 명시되어 있는지 확인한다.
  • /admin/partner/agent/list가 별도 날짜 입력 없이 현재 월 기준 5종 정산 합계를 계산하도록 구현 계획에 반영되어 있는지 확인한다.
  • 에이전트 목록 응답 DTO와 리스트 조회 테스트에 live/content/community/contentDonation/channelDonation 5종 agentSettlementAmount summary 필드가 모두 반영되어 있는지 확인한다.
  • 해당 월에 정산 내역이 없는 에이전트도 목록에서 제외하지 않고 5종 합계를 0으로 표기하는 쿼리/응답/테스트 계획이 포함되어 있는지 확인한다.
  • 리스트 summary 값이 같은 월 기준 상세 조회 응답의 total.agentSettlementAmount와 일치하는지 검증하는 회귀 테스트가 구현 계획에 포함되어 있는지 확인한다.

구현 시 유의사항

  • 관리자용 정산 API를 새로 만든다고 해서 계산 query를 복제하지 않는다.
  • 현재 assignment 활성 판정은 시간창 규칙을 반드시 동일하게 사용한다.
  • 정산 응답 계약은 기존 agent 응답과 불필요하게 분기하지 않는다.
  • 목록 화면은 요약 정보만 제공하고, 상세 정산은 상세 화면에서만 조회한다.
  • 목록의 5종 합계는 상세 API의 기간 필터와 별개로, 현재 월 기준 요약값이라는 의미를 문서와 코드에서 동일하게 유지한다.
  • 해당 월에 정산 내역이 없더라도 에이전트 행은 유지하고 금액은 0으로 표기한다.

최종 설계 결론

  • 이번 기능은 “새 정산 엔진 추가”가 아니라 “기존 agent 정산 계산을 ADMIN에서 조회할 수 있게 read API를 보강”하는 작업으로 본다.
  • 관리자 페이지 흐름은 아래로 고정한다.
    1. /admin/partner/agent/list로 에이전트 목록과 현재 월 기준 5종 정산 합계 조회
    2. 상세 화면에서 /admin/partner/agent/{agentId}/creator/list로 현재 소속 크리에이터 조회
    3. 필요 시 /admin/partner/agent/creator/search로 크리에이터 검색 후 기존 assignment API로 소속 지정
    4. 상세 화면에서 /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 부재와 기존 계산 로직 재사용 가능성을 문서에 반영했다.