基于Jdk17+SpringBoot3AI智慧教育平台,告别低效学习,AI精准导学 + 新架构稳跑
在线职业教育早已不是 "上传视频 + 卖课" 的简单模式。当用户规模突破百万、课程交互需求激增、AI 技术重塑学习体验时,平台需要一套能扛住高并发、支撑智能化、适配技术迭代的底层架构。AI智慧教育平台正是在这样的背景下,从 逐步升级,不仅完成了技术栈的迭代,更构建了覆盖 "学 - 教 - 管 - 营" 全链路的在线教育生态。

核心技术特点解析
AI智教育平基于 
jdk17+SpringBoot3.3.5+SpringCloudAlibaba
进行升级,聚焦在线职业技能培训场景,融合了分布式架构、高并发处理、AI 智能服务等核心技术,具体特点如下:
1. 分布式架构与高可用设计
分布式锁与任务调度
基于自定义注解和 Redisson 实现分布式锁,解决跨服务资源竞争问题;集成 XXL-JOB 实现分布式任务调度,支持定时任务的集群化管理(如课程评分统计、日志清理等)。
服务治理
依托 SpringCloudAlibaba 生态,使用 Nacos 进行服务注册与配置管理,Sentinel 实现流量控制与熔断降级,Seata 保障分布式事务一致性。
全链路可观测性
通过 Prometheus+Grafana 收集系统指标(如接口响应时间、服务调用量),结合 SkyWalking 实现分布式链路追踪,快速定位问题。
2. 高性能数据处理与存储
多级缓存策略
采用 Caffeine 本地缓存(热点数据如课程分类)+Redis 分布式缓存(用户会话、学习进度),减少数据库访问压力;通过 Redis 合并写请求 + 定时任务异步持久化,优化高并发写入场景。
高效存储方案
使用 Minio 实现对象存储,支持大文件分片上传、秒传和断点续传(适配视频、课件等资源);集成 InfluxDB 存储日志与数据埋点信息,满足高写入、高查询性能需求。
数据库优化
针对高并发场景(如课程报名、优惠券领取),通过分库分表(如排行榜数据)、LUA 脚本(优惠券秒杀)保障数据一致性与性能。
3. 消息与异步处理
可靠消息与延迟队列
基于 RabbitMQ 实现可靠消息投递(如订单状态变更通知)和延迟消息(如课程过期提醒),结合 DelayQueue 处理本地延迟任务,提升系统异步处理能力。
并发任务处理
使用 CompletableFuture+CountDownLatch 实现多任务并行处理(如课程数据统计、多平台支付结果校验),提高任务执行效率。
日志削峰
通过网关全局过滤器拦截请求日志,结合 Redis 临时存储 + MQ 异步消费,解决高并发下日志写入压力。
4. 业务场景深度优化
视频服务
整合腾讯云 VOD,实现视频加密(防盗链)、点播、审核及雪碧图生成,同时提供高并发高精度的视频进度记录与回放方案。
支付与交易
集成支付宝、微信支付的多平台支付系统,支持订单拆单退款;基于 Spring 状态机优化订单状态流转,确保交易流程清晰可控。
互动功能
实现通用问答(评论)、高性能点赞系统、多人在线群聊(WebSocket),并通过 DFA 有穷自动机算法过滤聊天违禁词。
营销系统
支持高并发优惠券领取(秒杀方案)、智能优惠券叠加推荐(MapReduce 思想),以及高性能签到、积分排行榜(实时与历史排行)。
5. AI 智能服务集成
个人知识库
兼容 jdk8 的 Langchain4j 框架整合 Qdrant 向量数据库,实现用户上传文档(如笔记、课件)的解析与向量存储,支持基于语义的精准检索。
AI 辅助学习
对接 AI 模型提供课程咨询、知识讲解等功能,未来计划结合阿里云百炼平台实现 AI 语音对话,提升学习交互体验。
6. 搜索与推荐
智能搜索
基于 Elasticsearch 实现课程检索,支持搜索提词器(拼音、首字母联想)、检索历史记录,提升用户搜索效率。
个性化推荐
通过数据埋点构建用户画像,结合课程内容特征实现 “猜你喜欢”“精品好课” 等推荐,优化学习内容触达。
平台架构设计
平台采用微服务架构,按业务域拆分为多个独立服务,配合前端分层设计,形成 “前端 - 网关 - 服务 - 存储” 的完整架构体系。
1. 整体架构分层
前端层
分为管理端和用户端,均基于 Vue3+vite+Tdesign+pinia 开发,分别面向后台管理员 / 教师和学员,提供差异化交互界面。
网关层
通过 tj-gateway 实现请求路由、鉴权、日志拦截、流量控制,作为系统统一入口。
服务层
按业务拆分多个微服务,通过 SpringCloudAlibaba 实现服务注册、发现与通信。
存储层
包括关系型数据库(MySQL)、缓存(Redis)、对象存储(Minio)、向量数据库(Qdrant)、时序数据库(InfluxDB)、搜索引擎(Elasticsearch)等。
中间件层
集成 RabbitMQ(消息)、XXL-JOB(任务调度)、Redisson(分布式锁)、Nacos(配置 / 注册)等,支撑服务高可用运行。

