import { FASTAPI_BASE_URL, authHeaders, readError, type ApiEnvelope, type ScoreType } from './session' import type { EvaluationResult } from './assessment' export type TeachingAnswer = { question_id: number | string selected_answer: string } export type TeachingCaseOption = { key: string value: string text: string } export type TeachingCaseQuestion = { id: number | string question: string options: TeachingCaseOption[] correctAnswer?: string analysis?: string note?: string videoTitle?: string videoDesc?: string videoUrl?: string } type TeachingEvaluationPayload = { case_id: number answers: TeachingAnswer[] score_type?: ScoreType } type TeachingCaseItemsResponse = { items?: unknown[] questions?: unknown[] results?: unknown[] list?: unknown[] } export async function fetchTeachingCaseItems(caseId: number) { const response = await fetch(`${FASTAPI_BASE_URL}/teaching/cases/${caseId}/items`, { method: 'GET', headers: authHeaders() }) if (!response.ok) { throw new Error(await readError(response)) } const result = (await response.json()) as ApiEnvelope if (result.code !== 'OK' || !result.data) { throw new Error(result.message || '题目列表加载失败') } const rawItems = Array.isArray(result.data) ? result.data : result.data.items || result.data.questions || result.data.results || result.data.list || [] return rawItems.map(normalizeTeachingQuestion).filter((item): item is TeachingCaseQuestion => Boolean(item)) } export async function generateTeachingEvaluation(payload: TeachingEvaluationPayload) { const response = await fetch(`${FASTAPI_BASE_URL}/teaching/evaluation`, { method: 'POST', headers: authHeaders(), body: JSON.stringify(payload) }) if (!response.ok) { throw new Error(await readError(response)) } const result = (await response.json()) as ApiEnvelope if (result.code !== 'OK' || !result.data?.evaluation_id) { throw new Error(result.message || '教学评价生成失败') } return result.data } function normalizeTeachingQuestion(item: unknown) { if (!item || typeof item !== 'object') return null const source = item as Record const video = readObject(source.video) const id = readId(source) const question = readString(source, ['question', 'title', 'stem', 'content', 'text']) const options = normalizeOptions(source.options || source.choices || source.answers) if (!id || !question || options.length === 0) return null return { id, question, options, correctAnswer: readString(source, ['correct_answer', 'correctAnswer', 'answer', 'right_answer']), analysis: readString(source, ['analysis', 'explanation', '解析']), note: readString(source, ['note', 'hint', 'comment']), videoTitle: readString(video, ['title', 'name']) || readString(source, ['video_title', 'videoTitle']), videoDesc: readString(video, ['description', 'desc']) || readString(source, ['video_desc', 'videoDesc', 'video_description']), videoUrl: readString(video, ['url']) || readString(source, ['video_url', 'videoUrl']) } } function readObject(value: unknown) { if (value && typeof value === 'object' && !Array.isArray(value)) return value as Record return {} } function readId(source: Record) { const value = source.question_id || source.id || source.item_id if (typeof value === 'number' || typeof value === 'string') return value return '' } function normalizeOptions(value: unknown): TeachingCaseOption[] { if (Array.isArray(value)) { return value.map((item, index) => normalizeOption(item, index)).filter((item): item is TeachingCaseOption => Boolean(item)) } if (value && typeof value === 'object') { return Object.entries(value as Record).map(([key, text]) => ({ key, value: key, text: typeof text === 'string' ? text : String(text || '') })).filter(item => item.text) } return [] } function normalizeOption(item: unknown, index: number) { const fallbackKey = String.fromCharCode(65 + index) if (typeof item === 'string') { return { key: fallbackKey, value: fallbackKey, text: item } } if (!item || typeof item !== 'object') return null const source = item as Record const value = readString(source, ['value', 'key', 'option', 'option_key', 'id']) || fallbackKey const key = readString(source, ['key', 'option', 'option_key']) || value const text = readString(source, ['label', 'text', 'content', 'option_text', 'name']) || value return { key, value, text } } function readString(source: Record, keys: string[]) { for (const key of keys) { const value = source[key] if (typeof value === 'string' && value.trim()) return value if (typeof value === 'number') return String(value) } return '' }