Skip to content

Commit d4ac51a

Browse files
committed
新增功能 - AI 生成面试题(调用面试鸭搜索工具完成)
1 parent 9b31f83 commit d4ac51a

File tree

11 files changed

+289
-4
lines changed

11 files changed

+289
-4
lines changed

coder-test-frontend/src/utils/request.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { ElMessage } from 'element-plus'
44
// 创建axios实例
55
const request = axios.create({
66
baseURL: '/api',
7-
timeout: 60000,
7+
timeout: 300000,
88
withCredentials: true // 携带cookie
99
})
1010

coder-test-frontend/src/views/Result.vue

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,38 @@
126126
</el-card>
127127
</div>
128128

129+
<!-- 面试题推荐 -->
130+
<div v-if="recommendedQuestionsContent" class="interview-questions-section">
131+
<el-card>
132+
<template #header>
133+
<div class="section-title">
134+
<el-icon><QuestionFilled /></el-icon>
135+
相关面试题推荐
136+
</div>
137+
</template>
138+
139+
<div class="interview-questions-content">
140+
<div class="markdown-content">
141+
<Viewer :value="recommendedQuestionsContent" :plugins="plugins" />
142+
</div>
143+
144+
<div class="questions-footer">
145+
<el-alert
146+
title="💡 提示"
147+
type="info"
148+
:closable="false"
149+
show-icon
150+
>
151+
<template #default>
152+
以上面试题来自 <a href="https://www.mianshiya.com" target="_blank" class="mianshiya-link">面试鸭</a>,
153+
建议结合本次关卡的知识点进行针对性练习,提升面试通过率!
154+
</template>
155+
</el-alert>
156+
</div>
157+
</div>
158+
</el-card>
159+
</div>
160+
129161
<!-- 底部操作 -->
130162
<div class="bottom-actions">
131163
<el-button size="large" @click="$router.push('/')" class="home-btn">
@@ -154,7 +186,8 @@ import {
154186
Document,
155187
Check,
156188
Reading,
157-
House
189+
House,
190+
QuestionFilled
158191
} from '@element-plus/icons-vue'
159192
import GlobalNavbar from '../components/GlobalNavbar.vue'
160193
@@ -195,6 +228,11 @@ const standardAnswerContent = computed(() => {
195228
return resultData.value?.standardAnswer || ''
196229
})
197230
231+
// 获取推荐面试题内容
232+
const recommendedQuestionsContent = computed(() => {
233+
return resultData.value?.recommendedQuestions || ''
234+
})
235+
198236
// 获取分数样式类
199237
const getScoreClass = (score) => {
200238
if (score >= 80) return 'excellent'
@@ -209,6 +247,7 @@ const getSalaryChangeClass = (change) => {
209247
return 'salary-neutral'
210248
}
211249
250+
212251
// 获取结果详情
213252
const fetchResultDetail = async () => {
214253
const id = route.params.id
@@ -673,6 +712,32 @@ onMounted(() => {
673712
transform: translateY(-2px) !important;
674713
}
675714
715+
/* 面试题推荐区域样式 */
716+
.interview-questions-section {
717+
margin-top: 35px;
718+
}
719+
720+
.interview-questions-content {
721+
padding: 10px 0;
722+
}
723+
724+
.questions-footer {
725+
margin-top: 25px;
726+
padding-top: 20px;
727+
border-top: 1px solid var(--border-light);
728+
}
729+
730+
.mianshiya-link {
731+
color: var(--accent-copper);
732+
text-decoration: none;
733+
font-weight: 600;
734+
transition: color 0.3s ease;
735+
}
736+
737+
.mianshiya-link:hover {
738+
color: var(--accent-gold);
739+
}
740+
676741
@media (max-width: 768px) {
677742
.main-content {
678743
padding: 30px 20px;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-- 为 user_level 表添加推荐面试题字段
2+
ALTER TABLE user_level
3+
ADD COLUMN recommendedQuestions TEXT COMMENT '推荐面试题' AFTER standardAnswer;

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@
8787
<artifactId>langchain4j-reactor</artifactId>
8888
<version>1.4.0-beta10</version>
8989
</dependency>
90+
<!-- Jsoup for web scraping -->
91+
<dependency>
92+
<groupId>org.jsoup</groupId>
93+
<artifactId>jsoup</artifactId>
94+
<version>1.17.2</version>
95+
</dependency>
9096
</dependencies>
9197

9298
<dependencyManagement>

src/main/java/com/yupi/codertestbackend/config/AiConfig.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package com.yupi.codertestbackend.config;
22

3+
import com.yupi.codertestbackend.service.ai.InterviewQuestionSearchTool;
34
import com.yupi.codertestbackend.service.ai.LevelGenerationAiService;
45
import com.yupi.codertestbackend.service.ai.ResultReportAiService;
56
import dev.langchain4j.community.model.dashscope.QwenChatModel;
67
import dev.langchain4j.model.chat.ChatModel;
78
import dev.langchain4j.service.AiServices;
9+
import jakarta.annotation.Resource;
810
import org.springframework.beans.factory.annotation.Value;
911
import org.springframework.context.annotation.Bean;
1012
import org.springframework.context.annotation.Configuration;
@@ -23,6 +25,9 @@ public class AiConfig {
2325

2426
@Value("${langchain4j.dashscope.temperature:0.7}")
2527
private Float temperature;
28+
29+
@Resource
30+
private InterviewQuestionSearchTool interviewQuestionSearchTool;
2631

2732
/**
2833
* 配置 ChatModel Bean
@@ -53,6 +58,7 @@ public LevelGenerationAiService levelGenerationAiService(ChatModel chatModel) {
5358
public ResultReportAiService resultReportAiService(ChatModel chatModel) {
5459
return AiServices.builder(ResultReportAiService.class)
5560
.chatModel(chatModel)
61+
.tools(interviewQuestionSearchTool)
5662
.build();
5763
}
5864
}

src/main/java/com/yupi/codertestbackend/model/dto/ai/ResultReportResponse.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,9 @@ public class ResultReportResponse {
4444
* 标准的、详细的关卡分析和解读
4545
*/
4646
private String standardAnswer;
47+
48+
/**
49+
* 推荐的面试题列表
50+
*/
51+
private String recommendedQuestions;
4752
}

src/main/java/com/yupi/codertestbackend/model/entity/UserLevel.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ public class UserLevel implements Serializable {
6969
*/
7070
private String standardAnswer;
7171

72+
/**
73+
* 推荐面试题
74+
*/
75+
private String recommendedQuestions;
76+
7277
/**
7378
* 创建时间
7479
*/
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
package com.yupi.codertestbackend.service.ai;
2+
3+
import dev.langchain4j.agent.tool.Tool;
4+
import lombok.extern.slf4j.Slf4j;
5+
import org.jsoup.Jsoup;
6+
import org.jsoup.nodes.Document;
7+
import org.jsoup.nodes.Element;
8+
import org.jsoup.select.Elements;
9+
import org.springframework.stereotype.Component;
10+
11+
import java.net.URLEncoder;
12+
import java.nio.charset.StandardCharsets;
13+
import java.util.ArrayList;
14+
import java.util.List;
15+
16+
/**
17+
* 面试题搜索工具
18+
* 从面试鸭网站搜索相关面试题
19+
*/
20+
@Component
21+
@Slf4j
22+
public class InterviewQuestionSearchTool {
23+
24+
private static final String SEARCH_URL_TEMPLATE = "https://www.mianshiya.com/search/all?searchText=%s";
25+
26+
/**
27+
* 搜索面试题
28+
*
29+
* @param query 搜索关键词,应该是技术相关的关键词,比如"Java"、"Spring Boot"、"Vue"等
30+
* @return 搜索到的面试题列表,格式为:题目标题 - 题目链接
31+
*/
32+
@Tool("根据技术关键词搜索相关的面试题,帮助用户获得针对性的面试题推荐")
33+
public String searchInterviewQuestions(String query) {
34+
try {
35+
log.info("开始搜索面试题,关键词:{}", query);
36+
37+
// URL编码搜索关键词
38+
String encodedQuery = URLEncoder.encode(query, StandardCharsets.UTF_8);
39+
String searchUrl = String.format(SEARCH_URL_TEMPLATE, encodedQuery);
40+
41+
// 使用Jsoup获取网页内容
42+
Document doc = Jsoup.connect(searchUrl)
43+
.userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
44+
.timeout(10000)
45+
.get();
46+
47+
// 查找题目列表容器
48+
Element questionTableView = doc.selectFirst(".question-table-view");
49+
if (questionTableView == null) {
50+
log.warn("未找到题目列表容器,关键词:{}", query);
51+
return "未找到相关面试题,建议尝试其他关键词。";
52+
}
53+
54+
// 提取题目信息
55+
Elements questionItems = questionTableView.select(".ant-table-row");
56+
List<String> questions = new ArrayList<>();
57+
58+
int count = 0;
59+
for (Element row : questionItems) {
60+
if (count >= 3) { // 限制返回数量
61+
break;
62+
}
63+
64+
try {
65+
// 获取所有列
66+
Elements cells = row.select(".ant-table-cell");
67+
68+
// 确保列数足够(至少4列:空列、标题列、难度列、标签列)
69+
if (cells.size() < 4) {
70+
continue;
71+
}
72+
73+
// 第二列:题目标题和链接(索引为1)
74+
Element titleCell = cells.get(1);
75+
String title = titleCell.text().trim();
76+
if (title.isEmpty()) {
77+
continue;
78+
}
79+
80+
// 提取题目链接
81+
Element linkElement = titleCell.selectFirst("a");
82+
String link = "";
83+
if (linkElement != null) {
84+
String href = linkElement.attr("href");
85+
if (!href.isEmpty()) {
86+
link = href.startsWith("http") ? href : "https://www.mianshiya.com" + href;
87+
}
88+
}
89+
90+
// 第三列:题目难度(索引为2)
91+
String difficulty = cells.get(2).text().trim();
92+
93+
// 第四列:题目标签(索引为3)
94+
String tags = cells.get(3).text().trim();
95+
96+
// 构建题目信息
97+
StringBuilder questionInfo = new StringBuilder();
98+
questionInfo.append(title);
99+
100+
// 添加难度信息
101+
if (!difficulty.isEmpty()) {
102+
questionInfo.append(" [难度: ").append(difficulty).append("]");
103+
}
104+
105+
// 添加标签信息
106+
if (!tags.isEmpty()) {
107+
questionInfo.append(" [标签: ").append(tags).append("]");
108+
}
109+
110+
// 添加链接
111+
if (!link.isEmpty()) {
112+
questionInfo.append(" - ").append(link);
113+
}
114+
115+
questions.add(questionInfo.toString());
116+
count++;
117+
118+
} catch (Exception e) {
119+
log.warn("解析题目信息时出错:{}", e.getMessage());
120+
continue;
121+
}
122+
}
123+
124+
if (questions.isEmpty()) {
125+
log.warn("未解析到有效的面试题,关键词:{}", query);
126+
return "未找到相关面试题,建议尝试其他关键词。";
127+
}
128+
129+
log.info("成功搜索到{}道面试题,关键词:{}", questions.size(), query);
130+
131+
// 构建返回结果
132+
StringBuilder result = new StringBuilder();
133+
result.append("找到以下相关面试题:\n\n");
134+
for (int i = 0; i < questions.size(); i++) {
135+
result.append(i + 1).append(". ").append(questions.get(i)).append("\n");
136+
}
137+
138+
return result.toString();
139+
140+
} catch (Exception e) {
141+
log.error("搜索面试题时发生错误,关键词:{},错误:{}", query, e.getMessage());
142+
return "搜索面试题时发生错误,请稍后再试。";
143+
}
144+
}
145+
}

src/main/java/com/yupi/codertestbackend/service/impl/UserLevelServiceImpl.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ public UserLevel submitAnswer(SubmitAnswerRequest submitAnswerRequest, String us
102102
userLevel.setReason(aiResponse.getReason());
103103
userLevel.setTrueOptions(JSONUtil.toJsonStr(aiResponse.getTrueOptions()));
104104
userLevel.setStandardAnswer(aiResponse.getStandardAnswer());
105+
userLevel.setRecommendedQuestions(aiResponse.getRecommendedQuestions());
105106

106107
// 4. 保存结果
107108
boolean saveResult = this.save(userLevel);

src/main/resources/prompts/result-report-system.txt

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
8. 生成系统架构图的Mermaid代码(用于帮助用户理解整体架构)
2020
9. 生成核心业务流程图的Mermaid代码(用于帮助用户理解关键流程)
2121
10. 基于用户当前薪资水平和作答情况的个性化学习建议
22+
11. 推荐技术相关的面试题(使用面试题搜索工具搜索10-30道相关面试题)
2223

2324
## 详细要求补充
2425

@@ -63,6 +64,28 @@
6364
> - 📄 老鱼简历 (laoyujianli.com) - 专业简历制作工具,让HR眼前一亮
6465
> - 🦆 面试鸭 (mianshiya.com) - 程序员面试刷题神器,高效通过技术面试
6566

67+
### 面试题推荐要求
68+
69+
**必须包含以下内容**:
70+
1. **技术关键词提取**:从关卡内容和正确选项中提取 5 - 10 个最符合用户当前应该学习的核心技术关键词
71+
2. **面试题搜索**:使用面试题搜索工具,针对每个关键词搜索相关面试题
72+
3. **关联度判断**:评估搜索到的面试题与关卡技术点的关联度,只推荐高关联度的题目
73+
4. **题目筛选**:从搜索结果中筛选出10-30道最相关、最有价值的面试题
74+
5. **分类整理**:按技术栈或难度等维度对推荐的面试题进行分类整理
75+
76+
**面试题推荐格式要求**:
77+
- 每道题目包含:题目标题、技术标签、题目链接(如有)
78+
- 按技术分类组织,如:Java基础、Spring框架、数据库、前端技术等
79+
- 优先推荐与用户当前薪资水平匹配的题目难度
80+
- 如果某个技术关键词搜索不到相关题目,应该说明并建议其他学习资源
81+
82+
每道具体题目的参考格式如下,注意在题目序号后面使用中文括号来展示
83+
84+
- 1042)让你设计一个文件上传系统,怎么设计? [难度: 困难] [标签: VIP 后端 系统设计 场景题] - https://www.mianshiya.com/question/1801086758341124097
85+
- 169)如何设计一个点赞系统? [难度: 中等] [标签: VIP 系统设计 场景题] - https://www.mianshiya.com/question/1772882950057148418
86+
- 724)让你设计一个消息队列,怎么设计? [难度: 困难] [标签: VIP 后端 系统设计 场景题] - https://www.mianshiya.com/question/1795654204034162689
87+
88+
6689
必须只生成我要求的内容,不要生成任何多余的内容!
6790

6891
参考的自然语言风格:
@@ -87,6 +110,7 @@
87110
"Vue 框架",
88111
"NPM 包管理器"
89112
],
90-
"standardAnswer": "...一段 Markdown 文本,包括详细的关卡分析和解读、Mermaid架构图和流程图、个性化学习建议"
113+
"standardAnswer": "...一段 Markdown 文本,包括详细的关卡分析和解读、Mermaid架构图和流程图、个性化学习建议",
114+
"recommendedQuestions": "...推荐的面试题列表,按技术分类整理"
91115
}
92116
```

0 commit comments

Comments
 (0)