diff --git a/PROJECT_DOCUMENTATION.md b/PROJECT_DOCUMENTATION.md new file mode 100644 index 0000000..aac03a4 --- /dev/null +++ b/PROJECT_DOCUMENTATION.md @@ -0,0 +1,297 @@ +# 记账小助手 — 项目文档 + +## 项目概述 + +| 项目 | 说明 | +|------|------| +| **应用名称** | 记账小助手(keeping_accounts) | +| **平台** | HarmonyOS (API 6.1 / Stage 模式) | +| **Bundle Name** | `com.example.keeping_accounts` | +| **版本** | 1.0.0 (versionCode: 1000000) | +| **开发框架** | ArkTS + ArkUI 声明式 UI | +| **构建工具** | Hvigor | +| **目标设备** | 手机 (phone) + 手表(通过蓝牙同步) | +| **跨平台** | 支持 Android / iOS(通过 ArkUI-X) | + +--- + +## 目录结构 + +``` +keeping_accounts/ +├── AppScope/ # 应用全局配置 +│ ├── app.json5 # Bundle 名称、图标、版本号 +│ └── resources/ # 全局资源(图标、背景、字符串) +├── build-profile.json5 # 签名配置、SDK 版本、模块列表 +├── hvigorfile.ts # Hvigor 构建入口 +├── code-linter.json5 # 代码检查规则 +├── lodefusi/ # 主模块(entry) +│ ├── build-profile.json5 # 模块构建配置 +│ ├── oh-package.json5 # 模块依赖声明 +│ ├── hvigorfile.ts # 模块构建脚本 +│ ├── obfuscation-rules.txt # 代码混淆规则 +│ ├── src/ +│ │ ├── main/ +│ │ │ ├── module.json5 # 模块清单(权限、Ability、页面路由) +│ │ │ ├── ets/ +│ │ │ │ ├── pages/ +│ │ │ │ │ └── Index.ets # 主页面(启动页 + Tab 导航) +│ │ │ │ ├── lodefusiability/ +│ │ │ │ │ └── LodefusiAbility.ets # UIAbility 入口 +│ │ │ │ ├── components/ +│ │ │ │ │ ├── Overview.ets # 总览页面(月度统计) +│ │ │ │ │ ├── AddExpense.ets # 记账页面(手动 + AI) +│ │ │ │ │ ├── ExpenseRecords.ets # 记录列表(按日期分组) +│ │ │ │ │ ├── Device.ets # 设备页面(蓝牙手表同步) +│ │ │ │ │ └── BluetoothTransfer.ets # 蓝牙传输模态页 +│ │ │ │ └── model/ +│ │ │ │ ├── ExpenseModel.ets # 数据模型 + 本地持久化 +│ │ │ │ ├── ApiService.ets # HTTP API 服务 +│ │ │ │ ├── ApiConfig.ets # 服务器地址配置 +│ │ │ │ └── BluetoothService.ets # 蓝牙 SPP 传输服务 +│ │ │ └── resources/ # 模块资源(颜色、字符串、图标) +│ │ ├── mock/ # Mock 数据配置 +│ │ ├── ohosTest/ # 自动化测试 +│ │ └── test/ # 本地单元测试 +│ └── ... +└── .arkui-x/ # ArkUI-X 跨平台配置(Android/iOS) + ├── android/ # Android 工程 + └── ios/ # iOS 工程 +``` + +--- + +## 架构设计 + +``` +┌─────────────────────────────────────────────────┐ +│ Index.ets (主入口) │ +│ 启动页 → Tab 导航 (总览/记账/设备) │ +├──────────┬──────────────────┬───────────────────┤ +│ Overview │ AddExpense │ DevicePage │ +│ 月度统计 │ 手动记账+AI分类 │ 蓝牙设备管理+同步 │ +├──────────┴──────────────────┴───────────────────┤ +│ ExpenseRecords (记录列表) │ +│ 按日期分组、分类筛选、长按删除 │ +├─────────────────────────────────────────────────┤ +│ BluetoothTransfer (蓝牙传输) │ +│ 发送选择 → 搜索设备 → 连接 → 传输 │ +├──────────┬──────────────────┬───────────────────┤ +│ExpenseModel│ ApiService │ BluetoothService │ +│ 本地存储 │ HTTP 同步 │ SPP 串口蓝牙 │ +│Preferences│ /api/bills │ RFCOMM 通道 │ +└──────────┴──────────────────┴───────────────────┘ +``` + +**数据流**: +1. 用户在记账页添加记录 → `ExpenseModel.addExpense()` 写入本地 Preferences +2. 同时异步调用 `ApiService.submitBill()` 同步到远端服务器(失败则静默降级,仅保存本地) +3. 总览页通过 `@Consume` 获取全量数据,按月筛选后计算分类汇总 +4. 设备页通过 `BluetoothService.connectAndSend()` 将选中数据以 JSON 形式 SPP 传输给手表 + +--- + +## 模块详解 + +### 1. 主入口 — `Index.ets` + +- **启动页**:显示深蓝渐变背景 + "¥" 图标 + "这个月你省钱了吗?" slogan,带有淡入动画(1s)+ 2.2s 后淡出 +- **主界面**:3 个 Tab 页切换(底部导航栏),每个 Tab 有自定义图标: + - 总览(环形图标) + - 记账("+" 图标) + - 设备(手表图标) +- 页面级别的 `@Provide expenses` 将消费数据共享给所有子组件 + +### 2. 总览页 — `Overview.ets` + +- **环形进度卡片**:总收入(绿)、总支出(红)、结余(蓝),显示当月合计金额 +- **消费分类网格**:按金额降序排列,每个分类显示颜色圆点、金额、线性进度条 +- **收入来源网格**:同理展示收入分类 +- **空状态**:当月无记录时显示 "💰 本月暂无记录" +- **刷新按钮**:从本地存储重新加载数据 + +### 3. 记账页 — `AddExpense.ets` + +- **类型切换**:消费(红色主题)/ 收入(绿色主题),切换时重置分类选择 +- **金额输入**:仅允许数字和小数点,自动过滤非法字符,显示 "¥" 前缀 +- **分类选择**: + - **手动模式**:4 列 Grid 展示分类色块,选中项高亮 + 阴影 + - **AI 智能分类模式**(仅消费类型可用): + - 用户输入消费内容描述,开启 Toggle 开关 + - 调用 `/api/transfer` 接口进行 AI 分类 + - 显示加载动画 "AI 正在识别..." + - 识别结果以蓝色标签展示,自动填充分类 + - 分类完成后自动调用 `/api/bills` 同步到服务器 +- **备注输入**:选填文本,AI 模式下替代为描述输入框 +- **提交按钮**:带 loading 状态,成功后显示 "✓ 添加成功" 浮层 Toast(1.8s 自动消失) +- **服务端同步**:静默失败策略,服务器不可用时 Toast 提示但数据已保存在本地 + +### 4. 记录列表 — `ExpenseRecords.ets` + +- **按日期分组**:`yyyy-MM-dd` 为 key,每日显示日期标题 + 当日收支汇总 +- **记录行**: + - 左侧:分类颜色圆点 + 分类名 + AI 标签(蓝色胶囊)+ 备注 + - 右侧:金额(消费红/收入绿)+ 格式化时间 `MM-dd HH:mm` +- **长按删除**:触发确认对话框,确认后从本地存储删除 +- **分类筛选**:支持按分类过滤,顶部显示筛选标签 + 记录数量 + 返回按钮 +- **空状态**:无记录时显示 "📋 暂无消费记录" + +### 5. 设备页 — `DevicePage.ets` + +- **蓝牙状态栏**:显示蓝牙开关状态(绿点/灰点),刷新按钮 +- **已配对设备列表**: + - 显示设备名称、连接状态(绿点/灰点)、上次同步时间、已同步条数 + - 点击"连接"按钮模拟连接 + - 点击"同步"按钮将选中数据通过蓝牙发送 +- **同步设置**: + - 发送消费记录(Toggle 开关) + - 发送收入记录(Toggle 开关) +- **手表应用设置**: + - 手表端显示消费明细 / 收入明细 / 震动提醒(预留 UI) +- **同步结果**:顶部显示同步进度/结果横幅 + +### 6. 蓝牙传输 — `BluetoothTransfer.ets` + +这是一个模态弹窗组件,支持发送和接收两种模式: + +**发送流程**: +1. 选择记录(Checkbox 多选 + 全选) +2. 搜索附近的蓝牙设备(12s 超时) +3. 显示设备列表(设备名 + 地址 + 连接按钮) +4. 连接设备并通过 SPP 传输选中的 JSON 数据 +5. 显示传输进度百分比 + +**接收流程**: +1. 等待发送方连接(显示本机蓝牙名称) +2. 自动接收数据(`__BT_EOF__` 标记结束) +3. 接收完成后显示记录数,提供"导入数据"按钮 +4. 导入将数据合并到本地存储 + +**状态机**:`IDLE → SELECTING → SCANNING → CONNECTING → TRANSFERRING → COMPLETE / ERROR` +(接收时:`IDLE → WAITING → TRANSFERRING → COMPLETE / ERROR`) + +--- + +## 数据模型 + +### ExpenseItem + +```typescript +interface ExpenseItem { + id: string; // 唯一标识(时间戳+计数器+随机数) + type: '消费' | '收入'; // 交易类型 + category: string; // 分类名称(餐饮、工资等) + amount: number; // 金额 + remark: string; // 备注 + message: string; // AI 分类返回的建议消息 + createTime: string; // ISO 时间字符串 +} +``` + +### 预设分类 + +| 类型 | 分类项 | +|------|--------| +| **消费** | 餐饮、医药、数码、还款、购物、交通、娱乐、住房、日用、服饰、教育、人情、其他 | +| **收入** | 工资、奖金、理财、兼职、报销、退款、红包、其他收入 | + +每个分类有对应的颜色值,用于 UI 中的圆点和进度条等元素。 + +### 数据持久化 + +- **本地存储**:使用 HarmonyOS `preferences` API,键值对存储在 `expense_store`,key 为 `expenses` +- **数据迁移**:`migrateItem()` 函数兼容旧版数据结构(如 `mash` → `amount`、`name` → `category`、`text/note` → `remark` 等字段映射) +- **生成 ID**:`Date.now().toString(36) + 计数器.toString(36) + 随机数` + +--- + +## API 服务 + +### 服务器配置 + +- 配置文件存储在 `app_config` preferences 中,key 为 `server_url` +- 默认地址:`http://192.168.1.1:9255` + +### 接口列表 + +| 接口 | 方法 | 路径 | 说明 | +|------|------|------|------| +| 提交账单 | POST | `/api/bills` | 将消费/收入记录同步到服务器 | +| AI 分类 | POST | `/api/transfer` | 根据消费描述自动推荐分类 | +| 查询账单 | GET | `/api/bills?type=&category=` | 按类型和分类筛选查询 | + +### 错误处理 + +- 网络不可用、超时(连接 5s / 读取 10s)、服务端异常均返回 `{ code: -1, msg: '...' }` +- 调用方采用静默降级策略,不影响本地数据保存 + +--- + +## 蓝牙服务 — `BluetoothService` + +### 技术方案 + +- 使用 **SPP(Serial Port Profile)** 基于 RFCOMM 通道传输 +- UUID:`00001101-0000-1000-8000-00805F9B34FB` +- 数据格式:JSON 字符串 + `__BT_EOF__` 结束标记 +- 分块传输:每块 512 字节 + +### 主要功能 + +| 方法 | 说明 | +|------|------| +| `isBluetoothEnabled()` | 检查蓝牙是否开启 | +| `enableBluetooth()` | 打开蓝牙 | +| `waitForBluetoothOn(timeoutMs)` | 等待蓝牙开启(监听 `stateChange` 事件) | +| `getLocalDeviceName()` | 获取本机蓝牙名称 | +| `getPairedDevices()` | 获取已配对设备列表 | +| `startDiscovery(cb)` | 搜索附近蓝牙设备 | +| `stopDiscovery()` | 停止搜索 | +| `startServer(onResult, onProgress)` | 启动 SPP 服务端监听(接收模式) | +| `connectAndSend(deviceId, data, onResult, onProgress)` | 连接设备并发送数据 | +| `stopServer()` / `cancelTransfer()` / `destroy()` | 终止/清理资源 | + +### UTF-8 编解码 + +- 内建 `strToBytes()` 和 `bytesToStr()` 实现 UTF-8 编解码(支持中文),不依赖外部库 + +--- + +## 权限列表 + +| 权限 | 用途 | +|------|------| +| `ohos.permission.INTERNET` | HTTP API 通信 | +| `ohos.permission.USE_BLUETOOTH` | 蓝牙数据传输 | +| `ohos.permission.DISCOVER_BLUETOOTH` | 搜索附近蓝牙设备 | + +--- + +## 离线策略 + +整体采用 **本地优先(Local-First)** 架构: + +1. 用户操作直接写入本地 Preferences,操作体验不受网络影响 +2. 服务器同步为异步旁路流程,成功/失败均不影响本地数据完整性 +3. 蓝牙传输同样先将接收数据合并到本地存储,再触发 UI 刷新 +4. 总览页刷新按钮从本地存储重新读取,确保数据一致性 + +--- + +## 跨平台支持(ArkUI-X) + +项目包含 `.arkui-x/` 目录,配置了 Android 和 iOS 的原生工程: +- **Android**:Gradle 构建,包含 `arkui_android_adapter.jar` 和相关 `.so` 库 +- **iOS**:Xcode 工程(`app.xcodeproj`),包含 `AppDelegate` 和 `LodefusiLodefusiAbilityViewController` + +这意味着同一套 ArkTS 代码可以编译为 HarmonyOS / Android / iOS 三端应用。 + +--- + +## 测试 + +| 测试类型 | 路径 | 说明 | +|----------|------|------| +| 本地单元测试 | `src/test/List.test.ets`、`LocalUnit.test.ets` | 逻辑层测试 | +| 自动化测试 | `src/ohosTest/ets/test/Ability.test.ets`、`List.test.ets` | 设备上运行的 UI 测试 | +| Mock 配置 | `src/mock/mock-config.json5` | 模拟数据配置 |