feat(ai): 新增 AI 模块并集成至备课/错题集/试卷/改题四大业务场景
- 新增 src/modules/ai 独立模块,遵循三层架构(actions → services → shared/lib/ai) - 通过 AiClientProvider + useAiClient 实现 React Context 依赖注入,业务组件零直接 import - 6 个 Server Actions 均调用 requirePermission() 权限校验,返回 ActionState<T> - withAiTracking 统一埋点,覆盖 chat/similar_question/grading_assist/lesson_content/question_variant/weakness_analysis - 集成场景:作业批改 AiGradingAssist、错题集 AiErrorBookAnalysis、备课 AiLessonContentGenerator、试卷 AiQuestionVariantGenerator - 全量 i18n(en/zh-CN ai.json),Error Boundary + Skeleton 边界处理 - 同步架构图 004/005,新增审计报告 ai-module-audit-report.md
This commit is contained in:
@@ -27,6 +27,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@/shared/components/ui/
|
||||
import { gradeHomeworkSubmissionAction } from "../actions"
|
||||
import { formatDate } from "@/shared/lib/utils"
|
||||
import { QuestionRenderer } from "./question-renderer"
|
||||
import { AiGradingAssist } from "@/modules/ai/components/ai-grading-assist"
|
||||
import {
|
||||
applyAutoGrades as applyAutoGradesUtil,
|
||||
extractAnswerValue,
|
||||
@@ -136,16 +137,21 @@ export function HomeworkGradingView({
|
||||
formData.set("submissionId", submissionId)
|
||||
formData.set("answersJson", JSON.stringify(payload))
|
||||
|
||||
const result = await gradeHomeworkSubmissionAction(null, formData)
|
||||
try {
|
||||
const result = await gradeHomeworkSubmissionAction(null, formData)
|
||||
|
||||
if (result.success) {
|
||||
toast.success(t("homework.grade.gradesSaved"))
|
||||
// Optionally redirect or stay
|
||||
router.refresh()
|
||||
} else {
|
||||
toast.error(result.message || t("homework.grade.gradesSaveFailed"))
|
||||
if (result.success) {
|
||||
toast.success(t("homework.grade.gradesSaved"))
|
||||
// Optionally redirect or stay
|
||||
router.refresh()
|
||||
} else {
|
||||
toast.error(result.message || t("homework.grade.gradesSaveFailed"))
|
||||
}
|
||||
} catch {
|
||||
toast.error(t("homework.grade.gradesSaveFailed"))
|
||||
} finally {
|
||||
setIsSubmitting(false)
|
||||
}
|
||||
setIsSubmitting(false)
|
||||
}
|
||||
|
||||
const handleScrollToQuestion = (id: string) => {
|
||||
@@ -337,6 +343,22 @@ export function HomeworkGradingView({
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* AI Grading Assist (subjective questions only) */}
|
||||
{!isAutoGradable(ans) && (
|
||||
<AiGradingAssist
|
||||
questionText={extractQuestionText(ans.questionContent)}
|
||||
questionType={ans.questionType}
|
||||
studentAnswer={formatStudentAnswer(ans.studentAnswer)}
|
||||
correctAnswer={getTextCorrectAnswers(ans.questionContent).join(" / ")}
|
||||
maxScore={ans.maxScore}
|
||||
onApplyScore={(score) => handleManualScoreChange(ans.id, String(score))}
|
||||
onApplyFeedback={(feedback) => {
|
||||
handleFeedbackChange(ans.id, feedback)
|
||||
setShowFeedbackByAnswerId((prev) => ({ ...prev, [ans.id]: true }))
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</CardFooter>
|
||||
</Card>
|
||||
)
|
||||
@@ -520,3 +542,21 @@ const formatStudentAnswer = (studentAnswer: unknown): string => {
|
||||
if (v == null) return "—"
|
||||
return JSON.stringify(v)
|
||||
}
|
||||
|
||||
/**
|
||||
* 从题目内容中提取纯文本(用于 AI 批改输入)
|
||||
*
|
||||
* 优先使用 `text` 字段;若不存在则回退到 JSON 字符串,
|
||||
* 保证 AI 服务能拿到可读的题目描述。
|
||||
*/
|
||||
const extractQuestionText = (content: QuestionContent | null): string => {
|
||||
if (!content) return ""
|
||||
if (typeof content.text === "string" && content.text.trim().length > 0) {
|
||||
return content.text
|
||||
}
|
||||
try {
|
||||
return JSON.stringify(content)
|
||||
} catch {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user