前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >sql2java-excel(二):基于apache poi实现数据库表的导出的spring web支持

sql2java-excel(二):基于apache poi实现数据库表的导出的spring web支持

作者头像
10km
发布于 2022-09-27 00:23:57
发布于 2022-09-27 00:23:57
1.6K00
代码可运行
举报
文章被收录于专栏:10km的专栏10km的专栏
运行总次数:0
代码可运行

sql2java是我几年年开始写的一个sql2java是一个轻量级数据库(SQL)访问代码(java)生成器。这几年一直在根据工作需要维护升级,最近的项目中需要对数据库的记录提供导出excel的功能。

就开始学习apache的POI,参照网上的示例实现了单张表的导出。并进一步将它封装成一个通用库成为sql2java下的子项目sql2java-excel.以方便在其他项目中技术复用。

本文开始介绍sql2java-excel的使用spring 支持的部分

Spring Web导出

快速入门

以下是基于Spring Web的数据库导出最简示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import static gu.sql2java.Managers.instanceOf;

@RestController
@Api(value="ExcelController",tags={"Excel Export Controller"})
public class ExcelController {
	/** 
	 * 这里必须指定produces,否则swagger下载的excel文件不正确,
	 * 参见 <a href="https://blog.csdn.net/iuie_sl/article/details/114904170 ">《使用swagger api 下载excel,excel打不开》</a>
	 */
    @ApiOperation(value = "导出设备列表", notes = "",httpMethod="GET",produces=MediaType.APPLICATION_OCTET_STREAM_VALUE)
    @RequestMapping(method=RequestMethod.GET, value="/ExcelController/exportDevices")
	public void exportDevices(HttpServletResponse response) throws IOException {
		response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
		SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd_HHmmss");
		String currentDateTime = dateFormatter.format(new Date());
		/** 设置下载文件文件名 */
		String headerKey = "Content-Disposition";
		String headerValue = "attachment; filename=device_" + currentDateTime + ".xlsx";
		response.setHeader(headerKey, headerValue);
		/** 从数据库中获取记录 **/
		List<DeviceBean> beans = instanceOf(IDeviceManager.class).loadAllAsList();
		ExcelGenerator<DeviceBean> generator = new ExcelGenerator<DeviceBean>(beans){};
        /** 输出到 http response */ 
		generator.generate(response);
	}

}

自定义导出配置

以下是基于Spring Web的数据库导出示例,与前一个示例不同的就是增加了通过SheetConfig对象设置excel 导出配置参数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import static gu.sql2java.Managers.instanceOf;

@RestController
@Api(value="ExcelController",tags={"Excel Export Controller"})
public class ExcelController {
	/** 
	 * 这里必须指定produces,否则swagger下载的excel文件不正确,
	 * 参见 <a href="https://blog.csdn.net/iuie_sl/article/details/114904170 ">《使用swagger api 下载excel,excel打不开》</a>
	 */
    @ApiOperation(value = "导出设备列表", notes = "",httpMethod="GET",produces=MediaType.APPLICATION_OCTET_STREAM_VALUE)
    @RequestMapping(method=RequestMethod.GET, value="/ExcelController/exportDevices")
	public void exportDevices(HttpServletResponse response) throws IOException {
		response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
		SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd_HHmmss");
		String currentDateTime = dateFormatter.format(new Date());
		/** 设置下载文件文件名 */
		String headerKey = "Content-Disposition";
		String headerValue = "attachment; filename=device_" + currentDateTime + ".xlsx";
		response.setHeader(headerKey, headerValue);
		/** 从数据库中获取记录 **/
		List<DeviceBean> beans = instanceOf(IDeviceManager.class).loadAllAsList();
		ExcelGenerator<DeviceBean> generator = new ExcelGenerator<DeviceBean>(beans){};
        /** excel 导出配置对象 */
        SheetConfig sheetConfig = generator.getSheetConfig();
        /** 设置exce表的标题 */
    	sheetConfig.setTitle("设备表记录");
        /** 设置输出字段列名,如果不指定则输出原始的英文字段名 */
        sheetConfig.configOf("physicalAddress").getColumnConfig().setName("物理地址");
    	sheetConfig.configOf("addressType").getColumnConfig().setName("地址类型");
    	sheetConfig.configOf("iotCard").getColumnConfig().setName("物联网卡编号");
    	sheetConfig.configOf("status").getColumnConfig().setName("设备状态").setReadConverterExp("ENABLE=正常,DISABLE=禁用,MAINTAIN=维护,PENDING=挂起(待审核)");
    	sheetConfig.configOf("fixedMode").getColumnConfig().setName("安装方式").setReadConverterExp("HANG=悬挂,FLOOR=落地");
    	sheetConfig.configOf("name").getColumnConfig().setName("设备名称");
    	sheetConfig.configOf("groupId").getColumnConfig().setName("设备组ID");
    	sheetConfig.configOf("model").getColumnConfig().setName("设备型号");
    	sheetConfig.configOf("vendor").getColumnConfig().setName("设备供应商");
    	sheetConfig.configOf("osArch").getColumnConfig().setName("操作系统平台");
    	sheetConfig.configOf("versionInfo").getColumnConfig().setName("版本");
    	sheetConfig.configOf("planId").getColumnConfig().setName("当前节目ID");
    	sheetConfig.configOf("targetId").getColumnConfig().setName("目标节目ID");
    	sheetConfig.configOf("screenInfo").getColumnConfig().setName("屏幕信息").setHandler(ScreenInfoFormatter.class);
    	sheetConfig.configOf("createTime").getColumnConfig().setName("记录创建时间");
    	sheetConfig.configOf("updateTime").getColumnConfig().setName("记录更新时间");
    	sheetConfig.configOf("remark").getColumnConfig().setName("备注");
        /** 设置子成员输出字段名 */
    	sheetConfig.addNestedColumn("props.last_active_time","上次在线时间");
    	sheetConfig.addNestedColumn("props.disk_capacity","磁盘容量");
    	sheetConfig.addNestedColumn("props.network","网络连接类型");
    	sheetConfig.addNestedColumn("props.status_comment","状态变更说明");
    	sheetConfig.addNestedColumn("device_detail.device_name","产品名称");
    	sheetConfig.addNestedColumn("device_detail.manufacturer","制造商");
    	sheetConfig.addNestedColumn("device_detail.made_date","生产日期");
        /** 设置不需要导出的隐藏字段 */
    	sheetConfig.setHideColumns("props","token_time","device_detail");
        /** 输出到 http response */ 
		generator.generate(response);
	}
}

