- /files/move|copy go through planImport's new `transfer` variant (keep names, directory expanded to per-file plan) → organization_record + verifyAndImport/executeRecord: per-file ledger resumable, fsproxy, cross-volume directory move = per-file copy→verify→delete then remove empty source dirs; shared plan store + plan_stale; asset_location follows per file, path_binding revised when a whole directory moved (finalizeTransfer); request waits up to FILES_TRANSFER_WAIT_SEC, else executed.pending - /files/organize per-item overrides take effect: season/episodes (domain planOrganize overrides), contentUnitId coverage, targetDir (no binding), operation, slotId (satisfied after verify); warnings only for inapplicable overrides - delete/trash/restore write organization_record (action trash|delete|restore, file-level results; migration 0026) - GET /files/download (admin session, nfo/subtitle/image, size cap, streamed, audited) - GET /files/records?ids|planIds for the organize page - fs race errors map to 503/404/403/400 instead of 500 - runbook updated Co-Authored-By: Claude Opus 5.5 <[email protected]>
@umt/mapping
domain ↔ contracts 的集中映射(W1 审查 B5)。只依赖 @umt/domain 与 @umt/contracts,不依赖 db / adapters。server 各模块需要在两套取值之间转换时一律走这里,不要在模块里各写一份。
db 行(snake_case)↔ contracts(camelCase)的转换不在本包;adapters 的 SiteRelease 以结构类型 SiteReleaseLike 接入(兼容性由 apps/server/test/mapping-compat.test.ts 在类型层保证)。
| 文件 | 导出 | 方向 |
|---|---|---|
download.ts |
toContractDownloadState / toDomainDownloadStatus / mergeObservedDownloadState / DOMAIN_DOWNLOAD_STATUSES |
domain paused_by_threshold/paused_by_user/error/missing_files ↔ contracts paused+PauseReason/failed/missing;业务叠加态(submitting/stalled/importing/imported)在合并观察时保留 |
evidence.ts |
toContractEvidenceLevel / toDomainConfidence / toDomainMediaType / toContractMediaKind / toDomainEntityType / toContractEntityType |
needs_confirm ↔ needs_review,manual → rule_confirmed,rejected → null;tv ↔ series/variety |
identity.ts |
toContractResolveStatus / toReleaseResolutionStatus / rankedToIdentityCreate / localIdentityCandidate |
`auto/ambiguous/no_match → resolved |
slot.ts |
toContractSlotStatus / toDomainSlotStatus / slotSpecFromRow / slotSpecToColumns / toContractSlotEvaluation |
Slot 状态;规则 JSON 列 ↔ SlotSpec;评估结果 → { decision, acceptable, rejected[] } |
release.ts |
toParsedColumns / fromParsedColumns / toContractContentType / toDomainContentType / toContractSegment / toDomainSegment |
ParsedRelease ↔ source_release.parsed + parser_version(版本不符返回 null,重解析) |
site-release.ts |
siteReleaseToSourceRelease / sourceReleaseToSiteRelease / SiteReleaseLike |
站点资源 ↔ SourceReleaseCreate / SourceRelease;downloadUrl 不入库 |
path-binding.ts |
toDomainPathBinding / toPathBindingCreate / parseTemplateVersion |
entityId/scope/season/templateVersion:number/active ↔ mediaEntityId/bindingKind/seasonNumber/templateVersion:string/status |
organize.ts |
organizePlanToRecord / organizePlanToFileResults / organizePlanToCapabilityOutput / organizationStatusFromFiles / toOrganizationAction / toOrganizeOperation / toFileRole |
OrganizePlan → organization_record / organization_file_result 草稿 / organize.plan 输出 |
规则
- 映射表一律写成
Record<一侧的联合类型, …>:domain 增删取值时 typecheck 失败;contracts 侧在test/enums.test.ts按z.enum(...).options逐个断言,新增取值时测试失败。 - 新增映射先写测试(往返、schema 合法)。输出给 contracts 的对象都用对应 zod schema 在测试里
safeParse一遍。