Files
IOT_SIM/server/src/grading/queue.ts
T
xuyueandClaude Opus 5.5 de277d3e9d
CI/CD / test (push) Canceled after 0s
CI/CD / deploy (push) Canceled after 0s
用 bun fmt 格式化全部代码和文档
- bunfig.toml 的 [fmt]:不写分号、单引号、一行最长 160,和原来的写法一致
- .prettierignore:docs/教材摘要/ 是书上的摘录,不格式化
- 模板里一行写两条语句的事件处理(@keydown.delete、@dblclick)改成调用方法:去掉分号后拆成两行,Vue 编译不过

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 14:02:33 +08:00

76 lines
2.6 KiB
TypeScript

// 评分队列:学生提交后立即返回,后台按提交顺序评分(同时评的份数不超过检查项的并发上限,保证评分槽位能用满),结果写回数据库。
// 服务器重启前没评完的提交,启动时继续评。评完调用 onGraded(接着做 AI 评阅)。
import type { Database } from 'bun:sqlite'
import type { SourceFile } from '@iot/shared'
import { mkdir, rm } from 'node:fs/promises'
import { dirname, join } from 'node:path'
import { assignmentChecks, pendingSubmissionIds, saveGrade, submissionForGrading } from '../assignments'
import { config } from '../config'
import { safeJoin } from '../run-session'
import { grade } from './grader'
/** 把作品写进一个干净的工程目录(评分和自测用) */
export async function writeProject(dir: string, files: SourceFile[]) {
await rm(dir, { recursive: true, force: true })
for (const file of files) {
const target = safeJoin(dir, file.path)
await mkdir(dirname(target), { recursive: true })
await Bun.write(target, file.content)
}
}
export function createGradingQueue(db: Database, onGraded: (submissionId: number) => void = () => {}) {
const queue: number[] = []
let active = 0
let idleWaiters: (() => void)[] = []
async function gradeOne(id: number) {
const submission = submissionForGrading(db, id)
const checks = submission && assignmentChecks(db, submission.assignmentId)
if (!submission || !checks) return
const dir = join(config.runsDir, `grade-${id}`)
try {
await writeProject(dir, submission.files)
saveGrade(db, id, await grade(dir, checks, submission.sampleId))
onGraded(id)
} catch (error) {
console.error(`评分出错(提交 ${id}):`, error)
saveGrade(db, id, null)
} finally {
await rm(dir, { recursive: true, force: true })
}
}
function pump() {
while (active < config.gradeConcurrency && queue.length > 0) {
const id = queue.shift()!
active++
void gradeOne(id).finally(() => {
active--
pump()
})
}
if (active === 0 && queue.length === 0) {
for (const resolve of idleWaiters) resolve()
idleWaiters = []
}
}
for (const id of pendingSubmissionIds(db)) queue.push(id)
pump()
return {
enqueue(id: number) {
queue.push(id)
pump()
},
/** 等队列全部评完(测试用) */
idle(): Promise<void> {
if (active === 0 && queue.length === 0) return Promise.resolve()
return new Promise((resolve) => idleWaiters.push(resolve))
},
}
}
export type GradingQueue = ReturnType<typeof createGradingQueue>