77 Commits

Author SHA1 Message Date
iven
7af7cd64e6 feat(app): E.1 内测 APK 构建配置 — Android 品牌化 + 签名 + ProGuard
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
- 更新应用名称为「暖记」(AndroidManifest + strings.xml)
- 添加必要权限: INTERNET, CAMERA, READ_MEDIA_IMAGES, READ_EXTERNAL_STORAGE
- 生成 release 签名密钥 (RSA 2048, 10000 天有效期)
- 配置 ProGuard/R8 代码混淆 + 资源压缩
- 品牌化启动页: 奶油白背景 + 珊瑚色圆形「暖」字 logo
- 品牌化应用图标: 各密度 mipmap (mdpi~xxxhdpi)
- 添加阿里云 Maven 镜像加速依赖下载
- AGP 9.x 兼容: 自动为旧 Flutter 插件注入 namespace
- Gradle 性能优化: 并行编译 + 构建缓存
2026-06-07 20:17:19 +08:00
iven
ec8a04c80a feat(app): D.3 中等优先级 UX 改进 — 保存指示器 + 触摸目标 + 主题持久化
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
D.3.2 三态保存指示器:
- 未保存 (灰色) → 保存中 (琥珀色脉冲点) → 已保存 (绿色点)
- _PulsingDot 动画组件,800ms 呼吸效果
- 点击'完成'时显示保存中状态

D.3.3 工具栏触摸目标:
- BoxConstraints 36x36 → 44x44,符合 WCAG 标准

D.3.4 主题偏好持久化:
- SettingsBloc 接受 SharedPreferences,保存/恢复 themeMode
- NuanjiApp 改为 StatefulWidget,异步初始化 SharedPreferences
- 启动时显示 loading,初始化完成后渲染 app
2026-06-07 13:50:34 +08:00
iven
750605e479 feat(app): 全局离线提示横幅 — 网络不可用时显示黄色警告
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
- 新增 OfflineBanner widget: 监听 connectivity_plus 自动显示/隐藏
- AnimatedCrossFade 滑入滑出动画 + warmCurve 弹性曲线
- 黄色警告条: wifi_off 图标 + '网络不可用,部分功能受限'
- 嵌入 ResponsiveScaffold 的 body 上方 (手机/平板/桌面三端)
- 只在离线时显示,恢复网络后自动消失
2026-06-07 13:44:40 +08:00
iven
346c751cbb refactor(app): 迁移 4 个页面到共享 EmptyStateWidget + ErrorStateWidget
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
迁移统计:
- discover_page: _buildError → ErrorStateWidget, _buildEmptyHint → EmptyStateWidget
- sticker_library_page: 错误 + 空列表 → 共享组件
- class_page: 错误/班级列表空/日记墙空/话题空 → 共享组件 (4 处)
- calendar_page: CalendarError → ErrorStateWidget

统一体验: 所有页面空状态使用一致的 icon + title + subtitle + CTA 布局
2026-06-07 13:42:56 +08:00
iven
2f96f9a4f4 feat(app): 编辑器未保存确认 + 日历今天按钮
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
D.2.3 编辑器未保存确认:
- _handleBack 检查 state.isDirty,有未保存修改时弹出确认对话框
- 对话框: '放弃编辑?' / '你有未保存的修改' / [继续编辑] [放弃]

D.2.4 日历今天按钮:
- _MonthNavigator 新增 onToday 回调
- 不在当前月时显示 '今天' 文字按钮,点击跳回当月
- _isCurrentMonth 辅助判断
2026-06-07 13:38:34 +08:00
iven
f64355946c feat(app): 共享 UI 组件 + 4 个关键 UX bug 修复
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
Phase 0 — 共享组件:
- EmptyStateWidget: 统一空状态 (icon + title + subtitle + CTA)
- ErrorStateWidget: 统一错误状态 (message + retry)
- SkeletonBox + SkeletonList: 统一骨架屏加载 (shimmer 动画)

