一、特殊名词介绍

二、常用接口说明

接口主要场景备注
com.fr.report.fun.ExportOperateProvider新文件类型导出接口
com.fr.report.fun.ExportExtensionProcessor导出操作统一处理接口易冲突!禁止新商城插件使用,仅用于单个客户条件允许下的定制
com.fr.report.fun.FormatActionProvider不预览(format)导出处理接口易冲突!禁止新商城插件使用,仅用于单个客户条件允许下的定制
com.fr.form.stable.FormExportProcessor决策报表导出接口
com.fr.report.fun.ExcelExportAppProviderexcel细分导出类型接口
com.fr.io.exporter.PDFExporterCreatorPDF细分导出类型接口易冲突!禁止新商城插件使用,仅用于单个客户条件允许下的定制
com.fr.stable.fun.ExcelExportCellValueProvider导出excel单元格值处理接口
com.fr.report.fun.CommentExcelProcessor导出excel对每个sheet的处理接口易冲突!禁止新商城插件使用,仅用于单个客户条件允许下的定制
com.fr.design.fun.ToolbarItemProvider报表工具栏按钮扩展接口配合实现新导出类型
com.fr.report.fun.ExtensionButtonProvider报表工具栏导出菜单按钮扩展接口配合实现新导出类型
com.fr.stable.fun.LocaleFinder国际化配合实现新导出类型
三组常见引入JS和CSS的插件接口对比引入JS/CSS资源配合实现新导出类型
三组开放web服务接口的插件接口对比提供web服务配合实现新导出类型

帆软报表中涉及导出的接口比较多,目前已知被插件中使用的导出功能接口主要有8个(上表中的前8个)。接口开放年代都比较久远,存在相互耦合和包含的关系,所以使用时开发者需要先理清楚这些接口的关系,并根据实际需要解决的需求场景和环境合理的选择适合的接口。否则极易引起冲突,导致后期维护风险极高。

帆软报表中导出根据触发条件一共分为以下几种场景:

1.预览导出按钮导出:这部分链路稍微有点长,将采用删减代码注释对各接口的调用逻辑进行阐述【为了方便理解,代码中只保留了跟导出接口直接相关的部分代码,接口的注意点也直接通过注释体现在代码中】。

package com.fr.web.core.reserve;

...

public class ExportService extends NoOPService {

    ...
  
    public String actionOP() {
        return "export";
    }

    /**
     * 执行  hugh:这里就是导出的入口点
     *
     * @param req       http请求
     * @param res       http应答
     * @param sessionID 会话ID
     * @throws Exception
     */
    public void process(HttpServletRequest req, HttpServletResponse res, String sessionID) throws Exception {
        ...
        dealWithExport(req, res, sessionID, false);
    }

    /**
     * 进行导出
     *
     * @param req       http请求
     * @param res       http 应答
     * @param sessionID 会话ID
     * @param isEmbbed  嵌入
     * @throws Exception
     */
    public static void dealWithExport(HttpServletRequest req,
                                      HttpServletResponse res, String sessionID, boolean isEmbbed) throws Exception {
        ...

        ExportExtensionProcessor processor = getExportExtensionProcessor();
		//hugh: ExportExtensionProcessor接口的fileName 方法执行
        String fileName = processor.fileName(req, sessionIDInfor);

        String format = WebUtils.getHTTPRequestParameter(req, "format");
		//hugh: ExportExtensionProcessor接口的createCollection 方法执行
        ExportCollection exportCollection = processor.createCollection(req, res, sessionIDInfor, format, fileName, isEmbbed);

        exportCollection.doExport(req, res, sessionIDInfor, format);

        ...
    }

    private static ExportExtensionProcessor getExportExtensionProcessor() {
		//hugh:读取插件中的导出接口,如果读取不到就采用产品内置的导出接口DefaultExportExtension
		//ExportExtensionProcessor本身是独占的接口,也就是最终不论有多少插件实现了这个接口,最终都只会生效一个。
		//同时插件实现的生效后产品本身的DefaultExportExtension就不再返回。所以开发者在使用这个接口时,最好是直接继承DefaultExportExtension实现
		//然后把后续的接口兼容进去。否则这里是一个高冲突的风险点。
        ExportExtensionProcessor processor = ExtraReportClassManager.getInstance().getSingle(ExportExtensionProcessor.MARK_TAG);
        if (processor == null) {
            processor = new DefaultExportExtension();
        }
        return processor;
    }

    ...

}


package com.fr.web.core.reserve;

...

public class ExportService extends NoOPService {

    ...
  
    public String actionOP() {
        return "export";
    }

    /**
     * 执行  hugh:这里就是导出的入口点
     *
     * @param req       http请求
     * @param res       http应答
     * @param sessionID 会话ID
     * @throws Exception
     */
    public void process(HttpServletRequest req, HttpServletResponse res, String sessionID) throws Exception {
        ...
        dealWithExport(req, res, sessionID, false);
    }

    /**
     * 进行导出
     *
     * @param req       http请求
     * @param res       http 应答
     * @param sessionID 会话ID
     * @param isEmbbed  嵌入
     * @throws Exception
     */
    public static void dealWithExport(HttpServletRequest req,
                                      HttpServletResponse res, String sessionID, boolean isEmbbed) throws Exception {
        ...

        ExportExtensionProcessor processor = getExportExtensionProcessor();

        String fileName = processor.fileName(req, sessionIDInfor);

        String format = WebUtils.getHTTPRequestParameter(req, "format");
		
        ExportCollection exportCollection = processor.createCollection(req, res, sessionIDInfor, format, fileName, isEmbbed);

        exportCollection.doExport(req, res, sessionIDInfor, format);

        ...
    }

    private static ExportExtensionProcessor getExportExtensionProcessor() {
		//hugh:读取插件中的导出接口,如果读取不到就采用产品内置的导出接口DefaultExportExtension
		//ExportExtensionProcessor本身是独占的接口,也就是最终不论有多少插件实现了这个接口,最终都只会生效一个。
		//同时插件实现的生效后产品本身的DefaultExportExtension就不再返回。所以开发者在使用这个接口时,最好是直接继承DefaultExportExtension实现
		//然后把后续的接口兼容进去。否则这里是一个高冲突的风险点。
        ExportExtensionProcessor processor = ExtraReportClassManager.getInstance().getSingle(ExportExtensionProcessor.MARK_TAG);
        if (processor == null) {
            processor = new DefaultExportExtension();
        }
        return processor;
    }

    ...

}

2.web端不预览导出

3.定时调度/预览邮件附件导出

4.设计器导出:此处导出类全部由DesignReportExportType类加载和初始化,通过IDEA自带的反编译开发者可以了解到设计器导出导出PDF会执行PDFExporterCreator接口,导出excel会执行CommentExcelProcessorExcelExportCellValueProvider接口其他接口不会执行。

5.后台自定义的其他导出


三、开源案例

免责声明:所有文档中的开源示例,均为开发者自行开发并提供。仅用于参考和学习使用,开发者和官方均无义务对开源案例所涉及的所有成果进行教学和指导。若作为商用一切后果责任由使用者自行承担。