上传文件至「/」

This commit is contained in:
2026-07-13 03:54:20 +00:00
parent 976c14cd65
commit dabcd9a951
+297
View File
@@ -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 状态,成功后显示 "✓ 添加成功" 浮层 Toast1.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`
### 技术方案
- 使用 **SPPSerial 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` | 模拟数据配置 |