class ExamRecord { const ExamRecord({ required this.code, required this.name, this.assessment = '', this.candidate = '', this.sequence = '', this.room = '', this.seat = '', this.time = '', }); final String code, name, assessment, candidate, sequence, room, seat, time; Map toJson() => { 'code': code, 'name': name, 'assessment': assessment, 'candidate': candidate, 'sequence': sequence, 'room': room, 'seat': seat, 'time': time, }; factory ExamRecord.fromJson(Map json) { String field(String key) { final value = json[key]; if (value is! String || value.length > 1000) { throw const FormatException('考试信息无效'); } return value; } final record = ExamRecord( code: field('code'), name: field('name'), assessment: field('assessment'), candidate: field('candidate'), sequence: field('sequence'), room: field('room'), seat: field('seat'), time: field('time'), ); if (record.code.isEmpty || record.name.isEmpty) { throw const FormatException('考试课程无效'); } return record; } } class ExamSchedule { ExamSchedule({ required this.semesterLabel, required this.termId, required List records, }) : records = List.unmodifiable(records); final String semesterLabel, termId; final List records; Map toJson() => { 'semesterLabel': semesterLabel, 'termId': termId, 'records': records.map((r) => r.toJson()).toList(), }; factory ExamSchedule.fromJson(Map json) { final label = json['semesterLabel'], id = json['termId'], rows = json['records']; if (label is! String || label.trim().isEmpty || id is! String || !RegExp(r'^\d+$').hasMatch(id) || rows is! List || rows.length > 500) { throw const FormatException('考试列表无效'); } final records = rows .map((r) => ExamRecord.fromJson(Map.from(r as Map))) .toList(); if (records.map((r) => r.code).toSet().length != records.length) { throw const FormatException('考试课程重复'); } return ExamSchedule(semesterLabel: label, termId: id, records: records); } }