Compare commits

...

196 Commits
main ... dev2

Author SHA1 Message Date
Lqc
5ea2d62f7f 我的活动,最常访问关注,修改密码 2025-10-20 20:58:06 +08:00
Lqc
a76e5306cf 我的关注 2025-10-17 20:47:49 +08:00
Lqc
b077443687 作业查询逻辑修改 2025-10-15 17:15:02 +08:00
Lqc
821be43cfd 作业查询修改 2025-10-14 17:47:33 +08:00
Lqc
1bd0490dd9 我的练习-练习查询 2025-10-14 05:44:32 +08:00
Lqc
1887802f52 用户-编辑个人信息-修改 2025-10-14 00:28:26 +08:00
Lqc
1e0fb9883b 我的考试查询考试 2025-10-14 00:22:04 +08:00
Lqc
989d1db754 Merge remote-tracking branch 'origin/dev2' into dev2 2025-10-11 20:15:19 +08:00
Lqc
4d9812466a 我的作业查询作业 2025-10-11 20:15:00 +08:00
GoCo
925817bb1e feat: 🎸 学员端菜单接口 2025-09-29 16:05:57 +08:00
GoCo
71c82fa332 feat: 🎸 接口补充 2025-09-29 14:54:48 +08:00
GoCo
2b18350e5a ci: 🎡 docker compose 2025-09-27 11:33:32 +08:00
GoCo
46f258dd97 ci: 🎡 删冲突标记 2025-09-27 11:23:04 +08:00
GoCo
718f84b8a4 feat: 🎸 评论点赞通知 2025-09-27 10:50:51 +08:00
GoCo
237a4f10a8 Merge branch 'dev2' of http://110.42.96.64:19890/GoCo/OL-LearnPlatform-Backend into dev2
merge
2025-09-26 10:29:01 +08:00
GoCo
3f56528979 feat: 🎸 统计接口 & 访问日志 2025-09-26 10:28:57 +08:00
Your Name
92d85b3e44 docker-compose.yml 2025-09-25 15:22:43 +08:00
GoCo
61d70bb379 feat: 🎸 新增接口 2025-09-25 09:41:17 +08:00
GoCo
035794f916 feat: 🎸 资源内容 & 视频字幕总结接口&更多课程修复 2025-09-23 19:05:37 +08:00
GoCo
10c03265d8 Merge branch 'dev2' of http://110.42.96.64:19890/GoCo/OL-LearnPlatform-Backend into dev2
merge
2025-09-23 13:58:10 +08:00
GoCo
d7944a0c64 feat: 🎸 查询章节详情 修改 2025-09-23 13:58:02 +08:00
Lqc
af808a80b7 题目返回创建人用户名 2025-09-23 11:49:07 +08:00
Lqc
4526e237a6 Merge remote-tracking branch 'origin/dev2' into dev2 2025-09-23 11:32:52 +08:00
Lqc
1963b6df29 章节绑定练习详情微调 2025-09-23 11:32:39 +08:00
GoCo
ab75199f17 Merge branch 'dev2' of http://110.42.96.64:19890/GoCo/OL-LearnPlatform-Backend into dev2
merge
2025-09-23 11:18:40 +08:00
GoCo
1ea941a4d1 feat: 🎸 注册接口报错修复 & 考试练习讨论章节 2025-09-23 11:18:37 +08:00
Lqc
319f57419f Merge remote-tracking branch 'origin/dev2' into dev2 2025-09-23 11:03:38 +08:00
Lqc
181c267219 章节绑定练习详情 2025-09-23 11:03:17 +08:00
GoCo
341dab35d0 Merge branch 'dev2' of http://110.42.96.64:19890/GoCo/OL-LearnPlatform-Backend into dev2
merge
2025-09-22 20:07:20 +08:00
GoCo
b13795a3b7 feat: 🎸 课程章节新增 编辑targetid参数 & 查询群聊用户增加返回字段 &新增发送消息接口 2025-09-22 20:07:09 +08:00
Lqc
53f6de63ce Merge remote-tracking branch 'origin/dev2' into dev2 2025-09-22 18:18:52 +08:00
Lqc
7505a27317 重新练习接口 2025-09-22 18:18:32 +08:00
GoCo
61e35598b3 Merge branch 'dev2' of http://110.42.96.64:19890/GoCo/OL-LearnPlatform-Backend into dev2
merge
2025-09-22 16:18:36 +08:00
GoCo
7837fca784 feat: 🎸 作业编辑、取消评论置顶接口 2025-09-22 16:18:26 +08:00
Lqc
3c21fc3500 Merge remote-tracking branch 'origin/dev2' into dev2 2025-09-22 15:34:03 +08:00
Lqc
bd4ca454a3 题库创建人用户名返回 2025-09-22 15:33:29 +08:00
GoCo
59cd0049e2 feat: 🎸 在线交流接口 2025-09-22 08:44:15 +08:00
GoCo
c7a66f623f Merge branch 'dev2' of http://110.42.96.64:19890/GoCo/OL-LearnPlatform-Backend into dev2
merge
2025-09-21 16:25:24 +08:00
GoCo
5529646506 feat: 🎸 接口补充 2025-09-21 16:25:15 +08:00
小张
16abb964a5 fix:门户设计部分搬迁 2025-09-21 00:27:58 +08:00
Lqc
894e9d38b4 课程章节 考试列表查询 2025-09-20 18:01:53 +08:00
GoCo
40aa49a5ba feat: 🎸 作业列表 2025-09-20 10:42:32 +08:00
GoCo
b8e649c13c feat: 🎸 讨论列表接口修改 2025-09-20 10:21:12 +08:00
GoCo
5da9d2135a Merge branch 'dev2' of http://110.42.96.64:19890/GoCo/OL-LearnPlatform-Backend into dev2
merge
2025-09-20 09:59:38 +08:00
GoCo
9dca80de0e feat: 🎸 接口补充 2025-09-20 09:59:29 +08:00
Lqc
65e8179e14 试卷,创建人名一起返回 2025-09-20 06:07:38 +08:00
Lqc
678b9eeb6e 查询试卷所有题目详情 2025-09-20 05:48:59 +08:00
GoCo
e0aa844162 feat: 🎸 讨论表代码+接口、作业接口、消息接口、课程章节接口 2025-09-19 10:06:17 +08:00
GoCo
93a5b7e3b2 feat: 🎸 student_list查询院校信息 2025-09-18 09:27:41 +08:00
GoCo
a73cc74342 Merge branch 'dev2' of http://110.42.96.64:19890/GoCo/OL-LearnPlatform-Backend into dev2
merge
2025-09-18 09:18:24 +08:00
GoCo
e92e6ffeca feat: 🎸 资源接口 2025-09-18 09:18:15 +08:00
Lqc
06063b975a Merge remote-tracking branch 'origin/dev2' into dev2 2025-09-18 08:53:42 +08:00
Lqc
65e68d3c8b 微调 2025-09-18 08:53:21 +08:00
GoCo
a9e3e9146a feat: 🎸 新建章节返回章节id 2025-09-17 20:14:01 +08:00
GoCo
87fbf7c686 feat: 🎸 课程增加学期和是否允许下载字段 2025-09-17 10:10:38 +08:00
GoCo
7dda8cae1c Merge branch 'dev2' of http://110.42.96.64:19890/GoCo/OL-LearnPlatform-Backend into dev2
merge
2025-09-17 09:55:23 +08:00
GoCo
8b45b63bbe feat: 🎸 通知中心接口 2025-09-17 04:33:04 +08:00
Lqc
a57ab82151 Merge remote-tracking branch 'origin/dev2' into dev2 2025-09-17 01:27:26 +08:00
Lqc
4f5ee086a4 试卷题目导入 2025-09-17 01:27:03 +08:00
GoCo
7fca0228fd Merge branch 'dev2' of http://110.42.96.64:19890/GoCo/OL-LearnPlatform-Backend into dev2
merge
2025-09-16 17:28:10 +08:00
GoCo
ea534f1a90 feat: 🎸 课程列表接口 新增课程接口修改 2025-09-16 17:27:45 +08:00
Lqc
a793020c5e 权限 2025-09-16 15:07:46 +08:00
Lqc
16bac95dee 题目状态 2025-09-16 00:31:40 +08:00
GoCo
01e9b12eaf feat: 🎸 题目加知识点字段 2025-09-15 21:35:59 +08:00
GoCo
d925089869 feat: 🎸 接口补充 & question表字段新增status 2025-09-15 10:09:47 +08:00
GoCo
b4c0be4051 feat: 🎸 修复查询课程列表字段名错误 2025-09-13 18:22:44 +08:00
GoCo
44b025f686 feat: 🎸 新增课程 多个分类班级 2025-09-13 13:54:14 +08:00
GoCo
132b020763 feat: 🎸 新增课程 添加班级 2025-09-13 10:50:20 +08:00
GoCo
8f1fec0169 feat: 🎸 新建课程携带分类id 2025-09-12 17:45:21 +08:00
GoCo
a4fe5c17e6 feat: 🎸 接口补充 2025-09-12 09:31:32 +08:00
GoCo
8e484ad8de feat: 🎸 查询班级列表 2025-09-11 20:05:43 +08:00
GoCo
625d8b1383 feat: 🎸 学员注册 2025-09-11 10:08:28 +08:00
GoCo
561eed3147 feat: 🎸 dev mysql配置 2025-09-11 09:12:58 +08:00
GoCo
10218685cc Merge branch 'dev2' of http://110.42.96.64:19890/GoCo/OL-LearnPlatform-Backend into dev2
merge
2025-09-11 09:07:10 +08:00
GoCo
f8515a5bb0 feat: 🎸 课程 班级字段补充 & 会话表 2025-09-11 09:07:05 +08:00
Lqc
877f6b6046 题库权限,新增删除查询 2025-09-10 12:57:50 +08:00
Lqc
a07f29f9b2 新建题库,查询题库需要权限 2025-09-08 17:21:27 +08:00
GoCo
2824d20fc1 feat: 🎸 excel导入学生 2025-09-08 17:04:53 +08:00
Lqc
af1102a1b9 返回id 2025-09-08 16:26:51 +08:00
GoCo
461bbaec56 feat: 🎸 课程章节支持关键词模糊查询 2025-09-06 21:12:59 +08:00
GoCo
4ccef54e7e feat: 🎸 更多课程 2025-09-06 09:57:09 +08:00
GoCo
bb5a154768 Merge branch 'dev2' of http://110.42.96.64:19890/GoCo/OL-LearnPlatform-Backend into dev2
merge
2025-09-06 09:51:40 +08:00
GoCo
ce9c3099c0 feat: 🎸 内容权限&课程 班级 接口 2025-09-06 09:51:35 +08:00
Lqc
d9c60a21db 新增题目时有题库id新增到题库,没有题库id只存入题目表 2025-09-05 20:12:21 +08:00
Lqc
978ba1ecd9 微调 2025-09-05 15:52:56 +08:00
Lqc
0a043be325 题库题目的excel导入导出 2025-09-05 15:52:13 +08:00
Lqc
00615712f8 Merge remote-tracking branch 'origin/dev2' into dev2 2025-09-05 11:29:46 +08:00
Lqc
918514a6ec 题库的题目数量 2025-09-05 11:29:02 +08:00
GoCo
a3a1ef430e Merge branch 'dev2' of http://110.42.96.64:19890/GoCo/OL-LearnPlatform-Backend into dev2
merge
2025-09-04 09:24:30 +08:00
GoCo
b376d406d7 feat: 🎸 课程列表增加当前用户是否已报名字段 2025-09-04 09:24:21 +08:00
GoCo
e6d382c7cf feat: 🎸 课程班级 2025-09-04 03:43:35 +08:00
Lqc
27dabcce7a 微调 2025-09-03 16:17:06 +08:00
Lqc
d294bfc606 微调 2025-09-03 15:39:17 +08:00
Lqc
c4a6307b9a 微调 2025-09-03 15:27:40 +08:00
Lqc
b41598f7f8 获取考试结果 2025-09-03 14:56:13 +08:00
GoCo
ce36c26dff feat: 🎸 新增课程评论 2025-09-03 09:44:49 +08:00
GoCo
6cc473fead Merge branch 'dev2' of http://110.42.96.64:19890/GoCo/OL-LearnPlatform-Backend into dev2
merge
2025-09-02 15:55:50 +08:00
GoCo
d1373a5524 feat: 🎸 题目增加程度和能力字段 2025-09-02 15:55:41 +08:00
Lqc
48935314bf Merge remote-tracking branch 'origin/dev2' into dev2 2025-09-02 15:02:00 +08:00
Lqc
5a5557d479 考试主观题批改 2025-09-02 15:01:28 +08:00
GoCo
a9ae28e3e1 feat: 🎸 查询视频学习进度接口 2025-09-02 04:15:23 +08:00
GoCo
35fb15e2e3 feat: 🎸 课程视频学习进度统计接口 2025-09-02 04:03:17 +08:00
GoCo
40bfe138f0 feat: 🎸 课程增加 ai伴学 字段 2025-09-02 03:00:23 +08:00
GoCo
f020747b1f feat: 🎸 解决 修改用户角色,权限无法及时同步 2025-09-02 02:42:19 +08:00
GoCo
c0a2047544 feat: 🎸 docker compose redis端口配置 2025-09-01 17:05:57 +08:00
GoCo
cff6d963a0 Merge branch 'dev2' of http://110.42.96.64:19890/GoCo/OL-LearnPlatform-Backend into dev2
merge
2025-09-01 16:11:21 +08:00
GoCo
1ca17d6b92 feat: 🎸 mysql配置 2025-09-01 16:11:10 +08:00
Lqc
44ee564b5f 考试客观题批改 2025-09-01 16:01:34 +08:00
GoCo
e70ed44bfb feat: 🎸 mysql配置 2025-08-31 20:26:25 +08:00
GoCo
a1054006e6 feat: 🎸 mysql配置 2025-08-31 20:23:54 +08:00
GoCo
3f199d375b 考试 试卷重构 2025-08-31 20:20:19 +08:00
GoCo
3429e6ac7b 学员端 接口迁移 2025-08-31 17:59:58 +08:00
GoCo
ac7864f34b 前台用户 重构 2025-08-31 12:20:05 +08:00
GoCo
1596e6fc33 作业 重构 2025-08-31 12:09:53 +08:00
GoCo
67068a03b6 课程 重构 2025-08-31 11:55:37 +08:00
GoCo
337ba0f42a Merge branch 'dev' of http://110.42.96.65:19890/GoCo/OL-LearnPlatform-Backend into dev
merge
2025-08-29 17:21:51 +08:00
GoCo
42eb767979 feat: 🎸 课程列表按最新\最热\推荐排序 2025-08-29 17:21:42 +08:00
Lqc
326e764f29 微调 2025-08-29 16:26:47 +08:00
Lqc
3896f6df85 微调 2025-08-29 16:11:48 +08:00
Lqc
6cf1b91b85 提交考试时计算选择题,多选题,判断题得分 2025-08-29 15:59:54 +08:00
Lqc
61665d749d 获取所有题库 2025-08-29 15:01:33 +08:00
GoCo
bf470b1c79 feat: 🎸 课程总数 2025-08-29 05:12:30 +08:00
Lqc
58ea4b3071 查询考试进度 2025-08-28 14:45:25 +08:00
Lqc
69d2a464cb 随机组卷 2025-08-28 14:35:03 +08:00
Lqc
97419396f6 查询题目详情更改返回结果 2025-08-28 13:49:05 +08:00
GoCo
5861ce2655 Merge branch 'dev' of http://110.42.96.65:19890/GoCo/OL-LearnPlatform-Backend into dev
merge
2025-08-28 06:02:07 +08:00
GoCo
a51f6c6e2e feat: 🎸 查询精选活动 2025-08-28 06:01:56 +08:00
GoCo
9d490fbbda feat: 🎸 查询当前教师创建的课程增加关键词和状态参数 2025-08-28 05:37:17 +08:00
Lqc
9dc9b50b1d Merge remote-tracking branch 'origin/dev' into dev 2025-08-27 21:41:17 +08:00
Lqc
e06a4b9f89 提交答案,提交考试,获取试卷初始化 2025-08-27 21:40:42 +08:00
GoCo
5b0e087dcd feat: 🎸 搜索以及获取热搜 2025-08-27 17:17:09 +08:00
Lqc
796f1502b6 考试,获取考试试题 2025-08-26 14:07:34 +08:00
GoCo
a9a2a16618 feat: 🎸 考试 试卷 表生成 2025-08-26 02:25:27 +08:00
GoCo
c48769872d Merge branch 'dev' of http://110.42.96.65:19890/GoCo/OL-LearnPlatform-Backend into dev
merge
2025-08-26 02:06:22 +08:00
GoCo
9830e0d3b3 查询高校列表 2025-08-26 02:06:13 +08:00
Lqc
52798ce478 查询题库下题目时,返回复合题下的子题目 2025-08-25 12:46:40 +08:00
Lqc
35ed6f0b1b Merge branch 'dev' of http://110.42.96.65:19890/GoCo/OL-LearnPlatform-Backend into dev 2025-08-25 12:45:25 +08:00
GoCo
50e127c505 Merge branch 'dev' of http://110.42.96.65:19890/GoCo/OL-LearnPlatform-Backend into dev
merge
2025-08-25 10:55:48 +08:00
GoCo
f2cf55e293 feat: 🎸 首页数据查询接口 2025-08-25 10:55:07 +08:00
小张
520fbcf177 fix:logo标题替换我们云岭智教 2025-08-23 17:44:48 +08:00
Lqc
d9c50d4750 查询题目详情对于复合题返回题目及答案 2025-08-23 14:57:43 +08:00
GoCo
3f2c7877ed feat: 🎸 删除生成文件 2025-08-22 17:00:35 +08:00
GoCo
78de817ca9 Merge branch 'dev' of http://110.42.96.65:19890/GoCo/OL-LearnPlatform-Backend into dev
merge
2025-08-22 16:56:26 +08:00
GoCo
6b2ae0f50d feat: 🎸 课程进度查询接口 2025-08-22 16:56:11 +08:00
Lqc
b3fda1650b 冲突 2025-08-22 16:02:46 +08:00
Lqc
d7b8733123 冲突 2025-08-22 16:02:01 +08:00
Lqc
81a6f0ee43 Merge branch 'dev' of http://110.42.96.65:19890/GoCo/OL-LearnPlatform-Backend into dev
# Conflicts:
#	jeecg-boot/jeecg-boot-module/jeecg-module-learn/src/main/java/org/jeecg/modules/biz/controller/RepoBizController.java
2025-08-22 16:01:01 +08:00
Lqc
7f9bd444dd 题目题库 2025-08-22 15:54:27 +08:00
GoCo
049ee30611 feat: 🎸 新建题库 2025-08-22 11:02:57 +08:00
GoCo
0d83336140 feat: 🎸 题库 题目表 2025-08-21 17:56:27 +08:00
Lqc
bf96853b68 作业提交、查询,教师批改 2025-08-21 14:37:41 +08:00
GoCo
4080fa4ce5 feat: 🎸 活动报名表 生成 2025-08-21 12:06:44 +08:00
GoCo
d069086ea4 feat: 🎸 作业提交表gen 2025-08-21 10:26:16 +08:00
GoCo
b4bbcb4d7d fix: 🐛 nginx max body size 2025-08-20 13:05:43 +08:00
GoCo
23ad7f14f9 fix: 🐛 nginx 配置 2025-08-20 13:01:24 +08:00
GoCo
4619edaad8 ci: 🎡 nginx配置 2025-08-20 12:48:48 +08:00
GoCo
593224a478 ci: 🎡 上传文件大小限制 2025-08-20 11:23:47 +08:00
GoCo
8717b32306 feat: 🎸 课程报名、导入学生、上传视频切片接口&dockerfile增加ffmpeg 2025-08-20 05:53:27 +08:00
GoCo
7188b7a500 feat: 🎸 课程报名&查询报名情况接口 2025-08-19 02:49:29 +08:00
GoCo
3d9fda6230 作业+文档章节接口 2025-08-18 17:32:52 +08:00
GoCo
4c160a7bb3 feat: 🎸 课程列表接口,参数查询逻辑补齐 2025-08-18 11:49:27 +08:00
GoCo
d2b61c6d95 feat: 🎸 评论表&课程评论接口 2025-08-16 17:19:41 +08:00
GoCo
de06ae4b92 feat: 🎸 课程章节后台管理页面改弹出框选课 2025-08-16 14:18:07 +08:00
GoCo
595bd526d4 feat: 🎸 用户信息接口,改成dto并配置字段含义 2025-08-15 18:10:01 +08:00
GoCo
960aee1b68 feat: 🎸 用户信息查询接口 2025-08-15 17:53:42 +08:00
GoCo
ec89859f60 feat: 🎸 课程列表携带讲师信息 2025-08-15 17:13:22 +08:00
GoCo
64b7eded58 ci: 🎡 临时解决,上传文件认证失败 2025-08-14 14:09:49 +08:00
GoCo
a4754cb513 ci: 🎡 dockerfile nginx反代请求头 2025-08-14 13:36:37 +08:00
GoCo
00df9d643b ci: 🎡 前端dockerfile nginx反代配置携带x-access-token header 2025-08-14 13:32:04 +08:00
GoCo
d9ef0b8fa6 feat: 🎸 课程、用户信息、活动、师资力量接口 2025-08-14 07:06:05 +08:00
GoCo
02d0d55a9d ci: 🎡 部署脚本调整 2025-08-13 04:57:27 +08:00
GoCo
801e48b660 feat: 🎸 课程接口&用户登录 2025-08-13 04:46:50 +08:00
GoCo
c08fc2bb79 ci: 🎡 minio config 2025-08-12 20:00:14 +08:00
GoCo
2334fae638 ci: 🎡 docker compose 2025-08-12 19:51:39 +08:00
GoCo
880626acfc ci: 🎡 minio 2025-08-12 19:44:45 +08:00
GoCo
8f9e974aad feat: 🎸 课程列表接口-01 2025-08-12 19:17:33 +08:00
962101f37a ci: 🎡 修改部署脚本 2025-08-10 01:45:12 +08:00
lvzhihao
9738eec256 提交资源接口 2025-08-09 15:35:04 +08:00
lvzhihao
7caffa67cc 更改了一下课程状态接口 2025-08-09 15:29:15 +08:00
lvzhihao
5bfea2b020 提交课程章节相关接口 2025-08-09 14:59:43 +08:00
lvzhihao
a496d61524 提交课程分类相关接口 2025-08-09 14:25:23 +08:00
lvzhihao
0427eebee7 提交课程相关接口 2025-08-09 13:31:44 +08:00
lvzhihao
60c7ff577d 删除课程代码 2025-08-09 13:24:01 +08:00
GoCo
025be43257 feat: 🎸 视频上传接口&示例接口 2025-08-09 01:36:44 +08:00
GoCo
e5e0823e7a feat: 🎸 测试表 2025-08-08 20:14:27 +08:00
GoCo
865d3f8f22 tmp 2025-08-08 20:12:01 +08:00
lvzhihao
b992471374 删除课程代码 2025-08-08 18:30:54 +08:00
lvzhihao
bc369ad2dc 课程相关接口 2025-08-08 18:22:40 +08:00
Lzh
a6f8b2cd25 更新 jeecgboot-vue3/Dockerfile 2025-08-08 18:11:01 +08:00
Lzh
97e23b2ad1 更新 docker-compose.yml 2025-08-08 17:52:00 +08:00
Lzh
83b06b7648 更新 jeecgboot-vue3/Dockerfile 2025-08-08 17:45:20 +08:00
lvzhihao
64262ad748 课程相关接口 2025-08-07 21:34:11 +08:00
lvzhihao
6b2ab8eed7 Merge remote-tracking branch 'origin/dev' into dev
# Conflicts:
#	jeecg-boot/jeecg-boot-module/jeecg-module-learn/src/main/java/org/jeecg/modules/learn/gen/entity/Course.java
#	jeecg-boot/jeecg-boot-module/jeecg-module-learn/src/main/java/org/jeecg/modules/learn/gen/vue3/Course.data.ts
#	jeecg-boot/jeecg-boot-module/jeecg-module-learn/src/main/java/org/jeecg/modules/learn/gen/vue3/V20250807_1__menu_insert_Course.sql
#	jeecgboot-vue3/src/views/gen/CourseList.vue
#	jeecgboot-vue3/src/views/learn/Course.data.ts
#	jeecgboot-vue3/src/views/learn/CourseList.vue
2025-08-07 21:33:42 +08:00
lvzhihao
4ddb774b32 课程相关接口 2025-08-07 21:29:49 +08:00
GoCo
09a8f99ab8 feat: 🎸 init 2025-08-07 20:23:55 +08:00
GoCo
0922190012 feat: 🎸 init 2025-08-07 19:31:36 +08:00
1218 changed files with 118567 additions and 2419 deletions

4
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,4 @@
{
"java.configuration.updateBuildConfiguration": "automatic",
"java.compile.nullAnalysis.mode": "automatic"
}

View File

@ -18,14 +18,17 @@ services:
--max_allowed_packet=128M
--default-authentication-plugin=caching_sha2_password
ports:
- 13306:3306
- 25523:3306
# 添加数据卷挂载将MySQL数据持久化到宿主机目录
volumes:
- /home/visionx/AIOL/mysql-jeecg:/var/lib/mysql
networks:
- jeecg-boot
jeecg-boot-redis:
image: registry.cn-hangzhou.aliyuncs.com/jeecgdocker/redis:5.0
# ports:
# - 3792:6379
ports:
- 25524:6379
restart: always
hostname: jeecg-boot-redis
container_name: jeecg-boot-redis
@ -39,8 +42,8 @@ services:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: vector_db
ports:
- 5432:5432
# ports:
# - 25524:5432
restart: always
networks:
- jeecg-boot
@ -56,7 +59,7 @@ services:
image: jeecg-boot-system
hostname: jeecg-boot-system
ports:
- 8080:8080
- 25525:8080
networks:
- jeecg-boot
jeecg-vue:
@ -69,8 +72,9 @@ services:
networks:
- jeecg-boot
ports:
- 80:80
- 25526:80
networks:
jeecg-boot:
name: jeecg_boot

View File

@ -165,7 +165,7 @@ public class JeecgBootExceptionHandler {
public Result<?> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {
log.error(e.getMessage(), e);
addSysLog(e);
return Result.error("文件大小超出10MB限制, 请压缩或降低文件质量! ");
return Result.error("文件大小超出限制, 请压缩或降低文件质量! ");
}
/**

View File

@ -56,6 +56,7 @@ public class MybatisInterceptor implements Interceptor {
if (sysUser != null) {
// 登录人账号
field.setAccessible(true);
// field.set(parameter, sysUser.getId());
field.set(parameter, sysUser.getUsername());
field.setAccessible(false);
}

View File

@ -61,7 +61,7 @@ public class ShiroRealm extends AuthorizingRealm {
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
log.debug("===============Shiro权限认证开始============ [ roles、permissions]==========");
log.info("===============Shiro权限认证开始============ [ roles、permissions]==========");
String username = null;
String userId = null;
if (principals != null) {
@ -73,13 +73,12 @@ public class ShiroRealm extends AuthorizingRealm {
// 设置用户拥有的角色集合比如admin,test
Set<String> roleSet = commonApi.queryUserRolesById(userId);
//System.out.println(roleSet.toString());
// System.out.println(roleSet.toString());
info.setRoles(roleSet);
// 设置用户拥有的权限集合比如sys:role:add,sys:user:add
Set<String> permissionSet = commonApi.queryUserAuths(userId);
info.addStringPermissions(permissionSet);
//System.out.println(permissionSet);
log.info("===============Shiro权限认证成功==============");
return info;
}

View File

@ -0,0 +1,43 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-module</artifactId>
<version>3.8.2</version>
</parent>
<artifactId>jeecg-module-aiol</artifactId>
<packaging>jar</packaging>
<name>jeecg-module-aiol</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-base-core</artifactId>
</dependency>
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-system-local-api</artifactId>
</dependency>
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-system-biz</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/ws.schild/jave-all-deps -->
<dependency>
<groupId>ws.schild</groupId>
<artifactId>jave-all-deps</artifactId>
<version>3.5.0</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,10 @@
package org.jeecg.modules.aiol.constant;
public class CourseSectionTypeConst {
public static final int VIDEO = 0;
public static final int DOCUMENT = 1;
public static final int EXAM = 2;
public static final int HOMEWORK = 3;
public static final int EXERCISE = 4;
public static final int DISCUSSION = 5;
}

View File

@ -0,0 +1,55 @@
package org.jeecg.modules.aiol.constant;
/**
* entity_link 表类型常量定义
* source_type主体类型
* target_type内容类型
*/
public final class EntityLinkConst {
private EntityLinkConst() {}
/** 主体类型 */
public static final class SourceType {
private SourceType() {}
// 课程
public static final String COURSE = "course";
// 课程分类
public static final String COURSE_CATEGORY = "course_category";
// 课程章节
public static final String COURSE_SECTION = "course_section";
// 专题字典/专题
public static final String SUBJECT = "subject";
// 活动
public static final String ACTIVITY = "activity";
}
/** 内容类型 */
public static final class TargetType {
private TargetType() {}
// 资源对应资源表包括视频和文档
public static final String RESOURCE = "resource";
// 作业对应作业表
public static final String HOMEWORK = "homework";
// 考试对应考试表包括考试和练习
public static final String EXAM = "exam";
// 课程
public static final String COURSE = "course";
// 题库
public static final String REPO = "repo";
// 班级
public static final String CLASS = "class";
// 讨论
public static final String DISCUSSION = "discussion";
}
/** 资源类型 0:视频,1:图片,2:文档 */
public static final class ResourceType {
private ResourceType() {}
public static final int VIDEO = 0;
public static final int IMAGE = 1;
public static final int DOCUMENT = 2;
}
}

View File

@ -0,0 +1,21 @@
package org.jeecg.modules.aiol.constant;
public class EntityPermissionConst {
public static final class EntityType {
private EntityType() {}
public static final String COURSE = "course";
public static final String CLASS = "class";
public static final String ACTIVITY = "activity";
public static final String HOMEWORK = "homework";
public static final String EXAM = "exam";
}
public static final class Mode {
private Mode() {}
public static final String READ = "read";
public static final String WRITE = "write";
public static final String MANAGE = "manage";
}
}

View File

@ -0,0 +1,9 @@
package org.jeecg.modules.aiol.constant;
public class RedisConst {
// 视频资源信息
public static final String VIDEO_RESOURCE_CACHE_KEY_PREFIX = "aiol:video_resource:";
public static final int VIDEO_RESOURCE_CACHE_EXPIRE = 30 * 24 * 60 * 60; // 30天过期
}

View File

@ -0,0 +1,7 @@
package org.jeecg.modules.aiol.constant;
public final class ResourceTypeConst {
public static final int VIDEO = 0;
public static final int IMAGE = 1;
public static final int DOCUMENT = 2;
}

View File

@ -0,0 +1,6 @@
package org.jeecg.modules.aiol.constant;
public class RoleConst {
public static final String TEACHER_ROLE_ID = "1955367301787348993";
public static final String STUDENT_ROLE_ID = "1955367267343724546";
}

View File

@ -0,0 +1,221 @@
package org.jeecg.modules.aiol.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.shiro.IgnoreAuth;
import org.jeecg.modules.aiol.entity.AiolActivity;
import org.jeecg.modules.aiol.entity.AiolTag;
import org.jeecg.modules.aiol.mapper.AiolTagMapper;
import org.jeecg.modules.aiol.service.IAiolActivityService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 活动
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Tag(name = "活动")
@RestController
@RequestMapping("/aiol/aiolActivity")
@Slf4j
public class AiolActivityController extends JeecgController<AiolActivity, IAiolActivityService> {
@Autowired
private IAiolActivityService aiolActivityService;
/**
* 分页列表查询
*
* @param aiolActivity
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "活动-分页列表查询")
@Operation(summary = "活动-分页列表查询")
@GetMapping(value = "/list")
@IgnoreAuth
public Result<IPage<AiolActivity>> queryPageList(AiolActivity aiolActivity,
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolActivity> queryWrapper = QueryGenerator.initQueryWrapper(aiolActivity, req.getParameterMap());
Page<AiolActivity> page = new Page<AiolActivity>(pageNo, pageSize);
IPage<AiolActivity> pageList = aiolActivityService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolActivity
* @return
*/
@AutoLog(value = "活动-添加")
@Operation(summary = "活动-添加")
@RequiresPermissions("aiol:aiol_activity:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolActivity aiolActivity) {
aiolActivityService.save(aiolActivity);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param aiolActivity
* @return
*/
@AutoLog(value = "活动-编辑")
@Operation(summary = "活动-编辑")
@RequiresPermissions("aiol:aiol_activity:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
public Result<String> edit(@RequestBody AiolActivity aiolActivity) {
aiolActivityService.updateById(aiolActivity);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "活动-通过id删除")
@Operation(summary = "活动-通过id删除")
@RequiresPermissions("aiol:aiol_activity:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name = "id", required = true) String id) {
aiolActivityService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "活动-批量删除")
@Operation(summary = "活动-批量删除")
@RequiresPermissions("aiol:aiol_activity:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
this.aiolActivityService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "活动-通过id查询")
@Operation(summary = "活动-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolActivity> queryById(@RequestParam(name = "id", required = true) String id) {
AiolActivity aiolActivity = aiolActivityService.getById(id);
if (aiolActivity == null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolActivity);
}
/**
* 导出excel
*
* @param request
* @param aiolActivity
*/
@RequiresPermissions("aiol:aiol_activity:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolActivity aiolActivity) {
return super.exportXls(request, aiolActivity, AiolActivity.class, "活动");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_activity:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolActivity.class);
}
@Autowired
private AiolTagMapper aiolTagMapper;
@GetMapping("/selected")
@Operation(summary = "查询精选活动")
@IgnoreAuth
public Result<List<AiolActivity>> getSelectedActivities() {
// 1. aiol_tag 表查询 target_type='activity' target_id 列表
QueryWrapper<AiolTag> tagWrapper = new QueryWrapper<>();
tagWrapper.eq("target_type", "activity");
List<AiolTag> tags = aiolTagMapper.selectList(tagWrapper);
if (tags == null || tags.isEmpty()) {
return Result.OK(List.of());
}
// 2. 提取 target_id 列表
List<String> activityIds = tags.stream()
.map(AiolTag::getTargetId)
.filter(id -> id != null && !id.trim().isEmpty())
.collect(Collectors.toList());
if (activityIds.isEmpty()) {
return Result.OK(List.of());
}
// 3. 根据 target_id 列表查询活动数据
QueryWrapper<AiolActivity> activityWrapper = new QueryWrapper<>();
activityWrapper.in("id", activityIds);
List<AiolActivity> activities = aiolActivityService.list(activityWrapper);
return Result.OK(activities);
}
}

View File

@ -0,0 +1,221 @@
package org.jeecg.modules.aiol.controller;
import java.util.*;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.aiol.entity.AiolActivity;
import org.jeecg.modules.aiol.entity.AiolActivitySignup;
import org.jeecg.modules.aiol.service.IAiolActivityService;
import org.jeecg.modules.aiol.service.IAiolActivitySignupService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.service.impl.SysUserServiceImpl;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 活动报名
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Tag(name="活动报名")
@RestController
@RequestMapping("/aiol/aiolActivitySignup")
@Slf4j
public class AiolActivitySignupController extends JeecgController<AiolActivitySignup, IAiolActivitySignupService> {
@Autowired
private IAiolActivitySignupService aiolActivitySignupService;
@Autowired
private SysUserServiceImpl sysUserService;
@Autowired
private IAiolActivityService aiolActivityService;
/**
* 分页列表查询
*
* @param aiolActivitySignup
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "活动报名-分页列表查询")
@Operation(summary="活动报名-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolActivitySignup>> queryPageList(AiolActivitySignup aiolActivitySignup,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolActivitySignup> queryWrapper = QueryGenerator.initQueryWrapper(aiolActivitySignup, req.getParameterMap());
Page<AiolActivitySignup> page = new Page<AiolActivitySignup>(pageNo, pageSize);
IPage<AiolActivitySignup> pageList = aiolActivitySignupService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolActivitySignup
* @return
*/
@AutoLog(value = "活动报名-添加")
@Operation(summary="活动报名-添加")
@RequiresPermissions("aiol:aiol_activity_signup:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolActivitySignup aiolActivitySignup) {
aiolActivitySignupService.save(aiolActivitySignup);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param aiolActivitySignup
* @return
*/
@AutoLog(value = "活动报名-编辑")
@Operation(summary="活动报名-编辑")
@RequiresPermissions("aiol:aiol_activity_signup:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AiolActivitySignup aiolActivitySignup) {
aiolActivitySignupService.updateById(aiolActivitySignup);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "活动报名-通过id删除")
@Operation(summary="活动报名-通过id删除")
@RequiresPermissions("aiol:aiol_activity_signup:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
aiolActivitySignupService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "活动报名-批量删除")
@Operation(summary="活动报名-批量删除")
@RequiresPermissions("aiol:aiol_activity_signup:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.aiolActivitySignupService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "活动报名-通过id查询")
@Operation(summary="活动报名-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolActivitySignup> queryById(@RequestParam(name="id",required=true) String id) {
AiolActivitySignup aiolActivitySignup = aiolActivitySignupService.getById(id);
if(aiolActivitySignup==null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolActivitySignup);
}
/**
* 导出excel
*
* @param request
* @param aiolActivitySignup
*/
@RequiresPermissions("aiol:aiol_activity_signup:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolActivitySignup aiolActivitySignup) {
return super.exportXls(request, aiolActivitySignup, AiolActivitySignup.class, "活动报名");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_activity_signup:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolActivitySignup.class);
}
//查看我报名的活动
@GetMapping(value = "/queryMyActivity")
@Operation(summary="我的活动查询")
public Result<List<AiolActivity>> queryMyActivit(HttpServletRequest req) {
try {
// 1. 从JWT中获取当前用户信息
String username = JwtUtil.getUserNameByToken(req);
if (username == null || username.trim().isEmpty()) {
return Result.error(401, "用户未登录或token无效");
}
SysUser currentUser = sysUserService.getUserByName(username);
if (currentUser == null) {
return Result.error(404, "用户不存在");
}
// 2. 根据当前用户ID查询报名信息
QueryWrapper<AiolActivitySignup> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_id", currentUser.getId());
List<AiolActivitySignup> list = aiolActivitySignupService.list(queryWrapper);
// 3. 根据报名信息中的活动ID查询活动信息
List<AiolActivity> activityList = new ArrayList<>();
List<String> activityIds = list.stream()
.map(AiolActivitySignup::getActivityId)
.collect(Collectors.toList());
if (!activityIds.isEmpty()) {
activityList = aiolActivityService.listByIds(activityIds);
}
return Result.OK(activityList);
}catch (Exception e){
log.error("查询失败: {}", e.getMessage(), e);
}
return Result.OK();
}
}

View File

@ -0,0 +1,989 @@
package org.jeecg.modules.aiol.controller;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.shiro.IgnoreAuth;
import org.jeecg.modules.aiol.entity.AiolChat;
import org.jeecg.modules.aiol.entity.AiolChatMember;
import org.jeecg.modules.aiol.entity.AiolChatMessage;
import org.jeecg.modules.aiol.entity.AiolClass;
import org.jeecg.modules.aiol.entity.AiolClassStudent;
import org.jeecg.modules.aiol.dto.ChatWithUnreadCountDTO;
import org.jeecg.modules.aiol.mapper.AiolChatMemberMapper;
import org.jeecg.modules.aiol.mapper.AiolChatMessageMapper;
import org.jeecg.modules.aiol.mapper.AiolClassMapper;
import org.jeecg.modules.aiol.mapper.AiolClassStudentMapper;
import org.jeecg.modules.system.mapper.SysUserRoleMapper;
import org.jeecg.modules.aiol.constant.RoleConst;
import org.jeecg.modules.aiol.service.IAiolChatService;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.entity.SysUserRole;
import org.jeecg.modules.system.mapper.SysUserMapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 会话
* @Author: jeecg-boot
* @Date: 2025-09-11
* @Version: V1.0
*/
@Tag(name = "会话")
@RestController
@RequestMapping("/aiol/aiolChat")
@Slf4j
public class AiolChatController extends JeecgController<AiolChat, IAiolChatService> {
@Autowired
private IAiolChatService aiolChatService;
@Autowired
private AiolChatMemberMapper aiolChatMemberMapper;
@Autowired
private ISysBaseAPI sysBaseApi;
@Autowired
private SysUserMapper sysUserMapper;
@Autowired
private AiolChatMessageMapper aiolChatMessageMapper;
@Autowired
private AiolClassMapper aiolClassMapper;
@Autowired
private AiolClassStudentMapper aiolClassStudentMapper;
@Autowired
private SysUserRoleMapper sysUserRoleMapper;
/**
* 分页列表查询
*
* @param aiolChat
* @param pageNo
* @param pageSize
* @param req
* @return
*/
// @AutoLog(value = "会话-分页列表查询")
@Operation(summary = "会话-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolChat>> queryPageList(AiolChat aiolChat,
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolChat> queryWrapper = QueryGenerator.initQueryWrapper(aiolChat, req.getParameterMap());
Page<AiolChat> page = new Page<AiolChat>(pageNo, pageSize);
IPage<AiolChat> pageList = aiolChatService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolChat
* @return
*/
@AutoLog(value = "会话-添加")
@Operation(summary = "会话-添加")
@RequiresPermissions("aiol:aiol_chat:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolChat aiolChat) {
aiolChatService.save(aiolChat);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param aiolChat
* @return
*/
@AutoLog(value = "会话-编辑")
@Operation(summary = "会话-编辑")
@RequiresPermissions("aiol:aiol_chat:edit")
@RequestMapping(value = "/edit", method = { RequestMethod.PUT, RequestMethod.POST })
public Result<String> edit(@RequestBody AiolChat aiolChat) {
aiolChatService.updateById(aiolChat);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "会话-通过id删除")
@Operation(summary = "会话-通过id删除")
@RequiresPermissions("aiol:aiol_chat:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name = "id", required = true) String id) {
aiolChatService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "会话-批量删除")
@Operation(summary = "会话-批量删除")
@RequiresPermissions("aiol:aiol_chat:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
this.aiolChatService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
// @AutoLog(value = "会话-通过id查询")
@Operation(summary = "会话-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolChat> queryById(@RequestParam(name = "id", required = true) String id) {
AiolChat aiolChat = aiolChatService.getById(id);
if (aiolChat == null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolChat);
}
/**
* 导出excel
*
* @param request
* @param aiolChat
*/
@RequiresPermissions("aiol:aiol_chat:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolChat aiolChat) {
return super.exportXls(request, aiolChat, AiolChat.class, "会话");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_chat:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolChat.class);
}
/**
* 查询当前用户会话列表
*
* @param request
* @return
*/
@Operation(summary = "查询当前用户会话列表", description = "根据当前登录用户ID查询其参与的会话列表包含未读消息数")
@GetMapping(value = "/my_chats")
public Result<List<ChatWithUnreadCountDTO>> queryMyChatList(HttpServletRequest request) {
try {
// 1. 从token获取当前用户信息
String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
if (token == null || token.trim().isEmpty()) {
return Result.error("用户未登录");
}
String username = JwtUtil.getUsername(token);
LoginUser sysUser = sysBaseApi.getUserByName(username);
if (sysUser == null) {
return Result.error("用户信息不存在");
}
// 2. 根据用户ID查询会话成员表获取chat_id列表和last_read_msg_id
QueryWrapper<AiolChatMember> memberWrapper = new QueryWrapper<>();
memberWrapper.eq("user_id", sysUser.getId());
List<AiolChatMember> chatMembers = aiolChatMemberMapper.selectList(memberWrapper);
if (chatMembers.isEmpty()) {
return Result.OK(new java.util.ArrayList<>());
}
// 3. 提取chat_id列表
List<String> chatIds = chatMembers.stream()
.map(AiolChatMember::getChatId)
.collect(Collectors.toList());
// 4. 根据chat_id列表查询会话详情
QueryWrapper<AiolChat> chatWrapper = new QueryWrapper<>();
chatWrapper.in("id", chatIds);
chatWrapper.orderByDesc("create_time");
List<AiolChat> chatList = aiolChatService.list(chatWrapper);
// 5. 转换为包含未读消息数的DTO列表
List<ChatWithUnreadCountDTO> resultList = new java.util.ArrayList<>();
for (AiolChat chat : chatList) {
ChatWithUnreadCountDTO chatDTO = convertToChatWithUnreadCount(chat, chatMembers, sysUser.getId());
resultList.add(chatDTO);
}
log.info("用户 {} 查询到 {} 个会话", username, resultList.size());
return Result.OK(resultList);
} catch (Exception e) {
log.error("查询用户会话列表失败: {}", e.getMessage(), e);
return Result.error("查询会话列表失败: " + e.getMessage());
}
}
/**
* 查询群聊会话成员列表
*
* @param chatId 会话ID
* @return
*/
@Operation(summary = "查询群聊会话成员列表", description = "根据会话ID查询该会话的所有成员信息包括用户ID、真实姓名、头像和教师身份")
@GetMapping(value = "/{chatId}/members")
public Result<List<Map<String, Object>>> queryChatMembers(@PathVariable(value = "chatId") String chatId) {
try {
// 1. 根据会话ID查询会话成员
QueryWrapper<AiolChatMember> memberWrapper = new QueryWrapper<>();
memberWrapper.eq("chat_id", chatId);
List<AiolChatMember> chatMembers = aiolChatMemberMapper.selectList(memberWrapper);
if (chatMembers.isEmpty()) {
return Result.OK(new java.util.ArrayList<>());
}
// 将成员列表按userId建立映射便于取出member相关字段
Map<String, AiolChatMember> userIdToMemberMap = chatMembers.stream()
.collect(Collectors.toMap(AiolChatMember::getUserId, m -> m, (a, b) -> a));
// 2. 提取用户ID列表
List<String> userIds = chatMembers.stream()
.map(AiolChatMember::getUserId)
.collect(Collectors.toList());
// 3. 查询用户信息
List<SysUser> userList = sysUserMapper.selectByIds(userIds);
// 4. 查询所有用户的教师角色身份
Map<String, Boolean> teacherStatusMap = new HashMap<>();
if (!userIds.isEmpty()) {
try {
QueryWrapper<SysUserRole> roleWrapper = new QueryWrapper<>();
roleWrapper.eq("role_id", RoleConst.TEACHER_ROLE_ID)
.in("user_id", userIds);
List<SysUserRole> teacherRoleList = sysUserRoleMapper.selectList(roleWrapper);
// 构建教师身份映射
for (SysUserRole userRole : teacherRoleList) {
teacherStatusMap.put(userRole.getUserId(), true);
}
} catch (Exception e) {
log.warn("查询教师角色身份失败: error={}", e.getMessage());
}
}
// 5. 构建返回结果
List<Map<String, Object>> result = new ArrayList<>();
for (SysUser user : userList) {
Map<String, Object> memberInfo = new HashMap<>();
memberInfo.put("id", user.getId());
memberInfo.put("realname", user.getRealname());
memberInfo.put("avatar", user.getAvatar());
// 添加教师身份标记
boolean isTeacher = teacherStatusMap.getOrDefault(user.getId(), false);
memberInfo.put("isTeacher", isTeacher);
// 可选添加更多用户信息
memberInfo.put("username", user.getUsername());
memberInfo.put("phone", user.getPhone());
memberInfo.put("email", user.getEmail());
memberInfo.put("sex", user.getSex());
// 新增返回aiol_chat_member表中的角色与设置字段
AiolChatMember member = userIdToMemberMap.get(user.getId());
if (member != null) {
memberInfo.put("role", member.getRole());
memberInfo.put("izMuted", member.getIzMuted());
memberInfo.put("izNotDisturb", member.getIzNotDisturb());
memberInfo.put("lastReadMsgId", member.getLastReadMsgId());
}
result.add(memberInfo);
}
log.info("查询会话 {} 的成员列表,共 {} 个成员", chatId, result.size());
return Result.OK(result);
} catch (Exception e) {
log.error("查询会话成员列表失败: chatId={}, error={}", chatId, e.getMessage(), e);
return Result.error("查询会话成员列表失败: " + e.getMessage());
}
}
/**
* 查询会话消息列表
*
* @param chatId 会话ID
* @param pageNo 页码
* @param pageSize 每页大小
* @return
*/
@Operation(summary = "查询会话消息列表", description = "根据会话ID查询该会话的消息列表包含发送者信息")
@GetMapping(value = "/{chatId}/messages")
public Result<List<Map<String, Object>>> queryChatMessages(
@PathVariable(value = "chatId") String chatId) {
try {
// 1. 构建查询条件
QueryWrapper<AiolChatMessage> messageWrapper = new QueryWrapper<>();
messageWrapper.eq("chat_id", chatId);
messageWrapper.orderByDesc("create_time");
List<AiolChatMessage> messageList = aiolChatMessageMapper.selectList(messageWrapper);
if (messageList.isEmpty()) {
return Result.OK(new java.util.ArrayList<>());
}
// 2. 提取发送者ID列表
List<String> senderIds = messageList.stream()
.map(AiolChatMessage::getSenderId)
.distinct()
.collect(Collectors.toList());
// 3. 查询发送者信息
List<SysUser> senderList = sysUserMapper.selectByIds(senderIds);
Map<String, SysUser> senderMap = senderList.stream()
.collect(Collectors.toMap(SysUser::getId, user -> user));
// 4. 构建返回结果包含发送者信息
List<Map<String, Object>> result = new java.util.ArrayList<>();
for (AiolChatMessage message : messageList) {
Map<String, Object> messageInfo = new java.util.HashMap<>();
// 添加消息基本信息
messageInfo.put("id", message.getId());
messageInfo.put("chatId", message.getChatId());
messageInfo.put("senderId", message.getSenderId());
messageInfo.put("content", message.getContent());
messageInfo.put("messageType", message.getMessageType());
messageInfo.put("status", message.getStatus());
messageInfo.put("fileUrl", message.getFileUrl());
messageInfo.put("fileName", message.getFileName());
messageInfo.put("fileSize", message.getFileSize());
messageInfo.put("createTime", message.getCreateTime());
// 添加发送者信息
SysUser sender = senderMap.get(message.getSenderId());
if (sender != null) {
messageInfo.put("senderInfo", new java.util.HashMap<String, Object>() {
{
put("id", sender.getId());
put("realname", sender.getRealname());
put("avatar", sender.getAvatar());
put("username", sender.getUsername());
}
});
} else {
// 如果找不到发送者信息设置默认值
messageInfo.put("senderInfo", new java.util.HashMap<String, Object>() {
{
put("id", message.getSenderId());
put("realname", "未知用户");
put("avatar", "");
put("username", "");
}
});
}
result.add(messageInfo);
}
log.info("查询会话 {} 的消息列表,共 {} 条消息", chatId, result.size());
return Result.OK(result);
} catch (Exception e) {
log.error("查询会话消息列表失败: chatId={}, error={}", chatId, e.getMessage(), e);
return Result.error("查询会话消息列表失败: " + e.getMessage());
}
}
/**
* 查询会话详情
*
* @param chatId 会话ID
* @return
*/
@Operation(summary = "查询会话详情", description = "根据会话ID查询会话详情群聊类型会包含班级信息")
@GetMapping(value = "/{chatId}/detail")
public Result<Map<String, Object>> queryChatDetail(@PathVariable(value = "chatId") String chatId) {
try {
// 1. 查询会话基本信息
AiolChat chat = aiolChatService.getById(chatId);
if (chat == null) {
return Result.error("会话不存在");
}
// 2. 构建返回结果
Map<String, Object> result = new java.util.HashMap<>();
result.put("id", chat.getId());
result.put("type", chat.getType());
result.put("name", chat.getName());
result.put("avatar", chat.getAvatar());
result.put("refId", chat.getRefId());
result.put("izAllMuted", chat.getIzAllMuted());
result.put("showLabel", chat.getShowLabel());
result.put("createBy", chat.getCreateBy());
result.put("createTime", chat.getCreateTime());
result.put("updateBy", chat.getUpdateBy());
result.put("updateTime", chat.getUpdateTime());
// 3. 如果是群聊类型type==1查询班级信息
if (chat.getType() != null && chat.getType() == 1 && chat.getRefId() != null) {
try {
// 查询班级信息
AiolClass aiolClass = aiolClassMapper.selectById(chat.getRefId());
if (aiolClass != null) {
Map<String, Object> classInfo = new java.util.HashMap<>();
classInfo.put("id", aiolClass.getId());
classInfo.put("name", aiolClass.getName());
classInfo.put("courseId", aiolClass.getCourseId());
classInfo.put("inviteCode", aiolClass.getInviteCode());
classInfo.put("createBy", aiolClass.getCreateBy());
classInfo.put("createTime", aiolClass.getCreateTime());
// 查询班级人数
QueryWrapper<AiolClassStudent> studentWrapper = new QueryWrapper<>();
studentWrapper.eq("class_id", aiolClass.getId());
long studentCount = aiolClassStudentMapper.selectCount(studentWrapper);
classInfo.put("studentCount", studentCount);
result.put("classInfo", classInfo);
} else {
log.warn("群聊会话 {} 关联的班级 {} 不存在", chatId, chat.getRefId());
result.put("classInfo", null);
}
} catch (Exception e) {
log.error("查询群聊班级信息失败: chatId={}, refId={}, error={}",
chatId, chat.getRefId(), e.getMessage(), e);
result.put("classInfo", null);
}
} else {
result.put("classInfo", null);
}
log.info("查询会话详情成功: chatId={}, type={}", chatId, chat.getType());
return Result.OK(result);
} catch (Exception e) {
log.error("查询会话详情失败: chatId={}, error={}", chatId, e.getMessage(), e);
return Result.error("查询会话详情失败: " + e.getMessage());
}
}
/**
* 开启全员禁言
*
* @param chatId 会话ID
* @return
*/
@AutoLog(value = "会话-开启全员禁言")
@Operation(summary = "开启全员禁言", description = "开启群聊的全员禁言功能设置iz_all_muted字段为1")
@PostMapping(value = "/{chatId}/mute_all")
public Result<String> muteAll(@PathVariable(value = "chatId") String chatId) {
try {
return updateChatSetting(chatId, "iz_all_muted", 1, "开启全员禁言");
} catch (Exception e) {
log.error("开启全员禁言失败: chatId={}, error={}", chatId, e.getMessage(), e);
return Result.error("开启全员禁言失败: " + e.getMessage());
}
}
/**
* 关闭全员禁言
*
* @param chatId 会话ID
* @return
*/
@AutoLog(value = "会话-关闭全员禁言")
@Operation(summary = "关闭全员禁言", description = "关闭群聊的全员禁言功能设置iz_all_muted字段为0")
@PostMapping(value = "/{chatId}/unmute_all")
public Result<String> unmuteAll(@PathVariable(value = "chatId") String chatId) {
try {
return updateChatSetting(chatId, "iz_all_muted", 0, "关闭全员禁言");
} catch (Exception e) {
log.error("关闭全员禁言失败: chatId={}, error={}", chatId, e.getMessage(), e);
return Result.error("关闭全员禁言失败: " + e.getMessage());
}
}
/**
* 开启显示教师标签
*
* @param chatId 会话ID
* @return
*/
@AutoLog(value = "会话-开启显示教师标签")
@Operation(summary = "开启显示教师标签", description = "开启群聊中显示教师标签功能设置show_label字段为1")
@PostMapping(value = "/{chatId}/show_label")
public Result<String> showLabel(@PathVariable(value = "chatId") String chatId) {
try {
return updateChatSetting(chatId, "show_label", 1, "开启显示教师标签");
} catch (Exception e) {
log.error("开启显示教师标签失败: chatId={}, error={}", chatId, e.getMessage(), e);
return Result.error("开启显示教师标签失败: " + e.getMessage());
}
}
/**
* 关闭显示教师标签
*
* @param chatId 会话ID
* @return
*/
@AutoLog(value = "会话-关闭显示教师标签")
@Operation(summary = "关闭显示教师标签", description = "关闭群聊中显示教师标签功能设置show_label字段为0")
@PostMapping(value = "/{chatId}/hide_label")
public Result<String> hideLabel(@PathVariable(value = "chatId") String chatId) {
try {
return updateChatSetting(chatId, "show_label", 0, "关闭显示教师标签");
} catch (Exception e) {
log.error("关闭显示教师标签失败: chatId={}, error={}", chatId, e.getMessage(), e);
return Result.error("关闭显示教师标签失败: " + e.getMessage());
}
}
/**
* 通用更新会话设置的方法
*
* @param chatId 会话ID
* @param fieldName 字段名
* @param value 字段值
* @param operationName 操作名称
* @return
*/
private Result<String> updateChatSetting(String chatId, String fieldName, Integer value, String operationName) {
// 1. 查询会话是否存在
AiolChat chat = aiolChatService.getById(chatId);
if (chat == null) {
return Result.error("会话不存在");
}
// 2. 根据字段名设置相应的值
if ("iz_all_muted".equals(fieldName)) {
chat.setIzAllMuted(value);
} else if ("show_label".equals(fieldName)) {
chat.setShowLabel(value);
} else {
return Result.error("无效的字段名");
}
// 3. 更新数据库
boolean updated = aiolChatService.updateById(chat);
if (!updated) {
return Result.error(operationName + "失败");
}
log.info("{}成功: chatId={}, {}={}", operationName, chatId, fieldName, value);
return Result.OK(operationName + "成功!");
}
/**
* 禁言群聊成员
*
* @param chatId 会话ID
* @param userId 用户ID
* @return
*/
@AutoLog(value = "会话-禁言群聊成员")
@Operation(summary = "禁言群聊成员", description = "禁言指定群聊成员设置iz_muted字段为1")
@PostMapping(value = "/{chatId}/mute_member/{userId}")
public Result<String> muteMember(@PathVariable(value = "chatId") String chatId,
@PathVariable(value = "userId") String userId) {
try {
return updateMemberSetting(chatId, userId, "iz_muted", 1, "禁言群聊成员");
} catch (Exception e) {
log.error("禁言群聊成员失败: chatId={}, userId={}, error={}", chatId, userId, e.getMessage(), e);
return Result.error("禁言群聊成员失败: " + e.getMessage());
}
}
/**
* 解除禁言群聊成员
*
* @param chatId 会话ID
* @param userId 用户ID
* @return
*/
@AutoLog(value = "会话-解除禁言群聊成员")
@Operation(summary = "解除禁言群聊成员", description = "解除指定群聊成员的禁言状态设置iz_muted字段为0")
@PostMapping(value = "/{chatId}/unmute_member/{userId}")
public Result<String> unmuteMember(@PathVariable(value = "chatId") String chatId,
@PathVariable(value = "userId") String userId) {
try {
return updateMemberSetting(chatId, userId, "iz_muted", 0, "解除禁言群聊成员");
} catch (Exception e) {
log.error("解除禁言群聊成员失败: chatId={}, userId={}, error={}", chatId, userId, e.getMessage(), e);
return Result.error("解除禁言群聊成员失败: " + e.getMessage());
}
}
/**
* 开启免打扰
*
* @param chatId 会话ID
* @param userId 用户ID
* @return
*/
@AutoLog(value = "会话-开启免打扰")
@Operation(summary = "开启免打扰", description = "为指定用户开启群聊免打扰功能设置iz_not_disturb字段为1")
@PostMapping(value = "/{chatId}/enable_not_disturb/{userId}")
public Result<String> enableNotDisturb(@PathVariable(value = "chatId") String chatId,
@PathVariable(value = "userId") String userId) {
try {
return updateMemberSetting(chatId, userId, "iz_not_disturb", 1, "开启免打扰");
} catch (Exception e) {
log.error("开启免打扰失败: chatId={}, userId={}, error={}", chatId, userId, e.getMessage(), e);
return Result.error("开启免打扰失败: " + e.getMessage());
}
}
/**
* 关闭免打扰
*
* @param chatId 会话ID
* @param userId 用户ID
* @return
*/
@AutoLog(value = "会话-关闭免打扰")
@Operation(summary = "关闭免打扰", description = "为指定用户关闭群聊免打扰功能设置iz_not_disturb字段为0")
@PostMapping(value = "/{chatId}/disable_not_disturb/{userId}")
public Result<String> disableNotDisturb(@PathVariable(value = "chatId") String chatId,
@PathVariable(value = "userId") String userId) {
try {
return updateMemberSetting(chatId, userId, "iz_not_disturb", 0, "关闭免打扰");
} catch (Exception e) {
log.error("关闭免打扰失败: chatId={}, userId={}, error={}", chatId, userId, e.getMessage(), e);
return Result.error("关闭免打扰失败: " + e.getMessage());
}
}
/**
* 通用更新群聊成员设置的方法
*
* @param chatId 会话ID
* @param userId 用户ID
* @param fieldName 字段名
* @param value 字段值
* @param operationName 操作名称
* @return
*/
private Result<String> updateMemberSetting(String chatId, String userId, String fieldName, Integer value,
String operationName) {
// 1. 查询群聊成员是否存在
QueryWrapper<AiolChatMember> memberWrapper = new QueryWrapper<>();
memberWrapper.eq("chat_id", chatId).eq("user_id", userId);
AiolChatMember chatMember = aiolChatMemberMapper.selectOne(memberWrapper);
if (chatMember == null) {
return Result.error("该用户不是群聊成员或会话不存在");
}
// 2. 根据字段名设置相应的值
if ("iz_muted".equals(fieldName)) {
chatMember.setIzMuted(value);
} else if ("iz_not_disturb".equals(fieldName)) {
chatMember.setIzNotDisturb(value);
} else {
return Result.error("无效的字段名");
}
// 3. 更新数据库
boolean updated = aiolChatMemberMapper.updateById(chatMember) > 0;
if (!updated) {
return Result.error(operationName + "失败");
}
log.info("{}成功: chatId={}, userId={}, {}={}", operationName, chatId, userId, fieldName, value);
return Result.OK(operationName + "成功!");
}
/**
* 将AiolChat转换为包含未读消息数的DTO
*
* @param chat 会话实体
* @param chatMembers 会话成员列表
* @param userId 当前用户ID
* @return 包含未读消息数的DTO
*/
private ChatWithUnreadCountDTO convertToChatWithUnreadCount(AiolChat chat, List<AiolChatMember> chatMembers,
String userId) {
ChatWithUnreadCountDTO chatDTO = new ChatWithUnreadCountDTO();
// 复制基本属性
chatDTO.setId(chat.getId());
chatDTO.setType(chat.getType());
chatDTO.setName(chat.getName());
chatDTO.setAvatar(chat.getAvatar());
chatDTO.setRefId(chat.getRefId());
chatDTO.setIzAllMuted(chat.getIzAllMuted());
chatDTO.setShowLabel(chat.getShowLabel());
chatDTO.setCreateBy(chat.getCreateBy());
chatDTO.setCreateTime(chat.getCreateTime());
chatDTO.setUpdateBy(chat.getUpdateBy());
chatDTO.setUpdateTime(chat.getUpdateTime());
try {
// 1. 计算未读消息数
AiolChatMember currentUserMember = chatMembers.stream()
.filter(member -> member.getChatId().equals(chat.getId()) && member.getUserId().equals(userId))
.findFirst()
.orElse(null);
int unreadCount = 0;
if (currentUserMember != null) {
String lastReadMsgId = currentUserMember.getLastReadMsgId();
// 查询该会话中last_read_msg_id之后的消息数量
QueryWrapper<AiolChatMessage> messageWrapper = new QueryWrapper<>();
messageWrapper.eq("chat_id", chat.getId());
if (lastReadMsgId != null && !lastReadMsgId.trim().isEmpty()) {
// 如果有最后读取的消息ID查询该消息之后的消息
QueryWrapper<AiolChatMessage> lastReadMsgWrapper = new QueryWrapper<>();
lastReadMsgWrapper.eq("chat_id", chat.getId()).eq("id", lastReadMsgId);
AiolChatMessage lastReadMsg = aiolChatMessageMapper.selectOne(lastReadMsgWrapper);
if (lastReadMsg != null) {
// 查询创建时间晚于最后读取消息的消息数量
messageWrapper.gt("create_time", lastReadMsg.getCreateTime());
}
}
Long count = aiolChatMessageMapper.selectCount(messageWrapper);
unreadCount = count != null ? count.intValue() : 0;
}
chatDTO.setUnreadCount(unreadCount);
// 2. 处理私聊类型的会话获取对方用户信息
if (chat.getType() != null && chat.getType() == 0) {
// 私聊类型需要获取对方用户信息
try {
// 查询该会话的成员排除当前用户
QueryWrapper<AiolChatMember> otherMemberWrapper = new QueryWrapper<>();
otherMemberWrapper.eq("chat_id", chat.getId())
.ne("user_id", userId);
List<AiolChatMember> otherMembers = aiolChatMemberMapper.selectList(otherMemberWrapper);
if (!otherMembers.isEmpty()) {
// 获取对方用户ID
String otherUserId = otherMembers.get(0).getUserId();
// 查询对方用户信息
SysUser otherUser = sysUserMapper.selectById(otherUserId);
if (otherUser != null) {
// 替换会话的name和avatar为对方用户信息
chatDTO.setName(otherUser.getRealname());
chatDTO.setAvatar(otherUser.getAvatar());
}
}
} catch (Exception e) {
log.warn("获取私聊对方用户信息失败: chatId={}, error={}", chat.getId(), e.getMessage());
}
}
} catch (Exception e) {
log.error("转换会话信息失败: chatId={}, error={}", chat.getId(), e.getMessage(), e);
// 即使转换失败也返回基本的会话信息
chatDTO.setUnreadCount(0);
}
return chatDTO;
}
/**
* 更新用户最后读取的消息ID
*
* @param chatId 会话ID
* @param messageId 消息ID
* @param request HTTP请求对象
* @return
*/
@AutoLog(value = "会话-更新最后读取消息ID")
@Operation(summary = "更新最后读取消息ID", description = "更新当前用户在指定会话中的最后读取消息ID")
@PostMapping(value = "/{chatId}/update_last_read/{messageId}")
public Result<String> updateLastReadMsgId(@PathVariable(value = "chatId") String chatId,
@PathVariable(value = "messageId") String messageId,
HttpServletRequest request) {
try {
// 1. 从token获取当前用户信息
String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
if (token == null || token.trim().isEmpty()) {
return Result.error("用户未登录");
}
String username = JwtUtil.getUsername(token);
LoginUser sysUser = sysBaseApi.getUserByName(username);
if (sysUser == null) {
return Result.error("用户信息不存在");
}
// 2. 查询群聊成员是否存在
QueryWrapper<AiolChatMember> memberWrapper = new QueryWrapper<>();
memberWrapper.eq("chat_id", chatId).eq("user_id", sysUser.getId());
AiolChatMember chatMember = aiolChatMemberMapper.selectOne(memberWrapper);
if (chatMember == null) {
return Result.error("该用户不是群聊成员或会话不存在");
}
// 3. 验证消息是否存在
AiolChatMessage message = aiolChatMessageMapper.selectById(messageId);
if (message == null || !message.getChatId().equals(chatId)) {
return Result.error("消息不存在或不属于该会话");
}
// 4. 更新最后读取的消息ID
chatMember.setLastReadMsgId(messageId);
boolean updated = aiolChatMemberMapper.updateById(chatMember) > 0;
if (!updated) {
return Result.error("更新失败");
}
log.info("用户 {} 更新会话 {} 的最后读取消息ID为 {}", username, chatId, messageId);
return Result.OK("更新成功!");
} catch (Exception e) {
log.error("更新最后读取消息ID失败: chatId={}, messageId={}, error={}", chatId, messageId, e.getMessage(), e);
return Result.error("更新失败: " + e.getMessage());
}
}
/**
* 退出群聊
*
* @param chatId 会话ID
* @param request HTTP请求对象
* @return
*/
@AutoLog(value = "会话-退出群聊")
@Operation(summary = "退出群聊", description = "当前用户退出指定的群聊会话从aiol_chat_member表中删除用户记录")
@DeleteMapping(value = "/{chatId}/exit")
public Result<String> exitChat(@PathVariable(value = "chatId") String chatId,
HttpServletRequest request) {
try {
// 1. 从token获取当前用户信息
String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
if (token == null || token.trim().isEmpty()) {
return Result.error("用户未登录");
}
String username = JwtUtil.getUsername(token);
LoginUser sysUser = sysBaseApi.getUserByName(username);
if (sysUser == null) {
return Result.error("用户信息不存在");
}
// 2. 验证会话是否存在
AiolChat chat = aiolChatService.getById(chatId);
if (chat == null) {
return Result.error("会话不存在");
}
// 3. 查询用户是否为该会话的成员
QueryWrapper<AiolChatMember> memberWrapper = new QueryWrapper<>();
memberWrapper.eq("chat_id", chatId).eq("user_id", sysUser.getId());
AiolChatMember chatMember = aiolChatMemberMapper.selectOne(memberWrapper);
if (chatMember == null) {
return Result.error("您不是该会话的成员");
}
// 4. 检查是否为会话创建者可选不允许创建者退出
if (chat.getCreateBy() != null && chat.getCreateBy().equals(sysUser.getId())) {
// 可选如果创建者退出需要转移创建者权限或不允许退出
log.warn("会话创建者尝试退出群聊: chatId={}, userId={}", chatId, sysUser.getId());
// 这里可以选择是否允许创建者退出或者需要先转移权限
// return Result.error("群聊创建者不能直接退出,请先转移群主权限");
}
// 5. 从会话成员表中删除该用户
boolean deleted = aiolChatMemberMapper.deleteById(chatMember.getId()) > 0;
if (!deleted) {
return Result.error("退出群聊失败");
}
// 6. 检查会话是否还有其他成员如果没有则删除会话可选
QueryWrapper<AiolChatMember> remainingMemberWrapper = new QueryWrapper<>();
remainingMemberWrapper.eq("chat_id", chatId);
long remainingMemberCount = aiolChatMemberMapper.selectCount(remainingMemberWrapper);
if (remainingMemberCount == 0) {
// 如果没有其他成员了删除会话
aiolChatService.removeById(chatId);
log.info("会话 {} 无成员,已自动删除", chatId);
}
log.info("用户 {} 成功退出群聊 {}", username, chatId);
return Result.OK("退出群聊成功!");
} catch (Exception e) {
log.error("退出群聊失败: chatId={}, error={}", chatId, e.getMessage(), e);
return Result.error("退出群聊失败: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,188 @@
package org.jeecg.modules.aiol.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.aiol.entity.AiolChatMember;
import org.jeecg.modules.aiol.entity.AiolChatMessage;
import org.jeecg.modules.aiol.service.IAiolChatMemberService;
import org.jeecg.modules.aiol.service.IAiolChatMessageService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 会话用户
* @Author: jeecg-boot
* @Date: 2025-09-11
* @Version: V1.0
*/
@Tag(name="会话用户")
@RestController
@RequestMapping("/aiol/aiolChatMember")
@Slf4j
public class AiolChatMemberController extends JeecgController<AiolChatMember, IAiolChatMemberService> {
@Autowired
private IAiolChatMemberService aiolChatMemberService;
/**
* 分页列表查询
*
* @param aiolChatMember
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "会话用户-分页列表查询")
@Operation(summary="会话用户-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolChatMember>> queryPageList(AiolChatMember aiolChatMember,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolChatMember> queryWrapper = QueryGenerator.initQueryWrapper(aiolChatMember, req.getParameterMap());
Page<AiolChatMember> page = new Page<AiolChatMember>(pageNo, pageSize);
IPage<AiolChatMember> pageList = aiolChatMemberService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolChatMember
* @return
*/
@AutoLog(value = "会话用户-添加")
@Operation(summary="会话用户-添加")
@RequiresPermissions("aiol:aiol_chat_member:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolChatMember aiolChatMember) {
aiolChatMemberService.save(aiolChatMember);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param aiolChatMember
* @return
*/
@AutoLog(value = "会话用户-编辑")
@Operation(summary="会话用户-编辑")
@RequiresPermissions("aiol:aiol_chat_member:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AiolChatMember aiolChatMember) {
aiolChatMemberService.updateById(aiolChatMember);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "会话用户-通过id删除")
@Operation(summary="会话用户-通过id删除")
@RequiresPermissions("aiol:aiol_chat_member:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
aiolChatMemberService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "会话用户-批量删除")
@Operation(summary="会话用户-批量删除")
@RequiresPermissions("aiol:aiol_chat_member:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.aiolChatMemberService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "会话用户-通过id查询")
@Operation(summary="会话用户-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolChatMember> queryById(@RequestParam(name="id",required=true) String id) {
AiolChatMember aiolChatMember = aiolChatMemberService.getById(id);
if(aiolChatMember==null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolChatMember);
}
/**
* 导出excel
*
* @param request
* @param aiolChatMember
*/
@RequiresPermissions("aiol:aiol_chat_member:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolChatMember aiolChatMember) {
return super.exportXls(request, aiolChatMember, AiolChatMember.class, "会话用户");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_chat_member:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolChatMember.class);
}
}

View File

@ -0,0 +1,251 @@
package org.jeecg.modules.aiol.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.aiol.entity.AiolChatMember;
import org.jeecg.modules.aiol.entity.AiolChatMessage;
import org.jeecg.modules.aiol.service.IAiolChatMemberService;
import org.jeecg.modules.aiol.service.IAiolChatMessageService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.constant.CommonConstant;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 会话消息
* @Author: jeecg-boot
* @Date: 2025-09-11
* @Version: V1.0
*/
@Tag(name = "会话消息")
@RestController
@RequestMapping("/aiol/aiolChatMessage")
@Slf4j
public class AiolChatMessageController extends JeecgController<AiolChatMessage, IAiolChatMessageService> {
@Autowired
private IAiolChatMessageService aiolChatMessageService;
@Autowired
private ISysBaseAPI sysBaseApi;
@Autowired
private IAiolChatMemberService aiolChatMemberService;
/**
* 分页列表查询
*
* @param aiolChatMessage
* @param pageNo
* @param pageSize
* @param req
* @return
*/
// @AutoLog(value = "会话消息-分页列表查询")
@Operation(summary = "会话消息-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolChatMessage>> queryPageList(AiolChatMessage aiolChatMessage,
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolChatMessage> queryWrapper = QueryGenerator.initQueryWrapper(aiolChatMessage,
req.getParameterMap());
Page<AiolChatMessage> page = new Page<AiolChatMessage>(pageNo, pageSize);
IPage<AiolChatMessage> pageList = aiolChatMessageService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolChatMessage
* @return
*/
@AutoLog(value = "会话消息-添加")
@Operation(summary = "会话消息-添加")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolChatMessage aiolChatMessage) {
aiolChatMessageService.save(aiolChatMessage);
return Result.OK(aiolChatMessage.getId());
}
/**
* 编辑
*
* @param aiolChatMessage
* @return
*/
@AutoLog(value = "会话消息-编辑")
@Operation(summary = "会话消息-编辑")
@RequiresPermissions("aiol:aiol_chat_message:edit")
@RequestMapping(value = "/edit", method = { RequestMethod.PUT, RequestMethod.POST })
public Result<String> edit(@RequestBody AiolChatMessage aiolChatMessage) {
aiolChatMessageService.updateById(aiolChatMessage);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "会话消息-通过id删除")
@Operation(summary = "会话消息-通过id删除")
@RequiresPermissions("aiol:aiol_chat_message:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name = "id", required = true) String id) {
aiolChatMessageService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "会话消息-批量删除")
@Operation(summary = "会话消息-批量删除")
@RequiresPermissions("aiol:aiol_chat_message:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
this.aiolChatMessageService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
// @AutoLog(value = "会话消息-通过id查询")
@Operation(summary = "会话消息-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolChatMessage> queryById(@RequestParam(name = "id", required = true) String id) {
AiolChatMessage aiolChatMessage = aiolChatMessageService.getById(id);
if (aiolChatMessage == null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolChatMessage);
}
/**
* 导出excel
*
* @param request
* @param aiolChatMessage
*/
@RequiresPermissions("aiol:aiol_chat_message:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolChatMessage aiolChatMessage) {
return super.exportXls(request, aiolChatMessage, AiolChatMessage.class, "会话消息");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_chat_message:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolChatMessage.class);
}
/**
* 发送消息
* 前端传递 chat_idcontentmessage_typefile_urlsender_id 从token获取
*/
@AutoLog(value = "会话用户-发送消息")
@Operation(summary = "会话用户-发送消息", description = "发送会话消息sender_id从token获取")
@PostMapping(value = "/send")
public Result<String> sendMessage(@RequestBody Map<String, Object> body, HttpServletRequest request) {
try {
// 1. 从token获取当前用户
String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
if (token == null || token.trim().isEmpty()) {
return Result.error("用户未登录");
}
String username = JwtUtil.getUsername(token);
LoginUser sysUser = sysBaseApi.getUserByName(username);
if (sysUser == null) {
return Result.error("用户信息不存在");
}
// 2. 解析参数
String chatId = body.get("chat_id") != null ? String.valueOf(body.get("chat_id")) : null;
String content = body.get("content") != null ? String.valueOf(body.get("content")) : null;
Integer messageType = body.get("message_type") != null
? Integer.parseInt(String.valueOf(body.get("message_type")))
: 0;
String fileUrl = body.get("file_url") != null ? String.valueOf(body.get("file_url")) : null;
if (chatId == null || chatId.trim().isEmpty()) {
return Result.error("chat_id不能为空");
}
// 3. 校验用户是否在该会话中
QueryWrapper<AiolChatMember> memberWrapper = new QueryWrapper<>();
memberWrapper.eq("chat_id", chatId).eq("user_id", sysUser.getId());
AiolChatMember chatMember = aiolChatMemberService.getOne(memberWrapper);
if (chatMember == null) {
return Result.error("您不是该会话的成员");
}
// 4. 组装消息并保存
AiolChatMessage message = new AiolChatMessage();
message.setChatId(chatId);
message.setSenderId(sysUser.getId());
message.setContent(content);
message.setMessageType(messageType);
message.setStatus(0);
message.setFileUrl(fileUrl);
aiolChatMessageService.save(message);
log.info("用户 {} 在会话 {} 发送消息成功, messageId={}", username, chatId, message.getId());
return Result.OK(message.getId());
} catch (Exception e) {
log.error("发送消息失败: error={}", e.getMessage(), e);
return Result.error("发送消息失败: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,663 @@
package org.jeecg.modules.aiol.controller;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.modules.aiol.entity.AiolClass;
import org.jeecg.modules.aiol.entity.AiolClassStudent;
import org.jeecg.modules.aiol.entity.AiolUserInfo;
import org.jeecg.modules.aiol.service.IAiolClassService;
import org.jeecg.modules.aiol.service.IAiolClassStudentService;
import org.jeecg.modules.aiol.service.IAiolUserInfoService;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.mapper.SysUserMapper;
import org.jeecg.modules.system.service.ISysUserService;
import org.jeecg.modules.aiol.mapper.AiolUserInfoMapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.util.Random;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.constant.CommonConstant;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 班级
* @Author: jeecg-boot
* @Date: 2025-09-04
* @Version: V1.0
*/
@Tag(name="班级")
@RestController
@RequestMapping("/aiol/aiolClass")
@Slf4j
public class AiolClassController extends JeecgController<AiolClass, IAiolClassService> {
@Autowired
private IAiolClassService aiolClassService;
@Autowired
private IAiolClassStudentService aiolClassStudentService;
@Autowired
private ISysBaseAPI sysBaseApi;
@Autowired
private SysUserMapper sysUserMapper;
@Autowired
private ISysUserService sysUserService;
@Autowired
private IAiolUserInfoService aiolUserInfoService;
@Autowired
private AiolUserInfoMapper aiolUserInfoMapper;
/**
* 生成8位不重复的邀请码
* @return 邀请码
*/
private String generateUniqueInviteCode() {
String inviteCode;
int maxAttempts = 100; // 最大尝试次数防止无限循环
int attempts = 0;
do {
inviteCode = generateRandomInviteCode();
attempts++;
if (attempts > maxAttempts) {
log.error("生成邀请码失败,已达到最大尝试次数: {}", maxAttempts);
throw new RuntimeException("生成邀请码失败,请稍后重试");
}
} while (isInviteCodeExists(inviteCode));
log.info("成功生成邀请码: {}, 尝试次数: {}", inviteCode, attempts);
return inviteCode;
}
/**
* 生成8位随机邀请码数字+大写字母
* @return 8位邀请码
*/
private String generateRandomInviteCode() {
String characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
Random random = new Random();
StringBuilder inviteCode = new StringBuilder(8);
for (int i = 0; i < 8; i++) {
inviteCode.append(characters.charAt(random.nextInt(characters.length())));
}
return inviteCode.toString();
}
/**
* 检查邀请码是否已存在
* @param inviteCode 邀请码
* @return true-已存在false-不存在
*/
private boolean isInviteCodeExists(String inviteCode) {
QueryWrapper<AiolClass> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("invite_code", inviteCode);
return aiolClassService.count(queryWrapper) > 0;
}
/**
* 分页列表查询
*
* @param aiolClass
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "班级-分页列表查询")
@Operation(summary="班级-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolClass>> queryPageList(AiolClass aiolClass,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolClass> queryWrapper = QueryGenerator.initQueryWrapper(aiolClass, req.getParameterMap());
Page<AiolClass> page = new Page<AiolClass>(pageNo, pageSize);
IPage<AiolClass> pageList = aiolClassService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolClass
* @return
*/
@AutoLog(value = "班级-添加")
@Operation(summary="班级-添加")
@RequiresPermissions("aiol:aiol_class:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolClass aiolClass) {
try {
// 自动生成8位不重复的邀请码
String inviteCode = generateUniqueInviteCode();
aiolClass.setInviteCode(inviteCode);
aiolClassService.save(aiolClass);
log.info("班级添加成功,班级名: {}, 邀请码: {}", aiolClass.getName(), inviteCode);
return Result.OK(aiolClass.getId());
} catch (Exception e) {
log.error("添加班级失败: {}", e.getMessage(), e);
return Result.error("添加班级失败: " + e.getMessage());
}
}
/**
* 编辑
*
* @param aiolClass
* @return
*/
@AutoLog(value = "班级-编辑")
@Operation(summary="班级-编辑")
@RequiresPermissions("aiol:aiol_class:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AiolClass aiolClass) {
aiolClassService.updateById(aiolClass);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "班级-通过id删除")
@Operation(summary="班级-通过id删除")
@RequiresPermissions("aiol:aiol_class:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
aiolClassService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "班级-批量删除")
@Operation(summary="班级-批量删除")
@RequiresPermissions("aiol:aiol_class:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.aiolClassService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "班级-通过id查询")
@Operation(summary="班级-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolClass> queryById(@RequestParam(name="id",required=true) String id) {
AiolClass aiolClass = aiolClassService.getById(id);
if(aiolClass==null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolClass);
}
/**
* 导出excel
*
* @param request
* @param aiolClass
*/
@RequiresPermissions("aiol:aiol_class:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolClass aiolClass) {
return super.exportXls(request, aiolClass, AiolClass.class, "班级");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_class:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolClass.class);
}
/**
* 批量导入学生到班级
*
* @param classId 班级ID
* @param requestBody 请求体包含ids字段
* @param request HTTP请求对象
* @return
*/
@AutoLog(value = "班级学生-批量导入学生")
@Operation(summary = "批量导入学生到班级", description = "请求体为JSON格式包含ids字段ids为逗号分隔的学生ID字符串")
@PostMapping(value = "/{classId}/import_students")
public Result<Map<String, Object>> importStudentsToClass(
@PathVariable(value = "classId") String classId,
@RequestBody Map<String, Object> requestBody,
HttpServletRequest request) {
try {
// 1. 获取当前登录用户信息
String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
String username = JwtUtil.getUsername(token);
LoginUser sysUser = sysBaseApi.getUserByName(username);
if (sysUser == null) {
return Result.error("用户未登录或登录已过期");
}
// 2. 从请求体中获取学生ID列表
String ids = (String) requestBody.get("ids");
if (ids == null || ids.trim().isEmpty()) {
return Result.error("ids字段不能为空");
}
// 3. 调用Service层处理批量导入逻辑
Map<String, Object> result = aiolClassService.importStudentsToClass(classId, ids, sysUser);
log.info("用户 {} 成功导入学生到班级班级ID: {}, 导入结果: {}",
username, classId, result);
return Result.OK(result);
} catch (Exception e) {
log.error("批量导入学生到班级失败: classId={}, error={}", classId, e.getMessage(), e);
return Result.error("批量导入学生失败: " + e.getMessage());
}
}
/**
* 查询班级学生列表
*
* @param classId 班级ID
* @return
*/
@Operation(summary = "查询班级学生列表", description = "根据班级ID查询该班级下的所有学生")
@GetMapping(value = "/{classId}/student_list")
public Result<List<Map<String, Object>>> queryClassStudentList(@PathVariable("classId") String classId) {
try {
// 1. 查询班级学生关联记录
QueryWrapper<AiolClassStudent> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("class_id", classId);
List<AiolClassStudent> classStudents = aiolClassStudentService.list(queryWrapper);
// 2. 联查用户信息构建包含学生信息的列表
List<Map<String, Object>> result = new ArrayList<>();
for (AiolClassStudent classStudent : classStudents) {
Map<String, Object> studentInfo = new HashMap<>();
// 复制班级学生关联信息
studentInfo.put("id", classStudent.getId());
studentInfo.put("classId", classStudent.getClassId());
studentInfo.put("studentId", classStudent.getStudentId());
studentInfo.put("createTime", classStudent.getCreateTime());
studentInfo.put("createBy", classStudent.getCreateBy());
studentInfo.put("updateTime", classStudent.getUpdateTime());
studentInfo.put("updateBy", classStudent.getUpdateBy());
// 查询学生用户信息
SysUser sysUser = sysUserMapper.selectById(classStudent.getStudentId());
if (sysUser != null) {
studentInfo.put("realname", sysUser.getRealname());
studentInfo.put("userAvatar", sysUser.getAvatar());
studentInfo.put("username", sysUser.getUsername());
studentInfo.put("phone", sysUser.getPhone());
studentInfo.put("email", sysUser.getEmail());
studentInfo.put("status", sysUser.getStatus());
studentInfo.put("sex", sysUser.getSex());
studentInfo.put("birthday", sysUser.getBirthday());
}
// 查询学生扩展信息aiol_user_info
QueryWrapper<AiolUserInfo> userInfoWrapper = new QueryWrapper<>();
userInfoWrapper.eq("user_id", classStudent.getStudentId());
AiolUserInfo userInfo = aiolUserInfoMapper.selectOne(userInfoWrapper);
if (userInfo != null) {
studentInfo.put("major", userInfo.getMajor());
studentInfo.put("college", userInfo.getCollege());
studentInfo.put("education", userInfo.getEducation());
studentInfo.put("title", userInfo.getTitle());
} else {
// 如果没有扩展信息设置默认值
studentInfo.put("major", "");
studentInfo.put("college", "");
studentInfo.put("education", "");
studentInfo.put("title", "");
}
result.add(studentInfo);
}
return Result.OK(result);
} catch (Exception e) {
log.error("查询班级学生列表失败: classId={}, error={}", classId, e.getMessage(), e);
return Result.error("查询班级学生列表失败: " + e.getMessage());
}
}
/**
* 从班级中移除学生
*
* @param classId 班级ID
* @param studentId 学生ID
* @return
*/
@AutoLog(value = "班级学生-移除学生")
@Operation(summary = "从班级中移除学生", description = "将指定学生从班级中移除")
@DeleteMapping(value = "/{classId}/remove_student/{studentId}")
public Result<String> removeStudentFromClass(
@PathVariable("classId") String classId,
@PathVariable("studentId") String studentId) {
try {
QueryWrapper<AiolClassStudent> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("class_id", classId)
.eq("student_id", studentId);
boolean removed = aiolClassStudentService.remove(queryWrapper);
if (removed) {
log.info("成功从班级移除学生班级ID: {}, 学生ID: {}", classId, studentId);
return Result.OK("学生移除成功!");
} else {
return Result.error("未找到该学生在班级中的记录");
}
} catch (Exception e) {
log.error("从班级移除学生失败: classId={}, studentId={}, error={}",
classId, studentId, e.getMessage(), e);
return Result.error("移除学生失败: " + e.getMessage());
}
}
/**
* 导入学生到班级通过Excel
*
* @param classId 班级ID
* @param request HTTP请求对象
* @return
*/
@AutoLog(value = "班级学生-导入学生")
@Operation(summary = "导入学生到班级", description = "通过Excel文件导入学生到指定班级如果学生不存在则自动创建")
@PostMapping(value = "/{classId}/import_students_excel")
public Result<Map<String, Object>> importStudentsToClassByExcel(
@PathVariable("classId") String classId,
HttpServletRequest request) {
try {
// 1. 获取当前登录用户信息
String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
String username = JwtUtil.getUsername(token);
LoginUser sysUser = sysBaseApi.getUserByName(username);
if (sysUser == null) {
return Result.error("用户未登录或登录已过期");
}
// 2. 模拟Excel数据暂时不实现Excel解析直接使用模拟数据
List<Map<String, String>> studentDataList = new ArrayList<>();
// 模拟数据示例
Map<String, String> student1 = new HashMap<>();
student1.put("studentNumber", "2024001");
student1.put("realName", "张三");
studentDataList.add(student1);
Map<String, String> student2 = new HashMap<>();
student2.put("studentNumber", "2024002");
student2.put("realName", "李四");
studentDataList.add(student2);
// 3. 处理学生数据
int successCount = 0;
int failCount = 0;
List<String> errorMessages = new ArrayList<>();
List<String> createdStudentIds = new ArrayList<>();
for (Map<String, String> studentData : studentDataList) {
try {
String studentNumber = studentData.get("studentNumber");
String realName = studentData.get("realName");
// 检查学生是否已存在
SysUser existingStudent = sysUserService.getUserByName(studentNumber);
String studentId;
if (existingStudent != null) {
// 学生已存在直接使用现有学生ID
studentId = existingStudent.getId();
log.info("学生已存在,使用现有学生: 学号={}, 姓名={}", studentNumber, realName);
} else {
// 学生不存在创建新学生
SysUser newStudent = sysUserService.createStudentUser(studentNumber, realName, null);
studentId = newStudent.getId();
createdStudentIds.add(studentId);
log.info("创建新学生: 学号={}, 姓名={}, ID={}", studentNumber, realName, studentId);
}
// 检查学生是否已在班级中
QueryWrapper<AiolClassStudent> checkWrapper = new QueryWrapper<>();
checkWrapper.eq("class_id", classId)
.eq("student_id", studentId);
AiolClassStudent existingRelation = aiolClassStudentService.getOne(checkWrapper);
if (existingRelation != null) {
log.info("学生已在班级中: 学号={}, 班级ID={}", studentNumber, classId);
continue; // 跳过已存在的学生
}
// 创建班级学生关联
AiolClassStudent classStudent = new AiolClassStudent();
classStudent.setClassId(classId);
classStudent.setStudentId(studentId);
classStudent.setCreateBy(sysUser.getUsername());
classStudent.setCreateTime(new Date());
boolean saved = aiolClassStudentService.save(classStudent);
if (saved) {
successCount++;
log.info("成功将学生添加到班级: 学号={}, 班级ID={}", studentNumber, classId);
} else {
failCount++;
errorMessages.add("保存学生 " + studentNumber + " 到班级失败");
}
} catch (Exception e) {
failCount++;
String errorMsg = "处理学生 " + studentData.get("studentNumber") + " 失败: " + e.getMessage();
errorMessages.add(errorMsg);
log.error(errorMsg, e);
}
}
// 4. 构建返回结果
Map<String, Object> result = new HashMap<>();
result.put("totalCount", studentDataList.size());
result.put("successCount", successCount);
result.put("failCount", failCount);
result.put("createdStudentCount", createdStudentIds.size());
result.put("errorMessages", errorMessages);
result.put("createdStudentIds", createdStudentIds);
log.info("导入学生完成: 班级ID={}, 总数={}, 成功={}, 失败={}, 新创建学生={}",
classId, studentDataList.size(), successCount, failCount, createdStudentIds.size());
return Result.OK(result);
} catch (Exception e) {
log.error("导入学生到班级失败: classId={}, error={}", classId, e.getMessage(), e);
return Result.error("导入学生失败: " + e.getMessage());
}
}
/**
* 查询班级列表
*
* @param courseId 可选课程ID不传时查询当前登录用户创建的全部班级
* @param request HTTP请求对象用于获取登录态
* @return 班级列表
*/
@Operation(summary = "查询班级列表", description = "参数courseId可选不传则查当前登录用户创建的全部班级传则查对应课程id的班级")
@GetMapping(value = "/query_list")
public Result<List<AiolClass>> queryClassList(
@RequestParam(name = "courseId", required = false) String courseId,
HttpServletRequest request) {
try {
QueryWrapper<AiolClass> queryWrapper = new QueryWrapper<>();
if (courseId == null || courseId.trim().isEmpty()) {
// 未传courseId查询当前登录用户创建的班级
String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
String username = JwtUtil.getUsername(token);
LoginUser sysUser = sysBaseApi.getUserByName(username);
if (sysUser == null) {
return Result.error("用户未登录或登录已过期");
}
queryWrapper.eq("create_by", sysUser.getUsername());
} else {
// 传了courseId按课程查询
queryWrapper.eq("course_id", courseId);
}
List<AiolClass> list = aiolClassService.list(queryWrapper);
return Result.OK(list);
} catch (Exception e) {
log.error("查询班级列表失败: courseId={}, error={}", courseId, e.getMessage(), e);
return Result.error("查询班级列表失败: " + e.getMessage());
}
}
/**
* 新建学生并添加至班级
*
* 前端传参realName(姓名)studentNumber(学号)password(登录密码可选)school(所属学校)classId(所属班级ID支持多个用逗号分割)
*/
@AutoLog(value = "班级学生-新建学生并添加至班级")
@Operation(summary = "新建学生并添加至班级", description = "创建sysUser保存aiol_user_info的学校信息并写入aiol_class_student。支持多个班级ID用逗号分割")
@PostMapping(value = "/create_and_add_student")
public Result<Map<String, Object>> createStudentAndAddToClass(@RequestBody Map<String, Object> body,
HttpServletRequest request) {
try {
String realName = (String) body.get("realName");
String studentNumber = (String) body.get("studentNumber");
String password = (String) body.get("password");
String school = (String) body.get("school");
String classId = (String) body.get("classId");
if (realName == null || realName.trim().isEmpty()) {
return Result.error("姓名不能为空");
}
if (studentNumber == null || studentNumber.trim().isEmpty()) {
return Result.error("学号不能为空");
}
if (classId == null || classId.trim().isEmpty()) {
return Result.error("班级ID不能为空");
}
// 获取当前登录用户
String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
String username = JwtUtil.getUsername(token);
LoginUser sysUser = sysBaseApi.getUserByName(username);
if (sysUser == null) {
return Result.error("用户未登录或登录已过期");
}
// 1) 创建学生用户sys_user
SysUser created = sysUserService.createStudentUser(studentNumber, realName, password);
// 2) 保存学校至 aiol_user_info使用 college 字段存储学校
AiolUserInfo userInfo = new AiolUserInfo();
userInfo.setUserId(created.getId());
userInfo.setCollege(school);
userInfo.setCreateBy(sysUser.getUsername());
userInfo.setCreateTime(new Date());
aiolUserInfoService.save(userInfo);
// 3) 写入班级关系 aiol_class_student
// 支持多个班级ID用逗号分割
String[] classIds = classId.split(",");
int successCount = 0;
int skipCount = 0;
for (String singleClassId : classIds) {
if (singleClassId == null || singleClassId.trim().isEmpty()) {
continue;
}
singleClassId = singleClassId.trim();
// 检查是否已存在关系避免重复
QueryWrapper<AiolClassStudent> checkWrapper = new QueryWrapper<>();
checkWrapper.eq("class_id", singleClassId).eq("student_id", created.getId());
AiolClassStudent exist = aiolClassStudentService.getOne(checkWrapper);
if (exist == null) {
AiolClassStudent relation = new AiolClassStudent();
relation.setClassId(singleClassId);
relation.setStudentId(created.getId());
relation.setCreateBy(sysUser.getUsername());
relation.setCreateTime(new Date());
boolean saved = aiolClassStudentService.save(relation);
if (saved) {
successCount++;
log.info("成功将学生 {} 添加到班级 {}", created.getUsername(), singleClassId);
}
} else {
skipCount++;
log.info("学生 {} 已在班级 {} 中,跳过", created.getUsername(), singleClassId);
}
}
Map<String, Object> resp = new HashMap<>();
resp.put("userId", created.getId());
resp.put("username", created.getUsername());
resp.put("classId", classId);
resp.put("school", school);
resp.put("totalClassCount", classIds.length);
resp.put("successCount", successCount);
resp.put("skipCount", skipCount);
return Result.OK(resp);
} catch (Exception e) {
log.error("新建学生并添加至班级失败: body={}, error={}", body, e.getMessage(), e);
return Result.error("新建学生并添加至班级失败: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,321 @@
package org.jeecg.modules.aiol.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Date;
import java.util.ArrayList;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.modules.aiol.entity.AiolClassStudent;
import org.jeecg.modules.aiol.service.IAiolClassStudentService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.constant.CommonConstant;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 班级学生
* @Author: jeecg-boot
* @Date: 2025-09-04
* @Version: V1.0
*/
@Tag(name="班级学生")
@RestController
@RequestMapping("/aiol/aiolClassStudent")
@Slf4j
public class AiolClassStudentController extends JeecgController<AiolClassStudent, IAiolClassStudentService> {
@Autowired
private IAiolClassStudentService aiolClassStudentService;
@Autowired
private ISysBaseAPI sysBaseApi;
/**
* 分页列表查询
*
* @param aiolClassStudent
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "班级学生-分页列表查询")
@Operation(summary="班级学生-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolClassStudent>> queryPageList(AiolClassStudent aiolClassStudent,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolClassStudent> queryWrapper = QueryGenerator.initQueryWrapper(aiolClassStudent, req.getParameterMap());
Page<AiolClassStudent> page = new Page<AiolClassStudent>(pageNo, pageSize);
IPage<AiolClassStudent> pageList = aiolClassStudentService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolClassStudent
* @return
*/
@AutoLog(value = "班级学生-添加")
@Operation(summary="班级学生-添加")
@RequiresPermissions("aiol:aiol_class_student:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolClassStudent aiolClassStudent) {
aiolClassStudentService.save(aiolClassStudent);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param aiolClassStudent
* @return
*/
@AutoLog(value = "班级学生-编辑")
@Operation(summary="班级学生-编辑")
@RequiresPermissions("aiol:aiol_class_student:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AiolClassStudent aiolClassStudent) {
aiolClassStudentService.updateById(aiolClassStudent);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "班级学生-通过id删除")
@Operation(summary="班级学生-通过id删除")
@RequiresPermissions("aiol:aiol_class_student:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
aiolClassStudentService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "班级学生-批量删除")
@Operation(summary="班级学生-批量删除")
@RequiresPermissions("aiol:aiol_class_student:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.aiolClassStudentService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "班级学生-通过id查询")
@Operation(summary="班级学生-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolClassStudent> queryById(@RequestParam(name="id",required=true) String id) {
AiolClassStudent aiolClassStudent = aiolClassStudentService.getById(id);
if(aiolClassStudent==null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolClassStudent);
}
/**
* 导出excel
*
* @param request
* @param aiolClassStudent
*/
@RequiresPermissions("aiol:aiol_class_student:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolClassStudent aiolClassStudent) {
return super.exportXls(request, aiolClassStudent, AiolClassStudent.class, "班级学生");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_class_student:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolClassStudent.class);
}
/**
* 批量调班
*
* @param requestBody 包含学生ID列表原班级ID和新班级ID
* @param request HTTP请求对象
* @return 调班结果
*/
@AutoLog(value = "班级学生-批量调班")
@Operation(summary = "批量调班", description = "将指定学生从原班级调转到新班级")
@PostMapping(value = "/batchTransfer")
public Result<Map<String, Object>> batchTransfer(@RequestBody Map<String, Object> requestBody,
HttpServletRequest request) {
try {
// 1. 获取当前登录用户信息
String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
String username = JwtUtil.getUsername(token);
LoginUser sysUser = sysBaseApi.getUserByName(username);
if (sysUser == null) {
return Result.error("用户未登录或登录已过期");
}
// 2. 从请求体中获取参数
@SuppressWarnings("unchecked")
List<String> studentIds = (List<String>) requestBody.get("studentIds");
String originalClassId = (String) requestBody.get("originalClassId");
String newClassId = (String) requestBody.get("newClassId");
// 3. 参数验证
if (studentIds == null || studentIds.isEmpty()) {
return Result.error("学生ID列表不能为空");
}
if (originalClassId == null || originalClassId.trim().isEmpty()) {
return Result.error("原班级ID不能为空");
}
if (newClassId == null || newClassId.trim().isEmpty()) {
return Result.error("新班级ID不能为空");
}
if (originalClassId.equals(newClassId)) {
return Result.error("原班级和新班级不能相同");
}
// 4. 执行批量调班操作
Map<String, Object> result = performBatchTransfer(studentIds, originalClassId, newClassId, sysUser);
log.info("用户 {} 执行批量调班操作完成,结果: {}", username, result);
return Result.OK(result);
} catch (Exception e) {
log.error("批量调班失败: {}", e.getMessage(), e);
return Result.error("批量调班失败: " + e.getMessage());
}
}
/**
* 执行批量调班操作
*
* @param studentIds 学生ID列表
* @param originalClassId 原班级ID
* @param newClassId 新班级ID
* @param sysUser 当前登录用户
* @return 调班结果统计
*/
private Map<String, Object> performBatchTransfer(List<String> studentIds, String originalClassId,
String newClassId, LoginUser sysUser) {
Map<String, Object> result = new HashMap<>();
int successCount = 0;
int failCount = 0;
int notFoundCount = 0;
int alreadyInNewClassCount = 0;
List<String> successStudentIds = new ArrayList<>();
List<String> failStudentIds = new ArrayList<>();
List<String> notFoundStudentIds = new ArrayList<>();
List<String> alreadyInNewClassStudentIds = new ArrayList<>();
for (String studentId : studentIds) {
try {
// 1. 查找原班级中的学生记录
QueryWrapper<AiolClassStudent> originalQuery = new QueryWrapper<>();
originalQuery.eq("class_id", originalClassId)
.eq("student_id", studentId);
AiolClassStudent originalRecord = aiolClassStudentService.getOne(originalQuery);
if (originalRecord == null) {
notFoundCount++;
notFoundStudentIds.add(studentId);
log.warn("学生 {} 在原班级 {} 中不存在", studentId, originalClassId);
continue;
}
// 2. 检查学生是否已在新班级中
QueryWrapper<AiolClassStudent> newClassQuery = new QueryWrapper<>();
newClassQuery.eq("class_id", newClassId)
.eq("student_id", studentId);
AiolClassStudent existingInNewClass = aiolClassStudentService.getOne(newClassQuery);
if (existingInNewClass != null) {
alreadyInNewClassCount++;
alreadyInNewClassStudentIds.add(studentId);
log.warn("学生 {} 已在新班级 {} 中", studentId, newClassId);
continue;
}
// 3. 执行调班操作更新原记录的班级ID
originalRecord.setClassId(newClassId);
originalRecord.setUpdateBy(sysUser.getUsername());
originalRecord.setUpdateTime(new Date());
boolean updateResult = aiolClassStudentService.updateById(originalRecord);
if (updateResult) {
successCount++;
successStudentIds.add(studentId);
log.info("成功将学生 {} 从班级 {} 调转到班级 {}", studentId, originalClassId, newClassId);
} else {
failCount++;
failStudentIds.add(studentId);
log.error("更新学生 {} 的班级信息失败", studentId);
}
} catch (Exception e) {
failCount++;
failStudentIds.add(studentId);
log.error("处理学生 {} 调班时发生异常: {}", studentId, e.getMessage(), e);
}
}
// 4. 构建返回结果
result.put("totalCount", studentIds.size());
result.put("successCount", successCount);
result.put("failCount", failCount);
result.put("notFoundCount", notFoundCount);
result.put("alreadyInNewClassCount", alreadyInNewClassCount);
result.put("originalClassId", originalClassId);
result.put("newClassId", newClassId);
result.put("successStudentIds", successStudentIds);
result.put("failStudentIds", failStudentIds);
result.put("notFoundStudentIds", notFoundStudentIds);
result.put("alreadyInNewClassStudentIds", alreadyInNewClassStudentIds);
log.info("批量调班操作完成 - 总数: {}, 成功: {}, 失败: {}, 未找到: {}, 已在新班级: {}",
studentIds.size(), successCount, failCount, notFoundCount, alreadyInNewClassCount);
return result;
}
}

View File

@ -0,0 +1,564 @@
package org.jeecg.modules.aiol.controller;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.config.shiro.IgnoreAuth;
import org.jeecg.modules.aiol.dto.CommentWithUserInfo;
import org.jeecg.modules.aiol.entity.AiolComment;
import org.jeecg.modules.aiol.service.IAiolCommentService;
import org.jeecg.modules.aiol.entity.AiolCourse;
import org.jeecg.modules.aiol.entity.AiolDiscussion;
import org.jeecg.modules.aiol.service.IAiolCourseService;
import org.jeecg.modules.aiol.service.IAiolDiscussionService;
import org.jeecg.modules.aiol.utils.MessageNotificationUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.constant.CommonConstant;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 评论
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Tag(name = "评论")
@RestController
@RequestMapping("/aiol/aiolComment")
@Slf4j
public class AiolCommentController extends JeecgController<AiolComment, IAiolCommentService> {
@Autowired
private IAiolCommentService aiolCommentService;
@Autowired
private MessageNotificationUtil messageNotificationUtil;
@Autowired
private IAiolCourseService aiolCourseService;
@Autowired
private IAiolDiscussionService aiolDiscussionService;
/**
* 分页列表查询
*
* @param aiolComment
* @param pageNo
* @param pageSize
* @param req
* @return
*/
// @AutoLog(value = "评论-分页列表查询")
@Operation(summary = "评论-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolComment>> queryPageList(AiolComment aiolComment,
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolComment> queryWrapper = QueryGenerator.initQueryWrapper(aiolComment, req.getParameterMap());
Page<AiolComment> page = new Page<AiolComment>(pageNo, pageSize);
IPage<AiolComment> pageList = aiolCommentService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolComment
* @return
*/
@AutoLog(value = "评论-添加")
@Operation(summary = "评论-添加")
@RequiresPermissions("aiol:aiol_comment:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolComment aiolComment, HttpServletRequest request) {
try {
// 1. 获取当前登录用户信息
String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
String username = JwtUtil.getUsername(token);
LoginUser sysUser = sysBaseApi.getUserByName(username);
if (sysUser == null) {
return Result.error("用户未登录或登录已过期");
}
aiolComment.setUserId(sysUser.getId()); // 用户ID
aiolComment.setLikeCount(0);
// 2. 保存评论
aiolCommentService.save(aiolComment);
// 3. 发送评论通知
sendCommentNotification(aiolComment, sysUser);
log.info("用户 {} 成功添加评论评论ID: {}, 目标类型: {}, 目标ID: {}",
username, aiolComment.getId(), aiolComment.getTargetType(), aiolComment.getTargetId());
return Result.OK(aiolComment.getId());
} catch (Exception e) {
log.error("添加评论失败: targetType={}, targetId={}, error={}",
aiolComment.getTargetType(), aiolComment.getTargetId(), e.getMessage(), e);
return Result.error("添加评论失败: " + e.getMessage());
}
}
/**
* 编辑
*
* @param aiolComment
* @return
*/
@AutoLog(value = "评论-编辑")
@Operation(summary = "评论-编辑")
@RequiresPermissions("aiol:aiol_comment:edit")
@RequestMapping(value = "/edit", method = { RequestMethod.PUT, RequestMethod.POST })
public Result<String> edit(@RequestBody AiolComment aiolComment) {
aiolCommentService.updateById(aiolComment);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "评论-通过id删除")
@Operation(summary = "评论-通过id删除")
// @RequiresPermissions("aiol:aiol_comment:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name = "id", required = true) String id) {
aiolCommentService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "评论-批量删除")
@Operation(summary = "评论-批量删除")
@RequiresPermissions("aiol:aiol_comment:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
this.aiolCommentService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
// @AutoLog(value = "评论-通过id查询")
@Operation(summary = "评论-通过id查询")
@GetMapping(value = "/queryById")
@IgnoreAuth
public Result<AiolComment> queryById(@RequestParam(name = "id", required = true) String id) {
AiolComment aiolComment = aiolCommentService.getById(id);
if (aiolComment == null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolComment);
}
/**
* 导出excel
*
* @param request
* @param aiolComment
*/
@RequiresPermissions("aiol:aiol_comment:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolComment aiolComment) {
return super.exportXls(request, aiolComment, AiolComment.class, "评论");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_comment:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolComment.class);
}
@GetMapping("/course/{courseId}/list")
@Operation(summary = "查询课程评论列表", description = "根据课程ID查询课程评论列表包含用户信息和所有子评论支持多层嵌套所有子评论都放在顶级评论的replies中")
@IgnoreAuth
public Result<List<CommentWithUserInfo>> queryCourseCommentList(@PathVariable("courseId") String courseId) {
try {
// 1. 查询课程的一级评论target_type=course, target_id=courseId
List<CommentWithUserInfo> parentComments = aiolCommentService.getCommentList("course", courseId);
// 2. 为每个一级评论查询其所有子评论递归查询所有层级
for (CommentWithUserInfo parentComment : parentComments) {
List<CommentWithUserInfo> allReplies = getAllRepliesRecursively(parentComment.getId());
parentComment.setReplies(allReplies);
}
log.info("查询课程评论列表成功: courseId={}, 一级评论数量={}", courseId, parentComments.size());
return Result.OK(parentComments);
} catch (Exception e) {
log.error("查询课程评论列表失败: courseId={}, error={}", courseId, e.getMessage(), e);
return Result.error("查询课程评论列表失败: " + e.getMessage());
}
}
@GetMapping("/discussion/{discussionId}/list")
@Operation(summary = "查询讨论评论列表", description = "根据讨论ID查询讨论评论列表包含用户信息和所有子评论支持多层嵌套所有子评论都放在顶级评论的replies中")
@IgnoreAuth
public Result<List<CommentWithUserInfo>> queryDiscussionCommentList(@PathVariable("discussionId") String discussionId) {
try {
// 1. 查询讨论的一级评论target_type=discussion, target_id=discussionId
List<CommentWithUserInfo> parentComments = aiolCommentService.getCommentList("discussion", discussionId);
// 2. 为每个一级评论查询其所有子评论递归查询所有层级
for (CommentWithUserInfo parentComment : parentComments) {
List<CommentWithUserInfo> allReplies = getAllRepliesRecursively(parentComment.getId());
parentComment.setReplies(allReplies);
}
log.info("查询讨论评论列表成功: discussionId={}, 一级评论数量={}", discussionId, parentComments.size());
return Result.OK(parentComments);
} catch (Exception e) {
log.error("查询讨论评论列表失败: discussionId={}, error={}", discussionId, e.getMessage(), e);
return Result.error("查询讨论评论列表失败: " + e.getMessage());
}
}
/**
* 递归查询评论的所有子评论
* @param commentId 评论ID
* @return 所有子评论列表
*/
private List<CommentWithUserInfo> getAllRepliesRecursively(String commentId) {
List<CommentWithUserInfo> allReplies = new ArrayList<>();
// 查询直接子评论target_type=comment, target_id=commentId
List<CommentWithUserInfo> directReplies = aiolCommentService.getCommentList("comment", commentId);
for (CommentWithUserInfo reply : directReplies) {
// 将当前回复添加到总列表中
allReplies.add(reply);
// 递归查询当前回复的子评论
List<CommentWithUserInfo> subReplies = getAllRepliesRecursively(reply.getId());
allReplies.addAll(subReplies);
}
return allReplies;
}
// @GetMapping("/activity/{activityId}/list")
// @Operation(summary = "查询活动评论列表", description = "根据活动ID查询活动评论列表包含用户信息")
// public Result<List<CommentWithUserInfo>>
// queryActivityCommentList(@PathVariable("activityId") String activityId) {
// List<CommentWithUserInfo> list = commentBizService.getCommentList("activity",
// activityId);
// return Result.OK(list);
// }
// @GetMapping("/{targetType}/{targetId}/list")
// @Operation(summary = "查询通用评论列表", description = "根据目标类型和目标ID查询评论列表包含用户信息")
// public Result<List<CommentWithUserInfo>> queryCommentList(
// @PathVariable("targetType") String targetType,
// @PathVariable("targetId") String targetId) {
// List<CommentWithUserInfo> list = commentBizService.getCommentList(targetType,
// targetId);
// return Result.OK(list);
// }
@Autowired
private ISysBaseAPI sysBaseApi;
/**
* 新增课程评论
*
* @param courseId 课程ID
* @param aiolComment 评论信息
* @param request HTTP请求对象
* @return
*/
// @AutoLog(value = "评论-新增课程评论")
// @Operation(summary = "新增课程评论", description = "新增课程评论target_type默认为courseuser_id通过token自动获取")
// @PostMapping(value = "/course/{courseId}/add")
// public Result<String> addCourseComment(
// @PathVariable("courseId") String courseId,
// @RequestBody AiolComment aiolComment,
// HttpServletRequest request) {
// try {
// // 1. 获取当前登录用户信息
// String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
// String username = JwtUtil.getUsername(token);
// LoginUser sysUser = sysBaseApi.getUserByName(username);
// if (sysUser == null) {
// return Result.error("用户未登录或登录已过期");
// }
// // 2. 设置评论基本信息
// aiolComment.setTargetType("course");
// aiolComment.setTargetId(courseId); // 课程ID
// aiolComment.setUserId(sysUser.getId()); // 用户ID
// // 3. 保存评论
// aiolCommentService.save(aiolComment);
// log.info("用户 {} 成功添加课程评论课程ID: {}, 评论ID: {}",
// username, courseId, aiolComment.getId());
// return Result.OK("评论添加成功!");
// } catch (Exception e) {
// log.error("添加课程评论失败: courseId={}, error={}", courseId, e.getMessage(), e);
// return Result.error("评论添加失败: " + e.getMessage());
// }
// }
@AutoLog(value = "评论-点赞")
@Operation(summary = "点赞评论", description = "为指定评论点赞增加like_count字段值并发送通知")
@GetMapping(value = "/like/{commentId}")
public Result<String> likeComment(@PathVariable("commentId") String commentId, HttpServletRequest request) {
try {
// 1. 获取当前登录用户信息
String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
String username = JwtUtil.getUsername(token);
LoginUser sysUser = sysBaseApi.getUserByName(username);
if (sysUser == null) {
return Result.error("用户未登录或登录已过期");
}
// 2. 查询评论是否存在
AiolComment comment = aiolCommentService.getById(commentId);
if (comment == null) {
return Result.error("评论不存在");
}
// 3. 更新点赞数
Integer currentLikeCount = comment.getLikeCount() != null ? comment.getLikeCount() : 0;
comment.setLikeCount(currentLikeCount + 1);
boolean updated = aiolCommentService.updateById(comment);
if (!updated) {
return Result.error("点赞失败");
}
// 4. 发送点赞通知
sendLikeNotification(comment, sysUser);
log.info("用户 {} 评论点赞成功: commentId={}, 当前点赞数={}", username, commentId, comment.getLikeCount());
return Result.OK("点赞成功!");
} catch (Exception e) {
log.error("评论点赞失败: commentId={}, error={}", commentId, e.getMessage(), e);
return Result.error("点赞失败: " + e.getMessage());
}
}
@AutoLog(value = "评论-置顶")
@Operation(summary = "置顶评论", description = "将指定评论置顶设置iz_top字段为1")
@RequiresPermissions("aiol:aiol_comment:edit")
@GetMapping(value = "/top/{commentId}")
public Result<String> topComment(@PathVariable("commentId") String commentId) {
try {
// 1. 查询评论是否存在
AiolComment comment = aiolCommentService.getById(commentId);
if (comment == null) {
return Result.error("评论不存在");
}
// 2. 设置置顶状态
comment.setIzTop(1);
boolean updated = aiolCommentService.updateById(comment);
if (!updated) {
return Result.error("置顶失败");
}
log.info("评论置顶成功: commentId={}", commentId);
return Result.OK("置顶成功!");
} catch (Exception e) {
log.error("评论置顶失败: commentId={}, error={}", commentId, e.getMessage(), e);
return Result.error("置顶失败: " + e.getMessage());
}
}
@AutoLog(value = "评论-取消置顶")
@Operation(summary = "取消置顶评论", description = "将指定评论取消置顶设置iz_top字段为0")
@RequiresPermissions("aiol:aiol_comment:edit")
@GetMapping(value = "/untop/{commentId}")
public Result<String> untopComment(@PathVariable("commentId") String commentId) {
try {
// 1. 查询评论是否存在
AiolComment comment = aiolCommentService.getById(commentId);
if (comment == null) {
return Result.error("评论不存在");
}
// 2. 取消置顶状态
comment.setIzTop(0);
boolean updated = aiolCommentService.updateById(comment);
if (!updated) {
return Result.error("取消置顶失败");
}
log.info("评论取消置顶成功: commentId={}", commentId);
return Result.OK("取消置顶成功!");
} catch (Exception e) {
log.error("评论取消置顶失败: commentId={}, error={}", commentId, e.getMessage(), e);
return Result.error("取消置顶失败: " + e.getMessage());
}
}
/**
* 发送评论通知
*
* @param aiolComment 评论对象
* @param sysUser 当前用户
*/
private void sendCommentNotification(AiolComment aiolComment, LoginUser sysUser) {
try {
String targetType = aiolComment.getTargetType();
String targetId = aiolComment.getTargetId();
// 确定接收通知的用户和实体标题
String toUserId = null;
String entityTitle = "未知实体";
if ("course".equals(targetType)) {
// 课程评论通知课程创建者
AiolCourse course = aiolCourseService.getById(targetId);
if (course != null) {
toUserId = course.getCreateBy(); // 课程创建者
entityTitle = course.getName(); // 课程名称
}
} else if ("discussion".equals(targetType)) {
// 讨论评论通知讨论创建者
AiolDiscussion discussion = aiolDiscussionService.getById(targetId);
if (discussion != null) {
toUserId = discussion.getCreateBy(); // 讨论创建者
entityTitle = discussion.getTitle(); // 讨论标题
}
} else if ("comment".equals(targetType)) {
// 回复评论通知被回复的评论作者
AiolComment parentComment = aiolCommentService.getById(targetId);
if (parentComment != null) {
toUserId = parentComment.getUserId(); // 被回复评论的作者
entityTitle = "评论回复"; // 回复类型
}
}
// 不给自己发通知
if (toUserId != null && !toUserId.equals(sysUser.getId()) && !toUserId.equals(sysUser.getUsername())) {
// 生成当前时间格式yyyy-MM-dd HH:mm:ss
String actionTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
// 调用消息通知工具类发送评论通知
messageNotificationUtil.sendCommentNotification(
toUserId, // 接收用户ID
sysUser.getId(), // 发送用户ID
sysUser.getId(), // 发送者ID
sysUser.getUsername(), // 发送者用户名
Long.valueOf(aiolComment.getId()), // 评论ID
aiolComment.getContent(), // 评论内容
targetType, // 实体类型
targetId, // 实体ID
entityTitle, // 实体标题
actionTime // 操作时间
);
log.info("评论通知发送成功: 评论者={}, 接收者={}, 目标类型={}, 实体标题={}",
sysUser.getUsername(), toUserId, targetType, entityTitle);
} else {
log.debug("跳过通知发送: 自己评论自己的内容或未找到接收者, 评论者={}, 接收者={}",
sysUser.getId(), toUserId);
}
} catch (Exception e) {
// 通知发送失败不影响评论的正常添加只记录错误日志
log.error("发送评论通知失败: commentId={}, targetType={}, targetId={}, error={}",
aiolComment.getId(), aiolComment.getTargetType(), aiolComment.getTargetId(), e.getMessage(), e);
}
}
/**
* 发送点赞通知
*
* @param comment 被点赞的评论对象
* @param sysUser 点赞用户
*/
private void sendLikeNotification(AiolComment comment, LoginUser sysUser) {
try {
// 获取评论作者ID
String toUserId = comment.getUserId();
String entityTitle = "评论";
// 不给自己发通知
if (toUserId != null && !toUserId.equals(sysUser.getId()) && !toUserId.equals(sysUser.getUsername())) {
// 生成当前时间格式yyyy-MM-dd HH:mm:ss
String actionTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
// 调用消息通知工具类发送点赞通知
messageNotificationUtil.sendLikeNotification(
toUserId, // 接收用户ID评论作者
sysUser.getId(), // 发送用户ID点赞者
sysUser.getId(), // 发送者ID
sysUser.getUsername(), // 发送者用户名
"comment", // 实体类型
comment.getId(), // 实体ID评论ID
entityTitle, // 实体标题
"like", // 操作类型
actionTime // 操作时间
);
log.info("点赞通知发送成功: 点赞者={}, 接收者={}, 评论ID={}",
sysUser.getUsername(), toUserId, comment.getId());
} else {
log.debug("跳过点赞通知发送: 自己点赞自己的评论, 点赞者={}, 接收者={}",
sysUser.getId(), toUserId);
}
} catch (Exception e) {
// 通知发送失败不影响点赞的正常操作只记录错误日志
log.error("发送点赞通知失败: commentId={}, error={}",
comment.getId(), e.getMessage(), e);
}
}
}

View File

@ -0,0 +1,182 @@
package org.jeecg.modules.aiol.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.aiol.entity.AiolContentConfig;
import org.jeecg.modules.aiol.service.IAiolContentConfigService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 内容配置
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Tag(name="内容配置")
@RestController
@RequestMapping("/aiol/aiolContentConfig")
@Slf4j
public class AiolContentConfigController extends JeecgController<AiolContentConfig, IAiolContentConfigService> {
@Autowired
private IAiolContentConfigService aiolContentConfigService;
/**
* 分页列表查询
*
* @param aiolContentConfig
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "内容配置-分页列表查询")
@Operation(summary="内容配置-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolContentConfig>> queryPageList(AiolContentConfig aiolContentConfig,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolContentConfig> queryWrapper = QueryGenerator.initQueryWrapper(aiolContentConfig, req.getParameterMap());
Page<AiolContentConfig> page = new Page<AiolContentConfig>(pageNo, pageSize);
IPage<AiolContentConfig> pageList = aiolContentConfigService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolContentConfig
* @return
*/
@AutoLog(value = "内容配置-添加")
@Operation(summary="内容配置-添加")
@RequiresPermissions("aiol:aiol_content_config:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolContentConfig aiolContentConfig) {
aiolContentConfigService.save(aiolContentConfig);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param aiolContentConfig
* @return
*/
@AutoLog(value = "内容配置-编辑")
@Operation(summary="内容配置-编辑")
@RequiresPermissions("aiol:aiol_content_config:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AiolContentConfig aiolContentConfig) {
aiolContentConfigService.updateById(aiolContentConfig);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "内容配置-通过id删除")
@Operation(summary="内容配置-通过id删除")
@RequiresPermissions("aiol:aiol_content_config:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
aiolContentConfigService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "内容配置-批量删除")
@Operation(summary="内容配置-批量删除")
@RequiresPermissions("aiol:aiol_content_config:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.aiolContentConfigService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "内容配置-通过id查询")
@Operation(summary="内容配置-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolContentConfig> queryById(@RequestParam(name="id",required=true) String id) {
AiolContentConfig aiolContentConfig = aiolContentConfigService.getById(id);
if(aiolContentConfig==null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolContentConfig);
}
/**
* 导出excel
*
* @param request
* @param aiolContentConfig
*/
@RequiresPermissions("aiol:aiol_content_config:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolContentConfig aiolContentConfig) {
return super.exportXls(request, aiolContentConfig, AiolContentConfig.class, "内容配置");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_content_config:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolContentConfig.class);
}
}

View File

@ -0,0 +1,182 @@
package org.jeecg.modules.aiol.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.aiol.entity.AiolCourseCategory;
import org.jeecg.modules.aiol.service.IAiolCourseCategoryService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 课程分类
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Tag(name="课程分类")
@RestController
@RequestMapping("/aiol/aiolCourseCategory")
@Slf4j
public class AiolCourseCategoryController extends JeecgController<AiolCourseCategory, IAiolCourseCategoryService> {
@Autowired
private IAiolCourseCategoryService aiolCourseCategoryService;
/**
* 分页列表查询
*
* @param aiolCourseCategory
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "课程分类-分页列表查询")
@Operation(summary="课程分类-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolCourseCategory>> queryPageList(AiolCourseCategory aiolCourseCategory,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolCourseCategory> queryWrapper = QueryGenerator.initQueryWrapper(aiolCourseCategory, req.getParameterMap());
Page<AiolCourseCategory> page = new Page<AiolCourseCategory>(pageNo, pageSize);
IPage<AiolCourseCategory> pageList = aiolCourseCategoryService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolCourseCategory
* @return
*/
@AutoLog(value = "课程分类-添加")
@Operation(summary="课程分类-添加")
@RequiresPermissions("aiol:aiol_course_category:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolCourseCategory aiolCourseCategory) {
aiolCourseCategoryService.save(aiolCourseCategory);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param aiolCourseCategory
* @return
*/
@AutoLog(value = "课程分类-编辑")
@Operation(summary="课程分类-编辑")
@RequiresPermissions("aiol:aiol_course_category:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AiolCourseCategory aiolCourseCategory) {
aiolCourseCategoryService.updateById(aiolCourseCategory);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "课程分类-通过id删除")
@Operation(summary="课程分类-通过id删除")
@RequiresPermissions("aiol:aiol_course_category:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
aiolCourseCategoryService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "课程分类-批量删除")
@Operation(summary="课程分类-批量删除")
@RequiresPermissions("aiol:aiol_course_category:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.aiolCourseCategoryService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "课程分类-通过id查询")
@Operation(summary="课程分类-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolCourseCategory> queryById(@RequestParam(name="id",required=true) String id) {
AiolCourseCategory aiolCourseCategory = aiolCourseCategoryService.getById(id);
if(aiolCourseCategory==null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolCourseCategory);
}
/**
* 导出excel
*
* @param request
* @param aiolCourseCategory
*/
@RequiresPermissions("aiol:aiol_course_category:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolCourseCategory aiolCourseCategory) {
return super.exportXls(request, aiolCourseCategory, AiolCourseCategory.class, "课程分类");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_course_category:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolCourseCategory.class);
}
}

View File

@ -0,0 +1,264 @@
package org.jeecg.modules.aiol.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.aiol.entity.AiolCourseSection;
import org.jeecg.modules.aiol.entity.AiolEntityLink;
import org.jeecg.modules.aiol.constant.EntityLinkConst;
import org.jeecg.modules.aiol.dto.AiolCourseSectionDTO;
import org.jeecg.modules.aiol.service.IAiolCourseSectionService;
import org.jeecg.modules.aiol.service.IAiolEntityLinkService;
import org.jeecg.modules.aiol.mapper.AiolEntityLinkMapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 课程章节
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Tag(name="课程章节")
@RestController
@RequestMapping("/aiol/aiolCourseSection")
@Slf4j
public class AiolCourseSectionController extends JeecgController<AiolCourseSection, IAiolCourseSectionService> {
@Autowired
private IAiolCourseSectionService aiolCourseSectionService;
@Autowired
private AiolEntityLinkMapper aiolEntityLinkMapper;
@Autowired
private IAiolEntityLinkService aiolEntityLinkService;
/**
* 分页列表查询
*
* @param aiolCourseSection
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "课程章节-分页列表查询")
@Operation(summary="课程章节-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolCourseSection>> queryPageList(AiolCourseSection aiolCourseSection,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolCourseSection> queryWrapper = QueryGenerator.initQueryWrapper(aiolCourseSection, req.getParameterMap());
Page<AiolCourseSection> page = new Page<AiolCourseSection>(pageNo, pageSize);
IPage<AiolCourseSection> pageList = aiolCourseSectionService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param sectionDTO 课程章节DTO包含章节信息和资源关联信息
* @return
*/
@AutoLog(value = "课程章节-添加")
@Operation(summary="课程章节-添加", description = "添加课程章节并关联资源支持resourceId和resourceType参数")
@RequiresPermissions("aiol:aiol_course_section:add")
@PostMapping(value = "/add")
@Transactional(rollbackFor = Exception.class)
public Result<String> add(@RequestBody AiolCourseSectionDTO sectionDTO) {
try {
// 1. 保存章节信息
aiolCourseSectionService.save(sectionDTO);
// 2. 处理资源关联
if (sectionDTO.getTargetId() != null && !sectionDTO.getTargetId().trim().isEmpty() &&
sectionDTO.getType() != null) {
aiolEntityLinkService.save(
EntityLinkConst.SourceType.COURSE_SECTION,
sectionDTO.getId(),
getTargetType(sectionDTO.getType()),
sectionDTO.getTargetId()
);
}
return Result.OK(sectionDTO.getId());
} catch (Exception e) {
log.error("添加课程章节失败: {}", e.getMessage(), e);
return Result.error("添加课程章节失败: " + e.getMessage());
}
}
private String getTargetType(Integer type) {
switch (type) {
case 0:
return EntityLinkConst.TargetType.RESOURCE;
case 1:
return EntityLinkConst.TargetType.RESOURCE;
case 2:
return EntityLinkConst.TargetType.EXAM;
case 3:
return EntityLinkConst.TargetType.HOMEWORK;
case 4:
return EntityLinkConst.TargetType.EXAM;
case 5:
return EntityLinkConst.TargetType.DISCUSSION;
default:
return null;
}
}
/**
* 编辑
*
* @param sectionDTO 课程章节DTO包含章节信息和资源关联信息
* @return
*/
@AutoLog(value = "课程章节-编辑")
@Operation(summary="课程章节-编辑", description = "编辑课程章节并更新资源关联支持resourceId和resourceType参数")
@RequiresPermissions("aiol:aiol_course_section:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
@Transactional(rollbackFor = Exception.class)
public Result<String> edit(@RequestBody AiolCourseSectionDTO sectionDTO) {
try {
// 1. 更新章节信息
aiolCourseSectionService.updateById(sectionDTO);
// 2. 处理资源关联更新
if (sectionDTO.getTargetId() != null && !sectionDTO.getTargetId().trim().isEmpty() &&
sectionDTO.getType() != null) {
// 先删除旧的关联关系
QueryWrapper<AiolEntityLink> deleteWrapper = new QueryWrapper<>();
deleteWrapper.eq("source_type", EntityLinkConst.SourceType.COURSE_SECTION)
.eq("source_id", sectionDTO.getId());
aiolEntityLinkMapper.delete(deleteWrapper);
// 创建新的关联关系
aiolEntityLinkService.save(
EntityLinkConst.SourceType.COURSE_SECTION,
sectionDTO.getId(),
getTargetType(sectionDTO.getType()),
sectionDTO.getTargetId()
);
} else {
// 如果没有提供资源信息删除所有关联关系
QueryWrapper<AiolEntityLink> deleteWrapper = new QueryWrapper<>();
deleteWrapper.eq("source_type", EntityLinkConst.SourceType.COURSE_SECTION)
.eq("source_id", sectionDTO.getId());
aiolEntityLinkMapper.delete(deleteWrapper);
}
return Result.OK("编辑成功!");
} catch (Exception e) {
log.error("编辑课程章节失败: {}", e.getMessage(), e);
return Result.error("编辑课程章节失败: " + e.getMessage());
}
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "课程章节-通过id删除")
@Operation(summary="课程章节-通过id删除")
@RequiresPermissions("aiol:aiol_course_section:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
aiolCourseSectionService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "课程章节-批量删除")
@Operation(summary="课程章节-批量删除")
@RequiresPermissions("aiol:aiol_course_section:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.aiolCourseSectionService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "课程章节-通过id查询")
@Operation(summary="课程章节-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolCourseSection> queryById(@RequestParam(name="id",required=true) String id) {
AiolCourseSection aiolCourseSection = aiolCourseSectionService.getById(id);
if(aiolCourseSection==null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolCourseSection);
}
/**
* 导出excel
*
* @param request
* @param aiolCourseSection
*/
@RequiresPermissions("aiol:aiol_course_section:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolCourseSection aiolCourseSection) {
return super.exportXls(request, aiolCourseSection, AiolCourseSection.class, "课程章节");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_course_section:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolCourseSection.class);
}
}

View File

@ -0,0 +1,182 @@
package org.jeecg.modules.aiol.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.aiol.entity.AiolCourseSignup;
import org.jeecg.modules.aiol.service.IAiolCourseSignupService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 课程报名
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Tag(name="课程报名")
@RestController
@RequestMapping("/aiol/aiolCourseSignup")
@Slf4j
public class AiolCourseSignupController extends JeecgController<AiolCourseSignup, IAiolCourseSignupService> {
@Autowired
private IAiolCourseSignupService aiolCourseSignupService;
/**
* 分页列表查询
*
* @param aiolCourseSignup
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "课程报名-分页列表查询")
@Operation(summary="课程报名-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolCourseSignup>> queryPageList(AiolCourseSignup aiolCourseSignup,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolCourseSignup> queryWrapper = QueryGenerator.initQueryWrapper(aiolCourseSignup, req.getParameterMap());
Page<AiolCourseSignup> page = new Page<AiolCourseSignup>(pageNo, pageSize);
IPage<AiolCourseSignup> pageList = aiolCourseSignupService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolCourseSignup
* @return
*/
@AutoLog(value = "课程报名-添加")
@Operation(summary="课程报名-添加")
@RequiresPermissions("aiol:aiol_course_signup:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolCourseSignup aiolCourseSignup) {
aiolCourseSignupService.save(aiolCourseSignup);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param aiolCourseSignup
* @return
*/
@AutoLog(value = "课程报名-编辑")
@Operation(summary="课程报名-编辑")
@RequiresPermissions("aiol:aiol_course_signup:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AiolCourseSignup aiolCourseSignup) {
aiolCourseSignupService.updateById(aiolCourseSignup);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "课程报名-通过id删除")
@Operation(summary="课程报名-通过id删除")
@RequiresPermissions("aiol:aiol_course_signup:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
aiolCourseSignupService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "课程报名-批量删除")
@Operation(summary="课程报名-批量删除")
@RequiresPermissions("aiol:aiol_course_signup:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.aiolCourseSignupService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "课程报名-通过id查询")
@Operation(summary="课程报名-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolCourseSignup> queryById(@RequestParam(name="id",required=true) String id) {
AiolCourseSignup aiolCourseSignup = aiolCourseSignupService.getById(id);
if(aiolCourseSignup==null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolCourseSignup);
}
/**
* 导出excel
*
* @param request
* @param aiolCourseSignup
*/
@RequiresPermissions("aiol:aiol_course_signup:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolCourseSignup aiolCourseSignup) {
return super.exportXls(request, aiolCourseSignup, AiolCourseSignup.class, "课程报名");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_course_signup:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolCourseSignup.class);
}
}

View File

@ -0,0 +1,182 @@
package org.jeecg.modules.aiol.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.aiol.entity.AiolCourseTeacher;
import org.jeecg.modules.aiol.service.IAiolCourseTeacherService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 授课教师
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Tag(name="授课教师")
@RestController
@RequestMapping("/aiol/aiolCourseTeacher")
@Slf4j
public class AiolCourseTeacherController extends JeecgController<AiolCourseTeacher, IAiolCourseTeacherService> {
@Autowired
private IAiolCourseTeacherService aiolCourseTeacherService;
/**
* 分页列表查询
*
* @param aiolCourseTeacher
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "授课教师-分页列表查询")
@Operation(summary="授课教师-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolCourseTeacher>> queryPageList(AiolCourseTeacher aiolCourseTeacher,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolCourseTeacher> queryWrapper = QueryGenerator.initQueryWrapper(aiolCourseTeacher, req.getParameterMap());
Page<AiolCourseTeacher> page = new Page<AiolCourseTeacher>(pageNo, pageSize);
IPage<AiolCourseTeacher> pageList = aiolCourseTeacherService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolCourseTeacher
* @return
*/
@AutoLog(value = "授课教师-添加")
@Operation(summary="授课教师-添加")
@RequiresPermissions("aiol:aiol_course_teacher:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolCourseTeacher aiolCourseTeacher) {
aiolCourseTeacherService.save(aiolCourseTeacher);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param aiolCourseTeacher
* @return
*/
@AutoLog(value = "授课教师-编辑")
@Operation(summary="授课教师-编辑")
@RequiresPermissions("aiol:aiol_course_teacher:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AiolCourseTeacher aiolCourseTeacher) {
aiolCourseTeacherService.updateById(aiolCourseTeacher);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "授课教师-通过id删除")
@Operation(summary="授课教师-通过id删除")
@RequiresPermissions("aiol:aiol_course_teacher:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
aiolCourseTeacherService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "授课教师-批量删除")
@Operation(summary="授课教师-批量删除")
@RequiresPermissions("aiol:aiol_course_teacher:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.aiolCourseTeacherService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "授课教师-通过id查询")
@Operation(summary="授课教师-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolCourseTeacher> queryById(@RequestParam(name="id",required=true) String id) {
AiolCourseTeacher aiolCourseTeacher = aiolCourseTeacherService.getById(id);
if(aiolCourseTeacher==null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolCourseTeacher);
}
/**
* 导出excel
*
* @param request
* @param aiolCourseTeacher
*/
@RequiresPermissions("aiol:aiol_course_teacher:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolCourseTeacher aiolCourseTeacher) {
return super.exportXls(request, aiolCourseTeacher, AiolCourseTeacher.class, "授课教师");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_course_teacher:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolCourseTeacher.class);
}
}

View File

@ -0,0 +1,292 @@
package org.jeecg.modules.aiol.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.shiro.IgnoreAuth;
import org.jeecg.modules.aiol.entity.AiolDiscussion;
import org.jeecg.modules.aiol.dto.DiscussionWithSectionDTO;
import org.jeecg.modules.aiol.dto.AiolDiscussionSaveDTO;
import org.jeecg.modules.aiol.service.IAiolDiscussionService;
import org.jeecg.modules.aiol.service.IAiolEntityLinkService;
import org.jeecg.modules.aiol.service.IAiolCourseSectionService;
import org.jeecg.modules.aiol.entity.AiolCourseSection;
import org.jeecg.modules.aiol.constant.EntityLinkConst;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.constant.CommonConstant;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 讨论
* @Author: jeecg-boot
* @Date: 2025-09-19
* @Version: V1.0
*/
@Tag(name="讨论")
@RestController
@RequestMapping("/aiol/aiolDiscussion")
@Slf4j
public class AiolDiscussionController extends JeecgController<AiolDiscussion, IAiolDiscussionService> {
@Autowired
private IAiolDiscussionService aiolDiscussionService;
@Autowired
private IAiolEntityLinkService aiolEntityLinkService;
@Autowired
private IAiolCourseSectionService aiolCourseSectionService;
@Autowired
private ISysBaseAPI sysBaseApi;
/**
* 分页列表查询
*
* @param aiolDiscussion
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "讨论-分页列表查询")
@Operation(summary="讨论-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolDiscussion>> queryPageList(AiolDiscussion aiolDiscussion,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolDiscussion> queryWrapper = QueryGenerator.initQueryWrapper(aiolDiscussion, req.getParameterMap());
Page<AiolDiscussion> page = new Page<AiolDiscussion>(pageNo, pageSize);
IPage<AiolDiscussion> pageList = aiolDiscussionService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolDiscussionSaveDTO 讨论保存DTO包含章节ID
* @return
*/
@AutoLog(value = "讨论-添加")
@Operation(summary="讨论-添加", description="添加讨论,可选择关联到指定章节")
@RequiresPermissions("aiol:aiol_discussion:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolDiscussionSaveDTO aiolDiscussionSaveDTO) {
try {
// 创建讨论实体
AiolDiscussion aiolDiscussion = new AiolDiscussion();
aiolDiscussion.setTitle(aiolDiscussionSaveDTO.getTitle());
aiolDiscussion.setDescription(aiolDiscussionSaveDTO.getDescription());
aiolDiscussion.setCourseId(aiolDiscussionSaveDTO.getCourseId());
// 保存讨论记录
aiolDiscussionService.save(aiolDiscussion);
// 如果传入了sectionId创建与章节的关联关系
String sectionId = aiolDiscussionSaveDTO.getSectionId();
if (sectionId != null && !sectionId.trim().isEmpty()) {
aiolEntityLinkService.save(
EntityLinkConst.SourceType.COURSE_SECTION,
sectionId.trim(),
EntityLinkConst.TargetType.DISCUSSION,
aiolDiscussion.getId()
);
log.info("讨论 {} 成功关联到章节 {}", aiolDiscussion.getId(), sectionId);
}
return Result.OK(aiolDiscussion.getId());
} catch (Exception e) {
log.error("添加讨论失败: {}", e.getMessage(), e);
return Result.error("添加讨论失败: " + e.getMessage());
}
}
/**
* 编辑
*
* @param aiolDiscussion
* @return
*/
@AutoLog(value = "讨论-编辑")
@Operation(summary="讨论-编辑")
@RequiresPermissions("aiol:aiol_discussion:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AiolDiscussion aiolDiscussion) {
aiolDiscussionService.updateById(aiolDiscussion);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "讨论-通过id删除")
@Operation(summary="讨论-通过id删除")
@RequiresPermissions("aiol:aiol_discussion:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
aiolDiscussionService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "讨论-批量删除")
@Operation(summary="讨论-批量删除")
@RequiresPermissions("aiol:aiol_discussion:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.aiolDiscussionService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "讨论-通过id查询")
@Operation(summary="讨论-通过id查询")
@GetMapping(value = "/queryById")
@IgnoreAuth
public Result<AiolDiscussion> queryById(@RequestParam(name="id",required=true) String id) {
AiolDiscussion aiolDiscussion = aiolDiscussionService.getById(id);
if(aiolDiscussion==null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolDiscussion);
}
/**
* 导出excel
*
* @param request
* @param aiolDiscussion
*/
@RequiresPermissions("aiol:aiol_discussion:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolDiscussion aiolDiscussion) {
return super.exportXls(request, aiolDiscussion, AiolDiscussion.class, "讨论");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_discussion:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolDiscussion.class);
}
@AutoLog(value = "讨论-查询课程下的讨论列表")
@Operation(summary = "查询课程讨论列表", description = "查询课程讨论列表包含关联的章节ID")
@GetMapping(value = "/teacher_list")
public Result<List<DiscussionWithSectionDTO>> queryCourseDiscussions(HttpServletRequest request, @RequestParam(value = "courseId") String courseId) {
try {
// 1. 查询课程下的讨论列表
QueryWrapper<AiolDiscussion> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("course_id", courseId)
.orderByDesc("create_time");
List<AiolDiscussion> discussionList = aiolDiscussionService.list(queryWrapper);
// 2. 转换为包含章节信息的DTO列表
List<DiscussionWithSectionDTO> resultList = discussionList.stream()
.map(this::convertToDiscussionWithSection)
.collect(Collectors.toList());
return Result.OK(resultList);
} catch (Exception e) {
log.error("查询课程讨论列表失败: error={}", e.getMessage(), e);
return Result.error("查询课程讨论列表失败: " + e.getMessage());
}
}
/**
* 将AiolDiscussion转换为包含章节信息的DTO
* @param discussion 讨论实体
* @return 包含章节ID的DTO
*/
private DiscussionWithSectionDTO convertToDiscussionWithSection(AiolDiscussion discussion) {
DiscussionWithSectionDTO dto = new DiscussionWithSectionDTO();
// 复制基本属性
dto.setId(discussion.getId());
dto.setTitle(discussion.getTitle());
dto.setDescription(discussion.getDescription());
dto.setCourseId(discussion.getCourseId());
dto.setCreateBy(discussion.getCreateBy());
dto.setCreateTime(discussion.getCreateTime());
dto.setUpdateBy(discussion.getUpdateBy());
dto.setUpdateTime(discussion.getUpdateTime());
try {
// 查询关联的章节ID
String sectionId = aiolEntityLinkService.listSourceId(
EntityLinkConst.TargetType.DISCUSSION,
discussion.getId(),
EntityLinkConst.SourceType.COURSE_SECTION
);
if (sectionId != null) {
dto.setSectionId(sectionId);
// 查询章节名称
AiolCourseSection section = aiolCourseSectionService.getById(sectionId);
if (section != null && section.getName() != null) {
dto.setSectionName(section.getName());
}
}
} catch (Exception e) {
log.error("查询讨论关联章节失败: discussionId={}, error={}", discussion.getId(), e.getMessage(), e);
// 即使查询失败也返回基本的讨论信息
}
return dto;
}
}

View File

@ -0,0 +1,182 @@
package org.jeecg.modules.aiol.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.aiol.entity.AiolEntityLink;
import org.jeecg.modules.aiol.service.IAiolEntityLinkService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 主体绑定
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Tag(name="主体绑定")
@RestController
@RequestMapping("/aiol/aiolEntityLink")
@Slf4j
public class AiolEntityLinkController extends JeecgController<AiolEntityLink, IAiolEntityLinkService> {
@Autowired
private IAiolEntityLinkService aiolEntityLinkService;
/**
* 分页列表查询
*
* @param aiolEntityLink
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "主体绑定-分页列表查询")
@Operation(summary="主体绑定-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolEntityLink>> queryPageList(AiolEntityLink aiolEntityLink,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolEntityLink> queryWrapper = QueryGenerator.initQueryWrapper(aiolEntityLink, req.getParameterMap());
Page<AiolEntityLink> page = new Page<AiolEntityLink>(pageNo, pageSize);
IPage<AiolEntityLink> pageList = aiolEntityLinkService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolEntityLink
* @return
*/
@AutoLog(value = "主体绑定-添加")
@Operation(summary="主体绑定-添加")
@RequiresPermissions("aiol:aiol_entity_link:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolEntityLink aiolEntityLink) {
aiolEntityLinkService.save(aiolEntityLink);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param aiolEntityLink
* @return
*/
@AutoLog(value = "主体绑定-编辑")
@Operation(summary="主体绑定-编辑")
@RequiresPermissions("aiol:aiol_entity_link:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AiolEntityLink aiolEntityLink) {
aiolEntityLinkService.updateById(aiolEntityLink);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "主体绑定-通过id删除")
@Operation(summary="主体绑定-通过id删除")
@RequiresPermissions("aiol:aiol_entity_link:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
aiolEntityLinkService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "主体绑定-批量删除")
@Operation(summary="主体绑定-批量删除")
@RequiresPermissions("aiol:aiol_entity_link:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.aiolEntityLinkService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "主体绑定-通过id查询")
@Operation(summary="主体绑定-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolEntityLink> queryById(@RequestParam(name="id",required=true) String id) {
AiolEntityLink aiolEntityLink = aiolEntityLinkService.getById(id);
if(aiolEntityLink==null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolEntityLink);
}
/**
* 导出excel
*
* @param request
* @param aiolEntityLink
*/
@RequiresPermissions("aiol:aiol_entity_link:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolEntityLink aiolEntityLink) {
return super.exportXls(request, aiolEntityLink, AiolEntityLink.class, "主体绑定");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_entity_link:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolEntityLink.class);
}
}

View File

@ -0,0 +1,182 @@
package org.jeecg.modules.aiol.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.aiol.entity.AiolEntityPermission;
import org.jeecg.modules.aiol.service.IAiolEntityPermissionService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 实体权限
* @Author: jeecg-boot
* @Date: 2025-09-05
* @Version: V1.0
*/
@Tag(name="实体权限")
@RestController
@RequestMapping("/aiol/aiolEntityPermission")
@Slf4j
public class AiolEntityPermissionController extends JeecgController<AiolEntityPermission, IAiolEntityPermissionService> {
@Autowired
private IAiolEntityPermissionService aiolEntityPermissionService;
/**
* 分页列表查询
*
* @param aiolEntityPermission
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "实体权限-分页列表查询")
@Operation(summary="实体权限-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolEntityPermission>> queryPageList(AiolEntityPermission aiolEntityPermission,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolEntityPermission> queryWrapper = QueryGenerator.initQueryWrapper(aiolEntityPermission, req.getParameterMap());
Page<AiolEntityPermission> page = new Page<AiolEntityPermission>(pageNo, pageSize);
IPage<AiolEntityPermission> pageList = aiolEntityPermissionService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolEntityPermission
* @return
*/
@AutoLog(value = "实体权限-添加")
@Operation(summary="实体权限-添加")
@RequiresPermissions("aiol:aiol_entity_permission:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolEntityPermission aiolEntityPermission) {
aiolEntityPermissionService.save(aiolEntityPermission);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param aiolEntityPermission
* @return
*/
@AutoLog(value = "实体权限-编辑")
@Operation(summary="实体权限-编辑")
@RequiresPermissions("aiol:aiol_entity_permission:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AiolEntityPermission aiolEntityPermission) {
aiolEntityPermissionService.updateById(aiolEntityPermission);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "实体权限-通过id删除")
@Operation(summary="实体权限-通过id删除")
@RequiresPermissions("aiol:aiol_entity_permission:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
aiolEntityPermissionService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "实体权限-批量删除")
@Operation(summary="实体权限-批量删除")
@RequiresPermissions("aiol:aiol_entity_permission:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.aiolEntityPermissionService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "实体权限-通过id查询")
@Operation(summary="实体权限-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolEntityPermission> queryById(@RequestParam(name="id",required=true) String id) {
AiolEntityPermission aiolEntityPermission = aiolEntityPermissionService.getById(id);
if(aiolEntityPermission==null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolEntityPermission);
}
/**
* 导出excel
*
* @param request
* @param aiolEntityPermission
*/
@RequiresPermissions("aiol:aiol_entity_permission:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolEntityPermission aiolEntityPermission) {
return super.exportXls(request, aiolEntityPermission, AiolEntityPermission.class, "实体权限");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_entity_permission:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolEntityPermission.class);
}
}

View File

@ -0,0 +1,182 @@
package org.jeecg.modules.aiol.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.aiol.entity.AiolExamAnswer;
import org.jeecg.modules.aiol.service.IAiolExamAnswerService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 考试答题
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Tag(name="考试答题")
@RestController
@RequestMapping("/aiol/aiolExamAnswer")
@Slf4j
public class AiolExamAnswerController extends JeecgController<AiolExamAnswer, IAiolExamAnswerService> {
@Autowired
private IAiolExamAnswerService aiolExamAnswerService;
/**
* 分页列表查询
*
* @param aiolExamAnswer
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "考试答题-分页列表查询")
@Operation(summary="考试答题-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolExamAnswer>> queryPageList(AiolExamAnswer aiolExamAnswer,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolExamAnswer> queryWrapper = QueryGenerator.initQueryWrapper(aiolExamAnswer, req.getParameterMap());
Page<AiolExamAnswer> page = new Page<AiolExamAnswer>(pageNo, pageSize);
IPage<AiolExamAnswer> pageList = aiolExamAnswerService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolExamAnswer
* @return
*/
@AutoLog(value = "考试答题-添加")
@Operation(summary="考试答题-添加")
@RequiresPermissions("aiol:aiol_exam_answer:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolExamAnswer aiolExamAnswer) {
aiolExamAnswerService.save(aiolExamAnswer);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param aiolExamAnswer
* @return
*/
@AutoLog(value = "考试答题-编辑")
@Operation(summary="考试答题-编辑")
@RequiresPermissions("aiol:aiol_exam_answer:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AiolExamAnswer aiolExamAnswer) {
aiolExamAnswerService.updateById(aiolExamAnswer);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "考试答题-通过id删除")
@Operation(summary="考试答题-通过id删除")
@RequiresPermissions("aiol:aiol_exam_answer:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
aiolExamAnswerService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "考试答题-批量删除")
@Operation(summary="考试答题-批量删除")
@RequiresPermissions("aiol:aiol_exam_answer:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.aiolExamAnswerService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "考试答题-通过id查询")
@Operation(summary="考试答题-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolExamAnswer> queryById(@RequestParam(name="id",required=true) String id) {
AiolExamAnswer aiolExamAnswer = aiolExamAnswerService.getById(id);
if(aiolExamAnswer==null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolExamAnswer);
}
/**
* 导出excel
*
* @param request
* @param aiolExamAnswer
*/
@RequiresPermissions("aiol:aiol_exam_answer:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolExamAnswer aiolExamAnswer) {
return super.exportXls(request, aiolExamAnswer, AiolExamAnswer.class, "考试答题");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_exam_answer:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolExamAnswer.class);
}
}

View File

@ -0,0 +1,199 @@
package org.jeecg.modules.aiol.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.aiol.entity.AiolExamAnswer;
import org.jeecg.modules.aiol.entity.AiolExamRecord;
import org.jeecg.modules.aiol.service.IAiolExamAnswerService;
import org.jeecg.modules.aiol.service.IAiolExamRecordService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 考试记录
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Tag(name="考试记录")
@RestController
@RequestMapping("/aiol/aiolExamRecord")
@Slf4j
public class AiolExamRecordController extends JeecgController<AiolExamRecord, IAiolExamRecordService> {
@Autowired
private IAiolExamRecordService aiolExamRecordService;
@Autowired
private IAiolExamAnswerService aiolExamAnswerService;
/**
* 分页列表查询
*
* @param aiolExamRecord
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "考试记录-分页列表查询")
@Operation(summary="考试记录-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolExamRecord>> queryPageList(AiolExamRecord aiolExamRecord,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolExamRecord> queryWrapper = QueryGenerator.initQueryWrapper(aiolExamRecord, req.getParameterMap());
Page<AiolExamRecord> page = new Page<AiolExamRecord>(pageNo, pageSize);
IPage<AiolExamRecord> pageList = aiolExamRecordService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolExamRecord
* @return
*/
@AutoLog(value = "考试记录-添加")
@Operation(summary="考试记录-添加")
@RequiresPermissions("aiol:aiol_exam_record:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolExamRecord aiolExamRecord) {
aiolExamRecordService.save(aiolExamRecord);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param aiolExamRecord
* @return
*/
@AutoLog(value = "考试记录-编辑")
@Operation(summary="考试记录-编辑")
@RequiresPermissions("aiol:aiol_exam_record:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.POST})
public Result<String> edit(@RequestBody AiolExamRecord aiolExamRecord) {
aiolExamRecordService.updateById(aiolExamRecord);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "考试记录-通过id删除")
@Operation(summary="考试记录-通过id删除")
@RequiresPermissions("aiol:aiol_exam_record:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
aiolExamRecordService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "考试记录-批量删除")
@Operation(summary="考试记录-批量删除")
@RequiresPermissions("aiol:aiol_exam_record:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.aiolExamRecordService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
@AutoLog(value = "考试记录-重新练习")
@Operation(summary="考试记录-重新练习")
@RequiresPermissions("aiol:aiol_exam_record:edit")
@Transactional
@RequestMapping(value = "/rePractice", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> rePractice(@RequestParam String userId,@RequestParam String examId) {
aiolExamRecordService.remove(new QueryWrapper<AiolExamRecord>().eq("user_id", userId).eq("exam_id", examId));
aiolExamAnswerService.remove(new QueryWrapper<AiolExamAnswer>().eq("user_id", userId).eq("exam_id", examId));
return Result.OK("请返回重新练习一次吧!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "考试记录-通过id查询")
@Operation(summary="考试记录-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolExamRecord> queryById(@RequestParam(name="id",required=true) String id) {
AiolExamRecord aiolExamRecord = aiolExamRecordService.getById(id);
if(aiolExamRecord==null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolExamRecord);
}
/**
* 导出excel
*
* @param request
* @param aiolExamRecord
*/
@RequiresPermissions("aiol:aiol_exam_record:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolExamRecord aiolExamRecord) {
return super.exportXls(request, aiolExamRecord, AiolExamRecord.class, "考试记录");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_exam_record:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolExamRecord.class);
}
}

View File

@ -0,0 +1,718 @@
package org.jeecg.modules.aiol.controller;
import java.util.*;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.aiol.dto.AiolHomeworkSaveDTO;
import org.jeecg.modules.aiol.dto.StudentSubmitHomework;
import org.jeecg.modules.aiol.dto.HomeworkWithDetailsDTO;
import org.jeecg.modules.aiol.entity.*;
import org.jeecg.modules.aiol.service.*;
import org.jeecg.modules.aiol.constant.EntityLinkConst;
import org.jeecg.modules.aiol.mapper.AiolClassStudentMapper;
import org.jeecg.modules.aiol.mapper.AiolCourseSignupMapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.service.ISysUserService;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 作业
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Tag(name = "作业")
@RestController
@RequestMapping("/aiol/aiolHomework")
@Slf4j
public class AiolHomeworkController extends JeecgController<AiolHomework, IAiolHomeworkService> {
@Autowired
private IAiolHomeworkService aiolHomeworkService;
@Autowired
private IAiolCourseSignupService aiolCourseSignupService;
@Autowired
private ISysUserService sysUserService;
@Autowired
private ISysBaseAPI sysBaseApi;
@Autowired
private IAiolEntityLinkService aiolEntityLinkService;
@Autowired
private IAiolHomeworkSubmitService aiolHomeworkSubmitService;
/**
* 分页列表查询
*
* @param aiolHomework
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "作业-分页列表查询")
@Operation(summary = "作业-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolHomework>> queryPageList(AiolHomework aiolHomework,
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolHomework> queryWrapper = QueryGenerator.initQueryWrapper(aiolHomework, req.getParameterMap());
Page<AiolHomework> page = new Page<AiolHomework>(pageNo, pageSize);
IPage<AiolHomework> pageList = aiolHomeworkService.page(page, queryWrapper);
return Result.OK(pageList);
}
@Operation(summary = "我的作业-作业查询")
@GetMapping(value = "/myHomeworkList")
public Result<?> queryMyHomeworkList(HttpServletRequest request, @RequestParam(name = "type", defaultValue = "4", required = false) Integer type) {
// 尝试获取token判断用户id
String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
List<MyHomeworkDTO> homeworkDTOList = new ArrayList<>();
if (token != null && !token.trim().isEmpty()) {
try {
String username = JwtUtil.getUsername(token);
LoginUser sysUser = sysBaseApi.getUserByName(username);
// 获取当前用户ID
String currentUserId = sysUser.getId();
if (sysUser != null) {
//获取课程id列表
List<AiolCourseSignup> courseSignupList = aiolCourseSignupMapper.selectList(
new QueryWrapper<AiolCourseSignup>()
.eq("user_id", sysUser.getId())
.select("course_id")
);
log.info("查询到课程数量: {}", courseSignupList.size());
// 获取课程章节id列表
List<AiolCourseSection> courseId = aiolCourseSectionService.list(
new QueryWrapper<AiolCourseSection>()
.in("course_id", courseSignupList.stream().map(AiolCourseSignup::getCourseId).collect(Collectors.toList()))
.select("id"));
List<String> courseIdList = courseId.stream()
.map(AiolCourseSection::getId)
.collect(Collectors.toList());
log.info("查询到课程章节数量: {}", courseId.size());
//获取实体列表里的作业
List<String> list = new ArrayList<>();
if (!courseIdList.isEmpty()) {
list = aiolEntityLinkService.list(new QueryWrapper<AiolEntityLink>()
.eq("source_type", "course_section")
.in("source_id", courseIdList)
.eq("target_type", "homework")
.select("target_id")).stream().map(AiolEntityLink::getTargetId).collect(Collectors.toList());
} else return Result.ok();
log.info("查询到作业关联数量: {}", list.size());
// 查询作业
QueryWrapper<AiolHomework> queryWrapper = new QueryWrapper<>();
if (!list.isEmpty()) {
queryWrapper.in("id", list);
} else return Result.ok();
// 查询作业列表并去重
List<AiolHomework> homeworkList = aiolHomeworkService.list(queryWrapper)
.stream()
.distinct()
.collect(Collectors.toList());
// 获取所有创建人ID
List<String> creatorIds = homeworkList.stream()
.map(AiolHomework::getCreateBy)
.distinct()
.collect(Collectors.toList());
if (creatorIds.isEmpty()) return Result.ok();
// 查询所有作业的提交记录
List<AiolHomeworkSubmit> submitRecords = aiolHomeworkSubmitService.list(
new QueryWrapper<AiolHomeworkSubmit>()
.in("homework_id", homeworkList.stream().map(AiolHomework::getId).collect(Collectors.toList()))
);
// 按作业ID和学生ID分组存储提交记录
Map<String, Map<String, List<AiolHomeworkSubmit>>> userSubmitMap = submitRecords.stream()
.collect(Collectors.groupingBy(
AiolHomeworkSubmit::getHomeworkId,
Collectors.groupingBy(
AiolHomeworkSubmit::getStudentId
)
));
// 按作业ID分组统计提交人数
Map<String, Long> submitCountMap = submitRecords.stream()
.collect(Collectors.groupingBy(
AiolHomeworkSubmit::getHomeworkId,
Collectors.counting()
));
// 查询创建人信息
List<SysUser> userList = sysUserService.list(
new QueryWrapper<SysUser>()
.in("username", creatorIds)
);
// 创建ID到用户名的映射
Map<String, SysUser> userMap = userList.stream()
.collect(Collectors.toMap(
SysUser::getUsername,
SysUser -> SysUser
));
// 替换作业中的创建人ID为用户名
for (AiolHomework homework : homeworkList) {
MyHomeworkDTO myHomeworkDTO = new MyHomeworkDTO();
myHomeworkDTO.setAiolHomework(homework);
// 获取当前用户的提交记录如果没有则返回空列表
List<AiolHomeworkSubmit> userSubmits = userSubmitMap.getOrDefault(homework.getId(), Collections.emptyMap())
.getOrDefault(currentUserId, Collections.emptyList());
if (type == 1 || type == 2) {
if (userSubmits.isEmpty() || !userSubmits.get(0).getStatus().equals(type)) {
continue;
}
} else if (type == 0) {
if (!userSubmits.isEmpty() && !userSubmits.get(0).getStatus().equals(type)) {
continue;
}
}
myHomeworkDTO.setAiolHomeworkSubmits(userSubmits);
myHomeworkDTO.setSubmitCount(submitCountMap.getOrDefault(homework.getId(), 0L));
String creatorId = homework.getCreateBy();
if (creatorId != null && userMap.containsKey(creatorId)) {
myHomeworkDTO.setRealName(userMap.get(creatorId).getRealname());
myHomeworkDTO.setAvatar(userMap.get(creatorId).getAvatar());
}
homeworkDTOList.add(myHomeworkDTO);
}
}
} catch (Exception e) {
return Result.error(e.getMessage());
}
}
return Result.ok(homeworkDTOList);
}
/**
* 添加
*
* @param aiolHomework
* @return
*/
@AutoLog(value = "作业-添加")
@Operation(summary = "作业-添加")
@RequiresPermissions("aiol:aiol_homework:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolHomework aiolHomework) {
aiolHomeworkService.save(aiolHomework);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param aiolHomework
* @return
*/
@AutoLog(value = "作业-编辑")
@Operation(summary = "作业-编辑")
@RequiresPermissions("aiol:aiol_homework:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
public Result<String> edit(@RequestBody AiolHomework aiolHomework) {
aiolHomeworkService.updateById(aiolHomework);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "作业-通过id删除")
@Operation(summary = "作业-通过id删除")
@RequiresPermissions("aiol:aiol_homework:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name = "id", required = true) String id) {
aiolHomeworkService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "作业-批量删除")
@Operation(summary = "作业-批量删除")
@RequiresPermissions("aiol:aiol_homework:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
this.aiolHomeworkService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "作业-通过id查询")
@Operation(summary = "作业-通过id查询", description = "根据ID查询作业详情包含班级名和章节信息")
@GetMapping(value = "/queryById")
public Result<HomeworkWithDetailsDTO> queryById(@RequestParam(name = "id", required = true) String id) {
AiolHomework aiolHomework = aiolHomeworkService.getById(id);
if (aiolHomework == null) {
return Result.error("未找到对应数据");
}
// 转换为包含详情的DTO
HomeworkWithDetailsDTO result = convertToHomeworkWithDetails(aiolHomework);
return Result.OK(result);
}
/**
* 导出excel
*
* @param request
* @param aiolHomework
*/
@RequiresPermissions("aiol:aiol_homework:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolHomework aiolHomework) {
return super.exportXls(request, aiolHomework, AiolHomework.class, "作业");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_homework:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolHomework.class);
}
@Autowired
private IAiolHomeworkSubmitService homeworkSubmitService;
@Autowired
private IAiolEntityLinkService entityLinkBizService;
@Autowired
private AiolClassStudentMapper aiolClassStudentMapper;
@Autowired
private AiolCourseSignupMapper aiolCourseSignupMapper;
@Autowired
private IAiolClassService aiolClassService;
@Autowired
private IAiolCourseSectionService aiolCourseSectionService;
@GetMapping("/course/{courseId}")
@Operation(summary = "查询课程作业")
public Result<List<AiolHomework>> list(@PathVariable String courseId) {
return Result.OK(aiolHomeworkService.listByCourseId(courseId));
}
@AutoLog(value = "作业-教师端查询作业列表")
@Operation(summary = "查询课程作业列表", description = "查询当前登录教师创建的作业列表,包含班级名和章节信息")
@GetMapping(value = "/teacher_list")
public Result<List<HomeworkWithDetailsDTO>> teacherList(@RequestParam(value = "courseId") String courseId, HttpServletRequest request) {
try {
QueryWrapper<AiolHomework> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("course_id", courseId)
.orderByDesc("create_time");
List<AiolHomework> homeworkList = aiolHomeworkService.list(queryWrapper);
// 转换为包含详情的DTO列表
List<HomeworkWithDetailsDTO> resultList = homeworkList.stream()
.map(this::convertToHomeworkWithDetails)
.collect(Collectors.toList());
return Result.OK(resultList);
} catch (Exception e) {
log.error("查询教师作业列表失败: error={}", e.getMessage(), e);
return Result.error("查询教师作业列表失败: " + e.getMessage());
}
}
@PostMapping("submit")
@Operation(summary = "提交作业")
public Result<String> submit(@RequestBody StudentSubmitHomework studentSubmitHomework) {
return Result.OK(homeworkSubmitService.submit(studentSubmitHomework) ? "提交成功" : "提交失败");
}
@GetMapping("submitted/{studentId}")
@Operation(summary = "查询我已提交的作业")
public Result<List<AiolHomeworkSubmit>> submitted(@PathVariable String studentId) {
return Result.OK(homeworkSubmitService.submitted(studentId));
}
@PostMapping("correct/{homeworkSubmitId}")
@Operation(summary = "教师批改作业")
public Result<Integer> correct(@PathVariable String homeworkSubmitId, @RequestParam Integer score,
@RequestParam String comment, @RequestParam String teacherId) {
return Result.OK(homeworkSubmitService.correct(homeworkSubmitId, score, comment, teacherId));
}
@AutoLog(value = "作业-教师删除")
@Operation(summary = "作业-教师删除", description = "教师删除作业,同时删除相关的作业提交记录")
@RequiresPermissions("aiol:aiol_homework:delete")
@DeleteMapping(value = "/teacher_delete")
public Result<String> teacherDelete(@RequestParam(name = "id", required = true) String homeworkId) {
try {
// 1. 删除作业提交记录
QueryWrapper<AiolHomeworkSubmit> submitWrapper = new QueryWrapper<>();
submitWrapper.eq("homework_id", homeworkId);
List<AiolHomeworkSubmit> submitList = homeworkSubmitService.list(submitWrapper);
if (!submitList.isEmpty()) {
List<String> submitIds = submitList.stream()
.map(AiolHomeworkSubmit::getId)
.collect(Collectors.toList());
homeworkSubmitService.removeByIds(submitIds);
log.info("删除作业提交记录成功: homeworkId={}, 删除数量={}", homeworkId, submitIds.size());
}
// 2. 删除作业记录
boolean deleted = aiolHomeworkService.removeById(homeworkId);
if (!deleted) {
return Result.error("删除作业失败,作业不存在");
}
log.info("教师删除作业成功: homeworkId={}", homeworkId);
return Result.OK("删除成功!");
} catch (Exception e) {
log.error("教师删除作业失败: homeworkId={}, error={}", homeworkId, e.getMessage(), e);
return Result.error("删除作业失败: " + e.getMessage());
}
}
@AutoLog(value = "作业-教师添加")
@Operation(summary = "作业-教师添加", description = "教师添加作业,支持指定班级、课程和章节,自动为学生创建作业提交记录")
@RequiresPermissions("aiol:aiol_homework:add")
@PostMapping(value = "/teacher_add")
public Result<String> teacherAdd(@RequestBody AiolHomeworkSaveDTO homeworkSaveDTO) {
try {
// 1. 保存作业记录
AiolHomework aiolHomework = new AiolHomework();
// 复制DTO中的作业字段到实体
BeanUtils.copyProperties(homeworkSaveDTO, aiolHomework);
aiolHomeworkService.save(aiolHomework);
String homeworkId = aiolHomework.getId();
String classId = homeworkSaveDTO.getClassId();
String courseId = homeworkSaveDTO.getCourseId();
String sectionId = homeworkSaveDTO.getSectionId();
// 2. 为学生创建作业提交记录
List<String> studentIds = new ArrayList<>();
if (classId != null && !classId.trim().isEmpty()) {
// 如果传递了班级ID查询指定班级的学生
String[] classIds = classId.split(",");
for (String singleClassId : classIds) {
if (singleClassId != null && !singleClassId.trim().isEmpty()) {
singleClassId = singleClassId.trim();
QueryWrapper<AiolClassStudent> classStudentWrapper = new QueryWrapper<>();
classStudentWrapper.eq("class_id", singleClassId);
List<AiolClassStudent> classStudents = aiolClassStudentMapper.selectList(classStudentWrapper);
for (AiolClassStudent classStudent : classStudents) {
String studentId = classStudent.getStudentId();
if (!studentIds.contains(studentId)) {
studentIds.add(studentId);
}
}
}
}
} else if (courseId != null && !courseId.trim().isEmpty()) {
// 如果没有传递班级ID查询课程下的所有学生
QueryWrapper<AiolCourseSignup> courseSignupWrapper = new QueryWrapper<>();
courseSignupWrapper.eq("course_id", courseId);
List<AiolCourseSignup> courseSignups = aiolCourseSignupMapper.selectList(courseSignupWrapper);
for (AiolCourseSignup courseSignup : courseSignups) {
String studentId = courseSignup.getUserId();
if (!studentIds.contains(studentId)) {
studentIds.add(studentId);
}
}
}
// 为每个学生创建空的作业提交记录
for (String studentId : studentIds) {
homeworkSubmitService.createEmptySubmit(homeworkId, studentId);
}
// 3. 如果传递了章节ID创建章节与作业的关联关系
if (sectionId != null && !sectionId.trim().isEmpty()) {
entityLinkBizService.save(
EntityLinkConst.SourceType.COURSE_SECTION,
sectionId.trim(),
EntityLinkConst.TargetType.HOMEWORK,
homeworkId
);
log.info("作业 {} 成功关联到章节 {}", homeworkId, sectionId);
}
log.info("教师添加作业成功: homeworkId={}, courseId={}, classId={}, sectionId={}, 学生数量={}",
homeworkId, courseId, classId, sectionId, studentIds.size());
return Result.OK("添加成功!");
} catch (Exception e) {
log.error("教师添加作业失败: homeworkSaveDTO={}, error={}", homeworkSaveDTO, e.getMessage(), e);
return Result.error("添加作业失败: " + e.getMessage());
}
}
@AutoLog(value = "作业-教师编辑")
@Operation(summary = "作业-教师编辑", description = "教师编辑作业,更新作业基础信息、章节关联,并为新范围内学生补齐提交记录")
@RequiresPermissions("aiol:aiol_homework:edit")
@PostMapping(value = "/teacher_edit")
public Result<String> teacherEdit(@RequestBody AiolHomeworkSaveDTO homeworkSaveDTO) {
try {
// 1. 校验ID并查询原记录
if (homeworkSaveDTO.getId() == null || homeworkSaveDTO.getId().trim().isEmpty()) {
return Result.error("作业ID不能为空");
}
AiolHomework origin = aiolHomeworkService.getById(homeworkSaveDTO.getId());
if (origin == null) {
return Result.error("作业不存在");
}
// 2. 覆盖更新作业基础信息
AiolHomework toUpdate = new AiolHomework();
org.springframework.beans.BeanUtils.copyProperties(homeworkSaveDTO, toUpdate);
boolean updated = aiolHomeworkService.updateById(toUpdate);
if (!updated) {
return Result.error("更新作业失败");
}
final String homeworkId = homeworkSaveDTO.getId();
final String classId = homeworkSaveDTO.getClassId();
final String courseId = homeworkSaveDTO.getCourseId();
final String sectionId = homeworkSaveDTO.getSectionId();
// 3. 处理章节关联先清除旧关联再新增若提供
try {
QueryWrapper<AiolEntityLink> delWrapper = new QueryWrapper<>();
delWrapper.eq("target_type", EntityLinkConst.TargetType.HOMEWORK)
.eq("target_id", homeworkId)
.eq("source_type", EntityLinkConst.SourceType.COURSE_SECTION);
entityLinkBizService.remove(delWrapper);
if (sectionId != null && !sectionId.trim().isEmpty()) {
entityLinkBizService.save(
EntityLinkConst.SourceType.COURSE_SECTION,
sectionId.trim(),
EntityLinkConst.TargetType.HOMEWORK,
homeworkId
);
}
} catch (Exception e) {
log.warn("更新作业章节关联失败但不影响主体更新: homeworkId={}, error={}", homeworkId, e.getMessage());
}
// 4. 依据新的班级/课程范围为缺失的学生补齐提交记录不删除已有
try {
List<String> needStudentIds = new ArrayList<>();
if (classId != null && !classId.trim().isEmpty()) {
String[] classIds = classId.split(",");
for (String singleClassId : classIds) {
if (singleClassId == null || singleClassId.trim().isEmpty()) {
continue;
}
QueryWrapper<AiolClassStudent> csWrapper = new QueryWrapper<>();
csWrapper.eq("class_id", singleClassId.trim());
List<AiolClassStudent> classStudents = aiolClassStudentMapper.selectList(csWrapper);
for (AiolClassStudent cs : classStudents) {
String sid = cs.getStudentId();
if (sid != null && !sid.isEmpty() && !needStudentIds.contains(sid)) {
needStudentIds.add(sid);
}
}
}
} else if (courseId != null && !courseId.trim().isEmpty()) {
QueryWrapper<AiolCourseSignup> suWrapper = new QueryWrapper<>();
suWrapper.eq("course_id", courseId.trim());
List<AiolCourseSignup> signups = aiolCourseSignupMapper.selectList(suWrapper);
for (AiolCourseSignup su : signups) {
String sid = su.getUserId();
if (sid != null && !sid.isEmpty() && !needStudentIds.contains(sid)) {
needStudentIds.add(sid);
}
}
}
// 补齐缺失提交记录
if (!needStudentIds.isEmpty()) {
for (String sid : needStudentIds) {
QueryWrapper<AiolHomeworkSubmit> existWrapper = new QueryWrapper<>();
existWrapper.eq("homework_id", homeworkId).eq("student_id", sid);
long exist = homeworkSubmitService.count(existWrapper);
if (exist == 0) {
homeworkSubmitService.createEmptySubmit(homeworkId, sid);
}
}
}
} catch (Exception e) {
log.warn("补齐作业提交记录失败但不影响主体更新: homeworkId={}, error={}", homeworkId, e.getMessage());
}
log.info("教师编辑作业成功: homeworkId={}", homeworkId);
return Result.OK("编辑成功!");
} catch (Exception e) {
log.error("教师编辑作业失败: homeworkSaveDTO={}, error={}", homeworkSaveDTO, e.getMessage(), e);
return Result.error("编辑作业失败: " + e.getMessage());
}
}
@AutoLog(value = "作业-批阅")
@Operation(summary = "作业批阅", description = "教师批阅作业,更新作业提交记录的分数和状态")
@RequiresPermissions("aiol:aiol_homework:edit")
@PostMapping(value = "/review")
public Result<String> reviewHomework(@RequestBody Map<String, Object> reviewParams) {
try {
// 1. 从JSON参数中提取数据
String submitId = (String) reviewParams.get("submitId");
Integer score = (Integer) reviewParams.get("score");
String comment = (String) reviewParams.get("comment");
// 验证必填参数
if (submitId == null || submitId.trim().isEmpty()) {
return Result.error("submitId不能为空");
}
// 2. 查询作业提交记录
AiolHomeworkSubmit submit = homeworkSubmitService.getById(submitId);
if (submit == null) {
return Result.error("作业提交记录不存在");
}
// 3. 更新作业提交记录
AiolHomeworkSubmit updateSubmit = new AiolHomeworkSubmit();
updateSubmit.setId(submitId);
// 设置分数如果提供
if (score != null) {
updateSubmit.setScore(score);
}
// 设置评语如果提供
if (comment != null && !comment.trim().isEmpty()) {
updateSubmit.setComment(comment.trim());
}
// 更新状态为已批阅3
updateSubmit.setStatus(3);
// 4. 保存更新
boolean updated = homeworkSubmitService.updateById(updateSubmit);
if (!updated) {
return Result.error("批阅失败,更新作业提交记录失败");
}
log.info("作业批阅成功: submitId={}, score={}, status=3", submitId, score);
return Result.OK("批阅成功!");
} catch (Exception e) {
log.error("作业批阅失败: reviewParams={}, error={}", reviewParams, e.getMessage(), e);
return Result.error("批阅失败: " + e.getMessage());
}
}
/**
* 将AiolHomework转换为包含详情的DTO
*
* @param homework 作业实体
* @return 包含班级名和章节信息的DTO
*/
private HomeworkWithDetailsDTO convertToHomeworkWithDetails(AiolHomework homework) {
HomeworkWithDetailsDTO dto = new HomeworkWithDetailsDTO();
BeanUtils.copyProperties(homework, dto);
try {
// 1. 查询班级信息
List<String> classNames = new ArrayList<>();
String classId = homework.getClassId();
if (classId != null && !classId.trim().isEmpty()) {
String[] classIds = classId.split(",");
for (String singleClassId : classIds) {
if (singleClassId != null && !singleClassId.trim().isEmpty()) {
singleClassId = singleClassId.trim();
AiolClass aiolClass = aiolClassService.getById(singleClassId);
if (aiolClass != null && aiolClass.getName() != null) {
classNames.add(aiolClass.getName());
}
}
}
}
dto.setClassNames(classNames);
// 2. 查询章节信息
List<String> sectionIds = entityLinkBizService.listSourceIds(
EntityLinkConst.TargetType.HOMEWORK,
homework.getId(),
EntityLinkConst.SourceType.COURSE_SECTION
);
if (!sectionIds.isEmpty()) {
String sectionId = sectionIds.get(0); // 取第一个章节ID
dto.setSectionId(sectionId);
AiolCourseSection section = aiolCourseSectionService.getById(sectionId);
if (section != null && section.getName() != null) {
dto.setSectionTitle(section.getName());
}
}
} catch (Exception e) {
log.error("转换作业详情失败: homeworkId={}, error={}", homework.getId(), e.getMessage(), e);
// 即使转换失败也返回基本的作业信息
}
return dto;
}
}

View File

@ -0,0 +1,328 @@
package org.jeecg.modules.aiol.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.aiol.entity.AiolHomeworkSubmit;
import org.jeecg.modules.aiol.entity.AiolHomework;
import org.jeecg.modules.aiol.entity.AiolClass;
import org.jeecg.modules.aiol.entity.AiolClassStudent;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.aiol.service.IAiolHomeworkSubmitService;
import org.jeecg.modules.aiol.service.IAiolHomeworkService;
import org.jeecg.modules.aiol.service.IAiolClassService;
import org.jeecg.modules.aiol.mapper.AiolClassStudentMapper;
import org.jeecg.modules.system.service.ISysUserService;
import org.jeecg.modules.aiol.dto.HomeworkSubmitDetailDTO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 作业提交
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Tag(name="作业提交")
@RestController
@RequestMapping("/aiol/aiolHomeworkSubmit")
@Slf4j
public class AiolHomeworkSubmitController extends JeecgController<AiolHomeworkSubmit, IAiolHomeworkSubmitService> {
@Autowired
private IAiolHomeworkSubmitService aiolHomeworkSubmitService;
@Autowired
private IAiolHomeworkService aiolHomeworkService;
@Autowired
private IAiolClassService aiolClassService;
@Autowired
private AiolClassStudentMapper aiolClassStudentMapper;
@Autowired
private ISysUserService sysUserService;
/**
* 分页列表查询
*
* @param aiolHomeworkSubmit
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "作业提交-分页列表查询")
@Operation(summary="作业提交-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolHomeworkSubmit>> queryPageList(AiolHomeworkSubmit aiolHomeworkSubmit,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolHomeworkSubmit> queryWrapper = QueryGenerator.initQueryWrapper(aiolHomeworkSubmit, req.getParameterMap());
Page<AiolHomeworkSubmit> page = new Page<AiolHomeworkSubmit>(pageNo, pageSize);
IPage<AiolHomeworkSubmit> pageList = aiolHomeworkSubmitService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolHomeworkSubmit
* @return
*/
@AutoLog(value = "作业提交-添加")
@Operation(summary="作业提交-添加")
@RequiresPermissions("aiol:aiol_homework_submit:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolHomeworkSubmit aiolHomeworkSubmit) {
aiolHomeworkSubmitService.save(aiolHomeworkSubmit);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param aiolHomeworkSubmit
* @return
*/
@AutoLog(value = "作业提交-编辑")
@Operation(summary="作业提交-编辑")
@RequiresPermissions("aiol:aiol_homework_submit:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AiolHomeworkSubmit aiolHomeworkSubmit) {
aiolHomeworkSubmitService.updateById(aiolHomeworkSubmit);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "作业提交-通过id删除")
@Operation(summary="作业提交-通过id删除")
@RequiresPermissions("aiol:aiol_homework_submit:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
aiolHomeworkSubmitService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "作业提交-批量删除")
@Operation(summary="作业提交-批量删除")
@RequiresPermissions("aiol:aiol_homework_submit:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.aiolHomeworkSubmitService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "作业提交-通过id查询")
@Operation(summary="作业提交-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolHomeworkSubmit> queryById(@RequestParam(name="id",required=true) String id) {
AiolHomeworkSubmit aiolHomeworkSubmit = aiolHomeworkSubmitService.getById(id);
if(aiolHomeworkSubmit==null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolHomeworkSubmit);
}
/**
* 导出excel
*
* @param request
* @param aiolHomeworkSubmit
*/
@RequiresPermissions("aiol:aiol_homework_submit:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolHomeworkSubmit aiolHomeworkSubmit) {
return super.exportXls(request, aiolHomeworkSubmit, AiolHomeworkSubmit.class, "作业提交");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_homework_submit:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolHomeworkSubmit.class);
}
@AutoLog(value = "作业提交-查询作业提交情况")
@Operation(summary = "查询作业提交情况", description = "根据作业ID查询该作业的所有提交情况包含作业信息、学生信息、班级信息")
@GetMapping(value = "/homework/{homeworkId}/submits")
public Result<List<HomeworkSubmitDetailDTO>> queryHomeworkSubmits(@PathVariable("homeworkId") String homeworkId) {
try {
// 1. 查询指定作业的所有提交记录
QueryWrapper<AiolHomeworkSubmit> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("homework_id", homeworkId)
.orderByDesc("create_time");
List<AiolHomeworkSubmit> submitList = aiolHomeworkSubmitService.list(queryWrapper);
if (submitList.isEmpty()) {
log.info("查询作业提交情况成功: homeworkId={}, 提交数量=0", homeworkId);
return Result.OK(new java.util.ArrayList<>());
}
// 2. 查询作业信息
AiolHomework homework = aiolHomeworkService.getById(homeworkId);
if (homework == null) {
return Result.error("作业不存在");
}
// 3. 提取所有学生ID
List<String> studentIds = submitList.stream()
.map(AiolHomeworkSubmit::getStudentId)
.distinct()
.collect(Collectors.toList());
// 4. 批量查询学生信息
final Map<String, SysUser> studentMap;
if (!studentIds.isEmpty()) {
List<SysUser> students = sysUserService.listByIds(studentIds);
studentMap = students.stream()
.collect(Collectors.toMap(SysUser::getId, student -> student));
} else {
studentMap = new HashMap<>();
}
// 5. 查询学生班级关系
Map<String, AiolClass> studentClassMap = new HashMap<>();
if (!studentIds.isEmpty()) {
QueryWrapper<AiolClassStudent> classStudentWrapper = new QueryWrapper<>();
classStudentWrapper.in("student_id", studentIds);
List<AiolClassStudent> classStudents = aiolClassStudentMapper.selectList(classStudentWrapper);
if (!classStudents.isEmpty()) {
// 提取班级ID
List<String> classIds = classStudents.stream()
.map(AiolClassStudent::getClassId)
.distinct()
.collect(Collectors.toList());
// 批量查询班级信息
List<AiolClass> classes = aiolClassService.listByIds(classIds);
Map<String, AiolClass> classMap = classes.stream()
.collect(Collectors.toMap(AiolClass::getId, cls -> cls));
// 建立学生到班级的映射
for (AiolClassStudent classStudent : classStudents) {
AiolClass cls = classMap.get(classStudent.getClassId());
if (cls != null) {
studentClassMap.put(classStudent.getStudentId(), cls);
}
}
}
}
// 6. 组装结果
List<HomeworkSubmitDetailDTO> resultList = submitList.stream()
.map(submit -> {
HomeworkSubmitDetailDTO dto = new HomeworkSubmitDetailDTO();
// 复制提交信息
dto.setId(submit.getId());
dto.setHomeworkId(submit.getHomeworkId());
dto.setStudentId(submit.getStudentId());
dto.setContent(submit.getContent());
dto.setAttachment(submit.getAttachment());
dto.setScore(submit.getScore());
dto.setComment(submit.getComment());
dto.setStatus(submit.getStatus());
dto.setCreateTime(submit.getCreateTime());
dto.setUpdateTime(submit.getUpdateTime());
// 设置作业信息
dto.setHomeworkTitle(homework.getTitle());
dto.setHomeworkDescription(homework.getDescription());
dto.setHomeworkMaxScore(homework.getMaxScore());
dto.setHomeworkPassScore(homework.getPassScore());
dto.setHomeworkStartTime(homework.getStartTime());
dto.setHomeworkEndTime(homework.getEndTime());
dto.setHomeworkStatus(homework.getStatus());
dto.setHomeworkNotifyTime(homework.getNotifyTime());
dto.setHomeworkAllowMakeup(homework.getAllowMakeup());
dto.setHomeworkMakeupTime(homework.getMakeupTime());
// 设置学生信息
SysUser student = studentMap.get(submit.getStudentId());
if (student != null) {
dto.setStudentUsername(student.getUsername());
dto.setStudentRealname(student.getRealname());
dto.setStudentAvatar(student.getAvatar());
}
// 设置班级信息
AiolClass cls = studentClassMap.get(submit.getStudentId());
if (cls != null) {
dto.setClassId(cls.getId());
dto.setClassName(cls.getName());
}
return dto;
})
.collect(Collectors.toList());
log.info("查询作业提交情况成功: homeworkId={}, 提交数量={}", homeworkId, resultList.size());
return Result.OK(resultList);
} catch (Exception e) {
log.error("查询作业提交情况失败: homeworkId={}, error={}", homeworkId, e.getMessage(), e);
return Result.error("查询作业提交情况失败: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,166 @@
package org.jeecg.modules.aiol.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.config.shiro.IgnoreAuth;
import org.jeecg.modules.aiol.dto.CommentWithUserInfo;
import org.jeecg.modules.aiol.entity.AiolContentConfig;
import org.jeecg.modules.aiol.entity.AiolCourse;
import org.jeecg.modules.aiol.mapper.AiolContentConfigMapper;
import org.jeecg.modules.aiol.mapper.AiolCourseMapper;
import org.jeecg.modules.aiol.service.IAiolCommentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.*;
@Tag(name = "首页")
@RestController
@RequestMapping("/aiol/index")
@Slf4j
public class AiolIndexController {
@Autowired
private AiolContentConfigMapper contentConfigMapper;
@Autowired
private IAiolCommentService commentBizService;
@Autowired(required = false)
private StringRedisTemplate stringRedisTemplate;
@Autowired
private AiolCourseMapper courseMapper;
private static final String HOT_SEARCH_ZSET_KEY = "hot:search";
@GetMapping("/content")
@Operation(summary = "查询首页内容")
@IgnoreAuth
public Result<String> queryContent(@RequestParam String contentKey) {
AiolContentConfig contentConfig = contentConfigMapper.selectOne(new QueryWrapper<AiolContentConfig>().eq("content_key", contentKey));
if (contentConfig == null) {
return Result.error("内容配置不存在");
}
return Result.OK(contentConfig.getContentValue());
}
@GetMapping("/statistics")
@Operation(summary = "查询首页数据统计")
@IgnoreAuth
public Result<Map<String, Object>> queryStatistics() {
Map<String, Object> map = new HashMap<>();
// 定义统计项的配置键
String[] statisticsKeys = {"xxsp", "mszj", "pxjc", "zysc", "zxsy"};
// 从配置表查询每个统计项的值
for (String key : statisticsKeys) {
AiolContentConfig contentConfig = contentConfigMapper.selectOne(
new QueryWrapper<AiolContentConfig>().eq("content_key", key)
);
if (contentConfig != null && contentConfig.getContentValue() != null) {
try {
// 尝试解析为数字如果失败则使用默认值0
Integer value = Integer.parseInt(contentConfig.getContentValue());
map.put(key, value);
} catch (NumberFormatException e) {
log.warn("配置项 {} 的值 {} 不是有效数字使用默认值0", key, contentConfig.getContentValue());
map.put(key, 0);
}
} else {
log.warn("配置项 {} 不存在使用默认值0", key);
map.put(key, 0);
}
}
return Result.OK(map);
}
@GetMapping("/selected_comments")
@Operation(summary = "查询精选评论")
@IgnoreAuth
public Result<List<CommentWithUserInfo>> querySelectedComments() {
// 查询精选评论包含用户信息
List<CommentWithUserInfo> comments = commentBizService.getAllSelectedComments();
return Result.OK(comments);
}
@GetMapping("/hot_search")
@Operation(summary = "查询热门搜索记录(关键词+搜索次数Redis ZSet 排名)")
@IgnoreAuth
public Result<List<Map<String, Object>>> queryHotSearch(@RequestParam(defaultValue = "10") Integer limit) {
if (limit == null || limit <= 0) {
limit = 10;
}
if (stringRedisTemplate == null) {
return Result.error("Redis 未配置,无法查询热门搜索");
}
Set<ZSetOperations.TypedTuple<String>> tuples = stringRedisTemplate.opsForZSet()
.reverseRangeWithScores(HOT_SEARCH_ZSET_KEY, 0, limit - 1);
List<Map<String, Object>> result = new ArrayList<>();
if (tuples != null) {
for (ZSetOperations.TypedTuple<String> tuple : tuples) {
if (tuple == null) {
continue;
}
Map<String, Object> item = new HashMap<>();
item.put("keyword", tuple.getValue());
item.put("count", tuple.getScore() == null ? 0 : tuple.getScore().longValue());
result.add(item);
}
}
return Result.OK(result);
}
@GetMapping("/search")
@Operation(summary = "全局搜索课程name/description/school")
@IgnoreAuth
public Result<List<AiolCourse>> globalSearch(
@RequestParam String keyword,
@RequestParam(required = false, defaultValue = "20") Integer limit
) {
if (limit == null || limit <= 0) {
limit = 20;
}
if (limit > 100) {
limit = 100;
}
if (keyword == null || keyword.trim().isEmpty()) {
return Result.OK(new ArrayList<>());
}
if (keyword.trim().length() < 2) {
return Result.error("关键词长度至少为2个字符");
}
// 记录关键词到 Redis 热搜
if (stringRedisTemplate != null) {
try {
stringRedisTemplate.opsForZSet().incrementScore(HOT_SEARCH_ZSET_KEY, keyword.trim(), 1D);
} catch (Exception e) {
log.warn("记录热搜关键词到 Redis 失败: {}", keyword, e);
}
}
String kw = keyword.trim();
QueryWrapper<AiolCourse> wrapper = new QueryWrapper<>();
wrapper.lambda()
.like(AiolCourse::getName, kw)
.or()
.like(AiolCourse::getDescription, kw)
.or()
.like(AiolCourse::getSchool, kw);
List<AiolCourse> list = courseMapper.selectList(wrapper.last("limit " + limit));
return Result.OK(list);
}
}

View File

@ -0,0 +1,182 @@
package org.jeecg.modules.aiol.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.aiol.entity.AiolLearnProgress;
import org.jeecg.modules.aiol.service.IAiolLearnProgressService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 学习进度
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Tag(name="学习进度")
@RestController
@RequestMapping("/aiol/aiolLearnProgress")
@Slf4j
public class AiolLearnProgressController extends JeecgController<AiolLearnProgress, IAiolLearnProgressService> {
@Autowired
private IAiolLearnProgressService aiolLearnProgressService;
/**
* 分页列表查询
*
* @param aiolLearnProgress
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "学习进度-分页列表查询")
@Operation(summary="学习进度-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolLearnProgress>> queryPageList(AiolLearnProgress aiolLearnProgress,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolLearnProgress> queryWrapper = QueryGenerator.initQueryWrapper(aiolLearnProgress, req.getParameterMap());
Page<AiolLearnProgress> page = new Page<AiolLearnProgress>(pageNo, pageSize);
IPage<AiolLearnProgress> pageList = aiolLearnProgressService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolLearnProgress
* @return
*/
@AutoLog(value = "学习进度-添加")
@Operation(summary="学习进度-添加")
@RequiresPermissions("aiol:aiol_learn_progress:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolLearnProgress aiolLearnProgress) {
aiolLearnProgressService.save(aiolLearnProgress);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param aiolLearnProgress
* @return
*/
@AutoLog(value = "学习进度-编辑")
@Operation(summary="学习进度-编辑")
@RequiresPermissions("aiol:aiol_learn_progress:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AiolLearnProgress aiolLearnProgress) {
aiolLearnProgressService.updateById(aiolLearnProgress);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "学习进度-通过id删除")
@Operation(summary="学习进度-通过id删除")
@RequiresPermissions("aiol:aiol_learn_progress:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
aiolLearnProgressService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "学习进度-批量删除")
@Operation(summary="学习进度-批量删除")
@RequiresPermissions("aiol:aiol_learn_progress:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.aiolLearnProgressService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "学习进度-通过id查询")
@Operation(summary="学习进度-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolLearnProgress> queryById(@RequestParam(name="id",required=true) String id) {
AiolLearnProgress aiolLearnProgress = aiolLearnProgressService.getById(id);
if(aiolLearnProgress==null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolLearnProgress);
}
/**
* 导出excel
*
* @param request
* @param aiolLearnProgress
*/
@RequiresPermissions("aiol:aiol_learn_progress:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolLearnProgress aiolLearnProgress) {
return super.exportXls(request, aiolLearnProgress, AiolLearnProgress.class, "学习进度");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_learn_progress:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolLearnProgress.class);
}
}

View File

@ -0,0 +1,229 @@
package org.jeecg.modules.aiol.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.shiro.IgnoreAuth;
import org.jeecg.modules.aiol.entity.AiolMenu;
import org.jeecg.modules.aiol.service.IAiolMenuService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 前台菜单
* @Author: jeecg-boot
* @Date: 2025-09-29
* @Version: V1.0
*/
@Tag(name="前台菜单")
@RestController
@RequestMapping("/aiol/aiolMenu")
@Slf4j
public class AiolMenuController extends JeecgController<AiolMenu, IAiolMenuService> {
@Autowired
private IAiolMenuService aiolMenuService;
/**
* 分页列表查询
*
* @param aiolMenu
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "前台菜单-分页列表查询")
@Operation(summary="前台菜单-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolMenu>> queryPageList(AiolMenu aiolMenu,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolMenu> queryWrapper = QueryGenerator.initQueryWrapper(aiolMenu, req.getParameterMap());
Page<AiolMenu> page = new Page<AiolMenu>(pageNo, pageSize);
IPage<AiolMenu> pageList = aiolMenuService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolMenu
* @return
*/
@AutoLog(value = "前台菜单-添加")
@Operation(summary="前台菜单-添加")
@RequiresPermissions("aiol:aiol_menu:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolMenu aiolMenu) {
aiolMenuService.save(aiolMenu);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param aiolMenu
* @return
*/
@AutoLog(value = "前台菜单-编辑")
@Operation(summary="前台菜单-编辑")
@RequiresPermissions("aiol:aiol_menu:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AiolMenu aiolMenu) {
aiolMenuService.updateById(aiolMenu);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "前台菜单-通过id删除")
@Operation(summary="前台菜单-通过id删除")
@RequiresPermissions("aiol:aiol_menu:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
aiolMenuService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "前台菜单-批量删除")
@Operation(summary="前台菜单-批量删除")
@RequiresPermissions("aiol:aiol_menu:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.aiolMenuService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "前台菜单-通过id查询")
@Operation(summary="前台菜单-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolMenu> queryById(@RequestParam(name="id",required=true) String id) {
AiolMenu aiolMenu = aiolMenuService.getById(id);
if(aiolMenu==null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolMenu);
}
/**
* 导出excel
*
* @param request
* @param aiolMenu
*/
@RequiresPermissions("aiol:aiol_menu:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolMenu aiolMenu) {
return super.exportXls(request, aiolMenu, AiolMenu.class, "前台菜单");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_menu:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolMenu.class);
}
/**
* 获取前台菜单
*
* @return
*/
@Operation(summary="获取前台菜单")
@GetMapping(value = "/getIndexMenus")
@IgnoreAuth
public Result<List<AiolMenu>> getIndexMenus() {
QueryWrapper<AiolMenu> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("type", "index")
.eq("iz_visible", 1)
.orderByDesc("sort_order");
List<AiolMenu> menuList = aiolMenuService.list(queryWrapper);
return Result.OK(menuList);
}
@Operation(summary="获取学员端菜单")
@GetMapping(value = "/getStudentMenus")
@IgnoreAuth
public Result<List<AiolMenu>> getStudentMenus() {
QueryWrapper<AiolMenu> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("type", "student")
.eq("iz_visible", 1)
.orderByDesc("sort_order");
List<AiolMenu> menuList = aiolMenuService.list(queryWrapper);
return Result.OK(menuList);
}
/**
* 获取教师端菜单
*
* @return
*/
@Operation(summary="获取教师端菜单")
@GetMapping(value = "/getTeacherMenus")
@IgnoreAuth
public Result<List<AiolMenu>> getTeacherMenus() {
QueryWrapper<AiolMenu> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("type", "teacher")
.eq("iz_visible", 1)
.orderByDesc("sort_order");
List<AiolMenu> menuList = aiolMenuService.list(queryWrapper);
return Result.OK(menuList);
}
}

View File

@ -0,0 +1,262 @@
package org.jeecg.modules.aiol.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.jeecg.common.api.vo.Result;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.system.vo.LoginUser;
import org.springframework.web.bind.annotation.RequestParam;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import org.jeecg.modules.system.model.AnnouncementSendModel;
import org.jeecg.modules.system.service.ISysAnnouncementSendService;
import org.jeecg.modules.system.service.ISysAnnouncementService;
import org.jeecg.modules.system.entity.SysAnnouncementSend;
import org.jeecg.modules.system.entity.SysAnnouncement;
import org.jeecg.modules.aiol.utils.MessageNotificationUtil;
import org.jeecg.common.constant.CommonConstant;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Date;
import lombok.extern.slf4j.Slf4j;
@RestController
@RequestMapping("/aiol/message")
@Slf4j
public class AiolMessageController {
@Autowired
private ISysAnnouncementSendService sysAnnouncementSendService;
@Autowired
private ISysAnnouncementService sysAnnouncementService;
@Autowired
private MessageNotificationUtil messageNotificationUtil;
@GetMapping("/test")
public void test() {
// 测试评论通知
messageNotificationUtil.sendCommentNotification(
"202121200873", // 接收用户ID
"202121200874", // 发送用户ID
"1966804797404344321", // 发送者ID
"小明", // 发送者用户名
456L, // 评论ID
"老师讲得真棒![user:789:李四] 快来看这个课程!", // 评论内容
"course", // 实体类型
"1954463468539371522", // 实体ID
"python语言基础与应用", // 实体标题
"2025-09-16T15:30:00Z" // 操作时间
);
// 测试点赞通知
messageNotificationUtil.sendLikeNotification(
"202121200873", // 接收用户ID
"202121200874", // 发送用户ID
"202121200874", // 发送者ID
"202121200874", // 发送者用户名
"course", // 实体类型
"1954463468539371522", // 实体ID
"python语言基础与应用", // 实体标题
"like", // 操作类型
"2025-09-16T15:35:00Z" // 操作时间
);
}
@GetMapping("/comments_at")
public Result<IPage<AnnouncementSendModel>> queryCommentsAndAt(
AnnouncementSendModel model,
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
try {
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
String userId = sysUser.getId();
model.setUserId(userId);
model.setPageNo((pageNo - 1) * pageSize);
model.setPageSize(pageSize);
// 处理时间范围与SysAnnouncementSendController一致
if (StringUtils.isNotEmpty(model.getSendTimeBegin())) {
model.setSendTimeBegin(model.getSendTimeBegin() + " 00:00:00");
}
if (StringUtils.isNotEmpty(model.getSendTimeEnd())) {
model.setSendTimeEnd(model.getSendTimeEnd() + " 23:59:59");
}
// 评论和@ 消息类别 = 3
model.setMsgCategory("3");
Page<AnnouncementSendModel> page = new Page<>(pageNo, pageSize);
IPage<AnnouncementSendModel> pageList = sysAnnouncementSendService.getMyAnnouncementSendPage(page, model);
return Result.OK(pageList);
} catch (Exception e) {
return Result.error("查询评论/@消息失败: " + e.getMessage());
}
}
@GetMapping("/likes")
public Result<IPage<AnnouncementSendModel>> queryLikes(
AnnouncementSendModel model,
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
try {
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
String userId = sysUser.getId();
model.setUserId(userId);
model.setPageNo((pageNo - 1) * pageSize);
model.setPageSize(pageSize);
if (StringUtils.isNotEmpty(model.getSendTimeBegin())) {
model.setSendTimeBegin(model.getSendTimeBegin() + " 00:00:00");
}
if (StringUtils.isNotEmpty(model.getSendTimeEnd())) {
model.setSendTimeEnd(model.getSendTimeEnd() + " 23:59:59");
}
// 点赞 消息类别 = 4
model.setMsgCategory("4");
Page<AnnouncementSendModel> page = new Page<>(pageNo, pageSize);
IPage<AnnouncementSendModel> pageList = sysAnnouncementSendService.getMyAnnouncementSendPage(page, model);
return Result.OK(pageList);
} catch (Exception e) {
return Result.error("查询点赞消息失败: " + e.getMessage());
}
}
@GetMapping("/system")
public Result<IPage<AnnouncementSendModel>> querySystemMessages(
AnnouncementSendModel model,
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
try {
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
String userId = sysUser.getId();
model.setUserId(userId);
model.setPageNo((pageNo - 1) * pageSize);
model.setPageSize(pageSize);
// 处理时间范围与SysAnnouncementSendController一致
if (StringUtils.isNotEmpty(model.getSendTimeBegin())) {
model.setSendTimeBegin(model.getSendTimeBegin() + " 00:00:00");
}
if (StringUtils.isNotEmpty(model.getSendTimeEnd())) {
model.setSendTimeEnd(model.getSendTimeEnd() + " 23:59:59");
}
// 系统消息 消息类别 = 2
model.setMsgCategory("2");
Page<AnnouncementSendModel> page = new Page<>(pageNo, pageSize);
IPage<AnnouncementSendModel> pageList = sysAnnouncementSendService.getMyAnnouncementSendPage(page, model);
return Result.OK(pageList);
} catch (Exception e) {
return Result.error("查询系统消息失败: " + e.getMessage());
}
}
@GetMapping("/unread_count")
public Result<Map<String, Object>> queryUnreadMessageCount() {
try {
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
String userId = sysUser.getId();
Map<String, Object> result = new HashMap<>();
// 1. 先查询用户所有未读消息的annt_id
QueryWrapper<SysAnnouncementSend> unreadWrapper = new QueryWrapper<>();
unreadWrapper.eq("user_id", userId)
.or().eq("user_id", sysUser.getUsername())
.eq("read_flag", 0);
List<SysAnnouncementSend> unreadMessages = sysAnnouncementSendService.list(unreadWrapper);
if (unreadMessages.isEmpty()) {
// 如果没有未读消息返回0
result.put("commentsUnreadCount", 0);
result.put("likesUnreadCount", 0);
result.put("systemUnreadCount", 0);
result.put("totalUnreadCount", 0);
return Result.OK(result);
}
// 2. 提取所有annt_id
List<String> anntIds = unreadMessages.stream()
.map(SysAnnouncementSend::getAnntId)
.collect(java.util.stream.Collectors.toList());
// 3. 根据annt_id查询对应的消息详情获取msg_category
QueryWrapper<SysAnnouncement> announcementWrapper = new QueryWrapper<>();
announcementWrapper.in("id", anntIds);
List<SysAnnouncement> announcements = sysAnnouncementService.list(announcementWrapper);
// 4. 按msg_category分类统计
long commentsUnreadCount = 0; // msg_category=3
long likesUnreadCount = 0; // msg_category=4
long systemUnreadCount = 0; // msg_category=2
for (SysAnnouncement announcement : announcements) {
String msgCategory = announcement.getMsgCategory();
if ("3".equals(msgCategory)) {
commentsUnreadCount++;
} else if ("4".equals(msgCategory)) {
likesUnreadCount++;
} else if ("2".equals(msgCategory)) {
systemUnreadCount++;
}
}
// 5. 计算总未读数量
long totalUnreadCount = commentsUnreadCount + likesUnreadCount + systemUnreadCount;
result.put("commentsUnreadCount", commentsUnreadCount);
result.put("likesUnreadCount", likesUnreadCount);
result.put("systemUnreadCount", systemUnreadCount);
result.put("totalUnreadCount", totalUnreadCount);
return Result.OK(result);
} catch (Exception e) {
return Result.error("查询未读消息数量失败: " + e.getMessage());
}
}
/**
* @功能标记单条消息已读
* @param sendId 消息发送记录ID
* @return
*/
@GetMapping(value = "/readOne")
public Result<SysAnnouncementSend> readOne(@RequestParam(name = "sendId", required = true) String sendId) {
Result<SysAnnouncementSend> result = new Result<SysAnnouncementSend>();
try {
if (StringUtils.isEmpty(sendId)) {
result.error500("消息ID不能为空");
return result;
}
// 构建更新条件根据sendId和userId更新单条消息
LambdaUpdateWrapper<SysAnnouncementSend> updateWrapper = new UpdateWrapper<SysAnnouncementSend>().lambda();
updateWrapper.set(SysAnnouncementSend::getReadFlag, CommonConstant.HAS_READ_FLAG);
updateWrapper.set(SysAnnouncementSend::getReadTime, new Date());
updateWrapper.eq(SysAnnouncementSend::getId, sendId);
SysAnnouncementSend announcementSend = new SysAnnouncementSend();
boolean updateResult = sysAnnouncementSendService.update(announcementSend, updateWrapper);
if (updateResult) {
result.setSuccess(true);
result.setMessage("消息已标记为已读");
} else {
result.error500("标记已读失败,可能消息不存在或已被标记");
}
return result;
} catch (Exception e) {
log.error("标记单条消息已读失败: " + e.getMessage(), e);
result.error500("标记已读失败: " + e.getMessage());
return result;
}
}
}

View File

@ -0,0 +1,184 @@
package org.jeecg.modules.aiol.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.aiol.entity.AiolPaper;
import org.jeecg.modules.aiol.service.IAiolPaperService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 试卷
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Tag(name="试卷")
@RestController
@RequestMapping("/aiol/aiolPaper")
@Slf4j
public class AiolPaperController extends JeecgController<AiolPaper, IAiolPaperService> {
@Autowired
private IAiolPaperService aiolPaperService;
/**
* 分页列表查询
*
* @param aiolPaper
* @param pageNo
* @param pageSize
* @param req
* @return
*/
@Operation(summary="试卷-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<Map<String, Object>>> queryPageList(AiolPaper aiolPaper,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolPaper> queryWrapper = QueryGenerator.initQueryWrapper(aiolPaper, req.getParameterMap());
Page<Map<String, Object>> page = new Page<>(pageNo, pageSize);
queryWrapper.select("aiol_paper.*", "(SELECT realname FROM sys_user WHERE username = aiol_paper.create_by) as createByName");
IPage<Map<String, Object>> pageList = aiolPaperService.pageMaps(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolPaper
* @return
*/
@AutoLog(value = "试卷-添加")
@Operation(summary="试卷-添加")
@RequiresPermissions("aiol:aiol_paper:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolPaper aiolPaper) {
aiolPaperService.save(aiolPaper);
return Result.OK(aiolPaper.getId());
}
/**
* 编辑
*
* @param aiolPaper
* @return
*/
@AutoLog(value = "试卷-编辑")
@Operation(summary="试卷-编辑")
@RequiresPermissions("aiol:aiol_paper:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AiolPaper aiolPaper) {
aiolPaperService.updateById(aiolPaper);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "试卷-通过id删除")
@Operation(summary="试卷-通过id删除")
@RequiresPermissions("aiol:aiol_paper:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
aiolPaperService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "试卷-批量删除")
@Operation(summary="试卷-批量删除")
@RequiresPermissions("aiol:aiol_paper:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.aiolPaperService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "试卷-通过id查询")
@Operation(summary="试卷-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolPaper> queryById(@RequestParam(name="id",required=true) String id) {
AiolPaper aiolPaper = aiolPaperService.getById(id);
if(aiolPaper==null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolPaper);
}
/**
* 导出excel
*
* @param request
* @param aiolPaper
*/
@RequiresPermissions("aiol:aiol_paper:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolPaper aiolPaper) {
return super.exportXls(request, aiolPaper, AiolPaper.class, "试卷");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_paper:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolPaper.class);
}
}

View File

@ -0,0 +1,328 @@
package org.jeecg.modules.aiol.controller;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.modules.aiol.dto.QuestionAnswerDTO;
import org.jeecg.modules.aiol.entity.AiolPaperQuestion;
import org.jeecg.modules.aiol.entity.AiolQuestion;
import org.jeecg.modules.aiol.entity.AiolQuestionAnswer;
import org.jeecg.modules.aiol.entity.AiolQuestionOption;
import org.jeecg.modules.aiol.service.IAiolPaperQuestionService;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.aiol.service.IAiolQuestionAnswerService;
import org.jeecg.modules.aiol.service.IAiolQuestionOptionService;
import org.jeecg.modules.aiol.service.IAiolQuestionService;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Description: 试卷试题
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Tag(name="试卷试题")
@RestController
@RequestMapping("/aiol/aiolPaperQuestion")
@Slf4j
public class AiolPaperQuestionController extends JeecgController<AiolPaperQuestion, IAiolPaperQuestionService> {
@Autowired
private IAiolPaperQuestionService aiolPaperQuestionService;
@Autowired
private IAiolQuestionService aiolQuestionService;
@Autowired
private IAiolQuestionAnswerService answerService;
@Autowired
private IAiolQuestionOptionService optionService;
/**
* 分页列表查询
*
* @param aiolPaperQuestion
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "试卷试题-分页列表查询")
@Operation(summary="试卷试题-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolPaperQuestion>> queryPageList(AiolPaperQuestion aiolPaperQuestion,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolPaperQuestion> queryWrapper = QueryGenerator.initQueryWrapper(aiolPaperQuestion, req.getParameterMap());
Page<AiolPaperQuestion> page = new Page<AiolPaperQuestion>(pageNo, pageSize);
IPage<AiolPaperQuestion> pageList = aiolPaperQuestionService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolPaperQuestion
* @return
*/
@AutoLog(value = "试卷试题-添加")
@Operation(summary="试卷试题-添加")
@RequiresPermissions("aiol:aiol_paper_question:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolPaperQuestion aiolPaperQuestion) {
boolean isScoreNaN = false;
//获取试题及其子题目分数
List<AiolPaperQuestion> paperQuestions = new ArrayList<>();
AiolQuestion question = aiolQuestionService.getById(aiolPaperQuestion.getQuestionId());
if(aiolPaperQuestion.getScore().isNaN()){
isScoreNaN = true;
aiolPaperQuestion.setScore(Double.valueOf(question.getScore()));
}
paperQuestions.add(aiolPaperQuestion);
if( question.getType() == 5 ){
//如果试题类型为复合题则获取其子题目
LambdaQueryWrapper<AiolQuestion> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(AiolQuestion::getParentId, question.getId());
List<AiolQuestion> questions = aiolQuestionService.list(queryWrapper);
for(AiolQuestion q : questions){
AiolPaperQuestion sonPaperQuestion = new AiolPaperQuestion();
sonPaperQuestion.setPaperId(aiolPaperQuestion.getPaperId());
sonPaperQuestion.setQuestionId(q.getId());
sonPaperQuestion.setOrderNo(aiolPaperQuestion.getOrderNo());
if(isScoreNaN){
sonPaperQuestion.setScore(Double.valueOf(q.getScore()));
}else{
// 计算原始分数
double rawScore = aiolPaperQuestion.getScore() * q.getScore() / question.getScore();
// 四舍五入保留两位小数
BigDecimal bd = new BigDecimal(rawScore);
bd = bd.setScale(2, RoundingMode.HALF_UP);
sonPaperQuestion.setScore(bd.doubleValue());
}
paperQuestions.add(sonPaperQuestion);
}
}
aiolPaperQuestionService.saveBatch(paperQuestions);
return Result.OK(aiolPaperQuestion.getId());
}
/**
* 编辑
*
* @param aiolPaperQuestion
* @return
*/
@AutoLog(value = "试卷试题-编辑")
@Operation(summary="试卷试题-编辑")
@RequiresPermissions("aiol:aiol_paper_question:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AiolPaperQuestion aiolPaperQuestion) {
aiolPaperQuestionService.updateById(aiolPaperQuestion);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "试卷试题-通过id删除")
@Operation(summary="试卷试题-通过id删除")
@RequiresPermissions("aiol:aiol_paper_question:delete")
@DeleteMapping(value = "/delete")
@Transactional
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
AiolPaperQuestion byId = aiolPaperQuestionService.getById(id);
//如果试题类型为复合题则删除其子题目
List<AiolQuestion> list = aiolQuestionService.list(
new LambdaQueryWrapper<AiolQuestion>().
eq(AiolQuestion::getParentId, byId.getQuestionId()));
if(!list.isEmpty()){
aiolPaperQuestionService.remove(
new LambdaQueryWrapper<AiolPaperQuestion>().
eq(AiolPaperQuestion::getPaperId, byId.getPaperId())
.in(AiolPaperQuestion::getQuestionId, list.stream().map(AiolQuestion::getId).collect(Collectors.toList()))
);
}
aiolPaperQuestionService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "试卷试题-批量删除")
@Operation(summary="试卷试题-批量删除")
@RequiresPermissions("aiol:aiol_paper_question:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.aiolPaperQuestionService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "试卷试题-通过id查询")
@Operation(summary="试卷试题-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolPaperQuestion> queryById(@RequestParam(name="id",required=true) String id) {
AiolPaperQuestion aiolPaperQuestion = aiolPaperQuestionService.getById(id);
if(aiolPaperQuestion==null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolPaperQuestion);
}
/**
* 导出excel
*
* @param request
* @param aiolPaperQuestion
*/
@RequiresPermissions("aiol:aiol_paper_question:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolPaperQuestion aiolPaperQuestion) {
return super.exportXls(request, aiolPaperQuestion, AiolPaperQuestion.class, "试卷试题");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_paper_question:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolPaperQuestion.class);
}
@GetMapping("/paperDetail/{paperId}")
@Operation(summary = "查询试卷所有题目详情")
public Result<?> paperDetail(@PathVariable String paperId) {
List<AiolPaperQuestion> list = aiolPaperQuestionService.list(new LambdaQueryWrapper<AiolPaperQuestion>().eq(AiolPaperQuestion::getPaperId, paperId));
// 收集所有需要的 questionId
List<String> questionIds = list.stream()
.map(AiolPaperQuestion::getQuestionId)
.collect(Collectors.toList());
if(questionIds.isEmpty()){
return Result.error("试卷没有题目");
}
return Result.OK(convertToQuestionAnswerDTO(questionIds,aiolQuestionService,optionService,answerService));
}
/**
* 将题目列表转换为题目答案DTO映射
*
* @param questionIds 题目id列表
* @param aiolQuestionService 题目服务
* @param optionService 选项服务
* @param answerService 答案服务
* @return 题目答案DTO映射key为题目ID
*/
public static Collection<QuestionAnswerDTO> convertToQuestionAnswerDTO(
List<String> questionIds,
IAiolQuestionService aiolQuestionService,
IAiolQuestionOptionService optionService,
IAiolQuestionAnswerService answerService) {
// 批量查询所有题目
List<AiolQuestion> questions = aiolQuestionService.listByIds(questionIds);
// 分离选项和答案的ID
List<String> optionIds = new ArrayList<>();
List<String> answerIds = new ArrayList<>();
for (AiolQuestion question : questions) {
if (question.getType() >= 0 && question.getType() <= 2) {
optionIds.add(question.getId());
} else if (question.getType() == 3 || question.getType() == 4) {
answerIds.add(question.getId());
}
}
// 批量查询选项和答案
List<AiolQuestionOption> questionOptions = Collections.emptyList();
List<AiolQuestionAnswer> questionAnswers = Collections.emptyList();
if (!optionIds.isEmpty()) {
questionOptions = optionService.list(new LambdaQueryWrapper<AiolQuestionOption>().in(AiolQuestionOption::getQuestionId, optionIds));
}
if (!answerIds.isEmpty()) {
questionAnswers = answerService.list(new LambdaQueryWrapper<AiolQuestionAnswer>().in(AiolQuestionAnswer::getQuestionId, answerIds));
}
// 构建题目id的映射
Map<String, List<AiolQuestionOption>> optionMap = questionOptions.stream()
.collect(Collectors.groupingBy(AiolQuestionOption::getQuestionId));
Map<String, List<AiolQuestionAnswer>> answerMap = questionAnswers.stream()
.collect(Collectors.groupingBy(AiolQuestionAnswer::getQuestionId));
// 构建结果列表
Map<String, QuestionAnswerDTO> result = new HashMap<>();
Map<String, List<QuestionAnswerDTO>> children = new HashMap<>();
for (AiolQuestion question : questions) {
QuestionAnswerDTO questionAnswerDTO = new QuestionAnswerDTO();
questionAnswerDTO.setQuestion(question);
// 设置对应的选项或答案
if (question.getType() >= 0 && question.getType() <= 2) {
questionAnswerDTO.setAnswer(optionMap.get(question.getId()));
} else if (question.getType() == 3 || question.getType() == 4) {
questionAnswerDTO.setAnswer(answerMap.get(question.getId()));
}
// 处理父子关系
if (question.getParentId() == null || question.getParentId().isEmpty()) {
result.put(question.getId(), questionAnswerDTO);
} else {
children.computeIfAbsent(question.getParentId(), k -> new ArrayList<>())
.add(questionAnswerDTO);
}
}
// 将子问题列表添加到对应的父问题中
children.forEach((parentId, childList) -> {
if (result.containsKey(parentId)) {
result.get(parentId).setChildren(childList);
}
});
return result.values();
}
}

View File

@ -0,0 +1,182 @@
package org.jeecg.modules.aiol.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.aiol.entity.AiolQuestionAnswer;
import org.jeecg.modules.aiol.service.IAiolQuestionAnswerService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 题目答案
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Tag(name="题目答案")
@RestController
@RequestMapping("/aiol/aiolQuestionAnswer")
@Slf4j
public class AiolQuestionAnswerController extends JeecgController<AiolQuestionAnswer, IAiolQuestionAnswerService> {
@Autowired
private IAiolQuestionAnswerService aiolQuestionAnswerService;
/**
* 分页列表查询
*
* @param aiolQuestionAnswer
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "题目答案-分页列表查询")
@Operation(summary="题目答案-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolQuestionAnswer>> queryPageList(AiolQuestionAnswer aiolQuestionAnswer,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolQuestionAnswer> queryWrapper = QueryGenerator.initQueryWrapper(aiolQuestionAnswer, req.getParameterMap());
Page<AiolQuestionAnswer> page = new Page<AiolQuestionAnswer>(pageNo, pageSize);
IPage<AiolQuestionAnswer> pageList = aiolQuestionAnswerService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolQuestionAnswer
* @return
*/
@AutoLog(value = "题目答案-添加")
@Operation(summary="题目答案-添加")
@RequiresPermissions("aiol:aiol_question_answer:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolQuestionAnswer aiolQuestionAnswer) {
aiolQuestionAnswerService.save(aiolQuestionAnswer);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param aiolQuestionAnswer
* @return
*/
@AutoLog(value = "题目答案-编辑")
@Operation(summary="题目答案-编辑")
@RequiresPermissions("aiol:aiol_question_answer:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AiolQuestionAnswer aiolQuestionAnswer) {
aiolQuestionAnswerService.updateById(aiolQuestionAnswer);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "题目答案-通过id删除")
@Operation(summary="题目答案-通过id删除")
@RequiresPermissions("aiol:aiol_question_answer:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
aiolQuestionAnswerService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "题目答案-批量删除")
@Operation(summary="题目答案-批量删除")
@RequiresPermissions("aiol:aiol_question_answer:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.aiolQuestionAnswerService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "题目答案-通过id查询")
@Operation(summary="题目答案-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolQuestionAnswer> queryById(@RequestParam(name="id",required=true) String id) {
AiolQuestionAnswer aiolQuestionAnswer = aiolQuestionAnswerService.getById(id);
if(aiolQuestionAnswer==null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolQuestionAnswer);
}
/**
* 导出excel
*
* @param request
* @param aiolQuestionAnswer
*/
@RequiresPermissions("aiol:aiol_question_answer:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolQuestionAnswer aiolQuestionAnswer) {
return super.exportXls(request, aiolQuestionAnswer, AiolQuestionAnswer.class, "题目答案");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_question_answer:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolQuestionAnswer.class);
}
}

View File

@ -0,0 +1,216 @@
package org.jeecg.modules.aiol.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.aiol.entity.AiolQuestion;
import org.jeecg.modules.aiol.entity.AiolQuestionRepo;
import org.jeecg.modules.aiol.service.IAiolQuestionRepoService;
import org.jeecg.modules.aiol.service.IAiolQuestionService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 题目
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Tag(name="题目")
@RestController
@RequestMapping("/aiol/aiolQuestion")
@Slf4j
public class AiolQuestionController extends JeecgController<AiolQuestion, IAiolQuestionService> {
@Autowired
private IAiolQuestionService aiolQuestionService;
@Autowired
private AiolQuestionRepoController aiolQuestionRepoController;
/**
* 分页列表查询
*
* @param aiolQuestion
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "题目-分页列表查询")
@Operation(summary="题目-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolQuestion>> queryPageList(AiolQuestion aiolQuestion,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolQuestion> queryWrapper = QueryGenerator.initQueryWrapper(aiolQuestion, req.getParameterMap());
Page<AiolQuestion> page = new Page<AiolQuestion>(pageNo, pageSize);
IPage<AiolQuestion> pageList = aiolQuestionService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolQuestion
* @return
*/
@AutoLog(value = "题目-添加")
@Operation(summary="题目-添加")
@RequiresPermissions("aiol:aiol_question:add")
@PostMapping(value = "/add")
@Transactional
public Result<String> add(@RequestBody AiolQuestion aiolQuestion,@RequestParam(name="repoId",required=false) String repoId) {
aiolQuestion.setStatus(1);
aiolQuestionService.save(aiolQuestion);
//题库题目添加
if(repoId!=null&& !repoId.isEmpty()){
AiolQuestionRepo aiolQuestionRepo = new AiolQuestionRepo();
aiolQuestionRepo.setRepoId(repoId);
aiolQuestionRepo.setQuestionId(aiolQuestion.getId());
aiolQuestionRepoController.add(aiolQuestionRepo);
}
return Result.OK(aiolQuestion.getId());
}
@AutoLog(value = "题目-是否入库")
@Operation(summary="题目-是否入库")
@PostMapping(value = "/whetherPutStorage")
public Result<String> whetherPutStorage(@RequestBody List<String> ids){
try {
// 参数校验
if (ids == null || ids.isEmpty()) {
return Result.error("参数不能为空");
}
// 调用服务层更新状态
aiolQuestionService.updateStatusByIds(ids);
return Result.OK("操作成功");
} catch (Exception e) {
log.error("更新题目状态失败", e);
return Result.error("操作失败:" + e.getMessage());
}
}
/**
* 编辑
*
* @param aiolQuestion
* @return
*/
@AutoLog(value = "题目-编辑")
@Operation(summary="题目-编辑")
@RequiresPermissions("aiol:aiol_question:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AiolQuestion aiolQuestion) {
aiolQuestionService.updateById(aiolQuestion);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "题目-通过id删除")
@Operation(summary="题目-通过id删除")
@RequiresPermissions("aiol:aiol_question:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
aiolQuestionService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "题目-批量删除")
@Operation(summary="题目-批量删除")
@RequiresPermissions("aiol:aiol_question:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.aiolQuestionService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "题目-通过id查询")
@Operation(summary="题目-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolQuestion> queryById(@RequestParam(name="id",required=true) String id) {
AiolQuestion aiolQuestion = aiolQuestionService.getById(id);
if(aiolQuestion==null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolQuestion);
}
/**
* 导出excel
*
* @param request
* @param aiolQuestion
*/
@RequiresPermissions("aiol:aiol_question:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolQuestion aiolQuestion) {
return super.exportXls(request, aiolQuestion, AiolQuestion.class, "题目");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_question:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolQuestion.class);
}
}

View File

@ -0,0 +1,182 @@
package org.jeecg.modules.aiol.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.aiol.entity.AiolQuestionOption;
import org.jeecg.modules.aiol.service.IAiolQuestionOptionService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 题目选项
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Tag(name="题目选项")
@RestController
@RequestMapping("/aiol/aiolQuestionOption")
@Slf4j
public class AiolQuestionOptionController extends JeecgController<AiolQuestionOption, IAiolQuestionOptionService> {
@Autowired
private IAiolQuestionOptionService aiolQuestionOptionService;
/**
* 分页列表查询
*
* @param aiolQuestionOption
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "题目选项-分页列表查询")
@Operation(summary="题目选项-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolQuestionOption>> queryPageList(AiolQuestionOption aiolQuestionOption,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolQuestionOption> queryWrapper = QueryGenerator.initQueryWrapper(aiolQuestionOption, req.getParameterMap());
Page<AiolQuestionOption> page = new Page<AiolQuestionOption>(pageNo, pageSize);
IPage<AiolQuestionOption> pageList = aiolQuestionOptionService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolQuestionOption
* @return
*/
@AutoLog(value = "题目选项-添加")
@Operation(summary="题目选项-添加")
@RequiresPermissions("aiol:aiol_question_option:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolQuestionOption aiolQuestionOption) {
aiolQuestionOptionService.save(aiolQuestionOption);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param aiolQuestionOption
* @return
*/
@AutoLog(value = "题目选项-编辑")
@Operation(summary="题目选项-编辑")
@RequiresPermissions("aiol:aiol_question_option:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AiolQuestionOption aiolQuestionOption) {
aiolQuestionOptionService.updateById(aiolQuestionOption);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "题目选项-通过id删除")
@Operation(summary="题目选项-通过id删除")
@RequiresPermissions("aiol:aiol_question_option:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
aiolQuestionOptionService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "题目选项-批量删除")
@Operation(summary="题目选项-批量删除")
@RequiresPermissions("aiol:aiol_question_option:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.aiolQuestionOptionService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "题目选项-通过id查询")
@Operation(summary="题目选项-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolQuestionOption> queryById(@RequestParam(name="id",required=true) String id) {
AiolQuestionOption aiolQuestionOption = aiolQuestionOptionService.getById(id);
if(aiolQuestionOption==null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolQuestionOption);
}
/**
* 导出excel
*
* @param request
* @param aiolQuestionOption
*/
@RequiresPermissions("aiol:aiol_question_option:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolQuestionOption aiolQuestionOption) {
return super.exportXls(request, aiolQuestionOption, AiolQuestionOption.class, "题目选项");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_question_option:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolQuestionOption.class);
}
}

View File

@ -0,0 +1,220 @@
package org.jeecg.modules.aiol.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.aiol.entity.AiolQuestionRepo;
import org.jeecg.modules.aiol.entity.AiolRepo;
import org.jeecg.modules.aiol.service.IAiolQuestionRepoService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.aiol.service.IAiolRepoService;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 题库题目
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Tag(name="题库题目")
@RestController
@RequestMapping("/aiol/aiolQuestionRepo")
@Slf4j
public class AiolQuestionRepoController extends JeecgController<AiolQuestionRepo, IAiolQuestionRepoService> {
@Autowired
private IAiolQuestionRepoService aiolQuestionRepoService;
@Autowired
private IAiolRepoService aiolRepoService;
/**
* 分页列表查询
*
* @param aiolQuestionRepo
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "题库题目-分页列表查询")
@Operation(summary="题库题目-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolQuestionRepo>> queryPageList(AiolQuestionRepo aiolQuestionRepo,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolQuestionRepo> queryWrapper = QueryGenerator.initQueryWrapper(aiolQuestionRepo, req.getParameterMap());
Page<AiolQuestionRepo> page = new Page<AiolQuestionRepo>(pageNo, pageSize);
IPage<AiolQuestionRepo> pageList = aiolQuestionRepoService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolQuestionRepo
* @return
*/
@AutoLog(value = "题库题目-添加")
@Operation(summary="题库题目-添加")
@RequiresPermissions("aiol:aiol_question_repo:add")
@PostMapping(value = "/add")
@Transactional
public Result<String> add(@RequestBody AiolQuestionRepo aiolQuestionRepo) {
aiolQuestionRepoService.save(aiolQuestionRepo);
// 2. 获取题库ID
String repoId = aiolQuestionRepo.getRepoId();
// 3. 查询该题库
AiolRepo repo = aiolRepoService.getById(repoId);
if (repo != null) {
if(repo.getQuestionCount() != null){
repo.setQuestionCount(repo.getQuestionCount() + 1);
}else {
// 查询当前题库中的题目数量
List<String> questionIds = aiolQuestionRepoService.questionList(repoId);
int questionCount = questionIds.size();
// 更新题库的题目数量
repo.setQuestionCount(questionCount);
}
aiolRepoService.updateById(repo);
}
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param aiolQuestionRepo
* @return
*/
@AutoLog(value = "题库题目-编辑")
@Operation(summary="题库题目-编辑")
@RequiresPermissions("aiol:aiol_question_repo:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AiolQuestionRepo aiolQuestionRepo) {
aiolQuestionRepoService.updateById(aiolQuestionRepo);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "题库题目-通过id删除")
@Operation(summary="题库题目-通过id删除")
@RequiresPermissions("aiol:aiol_question_repo:delete")
@DeleteMapping(value = "/delete")
@Transactional
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
// 获取题库ID
String repoId = aiolQuestionRepoService.getById(id).getRepoId();
aiolQuestionRepoService.removeById(id);
// 查询该题库
AiolRepo repo = aiolRepoService.getById(repoId);
if (repo != null) {
if(repo.getQuestionCount() != null){
repo.setQuestionCount(repo.getQuestionCount() - 1);
}else {
// 查询当前题库中的题目数量
List<String> questionIds = aiolQuestionRepoService.questionList(repoId);
int questionCount = questionIds.size() - 1;
// 更新题库的题目数量
repo.setQuestionCount(questionCount);
}
aiolRepoService.updateById(repo);
}
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "题库题目-批量删除")
@Operation(summary="题库题目-批量删除")
@RequiresPermissions("aiol:aiol_question_repo:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.aiolQuestionRepoService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "题库题目-通过id查询")
@Operation(summary="题库题目-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolQuestionRepo> queryById(@RequestParam(name="id",required=true) String id) {
AiolQuestionRepo aiolQuestionRepo = aiolQuestionRepoService.getById(id);
if(aiolQuestionRepo==null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolQuestionRepo);
}
/**
* 导出excel
*
* @param request
* @param aiolQuestionRepo
*/
@RequiresPermissions("aiol:aiol_question_repo:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolQuestionRepo aiolQuestionRepo) {
return super.exportXls(request, aiolQuestionRepo, AiolQuestionRepo.class, "题库题目");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_question_repo:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolQuestionRepo.class);
}
}

View File

@ -0,0 +1,955 @@
package org.jeecg.modules.aiol.controller;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.modules.aiol.constant.EntityLinkConst;
import org.jeecg.modules.aiol.dto.*;
import org.jeecg.modules.aiol.entity.*;
import org.jeecg.modules.aiol.mapper.AiolRepoMapper;
import org.jeecg.modules.aiol.service.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.service.ISysUserService;
import org.jeecgframework.poi.excel.ExcelExportUtil;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 题库
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Tag(name = "题库")
@RestController
@RequestMapping("/aiol/aiolRepo")
@Slf4j
public class AiolRepoController extends JeecgController<AiolRepo, IAiolRepoService> {
@Autowired
private IAiolRepoService aiolRepoService;
@Autowired
private IAiolEntityPermissionService aiolEntityPermissionService;
@Autowired
private ISysBaseAPI sysBaseApi;
/**
* 分页列表查询
*
* @param aiolRepo
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "题库-分页列表查询")
@Operation(summary = "题库-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolRepo>> queryPageList(AiolRepo aiolRepo,
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolRepo> queryWrapper = QueryGenerator.initQueryWrapper(aiolRepo, req.getParameterMap());
Page<AiolRepo> page = new Page<AiolRepo>(pageNo, pageSize);
IPage<AiolRepo> pageList = aiolRepoService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolRepo
* @return
*/
@AutoLog(value = "题库-添加")
@Operation(summary = "题库-添加")
@RequiresPermissions("aiol:aiol_repo:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolRepo aiolRepo) {
aiolRepoService.save(aiolRepo);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param aiolRepo
* @return
*/
@AutoLog(value = "题库-编辑")
@Operation(summary = "题库-编辑")
@RequiresPermissions("aiol:aiol_repo:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
public Result<String> edit(@RequestBody AiolRepo aiolRepo) {
aiolRepoService.updateById(aiolRepo);
return Result.OK(aiolRepo.getId());
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "题库-通过id删除")
@Operation(summary = "题库-通过id删除")
@RequiresPermissions("aiol:aiol_repo:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name = "id", required = true) String id) {
aiolRepoService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "题库-批量删除")
@Operation(summary = "题库-批量删除")
@RequiresPermissions("aiol:aiol_repo:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
this.aiolRepoService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "题库-通过id查询")
@Operation(summary = "题库-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolRepo> queryById(@RequestParam(name = "id", required = true) String id) {
AiolRepo aiolRepo = aiolRepoService.getById(id);
if (aiolRepo == null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolRepo);
}
/**
* 导出excel
*
* @param request
* @param aiolRepo
*/
@RequiresPermissions("aiol:aiol_repo:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolRepo aiolRepo) {
return super.exportXls(request, aiolRepo, AiolRepo.class, "题库");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_repo:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolRepo.class);
}
@Autowired
private AiolRepoMapper repoMapper;
@Autowired
private IAiolQuestionRepoService questionRepoService;
@Autowired
private IAiolEntityLinkService entityLinkBizService;
@Autowired
private IAiolQuestionService questionService;
@Autowired
private IAiolQuestionOptionService questionOptionService;
@Autowired
private IAiolQuestionAnswerService questionAnswerService;
@Autowired
private ISysUserService sysUserService;
@PostMapping("course_add")
@Operation(summary = "课程下新建题库")
@Transactional
public Result<Integer> correct(@RequestBody Map<String, Object> data) {
String title = (String) data.get("title");
String remark = (String) data.get("remark");
String courseId = (String) data.get("courseId");
AiolRepo repo = new AiolRepo();
repo.setTitle(title);
repo.setRemark(remark);
repoMapper.insert(repo);
entityLinkBizService.save(EntityLinkConst.SourceType.COURSE, courseId, EntityLinkConst.TargetType.REPO, repo.getId());
return Result.OK(repo.getId());
}
@GetMapping("repoList")
@Operation(summary = "获取有权限的所有题库")
public Result<List<Map<String, Object>>> repoList(HttpServletRequest request) {
// 尝试获取token判断用户id
String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
if (token != null && !token.trim().isEmpty()) {
try {
String username = JwtUtil.getUsername(token);
LoginUser sysUser = sysBaseApi.getUserByName(username);
if (sysUser != null) {
// 获取用户有权限的题库ID列表
List<AiolEntityPermission> list = aiolEntityPermissionService.list(new QueryWrapper<AiolEntityPermission>()
.eq("user_id", sysUser.getId())
.eq("entity_type", "repo")
);
if (list.isEmpty()) {
return Result.error("该用户没有权限访问任意题库");
}
List<String> repoIds = list.stream().map(AiolEntityPermission::getEntityId).collect(Collectors.toList());
// 构建查询条件
QueryWrapper<AiolRepo> queryWrapper = new QueryWrapper<>();
queryWrapper.in("id", repoIds)
.select("aiol_repo.*", "(SELECT realname FROM sys_user WHERE username = aiol_repo.create_by) as createByName");
// 根据ID列表查询题库
List<Map<String, Object>> repoList = repoMapper.selectMaps(queryWrapper);
return Result.OK(repoList);
}
} catch (Exception e) {
// token无效或解析失败忽略错误继续执行原有逻辑
log.debug("Token解析失败按未登录用户处理: {}", e.getMessage());
}
}
return Result.error("请检查登录状态");
}
@PostMapping(value = "/courseAdd")
@Operation(summary = "课程新建题库")
@Transactional
public Result<String> courseAdd(@RequestBody AiolRepo repo,HttpServletRequest request) {
aiolRepoService.save(repo);
AiolEntityPermission aiolEntityPermission = new AiolEntityPermission();
aiolEntityPermission.setEntityId(repo.getId());
aiolEntityPermission.setEntityType("repo");
// 尝试获取token判断用户id
String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
if (token != null && !token.trim().isEmpty()) {
try {
String username = JwtUtil.getUsername(token);
LoginUser sysUser = sysBaseApi.getUserByName(username);
if (sysUser != null) {
aiolEntityPermission.setUserId(sysUser.getId());
}
} catch (Exception e) {
// token无效或解析失败忽略错误继续执行原有逻辑
log.debug("Token解析失败按未登录用户处理: {}", e.getMessage());
}
}
aiolEntityPermissionService.save(aiolEntityPermission);
return Result.OK(repo.getId());
}
@GetMapping("userList")
@Operation(summary = "获取用户")
public Result<List<UserPermission>> userList(
@RequestParam(required = false) String username,
@RequestParam(required = false) String employeeNumber,
@RequestParam String repoId) {
// 创建查询条件
LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
// 当用户名不为空时添加用户名查询条件模糊查询
if (StringUtils.isNotBlank(username)) {
queryWrapper.like(SysUser::getUsername, username);
}
// 当用户工号不为空时添加工号查询条件精确查询
if (StringUtils.isNotBlank(employeeNumber)) {
queryWrapper.eq(SysUser::getWorkNo, employeeNumber);
}
// 执行查询
List<SysUser> userList = sysUserService.list(queryWrapper);
// 获取有权限的用户列表
Result<List<UserPermission>> result = userListByRepoId(repoId);
List<UserPermission> userListByRepo = result != null ? result.getResult() : null;
// 创建ID到UserPermission的映射
Map<String, UserPermission> userPermissionMap;
if (userListByRepo == null || userListByRepo.isEmpty()) {
userPermissionMap = new HashMap<>();
} else {
userPermissionMap = userListByRepo.stream()
.collect(Collectors.toMap(
UserPermission::getUserId,
user -> user,
(existing, replacement) -> existing
));
}
List<UserPermission> userPermissionList = new ArrayList<>();
userList.forEach(user -> {
// 如果当前用户在用户列表中
if (userPermissionMap.containsKey(user.getId())) {
userPermissionList.add(userPermissionMap.get(user.getId()));
}else {
UserPermission userPermission = new UserPermission();
userPermission.setUserId(user.getId()); // 用户ID
userPermission.setUserName(user.getUsername()); // 用户名
userPermission.setRealName(user.getRealname()); // 用户真实姓名
userPermission.setUserAvatar(user.getAvatar()); // 用户头像
userPermission.setPermission(false);
userPermissionList.add(userPermission);
}
});
// 返回结果
return Result.ok(userPermissionList);
}
@PostMapping("addOrDeletePermission")
@Operation(summary = "添加权限")
@Transactional
public Result<String> addOrDeletePermission(@RequestParam String userId, @RequestParam String repoId,@RequestParam String type) {
AiolEntityPermission aiolEntityPermission = new AiolEntityPermission();
aiolEntityPermission.setEntityId(repoId);
aiolEntityPermission.setEntityType("repo");
aiolEntityPermission.setUserId(userId);
if(type.equals("add")){
aiolEntityPermissionService.saveOrUpdate(aiolEntityPermission);
return Result.OK("添加成功");
}
else if(type.equals("delete")){
aiolEntityPermissionService.remove(new QueryWrapper<>(aiolEntityPermission));
return Result.OK("删除成功");
}
return Result.OK();
}
//获取有权限的用户
@GetMapping("userListByRepoId/{repoId}")
@Operation(summary = "获取题库有权限的用户列表")
public Result<List<UserPermission>> userListByRepoId(@PathVariable String repoId) {
// 获取用户有权限的题库ID列表
List<AiolEntityPermission> list = aiolEntityPermissionService.list(new QueryWrapper<AiolEntityPermission>()
.eq("entity_id", repoId)
.eq("entity_type", "repo")
);
if (list.isEmpty()) {
return Result.ok();
}
List<String> userIds = list.stream().map(AiolEntityPermission::getUserId).collect(Collectors.toList());
List<SysUser> sysUsers = sysUserService.listByIds(userIds);
List<UserPermission> userPermissionList = new ArrayList<>();
sysUsers.forEach(user -> {
if (user != null) {
UserPermission userPermission = new UserPermission();
userPermission.setUserId(user.getId()); // 用户ID
userPermission.setUserName(user.getUsername()); // 用户名
userPermission.setRealName(user.getRealname()); // 用户真实姓名
userPermission.setUserAvatar(user.getAvatar()); // 用户头像
userPermission.setPermission(true);
userPermissionList.add(userPermission);
}
});
// 根据ID列表查询用户
return Result.ok(userPermissionList);
}
@GetMapping("/questionList/{repoId}")
@Operation(summary = "查询题库下题目")
public Result<List<QuestionWithCreator>> questionList(@PathVariable String repoId) {
// 获取题库中的题目ID列表
List<String> repoQuestionIds = questionRepoService.questionList(repoId);
if (repoQuestionIds.isEmpty()) {
return Result.error("该题库下没有题目或该题库不存在");
}
// 根据ID列表查询题目
List<AiolQuestion> questions = questionService.listByIds(repoQuestionIds);
if (questions.isEmpty()) {
return Result.error("题目不存在");
}
//获取所有创建人id
List<String> createIds = questions.stream()
.map(AiolQuestion::getCreateBy)
.collect(Collectors.toSet())
.stream()
.collect(Collectors.toList());
// 根据ID列表查询用户信息
Map<String, String> createIdToUsernameMap = new HashMap<>();
if (!createIds.isEmpty()) {
List<SysUser> users = sysUserService.list(
new LambdaQueryWrapper<SysUser>()
.in(SysUser::getUsername, createIds)
);
// 创建ID到用户名的映射
createIdToUsernameMap = users.stream()
.collect(Collectors.toMap(
SysUser::getUsername,
SysUser::getRealname,
(existing, replacement) -> existing
));
}
//获取复选题id
List<String> type5Ids = questions.stream()
.filter(question -> question.getType() == 5)
.map(AiolQuestion::getId)
.collect(Collectors.toList());
//获取复选题所包含的题目
List<AiolQuestion> type5Questions = new ArrayList<>();
if (!type5Ids.isEmpty()) {
type5Questions = questionService.list(
new LambdaQueryWrapper<AiolQuestion>()
.in(AiolQuestion::getParentId, type5Ids)
);
}
// 创建一个映射用于快速查找父ID对应的子题目
Map<String, List<AiolQuestion>> parentToChildrenMap = type5Questions.stream()
.collect(Collectors.groupingBy(AiolQuestion::getParentId));
// 构建结果列表
List<QuestionWithCreator> resultQuestions = new ArrayList<>();
for (AiolQuestion question : questions) {
// 创建扩展对象
QuestionWithCreator questionWithCreator = new QuestionWithCreator();
BeanUtils.copyProperties(question, questionWithCreator);
// 设置创建人用户名
String createBy = question.getCreateBy();
if (StringUtils.isNotBlank(createBy)) {
questionWithCreator.setCreateByName(createIdToUsernameMap.getOrDefault(createBy, ""));
} else {
questionWithCreator.setCreateByName("");
}
resultQuestions.add(questionWithCreator);
if (question.getType() == 5) {
List<AiolQuestion> children = parentToChildrenMap.getOrDefault(question.getId(), Collections.emptyList());
for (AiolQuestion child : children) {
// 创建子题目的扩展对象
QuestionWithCreator childWithCreator = new QuestionWithCreator();
BeanUtils.copyProperties(child, childWithCreator);
// 设置子题目的创建人用户名
String childCreateBy = child.getCreateBy();
if (StringUtils.isNotBlank(childCreateBy)) {
childWithCreator.setCreateByName(createIdToUsernameMap.getOrDefault(childCreateBy, ""));
} else {
childWithCreator.setCreateByName("");
}
resultQuestions.add(childWithCreator);
}
}
}
return Result.ok(resultQuestions);
}
@GetMapping("course_list")
@Operation(summary = "获取课程下的题库")
public Result<List<AiolRepo>> courseList(@RequestParam String courseId) {
List<String> targetIds = entityLinkBizService.listTargetIds(EntityLinkConst.SourceType.COURSE, courseId, EntityLinkConst.TargetType.REPO);
if (targetIds.size() > 0) {
List<AiolRepo> list = repoMapper.selectByIds(targetIds);
return Result.OK(list);
} else {
return Result.OK(new ArrayList<>());
}
}
@GetMapping("/repoList/{questionId}")
@Operation(summary = "查询题目详情")
public Result<?> questionDetail(@PathVariable String questionId) {
AiolQuestion rootQuestion = questionService.getById(questionId);
if (rootQuestion == null) {
return Result.error("题目不存在");
}
QuestionAnswerDTO questionAnswerDTO = new QuestionAnswerDTO();
questionAnswerDTO.setQuestion(rootQuestion);
if (rootQuestion.getType() >= 0 && rootQuestion.getType() <= 2) {
questionAnswerDTO.setAnswer(choiceDetail(questionId));
return Result.ok(questionAnswerDTO);
} else if (rootQuestion.getType() == 3 || rootQuestion.getType() == 4) {
questionAnswerDTO.setAnswer(answerDetail(questionId));
return Result.ok(questionAnswerDTO);
} else {
//查询复合题所包含的题目
List<AiolQuestion> list = questionService.list(
new LambdaQueryWrapper<AiolQuestion>().
eq(AiolQuestion::getParentId, questionId)
);
//根据题目类型进行分组false选择多选判断题true填空简答题
Map<Boolean, List<AiolQuestion>> groupedQuestions = list.stream()
.collect(Collectors.partitioningBy(
q -> q.getType() > 2
));
//获取选择题多选题判断题答案
List<AiolQuestion> question = groupedQuestions.get(false);
if (!question.isEmpty()) {
question.forEach(q -> {
QuestionAnswerDTO qad = new QuestionAnswerDTO();
qad.setQuestion(q);
qad.setAnswer(choiceDetail(q.getId()));
questionAnswerDTO.getChildren().add(qad);
});
}
//获取填空题简答题答案
List<AiolQuestion> question1 = groupedQuestions.get(true);
if (!question1.isEmpty()) {
question1.forEach(q -> {
QuestionAnswerDTO qad = new QuestionAnswerDTO();
qad.setQuestion(q);
qad.setAnswer(answerDetail(q.getId()));
questionAnswerDTO.getChildren().add(qad);
});
}
return Result.ok(questionAnswerDTO);
}
}
//题库题目excel导出
@GetMapping("/exportXls")
@Operation(summary = "题库题目excel导出")
public void exportXls(@RequestParam(name = "repoId", required = true) String repoId, HttpServletResponse response) {
try {
// 1. 获取题库信息
AiolRepo repo = aiolRepoService.getById(repoId);
if (repo == null) {
throw new RuntimeException("题库不存在");
}
// 2. 获取题库下的所有题目
List<AiolQuestion> questions = questionService.list(new LambdaQueryWrapper<AiolQuestion>()
.inSql(AiolQuestion::getId, "SELECT question_id FROM aiol_question_repo WHERE repo_id = '" + repoId + "'"));
List<AiolQuestion> questionList = new ArrayList<>();
if (questions.isEmpty()) {
throw new RuntimeException("题库下没有题目");
} else {
//获取复合题id
List<String> parentIds = questions.stream().filter(q -> q.getType() == 5).map(AiolQuestion::getId).collect(Collectors.toList());
//获取复合题下的子题
if (!parentIds.isEmpty()) {
List<AiolQuestion> childQuestions = questionService.list(new LambdaQueryWrapper<AiolQuestion>().in(AiolQuestion::getParentId, parentIds));
//将子题目放到复合题后面
// 创建一个映射用于快速查找父ID对应的子题目
Map<String, List<AiolQuestion>> parentToChildrenMap = childQuestions.stream()
.collect(Collectors.groupingBy(AiolQuestion::getParentId));
// 将子题目添加到原始列表中保持原有顺序
for (AiolQuestion question : questions) {
questionList.add(question);
if (question.getType() == 5) {
List<AiolQuestion> children = parentToChildrenMap.getOrDefault(question.getId(), Collections.emptyList());
questionList.addAll(children);
}
}
} else {
questionList.addAll(questions);
}
}
// 3. 转换为ExcelDTO
List<QuestionExcelDTO> dtoList = new ArrayList<>();
for (AiolQuestion question : questionList) {
QuestionExcelDTO dto = new QuestionExcelDTO();
dto.setQuestionId(question.getId());
dto.setContent(question.getContent());
dto.setAnalysis(question.getAnalysis());
dto.setScore(Double.valueOf(question.getScore()));
// 设置题目类型
switch (question.getType()) {
case 0:
dto.setType("单选");
break;
case 1:
dto.setType("多选");
break;
case 2:
dto.setType("判断");
break;
case 3:
dto.setType("填空");
break;
case 4:
dto.setType("简答");
break;
case 5:
dto.setType("复合题");
break;
}
// 设置难度
switch (question.getDifficulty()) {
case 0:
dto.setDifficulty("简单");
break;
case 1:
dto.setDifficulty("中等");
break;
case 2:
dto.setDifficulty("困难");
break;
}
// 设置程度
switch (question.getDegree()) {
case 0:
dto.setDegree("了解");
break;
case 1:
dto.setDegree("熟悉");
break;
case 2:
dto.setDegree("掌握");
break;
}
// 设置能力
switch (question.getAbility()) {
case 0:
dto.setAbility("识记");
break;
case 1:
dto.setAbility("理解");
break;
case 2:
dto.setAbility("应用");
break;
}
// 设置选项和答案
if (question.getType() >= 0 && question.getType() <= 2) {
// 选择题多选题判断题
List<AiolQuestionOption> options = questionOptionService.list(
new LambdaQueryWrapper<AiolQuestionOption>()
.eq(AiolQuestionOption::getQuestionId, question.getId())
.orderByAsc(AiolQuestionOption::getOrderNo)
);
// 设置选项
for (int i = 0; i < options.size(); i++) {
AiolQuestionOption option = options.get(i);
char optionLabel = (char) ('A' + i);
switch (optionLabel) {
case 'A':
dto.setOptionA(option.getContent());
break;
case 'B':
dto.setOptionB(option.getContent());
break;
case 'C':
dto.setOptionC(option.getContent());
break;
case 'D':
dto.setOptionD(option.getContent());
break;
case 'E':
dto.setOptionE(option.getContent());
break;
}
}
// 设置正确答案
StringBuilder correctAnswers = new StringBuilder();
for (AiolQuestionOption option : options) {
if (option.getIzCorrent() == 1) {
char optionLabel = (char) ('A' + (option.getOrderNo() - 1));
if (!correctAnswers.isEmpty()) correctAnswers.append(",");
correctAnswers.append(optionLabel);
}
}
dto.setCorrectAnswers(correctAnswers.toString());
} else if (question.getType() == 3 || question.getType() == 4) {
// 填空题简答题
List<AiolQuestionAnswer> answers = questionAnswerService.list(
new LambdaQueryWrapper<AiolQuestionAnswer>()
.eq(AiolQuestionAnswer::getQuestionId, question.getId())
.orderByAsc(AiolQuestionAnswer::getOrderNo)
);
StringBuilder answerTexts = new StringBuilder();
int orderNo = 1;
for (AiolQuestionAnswer answer : answers) {
if (!answerTexts.isEmpty()) {
if (orderNo != answer.getOrderNo()) {
answerTexts.append(",");
orderNo = answer.getOrderNo();
} else {
answerTexts.append("|");
}
}
answerTexts.append(answer.getAnswerText());
}
dto.setCorrectAnswers(answerTexts.toString());
}
dtoList.add(dto);
}
// 设置响应头
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode(repo.getTitle() + "_题目导出", "UTF-8").replaceAll("\\+", "%");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
// 使用AutoPoi创建Workbook
ExportParams exportParams = new ExportParams("题目列表", "题库题目数据");
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, QuestionExcelDTO.class, dtoList);
// 写入响应流
workbook.write(response.getOutputStream());
// 关闭资源
workbook.close();
response.getOutputStream().flush();
response.getOutputStream().close();
} catch (Exception e) {
log.error("导出题库题目失败", e);
throw new RuntimeException("导出题库题目失败:" + e.getMessage());
}
}
@PostMapping("/importXls")
@Operation(summary = "题库题目excel导入")
@Transactional
public Result<String> importXls(@RequestParam(name = "file", required = true) MultipartFile file,
@RequestParam(name = "repoId", required = true) String repoId) {
try {
// 1. 验证题库是否存在
AiolRepo repo = aiolRepoService.getById(repoId);
if (repo == null) {
return Result.error("题库不存在");
}
// 2. 读取Excel文件
ImportParams params = new ImportParams();
params.setTitleRows(1); // 标题行数
params.setHeadRows(1); // 表头行数
List<QuestionExcelDTO> dtoList = ExcelImportUtil.importExcel(file.getInputStream(), QuestionExcelDTO.class, params);
if (CollectionUtils.isEmpty(dtoList)) {
return Result.error("Excel文件为空或格式不正确");
}
// 3. 处理导入数据
List<AiolQuestion> questions = new ArrayList<>();
List<AiolQuestionOption> options = new ArrayList<>();
List<AiolQuestionAnswer> answers = new ArrayList<>();
List<AiolQuestionRepo> questionRepos = new ArrayList<>();
String parentId = null;
for (QuestionExcelDTO dto : dtoList) {
// 3.1 创建题目
AiolQuestion question = new AiolQuestion();
question.setId(String.valueOf(IdWorker.getId()));
question.setContent(dto.getContent());
question.setAnalysis(dto.getAnalysis());
question.setScore(dto.getScore().intValue());
if (StringUtils.isNotBlank(dto.getParentId()) && dto.getParentId().equals("^")) {
question.setParentId(parentId);
} else {
parentId = question.getId();
}
System.out.println(dto.getType());
// 设置题目类型
switch (dto.getType()) {
case "单选":
question.setType(0);
break;
case "多选":
question.setType(1);
break;
case "判断":
question.setType(2);
break;
case "填空":
question.setType(3);
break;
case "简答":
question.setType(4);
break;
case "复合题":
question.setType(5);
break;
default:
throw new RuntimeException("题目类型不正确:" + dto.getType());
}
// 设置难度
switch (dto.getDifficulty()) {
case "简单":
question.setDifficulty(0);
break;
case "中等":
question.setDifficulty(1);
break;
case "困难":
question.setDifficulty(2);
break;
}
// 设置程度
switch (dto.getDegree()) {
case "了解":
question.setDegree(0);
break;
case "熟悉":
question.setDegree(1);
break;
case "掌握":
question.setDegree(2);
break;
}
// 设置能力
switch (dto.getAbility()) {
case "识记":
question.setAbility(0);
break;
case "理解":
question.setAbility(1);
break;
case "应用":
question.setAbility(2);
break;
}
questions.add(question);
// 3.2 处理选项和答案
if (question.getType() >= 0 && question.getType() <= 2) {
// 选择题多选题判断题
String correctAnswers = dto.getCorrectAnswers();
if (StringUtils.isEmpty(correctAnswers)) {
return Result.error("题目 {} 缺少正确答案", dto.getContent());
}
String[] answerLabels = correctAnswers.split(",");
List<String> optionContents = Arrays.asList(
dto.getOptionA(),
dto.getOptionB(),
dto.getOptionC(),
dto.getOptionD(),
dto.getOptionE()
).stream()
.filter(Objects::nonNull)
.collect(Collectors.toList());
for (int i = 0; i < optionContents.size(); i++) {
AiolQuestionOption option = new AiolQuestionOption();
option.setQuestionId(question.getId());
option.setContent(optionContents.get(i));
option.setOrderNo(i + 1);
// 检查是否为正确答案
boolean isCorrect = Arrays.asList(answerLabels)
.contains(String.valueOf((char) ('A' + i)));
option.setIzCorrent(isCorrect ? 1 : 0);
options.add(option);
}
} else if (question.getType() == 3 || question.getType() == 4) {
// 填空题简答题
String correctAnswers = dto.getCorrectAnswers();
if (StringUtils.isEmpty(correctAnswers)) {
return Result.error("题目 {} 缺少正确答案", dto.getContent());
}
String[] answerTexts = correctAnswers.split(",");
for (int i = 0; i < answerTexts.length; i++) {
AiolQuestionAnswer answer = new AiolQuestionAnswer();
answer.setQuestionId(question.getId());
answer.setAnswerText(answerTexts[i]);
answer.setOrderNo(i + 1);
answers.add(answer);
}
}
// 3.3 创建题库题目关联关系
AiolQuestionRepo questionRepo = new AiolQuestionRepo();
questionRepo.setRepoId(repoId);
questionRepo.setQuestionId(question.getId());
questionRepos.add(questionRepo);
}
// 4. 批量保存数据
questionService.saveBatch(questions);
questionOptionService.saveBatch(options);
questionAnswerService.saveBatch(answers);
questionRepoService.saveBatch(questionRepos);
// 5. 更新题库题目数量
repo.setQuestionCount(repo.getQuestionCount() != null ? repo.getQuestionCount()+questionRepos.size():questionRepos.size());
aiolRepoService.updateById(repo);
return Result.OK("导入成功");
} catch (Exception e) {
log.error("导入题库题目失败", e);
return Result.error("导入失败:" + e.getMessage());
}
}
//查询选择题多选题判断题答案
public List<AiolQuestionOption> choiceDetail(String questionId) {
return questionOptionService.list(
new LambdaQueryWrapper<AiolQuestionOption>().
eq(AiolQuestionOption::getQuestionId, questionId).
orderByAsc(AiolQuestionOption::getOrderNo)
);
}
//查询填空题简答题答案
public List<AiolQuestionAnswer> answerDetail(String questionId) {
return questionAnswerService.list(
new LambdaQueryWrapper<AiolQuestionAnswer>().
eq(AiolQuestionAnswer::getQuestionId, questionId).
orderByAsc(AiolQuestionAnswer::getOrderNo)
);
}
}

View File

@ -0,0 +1,219 @@
package org.jeecg.modules.aiol.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.aiol.entity.AiolResourceContent;
import org.jeecg.modules.aiol.service.IAiolResourceContentService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.jeecg.config.shiro.IgnoreAuth;
/**
* @Description: 资源内容
* @Author: jeecg-boot
* @Date: 2025-09-23
* @Version: V1.0
*/
@Tag(name="资源内容")
@RestController
@RequestMapping("/aiol/aiolResourceContent")
@Slf4j
public class AiolResourceContentController extends JeecgController<AiolResourceContent, IAiolResourceContentService> {
@Autowired
private IAiolResourceContentService aiolResourceContentService;
/**
* 分页列表查询
*
* @param aiolResourceContent
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "资源内容-分页列表查询")
@Operation(summary="资源内容-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolResourceContent>> queryPageList(AiolResourceContent aiolResourceContent,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolResourceContent> queryWrapper = QueryGenerator.initQueryWrapper(aiolResourceContent, req.getParameterMap());
Page<AiolResourceContent> page = new Page<AiolResourceContent>(pageNo, pageSize);
IPage<AiolResourceContent> pageList = aiolResourceContentService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolResourceContent
* @return
*/
@AutoLog(value = "资源内容-添加")
@Operation(summary="资源内容-添加")
@RequiresPermissions("aiol:aiol_resource_content:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolResourceContent aiolResourceContent) {
aiolResourceContentService.save(aiolResourceContent);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param aiolResourceContent
* @return
*/
@AutoLog(value = "资源内容-编辑")
@Operation(summary="资源内容-编辑")
@RequiresPermissions("aiol:aiol_resource_content:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AiolResourceContent aiolResourceContent) {
aiolResourceContentService.updateById(aiolResourceContent);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "资源内容-通过id删除")
@Operation(summary="资源内容-通过id删除")
@RequiresPermissions("aiol:aiol_resource_content:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
aiolResourceContentService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "资源内容-批量删除")
@Operation(summary="资源内容-批量删除")
@RequiresPermissions("aiol:aiol_resource_content:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.aiolResourceContentService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "资源内容-通过id查询")
@Operation(summary="资源内容-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolResourceContent> queryById(@RequestParam(name="id",required=true) String id) {
AiolResourceContent aiolResourceContent = aiolResourceContentService.getById(id);
if(aiolResourceContent==null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolResourceContent);
}
/**
* 查询视频总结
* 根据资源ID在 aiol_resource_content 表查询 content_type=video_summary 的记录
*/
@Operation(summary = "查询视频总结", description = "根据资源ID查询content_type=video_summary的资源内容")
@IgnoreAuth
@GetMapping(value = "/video_summary")
public Result<AiolResourceContent> queryVideoSummary(@RequestParam(name = "resourceId") String resourceId) {
try {
QueryWrapper<AiolResourceContent> wrapper = new QueryWrapper<>();
wrapper.eq("resource_id", resourceId).eq("content_type", "video_summary");
// 存在多条时仅取第一条避免抛异常
AiolResourceContent record = aiolResourceContentService.getOne(wrapper, false);
return Result.OK(record);
} catch (Exception e) {
log.error("查询视频总结失败: resourceId={}, error={}", resourceId, e.getMessage(), e);
return Result.error("查询视频总结失败: " + e.getMessage());
}
}
@Operation(summary = "查询视频字幕", description = "根据资源ID查询content_type=video_subtitle的资源内容")
@IgnoreAuth
@GetMapping(value = "/video_subtitle")
public Result<AiolResourceContent> queryVideoSubtitle(@RequestParam(name = "resourceId") String resourceId) {
try {
QueryWrapper<AiolResourceContent> wrapper = new QueryWrapper<>();
wrapper.eq("resource_id", resourceId).eq("content_type", "video_subtitle");
// 存在多条时仅取第一条避免抛异常
AiolResourceContent record = aiolResourceContentService.getOne(wrapper, false);
return Result.OK(record);
} catch (Exception e) {
log.error("查询视频字幕失败: resourceId={}, error={}", resourceId, e.getMessage(), e);
return Result.error("查询视频字幕失败: " + e.getMessage());
}
}
/**
* 导出excel
*
* @param request
* @param aiolResourceContent
*/
@RequiresPermissions("aiol:aiol_resource_content:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolResourceContent aiolResourceContent) {
return super.exportXls(request, aiolResourceContent, AiolResourceContent.class, "资源内容");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_resource_content:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolResourceContent.class);
}
}

View File

@ -0,0 +1,585 @@
package org.jeecg.modules.aiol.controller;
import java.util.*;
import java.util.stream.Collectors;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.common.util.MinioUtil;
import org.jeecg.common.util.oss.OssBootUtil;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.config.shiro.IgnoreAuth;
import org.jeecg.modules.aiol.constant.EntityLinkConst;
import org.jeecg.modules.aiol.constant.ResourceTypeConst;
import org.jeecg.modules.aiol.entity.AiolResource;
import org.jeecg.modules.aiol.service.IAiolEntityLinkService;
import org.jeecg.modules.aiol.service.IAiolResourceService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import ws.schild.jave.MultimediaObject;
import ws.schild.jave.info.MultimediaInfo;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 资源
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Tag(name = "资源")
@RestController
@RequestMapping("/aiol/aiolResource")
@Slf4j
public class AiolResourceController extends JeecgController<AiolResource, IAiolResourceService> {
@Autowired
private IAiolResourceService aiolResourceService;
/**
* 分页列表查询
*
* @param aiolResource
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "资源-分页列表查询")
@Operation(summary = "资源-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolResource>> queryPageList(AiolResource aiolResource,
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolResource> queryWrapper = QueryGenerator.initQueryWrapper(aiolResource, req.getParameterMap());
Page<AiolResource> page = new Page<AiolResource>(pageNo, pageSize);
IPage<AiolResource> pageList = aiolResourceService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolResource
* @return
*/
@AutoLog(value = "资源-添加")
@Operation(summary = "资源-添加")
@RequiresPermissions("aiol:aiol_resource:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolResource aiolResource) {
aiolResourceService.save(aiolResource);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param aiolResource
* @return
*/
@AutoLog(value = "资源-编辑")
@Operation(summary = "资源-编辑")
@RequiresPermissions("aiol:aiol_resource:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
public Result<String> edit(@RequestBody AiolResource aiolResource) {
aiolResourceService.updateById(aiolResource);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "资源-通过id删除")
@Operation(summary = "资源-通过id删除")
@RequiresPermissions("aiol:aiol_resource:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name = "id", required = true) String id) {
aiolResourceService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "资源-批量删除")
@Operation(summary = "资源-批量删除")
@RequiresPermissions("aiol:aiol_resource:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
this.aiolResourceService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "资源-通过id查询")
@Operation(summary = "资源-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolResource> queryById(@RequestParam(name = "id", required = true) String id) {
AiolResource aiolResource = aiolResourceService.getById(id);
if (aiolResource == null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolResource);
}
/**
* 导出excel
*
* @param request
* @param aiolResource
*/
@RequiresPermissions("aiol:aiol_resource:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolResource aiolResource) {
return super.exportXls(request, aiolResource, AiolResource.class, "资源");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_resource:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolResource.class);
}
@Autowired
private IAiolEntityLinkService entityLinkBizService;
@GetMapping("/feature")
@Operation(summary = "查询精品资源")
@IgnoreAuth
public Result<List<AiolResource>> queryFeatureResource() {
List<AiolResource> list = aiolResourceService.list(new QueryWrapper<AiolResource>().eq("iz_featured", 1));
return Result.OK(list);
}
@GetMapping("/video")
@Operation(summary = "按课程分类查询视频资源")
@IgnoreAuth
public Result<List<AiolResource>> queryVideoResource(@RequestParam("categoryId") String courseCategoryId) {
List<String> targetIds = entityLinkBizService.listTargetIds(EntityLinkConst.SourceType.COURSE_CATEGORY, courseCategoryId, EntityLinkConst.TargetType.RESOURCE);
List<AiolResource> list = new ArrayList<>();
for (String targetId : targetIds) {
AiolResource resource = aiolResourceService.getById(targetId);
if (resource.getType() == EntityLinkConst.ResourceType.VIDEO) {
list.add(resource);
}
}
return Result.OK(list);
}
@GetMapping("/image")
@Operation(summary = "按课程分类查询图片资源")
@IgnoreAuth
public Result<List<AiolResource>> queryImageResource(@RequestParam("categoryId") String courseCategoryId) {
List<String> targetIds = entityLinkBizService.listTargetIds(EntityLinkConst.SourceType.COURSE_CATEGORY, courseCategoryId, EntityLinkConst.TargetType.RESOURCE);
List<AiolResource> list = new ArrayList<>();
for (String targetId : targetIds) {
AiolResource resource = aiolResourceService.getById(targetId);
if (resource.getType() == EntityLinkConst.ResourceType.IMAGE) {
list.add(resource);
}
}
return Result.OK(list);
}
@PostMapping("/video_upload")
@Operation(summary = "课程视频文件上传", description = "课程视频文件上传,创建资源记录并关联到课程")
public Result<AiolResource> upload(
@RequestParam("file") MultipartFile file,
@RequestParam("courseId") String courseId,
@RequestParam("name") String name,
@RequestParam(value = "description", required = false) String description,
HttpServletRequest request) throws Exception {
if (file == null || file.isEmpty()) {
return Result.error("没有找到上传的文件");
}
if (courseId == null || courseId.trim().isEmpty()) {
return Result.error("课程ID不能为空");
}
if (name == null || name.trim().isEmpty()) {
return Result.error("视频名称不能为空");
}
try {
// 1. 上传视频文件获取各清晰度的m3u8地址
Map<String, String> qualityUrls = aiolResourceService.uploadHls(file, request);
// 2. 将m3u8地址用逗号拼接成字符串
String fileUrl = String.join(",", qualityUrls.values());
// 3. TODO 获取视频时长和文件大小
Integer duration = (int) 0;
Integer fileSize = (int) file.getSize();
// 4. 创建资源记录
AiolResource resource = new AiolResource();
resource.setName(name.trim());
resource.setDescription(description != null ? description.trim() : "");
resource.setType(ResourceTypeConst.VIDEO);
resource.setFileUrl(fileUrl);
resource.setDuration(duration);
resource.setFileSize(fileSize);
// 5. 保存资源记录
boolean saved = aiolResourceService.save(resource);
if (!saved) {
return Result.error("保存资源记录失败");
}
// 6. 创建课程与资源的关联关系
entityLinkBizService.save(
EntityLinkConst.SourceType.COURSE,
courseId,
EntityLinkConst.TargetType.RESOURCE,
resource.getId()
);
log.info("视频上传成功: resourceId={}, courseId={}, name={}",
resource.getId(), courseId, name);
return Result.OK(resource);
} catch (Exception e) {
log.error("视频上传失败: courseId={}, name={}, error={}",
courseId, name, e.getMessage(), e);
return Result.error("视频上传失败: " + e.getMessage());
}
}
@PostMapping("/document_upload")
@Operation(summary = "课程文档文件上传", description = "上传文档文件并关联到课程支持word、ppt、excel、pdf、txt、markdown等格式")
public Result<AiolResource> uploadDocument(
@RequestParam("file") MultipartFile file,
@RequestParam("courseId") String courseId,
@RequestParam("name") String name,
@RequestParam(value = "description", required = false) String description,
HttpServletRequest request) throws Exception {
if (file == null || file.isEmpty()) {
return Result.error("没有找到上传的文件");
}
if (courseId == null || courseId.trim().isEmpty()) {
return Result.error("课程ID不能为空");
}
if (name == null || name.trim().isEmpty()) {
return Result.error("文档名称不能为空");
}
// 检查文件类型
String originalFilename = file.getOriginalFilename();
if (originalFilename == null || !isValidDocumentType(originalFilename)) {
return Result.error("不支持的文件类型仅支持word、ppt、excel、pdf、txt、markdown等格式");
}
try {
// 1. 上传文档文件
String fileUrl = uploadDocumentFile(file, request);
// 2. 获取文件大小
Integer fileSize = (int) file.getSize();
// 3. 创建资源记录
AiolResource resource = new AiolResource();
resource.setName(name.trim());
resource.setDescription(description != null ? description.trim() : "");
resource.setType(ResourceTypeConst.DOCUMENT); // 2:文档
resource.setFileUrl(fileUrl);
resource.setFileSize(fileSize);
// 4. 保存资源记录
boolean saved = aiolResourceService.save(resource);
if (!saved) {
return Result.error("保存资源记录失败");
}
// 5. 创建课程与资源的关联关系
entityLinkBizService.save(
EntityLinkConst.SourceType.COURSE,
courseId,
EntityLinkConst.TargetType.RESOURCE,
resource.getId()
);
log.info("文档上传成功: resourceId={}, courseId={}, name={}",
resource.getId(), courseId, name);
return Result.OK(resource);
} catch (Exception e) {
log.error("文档上传失败: courseId={}, name={}, error={}",
courseId, name, e.getMessage(), e);
return Result.error("文档上传失败: " + e.getMessage());
}
}
@PostMapping("/image_upload")
@Operation(summary = "课程图片文件上传", description = "上传图片文件并关联到课程支持jpg、jpeg、png、gif、bmp、webp等格式")
public Result<AiolResource> uploadImage(
@RequestParam("file") MultipartFile file,
@RequestParam("courseId") String courseId,
@RequestParam("name") String name,
@RequestParam(value = "description", required = false) String description,
HttpServletRequest request) throws Exception {
if (file == null || file.isEmpty()) {
return Result.error("没有找到上传的文件");
}
if (courseId == null || courseId.trim().isEmpty()) {
return Result.error("课程ID不能为空");
}
if (name == null || name.trim().isEmpty()) {
return Result.error("图片名称不能为空");
}
// 检查文件类型
String originalFilename = file.getOriginalFilename();
if (originalFilename == null || !isValidImageType(originalFilename)) {
return Result.error("不支持的文件类型仅支持jpg、jpeg、png、gif、bmp、webp等格式");
}
try {
// 1. 上传图片文件
String fileUrl = uploadImageFile(file, request);
// 2. 获取文件大小
Integer fileSize = (int) file.getSize();
// 3. 创建资源记录
AiolResource resource = new AiolResource();
resource.setName(name.trim());
resource.setDescription(description != null ? description.trim() : "");
resource.setType(ResourceTypeConst.IMAGE); // 1:图片
resource.setFileUrl(fileUrl);
resource.setFileSize(fileSize);
// 4. 保存资源记录
boolean saved = aiolResourceService.save(resource);
if (!saved) {
return Result.error("保存资源记录失败");
}
// 5. 创建课程与资源的关联关系
entityLinkBizService.save(
EntityLinkConst.SourceType.COURSE,
courseId,
EntityLinkConst.TargetType.RESOURCE,
resource.getId()
);
log.info("图片上传成功: resourceId={}, courseId={}, name={}",
resource.getId(), courseId, name);
return Result.OK(resource);
} catch (Exception e) {
log.error("图片上传失败: courseId={}, name={}, error={}",
courseId, name, e.getMessage(), e);
return Result.error("图片上传失败: " + e.getMessage());
}
}
@GetMapping("/course_materials")
@Operation(summary = "查询课程课件", description = "根据课程ID、资源分类和资源名关键词查询课程相关资源")
@IgnoreAuth
public Result<List<AiolResource>> queryCourseMaterials(
@RequestParam("courseId") String courseId,
@RequestParam(value = "resourceType", required = false) Integer resourceType,
@RequestParam(value = "name", required = false) String name) {
try {
// 1. 根据课程ID查询关联的资源ID列表
List<String> resourceIds = entityLinkBizService.listTargetIds(
EntityLinkConst.SourceType.COURSE,
courseId,
EntityLinkConst.TargetType.RESOURCE
);
if (resourceIds.isEmpty()) {
return Result.OK(new ArrayList<>());
}
// 2. 根据资源ID列表查询资源详情
QueryWrapper<AiolResource> queryWrapper = new QueryWrapper<>();
queryWrapper.in("id", resourceIds);
// 3. 根据资源分类过滤
if (resourceType != null) {
queryWrapper.eq("type", resourceType);
}
// 4. 根据资源名关键词过滤
if (name != null && !name.trim().isEmpty()) {
queryWrapper.like("name", name.trim());
}
List<AiolResource> resourceList = aiolResourceService.list(queryWrapper);
log.info("查询课程课件成功: courseId={}, resourceType={}, name={}, 结果数量={}",
courseId, resourceType, name, resourceList.size());
return Result.OK(resourceList);
} catch (Exception e) {
log.error("查询课程课件失败: courseId={}, resourceType={}, name={}, error={}",
courseId, resourceType, name, e.getMessage(), e);
return Result.error("查询课程课件失败: " + e.getMessage());
}
}
/**
* 检查是否为有效的文档类型
*/
private boolean isValidDocumentType(String filename) {
if (filename == null) return false;
String lowerFilename = filename.toLowerCase();
return lowerFilename.endsWith(".doc") || lowerFilename.endsWith(".docx") || // Word
lowerFilename.endsWith(".ppt") || lowerFilename.endsWith(".pptx") || // PowerPoint
lowerFilename.endsWith(".xls") || lowerFilename.endsWith(".xlsx") || // Excel
lowerFilename.endsWith(".pdf") || // PDF
lowerFilename.endsWith(".txt") || // Text
lowerFilename.endsWith(".md") || lowerFilename.endsWith(".markdown"); // Markdown
}
/**
* 检查是否为有效的图片类型
*/
private boolean isValidImageType(String filename) {
if (filename == null) return false;
String lowerFilename = filename.toLowerCase();
return lowerFilename.endsWith(".jpg") || lowerFilename.endsWith(".jpeg") || // JPEG
lowerFilename.endsWith(".png") || // PNG
lowerFilename.endsWith(".gif") || // GIF
lowerFilename.endsWith(".bmp") || // BMP
lowerFilename.endsWith(".webp"); // WebP
}
/**
* 上传文档文件
*/
private String uploadDocumentFile(MultipartFile file, HttpServletRequest request) throws Exception {
// 读取上传类型
String configUploadType = SpringContextUtils.getApplicationContext().getEnvironment().getProperty("jeecg.uploadType", "minio");
String uploadType = configUploadType;
// 生成文件路径
String uuid = UUID.randomUUID().toString();
String originalFilename = file.getOriginalFilename();
String fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
String fileName = uuid + fileExtension;
String relativePath = "document/" + fileName;
try (InputStream inputStream = file.getInputStream()) {
String fileUrl = "";
if ("minio".equals(uploadType)) {
fileUrl = MinioUtil.upload(inputStream, relativePath);
} else if ("alioss".equals(uploadType)) {
OssBootUtil.upload(inputStream, relativePath);
fileUrl = relativePath; // 可在网关拼域名
} else {
// 本地存储
String uploadpath = SpringContextUtils.getApplicationContext().getEnvironment().getProperty("jeecg.path.upload");
Path target = Path.of(uploadpath, relativePath);
Files.createDirectories(target.getParent());
Files.copy(inputStream, target, StandardCopyOption.REPLACE_EXISTING);
fileUrl = relativePath;
}
log.info("文档文件上传成功: originalFilename={}, fileUrl={}", originalFilename, fileUrl);
return fileUrl;
}
}
/**
* 上传图片文件
*/
private String uploadImageFile(MultipartFile file, HttpServletRequest request) throws Exception {
// 读取上传类型
String configUploadType = SpringContextUtils.getApplicationContext().getEnvironment().getProperty("jeecg.uploadType", "minio");
String uploadType = configUploadType;
// 生成文件路径
String uuid = UUID.randomUUID().toString();
String originalFilename = file.getOriginalFilename();
String fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
String fileName = uuid + fileExtension;
String relativePath = "image/" + fileName;
try (InputStream inputStream = file.getInputStream()) {
String fileUrl = "";
if ("minio".equals(uploadType)) {
fileUrl = MinioUtil.upload(inputStream, relativePath);
} else if ("alioss".equals(uploadType)) {
OssBootUtil.upload(inputStream, relativePath);
fileUrl = relativePath; // 可在网关拼域名
} else {
// 本地存储
String uploadpath = SpringContextUtils.getApplicationContext().getEnvironment().getProperty("jeecg.path.upload");
Path target = Path.of(uploadpath, relativePath);
Files.createDirectories(target.getParent());
Files.copy(inputStream, target, StandardCopyOption.REPLACE_EXISTING);
fileUrl = relativePath;
}
log.info("图片文件上传成功: originalFilename={}, fileUrl={}", originalFilename, fileUrl);
return fileUrl;
}
}
}

View File

@ -0,0 +1,470 @@
package org.jeecg.modules.aiol.controller;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.aiol.entity.AiolClass;
import org.jeecg.modules.aiol.entity.AiolClassStudent;
import org.jeecg.modules.aiol.entity.AiolHomework;
import org.jeecg.modules.aiol.entity.AiolCourseSection;
import org.jeecg.modules.aiol.entity.AiolDiscussion;
import org.jeecg.modules.aiol.entity.AiolEntityLink;
import org.jeecg.modules.aiol.entity.AiolResource;
import org.jeecg.modules.aiol.entity.AiolComment;
import org.jeecg.modules.aiol.service.IAiolClassService;
import org.jeecg.modules.aiol.service.IAiolClassStudentService;
import org.jeecg.modules.aiol.service.IAiolHomeworkService;
import org.jeecg.modules.aiol.service.IAiolCourseSectionService;
import org.jeecg.modules.aiol.service.IAiolDiscussionService;
import org.jeecg.modules.aiol.service.IAiolEntityLinkService;
import org.jeecg.modules.aiol.service.IAiolResourceService;
import org.jeecg.modules.aiol.service.IAiolCommentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.HashSet;
import java.util.Set;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.stream.Collectors;
import java.time.ZoneId;
import java.util.Date;
@Tag(name = "统计")
@RestController
@RequestMapping("/aiol/statistics")
@Slf4j
public class AiolStatisticsController {
@Autowired
private IAiolClassService aiolClassService;
@Autowired
private IAiolClassStudentService aiolClassStudentService;
@Autowired
private IAiolHomeworkService aiolHomeworkService;
@Autowired
private IAiolCourseSectionService aiolCourseSectionService;
@Autowired
private IAiolDiscussionService aiolDiscussionService;
@Autowired
private IAiolEntityLinkService aiolEntityLinkService;
@Autowired
private IAiolResourceService aiolResourceService;
@Autowired
private IAiolCommentService aiolCommentService;
/**
* 课程基础数据统计
*
* @param courseId 课程ID
* @return 课程统计数据
*/
@Operation(summary = "课程基础数据统计", description = "统计课程下的学生人数、班级数量、作业数量、考试数量、练习数量、讨论数量")
@GetMapping(value = "/course-basic-stats")
public Result<Map<String, Object>> getCourseBasicStats(@RequestParam(name = "courseId", required = true) String courseId) {
try {
log.info("开始统计课程基础数据课程ID: {}", courseId);
Map<String, Object> stats = new HashMap<>();
// 1. 统计课程下班级数量
long classCount = getClassCount(courseId);
stats.put("classCount", classCount);
// 2. 统计课程下学生人数
long studentCount = getStudentCount(courseId);
stats.put("studentCount", studentCount);
// 3. 统计课程下作业数量
long homeworkCount = getHomeworkCount(courseId);
stats.put("homeworkCount", homeworkCount);
// 4. 统计课程下考试数量 (type=2)
long examCount = getExamCount(courseId);
stats.put("examCount", examCount);
// 5. 统计课程下练习数量 (type=4)
long practiceCount = getPracticeCount(courseId);
stats.put("practiceCount", practiceCount);
// 6. 统计课程下讨论数量
long discussionCount = getDiscussionCount(courseId);
stats.put("discussionCount", discussionCount);
log.info("课程基础数据统计完成课程ID: {}, 统计结果: {}", courseId, stats);
return Result.OK(stats);
} catch (Exception e) {
log.error("课程基础数据统计失败课程ID: {}, 错误: {}", courseId, e.getMessage(), e);
return Result.error("统计失败: " + e.getMessage());
}
}
/**
* 统计课程下班级数量
*
* @param courseId 课程ID
* @return 班级数量
*/
private long getClassCount(String courseId) {
QueryWrapper<AiolClass> classQuery = new QueryWrapper<>();
classQuery.eq("course_id", courseId);
long count = aiolClassService.count(classQuery);
log.debug("课程 {} 班级数量: {}", courseId, count);
return count;
}
/**
* 统计课程下学生人数
*
* @param courseId 课程ID
* @return 学生人数
*/
private long getStudentCount(String courseId) {
// 1. 先查询课程下的所有班级
QueryWrapper<AiolClass> classQuery = new QueryWrapper<>();
classQuery.eq("course_id", courseId);
List<AiolClass> classList = aiolClassService.list(classQuery);
if (classList.isEmpty()) {
log.debug("课程 {} 没有班级,学生人数为 0", courseId);
return 0;
}
// 2. 收集所有班级ID
List<String> classIds = classList.stream()
.map(AiolClass::getId)
.collect(java.util.stream.Collectors.toList());
// 3. 查询这些班级下的所有学生使用Set去重
QueryWrapper<AiolClassStudent> studentQuery = new QueryWrapper<>();
studentQuery.in("class_id", classIds);
List<AiolClassStudent> studentList = aiolClassStudentService.list(studentQuery);
// 4. 去重统计学生人数一个学生可能在多个班级中
Set<String> uniqueStudentIds = new HashSet<>();
for (AiolClassStudent student : studentList) {
uniqueStudentIds.add(student.getStudentId());
}
long count = uniqueStudentIds.size();
log.debug("课程 {} 学生人数: {} (去重后)", courseId, count);
return count;
}
/**
* 统计课程下作业数量
*
* @param courseId 课程ID
* @return 作业数量
*/
private long getHomeworkCount(String courseId) {
QueryWrapper<AiolHomework> homeworkQuery = new QueryWrapper<>();
homeworkQuery.eq("course_id", courseId);
long count = aiolHomeworkService.count(homeworkQuery);
log.debug("课程 {} 作业数量: {}", courseId, count);
return count;
}
/**
* 统计课程下考试数量 (type=2)
*
* @param courseId 课程ID
* @return 考试数量
*/
private long getExamCount(String courseId) {
QueryWrapper<AiolCourseSection> examQuery = new QueryWrapper<>();
examQuery.eq("course_id", courseId)
.eq("type", 2);
long count = aiolCourseSectionService.count(examQuery);
log.debug("课程 {} 考试数量: {}", courseId, count);
return count;
}
/**
* 统计课程下练习数量 (type=4)
*
* @param courseId 课程ID
* @return 练习数量
*/
private long getPracticeCount(String courseId) {
QueryWrapper<AiolCourseSection> practiceQuery = new QueryWrapper<>();
practiceQuery.eq("course_id", courseId)
.eq("type", 4);
long count = aiolCourseSectionService.count(practiceQuery);
log.debug("课程 {} 练习数量: {}", courseId, count);
return count;
}
/**
* 统计课程下讨论数量
*
* @param courseId 课程ID
* @return 讨论数量
*/
private long getDiscussionCount(String courseId) {
QueryWrapper<AiolDiscussion> discussionQuery = new QueryWrapper<>();
discussionQuery.eq("course_id", courseId);
long count = aiolDiscussionService.count(discussionQuery);
log.debug("课程 {} 讨论数量: {}", courseId, count);
return count;
}
/**
* 课程教学建设数据统计
*
* @param courseId 课程ID
* @return 课程教学建设统计数据
*/
@Operation(summary = "课程教学建设数据统计", description = "统计课程下的课件/视频数量、资料/文档数量、题库数量、试卷数量")
@GetMapping(value = "/course-teaching-stats")
public Result<Map<String, Object>> getCourseTeachingStats(@RequestParam(name = "courseId", required = true) String courseId) {
try {
log.info("开始统计课程教学建设数据课程ID: {}", courseId);
Map<String, Object> stats = new HashMap<>();
// 1. 统计课件/视频数量和资料/文档数量
Map<String, Long> resourceStats = getResourceStats(courseId);
stats.put("videoCount", resourceStats.get("videoCount"));
stats.put("documentCount", resourceStats.get("documentCount"));
// 2. 统计题库数量
long questionCount = getQuestionCount(courseId);
stats.put("questionCount", questionCount);
log.info("课程教学建设数据统计完成课程ID: {}, 统计结果: {}", courseId, stats);
return Result.OK(stats);
} catch (Exception e) {
log.error("课程教学建设数据统计失败课程ID: {}, 错误: {}", courseId, e.getMessage(), e);
return Result.error("统计失败: " + e.getMessage());
}
}
/**
* 统计课程下资源数量视频和文档
*
* @param courseId 课程ID
* @return 资源统计结果
*/
private Map<String, Long> getResourceStats(String courseId) {
Map<String, Long> resourceStats = new HashMap<>();
long videoCount = 0;
long documentCount = 0;
try {
// 1. 查询课程关联的资源链接
QueryWrapper<AiolEntityLink> linkQuery = new QueryWrapper<>();
linkQuery.eq("source_type", "course")
.eq("source_id", courseId)
.eq("target_type", "resource");
List<AiolEntityLink> entityLinks = aiolEntityLinkService.list(linkQuery);
log.debug("课程 {} 关联的资源链接数量: {}", courseId, entityLinks.size());
if (!entityLinks.isEmpty()) {
// 2. 收集所有资源ID
List<String> resourceIds = entityLinks.stream()
.map(AiolEntityLink::getTargetId)
.collect(java.util.stream.Collectors.toList());
// 3. 查询资源详情并按类型统计
QueryWrapper<AiolResource> resourceQuery = new QueryWrapper<>();
resourceQuery.in("id", resourceIds);
List<AiolResource> resources = aiolResourceService.list(resourceQuery);
// 4. 按类型分类统计
for (AiolResource resource : resources) {
Integer type = resource.getType();
if (type != null) {
if (type == 0) {
// 视频类型
videoCount++;
} else if (type == 1 || type == 2) {
// 图片(1)和文档(2)统称为资料/文档
documentCount++;
}
}
}
log.debug("课程 {} 资源统计完成 - 视频: {}, 文档: {}", courseId, videoCount, documentCount);
}
} catch (Exception e) {
log.error("统计课程 {} 资源数量时发生异常: {}", courseId, e.getMessage(), e);
}
resourceStats.put("videoCount", videoCount);
resourceStats.put("documentCount", documentCount);
return resourceStats;
}
/**
* 统计课程下题库数量
*
* @param courseId 课程ID
* @return 题库数量
*/
private long getQuestionCount(String courseId) {
QueryWrapper<AiolEntityLink> repoQuery = new QueryWrapper<>();
repoQuery.eq("source_type", "course")
.eq("source_id", courseId)
.eq("target_type", "repo");
long count = aiolEntityLinkService.count(repoQuery);
log.debug("课程 {} 题库数量: {}", courseId, count);
return count;
}
/**
* 课程评论数量统计按天
*
* @param courseId 课程ID
* @param limit 天数限制统计从今天往前limit天的数据
* @return 每天的评论数量统计
*/
@Operation(summary = "课程评论数量统计", description = "统计课程下指定天数内每天的评论数量,包括子评论")
@GetMapping(value = "/course-comment-stats")
public Result<Map<String, Object>> getCourseCommentStats(
@RequestParam(name = "courseId", required = true) String courseId,
@RequestParam(name = "limit", defaultValue = "7") Integer limit) {
try {
log.info("开始统计课程评论数据课程ID: {}, 天数限制: {}", courseId, limit);
// 1. 查询课程的直接评论
List<AiolComment> courseComments = getCourseDirectComments(courseId, limit);
log.debug("课程 {} 直接评论数量: {}", courseId, courseComments.size());
// 2. 递归查询所有子评论
List<AiolComment> allComments = new ArrayList<>(courseComments);
for (AiolComment comment : courseComments) {
List<AiolComment> subComments = getAllSubComments(comment.getId());
allComments.addAll(subComments);
}
log.debug("课程 {} 总评论数量(含子评论): {}", courseId, allComments.size());
// 3. 按日期分组统计
Map<String, Long> dailyStats = groupCommentsByDate(allComments, limit);
// 4. 构建返回结果
Map<String, Object> result = new HashMap<>();
result.put("courseId", courseId);
result.put("limit", limit);
result.put("totalComments", allComments.size());
result.put("dailyStats", dailyStats);
log.info("课程评论数据统计完成课程ID: {}, 总评论数: {}", courseId, allComments.size());
return Result.OK(result);
} catch (Exception e) {
log.error("课程评论数据统计失败课程ID: {}, 错误: {}", courseId, e.getMessage(), e);
return Result.error("统计失败: " + e.getMessage());
}
}
/**
* 获取课程的直接评论指定天数内
*
* @param courseId 课程ID
* @param limit 天数限制
* @return 课程直接评论列表
*/
private List<AiolComment> getCourseDirectComments(String courseId, Integer limit) {
QueryWrapper<AiolComment> commentQuery = new QueryWrapper<>();
commentQuery.eq("target_type", "course")
.eq("target_id", courseId);
// 添加时间范围限制
if (limit != null && limit > 0) {
LocalDateTime startTime = LocalDate.now().minusDays(limit - 1).atStartOfDay();
Date startDate = Date.from(startTime.atZone(ZoneId.systemDefault()).toInstant());
commentQuery.ge("create_time", startDate);
}
commentQuery.orderByDesc("create_time");
return aiolCommentService.list(commentQuery);
}
/**
* 递归获取评论的所有子评论
*
* @param commentId 父评论ID
* @return 所有子评论列表
*/
private List<AiolComment> getAllSubComments(String commentId) {
List<AiolComment> allSubComments = new ArrayList<>();
// 查询直接子评论
QueryWrapper<AiolComment> subCommentQuery = new QueryWrapper<>();
subCommentQuery.eq("target_type", "comment")
.eq("target_id", commentId)
.orderByDesc("create_time");
List<AiolComment> directSubComments = aiolCommentService.list(subCommentQuery);
allSubComments.addAll(directSubComments);
// 递归查询子评论的子评论
for (AiolComment subComment : directSubComments) {
List<AiolComment> subSubComments = getAllSubComments(subComment.getId());
allSubComments.addAll(subSubComments);
}
return allSubComments;
}
/**
* 按日期分组统计评论数量
*
* @param comments 所有评论列表
* @param limit 天数限制
* @return 每日评论数量统计
*/
private Map<String, Long> groupCommentsByDate(List<AiolComment> comments, Integer limit) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
// 初始化指定天数的日期统计确保每天都有数据即使是0
Map<String, Long> dailyStats = new LinkedHashMap<>();
for (int i = limit - 1; i >= 0; i--) {
String date = LocalDate.now().minusDays(i).format(formatter);
dailyStats.put(date, 0L);
}
// 按创建日期分组统计评论数量
Map<String, Long> commentStats = comments.stream()
.filter(comment -> comment.getCreateTime() != null)
.collect(Collectors.groupingBy(
comment -> {
Date createTime = comment.getCreateTime();
LocalDateTime localDateTime = createTime.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDateTime();
return localDateTime.toLocalDate().format(formatter);
},
Collectors.counting()
));
// 合并统计结果
commentStats.forEach((date, count) -> {
if (dailyStats.containsKey(date)) {
dailyStats.put(date, count);
}
});
return dailyStats;
}
}

View File

@ -0,0 +1,182 @@
package org.jeecg.modules.aiol.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.aiol.entity.AiolTag;
import org.jeecg.modules.aiol.service.IAiolTagService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 标签
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Tag(name="标签")
@RestController
@RequestMapping("/aiol/aiolTag")
@Slf4j
public class AiolTagController extends JeecgController<AiolTag, IAiolTagService> {
@Autowired
private IAiolTagService aiolTagService;
/**
* 分页列表查询
*
* @param aiolTag
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "标签-分页列表查询")
@Operation(summary="标签-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolTag>> queryPageList(AiolTag aiolTag,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolTag> queryWrapper = QueryGenerator.initQueryWrapper(aiolTag, req.getParameterMap());
Page<AiolTag> page = new Page<AiolTag>(pageNo, pageSize);
IPage<AiolTag> pageList = aiolTagService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolTag
* @return
*/
@AutoLog(value = "标签-添加")
@Operation(summary="标签-添加")
@RequiresPermissions("aiol:aiol_tag:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolTag aiolTag) {
aiolTagService.save(aiolTag);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param aiolTag
* @return
*/
@AutoLog(value = "标签-编辑")
@Operation(summary="标签-编辑")
@RequiresPermissions("aiol:aiol_tag:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AiolTag aiolTag) {
aiolTagService.updateById(aiolTag);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "标签-通过id删除")
@Operation(summary="标签-通过id删除")
@RequiresPermissions("aiol:aiol_tag:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
aiolTagService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "标签-批量删除")
@Operation(summary="标签-批量删除")
@RequiresPermissions("aiol:aiol_tag:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.aiolTagService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "标签-通过id查询")
@Operation(summary="标签-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolTag> queryById(@RequestParam(name="id",required=true) String id) {
AiolTag aiolTag = aiolTagService.getById(id);
if(aiolTag==null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolTag);
}
/**
* 导出excel
*
* @param request
* @param aiolTag
*/
@RequiresPermissions("aiol:aiol_tag:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolTag aiolTag) {
return super.exportXls(request, aiolTag, AiolTag.class, "标签");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_tag:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolTag.class);
}
}

View File

@ -0,0 +1,495 @@
package org.jeecg.modules.aiol.controller;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.DictModel;
import org.jeecg.common.util.PasswordUtil;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.config.shiro.IgnoreAuth;
import org.jeecg.modules.aiol.dto.TeacherInfo;
import org.jeecg.modules.aiol.dto.UserInfoResponse;
import org.jeecg.modules.aiol.dto.EditProfileDTO;
import org.jeecg.modules.aiol.entity.AiolClass;
import org.jeecg.modules.aiol.entity.AiolClassStudent;
import org.jeecg.modules.aiol.entity.AiolUserInfo;
import org.jeecg.modules.aiol.mapper.AiolUserInfoMapper;
import org.jeecg.modules.aiol.service.IAiolClassService;
import org.jeecg.modules.aiol.service.IAiolClassStudentService;
import org.jeecg.modules.aiol.service.IAiolUserInfoService;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.service.ISysUserService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Tag(name = "前台用户")
@RestController
@RequestMapping("/aiol/aiolUser")
@Slf4j
public class AiolUserController {
@Autowired
private ISysUserService sysUserService;
@Autowired
private RedisUtil redisUtil;
@Autowired
private IAiolUserInfoService userBizService;
@Autowired
private AiolUserInfoMapper userInfoMapper;
@Autowired
private ISysBaseAPI sysBaseApi;
@Autowired
private IAiolClassService aiolClassService;
@Autowired
private IAiolClassStudentService aiolClassStudentService;
@PostMapping("/login")
@Operation(summary = "用户登录")
@IgnoreAuth
public Result<JSONObject> login(@RequestBody Map<String, String> user, HttpServletRequest request) {
Result<JSONObject> result = new Result<JSONObject>();
String username = user.get("username");
String password = user.get("password");
if (isLoginFailOvertimes(username)) {
return result.error500("该用户登录失败次数过多请于10分钟后再次登录");
}
// step.2 校验用户是否存在且有效
LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysUser::getUsername, username);
SysUser sysUser = sysUserService.getOne(queryWrapper);
Result<?> checkResult = sysUserService.checkUserIsEffective(sysUser);
if (!checkResult.isSuccess()) {
return result.error500(checkResult.getMessage());
}
// step.3 校验用户名或密码是否正确
String userpassword = PasswordUtil.encrypt(username, password, sysUser.getSalt());
String syspassword = sysUser.getPassword();
if (!syspassword.equals(userpassword)) {
addLoginFailOvertimes(username);
result.error500("用户名或密码错误");
return result;
}
// step.4 登录成功获取用户信息
JSONObject obj = new JSONObject(new LinkedHashMap<>());
// 1.生成token
String token = JwtUtil.sign(username, syspassword);
// 设置token缓存有效时间
redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME * 2 / 1000);
obj.put("token", token);
// TODO 查询用户信息
result.setResult(obj);
result.success("登录成功");
return result;
}
/**
* 登录失败超出次数5 返回true
*
* @param username
* @return
*/
private boolean isLoginFailOvertimes(String username) {
String key = CommonConstant.LOGIN_FAIL + username;
Object failTime = redisUtil.get(key);
if (failTime != null) {
Integer val = Integer.parseInt(failTime.toString());
if (val > 5) {
return true;
}
}
return false;
}
/**
* 记录登录失败次数
*
* @param username
*/
private void addLoginFailOvertimes(String username) {
String key = CommonConstant.LOGIN_FAIL + username;
Object failTime = redisUtil.get(key);
Integer val = 0;
if (failTime != null) {
val = Integer.parseInt(failTime.toString());
}
// 10分钟一分钟为60s
redisUtil.set(key, ++val, 600);
}
@GetMapping("/all_teachers")
@Operation(summary = "查询师资力量", description = "categoryId为all则查询全部")
@IgnoreAuth
public Result<List<TeacherInfo>> queryAllTeachers(@RequestParam("categoryId") String categoryId) {
List<TeacherInfo> list = userBizService.queryAllTeachers(categoryId);
return Result.OK(list);
}
@GetMapping("/info")
@Operation(summary = "查询用户信息", description = "通过JWT token获取当前用户的详细信息包括基本信息、角色和扩展信息")
public Result<UserInfoResponse> queryUserInfo(HttpServletRequest request) {
try {
// 1. 从JWT中获取用户名
String username = JwtUtil.getUserNameByToken(request);
if (username == null || username.trim().isEmpty()) {
return Result.error(401, "用户未登录或token无效");
}
// 2. 根据用户名查询系统用户信息
SysUser sysUser = sysUserService.getUserByName(username);
if (sysUser == null) {
return Result.error(404, "用户不存在");
}
// 3. 查询用户角色
List<String> roles = sysUserService.getUserRolesSet(username).stream()
.collect(Collectors.toList());
// 4. 根据用户ID查询user_info表信息
AiolUserInfo userInfo = userInfoMapper.selectOne(
new QueryWrapper<AiolUserInfo>().eq("user_id", sysUser.getId()));
// 5. 构建返回结果
UserInfoResponse response = new UserInfoResponse();
// 基本用户信息
UserInfoResponse.BaseInfo baseInfo = new UserInfoResponse.BaseInfo();
BeanUtils.copyProperties(sysUser, baseInfo);
response.setBaseInfo(baseInfo);
response.setRoles(roles);
// 扩展用户信息
if (userInfo != null) {
UserInfoResponse.ExtendedInfo extendedInfo = new UserInfoResponse.ExtendedInfo();
BeanUtils.copyProperties(userInfo, extendedInfo);
response.setExtendedInfo(extendedInfo);
}
return Result.OK(response);
} catch (Exception e) {
log.error("查询用户信息失败:" + e.getMessage(), e);
return Result.error(500, "查询用户信息失败:" + e.getMessage());
}
}
@GetMapping("/schools")
@Operation(summary = "查询学校列表")
@IgnoreAuth
public Result<List<String>> querySchools() {
List<DictModel> list = sysBaseApi.getDictItems("school_list");
List<String> schools = list.stream()
.map(d -> d.getLabel())
.collect(Collectors.toList());
return Result.OK(schools);
}
@GetMapping("/teachers")
@Operation(summary = "查询所有教师用户", description = "支持按教师姓名和工号进行可选查询")
@IgnoreAuth
public Result<List<Map<String, Object>>> queryAllTeachers(
@RequestParam(value = "realName", required = false) String realName,
@RequestParam(value = "workNo", required = false) String workNo) {
try {
// 构建查询条件
LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
// 只查询教师角色的用户
queryWrapper.inSql(SysUser::getId,
"SELECT user_id FROM sys_user_role WHERE role_id = '1955367301787348993'"); // 教师角色ID
// 可选条件按姓名查询
if (realName != null && !realName.trim().isEmpty()) {
queryWrapper.like(SysUser::getRealname, realName.trim());
}
// 可选条件按工号查询
if (workNo != null && !workNo.trim().isEmpty()) {
queryWrapper.like(SysUser::getWorkNo, workNo.trim());
}
// 查询用户列表
List<SysUser> teachers = sysUserService.list(queryWrapper);
// 构建返回结果
List<Map<String, Object>> result = teachers.stream().map(teacher -> {
Map<String, Object> teacherInfo = new LinkedHashMap<>();
teacherInfo.put("id", teacher.getId());
teacherInfo.put("username", teacher.getUsername());
teacherInfo.put("realname", teacher.getRealname());
teacherInfo.put("workNo", teacher.getWorkNo());
teacherInfo.put("phone", teacher.getPhone());
teacherInfo.put("email", teacher.getEmail());
teacherInfo.put("avatar", teacher.getAvatar());
teacherInfo.put("status", teacher.getStatus());
teacherInfo.put("createTime", teacher.getCreateTime());
// 查询扩展信息
AiolUserInfo userInfo = userInfoMapper.selectOne(
new QueryWrapper<AiolUserInfo>().eq("user_id", teacher.getId()));
if (userInfo != null) {
teacherInfo.put("major", userInfo.getMajor());
teacherInfo.put("college", userInfo.getCollege());
teacherInfo.put("education", userInfo.getEducation());
teacherInfo.put("title", userInfo.getTitle());
teacherInfo.put("tag", userInfo.getTag());
}
return teacherInfo;
}).collect(Collectors.toList());
return Result.OK(result);
} catch (Exception e) {
log.error("查询教师用户失败: realName={}, workNo={}, error={}", realName, workNo, e.getMessage(), e);
return Result.error("查询教师用户失败: " + e.getMessage());
}
}
@PostMapping("/register")
@Operation(summary = "学生注册", description = "学生通过学号、邀请码和密码进行注册,注册成功后自动加入对应班级")
@IgnoreAuth
@Transactional(rollbackFor = Exception.class)
public Result<JSONObject> studentRegister(@RequestBody Map<String, String> registerData) {
Result<JSONObject> result = new Result<>();
try {
// 1. 获取注册参数
String studentNumber = registerData.get("studentNumber");
String inviteCode = registerData.get("inviteCode");
String password = registerData.get("password");
// 2. 参数验证
if (studentNumber == null || studentNumber.trim().isEmpty()) {
return result.error500("学号不能为空");
}
if (inviteCode == null || inviteCode.trim().isEmpty()) {
return result.error500("邀请码不能为空");
}
if (password == null || password.trim().isEmpty()) {
return result.error500("密码不能为空");
}
// 3. 检查学号是否已被注册
SysUser existingUser = sysUserService.getUserByName(studentNumber);
if (existingUser != null) {
return result.error500("学号已被注册,请使用其他学号");
}
// 4. 检查邀请码是否对应某个班级
LambdaQueryWrapper<AiolClass> classQuery = new LambdaQueryWrapper<>();
classQuery.eq(AiolClass::getInviteCode, inviteCode);
// 允许存在重复邀请码时仅取第一条避免抛出期望1条结果却查询到多条的异常
AiolClass targetClass = aiolClassService.getOne(classQuery, false);
if (targetClass == null) {
return result.error500("邀请码无效,请检查邀请码是否正确");
}
// 5. 使用通用方法创建学生用户
SysUser studentUser = sysUserService.createStudentUser(studentNumber, studentNumber, password);
// 6. 设置工号学号
studentUser.setWorkNo(studentNumber);
sysUserService.updateById(studentUser);
// 7. 自动加入班级
AiolClassStudent classStudent = new AiolClassStudent();
classStudent.setClassId(targetClass.getId());
classStudent.setStudentId(studentUser.getId());
classStudent.setCreateBy("system"); // 系统创建
classStudent.setCreateTime(new Date());
boolean classStudentSaved = aiolClassStudentService.save(classStudent);
if (!classStudentSaved) {
throw new RuntimeException("加入班级失败");
}
// 8. 构建返回结果
JSONObject response = new JSONObject();
response.put("userId", studentUser.getId());
response.put("username", studentUser.getUsername());
response.put("workNo", studentUser.getWorkNo());
response.put("classId", targetClass.getId());
response.put("className", targetClass.getName());
response.put("message", "注册成功,已自动加入班级:" + targetClass.getName());
log.info("学生注册成功: 学号={}, 班级ID={}, 班级名={}",
studentNumber, targetClass.getId(), targetClass.getName());
result.setResult(response);
result.success("注册成功");
return result;
} catch (Exception e) {
log.error("学生注册失败: {}", e.getMessage(), e);
return result.error500("注册失败: " + e.getMessage());
}
}
@PostMapping("/edit_profile")
@Operation(summary = "编辑个人信息", description = "更新用户的基本信息和扩展信息分别存储到sys_user和aiol_user_info表")
@Transactional(rollbackFor = Exception.class)
public Result<String> editProfile(@RequestBody EditProfileDTO profileDTO, HttpServletRequest request) {
try {
// 1. 从JWT中获取当前用户信息
String username = JwtUtil.getUserNameByToken(request);
if (username == null || username.trim().isEmpty()) {
return Result.error(401, "用户未登录或token无效");
}
SysUser currentUser = sysUserService.getUserByName(username);
if (currentUser == null) {
return Result.error(404, "用户不存在");
}
String id = currentUser.getId();
// 2. 更新sys_user表的基本信息
SysUser userToUpdate = new SysUser();
userToUpdate.setId(id);
if (profileDTO.getRealname() != null) {
userToUpdate.setRealname(profileDTO.getRealname());
}
if (profileDTO.getAvatar() != null) {
userToUpdate.setAvatar(profileDTO.getAvatar());
}
if (profileDTO.getBirthday() != null) {
userToUpdate.setBirthday(profileDTO.getBirthday());
}
if (profileDTO.getSex() != null) {
userToUpdate.setSex(profileDTO.getSex());
}
if (profileDTO.getEmail() != null) {
userToUpdate.setEmail(profileDTO.getEmail());
}
if (profileDTO.getPhone() != null) {
userToUpdate.setPhone(profileDTO.getPhone());
}
// 设置更新信息
userToUpdate.setUpdateBy(username);
userToUpdate.setUpdateTime(new Date());
boolean userUpdated = sysUserService.updateById(userToUpdate);
if (!userUpdated) {
return Result.error(500, "更新用户基本信息失败");
}
// 3. 更新aiol_user_info表的扩展信息
AiolUserInfo userInfo = userInfoMapper.selectOne(
new QueryWrapper<AiolUserInfo>().eq("user_id", id));
if (userInfo == null) {
// 如果扩展信息不存在创建新记录
userInfo = new AiolUserInfo();
userInfo.setUserId(id);
userInfo.setCreateBy(username);
userInfo.setCreateTime(new Date());
}
// 更新扩展信息字段
if (profileDTO.getMajor() != null) {
userInfo.setMajor(profileDTO.getMajor());
}
if (profileDTO.getCollege() != null) {
userInfo.setCollege(profileDTO.getCollege());
}
if (profileDTO.getEducation() != null) {
userInfo.setEducation(profileDTO.getEducation());
}
if (profileDTO.getTitle() != null) {
userInfo.setTitle(profileDTO.getTitle());
}
if (profileDTO.getTag() != null) {
userInfo.setTag(profileDTO.getTag());
}
// 设置更新信息
userInfo.setUpdateBy(username);
userInfo.setUpdateTime(new Date());
boolean userInfoUpdated;
if (userInfo.getId() == null) {
// 新增记录
userInfoUpdated = userInfoMapper.insert(userInfo) > 0;
} else {
// 更新记录
userInfoUpdated = userInfoMapper.updateById(userInfo) > 0;
}
if (!userInfoUpdated) {
return Result.error(500, "更新用户扩展信息失败");
}
log.info("用户个人信息更新成功: userId={}, username={}", id, username);
return Result.OK("个人信息更新成功");
} catch (Exception e) {
log.error("编辑个人信息失败: {}", e.getMessage(), e);
return Result.error(500, "编辑个人信息失败: " + e.getMessage());
}
}
//密码修改
@AutoLog(value = "用户信息-修改密码")
@Operation(summary="用户信息-修改密码")
@PostMapping(value = "/updatePassword")
public Result<?> updatePassword(@RequestBody Map<String, String> params, HttpServletRequest request) {
try {
// 1. 从JWT中获取当前用户信息
String username = JwtUtil.getUserNameByToken(request);
if (username == null || username.trim().isEmpty()) {
return Result.error(401, "用户未登录或token无效");
}
SysUser currentUser = sysUserService.getUserByName(username);
if (currentUser == null) {
return Result.error(404, "用户不存在");
}
String oldPassword = params.get("oldPassword");
String newPassword = params.get("newPassword");
String id = currentUser.getId();
//判断密码是否一致
String oldPasswordEncrypt = PasswordUtil.encrypt(username, oldPassword, currentUser.getSalt());
String passwordEncrypt = currentUser.getPassword();
if (newPassword == null || newPassword.trim().isEmpty()) {
return Result.error(400, "新密码不允许为空");
}
if (newPassword.equals(oldPassword)) {
return Result.error(400, "新密码不能与旧密码相同");
}
if (!oldPasswordEncrypt.equals(passwordEncrypt)) {
return Result.error(400, "旧密码不正确");
}
//更新密码
String password = PasswordUtil.encrypt(username, newPassword, currentUser.getSalt());
currentUser.setPassword(password);
sysUserService.updateById(currentUser);
log.info("用户密码修改成功: userId={}, username={}", id, username);
return Result.OK("密码修改成功");
}catch (Exception e){
log.error("密码修改失败: {}", e.getMessage(), e);
return Result.error(500, "密码修改失败: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,435 @@
package org.jeecg.modules.aiol.controller;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.aiol.entity.AiolUserFollow;
import org.jeecg.modules.aiol.entity.UserFollow;
import org.jeecg.modules.aiol.service.IAiolUserFollowService;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.vo.LoginUser;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.service.impl.SysUserServiceImpl;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 关注关系
* @Author: jeecg-boot
* @Date: 2025-09-11
* @Version: V1.0
*/
@Tag(name = "关注关系")
@RestController
@RequestMapping("/aiol/aiolUserFollow")
@Slf4j
public class AiolUserFollowController extends JeecgController<AiolUserFollow, IAiolUserFollowService> {
@Autowired
private IAiolUserFollowService aiolUserFollowService;
@Autowired
private ISysBaseAPI sysBaseApi;
@Autowired
private SysUserServiceImpl sysUserService;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 分页列表查询
*
* @param aiolUserFollow
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "关注关系-分页列表查询")
@Operation(summary = "关注关系-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolUserFollow>> queryPageList(AiolUserFollow aiolUserFollow,
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolUserFollow> queryWrapper = QueryGenerator.initQueryWrapper(aiolUserFollow, req.getParameterMap());
Page<AiolUserFollow> page = new Page<AiolUserFollow>(pageNo, pageSize);
IPage<AiolUserFollow> pageList = aiolUserFollowService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolUserFollow
* @return
*/
@AutoLog(value = "关注关系-添加")
@Operation(summary = "关注关系-添加")
@RequiresPermissions("aiol:aiol_user_follow:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolUserFollow aiolUserFollow) {
aiolUserFollowService.save(aiolUserFollow);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param aiolUserFollow
* @return
*/
@AutoLog(value = "关注关系-编辑")
@Operation(summary = "关注关系-编辑")
@RequiresPermissions("aiol:aiol_user_follow:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
public Result<String> edit(@RequestBody AiolUserFollow aiolUserFollow) {
aiolUserFollowService.updateById(aiolUserFollow);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "关注关系-通过id删除")
@Operation(summary = "关注关系-通过id删除")
@RequiresPermissions("aiol:aiol_user_follow:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name = "id", required = true) String id) {
aiolUserFollowService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "关注关系-批量删除")
@Operation(summary = "关注关系-批量删除")
@RequiresPermissions("aiol:aiol_user_follow:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
this.aiolUserFollowService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "关注关系-通过id查询")
@Operation(summary = "关注关系-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolUserFollow> queryById(@RequestParam(name = "id", required = true) String id) {
AiolUserFollow aiolUserFollow = aiolUserFollowService.getById(id);
if (aiolUserFollow == null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolUserFollow);
}
/**
* 关注用户
*
* @param followedId 被关注者ID
* @param request
* @return
*/
@AutoLog(value = "关注关系-关注用户")
@Operation(summary = "关注用户", description = "当前登录用户关注指定用户")
@PostMapping(value = "/follow")
public Result<String> follow(@RequestParam(name = "followedId", required = true) String followedId,
HttpServletRequest request) {
try {
// 1. 获取当前登录用户信息
String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
String username = JwtUtil.getUsername(token);
LoginUser sysUser = sysBaseApi.getUserByName(username);
if (sysUser == null) {
return Result.error("用户未登录或登录已过期");
}
// 2. 检查是否已经关注
QueryWrapper<AiolUserFollow> checkWrapper = new QueryWrapper<>();
checkWrapper.eq("follower_id", sysUser.getId())
.eq("followed_id", followedId);
AiolUserFollow existingFollow = aiolUserFollowService.getOne(checkWrapper);
if (existingFollow != null) {
return Result.error("已经关注过该用户");
}
// 3. 检查是否关注自己
if (sysUser.getId().equals(followedId)) {
return Result.error("不能关注自己");
}
// 4. 创建关注关系
AiolUserFollow userFollow = new AiolUserFollow();
userFollow.setFollowerId(sysUser.getId());
userFollow.setFollowedId(followedId);
userFollow.setCreateBy(sysUser.getUsername());
userFollow.setCreateTime(new Date());
aiolUserFollowService.save(userFollow);
// 添加到Redis缓存
String recentFollowKey = "user:recent:follow:" + sysUser.getId();
String frequentVisitKey = "user:frequent:visit:" + sysUser.getId();
long currentTime = System.currentTimeMillis();
// 使用RedisTemplate操作Redis
// 存储最近关注使用时间戳作为分数
redisTemplate.opsForZSet().add(recentFollowKey, followedId, currentTime);
// 存储访问频率初始值为1
redisTemplate.opsForZSet().add(frequentVisitKey, followedId, 1);
// 设置过期时间例如30天
redisTemplate.expire(recentFollowKey, 30, TimeUnit.DAYS);
redisTemplate.expire(frequentVisitKey, 30, TimeUnit.DAYS);
return Result.OK("关注成功!");
} catch (Exception e) {
log.error("关注用户失败: followedId={}, error={}", followedId, e.getMessage(), e);
return Result.error("关注失败: " + e.getMessage());
}
}
//查询关注列表
@AutoLog(value = "关注关系-查询关注列表")
@Operation(summary = "查询关注列表", description = "当前登录用户关注的所有用户")
@GetMapping(value = "/followList")
public Result<List<?>> followList(HttpServletRequest req) {
try {
// 1. 获取当前登录用户信息
String token = req.getHeader(CommonConstant.X_ACCESS_TOKEN);
String username = JwtUtil.getUsername(token);
LoginUser sysUser = sysBaseApi.getUserByName(username);
if (sysUser == null) {
return Result.error("用户未登录或登录已过期");
}
// 2. 查询关注列表
QueryWrapper<AiolUserFollow> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("follower_id", sysUser.getId());
List<AiolUserFollow> list = aiolUserFollowService.list(queryWrapper);
if (list != null && !list.isEmpty()) {
// 使用Stream提取被关注者的ID列表
List<String> followedIds = list.stream()
.map(AiolUserFollow::getFollowedId)
.collect(Collectors.toList());
// 根据被关注者的ID列表查询用户信息
List<SysUser> followedUsers = sysUserService.listByIds(followedIds);
if (followedUsers != null && !followedUsers.isEmpty()) {
List<UserFollow> userFollows = new ArrayList<>();
for (SysUser followedUser : followedUsers) {
UserFollow userFollow = new UserFollow();
userFollow.setId(followedUser.getId());
userFollow.setIsFollow(true);
userFollow.setRealName(followedUser.getRealname());
userFollow.setAvatar(followedUser.getAvatar());
userFollows.add(userFollow);
}
return Result.OK(userFollows);
}
}
} catch (Exception e) {
return Result.error(e.getMessage());
}
return Result.OK();
}
//查询最近关注
@AutoLog(value = "关注关系-查询最近关注")
@Operation(summary = "查询最近关注", description = "当前登录用户最近关注的用户")
@GetMapping(value = "/recentFollowList")
public Result<List<?>> recentFollowList(HttpServletRequest req) {
try {
// 1. 获取当前登录用户信息
String token = req.getHeader(CommonConstant.X_ACCESS_TOKEN);
String username = JwtUtil.getUsername(token);
LoginUser sysUser = sysBaseApi.getUserByName(username);
if (sysUser == null) {
return Result.error("用户未登录或登录已过期");
}
// 2. 查询最近关注列表
String recentFollowKey = "user:recent:follow:" + sysUser.getId();
Set<Object> objects = redisTemplate.opsForZSet().range(recentFollowKey, 0, -1);
Set<String> followedIds = objects.stream()
.map(obj -> String.valueOf(obj))
.collect(Collectors.toSet());
if (followedIds != null && !followedIds.isEmpty()) {
// 根据被关注者的ID列表查询用户信息
List<SysUser> followedUsers = sysUserService.listByIds(followedIds);
if (followedUsers != null && !followedUsers.isEmpty()) {
List<UserFollow> userFollows = new ArrayList<>();
for (SysUser followedUser : followedUsers) {
UserFollow userFollow = new UserFollow();
userFollow.setId(followedUser.getId());
userFollow.setIsFollow(true);
userFollow.setRealName(followedUser.getRealname());
userFollow.setAvatar(followedUser.getAvatar());
userFollows.add(userFollow);
}
return Result.OK(userFollows);
}
}
} catch (Exception e) {
return Result.error(e.getMessage());
}
return Result.OK();
}
//查询最常访问
@AutoLog(value = "关注关系-查询最常访问")
@Operation(summary = "查询最常访问", description = "当前登录用户最常访问的用户")
@GetMapping(value = "/frequentVisitList")
public Result<List<?>> frequentVisitList(HttpServletRequest req) {
try {
// 1. 获取当前登录用户信息
String token = req.getHeader(CommonConstant.X_ACCESS_TOKEN);
String username = JwtUtil.getUsername(token);
LoginUser sysUser = sysBaseApi.getUserByName(username);
if (sysUser == null) {
return Result.error("用户未登录或登录已过期");
}
// 2. 查询最常访问列表
String frequentVisitKey = "user:frequent:visit:" + sysUser.getId();
Set<Object> objects = redisTemplate.opsForZSet().range(frequentVisitKey, 0, -1);
Set<String> followedIds = objects.stream()
.map(obj -> String.valueOf(obj))
.collect(Collectors.toSet());
if (followedIds != null && !followedIds.isEmpty()) {
// 根据被关注者的ID列表查询用户信息
List<SysUser> followedUsers = sysUserService.listByIds(followedIds);
if (followedUsers != null && !followedUsers.isEmpty()) {
List<UserFollow> userFollows = new ArrayList<>();
for (SysUser followedUser : followedUsers) {
UserFollow userFollow = new UserFollow();
userFollow.setId(followedUser.getId());
userFollow.setIsFollow(true);
userFollow.setRealName(followedUser.getRealname());
userFollow.setAvatar(followedUser.getAvatar());
userFollows.add(userFollow);
}
return Result.OK(userFollows);
}
}
}catch (Exception e){
return Result.error(e.getMessage());
}
return Result.OK();
}
/**
* 取消关注用户
*
* @param followedId 被关注者ID
* @param request
* @return
*/
@AutoLog(value = "关注关系-取消关注用户")
@Operation(summary = "取消关注用户", description = "当前登录用户取消关注指定用户")
@DeleteMapping(value = "/unfollow")
public Result<String> unfollow(@RequestParam(name = "followedId", required = true) String followedId,
HttpServletRequest request) {
try {
// 1. 获取当前登录用户信息
String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
String username = JwtUtil.getUsername(token);
LoginUser sysUser = sysBaseApi.getUserByName(username);
if (sysUser == null) {
return Result.error("用户未登录或登录已过期");
}
// 2. 查找关注关系
QueryWrapper<AiolUserFollow> deleteWrapper = new QueryWrapper<>();
deleteWrapper.eq("follower_id", sysUser.getId())
.eq("followed_id", followedId);
boolean removed = aiolUserFollowService.remove(deleteWrapper);
if (removed) {
// 从Redis中删除关注记录
String recentFollowKey = "user:recent:follow:" + sysUser.getId();
String frequentVisitKey = "user:frequent:visit:" + sysUser.getId();
// 从最近关注列表中删除
redisTemplate.opsForZSet().remove(recentFollowKey, followedId);
// 从访问频率列表中删除
redisTemplate.opsForZSet().remove(frequentVisitKey, followedId);
return Result.OK("取消关注成功!");
} else {
return Result.error("未找到关注关系");
}
} catch (Exception e) {
log.error("取消关注用户失败: followedId={}, error={}", followedId, e.getMessage(), e);
return Result.error("取消关注失败: " + e.getMessage());
}
}
/**
* 导出excel
*
* @param request
* @param aiolUserFollow
*/
@RequiresPermissions("aiol:aiol_user_follow:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolUserFollow aiolUserFollow) {
return super.exportXls(request, aiolUserFollow, AiolUserFollow.class, "关注关系");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_user_follow:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolUserFollow.class);
}
}

View File

@ -0,0 +1,182 @@
package org.jeecg.modules.aiol.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.aiol.entity.AiolUserInfo;
import org.jeecg.modules.aiol.service.IAiolUserInfoService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 用户信息
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Tag(name="用户信息")
@RestController
@RequestMapping("/aiol/aiolUserInfo")
@Slf4j
public class AiolUserInfoController extends JeecgController<AiolUserInfo, IAiolUserInfoService> {
@Autowired
private IAiolUserInfoService aiolUserInfoService;
/**
* 分页列表查询
*
* @param aiolUserInfo
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "用户信息-分页列表查询")
@Operation(summary="用户信息-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolUserInfo>> queryPageList(AiolUserInfo aiolUserInfo,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolUserInfo> queryWrapper = QueryGenerator.initQueryWrapper(aiolUserInfo, req.getParameterMap());
Page<AiolUserInfo> page = new Page<AiolUserInfo>(pageNo, pageSize);
IPage<AiolUserInfo> pageList = aiolUserInfoService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolUserInfo
* @return
*/
@AutoLog(value = "用户信息-添加")
@Operation(summary="用户信息-添加")
@RequiresPermissions("aiol:aiol_user_info:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolUserInfo aiolUserInfo) {
aiolUserInfoService.save(aiolUserInfo);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param aiolUserInfo
* @return
*/
@AutoLog(value = "用户信息-编辑")
@Operation(summary="用户信息-编辑")
@RequiresPermissions("aiol:aiol_user_info:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AiolUserInfo aiolUserInfo) {
aiolUserInfoService.updateById(aiolUserInfo);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "用户信息-通过id删除")
@Operation(summary="用户信息-通过id删除")
@RequiresPermissions("aiol:aiol_user_info:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
aiolUserInfoService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "用户信息-批量删除")
@Operation(summary="用户信息-批量删除")
@RequiresPermissions("aiol:aiol_user_info:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.aiolUserInfoService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "用户信息-通过id查询")
@Operation(summary="用户信息-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolUserInfo> queryById(@RequestParam(name="id",required=true) String id) {
AiolUserInfo aiolUserInfo = aiolUserInfoService.getById(id);
if(aiolUserInfo==null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolUserInfo);
}
/**
* 导出excel
*
* @param request
* @param aiolUserInfo
*/
@RequiresPermissions("aiol:aiol_user_info:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolUserInfo aiolUserInfo) {
return super.exportXls(request, aiolUserInfo, AiolUserInfo.class, "用户信息");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_user_info:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolUserInfo.class);
}
}

View File

@ -0,0 +1,239 @@
package org.jeecg.modules.aiol.controller;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.shiro.IgnoreAuth;
import org.jeecg.modules.aiol.entity.AiolViewLog;
import org.jeecg.modules.aiol.service.IAiolViewLogService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 访问日志
* @Author: jeecg-boot
* @Date: 2025-09-26
* @Version: V1.0
*/
@Tag(name="访问日志")
@RestController
@RequestMapping("/aiol/aiolViewLog")
@Slf4j
public class AiolViewLogController extends JeecgController<AiolViewLog, IAiolViewLogService> {
@Autowired
private IAiolViewLogService aiolViewLogService;
/**
* 分页列表查询
*
* @param aiolViewLog
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "访问日志-分页列表查询")
@Operation(summary="访问日志-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AiolViewLog>> queryPageList(AiolViewLog aiolViewLog,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AiolViewLog> queryWrapper = QueryGenerator.initQueryWrapper(aiolViewLog, req.getParameterMap());
Page<AiolViewLog> page = new Page<AiolViewLog>(pageNo, pageSize);
IPage<AiolViewLog> pageList = aiolViewLogService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param aiolViewLog
* @return
*/
@AutoLog(value = "访问日志-添加")
@Operation(summary="访问日志-添加")
@RequiresPermissions("aiol:aiol_view_log:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolViewLog aiolViewLog) {
aiolViewLogService.save(aiolViewLog);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param aiolViewLog
* @return
*/
@AutoLog(value = "访问日志-编辑")
@Operation(summary="访问日志-编辑")
@RequiresPermissions("aiol:aiol_view_log:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AiolViewLog aiolViewLog) {
aiolViewLogService.updateById(aiolViewLog);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "访问日志-通过id删除")
@Operation(summary="访问日志-通过id删除")
@RequiresPermissions("aiol:aiol_view_log:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
aiolViewLogService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "访问日志-批量删除")
@Operation(summary="访问日志-批量删除")
@RequiresPermissions("aiol:aiol_view_log:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.aiolViewLogService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "访问日志-通过id查询")
@Operation(summary="访问日志-通过id查询")
@GetMapping(value = "/queryById")
public Result<AiolViewLog> queryById(@RequestParam(name="id",required=true) String id) {
AiolViewLog aiolViewLog = aiolViewLogService.getById(id);
if(aiolViewLog==null) {
return Result.error("未找到对应数据");
}
return Result.OK(aiolViewLog);
}
/**
* 导出excel
*
* @param request
* @param aiolViewLog
*/
@RequiresPermissions("aiol:aiol_view_log:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AiolViewLog aiolViewLog) {
return super.exportXls(request, aiolViewLog, AiolViewLog.class, "访问日志");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("aiol:aiol_view_log:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AiolViewLog.class);
}
/**
* 记录实体访问量
*
* @param entityType 实体类型video或course
* @param entityId 实体ID
* @param request HTTP请求对象
* @return
*/
@AutoLog(value = "访问日志-记录实体访问量")
@Operation(summary="记录实体访问量")
@IgnoreAuth
@PostMapping(value = "/recordView")
public Result<String> recordView(@RequestParam(name="type", required=true) String entityType,
@RequestParam(name="id", required=true) String entityId,
HttpServletRequest request) {
// 验证实体类型
if (!"video".equals(entityType) && !"course".equals(entityType)) {
return Result.error("实体类型只能是video或course");
}
// 从Headers获取用户ID
String userId = request.getHeader("userId");
if (oConvertUtils.isEmpty(userId)) {
userId = null; // 未登录用户置空
}
// 创建访问日志记录
AiolViewLog viewLog = new AiolViewLog();
viewLog.setEntityType(entityType);
viewLog.setEntityId(entityId);
viewLog.setUserId(userId);
viewLog.setViewTime(new Date());
// 保存访问日志
aiolViewLogService.save(viewLog);
return Result.OK("访问记录成功!");
}
/**
* 查看实体访问量
*
* @param entityId 实体ID
* @return
*/
@Operation(summary="查看实体访问量")
@IgnoreAuth
@GetMapping(value = "/getViewCount")
public Result<Long> getViewCount(@RequestParam(name="id", required=true) String entityId) {
QueryWrapper<AiolViewLog> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("entity_id", entityId);
Long viewCount = aiolViewLogService.count(queryWrapper);
return Result.OK(viewCount);
}
}

View File

@ -0,0 +1,20 @@
package org.jeecg.modules.aiol.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.jeecg.modules.aiol.entity.AiolCourse;
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(description = "课程保存DTO扩展课程分类和班级")
public class AiolCourseSaveDTO extends AiolCourse {
@Schema(description = "课程分类ID支持多个用英文逗号分割")
private String categoryId;
@Schema(description = "班级ID支持多个用英文逗号分割")
private String classId;
}

View File

@ -0,0 +1,24 @@
package org.jeecg.modules.aiol.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.jeecg.modules.aiol.entity.AiolCourseSection;
/**
* @Description: 课程章节DTO
* @Author: jeecg-boot
* @Date: 2025-01-20
* @Version: V1.0
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(description = "课程章节DTO")
public class AiolCourseSectionDTO extends AiolCourseSection {
/**
* 资源ID
*/
@Schema(description = "资源ID")
private String targetId;
}

View File

@ -0,0 +1,18 @@
package org.jeecg.modules.aiol.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.jeecg.modules.aiol.entity.AiolCourse;
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(description = "课程DTO包含分类ID和班级ID逗号分隔")
public class AiolCourseWithLinkDTO extends AiolCourse {
@Schema(description = "课程分类ID多个以逗号分隔")
private String categoryId;
@Schema(description = "班级ID多个以逗号分隔")
private String classId;
}

View File

@ -0,0 +1,21 @@
package org.jeecg.modules.aiol.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.jeecg.modules.aiol.entity.AiolDiscussion;
/**
* @Description: 讨论保存DTO
* @Author: jeecg-boot
* @Date: 2025-01-16
* @Version: V1.0
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "讨论保存DTO")
public class AiolDiscussionSaveDTO extends AiolDiscussion {
@Schema(description = "关联的章节ID")
private String sectionId;
}

View File

@ -0,0 +1,16 @@
package org.jeecg.modules.aiol.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.jeecg.modules.aiol.entity.AiolHomework;
/**
* 作业保存DTO
* 继承AiolHomework实体增加班级ID课程ID章节ID字段
*/
@Data
@Schema(description = "作业保存DTO")
public class AiolHomeworkSaveDTO extends AiolHomework {
@Schema(description = "章节ID")
private String sectionId;
}

View File

@ -0,0 +1,21 @@
package org.jeecg.modules.aiol.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.jeecg.modules.aiol.entity.AiolChat;
/**
* @Description: 会话详情DTO包含未读消息数
* @Author: jeecg-boot
* @Date: 2025-01-16
* @Version: V1.0
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "会话详情,包含未读消息数")
public class ChatWithUnreadCountDTO extends AiolChat {
@Schema(description = "未读消息数")
private Integer unreadCount;
}

View File

@ -0,0 +1,25 @@
package org.jeecg.modules.aiol.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.jeecg.modules.aiol.entity.AiolComment;
import java.util.List;
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(description = "评论信息(包含用户信息)")
public class CommentWithUserInfo extends AiolComment {
@Schema(description = "用户姓名")
private String userName;
@Schema(description = "用户头像")
private String userAvatar;
@Schema(description = "用户标签")
private String userTag;
@Schema(description = "子评论列表")
private List<CommentWithUserInfo> replies;
}

View File

@ -0,0 +1,21 @@
package org.jeecg.modules.aiol.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.jeecg.modules.aiol.entity.AiolCourse;
import java.util.List;
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(description = "课程信息(包含讲师信息)")
public class CourseWithTeacherInfo extends AiolCourse {
@Schema(description = "授课讲师列表")
private List<TeacherInfo> teacherList;
/**
* 是否已报名该课程
*/
@Schema(description = "是否已报名该课程")
private Boolean isEnrolled = false;
}

View File

@ -0,0 +1,24 @@
package org.jeecg.modules.aiol.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.jeecg.modules.aiol.entity.AiolDiscussion;
/**
* @Description: 讨论详情DTO
* @Author: jeecg-boot
* @Date: 2025-01-16
* @Version: V1.0
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "讨论详情")
public class DiscussionWithSectionDTO extends AiolDiscussion {
@Schema(description = "关联的章节ID")
private String sectionId;
@Schema(description = "章节名称")
private String sectionName;
}

View File

@ -0,0 +1,49 @@
package org.jeecg.modules.aiol.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Date;
/**
* 编辑个人信息DTO
*/
@Data
@Schema(description = "编辑个人信息DTO")
public class EditProfileDTO {
@Schema(description = "真实姓名")
private String realname;
@Schema(description = "头像")
private String avatar;
@Schema(description = "生日")
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", timezone = "GMT+8")
private Date birthday;
@Schema(description = "性别1男 2")
private Integer sex;
@Schema(description = "邮箱")
private String email;
@Schema(description = "电话")
private String phone;
@Schema(description = "介绍")
private String tag;
@Schema(description = "专业")
private String major;
@Schema(description = "学院")
private String college;
@Schema(description = "学历")
private String education;
@Schema(description = "职称")
private String title;
}

View File

@ -0,0 +1,19 @@
package org.jeecg.modules.aiol.dto;
import lombok.Data;
import org.jeecg.modules.aiol.entity.AiolExamAnswer;
import org.jeecg.modules.aiol.entity.AiolQuestion;
import java.util.List;
@Data
public class ExaminationResult {
//题目内容
private AiolQuestion question;
//选项答案
private List<?> answer;
//用户答案
private AiolExamAnswer userAnswer;
//子题目列表
private List<ExaminationResult> children;
}

View File

@ -0,0 +1,68 @@
package org.jeecg.modules.aiol.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.jeecg.modules.aiol.entity.AiolHomeworkSubmit;
import java.util.Date;
/**
* @Description: 作业提交详情DTO
* @Author: jeecg-boot
* @Date: 2025-01-16
* @Version: V1.0
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "作业提交详情")
public class HomeworkSubmitDetailDTO extends AiolHomeworkSubmit {
// 作业信息
@Schema(description = "作业标题")
private String homeworkTitle;
@Schema(description = "作业说明")
private String homeworkDescription;
@Schema(description = "作业满分")
private Integer homeworkMaxScore;
@Schema(description = "作业及格分数")
private Integer homeworkPassScore;
@Schema(description = "作业开始时间")
private Date homeworkStartTime;
@Schema(description = "作业结束时间")
private Date homeworkEndTime;
@Schema(description = "作业状态")
private Integer homeworkStatus;
@Schema(description = "作业通知时间")
private Integer homeworkNotifyTime;
@Schema(description = "是否允许补交")
private Integer homeworkAllowMakeup;
@Schema(description = "补交截止时间")
private Date homeworkMakeupTime;
// 学生用户信息
@Schema(description = "学生用户名")
private String studentUsername;
@Schema(description = "学生真实姓名")
private String studentRealname;
@Schema(description = "学生头像")
private String studentAvatar;
// 班级信息
@Schema(description = "班级ID")
private String classId;
@Schema(description = "班级名称")
private String className;
}

View File

@ -0,0 +1,29 @@
package org.jeecg.modules.aiol.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.jeecg.modules.aiol.entity.AiolHomework;
import java.util.List;
/**
* @Description: 作业详情DTO
* @Author: jeecg-boot
* @Date: 2025-01-16
* @Version: V1.0
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "作业详情")
public class HomeworkWithDetailsDTO extends AiolHomework {
@Schema(description = "班级名称列表")
private List<String> classNames;
@Schema(description = "章节ID")
private String sectionId;
@Schema(description = "章节标题")
private String sectionTitle;
}

View File

@ -0,0 +1,17 @@
package org.jeecg.modules.aiol.dto;
import lombok.Data;
import org.jeecg.modules.aiol.entity.AiolQuestion;
import java.util.ArrayList;
import java.util.List;
@Data
public class QuestionAnswerDTO {
//题目内容
private AiolQuestion question;
//答案
private List<?> answer;
//子题目列表
private List<QuestionAnswerDTO> children = new ArrayList<>();
}

View File

@ -0,0 +1,20 @@
package org.jeecg.modules.aiol.dto;
import lombok.Data;
import org.jeecg.modules.aiol.entity.AiolExamAnswer;
import org.jeecg.modules.aiol.entity.AiolQuestion;
import org.jeecg.modules.aiol.entity.AiolQuestionAnswer;
import java.util.List;
@Data
public class QuestionAnswerUser {
//题目内容
private AiolQuestion question;
//答案
private AiolQuestionAnswer answer;
//用户答案
private AiolExamAnswer userAnswer;
//满分
private Double fullMark;
}

View File

@ -0,0 +1,74 @@
package org.jeecg.modules.aiol.dto;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import org.jeecgframework.poi.excel.annotation.Excel;
import java.util.ArrayList;
import java.util.List;
@Data
public class QuestionExcelDTO {
@Excel(name = "题目ID", width = 15)
private String questionId;
@Excel(name = "父题目ID", width = 15)
private String parentId;
@Excel(name = "题目类型", width = 15)
private String type;
@Excel(name = "题干", width = 50)
private String content;
@Excel(name = "选项A", width = 30)
private String optionA;
@Excel(name = "选项B", width = 30)
private String optionB;
@Excel(name = "选项C", width = 30)
private String optionC;
@Excel(name = "选项D", width = 30)
private String optionD;
@Excel(name = "选项E", width = 30)
private String optionE;
@Excel(name = "选项F", width = 30)
private String optionF;
@Excel(name = "选项G", width = 30)
private String optionG;
@Excel(name = "正确答案", width = 15)
private String correctAnswers;
@Excel(name = "题目解析", width = 50)
private String analysis;
@Excel(name = "难度", width = 15)
private String difficulty;
@Excel(name = "程度", width = 15)
private String degree;
@Excel(name = "能力", width = 15)
private String ability;
@Excel(name = "分值", width = 15)
private Double score;
// 内部使用存储解析后的选项列表
private List<OptionEntry> options = new ArrayList<>();
@Data
public static class OptionEntry {
private String content;
private boolean isCorrect;
private Integer orderNo;
}
}

View File

@ -0,0 +1,15 @@
package org.jeecg.modules.aiol.dto;
import org.jeecg.modules.aiol.entity.AiolQuestion;
public class QuestionWithCreator extends AiolQuestion {
private String createByName;
public String getCreateByName() {
return createByName;
}
public void setCreateByName(String createByName) {
this.createByName = createByName;
}
}

View File

@ -0,0 +1,25 @@
package org.jeecg.modules.aiol.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "学生提交作业")
public class StudentSubmitHomework {
/**作业id*/
@Schema(description = "作业id")
private String homeworkId;
/**学生id*/
@Schema(description = "学生id")
private String studentId;
/**作业内容*/
@Schema(description = "作业内容")
private String content;
/**附件*/
@Schema(description = "附件")
private String attachment;
/**状态*/
@Schema(description = "状态")
private Integer status;
}

View File

@ -0,0 +1,26 @@
package org.jeecg.modules.aiol.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "讲师信息")
public class TeacherInfo {
@Schema(description = "讲师ID")
private String id;
@Schema(description = "讲师姓名")
private String name;
@Schema(description = "讲师头像")
private String avatar;
@Schema(description = "职称")
private String title;
@Schema(description = "标签")
private String tag;
@Schema(description = "显示顺序")
private Integer sortOrder;
}

View File

@ -0,0 +1,74 @@
package org.jeecg.modules.aiol.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Date;
import java.util.List;
@Data
@Schema(description = "用户信息响应对象")
public class UserInfoResponse {
@Schema(description = "用户基本信息")
private BaseInfo baseInfo;
@Schema(description = "用户角色列表")
private List<String> roles;
@Schema(description = "扩展用户信息")
private ExtendedInfo extendedInfo;
@Data
@Schema(description = "用户基本信息")
public static class BaseInfo {
@Schema(description = "用户ID")
private String id;
@Schema(description = "用户名")
private String username;
@Schema(description = "真实姓名")
private String realname;
@Schema(description = "头像")
private String avatar;
@Schema(description = "手机号")
private String phone;
@Schema(description = "邮箱")
private String email;
@Schema(description = "性别(1-男2-女)")
private Integer sex;
@Schema(description = "生日")
private Date birthday;
@Schema(description = "状态(1-正常2-冻结)")
private Integer status;
}
@Data
@Schema(description = "扩展用户信息")
public static class ExtendedInfo {
@Schema(description = "专业")
private String major;
@Schema(description = "学院")
private String college;
@Schema(description = "学历")
private String education;
@Schema(description = "职称")
private String title;
@Schema(description = "标签")
private String tag;
@Schema(description = "显示顺序")
private Integer sortOrder;
}
}

View File

@ -0,0 +1,18 @@
package org.jeecg.modules.aiol.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class UserPermission {
@Schema(description = "用户ID")
private String userId;
@Schema(description = "用户名")
private String userName;
@Schema(description = "用户真实姓名")
private String realName;
@Schema(description = "用户头像")
private String userAvatar;
@Schema(description = "用户是否有权限")
private boolean permission;
}

View File

@ -0,0 +1,105 @@
package org.jeecg.modules.aiol.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 活动
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Data
@TableName("aiol_activity")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="活动")
public class AiolActivity implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**标题*/
@Excel(name = "标题", width = 15)
@Schema(description = "标题")
private java.lang.String title;
/**介绍*/
@Excel(name = "介绍", width = 15)
@Schema(description = "介绍")
private java.lang.String introduction;
/**说明图片*/
@Excel(name = "说明图片", width = 15)
@Schema(description = "说明图片")
private java.lang.String imgs;
/**头图*/
@Excel(name = "头图", width = 15)
@Schema(description = "头图")
private java.lang.String banner;
/**介绍视频*/
@Excel(name = "介绍视频", width = 15)
@Schema(description = "介绍视频")
private java.lang.String video;
/**报名人数上限*/
@Excel(name = "报名人数上限", width = 15)
@Schema(description = "报名人数上限")
private java.lang.Integer maxNum;
/**开始时间*/
@Excel(name = "开始时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "开始时间")
private java.util.Date startTime;
/**结束时间*/
@Excel(name = "结束时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "结束时间")
private java.util.Date endTime;
/**扩展字段*/
@Excel(name = "扩展字段", width = 15)
@Schema(description = "扩展字段")
private java.lang.String extra;
/**附件*/
@Excel(name = "附件", width = 15)
@Schema(description = "附件")
private java.lang.String attachment;
/**状态*/
@Excel(name = "状态", width = 15, dicCode = "course_status")
@Dict(dicCode = "course_status")
@Schema(description = "状态")
private java.lang.String status;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建日期")
private java.util.Date createTime;
/**更新人*/
@Schema(description = "更新人")
private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新日期")
private java.util.Date updateTime;
}

View File

@ -0,0 +1,86 @@
package org.jeecg.modules.aiol.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 活动报名
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Data
@TableName("aiol_activity_signup")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="活动报名")
public class AiolActivitySignup implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**姓名*/
@Excel(name = "姓名", width = 15)
@Schema(description = "姓名")
private java.lang.String name;
/**年龄*/
@Excel(name = "年龄", width = 15)
@Schema(description = "年龄")
private java.lang.String age;
/**手机号*/
@Excel(name = "手机号", width = 15)
@Schema(description = "手机号")
private java.lang.String phone;
/**邮箱*/
@Excel(name = "邮箱", width = 15)
@Schema(description = "邮箱")
private java.lang.String email;
/**扩展字段*/
@Excel(name = "扩展字段", width = 15)
@Schema(description = "扩展字段")
private java.lang.String extra;
/**附件*/
@Excel(name = "附件", width = 15)
@Schema(description = "附件")
private java.lang.String attachment;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建日期")
private java.util.Date createTime;
/**更新人*/
@Schema(description = "更新人")
private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新日期")
private java.util.Date updateTime;
/**活动id*/
@Schema(description = "活动id")
private java.lang.String activityId;
/**用户id*/
@Schema(description = "用户id")
private java.lang.String userId;
}

View File

@ -0,0 +1,80 @@
package org.jeecg.modules.aiol.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 会话
* @Author: jeecg-boot
* @Date: 2025-09-11
* @Version: V1.0
*/
@Data
@TableName("aiol_chat")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="会话")
public class AiolChat implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**会话类型*/
@Excel(name = "会话类型", width = 15)
@Schema(description = "会话类型")
private java.lang.Integer type;
/**群聊名称*/
@Excel(name = "群聊名称", width = 15)
@Schema(description = "群聊名称")
private java.lang.String name;
/**群聊头像*/
@Excel(name = "群聊头像", width = 15)
@Schema(description = "群聊头像")
private java.lang.String avatar;
/**关联id*/
@Excel(name = "关联id", width = 15)
@Schema(description = "关联id")
private java.lang.String refId;
/**是否全员禁言*/
@Excel(name = "是否全员禁言", width = 15)
@Schema(description = "是否全员禁言")
private java.lang.Integer izAllMuted;
/**是否显示教师标签*/
@Excel(name = "是否显示教师标签", width = 15)
@Schema(description = "是否显示教师标签")
private java.lang.Integer showLabel;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建日期")
private java.util.Date createTime;
/**更新人*/
@Schema(description = "更新人")
private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新日期")
private java.util.Date updateTime;
}

View File

@ -0,0 +1,80 @@
package org.jeecg.modules.aiol.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 会话用户
* @Author: jeecg-boot
* @Date: 2025-09-11
* @Version: V1.0
*/
@Data
@TableName("aiol_chat_member")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="会话用户")
public class AiolChatMember implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**会话id*/
@Excel(name = "会话id", width = 15)
@Schema(description = "会话id")
private java.lang.String chatId;
/**用户id*/
@Excel(name = "用户id", width = 15)
@Schema(description = "用户id")
private java.lang.String userId;
/**成员角色*/
@Excel(name = "成员角色", width = 15)
@Schema(description = "成员角色")
private java.lang.Integer role;
/**是否禁言*/
@Excel(name = "是否禁言", width = 15)
@Schema(description = "是否禁言")
private java.lang.Integer izMuted;
/**是否免打扰*/
@Excel(name = "是否免打扰", width = 15)
@Schema(description = "是否免打扰")
private java.lang.Integer izNotDisturb;
/**最后已读消息id*/
@Excel(name = "最后已读消息id", width = 15)
@Schema(description = "最后已读消息id")
private java.lang.String lastReadMsgId;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建日期")
private java.util.Date createTime;
/**更新人*/
@Schema(description = "更新人")
private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新日期")
private java.util.Date updateTime;
}

View File

@ -0,0 +1,88 @@
package org.jeecg.modules.aiol.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 会话消息
* @Author: jeecg-boot
* @Date: 2025-09-11
* @Version: V1.0
*/
@Data
@TableName("aiol_chat_message")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="会话消息")
public class AiolChatMessage implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**会话id*/
@Excel(name = "会话id", width = 15)
@Schema(description = "会话id")
private java.lang.String chatId;
/**发送者id*/
@Excel(name = "发送者id", width = 15)
@Schema(description = "发送者id")
private java.lang.String senderId;
/**内容*/
@Excel(name = "内容", width = 15)
@Schema(description = "内容")
private java.lang.String content;
/**消息类型*/
@Excel(name = "消息类型", width = 15)
@Schema(description = "消息类型")
private java.lang.Integer messageType;
/**状态*/
@Excel(name = "状态", width = 15)
@Schema(description = "状态")
private java.lang.Integer status;
/**文件url*/
@Excel(name = "文件url", width = 15)
@Schema(description = "文件url")
private java.lang.String fileUrl;
/**文件名*/
@Excel(name = "文件名", width = 15)
@Schema(description = "文件名")
private java.lang.String fileName;
/**文件大小*/
@Excel(name = "文件大小", width = 15)
@Schema(description = "文件大小")
private java.lang.String fileSize;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建日期")
private java.util.Date createTime;
/**更新人*/
@Schema(description = "更新人")
private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新日期")
private java.util.Date updateTime;
}

View File

@ -0,0 +1,68 @@
package org.jeecg.modules.aiol.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: aiol_class
* @Author: jeecg-boot
* @Date: 2025-09-11
* @Version: V1.0
*/
@Data
@TableName("aiol_class")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="aiol_class")
public class AiolClass implements Serializable {
private static final long serialVersionUID = 1L;
/**id*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "id")
private java.lang.String id;
/**班级名*/
@Excel(name = "班级名", width = 15)
@Schema(description = "班级名")
private java.lang.String name;
/**课程id*/
@Excel(name = "课程id", width = 15)
@Schema(description = "课程id")
private java.lang.String courseId;
/**邀请码*/
@Excel(name = "邀请码", width = 15)
@Schema(description = "邀请码")
private java.lang.String inviteCode;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建日期")
private java.util.Date createTime;
/**更新人*/
@Schema(description = "更新人")
private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新日期")
private java.util.Date updateTime;
}

View File

@ -0,0 +1,68 @@
package org.jeecg.modules.aiol.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 班级学生
* @Author: jeecg-boot
* @Date: 2025-09-04
* @Version: V1.0
*/
@Data
@TableName("aiol_class_student")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="班级学生")
public class AiolClassStudent implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**班级id*/
@Excel(name = "班级id", width = 15)
@Schema(description = "班级id")
private java.lang.String classId;
/**学生id*/
@Excel(name = "学生id", width = 15)
@Schema(description = "学生id")
private java.lang.String studentId;
/**状态*/
@Excel(name = "状态", width = 15)
@Schema(description = "状态")
private java.lang.Integer status;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建日期")
private java.util.Date createTime;
/**更新人*/
@Schema(description = "更新人")
private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新日期")
private java.util.Date updateTime;
}

View File

@ -0,0 +1,84 @@
package org.jeecg.modules.aiol.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 评论
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Data
@TableName("aiol_comment")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="评论")
public class AiolComment implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**用户id*/
@Excel(name = "用户id", width = 15)
@Schema(description = "用户id")
private java.lang.String userId;
/**目标类型*/
@Excel(name = "目标类型", width = 15)
@Schema(description = "目标类型")
private java.lang.String targetType;
/**目标id*/
@Excel(name = "目标id", width = 15)
@Schema(description = "目标id")
private java.lang.String targetId;
/**内容*/
@Excel(name = "内容", width = 15)
@Schema(description = "内容")
private java.lang.String content;
/**图片*/
@Excel(name = "图片", width = 15)
@Schema(description = "图片")
private java.lang.String imgs;
/**是否置顶*/
@Excel(name = "是否置顶", width = 15)
@Schema(description = "是否置顶")
private java.lang.Integer izTop;
/**点赞数*/
@Excel(name = "点赞数", width = 15)
@Schema(description = "点赞数")
private java.lang.Integer likeCount;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建日期")
private java.util.Date createTime;
/**更新人*/
@Schema(description = "更新人")
private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新日期")
private java.util.Date updateTime;
}

View File

@ -0,0 +1,56 @@
package org.jeecg.modules.aiol.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 内容配置
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Data
@TableName("aiol_content_config")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="内容配置")
public class AiolContentConfig implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**配置标识*/
@Excel(name = "配置标识", width = 15)
@Schema(description = "配置标识")
private java.lang.String contentKey;
/**配置值*/
@Excel(name = "配置值", width = 15)
@Schema(description = "配置值")
private java.lang.String contentValue;
/**值类型*/
@Excel(name = "值类型", width = 15)
@Schema(description = "值类型")
private java.lang.String valueType;
/**描述*/
@Excel(name = "描述", width = 15)
@Schema(description = "描述")
private java.lang.String description;
}

View File

@ -0,0 +1,168 @@
package org.jeecg.modules.aiol.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 课程
* @Author: jeecg-boot
* @Date: 2025-09-17
* @Version: V1.0
*/
@Data
@TableName("aiol_course")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="课程")
public class AiolCourse implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**课程名*/
@Excel(name = "课程名", width = 15)
@Schema(description = "课程名")
private java.lang.String name;
/**封面图*/
@Excel(name = "封面图", width = 15)
@Schema(description = "封面图")
private java.lang.String cover;
/**介绍视频*/
@Excel(name = "介绍视频", width = 15)
@Schema(description = "介绍视频")
private java.lang.String video;
/**学校*/
@Excel(name = "学校", width = 15)
@Schema(description = "学校")
private java.lang.String school;
/**课程概述*/
@Excel(name = "课程概述", width = 15)
@Schema(description = "课程概述")
private java.lang.String description;
/**课程类型*/
@Excel(name = "课程类型", width = 15, dicCode = "course_type")
@Dict(dicCode = "course_type")
@Schema(description = "课程类型")
private java.lang.Integer type;
/**授课目标*/
@Excel(name = "授课目标", width = 15)
@Schema(description = "授课目标")
private java.lang.String target;
/**课程难度*/
@Excel(name = "课程难度", width = 15, dicCode = "course_difficulty")
@Dict(dicCode = "course_difficulty")
@Schema(description = "课程难度")
private java.lang.Integer difficulty;
/**所属专题*/
@Excel(name = "所属专题", width = 15, dicCode = "course_subject")
@Dict(dicCode = "course_subject")
@Schema(description = "所属专题")
private java.lang.String subject;
/**课程大纲*/
@Excel(name = "课程大纲", width = 15)
@Schema(description = "课程大纲")
private java.lang.String outline;
/**预备知识*/
@Excel(name = "预备知识", width = 15)
@Schema(description = "预备知识")
private java.lang.String prerequisite;
/**参考资料*/
@Excel(name = "参考资料", width = 15)
@Schema(description = "参考资料")
private java.lang.String reference;
/**学时安排*/
@Excel(name = "学时安排", width = 15)
@Schema(description = "学时安排")
private java.lang.String arrangement;
/**开课时间*/
@Excel(name = "开课时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "开课时间")
private java.util.Date startTime;
/**结课时间*/
@Excel(name = "结课时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "结课时间")
private java.util.Date endTime;
/**已报名人数*/
@Excel(name = "已报名人数", width = 15)
@Schema(description = "已报名人数")
private java.lang.Integer enrollCount;
/**最大报名人数*/
@Excel(name = "最大报名人数", width = 15)
@Schema(description = "最大报名人数")
private java.lang.Integer maxEnroll;
/**状态*/
@Excel(name = "状态", width = 15, dicCode = "course_status")
@Dict(dicCode = "course_status")
@Schema(description = "状态")
private java.lang.Integer status;
/**常见问题*/
@Excel(name = "常见问题", width = 15)
@Schema(description = "常见问题")
private java.lang.String question;
/**是否ai伴学模式1*/
@Excel(name = "是否ai伴学模式1", width = 15)
@Schema(description = "是否ai伴学模式1")
private java.lang.Integer izAi;
/**离开页面是否暂停视频播放*/
@Excel(name = "离开页面是否暂停视频播放", width = 15)
@Schema(description = "离开页面是否暂停视频播放")
private java.lang.Integer pauseExit;
/**是否允许倍速播放*/
@Excel(name = "是否允许倍速播放", width = 15)
@Schema(description = "是否允许倍速播放")
private java.lang.Integer allowSpeed;
/**是否显示字幕*/
@Excel(name = "是否显示字幕", width = 15)
@Schema(description = "是否显示字幕")
private java.lang.Integer showSubtitle;
/**上架状态*/
@Excel(name = "上架状态", width = 15)
@Schema(description = "上架状态")
private java.lang.Integer publishStatus;
/**学期*/
@Excel(name = "学期", width = 15)
@Schema(description = "学期")
private java.lang.String semester;
/**是否允许下载*/
@Excel(name = "是否允许下载", width = 15)
@Schema(description = "是否允许下载")
private java.lang.Integer allowDownload;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**创建时间*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建时间")
private java.util.Date createTime;
/**更新人*/
@Schema(description = "更新人")
private java.lang.String updateBy;
/**更新时间*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新时间")
private java.util.Date updateTime;
}

View File

@ -0,0 +1,64 @@
package org.jeecg.modules.aiol.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 课程分类
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Data
@TableName("aiol_course_category")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="课程分类")
public class AiolCourseCategory implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**分类名*/
@Excel(name = "分类名", width = 15)
@Schema(description = "分类名")
private java.lang.String name;
/**排序*/
@Excel(name = "排序", width = 15)
@Schema(description = "排序")
private java.lang.Integer sortOrder;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**创建时间*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建时间")
private java.util.Date createTime;
/**更新人*/
@Schema(description = "更新人")
private java.lang.String updateBy;
/**更新时间*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新时间")
private java.util.Date updateTime;
}

View File

@ -0,0 +1,82 @@
package org.jeecg.modules.aiol.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 课程章节
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Data
@TableName("aiol_course_section")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="课程章节")
public class AiolCourseSection implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**课程id*/
@Excel(name = "课程id", width = 15)
@Schema(description = "课程id")
private java.lang.String courseId;
/**章节名*/
@Excel(name = "章节名", width = 15)
@Schema(description = "章节名")
private java.lang.String name;
/**章节类型*/
@Excel(name = "章节类型", width = 15, dicCode = "course_section_type")
@Dict(dicCode = "course_section_type")
@Schema(description = "章节类型")
private java.lang.Integer type;
/**排序号*/
@Excel(name = "排序号", width = 15)
@Schema(description = "排序号")
private java.lang.Integer sortOrder;
/**父章节id*/
@Excel(name = "父章节id", width = 15)
@Schema(description = "父章节id")
private java.lang.String parentId;
/**章节层级*/
@Excel(name = "章节层级", width = 15, dicCode = "course_section_level")
@Dict(dicCode = "course_section_level")
@Schema(description = "章节层级")
private java.lang.Integer level;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建日期")
private java.util.Date createTime;
/**更新人*/
@Schema(description = "更新人")
private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新日期")
private java.util.Date updateTime;
}

View File

@ -0,0 +1,64 @@
package org.jeecg.modules.aiol.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 课程报名
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Data
@TableName("aiol_course_signup")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="课程报名")
public class AiolCourseSignup implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**用户id*/
@Excel(name = "用户id", width = 15)
@Schema(description = "用户id")
private java.lang.String userId;
/**课程id*/
@Excel(name = "课程id", width = 15)
@Schema(description = "课程id")
private java.lang.String courseId;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建日期")
private java.util.Date createTime;
/**更新人*/
@Schema(description = "更新人")
private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新日期")
private java.util.Date updateTime;
}

View File

@ -0,0 +1,73 @@
package org.jeecg.modules.aiol.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 授课教师
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Data
@TableName("aiol_course_teacher")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="授课教师")
public class AiolCourseTeacher implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**课程id*/
@Excel(name = "课程id", width = 15)
@Schema(description = "课程id")
private java.lang.String courseId;
/**教师id*/
@Excel(name = "教师id", width = 15)
@Schema(description = "教师id")
private java.lang.String teacherId;
/**授课角色*/
@Excel(name = "授课角色", width = 15, dicCode = "course_role")
@Dict(dicCode = "course_role")
@Schema(description = "授课角色")
private java.lang.String role;
/**显示顺序*/
@Excel(name = "显示顺序", width = 15)
@Schema(description = "显示顺序")
private java.lang.Integer sortOrder;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建日期")
private java.util.Date createTime;
/**更新人*/
@Schema(description = "更新人")
private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新日期")
private java.util.Date updateTime;
}

View File

@ -0,0 +1,68 @@
package org.jeecg.modules.aiol.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 讨论
* @Author: jeecg-boot
* @Date: 2025-09-20
* @Version: V1.0
*/
@Data
@TableName("aiol_discussion")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="讨论")
public class AiolDiscussion implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**讨论标题*/
@Excel(name = "讨论标题", width = 15)
@Schema(description = "讨论标题")
private java.lang.String title;
/**讨论描述*/
@Excel(name = "讨论描述", width = 15)
@Schema(description = "讨论描述")
private java.lang.String description;
/**课程id*/
@Excel(name = "课程id", width = 15)
@Schema(description = "课程id")
private java.lang.String courseId;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建日期")
private java.util.Date createTime;
/**更新人*/
@Schema(description = "更新人")
private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新日期")
private java.util.Date updateTime;
}

View File

@ -0,0 +1,68 @@
package org.jeecg.modules.aiol.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 主体绑定
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Data
@TableName("aiol_entity_link")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="主体绑定")
public class AiolEntityLink implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**主体类型*/
@Excel(name = "主体类型", width = 15)
@Schema(description = "主体类型")
private java.lang.String sourceType;
/**主体id*/
@Excel(name = "主体id", width = 15)
@Schema(description = "主体id")
private java.lang.String sourceId;
/**内容类型*/
@Excel(name = "内容类型", width = 15)
@Schema(description = "内容类型")
private java.lang.String targetType;
/**内容id*/
@Excel(name = "内容id", width = 15)
@Schema(description = "内容id")
private java.lang.String targetId;
/**排序*/
@Excel(name = "排序", width = 15)
@Schema(description = "排序")
private java.lang.Integer sortOrder;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建日期")
private java.util.Date createTime;
}

View File

@ -0,0 +1,72 @@
package org.jeecg.modules.aiol.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 实体权限
* @Author: jeecg-boot
* @Date: 2025-09-05
* @Version: V1.0
*/
@Data
@TableName("aiol_entity_permission")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="实体权限")
public class AiolEntityPermission implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**实体类型*/
@Excel(name = "实体类型", width = 15)
@Schema(description = "实体类型")
private java.lang.String entityType;
/**实体id*/
@Excel(name = "实体id", width = 15)
@Schema(description = "实体id")
private java.lang.String entityId;
/**用户id*/
@Excel(name = "用户id", width = 15)
@Schema(description = "用户id")
private java.lang.String userId;
/**模式*/
@Excel(name = "模式", width = 15)
@Schema(description = "模式")
private java.lang.String mode;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建日期")
private java.util.Date createTime;
/**更新人*/
@Schema(description = "更新人")
private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新日期")
private java.util.Date updateTime;
}

View File

@ -0,0 +1,88 @@
package org.jeecg.modules.aiol.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 考试
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Data
@TableName("aiol_exam")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="考试")
public class AiolExam implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**考试名称*/
@Excel(name = "考试名称", width = 15)
@Schema(description = "考试名称")
private java.lang.String name;
/**试卷id*/
@Excel(name = "试卷id", width = 15)
@Schema(description = "试卷id")
private java.lang.String paperId;
/**开始时间*/
@Excel(name = "开始时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "开始时间")
private java.util.Date startTime;
/**结束时间*/
@Excel(name = "结束时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "结束时间")
private java.util.Date endTime;
/**考试时长*/
@Excel(name = "考试时长", width = 15)
@Schema(description = "考试时长")
private java.lang.Integer totalTime;
/**考试类型*/
@Excel(name = "考试类型", width = 15)
@Schema(description = "考试类型")
private java.lang.Integer type;
/**状态*/
@Excel(name = "状态", width = 15)
@Schema(description = "状态")
private java.lang.Integer status;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建日期")
private java.util.Date createTime;
/**更新人*/
@Schema(description = "更新人")
private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新日期")
private java.util.Date updateTime;
}

View File

@ -0,0 +1,84 @@
package org.jeecg.modules.aiol.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 考试答题
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Data
@TableName("aiol_exam_answer")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="考试答题")
public class AiolExamAnswer implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**考试id*/
@Excel(name = "考试id", width = 15)
@Schema(description = "考试id")
private java.lang.String examId;
/**用户id*/
@Excel(name = "用户id", width = 15)
@Schema(description = "用户id")
private java.lang.String userId;
/**复合题id*/
@Excel(name = "复合题id", width = 15)
@Schema(description = "复合题id")
private java.lang.String parentQuestionId;
/**题目id*/
@Excel(name = "题目id", width = 15)
@Schema(description = "题目id")
private java.lang.String questionId;
/**答案*/
@Excel(name = "答案", width = 15)
@Schema(description = "答案")
private java.lang.String answer;
/**是否正确*/
@Excel(name = "是否正确", width = 15)
@Schema(description = "是否正确")
private java.lang.Integer izCorrect;
/**题目得分*/
@Excel(name = "题目得分", width = 15)
@Schema(description = "题目得分")
private java.lang.Double score;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建日期")
private java.util.Date createTime;
/**更新人*/
@Schema(description = "更新人")
private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新日期")
private java.util.Date updateTime;
}

View File

@ -0,0 +1,86 @@
package org.jeecg.modules.aiol.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 考试记录
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Data
@TableName("aiol_exam_record")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="考试记录")
public class AiolExamRecord implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**考试id*/
@Excel(name = "考试id", width = 15)
@Schema(description = "考试id")
private java.lang.String examId;
/**用户id*/
@Excel(name = "用户id", width = 15)
@Schema(description = "用户id")
private java.lang.String userId;
/**总分*/
@Excel(name = "总分", width = 15)
@Schema(description = "总分")
private java.lang.Double totalScore;
/**状态*/
@Excel(name = "状态", width = 15)
@Schema(description = "状态")
private java.lang.Integer status;
/**提交时间*/
@Excel(name = "提交时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "提交时间")
private java.util.Date submittedAt;
/**ip地址*/
@Excel(name = "ip地址", width = 15)
@Schema(description = "ip地址")
private java.lang.String ipAddress;
/**设备信息*/
@Excel(name = "设备信息", width = 15)
@Schema(description = "设备信息")
private java.lang.String deviceInfo;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建日期")
private java.util.Date createTime;
/**更新人*/
@Schema(description = "更新人")
private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新日期")
private java.util.Date updateTime;
}

View File

@ -0,0 +1,115 @@
package org.jeecg.modules.aiol.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 作业
* @Author: jeecg-boot
* @Date: 2025-09-20
* @Version: V1.0
*/
@Data
@TableName("aiol_homework")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="作业")
public class AiolHomework implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**所属课程id*/
@Excel(name = "所属课程id", width = 15)
@Schema(description = "所属课程id")
private java.lang.String courseId;
/**班级id*/
@Excel(name = "班级id", width = 15)
@Schema(description = "班级id")
private java.lang.String classId;
/**标题*/
@Excel(name = "标题", width = 15)
@Schema(description = "标题")
private java.lang.String title;
/**说明*/
@Excel(name = "说明", width = 15)
@Schema(description = "说明")
private java.lang.String description;
/**附件*/
@Excel(name = "附件", width = 15)
@Schema(description = "附件")
private java.lang.String attachment;
/**满分*/
@Excel(name = "满分", width = 15)
@Schema(description = "满分")
private java.lang.Integer maxScore;
/**及格分数*/
@Excel(name = "及格分数", width = 15)
@Schema(description = "及格分数")
private java.lang.Integer passScore;
/**开始时间*/
@Excel(name = "开始时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "开始时间")
private java.util.Date startTime;
/**结束时间*/
@Excel(name = "结束时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "结束时间")
private java.util.Date endTime;
/**状态*/
@Excel(name = "状态", width = 15, dicCode = "course_status")
@Dict(dicCode = "course_status")
@Schema(description = "状态")
private java.lang.Integer status;
/**是否允许补交*/
@Excel(name = "是否允许补交", width = 15)
@Schema(description = "是否允许补交")
private java.lang.Integer allowMakeup;
/**补交截止时间*/
@Excel(name = "补交截止时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "补交截止时间")
private java.util.Date makeupTime;
/**作业通知时间*/
@Excel(name = "作业通知时间", width = 15)
@Schema(description = "作业通知时间")
private java.lang.Integer notifyTime;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建日期")
private java.util.Date createTime;
/**更新人*/
@Schema(description = "更新人")
private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新日期")
private java.util.Date updateTime;
}

View File

@ -0,0 +1,90 @@
package org.jeecg.modules.aiol.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 作业提交
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Data
@TableName("aiol_homework_submit")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="作业提交")
public class AiolHomeworkSubmit implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**作业id*/
@Excel(name = "作业id", width = 15)
@Schema(description = "作业id")
private java.lang.String homeworkId;
/**学生id*/
@Excel(name = "学生id", width = 15)
@Schema(description = "学生id")
private java.lang.String studentId;
/**作业内容*/
@Excel(name = "作业内容", width = 15)
@Schema(description = "作业内容")
private java.lang.String content;
/**附件*/
@Excel(name = "附件", width = 15)
@Schema(description = "附件")
private java.lang.String attachment;
/**得分*/
@Excel(name = "得分", width = 15)
@Schema(description = "得分")
private java.lang.Integer score;
/**批改意见*/
@Excel(name = "批改意见", width = 15)
@Schema(description = "批改意见")
private java.lang.String comment;
/**批改时间*/
@Excel(name = "批改时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "批改时间")
private java.util.Date gradedTime;
/**状态*/
@Excel(name = "状态", width = 15)
@Schema(description = "状态")
private java.lang.Integer status;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建日期")
private java.util.Date createTime;
/**更新人*/
@Schema(description = "更新人")
private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新日期")
private java.util.Date updateTime;
}

View File

@ -0,0 +1,80 @@
package org.jeecg.modules.aiol.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 学习进度
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Data
@TableName("aiol_learn_progress")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="学习进度")
public class AiolLearnProgress implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**用户id*/
@Excel(name = "用户id", width = 15)
@Schema(description = "用户id")
private java.lang.String userId;
/**课程id*/
@Excel(name = "课程id", width = 15)
@Schema(description = "课程id")
private java.lang.String courseId;
/**章节id*/
@Excel(name = "章节id", width = 15)
@Schema(description = "章节id")
private java.lang.String sectionId;
/**进度*/
@Excel(name = "进度", width = 15)
@Schema(description = "进度")
private java.lang.Integer progress;
/**时长*/
@Excel(name = "时长", width = 15)
@Schema(description = "时长")
private java.lang.Integer duration;
/**状态*/
@Excel(name = "状态", width = 15)
@Schema(description = "状态")
private java.lang.Integer status;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建日期")
private java.util.Date createTime;
/**更新人*/
@Schema(description = "更新人")
private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新日期")
private java.util.Date updateTime;
}

View File

@ -0,0 +1,72 @@
package org.jeecg.modules.aiol.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 前台菜单
* @Author: jeecg-boot
* @Date: 2025-09-29
* @Version: V1.0
*/
@Data
@TableName("aiol_menu")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="前台菜单")
public class AiolMenu implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**菜单项名称*/
@Excel(name = "菜单项名称", width = 15)
@Schema(description = "菜单项名称")
private java.lang.String name;
/**路径*/
@Excel(name = "路径", width = 15)
@Schema(description = "路径")
private java.lang.String path;
/**类型*/
@Excel(name = "类型", width = 15)
@Schema(description = "类型")
private java.lang.String type;
/**图标*/
@Excel(name = "图标", width = 15)
@Schema(description = "图标")
private java.lang.String icon;
/**父级菜单id*/
@Excel(name = "父级菜单id", width = 15)
@Schema(description = "父级菜单id")
private java.lang.String parentId;
/**排序号*/
@Excel(name = "排序号", width = 15)
@Schema(description = "排序号")
private java.lang.Integer sortOrder;
/**是否显示*/
@Excel(name = "是否显示", width = 15)
@Schema(description = "是否显示")
private java.lang.Integer izVisible;
/**权限标识符*/
@Excel(name = "权限标识符", width = 15)
@Schema(description = "权限标识符")
private java.lang.String permissionKey;
}

View File

@ -0,0 +1,84 @@
package org.jeecg.modules.aiol.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 试卷
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Data
@TableName("aiol_paper")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="试卷")
public class AiolPaper implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**试卷标题*/
@Excel(name = "试卷标题", width = 15)
@Schema(description = "试卷标题")
private java.lang.String title;
/**组卷模式*/
@Excel(name = "组卷模式", width = 15)
@Schema(description = "组卷模式")
private java.lang.Integer generateMode;
/**组卷规则*/
@Excel(name = "组卷规则", width = 15)
@Schema(description = "组卷规则")
private java.lang.String rules;
/**题库id*/
@Excel(name = "题库id", width = 15)
@Schema(description = "题库id")
private java.lang.String repoId;
/**总分*/
@Excel(name = "总分", width = 15)
@Schema(description = "总分")
private java.lang.Double totalScore;
/**及格分*/
@Excel(name = "及格分", width = 15)
@Schema(description = "及格分")
private java.lang.Double passScore;
/**是否需要批阅*/
@Excel(name = "是否需要批阅", width = 15)
@Schema(description = "是否需要批阅")
private java.lang.Integer requireReview;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建日期")
private java.util.Date createTime;
/**更新人*/
@Schema(description = "更新人")
private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新日期")
private java.util.Date updateTime;
}

View File

@ -0,0 +1,72 @@
package org.jeecg.modules.aiol.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 试卷试题
* @Author: jeecg-boot
* @Date: 2025-08-31
* @Version: V1.0
*/
@Data
@TableName("aiol_paper_question")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="试卷试题")
public class AiolPaperQuestion implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**试卷id*/
@Excel(name = "试卷id", width = 15)
@Schema(description = "试卷id")
private java.lang.String paperId;
/**题目id*/
@Excel(name = "题目id", width = 15)
@Schema(description = "题目id")
private java.lang.String questionId;
/**排序*/
@Excel(name = "排序", width = 15)
@Schema(description = "排序")
private java.lang.Integer orderNo;
/**分值*/
@Excel(name = "分值", width = 15)
@Schema(description = "分值")
private java.lang.Double score;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建日期")
private java.util.Date createTime;
/**更新人*/
@Schema(description = "更新人")
private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新日期")
private java.util.Date updateTime;
}

Some files were not shown because too many files have changed in this diff Show More