diff --git a/apps/api/src/routes/submission-statistics.ts b/apps/api/src/routes/submission-statistics.ts index 7621d7f..52ae43d 100644 --- a/apps/api/src/routes/submission-statistics.ts +++ b/apps/api/src/routes/submission-statistics.ts @@ -296,6 +296,19 @@ async function astOnlyByUser(where: SQL | undefined, userIds: number[]) { * 快照实测 count 65ms → 0.6ms。`ks2` 这种匹配上千个账号的宽前缀退回扫表,30~50ms, * 和原来持平。 */ +/** + * 用户名**精确**匹配。usernameFilter 是「包含」,学号互相包含是常态(ks24a1 包含在 ks24a10 里), + * 协作中老师看「这个学生」的提交时不能把别人的也捞进来。查无此人留恒假条件 + */ +export async function exactUsernameFilter(username: string) { + const [row] = await db + .select({ id: schema.user.id }) + .from(schema.user) + .where(eq(schema.user.username, username)) + .limit(1) + return row ? eq(schema.submission.userId, row.id) : sql`false` +} + export async function usernameFilter(username: string) { const like = `%${username}%` const users = await db diff --git a/apps/api/src/routes/submission.ts b/apps/api/src/routes/submission.ts index aef4091..56a545e 100644 --- a/apps/api/src/routes/submission.ts +++ b/apps/api/src/routes/submission.ts @@ -32,7 +32,12 @@ import { problemSetJoinTimes } from "../services/problemset" import { consumeToken } from "../services/throttling" import { dayStart } from "../time" import { asFilterValue, asRecord, isAdminRole, queryInteger } from "./helpers" -import { problemFilter, submissionStatisticsRoutes, usernameFilter } from "./submission-statistics" +import { + exactUsernameFilter, + problemFilter, + submissionStatisticsRoutes, + usernameFilter, +} from "./submission-statistics" export const submissionRoutes = new Hono() @@ -489,7 +494,11 @@ submissionRoutes.get("/submissions", optionalAuth, async (c) => { filters.push( ...(await Promise.all([ displayId ? problemFilter(displayId, null) : undefined, - username ? usernameFilter(username) : undefined, + username + ? c.req.query("exactUsername") === "1" + ? exactUsernameFilter(username) + : usernameFilter(username) + : undefined, ])), ) if (myself) filters.push(eq(schema.submission.userId, myself.id)) diff --git a/apps/web/src/oj/problem/components/CollabBar.vue b/apps/web/src/oj/problem/components/CollabBar.vue new file mode 100644 index 0000000..3330b0b --- /dev/null +++ b/apps/web/src/oj/problem/components/CollabBar.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/apps/web/src/oj/problem/components/EditorToolbar.vue b/apps/web/src/oj/problem/components/EditorToolbar.vue index 694ffc6..dccf4b8 100644 --- a/apps/web/src/oj/problem/components/EditorToolbar.vue +++ b/apps/web/src/oj/problem/components/EditorToolbar.vue @@ -108,14 +108,14 @@ const showCollabBar = computed(() => collabHere.value && userStore.isTeacherOrAb const helpButtonText = computed(() => { if (collabStore.helpStatus === "active") { const name = collabStore.teacherName - return name ? `${name} 老师帮你中` : "老师正在帮你" + return name ? `${name} 老师在帮你` : "老师正在帮你" } if (collabStore.helpStatus === "pending") { return collabStore.queueAhead > 0 - ? `已求助 · 前面 ${collabStore.queueAhead} 人` - : "已求助 · 待接入" + ? `已举手 · 前面 ${collabStore.queueAhead} 人` + : "已举手 · 老师马上来" } - return "求助" + return "举手求助" }) const helpButtonType = computed(() => { @@ -301,18 +301,6 @@ const goEdit = () => { 更多 - - +import { storeToRefs } from "pinia" +import { getSubmission, getSubmissions } from "oj/api" +import { useProblemStore } from "oj/store/problem" +import { useCollabStore } from "shared/store/collab" +import { parseTime } from "utils/functions" +import type { Submission } from "utils/types" + +/** + * 老师接单之后落在题目页,「结果」页签原来是空的 —— 学生多半是交了几次没过才举的手, + * 老师最先想知道的就是「他交上去是什么结果」。这里摆出学生最近一次交的(设计文档第 9 节)。 + * 管理员角色本来就能看任何人的提交,不需要新接口。 + */ +const SubmissionResult = defineAsyncComponent(() => import("./SubmissionResult.vue")) + +const collabStore = useCollabStore() +const { problem } = storeToRefs(useProblemStore()) + +const loading = ref(false) +const failed = ref(false) +const latest = ref(null) +const checked = ref(false) + +const peer = computed(() => collabStore.room?.peerName ?? "") + +async function load() { + if (!peer.value || !problem.value) return + loading.value = true + failed.value = false + try { + const list = await getSubmissions({ + username: peer.value, + exactUsername: "1", + problemDisplayId: problem.value._id, + limit: 1, + offset: 0, + }) + const row = list.results[0] + latest.value = row ? await getSubmission(row.id) : null + checked.value = true + } catch { + failed.value = true + } finally { + loading.value = false + } +} + +watch([peer, () => problem.value?._id], load, { immediate: true }) + + + + + diff --git a/apps/web/src/oj/problem/components/ProblemDrawer.vue b/apps/web/src/oj/problem/components/ProblemDrawer.vue index eb2a7a4..abb95f2 100644 --- a/apps/web/src/oj/problem/components/ProblemDrawer.vue +++ b/apps/web/src/oj/problem/components/ProblemDrawer.vue @@ -1,13 +1,13 @@