diff --git a/RuoYi-Vue/ruoyi-admin/src/main/java/com/ruoyi/web/controller/train/TrainAccountBindingAdminController.java b/RuoYi-Vue/ruoyi-admin/src/main/java/com/ruoyi/web/controller/train/TrainAccountBindingAdminController.java new file mode 100644 index 000000000..d3f8e8ea9 --- /dev/null +++ b/RuoYi-Vue/ruoyi-admin/src/main/java/com/ruoyi/web/controller/train/TrainAccountBindingAdminController.java @@ -0,0 +1,120 @@ +package com.ruoyi.web.controller.train; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.train.domain.dto.UserBindingDTO; +import com.ruoyi.train.domain.vo.BindableUserVO; +import com.ruoyi.web.service.train.TrainAccountBindingAdminService; + +/** + * 管理员端业务身份系统账号关联 Controller。 + * + *
为管理员提供受控的系统账号与教师/学员业务档案关联与解绑功能, + * 满足 Wave 2.1 / Wave 3 真实三角色生命周期管理流。
+ * + * @author ruoyi + */ +@RestController +@RequestMapping +public class TrainAccountBindingAdminController extends BaseController +{ + @Autowired + private TrainAccountBindingAdminService bindingAdminService; + + // ========================================== + // 教师业务档案账号绑定 + // ========================================== + + /** + * 查询可关联教师身份的系统用户列表 + */ + @PreAuthorize("@ss.hasPermi('train:teacher:bindUser')") + @GetMapping({"/train/teacher/bindable-users", "/train/teachers/bindable-users"}) + public AjaxResult getTeacherBindableUsers(@RequestParam(value = "keyword", required = false) String keyword) + { + List服务端严格基于当前会话与登录人关联的学员身份推导数据范围,杜绝任何外部主键传入与 IDOR 越权。
+ * + * @author ruoyi + */ +@RestController +@RequestMapping("/train/me/student") +public class TrainStudentMeController extends BaseController +{ + private final ITrainingIdentityService identityService; + private final ITrainStudentService studentService; + private final TrainEnrollmentMapper enrollmentMapper; + private final TrainLessonMapper lessonMapper; + private final TrainClassReportMapper reportMapper; + private final TrainStudentCheckinMapper checkinMapper; + private final TrainStudyDurationMapper durationMapper; + private final TrainStudentPortraitMapper portraitMapper; + + public TrainStudentMeController(ITrainingIdentityService identityService, + ITrainStudentService studentService, + TrainEnrollmentMapper enrollmentMapper, + TrainLessonMapper lessonMapper, + TrainClassReportMapper reportMapper, + TrainStudentCheckinMapper checkinMapper, + TrainStudyDurationMapper durationMapper, + TrainStudentPortraitMapper portraitMapper) + { + this.identityService = identityService; + this.studentService = studentService; + this.enrollmentMapper = enrollmentMapper; + this.lessonMapper = lessonMapper; + this.reportMapper = reportMapper; + this.checkinMapper = checkinMapper; + this.durationMapper = durationMapper; + this.portraitMapper = portraitMapper; + } + + @Override + protected void startPage() + { + if (com.ruoyi.common.utils.ServletUtils.getRequestAttributes() != null) + { + super.startPage(); + } + } + + /** + * 获取学员个人档案 (敏感信息已脱敏) + */ + @PreAuthorize("@ss.hasRole('train_student') or @ss.hasRole('admin') or @ss.hasPermi('train:me:student')") + @GetMapping + public AjaxResult getStudentProfile() + { + String sId = identityService.assertStudentIdentity(); + StudentDetailVO detail = studentService.selectStudentDetail(sId); + if (detail == null) + { + throw new ServiceException("未找到对应的学员档案数据", HttpStatus.NOT_FOUND); + } + return success(detail); + } + + /** + * 获取我的参训班级与状态列表 + */ + @PreAuthorize("@ss.hasRole('train_student') or @ss.hasRole('admin') or @ss.hasPermi('train:me:student')") + @GetMapping("/enrollments") + public TableDataInfo getMyEnrollments() + { + String sId = identityService.assertStudentIdentity(); + startPage(); + List服务端严格基于当前会话与登录人关联的教师身份推导数据范围,杜绝任何外部主键传入与 IDOR 越权。
+ * + * @author ruoyi + */ +@RestController +@RequestMapping("/train/me/teacher") +public class TrainTeacherMeController extends BaseController +{ + private final ITrainingIdentityService identityService; + private final TrainTeacherMapper teacherMapper; + private final TrainLessonMapper lessonMapper; + private final TrainEnrollmentMapper enrollmentMapper; + private final TrainStudentCheckinMapper checkinMapper; + + public TrainTeacherMeController(ITrainingIdentityService identityService, + TrainTeacherMapper teacherMapper, + TrainLessonMapper lessonMapper, + TrainEnrollmentMapper enrollmentMapper, + TrainStudentCheckinMapper checkinMapper) + { + this.identityService = identityService; + this.teacherMapper = teacherMapper; + this.lessonMapper = lessonMapper; + this.enrollmentMapper = enrollmentMapper; + this.checkinMapper = checkinMapper; + } + + @Override + protected void startPage() + { + if (com.ruoyi.common.utils.ServletUtils.getRequestAttributes() != null) + { + super.startPage(); + } + } + + /** + * 获取教师本人档案信息 + */ + @PreAuthorize("@ss.hasRole('train_teacher') or @ss.hasRole('admin') or @ss.hasPermi('train:me:teacher')") + @GetMapping + public AjaxResult getTeacherProfile() + { + String tId = identityService.assertTeacherIdentity(); + TrainTeacher teacher = teacherMapper.selectTrainTeacherByTId(tId); + if (teacher == null) + { + throw new ServiceException("未找到对应的教师档案数据", HttpStatus.NOT_FOUND); + } + teacher.setUserId(null); + return success(teacher); + } + + /** + * 获取我参与任教的培训班列表 + */ + @PreAuthorize("@ss.hasRole('train_teacher') or @ss.hasRole('admin') or @ss.hasPermi('train:me:teacher')") + @GetMapping("/plans") + public TableDataInfo getMyPlans(@RequestParam(value = "planStatus", required = false) Integer planStatus) + { + String tId = identityService.assertTeacherIdentity(); + startPage(); + List提供受控候选系统用户检索、角色校验、防静默覆盖保护, + * 并复用底层 {@link ITrainingIdentityService} 执行跨表互斥与并发锁绑定。
+ * + * @author ruoyi + */ +@Service +public class TrainAccountBindingAdminService +{ + private final ITrainingIdentityService identityService; + private final ISysUserService userService; + private final ISysRoleService roleService; + private final TrainTeacherMapper teacherMapper; + private final TrainStudentMapper studentMapper; + + public TrainAccountBindingAdminService(ITrainingIdentityService identityService, + ISysUserService userService, + ISysRoleService roleService, + TrainTeacherMapper teacherMapper, + TrainStudentMapper studentMapper) + { + this.identityService = identityService; + this.userService = userService; + this.roleService = roleService; + this.teacherMapper = teacherMapper; + this.studentMapper = studentMapper; + } + + /** + * 查询候选可绑定系统用户列表 (最小受控字段) + * + * @param targetType 目标身份类型 ("TEACHER" / "STUDENT") + * @param keyword 模糊搜索关键字 (匹配 userName 或 nickName) + * @return 候选用户视图列表 + */ + public List测试用例覆盖: + * 1. 候选用户查询与状态校验(包含已停用、缺失角色、已被其他教师或学员绑定的拒绝理由) + * 2. 教师绑定:正常绑定、角色不匹配拒绝、已被绑定防静默覆盖拒绝、同账号幂等 + * 3. 教师解绑:正常解绑、未绑定幂等解绑 + * 4. 学员绑定:正常绑定、角色不匹配拒绝、已被绑定防静默覆盖拒绝、同账号幂等 + * 5. 学员解绑:正常解绑、未绑定幂等解绑 + * 6. 列表信息装配 (enrich) 校验 + *
+ * + * @author ruoyi + */ +public class TrainAccountBindingAdminTest +{ + private TrainAccountBindingAdminService service; + private TrainAccountBindingAdminController controller; + + private Map欢迎使用智慧培训管理平台。本平台提供培训计划编排、学员主档与参训管理、智能排课排程、电子班牌联动、教学设施与实训场地调度,以及全流程多维学习数据分析等核心功能。
-
- 欢迎使用SYFJGYD学员管理系统。本平台提供培训计划编排、学员主档与参训管理、智能排课排程、电子班牌联动、教学设施与实训场地调度,以及全流程多维学习数据分析等核心功能。
+
+