feat: AI 意图路由/草稿存储 + Prompts 模块化 + UI 视觉统一 + AI 同意门 + 资源更新

后端:
- 新增 ai_intent_router 意图路由
- 新增 AiEntryDraft 草稿存储 + EF 迁移
- 新增 Prompts 模块化(global/modules/rag/router)
- AI Agent handlers 扩展

前端:
- 新增 AI 同意门 (ai_consent_gate/provider/details_page)
- HealthMetricVisuals 视觉统一
- 设备扫描/管理页面优化
- agent 插图替换 + 趋势指标图标

配置:
- DeepSeek 模型改 deepseek-v4-flash
- .gitignore 加 artifacts/audit
- build.gradle.kts 签名配置调整
This commit is contained in:
MingNian
2026-07-28 15:03:38 +08:00
parent 25a4078688
commit b4bc15b9b9
88 changed files with 5527 additions and 1240 deletions

View File

@@ -0,0 +1,154 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../core/app_colors.dart';
import '../../core/app_design_tokens.dart';
import '../../providers/ai_consent_provider.dart';
import '../../providers/auth_provider.dart';
class AiConsentDetailsPage extends ConsumerWidget {
const AiConsentDetailsPage({super.key});
Future<void> _revoke(BuildContext context, WidgetRef ref) async {
final userId = ref.read(authProvider).user?.id;
if (userId == null || userId.isEmpty) return;
final confirmed = await showDialog<bool>(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('撤回 AI 授权?'),
content: const Text('撤回后将退出当前账号,重新使用 App 前需要再次完成 AI 数据授权。'),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx, false),
child: const Text('取消'),
),
FilledButton(
onPressed: () => Navigator.pop(ctx, true),
child: const Text('撤回并退出'),
),
],
),
);
if (confirmed != true || !context.mounted) return;
final revoked = await ref.read(aiConsentProvider.notifier).revoke(userId);
if (!revoked) {
if (context.mounted) {
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('撤回授权失败,请稍后重试')));
}
return;
}
await ref.read(authProvider.notifier).logout();
}
@override
Widget build(BuildContext context, WidgetRef ref) {
final consent = ref.watch(aiConsentProvider);
final canRevoke = consent.granted && ref.watch(authProvider).user != null;
return Scaffold(
backgroundColor: AppColors.background,
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
scrolledUnderElevation: 0,
leading: IconButton(
icon: const Icon(Icons.arrow_back, color: AppColors.textPrimary),
onPressed: () => Navigator.of(context).maybePop(),
),
title: const Text(
'AI 数据授权说明',
style: TextStyle(
fontSize: 19,
fontWeight: FontWeight.w700,
color: AppColors.textPrimary,
),
),
centerTitle: true,
),
body: SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.fromLTRB(20, 20, 20, 32),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_section(
'我们会处理什么数据',
'当你使用 AI 对话、饮食识别或报告预整理功能时,可能会处理你主动输入的文字、健康数据、健康档案摘要、图片和 PDF 报告。',
),
_section(
'发送给哪些服务商',
'DeepSeek用于生成 AI 对话和健康信息整理。\n\n通义千问视觉服务:用于识别你主动上传的饮食图片、报告图片或 PDF 内容。\n\nFastGPT用于检索与问题相关的医学参考资料。',
),
_section(
'使用目的',
'上述数据仅用于提供 AI 对话、健康记录辅助、饮食图片识别、医学报告预整理和医学资料检索功能。AI 内容仅供健康管理参考,不能替代医生诊断或治疗。',
),
_section(
'你的选择',
'你可以在首次进入首页时选择是否授权。未授权时无法进入本 App 的主要服务。你也可以在设置中撤回授权;撤回后将退出当前账号,再次使用前需要重新授权。',
),
_section(
'更多信息',
'数据保存、删除、第三方服务保护措施和联系方式,请同时查看 App 内《隐私政策》和《第三方 SDK 共享清单》。',
),
if (canRevoke) ...[
const SizedBox(height: 12),
SizedBox(
width: double.infinity,
child: OutlinedButton(
onPressed: consent.isSaving
? null
: () => _revoke(context, ref),
style: OutlinedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: AppColors.error,
side: BorderSide(
color: AppColors.error.withValues(alpha: 0.4),
),
minimumSize: const Size.fromHeight(50),
),
child: consent.isSaving
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2.2,
color: AppColors.error,
),
)
: const Text('撤回 AI 数据授权'),
),
),
],
],
),
),
),
);
}
static Widget _section(String title, String body) {
return Padding(
padding: const EdgeInsets.only(bottom: 22),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: AppTextStyles.sectionTitle),
const SizedBox(height: 8),
Text(
body,
style: const TextStyle(
fontSize: 15,
height: 1.65,
color: AppColors.textSecondary,
),
),
],
),
);
}
}

View File

@@ -68,6 +68,11 @@ class SettingsPage extends ConsumerWidget {
params: {'type': 'about'},
),
),
_SettingsTile(
icon: LucideIcons.bot,
title: 'AI 数据授权',
onTap: () => pushRoute(ref, 'aiConsentDetails'),
),
_SettingsTile(
icon: LucideIcons.shield,
title: '隐私协议',