文章目录
- 1. 技术选型
- 2. 实现方案
- 2.1. 方案介绍
- 2.2. 方案实现(demo)
- 2.2.1. 使用echarts绘制html静态页
- 2.2.1.1. 选择合适的图表
- 2.2.1.2. 下载html demo
- 2.2.2. 使用Dataway准备数据接口
- 2.2.2.1. 部署dataway
- 2.2.2.2. 创建数据接口
- 2.2.3. 调试html demo + dataway接口
- 2.2.4. 发布html到nginx
- 2.2.4.1. 将完整url,放入到系统菜单上(配置菜单),使其可访问
- 3. 后期工作
整体 目标:
无代码或低代码,可以实现数据的图形化显示。
1. 技术选型
- 图表:Apache ECharts。样例官网:https://echarts.apache.org/examples/zh/index.html
- 后端接口数据:Hasor Dataway。官网:https://www.dataql.net/docs/dataway/overview
2. 实现方案
2.1. 方案介绍
**流程整合方案:**Dataway数据接口 + ECharts绘制Html = 动态图表
-
工作流图:
-
数据流图:
2.2. 方案实现(demo)
我们以一个日历图为例,来看一个动态图表实现的全过程。
-
目标:在日历图上,显示有数据推送的日期,并根据推送的数据量多少,对日期色块以深浅进行区分。
-
效果:
2.2.1. 使用echarts绘制html静态页
根据需求,从Apache ECharts样例官网:https://echarts.apache.org/examples/zh/index.html,筛选符合需求的图表。
2.2.1.1. 选择合适的图表
日历坐标系可以满足。
2.2.1.2. 下载html demo
下载样例,下载到的是一个完整可运行的html demo
<!--此示例下载自 https://echarts.apache.org/examples/zh/editor.html?c=calendar-simple
-->
<!DOCTYPE html>
<html lang="zh-CN" style="height: 100%">
<head><meta charset="utf-8">
</head>
<body style="height: 100%; margin: 0"><div id="container" style="height: 100%"></div><script type="text/javascript" src="https://fastly.jsdelivr.net/npm/echarts@5.4.1/dist/echarts.min.js"></script><script type="text/javascript">var dom = document.getElementById('container');var myChart = echarts.init(dom, null, {renderer: 'canvas',useDirtyRect: false});var app = {};var option;function getVirtualData(year) {const date = +echarts.time.parse(year + '-01-01');const end = +echarts.time.parse(year + '-12-31');const dayTime = 3600 * 24 * 1000;const data = [];for (let time = date; time <= end; time += dayTime) {data.push([echarts.time.format(time, '{yyyy}-{MM}-{dd}', false),Math.floor(Math.random() * 10000)]);}return data;
}
option = {visualMap: {show: false,min: 0,max: 10000},calendar: {range: '2017'},series: {type: 'heatmap',coordinateSystem: 'calendar',data: getVirtualData('2017')}
};if (option && typeof option === 'object') {myChart.setOption(option);}window.addEventListener('resize', myChart.resize);</script>
</body>
</html>
至此,静态页面有了,我们需要的是一个动态数据接口,选择使用dataway开源项目来实现动态接口的创建。
dataway手册:https://www.dataql.net/docs/dataway/overview
需要的数据格式应该是:
[['2022-11-16',9],['2022-11-17',8],['2022-12-03',66],['2022-12-05',1014],['2022-12-06',2253],['2022-12-07',135]
]
2.2.2. 使用Dataway准备数据接口
2.2.2.1. 部署dataway
只需要在基础的springboot开箱代码基础上加上maven依赖及少量配置,即可run一个dataway服务。
-
dataway依赖表创建
CREATE TABLE `interface_release` (`pub_id` varchar(64) NOT NULL COMMENT 'Publish ID',`pub_api_id` varchar(64) NOT NULL COMMENT '所属API ID',`pub_method` varchar(12) NOT NULL COMMENT 'HttpMethod:GET、PUT、POST',`pub_path` varchar(512) NOT NULL COMMENT '拦截路径',`pub_status` varchar(4) NOT NULL COMMENT '状态:-1-删除, 0-草稿,1-发布,2-有变更,3-禁用',`pub_comment` varchar(255) NOT NULL COMMENT '注释',`pub_type` varchar(24) NOT NULL COMMENT '脚本类型:SQL、DataQL',`pub_script` mediumtext NOT NULL COMMENT '查询脚本:xxxxxxx',`pub_script_ori` mediumtext NOT NULL COMMENT '原始查询脚本,仅当类型为SQL时不同',`pub_schema` mediumtext NOT NULL COMMENT '接口的请求/响应数据结构',`pub_sample` mediumtext NOT NULL COMMENT '请求/响应/请求头样本数据',`pub_option` mediumtext NOT NULL COMMENT '扩展配置信息',`pub_release_time` varchar(32) NOT NULL COMMENT '发布时间(下线不更新)',PRIMARY KEY (`pub_id`),KEY `idx_interface_release_api` (`pub_api_id`),KEY `idx_interface_release_path` (`pub_path`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='Dataway API 发布历史。';CREATE TABLE `interface_release` (`pub_id` varchar(64) NOT NULL COMMENT 'Publish ID',`pub_api_id` varchar(64) NOT NULL COMMENT '所属API ID',`pub_method` varchar(12) NOT NULL COMMENT 'HttpMethod:GET、PUT、POST',`pub_path` varchar(512) NOT NULL COMMENT '拦截路径',`pub_status` varchar(4) NOT NULL COMMENT '状态:-1-删除, 0-草稿,1-发布,2-有变更,3-禁用',`pub_comment` varchar(255) NOT NULL COMMENT '注释',`pub_type` varchar(24) NOT NULL COMMENT '脚本类型:SQL、DataQL',`pub_script` mediumtext NOT NULL COMMENT '查询脚本:xxxxxxx',`pub_script_ori` mediumtext NOT NULL COMMENT '原始查询脚本,仅当类型为SQL时不同',`pub_schema` mediumtext NOT NULL COMMENT '接口的请求/响应数据结构',`pub_sample` mediumtext NOT NULL COMMENT '请求/响应/请求头样本数据',`pub_option` mediumtext NOT NULL COMMENT '扩展配置信息',`pub_release_time` varchar(32) NOT NULL COMMENT '发布时间(下线不更新)',PRIMARY KEY (`pub_id`),KEY `idx_interface_release_api` (`pub_api_id`),KEY `idx_interface_release_path` (`pub_path`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='Dataway API 发布历史。';
-
dataway springboot项目引入相关依赖
<!--hasor dataway--> <dependency><groupId>net.hasor</groupId><artifactId>hasor-spring</artifactId><version>4.2.2</version> </dependency> <dependency><groupId>net.hasor</groupId><artifactId>hasor-dataway</artifactId><version>4.2.2</version> </dependency><!--作为 Spring Boot 项目有着自己完善的数据库方面工具支持。我们这次采用 druid + mysql + spring-boot-starter-jdbc 的方式。--> <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.30</version> </dependency> <dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.21</version> </dependency> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.10</version> </dependency>
-
dataway springboot项目配置
# 解决接口返回中文都是乱码 server.servlet.encoding.charset=utf-8 server.servlet.encoding.force=true server.servlet.encoding.enabled=true# 是否启用 Dataway 功能(必选:默认false) HASOR_DATAQL_DATAWAY=true # 是否开启 Dataway 后台管理界面(必选:默认false) HASOR_DATAQL_DATAWAY_ADMIN=true # dataway API工作路径(可选,默认:/api/) HASOR_DATAQL_DATAWAY_API_URL=/api/ # dataway-ui 的工作路径(可选,默认:/interface-ui/) HASOR_DATAQL_DATAWAY_UI_URL=/interface-ui/ # SQL执行器方言设置(可选,建议设置) HASOR_DATAQL_FX_PAGE_DIALECT=mysql# db spring.datasource.url=jdbc:mysql://localhost:3306/cdc?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=true&serverTimezone=Asia/Shanghai spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.type=com.alibaba.druid.pool.DruidDataSource # druid spring.datasource.druid.initial-size=3 spring.datasource.druid.min-idle=3 spring.datasource.druid.max-active=10 spring.datasource.druid.max-wait=60000 spring.datasource.druid.stat-view-servlet.login-username=admin spring.datasource.druid.stat-view-servlet.login-password=admin spring.datasource.druid.filter.stat.log-slow-sql=true spring.datasource.druid.filter.stat.slow-sql-millis=1
-
dataway springboot把数据源设置到 Hasor 容器中
package com.example.simple;import net.hasor.core.ApiBinder; import net.hasor.core.DimModule; import net.hasor.db.JdbcModule; import net.hasor.db.Level; import net.hasor.spring.SpringModule; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component;import javax.sql.DataSource;@DimModule @Component public class ExampleModule implements SpringModule {@Autowiredprivate DataSource dataSource = null;@Overridepublic void loadModule(ApiBinder apiBinder) throws Throwable {// .DataSource form Spring boot into HasorapiBinder.installModule(new JdbcModule(Level.Full, this.dataSource));} }
-
在 dataway springboot 中启用 Hasor
package com.example.simple;import net.hasor.spring.boot.EnableHasor; import net.hasor.spring.boot.EnableHasorWeb; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;@EnableHasor() @EnableHasorWeb() @SpringBootApplication public class SimpleApplication {public static void main(String[] args) {System.setProperty("file.encoding","UTF-8");SpringApplication.run(SimpleApplication.class, args);}}
-
至此,即可启动应用
2.2.2.2. 创建数据接口
书接上回,我们需要的数据格式应该是:
[['2022-11-16',9],['2022-11-17',8],['2022-12-03',66],['2022-12-05',1014],['2022-12-06',2253],['2022-12-07',135]
]
-
sql编写
对此,我们如果通过简单的sql,按天汇总数据量,那sql应该这么写:
select DATE_FORMAT(add_time,'%Y-%m-%d') as time,sum(received_data_total) as sum from cdc.`cdc_receive_round_log` where add_time > '2022-10-01' and data_platform='zgl' group by DATE_FORMAT(add_time,'%Y-%m-%d') ;
在mysql中执行结果如下:
-
基于sql,在dataway中创建接口
-
验证接口是否可访问
在浏览器访问: http://localhost:8080/api/received_count_per_day2
可以拿到结果:
{"success":true,"message":"OK","code":0,"lifeCycleTime":128,"executionTime":32,"value":{"zgl":[{"time":"2022-11-16","sum":9},{"time":"2022-11-17","sum":8},{"time":"2022-12-03","sum":66},{"time":"2022-12-05","sum":1014},{"time":"2022-12-06","sum":2253},{"time":"2022-12-07","sum":135}],"hsh":[{"time":"2022-10-08","sum":582},{"time":"2022-10-11","sum":165},{"time":"2022-10-14","sum":400},{"time":"2022-10-26","sum":200},{"time":"2022-11-03","sum":427},{"time":"2022-11-11","sum":200},{"time":"2022-11-18","sum":1500},{"time":"2022-11-30","sum":700},{"time":"2022-12-01","sum":882746},{"time":"2022-12-02","sum":260753}]}}
上述如图:
but:
dataway接口返回的数据value.zgl内的报文,是一个list中嵌套dict。但是我们echarts需要的数据报文,是list里嵌套list。
我们要怎么将dataway的返参结果调整为echarts所需结构?
方案有二:
其一:通过dataway的dataql语法进行调整
其二:在echarts html中,拿到dataway接口数据报文后,通过js进行调整。
经过思考,毋庸置疑应选方案二,以减少学习成本。(下一节描述此案例如何调整。)
2.2.3. 调试html demo + dataway接口
直接看代码注释:
<!DOCTYPE html>
<html lang="zh-CN" style="height: 100%"><head><meta charset="utf-8">
</head><body style="height: 100%; margin: 0"><!-- 图表容器 --><div id="container" style="height: 100%"></div><!-- echarts js --><script type="text/javascript" src="https://fastly.jsdelivr.net/npm/echarts@5.4.1/dist/echarts.min.js"></script><!-- 由于需要异步加载数据,此处需要引入jquery --><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js"></script><script type="text/javascript">// 获取dom,以便使用echarts进行渲染var dom = document.getElementById('container');var myChart = echarts.init(dom, null, {renderer: 'canvas',useDirtyRect: false});// 异步加载数据,http://localhost:8080/api/received_count_per_day2 就是我们使用dataway动态创建的接口$.get('http://localhost:8080/api/received_count_per_day2').done(function (data) {// zgl日历数据处理,将list嵌套dict,改为list嵌套list结构。var zgl_origion = data.value.zglvar zgl_echarts_data = []for (var i = 0; i < zgl_origion.length; i++) {var item = []item.push(zgl_origion[i]['time'])item.push(zgl_origion[i]['sum'])zgl_echarts_data.push(item)}//hsh日历数据处理,将list嵌套dict,改为list嵌套list结构。var hsh_origion = data.value.hshvar hsh_echarts_data = []for (var i = 0; i < hsh_origion.length; i++) {var item = []item.push(hsh_origion[i]['time'])item.push(hsh_origion[i]['sum'])hsh_echarts_data.push(item)}// 渲染图表myChart.setOption({title: {text: '' // 图表的头部文本},tooltip: {// 在划过日期块的时候,显示日期和数据。不写的话,默认只会显示数值,没有日期。formatter: function (p) {var format = echarts.format.formatTime('yyyy-MM-dd', p.data[0]);return format + '数量: ' + p.data[1];}},visualMap: {show: false,min: 0,max: 5000,textStyle: { color: '#000' },precision: 0,inRange: {// 在线调色板 https://www.sojson.com/web/panel.html// 色块深浅,将从下面的列表中根据数值大小自动选择color: ['#D4EFDF', '#82E0AA', '#58D68D', '#27AE60', '#229954', '#196F3D'],colorAlpha: 0.9, // 图元的颜色的透明度}},calendar: [{range: '2022',dayLabel: {show: true}},{top: 260,range: '2022',// cellSize: ['auto', 20]}],series: [{type: 'heatmap',coordinateSystem: 'calendar',data: zgl_echarts_data // zgl数据渲染},{type: 'heatmap',coordinateSystem: 'calendar',calendarIndex: 1,data: hsh_echarts_data // hsh数据渲染}]})});window.addEventListener('resize', myChart.resize);</script>
</body></html>
然后,直接用浏览器访问此页面,或在vscode中的live server进行访问:
可以看到,dataway是已经允许了跨域访问的,无需再做调整。
2.2.4. 发布html到nginx
2.2.4.1. 将完整url,放入到系统菜单上(配置菜单),使其可访问
这里要做的,是echarts html页面路径跟我们将要嵌入的系统域名保持一致,echarts html页面路径可以放在一个特别标记的位置上,比如 https://hello.com/my_echarts/calender_summary.html, 用诸如/my_echarts
来让nginx区分是访问原系统页面或我们编写的echarts html页面即可。
同时,dataway接口已经支持跨域访问,不用调整不提。
3. 后期工作
-
是否支持类似时间区间筛选之类的表头?
理论上是支持的,因为echarts的图表,默认是放在一个html元素如div中,只需要进行简单的html编写
-
图表的空间布局摆放要求?
同上,记上css定位等html技术,即可实现静态页的调整。