```
feat(core): 增强飞书表格功能与工具类支持 -为 ConvertFieldUtil、FileUtil、FsApiUtil 和 FsTableUtil 类添加更详细的 JavaDoc 注释,提升代码可读性- 在 FileUtil 中优化图片文件判断逻辑并增强 getFileName 方法以支持 URL 场景 - 扩展 FsApiUtil 支持更多飞书 API 参数格式化及异常处理 - 在 FsHelper 中引入 ReadBuilder 和 WriteBuilder 构建器模式,提供链式调用方式操作飞书表格读写 - FsTableUtil 新增对忽略唯一字段的支持,并扩展表头模板构建能力,支持字段描述自定义- GenerateUtil优化数字转换精度控制,并修复部分日志提示文案 - OptionsValueProcess 接口增加泛型参数支持,提高灵活性和类型安全性```
This commit is contained in:
		
							parent
							
								
									3e25a91aed
								
							
						
					
					
						commit
						891b019ae3
					
				@ -5,7 +5,9 @@ import cn.isliu.core.FileData;
 | 
				
			|||||||
import cn.isliu.core.FsTableData;
 | 
					import cn.isliu.core.FsTableData;
 | 
				
			||||||
import cn.isliu.core.Sheet;
 | 
					import cn.isliu.core.Sheet;
 | 
				
			||||||
import cn.isliu.core.annotation.TableConf;
 | 
					import cn.isliu.core.annotation.TableConf;
 | 
				
			||||||
 | 
					import cn.isliu.core.builder.ReadBuilder;
 | 
				
			||||||
import cn.isliu.core.builder.SheetBuilder;
 | 
					import cn.isliu.core.builder.SheetBuilder;
 | 
				
			||||||
 | 
					import cn.isliu.core.builder.WriteBuilder;
 | 
				
			||||||
import cn.isliu.core.client.FeishuClient;
 | 
					import cn.isliu.core.client.FeishuClient;
 | 
				
			||||||
import cn.isliu.core.client.FsClient;
 | 
					import cn.isliu.core.client.FsClient;
 | 
				
			||||||
import cn.isliu.core.enums.ErrorCode;
 | 
					import cn.isliu.core.enums.ErrorCode;
 | 
				
			||||||
@ -20,6 +22,8 @@ import com.google.gson.JsonObject;
 | 
				
			|||||||
import java.util.*;
 | 
					import java.util.*;
 | 
				
			||||||
import java.util.concurrent.atomic.AtomicInteger;
 | 
					import java.util.concurrent.atomic.AtomicInteger;
 | 
				
			||||||
import java.util.concurrent.atomic.AtomicReference;
 | 
					import java.util.concurrent.atomic.AtomicReference;
 | 
				
			||||||
 | 
					import java.util.logging.Level;
 | 
				
			||||||
 | 
					import java.util.logging.Logger;
 | 
				
			||||||
