skill 模块开发
This commit is contained in:
parent
bc432f1ccd
commit
681e3de3bb
|
|
@ -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<Page<Skill>> 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<Skill> 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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,4 +37,10 @@ public class Skill {
|
|||
private Integer state;
|
||||
|
||||
private String filePath;
|
||||
|
||||
private String fileTreeJson;
|
||||
|
||||
private String mdContent;
|
||||
|
||||
private Integer praisesCount;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Skill> 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<String, Object> upload(MultipartFile files, Long id) throws IOException;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<Skill> 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<String, Object> 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<String, Object> 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<Map<String, Object>> rootChildren = new ArrayList<>();
|
||||
Map<String, Map<String, Object>> pathToNode = new HashMap<>();
|
||||
Map<String, Object> 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<String, Object> 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<String, Object> node = pathToNode.get(fullPath);
|
||||
if (node == null) {
|
||||
boolean isDir = (i < parts.length - 1) || entry.isDirectory();
|
||||
Map<String, Object> newNode = new HashMap<>();
|
||||
newNode.put("name", part);
|
||||
if (isDir) {
|
||||
List<Map<String, Object>> children = new ArrayList<>();
|
||||
newNode.put("children", children);
|
||||
pathToNode.put(fullPath, newNode);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Map<String, Object>> parentChildren = (List<Map<String, Object>>) 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<Map<String, Object>> parentChildren = (List<Map<String, Object>>) 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,18 @@
|
|||
<if test="skill.previewPic != null and skill.previewPic !=''">
|
||||
preview_pic = #{skill.previewPic},
|
||||
</if>
|
||||
<if test="skill.praisesCount != null and skill.praisesCount !=''">
|
||||
praises_count = #{skill.praisesCount},
|
||||
</if>
|
||||
<if test="skill.filePath != null and skill.filePath !=''">
|
||||
file_path = #{skill.filePath},
|
||||
</if>
|
||||
<if test="skill.fileTreeJson != null and skill.fileTreeJson !=''">
|
||||
file_tree_json = #{skill.fileTreeJson},
|
||||
</if>
|
||||
<if test="skill.mdContent != null and skill.mdContent !=''">
|
||||
md_content = #{skill.mdContent},
|
||||
</if>
|
||||
<if test="skill.isPublic != null">
|
||||
is_public = #{skill.isPublic},
|
||||
</if>
|
||||
|
|
@ -40,7 +52,7 @@
|
|||
<select id="queryByPage" resultType="com.ruoyi.platform.domain.Skill">
|
||||
select * from skill
|
||||
<include refid="common_condition"></include>
|
||||
order by update_time desc limit #{pageable.offset}, #{pageable.pageSize}
|
||||
order by praises_count desc limit #{pageable.offset}, #{pageable.pageSize}
|
||||
</select>
|
||||
|
||||
<select id="getSkillByName" resultType="com.ruoyi.platform.domain.Skill">
|
||||
|
|
@ -66,6 +78,9 @@
|
|||
<if test="skill.isPublic != null">
|
||||
and is_public = #{skill.isPublic}
|
||||
</if>
|
||||
<if test="skill.type != null">
|
||||
and type = #{skill.type}
|
||||
</if>
|
||||
<if test="skill.createBy != null and skill.createBy != ''">
|
||||
and create_by = #{skill.createBy}
|
||||
</if>
|
||||
|
|
|
|||
Loading…
Reference in New Issue