Files
yjearth/src/main/java/com/yj/earth/business/controller/MilitaryLibraryController.java
2025-12-08 14:50:27 +08:00

630 lines
26 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.*;
import com.yj.earth.business.service.MilitaryLibraryService;
import com.yj.earth.business.service.SourceService;
import com.yj.earth.common.util.ApiResponse;
import com.yj.earth.common.util.FileCommonUtil;
import com.yj.earth.common.util.JsonUtil;
import com.yj.earth.common.util.SQLiteUtil;
import com.yj.earth.dto.militaryLibrary.AddMilitaryTypeDto;
import com.yj.earth.dto.militaryLibrary.CreateMilitaryLibraryDto;
import com.yj.earth.dto.militaryLibrary.DragMilitaryTypeDto;
import com.yj.earth.dto.militaryLibrary.UpdateMilitaryTypeNameDto;
import com.yj.earth.params.MilitaryParam;
import com.yj.earth.params.ModelParam;
import com.yj.earth.vo.MilitaryDataVo;
import com.yj.earth.vo.MilitaryTypeVo;
import com.yj.earth.vo.MilitaryVo;
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.beans.factory.annotation.Value;
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.nio.file.Files;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Tag(name = "军标数据管理")
@RestController
@RequestMapping("/militaryLibrary")
public class MilitaryLibraryController {
@Resource
private MilitaryLibraryService militaryLibraryService;
@Resource
private SourceService sourceService;
@Value("${server.host}")
private String serverHost;
@Value("${server.port}")
private int serverPort;
@Operation(summary = "创建军标库")
@PostMapping("/createMilitaryLibrary")
public ApiResponse createMilitaryLibrary(@RequestBody CreateMilitaryLibraryDto createDto) {
try {
// 参数校验与路径处理
String folderPath = createDto.getPath();
String libraryName = createDto.getName();
if (!StringUtils.hasText(folderPath) || !StringUtils.hasText(libraryName)) {
return ApiResponse.failure("路径和名称不能为空");
}
// 构建完整军标库文件路径SQLite文件
File parentDir = new File(folderPath);
File militaryFile = new File(parentDir, libraryName);
String militaryPath = militaryFile.getAbsolutePath().replace("\\", "/");
// 父目录不存在则创建
if (!parentDir.exists()) {
boolean mkdirsSuccess = parentDir.mkdirs();
if (!mkdirsSuccess) {
return ApiResponse.failure("创建父目录失败: " + folderPath);
}
}
// 校验军标库文件是否已存在
if (militaryFile.exists()) {
if (militaryFile.isDirectory()) {
return ApiResponse.failure("同名目录已存在、无法创建文件: " + militaryPath);
}
return ApiResponse.failure("军标库文件已存在: " + militaryPath);
}
// 创建SQLite文件
boolean createFileSuccess = militaryFile.createNewFile();
if (!createFileSuccess) {
return ApiResponse.failure("创建军标库文件失败: " + militaryPath);
}
// 初始化军标库表结构
SQLiteUtil.initializationMilitary(militaryPath);
// 添加军标库配置到数据库
addMilitaryLibrary(militaryPath);
return ApiResponse.success(null);
} catch (Exception e) {
return ApiResponse.failure("创建军标库失败: " + e.getMessage());
}
}
@Operation(summary = "导入军标库")
@PostMapping("/importMilitaryLibrary")
public ApiResponse importMilitaryLibrary(
@RequestParam("militaryPath") @Parameter(description = "军标库SQLite文件路径") String militaryPath) {
try {
// 校验路径是否存在
File militaryFile = new File(militaryPath);
if (!militaryFile.exists() || !militaryFile.isFile()) {
return ApiResponse.failure("军标库文件不存在: " + militaryPath);
}
// 添加到配置表并启用
addMilitaryLibrary(militaryPath);
return ApiResponse.success(null);
} catch (Exception e) {
return ApiResponse.failure("导入军标库失败: " + e.getMessage());
}
}
@Operation(summary = "添加军标类型")
@PostMapping("/addMilitaryType")
public ApiResponse addMilitaryType(@RequestBody AddMilitaryTypeDto addDto) throws SQLException, IllegalAccessException, InstantiationException {
// 获取当前启用的军标库路径
String militaryPath = getMilitaryLibrary();
if (militaryPath == null) {
return ApiResponse.failure("请先创建或导入军标库");
}
// 校验父级类型(若有)
String parentId = addDto.getParentId();
if (StringUtils.hasText(parentId)) {
String checkParentSql = "SELECT id FROM military_type WHERE id = ?";
List<Object> parentParams = new ArrayList<>();
parentParams.add(parentId);
MilitaryType parentType = SQLiteUtil.queryForObject(
militaryPath, checkParentSql, parentParams, MilitaryType.class
);
if (parentType == null) {
return ApiResponse.failure("父级军标类型不存在: " + parentId);
}
}
// 插入军标类型
String insertSql = "INSERT INTO military_type " +
"(id, name, parent_id, tree_index, created_at) " +
"VALUES (?, ?, ?, ?, ?)";
List<Object> params = new ArrayList<>();
params.add(UUID.fastUUID().toString(true));
params.add(addDto.getName());
params.add(parentId);
params.add(0);
params.add(LocalDateTime.now().toString());
SQLiteUtil.executeUpdate(militaryPath, insertSql, params);
return ApiResponse.success(null);
}
@Operation(summary = "修改军标类型名称")
@PostMapping("/updateMilitaryTypeName")
public ApiResponse updateMilitaryTypeName(@RequestBody UpdateMilitaryTypeNameDto updateDto)
throws SQLException {
String militaryPath = getMilitaryLibrary();
if (militaryPath == null) {
return ApiResponse.failure("请先创建或导入军标库");
}
// 执行更新
String updateSql = "UPDATE military_type SET name = ?, updated_at = ? WHERE id = ?";
List<Object> params = new ArrayList<>();
params.add(updateDto.getName());
params.add(LocalDateTime.now().toString());
params.add(updateDto.getId());
SQLiteUtil.executeUpdate(militaryPath, updateSql, params);
return ApiResponse.success(null);
}
@Operation(summary = "删除军标类型")
@PostMapping("/deleteMilitaryType")
public ApiResponse deleteMilitaryType(
@RequestParam("militaryTypeId") @Parameter(description = "军标类型ID") String typeId)
throws SQLException {
String militaryPath = getMilitaryLibrary();
if (militaryPath == null) {
return ApiResponse.failure("请先创建或导入军标库");
}
// 执行删除
String deleteSql = "DELETE FROM military_type WHERE id = ?";
List<Object> params = new ArrayList<>();
params.add(typeId);
SQLiteUtil.executeUpdate(militaryPath, deleteSql, params);
return ApiResponse.success(null);
}
@Operation(summary = "军标类型树形列表")
@GetMapping("/militaryTypeTree")
public ApiResponse militaryTypeTree(@Parameter(description = "军标名称") @RequestParam(value = "militaryName", required = false) String militaryName) throws SQLException, IllegalAccessException, InstantiationException {
List<MilitaryTypeVo> treeList = militaryTypeList(militaryName);
if (treeList == null) {
return ApiResponse.successWithMessage("请先创建或导入军标库");
}
return ApiResponse.success(treeList);
}
@Operation(summary = "添加军标文件")
@PostMapping("/addMilitaryFile")
public ApiResponse addMilitaryFile(@RequestParam("filePaths") List<String> filePaths,
@RequestParam("militaryTypeId") @Parameter(description = "军标类型ID") String typeId) throws IOException, SQLException {
String militaryPath = getMilitaryLibrary();
// 循环处理每个绝对路径对应的文件
for (String filePath : filePaths) {
File file = new File(filePath);
// 解析文件名与后缀
String originalFileName = file.getName();
String fileSuffix = FileUtil.extName(originalFileName);
String fileNameWithoutSuffix = FileUtil.mainName(originalFileName);
// 插入军标数据
String insertSql = "INSERT INTO military " +
"(id, military_type_id, military_name, military_type, military_data, created_at) " +
"VALUES (?, ?, ?, ?, ?, ?)";
List<Object> params = new ArrayList<>();
params.add(UUID.fastUUID().toString(true));
params.add(typeId);
params.add(fileNameWithoutSuffix);
params.add(fileSuffix);
params.add(Files.readAllBytes(file.toPath()));
params.add(LocalDateTime.now().toString());
SQLiteUtil.executeUpdate(militaryPath, insertSql, params);
}
return ApiResponse.success(null);
}
@Operation(summary = "获取军标文件数据")
@GetMapping("/data/military/{militaryId}/{fileSuffix}")
public ResponseEntity<byte[]> getMilitaryData(@PathVariable("militaryId") @Parameter(description = "军标ID") String militaryId, @PathVariable("fileSuffix") @Parameter(description = "军标文件后缀") String fileSuffix) {
try {
String militaryPath = getMilitaryLibrary();
if (militaryPath == null) {
return ResponseEntity.notFound().build();
}
// 查询军标二进制数据
String querySql = "SELECT military_name, military_data FROM military WHERE id = ?";
List<Object> params = new ArrayList<>();
params.add(militaryId);
MilitaryDataVo dataVo = SQLiteUtil.queryForObject(
militaryPath, querySql, params, MilitaryDataVo.class
);
if (dataVo == null || dataVo.getMilitaryData() == null) {
return ResponseEntity.notFound().build();
}
// 构建响应头【支持中文文件名+预览】
String originalFileName = dataVo.getMilitaryName() + "." + fileSuffix;
String encodedFileName = URLEncoder.encode(originalFileName, StandardCharsets.UTF_8.name());
MediaType contentType = FileCommonUtil.getImageMediaType(fileSuffix);
return ResponseEntity.ok()
.contentType(contentType)
.header(HttpHeaders.CONTENT_DISPOSITION,
"inline; filename=\"" + encodedFileName + "\"")
.body(dataVo.getMilitaryData());
} catch (Exception e) {
e.printStackTrace();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
@Operation(summary = "根据类型和名称模糊查询军标列表")
@PostMapping("/militaryList")
public ApiResponse getMilitaryList(
@RequestParam("militaryTypeId") @Parameter(description = "军标类型ID") String militaryTypeId,
@Parameter(description = "军标名称模糊查询") @RequestParam(value = "name", required = false) String name)
throws SQLException, IllegalAccessException, InstantiationException {
String militaryPath = getMilitaryLibrary();
if (militaryPath == null) {
return ApiResponse.failure("请先创建或导入军标库");
}
// 获取当前类型及所有子类型ID递归
List<String> typeIdList = getMilitaryTypeIdsWithChildren(militaryTypeId);
if (typeIdList == null || typeIdList.isEmpty()) {
return ApiResponse.success(new ArrayList<>());
}
// 构建IN条件
String idsWithQuotes = typeIdList.stream()
.map(id -> "'" + id + "'")
.collect(Collectors.joining(","));
// 构建SQL语句
StringBuilder sql = new StringBuilder("""
SELECT
m.id,
m.military_type_id as militaryTypeId,
m.military_name as militaryName,
m.military_type as militaryType,
m.created_at as createdAt,
m.updated_at as updatedAt,
t.name as militaryTypeName
FROM military m
JOIN military_type t ON m.military_type_id = t.id
WHERE m.military_type_id IN (%s)
""".formatted(idsWithQuotes));
// 如果传入了名称、则增加模糊查询条件
if (name != null && !name.trim().isEmpty()) {
// 直接拼接SQL、注意SQL注入风险
sql.append(" AND m.military_name LIKE '%" + name.trim() + "%'");
}
// 统一添加排序条件
sql.append(" ORDER BY m.created_at DESC");
// 执行查询
List<MilitaryVo> militaryVoList = SQLiteUtil.queryForList(
militaryPath, sql.toString(), null, MilitaryVo.class
);
for (MilitaryVo vo : militaryVoList) {
vo.setMilitaryDataUrl("/militaryLibrary/data/military/" + vo.getId() + "/" + vo.getMilitaryType());
}
return ApiResponse.success(militaryVoList);
}
@Operation(summary = "更新军标名称")
@PostMapping("/updateMilitaryInfo")
public ApiResponse updateMilitaryInfo(
@RequestParam("militaryId") @Parameter(description = "军标ID") String militaryId,
@RequestParam("militaryName") @Parameter(description = "新军标名称") String militaryName)
throws SQLException {
String militaryPath = getMilitaryLibrary();
if (militaryPath == null) {
return ApiResponse.failure("请先创建或导入军标库");
}
String updateSql = "UPDATE military SET military_name = ?, updated_at = ? WHERE id = ?";
List<Object> params = new ArrayList<>();
params.add(militaryName);
params.add(LocalDateTime.now().toString());
params.add(militaryId);
SQLiteUtil.executeUpdate(militaryPath, updateSql, params);
return ApiResponse.success(null);
}
@Operation(summary = "删除军标")
@PostMapping("/deleteMilitary")
public ApiResponse deleteMilitary(@RequestParam("militaryId") @Parameter(description = "军标ID") String militaryId) throws SQLException {
String militaryPath = getMilitaryLibrary();
if (militaryPath == null) {
return ApiResponse.failure("请先创建或导入军标库");
}
// 存储被删除的Source ID列表
List<String> deletedSourceIds = new ArrayList<>();
// 获取URL
String MilitaryDataUrlByModelId = getMilitaryDataUrlByModelId(militaryId);
// 查询资源
LambdaQueryWrapper<Source> queryWrapper = new LambdaQueryWrapper<>();
// 查询类型为 model 的
queryWrapper.eq(Source::getSourceType, "military");
// 查询列表
List<Source> sourceList = sourceService.list(queryWrapper);
// 遍历数据
for (Source source : sourceList) {
// 取出 params 字段
String dataParams = source.getParams();
if (dataParams != null) {
// 转换为 Model 对象
MilitaryParam militaryParam = JsonUtil.jsonToObject(dataParams, MilitaryParam.class);
if (MilitaryDataUrlByModelId.equals(militaryParam.getUrl())) {
// 删除这个资源
sourceService.removeById(source.getId());
// 添加到被删除的ID列表
deletedSourceIds.add(source.getId());
}
}
}
// 执行删除
String deleteSql = "DELETE FROM military WHERE id = ?";
List<Object> params = new ArrayList<>();
params.add(militaryId);
SQLiteUtil.executeUpdate(militaryPath, deleteSql, params);
return ApiResponse.success(deletedSourceIds);
}
@Operation(summary = "拖动调整军标类型层级")
@PostMapping("/dragMilitaryType")
public ApiResponse dragMilitaryType(@RequestBody List<DragMilitaryTypeDto> dragDtoList)
throws SQLException, IllegalAccessException, InstantiationException {
String militaryPath = getMilitaryLibrary();
if (militaryPath == null) {
return ApiResponse.failure("请先创建或导入军标库");
}
// 批量更新层级和排序
for (DragMilitaryTypeDto dto : dragDtoList) {
String updateSql = "UPDATE military_type " +
"SET parent_id = ?, tree_index = ?, updated_at = ? " +
"WHERE id = ?";
List<Object> params = new ArrayList<>();
params.add(dto.getParentId());
params.add(dto.getTreeIndex());
params.add(LocalDateTime.now().toString());
params.add(dto.getId());
SQLiteUtil.executeUpdate(militaryPath, updateSql, params);
}
// 返回更新后的树形列表
List<MilitaryTypeVo> treeList = militaryTypeList(null);
return ApiResponse.success(treeList);
}
private String getMilitaryLibrary() {
LambdaQueryWrapper<MilitaryLibrary> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(MilitaryLibrary::getIsEnable, 1); // 1=启用、0=未启用
MilitaryLibrary library = militaryLibraryService.getOne(queryWrapper);
if (library == null) {
return null;
}
return library.getPath();
}
private void addMilitaryLibrary(String militaryPath) {
// 所有已存在的军标库设置为「未启用」
List<MilitaryLibrary> existLibraries = militaryLibraryService.list();
for (MilitaryLibrary library : existLibraries) {
library.setIsEnable(0);
militaryLibraryService.updateById(library);
}
// 检查路径是否已存在(存在则启用、不存在则新增)
LambdaQueryWrapper<MilitaryLibrary> pathWrapper = new LambdaQueryWrapper<>();
pathWrapper.eq(MilitaryLibrary::getPath, militaryPath);
MilitaryLibrary existLibrary = militaryLibraryService.getOne(pathWrapper);
if (existLibrary != null) {
existLibrary.setIsEnable(1);
militaryLibraryService.updateById(existLibrary);
} else {
MilitaryLibrary newLibrary = new MilitaryLibrary();
newLibrary.setId(UUID.fastUUID().toString(true));
newLibrary.setPath(militaryPath);
newLibrary.setName(FileUtil.mainName(militaryPath));
newLibrary.setIsEnable(1);
newLibrary.setCreatedAt(LocalDateTime.now());
militaryLibraryService.save(newLibrary);
}
}
/**
* 查询军标分类树形结构
*/
private List<MilitaryTypeVo> militaryTypeList(String militaryName) throws SQLException, IllegalAccessException, InstantiationException {
String militaryPath = getMilitaryLibrary();
if (militaryPath == null) {
return null;
}
// 构建基础SQL语句
StringBuilder sqlBuilder = new StringBuilder("""
SELECT DISTINCT military_type.id, military_type.name, military_type.parent_id,
military_type.tree_index, military_type.created_at,
military_type.updated_at
FROM military_type
""");
// 如果传入了军标名称、则拼接 JOIN 和 WHERE 条件
if (militaryName != null && !militaryName.trim().isEmpty()) {
String trimmedName = militaryName.trim();
// 直接拼接SQL
sqlBuilder.append(" INNER JOIN military ON military_type.id = military.military_type_id");
sqlBuilder.append(" WHERE military.military_name LIKE '%" + trimmedName + "%'");
}
// 为所有查询都加上统一的排序条件
sqlBuilder.append(" ORDER BY military_type.tree_index ASC");
// 执行查询、获取符合条件的军标分类列表
List<MilitaryType> militaryTypes = SQLiteUtil.queryForList(
militaryPath,
sqlBuilder.toString(),
null,
MilitaryType.class
);
// 将扁平的分类列表转换为树形结构
return buildMilitaryTypeTree(militaryTypes);
}
private List<MilitaryTypeVo> buildMilitaryTypeTree(List<MilitaryType> typeList) {
// 转换为VO
List<MilitaryTypeVo> voList = typeList.stream()
.map(militaryType -> new MilitaryTypeVo(militaryType)).collect(Collectors.toList());
// 构建ID→VO的映射方便快速查找父级
Map<String, MilitaryTypeVo> voMap = voList.stream()
.collect(Collectors.toMap(MilitaryTypeVo::getId, vo -> vo));
// 组装树形结构
List<MilitaryTypeVo> rootList = new ArrayList<>();
for (MilitaryTypeVo vo : voList) {
String parentId = vo.getParentId();
if (parentId == null || parentId.isEmpty()) {
rootList.add(vo);
} else {
MilitaryTypeVo parentVo = voMap.get(parentId);
if (parentVo != null) {
parentVo.getChildren().add(vo);
}
}
}
// 排序
rootList.sort(Comparator.comparingInt(MilitaryTypeVo::getTreeIndex));
sortMilitaryTypeChildren(rootList);
return rootList;
}
private void sortMilitaryTypeChildren(List<MilitaryTypeVo> voList) {
if (voList == null || voList.isEmpty()) {
return;
}
// 排序当前层级
voList.sort(Comparator.comparingInt(MilitaryTypeVo::getTreeIndex));
// 递归排序子层级
for (MilitaryTypeVo vo : voList) {
sortMilitaryTypeChildren(vo.getChildren());
}
}
private List<String> getMilitaryTypeIdsWithChildren(String typeId)
throws SQLException, IllegalAccessException, InstantiationException {
List<String> idList = new ArrayList<>();
if (!StringUtils.hasText(typeId)) {
return idList;
}
String militaryPath = getMilitaryLibrary();
if (militaryPath == null) {
return idList;
}
// 校验类型是否存在
if (!isMilitaryTypeExist(militaryPath, typeId)) {
return idList;
}
// 递归收集ID包含自身
recursiveGetMilitaryTypeChildren(militaryPath, typeId, idList);
return idList;
}
private void recursiveGetMilitaryTypeChildren(String militaryPath, String currentId, List<String> idList)
throws SQLException, IllegalAccessException, InstantiationException {
// 先添加当前ID
idList.add(currentId);
// 查询直接子类型
String querySql = "SELECT id FROM military_type WHERE parent_id = ? ORDER BY tree_index ASC";
List<Object> params = new ArrayList<>();
params.add(currentId);
List<MilitaryType> childList = SQLiteUtil.queryForList(
militaryPath, querySql, params, MilitaryType.class
);
// 递归查询子类型的子类型
if (childList != null && !childList.isEmpty()) {
for (MilitaryType child : childList) {
recursiveGetMilitaryTypeChildren(militaryPath, child.getId(), idList);
}
}
}
/**
* 根据军标ID获取模型数据访问URL
*/
private String getMilitaryDataUrlByModelId(String militaryId) {
try {
// 获取当前启用的军标库路径
String militaryLibraryPath = getMilitaryLibrary();
if (militaryLibraryPath == null) {
return null;
}
// 查询军标对应的类型后缀
String sql = "SELECT military_type FROM military WHERE id = ?";
List<Object> params = new ArrayList<>();
params.add(militaryId);
Military military = SQLiteUtil.queryForObject(militaryLibraryPath, sql, params, Military.class);
// 得到类型ID
String militaryType = military.getMilitaryType();
// 拼接完整URL
return "http://" + serverHost + ":" + serverPort + "/militaryLibrary/data/military/" + militaryId + "/" + militaryType;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private boolean isMilitaryTypeExist(String militaryPath, String typeId)
throws SQLException, IllegalAccessException, InstantiationException {
String checkSql = "SELECT id FROM military_type WHERE id = ?";
List<Object> params = new ArrayList<>();
params.add(typeId);
List<MilitaryType> existList = SQLiteUtil.queryForList(
militaryPath, checkSql, params, MilitaryType.class
);
return existList != null && !existList.isEmpty();
}
}