Phase 1 — Bug 修复:
- 班级评论按 journalId 过滤,避免显示在错误日记卡片下
- moodCellColors key 修正: love/tired → angry/thinking
- 日历非 CalendarLoaded 状态改为加载指示器 (不再 SizedBox.shrink)
- 贴纸数统计改为 '--' 占位 (之前错误显示日记总数)
2026-06-07 13:36:10 +08:00
iven
225af89e41 fix(app): Token 刷新彻底失败时通知 AuthBloc 派发 AuthExpired
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
审计 9a-AUTH-01 修复:
- ApiClient 新增 onAuthFailed 回调,在 401 刷新失败后触发
- app.dart 注册回调:派发 AuthExpired → GoRouter 重定向到登录页
- 之前刷新失败只清除内存 token,用户停留在死页面
- 现在 401 → 尝试刷新 → 失败 → AuthExpired → 自动跳转登录
2026-06-07 12:59:12 +08:00
iven
3c3d70c751 fix(app): 日历页切换月份后保留选中日期的日记列表
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
2026-06-07 10:44:55 +08:00
iven
41ef28f20b test(app): CalendarBloc 新增 31 个单元测试 2026-06-07 10:44:15 +08:00
iven
d67eedf7de feat(app): 多页面动态化 — 搜索/资料/教师/贴纸库/模板/日历
- SearchPage: 热搜词从日记标签频率动态生成 + 模板搜索网格
- ProfilePage: 成就徽章从 AchievementBloc 动态加载 + 头像首字母
- TeacherPage: 班级码改为对话框展示 (班级名+码+人数)
- StickerLibraryPage: 分类从 API 动态合并 + 精选包卡片动态化
- TemplateGalleryPage: 适配动态数据
- ClassPage: 微调
- HomePage: 路由适配
- CalendarBloc: 新增测试
- AppRouter: 路由更新
2026-06-07 10:44:04 +08:00
iven
a05374e8d1 feat(app): 编辑器增强 — 查看模式 + 图层排序 + 标签/贴纸动态化
- EditorPage 新增查看模式: 打开已保存日记默认只读,编辑按钮切换
- EditorBloc 新增 ElementLayerChanged 事件,支持置顶/置底图层排序
- DraggableElement 添加图层控制按钮 (置顶/置底/删除)
- TagPanel 标签建议改为从日记历史动态生成 (Top 10 频率)
- StickerPickerSheet 重构,预留 API 扩展点
2026-06-07 10:43:37 +08:00
iven
a5d2b0409f feat(app): 发现页动态化 — DiscoverBloc + API 驱动 + 下拉刷新
- 新增 DiscoverBloc (LoadData/Refresh) + DiscoverModels 4 个数据类
- DiscoverPage 改为 BlocBuilder 驱动: loading/loaded/error/empty 四态
- 替换全部硬编码占位数据为 API 实时数据
- 添加 RefreshIndicator 下拉刷新
- 离线异常时保留已有数据,友好错误提示
2026-06-07 10:43:23 +08:00
iven
4cb91f3ac9 fix(app): 修复打开已保存日记时笔迹不可见 — syncStrokes 后缺少 setState
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
根因:_syncCacheAfterBuild 调用 _cache.syncStrokes() 后没有触发
setState,导致 CachedStrokesPainter 不知道缓存已更新,不执行重绘。

对比正常绘画流程(addStroke 后有 setState),加载笔迹流程遗漏了
重绘触发。添加 .then(() => setState) 后,合成图重建完成即刷新画布。
2026-06-04 23:12:51 +08:00
iven
bb388ed8ff fix(app): 日记可见性修复 — 私密日记仅本地 + Web 端 ID 修复 + 分享按钮
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
问题修复:
1. Web端保存的日记看不到:createJournal 返回值未捕获,server ID 丢失导致
   后续元素保存用错 ID。现在使用 saved.id 贯穿全部操作。