Spring AOP导出

启用aspect

excelGenerator支持Spring AOP(切面),需要如下在@ComponentScan注解中增加包名gu.sql2java.excel.aspect,才能让spring扫描到excelGenerator的切面导出实现类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@SpringBootApplication
@ComponentScan({"net.facelib.eam.devicecenter","net.facelib.eam.web.handler","gu.sql2java.excel.aspect"})
public class RestfulService {
	//
}

快速入门

通过对服务方法增加@ExcelSheet注解,就可以实现数据库记录的Excel导出.

  • 要求服务方法的返回类型为Collection,Set,List以及任何实现了java.lang.Iterable接口的可迭代容器
  • 要求容器元素类型为Map或Java Bean(fastjson的JSONObject也属于Map)
  • 不限制HTTP method的类型,可以是POST,也可以是GET

以下是基于Spring AOP的数据库导出最简示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
.	/** 
	 * 设备表导出EXCEL<br>
	 */
	@ExcelSheet()
	/** 
	 * 这里必须指定produces,否则swagger下载的excel文件不正确,
	 * 参见 <a href="https://blog.csdn.net/iuie_sl/article/details/114904170 ">《使用swagger api 下载excel,excel打不开》</a>
	 */
	@ApiOperation(value = "设备表导出EXCEL", notes = "设备表导出EXCEL",httpMethod="GET",produces="application/octet-stream")
    @RequestMapping(method=RequestMethod.GET,produces={"application/octet-stream")
	public List<JSONObject> exportDevices() {
		return instanceOf(IDeviceManager.class).loadAllAsList();

	}

Excel输出配置

如果需要对导出的EXCEL进行配置(标题,列名,字体等等),可以通过在服务方法中增加注解来实现,基于上面的示例在exportDevices方法中增加@ExcelSheet注解对Excel 输出的全局参数进行配置,增加 @ExcelColumn注解对各个输出字段进行配置

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
	/** 
	 * 设备表导出EXCEL<br>
	 */
	@ExcelSheet(title="设备表记录",
			fileNamePrefix = "device_",
			hideColumns={"props","token_time","device_detail"},
			defaultIncludeColumns={"id","provinces","city","top_group_name","name",
					"physicalAddress","addressType","iotCard","network","screenInfo","fixedMode","model","osArch","versionInfo",
					"status","props.disk_capacity","props.last_active_time","createTime","remark"})
	@ExcelColumn(columnName="physicalAddress",name="物理地址")
	@ExcelColumn(columnName="addressType",name="地址类型")
	@ExcelColumn(columnName="iotCard",name="物联网卡编号")
	@ExcelColumn(columnName="status",name="设备状态",readConverterExp="ENABLE=正常,DISABLE=禁用,MAINTAIN=维护,PENDING=挂起(待审核)")
	@ExcelColumn(columnName="fixedMode",name="安装方式",readConverterExp="HANG=悬挂,FLOOR=落地")
	@ExcelColumn(columnName="name",name="设备名称")
	@ExcelColumn(columnName="groupId",name="设备组ID")
	@ExcelColumn(columnName="provinces",name="省/自治区/直辖市")
	@ExcelColumn(columnName="city",name="市")
	@ExcelColumn(columnName="top_group_name",name="设备组名称")
	@ExcelColumn(columnName="screenInfo",name="屏幕信息")
	@ExcelColumn(columnName="name",name="设备名称")
	@ExcelColumn(columnName="model",name="设备型号")
	@ExcelColumn(columnName="vendor",name="设备供应商")
	@ExcelColumn(columnName="osArch",name="操作系统平台")
	@ExcelColumn(columnName="network",name="网络连接类型")
	@ExcelColumn(columnName="versionInfo",name="版本")
	@ExcelColumn(columnName="planId",name="当前节目ID")
	@ExcelColumn(columnName="targetId",name="目标节目ID")
	@ExcelColumn(columnName="createTime",name="记录创建时间")
	@ExcelColumn(columnName="updateTime",name="记录修改时间")
	@ExcelColumn(columnName="remark",name="备注")
	@ExcelColumn(columnName="props.last_active_time",name="上次在线时间")
	@ExcelColumn(columnName="props.disk_capacity",name="磁盘容量")
	@ExcelColumn(columnName="props.status_comment",name="状态变更说明")
	@ExcelColumn(columnName="device_detail.device_name",name="产品名称")
	@ExcelColumn(columnName="device_detail.manufacturer",name="制造商")
	@ExcelColumn(columnName="device_detail.made_date",name="生产日期")
	/** 
	 * 这里必须指定produces,否则swagger下载的excel文件不正确,
	 * 参见 <a href="https://blog.csdn.net/iuie_sl/article/details/114904170 ">《使用swagger api 下载excel,excel打不开》</a>
	 */
	@ApiOperation(value = "设备表导出EXCEL", notes = "设备表导出EXCEL",httpMethod="GET",produces="application/octet-stream")
    @RequestMapping(method=RequestMethod.GET,produces="application/octet-stream")
	public List<DeviceBean> exportDevices() {
		return instanceOf(IDeviceManager.class).loadAllAsList();
	}

Web端控制输出格式

Spring AOP方式导出Excel也支持Web端通过请求参数控制Excel的输出格式,这种方式要求服务方法定义与@ExcelSheet注解中对应的方法名同名的参数,切面执行时会自动将这些参数注入到@ExcelSheet注解中。

基于上面的exportDevices服务方法改造示例如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
	/** 
	 * 设备表导出EXCEL<br>
	 */
	@ExcelSheet(title="设备表记录",
			fileNamePrefix = "device_",
			hideColumns={"props","token_time","device_detail"},
			defaultIncludeColumns={"id","provinces","city","top_group_name","name",
					"physicalAddress","addressType","iotCard","network","screenInfo","fixedMode","model","osArch","versionInfo",
					"status","props.disk_capacity","props.last_active_time","createTime","remark"})
	@ExcelColumn(columnName="physicalAddress",name="物理地址")
	@ExcelColumn(columnName="addressType",name="地址类型")
	@ExcelColumn(columnName="iotCard",name="物联网卡编号")
	@ExcelColumn(columnName="status",name="设备状态",readConverterExp="ENABLE=正常,DISABLE=禁用,MAINTAIN=维护,PENDING=挂起(待审核)")
	@ExcelColumn(columnName="fixedMode",name="安装方式",readConverterExp="HANG=悬挂,FLOOR=落地")
	@ExcelColumn(columnName="name",name="设备名称")
	@ExcelColumn(columnName="groupId",name="设备组ID")
	@ExcelColumn(columnName="provinces",name="省/自治区/直辖市")
	@ExcelColumn(columnName="city",name="市")
	@ExcelColumn(columnName="top_group_name",name="设备组名称")
	@ExcelColumn(columnName="screenInfo",name="屏幕信息")
	@ExcelColumn(columnName="name",name="设备名称")
	@ExcelColumn(columnName="model",name="设备型号")
	@ExcelColumn(columnName="vendor",name="设备供应商")
	@ExcelColumn(columnName="osArch",name="操作系统平台")
	@ExcelColumn(columnName="network",name="网络连接类型")
	@ExcelColumn(columnName="versionInfo",name="版本")
	@ExcelColumn(columnName="planId",name="当前节目ID")
	@ExcelColumn(columnName="targetId",name="目标节目ID")
	@ExcelColumn(columnName="createTime",name="记录创建时间")
	@ExcelColumn(columnName="updateTime",name="记录修改时间")
	@ExcelColumn(columnName="remark",name="备注")
	@ExcelColumn(columnName="props.last_active_time",name="上次在线时间")
	@ExcelColumn(columnName="props.disk_capacity",name="磁盘容量")
	@ExcelColumn(columnName="props.status_comment",name="状态变更说明")
	@ExcelColumn(columnName="device_detail.device_name",name="产品名称")
	@ExcelColumn(columnName="device_detail.manufacturer",name="制造商")
	@ExcelColumn(columnName="device_detail.made_date",name="生产日期")
	/** 
	 * 这里必须指定produces,否则swagger下载的excel文件不正确,
	 * 参见 <a href="https://blog.csdn.net/iuie_sl/article/details/114904170 ">《使用swagger api 下载excel,excel打不开》</a>
	 */
	@ApiOperation(value = "设备表导出EXCEL", notes = "设备表导出EXCEL",httpMethod="GET",produces="application/octet-stream,application/json")
    @RequestMapping(method=RequestMethod.GET,produces={"application/octet-stream","application/json"})
	public List<DeviceBean> exportDevices(Integer getParameter, String exportFileName, 
			String sheetName, 
			String title, String titleFontName, Integer titleFontHeight, IndexedColors titleFontColor, IndexedColors titleFillColor, HorizontalAlignment titleHorizontalAlign, 
			String headerFontName, Integer headerFontHeight, IndexedColors headerFontColor, IndexedColors headerFillColor, HorizontalAlignment headerHorizontalAlign, Boolean firstBold, 
			Integer fontHeight, String fontName, IndexedColors fontColor, IndexedColors fillColor, HorizontalAlignment horizontalAlign, 
			String integralFormat, String dateTimeFormat, String dateFormat, String timeFormat, String timestampFormat, 
			List<String> includeColumns, List<String> excludeColumns) {
		return instanceOf(IDeviceManager.class).loadAllAsList();
	}

完整示例代码参见:

https://gitee.com/l0km/sql2java/blob/master/sql2java-excel/src/test/java/gu/sql2java/excel/ExcelExportTest.java

请求参数说明

所有的请求参数都不是必须定义的,应用层根据需要决定允许哪些参数被Web端控制就定义对应的参数。

请求参数定义的顺序也没有要求,但参数名必须与@ExcelSheet中对应的方法名一致才有效,类型也必须一致,对于枚举类型的参数,类型为String,切面执行时会自动将枚举变量名字符串转为枚举类型变量。

参数名

类型

默认值

说明

getParameter

Integer

为null时,输出Excel数据, 前端的Response Content Type需要设置为application/octet-stream,不为null时,根据getParameter的值返回JSON格式的结果,Response Content Type需要设置为application/json,参见后面的getParameter 参数值说明。

exportFileName

String

指定导出的excel文件名,不指定则自动以日期命名

sheetName

String

exportedExcel

excel Sheet名字

title

String

标题,为null或空不输出标题

titleFontName

String

Calibri

标题字体名

titleFontHeight

Integer

32

标题字体高度

titleFontColor

IndexedColors

BLACK

标题字体颜色

titleFillColor

IndexedColors

WHITE

标题单元背景填充颜色

titleHorizontalAlign

HorizontalAlignment

CENTER

标题对齐方式

headerFontName

String

Calibri

首行(字段名)字体名

headerFontHeight

Integer

16

首行(字段名)字体高度

headerFontColor

IndexedColors

BLACK

首行(字段名)字体颜色

headerFillColor

IndexedColors

WHITE

单元背景填充颜色

headerHorizontalAlign

HorizontalAlignment

CENTER

首行对齐方式

firstBold

Boolean

true

字体设置:true首行(字段名)字体加粗

fontHeight

Integer

16

默认单元格字体高度

fontName

String

Calibri

默认单元格字体名

fontColor

IndexedColors

BLACK

默认单元格字体颜色

fillColor

IndexedColors

WHITE

默认单元格背景填充颜色

horizontalAlign

HorizontalAlignment

CENTER

默认单元格水平对齐方式

integralFormat

String

0

默认整数(Integer,Long,Short)格式

dateTimeFormat

String

yyyy-MM-dd HH:mm:ss

(java.util.Date)日期时间格式

dateFormat

String

yyyy-MM-dd

(java.sql.Date)日期格式

timeFormat

String

HH:mm:ss

(java.sql.Time)时间格式

timestampFormat

String

yyyy-MM-dd’T’HH:mm:ss.SSSZ

(java.sql.Timestamp)时间戳格式

includeColumns

List<String>

{}

字段输出白名单,在此名单中的字段会被输出,同时指定白名单和黑名单时以白名单为准,此名单为null则使用默认输出字段列表

excludeColumns

List<String>

{}

字段输出黑名单,在此名单中的字段不会被输出,同时指定白名单和黑名单时以白名单为准


getParameter 参数值说明

参数值

说明

1

返回所有可选的字段列表,参见SheetConfig#getAvailableColumns()

2

返回默认输出的字段名及字段显示名称,参见SheetConfig#getDefaultExportColumns()

3

返回默认输出的字段名,参见SheetConfig#getDefaultExportColumnNames()

其他

抛出异常

produces设置

如果定义了getParameter参数,getParameter参数不为null时服务方法会返回JSON格式的数据,所以在@ApiOperation@RequestMapping注解的produces字段需要增加内容类型application/json

与WhereHelper配合

SpringAOP导出支持@ExcelSheet@EnableWhereHelper注解同时使用自动生成SELECT WHERE条件查询语句导出数据

基于上面的exportDevices服务方法改造示例如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
	/** 
	 * 设备表导出EXCEL<br>
	 *  查询参数说明:
	 * <ul>
	 * <li>orderBy 排序字段名,格式:${字段1}[DESC|ASC]...,${字段N}[DESC|ASC]), 默认排序字段:id</li>
	 * <li>provinces        ----不为null时过滤设备所属顶级设备组的省份名称</li>
	 * <li>city        ----不为null时过滤设备所属顶级设备组的城市名称</li>
	 * <li>group_id 不为null时过滤grup_id(所属设备组id)字段</li>
	 * <li>status 不为null时过滤status(设备状态)字段,可用值:ENABLE:正常,DISABLE:禁用,MAINTAIN:维护,PENDING:挂起(待审核)</li>
	 * <li>address_type 不为null时过滤address_type(设备物理地址类型)字段(),可用值:MAC(网卡MAC地址),IMEI(国际移动设备识别码)</li>
	 * <li>physical_address 不为null时过滤physical_address(设备物理地址)字段,模糊匹配所有physical_address字段包含${physical_address}的记录</li>
	 * <li>iot_card 不为null时过滤iot_card(设备物联网卡编号)字段,模糊匹配所有iot_card字段包含${iot_card}的记录</li>
	 * <li>screen_info 不为null时过滤screen_info(设备屏幕信息)字段,模糊匹配所有screen_info字段包含${screen_info}的记录,格式示例:15H1080x960--15(英)寸横屏分辨率1080x960</li>
	 * <li>os_arch 不为null时过滤os_arch(操作系统名称及版本及硬件架构名称)字段,模糊匹配所有os_arch字段包含${os_arch}的记录,例如:Windows-x86_64,Linux-x86_64,Android-arm</li>
	 * <li>network   ----不为null时过滤network(设备网络连接类型)字段,目前支持类型:4G,WIFI,ETHERNET</li>
	 * <li>version_info 不为null时过滤version_info(设备端应用程序的版本号)字段,模糊匹配所有version_info字段包含${version_info}的记录</li>
	 * <li>model 不为null时过滤model(设备产品型号)字段,模糊匹配所有model字段包含${model}的记录</li>
	 * <li>vendor 不为null时过滤vendor(设备供应商)字段,模糊匹配所有vendor字段包含${vendor}的记录</li>
	 * <li>name 不为null时过滤设备名称字段,模糊匹配所有name字段包含${name}的记录</li>
	 * <li>create_time_max 不为null时过滤create_time(设备记录创建时间字段),查询所有create_time早于create_time_max的记录</li>
	 * <li>create_time_min 不为null时过滤create_time字段(设备记录创建时间),查询所有create_time晚于create_time_max的记录</li>
	 * <li>update_time_max 不为null时过滤update_time(设备记录更新时间字段),查询所有update_time早于update_time_max的记录</li>
	 * <li>update_time_min 不为null时过滤update_time(设备记录更新时间字段),查询所有update_time晚于update_time_max的记录</li>
	 * </ul>
	 */
	@ExcelSheet(title="设备表记录",
			fileNamePrefix = "device_",
			hideColumns={"props","token_time","device_detail"},
			defaultIncludeColumns={"id","provinces","city","top_group_name","name",
					"physicalAddress","addressType","iotCard","network","screenInfo","fixedMode","model","osArch","versionInfo",
					"status","props.disk_capacity","props.last_active_time","createTime","remark"})
	@ExcelColumn(columnName="physicalAddress",name="物理地址")
	@ExcelColumn(columnName="addressType",name="地址类型")
	@ExcelColumn(columnName="iotCard",name="物联网卡编号")
	@ExcelColumn(columnName="status",name="设备状态",readConverterExp="ENABLE=正常,DISABLE=禁用,MAINTAIN=维护,PENDING=挂起(待审核)")
	@ExcelColumn(columnName="fixedMode",name="安装方式",readConverterExp="HANG=悬挂,FLOOR=落地")
	@ExcelColumn(columnName="name",name="设备名称")
	@ExcelColumn(columnName="groupId",name="设备组ID")
	@ExcelColumn(columnName="provinces",name="省/自治区/直辖市")
	@ExcelColumn(columnName="city",name="市")
	@ExcelColumn(columnName="top_group_name",name="设备组名称")
	@ExcelColumn(columnName="screenInfo",name="屏幕信息")
	@ExcelColumn(columnName="name",name="设备名称")
	@ExcelColumn(columnName="model",name="设备型号")
	@ExcelColumn(columnName="vendor",name="设备供应商")
	@ExcelColumn(columnName="osArch",name="操作系统平台")
	@ExcelColumn(columnName="network",name="网络连接类型")
	@ExcelColumn(columnName="versionInfo",name="版本")
	@ExcelColumn(columnName="planId",name="当前节目ID")
	@ExcelColumn(columnName="targetId",name="目标节目ID")
	@ExcelColumn(columnName="createTime",name="记录创建时间")
	@ExcelColumn(columnName="updateTime",name="记录修改时间")
	@ExcelColumn(columnName="remark",name="备注")
	@ExcelColumn(columnName="props.last_active_time",name="上次在线时间")
	@ExcelColumn(columnName="props.disk_capacity",name="磁盘容量")
	@ExcelColumn(columnName="props.status_comment",name="状态变更说明")
	@ExcelColumn(columnName="device_detail.device_name",name="产品名称")
	@ExcelColumn(columnName="device_detail.manufacturer",name="制造商")
	@ExcelColumn(columnName="device_detail.made_date",name="生产日期")
	/** 启用WhereHelper动态生成SQL语句 */
	@EnableWhereHelper(/* 设置调试输出 */debuglog=false)
	/** 定义WhereHelper生成SQL语句的表达式 */
	@OrderBy("id")
	@Equal("group_id")
	@Equal("status")
	@Equal("address_type")
	@Equal("network")
	@IfElse(test="!isEmpty(${provinces})",doStatement=" dgrp.provinces = '${provinces}'")
	@IfElse(test="!isEmpty(${city})",doStatement=" dgrp.city = '${city}'")
	@IfElse(test="!isEmpty(${name})",doStatement=" dc_device.name like '%${name}%'")
	@IfElse(test="!isEmpty(${physical_address})",doStatement=" physical_address like '%${physical_address}%'")
	@IfElse(test="!isEmpty(${iot_card})",doStatement=" iot_card like '%${iot_card}%'")
	@IfElse(test="!isEmpty(${screen_info})",doStatement=" screen_info like '%${screen_info}%'")
	@IfElse(test="!isEmpty(${os_arch})",doStatement=" os_arch like '%${os_arch}%'")
	@IfElse(test="!isEmpty(${version_info})",doStatement=" version_info like '%${version_info}%'")
	@IfElse(test="!isEmpty(${model})",doStatement=" model like '%${model}%'")
	@IfElse(test="!isEmpty(${vendor})",doStatement=" model like '%${vendor}%'")
	@IfElse(test="!isEmpty(${create_time_max})",doStatement=" dc_device.create_time <= '${create_time_max}'")
	@IfElse(test="!isEmpty(${create_time_min})",doStatement=" dc_device.create_time >= '${create_time_min}'")
	@IfElse(test="!isEmpty(${update_time_max})",doStatement=" dc_device.update_time <= '${update_time_max}'")
	@IfElse(test="!isEmpty(${update_time_min})",doStatement=" dc_device.update_time >= '${update_time_min}'")
	@Expression("dc_device.name !='$DUMMY_DEVICE_DONOT_DELETE$'")
	/** 
	 * 这里必须指定produces,否则swagger下载的excel文件不正确,
	 * 参见 <a href="https://blog.csdn.net/iuie_sl/article/details/114904170 ">《使用swagger api 下载excel,excel打不开》</a>
	 */
	@ApiOperation(value = "设备表导出EXCEL", notes = "设备表导出EXCEL",httpMethod="GET",produces="application/octet-stream,application/json")
    @RequestMapping(method=RequestMethod.GET,produces={"application/octet-stream","application/json"})
	public List<DeviceBean> exportDevices(Integer getParameter, String exportFileName, 
			String sheetName, 
			String title, String titleFontName, Integer titleFontHeight, IndexedColors titleFontColor, IndexedColors titleFillColor, HorizontalAlignment titleHorizontalAlign, 
			String headerFontName, Integer headerFontHeight, IndexedColors headerFontColor, IndexedColors headerFillColor, HorizontalAlignment headerHorizontalAlign, Boolean firstBold, 
			Integer fontHeight, String fontName, IndexedColors fontColor, IndexedColors fillColor, HorizontalAlignment horizontalAlign, 
			String integralFormat, String dateTimeFormat, String dateFormat, String timeFormat, String timestampFormat, 
			List<String> includeColumns, List<String> excludeColumns) {
		return instanceOf(IDeviceManager.class).loadByWhereAsList(PageHelper.getWhere());
	}

Map(JSON)类型记录指定导出配置

如下服务方法,从数据库读取的记录类型为sql2java生成的 DeviceBean,包含了@ExcelColumn,@ExcelSheet注解,但因为还需要为每条记录增加额外的数据字段,所以将DeviceBean对象转换成了JSONObject,服务方法服务方法返回的类型为JSONObject.

那么虽然原始的DeviceBean记录中是有@ExcelColumn,@ExcelSheet注解提供Excel导出配置的,但Excel 导出切面在执行过程中收到的数据记录类型为Map(JSONObject也是Map).所以这些@ExcelColumn,@ExcelSheet注解提供的Excel导出配置数据在数据类型转过程实际是被丢失了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    @RequestMapping(method=RequestMethod.GET,produces={MediaType.APPLICATION_OCTET_STREAM_VALUE,MediaType.APPLICATION_JSON_VALUE})
	public List<JSONObject> exportDevices() {
        /** 从数据库读取的记录类型为sql2java生成的 DeviceBean,包含了@ExcelColumn,@ExcelSheet注解 */
		List<DeviceBean> beans = dm.daoLoadDeviceByJoinWhere("JOIN dc_device_group AS dgrp ON group_id = dgrp.id",PageHelper.getWhere());
        /** 将DeviceBean对象转换成了JSONObject,并根据需要添加了一些新的字段,服务方法返回的类型为JSONObject*/
		List<JSONObject> maps = Lists.transform(beans,l->dm.formatAsJson(l, dm.deviceFormater, false));
		return maps;

	}

为了解决数据类型转换而导致的原数据类型中@ExcelColumn,@ExcelSheet提供的Excel导出配置参数的丢失,excelGenerator的Spring AOP(切面)实现提供了在服务方法指定原始数据类型的机制,以允许服务方法通知切面执行时原始的数据类型是什么,这样切面执行时就可以从原始的数据类型中获取原数据类型中@ExcelColumn,@ExcelSheet提供的Excel导出配置参数。

修改上面的exportDevices方法,增加调用 ExcelHelperAround.setBeanClass方法指定原始数据类型

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    @RequestMapping(method=RequestMethod.GET,produces={MediaType.APPLICATION_OCTET_STREAM_VALUE,MediaType.APPLICATION_JSON_VALUE})
	public List<JSONObject> exportDevices() {
		List<DeviceBean> beans = dm.daoLoadDeviceByJoinWhere("JOIN dc_device_group AS dgrp ON group_id = dgrp.id",PageHelper.getWhere());
		List<JSONObject> maps = Lists.transform(beans,l->dm.formatAsJson(l, dm.deviceFormater, false));
		/** 
		 * 服务方法的返回类型为JSONObject,无法将 DeviceBean 中@ExcelSheet,@ExcelColumn注解传递ExcelHelperAround,
		 * 所以这里通过调用 ExcelHelperAround#setBeanClass 方法告诉 ExcelHelperAround对象数据记录的原始的数据类型,
		 * 这样ExcelHelperAround就可以通过该类型获取 JSON 字段的Excel配置参数
		 */
		ExcelHelperAround.setBeanClass(DeviceBean.class);
		return maps;
	}

服务方法中不仅可以通过setBeanClass方法指定数据记录的原始类型,

ExcelHelperAround还提供了静态方法setSheetConfig(SheetConfig)定义全部的Excel导出配置参数,参见ExcelHelperAround#setSheetConfig(SheetConfig)的说明.

@ExcelColumn注解

gu.sql2java.excel.ExcelColumn 用于定义导出Excel数据列的配置注解。此注解可以定义在类,(服务)方法及类成员字段上。

定义在类上,代表定义类中成员的导出配置,可以定义多个。

定义在类成员(Field)上代表定义当前成员的导出配置,只能定义一个成员字段配置注解,可以定义多个子成员字段配置注解.

子成员命名要以.分割的各子成员字段名组成,比如成员字段名为props,那么其子成员字段last_active_time的名字定义为props.last_active_time

定义在类和服务方法上的@ExcelColumn注解必须指定columnName字段

定义在(服务)方法上的用法一般用于切面(aspect)执行方式。

注解类字段说明如下:

字段名

默认值

说明

sort

Integer.MAX_VALUE

导出时该字段在excel中的排序

columnName

对象(Java Bean/Map/JSON)中的字段名,当在Field上使用此注解时自动为Field name不需要填此字段

name

导出到Excel中的名字.为空则与{@link #columnName}相等

integralFormat

默认整数(Integer,Long,Short)格式

dateFormat

日期格式, 如: yyyy-MM-dd

readConverterExp

读取内容转表达式 (如: 0=男,1=女,2=未知,=错误值), ''为匹配其他未定义值的表达式

separator

.

分隔符,读取字符串组内容

scale

BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化)【暂未支持】

roundingMode

BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN【暂未支持】

height

导出时在excel中每个列的高度 单位为字符

width

导出时在excel中每个列的宽 单位为字符

suffix

文字后缀,如% 90 变成90%【暂未支持】

defaultValue

当值为空时,字段的默认值【暂未支持】

prompt

提示信息【暂未支持】

combo

设置只能选择不能输入的列内容【暂未支持】

isExport

是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写【暂未支持】

targetAttr

另一个类中的属性名称,支持多级获取,以小数点隔开【暂未支持】

isStatistics

是否自动统计数据,在最后追加一行统计数据总和【暂未支持】

cellType

ColumnType.STRING

导出类型(0数字 1字符串)【暂未支持】

color

导出字体颜色,参见{@link org.apache.poi.ss.usermodel.IndexedColors}

fillColor

单元格填充颜色,参见{@link org.apache.poi.ss.usermodel.IndexedColors}

horizontalAlign

导出字段水平对齐方式,参见{@link org.apache.poi.ss.usermodel.HorizontalAlignment}

handler

ExcelHandlerAdapter.class

自定义数据处理器

args

{}

自定义数据处理器参数

readMethod

指定字段读取方法名

writeMethod

指定字段写入方法名

@ExcelSheet注解

字段方法名

默认值

说明

sheetName

exportedExcel

excel Sheet名字

fileNamePrefix

导出的文件名前缀

title

excel Sheet 标题

titleFontName

Calibri

标题字体名

titleFontHeight

16

标题字体高度

titleFontColor

BLACK

标题字体颜色,参见 org.apache.poi.ss.usermodel.IndexedColors

titleFillColor

WHITE

标题单元背景填充颜色,参见 org.apache.poi.ss.usermodel.IndexedColors

titleHorizontalAlign

CENTER

标题对齐水平方式

titleHorizontalAlign

CENTER

标题对齐水平方式

headerFontName

Calibri

首行(字段名)字体名

headerFontHeight

16

首行(字段名)字体高度

headerFontColor

BLACK

首行(字段名)字体颜色,参见 org.apache.poi.ss.usermodel.IndexedColors

headerFillColor

GREY_25_PERCENT

首行(字段名)单元背景填充颜色,参见 org.apache.poi.ss.usermodel.IndexedColors

headerHorizontalAlign

CENTER

首行(字段名)水平对齐方式

firstBold

true

字体设置:标题行字体加粗

fontHeight

16

默认字体高度

fontName

Calibri

默认字体名

fontColor

BLACK

默认字体颜色,参见org.apache.poi.ss.usermodel.IndexedColors

fillColor

WHITE

默认单元背景填充颜色,参见org.apache.poi.ss.usermodel.IndexedColors

horizontalAlign

CENTER

默认导出字段水平对齐方式

integralFormat

0

默认整数(Integer,Long,Short)格式

dateTimeFormat

yyyy-MM-dd HH:mm:ss

(java.util.Date)日期时间格式

dateFormat

yyyy-MM-dd

(java.sql.Date)日期格式

timeFormat

HH:mm:ss

(java.sql.Time)时间格式

timestampFormat

yyyy-MM-dd’T’HH:mm:ss.SSSZ

(java.sql.Timestamp)时间戳格式

scale

-1

BigDecimal 精度【暂未支持】

roundingMode

6

BigDecimal 舍入规则【暂未支持】

maxHeight

0

导出时在excel中每个列的最大高度, 单位为字符

maxWidth

32

导出时在excel中每个列的最大宽度, 单位为字符

defaultValue

当值为空时,字段的默认值

includeColumns

{}

字段输出白名单,在此名单中的字段会被输出,同时指定白名单和黑名单时以白名单为准

excludeColumns

{}

字段输出黑名单,在此名单中的字段不会被输出,同时指定白名单和黑名单时以白名单为准

hideColumns

{}

隐藏字段名单,指定任何情况下都不输出的字段列表,在此名单中的字段,不论includeColumns(),excludeColumns()如何设置都不会被输出

defaultIncludeColumns

{}

默认的字段输出白名单,此字段用于给前端提供默认的输出字段及顺序

getParameterArgName

getParameter

Spring Controller 服务方法中获取excel导出参数的开关参数名

exportFileNameArgName

exportFileName

Spring Controller 服务方法中定义导出文件名的参数名

注解位置优先级及配置合并

服务方法(Method)>类(Class)>类成员(Field),这是当在不同位置定义了个多个同名注解时,同名参数优先使用的顺序。

对于不同名的参数则遵循合并。

如一个car字段在类成员(Field)上定义了@ExcelColumn(name="汽车")注解,在该成员所在的类上也定义了注解@ExcelColumn(columnName="car",name="车型",color="RED")注解,在服务方法同样定义了@ExcelColumn(columnName="car",name="车型及车牌",fillColor="BLUE")注解。

那么最终该字段的定义为:

name=“车型及车牌”,color=“RED”,fillColor=“BLUE”。

三个位置都定义的name字段,遵循服务方法(Method)>类(Class)>字段(Field)优先顺序覆盖低优先级的值。

color,filleColor字段在三个位置定义的注解中并不冲突,所以遵循合并原则

关于sql2java-excel的入门使用说明参见上一篇博客:

《sql2java-excel(一):基于apache poi实现数据库表的导出及支持spring web》

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-08-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【完结】深度学习CV算法工程师从入门到初级面试有多远,大概是25篇文章的距离
一直有同学希望我在公众号写写面试相关的东西,一直没写。我们不会开相关的板块,因为没有标准,容易引起争议,而且可能会加重大家的浮躁和焦虑。
机器视觉CV
2019/07/15
1.5K0
【完结】深度学习CV算法工程师从入门到初级面试有多远,大概是25篇文章的距离
【AI初识境】近20年深度学习在图像领域的重要进展节点
这是专栏《AI初识境》的第3篇文章。所谓初识,就是对相关技术有基本了解,掌握了基本的使用方法。
机器视觉CV
2019/10/11
9310
【AI初识境】近20年深度学习在图像领域的重要进展节点
深度学习理论篇之----前世、今生、未来
2017政府工作报告,指出要加快培育壮大包括人工智能在内的新兴产业,“人工智能”也首次被写入了全国政府工作报告。百度李彦宏,腾讯马化腾都在两会上就人工智能发表意见。科大讯飞刘庆峰在朋友圈分享了讯飞听见支持两会直播的消息。结合上月科技部新闻,“科技创新2030—重大项目”或将新增“人工智能2.0”,人工智能在中国的政治、经济、学术领域都成为重中之重。这是中国 AI人最好的时代——2017年,中国人工智能迎来真正的新纪元。
用户5410712
2022/06/01
7480
深度学习理论篇之----前世、今生、未来
【图像分割应用】医学图像分割(三)——肿瘤分割
这是专栏《图像分割应用》的第3篇文章,本专栏主要介绍图像分割在各个领域的应用、难点、技术要求等常见问题。
用户1508658
2019/07/23
3.2K0
基于深度学习方法的图像分割,差距不止一点点
图像分割(image segmentation)技术是计算机视觉领域的一个重要的研究方向,图像分割是计算机视觉中的一个关键过程。它包括将视觉输入分割成片段以简化图像分析。片段表示目标或目标的一部分,并由像素集或“超像素”组成。图像分割将像素组织成更大的部分,消除了将单个像素作为观察单位的需要。图像分析有三个层次: 分类 - 将整幅图片分成“人”、“动物”、“户外”等类别 目标检测 - 检测图像中的目标并在其周围画一个矩形,例如一个人或一只羊。 分割 - 识别图像的部分,并理解它们属于什么对象。分割是进行目标
机器学习AI算法工程
2022/09/22
9050
基于深度学习方法的图像分割,差距不止一点点
【计算机视觉】一、计算机视觉概述
  计算机视觉是人工智能的重要组成部分,是赋予机器自然视觉能力的学科,相当于是人工智能的大门。
Qomolangma
2024/07/30
4280
【计算机视觉】一、计算机视觉概述
干货 | 一文理清深度学习
Generative(生成模型)和discriminative(辨别模型)是两类实现算法。
CloudBest
2021/04/20
4920
干货 | 一文理清深度学习
【图像分割应用】医学图像分割(二)——心脏分割
这是专栏《图像分割应用》的第2篇文章,本专栏主要介绍图像分割在各个领域的应用、难点、技术要求等常见问题。
用户1508658
2019/07/23
3.6K0
首场ACRV机器人视觉挑战,全卷积神经网络实现交互式医学图像分割 | AI一周学术
呜啦啦啦啦啦啦啦大家好,本周的AI Scholar Weekly栏目又和大家见面啦!
大数据文摘
2019/04/26
4850
首场ACRV机器人视觉挑战,全卷积神经网络实现交互式医学图像分割 | AI一周学术
【AIDL专栏】梅涛:深度视觉理解(附PPT)
“人工智能前沿讲习班”(AIDL)由中国人工智能学会主办,旨在短时间内集中学习某一领域的基础理论、最新进展和落地方向,并促进产、学、研相关从业人员的相互交流。对于硕士、博士、青年教师、企事业单位相关从业者,预期转行AI领域的爱好者均具有重要的意义。
马上科普尚尚
2020/05/14
7910
【AIDL专栏】梅涛:深度视觉理解(附PPT)
【AIDL专栏】罗杰波: Computer Vision ++: The Next Step Towards Big AI
“人工智能前沿讲习班”(AIDL)由中国人工智能学会主办,旨在短时间内集中学习某一领域的基础理论、最新进展和落地方向,并促进产、学、研相关从业人员的相互交流。对于硕士、博士、青年教师、企事业单位相关从业者,预期转行AI领域的爱好者均具有重要的意义。2018年AIDL活动正在筹备,敬请关注公众号获取最新消息。
马上科普尚尚
2020/05/14
5270
【AIDL专栏】罗杰波: Computer Vision ++: The Next Step Towards Big AI
想学图像分割,强烈建议从这5篇图像分割算法综述
在过去的一年中,计算机视觉领域出现了许多优秀的工作,并推动了相关领域的技术发展与进步。去年上半年,极市曾盘点过计算机视觉领域综述论文,并进行了分类整理,得到了很多读者的支持。因此,在2021年初,我们对2020年出现的全部计算机视觉综述论文进行了分方向梳理,希望能帮助大家学习进步。
AI算法与图像处理
2021/02/05
3.4K0
想学图像分割,强烈建议从这5篇图像分割算法综述
深度学习中的图像分割:方法和应用
基于人工智能和深度学习方法的现代计算机视觉技术在过去10年里取得了显著进展。如今,它被用于图像分类、人脸识别、图像中物体的识别、视频分析和分类以及机器人和自动驾驶车辆的图像处理等应用上。
OpenCV学堂
2020/12/08
3.4K0
深度学习中的图像分割:方法和应用
机器视觉与计算机视觉的区别?
计算机视觉与机器视觉,首先是应用场景不一样,就像@Vinjn张静 回答的那样:你把摄像头对着人就是CV,对着车间就是MV。 计算机视觉和机器视觉应用场景不同,就像拉货车和载客车是的,侧重点不同而已,一个侧重人工智能分支,一个侧重工业应用!简单说起来的话,计算机视觉偏重于深度学习并且偏向软件,机器视觉偏重于特征识别同时对硬件方面要求也比较高,不过随着对智能识别要求越来越高的发展,这两个方向毕竟会互相渗透互相融合,区别也仅仅限于应用领域不同而已。 其次,我感觉最大的区别,在于技术要求的侧重点不一样,甚至差别很
智能算法
2018/04/03
3.2K0
机器视觉与计算机视觉的区别?
从学术研究到应用落地,这 6 位计算机视觉大咖在 CV 专场上都讲了什么? | CCF-GAIR 2018
AI 研习社按:2018 全球人工智能与机器人峰会(CCF-GAIR)在深圳召开,峰会由中国计算机学会(CCF)主办,雷锋网、香港中文大学(深圳)承办,得到了宝安区政府的大力指导,是国内人工智能和机器人学术界、工业界及投资界三大领域的顶级交流盛会,旨在打造国内人工智能领域最具实力的跨界交流合作平台。
AI研习社
2018/07/26
1.1K0
从学术研究到应用落地,这 6 位计算机视觉大咖在 CV 专场上都讲了什么? | CCF-GAIR 2018
图像分割综述
这一大部分我们将要介绍的是深度学习大火之前人们利用数字图像处理、拓扑学、数学等方面的只是来进行图像分割的方法。当然现在随着算力的增加以及深度学习的不断发展,一些传统的分割方法在效果上已经不能与基于深度学习的分割方法相比较了,但是有些天才的思想还是非常值得我们去学习的。 1.基于阈值的分割方法 阈值法的基本思想是基于图像的灰度特征来计算一个或多个灰度阈值,并将图像中每个像素的灰度值与阈值作比较,最后将像素根据比较结果分到合适的类别中。因此,该方法最为关键的一步就是按照某个准则函数来求解最佳灰度阈值。 阈值法特别适用于目标和背景占据不同灰度级范围的图。 图像若只有目标和背景两大类,那么只需要选取一个阈值进行分割,此方法成为单阈值分割;但是如果图像中有多个目标需要提取,单一阈值的分割就会出现作物,在这种情况下就需要选取多个阈值将每个目标分隔开,这种分割方法相应的成为多阈值分割。
用户1150922
2019/07/10
2.1K0
百度计算机视觉首席科学家王井东:在视觉的竞技场,研究与落地没有明显的界限
在王井东看来,百度搜索引擎、自动驾驶、智能云、小度等等不同的业务线中,有共同的视觉研究难题。
AI掘金志
2022/11/08
5030
百度计算机视觉首席科学家王井东:在视觉的竞技场,研究与落地没有明显的界限
微软沈向洋:计算机视觉未来在语义层 “两大一精”是关键
为了更好地引导和推动我国人工智能领域的发展,由中国人工智能学会发起主办,CSDN承办的2015中国人工智能大会(CCAI 2015)于7月26-27日在北京友谊宾馆召开。本次会议的主旨是创办国内人工智
用户1737318
2018/06/05
6790
旷视首席科学家孙剑:深度学习变革视觉计算丨CCF-GAIR 2019
7月12日-7月14日,2019第四届全球人工智能与机器人峰会(CCF-GAIR 2019)于深圳正式召开。
AI掘金志
2019/08/29
6870
旷视首席科学家孙剑:深度学习变革视觉计算丨CCF-GAIR 2019
哀悼!旷视首席科学家孙剑突然离世,终年45岁,曾任微软亚洲研究院首席研究员
点击图片立刻参与! 孙剑博士,一路走好。 作者 | 镁客星球编辑部 今天凌晨,巨星陨落。 6月14日,“AI四小龙”之一的旷视科技发布讣告,旷视首席科学家、旷视研究院院长孙剑博士因突发疾病抢救无效于2022年6月14日凌晨去世。 旷视科技在讣告中表示: 我们万分难过,旷视首席科学家、旷视研究院院长孙剑博士因突发疾病抢救无效,于2022年6月14日凌晨,永远离开了我们。 孙剑博士一生专注于科研工作。他的不幸离世,让旷视失去了一位在人工智能技术领域探索和创新的领路人。每一位和他共事过的旷视同学,失去了一位智
镁客网
2022/06/16
4880
哀悼!旷视首席科学家孙剑突然离世,终年45岁,曾任微软亚洲研究院首席研究员
推荐阅读
相关推荐
【完结】深度学习CV算法工程师从入门到初级面试有多远,大概是25篇文章的距离
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档