一、项目背景

企业日常工作中需要制作大量的报表,比如商品的销量、销售额、库存详情、员工打卡信息、保险报销、办公用品采购、差旅报销、项目进度等等,都需要制作统计图表以更直观地查阅。但是报表的制作往往需要耗费大量的时间,即使复用制作好的报表模版,一次次周期性对数据的复制粘贴操作也很耗人,同时模版在此过程中也会逐渐变得面目全非。

基于此,我们需要挖掘数据背后隐藏的关联信息,将人工的常规性操作抽离出来,使用工具和代码去实现,这个过程就称之为报表自动化。文内附前后端demo项目源码,家人们自行下载即可。

二、报表自动化的优势

报表自动化带来的价值有哪些呢?

1、节省时间,提高效率

身处信息爆炸的时代,任何一家企业都有体量庞大、结构复杂、各种各样的数据,多类数据交互融合,对其进行分析经常伴随着大量人力资源的消耗。自动化报表通过合理的设计,独立出各个业务功能模块,后续重复引用该模块,实现重复操作的代码复用。

对于固定流程或逻辑的一些操作,计算机的执行速度是人力不可及的,为我们节省了时间,可以投入更有意义的工作。

2、降低出错率

人工操作总是受太多不可控因素影响,存在各种出错的潜在可能。与之相比,自动化意味着通过编码手段实现了持久化的逻辑、流程,经过重复的测试验证之后,便可完全信任该程序。在重复性的工作场景下机器产出的稳定性远高于人工操作。

3、时效性高

日报、周报、月报这种周期性的报表,人为操作很难控制时间的准确性,但是通过代码控制可以最大程度的保证其定点触发操作。

三、系统功能点

  1. 任务配置灵活:支持根据业务需求,通过界面操作控制任务的启停状态,任务对应生成的文件类型等。
  2. 报表模版设计自由:业务人员可根据对应任务配置的预览数据自定义报表模版,然后将其保存生效。
  3. 前端预览:支持从前端预览报表详情。
  4. 定时发送:定时生成报表文件并将其同步到微信群。
  5. 支持多种类型:支持Excel、PDF、图表等文件类型的报表格式。
  6. 数据自动抽取:动态读取数据库中的数据生成报表。
  7. 模版和数据独立存储:使用在线表格设计器编辑模版,存储时只保存模版,数据从数据库加载。

四、方案设计

1、整体流程

报表自动化的起点是能对接数据源,期间能自动化的生成事先设计好格式的报表,最终通过企业微信自动推送消息。具体流程可以分为3个步骤:

1.报表模版设计

2.对接数据源:从数据库中读取数据,动态适配数据模版。

3.自动化过程实现:利用定时任务,定时捞取数据,借助GcExcel生成对应类型的文件,通过对接企业微信的API,将文件同步到微信群。

2、技术栈

前端:react+spreadJs

Demo地址:https://gcdn.grapecity.com.cn/forum.php?mod=attachment&aid=MjM4NzkxfDFhNjNlYjc2fDE2NjYyNDUxMjZ8NjI2NzZ8OTk3MTg%3D

后端:Java+GcExcel+mysql+Quartz

Demo地址:

https://gcdn.grapecity.com.cn/forum.php?mod=attachment&aid=MjM4NzkyfGExOWYxNmQzfDE2NjYyNDUxMjZ8NjI2NzZ8OTk3MTg%3D

3、具体实现

3.1 数据库表设计

note:此为测试demo,故没有创建主键索引之外的索引。

3.1.1 任务配置列表 task_config

依赖Quartz组件实现定时任务。读取任务配置表中启动状态的任务配置,按照任务类型读取对应数据源的增量更新数据。

