speaking.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import type { TopicDiscussionConfig } from '@/types/englishSpeaking'
  2. import { SPEAKING_CONFIG_API_BASE_URL } from '@/views/Editor/EnglishSpeaking/services/speakingApiConfig'
  3. const API_BASE = SPEAKING_CONFIG_API_BASE_URL
  4. export interface SpeakingConfigRecord {
  5. id: string
  6. config: TopicDiscussionConfig
  7. ownerUid?: string | null
  8. }
  9. async function parse<T>(res: Response): Promise<T> {
  10. if (!res.ok) {
  11. const detail = await res.text().catch(() => '')
  12. throw new Error(`[${res.status}] ${detail || res.statusText}`)
  13. }
  14. return res.json() as Promise<T>
  15. }
  16. /** 新建话题讨论配置 */
  17. export async function createSpeakingConfig(
  18. config: TopicDiscussionConfig,
  19. ownerUid?: string | null,
  20. ): Promise<SpeakingConfigRecord> {
  21. const res = await fetch(API_BASE, {
  22. method: 'POST',
  23. headers: { 'Content-Type': 'application/json' },
  24. body: JSON.stringify({ config, ownerUid: ownerUid ?? null }),
  25. })
  26. return parse<SpeakingConfigRecord>(res)
  27. }
  28. /** 读取话题讨论配置 */
  29. export async function getSpeakingConfig(id: string): Promise<SpeakingConfigRecord> {
  30. const res = await fetch(`${API_BASE}/${encodeURIComponent(id)}`)
  31. return parse<SpeakingConfigRecord>(res)
  32. }
  33. /** 更新话题讨论配置 */
  34. export async function updateSpeakingConfig(
  35. id: string,
  36. config: TopicDiscussionConfig,
  37. ): Promise<SpeakingConfigRecord> {
  38. const res = await fetch(`${API_BASE}/${encodeURIComponent(id)}`, {
  39. method: 'PUT',
  40. headers: { 'Content-Type': 'application/json' },
  41. body: JSON.stringify({ config }),
  42. })
  43. return parse<SpeakingConfigRecord>(res)
  44. }
  45. import { SPEAKING_DIALOGUE_API_BASE_URL } from '@/views/Editor/EnglishSpeaking/services/speakingApiConfig'
  46. const DIALOGUE_BASE = SPEAKING_DIALOGUE_API_BASE_URL
  47. export interface ClassSessionSummary {
  48. userId: string
  49. sessionId: string
  50. status: 'active' | 'completed' | 'abandoned'
  51. overallStatus: 'ready' | 'generating' | 'failed' | null
  52. currentRound: number
  53. totalRounds: number
  54. overallScore: number | null
  55. dimensions: Record<string, number>
  56. topHighlights: string[]
  57. topImprovements: string[]
  58. createdAt: string | null
  59. completedAt: string | null
  60. }
  61. export interface ListSessionsByConfigResponse {
  62. summaries: ClassSessionSummary[]
  63. }
  64. export async function listSpeakingSessionsByConfig(
  65. configId: string,
  66. userIds: string[],
  67. ): Promise<ListSessionsByConfigResponse> {
  68. const params = new URLSearchParams({ configId, userIds: userIds.join(',') })
  69. const res = await fetch(`${DIALOGUE_BASE}/sessions/by-config?${params}`, {
  70. method: 'GET',
  71. credentials: 'include',
  72. })
  73. if (!res.ok) throw new Error(`listSpeakingSessionsByConfig failed: ${res.status}`)
  74. return res.json()
  75. }
  76. export interface ClassSummaryResponse {
  77. bullets: [string, string, string]
  78. generatedAt: string
  79. fromCache: boolean
  80. llmStatus: 'ok' | 'fallback'
  81. }
  82. export async function generateClassSummary(
  83. configId: string,
  84. userIds: string[],
  85. locale: 'zh' | 'en' | 'hk',
  86. ): Promise<ClassSummaryResponse> {
  87. const res = await fetch(`${DIALOGUE_BASE}/sessions/by-config/summary`, {
  88. method: 'POST',
  89. headers: { 'Content-Type': 'application/json' },
  90. credentials: 'include',
  91. body: JSON.stringify({ configId, userIds, locale }),
  92. })
  93. if (!res.ok) throw new Error(`generateClassSummary failed: ${res.status}`)
  94. return res.json()
  95. }