提交代码0326

This commit is contained in:
lilin 2025-03-26 18:33:02 +08:00
parent 64a3231f01
commit fc32e0fecd
6 changed files with 230 additions and 25 deletions

View File

@ -98,6 +98,12 @@
<artifactId>spring-boot-starter-quartz</artifactId> <artifactId>spring-boot-starter-quartz</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.opencsv</groupId>
<artifactId>opencsv</artifactId>
<version>5.7.1</version>
</dependency>
<!-- spring-elasticsearch搜素--> <!-- spring-elasticsearch搜素-->
<!-- <dependency>--> <!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>--> <!-- <groupId>org.springframework.boot</groupId>-->

View File

@ -426,12 +426,12 @@ public class TsFilesController {
} }
/**
* 查询文件内容接口 /**********************************
* * 用途说明: 查询文件内容接口
* @param id 文件的id * 参数说明 id 文件的ID
* @return 文件内容的纯文本UTF-8 编码 * 返回值说明: com.yfd.platform.config.ResponseResult文件内容的纯文本UTF-8 编码
*/ ***********************************/
@Log(module = "实验数据管理", value = "查询文件内容!") @Log(module = "实验数据管理", value = "查询文件内容!")
@GetMapping("/api/files/content") @GetMapping("/api/files/content")
@ApiOperation("查询文件内容") @ApiOperation("查询文件内容")
@ -447,13 +447,12 @@ public class TsFilesController {
} }
} }
/** /**********************************
* 保存文件内容接口 * 用途说明: 保存文件内容接口
* * 参数说明 id 文件的ID
* @param id 文件的id * 参数说明 content 新的文件内容HTML/文本
* @param content 新的文件内容HTML/文本 * 返回值说明: com.yfd.platform.config.ResponseResult操作结果
* @return 操作结果 ***********************************/
*/
@Log(module = "实验数据管理", value = "保存文件内容!") @Log(module = "实验数据管理", value = "保存文件内容!")
@PostMapping("/save/files/content") @PostMapping("/save/files/content")
@ApiOperation("保存文件内容") @ApiOperation("保存文件内容")
@ -470,5 +469,19 @@ public class TsFilesController {
} }
/**********************************
* 用途说明: 批量修改文件中多行多列的内容
* 参数说明 request 要修改的文件信息
* 返回值说明: com.yfd.platform.config.ResponseResult操作结果
***********************************/
@Log(module = "实验数据管理", value = "批量修改文件中多行多列的内容!")
@PostMapping("/batchModify")
@ApiOperation("批量修改文件中多行多列的内容")
public ResponseResult batchModifyFile(@RequestBody BatchModifyRequest request) throws IOException {
tsFilesService.batchUpdateFile(request.getId(), request.getModifications());
return ResponseResult.success("文件保存成功");
}
} }

View File

@ -0,0 +1,18 @@
package com.yfd.platform.modules.experimentalData.domain;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import java.util.List;
// 批量修改请求对象
@Data
public class BatchModifyRequest {
@NotBlank
private String id; // 文件ID
@NotEmpty
private List<ModifyCommand> modifications;
}

View File

@ -0,0 +1,20 @@
package com.yfd.platform.modules.experimentalData.domain;
import lombok.Data;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
// 单条修改指令
@Data
public class ModifyCommand {
@Min(1)
private int lineNum; // 行号从1开始
@Min(1)
private int colNum; // 列号从1开始
@NotBlank
private String newValue;
}

View File

@ -2,10 +2,7 @@ package com.yfd.platform.modules.experimentalData.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yfd.platform.config.ResponseResult; import com.yfd.platform.config.ResponseResult;
import com.yfd.platform.modules.experimentalData.domain.DualTreeResponse; import com.yfd.platform.modules.experimentalData.domain.*;
import com.yfd.platform.modules.experimentalData.domain.MoveCopyFileFolderRequest;
import com.yfd.platform.modules.experimentalData.domain.Parameter;
import com.yfd.platform.modules.experimentalData.domain.TsFiles;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
@ -170,12 +167,18 @@ public interface ITsFilesService extends IService<TsFiles> {
*/ */
String readFileContent(String id) throws IOException; String readFileContent(String id) throws IOException;
/** /**********************************
* 保存文件内容接口 * 用途说明: 保存文件内容接口
* * 参数说明 id 文件的ID
* @param id 文件的id * 参数说明 content 新的文件内容HTML/文本
* @param content 新的文件内容HTML/文本 * 返回值说明: com.yfd.platform.config.ResponseResult操作结果
* @return 操作结果 ***********************************/
*/
void saveFileContent(String id, String content) throws IOException; void saveFileContent(String id, String content) throws IOException;
/**********************************
* 用途说明: 批量修改文件中多行多列的内容
* 参数说明 request 要修改的文件信息
* 返回值说明: com.yfd.platform.config.ResponseResult操作结果
***********************************/
void batchUpdateFile(String id, List<ModifyCommand> modifications) throws IOException;
} }

View File

