package com.sztzjy.linkCommerce.controller.stu; import com.alibaba.excel.EasyExcel; import com.sztzjy.linkCommerce.config.exception.UnAuthorizedException; import com.sztzjy.linkCommerce.config.security.JwtUser; import com.sztzjy.linkCommerce.config.security.TokenProvider; import com.sztzjy.linkCommerce.entity.TrainingTask; import com.sztzjy.linkCommerce.entity.importDto.TrainingTaskImportDTO; import com.sztzjy.linkCommerce.service.TrainingTaskService; import com.sztzjy.linkCommerce.util.ResultEntity; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.interceptor.TransactionAspectSupport; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.InputStream; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @Api(tags = "Training task management") @RequestMapping("api/training-tasks") @RestController public class TrainingTaskController { @Autowired private TrainingTaskService trainingTaskService; @GetMapping @ApiOperation("Training task list") public ResultEntity> list(@RequestParam(required = false) String projectName, @RequestParam(required = false) String taskName, @RequestParam(required = false) Boolean enabledOnly, HttpServletRequest request) { JwtUser user = TokenProvider.getJWTUser(request); if (isStudent(user)) { List studentTasks = trainingTaskService.listForStudent(user, enabledOnly); studentTasks.forEach(this::removeReferenceAnswerForStudent); return new ResultEntity<>(HttpStatus.OK, "Query success", studentTasks); } return new ResultEntity<>(HttpStatus.OK, "Query success", trainingTaskService.listDefaults(projectName, taskName, enabledOnly)); } @GetMapping("/{id}") @ApiOperation("Training task detail") public ResultEntity detail(@PathVariable String id, HttpServletRequest request) { requireAdmin(request); TrainingTask task = trainingTaskService.getById(id); if (task == null) { return new ResultEntity<>(HttpStatus.NOT_FOUND, "Training task not found"); } return new ResultEntity<>(HttpStatus.OK, "Query success", task); } @GetMapping("/key/{taskKey}") @ApiOperation("Training task detail by key") public ResultEntity detailByTaskKey(@PathVariable String taskKey, HttpServletRequest request) { JwtUser user = TokenProvider.getJWTUser(request); TrainingTask task = isStudent(user) ? trainingTaskService.getStudentTaskByTaskKey(taskKey, user) : trainingTaskService.getByTaskKey(taskKey); if (task == null) { return new ResultEntity<>(HttpStatus.OK, "Training task not configured", null); } if (isStudent(user)) { removeReferenceAnswerForStudent(task); } return new ResultEntity<>(HttpStatus.OK, "Query success", task); } @GetMapping("/classes/{teachingClassId}") @ApiOperation("Training task list for teaching class") public ResultEntity> listForTeachingClass(@PathVariable String teachingClassId, @RequestParam(required = false) Boolean enabledOnly, HttpServletRequest request) { JwtUser user = requireTeacher(request); try { return new ResultEntity<>(HttpStatus.OK, "Query success", trainingTaskService.listForTeachingClass(teachingClassId, enabledOnly, user)); } catch (IllegalArgumentException e) { return new ResultEntity<>(HttpStatus.BAD_REQUEST, e.getMessage()); } } @GetMapping("/classes/{teachingClassId}/key/{taskKey}") @ApiOperation("Training task detail for teaching class by key") public ResultEntity getClassTaskByTaskKey(@PathVariable String teachingClassId, @PathVariable String taskKey, HttpServletRequest request) { JwtUser user = requireTeacher(request); try { TrainingTask task = trainingTaskService.getClassTaskByTaskKey(teachingClassId, taskKey, user.getUserId()); if (task == null) { return new ResultEntity<>(HttpStatus.OK, "Training task not configured", null); } return new ResultEntity<>(HttpStatus.OK, "Query success", task); } catch (IllegalArgumentException e) { return new ResultEntity<>(HttpStatus.BAD_REQUEST, e.getMessage()); } } @PutMapping("/classes/{teachingClassId}/{taskKey}") @ApiOperation("Save training task for teaching class") public ResultEntity saveClassTask(@PathVariable String teachingClassId, @PathVariable String taskKey, @RequestBody TrainingTask task, HttpServletRequest request) { JwtUser user = requireTeacher(request); try { return new ResultEntity<>(HttpStatus.OK, "Save success", trainingTaskService.saveClassTask(teachingClassId, taskKey, task, user.getUserId())); } catch (IllegalArgumentException e) { return new ResultEntity<>(HttpStatus.BAD_REQUEST, e.getMessage()); } } @DeleteMapping("/classes/{teachingClassId}/{taskKey}/override") @ApiOperation("Restore training task default for teaching class") public ResultEntity restoreClassTaskDefault(@PathVariable String teachingClassId, @PathVariable String taskKey, HttpServletRequest request) { JwtUser user = requireTeacher(request); try { trainingTaskService.restoreClassTaskDefault(teachingClassId, taskKey, user.getUserId()); return new ResultEntity(HttpStatus.OK, "Restore success"); } catch (IllegalArgumentException e) { return new ResultEntity(HttpStatus.BAD_REQUEST, e.getMessage()); } } @DeleteMapping("/classes/{teachingClassId}/overrides") @ApiOperation("Restore all training task defaults for teaching class") public ResultEntity restoreClassTasksDefaults(@PathVariable String teachingClassId, HttpServletRequest request) { JwtUser user = requireTeacher(request); try { trainingTaskService.restoreClassTasksDefaults(teachingClassId, user.getUserId()); return new ResultEntity(HttpStatus.OK, "Restore success"); } catch (IllegalArgumentException e) { return new ResultEntity(HttpStatus.BAD_REQUEST, e.getMessage()); } } @PostMapping @ApiOperation("Create training task") public ResultEntity create(@RequestBody TrainingTask task, HttpServletRequest request) { requireAdmin(request); return new ResultEntity<>(HttpStatus.BAD_REQUEST, "Built-in training tasks can only be updated and cannot be created"); } @PutMapping("/{id}") @ApiOperation("Update training task") public ResultEntity update(@PathVariable String id, @RequestBody TrainingTask task, HttpServletRequest request) { requireAdmin(request); if (trainingTaskService.getById(id) == null) { return new ResultEntity<>(HttpStatus.NOT_FOUND, "Training task not found"); } try { return new ResultEntity<>(HttpStatus.OK, "Update success", trainingTaskService.update(id, task)); } catch (IllegalArgumentException e) { return new ResultEntity<>(HttpStatus.BAD_REQUEST, e.getMessage()); } } @PostMapping("/knowledge/reindex-case-materials") @ApiOperation("Reindex existing case materials for AI training knowledge") public ResultEntity reindexCaseMaterials(HttpServletRequest request) { requireAdmin(request); int count = trainingTaskService.reindexCaseMaterials(); return new ResultEntity<>(HttpStatus.OK, "Case material indexing submitted", count); } @DeleteMapping("/{id}") @ApiOperation("Delete training task") public ResultEntity delete(@PathVariable String id, HttpServletRequest request) { requireAdmin(request); if (trainingTaskService.getById(id) == null) { return new ResultEntity<>(HttpStatus.NOT_FOUND, "Training task not found"); } try { trainingTaskService.delete(id); return new ResultEntity<>(HttpStatus.OK, "Delete success"); } catch (IllegalArgumentException e) { return new ResultEntity<>(HttpStatus.BAD_REQUEST, e.getMessage()); } } @PostMapping("/import") @ApiOperation("Import training tasks") @Transactional(rollbackFor = Exception.class) public ResultEntity importTasks(@RequestParam("file") MultipartFile file, HttpServletRequest request) { JwtUser user = requireAdmin(request); ResultEntity fileError = validateImportFile(file); if (fileError != null) { return fileError; } try (InputStream inputStream = file.getInputStream()) { List rows = EasyExcel.read(inputStream) .head(TrainingTaskImportDTO.class) .sheet() .doReadSync(); List errors = new ArrayList<>(); int count = trainingTaskService.importTasks(rows, user.getUserId(), errors); if (!errors.isEmpty()) { TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); return new ResultEntity<>(HttpStatus.BAD_REQUEST, "Data validation failed", errors); } return new ResultEntity<>(HttpStatus.OK, "Import success, total " + count + " rows"); } catch (IllegalArgumentException e) { TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); return new ResultEntity<>(HttpStatus.BAD_REQUEST, e.getMessage()); } catch (Exception e) { TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); return new ResultEntity<>(HttpStatus.ACCEPTED, "Import failed: " + e.getMessage()); } } @GetMapping("/template") @ApiOperation("Download training task import template") public void downloadTemplate(HttpServletRequest request, HttpServletResponse response) throws Exception { requireAdmin(request); String filename = URLEncoder.encode("training-task-import-template.xlsx", StandardCharsets.UTF_8.name()).replaceAll("\\+", "%20"); response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8"); response.setHeader("Content-Disposition", "attachment;filename*=UTF-8''" + filename); EasyExcel.write(response.getOutputStream(), TrainingTaskImportDTO.class) .sheet("training-tasks") .doWrite(trainingTaskService.templateRows()); } private boolean isStudent(JwtUser user) { return user != null && user.getRoleId() == 4; } /** 参考答案属于教师评阅资料,学生任务接口不得返回文件名或地址。 */ private void removeReferenceAnswerForStudent(TrainingTask task) { if (task == null) return; task.setReferenceAnswerName(null); task.setReferenceAnswerUrl(null); } private JwtUser requireAdmin(HttpServletRequest request) { JwtUser user = TokenProvider.getJWTUser(request); if (user == null || user.getRoleId() != 1) { throw new UnAuthorizedException("Admin only"); } return user; } private JwtUser requireTeacher(HttpServletRequest request) { JwtUser user = TokenProvider.getJWTUser(request); if (user == null || (user.getRoleId() != 1 && user.getRoleId() != 3)) { throw new UnAuthorizedException("Teacher only"); } return user; } private ResultEntity validateImportFile(MultipartFile file) { if (file == null || file.isEmpty()) { return new ResultEntity<>(HttpStatus.BAD_REQUEST, "Please select an Excel file"); } String filename = StringUtils.defaultString(file.getOriginalFilename()); if (!StringUtils.endsWithIgnoreCase(filename, ".xls") && !StringUtils.endsWithIgnoreCase(filename, ".xlsx")) { return new ResultEntity<>(HttpStatus.BAD_REQUEST, "Only xls or xlsx files are supported"); } return null; } }