字段名称 字段类型 字段含义 备注
id long ID 自增主键
task_id varchar 任务ID
task_type varchar 任务类型 和任务内容对应,比如订单/物流单信息
task_name varchar 任务名称
task_status int 任务状态 1:启动 0:终止 只有启动状态的配置才能生成任务信息
task_context text 任务内容 模版信息,按照模版绑定数据源
save_type char 保存类型 保存文件的类型 excel/PDF
time_span int 执行任务的时间间隔 单位s。
operator char 操作者 便于追溯
create_time dateTime 创建时间
update_time dateTime 更新时间
SQL
CREATE TABLE `task_config` (
`id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`task_id` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '任务ID',
`task_type` VARCHAR(64) DEFAULT NULL DEFAULT '' COMMENT '任务类型',
`task_name` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '任务名称',
`task_status` TINYINT(4) NOT NULL DEFAULT 0 COMMENT '任务状态:1:启动 0:终止',
`task_context` text COMMENT '任务模版信息',
`time_span` TINYINT(4) NOT NULL DEFAULT 0 COMMENT '时间间隔,单位s',
`operator` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '操作人',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=INNODB DEFAULT CHARSET=UTF8MB4 COMMENT='任务配置表';

3.1.2 订单信息表 order

订单相关数据源信息,对应订单类的报表任务。

字段名称 字段类型 字段含义 备注
ID long 自增ID
order_id char 订单号
order_amount decimal 订单金额
order_discount decimal 订单优惠金额
shipping_fee decimal 订单运费
receiver_name varchar 下单人姓名
receiver_state varchar
receiver_city varchar
receiver_district varchar
receiver_address varchar 详细地址
create_time dateTime 创建时间
update_time dateTime 更新时间
SQL
CREATE TABLE `order` (
`id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`order_id` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '订单ID',
`order_amount` DECIMAL DEFAULT NULL DEFAULT 0 COMMENT '订单金额',
`order_discount` DECIMAL DEFAULT NULL DEFAULT 0 COMMENT '订单折扣金额',
`shipping_fee` DECIMAL DEFAULT NULL DEFAULT 0 COMMENT '运费',
`receiver_name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '收件人姓名',
`receiver_state` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '省',
`receiver_city` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '市',
`receiver_district` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '区',
`receiver_address` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '详细地址',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=INNODB DEFAULT CHARSET=UTF8MB4 COMMENT='订单表';

3.1.3 发货单表 waybill

发货单相关数据源信息,对应发货单类的报表任务。

字段名称 字段类型 字段含义 备注
ID long 自增ID
order_id char 订单号
waybill_id char 物流单号ID
express_sn varchar 物流编号
express_name varchar 物流公司
receiver_name varchar 下单人姓名
receiver_state varchar
receiver_city varchar
receiver_district varchar
receiver_address varchar 详细地址
out_time dateTime 发货时间
create_time dateTime 创建时间
update_time dateTime 更新时间
SQL
CREATE TABLE `waybill` (
`id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`order_id` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '订单ID',
`waybill_id` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '发货单ID',
`express_sn` VARCHAR(128) DEFAULT NULL DEFAULT '' COMMENT '物流单号',
`express_name` VARCHAR(64) DEFAULT NULL DEFAULT '' COMMENT '物流公司名称',
`receiver_name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '收件人姓名',
`receiver_state` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '省',
`receiver_city` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '市',
`receiver_district` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '区',
`receiver_address` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '详细地址',
`out_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '发货时间',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=INNODB DEFAULT CHARSET=UTF8MB4 COMMENT='发货单表';
3.2 功能拆解

1、前端实现

前端使用React框架,嵌入了SpreadJS组件,初始化时从后端读取任务配置列表数据并展示。可从前端配置任务规则,主要是配置模版信息。

2、后端

后端是一个SpringBoot项目,嵌入GcExcel组件对编辑、导出等操作,同时借助Quartz定时任务调度框架实现定时任务的管理,并接入了企业微信暴露的群机器人消息对接API,发送消息到企业微信群。

定时任务调度框架quartz

Quartz的三个核心概念:调度器、任务、触发器,三者之间的关系是:

一个作业,比较重要的三个要素就是Scheduler,JobDetail,Trigger;而Trigger对于Job而言就好比一个驱动器,没有触发器来定时驱动作业,作业就无法运行;对于Job而言,一个Job可以对应多个Trigger,但对于Trigger而言,一个Trigger只能对应一个Job,所以一个Trigger只能被指派给一个Job;如果你需要一个更复杂的触发计划,可以创建多个Trigger并指派它们给同一个Job。

调度器的主要API

Java
//绑定jobDetail与trigger
scheduler.scheduleJob(jobDetail, trigger);
//检查JobDetail是否存在
scheduler.checkExists(JobKey.jobKey(name, group))
//检查Trigger是否存在
scheduler.checkExists(TriggerKey.triggerKey(name, group))
//删除jobDetail
scheduler.deleteJob(JobKey.jobKey(name, group))
//立即执行一次指定的任务
scheduler.triggerJob(JobKey.jobKey(name, group), dataMap)
//启动任务调度
scheduler.start();
//暂停指定的job
scheduler.pauseJob(jobKey);
//任务调度挂起,即暂停操作
scheduler.standby();
//关闭任务调度,同shutdown(false)
scheduler.shutdown();
//表示等待所有正在执行的Job执行完毕之后,再关闭Scheduler
scheduler.shutdown(true);
// 表示直接关闭Scheduler
scheduler.shutdown(false);

定时任务出发规则:

1、使用cron表达式 定时发送

Java
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?")) // 日历
.build();

2、使用simpleTrigger触发器

为那种需要在特定的日期/时间启动,且以一个可能的间隔时间重复执行 n 次的 Job 所设计的。

JavaScript
//立即开始执行,2秒执行一次,重复3次,3秒后结束执行(当重复次数或者结束时间有一个先达到时,就会停止执行)
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "triggerGroup1")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).withRepeatCount(3))
.endAt(new Date(new Date().getTime() + 3000L))
.build();

