搭建环境
JDK选择
- JDK选择1.8版本,因为Java8是目前应用最广泛的版本,兼容性很好
Maven的构建
选择Maven原因
- Maven是一个强大的项目管理和构建工具,目前的应用非常广泛,它可以做到自动下载依赖、统一项目结构(大家都默认的规范,别人更容易看懂项目结构)、一键构建(写一条命令,就可以完成编译、测试、打包等步骤)
搭建步骤
- 在IDEA中直接选择new一个project,选择Maven
- 填写groupId(项目组标识)、artifactId(项目名称)
MySQL数据库的搭建
作用
- 用于存放用户数据、题库信息、评分数据等信息
搭建步骤
安装与搭建
- 下载安装VC++运行库
- 下载安装MySQL8.0版本
- 初始化数据目录,临时密码: pjRkkTCYf5:n
- 登陆后修改临时密码为123456
数据库的具体实现
利用SQL语法创建项目所需的数据库和表
SQL语法入门
- 创建数据库: CREATE DATABASE 数据库名;(最好不要用中文的数据库名)
- 使用数据库: USE 数据库名;
- 查看表结构:DESCRIBE 表名
- 常用的数据类型
- INT/INTEGER:整数型,范围为±21亿
- BIGINT:大整数型
- TINYINT: 小整数型,常用来表示状态(0/1) ,范围为-128~127
- VARCHAR(n):可变长度的字符串,最多n个字符
- CHAR(n):固定长度字符串,必须存储n个字符
- TEXT:长文本,最多65535个字符,可以用来存储文本、答案内容等
- DATE:存储日期,格式为年-月-日
- DATETIME:存储日期和时间(2026-03-30 14:30:02)
- TIMESTAMP:存储日期和时间,但会自动根据时区转换
- BOOLEAN/BOOL:布尔型,实际上等同于TINYINT(1),0为false,1为true
- FLOAT/DOUBLE:小数,精度较低
- DECIMAL(m,n):精确小数,如DECIMAL(10,4)表示10位数字,其中4位小数
- 表的创建 :
CREATE TABLE 表名( 列名 数据类型 [约束], ... ); - 主键 (id):用于表示数据,每一条数据虽然可能有同一个用户名,但只会有唯一的主键,便于后续的增删查改,通常使用自增的整数来作为主键,同时id只保证唯一,不保证顺序。
id INT AUTO_INCREMENT PRIMARY KEY其中,INT表示主键的类型为整数型,AUTO_INCREMENT 表示自动递增,PRIMARY KEY 声明该字段为主键 - 常用约束条件
- NOT NULL :非空,约束字段不能为空
- UNIQUE :唯一,约束该列的字段不能相同
- PRIMARY KEY:主键,表示每一行,它自动拥有NOT NULL和UNIQUE的约束
- DEFAULT :默认值,当插入列的时候没有给该列指定值时,则自动填入预设的默认值,例如
ALTER users ADD user_email VARCHAR(50) DEFAULT 'unknown@email.com' - FOREIGN KEY :外键 ,用来连接两个表。外键列的值必须是另一个表主键列中存在的值,保证引用的完整性。例如,在"用户评分"表中,可以用user_id列引用users表的id列,保证每一个评分都可以对应一个真实的用户。语法:
FOREIGN KEY (本表列名) REFERENCES 另一个表名 (另一个表的列名) - CHECK :检查,用于限制列中数据的范围或格式。用法:列名 数据类型 CHECK(检查条件)
- AUTO_INCREMENT:自动递增,常与PRIMARY KEY连用,用于在一列生成递增的数据
- 在表中添加新的列:ALTER TABLE 表名 ADD 列名 数据类型 约束条件
- 逻辑符号:使用AND或者BETWEEN...AND...(length<=5 AND length>=1或者length BETWEEN 1AND 5)
- 在表中添加数据 :INSERT 语句:
INSERT INTO 表名 (列名1,列名2,列名3,...) VALUES (值1,值2,值3,...);插入多行数据:INSERT INTO 表名 (列名) VALUES (数据1),(数据2),(数据3)...;
使用SQL创建数据库
CREATE DATABASE voice_mock;创建一个名为 voice_mock 的数据库USE voice_mock;进入该数据库CREATE TABLE user( ... );创建一个名为user的表id INT AUTO_INCREMENT PRIMARY KEY设置主键为递增的整数(1,2,3...)user_id VARCHAR(15) NOT NULL UNIQUE创建列,列名为user_id,数据类型为VARCHAR(50),约束条件为NOT NULL和UNIQUE- 同样的方法创建user_password、user_email等列,完善表的结构
- 创建一个questions表,其中包括
- 问题的序号(主键)
- 问题的题干(TEXT类型,不能为空)
- 问题的难度(INT类型,在1~5之间,不能为空)
- 问题的分类(单独创建一张 category 表(分类表),然后在 questions 表中用 category_id 关联(外键))
将数据库与项目连接起来(使用原生JDBC)
- 在maven中添加依赖:
java
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
</dependency>
-
在src源代码中加入TestDB类,来确认数据库是否连接成功,TestDB类具体内容如下
- 导入JDBC所需要的类
import java.sql.Connection;表示与数据库的连接import java.sql.DriverManger;负责管理数据库驱动,帮助建立连接import java.sql.SQLException;处理数据库操作时可能会出现的异常
- 定义连接信息
String url = "jdbc:mysql://localhost:3306/voice_mock?useSSL=false&serverTimezone=UTC";其中jdbc:mysql://是固定协议头,它表示使用的是MySQL驱动;localhost:3306是数据库服务器的地址和端口(默认本机端口3306);voice_mock是所连接的数据库名;?useSSL=false&serverTimezone=UTC是参数,表示关闭SSL,设置时区避免时间转换错误,这两个参数可以避免很多报错String user = "root";数据库用户名String password="123456";数据库的密码
- 建立连接(核心)
java
try (Connection conn = DriverManager.getConnection(url, user, password)) {
System.out.println("数据库连接成功!");
} catch (SQLException e) {
System.out.println("连接失败:" + e.getMessage());
e.printStackTrace();
}
连接的本质是Java程序(客户端)与数据库(服务端)建立网络连接,DriverManager.getConnection(url,use,password)的作用是解析URL,识别出数据库是MySQL数据库并建立TCP链接,随后发送用户名和密码进行验证,若验证通过就会返回一个Connection的对象,用于与数据库进行交互。
完整代码如下:
java
package com.nwuby.test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class TestDB {
public static void main(String[] args) {
// 连接信息
String url = "jdbc:mysql://localhost:3306/voice_mock?useSSL=false&serverTimezone=UTC";
String user = "root";
String password = "123456"; // 改成你的密码
// 尝试连接
try (Connection conn = DriverManager.getConnection(url, user, password)) {
System.out.println("数据库连接成功!");
} catch (SQLException e) {
System.out.println("连接失败:" + e.getMessage());
e.printStackTrace();
}
}
}
- 验证数据库可以正常连接后,开始将数据库的数据与客户端进行连接
- 创建
DBUtil类,DBUtil类用来将调用数据库的细节集中起来,其他地方需要连接数据库时只需要调用即可,不需要再重复写DriverManager.getConnection()- 定义连接常量:将对应数据库的url,use,password定义出来,便于连接的操作
- 构造静态的
getConnection方法,返回Connection对象,如果出错则抛出异常,使用静态方法可以直接通过类名调用
java
package com.voiceMock.util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
//DBUtil类用来将调用数据库的细节集中起来,其他地方需要连接数据库时只需要调用即可,不需要再重复写DriverManager.getConnection()
public class DBUtil {
//定义连接常量
private static String url = "jdbc:mysql://localhost:3306/voice_mock?useSSL=false&serverTimezone=UTC";
private static String user = "root";
private static String password = "123456";
//提供获取连接的方法,使用静态方法,可以通过类名调用;返回Connection对象,若连接失败抛出SQLException异常
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, user, password);
}
}
- 创建Question类,用于存储并封装从数据库中得到的数据,并构建getter和setter方法,便于后续的操作
java
package com.voiceMock.entity;
public class Question {
private int id;
private String title;
private int difficulty;
private int categoryId;
public Question(){
}
public Question(int id, String title, int difficulty, int categoryId) {
this.id = id;
this.title = title;
this.difficulty = difficulty;
this.categoryId = categoryId;
}
public void setDifficulty(int difficulty) {
this.difficulty = difficulty;
}
public void setId(int id) {
this.id = id;
}
public void setTitle(String title) {
this.title = title;
}
public void setCategoryId(int categoryId) {
this.categoryId = categoryId;
}
public int getId() {
return id;
}
public String getTitle() {
return title;
}
public int getDifficulty() {
return difficulty;
}
public int getCategoryId() {
return categoryId;
}
}
- 创建QuestionDAO类,用于完成数据库与Java程序的具体交互
- 构造getQuestions方法,将返回值类型设置为
List<Question>,返回由Question对象组成的列表 - 在方法内定义局部变量questions,用动态数组进行实现,用于存储从数据库中拿到的问题
- 书写sql命令符,用于对执行相对应的操作
- 使用try-with-resources获取资源,在try代码块中写while循环,将question存入questions列表
- 采用PreparedStatement而不用Statement的原因,安全:参数化查询防止 SQL 注入;性能:数据库可预编译 SQL,多次执行时更快。;代码清晰:避免字符串拼接
- 返回questions
- 构造getQuestions方法,将返回值类型设置为
java
package com.voiceMock.dao;
import com.voiceMock.entity.Question;
import com.voiceMock.util.DBUtil;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
//从数据库中取出数据并存储
public class QuestionDAO {
public List<Question> getQuestions() {
List<Question> questions=new ArrayList<>();//将questions设置为局部变量
String sql="SELECT id,title,difficulty,category_id FROM questions";//书写sql命令符
// 使用try-with-resources获取资源,try块执行结束后,这三个资源都会调用各自的close方法,释放连接和资源
try(Connection conn = DBUtil.getConnection(); //获取一个与数据库的连接
PreparedStatement pstmt =conn.prepareStatement(sql);//从已有的连接中创建一个PreparedStatement对象,负责将SQL语句发送给数据库,并接受返回的结果
ResultSet rs =pstmt.executeQuery()){//执行SQL语句,并返回结果集,返回的结果封装在ResultSet对象中
while(rs.next()){//移动游标到下一行,有数据时返回true
//rs.getInt("列名")获取当前行指定列的值
int id=rs.getInt("id");
String title=rs.getString("title");
int difficulty=rs.getInt("difficulty");
int categoryId=rs.getInt("category_id");
Question question=new Question(id,title,difficulty,categoryId);
questions.add(question);
}
}catch (SQLException e){
e.printStackTrace();//如果数据库操作出错,会报出详细错误
}
return questions;
}
}
UI界面的开发
- 首先实现核心功能(出题、答题、提交),在界面中划分出出题区与答题区,并添加对应按钮。
- 将数据库中的题目加载到界面上
- 添加三个成员变量
questionDAO:负责从数据库读取题目;questionList:存放从数据库读取的所有题目;currentQuestion:当前正在显示的题目对象。 - 在MainFrame的构造函数中,初始化questionDAO,从数据库中获取题目
- 添加成员变量JTextArea answerArea和questionArea,分别用于表示答案区与问题区
- 构造一个显示题目的方法,逻辑为:从 questionList 中随机选一个索引,获取对应的 Question 对象,并设置到 currentQuestion,将题目标题显示在 questionArea 中,最终清空答案输入区。
- 添加事件监听器,在点击"下一题"按钮时,问题区展示下一道题目。
调用AI进行评分
- 获取并配置API Key
- 首先前往火山引擎注册登录认证,并获取名称与API Key
- 配置API Key到环境变量:在cmd命令中,输入
setx ARK_API_KEY "6beb8ac9-e44e-497d-91d9-5b88874f4633" - 安装第三方SDK:在maven的pom.xml中添加依赖
<dependency> <groupId>com.volcengine</groupId> <artifactId>volcengine-java-sdk-ark-runtime</artifactId> <version>LATEST</version> </dependency>
- 创建AIService类,其作用为:接收题目与回答内容,构造一个合适的prompt,调用豆包的API发送请求,接收AI返回的评分和解析并返回给调用方
- 首先在构造方法中构建ArkService实例,ArkService是豆包SDK的核心入口,用来调用豆包大模型。要用到的语法有:
ArkService.builder(),一种设计模式,可链式设置参数.apiKey(),传入你的apiKey,用于认证.built(),创建真正的ArkService对象
- 其次构造方法
getScoreAndFeedback,负责实现评分的具体功能
- 构造prompt,给出一段清晰的提示词,让AI明白自己的任务是什么,并将题目与用户回答也并入其中
java
String prompt = String.format(
"你是一个面试官。请根据以下题目,对用户的回答进行评分(0-100分),并给出解析和改进建议。\n\n题目:%s\n用户回答:%s\n\n输出格式:\n评分:XX\n解析:...",
question, answer
);
- 根据豆包API的要求,将提示词包装为ChatMessage对象
java
ChatMessage userMessage = ChatMessage.builder()
.role(ChatMessageRole.USER)
.content(prompt)
.build();
- 构建请求对象 ChatCompletionRequest,设置model为已申请并开通的大模型,将用户的消息存储在Arrays中
java
ChatCompletionRequest request = ChatCompletionRequest.builder()
.model("doubao-seed-2-0-pro-260215")
.messages(Arrays.asList(userMessage))
.build();
- 调用API并处理结果,用try-catch处理异常结果
java
Object content = service.createChatCompletion(request)
.getChoices().get(0)
.getMessage().getContent();
- 在MainFrame中集成AIService
- 在MainFrame类中添加成员变量aiService
- 在构造函数中初始化aiService
- 修改按钮监听器,调用AI服务
根据用户的答题结果动态调整题目权重
- 首先需要将用户回答问题的评分记录下来
- 在数据库中创建user_answers表,用于存储用户的回答数据
- 在com.voiceMock.entity包下创建UserAnswer实体类
- 在com.voiceMock.dao包下创建UserAnswerDao类,用于修改调用数据库
- 在MainFrame中添加一个私有方法
extractScore,用于接收AI返回的分数 - 修改MainFrame中的submitBtn,使得在点击提交按钮后保存此次的答题记录至数据库
- 根据用户在各题目种类的评分情况,降低高分类题目的出现概率,提高低分类题目的概率
- 在UserAnswerDAO类中加入统计方法
getAverageScoreByCategory,使用图来储存 - 在QuestionDAO中加入找题方法
getAdaptiveQuestion,将得分低的题目类别的权重增加 - 在MainFrame中的showRandomQuestion 方法替换成 showAdaptiveQuestion,达到个性化出题的效果
AI生成个性学习计划
- 创建categoryDAO类,用来查询categories表中的名称,在其中用图来存储分类的ID与名称
- 在AIService中添加generateStudyPlan方法,将用户的数据从数据库中拿出,并且交给AI,设置prompt来得到个性学习计划
- 在MainFrame中添加创建学习计划按钮
用户注册与登录功能
- 创建User实体类
- 创建UserDAO类,实现注册与登陆的方法,将用户数据储存在mysql的users表中
- 创建登录窗口的UI,并添加相关按钮