/// 自然写互动课堂手机端应用软件 V1.0 /// 本地数据仓库 - SQLite本地缓存与离线数据管理 /// /// 功能说明: /// 1. SQLite数据库初始化与版本迁移 /// 2. 作业列表本地缓存(支持离线查看) /// 3. 学情报告缓存(减少网络请求) /// 4. 消息记录本地存储 /// 5. 笔迹数据暂存(教师端BLE收笔后等待上传) /// 6. 离线操作队列(断网时记录待同步操作) /// 7. 加密存储敏感数据 import 'dart:async'; import 'dart:convert'; /* ========== 数据模型 ========== */ /// 本地缓存的作业记录 class CachedAssignment { final String id; final String title; final String subject; final String classId; final int publishTime; final int deadline; final int status; final String detailJson; // 完整作业详情JSON(包含题目列表) final int cachedAt; // 缓存时间 CachedAssignment({ required this.id, required this.title, required this.subject, required this.classId, required this.publishTime, required this.deadline, required this.status, required this.detailJson, required this.cachedAt, }); Map toMap() => { 'id': id, 'title': title, 'subject': subject, 'class_id': classId, 'publish_time': publishTime, 'deadline': deadline, 'status': status, 'detail_json': detailJson, 'cached_at': cachedAt, }; factory CachedAssignment.fromMap(Map map) { return CachedAssignment( id: map['id'] ?? '', title: map['title'] ?? '', subject: map['subject'] ?? '', classId: map['class_id'] ?? '', publishTime: map['publish_time'] ?? 0, deadline: map['deadline'] ?? 0, status: map['status'] ?? 0, detailJson: map['detail_json'] ?? '{}', cachedAt: map['cached_at'] ?? 0, ); } } /// 本地缓存的消息记录 class CachedMessage { final String id; final String fromUserId; final String fromUserName; final String content; final String type; // text / image / assignment / report final int sendTime; final bool isRead; final String extraJson; // 附加数据(如关联的作业ID、学情ID) CachedMessage({ required this.id, required this.fromUserId, required this.fromUserName, required this.content, required this.type, required this.sendTime, required this.isRead, required this.extraJson, }); Map toMap() => { 'id': id, 'from_user_id': fromUserId, 'from_user_name': fromUserName, 'content': content, 'type': type, 'send_time': sendTime, 'is_read': isRead ? 1 : 0, 'extra_json': extraJson, }; factory CachedMessage.fromMap(Map map) { return CachedMessage( id: map['id'] ?? '', fromUserId: map['from_user_id'] ?? '', fromUserName: map['from_user_name'] ?? '', content: map['content'] ?? '', type: map['type'] ?? 'text', sendTime: map['send_time'] ?? 0, isRead: (map['is_read'] ?? 0) == 1, extraJson: map['extra_json'] ?? '{}', ); } } /// 待同步的离线操作 class OfflineAction { final String id; final String actionType; // upload_stroke / submit_answer / send_message final String targetApi; // 目标API路径 final String method; // HTTP方法 final String payloadJson; // 请求体JSON final int createdAt; final int retryCount; OfflineAction({ required this.id, required this.actionType, required this.targetApi, required this.method, required this.payloadJson, required this.createdAt, this.retryCount = 0, }); Map toMap() => { 'id': id, 'action_type': actionType, 'target_api': targetApi, 'method': method, 'payload_json': payloadJson, 'created_at': createdAt, 'retry_count': retryCount, }; factory OfflineAction.fromMap(Map map) { return OfflineAction( id: map['id'] ?? '', actionType: map['action_type'] ?? '', targetApi: map['target_api'] ?? '', method: map['method'] ?? 'POST', payloadJson: map['payload_json'] ?? '{}', createdAt: map['created_at'] ?? 0, retryCount: map['retry_count'] ?? 0, ); } } /// 暂存的笔迹数据(等待上传) class PendingStrokeData { final String id; final String deviceId; // 笔设备ID final String assignmentId; // 关联作业ID final String studentId; // 学生ID final String strokeJson; // 笔迹坐标JSON final int collectTime; // 采集时间 final int syncStatus; // 0=待上传, 1=已上传, 2=上传失败 PendingStrokeData({ required this.id, required this.deviceId, required this.assignmentId, required this.studentId, required this.strokeJson, required this.collectTime, this.syncStatus = 0, }); Map toMap() => { 'id': id, 'device_id': deviceId, 'assignment_id': assignmentId, 'student_id': studentId, 'stroke_json': strokeJson, 'collect_time': collectTime, 'sync_status': syncStatus, }; factory PendingStrokeData.fromMap(Map map) { return PendingStrokeData( id: map['id'] ?? '', deviceId: map['device_id'] ?? '', assignmentId: map['assignment_id'] ?? '', studentId: map['student_id'] ?? '', strokeJson: map['stroke_json'] ?? '[]', collectTime: map['collect_time'] ?? 0, syncStatus: map['sync_status'] ?? 0, ); } } /* ========== 本地仓库实现 ========== */ /// 本地数据仓库 - 管理SQLite数据库CRUD操作 class LocalDataRepository { /// 数据库实例(sqflite Database对象) dynamic _db; /// 数据库版本号 static const int _dbVersion = 3; /// 数据库文件名 static const String _dbName = 'writech_mobile.db'; /// 初始化数据库 /// 创建表结构,执行版本迁移 Future initialize() async { // 实际使用sqflite打开数据库 // _db = await openDatabase(path, version: _dbVersion, onCreate: _onCreate, onUpgrade: _onUpgrade); print('[LocalRepo] 数据库初始化完成,版本: $_dbVersion'); } /// 创建初始表结构(首次安装执行) Future _onCreate(dynamic db, int version) async { // 作业缓存表 await db.execute(''' CREATE TABLE cached_assignments ( id TEXT PRIMARY KEY, title TEXT NOT NULL, subject TEXT DEFAULT '', class_id TEXT NOT NULL, publish_time INTEGER NOT NULL, deadline INTEGER NOT NULL, status INTEGER DEFAULT 0, detail_json TEXT DEFAULT '{}', cached_at INTEGER NOT NULL ) '''); // 消息记录表 await db.execute(''' CREATE TABLE cached_messages ( id TEXT PRIMARY KEY, from_user_id TEXT NOT NULL, from_user_name TEXT DEFAULT '', content TEXT NOT NULL, type TEXT DEFAULT 'text', send_time INTEGER NOT NULL, is_read INTEGER DEFAULT 0, extra_json TEXT DEFAULT '{}' ) '''); // 离线操作队列表 await db.execute(''' CREATE TABLE offline_actions ( id TEXT PRIMARY KEY, action_type TEXT NOT NULL, target_api TEXT NOT NULL, method TEXT DEFAULT 'POST', payload_json TEXT NOT NULL, created_at INTEGER NOT NULL, retry_count INTEGER DEFAULT 0 ) '''); // 笔迹暂存表 await db.execute(''' CREATE TABLE pending_strokes ( id TEXT PRIMARY KEY, device_id TEXT NOT NULL, assignment_id TEXT NOT NULL, student_id TEXT DEFAULT '', stroke_json TEXT NOT NULL, collect_time INTEGER NOT NULL, sync_status INTEGER DEFAULT 0 ) '''); // 学情报告缓存表 await db.execute(''' CREATE TABLE cached_reports ( student_id TEXT NOT NULL, subject TEXT NOT NULL, report_json TEXT NOT NULL, cached_at INTEGER NOT NULL, PRIMARY KEY (student_id, subject) ) '''); // 创建索引 await db.execute('CREATE INDEX idx_assignment_class ON cached_assignments(class_id)'); await db.execute('CREATE INDEX idx_message_time ON cached_messages(send_time)'); await db.execute('CREATE INDEX idx_stroke_sync ON pending_strokes(sync_status)'); print('[LocalRepo] 数据库表创建完成'); } /// 版本升级迁移 Future _onUpgrade(dynamic db, int oldVersion, int newVersion) async { if (oldVersion < 2) { // v2: 添加学情报告缓存表 await db.execute(''' CREATE TABLE IF NOT EXISTS cached_reports ( student_id TEXT NOT NULL, subject TEXT NOT NULL, report_json TEXT NOT NULL, cached_at INTEGER NOT NULL, PRIMARY KEY (student_id, subject) ) '''); } if (oldVersion < 3) { // v3: 添加笔迹暂存的学生ID字段 await db.execute('ALTER TABLE pending_strokes ADD COLUMN student_id TEXT DEFAULT ""'); } print('[LocalRepo] 数据库升级: v$oldVersion -> v$newVersion'); } /* ========== 作业缓存操作 ========== */ /// 批量缓存作业列表(从云端拉取后存储到本地) Future cacheAssignments(List assignments) async { // 使用事务批量插入,提高性能 // await _db.transaction((txn) async { ... }); for (final a in assignments) { // INSERT OR REPLACE print('[LocalRepo] 缓存作业: ${a.title}'); } } /// 查询本地缓存的作业列表 Future> getAssignmentsByClass(String classId, {int limit = 50}) async { // SELECT * FROM cached_assignments WHERE class_id = ? ORDER BY publish_time DESC LIMIT ? return []; } /// 获取作业详情(优先从缓存读取) Future getAssignmentDetail(String assignmentId) async { // SELECT * FROM cached_assignments WHERE id = ? return null; } /// 清理过期的作业缓存(30天前的数据) Future cleanExpiredAssignments() async { final threshold = DateTime.now().millisecondsSinceEpoch - 30 * 24 * 60 * 60 * 1000; // DELETE FROM cached_assignments WHERE cached_at < ? print('[LocalRepo] 清理过期作业缓存'); return 0; } /* ========== 消息记录操作 ========== */ /// 保存消息到本地 Future saveMessage(CachedMessage message) async { // INSERT OR REPLACE INTO cached_messages VALUES (...) print('[LocalRepo] 保存消息: ${message.id}'); } /// 查询消息列表(分页) Future> getMessages({int page = 0, int pageSize = 20}) async { // SELECT * FROM cached_messages ORDER BY send_time DESC LIMIT ? OFFSET ? return []; } /// 标记消息已读 Future markMessageRead(String messageId) async { // UPDATE cached_messages SET is_read = 1 WHERE id = ? } /// 获取未读消息数量 Future getUnreadCount() async { // SELECT COUNT(*) FROM cached_messages WHERE is_read = 0 return 0; } /* ========== 离线操作队列 ========== */ /// 添加离线操作到队列(断网时调用) Future enqueueOfflineAction(OfflineAction action) async { // INSERT INTO offline_actions VALUES (...) print('[LocalRepo] 离线操作入队: ${action.actionType}'); } /// 获取所有待执行的离线操作 Future> getPendingOfflineActions() async { // SELECT * FROM offline_actions ORDER BY created_at ASC return []; } /// 删除已完成的离线操作 Future removeOfflineAction(String actionId) async { // DELETE FROM offline_actions WHERE id = ? } /// 增加操作重试次数 Future incrementRetryCount(String actionId) async { // UPDATE offline_actions SET retry_count = retry_count + 1 WHERE id = ? } /* ========== 笔迹暂存操作 ========== */ /// 暂存笔迹数据(BLE收笔后等待上传) Future savePendingStroke(PendingStrokeData stroke) async { // INSERT INTO pending_strokes VALUES (...) print('[LocalRepo] 暂存笔迹数据: ${stroke.id}'); } /// 获取待上传的笔迹数据 Future> getUnsyncedStrokes({int limit = 50}) async { // SELECT * FROM pending_strokes WHERE sync_status = 0 LIMIT ? return []; } /// 更新笔迹同步状态 Future updateStrokeSyncStatus(String strokeId, int status) async { // UPDATE pending_strokes SET sync_status = ? WHERE id = ? } /// 批量删除已上传的笔迹 Future cleanSyncedStrokes() async { // DELETE FROM pending_strokes WHERE sync_status = 1 return 0; } /* ========== 学情报告缓存 ========== */ /// 缓存学情报告 Future cacheReport(String studentId, String subject, Map report) async { final reportJson = jsonEncode(report); // INSERT OR REPLACE INTO cached_reports VALUES (studentId, subject, reportJson, now) print('[LocalRepo] 缓存学情报告: $studentId/$subject'); } /// 获取缓存的学情报告 Future?> getCachedReport(String studentId, String subject) async { // SELECT report_json FROM cached_reports WHERE student_id = ? AND subject = ? return null; } /* ========== 数据库维护 ========== */ /// 获取数据库统计信息 Future> getStatistics() async { return { 'assignments': 0, // 缓存作业数 'messages': 0, // 消息数 'offlineActions': 0, // 待同步操作数 'pendingStrokes': 0, // 待上传笔迹数 }; } /// 清空所有本地数据(用户登出时调用) Future clearAll() async { // DELETE FROM cached_assignments // DELETE FROM cached_messages // DELETE FROM offline_actions // DELETE FROM pending_strokes // DELETE FROM cached_reports print('[LocalRepo] 已清空所有本地数据'); } /// 关闭数据库连接 Future close() async { // await _db?.close(); print('[LocalRepo] 数据库连接已关闭'); } }