Files
yjearth/src/main/java/com/yj/earth/business/controller/ModelLibraryController.java
2025-11-19 16:10:25 +08:00

582 lines
24 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.yj.earth.business.controller;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.UUID;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yj.earth.annotation.CheckAuth;
import com.yj.earth.business.domain.ModelLibrary;
import com.yj.earth.business.domain.ModelType;
import com.yj.earth.business.service.FileInfoService;
import com.yj.earth.business.service.ModelLibraryService;
import com.yj.earth.common.util.ApiResponse;
import com.yj.earth.common.util.FileCommonUtil;
import com.yj.earth.common.util.SQLiteUtil;
import com.yj.earth.dto.militaryLibrary.DragMilitaryTypeDto;
import com.yj.earth.dto.modelLibrary.AddModelTypeDto;
import com.yj.earth.dto.modelLibrary.CreateModelLibraryDto;
import com.yj.earth.dto.modelLibrary.DragModelTypeDto;
import com.yj.earth.dto.modelLibrary.UpdateModelTypeNameDto;
import com.yj.earth.vo.ModelDataVo;
import com.yj.earth.vo.ModelTypeVo;
import com.yj.earth.vo.ModelVo;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.jetbrains.annotations.NotNull;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
@Tag(name = "模型数据管理")
@RestController
@RequestMapping("/modelLibrary")
public class ModelLibraryController {
@Resource
private ModelLibraryService modelLibraryService;
@Resource
private FileInfoService fileInfoService;
@Operation(summary = "创建模型库")
@PostMapping("/createModelLibrary")
public ApiResponse createModelLibrary(@RequestBody CreateModelLibraryDto createModelLibraryDto) {
try {
// 参数校验
String folderPath = createModelLibraryDto.getPath();
String modelName = createModelLibraryDto.getName();
// 处理路径、组合为完整模型库文件路径
File parentDir = new File(folderPath);
File modelFile = new File(parentDir, modelName);
String modelPath = modelFile.getAbsolutePath().replace("\\", "/");
// 检查父目录是否存在、不存在则创建
if (!parentDir.exists()) {
boolean mkdirsSuccess = parentDir.mkdirs();
if (!mkdirsSuccess) {
return ApiResponse.failure("创建父目录失败:" + folderPath);
}
}
// 检查模型库文件是否已存在
if (modelFile.exists()) {
if (modelFile.isDirectory()) {
return ApiResponse.failure("同名目录已存在、无法创建文件:" + modelPath);
}
return ApiResponse.failure("模型库文件已存在:" + modelPath);
}
// 创建模型库文件(
boolean createSuccess = modelFile.createNewFile();
if (!createSuccess) {
return ApiResponse.failure("创建模型库文件失败:" + modelPath);
}
// 添加模型库信息
addModelLibrary(modelPath);
SQLiteUtil.initializationModel(modelPath);
return ApiResponse.success(null);
} catch (Exception e) {
return ApiResponse.failure("创建模型库失败:" + e.getMessage());
}
}
@Operation(summary = "导入模型库")
@PostMapping("/importModelLibrary")
public ApiResponse importModelLibrary(@RequestParam("modelPath") @Parameter(description = "模型库路径") String modelPath) {
addModelLibrary(modelPath);
return ApiResponse.success(null);
}
@Operation(summary = "添加模型类型")
@PostMapping("/addModelType")
public ApiResponse addModelType(@RequestBody AddModelTypeDto addModelTypeDto) throws SQLException, IllegalAccessException, InstantiationException {
String modelPath = getModelLibrary();
if (modelPath == null) {
return ApiResponse.failure("请先创建或导入模型库");
}
// 检查父级是否存在
String parentId = addModelTypeDto.getParentId();
if (parentId != null) {
String sql = "SELECT * FROM model_type WHERE id = ?";
List<Object> params = new ArrayList<>();
params.add(parentId);
ModelType modelType = SQLiteUtil.queryForObject(modelPath, sql, params, ModelType.class);
if (modelType == null) {
return ApiResponse.failure("父级模型类型不存在");
}
}
String sql = "INSERT INTO model_type " +
"(id, name, parent_id, tree_index, created_at) " +
"VALUES (?, ?, ?, ?, ?)";
List<Object> params = new ArrayList<>();
params.add(UUID.fastUUID().toString(true));
params.add(addModelTypeDto.getName());
params.add(addModelTypeDto.getParentId());
params.add(0);
params.add(LocalDateTime.now());
SQLiteUtil.executeUpdate(modelPath, sql, params);
return ApiResponse.success(null);
}
@Operation(summary = "修改模型类型名称")
@PostMapping("/updateModelTypeName")
public ApiResponse updateModelTypeName(@RequestBody UpdateModelTypeNameDto updateModelTypeNameDto) throws SQLException {
String modelPath = getModelLibrary();
if (modelPath == null) {
return ApiResponse.failure("请先创建或导入模型库");
}
String sql = "UPDATE model_type SET name = ? WHERE id = ?";
List<Object> params = new ArrayList<>();
params.add(updateModelTypeNameDto.getName());
params.add(updateModelTypeNameDto.getId());
SQLiteUtil.executeUpdate(modelPath, sql, params);
return ApiResponse.success(null);
}
@Operation(summary = "删除模型类型")
@PostMapping("/deleteModelType")
public ApiResponse deleteModelType(@Parameter(description = "模型类型ID") @RequestParam("modelTypeId") String modelTypeId) throws SQLException {
String modelPath = getModelLibrary();
if (modelPath == null) {
return ApiResponse.failure("请先创建或导入模型库");
}
String sql = "DELETE FROM model_type WHERE id = ?";
List<Object> params = new ArrayList<>();
params.add(modelTypeId);
SQLiteUtil.executeUpdate(modelPath, sql, params);
return ApiResponse.success(null);
}
@Operation(summary = "模型类型列表")
@GetMapping("/modelTypeList")
public ApiResponse modelTypeTree() throws SQLException, IllegalAccessException, InstantiationException {
return ApiResponse.success(modelTypeList());
}
@Operation(summary = "添加模型文件")
@PostMapping("/addModelFile")
public ApiResponse addModelFile(@RequestParam("files") MultipartFile[] files, @Parameter(description = "模型类型ID") @RequestParam("modelTypeId") String modelTypeId) throws IOException, SQLException {
// 获取最新的模型库路径
String modelPath = getModelLibrary();
if (modelPath == null) {
return ApiResponse.failure("请先创建或导入模型库");
}
// 循环处理每个上传的文件
for (MultipartFile file : files) {
// 跳过空文件
if (file.isEmpty()) {
continue;
}
// 获取文件信息
String fileName = file.getOriginalFilename();
if (fileName == null) {
continue;
}
String fileSuffix = FileUtil.extName(fileName);
String fileNameWithoutSuffix = FileUtil.mainName(fileName);
// 构建插入SQL
String sql = "INSERT INTO model " +
"(id, model_type_id, model_name, model_type, model_data, created_at) " +
"VALUES (?, ?, ?, ?, ?, ?)";
List<Object> params = new ArrayList<>();
String modelId = UUID.fastUUID().toString(true);
params.add(modelId);
params.add(modelTypeId);
params.add(fileNameWithoutSuffix);
params.add(fileSuffix);
params.add(file.getBytes());
params.add(LocalDateTime.now());
// 执行插入操作
SQLiteUtil.executeUpdate(modelPath, sql, params);
}
return ApiResponse.success(null);
}
@Operation(summary = "获取模型数据")
@GetMapping("/data/{type}/{modelId}/{fileSuffix}")
public ResponseEntity<byte[]> modelData(
@Parameter(description = "数据类型") @PathVariable("type") String type,
@Parameter(description = "模型ID") @PathVariable("modelId") String modelId,
@Parameter(description = "模型类型") @PathVariable(value = "fileSuffix") String fileSuffix) {
try {
// 获取最新的模型库
String modelPath = getModelLibrary();
if (modelPath == null) {
return ResponseEntity.notFound().build();
}
List<Object> params = new ArrayList<>();
params.add(modelId);
String sql = null;
if (type.equals("model")) {
sql = "SELECT model_name, model_data FROM model WHERE id = ?";
} else {
sql = "SELECT model_name, poster_data FROM model WHERE id = ?";
}
// 查询模型数据
ModelDataVo modelDataVo = SQLiteUtil.queryForObject(
modelPath,
sql,
params,
ModelDataVo.class
);
if (modelDataVo == null) {
return ResponseEntity.notFound().build();
}
// 构建原始文件名(使用从数据库查询的名称更合理)
String originalFileName = modelDataVo.getModelName() + "." + fileSuffix;
// 对文件名进行URL编码处理、解决非ASCII字符问题
String encodedFileName = URLEncoder.encode(originalFileName, StandardCharsets.UTF_8.name());
// 根据图片后缀获取对应的MediaType
MediaType contentType = FileCommonUtil.getImageMediaType(fileSuffix);
// 设置响应头、使用 inline 确保可以预览
if (type.equals("model")) {
return ResponseEntity.ok()
.contentType(contentType)
.header(HttpHeaders.CONTENT_DISPOSITION,
"inline; filename=\"" + encodedFileName + "\"")
.body(modelDataVo.getModelData());
} else {
return ResponseEntity.ok()
.contentType(contentType)
.header(HttpHeaders.CONTENT_DISPOSITION,
"inline; filename=\"" + encodedFileName + "\"")
.body(modelDataVo.getPosterData());
}
} catch (SQLException e) {
e.printStackTrace();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
} catch (Exception e) {
e.printStackTrace();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
@Operation(summary = "根据模型类型查看模型列表")
@PostMapping("/modelList")
public ApiResponse modelList(@Parameter(description = "模型类型ID") @RequestParam("modelTypeId") String modelTypeId) throws SQLException, IllegalAccessException, InstantiationException {
// 获取最新的模型库
String modelPath = getModelLibrary();
if (modelPath == null) {
return ApiResponse.failure("请先创建或导入模型库");
}
// 获取分类ID及其所有子分类ID的列表
List<String> typeIdList = getModelTypeIdsWithChildren(modelTypeId);
if (typeIdList.isEmpty()) {
return ApiResponse.success(null);
}
String idsWithQuotes = typeIdList.stream()
.map(id -> "'" + id + "'")
.collect(Collectors.joining(","));
// 多表联查、查询所有指定分类及其子分类下的模型
String sql = """
SELECT
model.id,
model.model_type_id as modelTypeId,
model.model_name as modelName,
model.model_type as modelType,
model.poster_type as posterType,
model.created_at as createdAt,
model.updated_at as updatedAt,
model_type.name as modelTypeName
FROM model
JOIN model_type ON model.model_type_id = model_type.id
WHERE model.model_type_id IN (?)
""".replace("?", idsWithQuotes);
// 使用所有分类ID作为查询参数
List<Object> params = new ArrayList<>();
List<ModelVo> modelVos = SQLiteUtil.queryForList(modelPath, sql, null, ModelVo.class);
// 循环遍历数据
for (ModelVo modelVo : modelVos) {
if (modelVo.getModelType() != null) {
String processedModelType = trimDot(modelVo.getModelType());
modelVo.setModelDataUrl("/modelLibrary/data/model/" + modelVo.getId() + "/" + processedModelType);
modelVo.setModelType(processedModelType);
}
if (modelVo.getPosterType() != null) {
String processedPosterType = trimDot(modelVo.getPosterType());
modelVo.setPosterDataUrl("/modelLibrary/data/poster/" + modelVo.getId() + "/" + processedPosterType);
modelVo.setPosterType(processedPosterType);
}
}
return ApiResponse.success(modelVos);
}
@Operation(summary = "更新模型信息")
@PostMapping("/uploadModelInfo")
public ApiResponse uploadModelInfo(@Parameter(description = "模型封面文件") @RequestParam(value = "file", required = false) MultipartFile file,
@Parameter(description = "模型ID") @RequestParam("modelId") String modelId,
@Parameter(description = "模型名称") @RequestParam(value = "modelName", required = false) String modelName) throws IOException, SQLException {
// 获取最新的模型库路径
String modelPath = getModelLibrary();
if (modelPath == null) {
return ApiResponse.failure("请先创建或导入模型库");
}
// 动态构建SQL更新语句和参数
StringBuilder sql = new StringBuilder("UPDATE model SET updated_at = ? ");
List<Object> params = new ArrayList<>();
// 始终更新时间
params.add(LocalDateTime.now());
// 处理封面文件
if (file != null && !file.isEmpty()) {
String fileSuffix = FileUtil.extName(file.getOriginalFilename());
sql.append(", poster_type = ?, poster_data = ? ");
params.add(fileSuffix);
params.add(file.getBytes());
}
// 处理模型名称
if (StringUtils.hasText(modelName)) {
sql.append(", model_name = ? ");
params.add(modelName);
}
// 拼接WHERE条件
sql.append("WHERE id = ?");
params.add(modelId);
// 执行更新
SQLiteUtil.executeUpdate(modelPath, sql.toString(), params);
return ApiResponse.success(null);
}
@Operation(summary = "删除模型")
@PostMapping("/deleteModel")
public ApiResponse deleteModel(@Parameter(description = "模型ID") @RequestParam("modelId") String modelId) throws SQLException {
String modelPath = getModelLibrary();
if (modelPath == null) {
return ApiResponse.failure("请先创建或导入模型库");
}
String sql = "DELETE FROM model WHERE id = ?";
List<Object> params = new ArrayList<>();
params.add(modelId);
SQLiteUtil.executeUpdate(modelPath, sql, params);
return ApiResponse.success(null);
}
@Operation(summary = "拖动层级")
@PostMapping("/dragModelType")
public ApiResponse dragModelType(@RequestBody List<DragModelTypeDto> dragModelTypeDtoList) throws SQLException, IllegalAccessException, InstantiationException {
String modelPath = getModelLibrary();
if (modelPath == null) {
return ApiResponse.failure("请先创建或导入模型库");
}
// 遍历数据列表
for (DragModelTypeDto dragModelTypeDto : dragModelTypeDtoList) {
String id = dragModelTypeDto.getId();
String parentId = dragModelTypeDto.getParentId();
String treeIndex = dragModelTypeDto.getTreeIndex();
List<Object> params = new ArrayList<>();
params.add(parentId);
params.add(treeIndex);
params.add(id);
SQLiteUtil.executeUpdate(modelPath, "UPDATE model_type SET parent_id = ?, tree_index = ? WHERE id = ?", params);
}
// 返回树列表
return ApiResponse.success(modelTypeList());
}
private List<ModelTypeVo> modelTypeList() throws SQLException, IllegalAccessException, InstantiationException {
String modelPath = getModelLibrary();
if (modelPath == null) {
return null;
}
String sql = """
SELECT id, name, parent_id as parentId,
tree_index as treeIndex, created_at as createdAt,
updated_at as updatedAt FROM model_type ORDER BY tree_index ASC
""";
// 查询所有模型的类型
List<ModelType> modelTypes = SQLiteUtil.queryForList(modelPath, sql, null, ModelType.class);
// 转换为树形结构
return buildModelTypeTree(modelTypes);
}
private List<ModelTypeVo> buildModelTypeTree(List<ModelType> modelTypes) {
List<ModelTypeVo> treeNodes = modelTypes.stream()
.map(modelType -> new ModelTypeVo(modelType))
.collect(Collectors.toList());
// 构建节点ID到节点的映射
Map<String, ModelTypeVo> nodeMap = treeNodes.stream()
.collect(Collectors.toMap(ModelTypeVo::getId, node -> node));
// 根节点列表
List<ModelTypeVo> rootNodes = new ArrayList<>();
// 为每个节点添加子节点
for (ModelTypeVo node : treeNodes) {
String parentId = node.getParentId();
if (parentId == null || parentId.isEmpty()) {
// 没有父节点的是根节点
rootNodes.add(node);
} else {
// 找到父节点并添加为子节点
ModelTypeVo parentNode = nodeMap.get(parentId);
if (parentNode != null) {
parentNode.getChildren().add(node);
}
}
}
// 排序根节点
rootNodes.sort(Comparator.comparingInt(ModelTypeVo::getTreeIndex));
// 递归排序所有子节点
sortChildren(rootNodes);
return rootNodes;
}
// 递归排序所有子节点
private void sortChildren(List<ModelTypeVo> nodes) {
if (nodes == null || nodes.isEmpty()) {
return;
}
// 排序当前层级节点
nodes.sort(Comparator.comparingInt(ModelTypeVo::getTreeIndex));
// 递归排序下一层级
for (ModelTypeVo node : nodes) {
sortChildren(node.getChildren());
}
}
private String getModelLibrary() {
// 获取启用的模型库
LambdaQueryWrapper<ModelLibrary> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ModelLibrary::getIsEnable, 1);
ModelLibrary modelLibrary = modelLibraryService.getOne(queryWrapper);
if (modelLibrary == null) {
return null;
}
return modelLibrary.getPath();
}
private void addModelLibrary(String modelPath) {
// 查询系统所有的模型库
List<ModelLibrary> modelLibraries = modelLibraryService.list();
// 遍历并更新状态
for (ModelLibrary modelLibrary : modelLibraries) {
// 设置启用状态为0
modelLibrary.setIsEnable(0);
modelLibraryService.updateById(modelLibrary);
}
// 检查相同路径的模型库是否已存在
LambdaQueryWrapper<ModelLibrary> pathWrapper = new LambdaQueryWrapper<>();
pathWrapper.eq(ModelLibrary::getPath, modelPath);
ModelLibrary existingModel = modelLibraryService.getOne(pathWrapper);
// 若存在相同路径的模型库、不做处理、仅仅更新状态为显示
if (existingModel != null) {
existingModel.setIsEnable(1);
modelLibraryService.updateById(existingModel);
return;
} else {
// 新增模型库
ModelLibrary newModel = new ModelLibrary();
File file = FileUtil.file(modelPath);
newModel.setId(UUID.fastUUID().toString(true));
newModel.setPath(modelPath);
newModel.setName(FileUtil.extName(file));
newModel.setIsEnable(1);
modelLibraryService.save(newModel);
}
}
/**
* 根据模型分类ID、查询该分类ID及其所有子分类ID的列表递归包含所有层级子分类
*/
private List<String> getModelTypeIdsWithChildren(String modelTypeId) throws SQLException, IllegalAccessException, InstantiationException {
// 结果列表初始化
List<String> typeIdList = new ArrayList<>();
// 校验入参分类ID不能为空
if (StringUtils.isEmpty(modelTypeId)) {
return null;
}
// 获取当前启用的模型库路径
String modelPath = getModelLibrary();
if (modelPath == null) {
return null;
}
// 校验目标分类ID是否存在避免无效ID导致递归无结果
if (!isModelTypeExist(modelPath, modelTypeId)) {
return null;
}
// 递归查询: 收集当前分类及所有子分类ID
recursiveGetChildIds(modelPath, modelTypeId, typeIdList);
return typeIdList;
}
/**
* 递归查询子分类ID、并将结果存入列表
*/
private void recursiveGetChildIds(String modelPath, String currentTypeId, List<String> resultList) throws SQLException, IllegalAccessException, InstantiationException {
// 先将当前分类ID加入结果列表确保包含自身
resultList.add(currentTypeId);
// 查询当前分类的「直接子分类」
String childSql = "SELECT id FROM model_type WHERE parent_id = ? ORDER BY tree_index ASC";
List<Object> childParams = new ArrayList<>();
childParams.add(currentTypeId);
List<ModelType> childModelTypes = SQLiteUtil.queryForList(
modelPath, childSql, childParams, ModelType.class
);
// 若存在子分类、递归查询每个子分类的子分类
if (childModelTypes != null && !childModelTypes.isEmpty()) {
for (ModelType childType : childModelTypes) {
recursiveGetChildIds(modelPath, childType.getId(), resultList);
}
}
}
/**
* 校验模型分类ID是否存在于模型库中
*/
private boolean isModelTypeExist(String modelPath, String typeId)
throws SQLException, IllegalAccessException, InstantiationException {
String checkSql = "SELECT id FROM model_type WHERE id = ?";
List<Object> checkParams = new ArrayList<>();
checkParams.add(typeId);
List<ModelType> existTypes = SQLiteUtil.queryForList(modelPath, checkSql, checkParams, ModelType.class);
// 若查询结果非空、说明分类存在
return existTypes != null && !existTypes.isEmpty();
}
private String trimDot(String str) {
if (str == null) {
return null;
}
return str.startsWith(".") ? str.substring(1) : str;
}
}