2. 核心服务模块
| 模块名称 | 核心功能 | 
|---|---|
| auth | 统一认证授权中心,处理用户登录、权限校验(基于 OAuth2.0) | 
| course | 课程管理中心,负责课程创建、编辑、目录管理、视频关联等 | 
| learning | 学习中心,管理学习进度、笔记、签到、积分、学习计划等 | 
| pay | 支付中台,集成多支付渠道,处理支付、退款、对账等 | 
| trade | 交易中心,管理订单创建、状态流转、拆单等 | 
| chat | AI 智能服务,实现 AI 对话、个人知识库管理、智能推荐等 | 
| search | 搜索系统,提供课程检索、提词、个性化推荐 | 
| data | 数据中心,处理日志分析、流量统计、数据展板(Kibana)等 | 
| message | 消息中心,负责系统通知、用户私聊、群聊(WebSocket) | 
| promotion | 营销中心,管理优惠券、折扣活动、积分商城等 | 
核心平台功能
平台功能按角色场景拆分,每个模块聚焦具体需求,形成完整业务链路。
1. 学员端:让学习更高效、更沉浸
课程学习
支持视频点播(腾讯云 VOD 加密保护)、直播互动,高精度记录学习进度(支持回放断点续传);
学习辅助
AI 对话答疑、学习计划制定、章节测试与考试,形成 "学 - 练 - 测" 闭环;
互动社交
课程评论、问答点赞、同学群聊,提升学习氛围感。
2. 教师端:让教学更轻松、更精准
课程管理
在线创建课程、上传课件、编辑章节,支持视频自动审核;
学员管理
查看学员学习数据、回复问答、发起直播答疑,实时掌握教学效果;
数据看板
课程访问量、完课率、考试通过率等核心指标可视化,辅助优化教学内容。
3. 管理端:让运营更精细、更可控
平台运营
优惠券发放、营销活动配置、支付对账,支持支付宝 / 微信多渠道支付;
权限管理
基于角色的权限控制,区分管理员、教师、学员操作范围;
数据监控
用户增长、流量分布、订单交易等数据实时展示,Kibana 生成数据大屏辅助决策。
代码实现
1. 大文件分块合并
@Service
@Slf4j
public class FileMergeServiceImpl implements FileMergeService {
/**
* 合并分块文件
* @param chunkFolderPath 分块文件夹路径
* @param originalFilePath 原始文件路径(用于校验)
* @param mergeFilePath 合并后文件路径
* @return 是否合并成功
*/
@Override
public boolean mergeChunks(String chunkFolderPath, String originalFilePath, String mergeFilePath) throws IOException {
File chunkFolder = new File(chunkFolderPath);
File originalFile = new File(originalFilePath);
File mergeFile = new File(mergeFilePath);
// 校验分块文件夹是否存在
if (!chunkFolder.exists() || !chunkFolder.isDirectory()) {
log.error("分块文件夹不存在: {}", chunkFolderPath);
return false;
}
// 删除已存在的合并文件
if (mergeFile.exists() && !mergeFile.delete()) {
log.error("删除已有合并文件失败: {}", mergeFilePath);
return false;
}
mergeFile.createNewFile();
// 读取分块文件并排序
File[] chunkFiles = chunkFolder.listFiles();
if (chunkFiles == null || chunkFiles.length == 0) {
log.error("分块文件为空: {}", chunkFolderPath);
return false;
}
List<File> sortedChunks = Arrays.stream(chunkFiles)
.sorted(Comparator.comparingInt(f -> Integer.parseInt(f.getName())))
.collect(Collectors.toList());
// 合并分块
try (RandomAccessFile rafWrite = new RandomAccessFile(mergeFile, "rw")) {
byte[] buffer = new byte[1024 * 8]; // 8KB缓冲区
for (File chunk : sortedChunks) {
try (RandomAccessFile rafRead = new RandomAccessFile(chunk, "r")) {
int len;
while ((len = rafRead.read(buffer)) != -1) {
rafWrite.write(buffer, 0, len);
}
}
}
}
// 校验文件完整性(MD5对比)
try (FileInputStream originalIn = new FileInputStream(originalFile);
FileInputStream mergeIn = new FileInputStream(mergeFile)) {
String originalMd5 = DigestUtils.md5Hex(originalIn);
String mergeMd5 = DigestUtils.md5Hex(mergeIn);
return originalMd5.equals(mergeMd5);
}
}
}
2. 搜索服务
@Service
@Slf4j
public class SuggestionServiceImpl implements SuggestionService {
@Autowired
private ElasticsearchRestTemplate elasticsearchTemplate;
/**
* 获取搜索建议(关键词+拼音+首字母)
*/
@Override
public List<String> getSuggestions(String keyword) {
if (StringUtils.isBlank(keyword)) {
return Collections.emptyList();
}
// 构建三个维度的补全查询
CompletionSuggestionBuilder keywordSuggest = SuggestBuilders
.completionSuggestion("keyword")
.prefix(keyword, Fuzziness.ONE) // 允许1个字符误差
.size(10);
CompletionSuggestionBuilder pinyinSuggest = SuggestBuilders
.completionSuggestion("keywordPinyin")
.prefix(keyword, Fuzziness.ZERO)
.size(10);
CompletionSuggestionBuilder initialSuggest = SuggestBuilders
.completionSuggestion("keywordSequence")
.prefix(keyword.toUpperCase(), Fuzziness.ZERO)
.size(10);
// 组合查询
SuggestBuilder suggestBuilder = new SuggestBuilder()
.addSuggestion("keyword_suggest", keywordSuggest)
.addSuggestion("pinyin_suggest", pinyinSuggest)
.addSuggestion("initial_suggest", initialSuggest);
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withSuggestBuilder(suggestBuilder)
.build();
// 执行查询
SearchHits<SuggestIndex> hits = elasticsearchTemplate.search(query, SuggestIndex.class);
Suggest suggest = hits.getSuggest();
if (suggest == null) {
return Collections.emptyList();
}
// 合并去重结果
Set<String> suggestions = new LinkedHashSet<>(); // 保持顺序+去重
extractSuggestions(suggest, "keyword_suggest", suggestions);
extractSuggestions(suggest, "pinyin_suggest", suggestions);
extractSuggestions(suggest, "initial_suggest", suggestions);
return new ArrayList<>(suggestions);
}
// 提取单个建议结果
private void extractSuggestions(Suggest suggest, String name, Set<String> result) {
CompletionSuggestion completion = suggest.getSuggestion(name);
completion.getEntries().forEach(entry -> 
entry.getOptions().forEach(option -> {
// 优先从命中实体取标题,否则用建议文本
String title = option.getHit().getSource().getTitle();
result.add(StringUtils.isNotBlank(title) ? title : option.getText().string());
})
);
}
}
3. AI 课程陪学实时对话
@Service
@Slf4j
public class AiChatServiceImpl implements AiChatService {
@Autowired
private StreamingChatLanguageModel streamingChatModel;
/**
* 实时AI聊天(SSE流推送)
*/
@Override
public SseEmitter chatWithAi(String sessionId, String message) {
// 校验登录状态
Long userId = UserContext.getUser();
if (userId == null) {
SseEmitter errorEmitter = new SseEmitter(0L);
errorEmitter.completeWithError(new UnauthorizedException("请先登录"));
return errorEmitter;
}
// 创建SSE发射器(30分钟超时)
SseEmitter emitter = new SseEmitter(1800000L);
// 注册回调
emitter.onTimeout(() -> {
log.warn("SSE连接超时: sessionId={}", sessionId);
emitter.complete();
});
emitter.onError(e -> log.error("SSE异常: sessionId={}", sessionId, e));
// 调用大模型流式生成
try {
streamingChatModel.generate(buildPrompt(message), new StreamingResponseHandler<AiMessage>() {
@Override
public void onNext(String chunk) {
try {
// 发送单个文本块(SSE格式)
emitter.send(SseEmitter.event()
.name("message")
.data(chunk, MediaType.TEXT_PLAIN));
} catch (IOException e) {
log.error("发送SSE消息失败", e);
emitter.completeWithError(e);
}
}
@Override
public void onComplete(Response response) {
try {
// 发送结束标记
emitter.send(SseEmitter.event()
.name("done")
.data("[DONE]"));
emitter.complete();
} catch (IOException e) {
log.error("发送结束消息失败", e);
}
}
@Override
public void onError(Throwable error) {
log.error("AI生成异常", error);
emitter.completeWithError(error);
}
});
} catch (Exception e) {
log.error("AI调用失败", e);
emitter.completeWithError(e);
}
return emitter;
}
// 构建提示词(附加上下文)
private String buildPrompt(String message) {
return PromptBuilder.buildSystemMessage(getContextFromRedis(), message);
}
// 从Redis获取会话上下文
private String getContextFromRedis() {
// 实际实现需从Redis查询历史对话
return "";
}
}
4. 优惠券兑换码生成
@Service
@Slf4j
public class ExchangeCodeGenerateService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private ExchangeCodeMapper exchangeCodeMapper;
// Redis自增键前缀
private static final String SERIAL_KEY = "promotion:coupon:code:serial:";
// 兑换码批次缓存键
private static final String BATCH_KEY = "promotion:coupon:code:batch:";
/**
* 异步生成优惠券兑换码
*/
@Async("codeGenerateExecutor") // 自定义线程池
public void generateExchangeCodes(Coupon coupon) {
int totalNum = coupon.getTotalNum();
if (totalNum <= 0) {
log.warn("优惠券{}无需生成兑换码(数量为0)", coupon.getId());
return;
}
// 1. 生成连续序列号(Redis自增保证唯一性)
String serialKey = SERIAL_KEY + coupon.getId();
Long maxSerial = redisTemplate.opsForValue().increment(serialKey, totalNum);
if (maxSerial == null) {
log.error("生成序列号失败: couponId={}", coupon.getId());
return;
}
int startSerial = maxSerial.intValue() - totalNum + 1;
// 2. 批量生成兑换码
List<ExchangeCode> codes = new ArrayList<>(totalNum);
for (int i = startSerial; i <= maxSerial; i++) {
String code = CodeUtil.generateCode(i, coupon.getId()); // 自定义编码规则
ExchangeCode exchangeCode = new ExchangeCode();
exchangeCode.setId((long) i);
exchangeCode.setCode(code);
exchangeCode.setCouponId(coupon.getId());
exchangeCode.setExpireTime(coupon.getEndTime());
exchangeCode.setStatus(CodeStatus.UNUSED);
codes.add(exchangeCode);
}
// 3. 批量入库
exchangeCodeMapper.batchInsert(codes); // MyBatis-Plus批量插入
log.info("优惠券{}生成{}个兑换码完成", coupon.getId(), totalNum);
// 4. 缓存批次信息(用于后续查询)
redisTemplate.opsForValue().set(BATCH_KEY + coupon.getId(), startSerial + "-" + maxSerial, 7, TimeUnit.DAYS);
}
}
4. 用户私信功能
package com.example.foodchain.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.foodchain.mapper.OrderMapper;
import com.example.foodchain.model.Order;
import com.example.foodchain.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.UUID;
/**
* 订单服务实现类
*/
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Override
public String createOrder(Order order) {
// 生成订单编号(ORDER+日期+随机数)
String dateStr = new java.text.SimpleDateFormat("yyyyMMdd").format(new Date());
String orderNo = "ORDER" + dateStr + UUID.randomUUID().toString().substring(0, 6);
// 设置订单默认信息
order.setOrderNo(orderNo);
order.setOrderStatus(0); // 待支付
order.setCreateTime(new Date());
// 插入数据库
orderMapper.insert(order);
return orderNo;
}
}
5. 课程知识库文件解析
@Service
@Slf4j
public class KnowledgeParseServiceImpl implements KnowledgeService {
@Autowired
private QdrantClient qdrantClient;
@Autowired
private MinioClient minioClient;
/**
* 上传课程资料并解析入库
*/
@Override
@Transactional
public void uploadCourseDoc(MultipartFile file, Long courseId) {
try {
// 1. 保存文件到MinIO
String fileKey = "course/knowledge/" + courseId + "/" + UUID.randomUUID();
minioClient.putObject(
PutObjectArgs.builder()
.bucket("mooc-knowledge")
.object(fileKey)
.stream(file.getInputStream(), file.getSize(), -1)
.build()
);
// 2. 解析文件内容(支持PDF/Markdown)
List<String> chunks = parseFile(file, fileKey);
// 3. 分块嵌入向量并写入Qdrant
for (String chunk : chunks) {
// 生成向量
float[] vector = EmbeddingUtil.embed(chunk);
// 构建向量点(包含课程ID用于过滤)
Point point = Point.builder()
.id(UUID.randomUUID().toString())
.vector(vector)
.payload(Map.of(
"content", chunk,
"course_id", courseId,
"file_key", fileKey,
"create_time", LocalDateTime.now().toString()
))
.build();
// 写入向量库
qdrantClient.upsert(UpsertPoints.builder()
.collectionName("course_knowledge")
.points(Collections.singletonList(point))
.build());
}
} catch (Exception e) {
log.error("上传课程知识库失败", e);
throw new BadRequestException("文件解析失败");
}
}
// 解析文件为文本块(按章节/段落拆分)
private List<String> parseFile(MultipartFile file, String fileKey) throws IOException {
String fileName = file.getOriginalFilename();
if (fileName.endsWith(".md")) {
return MarkdownParser.splitIntoChunks(new String(file.getBytes()));
} else if (fileName.endsWith(".pdf")) {
return PdfParser.splitIntoChunks(file.getInputStream());
} else {
throw new BadRequestException("仅支持PDF和Markdown文件");
}
}
}
6. AI 课程交互接口
import request from "@/utils/request.js";
const AI_COURSE_PREFIX = "/ct/course/ai";
// 课程AI陪学对话(流式)
export const courseAiChatStream = (params) => {
return request({
url: `${AI_COURSE_PREFIX}/chat/stream`,
method: "get",
params,
responseType: "stream", // 流式响应
});
};
// 上传课程资料到知识库
export const uploadCourseDoc = (file, courseId) => {
const formData = new FormData();
formData.append("file", file);
formData.append("courseId", courseId);
return request({
url: `${AI_COURSE_PREFIX}/knowledge/upload`,
method: "post",
data: formData,
headers: { "Content-Type": "multipart/form-data" },
});
};
// 获取课程知识库列表
export const getCourseKnowledgeList = (courseId) => {
return request({
url: `${AI_COURSE_PREFIX}/knowledge/list`,
method: "get",
params: { courseId },
});
};
5
UI展示

课程中心


考试中心

题目管理

数据中心

营销中心

订单管理

用户管理

用户端



