fix(exam): 增强动态考题答案解析逻辑和调试日志
Some checks failed
continuous-integration/drone/push Build is failing

- 修复正确答案字母解析的正则表达式
- 支持更多冒号格式(中英文冒号、带空格)
- 添加详细的调试日志帮助定位问题
- 当无法解析时尝试内容匹配
This commit is contained in:
yuliang_guo
2026-01-28 13:19:31 +08:00
parent 98fc8b8eea
commit 22d8534fdf

View File

@@ -340,30 +340,56 @@ const transformDifyQuestions = (difyQuestions: any[]): any[] => {
if (q.type === 'single_choice' || q.type === 'multiple_choice') {
const options = q.topic?.options || {}
// 解析正确答案:支持 "A"、"A,B"、"A、B"、"Axxx" 等多种格式
const correctAnswerStr = String(q.correct || '')
const correctAnswerStr = String(q.correct || '').trim()
console.log(`📝 开始解析题目[${index + 1}]`)
console.log(` 原始correct: "${correctAnswerStr}"`)
console.log(` 原始options:`, options)
// 更精确地提取正确答案字母(避免误提取选项内容中的字母)
let correctLetters: string[] = []
// 情况1纯选项字母格式如 "A", "B", "A,B", "A、B", "A,B,C"
const pureLetterMatch = correctAnswerStr.trim().match(/^[A-Da-d]([,、\s]+[A-Da-d])*$/)
const pureLetterMatch = correctAnswerStr.match(/^[A-Da-d]([,、\s]+[A-Da-d])*$/)
if (pureLetterMatch) {
correctLetters = correctAnswerStr.match(/[A-Da-d]/g)?.map(l => l.toUpperCase()) || []
console.log(` ✓ 匹配情况1(纯字母): correctLetters=[${correctLetters.join(',')}]`)
} else {
// 情况2选项字母+内容格式(如 "Axxx" 或 "A: xxx"
// 只提取开头的选项字母(可能有多个,如 "A,Cxxx"
const prefixMatch = correctAnswerStr.match(/^([A-Da-d](?:[,、\s]*[A-Da-d])*)[:\s]/)
// 情况2选项字母+内容格式(如 "Axxx" 或 "A: xxx" 或 "A:xxx"
// 支持多种冒号格式:中文全角冒号、英文冒号
const prefixMatch = correctAnswerStr.match(/^([A-Da-d](?:[,、\s]*[A-Da-d])*)\s*[:]\s*/)
if (prefixMatch) {
correctLetters = prefixMatch[1].match(/[A-Da-d]/g)?.map(l => l.toUpperCase()) || []
console.log(` ✓ 匹配情况2(字母+冒号): prefixMatch="${prefixMatch[0]}", correctLetters=[${correctLetters.join(',')}]`)
} else {
// 情况3只有开头字母如 "A" 后面直接跟内容)
const firstLetterMatch = correctAnswerStr.match(/^([A-Da-d])/)
// 情况3只有开头字母如 "A" 后面直接跟非字母内容)
const firstLetterMatch = correctAnswerStr.match(/^([A-Da-d])(?![A-Za-z])/)
if (firstLetterMatch) {
correctLetters = [firstLetterMatch[1].toUpperCase()]
console.log(` ✓ 匹配情况3(开头字母): correctLetters=[${correctLetters.join(',')}]`)
} else {
// 情况4无法解析尝试从选项中查找匹配的答案
console.log(` ⚠️ 无法从correct字符串解析字母尝试内容匹配`)
// 尝试在选项中查找完全匹配的内容
const optionValues = Object.values(options)
optionValues.forEach((optVal: any, idx: number) => {
const optContent = String(optVal || '')
if (optContent === correctAnswerStr || optContent.includes(correctAnswerStr)) {
const letter = String.fromCharCode(65 + idx)
correctLetters.push(letter)
console.log(` → 内容匹配: 选项${letter}="${optContent.substring(0, 30)}..."`)
}
})
}
}
}
// 确保至少有一个正确答案
if (correctLetters.length === 0) {
console.warn(` ❌ 警告:未能解析出正确答案字母,默认使用第一个选项`)
correctLetters = ['A']
}
console.log(`🔍 解析题目[${index + 1}] - correct: "${q.correct}", correctLetters: [${correctLetters.join(', ')}]`)
// 获取所有选项的键并排序,确保顺序一致
@@ -376,38 +402,46 @@ const transformDifyQuestions = (difyQuestions: any[]): any[] => {
return a.localeCompare(b)
})
console.log(` 选项键排序后: [${optionKeys.join(', ')}]`)
transformed.options = optionKeys.map((key, idx) => {
const opt = options[key]
const optStr = String(opt)
const optStr = String(opt || '')
// 提取选项内容(去掉 "A" 或 "A:" 前缀)
let content = optStr
if (optStr.match(/^[A-Da-d][:]/)) {
content = optStr.substring(2).trim()
} else if (optStr.includes('')) {
content = optStr.split('').slice(1).join('').trim()
// 匹配 "Axxx" 或 "A:xxx" 格式(支持中英文冒号)
const contentPrefixMatch = optStr.match(/^[A-Da-d]\s*[:]\s*(.*)$/)
if (contentPrefixMatch) {
content = contentPrefixMatch[1].trim()
}
// 提取当前选项的字母优先级optStr前缀 > key字母 > 索引推断)
// 提取当前选项的字母
// 优先级1. 选项内容前缀 > 2. key字母 > 3. 索引推断
let optionLetter = ''
let letterSource = ''
// 1. 尝试从选项内容前缀提取(如 "Axxx" 或 "A:xxx"
const prefixMatch = optStr.match(/^([A-Da-d])[:]/)
const prefixMatch = optStr.match(/^([A-Da-d])\s*[:]/)
if (prefixMatch) {
optionLetter = prefixMatch[1].toUpperCase()
letterSource = '内容前缀'
}
// 2. 尝试从 key 提取(如果 key 是单个字母)
// 2. 尝试从 key 提取(如果 key 是单个字母 A-D
else if (key.match(/^[A-Da-d]$/)) {
optionLetter = key.toUpperCase()
letterSource = 'key字母'
}
// 3. 根据索引推断选项字母0->A, 1->B, 2->C, 3->D
else {
optionLetter = String.fromCharCode(65 + idx) // 65 = 'A'
letterSource = '索引推断'
}
// 判断当前选项是否正确:检查选项字母是否在正确答案列表中
const isCorrect = correctLetters.includes(optionLetter)
console.log(` 选项[${key}/${idx}]: letter=${optionLetter}, isCorrect=${isCorrect}, content="${content.substring(0, 20)}..."`)
console.log(` 选项[${key}/${idx}]: letter=${optionLetter}(${letterSource}), isCorrect=${isCorrect}, correctLetters=[${correctLetters.join(',')}], content="${content.substring(0, 25)}..."`)
return { content, isCorrect, optionLetter }
})