feat: add Flutter mobile clients and staff delivery API
This commit is contained in:
@@ -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 + Repository,UI、业务编排和数据边界严格分离:
|
||||
|
||||
```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 和安全存储改动必须真机回归。
|
||||
|
||||
Reference in New Issue
Block a user