Ver código fonte

修改项目名称 pom文件

王辉 1 mês atrás
pai
commit
d86942f516
100 arquivos alterados com 14502 adições e 0 exclusões
  1. 36 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/commpen/ProducerCommpen.java
  2. 196 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/component/AsyncTaskComp.java
  3. 561 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/component/FileDealComp.java
  4. 69 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/component/JwtComp.java
  5. 273 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/component/ResourceFileDealComp.java
  6. 58 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/component/UserDealComp.java
  7. 20 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/es/ElasticSearchConfig.java
  8. 26 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/es/FileSearch.java
  9. 9 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/jwt/JwtHeader.java
  10. 10 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/jwt/JwtPayload.java
  11. 15 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/jwt/JwtProperties.java
  12. 11 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/jwt/RegisterdClaims.java
  13. 10 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/kafka/KafkaProducerConfiguration.java
  14. 54 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/m3u8/FFmpegConfig.java
  15. 33 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/m3u8/FilePath.java
  16. 128 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/m3u8/M3u8Component.java
  17. 32 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/m3u8/SpringAsyncConfig.java
  18. 133 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/minio/MinioConfig.java
  19. 80 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/minio/MinioProperties.java
  20. 33 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/minio/WebMvcConfig.java
  21. 82 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/onlyoffice/OnlyofficeConfiguration.java
  22. 38 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/plus/MybatisPlusConfig.java
  23. 85 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/threadpool/AsyncThreadPoolAutoConfiguration.java
  24. 51 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/threadpool/AsyncThreadPoolProperties.java
  25. 16 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/threadpool/BaseAsyncUncaughtExceptionHandler.java
  26. 22 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/constant/CommonFileTypeEnum.java
  27. 26 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/constant/FileType.java
  28. 28 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/constant/FileTypeEnum.java
  29. 30 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/consumer/TranscodingConsumer.java
  30. 78 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/consumer/config/ConsumerConfig.java
  31. 126 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/ClassTeamController.java
  32. 163 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CollectCourseController.java
  33. 256 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CollegeController.java
  34. 157 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CollegeOrgController.java
  35. 155 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CollegePositionController.java
  36. 212 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CollegeUserController.java
  37. 111 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CommonFileController.java
  38. 476 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CourseChapterController.java
  39. 156 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CourseChapterKnowledgeController.java
  40. 336 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CourseClassHourController.java
  41. 126 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CourseLogController.java
  42. 172 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CourseNotebooksController.java
  43. 208 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CourseNotesController.java
  44. 185 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CourseOpenController.java
  45. 272 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CourseQuestionAnswerController.java
  46. 58 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CourseStudentBehaviorSocket.java
  47. 199 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CourseStudentBurialpointController.java
  48. 201 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CourseStudentCollectController.java
  49. 365 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CourseStudentRelateController.java
  50. 92 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/ExamineController.java
  51. 1091 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/FileController.java
  52. 35 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/FileStasticController.java
  53. 151 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/GradesController.java
  54. 136 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/KeyWordController.java
  55. 149 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/MajorController.java
  56. 126 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/MaterrialLibraryController.java
  57. 63 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/MinioController.java
  58. 179 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/NoticeController.java
  59. 240 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/OfficeController.java
  60. 50 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/OperationLogBeanController.java
  61. 65 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/PlatformStatusOverviewController.java
  62. 132 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/QuestionAnswerGiveController.java
  63. 106 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/RecoveryFileController.java
  64. 117 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/ResourceCollectController.java
  65. 355 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/ResourceFileController.java
  66. 140 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/ResourceFileFormatController.java
  67. 126 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/ResourceFootprintController.java
  68. 1066 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/ResourceRecordController.java
  69. 191 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/ResourceStatisticController.java
  70. 169 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/ResourceTypeController.java
  71. 131 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/ResourceUserfileConvertController.java
  72. 132 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/RoomController.java
  73. 218 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/SemesterController.java
  74. 249 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/ShareController.java
  75. 260 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/ShareGroupController.java
  76. 166 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/StorageController.java
  77. 147 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/StorageReController.java
  78. 206 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/StudyBehaviorProgressController.java
  79. 47 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/SysParamController.java
  80. 85 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/TaskController.java
  81. 169 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/TeachingActivityController.java
  82. 55 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/TestWebSocket.java
  83. 46 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/TranscodingController.java
  84. 171 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/UserCommentController.java
  85. 153 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/UserController.java
  86. 165 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/UserReadController.java
  87. 146 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/VideoAnalysisProgressController.java
  88. 71 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/domain/CapacityAuditInfo.java
  89. 63 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/domain/CapacityReAuditInfo.java
  90. 75 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/domain/Chapter.java
  91. 98 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/domain/ClassHour.java
  92. 91 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/domain/ClassTeam.java
  93. 86 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/domain/CollectCourse.java
  94. 80 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/domain/College.java
  95. 91 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/domain/CollegeOrg.java
  96. 97 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/domain/CollegePosition.java
  97. 109 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/domain/CollegeUser.java
  98. 30 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/domain/CommonFile.java
  99. 34 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/domain/CourseAuditRecordEnum.java
  100. 75 0
      education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/domain/CourseChapterKnowledge.java

+ 36 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/commpen/ProducerCommpen.java

@@ -0,0 +1,36 @@
+package vip.xiaonuo.disk.commpen;
+
+
+import org.apache.kafka.clients.producer.Producer;
+import org.springframework.stereotype.Component;
+import vip.xiaonuo.disk.properties.KafkaProducerProperties;
+import vip.xiaonuo.disk.util.KafKaProducerUtil;
+
+import javax.annotation.Resource;
+
+/*******************************************************
+ * kafka生产者初始化 
+ * @ClassName : ProducerCommpen
+ * @author: lin xue peng
+ * @time: 2023-07-17 23:17:54
+  * *****************************************************/
+@Component
+//@Scope("singleton")
+public class ProducerCommpen {
+
+    private Producer<String, String> producerInstance;
+
+
+
+    @Resource
+    private KafkaProducerProperties kafkaPro;
+
+
+    public synchronized Producer<String, String> getProducer() {
+        if (producerInstance == null) {
+            System.err.println("初始化kafka服务!!!");
+            producerInstance = KafKaProducerUtil.initProps(kafkaPro.getUserName(),kafkaPro.getPwd(),kafkaPro.getServers());
+        }
+        return producerInstance;
+    }
+}

+ 196 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/component/AsyncTaskComp.java

@@ -0,0 +1,196 @@
+package vip.xiaonuo.disk.component;
+
+import com.alibaba.fastjson2.JSON;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import vip.xiaonuo.disk.domain.FileBean;
+import vip.xiaonuo.disk.io.QiwenFile;
+import vip.xiaonuo.disk.mapper.FileMapper;
+import vip.xiaonuo.disk.mapper.UserFileMapper;
+import com.qiwenshare.ufop.factory.UFOPFactory;
+import com.qiwenshare.ufop.operation.copy.domain.CopyFile;
+import com.qiwenshare.ufop.util.UFOPUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.IOUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.annotation.AsyncResult;
+import org.springframework.stereotype.Component;
+import vip.xiaonuo.disk.service.IFiletransferService;
+import vip.xiaonuo.disk.service.IRecoveryFileService;
+import vip.xiaonuo.disk.domain.UserFile;
+
+import javax.annotation.Resource;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.Future;
+
+/**
+ * 功能描述:异步任务业务类(@Async也可添加在方法上)
+ */
+@Slf4j
+@Component
+@Async("asyncTaskExecutor")
+public class AsyncTaskComp {
+
+    @Resource
+    IRecoveryFileService recoveryFileService;
+    @Resource
+    IFiletransferService filetransferService;
+    @Resource
+    UFOPFactory ufopFactory;
+    @Resource
+    UserFileMapper userFileMapper;
+    @Resource
+    FileMapper fileMapper;
+    @Resource
+    vip.xiaonuo.disk.component.FileDealComp fileDealComp;
+
+    @Value("${ufop.storage-type}")
+    private Integer storageType;
+
+    public Long getFilePointCount(String fileId) {
+        LambdaQueryWrapper<UserFile> lambdaQueryWrapper = new LambdaQueryWrapper<>();
+        lambdaQueryWrapper.eq(UserFile::getFileId, fileId);
+        return userFileMapper.selectCount(lambdaQueryWrapper);
+    }
+
+    public Future<String> deleteUserFile(String userFileId) {
+        UserFile userFile = userFileMapper.selectById(userFileId);
+        if (userFile.getIsDir() == 1) {
+            LambdaQueryWrapper<UserFile> userFileLambdaQueryWrapper = new LambdaQueryWrapper<>();
+            userFileLambdaQueryWrapper.eq(UserFile::getDeleteBatchNum, userFile.getDeleteBatchNum());
+            List<UserFile> list = userFileMapper.selectList(userFileLambdaQueryWrapper);
+            recoveryFileService.deleteUserFileByDeleteBatchNum(userFile.getDeleteBatchNum());
+            for (UserFile userFileItem : list) {
+
+                Long filePointCount = getFilePointCount(userFileItem.getFileId());
+
+                if (filePointCount != null && filePointCount == 0 && userFileItem.getIsDir() == 0) {
+                    FileBean fileBean = fileMapper.selectById(userFileItem.getFileId());
+                    if (fileBean != null) {
+                        try {
+                            filetransferService.deleteFile(fileBean);
+                            fileMapper.deleteById(fileBean.getFileId());
+                        } catch (Exception e) {
+//                            log.error("删除本地文件失败:" + JSON.toJSONString(fileBean));
+                        }
+                    }
+
+
+                }
+            }
+        } else {
+
+            recoveryFileService.deleteUserFileByDeleteBatchNum(userFile.getDeleteBatchNum());
+            Long filePointCount = getFilePointCount(userFile.getFileId());
+
+            if (filePointCount != null && filePointCount == 0 && userFile.getIsDir() == 0) {
+                FileBean fileBean = fileMapper.selectById(userFile.getFileId());
+                try {
+                    filetransferService.deleteFile(fileBean);
+                    fileMapper.deleteById(fileBean.getFileId());
+                } catch (Exception e) {
+//                    log.error("删除本地文件失败:" + JSON.toJSONString(fileBean));
+                }
+            }
+        }
+
+        return new AsyncResult<>("deleteUserFile");
+    }
+
+    public Future<String> checkESUserFileId(String userFileId) {
+        UserFile userFile = userFileMapper.selectById(userFileId);
+        if (userFile == null) {
+            fileDealComp.deleteESByUserFileId(userFileId);
+        }
+        return new AsyncResult<>("checkUserFileId");
+    }
+
+
+    public Future<String> saveUnzipFile(UserFile userFile, FileBean fileBean, int unzipMode, String entryName, String filePath) {
+        String unzipUrl = UFOPUtils.getTempFile(fileBean.getFileUrl()).getAbsolutePath().replace("." + userFile.getExtendName(), "");
+        String totalFileUrl = unzipUrl + entryName;
+        File currentFile = new File(totalFileUrl);
+
+        String fileId = null;
+        if (!currentFile.isDirectory()) {
+
+            FileInputStream fis = null;
+            String md5Str = UUID.randomUUID().toString();
+            try {
+                fis = new FileInputStream(currentFile);
+                md5Str = DigestUtils.md5Hex(fis);
+            } catch (IOException e) {
+                e.printStackTrace();
+            } finally {
+                IOUtils.closeQuietly(fis);
+            }
+
+            FileInputStream fileInputStream = null;
+            try {
+                Map<String, Object> param = new HashMap<>();
+                param.put("identifier", md5Str);
+                List<FileBean> list = fileMapper.selectByMap(param);
+
+                if (list != null && !list.isEmpty()) { //文件已存在
+                    fileId = list.get(0).getFileId();
+                } else { //文件不存在
+                    fileInputStream = new FileInputStream(currentFile);
+                    CopyFile createFile = new CopyFile();
+                    createFile.setExtendName(FilenameUtils.getExtension(totalFileUrl));
+                    String saveFileUrl = ufopFactory.getCopier().copy(fileInputStream, createFile);
+
+                    FileBean tempFileBean = new FileBean(saveFileUrl, currentFile.length(), storageType, md5Str, userFile.getUserId());
+
+                    fileMapper.insert(tempFileBean);
+                    fileId = tempFileBean.getFileId();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            } finally {
+                IOUtils.closeQuietly(fileInputStream);
+                System.gc();
+                try {
+                    Thread.sleep(100);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+                currentFile.delete();
+            }
+
+
+        }
+
+
+        QiwenFile qiwenFile = null;
+        if (unzipMode == 0) {
+            qiwenFile = new QiwenFile(userFile.getFilePath(), entryName, currentFile.isDirectory());
+        } else if (unzipMode == 1) {
+            qiwenFile = new QiwenFile(userFile.getFilePath() + "/" + userFile.getFileName(), entryName, currentFile.isDirectory());
+        } else if (unzipMode == 2) {
+            qiwenFile = new QiwenFile(filePath, entryName, currentFile.isDirectory());
+        }
+
+        UserFile saveUserFile = new UserFile(qiwenFile, userFile.getUserId(), fileId);
+        String fileName = fileDealComp.getRepeatFileName(saveUserFile, saveUserFile.getFilePath());
+
+        if (saveUserFile.getIsDir() == 1 && !fileName.equals(saveUserFile.getFileName())) {
+            //如果是目录,而且重复,什么也不做
+        } else {
+            saveUserFile.setFileName(fileName);
+            userFileMapper.insert(saveUserFile);
+        }
+        fileDealComp.restoreParentFilePath(qiwenFile, userFile.getUserId());
+
+        return new AsyncResult<>("saveUnzipFile");
+    }
+
+
+}

+ 561 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/component/FileDealComp.java

@@ -0,0 +1,561 @@
+package vip.xiaonuo.disk.component;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.IdUtil;
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.qiwenshare.common.util.DateUtil;
+import com.qiwenshare.common.util.MusicUtils;
+import com.qiwenshare.common.util.security.SessionUtil;
+import com.qiwenshare.ufop.factory.UFOPFactory;
+import com.qiwenshare.ufop.operation.copy.Copier;
+import com.qiwenshare.ufop.operation.copy.domain.CopyFile;
+import com.qiwenshare.ufop.operation.download.Downloader;
+import com.qiwenshare.ufop.operation.download.domain.DownloadFile;
+import com.qiwenshare.ufop.operation.write.Writer;
+import com.qiwenshare.ufop.operation.write.domain.WriteFile;
+import com.qiwenshare.ufop.util.UFOPUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.jaudiotagger.audio.AudioFile;
+import org.jaudiotagger.audio.AudioFileIO;
+import org.jaudiotagger.audio.AudioHeader;
+import org.jaudiotagger.audio.flac.FlacFileReader;
+import org.jaudiotagger.audio.mp3.MP3File;
+import org.jaudiotagger.tag.FieldKey;
+import org.jaudiotagger.tag.Tag;
+import org.jaudiotagger.tag.datatype.Artwork;
+import org.jaudiotagger.tag.id3.AbstractID3v2Frame;
+import org.jaudiotagger.tag.id3.AbstractID3v2Tag;
+import org.jaudiotagger.tag.id3.framebody.FrameBodyAPIC;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
+import vip.xiaonuo.disk.config.es.FileSearch;
+import vip.xiaonuo.disk.domain.*;
+import vip.xiaonuo.disk.io.QiwenFile;
+import vip.xiaonuo.disk.mapper.FileMapper;
+import vip.xiaonuo.disk.mapper.MusicMapper;
+import vip.xiaonuo.disk.mapper.UserFileMapper;
+import vip.xiaonuo.disk.service.IShareFileService;
+import vip.xiaonuo.disk.service.IShareService;
+import vip.xiaonuo.disk.service.IUserService;
+import vip.xiaonuo.disk.util.QiwenFileUtil;
+import vip.xiaonuo.disk.util.TreeNode;
+
+import javax.annotation.Resource;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.*;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+/**
+ * 文件逻辑处理组件
+ */
+@Slf4j
+@Component
+public class FileDealComp {
+    @Resource
+    private UserFileMapper userFileMapper;
+    @Resource
+    private FileMapper fileMapper;
+    @Resource
+    private IUserService userService;
+    @Resource
+    private IShareService shareService;
+    @Resource
+    private IShareFileService shareFileService;
+    @Resource
+    private UFOPFactory ufopFactory;
+    @Resource
+    private MusicMapper musicMapper;
+    @Autowired
+    private ElasticsearchClient elasticsearchClient;
+
+    public static Executor exec = Executors.newFixedThreadPool(20);
+
+    /**
+     * 获取重复文件名
+     * <p>
+     * 场景1: 文件还原时,在 savefilePath 路径下,保存 测试.txt 文件重名,则会生成 测试(1).txt
+     * 场景2: 上传文件时,在 savefilePath 路径下,保存 测试.txt 文件重名,则会生成 测试(1).txt
+     *
+     * @param userFile
+     * @param savefilePath
+     * @return
+     */
+    public String getRepeatFileName(UserFile userFile, String savefilePath) {
+        String fileName = userFile.getFileName();
+        String extendName = userFile.getExtendName();
+
+        String userId = userFile.getUserId();
+        int isDir = userFile.getIsDir();
+        LambdaQueryWrapper<UserFile> lambdaQueryWrapper = new LambdaQueryWrapper<>();
+        lambdaQueryWrapper.eq(UserFile::getFilePath, savefilePath)
+                .eq(UserFile::getDeleteFlag, 0)
+                .eq(UserFile::getUserId, userId)
+                .eq(UserFile::getFileName, fileName)
+                .eq(UserFile::getIsDir, isDir);
+        if (userFile.isFile()) {
+            lambdaQueryWrapper.eq(UserFile::getExtendName, extendName);
+        }
+        List<UserFile> list = userFileMapper.selectList(lambdaQueryWrapper);
+        if (CollectionUtils.isEmpty(list)) {
+            return fileName;
+        }
+
+        int i = 0;
+
+        while (!CollectionUtils.isEmpty(list)) {
+            i++;
+            LambdaQueryWrapper<UserFile> lambdaQueryWrapper1 = new LambdaQueryWrapper<>();
+            lambdaQueryWrapper1.eq(UserFile::getFilePath, savefilePath)
+                    .eq(UserFile::getDeleteFlag, 0)
+                    .eq(UserFile::getUserId, userId)
+                    .eq(UserFile::getFileName, fileName + "(" + i + ")")
+                    .eq(UserFile::getIsDir, isDir);
+            if (userFile.isFile()) {
+                lambdaQueryWrapper1.eq(UserFile::getExtendName, extendName);
+            }
+            list = userFileMapper.selectList(lambdaQueryWrapper1);
+
+        }
+
+        return fileName + "(" + i + ")";
+
+    }
+
+    /**
+     * 还原父文件路径
+     * <p>
+     * 1、回收站文件还原操作会将文件恢复到原来的路径下,当还原文件的时候,如果父目录已经不存在了,则需要把父母录给还原
+     * 2、上传目录
+     *
+     * @param sessionUserId
+     */
+    public void restoreParentFilePath(QiwenFile qiwenFile, String sessionUserId) {
+
+        if (qiwenFile.isFile()) {
+            qiwenFile = qiwenFile.getParentFile();
+        }
+        while (qiwenFile.getParent() != null) {
+            String fileName = qiwenFile.getName();
+            String parentFilePath = qiwenFile.getParent();
+
+            LambdaQueryWrapper<UserFile> lambdaQueryWrapper = new LambdaQueryWrapper<>();
+            lambdaQueryWrapper.eq(UserFile::getFilePath, parentFilePath)
+                    .eq(UserFile::getFileName, fileName)
+                    .eq(UserFile::getDeleteFlag, 0)
+                    .eq(UserFile::getIsDir, 1)
+                    .eq(UserFile::getUserId, sessionUserId);
+            List<UserFile> userFileList = userFileMapper.selectList(lambdaQueryWrapper);
+            if (userFileList.size() == 0) {
+                UserFile userFile = QiwenFileUtil.getQiwenDir(sessionUserId, parentFilePath, fileName);
+                try {
+                    userFileMapper.insert(userFile);
+                } catch (Exception e) {
+                    //ignore
+                }
+            }
+            qiwenFile = new QiwenFile(parentFilePath, true);
+        }
+    }
+
+
+    /**
+     * 删除重复的子目录文件
+     * <p>
+     * 当还原目录的时候,如果其子目录在文件系统中已存在,则还原之后进行去重操作
+     *
+     * @param filePath
+     * @param sessionUserId
+     */
+    public void deleteRepeatSubDirFile(String filePath, String sessionUserId) {
+        log.debug("删除子目录:" + filePath);
+        LambdaQueryWrapper<UserFile> lambdaQueryWrapper = new LambdaQueryWrapper<>();
+
+        lambdaQueryWrapper.select(UserFile::getFileName, UserFile::getFilePath)
+                .likeRight(UserFile::getFilePath, QiwenFileUtil.formatLikePath(filePath))
+                .eq(UserFile::getIsDir, 1)
+                .eq(UserFile::getDeleteFlag, 0)
+                .eq(UserFile::getUserId, sessionUserId)
+                .groupBy(UserFile::getFilePath, UserFile::getFileName)
+                .having("count(file_name) >= 2");
+        List<UserFile> repeatList = userFileMapper.selectList(lambdaQueryWrapper);
+
+        for (UserFile userFile : repeatList) {
+            LambdaQueryWrapper<UserFile> lambdaQueryWrapper1 = new LambdaQueryWrapper<>();
+            lambdaQueryWrapper1.eq(UserFile::getFilePath, userFile.getFilePath())
+                    .eq(UserFile::getFileName, userFile.getFileName())
+                    .eq(UserFile::getDeleteFlag, "0");
+            List<UserFile> userFiles = userFileMapper.selectList(lambdaQueryWrapper1);
+            for (int i = 0; i < userFiles.size() - 1; i++) {
+                userFileMapper.deleteById(userFiles.get(i).getUserFileId());
+            }
+        }
+    }
+
+    /**
+     * 组织一个树目录节点,文件移动的时候使用
+     *
+     * @param treeNode
+     * @param id
+     * @param filePath
+     * @param nodeNameQueue
+     * @return
+     */
+    public TreeNode insertTreeNode(TreeNode treeNode, long id, String filePath, Queue<String> nodeNameQueue) {
+
+        List<TreeNode> childrenTreeNodes = treeNode.getChildren();
+        String currentNodeName = nodeNameQueue.peek();
+        if (currentNodeName == null) {
+            return treeNode;
+        }
+
+        QiwenFile qiwenFile = new QiwenFile(filePath, currentNodeName, true);
+        filePath = qiwenFile.getPath();
+
+        if (!isExistPath(childrenTreeNodes, currentNodeName)) {  //1、判断有没有该子节点,如果没有则插入
+            //插入
+            TreeNode resultTreeNode = new TreeNode();
+
+            resultTreeNode.setFilePath(filePath);
+            resultTreeNode.setLabel(nodeNameQueue.poll());
+            resultTreeNode.setId(++id);
+
+            childrenTreeNodes.add(resultTreeNode);
+
+        } else {  //2、如果有,则跳过
+            nodeNameQueue.poll();
+        }
+
+        if (nodeNameQueue.size() != 0) {
+            for (int i = 0; i < childrenTreeNodes.size(); i++) {
+
+                TreeNode childrenTreeNode = childrenTreeNodes.get(i);
+                if (currentNodeName.equals(childrenTreeNode.getLabel())) {
+                    childrenTreeNode = insertTreeNode(childrenTreeNode, id * 10, filePath, nodeNameQueue);
+                    childrenTreeNodes.remove(i);
+                    childrenTreeNodes.add(childrenTreeNode);
+                    treeNode.setChildren(childrenTreeNodes);
+                }
+
+            }
+        } else {
+            treeNode.setChildren(childrenTreeNodes);
+        }
+
+        return treeNode;
+
+    }
+
+    /**
+     * 判断该路径在树节点中是否已经存在
+     *
+     * @param childrenTreeNodes
+     * @param path
+     * @return
+     */
+    public boolean isExistPath(List<TreeNode> childrenTreeNodes, String path) {
+        boolean isExistPath = false;
+
+        try {
+            for (TreeNode childrenTreeNode : childrenTreeNodes) {
+                if (path.equals(childrenTreeNode.getLabel())) {
+                    isExistPath = true;
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+
+        return isExistPath;
+    }
+
+
+    public void uploadESByUserFileId(String userFileId) {
+        exec.execute(()->{
+            try {
+
+                Map<String, Object> param = new HashMap<>();
+                param.put("userFileId", userFileId);
+                List<UserFile> userfileResult = userFileMapper.selectByMap(param);
+                if (userfileResult != null && userfileResult.size() > 0) {
+                    FileSearch fileSearch = new FileSearch();
+                    BeanUtil.copyProperties(userfileResult.get(0), fileSearch);
+                /*if (fileSearch.getIsDir() == 0) {
+
+                    Reader reader = ufopFactory.getReader(fileSearch.getStorageType());
+                    ReadFile readFile = new ReadFile();
+                    readFile.setFileUrl(fileSearch.getFileUrl());
+                    String content = reader.read(readFile);
+                    //全文搜索
+                    fileSearch.setContent(content);
+
+                }*/
+                    elasticsearchClient.index(i -> i.index("filesearch").id(fileSearch.getUserFileId()).document(fileSearch));
+                }
+            } catch (Exception e) {
+                log.debug("ES更新操作失败,请检查配置");
+            }
+        });
+
+
+    }
+
+    public void deleteESByUserFileId(String userFileId) {
+        exec.execute(() -> {
+            try {
+                elasticsearchClient.delete(d -> d
+                        .index("filesearch")
+                        .id(userFileId));
+            } catch (Exception e) {
+                log.debug("ES删除操作失败,请检查配置");
+            }
+        });
+
+
+    }
+
+    /**
+     * 根据用户传入的参数,判断是否有下载或者预览权限
+     *
+     * @return
+     */
+    public boolean checkAuthDownloadAndPreview(String shareBatchNum,
+                                               String extractionCode,
+                                               String token,
+                                               String userFileIds,
+                                               Integer platform) {
+        log.debug("权限检查开始:shareBatchNum:{}, extractionCode:{}, token:{}, userFileIds{}", shareBatchNum, extractionCode, token, userFileIds);
+        if (platform != null && platform == 2) {
+            return true;
+        }
+        String[] userFileIdArr = userFileIds.split(",");
+        for (String userFileId : userFileIdArr) {
+
+            UserFile userFile = userFileMapper.selectById(userFileId);
+            log.debug(JSON.toJSONString(userFile));
+            if ("undefined".equals(shareBatchNum) || StringUtils.isEmpty(shareBatchNum)) {
+
+               // String userId = userService.getUserIdByToken(token);
+                String userId = StpLoginUserUtil.getLoginUser().getId();
+//                log.debug(JSON.toJSONString("当前登录session用户id:" + userId));
+                if (userId == null) {
+                    return false;
+                }
+                log.debug("文件所属用户id:" + userFile.getUserId());
+                log.debug("登录用户id:" + userId);
+                if (!userFile.getUserId().equals(userId)) {
+                    log.info("用户id不一致,权限校验失败");
+                    return false;
+                }
+            } else {
+                Map<String, Object> param = new HashMap<>();
+                param.put("shareBatchNum", shareBatchNum);
+                List<Share> shareList = shareService.listByMap(param);
+                //判断批次号
+                if (shareList.size() <= 0) {
+                    log.info("分享批次号不存在,权限校验失败");
+                    return false;
+                }
+                Integer shareType = shareList.get(0).getShareType();
+                if (1 == shareType) {
+                    //判断提取码
+                    if (!shareList.get(0).getExtractionCode().equals(extractionCode)) {
+                        log.info("提取码错误,权限校验失败");
+                        return false;
+                    }
+                }
+                param.put("userFileId", userFileId);
+                List<ShareFile> shareFileList = shareFileService.listByMap(param);
+                if (shareFileList.size() <= 0) {
+                    log.info("用户id和分享批次号不匹配,权限校验失败");
+                    return false;
+                }
+
+            }
+
+        }
+        return true;
+    }
+
+    /**
+     * 拷贝文件
+     * 场景:修改的文件被多处引用时,需要重新拷贝一份,然后在新的基础上修改
+     *
+     * @param fileBean
+     * @param userFile
+     * @return
+     */
+    public String copyFile(FileBean fileBean, UserFile userFile) {
+        Copier copier = ufopFactory.getCopier();
+        Downloader downloader = ufopFactory.getDownloader(fileBean.getStorageType());
+        DownloadFile downloadFile = new DownloadFile();
+        downloadFile.setFileUrl(fileBean.getFileUrl());
+        CopyFile copyFile = new CopyFile();
+        copyFile.setExtendName(userFile.getExtendName());
+        String fileUrl = copier.copy(downloader.getInputStream(downloadFile), copyFile);
+        if (downloadFile.getOssClient() != null) {
+            downloadFile.getOssClient().shutdown();
+        }
+        fileBean.setFileUrl(fileUrl);
+        fileBean.setFileId(IdUtil.getSnowflakeNextIdStr());
+        fileMapper.insert(fileBean);
+        userFile.setFileId(fileBean.getFileId());
+        userFile.setUploadTime(DateUtil.getCurrentTime());
+        userFile.setModifyTime(DateUtil.getCurrentTime());
+        userFile.setModifyUserId(SessionUtil.getUserId());
+        userFileMapper.updateById(userFile);
+        return fileUrl;
+    }
+
+    public String getIdentifierByFile(String fileUrl, int storageType) throws IOException {
+        DownloadFile downloadFile = new DownloadFile();
+        downloadFile.setFileUrl(fileUrl);
+        InputStream inputStream = ufopFactory.getDownloader(storageType).getInputStream(downloadFile);
+        return DigestUtils.md5Hex(inputStream);
+    }
+
+    public void saveFileInputStream(int storageType, String fileUrl, InputStream inputStream) throws IOException {
+        Writer writer1 = ufopFactory.getWriter(storageType);
+        WriteFile writeFile = new WriteFile();
+        writeFile.setFileUrl(fileUrl);
+        int fileSize = inputStream.available();
+        writeFile.setFileSize(fileSize);
+        writer1.write(inputStream, writeFile);
+    }
+
+    public boolean isDirExist(String fileName, String filePath, String userId) {
+        LambdaQueryWrapper<UserFile> lambdaQueryWrapper = new LambdaQueryWrapper<>();
+        lambdaQueryWrapper.eq(UserFile::getFileName, fileName)
+                .eq(UserFile::getFilePath, QiwenFile.formatPath(filePath))
+                .eq(UserFile::getUserId, userId)
+                .eq(UserFile::getDeleteFlag, 0)
+                .eq(UserFile::getIsDir, 1);
+        List<UserFile> list = userFileMapper.selectList(lambdaQueryWrapper);
+        if (list != null && !list.isEmpty()) {
+            return true;
+        }
+        return false;
+    }
+
+
+    public void parseMusicFile(String extendName, int storageType, String fileUrl, String fileId) {
+        File outFile = null;
+        InputStream inputStream = null;
+        FileOutputStream fileOutputStream = null;
+        try {
+            if ("mp3".equalsIgnoreCase(extendName) || "flac".equalsIgnoreCase(extendName)) {
+                Downloader downloader = ufopFactory.getDownloader(storageType);
+                DownloadFile downloadFile = new DownloadFile();
+                downloadFile.setFileUrl(fileUrl);
+                inputStream = downloader.getInputStream(downloadFile);
+                outFile = UFOPUtils.getTempFile(fileUrl);
+                if (!outFile.exists()) {
+                    outFile.createNewFile();
+                }
+                fileOutputStream = new FileOutputStream(outFile);
+                IOUtils.copy(inputStream, fileOutputStream);
+                Music music = new Music();
+              //  music.setMusicId(IdUtil.getSnowflakeNextIdStr());
+                music.setFileId(fileId);
+
+                Tag tag = null;
+                AudioHeader audioHeader = null;
+                if ("mp3".equalsIgnoreCase(extendName)) {
+                    MP3File f = (MP3File) AudioFileIO.read(outFile);
+                    tag = f.getTag();
+                    audioHeader = f.getAudioHeader();
+                    MP3File mp3file = new MP3File(outFile);
+                    if (mp3file.hasID3v2Tag()) {
+                        AbstractID3v2Tag id3v2Tag = mp3file.getID3v2TagAsv24();
+                        AbstractID3v2Frame frame = (AbstractID3v2Frame) id3v2Tag.getFrame("APIC");
+                        FrameBodyAPIC body;
+                        if (frame != null && !frame.isEmpty()) {
+                            body = (FrameBodyAPIC) frame.getBody();
+                            byte[] imageData = body.getImageData();
+                            music.setAlbumImage(Base64.getEncoder().encodeToString(imageData));
+                        }
+                        if (tag != null) {
+                            music.setArtist(tag.getFirst(FieldKey.ARTIST));
+                            music.setTitle(tag.getFirst(FieldKey.TITLE));
+                            music.setAlbum(tag.getFirst(FieldKey.ALBUM));
+                            music.setYear(tag.getFirst(FieldKey.YEAR));
+                            try {
+                                music.setTrack(tag.getFirst(FieldKey.TRACK));
+                            } catch (Exception e) {
+                                // ignore
+                            }
+
+                            music.setGenre(tag.getFirst(FieldKey.GENRE));
+                            music.setComment1(tag.getFirst(FieldKey.COMMENT));
+                            music.setLyrics(tag.getFirst(FieldKey.LYRICS));
+                            music.setComposer(tag.getFirst(FieldKey.COMPOSER));
+                            music.setAlbumArtist(tag.getFirst(FieldKey.ALBUM_ARTIST));
+                            music.setEncoder(tag.getFirst(FieldKey.ENCODER));
+                        }
+                    }
+                } else if ("flac".equalsIgnoreCase(extendName)) {
+                    AudioFile f = new FlacFileReader().read(outFile);
+                    tag = f.getTag();
+                    audioHeader = f.getAudioHeader();
+                    if (tag != null) {
+                        music.setArtist(StringUtils.join(tag.getFields(FieldKey.ARTIST), ","));
+                        music.setTitle(StringUtils.join(tag.getFields(FieldKey.TITLE), ","));
+                        music.setAlbum(StringUtils.join(tag.getFields(FieldKey.ALBUM), ","));
+                        music.setYear(StringUtils.join(tag.getFields(FieldKey.YEAR), ","));
+                        music.setTrack(StringUtils.join(tag.getFields(FieldKey.TRACK), ","));
+                        music.setGenre(StringUtils.join(tag.getFields(FieldKey.GENRE), ","));
+                        music.setComment1(StringUtils.join(tag.getFields(FieldKey.COMMENT), ","));
+                        music.setLyrics(StringUtils.join(tag.getFields(FieldKey.LYRICS), ","));
+                        music.setComposer(StringUtils.join(tag.getFields(FieldKey.COMPOSER), ","));
+                        music.setAlbumArtist(StringUtils.join(tag.getFields(FieldKey.ALBUM_ARTIST), ","));
+                        music.setEncoder(StringUtils.join(tag.getFields(FieldKey.ENCODER), ","));
+                        List<Artwork> artworkList = tag.getArtworkList();
+                        if (artworkList != null && !artworkList.isEmpty()) {
+                            Artwork artwork = artworkList.get(0);
+                            byte[] binaryData = artwork.getBinaryData();
+                            music.setAlbumImage(Base64.getEncoder().encodeToString(binaryData));
+                        }
+                    }
+
+                }
+
+                if (audioHeader != null) {
+                    music.setTrackLength(Float.parseFloat(audioHeader.getTrackLength() + ""));
+                }
+
+                if (StringUtils.isEmpty(music.getLyrics())) {
+                    try {
+
+                        String lyc = MusicUtils.getLyc(music.getArtist(), music.getTitle(), music.getAlbum());
+                        music.setLyrics(lyc);
+                    } catch (Exception e) {
+                        log.info(e.getMessage());
+                    }
+                }
+                musicMapper.insert(music);
+            }
+        } catch (Exception e) {
+            log.error("解析音乐信息失败!", e);
+        } finally {
+            IOUtils.closeQuietly(inputStream);
+            IOUtils.closeQuietly(fileOutputStream);
+            if (outFile != null) {
+                if (outFile.exists()) {
+                    outFile.delete();
+                }
+            }
+        }
+    }
+
+}

+ 69 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/component/JwtComp.java

@@ -0,0 +1,69 @@
+package vip.xiaonuo.disk.component;
+
+import com.alibaba.fastjson2.JSON;
+import com.qiwenshare.common.util.math.CalculatorUtils;
+import vip.xiaonuo.disk.config.jwt.JwtProperties;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.JwtBuilder;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import io.jsonwebtoken.impl.DefaultClaims;
+import org.apache.commons.codec.binary.Base64;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.util.Date;
+import java.util.Map;
+
+@Component
+public class JwtComp {
+
+	@Resource
+	JwtProperties jwtProperties;
+
+
+	// 由字符串生成加密key
+	private SecretKey generalKey() {
+		// 本地的密码解码
+		byte[] encodedKey = Base64.decodeBase64(jwtProperties.getSecret());
+		// 根据给定的字节数组使用AES加密算法构造一个密钥
+        return new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
+	}
+
+	// 创建jwt
+	public String createJWT(Map<String, Object> param) {
+		String subject = JSON.toJSONString(param);
+		// 生成JWT的时间
+		long nowTime = System.currentTimeMillis();
+		Date nowDate = new Date(nowTime);
+		// 生成签名的时候使用的秘钥secret,切记这个秘钥不能外露,是你服务端的私钥,在任何场景都不应该流露出去,一旦客户端得知这个secret,那就意味着客户端是可以自我签发jwt的
+		SecretKey key = generalKey();
+		Double expireTime = CalculatorUtils.conversion(jwtProperties.getPayload().getRegisterdClaims().getExp());
+
+		// 为payload添加各种标准声明和私有声明
+		DefaultClaims defaultClaims = new DefaultClaims();
+		defaultClaims.setIssuer(jwtProperties.getPayload().getRegisterdClaims().getIss());
+		defaultClaims.setExpiration(new Date(System.currentTimeMillis() + expireTime.longValue()));
+		defaultClaims.setSubject(subject);
+		defaultClaims.setAudience(jwtProperties.getPayload().getRegisterdClaims().getAud());
+
+		JwtBuilder builder = Jwts.builder() // 表示new一个JwtBuilder,设置jwt的body
+				.setClaims(defaultClaims)
+				.setIssuedAt(nowDate) // iat(issuedAt):jwt的签发时间
+				.signWith(SignatureAlgorithm.forName(jwtProperties.getHeader().getAlg()), key); // 设置签名,使用的是签名算法和签名使用的秘钥
+
+		return builder.compact();
+	}
+
+	// 解密jwt
+	public Claims parseJWT(String jwt) throws Exception {
+		SecretKey key = generalKey(); // 签名秘钥,和生成的签名的秘钥一模一样
+        return Jwts.parser() // 得到DefaultJwtParser
+                .setSigningKey(key) // 设置签名的秘钥
+                .parseClaimsJws(jwt).getBody();
+	}
+
+
+}

+ 273 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/component/ResourceFileDealComp.java

@@ -0,0 +1,273 @@
+package vip.xiaonuo.disk.component;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.IdUtil;
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.qiwenshare.common.util.DateUtil;
+import com.qiwenshare.common.util.MusicUtils;
+import com.qiwenshare.common.util.security.SessionUtil;
+import com.qiwenshare.ufop.factory.UFOPFactory;
+import com.qiwenshare.ufop.operation.copy.Copier;
+import com.qiwenshare.ufop.operation.copy.domain.CopyFile;
+import com.qiwenshare.ufop.operation.download.Downloader;
+import com.qiwenshare.ufop.operation.download.domain.DownloadFile;
+import com.qiwenshare.ufop.operation.write.Writer;
+import com.qiwenshare.ufop.operation.write.domain.WriteFile;
+import com.qiwenshare.ufop.util.UFOPUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.jaudiotagger.audio.AudioFile;
+import org.jaudiotagger.audio.AudioFileIO;
+import org.jaudiotagger.audio.AudioHeader;
+import org.jaudiotagger.audio.flac.FlacFileReader;
+import org.jaudiotagger.audio.mp3.MP3File;
+import org.jaudiotagger.tag.FieldKey;
+import org.jaudiotagger.tag.Tag;
+import org.jaudiotagger.tag.datatype.Artwork;
+import org.jaudiotagger.tag.id3.AbstractID3v2Frame;
+import org.jaudiotagger.tag.id3.AbstractID3v2Tag;
+import org.jaudiotagger.tag.id3.framebody.FrameBodyAPIC;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
+import vip.xiaonuo.disk.config.es.FileSearch;
+import vip.xiaonuo.disk.domain.*;
+import vip.xiaonuo.disk.io.QiwenFile;
+import vip.xiaonuo.disk.mapper.*;
+import vip.xiaonuo.disk.service.IShareFileService;
+import vip.xiaonuo.disk.service.IShareService;
+import vip.xiaonuo.disk.service.IUserService;
+import vip.xiaonuo.disk.util.QiwenFileUtil;
+import vip.xiaonuo.disk.util.TreeNode;
+
+import javax.annotation.Resource;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.*;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+/**
+ * 文件逻辑处理组件
+ */
+@Slf4j
+@Component
+public class ResourceFileDealComp {
+
+    @Resource
+    ResourceFileMapper resourceFileMapper;
+
+    @Resource
+    ResourceUserFileMapper resourceUserFileMapper;
+
+    @Resource
+    private UFOPFactory ufopFactory;
+
+    @Resource
+    private MusicMapper musicMapper;
+
+
+    /**
+     * 还原父文件路径
+     * <p>
+     * 1、回收站文件还原操作会将文件恢复到原来的路径下,当还原文件的时候,如果父目录已经不存在了,则需要把父母录给还原
+     * 2、上传目录
+     *
+     * @param sessionUserId
+     */
+    public void restoreParentFilePath(QiwenFile qiwenFile, String sessionUserId) {
+
+        if (qiwenFile.isFile()) {
+            qiwenFile = qiwenFile.getParentFile();
+        }
+        while (qiwenFile.getParent() != null) {
+            String fileName = qiwenFile.getName();
+            String parentFilePath = qiwenFile.getParent();
+
+            LambdaQueryWrapper<ResourceUserFile> lambdaQueryWrapper = new LambdaQueryWrapper<>();
+            lambdaQueryWrapper.eq(ResourceUserFile::getFilePath, parentFilePath)
+                    .eq(ResourceUserFile::getFileName, fileName)
+                    .eq(ResourceUserFile::getDeleteFlag, "NOT_DELETE")
+                    .eq(ResourceUserFile::getIsDir, 1)
+                    .eq(ResourceUserFile::getUserId, sessionUserId);
+            List<ResourceUserFile> userFileList = resourceUserFileMapper.selectList(lambdaQueryWrapper);
+            if (userFileList.size() == 0) {
+                ResourceUserFile userFile = QiwenFileUtil.getQiwenDirResource(sessionUserId, parentFilePath, fileName);
+                try {
+                    resourceUserFileMapper.insert(userFile);
+                } catch (Exception e) {
+                    //ignore
+                }
+            }
+            qiwenFile = new QiwenFile(parentFilePath, true);
+        }
+    }
+
+
+    /**
+     * 获取重复文件名
+     * <p>
+     * 场景1: 文件还原时,在 savefilePath 路径下,保存 测试.txt 文件重名,则会生成 测试(1).txt
+     * 场景2: 上传文件时,在 savefilePath 路径下,保存 测试.txt 文件重名,则会生成 测试(1).txt
+     *
+     * @param userFile
+     * @param savefilePath
+     * @return
+     */
+    public String getRepeatFileName(ResourceUserFile userFile, String savefilePath) {
+        String fileName = userFile.getFileName();
+        String extendName = userFile.getExtendName();
+
+        String userId = userFile.getUserId();
+        int isDir = userFile.getIsDir();
+        LambdaQueryWrapper<ResourceUserFile> lambdaQueryWrapper = new LambdaQueryWrapper<>();
+        lambdaQueryWrapper.eq(ResourceUserFile::getFilePath, savefilePath)
+                .eq(ResourceUserFile::getDeleteFlag, "NOT_DELETE")
+                .eq(ResourceUserFile::getUserId, userId)
+                .eq(ResourceUserFile::getFileName, fileName)
+                .eq(ResourceUserFile::getIsDir, isDir);
+        if (userFile.isFile()) {
+            lambdaQueryWrapper.eq(ResourceUserFile::getExtendName, extendName);
+        }
+        List<ResourceUserFile> list = resourceUserFileMapper.selectList(lambdaQueryWrapper);
+        if (CollectionUtils.isEmpty(list)) {
+            return fileName;
+        }
+
+        int i = 0;
+
+        while (!CollectionUtils.isEmpty(list)) {
+            i++;
+            LambdaQueryWrapper<ResourceUserFile> lambdaQueryWrapper1 = new LambdaQueryWrapper<>();
+            lambdaQueryWrapper1.eq(ResourceUserFile::getFilePath, savefilePath)
+                    .eq(ResourceUserFile::getDeleteFlag, "NOT_DELETE")
+                    .eq(ResourceUserFile::getUserId, userId)
+                    .eq(ResourceUserFile::getFileName, fileName + "(" + i + ")")
+                    .eq(ResourceUserFile::getIsDir, isDir);
+            if (userFile.isFile()) {
+                lambdaQueryWrapper1.eq(ResourceUserFile::getExtendName, extendName);
+            }
+            list = resourceUserFileMapper.selectList(lambdaQueryWrapper1);
+
+        }
+
+        return fileName + "(" + i + ")";
+
+    }
+
+    public void parseMusicFile(String extendName, int storageType, String fileUrl, String fileId) {
+        File outFile = null;
+        InputStream inputStream = null;
+        FileOutputStream fileOutputStream = null;
+        try {
+            if ("mp3".equalsIgnoreCase(extendName) || "flac".equalsIgnoreCase(extendName)) {
+                Downloader downloader = ufopFactory.getDownloader(storageType);
+                DownloadFile downloadFile = new DownloadFile();
+                downloadFile.setFileUrl(fileUrl);
+                inputStream = downloader.getInputStream(downloadFile);
+                outFile = UFOPUtils.getTempFile(fileUrl);
+                if (!outFile.exists()) {
+                    outFile.createNewFile();
+                }
+                fileOutputStream = new FileOutputStream(outFile);
+                IOUtils.copy(inputStream, fileOutputStream);
+                Music music = new Music();
+                //  music.setMusicId(IdUtil.getSnowflakeNextIdStr());
+                music.setFileId(fileId);
+
+                Tag tag = null;
+                AudioHeader audioHeader = null;
+                if ("mp3".equalsIgnoreCase(extendName)) {
+                    MP3File f = (MP3File) AudioFileIO.read(outFile);
+                    tag = f.getTag();
+                    audioHeader = f.getAudioHeader();
+                    MP3File mp3file = new MP3File(outFile);
+                    if (mp3file.hasID3v2Tag()) {
+                        AbstractID3v2Tag id3v2Tag = mp3file.getID3v2TagAsv24();
+                        AbstractID3v2Frame frame = (AbstractID3v2Frame) id3v2Tag.getFrame("APIC");
+                        FrameBodyAPIC body;
+                        if (frame != null && !frame.isEmpty()) {
+                            body = (FrameBodyAPIC) frame.getBody();
+                            byte[] imageData = body.getImageData();
+                            music.setAlbumImage(Base64.getEncoder().encodeToString(imageData));
+                        }
+                        if (tag != null) {
+                            music.setArtist(tag.getFirst(FieldKey.ARTIST));
+                            music.setTitle(tag.getFirst(FieldKey.TITLE));
+                            music.setAlbum(tag.getFirst(FieldKey.ALBUM));
+                            music.setYear(tag.getFirst(FieldKey.YEAR));
+                            try {
+                                music.setTrack(tag.getFirst(FieldKey.TRACK));
+                            } catch (Exception e) {
+                                // ignore
+                            }
+
+                            music.setGenre(tag.getFirst(FieldKey.GENRE));
+                            music.setComment1(tag.getFirst(FieldKey.COMMENT));
+                            music.setLyrics(tag.getFirst(FieldKey.LYRICS));
+                            music.setComposer(tag.getFirst(FieldKey.COMPOSER));
+                            music.setAlbumArtist(tag.getFirst(FieldKey.ALBUM_ARTIST));
+                            music.setEncoder(tag.getFirst(FieldKey.ENCODER));
+                        }
+                    }
+                } else if ("flac".equalsIgnoreCase(extendName)) {
+                    AudioFile f = new FlacFileReader().read(outFile);
+                    tag = f.getTag();
+                    audioHeader = f.getAudioHeader();
+                    if (tag != null) {
+                        music.setArtist(StringUtils.join(tag.getFields(FieldKey.ARTIST), ","));
+                        music.setTitle(StringUtils.join(tag.getFields(FieldKey.TITLE), ","));
+                        music.setAlbum(StringUtils.join(tag.getFields(FieldKey.ALBUM), ","));
+                        music.setYear(StringUtils.join(tag.getFields(FieldKey.YEAR), ","));
+                        music.setTrack(StringUtils.join(tag.getFields(FieldKey.TRACK), ","));
+                        music.setGenre(StringUtils.join(tag.getFields(FieldKey.GENRE), ","));
+                        music.setComment1(StringUtils.join(tag.getFields(FieldKey.COMMENT), ","));
+                        music.setLyrics(StringUtils.join(tag.getFields(FieldKey.LYRICS), ","));
+                        music.setComposer(StringUtils.join(tag.getFields(FieldKey.COMPOSER), ","));
+                        music.setAlbumArtist(StringUtils.join(tag.getFields(FieldKey.ALBUM_ARTIST), ","));
+                        music.setEncoder(StringUtils.join(tag.getFields(FieldKey.ENCODER), ","));
+                        List<Artwork> artworkList = tag.getArtworkList();
+                        if (artworkList != null && !artworkList.isEmpty()) {
+                            Artwork artwork = artworkList.get(0);
+                            byte[] binaryData = artwork.getBinaryData();
+                            music.setAlbumImage(Base64.getEncoder().encodeToString(binaryData));
+                        }
+                    }
+
+                }
+
+                if (audioHeader != null) {
+                    music.setTrackLength(Float.parseFloat(audioHeader.getTrackLength() + ""));
+                }
+
+                if (StringUtils.isEmpty(music.getLyrics())) {
+                    try {
+
+                        String lyc = MusicUtils.getLyc(music.getArtist(), music.getTitle(), music.getAlbum());
+                        music.setLyrics(lyc);
+                    } catch (Exception e) {
+                        log.info(e.getMessage());
+                    }
+                }
+                musicMapper.insert(music);
+            }
+        } catch (Exception e) {
+            log.error("解析音乐信息失败!", e);
+        } finally {
+            IOUtils.closeQuietly(inputStream);
+            IOUtils.closeQuietly(fileOutputStream);
+            if (outFile != null) {
+                if (outFile.exists()) {
+                    outFile.delete();
+                }
+            }
+        }
+    }
+}

+ 58 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/component/UserDealComp.java

@@ -0,0 +1,58 @@
+package vip.xiaonuo.disk.component;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.qiwenshare.common.constant.RegexConstant;
+import vip.xiaonuo.disk.domain.user.UserBean;
+import vip.xiaonuo.disk.mapper.UserMapper;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.List;
+import java.util.regex.Pattern;
+
+@Component
+public class UserDealComp {
+    @Resource
+    UserMapper userMapper;
+
+
+    /**
+     * 检测用户名是否存在
+     *
+     * @param userBean
+     */
+    public Boolean isUserNameExit(UserBean userBean) {
+        LambdaQueryWrapper<UserBean> lambdaQueryWrapper = new LambdaQueryWrapper<>();
+        lambdaQueryWrapper.eq(UserBean::getUsername, userBean.getUsername());
+        List<UserBean> list = userMapper.selectList(lambdaQueryWrapper);
+        if (list != null && !list.isEmpty()) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * 检测手机号是否存在
+     *
+     * @param userBean
+     * @return
+     */
+    public Boolean isPhoneExit(UserBean userBean) {
+
+        LambdaQueryWrapper<UserBean> lambdaQueryWrapper = new LambdaQueryWrapper<>();
+        lambdaQueryWrapper.eq(UserBean::getTelephone, userBean.getTelephone());
+        List<UserBean> list = userMapper.selectList(lambdaQueryWrapper);
+        if (list != null && !list.isEmpty()) {
+            return true;
+        } else {
+            return false;
+        }
+
+    }
+
+    public Boolean isPhoneFormatRight(String phone){
+        boolean isRight = Pattern.matches(RegexConstant.PASSWORD_REGEX, phone);
+        return isRight;
+    }
+}

+ 20 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/es/ElasticSearchConfig.java

@@ -0,0 +1,20 @@
+package vip.xiaonuo.disk.config.es;
+
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import co.elastic.clients.json.jackson.JacksonJsonpMapper;
+import co.elastic.clients.transport.ElasticsearchTransport;
+import co.elastic.clients.transport.rest_client.RestClientTransport;
+import org.apache.http.HttpHost;
+import org.elasticsearch.client.RestClient;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class ElasticSearchConfig {
+    @Bean
+    public ElasticsearchClient elasticsearchClient(){
+        RestClient client = RestClient.builder(new HttpHost("localhost", 9200,"http")).build();
+        ElasticsearchTransport transport = new RestClientTransport(client,new JacksonJsonpMapper());
+        return new ElasticsearchClient(transport);
+    }
+}

+ 26 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/es/FileSearch.java

@@ -0,0 +1,26 @@
+package vip.xiaonuo.disk.config.es;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.Data;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class FileSearch {
+    private String indexName;
+    private String userFileId;
+    private String fileId;
+    private String fileName;
+    private String content;
+    private String fileUrl;
+    private Long fileSize;
+    private Integer storageType;
+    private String identifier;
+    private Long userId;
+    private String filePath;
+    private String extendName;
+    private Integer isDir;
+    private String uploadTime;
+    private Integer deleteFlag;
+    private String deleteTime;
+    private String deleteBatchNum;
+}

+ 9 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/jwt/JwtHeader.java

@@ -0,0 +1,9 @@
+package vip.xiaonuo.disk.config.jwt;
+
+import lombok.Data;
+
+@Data
+public class JwtHeader {
+    private String alg;
+    private String typ;
+}

+ 10 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/jwt/JwtPayload.java

@@ -0,0 +1,10 @@
+package vip.xiaonuo.disk.config.jwt;
+
+import lombok.Data;
+
+@Data
+public class JwtPayload {
+    private RegisterdClaims registerdClaims;
+
+}
+

+ 15 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/jwt/JwtProperties.java

@@ -0,0 +1,15 @@
+package vip.xiaonuo.disk.config.jwt;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Data
+@Component
+@ConfigurationProperties(prefix = "jwt")
+public class JwtProperties {
+    private String secret;
+    private JwtHeader header;
+    private JwtPayload payload;
+}
+

+ 11 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/jwt/RegisterdClaims.java

@@ -0,0 +1,11 @@
+package vip.xiaonuo.disk.config.jwt;
+
+import lombok.Data;
+
+@Data
+public class RegisterdClaims {
+    private String iss;
+    private String exp;
+    private String sub;
+    private String aud;
+}

+ 10 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/kafka/KafkaProducerConfiguration.java

@@ -0,0 +1,10 @@
+package vip.xiaonuo.disk.config.kafka;
+
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+import vip.xiaonuo.disk.properties.KafkaProducerProperties;
+
+@Configuration
+@EnableConfigurationProperties(KafkaProducerProperties.class)
+public class KafkaProducerConfiguration {
+}

+ 54 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/m3u8/FFmpegConfig.java

@@ -0,0 +1,54 @@
+package vip.xiaonuo.disk.config.m3u8;
+
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import net.bramp.ffmpeg.FFmpeg;
+import net.bramp.ffmpeg.FFprobe;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import javax.annotation.Resource;
+import java.io.File;
+
+//@Configuration
+@Slf4j
+public class FFmpegConfig {
+
+    //@Resource
+    private FilePath filePath;
+
+    @SneakyThrows
+    //@Bean
+    public FFmpeg fFmpeg() {
+        String path = "";
+        if (isLinux()){
+            path+=filePath.getFfmpegPathLinux() + File.separator + "ffmpeg";
+        }else if (isWindows()){
+            path+=filePath.getFfmpegPathWin() + File.separator + "ffmpeg.exe";
+        }
+        log.info("ffmpeg 路径为{}",path);
+        return new FFmpeg(path);
+    }
+
+    @SneakyThrows
+    //@Bean
+    public FFprobe fFprobe() {
+        String path = "";
+        if (isLinux()){
+            path+=filePath.getFfmpegPathLinux() + File.separator + "ffprobe";
+        }else if (isWindows()){
+            path+=filePath.getFfmpegPathWin() + File.separator + "ffprobe.exe";
+        }
+        log.info("ffprobe 路径为{}",path);
+        return new FFprobe(path);
+    }
+    public static boolean isLinux() {
+        return System.getProperty("os.name").toLowerCase().contains("linux");
+    }
+
+    public static boolean isWindows() {
+        return System.getProperty("os.name").toLowerCase().contains("windows");
+    }
+
+
+}

+ 33 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/m3u8/FilePath.java

@@ -0,0 +1,33 @@
+package vip.xiaonuo.disk.config.m3u8;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Data
+//@Component
+//@ConfigurationProperties(prefix = "m3u8.convertor")
+public class FilePath {
+    /**
+    * 文件上传临时路径 (本地文件转换不需要)
+    */
+    private String tempPath = "D:\\file\\tmp\\";
+
+    /**
+     * m3u8文件转换后,储存的根路径
+     */
+    private String basePath = "D:\\file\\m3u8\\";
+
+    /**
+     * m3u8文件转换后,储存的根路径
+     */
+    private String bigPath = "D:\\file\\big\\";
+
+    private String proxy = "m3u8/";
+
+    private String proxyURL= "http://127.0.0.1:8080/";
+
+    private String ffmpegPathWin = "C:\\Program Files\\ffmpeg\\bin";
+
+    private String ffmpegPathLinux = "ffmpeg";
+}

+ 128 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/m3u8/M3u8Component.java

@@ -0,0 +1,128 @@
+package vip.xiaonuo.disk.config.m3u8;
+
+import cn.hutool.core.io.FileUtil;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import net.bramp.ffmpeg.FFmpeg;
+import net.bramp.ffmpeg.FFmpegExecutor;
+import net.bramp.ffmpeg.FFprobe;
+import net.bramp.ffmpeg.builder.FFmpegBuilder;
+import net.bramp.ffmpeg.probe.FFmpegProbeResult;
+import net.bramp.ffmpeg.probe.FFmpegStream;
+import org.springframework.stereotype.Component;
+import org.springframework.web.multipart.MultipartFile;
+import vip.xiaonuo.disk.util.m3u8Util;
+import javax.annotation.Resource;
+import java.io.File;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+
+@Slf4j
+//@Component
+public class M3u8Component {
+
+    //@Resource
+    private FFmpeg ffmpeg;
+
+    //@Resource
+    private FFprobe ffprobe;
+
+    //@Resource
+    private FilePath filePath;
+
+
+
+    /**
+     * 视频文件转 m3u8
+     * 支持: .mp4 | .flv | .avi | .mov | .wmv | .wav
+     * @param file 视频文件
+     * @return 路径
+     */
+    public String mediaFileToM3u8(MultipartFile file){
+        if (file.isEmpty()) {
+            throw new RuntimeException("未发现文件");
+        }
+        log.info("开始解析视频");
+        long start = System.currentTimeMillis();
+        //临时目录创建
+        String tempFilePath = filePath.getTempPath();
+        if (!FileUtil.exist(tempFilePath)) {
+            FileUtil.mkdir(tempFilePath);
+        }
+        String filePathName = tempFilePath + file.getOriginalFilename();
+        File dest = new File(filePathName);
+        try {
+            file.transferTo(dest);
+        }catch (Exception e){
+            log.error("视频转m3u8格式存在异常,异常原因e:{}",e.getMessage());
+        }
+        long end = System.currentTimeMillis();
+        log.info("临时文件上传成功......耗时:{} ms", end - start);
+        String m3u8FilePath = localFileToM3u8(filePathName);
+        log.info("视频转换已完成 !");
+        return m3u8FilePath;
+    }
+
+    /**
+     * 本地媒体资源转换
+     * @param filePathName : 文件路径
+     * @return :
+     */
+    @SneakyThrows
+    public String localFileToM3u8(String filePathName) {
+        long startTime = System.currentTimeMillis();
+        final FFmpegProbeResult probe = ffprobe.probe(filePathName);
+        final List<FFmpegStream> streams = probe.getStreams().stream().filter(fFmpegStream -> fFmpegStream.codec_type != null).collect(Collectors.toList());
+        final Optional<FFmpegStream> audioStream = streams.stream().filter(fFmpegStream -> FFmpegStream.CodecType.AUDIO.equals(fFmpegStream.codec_type)).findFirst();
+        final Optional<FFmpegStream> videoStream = streams.stream().filter(fFmpegStream -> FFmpegStream.CodecType.VIDEO.equals(fFmpegStream.codec_type)).findFirst();
+
+        if (!audioStream.isPresent()) {
+            log.error("未发现音频流");
+        }
+        if (!videoStream.isPresent()) {
+            log.error("未发现视频流");
+        }
+        //m3u8文件 存储路径
+        String filePath = m3u8Util.generateFilePath(this.filePath.getBasePath());
+        if (!FileUtil.exist(filePath)) {
+            FileUtil.mkdir(filePath);
+        }
+        String mainName = m3u8Util.getFileMainName(filePathName);
+        String m3u8FileName = filePath + mainName + ".m3u8";
+
+        //下面这一串参数别乱动,经过调优的,1G视频大概需要10秒左右,如果是大佬随意改
+        //"-vsync", "2", "-c:v", "copy", "-c:a", "copy", "-tune", "fastdecode", "-hls_wrap", "0", "-hls_time", "10", "-hls_list_size", "0", "-threads", "12"
+        FFmpegBuilder builder = new FFmpegBuilder()
+                .setInput(filePathName)
+                .overrideOutputFiles(true)
+                .addOutput(m3u8FileName)//输出文件
+                .setFormat(probe.getFormat().format_name) //"mp4"
+                .setAudioBitRate(audioStream.map(fFmpegStream -> fFmpegStream.bit_rate).orElse(0L))
+                .setAudioChannels(1)
+                .setAudioCodec("aac")        // using the aac codec
+                .setAudioSampleRate(audioStream.get().sample_rate)
+                .setAudioBitRate(audioStream.get().bit_rate)
+                .setStrict(FFmpegBuilder.Strict.STRICT)
+                .setFormat("hls")
+                .setPreset("ultrafast")
+                .addExtraArgs("-vsync", "2", "-c:v", "copy", "-c:a", "copy", "-tune", "fastdecode", "-hls_time", "10", "-hls_list_size", "0", "-threads", "12")
+                .done();
+
+        FFmpegExecutor executor = new FFmpegExecutor(ffmpeg, ffprobe);
+        // Run a one-pass encode
+        executor.createJob(builder).run();
+
+        File dest = new File(filePathName);
+        if (dest.isFile() && dest.exists()) {
+            dest.delete();
+            System.gc();
+            log.warn("临时文件 {}已删除", dest.getName());
+        }
+        long endTime = System.currentTimeMillis();
+        log.info("文件:{} 转换完成!共耗时{} ms", dest.getName(), (endTime - startTime));
+        return m3u8FileName;
+    }
+
+}

+ 32 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/m3u8/SpringAsyncConfig.java

@@ -0,0 +1,32 @@
+package vip.xiaonuo.disk.config.m3u8;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+@Configuration
+@EnableAsync
+public class SpringAsyncConfig {
+    /**
+     * 线程池参数根据minIO设置,如果开启线程太多会被MinIO拒绝
+     * @return :
+     */
+    @Bean("minIOUploadTreadPool")
+    public ThreadPoolTaskExecutor  asyncServiceExecutorForMinIo() {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        // 设置核心线程数,采用IO密集 h/(1-拥塞)
+        executor.setCorePoolSize(6);
+        // 设置最大线程数,由于minIO连接数量有限,此处尽力设计大点
+        executor.setMaxPoolSize(500);
+        // 设置线程活跃时间(秒)
+        executor.setKeepAliveSeconds(30);
+        // 设置默认线程名称
+        executor.setThreadNamePrefix("minio-upload-task-");
+        // 等待所有任务结束后再关闭线程池
+        executor.setWaitForTasksToCompleteOnShutdown(true);
+        //执行初始化
+        executor.initialize();
+        return executor;
+    }
+}

+ 133 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/minio/MinioConfig.java

@@ -0,0 +1,133 @@
+package vip.xiaonuo.disk.config.minio;
+
+import io.minio.MinioClient;
+import io.minio.SetBucketPolicyArgs;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author 吴浩炼
+ * @since 2024/05/29
+ */
+@Configuration
+@ConfigurationProperties(prefix = "minio")
+public class MinioConfig {
+    /**
+     * 服务地址
+     */
+    private String url;
+    private String domain;
+
+    public String getDomain() {
+        return domain;
+    }
+
+    public void setDomain(String domain) {
+        this.domain = domain;
+    }
+
+    /**
+     * 用户名
+     */
+    private String accessKey;
+
+    /**
+     * 密码
+     */
+    private String secretKey;
+
+    /**
+     * 存储桶名称
+     */
+    private String bucketName;
+    /**
+     * 文件存储指定位置路径
+     */
+    private String filePath;
+
+    public String getUrl()
+    {
+        return url;
+    }
+
+    public void setUrl(String url)
+    {
+        this.url = url;
+    }
+
+    public String getAccessKey()
+    {
+        return accessKey;
+    }
+
+    public void setAccessKey(String accessKey)
+    {
+        this.accessKey = accessKey;
+    }
+
+    public String getSecretKey()
+    {
+        return secretKey;
+    }
+
+    public void setSecretKey(String secretKey)
+    {
+        this.secretKey = secretKey;
+    }
+
+    public String getBucketName()
+    {
+        return bucketName;
+    }
+
+    public void setBucketName(String bucketName)
+    {
+        this.bucketName = bucketName;
+    }
+
+    public String getFilePath() {
+        return filePath;
+    }
+
+    public void setFilePath(String filePath) {
+        this.filePath = filePath;
+    }
+
+
+    @Bean
+    public MinioClient getMinioClient()
+    {
+        MinioClient   minioClient=MinioClient.builder().endpoint(url).credentials(accessKey, secretKey).build();
+        // 设置存储桶策略为公开读取
+        String policy = "{\n" +
+                "  \"Version\": \"2012-10-17\",\n" +
+                "  \"Statement\": [\n" +
+                "    {\n" +
+                "      \"Effect\": \"Allow\",\n" +
+                "      \"Principal\": {\"AWS\": \"*\"},\n" +
+                "      \"Action\": [\"s3:GetBucketLocation\", \"s3:ListBucket\"],\n" +
+                "      \"Resource\": \"arn:aws:s3:::" + bucketName + "\"\n" +
+                "    },\n" +
+                "    {\n" +
+                "      \"Effect\": \"Allow\",\n" +
+                "      \"Principal\": {\"AWS\": \"*\"},\n" +
+                "      \"Action\": \"s3:GetObject\",\n" +
+                "      \"Resource\": \"arn:aws:s3:::" + bucketName + "/*\"\n" +
+                "    }\n" +
+                "  ]\n" +
+                "}";
+
+        try {
+            minioClient.setBucketPolicy(
+                    SetBucketPolicyArgs.builder()
+                            .bucket(bucketName)
+                            .config(policy)
+                            .build());
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+        return minioClient;
+    }
+
+}

+ 80 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/minio/MinioProperties.java

@@ -0,0 +1,80 @@
+package vip.xiaonuo.disk.config.minio;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.util.Objects;
+
+/**
+ * @author 吴浩炼
+ * @since 2024/05/30
+ */
+@ConfigurationProperties(prefix = "ufop.minio")
+public class MinioProperties {
+    private String endpoint;
+
+    private String accessKey;
+
+    private String secretKey;
+
+    private String bucketName;
+
+    public String getEndpoint() {
+        return endpoint;
+    }
+
+    public void setEndpoint(String endpoint) {
+        this.endpoint = endpoint;
+    }
+
+    public String getAccessKey() {
+        return accessKey;
+    }
+
+    public void setAccessKey(String accessKey) {
+        this.accessKey = accessKey;
+    }
+
+    public String getSecretKey() {
+        return secretKey;
+    }
+
+    public void setSecretKey(String secretKey) {
+        this.secretKey = secretKey;
+    }
+
+    public String getBucketName() {
+        return bucketName;
+    }
+
+    public void setBucketName(String bucketName) {
+        this.bucketName = bucketName;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        MinioProperties that = (MinioProperties) o;
+        return Objects.equals(endpoint, that.endpoint) && Objects.equals(accessKey, that.accessKey) &&
+                Objects.equals(secretKey, that.secretKey) && Objects.equals(bucketName, that.bucketName);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(endpoint, accessKey, secretKey, bucketName);
+    }
+
+    @Override
+    public String toString() {
+        return "MinioProperties{" +
+                "endpoint='" + endpoint + '\'' +
+                ", accessKey='" + accessKey + '\'' +
+                ", secretKey='" + secretKey + '\'' +
+                ", bucketName='" + bucketName + '\'' +
+                '}';
+    }
+}

+ 33 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/minio/WebMvcConfig.java

@@ -0,0 +1,33 @@
+package vip.xiaonuo.disk.config.minio;
+
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+
+/**
+ * @author 吴浩炼
+ * @since 2024/05/30
+ */
+@Configuration
+public class WebMvcConfig {
+
+    /**
+     * 开启跨域
+     */
+    @Bean
+    public FilterRegistrationBean<CorsFilter> corsFilterBean() {
+        CorsConfiguration corsConfiguration = new CorsConfiguration();
+        corsConfiguration.setAllowCredentials(true);
+        corsConfiguration.addAllowedOriginPattern("*");
+        corsConfiguration.addAllowedHeader("*");
+        corsConfiguration.addAllowedMethod("*");
+        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+        source.registerCorsConfiguration("/**", corsConfiguration);
+        FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));
+        bean.setOrder(-111111);
+        return bean;
+    }
+}

+ 82 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/onlyoffice/OnlyofficeConfiguration.java

@@ -0,0 +1,82 @@
+/**
+ *
+ * (c) Copyright Ascensio System SIA 2021
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package vip.xiaonuo.disk.config.onlyoffice;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import vip.xiaonuo.disk.office.documentserver.storage.FileStoragePathBuilder;
+import vip.xiaonuo.disk.office.documentserver.util.SSLUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.modelmapper.ModelMapper;
+import org.modelmapper.convention.MatchingStrategies;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import javax.annotation.PostConstruct;
+
+@Configuration
+public class OnlyofficeConfiguration {
+
+    @Value("${files.storage}")
+    private String storageAddress;
+
+    @Value("${files.docservice.verify-peer-off}")
+    private String verifyPerrOff;
+
+    @Autowired
+    private FileStoragePathBuilder storagePathBuilder;
+
+    @Autowired
+    private SSLUtils ssl;
+
+    @Bean
+    public ModelMapper mapper(){  // create the model mapper
+        ModelMapper mapper = new ModelMapper();
+        mapper.getConfiguration()  // get the mapper configuration and set new parameters to it
+                .setMatchingStrategy(MatchingStrategies.STRICT)  // specify the STRICT matching strategy
+                .setFieldMatchingEnabled(true)  // define if the field matching is enabled or not
+                .setSkipNullEnabled(true)  // define if null value will be skipped or not
+                .setFieldAccessLevel(org.modelmapper.config.Configuration.AccessLevel.PRIVATE);  // specify the PRIVATE field access level
+        return mapper;
+    }
+
+//    @Bean
+//    public JSONParser jsonParser(){  // create JSON parser
+//        return new JSONParser();
+//    }
+
+    @PostConstruct
+    public void init(){  // initialize the storage path builder
+        storagePathBuilder.configure(StringUtils.isEmpty(storageAddress) ? null : storageAddress);
+        if(!verifyPerrOff.isEmpty()) {
+            try{
+                if(verifyPerrOff.equals("true")) {
+                    ssl.turnOffSslChecking(); //the certificate will be ignored
+                } else {
+                    ssl.turnOnSslChecking(); //the certificate will be verified
+                }
+            } catch(Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+
+}

+ 38 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/plus/MybatisPlusConfig.java

@@ -0,0 +1,38 @@
+package vip.xiaonuo.disk.config.plus;
+
+import com.baomidou.mybatisplus.annotation.DbType;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/*******************************************************
+ * mybatis-plus分页拦截插件
+ * @ClassName : MybatisPlusConfig
+ * @author: lin xue peng
+ * @time: 2023-06-09 13:23:24
+  * *****************************************************/
+@Configuration
+@MapperScan("vip.xiaonuo.disk.mapper")
+public class MybatisPlusConfig {
+
+
+    @Bean
+    public MybatisPlusInterceptor mybatisPlusInterceptor() {
+        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
+        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
+        interceptor.addInnerInterceptor(paginationInnerInterceptor);
+        return interceptor;
+    }
+
+
+    /**
+     * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
+     */
+//    @Bean
+//    public ConfigurationCustomizer configurationCustomizer() {
+//        return configuration -> configuration.setUseDeprecatedExecutor(false);
+//    }
+
+}

+ 85 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/threadpool/AsyncThreadPoolAutoConfiguration.java

@@ -0,0 +1,85 @@
+package vip.xiaonuo.disk.config.threadpool;
+
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.scheduling.annotation.AsyncConfigurer;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.stereotype.Component;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * 异步线程池配置 AsyncConfigurer在applicationContext早期初始化,如果需要依赖于其它的bean,尽可能的将它们声明为lazy
+ */
+@Slf4j
+@EnableAsync
+@Component
+@EnableConfigurationProperties(AsyncThreadPoolProperties.class)
+public class AsyncThreadPoolAutoConfiguration implements AsyncConfigurer {
+
+    @Autowired
+    private AsyncThreadPoolProperties asyncThreadPoolProperties;
+
+    /**
+     * 定义线程池
+     * 使用{@link java.util.concurrent.LinkedBlockingQueue}(FIFO)队列,是一个用于并发环境下的阻塞队列集合类
+     * ThreadPoolTaskExecutor不是完全被IOC容器管理的bean,可以在方法上加上@Bean注解交给容器管理,这样可以将taskExecutor.initialize()方法调用去掉,容器会自动调用
+     *
+     * @return
+     */
+    @Bean("asyncTaskExecutor")
+    @Override
+    public Executor getAsyncExecutor() {
+        //Java虚拟机可用的处理器数
+        int processors = Runtime.getRuntime().availableProcessors();
+        //定义线程池
+        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
+        //核心线程数
+        taskExecutor.setCorePoolSize(Objects.nonNull(asyncThreadPoolProperties.getCorePoolSize()) ? asyncThreadPoolProperties.getCorePoolSize() : processors);
+        //线程池最大线程数,默认:40000
+        taskExecutor.setMaxPoolSize(Objects.nonNull(asyncThreadPoolProperties.getMaxPoolSize()) ? asyncThreadPoolProperties.getMaxPoolSize() : 40000);
+        //线程队列最大线程数,默认:80000
+        taskExecutor.setQueueCapacity(Objects.nonNull(asyncThreadPoolProperties.getMaxPoolSize()) ? asyncThreadPoolProperties.getMaxPoolSize() : 80000);
+        //线程名称前缀
+        taskExecutor.setThreadNamePrefix(StringUtils.isNotEmpty(asyncThreadPoolProperties.getThreadNamePrefix()) ? asyncThreadPoolProperties.getThreadNamePrefix() : "Async-ThreadPool-");
+        //线程池中线程最大空闲时间,默认:60,单位:秒
+        taskExecutor.setKeepAliveSeconds(asyncThreadPoolProperties.getKeepAliveSeconds());
+        //核心线程是否允许超时,默认:false
+        taskExecutor.setAllowCoreThreadTimeOut(asyncThreadPoolProperties.isAllowCoreThreadTimeOut());
+        //IOC容器关闭时是否阻塞等待剩余的任务执行完成,默认:false(必须设置setAwaitTerminationSeconds)
+        taskExecutor.setWaitForTasksToCompleteOnShutdown(asyncThreadPoolProperties.isWaitForTasksToCompleteOnShutdown());
+        //阻塞IOC容器关闭的时间,默认:10秒(必须设置setWaitForTasksToCompleteOnShutdown)
+        taskExecutor.setAwaitTerminationSeconds(asyncThreadPoolProperties.getAwaitTerminationSeconds());
+        /**
+         * 拒绝策略,默认是AbortPolicy
+         * AbortPolicy:丢弃任务并抛出RejectedExecutionException异常
+         * DiscardPolicy:丢弃任务但不抛出异常
+         * DiscardOldestPolicy:丢弃最旧的处理程序,然后重试,如果执行器关闭,这时丢弃任务
+         * CallerRunsPolicy:执行器执行任务失败,则在策略回调方法中执行任务,如果执行器关闭,这时丢弃任务
+         */
+        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
+        //初始化
+        //taskExecutor.initialize();
+
+        return taskExecutor;
+    }
+
+    /**
+     * 异步方法执行的过程中抛出的异常捕获
+     *
+     * @return AsyncUncaughtExceptionHandler
+     */
+    @Override
+    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
+        return new BaseAsyncUncaughtExceptionHandler();
+    }
+}
+

+ 51 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/threadpool/AsyncThreadPoolProperties.java

@@ -0,0 +1,51 @@
+package vip.xiaonuo.disk.config.threadpool;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * 异步线程池配置文件
+ */
+@Data
+@ConfigurationProperties(prefix = "spring.async-thread-pool")
+public class AsyncThreadPoolProperties  {
+    /**
+     * 是否启动异步线程池,默认 false
+     */
+    private boolean enable;
+    /**
+     * 核心线程数,默认:Java虚拟机可用线程数
+     */
+    private Integer corePoolSize=8;
+    /**
+     * 线程池最大线程数,默认:40000
+     */
+    private Integer maxPoolSize=500;
+    /**
+     * 线程队列最大线程数,默认:80000
+     */
+    private Integer queueCapacity = 5;
+
+    /**
+     * 线程池中线程最大空闲时间,默认:60,单位:秒
+     */
+    private Integer keepAliveSeconds = 600;
+    /**
+     * 自定义线程名前缀,默认:Async-ThreadPool-
+     */
+    private String threadNamePrefix = "async-threadpool-";
+    /**
+     * 核心线程是否允许超时,默认false
+     */
+    private boolean allowCoreThreadTimeOut;
+    /**
+     * IOC容器关闭时是否阻塞等待剩余的任务执行完成,默认:false(必须设置setAwaitTerminationSeconds)
+     */
+    private boolean waitForTasksToCompleteOnShutdown;
+    /**
+     * 阻塞IOC容器关闭的时间,默认:10秒(必须设置setWaitForTasksToCompleteOnShutdown)
+     */
+    private int awaitTerminationSeconds = 10;
+
+}
+

+ 16 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/config/threadpool/BaseAsyncUncaughtExceptionHandler.java

@@ -0,0 +1,16 @@
+package vip.xiaonuo.disk.config.threadpool;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+@Slf4j
+public class BaseAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {
+    @Override
+    public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
+        log.error("捕获线程异常method[{}] params{}", method, Arrays.toString(objects));
+        log.error("线程异常");
+    }
+}

+ 22 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/constant/CommonFileTypeEnum.java

@@ -0,0 +1,22 @@
+package vip.xiaonuo.disk.constant;
+
+public enum CommonFileTypeEnum {
+    EVERYONE(0, "所有人"),
+    PERSONAL(1, "个人");
+
+
+    private int type;
+    private String desc;
+    CommonFileTypeEnum(int type, String desc) {
+        this.type = type;
+        this.desc = desc;
+    }
+
+    public int getType() {
+        return type;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+}

+ 26 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/constant/FileType.java

@@ -0,0 +1,26 @@
+/**
+ *
+ * (c) Copyright Ascensio System SIA 2021
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package vip.xiaonuo.disk.constant;
+
+public enum FileType
+{
+    Word,
+    Cell,
+    Slide
+}

+ 28 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/constant/FileTypeEnum.java

@@ -0,0 +1,28 @@
+package vip.xiaonuo.disk.constant;
+
+public enum FileTypeEnum {
+    TOTAL(0, "全部"),
+    PICTURE(1, "图片"),
+    DOCUMENT(2, "文档"),
+    VIDEO(3, "视频"),
+    MUSIC(4, "音乐"),
+    OTHER(5, "其他"),
+    SHARE(6, "分享"),
+    RECYCLE(7, "回收站");
+
+
+    private int type;
+    private String desc;
+    FileTypeEnum(int type, String desc) {
+        this.type = type;
+        this.desc = desc;
+    }
+
+    public int getType() {
+        return type;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+}

+ 30 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/consumer/TranscodingConsumer.java

@@ -0,0 +1,30 @@
+package vip.xiaonuo.disk.consumer;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.kafka.clients.consumer.ConsumerRecord;
+import org.springframework.kafka.annotation.KafkaListener;
+import org.springframework.kafka.support.Acknowledgment;
+import org.springframework.stereotype.Component;
+import vip.xiaonuo.disk.service.TranscodingService;
+
+import javax.annotation.Resource;
+import java.util.List;
+@Slf4j
+@Component
+public class TranscodingConsumer {
+
+    @Resource
+    private TranscodingService transcodingService;
+    @KafkaListener(containerFactory = "listenerFactory", topics = "TRANSCODING_RESOURCE", groupId = "transcoding_resource_group")
+    public void transcodingResourceListen(List<ConsumerRecord<String, String>> consumerRecordList, Acknowledgment acknowledgment) {
+        try {
+            log.info("transcodingResourceListen===================");
+            log.info("transcodingResourceListen===================");
+            log.info("transcodingResourceListen===================");
+            transcodingService.transcodingResourceParallel(consumerRecordList);
+            acknowledgment.acknowledge();
+        } catch (Exception e) {
+            log.error("transcodingResourceListen bath insert failed, exception is", e);
+        }
+    }
+}

+ 78 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/consumer/config/ConsumerConfig.java

@@ -0,0 +1,78 @@
+package vip.xiaonuo.disk.consumer.config;
+
+import org.apache.kafka.common.serialization.StringDeserializer;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
+import org.springframework.kafka.config.KafkaListenerContainerFactory;
+import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
+import org.springframework.kafka.listener.ConcurrentMessageListenerContainer;
+import org.springframework.kafka.listener.ContainerProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+@Component
+public class ConsumerConfig {
+    @Value("${kafkaproducer-config.servers}")
+    private String servers;
+
+    @Value("${kafkaproducer-config.username}")
+    private String username;
+
+    @Value("${kafkaproducer-config.pwd}")
+    private String pwd;
+    @Bean
+    KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<String, String>> listenerFactory() {
+        ConcurrentKafkaListenerContainerFactory<String, String> factory = new
+                ConcurrentKafkaListenerContainerFactory<>();
+        factory.setConsumerFactory(new DefaultKafkaConsumerFactory<>(consumerConfigs()));
+
+        // 3s拉取一次数据
+        factory.getContainerProperties().setIdleBetweenPolls(TimeUnit.SECONDS.toMillis(2));
+        // 此处需要额外设置为手动提交
+        factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.MANUAL);
+        // 开启批量监听
+        factory.setBatchListener(true);
+
+        return factory;
+    }
+
+
+    private Map<String, Object> consumerConfigs() {
+        Map<String, Object> props = new HashMap<>();
+        //kafka消费的的地址
+        props.put(org.apache.kafka.clients.consumer.ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, servers);
+        //是否自动提交
+        props.put(org.apache.kafka.clients.consumer.ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");
+
+        //请求超时时间,5/3 min
+        props.put(org.apache.kafka.clients.consumer.ConsumerConfig.REQUEST_TIMEOUT_MS_CONFIG, "100000");
+        //从poll(拉)的回话处理时长
+        props.put(org.apache.kafka.clients.consumer.ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000");
+        //超时时间,5min
+        props.put(org.apache.kafka.clients.consumer.ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "300000");
+        props.put(org.apache.kafka.clients.consumer.ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, "86400000");
+        //单个分区获取消息的最大值为900M
+        props.put(org.apache.kafka.clients.consumer.ConsumerConfig.MAX_PARTITION_FETCH_BYTES_CONFIG, 943718400);
+        //一次最大拉取的条数
+        props.put(org.apache.kafka.clients.consumer.ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 500);
+        props.put(org.apache.kafka.clients.consumer.ConsumerConfig.FETCH_MAX_BYTES_CONFIG, 1073741824);
+        //earliest当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,从头开始消费
+        //latest当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,消费新产生的该分区下的数据
+        //none topic各分区都存在已提交的offset时,从offset后开始消费;只要有一个分区不存在已提交的offset,则抛出异常
+        props.put(org.apache.kafka.clients.consumer.ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
+        //序列化
+        props.put(org.apache.kafka.clients.consumer.ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
+        props.put(org.apache.kafka.clients.consumer.ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
+        props.put("sasl.jaas.config",
+                "org.apache.kafka.common.security.scram.ScramLoginModule required username=\"" + username + "\" password=\"" + pwd + "\";");
+        props.put("sasl.mechanism", "PLAIN");
+        props.put("security.protocol", "SASL_PLAINTEXT");
+
+        return props;
+    }
+
+}

+ 126 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/ClassTeamController.java

@@ -0,0 +1,126 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.disk.param.ClassTeamAddParam;
+import vip.xiaonuo.disk.param.ClassTeamEditParam;
+import vip.xiaonuo.disk.param.ClassTeamIdParam;
+import vip.xiaonuo.disk.param.ClassTeamPageParam;
+import vip.xiaonuo.disk.service.ClassTeamService;
+import vip.xiaonuo.disk.vo.classteam.ClassTeamVo;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+
+/**
+ * class_team控制器
+ *
+ * @author pans
+ * @date  2025/07/01 19:24
+ */
+@Api(tags = "class_team控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class ClassTeamController {
+
+    @Resource
+    private ClassTeamService classTeamService;
+
+    /**
+     * 获取class_team分页
+     *
+     * @author pans
+     * @date  2025/07/01 19:24
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取class_team分页")
+    @GetMapping("/disk/team/page")
+    public CommonResult<Page<ClassTeamVo>> page(ClassTeamPageParam classTeamPageParam) {
+        return CommonResult.data(classTeamService.page(classTeamPageParam));
+    }
+
+    /**
+     * 添加class_team
+     *
+     * @author pans
+     * @date  2025/07/01 19:24
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加class_team")
+    @CommonLog("添加class_team")
+    @PostMapping("/disk/team/add")
+    public CommonResult<String> add(@RequestBody @Valid ClassTeamAddParam classTeamAddParam) {
+        classTeamService.add(classTeamAddParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑class_team
+     *
+     * @author pans
+     * @date  2025/07/01 19:24
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑class_team")
+    @CommonLog("编辑class_team")
+    @PostMapping("/disk/team/edit")
+    public CommonResult<String> edit(@RequestBody @Valid ClassTeamEditParam classTeamEditParam) {
+        classTeamService.edit(classTeamEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除class_team
+     *
+     * @author pans
+     * @date  2025/07/01 19:24
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除class_team")
+    @CommonLog("删除class_team")
+    @PostMapping("/disk/team/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<ClassTeamIdParam> classTeamIdParamList) {
+        classTeamService.delete(classTeamIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取class_team详情
+     *
+     * @author pans
+     * @date  2025/07/01 19:24
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取class_team详情")
+    @GetMapping("/disk/team/detail")
+    public CommonResult<ClassTeamVo> detail(@Valid ClassTeamIdParam classTeamIdParam) {
+        return CommonResult.data(classTeamService.detail(classTeamIdParam));
+    }
+
+}

+ 163 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CollectCourseController.java

@@ -0,0 +1,163 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.disk.domain.CollectCourse;
+import vip.xiaonuo.disk.param.CollectCourseAddParam;
+import vip.xiaonuo.disk.param.CollectCourseEditParam;
+import vip.xiaonuo.disk.param.CollectCourseIdParam;
+import vip.xiaonuo.disk.param.CollectCoursePageParam;
+import vip.xiaonuo.disk.service.CollectCourseService;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+
+/**
+ * 收藏课程控制器
+ *
+ * @author 金吉龙
+ * @date  2025/07/01 19:01
+ */
+@Api(tags = "收藏课程控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class CollectCourseController {
+
+    @Resource
+    private CollectCourseService collectCourseService;
+
+    /**
+     * 获取收藏课程分页
+     *
+     * @author 金吉龙
+     * @date  2025/07/01 19:01
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取收藏课程分页")
+    //@SaCheckPermission("/disk/collectCourse/page")
+    @GetMapping("/disk/collectCourse/page")
+    public CommonResult<Page<CollectCourse>> page(CollectCoursePageParam collectCoursePageParam) {
+        return CommonResult.data(collectCourseService.page(collectCoursePageParam));
+    }
+
+    /**
+     * 收藏课程
+     *
+     * @author 金吉龙
+     * @date  2025/07/01 19:01
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加收藏课程")
+    @CommonLog("添加收藏课程")
+    //@SaCheckPermission("/disk/collectCourse/add")
+    @PostMapping("/disk/collectCourse/add")
+    public CommonResult<String> add(@RequestBody @Valid CollectCourseAddParam collectCourseAddParam) {
+        collectCourseService.add(collectCourseAddParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 取消收藏课程
+     *
+     * @author 金吉龙
+     * @date  2025/07/01 19:01
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加收藏课程")
+    @CommonLog("添加收藏课程")
+    //@SaCheckPermission("/disk/collectCourse/cancel")
+    @PostMapping("/disk/collectCourse/cancel")
+    public CommonResult<String> cancel(@RequestBody @Valid CollectCourseAddParam collectCourseAddParam) {
+        collectCourseService.cancel(collectCourseAddParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 校验是否收藏此课程
+     *
+     * @author 金吉龙
+     * @date  2025/07/01 19:01
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加收藏课程")
+    @CommonLog("添加收藏课程")
+    //@SaCheckPermission("/disk/collectCourse/checkCollect")
+    @PostMapping("/disk/collectCourse/checkCollect")
+    public CommonResult<Integer> checkCollect(@RequestBody @Valid CollectCourseAddParam collectCourseAddParam) {
+        return collectCourseService.checkCollect(collectCourseAddParam);
+    }
+
+    /**
+     * 编辑收藏课程
+     *
+     * @author 金吉龙
+     * @date  2025/07/01 19:01
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑收藏课程")
+    @CommonLog("编辑收藏课程")
+    //@SaCheckPermission("/disk/collectCourse/edit")
+    @PostMapping("/disk/collectCourse/edit")
+    public CommonResult<String> edit(@RequestBody @Valid CollectCourseEditParam collectCourseEditParam) {
+        collectCourseService.edit(collectCourseEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除收藏课程
+     *
+     * @author 金吉龙
+     * @date  2025/07/01 19:01
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除收藏课程")
+    @CommonLog("删除收藏课程")
+    //@SaCheckPermission("/disk/collectCourse/delete")
+    @PostMapping("/disk/collectCourse/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<CollectCourseIdParam> collectCourseIdParamList) {
+        collectCourseService.delete(collectCourseIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取收藏课程详情
+     *
+     * @author 金吉龙
+     * @date  2025/07/01 19:01
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取收藏课程详情")
+    //@SaCheckPermission("/disk/collectCourse/detail")
+    @GetMapping("/disk/collectCourse/detail")
+    public CommonResult<CollectCourse> detail(@Valid CollectCourseIdParam collectCourseIdParam) {
+        return CommonResult.data(collectCourseService.detail(collectCourseIdParam));
+    }
+
+}

+ 256 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CollegeController.java

@@ -0,0 +1,256 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import cn.hutool.core.lang.tree.Tree;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.common.util.TreeUtil;
+import vip.xiaonuo.disk.domain.College;
+import vip.xiaonuo.disk.param.CollegeAddParam;
+import vip.xiaonuo.disk.param.CollegeEditParam;
+import vip.xiaonuo.disk.param.CollegeIdParam;
+import vip.xiaonuo.disk.param.CollegePageParam;
+import vip.xiaonuo.disk.service.CollegeService;
+import vip.xiaonuo.sys.modular.user.service.SysUserService;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * college控制器
+ *
+ * @author pans
+ * @date  2025/06/26 09:09
+ */
+@Api(tags = "college控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class CollegeController {
+
+    @Resource
+    private CollegeService collegeService;
+    @Resource
+    private SysUserService sysUserService;
+
+    /**
+     * 获取college分页
+     *
+     * @author pans
+     * @date  2025/06/26 09:09
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取college分页")
+    @GetMapping("/disk/college/page")
+    public CommonResult<Page<College>> page(CollegePageParam collegePageParam) {
+        return CommonResult.data(collegeService.page(collegePageParam));
+    }
+
+    /**
+     * 获取组织树
+     *
+     * @author xuyuxiang
+     * @date 2022/4/24 20:00
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("获取组织树")
+    @GetMapping("/disk/college/tree")
+    public CommonResult<List<Tree<String>>> tree() {
+        return CommonResult.data(collegeService.tree());
+    }
+
+
+    /**
+     * 获取组织树
+     *
+     * @author xuyuxiang
+     * @date 2022/4/24 20:00
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("获取组织树")
+    @GetMapping("/disk/college/treeAll")
+    public CommonResult<List<Tree<String>>> treeAll() {
+        return CommonResult.data(collegeService.treeAll());
+    }
+
+    /**
+     * 添加组织
+     *
+     * @author xuyuxiang
+     * @date 2022/4/24 20:47
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("添加组织")
+    @CommonLog("添加组织")
+    @PostMapping("/disk/college/add")
+    public CommonResult<String> add(@RequestBody @Valid CollegeAddParam sysOrgAddParam) {
+        collegeService.add(sysOrgAddParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑组织
+     *
+     * @author xuyuxiang
+     * @date 2022/4/24 20:47
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("编辑组织")
+    @CommonLog("编辑组织")
+    @PostMapping("/disk/college/edit")
+    public CommonResult<String> edit(@RequestBody @Valid CollegeEditParam collegeEditParam) {
+        collegeService.edit(collegeEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除组织
+     *
+     * @author xuyuxiang
+     * @date 2022/4/24 20:00
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("删除组织")
+    @CommonLog("删除组织")
+    @PostMapping("/disk/college/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                       CommonValidList<CollegeIdParam> collegeIdParamList) {
+        collegeService.delete(collegeIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取组织详情
+     *
+     * @author xuyuxiang
+     * @date 2022/4/24 20:00
+     */
+    @ApiOperationSupport(order = 6)
+    @ApiOperation("获取组织详情")
+    @GetMapping("/disk/college/detail")
+    public CommonResult<College> detail(@Valid CollegeIdParam collegeIdParam) {
+        return CommonResult.data(collegeService.detail(collegeIdParam));
+    }
+
+    /* ====组织部分所需要用到的选择器==== */
+
+    /**
+     * 获取组织树选择器
+     *
+     * @author xuyuxiang
+     * @date 2022/4/24 20:00
+     */
+    @ApiOperationSupport(order = 7)
+    @ApiOperation("获取组织树选择器")
+    @GetMapping("/disk/college/orgTreeSelector")
+    public CommonResult<List<Tree<String>>> orgTreeSelector() {
+        return CommonResult.data(collegeService.orgTreeSelector());
+    }
+
+
+    /**
+     * 获取院系人员树-一次查回(担心卡,暂时不采用,预备用)
+     *
+     * @author xuyuxiang
+     * @date 2022/4/24 20:00
+     */
+    @ApiOperationSupport(order = 7)
+    @ApiOperation("获取院系人员树-一次查回")
+    @GetMapping("/disk/college/orgUserTreeSelector")
+    public CommonResult<List<Map<String,Object>>> orgUserTreeSelector(HttpServletRequest req) {
+        Map param=new HashMap();
+        Map result = new HashMap();
+        List<Map<String,Object>> resultList= null;
+        try {
+            String collegeParentId = "0";
+            if(StringUtils.isNotEmpty(req.getParameter("collegeParentId")))collegeParentId=req.getParameter("collegeParentId");
+            //用于装当前单位的map
+            Map nowData = new HashMap();
+            List<Map<String, Object>> tree = new ArrayList<>();
+            //最终用来化成树的集合
+            List<Map<String,Object>> dataList=new ArrayList<>();
+            //查询该父级id的子级院系
+            List<Map<String,Object>> orgList=collegeService.getOrgUserChildList(param);
+            List<Map<String,Object>> userList=sysUserService.getOrgUserChildList(param);
+            System.out.println("----------------------------------------------------------------组织人员树数据打印开始");
+            dataList.addAll(orgList);
+            dataList.addAll(userList);
+            for(Map one:dataList)
+            {
+                if(one.get("id").equals(collegeParentId))
+                {
+                    nowData=one;
+                    break;
+                }
+                System.out.println("----------------------------------------------------------------组织人员树数据打印逐条数据"+one);
+            }
+            tree = TreeUtil.getThree(dataList,collegeParentId);
+            System.out.println("----------------------------------------------------------------组织人员树数据打印树数据"+tree);
+            System.out.println("----------------------------------------------------------------组织人员树数据打印结束");
+            //如果传进来的id不为空,则把传进来的id也作为一条数据
+            resultList = new ArrayList<>();
+            if(nowData.get("id")!=null)
+            {
+                nowData.put("children",tree);
+                resultList.add(nowData);
+                result.put("result",resultList);
+            }
+            else
+            {
+                resultList.addAll(tree);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return CommonResult.data(resultList);
+    }
+
+    /**
+     * 获取院系人员树-分级请求
+     *
+     * @author xuyuxiang
+     * @date 2022/4/24 20:00
+     */
+    @ApiOperationSupport(order = 7)
+    @ApiOperation("获取组织人员树-分级请求")
+    @GetMapping("/disk/college/orgUserTreeRespectively")
+    public CommonResult<List<Map<String, Object>>> orgUserTreeRespectively(HttpServletRequest req) {
+        Map param =new HashMap();
+        String collegeParentId="0";
+        if(StringUtils.isNotEmpty(req.getParameter("collegeParentId")))collegeParentId=req.getParameter("collegeParentId");
+        param.put("collegeParentId",collegeParentId );
+        List<Map<String, Object>> result=collegeService.orgUserTreeRespectively(param);
+        return CommonResult.data(result);
+    }
+
+
+}

+ 157 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CollegeOrgController.java

@@ -0,0 +1,157 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.hutool.core.collection.CollectionUtil;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.common.util.AuthUtil;
+import vip.xiaonuo.disk.domain.CollegeOrg;
+import vip.xiaonuo.disk.param.collegeOrg.CollegeOrgAddParam;
+import vip.xiaonuo.disk.param.collegeOrg.CollegeOrgEditParam;
+import vip.xiaonuo.disk.param.collegeOrg.CollegeOrgIdParam;
+import vip.xiaonuo.disk.param.collegeOrg.CollegeOrgPageParam;
+import vip.xiaonuo.disk.param.collegeUser.CollegeUserAddParam;
+import vip.xiaonuo.disk.service.CollegeOrgService;
+
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import java.util.List;
+
+/**
+ * 下发组织日志表控制器
+ *
+ * @author pans
+ * @date  2025/09/04 15:14
+ */
+@Slf4j
+@Api(tags = "下发组织日志表控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class CollegeOrgController {
+
+    @Resource
+    private CollegeOrgService collegeOrgService;
+
+    @Resource
+    private AuthUtil authUtil;
+
+    /**
+     * 获取下发组织日志表分页
+     *
+     * @author pans
+     * @date  2025/09/04 15:14
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取下发组织日志表分页")
+    //@SaCheckPermission("/disk/CollegeOrg/page")
+    @GetMapping("/disk/CollegeOrg/page")
+    public CommonResult<Page<CollegeOrg>> page(CollegeOrgPageParam collegeOrgPageParam) {
+        return CommonResult.data(collegeOrgService.page(collegeOrgPageParam));
+    }
+
+    /**
+     * 添加下发组织日志表
+     *
+     * @author pans
+     * @date  2025/09/04 15:14
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加下发组织日志表")
+    @CommonLog("添加下发组织日志表")
+    @PostMapping("/disk/CollegeOrg/add")
+    public CommonResult<String> add(@RequestBody @Valid List<CollegeOrgAddParam> collegeOrgAddParamList, HttpServletRequest httpServletRequest) {
+        CommonResult<String> rs=authUtil.valid(httpServletRequest);
+        try {
+            if(rs.getCode().equals(200)){
+                if(CollectionUtil.isEmpty(collegeOrgAddParamList)){
+                    log.info("=================下发组织数据为空===================");
+                    return CommonResult.ok("下发组织数据为空");
+                }
+                collegeOrgAddParamList.stream().forEach(collegeOrgAddParam -> {
+                    collegeOrgService.add(collegeOrgAddParam);
+                });
+                return CommonResult.ok("下发组织数据成功");
+            }
+        } catch (Exception e) {
+            log.info("下发组织数据失败,失败原因:{}", e);
+            return CommonResult.error("下发组织数据失败==============================>"+e.getMessage());
+        }
+        return CommonResult.error(rs.getMsg());
+    }
+
+    /**
+     * 编辑下发组织日志表
+     *
+     * @author pans
+     * @date  2025/09/04 15:14
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑下发组织日志表")
+    @CommonLog("编辑下发组织日志表")
+    //@SaCheckPermission("/disk/CollegeOrg/edit")
+    @PostMapping("/disk/CollegeOrg/edit")
+    public CommonResult<String> edit(@RequestBody @Valid CollegeOrgEditParam collegeOrgEditParam) {
+        collegeOrgService.edit(collegeOrgEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除下发组织日志表
+     *
+     * @author pans
+     * @date  2025/09/04 15:14
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除下发组织日志表")
+    @CommonLog("删除下发组织日志表")
+    //@SaCheckPermission("/disk/CollegeOrg/delete")
+    @PostMapping("/disk/CollegeOrg/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<CollegeOrgIdParam> collegeOrgIdParamList) {
+        collegeOrgService.delete(collegeOrgIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取下发组织日志表详情
+     *
+     * @author pans
+     * @date  2025/09/04 15:14
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取下发组织日志表详情")
+    //@SaCheckPermission("/disk/CollegeOrg/detail")
+    @GetMapping("/disk/CollegeOrg/detail")
+    public CommonResult<CollegeOrg> detail(@Valid CollegeOrgIdParam collegeOrgIdParam) {
+        return CommonResult.data(collegeOrgService.detail(collegeOrgIdParam));
+    }
+
+}

+ 155 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CollegePositionController.java

@@ -0,0 +1,155 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.hutool.core.collection.CollectionUtil;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.common.util.AuthUtil;
+import vip.xiaonuo.disk.domain.CollegePosition;
+import vip.xiaonuo.disk.param.collegeOrg.CollegeOrgAddParam;
+import vip.xiaonuo.disk.param.collegePosition.CollegePositionAddParam;
+import vip.xiaonuo.disk.param.collegePosition.CollegePositionEditParam;
+import vip.xiaonuo.disk.param.collegePosition.CollegePositionIdParam;
+import vip.xiaonuo.disk.param.collegePosition.CollegePositionPageParam;
+import vip.xiaonuo.disk.service.CollegePositionService;
+
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import java.util.List;
+
+/**
+ * 下发岗位日志表控制器
+ *
+ * @author pans
+ * @date  2025/09/04 15:39
+ */
+@Slf4j
+@Api(tags = "下发岗位日志表控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class CollegePositionController {
+
+    @Resource
+    private CollegePositionService collegePositionService;
+    @Resource
+    private AuthUtil authUtil;
+    /**
+     * 获取下发岗位日志表分页
+     *
+     * @author pans
+     * @date  2025/09/04 15:39
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取下发岗位日志表分页")
+    //@SaCheckPermission("/disk/CollegePosition/page")
+    @GetMapping("/disk/CollegePosition/page")
+    public CommonResult<Page<CollegePosition>> page(CollegePositionPageParam collegePositionPageParam) {
+        return CommonResult.data(collegePositionService.page(collegePositionPageParam));
+    }
+
+    /**
+     * 添加下发岗位日志表
+     *
+     * @author pans
+     * @date  2025/09/04 15:39
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加下发岗位日志表")
+    @CommonLog("添加下发岗位日志表")
+    @PostMapping("/disk/CollegePosition/add")
+    public CommonResult<String> add(@RequestBody @Valid List<CollegePositionAddParam> collegePositionAddParamList, HttpServletRequest httpServletRequest) {
+        CommonResult<String> rs=authUtil.valid(httpServletRequest);
+        try {
+            if(rs.getCode().equals(200)){
+                if(CollectionUtil.isEmpty(collegePositionAddParamList)){
+                    log.info("=================下发岗位数据为空===================");
+                    return CommonResult.ok("下发岗位数据为空");
+                }
+                collegePositionAddParamList.stream().forEach(collegePositionAddParam -> {
+                    collegePositionService.add(collegePositionAddParam);
+                });
+                return CommonResult.ok("下发岗位数据成功");
+            }
+        } catch (Exception e) {
+            log.info("下发岗位数据失败,失败原因:{}", e);
+            return CommonResult.error("下发岗位数据失败==============================>"+e.getMessage());
+        }
+        return CommonResult.error(rs.getMsg());
+    }
+
+    /**
+     * 编辑下发岗位日志表
+     *
+     * @author pans
+     * @date  2025/09/04 15:39
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑下发岗位日志表")
+    @CommonLog("编辑下发岗位日志表")
+    //@SaCheckPermission("/disk/CollegePosition/edit")
+    @PostMapping("/disk/CollegePosition/edit")
+    public CommonResult<String> edit(@RequestBody @Valid CollegePositionEditParam collegePositionEditParam) {
+        collegePositionService.edit(collegePositionEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除下发岗位日志表
+     *
+     * @author pans
+     * @date  2025/09/04 15:39
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除下发岗位日志表")
+    @CommonLog("删除下发岗位日志表")
+    //@SaCheckPermission("/disk/CollegePosition/delete")
+    @PostMapping("/disk/CollegePosition/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<CollegePositionIdParam> collegePositionIdParamList) {
+        collegePositionService.delete(collegePositionIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取下发岗位日志表详情
+     *
+     * @author pans
+     * @date  2025/09/04 15:39
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取下发岗位日志表详情")
+    //@SaCheckPermission("/disk/CollegePosition/detail")
+    @GetMapping("/disk/CollegePosition/detail")
+    public CommonResult<CollegePosition> detail(@Valid CollegePositionIdParam collegePositionIdParam) {
+        return CommonResult.data(collegePositionService.detail(collegePositionIdParam));
+    }
+
+}

+ 212 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CollegeUserController.java

@@ -0,0 +1,212 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.servlet.ModelAndView;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.common.util.AuthUtil;
+import vip.xiaonuo.disk.domain.CollegeUser;
+import vip.xiaonuo.disk.param.collegeUser.CollegeUserAddParam;
+import vip.xiaonuo.disk.param.collegeUser.CollegeUserEditParam;
+import vip.xiaonuo.disk.param.collegeUser.CollegeUserIdParam;
+import vip.xiaonuo.disk.param.collegeUser.CollegeUserPageParam;
+import vip.xiaonuo.disk.service.CollegeUserService;
+import vip.xiaonuo.disk.util.HttpRequest;
+
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 下发用户日志表控制器
+ *
+ * @author pans
+ * @date  2025/09/04 14:40
+ */
+@Slf4j
+@Api(tags = "下发用户日志表控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class CollegeUserController {
+
+
+
+    @Resource
+    private CollegeUserService collegeUserService;
+    @Resource
+    private AuthUtil authUtil;
+    /**
+     * 获取下发用户日志表分页
+     *
+     * @author pans
+     * @date  2025/09/04 14:40
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取下发用户日志表分页")
+    //@SaCheckPermission("/disk/CollegeUser/page")
+    @GetMapping("/disk/CollegeUser/page")
+    public CommonResult<Page<CollegeUser>> page(CollegeUserPageParam collegeUserPageParam) {
+        return CommonResult.data(collegeUserService.page(collegeUserPageParam));
+    }
+
+    /**
+     * 添加下发用户日志表
+     *
+     * @author pans
+     * @date  2025/09/04 14:40
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加下发用户日志表")
+    @CommonLog("添加下发用户日志表")
+    @PostMapping("/disk/CollegeUser/add")
+    public CommonResult<String> add(@RequestBody @Valid List<CollegeUserAddParam> collegeUserAddParamList, HttpServletRequest httpServletRequest) {
+        CommonResult<String> rs=authUtil.valid(httpServletRequest);
+        try {
+            if(rs.getCode().equals(200)){
+                if(CollectionUtil.isEmpty(collegeUserAddParamList)){
+                    log.info("=================下发用户数据为空===================");
+                    return CommonResult.ok("下发用户数据为空");
+                }
+                collegeUserAddParamList.stream().forEach(collegeUserAddParam -> {
+                    collegeUserService.add(collegeUserAddParam);
+                });
+                return CommonResult.ok("下发用户数据成功");
+            }
+        } catch (Exception e) {
+            log.info("下发用户数据失败,失败原因:{}", e);
+            return CommonResult.error("下发用户数据失败==============================>"+e.getMessage());
+        }
+        return CommonResult.error(rs.getMsg());
+    }
+
+    /**
+     * 编辑下发用户日志表
+     *
+     * @author pans
+     * @date  2025/09/04 14:40
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑下发用户日志表")
+    @CommonLog("编辑下发用户日志表")
+    //@SaCheckPermission("/disk/CollegeUser/edit")
+    @PostMapping("/disk/CollegeUser/edit")
+    public CommonResult<String> edit(@RequestBody @Valid CollegeUserEditParam collegeUserEditParam) {
+        collegeUserService.edit(collegeUserEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除下发用户日志表
+     *
+     * @author pans
+     * @date  2025/09/04 14:40
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除下发用户日志表")
+    @CommonLog("删除下发用户日志表")
+    //@SaCheckPermission("/disk/CollegeUser/delete")
+    @PostMapping("/disk/CollegeUser/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<CollegeUserIdParam> collegeUserIdParamList) {
+        collegeUserService.delete(collegeUserIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取下发用户日志表详情
+     *
+     * @author pans
+     * @date  2025/09/04 14:40
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取下发用户日志表详情")
+    //@SaCheckPermission("/disk/CollegeUser/detail")
+    @GetMapping("/disk/CollegeUser/detail")
+    public CommonResult<CollegeUser> detail(@Valid CollegeUserIdParam collegeUserIdParam) {
+        return CommonResult.data(collegeUserService.detail(collegeUserIdParam));
+    }
+
+
+    /**
+     * 根据code获取用户信息
+     * @param httpServletRequest
+     * @return
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("根据code获取用户信息")
+    @GetMapping("/disk/CollegeUser/getUser")
+    public CommonResult<String> getUser(HttpServletRequest httpServletRequest){
+        log.info("==================根据code获取用户信息接口=====================");
+        CommonResult<String> rs=authUtil.valid(httpServletRequest);
+        //根据用户信息生成自己系统的token
+        try {
+            if(rs.getCode().equals(200)){
+                CommonResult<String> result=collegeUserService.getUser(httpServletRequest);
+                return CommonResult.data(result.getMsg());
+            }
+        } catch (Exception e) {
+            log.info("根据code获取用户信息失败,失败原因:{}", e);
+            return CommonResult.error("根据code获取用户信息失败==============================>"+e.getMessage());
+        }
+        return CommonResult.error(rs.getMsg());
+    }
+
+    /**
+     * 根据用户id获取token
+     * @param httpServletRequest
+     * @return
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("根据用户id获取token")
+    @GetMapping("/disk/CollegeUser/getToken")
+    public CommonResult<String> getToken(HttpServletRequest httpServletRequest){
+        try {
+            String userId=httpServletRequest.getParameter("userId");
+            if(ObjectUtil.isEmpty(userId)){
+                return CommonResult.error("用户id为空");
+            }
+            String  token = collegeUserService.getAuthToken(userId);
+            return CommonResult.data(token);
+        } catch (Exception e) {
+            return CommonResult.error("根据用户id获取token为空======================》"+e.getMessage());
+        }
+    }
+
+
+}

+ 111 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CommonFileController.java

@@ -0,0 +1,111 @@
+package vip.xiaonuo.disk.controller;
+
+import cn.hutool.core.util.IdUtil;
+import com.alibaba.fastjson2.JSON;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.qiwenshare.common.anno.MyLog;
+import com.qiwenshare.common.result.RestResult;
+import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
+import vip.xiaonuo.disk.service.ICommonFileService;
+import vip.xiaonuo.disk.service.IFilePermissionService;
+import vip.xiaonuo.disk.service.IUserFileService;
+import vip.xiaonuo.disk.domain.CommonFile;
+import vip.xiaonuo.disk.domain.FilePermission;
+import vip.xiaonuo.disk.domain.UserFile;
+import vip.xiaonuo.disk.dto.commonfile.CommonFileDTO;
+import vip.xiaonuo.disk.io.QiwenFile;
+import vip.xiaonuo.disk.vo.commonfile.CommonFileListVo;
+import vip.xiaonuo.disk.vo.commonfile.CommonFileUser;
+import vip.xiaonuo.disk.vo.file.FileListVO;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.List;
+
+@Tag(name = "common", description = "该接口为文件共享接口")
+@RestController
+@Slf4j
+@RequestMapping("/common")
+public class CommonFileController {
+
+    public static final String CURRENT_MODULE = "文件共享";
+
+    @Resource
+    ICommonFileService commonFileService;
+    @Resource
+    IFilePermissionService filePermissionService;
+    @Resource
+    IUserFileService userFileService;
+
+    @Operation(summary = "将文件共享给他人", description = "共享文件统一接口", tags = {"common"})
+    @PostMapping(value = "/commonfile")
+    @MyLog(operation = "共享文件", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult<String> commonFile( @RequestBody CommonFileDTO commonFileDTO) {
+        CommonFile commonFile = new CommonFile();
+        commonFile.setUserFileId(commonFileDTO.getUserFileId());
+        commonFile.setCommonFileId(IdUtil.getSnowflakeNextIdStr());
+
+        commonFileService.save(commonFile);
+
+        List<FilePermission> list = JSON.parseArray(commonFileDTO.getCommonUserList(), FilePermission.class);
+
+        List<FilePermission> filePermissionList = new ArrayList<>();
+       for (FilePermission filePermission : list) {
+           filePermission.setFilePermissionId(IdUtil.getSnowflakeNextId());
+            filePermission.setCommonFileId(commonFile.commonFileId);
+            filePermissionList.add(filePermission);
+       }
+        filePermissionService.saveBatch(filePermissionList);
+
+        return RestResult.success();
+    }
+
+    @Operation(summary = "获取共享空间的全量用户列表", description = "共享文件用户接口", tags = {"common"})
+    @GetMapping(value = "/commonfileuser")
+    @MyLog(operation = "共享文件用户", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult<List<CommonFileUser>> commonFileUserList() {
+
+//        JwtUser sessionUserBean =  SessionUtil.getSession();
+        String userId = StpLoginUserUtil.getLoginUser().getId();
+        List<CommonFileUser> list = commonFileService.selectCommonFileUser(userId);
+        return RestResult.success().data(list);
+    }
+
+    @Operation(summary = "获取共享用户文件列表", description = "用来做前台列表展示", tags = {"file"})
+    @RequestMapping(value = "/getCommonFileByUser", method = RequestMethod.GET)
+    @ResponseBody
+    public RestResult<CommonFileListVo> getCommonFileByUser(
+            @Parameter(description = "用户id", required = true) String userId){
+//        JwtUser sessionUserBean =  SessionUtil.getSession();
+        List<CommonFileListVo> commonFileVo = commonFileService.selectCommonFileByUser(userId, StpLoginUserUtil.getLoginUser().getId());
+
+        return RestResult.success().data(commonFileVo);
+
+    }
+
+    @Operation(summary = "获取共享空间中某个用户的文件列表", description = "用来做前台列表展示", tags = {"file"})
+    @RequestMapping(value = "/commonFileList", method = RequestMethod.GET)
+    @ResponseBody
+    public RestResult<FileListVO> commonFileList(
+            @Parameter(description = "用户id", required = true) Long commonFileId,
+            @Parameter(description = "文件路径", required = true) String filePath,
+            @Parameter(description = "当前页", required = true) long currentPage,
+            @Parameter(description = "页面数量", required = true) long pageCount){
+
+        CommonFile commonFile = commonFileService.getById(commonFileId);
+        UserFile userFile = userFileService.getById(commonFile.getUserFileId());
+        QiwenFile qiwenFile = new QiwenFile(userFile.getFilePath(), filePath, true);
+        IPage<FileListVO> fileList = userFileService.userFileList(userFile.getUserId(), qiwenFile.getPath(), currentPage, pageCount);
+
+        return RestResult.success().data(fileList);
+
+    }
+
+}

+ 476 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CourseChapterController.java

@@ -0,0 +1,476 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollStreamUtil;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.disk.domain.Chapter;
+import vip.xiaonuo.disk.domain.CourseRelate;
+import vip.xiaonuo.disk.param.ChapterAddParam;
+import vip.xiaonuo.disk.param.ChapterEditParam;
+import vip.xiaonuo.disk.param.ChapterIdParam;
+import vip.xiaonuo.disk.param.ChapterPageParam;
+import vip.xiaonuo.disk.service.ChapterService;
+import vip.xiaonuo.disk.service.ClassHourService;
+import vip.xiaonuo.disk.service.CourseRelateService;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 课程章节表控制器
+ *
+ * @author pans
+ * @date  2025/07/02 15:34
+ */
+@Api(tags = "课程章节表控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class CourseChapterController {
+
+    @Resource
+    private ChapterService chapterService;
+    @Resource
+    private ClassHourService classHourService;
+    @Resource
+    private CourseRelateService courseRelateService;
+
+
+
+    /**
+     * 课程章节表-分页列表
+     *
+     * @author pans
+     * @date  2025/07/02 15:34
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("课程章节表-分页列表")
+    //@SaCheckPermission("/disk/chapter/page")
+    @GetMapping("/disk/chapter/page")
+    public CommonResult<Page<Map<String,Object>>> page(ChapterPageParam chapterPageParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        if(StringUtils.isEmpty(req.getParameter("courseId")))return CommonResult.error("课程id不能为空");
+        param.put("courseId", req.getParameter("courseId"));
+        param.put("name", req.getParameter("name"));
+        //查回章节信息
+        Page<Map<String,Object>> chapterPage=chapterService.queryList(param);
+        List<Map<String,Object>> chapterList=chapterPage.getRecords();
+        //挂上所有章节的课时信息
+        //先取出所有章节id
+        List<String> chapterIdList=  chapterList.stream().map(map -> (String) map.get("id")).collect(Collectors.toList());
+        param.clear();
+        param.put("chapterIdList", chapterIdList);
+        List<Map<String,Object>> classHourList =classHourService.queryAllList(param);
+        // 1. 准备结果:为每个章节添加classHours字段
+        for (Map<String, Object> chapter : chapterList) {
+            if (chapter == null || chapter.get("id") == null) continue;
+
+            String chapterId = chapter.get("id").toString();
+            List<Map<String, Object>> chapterClassHours = new ArrayList<>();
+
+            // 2. 倒序遍历课程列表(关键点!)
+            for (int i = classHourList.size() - 1; i >= 0; i--) {
+                Map<String, Object> classHour = classHourList.get(i);
+                if (classHour == null || classHour.get("chapterId") == null) continue;
+
+                // 3. 匹配当前章节
+                if (chapterId.equals(classHour.get("chapterId").toString())) {
+                    chapterClassHours.add(classHour);  // 添加到章节
+                    classHourList.remove(i);          // 从原列表删除(安全操作)
+                }
+            }
+
+
+            chapter.put("classHours", chapterClassHours);
+        }
+//        //按chapterId分组课程
+//        Map<String, List<Map<String, Object>>> classHourByChapter = classHourList.stream()
+//                .filter(Objects::nonNull)
+//                .filter(classHour -> classHour.get("chapterId") != null)
+//                .collect(Collectors.groupingBy(
+//                        classHour -> classHour.get("chapterId").toString()
+//                ));
+//
+//        //为每个章节添加对应的课程列表
+//        chapterList.stream()
+//                .filter(Objects::nonNull)
+//                .filter(chapter -> chapter.get("id") != null)
+//                .forEach(chapter -> {
+//                    String chapterId = chapter.get("id").toString();
+//                    chapter.put("classHours",
+//                            classHourByChapter.getOrDefault(chapterId, Collections.emptyList()));
+//                });
+
+        return CommonResult.data(chapterPage);
+    }
+
+    /**
+     * 课程章节表-全量列表
+     *
+     * @author pans
+     * @date  2025/07/02 15:34
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("课程章节表-全量列表")
+    //@SaCheckPermission("/disk/chapter/allList")
+    @GetMapping("/disk/chapter/allList")
+    public CommonResult<List<Map<String,Object>>> allList(ChapterPageParam chapterPageParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        if(StringUtils.isEmpty(req.getParameter("courseId")))return CommonResult.error("课程id不能为空");
+        param.put("courseId", req.getParameter("courseId"));
+        param.put("name", req.getParameter("name"));
+        List<Map<String,Object>> chapterList=chapterService.queryAllList(param);
+        //挂上所有章节的课时信息
+        //先取出所有章节id
+        List<String> chapterIdList=  chapterList.stream().map(map -> (String) map.get("id")).collect(Collectors.toList());
+        param.clear();
+        param.put("chapterIdList", chapterIdList);
+        List<Map<String,Object>> classHourList =classHourService.queryAllList(param);
+        // 1. 准备结果:为每个章节添加classHours字段
+        for (Map<String, Object> chapter : chapterList) {
+            if (chapter == null || chapter.get("id") == null) continue;
+
+            String chapterId = chapter.get("id").toString();
+            List<Map<String, Object>> chapterClassHours = new ArrayList<>();
+
+            // 2. 倒序遍历课程列表(关键点!)
+            for (int i = classHourList.size() - 1; i >= 0; i--) {
+                Map<String, Object> classHour = classHourList.get(i);
+                if (classHour == null || classHour.get("chapterId") == null) continue;
+
+                // 3. 匹配当前章节
+                if (chapterId.equals(classHour.get("chapterId").toString())) {
+                    chapterClassHours.add(classHour);  // 添加到章节
+                    classHourList.remove(i);          // 从原列表删除(安全操作)
+                }
+            }
+            chapter.put("classHours", chapterClassHours);
+        }
+//        //按chapterId分组课程
+//        Map<String, List<Map<String, Object>>> classHourByChapter = classHourList.stream()
+//                .filter(Objects::nonNull)
+//                .filter(classHour -> classHour.get("chapterId") != null)
+//                .collect(Collectors.groupingBy(
+//                        classHour -> classHour.get("chapterId").toString()
+//                ));
+//
+//        //为每个章节添加对应的课程列表
+//        chapterList.stream()
+//                .filter(Objects::nonNull)
+//                .filter(chapter -> chapter.get("id") != null)
+//                .forEach(chapter -> {
+//                    String chapterId = chapter.get("id").toString();
+//                    chapter.put("classHours",
+//                            classHourByChapter.getOrDefault(chapterId, Collections.emptyList()));
+//                });
+        return CommonResult.data(chapterList);
+    }
+
+    /**
+     * 课程章节表-下拉列表
+     *
+     * @author pans
+     * @date  2025/07/02 15:34
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("课程章节表-下拉列表")
+    @GetMapping("/disk/chapter/downList")
+    public CommonResult<List<Chapter>> downList(ChapterPageParam chapterPageParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("courseId", req.getParameter("courseId"));
+        param.put("name", req.getParameter("name"));
+        List<Chapter> chapterList=chapterService.wrapperList(param);
+        return CommonResult.data(chapterList);
+    }
+
+    /**
+     * 添加课程章节表
+     *
+     * @author pans
+     * @date  2025/07/02 15:34
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加课程章节表")
+    @CommonLog("添加课程章节表")
+    //@SaCheckPermission("/disk/chapter/add")
+    @PostMapping("/disk/chapter/add")
+    public CommonResult<String> add(@RequestBody @Valid ChapterAddParam chapterAddParam) {
+        Chapter chapter = BeanUtil.toBean(chapterAddParam, Chapter.class);
+        chapterService.addOne(chapter);
+        //添加文件,测验等关联信息
+        List<CourseRelate> courseRelates = chapterAddParam.getCourseRelates();
+        if(courseRelates!=null && courseRelates.size()>0)
+        {
+            courseRelates = courseRelates.stream()
+                    .peek(oneCourseRelate -> oneCourseRelate.setChapterhourType("0")) // 修改属性
+                    .peek(oneCourseRelate -> oneCourseRelate.setMainId(chapter.getId())) // 修改属性
+                    .peek(oneCourseRelate -> oneCourseRelate.setInfoType("0")) // 修改属性
+                    .collect(Collectors.toList());
+            courseRelateService.addBatch(courseRelates);
+        }
+
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑课程章节表
+     *
+     * @author pans
+     * @date  2025/07/02 15:34
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑课程章节表")
+    @CommonLog("编辑课程章节表")
+    //@SaCheckPermission("/disk/chapter/edit")
+    @PostMapping("/disk/chapter/edit")
+    public CommonResult<String> edit(@RequestBody @Valid ChapterEditParam chapterEditParam) {
+        Chapter chapter = BeanUtil.toBean(chapterEditParam, Chapter.class);
+        chapterService.editOne(chapter);
+        //添加文件,测验等关联信息
+        List<CourseRelate> courseRelates = chapterEditParam.getCourseRelates();
+        if(courseRelates!=null && courseRelates.size()>0)
+        {
+            //先删除过往关联内容
+            Map relateParam =new HashMap();
+            relateParam.put("mainId", chapter.getId());
+            relateParam.put("chapterhourType","0");
+            relateParam.put("infoType","0");
+            List<CourseRelate> delCourseRelateList=courseRelateService.wrapperList(relateParam);
+            List<String> delCourseRelateIdList=CollStreamUtil.toList(delCourseRelateList, CourseRelate::getId);
+            courseRelateService.deleteByIdsReal(delCourseRelateIdList);
+            courseRelates = courseRelates.stream()
+                    .peek(oneCourseRelate -> oneCourseRelate.setChapterhourType("0")) // 修改属性
+                    .peek(oneCourseRelate -> oneCourseRelate.setMainId(chapter.getId())) // 修改属性
+                    .peek(oneCourseRelate -> oneCourseRelate.setInfoType("0")) // 修改属性
+                    .collect(Collectors.toList());
+            courseRelateService.addBatch(courseRelates);
+        }
+
+
+
+
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除课程章节表
+     *
+     * @author pans
+     * @date  2025/07/02 15:34
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除课程章节表")
+    @CommonLog("删除课程章节表")
+    //@SaCheckPermission("/disk/chapter/delete")
+    @PostMapping("/disk/chapter/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<ChapterIdParam> chapterIdParamList) {
+        chapterService.delete(chapterIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取课程章节表详情
+     *
+     * @author pans
+     * @date  2025/07/02 15:34
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取课程章节表详情")
+    //@SaCheckPermission("/disk/chapter/detail")
+    @GetMapping("/disk/chapter/detail")
+    public CommonResult<Map<String,Object>> detail(@Valid ChapterIdParam chapterIdParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("id", req.getParameter("id"));
+        Map<String,Object> result=chapterService.queryInfo(param);
+        //查回课程关联信息
+        List<Map<String,Object>> courseRelatesList=new ArrayList<>();
+        Map funcTypeParam =new HashMap();
+        funcTypeParam.put("mainId", chapterIdParam.getId());
+        funcTypeParam.put("chapterhourType","0");
+        List<CourseRelate> relateList=courseRelateService.wrapperList(funcTypeParam);
+        List<String>funcTypeList=CollStreamUtil.toList(relateList, CourseRelate::getFuncType);
+        //去重类型
+        funcTypeList=funcTypeList.stream().distinct().collect(Collectors.toList());
+        Map relateParam =new HashMap();
+        relateParam.put("mainId", req.getParameter("id"));
+        relateParam.put("chapterhourType", "0");
+        //分别查回视频,讲义,字幕,作业,测验等
+        for(String oneFunctype:funcTypeList)
+        {
+            relateParam.put("funcType", oneFunctype);
+            List<Map<String,Object>> videoList=courseRelateService.queryMapList(relateParam);
+            courseRelatesList.addAll(videoList);
+        }
+        result.put("courseRelates", courseRelatesList);
+        return CommonResult.data(result);
+    }
+
+    /**
+     * 课程中心-课程章节表详情
+     *
+     * @author pans
+     * @date  2025/07/02 15:34
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("课程中心-课程章节表详情")
+    //@SaCheckPermission("/disk/coursecentry/chapterDetail")
+    @GetMapping("/disk/coursecentry/chapterDetail")
+    public CommonResult<Map<String,Object>> chapterDetail(@Valid ChapterIdParam chapterIdParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("id", req.getParameter("id"));
+        Map<String,Object> result=chapterService.queryInfo(param);
+        //查回课程关联信息
+        List<Map<String,Object>> courseRelatesList=new ArrayList<>();
+        Map funcTypeParam =new HashMap();
+        funcTypeParam.put("mainId", chapterIdParam.getId());
+        funcTypeParam.put("chapterhourType","0");
+        List<CourseRelate> relateList=courseRelateService.wrapperList(funcTypeParam);
+        List<String>funcTypeList=CollStreamUtil.toList(relateList, CourseRelate::getFuncType);
+        Map relateParam =new HashMap();
+        relateParam.put("mainId", req.getParameter("id"));
+        relateParam.put("chapterhourType", "0");
+        //分别查回视频,讲义,字幕,作业,测验等
+        for(String oneFunctype:funcTypeList)
+        {
+            relateParam.put("funcType", oneFunctype);
+            List<Map<String,Object>> videoList=courseRelateService.queryMapList(relateParam);
+            courseRelatesList.addAll(videoList);
+        }
+        result.put("courseRelates", courseRelatesList);
+        return CommonResult.data(result);
+    }
+
+    /**
+     * 课程中心-获取课程章节关联资源
+     *
+     * @author pans
+     * @date  2025/07/02 15:34
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("课程中心-获取课程章节关联资源")
+    //@SaCheckPermission("/disk/coursecentry/chapterRelate")
+    @GetMapping("/disk/coursecentry/chapterRelate")
+    public CommonResult<Map<String,Object>> chapterRelate(@Valid ChapterIdParam chapterIdParam, HttpServletRequest req) {
+        Map result=new HashMap();
+        //查回课程关联信息
+        List<Map<String,Object>> courseRelatesList=new ArrayList<>();
+        Map funcTypeParam =new HashMap();
+        funcTypeParam.put("mainId", chapterIdParam.getId());
+        funcTypeParam.put("chapterhourType","1");
+        funcTypeParam.put("funcType",req.getParameter("funcType"));
+        List<CourseRelate> relateList=courseRelateService.wrapperList(funcTypeParam);
+        List<String>funcTypeList=CollStreamUtil.toList(relateList, CourseRelate::getFuncType);
+        Map relateParam =new HashMap();
+        relateParam.put("mainId", req.getParameter("id"));
+        relateParam.put("chapterhourType", "1");
+        //分别查回视频,讲义,字幕,作业,测验等
+        for(String oneFunctype:funcTypeList)
+        {
+            relateParam.put("funcType", oneFunctype);
+            List<Map<String,Object>> videoList=courseRelateService.queryMapList(relateParam);
+            courseRelatesList.addAll(videoList);
+        }
+        result.put("courseRelates", courseRelatesList);
+        return CommonResult.data(result);
+    }
+
+    /**
+     * 课程中心-章节全量列表(学生端)
+     *
+     * @author pans
+     * @date  2025/07/02 15:34
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("课程中心-章节全量列表(学生端)")
+    //@SaCheckPermission("/disk/coursecentry/coursechapterList")
+    @GetMapping("/disk/coursecentry/coursechapterList")
+    public CommonResult<List<Map<String,Object>>> coursechapterList(ChapterPageParam chapterPageParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        if(StringUtils.isEmpty(req.getParameter("courseId")))return CommonResult.error("课程id不能为空");
+        param.put("courseId", req.getParameter("courseId"));
+        param.put("name", req.getParameter("name"));
+        List<Map<String,Object>> chapterList=chapterService.queryAllList(param);
+        //挂上所有章节的课时信息
+        //先取出所有章节id
+        List<String> chapterIdList=  chapterList.stream().map(map -> (String) map.get("id")).collect(Collectors.toList());
+        param.clear();
+        param.put("chapterIdList", chapterIdList);
+        //只查出资源审核通过的课时
+        param.put("verifyPass", "1");
+        List<Map<String,Object>> classHourList =classHourService.queryAllList(param);
+        // 1. 准备结果:为每个章节添加classHours字段
+        for (Map<String, Object> chapter : chapterList) {
+            if (chapter == null || chapter.get("id") == null) continue;
+
+            String chapterId = chapter.get("id").toString();
+            List<Map<String, Object>> chapterClassHours = new ArrayList<>();
+
+            // 2. 倒序遍历课程列表(关键点!)
+            for (int i = classHourList.size() - 1; i >= 0; i--) {
+                Map<String, Object> classHour = classHourList.get(i);
+                if (classHour == null || classHour.get("chapterId") == null) continue;
+
+                // 3. 匹配当前章节
+                if (chapterId.equals(classHour.get("chapterId").toString())) {
+                    chapterClassHours.add(classHour);  // 添加到章节
+                    classHourList.remove(i);          // 从原列表删除(安全操作)
+                }
+            }
+
+            chapter.put("classHours", chapterClassHours);
+        }
+//        //按chapterId分组课程
+//        Map<String, List<Map<String, Object>>> classHourByChapter = classHourList.stream()
+//                .filter(Objects::nonNull)
+//                .filter(classHour -> classHour.get("chapterId") != null)
+//                .collect(Collectors.groupingBy(
+//                        classHour -> classHour.get("chapterId").toString()
+//                ));
+//
+//        //为每个章节添加对应的课程列表
+//        chapterList.stream()
+//                .filter(Objects::nonNull)
+//                .filter(chapter -> chapter.get("id") != null)
+//                .forEach(chapter -> {
+//                    String chapterId = chapter.get("id").toString();
+//                    chapter.put("classHours",
+//                            classHourByChapter.getOrDefault(chapterId, Collections.emptyList()));
+//                });
+        return CommonResult.data(chapterList);
+    }
+
+}

+ 156 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CourseChapterKnowledgeController.java

@@ -0,0 +1,156 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.disk.domain.CourseChapterKnowledge;
+import vip.xiaonuo.disk.param.konwledge.CourseChapterKnowledgeAddParam;
+import vip.xiaonuo.disk.param.konwledge.CourseChapterKnowledgeEditParam;
+import vip.xiaonuo.disk.param.konwledge.CourseChapterKnowledgeIdParam;
+import vip.xiaonuo.disk.param.konwledge.CourseChapterKnowledgePageParam;
+import vip.xiaonuo.disk.service.CourseChapterKnowledgeService;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * COURSE_CHAPTER_KNOWLEDGE控制器
+ *
+ * @author pans
+ * @date  2025/08/04 17:14
+ */
+@Api(tags = "作业相关")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class CourseChapterKnowledgeController {
+
+    @Resource
+    private CourseChapterKnowledgeService courseChapterKnowledgeService;
+
+    /**
+     * 获取COURSE_CHAPTER_KNOWLEDGE分页
+     *
+     * @author pans
+     * @date  2025/08/04 17:14
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("知识点列表")
+    @GetMapping("/disk/chapterknowledge/page")
+    public CommonResult<Page<CourseChapterKnowledge>> page(CourseChapterKnowledgePageParam courseChapterKnowledgePageParam) {
+        return CommonResult.data(courseChapterKnowledgeService.page(courseChapterKnowledgePageParam));
+    }
+
+    /**
+     * 添加COURSE_CHAPTER_KNOWLEDGE
+     *
+     * @author pans
+     * @date  2025/08/04 17:14
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加知识点")
+    @CommonLog("添加COURSE_CHAPTER_KNOWLEDGE")
+    @PostMapping("/disk/chapterknowledge/add")
+    public CommonResult<String> add(@RequestBody @Valid CourseChapterKnowledgeAddParam courseChapterKnowledgeAddParam) {
+        courseChapterKnowledgeService.add(courseChapterKnowledgeAddParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑COURSE_CHAPTER_KNOWLEDGE
+     *
+     * @author pans
+     * @date  2025/08/04 17:14
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑知识点")
+    @CommonLog("编辑COURSE_CHAPTER_KNOWLEDGE")
+    @PostMapping("/disk/chapterknowledge/edit")
+    public CommonResult<String> edit(@RequestBody @Valid CourseChapterKnowledgeEditParam courseChapterKnowledgeEditParam) {
+        courseChapterKnowledgeService.edit(courseChapterKnowledgeEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除COURSE_CHAPTER_KNOWLEDGE
+     *
+     * @author pans
+     * @date  2025/08/04 17:14
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除知识点")
+    @CommonLog("删除COURSE_CHAPTER_KNOWLEDGE")
+    @PostMapping("/disk/chapterknowledge/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<CourseChapterKnowledgeIdParam> courseChapterKnowledgeIdParamList) {
+        courseChapterKnowledgeService.delete(courseChapterKnowledgeIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取COURSE_CHAPTER_KNOWLEDGE详情
+     *
+     * @author pans
+     * @date  2025/08/04 17:14
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("知识点详情")
+    @GetMapping("/disk/chapterknowledge/detail")
+    public CommonResult<CourseChapterKnowledge> detail(@Valid CourseChapterKnowledgeIdParam courseChapterKnowledgeIdParam) {
+        return CommonResult.data(courseChapterKnowledgeService.detail(courseChapterKnowledgeIdParam));
+    }
+
+
+
+    /**
+     * 根据课程id查询每个知识点的学习人数
+     *
+     * @author pans
+     * @date  2025/08/04 17:14
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("作业个人完成比例")
+    @GetMapping("/disk/chapterknowledge/selectKnowledgeByCourseId")
+    public CommonResult<List<Map<String,Object>>> selectKnowledgeByCourseId(@RequestParam(name = "courseId", required = true) String courseId) {
+        return CommonResult.data(courseChapterKnowledgeService.selectKnowledgeByCourseId(courseId));
+    }
+
+
+
+    /**
+     * 根据课程id和知识点查询每个知识点的学习人数列表
+     *
+     * @author pans
+     * @date  2025/08/04 17:14
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("班级人均完成比例")
+    @GetMapping("/disk/chapterknowledge/selectKnowledgeListByCourseId")
+    public CommonResult<List<Map<String,Object>>> selectKnowledgeListByCourseId(@RequestParam(name = "courseId", required = true) String courseId,@RequestParam(name = "knowledgeId", required = true) String knowledgeId) {
+        return CommonResult.data(courseChapterKnowledgeService.selectKnowledgeListByCourseId(courseId,knowledgeId));
+    }
+
+
+}

+ 336 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CourseClassHourController.java

@@ -0,0 +1,336 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollStreamUtil;
+import cn.hutool.core.util.IdUtil;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.collections4.MapUtils;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.disk.domain.*;
+import vip.xiaonuo.disk.param.*;
+import vip.xiaonuo.disk.service.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 课时表控制器
+ *
+ * @author pans
+ * @date  2025/07/02 10:36
+ */
+@Api(tags = "课时表控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class CourseClassHourController {
+
+    @Resource
+    private ClassHourService classHourService;
+    @Resource
+    private CourseRelateService courseRelateService;
+    @Resource
+    private ChapterService chapterService;
+    @Resource
+    private CourseInfoService courseInfoService;
+    @Resource
+    private CourseChapterKnowledgeService courseChapterKnowledgeService;
+    /**
+     * 获取课时表分页
+     *
+     * @author pans
+     * @date  2025/07/02 10:36
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取课时表分页")
+    @GetMapping("/disk/hour/page")
+    public CommonResult<Page<Map<String,Object>>> page(ClassHourPageParam classHourPageParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("chapterId", req.getParameter("chapterId"));
+        Page<Map<String,Object>> page=classHourService.queryList(param);
+        return CommonResult.data(page);
+    }
+
+    /**
+     * 课时-下拉列表
+     *
+     * @author pans
+     * @date  2025/07/02 15:34
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("课时-下拉列表")
+    @GetMapping("/disk/hour/downList")
+    public CommonResult<List<ClassHour>> downList(ChapterPageParam chapterPageParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("chapterId", req.getParameter("chapterId"));
+        param.put("name", req.getParameter("name"));
+        List<ClassHour> hourList=classHourService.wrapperList(param);
+        return CommonResult.data(hourList);
+    }
+
+    /**
+     * 添加课时表
+     *
+     * @author pans
+     * @date  2025/07/02 10:36
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加课时表")
+    @CommonLog("添加课时表")
+    @PostMapping("/disk/hour/add")
+    public CommonResult<String> add(@RequestBody @Valid ClassHourAddParam classHourAddParam) {
+        ClassHour classHour = BeanUtil.toBean(classHourAddParam, ClassHour.class);
+        classHourService.addOne(classHour);
+        //添加文件,测验等关联信息
+        List<CourseRelate> courseRelates = classHourAddParam.getCourseRelates();
+        if(courseRelates!=null && courseRelates.size()>0)
+        {
+            courseRelates = courseRelates.stream()
+                    .peek(oneCourseRelate -> oneCourseRelate.setChapterhourType("1")) // 修改属性
+                    .peek(oneCourseRelate -> oneCourseRelate.setMainId(classHour.getId())) // 修改属性
+                    .peek(oneCourseRelate -> oneCourseRelate.setInfoType("0")) // 修改属性
+                    .collect(Collectors.toList());
+            courseRelateService.addBatch(courseRelates);
+        }
+        //更新课程的课时数量字段
+        Chapter chapter = chapterService.queryEntity(classHour.getChapterId());
+        CourseInfo courseInfo = courseInfoService.queryEntity(chapter.getCourseId());
+        Map param =new HashMap();
+        param.put("courseId", chapter.getCourseId());
+        List<Chapter> chapterList=chapterService.wrapperList(param);
+        List<String> chapterIdList= CollStreamUtil.toList(chapterList, Chapter::getId);
+        param.clear();
+        param.put("chapterIdList", chapterIdList);
+        List<ClassHour> classHourList =classHourService.wrapperList(param);
+        courseInfo.setHourCount(String.valueOf(classHourList.size()));
+        courseInfoService.editOne(courseInfo);
+
+
+        //添加章节,设计知识点关联表
+        List<CourseChapterKnowledge> list=new ArrayList<>();
+        classHourAddParam.getKnowledgeIds().stream().forEach(knowledgeId -> {
+
+            CourseChapterKnowledge courseChapterKnowledge = new CourseChapterKnowledge();
+            courseChapterKnowledge.setCourseId(chapter.getCourseId());
+            courseChapterKnowledge.setChapterId(chapter.getId());
+            courseChapterKnowledge.setHourId(classHour.getId());
+            courseChapterKnowledge.setKnowledgeId(knowledgeId);
+            courseChapterKnowledge.setId(IdUtil.getSnowflake().nextIdStr());
+            list.add(courseChapterKnowledge);
+        });
+        courseChapterKnowledgeService.saveBatch(list);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑课时表
+     *
+     * @author pans
+     * @date  2025/07/02 10:36
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑课时表")
+    @CommonLog("编辑课时表")
+    @PostMapping("/disk/hour/edit")
+    public CommonResult<String> edit(@RequestBody @Valid ClassHourEditParam classHourEditParam) {
+        ClassHour classHour = classHourService.queryEntity(classHourEditParam.getId());
+        Chapter chapter = chapterService.queryEntity(classHour.getChapterId());
+        CourseInfo courseInfo = courseInfoService.queryEntity(chapter.getCourseId());
+        classHourEditParam.setChapterId(chapter.getId());
+        BeanUtil.copyProperties(classHourEditParam, classHour);
+        classHourService.editOne(classHour);
+
+        //重新添加关联内容
+        List<CourseRelate> courseRelates = classHourEditParam.getCourseRelates();
+        if(courseRelates!=null && courseRelates.size()>0)
+        {
+            //先删除过往关联内容
+            Map relateParam =new HashMap();
+            relateParam.put("mainId", classHour.getId());
+            relateParam.put("chapterhourType","1");
+            relateParam.put("infoType","0");
+            List<CourseRelate> delCourseRelateList=courseRelateService.wrapperList(relateParam);
+            List<String> delCourseRelateIdList=CollStreamUtil.toList(delCourseRelateList, CourseRelate::getId);
+            courseRelateService.deleteByIdsReal(delCourseRelateIdList);
+            courseRelates = courseRelates.stream()
+                    .peek(oneCourseRelate -> oneCourseRelate.setChapterhourType("1"))
+                    .peek(oneCourseRelate -> oneCourseRelate.setMainId(classHour.getId())) // 修改属性
+                    .peek(oneCourseRelate -> oneCourseRelate.setInfoType("0")) // 修改属性
+                    .collect(Collectors.toList());
+            courseRelateService.addBatch(courseRelates);
+        }
+
+
+
+
+        //根据章节id和课程id和课时id删除关联表
+        Map<String, Object> knowledgeParam = new HashMap<>();
+        knowledgeParam.put("COURSE_ID", courseInfo.getCourseId());
+        knowledgeParam.put("CHAPTER_ID", classHour.getChapterId());
+        knowledgeParam.put("HOUR_ID", classHour.getId());
+        courseChapterKnowledgeService.removeByMap(knowledgeParam);
+
+        //添加章节,设计知识点关联表
+        List<CourseChapterKnowledge> list=new ArrayList<>();
+        classHourEditParam.getKnowledgeIds().stream().forEach(knowledgeId -> {
+
+            CourseChapterKnowledge courseChapterKnowledge = new CourseChapterKnowledge();
+            courseChapterKnowledge.setCourseId(courseInfo.getCourseId());
+            courseChapterKnowledge.setChapterId(classHour.getChapterId());
+            courseChapterKnowledge.setHourId(classHour.getId());
+            courseChapterKnowledge.setKnowledgeId(knowledgeId);
+            courseChapterKnowledge.setId(IdUtil.getSnowflake().nextIdStr());
+            list.add(courseChapterKnowledge);
+        });
+        courseChapterKnowledgeService.saveBatch(list);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除课时表
+     *
+     * @author pans
+     * @date  2025/07/02 10:36
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除课时表")
+    @CommonLog("删除课时表")
+    @PostMapping("/disk/hour/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<ClassHourIdParam> classHourIdParamList) {
+        ClassHour classHour=classHourService.queryEntity(classHourIdParamList.get(0).getId());
+        Chapter chapter = chapterService.queryEntity(classHour.getChapterId());
+        classHourService.delete(classHourIdParamList);
+        //更新课程的课时数量字段
+        CourseInfo courseInfo = courseInfoService.queryEntity(chapter.getCourseId());
+        Map param =new HashMap();
+        param.put("courseId", chapter.getCourseId());
+        List<Chapter> chapterList=chapterService.wrapperList(param);
+        List<String> chapterIdList= CollStreamUtil.toList(chapterList, Chapter::getId);
+        param.clear();
+        param.put("chapterIdList", chapterIdList);
+        List<ClassHour> classHourList =classHourService.wrapperList(param);
+        courseInfo.setHourCount(String.valueOf(classHourList.size()));
+        courseInfoService.editOne(courseInfo);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取课时表详情
+     *
+     * @author pans
+     * @date  2025/07/02 10:36
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取课时表详情")
+    @GetMapping("/disk/hour/detail")
+    public CommonResult<Map<String,Object>> detail(@Valid ClassHourIdParam classHourIdParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("id", req.getParameter("id"));
+        Map<String,Object> result=classHourService.queryInfo(param);
+        //查回课程关联信息
+        List<Map<String,Object>> courseRelatesList=new ArrayList<>();
+        Map funcTypeParam =new HashMap();
+        funcTypeParam.put("mainId", classHourIdParam.getId());
+        funcTypeParam.put("chapterhourType","1");
+        funcTypeParam.put("infoType","0");
+        List<CourseRelate> relateList=courseRelateService.wrapperList(funcTypeParam);
+        List<String>funcTypeList=CollStreamUtil.toList(relateList, CourseRelate::getFuncType);
+        //去重类型
+        funcTypeList=funcTypeList.stream().distinct().collect(Collectors.toList());
+        Map relateParam =new HashMap();
+        relateParam.put("mainId", req.getParameter("id"));
+        relateParam.put("chapterhourType", "1");
+        //分别查回视频,讲义,字幕,作业,测验等
+        for(String oneFunctype:funcTypeList)
+        {
+            relateParam.put("funcType", oneFunctype);
+            List<Map<String,Object>> videoList=courseRelateService.queryMapList(relateParam);
+            courseRelatesList.addAll(videoList);
+        }
+        result.put("courseRelates", courseRelatesList);
+
+
+       String chapterId=MapUtils.getString(result, "chapterId");
+
+        Chapter chapter = chapterService.queryEntity(chapterId);
+        CourseInfo courseInfo = courseInfoService.queryEntity(chapter.getCourseId());
+
+
+        //根据章节,获得的知识点
+        List<String> knowledegeList=courseChapterKnowledgeService.selectknowledge(courseInfo.getCourseId(),chapterId,classHourIdParam.getId());
+        //章节对应知识点
+        result.put("knowledgeIds", knowledegeList);
+        return CommonResult.data(result);
+    }
+
+    /**
+     * 课程中心-课时表详情
+     *
+     * @author pans
+     * @date  2025/07/02 10:36
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("课程中心-课时表详情")
+    @GetMapping("/disk/coursecentry/hourDetail")
+    public CommonResult<Map<String,Object>> hourDetail(@Valid ClassHourIdParam classHourIdParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("id", req.getParameter("id"));
+        Map<String,Object> result=classHourService.queryInfo(param);
+
+        //查回课程关联信息
+        List<Map<String,Object>> courseRelatesList=new ArrayList<>();
+        Map funcTypeParam =new HashMap();
+        funcTypeParam.put("mainId", classHourIdParam.getId());
+        funcTypeParam.put("chapterhourType","1");
+        funcTypeParam.put("infoType","0");
+        List<CourseRelate> relateList=courseRelateService.wrapperList(funcTypeParam);
+        List<String>funcTypeList=CollStreamUtil.toList(relateList, CourseRelate::getFuncType);
+        //去重类型
+        funcTypeList=funcTypeList.stream().distinct().collect(Collectors.toList());
+        Map relateParam =new HashMap();
+        relateParam.put("mainId", req.getParameter("id"));
+        relateParam.put("chapterhourType", "1");
+        //分别查回视频,讲义,字幕,作业,测验等
+        for(String oneFunctype:funcTypeList)
+        {
+            relateParam.put("funcType", oneFunctype);
+            List<Map<String,Object>> videoList=courseRelateService.queryMapList(relateParam);
+            courseRelatesList.addAll(videoList);
+        }
+        result.put("courseRelates", courseRelatesList);
+        return CommonResult.data(result);
+    }
+
+}

+ 126 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CourseLogController.java

@@ -0,0 +1,126 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.disk.domain.CourseLog;
+import vip.xiaonuo.disk.param.courselog.CourseLogAddParam;
+import vip.xiaonuo.disk.param.courselog.CourseLogEditParam;
+import vip.xiaonuo.disk.param.courselog.CourseLogIdParam;
+import vip.xiaonuo.disk.param.courselog.CourseLogPageParam;
+import vip.xiaonuo.disk.service.CourseLogService;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+
+/**
+ * 课程足迹控制器
+ *
+ * @author pans
+ * @date  2025/08/05 10:48
+ */
+@Api(tags = "课程足迹控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class CourseLogController {
+
+    @Resource
+    private CourseLogService courseLogService;
+
+    /**
+     * 获取课程足迹分页
+     *
+     * @author pans
+     * @date  2025/08/05 10:48
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取课程足迹分页")
+    @GetMapping("/disk/courselog/page")
+    public CommonResult<Page<CourseLog>> page(CourseLogPageParam courseLogPageParam) {
+        return CommonResult.data(courseLogService.page(courseLogPageParam));
+    }
+
+    /**
+     * 添加课程足迹
+     *
+     * @author pans
+     * @date  2025/08/05 10:48
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加课程足迹")
+    @CommonLog("添加课程足迹")
+    @PostMapping("/disk/courselog/add")
+    public CommonResult<String> add(@RequestBody @Valid CourseLogAddParam courseLogAddParam) {
+        courseLogService.add(courseLogAddParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑课程足迹
+     *
+     * @author pans
+     * @date  2025/08/05 10:48
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑课程足迹")
+    @CommonLog("编辑课程足迹")
+    @PostMapping("/disk/courselog/edit")
+    public CommonResult<String> edit(@RequestBody @Valid CourseLogEditParam courseLogEditParam) {
+        courseLogService.edit(courseLogEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除课程足迹
+     *
+     * @author pans
+     * @date  2025/08/05 10:48
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除课程足迹")
+    @CommonLog("删除课程足迹")
+    @PostMapping("/disk/courselog/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<CourseLogIdParam> courseLogIdParamList) {
+        courseLogService.delete(courseLogIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取课程足迹详情
+     *
+     * @author pans
+     * @date  2025/08/05 10:48
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取课程足迹详情")
+    @GetMapping("/disk/courselog/detail")
+    public CommonResult<CourseLog> detail(@Valid CourseLogIdParam courseLogIdParam) {
+        return CommonResult.data(courseLogService.detail(courseLogIdParam));
+    }
+
+}

+ 172 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CourseNotebooksController.java

@@ -0,0 +1,172 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.hutool.core.collection.CollStreamUtil;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.disk.domain.CourseNotebooks;
+import vip.xiaonuo.disk.domain.CourseNotes;
+import vip.xiaonuo.disk.domain.CourseRelate;
+import vip.xiaonuo.disk.param.CourseNotebooksAddParam;
+import vip.xiaonuo.disk.param.CourseNotebooksEditParam;
+import vip.xiaonuo.disk.param.CourseNotebooksIdParam;
+import vip.xiaonuo.disk.param.CourseNotebooksPageParam;
+import vip.xiaonuo.disk.param.courseclasshourfilerelate.CourseRelateIdParam;
+import vip.xiaonuo.disk.service.CourseNotebooksService;
+import vip.xiaonuo.disk.service.CourseNotesService;
+import vip.xiaonuo.disk.service.CourseRelateService;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 课程笔记本表控制器
+ *
+ * @author 金吉龙
+ * @date  2025/07/01 18:40
+ */
+@Api(tags = "课程笔记本表控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class CourseNotebooksController {
+
+    @Resource
+    private CourseNotebooksService courseNotebooksService;
+    @Resource
+    private CourseNotesService courseNotesService;
+    @Resource
+    private CourseRelateService courseRelateService;
+
+    /**
+     * 获取课程笔记本表分页
+     *
+     * @author 金吉龙
+     * @date  2025/07/01 18:40
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取课程笔记本表分页")
+    //@SaCheckPermission("/disk/courseNotebooks/page")
+    @GetMapping("/disk/courseNotebooks/page")
+    public CommonResult<Page<Map<String,Object>>> page(CourseNotebooksPageParam courseNotebooksPageParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("courseId", req.getParameter("courseId"));
+        param.put("courseName", req.getParameter("courseName"));
+        param.put("userId", StpLoginUserUtil.getLoginUser().getId());
+        Page<Map<String,Object>> page=courseNotebooksService.queryList(param);
+        return CommonResult.data(page);
+    }
+
+    /**
+     * 添加课程笔记本表(暂时废弃)
+     *
+     * @author 金吉龙
+     * @date  2025/07/01 18:40
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加课程笔记本表")
+    @CommonLog("添加课程笔记本表")
+    //@SaCheckPermission("/disk/courseNotebooks/add")
+    @PostMapping("/disk/courseNotebooks/add")
+    public CommonResult<String> add(@RequestBody @Valid CourseNotebooksAddParam courseNotebooksAddParam) {
+        courseNotebooksService.add(courseNotebooksAddParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑课程笔记本表(暂时废弃)
+     *
+     * @author 金吉龙
+     * @date  2025/07/01 18:40
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑课程笔记本表")
+    @CommonLog("编辑课程笔记本表")
+    //@SaCheckPermission("/disk/courseNotebooks/edit")
+    @PostMapping("/disk/courseNotebooks/edit")
+    public CommonResult<String> edit(@RequestBody @Valid CourseNotebooksEditParam courseNotebooksEditParam) {
+        courseNotebooksService.edit(courseNotebooksEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除课程笔记本表
+     *
+     * @author 金吉龙
+     * @date  2025/07/01 18:40
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除课程笔记本表")
+    @CommonLog("删除课程笔记本表")
+    //@SaCheckPermission("/disk/courseNotebooks/delete")
+    @PostMapping("/disk/courseNotebooks/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<CourseNotebooksIdParam> courseNotebooksIdParamList) {
+        //1.先查回这些要删除的笔记是什么课程的
+        List<String> deleteIdList=CollStreamUtil.toList(courseNotebooksIdParamList, CourseNotebooksIdParam::getNotebookId);
+        Map param=new HashMap<>();
+        param.put("notebookIdList", deleteIdList);
+        List<CourseNotebooks> wllDelCourseNotebooksList=courseNotebooksService.wrapperList(param);
+        //2.删除笔记本本身
+        courseNotebooksService.delete(courseNotebooksIdParamList);
+        //3.删除该课程对应的具体笔记,以及课程关联的信息
+        List<String> courseIdList=CollStreamUtil.toList(wllDelCourseNotebooksList, CourseNotebooks::getCourseId);
+        param.clear();
+        param.put("courseIdList", courseIdList);
+        param.put("userId", StpLoginUserUtil.getLoginUser().getId());
+        List<CourseNotes> wllDelCourseNotesList=courseNotesService.wrapperList(param);
+        List<String> wllDelNoteIdList=CollStreamUtil.toList(wllDelCourseNotesList, CourseNotes::getNoteId);
+        courseNotesService.deleteByIds(wllDelNoteIdList);
+        param.clear();
+        param.put("relateIdList", wllDelNoteIdList);
+        List<CourseRelate> wllDelCourseRelateList=courseRelateService.wrapperList(param);
+        List<String> wllDelCourseRelateIdList=CollStreamUtil.toList(wllDelCourseRelateList, CourseRelate::getId);
+        courseRelateService.deleteByIdsReal(wllDelCourseRelateIdList);
+
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取课程笔记本表详情(暂时废弃)
+     *
+     * @author 金吉龙
+     * @date  2025/07/01 18:40
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取课程笔记本表详情")
+    //@SaCheckPermission("/disk/courseNotebooks/detail")
+    @GetMapping("/disk/courseNotebooks/detail")
+    public CommonResult<CourseNotebooks> detail(@Valid CourseNotebooksIdParam courseNotebooksIdParam) {
+        return CommonResult.data(courseNotebooksService.detail(courseNotebooksIdParam));
+    }
+
+}

+ 208 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CourseNotesController.java

@@ -0,0 +1,208 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollStreamUtil;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.disk.domain.*;
+import vip.xiaonuo.disk.param.CourseNotesAddParam;
+import vip.xiaonuo.disk.param.CourseNotesEditParam;
+import vip.xiaonuo.disk.param.CourseNotesIdParam;
+import vip.xiaonuo.disk.param.CourseNotesPageParam;
+import vip.xiaonuo.disk.service.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 课程笔记表控制器
+ *
+ * @author 金吉龙
+ * @date  2025/07/01 18:56
+ */
+@Api(tags = "课程笔记表控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class CourseNotesController {
+
+    @Resource
+    private CourseNotesService courseNotesService;
+    @Resource
+    private CourseInfoService courseInfoService;
+    @Resource
+    private ChapterService chapterService;
+    @Resource
+    private ClassHourService classHourService;
+    @Resource
+    private CourseNotebooksService courseNotebooksService;
+    @Resource
+    private CourseRelateService courseRelateService;
+
+    /**
+     * 课程笔记-分页列表
+     *
+     * @author 金吉龙
+     * @date  2025/07/01 18:56
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("课程笔记-分页列表")
+    @GetMapping("/disk/courseNotes/page")
+    public CommonResult<Page<Map<String,Object>>> page(CourseNotesPageParam courseNotesPageParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("courseId", req.getParameter("courseId"));
+        param.put("hourId", req.getParameter("hourId"));
+        param.put("userId", StpLoginUserUtil.getLoginUser().getId());
+        Page<Map<String,Object>> page=courseNotesService.queryList(param);
+        return CommonResult.data(page);
+    }
+
+    /**
+     * 添加课程笔记表
+     *
+     * @author 金吉龙
+     * @date  2025/07/01 18:56
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加课程笔记表")
+    @CommonLog("添加课程笔记表")
+    @PostMapping("/disk/courseNotes/add")
+    public CommonResult<String> add(@RequestBody @Valid CourseNotesAddParam courseNotesAddParam) {
+        Map param=new HashMap<>();
+        //1.先校验这个课时是不是第一次创建笔记,如果是第一次创建,则创建课程笔记本
+        String courseId = courseNotesAddParam.getCourseId();
+        if(StringUtils.isEmpty(courseId))
+        {
+            if(StringUtils.isEmpty(courseNotesAddParam.getHourId()))return CommonResult.error("课时id不能为空");
+            ClassHour classHour=classHourService.queryEntity(courseNotesAddParam.getHourId());
+            Chapter chapter = chapterService.queryEntity(classHour.getChapterId());
+            courseId=chapter.getCourseId();
+        }
+        param.put("courseId", courseId);
+        param.put("userId", StpLoginUserUtil.getLoginUser().getId() );
+        List<CourseNotebooks> courseNotebooksList=courseNotebooksService.wrapperList(param);
+        CourseNotebooks courseNotebooks=new CourseNotebooks();
+        if(courseNotebooksList.size()==0)
+        {
+            courseNotebooks.setCourseId(courseId);
+            courseNotebooks.setUserId(StpLoginUserUtil.getLoginUser().getId());
+            courseNotebooksService.addOne(courseNotebooks);
+        }
+
+        //2.存储笔记本体信息
+        CourseNotes courseNotes = BeanUtil.toBean(courseNotesAddParam, CourseNotes.class);
+        courseNotes.setUserId(StpLoginUserUtil.getLoginUser().getId());
+        courseNotes.setCourseId(courseId);
+        courseNotesService.addOne(courseNotes);
+        //3.添加进课程关联信息
+        CourseRelate courseRelate=new CourseRelate();
+        courseRelate.setMainId(courseNotesAddParam.getHourId());
+        courseRelate.setRelateId(courseNotes.getNoteId());
+        //功能类型,1视频资源,2讲义,3字幕,4作业,5测验,6笔记,7问答
+        courseRelate.setFuncType("6");
+        courseRelate.setChapterhourType("1");
+        courseRelate.setInfoType("1");
+        courseRelateService.addOne(courseRelate);
+
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑课程笔记表
+     *
+     * @author 金吉龙
+     * @date  2025/07/01 18:56
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑课程笔记表")
+    @CommonLog("编辑课程笔记表")
+    @PostMapping("/disk/courseNotes/edit")
+    public CommonResult<String> edit(@RequestBody @Valid CourseNotesEditParam courseNotesEditParam) {
+        CourseNotes courseNotes = BeanUtil.toBean(courseNotesEditParam, CourseNotes.class);
+        courseNotesService.editOne(courseNotes);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除课程笔记表
+     *
+     * @author 金吉龙
+     * @date  2025/07/01 18:56
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除课程笔记表")
+    @CommonLog("删除课程笔记表")
+    @PostMapping("/disk/courseNotes/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<CourseNotesIdParam> courseNotesIdParamList) {
+        //删除笔记基本信息
+        courseNotesService.delete(courseNotesIdParamList);
+        //删除课程关联笔记信息
+        List<String> deleteNoteIdList=CollStreamUtil.toList(courseNotesIdParamList, CourseNotesIdParam::getNoteId);
+        Map param=new HashMap<>();
+        param.put("relateIdList", deleteNoteIdList);
+        List<CourseRelate> courseRelateList=courseRelateService.wrapperList(param);
+        List<String> deleteRelateIdList=CollStreamUtil.toList(courseRelateList, CourseRelate::getId);
+        courseRelateService.deleteByIdsReal(deleteRelateIdList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取课程笔记表详情
+     *
+     * @author 金吉龙
+     * @date  2025/07/01 18:56
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取课程笔记表详情")
+    @GetMapping("/disk/courseNotes/detail")
+    public CommonResult<CourseNotes> detail(@Valid CourseNotesIdParam courseNotesIdParam) {
+        return CommonResult.data(courseNotesService.detail(courseNotesIdParam));
+    }
+
+    /**
+     * 根据章节ip查询记录的笔记
+     *
+     * @author 金吉龙
+     * @date  2025/07/01 18:56
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取课程笔记表详情")
+    @GetMapping("/disk/courseNotes/getByChapterId")
+    public CommonResult<CourseNotes> getByChapterId(@Valid CourseNotesAddParam courseNotesAddParam) {
+        return CommonResult.data(courseNotesService.getByChapterId(courseNotesAddParam));
+    }
+
+
+
+}

+ 185 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CourseOpenController.java

@@ -0,0 +1,185 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.hutool.core.bean.BeanUtil;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.disk.domain.CourseOpen;
+import vip.xiaonuo.disk.param.courseInfo.CourseInfoPageParam;
+import vip.xiaonuo.disk.param.courseopen.CourseOpenAddParam;
+import vip.xiaonuo.disk.param.courseopen.CourseOpenEditParam;
+import vip.xiaonuo.disk.param.courseopen.CourseOpenIdParam;
+import vip.xiaonuo.disk.param.courseopen.CourseOpenPageParam;
+import vip.xiaonuo.disk.service.CourseOpenService;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 课程开课表控制器
+ *
+ * @author honorfire
+ * @date  2025/08/01 17:48
+ */
+@Api(tags = "课程开课表控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class CourseOpenController {
+
+    @Resource
+    private CourseOpenService courseOpenService;
+
+    /**
+     * 课程开课-分页列表
+     *
+     * @author honorfire
+     * @date  2025/08/01 17:48
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("课程开课-分页列表")
+    //@SaCheckPermission("/disk/courseopen/page")
+    @GetMapping("/disk/courseopen/page")
+    public CommonResult<Page<Map<String,Object>>> page(CourseOpenPageParam courseOpenPageParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("gradesId", req.getParameter("gradesId"));
+        param.put("courseId", req.getParameter("courseId"));
+        param.put("courseName", req.getParameter("courseName"));
+        param.put("semesterId", req.getParameter("semesterId"));
+        param.put("weekType", req.getParameter("weekType"));
+        param.put("status", req.getParameter("status"));
+        param.put("gradeName", req.getParameter("gradeName"));
+        Page<Map<String,Object>> list=courseOpenService.queryList(param);
+        return CommonResult.data(list);
+    }
+
+    /**
+     * 课程开课-添加
+     *
+     * @author honorfire
+     * @date  2025/08/01 17:48
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("课程开课-添加")
+    @CommonLog("课程开课-添加")
+    //@SaCheckPermission("/disk/courseopen/add")
+    @PostMapping("/disk/courseopen/add")
+    public CommonResult<String> add(@RequestBody @Valid CourseOpenAddParam courseOpenAddParam) {
+        CourseOpen courseOpen = BeanUtil.toBean(courseOpenAddParam, CourseOpen.class);
+        courseOpenService.addOne(courseOpen);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 课程开课-编辑
+     *
+     * @author honorfire
+     * @date  2025/08/01 17:48
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("课程开课-编辑")
+    @CommonLog("课程开课-编辑")
+    //@SaCheckPermission("/disk/courseopen/edit")
+    @PostMapping("/disk/courseopen/edit")
+    public CommonResult<String> edit(@RequestBody @Valid CourseOpenEditParam courseOpenEditParam) {
+        CourseOpen courseOpen = BeanUtil.toBean(courseOpenEditParam, CourseOpen.class);
+        courseOpenService.editOne(courseOpen);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 课程开课-删除
+     *
+     * @author honorfire
+     * @date  2025/08/01 17:48
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("课程开课-删除")
+    @CommonLog("课程开课-删除")
+    //@SaCheckPermission("/disk/courseopen/delete")
+    @PostMapping("/disk/courseopen/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<CourseOpenIdParam> courseOpenIdParamList) {
+        courseOpenService.delete(courseOpenIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 课程开课-详情
+     *
+     * @author honorfire
+     * @date  2025/08/01 17:48
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("课程开课-详情")
+    //@SaCheckPermission("/disk/courseopen/detail")
+    @GetMapping("/disk/courseopen/detail")
+    public CommonResult<Map<String,Object>> detail(@Valid CourseOpenIdParam courseOpenIdParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("id", req.getParameter("id"));
+        Map<String,Object> result=courseOpenService.queryInfo(param);
+        return CommonResult.data(result);
+    }
+
+
+    /**
+     * 根据学期id查询课程id
+     * @param semesterId
+     * @return
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("根据学期id查询课程id")
+    @GetMapping("/disk/courseopen/queryCourseList")
+    public CommonResult<List<Map<String,Object>>> page(@RequestParam("semesterId") String semesterId) {
+        return CommonResult.data(courseOpenService.queryCourseList(semesterId));
+    }
+
+    /**
+     * 课程开课-历史开课统计
+     *
+     * @author honorfire
+     * @date  2025/06/20 15:00
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("课程开课-历史开课统计")
+    @GetMapping("/disk/courseopen/historyProgressPage")
+    public CommonResult<Page<Map<String,Object>>> historyProgressPage(HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("courseName", req.getParameter("courseName"));
+        param.put("beginTime", req.getParameter("beginTime"));
+        param.put("endTime", req.getParameter("endTime"));
+
+        Page<Map<String,Object>> list=courseOpenService.historyProgressPage(param);
+        return CommonResult.data(list);
+    }
+
+
+
+
+}

+ 272 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CourseQuestionAnswerController.java

@@ -0,0 +1,272 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollStreamUtil;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.disk.domain.Chapter;
+import vip.xiaonuo.disk.domain.ClassHour;
+import vip.xiaonuo.disk.domain.CourseRelate;
+import vip.xiaonuo.disk.domain.QuestionAnswer;
+import vip.xiaonuo.disk.param.*;
+import vip.xiaonuo.disk.service.ChapterService;
+import vip.xiaonuo.disk.service.ClassHourService;
+import vip.xiaonuo.disk.service.CourseRelateService;
+import vip.xiaonuo.disk.service.QuestionAnswerService;
+import vip.xiaonuo.disk.vo.questionanswer.QuestionAnswerVo;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 课程问答控制器
+ *
+ * @author pans
+ * @date  2025/07/11 18:52
+ */
+@Api(tags = "课程问答控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class CourseQuestionAnswerController {
+
+    @Resource
+    private QuestionAnswerService questionAnswerService;
+    @Resource
+    private CourseRelateService courseRelateService;
+    @Resource
+    private ChapterService chapterService;
+    @Resource
+    private ClassHourService classHourService;
+
+    /**
+     * 获取课程问答分页
+     *
+     * @author pans
+     * @date  2025/07/11 18:52
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取课程问答分页")
+    @GetMapping("/disk/answer/page")
+    public CommonResult<Page<QuestionAnswerVo>> page(QuestionAnswerPageParam questionAnswerPageParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("courseId", req.getParameter("courseId"));
+        param.put("hourId", req.getParameter("hourId"));
+        param.put("userId", StpLoginUserUtil.getLoginUser().getId());
+        //是否是查父级评论,0否1是
+        param.put("isPid", "1");
+        //教育身份,0管理员1老师2学生,默认是学生
+        String eduIdentity="2";
+        if(StringUtils.isNotEmpty(req.getParameter("eduIdentity")))eduIdentity=req.getParameter("eduIdentity");
+        param.put("eduIdentity", eduIdentity);
+        //查询一级评论问题
+        Page<QuestionAnswerVo> page=questionAnswerService.queryList(param);
+        List<QuestionAnswerVo> recordList=page.getRecords();
+        List<String> recordIdList=  recordList.stream().map(map -> (String) map.getId()).collect(Collectors.toList());
+
+        param.clear();
+        param.put("isPid", "0");
+        param.put("pidList", recordIdList);
+        //查询二级评论问题
+        List<QuestionAnswerVo> recordTwoList=questionAnswerService.queryMapList(param);
+        // 1. 准备结果:为每个章节添加classHours字段
+        for (QuestionAnswerVo record : recordList) {
+            //预留是否有子集字段,0否1是
+            String haveAnswer="0";
+            if (record == null || record.getId() == null) continue;
+
+            String recordId = record.getId().toString();
+            List<QuestionAnswerVo> recordChildList = new ArrayList<>();
+
+            // 2. 倒序遍历课程列表(关键点!)
+            for (int i = recordTwoList.size() - 1; i >= 0; i--) {
+                QuestionAnswerVo recordTwo = recordTwoList.get(i);
+                if (recordTwo == null || recordTwo.getPid() == null) continue;
+
+                // 3. 匹配当前章节
+                if (recordId.equals(recordTwo.getPid().toString())) {
+                    recordChildList.add(recordTwo);  // 添加到章节
+                    recordTwoList.remove(i);          // 从原列表删除(安全操作)
+                }
+            }
+            if(recordChildList.size()>0)haveAnswer="1";
+            record.setHaveAnswer(haveAnswer);
+            record.setRecordChildList(recordChildList);
+        }
+
+        return CommonResult.data(page);
+    }
+
+
+    /**
+     * 添加课程问答
+     *
+     * @author pans
+     * @date  2025/07/11 18:52
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加课程问答")
+    @CommonLog("添加课程问答")
+    @PostMapping("/disk/answer/add")
+    public CommonResult<String> add(@RequestBody @Valid QuestionAnswerAddParam questionAnswerAddParam) {
+        //1.添加问答信息
+        QuestionAnswer questionAnswer = BeanUtil.toBean(questionAnswerAddParam, QuestionAnswer.class);
+        questionAnswer.setUserId(StpLoginUserUtil.getLoginUser().getId());
+        if(StringUtils.isEmpty(questionAnswer.getCourseId()))
+        {
+            ClassHour classHour = classHourService.queryEntity(questionAnswerAddParam.getHourId());
+            Chapter chapter = chapterService.queryEntity(classHour.getChapterId());
+            questionAnswer.setCourseId(chapter.getCourseId());
+        }
+        questionAnswer=questionAnswerService.addOne(questionAnswer);
+        //2.添加进课程关联信息
+        CourseRelate courseRelate=new CourseRelate();
+        courseRelate.setMainId(questionAnswerAddParam.getHourId());
+        courseRelate.setRelateId(questionAnswer.getId());
+        //功能类型,1视频资源,2讲义,3字幕,4作业,5测验,6笔记,7问答
+        courseRelate.setFuncType("7");
+        courseRelate.setChapterhourType("1");
+        courseRelate.setInfoType("1");
+        courseRelateService.addOne(courseRelate);
+
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑课程问答
+     *
+     * @author pans
+     * @date  2025/07/11 18:52
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑课程问答")
+    @CommonLog("编辑课程问答")
+    @PostMapping("/disk/answer/edit")
+    public CommonResult<String> edit(@RequestBody @Valid QuestionAnswerEditParam questionAnswerEditParam) {
+        questionAnswerService.edit(questionAnswerEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除课程问答
+     *
+     * @author pans
+     * @date  2025/07/11 18:52
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除课程问答")
+    @CommonLog("删除课程问答")
+    @PostMapping("/disk/answer/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<QuestionAnswerIdParam> questionAnswerIdParamList) {
+        questionAnswerService.delete(questionAnswerIdParamList);
+        //删除课程关联问答信息
+        List<String> deleteIdList= CollStreamUtil.toList(questionAnswerIdParamList, QuestionAnswerIdParam::getId);
+        Map param=new HashMap<>();
+        param.put("relateIdList", deleteIdList);
+        List<CourseRelate> courseRelateList=courseRelateService.wrapperList(param);
+        List<String> deleteRelateIdList=CollStreamUtil.toList(courseRelateList, CourseRelate::getId);
+        courseRelateService.deleteByIdsReal(deleteRelateIdList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取课程问答详情
+     *
+     * @author pans
+     * @date  2025/07/11 18:52
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取课程问答详情")
+    @GetMapping("/disk/answer/detail")
+    public CommonResult<Map<String,Object>> detail(@Valid QuestionAnswerIdParam questionAnswerIdParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("id", req.getParameter("id"));
+        Map<String,Object> result=new HashMap<>();
+        QuestionAnswerVo studentAnswer=questionAnswerService.queryInfo(param);
+        param.clear();
+        param.put("isPid", "0");
+        param.put("pid", req.getParameter("id"));
+//        result=studentAnswer;
+//        //查询二级评论问题
+//        List<Map<String, Object>> recordChildList = new ArrayList<>();
+//        List<Map<String,Object>> recordTwoList=questionAnswerService.queryMapList(param);
+//        recordChildList.addAll(recordTwoList);
+//        result.put("recordChildList", recordChildList);
+        //原来是父子级,现在变成一个list
+        param.clear();
+        param.put("isPid", "0");
+        param.put("pid", req.getParameter("id"));
+        List<QuestionAnswerVo> recordList = new ArrayList<>();
+        List<QuestionAnswerVo> recordTwoList=questionAnswerService.queryMapList(param);
+        recordList.add(studentAnswer);
+        recordList.addAll(recordTwoList);
+        result.put("recordList", recordList);
+        return CommonResult.data(result);
+    }
+
+
+    /**
+     * 点赞
+     *
+     * @author pans
+     * @date  2025/06/27 11:07
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("点赞")
+    @CommonLog("点赞")
+    @GetMapping("/disk/answer/give")
+    public CommonResult<String> edit(@Valid @NotNull(message="主键id不能为空") String id) {
+        questionAnswerService.give(id);
+        return CommonResult.ok();
+    }
+
+
+    /**
+     * 取消点赞
+     *
+     * @author pans
+     * @date  2025/06/27 11:07
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("点赞")
+    @CommonLog("点赞")
+    @GetMapping("/disk/answer/giveCancel")
+    public CommonResult<String> giveCancel(@Valid @NotNull(message="主键id不能为空") String id) {
+        return CommonResult.ok(questionAnswerService.giveCancel(id));
+    }
+
+}

+ 58 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CourseStudentBehaviorSocket.java

@@ -0,0 +1,58 @@
+package vip.xiaonuo.disk.controller;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
+
+import javax.websocket.*;
+import javax.websocket.server.PathParam;
+import javax.websocket.server.ServerEndpoint;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 测试websocket
+ *
+ * @author jinjilong
+ */
+@Slf4j
+@Component
+@ServerEndpoint("/webSocket/courseStudentBehavior/{clientId}?courseId={courseId}&userId=${userId}")
+public class CourseStudentBehaviorSocket {
+
+    private Session session;
+
+
+
+
+    private static ConcurrentHashMap<String, CourseStudentBehaviorSocket> meetingWebSocket = new ConcurrentHashMap<>();
+
+    @OnOpen
+    public void onOpen(Session session, @PathParam(value = "courseId") String courseId, @PathParam(value = "userId") String userId) {
+        System.out.println("用户:" + userId + "连接成功");
+
+//        session.getUserProperties().put("userId", userId);
+//        session.getUserProperties().put("courseId", courseId);
+    }
+
+
+    @OnClose
+    public void onClose() {
+
+    }
+
+
+    @OnMessage
+    public void onMessage(String message, Session session) throws Exception
+    {
+
+
+
+    }
+
+    @OnError
+    public void onError(Session session, Throwable throwable) {
+
+    }
+
+
+}

+ 199 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CourseStudentBurialpointController.java

@@ -0,0 +1,199 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollStreamUtil;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.disk.domain.CourseStudentBurialpoint;
+import vip.xiaonuo.disk.param.coursestudentprogress.CourseStudentProgressAddParam;
+import vip.xiaonuo.disk.param.coursestudentprogress.CourseStudentProgressEditParam;
+import vip.xiaonuo.disk.param.coursestudentprogress.CourseStudentProgressIdParam;
+import vip.xiaonuo.disk.param.coursestudentprogress.CourseStudentProgressPageParam;
+import vip.xiaonuo.disk.service.CourseStudentBurialpointService;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 课程学生-学习埋点表控制器
+ *
+ * @author honorfire
+ * @date  2025/07/30 14:57
+ */
+@Api(tags = "课程学生-学习埋点表控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class CourseStudentBurialpointController {
+
+    @Resource
+    private CourseStudentBurialpointService courseStudentBurialpointService;
+
+    /**
+     * 获取课程学生-学习埋点表分页
+     *
+     * @author honorfire
+     * @date  2025/07/30 14:57
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取课程学生-学习埋点表分页")
+    @GetMapping("/disk/coursestudentburialpoint/page")
+    public CommonResult<Page<CourseStudentBurialpoint>> page(CourseStudentProgressPageParam courseStudentProgressPageParam) {
+        return CommonResult.data(courseStudentBurialpointService.page(courseStudentProgressPageParam));
+    }
+
+    /**
+     * 课程学生学习进度表-添加
+     *
+     * @author honorfire
+     * @date  2025/07/30 14:57
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("课程学生学习进度表-添加")
+    @CommonLog("课程学生学习进度表-添加")
+    @PostMapping("/disk/coursestudentburialpoint/add")
+    public CommonResult<String> add(@RequestBody @Valid CourseStudentProgressAddParam courseStudentProgressAddParam) {
+        Map param=new HashMap();
+        CourseStudentBurialpoint courseStudentBurialpoint =BeanUtil.toBean(courseStudentProgressAddParam, CourseStudentBurialpoint.class);
+        String userId =StpLoginUserUtil.getLoginUser().getId();
+        courseStudentBurialpoint.setUserId(userId);
+        //进度不能覆盖,查询该学生该课程上一次的学习进度,如果本次进度不大于最高的进度,就只存最高的进度
+        if("1".equals(courseStudentBurialpoint.getType())){
+            param.put("userId", userId);
+            param.put("hourId", courseStudentBurialpoint.getHourId());
+            param.put("funcType", courseStudentBurialpoint.getFuncType());
+            param.put("type", courseStudentBurialpoint.getType());
+            List<CourseStudentBurialpoint> alreadyList= courseStudentBurialpointService.wrapperList(param);
+            List<String> alreadyProgressList=CollStreamUtil.toList(alreadyList, CourseStudentBurialpoint::getProgress);
+            //将String转为Long
+            List<Long> alreadyProgressLongList=alreadyProgressList.stream().map(Long::parseLong).collect(Collectors.toList());
+            //取出最大的进度
+            Long maxProgress=alreadyProgressLongList.stream().max(Long::compareTo).orElse(0L);
+            if(Long.valueOf(courseStudentProgressAddParam.getProgress()).longValue()<maxProgress.longValue()){
+                courseStudentBurialpoint.setProgress(String.valueOf(maxProgress));
+            }
+        }
+        courseStudentBurialpointService.addOne(courseStudentBurialpoint);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 课程学生学习进度-查询最近一次进度
+     *
+     * @author honorfire
+     * @date  2025/07/30 14:57
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("课程学生学习进度-查询最近一次进度")
+    @GetMapping("/disk/coursestudentburialpoint/theLastDetail")
+    public CommonResult<CourseStudentBurialpoint> theLastDetail(@Valid CourseStudentProgressPageParam courseStudentProgressPageParam, HttpServletRequest req) {
+        Map param=new HashMap();
+        param.put("userId", StpLoginUserUtil.getLoginUser().getId());
+        param.put("hourId", req.getParameter("hourId"));
+        param.put("funcType", req.getParameter("funcType"));
+        param.put("type", req.getParameter("type"));
+        //是否查询最近一次进度,0否1是
+        param.put("isLast", "1");
+        List<CourseStudentBurialpoint> alreadyList= courseStudentBurialpointService.wrapperList(param);
+        CourseStudentBurialpoint courseStudentBurialpoint =null;
+        if(alreadyList.size()>0)
+        {
+            courseStudentBurialpoint =alreadyList.get(0);
+        }
+        return CommonResult.data(courseStudentBurialpoint);
+    }
+
+
+    /**
+     * 编辑课程学生-学习埋点表
+     *
+     * @author honorfire
+     * @date  2025/07/30 14:57
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑课程学生-学习埋点表")
+    @CommonLog("编辑课程学生-学习埋点表")
+    @PostMapping("/disk/coursestudentburialpoint/edit")
+    public CommonResult<String> edit(@RequestBody @Valid CourseStudentProgressEditParam courseStudentProgressEditParam) {
+        CourseStudentBurialpoint courseStudentBurialpoint =BeanUtil.toBean(courseStudentProgressEditParam, CourseStudentBurialpoint.class);
+        courseStudentBurialpointService.editOne(courseStudentBurialpoint);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除课程学生-学习埋点表
+     *
+     * @author honorfire
+     * @date  2025/07/30 14:57
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除课程学生-学习埋点表")
+    @CommonLog("删除课程学生-学习埋点表")
+    //@SaCheckPermission("/disk/coursestudentburialpoint/delete")
+    @PostMapping("/disk/coursestudentburialpoint/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<CourseStudentProgressIdParam> courseStudentProgressIdParamList) {
+        courseStudentBurialpointService.delete(courseStudentProgressIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取课程学生-学习埋点表详情
+     *
+     * @author honorfire
+     * @date  2025/07/30 14:57
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取课程学生-学习埋点表详情")
+    //@SaCheckPermission("/disk/coursestudentburialpoint/detail")
+    @GetMapping("/disk/coursestudentburialpoint/detail")
+    public CommonResult<CourseStudentBurialpoint> detail(@Valid CourseStudentProgressIdParam courseStudentProgressIdParam) {
+        return CommonResult.data(courseStudentBurialpointService.detail(courseStudentProgressIdParam));
+    }
+
+    /**
+     * 删除学习足迹
+     *
+     * @param id 足迹id
+     * @return 操作结果
+     *
+     * @author jinjilong
+     * @date 2025-10-30
+     */
+    @ApiOperationSupport(order = 6)
+    @ApiOperation("删除学习足迹")
+    @CommonLog("删除学习足迹")
+    @GetMapping("/disk/coursestudentburialpoint/deleteBurialPoint")
+    public CommonResult<String> deleteBurialPoint(@RequestParam("id") String id) {
+        return courseStudentBurialpointService.deleteBurialPoint(id);
+    }
+
+}

+ 201 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CourseStudentCollectController.java

@@ -0,0 +1,201 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollStreamUtil;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.disk.domain.CourseStudentCollect;
+import vip.xiaonuo.disk.param.coursestudentcollect.*;
+import vip.xiaonuo.disk.service.CourseStudentCollectService;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 课程学生-收藏表控制器
+ *
+ * @author honorfire
+ * @date  2025/07/19 16:36
+ */
+@Api(tags = "课程学生-收藏表控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class CourseStudentCollectController {
+
+    @Resource
+    private CourseStudentCollectService courseStudentCollectService;
+
+    /**
+     * 课程收藏-分页列表
+     *
+     * @author honorfire
+     * @date  2025/07/19 16:36
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("课程收藏-分页列表")
+    //@SaCheckPermission("/disk/coursestudentcollect/page")
+    @GetMapping("/disk/coursestudentcollect/page")
+    public CommonResult<Page<Map<String, Object>>> page(CourseStudentCollectPageParam courseStudentCollectPageParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("userId", StpLoginUserUtil.getLoginUser().getId());
+        param.put("courseName", req.getParameter("courseName"));
+        param.put("collegeId", req.getParameter("collegeId"));
+        param.put("collegeTwoId", req.getParameter("collegeTwoId"));
+        param.put("collegeThreeId", req.getParameter("collegeThreeId"));
+        param.put("majorId", req.getParameter("majorId"));
+        param.put("courseType", req.getParameter("courseType"));
+        param.put("beginTime", req.getParameter("beginTime"));
+        param.put("endTime", req.getParameter("endTime"));
+        //排序类型,0最新,1热门
+        String sortflag="0";
+        if(StringUtils.isNotEmpty(req.getParameter("sortflag")))sortflag=req.getParameter("sortflag");
+        param.put("sortflag", sortflag);
+
+        Page<Map<String,Object>> list=courseStudentCollectService.queryList(param);
+        return CommonResult.data(list);
+    }
+
+    /**
+     * 课程收藏-添加
+     *
+     * @author honorfire
+     * @date  2025/07/19 16:36
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("课程收藏-添加")
+    @CommonLog("课程收藏-添加")
+    //@SaCheckPermission("/disk/coursestudentcollect/add")
+    @PostMapping("/disk/coursestudentcollect/add")
+    public CommonResult<String> add(@RequestBody @Valid CourseStudentCollectAddParam courseStudentCollectAddParam) {
+        CourseStudentCollect courseStudentCollect = BeanUtil.toBean(courseStudentCollectAddParam, CourseStudentCollect.class);
+        courseStudentCollect.setUserId(StpLoginUserUtil.getLoginUser().getId());
+        courseStudentCollectService.addOne(courseStudentCollect);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 课程收藏-取消收藏
+     *
+     * @author honorfire
+     * @date  2025/07/19 16:36
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("课程收藏-取消收藏")
+    @CommonLog("课程收藏-取消收藏")
+    //@SaCheckPermission("/disk/coursestudentcollect/notCollect")
+    @PostMapping("/disk/coursestudentcollect/notCollect")
+    public CommonResult<String> notCollect(@RequestBody @Valid CourseStudentCollectNotCollectParam courseStudentCollectNotCollectParam) {
+        Map param=new HashMap();
+        param.put("courseId", courseStudentCollectNotCollectParam.getCourseId());
+        param.put("userId", StpLoginUserUtil.getLoginUser().getId());
+        List<CourseStudentCollect> deleteList=courseStudentCollectService.wrapperList(param);
+        List<String> deleteIdList=CollStreamUtil.toList(deleteList, CourseStudentCollect::getId);
+        courseStudentCollectService.deleteByIds(deleteIdList);
+        return CommonResult.ok();
+    }
+
+
+    /**
+     * 课程收藏-编辑
+     *
+     * @author honorfire
+     * @date  2025/07/19 16:36
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("课程收藏-编辑")
+    @CommonLog("课程收藏-编辑")
+    //@SaCheckPermission("/disk/coursestudentcollect/edit")
+    @PostMapping("/disk/coursestudentcollect/edit")
+    public CommonResult<String> edit(@RequestBody @Valid CourseStudentCollectEditParam courseStudentCollectEditParam) {
+        CourseStudentCollect courseStudentCollect = BeanUtil.toBean(courseStudentCollectEditParam, CourseStudentCollect.class);
+        courseStudentCollectService.editOne(courseStudentCollect);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 课程收藏-删除
+     *
+     * @author honorfire
+     * @date  2025/07/19 16:36
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("课程收藏-删除")
+    @CommonLog("课程收藏-删除")
+    //@SaCheckPermission("/disk/coursestudentcollect/delete")
+    @PostMapping("/disk/coursestudentcollect/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<CourseStudentCollectIdParam> courseStudentCollectIdParamList) {
+        courseStudentCollectService.delete(courseStudentCollectIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 课程收藏-详情
+     *
+     * @author honorfire
+     * @date  2025/07/19 16:36
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("课程收藏-详情")
+    //@SaCheckPermission("/disk/coursestudentcollect/detail")
+    @GetMapping("/disk/coursestudentcollect/detail")
+    public CommonResult<Map<String,Object>> detail(@Valid CourseStudentCollectIdParam courseStudentCollectIdParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("id", req.getParameter("id"));
+        param.put("courseId", req.getParameter("courseId"));
+        Map<String,Object> result=courseStudentCollectService.queryInfo(param);
+        return CommonResult.data(result);
+    }
+
+    /**
+     * 课程收藏-是否收藏
+     *
+     * @author honorfire
+     * @date  2025/07/19 16:36
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("课程收藏-详情")
+    //@SaCheckPermission("/disk/coursestudentcollect/isCollect")
+    @GetMapping("/disk/coursestudentcollect/isCollect")
+    public CommonResult<Map<String,Object>> isCollect(@Valid CourseStudentCollectIdParam courseStudentCollectIdParam, HttpServletRequest req) {
+        Map result=new HashMap();
+        Map param =new HashMap();
+        param.put("id", req.getParameter("id"));
+        Boolean isCollect=courseStudentCollectService.isCollect(param);
+        result.put("isCollect", isCollect);
+        return CommonResult.data(result);
+    }
+
+}

+ 365 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/CourseStudentRelateController.java

@@ -0,0 +1,365 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollStreamUtil;
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONObject;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.MediaType;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.handler.CommonSm4CbcTypeHandler;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.common.util.CommonCryptogramUtil;
+import vip.xiaonuo.disk.domain.Chapter;
+import vip.xiaonuo.disk.domain.ClassHour;
+import vip.xiaonuo.disk.domain.CourseStudentRelate;
+import vip.xiaonuo.disk.domain.ResourceRecordUserRelate;
+import vip.xiaonuo.disk.param.coursestudentrelate.CourseStudentRelateAddParam;
+import vip.xiaonuo.disk.param.coursestudentrelate.CourseStudentRelateEditParam;
+import vip.xiaonuo.disk.param.coursestudentrelate.CourseStudentRelateIdParam;
+import vip.xiaonuo.disk.param.coursestudentrelate.CourseStudentRelatePageParam;
+import vip.xiaonuo.disk.service.ChapterService;
+import vip.xiaonuo.disk.service.ClassHourService;
+import vip.xiaonuo.disk.service.CourseStudentRelateService;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import java.io.IOException;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 课程学生关联表控制器
+ *
+ * @author honorfire
+ * @date  2025/07/12 11:04
+ */
+@Api(tags = "课程学生关联表控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class CourseStudentRelateController {
+
+    @Resource
+    private CourseStudentRelateService courseStudentRelateService;
+    @Resource
+    private ChapterService chapterService;
+    @Resource
+    private ClassHourService classHourService;
+    @Resource
+    private CommonCryptogramUtil commonCryptogramUtil;
+
+    /**
+     * 获取课程学生关联表分页
+     *
+     * @author honorfire
+     * @date  2025/07/12 11:04
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取课程学生关联表分页")
+    //@SaCheckPermission("/disk/coursestudentrelate/page")
+    @GetMapping("/disk/coursestudentrelate/page")
+    public CommonResult<Page<Map<String,Object>>> page(CourseStudentRelatePageParam courseStudentRelatePageParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("gradeName", req.getParameter("gradeName"));
+        param.put("courseId", req.getParameter("courseId"));
+        param.put("queryInfo", req.getParameter("queryInfo"));
+        param.put("studentNum", req.getParameter("studentNum"));
+        param.put("gender", req.getParameter("gender"));
+        param.put("userStatus", req.getParameter("userStatus"));
+        param.put("latestLoginStartTime", req.getParameter("latestLoginStartTime"));
+        param.put("latestLoginEndTime", req.getParameter("latestLoginEndTime"));
+        Page<Map<String,Object>> page=courseStudentRelateService.queryList(param);
+        List<Map<String,Object>> list=page.getRecords();
+        //根据获取所有b端登录用户的方法得到的获取所有在线用户id,然后比对这些用户是否在其中,在则为在线,不在为离线
+        //0离线1在线
+        List<String> userIdList = StpUtil.searchSessionId("", 0, Integer.MAX_VALUE, true)
+                .stream()
+                .map(sessionId -> StrUtil.split(sessionId, StrUtil.COLON).get(3))
+                .collect(Collectors.toList());
+        for(Map oneUser : list)
+        {
+            //处理手机号和身份证号
+            String phone="";
+            String idCardNumber="";
+            String birth="";
+            if(ObjectUtil.isNotEmpty(oneUser.get("phone"))){
+                phone= String.valueOf(oneUser.get("phone"));
+                phone=commonCryptogramUtil.doSm4CbcDecrypt(phone);
+            }
+            if(ObjectUtil.isNotEmpty(oneUser.get("idCardNumber"))){
+                idCardNumber=String.valueOf(oneUser.get("idCardNumber"));
+                idCardNumber=commonCryptogramUtil.doSm4CbcDecrypt(idCardNumber);
+                birth=extractBirthday(idCardNumber);
+            }
+            oneUser.put("phone",phone);
+            oneUser.put("birth",birth);
+            //处理在线状态
+            if(userIdList.contains(oneUser.get("userId")))
+            {
+                oneUser.put("isLogin","1");
+                oneUser.put("isLoginName","在线");
+            }else
+            {
+                oneUser.put("isLogin","0");
+                oneUser.put("isLoginName","离线");
+            }
+        }
+
+
+        return CommonResult.data(page);
+    }
+
+    /**
+     * 添加课程学生关联表
+     *
+     * @author honorfire
+     * @date  2025/07/12 11:04
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加课程学生关联表")
+    @CommonLog("添加课程学生关联表")
+    //@SaCheckPermission("/disk/coursestudentrelate/add")
+    @PostMapping("/disk/coursestudentrelate/add")
+    public CommonResult<String> add(@RequestBody @Valid CourseStudentRelateAddParam courseStudentRelateAddParam) {
+        Map param=new HashMap();
+        List<CourseStudentRelate> willAddList=new ArrayList<>();
+        //先排查该课程要新关联的学生id中,是否已有某学生关联,存在的话进行剔除
+        String[] userIds = courseStudentRelateAddParam.getUserIds().split(",");
+        List<String> willUserIdList= new ArrayList<>(Arrays.asList(userIds));
+        param.put("courseId", courseStudentRelateAddParam.getCourseId());
+        List<CourseStudentRelate> alreadyRelateList=courseStudentRelateService.listWrapper(param);
+        List<String> alreadyRelateIdList=CollStreamUtil.toList(alreadyRelateList, CourseStudentRelate::getUserId);
+        for (int i = willUserIdList.size() - 1; i >= 0; i--)
+        {
+            String willUserId = willUserIdList.get(i);
+            if(alreadyRelateIdList.contains(willUserId))
+            {
+                willUserIdList.remove(i);
+            }
+        }
+        if(willUserIdList.size()>0)
+        {
+            for(String willUserId:willUserIdList)
+            {
+                CourseStudentRelate courseStudentRelate = BeanUtil.toBean(courseStudentRelateAddParam, CourseStudentRelate.class);
+                courseStudentRelate.setUserId(willUserId);
+                willAddList.add(courseStudentRelate);
+            }
+            courseStudentRelateService.addBatch(willAddList);
+        }
+
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑课程学生关联表
+     *
+     * @author honorfire
+     * @date  2025/07/12 11:04
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑课程学生关联表")
+    @CommonLog("编辑课程学生关联表")
+    //@SaCheckPermission("/disk/coursestudentrelate/edit")
+    @PostMapping("/disk/coursestudentrelate/edit")
+    public CommonResult<String> edit(@RequestBody @Valid CourseStudentRelateEditParam courseStudentRelateEditParam) {
+        CourseStudentRelate courseStudentRelate = BeanUtil.toBean(courseStudentRelateEditParam, CourseStudentRelate.class);
+        courseStudentRelateService.editOne(courseStudentRelate);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除课程学生关联表
+     *
+     * @author honorfire
+     * @date  2025/07/12 11:04
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除课程学生关联表")
+    @CommonLog("删除课程学生关联表")
+    //@SaCheckPermission("/disk/coursestudentrelate/delete")
+    @PostMapping("/disk/coursestudentrelate/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<CourseStudentRelateIdParam> courseStudentRelateIdParamList) {
+        List<String> wllDelIdList=CollStreamUtil.toList(courseStudentRelateIdParamList, CourseStudentRelateIdParam::getId);
+        courseStudentRelateService.deleteByIdsReal(wllDelIdList);
+//        courseStudentRelateService.delete(courseStudentRelateIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 课程学生关联-详情
+     *
+     * @author honorfire
+     * @date  2025/07/12 11:04
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("课程学生关联-详情")
+    //@SaCheckPermission("/disk/coursestudentrelate/detail")
+    @GetMapping("/disk/coursestudentrelate/detail")
+    public CommonResult<Map<String,Object>> detail(@Valid CourseStudentRelateIdParam courseStudentRelateIdParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("id", req.getParameter("id"));
+        Map<String,Object> oneUser=courseStudentRelateService.queryInfo(param);
+        String phone="";
+        String idCardNumber="";
+        String birth="";
+        if(ObjectUtil.isNotEmpty(oneUser.get("phone"))){
+            phone= String.valueOf(oneUser.get("phone"));
+            phone=commonCryptogramUtil.doSm4CbcDecrypt(phone);
+        }
+        if(ObjectUtil.isNotEmpty(oneUser.get("idCardNumber"))){
+            idCardNumber=String.valueOf(oneUser.get("idCardNumber"));
+            idCardNumber=commonCryptogramUtil.doSm4CbcDecrypt(idCardNumber);
+            birth=extractBirthday(idCardNumber);
+        }
+        oneUser.put("phone",phone);
+        oneUser.put("birth",birth);
+
+        List<String> userIdList = StpUtil.searchSessionId("", 0, Integer.MAX_VALUE, true)
+                .stream()
+                .map(sessionId -> StrUtil.split(sessionId, StrUtil.COLON).get(3))
+                .collect(Collectors.toList());
+        if(userIdList.contains(oneUser.get("userId")))
+        {
+            oneUser.put("isLogin","1");
+            oneUser.put("isLoginName","在线");
+        }else
+        {
+            oneUser.put("isLogin","0");
+            oneUser.put("isLoginName","离线");
+        }
+        return CommonResult.data(oneUser);
+    }
+
+    /**
+     * 课程学生关联-导入
+     *
+     * @author xuyuxiang
+     * @date 2022/4/24 20:00
+     */
+    @ApiOperationSupport(order = 16)
+    @ApiOperation("用户导入")
+    @CommonLog("用户导入")
+    @PostMapping("/disk/coursestudentrelate/import")
+    public CommonResult<JSONObject> importInfo(@RequestPart("file") @ApiParam(value="文件", required = true) MultipartFile file,@RequestParam("courseId") String courseId) {
+        if(StringUtils.isEmpty(courseId))return CommonResult.error("课程id不能为空");
+
+        return CommonResult.data(courseStudentRelateService.importInfo(file,courseId));
+    }
+
+    /**
+     * 课程学生关联-下载导入模板
+     *
+     */
+    @ApiOperationSupport(order = 15)
+    @ApiOperation("课程学生关联-下载导入模板")
+    @CommonLog("课程学生关联-下载导入模板")
+    @GetMapping(value = "/disk/coursestudentrelate/downloadImportlate", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
+    public void downloadImportUserTemplate(HttpServletResponse response) throws IOException {
+        courseStudentRelateService.downloadImportTemplate(response);
+    }
+
+    /**
+     * 从身份证号提取生日(支持15/18位)
+     * @param idCard 身份证号码
+     * @return 格式为"yyyy年MM月dd日"的生日字符串,无效身份证返回null
+     */
+    public String extractBirthday(String idCard) {
+        return Optional.ofNullable(idCard)
+                .map(String::trim)  // 去除首尾空格
+                .filter(id -> id.matches("^[1-9]\\d{5}(19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[\\dXx]$"))
+                .map(id -> {
+                    String birthPart = id.substring(6, 14);  // 直接取18位的年月日
+                    try {
+                        LocalDate birthDate = LocalDate.parse(
+                                birthPart,
+                                DateTimeFormatter.ofPattern("yyyyMMdd")  // 明确指定格式
+                        );
+                        return birthDate.format(
+                                DateTimeFormatter.ofPattern("yyyy年MM月dd日")
+                        );
+                    } catch (Exception e) {
+                        return null;
+                    }
+                })
+                .orElse(null);
+    }
+
+    /**
+     * 获取关联学生-下拉列表
+     *
+     * @author honorfire
+     * @date  2025/07/12 11:04
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取关联学生-下拉列表")
+    //@SaCheckPermission("/disk/coursestudentrelate/downList")
+    @GetMapping("/disk/coursestudentrelate/downList")
+    public CommonResult<Page<Map<String,Object>>> downList(CourseStudentRelatePageParam courseStudentRelatePageParam, HttpServletRequest req) {
+        Map<String,Object> param = convertRequestParamsToMapWithMultiValues(req);
+        return CommonResult.data(courseStudentRelateService.downList(param));
+    }
+
+
+    /**
+     * 将HttpServletRequest中的查询参数转换为Map(支持多值参数)
+     * @param request HTTP请求对象
+     * @return 包含所有参数的Map
+     */
+    public static Map<String, Object> convertRequestParamsToMapWithMultiValues(HttpServletRequest request) {
+        Map<String, Object> paramsMap = new HashMap<>();
+        Map<String, String[]> parameterMap = request.getParameterMap();
+
+        for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
+            String paramName = entry.getKey();
+            String[] paramValues = entry.getValue();
+
+            if (paramValues.length == 1) {
+                paramsMap.put(paramName, paramValues[0]);
+            } else if (paramValues.length > 1) {
+                paramsMap.put(paramName, Arrays.asList(paramValues));
+            } else {
+                paramsMap.put(paramName, "");
+            }
+        }
+
+        return paramsMap;
+    }
+
+
+
+
+}

+ 92 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/ExamineController.java

@@ -0,0 +1,92 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.v3.oas.annotations.Operation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.disk.domain.Examine;
+import vip.xiaonuo.disk.dto.examine.ExamineReq;
+import vip.xiaonuo.disk.service.ExamineService;
+import vip.xiaonuo.disk.util.PageQuery;
+import vip.xiaonuo.disk.util.TableDataInfo;
+import vip.xiaonuo.disk.vo.examine.ExamineVo;
+
+import javax.annotation.Resource;
+import java.util.Map;
+
+/**
+ * examine控制器
+ *
+ * @author pans
+ * @date  2025/06/19 11:21
+ */
+@Api(tags = "examine控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+@RequestMapping("/examine")
+public class ExamineController {
+
+    @Resource
+    private ExamineService examineService;
+
+    @Operation(summary = "审批列表", description = "审批列表查询接口")
+    @RequestMapping(value = "/queryList", method = RequestMethod.GET)
+    @ResponseBody
+    public TableDataInfo<ExamineVo> queryList(ExamineReq examineReq, PageQuery pageQuery) {
+        return examineService.queryList(examineReq,pageQuery);
+    }
+
+
+    @Operation(summary = "审核通过", description = "审核通过")
+    @RequestMapping(value = "/pass", method = RequestMethod.POST)
+    @ResponseBody
+    public CommonResult<String> pass(@RequestBody Examine examine) {
+        try {
+            examineService.pass(examine);
+            return CommonResult.ok("审核通过");
+        } catch (Exception e) {
+            return CommonResult.error("审核失败");
+        }
+    }
+
+
+    @Operation(summary = "驳回", description = "审核通过")
+    @RequestMapping(value = "/reject", method = RequestMethod.POST)
+    @ResponseBody
+    public CommonResult<String> reject(@RequestBody Examine examine) {
+        try {
+            examineService.reject(examine);
+            return CommonResult.ok("驳回成功");
+        } catch (Exception e) {
+            return CommonResult.error("驳回失败");
+        }
+    }
+
+
+    @Operation(summary = "文件详情接口", description = "文件详情接口")
+    @RequestMapping(value = "/detail", method = RequestMethod.GET)
+    @ResponseBody
+    public CommonResult<Map<String,Object>> detail(String userFileId) {
+        Map<String,Object> map= examineService.detail(userFileId);
+        return CommonResult.ok().data(map);
+    }
+
+
+
+
+}

+ 1091 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/FileController.java

@@ -0,0 +1,1091 @@
+package vip.xiaonuo.disk.controller;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.IdUtil;
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import co.elastic.clients.elasticsearch.core.SearchResponse;
+import co.elastic.clients.elasticsearch.core.search.HighlighterEncoder;
+import co.elastic.clients.elasticsearch.core.search.Hit;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.qiwenshare.common.anno.MyLog;
+import com.qiwenshare.common.exception.QiwenException;
+import com.qiwenshare.common.result.RestResult;
+import com.qiwenshare.common.util.DateUtil;
+import com.qiwenshare.common.util.security.SessionUtil;
+import com.qiwenshare.ufop.factory.UFOPFactory;
+import com.qiwenshare.ufop.operation.copy.Copier;
+import com.qiwenshare.ufop.operation.copy.domain.CopyFile;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.jetty.util.StringUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.util.ClassUtils;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
+import vip.xiaonuo.disk.component.AsyncTaskComp;
+import vip.xiaonuo.disk.component.FileDealComp;
+import vip.xiaonuo.disk.config.es.FileSearch;
+import vip.xiaonuo.disk.domain.FileBean;
+import vip.xiaonuo.disk.domain.UserFile;
+import vip.xiaonuo.disk.dto.file.*;
+import vip.xiaonuo.disk.io.QiwenFile;
+import vip.xiaonuo.disk.service.IFileService;
+import vip.xiaonuo.disk.service.IUserFileService;
+import vip.xiaonuo.disk.service.M3u8UploadService;
+import vip.xiaonuo.disk.util.OperationLogUtil;
+import vip.xiaonuo.disk.util.QiwenFileUtil;
+import vip.xiaonuo.disk.util.TreeNode;
+import vip.xiaonuo.disk.vo.file.FileDetailVO;
+import vip.xiaonuo.disk.vo.file.FileListVO;
+import vip.xiaonuo.disk.vo.file.SearchFileVO;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
+import java.io.ByteArrayInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.*;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.regex.Pattern;
+
+@Tag(name = "file", description = "该接口为文件接口,主要用来做一些文件的基本操作,如创建目录,删除,移动,复制等。")
+@RestController
+@Slf4j
+@RequestMapping("/file")
+public class FileController {
+
+    @Resource
+    IFileService fileService;
+    @Resource
+    IUserFileService userFileService;
+    @Resource
+    UFOPFactory ufopFactory;
+    @Resource
+    FileDealComp fileDealComp;
+    @Resource
+    AsyncTaskComp asyncTaskComp;
+    @Resource
+    OperationLogUtil operationLogUtil;
+
+    @Autowired
+    private ElasticsearchClient elasticsearchClient;
+    @Value("${ufop.storage-type}")
+    private Integer storageType;
+
+    @Value("${common.account}")
+    private String commonUserId;
+
+    @Resource
+    private M3u8UploadService m3u8UploadService;
+
+
+    public static Executor executor = Executors.newFixedThreadPool(20);
+
+    public static final String CURRENT_MODULE = "文件接口";
+
+    public static final String SUCCESS = "操作成功";
+
+    public static final String FAIL = "操作失败";
+    @Operation(summary = "创建文件", description = "创建文件", tags = {"file"})
+    @ResponseBody
+    @RequestMapping(value = "/createFile", method = RequestMethod.POST)
+    public RestResult<Object> createFile(@Valid @RequestBody CreateFileDTO createFileDTO) {
+
+        try {
+
+            String userId = SessionUtil.getUserId();
+            String filePath = createFileDTO.getFilePath();
+            String fileName = createFileDTO.getFileName();
+            String extendName = createFileDTO.getExtendName();
+            List<UserFile> userFiles = userFileService.selectSameUserFile(fileName, filePath, extendName, userId);
+            if (userFiles != null && !userFiles.isEmpty()) {
+                return RestResult.fail().message("同名文件已存在");
+            }
+            String uuid = UUID.randomUUID().toString().replaceAll("-", "");
+
+            String templateFilePath = "";
+            if ("docx".equals(extendName)) {
+                templateFilePath = "template/Word.docx";
+            } else if ("xlsx".equals(extendName)) {
+                templateFilePath = "template/Excel.xlsx";
+            } else if ("pptx".equals(extendName)) {
+                templateFilePath = "template/PowerPoint.pptx";
+            } else if ("txt".equals(extendName)) {
+                templateFilePath = "template/Text.txt";
+            } else if ("drawio".equals(extendName)) {
+                templateFilePath = "template/Drawio.drawio";
+            }
+            String url2 = ClassUtils.getDefaultClassLoader().getResource("static/" + templateFilePath).getPath();
+            url2 = URLDecoder.decode(url2, "UTF-8");
+            FileInputStream fileInputStream = new FileInputStream(url2);
+            Copier copier = ufopFactory.getCopier();
+            CopyFile copyFile = new CopyFile();
+            copyFile.setExtendName(extendName);
+            String fileUrl = copier.copy(fileInputStream, copyFile);
+
+            FileBean fileBean = new FileBean();
+            fileBean.setFileId(IdUtil.getSnowflakeNextIdStr());
+            fileBean.setFileSize(0L);
+            fileBean.setFileUrl(fileUrl);
+            fileBean.setStorageType(storageType);
+            fileBean.setIdentifier(uuid);
+            fileBean.setCreateTime(DateUtil.getCurrentTime());
+            fileBean.setCreateUserId(StpLoginUserUtil.getLoginUser().getId());
+            fileBean.setFileStatus(1);
+            boolean saveFlag = fileService.save(fileBean);
+            UserFile userFile = new UserFile();
+            if (saveFlag) {
+                userFile.setUserFileId(IdUtil.getSnowflakeNextIdStr());
+                userFile.setUserId(userId);
+                userFile.setFileName(fileName);
+                userFile.setFilePath(filePath);
+                userFile.setDeleteFlag(0);
+                userFile.setIsDir(0);
+                userFile.setExtendName(extendName);
+                userFile.setUploadTime(DateUtil.getCurrentTime());
+                userFile.setFileId(fileBean.getFileId());
+                userFile.setCreateTime(DateUtil.getCurrentTime());
+                userFile.setCreateUserId(SessionUtil.getUserId());
+                userFileService.save(userFile);
+            }
+
+
+            return RestResult.success().message("文件创建成功");
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            return RestResult.fail().message(e.getMessage());
+        }
+    }
+
+
+
+
+
+
+
+    @Operation(summary = "创建文件", description = "创建文件", tags = {"file"})
+    @ResponseBody
+    @RequestMapping(value = "/createCommonFile", method = RequestMethod.POST)
+    public RestResult<Object> createCommonFile(HttpServletRequest httpServletRequest,@Valid @RequestBody CreateFileDTO createFileDTO) {
+
+        try {
+
+            String userId = commonUserId;
+            String filePath = createFileDTO.getFilePath();
+            String fileName = createFileDTO.getFileName();
+            String extendName = createFileDTO.getExtendName();
+            List<UserFile> userFiles = userFileService.selectSameUserFile(fileName, filePath, extendName, userId);
+            if (userFiles != null && !userFiles.isEmpty()) {
+                return RestResult.fail().message("同名文件已存在");
+            }
+            String uuid = UUID.randomUUID().toString().replaceAll("-", "");
+
+            String templateFilePath = "";
+            if ("docx".equals(extendName)) {
+                templateFilePath = "template/Word.docx";
+            } else if ("xlsx".equals(extendName)) {
+                templateFilePath = "template/Excel.xlsx";
+            } else if ("pptx".equals(extendName)) {
+                templateFilePath = "template/PowerPoint.pptx";
+            } else if ("txt".equals(extendName)) {
+                templateFilePath = "template/Text.txt";
+            } else if ("drawio".equals(extendName)) {
+                templateFilePath = "template/Drawio.drawio";
+            }
+            String url2 = ClassUtils.getDefaultClassLoader().getResource("static/" + templateFilePath).getPath();
+            url2 = URLDecoder.decode(url2, "UTF-8");
+            FileInputStream fileInputStream = new FileInputStream(url2);
+            Copier copier = ufopFactory.getCopier();
+            CopyFile copyFile = new CopyFile();
+            copyFile.setExtendName(extendName);
+            String fileUrl = copier.copy(fileInputStream, copyFile);
+
+            FileBean fileBean = new FileBean();
+            fileBean.setFileId(IdUtil.getSnowflakeNextIdStr());
+            fileBean.setFileSize(0L);
+            fileBean.setFileUrl(fileUrl);
+            fileBean.setStorageType(storageType);
+            fileBean.setIdentifier(uuid);
+            fileBean.setCreateTime(DateUtil.getCurrentTime());
+            fileBean.setCreateUserId(StpLoginUserUtil.getLoginUser().getId());
+            fileBean.setFileStatus(1);
+            boolean saveFlag = fileService.save(fileBean);
+            UserFile userFile = new UserFile();
+            if (saveFlag) {
+                userFile.setUserFileId(IdUtil.getSnowflakeNextIdStr());
+                userFile.setUserId(userId);
+                userFile.setFileName(fileName);
+                userFile.setFilePath(filePath);
+                userFile.setDeleteFlag(0);
+                userFile.setIsDir(0);
+                userFile.setExtendName(extendName);
+                userFile.setUploadTime(DateUtil.getCurrentTime());
+                userFile.setFileId(fileBean.getFileId());
+                userFile.setCreateTime(DateUtil.getCurrentTime());
+                userFile.setCreateUserId(SessionUtil.getUserId());
+                userFileService.save(userFile);
+            }
+
+            return RestResult.success().message("文件创建成功");
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            return RestResult.fail().message(e.getMessage());
+        }
+    }
+
+
+
+
+
+    @Operation(summary = "创建文件夹", description = "目录(文件夹)的创建", tags = {"file"})
+    @RequestMapping(value = "/createFold", method = RequestMethod.POST)
+    @MyLog(operation = "创建文件夹", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult<String> createFold(HttpServletRequest httpServletRequest,@Valid @RequestBody CreateFoldDTO createFoldDto) {
+
+        String userId = StpLoginUserUtil.getLoginUser().getId();
+        String filePath = createFoldDto.getFilePath();
+
+
+        boolean isDirExist = fileDealComp.isDirExist(createFoldDto.getFileName(), createFoldDto.getFilePath(), userId);
+
+        if (isDirExist) {
+            return RestResult.fail().message("同名文件夹已存在");
+        }
+
+        UserFile userFile = QiwenFileUtil.getQiwenDir(userId, filePath, createFoldDto.getFileName());
+
+        userFileService.save(userFile);
+        fileDealComp.uploadESByUserFileId(userFile.getUserFileId());
+        operationLogUtil.getOperationLogObj(httpServletRequest, StpLoginUserUtil.getLoginUser().getId(), RestResult.success().getMessage(),CURRENT_MODULE,"创建文件夹",SUCCESS,userFile.getUserFileId(),"downLoad",userFile.getFileName());
+
+        return RestResult.success();
+    }
+
+
+
+    @Operation(summary = "创建文件夹", description = "目录(文件夹)的创建", tags = {"file"})
+    @RequestMapping(value = "/createCommonFold", method = RequestMethod.POST)
+    @MyLog(operation = "创建文件夹", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult<String> createCommonFold(HttpServletRequest httpServletRequest,@Valid @RequestBody CreateFoldDTO createFoldDto) {
+
+//        String userId = SessionUtil.getSession().getUserId();
+        String userId = commonUserId;
+        String filePath = createFoldDto.getFilePath();
+
+
+        boolean isDirExist = fileDealComp.isDirExist(createFoldDto.getFileName(), createFoldDto.getFilePath(), userId);
+
+        if (isDirExist) {
+            return RestResult.fail().message("同名文件夹已存在");
+        }
+
+        UserFile userFile = QiwenFileUtil.getQiwenDir(userId, filePath, createFoldDto.getFileName());
+
+        userFileService.save(userFile);
+        fileDealComp.uploadESByUserFileId(userFile.getUserFileId());
+        operationLogUtil.getOperationLogObj(httpServletRequest, StpLoginUserUtil.getLoginUser().getId(), RestResult.success().getMessage(),CURRENT_MODULE,"创建文件夹",SUCCESS,userFile.getUserFileId(),"downLoad",userFile.getFileName());
+        return RestResult.success();
+    }
+
+
+
+
+
+
+
+    @Operation(summary = "文件搜索", description = "文件搜索", tags = {"file"})
+    @GetMapping(value = "/search")
+    @MyLog(operation = "文件搜索", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult<SearchFileVO> searchFile(HttpServletRequest httpServletRequest,SearchFileDTO searchFileDTO) {
+//        JwtUser sessionUserBean =  SessionUtil.getSession();
+        String userId = StpLoginUserUtil.getLoginUser().getId();
+
+        int currentPage = (int)searchFileDTO.getCurrentPage() - 1;
+        int pageCount = (int)(searchFileDTO.getPageCount() == 0 ? 10 : searchFileDTO.getPageCount());
+
+        SearchResponse<FileSearch> search = null;
+        try {
+            search = elasticsearchClient.search(s -> s
+                            .index("filesearch")
+                            .query(_1 -> _1
+                                    .bool(_2 -> _2
+                                            .must(_3 -> _3
+                                                    .bool(_4 -> _4
+                                                            .should(_5 -> _5
+                                                                    .match(_6 -> _6
+                                                                            .field("fileName")
+                                                                            .query(searchFileDTO.getFileName())))
+                                                            .should(_5 -> _5
+                                                                    .wildcard(_6 -> _6
+                                                                            .field("fileName")
+                                                                            .wildcard("*" + searchFileDTO.getFileName() + "*")))
+                                                            .should(_5 -> _5
+                                                                    .match(_6 -> _6
+                                                                            .field("content")
+                                                                            .query(searchFileDTO.getFileName())))
+                                                            .should(_5 -> _5
+                                                                    .wildcard(_6 -> _6
+                                                                            .field("content")
+                                                                            .wildcard("*" + searchFileDTO.getFileName() + "*")))
+                                                    ))
+                                            .must(_3 -> _3
+                                                    .term(_4 -> _4
+                                                            .field("userId")
+                                                            .value(userId)))
+                                    ))
+                            .from(currentPage)
+                            .size(pageCount)
+                            .highlight(h -> h
+                                    .fields("fileName", f -> f.type("plain")
+                                            .preTags("<span class='keyword'>").postTags("</span>"))
+                                    .encoder(HighlighterEncoder.Html))
+                            ,
+                    FileSearch.class);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        List<SearchFileVO> searchFileVOList = new ArrayList<>();
+        for (Hit<FileSearch> hit : search.hits().hits()) {
+            SearchFileVO searchFileVO = new SearchFileVO();
+            BeanUtil.copyProperties(hit.source(), searchFileVO);
+            searchFileVO.setHighLight(hit.highlight());
+            searchFileVOList.add(searchFileVO);
+            asyncTaskComp.checkESUserFileId(searchFileVO.getUserFileId());
+        }
+
+        return RestResult.success().dataList(searchFileVOList, searchFileVOList.size());
+    }
+
+
+
+
+
+
+
+    @Operation(summary = "文件搜索", description = "文件搜索", tags = {"file"})
+    @GetMapping(value = "/searchCommon")
+    @MyLog(operation = "文件搜索", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult<SearchFileVO> searchCommon(HttpServletRequest httpServletRequest,SearchFileDTO searchFileDTO) {
+        String userId = commonUserId;
+
+        int currentPage = (int)searchFileDTO.getCurrentPage() - 1;
+        int pageCount = (int)(searchFileDTO.getPageCount() == 0 ? 10 : searchFileDTO.getPageCount());
+
+        SearchResponse<FileSearch> search = null;
+        try {
+            search = elasticsearchClient.search(s -> s
+                            .index("filesearch")
+                            .query(_1 -> _1
+                                    .bool(_2 -> _2
+                                            .must(_3 -> _3
+                                                    .bool(_4 -> _4
+                                                            .should(_5 -> _5
+                                                                    .match(_6 -> _6
+                                                                            .field("fileName")
+                                                                            .query(searchFileDTO.getFileName())))
+                                                            .should(_5 -> _5
+                                                                    .wildcard(_6 -> _6
+                                                                            .field("fileName")
+                                                                            .wildcard("*" + searchFileDTO.getFileName() + "*")))
+                                                            .should(_5 -> _5
+                                                                    .match(_6 -> _6
+                                                                            .field("content")
+                                                                            .query(searchFileDTO.getFileName())))
+                                                            .should(_5 -> _5
+                                                                    .wildcard(_6 -> _6
+                                                                            .field("content")
+                                                                            .wildcard("*" + searchFileDTO.getFileName() + "*")))
+                                                    ))
+                                            .must(_3 -> _3
+                                                    .term(_4 -> _4
+                                                            .field("userId")
+                                                            .value(userId)))
+                                    ))
+                            .from(currentPage)
+                            .size(pageCount)
+                            .highlight(h -> h
+                                    .fields("fileName", f -> f.type("plain")
+                                            .preTags("<span class='keyword'>").postTags("</span>"))
+                                    .encoder(HighlighterEncoder.Html))
+                    ,
+                    FileSearch.class);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        List<SearchFileVO> searchFileVOList = new ArrayList<>();
+        for (Hit<FileSearch> hit : search.hits().hits()) {
+            SearchFileVO searchFileVO = new SearchFileVO();
+            BeanUtil.copyProperties(hit.source(), searchFileVO);
+            searchFileVO.setHighLight(hit.highlight());
+            searchFileVOList.add(searchFileVO);
+            asyncTaskComp.checkESUserFileId(searchFileVO.getUserFileId());
+        }
+        return RestResult.success().dataList(searchFileVOList, searchFileVOList.size());
+    }
+
+
+
+
+
+
+    @Operation(summary = "文件重命名", description = "文件重命名", tags = {"file"})
+    @RequestMapping(value = "/renamefile", method = RequestMethod.POST)
+    @MyLog(operation = "文件重命名", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult<String> renameFile(HttpServletRequest httpServletRequest,@RequestBody RenameFileDTO renameFileDto) {
+
+        String userId = StpLoginUserUtil.getLoginUser().getId();
+        UserFile userFile = userFileService.getById(renameFileDto.getUserFileId());
+
+        List<UserFile> userFiles = userFileService.selectUserFileByNameAndPath(renameFileDto.getFileName(), userFile.getFilePath(), userId);
+        if (userFiles != null && !userFiles.isEmpty()) {
+            return RestResult.fail().message("同名文件已存在");
+        }
+
+        LambdaUpdateWrapper<UserFile> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
+        lambdaUpdateWrapper.set(UserFile::getFileName, renameFileDto.getFileName())
+                .set(UserFile::getUploadTime, DateUtil.getCurrentTime())
+                .eq(UserFile::getUserFileId, renameFileDto.getUserFileId());
+        userFileService.update(lambdaUpdateWrapper);
+        if (1 == userFile.getIsDir()) {
+            List<UserFile> list = userFileService.selectUserFileByLikeRightFilePath(new QiwenFile(userFile.getFilePath(), userFile.getFileName(), true).getPath(), userId);
+
+            for (UserFile newUserFile : list) {
+                String escapedPattern = Pattern.quote(new QiwenFile(userFile.getFilePath(), userFile.getFileName(), userFile.getIsDir() == 1).getPath());
+                newUserFile.setFilePath(newUserFile.getFilePath().replaceFirst(escapedPattern,
+                        new QiwenFile(userFile.getFilePath(), renameFileDto.getFileName(), userFile.getIsDir() == 1).getPath()));
+                userFileService.updateById(newUserFile);
+            }
+        }
+        fileDealComp.uploadESByUserFileId(renameFileDto.getUserFileId());
+        operationLogUtil.getOperationLogObj(httpServletRequest, StpLoginUserUtil.getLoginUser().getId(), RestResult.success().getMessage(),CURRENT_MODULE,"文件重命名",SUCCESS,userFile.getUserFileId(),"renamefile",userFile.getFileName());
+
+
+        return RestResult.success();
+    }
+
+
+
+
+
+
+
+    @Operation(summary = "文件重命名", description = "文件重命名", tags = {"file"})
+    @RequestMapping(value = "/renameCommonfile", method = RequestMethod.POST)
+    @MyLog(operation = "文件重命名", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult<String> renameCommonfile(HttpServletRequest httpServletRequest,@RequestBody RenameFileDTO renameFileDto) {
+
+        String userId =commonUserId;
+        UserFile userFile = userFileService.getById(renameFileDto.getUserFileId());
+
+        List<UserFile> userFiles = userFileService.selectUserFileByNameAndPath(renameFileDto.getFileName(), userFile.getFilePath(), userId);
+        if (userFiles != null && !userFiles.isEmpty()) {
+            return RestResult.fail().message("同名文件已存在");
+        }
+
+        LambdaUpdateWrapper<UserFile> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
+        lambdaUpdateWrapper.set(UserFile::getFileName, renameFileDto.getFileName())
+                .set(UserFile::getUploadTime, DateUtil.getCurrentTime())
+                .eq(UserFile::getUserFileId, renameFileDto.getUserFileId());
+        userFileService.update(lambdaUpdateWrapper);
+        if (1 == userFile.getIsDir()) {
+            List<UserFile> list = userFileService.selectUserFileByLikeRightFilePath(new QiwenFile(userFile.getFilePath(), userFile.getFileName(), true).getPath(), userId);
+
+            for (UserFile newUserFile : list) {
+                String escapedPattern = Pattern.quote(new QiwenFile(userFile.getFilePath(), userFile.getFileName(), userFile.getIsDir() == 1).getPath());
+                newUserFile.setFilePath(newUserFile.getFilePath().replaceFirst(escapedPattern,
+                        new QiwenFile(userFile.getFilePath(), renameFileDto.getFileName(), userFile.getIsDir() == 1).getPath()));
+                userFileService.updateById(newUserFile);
+            }
+        }
+        fileDealComp.uploadESByUserFileId(renameFileDto.getUserFileId());
+        operationLogUtil.getOperationLogObj(httpServletRequest, StpLoginUserUtil.getLoginUser().getId(), RestResult.success().getMessage(),CURRENT_MODULE,"文件重命名",SUCCESS,userFile.getUserFileId(),"renamefile",userFile.getFileName());
+        return RestResult.success();
+    }
+
+
+
+
+    @Operation(summary = "获取文件列表", description = "用来做前台列表展示", tags = {"file"})
+    @RequestMapping(value = "/getfilelist", method = RequestMethod.GET)
+    @ResponseBody
+    public RestResult<FileListVO> getFileList(
+            @Parameter(description = "文件类型", required = true) String fileType,
+            @Parameter(description = "文件路径", required = true) String filePath,
+            @Parameter(description = "当前页", required = true) long currentPage,
+            @Parameter(description = "页面数量", required = true) long pageCount){
+        String userId = StpLoginUserUtil.getLoginUser().getId();
+        if ("0".equals(fileType)) {
+            IPage<FileListVO> fileList = userFileService.userFileList(userId, filePath, currentPage, pageCount);
+            return RestResult.success().dataList(fileList.getRecords(), fileList.getTotal());
+        } else {
+            IPage<FileListVO> fileList = userFileService.getFileByFileType(Integer.valueOf(fileType), currentPage, pageCount, userId);
+            return RestResult.success().dataList(fileList.getRecords(), fileList.getTotal());
+        }
+    }
+
+
+    @Operation(summary = "获取公共资源库文件列表", description = "用来做前台列表展示", tags = {"file"})
+    @RequestMapping(value = "/getcommonfilelist", method = RequestMethod.GET)
+    @ResponseBody
+    public RestResult<FileListVO> getcommonfilelist(
+            @Parameter(description = "文件类型", required = true) String fileType,
+            @Parameter(description = "文件路径", required = true) String filePath,
+            @Parameter(description = "当前页", required = true) long currentPage,
+            @Parameter(description = "页面数量", required = true) long pageCount
+    ){
+        String userId = commonUserId;
+        if ("0".equals(fileType)) {
+            IPage<FileListVO> fileList = userFileService.userFileList(userId, filePath, currentPage, pageCount);
+            return RestResult.success().dataList(fileList.getRecords(), fileList.getTotal());
+        } else {
+            IPage<FileListVO> fileList = userFileService.getFileByFileType(Integer.valueOf(fileType), currentPage, pageCount, userId);
+            return RestResult.success().dataList(fileList.getRecords(), fileList.getTotal());
+        }
+    }
+
+
+
+
+    @Operation(summary = "批量删除文件", description = "批量删除文件", tags = {"file"})
+    @RequestMapping(value = "/batchdeletefile", method = RequestMethod.POST)
+    @MyLog(operation = "批量删除文件", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult<String> deleteImageByIds(HttpServletRequest httpServletRequest,@RequestBody BatchDeleteFileDTO batchDeleteFileDto) {
+        String userFileIds = batchDeleteFileDto.getUserFileIds();
+        String[] userFileIdList = userFileIds.split(",");
+        userFileService.update(new UpdateWrapper<UserFile>().lambda().set(UserFile::getDeleteFlag, 1).in(UserFile::getUserFileId, Arrays.asList(userFileIdList)));
+        for (String userFileId : userFileIdList) {
+            executor.execute(()->{
+                    userFileService.deleteUserFile(userFileId, StpLoginUserUtil.getLoginUser().getId());
+            });
+            UserFile userFile = userFileService.getById(userFileId);
+            fileDealComp.deleteESByUserFileId(userFileId);
+            operationLogUtil.getOperationLogObj(httpServletRequest, StpLoginUserUtil.getLoginUser().getId(), RestResult.success().getMessage(),CURRENT_MODULE,"批量删除文件",SUCCESS,userFileId,"batchdeletefile",userFile.getFileName());
+
+        }
+
+        return RestResult.success().message("批量删除文件成功");
+    }
+
+
+
+    @Operation(summary = "批量删除文件", description = "批量删除文件", tags = {"file"})
+    @RequestMapping(value = "/batchdeleteCommonfile", method = RequestMethod.POST)
+    @MyLog(operation = "批量删除文件", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult<String> batchdeleteCommonfile(HttpServletRequest httpServletRequest,@RequestBody BatchDeleteFileDTO batchDeleteFileDto) {
+        String userFileIds = commonUserId;
+        String[] userFileIdList = userFileIds.split(",");
+        userFileService.update(new UpdateWrapper<UserFile>().lambda().set(UserFile::getDeleteFlag, 1).in(UserFile::getUserFileId, Arrays.asList(userFileIdList)));
+        for (String userFileId : userFileIdList) {
+            executor.execute(()->{
+                userFileService.deleteUserFile(userFileId, commonUserId);
+            });
+            UserFile userFile = userFileService.getById(userFileId);
+            operationLogUtil.getOperationLogObj(httpServletRequest, StpLoginUserUtil.getLoginUser().getId(), RestResult.success().getMessage(),CURRENT_MODULE,"批量删除文件",SUCCESS,userFileId,"batchdeleteCommonfile",userFile.getFileName());
+
+            fileDealComp.deleteESByUserFileId(userFileId);
+        }
+
+        return RestResult.success().message("批量删除文件成功");
+    }
+
+
+
+
+
+
+
+    @Operation(summary = "删除文件", description = "可以删除文件或者目录", tags = {"file"})
+    @RequestMapping(value = "/deletefile", method = RequestMethod.POST)
+    @MyLog(operation = "删除文件", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult deleteFile(HttpServletRequest httpServletRequest,@RequestBody DeleteFileDTO deleteFileDto) {
+
+//        JwtUser sessionUserBean =  SessionUtil.getSession();
+        String userId = StpLoginUserUtil.getLoginUser().getId();
+        userFileService.deleteUserFile(deleteFileDto.getUserFileId(), userId);
+        UserFile userFile = userFileService.getById(deleteFileDto.getUserFileId());
+        fileDealComp.deleteESByUserFileId(deleteFileDto.getUserFileId());
+        operationLogUtil.getOperationLogObj(httpServletRequest, StpLoginUserUtil.getLoginUser().getId(), RestResult.success().getMessage(),CURRENT_MODULE,"删除文件",SUCCESS,deleteFileDto.getUserFileId(),"deletefile",userFile.getFileName());
+
+        return RestResult.success();
+
+    }
+
+
+    @Operation(summary = "删除文件", description = "可以删除文件或者目录", tags = {"file"})
+    @RequestMapping(value = "/deleteCommonfile", method = RequestMethod.POST)
+    @MyLog(operation = "删除文件", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult deleteCommonFile(HttpServletRequest httpServletRequest,@RequestBody DeleteFileDTO deleteFileDto) {
+        String userId =commonUserId;
+        userFileService.deleteUserFile(deleteFileDto.getUserFileId(), userId);
+        fileDealComp.deleteESByUserFileId(deleteFileDto.getUserFileId());
+        UserFile userFile = userFileService.getById(deleteFileDto.getUserFileId());
+        operationLogUtil.getOperationLogObj(httpServletRequest, StpLoginUserUtil.getLoginUser().getId(), RestResult.success().getMessage(),CURRENT_MODULE,"删除文件",SUCCESS,deleteFileDto.getUserFileId(),"deleteCommonfile",userFile.getFileName());
+
+        return RestResult.success();
+
+    }
+
+
+
+
+    @Operation(summary = "解压文件", description = "解压文件。", tags = {"file"})
+    @RequestMapping(value = "/unzipfile", method = RequestMethod.POST)
+    @MyLog(operation = "解压文件", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult<String> unzipFile(HttpServletRequest httpServletRequest,@RequestBody UnzipFileDTO unzipFileDto) {
+
+        try {
+            fileService.unzipFile(unzipFileDto.getUserFileId(), unzipFileDto.getUnzipMode(), unzipFileDto.getFilePath());
+            UserFile userFile = userFileService.getById(unzipFileDto.getUserFileId());
+            operationLogUtil.getOperationLogObj(httpServletRequest, StpLoginUserUtil.getLoginUser().getId(), RestResult.success().getMessage(),CURRENT_MODULE,"解压文件",SUCCESS,unzipFileDto.getUserFileId(),"unzipfile",userFile.getFileName());
+        } catch (QiwenException e) {
+            return RestResult.fail().message(e.getMessage());
+        }
+
+        return RestResult.success();
+
+    }
+
+
+
+    @Operation(summary = "解压文件", description = "解压文件。", tags = {"file"})
+    @RequestMapping(value = "/unzipCommonFile", method = RequestMethod.POST)
+    @MyLog(operation = "解压文件", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult<String> unzipCommonFile(HttpServletRequest httpServletRequest,@RequestBody UnzipFileDTO unzipFileDto) {
+
+        try {
+            fileService.unzipFile(unzipFileDto.getUserFileId(), unzipFileDto.getUnzipMode(), unzipFileDto.getFilePath());
+            UserFile userFile = userFileService.getById(unzipFileDto.getUserFileId());
+            operationLogUtil.getOperationLogObj(httpServletRequest, StpLoginUserUtil.getLoginUser().getId(), RestResult.success().getMessage(),CURRENT_MODULE,"解压文件",SUCCESS,unzipFileDto.getUserFileId(),"unzipCommonFile",userFile.getFileName());
+        } catch (QiwenException e) {
+            return RestResult.fail().message(e.getMessage());
+        }
+
+        return RestResult.success();
+
+    }
+
+    @Operation(summary = "文件复制", description = "可以复制文件或者目录", tags = {"file"})
+    @RequestMapping(value = "/copyfile", method = RequestMethod.POST)
+    @MyLog(operation = "文件复制", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult<String> copyFile(HttpServletRequest httpServletRequest,@RequestBody CopyFileDTO copyFileDTO) {
+        String userId = StpLoginUserUtil.getLoginUser().getId();
+        String filePath = copyFileDTO.getFilePath();
+        String userFileIds = copyFileDTO.getUserFileIds();
+        String[] userFileIdArr = userFileIds.split(",");
+        for (String userFileId : userFileIdArr) {
+            UserFile userFile = userFileService.getById(userFileId);
+            String oldfilePath = userFile.getFilePath();
+            String fileName = userFile.getFileName();
+            if (userFile.isDirectory()) {
+                QiwenFile qiwenFile = new QiwenFile(oldfilePath, fileName, true);
+                if (filePath.startsWith(qiwenFile.getPath() + QiwenFile.separator) || filePath.equals(qiwenFile.getPath())) {
+                    return RestResult.fail().message("原路径与目标路径冲突,不能复制");
+                }
+            }
+
+            userFileService.userFileCopy(userId, userFileId, filePath);
+            fileDealComp.deleteRepeatSubDirFile(filePath, userId);
+            operationLogUtil.getOperationLogObj(httpServletRequest, StpLoginUserUtil.getLoginUser().getId(), RestResult.success().getMessage(),CURRENT_MODULE,"文件复制",SUCCESS,userFileId,"copyfile",userFile.getFileName());
+
+        }
+
+        return RestResult.success();
+
+    }
+
+
+    @Operation(summary = "文件复制", description = "可以复制文件或者目录", tags = {"file"})
+    @RequestMapping(value = "/copyCommonfile", method = RequestMethod.POST)
+    @MyLog(operation = "文件复制", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult<String> copyCommonfile(HttpServletRequest httpServletRequest,@RequestBody CopyFileDTO copyFileDTO) {
+        String userId = commonUserId;
+        String filePath = copyFileDTO.getFilePath();
+        String userFileIds = copyFileDTO.getUserFileIds();
+        String[] userFileIdArr = userFileIds.split(",");
+        for (String userFileId : userFileIdArr) {
+            UserFile userFile = userFileService.getById(userFileId);
+            String oldfilePath = userFile.getFilePath();
+            String fileName = userFile.getFileName();
+            if (userFile.isDirectory()) {
+                QiwenFile qiwenFile = new QiwenFile(oldfilePath, fileName, true);
+                if (filePath.startsWith(qiwenFile.getPath() + QiwenFile.separator) || filePath.equals(qiwenFile.getPath())) {
+                    return RestResult.fail().message("原路径与目标路径冲突,不能复制");
+                }
+            }
+            operationLogUtil.getOperationLogObj(httpServletRequest, StpLoginUserUtil.getLoginUser().getId(), commonUserId,CURRENT_MODULE,"文件复制",SUCCESS,userFileId,"copyCommonfile",userFile.getFileName());
+
+            userFileService.userFileCopy(userId, userFileId, filePath);
+            fileDealComp.deleteRepeatSubDirFile(filePath, userId);
+        }
+
+        return RestResult.success();
+
+    }
+
+
+
+
+
+
+    @Operation(summary = "文件移动", description = "可以移动文件或者目录", tags = {"file"})
+    @RequestMapping(value = "/movefile", method = RequestMethod.POST)
+    @MyLog(operation = "文件移动", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult<String> moveFile(HttpServletRequest httpServletRequest,@RequestBody MoveFileDTO moveFileDto) {
+
+//        JwtUser sessionUserBean =  SessionUtil.getSession();
+        String userId = StpLoginUserUtil.getLoginUser().getId();
+        UserFile userFile = userFileService.getById(moveFileDto.getUserFileId());
+        String oldfilePath = userFile.getFilePath();
+        String newfilePath = moveFileDto.getFilePath();
+        String fileName = userFile.getFileName();
+        String extendName = userFile.getExtendName();
+        if (StringUtil.isEmpty(extendName)) {
+            QiwenFile qiwenFile = new QiwenFile(oldfilePath, fileName, true);
+            if (newfilePath.startsWith(qiwenFile.getPath() + QiwenFile.separator) || newfilePath.equals(qiwenFile.getPath())) {
+                return RestResult.fail().message("原路径与目标路径冲突,不能移动");
+            }
+        }
+
+        userFileService.updateFilepathByUserFileId(moveFileDto.getUserFileId(), newfilePath, userId);
+        operationLogUtil.getOperationLogObj(httpServletRequest, StpLoginUserUtil.getLoginUser().getId(), commonUserId,CURRENT_MODULE,"文件复制",SUCCESS,userFile.getUserFileId(),"movefile",userFile.getFileName());
+
+        fileDealComp.deleteRepeatSubDirFile(newfilePath, userId);
+        return RestResult.success();
+
+    }
+
+
+
+    @Operation(summary = "文件移动", description = "可以移动文件或者目录", tags = {"file"})
+    @RequestMapping(value = "/moveCommonFile", method = RequestMethod.POST)
+    @MyLog(operation = "文件移动", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult<String> moveCommonFile(HttpServletRequest httpServletRequest,@RequestBody MoveFileDTO moveFileDto) {
+
+//        JwtUser sessionUserBean =  SessionUtil.getSession();
+        String userId =commonUserId;
+        UserFile userFile = userFileService.getById(moveFileDto.getUserFileId());
+        String oldfilePath = userFile.getFilePath();
+        String newfilePath = moveFileDto.getFilePath();
+        String fileName = userFile.getFileName();
+        String extendName = userFile.getExtendName();
+        if (StringUtil.isEmpty(extendName)) {
+            QiwenFile qiwenFile = new QiwenFile(oldfilePath, fileName, true);
+            if (newfilePath.startsWith(qiwenFile.getPath() + QiwenFile.separator) || newfilePath.equals(qiwenFile.getPath())) {
+                return RestResult.fail().message("原路径与目标路径冲突,不能移动");
+            }
+        }
+
+        userFileService.updateFilepathByUserFileId(moveFileDto.getUserFileId(), newfilePath, userId);
+
+        fileDealComp.deleteRepeatSubDirFile(newfilePath, userId);
+        operationLogUtil.getOperationLogObj(httpServletRequest, StpLoginUserUtil.getLoginUser().getId(), commonUserId,CURRENT_MODULE,"文件移动",SUCCESS,userFile.getUserFileId(),"moveCommonFile",userFile.getFileName());
+        return RestResult.success();
+
+    }
+
+
+
+
+
+
+    @Operation(summary = "批量移动文件", description = "可以同时选择移动多个文件或者目录", tags = {"file"})
+    @RequestMapping(value = "/batchmovefile", method = RequestMethod.POST)
+    @MyLog(operation = "批量移动文件", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult<String> batchMoveFile(HttpServletRequest httpServletRequest,@RequestBody BatchMoveFileDTO batchMoveFileDto) {
+
+//        JwtUser sessionUserBean =  SessionUtil.getSession();
+        String userId = StpLoginUserUtil.getLoginUser().getId();
+
+        String newfilePath = batchMoveFileDto.getFilePath();
+
+        String userFileIds = batchMoveFileDto.getUserFileIds();
+        String[] userFileIdArr = userFileIds.split(",");
+
+        for (String userFileId : userFileIdArr) {
+            UserFile userFile = userFileService.getById(userFileId);
+            if (StringUtil.isEmpty(userFile.getExtendName())) {
+                QiwenFile qiwenFile = new QiwenFile(userFile.getFilePath(), userFile.getFileName(), true);
+                if (newfilePath.startsWith(qiwenFile.getPath() + QiwenFile.separator) || newfilePath.equals(qiwenFile.getPath())) {
+                    return RestResult.fail().message("原路径与目标路径冲突,不能移动");
+                }
+            }
+            userFileService.updateFilepathByUserFileId(userFile.getUserFileId(), newfilePath, userId);
+            operationLogUtil.getOperationLogObj(httpServletRequest, StpLoginUserUtil.getLoginUser().getId(), commonUserId,CURRENT_MODULE,"文件移动",SUCCESS,userFile.getUserFileId(),"batchmovefile",userFile.getFileName());
+
+        }
+
+        return RestResult.success().data("批量移动文件成功");
+
+    }
+
+
+
+
+    @Operation(summary = "批量移动文件", description = "可以同时选择移动多个文件或者目录", tags = {"file"})
+    @RequestMapping(value = "/batchmoveCommonFile", method = RequestMethod.POST)
+    @MyLog(operation = "批量移动文件", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult<String> batchmoveCommonFile(HttpServletRequest httpServletRequest,@RequestBody BatchMoveFileDTO batchMoveFileDto) {
+
+        String userId =commonUserId;
+
+        String newfilePath = batchMoveFileDto.getFilePath();
+
+        String userFileIds = batchMoveFileDto.getUserFileIds();
+        String[] userFileIdArr = userFileIds.split(",");
+
+        for (String userFileId : userFileIdArr) {
+            UserFile userFile = userFileService.getById(userFileId);
+            if (StringUtil.isEmpty(userFile.getExtendName())) {
+                QiwenFile qiwenFile = new QiwenFile(userFile.getFilePath(), userFile.getFileName(), true);
+                if (newfilePath.startsWith(qiwenFile.getPath() + QiwenFile.separator) || newfilePath.equals(qiwenFile.getPath())) {
+                    return RestResult.fail().message("原路径与目标路径冲突,不能移动");
+                }
+            }
+            userFileService.updateFilepathByUserFileId(userFile.getUserFileId(), newfilePath, userId);
+            operationLogUtil.getOperationLogObj(httpServletRequest, StpLoginUserUtil.getLoginUser().getId(), commonUserId,CURRENT_MODULE,"批量移动文件",SUCCESS,userFile.getUserFileId(),"batchmoveCommonFile",userFile.getFileName());
+
+        }
+
+        return RestResult.success().data("批量移动文件成功");
+
+    }
+
+
+
+    @Operation(summary = "获取文件树", description = "文件移动的时候需要用到该接口,用来展示目录树", tags = {"file"})
+    @RequestMapping(value = "/getfiletree", method = RequestMethod.GET)
+    @ResponseBody
+    public RestResult<TreeNode> getFileTree() {
+        RestResult<TreeNode> result = new RestResult<TreeNode>();
+
+//        JwtUser sessionUserBean =  SessionUtil.getSession();
+        String userId = StpLoginUserUtil.getLoginUser().getId();
+        List<UserFile> userFileList = userFileService.selectFilePathTreeByUserId(userId);
+        TreeNode resultTreeNode = new TreeNode();
+        resultTreeNode.setLabel(QiwenFile.separator);
+        resultTreeNode.setId(0L);
+        long id = 1;
+        for (int i = 0; i < userFileList.size(); i++){
+            UserFile userFile = userFileList.get(i);
+            QiwenFile qiwenFile = new QiwenFile(userFile.getFilePath(), userFile.getFileName(), false);
+            String filePath = qiwenFile.getPath();
+
+            Queue<String> queue = new LinkedList<>();
+
+            String[] strArr = filePath.split(QiwenFile.separator);
+            for (int j = 0; j < strArr.length; j++){
+                if (!"".equals(strArr[j]) && strArr[j] != null){
+                    queue.add(strArr[j]);
+                }
+
+            }
+            if (queue.size() == 0){
+                continue;
+            }
+
+            resultTreeNode = fileDealComp.insertTreeNode(resultTreeNode, id++, QiwenFile.separator, queue);
+
+
+        }
+        List<TreeNode> treeNodeList = resultTreeNode.getChildren();
+        Collections.sort(treeNodeList, (o1, o2) -> {
+            long i = o1.getId() - o2.getId();
+            return (int) i;
+        });
+        result.setSuccess(true);
+        result.setData(resultTreeNode);
+        return result;
+
+    }
+
+
+
+
+
+
+    @Operation(summary = "获取文件树", description = "文件移动的时候需要用到该接口,用来展示目录树", tags = {"file"})
+    @RequestMapping(value = "/getCommonFileTree", method = RequestMethod.GET)
+    @ResponseBody
+    public RestResult<TreeNode> getCommonFileTree() {
+        RestResult<TreeNode> result = new RestResult<TreeNode>();
+
+        String userId = commonUserId;
+        List<UserFile> userFileList = userFileService.selectFilePathTreeByUserId(userId);
+        TreeNode resultTreeNode = new TreeNode();
+        resultTreeNode.setLabel(QiwenFile.separator);
+        resultTreeNode.setId(0L);
+        long id = 1;
+        for (int i = 0; i < userFileList.size(); i++){
+            UserFile userFile = userFileList.get(i);
+            QiwenFile qiwenFile = new QiwenFile(userFile.getFilePath(), userFile.getFileName(), false);
+            String filePath = qiwenFile.getPath();
+
+            Queue<String> queue = new LinkedList<>();
+
+            String[] strArr = filePath.split(QiwenFile.separator);
+            for (int j = 0; j < strArr.length; j++){
+                if (!"".equals(strArr[j]) && strArr[j] != null){
+                    queue.add(strArr[j]);
+                }
+
+            }
+            if (queue.size() == 0){
+                continue;
+            }
+
+            resultTreeNode = fileDealComp.insertTreeNode(resultTreeNode, id++, QiwenFile.separator, queue);
+
+
+        }
+        List<TreeNode> treeNodeList = resultTreeNode.getChildren();
+        Collections.sort(treeNodeList, (o1, o2) -> {
+            long i = o1.getId() - o2.getId();
+            return (int) i;
+        });
+        result.setSuccess(true);
+        result.setData(resultTreeNode);
+        return result;
+
+    }
+
+
+
+
+
+
+    @Operation(summary = "修改文件", description = "支持普通文本类文件的修改", tags = {"file"})
+    @RequestMapping(value = "/update", method = RequestMethod.POST)
+    @ResponseBody
+    public RestResult<String> updateFile(@RequestBody UpdateFileDTO updateFileDTO) {
+//        JwtUser sessionUserBean =  SessionUtil.getSession();
+        String userId = StpLoginUserUtil.getLoginUser().getId();
+        UserFile userFile = userFileService.getById(updateFileDTO.getUserFileId());
+        FileBean fileBean = fileService.getById(userFile.getFileId());
+        Long pointCount = fileService.getFilePointCount(userFile.getFileId());
+        String fileUrl = fileBean.getFileUrl();
+        if (pointCount > 1) {
+            fileUrl = fileDealComp.copyFile(fileBean, userFile);
+        }
+        String content = updateFileDTO.getFileContent();
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(content.getBytes());
+        try {
+            int fileSize = byteArrayInputStream.available();
+            fileDealComp.saveFileInputStream(fileBean.getStorageType(), fileUrl, byteArrayInputStream);
+
+            String md5Str = fileDealComp.getIdentifierByFile(fileUrl, fileBean.getStorageType());
+
+            fileService.updateFileDetail(userFile.getUserFileId(), md5Str, fileSize);
+
+
+        } catch (Exception e) {
+            throw new QiwenException(999999, "修改文件异常");
+        } finally {
+            try {
+                byteArrayInputStream.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        return RestResult.success().message("修改文件成功");
+    }
+
+
+
+    @Operation(summary = "修改文件", description = "支持普通文本类文件的修改", tags = {"file"})
+    @RequestMapping(value = "/updateCommon", method = RequestMethod.POST)
+    @ResponseBody
+    public RestResult<String> updateCommon(@RequestBody UpdateFileDTO updateFileDTO) {
+//        JwtUser sessionUserBean =  SessionUtil.getSession();
+        String userId = commonUserId;
+        UserFile userFile = userFileService.getById(updateFileDTO.getUserFileId());
+        FileBean fileBean = fileService.getById(userFile.getFileId());
+        Long pointCount = fileService.getFilePointCount(userFile.getFileId());
+        String fileUrl = fileBean.getFileUrl();
+        if (pointCount > 1) {
+            fileUrl = fileDealComp.copyFile(fileBean, userFile);
+        }
+        String content = updateFileDTO.getFileContent();
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(content.getBytes());
+        try {
+            int fileSize = byteArrayInputStream.available();
+            fileDealComp.saveFileInputStream(fileBean.getStorageType(), fileUrl, byteArrayInputStream);
+
+            String md5Str = fileDealComp.getIdentifierByFile(fileUrl, fileBean.getStorageType());
+
+            fileService.updateFileDetail(userFile.getUserFileId(), md5Str, fileSize);
+
+
+        } catch (Exception e) {
+            throw new QiwenException(999999, "修改文件异常");
+        } finally {
+            try {
+                byteArrayInputStream.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        return RestResult.success().message("修改文件成功");
+    }
+
+
+
+
+
+    @Operation(summary = "查询文件详情", description = "查询文件详情", tags = {"file"})
+    @RequestMapping(value = "/detail", method = RequestMethod.GET)
+    @ResponseBody
+    public RestResult<FileDetailVO> queryFileDetail(
+            @Parameter(description = "用户文件Id", required = true) String userFileId){
+        FileDetailVO vo = fileService.getFileDetail(userFileId);
+        return RestResult.success().data(vo);
+    }
+
+
+
+    @Operation(summary = "查询文件详情", description = "查询文件详情", tags = {"file"})
+    @RequestMapping(value = "/detailCommon", method = RequestMethod.GET)
+    @ResponseBody
+    public RestResult<FileDetailVO> detailCommon(
+            @Parameter(description = "用户文件Id", required = true) String userFileId){
+        FileDetailVO vo = fileService.getFileDetail(userFileId);
+        return RestResult.success().data(vo);
+    }
+
+
+    @Operation(summary = "m3u8格式转换测试", description = "m3u8格式转换测试", tags = {"file"})
+    @RequestMapping(value = "/m3u8Upload", method = RequestMethod.GET)
+    @ResponseBody
+    public RestResult<FileDetailVO> m3u8Upload(
+            @Parameter(description = "视频文件", required = true) MultipartFile file){
+        return RestResult.success().data(m3u8UploadService.m3u8Upload(file));
+    }
+}

+ 35 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/FileStasticController.java

@@ -0,0 +1,35 @@
+package vip.xiaonuo.disk.controller;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.disk.service.FileStasticService;
+
+import javax.annotation.Resource;
+import java.util.Map;
+
+@Tag(name = "operationLog", description = "资源概览接口")
+@RestController
+@Slf4j
+@RequestMapping("/fileStastic")
+public class FileStasticController {
+
+   @Resource
+   private FileStasticService fileStasticService;
+
+
+    @Operation(summary = "资源概览存储空间统计接口", description = "资源概览存储空间统计接口")
+    @RequestMapping(value = "/queryList", method = RequestMethod.GET)
+    @ResponseBody
+    public CommonResult<Map<String, Object>> queryList() {
+        Map<String, Object> map= fileStasticService.queryList();
+        return CommonResult.data(map);
+    }
+
+
+}

+ 151 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/GradesController.java

@@ -0,0 +1,151 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.disk.domain.CourseRelate;
+import vip.xiaonuo.disk.domain.Grades;
+import vip.xiaonuo.disk.param.grades.GradesAddParam;
+import vip.xiaonuo.disk.param.grades.GradesEditParam;
+import vip.xiaonuo.disk.param.grades.GradesIdParam;
+import vip.xiaonuo.disk.param.grades.GradesPageParam;
+import vip.xiaonuo.disk.service.GradesService;
+import vip.xiaonuo.disk.vo.grades.GradesVo;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * grades控制器
+ *
+ * @author pans
+ * @date  2025/07/24 18:33
+ */
+@Api(tags = "grades控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class GradesController {
+
+    @Resource
+    private GradesService gradesService;
+
+    /**
+     * 获取grades分页
+     *
+     * @author pans
+     * @date  2025/07/24 18:33
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取grades分页")
+    @GetMapping("/disk/grades/page")
+    public CommonResult<Page<GradesVo>> page(GradesPageParam gradesPageParam) {
+        return CommonResult.data(gradesService.page(gradesPageParam));
+    }
+
+    /**
+     * 添加grades
+     *
+     * @author pans
+     * @date  2025/07/24 18:33
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加grades")
+    @CommonLog("添加grades")
+    @PostMapping("/disk/grades/add")
+    public CommonResult<String> add(@RequestBody @Valid GradesAddParam gradesAddParam) {
+        gradesService.add(gradesAddParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑grades
+     *
+     * @author pans
+     * @date  2025/07/24 18:33
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑grades")
+    @CommonLog("编辑grades")
+    @PostMapping("/disk/grades/edit")
+    public CommonResult<String> edit(@RequestBody @Valid GradesEditParam gradesEditParam) {
+        gradesService.edit(gradesEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除grades
+     *
+     * @author pans
+     * @date  2025/07/24 18:33
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除grades")
+    @CommonLog("删除grades")
+    @PostMapping("/disk/grades/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<GradesIdParam> gradesIdParamList) {
+        gradesService.delete(gradesIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取grades详情
+     *
+     * @author pans
+     * @date  2025/07/24 18:33
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取grades详情")
+    @GetMapping("/disk/grades/detail")
+    public CommonResult<GradesVo> detail(@Valid GradesIdParam gradesIdParam) {
+        return CommonResult.data(gradesService.detail(gradesIdParam));
+    }
+
+
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取班级列表")
+    @GetMapping("/disk/grades/queryList")
+    public CommonResult<List<GradesVo>> queryList(GradesPageParam gradesPageParam) {
+        return CommonResult.data(gradesService.queryList(gradesPageParam));
+    }
+
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取班级-全量列表")
+    @GetMapping("/disk/grades/allList")
+    public CommonResult<List<Grades>> allList(GradesPageParam gradesPageParam) {
+        Map param =new HashMap();
+        param.put("gradesName", gradesPageParam.getGradesName());
+        param.put("collegeId", gradesPageParam.getCollegeId());
+        param.put("majorId", gradesPageParam.getMajorId());
+        List<Grades> list=gradesService.wrapperList(param);
+        return CommonResult.data(list);
+    }
+
+}

+ 136 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/KeyWordController.java

@@ -0,0 +1,136 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.disk.domain.KeyWord;
+import vip.xiaonuo.disk.param.KeyWordAddParam;
+import vip.xiaonuo.disk.param.KeyWordEditParam;
+import vip.xiaonuo.disk.param.KeyWordIdParam;
+import vip.xiaonuo.disk.param.KeyWordPageParam;
+import vip.xiaonuo.disk.service.KeyWordService;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import java.util.List;
+
+/**
+ * key_word控制器
+ *
+ * @author pans
+ * @date  2025/06/27 10:08
+ */
+@Api(tags = "key_word控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class KeyWordController {
+
+    @Resource
+    private KeyWordService keyWordService;
+
+    /**
+     * 获取key_word分页
+     *
+     * @author pans
+     * @date  2025/06/27 10:08
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取key_word分页")
+    @GetMapping("/disk/word/page")
+    public CommonResult<Page<KeyWord>> page(KeyWordPageParam keyWordPageParam) {
+        return CommonResult.data(keyWordService.page(keyWordPageParam));
+    }
+
+    /**
+     * 添加key_word
+     *
+     * @author pans
+     * @date  2025/06/27 10:08
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加key_word")
+    @CommonLog("添加key_word")
+    @PostMapping("/disk/word/add")
+    public CommonResult<String> add(@RequestBody @Valid KeyWordAddParam keyWordAddParam) {
+        keyWordService.add(keyWordAddParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑key_word
+     *
+     * @author pans
+     * @date  2025/06/27 10:08
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑key_word")
+    @CommonLog("编辑key_word")
+    @PostMapping("/disk/word/edit")
+    public CommonResult<String> edit(@RequestBody @Valid KeyWordEditParam keyWordEditParam) {
+        keyWordService.edit(keyWordEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除key_word
+     *
+     * @author pans
+     * @date  2025/06/27 10:08
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除key_word")
+    @CommonLog("删除key_word")
+    @PostMapping("/disk/word/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<KeyWordIdParam> keyWordIdParamList) {
+        keyWordService.delete(keyWordIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取key_word详情
+     *
+     * @author pans
+     * @date  2025/06/27 10:08
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取key_word详情")
+    @GetMapping("/disk/word/detail")
+    public CommonResult<KeyWord> detail(@Valid KeyWordIdParam keyWordIdParam) {
+        return CommonResult.data(keyWordService.detail(keyWordIdParam));
+    }
+
+    @ApiOperationSupport(order = 6)
+    @GetMapping("/disk/word/select")
+    public CommonResult<List<KeyWord>> select(String popular) {
+        return CommonResult.data(keyWordService.select(popular));
+    }
+
+
+
+
+}

+ 149 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/MajorController.java

@@ -0,0 +1,149 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.disk.domain.Major;
+import vip.xiaonuo.disk.param.MajorAddParam;
+import vip.xiaonuo.disk.param.MajorEditParam;
+import vip.xiaonuo.disk.param.MajorIdParam;
+import vip.xiaonuo.disk.param.MajorPageParam;
+import vip.xiaonuo.disk.service.MajorService;
+import vip.xiaonuo.disk.vo.major.MajorVo;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * major控制器
+ *
+ * @author pans
+ * @date  2025/06/26 10:41
+ */
+@Api(tags = "major控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class MajorController {
+
+    @Resource
+    private MajorService majorService;
+
+    /**
+     * 获取major分页
+     *
+     * @author pans
+     * @date  2025/06/26 10:41
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取major分页")
+    @GetMapping("/disk/major/page")
+    public CommonResult<Page<MajorVo>> page(MajorPageParam majorPageParam) {
+        return CommonResult.data(majorService.page(majorPageParam));
+    }
+
+    /**
+     * 添加major
+     *
+     * @author pans
+     * @date  2025/06/26 10:41
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加major")
+    @CommonLog("添加major")
+    @PostMapping("/disk/major/add")
+    public CommonResult<String> add(@RequestBody @Valid MajorAddParam majorAddParam) {
+        majorService.add(majorAddParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑major
+     *
+     * @author pans
+     * @date  2025/06/26 10:41
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑major")
+    @CommonLog("编辑major")
+    @PostMapping("/disk/major/edit")
+    public CommonResult<String> edit(@RequestBody @Valid MajorEditParam majorEditParam) {
+        majorService.edit(majorEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除major
+     *
+     * @author pans
+     * @date  2025/06/26 10:41
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除major")
+    @CommonLog("删除major")
+    @PostMapping("/disk/major/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<MajorIdParam> majorIdParamList) {
+        majorService.delete(majorIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取major详情
+     *
+     * @author pans
+     * @date  2025/06/26 10:41
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取major详情")
+    @GetMapping("/disk/major/detail")
+    public CommonResult<MajorVo> detail(@Valid MajorIdParam majorIdParam) {
+        return CommonResult.data(majorService.detail(majorIdParam));
+    }
+
+
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取major详情")
+    @GetMapping("/disk/major/select")
+    public CommonResult<List<MajorVo>> select(@RequestParam(value = "collegeId",required = true) String collegeId) {
+        return CommonResult.data(majorService.select(collegeId));
+    }
+
+    /**
+     * 获取专业-下拉列表
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("专业-下拉列表")
+    @GetMapping("/disk/major/downList")
+    public CommonResult<List<Major>> downList(MajorPageParam majorPageParam, HttpServletRequest req) {
+        Map param=new HashMap();
+        param.put("collegeId", req.getParameter("collegeId"));
+        List<Major> majorList=majorService.wrapperList(param);
+        return CommonResult.data(majorList);
+    }
+
+}

+ 126 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/MaterrialLibraryController.java

@@ -0,0 +1,126 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.disk.param.MaterrialLibraryAddParam;
+import vip.xiaonuo.disk.param.MaterrialLibraryEditParam;
+import vip.xiaonuo.disk.param.MaterrialLibraryIdParam;
+import vip.xiaonuo.disk.param.MaterrialLibraryPageParam;
+import vip.xiaonuo.disk.service.MaterrialLibraryService;
+import vip.xiaonuo.disk.vo.materrial.MaterrialLibraryVo;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+
+/**
+ * 素材库控制器
+ *
+ * @author pans
+ * @date  2025/07/01 16:16
+ */
+@Api(tags = "素材库控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class MaterrialLibraryController {
+
+    @Resource
+    private MaterrialLibraryService materrialLibraryService;
+
+    /**
+     * 获取素材库分页
+     *
+     * @author pans
+     * @date  2025/07/01 16:16
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取素材库分页")
+    @GetMapping("/disk/library/page")
+    public CommonResult<Page<MaterrialLibraryVo>> page(MaterrialLibraryPageParam materrialLibraryPageParam) {
+        return CommonResult.data(materrialLibraryService.page(materrialLibraryPageParam));
+    }
+
+    /**
+     * 添加素材库
+     *
+     * @author pans
+     * @date  2025/07/01 16:16
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加素材库")
+    @CommonLog("添加素材库")
+    @PostMapping("/disk/library/add")
+    public CommonResult<String> add(@RequestBody @Valid MaterrialLibraryAddParam materrialLibraryAddParam) {
+        materrialLibraryService.add(materrialLibraryAddParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑素材库
+     *
+     * @author pans
+     * @date  2025/07/01 16:16
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑素材库")
+    @CommonLog("编辑素材库")
+    @PostMapping("/disk/library/edit")
+    public CommonResult<String> edit(@RequestBody @Valid MaterrialLibraryEditParam materrialLibraryEditParam) {
+        materrialLibraryService.edit(materrialLibraryEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除素材库
+     *
+     * @author pans
+     * @date  2025/07/01 16:16
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除素材库")
+    @CommonLog("删除素材库")
+    @PostMapping("/disk/library/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<MaterrialLibraryIdParam> materrialLibraryIdParamList) {
+        materrialLibraryService.delete(materrialLibraryIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取素材库详情
+     *
+     * @author pans
+     * @date  2025/07/01 16:16
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取素材库详情")
+    @GetMapping("/disk/library/detail")
+    public CommonResult<MaterrialLibraryVo> detail(@Valid MaterrialLibraryIdParam materrialLibraryIdParam) {
+        return CommonResult.data(materrialLibraryService.detail(materrialLibraryIdParam));
+    }
+
+}

+ 63 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/MinioController.java

@@ -0,0 +1,63 @@
+package vip.xiaonuo.disk.controller;
+
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+import vip.xiaonuo.disk.domain.UploadFile;
+import vip.xiaonuo.disk.service.MinioService;
+import vip.xiaonuo.disk.util.MinioUtil;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author 吴浩炼
+ * @since 2024/05/29
+ */
+@RestController
+@RequestMapping("/minio")
+public class MinioController {
+
+    @Autowired
+    private MinioService minioService;
+
+    @Autowired
+    private MinioUtil minioUtil;
+
+    Logger logger = LoggerFactory.getLogger(MinioUtil.class);
+
+
+    @GetMapping("/checkExits")
+    public ResponseEntity<Boolean> checkExits(@RequestParam String md5, @RequestParam String fileSuffix) {
+        return ResponseEntity.ok(minioUtil.checkExist(md5, fileSuffix));
+    }
+
+    @PostMapping("/upload")
+    public ResponseEntity<Boolean> upload(String md5,@RequestParam Integer chunkIndex,@RequestParam MultipartFile chunk)
+            throws IOException {
+        return ResponseEntity.ok(minioUtil.upload(md5, chunkIndex, chunk.getInputStream(), chunk.getSize()));
+    }
+
+    @PostMapping("/merge")
+    public ResponseEntity<Map<String,Object>> merge(@RequestParam String md5, @RequestParam Integer chunkTotal, @RequestParam String fileSuffix, @RequestParam String fileName, @RequestParam long fileSize, @RequestParam String affiliationFuncType) {
+        return ResponseEntity.ok(minioUtil.merge(md5, chunkTotal, fileSuffix,fileSize,fileName,affiliationFuncType));
+    }
+
+
+
+    @GetMapping("/delete")
+    public ResponseEntity<Boolean> delete(@RequestParam String url) {
+        System.out.println("url34:" + url);
+        return ResponseEntity.ok(minioUtil.deleteFileLink(url));
+    }
+
+    @PostMapping("/checkMd5List")
+    public ResponseEntity<?> checkFilesExits(@RequestBody List<UploadFile> genericItems){
+        return minioUtil.checkFilesExits(genericItems);
+    }
+}

+ 179 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/NoticeController.java

@@ -0,0 +1,179 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.disk.param.notice.NoticeAddParam;
+import vip.xiaonuo.disk.param.notice.NoticeEditParam;
+import vip.xiaonuo.disk.param.notice.NoticeIdParam;
+import vip.xiaonuo.disk.param.notice.NoticePageParam;
+import vip.xiaonuo.disk.service.NoticeService;
+import vip.xiaonuo.disk.vo.notice.NoticeVo;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+
+/**
+ * notice控制器
+ *
+ * @author pans
+ * @date  2025/07/25 16:17
+ */
+@Api(tags = "notice控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class NoticeController {
+
+    @Resource
+    private NoticeService noticeService;
+
+    /**
+     * 获取notice分页
+     *
+     * @author pans
+     * @date  2025/07/25 16:17
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取notice分页")
+    @GetMapping("/disk/notice/page")
+    public CommonResult<Page<NoticeVo>> page(NoticePageParam noticePageParam) {
+        return CommonResult.data(noticeService.page(noticePageParam));
+    }
+
+
+
+
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取notice分页")
+    @GetMapping("/disk/notice/readPage")
+    public CommonResult<Page<NoticeVo>> readPage(NoticePageParam noticePageParam) {
+        return CommonResult.data(noticeService.readPage(noticePageParam));
+    }
+
+    /**
+     * 添加notice
+     *
+     * @author pans
+     * @date  2025/07/25 16:17
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加notice")
+    @CommonLog("添加notice")
+    @PostMapping("/disk/notice/add")
+    public CommonResult<String> add(@RequestBody @Valid NoticeAddParam noticeAddParam) {
+        noticeService.add(noticeAddParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑notice
+     *
+     * @author pans
+     * @date  2025/07/25 16:17
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑notice")
+    @CommonLog("编辑notice")
+    @PostMapping("/disk/notice/edit")
+    public CommonResult<String> edit(@RequestBody @Valid NoticeEditParam noticeEditParam) {
+        noticeService.edit(noticeEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除notice
+     *
+     * @author pans
+     * @date  2025/07/25 16:17
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除notice")
+    @CommonLog("删除notice")
+    @PostMapping("/disk/notice/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<NoticeIdParam> noticeIdParamList) {
+        noticeService.delete(noticeIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取notice详情
+     *
+     * @author pans
+     * @date  2025/07/25 16:17
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取notice详情")
+    @GetMapping("/disk/notice/detail")
+    public CommonResult<NoticeVo> detail(@Valid NoticeIdParam noticeIdParam) {
+        return CommonResult.data(noticeService.detail(noticeIdParam));
+    }
+
+
+    /**
+     * 获取notice详情
+     *
+     * @author pans
+     * @date  2025/07/25 16:17
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取notice详情")
+    @GetMapping("/disk/notice/readDetail")
+    public CommonResult<NoticeVo> readDetail(@Valid NoticeIdParam noticeIdParam) {
+        return CommonResult.data(noticeService.readDetail(noticeIdParam));
+    }
+
+
+
+
+    /**
+     * 发布
+     *
+     * @author pans
+     * @date  2025/07/25 16:17
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("发布")
+    @GetMapping("/disk/notice/publish")
+    public CommonResult<?> publish(@Valid NoticeIdParam noticeIdParam) {
+        noticeService.publish(noticeIdParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 取消发布
+     * @author pans
+     * @date  2025/07/25 16:17
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("取消发布")
+    @GetMapping("/disk/notice/cancel")
+    public CommonResult<?> cancel(@Valid NoticeIdParam noticeIdParam) {
+        noticeService.cancel(noticeIdParam);
+        return CommonResult.ok();
+    }
+}

+ 240 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/OfficeController.java

@@ -0,0 +1,240 @@
+package vip.xiaonuo.disk.controller;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.qiwenshare.common.exception.NotLoginException;
+import com.qiwenshare.common.result.RestResult;
+import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
+import vip.xiaonuo.disk.service.IFileService;
+import vip.xiaonuo.disk.service.IUserFileService;
+import vip.xiaonuo.disk.service.IUserService;
+import vip.xiaonuo.disk.component.FileDealComp;
+import vip.xiaonuo.disk.domain.FileBean;
+import vip.xiaonuo.disk.domain.UserFile;
+import vip.xiaonuo.disk.domain.user.UserBean;
+import vip.xiaonuo.disk.dto.file.EditOfficeFileDTO;
+import vip.xiaonuo.disk.dto.file.PreviewOfficeFileDTO;
+import vip.xiaonuo.disk.office.documentserver.managers.history.HistoryManager;
+import vip.xiaonuo.disk.office.documentserver.models.enums.Action;
+import vip.xiaonuo.disk.office.documentserver.models.enums.Type;
+import vip.xiaonuo.disk.office.documentserver.models.filemodel.FileModel;
+import vip.xiaonuo.disk.office.entities.User;
+import vip.xiaonuo.disk.office.services.configurers.FileConfigurer;
+import vip.xiaonuo.disk.office.services.configurers.wrappers.DefaultFileWrapper;
+import com.qiwenshare.ufop.factory.UFOPFactory;
+import com.qiwenshare.ufop.operation.download.domain.DownloadFile;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Locale;
+import java.util.Scanner;
+
+@Tag(name = "office", description = "该接口为Onlyoffice文件操作接口,主要用来做一些文档的编辑,浏览等。")
+@RestController
+@Slf4j
+@RequestMapping({"/office"})
+public class OfficeController {
+    public static final String CURRENT_MODULE = "Onlyoffice文件操作接口";
+    @Resource
+    IUserService userService;
+    @Resource
+    UFOPFactory ufopFactory;
+    @Resource
+    FileDealComp fileDealComp;
+    @Value("${deployment.host}")
+    private String deploymentHost;
+    @Value("${server.port}")
+    private String port;
+    @Value("${ufop.storage-type}")
+    private Integer storageType;
+
+    @Value("${files.docservice.url.site}")
+    private String docserviceSite;
+
+    @Value("${files.docservice.url.api}")
+    private String docserviceApiUrl;
+    @Autowired
+    private FileConfigurer<DefaultFileWrapper> fileConfigurer;
+
+    @Resource
+    IFileService fileService;
+    @Resource
+    IUserFileService userFileService;
+    @Autowired
+    private HistoryManager historyManager;
+
+    @Operation(summary = "预览office文件", description = "预览office文件", tags = {"office"})
+    @RequestMapping(value = "/previewofficefile", method = RequestMethod.POST)
+    @ResponseBody
+    public RestResult<Object> previewOfficeFile(HttpServletRequest request, @RequestBody PreviewOfficeFileDTO previewOfficeFileDTO) {
+        RestResult<Object> result = new RestResult<>();
+        try {
+            String token = request.getHeader("token");
+            String previewUrl = request.getScheme() + "://" + deploymentHost + ":" + port + "/filetransfer/preview?userFileId=" + previewOfficeFileDTO.getUserFileId() + "&isMin=false&shareBatchNum=undefined&extractionCode=undefined&token=" + token;
+//            JwtUser loginUser = SessionUtil.getSession();
+            String userId = StpLoginUserUtil.getLoginUser().getId();
+            UserFile userFile = userFileService.getById(previewOfficeFileDTO.getUserFileId());
+
+
+
+            UserBean userBean = userService.getById(userId);
+            User user = new User(userBean);
+
+            Action action = Action.view;
+            Type type = Type.desktop;
+            Locale locale = new Locale("zh");
+            FileModel fileModel = fileConfigurer.getFileModel(
+                    DefaultFileWrapper
+                            .builder()
+                            .userFile(userFile)
+                            .type(type)
+                            .lang(locale.toLanguageTag())
+                            .action(action)
+                            .user(user)
+                            .actionData(previewUrl)
+                            .build()
+            );
+
+            JSONObject jsonObject = new JSONObject();
+            jsonObject.put("file",fileModel);
+//            jsonObject.put("fileHistory", historyManager.getHistory(fileModel.getDocument()));  // get file history and add it to the model
+            jsonObject.put("docserviceApiUrl", docserviceSite + docserviceApiUrl);
+            jsonObject.put("reportName",userFile.getFileName());
+            result.setData(jsonObject);
+            result.setCode(200);
+            result.setMessage("获取报告成功!");
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            result.setCode(500);
+            result.setMessage("服务器错误!");
+        }
+        return result;
+    }
+    @Operation(summary = "编辑office文件", description = "编辑office文件", tags = {"office"})
+    @ResponseBody
+    @RequestMapping(value = "/editofficefile", method = RequestMethod.POST)
+    public RestResult<Object> editOfficeFile(HttpServletRequest request, @RequestBody EditOfficeFileDTO editOfficeFileDTO) {
+        RestResult<Object> result = new RestResult<>();
+        String token = request.getHeader("token");
+        String previewUrl = request.getScheme() + "://" + deploymentHost + ":" + port + "/filetransfer/preview?userFileId=" + editOfficeFileDTO.getUserFileId() + "&isMin=false&shareBatchNum=undefined&extractionCode=undefined&token=" + token;
+        log.info("editOfficeFile");
+        try {
+//            JwtUser loginUser = SessionUtil.getSession();
+            String userId = StpLoginUserUtil.getLoginUser().getId();
+            UserFile userFile = userFileService.getById(editOfficeFileDTO.getUserFileId());
+
+
+            UserBean userBean = userService.getById(userId);
+            User user = new User(userBean);
+
+            Action action = Action.edit;
+            Type type = Type.desktop;
+            Locale locale = new Locale("zh");
+            FileModel fileModel = fileConfigurer.getFileModel(
+                    DefaultFileWrapper
+                            .builder()
+                            .userFile(userFile)
+                            .type(type)
+                            .lang(locale.toLanguageTag())
+                            .action(action)
+                            .user(user)
+                            .actionData(previewUrl)
+                            .build()
+            );
+            JSONObject jsonObject = new JSONObject();
+            jsonObject.put("file",fileModel);
+            jsonObject.put("docserviceApiUrl", docserviceSite + docserviceApiUrl);
+            jsonObject.put("reportName",userFile.getFileName());
+            result.setData(jsonObject);
+            result.setCode(200);
+            result.setMessage("编辑报告成功!");
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            result.setCode(500);
+            result.setMessage("服务器错误!");
+        }
+        return result;
+    }
+
+
+    @RequestMapping(value = "/IndexServlet", method = RequestMethod.POST)
+    @ResponseBody
+    public void IndexServlet(HttpServletResponse response, HttpServletRequest request) throws IOException {
+        String token = request.getParameter("token");
+//        String userId = userService.getUserIdByToken(token);
+        String userId = StpLoginUserUtil.getLoginUser().getId();
+        if (StringUtils.isEmpty(userId)) {
+            throw new NotLoginException();
+        }
+
+        PrintWriter writer = response.getWriter();
+        Scanner scanner = new Scanner(request.getInputStream()).useDelimiter("\\A");
+        String body = scanner.hasNext() ? scanner.next() : "";
+
+        JSONObject jsonObj = JSON.parseObject(body);
+        log.info("===saveeditedfile:" + jsonObj.get("status")); ;
+        String status = jsonObj != null ? jsonObj.get("status").toString() : "";
+        if ("2".equals(status) || "6".equals(status)) {
+            String type = request.getParameter("type");
+            String downloadUri = (String) jsonObj.get("url");
+
+            if("edit".equals(type)){ //修改报告
+                String userFileId = request.getParameter("userFileId");
+                UserFile userFile = userFileService.getById(userFileId);
+                FileBean fileBean = fileService.getById(userFile.getFileId());
+                Long pointCount = fileService.getFilePointCount(userFile.getFileId());
+                String fileUrl = fileBean.getFileUrl();
+                if (pointCount > 1) {
+                    fileUrl = fileDealComp.copyFile(fileBean, userFile);
+                }
+
+                URL url = new URL(downloadUri);
+                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+
+                try {
+                    InputStream stream = connection.getInputStream();
+                    fileDealComp.saveFileInputStream(fileBean.getStorageType(), fileUrl, stream);
+
+                } catch (Exception e) {
+                    log.error(e.getMessage());
+                } finally {
+
+                    int fileLength = connection.getContentLength();
+                    log.info("当前修改文件大小为:" + (long) fileLength);
+
+                    DownloadFile downloadFile = new DownloadFile();
+                    downloadFile.setFileUrl(fileBean.getFileUrl());
+                    InputStream inputStream = ufopFactory.getDownloader(fileBean.getStorageType()).getInputStream(downloadFile);
+                    String md5Str = DigestUtils.md5Hex(inputStream);
+
+                    fileService.updateFileDetail(userFile.getUserFileId(), md5Str, fileLength);
+                    connection.disconnect();
+                }
+            }
+        }
+
+        if("3".equals(status)||"7".equals(status)) {//不强制手动保存时为6,"6".equals(status)
+            log.debug("====保存失败:");
+            writer.write("{\"error\":1}");
+        }else {
+            log.debug("状态为:0") ;
+            writer.write("{\"error\":" + "0" + "}");
+
+        }
+    }
+
+}

+ 50 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/OperationLogBeanController.java

@@ -0,0 +1,50 @@
+package vip.xiaonuo.disk.controller;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.disk.service.IOperationLogService;
+import vip.xiaonuo.disk.util.PageQuery;
+import vip.xiaonuo.disk.util.TableDataInfo;
+import vip.xiaonuo.disk.vo.operationLog.OperationLogBeanVo;
+
+import javax.annotation.Resource;
+import java.util.List;
+import java.util.Map;
+
+@Tag(name = "operationLog", description = "日志操作接口")
+@RestController
+@Slf4j
+@RequestMapping("/operationLog")
+public class OperationLogBeanController {
+
+    @Resource
+    private IOperationLogService iOperationLogService;
+    @Operation(summary = "获取操作日志列表", description = "获取操作日志列表", tags = {"file"})
+    @RequestMapping(value = "/getList", method = RequestMethod.GET)
+    @ResponseBody
+    public CommonResult<List<OperationLogBeanVo>> getList(String type){
+        List<OperationLogBeanVo> list =iOperationLogService.selectOperationLog(type);
+        return CommonResult.data(list);
+    }
+
+
+
+    @Operation(summary = "获取全部资源日志", description = "获取全部资源日志", tags = {"file"})
+    @RequestMapping(value = "/getAllList", method = RequestMethod.GET)
+    @ResponseBody
+    public TableDataInfo<Map<String,Object>> getAllList(@RequestParam(value = "type", required = false) String type,
+                                                              @RequestParam(value = "startDate", required = false) String startDate,
+                                                              @RequestParam(value = "endDate", required = false) String endDate,
+                                                              @RequestParam(value = "userName", required = false) String userName, PageQuery pageQuery
+    ){
+        return iOperationLogService.getAllList(type,startDate,endDate,userName,pageQuery);
+    }
+
+
+
+
+
+}

+ 65 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/PlatformStatusOverviewController.java

@@ -0,0 +1,65 @@
+package vip.xiaonuo.disk.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+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 vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.disk.service.PlatformStatusOverviewService;
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 平台运行状态总览
+ * @author pans
+ */
+@Api(tags = "平台运行状态总览")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@RequestMapping("/platformStatusOverview")
+public class PlatformStatusOverviewController {
+
+    @Resource
+    private PlatformStatusOverviewService platformStatusOverviewService;
+
+    @ApiOperation("系统状态")
+    @GetMapping("/systemStatus")
+    public CommonResult<Map<String, Object>> systemStatus() {
+        return CommonResult.data(platformStatusOverviewService.getSystemStatus());
+    }
+
+    @ApiOperation("热门课程")
+    @GetMapping("/popularCourses")
+    public CommonResult<Map<String, Object>> popularCourses() {
+        return CommonResult.data(platformStatusOverviewService.getPopularCourses());
+    }
+
+    @ApiOperation("所有开课课程")
+    @GetMapping("/allCourses")
+    public CommonResult<Map<String, Object>> allCourses() {
+        return CommonResult.data(platformStatusOverviewService.getAllCourses());
+    }
+
+    @ApiOperation("所有开课课程-分页版")
+    @GetMapping("/allCoursesPage")
+    public CommonResult<Page<Map<String,Object>>> allCoursesPage(HttpServletRequest req) {
+        Map param =new HashMap();
+        Page<Map<String,Object>> list=platformStatusOverviewService.getAllCoursesPage(param);
+        return CommonResult.data(list);
+    }
+
+    @ApiOperation("指定课程近7天访问量趋势")
+    @GetMapping("/courseVisitTrend")
+    public CommonResult<Map<String, Object>> courseVisitTrend(
+            @ApiParam(value = "课程ID", required = true)
+            @RequestParam("courseId") Long courseId) {
+        return CommonResult.data(platformStatusOverviewService.getCourseVisitTrend(courseId));
+    }
+}

+ 132 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/QuestionAnswerGiveController.java

@@ -0,0 +1,132 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.disk.domain.QuestionAnswerGive;
+import vip.xiaonuo.disk.param.QuestionAnswerGiveAddParam;
+import vip.xiaonuo.disk.param.QuestionAnswerGiveEditParam;
+import vip.xiaonuo.disk.param.QuestionAnswerGiveIdParam;
+import vip.xiaonuo.disk.param.QuestionAnswerGivePageParam;
+import vip.xiaonuo.disk.service.QuestionAnswerGiveService;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+
+/**
+ * question_answer_give控制器
+ *
+ * @author pans
+ * @date  2025/07/11 19:32
+ */
+@Api(tags = "课程问答_give控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class QuestionAnswerGiveController {
+
+    @Resource
+    private QuestionAnswerGiveService questionAnswerGiveService;
+
+    /**
+     * 获取question_answer_give分页
+     *
+     * @author pans
+     * @date  2025/07/11 19:32
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取question_answer_give分页")
+    //@SaCheckPermission("/disk/answergive/page")
+    @GetMapping("/disk/answergive/page")
+    public CommonResult<Page<QuestionAnswerGive>> page(QuestionAnswerGivePageParam questionAnswerGivePageParam) {
+        return CommonResult.data(questionAnswerGiveService.page(questionAnswerGivePageParam));
+    }
+
+    /**
+     * 添加question_answer_give
+     *
+     * @author pans
+     * @date  2025/07/11 19:32
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加question_answer_give")
+    @CommonLog("添加question_answer_give")
+    //@SaCheckPermission("/disk/answergive/add")
+    @PostMapping("/disk/answergive/add")
+    public CommonResult<String> add(@RequestBody @Valid QuestionAnswerGiveAddParam questionAnswerGiveAddParam) {
+        questionAnswerGiveService.add(questionAnswerGiveAddParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑question_answer_give
+     *
+     * @author pans
+     * @date  2025/07/11 19:32
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑question_answer_give")
+    @CommonLog("编辑question_answer_give")
+    //@SaCheckPermission("/disk/answergive/edit")
+    @PostMapping("/disk/answergive/edit")
+    public CommonResult<String> edit(@RequestBody @Valid QuestionAnswerGiveEditParam questionAnswerGiveEditParam) {
+        questionAnswerGiveService.edit(questionAnswerGiveEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除question_answer_give
+     *
+     * @author pans
+     * @date  2025/07/11 19:32
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除question_answer_give")
+    @CommonLog("删除question_answer_give")
+    //@SaCheckPermission("/disk/answergive/delete")
+    @PostMapping("/disk/answergive/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<QuestionAnswerGiveIdParam> questionAnswerGiveIdParamList) {
+        questionAnswerGiveService.delete(questionAnswerGiveIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取question_answer_give详情
+     *
+     * @author pans
+     * @date  2025/07/11 19:32
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取question_answer_give详情")
+    //@SaCheckPermission("/disk/answergive/detail")
+    @GetMapping("/disk/answergive/detail")
+    public CommonResult<QuestionAnswerGive> detail(@Valid QuestionAnswerGiveIdParam questionAnswerGiveIdParam) {
+        return CommonResult.data(questionAnswerGiveService.detail(questionAnswerGiveIdParam));
+    }
+
+}

+ 106 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/RecoveryFileController.java

@@ -0,0 +1,106 @@
+package vip.xiaonuo.disk.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.qiwenshare.common.anno.MyLog;
+import com.qiwenshare.common.result.RestResult;
+import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
+import vip.xiaonuo.disk.component.AsyncTaskComp;
+import vip.xiaonuo.disk.domain.RecoveryFile;
+import vip.xiaonuo.disk.dto.file.DeleteRecoveryFileDTO;
+import vip.xiaonuo.disk.dto.recoveryfile.BatchDeleteRecoveryFileDTO;
+import vip.xiaonuo.disk.dto.recoveryfile.RestoreFileDTO;
+import vip.xiaonuo.disk.service.*;
+import vip.xiaonuo.disk.vo.file.RecoveryFileListVo;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+@Tag(name = "recoveryfile", description = "文件删除后会进入回收站,该接口主要是对回收站文件进行管理")
+@RestController
+@Slf4j
+@RequestMapping("/recoveryfile")
+public class RecoveryFileController {
+    @Resource
+    IRecoveryFileService recoveryFileService;
+    @Resource
+    IUserFileService userFileService;
+    @Resource
+    IUserService userService;
+    @Resource
+    IFileService fileService;
+    @Resource
+    IFiletransferService filetransferService;
+    @Resource
+    AsyncTaskComp asyncTaskComp;
+
+
+    public static final String CURRENT_MODULE = "回收站文件接口";
+
+    @Operation(summary = "删除回收文件", description = "删除回收文件", tags = {"recoveryfile"})
+    @MyLog(operation = "删除回收文件", module = CURRENT_MODULE)
+    @RequestMapping(value = "/deleterecoveryfile", method = RequestMethod.POST)
+    @ResponseBody
+    public RestResult<String> deleteRecoveryFile(@RequestBody DeleteRecoveryFileDTO deleteRecoveryFileDTO) {
+        RecoveryFile recoveryFile = recoveryFileService.getOne(new QueryWrapper<RecoveryFile>().lambda().eq(RecoveryFile::getUserFileId, deleteRecoveryFileDTO.getUserFileId()));
+
+        asyncTaskComp.deleteUserFile(recoveryFile.getUserFileId());
+
+        recoveryFileService.removeById(recoveryFile.getRecoveryFileId());
+        return RestResult.success().data("删除成功");
+    }
+
+    @Operation(summary = "批量删除回收文件", description = "批量删除回收文件", tags = {"recoveryfile"})
+    @RequestMapping(value = "/batchdelete", method = RequestMethod.POST)
+    @MyLog(operation = "批量删除回收文件", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult<String> batchDeleteRecoveryFile(@RequestBody BatchDeleteRecoveryFileDTO batchDeleteRecoveryFileDTO) {
+        String userFileIds = batchDeleteRecoveryFileDTO.getUserFileIds();
+        String[] userFileIdList = userFileIds.split(",");
+        for (String userFileId : userFileIdList) {
+            RecoveryFile recoveryFile = recoveryFileService.getOne(new QueryWrapper<RecoveryFile>().lambda().eq(RecoveryFile::getUserFileId, userFileId));
+
+            if (recoveryFile != null) {
+                asyncTaskComp.deleteUserFile(recoveryFile.getUserFileId());
+
+                recoveryFileService.removeById(recoveryFile.getRecoveryFileId());
+            }
+
+        }
+        return RestResult.success().data("批量删除成功");
+    }
+
+    @Operation(summary = "回收文件列表", description = "回收文件列表", tags = {"recoveryfile"})
+    @RequestMapping(value = "/list", method = RequestMethod.GET)
+    @ResponseBody
+    public RestResult<RecoveryFileListVo> getRecoveryFileList() {
+//        JwtUser sessionUserBean = SessionUtil.getSession();
+        String userId = StpLoginUserUtil.getLoginUser().getId();
+        List<RecoveryFileListVo> recoveryFileList = recoveryFileService.selectRecoveryFileList(userId);
+        return RestResult.success().dataList(recoveryFileList, recoveryFileList.size());
+    }
+
+    @Operation(summary = "还原文件", description = "还原文件", tags = {"recoveryfile"})
+    @RequestMapping(value = "/restorefile", method = RequestMethod.POST)
+    @MyLog(operation = "还原文件", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult restoreFile(@RequestBody RestoreFileDTO restoreFileDto) {
+//        JwtUser sessionUserBean = SessionUtil.getSession();
+        String userId = StpLoginUserUtil.getLoginUser().getId();
+        recoveryFileService.restorefile(restoreFileDto.getDeleteBatchNum(), restoreFileDto.getFilePath(), userId);
+        return RestResult.success().message("还原成功!");
+    }
+
+}
+
+
+
+
+
+
+
+
+

+ 117 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/ResourceCollectController.java

@@ -0,0 +1,117 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.disk.param.ResourceCollectAddParam;
+import vip.xiaonuo.disk.param.ResourceCollectPageParam;
+import vip.xiaonuo.disk.service.ResourceCollectService;
+import vip.xiaonuo.disk.vo.collect.ResourceCollectVo;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+
+/**
+ * resource_collect控制器
+ *
+ * @author pans
+ * @date  2025/07/02 15:01
+ */
+@Api(tags = "resource_collect控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class ResourceCollectController {
+
+    @Resource
+    private ResourceCollectService resourceCollectService;
+
+
+
+    /**
+     * 获取我的收藏
+     *
+     * @author pans
+     * @date  2025/07/04 14:39
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取resource_type分页")
+    @GetMapping("/disk/collect/selectPageList")
+    public CommonResult<Page<ResourceCollectVo>> selectPageList(ResourceCollectPageParam resourceCollectPageParam) {
+        return CommonResult.data(resourceCollectService.selectPageList(resourceCollectPageParam));
+    }
+    /**
+     * 根据用户id,查询是否收藏资源
+     *
+     * @author pans
+     * @date  2025/07/02 15:01
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取resource_collect分页")
+    @GetMapping("/disk/collect/queryList")
+    public CommonResult<Boolean> queryList(String resourceId) {
+        return CommonResult.data(resourceCollectService.queryList(resourceId));
+    }
+
+
+
+
+
+    /**
+     * 收藏资源
+     *
+     * @author pans
+     * @date  2025/07/02 15:01
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加resource_collect")
+    @CommonLog("添加resource_collect")
+    @PostMapping("/disk/collect/add")
+    public CommonResult<String> add(@RequestBody @Valid ResourceCollectAddParam resourceCollectAddParam) {
+        resourceCollectService.add(resourceCollectAddParam);
+        return CommonResult.ok();
+    }
+
+
+    /**
+     * 取消资源
+     *
+     * @author pans
+     * @date  2025/07/02 15:01
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加resource_collect")
+    @CommonLog("添加resource_collect")
+    @PostMapping("/disk/collect/cancel")
+    public CommonResult<String> cancel(@RequestBody @Valid ResourceCollectAddParam resourceCollectAddParam) {
+        resourceCollectService.cancel(resourceCollectAddParam);
+        return CommonResult.ok();
+    }
+
+
+
+
+
+
+}

+ 355 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/ResourceFileController.java

@@ -0,0 +1,355 @@
+package vip.xiaonuo.disk.controller;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import com.qiwenshare.common.anno.MyLog;
+import com.qiwenshare.common.result.RestResult;
+import com.qiwenshare.common.util.MimeUtils;
+import com.qiwenshare.ufop.factory.UFOPFactory;
+import com.qiwenshare.ufop.operation.download.Downloader;
+import com.qiwenshare.ufop.operation.download.domain.DownloadFile;
+import com.qiwenshare.ufop.operation.download.domain.Range;
+import com.qiwenshare.ufop.util.UFOPUtils;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.bind.annotation.*;
+import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.util.AuthUtil;
+import vip.xiaonuo.disk.domain.ResourceFile;
+import vip.xiaonuo.disk.domain.ResourceUserFile;
+import vip.xiaonuo.disk.domain.StorageBean;
+import vip.xiaonuo.disk.dto.file.*;
+import vip.xiaonuo.disk.io.QiwenFile;
+import vip.xiaonuo.disk.service.IStorageReService;
+import vip.xiaonuo.disk.service.ResourceFileService;
+import vip.xiaonuo.disk.service.ResourceUserfileService;
+import vip.xiaonuo.disk.vo.file.UploadFileVo;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.*;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Tag(name = "filetransfer", description = "该接口为文件传输接口,主要用来做文件的上传、下载和预览,用于在线教育单拆")
+@RestController
+@RequestMapping("/resourceFile")
+public class ResourceFileController {
+
+    @Resource
+    private ResourceFileService resourceFileService;
+
+    @Resource
+    private ResourceUserfileService resourceUserFileService;
+
+    public static Executor executor = Executors.newFixedThreadPool(20);
+
+    @Resource
+    UFOPFactory ufopFactory;
+
+    @Resource
+    private AuthUtil authUtil;
+
+    @Resource
+    private IStorageReService storageReService;
+
+    public static final String SUCCESS = "操作成功";
+
+    public static final String CURRENT_MODULE = "文件传输接口";
+
+    /**
+     * 上传前校验状况接口
+     * 校验是否已上传过无需上传
+     * 校验是否已有分片,并将上传好的分片返给前端
+     * */
+    @Operation(summary = "极速上传", description = "校验文件MD5判断文件是否存在,如果存在直接上传成功并返回skipUpload=true,如果不存在返回skipUpload=false需要再次调用该接口的POST方法", tags = {"filetransfer"})
+    @RequestMapping(value = "/uploadfile", method = RequestMethod.GET)
+    @MyLog(operation = "极速上传", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult<UploadFileVo> uploadFileSpeed(HttpServletRequest request, UploadFileDTO uploadFileDto) {
+        String userId = StpLoginUserUtil.getLoginUser().getId();
+
+        boolean isCheckSuccess = storageReService.checkStorage(userId, uploadFileDto.getTotalSize());
+        if (!isCheckSuccess) {
+            return RestResult.fail().message("存储空间不足");
+        }
+        UploadFileVo uploadFileVo = resourceFileService.uploadFileSpeed(request,userId,uploadFileDto);
+        return RestResult.success().data(uploadFileVo);
+
+    }
+
+
+    @Operation(summary = "上传文件", description = "真正的上传文件接口", tags = {"filetransfer"})
+    @RequestMapping(value = "/uploadfile", method = RequestMethod.POST)
+    @MyLog(operation = "上传文件", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult<UploadFileVo> uploadFile(HttpServletRequest request, UploadFileDTO uploadFileDto) {
+        String userId = StpLoginUserUtil.getLoginUser().getId();
+
+        boolean isCheckSuccess = storageReService.checkStorage(userId, uploadFileDto.getTotalSize());
+        if (!isCheckSuccess) {
+            return RestResult.fail().message("存储空间不足");
+        }
+
+        UploadFileVo uploadFileVo =resourceFileService.uploadFile(request, uploadFileDto, userId);
+
+
+        return RestResult.success().data(uploadFileVo);
+
+    }
+
+    @Operation(summary = "下载文件", description = "下载文件接口", tags = {"filetransfer"})
+    @MyLog(operation = "下载文件", module = CURRENT_MODULE)
+    @RequestMapping(value = "/downloadfile", method = RequestMethod.GET)
+    public void downloadFile(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, DownloadFileDTO downloadFileDTO) {
+//        Cookie[] cookieArr = httpServletRequest.getCookies();
+        httpServletResponse.setContentType("application/force-download");// 设置强制下载不打开
+        ResourceUserFile resourceUserFile = resourceUserFileService.queryEntity(downloadFileDTO.getUserFileId());
+        String fileName = "";
+        if (resourceUserFile.getIsDir() == 1) {
+            fileName = resourceUserFile.getFileName() + ".zip";
+        } else {
+            fileName = resourceUserFile.getFileName() + "." + resourceUserFile.getExtendName();
+
+        }
+        try {
+            fileName = new String(fileName.getBytes("utf-8"), "ISO-8859-1");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+
+        httpServletResponse.addHeader("Content-Disposition", "attachment;fileName=" + fileName);// 设置文件名
+
+        resourceFileService.downloadFile(httpServletResponse, downloadFileDTO);
+
+    }
+
+
+    @Operation(summary = "批量下载文件", description = "批量下载文件", tags = {"filetransfer"})
+    @RequestMapping(value = "/batchDownloadFile", method = RequestMethod.GET)
+    @MyLog(operation = "批量下载文件", module = CURRENT_MODULE)
+    @ResponseBody
+    public void batchDownloadFile(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BatchDownloadFileDTO batchDownloadFileDTO) {
+
+        String files = batchDownloadFileDTO.getUserFileIds();
+        String[] userFileIdStrs = files.split(",");
+        List<String> userFileIds = new ArrayList<>();
+        for(String userFileId : userFileIdStrs) {
+            ResourceUserFile resourceUserFile = resourceUserFileService.getById(userFileId);
+            if (resourceUserFile.getIsDir() == 0) {
+                userFileIds.add(userFileId);
+            } else {
+                QiwenFile qiwenFile = new QiwenFile(resourceUserFile.getFilePath(), resourceUserFile.getFileName(), true);
+                List<ResourceUserFile> userFileList = resourceUserFileService.selectUserFileByLikeRightFilePath(qiwenFile.getPath(), resourceUserFile.getUserId());
+                List<String> userFileIds1 = userFileList.stream().map(ResourceUserFile::getUserFileId).collect(Collectors.toList());
+                userFileIds.add(resourceUserFile.getUserFileId());
+                userFileIds.addAll(userFileIds1);
+            }
+
+        }
+        ResourceUserFile userFile = resourceUserFileService.getById(userFileIdStrs[0]);
+        httpServletResponse.setContentType("application/force-download");// 设置强制下载不打开
+        Date date = new Date();
+        String fileName = String.valueOf(date.getTime());
+        httpServletResponse.addHeader("Content-Disposition", "attachment;fileName=" + fileName + ".zip");// 设置文件名
+    }
+
+    @Operation(summary="预览文件", description="用于文件预览", tags = {"filetransfer"})
+    @GetMapping("/preview")
+    public void preview(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,  PreviewDTO previewDTO) throws IOException {
+
+        if (previewDTO.getPlatform() != null && previewDTO.getPlatform() == 2) {
+            resourceFileService.previewPictureFile(httpServletResponse, previewDTO);
+            return ;
+        }
+
+        ResourceUserFile resourceUserFile = resourceUserFileService.getById(previewDTO.getUserFileId());
+
+
+        String fileName = resourceUserFile.getFileName() + "." + resourceUserFile.getExtendName();
+        try {
+            fileName = new String(fileName.getBytes("utf-8"), "ISO-8859-1");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+
+        httpServletResponse.addHeader("Content-Disposition", "fileName=" + fileName);// 设置文件名
+        String mime = MimeUtils.getMime(resourceUserFile.getExtendName());
+        httpServletResponse.setHeader("Content-Type", mime);
+        if (UFOPUtils.isImageFile(resourceUserFile.getExtendName())) {
+            httpServletResponse.setHeader("cache-control", "public");
+        }
+
+        ResourceFile resourceFile = resourceFileService.getById(resourceUserFile.getFileId());
+        if (UFOPUtils.isVideoFile(resourceUserFile.getExtendName()) || "mp3".equalsIgnoreCase(resourceUserFile.getExtendName()) || "flac".equalsIgnoreCase(resourceUserFile.getExtendName())) {
+            //获取从那个字节开始读取文件
+            String rangeString = httpServletRequest.getHeader("Range");
+            int start = 0;
+            if (StringUtils.isNotBlank(rangeString)) {
+                start = Integer.parseInt(rangeString.substring(rangeString.indexOf("=") + 1, rangeString.indexOf("-")));
+            }
+
+            Downloader downloader = ufopFactory.getDownloader(resourceFile.getStorageType());
+            DownloadFile downloadFile = new DownloadFile();
+            downloadFile.setFileUrl(resourceFile.getFileUrl());
+            Range range = new Range();
+            range.setStart(start);
+
+            if (start + 1024 * 1024 * 1 >= resourceFile.getFileSize().intValue()) {
+                range.setLength(resourceFile.getFileSize().intValue() - start);
+            } else {
+                range.setLength(1024 * 1024 * 1);
+            }
+            downloadFile.setRange(range);
+            InputStream inputStream = downloader.getInputStream(downloadFile);
+
+            OutputStream outputStream = httpServletResponse.getOutputStream();
+            try {
+
+                //返回码需要为206,代表只处理了部分请求,响应了部分数据
+
+                httpServletResponse.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
+                // 每次请求只返回1MB的视频流
+
+                httpServletResponse.setHeader("Accept-Ranges", "bytes");
+                //设置此次相应返回的数据范围
+                httpServletResponse.setHeader("Content-Range", "bytes " + start + "-" + (resourceFile.getFileSize() - 1) + "/" + resourceFile.getFileSize());
+                IOUtils.copy(inputStream, outputStream);
+
+
+            } finally {
+                IOUtils.closeQuietly(inputStream);
+                IOUtils.closeQuietly(outputStream);
+                if (downloadFile.getOssClient() != null) {
+                    downloadFile.getOssClient().shutdown();
+                }
+            }
+
+
+        } else {
+            resourceFileService.previewFile(httpServletResponse, previewDTO);
+
+        }
+
+    }
+
+    @Operation(summary = "批量删除文件", description = "批量删除文件", tags = {"file"})
+    @RequestMapping(value = "/batchdeletefile", method = RequestMethod.POST)
+    @MyLog(operation = "批量删除文件", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult<String> deleteImageByIds(HttpServletRequest httpServletRequest,@RequestBody BatchDeleteFileDTO batchDeleteFileDto) {
+        String userFileIds = batchDeleteFileDto.getUserFileIds();
+        String[] userFileIdList = userFileIds.split(",");
+        resourceUserFileService.update(new UpdateWrapper<ResourceUserFile>().lambda().set(ResourceUserFile::getDeleteFlag, 1).in(ResourceUserFile::getUserFileId, Arrays.asList(userFileIdList)));
+        for (String userFileId : userFileIdList) {
+            executor.execute(()->{
+                resourceUserFileService.deleteResourceUserFile(userFileId, StpLoginUserUtil.getLoginUser().getId());
+            });
+            ResourceUserFile resourceUserFile = resourceUserFileService.getById(userFileId);
+
+
+        }
+
+        return RestResult.success().message("批量删除文件成功");
+    }
+
+    @Operation(summary = "删除文件", description = "可以删除文件或者目录", tags = {"file"})
+    @RequestMapping(value = "/deletefile", method = RequestMethod.POST)
+    @MyLog(operation = "删除文件", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult deleteFile(HttpServletRequest httpServletRequest,@RequestBody DeleteFileDTO deleteFileDto) {
+
+//        JwtUser sessionUserBean =  SessionUtil.getSession();
+        String userId = StpLoginUserUtil.getLoginUser().getId();
+        resourceUserFileService.deleteResourceUserFile(deleteFileDto.getUserFileId(), userId);
+        ResourceUserFile resourceUserFile = resourceUserFileService.getById(deleteFileDto.getUserFileId());
+
+        return RestResult.success();
+
+    }
+
+    @Operation(summary = "资源库与素材库拉取文件", description = "真正的上传文件接口", tags = {"filetransfer"})
+    @RequestMapping(value = "/eachOtherExchange", method = RequestMethod.POST)
+    @MyLog(operation = "资源库与素材库拉取文件", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult<UploadFileVo> eachOtherExchange(HttpServletRequest request, EachOtherExchangeDTO uploadFileDto) {
+        String userId = StpLoginUserUtil.getLoginUser().getId();
+
+        UploadFileVo uploadFileVo =resourceFileService.eachOtherExchange(request, uploadFileDto, userId);
+
+        return RestResult.success().data(uploadFileVo);
+
+    }
+
+
+    @Operation(summary = "获取存储信息", description = "获取存储信息", tags = {"filetransfer"})
+    @RequestMapping(value = "/getstorage", method = RequestMethod.GET)
+    @ResponseBody
+    public RestResult<StorageBean> getStorage(HttpServletRequest httpServletRequest) {
+
+//        JwtUser sessionUserBean = SessionUtil.getSession();
+        String userId = StpLoginUserUtil.getLoginUser().getId();
+        StorageBean storageBean = new StorageBean();
+
+        storageBean.setUserId(userId);
+
+
+        Long storageSize = resourceFileService.selectStorageSizeByUserId(userId);
+        StorageBean storage = new StorageBean();
+        storage.setUserId(userId);
+        if(ObjectUtil.isNotEmpty(storageSize)){
+            storage.setStorageSize(storageSize);
+        }else{
+            storage.setStorageSize(0l);
+        }
+        Long totalStorageSize = storageReService.getTotalStorageSize(userId);
+        storage.setTotalStorageSize(totalStorageSize);
+        return RestResult.success().data(storage);
+
+    }
+
+
+    @Operation(summary = "获取存储信息", description = "获取存储信息", tags = {"filetransfer"})
+    @RequestMapping(value = "/getResourceStorage", method = RequestMethod.GET)
+    @ResponseBody
+    public CommonResult<StorageBean> getResourceStorage(HttpServletRequest httpServletRequest) {
+
+        CommonResult<String> rs=authUtil.valid(httpServletRequest);
+
+        if(rs.getCode().equals(200)){
+
+            String userAccount=httpServletRequest.getParameter("userAccount");
+
+            //根据用户账号获取用户id
+            String userId=storageReService.selectUserId(userAccount);
+            if(StringUtils.isBlank(userId)){
+                return CommonResult.error("该用户不存在!");
+            }
+
+            StorageBean storageBean = new StorageBean();
+
+            storageBean.setUserId(userId);
+
+            Long storageSize = resourceFileService.selectStorageSizeByUserId(userId);
+            StorageBean storage = new StorageBean();
+            storage.setUserId(userId);
+            storage.setStorageSize(storageSize);
+            Long totalStorageSize = storageReService.getTotalStorageSize(userId);
+            storage.setTotalStorageSize(totalStorageSize);
+            return CommonResult.data(storage);
+        }
+        return CommonResult.error(rs.getMsg());
+    }
+
+}

+ 140 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/ResourceFileFormatController.java

@@ -0,0 +1,140 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.disk.domain.ResourceFileFormat;
+import vip.xiaonuo.disk.param.ResourceFileFormatAddParam;
+import vip.xiaonuo.disk.param.ResourceFileFormatEditParam;
+import vip.xiaonuo.disk.param.ResourceFileFormatIdParam;
+import vip.xiaonuo.disk.param.ResourceFileFormatPageParam;
+import vip.xiaonuo.disk.service.ResourceFileFormatService;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import java.util.List;
+
+/**
+ * resource_file_format控制器
+ *
+ * @author pans
+ * @date  2025/06/28 12:01
+ */
+@Api(tags = "resource_file_format控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class ResourceFileFormatController {
+
+    @Resource
+    private ResourceFileFormatService resourceFileFormatService;
+
+    /**
+     * 获取resource_file_format分页
+     *
+     * @author pans
+     * @date  2025/06/28 12:01
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取resource_file_format分页")
+    @GetMapping("/disk/fileformat/page")
+    public CommonResult<Page<ResourceFileFormat>> page(ResourceFileFormatPageParam resourceFileFormatPageParam) {
+        return CommonResult.data(resourceFileFormatService.page(resourceFileFormatPageParam));
+    }
+
+    /**
+     * resource_file_format-全量列表
+     *
+     * @author pans
+     * @date  2025/06/28 12:01
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("resource_file_format-全量列表")
+    @GetMapping("/disk/fileformat/allList")
+    public CommonResult<List<ResourceFileFormat>> allList() {
+        return CommonResult.data(resourceFileFormatService.allList());
+    }
+
+    /**
+     * 添加resource_file_format
+     *
+     * @author pans
+     * @date  2025/06/28 12:01
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加resource_file_format")
+    @CommonLog("添加resource_file_format")
+    @PostMapping("/disk/fileformat/add")
+    public CommonResult<String> add(@RequestBody @Valid ResourceFileFormatAddParam resourceFileFormatAddParam) {
+        resourceFileFormatService.add(resourceFileFormatAddParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑resource_file_format
+     *
+     * @author pans
+     * @date  2025/06/28 12:01
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑resource_file_format")
+    @CommonLog("编辑resource_file_format")
+    @PostMapping("/disk/fileformat/edit")
+    public CommonResult<String> edit(@RequestBody @Valid ResourceFileFormatEditParam resourceFileFormatEditParam) {
+        resourceFileFormatService.edit(resourceFileFormatEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除resource_file_format
+     *
+     * @author pans
+     * @date  2025/06/28 12:01
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除resource_file_format")
+    @CommonLog("删除resource_file_format")
+    @PostMapping("/disk/fileformat/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<ResourceFileFormatIdParam> resourceFileFormatIdParamList) {
+        resourceFileFormatService.delete(resourceFileFormatIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取resource_file_format详情
+     *
+     * @author pans
+     * @date  2025/06/28 12:01
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取resource_file_format详情")
+    @GetMapping("/disk/fileformat/detail")
+    public CommonResult<ResourceFileFormat> detail(@Valid ResourceFileFormatIdParam resourceFileFormatIdParam) {
+        return CommonResult.data(resourceFileFormatService.detail(resourceFileFormatIdParam));
+    }
+
+}

+ 126 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/ResourceFootprintController.java

@@ -0,0 +1,126 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.disk.domain.ResourceFootprint;
+import vip.xiaonuo.disk.param.footprint.ResourceFootprintAddParam;
+import vip.xiaonuo.disk.param.footprint.ResourceFootprintEditParam;
+import vip.xiaonuo.disk.param.footprint.ResourceFootprintIdParam;
+import vip.xiaonuo.disk.param.footprint.ResourceFootprintPageParam;
+import vip.xiaonuo.disk.service.ResourceFootprintService;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+
+/**
+ * RESOURCE_FOOTPRINT控制器
+ *
+ * @author pans
+ * @date  2025/07/31 14:10
+ */
+@Api(tags = "RESOURCE_FOOTPRINT控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class ResourceFootprintController {
+
+    @Resource
+    private ResourceFootprintService resourceFootprintService;
+
+    /**
+     * 获取RESOURCE_FOOTPRINT分页
+     *
+     * @author pans
+     * @date  2025/07/31 14:10
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取RESOURCE_FOOTPRINT分页")
+    @GetMapping("/disk/footprint/page")
+    public CommonResult<Page<ResourceFootprint>> page(ResourceFootprintPageParam resourceFootprintPageParam) {
+        return CommonResult.data(resourceFootprintService.page(resourceFootprintPageParam));
+    }
+
+    /**
+     * 添加RESOURCE_FOOTPRINT
+     *
+     * @author pans
+     * @date  2025/07/31 14:10
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加RESOURCE_FOOTPRINT")
+    @CommonLog("添加RESOURCE_FOOTPRINT")
+    @PostMapping("/disk/footprint/add")
+    public CommonResult<String> add(@RequestBody @Valid ResourceFootprintAddParam resourceFootprintAddParam) {
+        resourceFootprintService.add(resourceFootprintAddParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑RESOURCE_FOOTPRINT
+     *
+     * @author pans
+     * @date  2025/07/31 14:10
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑RESOURCE_FOOTPRINT")
+    @CommonLog("编辑RESOURCE_FOOTPRINT")
+    @PostMapping("/disk/footprint/edit")
+    public CommonResult<String> edit(@RequestBody @Valid ResourceFootprintEditParam resourceFootprintEditParam) {
+        resourceFootprintService.edit(resourceFootprintEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除RESOURCE_FOOTPRINT
+     *
+     * @author pans
+     * @date  2025/07/31 14:10
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除RESOURCE_FOOTPRINT")
+    @CommonLog("删除RESOURCE_FOOTPRINT")
+    @PostMapping("/disk/footprint/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<ResourceFootprintIdParam> resourceFootprintIdParamList) {
+        resourceFootprintService.delete(resourceFootprintIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取RESOURCE_FOOTPRINT详情
+     *
+     * @author pans
+     * @date  2025/07/31 14:10
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取RESOURCE_FOOTPRINT详情")
+    @GetMapping("/disk/footprint/detail")
+    public CommonResult<ResourceFootprint> detail(@Valid ResourceFootprintIdParam resourceFootprintIdParam) {
+        return CommonResult.data(resourceFootprintService.detail(resourceFootprintIdParam));
+    }
+
+}

+ 1066 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/ResourceRecordController.java

@@ -0,0 +1,1066 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollStreamUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.ui.Model;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.servlet.ModelAndView;
+import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.common.util.AuthUtil;
+import vip.xiaonuo.common.util.PinyinUtils;
+import vip.xiaonuo.disk.domain.CourseAuditRecord;
+import vip.xiaonuo.disk.domain.KeyWord;
+import vip.xiaonuo.disk.domain.ResourceRecordUserRelate;
+import vip.xiaonuo.disk.domain.ResourceUserFile;
+import vip.xiaonuo.disk.param.EditUserFileNameParam;
+import vip.xiaonuo.disk.param.courseauditrecord.CourseAuditRecordAddParam;
+import vip.xiaonuo.disk.param.courseauditrecord.CourseAuditRecordEditParam;
+import vip.xiaonuo.disk.param.courseauditrecord.CourseAuditRecordIdParam;
+import vip.xiaonuo.disk.param.courseauditrecord.CourseAuditRecordPageParam;
+import vip.xiaonuo.disk.service.*;
+import vip.xiaonuo.disk.util.UrlDeal;
+import vip.xiaonuo.disk.vo.collect.ResourceCollectVo;
+import vip.xiaonuo.disk.vo.comment.UserCommentVo;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 资源管理控制器
+ *
+ * @author honorfire
+ * @date  2025/06/20 14:58
+ */
+@Api(tags = "资源管理控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+//@RefreshScope
+@Validated
+public class ResourceRecordController {
+
+
+    @Resource
+    private CourseAuditRecordService courseAuditRecordService;
+
+    @Resource
+    private UserCommentService userCommentService;
+
+    @Resource
+    private ResourceCollectService resourceCollectService;
+
+    @Resource
+    private KeyWordService keyWordService;
+
+    @Resource
+    private ResourceRecordUserRelateService resourceRecordUserRelateService;
+
+    @Resource
+    private CourseInfoService courseInfoService;
+
+    @Resource
+    private ResourceUserfileService resourceUserfileService;
+
+    @Resource
+    private PinyinUtils pinyinUtils;
+
+    @Value("${resource.sharelink.stu}")
+    private String stuResourceShareLink;
+
+    @Value("${resource.sharelink.tea}")
+    private String teaResourceShareLink;
+
+
+    @Value("${teacher.url}")
+    private String teacherUrl;
+
+    @Value("${student.url}")
+    private String studentUrl;
+
+    @Resource
+    private AuthUtil authUtil;
+    /**
+     * 资源管理-课程下拉列表
+     *
+     * @author honorfire
+     * @date  2025/06/20 14:58
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("资源管理-课程下拉列表")
+    //@SaCheckPermission("/disk/courseauditrecord/courceDownList")
+    @GetMapping("/disk/courseauditrecord/courceDownList")
+    public CommonResult<Page<Map<String,Object>>> courceDownList(CourseAuditRecordPageParam courseAuditRecordPageParam, HttpServletRequest req) {
+        Map param =new HashMap();
+
+        //是否是视频资源,0否1是,默认为否
+        List<String> extendNameList = new ArrayList<>();
+        String isVedio="0";
+        if(StringUtils.isNotEmpty(req.getParameter("isVedio")))isVedio=req.getParameter("isVedio");
+        if("1".equals(isVedio)){
+            String extendName="wmv,avi,flv,mpeg,mpg,rmvb,mov,mkv,mp4";
+            extendNameList = Arrays.asList(extendName.split(","));
+            param.put("extendNameList", extendNameList);
+        }
+        //文件名称
+        param.put("fileName", req.getParameter("fileName"));
+        //功能归属表时,0资源库1课程素材库,默认属于资源库
+        param.put("affiliationFuncType",req.getParameter("affiliationFuncType"));
+        Page<Map<String,Object>> list=courseAuditRecordService.courceDownList(param);
+        return CommonResult.data(list);
+    }
+
+    /**
+     * 资源管理-分页列表
+     *
+     * @author honorfire
+     * @date  2025/06/20 14:58
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("资源管理-分页列表")
+    //@SaCheckPermission("/disk/courseauditrecord/page")
+    @GetMapping("/disk/courseauditrecord/page")
+    public CommonResult<Page<Map<String,Object>>> page(CourseAuditRecordPageParam courseAuditRecordPageParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        //默认不传情况,列表查询的是未审核的
+        String verifyStatus="0";
+        if(StringUtils.isNotEmpty(req.getParameter("verifyStatus")))verifyStatus=req.getParameter("verifyStatus");
+//        param.put("verifyStatus", verifyStatus);
+        //改良,审核状态可以查多个
+        param.put("verifyStatusList", Arrays.asList(verifyStatus.split(",")));
+        //是否是个人资源库,0否1是,默认为0
+        String isSelf="0";
+        if(StringUtils.isNotEmpty(req.getParameter("isSelf")))isSelf=req.getParameter("isSelf");
+        if("1".equals(isSelf))
+        {
+            param.put("userId", StpLoginUserUtil.getLoginUser().getId());
+        }
+
+        //文件名称
+        param.put("fileName", req.getParameter("fileName"));
+        param.put("collegeId", req.getParameter("collegeId"));
+        param.put("collegeTwoId", req.getParameter("collegeTwoId"));
+        param.put("collegeThreeId", req.getParameter("collegeThreeId"));
+        param.put("majorId", req.getParameter("majorId"));
+        param.put("resourceType", req.getParameter("resourceType"));
+        param.put("resourceTwoType", req.getParameter("resourceTwoType"));
+        param.put("resourceThreeType", req.getParameter("resourceThreeType"));
+        param.put("suffix", req.getParameter("suffix"));
+        //功能归属表时,0资源库1课程素材库,默认属于资源库
+        param.put("affiliationFuncType",req.getParameter("affiliationFuncType"));
+        Page<Map<String,Object>> list=courseAuditRecordService.queryList(param);
+        return CommonResult.data(list);
+    }
+
+
+    /**
+     * 资源中心-分页列表
+     *
+     * @author honorfire
+     * @date  2025/06/20 14:58
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("资源中心-分页列表")
+    //@SaCheckPermission("/disk/resourcecentre/page")
+    @GetMapping("/disk/resourcecentre/page")
+    public CommonResult<Page<Map<String,Object>>> resourcecentrePage(CourseAuditRecordPageParam courseAuditRecordPageParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("startTime", req.getParameter("startTime"));
+        param.put("endTime", req.getParameter("endTime"));
+        //资源中心排序标识,0最新,1热门,2学习素材,默认为0
+        //2025.9.16要求热门不在单纯为排序了,而是只查出热门类型的内容
+        String sortflag="0";
+        if(StringUtils.isNotEmpty(req.getParameter("sortflag")))sortflag=req.getParameter("sortflag");
+        param.put("sortflag", sortflag);
+        //资源中心功能标识,0默认,1资源推荐,2相关资源
+        String funcType="0";
+        if(StringUtils.isNotEmpty(req.getParameter("funcType")))funcType=req.getParameter("funcType");
+        param.put("funcType", funcType);
+        if("2".equals(funcType))
+        {
+            String userId=StpLoginUserUtil.getLoginUser().getId();
+            param.put("sortflag", "1");
+        }
+
+        //资源中心必须全部是审核通过已发布的
+        String verifyStatus="2";
+        param.put("verifyStatus", verifyStatus);
+        //检索信息
+        if(StringUtils.isNotEmpty(req.getParameter("queryInfo")))
+        {
+            //信息检索会从多个方面触发,用or连接,分别检索资源名称,资源名称,资源拼音,关键词,关键词拼音,老师名称
+            String queryInfo=req.getParameter("queryInfo");
+            param.put("queryInfo", queryInfo);
+            String queryInfoPinyin=StringUtils.deleteWhitespace(pinyinUtils.toPinyin(queryInfo));
+            param.put("queryInfoPinyin", queryInfoPinyin);
+        }
+        //私密权限私可见资源
+        Map privacyParam=new HashMap();
+        privacyParam.put("userId", StpLoginUserUtil.getLoginUser().getId());
+        List<ResourceRecordUserRelate> userRelateList=resourceRecordUserRelateService.allList(privacyParam);
+        List<String> privacyList=userRelateList.stream().map(ResourceRecordUserRelate::getResourceRecord).collect(Collectors.toList());
+        param.put("privacyList",privacyList );
+        param.put("collegeId", req.getParameter("collegeId"));
+        param.put("collegeTwoId", req.getParameter("collegeTwoId"));
+        param.put("collegeThreeId", req.getParameter("collegeThreeId"));
+        param.put("resourceType", req.getParameter("resourceType"));
+        param.put("resourceTwoType", req.getParameter("resourceTwoType"));
+        param.put("resourceThreeType", req.getParameter("resourceThreeType"));
+        param.put("suffix", req.getParameter("suffix"));
+        //功能归属表时,0资源库1课程素材库,默认属于资源库
+        param.put("affiliationFuncType",req.getParameter("affiliationFuncType"));
+        Page<Map<String,Object>> list=courseAuditRecordService.queryResourceCentreList(param);
+        return CommonResult.data(list);
+    }
+
+
+
+
+
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("资源进行top5排行")
+    @GetMapping("/disk/resourcecentre/top5")
+    public CommonResult<List<Map<String,Object>>> selectTop5(HttpServletRequest req){
+        CommonResult<String> rs=authUtil.valid(req);
+        if(rs.getCode().equals(200)){
+            List<Map<String,Object>> list=courseAuditRecordService.selectTop5();
+            return CommonResult.data(list);
+        }
+       return CommonResult.error(rs.getMsg());
+    }
+
+
+
+
+
+    /**
+     * 资源管理-添加
+     *
+     * @author honorfire
+     * @date  2025/06/20 14:58
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("资源管理-添加")
+    @CommonLog("资源管理-添加")
+    //@SaCheckPermission("/disk/courseauditrecord/add")
+    @PostMapping("/disk/courseauditrecord/add")
+    public CommonResult<Map<String,Object>> add(@RequestBody @Valid CourseAuditRecordAddParam courseAuditRecordAddParam) {
+        Map result=new HashMap();
+        //判断教育身份,如果是管理员,直接资源审核通过
+        String eduIdentity=StpLoginUserUtil.getLoginUser().getEduIdentity();
+
+        //生成统一的上传批次号
+        String uploadBatchNum = UUID.randomUUID().toString();
+        if("1".equals(courseAuditRecordAddParam.getAuthType()))
+        {
+            if(StringUtils.isEmpty(courseAuditRecordAddParam.getUserRelateIds()))return CommonResult.error("私密权限关联用户不能为空");
+        }
+        //该次上传附件信息
+        String userFileIdsStr=courseAuditRecordAddParam.getUserfileIds();
+        String[] userFileIdList = userFileIdsStr.split(",");
+        List<CourseAuditRecord> courseAuditRecordList = new ArrayList<>();
+        List<String> addIdList=new ArrayList<>();
+        for (String userFileId : userFileIdList) {
+            CourseAuditRecord courseAuditRecord = BeanUtil.toBean(courseAuditRecordAddParam, CourseAuditRecord.class);
+            //2025.6.27废弃,改用userfile表,直接有相关信息
+//            ResourceUserFile resourceUserFile =resourceUserfileService.queryEntity(userFileId);
+//            courseAuditRecord.setFileName(resourceUserFile.getFileName());
+            courseAuditRecord.setUserfileId(userFileId);
+            if("0".equals(eduIdentity))
+            {
+                courseAuditRecord.setVerifyStatus("2");
+            }else
+            {
+                courseAuditRecord.setVerifyStatus("0");
+            }
+            courseAuditRecord.setUploadBatchNum(uploadBatchNum);
+            if(StringUtils.isNotEmpty(courseAuditRecordAddParam.getKeywordValue()))courseAuditRecord.setKeywordPinyin(StringUtils.deleteWhitespace(pinyinUtils.toPinyin(courseAuditRecordAddParam.getKeywordValue(),false)));
+            courseAuditRecordList.add(courseAuditRecord);
+            courseAuditRecord=courseAuditRecordService.addOne(courseAuditRecord);
+            addIdList.add(courseAuditRecord.getId());
+            //存储私密权限下关联的用户
+            if("1".equals(courseAuditRecordAddParam.getAuthType()))
+            {
+                String userRelateIdsStr=courseAuditRecordAddParam.getUserRelateIds();
+                String[] userRelateIdArray = userRelateIdsStr.split(",");
+                List<String> userRelateIdList=Arrays.asList(userRelateIdArray);
+                List<ResourceRecordUserRelate> resourceRecordUserRelateList = new ArrayList<>();
+                for(String userRelateId:userRelateIdList)
+                {
+                    ResourceRecordUserRelate resourceRecordUserRelate=new ResourceRecordUserRelate();
+                    resourceRecordUserRelate.setUserId(userRelateId);
+                    resourceRecordUserRelate.setResourceRecord(courseAuditRecord.getId());
+                    resourceRecordUserRelateList.add(resourceRecordUserRelate);
+                }
+                //权限中如果没勾选自己,需要自动加入自己
+                String selfId=StpLoginUserUtil.getLoginUser().getId();
+                if(!userRelateIdList.contains(selfId))
+                {
+                    ResourceRecordUserRelate resourceRecordUserRelate=new ResourceRecordUserRelate();
+                    resourceRecordUserRelate.setUserId(selfId);
+                    resourceRecordUserRelate.setResourceRecord(courseAuditRecord.getId());
+                    resourceRecordUserRelateList.add(resourceRecordUserRelate);
+                }
+
+                resourceRecordUserRelateService.addBatch(resourceRecordUserRelateList);
+            }
+
+        }
+//        courseAuditRecordService.addBatch(courseAuditRecordList);
+        String addIdListStr= String.join(",",addIdList);
+        result.put("addIdListStr",addIdListStr);
+        return CommonResult.data(result);
+    }
+
+    /**
+     * 资源管理-编辑
+     *
+     * @author honorfire
+     * @date  2025/06/20 14:58
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("资源管理-编辑")
+    @CommonLog("资源管理-编辑")
+    //@SaCheckPermission("/disk/courseauditrecord/edit")
+    @PostMapping("/disk/courseauditrecord/edit")
+    public CommonResult<String> edit(@RequestBody @Valid CourseAuditRecordEditParam courseAuditRecordEditParam) {
+        if(StringUtils.isEmpty(courseAuditRecordEditParam.getId()))return CommonResult.error("id不能为空");
+        if(StringUtils.isNotEmpty(courseAuditRecordEditParam.getAuthType()))
+        {
+            if("1".equals(courseAuditRecordEditParam.getAuthType()))
+            {
+                if(StringUtils.isEmpty(courseAuditRecordEditParam.getUserRelateIds()))return CommonResult.error("私密权限关联用户不能为空");
+            }
+        }
+        CourseAuditRecord courseAuditRecord = BeanUtil.toBean(courseAuditRecordEditParam, CourseAuditRecord.class);
+        if(StringUtils.isNotEmpty(courseAuditRecordEditParam.getKeywordValue()))courseAuditRecord.setKeywordPinyin(StringUtils.deleteWhitespace(pinyinUtils.toPinyin(courseAuditRecordEditParam.getKeywordValue(),false)));
+        if(StringUtils.isEmpty(courseAuditRecordEditParam.getCollegeId()))courseAuditRecord.setCourseId("");
+        if(StringUtils.isEmpty(courseAuditRecordEditParam.getCollegeTwoId()))courseAuditRecord.setCollegeTwoId("");
+        if(StringUtils.isEmpty(courseAuditRecordEditParam.getCollegeThreeId()))courseAuditRecord.setCollegeThreeId("");
+        if(StringUtils.isEmpty(courseAuditRecordEditParam.getResourceType()))courseAuditRecord.setResourceType("");
+        if(StringUtils.isEmpty(courseAuditRecordEditParam.getResourceTwoType()))courseAuditRecord.setResourceTwoType("");
+        if(StringUtils.isEmpty(courseAuditRecordEditParam.getResourceThreeType()))courseAuditRecord.setResourceThreeType("");
+        //如果过往状态是已发布,需要将状态恢复成待审核
+        CourseAuditRecord oldCourseAuditRecord = courseAuditRecordService.queryEntity(courseAuditRecordEditParam.getId());
+        if(StringUtils.isNotEmpty(oldCourseAuditRecord.getVerifyStatus()))if("2".equals(oldCourseAuditRecord.getVerifyStatus()))courseAuditRecord.setVerifyStatus("1");
+        courseAuditRecordService.editOne(courseAuditRecord);
+        //删除旧私密权限下关联的用户
+        Map deleteRelateParam=new HashMap();
+        deleteRelateParam.put("resourceRecord", courseAuditRecord.getId());
+        List<ResourceRecordUserRelate> deleteRelateList=resourceRecordUserRelateService.allList(deleteRelateParam);
+        List<String> deleteRelateIdList=CollStreamUtil.toList(deleteRelateList, ResourceRecordUserRelate::getId);
+        resourceRecordUserRelateService.deleteByIds(deleteRelateIdList);
+        if(StringUtils.isNotEmpty(courseAuditRecordEditParam.getAuthType()))
+        {
+            //存储私密权限下关联的用户
+            if("1".equals(courseAuditRecordEditParam.getAuthType()))
+            {
+                String userRelateIdsStr=courseAuditRecordEditParam.getUserRelateIds();
+                String[] userRelateIdArray = userRelateIdsStr.split(",");
+                List<String> userRelateIdList=Arrays.asList(userRelateIdArray);
+                List<ResourceRecordUserRelate> resourceRecordUserRelateList = new ArrayList<>();
+                for(String userRelateId:userRelateIdList)
+                {
+                    ResourceRecordUserRelate resourceRecordUserRelate=new ResourceRecordUserRelate();
+                    resourceRecordUserRelate.setUserId(userRelateId);
+                    resourceRecordUserRelate.setResourceRecord(courseAuditRecord.getId());
+                    resourceRecordUserRelateList.add(resourceRecordUserRelate);
+                }
+                //权限中如果没勾选自己,需要自动加入自己
+                String selfId=StpLoginUserUtil.getLoginUser().getId();
+                if(!userRelateIdList.contains(selfId))
+                {
+                    ResourceRecordUserRelate resourceRecordUserRelate=new ResourceRecordUserRelate();
+                    resourceRecordUserRelate.setUserId(selfId);
+                    resourceRecordUserRelate.setResourceRecord(courseAuditRecord.getId());
+                    resourceRecordUserRelateList.add(resourceRecordUserRelate);
+                }
+
+                resourceRecordUserRelateService.addBatch(resourceRecordUserRelateList);
+            }
+        }
+        //如果传入资源名称,则对resource_userfile表的名称进行同步
+        if(StringUtils.isNotEmpty(courseAuditRecordEditParam.getResourceName()))
+        {
+            ResourceUserFile resourceUserfile=resourceUserfileService.queryEntity(oldCourseAuditRecord.getUserfileId());
+            resourceUserfile.setFileName(courseAuditRecordEditParam.getResourceName());
+            resourceUserfileService.editOne(resourceUserfile);
+        }
+
+        return CommonResult.ok();
+    }
+
+    /**
+     * 资源管理-修改状态
+     *
+     * @author honorfire
+     * @date  2025/06/20 14:58
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("资源管理-修改状态")
+    @CommonLog("资源管理-修改状态")
+    //@SaCheckPermission("/disk/courseauditrecord/updateStatus")
+    @PostMapping("/disk/courseauditrecord/updateStatus")
+    public CommonResult<String> updateStatus(@RequestBody @Valid CourseAuditRecordEditParam courseAuditRecordEditParam) {
+        if(StringUtils.isEmpty(courseAuditRecordEditParam.getIds()))return CommonResult.error("id组不能为空");
+        if(StringUtils.isEmpty(courseAuditRecordEditParam.getVerifyStatus()))return CommonResult.error("审核状态不能为空");
+
+        if("1".equals(courseAuditRecordEditParam.getVerifyStatus()))
+        {
+            if(StringUtils.isEmpty(courseAuditRecordEditParam.getResourceDesc()) )return CommonResult.error("发布未传资源信息");
+            if(StringUtils.isEmpty(courseAuditRecordEditParam.getCoverImage()) )return CommonResult.error("发布未传封面");
+        }
+
+        //修改审核状态需要同步改变课程状态(2025.6.26废弃)
+        //批量处理资源管理击状态
+        String idsStr=courseAuditRecordEditParam.getIds();
+        String[] idList = idsStr.split(",");
+        List<CourseAuditRecord> courseAuditRecordList = new ArrayList<>();
+        for (String id : idList) {
+            //1.查回课程审核记录(资源管理)设置审核状态以及相关信息
+            CourseAuditRecord courseAuditRecord=courseAuditRecordService.queryEntity(id);
+            if("1".equals(courseAuditRecordEditParam.getVerifyStatus()))courseAuditRecord.setResourceDesc(courseAuditRecordEditParam.getResourceDesc());
+            if("1".equals(courseAuditRecordEditParam.getVerifyStatus()))courseAuditRecord.setCoverImage(courseAuditRecordEditParam.getCoverImage());
+            courseAuditRecord.setVerifyStatus(courseAuditRecordEditParam.getVerifyStatus());
+            courseAuditRecordList.add(courseAuditRecord);
+        }
+        courseAuditRecordService.editBatch(courseAuditRecordList);
+
+        //2.查回对应课程信息(2025.6.27废弃,课程和资源解耦,不在强绑定)
+//        CourseInfo courseInfo=courseInfoService.queryEntity(courseAuditRecord.getCourseId());
+        //设置审核状态(2025.6.26废弃)
+//        courseInfo.setVerifyStatus(courseAuditRecord.getVerifyStatus());
+//        if("2".equals(courseAuditRecordEditParam.getVerifyStatus()))courseInfo.setPublishTime(new Date());
+        //查看是否课程有关联资源
+//        Map queryRelateParam = new HashMap();
+//        queryRelateParam.put("courseId", courseInfo.getCourseId());
+//        queryRelateParam.put("verifyStatus", "2");
+//        List<CourseAuditRecord> relateResourceList=courseAuditRecordService.queryCourseAuditRecordList(queryRelateParam);
+//        if(relateResourceList != null && relateResourceList.size() > 0)
+//        {
+//            courseInfo.setHaveresource("1");
+//        }
+//        else
+//        {
+//            courseInfo.setHaveresource("0");
+//        }
+//        courseInfoService.editOne(courseInfo);
+
+        return CommonResult.ok();
+    }
+
+
+    /**
+     * 资源中心-添加浏览次数
+     *
+     * @author honorfire
+     * @date  2025/06/20 14:58
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("资源中心-添加浏览次数")
+    @CommonLog("资源中心-添加浏览次数")
+    //@SaCheckPermission("/disk/courseauditrecord/addViewCount")
+    @PostMapping("/disk/courseauditrecord/addViewCount")
+    public CommonResult<String> addViewCount(@RequestBody @Valid CourseAuditRecordEditParam courseAuditRecordEditParam) {
+        CourseAuditRecord courseAuditRecord=courseAuditRecordService.queryEntity(courseAuditRecordEditParam.getId());
+        courseAuditRecord.setViewCount(String.valueOf(Integer.valueOf(courseAuditRecord.getViewCount())+1));
+        courseAuditRecordService.editOne(courseAuditRecord);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 资源中心-获取分享链接
+     *
+     * @author honorfire
+     * @date  2025/06/20 14:58
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("资源中心-获取分享链接")
+    @CommonLog("资源中心-获取分享链接")
+    @GetMapping("/disk/courseauditrecord/getShareLink")
+    public CommonResult<Map<String,Object>> getShareLink(@Valid CourseAuditRecordIdParam courseAuditRecordIdParam, HttpServletRequest req) {
+        Map result=new HashMap();
+        String id=req.getParameter("id");
+//        CourseAuditRecord courseAuditRecord=courseAuditRecordService.queryEntity(id);
+//        String userfileId=courseAuditRecord.getUserfileId();
+//        String shareLink="/api/webapp/resourceFile/downloadfile?userFileId="+userfileId+"&admin=true";
+        String shareLink="/api/webapp/disk/courseauditrecord/getShareInfoPage?id="+id;
+        result.put("shareLink", shareLink);
+        return CommonResult.data(result);
+    }
+
+    /**
+     * 资源管理-删除
+     *
+     * @author honorfire
+     * @date  2025/06/20 14:58
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("资源管理-删除")
+    @CommonLog("资源管理-删除")
+    //@SaCheckPermission("/disk/courseauditrecord/delete")
+    @PostMapping("/disk/courseauditrecord/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                               CommonValidList<CourseAuditRecordIdParam> courseAuditRecordIdParamList) {
+
+        //删除资源管理记录本身
+        courseAuditRecordService.delete(courseAuditRecordIdParamList);
+
+
+        return CommonResult.ok();
+    }
+
+    /**
+     * 资源管理-详情
+     *
+     * @author honorfire
+     * @date  2025/06/20 14:58
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("资源管理-详情")
+    //@SaCheckPermission("/disk/courseauditrecord/detail")
+    @GetMapping("/disk/courseauditrecord/detail")
+    public CommonResult<Map<String,Object>> detail(@Valid CourseAuditRecordIdParam courseAuditRecordIdParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("id", req.getParameter("id"));
+        Map<String,Object> result=courseAuditRecordService.queryInfo(param);
+        //获取关键词
+        String keyword= String.valueOf(result.get("keyword"));
+        keyword=StringUtils.deleteWhitespace(keyword);
+        if(StringUtils.isNotEmpty(keyword))
+        {
+            String[] keywordIdArray=keyword.split(",");
+            List<Integer> keywordIdList = Arrays.stream(keywordIdArray)
+                    .map(Integer::parseInt)
+                    .collect(Collectors.toList());
+            List<KeyWord>  keywordList=keyWordService.selectByIds(keywordIdList);
+            result.put("keywordList",keywordList);
+        }
+        //获取私密权限下关联的用户
+        param.clear();
+        if(StringUtils.isNotEmpty(String.valueOf(result.get("authType"))))
+        {
+            if("1".equals(String.valueOf(result.get("authType"))))
+            {
+                param.put("resourceRecord", req.getParameter("id"));
+                List<ResourceRecordUserRelate> userRelateList=resourceRecordUserRelateService.allList(param);
+//                result.put("userRelateList",userRelateList);
+                List<String> userRelateIdList=CollStreamUtil.toList(userRelateList, ResourceRecordUserRelate::getUserId);
+                result.put("userRelateIdList",userRelateIdList);
+            }
+        }
+
+        return CommonResult.data(result);
+    }
+
+    /**
+     * 资源中心-详情
+     *
+     * @author honorfire
+     * @date  2025/06/20 14:58
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("资源中心-详情")
+    //@SaCheckPermission("/disk/resourcecentre/detail")
+    @GetMapping("/disk/resourcecentre/detail")
+    public CommonResult<Map<String,Object>> resourcecentreDetail(@Valid CourseAuditRecordIdParam courseAuditRecordIdParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("id", req.getParameter("id"));
+        Map<String,Object> result=courseAuditRecordService.queryInfo(param);
+        //2025.8.20会议后,要求直接在详情进行验权
+        String userEduIdentity=StpLoginUserUtil.getLoginUser().getEduIdentity();
+        System.out.println("----------------------------------------------------------------用户教育身份:"+userEduIdentity);
+        //如果管理员直接放行
+        if(!"0".equals(userEduIdentity))
+        {
+            //如果资源是公开的,直接放行,没有权限直接返回错误信息
+            String isHaveAuth="0";
+            if("0".equals(result.get("authType")))
+            {
+                isHaveAuth="1";
+            }
+            else if("1".equals(result.get("authType")))//如果是私密的,检查是否有权限
+            {
+                param.put("resourceRecord", req.getParameter("id"));
+                param.put("userId", StpLoginUserUtil.getLoginUser().getId());
+                List<ResourceRecordUserRelate> userRelateList=resourceRecordUserRelateService.allList(param);
+                if(userRelateList.size()>0)isHaveAuth="1";
+            }
+            System.out.println("----------------------------------------------------------------是否有资源查看权限"+isHaveAuth);
+            if("0".equals(isHaveAuth))return CommonResult.error("您没有访问权限");
+        }
+
+        //评论数
+        List<UserCommentVo> commentList=userCommentService.queryList(req.getParameter("id"));
+        result.put("commentNum",commentList.size());
+        //收藏数
+        List<ResourceCollectVo> resourceCollectList=resourceCollectService.queryResourceCollectVoList(req.getParameter("id"));
+        result.put("collectNum",resourceCollectList.size());
+        //是否收藏
+        Boolean isCollet=resourceCollectService.queryList(req.getParameter("id"));
+        result.put("isCollet",isCollet);
+        //获取关键词
+        String keyword= String.valueOf(result.get("keyword"));
+        keyword=StringUtils.deleteWhitespace(keyword);
+        if(StringUtils.isNotEmpty(keyword))
+        {
+            String[] keywordIdArray = keyword.split(",");
+            List<Integer> keywordIdList = Arrays.stream(keywordIdArray)
+                                                .map(Integer::parseInt)
+                                                .collect(Collectors.toList());
+            List<KeyWord>  keywordList=keyWordService.selectByIds(keywordIdList);
+            result.put("keywordList",keywordList);
+        }
+
+        return CommonResult.data(result);
+    }
+
+    /**
+     * 资源中心-资源是否有查看权限
+     *
+     * @author honorfire
+     * @date  2025/06/20 14:58
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("资源中心-资源是否有查看权限")
+    @GetMapping("/disk/resourcecentre/isHaveAuth")
+    public CommonResult<Map<String,Object>> isHaveAuth(@Valid CourseAuditRecordIdParam courseAuditRecordIdParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        String isHaveAuth="0";
+        param.put("id", req.getParameter("id"));
+        Map result=new HashMap();
+        Map<String,Object> resourceResult=courseAuditRecordService.queryInfo(param);
+        //如果资源是公开的,直接放行
+        if("0".equals(resourceResult.get("authType")))
+        {
+            isHaveAuth="1";
+        }
+        else if("1".equals(resourceResult.get("authType")))//如果是私密的,检查是否有权限
+        {
+            param.put("resourceRecord", req.getParameter("id"));
+            param.put("userId", StpLoginUserUtil.getLoginUser().getId());
+            List<ResourceRecordUserRelate> userRelateList=resourceRecordUserRelateService.allList(param);
+            if(userRelateList.size()>0)isHaveAuth="1";
+        }
+
+        result.put("isHaveAuth",isHaveAuth);
+        return CommonResult.data(result);
+    }
+
+    /**
+     * 资源管理-查询最近一次信息
+     *
+     * @author honorfire
+     * @date  2025/06/20 14:58
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("资源管理-查询最近一次信息")
+    //@SaCheckPermission("/disk/courseauditrecord/recentlyRecord")
+    @GetMapping("/disk/courseauditrecord/recentlyRecord")
+    public CommonResult<Map<String,Object>> recentlyRecord(CourseAuditRecordIdParam courseAuditRecordIdParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        String userId = StpLoginUserUtil.getLoginUser().getId();
+        param.put("userId", userId);
+        Map<String,Object> result=courseAuditRecordService.queryRecentlyRecord(param);
+        if(ObjectUtil.isNotEmpty(result))
+        {
+            //获取关键词
+            String keyword= String.valueOf(result.get("keyword"));
+            keyword=StringUtils.deleteWhitespace(keyword);
+            if(StringUtils.isNotEmpty(keyword))
+            {
+                String[] keywordIdArray=keyword.split(",");
+                List<Integer> keywordIdList = Arrays.stream(keywordIdArray)
+                        .map(Integer::parseInt)
+                        .collect(Collectors.toList());
+                List<KeyWord>  keywordList=keyWordService.selectByIds(keywordIdList);
+                result.put("keywordList",keywordList);
+            }
+            //获取私密权限下关联的用户
+            param.clear();
+            if(StringUtils.isNotEmpty(String.valueOf(result.get("authType"))))
+            {
+                if("1".equals(String.valueOf(result.get("authType"))))
+                {
+                    param.put("resourceRecord", req.getParameter("id"));
+                    List<ResourceRecordUserRelate> userRelateList=resourceRecordUserRelateService.allList(param);
+                    result.put("userRelateList",userRelateList);
+                }
+            }
+        }
+
+        return CommonResult.data(result);
+    }
+
+    //应2025.10.30新需求,学生能操作对应老师资源列表,出新相关操作接口,起
+
+    /**
+     * 学员超链接-查询该学生的授课老师列表
+     *
+     * @author honorfire
+     * @date  2025/10/30
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("学员超链接-查询该学生的授课老师列表")
+    //@SaCheckPermission("/disk/courseauditrecord/getStudentTeacher")
+    @GetMapping("/disk/courseauditrecord/getStudentTeacher")
+    public CommonResult<Page<Map<String,Object>>> getStudentTeacher(CourseAuditRecordIdParam courseAuditRecordIdParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        String userId = StpLoginUserUtil.getLoginUser().getId();
+        param.put("userId", userId);
+        Page<Map<String,Object>> result=courseAuditRecordService.getStudentTeacherList(param);
+
+        return CommonResult.data(result);
+    }
+
+    /**
+     * 资源管理-学员超链接-分页列表
+     *
+     * @author honorfire
+     * @date  2025/10/30
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("资源管理-学员超链接-分页列表")
+    //@SaCheckPermission("/disk/courseauditrecord/stuLinkResourceRecordPage")
+    @GetMapping("/disk/courseauditrecord/stuLinkResourceRecordPage")
+    public CommonResult<Page<Map<String,Object>>> stuLinkResourceRecordPage(CourseAuditRecordPageParam courseAuditRecordPageParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        //默认不传情况,列表查询的是未审核的
+        String verifyStatus="0";
+        if(StringUtils.isNotEmpty(req.getParameter("verifyStatus")))verifyStatus=req.getParameter("verifyStatus");
+//        param.put("verifyStatus", verifyStatus);
+        //改良,审核状态可以查多个
+        param.put("verifyStatusList", Arrays.asList(verifyStatus.split(",")));
+        //2025.10.30学生超链接功能标识,1老师上传,2查学生上传
+        String stulinkType=req.getParameter("stulinkType");
+        //默认是查这个学生对应的老师上传的
+        if(StringUtils.isEmpty(stulinkType))stulinkType="1";
+        param.put("stulinkType", stulinkType);
+        if("1".equals(stulinkType))
+        {
+            //学生超链接里每次只查这个老师的资源列表,老师id必传
+            if(StringUtils.isEmpty(req.getParameter("teacherId")))return CommonResult.error("学生超链接-列表-请传入老师id");
+            param.put("teacherId", req.getParameter("teacherId"));
+        }
+        else if("2".equals(stulinkType))
+        {
+            //学生超链接里默认查指定某个学生上传的资源,但是不传查默认当前这个学生上传的
+            String studentId=req.getParameter("studentId");
+            if(StringUtils.isEmpty(studentId))studentId=StpLoginUserUtil.getLoginUser().getId();
+            param.put("studentId", studentId);
+        }
+
+        //文件名称
+        param.put("fileName", req.getParameter("fileName"));
+        param.put("collegeId", req.getParameter("collegeId"));
+        param.put("collegeTwoId", req.getParameter("collegeTwoId"));
+        param.put("collegeThreeId", req.getParameter("collegeThreeId"));
+        param.put("majorId", req.getParameter("majorId"));
+        param.put("resourceType", req.getParameter("resourceType"));
+        param.put("resourceTwoType", req.getParameter("resourceTwoType"));
+        param.put("resourceThreeType", req.getParameter("resourceThreeType"));
+        param.put("suffix", req.getParameter("suffix"));
+        //功能归属表时,0资源库1课程素材库,默认属于资源库
+//        param.put("affiliationFuncType",req.getParameter("affiliationFuncType"));
+        Page<Map<String,Object>> list=courseAuditRecordService.queryList(param);
+        return CommonResult.data(list);
+    }
+
+    /**
+     * 资源管理-学员超链接-添加
+     *
+     * @author honorfire
+     * @date  2025/10/30
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("资源管理-学员超链接-添加")
+    @CommonLog("资源管理-学员超链接-添加")
+    //@SaCheckPermission("/disk/coursestulink/stuLinkResourceRecordAdd")
+    @PostMapping("/disk/courseauditrecord/stuLinkResourceRecordAdd")
+    public CommonResult<Map<String,Object>> stuLinkResourceRecordAdd(@RequestBody @Valid CourseAuditRecordAddParam courseAuditRecordAddParam) {
+        Map result=new HashMap();
+        //2025.10.30学生超链接功能标识,1老师上传,2查学生上传
+        String stulinkType="1";
+        //2025.10.30学生超链接功能下,应甲方需要,学生在此添加资源,强行把资源归为老师上传,但是把学生信息本地埋点至fileName字段用于追溯
+        if("1".equals(stulinkType))
+        {
+            if(StringUtils.isEmpty(courseAuditRecordAddParam.getTeacherId()))return CommonResult.error("学生超链接-新增-请传入老师id");
+        }
+        String teacherId =courseAuditRecordAddParam.getTeacherId();
+
+        //生成统一的上传批次号
+        String uploadBatchNum = UUID.randomUUID().toString();
+        if("1".equals(courseAuditRecordAddParam.getAuthType()))
+        {
+            if(StringUtils.isEmpty(courseAuditRecordAddParam.getUserRelateIds()))return CommonResult.error("私密权限关联用户不能为空");
+        }
+        //该次上传附件信息
+        String userFileIdsStr=courseAuditRecordAddParam.getUserfileIds();
+        String[] userFileIdList = userFileIdsStr.split(",");
+        List<CourseAuditRecord> courseAuditRecordList = new ArrayList<>();
+        List<String> addIdList=new ArrayList<>();
+        for (String userFileId : userFileIdList) {
+            CourseAuditRecord courseAuditRecord = BeanUtil.toBean(courseAuditRecordAddParam, CourseAuditRecord.class);
+            //2025.10.30学生超链接功能下,应甲方需要,学生在此添加资源,强行把资源归为老师上传,但是把学生信息也做埋点
+            if("1".equals(stulinkType))
+            {
+                courseAuditRecord.setCreateUser(teacherId);
+                courseAuditRecord.setLinkStuId(StpLoginUserUtil.getLoginUser().getId());
+            }
+            //2025.6.27废弃,改用userfile表,直接有相关信息
+//            ResourceUserFile resourceUserFile =resourceUserfileService.queryEntity(userFileId);
+//            courseAuditRecord.setFileName(resourceUserFile.getFileName());
+            courseAuditRecord.setVerifyStatus("0");
+            courseAuditRecord.setUserfileId(userFileId);
+            courseAuditRecord.setUploadBatchNum(uploadBatchNum);
+            if(StringUtils.isNotEmpty(courseAuditRecordAddParam.getKeywordValue()))courseAuditRecord.setKeywordPinyin(StringUtils.deleteWhitespace(pinyinUtils.toPinyin(courseAuditRecordAddParam.getKeywordValue(),false)));
+            courseAuditRecordList.add(courseAuditRecord);
+            courseAuditRecord=courseAuditRecordService.addOne(courseAuditRecord);
+            addIdList.add(courseAuditRecord.getId());
+            //存储私密权限下关联的用户
+            if("1".equals(courseAuditRecordAddParam.getAuthType()))
+            {
+                String userRelateIdsStr=courseAuditRecordAddParam.getUserRelateIds();
+                String[] userRelateIdArray = userRelateIdsStr.split(",");
+                List<String> userRelateIdList=Arrays.asList(userRelateIdArray);
+                List<ResourceRecordUserRelate> resourceRecordUserRelateList = new ArrayList<>();
+                for(String userRelateId:userRelateIdList)
+                {
+                    ResourceRecordUserRelate resourceRecordUserRelate=new ResourceRecordUserRelate();
+                    resourceRecordUserRelate.setUserId(userRelateId);
+                    resourceRecordUserRelate.setResourceRecord(courseAuditRecord.getId());
+                    resourceRecordUserRelateList.add(resourceRecordUserRelate);
+                }
+                //权限中如果没勾选自己,需要自动加入自己
+                String selfId=StpLoginUserUtil.getLoginUser().getId();
+                if(!userRelateIdList.contains(selfId))
+                {
+                    ResourceRecordUserRelate resourceRecordUserRelate=new ResourceRecordUserRelate();
+                    resourceRecordUserRelate.setUserId(selfId);
+                    resourceRecordUserRelate.setResourceRecord(courseAuditRecord.getId());
+                    resourceRecordUserRelateList.add(resourceRecordUserRelate);
+                }
+
+                resourceRecordUserRelateService.addBatch(resourceRecordUserRelateList);
+            }
+
+        }
+//        courseAuditRecordService.addBatch(courseAuditRecordList);
+        String addIdListStr= String.join(",",addIdList);
+        result.put("addIdListStr",addIdListStr);
+        return CommonResult.data(result);
+    }
+
+    /**
+     * 资源管理-学员超链接-编辑
+     *
+     * @author honorfire
+     * @date  2025/06/20 14:58
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("资源管理-学员超链接-编辑")
+    @CommonLog("资源管理-学员超链接-编辑")
+    //@SaCheckPermission("/disk/courseauditrecord/stuLinkResourceRecordEdit")
+    @PostMapping("/disk/courseauditrecord/stuLinkResourceRecordEdit")
+    public CommonResult<String> stuLinkResourceRecordEdit(@RequestBody @Valid CourseAuditRecordEditParam courseAuditRecordEditParam) {
+        if(StringUtils.isEmpty(courseAuditRecordEditParam.getId()))return CommonResult.error("id不能为空");
+        if(StringUtils.isNotEmpty(courseAuditRecordEditParam.getAuthType()))
+        {
+            if("1".equals(courseAuditRecordEditParam.getAuthType()))
+            {
+                if(StringUtils.isEmpty(courseAuditRecordEditParam.getUserRelateIds()))return CommonResult.error("私密权限关联用户不能为空");
+            }
+        }
+        //2025.10.30学生超链接功能标识,1老师上传,2查学生上传
+        String stulinkType="1";
+        //2025.10.30学生超链接功能下,应甲方需要,学生在此添加资源,强行把资源归为老师上传,但是把学生信息本地埋点至fileName字段用于追溯
+        if("1".equals(stulinkType))
+        {
+            if(StringUtils.isEmpty(courseAuditRecordEditParam.getTeacherId()))return CommonResult.error("学生超链接-编辑-请传入老师id");
+        }
+        String teacherId =courseAuditRecordEditParam.getTeacherId();
+
+        CourseAuditRecord courseAuditRecord = BeanUtil.toBean(courseAuditRecordEditParam, CourseAuditRecord.class);
+        if("1".equals(stulinkType))
+        {
+            courseAuditRecord.setCreateUser(teacherId);
+            courseAuditRecord.setLinkStuId(StpLoginUserUtil.getLoginUser().getId());
+        }
+        if(StringUtils.isNotEmpty(courseAuditRecordEditParam.getKeywordValue()))courseAuditRecord.setKeywordPinyin(StringUtils.deleteWhitespace(pinyinUtils.toPinyin(courseAuditRecordEditParam.getKeywordValue(),false)));
+        if(StringUtils.isEmpty(courseAuditRecordEditParam.getCollegeId()))courseAuditRecord.setCourseId("");
+        if(StringUtils.isEmpty(courseAuditRecordEditParam.getCollegeTwoId()))courseAuditRecord.setCollegeTwoId("");
+        if(StringUtils.isEmpty(courseAuditRecordEditParam.getCollegeThreeId()))courseAuditRecord.setCollegeThreeId("");
+        if(StringUtils.isEmpty(courseAuditRecordEditParam.getResourceType()))courseAuditRecord.setResourceType("");
+        if(StringUtils.isEmpty(courseAuditRecordEditParam.getResourceTwoType()))courseAuditRecord.setResourceTwoType("");
+        if(StringUtils.isEmpty(courseAuditRecordEditParam.getResourceThreeType()))courseAuditRecord.setResourceThreeType("");
+        //如果过往状态是已发布,需要将状态恢复成待审核
+        CourseAuditRecord oldCourseAuditRecord = courseAuditRecordService.queryEntity(courseAuditRecordEditParam.getId());
+        if(StringUtils.isNotEmpty(oldCourseAuditRecord.getVerifyStatus()))if("2".equals(oldCourseAuditRecord.getVerifyStatus()))courseAuditRecord.setVerifyStatus("1");
+        courseAuditRecordService.editOne(courseAuditRecord);
+        //删除旧私密权限下关联的用户
+        Map deleteRelateParam=new HashMap();
+        deleteRelateParam.put("resourceRecord", courseAuditRecord.getId());
+        List<ResourceRecordUserRelate> deleteRelateList=resourceRecordUserRelateService.allList(deleteRelateParam);
+        List<String> deleteRelateIdList=CollStreamUtil.toList(deleteRelateList, ResourceRecordUserRelate::getId);
+        resourceRecordUserRelateService.deleteByIds(deleteRelateIdList);
+        if(StringUtils.isNotEmpty(courseAuditRecordEditParam.getAuthType()))
+        {
+            //存储私密权限下关联的用户
+            if("1".equals(courseAuditRecordEditParam.getAuthType()))
+            {
+                String userRelateIdsStr=courseAuditRecordEditParam.getUserRelateIds();
+                String[] userRelateIdArray = userRelateIdsStr.split(",");
+                List<String> userRelateIdList=Arrays.asList(userRelateIdArray);
+                List<ResourceRecordUserRelate> resourceRecordUserRelateList = new ArrayList<>();
+                for(String userRelateId:userRelateIdList)
+                {
+                    ResourceRecordUserRelate resourceRecordUserRelate=new ResourceRecordUserRelate();
+                    resourceRecordUserRelate.setUserId(userRelateId);
+                    resourceRecordUserRelate.setResourceRecord(courseAuditRecord.getId());
+                    resourceRecordUserRelateList.add(resourceRecordUserRelate);
+                }
+                //权限中如果没勾选自己,需要自动加入自己
+                String selfId=StpLoginUserUtil.getLoginUser().getId();
+                if(!userRelateIdList.contains(selfId))
+                {
+                    ResourceRecordUserRelate resourceRecordUserRelate=new ResourceRecordUserRelate();
+                    resourceRecordUserRelate.setUserId(selfId);
+                    resourceRecordUserRelate.setResourceRecord(courseAuditRecord.getId());
+                    resourceRecordUserRelateList.add(resourceRecordUserRelate);
+                }
+
+                resourceRecordUserRelateService.addBatch(resourceRecordUserRelateList);
+            }
+        }
+        //如果传入资源名称,则对resource_userfile表的名称进行同步
+        if(StringUtils.isNotEmpty(courseAuditRecordEditParam.getResourceName()))
+        {
+            ResourceUserFile resourceUserfile=resourceUserfileService.queryEntity(oldCourseAuditRecord.getUserfileId());
+            resourceUserfile.setFileName(courseAuditRecordEditParam.getResourceName());
+            resourceUserfileService.editOne(resourceUserfile);
+        }
+
+        return CommonResult.ok();
+    }
+
+    //应2025.10.30新需求,学生能操作对应老师资源列表,出新相关操作接口,止
+
+    /**
+     * 资源管理-获取分享链接页面
+     *
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("资源管理-获取分享链接页面")
+    @GetMapping("/disk/courseauditrecord/getShareInfoPage")
+    public ModelAndView getShareInfoPage(HttpServletRequest req) { // 返回ModelAndView
+        String id = req.getParameter("id");
+        String stuShareLink = stuResourceShareLink + "?id=" + id+"&isDump=1";
+        String teaShareLink = teaResourceShareLink + "?id=" + id+"&isDump=1";
+
+        ModelAndView mav = new ModelAndView();
+        mav.setViewName("share-page"); // 设置视图名称
+        mav.addObject("stuLink", stuShareLink); // 添加数据
+        mav.addObject("tealink", teaShareLink);
+
+        return mav; // 返回ModelAndView对象
+    }
+
+
+
+
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("门户端-跳转公共页面")
+    @GetMapping("/disk/courseauditrecord/loginJump")
+    public ModelAndView loginJump(HttpServletRequest req,Model model) { // 返回ModelAndView
+        String code = req.getParameter("code");
+        String state = req.getParameter("state");
+        // type=1&id=1958766027408039938
+        state=UrlDeal.decodeURIComponent(UrlDeal.decodeURIComponent(state));
+
+        String type="";
+        String id="";
+        if(state.contains("&")){
+            String stateType=state.split("&")[0];
+            String stateId=state.split("&")[1];
+            type=stateType.split("=")[1];
+            id=stateId.split("=")[1];
+        }else{
+            type=state.split("=")[1];
+        }
+
+        ModelAndView mav = new ModelAndView();
+        mav.setViewName("loginJump"); // 设置视图名称
+        model.addAttribute("teacherUrl", teacherUrl); // 添加数据
+        model.addAttribute("studentUrl", studentUrl);
+        model.addAttribute("code", code); // 添加数据
+        model.addAttribute("type", type);
+        if(ObjectUtil.isNotEmpty(id)){
+            model.addAttribute("id", id);
+        }else{
+            model.addAttribute("id", null);
+        }
+        return mav;
+    }
+
+    /**
+     * 修改用户自己的资源名称功能 2025年10月24日需求
+     * @param param 文件id和修改后的文件名称
+     * @return 修改结果
+     *
+     * @author 金吉龙
+     * @date 2025/10/24
+     */
+    @PostMapping("/disk/courseauditrecord/editUserFileName")
+    public CommonResult<String> editUserFileName(@RequestBody EditUserFileNameParam param) {
+        return courseAuditRecordService.editUserFileName(param);
+    }
+
+    public static void main(String[] args) {
+        String url="type=0";
+       // String url="type=1&id=1958766027408039938";
+        String type="";
+        String id="";
+        if(url.contains("&")){
+            String stateType=url.split("&")[0];
+            String stateId=url.split("&")[1];
+            type=stateType.split("=")[1];
+            id=stateId.split("=")[1];
+        }else{
+            type=url.split("=")[1];
+        }
+
+        System.out.println(type);
+        System.out.println(id);
+    }
+
+
+}

+ 191 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/ResourceStatisticController.java

@@ -0,0 +1,191 @@
+package vip.xiaonuo.disk.controller;
+
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import com.qiwenshare.common.result.RestResult;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.v3.oas.annotations.Operation;
+import org.springframework.web.bind.annotation.*;
+import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.disk.domain.StorageBean;
+import vip.xiaonuo.disk.service.ResourceFileService;
+import vip.xiaonuo.disk.service.ResourceStatisticService;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 资源统计分析
+ * @author pans
+ */
+@Api(tags = "资源统计分析")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@RequestMapping("/resourceStatistic")
+public class ResourceStatisticController {
+
+    @Resource
+    private ResourceStatisticService resourceStatisticService;
+    @Resource
+    private ResourceFileService resourceFileService;
+    /**
+     * 资源统计分析总统计
+     *
+     * @author pans
+     * @date  2025/08/27 14:10
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("资源统计分析总统计")
+    @GetMapping("/totalStatistic")
+    public CommonResult<Map<String, Object>> totalStatistic() {
+        return CommonResult.data(resourceStatisticService.getResourceStatisticData());
+    }
+
+
+    /**
+     * 资源类型分布(按资源类型统计)
+     *
+     * @author pans
+     * @date  2025/08/27 14:10
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("资源类型分布")
+    @GetMapping("/resourceTypeStatistic")
+    public CommonResult<List<Map<String, Object>>> resourceTypeStatistic(String collegeId,String type) {
+        return CommonResult.data(resourceStatisticService.resourceTypeStatistic(collegeId,type));
+    }
+
+
+    /**
+     * 资源类型分布(按院系统计)
+     *
+     * @author pans
+     * @date  2025/08/27 14:10
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("资源类型分布")
+    @GetMapping("/collegeStatistic")
+    public CommonResult<List<Map<String, Object>>> collegeStatistic(String collegeId,String type) {
+        return CommonResult.data(resourceStatisticService.collegeStatistic(collegeId,type));
+    }
+
+
+
+    /**
+     * 资源公开情况
+     *
+     * @author pans
+     * @date  2025/08/27 14:10
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("资源公开分布")
+    @GetMapping("/resourcePublicStatistic")
+    public CommonResult<List<Map<String, Object>>> resourcePublicStatistic(String collegeId,String type) {
+        return CommonResult.data(resourceStatisticService.resourcePublicStatistic(collegeId,type));
+    }
+
+
+
+    /**
+     * 资源热门、推荐统计
+     *
+     * @author pans
+     * @date  2025/08/27 14:10
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("资源公开分布")
+    @GetMapping("/hotStatistic")
+    public CommonResult<List<Map<String, Object>>> hotStatistic(String collegeId,String type) {
+        return CommonResult.data(resourceStatisticService.hotStatistic(collegeId,type));
+    }
+
+
+
+    /**
+     * 不同格式的文件数量
+     *
+     * @author pans
+     * @date  2025/08/27 14:10
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("不同格式的文件数量")
+    @GetMapping("/formatStatistic")
+    public CommonResult<List<Map<String, Object>>> formatStatistic(String collegeId,String type) {
+        return CommonResult.data(resourceStatisticService.formatStatistic(collegeId,type));
+    }
+
+
+    /**
+     * 不同格式的文件数量
+     *
+     * @author pans
+     * @date  2025/08/27 14:10
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("不同文件格式存储空间占用")
+    @GetMapping("/storageStatistic")
+    public CommonResult<List<Map<String, Object>>> storageStatistic(String collegeId,String type) {
+        return CommonResult.data(resourceStatisticService.storageStatistic(collegeId,type));
+    }
+
+
+
+    /**
+     * 获取组织架构
+     *
+     * @author pans
+     * @date  2025/08/27 14:10
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取组织架构")
+    @GetMapping("/selectOrgList")
+    public CommonResult<List<Map<String, Object>>> selectOrgList() {
+        return CommonResult.data(resourceStatisticService.selectOrgList());
+    }
+
+
+    /**
+     * 各资源平均观看人数
+     *
+     * @author pans
+     * @date  2025/08/27 14:10
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("各资源平均观看人数")
+    @GetMapping("/selectResourceCans")
+    public CommonResult<List<Map<String, Object>>> selectResourceCans(String collegeId,String type) {
+        return CommonResult.data(resourceStatisticService.selectResourceCans(collegeId,type));
+    }
+
+    /**
+     * 资源使用情况统计
+     *
+     * @author pans
+     * @date  2025/08/27 14:10
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("资源使用情况统计")
+    @GetMapping("/selectResourceUse")
+    public CommonResult<List<Map<String, Object>>> selectResourceUse(String collegeId,String type) {
+        return CommonResult.data(resourceStatisticService.selectResourceUse(collegeId,type));
+    }
+
+
+    /**
+     * 资源上传和访问趋势
+     *
+     * @author pans
+     * @date  2025/08/27 14:10
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("资源上传和访问趋势")
+    @GetMapping("/selectResourceUpload")
+    public CommonResult<List<Map<String, Object>>> selectResourceUpload(String collegeId,String type) {
+        return CommonResult.data(resourceStatisticService.selectResourceUpload(collegeId,type));
+    }
+
+}

+ 169 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/ResourceTypeController.java

@@ -0,0 +1,169 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import cn.hutool.core.lang.tree.Tree;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.disk.domain.ResourceType;
+import vip.xiaonuo.disk.param.ResourceTypeAddParam;
+import vip.xiaonuo.disk.param.ResourceTypeEditParam;
+import vip.xiaonuo.disk.param.ResourceTypeIdParam;
+import vip.xiaonuo.disk.param.ResourceTypePageParam;
+import vip.xiaonuo.disk.service.ResourceTypeService;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import java.util.List;
+
+/**
+ * resource_type控制器
+ *
+ * @author pans
+ * @date  2025/07/04 14:39
+ */
+@Api(tags = "resource_type控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class ResourceTypeController {
+
+    @Resource
+    private ResourceTypeService resourceTypeService;
+
+    /**
+     * 获取resource_type分页
+     *
+     * @author pans
+     * @date  2025/07/04 14:39
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取resource_type分页")
+    @GetMapping("/disk/type/page")
+    public CommonResult<Page<ResourceType>> page(ResourceTypePageParam resourceTypePageParam) {
+        return CommonResult.data(resourceTypeService.page(resourceTypePageParam));
+    }
+
+    /**
+     * 获取组织树
+     *
+     * @author xuyuxiang
+     * @date 2022/4/24 20:00
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("获取组织树")
+    @GetMapping("/disk/type/tree")
+    public CommonResult<List<Tree<String>>> tree() {
+        return CommonResult.data(resourceTypeService.tree());
+    }
+
+
+    /**
+     * 获取组织树
+     *
+     * @author xuyuxiang
+     * @date 2022/4/24 20:00
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("获取组织树")
+    @GetMapping("/disk/type/treeAll")
+    public CommonResult<List<Tree<String>>> treeAll() {
+        return CommonResult.data(resourceTypeService.treeAll());
+    }
+    /**
+     * 添加resource_type
+     *
+     * @author pans
+     * @date  2025/07/04 14:39
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加resource_type")
+    @CommonLog("添加resource_type")
+    @PostMapping("/disk/type/add")
+    public CommonResult<String> add(@RequestBody @Valid ResourceTypeAddParam resourceTypeAddParam) {
+        resourceTypeService.add(resourceTypeAddParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑resource_type
+     *
+     * @author pans
+     * @date  2025/07/04 14:39
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑resource_type")
+    @CommonLog("编辑resource_type")
+    @PostMapping("/disk/type/edit")
+    public CommonResult<String> edit(@RequestBody @Valid ResourceTypeEditParam resourceTypeEditParam) {
+        resourceTypeService.edit(resourceTypeEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除resource_type
+     *
+     * @author pans
+     * @date  2025/07/04 14:39
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除resource_type")
+    @CommonLog("删除resource_type")
+    @PostMapping("/disk/type/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<ResourceTypeIdParam> resourceTypeIdParamList) {
+        resourceTypeService.delete(resourceTypeIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取resource_type详情
+     *
+     * @author pans
+     * @date  2025/07/04 14:39
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取resource_type详情")
+    @GetMapping("/disk/type/detail")
+    public CommonResult<ResourceType> detail(@Valid ResourceTypeIdParam resourceTypeIdParam) {
+        return CommonResult.data(resourceTypeService.detail(resourceTypeIdParam));
+    }
+
+
+
+    /**
+     * 获取组织树选择器
+     *
+     * @author xuyuxiang
+     * @date 2022/4/24 20:00
+     */
+    @ApiOperationSupport(order = 7)
+    @ApiOperation("获取组织树选择器")
+    @GetMapping("/disk/type/orgTreeSelector")
+    public CommonResult<List<Tree<String>>> orgTreeSelector() {
+        return CommonResult.data(resourceTypeService.orgTreeSelector());
+    }
+
+}

+ 131 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/ResourceUserfileConvertController.java

@@ -0,0 +1,131 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.disk.domain.ResourceUserfileConvert;
+import vip.xiaonuo.disk.param.userfileconvert.ResourceUserfileConvertAddParam;
+import vip.xiaonuo.disk.param.userfileconvert.ResourceUserfileConvertEditParam;
+import vip.xiaonuo.disk.param.userfileconvert.ResourceUserfileConvertIdParam;
+import vip.xiaonuo.disk.param.userfileconvert.ResourceUserfileConvertPageParam;
+import vip.xiaonuo.disk.service.ResourceUserfileConvertService;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+
+/**
+ * RESOURCE_USERFILE_CONVERT控制器
+ *
+ * @author pans
+ * @date  2025/07/25 09:14
+ */
+@Api(tags = "RESOURCE_USERFILE_CONVERT控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class ResourceUserfileConvertController {
+
+    @Resource
+    private ResourceUserfileConvertService resourceUserfileConvertService;
+
+    /**
+     * 获取RESOURCE_USERFILE_CONVERT分页
+     *
+     * @author pans
+     * @date  2025/07/25 09:14
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取RESOURCE_USERFILE_CONVERT分页")
+    @GetMapping("/disk/userfileconvert/page")
+    public CommonResult<Page<ResourceUserfileConvert>> page(ResourceUserfileConvertPageParam resourceUserfileConvertPageParam) {
+        return CommonResult.data(resourceUserfileConvertService.page(resourceUserfileConvertPageParam));
+    }
+
+    /**
+     * 添加RESOURCE_USERFILE_CONVERT
+     *
+     * @author pans
+     * @date  2025/07/25 09:14
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加RESOURCE_USERFILE_CONVERT")
+    @CommonLog("添加RESOURCE_USERFILE_CONVERT")
+    //@SaCheckPermission("/disk/userfileconvert/add")
+    @PostMapping("/disk/userfileconvert/add")
+    public CommonResult<String> add(@RequestBody @Valid ResourceUserfileConvertAddParam resourceUserfileConvertAddParam) {
+        resourceUserfileConvertService.add(resourceUserfileConvertAddParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑RESOURCE_USERFILE_CONVERT
+     *
+     * @author pans
+     * @date  2025/07/25 09:14
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑RESOURCE_USERFILE_CONVERT")
+    @CommonLog("编辑RESOURCE_USERFILE_CONVERT")
+    //@SaCheckPermission("/disk/userfileconvert/edit")
+    @PostMapping("/disk/userfileconvert/edit")
+    public CommonResult<String> edit(@RequestBody @Valid ResourceUserfileConvertEditParam resourceUserfileConvertEditParam) {
+        resourceUserfileConvertService.edit(resourceUserfileConvertEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除RESOURCE_USERFILE_CONVERT
+     *
+     * @author pans
+     * @date  2025/07/25 09:14
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除RESOURCE_USERFILE_CONVERT")
+    @CommonLog("删除RESOURCE_USERFILE_CONVERT")
+    //@SaCheckPermission("/disk/userfileconvert/delete")
+    @PostMapping("/disk/userfileconvert/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<ResourceUserfileConvertIdParam> resourceUserfileConvertIdParamList) {
+        resourceUserfileConvertService.delete(resourceUserfileConvertIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取RESOURCE_USERFILE_CONVERT详情
+     *
+     * @author pans
+     * @date  2025/07/25 09:14
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取RESOURCE_USERFILE_CONVERT详情")
+    //@SaCheckPermission("/disk/userfileconvert/detail")
+    @GetMapping("/disk/userfileconvert/detail")
+    public CommonResult<ResourceUserfileConvert> detail(@Valid ResourceUserfileConvertIdParam resourceUserfileConvertIdParam) {
+        return CommonResult.data(resourceUserfileConvertService.detail(resourceUserfileConvertIdParam));
+    }
+
+}

+ 132 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/RoomController.java

@@ -0,0 +1,132 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.disk.domain.Room;
+import vip.xiaonuo.disk.param.RoomAddParam;
+import vip.xiaonuo.disk.param.RoomEditParam;
+import vip.xiaonuo.disk.param.RoomIdParam;
+import vip.xiaonuo.disk.param.RoomPageParam;
+import vip.xiaonuo.disk.service.RoomService;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+
+/**
+ * 教室表控制器
+ *
+ * @author pans
+ * @date  2025/07/04 09:07
+ */
+@Api(tags = "教室表控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class RoomController {
+
+    @Resource
+    private RoomService roomService;
+
+    /**
+     * 获取教室表分页
+     *
+     * @author pans
+     * @date  2025/07/04 09:07
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取教室表分页")
+    //@SaCheckPermission("/disk/room/page")
+    @GetMapping("/disk/room/page")
+    public CommonResult<Page<Room>> page(RoomPageParam roomPageParam) {
+        return CommonResult.data(roomService.page(roomPageParam));
+    }
+
+    /**
+     * 添加教室表
+     *
+     * @author pans
+     * @date  2025/07/04 09:07
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加教室表")
+    @CommonLog("添加教室表")
+    //@SaCheckPermission("/disk/room/add")
+    @PostMapping("/disk/room/add")
+    public CommonResult<String> add(@RequestBody @Valid RoomAddParam roomAddParam) {
+        roomService.add(roomAddParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑教室表
+     *
+     * @author pans
+     * @date  2025/07/04 09:07
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑教室表")
+    @CommonLog("编辑教室表")
+    //@SaCheckPermission("/disk/room/edit")
+    @PostMapping("/disk/room/edit")
+    public CommonResult<String> edit(@RequestBody @Valid RoomEditParam roomEditParam) {
+        roomService.edit(roomEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除教室表
+     *
+     * @author pans
+     * @date  2025/07/04 09:07
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除教室表")
+    @CommonLog("删除教室表")
+    //@SaCheckPermission("/disk/room/delete")
+    @PostMapping("/disk/room/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<RoomIdParam> roomIdParamList) {
+        roomService.delete(roomIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取教室表详情
+     *
+     * @author pans
+     * @date  2025/07/04 09:07
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取教室表详情")
+    //@SaCheckPermission("/disk/room/detail")
+    @GetMapping("/disk/room/detail")
+    public CommonResult<Room> detail(@Valid RoomIdParam roomIdParam) {
+        return CommonResult.data(roomService.detail(roomIdParam));
+    }
+
+}

+ 218 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/SemesterController.java

@@ -0,0 +1,218 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.hutool.core.bean.BeanUtil;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.disk.domain.Semester;
+import vip.xiaonuo.disk.param.semester.SemesterAddParam;
+import vip.xiaonuo.disk.param.semester.SemesterEditParam;
+import vip.xiaonuo.disk.param.semester.SemesterIdParam;
+import vip.xiaonuo.disk.param.semester.SemesterPageParam;
+import vip.xiaonuo.disk.service.SemesterService;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+/**
+ * 学期表控制器
+ *
+ * @author honorfire
+ * @date  2025/07/11 14:42
+ */
+@Api(tags = "学期表控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class SemesterController {
+
+    @Resource
+    private SemesterService semesterService;
+
+    /**
+     * 学期表-分页列表
+     *
+     * @author honorfire
+     * @date  2025/07/11 14:42
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("学期表-分页列表")
+    //@SaCheckPermission("/disk/semester/page")
+    @GetMapping("/disk/semester/page")
+    public CommonResult<Page<Map<String,Object>>> page(SemesterPageParam semesterPageParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("name", req.getParameter("name"));
+        param.put("multiyear", req.getParameter("multiyear"));
+        param.put("quater", req.getParameter("quater"));
+
+        Page<Map<String,Object>> list=semesterService.queryList(param);
+        return CommonResult.data(list);
+    }
+
+    /**
+     * 学期表-下拉列表
+     *
+     * @author honorfire
+     * @date  2025/07/11 14:42
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("学期表-下拉列表")
+    //@SaCheckPermission("/disk/semester/downList")
+    @GetMapping("/disk/semester/downList")
+    public CommonResult<List<Map<String,Object>>> downList(SemesterPageParam semesterPageParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("type",req.getParameter("type") );
+
+        List<Map<String,Object>> list=semesterService.downList(param);
+        return CommonResult.data(list);
+    }
+
+    /**
+     * 添加学期表
+     *
+     * @author honorfire
+     * @date  2025/07/11 14:42
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加学期表")
+    @CommonLog("添加学期表")
+    //@SaCheckPermission("/disk/semester/add")
+    @PostMapping("/disk/semester/add")
+    public CommonResult<String> add(@RequestBody @Valid SemesterAddParam semesterAddParam) {
+        Semester semester = BeanUtil.toBean(semesterAddParam, Semester.class);
+
+        try {
+            if (semester.getStartTime() != null) {
+                SimpleDateFormat sdt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                Calendar calendar = Calendar.getInstance();
+                calendar.setTime(sdt.parse(semester.getStartTime()));
+                int year = calendar.get(Calendar.YEAR);
+                // 从startTime字段中提取年份并存入multiyear字段
+                semester.setMultiyear(String.valueOf(year));
+                // 从startTime字段中提取月份判断并存入quarter字段
+                int month = calendar.get(Calendar.MONTH) + 1; // 月份是0-11,需要+1
+                String quarter;
+                if (month >= 1 && month <= 3) {
+                    quarter = "1";
+                } else if (month >= 4 && month <= 6) {
+                    quarter = "2";
+                } else if (month >= 7 && month <= 9) {
+                    quarter = "3";
+                } else {
+                    quarter = "4";
+                }
+                semester.setQuater(quarter);
+            }
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+        semesterService.addOne(semester);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑学期表
+     *
+     * @author honorfire
+     * @date  2025/07/11 14:42
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑学期表")
+    @CommonLog("编辑学期表")
+    //@SaCheckPermission("/disk/semester/edit")
+    @PostMapping("/disk/semester/edit")
+    public CommonResult<String> edit(@RequestBody @Valid SemesterEditParam semesterEditParam) {
+        Semester semester = BeanUtil.toBean(semesterEditParam, Semester.class);
+        try {
+            if (semester.getStartTime() != null) {
+                SimpleDateFormat sdt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                Calendar calendar = Calendar.getInstance();
+                calendar.setTime(sdt.parse(semester.getStartTime()));
+                int year = calendar.get(Calendar.YEAR);
+                // 从startTime字段中提取年份并存入multiyear字段
+                semester.setMultiyear(String.valueOf(year));
+                // 从startTime字段中提取月份判断并存入quarter字段
+                int month = calendar.get(Calendar.MONTH) + 1; // 月份是0-11,需要+1
+                String quarter;
+                if (month >= 1 && month <= 3) {
+                    quarter = "1";
+                } else if (month >= 4 && month <= 6) {
+                    quarter = "2";
+                } else if (month >= 7 && month <= 9) {
+                    quarter = "3";
+                } else {
+                    quarter = "4";
+                }
+                semester.setQuater(quarter);
+            }
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+        semesterService.editOne(semester);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除学期表
+     *
+     * @author honorfire
+     * @date  2025/07/11 14:42
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除学期表")
+    @CommonLog("删除学期表")
+    //@SaCheckPermission("/disk/semester/delete")
+    @PostMapping("/disk/semester/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<SemesterIdParam> semesterIdParamList) {
+        semesterService.delete(semesterIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 获取学期表详情
+     *
+     * @author honorfire
+     * @date  2025/07/11 14:42
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("获取学期表详情")
+    //@SaCheckPermission("/disk/semester/detail")
+    @GetMapping("/disk/semester/detail")
+    public CommonResult<Map<String,Object>> detail(@Valid SemesterIdParam semesterIdParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("id", req.getParameter("id"));
+        Map<String,Object> result=semesterService.queryInfo(param);
+        return CommonResult.data(result);
+    }
+
+}

+ 249 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/ShareController.java

@@ -0,0 +1,249 @@
+package vip.xiaonuo.disk.controller;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.RandomUtil;
+import com.alibaba.fastjson2.JSON;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.qiwenshare.common.anno.MyLog;
+import com.qiwenshare.common.result.RestResult;
+import com.qiwenshare.common.util.DateUtil;
+import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
+import vip.xiaonuo.disk.service.IShareFileService;
+import vip.xiaonuo.disk.service.IShareService;
+import vip.xiaonuo.disk.service.IUserFileService;
+import vip.xiaonuo.disk.component.FileDealComp;
+import vip.xiaonuo.disk.domain.Share;
+import vip.xiaonuo.disk.domain.ShareFile;
+import vip.xiaonuo.disk.domain.UserFile;
+import vip.xiaonuo.disk.dto.sharefile.*;
+import vip.xiaonuo.disk.io.QiwenFile;
+import vip.xiaonuo.disk.vo.share.ShareFileListVO;
+import vip.xiaonuo.disk.vo.share.ShareFileVO;
+import vip.xiaonuo.disk.vo.share.ShareListVO;
+import vip.xiaonuo.disk.vo.share.ShareTypeVO;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.text.ParseException;
+import java.util.*;
+
+@Tag(name = "share", description = "该接口为文件分享接口")
+@RestController
+@Slf4j
+@RequestMapping("/share")
+public class ShareController {
+
+    public static final String CURRENT_MODULE = "文件分享";
+
+    @Resource
+    IShareFileService shareFileService;
+    @Resource
+    IShareService shareService;
+    @Resource
+    IUserFileService userFileService;
+    @Resource
+    FileDealComp fileDealComp;
+
+    @Operation(summary = "分享文件", description = "分享文件统一接口", tags = {"share"})
+    @PostMapping(value = "/sharefile")
+    @MyLog(operation = "分享文件", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult<ShareFileVO> shareFile( @RequestBody ShareFileDTO shareFileDTO) {
+        //预留用于返回前端的批次号和提取码
+        ShareFileVO shareSecretVO = new ShareFileVO();
+//        JwtUser sessionUserBean = SessionUtil.getSession();
+        String userId = StpLoginUserUtil.getLoginUser().getId();
+
+        String uuid = UUID.randomUUID().toString().replace("-", "");
+        Share share = new Share();
+        share.setShareId(IdUtil.getSnowflakeNextIdStr());
+        BeanUtil.copyProperties(shareFileDTO, share);
+        share.setShareTime(DateUtil.getCurrentTime());
+        share.setUserId(userId);
+        share.setShareStatus(0);
+        if (shareFileDTO.getShareType() == 1) {
+            String extractionCode = RandomUtil.randomNumbers(6);
+            share.setExtractionCode(extractionCode);
+            shareSecretVO.setExtractionCode(share.getExtractionCode());
+        }
+
+        share.setShareBatchNum(uuid);
+        shareService.save(share);
+
+        //此处逻辑,支持多个文件传输,也包括文件夹
+        List<ShareFile> saveFileList = new ArrayList<>();
+        String userFileIds = shareFileDTO.getUserFileIds();
+        String[] userFileIdList = userFileIds.split(",");
+        //遍历用户选择的文件
+        for (String userFileId : userFileIdList) {
+            UserFile userFile = userFileService.getById(userFileId);
+            if (userFile.getUserId().compareTo(userId) != 0) {
+                return RestResult.fail().message("您只能分享自己的文件");
+            }
+            //此处逻辑,如果是目录需要先把目录下的所有文件都添加到userfileList集合中,最后再把目录这条"文件"也加入到列表
+            if (userFile.getIsDir() == 1) {
+                QiwenFile qiwenFile = new QiwenFile(userFile.getFilePath(), userFile.getFileName(), true);
+                List<UserFile> userfileList = userFileService.selectUserFileByLikeRightFilePath(qiwenFile.getPath(), userId);
+                for (UserFile userFile1 : userfileList) {
+                    ShareFile shareFile1 = new ShareFile();
+                    shareFile1.setShareFileId(IdUtil.getSnowflakeNextIdStr());
+                    shareFile1.setUserFileId(userFile1.getUserFileId());
+                    shareFile1.setShareBatchNum(uuid);
+                    shareFile1.setShareFilePath(userFile1.getFilePath().replaceFirst(userFile.getFilePath().equals("/") ? "" : userFile.getFilePath(), ""));
+                    saveFileList.add(shareFile1);
+                }
+            }
+            ShareFile shareFile = new ShareFile();
+            shareFile.setShareFileId(IdUtil.getSnowflakeNextIdStr());
+            shareFile.setUserFileId(userFileId);
+            shareFile.setShareFilePath("/");
+            shareFile.setShareBatchNum(uuid);
+            saveFileList.add(shareFile);
+
+
+        }
+        shareFileService.saveBatch(saveFileList);
+        shareSecretVO.setShareBatchNum(uuid);
+
+        return RestResult.success().data(shareSecretVO);
+    }
+
+    @Operation(summary = "保存分享文件", description = "用来将别人分享的文件保存到自己的网盘中", tags = {"share"})
+    @PostMapping(value = "/savesharefile")
+    @MyLog(operation = "保存分享文件", module = CURRENT_MODULE)
+    @Transactional(rollbackFor=Exception.class)
+    @ResponseBody
+    public RestResult saveShareFile(@RequestBody SaveShareFileDTO saveShareFileDTO) {
+
+//        JwtUser sessionUserBean = SessionUtil.getSession();
+        String userId = StpLoginUserUtil.getLoginUser().getId();
+//        List<ShareFile> fileList = JSON.parseArray(saveShareFileDTO.getFiles(), ShareFile.class);
+        String savefilePath = saveShareFileDTO.getFilePath();
+//        String userId = sessionUserBean.getUserId();
+        String[] userFileIdArr = saveShareFileDTO.getUserFileIds().split(",");
+        List<UserFile> saveUserFileList = new ArrayList<>();
+        for (String userFileId : userFileIdArr) {
+
+
+            UserFile userFile = userFileService.getById(userFileId);
+            String fileName = userFile.getFileName();
+            String filePath = userFile.getFilePath();
+
+            UserFile userFile2 = new UserFile();
+            BeanUtil.copyProperties(userFile, userFile2);
+
+            String savefileName = fileDealComp.getRepeatFileName(userFile, savefilePath);
+
+            //如果是目录,则把所有目录及子目录下的文件都保存
+            if (userFile.getIsDir() == 1) {
+                ShareFile shareFile = shareFileService.getOne(new QueryWrapper<ShareFile>().lambda().eq(ShareFile::getUserFileId, userFileId).eq(ShareFile::getShareBatchNum, saveShareFileDTO.getShareBatchNum()));
+                List<ShareFile> shareFileList = shareFileService.list(new QueryWrapper<ShareFile>().lambda().eq(ShareFile::getShareBatchNum, saveShareFileDTO.getShareBatchNum()).likeRight(ShareFile::getShareFilePath, QiwenFile.formatPath(shareFile.getShareFilePath() +"/"+ fileName)));
+
+
+
+                for (ShareFile shareFile1 : shareFileList) {
+                    UserFile userFile1 = userFileService.getById(shareFile1.getUserFileId());
+                    userFile1.setUserFileId(IdUtil.getSnowflakeNextIdStr());
+                    userFile1.setUserId(userId);
+                    userFile1.setFilePath(userFile1.getFilePath().replaceFirst(QiwenFile.formatPath(filePath + "/" + fileName), QiwenFile.formatPath(savefilePath + "/" + savefileName)));
+                    saveUserFileList.add(userFile1);
+//                    log.info("当前文件:" + JSON.toJSONString(userFile1));
+                }
+            }
+            userFile2.setUserFileId(IdUtil.getSnowflakeNextIdStr());
+            userFile2.setUserId(userId);
+            userFile2.setFilePath(savefilePath);
+            userFile2.setFileName(savefileName);
+            saveUserFileList.add(userFile2);
+
+        }
+//        log.info("----------" + JSON.toJSONString(saveUserFileList));
+        userFileService.saveBatch(saveUserFileList);
+
+        return RestResult.success();
+    }
+
+    @Operation(summary = "查看已分享列表", description = "查看已分享列表", tags = {"share"})
+    @GetMapping(value = "/shareList")
+    @ResponseBody
+    public RestResult<ShareListVO> shareList(ShareListDTO shareListDTO) {
+//        JwtUser sessionUserBean = SessionUtil.getSession();
+        String userId = StpLoginUserUtil.getLoginUser().getId();
+        List<ShareListVO> shareList = shareService.selectShareList(shareListDTO, userId);
+
+        int total = shareService.selectShareListTotalCount(shareListDTO, userId);
+
+        return RestResult.success().dataList(shareList, total);
+    }
+
+
+    @Operation(summary = "查看被分享文件列表", description = "分享列表", tags = {"share"})
+    @GetMapping(value = "/sharefileList")
+    @ResponseBody
+    public RestResult<ShareFileListVO> shareFileList(ShareFileListDTO shareFileListBySecretDTO) {
+        String shareBatchNum = shareFileListBySecretDTO.getShareBatchNum();
+        String shareFilePath = shareFileListBySecretDTO.getShareFilePath();
+        List<ShareFileListVO> list = shareFileService.selectShareFileList(shareBatchNum, shareFilePath);
+        for (ShareFileListVO shareFileListVO : list) {
+            shareFileListVO.setShareFilePath(shareFilePath);
+        }
+        return RestResult.success().dataList(list, list.size());
+    }
+
+    @Operation(summary = "分享类型", description = "可用此接口判断是否需要提取码", tags = {"share"})
+    @GetMapping(value = "/sharetype")
+    @ResponseBody
+    public RestResult<ShareTypeVO> shareType(ShareTypeDTO shareTypeDTO) {
+        LambdaQueryWrapper<Share> lambdaQueryWrapper = new LambdaQueryWrapper<>();
+        lambdaQueryWrapper.eq(Share::getShareBatchNum, shareTypeDTO.getShareBatchNum());
+        Share share = shareService.getOne(lambdaQueryWrapper);
+        ShareTypeVO shareTypeVO = new ShareTypeVO();
+        shareTypeVO.setShareType(share.getShareType());
+        return RestResult.success().data(shareTypeVO);
+    }
+
+    @Operation(summary = "校验提取码", description = "校验提取码", tags = {"share"})
+    @GetMapping(value = "/checkextractioncode")
+    @ResponseBody
+    public RestResult<String> checkExtractionCode(CheckExtractionCodeDTO checkExtractionCodeDTO) {
+        LambdaQueryWrapper<Share> lambdaQueryWrapper = new LambdaQueryWrapper<>();
+        lambdaQueryWrapper.eq(Share::getShareBatchNum, checkExtractionCodeDTO.getShareBatchNum())
+                .eq(Share::getExtractionCode, checkExtractionCodeDTO.getExtractionCode());
+        List<Share> list = shareService.list(lambdaQueryWrapper);
+        if (list.isEmpty()) {
+            return RestResult.fail().message("校验失败");
+        } else {
+            return RestResult.success();
+        }
+    }
+
+    @Operation(summary = "校验过期时间", description = "校验过期时间", tags = {"share"})
+    @GetMapping(value = "/checkendtime")
+    @ResponseBody
+    public RestResult<String> checkEndTime(CheckEndTimeDTO checkEndTimeDTO) {
+        LambdaQueryWrapper<Share> lambdaQueryWrapper = new LambdaQueryWrapper<>();
+        lambdaQueryWrapper.eq(Share::getShareBatchNum, checkEndTimeDTO.getShareBatchNum());
+        Share share = shareService.getOne(lambdaQueryWrapper);
+        if (share == null) {
+            return RestResult.fail().message("文件不存在!");
+        }
+        String endTime = share.getEndTime();
+        Date endTimeDate = null;
+        try {
+            endTimeDate = DateUtil.getDateByFormatString(endTime, "yyyy-MM-dd HH:mm:ss");
+        } catch (ParseException e) {
+            log.error("日期解析失败:{}" , e);
+        }
+        if (new Date().after(endTimeDate))  {
+            return RestResult.fail().message("分享已过期");
+        } else {
+            return RestResult.success();
+        }
+    }
+}

+ 260 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/ShareGroupController.java

@@ -0,0 +1,260 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import cn.hutool.core.collection.CollStreamUtil;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.auth.core.enums.SysUserStatusEnum;
+import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.common.util.CommonAvatarUtil;
+import vip.xiaonuo.common.util.CommonCryptogramUtil;
+import vip.xiaonuo.common.util.PinyinUtils;
+import vip.xiaonuo.dev.api.DevConfigApi;
+import vip.xiaonuo.disk.param.shareGroupuser.*;
+import vip.xiaonuo.disk.service.ShareGroupUserService;
+import vip.xiaonuo.sys.modular.relation.enums.SysRelationCategoryEnum;
+import vip.xiaonuo.sys.modular.relation.service.SysRelationService;
+import vip.xiaonuo.sys.modular.user.entity.SysUser;
+import vip.xiaonuo.sys.modular.user.service.SysUserService;
+import vip.xiaonuo.sys.modular.user.service.impl.SysUserServiceImpl;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 资源共享-群组控制器
+ *
+ * @author honorfire
+ * @date  2025/06/18 14:16
+ */
+@Api(tags = "资源共享-群组控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class ShareGroupController {
+
+    @Resource
+    private ShareGroupUserService shareGroupUserService;
+    @Resource
+    private DevConfigApi devConfigApi;
+    @Resource
+    private PinyinUtils pinyinUtils;
+    @Resource
+    private SysUserService sysUserService;
+    @Resource
+    private SysRelationService sysRelationService;
+
+    private static final String SNOWY_SYS_DEFAULT_PASSWORD_KEY = SysUserServiceImpl.SNOWY_SYS_DEFAULT_PASSWORD_KEY;
+
+    /**
+     * 资源共享-群组-分页列表
+     *
+     * @author honorfire
+     * @date  2025/06/18 14:16
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("资源共享-群组-分页列表")
+//    //@SaCheckPermission("/disk/sharegroup/page")
+    @GetMapping("/disk/sharegroup/page")
+    public CommonResult<Page<Map<String,Object>>> page(ShareGroupUserPageParam shareGroupUserPageParam, HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("userId", StpLoginUserUtil.getLoginUser().getId());
+        Page<Map<String,Object>> groupList=sysUserService.getGroupList(param);
+        return CommonResult.data(groupList);
+    }
+
+    /**
+     * 资源共享-群组-新增
+     *
+     * @author honorfire
+     * @date  2025/06/18 14:16
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("资源共享-群组-新增")
+    @CommonLog("资源共享-群组-新增")
+//    //@SaCheckPermission("/disk/sharegroup/add")
+    @PostMapping("/disk/sharegroup/add")
+    public CommonResult<String> add(@RequestBody @Valid ShareGroupAddParam shareGroupAddParam) {
+        //1.先创建一个角色为群组的用户
+        SysUser groupUser = new SysUser();
+        groupUser.setName(shareGroupAddParam.getName());
+        //设置账号,账号采取文字拼音加时间戳
+        String groupUserAccount=pinyinUtils.toFirstLetters(shareGroupAddParam.getName())+System.currentTimeMillis();
+        groupUser.setAccount(groupUserAccount);
+        //设置群组默认组织、职位
+        groupUser.setOrgId("100");
+        groupUser.setPositionId("100");
+        //资源库专用账号
+        groupUser.setIsResourceaccount("1");
+        // 设置默认头像
+        groupUser.setAvatar(CommonAvatarUtil.generateImg(groupUser.getName()));
+        // 设置默认密码
+        groupUser.setPassword(CommonCryptogramUtil.doHashValue(devConfigApi.getValueByKey(SNOWY_SYS_DEFAULT_PASSWORD_KEY)));
+        // 设置状态
+        groupUser.setUserStatus(SysUserStatusEnum.ENABLE.getValue());
+        //先保存一下该群组”用户“
+        groupUser=sysUserService.addOne(groupUser);
+
+        //2.存入该群组”用户“特定职务
+        sysRelationService.saveRelationWithAppend(groupUser.getId(),"101",SysRelationCategoryEnum.SYS_USER_HAS_ROLE.getValue());
+
+        //3.存入群组关系
+        String userIdsStr=shareGroupAddParam.getUserIds();
+        String[] userIdList = userIdsStr.split(",");
+        List<ShareGroupUser> shareGroupUserList=new ArrayList<>();
+        //首先添加创建群组的人
+        ShareGroupUser ownGroupUser=new ShareGroupUser();
+        ownGroupUser.setUserId(StpLoginUserUtil.getLoginUser().getId());
+        ownGroupUser.setGroupId(groupUser.getId());
+        ownGroupUser.setIsCreater("1");
+        shareGroupUserList.add(ownGroupUser);
+        for(String userId:userIdList)
+        {
+            ShareGroupUser otherGroupUser=new ShareGroupUser();
+            otherGroupUser.setUserId(userId);
+            otherGroupUser.setGroupId(groupUser.getId());
+            otherGroupUser.setIsCreater("0");
+            shareGroupUserList.add(otherGroupUser);
+        }
+
+        shareGroupUserService.addBatch(shareGroupUserList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 资源共享-群组-编辑
+     *
+     * @author honorfire
+     * @date  2025/06/18 14:16
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("资源共享-群组-编辑")
+    @CommonLog("资源共享-群组-编辑")
+//    //@SaCheckPermission("/disk/sharegroup/edit")
+    @PostMapping("/disk/sharegroup/edit")
+    public CommonResult<String> edit(@RequestBody @Valid ShareGroupEditParam shareGroupEditParam) {
+        //1.先创建一个角色为群组的用户
+        SysUser groupUser = new SysUser();
+        if(StringUtils.isNotEmpty(shareGroupEditParam.getName()))
+        {
+            groupUser.setName(shareGroupEditParam.getName());
+            //设置账号,账号采取文字拼音加时间戳
+            String groupUserAccount=pinyinUtils.toFirstLetters(shareGroupEditParam.getName())+System.currentTimeMillis();
+        }
+        //先保存一下该群组”用户“
+        groupUser=sysUserService.editOne(groupUser);
+
+        //2.群组用户id不为空的情况,删除旧群组关系,重新存入
+        List<ShareGroupUser> shareGroupUserList=new ArrayList<>();
+        if(StringUtils.isNotEmpty(shareGroupEditParam.getUserIds()))
+        {
+            //删除旧群组id
+            //查询旧关联id
+            Map queryDeleteGroupUserMap=new HashMap();
+            queryDeleteGroupUserMap.put("groupId",groupUser.getId());
+            List<Map<String, Object>> groupUserList= shareGroupUserService.queryGroupUserList(queryDeleteGroupUserMap);
+            List<String> deleteGroupUserIdList=new ArrayList<>();
+            for(Map<String, Object> deleteGroupUser:groupUserList)
+            {
+                deleteGroupUserIdList.add(deleteGroupUser.get("groupUserId").toString());
+            }
+            shareGroupUserService.deleteByIds(deleteGroupUserIdList);
+
+            //重新存入群组关系
+            String userIdsStr=shareGroupEditParam.getUserIds();
+            String[] userIdList = userIdsStr.split(",");
+            //首先添加创建群组的人
+            ShareGroupUser ownGroupUser=new ShareGroupUser();
+            ownGroupUser.setUserId(StpLoginUserUtil.getLoginUser().getId());
+            ownGroupUser.setGroupId(groupUser.getId());
+            ownGroupUser.setIsCreater("1");
+            shareGroupUserList.add(ownGroupUser);
+            for(String userId:userIdList)
+            {
+                ShareGroupUser otherGroupUser=new ShareGroupUser();
+                otherGroupUser.setUserId(userId);
+                otherGroupUser.setGroupId(groupUser.getId());
+                otherGroupUser.setIsCreater("0");
+                shareGroupUserList.add(otherGroupUser);
+            }
+        }
+
+        shareGroupUserService.addBatch(shareGroupUserList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 资源共享-群组-删除
+     *
+     * @author honorfire
+     * @date  2025/06/18 14:16
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("资源共享-群组-删除")
+    @CommonLog("资源共享-群组-删除")
+//    //@SaCheckPermission("/disk/sharegroup/delete")
+    @PostMapping("/disk/sharegroup/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<ShareGroupIdParam> shareGroupIdParamList) {
+        List<String> groupIdList = CollStreamUtil.toList(shareGroupIdParamList, ShareGroupIdParam::getGroupId);
+        sysUserService.deleteByIds(groupIdList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 资源共享-群组-详情
+     *
+     * @author honorfire
+     * @date  2025/06/18 14:16
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("资源共享-群组-详情")
+//    //@SaCheckPermission("/disk/sharegroup/detail")
+    @GetMapping("/disk/sharegroup/detail")
+    public CommonResult<Map<String,Object>> detail(@Valid ShareGroupIdParam shareGroupIdParam) {
+        Map param=new HashMap();
+        Map<String,Object> result= new HashMap<>();
+        //查回群组基本信息
+        SysUser group=sysUserService.queryEntity(shareGroupIdParam.getGroupId());
+        result.put("groupId",group.getId());
+        result.put("groupName",group.getName());
+        result.put("groupAccount",group.getAccount());
+        //查回群组所有关联人员
+        Map queryGroupUserMap=new HashMap();
+        queryGroupUserMap.put("groupId",group);
+        List<Map<String, Object>> groupUserList= shareGroupUserService.queryGroupUserList(queryGroupUserMap);
+        result.put("groupUserList",groupUserList);
+
+        return CommonResult.data(result);
+    }
+
+}

+ 166 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/StorageController.java

@@ -0,0 +1,166 @@
+package vip.xiaonuo.disk.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.qiwenshare.common.result.RestResult;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.disk.domain.CapacityAuditInfo;
+import vip.xiaonuo.disk.domain.StorageBean;
+import vip.xiaonuo.disk.dto.storage.GetUserStorageDTO;
+import vip.xiaonuo.disk.service.IStorageService;
+import vip.xiaonuo.disk.util.TableDataInfo;
+import vip.xiaonuo.disk.vo.storage.CapacityAuditInfoVo;
+import vip.xiaonuo.disk.vo.storage.StorageBeanVO;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ * 存储空间 controller
+ *
+ * @author jinjilong
+ */
+@RestController
+@Slf4j
+public class StorageController {
+
+    @Resource
+    private IStorageService storageService;
+
+
+    /**
+     * 查询当前默认创建空间大小 单位MB
+     *
+     * @return 默认创建空间大小
+     */
+    @GetMapping("/storage/getDefaultStorage")
+    public CommonResult<Long> getDefaultStorage() {
+        return storageService.getDefaultStorage();
+    }
+
+    /**
+     * 修改当前默认创建空间大小 单位MB
+     *
+     * @return 修改结果
+     */
+    @GetMapping("/storage/updateDefaultStorage")
+    public CommonResult<String> updateDefaultStorage(@RequestParam(value = "size") Long size) {
+        return storageService.updateDefaultStorage(size);
+    }
+
+
+    /**
+     * 查询所有用户的存储空间大小 单位MB
+     *
+     * @param getUserStorageDTO 查询条件
+     *
+     * @return 所有用户的存储空间大小
+     */
+    @PostMapping("/storage/getUserStorageList")
+    public CommonResult<TableDataInfo<StorageBeanVO>> getUserStorageList(@RequestBody GetUserStorageDTO getUserStorageDTO) {
+        return storageService.getUserStorageList(getUserStorageDTO);
+    }
+
+    /**
+     * 更改用户的存储空间大小 单位MB
+     *
+     * @param getUserStorageDTO 更改条件
+     *
+     * @return 更改结果
+     */
+    @PostMapping("/storage/updateUserStorage")
+    public CommonResult<String> updateUserStorage(@RequestBody GetUserStorageDTO getUserStorageDTO) {
+        return storageService.updateUserStorage(getUserStorageDTO);
+    }
+
+
+    /**
+     * 用户查看自己的存储空间大小 单位MB
+     *
+     * @return 用户存储空间大小
+     */
+    @GetMapping("/storage/getUserStorage")
+    public CommonResult<Long> getUserStorage() {
+        return storageService.getUserStorage();
+    }
+
+
+    /**
+     * 提交改变存储空间大小的申请
+     *
+     * @param capacityAuditInfo 申请信息
+     *
+     * @return 提交结果
+     */
+    @PostMapping("/storage/applyChangeStorage")
+    public CommonResult<String> applyChangeStorage(@RequestBody CapacityAuditInfo capacityAuditInfo) {
+        return storageService.applyChangeStorage(capacityAuditInfo);
+    }
+
+    /**
+     * 查询存储空间大小变更申请列表
+     *
+     * @param auditState 申请状态 0待审核 1审核通过 2拒绝
+     * @param pageNum 页码
+     * @param pageSize 页大小
+     * @param usernameOrNickname 用户名或昵称
+     *
+     * @return 存储空间大小变更申请列表
+     */
+    @GetMapping("/storage/getCapacityAuditList")
+    public CommonResult<TableDataInfo<CapacityAuditInfoVo>> getCapacityAuditList(@RequestParam(value = "pageNum") Integer pageNum,
+                                                                         @RequestParam(value = "pageSize") Integer pageSize,
+                                                                         @RequestParam(value = "auditState", required = false) Integer auditState,
+                                                                         @RequestParam(value = "usernameOrNickname", required = false) String usernameOrNickname) {
+        return storageService.getCapacityAuditList(pageNum, pageSize, auditState, usernameOrNickname);
+    }
+
+    /**
+     * 查询存储空间大小变更申请详情
+     *
+     * @param id 申请ID
+     * @return 申请详情
+     */
+    @GetMapping("/storage/getCapacityAuditDetail")
+    public CommonResult<CapacityAuditInfoVo> getCapacityAuditDetail(@RequestParam(value = "id") String id) {
+        return storageService.getCapacityAuditDetail(id);
+    }
+
+
+    /**
+     * 审核扩容申请
+     *
+     * @param id 申请ID
+     * @param auditState 审核状态
+     * @param rejectReason 拒绝原因
+     * @return 审核结果
+     */
+    @GetMapping("/storage/auditCapacityAudit")
+    public CommonResult<String> auditCapacityAudit(@RequestParam(value = "id") String id,
+                                                   @RequestParam(value = "auditState") Integer auditState,
+                                                   @RequestParam(value = "rejectReason", required = false) String rejectReason) {
+        return storageService.auditCapacityAudit(id, auditState, rejectReason);
+    }
+
+
+    /**
+     * 用户查看自己申请记录列表
+     *
+     * @param pageNum 页码
+     * @param pageSize 页大小
+     * @param auditState 申请状态 0待审核 1审核通过 2拒绝
+     *
+     * @return 申请记录列表
+     */
+    @GetMapping("/storage/getUserCapacityAuditList")
+    public CommonResult<TableDataInfo<CapacityAuditInfoVo>> getUserCapacityAuditList(@RequestParam(value = "pageNum") Integer pageNum,
+                                                                             @RequestParam(value = "pageSize") Integer pageSize,
+                                                                             @RequestParam(value = "auditState", required = false) Integer auditState) {
+        return storageService.getUserCapacityAuditList(pageNum, pageSize, auditState);
+    }
+
+
+
+}

+ 147 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/StorageReController.java

@@ -0,0 +1,147 @@
+package vip.xiaonuo.disk.controller;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.disk.domain.CapacityReAuditInfo;
+import vip.xiaonuo.disk.dto.storage.GetUserStorageDTO;
+import vip.xiaonuo.disk.service.IStorageReService;
+import vip.xiaonuo.disk.util.TableDataInfo;
+import vip.xiaonuo.disk.vo.storage.CapacityReAuditInfoVo;
+import vip.xiaonuo.disk.vo.storage.StorageReBeanVO;
+
+import javax.annotation.Resource;
+@RestController
+@Slf4j
+public class StorageReController {
+
+    @Resource
+    private IStorageReService storageReService;
+
+
+    /**
+     * 查询当前默认创建空间大小 单位MB
+     *
+     * @return 默认创建空间大小
+     */
+    @GetMapping("/storagere/getDefaultStorage")
+    public CommonResult<Long> getDefaultStorage() {
+        return storageReService.getDefaultStorage();
+    }
+
+    /**
+     * 修改当前默认创建空间大小 单位MB
+     *
+     * @return 修改结果
+     */
+    @GetMapping("/storagere/updateDefaultStorage")
+    public CommonResult<String> updateDefaultStorage(@RequestParam(value = "size") Long size) {
+        return storageReService.updateDefaultStorage(size);
+    }
+
+
+    /**
+     * 查询所有用户的存储空间大小 单位MB
+     *
+     * @param getUserStorageDTO 查询条件
+     * @return 所有用户的存储空间大小
+     */
+    @PostMapping("/storagere/getUserStorageList")
+    public CommonResult<TableDataInfo<StorageReBeanVO>> getUserStorageList(@RequestBody GetUserStorageDTO getUserStorageDTO) {
+        return storageReService.getUserStorageList(getUserStorageDTO);
+    }
+
+    /**
+     * 更改用户的存储空间大小 单位MB
+     *
+     * @param getUserStorageDTO 更改条件
+     * @return 更改结果
+     */
+    @PostMapping("/storagere/updateUserStorage")
+    public CommonResult<String> updateUserStorage(@RequestBody GetUserStorageDTO getUserStorageDTO) {
+        return storageReService.updateUserStorage(getUserStorageDTO);
+    }
+
+
+    /**
+     * 用户查看自己的存储空间大小 单位MB
+     *
+     * @return 用户存储空间大小
+     */
+    @GetMapping("/storagere/getUserStorage")
+    public CommonResult<Long> getUserStorage() {
+        return storageReService.getUserStorage();
+    }
+
+
+    /**
+     * 提交改变存储空间大小的申请
+     *
+     * @param  capacityReAuditInfo
+     * @return 提交结果
+     */
+    @PostMapping("/storagere/applyChangeStorage")
+    public CommonResult<String> applyChangeStorage(@RequestBody CapacityReAuditInfo capacityReAuditInfo) {
+        return storageReService.applyChangeStorage(capacityReAuditInfo);
+    }
+
+    /**
+     * 查询存储空间大小变更申请列表
+     *
+     * @param auditState         申请状态 0待审核 1审核通过 2拒绝
+     * @param pageNum            页码
+     * @param pageSize           页大小
+     * @param usernameOrNickname 用户名或昵称
+     * @return 存储空间大小变更申请列表
+     */
+    @GetMapping("/storagere/getCapacityAuditList")
+    public CommonResult<TableDataInfo<CapacityReAuditInfoVo>> getCapacityAuditList(@RequestParam(value = "pageNum") Integer pageNum,
+                                                                                   @RequestParam(value = "pageSize") Integer pageSize,
+                                                                                   @RequestParam(value = "auditState", required = false) Integer auditState,
+                                                                                   @RequestParam(value = "usernameOrNickname", required = false) String usernameOrNickname) {
+        return storageReService.getCapacityAuditList(pageNum, pageSize, auditState, usernameOrNickname);
+    }
+
+    /**
+     * 查询存储空间大小变更申请详情
+     *
+     * @param id 申请ID
+     * @return 申请详情
+     */
+    @GetMapping("/storagere/getCapacityAuditDetail")
+    public CommonResult<CapacityReAuditInfoVo> getCapacityAuditDetail(@RequestParam(value = "id") String id) {
+        return storageReService.getCapacityAuditDetail(id);
+    }
+
+
+    /**
+     * 审核扩容申请
+     *
+     * @param id           申请ID
+     * @param auditState   审核状态
+     * @param rejectReason 拒绝原因
+     * @return 审核结果
+     */
+    @GetMapping("/storagere/auditCapacityAudit")
+    public CommonResult<String> auditCapacityAudit(@RequestParam(value = "id") String id,
+                                                   @RequestParam(value = "auditState") Integer auditState,
+                                                   @RequestParam(value = "rejectReason", required = false) String rejectReason) {
+        return storageReService.auditCapacityAudit(id, auditState, rejectReason);
+    }
+
+
+    /**
+     * 用户查看自己申请记录列表
+     *
+     * @param pageNum    页码
+     * @param pageSize   页大小
+     * @param auditState 申请状态 0待审核 1审核通过 2拒绝
+     * @return 申请记录列表
+     */
+    @GetMapping("/storagere/getUserCapacityAuditList")
+    public CommonResult<TableDataInfo<CapacityReAuditInfoVo>> getUserCapacityAuditList(@RequestParam(value = "pageNum") Integer pageNum,
+                                                                                     @RequestParam(value = "pageSize") Integer pageSize,
+                                                                                     @RequestParam(value = "auditState", required = false) Integer auditState) {
+        return storageReService.getUserCapacityAuditList(pageNum, pageSize, auditState);
+    }
+}

+ 206 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/StudyBehaviorProgressController.java

@@ -0,0 +1,206 @@
+package vip.xiaonuo.disk.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.disk.domain.HomeWorkResult;
+import vip.xiaonuo.disk.service.StudyBehaviorProgressService;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 统计-学习行为分析控制器
+ *
+ * @author honorfire
+ * @date  2025/06/18 14:16
+ */
+@Api(tags = "统计-学习行为分析控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class StudyBehaviorProgressController {
+
+    @Resource
+    private StudyBehaviorProgressService studyBehaviorProgressService;
+
+    /**
+     * 学习行为分析-课程访问统计
+     *
+     * @author honorfire
+     * @date  2025/07/11 18:52
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("学习行为分析-课程访问统计")
+    @GetMapping("/disk/studyBehavior/courseVisitProgress")
+    public CommonResult<Map<String,Object>> courseVisitProgress(HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("orgId", req.getParameter("orgId"));
+        param.put("startTime", req.getParameter("startTime"));
+        param.put("endTime", req.getParameter("endTime"));
+
+        Map<String,Object> result=studyBehaviorProgressService.getCourseVisitProgress(param);
+        return CommonResult.data(result);
+    }
+
+    /**
+     * 学习行为分析-用户登录统计
+     *
+     * @author honorfire
+     * @date  2025/07/11 18:52
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("学习行为分析-用户登录统计")
+    @GetMapping("/disk/studyBehavior/userLoginProgress")
+    public CommonResult<Map<String,Object>> userLoginProgress(HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("orgId", req.getParameter("orgId"));
+        param.put("startTime", req.getParameter("startTime"));
+        param.put("endTime", req.getParameter("endTime"));
+
+        Map<String,Object> result=studyBehaviorProgressService.getUserLoginProgress(param);
+        return CommonResult.data(result);
+    }
+
+    /**
+     * 学习行为分析-观看时长统计
+     *
+     * @author honorfire
+     * @date  2025/07/11 18:52
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("学习行为分析-观看时长统计")
+    @GetMapping("/disk/studyBehavior/watchDurationProgress")
+    public CommonResult<Map<String,Object>> watchDurationProgress(HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("orgId", req.getParameter("orgId"));
+        param.put("startTime", req.getParameter("startTime"));
+        param.put("endTime", req.getParameter("endTime"));
+
+        Map<String,Object> result=studyBehaviorProgressService.getWatchDurationProgress(param);
+        return CommonResult.data(result);
+    }
+
+    /**
+     * 学习行为分析-用户登录时段分布
+     *
+     * @author honorfire
+     * @date  2025/07/11 18:52
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("学习行为分析-用户登录时段分布")
+    @GetMapping("/disk/studyBehavior/loginFrameDistribution")
+    public CommonResult<Page<Map<String,Object>>> loginFrameDistribution(HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("orgId", req.getParameter("orgId"));
+        param.put("startTime", req.getParameter("startTime"));
+        param.put("endTime", req.getParameter("endTime"));
+        Page<Map<String,Object>> list=studyBehaviorProgressService.getLoginFrameDistribution(param);
+        return CommonResult.data(list);
+    }
+
+    /**
+     * 学习行为分析-课程访问热度排行
+     *
+     * @author honorfire
+     * @date  2025/07/11 18:52
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("学习行为分析-课程访问热度排行")
+    @GetMapping("/disk/studyBehavior/courseVisitHeatRank")
+    public CommonResult<Page<Map<String,Object>>> courseVisitHeatRank(HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("orgId", req.getParameter("orgId"));
+        param.put("startTime", req.getParameter("startTime"));
+        param.put("endTime", req.getParameter("endTime"));
+        Page<Map<String,Object>> list=studyBehaviorProgressService.getCourseVisitHeatRank(param);
+        return CommonResult.data(list);
+    }
+
+    /**
+     * 学习行为分析-学院课程详细统计
+     *
+     * @author honorfire
+     * @date  2025/07/11 18:52
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("学习行为分析-学院课程详细统计")
+    @GetMapping("/disk/studyBehavior/collegeCourseDetailProgress")
+    public CommonResult<Page<Map<String,Object>>> collegeCourseDetailProgress(HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("orgId", req.getParameter("orgId"));
+        param.put("startTime", req.getParameter("startTime"));
+        param.put("endTime", req.getParameter("endTime"));
+        Page<Map<String,Object>> list=studyBehaviorProgressService.getCollegeCourseDetailProgress(param);
+        return CommonResult.data(list);
+    }
+
+    /**
+     * 学习行为分析-学员维度分析
+     *
+     * @author honorfire
+     * @date  2025/07/11 18:52
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("学习行为分析-学员维度分析")
+    @GetMapping("/disk/studyBehavior/studentAnalyse")
+    public CommonResult<Page<Map<String,Object>>> studentAnalyse(HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("name", req.getParameter("name"));
+        Page<Map<String,Object>> list=studyBehaviorProgressService.getStudentAnalyse(param);
+        return CommonResult.data(list);
+    }
+
+    /**
+     * 学习行为分析-获得学生课程作业错误率
+     *
+     * @author honorfire
+     * @date  2025/07/11 18:52
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("学习行为分析-获得学生课程作业错误率")
+    @GetMapping("/disk/studyBehavior/studentHomeWorkErrorRate")
+    public CommonResult<Page<Map<String,Object>>> studentHomeWorkErrorRate(HttpServletRequest req) {
+        Map param =new HashMap();
+        //学生名称
+        param.put("name", req.getParameter("name"));
+        param.put("startTime", req.getParameter("startTime"));
+        param.put("endTime", req.getParameter("endTime"));
+        Page<Map<String,Object>> list=studyBehaviorProgressService.getStudentHomeWorkErrorRate(param);
+        return CommonResult.data(list);
+    }
+
+    /**
+     * 学习行为分析-作业情况
+     * 2025.10.30.这个是假接口,只是为了应对标合同数据挖掘所用
+     *
+     * @author honorfire
+     * @date  2025/07/11 18:52
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("学习行为分析-作业情况:作业相关(作业名称、所在知识点、提交次数、每次提交的分数、每次提交的错题、每个错题的错误选项与对应发生的比例、作业个人完成比例、班级人均完成比例等数据)")
+    @GetMapping("/disk/studyBehavior/homeWorkResult")
+    public CommonResult<Page<HomeWorkResult>> homeWorkResult(HttpServletRequest req) {
+        Map param =new HashMap();
+        //学生名称
+        param.put("name", req.getParameter("name"));
+        param.put("startTime", req.getParameter("startTime"));
+        param.put("endTime", req.getParameter("endTime"));
+        List<HomeWorkResult> list=new ArrayList<>();
+        Page<HomeWorkResult> page= new Page<>();
+        page.setRecords(list);
+        return CommonResult.data(page);
+    }
+
+
+}

+ 47 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/SysParamController.java

@@ -0,0 +1,47 @@
+package vip.xiaonuo.disk.controller;
+
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.qiwenshare.common.result.RestResult;
+import vip.xiaonuo.disk.service.ISysParamService;
+import vip.xiaonuo.disk.domain.SysParam;
+import vip.xiaonuo.disk.dto.param.QueryGroupParamDTO;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Tag(name = "系统参数管理")
+@RestController
+@RequestMapping("/param")
+public class SysParamController {
+    @Resource
+    ISysParamService sysParamService;
+
+    @Operation(summary = "查询系统参数组", tags = {"系统参数管理"})
+    @RequestMapping(value = "/grouplist", method = RequestMethod.GET)
+    @ResponseBody
+    public RestResult<Map> groupList(
+            @Parameter(description = "查询参数dto", required = false)
+                    QueryGroupParamDTO queryGroupParamDTO
+    ) {
+        List<SysParam> list = sysParamService.list(new QueryWrapper<SysParam>().lambda().eq(SysParam::getGroupName, queryGroupParamDTO.getGroupName()));
+        Map<String, Object> result = new HashMap<>();
+
+        for (SysParam sysParam : list) {
+            result.put(sysParam.getSysParamKey(), sysParam.getSysParamValue());
+        }
+
+        return RestResult.success().data(result);
+    }
+
+
+}

+ 85 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/TaskController.java

@@ -0,0 +1,85 @@
+package vip.xiaonuo.disk.controller;
+
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import vip.xiaonuo.disk.service.IShareFileService;
+import vip.xiaonuo.disk.component.FileDealComp;
+import vip.xiaonuo.disk.domain.ShareFile;
+import vip.xiaonuo.disk.domain.UserFile;
+import vip.xiaonuo.disk.io.QiwenFile;
+import vip.xiaonuo.disk.service.impl.UserFileService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Controller;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+@Slf4j
+@Controller
+public class TaskController {
+
+    @Resource
+    UserFileService userFileService;
+    @Resource
+    FileDealComp fileDealComp;
+    @Resource
+    IShareFileService shareFileService;
+    @Autowired
+    private ElasticsearchClient elasticsearchClient;
+
+
+    @Scheduled(fixedRate = 1000 * 60 * 60 * 24)
+    public void updateElasticSearch() {
+        List<UserFile> userfileList = userFileService.list(new QueryWrapper<UserFile>().eq("deleteFlag", 0));
+        for (int i = 0; i < userfileList.size(); i++) {
+            try {
+
+                QiwenFile ufopFile = new QiwenFile(userfileList.get(i).getFilePath(), userfileList.get(i).getFileName(), userfileList.get(i).getIsDir() == 1);
+                fileDealComp.restoreParentFilePath(ufopFile, userfileList.get(i).getUserId());
+                if (i % 1000 == 0 || i == userfileList.size() - 1) {
+                    log.info("目录健康检查进度:" + (i + 1) + "/" + userfileList.size());
+                }
+
+            } catch (Exception e) {
+                log.error(e.getMessage());
+            }
+        }
+        userfileList = userFileService.list(new QueryWrapper<UserFile>().eq("deleteFlag", 0));
+        for (UserFile userFile : userfileList) {
+            fileDealComp.uploadESByUserFileId(userFile.getUserFileId());
+        }
+
+    }
+
+    @Scheduled(fixedRate = Long.MAX_VALUE)
+    public void updateFilePath() {
+        List<UserFile> list = userFileService.list();
+        for (UserFile userFile : list) {
+            try {
+                String path = QiwenFile.formatPath(userFile.getFilePath());
+                if (!userFile.getFilePath().equals(path)) {
+                    userFile.setFilePath(path);
+                    userFileService.updateById(userFile);
+                }
+            } catch (Exception e) {
+                // ignore
+            }
+        }
+    }
+
+    @Scheduled(fixedRate = Long.MAX_VALUE)
+    public void updateShareFilePath() {
+        List<ShareFile> list = shareFileService.list();
+        for (ShareFile shareFile : list) {
+            try {
+                String path = QiwenFile.formatPath(shareFile.getShareFilePath());
+                shareFile.setShareFilePath(path);
+                shareFileService.updateById(shareFile);
+            } catch (Exception e) {
+                //ignore
+            }
+        }
+    }
+}

+ 169 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/TeachingActivityController.java

@@ -0,0 +1,169 @@
+package vip.xiaonuo.disk.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+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 vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.disk.service.TeachingActivityService;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Api(tags = "教学活动分析")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@RequestMapping("/teachingActivity")
+public class TeachingActivityController {
+    @Resource
+    private TeachingActivityService teachingActivityService;
+
+
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("文档观看统计")
+    @GetMapping("/documentStatistic")
+    public CommonResult<Map<String, Object>> documentStatistic(HttpServletRequest req) {
+        Map param = new HashMap();
+        param.put("courseId", req.getParameter("courseId"));
+        param.put("type", req.getParameter("type"));
+        param.put("startTime", req.getParameter("startTime"));
+        param.put("endTime", req.getParameter("endTime"));
+        return CommonResult.data(teachingActivityService.documentStatistic(param));
+    }
+
+
+
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("文档跳出分析")
+    @GetMapping("/documentJumpStatistic")
+    public CommonResult<Map<String, Object>> documentJumpStatistic(HttpServletRequest req) {
+        Map param = new HashMap();
+        param.put("courseId", req.getParameter("courseId"));
+        param.put("type", req.getParameter("type"));
+        param.put("startTime", req.getParameter("startTime"));
+        param.put("endTime", req.getParameter("endTime"));
+        return CommonResult.data(teachingActivityService.documentJumpStatistic(param));
+    }
+
+    /**
+     * 教学活动分析-讨论互动统计
+     *
+     * @author honorfire
+     * @date  2025/07/11 18:52
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("教学活动分析-讨论互动统计")
+    @GetMapping("/discussInteractionAnalyse")
+    public CommonResult<Map<String,Object>> discussInteractionAnalyse(HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("courseId", req.getParameter("courseId"));
+        param.put("startTime", req.getParameter("startTime"));
+        param.put("endTime", req.getParameter("endTime"));
+
+        Map<String,Object> result=teachingActivityService.getDiscussInteractionAnalyse(param);
+        return CommonResult.data(result);
+    }
+
+    /**
+     * 教学活动分析-学员每周发帖统计
+     *
+     * @author honorfire
+     * @date  2025/07/11 18:52
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("教学活动分析-学员每周发帖统计")
+    @GetMapping("/stuWeeklyPostProgress")
+    public CommonResult<Map<String,Object>> stuWeeklyPostProgress(HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("courseId", req.getParameter("courseId"));
+        //该接口只查询最近一周,不受时间检索
+//        param.put("startTime", req.getParameter("startTime"));
+//        param.put("endTime", req.getParameter("endTime"));
+
+        Map<String,Object> result=teachingActivityService.getStuWeeklyPostProgress(param);
+        return CommonResult.data(result);
+    }
+
+    /**
+     * 教学活动分析-教员每周发帖统计
+     *
+     * @author honorfire
+     * @date  2025/07/11 18:52
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("教学活动分析-教员每周发帖统计")
+    @GetMapping("/teachWeeklyPostProgress")
+    public CommonResult<Map<String,Object>> teachWeeklyPostProgress(HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("courseId", req.getParameter("courseId"));
+        //该接口只查询最近一周,不受时间检索
+//        param.put("startTime", req.getParameter("startTime"));
+//        param.put("endTime", req.getParameter("endTime"));
+
+        Map<String,Object> result=teachingActivityService.getTeachWeeklyPostProgress(param);
+        return CommonResult.data(result);
+    }
+
+    /**
+     * 教学活动分析-课程讨论详情
+     *R
+     * @author honorfire
+     * @date  2025/07/11 18:52
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("教学活动分析-课程讨论详情")
+    @GetMapping("/courseDiscussDetail")
+    public CommonResult<Page<Map<String,Object>>> courseDiscussDetail(HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("courseId", req.getParameter("courseId"));
+        param.put("startTime", req.getParameter("startTime"));
+        param.put("endTime", req.getParameter("endTime"));
+        Page<Map<String,Object>> list=teachingActivityService.courseDiscussDetail(param);
+        return CommonResult.data(list);
+    }
+
+
+
+
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("开课每日访问人数统计")
+    @GetMapping("/courseOpenStatistic")
+    public CommonResult<List<Map<String, Object>>> courseOpenStatistic(HttpServletRequest req) {
+        Map param = new HashMap();
+        param.put("courseId", req.getParameter("courseId"));
+        param.put("type", req.getParameter("type"));
+        //该接口只查询最近一周,不受时间检索
+//        param.put("startTime", req.getParameter("startTime"));
+//        param.put("endTime", req.getParameter("endTime"));
+        return CommonResult.data(teachingActivityService.courseOpenStatistic(param));
+    }
+
+
+    /**
+     * 教学活动分析-文档观看详细统计(讲义查看状况)
+     *
+     * @author honorfire
+     * @date  2025/07/11 18:52
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("文档观看详细统计")
+    @GetMapping("/documentDetailStatistic")
+    public CommonResult<Page<Map<String, Object>>> documentDetailStatistic(HttpServletRequest req) {
+        Map param = new HashMap();
+        param.put("courseId", req.getParameter("courseId"));
+        param.put("type", req.getParameter("type"));
+        param.put("startTime", req.getParameter("startTime"));
+        param.put("endTime", req.getParameter("endTime"));
+//        teachingActivityService.documentDetailStatistic(param);
+        Page<Map<String, Object>> list = teachingActivityService.documentWatchDetailStatistic(param);
+        return CommonResult.data(list);
+    }
+}

+ 55 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/TestWebSocket.java

@@ -0,0 +1,55 @@
+package vip.xiaonuo.disk.controller;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import javax.websocket.*;
+import javax.websocket.server.PathParam;
+import javax.websocket.server.ServerEndpoint;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 测试websocket
+ *
+ * @author jinjilong
+ */
+@Slf4j
+@Component
+@ServerEndpoint("/webSocket/auto/111")
+public class TestWebSocket {
+
+    private Session session;
+
+    private String userId="1";
+
+
+
+    private static ConcurrentHashMap<String, TestWebSocket> meetingWebSocket = new ConcurrentHashMap<>();
+
+    @OnOpen
+    public void onOpen(Session session, @PathParam(value = "userId") String userId) {
+        System.out.println("用户:" + userId + "连接成功");
+
+    }
+
+
+    @OnClose
+    public void onClose() {
+
+    }
+
+
+    @OnMessage
+    public void onMessage(String message) {
+
+
+
+    }
+
+    @OnError
+    public void onError(Session session, Throwable throwable) {
+
+    }
+
+
+}

+ 46 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/TranscodingController.java

@@ -0,0 +1,46 @@
+package vip.xiaonuo.disk.controller;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.disk.dto.file.TranscodingReqDTO;
+import vip.xiaonuo.disk.dto.file.TranscodingResourceReqDTO;
+import vip.xiaonuo.disk.service.TranscodingService;
+
+import javax.annotation.Resource;
+
+/**
+ * 资源转码controller
+ *
+ * @author jinjilong
+ */
+@RestController
+@Slf4j
+@Validated
+public class TranscodingController {
+
+    @Resource
+    private TranscodingService transcodingService;
+
+
+    /**
+     * 视频资源转码
+     *
+     * @param transcodingReqDTO 转码请求
+     * @return 转码结果
+     *
+     * @author jinjilong
+     */
+    @PostMapping("/transcoding/file")
+    public CommonResult<String> transcodingVideo(@RequestBody TranscodingReqDTO transcodingReqDTO) {
+        return transcodingService.transcodingVideo(transcodingReqDTO);
+    }
+    @PostMapping("/transcodingResource/file")
+    public CommonResult<String> transcodingResource(@RequestBody TranscodingResourceReqDTO transcodingResourceReqDTO) {
+        return transcodingService.transcodingResource(transcodingResourceReqDTO);
+    }
+
+}

+ 171 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/UserCommentController.java

@@ -0,0 +1,171 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.disk.param.UserCommentAddParam;
+import vip.xiaonuo.disk.param.UserCommentEditParam;
+import vip.xiaonuo.disk.param.UserCommentIdParam;
+import vip.xiaonuo.disk.param.UserCommentPageParam;
+import vip.xiaonuo.disk.service.UserCommentService;
+import vip.xiaonuo.disk.vo.comment.UserCommentVo;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+/**
+ * user_comment控制器
+ *
+ * @author pans
+ * @date  2025/06/27 11:07
+ */
+@Api(tags = "user_comment控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class UserCommentController {
+
+    @Resource
+    private UserCommentService userCommentService;
+
+    /**
+     * 最新评论list获取
+     *
+     * @author pans
+     * @date  2025/06/27 11:07
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取user_comment分页")
+    @GetMapping("/disk/comment/page")
+    public CommonResult<Page<UserCommentVo>> page(UserCommentPageParam userCommentPageParam) {
+        return CommonResult.data(userCommentService.page(userCommentPageParam));
+    }
+
+
+    /**
+     * 热门评论list获取
+     *
+     * @author pans
+     * @date  2025/06/27 11:07
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("获取user_comment分页")
+    @GetMapping("/disk/comment/pageList")
+    public CommonResult<Page<UserCommentVo>> pageList(UserCommentPageParam userCommentPageParam) {
+        return CommonResult.data(userCommentService.pageList(userCommentPageParam));
+    }
+
+    /**
+     * 添加user_comment
+     *
+     * @author pans
+     * @date  2025/06/27 11:07
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("添加user_comment")
+    @CommonLog("添加user_comment")
+    @PostMapping("/disk/comment/add")
+    public CommonResult<String> add(@RequestBody @Valid UserCommentAddParam userCommentAddParam) {
+        // 检查敏感词
+        String titleIsIllegal = userCommentService.filterSensitiveWordsTitle(userCommentAddParam.getCommentName());
+        if("1".equals(titleIsIllegal)) {
+            return CommonResult.error("内容含有敏感词,请重新编辑发送");
+        }
+        userCommentService.add(userCommentAddParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 编辑user_comment
+     *
+     * @author pans
+     * @date  2025/06/27 11:07
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("编辑user_comment")
+    @CommonLog("编辑user_comment")
+    @PostMapping("/disk/comment/edit")
+    public CommonResult<String> edit(@RequestBody @Valid UserCommentEditParam userCommentEditParam) {
+        // 检查敏感词
+        String titleIsIllegal = userCommentService.filterSensitiveWordsTitle(userCommentEditParam.getCommentName());
+        if("1".equals(titleIsIllegal)) {
+            return CommonResult.error("内容含有敏感词,请重新编辑发送");
+        }
+        userCommentService.edit(userCommentEditParam);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 删除user_comment
+     *
+     * @author pans
+     * @date  2025/06/27 11:07
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("删除user_comment")
+    @CommonLog("删除user_comment")
+    @PostMapping("/disk/comment/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<UserCommentIdParam> userCommentIdParamList) {
+        userCommentService.delete(userCommentIdParamList);
+        return CommonResult.ok();
+    }
+
+
+
+    /**
+     * 点赞
+     *
+     * @author pans
+     * @date  2025/06/27 11:07
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("点赞")
+    @CommonLog("点赞")
+    @GetMapping("/disk/comment/give")
+    public CommonResult<String> edit(@Valid @NotNull(message="评论id不能为空") Integer id) {
+        userCommentService.give(id);
+        return CommonResult.ok();
+    }
+
+
+    /**
+     * 取消点赞
+     *
+     * @author pans
+     * @date  2025/06/27 11:07
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("点赞")
+    @CommonLog("点赞")
+    @GetMapping("/disk/comment/giveCancel")
+    public CommonResult<String> giveCancel(@Valid @NotNull(message="评论id不能为空") Integer id) {
+        return CommonResult.ok(userCommentService.giveCancel(id));
+    }
+
+
+}

+ 153 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/UserController.java

@@ -0,0 +1,153 @@
+package vip.xiaonuo.disk.controller;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.qiwenshare.common.anno.MyLog;
+import com.qiwenshare.common.result.RestResult;
+import com.qiwenshare.common.util.DateUtil;
+import com.qiwenshare.common.util.HashUtils;
+import vip.xiaonuo.auth.core.pojo.SaBaseLoginUser;
+import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
+import vip.xiaonuo.disk.service.IUserLoginInfoService;
+import vip.xiaonuo.disk.service.IUserService;
+import vip.xiaonuo.disk.component.JwtComp;
+import vip.xiaonuo.disk.domain.UserLoginInfo;
+import vip.xiaonuo.disk.domain.user.UserBean;
+import vip.xiaonuo.disk.dto.user.RegisterDTO;
+import vip.xiaonuo.disk.vo.user.UserLoginVo;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import java.util.HashMap;
+import java.util.Map;
+
+
+@Tag(name = "user", description = "该接口为用户接口,主要做用户登录,注册和校验token")
+@RestController
+@Slf4j
+@RequestMapping("/user")
+public class UserController {
+
+    @Resource
+    IUserService userService;
+    @Resource
+    IUserLoginInfoService userLoginInfoService;
+    @Resource
+    JwtComp jwtComp;
+
+    public static Map<String, String> verificationCodeMap = new HashMap<>();
+
+
+    public static final String CURRENT_MODULE = "用户管理";
+
+    @Operation(summary = "用户注册", description = "注册账号", tags = {"user"})
+    @PostMapping(value = "/register")
+    @MyLog(operation = "用户注册", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult<String> addUser(@Valid @RequestBody RegisterDTO registerDTO) {
+        RestResult<String> restResult = null;
+        UserBean userBean = new UserBean();
+        BeanUtil.copyProperties(registerDTO, userBean);
+        restResult = userService.registerUser(userBean);
+        return restResult;
+    }
+
+    @Operation(summary = "用户登录", description = "用户登录认证后才能进入系统", tags = {"user"})
+    @GetMapping("/login")
+    @MyLog(operation = "用户登录", module = CURRENT_MODULE)
+    @ResponseBody
+    public RestResult<UserLoginVo> userLogin(
+            @Parameter(description = "登录手机号") String telephone,
+            @Parameter(description = "登录密码") String password){
+        RestResult<UserLoginVo> restResult = new RestResult<UserLoginVo>();
+        String salt = userService.getSaltByTelephone(telephone);
+        String hashPassword = HashUtils.hashHex("MD5", password, salt, 1024);
+        UserBean result = userService.selectUserByTelephoneAndPassword(telephone, hashPassword);
+        if (result == null) {
+            return RestResult.fail().message("手机号或密码错误!");
+        }
+
+        Map<String, Object> param = new HashMap<>();
+        param.put("userId", result.getUserId());
+        String token = "";
+        try {
+            token = jwtComp.createJWT(param);
+        } catch (Exception e) {
+            log.info("登录失败:{}", e);
+            return RestResult.fail().message("创建token失败!");
+        }
+        UserBean sessionUserBean = userService.findUserInfoByTelephone(telephone);
+        if (sessionUserBean.getAvailable() != null && sessionUserBean.getAvailable() == 0) {
+            return RestResult.fail().message("用户已被禁用");
+        }
+        UserLoginVo userLoginVo = new UserLoginVo();
+        BeanUtil.copyProperties(sessionUserBean, userLoginVo);
+        userLoginVo.setToken("Bearer " + token);
+        restResult.setData(userLoginVo);
+        restResult.setSuccess(true);
+        restResult.setCode(200001);
+        return restResult;
+
+    }
+
+    @Operation(summary = "检查用户登录信息", description = "验证token的有效性", tags = {"user"})
+    @GetMapping("/checkuserlogininfo")
+    @ResponseBody
+    public RestResult<UserLoginVo> checkUserLoginInfo(@RequestHeader("token") String token) {
+        UserLoginVo userLoginVo = new UserLoginVo();
+//        String userId = userService.getUserIdByToken(token);
+        String userId = StpLoginUserUtil.getLoginUser().getId();
+
+        if (StringUtils.isNotEmpty(userId)) {
+            LambdaQueryWrapper<UserLoginInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>();
+            lambdaQueryWrapper.eq(UserLoginInfo::getUserId, userId);
+            lambdaQueryWrapper.likeRight(UserLoginInfo::getUserloginDate, DateUtil.getCurrentTime().substring(0, 10));
+            userLoginInfoService.remove(lambdaQueryWrapper);
+            UserLoginInfo userLoginInfo = new UserLoginInfo();
+            userLoginInfo.setUserId(userId);
+            userLoginInfo.setUserloginDate(DateUtil.getCurrentTime());
+            userLoginInfoService.save(userLoginInfo);
+            UserBean user = userService.getById(userId);
+            BeanUtil.copyProperties(user, userLoginVo);
+            if (StringUtils.isEmpty(user.getWxOpenId())) {
+                userLoginVo.setHasWxAuth(false);
+            } else {
+                userLoginVo.setHasWxAuth(true);
+            }
+            return RestResult.success().data(userLoginVo);
+
+        } else {
+            return RestResult.fail().message("用户暂未登录");
+        }
+
+    }
+
+    @Operation(summary = "检查微信认证", description = "检查微信认证", tags = {"user"})
+    @GetMapping("/checkWxAuth")
+    @ResponseBody
+    public RestResult<Boolean> checkWxAuth() {
+//        JwtUser sessionUserBean = SessionUtil.getSession();
+        SaBaseLoginUser loginUser = StpLoginUserUtil.getLoginUser();
+
+        if (loginUser != null && !"anonymousUser".equals(loginUser.getAccount())) {
+            UserBean user = userService.getById(loginUser.getId());
+
+            if (StringUtils.isEmpty(user.getWxOpenId())) {
+                return RestResult.success().data(false);
+            } else {
+                return RestResult.success().data(true);
+            }
+
+        } else {
+            return RestResult.fail().message("用户暂未登录");
+        }
+
+    }
+
+}

+ 165 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/UserReadController.java

@@ -0,0 +1,165 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.controller;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
+import vip.xiaonuo.common.annotation.CommonLog;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.common.pojo.CommonValidList;
+import vip.xiaonuo.disk.domain.UserRead;
+import vip.xiaonuo.disk.param.userread.UserReadAddParam;
+import vip.xiaonuo.disk.param.userread.UserReadEditParam;
+import vip.xiaonuo.disk.param.userread.UserReadIdParam;
+import vip.xiaonuo.disk.param.userread.UserReadPageParam;
+import vip.xiaonuo.disk.service.UserReadService;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 通用已读表控制器
+ *
+ * @author honorfire
+ * @date  2025/08/13 17:35
+ */
+@Api(tags = "通用已读表控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class UserReadController {
+
+    @Resource
+    private UserReadService userReadService;
+
+    /**
+     * 通用已读-获取分页列表
+     *
+     * @author honorfire
+     * @date  2025/08/13 17:35
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("通用已读-获取分页列表")
+    @GetMapping("/disk/userread/page")
+    public CommonResult<Page<UserRead>> page(UserReadPageParam userReadPageParam) {
+        return CommonResult.data(userReadService.page(userReadPageParam));
+    }
+
+    /**
+     * 通用已读-添加
+     *
+     * @author honorfire
+     * @date  2025/08/13 17:35
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("通用已读-添加")
+    @CommonLog("通用已读-添加")
+    @PostMapping("/disk/userread/add")
+    public CommonResult<String> add(@RequestBody @Valid UserReadAddParam userReadAddParam) {
+        Map param=new HashMap();
+        UserRead userRead = BeanUtil.toBean(userReadAddParam, UserRead.class);
+        userRead.setUserId(StpLoginUserUtil.getLoginUser().getId());
+        param.put("mainId",userRead.getMainId());
+        param.put("userId",StpLoginUserUtil.getLoginUser().getId());
+        param.put("funcType",userRead.getFuncType());
+        List<UserRead> alreadyReadList=userReadService.wrapperList(param);
+        if(!(alreadyReadList.size()>0))
+        {
+            userReadService.addOne(userRead);
+        }
+        return CommonResult.ok();
+    }
+
+    /**
+     * 通用已读-编辑
+     *
+     * @author honorfire
+     * @date  2025/08/13 17:35
+     */
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("通用已读-编辑")
+    @CommonLog("通用已读-编辑")
+    @PostMapping("/disk/userread/edit")
+    public CommonResult<String> edit(@RequestBody @Valid UserReadEditParam userReadEditParam) {
+        UserRead userRead = BeanUtil.toBean(userReadEditParam, UserRead.class);
+        userReadService.editOne(userRead);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 通用已读-删除
+     *
+     * @author honorfire
+     * @date  2025/08/13 17:35
+     */
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("通用已读-删除")
+    @CommonLog("通用已读-删除")
+    @PostMapping("/disk/userread/delete")
+    public CommonResult<String> delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空")
+                                                   CommonValidList<UserReadIdParam> userReadIdParamList) {
+        userReadService.delete(userReadIdParamList);
+        return CommonResult.ok();
+    }
+
+    /**
+     * 通用已读-详情
+     *
+     * @author honorfire
+     * @date  2025/08/13 17:35
+     */
+    @ApiOperationSupport(order = 5)
+    @ApiOperation("通用已读-详情")
+    @GetMapping("/disk/userread/detail")
+    public CommonResult<UserRead> detail(@Valid UserReadIdParam userReadIdParam) {
+        return CommonResult.data(userReadService.detail(userReadIdParam));
+    }
+
+    /**
+     * 通用阅读-获取数量
+     *
+     * @author xuyuxiang
+     * @date 2022/4/24 20:00
+     */
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("通用阅读-获取数量")
+    @GetMapping("/disk/userread/getCount")
+    public CommonResult<Map<String,Object>> getCount(HttpServletRequest req) {
+        Map param=new HashMap();
+        if(StringUtils.isEmpty(req.getParameter("funcType")))return CommonResult.error("功能类型不能为空");
+        //功能类型,1站内信2公告
+        String funcType=req.getParameter("funcType");
+        param.put("funcType", funcType);
+        param.put("category", req.getParameter("category"));
+        param.put("userId", StpLoginUserUtil.getLoginUser().getId());
+        Map<String,Object> result=userReadService.getCount(param);
+        return CommonResult.data(result);
+    }
+
+}

+ 146 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/controller/VideoAnalysisProgressController.java

@@ -0,0 +1,146 @@
+package vip.xiaonuo.disk.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+import vip.xiaonuo.common.pojo.CommonResult;
+import vip.xiaonuo.disk.service.VideoAnalysisProgressService;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 统计-视频分析控制器
+ *
+ * @author honorfire
+ * @date  2025/06/18 14:16
+ */
+@Api(tags = "统计-视频分析控制器")
+@ApiSupport(author = "SNOWY_TEAM", order = 1)
+@RestController
+@Validated
+public class VideoAnalysisProgressController {
+
+    @Resource
+    private VideoAnalysisProgressService videoAnalysisProgressService;
+
+    /**
+     * 视频分析-观看人数统计
+     *
+     * @author honorfire
+     * @date  2025/07/11 18:52
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("视频分析-观看人数统计")
+    @GetMapping("/disk/videoanalysis/watchUserCountProgress")
+    public CommonResult<Map<String,Object>> topFundamentalDetail(HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("courseId", req.getParameter("courseId"));
+        param.put("startTime", req.getParameter("startTime"));
+        param.put("endTime", req.getParameter("endTime"));
+
+        Map<String,Object> result=videoAnalysisProgressService.getWatchUserCountProgress(param);
+        return CommonResult.data(result);
+    }
+
+    /**
+     * 视频分析-讲义下载次数
+     *
+     * @author honorfire
+     * @date  2025/07/11 18:52
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("视频分析-讲义下载次数")
+    @GetMapping("/disk/videoanalysis/teachMaterialsDownloadCount")
+    public CommonResult<Map<String,Object>> teachMaterialsDownloadCount(HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("courseId", req.getParameter("courseId"));
+        param.put("startTime", req.getParameter("startTime"));
+        param.put("endTime", req.getParameter("endTime"));
+
+        Map<String,Object> result=videoAnalysisProgressService.getTeachMaterialsDownloadCount(param);
+        return CommonResult.data(result);
+    }
+
+    /**
+     * 视频分析-跳出时间分析
+     *
+     * @author honorfire
+     * @date  2025/07/11 18:52
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("视频分析-跳出时间分析")
+    @GetMapping("/disk/videoanalysis/jumpTimeAnalyse")
+    public CommonResult<Map<String,Object>> jumpTimeAnalyse(HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("courseId", req.getParameter("courseId"));
+        param.put("startTime", req.getParameter("startTime"));
+        param.put("endTime", req.getParameter("endTime"));
+
+        Map<String,Object> result=videoAnalysisProgressService.getJumpTimeAnalyse(param);
+        return CommonResult.data(result);
+    }
+
+    /**
+     * 视频分析-互动统计分析
+     *
+     * @author honorfire
+     * @date  2025/07/11 18:52
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("视频分析-互动统计分析")
+    @GetMapping("/disk/videoanalysis/interactionDataAnalyse")
+    public CommonResult<Map<String,Object>> interactionDataAnalyse(HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("courseId", req.getParameter("courseId"));
+        param.put("startTime", req.getParameter("startTime"));
+        param.put("endTime", req.getParameter("endTime"));
+
+        Map<String,Object> result=videoAnalysisProgressService.getInteractionDataAnalyse(param);
+        return CommonResult.data(result);
+    }
+
+    /**
+     * 视频分析-学员学习行为详细数据
+     *
+     * @author honorfire
+     * @date  2025/07/11 18:52
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("视频分析-学员学习行为详细数据:视频相关(每个学员的访问总时长、学员在每个视频的跳出时间点、快进快退时间点、访问视频的登入登出时间点、当前学习进度、学习单个视频的次数、针对每一讲产生的笔记数、讨论数、回帖数等数据)")
+    @GetMapping("/disk/videoanalysis/studyBehaviorDetailData")
+    public CommonResult<Page<Map<String,Object>>> studyBehaviorDetailData(HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("courseId", req.getParameter("courseId"));
+        param.put("startTime", req.getParameter("startTime"));
+        param.put("endTime", req.getParameter("endTime"));
+        Page<Map<String,Object>> list=videoAnalysisProgressService.getStudyBehaviorDetailData(param);
+        return CommonResult.data(list);
+    }
+
+    /**
+     * 视频分析-视频章节详细统计
+     *
+     * @author honorfire
+     * @date  2025/07/11 18:52
+     */
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("视频分析-视频章节详细统计")
+    @GetMapping("/disk/videoanalysis/videoDetailDataAnalysis")
+    public CommonResult<Page<Map<String,Object>>> videoDetailDataAnalysis(HttpServletRequest req) {
+        Map param =new HashMap();
+        param.put("courseId", req.getParameter("courseId"));
+        param.put("startTime", req.getParameter("startTime"));
+        param.put("endTime", req.getParameter("endTime"));
+        Page<Map<String,Object>> list=videoAnalysisProgressService.getVideoDetailDataAnalysis(param);
+        return CommonResult.data(list);
+    }
+
+}

+ 71 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/domain/CapacityAuditInfo.java

@@ -0,0 +1,71 @@
+package vip.xiaonuo.disk.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import vip.xiaonuo.common.pojo.CommonEntity;
+
+import java.util.Date;
+
+/**
+ * 容量审核信息表
+ *
+ * @author jinjilong
+ */
+@Data
+@TableName(value = "CAPACITY_AUDIT_INFO", autoResultMap = true)
+public class CapacityAuditInfo extends CommonEntity {
+
+    /** id */
+    @TableId
+    @ApiModelProperty(value = "id", position = 1)
+    private String id;
+
+    /**
+     * 申请人用户ID
+     */
+    private String applicantUserId;
+
+    /**
+     * 审核状态 0待审核 1审核通过 2审核拒绝
+     */
+    private Integer auditState;
+
+    /**
+     * 审核拒绝原因
+     */
+    private String rejectReason;
+
+    /**
+     * 申请后容量 单位MB
+     */
+    private Long applicantAfterCapacity;
+
+    /**
+     * 申请时间
+     */
+    private Date applicantTime;
+
+    /**
+     * 审核时间
+     */
+    private Date auditTime;
+
+    /**
+     * 审核人用户ID
+     */
+    private String auditUserId;
+
+    /**
+     * 扩容申请事由
+     */
+    private String applicantReason;
+
+    /**
+     * 申请前容量 单位MB
+     */
+    private Long applicantBeforeCapacity;
+
+
+}

+ 63 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/domain/CapacityReAuditInfo.java

@@ -0,0 +1,63 @@
+package vip.xiaonuo.disk.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import vip.xiaonuo.common.pojo.CommonEntity;
+
+import java.util.Date;
+@Data
+@TableName(value = "CAPACITYRE_AUDIT_INFO", autoResultMap = true)
+public class CapacityReAuditInfo extends CommonEntity {
+
+    /** id */
+    @TableId
+    @ApiModelProperty(value = "id", position = 1)
+    private String id;
+
+    /**
+     * 申请人用户ID
+     */
+    private String applicantUserId;
+
+    /**
+     * 审核状态 0待审核 1审核通过 2审核拒绝
+     */
+    private Integer auditState;
+
+    /**
+     * 审核拒绝原因
+     */
+    private String rejectReason;
+
+    /**
+     * 申请后容量 单位MB
+     */
+    private Long applicantAfterCapacity;
+
+    /**
+     * 申请时间
+     */
+    private Date applicantTime;
+
+    /**
+     * 审核时间
+     */
+    private Date auditTime;
+
+    /**
+     * 审核人用户ID
+     */
+    private String auditUserId;
+
+    /**
+     * 扩容申请事由
+     */
+    private String applicantReason;
+
+    /**
+     * 申请前容量 单位MB
+     */
+    private Long applicantBeforeCapacity;
+}

+ 75 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/domain/Chapter.java

@@ -0,0 +1,75 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+
+/**
+ * 课程章节表实体
+ *
+ * @author pans
+ * @date  2025/07/02 15:34
+ **/
+@Getter
+@Setter
+@TableName("COURSE_CHAPTER")
+public class Chapter {
+
+    /** 主键 */
+    @ApiModelProperty(value = "主键", position = 1)
+    private String id;
+
+    /** 父id */
+    @ApiModelProperty(value = "父id", position = 2)
+    private String pid;
+
+    /** 章节名称 */
+    @ApiModelProperty(value = "章节名称", position = 3)
+    private String name;
+
+    /** 课程id */
+    @ApiModelProperty(value = "课程id", position = 4)
+    private String courseId;
+
+    /** DELETE_FLAG */
+    @ApiModelProperty(value = "DELETE_FLAG", position = 5)
+    @TableLogic
+    @TableField(fill = FieldFill.INSERT)
+    private String deleteFlag;
+
+    /** UPDATE_TIME */
+    @ApiModelProperty(value = "UPDATE_TIME", position = 6)
+    @TableField(fill = FieldFill.UPDATE)
+    private Date updateTime;
+
+    /** CREATE_TIME */
+    @ApiModelProperty(value = "CREATE_TIME", position = 7)
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+    /** CREATE_USER */
+    @ApiModelProperty(value = "CREATE_USER", position = 8)
+    @TableField(fill = FieldFill.INSERT)
+    private String createUser;
+
+    /** UPDATE_USER */
+    @ApiModelProperty(value = "UPDATE_USER", position = 9)
+    @TableField(fill = FieldFill.UPDATE)
+    private String updateUser;
+
+}

+ 98 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/domain/ClassHour.java

@@ -0,0 +1,98 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 课时表实体
+ *
+ * @author pans
+ * @date  2025/07/02 10:36
+ **/
+@Getter
+@Setter
+@TableName("COURSE_CLASSHOUR")
+public class ClassHour {
+
+    /** 主键 */
+    @ApiModelProperty(value = "主键", position = 1)
+    private String id;
+
+    /** 开始时间 */
+    @ApiModelProperty(value = "开始时间", position = 2)
+    private Date startTime;
+
+    /** 结束时间 */
+    @ApiModelProperty(value = "结束时间", position = 3)
+    private Date endTime;
+
+    /** 班级id */
+    @ApiModelProperty(value = "班级id", position = 4)
+    private String classId;
+
+    /** 章节id */
+    @ApiModelProperty(value = "章节id", position = 4)
+    private String chapterId;
+
+    /** 课时名称 */
+    @ApiModelProperty(value = "课时名称", position = 6)
+    private String name;
+
+    /** 备注 */
+    @ApiModelProperty(value = "备注", position = 6)
+    private String remark;
+
+    /** 视频资源 */
+    @ApiModelProperty(value = "视频资源", position = 4)
+    private String videoResource;
+
+    /** 讲义资源 */
+    @ApiModelProperty(value = "讲义资源", position = 4)
+    private String teachmaterialsFile;
+
+    /** 字幕资源 */
+    @ApiModelProperty(value = "字幕资源", position = 4)
+    private String subtitleFile;
+
+    /** DELETE_FLAG */
+    @ApiModelProperty(value = "DELETE_FLAG", position = 7)
+    @TableLogic
+    @TableField(fill = FieldFill.INSERT)
+    private String deleteFlag;
+
+    /** UPDATE_TIME */
+    @ApiModelProperty(value = "UPDATE_TIME", position = 8)
+    @TableField(fill = FieldFill.UPDATE)
+    private Date updateTime;
+
+    /** CREATE_TIME */
+    @ApiModelProperty(value = "CREATE_TIME", position = 9)
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+    /** CREATE_USER */
+    @ApiModelProperty(value = "CREATE_USER", position = 10)
+    @TableField(fill = FieldFill.INSERT)
+    private String createUser;
+
+    /** UPDATE_USER */
+    @ApiModelProperty(value = "UPDATE_USER", position = 11)
+    @TableField(fill = FieldFill.UPDATE)
+    private String updateUser;
+}

+ 91 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/domain/ClassTeam.java

@@ -0,0 +1,91 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+
+/**
+ * class_team实体
+ *
+ * @author pans
+ * @date  2025/07/01 19:24
+ **/
+@Getter
+@Setter
+@TableName("class_team")
+public class ClassTeam {
+
+    /** ID */
+    @TableId(type = IdType.AUTO)
+    @ApiModelProperty(value = "ID", position = 1)
+    private Long id;
+
+    /** 班级名称 */
+    @ApiModelProperty(value = "班级名称", position = 2)
+    private String className;
+
+    /** 班级编码 */
+    @ApiModelProperty(value = "班级编码", position = 3)
+    private String classCode;
+
+    /** 专业id */
+    @ApiModelProperty(value = "专业id", position = 4)
+    private Integer majorId;
+
+    /** 院系一级id */
+    @ApiModelProperty(value = "院系一级id", position = 5)
+    private String collegeId;
+
+    /** 备注 */
+    @ApiModelProperty(value = "备注", position = 6)
+    private String remark;
+
+    /** DELETE_FLAG */
+    @ApiModelProperty(value = "DELETE_FLAG", position = 7)
+    @TableLogic
+    @TableField(fill = FieldFill.INSERT)
+    private String deleteFlag;
+
+    /** UPDATE_TIME */
+    @ApiModelProperty(value = "UPDATE_TIME", position = 8)
+    @TableField(fill = FieldFill.UPDATE)
+    private Date updateTime;
+
+    /** CREATE_TIME */
+    @ApiModelProperty(value = "CREATE_TIME", position = 9)
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+    /** CREATE_USER */
+    @ApiModelProperty(value = "CREATE_USER", position = 10)
+    @TableField(fill = FieldFill.INSERT)
+    private String createUser;
+
+    /** UPDATE_USER */
+    @ApiModelProperty(value = "UPDATE_USER", position = 11)
+    @TableField(fill = FieldFill.UPDATE)
+    private String updateUser;
+
+    /** 院系二级id */
+    @ApiModelProperty(value = "院系二级id", position = 12)
+    private String collegeTwoId;
+
+    /** 院系三级id */
+    @ApiModelProperty(value = "院系三级id", position = 13)
+    private String collegeThreeId;
+}

+ 86 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/domain/CollectCourse.java

@@ -0,0 +1,86 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 收藏课程实体
+ *
+ * @author 金吉龙
+ * @date  2025/07/01 19:01
+ **/
+@Getter
+@Setter
+@TableName("COLLECT_COURSE")
+public class CollectCourse {
+
+    /** 收藏主键ID */
+    @TableId
+    @ApiModelProperty(value = "收藏主键ID", position = 1)
+    private String collectId;
+
+    /** 课程主键ID */
+    @ApiModelProperty(value = "课程主键ID", position = 2)
+    private String courseId;
+
+    /** 收藏用户ID */
+    @ApiModelProperty(value = "收藏用户ID", position = 3)
+    private String collectUserId;
+
+    /** 收藏时间 */
+    @ApiModelProperty(value = "收藏时间", position = 4)
+    private Date collectTime;
+
+    /** 收藏状态 0已收藏 1取消收藏 */
+    @ApiModelProperty(value = "收藏状态 0已收藏 1取消收藏", position = 5)
+    private Integer collectState;
+
+    /** 删除状态 */
+    @ApiModelProperty(value = "删除状态", position = 6)
+    @TableLogic
+    @TableField(fill = FieldFill.INSERT)
+    private String deleteFlag;
+
+    /** 创建时间 */
+    @ApiModelProperty(value = "创建时间", position = 7)
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+    /** 创建用户 */
+    @ApiModelProperty(value = "创建用户", position = 8)
+    @TableField(fill = FieldFill.INSERT)
+    private String createUser;
+
+    /** 修改时间 */
+    @ApiModelProperty(value = "修改时间", position = 9)
+    @TableField(fill = FieldFill.UPDATE)
+    private Date updateTime;
+
+    /** 修改用户 */
+    @ApiModelProperty(value = "修改用户", position = 10)
+    @TableField(fill = FieldFill.UPDATE)
+    private String updateUser;
+
+    /**
+     * 课程信息
+     */
+    @TableField(exist = false)
+    private CourseInfo courseInfo;
+
+}

+ 80 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/domain/College.java

@@ -0,0 +1,80 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.domain;
+
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fhs.core.trans.anno.Trans;
+import com.fhs.core.trans.constant.TransType;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import vip.xiaonuo.common.pojo.CommonEntity;
+import vip.xiaonuo.sys.modular.user.entity.SysUser;
+
+/**
+ * college实体
+ *
+ * @author pans
+ * @date  2025/06/26 09:09
+ **/
+@Getter
+@Setter
+@TableName("college")
+public class College extends CommonEntity {
+    /** id */
+    @ApiModelProperty(value = "id", position = 1)
+    private String id;
+
+    /** 租户id */
+    @ApiModelProperty(value = "租户id", position = 2)
+    private String tenantId;
+
+    /** 父id */
+    @ApiModelProperty(value = "父id", position = 3)
+    private String parentId;
+
+    /** 主管id */
+    @ApiModelProperty(value = "主管id", position = 4)
+    @TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
+    @Trans(type = TransType.SIMPLE, target = SysUser.class, fields = "name", alias = "director", ref = "directorName")
+    private String directorId;
+
+    /** 名称 */
+    @ApiModelProperty(value = "名称", position = 5)
+    private String name;
+
+    /** 编码 */
+    @ApiModelProperty(value = "编码", position = 6)
+    private String code;
+
+    /** 分类 */
+    @ApiModelProperty(value = "分类", position = 7)
+    private String category;
+
+    /** 排序码 */
+    @ApiModelProperty(value = "排序码", position = 8)
+    private Integer sortCode;
+
+    /** 扩展信息 */
+    @ApiModelProperty(value = "扩展信息", position = 9)
+    @TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
+    private String extJson;
+
+    /** 是否是资源库特殊账号,0否1是 */
+    @ApiModelProperty(value = "是否是资源库特殊账号,0否1是")
+    @TableField(insertStrategy = FieldStrategy.IGNORED, updateStrategy = FieldStrategy.IGNORED)
+    private String isResourceaccount;
+
+}

+ 91 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/domain/CollegeOrg.java

@@ -0,0 +1,91 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 下发组织日志表实体
+ *
+ * @author pans
+ * @date  2025/09/04 15:14
+ **/
+@Getter
+@Setter
+@TableName("COLLEGE_ORG")
+public class CollegeOrg {
+    /** ID */
+    @TableId
+    @ApiModelProperty(value = "ID", position = 1)
+    private String id;
+
+    /** DEPT_ID */
+    @ApiModelProperty(value = "DEPT_ID", position = 2)
+    private Long deptId;
+
+    /** DEPT_NAME */
+    @ApiModelProperty(value = "DEPT_NAME", position = 3)
+    private String deptName;
+
+    /** ANCESTORS */
+    @ApiModelProperty(value = "ANCESTORS", position = 4)
+    private String ancestors;
+
+    /** DEPT_TYPE */
+    @ApiModelProperty(value = "DEPT_TYPE", position = 5)
+    private String deptType;
+
+    /** ORDER_NUM */
+    @ApiModelProperty(value = "ORDER_NUM", position = 6)
+    private String orderNum;
+
+    /** CREATE_TIME */
+    @ApiModelProperty(value = "CREATE_TIME", position = 7)
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+    /** 失败原因 */
+    @ApiModelProperty(value = "失败原因", position = 8)
+    private String reason;
+
+    /** 下发状态 0 失败  1正常 */
+    @ApiModelProperty(value = "下发状态 0 失败  1正常", position = 9)
+    private String issue;
+
+    /** PRI_KEY */
+    @ApiModelProperty(value = "PRI_KEY", position = 10)
+    private String priKey;
+
+    /** DEPT_CODE */
+    @ApiModelProperty(value = "DEPT_CODE", position = 11)
+    private String deptCode;
+
+    /** STATUS */
+    @ApiModelProperty(value = "STATUS", position = 12)
+    private String status;
+
+    /** COMMAND_TYPE */
+    @ApiModelProperty(value = "COMMAND_TYPE", position = 13)
+    private String commandType;
+
+    /** PARENT_ID */
+    @ApiModelProperty(value = "PARENT_ID", position = 14)
+    private String parentId;
+    /** 下发的json字符串 */
+    private String info;
+}

+ 97 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/domain/CollegePosition.java

@@ -0,0 +1,97 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 下发岗位日志表实体
+ *
+ * @author pans
+ * @date  2025/09/04 15:39
+ **/
+@Getter
+@Setter
+@TableName("COLLEGE_POSITION")
+public class CollegePosition {
+
+    /** ID */
+    @TableId
+    @ApiModelProperty(value = "ID", position = 1)
+    private String id;
+
+    /** POST_ID */
+    @ApiModelProperty(value = "POST_ID", position = 2)
+    private String postId;
+
+    /** POST_CODE */
+    @ApiModelProperty(value = "POST_CODE", position = 3)
+    private String postCode;
+
+    /** POST_NAME */
+    @ApiModelProperty(value = "POST_NAME", position = 4)
+    private String postName;
+
+    /** POST_SORT */
+    @ApiModelProperty(value = "POST_SORT", position = 5)
+    private Integer postSort;
+
+    /** DEPT_ID */
+    @ApiModelProperty(value = "DEPT_ID", position = 6)
+    private Long deptId;
+
+    /** DEPT_NAME */
+    @ApiModelProperty(value = "DEPT_NAME", position = 7)
+    private String deptName;
+
+    /** STATUS */
+    @ApiModelProperty(value = "STATUS", position = 8)
+    private String status;
+
+    /** REMARK */
+    @ApiModelProperty(value = "REMARK", position = 9)
+    private String remark;
+
+    /** CREATE_TIME */
+    @ApiModelProperty(value = "CREATE_TIME", position = 10)
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+    /** 失败原因 */
+    @ApiModelProperty(value = "失败原因", position = 11)
+    private String reason;
+
+    /** 下发状态 0失败  1正常 */
+    @ApiModelProperty(value = "下发状态 0失败  1正常", position = 12)
+    private String issue;
+
+    /** PRI_KEY */
+    @ApiModelProperty(value = "PRI_KEY", position = 13)
+    private String priKey;
+
+    /** COMMAND_TYPE */
+    @ApiModelProperty(value = "COMMAND_TYPE", position = 14)
+    private String commandType;
+
+    /** ORDER_NUM */
+    @ApiModelProperty(value = "ORDER_NUM", position = 15)
+    private String orderNum;
+
+    /** 下发的json字符串 */
+    private String info;
+}

+ 109 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/domain/CollegeUser.java

@@ -0,0 +1,109 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 下发用户日志表实体
+ *
+ * @author pans
+ * @date  2025/09/04 14:40
+ **/
+@Getter
+@Setter
+@TableName("COLLEGE_USER")
+public class CollegeUser {
+
+    /** ID */
+    @TableId
+    @ApiModelProperty(value = "ID", position = 1)
+    private String id;
+
+    /** USER_ID */
+    @ApiModelProperty(value = "USER_ID", position = 2)
+    private String userId;
+
+    /** APP_ID */
+    @ApiModelProperty(value = "APP_ID", position = 3)
+    private String appId;
+
+    /** CALL_NAME */
+    @ApiModelProperty(value = "CALL_NAME", position = 4)
+    private String callName;
+
+    /** USER_NAME */
+    @ApiModelProperty(value = "USER_NAME", position = 5)
+    private String userName;
+
+    /** EMAIL */
+    @ApiModelProperty(value = "EMAIL", position = 6)
+    private String email;
+
+    /** MOBILE */
+    @ApiModelProperty(value = "MOBILE", position = 7)
+    private String mobile;
+
+    /** SEX */
+    @ApiModelProperty(value = "SEX", position = 8)
+    private String sex;
+
+    /** STATUS */
+    @ApiModelProperty(value = "STATUS", position = 9)
+    private String status;
+
+    /** USER_CODE */
+    @ApiModelProperty(value = "USER_CODE", position = 10)
+    private String userCode;
+
+    /** USER_TYPE */
+    @ApiModelProperty(value = "USER_TYPE", position = 11)
+    private String userType;
+
+    /** CRATE_TIME */
+    @ApiModelProperty(value = "CRATE_TIME", position = 12)
+    private Date crateTime;
+
+    /** 失败原因 */
+    @ApiModelProperty(value = "失败原因", position = 13)
+    private String reason;
+
+    /** 下发状态(0失败  1成功) */
+    @ApiModelProperty(value = "下发状态(0失败  1成功)", position = 14)
+    private String issue;
+
+    /** DEL_FLAG */
+    @ApiModelProperty(value = "DEL_FLAG", position = 15)
+    private String delFlag;
+
+    /** POST */
+    @ApiModelProperty(value = "POST", position = 16)
+    private String post;
+
+    /** PRI_KEY */
+    @ApiModelProperty(value = "PRI_KEY", position = 17)
+    private String priKey;
+
+    /** COMMAND_TYPE */
+    @ApiModelProperty(value = "COMMAND_TYPE", position = 18)
+    private String commandType;
+    /** 下发的json字符串 */
+    private String info;
+}

+ 30 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/domain/CommonFile.java

@@ -0,0 +1,30 @@
+package vip.xiaonuo.disk.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import javax.persistence.*;
+
+/**
+ * @author MAC
+ * @version 1.0
+ * @description: TODO
+ * @date 2022/1/12 14:41
+ */
+@Data
+@Table(name = "commonfile")
+@Entity
+@TableName("commonfile")
+public class CommonFile {
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    @TableId(type = IdType.AUTO)
+    @Column(columnDefinition="varchar(20)")
+    public String commonFileId;
+    @Column(columnDefinition="varchar(20) comment '用户文件id'")
+    public String userFileId;
+//    @Column(columnDefinition="int(2) comment '文件权限'")
+//    public Integer filePermission;
+}

+ 34 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/domain/CourseAuditRecordEnum.java

@@ -0,0 +1,34 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.domain;
+
+import lombok.Getter;
+
+/**
+ * 课程审核记录表枚举
+ *
+ * @author honorfire
+ * @date  2025/06/20 14:58
+ **/
+@Getter
+public enum CourseAuditRecordEnum {
+
+    /** 测试 */
+    TEST("TEST");
+
+    private final String value;
+
+    CourseAuditRecordEnum(String value) {
+        this.value = value;
+    }
+}

+ 75 - 0
education-plugin/education-plugin-disk/education-plugin-disk-func/src/main/java/vip/xiaonuo/disk/domain/CourseChapterKnowledge.java

@@ -0,0 +1,75 @@
+/*
+ * Copyright [2022] [https://www.xiaonuo.vip]
+ *
+ * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
+ *
+ * 1.请不要删除和修改根目录下的LICENSE文件。
+ * 2.请不要删除和修改Snowy源码头部的版权声明。
+ * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
+ * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
+ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
+ * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
+ */
+package vip.xiaonuo.disk.domain;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+
+/**
+ * COURSE_CHAPTER_KNOWLEDGE实体
+ *
+ * @author pans
+ * @date  2025/08/04 17:14
+ **/
+@Getter
+@Setter
+@TableName("COURSE_CHAPTER_KNOWLEDGE")
+public class CourseChapterKnowledge {
+
+    /** ID */
+    @TableId
+    @ApiModelProperty(value = "ID", position = 1)
+    private String id;
+
+    /** COURSE_ID */
+    @ApiModelProperty(value = "COURSE_ID", position = 2)
+    private String courseId;
+
+    /** CHAPTER_ID */
+    @ApiModelProperty(value = "CHAPTER_ID", position = 3)
+    private String chapterId;
+
+    /** 课时id */
+    private String hourId;
+    
+    /** KNOWLEDGE_ID */
+    @ApiModelProperty(value = "KNOWLEDGE_ID", position = 4)
+    private String knowledgeId;
+
+    /** UPDATE_TIME */
+    @ApiModelProperty(value = "UPDATE_TIME", position = 5)
+    @TableField(fill = FieldFill.UPDATE)
+    private Date updateTime;
+
+    /** CREATE_TIME */
+    @ApiModelProperty(value = "CREATE_TIME", position = 6)
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+    /** CREATE_USER */
+    @ApiModelProperty(value = "CREATE_USER", position = 7)
+    @TableField(fill = FieldFill.INSERT)
+    private String createUser;
+
+    /** UPDATE_USER */
+    @ApiModelProperty(value = "UPDATE_USER", position = 8)
+    @TableField(fill = FieldFill.UPDATE)
+    private String updateUser;
+}

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff