证书数据 Excel 读取与 SQL 生成工具需求文档
证书数据 Excel 读取与 SQL 生成工具需求文档
一、功能概述
开发一个 Java 工具类,用于从 Excel 文件中读取资质证书数据,自动生成数据库操作 SQL 语句和导出 Excel 文件。
二、核心功能需求
- Excel 数据读取
多 Sheet 支持:自动识别并读取 Excel 所有 Sheet 页
按列解析:每列第 1 个非空数据作为父证书,同列下方数据作为子证书
层级结构:构建树形证书结构(Sheet 名 → 父证书 → 子证书)
数据限制:最大读取 100 行×100 列,连续 10 行/列为空则停止 - INSERT 语句生成
主键规则:拼音名称 + 260402(如:renyuanzigezhengshu260402)
生成范围:仅为每个 Sheet 页的父证书(首行证书)生成 INSERT 语句
表结构:base_certificate
字段映射:
certificate_id:主键(拼音名 +260402)
certificate_cn_name:证书中文名称
parent_id:父级证书 ID(有父级时填写,否则 NULL)
certificate_type:证书类型(固定为 2026002000001)
have_parent_flag:是否有父级(Y/N) - UPDATE 语句生成
用途:将子证书名称统一更新为父证书名称
目标表:RFDEV.TB_BASE_USER_QUALIFY
生成规则:每个父证书对应一条 UPDATE 语句
示例:
点击查看代码
UPDATE RFDEV.TB_BASE_USER_QUALIFY
SET QUALIFY_NAME = '注册计量师证书(二级)'
WHERE QUALIFY_NAME IN ('注册计量师证', '注册计量证', '二级注册计量师证书');
- IN 子句生成
用途:用于数据库查询验证证书关联情况
范围:包含所有 Sheet 页的所有父证书名称
格式:IN ('证书 1', '证书 2', '证书 3') - Excel 导出
文件名:output_certificates_时间戳.xlsx
包含内容:所有 Sheet 页的父证书数据(不包含子证书)
字段顺序:
Sheet 名称
层级(1=父证书)
排序号(从 1 开始递增)
证书 ID(拼音名 +260402)
证书名称
父级 ID
是否有父级
三、技术实现要求 - 依赖库
EasyExcel(3.3.2):Excel 读写
pinyin4j(2.5.1):中文转拼音
Lombok:简化代码
Apache POI:获取 Sheet 信息 - 关键算法
拼音转换:使用 pinyin4j 将中文名称转为小写无音调拼音
树形构建:按列遍历,首行为父,其余为子
去重处理:同一列中相同名称的子证书只保留一次 - 输出格式
控制台输出:
INSERT 语句(可注释掉不显示)
IN 子句
UPDATE 语句
文件输出:Excel 文件(项目根目录)
四、业务规则 - 证书层级关系
一级证书:每列第 1 个非空数据(父证书)
二级证书:同列第 1 个非空数据下方的数据(子证书)
三级及以上:暂不支持 - 数据处理规则
空值处理:跳过空单元格
重复数据处理:同列中相同名称的子证书只保留一次
Sheet 页命名:使用 Excel 原始 Sheet 名称作为根节点 - SQL 生成规则
先父后子:INSERT 语句先生成父证书,再生成子证书
外键关联:子证书的 parent_id 指向父证书的 certificate_id
SQL 转义:自动处理名称中的单引号等特殊字符
五、使用场景
场景 1:批量导入证书数据
使用生成的 INSERT 语句,将 Excel 中的证书分类批量导入数据库。
场景 2:数据标准化
使用 UPDATE 语句,将历史遗留的不规范证书名称统一为标准名称。
场景 3:数据验证
使用 IN 子句在数据库中查询已关联的证书,验证数据完整性。
场景 4:数据备份
导出的 Excel 文件可作为数据备份或迁移的中间文件格式。
点击查看代码
package org.dupl.excel;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.write.metadata.WriteSheet;
import lombok.Data;
import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType;
import org.dupl.entry.BaseCertificate;
import java.util.*;
/**
* Excel 证书数据读取工具
* 按列解析:首行为父级,同列其他行为子级
* 支持多 Sheet 页,Sheet 页名作为根节点
* 固定读取最多 100 行×100 列,连续 10 行/10 列无数据则结束
*
* @author system
* @date 2026-04-02
*/
public class CertificateExcelReader {
// 最大行列数
private static final int MAX_ROWS = 100;
private static final int MAX_COLS = 100;
// 连续空行/空列阈值
private static final int EMPTY_THRESHOLD = 10;
// 数据库表名
private static final String TABLE_NAME = "base_certificate";
// 主键后缀
private static final String ID_SUFFIX = "260402";
/**
* 读取 Excel 文件并构建树形证书结构
*
* @param filePath Excel 文件路径
* @return 证书树形列表(每个 Sheet 一个根节点)
*/
public List<BaseCertificate> readCertificateTree(String filePath) {
List<BaseCertificate> allSheetsCertificates = new ArrayList<>();
// 获取所有 sheet 名称
List<String> sheetNames = new ArrayList<>();
try {
org.apache.poi.ss.usermodel.Workbook workbook = org.apache.poi.ss.usermodel.WorkbookFactory.create(
new java.io.File(filePath)
);
int numberOfSheets = workbook.getNumberOfSheets();
for (int i = 0; i < numberOfSheets; i++) {
sheetNames.add(workbook.getSheetName(i));
}
workbook.close();
} catch (Exception e) {
System.err.println("获取 Sheet 名称失败:" + e.getMessage());
// 如果获取失败,至少读取第一个 sheet
sheetNames.add("Sheet1");
}
System.out.println("\n========== Excel 信息 ==========");
System.out.println("共发现 " + sheetNames.size() + " 个 Sheet 页:");
for (int i = 0; i < sheetNames.size(); i++) {
System.out.println(" " + (i + 1) + ". " + sheetNames.get(i));
}
System.out.println("================================\n");
// 遍历每个 Sheet
for (int i = 0; i < sheetNames.size(); i++) {
System.out.println("\n========== 处理第 " + (i + 1) + " 个 Sheet: " + sheetNames.get(i) + " ==========");
CertificateDataListener listener = new CertificateDataListener(sheetNames.get(i));
EasyExcel.read(filePath, CertificateRowData.class, listener)
.sheet(i) // 按索引读取 sheet
.headRowNumber(0) // 不跳过表头,从第 1 行开始读
.doRead();
List<BaseCertificate> sheetCertificates = listener.getCertificateTree();
// 如果该 sheet 有证书数据,创建一个以 sheet 名为根的节点
if (!sheetCertificates.isEmpty()) {
BaseCertificate sheetRoot = new BaseCertificate();
sheetRoot.setCertificateId("SHEET_" + i);
sheetRoot.setCertificateCnName(sheetNames.get(i));
sheetRoot.setCertificateType("ROOT");
sheetRoot.setDeleteFlag("0");
sheetRoot.setHaveParentFlag("N");
sheetRoot.setChildren(sheetCertificates);
allSheetsCertificates.add(sheetRoot);
System.out.println("Sheet '" + sheetNames.get(i) + "' 处理完成,共 " + sheetCertificates.size() + " 个证书分类");
}
}
return allSheetsCertificates;
}
/**
* Excel 行数据类 - 固定 100 列
*/
@Data
public static class CertificateRowData {
private String col0;
private String col1;
private String col2;
private String col3;
private String col4;
private String col5;
private String col6;
private String col7;
private String col8;
private String col9;
private String col10;
private String col11;
private String col12;
private String col13;
private String col14;
private String col15;
private String col16;
private String col17;
private String col18;
private String col19;
private String col20;
private String col21;
private String col22;
private String col23;
private String col24;
private String col25;
private String col26;
private String col27;
private String col28;
private String col29;
private String col30;
private String col31;
private String col32;
private String col33;
private String col34;
private String col35;
private String col36;
private String col37;
private String col38;
private String col39;
private String col40;
private String col41;
private String col42;
private String col43;
private String col44;
private String col45;
private String col46;
private String col47;
private String col48;
private String col49;
private String col50;
private String col51;
private String col52;
private String col53;
private String col54;
private String col55;
private String col56;
private String col57;
private String col58;
private String col59;
private String col60;
private String col61;
private String col62;
private String col63;
private String col64;
private String col65;
private String col66;
private String col67;
private String col68;
private String col69;
private String col70;
private String col71;
private String col72;
private String col73;
private String col74;
private String col75;
private String col76;
private String col77;
private String col78;
private String col79;
private String col80;
private String col81;
private String col82;
private String col83;
private String col84;
private String col85;
private String col86;
private String col87;
private String col88;
private String col89;
private String col90;
private String col91;
private String col92;
private String col93;
private String col94;
private String col95;
private String col96;
private String col97;
private String col98;
private String col99;
}
/**
* 监听器类 - 处理按列读取的数据
*/
private static class CertificateDataListener implements ReadListener<CertificateRowData> {
private String sheetName; // Sheet 名称
// 二维数组存储数据 [行][列]
private String[][] dataGrid = new String[MAX_ROWS][MAX_COLS];
private int maxRowUsed = 0;
// 最终生成的证书树
private List<BaseCertificate> certificateTree = new ArrayList<>();
public CertificateDataListener(String sheetName) {
this.sheetName = sheetName;
}
@Override
public void invoke(CertificateRowData data, AnalysisContext context) {
int rowNum = context.readRowHolder().getRowIndex();
if (rowNum >= MAX_ROWS) {
return; // 超过最大行数
}
// 提取每一列的数据
for (int col = 0; col < MAX_COLS; col++) {
String value = getCellValue(data, col);
dataGrid[rowNum][col] = value;
}
maxRowUsed = Math.max(maxRowUsed, rowNum);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 构建证书树
certificateTree = buildTree();
System.out.println("证书树构建完成,共 " + certificateTree.size() + " 个根证书");
}
/**
* 获取单元格值
*/
private String getCellValue(CertificateRowData data, int colIndex) {
switch (colIndex) {
case 0: return data.getCol0();
case 1: return data.getCol1();
case 2: return data.getCol2();
case 3: return data.getCol3();
case 4: return data.getCol4();
case 5: return data.getCol5();
case 6: return data.getCol6();
case 7: return data.getCol7();
case 8: return data.getCol8();
case 9: return data.getCol9();
case 10: return data.getCol10();
case 11: return data.getCol11();
case 12: return data.getCol12();
case 13: return data.getCol13();
case 14: return data.getCol14();
case 15: return data.getCol15();
case 16: return data.getCol16();
case 17: return data.getCol17();
case 18: return data.getCol18();
case 19: return data.getCol19();
case 20: return data.getCol20();
case 21: return data.getCol21();
case 22: return data.getCol22();
case 23: return data.getCol23();
case 24: return data.getCol24();
case 25: return data.getCol25();
case 26: return data.getCol26();
case 27: return data.getCol27();
case 28: return data.getCol28();
case 29: return data.getCol29();
case 30: return data.getCol30();
case 31: return data.getCol31();
case 32: return data.getCol32();
case 33: return data.getCol33();
case 34: return data.getCol34();
case 35: return data.getCol35();
case 36: return data.getCol36();
case 37: return data.getCol37();
case 38: return data.getCol38();
case 39: return data.getCol39();
case 40: return data.getCol40();
case 41: return data.getCol41();
case 42: return data.getCol42();
case 43: return data.getCol43();
case 44: return data.getCol44();
case 45: return data.getCol45();
case 46: return data.getCol46();
case 47: return data.getCol47();
case 48: return data.getCol48();
case 49: return data.getCol49();
case 50: return data.getCol50();
case 51: return data.getCol51();
case 52: return data.getCol52();
case 53: return data.getCol53();
case 54: return data.getCol54();
case 55: return data.getCol55();
case 56: return data.getCol56();
case 57: return data.getCol57();
case 58: return data.getCol58();
case 59: return data.getCol59();
case 60: return data.getCol60();
case 61: return data.getCol61();
case 62: return data.getCol62();
case 63: return data.getCol63();
case 64: return data.getCol64();
case 65: return data.getCol65();
case 66: return data.getCol66();
case 67: return data.getCol67();
case 68: return data.getCol68();
case 69: return data.getCol69();
case 70: return data.getCol70();
case 71: return data.getCol71();
case 72: return data.getCol72();
case 73: return data.getCol73();
case 74: return data.getCol74();
case 75: return data.getCol75();
case 76: return data.getCol76();
case 77: return data.getCol77();
case 78: return data.getCol78();
case 79: return data.getCol79();
case 80: return data.getCol80();
case 81: return data.getCol81();
case 82: return data.getCol82();
case 83: return data.getCol83();
case 84: return data.getCol84();
case 85: return data.getCol85();
case 86: return data.getCol86();
case 87: return data.getCol87();
case 88: return data.getCol88();
case 89: return data.getCol89();
case 90: return data.getCol90();
case 91: return data.getCol91();
case 92: return data.getCol92();
case 93: return data.getCol93();
case 94: return data.getCol94();
case 95: return data.getCol95();
case 96: return data.getCol96();
case 97: return data.getCol97();
case 98: return data.getCol98();
case 99: return data.getCol99();
default: return null;
}
}
/**
* 构建树形结构
* 每一列的第 1 个数据是父级,其他数据是子级
*/
private List<BaseCertificate> buildTree() {
List<BaseCertificate> tree = new ArrayList<>();
int certificateIdCounter = 1;
// 遍历每一列
int emptyCols = 0; // 连续空列计数
for (int col = 0; col < MAX_COLS && emptyCols < EMPTY_THRESHOLD; col++) {
List<String> columnValues = new ArrayList<>();
// 收集该列的数据(最多 MAX_ROWS 行)
// 注意:不跳过空值,保留原始位置
for (int row = 0; row <= maxRowUsed; row++) {
String value = dataGrid[row][col];
columnValues.add(value); // 保留空值占位
}
// 检查该列是否全为空
boolean allEmpty = true;
for (String val : columnValues) {
if (val != null && !val.trim().isEmpty()) {
allEmpty = false;
break;
}
}
if (allEmpty) {
emptyCols++;
continue;
} else {
emptyCols = 0; // 重置空列计数
}
// 第一个非空数据作为父级证书
String parentValue = null;
int parentRowIndex = -1;
for (int i = 0; i < columnValues.size(); i++) {
if (columnValues.get(i) != null && !columnValues.get(i).trim().isEmpty()) {
parentValue = columnValues.get(i).trim();
parentRowIndex = i;
break;
}
}
if (parentValue == null) {
continue;
}
BaseCertificate parentCert = new BaseCertificate();
parentCert.setCertificateId("CERT_" + (certificateIdCounter++));
parentCert.setCertificateCnName(parentValue);
parentCert.setCertificateType("2026002000001"); // 人员资质证书
parentCert.setDeleteFlag("0");
parentCert.setHaveParentFlag("N");
parentCert.setChildren(new ArrayList<>());
// 收集该列其他非空数据作为子证书
Set<String> addedChildren = new HashSet<>(); // 去重
for (int i = parentRowIndex + 1; i < columnValues.size(); i++) {
String childValue = columnValues.get(i);
if (childValue != null && !childValue.trim().isEmpty() && !addedChildren.contains(childValue.trim())) {
BaseCertificate childCert = new BaseCertificate();
childCert.setCertificateId("CERT_" + (certificateIdCounter++));
childCert.setCertificateCnName(childValue.trim());
childCert.setParentId(parentCert.getCertificateId());
childCert.setCertificateType("2026002000001");
childCert.setDeleteFlag("0");
childCert.setHaveParentFlag("Y");
parentCert.getChildren().add(childCert);
addedChildren.add(childValue.trim());
}
}
tree.add(parentCert);
}
// 打印证书树
printTree(tree);
return tree;
}
/**
* 打印证书树(精简版)
*/
private void printTree(List<BaseCertificate> tree) {
System.out.println("\n【" + sheetName + "】共 " + tree.size() + " 个证书分类:");
for (BaseCertificate parent : tree) {
System.out.println(" ├─ " + parent.getCertificateCnName() + " (" + parent.getChildren().size() + " 个子证书)");
if (parent.getChildren() != null && !parent.getChildren().isEmpty()) {
for (BaseCertificate child : parent.getChildren()) {
System.out.println(" │ └─ " + child.getCertificateCnName());
}
}
}
}
public List<BaseCertificate> getCertificateTree() {
return certificateTree;
}
}
/**
* 主方法 - 测试
*/
public static void main(String[] args) {
String filePath = "src/main/resources/人员资质(4).xlsx";
CertificateExcelReader reader = new CertificateExcelReader();
List<BaseCertificate> certificates = reader.readCertificateTree(filePath);
System.out.println("\n\n========== 最终汇总 ==========");
System.out.println("读取完成!共 " + certificates.size() + " 个 Sheet 页");
// 打印汇总信息
for (BaseCertificate sheetCert : certificates) {
System.out.println("\n📋 " + sheetCert.getCertificateCnName() + " (共 " + sheetCert.getChildren().size() + " 个证书分类)");
}
System.out.println("\n====================================\n");
// 生成 INSERT 语句
System.out.println("\n========== INSERT 语句 ==========");
String insertSql = generateInsertStatements(certificates);
// System.out.println(insertSql);
System.out.println("\n====================================\n");
// 生成 IN 子句
System.out.println("\n========== IN 子句 ==========");
String inClause = generateInClause(certificates);
System.out.println(inClause);
System.out.println("\n====================================\n");
// 生成 UPDATE 语句
System.out.println("\n========== UPDATE 语句 ==========");
String updateSql = generateUpdateStatements(certificates);
System.out.println(updateSql);
System.out.println("\n====================================\n");
// 输出 Excel 文件
String outputFilePath = "output_certificates_" + System.currentTimeMillis() + ".xlsx";
exportToExcel(certificates, outputFilePath);
System.out.println("Excel 文件已生成:" + outputFilePath);
// 可以进一步处理,比如保存到数据库
// saveToDatabase(certificates);
}
/**
* 将中文转换为拼音(小写,无音调)
*/
private static String toPinyin(String chinese) {
if (chinese == null || chinese.trim().isEmpty()) {
return "";
}
HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
format.setCaseType(HanyuPinyinCaseType.LOWERCASE);
format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
format.setVCharType(HanyuPinyinVCharType.WITH_V);
StringBuilder pinyin = new StringBuilder();
char[] chars = chinese.trim().toCharArray();
try {
for (char c : chars) {
if (c >= 0x4e00 && c <= 0x9fa5) { // 中文字符
String[] pinyins = PinyinHelper.toHanyuPinyinStringArray(c, format);
if (pinyins != null && pinyins.length > 0) {
pinyin.append(pinyins[0]);
} else {
pinyin.append(c);
}
} else if (Character.isLetterOrDigit(c)) { // 字母数字保留
pinyin.append(c);
}
// 其他字符(空格、标点等)跳过
}
} catch (Exception e) {
System.err.println("拼音转换失败:" + e.getMessage());
return chinese.replaceAll("[^a-zA-Z0-9]", "").toLowerCase();
}
return pinyin.toString();
}
/**
* 生成主键 ID:拼音 + 260402
*/
private static String generateId(String name) {
String pinyin = toPinyin(name);
// 限制长度,避免过长
if (pinyin.length() > 50) {
pinyin = pinyin.substring(0, 50);
}
return pinyin + ID_SUFFIX;
}
/**
* 生成 INSERT 语句(只生成父证书,不生成子证书)
*/
private static String generateInsertStatements(List<BaseCertificate> certificates) {
StringBuilder sqlBuilder = new StringBuilder();
// 用于存储证书名称到 ID 的映射
Map<String, String> certNameToIdMap = new HashMap<>();
for (BaseCertificate sheetRoot : certificates) {
// 为该 Sheet 下的所有证书生成 INSERT(只收集父证书)
if (sheetRoot.getChildren() != null) {
for (BaseCertificate cert : sheetRoot.getChildren()) {
collectParentCertificates(cert, certNameToIdMap);
}
}
}
// 生成 INSERT 语句(只生成父证书)
for (BaseCertificate sheetRoot : certificates) {
if (sheetRoot.getChildren() != null) {
for (BaseCertificate cert : sheetRoot.getChildren()) {
sqlBuilder.append(generateInsertStatement(cert, certNameToIdMap)).append("\n");
}
}
}
return sqlBuilder.toString();
}
/**
* 递归收集所有证书及其 ID 映射(只收集父级证书)
*/
private static void collectParentCertificates(BaseCertificate cert, Map<String, String> certNameToIdMap) {
String id = generateId(cert.getCertificateCnName());
certNameToIdMap.put(cert.getCertificateCnName(), id);
cert.setCertificateId(id); // 设置实际的 ID
// 递归处理子证书(但不为子证书生成 INSERT)
if (cert.getChildren() != null && !cert.getChildren().isEmpty()) {
for (BaseCertificate child : cert.getChildren()) {
collectParentCertificates(child, certNameToIdMap);
}
}
}
/**
* 递归收集所有证书及其 ID 映射
*/
private static void collectCertificates(BaseCertificate cert, Map<String, String> certNameToIdMap) {
String id = generateId(cert.getCertificateCnName());
certNameToIdMap.put(cert.getCertificateCnName(), id);
cert.setCertificateId(id); // 设置实际的 ID
if (cert.getChildren() != null && !cert.getChildren().isEmpty()) {
for (BaseCertificate child : cert.getChildren()) {
// 为子证书设置正确的父级 ID
child.setParentId(id);
collectCertificates(child, certNameToIdMap);
}
}
}
/**
* 生成单条 INSERT 语句
*/
private static String generateInsertStatement(BaseCertificate cert, Map<String, String> certNameToIdMap) {
String id = cert.getCertificateId(); // 使用已生成的 ID
String parentId = cert.getParentId(); // 已经是正确的父级 ID
return String.format(
"INSERT INTO %s (certificate_id, certificate_code, certificate_cn_name, certificate_en_name, parent_id, certificate_type, history_certificate_names, delete_flag, have_parent_flag) " +
"VALUES ('%s', NULL, '%s', NULL, %s, '%s', NULL, '%s', '%s');",
TABLE_NAME,
id,
escapeSql(cert.getCertificateCnName()),
parentId != null ? "'" + parentId + "'" : "NULL",
cert.getCertificateType(),
cert.getDeleteFlag(),
cert.getHaveParentFlag()
);
}
/**
* SQL 转义:处理单引号等特殊字符
*/
private static String escapeSql(String str) {
if (str == null) {
return "";
}
return str.replace("'", "''");
}
/**
* 生成 UPDATE 语句(将子证书名称更新为带层级的名称)
*/
private static String generateUpdateStatements(List<BaseCertificate> certificates) {
StringBuilder sqlBuilder = new StringBuilder();
Map<String, Set<String>> parentToChildrenMap = new HashMap<>();
// 收集父子关系
for (BaseCertificate sheetRoot : certificates) {
if (sheetRoot.getChildren() != null) {
for (BaseCertificate parentCert : sheetRoot.getChildren()) {
if (parentCert.getChildren() != null && !parentCert.getChildren().isEmpty()) {
String parentName = parentCert.getCertificateCnName();
Set<String> childNames = parentToChildrenMap.computeIfAbsent(parentName, k -> new HashSet<>());
for (BaseCertificate childCert : parentCert.getChildren()) {
childNames.add(childCert.getCertificateCnName());
}
}
}
}
}
// 为每个父证书生成 UPDATE 语句
for (Map.Entry<String, Set<String>> entry : parentToChildrenMap.entrySet()) {
String parentName = entry.getKey();
Set<String> childNames = entry.getValue();
StringBuilder inClause = new StringBuilder("IN (");
boolean first = true;
for (String childName : childNames) {
if (!first) {
inClause.append(", ");
}
inClause.append("'").append(escapeSql(childName)).append("'");
first = false;
}
inClause.append(")");
String updateSql = String.format(
"UPDATE RFDEV.TB_BASE_USER_QUALIFY \n" +
"SET QUALIFY_NAME = '%s' \n" +
"WHERE QUALIFY_NAME %s;\n",
escapeSql(parentName),
inClause.toString()
);
sqlBuilder.append(updateSql);
}
return sqlBuilder.toString();
}
private static void exportToExcel(List<BaseCertificate> certificates, String filePath) {
List<CertificateExportData> dataList = new ArrayList<>();
int sortOrder = 1;
for (BaseCertificate sheetRoot : certificates) {
System.out.println("处理 Sheet: " + sheetRoot.getCertificateCnName());
if (sheetRoot.getChildren() != null) {
for (BaseCertificate parentCert : sheetRoot.getChildren()) {
// 添加父证书
CertificateExportData parentData = new CertificateExportData();
parentData.setSheetName(sheetRoot.getCertificateCnName());
parentData.setLevel(1);
parentData.setSortOrder(sortOrder++);
parentData.setCertificateId(parentCert.getCertificateId());
parentData.setCertificateName(parentCert.getCertificateCnName());
parentData.setParentId(parentCert.getParentId());
parentData.setHaveParentFlag(parentCert.getHaveParentFlag());
dataList.add(parentData);
// 不添加子证书
}
}
}
// 写入 Excel
EasyExcel.write(filePath, CertificateExportData.class)
.sheet("证书数据")
.doWrite(dataList);
}
/**
* 生成 IN 子句(只包含父证书,包含所有 Sheet 页)
*/
private static String generateInClause(List<BaseCertificate> certificates) {
Set<String> certNames = new LinkedHashSet<>();
for (BaseCertificate sheetRoot : certificates) {
if (sheetRoot.getChildren() != null) {
for (BaseCertificate parentCert : sheetRoot.getChildren()) {
certNames.add(parentCert.getCertificateCnName());
// 不包含子证书
}
}
}
StringBuilder inClause = new StringBuilder("IN (");
boolean first = true;
for (String name : certNames) {
if (!first) {
inClause.append(", ");
}
inClause.append("'").append(escapeSql(name)).append("'");
first = false;
}
inClause.append(")");
return inClause.toString();
}
/**
* Excel 导出数据类
*/
@Data
public static class CertificateExportData {
@com.alibaba.excel.annotation.ExcelProperty(value = "Sheet 名称", index = 0)
private String sheetName;
@com.alibaba.excel.annotation.ExcelProperty(value = "层级", index = 1)
private Integer level;
@com.alibaba.excel.annotation.ExcelProperty(value = "排序", index = 2)
private Integer sortOrder;
@com.alibaba.excel.annotation.ExcelProperty(value = "证书 ID", index = 3)
private String certificateId;
@com.alibaba.excel.annotation.ExcelProperty(value = "证书名称", index = 4)
private String certificateName;
@com.alibaba.excel.annotation.ExcelProperty(value = "父级 ID", index = 5)
private String parentId;
@com.alibaba.excel.annotation.ExcelProperty(value = "是否有父级", index = 6)
private String haveParentFlag;
}
}

浙公网安备 33010602011771号