五、效果演示

使用步骤说明:

整个页面布局可以分为两大部分,上半部分为从数据库中读取的任务配置列表,下半部分为SpreadJS的Designer模块。在前端配置任务规则后,后端服务会读取具体的任务配置信息,调度任务进行生产。整个操作可以分为以下几个步骤:

1)、读取任务配置数据到React表格中。

2)、选中特定的任务配置项,读取对应数据源的数据到Worksheet中展示。

3)、编辑报表任务模版并保存。

由于json文件是存储在mysql数据库表中的一个字段中,若字段太大会导致溢出且影响性能,故仅保存样式,后端进行数据源动态查询去适配生成报表。

一个完整的json对象示例如下所示:

JSON
{"version":"15.0.2","sheetCount":1,"customList":[],"sheets":{"order":{"name":"order","isSelected":true,"rowCount":20,"columnCount":13,"frozenTrailingRowStickToEdge":true,"frozenTrailingColumnStickToEdge":true,"theme":"Office","data":{"defaultDataNode":{"style":{"themeFont":"Body"}}},"rowHeaderData":{"defaultDataNode":{"style":{"themeFont":"Body"}}},"colHeaderData":{"defaultDataNode":{"style":{"themeFont":"Body"}}},"columns":[{"name":"receiverName","displayName":"姓名","size":60,"visible":true},{"name":"orderAmount","displayName":"订单金额","size":80,"visible":true},{"name":"orderDiscount","displayName":"订单折扣","size":60,"visible":true}],"leftCellIndex":0,"topCellIndex":0,"selections":{"activeSelectedRangeIndex":-1,"length":0},"autoGenerateColumns":false,"rowOutlines":{"items":[]},"columnOutlines":{"items":[]},"cellStates":{},"states":{},"outlineColumnOptions":{},"autoMergeRangeInfos":[],"charts":[{"name":"Chart 1","x":4,"y":4,"width":480,"height":300,"startRow":0,"startRowOffset":4,"startColumn":0,"startColumnOffset":4,"endRow":15,"endRowOffset":4,"endColumn":7,"endColumnOffset":36,"isSelected":true,"typeName":"2","chartSpace":{"typeName":"chartSpace","roundedCorners":false,"chart":{"title":{"tx":{"rich":{"p":[{"elements":[{"elementType":0,"t":"Amount","rPr":{"latin":{"typeface":"+mn-lt"},"sz":18.67,"b":false,"solidFill":{"schemeClr":{"val":1,"lumMod":[65000],"lumOff":[35000]}}}}],"pPr":{"defRPr":{"latin":{"typeface":"+mn-lt"},"sz":18.67,"b":false,"solidFill":{"schemeClr":{"val":1,"lumMod":[65000],"lumOff":[35000]}}}},"endParaRPr":{}}],"bodyPr":{},"lstStyle":{}}},"overlay":false,"spPr":{"noFill":true,"ln":{"noFill":true},"effectLst":{}}},"autoTitleDeleted":false,"plotArea":{"axes":[{"axisType":0,"axId":59604390,"delete":false,"majorTickMark":2,"minorTickMark":2,"tickLblPos":2,"title":null,"axPos":0,"scaling":{"orientation":1},"spPr":{"ln":{"solidFill":{"schemeClr":{"val":1,"lumMod":[15000],"lumOff":[85000]}}}},"numFmt":{"formatCode":"General"},"txPr":{"p":[{"elements":[{"elementType":0,"t":"","rPr":{"latin":{"typeface":"+mn-lt"},"sz":12,"b":false,"solidFill":{"schemeClr":{"val":1,"lumMod":[65000],"lumOff":[35000]}}}}],"pPr":{"defRPr":{"latin":{"typeface":"+mn-lt"},"sz":12,"b":false,"solidFill":{"schemeClr":{"val":1,"lumMod":[65000],"lumOff":[35000]}}}},"endParaRPr":{}}]},"auto":true,"lblOffset":0,"tickMarkSkip":1,"noMultiLvlLbl":true,"AxisGroup":0,"AxisType":0,"crosses":1,"crossAx":36407267},{"axisType":3,"axId":36407267,"delete":false,"majorTickMark":2,"minorTickMark":2,"tickLblPos":2,"title":null,"axPos":1,"scaling":{"orientation":1},"spPr":{"ln":{"solidFill":{"schemeClr":{"val":1,"lumMod":[15000],"lumOff":[85000]}}}},"numFmt":{"formatCode":"General"},"txPr":{"p":[{"elements":[{"elementType":0,"t":"","rPr":{"latin":{"typeface":"+mn-lt"},"sz":12,"b":false,"solidFill":{"schemeClr":{"val":1,"lumMod":[65000],"lumOff":[35000]}}}}],"pPr":{"defRPr":{"latin":{"typeface":"+mn-lt"},"sz":12,"b":false,"solidFill":{"schemeClr":{"val":1,"lumMod":[65000],"lumOff":[35000]}}}},"endParaRPr":{}}]},"majorGridlines":{"spPr":{"ln":{"solidFill":{"srgbClr":{"val":[217,217,217]}},"w":1},"effectLst":{}}},"AxisGroup":0,"AxisType":1,"crosses":1,"crossBetween":0,"crossAx":59604390}],"chartGroups":[{"chartType":6,"ser":[{"seriesType":0,"idx":0,"order":0,"cat":{"strRef":{"f":"order!$A$1:$A$2"}},"val":{"numRef":{"f":"order!$B$1:$B$2","numCache":{"formatCode":"General"}}},"shape":2,"invertIfNegative":false},{"seriesType":0,"idx":1,"order":1,"cat":{"strRef":{"f":"order!$A$1:$A$2"}},"val":{"numRef":{"f":"order!$C$1:$C$2","numCache":{"formatCode":"General"}}},"shape":2,"invertIfNegative":false}],"axId":[59604390,36407267],"barDir":1,"grouping":1,"gapWidth":150,"varyColors":false,"overlap":-27}],"spPr":{"noFill":true,"ln":{"noFill":true}}},"legend":{"legendPos":4,"spPr":{"noFill":true,"ln":{"noFill":true}},"txPr":{"p":[{"elements":[{"elementType":0,"t":"","rPr":{"latin":{"typeface":"+mn-lt"},"sz":12,"b":false,"solidFill":{"schemeClr":{"val":1,"lumMod":[65000],"lumOff":[35000]}}}}],"pPr":{"defRPr":{"latin":{"typeface":"+mn-lt"},"sz":12,"b":false,"solidFill":{"schemeClr":{"val":1,"lumMod":[65000],"lumOff":[35000]}}}},"endParaRPr":{}}]}},"plotVisOnly":true,"dispBlanksAs":1,"dispNaAsBlank":false},"spPr":{"solidFill":{"schemeClr":{"val":0}},"ln":{"solidFill":{"schemeClr":{"val":1,"lumMod":[15000],"lumOff":[85000]}},"w":1}},"txPr":{"p":[{"elements":[{"elementType":0,"t":"","rPr":{"latin":{"typeface":"+mn-lt"},"b":false,"solidFill":{"schemeClr":{"val":1,"lumMod":[65000],"lumOff":[35000]}}}}],"pPr":{"defRPr":{"latin":{"typeface":"+mn-lt"},"b":false,"solidFill":{"schemeClr":{"val":1,"lumMod":[65000],"lumOff":[35000]}}}},"endParaRPr":{}}]}},"useAnimation":false}],"preserveUnsupportedChartFlag":false,"printInfo":{"paperSize":{"width":850,"height":1100,"kind":1}},"shapeCollectionOption":{"snapMode":0},"index":0}},"sheetTabCount":0,"pivotCaches":{},"i0c":0}