import java.util.stream.Collectors;
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@ -70,8 +74,12 @@ public class FsHelper {
 | 
				
			|||||||
            FsApiUtil.setCellType(sheetId, "@", "A1", column + 200, client, spreadsheetToken);
 | 
					            FsApiUtil.setCellType(sheetId, "@", "A1", column + 200, client, spreadsheetToken);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
            // 5 设置表格下拉
 | 
					            // 5 设置表格下拉
 | 
				
			||||||
            FsTableUtil.setTableOptions(spreadsheetToken, headers, fieldsMap, sheetId, tableConf.enableDesc());
 | 
					            FsTableUtil.setTableOptions(spreadsheetToken, headers, fieldsMap, sheetId, tableConf.enableDesc());
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            Logger.getLogger(SheetBuilder.class.getName()).log(Level.SEVERE,"【表格构建器】设置表格下拉异常!sheetId:" + sheetId + ", 错误信息:{}", e.getMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        return sheetId;
 | 
					        return sheetId;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -133,6 +141,22 @@ public class FsHelper {
 | 
				
			|||||||
        return  results;
 | 
					        return  results;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 创建飞书表格数据读取构建器
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * 返回一个数据读取构建器实例,支持链式调用和高级配置选项,
 | 
				
			||||||
 | 
					     * 如忽略唯一字段等功能。
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param sheetId 工作表ID
 | 
				
			||||||
 | 
					     * @param spreadsheetToken 电子表格Token
 | 
				
			||||||
 | 
					     * @param clazz 实体类Class对象,用于数据映射
 | 
				
			||||||
 | 
					     * @param <T> 实体类泛型
 | 
				
			||||||
 | 
					     * @return ReadBuilder实例,支持链式调用
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static <T> ReadBuilder<T> readBuilder(String sheetId, String spreadsheetToken, Class<T> clazz) {
 | 
				
			||||||
 | 
					        return new ReadBuilder<>(sheetId, spreadsheetToken, clazz);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 将数据写入飞书表格
 | 
					     * 将数据写入飞书表格
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
@ -244,4 +268,20 @@ public class FsHelper {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return resp;
 | 
					        return resp;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 创建飞书表格数据写入构建器
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * 返回一个数据写入构建器实例,支持链式调用和高级配置选项,
 | 
				
			||||||
 | 
					     * 如忽略唯一字段等功能。
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param sheetId 工作表ID
 | 
				
			||||||
 | 
					     * @param spreadsheetToken 电子表格Token
 | 
				
			||||||
 | 
					     * @param dataList 要写入的数据列表
 | 
				
			||||||
 | 
					     * @param <T> 实体类泛型
 | 
				
			||||||
 | 
					     * @return WriteBuilder实例,支持链式调用
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static <T> WriteBuilder<T> writeBuilder(String sheetId, String spreadsheetToken, List<T> dataList) {
 | 
				
			||||||
 | 
					        return new WriteBuilder<>(sheetId, spreadsheetToken, dataList);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										130
									
								
								src/main/java/cn/isliu/core/builder/ReadBuilder.java
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										130
									
								
								src/main/java/cn/isliu/core/builder/ReadBuilder.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,130 @@
 | 
				
			|||||||
 | 
					package cn.isliu.core.builder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import cn.isliu.core.BaseEntity;
 | 
				
			||||||
 | 
					import cn.isliu.core.FsTableData;
 | 
				
			||||||
 | 
					import cn.isliu.core.Sheet;
 | 
				
			||||||
 | 
					import cn.isliu.core.annotation.TableConf;
 | 
				
			||||||
 | 
					import cn.isliu.core.client.FeishuClient;
 | 
				
			||||||
 | 
					import cn.isliu.core.client.FsClient;
 | 
				
			||||||
 | 
					import cn.isliu.core.pojo.FieldProperty;
 | 
				
			||||||
 | 
					import cn.isliu.core.utils.*;
 | 
				
			||||||
 | 
					import com.google.gson.JsonObject;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 数据读取构建器
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * 提供链式调用方式读取飞书表格数据,支持忽略唯一字段等高级功能。
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ReadBuilder<T> {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private final String sheetId;
 | 
				
			||||||
 | 
					    private final String spreadsheetToken;
 | 
				
			||||||
 | 
					    private final Class<T> clazz;
 | 
				
			||||||
 | 
					    private List<String> ignoreUniqueFields;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 构造函数
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @param sheetId 工作表ID
 | 
				
			||||||
 | 
					     * @param spreadsheetToken 电子表格Token
 | 
				
			||||||
 | 
					     * @param clazz 实体类Class对象
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadBuilder(String sheetId, String spreadsheetToken, Class<T> clazz) {
 | 
				
			||||||
 | 
					        this.sheetId = sheetId;
 | 
				
			||||||
 | 
					        this.spreadsheetToken = spreadsheetToken;
 | 
				
			||||||
 | 
					        this.clazz = clazz;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 设置计算唯一标识时忽略的字段列表
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * 指定在计算数据行唯一标识时要忽略的字段名称列表。
 | 
				
			||||||
 | 
					     * 这些字段的值变化不会影响数据行的唯一性判断。
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @param fields 要忽略的字段名称列表
 | 
				
			||||||
 | 
					     * @return ReadBuilder实例,支持链式调用
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadBuilder<T> ignoreUniqueFields(List<String> fields) {
 | 
				
			||||||
 | 
					        this.ignoreUniqueFields = new ArrayList<>(fields);
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 执行数据读取并返回实体类对象列表
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * 根据配置的参数从飞书表格中读取数据并映射到实体类对象列表中。
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @return 映射后的实体类对象列表
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public List<T> build() {
 | 
				
			||||||
 | 
					        List<T> results = new ArrayList<>();
 | 
				
			||||||
 | 
					        FeishuClient client = FsClient.getInstance().getClient();
 | 
				
			||||||
 | 
					        Sheet sheet = FsApiUtil.getSheetMetadata(sheetId, client, spreadsheetToken);
 | 
				
			||||||
 | 
					        TableConf tableConf = PropertyUtil.getTableConf(clazz);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Map<String, FieldProperty> fieldsMap = PropertyUtil.getTablePropertyFieldsMap(clazz);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // 处理忽略字段名称映射
 | 
				
			||||||
 | 
					        List<String> processedIgnoreFields = processIgnoreFields(fieldsMap);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // 使用支持忽略字段的方法获取表格数据
 | 
				
			||||||
 | 
					        List<FsTableData> fsTableDataList = FsTableUtil.getFsTableData(sheet, spreadsheetToken, tableConf, processedIgnoreFields);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        List<String> fieldPathList = fieldsMap.values().stream().map(FieldProperty::getField).collect(Collectors.toList());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fsTableDataList.forEach(tableData -> {
 | 
				
			||||||
 | 
					            Object data = tableData.getData();
 | 
				
			||||||
 | 
					            if (data instanceof HashMap) {
 | 
				
			||||||
 | 
					                Map<String, Object> rowData = (HashMap<String, Object>) data;
 | 
				
			||||||
 | 
					                JsonObject jsonObject = JSONUtil.convertMapToJsonObject(rowData);
 | 
				
			||||||
 | 
					                Map<String, Object> dataMap = ConvertFieldUtil.convertPositionToField(jsonObject, fieldsMap);
 | 
				
			||||||
 | 
					                T t = GenerateUtil.generateInstance(fieldPathList, clazz, dataMap);
 | 
				
			||||||
 | 
					                if (t instanceof BaseEntity) {
 | 
				
			||||||
 | 
					                    BaseEntity baseEntity = (BaseEntity) t;
 | 
				
			||||||
 | 
					                    baseEntity.setUniqueId(tableData.getUniqueId());
 | 
				
			||||||
 | 
					                    baseEntity.setRow(tableData.getRow());
 | 
				
			||||||
 | 
					                    baseEntity.setRowData(rowData);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                results.add(t);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        return results;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 处理忽略字段名称映射
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * 将实体字段名称映射为表格列名称
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @param fieldsMap 字段映射
 | 
				
			||||||
 | 
					     * @return 处理后的忽略字段列表
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private List<String> processIgnoreFields(Map<String, FieldProperty> fieldsMap) {
 | 
				
			||||||
 | 
					        if (ignoreUniqueFields == null || ignoreUniqueFields.isEmpty()) {
 | 
				
			||||||
 | 
					            return new ArrayList<>();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        List<String> processedFields = new ArrayList<>();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // 遍历字段映射,找到对应的表格列名
 | 
				
			||||||
 | 
					        for (Map.Entry<String, FieldProperty> entry : fieldsMap.entrySet()) {
 | 
				
			||||||
 | 
					            String fieldName = entry.getValue().getField();
 | 
				
			||||||
 | 
					            // 获取字段的最后一部分名称(去掉嵌套路径)
 | 
				
			||||||
 | 
					            String simpleFieldName = fieldName.substring(fieldName.lastIndexOf(".") + 1);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // 如果忽略字段列表中包含此字段名,则添加对应的表格列名
 | 
				
			||||||
 | 
					            if (ignoreUniqueFields.contains(simpleFieldName)) {
 | 
				
			||||||
 | 
					                String tableColumnName = entry.getKey(); // 表格列名(注解中的value值)
 | 
				
			||||||
 | 
					                processedFields.add(tableColumnName);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return processedFields;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -8,10 +8,14 @@ import cn.isliu.core.service.CustomCellService;
 | 
				
			|||||||
import cn.isliu.core.utils.FsApiUtil;
 | 
					import cn.isliu.core.utils.FsApiUtil;
 | 
				
			||||||
import cn.isliu.core.utils.FsTableUtil;
 | 
					import cn.isliu.core.utils.FsTableUtil;
 | 
				
			||||||
import cn.isliu.core.utils.PropertyUtil;
 | 
					import cn.isliu.core.utils.PropertyUtil;
 | 
				
			||||||
 | 
					import cn.isliu.core.utils.StringUtil;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.ArrayList;
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.logging.Level;
 | 
				
			||||||
 | 
					import java.util.logging.Logger;
 | 
				
			||||||
import java.util.stream.Collectors;
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@ -25,6 +29,8 @@ public class SheetBuilder<T> {
 | 
				
			|||||||
    private final String spreadsheetToken;
 | 
					    private final String spreadsheetToken;
 | 
				
			||||||
    private final Class<T> clazz;
 | 
					    private final Class<T> clazz;
 | 
				
			||||||
    private List<String> includeFields;
 | 
					    private List<String> includeFields;
 | 
				
			||||||
 | 
					    private final Map<String, Object> customProperties = new HashMap<>();
 | 
				
			||||||
 | 
					    private final Map<String, String> fieldDescriptions = new HashMap<>();
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 构造函数
 | 
					     * 构造函数
 | 
				
			||||||
@ -52,6 +58,91 @@ public class SheetBuilder<T> {
 | 
				
			|||||||
        return this;
 | 
					        return this;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 设置自定义属性
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * 添加一个自定义属性,可以在构建表格时使用
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @param key 属性键
 | 
				
			||||||
 | 
					     * @param value 属性值
 | 
				
			||||||
 | 
					     * @return SheetBuilder实例,支持链式调用
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public SheetBuilder<T> addCustomProperty(String key, Object value) {
 | 
				
			||||||
 | 
					        this.customProperties.put(key, value);
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 批量设置自定义属性
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * 批量添加自定义属性,可以在构建表格时使用
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @param properties 自定义属性映射
 | 
				
			||||||
 | 
					     * @return SheetBuilder实例,支持链式调用
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public SheetBuilder<T> addCustomProperties(Map<String, Object> properties) {
 | 
				
			||||||
 | 
					        this.customProperties.putAll(properties);
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 获取自定义属性
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * 根据键获取已设置的自定义属性值
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @param key 属性键
 | 
				
			||||||
 | 
					     * @return 属性值,如果不存在则返回null
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public Object getCustomProperty(String key) {
 | 
				
			||||||
 | 
					        return this.customProperties.get(key);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 获取所有自定义属性
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @return 包含所有自定义属性的映射
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public Map<String, Object> getCustomProperties() {
 | 
				
			||||||
 | 
					        return new HashMap<>(this.customProperties);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 设置字段描述映射
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * 为实体类字段设置自定义描述信息,用于在表格描述行中显示。
 | 
				
			||||||
 | 
					     * 如果字段在映射中存在描述,则使用映射中的描述;否则使用注解中的描述。
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @param fieldDescriptions 字段名到描述的映射,key为字段名,value为描述文本
 | 
				
			||||||
 | 
					     * @return SheetBuilder实例,支持链式调用
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public SheetBuilder<T> fieldDescription(Map<String, String> fieldDescriptions) {
 | 
				
			||||||
 | 
					        this.fieldDescriptions.putAll(fieldDescriptions);
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 设置单个字段描述
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * 为指定字段设置自定义描述信息。
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @param fieldName 字段名
 | 
				
			||||||
 | 
					     * @param description 描述文本
 | 
				
			||||||
 | 
					     * @return SheetBuilder实例,支持链式调用
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public SheetBuilder<T> fieldDescription(String fieldName, String description) {
 | 
				
			||||||
 | 
					        this.fieldDescriptions.put(fieldName, description);
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 获取字段描述映射
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @return 包含所有字段描述的映射
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public Map<String, String> getFieldDescriptions() {
 | 
				
			||||||
 | 
					        return new HashMap<>(this.fieldDescriptions);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 构建表格并返回工作表ID
 | 
					     * 构建表格并返回工作表ID
 | 
				
			||||||
     * 
 | 
					     * 
 | 
				
			||||||
@ -79,7 +170,7 @@ public class SheetBuilder<T> {
 | 
				
			|||||||
        String sheetId = FsApiUtil.createSheet(sheetName, client, spreadsheetToken);
 | 
					        String sheetId = FsApiUtil.createSheet(sheetName, client, spreadsheetToken);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // 2、添加表头数据
 | 
					        // 2、添加表头数据
 | 
				
			||||||
        FsApiUtil.putValues(spreadsheetToken, FsTableUtil.getHeadTemplateBuilder(sheetId, headers, fieldsMap, tableConf), client);
 | 
					        FsApiUtil.putValues(spreadsheetToken, FsTableUtil.getHeadTemplateBuilder(sheetId, headers, fieldsMap, includeFields, tableConf, fieldDescriptions), client);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // 3、设置表格样式
 | 
					        // 3、设置表格样式
 | 
				
			||||||
        FsApiUtil.setTableStyle(FsTableUtil.getDefaultTableStyle(sheetId, fieldsMap, tableConf), client, spreadsheetToken);
 | 
					        FsApiUtil.setTableStyle(FsTableUtil.getDefaultTableStyle(sheetId, fieldsMap, tableConf), client, spreadsheetToken);
 | 
				
			||||||
@ -97,7 +188,11 @@ public class SheetBuilder<T> {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // 6、设置表格下拉
 | 
					        // 6、设置表格下拉
 | 
				
			||||||
        FsTableUtil.setTableOptions(spreadsheetToken, headers, fieldsMap, sheetId, tableConf.enableDesc());
 | 
					        try {
 | 
				
			||||||
 | 
					            FsTableUtil.setTableOptions(spreadsheetToken, headers, fieldsMap, sheetId, tableConf.enableDesc(), customProperties);
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            Logger.getLogger(SheetBuilder.class.getName()).log(Level.SEVERE,"【表格构建器】设置表格下拉异常!sheetId:" + sheetId + ", 错误信息:{}", e.getMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return sheetId;
 | 
					        return sheetId;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -116,7 +211,14 @@ public class SheetBuilder<T> {
 | 
				
			|||||||
        
 | 
					        
 | 
				
			||||||
        // 根据字段名过滤,保留指定的字段
 | 
					        // 根据字段名过滤,保留指定的字段
 | 
				
			||||||
        return allFieldsMap.entrySet().stream()
 | 
					        return allFieldsMap.entrySet().stream()
 | 
				
			||||||
                .filter(entry -> includeFields.contains(entry.getValue().getField()))
 | 
					                .filter(entry -> {
 | 
				
			||||||
 | 
					                    String field = entry.getValue().getField();
 | 
				
			||||||
 | 
					                    field = field.substring(field.lastIndexOf(".") + 1);
 | 
				
			||||||
 | 
					                    if (field.isEmpty()) {
 | 
				
			||||||
 | 
					                        return false;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    return includeFields.contains(StringUtil.toUnderscoreCase(field));
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
 | 
					                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										293
									
								
								src/main/java/cn/isliu/core/builder/WriteBuilder.java
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										293
									
								
								src/main/java/cn/isliu/core/builder/WriteBuilder.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,293 @@
 | 
				
			|||||||
 | 
					package cn.isliu.core.builder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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.client.FeishuClient;
 | 
				
			||||||
 | 
					import cn.isliu.core.client.FsClient;
 | 
				
			||||||
 | 
					import cn.isliu.core.enums.ErrorCode;
 | 
				
			||||||
 | 
					import cn.isliu.core.enums.FileType;
 | 
				
			||||||
 | 
					import cn.isliu.core.logging.FsLogger;
 | 
				
			||||||
 | 
					import cn.isliu.core.pojo.FieldProperty;
 | 
				
			||||||
 | 
					import cn.isliu.core.service.CustomValueService;
 | 
				
			||||||
 | 
					import cn.isliu.core.utils.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.concurrent.atomic.AtomicInteger;
 | 
				
			||||||
 | 
					import java.util.concurrent.atomic.AtomicReference;
 | 
				
			||||||
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 数据写入构建器
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * 提供链式调用方式写入飞书表格数据,支持忽略唯一字段等高级功能。
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class WriteBuilder<T> {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private final String sheetId;
 | 
				
			||||||
 | 
					    private final String spreadsheetToken;
 | 
				
			||||||
 | 
					    private final List<T> dataList;
 | 
				
			||||||
 | 
					    private List<String> ignoreUniqueFields;
 | 
				
			||||||
 | 
					    private Class<?> clazz;
 | 
				
			||||||
 | 
					    private boolean ignoreNotFound;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 构造函数
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @param sheetId 工作表ID
 | 
				
			||||||
 | 
					     * @param spreadsheetToken 电子表格Token
 | 
				
			||||||
 | 
					     * @param dataList 要写入的数据列表
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public WriteBuilder(String sheetId, String spreadsheetToken, List<T> dataList) {
 | 
				
			||||||
 | 
					        this.sheetId = sheetId;
 | 
				
			||||||
 | 
					        this.spreadsheetToken = spreadsheetToken;
 | 
				
			||||||
 | 
					        this.dataList = dataList;
 | 
				
			||||||
 | 
					        this.clazz = null;
 | 
				
			||||||
 | 
					        this.ignoreNotFound = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 设置计算唯一标识时忽略的字段列表
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * 指定在计算数据行唯一标识时要忽略的字段名称列表。
 | 
				
			||||||
 | 
					     * 这些字段的值变化不会影响数据行的唯一性判断。
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @param fields 要忽略的字段名称列表
 | 
				
			||||||
 | 
					     * @return WriteBuilder实例,支持链式调用
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public WriteBuilder<T> ignoreUniqueFields(List<String> fields) {
 | 
				
			||||||
 | 
					        this.ignoreUniqueFields = new ArrayList<>(fields);
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 设置用于解析注解的实体类
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * 指定用于解析@TableProperty注解的实体类。这个类可以与数据列表的类型不同,
 | 
				
			||||||
 | 
					     * 主要用于获取字段映射关系和表格配置信息。
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @param clazz 用于解析注解的实体类
 | 
				
			||||||
 | 
					     * @return WriteBuilder实例,支持链式调用
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public WriteBuilder<T> clazz(Class<?> clazz) {
 | 
				
			||||||
 | 
					        this.clazz = clazz;
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 设置是否忽略未找到的数据
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * 指定在写入数据时是否忽略未找到的字段。如果设置为true,
 | 
				
			||||||
 | 
					     * 当数据中包含表格中不存在的字段时,写入操作将继续执行,
 | 
				
			||||||
 | 
					     * 而不会抛出异常。默认值为false。
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ignoreNotFound 是否忽略未找到的字段,默认值为false
 | 
				
			||||||
 | 
					     * @return WriteBuilder实例,支持链式调用
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public WriteBuilder<T> ignoreNotFound(boolean ignoreNotFound) {
 | 
				
			||||||
 | 
					        this.ignoreNotFound = ignoreNotFound;
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 执行数据写入并返回操作结果
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * 根据配置的参数将数据写入到飞书表格中,支持新增和更新操作。
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @return 写入操作结果
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public Object build() {
 | 
				
			||||||
 | 
					        if (dataList.isEmpty()) {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Class<?> aClass = clazz;
 | 
				
			||||||
 | 
					        Map<String, FieldProperty> fieldsMap;
 | 
				
			||||||
 | 
					        TableConf tableConf = PropertyUtil.getTableConf(aClass);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Map<String, String> fieldMap = new HashMap<>();
 | 
				
			||||||
 | 
					        Class<?> sourceClass = dataList.get(0).getClass();
 | 
				
			||||||
 | 
					        if (aClass.equals(sourceClass)) {
 | 
				
			||||||
 | 
					            fieldsMap = PropertyUtil.getTablePropertyFieldsMap(aClass);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            fieldsMap = PropertyUtil.getTablePropertyFieldsMap(sourceClass);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fieldsMap.forEach((field, fieldProperty) -> fieldMap.put(field, fieldProperty.getField()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 处理忽略字段名称映射
 | 
				
			||||||
 | 
					        List<String> processedIgnoreFields = processIgnoreFields(fieldsMap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        FeishuClient client = FsClient.getInstance().getClient();
 | 
				
			||||||
 | 
					        Sheet sheet = FsApiUtil.getSheetMetadata(sheetId, client, spreadsheetToken);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // 使用支持忽略字段的方法获取表格数据
 | 
				
			||||||
 | 
					        List<FsTableData> fsTableDataList = FsTableUtil.getFsTableData(sheet, spreadsheetToken, tableConf, processedIgnoreFields);
 | 
				
			||||||
 | 
					        Map<String, Integer> currTableRowMap = fsTableDataList.stream().collect(Collectors.toMap(FsTableData::getUniqueId, FsTableData::getRow));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final Integer[] row = {0};
 | 
				
			||||||
 | 
					        fsTableDataList.forEach(fsTableData -> {
 | 
				
			||||||
 | 
					            if (fsTableData.getRow() > row[0]) {
 | 
				
			||||||
 | 
					                row[0] = fsTableData.getRow();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Map<String, String> titlePostionMap = FsTableUtil.getTitlePostionMap(sheet, spreadsheetToken, tableConf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 初始化批量插入对象
 | 
				
			||||||
 | 
					        CustomValueService.ValueRequest.BatchPutValuesBuilder resultValuesBuilder = CustomValueService.ValueRequest.batchPutValues();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        List<FileData> fileDataList = new ArrayList<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        AtomicInteger rowCount = new AtomicInteger(row[0] + 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (T data : dataList) {
 | 
				
			||||||
 | 
					            Map<String, Object> values = GenerateUtil.getFieldValue(data, fieldMap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 计算唯一标识:如果data类型与aClass相同,使用忽略字段逻辑;否则直接从data获取uniqueId
 | 
				
			||||||
 | 
					            String uniqueId;
 | 
				
			||||||
 | 
					            if (data.getClass().equals(aClass)) {
 | 
				
			||||||
 | 
					                // 类型相同,使用忽略字段逻辑计算唯一标识
 | 
				
			||||||
 | 
					                uniqueId = calculateUniqueIdWithIgnoreFields(data, processedIgnoreFields, aClass);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                uniqueId = GenerateUtil.getUniqueId(data);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            AtomicReference<Integer> rowNum = new AtomicReference<>(currTableRowMap.get(uniqueId));
 | 
				
			||||||
 | 
					            if (uniqueId != null && rowNum.get() != null) {
 | 
				
			||||||
 | 
					                rowNum.set(rowNum.get() + 1);
 | 
				
			||||||
 | 
					                values.forEach((field, fieldValue) -> {
 | 
				
			||||||
 | 
					                    if (!tableConf.enableCover() && fieldValue == null) {
 | 
				
			||||||
 | 
					                        return;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    String position = titlePostionMap.get(field);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    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.get());
 | 
				
			||||||
 | 
					                            fileDataList.add(fileData);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    resultValuesBuilder.addRange(sheetId, position + rowNum.get(), position + rowNum.get())
 | 
				
			||||||
 | 
					                            .addRow(GenerateUtil.getRowData(fieldValue));
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            } else if (!ignoreNotFound) {
 | 
				
			||||||
 | 
					                int rowCou = rowCount.incrementAndGet();
 | 
				
			||||||
 | 
					                values.forEach((field, fieldValue) -> {
 | 
				
			||||||
 | 
					                    if (!tableConf.enableCover() && fieldValue == null) {
 | 
				
			||||||
 | 
					                        return;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    String position = titlePostionMap.get(field);
 | 
				
			||||||
 | 
					                    if (fieldValue instanceof FileData) {
 | 
				
			||||||
 | 
					                        FileData fileData = (FileData) fieldValue;
 | 
				
			||||||
 | 
					                        fileData.setSheetId(sheetId);
 | 
				
			||||||
 | 
					                        fileData.setSpreadsheetToken(spreadsheetToken);
 | 
				
			||||||
 | 
					                        fileData.setPosition(position + rowCou);
 | 
				
			||||||
 | 
					                        fileDataList.add(fileData);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    resultValuesBuilder.addRange(sheetId, position + rowCou, position + rowCou)
 | 
				
			||||||
 | 
					                            .addRow(GenerateUtil.getRowData(fieldValue));
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int rowTotal = sheet.getGridProperties().getRowCount();
 | 
				
			||||||
 | 
					        int rowNum = rowCount.get();
 | 
				
			||||||
 | 
					        if (rowNum > rowTotal) {
 | 
				
			||||||
 | 
					            FsApiUtil.addRowColumns(sheetId, spreadsheetToken, "ROWS", rowTotal - rowNum, 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, "【飞书表格】 文件上传-文件上传异常! " + fileData.getFileUrl());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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, resultValuesBuilder.build(), client);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 处理忽略字段名称映射
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * 将实体字段名称映射为表格列名称
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @param fieldsMap 字段映射
 | 
				
			||||||
 | 
					     * @return 处理后的忽略字段列表
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private List<String> processIgnoreFields(Map<String, FieldProperty> fieldsMap) {
 | 
				
			||||||
 | 
					        if (ignoreUniqueFields == null || ignoreUniqueFields.isEmpty()) {
 | 
				
			||||||
 | 
					            return new ArrayList<>();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        List<String> processedFields = new ArrayList<>();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // 遍历字段映射,找到对应的表格列名
 | 
				
			||||||
 | 
					        for (Map.Entry<String, FieldProperty> entry : fieldsMap.entrySet()) {
 | 
				
			||||||
 | 
					            String fieldName = entry.getValue().getField();
 | 
				
			||||||
 | 
					            // 获取字段的最后一部分名称(去掉嵌套路径)
 | 
				
			||||||
 | 
					            String simpleFieldName = fieldName.substring(fieldName.lastIndexOf(".") + 1);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // 如果忽略字段列表中包含此字段名,则添加对应的表格列名
 | 
				
			||||||
 | 
					            if (ignoreUniqueFields.contains(simpleFieldName)) {
 | 
				
			||||||
 | 
					                String tableColumnName = entry.getKey(); // 表格列名(注解中的value值)
 | 
				
			||||||
 | 
					                processedFields.add(tableColumnName);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return processedFields;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 计算考虑忽略字段的唯一标识
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * 根据忽略字段列表计算数据的唯一标识
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @param data 数据对象
 | 
				
			||||||
 | 
					     * @param processedIgnoreFields 处理后的忽略字段列表
 | 
				
			||||||
 | 
					     * @param clazz 用于解析注解的实体类
 | 
				
			||||||
 | 
					     * @return 唯一标识
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String calculateUniqueIdWithIgnoreFields(T data, List<String> processedIgnoreFields, Class<?> clazz) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            // 获取所有字段值
 | 
				
			||||||
 | 
					            Map<String, Object> allFieldValues = GenerateUtil.getFieldValue(data, new HashMap<>());
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // 如果不需要忽略字段,使用原有逻辑
 | 
				
			||||||
 | 
					            if (processedIgnoreFields.isEmpty()) {
 | 
				
			||||||
 | 
					                return GenerateUtil.getUniqueId(data);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // 移除忽略字段后计算唯一标识
 | 
				
			||||||
 | 
					            Map<String, Object> filteredValues = new HashMap<>(allFieldValues);
 | 
				
			||||||
 | 
					            processedIgnoreFields.forEach(filteredValues::remove);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // 将过滤后的值转换为JSON字符串并计算SHA256
 | 
				
			||||||
 | 
					            String jsonStr = StringUtil.mapToJson(filteredValues);
 | 
				
			||||||
 | 
					            return StringUtil.getSHA256(jsonStr);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            // 如果计算失败,回退到原有逻辑
 | 
				
			||||||
 | 
					            return GenerateUtil.getUniqueId(data);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,6 +1,7 @@
 | 
				
			|||||||
package cn.isliu.core.converters;
 | 
					package cn.isliu.core.converters;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public interface OptionsValueProcess<T> {
 | 
					public interface OptionsValueProcess<T, R> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    T process(R r);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    T process();
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -148,6 +148,9 @@ public class FileUtil {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static String getFileName(String path) {
 | 
					    public static String getFileName(String path) {
 | 
				
			||||||
 | 
					        if (path.startsWith("http")) {
 | 
				
			||||||
 | 
					            return path.substring(path.lastIndexOf("/") + 1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        return Paths.get(path).getFileName().toString();
 | 
					        return Paths.get(path).getFileName().toString();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -38,7 +38,7 @@ public class FsApiUtil {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 获取工作表数据
 | 
					     * 获取工作表数据
 | 
				
			||||||
     * 
 | 
					     * <p>
 | 
				
			||||||
     * 从指定的飞书表格中读取指定范围的数据
 | 
					     * 从指定的飞书表格中读取指定范围的数据
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param sheetId          工作表ID
 | 
					     * @param sheetId          工作表ID
 | 
				
			||||||
@ -78,7 +78,7 @@ public class FsApiUtil {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 获取工作表元数据
 | 
					     * 获取工作表元数据
 | 
				
			||||||
     * 
 | 
					     * <p>
 | 
				
			||||||
     * 获取指定工作表的元数据信息,包括行列数、工作表名称等
 | 
					     * 获取指定工作表的元数据信息,包括行列数、工作表名称等
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param sheetId          工作表ID
 | 
					     * @param sheetId          工作表ID
 | 
				
			||||||
@ -157,7 +157,7 @@ public class FsApiUtil {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 获取根目录Token
 | 
					     * 获取根目录Token
 | 
				
			||||||
     *
 | 
					     * <p>
 | 
				
			||||||
     * 调用飞书开放平台API获取当前租户的根目录token,用于后续的文件夹和文件操作
 | 
					     * 调用飞书开放平台API获取当前租户的根目录token,用于后续的文件夹和文件操作
 | 
				
			||||||
     * API接口: GET https://open.feishu.cn/open-apis/drive/v1/files/root_folder/meta
 | 
					     * API接口: GET https://open.feishu.cn/open-apis/drive/v1/files/root_folder/meta
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
 | 
				
			|||||||
@ -38,6 +38,19 @@ public class FsTableUtil {
 | 
				
			|||||||
     * @return 飞书表格数据列表
 | 
					     * @return 飞书表格数据列表
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static List<FsTableData> getFsTableData(Sheet sheet, String spreadsheetToken, TableConf tableConf) {
 | 
					    public static List<FsTableData> getFsTableData(Sheet sheet, String spreadsheetToken, TableConf tableConf) {
 | 
				
			||||||
 | 
					        return getFsTableData(sheet, spreadsheetToken, tableConf, new ArrayList<>());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 获取飞书表格数据(支持忽略唯一字段)
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param sheet 工作表对象
 | 
				
			||||||
 | 
					     * @param spreadsheetToken 电子表格Token
 | 
				
			||||||
 | 
					     * @param tableConf 表格配置
 | 
				
			||||||
 | 
					     * @param ignoreUniqueFields 计算唯一标识时忽略的字段列表
 | 
				
			||||||
 | 
					     * @return 飞书表格数据列表
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static List<FsTableData> getFsTableData(Sheet sheet, String spreadsheetToken, TableConf tableConf, List<String> ignoreUniqueFields) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 计算数据范围
 | 
					        // 计算数据范围
 | 
				
			||||||
        GridProperties gridProperties = sheet.getGridProperties();
 | 
					        GridProperties gridProperties = sheet.getGridProperties();
 | 
				
			||||||
@ -70,7 +83,7 @@ public class FsTableUtil {
 | 
				
			|||||||
        // 获取飞书表格数据
 | 
					        // 获取飞书表格数据
 | 
				
			||||||
        TableData tableData = processSheetData(sheet, values);
 | 
					        TableData tableData = processSheetData(sheet, values);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        List<FsTableData> dataList = getFsTableData(tableData);
 | 
					        List<FsTableData> dataList = getFsTableData(tableData, ignoreUniqueFields);
 | 
				
			||||||
        Map<String, String> titleMap = new HashMap<>();
 | 
					        Map<String, String> titleMap = new HashMap<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        dataList.stream().filter(d -> d.getRow() == (tableConf.titleRow() - 1)).findFirst()
 | 
					        dataList.stream().filter(d -> d.getRow() == (tableConf.titleRow() - 1)).findFirst()
 | 
				
			||||||
@ -293,7 +306,9 @@ public class FsTableUtil {
 | 
				
			|||||||
        return resultMap;
 | 
					        return resultMap;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static void setTableOptions(String spreadsheetToken, List<String> headers, Map<String, FieldProperty> fieldsMap, String sheetId, boolean enableDesc) {
 | 
					    public static void setTableOptions(String spreadsheetToken, List<String> headers, Map<String, FieldProperty> fieldsMap,
 | 
				
			||||||
 | 
					                                       String sheetId, boolean enableDesc, Map<String, Object> customProperties) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        List<Object> list = Arrays.asList(headers.toArray());
 | 
					        List<Object> list = Arrays.asList(headers.toArray());
 | 
				
			||||||
        int line = getMaxLevel(fieldsMap) + (enableDesc ? 2 : 1);
 | 
					        int line = getMaxLevel(fieldsMap) + (enableDesc ? 2 : 1);
 | 
				
			||||||
        fieldsMap.forEach((field, fieldProperty) -> {
 | 
					        fieldsMap.forEach((field, fieldProperty) -> {
 | 
				
			||||||
@ -316,21 +331,43 @@ public class FsTableUtil {
 | 
				
			|||||||
                    List<String> result;
 | 
					                    List<String> result;
 | 
				
			||||||
                    Class<? extends OptionsValueProcess> optionsClass = tableProperty.optionsClass();
 | 
					                    Class<? extends OptionsValueProcess> optionsClass = tableProperty.optionsClass();
 | 
				
			||||||
                    try {
 | 
					                    try {
 | 
				
			||||||
 | 
					                        Map<String, Object> properties = new HashMap<>();
 | 
				
			||||||
 | 
					                        if (customProperties == null) {
 | 
				
			||||||
 | 
					                            properties.put("_field", fieldProperty);
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            customProperties.put("_field", fieldProperty);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                        OptionsValueProcess optionsValueProcess = optionsClass.getDeclaredConstructor().newInstance();
 | 
					                        OptionsValueProcess optionsValueProcess = optionsClass.getDeclaredConstructor().newInstance();
 | 
				
			||||||
                        result = (List<String>) optionsValueProcess.process();
 | 
					                        result = (List<String>) optionsValueProcess.process(customProperties == null ? properties : customProperties);
 | 
				
			||||||
                    } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
 | 
					                    } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
 | 
				
			||||||
                        throw new RuntimeException(e);
 | 
					                        throw new RuntimeException(e);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (result != null && !result.isEmpty()) {
 | 
				
			||||||
                        FsApiUtil.setOptions(sheetId, FsClient.getInstance().getClient(), spreadsheetToken, tableProperty.type() == TypeEnum.MULTI_SELECT, position + line, position + 200,
 | 
					                        FsApiUtil.setOptions(sheetId, FsClient.getInstance().getClient(), spreadsheetToken, tableProperty.type() == TypeEnum.MULTI_SELECT, position + line, position + 200,
 | 
				
			||||||
                                result);
 | 
					                                result);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static void setTableOptions(String spreadsheetToken, List<String> headers, Map<String, FieldProperty> fieldsMap, String sheetId, boolean enableDesc) {
 | 
				
			||||||
 | 
					        setTableOptions(spreadsheetToken, headers, fieldsMap, sheetId, enableDesc, null);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static CustomValueService.ValueRequest getHeadTemplateBuilder(String sheetId, List<String> headers,
 | 
					    public static CustomValueService.ValueRequest getHeadTemplateBuilder(String sheetId, List<String> headers,
 | 
				
			||||||
                                                                         Map<String, FieldProperty> fieldsMap, TableConf tableConf) {
 | 
					                                                                         Map<String, FieldProperty> fieldsMap, TableConf tableConf) {
 | 
				
			||||||
 | 
					        return getHeadTemplateBuilder(sheetId, headers, fieldsMap, null, tableConf);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static CustomValueService.ValueRequest getHeadTemplateBuilder(String sheetId, List<String> headers,
 | 
				
			||||||
 | 
					                                                                         Map<String, FieldProperty> fieldsMap, List<String> includeFields, TableConf tableConf) {
 | 
				
			||||||
 | 
					        return getHeadTemplateBuilder(sheetId, headers, fieldsMap, includeFields, tableConf, null);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static CustomValueService.ValueRequest getHeadTemplateBuilder(String sheetId, List<String> headers,
 | 
				
			||||||
 | 
					                                                                         Map<String, FieldProperty> fieldsMap, List<String> includeFields, TableConf tableConf, Map<String, String> fieldDescriptions) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        String position = FsTableUtil.getColumnNameByNuNumber(headers.size());
 | 
					        String position = FsTableUtil.getColumnNameByNuNumber(headers.size());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -342,13 +379,18 @@ public class FsTableUtil {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        if (maxLevel == 1) {
 | 
					        if (maxLevel == 1) {
 | 
				
			||||||
            // 单层级表头:按order排序的headers
 | 
					            // 单层级表头:按order排序的headers
 | 
				
			||||||
            List<String> sortedHeaders = getSortedHeaders(fieldsMap);
 | 
					            List<String> sortedHeaders;
 | 
				
			||||||
 | 
					            if (includeFields != null && !includeFields.isEmpty()) {
 | 
				
			||||||
 | 
					                sortedHeaders = includeFields.stream().sorted(Comparator.comparingInt(headers::indexOf)).collect(Collectors.toList());
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                sortedHeaders = getSortedHeaders(fieldsMap);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            int titleRow = tableConf.titleRow();
 | 
					            int titleRow = tableConf.titleRow();
 | 
				
			||||||
            if (tableConf.enableDesc()) {
 | 
					            if (tableConf.enableDesc()) {
 | 
				
			||||||
                int descRow = titleRow + 1;
 | 
					                int descRow = titleRow + 1;
 | 
				
			||||||
                batchPutValuesBuilder.addRange(sheetId + "!A" + titleRow + ":" + position + descRow);
 | 
					                batchPutValuesBuilder.addRange(sheetId + "!A" + titleRow + ":" + position + descRow);
 | 
				
			||||||
                batchPutValuesBuilder.addRow(sortedHeaders.toArray());
 | 
					                batchPutValuesBuilder.addRow(sortedHeaders.toArray());
 | 
				
			||||||
                batchPutValuesBuilder.addRow(getDescArray(sortedHeaders, fieldsMap));
 | 
					                batchPutValuesBuilder.addRow(getDescArray(sortedHeaders, fieldsMap, fieldDescriptions));
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                batchPutValuesBuilder.addRange(sheetId + "!A" + titleRow + ":" + position + titleRow);
 | 
					                batchPutValuesBuilder.addRange(sheetId + "!A" + titleRow + ":" + position + titleRow);
 | 
				
			||||||
                batchPutValuesBuilder.addRow(sortedHeaders.toArray());
 | 
					                batchPutValuesBuilder.addRow(sortedHeaders.toArray());
 | 
				
			||||||
@ -379,10 +421,15 @@ public class FsTableUtil {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            // 如果启用了描述,在最后一行添加描述
 | 
					            // 如果启用了描述,在最后一行添加描述
 | 
				
			||||||
            if (tableConf.enableDesc()) {
 | 
					            if (tableConf.enableDesc()) {
 | 
				
			||||||
                List<String> finalHeaders = getSortedHeaders(fieldsMap);
 | 
					                List<String> finalHeaders;
 | 
				
			||||||
 | 
					                if (includeFields != null && !includeFields.isEmpty()) {
 | 
				
			||||||
 | 
					                    finalHeaders = includeFields.stream().sorted(Comparator.comparingInt(headers::indexOf)).collect(Collectors.toList());
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    finalHeaders = getSortedHeaders(fieldsMap);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                int descRow = maxLevel + 1;
 | 
					                int descRow = maxLevel + 1;
 | 
				
			||||||
                batchPutValuesBuilder.addRange(sheetId + "!A" + descRow + ":" + position + descRow);
 | 
					                batchPutValuesBuilder.addRange(sheetId + "!A" + descRow + ":" + position + descRow);
 | 
				
			||||||
                batchPutValuesBuilder.addRow(getDescArray(finalHeaders, fieldsMap));
 | 
					                batchPutValuesBuilder.addRow(getDescArray(finalHeaders, fieldsMap, fieldDescriptions));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -416,12 +463,30 @@ public class FsTableUtil {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static Object[] getDescArray(List<String> headers, Map<String, FieldProperty> fieldsMap) {
 | 
					    private static Object[] getDescArray(List<String> headers, Map<String, FieldProperty> fieldsMap) {
 | 
				
			||||||
 | 
					        return getDescArray(headers, fieldsMap, null);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static Object[] getDescArray(List<String> headers, Map<String, FieldProperty> fieldsMap, Map<String, String> fieldDescriptions) {
 | 
				
			||||||
        Object[] descArray = new String[headers.size()];
 | 
					        Object[] descArray = new String[headers.size()];
 | 
				
			||||||
        for (int i = 0; i < headers.size(); i++) {
 | 
					        for (int i = 0; i < headers.size(); i++) {
 | 
				
			||||||
            String header = headers.get(i);
 | 
					            String header = headers.get(i);
 | 
				
			||||||
            FieldProperty fieldProperty = fieldsMap.get(header);
 | 
					            FieldProperty fieldProperty = fieldsMap.get(header);
 | 
				
			||||||
            if (fieldProperty != null && fieldProperty.getTableProperty() != null) {
 | 
					            if (fieldProperty != null && fieldProperty.getTableProperty() != null) {
 | 
				
			||||||
                String desc = fieldProperty.getTableProperty().desc();
 | 
					                String desc = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // 优先从字段描述映射中获取描述
 | 
				
			||||||
 | 
					                if (fieldDescriptions != null && !fieldDescriptions.isEmpty()) {
 | 
				
			||||||
 | 
					                    // 从字段路径中提取字段名(最后一个.后面的部分)
 | 
				
			||||||
 | 
					                    String fieldPath = fieldProperty.getField();
 | 
				
			||||||
 | 
					                    String fieldName = fieldPath.substring(fieldPath.lastIndexOf(".") + 1);
 | 
				
			||||||
 | 
					                    desc = fieldDescriptions.get(fieldName);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // 如果映射中没有找到,则从注解中获取
 | 
				
			||||||
 | 
					                if (desc == null || desc.isEmpty()) {
 | 
				
			||||||
 | 
					                    desc = fieldProperty.getTableProperty().desc();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (desc != null && !desc.isEmpty()) {
 | 
					                if (desc != null && !desc.isEmpty()) {
 | 
				
			||||||
                    try {
 | 
					                    try {
 | 
				
			||||||
                        JsonElement element = JsonParser.parseString(desc);
 | 
					                        JsonElement element = JsonParser.parseString(desc);
 | 
				
			||||||
@ -681,6 +746,10 @@ public class FsTableUtil {
 | 
				
			|||||||
        return mergeRequests;
 | 
					        return mergeRequests;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static TableConf getTableConf(Class<?> zClass) {
 | 
				
			||||||
 | 
					        return zClass.getAnnotation(TableConf.class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 分组信息类,用于辅助排序
 | 
					     * 分组信息类,用于辅助排序
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 | 
				
			|||||||
@ -24,7 +24,6 @@ import java.util.stream.Collectors;
 | 
				
			|||||||
public class GenerateUtil {
 | 
					public class GenerateUtil {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 使用统一的FsLogger替代java.util.logging.Logger
 | 
					    // 使用统一的FsLogger替代java.util.logging.Logger
 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 根据配置和数据生成DTO对象(通用版本)
 | 
					     * 根据配置和数据生成DTO对象(通用版本)
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
@ -49,7 +48,7 @@ public class GenerateUtil {
 | 
				
			|||||||
                try {
 | 
					                try {
 | 
				
			||||||
                    setNestedField(t, fieldPath, value);
 | 
					                    setNestedField(t, fieldPath, value);
 | 
				
			||||||
                } catch (Exception e) {
 | 
					                } catch (Exception e) {
 | 
				
			||||||
                    FsLogger.error(ErrorCode.DATA_CONVERSION_ERROR, "【巨量广告助手】 获取字段值异常!参数:" + fieldPath + ",异常:" + e.getMessage(), "generateList", e);
 | 
					                    FsLogger.error(ErrorCode.DATA_CONVERSION_ERROR, "【飞书助手】 获取字段值异常!参数:" + fieldPath + ",异常:" + e.getMessage(), "generateList", e);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
@ -179,11 +178,15 @@ public class GenerateUtil {
 | 
				
			|||||||
        Class<?> fieldType = field.getType();
 | 
					        Class<?> fieldType = field.getType();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 简单类型转换
 | 
					        // 简单类型转换
 | 
				
			||||||
        if (value != null) {
 | 
					        if (value != null && value != "") {
 | 
				
			||||||
            if (fieldType == String.class) {
 | 
					            if (fieldType == String.class) {
 | 
				
			||||||
                field.set(target, convertStrValue(value));
 | 
					                field.set(target, convertStrValue(value));
 | 
				
			||||||
            } else if (fieldType == Integer.class || fieldType == int.class) {
 | 
					            } else if (fieldType == Integer.class || fieldType == int.class) {
 | 
				
			||||||
                field.set(target, Integer.parseInt(convertValue(value)));
 | 
					                String val = convertValue(value);
 | 
				
			||||||
 | 
					                BigDecimal bd = new BigDecimal(val);
 | 
				
			||||||
 | 
					                bd = bd.setScale(0, BigDecimal.ROUND_DOWN);
 | 
				
			||||||
 | 
					                String result = bd.toPlainString();
 | 
				
			||||||
 | 
					                field.set(target, Integer.parseInt(result));
 | 
				
			||||||
            } else if (fieldType == Double.class || fieldType == double.class) {
 | 
					            } else if (fieldType == Double.class || fieldType == double.class) {
 | 
				
			||||||
                field.set(target, Double.parseDouble(convertValue(value)));
 | 
					                field.set(target, Double.parseDouble(convertValue(value)));
 | 
				
			||||||
            } else if (fieldType == Boolean.class || fieldType == boolean.class) {
 | 
					            } else if (fieldType == Boolean.class || fieldType == boolean.class) {
 | 
				
			||||||
@ -456,7 +459,7 @@ public class GenerateUtil {
 | 
				
			|||||||
        return fieldValue;
 | 
					        return fieldValue;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static <T> @Nullable String getUniqueId(T data) {
 | 
					    public static <T> String getUniqueId(T data) {
 | 
				
			||||||
        String uniqueId = null;
 | 
					        String uniqueId = null;
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            Object uniqueIdObj = GenerateUtil.getNestedFieldValue(data, "uniqueId");
 | 
					            Object uniqueIdObj = GenerateUtil.getNestedFieldValue(data, "uniqueId");
 | 
				
			||||||
 | 
				
			|||||||
@ -56,6 +56,28 @@ public class StringUtil {
 | 
				
			|||||||
        return uniqueId;
 | 
					        return uniqueId;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 驼峰转下划线命名
 | 
				
			||||||
 | 
					     * @param camelCaseName 驼峰命名的字符串
 | 
				
			||||||
 | 
					     * @return 下划线命名的字符串
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static String toUnderscoreCase(String camelCaseName) {
 | 
				
			||||||
 | 
					        if (camelCaseName == null || camelCaseName.isEmpty()) {
 | 
				
			||||||
 | 
					            return camelCaseName;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        StringBuilder result = new StringBuilder();
 | 
				
			||||||
 | 
					        for (char c : camelCaseName.toCharArray()) {
 | 
				
			||||||
 | 
					            if (Character.isUpperCase(c)) {
 | 
				
			||||||
 | 
					                result.append('_').append(Character.toLowerCase(c));
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                result.append(c);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return result.toString();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 下划线转驼峰命名
 | 
					     * 下划线转驼峰命名
 | 
				
			||||||
     * @param underscoreName 下划线命名的字符串
 | 
					     * @param underscoreName 下划线命名的字符串
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user