commit 561af4781762086459e32126237f119ae6a5aff9 Author: Joywayer Date: Thu Jun 25 06:25:30 2026 +0800 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d2ff201 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +/.appanalyzer \ No newline at end of file diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 0000000..6c42a04 --- /dev/null +++ b/.mcp.json @@ -0,0 +1,16 @@ +{ + "mcpServers": { + "deveco-mcp": { + "type": "stdio", + "command": "devecocli", + "args": [ + "serve", + "mcp" + ], + "env": { + "PROJECT_PATH": "G:\\Works\\YouleApp\\harmonyos\\gamelobby" + }, + "enabled": true + } + } +} \ No newline at end of file diff --git a/AppScope/app.json5 b/AppScope/app.json5 new file mode 100644 index 0000000..a247b28 --- /dev/null +++ b/AppScope/app.json5 @@ -0,0 +1,11 @@ +{ + "app": { + "bundleName": "com.example.gamelobby", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "buildVersion": "1", + "icon": "$media:layered_image", + "label": "$string:app_name" + } +} diff --git a/AppScope/resources/base/element/string.json b/AppScope/resources/base/element/string.json new file mode 100644 index 0000000..26498de --- /dev/null +++ b/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "GameLobby" + } + ] +} diff --git a/AppScope/resources/base/media/background.png b/AppScope/resources/base/media/background.png new file mode 100644 index 0000000..923f2b3 Binary files /dev/null and b/AppScope/resources/base/media/background.png differ diff --git a/AppScope/resources/base/media/foreground.png b/AppScope/resources/base/media/foreground.png new file mode 100644 index 0000000..eb94275 Binary files /dev/null and b/AppScope/resources/base/media/foreground.png differ diff --git a/AppScope/resources/base/media/layered_image.json b/AppScope/resources/base/media/layered_image.json new file mode 100644 index 0000000..fb49920 --- /dev/null +++ b/AppScope/resources/base/media/layered_image.json @@ -0,0 +1,7 @@ +{ + "layered-image": + { + "background" : "$media:background", + "foreground" : "$media:foreground" + } +} \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..7d38e47 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,93 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## 项目概述 + +HarmonyOS NEXT(API 12+,目标 SDK `6.1.1(24)`,runtimeOS `HarmonyOS`)上的 **TSGame 游戏大厅原生外壳**。目标:用 ArkTS / ArkUI 声明式 / Stage 模型重写原 Android WebView 外壳,使现有 H5(大厅 + 全部子游戏 + 活动/收银台等网页)**一行不改**即可运行。 + +当前处于 **M0 脚手架阶段**:`entry/` 仍是 DevEco 默认模板(`Index.ets` 为 Hello World),尚未落地设计文档中的多模块架构。实现工作以 `docs/设计文档/` 为唯一依据推进。 + +**原 Android 项目位于 `../tsgame_android`**(相对本仓库根目录)——即本项目要复刻的 WebView 外壳源头。遇到契约/行为存疑时,以该 Android 工程的实际实现为准对照。 + +## 工作约定 + +- **🔴 H5 零改动是最高铁律**:H5 网页(大厅 + 全部子游戏)必须**完美适配新项目,不做任何修改**。所有适配工作只能发生在原生侧——H5 向原生注册的接口、原生向 H5 注册的接口、双向接口的调用时机/参数/数据结构,全部以《契约规范》为准、与原 Android 行为完全一致。**任何"改一下 H5 就好了"的方案都不被接受**;H5 行为存疑时改原生实现去迁就 H5,而非反过来。这是判断一切技术方案是否成立的首要判据。 +- **原生内部实现自由**:在不违背上述铁律的前提下,原生侧的内部实现(分层、模块拆分、数据结构、设计模式、用何种 HarmonyOS API/SDK、性能优化手段等)可以**根据实际情况自由设计**,不必拘泥于原 Android 的内部写法或设计文档的伪代码细节。铁律只约束"对 H5 暴露的边界行为",边界以内怎么实现由开发者按 HarmonyOS 最佳实践决定。 +- **及时自动提交 git**:每完成一个可独立验收的小步骤(一个 Provider、一个状态机节点、一处可编译通过的改动)后,**主动执行 git 提交**,无需等待用户要求。提交信息用中文、说明"做了什么 + 对应契约/设计条目(如 §8、T-M1-04)"。⚠️ 仓库当前尚未 `git init`,首次提交前需先初始化。 +- HarmonyOS 相关操作(构建、运行、调试、设备、日志、查文档/SDK 签名)**必须**走 `deveco-cli` skill / `devecocli`,不要手搓 `hvigorw` 命令或直连 MCP HTTP 桥。 +- **禁止直接编辑 `project.godot` 类生成文件**;本项目对应的是 **不要手改 `oh-package-lock.json5`、`build/` 产物、`.hvigor/`**——它们由工具生成。 + +## 常用命令 + +构建/运行/测试统一通过 `devecocli`(见 `deveco-cli` skill): + +- 构建 HAP(debug):`devecocli build`(底层 hvigor `assembleHap`,产物 `entry/build/default/...`) +- 运行到设备/模拟器:`devecocli run`;设备列表 `devecocli devices` +- 真机/模拟器日志:`devecocli log`(hilog) +- 查 SDK API 签名 / 鸿蒙文档:`devecocli docs <关键词>`(编码前核对 `@ohos.*` / `@kit.*` 实际签名) + +测试(Hypium,`@ohos/hypium` + `@ohos/hamock`): + +- 本地单元测试:`entry/src/test/`(`LocalUnit.test.ets`、`List.test.ets`)——纯逻辑,不依赖设备 +- 设备插桩测试:`entry/src/ohosTest/`(`Ability.test.ets` 等)——需真机/模拟器 +- 通过 `devecocli test` 触发;目标:桥引擎 / `VersionResolver` / `MessageCodec` 单测覆盖率 **≥80%** + +代码检查(`code-linter.json5`,仅作用于 `**/*.ets`): + +- 规则集 `@performance/recommended` + `@typescript-eslint/recommended` +- **`@security/no-unsafe-*` 一组加密规则为 `error`**:禁用不安全的 AES/Hash/MAC/DH/DSA/ECDSA/RSA/3DES,写加密代码时务必遵守 + +## 架构(big picture,需读多文件才能掌握) + +> 真正的架构在设计文档里,代码尚未落地。三份 SSOT 文档(修改实现前必读对应章节): +> - `docs/设计文档/TSGame_原生与H5接口契约总规范.md` —— **对外不可变契约**(H5 零改动的判据:handler 名、参数、数据结构、调用时机) +> - `docs/设计文档/TSGame_HarmonyOS框架设计与开发指南.md` —— HarmonyOS 侧如何实现(分层、桥、容器、启动) +> - `docs/设计文档/Plan/` —— 落地计划:`00_总体规划` / `01_任务分解WBS`(任务 ID `T-M{里程碑}-{序号}`)/ `02_测试与验收` / `03_风险登记册` + +### 分层(依赖单向向下,跨层只依赖接口) + +``` +① 应用/编排层 EntryAbility · StartupOrchestrator · 路由 · DI 组装根(AppModule) +② 容器层 BridgeGameContainer(大厅/子游戏) │ GenericWebContainer(通用网页) +③ 桥引擎层 BridgeController · MessageCodec · HandlerRegistry · SettingsProxy +④ 能力层 每个原生能力 = 一个 CapabilityProvider 插件(share/login/location/...) +⑤ 领域服务层 ConfigManager · ResourceManager · AppDataInjector · VersionResolver +⑥ 平台服务层 HttpClient · Downloader · Unzipper · KvStore · LocalUploadServer · TaskScheduler +横切层 Logger/Tracer · ErrorCenter · EventBus · DIContainer · Contracts(TS类型 SSOT) +``` + +目标多模块拆分(HAR/HSP):`entry`(①②) / `feature_bridge`(③) / `feature_capabilities`(④) / `domain_resource`(⑤) / `platform`(⑥) / `contracts`(契约类型) / `common`(横切)。 + +### 三条不可违背的关键约束 + +1. **桥核心对能力零感知**:`feature_bridge` 不 import 任何具体能力,只持 `HandlerRegistry`;能力启动时把自己的 handler 注册进去。新增能力 = 新增一个 `CapabilityProvider`,零侵入桥核心。`contracts` HAR 用 `enum`/`interface` 固化所有 handler 名与 DTO,是全工程唯一来源(杜绝 `finsh`/`getcameraaAddress` 等拼写漂移)。 +2. **出站下发必须在 UI 线程**(§5.5):`runJavaScript` 只能在 UI 线程调用,而能力回调常在非 UI 线程。`BridgeController.dispatch` 内置 UI 线程守卫;Provider 不关心线程,任意线程 `callHandler` 最终都在 UI 线程下发。 +3. **TaskPool 只搬 Sendable**(§9.1):`@Concurrent` 任务跨线程传参/返回值必须是 Sendable,**不能传** callback / `WebviewController` / `UIAbilityContext`。下载/解压进度一律经 `emitter`/`AppStorage` 回 UI 线程,不要跨线程传函数。 + +### 桥协议核心("H5 零改动"命门,100% 复刻 lzyzsd/JsBridge) + +- H5→原生:`Web().onLoadIntercept` 拦截 `yy://`(含 iframe 导航);`yy://return/` 前缀走回执,其余触发 `flushMessageQueue()`。 +- **队列回传不靠 `runJavaScript` 返回值**:`_fetchQueue()` 在 H5 内把 `iframe.src` 置为 `yy://return/_fetchQueue/<队列JSON>`,再次经 `onLoadIntercept` → `handleReturnData` 路由到预登记的 `_fetchQueue` 回调。务必忠实复刻此机制。 +- 原生→H5:`controller.runJavaScript("WebViewJavascriptBridge._handleMessageFromNative('"+json+"');")`;桥 JS 在 `onPageEnd` 注入(直接复用库原文件)。 +- **必须设 `DefaultHandler`(空实现)**:未注册的 handler 名落到默认空实现而不抛错,是"H5 调用永不报错"的最后防线。 + +### 双容器模型 + +- **BridgeGameContainer**(大厅/子游戏,对应 Android `webviewActivity`):挂桥协议;`app_data.js` 必须在**加载页面之前**由 `AppDataInjector` 写入(H5 同步 `