- Create FeedbackService as centralized sound/vibration dispatcher - Create reusable StatusBar widget with StatusDotColor enum (blue/orange/green/red/yellow/amber) - Extend SoundService with beep/error/alert sound types and loop playback - Remove top feedback banners from both registration and boxing pages - Route all feedback through bottom status bar per PRD requirements - Add sound and vibration feedback to boxing module (was completely missing) - Handle network errors with yellow status, general errors with red - Enhance duplicate dialog with alert loop sound that stops on close - Add beep/error/alert sound file selectors in settings page Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
262 lines
10 KiB
Markdown
262 lines
10 KiB
Markdown
# 错误反馈机制重构计划
|
||
|
||
> 版本:v1.0
|
||
> 日期:2026-05-12
|
||
> 状态:已完成
|
||
|
||
---
|
||
|
||
## 1. 背景与目标
|
||
|
||
### 1.1 当前问题
|
||
|
||
对照 PRD(上架登记模块 v1.0 + 装箱编号模块 v1.3)的要求,当前 Flutter 应用在错误反馈方面存在以下差距:
|
||
|
||
| # | 问题 | 影响范围 |
|
||
|---|------|---------|
|
||
| 1 | **装箱模块完全没有声音和振动反馈** — `boxing_page.dart` 未引入 `SoundService` 和 `Vibration` | 装箱编号 |
|
||
| 2 | **反馈横幅位置错误** — 当前在页面顶部显示反馈横幅,PRD 要求统一由底部状态栏承载所有反馈文案 | 上架登记、装箱编号 |
|
||
| 3 | **底部状态栏缺少黄色(网络异常)和红色(错误)状态** — 注册页状态栏圆点仅使用蓝/橙/绿三色,错误信息只出现在顶部横幅 | 上架登记 |
|
||
| 4 | **反馈信息重复显示** — 顶部横幅和底部状态栏同时显示类似内容,造成冗余 | 上架登记、装箱编号 |
|
||
| 5 | **声音类型不足** — `SoundService` 只有 success/failure 两种,PRD 要求区分"扫描提示音"、"错误音"、"成功音"、"连续错误音" | 全局 |
|
||
| 6 | **重复上架弹窗缺少声音差异化** — PRD 要求"连续错误音"+"连续振动",当前只是普通失败音 | 上架登记 |
|
||
|
||
> LED 反馈暂时不实现,后续单独处理。
|
||
|
||
### 1.2 重构目标
|
||
|
||
- **统一反馈入口**:创建集中的 `FeedbackService`,管理声音/振动三通道反馈
|
||
- **消除重复**:移除顶部反馈横幅,所有反馈文案统一由底部状态栏显示
|
||
- **补全缺失**:为装箱模块补齐声音/振动反馈
|
||
- **符合 PRD**:状态栏圆点颜色、文案、持续时间与 PRD 表格一一对应
|
||
|
||
---
|
||
|
||
## 2. PRD 反馈要求汇总
|
||
|
||
### 2.1 上架登记模块(PRD 第 7 节)
|
||
|
||
| 事件 | 屏幕(底部状态栏) | 声音 | 振动 |
|
||
|------|-------------------|------|------|
|
||
| 扫入有效总排号 | 总排号字段填入,绿色高亮 | 短促提示音 | 短震 |
|
||
| 扫入有效货位号 | 货位号字段填入,绿色高亮 | 短促提示音 | 短震 |
|
||
| 扫入无效码 | 红色提示 2 秒:"无效码:{值}" | 错误音 | 短震 |
|
||
| 提交成功 | 绿色提示 1.5 秒 | 成功音 | 长震 |
|
||
| 提交失败 | 红色提示 2 秒 | 失败音 | 短震 |
|
||
| 重复上架错误 | 红色错误弹窗(需手动关闭) | 连续错误音 | 连续震 |
|
||
| 网络异常 | 黄色提示:"网络异常,请检查网络连接" | 失败音 | 短震 |
|
||
|
||
### 2.2 装箱编号模块(PRD 第 9 节)
|
||
|
||
| 事件 | 屏幕(底部状态栏) | 声音 | 振动 |
|
||
|------|-------------------|------|------|
|
||
| 扫入有效总排号 | 显示排产号信息,绿色对勾 | 短促提示音 | 短震 |
|
||
| 扫入无效码 | 红色提示:"无效码,请重新扫描" | 错误音 | 短震 |
|
||
| 提交成功 | 绿色提示 1.5 秒 | 成功音 | 长震 |
|
||
| 重复箱号 | amber 提示 + 输入框 amber 边框 | 失败音 | 短震 |
|
||
| 网络异常 | 黄色提示:"网络异常,请检查网络连接" | 失败音 | 短震 |
|
||
| 切换装箱模式 | 模式控件高亮切换 | 短促提示音 | — |
|
||
|
||
### 2.3 底部状态栏圆点颜色规范
|
||
|
||
| 状态 | 圆点颜色 |
|
||
|------|---------|
|
||
| 空闲等待 | 蓝色 |
|
||
| 提交中 | 橙色 |
|
||
| 成功 | 绿色 |
|
||
| 错误(无效码/提交失败/重复) | 红色 |
|
||
| 网络异常 | 黄色 |
|
||
| 警告(重复箱号) | amber |
|
||
|
||
---
|
||
|
||
## 3. 架构设计
|
||
|
||
### 3.1 新增 `FeedbackService`
|
||
|
||
创建 `lib/services/feedback_service.dart`,作为**唯一的反馈调度中心**。
|
||
|
||
```
|
||
FeedbackService
|
||
├── trigger(FeedbackEvent event) ← 统一入口
|
||
├── _playSound(SoundType type) ← 声音通道
|
||
├── _vibrate(VibrationPattern p) ← 振动通道
|
||
└── 底部状态栏状态由各 Page State 管理,FeedbackService 不直接操控 Widget
|
||
```
|
||
|
||
**FeedbackEvent 枚举:**
|
||
|
||
```dart
|
||
enum FeedbackEvent {
|
||
scanValid, // 扫入有效码 → 短促提示音 + 短震
|
||
scanInvalid, // 扫入无效码 → 错误音 + 短震
|
||
submitSuccess, // 提交成功 → 成功音 + 长震
|
||
submitFailure, // 提交失败 → 失败音 + 短震
|
||
duplicateError, // 重复上架 → 连续错误音 + 连续震
|
||
networkError, // 网络异常 → 失败音 + 短震
|
||
duplicateBoxNo, // 重复箱号 → 失败音 + 短震
|
||
modeSwitch, // 模式切换 → 短促提示音
|
||
}
|
||
```
|
||
|
||
### 3.2 声音扩展
|
||
|
||
扩展 `SoundService`,增加声音类型:
|
||
|
||
| 声音类型 | 方法名 | 用途 |
|
||
|---------|--------|------|
|
||
| 短促提示音 | `playBeep()` | 有效扫码、模式切换 |
|
||
| 错误音 | `playError()` | 无效码 |
|
||
| 成功音 | `playSuccess()` | 提交成功(保留现有) |
|
||
| 失败音 | `playFailure()` | 提交失败、网络异常(保留现有) |
|
||
| 连续错误音 | `playAlertLoop()` | 重复上架弹窗(循环播放直到弹窗关闭) |
|
||
|
||
**实现策略**:
|
||
- 在 `SoundService` 中增加 `beep`、`error`、`alert` 三个可配置路径
|
||
- 设置页面增加对应的文件选择器
|
||
- 若路径未配置则静默跳过(不阻塞业务)
|
||
|
||
### 3.3 底部状态栏组件化
|
||
|
||
抽取 `lib/widgets/status_bar.dart` 为独立 Widget:
|
||
|
||
```dart
|
||
enum StatusDotColor { blue, orange, green, red, yellow, amber }
|
||
|
||
class StatusBar extends StatelessWidget {
|
||
final StatusDotColor dotColor;
|
||
final String text;
|
||
// ...
|
||
}
|
||
```
|
||
|
||
所有页面统一使用此组件,消除重复代码。
|
||
|
||
---
|
||
|
||
## 4. 文件变更清单
|
||
|
||
### 4.1 新增文件
|
||
|
||
| 文件路径 | 说明 |
|
||
|---------|------|
|
||
| `lib/services/feedback_service.dart` | 反馈调度中心,管理声音/振动 |
|
||
| `lib/widgets/status_bar.dart` | 底部状态栏可复用组件 |
|
||
|
||
### 4.2 修改文件
|
||
|
||
| 文件路径 | 变更内容 |
|
||
|---------|---------|
|
||
| `lib/services/sound_service.dart` | 增加 `beep`/`error`/`alert` 声音类型和配置 |
|
||
| `lib/pages/registration_page.dart` | ① 移除顶部反馈横幅 ② 状态栏增加红/黄颜色 ③ 引入 FeedbackService ④ 重复上架弹窗调用连续错误音 |
|
||
| `lib/pages/boxing_page.dart` | ① 移除顶部反馈横幅 ② 引入 FeedbackService ③ 增加声音/振动反馈 ④ 状态栏统一使用 StatusBar 组件 |
|
||
| `lib/pages/settings_page.dart` | 增加声音文件配置项(beep/error/alert) |
|
||
|
||
### 4.3 不变文件
|
||
|
||
| 文件路径 | 原因 |
|
||
|---------|------|
|
||
| `lib/services/api_service.dart` | API 层只负责数据传输,不涉及反馈 |
|
||
| `lib/services/code_parser.dart` | 解析逻辑不变 |
|
||
| `lib/services/scanner_service.dart` | 扫码硬件层不变 |
|
||
| `lib/pages/home_page.dart` | 不涉及作业反馈 |
|
||
| `lib/pages/boxing_detail_page.dart` | 只读展示页,无反馈交互 |
|
||
|
||
---
|
||
|
||
## 5. 实施步骤
|
||
|
||
### 步骤 1:创建 `FeedbackService`(核心)
|
||
|
||
**文件**:`lib/services/feedback_service.dart`
|
||
|
||
- 定义 `FeedbackEvent` 枚举
|
||
- 根据 event 映射声音类型、振动模式
|
||
- 调用 `SoundService`、`Vibration`
|
||
|
||
**预估改动**:~70 行新代码
|
||
|
||
### 步骤 2:扩展 `SoundService`
|
||
|
||
**文件**:`lib/services/sound_service.dart`
|
||
|
||
- 新增 `playBeep()`、`playError()`、`playAlertLoop()`、`stopAlert()` 方法
|
||
- 新增 `beep_path`、`error_path`、`alert_path` 配置存取
|
||
- 所有播放方法在路径未配置时静默返回
|
||
|
||
**预估改动**:~40 行新增
|
||
|
||
### 步骤 3:创建 `StatusBar` 组件
|
||
|
||
**文件**:`lib/widgets/status_bar.dart`
|
||
|
||
- 抽取底部状态栏为独立 Widget
|
||
- 接收 `dotColor`(枚举)和 `text` 参数
|
||
- 统一样式:圆点 + 文字,背景色使用 `surfaceContainerHighest`
|
||
|
||
**预估改动**:~40 行新代码
|
||
|
||
### 步骤 4:重构上架登记页
|
||
|
||
**文件**:`lib/pages/registration_page.dart`
|
||
|
||
变更清单:
|
||
1. **删除**顶部反馈横幅相关代码(`_snackbarMessage`、`_snackbarColor`、`_showFeedback()` 方法中的横幅渲染)
|
||
2. **引入** `FeedbackService`,在以下时机调用 `trigger()`:
|
||
- `_onScan` 无效码 → `scanInvalid`
|
||
- `_onScan` 有效码 → `scanValid`
|
||
- `_submitOne` 成功 → `submitSuccess`
|
||
- `_submitOne` 失败 → `submitFailure`
|
||
- `_submitOne` 重复 → `duplicateError`
|
||
- catch 网络异常 → `networkError`
|
||
3. **修改**底部状态栏:
|
||
- 状态栏圆点颜色增加红色和黄色
|
||
- 错误文案在状态栏中显示(不再在横幅中)
|
||
- 移除 `_successMessage` 单独字段,统一由状态栏管理
|
||
4. **替换** 内联状态栏 Widget 为 `StatusBar` 组件
|
||
5. **修改** `_showDuplicateDialog`:打开时触发 `duplicateError`(连续音/震),关闭时停止
|
||
|
||
**预估改动**:净减 ~30 行(移除横幅 > 新增 trigger 调用)
|
||
|
||
### 步骤 5:重构装箱编号页
|
||
|
||
**文件**:`lib/pages/boxing_page.dart`
|
||
|
||
变更清单:
|
||
1. **删除**顶部反馈横幅相关代码
|
||
2. **删除** 内联底部状态栏代码,替换为 `StatusBar` 组件
|
||
3. **引入** `FeedbackService`,在以下时机调用 `trigger()`:
|
||
- `_onScan` 无效码 → `scanInvalid`
|
||
- `_queryBoxInfo` 成功 → `scanValid`
|
||
- `_submit` 成功 → `submitSuccess`
|
||
- `_submit` 失败 → `submitFailure`
|
||
- `_submit` 重复箱号 → `duplicateBoxNo`
|
||
- catch 网络异常 → `networkError`
|
||
- `_cycleMode` → `modeSwitch`
|
||
4. **修改**状态栏颜色逻辑,增加红色/黄色/amber
|
||
|
||
**预估改动**:净减 ~20 行
|
||
|
||
### 步骤 6:设置页扩展**文件**:`lib/pages/settings_page.dart`- 新增三个声音文件选择器:提示音(beep)、错误音(error)、警报音(alert)- 保持与现有 success/failure 选择器一致的 UI 风格**预估改动**:~60 行新增
|
||
|
||
---
|
||
|
||
## 6. 风险与约束
|
||
|
||
| 风险 | 缓解措施 |
|
||
|------|---------|
|
||
| LED 功能依赖设备硬件 | `LedService` 全面包裹 try-catch,不支持时静默降级 |
|
||
| 声音文件路径未配置 | 所有 `play*()` 方法在路径为空时静默返回 |
|
||
| 连续错误音循环播放 | `playAlertLoop()` 使用循环计数或时长上限,`stopAlert()` 确保可停止 |
|
||
| 振动权限 | Android 需要 `VIBRATE` 权限,检查 `AndroidManifest.xml` 是否已声明 |
|
||
|
||
---
|
||
|
||
## 7. 不在本次范围内
|
||
|
||
- 后端 API 错误格式调整(PRD 已定义,后端自行对齐)
|
||
- 操作日志与操作人员记录
|
||
- 离线缓存与联网同步
|
||
- 新模块开发
|
||
- LED 反馈(后续单独处理)
|