This commit is contained in:
jocay
2026-08-17 23:57:43 +08:00
parent 005260f893
commit 29b0f03c24
11 changed files with 1085 additions and 256 deletions
+4 -2
View File
@@ -1,7 +1,9 @@
const fs = require('fs');
const path = require('path');
const BASE = 'http://localhost:3030';
const BASE = (process.env.BASE_URL || 'http://localhost:3031').replace(/\/$/, '');
const ADMIN_USER = process.env.ADMIN_USER || 'admin';
const ADMIN_PASS = process.env.ADMIN_PASS || 'admin123';
const ARGS = process.argv.slice(2);
const FILES = ARGS.length ? ARGS : ['set-dart-dev.json', 'set-flutter-dev.json'];
@@ -9,7 +11,7 @@ async function main() {
const loginRes = await fetch(BASE + '/api/admin/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: 'admin', password: 'admin123' })
body: JSON.stringify({ username: ADMIN_USER, password: ADMIN_PASS })
});
const login = await loginRes.json();
if (!loginRes.ok) throw new Error('登录失败: ' + login.error);
+32 -4
View File
@@ -1,18 +1,46 @@
const d = require('../questions.json');
let total = 0, bad = [];
const setIds = new Set(), qIds = new Set(), setNames = new Set();
let maxSetId = 0, maxQId = 0;
if (!d || !Array.isArray(d.sets)) {
console.error('questions.json 根节点必须包含 sets 数组');
process.exit(1);
}
for (const set of d.sets) {
if (!Number.isInteger(set.id) || set.id < 1 || setIds.has(set.id)) bad.push(`[套题] ID 非法或重复: ${set.id}`);
else { setIds.add(set.id); maxSetId = Math.max(maxSetId, set.id); }
const normalizedName = typeof set.name === 'string' ? set.name.trim().toLowerCase() : '';
if (!normalizedName || setNames.has(normalizedName)) bad.push(`[套题 #${set.id}] 名称为空或重复`);
else setNames.add(normalizedName);
if (typeof set.name === 'string' && set.name.trim().length > 80) bad.push(`[套题 #${set.id}] 名称超过 80 个字符`);
if (set.description !== undefined && (typeof set.description !== 'string' || set.description.trim().length > 240)) {
bad.push(`[${set.name || set.id}] description 必须为不超过 240 字的字符串`);
}
if (set.published !== undefined && typeof set.published !== 'boolean') bad.push(`[${set.name || set.id}] published 必须为布尔值`);
if (!Array.isArray(set.questions)) { bad.push(`[${set.name || set.id}] questions 不是数组`); continue; }
if (set.lvNames != null && (!Array.isArray(set.lvNames) || set.lvNames.length !== 6 || set.lvNames.some(n => typeof n !== 'string' || !n.trim()))) {
bad.push(`[${set.name}] lvNames 必须为 6 个非空名称`);
}
for (const q of set.questions) {
total++;
if (!q.q || !Array.isArray(q.opts) || q.opts.length < 2) { bad.push(`[${set.name} #${q.id}] 选项不足`); continue; }
if (!Number.isInteger(q.id) || q.id < 1 || qIds.has(q.id)) { bad.push(`[${set.name}] 题目 ID 非法或重复: ${q.id}`); continue; }
qIds.add(q.id); maxQId = Math.max(maxQId, q.id);
if (typeof q.q !== 'string' || !q.q.trim()) { bad.push(`[${set.name} #${q.id}] 题干必须为非空字符串`); continue; }
if (!Array.isArray(q.opts) || q.opts.length < 2 || q.opts.some(o => typeof o !== 'string' || !o.trim())) {
bad.push(`[${set.name} #${q.id}] 至少需要 2 个非空字符串选项`); continue;
}
if (!Number.isInteger(q.ans) || q.ans < 0 || q.ans >= q.opts.length) { bad.push(`[${set.name} #${q.id}] ans 越界`); continue; }
if (!q.exp) { bad.push(`[${set.name} #${q.id}] 解析`); continue; }
if (typeof q.exp !== 'string' || !q.exp.trim()) { bad.push(`[${set.name} #${q.id}] 解析必须为非空字符串`); continue; }
if (!Number.isInteger(q.lv) || q.lv < 1 || q.lv > 6) { bad.push(`[${set.name} #${q.id}] lv 非法`); continue; }
const dup = q.opts.findIndex((o, i) => q.opts.indexOf(o) !== i);
const normalizedOpts = q.opts.map(o => o.trim());
const dup = normalizedOpts.findIndex((o, i) => normalizedOpts.indexOf(o) !== i);
if (dup !== -1) { bad.push(`[${set.name} #${q.id}] 选项重复: "${q.opts[dup]}"`); }
}
}
if (!Number.isInteger(d.nextSetId) || d.nextSetId <= maxSetId) bad.push(`nextSetId 应大于 ${maxSetId}`);
if (!Number.isInteger(d.nextQId) || d.nextQId <= maxQId) bad.push(`nextQId 应大于 ${maxQId}`);
console.log('总题数:', total);
if (bad.length) { console.log('问题:'); bad.forEach(b => console.log(' -', b)); }
if (bad.length) { console.log('问题:'); bad.forEach(b => console.log(' -', b)); process.exitCode = 1; }
else console.log('结构校验全部通过');
// 抽查逻辑已移除,因为选项顺序已被随机打乱