feat(video): 新增模型管理与MinIO对象存储功能

- 新增算法模型实体类VModel及对应CRUD接口和实现
- 新增MinIO对象记录实体类VMinioObject及对应CRUD接口和实现
- 实现模型下载重定向功能
- 扩展MinioService支持指定文件名上传和删除对象
- 在CommonController中增加上传后持久化MinIO对象记录逻辑
- 新增ModelController用于模型管理RESTful接口- 新增VMinioObjectController用于MinIO对象记录管理接口
- 添加相关Mapper XML配置和DAO接口
- 更新pom.xml引入必要依赖
This commit is contained in:
2025-09-29 10:37:12 +08:00
parent e4f0c65478
commit af815e00ee
16 changed files with 611 additions and 4 deletions

View File

@@ -22,6 +22,9 @@ import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.framework.config.ServerConfig;
import com.ruoyi.framework.config.MinioProperties;
import com.ruoyi.framework.service.MinioService;
import com.ruoyi.video.service.IVMinioObjectService;
import com.ruoyi.video.domain.VMinioObject;
import com.ruoyi.common.utils.SecurityUtils;
/**
* 通用请求处理
@@ -43,6 +46,9 @@ public class CommonController
@Autowired(required = false)
private MinioProperties minioProperties;
@Autowired
private IVMinioObjectService vMinioObjectService;
private static final String FILE_DELIMETER = ",";
/**
@@ -87,9 +93,18 @@ public class CommonController
{
if (minioProperties != null && minioProperties.isEnabled())
{
MinioService.UploadResult result = minioService.upload(file);
String fileName = result.getObjectName();
// 先生成唯一文件名(含扩展名),再用该名称上传
String uniqueObjectName = FileUploadUtils.extractFilename(file);
MinioService.UploadResult result = minioService.uploadWithName(file, uniqueObjectName);
String fileName = result.getObjectName(); // 即 uniqueObjectName
String url = result.getUrl();
// persist to v_minio_object
VMinioObject record = new VMinioObject();
record.setObjectName(fileName);
record.setUrl(url);
record.setOriginalName(file.getOriginalFilename());
try { record.setCreateBy(SecurityUtils.getUsername()); } catch (Exception ignored) {}
vMinioObjectService.insert(record);
AjaxResult ajax = AjaxResult.success();
ajax.put("url", url);
ajax.put("fileName", fileName);
@@ -132,13 +147,21 @@ public class CommonController
{
for (MultipartFile file : files)
{
MinioService.UploadResult result = minioService.upload(file);
String fileName = result.getObjectName();
String uniqueObjectName = FileUploadUtils.extractFilename(file);
MinioService.UploadResult result = minioService.uploadWithName(file, uniqueObjectName);
String fileName = result.getObjectName(); // 即 uniqueObjectName
String url = result.getUrl();
urls.add(url);
fileNames.add(fileName);
newFileNames.add(FileUtils.getName(fileName));
originalFilenames.add(file.getOriginalFilename());
// persist each to v_minio_object
VMinioObject record = new VMinioObject();
record.setObjectName(fileName);
record.setUrl(url);
record.setOriginalName(file.getOriginalFilename());
try { record.setCreateBy(SecurityUtils.getUsername()); } catch (Exception ignored) {}
vMinioObjectService.insert(record);
}
}
else

View File

@@ -0,0 +1,88 @@
package com.ruoyi.web.controller.system;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.service.MinioService;
import com.ruoyi.video.domain.VMinioObject;
import com.ruoyi.video.service.IVMinioObjectService;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/system/minio-object")
public class VMinioObjectController {
private final IVMinioObjectService vMinioObjectService;
private final MinioService minioService;
public VMinioObjectController(IVMinioObjectService vMinioObjectService,
MinioService minioService) {
this.vMinioObjectService = vMinioObjectService;
this.minioService = minioService;
}
/**
* 查询根据主键ID查询一条记录
*/
@GetMapping("/{id}")
public AjaxResult getById(@PathVariable("id") Long id) {
VMinioObject obj = vMinioObjectService.selectById(id);
if (obj == null) {
return AjaxResult.error("记录不存在");
}
return AjaxResult.success(obj);
}
/**
* 查询:根据唯一对象名查询
*/
@GetMapping("/name/{objectName}")
public AjaxResult getByObjectName(@PathVariable("objectName") String objectName) {
VMinioObject obj = vMinioObjectService.selectByObjectName(objectName);
if (obj == null) {
return AjaxResult.error("记录不存在");
}
return AjaxResult.success(obj);
}
/**
* 删除根据主键ID删除先删 MinIO后删数据库
*/
@DeleteMapping("/{id}")
public AjaxResult deleteById(@PathVariable("id") Long id) {
VMinioObject obj = vMinioObjectService.selectById(id);
if (obj == null) {
return AjaxResult.error("记录不存在或已删除");
}
String objectName = obj.getObjectName();
if (StringUtils.isEmpty(objectName)) {
return AjaxResult.error("对象名为空,无法删除 MinIO 对象");
}
try {
// 先删除 MinIO 中的对象,确保不留悬挂数据
minioService.deleteObject(objectName);
} catch (Exception e) {
return AjaxResult.error("删除 MinIO 对象失败: " + e.getMessage());
}
// 再删除数据库记录
int rows = vMinioObjectService.deleteById(id);
return rows > 0 ? AjaxResult.success() : AjaxResult.error("删除数据库记录失败");
}
/**
* 删除:根据唯一对象名删除(先 MinIO再 DB
*/
@DeleteMapping("/name/{objectName}")
public AjaxResult deleteByObjectName(@PathVariable("objectName") String objectName) {
VMinioObject obj = vMinioObjectService.selectByObjectName(objectName);
if (obj == null) {
return AjaxResult.error("记录不存在或已删除");
}
try {
minioService.deleteObject(objectName);
} catch (Exception e) {
return AjaxResult.error("删除 MinIO 对象失败: " + e.getMessage());
}
int rows = vMinioObjectService.deleteByObjectName(objectName);
return rows > 0 ? AjaxResult.success() : AjaxResult.error("删除数据库记录失败");
}
}

