feat(client): 实时授权状态与会话失效处理

- 心跳读取 /auth/ping 回带的授权概况,直接刷新横幅/状态栏/只读门禁,省去单独轮询 /license/info
- 账号被停用/删除(401 USER_DISABLED)即强制重新登录
- 令牌持久化经串行队列 + 会话代号守卫,杜绝续期写入与登出交叉把失效 token 写回
- 写操作门禁(write_guard)+ 授权文案(license_copy)按 grace/readonly/locked 分阶段降级

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-19 07:34:07 +08:00
parent e41085a878
commit 32bd64d676
23 changed files with 1319 additions and 408 deletions
+18 -12
View File
@@ -169,10 +169,12 @@ class _FinanceTabState extends ConsumerState<_FinanceTab> {
],
actions: (canClose && !WriteGuard.isReadonly(ref))
? [
TextButton(
onPressed: () => _closeRecord(r),
child: const Text('结清',
style: TextStyle(fontSize: 13, color: AppTheme.success)),
WriteGuard(
child: TextButton(
onPressed: () => _closeRecord(r),
child: const Text('结清',
style: TextStyle(fontSize: 13, color: AppTheme.success)),
),
),
]
: null,
@@ -318,10 +320,12 @@ class _FinanceTabState extends ConsumerState<_FinanceTab> {
if ((r.type == 'payable' || r.type == 'receivable') &&
r.status == 'open' &&
!WriteGuard.isReadonly(ref)) {
return DataCell(TextButton(
onPressed: () => _closeRecord(r),
child: const Text('结清',
style: TextStyle(fontSize: 12, color: AppTheme.success)),
return DataCell(WriteGuard(
child: TextButton(
onPressed: () => _closeRecord(r),
child: const Text('结清',
style: TextStyle(fontSize: 12, color: AppTheme.success)),
),
));
}
return const DataCell(SizedBox());
@@ -419,10 +423,12 @@ class _FinanceTabState extends ConsumerState<_FinanceTab> {
toolbar: Row(
children: [
if (addLabel != null && !WriteGuard.isReadonly(ref))
ElevatedButton.icon(
onPressed: _showAddDialog,
icon: const Icon(Icons.add, size: 16),
label: Text(addLabel),
WriteGuard(
child: ElevatedButton.icon(
onPressed: _showAddDialog,
icon: const Icon(Icons.add, size: 16),
label: Text(addLabel),
),
),
if (addLabel != null && !WriteGuard.isReadonly(ref))
const SizedBox(width: 8),