Skip to content

Commit 809e1d9

Browse files
committed
feat: implement student wrong book management with Socratic questioning and status tracking
1 parent 824504a commit 809e1d9

6 files changed

Lines changed: 18 additions & 57 deletions

File tree

apps/api/src/main/java/com/edunexus/api/config/AppConfig.java

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
package com.edunexus.api.config;
22

33
import java.net.URI;
4-
import java.util.concurrent.ThreadPoolExecutor;
54
import org.springframework.beans.factory.annotation.Value;
65
import org.springframework.context.annotation.Bean;
76
import org.springframework.context.annotation.Configuration;
7+
import org.springframework.core.task.SimpleAsyncTaskExecutor;
88
import org.springframework.core.task.TaskExecutor;
9-
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
109
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
1110
import org.springframework.security.crypto.password.PasswordEncoder;
1211
import org.springframework.web.servlet.config.annotation.CorsRegistry;
@@ -26,29 +25,15 @@ public PasswordEncoder passwordEncoder() {
2625

2726
@Bean(name = "documentIngestExecutor")
2827
public TaskExecutor documentIngestExecutor() {
29-
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
30-
executor.setCorePoolSize(2);
31-
executor.setMaxPoolSize(4);
32-
executor.setQueueCapacity(100);
33-
executor.setThreadNamePrefix("doc-ingest-");
34-
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
35-
executor.setWaitForTasksToCompleteOnShutdown(true);
36-
executor.setAwaitTerminationSeconds(30);
37-
executor.initialize();
28+
SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor("doc-ingest-");
29+
executor.setVirtualThreads(true);
3830
return executor;
3931
}
4032

4133
@Bean(name = "chatStreamExecutor")
4234
public TaskExecutor chatStreamExecutor() {
43-
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
44-
executor.setCorePoolSize(4);
45-
executor.setMaxPoolSize(8);
46-
executor.setQueueCapacity(200);
47-
executor.setThreadNamePrefix("chat-stream-");
48-
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
49-
executor.setWaitForTasksToCompleteOnShutdown(true);
50-
executor.setAwaitTerminationSeconds(15);
51-
executor.initialize();
35+
SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor("chat-stream-");
36+
executor.setVirtualThreads(true);
5237
return executor;
5338
}
5439

@@ -63,7 +48,7 @@ public void addCorsMappings(CorsRegistry registry) {
6348
.allowedOriginPatterns(allowedOriginPatterns)
6449
.allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS")
6550
.allowedHeaders("*")
66-
.exposedHeaders("X-Request-Id");
51+
.exposedHeaders("X-Request-Id", "X-Trace-Id");
6752
}
6853
};
6954
}

apps/web/lint_report.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

apps/web/src/components/common/MarkdownPreview.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ const renderedHtml = computed(() => {
126126
</script>
127127

128128
<template>
129+
<!-- eslint-disable-next-line vue/no-v-html -->
129130
<div class="markdown-body" :class="{ 'markdown-plain': plain }" v-html="renderedHtml" />
130131
</template>
131132

apps/web/src/pages/student/StudentProfilePage.vue

Lines changed: 3 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ import {
2121
User as UserIcon,
2222
AlertTriangle,
2323
Sparkles,
24-
TrendingUp,
25-
Network
24+
TrendingUp
2625
} from "lucide-vue-next";
2726
import { getMe } from "../../features/auth/api/auth.service";
2827
import {
@@ -146,36 +145,6 @@ const recordsLineOption = computed(() => {
146145
};
147146
});
148147
149-
const weakPointsBarOption = computed(() => ({
150-
tooltip: { trigger: "axis", axisPointer: { type: "shadow" } },
151-
grid: { left: 24, right: 36, top: 20, bottom: 24, containLabel: true },
152-
xAxis: { type: "value" },
153-
yAxis: {
154-
type: "category",
155-
data: topWeakPoints.value.map((item) => item.knowledgePoint || "未命名知识点")
156-
},
157-
series: [
158-
{
159-
type: "bar",
160-
data: topWeakPoints.value.map((item) => Number(item.errorRate || 0)),
161-
barWidth: 18,
162-
itemStyle: {
163-
borderRadius: [0, 999, 999, 0],
164-
color: {
165-
type: "linear",
166-
x: 0,
167-
y: 0,
168-
x2: 1,
169-
y2: 0,
170-
colorStops: [
171-
{ offset: 0, color: "#fb7185" },
172-
{ offset: 1, color: "#ef4444" }
173-
]
174-
}
175-
}
176-
}
177-
]
178-
}));
179148
180149
async function loadProfile(): Promise<void> {
181150
loading.value = true;
@@ -224,12 +193,14 @@ const topologyOption = computed(() => {
224193
{
225194
type: "graph",
226195
layout: "force",
196+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
227197
data: topologyData.value.nodes.map((n: any) => ({
228198
id: n.id,
229199
name: n.name,
230200
symbolSize: n.status === "weak" ? 40 : 25,
231201
itemStyle: { color: n.status === "weak" ? "#ef4444" : "#10b981" }
232202
})),
203+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
233204
links: topologyData.value.edges.map((e: any) => ({
234205
source: e.source,
235206
target: e.target,

apps/web/src/pages/student/StudentWrongBookPage.vue

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,9 @@ async function openSocratic(row: WrongBookEntryVO) {
183183
role: "ai",
184184
text: String(res.data.probe_question || res.data.summary || JSON.stringify(res.data))
185185
});
186-
} catch (err: any) {
187-
message.error("追问失败: " + err.message);
186+
} catch (err) {
187+
const msg = err instanceof Error ? err.message : String(err);
188+
message.error("追问失败: " + msg);
188189
} finally {
189190
socraticState.loading = false;
190191
}
@@ -212,10 +213,11 @@ async function sendSocraticReply() {
212213
if (socraticState.round >= 3 || res.data.encouragement) {
213214
socraticState.isFinished = true;
214215
}
215-
} catch (err: any) {
216+
} catch (err) {
216217
socraticState.round -= 1;
217218
socraticState.messages.pop();
218-
message.error("回复失败: " + err.message);
219+
const msg = err instanceof Error ? err.message : String(err);
220+
message.error("回复失败: " + msg);
219221
} finally {
220222
socraticState.loading = false;
221223
}

apps/web/vite.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import vue from "@vitejs/plugin-vue";
33
import { defineConfig } from "vite";
44

55
export default defineConfig({
6+
envDir: "../../",
67
plugins: [vue()],
78
resolve: {
89
alias: {

0 commit comments

Comments
 (0)