View File

@@ -0,0 +1,97 @@
package com.ruoyi.web.controller.video;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.video.domain.VModel;
import com.ruoyi.video.service.IVModelService;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/video/model")
public class ModelController {
private final IVModelService modelService;
public ModelController(IVModelService modelService) {
this.modelService = modelService;
}
/** 新增模型JSON */
@PostMapping
public AjaxResult create(@RequestBody VModel model) {
if (StringUtils.isEmpty(model.getModelName())) {
return AjaxResult.error("模型名称不能为空");
}
if (StringUtils.isEmpty(model.getUrl())) {
return AjaxResult.error("模型URL不能为空");
}
if (StringUtils.isEmpty(model.getFramework())) {
model.setFramework("onnx");
}
int rows = modelService.insert(model);
return rows > 0 ? AjaxResult.success(model) : AjaxResult.error("新增失败");
}
/** 根据ID查询 */
@GetMapping("/{id}")
public AjaxResult get(@PathVariable("id") Long id) {
VModel model = modelService.selectById(id);
return model != null ? AjaxResult.success(model) : AjaxResult.error("未找到记录");
}
/** 列表查询(可选条件) */
@GetMapping("/list")
public AjaxResult list(@RequestParam(value = "modelName", required = false) String modelName,
@RequestParam(value = "framework", required = false) String framework,
@RequestParam(value = "enabled", required = false) Integer enabled,
@RequestParam(value = "keyword", required = false) String keyword) {
Map<String, Object> params = new HashMap<>();
params.put("modelName", modelName);
params.put("framework", framework);
params.put("enabled", enabled);
params.put("keyword", keyword);
List<VModel> list = modelService.selectList(params);
return AjaxResult.success(list);
}
/** 删除 */
@DeleteMapping("/{id}")
public AjaxResult delete(@PathVariable("id") Long id) {
int rows = modelService.deleteById(id);
return rows > 0 ? AjaxResult.success() : AjaxResult.error("删除失败");
}
/** 启用/禁用 */
@PutMapping("/{id}/enable")
public AjaxResult enable(@PathVariable("id") Long id,
@RequestParam("enabled") Integer enabled) {
if (enabled == null || (enabled != 0 && enabled != 1)) {
return AjaxResult.error("enabled 仅支持 0 或 1");
}
int rows = modelService.updateEnabled(id, enabled);
return rows > 0 ? AjaxResult.success() : AjaxResult.error("更新失败");
}
/** 下载:直接 302 重定向到模型URL确保可点击下载 */
@GetMapping("/download/{id}")
public void download(@PathVariable("id") Long id, HttpServletResponse response) throws IOException {
VModel model = modelService.selectById(id);
if (model == null || StringUtils.isEmpty(model.getUrl())) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, "模型或URL不存在");
return;
}
// 302 跳转到真实URL
String target = model.getUrl();
response.setStatus(HttpServletResponse.SC_FOUND);
response.setHeader("Location", target);
response.setHeader("Content-Disposition", "attachment; filename=\"" + URLEncoder.encode(model.getModelName(), StandardCharsets.UTF_8) + ".onnx\"");
}
}