diff --git a/java/pom.xml b/java/pom.xml
index db07c4b..4735b95 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -98,6 +98,12 @@
spring-boot-starter-quartz
+
+ com.opencsv
+ opencsv
+ 5.7.1
+
+
diff --git a/java/src/main/java/com/yfd/platform/modules/experimentalData/controller/TsFilesController.java b/java/src/main/java/com/yfd/platform/modules/experimentalData/controller/TsFilesController.java
index 7107819..0644eeb 100644
--- a/java/src/main/java/com/yfd/platform/modules/experimentalData/controller/TsFilesController.java
+++ b/java/src/main/java/com/yfd/platform/modules/experimentalData/controller/TsFilesController.java
@@ -426,12 +426,12 @@ public class TsFilesController {
}
- /**
- * 查询文件内容接口
- *
- * @param id 文件的id
- * @return 文件内容的纯文本(UTF-8 编码)
- */
+
+ /**********************************
+ * 用途说明: 查询文件内容接口
+ * 参数说明 id 文件的ID
+ * 返回值说明: com.yfd.platform.config.ResponseResult文件内容的纯文本(UTF-8 编码)
+ ***********************************/
@Log(module = "实验数据管理", value = "查询文件内容!")
@GetMapping("/api/files/content")
@ApiOperation("查询文件内容")
@@ -447,13 +447,12 @@ public class TsFilesController {
}
}
- /**
- * 保存文件内容接口
- *
- * @param id 文件的id
- * @param content 新的文件内容(HTML/文本)
- * @return 操作结果
- */
+ /**********************************
+ * 用途说明: 保存文件内容接口
+ * 参数说明 id 文件的ID
+ * 参数说明 content 新的文件内容(HTML/文本)
+ * 返回值说明: com.yfd.platform.config.ResponseResult操作结果
+ ***********************************/
@Log(module = "实验数据管理", value = "保存文件内容!")
@PostMapping("/save/files/content")
@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("文件保存成功");
+ }
+
+
}
diff --git a/java/src/main/java/com/yfd/platform/modules/experimentalData/domain/BatchModifyRequest.java b/java/src/main/java/com/yfd/platform/modules/experimentalData/domain/BatchModifyRequest.java
new file mode 100644
index 0000000..cd74727
--- /dev/null
+++ b/java/src/main/java/com/yfd/platform/modules/experimentalData/domain/BatchModifyRequest.java
@@ -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 modifications;
+}
diff --git a/java/src/main/java/com/yfd/platform/modules/experimentalData/domain/ModifyCommand.java b/java/src/main/java/com/yfd/platform/modules/experimentalData/domain/ModifyCommand.java
new file mode 100644
index 0000000..9273fc8
--- /dev/null
+++ b/java/src/main/java/com/yfd/platform/modules/experimentalData/domain/ModifyCommand.java
@@ -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;
+}
diff --git a/java/src/main/java/com/yfd/platform/modules/experimentalData/service/ITsFilesService.java b/java/src/main/java/com/yfd/platform/modules/experimentalData/service/ITsFilesService.java
index ec51c28..5c60d19 100644
--- a/java/src/main/java/com/yfd/platform/modules/experimentalData/service/ITsFilesService.java
+++ b/java/src/main/java/com/yfd/platform/modules/experimentalData/service/ITsFilesService.java
@@ -2,10 +2,7 @@ package com.yfd.platform.modules.experimentalData.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yfd.platform.config.ResponseResult;
-import com.yfd.platform.modules.experimentalData.domain.DualTreeResponse;
-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.yfd.platform.modules.experimentalData.domain.*;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.web.bind.annotation.RequestParam;
@@ -170,12 +167,18 @@ public interface ITsFilesService extends IService {
*/
String readFileContent(String id) throws IOException;
- /**
- * 保存文件内容接口
- *
- * @param id 文件的id
- * @param content 新的文件内容(HTML/文本)
- * @return 操作结果
- */
+ /**********************************
+ * 用途说明: 保存文件内容接口
+ * 参数说明 id 文件的ID
+ * 参数说明 content 新的文件内容(HTML/文本)
+ * 返回值说明: com.yfd.platform.config.ResponseResult操作结果
+ ***********************************/
void saveFileContent(String id, String content) throws IOException;
+
+ /**********************************
+ * 用途说明: 批量修改文件中多行多列的内容
+ * 参数说明 request 要修改的文件信息
+ * 返回值说明: com.yfd.platform.config.ResponseResult操作结果
+ ***********************************/
+ void batchUpdateFile(String id, List modifications) throws IOException;
}
diff --git a/java/src/main/java/com/yfd/platform/modules/experimentalData/service/impl/TsFilesServiceImpl.java b/java/src/main/java/com/yfd/platform/modules/experimentalData/service/impl/TsFilesServiceImpl.java
index 9f1e8e7..8cf2ffc 100644
--- a/java/src/main/java/com/yfd/platform/modules/experimentalData/service/impl/TsFilesServiceImpl.java
+++ b/java/src/main/java/com/yfd/platform/modules/experimentalData/service/impl/TsFilesServiceImpl.java
@@ -3,6 +3,8 @@ package com.yfd.platform.modules.experimentalData.service.impl;
import cn.hutool.core.collection.CollUtil;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
@@ -24,6 +26,9 @@ import com.amazonaws.services.s3.model.S3ObjectInputStream;
import com.amazonaws.util.IOUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
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.config.ResponseResult;
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.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
-
+import org.springframework.beans.factory.annotation.Value; // 正确
import javax.annotation.Resource;
import javax.xml.crypto.Data;
import java.io.*;
@@ -108,6 +113,8 @@ public class TsFilesServiceImpl extends ServiceImpl impl
private List compressSuffixes;
private final Set addedEntries = new HashSet<>();
+
+
/**********************************
* 用途说明: 分页查询试验数据管理-文档内容
* 参数说明
@@ -4263,6 +4270,144 @@ public class TsFilesServiceImpl extends ServiceImpl impl
}
}
+ /**********************************
+ * 用途说明: 批量修改文件中多行多列的内容
+ * 参数说明 request 要修改的文件信息
+ * 返回值说明: com.yfd.platform.config.ResponseResult操作结果
+ ***********************************/
+ @Override
+ public void batchUpdateFile(String id, List modifications)throws IOException {
+ StorageSourceConfig config = storageSourceConfigMapper.selectOne(new QueryWrapper().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 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 lines, List 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);
+ }
+ }
+
}