@ -3,6 +3,8 @@ package com.yfd.platform.modules.experimentalData.service.impl;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.*; import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
@ -24,6 +26,9 @@ import com.amazonaws.services.s3.model.S3ObjectInputStream;
import com.amazonaws.util.IOUtils; import com.amazonaws.util.IOUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.opencsv.CSVReader;
import com.opencsv.CSVWriter;
import com.opencsv.exceptions.CsvValidationException;
import com.yfd.platform.component.ServerSendEventServer; import com.yfd.platform.component.ServerSendEventServer;
import com.yfd.platform.config.ResponseResult; import com.yfd.platform.config.ResponseResult;
import com.yfd.platform.modules.experimentalData.domain.*; import com.yfd.platform.modules.experimentalData.domain.*;
@ -62,7 +67,7 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.factory.annotation.Value; // 正确
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.xml.crypto.Data; import javax.xml.crypto.Data;
import java.io.*; import java.io.*;
@ -108,6 +113,8 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
private List<String> compressSuffixes; private List<String> compressSuffixes;
private final Set<String> addedEntries = new HashSet<>(); private final Set<String> addedEntries = new HashSet<>();
/********************************** /**********************************
* 用途说明: 分页查询试验数据管理-文档内容 * 用途说明: 分页查询试验数据管理-文档内容
* 参数说明 * 参数说明
@ -4263,6 +4270,144 @@ public class TsFilesServiceImpl extends ServiceImpl<TsFilesMapper, TsFiles> impl
} }
} }
/**********************************
* 用途说明: 批量修改文件中多行多列的内容
* 参数说明 request 要修改的文件信息
* 返回值说明: com.yfd.platform.config.ResponseResult操作结果
***********************************/
@Override
public void batchUpdateFile(String id, List<ModifyCommand> modifications)throws IOException {
StorageSourceConfig config = storageSourceConfigMapper.selectOne(new QueryWrapper<StorageSourceConfig>().eq("name", "filePath"));
TsFiles tsFile = tsFilesMapper.selectById(id);
if (tsFile == null) {
throw new IllegalArgumentException("文件ID不存在: " + id);
}
Path filePath = Paths.get(config.getValue(), tsFile.getWorkPath(), tsFile.getFileName()).normalize();
// 安全校验
if (!filePath.startsWith(config.getValue())) {
throw new SecurityException("路径越界: " + filePath);
}
// 1.2 检查文件是否为文本类型
validateTextFile(filePath);
LOGGER.info("批量修改文件: {}, 修改指令数: {}", filePath, modifications.size());
// 3. 创建临时文件在指定目录下
Path tempDir = Paths.get(config.getValue(), tsFile.getWorkPath()).normalize();
if (!Files.exists(tempDir)) {
Files.createDirectories(tempDir); // 确保目录存在
}
// 临时文件名格式原文件名_时间戳.tmp
String tempFileName = tsFile.getFileName() + "_" + System.currentTimeMillis() + ".tmp";
Path tempPath = tempDir.resolve(tempFileName);
try {
// 读取文件内容兼容Java 8
List<String> lines = new ArrayList<>();
try (BufferedReader reader = Files.newBufferedReader(filePath, StandardCharsets.UTF_8)) {
String line;
while ((line = reader.readLine()) != null) {
lines.add(line);
}
}
applyModifications(lines, modifications);
// 写入临时文件兼容Java 8
try (BufferedWriter writer = Files.newBufferedWriter(tempPath, StandardCharsets.UTF_8)) {
for (String line : lines) {
writer.write(line);
writer.newLine(); // 保留原换行符风格
}
}
Files.move(tempPath, filePath, StandardCopyOption.REPLACE_EXISTING);
} catch (Exception e) {
Files.deleteIfExists(tempPath);
throw e;
}
}
/**
* 应用所有修改到文件内容
* @param lines 文件每一行的内容
* @param modifications 修改指令集合
*/
private void applyModifications(List<String> lines, List<ModifyCommand> modifications) {
modifications.forEach(cmd -> {
if (cmd.getLineNum() > lines.size()) {
throw new IllegalArgumentException("行号超出范围: " + cmd.getLineNum());
}
String originalLine = lines.get(cmd.getLineNum() - 1);
String modifiedLine = modifyLine(originalLine, cmd.getColNum(), cmd.getNewValue());
lines.set(cmd.getLineNum() - 1, modifiedLine);
});
}
private void validateTextFile(Path filePath) throws IOException {
String mimeType = Files.probeContentType(filePath);
if (mimeType == null || !mimeType.startsWith("text/")) {
throw new IOException("只允许修改文本文件,检测到类型: " + mimeType);
}
}
private String modifyTabDelimitedLine(String line, int colNum, String newValue) {
String[] columns = line.split("\t", -1); // -1保留空列
if (colNum < 1 || colNum > columns.length) {
throw new IllegalArgumentException("列号越界: " + colNum);
}
columns[colNum - 1] = newValue;
return String.join("\t", columns);
}
private String modifyCsvLine(String line, int colNum, String newValue) {
try (CSVReader reader = new CSVReader(new StringReader(line))) {
String[] columns = reader.readNext();
if (colNum < 1 || colNum > columns.length) {
throw new IllegalArgumentException("列号越界: " + colNum);
}
columns[colNum - 1] = newValue;
StringWriter sw = new StringWriter();
try (CSVWriter writer = new CSVWriter(sw)) {
writer.writeNext(columns);
return sw.toString().trim(); // 去除末尾换行
}
} catch (IOException | CsvValidationException e) {
throw new UncheckedIOException("CSV解析失败", (IOException) e);
}
}
private String modifySpaceDelimitedLine(String line, int colNum, String newValue) {
// 处理连续多个空格保留原始对齐
String[] columns = line.split("\\s+", -1);
if (colNum < 1 || colNum > columns.length) {
throw new IllegalArgumentException("列号越界: " + colNum);
}
columns[colNum - 1] = newValue;
// 重建原始空格对齐假设原文件用固定空格数分隔
return String.join(" ", columns); // 用4个空格分隔
}
private String modifyLine(String line, int colNum, String newValue) {
// 自动检测分隔符类型
if (line.contains("\t")) {
return modifyTabDelimitedLine(line, colNum, newValue);
} else if (line.contains(",")) {
return modifyCsvLine(line, colNum, newValue);
} else {
return modifySpaceDelimitedLine(line, colNum, newValue);
}
}
} }