flutter架 构项目基础
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
/// ============================================================
|
||||
/// 【AppDatabase】本地业务数据库(存储分层第三层)—— 平台分发入口
|
||||
///
|
||||
/// 用法不变:main.dart 里 `await AppDatabase.instance.init();`
|
||||
///
|
||||
/// 一、为什么拆成条件导出(三个文件):
|
||||
/// isar 3.x 生成的 Schema 代码(*.g.dart)里含 64 位哈希常量
|
||||
/// (CollectionSchema.id,如 -6819722535046815095),远超 JS 可精确
|
||||
/// 表示的最大整数 2^53,dart2js 编译 Web 端时会直接报
|
||||
/// "The integer literal ... can't be represented exactly in JavaScript"。
|
||||
/// 即使 kIsWeb 在【运行时】跳过数据库,【编译期】依然失败——
|
||||
/// 因为 app_database.dart 静态 import 了模型 → 静态 import 了 .g.dart。
|
||||
///
|
||||
/// 二、条件导出的分发规则(编译期决定,运行期零开销):
|
||||
/// - native(Android/iOS/桌面,dart.library.io 为真):
|
||||
/// → app_database_io.dart,真 Isar 实现
|
||||
/// - web(dart2js / dart2wasm,dart.library.io 为假):
|
||||
/// → app_database_web.dart,空实现(web 仅调试 UI/网络)
|
||||
///
|
||||
/// 三、高频坑:
|
||||
/// 1. 三件套必须都定义 `class AppDatabase`,且成员签名保持一致,
|
||||
/// 否则共享代码(如 main.dart)在某个平台会编译不过;
|
||||
/// 2. web 端别用 Isar 集合类型(`IsarCollection<T>`),
|
||||
/// 那只存在于 io 实现里——web 端没有这些成员;
|
||||
/// 3. 改模型字段后仍要重跑:
|
||||
/// dart run build_runner build --delete-conflicting-outputs
|
||||
/// ============================================================
|
||||
library;
|
||||
|
||||
export 'app_database_web.dart'
|
||||
if (dart.library.io) 'app_database_io.dart';
|
||||
@@ -0,0 +1,117 @@
|
||||
import 'package:isar_community/isar.dart';
|
||||
import '../models/play_record.dart';
|
||||
import '../models/question.dart';
|
||||
import '../models/wrong_question.dart';
|
||||
import '../models/answer_record.dart';
|
||||
import 'package:new_learn/core/version/app_version_entity.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
/// ============================================================
|
||||
/// 【AppDatabase】Isar 本地业务数据库 —— native(Android/iOS/桌面)真实现
|
||||
///
|
||||
/// 仅当 dart.library.io 为真时编译(见 app_database.dart 条件导出)。
|
||||
/// web 端不会编译本文件,因此可以放心 import isar 与 *.g.dart。
|
||||
///
|
||||
/// 一、为什么需要第三层(对照前两层):
|
||||
/// 1. flutter_secure_storage → Token 等敏感凭据(最高安全)
|
||||
/// 2. shared_preferences → 用户信息/开关等小配置(明文快读)
|
||||
/// 3. AppDatabase(Isar) → 题库/错题/播放记录等大批量结构化数据
|
||||
/// 几十上百条 JSON 塞进 shared_preferences 会越写越卡,
|
||||
/// Isar 是本地 NoSQL 数据库:读写毫秒级、强类型、ACID 事务、支持索引。
|
||||
///
|
||||
/// 二、为什么用 isar_community 而不是 hive / isar:
|
||||
/// - hive:存 Map 无类型保障,官方已停更;
|
||||
/// - isar 3.1.8:只发在已宕机的 pub.isar-community.dev,装不上;
|
||||
/// - isar_community:社区在 pub.dev 的维护版,API 与 isar 完全一致。
|
||||
///
|
||||
/// 三、高频坑(Isar 专属):
|
||||
/// 1. init() 必须在 runApp 前 await(见 main.dart),
|
||||
/// 否则页面访问集合会抛 "Missing XxxSchema in Isar.open";
|
||||
/// 2. 模型改字段后必须重跑代码生成:
|
||||
/// dart run build_runner build --delete-conflicting-outputs
|
||||
/// (生成 *.g.dart,XxxSchema 就在里面);
|
||||
/// 3. 所有写操作包在 isar.writeTxn() 里;查询用 where()/filter();
|
||||
/// 4. 集合访问用 `isar.collection<T>()`(与生成的 isar.xxx 等价);
|
||||
/// 5. Web 端调试用空实现(app_database_web.dart),不要在这加 kIsWeb:
|
||||
/// 本文件只会在 native 编译,kIsWeb 恒为 false。
|
||||
/// ============================================================
|
||||
class AppDatabase {
|
||||
AppDatabase._();
|
||||
|
||||
static final AppDatabase instance = AppDatabase._();
|
||||
|
||||
late Isar isar;
|
||||
|
||||
/// 初始化:打开 Isar 实例并注册全部集合 Schema(main() 里 await)
|
||||
Future<void> init() async {
|
||||
final dir = await getApplicationDocumentsDirectory();
|
||||
isar = await Isar.open(
|
||||
[
|
||||
QuestionSchema,
|
||||
WrongQuestionSchema,
|
||||
PlayRecordSchema,
|
||||
RemoteVersionEntitySchema,
|
||||
AnswerRecordSchema,
|
||||
],
|
||||
directory: dir.path,
|
||||
name: 'app_db',
|
||||
inspector: true, // ✅加上这一行,debug模式启用Isar Inspector调试页面
|
||||
);
|
||||
debugPrintIsarInit();
|
||||
}
|
||||
|
||||
/// 集合访问守卫:未初始化时给出明确报错(而不是空指针)
|
||||
void _ensureReady() {
|
||||
if (!isar.isOpen) {
|
||||
throw StateError('本地数据库(Isar)未初始化:请在 main() 里 await init()');
|
||||
}
|
||||
}
|
||||
|
||||
/// 题库集合:新增/查询/删除题目都走它
|
||||
IsarCollection<Question> get questions {
|
||||
_ensureReady();
|
||||
return isar.collection<Question>();
|
||||
}
|
||||
|
||||
/// 错题本集合
|
||||
IsarCollection<WrongQuestion> get wrongQuestions {
|
||||
_ensureReady();
|
||||
return isar.collection<WrongQuestion>();
|
||||
}
|
||||
|
||||
/// 答题进度集合(续答功能:进卷读回 / 作答写入 / 交卷清理)
|
||||
IsarCollection<AnswerRecord> get answerRecords {
|
||||
_ensureReady();
|
||||
return isar.collection<AnswerRecord>();
|
||||
}
|
||||
|
||||
/// 播放记录集合
|
||||
IsarCollection<PlayRecord> get playRecords {
|
||||
_ensureReady();
|
||||
return isar.collection<PlayRecord>();
|
||||
}
|
||||
|
||||
/// 远端版本策略缓存集合(版本更新机制用)
|
||||
IsarCollection<RemoteVersionEntity> get remoteVersions {
|
||||
_ensureReady();
|
||||
return isar.collection<RemoteVersionEntity>();
|
||||
}
|
||||
|
||||
/// 清空全部业务数据(换账号/恢复出厂设置时调用)
|
||||
Future<void> clearAll() async {
|
||||
_ensureReady();
|
||||
await isar.writeTxn(() async {
|
||||
await questions.clear();
|
||||
await wrongQuestions.clear();
|
||||
await playRecords.clear();
|
||||
await remoteVersions.clear();
|
||||
await answerRecords.clear();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// 打印初始化完成日志(独立函数便于 web 空实现复用相同日志风格)
|
||||
void debugPrintIsarInit() {
|
||||
// ignore: avoid_print
|
||||
print('▶ 【Isar】业务数据库初始化完成(题库/错题/播放记录)');
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
/// ============================================================
|
||||
/// 【AppDatabase】本地业务数据库 —— web 空实现(Chrome/Edge 调试用)
|
||||
///
|
||||
/// 仅当 dart.library.io 为假时编译(见 app_database.dart 条件导出)。
|
||||
/// isar 3.x 全系不支持 web(官方版解析不了 Dart 3;社区版 web 实现是
|
||||
/// 占位 stub,调用即抛 "Please use Isar 2.5.0 if you need web support"),
|
||||
/// 且生成代码里的 64 位哈希常量 dart2js 编译不过。
|
||||
/// 所以 web 端用空实现:init() 直接跳过,UI/登录/网络调试完全不受影响。
|
||||
/// 生产走 Android/iOS,app_database_io.dart 真 Isar 照常。
|
||||
///
|
||||
/// ⚠ 注意:本文件不要 import isar 或任何 *.g.dart ——
|
||||
/// web 端没有 IsarCollection 等类型,集合读写成员也不存在(编译报错)。
|
||||
/// 这正是"web 只调试、不上数据库"的强制约束。
|
||||
/// ============================================================
|
||||
class AppDatabase {
|
||||
AppDatabase._();
|
||||
|
||||
static final AppDatabase instance = AppDatabase._();
|
||||
|
||||
/// web 调试模式:跳过本地数据库(UI/网络正常调试)
|
||||
Future<void> init() async {
|
||||
debugPrint('▶ 【Isar】Web 调试模式:跳过本地数据库(UI/网络正常调试)');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import 'package:isar_community/isar.dart';
|
||||
|
||||
part 'answer_record.g.dart';
|
||||
|
||||
/// ============================================================
|
||||
/// 【AnswerRecord】答题进度持久化模型(续答功能)
|
||||
///
|
||||
/// 一、解决什么问题:
|
||||
/// 答题中途退出 → 答案只存在 Riverpod 内存里,进程一杀就丢。
|
||||
/// 本模型按"试卷"维度把每题答案落 Isar,下次进卷按 paperId
|
||||
/// 读回注入 answerProvider,实现续答。
|
||||
///
|
||||
/// 二、设计要点:
|
||||
/// 1. paperId 加 @Index() 索引:进卷恢复、交卷清理都按它查
|
||||
/// (where(paperId).equalTo() 走索引,100 题毫秒级);
|
||||
/// 2. answer 统一 String:选择 "A,B" / 判断 "对" / 解答文本,
|
||||
/// 与 Question 模型的存储约定一致,交卷序列化也简单;
|
||||
/// 3. 交卷成功后按 paperId 批量删除,磁盘上永远只留
|
||||
/// "进行中"的试卷答案,不会膨胀。
|
||||
///
|
||||
/// 三、高频坑:
|
||||
/// 1. 和 Question 一样必须有 Id 主键 + 重跑 build_runner;
|
||||
/// 2. 模型 import 了 isar,web 端不能 import 本文件——
|
||||
/// store 层用条件导出(见 answer_record_store.dart)。
|
||||
/// ============================================================
|
||||
@collection
|
||||
class AnswerRecord {
|
||||
Id id = Isar.autoIncrement; // Isar 内部主键(自动递增)
|
||||
|
||||
@Index()
|
||||
int? paperId; // 试卷 id(续答恢复/交卷清理的维度)
|
||||
int? questionId; // 题目 id(对应 Question.questionId)
|
||||
String? answer; // 用户答案(多选 "A,B" 逗号拼接)
|
||||
DateTime? updatedAt; // 作答时间
|
||||
}
|
||||
@@ -0,0 +1,981 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'answer_record.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// IsarCollectionGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types
|
||||
|
||||
extension GetAnswerRecordCollection on Isar {
|
||||
IsarCollection<AnswerRecord> get answerRecords => this.collection();
|
||||
}
|
||||
|
||||
const AnswerRecordSchema = CollectionSchema(
|
||||
name: r'AnswerRecord',
|
||||
id: -783120389370632988,
|
||||
properties: {
|
||||
r'answer': PropertySchema(id: 0, name: r'answer', type: IsarType.string),
|
||||
r'paperId': PropertySchema(id: 1, name: r'paperId', type: IsarType.long),
|
||||
r'questionId': PropertySchema(
|
||||
id: 2,
|
||||
name: r'questionId',
|
||||
type: IsarType.long,
|
||||
),
|
||||
r'updatedAt': PropertySchema(
|
||||
id: 3,
|
||||
name: r'updatedAt',
|
||||
type: IsarType.dateTime,
|
||||
),
|
||||
},
|
||||
|
||||
estimateSize: _answerRecordEstimateSize,
|
||||
serialize: _answerRecordSerialize,
|
||||
deserialize: _answerRecordDeserialize,
|
||||
deserializeProp: _answerRecordDeserializeProp,
|
||||
idName: r'id',
|
||||
indexes: {
|
||||
r'paperId': IndexSchema(
|
||||
id: -7152058561277781812,
|
||||
name: r'paperId',
|
||||
unique: false,
|
||||
replace: false,
|
||||
properties: [
|
||||
IndexPropertySchema(
|
||||
name: r'paperId',
|
||||
type: IndexType.value,
|
||||
caseSensitive: false,
|
||||
),
|
||||
],
|
||||
),
|
||||
},
|
||||
links: {},
|
||||
embeddedSchemas: {},
|
||||
|
||||
getId: _answerRecordGetId,
|
||||
getLinks: _answerRecordGetLinks,
|
||||
attach: _answerRecordAttach,
|
||||
version: '3.3.2',
|
||||
);
|
||||
|
||||
int _answerRecordEstimateSize(
|
||||
AnswerRecord object,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
var bytesCount = offsets.last;
|
||||
{
|
||||
final value = object.answer;
|
||||
if (value != null) {
|
||||
bytesCount += 3 + value.length * 3;
|
||||
}
|
||||
}
|
||||
return bytesCount;
|
||||
}
|
||||
|
||||
void _answerRecordSerialize(
|
||||
AnswerRecord object,
|
||||
IsarWriter writer,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
writer.writeString(offsets[0], object.answer);
|
||||
writer.writeLong(offsets[1], object.paperId);
|
||||
writer.writeLong(offsets[2], object.questionId);
|
||||
writer.writeDateTime(offsets[3], object.updatedAt);
|
||||
}
|
||||
|
||||
AnswerRecord _answerRecordDeserialize(
|
||||
Id id,
|
||||
IsarReader reader,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
final object = AnswerRecord();
|
||||
object.answer = reader.readStringOrNull(offsets[0]);
|
||||
object.id = id;
|
||||
object.paperId = reader.readLongOrNull(offsets[1]);
|
||||
object.questionId = reader.readLongOrNull(offsets[2]);
|
||||
object.updatedAt = reader.readDateTimeOrNull(offsets[3]);
|
||||
return object;
|
||||
}
|
||||
|
||||
P _answerRecordDeserializeProp<P>(
|
||||
IsarReader reader,
|
||||
int propertyId,
|
||||
int offset,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
switch (propertyId) {
|
||||
case 0:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
case 1:
|
||||
return (reader.readLongOrNull(offset)) as P;
|
||||
case 2:
|
||||
return (reader.readLongOrNull(offset)) as P;
|
||||
case 3:
|
||||
return (reader.readDateTimeOrNull(offset)) as P;
|
||||
default:
|
||||
throw IsarError('Unknown property with id $propertyId');
|
||||
}
|
||||
}
|
||||
|
||||
Id _answerRecordGetId(AnswerRecord object) {
|
||||
return object.id;
|
||||
}
|
||||
|
||||
List<IsarLinkBase<dynamic>> _answerRecordGetLinks(AnswerRecord object) {
|
||||
return [];
|
||||
}
|
||||
|
||||
void _answerRecordAttach(
|
||||
IsarCollection<dynamic> col,
|
||||
Id id,
|
||||
AnswerRecord object,
|
||||
) {
|
||||
object.id = id;
|
||||
}
|
||||
|
||||
extension AnswerRecordQueryWhereSort
|
||||
on QueryBuilder<AnswerRecord, AnswerRecord, QWhere> {
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterWhere> anyId() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(const IdWhereClause.any());
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterWhere> anyPaperId() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
const IndexWhereClause.any(indexName: r'paperId'),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension AnswerRecordQueryWhere
|
||||
on QueryBuilder<AnswerRecord, AnswerRecord, QWhereClause> {
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterWhereClause> idEqualTo(Id id) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(IdWhereClause.between(lower: id, upper: id));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterWhereClause> idNotEqualTo(
|
||||
Id id,
|
||||
) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
if (query.whereSort == Sort.asc) {
|
||||
return query
|
||||
.addWhereClause(
|
||||
IdWhereClause.lessThan(upper: id, includeUpper: false),
|
||||
)
|
||||
.addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: id, includeLower: false),
|
||||
);
|
||||
} else {
|
||||
return query
|
||||
.addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: id, includeLower: false),
|
||||
)
|
||||
.addWhereClause(
|
||||
IdWhereClause.lessThan(upper: id, includeUpper: false),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterWhereClause> idGreaterThan(
|
||||
Id id, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: id, includeLower: include),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterWhereClause> idLessThan(
|
||||
Id id, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.lessThan(upper: id, includeUpper: include),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterWhereClause> idBetween(
|
||||
Id lowerId,
|
||||
Id upperId, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.between(
|
||||
lower: lowerId,
|
||||
includeLower: includeLower,
|
||||
upper: upperId,
|
||||
includeUpper: includeUpper,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterWhereClause> paperIdIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IndexWhereClause.equalTo(indexName: r'paperId', value: [null]),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterWhereClause>
|
||||
paperIdIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IndexWhereClause.between(
|
||||
indexName: r'paperId',
|
||||
lower: [null],
|
||||
includeLower: false,
|
||||
upper: [],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterWhereClause> paperIdEqualTo(
|
||||
int? paperId,
|
||||
) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IndexWhereClause.equalTo(indexName: r'paperId', value: [paperId]),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterWhereClause> paperIdNotEqualTo(
|
||||
int? paperId,
|
||||
) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
if (query.whereSort == Sort.asc) {
|
||||
return query
|
||||
.addWhereClause(
|
||||
IndexWhereClause.between(
|
||||
indexName: r'paperId',
|
||||
lower: [],
|
||||
upper: [paperId],
|
||||
includeUpper: false,
|
||||
),
|
||||
)
|
||||
.addWhereClause(
|
||||
IndexWhereClause.between(
|
||||
indexName: r'paperId',
|
||||
lower: [paperId],
|
||||
includeLower: false,
|
||||
upper: [],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return query
|
||||
.addWhereClause(
|
||||
IndexWhereClause.between(
|
||||
indexName: r'paperId',
|
||||
lower: [paperId],
|
||||
includeLower: false,
|
||||
upper: [],
|
||||
),
|
||||
)
|
||||
.addWhereClause(
|
||||
IndexWhereClause.between(
|
||||
indexName: r'paperId',
|
||||
lower: [],
|
||||
upper: [paperId],
|
||||
includeUpper: false,
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterWhereClause>
|
||||
paperIdGreaterThan(int? paperId, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IndexWhereClause.between(
|
||||
indexName: r'paperId',
|
||||
lower: [paperId],
|
||||
includeLower: include,
|
||||
upper: [],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterWhereClause> paperIdLessThan(
|
||||
int? paperId, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IndexWhereClause.between(
|
||||
indexName: r'paperId',
|
||||
lower: [],
|
||||
upper: [paperId],
|
||||
includeUpper: include,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterWhereClause> paperIdBetween(
|
||||
int? lowerPaperId,
|
||||
int? upperPaperId, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IndexWhereClause.between(
|
||||
indexName: r'paperId',
|
||||
lower: [lowerPaperId],
|
||||
includeLower: includeLower,
|
||||
upper: [upperPaperId],
|
||||
includeUpper: includeUpper,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension AnswerRecordQueryFilter
|
||||
on QueryBuilder<AnswerRecord, AnswerRecord, QFilterCondition> {
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition>
|
||||
answerIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNull(property: r'answer'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition>
|
||||
answerIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNotNull(property: r'answer'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition> answerEqualTo(
|
||||
String? value, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(
|
||||
property: r'answer',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition>
|
||||
answerGreaterThan(
|
||||
String? value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'answer',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition>
|
||||
answerLessThan(
|
||||
String? value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'answer',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition> answerBetween(
|
||||
String? lower,
|
||||
String? upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.between(
|
||||
property: r'answer',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition>
|
||||
answerStartsWith(String value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.startsWith(
|
||||
property: r'answer',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition>
|
||||
answerEndsWith(String value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.endsWith(
|
||||
property: r'answer',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition>
|
||||
answerContains(String value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.contains(
|
||||
property: r'answer',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition> answerMatches(
|
||||
String pattern, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.matches(
|
||||
property: r'answer',
|
||||
wildcard: pattern,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition>
|
||||
answerIsEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(property: r'answer', value: ''),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition>
|
||||
answerIsNotEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(property: r'answer', value: ''),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition> idEqualTo(
|
||||
Id value,
|
||||
) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(property: r'id', value: value),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition> idGreaterThan(
|
||||
Id value, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'id',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition> idLessThan(
|
||||
Id value, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'id',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition> idBetween(
|
||||
Id lower,
|
||||
Id upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.between(
|
||||
property: r'id',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition>
|
||||
paperIdIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNull(property: r'paperId'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition>
|
||||
paperIdIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNotNull(property: r'paperId'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition>
|
||||
paperIdEqualTo(int? value) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(property: r'paperId', value: value),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition>
|
||||
paperIdGreaterThan(int? value, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'paperId',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition>
|
||||
paperIdLessThan(int? value, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'paperId',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition>
|
||||
paperIdBetween(
|
||||
int? lower,
|
||||
int? upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.between(
|
||||
property: r'paperId',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition>
|
||||
questionIdIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNull(property: r'questionId'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition>
|
||||
questionIdIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNotNull(property: r'questionId'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition>
|
||||
questionIdEqualTo(int? value) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(property: r'questionId', value: value),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition>
|
||||
questionIdGreaterThan(int? value, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'questionId',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition>
|
||||
questionIdLessThan(int? value, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'questionId',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition>
|
||||
questionIdBetween(
|
||||
int? lower,
|
||||
int? upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.between(
|
||||
property: r'questionId',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition>
|
||||
updatedAtIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNull(property: r'updatedAt'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition>
|
||||
updatedAtIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNotNull(property: r'updatedAt'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition>
|
||||
updatedAtEqualTo(DateTime? value) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(property: r'updatedAt', value: value),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition>
|
||||
updatedAtGreaterThan(DateTime? value, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'updatedAt',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition>
|
||||
updatedAtLessThan(DateTime? value, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'updatedAt',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterFilterCondition>
|
||||
updatedAtBetween(
|
||||
DateTime? lower,
|
||||
DateTime? upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.between(
|
||||
property: r'updatedAt',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension AnswerRecordQueryObject
|
||||
on QueryBuilder<AnswerRecord, AnswerRecord, QFilterCondition> {}
|
||||
|
||||
extension AnswerRecordQueryLinks
|
||||
on QueryBuilder<AnswerRecord, AnswerRecord, QFilterCondition> {}
|
||||
|
||||
extension AnswerRecordQuerySortBy
|
||||
on QueryBuilder<AnswerRecord, AnswerRecord, QSortBy> {
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterSortBy> sortByAnswer() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'answer', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterSortBy> sortByAnswerDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'answer', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterSortBy> sortByPaperId() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'paperId', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterSortBy> sortByPaperIdDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'paperId', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterSortBy> sortByQuestionId() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'questionId', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterSortBy>
|
||||
sortByQuestionIdDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'questionId', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterSortBy> sortByUpdatedAt() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'updatedAt', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterSortBy> sortByUpdatedAtDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'updatedAt', Sort.desc);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension AnswerRecordQuerySortThenBy
|
||||
on QueryBuilder<AnswerRecord, AnswerRecord, QSortThenBy> {
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterSortBy> thenByAnswer() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'answer', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterSortBy> thenByAnswerDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'answer', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterSortBy> thenById() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'id', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterSortBy> thenByIdDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'id', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterSortBy> thenByPaperId() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'paperId', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterSortBy> thenByPaperIdDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'paperId', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterSortBy> thenByQuestionId() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'questionId', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterSortBy>
|
||||
thenByQuestionIdDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'questionId', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterSortBy> thenByUpdatedAt() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'updatedAt', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QAfterSortBy> thenByUpdatedAtDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'updatedAt', Sort.desc);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension AnswerRecordQueryWhereDistinct
|
||||
on QueryBuilder<AnswerRecord, AnswerRecord, QDistinct> {
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QDistinct> distinctByAnswer({
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'answer', caseSensitive: caseSensitive);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QDistinct> distinctByPaperId() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'paperId');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QDistinct> distinctByQuestionId() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'questionId');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, AnswerRecord, QDistinct> distinctByUpdatedAt() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'updatedAt');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension AnswerRecordQueryProperty
|
||||
on QueryBuilder<AnswerRecord, AnswerRecord, QQueryProperty> {
|
||||
QueryBuilder<AnswerRecord, int, QQueryOperations> idProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'id');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, String?, QQueryOperations> answerProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'answer');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, int?, QQueryOperations> paperIdProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'paperId');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, int?, QQueryOperations> questionIdProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'questionId');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<AnswerRecord, DateTime?, QQueryOperations> updatedAtProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'updatedAt');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import 'package:isar_community/isar.dart';
|
||||
|
||||
part 'play_record.g.dart';
|
||||
|
||||
/// ============================================================
|
||||
/// 【PlayRecord】播放记录模型(对应 AppDatabase 的播放记录集合)
|
||||
///
|
||||
/// 一、字段设计:课程 id + 进度(0~1)+ 最近播放时间,
|
||||
/// 下次进课程直接按 progress 续播。
|
||||
///
|
||||
/// 二、高频坑:
|
||||
/// 1. progress 用 double 存小数进度,展示成百分比自己换算;
|
||||
/// 2. updatedAt 每次播放暂停都更新,用于"最近播放"排序;
|
||||
/// 3. 改字段后记得重跑 build_runner。
|
||||
/// ============================================================
|
||||
@collection
|
||||
class PlayRecord {
|
||||
Id id = Isar.autoIncrement; // Isar 内部主键(自动递增)
|
||||
|
||||
int? courseId; // 课程/课时 id
|
||||
String? courseName; // 课程名
|
||||
double? progress; // 播放进度 0~1
|
||||
DateTime? updatedAt; // 最近播放时间
|
||||
}
|
||||
@@ -0,0 +1,852 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'play_record.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// IsarCollectionGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types
|
||||
|
||||
extension GetPlayRecordCollection on Isar {
|
||||
IsarCollection<PlayRecord> get playRecords => this.collection();
|
||||
}
|
||||
|
||||
const PlayRecordSchema = CollectionSchema(
|
||||
name: r'PlayRecord',
|
||||
id: -7338646677296495696,
|
||||
properties: {
|
||||
r'courseId': PropertySchema(id: 0, name: r'courseId', type: IsarType.long),
|
||||
r'courseName': PropertySchema(
|
||||
id: 1,
|
||||
name: r'courseName',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'progress': PropertySchema(
|
||||
id: 2,
|
||||
name: r'progress',
|
||||
type: IsarType.double,
|
||||
),
|
||||
r'updatedAt': PropertySchema(
|
||||
id: 3,
|
||||
name: r'updatedAt',
|
||||
type: IsarType.dateTime,
|
||||
),
|
||||
},
|
||||
|
||||
estimateSize: _playRecordEstimateSize,
|
||||
serialize: _playRecordSerialize,
|
||||
deserialize: _playRecordDeserialize,
|
||||
deserializeProp: _playRecordDeserializeProp,
|
||||
idName: r'id',
|
||||
indexes: {},
|
||||
links: {},
|
||||
embeddedSchemas: {},
|
||||
|
||||
getId: _playRecordGetId,
|
||||
getLinks: _playRecordGetLinks,
|
||||
attach: _playRecordAttach,
|
||||
version: '3.3.2',
|
||||
);
|
||||
|
||||
int _playRecordEstimateSize(
|
||||
PlayRecord object,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
var bytesCount = offsets.last;
|
||||
{
|
||||
final value = object.courseName;
|
||||
if (value != null) {
|
||||
bytesCount += 3 + value.length * 3;
|
||||
}
|
||||
}
|
||||
return bytesCount;
|
||||
}
|
||||
|
||||
void _playRecordSerialize(
|
||||
PlayRecord object,
|
||||
IsarWriter writer,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
writer.writeLong(offsets[0], object.courseId);
|
||||
writer.writeString(offsets[1], object.courseName);
|
||||
writer.writeDouble(offsets[2], object.progress);
|
||||
writer.writeDateTime(offsets[3], object.updatedAt);
|
||||
}
|
||||
|
||||
PlayRecord _playRecordDeserialize(
|
||||
Id id,
|
||||
IsarReader reader,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
final object = PlayRecord();
|
||||
object.courseId = reader.readLongOrNull(offsets[0]);
|
||||
object.courseName = reader.readStringOrNull(offsets[1]);
|
||||
object.id = id;
|
||||
object.progress = reader.readDoubleOrNull(offsets[2]);
|
||||
object.updatedAt = reader.readDateTimeOrNull(offsets[3]);
|
||||
return object;
|
||||
}
|
||||
|
||||
P _playRecordDeserializeProp<P>(
|
||||
IsarReader reader,
|
||||
int propertyId,
|
||||
int offset,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
switch (propertyId) {
|
||||
case 0:
|
||||
return (reader.readLongOrNull(offset)) as P;
|
||||
case 1:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
case 2:
|
||||
return (reader.readDoubleOrNull(offset)) as P;
|
||||
case 3:
|
||||
return (reader.readDateTimeOrNull(offset)) as P;
|
||||
default:
|
||||
throw IsarError('Unknown property with id $propertyId');
|
||||
}
|
||||
}
|
||||
|
||||
Id _playRecordGetId(PlayRecord object) {
|
||||
return object.id;
|
||||
}
|
||||
|
||||
List<IsarLinkBase<dynamic>> _playRecordGetLinks(PlayRecord object) {
|
||||
return [];
|
||||
}
|
||||
|
||||
void _playRecordAttach(IsarCollection<dynamic> col, Id id, PlayRecord object) {
|
||||
object.id = id;
|
||||
}
|
||||
|
||||
extension PlayRecordQueryWhereSort
|
||||
on QueryBuilder<PlayRecord, PlayRecord, QWhere> {
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterWhere> anyId() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(const IdWhereClause.any());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension PlayRecordQueryWhere
|
||||
on QueryBuilder<PlayRecord, PlayRecord, QWhereClause> {
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterWhereClause> idEqualTo(Id id) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(IdWhereClause.between(lower: id, upper: id));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterWhereClause> idNotEqualTo(Id id) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
if (query.whereSort == Sort.asc) {
|
||||
return query
|
||||
.addWhereClause(
|
||||
IdWhereClause.lessThan(upper: id, includeUpper: false),
|
||||
)
|
||||
.addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: id, includeLower: false),
|
||||
);
|
||||
} else {
|
||||
return query
|
||||
.addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: id, includeLower: false),
|
||||
)
|
||||
.addWhereClause(
|
||||
IdWhereClause.lessThan(upper: id, includeUpper: false),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterWhereClause> idGreaterThan(
|
||||
Id id, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: id, includeLower: include),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterWhereClause> idLessThan(
|
||||
Id id, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.lessThan(upper: id, includeUpper: include),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterWhereClause> idBetween(
|
||||
Id lowerId,
|
||||
Id upperId, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.between(
|
||||
lower: lowerId,
|
||||
includeLower: includeLower,
|
||||
upper: upperId,
|
||||
includeUpper: includeUpper,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension PlayRecordQueryFilter
|
||||
on QueryBuilder<PlayRecord, PlayRecord, QFilterCondition> {
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition> courseIdIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNull(property: r'courseId'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition>
|
||||
courseIdIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNotNull(property: r'courseId'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition> courseIdEqualTo(
|
||||
int? value,
|
||||
) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(property: r'courseId', value: value),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition>
|
||||
courseIdGreaterThan(int? value, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'courseId',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition> courseIdLessThan(
|
||||
int? value, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'courseId',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition> courseIdBetween(
|
||||
int? lower,
|
||||
int? upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.between(
|
||||
property: r'courseId',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition>
|
||||
courseNameIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNull(property: r'courseName'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition>
|
||||
courseNameIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNotNull(property: r'courseName'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition> courseNameEqualTo(
|
||||
String? value, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(
|
||||
property: r'courseName',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition>
|
||||
courseNameGreaterThan(
|
||||
String? value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'courseName',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition>
|
||||
courseNameLessThan(
|
||||
String? value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'courseName',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition> courseNameBetween(
|
||||
String? lower,
|
||||
String? upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.between(
|
||||
property: r'courseName',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition>
|
||||
courseNameStartsWith(String value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.startsWith(
|
||||
property: r'courseName',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition>
|
||||
courseNameEndsWith(String value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.endsWith(
|
||||
property: r'courseName',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition>
|
||||
courseNameContains(String value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.contains(
|
||||
property: r'courseName',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition> courseNameMatches(
|
||||
String pattern, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.matches(
|
||||
property: r'courseName',
|
||||
wildcard: pattern,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition>
|
||||
courseNameIsEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(property: r'courseName', value: ''),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition>
|
||||
courseNameIsNotEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(property: r'courseName', value: ''),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition> idEqualTo(
|
||||
Id value,
|
||||
) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(property: r'id', value: value),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition> idGreaterThan(
|
||||
Id value, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'id',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition> idLessThan(
|
||||
Id value, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'id',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition> idBetween(
|
||||
Id lower,
|
||||
Id upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.between(
|
||||
property: r'id',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition> progressIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNull(property: r'progress'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition>
|
||||
progressIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNotNull(property: r'progress'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition> progressEqualTo(
|
||||
double? value, {
|
||||
double epsilon = Query.epsilon,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(
|
||||
property: r'progress',
|
||||
value: value,
|
||||
|
||||
epsilon: epsilon,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition>
|
||||
progressGreaterThan(
|
||||
double? value, {
|
||||
bool include = false,
|
||||
double epsilon = Query.epsilon,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'progress',
|
||||
value: value,
|
||||
|
||||
epsilon: epsilon,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition> progressLessThan(
|
||||
double? value, {
|
||||
bool include = false,
|
||||
double epsilon = Query.epsilon,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'progress',
|
||||
value: value,
|
||||
|
||||
epsilon: epsilon,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition> progressBetween(
|
||||
double? lower,
|
||||
double? upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
double epsilon = Query.epsilon,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.between(
|
||||
property: r'progress',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
|
||||
epsilon: epsilon,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition>
|
||||
updatedAtIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNull(property: r'updatedAt'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition>
|
||||
updatedAtIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNotNull(property: r'updatedAt'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition> updatedAtEqualTo(
|
||||
DateTime? value,
|
||||
) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(property: r'updatedAt', value: value),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition>
|
||||
updatedAtGreaterThan(DateTime? value, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'updatedAt',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition> updatedAtLessThan(
|
||||
DateTime? value, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'updatedAt',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterFilterCondition> updatedAtBetween(
|
||||
DateTime? lower,
|
||||
DateTime? upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.between(
|
||||
property: r'updatedAt',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension PlayRecordQueryObject
|
||||
on QueryBuilder<PlayRecord, PlayRecord, QFilterCondition> {}
|
||||
|
||||
extension PlayRecordQueryLinks
|
||||
on QueryBuilder<PlayRecord, PlayRecord, QFilterCondition> {}
|
||||
|
||||
extension PlayRecordQuerySortBy
|
||||
on QueryBuilder<PlayRecord, PlayRecord, QSortBy> {
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterSortBy> sortByCourseId() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'courseId', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterSortBy> sortByCourseIdDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'courseId', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterSortBy> sortByCourseName() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'courseName', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterSortBy> sortByCourseNameDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'courseName', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterSortBy> sortByProgress() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'progress', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterSortBy> sortByProgressDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'progress', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterSortBy> sortByUpdatedAt() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'updatedAt', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterSortBy> sortByUpdatedAtDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'updatedAt', Sort.desc);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension PlayRecordQuerySortThenBy
|
||||
on QueryBuilder<PlayRecord, PlayRecord, QSortThenBy> {
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterSortBy> thenByCourseId() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'courseId', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterSortBy> thenByCourseIdDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'courseId', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterSortBy> thenByCourseName() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'courseName', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterSortBy> thenByCourseNameDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'courseName', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterSortBy> thenById() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'id', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterSortBy> thenByIdDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'id', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterSortBy> thenByProgress() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'progress', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterSortBy> thenByProgressDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'progress', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterSortBy> thenByUpdatedAt() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'updatedAt', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QAfterSortBy> thenByUpdatedAtDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'updatedAt', Sort.desc);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension PlayRecordQueryWhereDistinct
|
||||
on QueryBuilder<PlayRecord, PlayRecord, QDistinct> {
|
||||
QueryBuilder<PlayRecord, PlayRecord, QDistinct> distinctByCourseId() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'courseId');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QDistinct> distinctByCourseName({
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'courseName', caseSensitive: caseSensitive);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QDistinct> distinctByProgress() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'progress');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, PlayRecord, QDistinct> distinctByUpdatedAt() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'updatedAt');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension PlayRecordQueryProperty
|
||||
on QueryBuilder<PlayRecord, PlayRecord, QQueryProperty> {
|
||||
QueryBuilder<PlayRecord, int, QQueryOperations> idProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'id');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, int?, QQueryOperations> courseIdProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'courseId');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, String?, QQueryOperations> courseNameProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'courseName');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, double?, QQueryOperations> progressProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'progress');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<PlayRecord, DateTime?, QQueryOperations> updatedAtProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'updatedAt');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import 'package:isar_community/isar.dart';
|
||||
|
||||
part 'question.g.dart';
|
||||
|
||||
/// ============================================================
|
||||
/// 【Question】题库题目模型(Isar 业务集合,对应 AppDatabase 的题库)
|
||||
///
|
||||
/// 一、为什么用强类型类而不是 Hive 的 Map:
|
||||
/// Isar 是强类型 NoSQL,每张"表"(集合)必须对应一个 Dart 类,
|
||||
/// 类字段即数据库列。字段名/类型写错会在编译期直接报错,
|
||||
/// 比 Hive 存 Map(运行时才崩)安全得多。
|
||||
///
|
||||
/// 二、题型字段 type(与试卷接口模型 ExamQuestion 共用约定):
|
||||
/// 1 单选 / 2 多选 / 3 判断 / 4 解答;
|
||||
/// ⚠ 本 Isar 模型是"题库下载/收藏"等本地资产用途,
|
||||
/// 试卷做题页必须用纯 Dart 的 ExamQuestion(features/exam/),
|
||||
/// 业务代码 import 本模型会把 schema 哈希拉进 web 编译图导致
|
||||
/// dart2js 编译失败(详见 ExamQuestion 头注释)。
|
||||
/// options 统一存 JSON 字符串(选择题存选项数组,判断/解答可空),
|
||||
/// answer 统一存字符串(多选 "A,B" 逗号拼接),渲染层按题型解析。
|
||||
///
|
||||
/// 三、高频坑:
|
||||
/// 1. 每个集合必须有 Id 主键,Id id = Isar.autoIncrement 表示自增,
|
||||
/// 写入时 Isar 会自动分配 id 回填到对象上;
|
||||
/// 2. 字段尽量用可空类型(String?),未赋值存 null;
|
||||
/// 3. 改字段结构后必须重跑 build_runner,否则 Schema 还是旧的。
|
||||
/// ============================================================
|
||||
@collection
|
||||
class Question {
|
||||
Id id = Isar.autoIncrement; // Isar 内部主键(自动递增,别手动赋值)
|
||||
|
||||
int? questionId; // 服务器题号(业务去重用)
|
||||
String? title; // 题干(HTML 富文本,RichTextContent 渲染)
|
||||
String? options; // 选项(JSON 字符串,如 ["A项","B项","C项","D项"])
|
||||
String? answer; // 正确答案(多选 "A,B" 逗号拼接)
|
||||
int? type; // 题型:1 单选 / 2 多选 / 3 判断 / 4 解答
|
||||
String? analysis; // 解析(交卷后查看答案用)
|
||||
int? categoryId; // 分类 id
|
||||
DateTime? createdAt; // 收藏/下载时间
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
||||
import 'package:isar_community/isar.dart';
|
||||
|
||||
part 'wrong_question.g.dart';
|
||||
|
||||
/// ============================================================
|
||||
/// 【WrongQuestion】错题本模型(对应 AppDatabase 的错题集合)
|
||||
///
|
||||
/// 一、字段设计:存题目快照 + 我的错误答案 + 时间,
|
||||
/// 离线也能看错题,不用每次进错题本都拉接口。
|
||||
///
|
||||
/// 二、高频坑:
|
||||
/// 1. 和 Question 一样必须有 Id 主键;
|
||||
/// 2. 服务器题号存 questionId 用于去重(同一题错多次只留一条);
|
||||
/// 3. 改字段后记得重跑 build_runner。
|
||||
/// ============================================================
|
||||
@collection
|
||||
class WrongQuestion {
|
||||
Id id = Isar.autoIncrement; // Isar 内部主键(自动递增)
|
||||
|
||||
int? questionId; // 题目 id(去重)
|
||||
String? title; // 题干快照
|
||||
String? wrongAnswer; // 我提交的错误答案
|
||||
DateTime? wrongAt; // 加入错题本时间
|
||||
}
|
||||
@@ -0,0 +1,931 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'wrong_question.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// IsarCollectionGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types
|
||||
|
||||
extension GetWrongQuestionCollection on Isar {
|
||||
IsarCollection<WrongQuestion> get wrongQuestions => this.collection();
|
||||
}
|
||||
|
||||
const WrongQuestionSchema = CollectionSchema(
|
||||
name: r'WrongQuestion',
|
||||
id: -8005968927277545936,
|
||||
properties: {
|
||||
r'questionId': PropertySchema(
|
||||
id: 0,
|
||||
name: r'questionId',
|
||||
type: IsarType.long,
|
||||
),
|
||||
r'title': PropertySchema(id: 1, name: r'title', type: IsarType.string),
|
||||
r'wrongAnswer': PropertySchema(
|
||||
id: 2,
|
||||
name: r'wrongAnswer',
|
||||
type: IsarType.string,
|
||||
),
|
||||
r'wrongAt': PropertySchema(
|
||||
id: 3,
|
||||
name: r'wrongAt',
|
||||
type: IsarType.dateTime,
|
||||
),
|
||||
},
|
||||
|
||||
estimateSize: _wrongQuestionEstimateSize,
|
||||
serialize: _wrongQuestionSerialize,
|
||||
deserialize: _wrongQuestionDeserialize,
|
||||
deserializeProp: _wrongQuestionDeserializeProp,
|
||||
idName: r'id',
|
||||
indexes: {},
|
||||
links: {},
|
||||
embeddedSchemas: {},
|
||||
|
||||
getId: _wrongQuestionGetId,
|
||||
getLinks: _wrongQuestionGetLinks,
|
||||
attach: _wrongQuestionAttach,
|
||||
version: '3.3.2',
|
||||
);
|
||||
|
||||
int _wrongQuestionEstimateSize(
|
||||
WrongQuestion object,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
var bytesCount = offsets.last;
|
||||
{
|
||||
final value = object.title;
|
||||
if (value != null) {
|
||||
bytesCount += 3 + value.length * 3;
|
||||
}
|
||||
}
|
||||
{
|
||||
final value = object.wrongAnswer;
|
||||
if (value != null) {
|
||||
bytesCount += 3 + value.length * 3;
|
||||
}
|
||||
}
|
||||
return bytesCount;
|
||||
}
|
||||
|
||||
void _wrongQuestionSerialize(
|
||||
WrongQuestion object,
|
||||
IsarWriter writer,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
writer.writeLong(offsets[0], object.questionId);
|
||||
writer.writeString(offsets[1], object.title);
|
||||
writer.writeString(offsets[2], object.wrongAnswer);
|
||||
writer.writeDateTime(offsets[3], object.wrongAt);
|
||||
}
|
||||
|
||||
WrongQuestion _wrongQuestionDeserialize(
|
||||
Id id,
|
||||
IsarReader reader,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
final object = WrongQuestion();
|
||||
object.id = id;
|
||||
object.questionId = reader.readLongOrNull(offsets[0]);
|
||||
object.title = reader.readStringOrNull(offsets[1]);
|
||||
object.wrongAnswer = reader.readStringOrNull(offsets[2]);
|
||||
object.wrongAt = reader.readDateTimeOrNull(offsets[3]);
|
||||
return object;
|
||||
}
|
||||
|
||||
P _wrongQuestionDeserializeProp<P>(
|
||||
IsarReader reader,
|
||||
int propertyId,
|
||||
int offset,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
switch (propertyId) {
|
||||
case 0:
|
||||
return (reader.readLongOrNull(offset)) as P;
|
||||
case 1:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
case 2:
|
||||
return (reader.readStringOrNull(offset)) as P;
|
||||
case 3:
|
||||
return (reader.readDateTimeOrNull(offset)) as P;
|
||||
default:
|
||||
throw IsarError('Unknown property with id $propertyId');
|
||||
}
|
||||
}
|
||||
|
||||
Id _wrongQuestionGetId(WrongQuestion object) {
|
||||
return object.id;
|
||||
}
|
||||
|
||||
List<IsarLinkBase<dynamic>> _wrongQuestionGetLinks(WrongQuestion object) {
|
||||
return [];
|
||||
}
|
||||
|
||||
void _wrongQuestionAttach(
|
||||
IsarCollection<dynamic> col,
|
||||
Id id,
|
||||
WrongQuestion object,
|
||||
) {
|
||||
object.id = id;
|
||||
}
|
||||
|
||||
extension WrongQuestionQueryWhereSort
|
||||
on QueryBuilder<WrongQuestion, WrongQuestion, QWhere> {
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterWhere> anyId() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(const IdWhereClause.any());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension WrongQuestionQueryWhere
|
||||
on QueryBuilder<WrongQuestion, WrongQuestion, QWhereClause> {
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterWhereClause> idEqualTo(
|
||||
Id id,
|
||||
) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(IdWhereClause.between(lower: id, upper: id));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterWhereClause> idNotEqualTo(
|
||||
Id id,
|
||||
) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
if (query.whereSort == Sort.asc) {
|
||||
return query
|
||||
.addWhereClause(
|
||||
IdWhereClause.lessThan(upper: id, includeUpper: false),
|
||||
)
|
||||
.addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: id, includeLower: false),
|
||||
);
|
||||
} else {
|
||||
return query
|
||||
.addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: id, includeLower: false),
|
||||
)
|
||||
.addWhereClause(
|
||||
IdWhereClause.lessThan(upper: id, includeUpper: false),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterWhereClause> idGreaterThan(
|
||||
Id id, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: id, includeLower: include),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterWhereClause> idLessThan(
|
||||
Id id, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.lessThan(upper: id, includeUpper: include),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterWhereClause> idBetween(
|
||||
Id lowerId,
|
||||
Id upperId, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.between(
|
||||
lower: lowerId,
|
||||
includeLower: includeLower,
|
||||
upper: upperId,
|
||||
includeUpper: includeUpper,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension WrongQuestionQueryFilter
|
||||
on QueryBuilder<WrongQuestion, WrongQuestion, QFilterCondition> {
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition> idEqualTo(
|
||||
Id value,
|
||||
) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(property: r'id', value: value),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
idGreaterThan(Id value, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'id',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition> idLessThan(
|
||||
Id value, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'id',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition> idBetween(
|
||||
Id lower,
|
||||
Id upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.between(
|
||||
property: r'id',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
questionIdIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNull(property: r'questionId'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
questionIdIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNotNull(property: r'questionId'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
questionIdEqualTo(int? value) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(property: r'questionId', value: value),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
questionIdGreaterThan(int? value, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'questionId',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
questionIdLessThan(int? value, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'questionId',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
questionIdBetween(
|
||||
int? lower,
|
||||
int? upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.between(
|
||||
property: r'questionId',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
titleIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNull(property: r'title'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
titleIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNotNull(property: r'title'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
titleEqualTo(String? value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(
|
||||
property: r'title',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
titleGreaterThan(
|
||||
String? value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'title',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
titleLessThan(
|
||||
String? value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'title',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
titleBetween(
|
||||
String? lower,
|
||||
String? upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.between(
|
||||
property: r'title',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
titleStartsWith(String value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.startsWith(
|
||||
property: r'title',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
titleEndsWith(String value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.endsWith(
|
||||
property: r'title',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
titleContains(String value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.contains(
|
||||
property: r'title',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
titleMatches(String pattern, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.matches(
|
||||
property: r'title',
|
||||
wildcard: pattern,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
titleIsEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(property: r'title', value: ''),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
titleIsNotEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(property: r'title', value: ''),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
wrongAnswerIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNull(property: r'wrongAnswer'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
wrongAnswerIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNotNull(property: r'wrongAnswer'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
wrongAnswerEqualTo(String? value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(
|
||||
property: r'wrongAnswer',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
wrongAnswerGreaterThan(
|
||||
String? value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'wrongAnswer',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
wrongAnswerLessThan(
|
||||
String? value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'wrongAnswer',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
wrongAnswerBetween(
|
||||
String? lower,
|
||||
String? upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.between(
|
||||
property: r'wrongAnswer',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
wrongAnswerStartsWith(String value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.startsWith(
|
||||
property: r'wrongAnswer',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
wrongAnswerEndsWith(String value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.endsWith(
|
||||
property: r'wrongAnswer',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
wrongAnswerContains(String value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.contains(
|
||||
property: r'wrongAnswer',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
wrongAnswerMatches(String pattern, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.matches(
|
||||
property: r'wrongAnswer',
|
||||
wildcard: pattern,
|
||||
caseSensitive: caseSensitive,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
wrongAnswerIsEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(property: r'wrongAnswer', value: ''),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
wrongAnswerIsNotEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(property: r'wrongAnswer', value: ''),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
wrongAtIsNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNull(property: r'wrongAt'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
wrongAtIsNotNull() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
const FilterCondition.isNotNull(property: r'wrongAt'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
wrongAtEqualTo(DateTime? value) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.equalTo(property: r'wrongAt', value: value),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
wrongAtGreaterThan(DateTime? value, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'wrongAt',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
wrongAtLessThan(DateTime? value, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'wrongAt',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterFilterCondition>
|
||||
wrongAtBetween(
|
||||
DateTime? lower,
|
||||
DateTime? upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(
|
||||
FilterCondition.between(
|
||||
property: r'wrongAt',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension WrongQuestionQueryObject
|
||||
on QueryBuilder<WrongQuestion, WrongQuestion, QFilterCondition> {}
|
||||
|
||||
extension WrongQuestionQueryLinks
|
||||
on QueryBuilder<WrongQuestion, WrongQuestion, QFilterCondition> {}
|
||||
|
||||
extension WrongQuestionQuerySortBy
|
||||
on QueryBuilder<WrongQuestion, WrongQuestion, QSortBy> {
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterSortBy> sortByQuestionId() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'questionId', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterSortBy>
|
||||
sortByQuestionIdDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'questionId', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterSortBy> sortByTitle() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'title', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterSortBy> sortByTitleDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'title', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterSortBy> sortByWrongAnswer() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'wrongAnswer', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterSortBy>
|
||||
sortByWrongAnswerDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'wrongAnswer', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterSortBy> sortByWrongAt() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'wrongAt', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterSortBy> sortByWrongAtDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'wrongAt', Sort.desc);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension WrongQuestionQuerySortThenBy
|
||||
on QueryBuilder<WrongQuestion, WrongQuestion, QSortThenBy> {
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterSortBy> thenById() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'id', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterSortBy> thenByIdDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'id', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterSortBy> thenByQuestionId() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'questionId', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterSortBy>
|
||||
thenByQuestionIdDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'questionId', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterSortBy> thenByTitle() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'title', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterSortBy> thenByTitleDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'title', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterSortBy> thenByWrongAnswer() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'wrongAnswer', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterSortBy>
|
||||
thenByWrongAnswerDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'wrongAnswer', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterSortBy> thenByWrongAt() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'wrongAt', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QAfterSortBy> thenByWrongAtDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'wrongAt', Sort.desc);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension WrongQuestionQueryWhereDistinct
|
||||
on QueryBuilder<WrongQuestion, WrongQuestion, QDistinct> {
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QDistinct> distinctByQuestionId() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'questionId');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QDistinct> distinctByTitle({
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'title', caseSensitive: caseSensitive);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QDistinct> distinctByWrongAnswer({
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'wrongAnswer', caseSensitive: caseSensitive);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, WrongQuestion, QDistinct> distinctByWrongAt() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'wrongAt');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension WrongQuestionQueryProperty
|
||||
on QueryBuilder<WrongQuestion, WrongQuestion, QQueryProperty> {
|
||||
QueryBuilder<WrongQuestion, int, QQueryOperations> idProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'id');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, int?, QQueryOperations> questionIdProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'questionId');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, String?, QQueryOperations> titleProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'title');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, String?, QQueryOperations> wrongAnswerProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'wrongAnswer');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<WrongQuestion, DateTime?, QQueryOperations> wrongAtProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'wrongAt');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
/// ============================================================
|
||||
/// 【PrivacyStorage】隐私协议同意状态本地存储
|
||||
///
|
||||
/// 作用:
|
||||
/// 1. 记录用户是否已经同意启动隐私协议;
|
||||
/// 2. App 冷启动时先读这个值,决定是否要先拦住进入主流程;
|
||||
/// 3. 一旦同意过,就不再重复弹首次启动隐私弹层。
|
||||
///
|
||||
/// 为什么单独拆一个存储类:
|
||||
/// 这类"启动门禁"和用户资料不是一回事,放独立文件更清晰,
|
||||
/// 后续如果要补"撤回授权 / 重新查看协议"也更好扩展。
|
||||
/// ============================================================
|
||||
class PrivacyStorage {
|
||||
PrivacyStorage._();
|
||||
|
||||
static final PrivacyStorage instance = PrivacyStorage._();
|
||||
|
||||
static const String _privacyAcceptedKey = 'privacy_accepted';
|
||||
|
||||
/// 读取是否已同意隐私协议。
|
||||
/// 首次安装/首次打开没有记录时,默认返回 false。
|
||||
Future<bool> readAccepted() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getBool(_privacyAcceptedKey) ?? false;
|
||||
}
|
||||
|
||||
/// 保存"已同意隐私协议"状态。
|
||||
Future<void> saveAccepted(bool accepted) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setBool(_privacyAcceptedKey, accepted);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/// ============================================================
|
||||
/// 【AnswerRecordStore】答题进度数据源 —— 平台分发入口(条件导出)
|
||||
///
|
||||
/// 一、为什么也要条件导出(和 WrongQuestionStore 同理):
|
||||
/// AnswerRecord 模型 import 了 isar,web 端 import 直接编译失败。
|
||||
/// 所以"Isar 访问"全部收口到本 store,页面永远只面向本文件:
|
||||
/// - native → answer_record_store_io.dart(真 Isar,续答落盘)
|
||||
/// - web → answer_record_store_web.dart(空实现,跳过持久化)
|
||||
///
|
||||
/// 二、续答三动作(对应三个方法):
|
||||
/// findByPaper() 进卷时读回 → 注入 answerProvider
|
||||
/// put() 每次作答增量写单条(毫秒级)
|
||||
/// deleteByPaper() 交卷成功后按 paperId 批量清理
|
||||
///
|
||||
/// 三、接口统一用 dynamic:
|
||||
/// 模型 import 了 isar,web 端无法 import 模型,接口层返回
|
||||
/// `List<dynamic>`,页面用 dynamic 访问字段(见 wrong_question_store)。
|
||||
/// ============================================================
|
||||
library;
|
||||
|
||||
/// 如果是原生平台就用 Isar 真实现,否则用空实现兜底
|
||||
export 'answer_record_store_web.dart'
|
||||
if (dart.library.io) 'answer_record_store_io.dart';
|
||||
@@ -0,0 +1,68 @@
|
||||
import 'package:isar_community/isar.dart';
|
||||
import 'package:new_learn/core/storage/database/app_database_io.dart';
|
||||
import '../models/answer_record.dart';
|
||||
|
||||
/// ============================================================
|
||||
/// 【AnswerRecordStore】答题进度数据源 —— native 真实现(Isar)
|
||||
///
|
||||
/// 一、续答数据流(进卷 → 答题 → 交卷):
|
||||
/// findByPaper() 进卷时按 paperId 查全部答案 → 注入 answerProvider
|
||||
/// put() 每题作答写一条(增量 put,幂等:同题覆盖旧答案)
|
||||
/// deleteByPaper() 交卷成功后批量删该卷记录(防止下次恢复旧答案)
|
||||
///
|
||||
/// 二、为什么用 @Index() 索引:
|
||||
/// where(paperId).equalTo() 走索引查询,100 题毫秒级;
|
||||
/// 交卷 deleteByPaper 同样按 paperId 过滤,一次事务删干净。
|
||||
///
|
||||
/// 三、坑:
|
||||
/// 1. 写操作必须包在 isar.writeTxn()(Isar 事务约束);
|
||||
/// 2. 改模型字段后重跑 build_runner;
|
||||
/// 3. ⚠ 这里必须 import app_database_io.dart(具体实现),
|
||||
/// 不能 import app_database.dart(条件导出入口)!
|
||||
/// 分析器会检查所有条件分支,web 分支下入口解析成空实现,
|
||||
/// db.isar / db.answerRecords 会报 undefined getter。
|
||||
/// ============================================================
|
||||
class AnswerRecordStore {
|
||||
AnswerRecordStore._();
|
||||
|
||||
static final AnswerRecordStore instance = AnswerRecordStore._();
|
||||
|
||||
/// 【进卷恢复】按试卷查全部已答记录
|
||||
Future<List<dynamic>> findByPaper(int paperId) async {
|
||||
final db = AppDatabase.instance;
|
||||
final list = await db.answerRecords
|
||||
.where()
|
||||
.paperIdEqualTo(paperId)
|
||||
.findAll();
|
||||
return list.cast<dynamic>();
|
||||
}
|
||||
|
||||
/// 【作答写入】每题答案增量落盘(同题重复作答自动覆盖旧值)
|
||||
Future<void> put({
|
||||
required int paperId,
|
||||
required int questionId,
|
||||
required String answer,
|
||||
}) async {
|
||||
final db = AppDatabase.instance;
|
||||
await db.isar.writeTxn(() async {
|
||||
await db.answerRecords.put(
|
||||
AnswerRecord()
|
||||
..paperId = paperId
|
||||
..questionId = questionId
|
||||
..answer = answer
|
||||
..updatedAt = DateTime.now(),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// 【交卷清理】按试卷批量删除进度(交卷成功后调用)
|
||||
Future<void> deleteByPaper(int paperId) async {
|
||||
final db = AppDatabase.instance;
|
||||
await db.isar.writeTxn(() async {
|
||||
await db.answerRecords
|
||||
.where()
|
||||
.paperIdEqualTo(paperId)
|
||||
.deleteAll();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
/// ============================================================
|
||||
/// 【AnswerRecordStore】答题进度数据源 —— web 空实现
|
||||
///
|
||||
/// web 端不 import isar / 模型(编译约束),所有操作变为空实现:
|
||||
/// 写入 → 打印提示;读取 → 空列表。
|
||||
/// 效果:web 调试时"续答持久化"自动降级为纯内存(不落盘),
|
||||
/// UI/滑动/答题逻辑全部正常,只有刷新页面后进度丢失。
|
||||
/// ============================================================
|
||||
class AnswerRecordStore {
|
||||
AnswerRecordStore._();
|
||||
|
||||
static final AnswerRecordStore instance = AnswerRecordStore._();
|
||||
|
||||
Future<List<dynamic>> findByPaper(int paperId) async => const [];
|
||||
|
||||
Future<void> put({
|
||||
required int paperId,
|
||||
required int questionId,
|
||||
required String answer,
|
||||
}) async {
|
||||
debugPrint('▶ 【Isar】Web 调试模式:答题进度写入已跳过(仅真机可持久化续答)');
|
||||
}
|
||||
|
||||
Future<void> deleteByPaper(int paperId) async {}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/// ============================================================
|
||||
/// 【WrongQuestionStore】错题本数据源 —— 平台分发入口(条件导出)
|
||||
///
|
||||
/// 一、为什么也要条件导出(和 AppDatabase 同理):
|
||||
/// Isar 的集合类型(IsarCollection<T>)只存在于 native 实现,
|
||||
/// web 端 import isar / *.g.dart 直接编译失败。页面如果直接
|
||||
/// 访问 AppDatabase.instance.wrongQuestions,web 编译就报错。
|
||||
/// 所以把"Isar 访问"全部收口到本 store,页面永远只面向本文件:
|
||||
/// - native → wrong_question_store_io.dart(真 Isar)
|
||||
/// - web → wrong_question_store_web.dart(空实现,跳过写库)
|
||||
///
|
||||
/// 二、为什么接口用 dynamic 而非 WrongQuestion:
|
||||
/// WrongQuestion 模型 import 了 isar,web 端无法 import 模型。
|
||||
/// 接口统一返回 List<dynamic>,页面用 dynamic 访问字段,
|
||||
/// web 编译通过、运行时只是拿不到数据(演示降级提示)。
|
||||
///
|
||||
/// 三、这是【Isar 深水区:watch 响应式】的入口:
|
||||
/// watchAll() 返回 Stream:数据库一变(任何地方 put/delete),
|
||||
/// 流自动发事件 → 页面 StreamBuilder 自动重建,不用手动 reload。
|
||||
/// ============================================================
|
||||
library;
|
||||
|
||||
/// 如果是原生平台就用 Isar 真实现,否则用空实现兜底
|
||||
export 'wrong_question_store_web.dart'
|
||||
if (dart.library.io) 'wrong_question_store_io.dart';
|
||||
@@ -0,0 +1,79 @@
|
||||
import 'package:isar_community/isar.dart';
|
||||
import 'package:new_learn/core/storage/database/app_database_io.dart';
|
||||
import '../models/wrong_question.dart';
|
||||
|
||||
/// ============================================================
|
||||
/// 【WrongQuestionStore】错题本数据源 —— native 真实现(Isar)
|
||||
///
|
||||
/// 一、watch 响应式(本文件核心):
|
||||
/// watchAll() → AppDatabase.wrongQuestions.where().watch(fireImmediately: true)
|
||||
/// - fireImmediately: true = 订阅后立刻吐一次当前全量数据,
|
||||
/// 页面首帧就能显示,不用再单独查一次;
|
||||
/// - 之后任何地方 put/delete 错题(即使不是本页面),
|
||||
/// 流自动发新事件 → 订阅它的 StreamBuilder 自动重建。
|
||||
/// 这就是"数据库推数据"而非"页面拉数据"。
|
||||
///
|
||||
/// 二、对比非响应式:
|
||||
/// findAll() → where().findAll() 是主动查询,要自己触发/刷新。
|
||||
///
|
||||
/// 三、坑:
|
||||
/// 1. 写操作必须包在 isar.writeTxn()(Isar 事务约束);
|
||||
/// 2. 改模型字段后重跑 build_runner;
|
||||
/// 3. watch 返回 `Stream<List<Object>>`,接口层 cast 成
|
||||
/// `List<dynamic>` 供页面消费(web 端也要能编译);
|
||||
/// 4. ⚠ 这里必须 import app_database_io.dart(具体实现),
|
||||
/// 不能 import app_database.dart(条件导出入口)!
|
||||
/// 分析器会检查所有条件分支,web 分支下入口解析成空实现,
|
||||
/// db.isar / db.wrongQuestions 会报 undefined getter。
|
||||
/// 本文件只在 native 编译,直接 import io 实现最稳。
|
||||
/// 5. ⚠ isar_community 3.3.2 的 API 和 isar 3.1.x 不一样(大坑):
|
||||
/// - IsarCollection 上【没有】watch(),全量监听要写成
|
||||
/// where().watch()(返回 `Stream<List<T>>`);
|
||||
/// - QueryBuilder 上【没有】findAll(),终端方法(findAll/
|
||||
/// watch/count/findFirst...)都在 extension 里,而 Dart 的
|
||||
/// extension 成员要求定义它的库被 import 才可见(import 不传递),
|
||||
/// 所以本文件必须加 import 'package:isar_community/isar.dart',
|
||||
/// 否则 where().findAll() / where().watch() 报 undefined method。
|
||||
/// ============================================================
|
||||
class WrongQuestionStore {
|
||||
WrongQuestionStore._();
|
||||
|
||||
static final WrongQuestionStore instance = WrongQuestionStore._();
|
||||
|
||||
/// 添加一条错题(演示:任何写入都会触发 watch 事件)
|
||||
Future<void> add({
|
||||
int? questionId,
|
||||
String? title,
|
||||
String? wrongAnswer,
|
||||
}) async {
|
||||
final db = AppDatabase.instance;
|
||||
await db.isar.writeTxn(() async {
|
||||
await db.wrongQuestions.put(
|
||||
WrongQuestion()
|
||||
..questionId = questionId
|
||||
..title = title
|
||||
..wrongAnswer = wrongAnswer
|
||||
..wrongAt = DateTime.now(),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// 删除一条(按 Isar 主键 id)
|
||||
Future<void> remove(int id) async {
|
||||
final db = AppDatabase.instance;
|
||||
await db.isar.writeTxn(() => db.wrongQuestions.delete(id));
|
||||
}
|
||||
|
||||
/// 【响应式】错题集合 watch 流:数据一变自动发事件
|
||||
Stream<List<dynamic>> watchAll() {
|
||||
return AppDatabase.instance.wrongQuestions
|
||||
.where()
|
||||
.watch(fireImmediately: true)
|
||||
.cast<List<dynamic>>();
|
||||
}
|
||||
|
||||
/// 【非响应式】手动查询全部(对比 watch 用,需自己触发刷新)
|
||||
Future<List<dynamic>> findAll() async {
|
||||
return AppDatabase.instance.wrongQuestions.where().findAll();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
/// ============================================================
|
||||
/// 【WrongQuestionStore】错题本数据源 —— web 空实现
|
||||
///
|
||||
/// web 端不 import isar / 模型(编译约束),所有操作变为空实现:
|
||||
/// 写入 → 打印提示;读取 → 空流/空列表。
|
||||
/// demo 页面在 web 上用 kIsWeb 显示"仅真机可用"提示即可。
|
||||
/// ============================================================
|
||||
class WrongQuestionStore {
|
||||
WrongQuestionStore._();
|
||||
|
||||
static final WrongQuestionStore instance = WrongQuestionStore._();
|
||||
|
||||
Future<void> add({
|
||||
int? questionId,
|
||||
String? title,
|
||||
String? wrongAnswer,
|
||||
}) async {
|
||||
debugPrint('▶ 【Isar】Web 调试模式:错题写入已跳过(仅真机可用)');
|
||||
}
|
||||
|
||||
Future<void> remove(int id) async {}
|
||||
|
||||
Stream<List<dynamic>> watchAll() => const Stream.empty();
|
||||
|
||||
Future<List<dynamic>> findAll() async => const [];
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:new_learn/core/models/user_model.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
|
||||
/// ============================================================
|
||||
/// 【UserStorage】用户本地存储(token 安全存储 + 用户信息缓存)
|
||||
///
|
||||
/// 一、为什么用两个插件(对应 Vue 的 uni.setStorageSync):
|
||||
/// 1. flutter_secure_storage → 存 Token(敏感凭据)
|
||||
/// Android 走 Keystore 加密(EncryptedSharedPreferences),
|
||||
/// iOS 走 Keychain,越狱/root 也难读出,适合放登录凭据;
|
||||
/// 2. shared_preferences → 存用户信息缓存(非敏感)
|
||||
/// 明文 XML/plist,读写快,适合放 userName/avatar 这类
|
||||
/// 不敏感数据 + 各种开关。
|
||||
/// 混合方案 = 业界标准做法:凭据安全、配置轻量。
|
||||
///
|
||||
/// 二、高频坑:
|
||||
/// 1. 两个插件都要 await 初始化,别在 build 里同步调用;
|
||||
/// 2. shared_preferences 存对象必须先 jsonEncode 成字符串;
|
||||
/// 3. 密码永远不要明文存 shared_preferences!(记住密码≠存密码)
|
||||
/// ============================================================
|
||||
class UserStorage {
|
||||
UserStorage._();
|
||||
|
||||
static final UserStorage instance = UserStorage._();
|
||||
|
||||
static const String _tokenKey = 'auth_token';
|
||||
static const String _userKey = 'current_user';
|
||||
|
||||
final FlutterSecureStorage _secure = const FlutterSecureStorage();
|
||||
|
||||
// ---------- Token(安全存储) ----------
|
||||
|
||||
/// 保存登录 Token(登录成功后调用)
|
||||
Future<void> saveToken(String token) =>
|
||||
_secure.write(key: _tokenKey, value: token);
|
||||
|
||||
/// 读取 Token(冷启动 restoreToken 用)
|
||||
Future<String?> readToken() => _secure.read(key: _tokenKey);
|
||||
|
||||
/// 清除 Token(退出登录时调用)
|
||||
Future<void> clearToken() => _secure.delete(key: _tokenKey);
|
||||
|
||||
// ---------- 用户信息(普通缓存) ----------
|
||||
|
||||
/// 保存当前用户信息(登录后调 GetCurrentUserExtension 拿到再存)
|
||||
///
|
||||
/// 作用:
|
||||
/// 1. 把"内存里的用户对象"落到本地;
|
||||
/// 2. App 下次冷启动时,currentUserProvider.build() 可以直接读缓存回填;
|
||||
/// 3. 这样头像/用户名/班级等信息不用等接口返回,页面也能先展示。
|
||||
///
|
||||
/// 为什么要 jsonEncode:
|
||||
/// SharedPreferences 不能直接存 Dart 对象,只能存 String/int/bool 等基础类型,
|
||||
/// 所以要先把 CurrentUserExtension → Map → JSON 字符串。
|
||||
Future<void> saveUserInfo(CurrentUserExtension user) async {
|
||||
// 拿到 SharedPreferences 单例,后续通过 key-value 方式写本地缓存。
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
|
||||
// user.toJson():对象转 Map
|
||||
// jsonEncode(...):Map 转字符串
|
||||
// _userKey:当前用户缓存的固定 key
|
||||
await prefs.setString(_userKey, jsonEncode(user.toJson()));
|
||||
}
|
||||
|
||||
/// 读取当前用户信息(没有登录过则返回 null)
|
||||
Future<CurrentUserExtension?> readUserInfo() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final raw = prefs.getString(_userKey);
|
||||
if (raw == null) return null;
|
||||
try {
|
||||
return CurrentUserExtension.fromJson(
|
||||
jsonDecode(raw) as Map<String, dynamic>,
|
||||
);
|
||||
} catch (_) {
|
||||
return null; // 缓存损坏时当没登录过处理
|
||||
}
|
||||
}
|
||||
|
||||
/// 清除用户信息(退出登录时调用)
|
||||
Future<void> clearUserInfo() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.remove(_userKey);
|
||||
}
|
||||
|
||||
// ---------- 记住密码(账号普通缓存 + 密码安全存储) ----------
|
||||
|
||||
static const String _rememberedAccountKey = 'remembered_account';
|
||||
static const String _rememberedPasswordKey = 'remembered_password';
|
||||
|
||||
/// 保存记住的账号(非敏感 → shared_preferences 明文即可)
|
||||
Future<void> saveRememberedAccount(String account) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString(_rememberedAccountKey, account);
|
||||
}
|
||||
|
||||
/// 读取记住的账号(没存过返回 null)
|
||||
Future<String?> readRememberedAccount() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getString(_rememberedAccountKey);
|
||||
}
|
||||
|
||||
Future<void> clearRememberedAccount() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.remove(_rememberedAccountKey);
|
||||
}
|
||||
|
||||
/// 保存记住的密码(敏感 → flutter_secure_storage 加密存储)
|
||||
/// 注意:只有用户勾选"记住密码"才调用,不勾选绝不落盘
|
||||
Future<void> saveRememberedPassword(String password) =>
|
||||
_secure.write(key: _rememberedPasswordKey, value: password);
|
||||
|
||||
Future<String?> readRememberedPassword() =>
|
||||
_secure.read(key: _rememberedPasswordKey);
|
||||
|
||||
Future<void> clearRememberedPassword() =>
|
||||
_secure.delete(key: _rememberedPasswordKey);
|
||||
|
||||
/// 一键退出登录:清 Token + 用户信息
|
||||
/// (记住的账号密码保留,方便用户下次登录)
|
||||
Future<void> clearAll() async {
|
||||
await clearToken();
|
||||
await clearUserInfo();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user