perf(app): Phase 2 前端性能优化 5 项 — 8b-D01/D02/D03/M02/N01
- 8b-D01: Isar 添加 authorId+dateEpoch 复合索引和 dateEpoch 单独索引 - 8b-D02: getJournals 分页改为 DB 层 .offset().limit() 替代 Dart 层 sublist - 8b-D03: home_bloc monthCount 改用日期范围独立查询(不受分页限制) - 8b-M02: 笔画光栅化改为 BBox 裁剪 — 短笔画不再创建全画布尺寸图像 - _CacheEntry 增加 offset 字段记录 BBox 偏移 - _rasterizeStroke 计算包围盒 + 4px padding - _compositeIncremental 使用 offset 定位 - 8b-N01: SyncEngine enqueue 合并同一资源的操作 - create+update → create(最新数据) - update+update → update(最新数据) - update+delete → delete - create+delete → 取消(不发送) - 注意: Isar .g.dart 需运行 build_runner 重新生成
This commit is contained in:
@@ -16,7 +16,8 @@ class JournalEntryCollection {
|
||||
@Index()
|
||||
String id = '';
|
||||
|
||||
/// 作者 ID
|
||||
/// 作者 ID(索引 + 组合索引 authorId+dateEpoch,覆盖按作者查询并按日期排序的场景)
|
||||
@Index(composite: [CompositeIndex('dateEpoch')])
|
||||
String authorId = '';
|
||||
|
||||
/// 班级 ID(可选)
|
||||
@@ -25,7 +26,8 @@ class JournalEntryCollection {
|
||||
/// 日记标题
|
||||
String title = '';
|
||||
|
||||
/// 日记日期(epoch milliseconds)
|
||||
/// 日记日期(epoch milliseconds)— 单独索引支持日期范围查询
|
||||
@Index()
|
||||
int dateEpoch = 0;
|
||||
|
||||
/// 心情(enum → string)
|
||||
|
||||
@@ -64,19 +64,18 @@ class IsarJournalRepository implements JournalRepository {
|
||||
query = query.and().classIdEqualTo(classId);
|
||||
}
|
||||
|
||||
// 按日期降序排列
|
||||
var results = await query
|
||||
.sortByDateEpochDesc()
|
||||
.findAll();
|
||||
|
||||
// 分页
|
||||
// 按日期降序排列 + DB 层分页(替代全量加载后 Dart 层 sublist)
|
||||
if (page != null && pageSize != null) {
|
||||
final start = (page - 1) * pageSize;
|
||||
if (start >= results.length) return [];
|
||||
final end = (start + pageSize).clamp(0, results.length);
|
||||
results = results.sublist(start, end);
|
||||
final offset = (page - 1) * pageSize;
|
||||
final results = await query
|
||||
.sortByDateEpochDesc()
|
||||
.offset(offset)
|
||||
.limit(pageSize)
|
||||
.findAll();
|
||||
return results.map(_fromCollection).toList();
|
||||
}
|
||||
|
||||
final results = await query.sortByDateEpochDesc().findAll();
|
||||
return results.map(_fromCollection).toList();
|
||||
}
|
||||
|
||||
|
||||
@@ -136,21 +136,77 @@ class SyncEngine {
|
||||
bool get isSyncing => _status == SyncStatus.syncing;
|
||||
|
||||
/// 添加待同步操作到队列尾部
|
||||
///
|
||||
/// 合并策略(8b-N01):同一资源(endpoint 相同)的连续操作只保留最新一条。
|
||||
/// create+update → create(使用最新数据)
|
||||
/// update+update → update(使用最新数据)
|
||||
/// update+delete → delete(资源最终被删除)
|
||||
/// create+delete → 取消(资源从未存在)
|
||||
void enqueue(PendingOperation operation) {
|
||||
_pendingQueue.add(operation);
|
||||
// 查找队列中同一资源的最后一个操作
|
||||
PendingOperation? existing;
|
||||
for (final op in _pendingQueue) {
|
||||
if (op.endpoint == operation.endpoint) {
|
||||
existing = op;
|
||||
}
|
||||
}
|
||||
|
||||
if (existing != null) {
|
||||
final merged = _mergeOperations(existing, operation);
|
||||
_pendingQueue.remove(existing);
|
||||
if (merged != null) {
|
||||
_pendingQueue.add(merged);
|
||||
}
|
||||
// merged == null → create+delete 取消,不添加
|
||||
} else {
|
||||
_pendingQueue.add(operation);
|
||||
}
|
||||
|
||||
if (_status == SyncStatus.idle) {
|
||||
_status = SyncStatus.paused;
|
||||
}
|
||||
}
|
||||
|
||||
/// 批量添加待同步操作
|
||||
/// 批量添加待同步操作(每个操作独立走合并逻辑)
|
||||
void enqueueAll(List<PendingOperation> operations) {
|
||||
for (final op in operations) {
|
||||
_pendingQueue.add(op);
|
||||
enqueue(op);
|
||||
}
|
||||
if (_status == SyncStatus.idle && _pendingQueue.isNotEmpty) {
|
||||
_status = SyncStatus.paused;
|
||||
}
|
||||
|
||||
/// 合并同一资源的两个操作
|
||||
///
|
||||
/// 返回合并后的操作,或 null 表示应取消(create+delete)。
|
||||
PendingOperation? _mergeOperations(
|
||||
PendingOperation existing,
|
||||
PendingOperation incoming,
|
||||
) {
|
||||
// create + delete → 取消(资源从未同步到服务端)
|
||||
if (existing.type == SyncOperationType.create &&
|
||||
incoming.type == SyncOperationType.delete) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// create + update → create(使用最新数据)
|
||||
if (existing.type == SyncOperationType.create &&
|
||||
incoming.type == SyncOperationType.update) {
|
||||
return existing.copyWith(data: incoming.data, version: incoming.version);
|
||||
}
|
||||
|
||||
// update + update → update(使用最新数据)
|
||||
if (existing.type == SyncOperationType.update &&
|
||||
incoming.type == SyncOperationType.update) {
|
||||
return incoming;
|
||||
}
|
||||
|
||||
// update + delete → delete
|
||||
if (existing.type == SyncOperationType.update &&
|
||||
incoming.type == SyncOperationType.delete) {
|
||||
return incoming;
|
||||
}
|
||||
|
||||
// 其他组合(delete+create, create+create 等)不合并
|
||||
return incoming;
|
||||
}
|
||||
|
||||
/// 检查网络状态并尝试同步全部待处理操作
|
||||
|
||||
Reference in New Issue
Block a user