更多纯前端表格在线demo示例 :https://demo.grapecity.com.cn/spreadjs/gc-sjs-samples/index.html

移动端示例(可扫码体验):http://demo.grapecity.com.cn/spreadjs/mobilesample/

React + Springboot + Quartz,从0实现Excel报表自动化的更多相关文章

  1. ASP.NET MVC 4.0 中使用NPOI 2.2.0 按模板生成Excel报表

    使用 NPOI 你就可以在没有安装 Office 或者相应环境的机器上对 WORD/EXCEL 文档进行读写.NPOI是构建在POI 3.x版本之上的,它可以在没有安装Office的情况下对Word/ ...

  2. 学习了quartz.net2.0的心得

    由于项目中要运用到此调度方法,所以抽空学习了下,简单的用法可以掌握了 首先作为没有用过的人来说,怎么用呢? 于是百度了下原来先要下载 quartz.net2.0, 然后我就去下了个quartz.net ...

  3. 从数据库中导出excel报表

    通常需要将后台数据库中的数据集或者是其他列表等导出excel 报表,这里主要引用了Apose.cells dll 类库, (1)直接上主要代码: protected void txtExport_Cl ...

  4. 在.NET中使用EPPlus生成Excel报表 .

    --摘抄自:http://blog.csdn.net/zhoufoxcn/article/details/14112473 在开发.NET应用中可能会遇到需要生成带图表(Chart)的Excel报表的 ...

  5. poi导出Excel报表多表头双层表头、合并单元格

    效果图: controller层方法: /**     *      * 导出Excel报表     * @param request     * @return     *      */    @ ...

  6. java导出excel报表

    1.java导出excel报表: package cn.jcenterhome.util; import java.io.OutputStream;import java.util.List;impo ...

  7. Excel报表

    Excel报表 1.Excel报表导入到GridView protected void Page_Load(object sender, EventArgs e) { string path = Se ...

  8. ASP.NET打印EXCEl报表技术总结

    序言:我们在做企业项目或者一些管理系统的时候往往会用到导出到excel报表这项功能,下面我介绍的是用windows自带的excel来打印 首先必须引入:Interop.Excel.dll.Intero ...

  9. 使用POI生成Excel报表

    先把报表模板截图贴上来 下面是POI编写的报表生成类ExcelReport.java package com.jadyer.report; import java.io.FileNotFoundExc ...

随机推荐

  1. vue的一些问题

    如有不正,请指正! 目录 1.组件化 2.注册组件 a.创建组件构造器 b.注册组件: c.使用组件 3.组件不能访问vue实例上的数据 4.全局组件与局部组件 5.v-model语法糖 组件通信 6 ...

  2. Quicker程序实用及获取

    -- 仅代表个人见解 --官方网站:https://getquicker.net/主界面截图   桌面图标截图   3分钟快速体验Quicker  https://getquicker.net/KC/ ...

  3. EPLAN部件库共享方法

    EPLAN部件库共享方法       关键词: EPLAN部件库共享 EPLAN 摘要:在使用EPLAN时经常会碰到自己电脑里的部件库和公司里其他同事的部件库存在差异,如果不是很平凡的同步所有使用的部 ...

  4. 羽夏看Linux内核——门相关入门知识

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.如有好的建议,欢迎反馈.码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作.如想转载,请把我的转载信息附在文章后面,并 ...

  5. Apache DolphinScheduler 需要的sudo,还可以这么玩,长见识了!

    Apache DolphinScheduler(incubator)需要的sudo,还可以这么玩,长见识了! 在新一代大数据任务调度 - Apache DolphinScheduler(以下简称dol ...

  6. Java中list集合自定义排序-2022新项目

    一.业务场景 为了加快首页数据查询的效率,因此将首页查询的数据大多数都放在了缓存中,包括各种list集合数据.对这些 从缓存中获取的数据做了一个兜底处理,如果从缓存中没有获取到数据,则直接从数据库中去 ...

  7. Linux—权限管理

    Linux 权限管理 1.权限简介 Linux权限是操作系统用来限制对资源访问的机制,权限一般分为读.写.执行.系统中每个文件都拥有特定的权限:属主.属组以及其他人,通过这样的机制来限制哪些用户或用户 ...

  8. C# using()的本质

    " 程序世界没有秘密,所有答案都在源码里 " 01.点明观点 C#中,非托管资源使用之后必须释放,而using()是使用非托管资源的最佳方式,可以确保资源在代码块结束之后被正确释放 ...

  9. KingbaseFlySync ddl变更流程

    关键字: KingbaseFlySync.Linux.x86_64.mips64el.aarch64.Java 一.ddl变更流程 1. 停掉客户业务,保证没有新数据产生 确认Oracle数据库上所有 ...

  10. LFS(Linux From Scratch)构建过程全记录(七):进入Chroot并构建临时工具

    写在前面 本章将完成临时系统构建的最后缺失部分和各种包构建所需的工具. 解决了所有循环依赖关系后,就可以使用与主机操作系统完全隔离的"chroot"环境进行构建. 注意:接下来的指 ...