Files
opendance-web/web-server/src/scenes/scene.serializer.ts
T
opendance 584b7eaca9 feat(scenes): 场景管理后端 + 契约 0.5.0/0.7.0/0.4.0(T-WEB-015)
契约先行(三端逐字节一致,已同步 windows/android):
- rest-api -> 0.5.0:新增 GET/POST /api/scenes、GET/PATCH/DELETE /api/scenes/{id}
- data-models -> 0.7.0:新增 §7 Scene 与 7.1 SceneConfig(物理/环境/出生点/相机)
- database -> 0.4.0:新增 scenes 表(config/stats 用 MEDIUMTEXT,迁移可逆)

后端:
- entities/scene.entity.ts + 迁移 AddScenes1789650000000(scenes 表)
- scenes/scene-config.ts:SceneConfig 定义 + 默认值 + **边界收敛**(越界夹紧、
  非法枚举回退),保证客户端拿到的参数一定可解释;服务端只存参数不跑物理
- scenes/scenes.service.ts:上传(.glb/.gltf 直接导入;.zip/.pmx/.fbx 转 GLB,
  场景不做 humanoid 重绑定、不转 VRM)、列表/详情/更新/软删除、缩略图复用模型
  光栅化器、分析复用模型检查器、config 同时接受 JSON 字符串与平铺字段
- scenes/scenes.controller.ts + scenes.module.ts(app.module 注册)
- 审核复用 /api/reviews/:type/:id:TargetType.Scene 接入待审列表与状态流转
- catalog/export 增 scenes[],目录指纹纳入场景变更
- 枚举:TargetType.Scene、AssetKind.Scene / SceneThumbnail

测试:新增 test/scene-config.spec.ts 6 项(默认值/夹紧/枚举回退/出生点上限/颜色/
序列化);规模 19 文件 / 243 项;check-routes 35 条一致。版本 0.6.0。

注:管理端场景页(GLB 预览 + 物理参数可视化)与 Windows 场景选择留待下一轮。
2026-09-17 00:27:08 +08:00

53 lines
1.5 KiB
TypeScript

import { PublishStatus, SourceType } from '../common/enums';
import { Scene } from '../entities/scene.entity';
import type { SceneConfig } from './scene-config';
/** 契约 data-models.md §7 的对外表示 */
export interface SceneDto {
id: number;
name: string;
glbUrl: string | null;
thumbnailUrl: string | null;
fileSize: number | null;
fileSha256: string | null;
conversionNote: string | null;
/** 物理 / 环境 / 出生点 / 相机(服务端只存参数,仿真在客户端) */
config: SceneConfig | null;
stats: Record<string, unknown> | null;
sourceType: SourceType;
sourceName: string;
sourceUrl: string;
license: string;
author: string;
status: PublishStatus;
createdAt: string;
updatedAt: string;
}
export interface SceneUrlLookup {
glbUrl: string | null;
thumbnailUrl: string | null;
}
export function toSceneDto(scene: Scene, urls: SceneUrlLookup): SceneDto {
return {
id: scene.id,
name: scene.name,
glbUrl: urls.glbUrl,
thumbnailUrl: urls.thumbnailUrl,
fileSize: scene.fileSize ?? null,
fileSha256: scene.fileSha256 ?? null,
conversionNote: scene.conversionNote ?? null,
config: scene.config ?? null,
stats: scene.stats ?? null,
sourceType: scene.sourceType,
sourceName: scene.sourceName,
sourceUrl: scene.sourceUrl,
license: scene.license,
author: scene.author,
status: scene.status,
createdAt: scene.createdAt.toISOString(),
updatedAt: scene.updatedAt.toISOString(),
};
}