原来 /ai/detail 把区间内做出来的每道题连排名带等级整份下发,一个活跃学生一年几百道, 一次请求就是几百 KB,而表格一屏只看得到二十行。 拆成两支: - GET /ai/detail 只留聚合(solvedCount、difficulty、tags、attempts、activity、errors…) - GET /ai/solved?offset&limit 按首次通过时间升序分页给逐题明细 排名只跟当前这批题有关,所以分页那支只算一页的 problemIds,不必把整年算一遍。抽出 firstAcQuery / buildSolved / listSolved 三个函数,detail 和分页两边共用。 前端相应改动: - 难度分布改读后端的 difficulty 聚合(本来就在下发,之前是从逐题列表里现数的) - 几次做对改读新的 attempts 数组(每道题的尝试次数);tooltip 里的题名去掉了 —— 明细是分页拿的,不该为了一个 tooltip 把全量拉回来 - Overview 用 solvedCount - SolvedTable 的代码提交那张走 remote 分页,流程图那张仍是本地分页(一个 OJ 的 流程图题就那么几道,全量下发没问题);换时间范围回到第一页 - 表格原来是 max-height 1500 的滚动区,现在由分页兜住,滚动条和分页器不再并存 /ai/analysis 的 prompt 仍然带逐题明细(少了它模型只剩聚合数字),但顺手卡了 200 条 —— 以前是整份 solved 无上限塞进去,题做得多的学生一次调用能顶好几倍 token。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LZuPwqDmLEiK9zgQ9z9sVn
This commit is contained in:
1 parent
4d7be969d9
commit
8a88fe8d99
9 files changed
+225
-89
No files matched your search
@@ -1,10 +1,11 @@
|
||||
import type { DetailsData, DurationData } from "utils/types"
|
||||
import type { DetailsData, DurationData, SolvedProblem } from "utils/types"
|
||||
import { aiStreamError, consumeJSONEventStream } from "utils/stream"
|
||||
import {
|
||||
getAIDetailData,
|
||||
getAIDurationData,
|
||||
getAIHeatmapData,
|
||||
getAIPinnedReport,
|
||||
getAISolved,
|
||||
} from "../api"
|
||||
|
||||
export const useAIStore = defineStore("ai", () => {
|
||||
@@ -23,7 +24,8 @@ export const useAIStore = defineStore("ai", () => {
|
||||
tags: {},
|
||||
difficulty: {},
|
||||
contestCount: 0,
|
||||
solved: [],
|
||||
solvedCount: 0,
|
||||
attempts: [],
|
||||
flowcharts: [],
|
||||
activity: [],
|
||||
errors: [],
|
||||
@@ -31,10 +33,17 @@ export const useAIStore = defineStore("ai", () => {
|
||||
})
|
||||
const heatmapData = ref<{ timestamp: number; value: number }[]>([])
|
||||
|
||||
// 解题明细走服务端分页:一个活跃学生一年几百道题,整份跟着 detail 一起下发没必要
|
||||
const solvedRows = ref<SolvedProblem[]>([])
|
||||
const solvedTotal = ref(0)
|
||||
const solvedPage = ref(1)
|
||||
const solvedPageSize = ref(20)
|
||||
|
||||
const loading = reactive({
|
||||
fetching: false, // 合并 details 和 duration 的 loading
|
||||
ai: false,
|
||||
heatmap: false,
|
||||
solved: false,
|
||||
})
|
||||
|
||||
const mdContent = ref("")
|
||||
@@ -48,12 +57,13 @@ export const useAIStore = defineStore("ai", () => {
|
||||
)
|
||||
detailsData.start = res.start
|
||||
detailsData.end = res.end
|
||||
detailsData.solved = res.solved
|
||||
detailsData.grade = res.grade
|
||||
detailsData.className = res.className
|
||||
detailsData.tags = res.tags
|
||||
detailsData.difficulty = res.difficulty
|
||||
detailsData.contestCount = res.contestCount
|
||||
detailsData.solvedCount = res.solvedCount
|
||||
detailsData.attempts = res.attempts
|
||||
detailsData.activity = res.activity
|
||||
detailsData.errors = res.errors
|
||||
detailsData.rankScope = res.rankScope
|
||||
@@ -69,6 +79,25 @@ export const useAIStore = defineStore("ai", () => {
|
||||
durationData.value = res
|
||||
}
|
||||
|
||||
async function fetchSolved(page = solvedPage.value) {
|
||||
if (!rangeStart.value || !rangeEnd.value) return
|
||||
loading.solved = true
|
||||
try {
|
||||
const res = await getAISolved(
|
||||
rangeStart.value,
|
||||
rangeEnd.value,
|
||||
(page - 1) * solvedPageSize.value,
|
||||
solvedPageSize.value,
|
||||
targetUsername.value || undefined,
|
||||
)
|
||||
solvedRows.value = res.results
|
||||
solvedTotal.value = res.total
|
||||
solvedPage.value = page
|
||||
} finally {
|
||||
loading.solved = false
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchHeatmapData() {
|
||||
loading.heatmap = true
|
||||
const res = await getAIHeatmapData(targetUsername.value || undefined)
|
||||
@@ -83,11 +112,14 @@ export const useAIStore = defineStore("ai", () => {
|
||||
) {
|
||||
rangeStart.value = start
|
||||
rangeEnd.value = end
|
||||
// 换时间范围就回到第一页,否则停在第 5 页但新范围只有两页
|
||||
solvedPage.value = 1
|
||||
loading.fetching = true
|
||||
try {
|
||||
await Promise.all([
|
||||
fetchDetailsData(start, end),
|
||||
fetchDurationData(end, duration),
|
||||
fetchSolved(1),
|
||||
])
|
||||
} finally {
|
||||
loading.fetching = false
|
||||
@@ -196,6 +228,11 @@ export const useAIStore = defineStore("ai", () => {
|
||||
return {
|
||||
fetchAnalysisData,
|
||||
fetchHeatmapData,
|
||||
fetchSolved,
|
||||
solvedRows,
|
||||
solvedTotal,
|
||||
solvedPage,
|
||||
solvedPageSize,
|
||||
fetchAIAnalysis,
|
||||
fetchPinnedReport,
|
||||
simulatePinnedStream,
|
||||
|
||||
Reference in new issue
Block a user