dramaling-app/apps/mobile/lib/features/dialogue/models/dialogue_models.dart

547 lines
15 KiB
Dart

/// 對話場景模型
class DialogueScene {
final String id;
final String name;
final String description;
final String backgroundImageUrl;
final String characterId;
final String difficultyLevel;
final List<String> tags;
final Map<String, dynamic> metadata;
DialogueScene({
required this.id,
required this.name,
required this.description,
required this.backgroundImageUrl,
required this.characterId,
required this.difficultyLevel,
this.tags = const [],
this.metadata = const {},
});
factory DialogueScene.fromJson(Map<String, dynamic> json) {
return DialogueScene(
id: json['id'] as String,
name: json['name'] as String,
description: json['description'] as String,
backgroundImageUrl: json['backgroundImageUrl'] as String,
characterId: json['characterId'] as String,
difficultyLevel: json['difficultyLevel'] as String,
tags: List<String>.from(json['tags'] ?? []),
metadata: json['metadata'] as Map<String, dynamic>? ?? {},
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'description': description,
'backgroundImageUrl': backgroundImageUrl,
'characterId': characterId,
'difficultyLevel': difficultyLevel,
'tags': tags,
'metadata': metadata,
};
}
}
/// 對話角色模型
class DialogueCharacter {
final String id;
final String name;
final String description;
final String avatarUrl;
final String personality;
final String role;
final String background;
final List<String> specialities;
final Map<String, String> localizedNames;
DialogueCharacter({
required this.id,
required this.name,
required this.description,
required this.avatarUrl,
required this.personality,
required this.role,
required this.background,
this.specialities = const [],
this.localizedNames = const {},
});
factory DialogueCharacter.fromJson(Map<String, dynamic> json) {
return DialogueCharacter(
id: json['id'] as String,
name: json['name'] as String,
description: json['description'] as String,
avatarUrl: json['avatarUrl'] as String,
personality: json['personality'] as String,
role: json['role'] as String,
background: json['background'] as String,
specialities: List<String>.from(json['specialities'] ?? []),
localizedNames: Map<String, String>.from(json['localizedNames'] ?? {}),
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'description': description,
'avatarUrl': avatarUrl,
'personality': personality,
'role': role,
'background': background,
'specialities': specialities,
'localizedNames': localizedNames,
};
}
}
/// 對話消息模型
class DialogueMessage {
final String id;
final String content;
final bool isUser;
final DateTime timestamp;
final DialogueMessageType type;
final Map<String, dynamic>? metadata;
final String? audioUrl;
final double? confidence;
DialogueMessage({
required this.id,
required this.content,
required this.isUser,
required this.timestamp,
this.type = DialogueMessageType.text,
this.metadata,
this.audioUrl,
this.confidence,
});
factory DialogueMessage.fromJson(Map<String, dynamic> json) {
return DialogueMessage(
id: json['id'] as String,
content: json['content'] as String,
isUser: json['isUser'] as bool,
timestamp: DateTime.parse(json['timestamp'] as String),
type: DialogueMessageType.values.firstWhere(
(e) => e.toString() == 'DialogueMessageType.${json['type']}',
orElse: () => DialogueMessageType.text,
),
metadata: json['metadata'] as Map<String, dynamic>?,
audioUrl: json['audioUrl'] as String?,
confidence: json['confidence'] as double?,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'content': content,
'isUser': isUser,
'timestamp': timestamp.toIso8601String(),
'type': type.toString().split('.').last,
'metadata': metadata,
'audioUrl': audioUrl,
'confidence': confidence,
};
}
}
/// 對話消息類型
enum DialogueMessageType {
text,
audio,
system,
hint,
}
/// 對話任務模型
class DialogueTask {
final String id;
final String title;
final String description;
final DialogueTaskType type;
final Map<String, dynamic> requirements;
final double progress;
final bool isCompleted;
final int maxAttempts;
final int currentAttempts;
final String? completionMessage;
DialogueTask({
required this.id,
required this.title,
required this.description,
required this.type,
required this.requirements,
this.progress = 0.0,
this.isCompleted = false,
this.maxAttempts = 3,
this.currentAttempts = 0,
this.completionMessage,
});
factory DialogueTask.fromJson(Map<String, dynamic> json) {
return DialogueTask(
id: json['id'] as String,
title: json['title'] as String,
description: json['description'] as String,
type: DialogueTaskType.values.firstWhere(
(e) => e.toString() == 'DialogueTaskType.${json['type']}',
orElse: () => DialogueTaskType.conversation,
),
requirements: json['requirements'] as Map<String, dynamic>,
progress: json['progress'] as double? ?? 0.0,
isCompleted: json['isCompleted'] as bool? ?? false,
maxAttempts: json['maxAttempts'] as int? ?? 3,
currentAttempts: json['currentAttempts'] as int? ?? 0,
completionMessage: json['completionMessage'] as String?,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'title': title,
'description': description,
'type': type.toString().split('.').last,
'requirements': requirements,
'progress': progress,
'isCompleted': isCompleted,
'maxAttempts': maxAttempts,
'currentAttempts': currentAttempts,
'completionMessage': completionMessage,
};
}
DialogueTask copyWith({
String? id,
String? title,
String? description,
DialogueTaskType? type,
Map<String, dynamic>? requirements,
double? progress,
bool? isCompleted,
int? maxAttempts,
int? currentAttempts,
String? completionMessage,
}) {
return DialogueTask(
id: id ?? this.id,
title: title ?? this.title,
description: description ?? this.description,
type: type ?? this.type,
requirements: requirements ?? this.requirements,
progress: progress ?? this.progress,
isCompleted: isCompleted ?? this.isCompleted,
maxAttempts: maxAttempts ?? this.maxAttempts,
currentAttempts: currentAttempts ?? this.currentAttempts,
completionMessage: completionMessage ?? this.completionMessage,
);
}
}
/// 對話任務類型
enum DialogueTaskType {
conversation, // 完成對話
vocabulary, // 使用指定詞彙
grammar, // 語法練習
pronunciation, // 發音練習
comprehension, // 理解測試
}
/// 對話分析結果模型
class DialogueAnalysis {
final String id;
final String userReply;
final DateTime timestamp;
// 三維度評分
final double grammarScore;
final double semanticsScore;
final double fluencyScore;
// 詳細分析
final List<GrammarIssue> grammarIssues;
final List<String> usedVocabulary;
final List<String> missedVocabulary;
final List<String> suggestions;
// 任務相關
final double? taskProgress;
final bool isDialogueComplete;
// 其他
final Map<String, dynamic> metadata;
DialogueAnalysis({
required this.id,
required this.userReply,
required this.timestamp,
required this.grammarScore,
required this.semanticsScore,
required this.fluencyScore,
this.grammarIssues = const [],
this.usedVocabulary = const [],
this.missedVocabulary = const [],
this.suggestions = const [],
this.taskProgress,
this.isDialogueComplete = false,
this.metadata = const {},
});
factory DialogueAnalysis.fromJson(Map<String, dynamic> json) {
return DialogueAnalysis(
id: json['id'] as String,
userReply: json['userReply'] as String,
timestamp: DateTime.parse(json['timestamp'] as String),
grammarScore: json['grammarScore'] as double,
semanticsScore: json['semanticsScore'] as double,
fluencyScore: json['fluencyScore'] as double,
grammarIssues: (json['grammarIssues'] as List?)
?.map((e) => GrammarIssue.fromJson(e as Map<String, dynamic>))
.toList() ?? [],
usedVocabulary: List<String>.from(json['usedVocabulary'] ?? []),
missedVocabulary: List<String>.from(json['missedVocabulary'] ?? []),
suggestions: List<String>.from(json['suggestions'] ?? []),
taskProgress: json['taskProgress'] as double?,
isDialogueComplete: json['isDialogueComplete'] as bool? ?? false,
metadata: json['metadata'] as Map<String, dynamic>? ?? {},
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'userReply': userReply,
'timestamp': timestamp.toIso8601String(),
'grammarScore': grammarScore,
'semanticsScore': semanticsScore,
'fluencyScore': fluencyScore,
'grammarIssues': grammarIssues.map((e) => e.toJson()).toList(),
'usedVocabulary': usedVocabulary,
'missedVocabulary': missedVocabulary,
'suggestions': suggestions,
'taskProgress': taskProgress,
'isDialogueComplete': isDialogueComplete,
'metadata': metadata,
};
}
}
/// 語法問題模型
class GrammarIssue {
final String type;
final String description;
final String originalText;
final String suggestedText;
final int position;
final int length;
final GrammarIssueSeverity severity;
GrammarIssue({
required this.type,
required this.description,
required this.originalText,
required this.suggestedText,
required this.position,
required this.length,
required this.severity,
});
factory GrammarIssue.fromJson(Map<String, dynamic> json) {
return GrammarIssue(
type: json['type'] as String,
description: json['description'] as String,
originalText: json['originalText'] as String,
suggestedText: json['suggestedText'] as String,
position: json['position'] as int,
length: json['length'] as int,
severity: GrammarIssueSeverity.values.firstWhere(
(e) => e.toString() == 'GrammarIssueSeverity.${json['severity']}',
orElse: () => GrammarIssueSeverity.minor,
),
);
}
Map<String, dynamic> toJson() {
return {
'type': type,
'description': description,
'originalText': originalText,
'suggestedText': suggestedText,
'position': position,
'length': length,
'severity': severity.toString().split('.').last,
};
}
}
/// 語法問題嚴重程度
enum GrammarIssueSeverity {
minor,
moderate,
major,
critical,
}
/// 對話最終得分模型
class DialogueScore {
final double grammarScore;
final double semanticsScore;
final double fluencyScore;
final double taskBonus;
final double vocabularyBonus;
final double timeBonus;
final double totalScore;
final int starRating;
final DateTime timestamp;
final Map<String, dynamic> breakdown;
DialogueScore({
required this.grammarScore,
required this.semanticsScore,
required this.fluencyScore,
required this.taskBonus,
required this.vocabularyBonus,
required this.timeBonus,
required this.totalScore,
required this.starRating,
DateTime? timestamp,
this.breakdown = const {},
}) : timestamp = timestamp ?? DateTime.now();
factory DialogueScore.fromJson(Map<String, dynamic> json) {
return DialogueScore(
grammarScore: json['grammarScore'] as double,
semanticsScore: json['semanticsScore'] as double,
fluencyScore: json['fluencyScore'] as double,
taskBonus: json['taskBonus'] as double,
vocabularyBonus: json['vocabularyBonus'] as double,
timeBonus: json['timeBonus'] as double,
totalScore: json['totalScore'] as double,
starRating: json['starRating'] as int,
timestamp: DateTime.parse(json['timestamp'] as String),
breakdown: json['breakdown'] as Map<String, dynamic>? ?? {},
);
}
Map<String, dynamic> toJson() {
return {
'grammarScore': grammarScore,
'semanticsScore': semanticsScore,
'fluencyScore': fluencyScore,
'taskBonus': taskBonus,
'vocabularyBonus': vocabularyBonus,
'timeBonus': timeBonus,
'totalScore': totalScore,
'starRating': starRating,
'timestamp': timestamp.toIso8601String(),
'breakdown': breakdown,
};
}
String get grade {
if (totalScore >= 90) return 'A+';
if (totalScore >= 80) return 'A';
if (totalScore >= 70) return 'B';
if (totalScore >= 60) return 'C';
if (totalScore >= 50) return 'D';
return 'F';
}
String get comment {
switch (starRating) {
case 3:
return '優秀!你的表現非常出色!';
case 2:
return '很好!繼續努力就能更進一步!';
case 1:
return '不錯!還有改進的空間。';
default:
return '需要更多練習,加油!';
}
}
}
/// 詞彙項目模型
class VocabularyItem {
final String id;
final String word;
final String definition;
final String pronunciation;
final List<String> examples;
final String category;
final int difficulty;
final bool isRequired;
final bool isUsed;
VocabularyItem({
required this.id,
required this.word,
required this.definition,
required this.pronunciation,
this.examples = const [],
this.category = '',
this.difficulty = 1,
this.isRequired = false,
this.isUsed = false,
});
factory VocabularyItem.fromJson(Map<String, dynamic> json) {
return VocabularyItem(
id: json['id'] as String,
word: json['word'] as String,
definition: json['definition'] as String,
pronunciation: json['pronunciation'] as String,
examples: List<String>.from(json['examples'] ?? []),
category: json['category'] as String? ?? '',
difficulty: json['difficulty'] as int? ?? 1,
isRequired: json['isRequired'] as bool? ?? false,
isUsed: json['isUsed'] as bool? ?? false,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'word': word,
'definition': definition,
'pronunciation': pronunciation,
'examples': examples,
'category': category,
'difficulty': difficulty,
'isRequired': isRequired,
'isUsed': isUsed,
};
}
VocabularyItem copyWith({
String? id,
String? word,
String? definition,
String? pronunciation,
List<String>? examples,
String? category,
int? difficulty,
bool? isRequired,
bool? isUsed,
}) {
return VocabularyItem(
id: id ?? this.id,
word: word ?? this.word,
definition: definition ?? this.definition,
pronunciation: pronunciation ?? this.pronunciation,
examples: examples ?? this.examples,
category: category ?? this.category,
difficulty: difficulty ?? this.difficulty,
isRequired: isRequired ?? this.isRequired,
isUsed: isUsed ?? this.isUsed,
);
}
}