feat: 🎸 接口补充

This commit is contained in:
GoCo 2025-09-12 09:31:32 +08:00
parent 8e484ad8de
commit a4fe5c17e6
9 changed files with 359 additions and 11 deletions

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

@ -14,8 +14,10 @@ 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;
@ -56,6 +58,8 @@ public class AiolClassController extends JeecgController<AiolClass, IAiolClassSe
private SysUserMapper sysUserMapper;
@Autowired
private ISysUserService sysUserService;
@Autowired
private IAiolUserInfoService aiolUserInfoService;
/**
* 分页列表查询
@ -472,4 +476,77 @@ public class AiolClassController extends JeecgController<AiolClass, IAiolClassSe
return Result.error("查询班级列表失败: " + e.getMessage());
}
}
/**
* 新建学生并添加至班级
*
* 前端传参realName(姓名)studentNumber(学号)password(登录密码可选)school(所属学校)classId(所属班级ID)
*/
@AutoLog(value = "班级学生-新建学生并添加至班级")
@Operation(summary = "新建学生并添加至班级", description = "创建sysUser保存aiol_user_info的学校信息并写入aiol_class_student")
@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
// 检查是否已存在关系避免重复
QueryWrapper<AiolClassStudent> checkWrapper = new QueryWrapper<>();
checkWrapper.eq("class_id", classId).eq("student_id", created.getId());
AiolClassStudent exist = aiolClassStudentService.getOne(checkWrapper);
if (exist == null) {
AiolClassStudent relation = new AiolClassStudent();
relation.setClassId(classId);
relation.setStudentId(created.getId());
relation.setCreateBy(sysUser.getUsername());
relation.setCreateTime(new Date());
aiolClassStudentService.save(relation);
}
Map<String, Object> resp = new HashMap<>();
resp.put("userId", created.getId());
resp.put("username", created.getUsername());
resp.put("classId", classId);
resp.put("school", school);
return Result.OK(resp);
} catch (Exception e) {
log.error("新建学生并添加至班级失败: body={}, error={}", body, e.getMessage(), e);
return Result.error("新建学生并添加至班级失败: " + e.getMessage());
}
}
}

View File