2. 管理端看不到新建日记:后端 list_journals 添加 is_private 过滤,admin/teacher
   查看他人日记时排除私密日记。
3. RemoteJournalRepository 添加 onJournalChanged 变更通知流,HomeBloc 可自动刷新。
4. SyncEngine(native + web)enqueue 添加 is_private 防御性检查,私密日记不入队。
5. 编辑器 _persistState 条件入队:仅非私密日记同步到后端。
6. 分享流程改造:首次从私密变为公开时入队 create 操作上传。
7. 日记卡片添加可见性标签(仅自己可见/班级可见/公开),私密日记可点击分享。
8. 首页 _sharePrivateJournal 弹出 ShareBottomSheet 主动分享。
2026-06-04 12:03:24 +08:00
iven
c441aa4e34 fix(app): RemoteJournalRepository 创建日记 date 格式修复 — ISO 8601 → NaiveDate
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
根因: JournalEntry.toJson() 发送 '2026-06-04T12:00:00.123456',
后端 CreateJournalReq.date 是 chrono::NaiveDate,只接受 '2026-06-04'。
反序列化失败导致创建日记被拒绝,前端静默吞掉错误。

修复: createJournal 发送前将 date 截取为 YYYY-MM-DD 格式。
2026-06-04 10:47:14 +08:00
iven
e635557e67 fix(app): 修复 RemoteJournalRepository API 响应解析 — 分页信封嵌套
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
根因: 后端列表接口返回 { success, data: { data: [...], total, ... }, message }
但前端直接 body['data'] as List,类型不匹配导致 TypeError 被静默吞掉。

修复: getJournals 和 getJournalCount 正确解析分页信封
body['data']['data'] 获取列表, body['data']['total'] 获取总数。
2026-06-04 09:35:54 +08:00
iven
b72009718f fix(app): 日记保存逻辑修复 — EditorPage 改为 StatefulWidget + 更新合并编辑器状态
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
根因分析:
1. EditorPage 是 StatelessWidget,savedJournalId 作为 build() 局部变量
   每次重建都重置为 null,导致每次自动保存都走新建而非更新分支
2. 更新分支直接 repo.updateJournal(existing),没有把编辑器当前
   状态(标题/心情/标签)合并到已有日记中

修复:
- EditorPage 改为 StatefulWidget,_savedJournalId 存储在 State 中
- 更新分支用 existing.copyWith() 合并编辑器当前状态后保存
2026-06-04 00:13:51 +08:00
iven
9fce34f4ef fix(app): 修复 4 个 Flutter 交互问题
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
1. 首页数据不刷新 — JournalRepository 添加 onJournalChanged
   Stream 变更通知,HomeBloc 订阅后自动刷新
2. 画笔再次点击不弹出面板 — 添加 ToolReactivated 事件,
   工具栏检测已激活工具时发出重新激活信号
3. 钢笔铅笔效果一样 — 调整 perfect_freehand 参数
   (pen: size 10/smooth 0.65, pencil: size 3/smooth 0.35)
4. 橡皮擦不生效 — ActiveStrokePainter 橡皮擦模式绘制
   半透明灰色反馈,笔画完成后 setState 触发 Layer 1 重绘
5. 贴纸文字无法缩放 — DraggableElement 用 Scale 手势
   替换 Pan 手势,支持双指缩放和旋转
2026-06-04 00:05:22 +08:00
iven
988ee7335a feat(app): 内容安全词库 + 过滤服务 + 分享前检查 — 28 个测试全覆盖
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
新增文件:
- sensitive_words.dart — 8 分类 ~200 条敏感词 + 谐音/形近/数字变体映射
- content_filter_service.dart — 精确匹配 + 变体匹配 + 文本预处理(去零宽/空格/符号)
- content_filter_service_test.dart — 28 个测试(8分类精确/安全内容/预处理/变体/边界/词库完整性)

