Files
nj/crates/erp-diary/src/event.rs
iven 38592d61ce
Some checks failed
Main Merge / backend (push) Has been cancelled
Main Merge / frontend (push) Has been cancelled
refactor(diary): Phase 3 质量提升 — 201 状态码 + OpenAPI 文档 + DiaryEvent 类型安全
前端:
- 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

263 lines
8.2 KiB
Rust
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.
// erp-diary 事件定义
//
// DiaryEvent 是日记模块的领域事件枚举,提供类型安全的事件构建。
// 通过 `to_domain_event(tenant_id)` 转换为基座 DomainEvent 后发布到 EventBus。
//
// 使用方式Service 层):
// use crate::event::DiaryEvent;
// let evt = DiaryEvent::JournalCreated { journal_id, author_id, class_id };
// event_bus.publish(evt.to_domain_event(tenant_id), db).await;
use serde_json::json;
use uuid::Uuid;
use erp_core::events::DomainEvent;
/// 日记模块领域事件
#[derive(Debug, Clone)]
pub enum DiaryEvent {
/// 日记创建
JournalCreated {
journal_id: Uuid,
author_id: Uuid,
class_id: Option<Uuid>,
},
/// 日记更新
JournalUpdated {
journal_id: Uuid,
author_id: Uuid,
version: i32,
},
/// 日记删除
JournalDeleted {
journal_id: Uuid,
author_id: Uuid,
},
/// 日记分享到班级
JournalShared {
journal_id: Uuid,
author_id: Uuid,
class_id: Uuid,
},
/// 班级创建
ClassCreated {
class_id: Uuid,
teacher_id: Uuid,
},
/// 学生加入班级
StudentJoinedClass {
class_id: Uuid,
student_id: Uuid,
},
/// 老师布置主题
TopicAssigned {
topic_id: Uuid,
class_id: Uuid,
teacher_id: Uuid,
},
/// 老师点评
CommentCreated {
comment_id: Uuid,
journal_id: Uuid,
teacher_id: Uuid,
student_id: Uuid,
},
/// 家长绑定孩子
ParentBound {
parent_id: Uuid,
child_id: Uuid,
},
/// 成就解锁
AchievementUnlocked {
user_id: Uuid,
achievement_id: String,
},
}
impl DiaryEvent {
/// 返回事件类型字符串(用于 DomainEvent.event_type
pub fn event_type(&self) -> &'static str {
match self {
Self::JournalCreated { .. } => "diary.created",
Self::JournalUpdated { .. } => "diary.updated",
Self::JournalDeleted { .. } => "diary.deleted",
Self::JournalShared { .. } => "diary.shared",
Self::ClassCreated { .. } => "diary.class.created",
Self::StudentJoinedClass { .. } => "diary.class.student_joined",
Self::TopicAssigned { .. } => "diary.topic.assigned",
Self::CommentCreated { .. } => "diary.comment.created",
Self::ParentBound { .. } => "diary.parent.binding_confirmed",
Self::AchievementUnlocked { .. } => "diary.achievement.unlocked",
}
}
/// 返回事件 payloadJSON 格式)
pub fn payload(&self) -> serde_json::Value {
match self {
Self::JournalCreated {
journal_id,
author_id,
class_id,
} => json!({
"journal_id": journal_id,
"author_id": author_id,
"class_id": class_id,
}),
Self::JournalUpdated {
journal_id,
author_id,
version,
} => json!({
"journal_id": journal_id,
"author_id": author_id,
"version": version,
}),
Self::JournalDeleted {
journal_id,
author_id,
} => json!({
"journal_id": journal_id,
"author_id": author_id,
}),
Self::JournalShared {
journal_id,
author_id,
class_id,
} => json!({
"journal_id": journal_id,
"author_id": author_id,
"class_id": class_id,
}),
Self::ClassCreated {
class_id,
teacher_id,
} => json!({
"class_id": class_id,
"teacher_id": teacher_id,
}),
Self::StudentJoinedClass {
class_id,
student_id,
} => json!({
"class_id": class_id,
"student_id": student_id,
}),
Self::TopicAssigned {
topic_id,
class_id,
teacher_id,
} => json!({
"topic_id": topic_id,
"class_id": class_id,
"teacher_id": teacher_id,
}),
Self::CommentCreated {
comment_id,
journal_id,
teacher_id,
student_id,
} => json!({
"comment_id": comment_id,
"journal_id": journal_id,
"teacher_id": teacher_id,
"student_id": student_id,
}),
Self::ParentBound {
parent_id,
child_id,
} => json!({
"parent_id": parent_id,
"child_id": child_id,
}),
Self::AchievementUnlocked {
user_id,
achievement_id,
} => json!({
"user_id": user_id,
"achievement_id": achievement_id,
}),
}
}
/// 转换为基座 DomainEvent可直接发布到 EventBus
pub fn to_domain_event(&self, tenant_id: Uuid) -> DomainEvent {
DomainEvent::new(self.event_type(), tenant_id, self.payload())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn journal_created_event_type() {
let id = Uuid::now_v7();
let evt = DiaryEvent::JournalCreated {
journal_id: id,
author_id: id,
class_id: None,
};
assert_eq!(evt.event_type(), "diary.created");
assert_eq!(evt.payload()["journal_id"], id.to_string());
}
#[test]
fn class_created_event_type() {
let id = Uuid::now_v7();
let evt = DiaryEvent::ClassCreated {
class_id: id,
teacher_id: id,
};
assert_eq!(evt.event_type(), "diary.class.created");
}
#[test]
fn to_domain_event_preserves_fields() {
let tid = Uuid::now_v7();
let jid = Uuid::now_v7();
let aid = Uuid::now_v7();
let de = DiaryEvent::JournalCreated {
journal_id: jid,
author_id: aid,
class_id: Some(tid),
}
.to_domain_event(tid);
assert_eq!(de.event_type, "diary.created");
assert_eq!(de.tenant_id, tid);
assert_eq!(de.payload["journal_id"], jid.to_string());
assert_eq!(de.payload["author_id"], aid.to_string());
assert_eq!(de.payload["class_id"], tid.to_string());
}
#[test]
fn all_variants_have_correct_event_type() {
let id = Uuid::now_v7();
let variants: Vec<DiaryEvent> = vec![
DiaryEvent::JournalCreated { journal_id: id, author_id: id, class_id: None },
DiaryEvent::JournalUpdated { journal_id: id, author_id: id, version: 1 },
DiaryEvent::JournalDeleted { journal_id: id, author_id: id },
DiaryEvent::JournalShared { journal_id: id, author_id: id, class_id: id },
DiaryEvent::ClassCreated { class_id: id, teacher_id: id },
DiaryEvent::StudentJoinedClass { class_id: id, student_id: id },
DiaryEvent::TopicAssigned { topic_id: id, class_id: id, teacher_id: id },
DiaryEvent::CommentCreated { comment_id: id, journal_id: id, teacher_id: id, student_id: id },
DiaryEvent::ParentBound { parent_id: id, child_id: id },
DiaryEvent::AchievementUnlocked { user_id: id, achievement_id: "first_diary".into() },
];
let types: Vec<&str> = variants.iter().map(|v| v.event_type()).collect();
assert!(types.contains(&"diary.created"));
assert!(types.contains(&"diary.updated"));
assert!(types.contains(&"diary.deleted"));
assert!(types.contains(&"diary.shared"));
assert!(types.contains(&"diary.class.created"));
assert!(types.contains(&"diary.class.student_joined"));
assert!(types.contains(&"diary.topic.assigned"));
assert!(types.contains(&"diary.comment.created"));
assert!(types.contains(&"diary.parent.binding_confirmed"));
assert!(types.contains(&"diary.achievement.unlocked"));
}
}