feat(core): 支持表格数据 Upsert 模式写入
- 在 MapTableConfig 和 MapSheetConfig 中新增 upsert 配置项,控制是否启用 Upsert 模式 - MapWriteBuilder 新增 upsert 方法用于设置 Upsert 模式开关- 实现根据 upsert 配置决定是否读取现有数据进行匹配更新或直接追加写入 - 添加分组字段支持,允许按指定字段对数据进行分组处理 -优化数据写入逻辑,区分 Upsert 模式和纯追加模式的数据读取与行号计算 - 增强异常处理和日志记录,提升系统稳定性与可维护性
This commit is contained in:
parent
3b0b8712a8
commit
7ad1060adf
@ -65,4 +65,15 @@ public @interface TableConf {
|
||||
* @return 背景颜色
|
||||
*/
|
||||
String headBackColor() default "#cccccc";
|
||||
|
||||
|
||||
/**
|
||||
* 是否启用 Upsert 模式
|
||||
*
|
||||
* true(默认):根据唯一键匹配,存在则更新,不存在则追加
|
||||
* false:不匹配唯一键,所有数据直接追加到表格末尾
|
||||
*
|
||||
* @return 是否启用 Upsert 模式
|
||||
*/
|
||||
boolean upsert() default true;
|
||||
}
|
||||
@ -482,6 +482,12 @@ public class MapSheetBuilder {
|
||||
public String headBackColor() {
|
||||
return config.getHeadBackColor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean upsert() {
|
||||
// MapSheetConfig 继承自 MapTableConfig,支持 upsert 配置
|
||||
return config.isUpsert();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -522,4 +528,3 @@ public class MapSheetBuilder {
|
||||
return customProps;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package cn.isliu.core.builder;
|
||||
|
||||
import cn.isliu.core.*;
|
||||
import cn.isliu.core.annotation.TableConf;
|
||||
import cn.isliu.core.client.FeishuClient;
|
||||
import cn.isliu.core.client.FsClient;
|
||||
import cn.isliu.core.config.MapTableConfig;
|
||||
@ -9,6 +10,7 @@ import cn.isliu.core.enums.FileType;
|
||||
import cn.isliu.core.logging.FsLogger;
|
||||
import cn.isliu.core.service.CustomValueService;
|
||||
import cn.isliu.core.utils.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@ -31,6 +33,7 @@ public class MapWriteBuilder {
|
||||
private final String spreadsheetToken;
|
||||
private final List<Map<String, Object>> dataList;
|
||||
private MapTableConfig config;
|
||||
private String groupField;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
@ -123,6 +126,34 @@ public class MapWriteBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否启用 Upsert 模式
|
||||
*
|
||||
* true(默认):根据唯一键匹配,存在则更新,不存在则追加
|
||||
* false:不匹配唯一键,所有数据直接追加到表格末尾
|
||||
*
|
||||
* @param upsert true 为 Upsert 模式,false 为纯追加模式
|
||||
* @return MapWriteBuilder实例
|
||||
*/
|
||||
public MapWriteBuilder upsert(boolean upsert) {
|
||||
this.config.setUpsert(upsert);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置分组字段
|
||||
*
|
||||
* 配置分组字段,用于处理数据行分组。
|
||||
* 当数据行存在分组字段时,将按照分组字段进行分组,并分别处理每个分组。
|
||||
*
|
||||
* @param groupField 分组字段名称
|
||||
* @return MapWriteBuilder实例
|
||||
*/
|
||||
public MapWriteBuilder groupField(String groupField) {
|
||||
this.groupField = groupField;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行数据写入
|
||||
*
|
||||
@ -141,11 +172,47 @@ public class MapWriteBuilder {
|
||||
Map<String, String> titlePostionMap = readFieldsPositionMap(sheet, client);
|
||||
config.setFieldsPositionMap(titlePostionMap);
|
||||
|
||||
// 读取现有数据用于匹配和更新
|
||||
Map<String, Integer> currTableRowMap = readExistingData(sheet, client, titlePostionMap);
|
||||
// 根据 upsert 配置决定是否需要读取现有数据用于匹配
|
||||
Map<String, Integer> currTableRowMap = new HashMap<>();
|
||||
int nextAvailableRow = config.getHeadLine();
|
||||
int headLine = config.getHeadLine();
|
||||
int titleRow = config.getTitleRow();
|
||||
List<FsTableData> fsTableDataList;
|
||||
|
||||
// 计算下一个可用行号
|
||||
int nextAvailableRow = calculateNextAvailableRow(currTableRowMap, config.getHeadLine());
|
||||
if (config.isUpsert()) {
|
||||
// Upsert 模式:读取现有数据用于匹配和更新
|
||||
fsTableDataList = readExistingData(sheet, client, groupField);
|
||||
|
||||
if (!fsTableDataList.isEmpty()) {
|
||||
Map<String, String> fieldsPositionMap = fsTableDataList.get(0).getFieldsPositionMap();
|
||||
if (fieldsPositionMap != null) {
|
||||
titlePostionMap = fieldsPositionMap;
|
||||
}
|
||||
}
|
||||
|
||||
currTableRowMap = getCurrTableRowMap(fsTableDataList, titleRow, titlePostionMap, headLine);
|
||||
|
||||
nextAvailableRow = calculateNextAvailableRow(currTableRowMap, config.getHeadLine());
|
||||
} else {
|
||||
// 纯追加模式:只需要读取现有数据获取最大行号
|
||||
fsTableDataList = readMaxRowNumber(sheet, client, groupField);
|
||||
|
||||
if (!fsTableDataList.isEmpty()) {
|
||||
Map<String, String> fieldsPositionMap = fsTableDataList.get(0).getFieldsPositionMap();
|
||||
if (fieldsPositionMap != null) {
|
||||
titlePostionMap = fieldsPositionMap;
|
||||
}
|
||||
}
|
||||
|
||||
// 找到数据行中的最大行号
|
||||
int maxRow = fsTableDataList.stream()
|
||||
.filter(fsTableData -> fsTableData.getRow() >= headLine)
|
||||
.mapToInt(FsTableData::getRow)
|
||||
.max()
|
||||
.orElse(headLine - 1);
|
||||
|
||||
nextAvailableRow = maxRow + 1;
|
||||
}
|
||||
|
||||
// 初始化批量插入对象
|
||||
CustomValueService.ValueRequest.BatchPutValuesBuilder resultValuesBuilder =
|
||||
@ -154,7 +221,8 @@ public class MapWriteBuilder {
|
||||
List<FileData> fileDataList = new ArrayList<>();
|
||||
AtomicInteger rowCount = new AtomicInteger(nextAvailableRow);
|
||||
|
||||
// 处理每条数据
|
||||
if (config.isUpsert()) {
|
||||
// Upsert 模式:计算 uniqueId 并匹配更新或追加
|
||||
for (Map<String, Object> data : dataList) {
|
||||
String uniqueId = MapDataUtil.calculateUniqueId(data, config);
|
||||
|
||||
@ -172,6 +240,14 @@ public class MapWriteBuilder {
|
||||
fileDataList, config.isEnableCover());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 纯追加模式:不计算 uniqueId,所有数据直接追加到表格末尾
|
||||
for (Map<String, Object> data : dataList) {
|
||||
int newRow = rowCount.incrementAndGet();
|
||||
processDataRow(data, titlePostionMap, newRow, resultValuesBuilder,
|
||||
fileDataList, config.isEnableCover());
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否需要扩展行数
|
||||
ensureSufficientRows(sheet, rowCount.get(), client);
|
||||
@ -183,6 +259,44 @@ public class MapWriteBuilder {
|
||||
return batchWriteValues(resultValuesBuilder, client);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Map<String, Integer> getCurrTableRowMap(List<FsTableData> fsTableDataList, int titleRow,
|
||||
Map<String, String> titlePostionMap, int headLine) {
|
||||
Map<String, Integer> currTableRowMap;
|
||||
// 获取标题映射
|
||||
Map<String, String> titleMap = new HashMap<>();
|
||||
fsTableDataList.stream()
|
||||
.filter(d -> d.getRow() == (titleRow - 1))
|
||||
.findFirst()
|
||||
.ifPresent(d -> {
|
||||
Map<String, String> map = (Map<String, String>) d.getData();
|
||||
titleMap.putAll(map);
|
||||
});
|
||||
|
||||
// 转换为带字段名的数据,并计算唯一ID
|
||||
currTableRowMap = fsTableDataList.stream()
|
||||
.filter(fsTableData -> fsTableData.getRow() >= headLine)
|
||||
.map(item -> {
|
||||
Map<String, Object> resultMap = new HashMap<>();
|
||||
Map<String, Object> map = (Map<String, Object>) item.getData();
|
||||
|
||||
map.forEach((k, v) -> {
|
||||
String title = titleMap.get(k);
|
||||
if (title != null) {
|
||||
resultMap.put(title, v);
|
||||
}
|
||||
});
|
||||
|
||||
String uniqueId = MapDataUtil.calculateUniqueId(resultMap, config);
|
||||
item.setUniqueId(uniqueId);
|
||||
item.setFieldsPositionMap(titlePostionMap);
|
||||
return item;
|
||||
})
|
||||
.filter(item -> item.getUniqueId() != null)
|
||||
.collect(Collectors.toMap(FsTableData::getUniqueId, FsTableData::getRow, (v1, v2) -> v1));
|
||||
return currTableRowMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取字段位置映射
|
||||
*/
|
||||
@ -221,10 +335,10 @@ public class MapWriteBuilder {
|
||||
|
||||
/**
|
||||
* 读取现有数据
|
||||
*
|
||||
* @param groupField 分组字段名称,如果为null则读取全部数据
|
||||
*/
|
||||
private Map<String, Integer> readExistingData(Sheet sheet, FeishuClient client, Map<String, String> titlePostionMap) {
|
||||
int headLine = config.getHeadLine();
|
||||
int titleRow = config.getTitleRow();
|
||||
private List<FsTableData> readExistingData(Sheet sheet, FeishuClient client, String groupField) {
|
||||
int totalRow = sheet.getGridProperties().getRowCount();
|
||||
int colCount = sheet.getGridProperties().getColumnCount();
|
||||
int startOffset = 1;
|
||||
@ -257,39 +371,23 @@ public class MapWriteBuilder {
|
||||
|
||||
// 处理表格数据
|
||||
TableData tableData = processSheetData(sheet, values);
|
||||
List<FsTableData> dataList = getFsTableData(tableData, new ArrayList<>());
|
||||
|
||||
// 获取标题映射
|
||||
Map<String, String> titleMap = new HashMap<>();
|
||||
dataList.stream()
|
||||
.filter(d -> d.getRow() == (titleRow - 1))
|
||||
.findFirst()
|
||||
.ifPresent(d -> {
|
||||
Map<String, String> map = (Map<String, String>) d.getData();
|
||||
titleMap.putAll(map);
|
||||
});
|
||||
|
||||
// 转换为带字段名的数据,并计算唯一ID
|
||||
return dataList.stream()
|
||||
.filter(fsTableData -> fsTableData.getRow() >= headLine)
|
||||
.map(item -> {
|
||||
Map<String, Object> resultMap = new HashMap<>();
|
||||
Map<String, Object> map = (Map<String, Object>) item.getData();
|
||||
|
||||
map.forEach((k, v) -> {
|
||||
String title = titleMap.get(k);
|
||||
if (title != null) {
|
||||
resultMap.put(title, v);
|
||||
// 根据是否有分组字段,选择不同的处理方式
|
||||
List<FsTableData> dataList;
|
||||
if (groupField == null || groupField.isEmpty()) {
|
||||
// 无分组:读取全部数据
|
||||
dataList = getFsTableData(tableData, new ArrayList<>());
|
||||
} else {
|
||||
// 有分组:需要重新调用完整的分组读取方法
|
||||
// 创建临时的 TableConf 用于分组读取
|
||||
TableConf tempTableConf = createTempTableConf();
|
||||
Map<String, List<FsTableData>> groupDataMap = FsTableUtil.getGroupFsTableData(
|
||||
sheet, spreadsheetToken, tempTableConf, new ArrayList<>(), new HashMap<>()
|
||||
);
|
||||
dataList = groupDataMap.getOrDefault(groupField, new ArrayList<>());
|
||||
}
|
||||
});
|
||||
|
||||
String uniqueId = MapDataUtil.calculateUniqueId(resultMap, config);
|
||||
item.setUniqueId(uniqueId);
|
||||
item.setFieldsPositionMap(titlePostionMap);
|
||||
return item;
|
||||
})
|
||||
.filter(item -> item.getUniqueId() != null)
|
||||
.collect(Collectors.toMap(FsTableData::getUniqueId, FsTableData::getRow, (v1, v2) -> v1));
|
||||
return dataList;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -306,6 +404,122 @@ public class MapWriteBuilder {
|
||||
.orElse(headLine);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取表格最大行号(用于纯追加模式)
|
||||
*
|
||||
* 只读取数据获取最大行号,不计算 uniqueId 和构建映射表
|
||||
*
|
||||
* @param groupField 分组字段名称,如果为null则读取全部数据
|
||||
*/
|
||||
private List<FsTableData> readMaxRowNumber(Sheet sheet, FeishuClient client, String groupField) {
|
||||
int totalRow = sheet.getGridProperties().getRowCount();
|
||||
int colCount = sheet.getGridProperties().getColumnCount();
|
||||
int startOffset = 1;
|
||||
|
||||
// 批量读取数据
|
||||
int rowCountPerBatch = Math.min(totalRow, 100);
|
||||
int actualRows = Math.max(0, totalRow - startOffset);
|
||||
int batchCount = (actualRows + rowCountPerBatch - 1) / rowCountPerBatch;
|
||||
|
||||
List<List<Object>> values = new LinkedList<>();
|
||||
for (int i = 0; i < batchCount; i++) {
|
||||
int startRowIndex = startOffset + i * rowCountPerBatch;
|
||||
int endRowIndex = Math.min(startRowIndex + rowCountPerBatch - 1, totalRow - 1);
|
||||
|
||||
ValuesBatch valuesBatch = FsApiUtil.getSheetData(
|
||||
sheet.getSheetId(), spreadsheetToken,
|
||||
"A" + startRowIndex,
|
||||
getColumnName(colCount - 1) + endRowIndex,
|
||||
client
|
||||
);
|
||||
|
||||
if (valuesBatch != null && valuesBatch.getValueRanges() != null) {
|
||||
for (ValueRange valueRange : valuesBatch.getValueRanges()) {
|
||||
if (valueRange.getValues() != null) {
|
||||
values.addAll(valueRange.getValues());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理表格数据
|
||||
TableData tableData = processSheetData(sheet, values);
|
||||
|
||||
// 根据是否有分组字段,选择不同的处理方式
|
||||
List<FsTableData> dataList;
|
||||
if (groupField == null || groupField.isEmpty()) {
|
||||
// 无分组:读取全部数据
|
||||
dataList = getFsTableData(tableData, new ArrayList<>());
|
||||
} else {
|
||||
// 有分组:需要重新调用完整的分组读取方法
|
||||
// 创建临时的 TableConf 用于分组读取
|
||||
TableConf tempTableConf = createTempTableConf();
|
||||
Map<String, List<FsTableData>> groupDataMap = FsTableUtil.getGroupFsTableData(
|
||||
sheet, spreadsheetToken, tempTableConf, new ArrayList<>(), new HashMap<>()
|
||||
);
|
||||
dataList = groupDataMap.getOrDefault(groupField, new ArrayList<>());
|
||||
}
|
||||
|
||||
return dataList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建临时的 TableConf 对象(用于分组读取)
|
||||
*/
|
||||
private TableConf createTempTableConf() {
|
||||
return new TableConf() {
|
||||
@Override
|
||||
public Class<? extends java.lang.annotation.Annotation> annotationType() {
|
||||
return TableConf.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] uniKeys() {
|
||||
return config.getUniKeyNames().toArray(new String[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int headLine() {
|
||||
return config.getHeadLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int titleRow() {
|
||||
return config.getTitleRow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enableCover() {
|
||||
return config.isEnableCover();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isText() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enableDesc() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String headFontColor() {
|
||||
return "#ffffff";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String headBackColor() {
|
||||
return "#000000";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean upsert() {
|
||||
return config.isUpsert();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理单行数据
|
||||
*/
|
||||
@ -332,7 +546,7 @@ public class MapWriteBuilder {
|
||||
}
|
||||
|
||||
// 添加到批量写入
|
||||
if (enableCover || fieldValue != null) {
|
||||
if (enableCover || (fieldValue != null && !(fieldValue instanceof FileData))) {
|
||||
resultValuesBuilder.addRange(sheetId, position + rowNum, position + rowNum)
|
||||
.addRow(GenerateUtil.getRowData(fieldValue));
|
||||
}
|
||||
|
||||
@ -32,6 +32,7 @@ public class WriteBuilder<T> {
|
||||
private Class<?> clazz;
|
||||
private boolean ignoreNotFound;
|
||||
private String groupField;
|
||||
private Boolean upsert;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
@ -106,6 +107,22 @@ public class WriteBuilder<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否启用 Upsert 模式
|
||||
*
|
||||
* 此方法设置的值会覆盖 @TableConf 注解中的配置。
|
||||
*
|
||||
* true(默认):根据唯一键匹配,存在则更新,不存在则追加
|
||||
* false:不匹配唯一键,所有数据直接追加到表格末尾
|
||||
*
|
||||
* @param upsert true 为 Upsert 模式,false 为纯追加模式
|
||||
* @return WriteBuilder实例,支持链式调用
|
||||
*/
|
||||
public WriteBuilder<T> upsert(boolean upsert) {
|
||||
this.upsert = upsert;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行数据写入并返回操作结果
|
||||
*
|
||||
@ -133,6 +150,10 @@ public class WriteBuilder<T> {
|
||||
Sheet sheet = FsApiUtil.getSheetMetadata(sheetId, client, spreadsheetToken);
|
||||
|
||||
TableConf tableConf = aClass != null ? PropertyUtil.getTableConf(aClass) : PropertyUtil.getTableConf(sourceClass);
|
||||
|
||||
// 确定最终的 upsert 值:Builder 方法参数优先,否则使用注解配置
|
||||
boolean finalUpsert = (this.upsert != null) ? this.upsert : tableConf.upsert();
|
||||
|
||||
Map<String, String> titlePostionMap = FsTableUtil.getTitlePostionMap(sheet, spreadsheetToken, tableConf);
|
||||
Set<String> keys = titlePostionMap.keySet();
|
||||
|
||||
@ -161,12 +182,18 @@ public class WriteBuilder<T> {
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Integer> currTableRowMap = fsTableDataList.stream()
|
||||
// 根据 finalUpsert 决定是否构建映射表
|
||||
Map<String, Integer> currTableRowMap = new HashMap<>();
|
||||
if (finalUpsert) {
|
||||
// Upsert 模式:构建 uniqueId 到行号的映射表
|
||||
currTableRowMap = fsTableDataList.stream()
|
||||
.filter(fsTableData -> fsTableData.getRow() >= tableConf.headLine())
|
||||
.collect(Collectors.toMap(
|
||||
FsTableData::getUniqueId,
|
||||
FsTableData::getRow,
|
||||
(existing, replacement) -> existing));
|
||||
(existing, replacement) -> existing
|
||||
));
|
||||
}
|
||||
|
||||
final Integer[] row = {tableConf.headLine()};
|
||||
fsTableDataList.forEach(fsTableData -> {
|
||||
@ -182,6 +209,8 @@ public class WriteBuilder<T> {
|
||||
|
||||
AtomicInteger rowCount = new AtomicInteger(row[0]);
|
||||
|
||||
if (finalUpsert) {
|
||||
// Upsert 模式:计算 uniqueId 并匹配更新或追加
|
||||
for (T data : dataList) {
|
||||
Map<String, Object> values = GenerateUtil.getFieldValue(data, fieldMap);
|
||||
|
||||
@ -196,15 +225,12 @@ public class WriteBuilder<T> {
|
||||
|
||||
AtomicReference<Integer> rowNum = new AtomicReference<>(currTableRowMap.get(uniqueId));
|
||||
if (uniqueId != null && rowNum.get() != null) {
|
||||
// 找到匹配的行 → 更新
|
||||
rowNum.set(rowNum.get() + 1);
|
||||
Map<String, String> finalTitlePostionMap = titlePostionMap;
|
||||
values.forEach((field, fieldValue) -> {
|
||||
String position = finalTitlePostionMap.get(field);
|
||||
|
||||
if (position == null || position.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fieldValue instanceof FileData) {
|
||||
FileData fileData = (FileData) fieldValue;
|
||||
String fileType = fileData.getFileType();
|
||||
@ -221,15 +247,38 @@ public class WriteBuilder<T> {
|
||||
}
|
||||
});
|
||||
} else if (!ignoreNotFound) {
|
||||
// 未找到 && ignoreNotFound = false → 追加
|
||||
int rowCou = rowCount.incrementAndGet();
|
||||
Map<String, String> finalTitlePostionMap1 = titlePostionMap;
|
||||
values.forEach((field, fieldValue) -> {
|
||||
String position = finalTitlePostionMap1.get(field);
|
||||
|
||||
if (position == null || position.isEmpty()) {
|
||||
return;
|
||||
String position = finalTitlePostionMap1.get(field);
|
||||
if (fieldValue instanceof FileData) {
|
||||
FileData fileData = (FileData) fieldValue;
|
||||
fileData.setSheetId(sheetId);
|
||||
fileData.setSpreadsheetToken(spreadsheetToken);
|
||||
fileData.setPosition(position + rowCou);
|
||||
fileDataList.add(fileData);
|
||||
}
|
||||
|
||||
if (tableConf.enableCover() || fieldValue != null) {
|
||||
resultValuesBuilder.addRange(sheetId, position + rowCou, position + rowCou)
|
||||
.addRow(GenerateUtil.getRowData(fieldValue));
|
||||
}
|
||||
});
|
||||
}
|
||||
// else: 未找到 && ignoreNotFound = true → 跳过该数据
|
||||
}
|
||||
} else {
|
||||
// 纯追加模式:不计算 uniqueId,所有数据直接追加到表格末尾
|
||||
for (T data : dataList) {
|
||||
Map<String, Object> values = GenerateUtil.getFieldValue(data, fieldMap);
|
||||
|
||||
int rowCou = rowCount.incrementAndGet();
|
||||
Map<String, String> finalTitlePostionMap = titlePostionMap;
|
||||
values.forEach((field, fieldValue) -> {
|
||||
String position = finalTitlePostionMap.get(field);
|
||||
|
||||
if (fieldValue instanceof FileData) {
|
||||
FileData fileData = (FileData) fieldValue;
|
||||
fileData.setSheetId(sheetId);
|
||||
|
||||
@ -251,6 +251,22 @@ public class MapSheetConfig extends MapTableConfig {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置是否启用 Upsert 模式
|
||||
*
|
||||
* true(默认):根据唯一键匹配,存在则更新,不存在则追加
|
||||
* false:不匹配唯一键,所有数据直接追加到表格末尾
|
||||
*
|
||||
* @param upsert true 为 Upsert 模式,false 为纯追加模式
|
||||
* @return SheetBuilder实例
|
||||
*/
|
||||
public SheetBuilder upsert(boolean upsert) {
|
||||
config.setUpsert(upsert);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置字段定义列表
|
||||
*
|
||||
|
||||
@ -40,6 +40,13 @@ public class MapTableConfig {
|
||||
*/
|
||||
private boolean ignoreNotFound = false;
|
||||
|
||||
/**
|
||||
* 是否启用 Upsert 模式
|
||||
* true(默认):根据唯一键匹配,存在则更新,不存在则追加
|
||||
* false:不匹配唯一键,所有数据直接追加到表格末尾
|
||||
*/
|
||||
private boolean upsert = true;
|
||||
|
||||
/**
|
||||
* 字段位置映射 (字段名 -> 列位置,如 "添加SPU" -> "A")
|
||||
*/
|
||||
@ -156,6 +163,30 @@ public class MapTableConfig {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 是否启用 Upsert 模式
|
||||
*
|
||||
* @return true 为 Upsert 模式,false 为纯追加模式
|
||||
*/
|
||||
public boolean isUpsert() {
|
||||
return upsert;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否启用 Upsert 模式
|
||||
*
|
||||
* true(默认):根据唯一键匹配,存在则更新,不存在则追加
|
||||
* false:不匹配唯一键,所有数据直接追加到表格末尾
|
||||
*
|
||||
* @param upsert true 为 Upsert 模式,false 为纯追加模式
|
||||
* @return MapTableConfig实例,支持链式调用
|
||||
*/
|
||||
public MapTableConfig setUpsert(boolean upsert) {
|
||||
this.upsert = upsert;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字段位置映射
|
||||
*
|
||||
@ -266,6 +297,20 @@ public class MapTableConfig {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否启用 Upsert 模式
|
||||
*
|
||||
* true(默认):根据唯一键匹配,存在则更新,不存在则追加
|
||||
* false:不匹配唯一键,所有数据直接追加到表格末尾
|
||||
*
|
||||
* @param upsert true 为 Upsert 模式,false 为纯追加模式
|
||||
* @return Builder实例
|
||||
*/
|
||||
public Builder upsert(boolean upsert) {
|
||||
config.upsert = upsert;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置字段位置映射
|
||||
*
|
||||
|
||||
@ -484,6 +484,12 @@ public class PropertyUtil {
|
||||
public String headBackColor() {
|
||||
return "#cccccc";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean upsert() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
return tableConf;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user