fix(流程图): 学生能翻出全班评分、AI 调用没有限流

## 列表漏了一道门

代码提交列表在 `routes/submission.ts` 里有 `submission_list_show_all` 兜底:
关掉时非管理员一律返回空。流程图列表**从来没有这道门**,而它的过滤是

    if (myself === "1" || (!username && 是普通用户)) 只看自己
    else if (username) 按用户名模糊匹配

—— 只要带上 `username`,第二支就把第一支的限制绕过去了。学生在提交记录页把
语言切成「流程图」、用户名框随便填一个字,就能翻出全班同学的 AI 评分,不需要
动接口。补上和代码提交同一套口径。

## 提交与重判没有限流

每一次流程图提交都会触发一次外部 AI 调用,是和判题沙箱同级的有限资源,而这两个
入口都没限流。`canView` 还允许**本人**重试自己的提交,等于学生可以对着自己的
提交反复点,无上限地刷 AI 调用。

限流桶不能直接用 `throttling:user:<id>` —— 那是代码提交在用的桶(capacity 20,
回填约 1.8 个/分钟),共用的话学生在机房连着交几次代码,流程图这边就会莫名其妙
交不上去。单独开 `throttling:user:flowchart:<id>`。

重判对教师放行:成批点几十行是他们的正常用法。

## 提交编号的权限判断在前端自己算了一遍

契约里 `flowchartListItem.showLink` 是后端逐行下发的(与 `GET /flowcharts/:id`
的放行条件同源),前端却没用,自己按「超管或本人」重算了一次 —— 教师因此看得到
「重新判题」却打不开评分详情。

更要命的是无权限那一支渲染的 `n-text` **照样挂着 @click**,权限判断只改了外观。
学生点别人的编号,后端以 404 挡下,`loadSubmission` 只 console.error,于是弹出
一个 600px 高的空白面板,什么提示都没有。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-27 06:14:05 -06:00
parent 64facc5701
commit 8f08ed03a0
2 changed files with 37 additions and 12 deletions

View File

@@ -1,16 +1,15 @@
<template>
<n-button v-if="showLink" type="info" text @click="handleClick">
<n-button v-if="flowchart.showLink" type="info" text @click="handleClick">
{{ flowchart.id.slice(0, 12) }}
</n-button>
<n-text v-else class="flowchart-id" @click="handleClick">
<!-- 没权限时不能挂 @click后端 GET /flowcharts/:id 会以 404 挡下
前端只会得到一个静默失败的空白面板 -->
<n-text v-else class="flowchart-id" depth="3">
{{ flowchart.id.slice(0, 12) }}
</n-text>
</template>
<script setup lang="ts">
import type { FlowchartSubmissionListItem } from "utils/types"
import { useUserStore } from "shared/store/user"
const userStore = useUserStore()
interface Props {
flowchart: FlowchartSubmissionListItem
@@ -21,12 +20,9 @@ const emit = defineEmits<{
showDetail: [id: string]
}>()
const showLink = computed(() => {
if (!userStore.isAuthed) return false
if (userStore.isSuperAdmin) return true
return props.flowchart.username === userStore.user?.username
})
// showLink 由后端逐行下发(见 routes/flowchart.ts 的 canView
// GET /flowcharts/:id 的放行条件同源。原来前端自己按「超管或本人」算了一遍,
// 既漏了教师,也和后端对不上。
function handleClick() {
emit("showDetail", props.flowchart.id)
}