Files
nj/app/lib/data/models/school_class.dart
iven 5e6c6fdd62 feat(diary): 数据层 + 班级系统 (Phase F1 + B3)
Flutter 数据层 (Phase F1):
- journal_entry.dart: 日记数据模型 (Mood/Weather/tags/version)
- journal_element.dart: 元素模型 (text/image/sticker/handwriting_ref/tape)
- school_class.dart: 班级模型
- user_settings.dart: 用户设置 (主题/画笔/字号)
- isar_database.dart: Isar 初始化
- api_client.dart: Dio + JWT注入 + 离线感知 + 401处理
- journal_repository.dart: 抽象接口 + InMemory实现 (乐观锁)
- sync_engine.dart: WiFi同步 + 操作队列 + 重试(5次) + 快照持久化

Rust 班级系统 (Phase B3):
- class_service.rs: 创建班级(6位码) + 加入班级 + 成员管理
- topic_service.rs: 老师布置主题 + 主题列表
- comment_service.rs: 老师点评 + 评语列表
- class_handler.rs: 5个API端点 + 权限守卫
- topic_handler.rs: 2个API端点
- comment_handler.rs: 2个API端点
- dto.rs: 新增5个DTO (ClassMemberResp/CreateTopicReq/TopicResp/CreateCommentReq/CommentResp)
- 6条新路由注册

验证: cargo check 通过, 433测试全绿, flutter analyze 1 warning
2026-06-01 00:55:51 +08:00

113 lines
3.3 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 班级数据模型 — 手写不可变类(避免 build_runner 依赖)
import 'package:uuid/uuid.dart';
/// 班级 — 老师创建,学生通过班级码加入
///
/// 班级码安全规则:
/// - 6 位字母数字混合62^6 约 568 亿种组合)
/// - 有效期控制(学期结束自动失效)
/// - 连续 5 次错误锁定 30 分钟
class SchoolClass {
final String id;
final String name;
final String schoolName;
final String teacherId;
final String classCode;
final int memberCount;
final bool isActive;
final DateTime? expiresAt;
final DateTime createdAt;
final DateTime updatedAt;
const SchoolClass({
required this.id,
required this.name,
required this.schoolName,
required this.teacherId,
required this.classCode,
this.memberCount = 0,
this.isActive = true,
this.expiresAt,
required this.createdAt,
required this.updatedAt,
});
SchoolClass copyWith({
String? id,
String? name,
String? schoolName,
String? teacherId,
String? classCode,
int? memberCount,
bool? isActive,
DateTime? expiresAt,
bool clearExpiresAt = false,
DateTime? createdAt,
DateTime? updatedAt,
}) =>
SchoolClass(
id: id ?? this.id,
name: name ?? this.name,
schoolName: schoolName ?? this.schoolName,
teacherId: teacherId ?? this.teacherId,
classCode: classCode ?? this.classCode,
memberCount: memberCount ?? this.memberCount,
isActive: isActive ?? this.isActive,
expiresAt: clearExpiresAt ? null : (expiresAt ?? this.expiresAt),
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
);
Map<String, dynamic> toJson() => {
'id': id,
'name': name,
'school_name': schoolName,
'teacher_id': teacherId,
'class_code': classCode,
'member_count': memberCount,
'is_active': isActive,
'expires_at': expiresAt?.toIso8601String(),
'created_at': createdAt.toIso8601String(),
'updated_at': updatedAt.toIso8601String(),
};
factory SchoolClass.fromJson(Map<String, dynamic> json) => SchoolClass(
id: json['id'] as String,
name: json['name'] as String,
schoolName: json['school_name'] as String,
teacherId: json['teacher_id'] as String,
classCode: json['class_code'] as String,
memberCount: (json['member_count'] as int?) ?? 0,
isActive: (json['is_active'] as bool?) ?? true,
expiresAt: json['expires_at'] != null
? DateTime.parse(json['expires_at'] as String)
: null,
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
);
/// 创建新班级的工厂方法 — 自动生成 id 和时间戳
factory SchoolClass.create({
required String name,
required String schoolName,
required String teacherId,
required String classCode,
DateTime? expiresAt,
}) {
final now = DateTime.now();
return SchoolClass(
id: const Uuid().v4(),
name: name,
schoolName: schoolName,
teacherId: teacherId,
classCode: classCode,
memberCount: 0,
isActive: true,
expiresAt: expiresAt,
createdAt: now,
updatedAt: now,
);
}
}