修改:
- share_bottom_sheet.dart — 分享到班级前调用 ContentFilterService,
  有敏感词时弹出警告对话框(返回修改/仍然分享),新增 contentText 参数
2026-06-03 19:40:13 +08:00
iven
9c92cba87f test(app): ClassBloc + SearchBloc 单元测试 — 33 个测试全覆盖
ClassBloc (13 tests):
- 班级列表加载(成功/失败/loading 状态)
- 选中班级详情 + 子事件触发
- 成员/日记墙/主题/评语加载
- 创建班级 + 错误处理
- 加入班级 + 列表刷新
- 布置主题
- sharedToClass 过滤逻辑

SearchBloc (20 tests):
- 关键词搜索(标题/摘要/标签匹配、大小写不敏感)
- 心情筛选(null/有值/失败)
- 标签搜索
- 搜索历史(记录/去重)
- 清除搜索 + Tab 切换
- hasActiveFilter 属性
2026-06-03 19:03:29 +08:00
iven
f6d394afb6 test(app): 手写引擎 Canvas 集成测试 — 55 个测试全覆盖
4 个测试文件:
- stroke_model_test.dart (12 tests) — StrokePoint/Stroke 序列化、不可变性、默认值
- stroke_renderer_test.dart (19 tests) — parseHexColor/pointsToOutline/buildStrokePath/createPaintForStroke
- stroke_cache_test.dart (15 tests) — StrokeRasterCache 添加/同步/清除/尺寸变化/边界条件
- handwriting_canvas_test.dart (9 tests) — Widget 渲染结构、手势回调、去抖、预加载、连续笔画
2026-06-03 18:57:41 +08:00
iven
4cd381295a fix(app): Flutter Web 开发模式默认连接 localhost:3000 API
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
- app.dart: kDebugMode 下使用 AppConfig.dev (localhost:3000)
- dev.mjs: flutter run 传入 --dart-define API_BASE_URL/SSE_BASE_URL
2026-06-03 17:50:55 +08:00
iven
367f21de08 feat(app): 统一同步协议 — SyncModels + ApiClient.sync + SyncEngine.tryBatchSync
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
Flutter ↔ Rust 同步协议对齐:
- 新增 sync_models.dart: SyncReq/SyncResp/SyncChange/ConflictInfo
  与 Rust dto.rs 一一对应 (CreateJournal/UpdateJournal/DeleteJournal)
- ApiClient.sync(): 调用 POST /diary/sync 批量同步端点
- SyncEngine.tryBatchSync(): PendingOperation → SyncChange 批量提交
  成功清空队列,冲突保留待用户处理

保留原有逐个同步 trySync() 作为降级方案
后端 509/509 测试通过, Flutter analyze 0 error
2026-06-03 17:20:51 +08:00
iven
38592d61ce 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
2026-06-03 17:06:03 +08:00
iven
e8df3a9562 fix(app): 修复登录页 Logo 和文字未居中 — Stack alignment + Column mainAxisSize
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
2026-06-03 16:36:20 +08:00
iven
32a91551c4 perf(app): Phase 2 前端性能优化 5 项 — 8b-D01/D02/D03/M02/N01
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
- 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 重新生成
2026-06-03 16:05:11 +08:00
iven
99db8e5cb0 fix(app): 家长同意验证流程 — PIPL 第28条合规
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
- 新增 ParentalConsentPage: 显示隐私政策要点 + 双重确认复选框
- 角色选择流程: 学生 → 家长同意确认 → 班级码输入
- Authenticated 状态: 添加 needsParentalConsent/parentalConsentAt/selectedRole
- ParentalConsentAccepted 事件: 记录同意时间戳
- 路由守卫: 注册 /parental-consent 路径和重定向逻辑
- 非学生角色(老师/家长/独立用户)不需要经过同意流程

