feat: Week 4 收尾 + 架构治理 — 搜索/家长中心/Feature Flag/Docker/环境配置
架构治理: - Feature Flag 落地: Cargo.toml [features] default=["diary"] + main.rs cfg 条件编译 - 环境配置统一: AppConfig 类 + --dart-define 注入 + SSE 端口 8080→3000 修复 搜索替代方案 (无 FTS): - SearchBloc + 标签/心情筛选接入后端 API - JournalRepository 扩展 mood/tag 筛选参数 - 搜索页 UI 接入实际数据(替换占位文本) 家长中心最小集 (PIPL 合规): - 后端: parent_service (绑定/查看/导出/删除/解绑) + parent_handler (6 个 API 端点) - 前端: ParentBloc + ParentPage 功能完整实现 - 绑定孩子、只读查看日记、导出数据、删除数据、解绑 Docker 部署: - verify.sh 健康检查脚本 (Axum/PG/Redis/OpenAPI 四项检查) 测试修复: - home_bloc_test / calendar_bloc_test 适配 JournalRepository 新参数 验证: flutter test 84/84 pass, cargo test 76/76 pass, cargo check pass
This commit is contained in:
@@ -32,6 +32,8 @@ class IsarJournalRepository implements JournalRepository {
|
||||
DateTime? dateTo,
|
||||
int? page,
|
||||
int? pageSize,
|
||||
String? mood,
|
||||
String? tag,
|
||||
}) async {
|
||||
var query = _isar.journalEntryCollections
|
||||
.where()
|
||||
@@ -46,6 +48,16 @@ class IsarJournalRepository implements JournalRepository {
|
||||
query = query.and().dateEpochLessThan(dateTo.millisecondsSinceEpoch);
|
||||
}
|
||||
|
||||
// 心情过滤
|
||||
if (mood != null) {
|
||||
query = query.and().moodEqualTo(mood);
|
||||
}
|
||||
|
||||
// 标签过滤:Isar tagsJson 字段存储 JSON 数组,用 contains 匹配
|
||||
if (tag != null) {
|
||||
query = query.and().tagsJsonContains(tag);
|
||||
}
|
||||
|
||||
// 按日期降序排列
|
||||
var results = await query
|
||||
.sortByDateEpochDesc()
|
||||
|
||||
@@ -15,12 +15,14 @@ import '../models/journal_element.dart';
|
||||
/// - [dateFrom]/[dateTo]: 日期范围过滤(闭区间)
|
||||
/// - [page]/[pageSize]: 分页参数,从 1 开始
|
||||
abstract class JournalRepository {
|
||||
/// 获取日记列表(支持日期范围过滤和分页)
|
||||
/// 获取日记列表(支持日期范围、心情、标签过滤和分页)
|
||||
Future<List<JournalEntry>> getJournals({
|
||||
DateTime? dateFrom,
|
||||
DateTime? dateTo,
|
||||
int? page,
|
||||
int? pageSize,
|
||||
String? mood,
|
||||
String? tag,
|
||||
});
|
||||
|
||||
/// 获取单篇日记(返回 null 表示不存在)
|
||||
@@ -62,6 +64,8 @@ class InMemoryJournalRepository implements JournalRepository {
|
||||
DateTime? dateTo,
|
||||
int? page,
|
||||
int? pageSize,
|
||||
String? mood,
|
||||
String? tag,
|
||||
}) async {
|
||||
var results = _journals.values.toList();
|
||||
|
||||
@@ -73,6 +77,16 @@ class InMemoryJournalRepository implements JournalRepository {
|
||||
results = results.where((j) => j.date.isBefore(dateTo)).toList();
|
||||
}
|
||||
|
||||
// 心情过滤
|
||||
if (mood != null) {
|
||||
results = results.where((j) => j.mood.value == mood).toList();
|
||||
}
|
||||
|
||||
// 标签过滤(日记 tags 列表包含指定标签)
|
||||
if (tag != null) {
|
||||
results = results.where((j) => j.tags.contains(tag)).toList();
|
||||
}
|
||||
|
||||
// 按日期降序排列(最新在前)
|
||||
results.sort((a, b) => b.date.compareTo(a.date));
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ class RemoteJournalRepository implements JournalRepository {
|
||||
DateTime? dateTo,
|
||||
int? page,
|
||||
int? pageSize,
|
||||
String? mood,
|
||||
String? tag,
|
||||
}) async {
|
||||
final queryParams = <String, dynamic>{};
|
||||
// 后端 NaiveDateTime 格式: "2026-06-01T00:00:00"(不带毫秒)
|
||||
@@ -30,6 +32,8 @@ class RemoteJournalRepository implements JournalRepository {
|
||||
}
|
||||
if (page != null) queryParams['page'] = page;
|
||||
if (pageSize != null) queryParams['page_size'] = pageSize;
|
||||
if (mood != null) queryParams['mood'] = mood;
|
||||
if (tag != null) queryParams['tag'] = tag;
|
||||
|
||||
final response = await _api.get('/diary/journals', queryParams: queryParams);
|
||||
final body = response.data as Map<String, dynamic>;
|
||||
|
||||
@@ -39,7 +39,7 @@ class SseNotificationService {
|
||||
|
||||
SseNotificationService({
|
||||
required String token,
|
||||
String baseUrl = 'http://localhost:8080/api/v1',
|
||||
String baseUrl = 'http://localhost:3000/api/v1',
|
||||
}) : _token = token,
|
||||
_baseUrl = baseUrl;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user