feat: 小程序与管理端迭代(神仙AI、了解自己CRM、AI测试入口、报表与分润等)
Made-with: Cursor
This commit is contained in:
@@ -74,6 +74,21 @@ function applyDrawCountAfterShuffle(questions) {
|
||||
* @param {number|null|undefined} enterpriseId
|
||||
* @returns {Promise<Array>}
|
||||
*/
|
||||
function parseJsonBody(raw) {
|
||||
if (raw == null) return {}
|
||||
if (typeof raw === 'object' && !Array.isArray(raw)) return raw
|
||||
if (typeof raw === 'string') {
|
||||
const s = raw.trim()
|
||||
if (!s) return {}
|
||||
try {
|
||||
return JSON.parse(s)
|
||||
} catch (e) {
|
||||
return { _parseError: true, _rawSnippet: s.slice(0, 120) }
|
||||
}
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
function fetchQuestionBank(type, enterpriseId) {
|
||||
const q = [`type=${encodeURIComponent(type)}`]
|
||||
if (enterpriseId != null && Number(enterpriseId) > 0) {
|
||||
@@ -84,9 +99,14 @@ function fetchQuestionBank(type, enterpriseId) {
|
||||
method: 'GET',
|
||||
needAuth: true
|
||||
}).then((res) => {
|
||||
const body = res.data || {}
|
||||
const body = parseJsonBody(res.data)
|
||||
if (body._parseError) {
|
||||
throw new Error('接口返回非 JSON,请检查小程序 apiBase 与后台域名(可设 Storage key: apiBaseOverride)')
|
||||
}
|
||||
if (body.code !== 200 || body.data == null) {
|
||||
throw new Error(body.message || '拉取题库失败')
|
||||
const msg = body.message || '拉取题库失败'
|
||||
const hint = body.code === 401 || body.code === 403 ? `${msg}(请重新进入小程序或下拉刷新)` : msg
|
||||
throw new Error(hint)
|
||||
}
|
||||
const list = body.data.list
|
||||
if (!Array.isArray(list)) {
|
||||
@@ -96,18 +116,47 @@ function fetchQuestionBank(type, enterpriseId) {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 仅 mbti 类型支持本地 fallback 题库(60 题)
|
||||
* 网络异常(isNetworkError)或 5xx 且线上拉题失败时启用,保证测试可完成
|
||||
*/
|
||||
function loadLocalFallback(type) {
|
||||
if (type !== 'mbti') return null
|
||||
try {
|
||||
const { getMbtiLocalQuestions } = require('./mbtiLocalQuestions.js')
|
||||
const list = getMbtiLocalQuestions()
|
||||
return Array.isArray(list) && list.length ? list : null
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {'mbti'|'sbti'|'disc'|'pdp'} type
|
||||
* @param {{ enterpriseId?: number|null }} opts
|
||||
* @returns {Promise<Array>} 乱序后的题目;接口无题或失败则 reject
|
||||
* @param {{ enterpriseId?: number|null, allowLocalFallback?: boolean }} opts
|
||||
* @returns {Promise<Array>} 乱序后的题目;接口无题或失败则 reject(mbti 可降级本地)
|
||||
*/
|
||||
function loadQuestions(type, opts = {}) {
|
||||
const enterpriseId = resolveEnterpriseIdForQuestionBank(opts)
|
||||
const allowLocal = opts.allowLocalFallback !== false // 默认允许
|
||||
return fetchQuestionBank(type, enterpriseId).then((list) => {
|
||||
if (!list.length) {
|
||||
throw new Error('暂无启用题目')
|
||||
}
|
||||
return applyDrawCountAfterShuffle(shuffleQuestions(list))
|
||||
}).catch((err) => {
|
||||
// 网络异常 / 5xx / 401(登录失败)/ 超时均降级;4xx 业务错误不降级
|
||||
const status = err && err.statusCode
|
||||
const canFallback = err && (
|
||||
err.isNetworkError ||
|
||||
(status >= 500 && status < 600) ||
|
||||
status === 401
|
||||
)
|
||||
if (!allowLocal || !canFallback) throw err
|
||||
const local = loadLocalFallback(type)
|
||||
if (!local) throw err
|
||||
console.warn('[questionBank] 后端不可达,降级本地 fallback 题库:', type, err && err.message)
|
||||
return applyDrawCountAfterShuffle(shuffleQuestions(local))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user