审计 ID: S-03
2026-06-03 10:25:23 +08:00
iven
a34c9fd176 fix(app): 强制 HTTPS — Android 网络安全配置 + 生产默认 HTTPS
- Android: 添加 network_security_config.xml,默认禁止明文流量
- Android: 仅允许 localhost/127.0.0.1/10.0.2.2 明文(开发调试)
- Android: 更新 AndroidManifest 引用网络安全配置
- ApiClient: 默认 URL 改为 https://api.nuanji.app/api/v1
- AppConfig: fromEnvironment 默认值改为 HTTPS 生产地址
- AppConfig: dev 常量保留 localhost(仅用于本地开发)
- iOS: ATS 默认已强制 HTTPS,无需修改

审计 ID: 6b-C01
2026-06-03 10:13:20 +08:00
iven
45949e3ed0 fix(app): Token 自动刷新拦截器 — 401 时自动刷新 + 重试原请求
- ApiClient: 添加 onRefreshToken 回调,401 时自动调用刷新
- ApiClient: 并发保护(_isRefreshing),防止多个 401 触发多次刷新
- ApiClient: 跳过 /auth/refresh 自身的 401(避免无限循环)
- ApiClient: 刷新成功后自动重试原始请求
- AuthRepository: 注册 _handleAutoRefresh 回调
- 使用回调模式避免 ApiClient ↔ AuthRepository 循环依赖

审计 ID: 9a-AUTH-01
2026-06-03 10:07:33 +08:00
iven
11d0971a67 feat(app): pnpm 一键启动 + Flutter Web 编译修复
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
1. 新增 pnpm start:dev / pnpm start:stop 命令
   - scripts/dev.mjs: 跨平台启动脚本(后端+管理端+学生端)
   - scripts/stop.mjs: 端口清理停止脚本
   - 根 package.json 定义 pnpm 脚本

2. 修复 Flutter Web 编译(Isar 3.x + flutter_secure_storage 不兼容)
   - isar_database: 条件导出,Web 用空 stub
   - isar_journal_repository: 条件导出,Web 用空 stub
   - sync_engine: 条件导出,Web 用内存队列(无 Isar 持久化)
   - 移除 flutter_secure_storage(v9 web 插件用 dart:html)
   - 新增 SecureTokenStore 接口 + shared_preferences 实现
   - auth_repository 改用 SecureTokenStore 接口
2026-06-03 09:50:19 +08:00
iven
9ce300ddb9 fix(app): 修复笔画缓存 use-after-dispose — 移除增量合成时的提前 dispose
- _compositeIncremental 中不再 dispose strokeImage,因为 _cache 持有同一引用
- 提前 dispose 导致 syncStrokes/clear/dispose 时 double-dispose(use-after-free)
- 单笔画 image 生命周期由缓存统一管理:移除/清除/销毁时释放
- 更新 _rebuildComposite 注释,移除过时说明

