添加文件转换可配置功能

This commit is contained in:
wanxiaoli 2026-01-09 17:02:08 +08:00
parent a4a0cdbf1d
commit 26392c6047
4 changed files with 308 additions and 0 deletions

View File

@ -0,0 +1,10 @@
package com.yfd.platform.modules.experimentalData.config;
import lombok.Data;
import java.util.List;
@Data
public class InsConvertConfig {
private List<OutputConfig> outputs;
}

View File

@ -0,0 +1,13 @@
package com.yfd.platform.modules.experimentalData.config;
import lombok.Data;
import java.util.Map;
@Data
public class OutputConfig {
private String name; // 文件标识 vins/fvns
private String templateResource; // 模板路径 classpath
private String delimiter = ","; // 分隔符默认逗号
private String extension = ".txt"; // 文件扩展名默认 .txt
private Map<String, RuleConfig> rules; // 输出列规则
}

View File

@ -0,0 +1,11 @@
package com.yfd.platform.modules.experimentalData.config;
import lombok.Data;
@Data
public class RuleConfig {
private String type; // INPUT / CONST
private String from; // INPUT 类型对应 INS
private String value; // CONST 类型固定值
}

View File

@ -0,0 +1,274 @@
package com.yfd.platform.modules.experimentalData.service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yfd.platform.modules.experimentalData.config.InsConvertConfig;
import com.yfd.platform.modules.experimentalData.config.OutputConfig;
import com.yfd.platform.modules.experimentalData.config.RuleConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
/**
* INS 文件转换服务
* - JSON 配置支持 INPUT / CONST
* - 模板文件决定输出列全集
* - 支持默认配置 + 客户自定义配置
*/
@Service
@Slf4j
public class InsFileConvertNewService {
private static final Charset UTF8 = StandardCharsets.UTF_8;
/**
* 转换 INS 文件
* @param insFile INS 源文件
* @param jsonConfigFile 客户自定义 JSON 配置可为 null使用默认
* @return Map<输出文件标识, 输出文件对象>
*/
public Map<String, File> convert(File insFile, File jsonConfigFile) throws Exception {
long start = System.currentTimeMillis();
if (!insFile.exists()) {
throw new IOException("INS 文件不存在:" + insFile.getAbsolutePath());
}
// 1. 加载配置
InsConvertConfig config = loadConfig(jsonConfigFile);
// 2. 读取 INS 表头
String insDelimiter;
Map<String, Integer> insHeaderIndex;
try (BufferedReader insReader = Files.newBufferedReader(insFile.toPath(), UTF8)) {
String insHeaderLine = insReader.readLine();
if (insHeaderLine == null) {
throw new IllegalStateException("INS 文件为空");
}
//---------- 自动识别分隔符 ----------
if (insHeaderLine.contains("\t")) {
insDelimiter = "\t";
} else if (insHeaderLine.contains(",")) {
insDelimiter = ",";
} else {
throw new RuntimeException("无法识别 INS 文件的分隔符");
}
// 构建列名索引
String[] insHeaders = insHeaderLine.split(insDelimiter, -1);
Map<String, Integer> insIndex = new HashMap<>();
for (int i = 0; i < insHeaders.length; i++) {
insIndex.put(insHeaders[i].trim(), i);
}
insHeaderIndex = parseHeaderIndex(insHeaderLine,insDelimiter);
}
// 3. 校验 INS 必需字段
validateInsHeader(insHeaderIndex, config);
// 4. 初始化每个输出文件
Map<String, OutputContext> outputs = initOutputs(config.getOutputs(), insFile.getParentFile());
// 5. 流式读取 INS 正文
long rowCount = 0;
try (BufferedReader insReader = Files.newBufferedReader(insFile.toPath(), UTF8)) {
insReader.readLine(); // skip header
String line;
while ((line = insReader.readLine()) != null) {
rowCount++;
String[] insValues = line.split(insDelimiter, -1);
for (OutputContext ctx : outputs.values()) {
String[] out = new String[ctx.headers.length];
Arrays.fill(out, "0");
for (Map.Entry<String, RuleConfig> e : ctx.rules.entrySet()) {
String outCol = e.getKey();
RuleConfig rule = e.getValue();
int outIdx = ctx.headerIndex.get(outCol);
if ("INPUT".equalsIgnoreCase(rule.getType())) {
int inIdx = insHeaderIndex.get(rule.getFrom());
out[outIdx] = inIdx < insValues.length ? insValues[inIdx] : "0";
} else if ("CONST".equalsIgnoreCase(rule.getType())) {
out[outIdx] = rule.getValue() != null ? rule.getValue() : "0";
} else {
out[outIdx] = "0"; // 未来扩展 FORMULA
}
}
ctx.writer.write(String.join(ctx.delimiter, out));
ctx.writer.newLine();
}
}
}
// 6. 关闭 writer
for (OutputContext ctx : outputs.values()) {
ctx.writer.flush();
ctx.writer.close();
}
log.info("INS 转换完成,行数={}, 耗时={}ms", rowCount, System.currentTimeMillis() - start);
Map<String, File> result = new HashMap<>();
for (OutputContext ctx : outputs.values()) {
result.put(ctx.name, ctx.outFile);
}
return result;
}
// =================== 内部方法 ===================
/** 加载 JSON 配置,客户自定义优先,否则默认 */
private InsConvertConfig loadConfig(File jsonConfigFile) throws IOException {
ObjectMapper mapper = new ObjectMapper();
if (jsonConfigFile != null) {
log.info("加载客户自定义 JSON 配置:{}", jsonConfigFile.getAbsolutePath());
return mapper.readValue(jsonConfigFile, InsConvertConfig.class);
} else {
log.info("加载默认 JSON 配置config/ins-convert-default.json");
ClassPathResource res = new ClassPathResource("config/ins-convert-default.json");
try (InputStreamReader in = new InputStreamReader(res.getInputStream(), UTF8)) {
return mapper.readValue(in, InsConvertConfig.class);
}
}
}
/** 解析表头到 Map<列名,索引> */
private Map<String, Integer> parseHeaderIndex(String headerLine,String delimiter) {
String[] headers = headerLine.split(delimiter, -1);
Map<String, Integer> map = new HashMap<>();
for (int i = 0; i < headers.length; i++) {
map.put(headers[i].trim(), i);
}
return map;
}
/** 用","解析输出表头到 Map<列名,索引> */
private Map<String, Integer> parseOutHeaderIndex(String headerLine,String delimiter) {
String[] headers = headerLine.split(delimiter, -1);
Map<String, Integer> map = new HashMap<>();
for (int i = 0; i < headers.length; i++) {
map.put(headers[i].trim(), i);
}
return map;
}
/** 校验 INS 必需列 */
private void validateInsHeader(Map<String, Integer> insHeader, InsConvertConfig config) {
Set<String> required = new HashSet<>();
for (OutputConfig out : config.getOutputs()) {
for (RuleConfig rule : out.getRules().values()) {
if ("INPUT".equalsIgnoreCase(rule.getType())) {
required.add(rule.getFrom());
}
}
}
for (String col : required) {
if (!insHeader.containsKey(col)) {
throw new IllegalArgumentException("INS 缺少必需字段:" + col);
}
}
}
/** 初始化输出文件流和上下文 */
private Map<String, OutputContext> initOutputs(
List<OutputConfig> outputs, File parentDir) throws Exception {
Map<String, OutputContext> map = new HashMap<>();
for (OutputConfig out : outputs) {
// 1.读取模板文件表头
ClassPathResource tpl = new ClassPathResource(out.getTemplateResource());
String headerLine;
try (BufferedReader r = new BufferedReader(new InputStreamReader(tpl.getInputStream(), UTF8))) {
headerLine = r.readLine();
}
if (headerLine == null || headerLine.isEmpty()) {
throw new RuntimeException("模板文件为空:" + out.getTemplateResource());
}
String delimiter = out.getDelimiter() != null ? out.getDelimiter() : ",";
String[] headers = headerLine.split(delimiter, -1);
Map<String, Integer> headerIndex = parseOutHeaderIndex(headerLine,delimiter);
// ---------- 2. 输出规则列校验 ----------
for (String col : out.getRules().keySet()) {
if (!headerIndex.containsKey(col)) {
throw new IllegalStateException(
"规则列不存在于模板:" + col + " 模板文件:" + out.getTemplateResource());
}
}
// --------- 3. 构造输出文件 ---------
String ts = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
String ext = out.getExtension() != null ? out.getExtension() : ".txt";
File outFile = new File(parentDir, out.getName() + "_" + ts + ext);
BufferedWriter writer = Files.newBufferedWriter(outFile.toPath(), UTF8);
// ------- 4. 写入模板表头 ----------
writer.write(headerLine);
writer.newLine();
// ---------- 5. 初始化上下文 ----------
OutputContext ctx = new OutputContext();
ctx.name = out.getName();
ctx.headers = headers;
ctx.headerIndex = headerIndex;
ctx.rules = out.getRules();
ctx.writer = writer;
ctx.delimiter = out.getDelimiter();
ctx.outFile = outFile;
map.put(ctx.name, ctx);
}
return map;
}
/** 输出上下文 */
private static class OutputContext {
String name;
String[] headers;
Map<String, RuleConfig> rules;
Map<String, Integer> headerIndex;
BufferedWriter writer;
String delimiter;
File outFile;
}
// =================== 测试 main ===================
public static void main(String[] args) {
try {
InsFileConvertNewService service = new InsFileConvertNewService();
File insFile = new File("D:/data/ins_frameSimu_0.txt"); // 修改为实际路径
File jsonFile = new File("D:/data/ins-convert-2.json");
// File jsonFile = new File("D:/data/customer-ins-convert.json"); // 客户自定义 JSON
Map<String, File> result = service.convert(insFile,jsonFile); // 使用默认配置
result.forEach((k, f) -> System.out.println(k + " -> " + f.getAbsolutePath()));
System.out.println("转换完成!");
} catch (Exception e) {
e.printStackTrace();
}
}
}