feat(core): 新增Map数据写入飞书表格功能- 新增MapWriteBuilder类,支持链式调用写入Map格式数据
- 新增MapTableConfig配置类,用于替代注解配置 - 新增MapDataUtil工具类,提供唯一ID计算和字段映射功能- 在FsHelper中新增writeMap和writeMapBuilder方法 - 在WriteBuilder中增加空字段位置检查逻辑 - 完善字段位置映射处理,避免空指针异常
This commit is contained in:
		
							parent
							
								
									9500a1df12
								
							
						
					
					
						commit
						f9cff00b2f
					
				@ -5,11 +5,13 @@ import cn.isliu.core.FileData;
 | 
			
		||||
import cn.isliu.core.FsTableData;
 | 
			
		||||
import cn.isliu.core.Sheet;
 | 
			
		||||
import cn.isliu.core.annotation.TableConf;
 | 
			
		||||
import cn.isliu.core.builder.MapWriteBuilder;
 | 
			
		||||
import cn.isliu.core.builder.ReadBuilder;
 | 
			
		||||
import cn.isliu.core.builder.SheetBuilder;
 | 
			
		||||
import cn.isliu.core.builder.WriteBuilder;
 | 
			
		||||
import cn.isliu.core.client.FeishuClient;
 | 
			
		||||
import cn.isliu.core.client.FsClient;
 | 
			
		||||
import cn.isliu.core.config.MapTableConfig;
 | 
			
		||||
import cn.isliu.core.enums.ErrorCode;
 | 
			
		||||
import cn.isliu.core.enums.FileType;
 | 
			
		||||
import cn.isliu.core.logging.FsLogger;
 | 
			
		||||
