754 lines
21 KiB
Dart
754 lines
21 KiB
Dart
// 自然写互动课堂平板端应用软件 V1.0
|
|
// repository/local_repository.dart - SQLite + Hive本地数据存储
|
|
|
|
import 'dart:async';
|
|
import 'dart:convert';
|
|
|
|
/// 数据库表名常量
|
|
class PadDbTables {
|
|
static const String homework = 'pad_homework';
|
|
static const String homeworkQuestion = 'pad_homework_question';
|
|
static const String answerProgress = 'pad_answer_progress';
|
|
static const String errorBook = 'pad_error_book';
|
|
static const String studyPlan = 'pad_study_plan';
|
|
static const String studyTask = 'pad_study_task';
|
|
static const String practiceRecord = 'pad_practice_record';
|
|
static const String strokeCache = 'pad_stroke_cache';
|
|
static const String offlineAction = 'pad_offline_action';
|
|
static const String usageRecord = 'pad_usage_record';
|
|
}
|
|
|
|
/// 数据库版本
|
|
const int padDbVersion = 4;
|
|
|
|
/// 作业缓存模型
|
|
class CachedHomework {
|
|
final String id;
|
|
final String title;
|
|
final String subject;
|
|
final String teacherName;
|
|
final String status;
|
|
final String? deadline;
|
|
final String? content;
|
|
final int totalQuestions;
|
|
final int answeredQuestions;
|
|
final DateTime cachedAt;
|
|
|
|
CachedHomework({
|
|
required this.id,
|
|
required this.title,
|
|
required this.subject,
|
|
required this.teacherName,
|
|
required this.status,
|
|
this.deadline,
|
|
this.content,
|
|
this.totalQuestions = 0,
|
|
this.answeredQuestions = 0,
|
|
required this.cachedAt,
|
|
});
|
|
|
|
Map<String, dynamic> toMap() => {
|
|
'id': id,
|
|
'title': title,
|
|
'subject': subject,
|
|
'teacher_name': teacherName,
|
|
'status': status,
|
|
'deadline': deadline,
|
|
'content': content,
|
|
'total_questions': totalQuestions,
|
|
'answered_questions': answeredQuestions,
|
|
'cached_at': cachedAt.toIso8601String(),
|
|
};
|
|
|
|
factory CachedHomework.fromMap(Map<String, dynamic> map) {
|
|
return CachedHomework(
|
|
id: map['id'],
|
|
title: map['title'] ?? '',
|
|
subject: map['subject'] ?? '',
|
|
teacherName: map['teacher_name'] ?? '',
|
|
status: map['status'] ?? 'pending',
|
|
deadline: map['deadline'],
|
|
content: map['content'],
|
|
totalQuestions: map['total_questions'] ?? 0,
|
|
answeredQuestions: map['answered_questions'] ?? 0,
|
|
cachedAt: DateTime.parse(map['cached_at']),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// 错题记录模型
|
|
class ErrorBookEntry {
|
|
final String id;
|
|
final String homeworkId;
|
|
final String questionId;
|
|
final String subject;
|
|
final String? knowledgePoint;
|
|
final String questionContent;
|
|
final String? questionImageUrl;
|
|
final String? studentAnswer;
|
|
final String? correctAnswer;
|
|
final String? errorReason;
|
|
final int reviewCount;
|
|
final DateTime createdAt;
|
|
final DateTime? lastReviewAt;
|
|
|
|
ErrorBookEntry({
|
|
required this.id,
|
|
required this.homeworkId,
|
|
required this.questionId,
|
|
required this.subject,
|
|
this.knowledgePoint,
|
|
required this.questionContent,
|
|
this.questionImageUrl,
|
|
this.studentAnswer,
|
|
this.correctAnswer,
|
|
this.errorReason,
|
|
this.reviewCount = 0,
|
|
required this.createdAt,
|
|
this.lastReviewAt,
|
|
});
|
|
|
|
Map<String, dynamic> toMap() => {
|
|
'id': id,
|
|
'homework_id': homeworkId,
|
|
'question_id': questionId,
|
|
'subject': subject,
|
|
'knowledge_point': knowledgePoint,
|
|
'question_content': questionContent,
|
|
'question_image_url': questionImageUrl,
|
|
'student_answer': studentAnswer,
|
|
'correct_answer': correctAnswer,
|
|
'error_reason': errorReason,
|
|
'review_count': reviewCount,
|
|
'created_at': createdAt.toIso8601String(),
|
|
'last_review_at': lastReviewAt?.toIso8601String(),
|
|
};
|
|
|
|
factory ErrorBookEntry.fromMap(Map<String, dynamic> map) {
|
|
return ErrorBookEntry(
|
|
id: map['id'],
|
|
homeworkId: map['homework_id'] ?? '',
|
|
questionId: map['question_id'] ?? '',
|
|
subject: map['subject'] ?? '',
|
|
knowledgePoint: map['knowledge_point'],
|
|
questionContent: map['question_content'] ?? '',
|
|
questionImageUrl: map['question_image_url'],
|
|
studentAnswer: map['student_answer'],
|
|
correctAnswer: map['correct_answer'],
|
|
errorReason: map['error_reason'],
|
|
reviewCount: map['review_count'] ?? 0,
|
|
createdAt: DateTime.parse(map['created_at']),
|
|
lastReviewAt: map['last_review_at'] != null
|
|
? DateTime.parse(map['last_review_at'])
|
|
: null,
|
|
);
|
|
}
|
|
}
|
|
|
|
/// 学习计划模型
|
|
class StudyPlanEntry {
|
|
final String id;
|
|
final String title;
|
|
final String type;
|
|
final String? subject;
|
|
final DateTime startDate;
|
|
final DateTime endDate;
|
|
final double progress;
|
|
final int totalTasks;
|
|
final int completedTasks;
|
|
final bool isActive;
|
|
|
|
StudyPlanEntry({
|
|
required this.id,
|
|
required this.title,
|
|
required this.type,
|
|
this.subject,
|
|
required this.startDate,
|
|
required this.endDate,
|
|
this.progress = 0.0,
|
|
this.totalTasks = 0,
|
|
this.completedTasks = 0,
|
|
this.isActive = true,
|
|
});
|
|
|
|
Map<String, dynamic> toMap() => {
|
|
'id': id,
|
|
'title': title,
|
|
'type': type,
|
|
'subject': subject,
|
|
'start_date': startDate.toIso8601String(),
|
|
'end_date': endDate.toIso8601String(),
|
|
'progress': progress,
|
|
'total_tasks': totalTasks,
|
|
'completed_tasks': completedTasks,
|
|
'is_active': isActive ? 1 : 0,
|
|
};
|
|
}
|
|
|
|
/// 练字练习记录模型
|
|
class PracticeRecord {
|
|
final String id;
|
|
final String templateId;
|
|
final String character;
|
|
final int strokeScore;
|
|
final int structureScore;
|
|
final int overallScore;
|
|
final String? strokeDataJson;
|
|
final DateTime practiceAt;
|
|
|
|
PracticeRecord({
|
|
required this.id,
|
|
required this.templateId,
|
|
required this.character,
|
|
this.strokeScore = 0,
|
|
this.structureScore = 0,
|
|
this.overallScore = 0,
|
|
this.strokeDataJson,
|
|
required this.practiceAt,
|
|
});
|
|
|
|
Map<String, dynamic> toMap() => {
|
|
'id': id,
|
|
'template_id': templateId,
|
|
'character': character,
|
|
'stroke_score': strokeScore,
|
|
'structure_score': structureScore,
|
|
'overall_score': overallScore,
|
|
'stroke_data_json': strokeDataJson,
|
|
'practice_at': practiceAt.toIso8601String(),
|
|
};
|
|
}
|
|
|
|
/// 平板端本地数据存储仓库
|
|
/// 使用SQLite持久化存储 + Hive内存级KV缓存
|
|
/// 支持:作业缓存、错题本、学习计划、练字记录、离线操作队列、使用时长记录
|
|
class PadLocalRepository {
|
|
/// 数据库实例(实际使用sqflite库)
|
|
// late final Database _db;
|
|
|
|
/// 单例
|
|
static PadLocalRepository? _instance;
|
|
static PadLocalRepository get instance {
|
|
_instance ??= PadLocalRepository._internal();
|
|
return _instance!;
|
|
}
|
|
|
|
PadLocalRepository._internal();
|
|
|
|
/// 初始化数据库,创建表结构并执行版本迁移
|
|
Future<void> initialize() async {
|
|
// 实际调用: openDatabase(path, version: padDbVersion, ...)
|
|
// 以下为建表SQL
|
|
|
|
// V1: 基础表
|
|
await _createTablesV1();
|
|
|
|
// V2: 增加学习计划表
|
|
await _createTablesV2();
|
|
|
|
// V3: 增加使用时长记录表
|
|
await _createTablesV3();
|
|
|
|
// V4: 增加练字记录表和索引优化
|
|
await _createTablesV4();
|
|
}
|
|
|
|
/// V1建表:作业缓存、作答进度、错题本、离线操作队列
|
|
Future<void> _createTablesV1() async {
|
|
// 作业缓存表
|
|
const createHomework = '''
|
|
CREATE TABLE IF NOT EXISTS ${PadDbTables.homework} (
|
|
id TEXT PRIMARY KEY,
|
|
title TEXT NOT NULL,
|
|
subject TEXT NOT NULL,
|
|
teacher_name TEXT,
|
|
status TEXT DEFAULT 'pending',
|
|
deadline TEXT,
|
|
content TEXT,
|
|
total_questions INTEGER DEFAULT 0,
|
|
answered_questions INTEGER DEFAULT 0,
|
|
cached_at TEXT NOT NULL
|
|
)
|
|
''';
|
|
|
|
// 作业题目缓存表
|
|
const createQuestion = '''
|
|
CREATE TABLE IF NOT EXISTS ${PadDbTables.homeworkQuestion} (
|
|
id TEXT PRIMARY KEY,
|
|
homework_id TEXT NOT NULL,
|
|
question_index INTEGER,
|
|
type TEXT DEFAULT 'write',
|
|
content TEXT,
|
|
image_url TEXT,
|
|
options TEXT,
|
|
correct_answer TEXT,
|
|
FOREIGN KEY (homework_id) REFERENCES ${PadDbTables.homework}(id)
|
|
)
|
|
''';
|
|
|
|
// 作答进度暂存表
|
|
const createProgress = '''
|
|
CREATE TABLE IF NOT EXISTS ${PadDbTables.answerProgress} (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
homework_id TEXT NOT NULL,
|
|
question_id TEXT NOT NULL,
|
|
text_answer TEXT,
|
|
stroke_data TEXT,
|
|
saved_at TEXT NOT NULL,
|
|
UNIQUE(homework_id, question_id)
|
|
)
|
|
''';
|
|
|
|
// 错题本表
|
|
const createErrorBook = '''
|
|
CREATE TABLE IF NOT EXISTS ${PadDbTables.errorBook} (
|
|
id TEXT PRIMARY KEY,
|
|
homework_id TEXT,
|
|
question_id TEXT,
|
|
subject TEXT NOT NULL,
|
|
knowledge_point TEXT,
|
|
question_content TEXT NOT NULL,
|
|
question_image_url TEXT,
|
|
student_answer TEXT,
|
|
correct_answer TEXT,
|
|
error_reason TEXT,
|
|
review_count INTEGER DEFAULT 0,
|
|
created_at TEXT NOT NULL,
|
|
last_review_at TEXT
|
|
)
|
|
''';
|
|
|
|
// 离线操作队列表
|
|
const createOffline = '''
|
|
CREATE TABLE IF NOT EXISTS ${PadDbTables.offlineAction} (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
action_type TEXT NOT NULL,
|
|
payload TEXT NOT NULL,
|
|
retry_count INTEGER DEFAULT 0,
|
|
created_at TEXT NOT NULL,
|
|
status TEXT DEFAULT 'pending'
|
|
)
|
|
''';
|
|
|
|
// 笔迹暂存表
|
|
const createStroke = '''
|
|
CREATE TABLE IF NOT EXISTS ${PadDbTables.strokeCache} (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
homework_id TEXT,
|
|
question_id TEXT,
|
|
page_id TEXT,
|
|
stroke_json TEXT NOT NULL,
|
|
pen_mac TEXT,
|
|
created_at TEXT NOT NULL
|
|
)
|
|
''';
|
|
|
|
// 实际执行建表SQL
|
|
// await _db.execute(createHomework);
|
|
// await _db.execute(createQuestion);
|
|
// await _db.execute(createProgress);
|
|
// await _db.execute(createErrorBook);
|
|
// await _db.execute(createOffline);
|
|
// await _db.execute(createStroke);
|
|
}
|
|
|
|
/// V2建表:学习计划与任务
|
|
Future<void> _createTablesV2() async {
|
|
const createPlan = '''
|
|
CREATE TABLE IF NOT EXISTS ${PadDbTables.studyPlan} (
|
|
id TEXT PRIMARY KEY,
|
|
title TEXT NOT NULL,
|
|
type TEXT NOT NULL,
|
|
subject TEXT,
|
|
start_date TEXT NOT NULL,
|
|
end_date TEXT NOT NULL,
|
|
progress REAL DEFAULT 0.0,
|
|
total_tasks INTEGER DEFAULT 0,
|
|
completed_tasks INTEGER DEFAULT 0,
|
|
is_active INTEGER DEFAULT 1
|
|
)
|
|
''';
|
|
|
|
const createTask = '''
|
|
CREATE TABLE IF NOT EXISTS ${PadDbTables.studyTask} (
|
|
id TEXT PRIMARY KEY,
|
|
plan_id TEXT NOT NULL,
|
|
title TEXT NOT NULL,
|
|
description TEXT,
|
|
target_date TEXT,
|
|
is_completed INTEGER DEFAULT 0,
|
|
completed_at TEXT,
|
|
FOREIGN KEY (plan_id) REFERENCES ${PadDbTables.studyPlan}(id)
|
|
)
|
|
''';
|
|
|
|
// await _db.execute(createPlan);
|
|
// await _db.execute(createTask);
|
|
}
|
|
|
|
/// V3建表:使用时长记录
|
|
Future<void> _createTablesV3() async {
|
|
const createUsage = '''
|
|
CREATE TABLE IF NOT EXISTS ${PadDbTables.usageRecord} (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
date TEXT NOT NULL,
|
|
app_name TEXT DEFAULT 'writech',
|
|
subject TEXT,
|
|
duration_seconds INTEGER DEFAULT 0,
|
|
start_time TEXT NOT NULL,
|
|
end_time TEXT
|
|
)
|
|
''';
|
|
|
|
// await _db.execute(createUsage);
|
|
}
|
|
|
|
/// V4建表:练字记录 + 索引
|
|
Future<void> _createTablesV4() async {
|
|
const createPractice = '''
|
|
CREATE TABLE IF NOT EXISTS ${PadDbTables.practiceRecord} (
|
|
id TEXT PRIMARY KEY,
|
|
template_id TEXT NOT NULL,
|
|
character TEXT NOT NULL,
|
|
stroke_score INTEGER DEFAULT 0,
|
|
structure_score INTEGER DEFAULT 0,
|
|
overall_score INTEGER DEFAULT 0,
|
|
stroke_data_json TEXT,
|
|
practice_at TEXT NOT NULL
|
|
)
|
|
''';
|
|
|
|
// 索引优化
|
|
const indexHomeworkStatus = '''
|
|
CREATE INDEX IF NOT EXISTS idx_homework_status
|
|
ON ${PadDbTables.homework}(status)
|
|
''';
|
|
const indexErrorSubject = '''
|
|
CREATE INDEX IF NOT EXISTS idx_error_subject
|
|
ON ${PadDbTables.errorBook}(subject)
|
|
''';
|
|
const indexPracticeChar = '''
|
|
CREATE INDEX IF NOT EXISTS idx_practice_char
|
|
ON ${PadDbTables.practiceRecord}(character)
|
|
''';
|
|
|
|
// await _db.execute(createPractice);
|
|
// await _db.execute(indexHomeworkStatus);
|
|
// await _db.execute(indexErrorSubject);
|
|
// await _db.execute(indexPracticeChar);
|
|
}
|
|
|
|
// ============================================================
|
|
// 作业缓存 CRUD
|
|
// ============================================================
|
|
|
|
/// 缓存作业到本地(用于离线作答)
|
|
Future<void> cacheHomework(CachedHomework homework) async {
|
|
// await _db.insert(
|
|
// PadDbTables.homework,
|
|
// homework.toMap(),
|
|
// conflictAlgorithm: ConflictAlgorithm.replace,
|
|
// );
|
|
}
|
|
|
|
/// 获取本地缓存的作业列表
|
|
Future<List<CachedHomework>> getCachedHomeworks({
|
|
String? status,
|
|
int limit = 50,
|
|
}) async {
|
|
// String where = '';
|
|
// List<dynamic> whereArgs = [];
|
|
// if (status != null) {
|
|
// where = 'status = ?';
|
|
// whereArgs = [status];
|
|
// }
|
|
// final maps = await _db.query(
|
|
// PadDbTables.homework,
|
|
// where: where.isNotEmpty ? where : null,
|
|
// whereArgs: whereArgs.isNotEmpty ? whereArgs : null,
|
|
// orderBy: 'cached_at DESC',
|
|
// limit: limit,
|
|
// );
|
|
// return maps.map((m) => CachedHomework.fromMap(m)).toList();
|
|
return [];
|
|
}
|
|
|
|
/// 保存作答进度到本地
|
|
Future<void> saveAnswerProgress({
|
|
required String homeworkId,
|
|
required String questionId,
|
|
String? textAnswer,
|
|
String? strokeDataJson,
|
|
}) async {
|
|
// await _db.insert(
|
|
// PadDbTables.answerProgress,
|
|
// {
|
|
// 'homework_id': homeworkId,
|
|
// 'question_id': questionId,
|
|
// 'text_answer': textAnswer,
|
|
// 'stroke_data': strokeDataJson,
|
|
// 'saved_at': DateTime.now().toIso8601String(),
|
|
// },
|
|
// conflictAlgorithm: ConflictAlgorithm.replace,
|
|
// );
|
|
}
|
|
|
|
/// 获取某作业的所有作答进度
|
|
Future<Map<String, Map<String, dynamic>>> getAnswerProgress(
|
|
String homeworkId,
|
|
) async {
|
|
// final maps = await _db.query(
|
|
// PadDbTables.answerProgress,
|
|
// where: 'homework_id = ?',
|
|
// whereArgs: [homeworkId],
|
|
// );
|
|
// final result = <String, Map<String, dynamic>>{};
|
|
// for (final m in maps) {
|
|
// result[m['question_id'] as String] = m;
|
|
// }
|
|
// return result;
|
|
return {};
|
|
}
|
|
|
|
// ============================================================
|
|
// 错题本 CRUD
|
|
// ============================================================
|
|
|
|
/// 添加错题记录
|
|
Future<void> addErrorEntry(ErrorBookEntry entry) async {
|
|
// await _db.insert(
|
|
// PadDbTables.errorBook,
|
|
// entry.toMap(),
|
|
// conflictAlgorithm: ConflictAlgorithm.replace,
|
|
// );
|
|
}
|
|
|
|
/// 获取错题列表(支持按科目/知识点筛选)
|
|
Future<List<ErrorBookEntry>> getErrorEntries({
|
|
String? subject,
|
|
String? knowledgePoint,
|
|
int limit = 50,
|
|
int offset = 0,
|
|
}) async {
|
|
// final conditions = <String>[];
|
|
// final args = <dynamic>[];
|
|
// if (subject != null) {
|
|
// conditions.add('subject = ?');
|
|
// args.add(subject);
|
|
// }
|
|
// if (knowledgePoint != null) {
|
|
// conditions.add('knowledge_point = ?');
|
|
// args.add(knowledgePoint);
|
|
// }
|
|
// final maps = await _db.query(
|
|
// PadDbTables.errorBook,
|
|
// where: conditions.isNotEmpty ? conditions.join(' AND ') : null,
|
|
// whereArgs: args.isNotEmpty ? args : null,
|
|
// orderBy: 'created_at DESC',
|
|
// limit: limit,
|
|
// offset: offset,
|
|
// );
|
|
// return maps.map((m) => ErrorBookEntry.fromMap(m)).toList();
|
|
return [];
|
|
}
|
|
|
|
/// 更新错题复习次数
|
|
Future<void> updateErrorReviewCount(String entryId) async {
|
|
// await _db.rawUpdate('''
|
|
// UPDATE ${PadDbTables.errorBook}
|
|
// SET review_count = review_count + 1,
|
|
// last_review_at = ?
|
|
// WHERE id = ?
|
|
// ''', [DateTime.now().toIso8601String(), entryId]);
|
|
}
|
|
|
|
/// 获取错题统计(按科目分组计数)
|
|
Future<Map<String, int>> getErrorStatsBySubject() async {
|
|
// final maps = await _db.rawQuery('''
|
|
// SELECT subject, COUNT(*) as count
|
|
// FROM ${PadDbTables.errorBook}
|
|
// GROUP BY subject
|
|
// ''');
|
|
// return {for (var m in maps) m['subject'] as String: m['count'] as int};
|
|
return {};
|
|
}
|
|
|
|
// ============================================================
|
|
// 学习计划 CRUD
|
|
// ============================================================
|
|
|
|
/// 保存学习计划
|
|
Future<void> saveStudyPlan(StudyPlanEntry plan) async {
|
|
// await _db.insert(
|
|
// PadDbTables.studyPlan,
|
|
// plan.toMap(),
|
|
// conflictAlgorithm: ConflictAlgorithm.replace,
|
|
// );
|
|
}
|
|
|
|
/// 获取活跃的学习计划列表
|
|
Future<List<Map<String, dynamic>>> getActiveStudyPlans() async {
|
|
// return await _db.query(
|
|
// PadDbTables.studyPlan,
|
|
// where: 'is_active = 1',
|
|
// orderBy: 'start_date ASC',
|
|
// );
|
|
return [];
|
|
}
|
|
|
|
/// 更新学习计划进度
|
|
Future<void> updatePlanProgress(
|
|
String planId,
|
|
double progress,
|
|
int completedTasks,
|
|
) async {
|
|
// await _db.update(
|
|
// PadDbTables.studyPlan,
|
|
// {'progress': progress, 'completed_tasks': completedTasks},
|
|
// where: 'id = ?',
|
|
// whereArgs: [planId],
|
|
// );
|
|
}
|
|
|
|
// ============================================================
|
|
// 练字记录
|
|
// ============================================================
|
|
|
|
/// 保存练字记录
|
|
Future<void> savePracticeRecord(PracticeRecord record) async {
|
|
// await _db.insert(
|
|
// PadDbTables.practiceRecord,
|
|
// record.toMap(),
|
|
// conflictAlgorithm: ConflictAlgorithm.replace,
|
|
// );
|
|
}
|
|
|
|
/// 获取某字的练习历史(查看进步轨迹)
|
|
Future<List<Map<String, dynamic>>> getPracticeHistory(
|
|
String character, {
|
|
int limit = 20,
|
|
}) async {
|
|
// return await _db.query(
|
|
// PadDbTables.practiceRecord,
|
|
// where: 'character = ?',
|
|
// whereArgs: [character],
|
|
// orderBy: 'practice_at DESC',
|
|
// limit: limit,
|
|
// );
|
|
return [];
|
|
}
|
|
|
|
// ============================================================
|
|
// 离线操作队列
|
|
// ============================================================
|
|
|
|
/// 添加离线操作到队列
|
|
Future<void> enqueueOfflineAction(
|
|
String actionType,
|
|
Map<String, dynamic> payload,
|
|
) async {
|
|
// await _db.insert(PadDbTables.offlineAction, {
|
|
// 'action_type': actionType,
|
|
// 'payload': jsonEncode(payload),
|
|
// 'created_at': DateTime.now().toIso8601String(),
|
|
// 'status': 'pending',
|
|
// });
|
|
}
|
|
|
|
/// 获取待执行的离线操作
|
|
Future<List<Map<String, dynamic>>> getPendingOfflineActions() async {
|
|
// return await _db.query(
|
|
// PadDbTables.offlineAction,
|
|
// where: 'status = ? AND retry_count < 5',
|
|
// whereArgs: ['pending'],
|
|
// orderBy: 'created_at ASC',
|
|
// );
|
|
return [];
|
|
}
|
|
|
|
/// 标记离线操作完成
|
|
Future<void> markOfflineActionDone(int actionId) async {
|
|
// await _db.update(
|
|
// PadDbTables.offlineAction,
|
|
// {'status': 'done'},
|
|
// where: 'id = ?',
|
|
// whereArgs: [actionId],
|
|
// );
|
|
}
|
|
|
|
// ============================================================
|
|
// 使用时长记录
|
|
// ============================================================
|
|
|
|
/// 记录使用时长
|
|
Future<void> recordUsage({
|
|
required String date,
|
|
required int durationSeconds,
|
|
required String startTime,
|
|
String? endTime,
|
|
String? subject,
|
|
}) async {
|
|
// await _db.insert(PadDbTables.usageRecord, {
|
|
// 'date': date,
|
|
// 'duration_seconds': durationSeconds,
|
|
// 'start_time': startTime,
|
|
// 'end_time': endTime,
|
|
// 'subject': subject,
|
|
// });
|
|
}
|
|
|
|
/// 获取某日使用总时长(秒)
|
|
Future<int> getDailyUsage(String date) async {
|
|
// final result = await _db.rawQuery('''
|
|
// SELECT COALESCE(SUM(duration_seconds), 0) as total
|
|
// FROM ${PadDbTables.usageRecord}
|
|
// WHERE date = ?
|
|
// ''', [date]);
|
|
// return result.first['total'] as int? ?? 0;
|
|
return 0;
|
|
}
|
|
|
|
// ============================================================
|
|
// 数据库维护
|
|
// ============================================================
|
|
|
|
/// 清理过期缓存数据(30天前的作业缓存、90天前的笔迹暂存)
|
|
Future<void> cleanExpiredData() async {
|
|
final thirtyDaysAgo = DateTime.now()
|
|
.subtract(const Duration(days: 30))
|
|
.toIso8601String();
|
|
final ninetyDaysAgo = DateTime.now()
|
|
.subtract(const Duration(days: 90))
|
|
.toIso8601String();
|
|
|
|
// await _db.delete(
|
|
// PadDbTables.homework,
|
|
// where: 'cached_at < ? AND status IN (?, ?)',
|
|
// whereArgs: [thirtyDaysAgo, 'graded', 'expired'],
|
|
// );
|
|
// await _db.delete(
|
|
// PadDbTables.strokeCache,
|
|
// where: 'created_at < ?',
|
|
// whereArgs: [ninetyDaysAgo],
|
|
// );
|
|
// await _db.delete(
|
|
// PadDbTables.offlineAction,
|
|
// where: 'status = ? AND created_at < ?',
|
|
// whereArgs: ['done', thirtyDaysAgo],
|
|
// );
|
|
}
|
|
|
|
/// 获取本地数据库存储大小(字节)
|
|
Future<int> getDatabaseSize() async {
|
|
// final dbPath = await getDatabasesPath();
|
|
// final file = File('$dbPath/writech_pad.db');
|
|
// return file.existsSync() ? file.lengthSync() : 0;
|
|
return 0;
|
|
}
|
|
|
|
/// 关闭数据库
|
|
Future<void> close() async {
|
|
// await _db.close();
|
|
}
|
|
}
|