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 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; entryUrl: string; 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; /** 公开切换 CAS 版本号;作者下架与管理员审核都必须回传当前值。 */ publicationRevision: number; currentVersion: GameDistributionVersionSummary | null; 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; packageSha256: string; packageBytes: number; packageFileCount: number; packageEntryPath: 'index.html'; gameMetadata: GameDistributionCreateGameRequest; }; export type GameDistributionPrivateVersion = { versionId: string; gameId: string; versionNumber: number; packageSha256: string; packageBytes: number; status: GameDistributionVersionStatus; /** 游戏公开修订号;撤回与审核动作都必须回传当前值做 CAS。 */ publicationRevision: number; reviewReason?: 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; };