fix(voice): recover empty failed lessons
This commit is contained in:
@@ -38,6 +38,7 @@ pub fn router() -> OpenApiRouter<SharedState> {
|
||||
.routes(routes!(finish_lesson_session))
|
||||
.routes(routes!(mark_lesson_applied))
|
||||
.routes(routes!(retry_audio_segment))
|
||||
.routes(routes!(discard_empty_failed_lesson))
|
||||
.routes(routes!(discard_audio_segment))
|
||||
.routes(routes!(create_feedback_summary_run))
|
||||
.routes(routes!(get_feedback_summary_run))
|
||||
@@ -796,6 +797,67 @@ async fn retry_audio_segment(
|
||||
}))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
delete,
|
||||
path = "/api/v1/feedback-lessons/{lesson_id}",
|
||||
tag = "语音反馈",
|
||||
summary = "删除未保留音频的失败课堂",
|
||||
description = "删除没有任何录音片段、因而无法重试转录的失败课堂记录。",
|
||||
params(
|
||||
("Authorization" = String, Header, description = "Bearer 会话令牌", example = "Bearer <access-token>"),
|
||||
("lesson_id" = Uuid, Path, description = "失败课堂录音 ID")
|
||||
),
|
||||
responses(
|
||||
(status = 204, description = "空失败课堂已删除"),
|
||||
(status = 404, description = "课堂不存在、不属于当前用户或仍包含录音片段", body = ErrorBody),
|
||||
CommonApiErrors
|
||||
)
|
||||
)]
|
||||
async fn discard_empty_failed_lesson(
|
||||
State(state): State<SharedState>,
|
||||
headers: HeaderMap,
|
||||
Path(lesson_id): Path<Uuid>,
|
||||
) -> Result<StatusCode, ApiError> {
|
||||
let owner_id = state
|
||||
.auth
|
||||
.authenticate(state.pool.as_ref(), &headers)
|
||||
.await?
|
||||
.user_id;
|
||||
let mut transaction = pool(&state)?.begin().await?;
|
||||
let session_id = sqlx::query_scalar::<_, Uuid>(
|
||||
"SELECT f.id FROM lesson_sessions l \
|
||||
JOIN feedback_sessions f ON f.id = l.feedback_session_id \
|
||||
WHERE l.id = $1 AND f.owner_id = $2 AND f.status = 'active' \
|
||||
AND l.status = 'failed' \
|
||||
AND NOT EXISTS (SELECT 1 FROM audio_segments s WHERE s.lesson_session_id = l.id) \
|
||||
FOR UPDATE OF l",
|
||||
)
|
||||
.bind(lesson_id)
|
||||
.bind(owner_id)
|
||||
.fetch_optional(&mut *transaction)
|
||||
.await?
|
||||
.ok_or(ApiError::NotFound)?;
|
||||
|
||||
sqlx::query("DELETE FROM lesson_sessions WHERE id = $1")
|
||||
.bind(lesson_id)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
sqlx::query("UPDATE feedback_sessions SET updated_at = now() WHERE id = $1")
|
||||
.bind(session_id)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
transaction.commit().await?;
|
||||
|
||||
tracing::info!(
|
||||
event = "empty_failed_lesson_discarded",
|
||||
feedback_session_id = %session_id,
|
||||
lesson_id = %lesson_id,
|
||||
status = "discarded",
|
||||
"empty failed lesson discarded"
|
||||
);
|
||||
Ok(StatusCode::NO_CONTENT)
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
delete,
|
||||
path = "/api/v1/audio-segments/{segment_id}",
|
||||
|
||||
@@ -924,7 +924,8 @@ mod tests {
|
||||
.filter(|operation| operation.get("responses").is_some())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(operations.len(), 27);
|
||||
assert_eq!(operations.len(), 28);
|
||||
assert!(paths.contains_key("/api/v1/feedback-lessons/{lesson_id}"));
|
||||
assert!(paths.contains_key("/api/v1/feedback-sessions/{session_id}/summary-runs"));
|
||||
assert!(paths.contains_key("/api/v1/feedback-summary-runs/{run_id}"));
|
||||
assert!(paths.contains_key("/api/v1/feedback-summary-runs/{run_id}/apply"));
|
||||
|
||||
Reference in New Issue
Block a user