diff --git a/game-admin/src/api/wanxiang/feed.ts b/game-admin/src/api/wanxiang/feed.ts index bb6dd992..5c5c53b1 100644 --- a/game-admin/src/api/wanxiang/feed.ts +++ b/game-admin/src/api/wanxiang/feed.ts @@ -4,10 +4,12 @@ import { isMock, mockPage, mockResult } from './mock' /** * 造梦AI —— feed 模块 admin 接口(游戏流精选池运营) * 契约:contracts/api-schemas/feed.yaml - * - GET /admin-api/feed/featured/page 精选池列表(可按 zoneId 筛选) - * - POST /admin-api/feed/featured 置顶/取消 + 运营加权(写 game_feed_rank.pinned/boost) + * - GET /admin-api/feed/featured/page 精选池列表(可按 zoneId 筛选) + * - POST /admin-api/feed/featured 置顶/取消 + 运营加权(写 game_feed_rank.pinned/boost) + * - POST /admin-api/feed/exposure-limit 限曝光降权(U5;boost 反向操作,写 game_feed_rank.exposure_limit) + * - GET /admin-api/feed/featured/export-excel 精选池/限曝光排序覆盖层导出 Excel(U5) * 端与鉴权:admin-api 走 huijing 管理员 Token + RBAC;CommonResult code=0 成功(request 已解包 data)。 - * 边界:精选只作用于排序覆盖层(pinned/boost),不改 project 本体;仅 status=4 已发布可入精选。 + * 边界:精选/限曝光只作用于排序覆盖层(pinned/boost/exposure_limit),不改 project 本体;仅 status=4 已发布可入精选/限曝光。 */ /** 精选池单卡(对齐 feed.yaml FeedCardRespVO) */ @@ -39,6 +41,16 @@ export interface FeaturedPageReqVO extends PageParam { zoneId?: number } +/** + * 限曝光降权入参(对齐后端 ExposureLimitReqVO) + * exposureLimit 取值:0=取消降权 / >0=降权 x 分 / >=9999=硬限曝光(出流剔除);仅 status=4 已发布且已有排序行可设置。 + */ +export interface ExposureLimitReqVO { + gameId: number // 目标游戏ID(须已发布) + zoneId?: number // 作用专区;不传=默认混合流(zoneId=0) + exposureLimit: number // 限曝光降权因子(>=0) +} + // ---- mock 数据(对齐 FeedCardRespVO,均为已发布且进入精选池的游戏) ---- const mockFeaturedList: FeedCardRespVO[] = [ { @@ -104,3 +116,19 @@ export const saveFeatured = async (data: FeaturedReqVO) => { } return await request.post({ url: '/feed/featured', data }) } + +/** 限曝光降权(U5;boost 反向操作,写 exposure_limit;perm feed:featured:update) */ +export const setExposureLimit = async (data: ExposureLimitReqVO) => { + if (isMock()) { + return mockResult(true) + } + return await request.post({ url: '/feed/exposure-limit', data }) +} + +/** 导出精选池/限曝光排序覆盖层 Excel(U5;blob 下载,perm feed:featured:export;mock 下不下载,返回 null) */ +export const exportFeaturedExcel = async (params: { zoneId?: number }) => { + if (isMock()) { + return mockResult(null) + } + return await request.download({ url: '/feed/featured/export-excel', params }) +} diff --git a/game-admin/src/api/wanxiang/telemetry.ts b/game-admin/src/api/wanxiang/telemetry.ts index 6e1f669a..bbf42271 100644 --- a/game-admin/src/api/wanxiang/telemetry.ts +++ b/game-admin/src/api/wanxiang/telemetry.ts @@ -2,13 +2,16 @@ import request from '@/config/axios' import { isMock, mockPage, mockResult } from './mock' /** - * 造梦AI —— telemetry 模块 admin 接口(数据看板数据源) + * 造梦AI —— telemetry 模块 admin 接口(数据看板数据源 + 经营看板聚合) * 契约:contracts/api-schemas/telemetry.yaml - * - GET /admin-api/telemetry/game-stat/page 游戏维度聚合分页(看板表格数据源) - * - GET /admin-api/telemetry/game-stat/{gameId} 单游戏最新聚合快照 + * - GET /admin-api/telemetry/game-stat/page 游戏维度聚合分页(看板表格数据源) + * - GET /admin-api/telemetry/game-stat/{gameId} 单游戏最新聚合快照 + * - GET /admin-api/telemetry/dashboard 经营看板聚合(U5;跨模块只读 DAU/GMV/生成量/发布量) + * - GET /admin-api/telemetry/dashboard/export-excel 经营看板导出 Excel(U5) * 端与鉴权:admin-api 走 huijing 管理员 Token + RBAC;CommonResult code=0 成功(request 已解包 data)。 - * 量纲:qualityScore=运营质量分(0-100),≠ aigc 生成质量分(0-1)。 + * 量纲:qualityScore=运营质量分(0-100),≠ aigc 生成质量分(0-1);GMV 单位「分」(分账前原始收入)。 * 范围:广告/收益聚合字段为 Wave2 变现范围外(later 补列,本 VO 不含,非遗漏)。 + * 经营看板 ≠ game-stat:前者是跨模块(DAU+trade+aigc+project)区间聚合卡片,后者是按游戏维度的明细分页。 */ /** 游戏维度聚合快照(对齐 telemetry.yaml GameStatRespVO) */ @@ -34,6 +37,27 @@ export interface GameStatPageReqVO extends PageParam { statDate?: string // 统计日;空则取最新一日 } +/** + * 经营看板聚合快照(对齐后端 DashboardRespVO) + * 跨模块只读聚合:DAU(本模块) + GMV(trade,分) + 生成量(aigc) + 发布量(project);缺模块数据兜底 0 + degraded 标记,整体不 500。 + */ +export interface DashboardRespVO { + statDateStart?: string + statDateEnd?: string + dau?: number // 统计结束日当天去重活跃身份数 + gmv?: number // 区间原始收入合计(分账前,单位:分) + generatedCount?: number // 区间 aigc 成功生成任务数 + publishedCount?: number // 区间 project 已发布项目数 + degraded?: boolean // true=有跨模块数据源不可用、对应指标已兜底 0 + degradedSources?: string[] // 不可用数据源明细(如 ['trade','aigc']) +} + +/** 经营看板查询参数(对齐后端 /admin-api/telemetry/dashboard,区间必填) */ +export interface DashboardReqVO { + statDateStart: string + statDateEnd: string +} + // ---- mock 数据(对齐 GameStatRespVO,按日聚合) ---- const mockGameStatList: GameStatRespVO[] = [ { @@ -118,3 +142,28 @@ export const getGameStat = async (gameId: number) => { } return await request.get({ url: '/telemetry/game-stat/' + gameId }) } + +/** 经营看板聚合(U5;跨模块只读,perm telemetry:dashboard:query) */ +export const getDashboard = async (params: DashboardReqVO) => { + if (isMock()) { + return mockResult({ + statDateStart: params.statDateStart, + statDateEnd: params.statDateEnd, + dau: 1530, + gmv: 1000000, // 分(=10000元) + generatedCount: 320, + publishedCount: 88, + degraded: false, + degradedSources: [] + }) + } + return await request.get({ url: '/telemetry/dashboard', params }) +} + +/** 导出经营看板 Excel(U5;blob 下载,perm telemetry:dashboard:query;mock 下不下载,返回 null) */ +export const exportDashboardExcel = async (params: DashboardReqVO) => { + if (isMock()) { + return mockResult(null) + } + return await request.download({ url: '/telemetry/dashboard/export-excel', params }) +} diff --git a/game-admin/src/api/wanxiang/trade.ts b/game-admin/src/api/wanxiang/trade.ts index 5a9f08cc..c1552a0a 100644 --- a/game-admin/src/api/wanxiang/trade.ts +++ b/game-admin/src/api/wanxiang/trade.ts @@ -2,14 +2,18 @@ import request from '@/config/axios' import { isMock, mockPage, mockResult } from './mock' /** - * 造梦AI —— trade 模块 admin 接口(提现审核 + 收益流水查询 + 平台营收报表) + * 造梦AI —— trade 模块 admin 接口(提现审核 + 收益流水查询 + 平台营收报表 + 赋余额 + 赋订阅) * 契约:contracts/api-schemas/trade.yaml - * - GET /admin-api/trade/withdraw/page 提现审核队列(默认 status=0 待审核) - * - POST /admin-api/trade/withdraw/audit 审核(通过/驳回,驱动状态机;非法流转服务端拒绝) - * - GET /admin-api/trade/income/page 收益流水查询(跨创作者对账,只读) - * - GET /admin-api/trade/report/revenue 平台营收报表聚合(按日 gross/net/平台分成/提现,只读) + * - GET /admin-api/trade/withdraw/page 提现审核队列(默认 status=0 待审核) + * - POST /admin-api/trade/withdraw/audit 审核(通过/驳回,驱动状态机;非法流转服务端拒绝) + * - GET /admin-api/trade/income/page 收益流水查询(跨创作者对账,只读) + * - GET /admin-api/trade/report/revenue 平台营收报表聚合(按日 gross/net/平台分成/提现,只读) + * - GET /admin-api/trade/report/revenue/export-excel 营收报表导出 Excel(U5) + * - POST /admin-api/trade/account/grant admin 赋余额(U2;幂等键 bizNo,real 入账+流水可对账) + * - POST /admin-api/trade/subscription/grant admin 赋订阅(U2;幂等键 bizNo,续期叠加) * 端与鉴权:admin-api 走 huijing 管理员 Token + RBAC;CommonResult code=0 成功(request 已解包 data)。 * 资金口径:金额一律「分」(BIGINT),禁浮点;shareRate 为 DECIMAL(如 0.8)。 + * ⚠️ 资金操作:赋余额/赋订阅须前端二次确认 + 携带 bizNo 幂等键(后端 @NotEmpty 强校验,重试安全);操作人由 token 解析,非前端入参。 */ /** 提现状态机(对齐 trade.yaml WithdrawRespVO.status / x-state-machines) */ @@ -94,6 +98,36 @@ export interface RevenueReportReqVO { statDateEnd: string } +/** 订阅套餐(对齐后端 GrantSubscriptionReqVO.plan) */ +export enum SubscriptionPlanEnum { + MONTHLY = 1, // 月度 + QUARTERLY = 2, // 季度 + YEARLY = 3 // 年度 +} + +/** + * admin 赋余额入参(对齐后端 GrantBalanceReqVO) + * 资金操作:amount 单位「分」只增不减(>0);bizNo 幂等键(REQUIRED, max64, 调用端生成唯一);操作人由 token 解析非入参。 + */ +export interface GrantBalanceReqVO { + userId: number // 被赋余额的创作者用户ID + amount: number // 赋余额金额(分,>0) + bizNo: string // 业务单号=幂等键(REQUIRED) + remark?: string // 赋值原因(可空) +} + +/** + * admin 赋订阅入参(对齐后端 GrantSubscriptionReqVO) + * plan=1月度/2季度/3年度;durationDays>0 续期叠加;bizNo 幂等键(REQUIRED, max64);操作人由 token 解析非入参。 + */ +export interface GrantSubscriptionReqVO { + userId: number // 订阅用户ID + plan: number // 套餐:1月度/2季度/3年度 + durationDays: number // 赋订阅时长(天,>0,续期叠加) + bizNo: string // 业务单号=幂等键(REQUIRED) + remark?: string // 开通原因(可空) +} + // ---- mock 数据 ---- const mockWithdrawList: WithdrawRespVO[] = [ { @@ -200,3 +234,30 @@ export const getRevenueReport = async (params: RevenueReportReqVO) => { } return await request.get({ url: '/trade/report/revenue', params }) } + +/** 导出平台营收报表 Excel(U5;blob 下载,perm trade:report:export;mock 下不下载,返回 null) */ +export const exportRevenueExcel = async (params: RevenueReportReqVO) => { + if (isMock()) { + return mockResult(null) + } + return await request.download({ url: '/trade/report/revenue/export-excel', params }) +} + +/** admin 赋余额(U2;资金操作,返回赋余额流水 ID;perm trade:account:grant) */ +export const grantBalance = async (data: GrantBalanceReqVO) => { + if (isMock()) { + // mock:返回一个伪流水 ID(与真实返回 Long 同构) + return mockResult(Date.now()) + } + return await request.post({ url: '/trade/account/grant', data }) +} + +/** admin 赋订阅(U2;资金操作,返回赋值后到期时间字符串;perm trade:subscription:grant) */ +export const grantSubscription = async (data: GrantSubscriptionReqVO) => { + if (isMock()) { + // mock:返回一个伪到期时间(与真实返回 LocalDateTime 序列化同构) + const d = new Date(Date.now() + (data.durationDays ?? 30) * 24 * 3600 * 1000) + return mockResult(d.toISOString().slice(0, 19).replace('T', ' ')) + } + return await request.post({ url: '/trade/subscription/grant', data }) +} diff --git a/game-admin/src/router/modules/remaining.ts b/game-admin/src/router/modules/remaining.ts index e0f0eff7..3eed7d7c 100644 --- a/game-admin/src/router/modules/remaining.ts +++ b/game-admin/src/router/modules/remaining.ts @@ -238,8 +238,9 @@ const remainingRouter: AppRouteRecordRaw[] = [ // 造梦AI(wanxiang) MVP 运营页静态路由: // - 已裁剪 huijing 无关业务模块(ai/bpm/crm/erp/iot/mall/member/mp/pay/report)的全部静态路由, // 避免对已删视图的死 import 导致构建失败; - // - 新增造梦AI 5 个 MVP 运营页(wanxiang/*)静态路由:后端 RBAC 菜单未下发时也可路由可见、 + // - 新增造梦AI 8 个 MVP 运营页(wanxiang/*)静态路由:后端 RBAC 菜单未下发时也可路由可见、 // 可独立构建(mock 兜底见各 wanxiang api 模块),对接我方 admin-api。 + // (U10 追加 3 页:限曝光控制 / 经营看板 / 赋余额+订阅。) // =========================================================================== { path: '/wanxiang', @@ -270,6 +271,16 @@ const remainingRouter: AppRouteRecordRaw[] = [ icon: 'ep:star' } }, + { + // U10:限曝光控制(boost 反向运营,写排序覆盖层 exposure_limit) + path: 'exposure-limit', + component: () => import('@/views/wanxiang/exposureLimit/index.vue'), + name: 'WanxiangExposureLimit', + meta: { + title: '限曝光控制', + icon: 'ep:bottom' + } + }, { path: 'dashboard', component: () => import('@/views/wanxiang/dashboard/index.vue'), @@ -279,6 +290,16 @@ const remainingRouter: AppRouteRecordRaw[] = [ icon: 'ep:data-line' } }, + { + // U10:经营看板(跨模块只读聚合 DAU/GMV/生成量/发布量;区别于按游戏维度的「数据看板」) + path: 'operation-dashboard', + component: () => import('@/views/wanxiang/operationDashboard/index.vue'), + name: 'WanxiangOperationDashboard', + meta: { + title: '经营看板', + icon: 'ep:pie-chart' + } + }, { path: 'ad-slot', component: () => import('@/views/wanxiang/adSlot/index.vue'), @@ -296,6 +317,16 @@ const remainingRouter: AppRouteRecordRaw[] = [ title: '提现审核', icon: 'ep:wallet' } + }, + { + // U10:赋余额 + 赋订阅(资金操作 2 Tab;二次确认 + bizNo 幂等键) + path: 'grant', + component: () => import('@/views/wanxiang/grant/index.vue'), + name: 'WanxiangGrant', + meta: { + title: '赋余额/订阅', + icon: 'ep:money' + } } ] }, diff --git a/game-admin/src/views/wanxiang/exposureLimit/index.vue b/game-admin/src/views/wanxiang/exposureLimit/index.vue new file mode 100644 index 00000000..4a3edcc1 --- /dev/null +++ b/game-admin/src/views/wanxiang/exposureLimit/index.vue @@ -0,0 +1,246 @@ + + + diff --git a/game-admin/src/views/wanxiang/grant/index.vue b/game-admin/src/views/wanxiang/grant/index.vue new file mode 100644 index 00000000..4e80e0c4 --- /dev/null +++ b/game-admin/src/views/wanxiang/grant/index.vue @@ -0,0 +1,292 @@ + + + + + diff --git a/game-admin/src/views/wanxiang/operationDashboard/index.vue b/game-admin/src/views/wanxiang/operationDashboard/index.vue new file mode 100644 index 00000000..29236b13 --- /dev/null +++ b/game-admin/src/views/wanxiang/operationDashboard/index.vue @@ -0,0 +1,169 @@ + + + + + diff --git a/game-admin/src/views/wanxiang/withdraw/index.vue b/game-admin/src/views/wanxiang/withdraw/index.vue index bb40d1ff..01806fc9 100644 --- a/game-admin/src/views/wanxiang/withdraw/index.vue +++ b/game-admin/src/views/wanxiang/withdraw/index.vue @@ -128,6 +128,15 @@ 查询 + + 导出 + @@ -187,6 +196,7 @@