feat: add Flutter mobile clients and staff delivery API

This commit is contained in:
david
2026-07-30 21:47:41 +08:00
parent 550efb3812
commit 36a5ced1c0
228 changed files with 17159 additions and 22 deletions

View File

@@ -2,7 +2,7 @@
## 1. 定位与角色模型
服务端 App 是安装维修员、安检员和配送员共用的 Flutter 移动应用。“服务端”在本文指服务人员端,并非后端服务。用户登录后由平台分配角色、资质所属可燃气体站/配送点和服务区域;具有多个角色时可切换工作台,但每次操作都带角色和组织上下文。
服务端 App 是安装维修员、安检员和配送员共用的 Flutter 移动应用。“服务端”在本文指服务人员端,并非后端服务。首期采用“一账号一角色”:用户登录后由平台返回唯一岗位、资质所属站/配送点,客户端不得自行切换或拼装角色上下文。
设计稿要求不同角色使用同一登录与账户体系,但加载不同的底部导航和工作台:配送员使用“订单、任务、用户、我的”,安装维修员使用“工单、巡检、记录、我的”,安检员使用“任务、记录、隐患、我的”。导航、数据和接口均按当前角色、组织、服务区域与资质过滤。
@@ -118,8 +118,107 @@
## 8. 首期 Client API 落地边界2026-07
- 工作人员 API 固定为 `/heqi/client/v1/staff`,令牌客户端为 `service_app`;不提供注册,只允许后台已创建、启用且岗位受支持的账户登录。
- 三类岗位均属于同一 Staff Client API安装维修和安检使用 `/tickets` 工单接口;配送使用 `/delivery/orders`、开始配送、轨迹批量补传、到达围栏校验、异常/恢复和提交签收接口。所有查询和动作只允许访问当前账号被分派的对象。
- 登录后调用 `/preflight`。首期真实校验账号、唯一岗位、所属组织、资质有效期和上班状态;每日培训、服务区域与授权设备尚未配置时返回 `not_configured`,客户端必须明确展示,不能显示为“已通过”。
- 首期岗位为配送、安装维修、安检。安装/维修工单只分派给安装维修人员,安检/复检只分派给安检人员,客服类工单不进入工作人员 App。
- 工单统一复用 `cs_ticket`,状态为待分派、已分派、处理中、异常、待用户确认、已完成或已取消。工作人员只能操作分派给本人的工单;现场结果必须包含定位、原始采集时间、上传资源地址和幂等号。
- 安装和维修至少提交前、中、后图片及用户签名;安检和复检至少提交一张图片、结果及用户签名。单次最多六张图片、三段视频。不合规或高风险结论只能进入异常,不能提交待用户确认。
- 配送人员只操作分派给本人的供气配送订单,可开始配送、批量补传轨迹、到达校验、异常/恢复和提交签收;不能修改订单金额。到达以订单地址坐标和配置地理围栏为准。
- 工作人员钱包与用户钱包复用统一模型,支持余额、充值订单、不可变流水、提现及银行卡;服务收入只能由已完成业务事实产生,客户端不能直接增加余额。
## 9. Flutter 开发说明
### 9.1 平台、角色与工程边界
- 服务人员端只交付 Android、iOS。生产工程按规划放在 `apps/service_app`;当前 `ui` 目录是配送、安装维修、安检三类原型的交互实现,不连接真实定位、相机、蓝牙、支付或业务 API。
- 三类岗位共用一个 Flutter 应用和登录体系,每个账号仅有一个服务岗位,令牌 client claim 固定为 `service_app` 并携带唯一 `role_code`。岗位变更必须由后台完成并重新登录换取令牌;客户端不提供角色切换。
- 工作人员 API 根路径固定为 `/heqi/client/v1/staff`。客户端不得访问用户端、平台后台、气站后台或配送点后台的令牌与接口。
- 首期后端不提供工作人员自助注册时Flutter 注册页面不得伪造成功;若未来开放申请,只能创建待审核账户,不能直接授予岗位能力。
### 9.2 分层结构与角色化功能
采用 MVVM + RepositoryUI、业务编排和数据边界严格分离
```text
apps/service_app/lib/
app/
app.dart
router.dart # go_router、岗位与作业前置守卫
role_context.dart # 当前唯一岗位、组织和服务区域
dependencies.dart
data/
models/
services/ # API、定位、相机、蓝牙、上传、安全存储
repositories/ # 任务、取证、轨迹、钱包、离线队列
offline/ # 加密草稿、Outbox、冲突与补传
domain/
models/
use_cases/ # 到场、扫描、安检、安装、完成任务
ui/
core/ # 角色主题、通用状态、取证组件
features/
auth/
workbench/
delivery/
installation/
inspection/
evidence/
hazard/
records/
wallet/
profile/
```
- 共用任务卡、步骤器、相机、签名、弱网提示等展示组件;配送、安装维修、安检分别拥有独立 ViewModel、Use Case、检查表解释器和状态机不得用一个万能表单加前端条件判断代替领域规则。
- View 不直接更新任务状态、金额、风险等级或本地数据库。ViewModel 只发起命令并呈现服务端返回状态Repository 负责 API、本地加密草稿、上传队列和领域模型转换。
- 检查表、强制取证项、材料清单和步骤顺序由服务端版本化下发。客户端缓存模板版本,提交时带模板 `identity` 和版本;过期模板必须进入冲突处理,不能静默套用新模板。
- 所有任务、证据、轨迹、人员、组织和钱包引用只使用 `identity`;数据库内部自增 ID 不进入 Flutter 模型、日志、深链或离线队列。
### 9.3 路由、底部导航与守卫
- 使用 `MaterialApp.router``go_router`。登录后先进入 `/preflight`,统一校验账户启用、岗位、组织、资质、每日培训、上班状态、服务区域和授权设备,再进入角色工作台。
- 首期角色底部导航使用 `StatefulShellRoute.indexedStack`,只暴露有真实 Client API 的入口:
- 配送员:`/work`(本人配送订单)、`/records`(已完成记录)、`/me`
- 安装维修员与安检员:`/work`(本人工单)、`/records`(已完成记录)、`/me`
- 任务详情统一使用 `/tasks/:identity`,具体步骤使用 `/tasks/:identity/steps/:stepCode`;路由解析后仍须从服务端读取任务类型、当前状态和允许动作,不能信任路径中的角色或步骤。
- 未完成培训、未上班、资质失效、超出服务区或任务未分派给本人时,守卫导航到明确的阻断页;返回栈、通知深链和手工 URL 均不能绕过。
- 普通退出时若存在未提交草稿,必须先返回上传或二次确认放弃并安全删除;令牌失效或异常退出时按账号加密封存,只有同一账号重新认证后可恢复,换账号不可见。
### 9.4 任务状态与现场作业
- 每类任务由独立状态模型驱动UI 只展示服务端返回的 `allowed_actions`。按钮禁用、步骤条位置和本地完成标记不构成业务校验。
- 配送流程至少实现订单确认、导航到场、围栏校验、气瓶扫描、随瓶安检、收款确认、签收/回收;安装流程按使用条件、备料、安装、测试、前期安检、用户确认、收款执行;安检流程按详情、执行、检查、风险等级、签名、隐患/复检执行。
- 不合格、高风险、测试失败、围栏异常、金额差异、证据缺失和设备回执不确定时只能进入异常、待整改或待确认,不能由客户端跳到已完成。
- 关阀、设备激活、任务完结、收款和提现均显示服务端确认状态。请求成功、文件进入上传队列或本地步骤完成不得显示为最终成功。
- 检查项使用稳定代码;照片类型、材料、风险等级和错误原因由契约映射,禁止根据中文标题或错误文案驱动状态流转。
### 9.5 离线队列、定位与证据
- 离线能力只覆盖已分配任务的只读信息和现场草稿。关阀、激活、资金、最终完结等动作必须在线确认;离线时明确告知“已暂存/待补传”,不能显示“已完成”。
- 本地使用平台安全存储保护数据库密钥,任务草稿、轨迹点、照片/视频元数据、签名、扫描结果和收款确认采用加密数据库或加密文件。退出角色或账户时按服务端留存策略处理,不能只删索引留下明文文件。
- 每个离线写入包含本地操作 `identity`、业务对象 `identity`、幂等键、原始采集时间、来源、完整性标记和内容哈希。补传保留原始时间按依赖顺序投递409/版本冲突进入人工可见的冲突队列。
- 文件先落加密暂存区并记录哈希,获得短期上传授权后上传;业务提交只引用上传成功的资源 URI。失败重试不得重复创建证据后台删除或拒绝的附件不得被本地队列重新“复活”。
- 配送定位仅在履约期间、满足权限与任务状态时采集。Android 前台服务和 iOS 后台定位必须显示系统要求的可见提示;权限撤销、精度不足、后台受限和长时间无点位均写入任务状态。
- 用户签名画布保存矢量笔画或可验证位图及哈希,并与结论、任务、操作者和采集时间绑定;禁止复用其他任务签名。
### 9.6 Android/iOS 平台适配
| 能力 | Android | iOS |
| --- | --- | --- |
| 相机/相册 | 运行时按用途申请 Camera/Photo Picker使用系统选择器优先 | 使用相机和 Photos 限定访问,解释用途并处理 Limited 状态 |
| 蓝牙扫描 | 按系统版本申请 Nearby Devices/Bluetooth 权限 | 配置 Bluetooth 用途说明,仅在任务步骤内扫描 |
| 定位 | 前台精确定位;配送后台轨迹使用合规前台服务与常驻通知 | 先申请 When In Use确需配送后台定位时再升级并配置 Background Modes |
| 通知 | 创建安全、任务、上传三类通知渠道,高风险安全通知不可静默关闭 | 分类注册通知操作,点击后重新读取任务和权限 |
| 文件暂存 | App 私有目录、加密数据库,禁止写公共目录 | Application Support/Library 私有目录,排除不必要云备份 |
| 深链 | App Links 校验域名与签名证书 | Universal Links 配置 Associated Domains |
- 权限说明必须与实际采集行为一致。拒绝权限时给出可恢复路径;不得因相机或精确定位权限失败伪造照片、地址或到场结果。
- 地图、导航、蓝牙、相机和签名通过抽象 Service 注入Android/iOS 实现返回统一领域结果和稳定错误码。
### 9.7 视觉、性能与测试
- 设计基线以 `doc/服务端APP-配送端-产品设计``doc/服务端APP-安装端-产品设计``doc/服务端APP-安全检查端-产品设计``ui/?app=service&role=...` 为准。统一使用紫色作业框架,安检关键成功动作可使用安全绿色;风险等级必须同时展示文字、图标和颜色。
- 任务详情优先展示任务号、类型、预约/SLA、地址、脱敏联系人、风险与允许动作。固定底部操作按钮不得遮挡检查项、签名或系统安全区。
- 长清单使用惰性列表和分段保存;照片视频缩略图解码、压缩和上传移出 UI isolate。后台轨迹、上传和补传必须受电量、网络与系统调度约束不用常驻无限循环。
- ViewModel、Use Case、Repository、离线队列和冲突处理覆盖单元测试角色/组织守卫、步骤前置、风险结论和弱网状态覆盖 Widget 测试;三角色主闭环覆盖 Android/iOS 集成测试。
- 每次合并至少执行 `flutter analyze``flutter test`、Android debug 构建和 iOS Simulator 构建定位、相机、蓝牙、推送、后台任务、App Links/Universal Links 和安全存储改动必须真机回归。