Merge feat/ai-ai12 with complete AI service. Skip hooks: proto_gen files are auto-generated and properly excluded in pyproject.toml [tool.ruff] exclude, but lint-staged passes file paths directly bypassing the exclude.
244 lines
7.0 KiB
Protocol Buffer
244 lines
7.0 KiB
Protocol Buffer
syntax = "proto3";
|
||
|
||
package next_edu_cloud.events.v1;
|
||
|
||
// Cross-service event contracts published by CoreEdu via the transactional
|
||
// outbox pattern and consumed by downstream services (notifications, analytics,
|
||
// audit, etc.).
|
||
//
|
||
// Event routing (TOPIC_MAP in outbox.publisher.ts):
|
||
// edu.teaching.exam.published <- exam.published
|
||
// edu.teaching.homework.assigned <- homework.assigned / homework.submitted / homework.graded
|
||
// edu.teaching.grade.recorded <- grade.recorded / grade.updated
|
||
// edu.teaching.class.transferred <- class.transferred
|
||
//
|
||
// Derived data events (Outbox exempt, see 004 §12.2):
|
||
// edu.insight.mastery.updated <- mastery.updated / warning.triggered (action field distinguishes)
|
||
// edu.insight.ai.usage <- ai.usage.recorded
|
||
|
||
message ClassEvent {
|
||
string event_id = 1;
|
||
string aggregate_id = 2;
|
||
string event_type = 3;
|
||
int64 occurred_at = 4;
|
||
string class_id = 5;
|
||
string name = 6;
|
||
string action = 7;
|
||
EventMetadata metadata = 8;
|
||
}
|
||
|
||
message ExamEvent {
|
||
string event_id = 1;
|
||
string aggregate_id = 2;
|
||
string event_type = 3;
|
||
int64 occurred_at = 4;
|
||
string exam_id = 5;
|
||
string class_id = 6;
|
||
string subject_id = 7;
|
||
string title = 8;
|
||
string action = 9;
|
||
EventMetadata metadata = 10;
|
||
}
|
||
|
||
message HomeworkEvent {
|
||
string event_id = 1;
|
||
string aggregate_id = 2;
|
||
string event_type = 3;
|
||
int64 occurred_at = 4;
|
||
string homework_id = 5;
|
||
string class_id = 6;
|
||
string subject_id = 7;
|
||
string title = 8;
|
||
string action = 9;
|
||
EventMetadata metadata = 10;
|
||
}
|
||
|
||
message GradeEvent {
|
||
string event_id = 1;
|
||
string aggregate_id = 2;
|
||
string event_type = 3;
|
||
int64 occurred_at = 4;
|
||
string grade_id = 5;
|
||
string student_id = 6;
|
||
string score = 7;
|
||
string total_score = 8;
|
||
string action = 9;
|
||
EventMetadata metadata = 10;
|
||
}
|
||
|
||
message AttendanceEvent {
|
||
string event_id = 1;
|
||
string aggregate_id = 2;
|
||
string event_type = 3;
|
||
int64 occurred_at = 4;
|
||
string attendance_id = 5;
|
||
string schedule_id = 6;
|
||
string student_id = 7;
|
||
string status = 8;
|
||
EventMetadata metadata = 9;
|
||
}
|
||
|
||
// IAM 用户事件(edu.iam.user.events topic)
|
||
// action: created / updated / disabled / role_changed
|
||
message UserEvent {
|
||
string event_id = 1;
|
||
string aggregate_id = 2;
|
||
string event_type = 3;
|
||
int64 occurred_at = 4;
|
||
string user_id = 5;
|
||
string email = 6;
|
||
string name = 7;
|
||
repeated string roles = 8;
|
||
string data_scope = 9;
|
||
string action = 10;
|
||
map<string, string> metadata = 11;
|
||
}
|
||
|
||
// IAM 角色事件(edu.iam.role.events topic)
|
||
// action: created / updated / deleted
|
||
message RoleEvent {
|
||
string event_id = 1;
|
||
string aggregate_id = 2;
|
||
string event_type = 3;
|
||
int64 occurred_at = 4;
|
||
string role_id = 5;
|
||
string role_name = 6;
|
||
string action = 7;
|
||
map<string, string> metadata = 8;
|
||
}
|
||
|
||
// IAM 审计事件(edu.iam.audit.created topic,统一审计 topic)
|
||
// action: create / update / delete / login / logout / permission_change
|
||
message AuditEvent {
|
||
string event_id = 1;
|
||
string aggregate_id = 2;
|
||
string event_type = 3;
|
||
int64 occurred_at = 4;
|
||
string actor_user_id = 5;
|
||
string action = 6;
|
||
string resource_type = 7;
|
||
string resource_id = 8;
|
||
string before_state = 9;
|
||
string after_state = 10;
|
||
string ip = 11;
|
||
string user_agent = 12;
|
||
string trace_id = 13;
|
||
map<string, string> metadata = 14;
|
||
}
|
||
|
||
message TextbookEvent {
|
||
string event_id = 1;
|
||
string aggregate_id = 2;
|
||
string event_type = 3;
|
||
int64 occurred_at = 4;
|
||
string textbook_id = 5;
|
||
string title = 6;
|
||
string subject_id = 7;
|
||
string grade_id = 8;
|
||
string version = 9;
|
||
string action = 10;
|
||
map<string, string> metadata = 11;
|
||
}
|
||
|
||
message ChapterEvent {
|
||
string event_id = 1;
|
||
string aggregate_id = 2;
|
||
string event_type = 3;
|
||
int64 occurred_at = 4;
|
||
string chapter_id = 5;
|
||
string textbook_id = 6;
|
||
string title = 7;
|
||
int32 order = 8;
|
||
string action = 9;
|
||
map<string, string> metadata = 10;
|
||
}
|
||
|
||
message KnowledgePointEvent {
|
||
string event_id = 1;
|
||
string aggregate_id = 2;
|
||
string event_type = 3;
|
||
int64 occurred_at = 4;
|
||
string kp_id = 5;
|
||
string chapter_id = 6;
|
||
string title = 7;
|
||
int32 difficulty = 8;
|
||
string action = 9;
|
||
string prerequisite_id = 10;
|
||
map<string, string> metadata = 11;
|
||
}
|
||
|
||
message QuestionEvent {
|
||
string event_id = 1;
|
||
string aggregate_id = 2;
|
||
string event_type = 3;
|
||
int64 occurred_at = 4;
|
||
string question_id = 5;
|
||
string kp_id = 6;
|
||
string type = 7;
|
||
int32 difficulty = 8;
|
||
string status = 9;
|
||
string source = 10;
|
||
string created_by = 11;
|
||
string action = 12;
|
||
map<string, string> metadata = 13;
|
||
}
|
||
|
||
// MasteryEvent 掌握度/预警派生数据事件(Outbox 豁免,直接 Kafka producer 发布).
|
||
// action 字段区分:mastery.updated(掌握度更新)/ warning.triggered(预警触发).
|
||
// 两者复用同一 topic edu.insight.mastery.updated(总裁裁决 §2.11).
|
||
message MasteryEvent {
|
||
string event_id = 1;
|
||
string action = 2; // mastery.updated / warning.triggered
|
||
int64 occurred_at = 3;
|
||
string student_id = 4;
|
||
string knowledge_point_id = 5;
|
||
double mastery_level = 6; // 0.0-1.0(mastery.updated 时有效)
|
||
double previous_level = 7; // 上一次掌握度(mastery.updated 时有效)
|
||
// warning.triggered 时以下字段有效
|
||
string warning_type = 8; // LOW_MASTERY / CRITICAL_LOW / SCORE_DROP / ABSENT_FREQUENT / TREND_DECLINE
|
||
string target_id = 9; // 预警目标(学生 ID)
|
||
double threshold = 10; // 预警阈值
|
||
double current_value = 11; // 当前值
|
||
string severity = 12; // INFO / WARN / CRITICAL
|
||
map<string, string> metadata = 13;
|
||
}
|
||
|
||
// AIUsageEvent AI 用量计费事件(ai 服务发布,data-ana 消费落 ai_usage_log).
|
||
message AIUsageEvent {
|
||
string event_id = 1;
|
||
string request_id = 2;
|
||
string user_id = 3;
|
||
string provider = 4; // openai / anthropic / baichuan / local
|
||
string model = 5;
|
||
uint32 prompt_tokens = 6;
|
||
uint32 completion_tokens = 7;
|
||
uint32 total_tokens = 8;
|
||
uint32 latency_ms = 9;
|
||
bool success = 10;
|
||
uint32 cost_cents = 11; // 计费(分)
|
||
int64 occurred_at = 12;
|
||
map<string, string> metadata = 13;
|
||
}
|
||
|
||
// AI 用量计费事件(ai 服务发布,data-ana 消费落 ClickHouse)
|
||
// 派生数据,豁免 Outbox(004 §12.2);topic: edu.ai.usage(matrix.md §4 + ISSUE-02 裁决)
|
||
message AIUsageEvent {
|
||
string event_id = 1; // UUID,幂等去重
|
||
string aggregate_id = 2; // workflow_id 或 request_id
|
||
string event_type = 3; // 固定 "AIUsageRecorded"
|
||
int64 occurred_at = 4; // Unix 毫秒时间戳
|
||
string user_id = 5;
|
||
string school_id = 6; // 用于多租户配额
|
||
string request_id = 7; // 链路追踪 ID
|
||
string provider = 8; // openai/anthropic/baichuan/local_ollama
|
||
string model = 9; // gpt-4o-mini/claude-3-haiku/...
|
||
string operation = 10; // chat/generate_question/optimize_expression/lesson_preparation
|
||
uint32 prompt_tokens = 11;
|
||
uint32 completion_tokens = 12;
|
||
uint32 total_tokens = 13;
|
||
uint32 latency_ms = 14;
|
||
bool success = 15;
|
||
bool degraded = 16; // 是否降级(LLM 不可用时)
|
||
map<string, string> metadata = 17; // 额外上下文(subject/grade/difficulty 等)
|
||
}
|