From 681e3de3bb591a37120604f797b9b241e1521802 Mon Sep 17 00:00:00 2001 From: cyc Date: Tue, 31 Mar 2026 16:51:34 +0800 Subject: [PATCH] =?UTF-8?q?skill=20=E6=A8=A1=E5=9D=97=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/skills/SkillsController.java | 32 +++- .../java/com/ruoyi/platform/domain/Skill.java | 6 + .../ruoyi/platform/service/SkillService.java | 9 + .../impl/CSServiceVersionServiceImpl.java | 9 +- .../service/impl/SkillServiceImpl.java | 171 +++++++++++++++++- .../managementPlatform/SkillDaoMapper.xml | 17 +- 6 files changed, 230 insertions(+), 14 deletions(-) diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/skills/SkillsController.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/skills/SkillsController.java index 3c647888..07fb7819 100644 --- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/skills/SkillsController.java +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/skills/SkillsController.java @@ -14,13 +14,14 @@ import io.swagger.annotations.ApiOperation; import org.springframework.data.domain.PageRequest; import org.springframework.web.bind.annotation.*; import org.springframework.data.domain.Page; +import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; @RestController @RequestMapping("skills") -@Api(value = "新模型管理") +@Api(value = "技能管理") public class SkillsController extends BaseController { @Resource @@ -31,10 +32,12 @@ public class SkillsController extends BaseController { public GenericsAjaxResult> queryByPage(@RequestParam("page") int page, @RequestParam("size") int size, @RequestParam(value = "name", required = false) String name, + @RequestParam(value = "type", required = false) String type, @RequestParam("is_public") boolean isPublic) { PageRequest pageRequest = PageRequest.of(page, size); Skill skill = new Skill(); skill.setName(name); + skill.setType(type); if (!isPublic) { skill.setCreateBy(SecurityUtils.getUsername()); } else { @@ -43,7 +46,7 @@ public class SkillsController extends BaseController { return genericsSuccess(this.skillService.queryByPage(skill, pageRequest)); } - @PostMapping("/addSkill") + @PostMapping @ApiOperation("添加Skill") @OperationNotification("添加Skill,Skill名称:#{#skillVo.name}") public GenericsAjaxResult addSkill(@RequestBody SkillVo skillVo) throws Exception { @@ -77,4 +80,29 @@ public class SkillsController extends BaseController { LoginUser loginUser = SecurityUtils.getLoginUser(); return AjaxResult.success(skillService.publish(skill, loginUser, request)); } + + @PutMapping("/praise/{id}") + @ApiOperation(value = "点赞一个项目") + public AjaxResult praise(@PathVariable("id") Long id){ + return AjaxResult.success(skillService.praise(id)); + } + + @PutMapping("/unpraise/{id}") + @ApiOperation(value = "取消点赞一个项目") + public AjaxResult unpraise(@PathVariable("id") Long id){ + return AjaxResult.success(skillService.unpraise(id)); + } + + /** + * 上传 + * + * @param files 上传文件 + * @return 上传结果 + */ + @CrossOrigin(origins = "*", allowedHeaders = "*") + @PostMapping("/upload") + @ApiOperation(value = "上传") + public AjaxResult uploadDataset(@RequestParam("file") MultipartFile files,Long id) throws Exception { + return AjaxResult.success(skillService.upload(files, id)); + } } diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/Skill.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/Skill.java index 4612a5d2..a28c4c6b 100644 --- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/Skill.java +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/domain/Skill.java @@ -37,4 +37,10 @@ public class Skill { private Integer state; private String filePath; + + private String fileTreeJson; + + private String mdContent; + + private Integer praisesCount; } diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/SkillService.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/SkillService.java index e428e90a..63f45f7f 100644 --- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/SkillService.java +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/SkillService.java @@ -5,8 +5,11 @@ import com.ruoyi.platform.vo.SkillVo; import com.ruoyi.system.api.model.LoginUser; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; +import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.util.Map; public interface SkillService { Page queryByPage(Skill skill, PageRequest pageRequest); @@ -20,4 +23,10 @@ public interface SkillService { String delete(Long id) throws Exception; String publish(Skill skill, LoginUser loginUser, HttpServletRequest request); + + String praise(Long id); + + String unpraise(Long id); + + Map upload(MultipartFile files, Long id) throws IOException; } diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/CSServiceVersionServiceImpl.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/CSServiceVersionServiceImpl.java index e5fef5b3..c364ee2a 100644 --- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/CSServiceVersionServiceImpl.java +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/CSServiceVersionServiceImpl.java @@ -173,12 +173,15 @@ public class CSServiceVersionServiceImpl implements ServiceVersionService { public TrainingTask insertTask(TrainingTaskVo trainingTaskVo) throws Exception { TrainingTask trainingTask = new TrainingTask(); BeanUtils.copyProperties(trainingTaskVo, trainingTask); - trainingTask.setTaskInfo(JacksonUtil.toJSONString(trainingTaskVo.getTaskInfo())); - trainingTask.setTaskParam(JacksonUtil.toJSONString(trainingTaskVo.getTaskParam())); - trainingTask.setStatus("Pending"); + trainingTask.setState(1); + trainingTask.setStatus("Pending"); trainingTask.setCreateTime(new Date()); trainingTask.setUpdateTime(new Date()); + trainingTask.setTaskName((String) trainingTaskVo.getTaskInfo().get("name")); + trainingTask.setTaskInfo(JacksonUtil.toJSONString(trainingTaskVo.getTaskInfo())); + trainingTask.setTaskParam(JacksonUtil.toJSONString(trainingTaskVo.getTaskParam())); + String username = SecurityUtils.getLoginUser().getUsername(); trainingTask.setCreateBy(username); trainingTask.setUpdateBy(username); diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/SkillServiceImpl.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/SkillServiceImpl.java index bc8eb08b..2d460ace 100644 --- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/SkillServiceImpl.java +++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/SkillServiceImpl.java @@ -1,37 +1,55 @@ package com.ruoyi.platform.service.impl; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; import com.alibaba.fastjson2.JSON; import com.ruoyi.common.security.utils.SecurityUtils; -import com.ruoyi.platform.domain.ApprovalRequest; -import com.ruoyi.platform.domain.ApprovalStatus; -import com.ruoyi.platform.domain.ApprovalType; -import com.ruoyi.platform.domain.Skill; +import com.ruoyi.platform.domain.*; import com.ruoyi.platform.mapper.ApprovalInfoMapper; import com.ruoyi.platform.mapper.SkillDao; +import com.ruoyi.platform.service.MinioService; import com.ruoyi.platform.service.SkillService; import com.ruoyi.platform.vo.SkillVo; import com.ruoyi.system.api.constant.Constant; import com.ruoyi.system.api.model.LoginUser; +import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Value; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; -import java.util.List; -import java.util.Set; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; @Service("skillService") +@Transactional +@Log4j2 public class SkillServiceImpl implements SkillService { + @Value("${minio.dataReleaseBucketName}") + private String bucketName; + @Resource private SkillDao skillDao; @Resource private ApprovalInfoMapper approvalInfoMapper; + @Resource + private MinioService minioService; @Override public Page queryByPage(Skill skill, PageRequest pageRequest) { @@ -76,8 +94,7 @@ public class SkillServiceImpl implements SkillService { @Override public Skill queryById(Long id) { - Skill skill = skillDao.getSkillById(id); - return skill; + return skillDao.getSkillById(id); } @Override @@ -128,4 +145,142 @@ public class SkillServiceImpl implements SkillService { return "发布成功,待管理员审核"; } + @Override + public String praise(Long id) { + Skill skill = skillDao.getSkillById(id); + skill.setPraisesCount(skill.getPraisesCount() + 1); + skillDao.edit(skill); + return "点赞成功"; + } + + @Override + public String unpraise(Long id) { + Skill skill = skillDao.getSkillById(id); + skill.setPraisesCount(skill.getPraisesCount() - 1); + skillDao.edit(skill); + return "取消点赞成功"; + } + + @Override + public Map upload(MultipartFile file, Long id) throws IOException { + String originalFilename = file.getOriginalFilename(); + String fileType = getFileType(originalFilename); + if (fileType == null) { + throw new IllegalArgumentException("仅支持 .md 或 .zip 文件"); + } + + Skill skill = new Skill(); + skill.setId(id); + if ("md".equals(fileType)) { + skill.setMdContent(IoUtil.read(file.getInputStream(), StandardCharsets.UTF_8)); + } else if ("zip".equals(fileType)) { + // 解析 ZIP,直接填充 metadata 的 directoryTree 和 contentText + parseZipAndFillMetadata(file.getInputStream(), skill); + } + + String savePath = "/mini-model-platform-data/skill_file/upload/" + id + "/" + cn.hutool.core.lang.UUID.fastUUID() + "/" + file.getOriginalFilename(); + HashMap result = MapUtil.newHashMap(); + result.put("filename", file.getOriginalFilename()); + result.put("direct_url", savePath); + + // 异步上传原始文件到 MinIO + CompletableFuture.supplyAsync(() -> { + try { + minioService.uploadFile(bucketName, savePath, file); + return "上传成功"; + } catch (Exception e) { + throw new RuntimeException("异步提交任务失败", e); + } + }); + + skill.setFilePath(savePath); + skillDao.edit(skill); + return result; + } + + /** + * 解析 ZIP 包,直接填充 metadata 的 directoryTree 和 contentText + */ + private void parseZipAndFillMetadata(InputStream inputStream, Skill skill) throws IOException { + List> rootChildren = new ArrayList<>(); + Map> pathToNode = new HashMap<>(); + Map rootNode = new HashMap<>(); + rootNode.put("children", rootChildren); + pathToNode.put("", rootNode); + + StringBuilder skillMdContent = new StringBuilder(); + + try (ZipInputStream zis = new ZipInputStream(inputStream)) { + ZipEntry entry; + while ((entry = zis.getNextEntry()) != null) { + String entryName = entry.getName(); + // 去除目录结尾的斜杠 + if (entryName.endsWith("/")) { + entryName = entryName.substring(0, entryName.length() - 1); + } + + // 使用 Hutool 分割路径 + String[] parts = StrUtil.splitToArray(entryName, '/'); + StringBuilder currentPath = new StringBuilder(); + Map parentNode = pathToNode.get(""); // 根节点 + + for (int i = 0; i < parts.length; i++) { + String part = parts[i]; + if (i > 0) currentPath.append('/'); + currentPath.append(part); + String fullPath = currentPath.toString(); + + Map node = pathToNode.get(fullPath); + if (node == null) { + boolean isDir = (i < parts.length - 1) || entry.isDirectory(); + Map newNode = new HashMap<>(); + newNode.put("name", part); + if (isDir) { + List> children = new ArrayList<>(); + newNode.put("children", children); + pathToNode.put(fullPath, newNode); + @SuppressWarnings("unchecked") + List> parentChildren = (List>) parentNode.get("children"); + parentChildren.add(newNode); + } else { + newNode.put("children", null); + long sizeBytes = entry.getSize(); + if (sizeBytes < 0) sizeBytes = 0; + newNode.put("fileSize", FileUtil.readableFileSize(sizeBytes)); + pathToNode.put(fullPath, newNode); + @SuppressWarnings("unchecked") + List> parentChildren = (List>) parentNode.get("children"); + parentChildren.add(newNode); + } + } + parentNode = node != null ? node : pathToNode.get(fullPath); + } + + // 读取最外层 Skill.md 内容 + if (isOuterSkillMd(entryName) && !entry.isDirectory()) { + // 使用 Hutool 读取当前条目的内容 + String content = IoUtil.read(zis, StandardCharsets.UTF_8); + skillMdContent.append(content); + } + zis.closeEntry(); + } + } + + // 使用 Hutool 序列化为 JSON + skill.setFileTreeJson(JSONUtil.toJsonStr(rootChildren)); + skill.setMdContent(skillMdContent.toString()); + } + + private boolean isOuterSkillMd(String entryName) { + return "Skill.md".equals(entryName); + } + + private String getFileType(String fileName) { + if (fileName == null) return null; + String ext = StrUtil.subAfter(fileName, ".", true); + if ("md".equalsIgnoreCase(ext)) return "md"; + if ("zip".equalsIgnoreCase(ext)) return "zip"; + return null; + } + } diff --git a/ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/SkillDaoMapper.xml b/ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/SkillDaoMapper.xml index 23b76e1e..c235f8d4 100644 --- a/ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/SkillDaoMapper.xml +++ b/ruoyi-modules/management-platform/src/main/resources/mapper/managementPlatform/SkillDaoMapper.xml @@ -22,6 +22,18 @@ preview_pic = #{skill.previewPic}, + + praises_count = #{skill.praisesCount}, + + + file_path = #{skill.filePath}, + + + file_tree_json = #{skill.fileTreeJson}, + + + md_content = #{skill.mdContent}, + is_public = #{skill.isPublic}, @@ -40,7 +52,7 @@