feat: 重构会话管理与提醒通知系统
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
|
||||
---
|
||||
|
||||
## 6. 日历提醒未使用自定义提示音
|
||||
|
||||
**文件**: `apps/lib/core/notification/services/reminder_scheduler_service.dart`
|
||||
|
||||
**问题描述**:
|
||||
已添加 `apps/assets/reminder_sound/1.WAV` 自定义提示音文件,但代码中未使用,仍使用系统默认铃声:
|
||||
|
||||
```dart
|
||||
// 当前代码
|
||||
const androidDetails = AndroidNotificationDetails(
|
||||
_channelId,
|
||||
_channelName,
|
||||
// ...
|
||||
playSound: true, // 使用系统默认
|
||||
// 没有 customSound 配置
|
||||
);
|
||||
```
|
||||
|
||||
**根因**:
|
||||
添加了资源文件但未在通知配置中引用。
|
||||
|
||||
**建议修复**:
|
||||
1. 在 `pubspec.yaml` 添加 assets 路径:
|
||||
```yaml
|
||||
flutter:
|
||||
assets:
|
||||
- assets/reminder_sound/
|
||||
```
|
||||
|
||||
2. 在通知配置中引用自定义铃声:
|
||||
```dart
|
||||
// Android
|
||||
final androidDetails = AndroidNotificationDetails(
|
||||
_channelId,
|
||||
_channelName,
|
||||
playSound: true,
|
||||
sound: RawResourceAndroidNotificationSound('reminder_sound/1'),
|
||||
// ...
|
||||
);
|
||||
|
||||
// iOS
|
||||
const iosDetails = DarwinNotificationDetails(
|
||||
presentSound: true,
|
||||
sound: 'reminder_sound_1.wav',
|
||||
// ...
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 通知调度缺少变化检测(重复重建)
|
||||
|
||||
**文件**: `apps/lib/core/notification/services/reminder_scheduler_service.dart`
|
||||
|
||||
**问题描述**:
|
||||
每次获取日历事件都会调用 `upsertEventReminders`,内部执行 `cancelEventReminders` + 重建,即使事件没变化:
|
||||
|
||||
```dart
|
||||
Future<void> upsertEventReminders(event) async {
|
||||
await cancelEventReminders(event.eventId); // 总是先取消
|
||||
for (alarm in buildAlarmsForEvent(event)) {
|
||||
await _scheduleAlarm(alarm); // 总是重建
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**影响**:
|
||||
- 通知 ID 变化导致系统重新调度,浪费资源
|
||||
- 用户频繁访问日历时产生不必要的操作
|
||||
|
||||
**建议修复**:
|
||||
```dart
|
||||
Future<void> upsertEventReminders(event) async {
|
||||
final existing = await _getScheduledAlarm(event.eventId);
|
||||
if (existing != null && _isSameAlarm(existing, event)) {
|
||||
return; // 跳过,没变化
|
||||
}
|
||||
await cancelEventReminders(event.eventId);
|
||||
for (alarm in buildAlarmsForEvent(event)) {
|
||||
await _scheduleAlarm(alarm);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
## 相关文件
|
||||
|
||||
- `apps/lib/app/app.dart`
|
||||
- `apps/lib/data/cache/cache_scope.dart`
|
||||
- `apps/lib/data/cache/cache_store.dart`
|
||||
- `apps/lib/data/cache/cached_repository.dart`
|
||||
- `apps/lib/core/inbox/inbox_sync_store.dart`
|
||||
- `apps/lib/features/chat/presentation/bloc/chat_bloc.dart`
|
||||
- `apps/lib/app/services/app_prewarm_orchestrator.dart`
|
||||
- `apps/lib/core/notification/services/reminder_scheduler_service.dart`
|
||||
- `apps/lib/core/notification/services/reminder_permission_service.dart`
|
||||
- `apps/lib/core/notification/services/reminder_reconcile_service.dart`
|
||||
- `apps/assets/reminder_sound/`
|
||||
@@ -1,106 +0,0 @@
|
||||
# 路由守卫逻辑分散
|
||||
|
||||
## 问题描述
|
||||
|
||||
当前路由守卫逻辑分散在两处,可能导致判断不一致:
|
||||
|
||||
1. `app_router.dart` 的 `redirect()` - 核心守卫逻辑
|
||||
2. `LinksyApp` 的 `BlocListener` - 预留了位置但未使用
|
||||
|
||||
## 当前代码
|
||||
|
||||
```dart
|
||||
// app_router.dart
|
||||
GoRouter createAppRouter(AuthBloc authBloc) {
|
||||
return GoRouter(
|
||||
refreshListenable: GoRouterRefreshStream(authBloc.stream),
|
||||
redirect: (context, state) {
|
||||
final authState = authBloc.state;
|
||||
final isAuthenticated = authState is AuthAuthenticated;
|
||||
// ... 守卫判断逻辑
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// app.dart (LinksyApp)
|
||||
BlocListener<AuthBloc, AuthState>(
|
||||
listener: (context, state) {
|
||||
// Handle auth state changes if needed ← 预留但未使用
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
## 问题
|
||||
|
||||
| 问题 | 说明 |
|
||||
|------|------|
|
||||
| 逻辑分散 | 守卫在 `redirect()`,但 BlocListener 预留了位置 |
|
||||
| 隐患 | 将来可能有人在两处都加逻辑,导致不一致 |
|
||||
| 职责不清 | 到底是 redirect 管跳转,还是 BlocListener 管跳转 |
|
||||
|
||||
## 建议方案
|
||||
|
||||
**方案1:路由守卫集中在 redirect()(当前方案,保持但清理)**
|
||||
|
||||
```dart
|
||||
// app_router.dart
|
||||
GoRouter createAppRouter() {
|
||||
return GoRouter(
|
||||
refreshListenable: GoRouterRefreshStream(sl<AuthBloc>().stream),
|
||||
redirect: (context, state) {
|
||||
// 唯一的守卫逻辑
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// LinksyApp - 只做副作用,不做路由跳转
|
||||
BlocListener<AuthBloc, AuthState>(
|
||||
listener: (context, state) {
|
||||
// 埋点、Toast 等副作用
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
**方案2:路由守卫集中在 BlocListener**
|
||||
|
||||
```dart
|
||||
// app_router.dart - 不再有 redirect
|
||||
GoRouter createAppRouter() {
|
||||
return GoRouter(
|
||||
routes: [...],
|
||||
);
|
||||
}
|
||||
|
||||
// LinksyApp - 唯一的路由守卫入口
|
||||
BlocListener<AuthBloc, AuthState>(
|
||||
listener: (context, state) {
|
||||
final router = GoRouter.of(context);
|
||||
if (state is AuthUnauthenticated) {
|
||||
if (!isPublicRoute(router.matchedLocation)) {
|
||||
router.go(AppRoutes.authLogin);
|
||||
}
|
||||
} else if (state is AuthAuthenticated) {
|
||||
if (router.matchedLocation == AppRoutes.authLogin) {
|
||||
router.go(AppRoutes.homeMain);
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
## 收益
|
||||
|
||||
| 收益 | 说明 |
|
||||
|------|------|
|
||||
| 单一职责 | 路由跳转只在一处判断 |
|
||||
| 可维护 | 将来不会有人误在另一处加逻辑 |
|
||||
| 清晰 | 开发者知道去哪改守卫逻辑 |
|
||||
|
||||
## 涉及文件
|
||||
|
||||
- `apps/lib/app/app.dart`
|
||||
- `apps/lib/app/router/app_router.dart`
|
||||
|
||||
## 状态
|
||||
|
||||
- [ ] 待修复
|
||||
@@ -1,74 +0,0 @@
|
||||
# Agent Calendar/Inbox Stability Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** 修复 agent 日历分享失败、对话后日历不刷新、邀请信息不完整,并新增设置页一键清理本地缓存后强制重新拉取。
|
||||
|
||||
**Architecture:** 后端补齐 `calendar_share` 依赖注入与邀请 payload 字段,确保工具链与 API 路由行为一致。前端在 Chat 工具结果事件上增加日历刷新钩子,并在设置页提供缓存清理入口,复用既有 prewarm 机制触发重新拉取。协议文档同步更新 inbox 邀请结构,避免前后端契约漂移。
|
||||
|
||||
**Tech Stack:** FastAPI + SQLAlchemy + AgentScope tools, Flutter + CachedRepository + SharedPreferences cache.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: 修复 calendar_share 在 Agent 工具链中的依赖缺失
|
||||
|
||||
**Files:**
|
||||
- Modify: `backend/src/core/agentscope/tools/utils/calendar_domain.py`
|
||||
|
||||
**Steps:**
|
||||
1. 在 `create_schedule_service` 注入 `SQLAlchemyUserRepository`。
|
||||
2. 保持路由层与工具层对 `ScheduleItemService` 的依赖一致。
|
||||
3. 回归验证 `calendar_share` 不再因为 actor lookup 依赖缺失而失败。
|
||||
|
||||
### Task 2: 扩充 calendar invite payload(邀请人 + 时间 + 描述)
|
||||
|
||||
**Files:**
|
||||
- Modify: `backend/src/v1/schedule_items/service.py`
|
||||
- Modify: `docs/protocols/models/inbox-messages.md`
|
||||
- Test: `backend/tests/unit/v1/schedule_items/test_share.py`
|
||||
|
||||
**Steps:**
|
||||
1. 在 `share` 中构建邀请消息时写入 `actor.phone`。
|
||||
2. 在 `item` 中写入 `description/start_at/end_at/timezone`。
|
||||
3. 更新协议文档 `CalendarInviteContent`。
|
||||
4. 补充/更新单测断言新增字段。
|
||||
|
||||
### Task 3: 对话工具成功后触发日历缓存刷新钩子
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/lib/features/chat/presentation/bloc/chat_bloc.dart`
|
||||
- Modify: `apps/lib/features/chat/presentation/bloc/chat_bloc_events.dart`
|
||||
- Modify: `apps/lib/app/di/injection.dart`
|
||||
- Test: `apps/test/features/chat/presentation/bloc/chat_bloc_test.dart`
|
||||
|
||||
**Steps:**
|
||||
1. 给 `ChatBloc` 增加可注入回调 `onCalendarMutated`。
|
||||
2. 在 `ToolCallResultEvent` 中识别 `calendar_write` 成功/部分成功并触发回调。
|
||||
3. DI 中将回调绑定为 `CalendarRepository.getDayEvents/getMonthEvents(forceRefresh: true)`。
|
||||
4. 添加回归测试验证回调触发。
|
||||
|
||||
### Task 4: 设置页新增“清理缓存”并触发重新拉取
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/lib/data/cache/cache_store.dart`
|
||||
- Modify: `apps/lib/features/settings/presentation/screens/settings_screen.dart`
|
||||
- Modify: `apps/lib/l10n/app_zh.arb`
|
||||
- Modify: `apps/lib/l10n/app_en.arb`
|
||||
|
||||
**Steps:**
|
||||
1. 在 `HybridCacheStore` 增加按前缀清理能力(`cache:`)。
|
||||
2. 设置页在“检查更新”下新增“清理缓存”。
|
||||
3. 点击后清理缓存并触发 prewarm + inbox 快照刷新。
|
||||
4. 同步中英文文案并生成本地化代码。
|
||||
|
||||
### Task 5: 验证
|
||||
|
||||
**Commands:**
|
||||
- `uv run pytest backend/tests/unit/v1/schedule_items/test_share.py backend/tests/unit/core/agentscope/test_calendar_tools.py -k "share or calendar_share"`
|
||||
- `flutter test test/features/chat/presentation/bloc/chat_bloc_test.dart`
|
||||
- `flutter analyze`
|
||||
|
||||
**Expected:**
|
||||
- 后端分享链路测试通过,新增邀请字段存在。
|
||||
- ChatBloc 回归测试通过,`calendar_write` 成功时触发刷新回调。
|
||||
- Flutter 静态检查通过,无新增错误。
|
||||
Reference in New Issue
Block a user