diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/constant/RoleConst.java b/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/constant/RoleConst.java new file mode 100644 index 00000000..75722490 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/constant/RoleConst.java @@ -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"; +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/controller/AiolClassController.java b/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/controller/AiolClassController.java index b91dba80..84c14638 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/controller/AiolClassController.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/controller/AiolClassController.java @@ -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> createStudentAndAddToClass(@RequestBody Map 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 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 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()); + } + } } diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/controller/AiolCourseController.java b/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/controller/AiolCourseController.java index 93973559..f1626c71 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/controller/AiolCourseController.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/controller/AiolCourseController.java @@ -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 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 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 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 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 enrollCourse(@PathVariable(value = "courseId") String courseId, HttpServletRequest request, diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/controller/AiolCourseSectionController.java b/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/controller/AiolCourseSectionController.java index fbf78c5e..0e2cd116 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/controller/AiolCourseSectionController.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/controller/AiolCourseSectionController.java @@ -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 { @Autowired private IAiolCourseSectionService aiolCourseSectionService; + @Autowired + private AiolEntityLinkMapper aiolEntityLinkMapper; /** * 分页列表查询 @@ -79,32 +86,90 @@ public class AiolCourseSectionController extends JeecgController add(@RequestBody AiolCourseSection aiolCourseSection) { - aiolCourseSectionService.save(aiolCourseSection); + @Transactional(rollbackFor = Exception.class) + public Result 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 edit(@RequestBody AiolCourseSection aiolCourseSection) { - aiolCourseSectionService.updateById(aiolCourseSection); - return Result.OK("编辑成功!"); + @Transactional(rollbackFor = Exception.class) + public Result 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 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 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()); + } } /** diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/controller/AiolUserController.java b/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/controller/AiolUserController.java index 2a36584b..8b976c00 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/controller/AiolUserController.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/controller/AiolUserController.java @@ -202,6 +202,68 @@ public class AiolUserController { return Result.OK(schools); } + @GetMapping("/teachers") + @Operation(summary = "查询所有教师用户", description = "支持按教师姓名和工号进行可选查询") + @IgnoreAuth + public Result>> queryAllTeachers( + @RequestParam(value = "realName", required = false) String realName, + @RequestParam(value = "workNo", required = false) String workNo) { + try { + // 构建查询条件 + LambdaQueryWrapper 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 teachers = sysUserService.list(queryWrapper); + + // 构建返回结果 + List> result = teachers.stream().map(teacher -> { + Map 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().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 diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/dto/AiolCourseSectionDTO.java b/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/dto/AiolCourseSectionDTO.java new file mode 100644 index 00000000..e717ebd5 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/dto/AiolCourseSectionDTO.java @@ -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; +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/entity/AiolCourse.java b/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/entity/AiolCourse.java index 796d1edb..0cd76a80 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/entity/AiolCourse.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/entity/AiolCourse.java @@ -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; diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/service/impl/AiolCourseServiceImpl.java b/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/service/impl/AiolCourseServiceImpl.java index 3df0bee6..9545b011 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/service/impl/AiolCourseServiceImpl.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/service/impl/AiolCourseServiceImpl.java @@ -259,6 +259,10 @@ public class AiolCourseServiceImpl extends ServiceImpl courseList = courseMapper.selectList(queryWrapper); // 构建包含讲师信息的课程列表 diff --git a/jeecgboot-vue3/src/views/aiol/AiolCourse.data.ts b/jeecgboot-vue3/src/views/aiol/AiolCourse.data.ts index d10e62ab..9eb44bbf 100644 --- a/jeecgboot-vue3/src/views/aiol/AiolCourse.data.ts +++ b/jeecgboot-vue3/src/views/aiol/AiolCourse.data.ts @@ -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',}, }; /**