离线订单可视化与手动上传设计
一、背景与目标
当前离线订单保存在本地 SQLite 数据库 order_db.db 的 t_order 表中,由首页 180 秒定时任务按系统配置走 HTTP 或 MQTT 通道自动补传。现有流程缺少面向设备操作人员的可视化入口,无法直接查看尚未完成的订单,也无法对单笔订单发起人工补传。
本次新增独立的“离线订单”页面,使操作人员能够查看仍需处理的离线订单并手动上传,同时保持原有定时同步机制、状态定义和自动重试策略不变。
- 首页新增独立“离线订单”入口。
- 只展示待上传和上传中的订单。
- 支持单笔手动上传,并动态反映定时任务和手动任务造成的状态变化。
- 手动与定时上传通过数据库原子抢占互斥。
- 手动上传单次等待最长 30 秒;现有 10 分钟兜底恢复规则不变。
二、范围与不变项
2.1 本次范围
- 首页横向、竖向两套布局增加“离线订单”入口。
- 新增离线订单列表页面、列表适配器和单笔手动上传调度能力。
- HTTP 模式增加单笔上传执行入口;MQTT 模式在现有服务中增加单笔上传动作。
- 增加页面轮询刷新、并发互斥、30 秒手动超时和条件恢复处理。
2.2 明确不变
- 首页 180 秒 Alarm 定时周期、现有 HTTP Worker、现有 MQTT Service 均保持原逻辑。
order_status既有含义不调整。- 状态
3、9不展示,也不通过本页面重新上传。 - 状态
4超过 10 分钟恢复为状态1的后台兜底不调整。 - 不增加批量上传、删除订单、修改订单和重新上传业务失败订单等功能。
三、现有状态与展示规则
order_status | 含义 | 页面是否展示 | 手动操作 |
|---|---|---|---|
| 1 | 待上传 | 展示 | 可点击“上传” |
| 4 | 上传中 | 展示 | 点击提示正在同步 |
| 3 | 服务端明确业务失败 | 不展示 | 不再上传 |
| 9 | 上传成功、已完成 | 不展示 | 无 |
order_type = 2 AND order_status IN (1, 4)
状态 1 → 4 时订单仍留在列表并显示“上传中”;4 → 3/9 时从列表消失;4 → 1 时重新显示“待上传”。
四、Use Case 契约
| Use Case ID | UC-OFFLINE-ORDER-001 |
|---|---|
| Use Case 名称 | 查看并手动上传离线订单 |
| 主要角色 | 消费机操作人员 |
| 触发入口 | 首页“离线订单” |
| 优先级 | P0 |
| 状态 | Implemented |
| DoR | READY;服务端 msgid 幂等性作为实施前技术核验项,不改变已确认业务规则 |
4.1 主流程
- 操作人员从首页进入“离线订单”。
- 页面查询并展示状态
1、4的订单及汇总信息。 - 点击待上传订单的“上传”,确认消费人、金额、时间和当前上传通道。
- 确认后系统重新读取订单并原子执行
1 → 4抢占。 - 抢占成功后按
consume_submit_type调用 HTTP 或 MQTT 单笔上传入口。 - 服务端返回成功,订单更新为
9并从列表消失。
4.2 分支、异常与恢复
| 编号 | 条件 | 页面行为 | 数据处理 |
|---|---|---|---|
| A1 | 点击状态 4 的订单 | 提示“订单正在同步中,请勿重复操作” | 不修改状态 |
| A2 | 定时任务已先抢占 | 提示正在同步 | 保持当前状态 |
| E1 | 无网络或通道不可用 | 提示网络或通道不可用 | 不抢占;已抢占则条件恢复为 1 |
| E2 | 可重试业务失败 | 提示可稍后重试 | 条件更新 4 → 1 |
| E3 | 明确业务失败 | 订单移出列表 | 更新为 3 |
| E4 | 30 秒未取得结果 | 提示“上传超时,请稍后重试” | 按订单、本次上传时间条件执行 4 → 1 |
| E5 | 请求创建、入队或发布立即失败 | 提示上传失败 | 立即按本次尝试条件恢复为 1 |
五、首页入口设计
首页横向和竖向布局都按现有入口风格增加“离线订单”,位置固定为:
订餐核销 → 离线订单 → 余额查询
六、离线订单页面设计
6.1 页面结构
- 标题:离线订单。
- 汇总区:待上传数量、上传中数量、当前 MQTT 连接状态、当前网络状态。
- 列表字段:消费时间、消费人、消费金额、支付方式、上传状态、操作。
- 空数据态:提示“暂无待上传订单”。
- 网络异常态:保留本地订单展示,同时提示当前网络不可用。
6.2 操作状态
- 待上传:状态列显示橙色文字,不使用背景色块;操作列显示可点击“上传”按钮。
- 上传中:状态列显示灰色文字,不使用背景色块;操作列保留灰色“上传中…”按钮样式,点击只提示正在同步,不发起请求。
- 上传失败:恢复为状态
1并显示“待上传”;不新增持久化失败状态。 - 成功或明确业务失败:分别更新为
9、3并从列表移除。
6.3 动态刷新
onResume后每 5 秒查询本地数据库,刷新间隔由实例变量统一持有;onPause或销毁时停止。- 手动抢占成功后立即更新当前行,不等待下一轮刷新。
- 使用列表差量更新,避免闪烁、滚动位置跳动和重复提示。
- 定时任务造成的状态变化最迟 5 秒反映到列表和汇总数量。
- MQTT 状态展示“未启动、连接中、已连接、已断开”;连接回调通过事件立即刷新,5 秒状态快照读取负责兜底。
- 每次查询开始显示
offline_order_manager_progress_bar,查询完成、页面退出或结果丢弃时隐藏;加载期间禁止重复翻页并合并刷新。
6.4 分页查询与交互
- 默认每页查询 10 条,按
save_time DESC, id DESC稳定排序。 - 列表首列展示全局计数序号,按“分页偏移量 + 当前行位置 + 1”计算;翻页后连续递增,不按页重置。
- 页面使用
pageSize实例变量,默认值集中定义;DAO、页数和偏移量计算均接收该变量,后续可动态替换。 - 底部固定显示“上一页|第 X/Y 页,共 N 条|下一页”,首尾页按钮禁用,无数据时隐藏。
- 汇总区展示全部可见订单的待上传数和上传中数,不随当前页变化。
- 每 5 秒只查询当前页和全局统计;翻页立即刷新并滚动到顶部,加载期间忽略重复点击。
- 总页数减少导致当前页越界时,自动回退至新的最后一页。
- 增加匹配过滤及排序字段的组合索引,不改变订单字段、状态和定时同步规则。
6.5 手动上传确认弹框
- 不再使用系统默认
AlertDialog,直接复用项目通用NewSingleTipPop。 - 弹框沿用
common_pop_width、白色圆角主体、居中标题、分割线、灰色取消按钮和主题色确认按钮。 - 标题保持“确认上传订单”,内容继续展示消费人、金额、消费时间和 HTTP/MQTT 上传通道。
- 点击“确认上传”调用现有
startManualUpload(orderId);点击“取消”只关闭弹框,不修改订单状态。 - 沿用通用弹框的防误触规则:点击外部和返回键均不能误关闭。
- 本次只替换展示组件,不修改状态过滤、原子抢占、超时、通道分派和定时同步逻辑。
七、组件与职责
| 组件 | 职责 |
|---|---|
OfflineOrderActivity | 页面生命周期、5 秒当前页刷新、查询加载状态、分页交互、网络提示、确认弹框和结果提示 |
OfflineOrderListAdapter | 展示订单字段、状态和操作,使用差量更新 |
ManualOrderUploadManager | 二次校验、原子抢占、通道选择、30 秒超时、条件恢复和统一结果分发 |
ManualUploadOrderWorker | HTTP 模式只上传指定单笔订单,不遍历其他订单 |
MqttConnectionState | 保存离线订单上传链路使用的 MQTT 进程内状态并过滤重复变化 |
OfflineOrderPaginationPolicy | 统一默认页大小、总页数、页码校正和偏移量计算,支持任意合法页大小 |
OfflineOrderPage | 承载当前页订单、全局状态数量、有效页码、总页数和总条数 |
OrderManager | 增加页面查询、单笔读取和带本次上传时间的条件状态更新 |
CustomMqttService | 增加单笔上传动作、参数和回执关联处理 |
MqttSimple | 在初始化、成功、失败、连接丢失和主动断开时同步状态并发出变化事件 |
刷新周期、手动超时等规则统一定义为具名常量,并添加职责和并发边界注释,避免重复硬编码。
八、手动上传调度设计
8.1 统一入口
- 根据订单 ID 重新读取数据库,不信任列表缓存。
- 状态为 4 时返回“正在同步中”;状态不是 1 时刷新列表。
- 检查网络和当前通道,再原子执行
1 → 4并记录本次uploading_time。 - 只有更新行数为 1 的调用方获得本次上传所有权。
- 按
consume_submit_type分派 HTTP 或 MQTT 单笔上传。 - 请求创建失败、通道立即失败或 30 秒超时,按订单、状态和本次上传时间条件恢复为 1。
8.2 HTTP 单笔上传
ManualUploadOrderWorker只加载并上传指定订单,不复用定时 Worker 的全量查询入口。- 结果等待上限为 30 秒,超时后结束本次任务并条件恢复为 1。
- 成功、可重试失败、明确业务失败继续沿用现有三态回执规则。
- 旧任务晚到回调只有通过本次上传时间校验后才能修改本地状态。
8.3 MQTT 单笔上传
CustomMqttService增加ACTION_UPLOAD_SINGLE_ORDER,只处理传入订单。- 未连接或发布失败立即恢复为 1;发布成功后等待业务回执,30 秒无结果则条件恢复为 1。
- 晚到回执必须关联订单业务标识和本次尝试,旧失败回执不能覆盖新的上传尝试。
九、并发与互斥
手动上传不暂停、不取消、不改写定时任务。两条路径共同使用本地数据库状态作为互斥锁:
状态 1 --原子抢占成功--> 状态 4 --请求成功--> 状态 9
|--可重试失败/30秒超时--> 状态 1
|--明确业务失败--> 状态 3
- 手动任务先抢占,定时任务看到状态 4 后跳过。
- 定时任务先抢占,手动端提示“订单正在同步中,请勿重复操作”。
- 两个手动点击同时发生,只有一个条件更新成功。
- 状态恢复必须包含本次
uploading_time,旧任务不得释放新任务。 - 10 分钟恢复是后台兜底;30 秒超时结束的是本次人工等待和尝试,两者职责不同。
十、超时与晚到结果
| 规则 | 时长 | 用途 |
|---|---|---|
| 页面刷新周期 | 5 秒 | 动态反映本地状态并降低持续查询频率 |
| 定时自动上传周期 | 180 秒 | 保持现有 Alarm 调度 |
| 手动上传结果等待 | 30 秒 | 控制人工操作等待时长 |
| 上传中兜底恢复 | 10 分钟 | 保持现有异常进程恢复 |
- 30 秒超时后,旧尝试不能执行会覆盖新尝试的恢复操作。
- 晚到成功可按业务唯一标识完成订单,但服务端必须保证同一
msgid幂等。 - 晚到失败只能作用于所属尝试;无法证明所属关系时不得覆盖更新状态。
- HTTP 超时后取消可取消调用;MQTT 已发布消息不可撤回,依赖回执关联和服务端幂等。
十一、数据、日志与安全
- 不新增订单表和持久化业务状态。
- 个人信息仅在设备本地按现有权限展示,不写入新增外部日志。
- 日志记录本地订单 ID、
msgid、通道、触发来源、抢占结果、耗时和结果码,不记录支付凭证、完整请求体等敏感字段。 - 日志区分
manual与scheduled来源。 - 页面退出只停止 UI 刷新,不取消已获得所有权的上传任务。
十二、验收标准
| 编号 | 验收内容 |
|---|---|
| AC-001 | 首页两套布局均显示“订餐核销 → 离线订单 → 余额查询”,并可进入页面 |
| AC-002 | 只展示 order_type=2 且状态为 1、4 的订单 |
| AC-003 | 状态 3、9 不显示,也不能通过页面再次上传 |
| AC-004 | 定时任务把订单改为 4 后,页面最迟 5 秒显示“上传中” |
| AC-005 | 订单恢复为 1 后,页面最迟 5 秒恢复“待上传” |
| AC-006 | 订单变为 3 或 9 后,页面最迟 5 秒移除 |
| AC-007 | 点击上传中的订单只提示正在同步,不产生新请求 |
| AC-008 | 手动与定时并发时,只有一个调用方成功执行上传 |
| AC-009 | 严格按 consume_submit_type 选择 HTTP 或 MQTT |
| AC-010 | 30 秒无结果时提示超时,并仅恢复仍属于本次尝试的订单 |
| AC-011 | 180 秒定时同步和 10 分钟兜底恢复行为不变 |
| AC-012 | 页面退出停止刷新,但已发起上传继续完成并正确写库 |
| AC-013 | 页面动态展示 MQTT 的未启动、连接中、已连接和已断开状态,不影响定时同步 |
| AC-014 | 默认每页只查询 10 条,pageSize 集中定义并可在运行时替换,SQL 不写死页大小 |
| AC-015 | 分页栏显示上一页、页码/总页数/总条数和下一页,首尾页按钮正确禁用,无数据时隐藏 |
| AC-016 | 汇总数量始终为全局数量;当前页越界时自动回退到有效页 |
| AC-017 | 每次查询显示加载控件,完成或退出时隐藏;查询中不能重复翻页或并发读取 |
| AC-018 | 列表首列展示全局连续序号;默认第二页从 11 开始,动态调整页大小后仍按实际偏移量计数 |
| AC-019 | 手动上传确认使用项目通用弹框风格,取消不改变状态,确认继续执行原上传流程 |
十三、测试矩阵
| 测试编号 | 场景 | 预期结果 |
|---|---|---|
| UT-001 | 页面状态过滤 | 只返回状态 1、4 |
| UT-002 | 同订单并发抢占 | 只有一个更新成功 |
| UT-003 | 条件超时恢复 | 订单、状态、本次上传时间都匹配才恢复 |
| UT-004 | 通道选择 | HTTP/MQTT 与配置一致 |
| UT-005 | MQTT 状态快照 | 状态可更新,重复或空状态不产生变化 |
| UT-006 | 分页计算 | 任意合法页大小均正确计算总页数、有效页码和偏移量 |
| UT-007 | 页码越界恢复 | 空数据、页码小于 1、总页数缩减时返回正确页码 |
| UT-008 | 分页序号计算 | 第一页从 1 开始,后续页按动态页大小计算全局起始序号 |
| IT-001 | 手动先抢占、定时后执行 | 定时任务跳过状态 4 |
| IT-002 | 定时先抢占、手动后点击 | 提示正在同步,不重复请求 |
| IT-003/004 | HTTP/MQTT 三类回执 | 分别进入 9、1、3 |
| IT-005 | 手动任务 30 秒超时 | 提示超时并条件恢复为 1 |
| IT-006 | 旧回调与新任务并发 | 旧任务不能错误释放新任务 |
| UI-001 | 定时任务修改列表订单 | 最迟 5 秒正确更新或移除 |
| UI-002 | 页面前后台切换 | 前台刷新、后台停止、返回立即加载 |
| UI-003 | MQTT 连接、断开和重连 | 汇总区立即显示对应状态,订单定时同步不受影响 |
| UI-004 | 上一页、下一页和边界按钮 | 目标页正确、列表回到顶部、首尾按钮正确禁用 |
| UI-005 | 当前页订单被同步移除 | 总数更新,页码有效,末页清空后自动回退 |
| UI-006 | 查询加载状态 | 查询开始显示进度条,结束、异常或退出均隐藏,加载中分页按钮不可重复触发 |
| REG-001 | 原有定时 HTTP/MQTT | 180 秒调度、重试和 10 分钟恢复无回归 |
性能边界:以 500 条待处理订单连续前台运行 10 分钟,页面查询不得阻塞主线程,不得出现明显滚动卡顿、重复弹窗或持续内存增长。
十四、实施影响清单
预计新增:OfflineOrderActivity、OfflineOrderListAdapter、页面与列表项布局、ManualOrderUploadManager、ManualUploadOrderWorker 及入口图标资源。
预计修改:首页跳转与两套布局、OrderManager、CustomMqttService、订单回执处理、清单注册、字符串资源和测试文件。
具体类名、资源名和包路径在实施计划阶段以当前代码结构为准,不在设计阶段假定不存在的公共抽象。
十五、风险与实施前核验
- 编码前通过接口契约或联调日志确认服务端对同一订单固定
msgid的重复提交具备幂等处理。 - MQTT 回执必须能关联本次尝试;否则 30 秒后重试可能与晚到回执交错,需要补充可回显请求标识。
- 5 秒查询应在后台线程执行并使用差量刷新,避免阻塞主线程。
- 不复制完整定时批量逻辑;只复用请求组装和回执映射,调度入口与生命周期隔离。
十六、交付边界
- 本文为纯文字设计,不包含视觉原型或图片。
- 已冻结状态展示、首页位置、通道选择、并发互斥、5 秒刷新、30 秒手动超时和 10 分钟后台兜底规则。
- 当前未修改
CusumptionMachine源码,未执行 Git 暂存、提交或推送。 - 用户审核本文后,再进入实施计划和编码阶段。