refactor(diary): Phase 3 质量提升 — 201 状态码 + OpenAPI 文档 + DiaryEvent 类型安全
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled

前端:
- fix(app): Isar native 文件直接导入 isar_database_native.dart,消除 5 个条件导出类型错误
- chore(app): build_runner 重新生成 .g.dart 文件 (102 outputs)
- fix(app): 移除 secure_token_store_factory 未使用的 kIsWeb import

后端:
- refactor(diary): 所有创建端点 POST 返回 201 Created (9 handler, 11 端点)
- feat(diary): DiaryApiDoc OpenApi derive — 42 路径 + 32 Schema 汇总到 Swagger
- feat(diary): DiaryEvent 枚举添加 event_type/payload/to_domain_event 方法 + 4 测试

测试: 84/84 erp-diary 通过, 509/509 全仓库通过, Flutter analyze 0 error
This commit is contained in:
iven
2026-06-03 17:06:03 +08:00
parent e8df3a9562
commit 38592d61ce
17 changed files with 1038 additions and 70 deletions

View File

@@ -16,7 +16,7 @@ extension GetJournalElementCollectionCollection on Isar {
const JournalElementCollectionSchema = CollectionSchema(
name: r'JournalElementCollection',
id: -1002,
id: -3625932583395690305,
properties: {
r'contentJson': PropertySchema(
id: 0,
@@ -96,7 +96,7 @@ const JournalElementCollectionSchema = CollectionSchema(
idName: r'isarId',
indexes: {
r'id': IndexSchema(
id: -2002,
id: -3268401673993471357,
name: r'id',
unique: false,
replace: false,
@@ -109,7 +109,7 @@ const JournalElementCollectionSchema = CollectionSchema(
],
),
r'journalId': IndexSchema(
id: 3001,
id: 1745640946427815323,
name: r'journalId',
unique: false,
replace: false,

View File

@@ -16,7 +16,7 @@ extension GetJournalEntryCollectionCollection on Isar {
const JournalEntryCollectionSchema = CollectionSchema(
name: r'JournalEntryCollection',
id: -1001,
id: -6325316395299921961,
properties: {
r'assignedTopicId': PropertySchema(
id: 0,
@@ -106,7 +106,7 @@ const JournalEntryCollectionSchema = CollectionSchema(
idName: r'isarId',
indexes: {
r'id': IndexSchema(
id: -2001,
id: -3268401673993471357,
name: r'id',
unique: false,
replace: false,
@@ -117,6 +117,37 @@ const JournalEntryCollectionSchema = CollectionSchema(
caseSensitive: true,
)
],
),
r'authorId_dateEpoch': IndexSchema(
id: -4869847655132214108,
name: r'authorId_dateEpoch',
unique: false,
replace: false,
properties: [
IndexPropertySchema(
name: r'authorId',
type: IndexType.hash,
caseSensitive: true,
),
IndexPropertySchema(
name: r'dateEpoch',
type: IndexType.value,
caseSensitive: false,
)
],
),
r'dateEpoch': IndexSchema(
id: 359017825055613028,
name: r'dateEpoch',
unique: false,
replace: false,
properties: [
IndexPropertySchema(
name: r'dateEpoch',
type: IndexType.value,
caseSensitive: false,
)
],
)
},
links: {},
@@ -277,6 +308,15 @@ extension JournalEntryCollectionQueryWhereSort
return query.addWhereClause(const IdWhereClause.any());
});
}
QueryBuilder<JournalEntryCollection, JournalEntryCollection, QAfterWhere>
anyDateEpoch() {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(
const IndexWhereClause.any(indexName: r'dateEpoch'),
);
});
}
}
extension JournalEntryCollectionQueryWhere on QueryBuilder<
@@ -393,6 +433,242 @@ extension JournalEntryCollectionQueryWhere on QueryBuilder<
}
});
}
QueryBuilder<JournalEntryCollection, JournalEntryCollection,
QAfterWhereClause> authorIdEqualToAnyDateEpoch(String authorId) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IndexWhereClause.equalTo(
indexName: r'authorId_dateEpoch',
value: [authorId],
));
});
}
QueryBuilder<JournalEntryCollection, JournalEntryCollection,
QAfterWhereClause> authorIdNotEqualToAnyDateEpoch(String authorId) {
return QueryBuilder.apply(this, (query) {
if (query.whereSort == Sort.asc) {
return query
.addWhereClause(IndexWhereClause.between(
indexName: r'authorId_dateEpoch',
lower: [],
upper: [authorId],
includeUpper: false,
))
.addWhereClause(IndexWhereClause.between(
indexName: r'authorId_dateEpoch',
lower: [authorId],
includeLower: false,
upper: [],
));
} else {
return query
.addWhereClause(IndexWhereClause.between(
indexName: r'authorId_dateEpoch',
lower: [authorId],
includeLower: false,
upper: [],
))
.addWhereClause(IndexWhereClause.between(
indexName: r'authorId_dateEpoch',
lower: [],
upper: [authorId],
includeUpper: false,
));
}
});
}
QueryBuilder<JournalEntryCollection, JournalEntryCollection,
QAfterWhereClause>
authorIdDateEpochEqualTo(String authorId, int dateEpoch) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IndexWhereClause.equalTo(
indexName: r'authorId_dateEpoch',
value: [authorId, dateEpoch],
));
});
}
QueryBuilder<JournalEntryCollection, JournalEntryCollection,
QAfterWhereClause>
authorIdEqualToDateEpochNotEqualTo(String authorId, int dateEpoch) {
return QueryBuilder.apply(this, (query) {
if (query.whereSort == Sort.asc) {
return query
.addWhereClause(IndexWhereClause.between(
indexName: r'authorId_dateEpoch',
lower: [authorId],
upper: [authorId, dateEpoch],
includeUpper: false,
))
.addWhereClause(IndexWhereClause.between(
indexName: r'authorId_dateEpoch',
lower: [authorId, dateEpoch],
includeLower: false,
upper: [authorId],
));
} else {
return query
.addWhereClause(IndexWhereClause.between(
indexName: r'authorId_dateEpoch',
lower: [authorId, dateEpoch],
includeLower: false,
upper: [authorId],
))
.addWhereClause(IndexWhereClause.between(
indexName: r'authorId_dateEpoch',
lower: [authorId],
upper: [authorId, dateEpoch],
includeUpper: false,
));
}
});
}
QueryBuilder<JournalEntryCollection, JournalEntryCollection,
QAfterWhereClause> authorIdEqualToDateEpochGreaterThan(
String authorId,
int dateEpoch, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IndexWhereClause.between(
indexName: r'authorId_dateEpoch',
lower: [authorId, dateEpoch],
includeLower: include,
upper: [authorId],
));
});
}
QueryBuilder<JournalEntryCollection, JournalEntryCollection,
QAfterWhereClause> authorIdEqualToDateEpochLessThan(
String authorId,
int dateEpoch, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IndexWhereClause.between(
indexName: r'authorId_dateEpoch',
lower: [authorId],
upper: [authorId, dateEpoch],
includeUpper: include,
));
});
}
QueryBuilder<JournalEntryCollection, JournalEntryCollection,
QAfterWhereClause> authorIdEqualToDateEpochBetween(
String authorId,
int lowerDateEpoch,
int upperDateEpoch, {
bool includeLower = true,
bool includeUpper = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IndexWhereClause.between(
indexName: r'authorId_dateEpoch',
lower: [authorId, lowerDateEpoch],
includeLower: includeLower,
upper: [authorId, upperDateEpoch],
includeUpper: includeUpper,
));
});
}
QueryBuilder<JournalEntryCollection, JournalEntryCollection,
QAfterWhereClause> dateEpochEqualTo(int dateEpoch) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IndexWhereClause.equalTo(
indexName: r'dateEpoch',
value: [dateEpoch],
));
});
}
QueryBuilder<JournalEntryCollection, JournalEntryCollection,
QAfterWhereClause> dateEpochNotEqualTo(int dateEpoch) {
return QueryBuilder.apply(this, (query) {
if (query.whereSort == Sort.asc) {
return query
.addWhereClause(IndexWhereClause.between(
indexName: r'dateEpoch',
lower: [],
upper: [dateEpoch],
includeUpper: false,
))
.addWhereClause(IndexWhereClause.between(
indexName: r'dateEpoch',
lower: [dateEpoch],
includeLower: false,
upper: [],
));
} else {
return query
.addWhereClause(IndexWhereClause.between(
indexName: r'dateEpoch',
lower: [dateEpoch],
includeLower: false,
upper: [],
))
.addWhereClause(IndexWhereClause.between(
indexName: r'dateEpoch',
lower: [],
upper: [dateEpoch],
includeUpper: false,
));
}
});
}
QueryBuilder<JournalEntryCollection, JournalEntryCollection,
QAfterWhereClause> dateEpochGreaterThan(
int dateEpoch, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IndexWhereClause.between(
indexName: r'dateEpoch',
lower: [dateEpoch],
includeLower: include,
upper: [],
));
});
}
QueryBuilder<JournalEntryCollection, JournalEntryCollection,
QAfterWhereClause> dateEpochLessThan(
int dateEpoch, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IndexWhereClause.between(
indexName: r'dateEpoch',
lower: [],
upper: [dateEpoch],
includeUpper: include,
));
});
}
QueryBuilder<JournalEntryCollection, JournalEntryCollection,
QAfterWhereClause> dateEpochBetween(
int lowerDateEpoch,
int upperDateEpoch, {
bool includeLower = true,
bool includeUpper = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IndexWhereClause.between(
indexName: r'dateEpoch',
lower: [lowerDateEpoch],
includeLower: includeLower,
upper: [upperDateEpoch],
includeUpper: includeUpper,
));
});
}
}
extension JournalEntryCollectionQueryFilter on QueryBuilder<