审计 ID: 8b-R01
2026-06-03 01:06:34 +08:00
iven
d482497e49 fix(app): 修复 smoke test — 改为验证主题构建,避免 Isar 依赖
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
2026-06-03 00:00:15 +08:00
iven
f0741450bc feat(app): 家长端数据导出 — 添加 JSON 文件下载 + 预览
- 新建 file_download.dart 跨平台下载工具(conditional import)
- download_impl_web.dart: Web 平台通过 html.AnchorElement + Blob 下载
- download_impl.dart: 非 Web 平台 stub(Phase 2 扩展 path_provider)
- _ExportDataView: 添加下载按钮 + JSON 折叠预览 + PIPL 提示
- 移除 'Phase 1 预览' 占位文案,替换为完整下载功能
2026-06-02 23:36:35 +08:00
iven
c9a69d0be1 feat(app): 添加评论列表展示组件 — FutureBuilder 轮询模式 2026-06-02 23:26:54 +08:00
iven
9e53ca8555 feat(app): EditorPage 顶栏添加评语入口 — 仅已有日记显示
- 添加评语图标按钮(仅 journalId != null 时显示)
- 实现 _showComments 方法打开 CommentListSheet
- 补充 api_client + comment_list_sheet imports
2026-06-02 23:26:24 +08:00
iven
6c9a38b27b feat(app): 添加 EditorBloc.LoadJournal event — 加载已有日记数据
- LoadJournal event: 原子加载 title/mood/tags/strokes/elements/lastSavedAt
- _onLoadJournal handler: 不触发 auto-save (isDirty=false)
- 单元测试: 验证 LoadJournal 正确还原所有状态字段
- mood_bloc: linter 补充 foundation.dart import
2026-06-02 23:23:17 +08:00
iven
e57c3427a4 fix(app): 18 处 catch(e) 添加 debugPrint 异常日志
- parent_bloc: 6 处 (LoadChildren/BindChild/ViewJournals/ExportData/DeleteData/UnbindChild)
- search_bloc: 3 处 (SearchByMood/SearchByTag/SearchByKeyword)
- achievement_bloc: 1 处 (_fetchAchievements)
- sticker_bloc: 2 处 (_fetchPacks/fetchStickersInPack)
- template_bloc: 1 处 (_fetchTemplates)
- mood_bloc: 1 处 (_loadStats)
- home_bloc: 1 处 (_onLoadData)
- calendar_bloc: 1 处 (_onMonthChanged)
- sync_engine: 1 处 (trySync)
- weekly_page: 已有 debugPrint,无需修改
2026-06-02 23:21:16 +08:00
iven
c92ead60e3 feat(app): EditorPage 加载已有日记 — 替换为 LoadJournal 原子事件
- _loadExistingJournal 改用单一 LoadJournal event 替代多个细粒度事件
- 添加 _titleController 同步,确保 LoadJournal 后标题输入框正确显示
- 不触发 auto-save (isDirty=false),因为这是加载而非用户编辑
2026-06-02 23:16:58 +08:00
iven
85d6781372 fix: Phase 1.3 完善修复 — 管理端对接 + HMS清理 + 编辑器加载
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
- feat(web): ClassList.tsx 对接 update/deactivate/reset-code API
  - 编辑班级: PUT /diary/classes/:id
  - 停用班级: PATCH /diary/classes/:id/deactivate (Popconfirm 确认)
  - 重置班级码: POST /diary/classes/:id/reset-code (Popconfirm 确认)
  - 数据源改用 listAll() 获取所有班级
- fix(web): JournalList.tsx 班级筛选改用 classApi.listAll()
- fix(app): EditorPage 加载已有日记数据 (journalId 非空时)
  - 从 Isar 恢复笔画/元素/标签/心情/标题
  - _EditorView 改为 StatefulWidget + initState 加载
- chore(web): HMS 遗留代码清理
  - 删除 api/copilot.ts, healthFixtures.ts, healthHandlers.ts
  - AuditLogViewer 资源类型替换为日记模块类型
  - auth.test.ts / renderWithProviders 权限码 health.* → diary.*
- docs: 确认 M6 NotificationService 为误报 (已在 3 处调用)
2026-06-02 22:54:09 +08:00
iven
49d4aa36a7 fix(app): Phase 1.1 紧急修复 — SyncEngine 接入 + authorId + catch 异常处理
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
- feat(sync): SyncEngine 接入 EditorPage, 保存时 enqueue + 网络恢复自动 trySync
- fix(editor): authorId 从 AuthBloc 获取, 替代硬编码 'local'
- fix(bloc): class_bloc/calendar/profile/parent catch(_).全部改为 debugPrint
- feat(editor): 编辑器工具栏拆分 (brush_panel/tag_panel/text_format_bar/dot_grid_painter)
- feat(editor): EditorBloc 扩展 + EditorPage 增强
- feat(search): SearchBloc 扩展搜索功能
- feat(home): HomeBloc/HomePage 增强
- feat(auth): LoginPage 增强
- feat(templates): TemplateGalleryPage 重构
- fix(web): 管理端班级/日记页面修复
- fix(server): comment_service + theme_handler 修复
- docs: 添加全链路审计报告和验证截图
2026-06-02 21:21:43 +08:00
iven
7e928ae1e1 fix(app): 修复 P2~P4 共 10 项前端问题
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
P2 必须修复:
- 教师布置主题 classId 从硬编码改为班级下拉选择器
- 班级日记墙使用服务端 classId 过滤替代前端过滤
- Profile 统计栏接入 JournalRepository 真实数据
- WeeklyPage 从全硬编码改为 JournalRepository 数据驱动

