feat(voice): add reusable transcript records

This commit is contained in:
zhangheng
2026-07-22 17:26:55 +08:00
parent f755ed2b08
commit 108e280b3f
12 changed files with 353 additions and 57 deletions

View File

@@ -93,6 +93,7 @@ struct SegmentRow {
sequence: i32,
duration_ms: i32,
status: String,
transcript: Option<String>,
error_message: Option<String>,
}
@@ -141,6 +142,7 @@ struct LessonSummaryResponse {
duration_ms: i32,
applied_directly: bool,
included_in_generation: bool,
transcript: String,
segments: Vec<AudioSegmentResponse>,
started_at: DateTime<Utc>,
ended_at: Option<DateTime<Utc>>,
@@ -614,8 +616,8 @@ async fn finish_lesson_session(
post,
path = "/api/v1/feedback-lessons/{lesson_id}/mark-applied",
tag = "语音反馈",
summary = "标记录音已加入反馈",
description = "短录音文字直接加入反馈内容后,标记该节课无需再次汇总。",
summary = "标记录音已加入反馈",
description = "教师将该节转写手动加入反馈内容后,标记该节课无需再次汇总。",
params(
("Authorization" = String, Header, description = "Bearer 会话令牌", example = "Bearer <access-token>"),
("lesson_id" = Uuid, Path, description = "课堂录音 ID")
@@ -967,7 +969,8 @@ async fn load_session_response(
.fetch_all(pool)
.await?;
let segments = sqlx::query_as::<_, SegmentRow>(
"SELECT s.id, s.lesson_session_id, s.sequence, s.duration_ms, s.status, s.error_message \
"SELECT s.id, s.lesson_session_id, s.sequence, s.duration_ms, s.status, \
s.transcript, s.error_message \
FROM audio_segments s JOIN lesson_sessions l ON l.id = s.lesson_session_id \
WHERE l.feedback_session_id = $1 ORDER BY l.sequence, s.sequence",
)
@@ -994,7 +997,16 @@ async fn load_session_response(
lesson.status == "ready" && !lesson.applied_directly && !lesson.included_in_generation
});
let mut segments_by_lesson: HashMap<Uuid, Vec<AudioSegmentResponse>> = HashMap::new();
let mut transcripts_by_lesson: HashMap<Uuid, Vec<String>> = HashMap::new();
for segment in segments {
if segment.status == "ready" {
if let Some(transcript) = segment.transcript.as_deref() {
transcripts_by_lesson
.entry(segment.lesson_session_id)
.or_default()
.push(transcript.to_owned());
}
}
segments_by_lesson
.entry(segment.lesson_session_id)
.or_default()
@@ -1016,6 +1028,10 @@ async fn load_session_response(
duration_ms: lesson.duration_ms,
applied_directly: lesson.applied_directly,
included_in_generation: lesson.included_in_generation,
transcript: transcripts_by_lesson
.remove(&lesson.id)
.unwrap_or_default()
.join("\n"),
segments: segments_by_lesson.remove(&lesson.id).unwrap_or_default(),
started_at: lesson.started_at,
ended_at: lesson.ended_at,

View File

@@ -968,6 +968,10 @@ mod tests {
assert!(document["info"].get("license").is_none());
assert!(document["components"]["schemas"]["ProfileInput"]["example"].is_object());
assert!(document["components"]["schemas"]["ProfileDefaultsInput"]["example"].is_object());
assert!(
document["components"]["schemas"]["LessonSummaryResponse"]["properties"]["transcript"]
.is_object()
);
assert!(
document["components"]["schemas"]["FeedbackRecordInput"]["example"]["profile_id"]
.is_null()