@ -26,6 +26,7 @@ import org.jeecg.modules.aiol.service.IAiolClassService;
import org.jeecg.modules.aiol.service.IAiolEntityPermissionService;
import org.jeecg.modules.aiol.constant.EntityPermissionConst;
import org.jeecg.modules.aiol.mapper.AiolCourseSignupMapper;
import org.jeecg.modules.aiol.mapper.AiolCourseTeacherMapper;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.mapper.SysUserMapper;
import org.springframework.beans.BeanUtils;
@ -202,6 +203,8 @@ public class AiolCourseController extends JeecgController<AiolCourse, IAiolCours
private AiolCourseSignupMapper courseSignupMapper;
@Autowired
private SysUserMapper sysUserMapper;
@Autowired
private AiolCourseTeacherMapper courseTeacherMapper;
@GetMapping("/query_list")
@Operation(summary = "学员端-查询课程列表", description = "可根据分类、难度、专题进行检索三个参数可任意传递其中之一、之二或全部每个参数可传递多个值用英文逗号分割不传参或传递all则查询所有课程携带讲师信息")
@ -348,6 +351,92 @@ public class AiolCourseController extends JeecgController<AiolCourse, IAiolCours
return Result.OK(list);
}
@PostMapping("/{courseId}/bind_teacher")
@Operation(summary = "给课程绑定授课教师", description = "将指定教师绑定到课程传递courseId和userId")
public Result<String> bindTeacherToCourse(
@PathVariable(value = "courseId") String courseId,
@RequestParam(value = "userId") String userId,
HttpServletRequest request) {
try {
// 获取当前登录用户信息
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<AiolCourseTeacher> checkWrapper = new QueryWrapper<>();
checkWrapper.eq("course_id", courseId)
.eq("teacher_id", userId);
AiolCourseTeacher existingRelation = courseTeacherMapper.selectOne(checkWrapper);
if (existingRelation != null) {
return Result.error("该教师已绑定到此课程");
}
// 创建新的绑定关系
AiolCourseTeacher courseTeacher = new AiolCourseTeacher();
courseTeacher.setCourseId(courseId);
courseTeacher.setTeacherId(userId);
courseTeacher.setCreateBy(sysUser.getUsername());
courseTeacher.setCreateTime(new Date());
boolean saved = courseTeacherMapper.insert(courseTeacher) > 0;
if (saved) {
log.info("成功绑定教师到课程: courseId={}, teacherId={}, operator={}",
courseId, userId, username);
return Result.OK("绑定成功!");
} else {
return Result.error("绑定失败");
}
} catch (Exception e) {
log.error("绑定教师到课程失败: courseId={}, userId={}, error={}",
courseId, userId, e.getMessage(), e);
return Result.error("绑定失败: " + e.getMessage());
}
}
@DeleteMapping("/{courseId}/unbind_teacher")
@Operation(summary = "删除课程授课教师", description = "解除指定教师与课程的绑定关系传递courseId和userId")
public Result<String> unbindTeacherFromCourse(
@PathVariable(value = "courseId") String courseId,
@RequestParam(value = "userId") String userId,
HttpServletRequest request) {
try {
// 获取当前登录用户信息
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<AiolCourseTeacher> deleteWrapper = new QueryWrapper<>();
deleteWrapper.eq("course_id", courseId)
.eq("teacher_id", userId);
int deleted = courseTeacherMapper.delete(deleteWrapper);
if (deleted > 0) {
log.info("成功解除教师与课程的绑定: courseId={}, teacherId={}, operator={}",
courseId, userId, username);
return Result.OK("解绑成功!");
} else {
return Result.error("未找到绑定关系");
}
} catch (Exception e) {
log.error("解除教师与课程绑定失败: courseId={}, userId={}, error={}",
courseId, userId, e.getMessage(), e);
return Result.error("解绑失败: " + e.getMessage());
}
}
@PostMapping("/{courseId}/enroll")
@Operation(summary = "报名课程", description = "该接口需要携带用户登录token。根据课程id报名课程。返回值为报名结果报名成功返回success")
public Result<String> enrollCourse(@PathVariable(value = "courseId") String courseId, HttpServletRequest request,

View File

@ -15,7 +15,10 @@ 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.dto.AiolCourseSectionDTO;
import org.jeecg.modules.aiol.service.IAiolCourseSectionService;
import org.jeecg.modules.aiol.mapper.AiolEntityLinkMapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
@ -33,6 +36,8 @@ 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;
@ -51,6 +56,8 @@ import org.apache.shiro.authz.annotation.RequiresPermissions;
public class AiolCourseSectionController extends JeecgController<AiolCourseSection, IAiolCourseSectionService> {
@Autowired
private IAiolCourseSectionService aiolCourseSectionService;
@Autowired
private AiolEntityLinkMapper aiolEntityLinkMapper;
/**
* 分页列表查询
@ -79,32 +86,90 @@ public class AiolCourseSectionController extends JeecgController<AiolCourseSecti
/**
* 添加
*
* @param aiolCourseSection
* @param sectionDTO 课程章节DTO包含章节信息和资源关联信息
* @return
*/
@AutoLog(value = "课程章节-添加")
@Operation(summary="课程章节-添加")
@Operation(summary="课程章节-添加", description = "添加课程章节并关联资源支持resourceId和resourceType参数")
@RequiresPermissions("aiol:aiol_course_section:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AiolCourseSection aiolCourseSection) {
aiolCourseSectionService.save(aiolCourseSection);
@Transactional(rollbackFor = Exception.class)
public Result<String> add(@RequestBody AiolCourseSectionDTO sectionDTO) {
try {
// 1. 保存章节信息
aiolCourseSectionService.save(sectionDTO);
return Result.OK("添加成功!");
// 2. 处理资源关联
if (sectionDTO.getTargetId() != null && !sectionDTO.getTargetId().trim().isEmpty() &&
sectionDTO.getTargetType() != null && !sectionDTO.getTargetType().trim().isEmpty()) {
AiolEntityLink entityLink = new AiolEntityLink();
entityLink.setSourceType("course_section");
entityLink.setSourceId(sectionDTO.getId());
entityLink.setTargetType(sectionDTO.getTargetType());
entityLink.setTargetId(sectionDTO.getTargetId());
entityLink.setCreateBy(sectionDTO.getCreateBy());
entityLink.setCreateTime(new Date());
aiolEntityLinkMapper.insert(entityLink);
}
return Result.OK("添加成功!");
} catch (Exception e) {
log.error("添加课程章节失败: {}", e.getMessage(), e);
return Result.error("添加课程章节失败: " + e.getMessage());
}
}
/**
* 编辑
*
* @param aiolCourseSection
* @param sectionDTO 课程章节DTO包含章节信息和资源关联信息
* @return
*/
@AutoLog(value = "课程章节-编辑")
@Operation(summary="课程章节-编辑")
@Operation(summary="课程章节-编辑", description = "编辑课程章节并更新资源关联支持resourceId和resourceType参数")
@RequiresPermissions("aiol:aiol_course_section:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AiolCourseSection aiolCourseSection) {
aiolCourseSectionService.updateById(aiolCourseSection);
return Result.OK("编辑成功!");
@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.getTargetType() != null && !sectionDTO.getTargetType().trim().isEmpty()) {
// 先删除旧的关联关系
QueryWrapper<AiolEntityLink> deleteWrapper = new QueryWrapper<>();
deleteWrapper.eq("source_type", "course_section")
.eq("source_id", sectionDTO.getId());
aiolEntityLinkMapper.delete(deleteWrapper);
// 创建新的关联关系
AiolEntityLink entityLink = new AiolEntityLink();
entityLink.setSourceType("course_section");
entityLink.setSourceId(sectionDTO.getId());
entityLink.setTargetType(sectionDTO.getTargetType());
entityLink.setTargetId(sectionDTO.getTargetId());
entityLink.setCreateBy(sectionDTO.getUpdateBy());
entityLink.setCreateTime(new Date());
aiolEntityLinkMapper.insert(entityLink);
} else {
// 如果没有提供资源信息删除所有关联关系
QueryWrapper<AiolEntityLink> deleteWrapper = new QueryWrapper<>();
deleteWrapper.eq("source_type", "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());
}
}
/**

View File

@ -202,6 +202,68 @@ public class AiolUserController {
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

View File

@ -0,0 +1,30 @@
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;
/**
* 资源类型
*/
@Schema(description = "资源类型")
private String targetType;
}

View File

@ -22,7 +22,7 @@ import lombok.experimental.Accessors;
/**
* @Description: 课程
* @Author: jeecg-boot
* @Date: 2025-09-11
* @Date: 2025-09-12
* @Version: V1.0
*/
@Data
@ -137,6 +137,10 @@ public class AiolCourse implements Serializable {
@Excel(name = "是否显示字幕", width = 15)
@Schema(description = "是否显示字幕")
private java.lang.Integer showSubtitle;
/**上架状态*/
@Excel(name = "上架状态", width = 15)
@Schema(description = "上架状态")
private java.lang.Integer publishStatus;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;

View File

@ -259,6 +259,10 @@ public class AiolCourseServiceImpl extends ServiceImpl<AiolCourseMapper, AiolCou
queryWrapper.in("difficulty", difficultyList);
}
}
// 只查询已上架的课程
queryWrapper.eq("publishStatus", 1);
List<AiolCourse> courseList = courseMapper.selectList(queryWrapper);
// 构建包含讲师信息的课程列表

View File

@ -121,6 +121,11 @@ export const columns: BasicColumn[] = [
align:"center",
dataIndex: 'showSubtitle'
},
{
title: '上架状态',
align:"center",
dataIndex: 'publishStatus'
},
];
//
export const searchFormSchema: FormSchema[] = [
@ -268,6 +273,11 @@ export const formSchema: FormSchema[] = [
label: '是否显示字幕',
field: 'showSubtitle',
component: 'InputNumber',
},
{
label: '上架状态',
field: 'publishStatus',
component: 'InputNumber',
},
// TODO ID
{
@ -303,6 +313,7 @@ export const superQuerySchema = {
pauseExit: {title: '离开页面是否暂停视频播放',order: 20,view: 'number', type: 'number',},
allowSpeed: {title: '是否允许倍速播放',order: 21,view: 'number', type: 'number',},
showSubtitle: {title: '是否显示字幕',order: 22,view: 'number', type: 'number',},
publishStatus: {title: '上架状态',order: 23,view: 'number', type: 'number',},
};
/**