@ -216,6 +218,11 @@ public class FsHelper {
 | 
			
		||||
                rowNum.set(rowNum.get() + 1);
 | 
			
		||||
                values.forEach((field, fieldValue) -> {
 | 
			
		||||
                    String position = titlePostionMap.get(field);
 | 
			
		||||
 | 
			
		||||
                    if (position == null || position.isEmpty()) {
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (fieldValue instanceof FileData) {
 | 
			
		||||
                        FileData fileData = (FileData) fieldValue;
 | 
			
		||||
                        String fileType = fileData.getFileType();
 | 
			
		||||
@ -236,6 +243,11 @@ public class FsHelper {
 | 
			
		||||
                int rowCou = rowCount.incrementAndGet();
 | 
			
		||||
                values.forEach((field, fieldValue) -> {
 | 
			
		||||
                    String position = titlePostionMap.get(field);
 | 
			
		||||
 | 
			
		||||
                    if (position == null || position.isEmpty()) {
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (fieldValue instanceof FileData) {
 | 
			
		||||
                        FileData fileData = (FileData) fieldValue;
 | 
			
		||||
                        fileData.setSheetId(sheetId);
 | 
			
		||||
@ -286,4 +298,74 @@ public class FsHelper {
 | 
			
		||||
    public static <T> WriteBuilder<T> writeBuilder(String sheetId, String spreadsheetToken, List<T> dataList) {
 | 
			
		||||
        return new WriteBuilder<>(sheetId, spreadsheetToken, dataList);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 将 Map 数据写入飞书表格
 | 
			
		||||
     *
 | 
			
		||||
     * 直接使用 Map 格式的数据写入飞书表格,无需定义实体类和注解。
 | 
			
		||||
     * 适用于动态字段、临时数据写入等场景。
 | 
			
		||||
     *
 | 
			
		||||
     * 使用示例:
 | 
			
		||||
     * <pre>
 | 
			
		||||
     * List<Map<String, Object>> dataList = new ArrayList<>();
 | 
			
		||||
     * Map<String, Object> data = new HashMap<>();
 | 
			
		||||
     * data.put("字段名1", "值1");
 | 
			
		||||
     * data.put("字段名2", "值2");
 | 
			
		||||
     * dataList.add(data);
 | 
			
		||||
     *
 | 
			
		||||
     * MapTableConfig config = MapTableConfig.builder()
 | 
			
		||||
     *     .titleRow(2)
 | 
			
		||||
     *     .headLine(3)
 | 
			
		||||
     *     .addUniKeyName("字段名1")
 | 
			
		||||
     *     .enableCover(true)
 | 
			
		||||
     *     .build();
 | 
			
		||||
     *
 | 
			
		||||
     * FsHelper.writeMap(sheetId, spreadsheetToken, dataList, config);
 | 
			
		||||
     * </pre>
 | 
			
		||||
     *
 | 
			
		||||
     * @param sheetId 工作表ID
 | 
			
		||||
     * @param spreadsheetToken 电子表格Token
 | 
			
		||||
     * @param dataList Map数据列表,每个Map的key为字段名,value为字段值
 | 
			
		||||
     * @param config 表格配置,包含标题行、数据起始行、唯一键等配置信息
 | 
			
		||||
     * @return 写入操作结果
 | 
			
		||||
     */
 | 
			
		||||
    public static Object writeMap(String sheetId, String spreadsheetToken,
 | 
			
		||||
                                  List<Map<String, Object>> dataList, MapTableConfig config) {
 | 
			
		||||
        return new MapWriteBuilder(sheetId, spreadsheetToken, dataList)
 | 
			
		||||
                .config(config)
 | 
			
		||||
                .build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 创建 Map 数据写入构建器
 | 
			
		||||
     *
 | 
			
		||||
     * 返回一个 Map 数据写入构建器实例,支持链式调用和灵活配置。
 | 
			
		||||
     * 相比直接使用 writeMap 方法,构建器方式提供了更灵活的配置方式。
 | 
			
		||||
     *
 | 
			
		||||
     * 使用示例:
 | 
			
		||||
     * <pre>
 | 
			
		||||
     * List<Map<String, Object>> dataList = new ArrayList<>();
 | 
			
		||||
     * Map<String, Object> data = new HashMap<>();
 | 
			
		||||
     * data.put("字段名1", "值1");
 | 
			
		||||
     * data.put("字段名2", "值2");
 | 
			
		||||
     * dataList.add(data);
 | 
			
		||||
     *
 | 
			
		||||
     * FsHelper.writeMapBuilder(sheetId, spreadsheetToken, dataList)
 | 
			
		||||
     *     .titleRow(2)
 | 
			
		||||
     *     .headLine(3)
 | 
			
		||||
     *     .addUniKeyName("字段名1")
 | 
			
		||||
     *     .enableCover(true)
 | 
			
		||||
     *     .ignoreNotFound(false)
 | 
			
		||||
     *     .build();
 | 
			
		||||
     * </pre>
 | 
			
		||||
     *
 | 
			
		||||
     * @param sheetId 工作表ID
 | 
			
		||||
     * @param spreadsheetToken 电子表格Token
 | 
			
		||||
     * @param dataList 要写入的Map数据列表,每个Map的key为字段名,value为字段值
 | 
			
		||||
     * @return MapWriteBuilder实例,支持链式调用
 | 
			
		||||
     */
 | 
			
		||||
    public static MapWriteBuilder writeMapBuilder(String sheetId, String spreadsheetToken,
 | 
			
		||||
                                                  List<Map<String, Object>> dataList) {
 | 
			
		||||
        return new MapWriteBuilder(sheetId, spreadsheetToken, dataList);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										390
									
								
								src/main/java/cn/isliu/core/builder/MapWriteBuilder.java
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										390
									
								
								src/main/java/cn/isliu/core/builder/MapWriteBuilder.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,390 @@
 | 
			
		||||
package cn.isliu.core.builder;
 | 
			
		||||
 | 
			
		||||
import cn.isliu.core.*;
 | 
			
		||||
import cn.isliu.core.client.FeishuClient;
 | 
			
		||||
import cn.isliu.core.client.FsClient;
 | 
			
		||||
import cn.isliu.core.config.MapTableConfig;
 | 
			
		||||
import cn.isliu.core.enums.ErrorCode;
 | 
			
		||||
import cn.isliu.core.enums.FileType;
 | 
			
		||||
import cn.isliu.core.logging.FsLogger;
 | 
			
		||||
import cn.isliu.core.service.CustomValueService;
 | 
			
		||||
import cn.isliu.core.utils.*;
 | 
			
		||||
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import java.util.concurrent.atomic.AtomicInteger;
 | 
			
		||||
import java.util.concurrent.atomic.AtomicReference;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
import static cn.isliu.core.utils.FsTableUtil.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Map 数据写入构建器
 | 
			
		||||
 * 
 | 
			
		||||
 * 提供链式调用方式写入 Map 格式的飞书表格数据
 | 
			
		||||
 * 
 | 
			
		||||
 * @author Ls
 | 
			
		||||
 * @since 2025-10-16
 | 
			
		||||
 */
 | 
			
		||||
public class MapWriteBuilder {
 | 
			
		||||
    
 | 
			
		||||
    private final String sheetId;
 | 
			
		||||
    private final String spreadsheetToken;
 | 
			
		||||
    private final List<Map<String, Object>> dataList;
 | 
			
		||||
    private MapTableConfig config;
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 构造函数
 | 
			
		||||
     *
 | 
			
		||||
     * @param sheetId 工作表ID
 | 
			
		||||
     * @param spreadsheetToken 电子表格Token
 | 
			
		||||
     * @param dataList 要写入的Map数据列表
 | 
			
		||||
     */
 | 
			
		||||
    public MapWriteBuilder(String sheetId, String spreadsheetToken, List<Map<String, Object>> dataList) {
 | 
			
		||||
        this.sheetId = sheetId;
 | 
			
		||||
        this.spreadsheetToken = spreadsheetToken;
 | 
			
		||||
        this.dataList = dataList;
 | 
			
		||||
        this.config = MapTableConfig.createDefault();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置表格配置
 | 
			
		||||
     *
 | 
			
		||||
     * @param config Map表格配置
 | 
			
		||||
     * @return MapWriteBuilder实例
 | 
			
		||||
     */
 | 
			
		||||
    public MapWriteBuilder config(MapTableConfig config) {
 | 
			
		||||
        this.config = config;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置标题行
 | 
			
		||||
     *
 | 
			
		||||
     * @param titleRow 标题行行号
 | 
			
		||||
     * @return MapWriteBuilder实例
 | 
			
		||||
     */
 | 
			
		||||
    public MapWriteBuilder titleRow(int titleRow) {
 | 
			
		||||
        this.config.setTitleRow(titleRow);
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置数据起始行
 | 
			
		||||
     *
 | 
			
		||||
     * @param headLine 数据起始行号
 | 
			
		||||
     * @return MapWriteBuilder实例
 | 
			
		||||
     */
 | 
			
		||||
    public MapWriteBuilder headLine(int headLine) {
 | 
			
		||||
        this.config.setHeadLine(headLine);
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置唯一键字段
 | 
			
		||||
     *
 | 
			
		||||
     * @param uniKeyNames 唯一键字段名集合
 | 
			
		||||
     * @return MapWriteBuilder实例
 | 
			
		||||
     */
 | 
			
		||||
    public MapWriteBuilder uniKeyNames(Set<String> uniKeyNames) {
 | 
			
		||||
        this.config.setUniKeyNames(uniKeyNames);
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 添加唯一键字段
 | 
			
		||||
     *
 | 
			
		||||
     * @param uniKeyName 唯一键字段名
 | 
			
		||||
     * @return MapWriteBuilder实例
 | 
			
		||||
     */
 | 
			
		||||
    public MapWriteBuilder addUniKeyName(String uniKeyName) {
 | 
			
		||||
        this.config.addUniKeyName(uniKeyName);
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置是否覆盖已存在数据
 | 
			
		||||
     *
 | 
			
		||||
     * @param enableCover 是否覆盖
 | 
			
		||||
     * @return MapWriteBuilder实例
 | 
			
		||||
     */
 | 
			
		||||
    public MapWriteBuilder enableCover(boolean enableCover) {
 | 
			
		||||
        this.config.setEnableCover(enableCover);
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置是否忽略未找到的数据
 | 
			
		||||
     *
 | 
			
		||||
     * @param ignoreNotFound 是否忽略
 | 
			
		||||
     * @return MapWriteBuilder实例
 | 
			
		||||
     */
 | 
			
		||||
    public MapWriteBuilder ignoreNotFound(boolean ignoreNotFound) {
 | 
			
		||||
        this.config.setIgnoreNotFound(ignoreNotFound);
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 执行数据写入
 | 
			
		||||
     *
 | 
			
		||||
     * @return 写入操作结果
 | 
			
		||||
     */
 | 
			
		||||
    public Object build() {
 | 
			
		||||
        if (dataList.isEmpty()) {
 | 
			
		||||
            FsLogger.warn("【Map写入】数据列表为空,跳过写入操作");
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        FeishuClient client = FsClient.getInstance().getClient();
 | 
			
		||||
        Sheet sheet = FsApiUtil.getSheetMetadata(sheetId, client, spreadsheetToken);
 | 
			
		||||
        
 | 
			
		||||
        // 读取表格数据以获取字段位置映射和现有数据
 | 
			
		||||
        Map<String, String> titlePostionMap = readFieldsPositionMap(sheet, client);
 | 
			
		||||
        config.setFieldsPositionMap(titlePostionMap);
 | 
			
		||||
        
 | 
			
		||||
        // 读取现有数据用于匹配和更新
 | 
			
		||||
        Map<String, Integer> currTableRowMap = readExistingData(sheet, client, titlePostionMap);
 | 
			
		||||
        
 | 
			
		||||
        // 计算下一个可用行号
 | 
			
		||||
        int nextAvailableRow = calculateNextAvailableRow(currTableRowMap, config.getHeadLine());
 | 
			
		||||
        
 | 
			
		||||
        // 初始化批量插入对象
 | 
			
		||||
        CustomValueService.ValueRequest.BatchPutValuesBuilder resultValuesBuilder = 
 | 
			
		||||
            CustomValueService.ValueRequest.batchPutValues();
 | 
			
		||||
        
 | 
			
		||||
        List<FileData> fileDataList = new ArrayList<>();
 | 
			
		||||
        AtomicInteger rowCount = new AtomicInteger(nextAvailableRow);
 | 
			
		||||
        
 | 
			
		||||
        // 处理每条数据
 | 
			
		||||
        for (Map<String, Object> data : dataList) {
 | 
			
		||||
            String uniqueId = MapDataUtil.calculateUniqueId(data, config);
 | 
			
		||||
            
 | 
			
		||||
            AtomicReference<Integer> rowNum = new AtomicReference<>(currTableRowMap.get(uniqueId));
 | 
			
		||||
            
 | 
			
		||||
            if (uniqueId != null && rowNum.get() != null) {
 | 
			
		||||
                // 更新现有行
 | 
			
		||||
                rowNum.set(rowNum.get() + 1);
 | 
			
		||||
                processDataRow(data, titlePostionMap, rowNum.get(), resultValuesBuilder, 
 | 
			
		||||
                    fileDataList, config.isEnableCover());
 | 
			
		||||
            } else if (!config.isIgnoreNotFound()) {
 | 
			
		||||
                // 插入新行
 | 
			
		||||
                int newRow = rowCount.incrementAndGet();
 | 
			
		||||
                processDataRow(data, titlePostionMap, newRow, resultValuesBuilder, 
 | 
			
		||||
                    fileDataList, config.isEnableCover());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // 检查是否需要扩展行数
 | 
			
		||||
        ensureSufficientRows(sheet, rowCount.get(), client);
 | 
			
		||||
        
 | 
			
		||||
        // 上传文件
 | 
			
		||||
        uploadFiles(fileDataList, client);
 | 
			
		||||
        
 | 
			
		||||
        // 批量写入数据
 | 
			
		||||
        return batchWriteValues(resultValuesBuilder, client);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 读取字段位置映射
 | 
			
		||||
     */
 | 
			
		||||
    private Map<String, String> readFieldsPositionMap(Sheet sheet, FeishuClient client) {
 | 
			
		||||
        int titleRow = config.getTitleRow();
 | 
			
		||||
        int colCount = sheet.getGridProperties().getColumnCount();
 | 
			
		||||
        
 | 
			
		||||
        // 读取标题行数据
 | 
			
		||||
        ValuesBatch valuesBatch = FsApiUtil.getSheetData(
 | 
			
		||||
            sheet.getSheetId(), spreadsheetToken,
 | 
			
		||||
            "A" + titleRow,
 | 
			
		||||
            getColumnName(colCount - 1) + titleRow, 
 | 
			
		||||
            client
 | 
			
		||||
        );
 | 
			
		||||
        
 | 
			
		||||
        Map<String, String> fieldsPositionMap = new HashMap<>();
 | 
			
		||||
        
 | 
			
		||||
        if (valuesBatch != null && valuesBatch.getValueRanges() != null) {
 | 
			
		||||
            for (ValueRange valueRange : valuesBatch.getValueRanges()) {
 | 
			
		||||
                if (valueRange.getValues() != null && !valueRange.getValues().isEmpty()) {
 | 
			
		||||
                    List<Object> titleRowValues = valueRange.getValues().get(0);
 | 
			
		||||
                    for (int i = 0; i < titleRowValues.size(); i++) {
 | 
			
		||||
                        Object value = titleRowValues.get(i);
 | 
			
		||||
                        if (value != null) {
 | 
			
		||||
                            String fieldName = value.toString();
 | 
			
		||||
                            String columnPosition = getColumnName(i);
 | 
			
		||||
                            fieldsPositionMap.put(fieldName, columnPosition);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        return fieldsPositionMap;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 读取现有数据
 | 
			
		||||
     */
 | 
			
		||||
    private Map<String, Integer> readExistingData(Sheet sheet, FeishuClient client, Map<String, String> titlePostionMap) {
 | 
			
		||||
        int headLine = config.getHeadLine();
 | 
			
		||||
        int titleRow = config.getTitleRow();
 | 
			
		||||
        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 = 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);
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                
 | 
			
		||||
                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));
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 计算下一个可用行号
 | 
			
		||||
     */
 | 
			
		||||
    private int calculateNextAvailableRow(Map<String, Integer> currTableRowMap, int headLine) {
 | 
			
		||||
        if (currTableRowMap.isEmpty()) {
 | 
			
		||||
            return headLine;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        return currTableRowMap.values().stream()
 | 
			
		||||
            .max(Integer::compareTo)
 | 
			
		||||
            .map(maxRow -> maxRow + 1)
 | 
			
		||||
            .orElse(headLine);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 处理单行数据
 | 
			
		||||
     */
 | 
			
		||||
    private void processDataRow(Map<String, Object> data, Map<String, String> titlePostionMap,
 | 
			
		||||
                                 int rowNum, CustomValueService.ValueRequest.BatchPutValuesBuilder resultValuesBuilder,
 | 
			
		||||
                                 List<FileData> fileDataList, boolean enableCover) {
 | 
			
		||||
        data.forEach((field, fieldValue) -> {
 | 
			
		||||
            String position = titlePostionMap.get(field);
 | 
			
		||||
            
 | 
			
		||||
            if (position == null || position.isEmpty()) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            // 处理文件数据
 | 
			
		||||
            if (fieldValue instanceof FileData) {
 | 
			
		||||
                FileData fileData = (FileData) fieldValue;
 | 
			
		||||
                String fileType = fileData.getFileType();
 | 
			
		||||
                if (fileType.equals(FileType.IMAGE.getType())) {
 | 
			
		||||
                    fileData.setSheetId(sheetId);
 | 
			
		||||
                    fileData.setSpreadsheetToken(spreadsheetToken);
 | 
			
		||||
                    fileData.setPosition(position + rowNum);
 | 
			
		||||
                    fileDataList.add(fileData);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            // 添加到批量写入
 | 
			
		||||
            if (enableCover || fieldValue != null) {
 | 
			
		||||
                resultValuesBuilder.addRange(sheetId, position + rowNum, position + rowNum)
 | 
			
		||||
                    .addRow(GenerateUtil.getRowData(fieldValue));
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 确保行数足够
 | 
			
		||||
     */
 | 
			
		||||
    private void ensureSufficientRows(Sheet sheet, int requiredRows, FeishuClient client) {
 | 
			
		||||
        int rowTotal = sheet.getGridProperties().getRowCount();
 | 
			
		||||
        if (requiredRows >= rowTotal) {
 | 
			
		||||
            FsApiUtil.addRowColumns(sheetId, spreadsheetToken, "ROWS", requiredRows - rowTotal, client);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 上传文件
 | 
			
		||||
     */
 | 
			
		||||
    private void uploadFiles(List<FileData> fileDataList, FeishuClient client) {
 | 
			
		||||
        fileDataList.forEach(fileData -> {
 | 
			
		||||
            try {
 | 
			
		||||
                FsApiUtil.imageUpload(
 | 
			
		||||
                    fileData.getImageData(),
 | 
			
		||||
                    fileData.getFileName(),
 | 
			
		||||
                    fileData.getPosition(),
 | 
			
		||||
                    fileData.getSheetId(),
 | 
			
		||||
                    fileData.getSpreadsheetToken(),
 | 
			
		||||
                    client
 | 
			
		||||
                );
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                FsLogger.error(ErrorCode.BUSINESS_LOGIC_ERROR, 
 | 
			
		||||
                    "【飞书表格】Map写入-文件上传异常! " + fileData.getFileUrl());
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 批量写入数据
 | 
			
		||||
     */
 | 
			
		||||
    private Object batchWriteValues(CustomValueService.ValueRequest.BatchPutValuesBuilder resultValuesBuilder,
 | 
			
		||||
                                    FeishuClient client) {
 | 
			
		||||
        CustomValueService.ValueRequest build = resultValuesBuilder.build();
 | 
			
		||||
        CustomValueService.ValueBatchUpdatePutRequest batchPutValues = build.getBatchPutValues();
 | 
			
		||||
        List<CustomValueService.ValueRangeItem> valueRanges = batchPutValues.getValueRanges();
 | 
			
		||||
        
 | 
			
		||||
        if (valueRanges != null && !valueRanges.isEmpty()) {
 | 
			
		||||
            return FsApiUtil.batchPutValues(sheetId, spreadsheetToken, build, client);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        FsLogger.warn("【Map写入】没有数据需要写入");
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -198,6 +198,10 @@ public class WriteBuilder<T> {
 | 
			
		||||
                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();
 | 
			
		||||
@ -217,8 +221,12 @@ public class WriteBuilder<T> {
 | 
			
		||||
                int rowCou = rowCount.incrementAndGet();
 | 
			
		||||
                Map<String, String> finalTitlePostionMap1 = titlePostionMap;
 | 
			
		||||
                values.forEach((field, fieldValue) -> {
 | 
			
		||||
 | 
			
		||||
                    String position = finalTitlePostionMap1.get(field);
 | 
			
		||||
 | 
			
		||||
                    if (position == null || position.isEmpty()) {
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (fieldValue instanceof FileData) {
 | 
			
		||||
                        FileData fileData = (FileData) fieldValue;
 | 
			
		||||
                        fileData.setSheetId(sheetId);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										290
									
								
								src/main/java/cn/isliu/core/config/MapTableConfig.java
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										290
									
								
								src/main/java/cn/isliu/core/config/MapTableConfig.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,290 @@
 | 
			
		||||
package cn.isliu.core.config;
 | 
			
		||||
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Map方式表格配置类
 | 
			
		||||
 * 
 | 
			
		||||
 * 用于替代注解配置,支持使用Map方式操作飞书表格
 | 
			
		||||
 * 
 | 
			
		||||
 * @author Ls
 | 
			
		||||
 * @since 2025-10-16
 | 
			
		||||
 */
 | 
			
		||||
public class MapTableConfig {
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 标题行行号(从1开始)
 | 
			
		||||
     */
 | 
			
		||||
    private int titleRow = 1;
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 数据起始行行号(从1开始)
 | 
			
		||||
     */
 | 
			
		||||
    private int headLine = 1;
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 唯一键字段名列表
 | 
			
		||||
     */
 | 
			
		||||
    private Set<String> uniKeyNames = new HashSet<>();
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 是否覆盖已存在数据
 | 
			
		||||
     */
 | 
			
		||||
    private boolean enableCover = false;
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 是否忽略未找到的数据
 | 
			
		||||
     */
 | 
			
		||||
    private boolean ignoreNotFound = false;
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 字段位置映射 (字段名 -> 列位置,如 "添加SPU" -> "A")
 | 
			
		||||
     */
 | 
			
		||||
    private Map<String, String> fieldsPositionMap = new HashMap<>();
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取标题行行号
 | 
			
		||||
     *
 | 
			
		||||
     * @return 标题行行号
 | 
			
		||||
     */
 | 
			
		||||
    public int getTitleRow() {
 | 
			
		||||
        return titleRow;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置标题行行号
 | 
			
		||||
     *
 | 
			
		||||
     * @param titleRow 标题行行号
 | 
			
		||||
     * @return MapTableConfig实例,支持链式调用
 | 
			
		||||
     */
 | 
			
		||||
    public MapTableConfig setTitleRow(int titleRow) {
 | 
			
		||||
        this.titleRow = titleRow;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取数据起始行行号
 | 
			
		||||
     *
 | 
			
		||||
     * @return 数据起始行行号
 | 
			
		||||
     */
 | 
			
		||||
    public int getHeadLine() {
 | 
			
		||||
        return headLine;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置数据起始行行号
 | 
			
		||||
     *
 | 
			
		||||
     * @param headLine 数据起始行行号
 | 
			
		||||
     * @return MapTableConfig实例,支持链式调用
 | 
			
		||||
     */
 | 
			
		||||
    public MapTableConfig setHeadLine(int headLine) {
 | 
			
		||||
        this.headLine = headLine;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取唯一键字段名集合
 | 
			
		||||
     *
 | 
			
		||||
     * @return 唯一键字段名集合
 | 
			
		||||
     */
 | 
			
		||||
    public Set<String> getUniKeyNames() {
 | 
			
		||||
        return uniKeyNames;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置唯一键字段名集合
 | 
			
		||||
     *
 | 
			
		||||
     * @param uniKeyNames 唯一键字段名集合
 | 
			
		||||
     * @return MapTableConfig实例,支持链式调用
 | 
			
		||||
     */
 | 
			
		||||
    public MapTableConfig setUniKeyNames(Set<String> uniKeyNames) {
 | 
			
		||||
        this.uniKeyNames = uniKeyNames;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 添加唯一键字段名
 | 
			
		||||
     *
 | 
			
		||||
     * @param uniKeyName 唯一键字段名
 | 
			
		||||
     * @return MapTableConfig实例,支持链式调用
 | 
			
		||||
     */
 | 
			
		||||
    public MapTableConfig addUniKeyName(String uniKeyName) {
 | 
			
		||||
        this.uniKeyNames.add(uniKeyName);
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 是否覆盖已存在数据
 | 
			
		||||
     *
 | 
			
		||||
     * @return true表示覆盖,false表示不覆盖
 | 
			
		||||
     */
 | 
			
		||||
    public boolean isEnableCover() {
 | 
			
		||||
        return enableCover;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置是否覆盖已存在数据
 | 
			
		||||
     *
 | 
			
		||||
     * @param enableCover true表示覆盖,false表示不覆盖
 | 
			
		||||
     * @return MapTableConfig实例,支持链式调用
 | 
			
		||||
     */
 | 
			
		||||
    public MapTableConfig setEnableCover(boolean enableCover) {
 | 
			
		||||
        this.enableCover = enableCover;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 是否忽略未找到的数据
 | 
			
		||||
     *
 | 
			
		||||
     * @return true表示忽略,false表示不忽略
 | 
			
		||||
     */
 | 
			
		||||
    public boolean isIgnoreNotFound() {
 | 
			
		||||
        return ignoreNotFound;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置是否忽略未找到的数据
 | 
			
		||||
     *
 | 
			
		||||
     * @param ignoreNotFound true表示忽略,false表示不忽略
 | 
			
		||||
     * @return MapTableConfig实例,支持链式调用
 | 
			
		||||
     */
 | 
			
		||||
    public MapTableConfig setIgnoreNotFound(boolean ignoreNotFound) {
 | 
			
		||||
        this.ignoreNotFound = ignoreNotFound;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取字段位置映射
 | 
			
		||||
     *
 | 
			
		||||
     * @return 字段位置映射
 | 
			
		||||
     */
 | 
			
		||||
    public Map<String, String> getFieldsPositionMap() {
 | 
			
		||||
        return fieldsPositionMap;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置字段位置映射
 | 
			
		||||
     *
 | 
			
		||||
     * @param fieldsPositionMap 字段位置映射
 | 
			
		||||
     * @return MapTableConfig实例,支持链式调用
 | 
			
		||||
     */
 | 
			
		||||
    public MapTableConfig setFieldsPositionMap(Map<String, String> fieldsPositionMap) {
 | 
			
		||||
        this.fieldsPositionMap = fieldsPositionMap;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 创建默认配置
 | 
			
		||||
     *
 | 
			
		||||
     * @return 默认配置实例
 | 
			
		||||
     */
 | 
			
		||||
    public static MapTableConfig createDefault() {
 | 
			
		||||
        return new MapTableConfig();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 创建配置构建器
 | 
			
		||||
     *
 | 
			
		||||
     * @return 配置构建器实例
 | 
			
		||||
     */
 | 
			
		||||
    public static Builder builder() {
 | 
			
		||||
        return new Builder();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 配置构建器
 | 
			
		||||
     */
 | 
			
		||||
    public static class Builder {
 | 
			
		||||
        private final MapTableConfig config = new MapTableConfig();
 | 
			
		||||
        
 | 
			
		||||
        /**
 | 
			
		||||
         * 设置标题行行号
 | 
			
		||||
         *
 | 
			
		||||
         * @param titleRow 标题行行号
 | 
			
		||||
         * @return Builder实例
 | 
			
		||||
         */
 | 
			
		||||
        public Builder titleRow(int titleRow) {
 | 
			
		||||
            config.titleRow = titleRow;
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        /**
 | 
			
		||||
         * 设置数据起始行行号
 | 
			
		||||
         *
 | 
			
		||||
         * @param headLine 数据起始行行号
 | 
			
		||||
         * @return Builder实例
 | 
			
		||||
         */
 | 
			
		||||
        public Builder headLine(int headLine) {
 | 
			
		||||
            config.headLine = headLine;
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        /**
 | 
			
		||||
         * 设置唯一键字段名集合
 | 
			
		||||
         *
 | 
			
		||||
         * @param uniKeyNames 唯一键字段名集合
 | 
			
		||||
         * @return Builder实例
 | 
			
		||||
         */
 | 
			
		||||
        public Builder uniKeyNames(Set<String> uniKeyNames) {
 | 
			
		||||
            config.uniKeyNames = new HashSet<>(uniKeyNames);
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        /**
 | 
			
		||||
         * 添加唯一键字段名
 | 
			
		||||
         *
 | 
			
		||||
         * @param uniKeyName 唯一键字段名
 | 
			
		||||
         * @return Builder实例
 | 
			
		||||
         */
 | 
			
		||||
        public Builder addUniKeyName(String uniKeyName) {
 | 
			
		||||
            config.uniKeyNames.add(uniKeyName);
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        /**
 | 
			
		||||
         * 设置是否覆盖已存在数据
 | 
			
		||||
         *
 | 
			
		||||
         * @param enableCover true表示覆盖,false表示不覆盖
 | 
			
		||||
         * @return Builder实例
 | 
			
		||||
         */
 | 
			
		||||
        public Builder enableCover(boolean enableCover) {
 | 
			
		||||
            config.enableCover = enableCover;
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        /**
 | 
			
		||||
         * 设置是否忽略未找到的数据
 | 
			
		||||
         *
 | 
			
		||||
         * @param ignoreNotFound true表示忽略,false表示不忽略
 | 
			
		||||
         * @return Builder实例
 | 
			
		||||
         */
 | 
			
		||||
        public Builder ignoreNotFound(boolean ignoreNotFound) {
 | 
			
		||||
            config.ignoreNotFound = ignoreNotFound;
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        /**
 | 
			
		||||
         * 设置字段位置映射
 | 
			
		||||
         *
 | 
			
		||||
         * @param fieldsPositionMap 字段位置映射
 | 
			
		||||
         * @return Builder实例
 | 
			
		||||
         */
 | 
			
		||||
        public Builder fieldsPositionMap(Map<String, String> fieldsPositionMap) {
 | 
			
		||||
            config.fieldsPositionMap = new HashMap<>(fieldsPositionMap);
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        /**
 | 
			
		||||
         * 构建配置对象
 | 
			
		||||
         *
 | 
			
		||||
         * @return MapTableConfig实例
 | 
			
		||||
         */
 | 
			
		||||
        public MapTableConfig build() {
 | 
			
		||||
            return config;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										134
									
								
								src/main/java/cn/isliu/core/utils/MapDataUtil.java
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										134
									
								
								src/main/java/cn/isliu/core/utils/MapDataUtil.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,134 @@
 | 
			
		||||
package cn.isliu.core.utils;
 | 
			
		||||
 | 
			
		||||
import cn.isliu.core.config.MapTableConfig;
 | 
			
		||||
 | 
			
		||||
import java.util.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Map 数据处理工具类
 | 
			
		||||
 * 
 | 
			
		||||
 * 提供Map格式数据的处理方法,包括唯一ID计算、字段位置映射等
 | 
			
		||||
 * 
 | 
			
		||||
 * @author Ls
 | 
			
		||||
 * @since 2025-10-16
 | 
			
		||||
 */
 | 
			
		||||
public class MapDataUtil {
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 计算 Map 数据的唯一ID
 | 
			
		||||
     *
 | 
			
		||||
     * 根据配置的唯一键字段计算数据的唯一标识。
 | 
			
		||||
     * 如果没有指定唯一键,则使用所有字段计算。
 | 
			
		||||
     *
 | 
			
		||||
     * @param data Map数据
 | 
			
		||||
     * @param config 表格配置
 | 
			
		||||
     * @return 唯一ID(SHA256哈希值)
 | 
			
		||||
     */
 | 
			
		||||
    public static String calculateUniqueId(Map<String, Object> data, MapTableConfig config) {
 | 
			
		||||
        if (data == null || data.isEmpty()) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        Set<String> uniKeyNames = config.getUniKeyNames();
 | 
			
		||||
        
 | 
			
		||||
        if (uniKeyNames == null || uniKeyNames.isEmpty()) {
 | 
			
		||||
            // 如果没有指定唯一键,使用所有字段计算
 | 
			
		||||
            String jsonStr = StringUtil.mapToJson(data);
 | 
			
		||||
            return StringUtil.getSHA256(jsonStr);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // 使用指定的唯一键字段计算
 | 
			
		||||
        List<Object> uniKeyValues = new ArrayList<>();
 | 
			
		||||
        for (String key : uniKeyNames) {
 | 
			
		||||
            if (data.containsKey(key)) {
 | 
			
		||||
                uniKeyValues.add(data.get(key));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if (uniKeyValues.isEmpty()) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        String jsonStr = StringUtil.listToJson(uniKeyValues);
 | 
			
		||||
        return StringUtil.getSHA256(jsonStr);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 从标题行数据构建字段位置映射
 | 
			
		||||
     *
 | 
			
		||||
     * 将标题行的列位置与字段名称进行映射,用于后续数据写入时定位单元格位置。
 | 
			
		||||
     *
 | 
			
		||||
     * @param titleRowData 标题行数据,key为列位置(如"A"、"B"),value为字段名称
 | 
			
		||||
     * @return 字段位置映射,key为字段名称,value为列位置
 | 
			
		||||
     */
 | 
			
		||||
    public static Map<String, String> buildFieldsPositionMap(Map<String, String> titleRowData) {
 | 
			
		||||
        if (titleRowData == null || titleRowData.isEmpty()) {
 | 
			
		||||
            return new HashMap<>();
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        Map<String, String> fieldsPositionMap = new HashMap<>();
 | 
			
		||||
        titleRowData.forEach((position, fieldName) -> {
 | 
			
		||||
            if (fieldName != null && !fieldName.isEmpty()) {
 | 
			
		||||
                fieldsPositionMap.put(fieldName, position);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        return fieldsPositionMap;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 验证数据字段是否与表格字段匹配
 | 
			
		||||
     *
 | 
			
		||||
     * 检查数据中的字段是否在表格的字段位置映射中存在。
 | 
			
		||||
     *
 | 
			
		||||
     * @param data 数据Map
 | 
			
		||||
     * @param fieldsPositionMap 字段位置映射
 | 
			
		||||
     * @return 未匹配的字段列表
 | 
			
		||||
     */
 | 
			
		||||
    public static List<String> validateDataFields(Map<String, Object> data, Map<String, String> fieldsPositionMap) {
 | 
			
		||||
        if (data == null || data.isEmpty()) {
 | 
			
		||||
            return new ArrayList<>();
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if (fieldsPositionMap == null || fieldsPositionMap.isEmpty()) {
 | 
			
		||||
            return new ArrayList<>(data.keySet());
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        List<String> unmatchedFields = new ArrayList<>();
 | 
			
		||||
        for (String fieldName : data.keySet()) {
 | 
			
		||||
            if (!fieldsPositionMap.containsKey(fieldName)) {
 | 
			
		||||
                unmatchedFields.add(fieldName);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        return unmatchedFields;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 过滤数据字段
 | 
			
		||||
     *
 | 
			
		||||
     * 只保留在字段位置映射中存在的字段。
 | 
			
		||||
     *
 | 
			
		||||
     * @param data 原始数据Map
 | 
			
		||||
     * @param fieldsPositionMap 字段位置映射
 | 
			
		||||
     * @return 过滤后的数据Map
 | 
			
		||||
     */
 | 
			
		||||
    public static Map<String, Object> filterDataFields(Map<String, Object> data, Map<String, String> fieldsPositionMap) {
 | 
			
		||||
        if (data == null || data.isEmpty()) {
 | 
			
		||||
            return new HashMap<>();
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if (fieldsPositionMap == null || fieldsPositionMap.isEmpty()) {
 | 
			
		||||
            return new HashMap<>();
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        Map<String, Object> filteredData = new HashMap<>();
 | 
			
		||||
        data.forEach((fieldName, fieldValue) -> {
 | 
			
		||||
            if (fieldsPositionMap.containsKey(fieldName)) {
 | 
			
		||||
                filteredData.put(fieldName, fieldValue);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        
 | 
			
		||||
        return filteredData;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user