fix: add status guards to prevent data loss
- create: auto-delete REJECTED→throw error; add tenantId filter - remove: forbid PUBLISHED bank deletion - removeItem: forbid PUBLISHED item deletion - generateQuestions: restrict to DRAFT status only - frontend: render MULTIPLE_CHOICE options/judgment/followupHints - frontend: add judgment and followupHints to QuestionBankItem type - add 12 service guard tests (109 total)
This commit is contained in:
@@ -335,6 +335,33 @@ export default function QuestionBankDetailView() {
|
||||
<span className={`inline-flex items-center gap-1 px-2.5 py-1 text-[10px] font-black uppercase tracking-widest rounded-full border ${itemStat.bg} ${itemStat.text} ${itemStat.border}`}>{itemStat.icon}{itemStat.label}</span>
|
||||
</div>
|
||||
<p className="font-bold text-slate-900 leading-relaxed">{item.questionText}</p>
|
||||
{item.questionType === 'MULTIPLE_CHOICE' && item.options && item.options.length > 0 && (
|
||||
<div className="mt-3 space-y-1.5 pl-1 border-l-2 border-blue-200">
|
||||
{item.options.map((opt, i) => {
|
||||
const letter = String.fromCharCode(65 + i);
|
||||
const isCorrect = item.correctAnswer === letter;
|
||||
return (
|
||||
<div key={i} className={`flex items-center gap-2 px-3 py-2 rounded-xl text-sm ${isCorrect ? 'bg-emerald-50 border border-emerald-200' : 'bg-slate-50'}`}>
|
||||
<span className={`inline-flex items-center justify-center w-6 h-6 rounded-lg text-[10px] font-black shrink-0 ${isCorrect ? 'bg-emerald-500 text-white' : 'bg-slate-200 text-slate-500'}`}>{letter}</span>
|
||||
<span className={`font-medium ${isCorrect ? 'text-emerald-700' : 'text-slate-600'}`}>{opt}</span>
|
||||
{isCorrect && <Check size={14} className="text-emerald-500 shrink-0 ml-auto" />}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
{item.judgment && (
|
||||
<div className="mt-3 bg-blue-50/50 border border-blue-100 rounded-xl p-3">
|
||||
<span className="text-[10px] font-black text-blue-400 uppercase tracking-widest">{item.questionType === 'MULTIPLE_CHOICE' ? '解析' : '判定依据'}</span>
|
||||
<p className="text-xs text-slate-600 mt-1 leading-relaxed">{item.judgment}</p>
|
||||
</div>
|
||||
)}
|
||||
{item.questionType === 'SHORT_ANSWER' && item.followupHints && item.followupHints.length > 0 && (
|
||||
<div className="mt-2 flex flex-wrap gap-1.5 items-center">
|
||||
<span className="text-[10px] font-black text-purple-400 uppercase tracking-widest">追问方向</span>
|
||||
{item.followupHints.map((hint, i) => <span key={i} className="px-2.5 py-1 bg-purple-50 text-purple-600 text-[10px] font-medium rounded-lg border border-purple-100/50">#{i + 1} {hint}</span>)}
|
||||
</div>
|
||||
)}
|
||||
{item.keyPoints.length > 0 && (
|
||||
<div className="mt-3 flex flex-wrap gap-1.5 items-center">
|
||||
<span className="text-[10px] font-black text-slate-400 uppercase tracking-widest mr-1">{t('gradingPoints')}</span>
|
||||
|
||||
@@ -17,6 +17,8 @@ export interface QuestionBankItem {
|
||||
questionType: 'SHORT_ANSWER' | 'MULTIPLE_CHOICE' | 'TRUE_FALSE';
|
||||
options?: string[] | null;
|
||||
correctAnswer?: string | null;
|
||||
judgment?: string | null;
|
||||
followupHints?: string[] | null;
|
||||
keyPoints: string[];
|
||||
difficulty: 'STANDARD' | 'ADVANCED' | 'SPECIALIST';
|
||||
dimension: 'PROMPT' | 'LLM' | 'IDE' | 'DEV_PATTERN' | 'WORK_CAPABILITY';
|
||||
|
||||
Reference in New Issue
Block a user