lili b31cc37a4f chore(brand): 造梦AI→绘境AI 全活层品牌不变量收口(441 文件)
把活层退役旧品牌「造梦AI」统一改为现行「绘境AI」,brand-invariant 门红线归零。构成:
~412 Java @author 署名 + game-studio/index.html 浏览器标题 + game-runtime/package.json 描述
+ game-admin/.env VITE_APP_TITLE 运营后台标题 + contracts API title/@Schema 用户协议示例/events 描述
+ docs-design mockup 品牌位 + 各模块注释。全是注释/显示/元数据,零逻辑改动。
zaomeng 拼音持久化标识符按 2026-06-24 评审 GR-05 不动(347 处保留)。
白名单(docs/ip 法律备案 / _archive / 带日期留痕)与 AGENTS.md 门定义自身不动。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 08:29:11 -07:00

217 lines
7.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import request from '@/config/axios'
import { isMock, mockPage, mockResult } from './mock'
/**
* 绘境AI —— ad 模块 admin 接口(广告位管理 + 广告收入台账对账)
* 契约contracts/api-schemas/ad.yaml字段严格对齐契约#7 ad-slot.schema.json不重造广告位结构
* - POST /admin-api/ad/slot/create 新建广告位slotId 业务唯一)
* - PUT /admin-api/ad/slot/update 更新(按主键 idslotId 不可改)
* - DELETE /admin-api/ad/slot/delete?id= 逻辑删除
* - GET /admin-api/ad/slot/get?id= 详情
* - GET /admin-api/ad/slot/page 分页(按 type/provider/enabled 筛选)
* - GET /admin-api/ad/revenue/page 广告收入台账分页(归因/计费对账,只读)
* 端与鉴权admin-api 走 huijing 管理员 Token + RBACCommonResult code=0 成功request 已解包 data
* 资金口径:金额一律「分」(BIGINT)禁浮点ecpmFloor/ecpm/revenueAmount 均为分。
*/
/** 合规植入规则(对齐 ad.yaml AdComplianceVO / 契约#7 compliance */
export interface AdComplianceVO {
blockMinor?: boolean // 识别为未成年则不展示/不计费
maxPerSession?: number // 单会话最大展示次数
}
/** 广告位新建/更新入参(对齐 ad.yaml AdSlotSaveReqVO */
export interface AdSlotSaveReqVO {
id?: number // 更新必填、新建不传
slotId: string // 业务唯一键 uk_slot新建唯一、更新不可改
type: string // rewarded | interstitial | banner
provider: string // csj | gdt | mockMVP 默认 mock
placement?: string // game_end | pause | feed
providerSlotId?: string // 联盟侧真实广告位 IDprovider!=mock 时必填)
ecpmFloor?: number // eCPM 底价(分)
enabled: boolean
compliance?: AdComplianceVO
}
/** 广告位详情(对齐 ad.yaml AdSlotRespVO */
export interface AdSlotRespVO {
id: number
slotId: string
type: string
provider: string
placement?: string
providerSlotId?: string
ecpmFloor?: number
enabled: boolean
compliance?: AdComplianceVO
createTime?: string
updateTime?: string
}
/** 广告位分页查询参数(对齐 ad.yaml /admin-api/ad/slot/page */
export interface AdSlotPageReqVO extends PageParam {
type?: string
provider?: string
enabled?: boolean
}
/** 广告收入台账记录(对齐 ad.yaml AdRevenueRespVO */
export interface AdRevenueRespVO {
id: number // = trade 侧 source_ref 对账锚点
slotId: string
gameId: number
creatorUserId: number // 收入归因到的创作者用户 ID
eventType: string // impression | reward
provider: string
ecpm?: number // 分
revenueAmount: number // 本条广告收入(分)
traceId: string // 计费幂等键 / 对账 traceId
settleStatus: number // 0未结算 1已结算
statDate?: string // 归集日 yyyy-MM-dd
createTime?: string
}
/** 广告收入台账查询参数(对齐 ad.yaml /admin-api/ad/revenue/page */
export interface AdRevenuePageReqVO extends PageParam {
slotId?: string
gameId?: number
creatorUserId?: number
eventType?: string // impression | reward
settleStatus?: number // 0未结算 1已结算
statDate?: string
}
// ---- mock 数据 ----
const mockSlotList: AdSlotRespVO[] = [
{
id: 1,
slotId: 'slot_game_end_rewarded',
type: 'rewarded',
provider: 'mock',
placement: 'game_end',
providerSlotId: '',
ecpmFloor: 3500,
enabled: true,
compliance: { blockMinor: true, maxPerSession: 3 },
createTime: '2026-06-05 10:00:00',
updateTime: '2026-06-05 10:00:00'
},
{
id: 2,
slotId: 'slot_pause_interstitial',
type: 'interstitial',
provider: 'mock',
placement: 'pause',
providerSlotId: '',
ecpmFloor: 2000,
enabled: true,
compliance: { blockMinor: true, maxPerSession: 5 },
createTime: '2026-06-05 10:05:00',
updateTime: '2026-06-05 10:05:00'
},
{
id: 3,
slotId: 'slot_feed_banner',
type: 'banner',
provider: 'mock',
placement: 'feed',
providerSlotId: '',
ecpmFloor: 800,
enabled: false,
compliance: { blockMinor: true, maxPerSession: 10 },
createTime: '2026-06-05 10:10:00',
updateTime: '2026-06-06 09:00:00'
}
]
const mockRevenueList: AdRevenueRespVO[] = [
{
id: 90001,
slotId: 'slot_game_end_rewarded',
gameId: 2001,
creatorUserId: 30001,
eventType: 'reward',
provider: 'mock',
ecpm: 3500,
revenueAmount: 3,
traceId: 'trace-aaa-0001',
settleStatus: 0,
statDate: '2026-06-07',
createTime: '2026-06-07 12:30:00'
},
{
id: 90002,
slotId: 'slot_pause_interstitial',
gameId: 2002,
creatorUserId: 30002,
eventType: 'impression',
provider: 'mock',
ecpm: 2000,
revenueAmount: 2,
traceId: 'trace-bbb-0002',
settleStatus: 1,
statDate: '2026-06-06',
createTime: '2026-06-06 18:10:00'
}
]
/** 广告位分页 */
export const getAdSlotPage = async (params: AdSlotPageReqVO) => {
if (isMock()) {
let filtered = mockSlotList
if (params.type) filtered = filtered.filter((it) => it.type === params.type)
if (params.provider) filtered = filtered.filter((it) => it.provider === params.provider)
if (params.enabled != null) filtered = filtered.filter((it) => it.enabled === params.enabled)
return mockPage(filtered, params.pageNo, params.pageSize)
}
return await request.get({ url: '/ad/slot/page', params })
}
/** 广告位详情 */
export const getAdSlot = async (id: number) => {
if (isMock()) {
return mockResult(mockSlotList.find((it) => it.id === id) || mockSlotList[0])
}
return await request.get({ url: '/ad/slot/get?id=' + id })
}
/** 新建广告位(返回主键 ID */
export const createAdSlot = async (data: AdSlotSaveReqVO) => {
if (isMock()) {
return mockResult(Date.now())
}
return await request.post({ url: '/ad/slot/create', data })
}
/** 更新广告位slotId 不可改) */
export const updateAdSlot = async (data: AdSlotSaveReqVO) => {
if (isMock()) {
return mockResult(true)
}
return await request.put({ url: '/ad/slot/update', data })
}
/** 删除广告位(逻辑删除) */
export const deleteAdSlot = async (id: number) => {
if (isMock()) {
return mockResult(true)
}
return await request.delete({ url: '/ad/slot/delete?id=' + id })
}
/** 广告收入台账分页(只读对账) */
export const getAdRevenuePage = async (params: AdRevenuePageReqVO) => {
if (isMock()) {
let filtered = mockRevenueList
if (params.slotId) filtered = filtered.filter((it) => it.slotId === params.slotId)
if (params.gameId != null) filtered = filtered.filter((it) => it.gameId === params.gameId)
if (params.creatorUserId != null)
filtered = filtered.filter((it) => it.creatorUserId === params.creatorUserId)
if (params.eventType) filtered = filtered.filter((it) => it.eventType === params.eventType)
if (params.settleStatus != null)
filtered = filtered.filter((it) => it.settleStatus === params.settleStatus)
return mockPage(filtered, params.pageNo, params.pageSize)
}
return await request.get({ url: '/ad/revenue/page', params })
}