docs: import wiki at afc651f75a3a

ila
2026-08-07 16:37:07 +08:00
parent 23abc5f797
commit 6028692a2c
+87
@@ -0,0 +1,87 @@
<!-- docs-wiki-sync:docs/tasks/T-266.md@afc651f75a3abc2676bb13aa8a80f6aa6a25a72e -->
> 同步来源:[`docs/tasks/T-266.md`](/chengma/mroubao/src/commit/afc651f75a3abc2676bb13aa8a80f6aa6a25a72e/docs/tasks/T-266.md) · commit `afc651f75a3a`
---
id: T-266
title: 将设备 Bearer token 固定有效期延长为30天
phase: 2
deps:
- T-204
- T-206
status: DONE
created: 2026-07-31
context_ref: 200e263
work_branch: null
write_paths:
- docs/tasks/T-266.md
- docs/current-state.md
- docs/02-requirements.md
- docs/03-tech-stack.md
- docs/04-architecture.md
- docs/api.md
- backend-api/internal/usecase/**
- backend-api/internal/transport/httpapi/**
- android-buyer/app/src/test/java/com/roubao/autopilot/procurement/ProcurementApiClientTest.kt
---
## 问题 / 背景
内部采购手机当前每次登录取得固定 1 小时 BUYER 设备 Bearer token。采购设备是预授权
设备,token 由 Android Keystore-backed 安全存储保存;1 小时到期会增加频繁重新登录
和无人值守任务中断成本。需求将有效期延长为 1 个月。
Go `time.Duration` 不表达自然月,本任务把“1个月”固定定义为 **30 天,即 720 小时、
2,592,000 秒**,避免不同月份导致 API 与设备本地到期时间不确定。
## 行为契约
- 新签发 BUYER 设备 access token 的绝对有效期固定为 `30 * 24 * time.Hour`。
- `POST /api/v1/auth/token` 成功响应的 `expires_in` 固定为 `2592000`。
- 已签发 token 保留数据库中的原 `expires_at`,不批量延长;部署后设备需重新登录一次
才能取得30天 token。
- token 不因 heartbeat、claim、start、任务执行或 API 请求滚动续期;仍不增加 refresh
token。
- 用户禁用、设备禁用、token 撤销、设备绑定不匹配和角色错误仍在下一次请求立即失败,
不等待30天到期。
- 数据库仍只保存 token SHA-256;原 token 不写日志、不写 execution event。
## 不变边界
- ADMIN 服务端 session 仍为固定8小时。
- 设备预共享 secret、任务 claim token、claim 租约、execution 授权和幂等 key 的
生命周期均不变。
- 管理 Cookie 与 BUYER Bearer middleware 继续隔离。
- 不增加可配置环境变量,避免生产节点出现不一致的鉴权期限。
## 方案
1. 将 `AccessTokenLifetime` 改为 `30 * 24 * time.Hour`。
2. 更新 HTTP 契约测试,断言 `expires_in=2592000`;usecase 测试继续断言数据库到期
时间严格等于统一常量。
3. 同步需求、技术栈、架构、API 示例和当前状态文档。
4. 运行 Go 全量测试、Android 契约消费回归和根 `init.ps1`。
## 验收要点
- [x] 新登录产生的 `expires_at - created_at` 严格等于30天。
- [x] 登录响应 `expires_in` 为 `2592000`。
- [x] 过期、撤销、用户禁用和设备禁用测试继续通过。
- [x] 已有 token 不被迁移或静默延期。
- [x] Admin session、claim/execution 生命周期无改动。
- [x] Go 全量测试和根 `init.ps1` 通过。
## 执行记录
- 2026-07-31:将 `AccessTokenLifetime` 从 `time.Hour` 改为
`30 * 24 * time.Hour`;登录响应继续从该统一常量计算 `expires_in`,现在固定返回
`2592000`。ADMIN session 常量仍为8小时。
- 2026-07-31:更新 HTTP handler 测试精确断言 `2592000`;既有 usecase 测试继续
断言 `expires_at - created_at == AccessTokenLifetime`,SQLite 鉴权的过期、撤销、
用户禁用、设备禁用与绑定检查测试全部通过。
- 2026-07-31:Android 登录 mock 改为30天并断言本地计算的
`expiresAtEpochMillis` 落在登录时刻加30天的窗口内,证明跨端秒/毫秒换算无溢出。
- 2026-07-31:同步需求、技术栈、架构、API 示例和当前状态。没有新增或修改数据库
migration,因此已有 token 的 `expires_at` 不变;后端重启后设备必须重新登录一次
才取得30天 token。
- 2026-07-31:Go 全量测试通过,Android 定向消费测试通过,根 `init.ps1` 通过
Debug/Release 单测、Debug APK、共享契约、导入工具和全部 Go 后端测试。