Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
杨图强
ewaytek-deepseek
Commits
0487b6d5
Commit
0487b6d5
authored
Aug 28, 2025
by
何处是我家
Browse files
提交
parents
Changes
148
Show whitespace changes
Inline
Side-by-side
ewaytek-deepseek-common/src/main/java/com/ewaytek/deepseek/common/utils/poi/ExcelUtil.java
0 → 100644
View file @
0487b6d5
package
com.ewaytek.deepseek.common.utils.poi
;
import
java.io.File
;
import
java.io.FileOutputStream
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.OutputStream
;
import
java.lang.reflect.Field
;
import
java.lang.reflect.Method
;
import
java.lang.reflect.ParameterizedType
;
import
java.math.BigDecimal
;
import
java.text.DecimalFormat
;
import
java.time.LocalDate
;
import
java.time.LocalDateTime
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.Collection
;
import
java.util.Comparator
;
import
java.util.Date
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Set
;
import
java.util.UUID
;
import
java.util.stream.Collectors
;
import
javax.servlet.http.HttpServletResponse
;
import
com.ewaytek.deepseek.common.bean.base.ApiResponse
;
import
com.ewaytek.deepseek.common.config.DeepseekConfig
;
import
com.ewaytek.deepseek.common.core.text.Convert
;
import
com.ewaytek.deepseek.common.exception.UtilException
;
import
com.ewaytek.deepseek.common.utils.DateUtils
;
import
com.ewaytek.deepseek.common.utils.StringUtils
;
import
com.ewaytek.deepseek.common.utils.file.FileTypeUtils
;
import
com.ewaytek.deepseek.common.utils.file.FileUtils
;
import
com.ewaytek.deepseek.common.utils.file.ImageUtils
;
import
com.ewaytek.deepseek.common.utils.reflect.ReflectUtils
;
import
org.apache.commons.lang3.ArrayUtils
;
import
org.apache.commons.lang3.RegExUtils
;
import
org.apache.commons.lang3.reflect.FieldUtils
;
import
org.apache.poi.hssf.usermodel.HSSFClientAnchor
;
import
org.apache.poi.hssf.usermodel.HSSFPicture
;
import
org.apache.poi.hssf.usermodel.HSSFPictureData
;
import
org.apache.poi.hssf.usermodel.HSSFShape
;
import
org.apache.poi.hssf.usermodel.HSSFSheet
;
import
org.apache.poi.hssf.usermodel.HSSFWorkbook
;
import
org.apache.poi.ooxml.POIXMLDocumentPart
;
import
org.apache.poi.ss.usermodel.BorderStyle
;
import
org.apache.poi.ss.usermodel.Cell
;
import
org.apache.poi.ss.usermodel.CellStyle
;
import
org.apache.poi.ss.usermodel.CellType
;
import
org.apache.poi.ss.usermodel.ClientAnchor
;
import
org.apache.poi.ss.usermodel.DataFormat
;
import
org.apache.poi.ss.usermodel.DataValidation
;
import
org.apache.poi.ss.usermodel.DataValidationConstraint
;
import
org.apache.poi.ss.usermodel.DataValidationHelper
;
import
org.apache.poi.ss.usermodel.DateUtil
;
import
org.apache.poi.ss.usermodel.Drawing
;
import
org.apache.poi.ss.usermodel.FillPatternType
;
import
org.apache.poi.ss.usermodel.Font
;
import
org.apache.poi.ss.usermodel.HorizontalAlignment
;
import
org.apache.poi.ss.usermodel.IndexedColors
;
import
org.apache.poi.ss.usermodel.Name
;
import
org.apache.poi.ss.usermodel.PictureData
;
import
org.apache.poi.ss.usermodel.Row
;
import
org.apache.poi.ss.usermodel.Sheet
;
import
org.apache.poi.ss.usermodel.VerticalAlignment
;
import
org.apache.poi.ss.usermodel.Workbook
;
import
org.apache.poi.ss.usermodel.WorkbookFactory
;
import
org.apache.poi.ss.util.CellRangeAddress
;
import
org.apache.poi.ss.util.CellRangeAddressList
;
import
org.apache.poi.util.IOUtils
;
import
org.apache.poi.xssf.streaming.SXSSFWorkbook
;
import
org.apache.poi.xssf.usermodel.XSSFClientAnchor
;
import
org.apache.poi.xssf.usermodel.XSSFDataValidation
;
import
org.apache.poi.xssf.usermodel.XSSFDrawing
;
import
org.apache.poi.xssf.usermodel.XSSFPicture
;
import
org.apache.poi.xssf.usermodel.XSSFShape
;
import
org.apache.poi.xssf.usermodel.XSSFSheet
;
import
org.apache.poi.xssf.usermodel.XSSFWorkbook
;
import
org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
com.ewaytek.deepseek.common.annotation.Excel
;
import
com.ewaytek.deepseek.common.annotation.Excel.ColumnType
;
import
com.ewaytek.deepseek.common.annotation.Excel.Type
;
import
com.ewaytek.deepseek.common.annotation.Excels
;
/**
* Excel相关处理
*
* @author ruoyi
*/
public
class
ExcelUtil
<
T
>
{
private
static
final
Logger
log
=
LoggerFactory
.
getLogger
(
ExcelUtil
.
class
);
public
static
final
String
FORMULA_REGEX_STR
=
"=|-|\\+|@"
;
public
static
final
String
[]
FORMULA_STR
=
{
"="
,
"-"
,
"+"
,
"@"
};
/**
* 用于dictType属性数据存储,避免重复查缓存
*/
public
Map
<
String
,
String
>
sysDictMap
=
new
HashMap
<
String
,
String
>();
/**
* Excel sheet最大行数,默认65536
*/
public
static
final
int
sheetSize
=
65536
;
/**
* 工作表名称
*/
private
String
sheetName
;
/**
* 导出类型(EXPORT:导出数据;IMPORT:导入模板)
*/
private
Type
type
;
/**
* 工作薄对象
*/
private
Workbook
wb
;
/**
* 工作表对象
*/
private
Sheet
sheet
;
/**
* 样式列表
*/
private
Map
<
String
,
CellStyle
>
styles
;
/**
* 导入导出数据列表
*/
private
List
<
T
>
list
;
/**
* 注解列表
*/
private
List
<
Object
[]>
fields
;
/**
* 当前行号
*/
private
int
rownum
;
/**
* 标题
*/
private
String
title
;
/**
* 最大高度
*/
private
short
maxHeight
;
/**
* 合并后最后行数
*/
private
int
subMergedLastRowNum
=
0
;
/**
* 合并后开始行数
*/
private
int
subMergedFirstRowNum
=
1
;
/**
* 对象的子列表方法
*/
private
Method
subMethod
;
/**
* 对象的子列表属性
*/
private
List
<
Field
>
subFields
;
/**
* 统计列表
*/
private
Map
<
Integer
,
Double
>
statistics
=
new
HashMap
<
Integer
,
Double
>();
/**
* 数字格式
*/
private
static
final
DecimalFormat
DOUBLE_FORMAT
=
new
DecimalFormat
(
"######0.00"
);
/**
* 实体对象
*/
public
Class
<
T
>
clazz
;
/**
* 需要显示列属性
*/
public
String
[]
includeFields
;
/**
* 需要排除列属性
*/
public
String
[]
excludeFields
;
public
ExcelUtil
(
Class
<
T
>
clazz
)
{
this
.
clazz
=
clazz
;
}
/**
* 仅在Excel中显示列属性
*
* @param fields 列属性名 示例[单个"name"/多个"id","name"]
*/
public
void
showColumn
(
String
...
fields
)
{
this
.
includeFields
=
fields
;
}
/**
* 隐藏Excel中列属性
*
* @param fields 列属性名 示例[单个"name"/多个"id","name"]
*/
public
void
hideColumn
(
String
...
fields
)
{
this
.
excludeFields
=
fields
;
}
public
void
init
(
List
<
T
>
list
,
String
sheetName
,
String
title
,
Type
type
)
{
if
(
list
==
null
)
{
list
=
new
ArrayList
<
T
>();
}
this
.
list
=
list
;
this
.
sheetName
=
sheetName
;
this
.
type
=
type
;
this
.
title
=
title
;
createExcelField
();
createWorkbook
();
createTitle
();
createSubHead
();
}
/**
* 创建excel第一行标题
*/
public
void
createTitle
()
{
if
(
StringUtils
.
isNotEmpty
(
title
))
{
int
titleLastCol
=
this
.
fields
.
size
()
-
1
;
if
(
isSubList
())
{
titleLastCol
=
titleLastCol
+
subFields
.
size
()
-
1
;
}
Row
titleRow
=
sheet
.
createRow
(
rownum
==
0
?
rownum
++
:
0
);
titleRow
.
setHeightInPoints
(
30
);
Cell
titleCell
=
titleRow
.
createCell
(
0
);
titleCell
.
setCellStyle
(
styles
.
get
(
"title"
));
titleCell
.
setCellValue
(
title
);
sheet
.
addMergedRegion
(
new
CellRangeAddress
(
titleRow
.
getRowNum
(),
titleRow
.
getRowNum
(),
0
,
titleLastCol
));
}
}
/**
* 创建对象的子列表名称
*/
public
void
createSubHead
()
{
if
(
isSubList
())
{
Row
subRow
=
sheet
.
createRow
(
rownum
);
int
column
=
0
;
int
subFieldSize
=
subFields
!=
null
?
subFields
.
size
()
:
0
;
for
(
Object
[]
objects
:
fields
)
{
Field
field
=
(
Field
)
objects
[
0
];
Excel
attr
=
(
Excel
)
objects
[
1
];
if
(
Collection
.
class
.
isAssignableFrom
(
field
.
getType
()))
{
Cell
cell
=
subRow
.
createCell
(
column
);
cell
.
setCellValue
(
attr
.
name
());
cell
.
setCellStyle
(
styles
.
get
(
StringUtils
.
format
(
"header_{}_{}"
,
attr
.
headerColor
(),
attr
.
headerBackgroundColor
())));
if
(
subFieldSize
>
1
)
{
CellRangeAddress
cellAddress
=
new
CellRangeAddress
(
rownum
,
rownum
,
column
,
column
+
subFieldSize
-
1
);
sheet
.
addMergedRegion
(
cellAddress
);
}
column
+=
subFieldSize
;
}
else
{
Cell
cell
=
subRow
.
createCell
(
column
++);
cell
.
setCellValue
(
attr
.
name
());
cell
.
setCellStyle
(
styles
.
get
(
StringUtils
.
format
(
"header_{}_{}"
,
attr
.
headerColor
(),
attr
.
headerBackgroundColor
())));
}
}
rownum
++;
}
}
/**
* 对excel表单默认第一个索引名转换成list
*
* @param is 输入流
* @return 转换后集合
*/
public
List
<
T
>
importExcel
(
InputStream
is
)
{
return
importExcel
(
is
,
0
);
}
/**
* 对excel表单默认第一个索引名转换成list
*
* @param is 输入流
* @param titleNum 标题占用行数
* @return 转换后集合
*/
public
List
<
T
>
importExcel
(
InputStream
is
,
int
titleNum
)
{
List
<
T
>
list
=
null
;
try
{
list
=
importExcel
(
StringUtils
.
EMPTY
,
is
,
titleNum
);
}
catch
(
Exception
e
)
{
log
.
error
(
"导入Excel异常{}"
,
e
.
getMessage
());
throw
new
UtilException
(
e
.
getMessage
());
}
finally
{
IOUtils
.
closeQuietly
(
is
);
}
return
list
;
}
/**
* 对excel表单指定表格索引名转换成list
*
* @param sheetName 表格索引名
* @param titleNum 标题占用行数
* @param is 输入流
* @return 转换后集合
*/
public
List
<
T
>
importExcel
(
String
sheetName
,
InputStream
is
,
int
titleNum
)
throws
Exception
{
this
.
type
=
Type
.
IMPORT
;
this
.
wb
=
WorkbookFactory
.
create
(
is
);
List
<
T
>
list
=
new
ArrayList
<
T
>();
// 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet
Sheet
sheet
=
StringUtils
.
isNotEmpty
(
sheetName
)
?
wb
.
getSheet
(
sheetName
)
:
wb
.
getSheetAt
(
0
);
if
(
sheet
==
null
)
{
throw
new
IOException
(
"文件sheet不存在"
);
}
boolean
isXSSFWorkbook
=
!(
wb
instanceof
HSSFWorkbook
);
Map
<
String
,
PictureData
>
pictures
;
if
(
isXSSFWorkbook
)
{
pictures
=
getSheetPictures07
((
XSSFSheet
)
sheet
,
(
XSSFWorkbook
)
wb
);
}
else
{
pictures
=
getSheetPictures03
((
HSSFSheet
)
sheet
,
(
HSSFWorkbook
)
wb
);
}
// 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1
int
rows
=
sheet
.
getLastRowNum
();
if
(
rows
>
0
)
{
// 定义一个map用于存放excel列的序号和field.
Map
<
String
,
Integer
>
cellMap
=
new
HashMap
<
String
,
Integer
>();
// 获取表头
Row
heard
=
sheet
.
getRow
(
titleNum
);
for
(
int
i
=
0
;
i
<
heard
.
getPhysicalNumberOfCells
();
i
++)
{
Cell
cell
=
heard
.
getCell
(
i
);
if
(
StringUtils
.
isNotNull
(
cell
))
{
String
value
=
this
.
getCellValue
(
heard
,
i
).
toString
();
cellMap
.
put
(
value
,
i
);
}
else
{
cellMap
.
put
(
null
,
i
);
}
}
// 有数据时才处理 得到类的所有field.
List
<
Object
[]>
fields
=
this
.
getFields
();
Map
<
Integer
,
Object
[]>
fieldsMap
=
new
HashMap
<
Integer
,
Object
[]>();
for
(
Object
[]
objects
:
fields
)
{
Excel
attr
=
(
Excel
)
objects
[
1
];
Integer
column
=
cellMap
.
get
(
attr
.
name
());
if
(
column
!=
null
)
{
fieldsMap
.
put
(
column
,
objects
);
}
}
for
(
int
i
=
titleNum
+
1
;
i
<=
rows
;
i
++)
{
// 从第2行开始取数据,默认第一行是表头.
Row
row
=
sheet
.
getRow
(
i
);
// 判断当前行是否是空行
if
(
isRowEmpty
(
row
))
{
continue
;
}
T
entity
=
null
;
for
(
Map
.
Entry
<
Integer
,
Object
[]>
entry
:
fieldsMap
.
entrySet
())
{
Object
val
=
this
.
getCellValue
(
row
,
entry
.
getKey
());
// 如果不存在实例则新建.
entity
=
(
entity
==
null
?
clazz
.
newInstance
()
:
entity
);
// 从map中得到对应列的field.
Field
field
=
(
Field
)
entry
.
getValue
()[
0
];
Excel
attr
=
(
Excel
)
entry
.
getValue
()[
1
];
// 取得类型,并根据对象类型设置值.
Class
<?>
fieldType
=
field
.
getType
();
if
(
String
.
class
==
fieldType
)
{
String
s
=
Convert
.
toStr
(
val
);
if
(
StringUtils
.
endsWith
(
s
,
".0"
))
{
val
=
StringUtils
.
substringBefore
(
s
,
".0"
);
}
else
{
String
dateFormat
=
field
.
getAnnotation
(
Excel
.
class
).
dateFormat
();
if
(
StringUtils
.
isNotEmpty
(
dateFormat
))
{
val
=
parseDateToStr
(
dateFormat
,
val
);
}
else
{
val
=
Convert
.
toStr
(
val
);
}
}
}
else
if
((
Integer
.
TYPE
==
fieldType
||
Integer
.
class
==
fieldType
)
&&
StringUtils
.
isNumeric
(
Convert
.
toStr
(
val
)))
{
val
=
Convert
.
toInt
(
val
);
}
else
if
((
Long
.
TYPE
==
fieldType
||
Long
.
class
==
fieldType
)
&&
StringUtils
.
isNumeric
(
Convert
.
toStr
(
val
)))
{
val
=
Convert
.
toLong
(
val
);
}
else
if
(
Double
.
TYPE
==
fieldType
||
Double
.
class
==
fieldType
)
{
val
=
Convert
.
toDouble
(
val
);
}
else
if
(
Float
.
TYPE
==
fieldType
||
Float
.
class
==
fieldType
)
{
val
=
Convert
.
toFloat
(
val
);
}
else
if
(
BigDecimal
.
class
==
fieldType
)
{
val
=
Convert
.
toBigDecimal
(
val
);
}
else
if
(
Date
.
class
==
fieldType
)
{
if
(
val
instanceof
String
)
{
val
=
DateUtils
.
parseDate
(
val
);
}
else
if
(
val
instanceof
Double
)
{
val
=
DateUtil
.
getJavaDate
((
Double
)
val
);
}
}
else
if
(
Boolean
.
TYPE
==
fieldType
||
Boolean
.
class
==
fieldType
)
{
val
=
Convert
.
toBool
(
val
,
false
);
}
if
(
StringUtils
.
isNotNull
(
fieldType
))
{
String
propertyName
=
field
.
getName
();
if
(
StringUtils
.
isNotEmpty
(
attr
.
targetAttr
()))
{
propertyName
=
field
.
getName
()
+
"."
+
attr
.
targetAttr
();
}
if
(
StringUtils
.
isNotEmpty
(
attr
.
readConverterExp
()))
{
val
=
reverseByExp
(
Convert
.
toStr
(
val
),
attr
.
readConverterExp
(),
attr
.
separator
());
}
else
if
(
StringUtils
.
isNotEmpty
(
attr
.
dictType
()))
{
if
(!
sysDictMap
.
containsKey
(
attr
.
dictType
()
+
val
))
{
// String dictValue = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator());
// sysDictMap.put(attr.dictType() + val, dictValue);
}
val
=
sysDictMap
.
get
(
attr
.
dictType
()
+
val
);
}
else
if
(!
attr
.
handler
().
equals
(
ExcelHandlerAdapter
.
class
))
{
val
=
dataFormatHandlerAdapter
(
val
,
attr
,
null
);
}
else
if
(
ColumnType
.
IMAGE
==
attr
.
cellType
()
&&
StringUtils
.
isNotEmpty
(
pictures
))
{
PictureData
image
=
pictures
.
get
(
row
.
getRowNum
()
+
"_"
+
entry
.
getKey
());
if
(
image
==
null
)
{
val
=
""
;
}
else
{
byte
[]
data
=
image
.
getData
();
val
=
FileUtils
.
writeImportBytes
(
data
);
}
}
ReflectUtils
.
invokeSetter
(
entity
,
propertyName
,
val
);
}
}
list
.
add
(
entity
);
}
}
return
list
;
}
/**
* 对list数据源将其里面的数据导入到excel表单
*
* @param list 导出数据集合
* @param sheetName 工作表的名称
* @return 结果
*/
public
ApiResponse
exportExcel
(
List
<
T
>
list
,
String
sheetName
)
{
return
exportExcel
(
list
,
sheetName
,
StringUtils
.
EMPTY
);
}
/**
* 对list数据源将其里面的数据导入到excel表单
*
* @param list 导出数据集合
* @param sheetName 工作表的名称
* @param title 标题
* @return 结果
*/
public
ApiResponse
exportExcel
(
List
<
T
>
list
,
String
sheetName
,
String
title
)
{
this
.
init
(
list
,
sheetName
,
title
,
Type
.
EXPORT
);
return
exportExcel
();
}
/**
* 对list数据源将其里面的数据导入到excel表单
*
* @param response 返回数据
* @param list 导出数据集合
* @param sheetName 工作表的名称
* @return 结果
*/
public
void
exportExcel
(
HttpServletResponse
response
,
List
<
T
>
list
,
String
sheetName
)
{
exportExcel
(
response
,
list
,
sheetName
,
StringUtils
.
EMPTY
);
}
/**
* 对list数据源将其里面的数据导入到excel表单
*
* @param response 返回数据
* @param list 导出数据集合
* @param sheetName 工作表的名称
* @param title 标题
* @return 结果
*/
public
void
exportExcel
(
HttpServletResponse
response
,
List
<
T
>
list
,
String
sheetName
,
String
title
)
{
response
.
setContentType
(
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
);
response
.
setCharacterEncoding
(
"utf-8"
);
this
.
init
(
list
,
sheetName
,
title
,
Type
.
EXPORT
);
exportExcel
(
response
);
}
/**
* 对list数据源将其里面的数据导入到excel表单
*
* @param sheetName 工作表的名称
* @return 结果
*/
public
ApiResponse
importTemplateExcel
(
String
sheetName
)
{
return
importTemplateExcel
(
sheetName
,
StringUtils
.
EMPTY
);
}
/**
* 对list数据源将其里面的数据导入到excel表单
*
* @param sheetName 工作表的名称
* @param title 标题
* @return 结果
*/
public
ApiResponse
importTemplateExcel
(
String
sheetName
,
String
title
)
{
this
.
init
(
null
,
sheetName
,
title
,
Type
.
IMPORT
);
return
exportExcel
();
}
/**
* 对list数据源将其里面的数据导入到excel表单
*
* @param sheetName 工作表的名称
* @return 结果
*/
public
void
importTemplateExcel
(
HttpServletResponse
response
,
String
sheetName
)
{
importTemplateExcel
(
response
,
sheetName
,
StringUtils
.
EMPTY
);
}
/**
* 对list数据源将其里面的数据导入到excel表单
*
* @param sheetName 工作表的名称
* @param title 标题
* @return 结果
*/
public
void
importTemplateExcel
(
HttpServletResponse
response
,
String
sheetName
,
String
title
)
{
response
.
setContentType
(
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
);
response
.
setCharacterEncoding
(
"utf-8"
);
this
.
init
(
null
,
sheetName
,
title
,
Type
.
IMPORT
);
exportExcel
(
response
);
}
/**
* 对list数据源将其里面的数据导入到excel表单
*
* @return 结果
*/
public
void
exportExcel
(
HttpServletResponse
response
)
{
try
{
writeSheet
();
wb
.
write
(
response
.
getOutputStream
());
}
catch
(
Exception
e
)
{
log
.
error
(
"导出Excel异常{}"
,
e
.
getMessage
());
}
finally
{
IOUtils
.
closeQuietly
(
wb
);
}
}
/**
* 对list数据源将其里面的数据导入到excel表单
*
* @return 结果
*/
public
ApiResponse
exportExcel
()
{
OutputStream
out
=
null
;
try
{
writeSheet
();
String
filename
=
encodingFilename
(
sheetName
);
out
=
new
FileOutputStream
(
getAbsoluteFile
(
filename
));
wb
.
write
(
out
);
return
ApiResponse
.
success
(
filename
);
}
catch
(
Exception
e
)
{
log
.
error
(
"导出Excel异常{}"
,
e
.
getMessage
());
throw
new
UtilException
(
"导出Excel失败,请联系网站管理员!"
);
}
finally
{
IOUtils
.
closeQuietly
(
wb
);
IOUtils
.
closeQuietly
(
out
);
}
}
/**
* 创建写入数据到Sheet
*/
public
void
writeSheet
()
{
// 取出一共有多少个sheet.
int
sheetNo
=
Math
.
max
(
1
,
(
int
)
Math
.
ceil
(
list
.
size
()
*
1.0
/
sheetSize
));
for
(
int
index
=
0
;
index
<
sheetNo
;
index
++)
{
createSheet
(
sheetNo
,
index
);
// 产生一行
Row
row
=
sheet
.
createRow
(
rownum
);
int
column
=
0
;
// 写入各个字段的列头名称
for
(
Object
[]
os
:
fields
)
{
Field
field
=
(
Field
)
os
[
0
];
Excel
excel
=
(
Excel
)
os
[
1
];
if
(
Collection
.
class
.
isAssignableFrom
(
field
.
getType
()))
{
for
(
Field
subField
:
subFields
)
{
Excel
subExcel
=
subField
.
getAnnotation
(
Excel
.
class
);
this
.
createHeadCell
(
subExcel
,
row
,
column
++);
}
}
else
{
this
.
createHeadCell
(
excel
,
row
,
column
++);
}
}
if
(
Type
.
EXPORT
.
equals
(
type
))
{
fillExcelData
(
index
,
row
);
addStatisticsRow
();
}
}
}
/**
* 填充excel数据
*
* @param index 序号
* @param row 单元格行
*/
@SuppressWarnings
(
"unchecked"
)
public
void
fillExcelData
(
int
index
,
Row
row
)
{
int
startNo
=
index
*
sheetSize
;
int
endNo
=
Math
.
min
(
startNo
+
sheetSize
,
list
.
size
());
int
currentRowNum
=
rownum
+
1
;
// 从标题行后开始
for
(
int
i
=
startNo
;
i
<
endNo
;
i
++)
{
row
=
sheet
.
createRow
(
currentRowNum
);
T
vo
=
(
T
)
list
.
get
(
i
);
int
column
=
0
;
int
maxSubListSize
=
getCurrentMaxSubListSize
(
vo
);
for
(
Object
[]
os
:
fields
)
{
Field
field
=
(
Field
)
os
[
0
];
Excel
excel
=
(
Excel
)
os
[
1
];
if
(
Collection
.
class
.
isAssignableFrom
(
field
.
getType
()))
{
try
{
Collection
<?>
subList
=
(
Collection
<?>)
getTargetValue
(
vo
,
field
,
excel
);
if
(
subList
!=
null
&&
!
subList
.
isEmpty
())
{
int
subIndex
=
0
;
for
(
Object
subVo
:
subList
)
{
Row
subRow
=
sheet
.
getRow
(
currentRowNum
+
subIndex
);
if
(
subRow
==
null
)
{
subRow
=
sheet
.
createRow
(
currentRowNum
+
subIndex
);
}
int
subColumn
=
column
;
for
(
Field
subField
:
subFields
)
{
Excel
subExcel
=
subField
.
getAnnotation
(
Excel
.
class
);
addCell
(
subExcel
,
subRow
,
(
T
)
subVo
,
subField
,
subColumn
++);
}
subIndex
++;
}
column
+=
subFields
.
size
();
}
}
catch
(
Exception
e
)
{
log
.
error
(
"填充集合数据失败"
,
e
);
}
}
else
{
// 创建单元格并设置值
addCell
(
excel
,
row
,
vo
,
field
,
column
);
if
(
maxSubListSize
>
1
&&
excel
.
needMerge
())
{
sheet
.
addMergedRegion
(
new
CellRangeAddress
(
currentRowNum
,
currentRowNum
+
maxSubListSize
-
1
,
column
,
column
));
}
column
++;
}
}
currentRowNum
+=
maxSubListSize
;
}
}
/**
* 获取子列表最大数
*/
private
int
getCurrentMaxSubListSize
(
T
vo
)
{
int
maxSubListSize
=
1
;
for
(
Object
[]
os
:
fields
)
{
Field
field
=
(
Field
)
os
[
0
];
if
(
Collection
.
class
.
isAssignableFrom
(
field
.
getType
()))
{
try
{
Collection
<?>
subList
=
(
Collection
<?>)
getTargetValue
(
vo
,
field
,
(
Excel
)
os
[
1
]);
if
(
subList
!=
null
&&
!
subList
.
isEmpty
())
{
maxSubListSize
=
Math
.
max
(
maxSubListSize
,
subList
.
size
());
}
}
catch
(
Exception
e
)
{
log
.
error
(
"获取集合大小失败"
,
e
);
}
}
}
return
maxSubListSize
;
}
/**
* 创建表格样式
*
* @param wb 工作薄对象
* @return 样式列表
*/
private
Map
<
String
,
CellStyle
>
createStyles
(
Workbook
wb
)
{
// 写入各条记录,每条记录对应excel表中的一行
Map
<
String
,
CellStyle
>
styles
=
new
HashMap
<
String
,
CellStyle
>();
CellStyle
style
=
wb
.
createCellStyle
();
style
.
setAlignment
(
HorizontalAlignment
.
CENTER
);
style
.
setVerticalAlignment
(
VerticalAlignment
.
CENTER
);
Font
titleFont
=
wb
.
createFont
();
titleFont
.
setFontName
(
"Arial"
);
titleFont
.
setFontHeightInPoints
((
short
)
16
);
titleFont
.
setBold
(
true
);
style
.
setFont
(
titleFont
);
DataFormat
dataFormat
=
wb
.
createDataFormat
();
style
.
setDataFormat
(
dataFormat
.
getFormat
(
"@"
));
styles
.
put
(
"title"
,
style
);
style
=
wb
.
createCellStyle
();
style
.
setAlignment
(
HorizontalAlignment
.
CENTER
);
style
.
setVerticalAlignment
(
VerticalAlignment
.
CENTER
);
style
.
setBorderRight
(
BorderStyle
.
THIN
);
style
.
setRightBorderColor
(
IndexedColors
.
GREY_50_PERCENT
.
getIndex
());
style
.
setBorderLeft
(
BorderStyle
.
THIN
);
style
.
setLeftBorderColor
(
IndexedColors
.
GREY_50_PERCENT
.
getIndex
());
style
.
setBorderTop
(
BorderStyle
.
THIN
);
style
.
setTopBorderColor
(
IndexedColors
.
GREY_50_PERCENT
.
getIndex
());
style
.
setBorderBottom
(
BorderStyle
.
THIN
);
style
.
setBottomBorderColor
(
IndexedColors
.
GREY_50_PERCENT
.
getIndex
());
Font
dataFont
=
wb
.
createFont
();
dataFont
.
setFontName
(
"Arial"
);
dataFont
.
setFontHeightInPoints
((
short
)
10
);
style
.
setFont
(
dataFont
);
styles
.
put
(
"data"
,
style
);
style
=
wb
.
createCellStyle
();
style
.
setAlignment
(
HorizontalAlignment
.
CENTER
);
style
.
setVerticalAlignment
(
VerticalAlignment
.
CENTER
);
Font
totalFont
=
wb
.
createFont
();
totalFont
.
setFontName
(
"Arial"
);
totalFont
.
setFontHeightInPoints
((
short
)
10
);
style
.
setFont
(
totalFont
);
styles
.
put
(
"total"
,
style
);
styles
.
putAll
(
annotationHeaderStyles
(
wb
,
styles
));
styles
.
putAll
(
annotationDataStyles
(
wb
));
return
styles
;
}
/**
* 根据Excel注解创建表格头样式
*
* @param wb 工作薄对象
* @return 自定义样式列表
*/
private
Map
<
String
,
CellStyle
>
annotationHeaderStyles
(
Workbook
wb
,
Map
<
String
,
CellStyle
>
styles
)
{
Map
<
String
,
CellStyle
>
headerStyles
=
new
HashMap
<
String
,
CellStyle
>();
for
(
Object
[]
os
:
fields
)
{
Excel
excel
=
(
Excel
)
os
[
1
];
String
key
=
StringUtils
.
format
(
"header_{}_{}"
,
excel
.
headerColor
(),
excel
.
headerBackgroundColor
());
if
(!
headerStyles
.
containsKey
(
key
))
{
CellStyle
style
=
wb
.
createCellStyle
();
style
.
cloneStyleFrom
(
styles
.
get
(
"data"
));
style
.
setAlignment
(
HorizontalAlignment
.
CENTER
);
style
.
setVerticalAlignment
(
VerticalAlignment
.
CENTER
);
style
.
setFillForegroundColor
(
excel
.
headerBackgroundColor
().
index
);
style
.
setFillPattern
(
FillPatternType
.
SOLID_FOREGROUND
);
Font
headerFont
=
wb
.
createFont
();
headerFont
.
setFontName
(
"Arial"
);
headerFont
.
setFontHeightInPoints
((
short
)
10
);
headerFont
.
setBold
(
true
);
headerFont
.
setColor
(
excel
.
headerColor
().
index
);
style
.
setFont
(
headerFont
);
// 设置表格头单元格文本形式
DataFormat
dataFormat
=
wb
.
createDataFormat
();
style
.
setDataFormat
(
dataFormat
.
getFormat
(
"@"
));
headerStyles
.
put
(
key
,
style
);
}
}
return
headerStyles
;
}
/**
* 根据Excel注解创建表格列样式
*
* @param wb 工作薄对象
* @return 自定义样式列表
*/
private
Map
<
String
,
CellStyle
>
annotationDataStyles
(
Workbook
wb
)
{
Map
<
String
,
CellStyle
>
styles
=
new
HashMap
<
String
,
CellStyle
>();
for
(
Object
[]
os
:
fields
)
{
Field
field
=
(
Field
)
os
[
0
];
Excel
excel
=
(
Excel
)
os
[
1
];
if
(
Collection
.
class
.
isAssignableFrom
(
field
.
getType
()))
{
ParameterizedType
pt
=
(
ParameterizedType
)
field
.
getGenericType
();
Class
<?>
subClass
=
(
Class
<?>)
pt
.
getActualTypeArguments
()[
0
];
List
<
Field
>
subFields
=
FieldUtils
.
getFieldsListWithAnnotation
(
subClass
,
Excel
.
class
);
for
(
Field
subField
:
subFields
)
{
Excel
subExcel
=
subField
.
getAnnotation
(
Excel
.
class
);
annotationDataStyles
(
styles
,
subField
,
subExcel
);
}
}
else
{
annotationDataStyles
(
styles
,
field
,
excel
);
}
}
return
styles
;
}
/**
* 根据Excel注解创建表格列样式
*
* @param styles 自定义样式列表
* @param field 属性列信息
* @param excel 注解信息
*/
public
void
annotationDataStyles
(
Map
<
String
,
CellStyle
>
styles
,
Field
field
,
Excel
excel
)
{
String
key
=
StringUtils
.
format
(
"data_{}_{}_{}_{}_{}"
,
excel
.
align
(),
excel
.
color
(),
excel
.
backgroundColor
(),
excel
.
cellType
(),
excel
.
wrapText
());
if
(!
styles
.
containsKey
(
key
))
{
CellStyle
style
=
wb
.
createCellStyle
();
style
.
setAlignment
(
excel
.
align
());
style
.
setVerticalAlignment
(
VerticalAlignment
.
CENTER
);
style
.
setBorderRight
(
BorderStyle
.
THIN
);
style
.
setRightBorderColor
(
IndexedColors
.
GREY_50_PERCENT
.
getIndex
());
style
.
setBorderLeft
(
BorderStyle
.
THIN
);
style
.
setLeftBorderColor
(
IndexedColors
.
GREY_50_PERCENT
.
getIndex
());
style
.
setBorderTop
(
BorderStyle
.
THIN
);
style
.
setTopBorderColor
(
IndexedColors
.
GREY_50_PERCENT
.
getIndex
());
style
.
setBorderBottom
(
BorderStyle
.
THIN
);
style
.
setBottomBorderColor
(
IndexedColors
.
GREY_50_PERCENT
.
getIndex
());
style
.
setFillPattern
(
FillPatternType
.
SOLID_FOREGROUND
);
style
.
setFillForegroundColor
(
excel
.
backgroundColor
().
getIndex
());
style
.
setWrapText
(
excel
.
wrapText
());
Font
dataFont
=
wb
.
createFont
();
dataFont
.
setFontName
(
"Arial"
);
dataFont
.
setFontHeightInPoints
((
short
)
10
);
dataFont
.
setColor
(
excel
.
color
().
index
);
style
.
setFont
(
dataFont
);
if
(
ColumnType
.
TEXT
==
excel
.
cellType
())
{
DataFormat
dataFormat
=
wb
.
createDataFormat
();
style
.
setDataFormat
(
dataFormat
.
getFormat
(
"@"
));
}
styles
.
put
(
key
,
style
);
}
}
/**
* 创建单元格
*/
public
Cell
createHeadCell
(
Excel
attr
,
Row
row
,
int
column
)
{
// 创建列
Cell
cell
=
row
.
createCell
(
column
);
// 写入列信息
cell
.
setCellValue
(
attr
.
name
());
setDataValidation
(
attr
,
row
,
column
);
cell
.
setCellStyle
(
styles
.
get
(
StringUtils
.
format
(
"header_{}_{}"
,
attr
.
headerColor
(),
attr
.
headerBackgroundColor
())));
if
(
isSubList
())
{
// 填充默认样式,防止合并单元格样式失效
sheet
.
setDefaultColumnStyle
(
column
,
styles
.
get
(
StringUtils
.
format
(
"data_{}_{}_{}_{}_{}"
,
attr
.
align
(),
attr
.
color
(),
attr
.
backgroundColor
(),
attr
.
cellType
(),
attr
.
wrapText
())));
if
(
attr
.
needMerge
())
{
sheet
.
addMergedRegion
(
new
CellRangeAddress
(
rownum
-
1
,
rownum
,
column
,
column
));
}
}
return
cell
;
}
/**
* 设置单元格信息
*
* @param value 单元格值
* @param attr 注解相关
* @param cell 单元格信息
*/
public
void
setCellVo
(
Object
value
,
Excel
attr
,
Cell
cell
)
{
if
(
ColumnType
.
STRING
==
attr
.
cellType
()
||
ColumnType
.
TEXT
==
attr
.
cellType
())
{
String
cellValue
=
Convert
.
toStr
(
value
);
// 对于任何以表达式触发字符 =-+@开头的单元格,直接使用tab字符作为前缀,防止CSV注入。
if
(
StringUtils
.
startsWithAny
(
cellValue
,
FORMULA_STR
))
{
cellValue
=
RegExUtils
.
replaceFirst
(
cellValue
,
FORMULA_REGEX_STR
,
"\t$0"
);
}
if
(
value
instanceof
Collection
&&
StringUtils
.
equals
(
"[]"
,
cellValue
))
{
cellValue
=
StringUtils
.
EMPTY
;
}
cell
.
setCellValue
(
StringUtils
.
isNull
(
cellValue
)
?
attr
.
defaultValue
()
:
cellValue
+
attr
.
suffix
());
}
else
if
(
ColumnType
.
NUMERIC
==
attr
.
cellType
())
{
if
(
StringUtils
.
isNotNull
(
value
))
{
cell
.
setCellValue
(
StringUtils
.
contains
(
Convert
.
toStr
(
value
),
"."
)
?
Convert
.
toDouble
(
value
)
:
Convert
.
toInt
(
value
));
}
}
else
if
(
ColumnType
.
IMAGE
==
attr
.
cellType
())
{
ClientAnchor
anchor
=
new
XSSFClientAnchor
(
0
,
0
,
0
,
0
,
(
short
)
cell
.
getColumnIndex
(),
cell
.
getRow
().
getRowNum
(),
(
short
)
(
cell
.
getColumnIndex
()
+
1
),
cell
.
getRow
().
getRowNum
()
+
1
);
String
imagePath
=
Convert
.
toStr
(
value
);
if
(
StringUtils
.
isNotEmpty
(
imagePath
))
{
byte
[]
data
=
ImageUtils
.
getImage
(
imagePath
);
getDrawingPatriarch
(
cell
.
getSheet
()).
createPicture
(
anchor
,
cell
.
getSheet
().
getWorkbook
().
addPicture
(
data
,
getImageType
(
data
)));
}
}
}
/**
* 获取画布
*/
public
static
Drawing
<?>
getDrawingPatriarch
(
Sheet
sheet
)
{
if
(
sheet
.
getDrawingPatriarch
()
==
null
)
{
sheet
.
createDrawingPatriarch
();
}
return
sheet
.
getDrawingPatriarch
();
}
/**
* 获取图片类型,设置图片插入类型
*/
public
int
getImageType
(
byte
[]
value
)
{
String
type
=
FileTypeUtils
.
getFileExtendName
(
value
);
if
(
"JPG"
.
equalsIgnoreCase
(
type
))
{
return
Workbook
.
PICTURE_TYPE_JPEG
;
}
else
if
(
"PNG"
.
equalsIgnoreCase
(
type
))
{
return
Workbook
.
PICTURE_TYPE_PNG
;
}
return
Workbook
.
PICTURE_TYPE_JPEG
;
}
/**
* 创建表格样式
*/
public
void
setDataValidation
(
Excel
attr
,
Row
row
,
int
column
)
{
if
(
attr
.
name
().
indexOf
(
"注:"
)
>=
0
)
{
sheet
.
setColumnWidth
(
column
,
6000
);
}
else
{
// 设置列宽
sheet
.
setColumnWidth
(
column
,
(
int
)
((
attr
.
width
()
+
0.72
)
*
256
));
}
if
(
StringUtils
.
isNotEmpty
(
attr
.
prompt
())
||
attr
.
combo
().
length
>
0
||
attr
.
comboReadDict
())
{
String
[]
comboArray
=
attr
.
combo
();
if
(
attr
.
comboReadDict
())
{
if
(!
sysDictMap
.
containsKey
(
"combo_"
+
attr
.
dictType
()))
{
// String labels = DictUtils.getDictLabels(attr.dictType());
// sysDictMap.put("combo_" + attr.dictType(), labels);
}
String
val
=
sysDictMap
.
get
(
"combo_"
+
attr
.
dictType
());
// comboArray = StringUtils.split(val, DictUtils.SEPARATOR);
}
if
(
comboArray
.
length
>
15
||
StringUtils
.
join
(
comboArray
).
length
()
>
255
)
{
// 如果下拉数大于15或字符串长度大于255,则使用一个新sheet存储,避免生成的模板下拉值获取不到
setXSSFValidationWithHidden
(
sheet
,
comboArray
,
attr
.
prompt
(),
1
,
100
,
column
,
column
);
}
else
{
// 提示信息或只能选择不能输入的列内容.
setPromptOrValidation
(
sheet
,
comboArray
,
attr
.
prompt
(),
1
,
100
,
column
,
column
);
}
}
}
/**
* 添加单元格
*/
public
Cell
addCell
(
Excel
attr
,
Row
row
,
T
vo
,
Field
field
,
int
column
)
{
Cell
cell
=
null
;
try
{
// 设置行高
row
.
setHeight
(
maxHeight
);
// 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.
if
(
attr
.
isExport
())
{
// 创建cell
cell
=
row
.
createCell
(
column
);
if
(
isSubListValue
(
vo
)
&&
getListCellValue
(
vo
).
size
()
>
1
&&
attr
.
needMerge
())
{
if
(
subMergedLastRowNum
>=
subMergedFirstRowNum
)
{
sheet
.
addMergedRegion
(
new
CellRangeAddress
(
subMergedFirstRowNum
,
subMergedLastRowNum
,
column
,
column
));
}
}
cell
.
setCellStyle
(
styles
.
get
(
StringUtils
.
format
(
"data_{}_{}_{}_{}_{}"
,
attr
.
align
(),
attr
.
color
(),
attr
.
backgroundColor
(),
attr
.
cellType
(),
attr
.
wrapText
())));
// 用于读取对象中的属性
Object
value
=
getTargetValue
(
vo
,
field
,
attr
);
String
dateFormat
=
attr
.
dateFormat
();
String
readConverterExp
=
attr
.
readConverterExp
();
String
separator
=
attr
.
separator
();
String
dictType
=
attr
.
dictType
();
if
(
StringUtils
.
isNotEmpty
(
dateFormat
)
&&
StringUtils
.
isNotNull
(
value
))
{
cell
.
setCellValue
(
parseDateToStr
(
dateFormat
,
value
));
}
else
if
(
StringUtils
.
isNotEmpty
(
readConverterExp
)
&&
StringUtils
.
isNotNull
(
value
))
{
cell
.
setCellValue
(
convertByExp
(
Convert
.
toStr
(
value
),
readConverterExp
,
separator
));
}
else
if
(
StringUtils
.
isNotEmpty
(
dictType
)
&&
StringUtils
.
isNotNull
(
value
))
{
if
(!
sysDictMap
.
containsKey
(
dictType
+
value
))
{
// String lable = convertDictByExp(Convert.toStr(value), dictType, separator);
// sysDictMap.put(dictType + value, lable);
}
cell
.
setCellValue
(
sysDictMap
.
get
(
dictType
+
value
));
}
else
if
(
value
instanceof
BigDecimal
&&
-
1
!=
attr
.
scale
())
{
cell
.
setCellValue
((((
BigDecimal
)
value
).
setScale
(
attr
.
scale
(),
attr
.
roundingMode
())).
doubleValue
());
}
else
if
(!
attr
.
handler
().
equals
(
ExcelHandlerAdapter
.
class
))
{
cell
.
setCellValue
(
dataFormatHandlerAdapter
(
value
,
attr
,
cell
));
}
else
{
// 设置列类型
setCellVo
(
value
,
attr
,
cell
);
}
addStatisticsData
(
column
,
Convert
.
toStr
(
value
),
attr
);
}
}
catch
(
Exception
e
)
{
log
.
error
(
"导出Excel失败{}"
,
e
);
}
return
cell
;
}
/**
* 设置 POI XSSFSheet 单元格提示或选择框
*
* @param sheet 表单
* @param textlist 下拉框显示的内容
* @param promptContent 提示内容
* @param firstRow 开始行
* @param endRow 结束行
* @param firstCol 开始列
* @param endCol 结束列
*/
public
void
setPromptOrValidation
(
Sheet
sheet
,
String
[]
textlist
,
String
promptContent
,
int
firstRow
,
int
endRow
,
int
firstCol
,
int
endCol
)
{
DataValidationHelper
helper
=
sheet
.
getDataValidationHelper
();
DataValidationConstraint
constraint
=
textlist
.
length
>
0
?
helper
.
createExplicitListConstraint
(
textlist
)
:
helper
.
createCustomConstraint
(
"DD1"
);
CellRangeAddressList
regions
=
new
CellRangeAddressList
(
firstRow
,
endRow
,
firstCol
,
endCol
);
DataValidation
dataValidation
=
helper
.
createValidation
(
constraint
,
regions
);
if
(
StringUtils
.
isNotEmpty
(
promptContent
))
{
// 如果设置了提示信息则鼠标放上去提示
dataValidation
.
createPromptBox
(
""
,
promptContent
);
dataValidation
.
setShowPromptBox
(
true
);
}
// 处理Excel兼容性问题
if
(
dataValidation
instanceof
XSSFDataValidation
)
{
dataValidation
.
setSuppressDropDownArrow
(
true
);
dataValidation
.
setShowErrorBox
(
true
);
}
else
{
dataValidation
.
setSuppressDropDownArrow
(
false
);
}
sheet
.
addValidationData
(
dataValidation
);
}
/**
* 设置某些列的值只能输入预制的数据,显示下拉框(兼容超出一定数量的下拉框).
*
* @param sheet 要设置的sheet.
* @param textlist 下拉框显示的内容
* @param promptContent 提示内容
* @param firstRow 开始行
* @param endRow 结束行
* @param firstCol 开始列
* @param endCol 结束列
*/
public
void
setXSSFValidationWithHidden
(
Sheet
sheet
,
String
[]
textlist
,
String
promptContent
,
int
firstRow
,
int
endRow
,
int
firstCol
,
int
endCol
)
{
String
hideSheetName
=
"combo_"
+
firstCol
+
"_"
+
endCol
;
Sheet
hideSheet
=
wb
.
createSheet
(
hideSheetName
);
// 用于存储 下拉菜单数据
for
(
int
i
=
0
;
i
<
textlist
.
length
;
i
++)
{
hideSheet
.
createRow
(
i
).
createCell
(
0
).
setCellValue
(
textlist
[
i
]);
}
// 创建名称,可被其他单元格引用
Name
name
=
wb
.
createName
();
name
.
setNameName
(
hideSheetName
+
"_data"
);
name
.
setRefersToFormula
(
hideSheetName
+
"!$A$1:$A$"
+
textlist
.
length
);
DataValidationHelper
helper
=
sheet
.
getDataValidationHelper
();
// 加载下拉列表内容
DataValidationConstraint
constraint
=
helper
.
createFormulaListConstraint
(
hideSheetName
+
"_data"
);
// 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列
CellRangeAddressList
regions
=
new
CellRangeAddressList
(
firstRow
,
endRow
,
firstCol
,
endCol
);
// 数据有效性对象
DataValidation
dataValidation
=
helper
.
createValidation
(
constraint
,
regions
);
if
(
StringUtils
.
isNotEmpty
(
promptContent
))
{
// 如果设置了提示信息则鼠标放上去提示
dataValidation
.
createPromptBox
(
""
,
promptContent
);
dataValidation
.
setShowPromptBox
(
true
);
}
// 处理Excel兼容性问题
if
(
dataValidation
instanceof
XSSFDataValidation
)
{
dataValidation
.
setSuppressDropDownArrow
(
true
);
dataValidation
.
setShowErrorBox
(
true
);
}
else
{
dataValidation
.
setSuppressDropDownArrow
(
false
);
}
sheet
.
addValidationData
(
dataValidation
);
// 设置hiddenSheet隐藏
wb
.
setSheetHidden
(
wb
.
getSheetIndex
(
hideSheet
),
true
);
}
/**
* 解析导出值 0=男,1=女,2=未知
*
* @param propertyValue 参数值
* @param converterExp 翻译注解
* @param separator 分隔符
* @return 解析后值
*/
public
static
String
convertByExp
(
String
propertyValue
,
String
converterExp
,
String
separator
)
{
StringBuilder
propertyString
=
new
StringBuilder
();
String
[]
convertSource
=
converterExp
.
split
(
","
);
for
(
String
item
:
convertSource
)
{
String
[]
itemArray
=
item
.
split
(
"="
);
if
(
StringUtils
.
containsAny
(
propertyValue
,
separator
))
{
for
(
String
value
:
propertyValue
.
split
(
separator
))
{
if
(
itemArray
[
0
].
equals
(
value
))
{
propertyString
.
append
(
itemArray
[
1
]
+
separator
);
break
;
}
}
}
else
{
if
(
itemArray
[
0
].
equals
(
propertyValue
))
{
return
itemArray
[
1
];
}
}
}
return
StringUtils
.
stripEnd
(
propertyString
.
toString
(),
separator
);
}
/**
* 反向解析值 男=0,女=1,未知=2
*
* @param propertyValue 参数值
* @param converterExp 翻译注解
* @param separator 分隔符
* @return 解析后值
*/
public
static
String
reverseByExp
(
String
propertyValue
,
String
converterExp
,
String
separator
)
{
StringBuilder
propertyString
=
new
StringBuilder
();
String
[]
convertSource
=
converterExp
.
split
(
","
);
for
(
String
item
:
convertSource
)
{
String
[]
itemArray
=
item
.
split
(
"="
);
if
(
StringUtils
.
containsAny
(
propertyValue
,
separator
))
{
for
(
String
value
:
propertyValue
.
split
(
separator
))
{
if
(
itemArray
[
1
].
equals
(
value
))
{
propertyString
.
append
(
itemArray
[
0
]
+
separator
);
break
;
}
}
}
else
{
if
(
itemArray
[
1
].
equals
(
propertyValue
))
{
return
itemArray
[
0
];
}
}
}
return
StringUtils
.
stripEnd
(
propertyString
.
toString
(),
separator
);
}
/**
* 数据处理器
*
* @param value 数据值
* @param excel 数据注解
* @return
*/
public
String
dataFormatHandlerAdapter
(
Object
value
,
Excel
excel
,
Cell
cell
)
{
try
{
Object
instance
=
excel
.
handler
().
newInstance
();
Method
formatMethod
=
excel
.
handler
().
getMethod
(
"format"
,
new
Class
[]
{
Object
.
class
,
String
[].
class
,
Cell
.
class
,
Workbook
.
class
});
value
=
formatMethod
.
invoke
(
instance
,
value
,
excel
.
args
(),
cell
,
this
.
wb
);
}
catch
(
Exception
e
)
{
log
.
error
(
"不能格式化数据 "
+
excel
.
handler
(),
e
.
getMessage
());
}
return
Convert
.
toStr
(
value
);
}
/**
* 合计统计信息
*/
private
void
addStatisticsData
(
Integer
index
,
String
text
,
Excel
entity
)
{
if
(
entity
!=
null
&&
entity
.
isStatistics
())
{
Double
temp
=
0
D
;
if
(!
statistics
.
containsKey
(
index
))
{
statistics
.
put
(
index
,
temp
);
}
try
{
temp
=
Double
.
valueOf
(
text
);
}
catch
(
NumberFormatException
e
)
{
}
statistics
.
put
(
index
,
statistics
.
get
(
index
)
+
temp
);
}
}
/**
* 创建统计行
*/
public
void
addStatisticsRow
()
{
if
(
statistics
.
size
()
>
0
)
{
Row
row
=
sheet
.
createRow
(
sheet
.
getLastRowNum
()
+
1
);
Set
<
Integer
>
keys
=
statistics
.
keySet
();
Cell
cell
=
row
.
createCell
(
0
);
cell
.
setCellStyle
(
styles
.
get
(
"total"
));
cell
.
setCellValue
(
"合计"
);
for
(
Integer
key
:
keys
)
{
cell
=
row
.
createCell
(
key
);
cell
.
setCellStyle
(
styles
.
get
(
"total"
));
cell
.
setCellValue
(
DOUBLE_FORMAT
.
format
(
statistics
.
get
(
key
)));
}
statistics
.
clear
();
}
}
/**
* 编码文件名
*/
public
String
encodingFilename
(
String
filename
)
{
filename
=
UUID
.
randomUUID
()
+
"_"
+
filename
+
".xlsx"
;
return
filename
;
}
/**
* 获取下载路径
*
* @param filename 文件名称
*/
public
String
getAbsoluteFile
(
String
filename
)
{
String
downloadPath
=
DeepseekConfig
.
getDownloadPath
()
+
filename
;
File
desc
=
new
File
(
downloadPath
);
if
(!
desc
.
getParentFile
().
exists
())
{
desc
.
getParentFile
().
mkdirs
();
}
return
downloadPath
;
}
/**
* 获取bean中的属性值
*
* @param vo 实体对象
* @param field 字段
* @param excel 注解
* @return 最终的属性值
* @throws Exception
*/
private
Object
getTargetValue
(
T
vo
,
Field
field
,
Excel
excel
)
throws
Exception
{
field
.
setAccessible
(
true
);
Object
o
=
field
.
get
(
vo
);
if
(
StringUtils
.
isNotEmpty
(
excel
.
targetAttr
()))
{
String
target
=
excel
.
targetAttr
();
if
(
target
.
contains
(
"."
))
{
String
[]
targets
=
target
.
split
(
"[.]"
);
for
(
String
name
:
targets
)
{
o
=
getValue
(
o
,
name
);
}
}
else
{
o
=
getValue
(
o
,
target
);
}
}
return
o
;
}
/**
* 以类的属性的get方法方法形式获取值
*
* @param o
* @param name
* @return value
* @throws Exception
*/
private
Object
getValue
(
Object
o
,
String
name
)
throws
Exception
{
if
(
StringUtils
.
isNotNull
(
o
)
&&
StringUtils
.
isNotEmpty
(
name
))
{
Class
<?>
clazz
=
o
.
getClass
();
Field
field
=
clazz
.
getDeclaredField
(
name
);
field
.
setAccessible
(
true
);
o
=
field
.
get
(
o
);
}
return
o
;
}
/**
* 得到所有定义字段
*/
private
void
createExcelField
()
{
this
.
fields
=
getFields
();
this
.
fields
=
this
.
fields
.
stream
().
sorted
(
Comparator
.
comparing
(
objects
->
((
Excel
)
objects
[
1
]).
sort
())).
collect
(
Collectors
.
toList
());
this
.
maxHeight
=
getRowHeight
();
}
/**
* 获取字段注解信息
*/
public
List
<
Object
[]>
getFields
()
{
List
<
Object
[]>
fields
=
new
ArrayList
<
Object
[]>();
List
<
Field
>
tempFields
=
new
ArrayList
<>();
tempFields
.
addAll
(
Arrays
.
asList
(
clazz
.
getSuperclass
().
getDeclaredFields
()));
tempFields
.
addAll
(
Arrays
.
asList
(
clazz
.
getDeclaredFields
()));
if
(
StringUtils
.
isNotEmpty
(
includeFields
))
{
for
(
Field
field
:
tempFields
)
{
if
(
ArrayUtils
.
contains
(
this
.
includeFields
,
field
.
getName
())
||
field
.
isAnnotationPresent
(
Excels
.
class
))
{
addField
(
fields
,
field
);
}
}
}
else
if
(
StringUtils
.
isNotEmpty
(
excludeFields
))
{
for
(
Field
field
:
tempFields
)
{
if
(!
ArrayUtils
.
contains
(
this
.
excludeFields
,
field
.
getName
()))
{
addField
(
fields
,
field
);
}
}
}
else
{
for
(
Field
field
:
tempFields
)
{
addField
(
fields
,
field
);
}
}
return
fields
;
}
/**
* 添加字段信息
*/
public
void
addField
(
List
<
Object
[]>
fields
,
Field
field
)
{
// 单注解
if
(
field
.
isAnnotationPresent
(
Excel
.
class
))
{
Excel
attr
=
field
.
getAnnotation
(
Excel
.
class
);
if
(
attr
!=
null
&&
(
attr
.
type
()
==
Type
.
ALL
||
attr
.
type
()
==
type
))
{
fields
.
add
(
new
Object
[]
{
field
,
attr
});
}
if
(
Collection
.
class
.
isAssignableFrom
(
field
.
getType
()))
{
subMethod
=
getSubMethod
(
field
.
getName
(),
clazz
);
ParameterizedType
pt
=
(
ParameterizedType
)
field
.
getGenericType
();
Class
<?>
subClass
=
(
Class
<?>)
pt
.
getActualTypeArguments
()[
0
];
this
.
subFields
=
FieldUtils
.
getFieldsListWithAnnotation
(
subClass
,
Excel
.
class
);
}
}
// 多注解
if
(
field
.
isAnnotationPresent
(
Excels
.
class
))
{
Excels
attrs
=
field
.
getAnnotation
(
Excels
.
class
);
Excel
[]
excels
=
attrs
.
value
();
for
(
Excel
attr
:
excels
)
{
if
(
StringUtils
.
isNotEmpty
(
includeFields
))
{
if
(
ArrayUtils
.
contains
(
this
.
includeFields
,
field
.
getName
()
+
"."
+
attr
.
targetAttr
())
&&
(
attr
!=
null
&&
(
attr
.
type
()
==
Type
.
ALL
||
attr
.
type
()
==
type
)))
{
fields
.
add
(
new
Object
[]
{
field
,
attr
});
}
}
else
{
if
(!
ArrayUtils
.
contains
(
this
.
excludeFields
,
field
.
getName
()
+
"."
+
attr
.
targetAttr
())
&&
(
attr
!=
null
&&
(
attr
.
type
()
==
Type
.
ALL
||
attr
.
type
()
==
type
)))
{
fields
.
add
(
new
Object
[]
{
field
,
attr
});
}
}
}
}
}
/**
* 根据注解获取最大行高
*/
public
short
getRowHeight
()
{
double
maxHeight
=
0
;
for
(
Object
[]
os
:
this
.
fields
)
{
Excel
excel
=
(
Excel
)
os
[
1
];
maxHeight
=
Math
.
max
(
maxHeight
,
excel
.
height
());
}
return
(
short
)
(
maxHeight
*
20
);
}
/**
* 创建一个工作簿
*/
public
void
createWorkbook
()
{
this
.
wb
=
new
SXSSFWorkbook
(
500
);
this
.
sheet
=
wb
.
createSheet
();
wb
.
setSheetName
(
0
,
sheetName
);
this
.
styles
=
createStyles
(
wb
);
}
/**
* 创建工作表
*
* @param sheetNo sheet数量
* @param index 序号
*/
public
void
createSheet
(
int
sheetNo
,
int
index
)
{
// 设置工作表的名称.
if
(
sheetNo
>
1
&&
index
>
0
)
{
this
.
sheet
=
wb
.
createSheet
();
this
.
createTitle
();
wb
.
setSheetName
(
index
,
sheetName
+
index
);
}
}
/**
* 获取单元格值
*
* @param row 获取的行
* @param column 获取单元格列号
* @return 单元格值
*/
public
Object
getCellValue
(
Row
row
,
int
column
)
{
if
(
row
==
null
)
{
return
row
;
}
Object
val
=
""
;
try
{
Cell
cell
=
row
.
getCell
(
column
);
if
(
StringUtils
.
isNotNull
(
cell
))
{
if
(
cell
.
getCellType
()
==
CellType
.
NUMERIC
||
cell
.
getCellType
()
==
CellType
.
FORMULA
)
{
val
=
cell
.
getNumericCellValue
();
if
(
DateUtil
.
isCellDateFormatted
(
cell
))
{
val
=
DateUtil
.
getJavaDate
((
Double
)
val
);
// POI Excel 日期格式转换
}
else
{
if
((
Double
)
val
%
1
!=
0
)
{
val
=
new
BigDecimal
(
val
.
toString
());
}
else
{
val
=
new
DecimalFormat
(
"0"
).
format
(
val
);
}
}
}
else
if
(
cell
.
getCellType
()
==
CellType
.
STRING
)
{
val
=
cell
.
getStringCellValue
();
}
else
if
(
cell
.
getCellType
()
==
CellType
.
BOOLEAN
)
{
val
=
cell
.
getBooleanCellValue
();
}
else
if
(
cell
.
getCellType
()
==
CellType
.
ERROR
)
{
val
=
cell
.
getErrorCellValue
();
}
}
}
catch
(
Exception
e
)
{
return
val
;
}
return
val
;
}
/**
* 判断是否是空行
*
* @param row 判断的行
* @return
*/
private
boolean
isRowEmpty
(
Row
row
)
{
if
(
row
==
null
)
{
return
true
;
}
for
(
int
i
=
row
.
getFirstCellNum
();
i
<
row
.
getLastCellNum
();
i
++)
{
Cell
cell
=
row
.
getCell
(
i
);
if
(
cell
!=
null
&&
cell
.
getCellType
()
!=
CellType
.
BLANK
)
{
return
false
;
}
}
return
true
;
}
/**
* 获取Excel2003图片
*
* @param sheet 当前sheet对象
* @param workbook 工作簿对象
* @return Map key:图片单元格索引(1_1)String,value:图片流PictureData
*/
public
static
Map
<
String
,
PictureData
>
getSheetPictures03
(
HSSFSheet
sheet
,
HSSFWorkbook
workbook
)
{
Map
<
String
,
PictureData
>
sheetIndexPicMap
=
new
HashMap
<
String
,
PictureData
>();
List
<
HSSFPictureData
>
pictures
=
workbook
.
getAllPictures
();
if
(!
pictures
.
isEmpty
())
{
for
(
HSSFShape
shape
:
sheet
.
getDrawingPatriarch
().
getChildren
())
{
HSSFClientAnchor
anchor
=
(
HSSFClientAnchor
)
shape
.
getAnchor
();
if
(
shape
instanceof
HSSFPicture
)
{
HSSFPicture
pic
=
(
HSSFPicture
)
shape
;
int
pictureIndex
=
pic
.
getPictureIndex
()
-
1
;
HSSFPictureData
picData
=
pictures
.
get
(
pictureIndex
);
String
picIndex
=
anchor
.
getRow1
()
+
"_"
+
anchor
.
getCol1
();
sheetIndexPicMap
.
put
(
picIndex
,
picData
);
}
}
return
sheetIndexPicMap
;
}
else
{
return
sheetIndexPicMap
;
}
}
/**
* 获取Excel2007图片
*
* @param sheet 当前sheet对象
* @param workbook 工作簿对象
* @return Map key:图片单元格索引(1_1)String,value:图片流PictureData
*/
public
static
Map
<
String
,
PictureData
>
getSheetPictures07
(
XSSFSheet
sheet
,
XSSFWorkbook
workbook
)
{
Map
<
String
,
PictureData
>
sheetIndexPicMap
=
new
HashMap
<
String
,
PictureData
>();
for
(
POIXMLDocumentPart
dr
:
sheet
.
getRelations
())
{
if
(
dr
instanceof
XSSFDrawing
)
{
XSSFDrawing
drawing
=
(
XSSFDrawing
)
dr
;
List
<
XSSFShape
>
shapes
=
drawing
.
getShapes
();
for
(
XSSFShape
shape
:
shapes
)
{
if
(
shape
instanceof
XSSFPicture
)
{
XSSFPicture
pic
=
(
XSSFPicture
)
shape
;
XSSFClientAnchor
anchor
=
pic
.
getPreferredSize
();
CTMarker
ctMarker
=
anchor
.
getFrom
();
String
picIndex
=
ctMarker
.
getRow
()
+
"_"
+
ctMarker
.
getCol
();
sheetIndexPicMap
.
put
(
picIndex
,
pic
.
getPictureData
());
}
}
}
}
return
sheetIndexPicMap
;
}
/**
* 格式化不同类型的日期对象
*
* @param dateFormat 日期格式
* @param val 被格式化的日期对象
* @return 格式化后的日期字符
*/
public
String
parseDateToStr
(
String
dateFormat
,
Object
val
)
{
if
(
val
==
null
)
{
return
""
;
}
String
str
;
if
(
val
instanceof
Date
)
{
str
=
DateUtils
.
parseDateToStr
(
dateFormat
,
(
Date
)
val
);
}
else
if
(
val
instanceof
LocalDateTime
)
{
str
=
DateUtils
.
parseDateToStr
(
dateFormat
,
DateUtils
.
toDate
((
LocalDateTime
)
val
));
}
else
if
(
val
instanceof
LocalDate
)
{
str
=
DateUtils
.
parseDateToStr
(
dateFormat
,
DateUtils
.
toDate
((
LocalDate
)
val
));
}
else
{
str
=
val
.
toString
();
}
return
str
;
}
/**
* 是否有对象的子列表
*/
public
boolean
isSubList
()
{
return
StringUtils
.
isNotNull
(
subFields
)
&&
subFields
.
size
()
>
0
;
}
/**
* 是否有对象的子列表,集合不为空
*/
public
boolean
isSubListValue
(
T
vo
)
{
return
StringUtils
.
isNotNull
(
subFields
)
&&
subFields
.
size
()
>
0
&&
StringUtils
.
isNotNull
(
getListCellValue
(
vo
))
&&
getListCellValue
(
vo
).
size
()
>
0
;
}
/**
* 获取集合的值
*/
public
Collection
<?>
getListCellValue
(
Object
obj
)
{
Object
value
;
try
{
value
=
subMethod
.
invoke
(
obj
,
new
Object
[]
{});
}
catch
(
Exception
e
)
{
return
new
ArrayList
<
Object
>();
}
return
(
Collection
<?>)
value
;
}
/**
* 获取对象的子列表方法
*
* @param name 名称
* @param pojoClass 类对象
* @return 子列表方法
*/
public
Method
getSubMethod
(
String
name
,
Class
<?>
pojoClass
)
{
StringBuffer
getMethodName
=
new
StringBuffer
(
"get"
);
getMethodName
.
append
(
name
.
substring
(
0
,
1
).
toUpperCase
());
getMethodName
.
append
(
name
.
substring
(
1
));
Method
method
=
null
;
try
{
method
=
pojoClass
.
getMethod
(
getMethodName
.
toString
(),
new
Class
[]
{});
}
catch
(
Exception
e
)
{
log
.
error
(
"获取对象异常{}"
,
e
.
getMessage
());
}
return
method
;
}
}
ewaytek-deepseek-common/src/main/java/com/ewaytek/deepseek/common/utils/reflect/ReflectUtils.java
0 → 100644
View file @
0487b6d5
package
com.ewaytek.deepseek.common.utils.reflect
;
import
java.lang.reflect.Field
;
import
java.lang.reflect.InvocationTargetException
;
import
java.lang.reflect.Method
;
import
java.lang.reflect.Modifier
;
import
java.lang.reflect.ParameterizedType
;
import
java.lang.reflect.Type
;
import
java.util.Date
;
import
com.ewaytek.deepseek.common.core.text.Convert
;
import
com.ewaytek.deepseek.common.utils.DateUtils
;
import
org.apache.commons.lang3.StringUtils
;
import
org.apache.commons.lang3.Validate
;
import
org.apache.poi.ss.usermodel.DateUtil
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
/**
* 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.
*
* @author ruoyi
*/
@SuppressWarnings
(
"rawtypes"
)
public
class
ReflectUtils
{
private
static
final
String
SETTER_PREFIX
=
"set"
;
private
static
final
String
GETTER_PREFIX
=
"get"
;
private
static
final
String
CGLIB_CLASS_SEPARATOR
=
"$$"
;
private
static
Logger
logger
=
LoggerFactory
.
getLogger
(
ReflectUtils
.
class
);
/**
* 调用Getter方法.
* 支持多级,如:对象名.对象名.方法
*/
@SuppressWarnings
(
"unchecked"
)
public
static
<
E
>
E
invokeGetter
(
Object
obj
,
String
propertyName
)
{
Object
object
=
obj
;
for
(
String
name
:
StringUtils
.
split
(
propertyName
,
"."
))
{
String
getterMethodName
=
GETTER_PREFIX
+
StringUtils
.
capitalize
(
name
);
object
=
invokeMethod
(
object
,
getterMethodName
,
new
Class
[]
{},
new
Object
[]
{});
}
return
(
E
)
object
;
}
/**
* 调用Setter方法, 仅匹配方法名。
* 支持多级,如:对象名.对象名.方法
*/
public
static
<
E
>
void
invokeSetter
(
Object
obj
,
String
propertyName
,
E
value
)
{
Object
object
=
obj
;
String
[]
names
=
StringUtils
.
split
(
propertyName
,
"."
);
for
(
int
i
=
0
;
i
<
names
.
length
;
i
++)
{
if
(
i
<
names
.
length
-
1
)
{
String
getterMethodName
=
GETTER_PREFIX
+
StringUtils
.
capitalize
(
names
[
i
]);
object
=
invokeMethod
(
object
,
getterMethodName
,
new
Class
[]
{},
new
Object
[]
{});
}
else
{
String
setterMethodName
=
SETTER_PREFIX
+
StringUtils
.
capitalize
(
names
[
i
]);
invokeMethodByName
(
object
,
setterMethodName
,
new
Object
[]
{
value
});
}
}
}
/**
* 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
*/
@SuppressWarnings
(
"unchecked"
)
public
static
<
E
>
E
getFieldValue
(
final
Object
obj
,
final
String
fieldName
)
{
Field
field
=
getAccessibleField
(
obj
,
fieldName
);
if
(
field
==
null
)
{
logger
.
debug
(
"在 ["
+
obj
.
getClass
()
+
"] 中,没有找到 ["
+
fieldName
+
"] 字段 "
);
return
null
;
}
E
result
=
null
;
try
{
result
=
(
E
)
field
.
get
(
obj
);
}
catch
(
IllegalAccessException
e
)
{
logger
.
error
(
"不可能抛出的异常{}"
,
e
.
getMessage
());
}
return
result
;
}
/**
* 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
*/
public
static
<
E
>
void
setFieldValue
(
final
Object
obj
,
final
String
fieldName
,
final
E
value
)
{
Field
field
=
getAccessibleField
(
obj
,
fieldName
);
if
(
field
==
null
)
{
// throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
logger
.
debug
(
"在 ["
+
obj
.
getClass
()
+
"] 中,没有找到 ["
+
fieldName
+
"] 字段 "
);
return
;
}
try
{
field
.
set
(
obj
,
value
);
}
catch
(
IllegalAccessException
e
)
{
logger
.
error
(
"不可能抛出的异常: {}"
,
e
.
getMessage
());
}
}
/**
* 直接调用对象方法, 无视private/protected修饰符.
* 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用.
* 同时匹配方法名+参数类型,
*/
@SuppressWarnings
(
"unchecked"
)
public
static
<
E
>
E
invokeMethod
(
final
Object
obj
,
final
String
methodName
,
final
Class
<?>[]
parameterTypes
,
final
Object
[]
args
)
{
if
(
obj
==
null
||
methodName
==
null
)
{
return
null
;
}
Method
method
=
getAccessibleMethod
(
obj
,
methodName
,
parameterTypes
);
if
(
method
==
null
)
{
logger
.
debug
(
"在 ["
+
obj
.
getClass
()
+
"] 中,没有找到 ["
+
methodName
+
"] 方法 "
);
return
null
;
}
try
{
return
(
E
)
method
.
invoke
(
obj
,
args
);
}
catch
(
Exception
e
)
{
String
msg
=
"method: "
+
method
+
", obj: "
+
obj
+
", args: "
+
args
+
""
;
throw
convertReflectionExceptionToUnchecked
(
msg
,
e
);
}
}
/**
* 直接调用对象方法, 无视private/protected修饰符,
* 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用.
* 只匹配函数名,如果有多个同名函数调用第一个。
*/
@SuppressWarnings
(
"unchecked"
)
public
static
<
E
>
E
invokeMethodByName
(
final
Object
obj
,
final
String
methodName
,
final
Object
[]
args
)
{
Method
method
=
getAccessibleMethodByName
(
obj
,
methodName
,
args
.
length
);
if
(
method
==
null
)
{
// 如果为空不报错,直接返回空。
logger
.
debug
(
"在 ["
+
obj
.
getClass
()
+
"] 中,没有找到 ["
+
methodName
+
"] 方法 "
);
return
null
;
}
try
{
// 类型转换(将参数数据类型转换为目标方法参数类型)
Class
<?>[]
cs
=
method
.
getParameterTypes
();
for
(
int
i
=
0
;
i
<
cs
.
length
;
i
++)
{
if
(
args
[
i
]
!=
null
&&
!
args
[
i
].
getClass
().
equals
(
cs
[
i
]))
{
if
(
cs
[
i
]
==
String
.
class
)
{
args
[
i
]
=
Convert
.
toStr
(
args
[
i
]);
if
(
StringUtils
.
endsWith
((
String
)
args
[
i
],
".0"
))
{
args
[
i
]
=
StringUtils
.
substringBefore
((
String
)
args
[
i
],
".0"
);
}
}
else
if
(
cs
[
i
]
==
Integer
.
class
)
{
args
[
i
]
=
Convert
.
toInt
(
args
[
i
]);
}
else
if
(
cs
[
i
]
==
Long
.
class
)
{
args
[
i
]
=
Convert
.
toLong
(
args
[
i
]);
}
else
if
(
cs
[
i
]
==
Double
.
class
)
{
args
[
i
]
=
Convert
.
toDouble
(
args
[
i
]);
}
else
if
(
cs
[
i
]
==
Float
.
class
)
{
args
[
i
]
=
Convert
.
toFloat
(
args
[
i
]);
}
else
if
(
cs
[
i
]
==
Date
.
class
)
{
if
(
args
[
i
]
instanceof
String
)
{
args
[
i
]
=
DateUtils
.
parseDate
(
args
[
i
]);
}
else
{
args
[
i
]
=
DateUtil
.
getJavaDate
((
Double
)
args
[
i
]);
}
}
else
if
(
cs
[
i
]
==
boolean
.
class
||
cs
[
i
]
==
Boolean
.
class
)
{
args
[
i
]
=
Convert
.
toBool
(
args
[
i
]);
}
}
}
return
(
E
)
method
.
invoke
(
obj
,
args
);
}
catch
(
Exception
e
)
{
String
msg
=
"method: "
+
method
+
", obj: "
+
obj
+
", args: "
+
args
+
""
;
throw
convertReflectionExceptionToUnchecked
(
msg
,
e
);
}
}
/**
* 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.
* 如向上转型到Object仍无法找到, 返回null.
*/
public
static
Field
getAccessibleField
(
final
Object
obj
,
final
String
fieldName
)
{
// 为空不报错。直接返回 null
if
(
obj
==
null
)
{
return
null
;
}
Validate
.
notBlank
(
fieldName
,
"fieldName can't be blank"
);
for
(
Class
<?>
superClass
=
obj
.
getClass
();
superClass
!=
Object
.
class
;
superClass
=
superClass
.
getSuperclass
())
{
try
{
Field
field
=
superClass
.
getDeclaredField
(
fieldName
);
makeAccessible
(
field
);
return
field
;
}
catch
(
NoSuchFieldException
e
)
{
continue
;
}
}
return
null
;
}
/**
* 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
* 如向上转型到Object仍无法找到, 返回null.
* 匹配函数名+参数类型。
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
*/
public
static
Method
getAccessibleMethod
(
final
Object
obj
,
final
String
methodName
,
final
Class
<?>...
parameterTypes
)
{
// 为空不报错。直接返回 null
if
(
obj
==
null
)
{
return
null
;
}
Validate
.
notBlank
(
methodName
,
"methodName can't be blank"
);
for
(
Class
<?>
searchType
=
obj
.
getClass
();
searchType
!=
Object
.
class
;
searchType
=
searchType
.
getSuperclass
())
{
try
{
Method
method
=
searchType
.
getDeclaredMethod
(
methodName
,
parameterTypes
);
makeAccessible
(
method
);
return
method
;
}
catch
(
NoSuchMethodException
e
)
{
continue
;
}
}
return
null
;
}
/**
* 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
* 如向上转型到Object仍无法找到, 返回null.
* 只匹配函数名。
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
*/
public
static
Method
getAccessibleMethodByName
(
final
Object
obj
,
final
String
methodName
,
int
argsNum
)
{
// 为空不报错。直接返回 null
if
(
obj
==
null
)
{
return
null
;
}
Validate
.
notBlank
(
methodName
,
"methodName can't be blank"
);
for
(
Class
<?>
searchType
=
obj
.
getClass
();
searchType
!=
Object
.
class
;
searchType
=
searchType
.
getSuperclass
())
{
Method
[]
methods
=
searchType
.
getDeclaredMethods
();
for
(
Method
method
:
methods
)
{
if
(
method
.
getName
().
equals
(
methodName
)
&&
method
.
getParameterTypes
().
length
==
argsNum
)
{
makeAccessible
(
method
);
return
method
;
}
}
}
return
null
;
}
/**
* 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
*/
public
static
void
makeAccessible
(
Method
method
)
{
if
((!
Modifier
.
isPublic
(
method
.
getModifiers
())
||
!
Modifier
.
isPublic
(
method
.
getDeclaringClass
().
getModifiers
()))
&&
!
method
.
isAccessible
())
{
method
.
setAccessible
(
true
);
}
}
/**
* 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
*/
public
static
void
makeAccessible
(
Field
field
)
{
if
((!
Modifier
.
isPublic
(
field
.
getModifiers
())
||
!
Modifier
.
isPublic
(
field
.
getDeclaringClass
().
getModifiers
())
||
Modifier
.
isFinal
(
field
.
getModifiers
()))
&&
!
field
.
isAccessible
())
{
field
.
setAccessible
(
true
);
}
}
/**
* 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处
* 如无法找到, 返回Object.class.
*/
@SuppressWarnings
(
"unchecked"
)
public
static
<
T
>
Class
<
T
>
getClassGenricType
(
final
Class
clazz
)
{
return
getClassGenricType
(
clazz
,
0
);
}
/**
* 通过反射, 获得Class定义中声明的父类的泛型参数的类型.
* 如无法找到, 返回Object.class.
*/
public
static
Class
getClassGenricType
(
final
Class
clazz
,
final
int
index
)
{
Type
genType
=
clazz
.
getGenericSuperclass
();
if
(!(
genType
instanceof
ParameterizedType
))
{
logger
.
debug
(
clazz
.
getSimpleName
()
+
"'s superclass not ParameterizedType"
);
return
Object
.
class
;
}
Type
[]
params
=
((
ParameterizedType
)
genType
).
getActualTypeArguments
();
if
(
index
>=
params
.
length
||
index
<
0
)
{
logger
.
debug
(
"Index: "
+
index
+
", Size of "
+
clazz
.
getSimpleName
()
+
"'s Parameterized Type: "
+
params
.
length
);
return
Object
.
class
;
}
if
(!(
params
[
index
]
instanceof
Class
))
{
logger
.
debug
(
clazz
.
getSimpleName
()
+
" not set the actual class on superclass generic parameter"
);
return
Object
.
class
;
}
return
(
Class
)
params
[
index
];
}
public
static
Class
<?>
getUserClass
(
Object
instance
)
{
if
(
instance
==
null
)
{
throw
new
RuntimeException
(
"Instance must not be null"
);
}
Class
clazz
=
instance
.
getClass
();
if
(
clazz
!=
null
&&
clazz
.
getName
().
contains
(
CGLIB_CLASS_SEPARATOR
))
{
Class
<?>
superClass
=
clazz
.
getSuperclass
();
if
(
superClass
!=
null
&&
!
Object
.
class
.
equals
(
superClass
))
{
return
superClass
;
}
}
return
clazz
;
}
/**
* 将反射时的checked exception转换为unchecked exception.
*/
public
static
RuntimeException
convertReflectionExceptionToUnchecked
(
String
msg
,
Exception
e
)
{
if
(
e
instanceof
IllegalAccessException
||
e
instanceof
IllegalArgumentException
||
e
instanceof
NoSuchMethodException
)
{
return
new
IllegalArgumentException
(
msg
,
e
);
}
else
if
(
e
instanceof
InvocationTargetException
)
{
return
new
RuntimeException
(
msg
,
((
InvocationTargetException
)
e
).
getTargetException
());
}
return
new
RuntimeException
(
msg
,
e
);
}
}
ewaytek-deepseek-common/src/main/java/com/ewaytek/deepseek/common/utils/sign/Base64.java
0 → 100644
View file @
0487b6d5
package
com.ewaytek.deepseek.common.utils.sign
;
/**
* Base64工具类
*
* @author admin
*/
public
final
class
Base64
{
static
private
final
int
BASELENGTH
=
128
;
static
private
final
int
LOOKUPLENGTH
=
64
;
static
private
final
int
TWENTYFOURBITGROUP
=
24
;
static
private
final
int
EIGHTBIT
=
8
;
static
private
final
int
SIXTEENBIT
=
16
;
static
private
final
int
FOURBYTE
=
4
;
static
private
final
int
SIGN
=
-
128
;
static
private
final
char
PAD
=
'='
;
static
final
private
byte
[]
base64Alphabet
=
new
byte
[
BASELENGTH
];
static
final
private
char
[]
lookUpBase64Alphabet
=
new
char
[
LOOKUPLENGTH
];
static
{
for
(
int
i
=
0
;
i
<
BASELENGTH
;
++
i
)
{
base64Alphabet
[
i
]
=
-
1
;
}
for
(
int
i
=
'Z'
;
i
>=
'A'
;
i
--)
{
base64Alphabet
[
i
]
=
(
byte
)
(
i
-
'A'
);
}
for
(
int
i
=
'z'
;
i
>=
'a'
;
i
--)
{
base64Alphabet
[
i
]
=
(
byte
)
(
i
-
'a'
+
26
);
}
for
(
int
i
=
'9'
;
i
>=
'0'
;
i
--)
{
base64Alphabet
[
i
]
=
(
byte
)
(
i
-
'0'
+
52
);
}
base64Alphabet
[
'+'
]
=
62
;
base64Alphabet
[
'/'
]
=
63
;
for
(
int
i
=
0
;
i
<=
25
;
i
++)
{
lookUpBase64Alphabet
[
i
]
=
(
char
)
(
'A'
+
i
);
}
for
(
int
i
=
26
,
j
=
0
;
i
<=
51
;
i
++,
j
++)
{
lookUpBase64Alphabet
[
i
]
=
(
char
)
(
'a'
+
j
);
}
for
(
int
i
=
52
,
j
=
0
;
i
<=
61
;
i
++,
j
++)
{
lookUpBase64Alphabet
[
i
]
=
(
char
)
(
'0'
+
j
);
}
lookUpBase64Alphabet
[
62
]
=
'+'
;
lookUpBase64Alphabet
[
63
]
=
'/'
;
}
private
static
boolean
isWhiteSpace
(
char
octect
)
{
return
(
octect
==
0x20
||
octect
==
0xd
||
octect
==
0xa
||
octect
==
0x9
);
}
private
static
boolean
isPad
(
char
octect
)
{
return
(
octect
==
PAD
);
}
private
static
boolean
isData
(
char
octect
)
{
return
(
octect
<
BASELENGTH
&&
base64Alphabet
[
octect
]
!=
-
1
);
}
/**
* Encodes hex octects into Base64
*
* @param binaryData Array containing binaryData
* @return Encoded Base64 array
*/
public
static
String
encode
(
byte
[]
binaryData
)
{
if
(
binaryData
==
null
)
{
return
null
;
}
int
lengthDataBits
=
binaryData
.
length
*
EIGHTBIT
;
if
(
lengthDataBits
==
0
)
{
return
""
;
}
int
fewerThan24bits
=
lengthDataBits
%
TWENTYFOURBITGROUP
;
int
numberTriplets
=
lengthDataBits
/
TWENTYFOURBITGROUP
;
int
numberQuartet
=
fewerThan24bits
!=
0
?
numberTriplets
+
1
:
numberTriplets
;
char
[]
encodedData
=
null
;
encodedData
=
new
char
[
numberQuartet
*
4
];
byte
k
=
0
,
l
=
0
,
b1
=
0
,
b2
=
0
,
b3
=
0
;
int
encodedIndex
=
0
;
int
dataIndex
=
0
;
for
(
int
i
=
0
;
i
<
numberTriplets
;
i
++)
{
b1
=
binaryData
[
dataIndex
++];
b2
=
binaryData
[
dataIndex
++];
b3
=
binaryData
[
dataIndex
++];
l
=
(
byte
)
(
b2
&
0x0f
);
k
=
(
byte
)
(
b1
&
0x03
);
byte
val1
=
((
b1
&
SIGN
)
==
0
)
?
(
byte
)
(
b1
>>
2
)
:
(
byte
)
((
b1
)
>>
2
^
0xc0
);
byte
val2
=
((
b2
&
SIGN
)
==
0
)
?
(
byte
)
(
b2
>>
4
)
:
(
byte
)
((
b2
)
>>
4
^
0xf0
);
byte
val3
=
((
b3
&
SIGN
)
==
0
)
?
(
byte
)
(
b3
>>
6
)
:
(
byte
)
((
b3
)
>>
6
^
0xfc
);
encodedData
[
encodedIndex
++]
=
lookUpBase64Alphabet
[
val1
];
encodedData
[
encodedIndex
++]
=
lookUpBase64Alphabet
[
val2
|
(
k
<<
4
)];
encodedData
[
encodedIndex
++]
=
lookUpBase64Alphabet
[(
l
<<
2
)
|
val3
];
encodedData
[
encodedIndex
++]
=
lookUpBase64Alphabet
[
b3
&
0x3f
];
}
// form integral number of 6-bit groups
if
(
fewerThan24bits
==
EIGHTBIT
)
{
b1
=
binaryData
[
dataIndex
];
k
=
(
byte
)
(
b1
&
0x03
);
byte
val1
=
((
b1
&
SIGN
)
==
0
)
?
(
byte
)
(
b1
>>
2
)
:
(
byte
)
((
b1
)
>>
2
^
0xc0
);
encodedData
[
encodedIndex
++]
=
lookUpBase64Alphabet
[
val1
];
encodedData
[
encodedIndex
++]
=
lookUpBase64Alphabet
[
k
<<
4
];
encodedData
[
encodedIndex
++]
=
PAD
;
encodedData
[
encodedIndex
++]
=
PAD
;
}
else
if
(
fewerThan24bits
==
SIXTEENBIT
)
{
b1
=
binaryData
[
dataIndex
];
b2
=
binaryData
[
dataIndex
+
1
];
l
=
(
byte
)
(
b2
&
0x0f
);
k
=
(
byte
)
(
b1
&
0x03
);
byte
val1
=
((
b1
&
SIGN
)
==
0
)
?
(
byte
)
(
b1
>>
2
)
:
(
byte
)
((
b1
)
>>
2
^
0xc0
);
byte
val2
=
((
b2
&
SIGN
)
==
0
)
?
(
byte
)
(
b2
>>
4
)
:
(
byte
)
((
b2
)
>>
4
^
0xf0
);
encodedData
[
encodedIndex
++]
=
lookUpBase64Alphabet
[
val1
];
encodedData
[
encodedIndex
++]
=
lookUpBase64Alphabet
[
val2
|
(
k
<<
4
)];
encodedData
[
encodedIndex
++]
=
lookUpBase64Alphabet
[
l
<<
2
];
encodedData
[
encodedIndex
++]
=
PAD
;
}
return
new
String
(
encodedData
);
}
/**
* Decodes Base64 data into octects
*
* @param encoded string containing Base64 data
* @return Array containind decoded data.
*/
public
static
byte
[]
decode
(
String
encoded
)
{
if
(
encoded
==
null
)
{
return
null
;
}
char
[]
base64Data
=
encoded
.
toCharArray
();
// remove white spaces
int
len
=
removeWhiteSpace
(
base64Data
);
if
(
len
%
FOURBYTE
!=
0
)
{
return
null
;
// should be divisible by four
}
int
numberQuadruple
=
(
len
/
FOURBYTE
);
if
(
numberQuadruple
==
0
)
{
return
new
byte
[
0
];
}
byte
[]
decodedData
=
null
;
byte
b1
=
0
,
b2
=
0
,
b3
=
0
,
b4
=
0
;
char
d1
=
0
,
d2
=
0
,
d3
=
0
,
d4
=
0
;
int
i
=
0
;
int
encodedIndex
=
0
;
int
dataIndex
=
0
;
decodedData
=
new
byte
[(
numberQuadruple
)
*
3
];
for
(;
i
<
numberQuadruple
-
1
;
i
++)
{
if
(!
isData
((
d1
=
base64Data
[
dataIndex
++]))
||
!
isData
((
d2
=
base64Data
[
dataIndex
++]))
||
!
isData
((
d3
=
base64Data
[
dataIndex
++]))
||
!
isData
((
d4
=
base64Data
[
dataIndex
++])))
{
return
null
;
}
// if found "no data" just return null
b1
=
base64Alphabet
[
d1
];
b2
=
base64Alphabet
[
d2
];
b3
=
base64Alphabet
[
d3
];
b4
=
base64Alphabet
[
d4
];
decodedData
[
encodedIndex
++]
=
(
byte
)
(
b1
<<
2
|
b2
>>
4
);
decodedData
[
encodedIndex
++]
=
(
byte
)
(((
b2
&
0xf
)
<<
4
)
|
((
b3
>>
2
)
&
0xf
));
decodedData
[
encodedIndex
++]
=
(
byte
)
(
b3
<<
6
|
b4
);
}
if
(!
isData
((
d1
=
base64Data
[
dataIndex
++]))
||
!
isData
((
d2
=
base64Data
[
dataIndex
++])))
{
return
null
;
// if found "no data" just return null
}
b1
=
base64Alphabet
[
d1
];
b2
=
base64Alphabet
[
d2
];
d3
=
base64Data
[
dataIndex
++];
d4
=
base64Data
[
dataIndex
++];
if
(!
isData
((
d3
))
||
!
isData
((
d4
)))
{
// Check if they are PAD characters
if
(
isPad
(
d3
)
&&
isPad
(
d4
))
{
if
((
b2
&
0xf
)
!=
0
)
// last 4 bits should be zero
{
return
null
;
}
byte
[]
tmp
=
new
byte
[
i
*
3
+
1
];
System
.
arraycopy
(
decodedData
,
0
,
tmp
,
0
,
i
*
3
);
tmp
[
encodedIndex
]
=
(
byte
)
(
b1
<<
2
|
b2
>>
4
);
return
tmp
;
}
else
if
(!
isPad
(
d3
)
&&
isPad
(
d4
))
{
b3
=
base64Alphabet
[
d3
];
if
((
b3
&
0x3
)
!=
0
)
// last 2 bits should be zero
{
return
null
;
}
byte
[]
tmp
=
new
byte
[
i
*
3
+
2
];
System
.
arraycopy
(
decodedData
,
0
,
tmp
,
0
,
i
*
3
);
tmp
[
encodedIndex
++]
=
(
byte
)
(
b1
<<
2
|
b2
>>
4
);
tmp
[
encodedIndex
]
=
(
byte
)
(((
b2
&
0xf
)
<<
4
)
|
((
b3
>>
2
)
&
0xf
));
return
tmp
;
}
else
{
return
null
;
}
}
else
{
// No PAD e.g 3cQl
b3
=
base64Alphabet
[
d3
];
b4
=
base64Alphabet
[
d4
];
decodedData
[
encodedIndex
++]
=
(
byte
)
(
b1
<<
2
|
b2
>>
4
);
decodedData
[
encodedIndex
++]
=
(
byte
)
(((
b2
&
0xf
)
<<
4
)
|
((
b3
>>
2
)
&
0xf
));
decodedData
[
encodedIndex
++]
=
(
byte
)
(
b3
<<
6
|
b4
);
}
return
decodedData
;
}
/**
* remove WhiteSpace from MIME containing encoded Base64 data.
*
* @param data the byte array of base64 data (with WS)
* @return the new length
*/
private
static
int
removeWhiteSpace
(
char
[]
data
)
{
if
(
data
==
null
)
{
return
0
;
}
// count characters that's not whitespace
int
newSize
=
0
;
int
len
=
data
.
length
;
for
(
int
i
=
0
;
i
<
len
;
i
++)
{
if
(!
isWhiteSpace
(
data
[
i
]))
{
data
[
newSize
++]
=
data
[
i
];
}
}
return
newSize
;
}
}
ewaytek-deepseek-common/src/main/java/com/ewaytek/deepseek/common/utils/sign/Md5Utils.java
0 → 100644
View file @
0487b6d5
package
com.ewaytek.deepseek.common.utils.sign
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
java.nio.charset.StandardCharsets
;
import
java.security.MessageDigest
;
/**
* Md5加密方法
*
* @author admin
*/
public
class
Md5Utils
{
private
static
final
Logger
log
=
LoggerFactory
.
getLogger
(
Md5Utils
.
class
);
private
static
byte
[]
md5
(
String
s
)
{
MessageDigest
algorithm
;
try
{
algorithm
=
MessageDigest
.
getInstance
(
"MD5"
);
algorithm
.
reset
();
algorithm
.
update
(
s
.
getBytes
(
StandardCharsets
.
UTF_8
));
byte
[]
messageDigest
=
algorithm
.
digest
();
return
messageDigest
;
}
catch
(
Exception
e
)
{
log
.
error
(
"MD5 Error..."
,
e
);
}
return
null
;
}
private
static
final
String
toHex
(
byte
[]
hash
)
{
if
(
hash
==
null
)
{
return
null
;
}
StringBuffer
buf
=
new
StringBuffer
(
hash
.
length
*
2
);
int
i
;
for
(
i
=
0
;
i
<
hash
.
length
;
i
++)
{
if
((
hash
[
i
]
&
0xff
)
<
0x10
)
{
buf
.
append
(
"0"
);
}
buf
.
append
(
Long
.
toString
(
hash
[
i
]
&
0xff
,
16
));
}
return
buf
.
toString
();
}
public
static
String
hash
(
String
s
)
{
try
{
return
new
String
(
toHex
(
md5
(
s
)).
getBytes
(
StandardCharsets
.
UTF_8
),
StandardCharsets
.
UTF_8
);
}
catch
(
Exception
e
)
{
log
.
error
(
"not supported charset...{}"
,
e
);
return
s
;
}
}
}
ewaytek-deepseek-common/src/main/java/com/ewaytek/deepseek/common/utils/spring/SpringUtils.java
0 → 100644
View file @
0487b6d5
package
com.ewaytek.deepseek.common.utils.spring
;
import
com.ewaytek.deepseek.common.utils.StringUtils
;
import
org.springframework.aop.framework.AopContext
;
import
org.springframework.beans.BeansException
;
import
org.springframework.beans.factory.NoSuchBeanDefinitionException
;
import
org.springframework.beans.factory.config.BeanFactoryPostProcessor
;
import
org.springframework.beans.factory.config.ConfigurableListableBeanFactory
;
import
org.springframework.context.ApplicationContext
;
import
org.springframework.context.ApplicationContextAware
;
import
org.springframework.stereotype.Component
;
/**
* spring工具类 方便在非spring管理环境中获取bean
*
* @author admin
*/
@Component
public
final
class
SpringUtils
implements
BeanFactoryPostProcessor
,
ApplicationContextAware
{
/**
* Spring应用上下文环境
*/
private
static
ConfigurableListableBeanFactory
beanFactory
;
private
static
ApplicationContext
applicationContext
;
/**
* 获取对象
*
* @param name
* @return Object 一个以所给名字注册的bean的实例
* @throws org.springframework.beans.BeansException
*/
@SuppressWarnings
(
"unchecked"
)
public
static
<
T
>
T
getBean
(
String
name
)
throws
BeansException
{
return
(
T
)
beanFactory
.
getBean
(
name
);
}
/**
* 获取类型为requiredType的对象
*
* @param clz
* @return
* @throws org.springframework.beans.BeansException
*/
public
static
<
T
>
T
getBean
(
Class
<
T
>
clz
)
throws
BeansException
{
T
result
=
beanFactory
.
getBean
(
clz
);
return
result
;
}
/**
* 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
*
* @param name
* @return boolean
*/
public
static
boolean
containsBean
(
String
name
)
{
return
beanFactory
.
containsBean
(
name
);
}
/**
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
*
* @param name
* @return boolean
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*/
public
static
boolean
isSingleton
(
String
name
)
throws
NoSuchBeanDefinitionException
{
return
beanFactory
.
isSingleton
(
name
);
}
/**
* @param name
* @return Class 注册对象的类型
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*/
public
static
Class
<?>
getType
(
String
name
)
throws
NoSuchBeanDefinitionException
{
return
beanFactory
.
getType
(
name
);
}
/**
* 如果给定的bean名字在bean定义中有别名,则返回这些别名
*
* @param name
* @return
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*/
public
static
String
[]
getAliases
(
String
name
)
throws
NoSuchBeanDefinitionException
{
return
beanFactory
.
getAliases
(
name
);
}
/**
* 获取aop代理对象
*
* @param invoker
* @return
*/
@SuppressWarnings
(
"unchecked"
)
public
static
<
T
>
T
getAopProxy
(
T
invoker
)
{
return
(
T
)
AopContext
.
currentProxy
();
}
/**
* 获取当前的环境配置,无配置返回null
*
* @return 当前的环境配置
*/
public
static
String
[]
getActiveProfiles
()
{
return
applicationContext
.
getEnvironment
().
getActiveProfiles
();
}
/**
* 获取当前的环境配置,当有多个环境配置时,只获取第一个
*
* @return 当前的环境配置
*/
public
static
String
getActiveProfile
()
{
final
String
[]
activeProfiles
=
getActiveProfiles
();
return
StringUtils
.
isNotEmpty
(
activeProfiles
)
?
activeProfiles
[
0
]
:
null
;
}
/**
* 获取配置文件中的值
*
* @param key 配置文件的key
* @return 当前的配置文件的值
*/
public
static
String
getRequiredProperty
(
String
key
)
{
return
applicationContext
.
getEnvironment
().
getRequiredProperty
(
key
);
}
@Override
public
void
postProcessBeanFactory
(
ConfigurableListableBeanFactory
beanFactory
)
throws
BeansException
{
SpringUtils
.
beanFactory
=
beanFactory
;
}
@Override
public
void
setApplicationContext
(
ApplicationContext
applicationContext
)
throws
BeansException
{
SpringUtils
.
applicationContext
=
applicationContext
;
}
}
ewaytek-deepseek-common/src/main/java/com/ewaytek/deepseek/common/utils/sql/SqlUtil.java
0 → 100644
View file @
0487b6d5
package
com.ewaytek.deepseek.common.utils.sql
;
import
com.ewaytek.deepseek.common.exception.UtilException
;
import
com.ewaytek.deepseek.common.utils.StringUtils
;
/**
* sql操作工具类
*
* @author admin
*/
public
class
SqlUtil
{
/**
* 限制orderBy最大长度
*/
private
static
final
int
ORDER_BY_MAX_LENGTH
=
500
;
/**
* 定义常用的 sql关键字
*/
public
static
String
SQL_REGEX
=
"and |extractvalue|updatexml|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |+|user()"
;
/**
* 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
*/
public
static
String
SQL_PATTERN
=
"[a-zA-Z0-9_\\ \\,\\.]+"
;
/**
* 检查字符,防止注入绕过
*/
public
static
String
escapeOrderBySql
(
String
value
)
{
if
(
StringUtils
.
isNotEmpty
(
value
)
&&
!
isValidOrderBySql
(
value
))
{
throw
new
UtilException
(
"参数不符合规范,不能进行查询"
);
}
if
(
StringUtils
.
length
(
value
)
>
ORDER_BY_MAX_LENGTH
)
{
throw
new
UtilException
(
"参数已超过最大限制,不能进行查询"
);
}
return
value
;
}
/**
* 验证 order by 语法是否符合规范
*/
public
static
boolean
isValidOrderBySql
(
String
value
)
{
return
value
.
matches
(
SQL_PATTERN
);
}
/**
* SQL关键字检查
*/
public
static
void
filterKeyword
(
String
value
)
{
if
(
StringUtils
.
isEmpty
(
value
))
{
return
;
}
String
[]
sqlKeywords
=
StringUtils
.
split
(
SQL_REGEX
,
"\\|"
);
for
(
String
sqlKeyword
:
sqlKeywords
)
{
if
(
StringUtils
.
indexOfIgnoreCase
(
value
,
sqlKeyword
)
>
-
1
)
{
throw
new
UtilException
(
"参数存在SQL注入风险"
);
}
}
}
}
ewaytek-deepseek-common/src/main/java/com/ewaytek/deepseek/common/utils/uuid/IdUtils.java
0 → 100644
View file @
0487b6d5
package
com.ewaytek.deepseek.common.utils.uuid
;
/**
* ID生成器工具类
*
* @author admin
*/
public
class
IdUtils
{
/**
* 获取随机UUID
*
* @return 随机UUID
*/
public
static
String
randomUUID
()
{
return
UUID
.
randomUUID
().
toString
();
}
/**
* 简化的UUID,去掉了横线
*
* @return 简化的UUID,去掉了横线
*/
public
static
String
simpleUUID
()
{
return
UUID
.
randomUUID
().
toString
(
true
);
}
/**
* 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID
*
* @return 随机UUID
*/
public
static
String
fastUUID
()
{
return
UUID
.
fastUUID
().
toString
();
}
/**
* 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID
*
* @return 简化的UUID,去掉了横线
*/
public
static
String
fastSimpleUUID
()
{
return
UUID
.
fastUUID
().
toString
(
true
);
}
}
ewaytek-deepseek-common/src/main/java/com/ewaytek/deepseek/common/utils/uuid/Seq.java
0 → 100644
View file @
0487b6d5
package
com.ewaytek.deepseek.common.utils.uuid
;
import
com.ewaytek.deepseek.common.utils.DateUtils
;
import
com.ewaytek.deepseek.common.utils.StringUtils
;
import
java.util.concurrent.atomic.AtomicInteger
;
/**
* @author admin 序列生成类
*/
public
class
Seq
{
// 通用序列类型
public
static
final
String
commSeqType
=
"COMMON"
;
// 上传序列类型
public
static
final
String
uploadSeqType
=
"UPLOAD"
;
// 机器标识
private
static
final
String
machineCode
=
"A"
;
// 通用接口序列数
private
static
final
AtomicInteger
commSeq
=
new
AtomicInteger
(
1
);
// 上传接口序列数
private
static
final
AtomicInteger
uploadSeq
=
new
AtomicInteger
(
1
);
/**
* 获取通用序列号
*
* @return 序列值
*/
public
static
String
getId
()
{
return
getId
(
commSeqType
);
}
/**
* 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串
*
* @return 序列值
*/
public
static
String
getId
(
String
type
)
{
AtomicInteger
atomicInt
=
commSeq
;
if
(
uploadSeqType
.
equals
(
type
))
{
atomicInt
=
uploadSeq
;
}
return
getId
(
atomicInt
,
3
);
}
/**
* 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串
*
* @param atomicInt 序列数
* @param length 数值长度
* @return 序列值
*/
public
static
String
getId
(
AtomicInteger
atomicInt
,
int
length
)
{
String
result
=
DateUtils
.
dateTimeNow
();
result
+=
machineCode
;
result
+=
getSeq
(
atomicInt
,
length
);
return
result
;
}
/**
* 序列循环递增字符串[1, 10 的 (length)幂次方), 用0左补齐length位数
*
* @return 序列值
*/
private
synchronized
static
String
getSeq
(
AtomicInteger
atomicInt
,
int
length
)
{
// 先取值再+1
int
value
=
atomicInt
.
getAndIncrement
();
// 如果更新后值>=10 的 (length)幂次方则重置为1
int
maxSeq
=
(
int
)
Math
.
pow
(
10
,
length
);
if
(
atomicInt
.
get
()
>=
maxSeq
)
{
atomicInt
.
set
(
1
);
}
// 转字符串,用0左补齐
return
StringUtils
.
padl
(
value
,
length
);
}
}
ewaytek-deepseek-common/src/main/java/com/ewaytek/deepseek/common/utils/uuid/UUID.java
0 → 100644
View file @
0487b6d5
package
com.ewaytek.deepseek.common.utils.uuid
;
import
com.ewaytek.deepseek.common.exception.UtilException
;
import
java.security.MessageDigest
;
import
java.security.NoSuchAlgorithmException
;
import
java.security.SecureRandom
;
import
java.util.Random
;
import
java.util.concurrent.ThreadLocalRandom
;
/**
* 提供通用唯一识别码(universally unique identifier)(UUID)实现
*
* @author admin
*/
public
final
class
UUID
implements
java
.
io
.
Serializable
,
Comparable
<
UUID
>
{
private
static
final
long
serialVersionUID
=
-
1185015143654744140L
;
/**
* 此UUID的最高64有效位
*/
private
final
long
mostSigBits
;
/**
* 此UUID的最低64有效位
*/
private
final
long
leastSigBits
;
/**
* 私有构造
*
* @param data 数据
*/
private
UUID
(
byte
[]
data
)
{
long
msb
=
0
;
long
lsb
=
0
;
assert
data
.
length
==
16
:
"data must be 16 bytes in length"
;
for
(
int
i
=
0
;
i
<
8
;
i
++)
{
msb
=
(
msb
<<
8
)
|
(
data
[
i
]
&
0xff
);
}
for
(
int
i
=
8
;
i
<
16
;
i
++)
{
lsb
=
(
lsb
<<
8
)
|
(
data
[
i
]
&
0xff
);
}
this
.
mostSigBits
=
msb
;
this
.
leastSigBits
=
lsb
;
}
/**
* 使用指定的数据构造新的 UUID。
*
* @param mostSigBits 用于 {@code UUID} 的最高有效 64 位
* @param leastSigBits 用于 {@code UUID} 的最低有效 64 位
*/
public
UUID
(
long
mostSigBits
,
long
leastSigBits
)
{
this
.
mostSigBits
=
mostSigBits
;
this
.
leastSigBits
=
leastSigBits
;
}
/**
* 获取类型 4(伪随机生成的)UUID 的静态工厂。
*
* @return 随机生成的 {@code UUID}
*/
public
static
UUID
fastUUID
()
{
return
randomUUID
(
false
);
}
/**
* 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
*
* @return 随机生成的 {@code UUID}
*/
public
static
UUID
randomUUID
()
{
return
randomUUID
(
true
);
}
/**
* 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
*
* @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能
* @return 随机生成的 {@code UUID}
*/
public
static
UUID
randomUUID
(
boolean
isSecure
)
{
final
Random
ng
=
isSecure
?
Holder
.
numberGenerator
:
getRandom
();
byte
[]
randomBytes
=
new
byte
[
16
];
ng
.
nextBytes
(
randomBytes
);
randomBytes
[
6
]
&=
0x0f
;
/* clear version */
randomBytes
[
6
]
|=
0x40
;
/* set to version 4 */
randomBytes
[
8
]
&=
0x3f
;
/* clear variant */
randomBytes
[
8
]
|=
0x80
;
/* set to IETF variant */
return
new
UUID
(
randomBytes
);
}
/**
* 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。
*
* @param name 用于构造 UUID 的字节数组。
* @return 根据指定数组生成的 {@code UUID}
*/
public
static
UUID
nameUUIDFromBytes
(
byte
[]
name
)
{
MessageDigest
md
;
try
{
md
=
MessageDigest
.
getInstance
(
"MD5"
);
}
catch
(
NoSuchAlgorithmException
nsae
)
{
throw
new
InternalError
(
"MD5 not supported"
);
}
byte
[]
md5Bytes
=
md
.
digest
(
name
);
md5Bytes
[
6
]
&=
0x0f
;
/* clear version */
md5Bytes
[
6
]
|=
0x30
;
/* set to version 3 */
md5Bytes
[
8
]
&=
0x3f
;
/* clear variant */
md5Bytes
[
8
]
|=
0x80
;
/* set to IETF variant */
return
new
UUID
(
md5Bytes
);
}
/**
* 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。
*
* @param name 指定 {@code UUID} 字符串
* @return 具有指定值的 {@code UUID}
* @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常
*/
public
static
UUID
fromString
(
String
name
)
{
String
[]
components
=
name
.
split
(
"-"
);
if
(
components
.
length
!=
5
)
{
throw
new
IllegalArgumentException
(
"Invalid UUID string: "
+
name
);
}
for
(
int
i
=
0
;
i
<
5
;
i
++)
{
components
[
i
]
=
"0x"
+
components
[
i
];
}
long
mostSigBits
=
Long
.
decode
(
components
[
0
]).
longValue
();
mostSigBits
<<=
16
;
mostSigBits
|=
Long
.
decode
(
components
[
1
]).
longValue
();
mostSigBits
<<=
16
;
mostSigBits
|=
Long
.
decode
(
components
[
2
]).
longValue
();
long
leastSigBits
=
Long
.
decode
(
components
[
3
]).
longValue
();
leastSigBits
<<=
48
;
leastSigBits
|=
Long
.
decode
(
components
[
4
]).
longValue
();
return
new
UUID
(
mostSigBits
,
leastSigBits
);
}
/**
* 返回指定数字对应的hex值
*
* @param val 值
* @param digits 位
* @return 值
*/
private
static
String
digits
(
long
val
,
int
digits
)
{
long
hi
=
1L
<<
(
digits
*
4
);
return
Long
.
toHexString
(
hi
|
(
val
&
(
hi
-
1
))).
substring
(
1
);
}
/**
* 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG)
*
* @return {@link SecureRandom}
*/
public
static
SecureRandom
getSecureRandom
()
{
try
{
return
SecureRandom
.
getInstance
(
"SHA1PRNG"
);
}
catch
(
NoSuchAlgorithmException
e
)
{
throw
new
UtilException
(
e
);
}
}
/**
* 获取随机数生成器对象<br>
* ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。
*
* @return {@link ThreadLocalRandom}
*/
public
static
ThreadLocalRandom
getRandom
()
{
return
ThreadLocalRandom
.
current
();
}
/**
* 返回此 UUID 的 128 位值中的最低有效 64 位。
*
* @return 此 UUID 的 128 位值中的最低有效 64 位。
*/
public
long
getLeastSignificantBits
()
{
return
leastSigBits
;
}
/**
* 返回此 UUID 的 128 位值中的最高有效 64 位。
*
* @return 此 UUID 的 128 位值中最高有效 64 位。
*/
public
long
getMostSignificantBits
()
{
return
mostSigBits
;
}
/**
* 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。
* <p>
* 版本号具有以下含意:
* <ul>
* <li>1 基于时间的 UUID
* <li>2 DCE 安全 UUID
* <li>3 基于名称的 UUID
* <li>4 随机生成的 UUID
* </ul>
*
* @return 此 {@code UUID} 的版本号
*/
public
int
version
()
{
// Version is bits masked by 0x000000000000F000 in MS long
return
(
int
)
((
mostSigBits
>>
12
)
&
0x0f
);
}
/**
* 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。
* <p>
* 变体号具有以下含意:
* <ul>
* <li>0 为 NCS 向后兼容保留
* <li>2 <a href="http://www.ietf.org/rfc/rfc4122.txt">IETF RFC 4122</a>(Leach-Salz), 用于此类
* <li>6 保留,微软向后兼容
* <li>7 保留供以后定义使用
* </ul>
*
* @return 此 {@code UUID} 相关联的变体号
*/
public
int
variant
()
{
// This field is composed of a varying number of bits.
// 0 - - Reserved for NCS backward compatibility
// 1 0 - The IETF aka Leach-Salz variant (used by this class)
// 1 1 0 Reserved, Microsoft backward compatibility
// 1 1 1 Reserved for future definition.
return
(
int
)
((
leastSigBits
>>>
(
64
-
(
leastSigBits
>>>
62
)))
&
(
leastSigBits
>>
63
));
}
/**
* 与此 UUID 相关联的时间戳值。
*
* <p>
* 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。<br>
* 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。
*
* <p>
* 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
* 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
*
* @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。
*/
public
long
timestamp
()
throws
UnsupportedOperationException
{
checkTimeBase
();
return
(
mostSigBits
&
0x0FFF
L
)
<<
48
//
|
((
mostSigBits
>>
16
)
&
0x0FFFF
L
)
<<
32
//
|
mostSigBits
>>>
32
;
}
/**
* 与此 UUID 相关联的时钟序列值。
*
* <p>
* 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。
* <p>
* {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出
* UnsupportedOperationException。
*
* @return 此 {@code UUID} 的时钟序列
* @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
*/
public
int
clockSequence
()
throws
UnsupportedOperationException
{
checkTimeBase
();
return
(
int
)
((
leastSigBits
&
0x3FFF000000000000
L
)
>>>
48
);
}
/**
* 与此 UUID 相关的节点值。
*
* <p>
* 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。
* <p>
* 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
* 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
*
* @return 此 {@code UUID} 的节点值
* @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
*/
public
long
node
()
throws
UnsupportedOperationException
{
checkTimeBase
();
return
leastSigBits
&
0x0000FFFFFFFFFFFF
L
;
}
/**
* 返回此{@code UUID} 的字符串表现形式。
*
* <p>
* UUID 的字符串表示形式由此 BNF 描述:
*
* <pre>
* {@code
* UUID = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
* time_low = 4*<hexOctet>
* time_mid = 2*<hexOctet>
* time_high_and_version = 2*<hexOctet>
* variant_and_sequence = 2*<hexOctet>
* node = 6*<hexOctet>
* hexOctet = <hexDigit><hexDigit>
* hexDigit = [0-9a-fA-F]
* }
* </pre>
*
* </blockquote>
*
* @return 此{@code UUID} 的字符串表现形式
* @see #toString(boolean)
*/
@Override
public
String
toString
()
{
return
toString
(
false
);
}
/**
* 返回此{@code UUID} 的字符串表现形式。
*
* <p>
* UUID 的字符串表示形式由此 BNF 描述:
*
* <pre>
* {@code
* UUID = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
* time_low = 4*<hexOctet>
* time_mid = 2*<hexOctet>
* time_high_and_version = 2*<hexOctet>
* variant_and_sequence = 2*<hexOctet>
* node = 6*<hexOctet>
* hexOctet = <hexDigit><hexDigit>
* hexDigit = [0-9a-fA-F]
* }
* </pre>
*
* </blockquote>
*
* @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串
* @return 此{@code UUID} 的字符串表现形式
*/
public
String
toString
(
boolean
isSimple
)
{
final
StringBuilder
builder
=
new
StringBuilder
(
isSimple
?
32
:
36
);
// time_low
builder
.
append
(
digits
(
mostSigBits
>>
32
,
8
));
if
(!
isSimple
)
{
builder
.
append
(
'-'
);
}
// time_mid
builder
.
append
(
digits
(
mostSigBits
>>
16
,
4
));
if
(!
isSimple
)
{
builder
.
append
(
'-'
);
}
// time_high_and_version
builder
.
append
(
digits
(
mostSigBits
,
4
));
if
(!
isSimple
)
{
builder
.
append
(
'-'
);
}
// variant_and_sequence
builder
.
append
(
digits
(
leastSigBits
>>
48
,
4
));
if
(!
isSimple
)
{
builder
.
append
(
'-'
);
}
// node
builder
.
append
(
digits
(
leastSigBits
,
12
));
return
builder
.
toString
();
}
// Comparison Operations
/**
* 返回此 UUID 的哈希码。
*
* @return UUID 的哈希码值。
*/
@Override
public
int
hashCode
()
{
long
hilo
=
mostSigBits
^
leastSigBits
;
return
((
int
)
(
hilo
>>
32
))
^
(
int
)
hilo
;
}
// -------------------------------------------------------------------------------------------------------------------
// Private method start
/**
* 将此对象与指定对象比较。
* <p>
* 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。
*
* @param obj 要与之比较的对象
* @return 如果对象相同,则返回 {@code true};否则返回 {@code false}
*/
@Override
public
boolean
equals
(
Object
obj
)
{
if
((
null
==
obj
)
||
(
obj
.
getClass
()
!=
UUID
.
class
))
{
return
false
;
}
UUID
id
=
(
UUID
)
obj
;
return
(
mostSigBits
==
id
.
mostSigBits
&&
leastSigBits
==
id
.
leastSigBits
);
}
/**
* 将此 UUID 与指定的 UUID 比较。
*
* <p>
* 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。
*
* @param val 与此 UUID 比较的 UUID
* @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。
*/
@Override
public
int
compareTo
(
UUID
val
)
{
// The ordering is intentionally set up so that the UUIDs
// can simply be numerically compared as two numbers
return
(
this
.
mostSigBits
<
val
.
mostSigBits
?
-
1
:
//
(
this
.
mostSigBits
>
val
.
mostSigBits
?
1
:
//
(
this
.
leastSigBits
<
val
.
leastSigBits
?
-
1
:
//
(
this
.
leastSigBits
>
val
.
leastSigBits
?
1
:
//
0
))));
}
/**
* 检查是否为time-based版本UUID
*/
private
void
checkTimeBase
()
{
if
(
version
()
!=
1
)
{
throw
new
UnsupportedOperationException
(
"Not a time-based UUID"
);
}
}
/**
* SecureRandom 的单例
*/
private
static
class
Holder
{
static
final
SecureRandom
numberGenerator
=
getSecureRandom
();
}
}
ewaytek-deepseek-common/src/main/java/com/ewaytek/deepseek/common/xss/Xss.java
0 → 100644
View file @
0487b6d5
package
com.ewaytek.deepseek.common.xss
;
import
javax.validation.Constraint
;
import
javax.validation.Payload
;
import
java.lang.annotation.ElementType
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.RetentionPolicy
;
import
java.lang.annotation.Target
;
/**
* 自定义xss校验注解
*
* @author admin
*/
@Retention
(
RetentionPolicy
.
RUNTIME
)
@Target
(
value
=
{
ElementType
.
METHOD
,
ElementType
.
FIELD
,
ElementType
.
CONSTRUCTOR
,
ElementType
.
PARAMETER
})
@Constraint
(
validatedBy
=
{
XssValidator
.
class
})
public
@interface
Xss
{
String
message
()
default
"不允许任何脚本运行"
;
Class
<?>[]
groups
()
default
{};
Class
<?
extends
Payload
>[]
payload
()
default
{};
}
ewaytek-deepseek-common/src/main/java/com/ewaytek/deepseek/common/xss/XssValidator.java
0 → 100644
View file @
0487b6d5
package
com.ewaytek.deepseek.common.xss
;
import
com.ewaytek.deepseek.common.utils.StringUtils
;
import
javax.validation.ConstraintValidator
;
import
javax.validation.ConstraintValidatorContext
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
/**
* 自定义xss校验注解实现
*
* @author admin
*/
public
class
XssValidator
implements
ConstraintValidator
<
Xss
,
String
>
{
private
static
final
String
HTML_PATTERN
=
"<(\\S*?)[^>]*>.*?|<.*? />"
;
public
static
boolean
containsHtml
(
String
value
)
{
StringBuilder
sHtml
=
new
StringBuilder
();
Pattern
pattern
=
Pattern
.
compile
(
HTML_PATTERN
);
Matcher
matcher
=
pattern
.
matcher
(
value
);
while
(
matcher
.
find
())
{
sHtml
.
append
(
matcher
.
group
());
}
return
pattern
.
matcher
(
sHtml
).
matches
();
}
@Override
public
boolean
isValid
(
String
value
,
ConstraintValidatorContext
constraintValidatorContext
)
{
if
(
StringUtils
.
isBlank
(
value
))
{
return
true
;
}
return
!
containsHtml
(
value
);
}
}
ewaytek-deepseek-web/pom.xml
0 → 100644
View file @
0487b6d5
<?xml version="1.0" encoding="UTF-8"?>
<project
xmlns=
"http://maven.apache.org/POM/4.0.0"
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
>
<modelVersion>
4.0.0
</modelVersion>
<parent>
<groupId>
com.ewaytek.deepseek
</groupId>
<artifactId>
ewaytek-deepseek
</artifactId>
<version>
0.0.1-SNAPSHOT
</version>
</parent>
<artifactId>
ewaytek-deepseek-web
</artifactId>
<properties>
<maven.compiler.source>
8
</maven.compiler.source>
<maven.compiler.target>
8
</maven.compiler.target>
<project.build.sourceEncoding>
UTF-8
</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- 流式异步响应客户端-->
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-web
</artifactId>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-webflux
</artifactId>
</dependency>
<dependency>
<groupId>
com.squareup.okhttp3
</groupId>
<artifactId>
okhttp
</artifactId>
<version>
4.2.0
</version>
</dependency>
<dependency>
<groupId>
com.squareup.okhttp3
</groupId>
<artifactId>
okhttp-sse
</artifactId>
<version>
4.2.0
</version>
</dependency>
<dependency>
<groupId>
com.ewaytek.deepseek
</groupId>
<artifactId>
ewaytek-deepseek-common
</artifactId>
<version>
0.0.1-SNAPSHOT
</version>
</dependency>
<!-- https://mvnrepository.com/artifact/cn.hutool/hutool-http -->
<dependency>
<groupId>
cn.hutool
</groupId>
<artifactId>
hutool-http
</artifactId>
<version>
5.8.35
</version>
</dependency>
<dependency>
<groupId>
cn.hutool
</groupId>
<artifactId>
hutool-all
</artifactId>
<version>
5.8.35
</version>
</dependency>
<dependency>
<groupId>
com.squareup.retrofit2
</groupId>
<artifactId>
retrofit
</artifactId>
<version>
2.9.0
</version>
</dependency>
<dependency>
<groupId>
com.squareup.retrofit2
</groupId>
<artifactId>
converter-jackson
</artifactId>
<version>
2.9.0
</version>
</dependency>
<dependency>
<groupId>
com.squareup.retrofit2
</groupId>
<artifactId>
adapter-rxjava2
</artifactId>
<version>
2.9.0
</version>
</dependency>
<dependency>
<groupId>
jakarta.servlet
</groupId>
<artifactId>
jakarta.servlet-api
</artifactId>
</dependency>
<dependency>
<groupId>
jakarta.servlet
</groupId>
<artifactId>
jakarta.servlet-api
</artifactId>
</dependency>
<dependency>
<groupId>
com.google.guava
</groupId>
<artifactId>
guava
</artifactId>
<version>
32.1.2-jre
</version>
<!-- 请根据需要选择版本 -->
</dependency>
<dependency>
<groupId>
com.alibaba.nls
</groupId>
<artifactId>
nls-sdk-recognizer
</artifactId>
<version>
2.1.6
</version>
</dependency>
<dependency>
<groupId>
com.alibaba.nls
</groupId>
<artifactId>
nls-sdk-transcriber
</artifactId>
<version>
2.1.6
</version>
</dependency>
<dependency>
<groupId>
com.alibaba.nls
</groupId>
<artifactId>
nls-sdk-tts
</artifactId>
<version>
2.2.14
</version>
</dependency>
<dependency>
<groupId>
com.alibaba.nls
</groupId>
<artifactId>
nls-sdk-common
</artifactId>
<version>
2.2.14
</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-maven-plugin
</artifactId>
<version>
2.5.15
</version>
<configuration>
<fork>
true
</fork>
<!-- 如果没有该配置,devtools不会生效 -->
</configuration>
<executions>
<execution>
<goals>
<goal>
repackage
</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>
org.apache.maven.plugins
</groupId>
<artifactId>
maven-war-plugin
</artifactId>
<version>
3.1.0
</version>
<configuration>
<failOnMissingWebXml>
false
</failOnMissingWebXml>
<warName>
${project.artifactId}
</warName>
</configuration>
</plugin>
</plugins>
<finalName>
${project.artifactId}
</finalName>
</build>
</project>
ewaytek-deepseek-web/src/main/java/com/ewaytek/deepseek/DeepSeekDemoApplication.java
0 → 100644
View file @
0487b6d5
package
com.ewaytek.deepseek
;
import
org.springframework.boot.SpringApplication
;
import
org.springframework.boot.autoconfigure.SpringBootApplication
;
@SpringBootApplication
public
class
DeepSeekDemoApplication
{
public
static
void
main
(
String
[]
args
)
{
SpringApplication
.
run
(
DeepSeekDemoApplication
.
class
,
args
);
}
}
ewaytek-deepseek-web/src/main/java/com/ewaytek/deepseek/config/AliTtsConfig.java
0 → 100644
View file @
0487b6d5
package
com.ewaytek.deepseek.config
;
import
com.alibaba.nls.client.AccessToken
;
import
com.alibaba.nls.client.protocol.NlsClient
;
import
lombok.Data
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.boot.context.properties.ConfigurationProperties
;
import
org.springframework.context.annotation.Configuration
;
import
javax.annotation.PostConstruct
;
import
java.io.IOException
;
/**
* @author yangtq
* @date 2025/3/28
*/
@Data
@Slf4j
@Configuration
@ConfigurationProperties
(
prefix
=
"tts"
)
public
class
AliTtsConfig
{
private
String
appKey
;
private
String
accessKeyId
;
private
String
accessKeySecret
;
private
String
voice
;
private
int
volume
;
private
int
speech_rate
;
private
int
pitch_rate
;
private
int
longText
;
private
static
NlsClient
client
;
private
static
AccessToken
accessToken
;
@PostConstruct
public
void
init
()
{
log
.
info
(
"客户端启动-------------------------->"
);
synchronized
(
this
)
{
applyAccessToken
(
accessKeyId
,
accessKeySecret
);
client
=
new
NlsClient
(
accessToken
.
getToken
());
log
.
info
(
"阿里云 NlsClient 初始化完毕"
);
}
}
public
static
void
applyAccessToken
(
String
accessKeyId
,
String
accessKeySecret
)
{
accessToken
=
new
AccessToken
(
accessKeyId
,
accessKeySecret
);
try
{
accessToken
.
apply
();
log
.
info
(
"get token: "
+
accessToken
.
getToken
()
+
", expire time: "
+
accessToken
.
getExpireTime
());
}
catch
(
IOException
e
)
{
log
.
error
(
"https获取accessToken失败!"
+
e
.
getMessage
());
}
}
public
static
AccessToken
getAccessToken
()
{
return
accessToken
;
}
public
static
NlsClient
getNlsClient
()
{
return
client
;
}
public
String
getAppKey
()
{
return
appKey
;
}
}
ewaytek-deepseek-web/src/main/java/com/ewaytek/deepseek/config/DeepSeekConfig.java
0 → 100644
View file @
0487b6d5
package
com.ewaytek.deepseek.config
;
import
lombok.Data
;
import
org.springframework.boot.context.properties.ConfigurationProperties
;
import
org.springframework.context.annotation.Configuration
;
import
java.util.List
;
import
java.util.Map
;
@Data
@Configuration
@ConfigurationProperties
(
prefix
=
"deepseek"
)
public
class
DeepSeekConfig
{
private
String
url
;
private
String
apiKey
;
private
String
model
;
private
Double
frequencyPenalty
;
private
Integer
maxTokens
;
private
Double
presencePenalty
;
private
ResponseFormat
responseFormat
;
private
List
<
String
>
stop
;
private
Boolean
stream
;
private
StreamOptions
streamOptions
;
private
Double
temperature
;
private
Double
topP
;
private
List
<
Tool
>
tools
;
private
String
toolChoice
;
private
Boolean
logprobs
;
private
Integer
topLogprobs
;
@Data
public
static
class
ResponseFormat
{
private
String
type
;
}
@Data
public
static
class
StreamOptions
{
private
Boolean
includeUsage
;
}
@Data
public
static
class
Tool
{
private
String
type
;
private
Function
function
;
}
@Data
public
static
class
Function
{
private
String
name
;
private
String
description
;
private
Map
<
String
,
Object
>
parameters
;
}
}
ewaytek-deepseek-web/src/main/java/com/ewaytek/deepseek/config/DifyAudioSseEventSourceListener.java
0 → 100644
View file @
0487b6d5
package
com.ewaytek.deepseek.config
;
import
com.ewaytek.deepseek.common.utils.StringUtils
;
import
com.ewaytek.deepseek.doadmin.vo.dify.DifyStreamVO
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
lombok.AllArgsConstructor
;
import
lombok.SneakyThrows
;
import
lombok.extern.slf4j.Slf4j
;
import
okhttp3.Response
;
import
okhttp3.ResponseBody
;
import
okhttp3.sse.EventSource
;
import
okhttp3.sse.EventSourceListener
;
import
org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter
;
import
java.util.Objects
;
/**
* dify sse事件监听器
* @author yangtq
* @date 2025/2/25
*/
@Slf4j
@AllArgsConstructor
public
class
DifyAudioSseEventSourceListener
extends
EventSourceListener
{
private
static
final
String
DONE_SIGNAL
=
"[DONE]"
;
private
final
ResponseBodyEmitter
emitter
;
/**
* {@inheritDoc}
*/
@Override
public
void
onOpen
(
EventSource
eventSource
,
Response
response
)
{
log
.
info
(
"Dify建立sse连接..."
);
}
/**
* {@inheritDoc}
*/
@SneakyThrows
@Override
public
void
onEvent
(
EventSource
eventSource
,
String
id
,
String
type
,
String
data
)
{
log
.
debug
(
"DifyEventSourceListener data : {}"
,
data
);
if
(
data
.
equals
(
DONE_SIGNAL
))
{
// 成功响应
emitter
.
complete
();
return
;
}
ObjectMapper
mapper
=
new
ObjectMapper
();
DifyStreamVO
completionResponse
=
mapper
.
readValue
(
data
,
DifyStreamVO
.
class
);
if
(
completionResponse
==
null
){
return
;
}
String
content
=
completionResponse
.
getAnswer
();
if
(
StringUtils
.
isEmpty
(
content
)){
return
;
}
try
{
emitter
.
send
(
"data: "
+
data
+
"\n\n"
);
// emitter.send(completionResponse);
}
catch
(
Exception
e
)
{
log
.
error
(
"sse信息推送失败!"
,
e
);
eventSource
.
cancel
();
}
}
@Override
public
void
onClosed
(
EventSource
eventSource
)
{
log
.
info
(
"Dify关闭sse连接..."
);
emitter
.
complete
();
}
@SneakyThrows
@Override
public
void
onFailure
(
EventSource
eventSource
,
Throwable
t
,
Response
response
)
{
if
(
Objects
.
isNull
(
response
))
{
return
;
}
ResponseBody
body
=
response
.
body
();
if
(
Objects
.
nonNull
(
body
))
{
log
.
error
(
"Dify sse连接异常data:{},异常:{}"
,
body
.
string
(),
t
);
}
else
{
log
.
error
(
"Dify sse连接异常data:{},异常:{}"
,
response
,
t
);
}
eventSource
.
cancel
();
}
}
ewaytek-deepseek-web/src/main/java/com/ewaytek/deepseek/config/DifyConfig.java
0 → 100644
View file @
0487b6d5
package
com.ewaytek.deepseek.config
;
import
com.ewaytek.deepseek.service.dify.impl.DifyApiClient
;
import
com.ewaytek.deepseek.service.dify.impl.KeyRandomStrategy
;
import
lombok.Data
;
import
lombok.extern.slf4j.Slf4j
;
import
okhttp3.OkHttpClient
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.boot.context.properties.ConfigurationProperties
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.context.annotation.DependsOn
;
import
org.springframework.context.annotation.Lazy
;
import
java.util.concurrent.TimeUnit
;
/**
* @author yangtq
* @date 2025/2/25
*/
@Data
@Slf4j
@Configuration
@ConfigurationProperties
(
prefix
=
"dify"
)
public
class
DifyConfig
{
@Value
(
"${dify.api-key}"
)
private
String
apiKey
;
@Value
(
"${dify.api-host}"
)
private
String
apiHost
;
@Value
(
"${dify.conversation_url}"
)
private
String
conversationUrl
;
@Value
(
"${dify.api_key_tts}"
)
private
String
apiKeyTts
;
@Bean
(
name
=
"difyApiClient"
)
public
DifyApiClient
difyApiClient
()
{
OkHttpClient
okHttpClient
;
okHttpClient
=
new
OkHttpClient
.
Builder
()
.
connectTimeout
(
30
,
TimeUnit
.
SECONDS
)
.
writeTimeout
(
600
,
TimeUnit
.
SECONDS
)
.
readTimeout
(
600
,
TimeUnit
.
SECONDS
)
.
build
();
return
DifyApiClient
.
builder
()
.
apiHost
(
apiHost
)
.
apiKey
(
apiKey
)
//自定义key使用策略 默认随机策略
.
keyStrategy
(
new
KeyRandomStrategy
())
.
okHttpClient
(
okHttpClient
)
.
build
();
}
}
ewaytek-deepseek-web/src/main/java/com/ewaytek/deepseek/config/DifySseEventSourceListener.java
0 → 100644
View file @
0487b6d5
package
com.ewaytek.deepseek.config
;
import
com.ewaytek.deepseek.common.utils.StringUtils
;
import
com.ewaytek.deepseek.doadmin.vo.dify.DifyStreamVO
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
lombok.AllArgsConstructor
;
import
lombok.SneakyThrows
;
import
lombok.extern.slf4j.Slf4j
;
import
okhttp3.Response
;
import
okhttp3.ResponseBody
;
import
okhttp3.sse.EventSource
;
import
okhttp3.sse.EventSourceListener
;
import
org.springframework.http.MediaType
;
import
org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter
;
import
java.util.Objects
;
/**
* dify sse事件监听器
* @author yangtq
* @date 2025/2/25
*/
@Slf4j
@AllArgsConstructor
public
class
DifySseEventSourceListener
extends
EventSourceListener
{
private
static
final
String
DONE_SIGNAL
=
"[DONE]"
;
private
final
ResponseBodyEmitter
emitter
;
/**
* {@inheritDoc}
*/
@Override
public
void
onOpen
(
EventSource
eventSource
,
Response
response
)
{
log
.
info
(
"Dify建立sse连接..."
);
}
/**
* {@inheritDoc}
*/
@SneakyThrows
@Override
public
void
onEvent
(
EventSource
eventSource
,
String
id
,
String
type
,
String
data
)
{
log
.
debug
(
"DifyEventSourceListener data : {}"
,
data
);
if
(
data
.
equals
(
DONE_SIGNAL
))
{
// 成功响应
emitter
.
complete
();
return
;
}
ObjectMapper
mapper
=
new
ObjectMapper
();
DifyStreamVO
completionResponse
=
mapper
.
readValue
(
data
,
DifyStreamVO
.
class
);
if
(
completionResponse
==
null
){
return
;
}
String
content
=
completionResponse
.
getAnswer
();
if
(
StringUtils
.
isEmpty
(
content
)){
return
;
}
try
{
emitter
.
send
(
"data: "
+
data
+
"\n\n"
);
// emitter.send(completionResponse);
}
catch
(
Exception
e
)
{
log
.
error
(
"sse信息推送失败!"
,
e
);
eventSource
.
cancel
();
}
}
@Override
public
void
onClosed
(
EventSource
eventSource
)
{
log
.
info
(
"Dify关闭sse连接..."
);
emitter
.
complete
();
}
@SneakyThrows
@Override
public
void
onFailure
(
EventSource
eventSource
,
Throwable
t
,
Response
response
)
{
if
(
Objects
.
isNull
(
response
))
{
return
;
}
ResponseBody
body
=
response
.
body
();
if
(
Objects
.
nonNull
(
body
))
{
log
.
error
(
"Dify sse连接异常data:{},异常:{}"
,
body
.
string
(),
t
);
}
else
{
log
.
error
(
"Dify sse连接异常data:{},异常:{}"
,
response
,
t
);
}
eventSource
.
cancel
();
}
}
ewaytek-deepseek-web/src/main/java/com/ewaytek/deepseek/config/DoubaoSseEventSourceListener.java
0 → 100644
View file @
0487b6d5
package
com.ewaytek.deepseek.config
;
import
com.alibaba.fastjson2.JSON
;
import
com.ewaytek.deepseek.doadmin.dto.doubao.Choices
;
import
com.ewaytek.deepseek.doadmin.dto.doubao.Delta
;
import
com.ewaytek.deepseek.doadmin.dto.doubao.DoubaoChatResponseDTO
;
import
com.ewaytek.deepseek.doadmin.vo.dify.DifyStreamVO
;
import
lombok.extern.slf4j.Slf4j
;
import
okhttp3.Response
;
import
okhttp3.sse.EventSource
;
import
okhttp3.sse.EventSourceListener
;
import
org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter
;
import
java.io.IOException
;
import
java.util.ArrayList
;
import
java.util.List
;
@Slf4j
public
class
DoubaoSseEventSourceListener
extends
EventSourceListener
{
private
static
final
String
DONE_SIGNAL
=
"[DONE]"
;
private
final
ResponseBodyEmitter
emitter
;
private
final
String
Uid
;
public
DoubaoSseEventSourceListener
(
ResponseBodyEmitter
sseEmitter
,
String
uid
)
{
this
.
emitter
=
sseEmitter
;
this
.
Uid
=
uid
;
}
@Override
public
void
onOpen
(
EventSource
eventSource
,
Response
response
)
{
log
.
info
(
"Dify 建立 SSE 连接..."
);
}
@Override
public
void
onEvent
(
EventSource
eventSource
,
String
id
,
String
type
,
String
data
)
{
log
.
info
(
"DifyEventSourceListener 收到数据: {}"
,
data
);
DifyStreamVO
blockingVO
=
JSON
.
parseObject
(
data
,
DifyStreamVO
.
class
);
if
(
"message_end"
.
equals
(
blockingVO
.
getEvent
()))
{
log
.
info
(
"Dify 流式响应完成"
);
try
{
sendProcessedResponse
();
}
catch
(
IOException
e
)
{
throw
new
RuntimeException
(
e
);
}
emitter
.
complete
();
return
;
}
try
{
// 直接将数据发送给客户端
// emitter.send("data: " + data + "\n\n");
sendProcessedResponse
(
blockingVO
);
}
catch
(
Exception
e
)
{
log
.
error
(
"SSE 信息推送失败!"
,
e
);
eventSource
.
cancel
();
}
}
private
void
sendProcessedResponse
(
DifyStreamVO
blockingVO
)
throws
IOException
{
try
{
Delta
delta
=
new
Delta
();
delta
.
setRole
(
"assistant"
);
delta
.
setContent
(
blockingVO
.
getAnswer
());
Choices
choices
=
new
Choices
();
choices
.
setFinish_reason
(
null
);
choices
.
setIndex
(
0
);
choices
.
setDelta
(
delta
);
List
<
Choices
>
list
=
new
ArrayList
<>();
list
.
add
(
choices
);
DoubaoChatResponseDTO
responseDTO
=
new
DoubaoChatResponseDTO
();
responseDTO
.
setId
(
Uid
);
responseDTO
.
setCreated
(
System
.
currentTimeMillis
());
responseDTO
.
setObject
(
"chat.completion.chunk"
);
responseDTO
.
setModel
(
"CustomLLM"
);
responseDTO
.
setChoices
(
list
);
log
.
info
(
"sendProcessedResponse "
+
JSON
.
toJSONString
(
responseDTO
));
emitter
.
send
(
"data: "
+
JSON
.
toJSONString
(
responseDTO
)
+
"\n\n"
);
}
catch
(
IOException
e
)
{
log
.
error
(
"发送数据时发生错误"
,
e
);
// 在发生错误时,尝试关闭连接
emitter
.
completeWithError
(
e
);
}
}
private
void
sendProcessedResponse
()
throws
IOException
{
try
{
Delta
delta
=
new
Delta
();
delta
.
setRole
(
"assistant"
);
delta
.
setContent
(
null
);
Choices
choices
=
new
Choices
();
choices
.
setFinish_reason
(
null
);
choices
.
setIndex
(
0
);
choices
.
setDelta
(
delta
);
List
<
Choices
>
list
=
new
ArrayList
<>();
list
.
add
(
choices
);
DoubaoChatResponseDTO
responseDTO
=
new
DoubaoChatResponseDTO
();
responseDTO
.
setId
(
Uid
);
responseDTO
.
setCreated
(
System
.
currentTimeMillis
());
responseDTO
.
setObject
(
"chat.completion.chunk"
);
responseDTO
.
setModel
(
"CustomLLM"
);
responseDTO
.
setChoices
(
list
);
log
.
info
(
"sendProcessedResponse "
+
JSON
.
toJSONString
(
responseDTO
));
emitter
.
send
(
"data: "
+
JSON
.
toJSONString
(
responseDTO
)
+
"\n\n"
);
}
catch
(
IOException
e
)
{
log
.
error
(
"发送数据时发生错误"
,
e
);
// 在发生错误时,尝试关闭连接
emitter
.
completeWithError
(
e
);
}
}
@Override
public
void
onClosed
(
EventSource
eventSource
)
{
log
.
info
(
"Dify 关闭 SSE 连接..."
);
emitter
.
complete
();
}
@Override
public
void
onFailure
(
EventSource
eventSource
,
Throwable
t
,
Response
response
)
{
log
.
error
(
"Dify SSE 连接异常: {}"
,
t
.
getMessage
());
if
(
response
!=
null
)
{
try
{
log
.
error
(
"异常响应数据: {}"
,
response
.
body
().
string
());
}
catch
(
IOException
e
)
{
log
.
error
(
"读取异常响应数据失败"
,
e
);
}
}
eventSource
.
cancel
();
emitter
.
completeWithError
(
t
);
// 将错误传递给客户端
}
}
ewaytek-deepseek-web/src/main/java/com/ewaytek/deepseek/config/OkHttpConfig.java
0 → 100644
View file @
0487b6d5
package
com.ewaytek.deepseek.config
;
import
okhttp3.OkHttpClient
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
java.util.concurrent.TimeUnit
;
@Configuration
public
class
OkHttpConfig
{
@Bean
public
OkHttpClient
okHttpClient
()
{
return
new
OkHttpClient
.
Builder
()
.
connectTimeout
(
120
,
TimeUnit
.
SECONDS
)
// 设置连接超时时间为 120 秒
.
writeTimeout
(
120
,
TimeUnit
.
SECONDS
)
// 设置写超时时间为 120 秒
.
readTimeout
(
120
,
TimeUnit
.
SECONDS
)
// 设置读超时时间为 120 秒
.
build
();
}
}
Prev
1
2
3
4
5
6
7
8
Next
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