Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Y
yd-oss
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
xingmin
yd-oss
Commits
dedc1814
Commit
dedc1814
authored
Oct 15, 2025
by
zhangxingmin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
配置
parent
69b63b70
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
960 additions
and
266 deletions
+960
-266
yd-oss-api/src/main/java/com/yd/oss/api/service/impl/ApiExcelServiceImpl.java
+1
-0
yd-oss-feign/src/main/java/com/yd/oss/feign/annotation/ExcelCollection.java
+62
-6
yd-oss-feign/src/main/java/com/yd/oss/feign/annotation/ExcelField.java
+104
-8
yd-oss-feign/src/main/java/com/yd/oss/feign/annotation/ExcelSheet.java
+29
-4
yd-oss-service/src/main/java/com/yd/oss/service/service/impl/ExcelParserServiceImpl.java
+764
-248
No files found.
yd-oss-api/src/main/java/com/yd/oss/api/service/impl/ApiExcelServiceImpl.java
View file @
dedc1814
...
@@ -79,6 +79,7 @@ public class ApiExcelServiceImpl implements ApiExcelService {
...
@@ -79,6 +79,7 @@ public class ApiExcelServiceImpl implements ApiExcelService {
response
.
setMap
(
result
);
response
.
setMap
(
result
);
return
Result
.
success
(
response
);
return
Result
.
success
(
response
);
}
catch
(
Exception
e
)
{
}
catch
(
Exception
e
)
{
log
.
info
(
"Excel解析异常:{}"
,
e
.
getMessage
());
throw
new
BusinessException
(
"Excel解析异常!"
);
throw
new
BusinessException
(
"Excel解析异常!"
);
}
}
}
}
...
...
yd-oss-feign/src/main/java/com/yd/oss/feign/annotation/ExcelCollection.java
View file @
dedc1814
...
@@ -5,11 +5,66 @@ import java.lang.annotation.Retention;
...
@@ -5,11 +5,66 @@ import java.lang.annotation.Retention;
import
java.lang.annotation.RetentionPolicy
;
import
java.lang.annotation.RetentionPolicy
;
import
java.lang.annotation.Target
;
import
java.lang.annotation.Target
;
// Excel集合注解
/**
@Retention
(
RetentionPolicy
.
RUNTIME
)
* 增强的Excel集合注解,支持复杂数据结构
*/
@Target
(
ElementType
.
FIELD
)
@Target
(
ElementType
.
FIELD
)
@Retention
(
RetentionPolicy
.
RUNTIME
)
public
@interface
ExcelCollection
{
public
@interface
ExcelCollection
{
Class
<?>
type
();
// 集合元素类型
int
startRow
();
// 集合起始行
/**
int
endRow
()
default
-
1
;
// 集合结束行(-1表示自动检测)
* 集合元素类型
}
*/
Class
<?>
type
();
/**
* 起始行(0-based)
*/
int
startRow
();
/**
* 结束行(0-based,-1表示自动检测)
*/
int
endRow
()
default
-
1
;
/**
* 每个对象占用的行数(默认1行)
*/
int
rowSpan
()
default
1
;
/**
* 数据分组模式
*/
GroupMode
groupMode
()
default
GroupMode
.
SINGLE_ROW
;
/**
* 分组键字段(用于GROUP_BY_KEY模式)
*/
String
groupKey
()
default
""
;
// 在 ExcelCollection 注解中添加
String
endFlagField
()
default
""
;
// 用于判断集合结束的字段名
/**
* 下一个对象的第一个字段标题,用于判断集合结束
*/
String
nextFieldTitle
()
default
""
;
/**
* 数据分组模式枚举
*/
enum
GroupMode
{
/**
* 单行模式:每行一个对象
*/
SINGLE_ROW
,
/**
* 固定行跨度:每个对象占用固定行数
*/
FIXED_ROW_SPAN
,
/**
* 按键分组:根据指定字段的值进行分组
*/
GROUP_BY_KEY
}
}
\ No newline at end of file
yd-oss-feign/src/main/java/com/yd/oss/feign/annotation/ExcelField.java
View file @
dedc1814
...
@@ -5,14 +5,110 @@ import java.lang.annotation.Retention;
...
@@ -5,14 +5,110 @@ import java.lang.annotation.Retention;
import
java.lang.annotation.RetentionPolicy
;
import
java.lang.annotation.RetentionPolicy
;
import
java.lang.annotation.Target
;
import
java.lang.annotation.Target
;
// Excel字段注解
/**
@Retention
(
RetentionPolicy
.
RUNTIME
)
* 通用Excel字段注解
*/
@Target
(
ElementType
.
FIELD
)
@Target
(
ElementType
.
FIELD
)
@Retention
(
RetentionPolicy
.
RUNTIME
)
public
@interface
ExcelField
{
public
@interface
ExcelField
{
String
name
();
// 标题名称
int
titleRow
();
// 标题所在行
/**
int
titleCol
();
// 标题所在列
* 字段名称(用于日志和错误提示)
int
valueRow
()
default
-
1
;
// 值所在行(默认与标题同行)
*/
int
valueCol
()
default
-
1
;
// 值所在列(默认标题列+1)
String
name
()
default
""
;
String
dateFormat
()
default
"yyyy/MM/dd"
;
// 日期格式
/**
* 标题行(0-based)
*/
int
titleRow
()
default
-
1
;
/**
* 标题列(0-based)
*/
int
titleCol
()
default
-
1
;
/**
* 值行(0-based,-1表示使用titleRow)
*/
int
valueRow
()
default
-
1
;
/**
* 值列(0-based,-1表示使用titleCol + 1)
*/
int
valueCol
()
default
-
1
;
/**
* 日期格式
*/
String
dateFormat
()
default
"yyyy/MM/dd"
;
/**
* 是否必须
*/
boolean
required
()
default
false
;
/**
* 默认值
*/
String
defaultValue
()
default
""
;
/**
* 字段类型
*/
FieldType
fieldType
()
default
FieldType
.
SINGLE
;
/**
* 字段类型枚举
*/
enum
FieldType
{
/**
* 单值字段
*/
SINGLE
,
/**
* 列表字段(水平方向)
*/
HORIZONTAL_LIST
,
/**
* 列表字段(垂直方向)
*/
VERTICAL_LIST
}
/**
* 列表字段配置(当fieldType为LIST时使用)
*/
ListConfig
listConfig
()
default
@ListConfig
;
@interface
ListConfig
{
/**
* 起始行(0-based)
*/
int
startRow
()
default
-
1
;
/**
* 起始列(0-based)
*/
int
startCol
()
default
-
1
;
/**
* 结束行(0-based,-1表示自动检测)
*/
int
endRow
()
default
-
1
;
/**
* 结束列(0-based,-1表示自动检测)
*/
int
endCol
()
default
-
1
;
/**
* 方向:true为水平,false为垂直
*/
boolean
horizontal
()
default
true
;
/**
* 元素类型
*/
Class
<?>
elementType
()
default
String
.
class
;
}
}
}
yd-oss-feign/src/main/java/com/yd/oss/feign/annotation/ExcelSheet.java
View file @
dedc1814
...
@@ -5,10 +5,35 @@ import java.lang.annotation.Retention;
...
@@ -5,10 +5,35 @@ import java.lang.annotation.Retention;
import
java.lang.annotation.RetentionPolicy
;
import
java.lang.annotation.RetentionPolicy
;
import
java.lang.annotation.Target
;
import
java.lang.annotation.Target
;
// Excel Sheet注解
/**
@Retention
(
RetentionPolicy
.
RUNTIME
)
* 通用Excel Sheet注解
*/
@Target
(
ElementType
.
TYPE
)
@Target
(
ElementType
.
TYPE
)
@Retention
(
RetentionPolicy
.
RUNTIME
)
public
@interface
ExcelSheet
{
public
@interface
ExcelSheet
{
int
sheetIndex
()
default
0
;
// Sheet索引
String
sheetName
()
default
""
;
// Sheet名称
/**
* Sheet索引(0-based)
*/
int
sheetIndex
()
default
0
;
/**
* Sheet名称
*/
String
sheetName
()
default
""
;
/**
* 数据起始行(0-based)
*/
int
dataStartRow
()
default
0
;
/**
* 是否跳过空行
*/
boolean
skipEmptyRows
()
default
true
;
/**
* 最大行数限制
*/
int
maxRows
()
default
1000
;
}
}
yd-oss-service/src/main/java/com/yd/oss/service/service/impl/ExcelParserServiceImpl.java
View file @
dedc1814
package
com
.
yd
.
oss
.
service
.
service
.
impl
;
package
com
.
yd
.
oss
.
service
.
service
.
impl
;
import
com.alibaba.fastjson.JSON
;
import
com.yd.oss.feign.annotation.ExcelCollection
;
import
com.yd.oss.feign.annotation.ExcelCollection
;
import
com.yd.oss.feign.annotation.ExcelField
;
import
com.yd.oss.feign.annotation.ExcelField
;
import
com.yd.oss.feign.annotation.ExcelSheet
;
import
com.yd.oss.feign.annotation.ExcelSheet
;
...
@@ -21,20 +22,17 @@ import java.util.*;
...
@@ -21,20 +22,17 @@ import java.util.*;
*/
*/
@Service
@Service
public
class
ExcelParserServiceImpl
implements
ExcelParserService
{
public
class
ExcelParserServiceImpl
implements
ExcelParserService
{
private
static
final
Logger
log
=
LoggerFactory
.
getLogger
(
ExcelParserServiceImpl
.
class
);
private
static
final
Logger
log
=
LoggerFactory
.
getLogger
(
ExcelParserServiceImpl
.
class
);
/**
/**
* 解析Excel文件(多Sheet页版本)
* 解析Excel文件(多Sheet页版本)
* @param file 上传的Excel文件
* @param sheetClasses 每个Sheet页对应的类类型
* @return 包含所有Sheet页解析结果的Map,key为Sheet索引
*/
*/
@Override
@Override
public
Map
<
Integer
,
Object
>
parseExcelWithMultipleSheets
(
MultipartFile
file
,
Class
<?>...
sheetClasses
)
throws
Exception
{
public
Map
<
Integer
,
Object
>
parseExcelWithMultipleSheets
(
MultipartFile
file
,
Class
<?>...
sheetClasses
)
throws
Exception
{
Map
<
Integer
,
Object
>
resultMap
=
new
HashMap
<>();
Map
<
Integer
,
Object
>
resultMap
=
new
HashMap
<>();
Workbook
workbook
=
WorkbookFactory
.
create
(
file
.
getInputStream
());
Workbook
workbook
=
WorkbookFactory
.
create
(
file
.
getInputStream
());
try
{
try
{
for
(
Class
<?>
clazz
:
sheetClasses
)
{
for
(
Class
<?>
clazz
:
sheetClasses
)
{
ExcelSheet
sheetAnnotation
=
clazz
.
getAnnotation
(
ExcelSheet
.
class
);
ExcelSheet
sheetAnnotation
=
clazz
.
getAnnotation
(
ExcelSheet
.
class
);
...
@@ -42,32 +40,27 @@ public class ExcelParserServiceImpl implements ExcelParserService {
...
@@ -42,32 +40,27 @@ public class ExcelParserServiceImpl implements ExcelParserService {
log
.
warn
(
"类 {} 缺少@ExcelSheet注解,跳过处理"
,
clazz
.
getSimpleName
());
log
.
warn
(
"类 {} 缺少@ExcelSheet注解,跳过处理"
,
clazz
.
getSimpleName
());
continue
;
continue
;
}
}
Sheet
sheet
;
Sheet
sheet
=
getSheet
(
workbook
,
sheetAnnotation
);
if
(!
sheetAnnotation
.
sheetName
().
isEmpty
())
{
sheet
=
workbook
.
getSheet
(
sheetAnnotation
.
sheetName
());
}
else
{
sheet
=
workbook
.
getSheetAt
(
sheetAnnotation
.
sheetIndex
());
}
if
(
sheet
==
null
)
{
if
(
sheet
==
null
)
{
log
.
warn
(
"Sheet {} 不存在"
,
log
.
warn
(
"Sheet {} 不存在"
,
!
sheetAnnotation
.
sheetName
().
isEmpty
()
?
!
sheetAnnotation
.
sheetName
().
isEmpty
()
?
sheetAnnotation
.
sheetName
()
:
sheetAnnotation
.
sheetIndex
());
sheetAnnotation
.
sheetName
()
:
sheetAnnotation
.
sheetIndex
());
continue
;
continue
;
}
}
Object
instance
=
clazz
.
getDeclaredConstructor
().
newInstance
();
Object
instance
=
clazz
.
getDeclaredConstructor
().
newInstance
();
// 解析普通字段
// 解析普通字段
parseFieldsWithMergedCells
(
sheet
,
instance
);
parseFieldsWithMergedCells
(
sheet
,
instance
);
// 解析集合字段
// 解析集合字段
parseCollectionFields
(
sheet
,
instance
);
parseCollectionFields
(
sheet
,
instance
);
log
.
info
(
"解析结果: {}"
,
JSON
.
toJSONString
(
instance
));
resultMap
.
put
(
sheetAnnotation
.
sheetIndex
(),
instance
);
resultMap
.
put
(
sheetAnnotation
.
sheetIndex
(),
instance
);
}
}
return
resultMap
;
return
resultMap
;
}
finally
{
}
finally
{
workbook
.
close
();
workbook
.
close
();
...
@@ -76,41 +69,21 @@ public class ExcelParserServiceImpl implements ExcelParserService {
...
@@ -76,41 +69,21 @@ public class ExcelParserServiceImpl implements ExcelParserService {
/**
/**
* 解析单个Sheet页
* 解析单个Sheet页
* @param file
* @param clazz
* @param <T>
* @return
* @throws Exception
*/
*/
public
<
T
>
T
parseExcel
(
MultipartFile
file
,
Class
<
T
>
clazz
)
throws
Exception
{
public
<
T
>
T
parseExcel
(
MultipartFile
file
,
Class
<
T
>
clazz
)
throws
Exception
{
Workbook
workbook
=
WorkbookFactory
.
create
(
file
.
getInputStream
());
Workbook
workbook
=
WorkbookFactory
.
create
(
file
.
getInputStream
());
try
{
try
{
// 获取Sheet信息
ExcelSheet
sheetAnnotation
=
clazz
.
getAnnotation
(
ExcelSheet
.
class
);
ExcelSheet
sheetAnnotation
=
clazz
.
getAnnotation
(
ExcelSheet
.
class
);
Sheet
sheet
;
Sheet
sheet
=
getSheet
(
workbook
,
sheetAnnotation
);
if
(
sheetAnnotation
!=
null
)
{
if
(!
sheetAnnotation
.
sheetName
().
isEmpty
())
{
sheet
=
workbook
.
getSheet
(
sheetAnnotation
.
sheetName
());
}
else
{
sheet
=
workbook
.
getSheetAt
(
sheetAnnotation
.
sheetIndex
());
}
}
else
{
sheet
=
workbook
.
getSheetAt
(
0
);
// 默认第一个Sheet
}
if
(
sheet
==
null
)
{
if
(
sheet
==
null
)
{
throw
new
RuntimeException
(
"Sheet not found"
);
throw
new
RuntimeException
(
"Sheet not found"
);
}
}
T
instance
=
clazz
.
getDeclaredConstructor
().
newInstance
();
T
instance
=
clazz
.
getDeclaredConstructor
().
newInstance
();
// 解析普通字段
parseFieldsWithMergedCells
(
sheet
,
instance
);
parseFieldsWithMergedCells
(
sheet
,
instance
);
// 解析集合字段
parseCollectionFields
(
sheet
,
instance
);
parseCollectionFields
(
sheet
,
instance
);
return
instance
;
return
instance
;
}
finally
{
}
finally
{
workbook
.
close
();
workbook
.
close
();
...
@@ -118,12 +91,25 @@ public class ExcelParserServiceImpl implements ExcelParserService {
...
@@ -118,12 +91,25 @@ public class ExcelParserServiceImpl implements ExcelParserService {
}
}
/**
/**
* 获取Sheet
*/
private
Sheet
getSheet
(
Workbook
workbook
,
ExcelSheet
sheetAnnotation
)
{
if
(
sheetAnnotation
==
null
)
{
return
workbook
.
getSheetAt
(
0
);
}
if
(!
sheetAnnotation
.
sheetName
().
isEmpty
())
{
Sheet
sheet
=
workbook
.
getSheet
(
sheetAnnotation
.
sheetName
());
if
(
sheet
!=
null
)
return
sheet
;
}
return
workbook
.
getSheetAt
(
sheetAnnotation
.
sheetIndex
());
}
/**
* 处理合并单元格的字段解析
* 处理合并单元格的字段解析
* @param sheet
* @param instance
*/
*/
private
void
parseFieldsWithMergedCells
(
Sheet
sheet
,
Object
instance
)
{
private
void
parseFieldsWithMergedCells
(
Sheet
sheet
,
Object
instance
)
{
// 获取所有合并区域
List
<
CellRangeAddress
>
mergedRegions
=
sheet
.
getMergedRegions
();
List
<
CellRangeAddress
>
mergedRegions
=
sheet
.
getMergedRegions
();
for
(
Field
field
:
instance
.
getClass
().
getDeclaredFields
())
{
for
(
Field
field
:
instance
.
getClass
().
getDeclaredFields
())
{
...
@@ -170,10 +156,6 @@ public class ExcelParserServiceImpl implements ExcelParserService {
...
@@ -170,10 +156,6 @@ public class ExcelParserServiceImpl implements ExcelParserService {
/**
/**
* 查找包含指定行列的合并区域
* 查找包含指定行列的合并区域
* @param mergedRegions
* @param row
* @param column
* @return
*/
*/
private
CellRangeAddress
findMergedRegion
(
List
<
CellRangeAddress
>
mergedRegions
,
int
row
,
int
column
)
{
private
CellRangeAddress
findMergedRegion
(
List
<
CellRangeAddress
>
mergedRegions
,
int
row
,
int
column
)
{
for
(
CellRangeAddress
region
:
mergedRegions
)
{
for
(
CellRangeAddress
region
:
mergedRegions
)
{
...
@@ -185,268 +167,801 @@ public class ExcelParserServiceImpl implements ExcelParserService {
...
@@ -185,268 +167,801 @@ public class ExcelParserServiceImpl implements ExcelParserService {
}
}
/**
/**
* 将单元格值转换为字符串
* 解析集合字段(支持固定行跨度模式)
* @param cell 单元格对象
* @return 单元格值的字符串表示,若单元格为null则返回null
*/
*/
private
String
getCellValueAsString
(
Cell
cell
)
{
private
void
parseCollectionFields
(
Sheet
sheet
,
Object
instance
)
throws
Exception
{
if
(
cell
==
null
)
{
for
(
Field
field
:
instance
.
getClass
().
getDeclaredFields
())
{
return
null
;
ExcelCollection
collectionAnnotation
=
field
.
getAnnotation
(
ExcelCollection
.
class
);
if
(
collectionAnnotation
!=
null
)
{
List
<
Object
>
collectionData
=
new
ArrayList
<>();
Class
<?>
collectionType
=
collectionAnnotation
.
type
();
log
.
info
(
"开始解析集合字段: {}, 类型: {}, 起始行: {}, 分组模式: {}"
,
field
.
getName
(),
collectionType
.
getSimpleName
(),
collectionAnnotation
.
startRow
(),
collectionAnnotation
.
groupMode
());
// 获取目标类的所有字段及其Excel注解
Map
<
Field
,
ExcelField
>
fieldMappings
=
new
HashMap
<>();
for
(
Field
elementField
:
collectionType
.
getDeclaredFields
())
{
ExcelField
excelField
=
elementField
.
getAnnotation
(
ExcelField
.
class
);
if
(
excelField
!=
null
)
{
fieldMappings
.
put
(
elementField
,
excelField
);
}
}
if
(
collectionAnnotation
.
groupMode
()
==
ExcelCollection
.
GroupMode
.
FIXED_ROW_SPAN
)
{
// 固定行跨度模式 - 每个元素占用固定行数
parseFixedRowSpanCollection
(
sheet
,
collectionData
,
collectionType
,
fieldMappings
,
collectionAnnotation
);
}
else
{
// 单行模式 - 每个元素占用一行
parseSingleRowCollection
(
sheet
,
collectionData
,
collectionType
,
fieldMappings
,
collectionAnnotation
);
}
// 设置集合字段值
field
.
setAccessible
(
true
);
field
.
set
(
instance
,
collectionData
);
log
.
info
(
"集合字段 {} 解析完成,共 {} 条数据"
,
field
.
getName
(),
collectionData
.
size
());
}
}
}
switch
(
cell
.
getCellType
())
{
}
case
STRING:
return
cell
.
getStringCellValue
().
trim
();
/**
case
NUMERIC:
* 解析固定行跨度的集合(支持结束标志字段和下一个字段标题)
if
(
DateUtil
.
isCellDateFormatted
(
cell
))
{
*/
// 日期类型转换为字符串(可根据需要调整格式)
private
void
parseFixedRowSpanCollection
(
Sheet
sheet
,
List
<
Object
>
collectionData
,
return
new
SimpleDateFormat
(
"yyyy-MM-dd HH:mm:ss"
).
format
(
cell
.
getDateCellValue
());
Class
<?>
collectionType
,
Map
<
Field
,
ExcelField
>
fieldMappings
,
ExcelCollection
collectionAnnotation
)
throws
Exception
{
int
rowSpan
=
collectionAnnotation
.
rowSpan
();
int
startRow
=
collectionAnnotation
.
startRow
();
String
endFlagField
=
collectionAnnotation
.
endFlagField
();
String
nextFieldTitle
=
collectionAnnotation
.
nextFieldTitle
();
log
.
info
(
"开始解析固定行跨度集合,起始行: {}, 行跨度: {}, 结束标志字段: {}, 下一字段标题: {}"
,
startRow
,
rowSpan
,
endFlagField
,
nextFieldTitle
);
// 获取结束标志字段的映射信息
Field
endFlagFieldObj
=
null
;
ExcelField
endFlagExcelField
=
null
;
if
(!
endFlagField
.
isEmpty
())
{
try
{
endFlagFieldObj
=
collectionType
.
getDeclaredField
(
endFlagField
);
endFlagExcelField
=
endFlagFieldObj
.
getAnnotation
(
ExcelField
.
class
);
log
.
info
(
"找到结束标志字段: {}, 位置: 行{}, 列{}"
,
endFlagField
,
endFlagExcelField
.
valueRow
(),
endFlagExcelField
.
valueCol
());
}
catch
(
NoSuchFieldException
e
)
{
log
.
warn
(
"结束标志字段 {} 不存在,将使用默认结束判断"
,
endFlagField
);
}
}
// 遍历每个元素组
for
(
int
groupIndex
=
0
;
groupIndex
<
1000
;
groupIndex
++)
{
int
groupStartRow
=
startRow
+
(
groupIndex
*
rowSpan
);
int
nextGroupStartRow
=
groupStartRow
+
rowSpan
;
// 检查是否超出表格范围
if
(
groupStartRow
>
sheet
.
getLastRowNum
())
{
log
.
info
(
"超出表格范围,集合解析结束"
);
break
;
}
log
.
debug
(
"=== 解析组 {},起始行: {} ==="
,
groupIndex
+
1
,
groupStartRow
);
// 关键修改:先解析当前组
try
{
Object
element
=
collectionType
.
getDeclaredConstructor
().
newInstance
();
boolean
hasData
=
parseGroupData
(
sheet
,
groupStartRow
,
element
,
fieldMappings
);
if
(
hasData
)
{
collectionData
.
add
(
element
);
log
.
info
(
"成功解析组 {},组起始行: {}"
,
groupIndex
+
1
,
groupStartRow
);
}
else
{
}
else
{
// 数字类型避免科学计数法,转换为字符串
log
.
debug
(
"组起始行 {} 没有有效数据,跳过"
,
groupStartRow
);
return
String
.
valueOf
(
cell
.
getNumericCellValue
());
// 如果连续3个组没有数据,认为集合结束
if
(
groupIndex
>
0
&&
!
hasDataInNextGroup
(
sheet
,
nextGroupStartRow
,
fieldMappings
))
{
log
.
info
(
"检测到连续空组,集合解析结束"
);
break
;
}
}
}
case
BOOLEAN:
return
String
.
valueOf
(
cell
.
getBooleanCellValue
());
// 关键修改:在解析当前组后,检查下一个组是否应该结束
case
FORMULA:
boolean
shouldEnd
=
false
;
try
{
return
cell
.
getStringCellValue
().
trim
();
// 1. 检查结束标志字段(检查下一个组)
}
catch
(
Exception
e
)
{
if
(!
endFlagField
.
isEmpty
()
&&
endFlagExcelField
!=
null
)
{
// 公式计算结果不是字符串时,尝试获取数值
if
(
shouldEndByEndFlag
(
sheet
,
nextGroupStartRow
,
rowSpan
,
endFlagExcelField
))
{
return
String
.
valueOf
(
cell
.
getNumericCellValue
());
log
.
info
(
"检测到下一个组结束标志为空,集合解析结束"
);
shouldEnd
=
true
;
}
}
}
default
:
return
""
;
// 2. 检查下一个对象的第一个字段标题
if
(!
shouldEnd
&&
nextFieldTitle
!=
null
&&
!
nextFieldTitle
.
isEmpty
())
{
if
(
shouldEndByNextFieldTitle
(
sheet
,
nextGroupStartRow
,
fieldMappings
,
nextFieldTitle
))
{
log
.
info
(
"检测到下一个对象标题 '{}',集合解析结束"
,
nextFieldTitle
);
shouldEnd
=
true
;
}
}
if
(
shouldEnd
)
{
break
;
}
}
catch
(
Exception
e
)
{
log
.
error
(
"解析组起始行 {} 时发生异常: {}"
,
groupStartRow
,
e
.
getMessage
(),
e
);
}
}
}
log
.
info
(
"固定行跨度集合解析完成,共解析 {} 个元素"
,
collectionData
.
size
());
}
}
/**
/**
* 解析普通字段
* 根据下一个对象的第一个字段标题判断是否应该结束集合解析
* @param sheet
* @param instance
*/
*/
private
void
parseFields
(
Sheet
sheet
,
Object
instance
)
{
private
boolean
shouldEndByNextFieldTitle
(
Sheet
sheet
,
int
nextGroupStartRow
,
for
(
Field
field
:
instance
.
getClass
().
getDeclaredFields
())
{
Map
<
Field
,
ExcelField
>
fieldMappings
,
ExcelField
excelField
=
field
.
getAnnotation
(
ExcelField
.
class
);
String
expectedNextFieldTitle
)
{
if
(
excelField
!=
null
)
{
try
{
try
{
if
(
expectedNextFieldTitle
==
null
||
expectedNextFieldTitle
.
isEmpty
())
{
// 获取值单元格
return
false
;
int
valueRow
=
excelField
.
valueRow
()
>=
0
?
excelField
.
valueRow
()
:
excelField
.
titleRow
();
}
int
valueCol
=
excelField
.
valueCol
()
>=
0
?
excelField
.
valueCol
()
:
excelField
.
titleCol
()
+
1
;
// 获取第一个字段的标题位置(用于检查下一个对象的标题)
Row
row
=
sheet
.
getRow
(
valueRow
);
Field
firstField
=
getFirstFieldByColumnOrder
(
fieldMappings
);
if
(
row
==
null
)
continue
;
if
(
firstField
==
null
)
{
return
false
;
Cell
cell
=
row
.
getCell
(
valueCol
);
}
if
(
cell
==
null
)
continue
;
ExcelField
firstExcelField
=
fieldMappings
.
get
(
firstField
);
// 转换单元格值
int
titleRow
=
firstExcelField
.
titleRow
();
Object
value
=
convertCellValue
(
cell
,
field
.
getType
(),
excelField
.
dateFormat
());
int
titleCol
=
firstExcelField
.
titleCol
();
// 设置字段值
log
.
debug
(
"检查下一个字段标题: 下一组起始行={}, 标题行={}, 标题列={}, 预期标题='{}'"
,
field
.
setAccessible
(
true
);
nextGroupStartRow
,
titleRow
,
titleCol
,
expectedNextFieldTitle
);
field
.
set
(
instance
,
value
);
}
catch
(
Exception
e
)
{
// 检查下一组的标题行
log
.
warn
(
"设置字段 {} 值失败: {}"
,
field
.
getName
(),
e
.
getMessage
());
int
nextTitleRow
=
nextGroupStartRow
+
titleRow
;
}
if
(
nextTitleRow
>
sheet
.
getLastRowNum
())
{
log
.
debug
(
"下一组标题行 {} 超出表格范围"
,
nextTitleRow
);
return
true
;
}
}
Row
row
=
sheet
.
getRow
(
nextTitleRow
);
if
(
row
==
null
)
{
log
.
debug
(
"下一组标题行 {} 为null"
,
nextTitleRow
);
return
true
;
}
Cell
titleCell
=
row
.
getCell
(
titleCol
);
if
(
titleCell
==
null
)
{
log
.
debug
(
"下一组标题行{}列{}为null"
,
nextTitleRow
,
titleCol
);
return
true
;
}
String
actualTitle
=
getCellValueAsString
(
titleCell
);
log
.
debug
(
"实际下一组标题: '{}'"
,
actualTitle
);
// 如果下一组的标题与预期下一个对象的标题匹配,说明集合应该结束
if
(
expectedNextFieldTitle
.
equals
(
actualTitle
))
{
log
.
info
(
"检测到下一个对象标题 '{}',集合解析结束"
,
expectedNextFieldTitle
);
return
true
;
}
return
false
;
}
catch
(
Exception
e
)
{
log
.
warn
(
"检查下一个字段标题失败: {}"
,
e
.
getMessage
());
return
false
;
}
}
}
}
/**
/**
* 解析集合字段(增强版,支持动态结束行检测)
* 按列顺序获取第一个字段(列号最小的字段)
* @param sheet
* @param instance
* @throws Exception
*/
*/
private
void
parseCollectionFields
(
Sheet
sheet
,
Object
instance
)
throws
Exception
{
private
Field
getFirstFieldByColumnOrder
(
Map
<
Field
,
ExcelField
>
fieldMappings
)
{
for
(
Field
field
:
instance
.
getClass
().
getDeclaredFields
())
{
if
(
fieldMappings
.
isEmpty
())
{
ExcelCollection
collectionAnnotation
=
field
.
getAnnotation
(
ExcelCollection
.
class
);
return
null
;
if
(
collectionAnnotation
!=
null
)
{
}
List
<
Object
>
collectionData
=
new
ArrayList
<>();
return
fieldMappings
.
entrySet
().
stream
()
// 确定结束行(支持自动检测)
.
min
(
Comparator
.
comparingInt
(
entry
->
entry
.
getValue
().
valueCol
()))
int
endRow
=
collectionAnnotation
.
endRow
()
>=
0
?
.
map
(
Map
.
Entry
::
getKey
)
collectionAnnotation
.
endRow
()
:
findCollectionEndRow
(
sheet
,
collectionAnnotation
);
.
orElse
(
null
);
}
// 遍历行,解析集合元素
for
(
int
rowNum
=
collectionAnnotation
.
startRow
();
rowNum
<=
endRow
;
rowNum
++)
{
/**
Row
row
=
sheet
.
getRow
(
rowNum
);
* 根据结束标志字段判断是否应该结束集合解析
if
(
row
==
null
||
isRowEmpty
(
row
))
{
* 关键修改:检查当前组的结束标志,而不是下一个组
continue
;
// 跳过空行
*/
}
private
boolean
shouldEndByEndFlag
(
Sheet
sheet
,
int
currentGroupStartRow
,
int
rowSpan
,
ExcelField
endFlagExcelField
)
{
try
{
try
{
Object
element
=
collectionAnnotation
.
type
().
getDeclaredConstructor
().
newInstance
();
// 计算结束标志字段在当前组中的实际位置
boolean
hasData
=
false
;
int
actualRow
=
currentGroupStartRow
+
endFlagExcelField
.
valueRow
();
int
actualCol
=
endFlagExcelField
.
valueCol
();
// 解析元素字段
for
(
Field
elementField
:
collectionAnnotation
.
type
().
getDeclaredFields
())
{
log
.
debug
(
"检查结束标志: 当前组起始行={}, 实际行={}, 实际列={}"
,
ExcelField
excelField
=
elementField
.
getAnnotation
(
ExcelField
.
class
);
currentGroupStartRow
,
actualRow
,
actualCol
);
if
(
excelField
!=
null
)
{
// 计算值所在列
// 检查行号是否有效
int
valueCol
=
excelField
.
valueCol
()
>=
0
?
if
(
actualRow
>
sheet
.
getLastRowNum
())
{
excelField
.
valueCol
()
:
excelField
.
titleCol
();
log
.
debug
(
"结束标志检查: 行号 {} 超出表格范围"
,
actualRow
);
return
true
;
Cell
cell
=
row
.
getCell
(
valueCol
);
}
if
(
cell
!=
null
)
{
Object
value
=
convertCellValue
(
cell
,
elementField
.
getType
(),
excelField
.
dateFormat
());
Row
row
=
sheet
.
getRow
(
actualRow
);
elementField
.
setAccessible
(
true
);
if
(
row
==
null
)
{
elementField
.
set
(
element
,
value
);
log
.
debug
(
"结束标志检查: 行 {} 为null"
,
actualRow
);
hasData
=
true
;
return
true
;
}
}
}
}
Cell
cell
=
row
.
getCell
(
actualCol
);
if
(
cell
==
null
)
{
if
(
hasData
)
{
log
.
debug
(
"结束标志检查: 行{}列{}为null"
,
actualRow
,
actualCol
);
collectionData
.
add
(
element
);
return
true
;
}
}
}
catch
(
Exception
e
)
{
log
.
warn
(
"解析集合元素失败: {}"
,
e
.
getMessage
());
String
cellValue
=
getCellValueAsString
(
cell
);
log
.
debug
(
"结束标志字段值: '{}'"
,
cellValue
);
// 如果结束标志字段的值为空,说明集合结束
if
(
cellValue
==
null
||
cellValue
.
trim
().
isEmpty
())
{
log
.
info
(
"结束标志字段为空,集合解析结束"
);
return
true
;
}
return
false
;
}
catch
(
Exception
e
)
{
log
.
warn
(
"检查结束标志失败: {}"
,
e
.
getMessage
());
return
false
;
}
}
/**
* 获取第一个字段(用于结束判断)
*/
private
Field
getFirstField
(
Map
<
Field
,
ExcelField
>
fieldMappings
)
{
if
(
fieldMappings
.
isEmpty
())
{
return
null
;
}
return
fieldMappings
.
keySet
().
iterator
().
next
();
}
/**
* 检查是否应该结束集合解析
*/
private
boolean
shouldEndCollection
(
Sheet
sheet
,
int
groupStartRow
,
Field
firstField
,
ExcelField
excelField
,
List
<
Object
>
collectionData
)
{
try
{
if
(
firstField
==
null
||
excelField
==
null
)
{
return
false
;
}
// 获取上一个对象的第一个字段值
Object
lastElement
=
collectionData
.
get
(
collectionData
.
size
()
-
1
);
firstField
.
setAccessible
(
true
);
Object
lastValue
=
firstField
.
get
(
lastElement
);
// 获取当前组的第一个字段值
int
actualRow
=
groupStartRow
+
excelField
.
valueRow
();
int
actualCol
=
excelField
.
valueCol
();
if
(
actualRow
>
sheet
.
getLastRowNum
())
{
return
true
;
}
Row
row
=
sheet
.
getRow
(
actualRow
);
if
(
row
==
null
)
{
return
true
;
}
Cell
cell
=
row
.
getCell
(
actualCol
);
if
(
cell
==
null
)
{
return
true
;
}
String
currentCellValue
=
getCellValueAsString
(
cell
);
Object
currentValue
=
convertStringValue
(
currentCellValue
,
firstField
.
getType
(),
excelField
.
dateFormat
());
// 如果当前值与上一个值不同,说明集合结束
if
(!
Objects
.
equals
(
lastValue
,
currentValue
))
{
log
.
debug
(
"第一个字段值发生变化: {} -> {}"
,
lastValue
,
currentValue
);
return
true
;
}
return
false
;
}
catch
(
Exception
e
)
{
log
.
warn
(
"检查集合结束标志失败: {}"
,
e
.
getMessage
());
return
false
;
}
}
/**
* 解析组数据(通用方法)
*/
private
boolean
parseGroupData
(
Sheet
sheet
,
int
groupStartRow
,
Object
element
,
Map
<
Field
,
ExcelField
>
fieldMappings
)
throws
Exception
{
boolean
hasData
=
false
;
for
(
Map
.
Entry
<
Field
,
ExcelField
>
entry
:
fieldMappings
.
entrySet
())
{
Field
elementField
=
entry
.
getKey
();
ExcelField
excelField
=
entry
.
getValue
();
int
actualRow
=
groupStartRow
+
excelField
.
valueRow
();
int
actualCol
=
excelField
.
valueCol
();
log
.
debug
(
"字段 {}: 组起始行={}, 相对行={}, 实际行号={}, 列号={}"
,
elementField
.
getName
(),
groupStartRow
,
excelField
.
valueRow
(),
actualRow
,
actualCol
);
// 检查行号是否有效
if
(
actualRow
>
sheet
.
getLastRowNum
())
{
log
.
debug
(
"行号 {} 超出表格范围"
,
actualRow
);
continue
;
}
Row
row
=
sheet
.
getRow
(
actualRow
);
if
(
row
==
null
)
{
log
.
debug
(
"行 {} 为null,跳过"
,
actualRow
);
continue
;
}
Cell
cell
=
row
.
getCell
(
actualCol
);
if
(
cell
==
null
)
{
log
.
debug
(
"行{}列{}为null,跳过"
,
actualRow
,
actualCol
);
continue
;
}
String
cellValue
=
getCellValueAsString
(
cell
);
if
(
cellValue
==
null
||
cellValue
.
trim
().
isEmpty
())
{
log
.
debug
(
"行{}列{}的值为空,跳过"
,
actualRow
,
actualCol
);
continue
;
}
log
.
debug
(
"字段 {}: 行{}列{} = '{}'"
,
elementField
.
getName
(),
actualRow
,
actualCol
,
cellValue
);
try
{
Object
value
=
convertStringValue
(
cellValue
,
elementField
.
getType
(),
excelField
.
dateFormat
());
elementField
.
setAccessible
(
true
);
elementField
.
set
(
element
,
value
);
hasData
=
true
;
log
.
debug
(
"成功设置字段 {}: {} -> {}"
,
elementField
.
getName
(),
cellValue
,
value
);
}
catch
(
Exception
e
)
{
log
.
error
(
"设置字段 {} 值失败,行{}列{},值'{}': {}"
,
elementField
.
getName
(),
actualRow
,
actualCol
,
cellValue
,
e
.
getMessage
());
}
}
return
hasData
;
}
/**
* 检查下一个组是否有数据
*/
private
boolean
hasDataInNextGroup
(
Sheet
sheet
,
int
nextGroupStartRow
,
Map
<
Field
,
ExcelField
>
fieldMappings
)
{
for
(
Map
.
Entry
<
Field
,
ExcelField
>
entry
:
fieldMappings
.
entrySet
())
{
ExcelField
excelField
=
entry
.
getValue
();
int
valueCol
=
excelField
.
valueCol
();
// 直接使用相对行号
int
actualRow
=
nextGroupStartRow
+
excelField
.
valueRow
();
if
(
actualRow
>
sheet
.
getLastRowNum
())
{
continue
;
}
Row
row
=
sheet
.
getRow
(
actualRow
);
if
(
row
!=
null
)
{
Cell
cell
=
row
.
getCell
(
valueCol
);
if
(
cell
!=
null
)
{
String
cellValue
=
getCellValueAsString
(
cell
);
if
(
cellValue
!=
null
&&
!
cellValue
.
trim
().
isEmpty
())
{
return
true
;
}
}
}
}
// 设置集合字段值
field
.
setAccessible
(
true
);
field
.
set
(
instance
,
collectionData
);
}
}
}
}
return
false
;
}
}
/**
/**
* 自动检测集合的结束行
* 解析单行模式的集合(支持结束标志字段)
* @param sheet
* @param collectionAnnotation
* @return
*/
*/
private
int
findCollectionEndRow
(
Sheet
sheet
,
ExcelCollection
collectionAnnotation
)
{
private
void
parseSingleRowCollection
(
Sheet
sheet
,
List
<
Object
>
collectionData
,
int
endRow
=
collectionAnnotation
.
startRow
();
Class
<?>
collectionType
,
int
maxEmptyRows
=
5
;
// 连续空行的最大数量
Map
<
Field
,
ExcelField
>
fieldMappings
,
ExcelCollection
collectionAnnotation
)
throws
Exception
{
// 获取所有需要检查的列
int
startRow
=
collectionAnnotation
.
startRow
();
Set
<
Integer
>
columnsToCheck
=
new
HashSet
<>();
String
endFlagField
=
collectionAnnotation
.
endFlagField
();
for
(
Field
field
:
collectionAnnotation
.
type
().
getDeclaredFields
())
{
String
nextFieldTitle
=
collectionAnnotation
.
nextFieldTitle
();
ExcelField
excelField
=
field
.
getAnnotation
(
ExcelField
.
class
);
if
(
excelField
!=
null
)
{
log
.
info
(
"=== 开始解析单行集合 ==="
);
int
col
=
excelField
.
valueCol
()
>=
0
?
excelField
.
valueCol
()
:
excelField
.
titleCol
();
log
.
info
(
"集合类型: {}, 起始行: {}, 结束标志字段: {}, 下一字段标题: {}"
,
columnsToCheck
.
add
(
col
);
collectionType
.
getSimpleName
(),
startRow
,
endFlagField
,
nextFieldTitle
);
// 获取结束标志字段的映射信息
Field
endFlagFieldObj
=
null
;
ExcelField
endFlagExcelField
=
null
;
if
(!
endFlagField
.
isEmpty
())
{
try
{
endFlagFieldObj
=
collectionType
.
getDeclaredField
(
endFlagField
);
endFlagExcelField
=
endFlagFieldObj
.
getAnnotation
(
ExcelField
.
class
);
log
.
info
(
"结束标志字段映射: 字段={}, 相对行={}, 列={}"
,
endFlagField
,
endFlagExcelField
.
valueRow
(),
endFlagExcelField
.
valueCol
());
}
catch
(
NoSuchFieldException
e
)
{
log
.
warn
(
"结束标志字段 {} 不存在,将使用默认结束判断"
,
endFlagField
);
}
}
}
}
if
(
columnsToCheck
.
isEmpty
())
{
// 输出字段映射信息
return
sheet
.
getLastRowNum
();
// 如果没有字段注解,返回最后一行
log
.
info
(
"字段映射信息:"
);
for
(
Map
.
Entry
<
Field
,
ExcelField
>
entry
:
fieldMappings
.
entrySet
())
{
Field
field
=
entry
.
getKey
();
ExcelField
excelField
=
entry
.
getValue
();
log
.
info
(
" {}: 相对行={}, 列={}"
,
field
.
getName
(),
excelField
.
valueRow
(),
excelField
.
valueCol
());
}
}
int
emptyRowCount
=
0
;
// 遍历行,解析集合元素
for
(
int
rowNum
=
collectionAnnotation
.
startRow
();
rowNum
<=
sheet
.
getLastRowNum
();
rowNum
++)
{
int
parsedCount
=
0
;
for
(
int
rowNum
=
startRow
;
rowNum
<=
sheet
.
getLastRowNum
();
rowNum
++)
{
Row
row
=
sheet
.
getRow
(
rowNum
);
Row
row
=
sheet
.
getRow
(
rowNum
);
log
.
info
(
"--- 检查行 {} ---"
,
rowNum
);
if
(
row
==
null
)
{
if
(
row
==
null
)
{
emptyRowCount
++;
log
.
info
(
"行 {} 为null,跳过"
,
rowNum
);
if
(
emptyRowCount
>=
maxEmptyRows
)
{
continue
;
return
rowNum
-
maxEmptyRows
;
// 返回连续空行前的最后一行
}
// 检查结束标志
boolean
shouldEnd
=
false
;
if
(!
endFlagField
.
isEmpty
()
&&
endFlagExcelField
!=
null
)
{
shouldEnd
=
shouldEndSingleRowByEndFlag
(
sheet
,
rowNum
,
endFlagExcelField
);
log
.
info
(
"结束标志检查结果: {}"
,
shouldEnd
);
}
// 检查下一个字段标题(新增逻辑)
if
(!
shouldEnd
&&
nextFieldTitle
!=
null
&&
!
nextFieldTitle
.
isEmpty
())
{
shouldEnd
=
shouldEndSingleRowByNextFieldTitle
(
sheet
,
rowNum
,
fieldMappings
,
nextFieldTitle
);
log
.
info
(
"下一字段标题检查结果: {}"
,
shouldEnd
);
}
if
(
shouldEnd
)
{
log
.
info
(
"检测到单行集合结束条件,集合解析结束"
);
break
;
}
try
{
Object
element
=
collectionType
.
getDeclaredConstructor
().
newInstance
();
boolean
hasData
=
parseSingleRowData
(
sheet
,
rowNum
,
element
,
fieldMappings
);
if
(
hasData
)
{
collectionData
.
add
(
element
);
parsedCount
++;
log
.
info
(
"✅ 成功解析单行集合元素,行{}: {}"
,
rowNum
,
JSON
.
toJSONString
(
element
));
}
else
{
log
.
info
(
"❌ 行 {} 没有有效数据,跳过"
,
rowNum
);
// 如果连续3行没有数据,认为集合结束
if
(!
hasDataInNextRows
(
sheet
,
rowNum
+
1
,
3
,
fieldMappings
))
{
log
.
info
(
"检测到连续空行,单行集合解析结束"
);
break
;
}
}
}
}
catch
(
Exception
e
)
{
log
.
warn
(
"解析单行集合元素失败,行{}: {}"
,
rowNum
,
e
.
getMessage
());
}
}
log
.
info
(
"=== 单行集合解析完成,共解析 {} 个元素 ==="
,
parsedCount
);
}
/**
* 检查单行模式是否应该根据下一个字段标题结束
*/
private
boolean
shouldEndSingleRowByNextFieldTitle
(
Sheet
sheet
,
int
currentRow
,
Map
<
Field
,
ExcelField
>
fieldMappings
,
String
expectedNextFieldTitle
)
{
try
{
if
(
expectedNextFieldTitle
==
null
||
expectedNextFieldTitle
.
isEmpty
())
{
return
false
;
}
// 获取第一个字段的标题位置
Field
firstField
=
getFirstFieldByColumnOrder
(
fieldMappings
);
if
(
firstField
==
null
)
{
return
false
;
}
ExcelField
firstExcelField
=
fieldMappings
.
get
(
firstField
);
int
titleRow
=
firstExcelField
.
titleRow
();
int
titleCol
=
firstExcelField
.
titleCol
();
// 检查下一行的标题
int
nextTitleRow
=
currentRow
+
1
;
if
(
nextTitleRow
>
sheet
.
getLastRowNum
())
{
return
true
;
}
Row
row
=
sheet
.
getRow
(
nextTitleRow
);
if
(
row
==
null
)
{
return
true
;
}
Cell
titleCell
=
row
.
getCell
(
titleCol
);
if
(
titleCell
==
null
)
{
return
true
;
}
String
actualTitle
=
getCellValueAsString
(
titleCell
);
// 如果下一行的标题与预期下一个对象的标题匹配,说明集合应该结束
if
(
expectedNextFieldTitle
.
equals
(
actualTitle
))
{
log
.
info
(
"检测到单行模式下一个对象标题 '{}',集合解析结束"
,
expectedNextFieldTitle
);
return
true
;
}
return
false
;
}
catch
(
Exception
e
)
{
log
.
warn
(
"检查单行模式下一个字段标题失败: {}"
,
e
.
getMessage
());
return
false
;
}
}
/**
* 检查单行模式是否应该根据结束标志结束
*/
private
boolean
shouldEndSingleRowByEndFlag
(
Sheet
sheet
,
int
currentRow
,
ExcelField
endFlagExcelField
)
{
try
{
int
actualCol
=
endFlagExcelField
.
valueCol
();
log
.
debug
(
"检查单行结束标志: 行={}, 列={}"
,
currentRow
,
actualCol
);
Row
row
=
sheet
.
getRow
(
currentRow
);
if
(
row
==
null
)
{
log
.
debug
(
"结束标志检查: 行 {} 为null"
,
currentRow
);
return
true
;
}
Cell
cell
=
row
.
getCell
(
actualCol
);
if
(
cell
==
null
)
{
log
.
debug
(
"结束标志检查: 行{}列{}为null"
,
currentRow
,
actualCol
);
return
true
;
}
String
cellValue
=
getCellValueAsString
(
cell
);
log
.
debug
(
"单行结束标志字段值: '{}'"
,
cellValue
);
// 如果结束标志字段的值为空,说明集合结束
if
(
cellValue
==
null
||
cellValue
.
trim
().
isEmpty
())
{
log
.
info
(
"单行结束标志字段为空,集合解析结束"
);
return
true
;
}
return
false
;
}
catch
(
Exception
e
)
{
log
.
warn
(
"检查单行结束标志失败: {}"
,
e
.
getMessage
());
return
false
;
}
}
/**
* 解析单行数据
*/
private
boolean
parseSingleRowData
(
Sheet
sheet
,
int
rowNum
,
Object
element
,
Map
<
Field
,
ExcelField
>
fieldMappings
)
throws
Exception
{
boolean
hasData
=
false
;
Row
row
=
sheet
.
getRow
(
rowNum
);
if
(
row
==
null
)
{
return
false
;
}
for
(
Map
.
Entry
<
Field
,
ExcelField
>
entry
:
fieldMappings
.
entrySet
())
{
Field
elementField
=
entry
.
getKey
();
ExcelField
excelField
=
entry
.
getValue
();
int
actualCol
=
excelField
.
valueCol
();
log
.
debug
(
"单行字段 {}: 行={}, 列={}"
,
elementField
.
getName
(),
rowNum
,
actualCol
);
Cell
cell
=
row
.
getCell
(
actualCol
);
if
(
cell
==
null
)
{
log
.
debug
(
"行{}列{}为null,跳过"
,
rowNum
,
actualCol
);
continue
;
continue
;
}
}
boolean
hasData
=
false
;
String
cellValue
=
getCellValueAsString
(
cell
);
for
(
int
col
:
columnsToCheck
)
{
if
(
cellValue
==
null
||
cellValue
.
trim
().
isEmpty
())
{
Cell
cell
=
row
.
getCell
(
col
);
log
.
debug
(
"行{}列{}的值为空,跳过"
,
rowNum
,
actualCol
);
if
(
cell
!=
null
&&
cell
.
getCellType
()
!=
CellType
.
BLANK
)
{
continue
;
hasData
=
true
;
}
break
;
log
.
debug
(
"单行字段 {}: 行{}列{} = '{}'"
,
elementField
.
getName
(),
rowNum
,
actualCol
,
cellValue
);
try
{
Object
value
=
convertStringValue
(
cellValue
,
elementField
.
getType
(),
excelField
.
dateFormat
());
elementField
.
setAccessible
(
true
);
elementField
.
set
(
element
,
value
);
hasData
=
true
;
log
.
debug
(
"成功设置单行字段 {}: {} -> {}"
,
elementField
.
getName
(),
cellValue
,
value
);
}
catch
(
Exception
e
)
{
log
.
error
(
"设置单行字段 {} 值失败,行{}列{},值'{}': {}"
,
elementField
.
getName
(),
rowNum
,
actualCol
,
cellValue
,
e
.
getMessage
());
}
}
return
hasData
;
}
/**
* 检查后续行是否有数据
*/
private
boolean
hasDataInNextRows
(
Sheet
sheet
,
int
startRow
,
int
checkRowCount
,
Map
<
Field
,
ExcelField
>
fieldMappings
)
{
for
(
int
i
=
0
;
i
<
checkRowCount
;
i
++)
{
int
rowNum
=
startRow
+
i
;
if
(
rowNum
>
sheet
.
getLastRowNum
())
{
return
false
;
}
Row
row
=
sheet
.
getRow
(
rowNum
);
if
(
row
==
null
)
{
continue
;
}
for
(
Map
.
Entry
<
Field
,
ExcelField
>
entry
:
fieldMappings
.
entrySet
())
{
ExcelField
excelField
=
entry
.
getValue
();
int
valueCol
=
excelField
.
valueCol
();
Cell
cell
=
row
.
getCell
(
valueCol
);
if
(
cell
!=
null
)
{
String
cellValue
=
getCellValueAsString
(
cell
);
if
(
cellValue
!=
null
&&
!
cellValue
.
trim
().
isEmpty
())
{
return
true
;
}
}
}
}
}
}
if
(
hasData
)
{
return
false
;
emptyRowCount
=
0
;
}
endRow
=
rowNum
;
}
else
{
/**
* 自动检测集合的结束行
*/
private
int
findCollectionEndRow
(
Sheet
sheet
,
int
startRow
)
{
int
maxEmptyRows
=
3
;
// 连续空行的最大数量
int
emptyRowCount
=
0
;
int
lastDataRow
=
startRow
;
for
(
int
rowNum
=
startRow
;
rowNum
<=
sheet
.
getLastRowNum
();
rowNum
++)
{
Row
row
=
sheet
.
getRow
(
rowNum
);
if
(
isRowEmpty
(
row
))
{
emptyRowCount
++;
emptyRowCount
++;
if
(
emptyRowCount
>=
maxEmptyRows
)
{
if
(
emptyRowCount
>=
maxEmptyRows
)
{
return
endRow
;
// 返回最后有数据的行
return
lastDataRow
;
}
}
}
else
{
emptyRowCount
=
0
;
lastDataRow
=
rowNum
;
}
}
}
}
return
sheet
.
getLastRowNum
();
return
sheet
.
getLastRowNum
();
}
}
/**
/**
* 单元格值转换
* 将单元格值转换为字符串
* @param cell
* @param targetType
* @param dateFormat
* @return
*/
*/
private
Object
convertCellValue
(
Cell
cell
,
Class
<?>
targetType
,
String
dateFormat
)
{
private
String
getCellValueAsString
(
Cell
cell
)
{
if
(
cell
==
null
)
{
return
null
;
}
switch
(
cell
.
getCellType
())
{
switch
(
cell
.
getCellType
())
{
case
STRING:
case
STRING:
return
c
onvertStringValue
(
cell
.
getStringCellValue
().
trim
(),
targetType
,
dateFormat
);
return
c
ell
.
getStringCellValue
().
trim
(
);
case
NUMERIC:
case
NUMERIC:
if
(
DateUtil
.
isCellDateFormatted
(
cell
))
{
if
(
DateUtil
.
isCellDateFormatted
(
cell
))
{
return
cell
.
getDateCellValue
(
);
return
new
SimpleDateFormat
(
"yyyy/MM/dd"
).
format
(
cell
.
getDateCellValue
()
);
}
else
{
}
else
{
return
convertNumericValue
(
cell
.
getNumericCellValue
(),
targetType
);
// 数字类型避免科学计数法,转换为字符串
double
numericValue
=
cell
.
getNumericCellValue
();
if
(
numericValue
==
(
long
)
numericValue
)
{
return
String
.
valueOf
((
long
)
numericValue
);
}
else
{
return
String
.
valueOf
(
numericValue
);
}
}
}
case
BOOLEAN:
case
BOOLEAN:
return
cell
.
getBooleanCellValue
(
);
return
String
.
valueOf
(
cell
.
getBooleanCellValue
()
);
case
FORMULA:
case
FORMULA:
try
{
try
{
return
c
onvertStringValue
(
cell
.
getStringCellValue
().
trim
(),
targetType
,
dateFormat
);
return
c
ell
.
getStringCellValue
().
trim
(
);
}
catch
(
Exception
e
)
{
}
catch
(
Exception
e
)
{
return
cell
.
getCellFormula
();
// 公式计算结果不是字符串时,尝试获取数值
return
String
.
valueOf
(
cell
.
getNumericCellValue
());
}
}
default
:
default
:
return
null
;
return
""
;
}
}
}
}
/**
* 字符串值类型转换
*/
private
Object
convertStringValue
(
String
value
,
Class
<?>
targetType
,
String
dateFormat
)
{
private
Object
convertStringValue
(
String
value
,
Class
<?>
targetType
,
String
dateFormat
)
{
if
(
value
==
null
||
value
.
isEmpty
())
return
null
;
if
(
value
==
null
||
value
.
isEmpty
())
return
null
;
if
(
targetType
==
String
.
class
)
{
try
{
return
value
;
if
(
targetType
==
String
.
class
)
{
}
else
if
(
targetType
==
Integer
.
class
||
targetType
==
int
.
class
)
{
return
value
;
return
Integer
.
parseInt
(
value
);
}
else
if
(
targetType
==
Integer
.
class
||
targetType
==
int
.
class
)
{
}
else
if
(
targetType
==
Long
.
class
||
targetType
==
long
.
class
)
{
// 处理可能的小数点
return
Long
.
parseLong
(
value
);
if
(
value
.
contains
(
"."
))
{
}
else
if
(
targetType
==
Double
.
class
||
targetType
==
double
.
class
)
{
return
(
int
)
Double
.
parseDouble
(
value
);
return
Double
.
parseDouble
(
value
);
}
}
else
if
(
targetType
==
BigDecimal
.
class
)
{
return
Integer
.
parseInt
(
value
);
return
new
BigDecimal
(
value
);
}
else
if
(
targetType
==
Long
.
class
||
targetType
==
long
.
class
)
{
}
else
if
(
targetType
==
Date
.
class
)
{
if
(
value
.
contains
(
"."
))
{
try
{
return
(
long
)
Double
.
parseDouble
(
value
);
SimpleDateFormat
sdf
=
new
SimpleDateFormat
(
dateFormat
);
}
return
sdf
.
parse
(
value
);
return
Long
.
parseLong
(
value
);
}
catch
(
ParseException
e
)
{
}
else
if
(
targetType
==
Double
.
class
||
targetType
==
double
.
class
)
{
log
.
warn
(
"日期格式转换失败: {}"
,
value
);
return
Double
.
parseDouble
(
value
);
return
null
;
}
else
if
(
targetType
==
BigDecimal
.
class
)
{
}
// 特殊处理数值,确保是数值
}
else
if
(
targetType
==
Boolean
.
class
||
targetType
==
boolean
.
class
)
{
try
{
return
"是"
.
equals
(
value
)
||
"YES"
.
equalsIgnoreCase
(
value
)
||
"TRUE"
.
equalsIgnoreCase
(
value
);
return
new
BigDecimal
(
value
);
}
}
catch
(
NumberFormatException
e
)
{
return
value
;
log
.
warn
(
"BigDecimal转换失败,尝试清理字符串: {}"
,
value
);
}
// 清理非数字字符(保留小数点和负号)
String
cleanValue
=
value
.
replaceAll
(
"[^\\d.-]"
,
""
);
private
Object
convertNumericValue
(
double
value
,
Class
<?>
targetType
)
{
if
(!
cleanValue
.
isEmpty
())
{
if
(
targetType
==
Integer
.
class
||
targetType
==
int
.
class
)
{
return
new
BigDecimal
(
cleanValue
);
return
(
int
)
value
;
}
}
else
if
(
targetType
==
Long
.
class
||
targetType
==
long
.
class
)
{
return
null
;
return
(
long
)
value
;
}
}
else
if
(
targetType
==
Double
.
class
||
targetType
==
double
.
class
)
{
}
else
if
(
targetType
==
Date
.
class
)
{
return
value
;
try
{
}
else
if
(
targetType
==
BigDecimal
.
class
)
{
SimpleDateFormat
sdf
=
new
SimpleDateFormat
(
dateFormat
);
return
BigDecimal
.
valueOf
(
value
);
return
sdf
.
parse
(
value
);
}
else
if
(
targetType
==
Date
.
class
)
{
}
catch
(
ParseException
e
)
{
return
DateUtil
.
getJavaDate
(
value
);
log
.
warn
(
"日期格式转换失败: {}"
,
value
);
return
null
;
}
}
else
if
(
targetType
==
Boolean
.
class
||
targetType
==
boolean
.
class
)
{
return
"是"
.
equals
(
value
)
||
"YES"
.
equalsIgnoreCase
(
value
)
||
"TRUE"
.
equalsIgnoreCase
(
value
)
||
"1"
.
equals
(
value
);
}
}
catch
(
Exception
e
)
{
log
.
warn
(
"类型转换失败: 值='{}', 目标类型={}, 错误: {}"
,
value
,
targetType
.
getSimpleName
(),
e
.
getMessage
());
}
}
return
value
;
return
null
;
}
}
/**
* 检查行是否为空
*/
private
boolean
isRowEmpty
(
Row
row
)
{
private
boolean
isRowEmpty
(
Row
row
)
{
if
(
row
==
null
)
return
true
;
for
(
Cell
cell
:
row
)
{
for
(
Cell
cell
:
row
)
{
if
(
cell
!=
null
&&
cell
.
getCellType
()
!=
CellType
.
BLANK
)
{
if
(
cell
!=
null
&&
cell
.
getCellType
()
!=
CellType
.
BLANK
)
{
return
false
;
String
value
=
getCellValueAsString
(
cell
);
if
(
value
!=
null
&&
!
value
.
trim
().
isEmpty
())
{
return
false
;
}
}
}
}
}
return
true
;
return
true
;
}
}
}
}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment