Files
nj/wiki/erp-diary.md
iven ab58186ab3 docs(wiki): 全量项目健康度评估 + 技术债全景更新
- 新增 project-health.md — 项目评分/技术债全景/风险矩阵/改进路线图
- 更新 index.md — 代码量分布表/新发现技术债统计/新增症状条目
- 更新 architecture.md — Feature Flag 未实现状态/超大文件发现
- 更新 frontend.md — 状态管理不统一/SSE 端口/测试缺失等 11 项问题
- 更新 erp-diary.md — 代码量分布参考/班级码硬编码问题

基于 4 代理并行深度分析: 后端 Rust 51,459 行 + 前端 Flutter 18,398 行
2026-06-01 18:33:38 +08:00

116 lines
4.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: erp-diary 后端模块
updated: 2026-06-01
status: active
tags: [rust, axum, seaorm, diary, api]
---
# erp-diary — 暖记后端业务模块
> 从 [[index]] 导航。关联: [[architecture]] [[data-layer]]
## 1. 设计决策
### Q: 为什么独立 erp-diary crate
基座解耦原则erp-diary 通过 Feature Flag (`cargo build --features diary`) 按需引入。基座 crate 不依赖业务模块,其他项目可复用基座而不引入日记功能。
### Q: DiaryError 设计?
15 种变体枚举NotFound / VersionConflict / Unauthorized / ContentUnsafe 等),实现 `Into<AppError>` 转换为 HTTP 状态码映射。集成测试验证每个错误码的正确 HTTP 响应。
### Q: 为什么内容安全过滤在 Service 层?
日记标题、文字元素的文本需要敏感词检查(含谐音/拼音变体)。放在 Service 层而非 Handler 层,确保即使有新的入口点(事件消费、管理 API也不会绕过检查。
## 2. 关键文件 + 数据流
### 模块结构
```
crates/erp-diary/src/
├── lib.rs (206 行) — DiaryModule 实现 + Feature Flag 注册
├── dto.rs (569 行) — 请求/响应 DTO + Validate 注解
├── error.rs (193 行) — DiaryError 15 种变体 → HTTP 状态码
├── event.rs (61 行) — 事件定义 (diary.created 等)
├── state.rs (13 行) — DiaryState (DiaryModule 专用状态)
├── entity/ (15 文件) — SeaORM Entity
├── service/ (10 文件) — 业务逻辑
└── handler/ (8 文件) — HTTP Handler + utoipa 注解
```
### Entity 清单 (15 个)
achievement, class_member, comment, handwriting_stroke, journal_element, journal_entry, parent_child_binding, school_class, sticker, sticker_pack, teacher_profile, template, topic_assignment, user_achievement, user_settings
### Service 清单 (10 个)
journal, class, comment, content_safety, achievement, mood_stats, notification, sticker, sync, topic
### API 端点
| 端点前缀 | Handler | 主要操作 |
|---------|---------|---------|
| `/api/v1/diary/journals` | journal_handler | CRUD + 列表过滤 |
| `/api/v1/diary/journals/:id/elements` | journal_handler (同) | 元素 CRUD |
| `/api/v1/diary/classes` | class_handler | 班级 CRUD + 班级码 |
| `/api/v1/diary/comments` | comment_handler | 评论 CRUD |
| `/api/v1/diary/topics` | topic_handler | 主题布置 |
| `/api/v1/diary/achievements` | achievement_handler | 成就系统 |
| `/api/v1/diary/stickers` | sticker_handler | 贴纸管理 |
| `/api/v1/diary/stats` | stats_handler | 心情/写作统计 |
| `/api/v1/diary/sync` | sync_handler | 增量同步 API |
### 集成契约
| 方向 | 模块 | 接口 | 触发时机 |
|------|------|------|---------|
| 提供 → | erp-server | `DiaryModule::routes()` | 启动 feature=diary |
| 调用 → | erp-core | `EventBus::publish()` | 日记创建/更新/删除 |
| 调用 → | erp-auth | `require_permission()` | 每个 handler 入口 |
| 调用 → | erp-message | 通知服务 | 评论/班级事件 |
## 3. 代码逻辑
### 不变量
**所有 Entity 含标准字段** — id / tenant_id / created_at / updated_at / created_by / updated_by / deleted_at / version
**软删除** — 查询带 `deleted_at IS NULL` 过滤,不做硬删除
**多租户隔离** — 中间件注入 tenant_idhandler 不从 API 路径获取
**权限守卫** — 每个 handler 方法第一行 `require_permission("diary.xxx")`
**输入验证** — DTO 使用 `#[derive(Validate)]` + handler 层调 `.validate()`
**内容安全** — 日记标题/文字通过 ContentSafetyService 检查
## 4. 活跃问题 + 陷阱
| 问题 | 级别 | 状态 | 说明 |
|------|------|------|------|
| Docker 部署未验证 | HIGH | 待做 | docker/ 配置完善但 Dockerfile 不存在 |
| CI/CD 未建立 | MEDIUM | 待做 | 无自动化构建/测试/部署 |
| 文件上传未实现 | MEDIUM | 待做 | 照片/贴纸文件上传参考健康模块 |
| 代码分布 | INFO | 参考 | service 层 51.7%、handler 20.1%、entity 15.6%、dto 11.1% |
| 班级码硬编码 | LOW | 待修 | 前端 teacher 模块班级码 'a1b2c3' 未接入后端 |
### 代码量参考
| 层 | 行数 | 占比 |
|---|---:|---:|
| entity/ | 796 | 15.6% |
| handler/ | 1,029 | 20.1% |
| service/ | 2,641 | 51.7% |
| dto.rs | 569 | 11.1% |
| 其他 | 473 | 9.3% |
| **合计** | **5,108** | **100%** |
## 5. 变更记录
| 日期 | 变更 |
|------|------|
| 2026-06-01 | 补充代码量分布、班级码硬编码问题 |
| 2026-06-01 | 初始创建 — Entity/Service/Handler 清单、API 端点、集成契约 |