fix(models): 默认模型改用「压缩包 sha256」匹配(转换产物非字节级确定)
实测发现:同一份 zip 连续转换三次得到三个不同的产物 sha256(大小都是 1,152,464 字节,差异集中在 BIN chunk 的十几字节),说明 Assimp 的 GLB 导出不是字节级确定的。 因此原先把「转换产物 sha256」当作默认模型匹配键的设计是错的——换一次转换就失效。 - default-model.ts / default-model-source.ts:种子拆成 sourceSha256(上传压缩包的 sha256,稳定,主匹配键)与 fileSha256(产物 sha256,仅作记录) - models.service.ts:resolveDefaultModelId 依次按 压缩包 sha256 → 产物 sha256 → 名称 匹配,都落空才回落到 VRM 优先规则并告警 - ModelDto 增加 sourceSha256(非压缩包上传为 null) - scripts/import-default-model.mjs:写种子时用 sourceSha256;顺带修掉位置参数解析 (文件当第一个参数时识别不出,会打印用法) - 契约与文档同步:data-models §4.1、model-formats §1.1 记录该确定性结论; README / DSH / tasks/005 的「按产物 sha256 匹配」表述已更正 另附:把模型格式能力的实测矩阵写进 Release 说明(原生只支持 glTF 系; PMX→FBX 失败;VRM→GLB 丢 VRM 扩展;humanoid 骨骼命名覆盖率)。 验证:125 项单测通过;清理掉两个"文件只在本机临时目录"的模型(避免 sha256 去重 导致后续上传不落盘);种子按压缩包 sha256 命中后 defaultModelId 正确解析。
This commit is contained in:
@@ -5,8 +5,9 @@
|
||||
* 运行时只读:`ModelsService.resolveDefaultModelId()` 会先看这里,
|
||||
* 再回落到契约 `data-models.md` §4.1 的 VRM 优先规则。
|
||||
*
|
||||
* 为什么记 sha256 而不是只记 id:换库 / 重建库之后自增 id 会变,
|
||||
* 用转换产物的 sha256 才能稳定指回同一个模型。
|
||||
* 为什么记上传压缩包的 sha256 而不是只记 id:换库 / 重建库之后自增 id 会变;
|
||||
* 而**转换产物的 sha256 每次转换都不一样**(Assimp 的 GLB 导出非字节级确定),
|
||||
* 只有用户上传的那份压缩包才是稳定标识。
|
||||
*
|
||||
* 本文件由脚本重新生成,手改请注意保持结构(键名与类型不要变)。
|
||||
*/
|
||||
@@ -18,8 +19,17 @@ export interface DefaultModelSeed {
|
||||
name: string;
|
||||
/** 模型格式:fbx / glb / vrm */
|
||||
format: string;
|
||||
/** 转换产物文件的 sha256(定位默认模型的依据) */
|
||||
sha256: string;
|
||||
/**
|
||||
* 定位默认模型的主依据:**上传压缩包的 sha256**(用户文件,永远稳定)。
|
||||
* 注意不能用转换产物的 sha256:实测 Assimp 的 GLB 导出不是字节级确定的,
|
||||
* 同一份 zip 每次转换得到的文件 sha256 都不同(BIN chunk 里有十几字节浮动)。
|
||||
*/
|
||||
sourceSha256: string;
|
||||
/**
|
||||
* 转换产物文件的 sha256 —— **仅作记录,不用于匹配**(每次转换都会变)。
|
||||
* 直接上传模型文件(非压缩包)时,它同时就是 sourceSha256。
|
||||
*/
|
||||
fileSha256: string;
|
||||
/** 转换来源说明,例如 `Hatsune Miku.pmx → GLB`;非转换模型为 null */
|
||||
convertedFrom: string | null;
|
||||
/** 最近一次写入时间(ISO 8601) */
|
||||
@@ -28,15 +38,16 @@ export interface DefaultModelSeed {
|
||||
|
||||
/** 由 Hatsune Miku.pmx → GLB 转换得到,已设为默认模型 */
|
||||
export const DEFAULT_MODEL_SEED: DefaultModelSeed = {
|
||||
id: 16,
|
||||
id: 20,
|
||||
name: "初音未来 Hatsune Miku",
|
||||
format: "glb",
|
||||
sha256: "6d0ab0dce97c43dd7de84aba29b09f3b64f2953722d31b5421a39189ff2a8464",
|
||||
sourceSha256: "d1113cf074e19d3a5b4e7025973604c615d6e038ad222070e4748a805ac3c73f",
|
||||
fileSha256: "696c90c7e8514b759f764312d41f9b9069027e62f3aaa2d83ce2c3e475945028",
|
||||
convertedFrom: "Hatsune Miku.pmx → GLB",
|
||||
updatedAt: "2026-09-15T05:45:19.168Z",
|
||||
updatedAt: "2026-09-15T11:34:23.976Z",
|
||||
};
|
||||
|
||||
/** 该种子是否指向一个真实模型 */
|
||||
export function hasDefaultModelSeed(seed: DefaultModelSeed = DEFAULT_MODEL_SEED): boolean {
|
||||
return seed.sha256.trim().length > 0;
|
||||
return seed.sourceSha256.trim().length > 0 || seed.fileSha256.trim().length > 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user