【学习笔记】尚硅谷大数据项目之Flink实时数仓---数据可视化接口实现
这里写自定义目录标题
- 第 1 章 数据可视化接口
- 1.1 设计思路
- 1.2 需求梳理
- 1.2.1 最终显示效果图
- 1.2.2 分析可视化大屏
- 1.2.3 接口执行过程
- 第 2 章 Sugar 数据大屏
- 2.1 产品介绍
- 2.2 使用入口
- 2.3 创建数据大屏
- 第 3 章 总成交金额接口
- 3.1 Sugar 组件:数字翻牌器
- 3.1.1 添加组件
- 3.1.2 配置组件
- 3.1.3 查询组件需要的数据格式
- 3.1.4 接口访问路径以及返回格式
- 3.2 数据接口实现
- 3.2.1 创建数据接口模块
- 3.2.2 代码分层结构以及实现
- 3.2.3 测试本地接口
- 3.3 内网穿透
- 3.3.1 作用
- 3.3.2 工具
- 3.4 配置 Sugar 大屏
- 3.4.1 配置服务器全局 Host
- 3.4.2 大屏刷新数据
- 第 4 章 商品交易额不同维度的统计
- 4.1 三个关于商品交易额方面的统计
- 4.2 Sugar 组件:横向柱图、轮播饼图、轮播表格
- 4.2.1 添加组件
- 4.2.2 品牌排行的柱形图组件配置
- 4.2.3 品类分布的饼形图组件配置
- 4.2.4 商品排行的轮播表格组件配置
- 4.3 数据接口实现
- 4.3.1 创建商品交易额统计实体类 ProductStats
- 4.3.2 Mapper 层:在 ProductStatsMapper 中添加方法
- 4.3.3 Service 层:在 ProductStatsService 中增加方法
- 4.3.4 Service 层:在 ProductStatsServiceImpl 增加方法实现
- 4.3.5 Controller 层:在 SugarCongroller 添加方法
- 4.3.6 本地接口测试
- 4.4 刷新大屏图表数据
- 第 5 章 分省市的热力图统计
- 5.1 Sugar 组件:中国省份色彩
- 5.1.1 添加组件
- 5.1.2 配置组件
- 5.1.3 接口访问路径以及返回格式
- 5.2 数据接口实现
- 5.2.1 创建地区交易额统计实体类 ProvinceStats
- 5.2.2 Mapper 层:创建 ProvinceStatsMapper 接口
- 5.2.3 Service 层:创建 ProvinceStatsService 接口
- 5.2.4 Service 层:创建 ProvinceStatsServiceImpl 实现类
- 5.2.5 Controller 层:在 SugarController 中增加方法
- 第 6 章 流量统计数据
- 6.1 Sugar 组件:表格
- 6.1.1 添加组件
- 6.1.2 新老访客对比的表格组件配置
- 6.1.3 分时流量显示的折线组件配置
- 6.2 数据接口实现
- 6.2.1 创建访问流量统计实体类 VisitorStats
- 6.2.2 Mapper 层:创建 VisitorStatsMapper
- 6.2.3 Service 层:创建 VisitorStatsService 接口
- 6.2.4 Service 层:创建 VisitorStatsServiceImpl 实现类
- 6.2.5 Controller 层:在 SugarController 中增加方法
- 6.2.6 本地接口测试
- 第 7 章 热词字符云
- 7.1 Sugar 组件:字符云
- 7.1.1 添加组件
- 7.1.2 配置组件
- 7.1.3 接口访问路径以及返回格式
- 7.2 数据接口实现
- 7.2.1 创建关键词统计实体类
- 7.2.2 Mapper 层:创建 KeywordStatsMapper
- 7.2.3 Service 层:创建 KeywordStatsService 接口
- 7.2.4 Service 层:创建 KeywordStatsServiceImpl
- 7.2.5 Controller 层:在 SugarController 中增加方法
- 7.2.6 本地接口测试
- 第 9 章 总结
第 1 章 数据可视化接口
1.1 设计思路
之前数据分层处理,最后把轻度聚合的结果保存到 ClickHouse 中,主要的目的就是提供即时的数据查询、统计、分析服务。这些统计服务一般会用两种形式展现,一种是为专业的数据分析人员的 BI 工具,一种是面向非专业人员的更加直观的数据大屏。
以下主要是面向百度的 sugar 的数据大屏服务的接口开发。
1.2 需求梳理
1.2.1 最终显示效果图
1.2.2 分析可视化大屏
在可视化大屏中每个组件都需要一个单独的接口,图中一共涉及 8 个组件。
1.2.3 接口执行过程
之前我们实现了 DWS 层计算后写入到 ClickHouse 中,接下来就是要为可视化大屏服务,提供一个数据接口用来查询 ClickHouse 中的数据。这里主要有两项工作
➢ 配置可视化大屏服务。
➢ 编写数据查询接口以供可视化大屏进行访问。
第 2 章 Sugar 数据大屏
2.1 产品介绍
Sugar 是百度云推出的敏捷 BI 和数据可视化平台,目标是解决报表和大屏的数据 BI 分析和可视化问题,解放数据可视化系统的开发人力。
2.2 使用入口
https://cloud.baidu.com/product/sugar.html
2.3 创建数据大屏
第 3 章 总成交金额接口
3.1 Sugar 组件:数字翻牌器
3.1.1 添加组件
从大屏的编辑器上方选择【指标】→【数字翻牌器】
3.1.2 配置组件
点击组件,在右侧的菜单中选择【数据】,绑定方式改为【API 拉取】
下方的路径填写 $API_HOST/api/sugar/gmv
这个就是 sugar 会周期性访问的数据接口地址,可以自定义,其中$API_HOST 是个全局变量,需要在空间中配置(后面再说)。
3.1.3 查询组件需要的数据格式
在数据绑定的位置选择【静态 JSON】,可以看到数据需要的 JSON 格式
3.1.4 接口访问路径以及返回格式
➢ 访问路径
/api/sugar/gmv
➢ 返回格式
{"status": 0,"msg": "","data": 1201081.1632389291
}
3.2 数据接口实现
3.2.1 创建数据接口模块
- 在 gmall2021-parent 项目下创建新的模块 gmall2021-publisher
- 在 pom.xml 文件中添加需要的依赖
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.atguigu</groupId><artifactId>gmall2021-publisher-test</artifactId><version>0.0.1-SNAPSHOT</version><name>gmall2021-publisher-test</name><description>gmall2021-publisher-test</description><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.4.1</spring-boot.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.4</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.11</version></dependency><dependency><groupId>ru.yandex.clickhouse</groupId><artifactId>clickhouse-jdbc</artifactId><version>0.1.55</version></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>2.4.1</version><configuration><mainClass>com.atguigu.gmall2021publishertest.Gmall2021PublisherTestApplication</mainClass></configuration><executions><execution><id>repackage</id><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build></project>
3.2.2 代码分层结构以及实现
➢ 代码结构
- 修改 Springboot 核心配置文件 application.properties
server.port=8070
#配置 ClickHouse 驱动以及 URL
spring.datasource.driver-class-name=ru.yandex.clickhouse.ClickHouseDriver
spring.datasource.url=jdbc:clickhouse://hadoop102:8123/default
- 在 Application 中添加@MapperScan 的注解
package com.atguigu.gmall2021publishertest;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
@MapperScan(basePackages = "com.atguigu.gmall2021publishertest.mapper")
public class Gmall2021PublisherTestApplication {public static void main(String[] args) {SpringApplication.run(Gmall2021PublisherTestApplication.class, args);}}
- Mapper 层:创建 ProductStatsMapper 接口
package com.atguigu.gmall2021publishertest.mapper;import org.apache.ibatis.annotations.Select;import java.math.BigDecimal;public interface ProductStatsMapper {// select sum(order_amount) from product_stats_2021 where toYYYYMMDD(stt)=20210901;@Select("select sum(order_amount) from product_stats_2021 where toYYYYMMDD(stt)=#{date}")BigDecimal selectGMV(int date);
}
- Service 层:创建 ProductStatsService 接口
package com.atguigu.gmall2021publishertest.service;import com.atguigu.gmall2021publishertest.mapper.ProductStatsMapper;
import org.springframework.beans.factory.annotation.Autowired;import java.math.BigDecimal;public interface SugarService {//获取某一天的总交易额public BigDecimal getGMV(int date);
}
- Service 层:创建 ProductStatsServiceImpl 实现类
package com.atguigu.gmall2021publishertest.service.impl;import com.atguigu.gmall2021publishertest.mapper.ProductStatsMapper;
import com.atguigu.gmall2021publishertest.service.SugarService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.math.BigDecimal;@Service
public class SugarServiceImpl implements SugarService {@Autowiredprivate ProductStatsMapper productStatsMapper;@Overridepublic BigDecimal getGMV(int date){return productStatsMapper.selectGMV(date);}
}
- Controller 层:创建 SugarController 类
该类主要接收用户请求,并做出相应。根据 sugar 不同的组件,返回不同的格式
package com.atguigu.gmall2021publishertest.controller;import com.atguigu.gmall2021publishertest.service.SugarService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import java.text.SimpleDateFormat;@RestController
@RequestMapping("/api/sugar")
public class SugerController {@Autowiredprivate SugarService sugarService;@RequestMapping("/gmv")public String getGmv(@RequestParam(value = "date", defaultValue = "0" ) int date){if(date == 0){date = getToday();}return "{ " +" \"status\": 0, " +" \"msg\": \"\", " +" \"data\": " + sugarService.getGMV(date)+ " " +"}";}private int getToday() {SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");String dateTime = sdf.format(System.currentTimeMillis());return Integer.parseInt(dateTime);}
}
3.2.3 测试本地接口
- 启动 SpringBoot 应用程序
- 用浏览器访问测试接口
http://localhost:8070/api/sugar/gmv - 输出结果
3.3 内网穿透
3.3.1 作用
通常个人电脑无论是连接 WIFI 上网还是用网线上网,都是属于局域网里边的,外网无法直接访问到你的电脑,内网穿透可以让你的局域网中的电脑实现被外网访问功能。
3.3.2 工具
目前国内网穿透工具很多,常见的比如花生壳、Ngrok、网云穿等。
官网:
花生壳:https://hsk.oray.com
Ngrok: http://www.ngrok.cc
网云穿:http://www.neiwangchuantou.net/
3.4 配置 Sugar 大屏
3.4.1 配置服务器全局 Host
回到 Sugar 的空间管理中,在【空间设置】中增加$API_HOST
3.4.2 大屏刷新数据
然后回到大屏配置中,刷新图表数据,能看到数字已经显示
第 4 章 商品交易额不同维度的统计
4.1 三个关于商品交易额方面的统计
➢ 品牌,水平柱状图
➢ 品类,饼形图
➢ 商品 spu,轮播图
这三个的共同特征是可以根据商品统计信息计算出来。
4.2 Sugar 组件:横向柱图、轮播饼图、轮播表格
4.2.1 添加组件
- 横向柱图,用于显示品牌排行
- 轮播饼图,用于显示品类图
- 轮播表格,用于显示热门商品排行
4.2.2 品牌排行的柱形图组件配置
- 修改获取数据的方式,指定访问路径
访问路径: ${API_HOST}/api/sugar/trademark?limit=5 - 修改排序规则
因为排序规则是从下到上,所以排序定位从小到大 - 查看返回值数据格式
{"status": 0,"msg": "","data": {"categories": ["苹果","三星","华为","oppo","vivo","小米62"],"series": [{"name": "手机品牌","data": [9922,5774,5323,8043,7511,6487]}]}
}
4.2.3 品类分布的饼形图组件配置
- 修改获取数据的方式,指定访问路径
访问路径:${API_HOST}/api/sugar/category3 - 查看返回值数据格式
{"status": 0,"msg": "","data": [{"name": "PC","value": 97,"url": "http://www.baidu.com"},{"name": "iOS","value": 50,"url": "http://www.baidu.com"},{"name": "Android","value": 59,"url": "http://www.baidu.com"},{"name": "windows phone","value": 29},{"name": "Black berry","value": 3},{"name": "Nokia S60","value": 2},{"name": "Nokia S90","value": 1}]
}
4.2.4 商品排行的轮播表格组件配置
- 修改获取数据的方式,指定访问路径
访问路径:${API_HOST}/api/sugar/spu?limit=10 - 查看返回值数据格式
{"status": 0,"msg": "","data": {"columns": [{"name": "商品名称","id": "spu_name"},{"name": "成交金额","id": "amount"}],"rows": [{"spu_name": "商品 1","amount": "金额 1"},{"spu_name": "商品 2","amount": "金额 2"},{"spu_name": "商品 3","amount": "金额 3"}]}
}
4.3 数据接口实现
这三个图基本上都是根据用不同维度进行分组,金额进行聚合的方式查询商品统计表。
直接先实现三个 sql 查询
4.3.1 创建商品交易额统计实体类 ProductStats
package com.atguigu.gmall2021publishertest.bean;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/*** Desc: 商品交易额统计实体类*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ProductStats {String stt;String edt;Long sku_id;String sku_name;BigDecimal sku_price;Long spu_id;String spu_name;Long tm_id ;String tm_name;Long category3_id ;String category3_name ;@Builder.DefaultLong display_ct=0L;@Builder.DefaultLong click_ct=0L;@Builder.DefaultLong cart_ct=0L;@Builder.DefaultLong order_sku_num=0L;@Builder.DefaultBigDecimal order_amount=BigDecimal.ZERO;@Builder.DefaultLong order_ct=0L;@Builder.DefaultBigDecimal payment_amount=BigDecimal.ZERO;@Builder.DefaultLong refund_ct=0L;@Builder.DefaultBigDecimal refund_amount=BigDecimal.ZERO;@Builder.DefaultLong comment_ct=0L;@Builder.DefaultLong good_comment_ct=0L ;Long ts;
}
4.3.2 Mapper 层:在 ProductStatsMapper 中添加方法
//统计某天不同 SPU 商品交易额排名@Select("select spu_id,spu_name,sum(order_amount) order_amount," +"sum(product_stats.order_ct) order_ct from product_stats_2021 " +"where toYYYYMMDD(stt)=#{date} group by spu_id,spu_name " +"having order_amount>0 order by order_amount desc limit #{limit} ")public List<ProductStats> getProductStatsGroupBySpu(@Param("date") int date, @Param("limit") int limit);//统计某天不同类别商品交易额排名@Select("select category3_id,category3_name,sum(order_amount) order_amount " +"from product_stats_2021 " +"where toYYYYMMDD(stt)=#{date} group by category3_id,category3_name " +"having order_amount>0 order by order_amount desc limit #{limit}")public List<ProductStats> getProductStatsGroupByCategory3(@Param("date")int date ,@Param("limit") int limit);//统计某天不同品牌商品交易额排名@Select("select tm_id,tm_name,sum(order_amount) order_amount " +"from product_stats_2021 " +"where toYYYYMMDD(stt)=#{date} group by tm_id,tm_name " +"having order_amount>0 order by order_amount desc limit #{limit} ")public List<ProductStats> getProductStatsByTrademark(@Param("date")int date,@Param("limit") int limit);
4.3.3 Service 层:在 ProductStatsService 中增加方法
//统计某天不同 SPU 商品交易额排名public List<ProductStats> getProductStatsGroupBySpu(int date, int limit);//统计某天不同类别商品交易额排名public List<ProductStats> getProductStatsGroupByCategory3(int date,int limit);//统计某天不同品牌商品交易额排名public List<ProductStats> getProductStatsByTrademark(int date,int limit);
4.3.4 Service 层:在 ProductStatsServiceImpl 增加方法实现
@Overridepublic List<ProductStats> getProductStatsGroupBySpu(int date, int limit) {return productStatsMapper.getProductStatsGroupBySpu(date, limit);}@Overridepublic List<ProductStats> getProductStatsGroupByCategory3(int date, int limit) {return productStatsMapper.getProductStatsGroupByCategory3(date, limit);}@Overridepublic List<ProductStats> getProductStatsByTrademark(int date, int limit) {return productStatsMapper.getProductStatsByTrademark(date, limit);}
4.3.5 Controller 层:在 SugarCongroller 添加方法
注意:Controller 方法的定义必须依照,定好的接口访问路径和返回值格式。
- 商品列表接口方法
@RequestMapping("/spu")public String getProductStatsGroupBySpu(@RequestParam(value = "date", defaultValue = "0") Integer date,@RequestParam(value = "limit", defaultValue = "10") int limit) {if (date == 0) date = now();List<ProductStats> statsList = productStatsService.getProductStatsGroupBySpu(date, limit);//设置表头StringBuilder jsonBuilder =new StringBuilder(" " +"{\"status\":0,\"data\":{\"columns\":[" +"{\"name\":\"商品名称\",\"id\":\"spu_name\"}," +"{\"name\":\"交易额\",\"id\":\"order_amount\"}," +"{\"name\":\"订单数\",\"id\":\"order_ct\"}]," +"\"rows\":[");//循环拼接表体for (int i = 0; i < statsList.size(); i++) {ProductStats productStats = statsList.get(i);if (i >= 1) {jsonBuilder.append(",");}jsonBuilder.append("{\"spu_name\":\"" + productStats.getSpu_name() + "\"," +"\"order_amount\":" + productStats.getOrder_amount() + "," +"\"order_ct\":" + productStats.getOrder_ct() + "}");}jsonBuilder.append("]}}");return jsonBuilder.toString();}
- 品类接口方法
@RequestMapping("/category3")public String getProductStatsGroupByCategory3(@RequestParam(value = "date", defaultValue = "0") Integer date,@RequestParam(value = "limit", defaultValue = "4") int limit) {if (date == 0) {date = now();}List<ProductStats> statsList= productStatsService.getProductStatsGroupByCategory3(date, limit);StringBuilder dataJson = new StringBuilder("{ \"status\": 0, \"data\": [");int i = 0;for (ProductStats productStats : statsList) {if (i++ > 0) {dataJson.append(",");};dataJson.append("{\"name\":\"").append(productStats.getCategory3_name()).append("\",");dataJson.append("\"value\":").append(productStats.getOrder_amount()).append("}");}dataJson.append("]}");return dataJson.toString();}
- 品牌接口方法
@RequestMapping("/trademark")public String getProductStatsByTrademark(@RequestParam(value = "date", defaultValue = "0") Integer date,@RequestParam(value = "limit", defaultValue = "20") int limit) {if (date == 0) {date = now();}List<ProductStats> productStatsByTrademarkList= productStatsService.getProductStatsByTrademark(date, limit);List<String> tradeMarkList = new ArrayList<>();List<BigDecimal> amountList = new ArrayList<>();for (ProductStats productStats : productStatsByTrademarkList) {tradeMarkList.add(productStats.getTm_name());amountList.add(productStats.getOrder_amount());}String json = "{\"status\":0,\"data\":{" + "\"categories\":" +"[\"" + StringUtils.join(tradeMarkList, "\",\"") + "\"],\"series\":[" +"{\"data\":[" + StringUtils.join(amountList, ",") + "]}]}}";return json;}
4.3.6 本地接口测试
- 可以生成当前日期数据,具体步骤如下
➢ 启动 ZK、Kafka、ClickHouse、Redis、HDFS、Hbase、Maxwell
➢ 运行 BaseDBApp
➢ 运行 OrderWideApp
➢ 运行 ProductsStatsApp
➢ 运行 rt_dblog 目录下的 jar 包
➢ 查看 ClickHouse 中 products_stats_2021 表数据
- 启动 SpringBoot 项目,根据访问地址分别用浏览器测试一下接口
4.4 刷新大屏图表数据
第 5 章 分省市的热力图统计
5.1 Sugar 组件:中国省份色彩
5.1.1 添加组件
在上方地图栏位中选择【中国省份色彩】
5.1.2 配置组件
- 修改获取数据的方式,指定访问路径
访问路径:${API_HOST}/api/sugar/province
5.1.3 接口访问路径以及返回格式
➢ 访问路径
${API_HOST}/api/sugar/province
➢ 返回值格式
{"status": 0,"data": {"mapData": [{"name": "北京","value": 9131},{"name": "天津","value": 5740}]}
}
5.2 数据接口实现
5.2.1 创建地区交易额统计实体类 ProvinceStats
package com.atguigu.gmall2021publishertest.bean;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/*** Desc: 地区交易额统计实体类*/
@AllArgsConstructor
@Data
@NoArgsConstructor
public class ProvinceStats {private String stt;private String edt;private String province_id;private String province_name;private BigDecimal order_amount;private String ts;
}
5.2.2 Mapper 层:创建 ProvinceStatsMapper 接口
package com.atguigu.gmall2021publishertest.mapper;
import com.atguigu.gmall2021publishertest.bean.ProvinceStats;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/*** Desc: 地区维度统计 Mapper*/
public interface ProvinceStatsMapper {//按地区查询交易额@Select("select province_name,sum(order_amount) order_amount " +"from province_stats_2021 where toYYYYMMDD(stt)=#{date} " +"group by province_id ,province_name ")public List<ProvinceStats> selectProvinceStats(int date);
}
5.2.3 Service 层:创建 ProvinceStatsService 接口
package com.atguigu.gmall2021publishertest.service;import com.atguigu.gmall2021publishertest.bean.ProvinceStats;import java.util.List;/*** Desc: 地区维度统计接口*/
public interface ProvinceStatsService {public List<ProvinceStats> getProvinceStats(int date);
}
5.2.4 Service 层:创建 ProvinceStatsServiceImpl 实现类
package com.atguigu.gmall2021publishertest.service.impl;import com.atguigu.gmall2021publishertest.bean.ProvinceStats;
import com.atguigu.gmall2021publishertest.mapper.ProvinceStatsMapper;
import com.atguigu.gmall2021publishertest.service.ProvinceStatsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/*** Desc: 按地区维度统计 Service 实现*/
@Service
public class ProvinceStatsServiceImpl implements ProvinceStatsService {@AutowiredProvinceStatsMapper provinceStatsMapper;@Overridepublic List<ProvinceStats> getProvinceStats(int date) {return provinceStatsMapper.selectProvinceStats(date);}
}
5.2.5 Controller 层:在 SugarController 中增加方法
@AutowiredProvinceStatsService provinceStatsService;@RequestMapping("/province")public String getProvinceStats(@RequestParam(value = "date", defaultValue = "0") Integer date) {if (date == 0) {date = now();}StringBuilder jsonBuilder = new StringBuilder("{\"status\":0,\"data\":{\"mapData\":[");List<ProvinceStats> provinceStatsList = provinceStatsService.getProvinceStats(date);if (provinceStatsList.size() == 0) {// jsonBuilder.append( "{\"name\":\"北京\",\"value\":0.00}");}for (int i = 0; i < provinceStatsList.size(); i++) {if (i >= 1) {jsonBuilder.append(",");}ProvinceStats provinceStats = provinceStatsList.get(i);jsonBuilder.append("{\"name\":\"" + provinceStats.getProvince_name() +"\",\"value\":" + provinceStats.getOrder_amount() + " }");}jsonBuilder.append("]}}");return jsonBuilder.toString();}
第 6 章 流量统计数据
6.1 Sugar 组件:表格
6.1.1 添加组件
- 表格,用于显示新老访客对比
在上方【表格】栏位中选择【表格】 - 折线图,用于显示分时流量
在上方【图表】栏位中选择【折线图】
6.1.2 新老访客对比的表格组件配置
修改获取数据的方式,指定访问路径
访问路径: $API_HOST/api/sugar/visitor查看返回值数据格式
{"status": 0,"data": {"combineNum": 1,"columns": [{"name": "类别","id": "type"},{"name": "新用户","id": "new"},{"name": "老用户","id": "old"}],"rows": [{"type": "用户数","new": 123,"old": 13},{"type": "总访问页面","new": 123,"old": 145},{"type": "跳出率","new": 123,"old": 145},{"type": "平均在线时长","new": 123,"old": 145},{"type": "平均访问页面数","new": 23,"old": 145}]}
}
6.1.3 分时流量显示的折线组件配置
- 修改获取数据的方式,指定访问路径
访问路径: ${API_HOST}/api/sugar/hr - 查看返回值数据格式
{"status": 0,"data": {"categories": ["01","02","03","04","05"],"series": [{"name": "uv","data": [888065,892945,678379,733572,525091]},{"name": "pv","data": [563998,571831,622419,675294,708512]},{"name": "新用户","data": [563998,571831,622419,675294,708512]}]}
}
6.2 数据接口实现
6.2.1 创建访问流量统计实体类 VisitorStats
package com.atguigu.gmall2021publishertest.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.math.RoundingMode;
/*** Desc: 访客流量统计实体类*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class VisitorStats {private String stt;private String edt;private String vc;private String ch;private String ar;private String is_new;private Long uv_ct = 0L;private Long pv_ct = 0L;private Long sv_ct = 0L;private Long uj_ct = 0L;private Long dur_sum = 0L;private Long new_uv = 0L;private Long ts;private int hr;//计算跳出率 = 跳出次数*100/访问次数public BigDecimal getUjRate() {if (uv_ct != 0L) {return BigDecimal.valueOf(uj_ct).multiply(BigDecimal.valueOf(100)).divide(BigDecimal.valueOf(sv_ct), 2, RoundingMode.HALF_UP);} else {return BigDecimal.ZERO;}}//计算每次访问停留时间(秒) = 当日总停留时间(毫秒)/当日访问次数/1000public BigDecimal getDurPerSv() {if (uv_ct != 0L) {return BigDecimal.valueOf(dur_sum).divide(BigDecimal.valueOf(sv_ct), 0, RoundingMode.HALF_UP).divide(BigDecimal.valueOf(1000), 1, RoundingMode.HALF_UP);} else {return BigDecimal.ZERO;}}//计算每次访问停留页面数 = 当日总访问页面数/当日访问次数public BigDecimal getPvPerSv() {if (uv_ct != 0L) {return BigDecimal.valueOf(pv_ct).divide(BigDecimal.valueOf(sv_ct), 2, RoundingMode.HALF_UP);} else {return BigDecimal.ZERO;}}
}
6.2.2 Mapper 层:创建 VisitorStatsMapper
package com.atguigu.gmall2021publishertest.mapper;import com.atguigu.gmall2021publishertest.bean.VisitorStats;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/*** Desc: 访客流量统计 Mapper*/
public interface VisitorStatsMapper {//新老访客流量统计@Select("select is_new,sum(uv_ct) uv_ct,sum(pv_ct) pv_ct," +"sum(sv_ct) sv_ct, sum(uj_ct) uj_ct,sum(dur_sum) dur_sum " +"from visitor_stats_2021 where toYYYYMMDD(stt)=#{date} group by is_new")public List<VisitorStats> selectVisitorStatsByNewFlag(int date);//分时流量统计@Select("select sum(if(is_new='1', visitor_stats_2021.uv_ct,0)) new_uv,toHour(stt) hr," +"sum(visitor_stats_2021.uv_ct) uv_ct, sum(pv_ct) pv_ct, sum(uj_ct) uj_ct " +"from visitor_stats_2021 where toYYYYMMDD(stt)=#{date} group by toHour(stt)")public List<VisitorStats> selectVisitorStatsByHour(int date);@Select("select count(pv_ct) pv_ct from visitor_stats_2021 " +"where toYYYYMMDD(stt)=#{date} ")public Long selectPv(int date);@Select("select count(uv_ct) uv_ct from visitor_stats_2021 " +"where toYYYYMMDD(stt)=#{date} ")public Long selectUv(int date);
}
6.2.3 Service 层:创建 VisitorStatsService 接口
package com.atguigu.gmall2021publishertest.service;import com.atguigu.gmall2021publishertest.bean.VisitorStats;import java.util.List;public interface VisitorStatsService {public List<VisitorStats> getVisitorStatsByNewFlag(int date);public List<VisitorStats> getVisitorStatsByHour(int date);public Long getPv(int date);public Long getUv(int date);
}
6.2.4 Service 层:创建 VisitorStatsServiceImpl 实现类
package com.atguigu.gmall2021publishertest.service.impl;
import com.atguigu.gmall2021publishertest.bean.VisitorStats;
import com.atguigu.gmall2021publishertest.mapper.VisitorStatsMapper;
import com.atguigu.gmall2021publishertest.service.VisitorStatsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/*** Desc: 访问流量统计 Service 实现类*/
@Service
public class VisitorStatsServiceImpl implements VisitorStatsService {@AutowiredVisitorStatsMapper visitorStatsMapper;@Overridepublic List<VisitorStats> getVisitorStatsByNewFlag(int date) {return visitorStatsMapper.selectVisitorStatsByNewFlag(date);}@Overridepublic List<VisitorStats> getVisitorStatsByHour(int date) {return visitorStatsMapper.selectVisitorStatsByHour(date);}@Overridepublic Long getPv(int date) {return visitorStatsMapper.selectPv(date);}@Overridepublic Long getUv(int date) {return visitorStatsMapper.selectUv(date);}
}
6.2.5 Controller 层:在 SugarController 中增加方法
- 新老访客流量对比
@AutowiredVisitorStatsService visitorStatsService;@RequestMapping("/visitor")public String getVisitorStatsByNewFlag(@RequestParam(value = "date", defaultValue = "0")Integer date) {if (date == 0) date = now();List<VisitorStats> visitorStatsByNewFlag =visitorStatsService.getVisitorStatsByNewFlag(date);VisitorStats newVisitorStats = new VisitorStats();VisitorStats oldVisitorStats = new VisitorStats();//循环把数据赋给新访客统计对象和老访客统计对象for (VisitorStats visitorStats : visitorStatsByNewFlag) {if (visitorStats.getIs_new().equals("1")) {newVisitorStats = visitorStats;} else {oldVisitorStats = visitorStats;}}//把数据拼接入字符串String json = "{\"status\":0,\"data\":{\"combineNum\":1,\"columns\":" +"[{\"name\":\"类别\",\"id\":\"type\"}," +"{\"name\":\"新用户\",\"id\":\"new\"}," +"{\"name\":\"老用户\",\"id\":\"old\"}]," +"\"rows\":" +"[{\"type\":\"用户数(人)\"," +"\"new\": " + newVisitorStats.getUv_ct() + "," +"\"old\":" + oldVisitorStats.getUv_ct() + "}," +"{\"type\":\"总访问页面(次)\"," +"\"new\":" + newVisitorStats.getPv_ct() + "," +"\"old\":" + oldVisitorStats.getPv_ct() + "}," +"{\"type\":\"跳出率(%)\"," +"\"new\":" + newVisitorStats.getUjRate() + "," +"\"old\":" + oldVisitorStats.getUjRate() + "}," +"{\"type\":\"平均在线时长(秒)\"," +"\"new\":" + newVisitorStats.getDurPerSv() + "," +"\"old\":" + oldVisitorStats.getDurPerSv() + "}," +"{\"type\":\"平均访问页面数(人次)\"," +"\"new\":" + newVisitorStats.getPvPerSv() + "," +"\"old\":" + oldVisitorStats.getPvPerSv()+ "}]}}";return json;}
- 分时流量统计
@RequestMapping("/hr")public String getMidStatsGroupbyHourNewFlag(@RequestParam(value = "date",defaultValue ="0") Integer date ) {if(date==0) date=now();List<VisitorStats> visitorStatsHrList= visitorStatsService.getVisitorStatsByHour(date);//构建 24 位数组VisitorStats[] visitorStatsArr=new VisitorStats[24];//把对应小时的位置赋值for (VisitorStats visitorStats : visitorStatsHrList) {visitorStatsArr[visitorStats.getHr()] =visitorStats ;}List<String> hrList=new ArrayList<>();List<Long> uvList=new ArrayList<>();List<Long> pvList=new ArrayList<>();List<Long> newMidList=new ArrayList<>();//循环出固定的 0-23 个小时 从结果 map 中查询对应的值for (int hr = 0; hr <=23 ; hr++) {VisitorStats visitorStats = visitorStatsArr[hr];if (visitorStats!=null){uvList.add(visitorStats.getUv_ct()) ;pvList.add( visitorStats.getPv_ct());newMidList.add( visitorStats.getNew_uv());}else{ //该小时没有流量补零uvList.add(0L) ;pvList.add( 0L);newMidList.add( 0L);}//小时数不足两位补零hrList.add(String.format("%02d", hr));}//拼接字符串String json = "{\"status\":0,\"data\":{" + "\"categories\":" +"[\""+StringUtils.join(hrList,"\",\"")+ "\"],\"series\":[" +"{\"name\":\"uv\",\"data\":["+ StringUtils.join(uvList,",") +"]}," +"{\"name\":\"pv\",\"data\":["+ StringUtils.join(pvList,",") +"]}," +"{\"name\":\"新用户\",\"data\":["+ StringUtils.join(newMidList,",") +"]}]}}";return json;
6.2.6 本地接口测试
- 可以生成当前日期数据,具体步骤如下
➢ 启动 ZK、Kafka、Logger.sh、ClickHouse
➢ 运行 BaseLogApp
➢ 运行 UniqueVisitApp
➢ 运行 UserJumpDetailApp
➢ 运行 VisitorStatsApp
➢ 运行 rt_applog 目录下的 jar 包
➢ 查看 ClickHouse 中 visitor_stats_2021 表数据 - 启动 SpringBoot 项目,根据访问地址分别用浏览器测试一下接口
6.3 刷新大屏组件数据
第 7 章 热词字符云
7.1 Sugar 组件:字符云
7.1.1 添加组件
在上方【文字】栏位中选择【字符云】
7.1.2 配置组件
访问路径:${API_HOST}/api/sugar/keyword
7.1.3 接口访问路径以及返回格式
➢ 访问路径
${API_HOST}/api/sugar/keyword
➢ 返回值格式
{"status": 0,"data": [{"name": "data","value": 60679,},{"name": "dataZoom","value": 24347,}]
}
7.2 数据接口实现
7.2.1 创建关键词统计实体类
package com.atguigu.gmall2021publishertest.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/*** Desc: 关键词统计实体类*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class KeywordStats {private String stt;private String edt;private String keyword;private Long ct;private String ts;
}
7.2.2 Mapper 层:创建 KeywordStatsMapper
- SQL 语句
根据关键词的出现类型分配不同的热度分数
➢ 搜索关键词=10 分
➢ 下单商品=5 分
➢ 加入购物车=2 分
➢ 点击商品=1 分
➢ 其他=0 分
其中 ClickHouse 函数 multiIf 类似于 case when
select keyword,
sum(keyword_stats_2021.ct *
multiIf(
source='SEARCH',10,
source='ORDER',5,
source='CART',2,
source='CLICK',1,0
)) ct
from
keyword_stats
where
toYYYYMMDD(stt)=#{date}
group by
keyword
order by
sum(keyword_stats.ct)
limit #{limit};
- 接口类
package com.atguigu.gmall2021publishertest.mapper;import com.atguigu.gmall2021publishertest.bean.KeywordStats;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/*** Desc: 关键词统计 Mapper*/
public interface KeywordStatsMapper {@Select("select keyword," +"sum(keyword_stats_2021.ct * " +"multiIf(source='SEARCH',10,source='ORDER',3,source='CART',2,source='CLICK',1,0)) ct"+" from keyword_stats_2021 where toYYYYMMDD(stt)=#{date} group by keyword " +"order by sum(keyword_stats_2021.ct) desc limit #{limit} ")public List<KeywordStats> selectKeywordStats(@Param("date") int date, @Param("limit") intlimit);
}
7.2.3 Service 层:创建 KeywordStatsService 接口
package com.atguigu.gmall2021publishertest.service;
import com.atguigu.gmall2021publishertest.bean.KeywordStats;import java.util.List;
/*** Desc: 关键词统计接口*/
public interface KeywordStatsService {public List<KeywordStats> getKeywordStats(int date, int limit);
}
7.2.4 Service 层:创建 KeywordStatsServiceImpl
package com.atguigu.gmall2021publishertest.service.impl;
import com.atguigu.gmall2021publishertest.bean.KeywordStats;
import com.atguigu.gmall2021publishertest.mapper.KeywordStatsMapper;
import com.atguigu.gmall2021publishertest.service.KeywordStatsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/*** Desc:关键词统计接口实现类*/
@Service
public class KeywordStatsServiceImpl implements KeywordStatsService {@AutowiredKeywordStatsMapper keywordStatsMapper;@Overridepublic List<KeywordStats> getKeywordStats(int date, int limit) {return keywordStatsMapper.selectKeywordStats(date,limit);}
}
7.2.5 Controller 层:在 SugarController 中增加方法
@Autowiredprivate KeywordStatsService keywordStatsService;@RequestMapping("/keyword")public String getKeywordStats(@RequestParam(value = "date",defaultValue = "0") Integer date,@RequestParam(value = "limit",defaultValue = "20") intlimit){if(date==0){date=now();}//查询数据List<KeywordStats> keywordStatsList= keywordStatsService.getKeywordStats(date, limit);StringBuilder jsonSb=new StringBuilder( "{\"status\":0,\"msg\":\"\",\"data\":[" );//循环拼接字符串for (int i = 0; i < keywordStatsList.size(); i++) {KeywordStats keywordStats = keywordStatsList.get(i);if(i>=1){jsonSb.append(",");}jsonSb.append( "{\"name\":\"" + keywordStats.getKeyword() + "\"," +"\"value\":"+keywordStats.getCt()+"}");}jsonSb.append( "]}");return jsonSb.toString();}
7.2.6 本地接口测试
第 9 章 总结
数据接口部分开发的重点:
➢ 学会通过 springboot 搭建一个 web 服务。
➢ 学会在 Web 服务使用注解方式,通过 SQL 语句查询 ClickHouse。
➢ 学会通过 Sugar 实现数据大屏可视化配置,了解其中的地图、柱形图、饼图、折线图、
表格、轮播表、字符云等组件的使用预配置。
➢ 学会使用内网穿透工具,方便调试本地接口与互联网服务对接
【学习笔记】尚硅谷大数据项目之Flink实时数仓---数据可视化接口实现相关推荐
- 大数据项目之Flink实时数仓(数据采集/ODS层)
项目概览 实时大屏效果
- 电商数仓描述_笔记-尚硅谷大数据项目数据仓库-电商数仓V1.2新版
架构 项目框架 数仓架构 存储压缩 Snappy与LZO LZO安装: 读取LZO文件时,需要先创建索引,才可以进行切片. 框架版本选型Apache:运维麻烦,需要自己调研兼容性. CDH:国内使用最 ...
- maven学习笔记——尚硅谷
文章目录 maven学习笔记--尚硅谷 第一章 Maven概述 第一节 为什么要学习Maven 1.Maven 作为依赖管理工具 1.1 jar 包的规模 1.2 jar 包的来源 1.3 jar 包 ...
- MySQL学习笔记——尚硅谷李玉婷经典版MySQL基础笔记(一)
MySQL学习笔记--尚硅谷李玉婷经典版MySQL基础笔记(一) MySQL学习笔记目录 MySQL学习笔记--尚硅谷李玉婷经典版MySQL基础笔记(一) 一.基础知识 1.MySQL的语法规范 2. ...
- 【Flink实时数仓】数据仓库项目实战 《四》日志数据分流 【DWD】
文章目录 [Flink实时数仓]数据仓库项目实战 <四>日志数据分流-流量域 [DWD] 1.流量域未经加工的事务事实表 1.1主要任务 1.1.1数据清洗(ETL) 1.1.2新老访客状 ...
- 实时数仓-数据时效性如何保障?
实时数仓-数据时效性如何保障? 1.序篇 2.起因篇-为什么要做数据时效保障 3.定义篇-数据时效保障包含哪些内容 4.目标篇-时效性监控以及保障的目标 5.机制篇-怎么去做数据时效监控以及保障 5. ...
- Java学习笔记 | 尚硅谷项目三详解
该笔记基于B站视频:尚硅谷Java入门视频教程 目录 1,目标 2,需求说明 2.1,功能实现 2.1,菜单显示 2.2,添加功能 2.3,删除功能 2.3,查看团队成员 3,软件设计结构 4,具体实 ...
- Rabbitmq学习笔记(尚硅谷2021)
Rabbitmq学习笔记 (尚硅谷) 1.MQ 的概念 1.1 什么是 MQ? 1.2 为什么要用 MQ? 削峰 解耦 异步 1.3 MQ 的分类 ActiveMQ Kafka RocketMQ Ra ...
- 【HBase学习笔记-尚硅谷-Java API shell命令 谷粒微博案例】
HBase学习笔记 HBase 一.HBase简介 1.HBase介绍 2.HBase的逻辑结构和物理结构 3.数据模型 4.基本架构 二.快速入门 1.配置HBase 2.命令 三.API 1.获取 ...
- Springboot学习笔记 | 尚硅谷雷神
一.springboot入门 1.导入依赖 导入springboot版本仲裁中心 <parent><groupId>org.springframework.boot</g ...
最新文章
- c语言课程设计商品销售系统,c语言课程设计商品销售管理系统.pdf
- oracle有text字段吗,Oracle Text简介
- linux编程-open函数和write函数实现copy命令
- mysql explain insert_MySQL之EXPLAIN 执行计划详解
- 【Tomcat】Tomcat下设置项目为默认项目
- iOS开发过程中常见错误问题及解决方案
- matlab 音频处理、Python音频处理
- git解决 remote: Permission to wuheng1991/site-manager.git denied to XXX
- 【php】 php 的注释和结束符号之间的关系
- IDEA热更新插件-JRebel安装
- FME数据转换教程——MapGIS .WL/WP 转ArcGIS .Shp
- unity3D 如何提取游戏资源
- Java物流项目第一天 项目概述与基础数据服务开发
- TOM邮箱6.0版新功能体验—全新的交互设计
- 读良葛格初心者之路有感
- C# 处理PPT水印(二)——去除水印效果(文本水印、图片水印)
- Quartus 平台 FPGA 片内 RAM 使用
- 鉴源实验室丨汽车电子架构和CAN网络基础
- Python 计算思维训练——字典与字符串练习(一)
- IDE硬盘与SATA的表示
热门文章
- [Win]chrome 离线下载
- VS Code弹窗:尝试在目标目录创建文件时出错
- C#海康解码器上大屏代码事例
- 工业基础类IFC—概述
- 52个外文文献论文网站,写论文的你必囤!
- Android实现立体滚轮控件,Camera和Matrix实现真正的3D(WheelView)滚轮控件
- vue项目 echarts 中国地图,vue项目 echarts中国地图点击省份显示对应它的各个市,从零开始。
- 讲解wpe抓包,封包
- 商汤连发11款新品,把自己逼上“AI落地”极限
- linux mud 游戏,一笑天涯MUD游戏