View File

@@ -16,7 +16,7 @@ extension GetPendingOperationCollectionCollection on Isar {
const PendingOperationCollectionSchema = CollectionSchema(
name: r'PendingOperationCollection',
id: -1003,
id: -6885010264946527864,
properties: {
r'createdAtEpoch': PropertySchema(
id: 0,
@@ -61,7 +61,7 @@ const PendingOperationCollectionSchema = CollectionSchema(
idName: r'isarId',
indexes: {
r'id': IndexSchema(
id: -2003,
id: -3268401673993471357,
name: r'id',
unique: false,
replace: false,

View File

@@ -3,8 +3,6 @@
// 根据平台创建对应的 SecureTokenStore 实现。
// 运行时判断 kIsWeb避免 Web 编译时加载 flutter_secure_storage。
import 'package:flutter/foundation.dart' show kIsWeb;
import 'secure_token_store.dart';
import 'secure_token_store_web.dart';

View File

@@ -11,7 +11,7 @@ import 'dart:convert';
import 'package:isar/isar.dart';
import '../local/isar_database.dart';
import '../local/isar_database_native.dart';
import '../local/collections/journal_entry_collection.dart';
import '../local/collections/journal_element_collection.dart';
import '../models/journal_entry.dart';
@@ -20,7 +20,7 @@ import 'journal_repository.dart';
/// Isar 本地日记仓库 — JournalRepository 的 Isar 实现
class IsarJournalRepository implements JournalRepository {
Isar get _isar => IsarDatabase.instance!;
Isar get _isar => IsarDatabase.instance;
// ============================================================
// 日记 CRUD

View File

@@ -20,7 +20,7 @@ import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:isar/isar.dart';
import '../local/isar_database.dart';
import '../local/isar_database_native.dart';
import '../local/collections/pending_operation_collection.dart';
import '../remote/api_client.dart';
@@ -312,7 +312,7 @@ class SyncEngine {
/// 在 app 退出、isolate 暂停、或同步完成后调用。
Future<void> persistPendingQueue() async {
if (!IsarDatabase.isAvailable) return;
final isar = IsarDatabase.instance!;
final isar = IsarDatabase.instance;
final ops = snapshot;
await isar.writeTxn(() async {
@@ -333,7 +333,7 @@ class SyncEngine {
/// Web 平台上 Isar 不可用,跳过恢复。
Future<void> restorePendingQueue() async {
if (!IsarDatabase.isAvailable) return;
final isar = IsarDatabase.instance!;
final isar = IsarDatabase.instance;
final persisted = await isar.pendingOperationCollections
.where()
.anyIsarId()