feat: 完善听力口语辅助状态记录、练习分块与复习抽查间隔机制

This commit is contained in:
shen
2026-09-23 18:33:22 -07:00
parent 6b42b7abc3
commit d740a9e19c
106 changed files with 2933 additions and 845 deletions
+40 -6
View File
@@ -757,13 +757,16 @@ void main() {
isTrue,
);
}
state.completeSpeaking();
state.completeSpeaking(practiced: true);
final speaking = state.attemptEvidence.where(
(entry) => entry.taskId == 'lesson-$segmentId-speaking',
);
// Follow-reading is assisted practice for every target, never
// independent evidence for one of them.
expect(speaking.map((entry) => entry.itemId), targets);
// Follow-reading only credits core items contained in the model line.
expect(speaking.map((entry) => entry.itemId).toSet(), {
'A0-W36',
'A0-P01',
'A0-P03',
});
expect(
speaking.every((entry) => entry.outcome == EvidenceKind.assisted),
isTrue,
@@ -780,13 +783,44 @@ void main() {
test('a first-try listening choice is recognition evidence', () {
final state = AppState();
state.completePreview();
state.recordListeningPlayback();
state.completeListening(recognized: true);
for (final id in lessonById('a0-01').targetItemIds) {
expect(state.mastery[id]!.status, MasteryStatus.recognize);
expect(state.mastery['A0-P01']!.status, MasteryStatus.recognize);
expect(state.mastery['A0-P02']!.status, MasteryStatus.newItem);
expect(state.mastery['A0-P03']!.status, MasteryStatus.newItem);
});
test('segment comprehension does not award unrelated core items', () {
final state = AppState()..setPlacementStartLesson('a0-09');
state.completePreview();
state.recordListeningPlayback();
state.completeListening(recognized: true);
final evidence = state.attemptEvidence.where(
(entry) => entry.taskId == 'lesson-a0-09-a-listening-check',
);
expect(evidence.single.itemId, 'segment:a0-09-a');
expect(evidence.single.skill, '听力理解');
for (final id in lessonById('a0-09').targetItemIds) {
expect(state.mastery[id]!.status, MasteryStatus.newItem);
}
});
test('a model sentence without a core link stays segment evidence', () {
final state = AppState()..setPlacementStartLesson('a0-02');
state.completePreview();
state.completeListening();
state.completeSpeaking(practiced: true);
final evidence = state.attemptEvidence.where(
(entry) => entry.taskId == 'lesson-a0-02-a-speaking',
);
expect(evidence.single.itemId, 'segment:a0-02-a');
expect(evidence.single.skill, '句子跟读');
expect(state.mastery['A0-P04']!.status, MasteryStatus.newItem);
});
test('scene dialogue credits taught items the learner actually used', () {
final state = AppState();
state.completePreview();