7b38ef9623
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
- packages/shared 游戏契约补买断价与购买态:GameDistributionGame 增 priceMudPoints/purchased,版本摘要 entryUrl 可为 null - packages/shared 版本请求增 priceMudPoints,私有版本回读增同名字段;新增购买请求、购买记录、购买响应与播放会话 DTO - packages/shared 钱包流水来源类型增 game_purchase,网页与共享账单文案增「购买游戏」标签 - 游戏分发客户端新增 purchaseGame(Idempotency-Key + 期望价格)与 createGamePlaySession - 发布页新增「付费方式」免费/买断制表单,定价校验与服务端 0..=1000000 口径一致,价格随版本请求提交 - 详情页展示定价、付费未购买时主动作改为「N 泥点购买」,复用泥点确认弹窗展示余额,成功后刷新钱包并回到可玩状态,余额不足给出充值入口 - 游玩页付费作品先签发播放会话再挂载 iframe,免费作品链路不变;会话被拒时不挂载 iframe 也不上报游玩 - 发行入口守卫接受播放会话前缀路径,拒绝其它非发行路径 - 定向契约测试登记 4 组购买/播放会话 DTO,补发布定价、未购买不可玩、购买确认与播放会话挂载用例
327 lines
10 KiB
TypeScript
327 lines
10 KiB
TypeScript
export const GAME_DISTRIBUTION_CATEGORIES = [
|
|
'休闲',
|
|
'益智',
|
|
'动作',
|
|
'冒险',
|
|
'模拟',
|
|
'策略',
|
|
'其他',
|
|
] as const;
|
|
|
|
export type GameDistributionCategory =
|
|
(typeof GAME_DISTRIBUTION_CATEGORIES)[number];
|
|
|
|
export type GameDistributionPublishMetadataSuggestionRequest = {
|
|
name: string;
|
|
goal?: string | null;
|
|
/** 脱敏且有界的项目上下文摘要。 */
|
|
context?: string | null;
|
|
};
|
|
|
|
export type GameDistributionPublishMetadataSuggestion = {
|
|
summary: string;
|
|
category: GameDistributionCategory;
|
|
};
|
|
|
|
export type GameDistributionDeviceSupport = {
|
|
desktop: boolean;
|
|
mobile: boolean;
|
|
touch: boolean;
|
|
};
|
|
|
|
export type GameDistributionInputMode = 'keyboard' | 'mouse' | 'touch';
|
|
export type GameDistributionOrientation =
|
|
| 'landscape'
|
|
| 'portrait'
|
|
| 'responsive';
|
|
|
|
export type GameDistributionAuthor = {
|
|
id: string;
|
|
name: string;
|
|
avatarUrl?: string | null;
|
|
};
|
|
|
|
export type GameDistributionReview = {
|
|
/** 管理员隐藏后仍允许本人读取和编辑,不参与公共统计。 */
|
|
isHidden: boolean;
|
|
id: string;
|
|
gameId: string;
|
|
author: GameDistributionAuthor;
|
|
score: number;
|
|
comment: string;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
};
|
|
|
|
export type GameDistributionRatingSummary = {
|
|
averageScore: number | null;
|
|
ratingCount: number;
|
|
};
|
|
|
|
export type GameDistributionReviewsResponse = {
|
|
reviews: GameDistributionReview[];
|
|
page: number;
|
|
pageSize: number;
|
|
total: number;
|
|
totalPages: number;
|
|
ratingSummary: GameDistributionRatingSummary;
|
|
};
|
|
|
|
export type GameDistributionMyReviewResponse = {
|
|
review: GameDistributionReview | null;
|
|
};
|
|
|
|
export type GameDistributionSaveReviewRequest = {
|
|
score: number;
|
|
comment?: string;
|
|
};
|
|
|
|
export type GameDistributionSaveReviewResponse = {
|
|
review: GameDistributionReview;
|
|
ratingSummary: GameDistributionRatingSummary;
|
|
};
|
|
|
|
export type GameDistributionVersionStatus =
|
|
| 'awaiting_upload'
|
|
| 'uploaded'
|
|
| 'validating'
|
|
| 'pending_review'
|
|
| 'published'
|
|
| 'upload_failed'
|
|
| 'validation_failed'
|
|
| 'rejected'
|
|
| 'cancelled'
|
|
| 'revoked';
|
|
|
|
export type GameDistributionGameVisibility =
|
|
| 'unpublished'
|
|
| 'published'
|
|
| 'suspended';
|
|
|
|
export type GameDistributionVersionSummary = {
|
|
id: string;
|
|
version: string;
|
|
/**
|
|
* 发行入口:平台同源路径 `/games/<gameId>/`,客户端按当前 origin 解析后再交给 iframe。
|
|
* 兼容历史数据的绝对 URL(非当前源的 https 地址),新写入只用相对路径。
|
|
*
|
|
* 买断制作品在未购买时不下发公开入口,这一项为 `null`;游玩入口改由播放会话接口签发。
|
|
*/
|
|
entryUrl: string | null;
|
|
sha256: string;
|
|
publishedAt: string;
|
|
controls: string[];
|
|
};
|
|
|
|
export type GameDistributionGame = {
|
|
id: string;
|
|
title: string;
|
|
summary: string;
|
|
description: string;
|
|
category: GameDistributionCategory;
|
|
tags: string[];
|
|
coverColor: string;
|
|
icon: string;
|
|
/**
|
|
* 公开封面的 OSS objectKey;未上传封面时为空,展示层需回退到 coverColor/icon 占位。
|
|
* 通过 `/api/assets/read-url` 换签名 URL 读取,不接受直连外链。
|
|
*/
|
|
coverObjectKey?: string | null;
|
|
/** 公开截图的 OSS objectKey 列表,最多 6 张,随版本冻结。 */
|
|
screenshots?: string[];
|
|
author: GameDistributionAuthor;
|
|
deviceSupport: GameDistributionDeviceSupport;
|
|
inputModes?: GameDistributionInputMode[];
|
|
orientation?: GameDistributionOrientation;
|
|
status: Extract<GameDistributionGameVisibility, 'published' | 'unpublished'>;
|
|
/** 公开切换 CAS 版本号;作者下架与管理员审核都必须回传当前值。 */
|
|
publicationRevision: number;
|
|
/**
|
|
* 公开目录与公开详情一定带这一项(没有已公开版本时为 null);作者侧响应(我的作品列表、
|
|
* 创建、提交、审核、撤回)走服务端同一个 `game_payload`,不带这一项。
|
|
*/
|
|
currentVersion?: GameDistributionVersionSummary | null;
|
|
/** 公开列表和详情返回有效评价摘要;作者侧或旧响应可省略。 */
|
|
ratingSummary?: GameDistributionRatingSummary;
|
|
/** 买断价(整数泥点,`0` 表示免费)。旧响应可省略,读取方按 `0`(免费)兜底。 */
|
|
priceMudPoints: number;
|
|
/** 当前查看者是否已拥有;匿名与未登录恒为 `false`。 */
|
|
purchased: boolean;
|
|
playCount: number;
|
|
createdAt: string;
|
|
};
|
|
|
|
export type GameDistributionListResponse = {
|
|
games: GameDistributionGame[];
|
|
nextCursor?: string | null;
|
|
};
|
|
|
|
export type GameDistributionCreateGameRequest = {
|
|
/** 发布方本地项目标识;同一作者重复发布会复用既有 gameId。 */
|
|
localProjectId?: string | null;
|
|
title: string;
|
|
summary: string;
|
|
description?: string;
|
|
category: GameDistributionCategory;
|
|
tags?: string[];
|
|
coverAssetId?: string;
|
|
/** 截图素材 ID(最多 6 张,复用平台图片上传与归属校验)。 */
|
|
screenshots?: string[];
|
|
deviceSupport: GameDistributionDeviceSupport;
|
|
inputModes: GameDistributionInputMode[];
|
|
orientation: GameDistributionOrientation;
|
|
};
|
|
|
|
export type GameDistributionCreateVersionRequest = {
|
|
localProjectId?: string | null;
|
|
/**
|
|
* 用户可见的正整数版本标签(AGC 发布面板由工程内部版本序数派生后原样提交)。
|
|
*
|
|
* 传入时只要求 `>= 1`:同一 `gameId` 的同一个版本号可以反复提交,每次提交生成新的
|
|
* `versionId`,允许重复标签与回退到更小的版本号,不与已有最大值比较。
|
|
* 缺省时保留旧客户端兼容行为:服务端按该游戏已有最大版本号 +1。
|
|
*/
|
|
versionNumber?: number | null;
|
|
/**
|
|
* 作者提交的买断价(整数泥点,`0` 表示免费)。
|
|
*
|
|
* 价格随版本冻结,审核通过时与资料一起生效到游戏行;缺省按 `0`(免费)处理。
|
|
* 取值范围由服务端约束:`0..=1_000_000`,付费必须是正整数。
|
|
*/
|
|
priceMudPoints?: number;
|
|
packageSha256: string;
|
|
packageBytes: number;
|
|
packageFileCount: number;
|
|
packageEntryPath: 'index.html';
|
|
gameMetadata: GameDistributionCreateGameRequest;
|
|
};
|
|
|
|
/**
|
|
* 作者编辑游戏级展示资料。
|
|
*
|
|
* 只覆盖游戏行上的展示字段;随版本冻结的包摘要与资料快照不受影响。
|
|
* `expectedPublicationRevision` 是公开切换 CAS,并发变化返回 409。
|
|
*/
|
|
export type GameDistributionUpdateGameMetadataRequest = {
|
|
expectedPublicationRevision: number;
|
|
title: string;
|
|
summary: string;
|
|
description?: string | null;
|
|
category: GameDistributionCategory;
|
|
tags?: string[];
|
|
coverAssetId?: string | null;
|
|
/** 截图素材 ID(最多 6 张,复用平台图片上传与归属校验)。 */
|
|
screenshots?: string[];
|
|
deviceSupport: GameDistributionDeviceSupport;
|
|
inputModes: GameDistributionInputMode[];
|
|
orientation: GameDistributionOrientation;
|
|
};
|
|
|
|
export type GameDistributionPrivateVersion = {
|
|
versionId: string;
|
|
gameId: string;
|
|
versionNumber: number;
|
|
packageSha256: string;
|
|
packageBytes: number;
|
|
packageFileCount: number;
|
|
status: GameDistributionVersionStatus;
|
|
/** 游戏公开修订号;撤回与审核动作都必须回传当前值做 CAS。 */
|
|
publicationRevision: number;
|
|
/** 该版本冻结的买断价(整数泥点,`0` 表示免费);无价格的历史版本按免费口径处理。 */
|
|
priceMudPoints?: number;
|
|
reviewReason?: string | null;
|
|
/** 审核通过后由服务端派生的公开入口,未公开版本为 null。 */
|
|
entryUrl: string | null;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
};
|
|
/**
|
|
* 版本状态的下一步动作,由服务端派生;客户端只按它渲染主行动作,不自行推断状态。
|
|
*/
|
|
export type GameDistributionRecoveryAction =
|
|
| 'upload'
|
|
| 'submit'
|
|
| 'wait'
|
|
| 'none'
|
|
| 'reupload'
|
|
| 'fix_package'
|
|
| 'fix_metadata';
|
|
|
|
/** 冻结资料里的截图:同时保留素材 ID(作者续发可复用)与对象键(展示换签用)。 */
|
|
export type GameDistributionFrozenScreenshot = {
|
|
assetId: string;
|
|
objectKey: string;
|
|
};
|
|
|
|
/**
|
|
* 随版本冻结的游戏资料快照。
|
|
*
|
|
* 只有作者本人(版本回读)与管理员(审核回读)会拿到素材 ID;公开投影只给对象键。
|
|
* 历史版本可能没有快照,读取方必须按空值处理。
|
|
*/
|
|
export type GameDistributionVersionFrozenMetadata = {
|
|
title?: string;
|
|
summary?: string;
|
|
description?: string;
|
|
category?: GameDistributionCategory;
|
|
tags?: string[];
|
|
coverAssetId?: string | null;
|
|
coverObjectKey?: string | null;
|
|
screenshots?: GameDistributionFrozenScreenshot[];
|
|
deviceSupport?: GameDistributionDeviceSupport;
|
|
inputModes?: GameDistributionInputMode[];
|
|
orientation?: GameDistributionOrientation;
|
|
};
|
|
|
|
export type GameDistributionVersionDetail = {
|
|
game: GameDistributionGame;
|
|
version: GameDistributionPrivateVersion & {
|
|
recoveryAction: GameDistributionRecoveryAction;
|
|
frozenMetadata?: GameDistributionVersionFrozenMetadata | null;
|
|
};
|
|
};
|
|
|
|
export type GameDistributionCancelVersionRequest = {
|
|
expectedPublicationRevision: number;
|
|
reason?: string;
|
|
};
|
|
|
|
export type GameDistributionCancelVersionResponse = {
|
|
game: GameDistributionGame;
|
|
version: GameDistributionPrivateVersion;
|
|
replayed: boolean;
|
|
};
|
|
|
|
/**
|
|
* 买断制购买请求体。
|
|
*
|
|
* 客户端带上自己看到的价格:与服务端当前价格不一致时返回 409,不会按旧价扣费。
|
|
*/
|
|
export type GameDistributionPurchaseRequest = {
|
|
expectedPriceMudPoints: number;
|
|
};
|
|
|
|
/** 购买记录快照:每账号每作品最多一条,成交价随购买冻结,之后调价不影响既有所有权。 */
|
|
export type GameDistributionPurchase = {
|
|
purchaseId: string;
|
|
gameId: string;
|
|
priceMudPoints: number;
|
|
createdAt: string;
|
|
};
|
|
|
|
/** 购买响应:`walletBalance` 是扣费后的泥点余额,`replayed` 表示服务端按同一幂等键重放。 */
|
|
export type GameDistributionPurchaseResponse = {
|
|
purchase: GameDistributionPurchase;
|
|
walletBalance: number;
|
|
replayed: boolean;
|
|
};
|
|
|
|
/**
|
|
* 播放会话响应:`playUrl` 是绑定「`gameId` + 用户 + 短时效」的发行入口。
|
|
* 结算入口地址后交给 iframe,会话过期或作品下架后不可继续播放。
|
|
*/
|
|
export type GameDistributionPlaySessionResponse = {
|
|
playUrl: string;
|
|
expiresAt: string;
|
|
};
|