diff --git a/src/api/agent.js b/src/api/agent.js new file mode 100644 index 0000000..2faefda --- /dev/null +++ b/src/api/agent.js @@ -0,0 +1,145 @@ +import Vue from 'vue' + +// 공통: 페이지 파라미터 변환(1-based UI → 0-based Spring Pageable) +function toZeroBased(page) { + const p = Number(page || 1) + return Math.max(0, p - 1) +} + +// 에이전트 리스트 조회 +// 서버 스펙에 페이지네이션이 없다면 단순 GET으로 사용 +// 추후 필요 시 params(page,size) 확장 가능 +async function getAgentList() { + return Vue.axios.get('/admin/partner/agent/list') +} + +// 에이전트 정산 비율 목록 조회 +async function getAgentSettlementRatioList() { + return Vue.axios.get('/admin/partner/agent/ratio') +} + +// 에이전트 정산 비율 등록 +// payload: { memberId: number, settlementRatio: number, effectiveFrom: string(yyyy-MM-ddTHH:mm:ss) } +async function createAgentSettlementRatio(payload) { + return Vue.axios.post('/admin/partner/agent/ratio', payload) +} + +// 에이전트 정산 비율 수정 +// payload: { memberId: number, settlementRatio: number, effectiveFrom: string(yyyy-MM-ddTHH:mm:ss) } +async function updateAgentSettlementRatio(payload) { + return Vue.axios.post('/admin/partner/agent/ratio/update', payload) +} + +// 에이전트 닉네임 검색 +// 반환: [{ id, nickname }] +async function searchAgentByNickname(query) { + try { + const res = await Vue.axios.get('/admin/partner/agent/search-by-nickname', { + params: { nickname: query, search_word: query } + }) + if (res && Array.isArray(res.data)) return res.data + if (res && res.data && Array.isArray(res.data.data)) return res.data.data + return [] + } catch (e) { + return [] + } +} + +// 에이전트 소속 크리에이터 목록 조회 +// GET /admin/partner/agent/{agentId}/creator/list +// params: { page, size } +async function getAgentAssignedCreatorList(agentId, page = 1, size = 20) { + // Spring Pageable은 일반적으로 0-based page index를 사용 + const zeroBasedPage = Math.max(0, Number(page || 1) - 1) + return Vue.axios.get(`/admin/partner/agent/${agentId}/creator/list`, { + params: { page: zeroBasedPage, size } + }) +} + +// 추가 가능한 크리에이터 검색 +// GET /admin/partner/agent/creator/search?search_word=... +async function searchAdminAgentAssignableCreators(search_word) { + return Vue.axios.get('/admin/partner/agent/creator/search', { + params: { search_word } + }) +} + +// 에이전트에 크리에이터 소속 시키기 +// POST /admin/partner/agent/assignment +// payload: { agentId, creatorId, assignedAt } // assignedAt: LocalDateTime string (yyyy-MM-ddTHH:mm:ss) +async function assignAgentCreator(payload) { + return Vue.axios.post('/admin/partner/agent/assignment', payload) +} + +// 크리에이터 소속 해제 +// POST /admin/partner/agent/assignment/remove +// payload: { creatorId, unassignedAt } // unassignedAt: LocalDateTime string +async function removeAgentCreator(payload) { + return Vue.axios.post('/admin/partner/agent/assignment/remove', payload) +} + +// ========================= +// 정산 상세 - 에이전트별(크리에이터 기준 집계) +// 공통 Request: startDateStr, endDateStr, Spring Pageable(page,size,sort) +// 공통 Response: ApiResponse +// { success, message, data: { totalCount, total:{...}, items:[...] } } + +function buildSettlementParams({ startDateStr, endDateStr, page = 1, size = 20, sort }) { + const params = { + startDateStr, + endDateStr, + page: toZeroBased(page), + size + } + if (sort) params.sort = sort + return params +} + +async function getAgentLiveSettlementByCreator(agentId, { startDateStr, endDateStr, page = 1, size = 20, sort } = {}) { + return Vue.axios.get(`/admin/partner/agent/${agentId}/calculate/live-by-creator`, { + params: buildSettlementParams({ startDateStr, endDateStr, page, size, sort }) + }) +} + +async function getAgentContentSettlementByCreator(agentId, { startDateStr, endDateStr, page = 1, size = 20, sort } = {}) { + return Vue.axios.get(`/admin/partner/agent/${agentId}/calculate/content-by-creator`, { + params: buildSettlementParams({ startDateStr, endDateStr, page, size, sort }) + }) +} + +async function getAgentCommunitySettlementByCreator(agentId, { startDateStr, endDateStr, page = 1, size = 20, sort } = {}) { + return Vue.axios.get(`/admin/partner/agent/${agentId}/calculate/community-by-creator`, { + params: buildSettlementParams({ startDateStr, endDateStr, page, size, sort }) + }) +} + +async function getAgentContentDonationSettlementByCreator(agentId, { startDateStr, endDateStr, page = 1, size = 20, sort } = {}) { + return Vue.axios.get(`/admin/partner/agent/${agentId}/calculate/content-donation-by-creator`, { + params: buildSettlementParams({ startDateStr, endDateStr, page, size, sort }) + }) +} + +async function getAgentChannelDonationSettlementByCreator(agentId, { startDateStr, endDateStr, page = 1, size = 20, sort } = {}) { + return Vue.axios.get(`/admin/partner/agent/${agentId}/calculate/channel-donation-by-creator`, { + params: buildSettlementParams({ startDateStr, endDateStr, page, size, sort }) + }) +} + +export { + getAgentList, + getAgentSettlementRatioList, + createAgentSettlementRatio, + updateAgentSettlementRatio, + searchAgentByNickname, + // 에이전트 상세 - 소속 크리에이터 관리 + getAgentAssignedCreatorList, + searchAdminAgentAssignableCreators, + assignAgentCreator, + removeAgentCreator, + // 에이전트 정산 상세 (크리에이터 기준) + getAgentLiveSettlementByCreator, + getAgentContentSettlementByCreator, + getAgentCommunitySettlementByCreator, + getAgentContentDonationSettlementByCreator, + getAgentChannelDonationSettlementByCreator, +} diff --git a/src/components/SideMenu.vue b/src/components/SideMenu.vue index 2819df9..239b15b 100644 --- a/src/components/SideMenu.vue +++ b/src/components/SideMenu.vue @@ -150,6 +150,29 @@ export default { ] }) + // 에이전트 관리 메뉴를 '크리에이터 관리' 바로 아래에 추가 + try { + const insertAfterTitle = '크리에이터 관리' + const agentMenu = { + title: '에이전트 관리', + route: null, + items: [ + { title: '에이전트 리스트', route: '/agent/list', items: null }, + { title: '에이전트 정산 비율', route: '/agent/settlement-ratio', items: null }, + ] + } + + const idx = this.items.findIndex(m => m && m.title === insertAfterTitle) + if (idx >= 0) { + this.items.splice(idx + 1, 0, agentMenu) + } else { + // 기준 메뉴가 없으면 하단에 추가 + this.items.push(agentMenu) + } + } catch (e) { + // ignore + } + // 정산 관리 메뉴에 '채널 후원 정산' 추가 try { const calculateMenu = this.items.find(m => m && m.title === '정산 관리') diff --git a/src/router/index.js b/src/router/index.js index 307c7e3..48b4e84 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -50,6 +50,53 @@ const routes = [ name: 'CreatorReview', component: () => import(/* webpackChunkName: "counselor" */ '../views/Creator/CreatorSettlementRatio.vue') }, + // Agent Management + { + path: '/agent/list', + name: 'AgentList', + component: () => import(/* webpackChunkName: "agent" */ '../views/Agent/AgentList.vue') + }, + { + path: '/agent/:agentId/settlement/live', + name: 'AgentSettlementLive', + props: true, + component: () => import(/* webpackChunkName: "agent" */ '../views/Agent/AgentLiveSettlement.vue') + }, + { + path: '/agent/:agentId/settlement/content', + name: 'AgentSettlementContent', + props: true, + component: () => import(/* webpackChunkName: "agent" */ '../views/Agent/AgentContentSettlement.vue') + }, + { + path: '/agent/:agentId/settlement/community', + name: 'AgentSettlementCommunity', + props: true, + component: () => import(/* webpackChunkName: "agent" */ '../views/Agent/AgentCommunitySettlement.vue') + }, + { + path: '/agent/:agentId/settlement/content-donation', + name: 'AgentSettlementContentDonation', + props: true, + component: () => import(/* webpackChunkName: "agent" */ '../views/Agent/AgentContentDonationSettlement.vue') + }, + { + path: '/agent/:agentId/settlement/channel-donation', + name: 'AgentSettlementChannelDonation', + props: true, + component: () => import(/* webpackChunkName: "agent" */ '../views/Agent/AgentChannelDonationSettlement.vue') + }, + { + path: '/agent/settlement-ratio', + name: 'AgentSettlementRatio', + component: () => import(/* webpackChunkName: "agent" */ '../views/Agent/AgentSettlementRatio.vue') + }, + { + path: '/agent/:agentId', + name: 'AgentDetail', + props: true, + component: () => import(/* webpackChunkName: "agent" */ '../views/Agent/AgentDetail.vue') + }, { path: '/live/tags', name: 'LiveTags', diff --git a/src/views/Agent/AgentChannelDonationSettlement.vue b/src/views/Agent/AgentChannelDonationSettlement.vue new file mode 100644 index 0000000..5118352 --- /dev/null +++ b/src/views/Agent/AgentChannelDonationSettlement.vue @@ -0,0 +1,254 @@ + + + + + diff --git a/src/views/Agent/AgentCommunitySettlement.vue b/src/views/Agent/AgentCommunitySettlement.vue new file mode 100644 index 0000000..8e19895 --- /dev/null +++ b/src/views/Agent/AgentCommunitySettlement.vue @@ -0,0 +1,254 @@ + + + + + diff --git a/src/views/Agent/AgentContentDonationSettlement.vue b/src/views/Agent/AgentContentDonationSettlement.vue new file mode 100644 index 0000000..c97fa8b --- /dev/null +++ b/src/views/Agent/AgentContentDonationSettlement.vue @@ -0,0 +1,254 @@ + + + + + diff --git a/src/views/Agent/AgentContentSettlement.vue b/src/views/Agent/AgentContentSettlement.vue new file mode 100644 index 0000000..98365dd --- /dev/null +++ b/src/views/Agent/AgentContentSettlement.vue @@ -0,0 +1,254 @@ + + + + + diff --git a/src/views/Agent/AgentDetail.vue b/src/views/Agent/AgentDetail.vue new file mode 100644 index 0000000..d80257b --- /dev/null +++ b/src/views/Agent/AgentDetail.vue @@ -0,0 +1,515 @@ + + + diff --git a/src/views/Agent/AgentList.vue b/src/views/Agent/AgentList.vue new file mode 100644 index 0000000..6c9527c --- /dev/null +++ b/src/views/Agent/AgentList.vue @@ -0,0 +1,242 @@ + + + + + diff --git a/src/views/Agent/AgentLiveSettlement.vue b/src/views/Agent/AgentLiveSettlement.vue new file mode 100644 index 0000000..b711aff --- /dev/null +++ b/src/views/Agent/AgentLiveSettlement.vue @@ -0,0 +1,290 @@ + + + + + diff --git a/src/views/Agent/AgentSettlementRatio.vue b/src/views/Agent/AgentSettlementRatio.vue new file mode 100644 index 0000000..d8c678a --- /dev/null +++ b/src/views/Agent/AgentSettlementRatio.vue @@ -0,0 +1,339 @@ + + + + + diff --git a/src/views/Member/MemberList.vue b/src/views/Member/MemberList.vue index 07dfac1..8bcd212 100644 --- a/src/views/Member/MemberList.vue +++ b/src/views/Member/MemberList.vue @@ -183,6 +183,10 @@ value="CREATOR" label="크리에이터" /> +