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
@@ -3,6 +3,7 @@ import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:intl/intl.dart';
import 'package:onboarding_overlay/onboarding_overlay.dart';
import '../../../../l10n/app_localizations.dart';
import '../../../../shared/theme/design_tokens.dart';
@@ -16,6 +17,7 @@ import '../../../../shared/widgets/date_time_picker/date_time_picker_bottom_shee
import '../../../../shared/widgets/toast/toast.dart';
import '../../../../shared/widgets/toast/toast_type.dart';
import '../../data/models/divination_backend_models.dart';
import '../../data/apis/divination_api.dart';
import '../../data/models/divination_params.dart';
import '../../data/models/divination_result.dart';
import '../../data/services/divination_run_service.dart';
@@ -26,11 +28,13 @@ class ManualDivinationScreen extends StatefulWidget {
super.key,
required this.params,
required this.runService,
this.divinationApi,
required this.onCompleted,
});
final DivinationParams params;
final DivinationRunService runService;
final DivinationApi? divinationApi;
final Future<void> Function(DivinationResultData result) onCompleted;
@override
@@ -43,6 +47,16 @@ class _ManualDivinationScreenState extends State<ManualDivinationScreen>
final List<YaoType?> _selectedYaos = List<YaoType?>.filled(6, null);
late final AnimationController _blinkController;
bool _submitting = false;
final GlobalKey<OnboardingState> _onboardingKey =
GlobalKey<OnboardingState>();
final ScrollController _scrollController = ScrollController();
final GlobalKey _timeCardKey = GlobalKey();
final GlobalKey _yaoCardKey = GlobalKey();
final GlobalKey _analyzeButtonKey = GlobalKey();
final FocusNode _guideStep1Focus = FocusNode();
final FocusNode _guideStep2Focus = FocusNode();
final FocusNode _guideStep3Focus = FocusNode();
final FocusNode _guideStep4Focus = FocusNode();
@override
void initState() {
@@ -56,7 +70,12 @@ class _ManualDivinationScreenState extends State<ManualDivinationScreen>
@override
void dispose() {
_scrollController.dispose();
_blinkController.dispose();
_guideStep1Focus.dispose();
_guideStep2Focus.dispose();
_guideStep3Focus.dispose();
_guideStep4Focus.dispose();
super.dispose();
}
@@ -66,6 +85,55 @@ class _ManualDivinationScreenState extends State<ManualDivinationScreen>
Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme;
final l10n = AppLocalizations.of(context)!;
final guideSteps = [
OnboardingStep(
focusNode: _guideStep1Focus,
titleText: l10n.manualGuideStep1Title,
bodyText: l10n.manualGuideStep1Body,
titleTextColor: Colors.white,
bodyTextColor: Colors.white,
hasArrow: false,
hasLabelBox: true,
fullscreen: true,
overlayColor: Colors.black.withValues(alpha: 0.7),
),
OnboardingStep(
focusNode: _guideStep2Focus,
titleText: l10n.manualGuideStep2Title,
bodyText: l10n.manualGuideStep2Body,
titleTextColor: Colors.white,
bodyTextColor: Colors.white,
hasArrow: true,
hasLabelBox: true,
arrowPosition: ArrowPosition.top,
delay: const Duration(milliseconds: 320),
overlayColor: Colors.black.withValues(alpha: 0.7),
),
OnboardingStep(
focusNode: _guideStep3Focus,
titleText: l10n.manualGuideStep3Title,
bodyText: l10n.manualGuideStep3Body,
titleTextColor: Colors.white,
bodyTextColor: Colors.white,
hasArrow: true,
hasLabelBox: true,
arrowPosition: ArrowPosition.top,
delay: const Duration(milliseconds: 320),
overlayColor: Colors.black.withValues(alpha: 0.7),
),
OnboardingStep(
focusNode: _guideStep4Focus,
titleText: l10n.manualGuideStep4Title,
bodyText: l10n.manualGuideStep4Body,
titleTextColor: Colors.white,
bodyTextColor: Colors.white,
hasArrow: true,
hasLabelBox: true,
arrowPosition: ArrowPosition.top,
delay: const Duration(milliseconds: 320),
overlayColor: Colors.black.withValues(alpha: 0.7),
),
];
return Scaffold(
backgroundColor: colors.surface,
appBar: AppBar(
@@ -74,49 +142,80 @@ class _ManualDivinationScreenState extends State<ManualDivinationScreen>
backgroundColor: colors.surface,
surfaceTintColor: colors.surface,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(AppSpacing.xl),
child: Column(
children: [
_buildInstruction(),
const SizedBox(height: AppSpacing.lg),
_TimeCard(selectedTime: _selectedTime, onPickTime: _pickTime),
const SizedBox(height: AppSpacing.lg),
_YaoSelectionCard(
selectedYaos: _selectedYaos,
blinkAnimation: _blinkController,
onSelect: _onSelectYao,
onNeedTip: _showOrderTip,
),
const SizedBox(height: AppSpacing.xl),
AnimatedBuilder(
animation: _blinkController,
builder: (context, _) {
final base = colors.primary;
return SizedBox(
width: double.infinity,
height: 50,
child: FilledButton(
onPressed: _allSelected && !_submitting ? _submitRun : null,
style: FilledButton.styleFrom(
backgroundColor: _allSelected
? base.withValues(
alpha: 0.6 + _blinkController.value * 0.4,
)
: base,
),
child: _submitting
? const SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(strokeWidth: 2),
)
: Text(l10n.manualStartResolve),
body: Onboarding(
key: _onboardingKey,
steps: guideSteps,
onChanged: _onGuideStepChanged,
child: SingleChildScrollView(
controller: _scrollController,
padding: const EdgeInsets.all(AppSpacing.xl),
child: Column(
children: [
_buildInstruction(),
const SizedBox(height: AppSpacing.lg),
Container(
key: _timeCardKey,
child: Focus(
focusNode: _guideStep2Focus,
child: _TimeCard(
selectedTime: _selectedTime,
onPickTime: _pickTime,
),
);
},
),
],
),
),
const SizedBox(height: AppSpacing.lg),
Container(
key: _yaoCardKey,
child: Focus(
focusNode: _guideStep3Focus,
child: _YaoSelectionCard(
selectedYaos: _selectedYaos,
blinkAnimation: _blinkController,
onSelect: _onSelectYao,
onNeedTip: _showOrderTip,
),
),
),
const SizedBox(height: AppSpacing.xl),
Container(
key: _analyzeButtonKey,
child: Focus(
focusNode: _guideStep4Focus,
child: AnimatedBuilder(
animation: _blinkController,
builder: (context, _) {
final base = colors.primary;
return SizedBox(
width: double.infinity,
height: 50,
child: FilledButton(
onPressed: _allSelected && !_submitting
? _submitRun
: null,
style: FilledButton.styleFrom(
backgroundColor: _allSelected
? base.withValues(
alpha: 0.6 + _blinkController.value * 0.4,
)
: base,
),
child: _submitting
? const SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(
strokeWidth: 2,
),
)
: Text(l10n.manualStartResolve),
),
);
},
),
),
),
],
),
),
),
);
@@ -126,26 +225,40 @@ class _ManualDivinationScreenState extends State<ManualDivinationScreen>
final l10n = AppLocalizations.of(context)!;
return DivinationInstructionCard(
text: l10n.manualYaoInstruction,
onTap: () {
showDialog<void>(
context: context,
builder: (_) {
return DivinationGuideDialog(
title: l10n.manualSelectYaoTitle,
guideImages: const [
['assets/images/tutorial/tutorial_1.png'],
['assets/images/tutorial/tutorial_2.png'],
['assets/images/tutorial/tutorial_3.png'],
],
instructions: [
l10n.divinationManualGuideStep1,
l10n.divinationManualGuideStep2,
l10n.divinationManualGuideStep3,
],
);
},
);
},
onTap: _showGuide,
);
}
void _showGuide() {
_scrollToGuideStep(0);
Future<void>.delayed(const Duration(milliseconds: 120), () {
if (!mounted) {
return;
}
_onboardingKey.currentState?.show();
});
}
void _onGuideStepChanged(int currentIndex) {
_scrollToGuideStep(currentIndex + 1);
}
void _scrollToGuideStep(int stepIndex) {
final GlobalKey? targetKey = switch (stepIndex) {
1 => _timeCardKey,
2 => _yaoCardKey,
3 => _analyzeButtonKey,
_ => null,
};
final targetContext = targetKey?.currentContext;
if (targetContext == null) {
return;
}
Scrollable.ensureVisible(
targetContext,
duration: const Duration(milliseconds: 260),
curve: Curves.easeOut,
alignment: 0.12,
);
}
@@ -253,6 +366,7 @@ class _ManualDivinationScreenState extends State<ManualDivinationScreen>
params: widget.params.copyWith(divinationTime: _selectedTime),
yaoStates: _selectedYaos.cast<YaoType>(),
runService: widget.runService,
divinationApi: widget.divinationApi,
onCompleted: widget.onCompleted,
),
),
@@ -337,7 +451,10 @@ class _YaoSelectionCard extends StatelessWidget {
Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme;
final l10n = AppLocalizations.of(context)!;
final rowNames = DivinationTerms.yaoNames.reversed.toList();
final rowNames = List<String>.generate(
6,
(i) => DivinationTerms.yaoName(l10n, i),
).reversed.toList();
return Card(
margin: EdgeInsets.zero,
color: colors.surface,