docs: 更新协议文档,删除废弃计划文档

- 更新 http-error-codes, user-points-chat-data-protocol
- 更新 divination-run-protocol, profile-protocol
- 删除废弃的后端和前端设计计划文档
This commit is contained in:
qzl
2026-04-08 17:23:02 +08:00
parent 49fc9a116f
commit e80a82bef4
57 changed files with 4117 additions and 2269 deletions
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:intl/intl.dart';
import '../../../../core/logging/logger.dart';
import '../../../../l10n/app_localizations.dart';
import '../../../../shared/theme/app_color_palette.dart';
import '../../../../shared/theme/design_tokens.dart';
@@ -12,24 +13,37 @@ import '../../../../shared/widgets/divination/yao_glyph.dart';
import '../../../../shared/widgets/divination/yao_legend.dart';
import '../../../../shared/widgets/toast/toast.dart';
import '../../../../shared/widgets/toast/toast_type.dart';
import '../../data/apis/divination_api.dart';
import '../../data/models/divination_params.dart';
import '../../data/models/divination_result.dart';
import 'follow_up_chat_screen.dart';
class DivinationResultScreen extends StatefulWidget {
const DivinationResultScreen({super.key, required this.data});
const DivinationResultScreen({
super.key,
required this.data,
this.divinationApi,
this.enableIntroTransition = false,
});
final DivinationResultData data;
final DivinationApi? divinationApi;
final bool enableIntroTransition;
@override
State<DivinationResultScreen> createState() => _DivinationResultScreenState();
}
class _DivinationResultScreenState extends State<DivinationResultScreen> {
bool _showIntro = true;
static final Logger _logger = getLogger('features.divination.result_screen');
bool _showIntro = false;
bool _introCollapsed = false;
Rect? _introTargetRect;
final GlobalKey _stackKey = GlobalKey();
final GlobalKey _finalSignCardKey = GlobalKey();
bool _followUpEligibilityLoading = false;
bool _canSendFollowUp = true;
void _backToHome() {
final navigator = Navigator.of(context);
@@ -39,9 +53,48 @@ class _DivinationResultScreenState extends State<DivinationResultScreen> {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
_prepareIntro();
if (widget.enableIntroTransition) {
_showIntro = true;
WidgetsBinding.instance.addPostFrameCallback((_) {
_prepareIntro();
});
}
_loadFollowUpEligibility();
}
Future<void> _loadFollowUpEligibility() async {
if (widget.divinationApi == null || widget.data.threadId == null) {
return;
}
setState(() {
_followUpEligibilityLoading = true;
});
try {
final messages = await widget.divinationApi!.getSessionMessages(
threadId: widget.data.threadId!,
);
final userCount = messages.where((msg) => msg.role == 'user').length;
if (!mounted) {
return;
}
setState(() {
_canSendFollowUp = userCount < 2;
_followUpEligibilityLoading = false;
});
} catch (error, stackTrace) {
_logger.error(
message: 'Failed to load follow-up eligibility',
error: error,
stackTrace: stackTrace,
);
if (!mounted) {
return;
}
setState(() {
_canSendFollowUp = false;
_followUpEligibilityLoading = false;
});
}
}
Future<void> _prepareIntro() async {
@@ -138,6 +191,7 @@ class _DivinationResultScreenState extends State<DivinationResultScreen> {
title: Text(l10n.resultScreenTitle),
centerTitle: true,
),
bottomNavigationBar: _buildFollowUpBar(context),
body: LayoutBuilder(
builder: (context, constraints) {
final stackSize = Size(constraints.maxWidth, constraints.maxHeight);
@@ -273,6 +327,24 @@ class _DivinationResultScreenState extends State<DivinationResultScreen> {
),
),
),
Positioned(
left: 0,
right: 0,
bottom: 0,
height: 80,
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
colors.surface.withValues(alpha: 0),
colors.surface,
],
),
),
),
),
],
);
},
@@ -280,6 +352,72 @@ class _DivinationResultScreenState extends State<DivinationResultScreen> {
),
);
}
Widget? _buildFollowUpBar(BuildContext context) {
if (widget.divinationApi == null || widget.data.threadId == null) {
return null;
}
final l10n = AppLocalizations.of(context)!;
final colors = Theme.of(context).colorScheme;
return SafeArea(
top: false,
child: Container(
padding: const EdgeInsets.fromLTRB(
AppSpacing.md,
AppSpacing.sm,
AppSpacing.md,
AppSpacing.md,
),
decoration: BoxDecoration(color: colors.surface),
child: Row(
children: [
Expanded(
child: Text(
_canSendFollowUp
? l10n.followUpEntryHint
: l10n.followUpQuotaUsed,
style: Theme.of(context).textTheme.bodyMedium,
),
),
const SizedBox(width: AppSpacing.sm),
FilledButton(
onPressed: _followUpEligibilityLoading
? null
: () async {
await Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (_) => FollowUpChatScreen(
result: widget.data,
api: widget.divinationApi!,
threadId: widget.data.threadId!,
),
),
);
if (!mounted) {
return;
}
await _loadFollowUpEligibility();
},
child: _followUpEligibilityLoading
? SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(
strokeWidth: 2,
color: colors.onPrimary,
),
)
: Text(
_canSendFollowUp
? l10n.followUpEntryAction
: l10n.followUpViewHistory,
),
),
],
),
),
);
}
}
class _ResultHeader extends StatelessWidget {