import 'writing_task.dart'; class WritingSubmission { final String id; final String taskId; final String userId; final String content; final int wordCount; final int timeSpent; // 秒 final WritingStatus status; final DateTime submittedAt; final WritingFeedback? feedback; final WritingScore? score; const WritingSubmission({ required this.id, required this.taskId, required this.userId, required this.content, required this.wordCount, required this.timeSpent, required this.status, required this.submittedAt, this.feedback, this.score, }); factory WritingSubmission.fromJson(Map json) { return WritingSubmission( id: json['id'] as String, taskId: json['taskId'] as String, userId: json['userId'] as String, content: json['content'] as String, wordCount: json['wordCount'] as int, timeSpent: json['timeSpent'] as int, status: WritingStatus.values.firstWhere( (e) => e.name == json['status'], orElse: () => WritingStatus.draft, ), submittedAt: DateTime.parse(json['submittedAt'] as String), feedback: json['feedback'] != null ? WritingFeedback.fromJson(json['feedback'] as Map) : null, score: json['score'] != null ? WritingScore.fromJson(json['score'] as Map) : null, ); } Map toJson() { return { 'id': id, 'taskId': taskId, 'userId': userId, 'content': content, 'wordCount': wordCount, 'timeSpent': timeSpent, 'status': status.name, 'submittedAt': submittedAt.toIso8601String(), 'feedback': feedback?.toJson(), 'score': score?.toJson(), }; } WritingSubmission copyWith({ String? id, String? taskId, String? userId, String? content, int? wordCount, int? timeSpent, WritingStatus? status, DateTime? submittedAt, WritingFeedback? feedback, WritingScore? score, }) { return WritingSubmission( id: id ?? this.id, taskId: taskId ?? this.taskId, userId: userId ?? this.userId, content: content ?? this.content, wordCount: wordCount ?? this.wordCount, timeSpent: timeSpent ?? this.timeSpent, status: status ?? this.status, submittedAt: submittedAt ?? this.submittedAt, feedback: feedback ?? this.feedback, score: score ?? this.score, ); } } class WritingFeedback { final String id; final String submissionId; final String overallComment; final List criteriaFeedbacks; final List errors; final List suggestions; final DateTime createdAt; const WritingFeedback({ required this.id, required this.submissionId, required this.overallComment, required this.criteriaFeedbacks, required this.errors, required this.suggestions, required this.createdAt, }); factory WritingFeedback.fromJson(Map json) { return WritingFeedback( id: json['id'] as String, submissionId: json['submissionId'] as String, overallComment: json['overallComment'] as String, criteriaFeedbacks: (json['criteriaFeedbacks'] as List) .map((e) => WritingCriteriaFeedback.fromJson(e as Map)) .toList(), errors: (json['errors'] as List) .map((e) => WritingError.fromJson(e as Map)) .toList(), suggestions: (json['suggestions'] as List) .map((e) => WritingSuggestion.fromJson(e as Map)) .toList(), createdAt: DateTime.parse(json['createdAt'] as String), ); } Map toJson() { return { 'id': id, 'submissionId': submissionId, 'overallComment': overallComment, 'criteriaFeedbacks': criteriaFeedbacks.map((e) => e.toJson()).toList(), 'errors': errors.map((e) => e.toJson()).toList(), 'suggestions': suggestions.map((e) => e.toJson()).toList(), 'createdAt': createdAt.toIso8601String(), }; } } class WritingScore { final String id; final String submissionId; final double totalScore; final double maxScore; final Map criteriaScores; final String grade; final DateTime createdAt; const WritingScore({ required this.id, required this.submissionId, required this.totalScore, required this.maxScore, required this.criteriaScores, required this.grade, required this.createdAt, }); double get percentage => (totalScore / maxScore) * 100; factory WritingScore.fromJson(Map json) { return WritingScore( id: json['id'] as String, submissionId: json['submissionId'] as String, totalScore: (json['totalScore'] as num).toDouble(), maxScore: (json['maxScore'] as num).toDouble(), criteriaScores: Map.fromEntries( (json['criteriaScores'] as Map).entries.map( (e) => MapEntry( WritingCriteria.values.firstWhere((c) => c.name == e.key), (e.value as num).toDouble(), ), ), ), grade: json['grade'] as String, createdAt: DateTime.parse(json['createdAt'] as String), ); } Map toJson() { return { 'id': id, 'submissionId': submissionId, 'totalScore': totalScore, 'maxScore': maxScore, 'criteriaScores': Map.fromEntries( criteriaScores.entries.map( (e) => MapEntry(e.key.name, e.value), ), ), 'grade': grade, 'createdAt': createdAt.toIso8601String(), }; } } class WritingCriteriaFeedback { final WritingCriteria criteria; final double score; final double maxScore; final String comment; final List strengths; final List improvements; const WritingCriteriaFeedback({ required this.criteria, required this.score, required this.maxScore, required this.comment, required this.strengths, required this.improvements, }); factory WritingCriteriaFeedback.fromJson(Map json) { return WritingCriteriaFeedback( criteria: WritingCriteria.values.firstWhere( (e) => e.name == json['criteria'], ), score: (json['score'] as num).toDouble(), maxScore: (json['maxScore'] as num).toDouble(), comment: json['comment'] as String, strengths: List.from(json['strengths'] ?? []), improvements: List.from(json['improvements'] ?? []), ); } Map toJson() { return { 'criteria': criteria.name, 'score': score, 'maxScore': maxScore, 'comment': comment, 'strengths': strengths, 'improvements': improvements, }; } } class WritingError { final WritingErrorType type; final String description; final String originalText; final String? suggestedText; final int startPosition; final int endPosition; final String explanation; const WritingError({ required this.type, required this.description, required this.originalText, this.suggestedText, required this.startPosition, required this.endPosition, required this.explanation, }); factory WritingError.fromJson(Map json) { return WritingError( type: WritingErrorType.values.firstWhere( (e) => e.name == json['type'], ), description: json['description'] as String, originalText: json['originalText'] as String, suggestedText: json['suggestedText'] as String?, startPosition: json['startPosition'] as int, endPosition: json['endPosition'] as int, explanation: json['explanation'] as String, ); } Map toJson() { return { 'type': type.name, 'description': description, 'originalText': originalText, 'suggestedText': suggestedText, 'startPosition': startPosition, 'endPosition': endPosition, 'explanation': explanation, }; } } class WritingSuggestion { final WritingSuggestionType type; final String title; final String description; final String? example; final int? position; const WritingSuggestion({ required this.type, required this.title, required this.description, this.example, this.position, }); factory WritingSuggestion.fromJson(Map json) { return WritingSuggestion( type: WritingSuggestionType.values.firstWhere( (e) => e.name == json['type'], ), title: json['title'] as String, description: json['description'] as String, example: json['example'] as String?, position: json['position'] as int?, ); } Map toJson() { return { 'type': type.name, 'title': title, 'description': description, 'example': example, 'position': position, }; } } enum WritingStatus { draft, submitted, grading, graded, revised, } enum WritingCriteria { content, organization, vocabulary, grammar, mechanics, } enum WritingErrorType { grammar, spelling, punctuation, vocabulary, structure, style, } enum WritingSuggestionType { improvement, enhancement, alternative, clarification, } extension WritingStatusExtension on WritingStatus { String get displayName { switch (this) { case WritingStatus.draft: return '草稿'; case WritingStatus.submitted: return '已提交'; case WritingStatus.grading: return '评分中'; case WritingStatus.graded: return '已评分'; case WritingStatus.revised: return '已修改'; } } } extension WritingCriteriaExtension on WritingCriteria { String get displayName { switch (this) { case WritingCriteria.content: return '内容'; case WritingCriteria.organization: return '结构'; case WritingCriteria.vocabulary: return '词汇'; case WritingCriteria.grammar: return '语法'; case WritingCriteria.mechanics: return '拼写标点'; } } } extension WritingErrorTypeExtension on WritingErrorType { String get displayName { switch (this) { case WritingErrorType.grammar: return '语法错误'; case WritingErrorType.spelling: return '拼写错误'; case WritingErrorType.punctuation: return '标点错误'; case WritingErrorType.vocabulary: return '词汇错误'; case WritingErrorType.structure: return '结构错误'; case WritingErrorType.style: return '风格问题'; } } }