add ws
Some checks failed
Deploy / deploy (push) Has been cancelled

This commit is contained in:
2025-10-07 17:04:35 +08:00
parent 437da9d588
commit 389393b70d
3 changed files with 575 additions and 73 deletions

View File

@@ -9,6 +9,10 @@ import { submissionMemoryFormat, submissionTimeFormat } from "utils/functions"
import { Submission, SubmitCodePayload } from "utils/types"
import SubmissionResultTag from "shared/components/SubmissionResultTag.vue"
import { isDesktop } from "shared/composables/breakpoints"
import {
useSubmissionWebSocket,
type SubmissionUpdate,
} from "shared/composables/websocket"
import { useUserStore } from "shared/store/user"
const ProblemComment = defineAsyncComponent(
@@ -39,7 +43,7 @@ const { start: showCommentPanel } = useTimeoutFn(
{ immediate: false },
)
const { start: fetchSubmission } = useTimeoutFn(
const { start: fetchSubmission, stop: stopFetchSubmission } = useTimeoutFn(
async () => {
const res = await getSubmission(submissionId.value)
submission.value = res.data
@@ -57,45 +61,76 @@ const { start: fetchSubmission } = useTimeoutFn(
{ immediate: false },
)
// WebSocket 消息处理器
const handleSubmissionUpdate = (data: SubmissionUpdate) => {
console.log("[Submit] 收到提交更新:", data)
if (data.submission_id !== submissionId.value) {
console.log(`[Submit] 提交ID不匹配: 期望=${submissionId.value}, 实际=${data.submission_id}`)
return
}
if (!submission.value) {
submission.value = {} as Submission
}
submission.value.result = data.result as Submission["result"]
// 判题完成,获取完整提交详情
if (data.status === "finished") {
console.log("[Submit] 判题完成,获取详细信息")
getSubmission(submissionId.value).then((res) => {
submission.value = res.data
submitted.value = false
// 判题完成后15 分钟无新提交则断开 WebSocket 连接
// 考虑到学生换题间隔约 10 分钟15 分钟可覆盖大部分场景
scheduleDisconnect(15 * 60 * 1000)
})
} else if (data.status === "error") {
console.log("[Submit] 判题出错")
submitted.value = false
// 判题出错后15 分钟无新提交则断开 WebSocket 连接
scheduleDisconnect(15 * 60 * 1000)
}
}
// 初始化 WebSocket按需连接模式
const {
connect,
subscribe,
scheduleDisconnect,
cancelScheduledDisconnect,
status: wsStatus,
} = useSubmissionWebSocket(handleSubmissionUpdate)
// 组件卸载时停止轮询
onUnmounted(() => {
stopFetchSubmission()
})
const judging = computed(
() =>
!!(
submission.value && submission.value.result === SubmissionStatus.judging
),
() => submission.value?.result === SubmissionStatus.judging,
)
const pending = computed(
() =>
!!(
submission.value && submission.value.result === SubmissionStatus.pending
),
() => submission.value?.result === SubmissionStatus.pending,
)
const submitting = computed(
() =>
!!(
submission.value &&
submission.value.result === SubmissionStatus.submitting
),
() => submission.value?.result === SubmissionStatus.submitting,
)
const submitDisabled = computed(() => {
if (!userStore.isAuthed) {
return true
}
if (code.value.trim() === "") {
return true
}
if (judging.value || pending.value || submitting.value) {
return true
}
if (submitted.value) {
return true
}
if (isPending.value) {
return true
}
return false
return (
!userStore.isAuthed ||
code.value.trim() === "" ||
judging.value ||
pending.value ||
submitting.value ||
submitted.value ||
isPending.value
)
})
const submitLabel = computed(() => {
@@ -115,43 +150,39 @@ const submitLabel = computed(() => {
})
const msg = computed(() => {
if (!submission.value) return ""
let msg = ""
const result = submission.value && submission.value.result
const result = submission.value.result
if (
result === SubmissionStatus.compile_error ||
result === SubmissionStatus.runtime_error
) {
msg += "请仔细检查,看看代码的格式是不是写错了!\n\n"
}
if (
submission.value &&
submission.value.statistic_info &&
submission.value.statistic_info.err_info
) {
if (submission.value.statistic_info?.err_info) {
msg += submission.value.statistic_info.err_info
}
return msg
})
const infoTable = computed(() => {
if (!submission.value?.info?.data?.length) return []
const result = submission.value.result
if (
submission.value &&
submission.value.result !== SubmissionStatus.accepted &&
submission.value.result !== SubmissionStatus.compile_error &&
submission.value.result !== SubmissionStatus.runtime_error &&
submission.value.info &&
submission.value.info.data &&
submission.value.info.data.length
result === SubmissionStatus.accepted ||
result === SubmissionStatus.compile_error ||
result === SubmissionStatus.runtime_error
) {
const data = submission.value.info.data
if (data.some((item) => item.result === 0)) {
return submission.value.info.data
} else {
return []
}
} else {
return []
}
const data = submission.value.info.data
return data.some((item) => item.result === 0) ? data : []
})
const columns: DataTableColumn<Submission["info"]["data"][number]>[] = [
@@ -175,9 +206,8 @@ const columns: DataTableColumn<Submission["info"]["data"][number]>[] = [
]
async function submit() {
if (!userStore.isAuthed) {
return
}
if (!userStore.isAuthed) return
const data: SubmitCodePayload = {
problem_id: problem.value!.id,
language: code.language,
@@ -186,35 +216,73 @@ async function submit() {
if (contestID) {
data.contest_id = parseInt(contestID)
}
submission.value = { result: 9 } as Submission
const res = await submitCode(data)
submissionId.value = res.data.submission_id
// 防止重复提交
console.log(`[Submit] 代码已提交: ID=${submissionId.value}`)
submitPending()
submitted.value = true
// 查询结果
fetchSubmission()
// 取消之前安排的断开倒计时(如果有新提交)
cancelScheduledDisconnect()
// 按需连接 WebSocket
if (wsStatus.value !== "connected") {
console.log(`[Submit] WebSocket 未连接,正在连接... 当前状态: ${wsStatus.value}`)
connect()
} else {
console.log("[Submit] WebSocket 已连接,直接订阅")
}
// 优先使用 WebSocket 实时更新
if (wsStatus.value === "connected" || wsStatus.value === "connecting") {
// 等待连接完成后订阅
const checkConnection = setInterval(() => {
if (wsStatus.value === "connected") {
clearInterval(checkConnection)
console.log(`[Submit] 订阅提交更新: ID=${submissionId.value}`)
subscribe(submissionId.value)
}
}, 100)
// 5 秒后如果还在判题中,降级到轮询作为保险
// 考虑到判题一般 1-3 秒完成5 秒足以覆盖绝大部分情况
setTimeout(() => {
clearInterval(checkConnection)
if (
submission.value &&
(submission.value.result === SubmissionStatus.judging ||
submission.value.result === SubmissionStatus.pending ||
submission.value.result === 9)
) {
console.log("WebSocket 未及时响应,降级到轮询模式")
fetchSubmission()
}
}, 5000)
} else {
// WebSocket 连接失败,直接使用轮询
fetchSubmission()
}
}
watch(
() => submission?.value?.result,
() => submission.value?.result,
(result) => {
if (result === SubmissionStatus.accepted) {
// 刷新题目状态
problem.value!.my_status = 0
// 放烟花
confetti({
particleCount: 300,
startVelocity: 30,
gravity: 0.5,
spread: 350,
origin: { x: 0.5, y: 0.4 },
})
// 题目在第一次完成之后,弹出点评框
if (!contestID) {
showCommentPanel()
}
}
if (result !== SubmissionStatus.accepted) return
// 刷新题目状态
problem.value!.my_status = 0
// 放烟花
confetti({
particleCount: 300,
startVelocity: 30,
gravity: 0.5,
spread: 350,
origin: { x: 0.5, y: 0.4 },
})
// 题目在第一次完成之后,弹出点评框
if (!contestID) showCommentPanel()
},
)
</script>