P3 建议改进:
- 提取 mood_utils.dart 公共函数,消除 4 处重复定义
- 贴纸库搜索框连接 StickerBloc 按名称过滤

P4 细节打磨:
- 家长页多孩子时显示 DropdownButton 选择器
- 搜索结果日记卡片点击跳转 /editor?id=
- MonthlyPage 照片数量从 JournalElement 统计
- calendar_page/mood_page/search_page 统一使用 moodToEmoji/moodToLabel
2026-06-02 20:21:51 +08:00
iven
181bfb1f3e fix(app): 对齐 Open Design spec — 字体/Token/首页/Tab栏/路由/Discover页
针对 docs/opendesign/warm-notes-design-spec.md 全面审查的修复:

## 🔴 阻断级修复(商用合规)
- 下载真实 Quicksand/Nunito 字体文件(原 0 字节)
- 添加 OFL.txt 许可证文件,履行 SIL Open Font License 分发义务

## 🟠 设计 Token 偏差
- AppRadius: 删除非规范的 xs=8px,所有引用迁移至 sm=10px
- AppColors.moodColors: 对齐 spec §3.6
  - happy #FFD93D → secondary #81B29A
  - calm #81B29A → tertiary #F2CC8F
  - sad #7B9CC4 → #5B7DB1
  - thinking #B8A9C9(淡紫,spec 无)→ #8B7E74
- AppShadows: blurRadius/alpha 精确对齐 spec §1 (12/20/32 + 0.06/0.08/0.12)
- DesignTokens: 补 spacing40 + 新增 safe-top/safe-bottom/tab-height/touch-min 常量

