# 工作人员端令牌失效恢复项目文档 v1.0 ## 1. 项目概述 - 项目名称:工作人员端令牌失效恢复与异步刷新修复。 - 主要功能:受保护接口拒绝失效令牌后自动清理会话、返回登录页并安全保存原目标页;同时消除页面刷新时向 `setState` 返回异步任务的错误写法。 - 技术栈:Flutter、Dart、GoRouter、HTTP、Flutter Secure Storage、Flutter Widget Test。 - 运行环境:项目当前 Flutter SDK、Web Release 静态服务及本地平台 API。 - 实施范围:仅 `service_app` 请求层、会话层、路由和页面错误处理;后端接口与数据库不变。 ## 2. 目录结构说明 ```text platforms/ ├── apps/service_app/ │ ├── lib/ │ │ ├── app/ │ │ │ ├── auth_navigation.dart # 登录地址与站内回跳校验 │ │ │ ├── dependencies.dart # 会话失效、清理和并发保护 │ │ │ └── router.dart # 鉴权守卫、过期提示和安全回跳 │ │ ├── data/services/ │ │ │ ├── api_client.dart # HTTP 与业务码鉴权失效识别 │ │ │ └── secure_session_store.dart # 可测试的安全存储接口 │ │ └── ui/features/ # 页面错误抑制和同步刷新 │ └── test/ │ ├── app/ # 会话、回跳和路由测试 │ ├── data/api_client_test.dart # 请求层鉴权测试 │ └── ui/preflight_page_test.dart # 作业检查重试组件测试 └── docs/ # 需求、安全、项目和操作日志 ``` ## 3. 核心文件说明 ### 请求与上传 - `ApiClient` 在请求发出前保存实际令牌快照。 - 受保护请求返回 HTTP 401,或统一响应返回业务码 `1301—1314`、`1715` 时,通知会话层并抛出 `SessionExpiredException`。 - 匿名登录请求不触发已有会话清理;普通 JSON 请求和文件上传使用同一识别规则。 ### 会话与安全存储 - `StaffSession.invalidate` 只接受与当前令牌一致的被拒令牌,并通过失效标记保证幂等。 - 失效时立即清空内存中的令牌、工作人员标识和角色,保留设备标识并通知路由。 - 持久化删除逐项尽力执行;单项失败只记录调试信息,不阻塞返回登录页。 - 新登录写入前等待旧清理任务结束,避免迟到删除覆盖新会话。 ### 路由与页面 - 鉴权守卫把原目标页保存为登录页 `redirect` 参数,并通过 `reason=expired` 展示失效原因。 - 仅允许站内绝对路径回跳;拒绝外部地址、协议相对地址、反斜杠地址和登录页循环。 - 登录成功后返回原目标页,缺失或非法目标默认进入 `/preflight`。 - 页面层不重复展示鉴权错误;普通断网和服务端错误仍沿用既有提示。 - 作业检查、个人资料和任务详情中的 7 处异步刷新均改为同步 `setState` 赋值。 ## 4. 变更记录 ### v1.0(2026-09-05) - 新增工作人员端统一会话失效异常与请求令牌回调。 - 新增会话清理、设备标识保留、并发幂等和迟到旧请求保护。 - 新增登录失效提示、目标页保存和安全回跳。 - 修复作业检查、个人资料和任务详情全部已知异步 `setState` 错误。 - 新增请求层、会话层、路由、安全地址和作业检查重试测试。 ## 5. 维护指南 - 新增受保护接口必须复用已装配 `onUnauthorized` 的统一 `ApiClient`。 - 新增文件上传入口不得绕过统一响应解析和会话失效处理。 - 页面捕获 `SessionExpiredException` 时只结束当前交互,不显示普通网络错误,也不重放写操作。 - 替换页面加载任务时,应先创建 `Future`,再用带语句块的同步 `setState` 回调赋值。 - 修改鉴权流程后至少运行 `flutter analyze --no-pub`、完整测试和 Release Web 构建。 ## 6. 已知边界 - 本次不新增刷新令牌接口,也不改变服务端 JWT 生命周期和错误码。 - 安全存储删除失败时,下次全新启动可能再次读取旧值,但当前运行会立即退出错误状态并可重新登录。 - 重新登录只恢复页面位置,不恢复或自动执行失效前的打卡、取证、上传及任务操作。