Перейти к основному содержимому

События

Правила

  • События не содержат raw answer, answer key, hidden solution и teacher notes.
  • События проверки публикуются после commit результата.
  • Evidence events являются низкоуровневыми signals, не mastery.
  • Usage events нужны соседним доменам для трассировки версии задачи.
  • Повторная доставка входящих команд идемпотентна.

Общий envelope

type TaskBankEvent<TPayload> = {
messageId: string;
messageKind: 'event';
messageType: string;
messageVersion: number;
producer: 'task-bank';
producerInstance: string;
traceId: string;
occurredAt: string;
actor?: {
type: 'user' | 'service' | 'system';
id?: string;
};
correlationId?: string;
data: TPayload;
metadata?: Record<string, unknown>;
};

Исходящие события

EventКогдаPayload
task-bank.problem.createdсоздана задачаproblemId, code, subjectKey
task-bank.problem.publishedопубликована задачаproblemId, versionId, version
task-bank.problem.retiredзадача выведена из новых использованийproblemId, reason
task-bank.problem_version.publishedопубликована versionproblemId, versionId, version
task-bank.problem_set.publishedопубликована подборкаproblemSetId, slug, purpose
task-bank.activity_template.publishedопубликован шаблон активностиactivityTemplateId, version, activityType
task-bank.activity_template.retiredшаблон больше не используется для новых экспортовactivityTemplateId, reason
task-bank.program_template.publishedопубликована программаprogramTemplateId, version, programType
task-bank.program_template.retiredпрограмма retiredprogramTemplateId, reason
task-bank.content_export_snapshot.createdсоздан snapshotsnapshotId, sourceRef, targetRef, status
task-bank.content_export_snapshot.lockedsnapshot зафиксированsnapshotId, sourceRef, targetRef, payloadHash
task-bank.public_projection.updatedобновлена public-safe projectionproblemId, canonicalUrl, publicStatus
task-bank.problem_relation.confirmedподтверждена связь задачrelationId, relationType, fromProblemId, toProblemId
task-bank.problem_usage.createdсоздан usageusageId, problemVersionId, contextRef
task-bank.attempt.startedначата attemptattemptId, problemVersionId, userId
task-bank.attempt.submittedотправлен ответattemptId, problemVersionId, userId
task-bank.answer_check.completedпроверка завершенаcheckId, attemptId, score, status
task-bank.manual_review.requiredнужна ручная проверкаreviewId, attemptId
task-bank.answer_check.overriddenрезультат изменён вручнуюcheckId, attemptId, reason
task-bank.evidence.createdсоздан evidenceevidenceId, problemId, userId, signal
task-bank.solution.accessedзакрытое решение прочитаноproblemVersionId, actor.id, context

Входящие команды

ИсточникКомандаИспользование
LMScreate usageпривязать задачу к уроку/домашке
LMScreate activity/program export snapshotимпортировать content template
LMSsubmit/check attemptпроверить учебную попытку
competitionscreate locked usageпривязать задачу к туру
competitionscreate locked content snapshotзафиксировать набор/тур
competitionssubmit/check attemptпроверить олимпиадный ответ
storefrontrequest public projectionоткрыть задачу/подборку в каталоге
managementrequest aggregate evidenceпрочитать агрегаты

Payload examples

task-bank.problem_usage.created

type ProblemUsageCreatedPayload = {
usageId: string;
problemVersionId: string;
contextRef: {
domain: 'lms' | 'competitions' | 'management' | 'storefront' | 'manual';
type: string;
id: string;
};
scoringRuleSnapshot?: Record<string, unknown>;
};

task-bank.content_export_snapshot.locked

type ContentExportSnapshotLockedPayload = {
snapshotId: string;
sourceRef: {
type: 'problem_set' | 'activity_template' | 'program_template';
id: string;
version: number;
};
targetRef: {
domain: 'lms' | 'competitions' | 'storefront' | 'management' | 'manual';
type: string;
id?: string;
};
payloadHash: string;
lockedAt: string;
};

task-bank.public_projection.updated

type PublicProjectionUpdatedPayload = {
problemId: string;
canonicalUrl: string;
publicStatus: 'published' | 'hidden' | 'embargoed';
primaryTopicId?: string;
};

task-bank.answer_check.completed

type AnswerCheckCompletedPayload = {
checkId: string;
attemptId: string;
problemVersionId: string;
status: 'checked' | 'needs_manual_review' | 'failed' | 'overridden';
isCorrect?: boolean;
score?: number;
maxScore?: number;
};

Идемпотентность

СценарийКлюч
create usageusage:{contextDomain}:{contextType}:{contextId}:{problemVersionId}:{position}
create export snapshotsnapshot:{sourceType}:{sourceId}:{sourceVersion}:{targetDomain}:{targetType}:{targetId}
lock export snapshotsnapshot:lock:{snapshotId}:{payloadHash}
start attemptattempt:start:{context}:{userId}:{problemVersionId}:{clientKey}
submit attemptattempt:submit:{attemptId}:{answerHash}
auto checkcheck:auto:{attemptId}:{checkingRuleId}
evidenceevidence:{attemptId}:{signal}

Retention

ДанныеСрок
problem versionsбессрочно
locked content export snapshotsне меньше retention target context
raw attemptsпо privacy policy и требованиям LMS/competitions
answer checksбессрочно или до архивирования контекста
evidenceаналитический срок, минимум 5 лет
audit logs5 лет