From 80e6718e8a1ef01118a66e091b3e0582c971c61a Mon Sep 17 00:00:00 2001 From: Yuyi-Oak <1722157266@qq.com> Date: Fri, 31 Jul 2026 18:28:02 +0800 Subject: [PATCH] =?UTF-8?q?fix(sync):=20=E5=AF=B9=E9=BD=90=20localized=20?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E5=A5=91=E7=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CURRENT_STATUS.md | 2 +- docs/reference/rpc-backend-api.md | 9 ++++++--- docs/reports/CURRENT_GAPS.md | 2 +- infrastructure/src/bin/bat_official_sync.rs | 13 ++++++++----- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/CURRENT_STATUS.md b/CURRENT_STATUS.md index 5dcf7b5..8d77b19 100644 --- a/CURRENT_STATUS.md +++ b/CURRENT_STATUS.md @@ -28,7 +28,7 @@ Rust 侧官方日服资源链路已经从实验验证推进到正式入口: 11. 官方同步会拒绝危险输出目录、路径逃逸和现有 symlink 路径组件;下载目标、`.part`、manifest、snapshot、PID、status、log 和控制锁文件不会跟随 symlink,daemon 状态类文件默认以 `0600` 权限创建。 12. `/official-version-state.json` 会明确保存当前已完成版本、正在拉取版本、上一个可用版本和失败版本;同一 app version、bundle version 和 Addressables root 的失败只保留最新一条,同一版本开始重新拉取或后续发布成功时会清理对应失败记录;`bat status` 会显示最后成功时间、下次检查时间、最后错误摘要、当前阶段、当前下载 URL 进度、版本状态摘要、最近历史失败版本和原因、结构化日志路径和轮转日志路径,人类可读输出不会把完整版本状态 JSON 内联打印。 13. 资源导入链路已支持 CAS + `ResourceRepository` 索引写入,官方同步可用 `--import-repository` / `BAT_IMPORT_REPOSITORY=1` 在已校验 release 发布后触发导入,默认 CAS 路径为 `/.cas`、SQLite 索引为 `/resources.sqlite`,也可通过 `--import-cas-root`、`--import-resource-db`、`BAT_IMPORT_CAS_ROOT`、`BAT_IMPORT_RESOURCE_DB` 覆盖;`resource.index` RPC 可按类型、hash、路径模式分页查询现有索引,数据库不存在时返回 `available=false` 且不会创建空库。`Resource` metadata 已通过 `metadata_json` 兼容迁移保存 release、平台、bundle path、parse status、TextAsset 名称和 TextUnit 数量/格式;当前/上一个/结构变化 catalog、失败 staging 复用、403/404、hash mismatch、CRC、metadata 迁移与 UnityFS 边界校验均有离线回归 fixture 或单测覆盖。 -14. 非 dry-run 官方同步在校验完成并发布后,会先对比上一完整 release 与当前 release 的 `official-download-manifest.json`,在当前 release 下写入 `official-resource-changes.json` 和 `crowdin-translation-handoff.json`;同一 destination 只有 size 或 BLAKE3 改变才算 modified,仅 URL/CDN 根变化但内容相同不会触发解析/翻译候选。随后刷新 `official-parse-cache.json` 和 `official-textunit-index.json`,并从 Added/Modified 资源、parse cache 与 TextUnit 明细索引派生 `official-textunit-tasks.json` 和 `crowdin-textunit-queue.json`;删除资源只进入差异记录,不进入 TextUnit/Crowdin 队列。`parse.text_units` 和 `parse.errors` RPC/CLI 可按 destination、archive entry、path id、class id、field path 和 format 查询当前 release 的 TextUnit 明细与解析错误;TextUnit 已包含 class id、field path、字段 offset/byte size 等可追溯定位。Crowdin 当前仅预留本地离线队列,不发网络请求;up-to-date 轮询在已有有效解析缓存、TextUnit 明细索引和 TextUnit 队列时只读取摘要,不重复解析。官方同步报告默认 `localized_release_status=not_localized`,含义是原版资源已经发布、汉化资源未发布;UnityFS TextAsset patch 发布成功后会写 `localized-patch-manifest.json`,校验 hash/size/diff/rollback 后才允许 `localized.status` 返回 `localized`。`bat` 首次启动会在二进制所在目录释放 `.env` 配置模板(`0600`),之后每次启动自动加载(不覆盖已存在的环境变量),支持 `BAT_OUTPUT`/`BAT_LOCALIZED_OUTPUT`/`BAT_IMPORT_REPOSITORY`/`BAT_IMPORT_CAS_ROOT`/`BAT_IMPORT_RESOURCE_DB`/`BAT_STATE_DIR`/`BAT_AUTO_DISCOVER`/`BAT_WATCH`/`BAT_DAEMON`/`BAT_PROXY` 等键,实现编辑 `.env` 后无参启动;优先级为命令行参数 > 进程环境变量 > `.env` > 内置默认值,`BAT_SKIP_ENV_FILE=1` 可整体禁用;Redis 键为预留。daemon 任务历史持久化在 `/bat-tasks.json`(版本化、`0600` 原子写),重启后任务经 `task.*` 仍可查,中断任务标记 `task_interrupted`(`BAT-ERR-700005`)。 +14. 非 dry-run 官方同步在校验完成并发布后,会先对比上一完整 release 与当前 release 的 `official-download-manifest.json`,在当前 release 下写入 `official-resource-changes.json` 和 `crowdin-translation-handoff.json`;同一 destination 只有 size 或 BLAKE3 改变才算 modified,仅 URL/CDN 根变化但内容相同不会触发解析/翻译候选。随后刷新 `official-parse-cache.json` 和 `official-textunit-index.json`,并从 Added/Modified 资源、parse cache 与 TextUnit 明细索引派生 `official-textunit-tasks.json` 和 `crowdin-textunit-queue.json`;删除资源只进入差异记录,不进入 TextUnit/Crowdin 队列。`parse.text_units` 和 `parse.errors` RPC/CLI 可按 destination、archive entry、path id、class id、field path 和 format 查询当前 release 的 TextUnit 明细与解析错误;TextUnit 已包含 class id、field path、字段 offset/byte size 等可追溯定位。Crowdin 当前仅预留本地离线队列,不发网络请求;up-to-date 轮询在已有有效解析缓存、TextUnit 明细索引和 TextUnit 队列时只读取摘要,不重复解析。官方同步报告默认 `localized_release_status=not_localized`,含义是原版资源已经发布、汉化资源未发布;UnityFS TextAsset patch 发布成功后会写 `localized-patch-manifest.json`,校验 hash/size/diff/rollback 后才允许 `localized.status` 返回 `status=published`、`status_code=localized.published` 和 `localized_release_status=localized`。`bat` 首次启动会在二进制所在目录释放 `.env` 配置模板(`0600`),之后每次启动自动加载(不覆盖已存在的环境变量),支持 `BAT_OUTPUT`/`BAT_LOCALIZED_OUTPUT`/`BAT_IMPORT_REPOSITORY`/`BAT_IMPORT_CAS_ROOT`/`BAT_IMPORT_RESOURCE_DB`/`BAT_STATE_DIR`/`BAT_AUTO_DISCOVER`/`BAT_WATCH`/`BAT_DAEMON`/`BAT_PROXY` 等键,实现编辑 `.env` 后无参启动;优先级为命令行参数 > 进程环境变量 > `.env` > 内置默认值,`BAT_SKIP_ENV_FILE=1` 可整体禁用;Redis 键为预留。daemon 任务历史持久化在 `/bat-tasks.json`(版本化、`0600` 原子写),重启后任务经 `task.*` 仍可查,中断任务标记 `task_interrupted`(`BAT-ERR-700005`)。 仍需明确:这不是完整产品完成。完整 AssetBundle 重打包、翻译、Web、以及 `bat-api` 的服务器联调/可选业务扩展仍是后续工作;G-008(产品级 Go 同步 CLI)已决策关闭。真实官方网络全量拉取 smoke 已固化(G-018 已关闭);真实大文件与运行报告默认在 `/tmp` 隔离目录,不纳入 Git。Go 细节见 `docs/reports/GO_STATUS.md`。 diff --git a/docs/reference/rpc-backend-api.md b/docs/reference/rpc-backend-api.md index 59c326f..e455e83 100644 --- a/docs/reference/rpc-backend-api.md +++ b/docs/reference/rpc-backend-api.md @@ -199,9 +199,12 @@ offset 和 error。TypeTree-covered managed reference 字段会进入结构化 `localized.status` 严格按 daemon / `.env` 中的 `BAT_LOCALIZED_OUTPUT` 或 `--localized-output` 查询汉化产物目录,不把 `./bat-resources` 与 `./bat-localized` 混用。当前支持未汉化发布状态和已汉化发布状态的只读报告。 -返回 `localized` 的条件是:`localized-version-state.json` 的官方 release ID -匹配当前官方 release,`current` symlink 指向汉化发布根下对应的 -`versions/`,并且该版本目录中的 +`status` / `status_code` 使用生命周期短状态和稳定状态码,例如 +`pending` / `localized.pending`、`stale` / `localized.stale`、`published` / +`localized.published`;旧的 `localized` / `not_localized` 业务标签放在 +`localized_release_status`。返回 `localized_release_status=localized` 的条件是: +`localized-version-state.json` 的官方 release ID 匹配当前官方 release, +`current` symlink 指向汉化发布根下对应的 `versions/`,并且该版本目录中的 `localized-patch-manifest.json` 存在且 release ID 匹配。响应会返回 `patch_manifest_path`、`patch_manifest_available`、 `patch_manifest_matches_release`、`patch_file_count`、 diff --git a/docs/reports/CURRENT_GAPS.md b/docs/reports/CURRENT_GAPS.md index cd72387..1633f24 100644 --- a/docs/reports/CURRENT_GAPS.md +++ b/docs/reports/CURRENT_GAPS.md @@ -359,7 +359,7 @@ - 官方同步报告新增 `localized_release_status=not_localized`,明确表示原版资源已发布、汉化资源未发布。 - `LocalizedPatchService` 已具备将给定汉化文件按官方相对路径发布到 `--localized-output` / `BAT_LOCALIZED_OUTPUT` 指定目录下的 `.staging/`、校验后移动到 `versions/`、原子切换 `current` 并写入 `localized-version-state.json` 的基础能力。 - `LocalizedPatchService` 已写入结构化 `localized-patch-manifest.json`,记录 TextAsset 操作、原始/汉化 hash、size、byte delta 和 rollback 信息;发布前后会校验 manifest hash/size 与 current symlink,失败时清理 staging / 未完成 version。 -- `localized.status` RPC 会读取 `.env` / daemon 配置中的汉化输出目录,校验汉化状态是否匹配当前官方 release,且要求 patch manifest 存在并匹配 release,避免写死 `./bat-localized` 或误报手工状态。 +- `localized.status` RPC 会读取 `.env` / daemon 配置中的汉化输出目录,校验汉化状态是否匹配当前官方 release,且要求 patch manifest 存在并匹配 release,避免写死 `./bat-localized` 或误报手工状态;其中 `status` / `status_code` 返回生命周期状态,`localized_release_status` 保留 `localized` / `not_localized` 发布标签。 仍未完成: diff --git a/infrastructure/src/bin/bat_official_sync.rs b/infrastructure/src/bin/bat_official_sync.rs index 48cdf23..40624c6 100644 --- a/infrastructure/src/bin/bat_official_sync.rs +++ b/infrastructure/src/bin/bat_official_sync.rs @@ -3055,7 +3055,7 @@ fn build_localized_status_report( let state_path = localized_root.join(LOCALIZED_VERSION_STATE_FILE); let current_path = localized_root.join(LOCALIZED_CURRENT_LINK); let state = read_localized_version_state(&localized_root)?; - let mut status = "not_localized"; + let mut localized_release_status = "not_localized"; let mut published_version_path = None; let mut matches_current_official_release = false; let mut current_points_to_published_version = false; @@ -3099,14 +3099,14 @@ fn build_localized_status_report( && current_points_to_published_version && patch_manifest_matches_release { - status = "localized"; + localized_release_status = "localized"; flow_status_code = ReleaseFlowStatusCode::LocalizedPublished; published_version_path = Some(candidate); } } } } - let (_, status_code, status_phase, status_terminal, status_retryable) = + let (status, status_code, status_phase, status_terminal, status_retryable) = flow_status_fields(flow_status_code); Ok(serde_json::json!({ @@ -3116,6 +3116,7 @@ fn build_localized_status_report( "status_phase": status_phase, "status_terminal": status_terminal, "status_retryable": status_retryable, + "localized_release_status": localized_release_status, "official_current_version_id": official_version_id, "localized_output_root": localized_root, "state_path": state_path, @@ -10913,8 +10914,9 @@ mod tests { let value = serde_json::to_value(&envelope).unwrap(); assert_eq!(value["ok"], true); assert_eq!(value["data"]["available"], true); - assert_eq!(value["data"]["status"], "localized"); + assert_eq!(value["data"]["status"], "published"); assert_eq!(value["data"]["status_code"], "localized.published"); + assert_eq!(value["data"]["localized_release_status"], "localized"); assert_eq!(value["data"]["official_current_version_id"], "v-current"); assert_eq!(value["data"]["matches_current_official_release"], true); assert_eq!(value["data"]["current_points_to_published_version"], true); @@ -10972,8 +10974,9 @@ mod tests { let value = serde_json::to_value(&envelope).unwrap(); assert_eq!(value["ok"], true); assert_eq!(value["data"]["available"], true); - assert_eq!(value["data"]["status"], "not_localized"); + assert_eq!(value["data"]["status"], "stale"); assert_eq!(value["data"]["status_code"], "localized.stale"); + assert_eq!(value["data"]["localized_release_status"], "not_localized"); assert_eq!(value["data"]["matches_current_official_release"], false); assert_eq!( value["data"]["published_version_path"],