- 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>
10 KiB
10 KiB
错误反馈机制重构计划
版本: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 枚举:
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:
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
变更清单:
- 删除顶部反馈横幅相关代码(
_snackbarMessage、_snackbarColor、_showFeedback()方法中的横幅渲染) - 引入
FeedbackService,在以下时机调用trigger():_onScan无效码 →scanInvalid_onScan有效码 →scanValid_submitOne成功 →submitSuccess_submitOne失败 →submitFailure_submitOne重复 →duplicateError- catch 网络异常 →
networkError
- 修改底部状态栏:
- 状态栏圆点颜色增加红色和黄色
- 错误文案在状态栏中显示(不再在横幅中)
- 移除
_successMessage单独字段,统一由状态栏管理
- 替换 内联状态栏 Widget 为
StatusBar组件 - 修改
_showDuplicateDialog:打开时触发duplicateError(连续音/震),关闭时停止
预估改动:净减 ~30 行(移除横幅 > 新增 trigger 调用)
步骤 5:重构装箱编号页
文件:lib/pages/boxing_page.dart
变更清单:
- 删除顶部反馈横幅相关代码
- 删除 内联底部状态栏代码,替换为
StatusBar组件 - 引入
FeedbackService,在以下时机调用trigger():_onScan无效码 →scanInvalid_queryBoxInfo成功 →scanValid_submit成功 →submitSuccess_submit失败 →submitFailure_submit重复箱号 →duplicateBoxNo- catch 网络异常 →
networkError _cycleMode→modeSwitch
- 修改状态栏颜色逻辑,增加红色/黄色/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 反馈(后续单独处理)