fix(voice): add per-lesson failure recovery
This commit is contained in:
@@ -22,6 +22,7 @@ import type {
|
||||
FeedbackSession,
|
||||
FeedbackSummaryRun,
|
||||
StudentProfile,
|
||||
VoiceAudioSegment,
|
||||
VoiceLesson,
|
||||
VoiceLessonFinished
|
||||
} from '../../utils/types'
|
||||
@@ -58,6 +59,7 @@ interface VoiceLessonView {
|
||||
canAdd: boolean
|
||||
summaryEligible: boolean
|
||||
summarySelected: boolean
|
||||
failedSegmentCount: number
|
||||
addActionText: string
|
||||
emptyText: string
|
||||
}
|
||||
@@ -144,6 +146,7 @@ function buildVoiceLessonViews(
|
||||
const transcript = lesson.transcript.trim()
|
||||
const isInDraft = Boolean(transcript) && content.includes(transcript)
|
||||
const summaryEligible = lesson.status === 'ready' && Boolean(transcript)
|
||||
const failedSegmentCount = lesson.segments.filter((segment) => segment.status === 'failed').length
|
||||
let emptyText = '转录完成后可查看内容'
|
||||
if (lesson.status === 'failed') {
|
||||
emptyText = lesson.segments.find((segment) => segment.status === 'failed')?.errorMessage
|
||||
@@ -162,6 +165,7 @@ function buildVoiceLessonViews(
|
||||
canAdd: lesson.status === 'ready' && Boolean(transcript) && !isInDraft,
|
||||
summaryEligible,
|
||||
summarySelected: summaryEligible && !excluded.has(lesson.id),
|
||||
failedSegmentCount,
|
||||
addActionText: isInDraft
|
||||
? '已在输入框'
|
||||
: lesson.appliedDirectly
|
||||
@@ -1056,13 +1060,34 @@ Page({
|
||||
if (!session) return
|
||||
const failedSegments = getFailedVoiceSegments(session)
|
||||
if (failedSegments.length === 0) return
|
||||
await this.retryVoiceSegments(failedSegments)
|
||||
},
|
||||
|
||||
async retryVoiceLesson(event: LessonActionEvent) {
|
||||
const session = this.data.session
|
||||
const lesson = session?.lessons.find((item) => item.id === event.currentTarget.dataset.lessonId)
|
||||
if (!lesson) return
|
||||
const failedSegments = lesson.segments.filter((segment) => segment.status === 'failed')
|
||||
if (failedSegments.length === 0) return
|
||||
await this.retryVoiceSegments(failedSegments, lesson.sequence)
|
||||
},
|
||||
|
||||
async retryVoiceSegments(
|
||||
failedSegments: VoiceAudioSegment[],
|
||||
lessonSequence?: number
|
||||
) {
|
||||
const session = this.data.session
|
||||
if (!session || this.data.recording || this.data.voiceBusy) return
|
||||
logVoiceOperation('transcription_retry_clicked', {
|
||||
feedbackSessionId: session.id,
|
||||
lessonSequence: lessonSequence || null,
|
||||
failedSegmentCount: failedSegments.length
|
||||
})
|
||||
this.setData({
|
||||
voiceBusy: true,
|
||||
voiceStatus: `正在重试${failedSegments.length}段转录`,
|
||||
voiceStatus: lessonSequence
|
||||
? `正在重试第${lessonSequence}节转录`
|
||||
: `正在重试${failedSegments.length}段转录`,
|
||||
primaryActionText: '正在重试转录',
|
||||
errorMessage: '',
|
||||
errorRequestId: ''
|
||||
@@ -1078,9 +1103,13 @@ Page({
|
||||
)
|
||||
logVoiceOperation('transcription_retry_submitted', {
|
||||
feedbackSessionId: session.id,
|
||||
lessonSequence: lessonSequence || null,
|
||||
segmentIds: failedSegments.map((segment) => segment.id)
|
||||
})
|
||||
wx.showToast({ title: '已重新提交,正在转录', icon: 'none' })
|
||||
wx.showToast({
|
||||
title: lessonSequence ? `第${lessonSequence}节已重新提交` : '已重新提交,正在转录',
|
||||
icon: 'none'
|
||||
})
|
||||
} catch (error) {
|
||||
logVoiceOperation('transcription_retry_failed', {
|
||||
feedbackSessionId: session.id,
|
||||
@@ -1094,13 +1123,17 @@ Page({
|
||||
}
|
||||
},
|
||||
|
||||
confirmDiscardFailedSegments(): Promise<boolean> {
|
||||
const failedSegmentCount = this.data.session?.failedSegmentCount || 0
|
||||
confirmDiscardVoiceSegments(
|
||||
failedSegmentCount: number,
|
||||
lessonSequence?: number
|
||||
): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
wx.showModal({
|
||||
title: '放弃失败录音',
|
||||
content: `将删除${failedSegmentCount}段失败录音,其余录音和反馈内容不受影响。此操作无法撤销。`,
|
||||
confirmText: '确认放弃',
|
||||
title: lessonSequence ? `删除第${lessonSequence}节录音` : '删除失败录音',
|
||||
content: lessonSequence
|
||||
? `将删除第${lessonSequence}节中${failedSegmentCount}段转录失败的录音,其余语音记录不受影响。此操作无法撤销。`
|
||||
: `将删除${failedSegmentCount}段失败录音,其余录音和反馈内容不受影响。此操作无法撤销。`,
|
||||
confirmText: '确认删除',
|
||||
confirmColor: '#d13f3a',
|
||||
success: (result) => resolve(result.confirm),
|
||||
fail: () => resolve(false)
|
||||
@@ -1113,11 +1146,38 @@ Page({
|
||||
const session = this.data.session
|
||||
if (!session) return
|
||||
const failedSegments = getFailedVoiceSegments(session)
|
||||
if (failedSegments.length === 0 || !(await this.confirmDiscardFailedSegments())) return
|
||||
if (
|
||||
failedSegments.length === 0
|
||||
|| !(await this.confirmDiscardVoiceSegments(failedSegments.length))
|
||||
) return
|
||||
await this.discardVoiceSegments(failedSegments)
|
||||
},
|
||||
|
||||
async discardVoiceLesson(event: LessonActionEvent) {
|
||||
if (this.data.recording || this.data.voiceBusy) return
|
||||
const session = this.data.session
|
||||
const lesson = session?.lessons.find((item) => item.id === event.currentTarget.dataset.lessonId)
|
||||
if (!session || !lesson) return
|
||||
const failedSegments = lesson.segments.filter((segment) => segment.status === 'failed')
|
||||
if (
|
||||
failedSegments.length === 0
|
||||
|| !(await this.confirmDiscardVoiceSegments(failedSegments.length, lesson.sequence))
|
||||
) return
|
||||
await this.discardVoiceSegments(failedSegments, lesson.sequence)
|
||||
},
|
||||
|
||||
async discardVoiceSegments(
|
||||
failedSegments: VoiceAudioSegment[],
|
||||
lessonSequence?: number
|
||||
) {
|
||||
const session = this.data.session
|
||||
if (!session) return
|
||||
|
||||
this.setData({
|
||||
voiceBusy: true,
|
||||
voiceStatus: `正在放弃${failedSegments.length}段失败录音`,
|
||||
voiceStatus: lessonSequence
|
||||
? `正在删除第${lessonSequence}节失败录音`
|
||||
: `正在删除${failedSegments.length}段失败录音`,
|
||||
primaryActionText: '正在处理',
|
||||
errorMessage: '',
|
||||
errorRequestId: ''
|
||||
@@ -1133,9 +1193,13 @@ Page({
|
||||
)
|
||||
logVoiceOperation('failed_transcription_discarded', {
|
||||
feedbackSessionId: session.id,
|
||||
lessonSequence: lessonSequence || null,
|
||||
segmentIds: failedSegments.map((segment) => segment.id)
|
||||
})
|
||||
wx.showToast({ title: '已放弃失败录音', icon: 'none' })
|
||||
wx.showToast({
|
||||
title: lessonSequence ? `第${lessonSequence}节失败录音已删除` : '失败录音已删除',
|
||||
icon: 'none'
|
||||
})
|
||||
} catch (error) {
|
||||
this.showApiError(error)
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user