feat: 后端架构重构 — Endpoint→Service→Repository分层 + AI确认机制 + 异步任务持久化
- 核心业务拆分为 Endpoint → Application Service → Repository 三层 - AI写入操作必须用户确认后才写库(确认卡片机制) - 报告/饮食/用药分析改为持久化任务队列(原子领取/重试/重启恢复) - 运动计划修复: 连续真实日期替代周模板 - 用药提醒去重 + 通知Outbox预留 - 认证收拢到AuthService, 管理员收拢到AdminService - AI会话加用户归属校验防串号 - 提示词调整为患者视角 - 开发假数据已关闭 - 21/21测试通过, 0警告0错误
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import 'dart:io';
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
@@ -11,6 +12,7 @@ import '../../providers/auth_provider.dart';
|
||||
import '../../providers/chat_provider.dart';
|
||||
import '../../providers/data_providers.dart';
|
||||
import '../../widgets/health_drawer.dart';
|
||||
import '../../services/in_app_notification_service.dart';
|
||||
import '../diet/diet_capture_page.dart';
|
||||
import 'widgets/chat_messages_view.dart';
|
||||
|
||||
@@ -26,15 +28,74 @@ class _HomePageState extends ConsumerState<HomePage> {
|
||||
final _focusNode = FocusNode();
|
||||
String? _pickedImagePath;
|
||||
int _lastMsgCount = 0;
|
||||
Timer? _notificationTimer;
|
||||
bool _checkingNotifications = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => _checkNotifications());
|
||||
_notificationTimer = Timer.periodic(
|
||||
const Duration(seconds: 30),
|
||||
(_) => _checkNotifications(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_notificationTimer?.cancel();
|
||||
_textCtrl.dispose();
|
||||
_scrollCtrl.dispose();
|
||||
_focusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _checkNotifications() async {
|
||||
if (!mounted || _checkingNotifications) return;
|
||||
if (!ref.read(authProvider).isLoggedIn) return;
|
||||
_checkingNotifications = true;
|
||||
try {
|
||||
final service = InAppNotificationService(ref.read(apiClientProvider));
|
||||
final notifications = await service.getPending();
|
||||
for (final notification in notifications) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
behavior: SnackBarBehavior.floating,
|
||||
duration: const Duration(seconds: 5),
|
||||
content: Row(
|
||||
children: [
|
||||
Icon(
|
||||
notification.type == 'ExerciseReminder'
|
||||
? Icons.directions_run_outlined
|
||||
: Icons.medication_outlined,
|
||||
color: Colors.white,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(notification.title,
|
||||
style: const TextStyle(fontWeight: FontWeight.w700)),
|
||||
Text(notification.message),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
await service.acknowledge(notification.id);
|
||||
}
|
||||
} catch (_) {
|
||||
// App 内提醒失败不阻断主页使用,下次轮询会重试。
|
||||
} finally {
|
||||
_checkingNotifications = false;
|
||||
}
|
||||
}
|
||||
|
||||
void _sendMessage() {
|
||||
final text = _textCtrl.text.trim();
|
||||
final imagePath = _pickedImagePath;
|
||||
|
||||
Reference in New Issue
Block a user