## 🟠 首页 §3.4 完全重构
- 新增问候语头部(xx好,小暖 + accent 色高亮名字)
- 新增 streak-badge pill 徽章(tertiary-soft + #B8860B 暖金)
- 心情选择器卡片背景从 primaryContainer 改为 surface(spec 规定 #FFFFFF)
- 心情卡片圆角 lg(22) → md(16) 对齐 spec
- 新增 today-card 渐变卡片 + 浮动右下圆形写按钮
- 新增 quick-stats 三栏统计(本月日记/连续天数/总日记数)
- 移除 AppBar 多余的贴纸/模板按钮,搜索按钮改路由到 /search
- HomeBloc 扩展 monthCount/totalCount 字段
- 日记卡片:72×72 预览图 + 标签摘要 + 心情圆点

## 🟠 路由 §3.12 + §3.13 拆分
- 新建 DiscoverPage (features/discover/views/discover_page.dart)
  - 搜索框(跳转 /search)
  - 每日推荐渐变卡片
  - 热门话题横向 chips(前 3 个 accent 高亮)
  - 精选模板 2 列网格
  - 达人日记列表
- /discover 路由从指向 SearchPage 改为 DiscoverPage
- 新增 /search 路由(全屏无 Tab)指向 SearchPage

## 🟠 Tab 栏 §2.2 重构
- 高度从 64px 改为 56+bottomPadding(含 safe-bottom,约 90px)
- 中心按钮从 CircularNotchedRectangle 凹槽改为 margin-top:-16px 凸起
- FAB 尺寸从默认改为 48×48 spec 规格
- FAB 图标从 edit_rounded 改为 add_rounded(spec §2.2)
- 删除未使用的 _navItems 旧常量

## 🟡 登录页圆角统一
- 移除 3 处 InputBorder 显式 mdBorder(16px) 覆盖
- 全局主题 smBorder(10px) 生效,对齐 spec
- 提交按钮圆角改为 pill(spec §2.6 Primary 按钮)

## 验证
- flutter analyze: 0 errors (剩余 40 个 warning/info 全为预存)
- flutter test: 84/85 通过(widget smoke test 预存失败,与本次无关)
2026-06-02 09:11:46 +08:00
iven
b320641d9c fix(app): 全链路验证修复 — 编译错误/CORS/迁移/启动脚本
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
前端修复:
- calendar_page: 移除不存在的 JournalEntry.content getter
- responsive_scaffold: 移除不存在的 notchThickness 参数
- splash_page: SingleTickerProvider → TickerProvider (多 AnimationController)
- profile_page: UserRoleType.name → .code (修复运行时崩溃)
- 导入缺失的 user.dart

后端修复:
- class_service: generate_class_code 取 UUID 后6位(随机部分)避免碰撞
- diary_role_seed: 移除不存在的 id 列,使用复合主键 ON CONFLICT

基础设施:
- config/default.toml: CORS 改为通配符(开发模式)
- scripts/dev.sh: 统一启动脚本(自动清理端口)
- docs/opendesign/: Open Design 设计规格 HTML 原型稿

验证结果: flutter analyze 0 error, cargo test 77/77 通过, 17个页面全部渲染正常
2026-06-02 01:03:58 +08:00
iven
749ef55b89 feat: Week 4 收尾 + 架构治理 — 搜索/家长中心/Feature Flag/Docker/环境配置
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
架构治理:
- 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
2026-06-01 23:53:34 +08:00
iven
ffde0c9e77 feat(test): Week 3 质量保障体系 — 55 新增测试 + CI/CD 流水线
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
前端第二批测试 (42 用例):
- AuthBloc: 16 用例 (启动恢复/登录/注册/角色选择/班级码/登出)
- HomeBloc: 8 用例 (数据加载/今日检测/心情统计/连续天数/离线容错)
- CalendarBloc: 10 用例 (月份切换/日期选择/视图模式/状态保持)
- MoodBloc: 8 用例 (统计加载/周期切换/API解析/错误处理)

后端 P0 单元测试 (13 用例):
- journal_service: 5 用例 (model_to_resp 转换/mood回退/weather回退/tags解析)
- sync_service: 8 用例 (冲突收集/DTO构造/序列化roundtrip/非冲突排除)

CI/CD:
- pr-check.yml: PR 触发 cargo fmt+check+clippy+test + flutter analyze+test
- main-merge.yml: main push 触发完整检查 + cargo audit 安全审计

测试统计: 前端 84 通过, 后端 73 通过 (全部通过)
2026-06-01 23:20:18 +08:00
iven
f0921d554c fix(app): 修复 ShareBottomSheet nullable 检查 2026-06-01 23:06:39 +08:00
iven
4c743e150e test(app): 前端第一批测试 — EditorBloc 19用例 + JournalElement 11用例 + InMemoryRepo 12用例
添加 mocktail 测试依赖 + 字体文件占位
总计 42 测试通过,覆盖工具/笔画/元素/撤销/重做/序列化/乐观锁/CRUD
2026-06-01 23:02:14 +08:00
iven
3eaf83c79a feat(app): 老师点评功能 — CommentCreate事件 + CommentBottomSheet + 日记墙点评按钮 2026-06-01 22:49:56 +08:00
iven
973bb56af6 feat(app): 编辑器完成按钮接入分享面板 — ShareBottomSheet + sharedToClass更新 2026-06-01 22:45:56 +08:00
iven
55285b57a7 feat(app): 班级码验证前后端联调 — AuthBloc接入API + 错误计数锁定UI 2026-06-01 22:42:33 +08:00